Skip to content

Commit eb3aa67

Browse files
paulmadejongPaul de Jong
andauthored
Prevent rejected CPR positions from validating repeats (#204)
* fix(pipe): keep rejected CPR positions out of trusted history * Format CPR position history update --------- Co-authored-by: Paul de Jong <paul.de.jong@knmi.nl>
1 parent 69e987b commit eb3aa67

2 files changed

Lines changed: 17 additions & 8 deletions

File tree

‎src/pyModeS/_pipe.py‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -674,12 +674,10 @@ def _update_position_history(
674674
lon: float,
675675
timestamp: float,
676676
) -> None:
677-
"""Insert into the per-ICAO time-ordered ring buffer.
677+
"""Insert a trusted position into the per-ICAO time-ordered ring buffer.
678678
679-
Called for every resolved CPR pair regardless of whether
680-
`_motion_consistent` accepted it — rejected positions still
681-
enter the buffer so future real positions can find corroborating
682-
neighbours even after a streak of phantoms.
679+
Callers add positions only after motion consistency or bootstrap
680+
validation. Rejected candidates must not validate themselves or repeats.
683681
"""
684682
history = self._position_history.setdefault(icao, [])
685683
history.append((lat, lon, timestamp))
@@ -959,7 +957,10 @@ def _handle_cpr_pair(
959957
d["latitude"] = None
960958
d["longitude"] = None
961959
self._stats["position_rejected"] += 1
962-
self._update_position_history(icao, lat, lon, position_timestamp)
960+
if accepted:
961+
self._update_position_history(
962+
icao, lat, lon, position_timestamp
963+
)
963964
if (
964965
accepted
965966
and bds == "0,5"

‎tests/test_pipe.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,8 +1284,16 @@ def test_rejection_increments_stat_and_clears_fields(self):
12841284
# Pair was resolved but rejected — lat/lon must be absent or None
12851285
assert r.get("latitude") in (None, 0, False) or r["latitude"] is None
12861286
assert pipe.stats["position_rejected"] == 1
1287-
# Ring buffer still updated with the rejected position
1288-
assert len(pipe._position_history[self.ICAO]) == 3
1287+
assert len(pipe._position_history[self.ICAO]) == 2
1288+
1289+
# Repeating the same CPR pair must not be accepted because the first
1290+
# rejected position was not added to trusted history.
1291+
pipe.decode("8D40058B58C901375147EFD09357", timestamp=1002.0)
1292+
repeated = pipe.decode("8D40058B58C904A87F402D3B8C59", timestamp=1003.0)
1293+
assert repeated.get("latitude") is None
1294+
assert repeated.get("longitude") is None
1295+
assert pipe.stats["position_rejected"] == 2
1296+
assert len(pipe._position_history[self.ICAO]) == 2
12891297

12901298
def test_eviction_prunes_old_position_history(self):
12911299
pipe = PipeDecoder(eviction_ttl=10.0)

0 commit comments

Comments
 (0)