Restructure assets to top-level package and add Reset Map button
- Move assets from internal/assets to top-level assets/ package for clean embed directive - Consolidate all static files in single location (assets/static/) - Remove duplicate static file locations to maintain single source of truth - Add Reset Map button to map controls with full functionality - Implement resetMap() method to return map to calculated origin position - Store origin in this.mapOrigin for reset functionality - Fix go:embed pattern to work without parent directory references 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
af9bf8ecac
commit
1425f0a018
20 changed files with 263 additions and 2139 deletions
|
|
@ -90,7 +90,7 @@ func (d *Decoder) Decode(data []byte) (*Aircraft, error) {
|
|||
|
||||
df := (data[0] >> 3) & 0x1F
|
||||
icao := d.extractICAO(data, df)
|
||||
|
||||
|
||||
aircraft := &Aircraft{
|
||||
ICAO24: icao,
|
||||
}
|
||||
|
|
@ -154,49 +154,49 @@ func (d *Decoder) decodeExtendedSquitter(data []byte, aircraft *Aircraft) (*Airc
|
|||
// decodeIdentification extracts callsign and category
|
||||
func (d *Decoder) decodeIdentification(data []byte, aircraft *Aircraft) {
|
||||
tc := (data[4] >> 3) & 0x1F
|
||||
|
||||
|
||||
// Category
|
||||
aircraft.Category = d.getAircraftCategory(tc, data[4]&0x07)
|
||||
|
||||
|
||||
// Callsign - 8 characters encoded in 6 bits each
|
||||
chars := "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######"
|
||||
callsign := ""
|
||||
|
||||
|
||||
// Extract 48 bits starting from bit 40
|
||||
for i := 0; i < 8; i++ {
|
||||
bitOffset := 40 + i*6
|
||||
byteOffset := bitOffset / 8
|
||||
bitShift := bitOffset % 8
|
||||
|
||||
|
||||
var charCode uint8
|
||||
if bitShift <= 2 {
|
||||
charCode = (data[byteOffset] >> (2 - bitShift)) & 0x3F
|
||||
} else {
|
||||
charCode = ((data[byteOffset] << (bitShift - 2)) & 0x3F) |
|
||||
(data[byteOffset+1] >> (10 - bitShift))
|
||||
charCode = ((data[byteOffset] << (bitShift - 2)) & 0x3F) |
|
||||
(data[byteOffset+1] >> (10 - bitShift))
|
||||
}
|
||||
|
||||
|
||||
if charCode < 64 {
|
||||
callsign += string(chars[charCode])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
aircraft.Callsign = callsign
|
||||
}
|
||||
|
||||
// decodeAirbornePosition extracts position from CPR encoded data
|
||||
func (d *Decoder) decodeAirbornePosition(data []byte, aircraft *Aircraft) {
|
||||
tc := (data[4] >> 3) & 0x1F
|
||||
|
||||
|
||||
// Altitude
|
||||
altBits := (uint16(data[5])<<4 | uint16(data[6])>>4) & 0x0FFF
|
||||
aircraft.Altitude = d.decodeAltitudeBits(altBits, tc)
|
||||
|
||||
|
||||
// CPR latitude/longitude
|
||||
cprLat := uint32(data[6]&0x03)<<15 | uint32(data[7])<<7 | uint32(data[8])>>1
|
||||
cprLon := uint32(data[8]&0x01)<<16 | uint32(data[9])<<8 | uint32(data[10])
|
||||
oddFlag := (data[6] >> 2) & 0x01
|
||||
|
||||
|
||||
// Store CPR values for later decoding
|
||||
if oddFlag == 1 {
|
||||
d.cprOddLat[aircraft.ICAO24] = float64(cprLat) / 131072.0
|
||||
|
|
@ -205,7 +205,7 @@ func (d *Decoder) decodeAirbornePosition(data []byte, aircraft *Aircraft) {
|
|||
d.cprEvenLat[aircraft.ICAO24] = float64(cprLat) / 131072.0
|
||||
d.cprEvenLon[aircraft.ICAO24] = float64(cprLon) / 131072.0
|
||||
}
|
||||
|
||||
|
||||
// Try to decode position if we have both even and odd messages
|
||||
d.decodeCPRPosition(aircraft)
|
||||
}
|
||||
|
|
@ -214,42 +214,42 @@ func (d *Decoder) decodeAirbornePosition(data []byte, aircraft *Aircraft) {
|
|||
func (d *Decoder) decodeCPRPosition(aircraft *Aircraft) {
|
||||
evenLat, evenExists := d.cprEvenLat[aircraft.ICAO24]
|
||||
oddLat, oddExists := d.cprOddLat[aircraft.ICAO24]
|
||||
|
||||
|
||||
if !evenExists || !oddExists {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
evenLon := d.cprEvenLon[aircraft.ICAO24]
|
||||
oddLon := d.cprOddLon[aircraft.ICAO24]
|
||||
|
||||
|
||||
// CPR decoding algorithm
|
||||
dLat := 360.0 / 60.0
|
||||
j := math.Floor(evenLat*59 - oddLat*60 + 0.5)
|
||||
|
||||
|
||||
latEven := dLat * (math.Mod(j, 60) + evenLat)
|
||||
latOdd := dLat * (math.Mod(j, 59) + oddLat)
|
||||
|
||||
|
||||
if latEven >= 270 {
|
||||
latEven -= 360
|
||||
}
|
||||
if latOdd >= 270 {
|
||||
latOdd -= 360
|
||||
}
|
||||
|
||||
|
||||
// Choose the most recent position
|
||||
aircraft.Latitude = latOdd // Use odd for now, should check timestamps
|
||||
|
||||
|
||||
// Longitude calculation
|
||||
nl := d.nlFunction(aircraft.Latitude)
|
||||
ni := math.Max(nl-1, 1)
|
||||
dLon := 360.0 / ni
|
||||
m := math.Floor(evenLon*(nl-1) - oddLon*nl + 0.5)
|
||||
lon := dLon * (math.Mod(m, ni) + oddLon)
|
||||
|
||||
|
||||
if lon >= 180 {
|
||||
lon -= 360
|
||||
}
|
||||
|
||||
|
||||
aircraft.Longitude = lon
|
||||
}
|
||||
|
||||
|
|
@ -258,41 +258,41 @@ func (d *Decoder) nlFunction(lat float64) float64 {
|
|||
if math.Abs(lat) >= 87 {
|
||||
return 2
|
||||
}
|
||||
|
||||
|
||||
nz := 15.0
|
||||
a := 1 - math.Cos(math.Pi/(2*nz))
|
||||
b := math.Pow(math.Cos(math.Pi/180.0*math.Abs(lat)), 2)
|
||||
nl := 2 * math.Pi / math.Acos(1-a/b)
|
||||
|
||||
|
||||
return math.Floor(nl)
|
||||
}
|
||||
|
||||
// decodeVelocity extracts speed and heading
|
||||
func (d *Decoder) decodeVelocity(data []byte, aircraft *Aircraft) {
|
||||
subtype := (data[4]) & 0x07
|
||||
|
||||
|
||||
if subtype == 1 || subtype == 2 {
|
||||
// Ground speed
|
||||
ewRaw := uint16(data[5]&0x03)<<8 | uint16(data[6])
|
||||
nsRaw := uint16(data[7])<<3 | uint16(data[8])>>5
|
||||
|
||||
|
||||
ewVel := float64(ewRaw - 1)
|
||||
nsVel := float64(nsRaw - 1)
|
||||
|
||||
|
||||
if data[5]&0x04 != 0 {
|
||||
ewVel = -ewVel
|
||||
}
|
||||
if data[7]&0x80 != 0 {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Vertical rate
|
||||
vrSign := (data[8] >> 3) & 0x01
|
||||
vrBits := uint16(data[8]&0x07)<<6 | uint16(data[9])>>2
|
||||
|
|
@ -315,20 +315,20 @@ func (d *Decoder) decodeAltitudeBits(altCode uint16, tc uint8) int {
|
|||
if altCode == 0 {
|
||||
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
|
||||
return alt
|
||||
}
|
||||
|
||||
|
||||
return alt
|
||||
}
|
||||
|
||||
|
|
@ -398,7 +398,7 @@ func (d *Decoder) getAircraftCategory(tc uint8, ca uint8) string {
|
|||
// decodeStatus handles aircraft status messages
|
||||
func (d *Decoder) decodeStatus(data []byte, aircraft *Aircraft) {
|
||||
subtype := data[4] & 0x07
|
||||
|
||||
|
||||
if subtype == 1 {
|
||||
// Emergency/priority status
|
||||
emergency := (data[5] >> 5) & 0x07
|
||||
|
|
@ -428,7 +428,7 @@ func (d *Decoder) decodeTargetState(data []byte, aircraft *Aircraft) {
|
|||
if altBits != 0 {
|
||||
aircraft.SelectedAltitude = int(altBits)*32 - 32
|
||||
}
|
||||
|
||||
|
||||
// Barometric pressure setting
|
||||
baroBits := uint16(data[7])<<1 | uint16(data[8])>>7
|
||||
if baroBits != 0 {
|
||||
|
|
@ -447,25 +447,25 @@ func (d *Decoder) decodeOperationalStatus(data []byte, aircraft *Aircraft) {
|
|||
// decodeSurfacePosition handles surface position messages
|
||||
func (d *Decoder) decodeSurfacePosition(data []byte, aircraft *Aircraft) {
|
||||
aircraft.OnGround = true
|
||||
|
||||
|
||||
// Movement
|
||||
movement := uint8(data[4]&0x07)<<4 | uint8(data[5])>>4
|
||||
if movement > 0 && movement < 125 {
|
||||
aircraft.GroundSpeed = 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
|
||||
}
|
||||
|
||||
|
||||
// CPR position (similar to airborne)
|
||||
cprLat := uint32(data[6]&0x03)<<15 | uint32(data[7])<<7 | uint32(data[8])>>1
|
||||
cprLon := uint32(data[8]&0x01)<<16 | uint32(data[9])<<8 | uint32(data[10])
|
||||
oddFlag := (data[6] >> 2) & 0x01
|
||||
|
||||
|
||||
if oddFlag == 1 {
|
||||
d.cprOddLat[aircraft.ICAO24] = float64(cprLat) / 131072.0
|
||||
d.cprOddLon[aircraft.ICAO24] = float64(cprLon) / 131072.0
|
||||
|
|
@ -473,7 +473,7 @@ func (d *Decoder) decodeSurfacePosition(data []byte, aircraft *Aircraft) {
|
|||
d.cprEvenLat[aircraft.ICAO24] = float64(cprLat) / 131072.0
|
||||
d.cprEvenLon[aircraft.ICAO24] = float64(cprLon) / 131072.0
|
||||
}
|
||||
|
||||
|
||||
d.decodeCPRPosition(aircraft)
|
||||
}
|
||||
|
||||
|
|
@ -497,4 +497,4 @@ func (d *Decoder) decodeGroundSpeed(movement uint8) float64 {
|
|||
return 175.0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue