BEAST decoding produces incorrect positions hundreds of km off target #35

Closed
opened 2025-09-01 09:40:50 +02:00 by olemd · 1 comment
Owner

Problem Description

BEAST format decoding occasionally produces aircraft positions that are hundreds of kilometers away from their actual location. This creates phantom aircraft jumps and unreliable position tracking.

Current Behavior

  • Aircraft positions decoded from BEAST format messages sometimes appear several hundred km from their true location
  • Position validation logs show "impossible speed" errors (e.g., 15176 knots) indicating massive position jumps
  • The system detects these as validation errors but the root cause is incorrect decoding

Evidence from Logs

[POSITION_VALIDATION] ICAO 486495: REJECTED - Impossible speed: 2845 knots (max allowed: 2000 knots)
[POSITION_VALIDATION] ICAO 4CAD86: REJECTED - Impossible speed: 2937 knots (max allowed: 2000 knots)
[POSITION_VALIDATION] ICAO 4CAD86: REJECTED - Impossible speed: 15176 knots (max allowed: 2000 knots)

Technical Details

Likely Issues:

  • CPR (Compact Position Reporting) decoding errors in /internal/modes/decoder.go
  • Missing or incorrect handling of even/odd CPR frame pairs
  • Possible issues with local/global CPR decoding logic
  • Reference position handling may be incorrect

Affected Code:

  • /internal/modes/decoder.go - CPR decoding functions
  • /internal/beast/parser.go - BEAST message parsing
  • /internal/merger/merger.go - Position validation logic

Impact

  • Safety: Incorrect position data could mislead users about actual aircraft locations
  • Reliability: Creates phantom tracks and impossible aircraft movements
  • User Trust: Erratic position jumps undermine confidence in the system

Investigation Needed

  1. Review CPR decoding implementation for correctness
  2. Verify even/odd frame pairing logic
  3. Check reference position calculation for local decoding
  4. Validate coordinate wrapping and edge cases
  5. Compare with reference implementations (dump1090, etc.)

Potential Solutions

  • Improve CPR decoding algorithm
  • Add more robust position validation before accepting decoded positions
  • Implement position smoothing/filtering
  • Use previous known good positions as reference for local CPR decoding
  • Add comprehensive unit tests for CPR edge cases
## Problem Description BEAST format decoding occasionally produces aircraft positions that are hundreds of kilometers away from their actual location. This creates phantom aircraft jumps and unreliable position tracking. ## Current Behavior - Aircraft positions decoded from BEAST format messages sometimes appear several hundred km from their true location - Position validation logs show "impossible speed" errors (e.g., 15176 knots) indicating massive position jumps - The system detects these as validation errors but the root cause is incorrect decoding ## Evidence from Logs ``` [POSITION_VALIDATION] ICAO 486495: REJECTED - Impossible speed: 2845 knots (max allowed: 2000 knots) [POSITION_VALIDATION] ICAO 4CAD86: REJECTED - Impossible speed: 2937 knots (max allowed: 2000 knots) [POSITION_VALIDATION] ICAO 4CAD86: REJECTED - Impossible speed: 15176 knots (max allowed: 2000 knots) ``` ## Technical Details **Likely Issues:** - CPR (Compact Position Reporting) decoding errors in `/internal/modes/decoder.go` - Missing or incorrect handling of even/odd CPR frame pairs - Possible issues with local/global CPR decoding logic - Reference position handling may be incorrect **Affected Code:** - `/internal/modes/decoder.go` - CPR decoding functions - `/internal/beast/parser.go` - BEAST message parsing - `/internal/merger/merger.go` - Position validation logic ## Impact - **Safety**: Incorrect position data could mislead users about actual aircraft locations - **Reliability**: Creates phantom tracks and impossible aircraft movements - **User Trust**: Erratic position jumps undermine confidence in the system ## Investigation Needed 1. Review CPR decoding implementation for correctness 2. Verify even/odd frame pairing logic 3. Check reference position calculation for local decoding 4. Validate coordinate wrapping and edge cases 5. Compare with reference implementations (dump1090, etc.) ## Potential Solutions - Improve CPR decoding algorithm - Add more robust position validation before accepting decoded positions - Implement position smoothing/filtering - Use previous known good positions as reference for local CPR decoding - Add comprehensive unit tests for CPR edge cases
Author
Owner

Root cause found and fixed in v0.0.13

The CPR global decoder had several defects (fixed in 30b21e9):

  • Odd frames used the even-frame zone width (360/60 instead of 360/59), which shifted latitude by about 0.1° per zone, or roughly 110 km at Nordic latitudes. The decoder then chose whichever solution was closer to the receiver, which for aircraft north of the receiver was usually the wrong one. This explains the "15176 knots" rejections in the logs.
  • math.Mod in Go returns negative results for negative input, while CPR needs a positive modulo. Some valid positions came out at about -300° and were dropped.
  • There was no check that both latitude solutions fall in the same longitude-zone band (NL(latEven) == NL(latOdd)), so pairs straddling a boundary were off by a whole zone.
  • Even and odd frames were paired regardless of age. Pairs are now limited to 10 s airborne and 25 s surface, matching dump1090-fa.
  • Surface positions were decoded on the airborne grid and shared state with airborne frames.

In a sweep of stationary position pairs between 58°N and 71°N, 83% were wrong or dropped before the fix; all now decode to within a few metres. Merger validation also no longer locks onto a bad first position (42352e1).

Live verification against dump1090 on the same Beast feed: v0.0.12 showed one aircraft about 680 km off and logged 13 rejections in 3 minutes. v0.0.13 showed no position more than 5 km off, a median difference of 60 m, and no rejections.

Released in v0.0.13.

## Root cause found and fixed in v0.0.13 The CPR global decoder had several defects (fixed in 30b21e9): - **Odd frames used the even-frame zone width** (360/60 instead of 360/59), which shifted latitude by about 0.1° per zone, or roughly 110 km at Nordic latitudes. The decoder then chose whichever solution was closer to the receiver, which for aircraft north of the receiver was usually the wrong one. This explains the "15176 knots" rejections in the logs. - `math.Mod` in Go returns negative results for negative input, while CPR needs a positive modulo. Some valid positions came out at about -300° and were dropped. - There was no check that both latitude solutions fall in the same longitude-zone band (`NL(latEven) == NL(latOdd)`), so pairs straddling a boundary were off by a whole zone. - Even and odd frames were paired regardless of age. Pairs are now limited to 10 s airborne and 25 s surface, matching dump1090-fa. - Surface positions were decoded on the airborne grid and shared state with airborne frames. In a sweep of stationary position pairs between 58°N and 71°N, 83% were wrong or dropped before the fix; all now decode to within a few metres. Merger validation also no longer locks onto a bad first position (42352e1). **Live verification** against dump1090 on the same Beast feed: v0.0.12 showed one aircraft about 680 km off and logged 13 rejections in 3 minutes. v0.0.13 showed no position more than 5 km off, a median difference of 60 m, and no rejections. Released in [v0.0.13](https://kode.naiv.no/olemd/skyview/releases/tag/v0.0.13).
olemd closed this issue 2026-09-29 14:41:08 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
olemd/skyview#35
No description provided.