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)