Fixed code smells
This commit is contained in:
137
protocol/adsb/ra.go
Normal file
137
protocol/adsb/ra.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package adsb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ResolutionAdvisory is an ACAS message providing information about ResolutionAdvisory
|
||||
//
|
||||
// Defined at 3.1.2.8.3.1 and 4.3.8.4.2.4
|
||||
type ResolutionAdvisory struct {
|
||||
ActiveRA ActiveResolutionAdvisory
|
||||
RAComplement RAComplement
|
||||
RATerminatedIndicator RATerminatedIndicator
|
||||
MultipleThreatEncounter MultipleThreatEncounter
|
||||
ThreatTypeIndicator ThreatTypeIndicator
|
||||
ThreatIdentityAddress *ThreatIdentityAddress
|
||||
ThreatIdentityAltitude *ThreatIdentityAltitude
|
||||
ThreatIdentityRange *ThreatIdentityRange
|
||||
ThreatIdentityBearing *ThreatIdentityBearing
|
||||
}
|
||||
|
||||
// ThreatTypeIndicator indicates the type of information contained in the TID part of the message
|
||||
type ThreatTypeIndicator int
|
||||
|
||||
// ThreatIdentityAddress is a 3 bytes ICAO Address. The Most Significant Byte of the address
|
||||
// is always 0.
|
||||
type ThreatIdentityAddress uint32
|
||||
|
||||
const (
|
||||
ThreatTypeNoIdentity ThreatTypeIndicator = iota // No identity data in TID
|
||||
ThreatTypeModeS // Contains a Mode S transponder address
|
||||
ThreatTypeAltitudeRangeBearing // Contains altitude, range and bearing data
|
||||
ThreatTypeReserved3 // Reserved
|
||||
)
|
||||
|
||||
// ThreatIdentityAltitude is the altitude of the threat. It is given in 100 feet increment
|
||||
type ThreatIdentityAltitude struct {
|
||||
AltitudeValid bool
|
||||
AltitudeInFeet int32
|
||||
}
|
||||
|
||||
func parseThreatTypeIndicator(data []byte) ThreatTypeIndicator {
|
||||
return ThreatTypeIndicator((data[2] & 0x0C) >> 2)
|
||||
}
|
||||
|
||||
// parseThreatIdentityAltitude reads the altitude code from a message
|
||||
func parseThreatIdentityAltitude(data []byte) (ThreatIdentityAltitude, error) {
|
||||
|
||||
// Altitude code is a 13 bits fields, so read a uint16
|
||||
// byte data[2] | data[3] | data[4]
|
||||
// bit 19 20 21 22 23|24 25 26 27 28 29 30 31|32 33 34 35 36
|
||||
// value _ _ _ C1 A1|C2 A2 C4 A4 0 B1 D1 B2|D2 B4 D4 _ _
|
||||
|
||||
// Start by D2 B4 D4
|
||||
altitudeCode := uint16(data[4]&0xE0) >> 5
|
||||
// Then pack B1 D1 B2
|
||||
altitudeCode += uint16(data[3]&0x07) << 3
|
||||
// Then C2 A2 C4 A4
|
||||
altitudeCode += uint16(data[3]&0xF0) << 2
|
||||
// Then C1 A1
|
||||
altitudeCode += uint16(data[2]&0x03) << 2
|
||||
|
||||
// Detect invalid altitude
|
||||
if altitudeCode == 0 {
|
||||
return ThreatIdentityAltitude{}, nil
|
||||
}
|
||||
|
||||
c1 := (altitudeCode & 0x0800) != 0
|
||||
a1 := (altitudeCode & 0x0400) != 0
|
||||
c2 := (altitudeCode & 0x0200) != 0
|
||||
a2 := (altitudeCode & 0x0100) != 0
|
||||
c4 := (altitudeCode & 0x0080) != 0
|
||||
a4 := (altitudeCode & 0x0040) != 0
|
||||
b1 := (altitudeCode & 0x0020) != 0
|
||||
d1 := (altitudeCode & 0x0010) != 0
|
||||
b2 := (altitudeCode & 0x0008) != 0
|
||||
d2 := (altitudeCode & 0x0004) != 0
|
||||
b4 := (altitudeCode & 0x0002) != 0
|
||||
d4 := (altitudeCode & 0x0001) != 0
|
||||
|
||||
altitudeFeet, err := gillhamToAltitude(d1, d2, d4, a1, a2, a4, b1, b2, b4, c1, c2, c4)
|
||||
if err != nil {
|
||||
return ThreatIdentityAltitude{}, errors.New("adsb: the altitude field is malformed")
|
||||
}
|
||||
|
||||
return ThreatIdentityAltitude{true, altitudeFeet}, nil
|
||||
}
|
||||
|
||||
// ThreatIdentityRange is TIDR (threat identity data range subfield). This 7-bit subfield (76-82) shall contain the
|
||||
// most recent threat range estimated by ACAS.
|
||||
type ThreatIdentityRange byte
|
||||
|
||||
func parseThreatIdentityRange(data []byte) ThreatIdentityRange {
|
||||
return ThreatIdentityRange((data[4]&0x1F)<<2 + (data[5]&0xA0)>>6)
|
||||
}
|
||||
|
||||
// ThreatIdentityBearing is TIDR (threat identity data bearing subfield). This 6-bit subfield (83-88) shall contain the
|
||||
// most recent estimated bearing of the threat aircraft, relative to the ACAS aircraft heading.
|
||||
type ThreatIdentityBearing byte
|
||||
|
||||
func parseThreatIdentityBearing(data []byte) ThreatIdentityBearing {
|
||||
return ThreatIdentityBearing(data[5] & 0x3F)
|
||||
}
|
||||
|
||||
func parseResolutionAdvisory(data []byte) (*ResolutionAdvisory, error) {
|
||||
if len(data) != 6 {
|
||||
return nil, errors.New("adsb: data for ACAS ResolutionAdvisory must be 6 bytes long")
|
||||
}
|
||||
|
||||
// Format of the message is as follows:
|
||||
// 0 1 2 3 4 5
|
||||
// | RAC | R R M T TID | TID | TID | TID |
|
||||
// ARA | ARA RAC | A A T T d d | d d d d d d d d | d d d d d d d d | d d d d d d _ _ |
|
||||
// | RAC | C T E I TIDA| TIDA | TIDA TIDR |TIDR TIDB |
|
||||
// a a a a a a a a | a a a a a a c c | c c t m i i a a | a a a a a a a a | a a a r r r r r | r r b b b b b b |
|
||||
|
||||
ra := new(ResolutionAdvisory)
|
||||
switch ra.ThreatTypeIndicator = parseThreatTypeIndicator(data); ra.ThreatTypeIndicator {
|
||||
case ThreatTypeModeS:
|
||||
addr := ThreatIdentityAddress((binary.BigEndian.Uint32(data[2:]) >> 2) & 0xFFFFFF)
|
||||
ra.ThreatIdentityAddress = &addr
|
||||
|
||||
case ThreatTypeAltitudeRangeBearing:
|
||||
altitude, err := parseThreatIdentityAltitude(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ra.ThreatIdentityAltitude = &altitude
|
||||
|
||||
threatRange := parseThreatIdentityRange(data)
|
||||
ra.ThreatIdentityRange = &threatRange
|
||||
|
||||
bearing := parseThreatIdentityBearing(data)
|
||||
ra.ThreatIdentityBearing = &bearing
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user