Skip to content

fix(pipe): motion-check orphan pair resolutions on locked ICAOs - #206

Open
mikehartley wants to merge 1 commit into
junzis:mainfrom
mikehartley:fix/orphan-motion-check
Open

mikehartley wants to merge 1 commit into
junzis:mainfrom
mikehartley:fix/orphan-motion-check

Conversation

@mikehartley

Copy link
Copy Markdown

While decoding my own receiver's feed with PipeDecoder I found occasional positions thousands of km from the aircraft's track. They come from orphan CPR pairs.

When an airborne position frame arrives with more than one fresh opposite-parity frame pending, _handle_cpr_pair resolves the most recent one as the primary pair and the older ones as orphan pairs. For an ICAO that has passed bootstrap, only the primary resolution goes through _motion_consistent, and the orphans share its verdict. An orphan resolves independently, though: if its older frame is corrupted (CRC-valid, CPR bits wrong), it can land far from the primary and is still written into that frame's result.

Example, aircraft 4CAA5F tracked near 52.47 N, 0.92 W: two even frames are pending when an odd one arrives. The older even, 8D4CAA5F608BB333B439270755FB, is corrupted. The primary pair is correct and passes the motion check; the orphan resolves at 70.77 N, 78.21 E and is returned unchecked. I have seen the same on an aggregated feed, where two receivers relayed different versions of one frame at the same moment, with the orphan's longitude exactly one zone (10deg) out.

Proposed change:
For locked ICAOs, when the primary is accepted, run each orphan's resolution through _motion_consistent as well, clear the ones that fail and count them in position_rejected, as for a rejected primary. Bootstrapping ICAOs are unchanged, and accepted orphans still do not enter the position history, as before.

Test:
test_orphan_pair_checked_on_its_own in TestPositionMotionConsistency uses the three real frames above. It fails on current main (the corrupted frame's latitude is 70.77) and passes with the change. The rest of the suite is unaffected.

Consideration:
This should not cost genuine positions. A genuine orphan is a real position from at most pair_window (10s) before a primary that has just passed the motion check, so it is within a few km of it, far inside the check's allowance (1500 kt plus 2 km by default, measured to the primary's later timestamp). In practice only a mis-resolved orphan fails, such as one built from a corrupted frame, or from a pair straddling a latitude-zone boundary, which is wrong anyway.

When a CPR frame pairs with more than one fresh opposite, the older
opposites are resolved as orphan pairs, independently of the primary,
and their results were written back sharing the primary's motion
verdict. An orphan built from a corrupted (but CRC-valid) older frame
can resolve far from the primary, e.g. a latitude-zone or longitude-zone
error, and reached the caller unchecked.

For locked ICAOs each orphan resolution now goes through the same motion
check as the primary; a failing one is cleared and counted in
position_rejected. Bootstrapping ICAOs are unchanged.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant