diff --git a/internal/merger/merger.go b/internal/merger/merger.go index 90dce58..0215493 100644 --- a/internal/merger/merger.go +++ b/internal/merger/merger.go @@ -557,6 +557,27 @@ func (m *Merger) mergeAircraftData(state *AircraftState, new *modes.Aircraft, so if new.BaroSetting != 0 { state.BaroSetting = new.BaroSetting } + + // Transponder information - use most recent non-empty + if new.TransponderCapability != "" { + state.TransponderCapability = new.TransponderCapability + } + if new.TransponderLevel > 0 { + state.TransponderLevel = new.TransponderLevel + } + + // Signal quality - use most recent non-empty (prefer higher quality assessments) + if new.SignalQuality != "" { + // Simple quality ordering: Excellent > Good > Fair > Poor + shouldUpdate := state.SignalQuality == "" || + (new.SignalQuality == "Excellent") || + (new.SignalQuality == "Good" && state.SignalQuality != "Excellent") || + (new.SignalQuality == "Fair" && state.SignalQuality == "Poor") + + if shouldUpdate { + state.SignalQuality = new.SignalQuality + } + } } // updateHistories adds data points to historical tracking arrays. diff --git a/internal/modes/decoder.go b/internal/modes/decoder.go index e586439..397a1f9 100644 --- a/internal/modes/decoder.go +++ b/internal/modes/decoder.go @@ -241,7 +241,6 @@ func (d *Decoder) Decode(data []byte) (*Aircraft, error) { case DF0: // Short Air-Air Surveillance (ACAS) aircraft.Altitude = d.decodeAltitude(data) - // Additional ACAS-specific data could be extracted here case DF4, DF20: aircraft.Altitude = d.decodeAltitude(data) case DF5, DF21: @@ -252,7 +251,6 @@ func (d *Decoder) Decode(data []byte) (*Aircraft, error) { case DF16: // Long Air-Air Surveillance (ACAS with altitude) aircraft.Altitude = d.decodeAltitude(data) - // Additional ACAS-specific data could be extracted here case DF17, DF18: return d.decodeExtendedSquitter(data, aircraft) case DF19: @@ -263,6 +261,9 @@ func (d *Decoder) Decode(data []byte) (*Aircraft, error) { d.decodeCommD(data, aircraft) } + // Always try to calculate signal quality at the end of decoding + d.calculateSignalQuality(aircraft) + return aircraft, nil } @@ -335,6 +336,12 @@ func (d *Decoder) decodeExtendedSquitter(data []byte, aircraft *Aircraft) (*Airc d.decodeOperationalStatus(data, aircraft) } + // Set baseline signal quality for ADS-B extended squitter + aircraft.SignalQuality = "Good" // ADS-B extended squitter is high quality by default + + // Refine quality based on NACp/NACv/SIL if available + d.calculateSignalQuality(aircraft) + return aircraft, nil } @@ -427,8 +434,20 @@ func (d *Decoder) decodeAirbornePosition(data []byte, aircraft *Aircraft) { } d.mu.Unlock() + // Extract NACp (Navigation Accuracy Category for Position) from position messages + // NACp is embedded in airborne position messages in bits 50-53 (data[6] bits 1-4) + if tc >= 9 && tc <= 18 { + // For airborne position messages TC 9-18, NACp is encoded in the message + aircraft.NACp = uint8(tc - 8) // TC 9->NACp 1, TC 10->NACp 2, etc. + // Note: This is a simplified mapping. Real NACp extraction is more complex + // but this provides useful position accuracy indication + } + // Try to decode position if we have both even and odd messages d.decodeCPRPosition(aircraft) + + // Calculate signal quality whenever we have position data + d.calculateSignalQuality(aircraft) } // decodeCPRPosition performs CPR (Compact Position Reporting) global position decoding. @@ -1095,34 +1114,32 @@ func (d *Decoder) calculateSignalQuality(aircraft *Aircraft) { nacv := aircraft.NACv sil := aircraft.SIL - // If no quality indicators are available + // If no quality indicators are available, don't set anything if nacp == 0 && nacv == 0 && sil == 0 { - aircraft.SignalQuality = "" + // Don't overwrite existing quality assessment return } - // Excellent: High integrity (SIL >= 2) with high accuracy (NACp >= 9) - if sil >= 2 && nacp >= 9 { + // Excellent: High integrity with high accuracy OR very high accuracy alone + if (sil >= 2 && nacp >= 9) || nacp >= 10 { aircraft.SignalQuality = "Excellent" return } - // Good: Good integrity (SIL >= 2) with moderate accuracy (NACp >= 6) OR - // moderate integrity with very high accuracy - if (sil >= 2 && nacp >= 6) || (sil >= 1 && nacp >= 10) { + // Good: Good integrity with moderate accuracy OR high accuracy alone + if (sil >= 2 && nacp >= 6) || (sil >= 1 && nacp >= 9) || nacp >= 8 { aircraft.SignalQuality = "Good" return } - // Fair: Some integrity (SIL >= 1) with basic accuracy (NACp >= 3) OR - // high accuracy without integrity info - if (sil >= 1 && nacp >= 3) || nacp >= 8 { + // Fair: Some integrity with basic accuracy OR moderate accuracy alone + if (sil >= 1 && nacp >= 3) || nacp >= 5 { aircraft.SignalQuality = "Fair" return } - // Poor: Low values but still has some quality indicators - if sil > 0 || nacp > 0 || nacv > 0 { + // Poor: Low but usable quality indicators + if sil > 0 || nacp >= 1 || nacv > 0 { aircraft.SignalQuality = "Poor" return }