Enhance aircraft details display to match dump1090 format

- Add country lookup from ICAO hex codes with flag display
- Implement UTC clocks and last update time indicators
- Enhance aircraft table with ICAO, squawk, and RSSI columns
- Add color-coded RSSI signal strength indicators
- Fix calculateDistance returning string instead of number
- Accept all SBS-1 message types for complete data capture
- Improve data merging to preserve country and registration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2025-08-23 23:20:31 +02:00
commit c8562a4f0d
5 changed files with 457 additions and 41 deletions

View file

@ -30,6 +30,9 @@ type Aircraft struct {
LastSeen time.Time `json:"last_seen"`
Messages int `json:"messages"`
TrackHistory []TrackPoint `json:"track_history,omitempty"`
RSSI float64 `json:"rssi,omitempty"`
Country string `json:"country,omitempty"`
Registration string `json:"registration,omitempty"`
}
type AircraftData struct {
@ -44,10 +47,10 @@ func ParseSBS1Line(line string) (*Aircraft, error) {
return nil, nil
}
messageType := parts[1]
if messageType != "1" && messageType != "3" && messageType != "4" {
return nil, nil
}
// messageType := parts[1]
// Accept all message types to get complete data
// MSG types: 1=ES_IDENT_AND_CATEGORY, 2=ES_SURFACE_POS, 3=ES_AIRBORNE_POS
// 4=ES_AIRBORNE_VEL, 5=SURVEILLANCE_ALT, 6=SURVEILLANCE_ID, 7=AIR_TO_AIR, 8=ALL_CALL_REPLY
aircraft := &Aircraft{
Hex: strings.TrimSpace(parts[4]),
@ -55,6 +58,8 @@ func ParseSBS1Line(line string) (*Aircraft, error) {
Messages: 1,
}
// Different message types contain different fields
// Always try to extract what's available
if parts[10] != "" {
aircraft.Flight = strings.TrimSpace(parts[10])
}
@ -72,8 +77,8 @@ func ParseSBS1Line(line string) (*Aircraft, error) {
}
if parts[13] != "" {
if track, err := strconv.Atoi(parts[13]); err == nil {
aircraft.Track = track
if track, err := strconv.ParseFloat(parts[13], 64); err == nil {
aircraft.Track = int(track)
}
}
@ -100,6 +105,121 @@ func ParseSBS1Line(line string) (*Aircraft, error) {
aircraft.OnGround = parts[21] == "1"
}
aircraft.Country = getCountryFromICAO(aircraft.Hex)
aircraft.Registration = getRegistrationFromICAO(aircraft.Hex)
return aircraft, nil
}
func getCountryFromICAO(icao string) string {
if len(icao) < 6 {
return "Unknown"
}
prefix := icao[:1]
switch prefix {
case "4":
return getCountryFrom4xxxx(icao)
case "A":
return "United States"
case "C":
return "Canada"
case "D":
return "Germany"
case "F":
return "France"
case "G":
return "United Kingdom"
case "I":
return "Italy"
case "J":
return "Japan"
case "P":
return getPCountry(icao)
case "S":
return getSCountry(icao)
case "O":
return getOCountry(icao)
default:
return "Unknown"
}
}
func getCountryFrom4xxxx(icao string) string {
if len(icao) >= 2 {
switch icao[:2] {
case "40":
return "United Kingdom"
case "44":
return "Austria"
case "45":
return "Denmark"
case "46":
return "Germany"
case "47":
return "Germany"
case "48":
return "Netherlands"
case "49":
return "Netherlands"
}
}
return "Europe"
}
func getPCountry(icao string) string {
if len(icao) >= 2 {
switch icao[:2] {
case "PH":
return "Netherlands"
case "PJ":
return "Netherlands Antilles"
}
}
return "Unknown"
}
func getSCountry(icao string) string {
if len(icao) >= 2 {
switch icao[:2] {
case "SE":
return "Sweden"
case "SX":
return "Greece"
}
}
return "Unknown"
}
func getOCountry(icao string) string {
if len(icao) >= 2 {
switch icao[:2] {
case "OO":
return "Belgium"
case "OH":
return "Finland"
}
}
return "Unknown"
}
func getRegistrationFromICAO(icao string) string {
// This is a simplified conversion - real registration lookup would need a database
country := getCountryFromICAO(icao)
switch country {
case "Germany":
return "D-" + icao[2:]
case "United Kingdom":
return "G-" + icao[2:]
case "France":
return "F-" + icao[2:]
case "Netherlands":
return "PH-" + icao[2:]
case "Sweden":
return "SE-" + icao[2:]
default:
return icao
}
}