Fixed code smells
This commit is contained in:
527
protocol/adsb/message.go
Normal file
527
protocol/adsb/message.go
Normal file
@@ -0,0 +1,527 @@
|
||||
package adsb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.maze.io/go/ham/protocol/adsb/fields"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLength = errors.New("adsb: the supplied data must be 7 bytes long")
|
||||
ErrFormatType = errors.New("adsb: invalid format type code")
|
||||
ErrSubType = errors.New("adsb: invalid sub type code")
|
||||
)
|
||||
|
||||
type Payload interface {
|
||||
UnmarshalBytes([]byte) error
|
||||
}
|
||||
|
||||
type Message []byte
|
||||
|
||||
// FormatType returns the format type code
|
||||
//
|
||||
// Code BDS Description V0 V1 V2
|
||||
//
|
||||
// 0 ? No Position
|
||||
// 1 0,8 Aircraft Id OK OK OK
|
||||
// 2 0,8 Aircraft Id OK OK OK
|
||||
// 3 0,8 Aircraft Id OK OK OK
|
||||
// 4 0,8 Aircraft Id OK OK OK
|
||||
// 5 0,6 Surface position OK OK OK
|
||||
// 6 0,6 Surface position OK OK OK
|
||||
// 7 0,6 Surface position OK OK OK
|
||||
// 8 0,6 Surface position OK OK OK
|
||||
// 9 0,5 Airborne position OK OK OK
|
||||
//
|
||||
// 10 0,5 Airborne position OK OK OK
|
||||
// 14 0,5 Airborne position OK OK OK
|
||||
// 12 0,5 Airborne position OK OK OK
|
||||
// 13 0,5 Airborne position OK OK OK
|
||||
// 14 0,5 Airborne position OK OK OK
|
||||
// 15 0,5 Airborne position OK OK OK
|
||||
// 16 0,5 Airborne position OK OK OK
|
||||
// 17 0,5 Airborne position OK OK OK
|
||||
// 18 0,5 Airborne position OK OK OK
|
||||
// 19 0,9 Airborne velocity OK OK OK
|
||||
// 20 0,5 Airborne position OK OK OK
|
||||
// 21 0,5 Airborne position OK OK OK
|
||||
// 22 0,5 Airborne position OK OK OK
|
||||
// 23 Reserved
|
||||
// 24 Reserved
|
||||
// 25 Reserved
|
||||
// 26 Reserved
|
||||
// 27 Reserved
|
||||
// 28 6,1 Emergency report OK OK OK
|
||||
// 29 6,2 Target and status __ OK OK
|
||||
// 30 Reserved
|
||||
// 31 6,5 Operational status OK OK OK
|
||||
func (msg Message) FormatType() byte {
|
||||
if len(msg) < 7 {
|
||||
return 0
|
||||
}
|
||||
return (msg[0] & 0xF8) >> 3
|
||||
}
|
||||
|
||||
func (msg Message) Payload() (Payload, error) {
|
||||
var payload Payload
|
||||
switch formatType := msg.FormatType(); formatType {
|
||||
case 0:
|
||||
if msg[2]&0x0F == 0 && msg[3] == 0 && msg[4] == 0 && msg[5] == 0 && msg[6] == 0 {
|
||||
payload = new(NoPositionInformation)
|
||||
} else {
|
||||
payload = new(AirbornePositionType0)
|
||||
}
|
||||
|
||||
case 1, 2, 3, 4:
|
||||
payload = new(AircraftIdentificationAndCategory)
|
||||
|
||||
case 5, 6, 7, 8:
|
||||
payload = new(SurfacePosition)
|
||||
|
||||
case 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22:
|
||||
payload = new(AirbornePosition)
|
||||
|
||||
case 19:
|
||||
// check sub type
|
||||
switch subType := msg[0] & 0x07; subType {
|
||||
case 1:
|
||||
payload = new(AirborneVelocityAirSpeedNormal)
|
||||
case 2:
|
||||
payload = new(AirborneVelocityAirSpeedSupersonic)
|
||||
case 3:
|
||||
payload = new(AirborneVelocityGroundSpeedNormal)
|
||||
case 4:
|
||||
payload = new(AirborneVelocityGroundSpeedSupersonic)
|
||||
default:
|
||||
return nil, fmt.Errorf("adsb: invalid airborne velocity sub type %d", subType)
|
||||
}
|
||||
|
||||
case 28:
|
||||
switch subType := msg[0] & 0x07; subType {
|
||||
case 0:
|
||||
payload = new(AircraftStatusNoInformation)
|
||||
case 1:
|
||||
payload = new(AircraftStatusEmergency)
|
||||
case 2:
|
||||
payload = new(AircraftStatusACAS)
|
||||
default:
|
||||
return nil, fmt.Errorf("adsb: invalid aircraft status sub type %d", subType)
|
||||
}
|
||||
|
||||
case 29:
|
||||
switch subType := (msg[0] & 0x06) >> 1; subType {
|
||||
case 0:
|
||||
payload = new(TargetStateAndStatus0)
|
||||
case 1:
|
||||
payload = new(TargetStateAndStatus1)
|
||||
default:
|
||||
return nil, fmt.Errorf("adsb: invalid target state and status sub type %d", subType)
|
||||
}
|
||||
|
||||
case 31:
|
||||
payload = new(AircraftOperationalStatus)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("adsb: the format type code %d is not supported", formatType)
|
||||
}
|
||||
|
||||
if err := payload.UnmarshalBytes(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
type NoPositionInformation struct {
|
||||
FormatType byte
|
||||
AltitudeBarometric int32
|
||||
NavigationIntegrityCategory byte
|
||||
}
|
||||
|
||||
func (msg *NoPositionInformation) UnmarshalBytes(data []byte) error {
|
||||
if len(data) != 7 {
|
||||
return ErrLength
|
||||
}
|
||||
|
||||
// Update the first byte to have a standard BDS code 0,5 (Airborne position with baro altitude) - format 15
|
||||
temp := []byte{0x78, data[1], data[2], 0, 0, 0, 0}
|
||||
var pos AirbornePosition
|
||||
if err := pos.UnmarshalBytes(temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.FormatType = pos.FormatType
|
||||
msg.AltitudeBarometric = pos.AltitudeInFeet
|
||||
msg.NavigationIntegrityCategory = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AirbornePosition is a message at the format BDS 0,5
|
||||
type AirbornePosition struct {
|
||||
FormatType byte
|
||||
SurveillanceStatus fields.SurveillanceStatus
|
||||
HasDualAntenna bool
|
||||
AltitudeSource fields.AltitudeSource
|
||||
AltitudeReportMethod fields.AltitudeReportMethod
|
||||
AltitudeInFeet int32
|
||||
TimeSynchronizedToUTC bool
|
||||
CompactPositionReportingFormat fields.CompactPositionReportingFormat
|
||||
EncodedLatitude uint32
|
||||
EncodedLongitude uint32
|
||||
}
|
||||
|
||||
func (msg *AirbornePosition) UnmarshalBytes(data []byte) error {
|
||||
if len(data) != 7 {
|
||||
return ErrLength
|
||||
}
|
||||
|
||||
msg.FormatType = Message(data).FormatType()
|
||||
if msg.FormatType < 9 || msg.FormatType > 22 || msg.FormatType == 19 {
|
||||
return ErrFormatType
|
||||
}
|
||||
|
||||
var err error
|
||||
if msg.AltitudeInFeet, msg.AltitudeSource, msg.AltitudeReportMethod, err = fields.ParseAltitude(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AirbornePositionType0 struct {
|
||||
FormatTypeCode byte
|
||||
NavigationIntegrityCategory byte
|
||||
AirbornePosition
|
||||
}
|
||||
|
||||
func (msg *AirbornePositionType0) UnmarshalBytes(data []byte) error {
|
||||
// Update the first byte to have a standard BDS code 0,5 (Airborne position with baro altitude) - format 22
|
||||
temp := []byte{0xB0, data[1], data[2], data[3], data[4], data[5], data[6]}
|
||||
|
||||
if err := msg.AirbornePosition.UnmarshalBytes(temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.NavigationIntegrityCategory = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AircraftIdentificationAndCategory struct {
|
||||
FormatType byte
|
||||
CategorySet byte
|
||||
Category byte
|
||||
Identification string
|
||||
}
|
||||
|
||||
func (msg *AircraftIdentificationAndCategory) UnmarshalBytes(data []byte) error {
|
||||
if len(data) < 7 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
msg.FormatType = Message(data).FormatType()
|
||||
for msg.FormatType < 1 || msg.FormatType > 4 {
|
||||
return ErrFormatType
|
||||
}
|
||||
|
||||
msg.Category = data[0] & 0x07
|
||||
switch msg.FormatType {
|
||||
case 1:
|
||||
msg.CategorySet = 'D'
|
||||
case 2:
|
||||
msg.CategorySet = 'C'
|
||||
case 3:
|
||||
msg.CategorySet = 'B'
|
||||
default:
|
||||
msg.CategorySet = 'A'
|
||||
}
|
||||
|
||||
// Get the codes
|
||||
codes := make([]byte, 8)
|
||||
codes[0] = (data[1] & 0xFC) >> 2
|
||||
codes[1] = (data[1]&0x03)<<4 + (data[2]&0xF0)>>4
|
||||
codes[2] = (data[2]&0x0F)<<2 + (data[3]&0xC0)>>6
|
||||
codes[3] = data[3] & 0x3F
|
||||
codes[4] = (data[4] & 0xFC) >> 2
|
||||
codes[5] = (data[4]&0x03)<<4 + (data[5]&0xF0)>>4
|
||||
codes[6] = (data[5]&0x0F)<<2 + (data[6]&0xC0)>>6
|
||||
codes[7] = data[6] & 0x3F
|
||||
|
||||
// Convert the codes to actual char
|
||||
chars := make([]byte, 8)
|
||||
for i, code := range codes {
|
||||
chars[i] = identificationCharacterCoding[code]
|
||||
}
|
||||
|
||||
msg.Identification = string(chars)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SurfacePosition struct {
|
||||
FormatType byte
|
||||
MovementStatus fields.MovementStatus
|
||||
MovementSpeed float64
|
||||
GroundTrackStatus bool
|
||||
GroundTrack float64
|
||||
TimeSynchronizedToUTC bool
|
||||
CompactPositionReportingFormat fields.CompactPositionReportingFormat
|
||||
EncodedLatitude fields.EncodedLatitude
|
||||
EncodedLongitude fields.EncodedLongitude
|
||||
}
|
||||
|
||||
func (msg *SurfacePosition) UnmarshalBytes(data []byte) error {
|
||||
if len(data) != 7 {
|
||||
return ErrLength
|
||||
}
|
||||
|
||||
msg.FormatType = (data[0] & 0xF8) >> 3
|
||||
if msg.FormatType < 5 || msg.FormatType > 8 {
|
||||
return ErrFormatType
|
||||
}
|
||||
|
||||
msg.MovementSpeed, msg.MovementStatus = fields.ParseMovementStatus(data)
|
||||
msg.GroundTrack, msg.GroundTrackStatus = fields.ParseGroundTrackStatus(data)
|
||||
msg.TimeSynchronizedToUTC = fields.ParseTimeSynchronizedToUTC(data)
|
||||
msg.CompactPositionReportingFormat = fields.ParseCompactPositioningReportFormat(data)
|
||||
msg.EncodedLatitude = fields.ParseEncodedLatitude(data)
|
||||
msg.EncodedLongitude = fields.ParseEncodedLongitude(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AirborneVelocity struct {
|
||||
FormatType byte
|
||||
SubType byte
|
||||
IntentChange bool
|
||||
IFRCapability bool
|
||||
NavigationUncertaintyCategory fields.NavigationUncertaintyCategory
|
||||
MagneticHeadingStatus bool
|
||||
MagneticHeading float64
|
||||
AirspeedStatus fields.NumericValueStatus
|
||||
Airspeed uint16
|
||||
VerticalRateSource fields.VerticalRateSource
|
||||
VerticalRateStatus fields.NumericValueStatus
|
||||
VerticalRate int16
|
||||
}
|
||||
|
||||
// AirborneVelocityGroundSpeedNormal is a message at the format BDS 9,0
|
||||
type AirborneVelocityGroundSpeedNormal struct {
|
||||
AirborneVelocity
|
||||
DirectionEastWest fields.DirectionEastWest
|
||||
VelocityEWStatus fields.NumericValueStatus
|
||||
VelocityEW uint16
|
||||
DirectionNorthSouth fields.DirectionNorthSouth
|
||||
VelocityNSStatus fields.NumericValueStatus
|
||||
VelocityNS uint16
|
||||
DifferenceAltitudeGNSSBaroStatus fields.NumericValueStatus
|
||||
DifferenceAltitudeGNSSBaro int16
|
||||
}
|
||||
|
||||
func (msg *AirborneVelocityGroundSpeedNormal) UnmarshalBytes(data []byte) error {
|
||||
if err := msg.unmarshalBytes(data, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.Airspeed, msg.AirspeedStatus = fields.ParseAirspeedSupersonic(data)
|
||||
msg.DirectionEastWest = fields.ParseDirectionEastWest(data)
|
||||
msg.VelocityEW, msg.VelocityEWStatus = fields.ParseVelocityEastWestNormal(data)
|
||||
msg.DirectionNorthSouth = fields.ParseDirectionNorthSouth(data)
|
||||
msg.VelocityNS, msg.VelocityNSStatus = fields.ParseVelocityNorthSouthNormal(data)
|
||||
msg.DifferenceAltitudeGNSSBaro, msg.DifferenceAltitudeGNSSBaroStatus = fields.ParseHeightDifferenceFromBaro(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AirborneVelocityGroundSpeedSupersonic is a message at the format BDS 9,0
|
||||
type AirborneVelocityGroundSpeedSupersonic struct {
|
||||
AirborneVelocity
|
||||
DirectionEastWest fields.DirectionEastWest
|
||||
VelocityEWStatus fields.NumericValueStatus
|
||||
VelocityEW uint16
|
||||
DirectionNorthSouth fields.DirectionNorthSouth
|
||||
VelocityNSStatus fields.NumericValueStatus
|
||||
VelocityNS uint16
|
||||
DifferenceAltitudeGNSSBaroStatus fields.NumericValueStatus
|
||||
DifferenceAltitudeGNSSBaro int16
|
||||
}
|
||||
|
||||
func (msg *AirborneVelocityGroundSpeedSupersonic) UnmarshalBytes(data []byte) error {
|
||||
if err := msg.unmarshalBytes(data, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.Airspeed, msg.AirspeedStatus = fields.ParseAirspeedSupersonic(data)
|
||||
msg.DirectionEastWest = fields.ParseDirectionEastWest(data)
|
||||
msg.VelocityEW, msg.VelocityEWStatus = fields.ParseVelocityEastWestSupersonic(data)
|
||||
msg.DirectionNorthSouth = fields.ParseDirectionNorthSouth(data)
|
||||
msg.VelocityNS, msg.VelocityNSStatus = fields.ParseVelocityNorthSouthSupersonic(data)
|
||||
msg.DifferenceAltitudeGNSSBaro, msg.DifferenceAltitudeGNSSBaroStatus = fields.ParseHeightDifferenceFromBaro(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AirborneVelocityAirSpeedNormal struct {
|
||||
AirborneVelocity
|
||||
HeightDifferenceFromBaroStatus fields.NumericValueStatus
|
||||
HeightDifferenceFromBaro int16
|
||||
}
|
||||
|
||||
func (msg *AirborneVelocity) unmarshalBytes(data []byte, subType byte) error {
|
||||
if len(data) != 7 {
|
||||
return ErrLength
|
||||
}
|
||||
|
||||
msg.FormatType = (data[0] & 0xF8) >> 3
|
||||
if msg.FormatType != 19 {
|
||||
return ErrFormatType
|
||||
}
|
||||
|
||||
msg.SubType = data[0] & 0x07
|
||||
if msg.SubType != subType {
|
||||
return ErrSubType
|
||||
}
|
||||
|
||||
msg.IntentChange = fields.ParseIntentChange(data)
|
||||
msg.IFRCapability = fields.ParseIFRCapability(data)
|
||||
msg.NavigationUncertaintyCategory = fields.ParseeNavigationUncertaintyCategory(data)
|
||||
msg.MagneticHeading, msg.MagneticHeadingStatus = fields.ParseMagneticHeading(data)
|
||||
msg.VerticalRateSource = fields.ParseVerticalRateSource(data)
|
||||
msg.VerticalRate, msg.VerticalRateStatus = fields.ParseVerticalRate(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg *AirborneVelocityAirSpeedNormal) UnmarshalBytes(data []byte) error {
|
||||
if err := msg.unmarshalBytes(data, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.Airspeed, msg.AirspeedStatus = fields.ParseAirspeedNormal(data)
|
||||
msg.HeightDifferenceFromBaro, msg.HeightDifferenceFromBaroStatus = fields.ParseHeightDifferenceFromBaro(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AirborneVelocityAirSpeedSupersonic struct {
|
||||
AirborneVelocity
|
||||
HeightDifferenceFromBaroStatus fields.NumericValueStatus
|
||||
HeightDifferenceFromBaro int16
|
||||
}
|
||||
|
||||
func (msg *AirborneVelocityAirSpeedSupersonic) UnmarshalBytes(data []byte) error {
|
||||
if err := msg.unmarshalBytes(data, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.Airspeed, msg.AirspeedStatus = fields.ParseAirspeedSupersonic(data)
|
||||
msg.HeightDifferenceFromBaro, msg.HeightDifferenceFromBaroStatus = fields.ParseHeightDifferenceFromBaro(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AircraftStatusNoInformation struct {
|
||||
FormatType byte
|
||||
SubType byte
|
||||
EmergencyPriorityStatus fields.EmergencyPriorityStatus
|
||||
}
|
||||
|
||||
func (msg *AircraftStatusNoInformation) UnmarshalBytes(data []byte) error {
|
||||
if len(data) != 7 {
|
||||
return ErrLength
|
||||
}
|
||||
|
||||
msg.FormatType = (data[0] & 0xF8) >> 3
|
||||
if msg.FormatType != 28 {
|
||||
return ErrFormatType
|
||||
}
|
||||
|
||||
msg.SubType = data[0] & 0x07
|
||||
if msg.SubType != 0 {
|
||||
return ErrSubType
|
||||
}
|
||||
|
||||
msg.EmergencyPriorityStatus = fields.ParseEmergencyPriorityStatus(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
type AircraftStatusEmergency struct {
|
||||
FormatType byte
|
||||
SubType byte
|
||||
EmergencyPriorityStatus fields.EmergencyPriorityStatus
|
||||
ModeACode uint16
|
||||
}
|
||||
|
||||
func (msg *AircraftStatusEmergency) UnmarshalBytes(data []byte) error {
|
||||
if len(data) != 7 {
|
||||
return ErrLength
|
||||
}
|
||||
|
||||
msg.FormatType = (data[0] & 0xF8) >> 3
|
||||
if msg.FormatType != 28 {
|
||||
return ErrFormatType
|
||||
}
|
||||
|
||||
msg.SubType = data[0] & 0x07
|
||||
if msg.SubType != 1 {
|
||||
return ErrSubType
|
||||
}
|
||||
|
||||
msg.EmergencyPriorityStatus = fields.ParseEmergencyPriorityStatus(data)
|
||||
msg.ModeACode = uint16((data[1]&0x1F)<<8) | uint16(data[2])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AircraftStatusACAS struct {
|
||||
FormatType byte
|
||||
SubType byte
|
||||
ResolutionAdvisory ResolutionAdvisory
|
||||
}
|
||||
|
||||
type TargetStateAndStatus0 struct {
|
||||
FormatType byte
|
||||
SubType byte
|
||||
VerticalDataAvailableSourceIndicator fields.VerticalDataAvailableSourceIndicator
|
||||
TargetAltitudeType fields.TargetAltitudeType
|
||||
TargetAltitudeCapability fields.TargetAltitudeCapability
|
||||
VerticalModeIndicator fields.VerticalModeIndicator
|
||||
TargetAltitudeStatus fields.NumericValueStatus
|
||||
TargetAltitude int32
|
||||
HorizontalDataAvailableSourceIndicator fields.HorizontalDataAvailableSourceIndicator
|
||||
TargetHeadingTrackAngleStatus fields.NumericValueStatus
|
||||
TargetHeadingTrackAngle uint16
|
||||
TargetHeadingTrackIndicator fields.TargetHeadingTrackIndicator
|
||||
HorizontalModeIndicator fields.HorizontalModeIndicator
|
||||
NavigationalAccuracyCategoryPosition fields.NavigationalAccuracyCategoryPositionV1
|
||||
NICBaro fields.NICBaro
|
||||
SurveillanceIntegrityLevel fields.SurveillanceIntegrityLevel
|
||||
CapabilityModeCode fields.CapabilityModeCode
|
||||
EmergencyPriorityStatus fields.EmergencyPriorityStatus
|
||||
}
|
||||
|
||||
func (msg *TargetStateAndStatus0) UnmarshalBytes(data []byte) error {
|
||||
if len(data) != 7 {
|
||||
return ErrLength
|
||||
}
|
||||
|
||||
msg.FormatType = (data[0] & 0xF8) >> 3
|
||||
if msg.FormatType != 29 {
|
||||
return ErrFormatType
|
||||
}
|
||||
|
||||
msg.SubType = (data[0] & 0x06) >> 1
|
||||
if msg.SubType != 0 {
|
||||
return ErrSubType
|
||||
}
|
||||
}
|
||||
|
||||
var identificationCharacterCoding = []byte{
|
||||
'#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#', '#', '#', '#', '#',
|
||||
' ', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '#', '#', '#', '#', '#',
|
||||
}
|
||||
Reference in New Issue
Block a user