Implement transponder code (squawk) lookup and textual descriptions

Resolves #30

- Add comprehensive squawk code lookup database with emergency, standard, military, and special codes
- Implement squawk.Database with 20+ common transponder codes including:
  * Emergency codes: 7700 (General Emergency), 7600 (Radio Failure), 7500 (Hijacking)
  * Standard codes: 1200/7000 (VFR), operational codes by region
  * Special codes: 1277 (SAR), 1255 (Fire Fighting), military codes
- Add SquawkDescription field to Aircraft struct and JSON marshaling
- Integrate squawk database into merger for automatic description population
- Update frontend with color-coded squawk display and tooltips:
  * Red for emergency codes with warning symbols
  * Orange for special operations
  * Gray for military codes
  * Green for standard operational codes
- Add comprehensive test coverage for squawk lookup functionality

Features:
- Visual emergency code identification for safety
- Educational descriptions for aviation enthusiasts
- Configurable lookup system for regional variations
- Hover tooltips with full code explanations
- Graceful fallback for unknown codes

🤖 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-31 12:02:02 +02:00
commit 62ace55fe1
6 changed files with 700 additions and 3 deletions

View file

@ -29,6 +29,7 @@ import (
"skyview/internal/icao"
"skyview/internal/modes"
"skyview/internal/squawk"
)
const (
@ -126,6 +127,7 @@ func (a *AircraftState) MarshalJSON() ([]byte, error) {
Heading int `json:"Heading"`
Category string `json:"Category"`
Squawk string `json:"Squawk"`
SquawkDescription string `json:"SquawkDescription"`
Emergency string `json:"Emergency"`
OnGround bool `json:"OnGround"`
Alert bool `json:"Alert"`
@ -173,6 +175,7 @@ func (a *AircraftState) MarshalJSON() ([]byte, error) {
Heading: a.Aircraft.Heading,
Category: a.Aircraft.Category,
Squawk: a.Aircraft.Squawk,
SquawkDescription: a.Aircraft.SquawkDescription,
Emergency: a.Aircraft.Emergency,
OnGround: a.Aircraft.OnGround,
Alert: a.Aircraft.Alert,
@ -268,6 +271,7 @@ type Merger struct {
aircraft map[uint32]*AircraftState // ICAO24 -> merged aircraft state
sources map[string]*Source // Source ID -> source information
icaoDB *icao.Database // ICAO country lookup database
squawkDB *squawk.Database // Transponder code lookup database
mu sync.RWMutex // Protects all maps and slices
historyLimit int // Maximum history points to retain
staleTimeout time.Duration // Time before aircraft considered stale (15 seconds)
@ -296,10 +300,13 @@ func NewMerger() (*Merger, error) {
return nil, fmt.Errorf("failed to initialize ICAO database: %w", err)
}
squawkDB := squawk.NewDatabase()
return &Merger{
aircraft: make(map[uint32]*AircraftState),
sources: make(map[string]*Source),
icaoDB: icaoDB,
squawkDB: squawkDB,
historyLimit: 500,
staleTimeout: 15 * time.Second, // Aircraft timeout - reasonable for ADS-B tracking
updateMetrics: make(map[uint32]*updateMetric),
@ -535,6 +542,8 @@ func (m *Merger) mergeAircraftData(state *AircraftState, new *modes.Aircraft, so
}
if new.Squawk != "" {
state.Squawk = new.Squawk
// Look up squawk description
state.SquawkDescription = m.squawkDB.FormatSquawkWithDescription(new.Squawk)
}
if new.Category != "" {
state.Category = new.Category