1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use std::convert::From;
use std::error::Error;
use std::fmt;
use std::str::FromStr;

/// Error type used to convey parsing errors.
#[derive(Debug)]
pub struct ParserError(String);

impl fmt::Display for ParserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl Error for ParserError {}

impl<T> From<nom::Err<T>> for ParserError
where
    nom::Err<T>: std::fmt::Debug,
{
    fn from(error: nom::Err<T>) -> Self {
        ParserError(format!("{:?}", error))
    }
}

/// Unique 24-bit ICAO address assigned to an aircraft upon national registration.
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
pub struct ICAOAddress(pub(crate) u8, pub(crate) u8, pub(crate) u8);

impl fmt::Display for ICAOAddress {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:02X}{:02X}{:02X}", self.0, self.1, self.2)
    }
}

/// 16 bit transponder squawk code.
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
pub struct Squawk(pub(crate) u8, pub(crate) u8);

impl From<u16> for Squawk {
    fn from(value: u16) -> Self {
        Squawk(((value & 0xFF00) >> 8) as u8, (value & 0x00FF) as u8)
    }
}

impl FromStr for Squawk {
    type Err = std::num::ParseIntError;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        Ok(u16::from_str_radix(value, 16)?.into())
    }
}

impl fmt::Display for Squawk {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:02X}{:02X}", self.0, self.1)
    }
}

/// Horizontal coordinates in the geographic coordinate system.
#[derive(Debug, PartialEq, Clone)]
pub struct Position {
    pub latitude: f64,
    pub longitude: f64,
}

/// Aircraft position is broadcast as a set of alternating odd and even frames
/// which encode position information using Compact Position Reporting (CPR).
#[derive(Debug, PartialEq, Clone)]
pub struct CPRFrame {
    /// Aircraft position in CPR format
    pub position: Position,
    /// Frame parity
    pub parity: Parity,
}

/// Frame parity.
#[derive(Debug, PartialEq, Clone)]
pub enum Parity {
    Even,
    Odd,
}

/// Source for vertical rate information.
#[derive(Debug, PartialEq, Clone)]
pub enum VerticalRateSource {
    /// Barometric pressure altitude change rate
    BarometricPressureAltitude,
    /// Geometric altitude change rate
    GeometricAltitude,
}

/// ADS-B/Mode-S message.
#[derive(Debug, PartialEq)]
pub struct Message {
    /// Downlink Format (DF)
    pub downlink_format: u8,
    /// Kind of message
    pub kind: MessageKind,
}

/// Kind of ADS-B/Mode-S message.
#[derive(Debug, PartialEq, Clone)]
pub enum MessageKind {
    /// ADSB message (DF 17/18)
    ADSBMessage {
        /// Aircraft transponder capability
        capability: u8,
        /// Aircraft ICAO address
        icao_address: ICAOAddress,
        /// Type code used to identify ADSB message contents
        type_code: u8,
        /// Kind of ADSB message
        kind: ADSBMessageKind,
        /// True if the CRC of the received message is valid
        crc: bool,
    },
    /// Mode-S message
    ModeSMessage {
        /// Aircraft ICAO address
        icao_address: ICAOAddress,
        /// Kind of Mode S message
        kind: ModeSMessageKind,
    },
    /// Unsupported message
    Unknown,
}

/// Kind of Mode-S message.
#[derive(Debug, PartialEq, Clone)]
pub enum ModeSMessageKind {
    /// Surveillance Identity message (DF 5)
    SurveillanceIdentity {
        /// Squawk code
        squawk: Squawk,
    },
}

/// Kind of ADSB message.
#[derive(Debug, PartialEq, Clone)]
pub enum ADSBMessageKind {
    /// Aicraft identification and category message (TC 1-4)
    AircraftIdentification {
        /// Emitter category used to determine the type of aircraft
        emitter_category: u8,
        /// Aircraft callsign
        callsign: String,
    },
    /// Airborne position message (TC 9-18)
    AirbornePosition {
        /// Altitude in feet
        altitude: u16,
        /// Odd or even frame encoding position information in CPR format
        cpr_frame: CPRFrame,
    },
    /// Airborne velocity message (TC 19)
    AirborneVelocity {
        /// Heading in degrees
        heading: f64,
        /// Ground speed in knots
        ground_speed: f64,
        /// Vertical rate in feet per minute, positive values indicate an aircraft is climbing and
        /// negative values indicate it is descending
        vertical_rate: i16,
        /// Source for vertical rate information
        vertical_rate_source: VerticalRateSource,
    },
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn convert_squawk() {
        assert_eq!(Squawk::from(22128), Squawk(86, 112));
        assert_eq!(Squawk::from_str("5670").unwrap(), Squawk(86, 112));
        assert_eq!(format!("{}", Squawk::from_str("5670").unwrap()), "5670");
        // 4608 1200
        assert_eq!(Squawk::from(4608), Squawk(18, 0));
        assert_eq!(Squawk::from_str("1200").unwrap(), Squawk(18, 0));
        assert_eq!(format!("{}", Squawk::from_str("1200").unwrap()), "1200");
    }
}