diff --git a/assets/static/js/app.js b/assets/static/js/app.js index d1aa62e..78d0443 100644 --- a/assets/static/js/app.js +++ b/assets/static/js/app.js @@ -477,7 +477,7 @@ class SkyView { // Popup Content createPopupContent(aircraft) { const type = this.getAircraftType(aircraft); - const country = this.getCountryFromICAO(aircraft.ICAO24 ? aircraft.ICAO24.toString(16).toUpperCase() : ''); + const country = this.getCountryFromICAO(aircraft.ICAO24 || ''); const flag = this.getCountryFlag(country); const altitude = aircraft.Altitude || aircraft.BaroAltitude || 0; @@ -499,7 +499,7 @@ class SkyView { @@ -611,7 +611,7 @@ class SkyView { if (searchTerm) { filteredData = filteredData.filter(aircraft => (aircraft.Callsign && aircraft.Callsign.toLowerCase().includes(searchTerm)) || - (aircraft.ICAO24 && aircraft.ICAO24.toString(16).toLowerCase().includes(searchTerm)) || + (aircraft.ICAO24 && aircraft.ICAO24.toLowerCase().includes(searchTerm)) || (aircraft.Squawk && aircraft.Squawk.includes(searchTerm)) ); } @@ -638,7 +638,8 @@ class SkyView { createTableRow(aircraft) { const type = this.getAircraftType(aircraft); - const icao = aircraft.ICAO24 ? aircraft.ICAO24.toString(16).toUpperCase() : 'N/A'; + // ICAO24 is already a hex string from the server + const icao = aircraft.ICAO24 || 'N/A'; const altitude = aircraft.Altitude || aircraft.BaroAltitude || 0; const distance = this.calculateDistance(aircraft); const sources = aircraft.Sources ? Object.keys(aircraft.Sources).length : 0; @@ -718,9 +719,9 @@ class SkyView { case 'speed': return (b.GroundSpeed || 0) - (a.GroundSpeed || 0); case 'flight': - return (a.Callsign || a.ICAO24?.toString(16) || '').localeCompare(b.Callsign || b.ICAO24?.toString(16) || ''); + return (a.Callsign || a.ICAO24 || '').localeCompare(b.Callsign || b.ICAO24 || ''); case 'icao': - return (a.ICAO24?.toString(16) || '').localeCompare(b.ICAO24?.toString(16) || ''); + return (a.ICAO24 || '').localeCompare(b.ICAO24 || ''); case 'squawk': return (a.Squawk || '').localeCompare(b.Squawk || ''); case 'signal': diff --git a/internal/merger/merger.go b/internal/merger/merger.go index c673538..4c6f7a1 100644 --- a/internal/merger/merger.go +++ b/internal/merger/merger.go @@ -113,8 +113,8 @@ type AltitudePoint struct { // Used for aircraft performance analysis and track prediction. type SpeedPoint struct { Time time.Time `json:"time"` // Timestamp when speed was received - GroundSpeed float64 `json:"ground_speed"` // Ground speed in knots - Track float64 `json:"track"` // Track angle in degrees + GroundSpeed int `json:"ground_speed"` // Ground speed in knots (integer) + Track int `json:"track"` // Track angle in degrees (0-359) } // Merger handles merging aircraft data from multiple sources with intelligent conflict resolution. diff --git a/internal/modes/decoder.go b/internal/modes/decoder.go index 9d59cc1..6ca1d5f 100644 --- a/internal/modes/decoder.go +++ b/internal/modes/decoder.go @@ -82,9 +82,9 @@ type Aircraft struct { // Motion and Dynamics VerticalRate int // Vertical rate in feet per minute (climb/descent) - GroundSpeed float64 // Ground speed in knots - Track float64 // Track angle in degrees (direction of movement) - Heading float64 // Aircraft heading in degrees (magnetic) + GroundSpeed int // Ground speed in knots (integer) + Track int // Track angle in degrees (0-359, integer) + Heading int // Aircraft heading in degrees (magnetic, integer) // Aircraft Information Category string // Aircraft category (size, type, performance) @@ -484,11 +484,16 @@ func (d *Decoder) decodeVelocity(data []byte, aircraft *Aircraft) { nsVel = -nsVel } - aircraft.GroundSpeed = math.Sqrt(ewVel*ewVel + nsVel*nsVel) - aircraft.Track = math.Atan2(ewVel, nsVel) * 180 / math.Pi - if aircraft.Track < 0 { - aircraft.Track += 360 + // Calculate ground speed in knots (rounded to integer) + speedKnots := math.Sqrt(ewVel*ewVel + nsVel*nsVel) + aircraft.GroundSpeed = int(math.Round(speedKnots)) + + // Calculate track in degrees (0-359) + trackDeg := math.Atan2(ewVel, nsVel) * 180 / math.Pi + if trackDeg < 0 { + trackDeg += 360 } + aircraft.Track = int(math.Round(trackDeg)) } // Vertical rate @@ -538,19 +543,36 @@ func (d *Decoder) decodeAltitudeBits(altCode uint16, tc uint8) int { return 0 } - // Gray code to binary conversion - var n uint16 - for i := uint(0); i < 12; i++ { - n ^= altCode >> i - } - - alt := int(n)*25 - 1000 - - if tc >= 20 && tc <= 22 { - // GNSS altitude + // Standard altitude encoding with 25 ft increments + // Check Q-bit (bit 4) for encoding type + qBit := (altCode >> 4) & 1 + + if qBit == 1 { + // Standard altitude with Q-bit set + // Remove Q-bit and reassemble 11-bit altitude code + n := ((altCode & 0x1F80) >> 2) | ((altCode & 0x0020) >> 1) | (altCode & 0x000F) + alt := int(n)*25 - 1000 + + // Validate altitude range + if alt < -1000 || alt > 60000 { + return 0 + } return alt } - + + // Gray code altitude (100 ft increments) - legacy encoding + // Convert from Gray code to binary + n := altCode + n ^= n >> 8 + n ^= n >> 4 + n ^= n >> 2 + n ^= n >> 1 + + // Convert to altitude in feet + alt := int(n&0x7FF) * 100 + if alt < 0 || alt > 60000 { + return 0 + } return alt } @@ -756,14 +778,14 @@ func (d *Decoder) decodeSurfacePosition(data []byte, aircraft *Aircraft) { // Movement movement := uint8(data[4]&0x07)<<4 | uint8(data[5])>>4 if movement > 0 && movement < 125 { - aircraft.GroundSpeed = d.decodeGroundSpeed(movement) + aircraft.GroundSpeed = int(math.Round(d.decodeGroundSpeed(movement))) } // Track trackValid := (data[5] >> 3) & 0x01 if trackValid != 0 { trackBits := uint16(data[5]&0x07)<<4 | uint16(data[6])>>4 - aircraft.Track = float64(trackBits) * 360.0 / 128.0 + aircraft.Track = int(math.Round(float64(trackBits) * 360.0 / 128.0)) } // CPR position (similar to airborne)