Skip to main content

tuxar_modes/
adsb.rs

1// ── No Position (TC=0) ───────────────────────────────────────────────────────
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct NoPositionMessage {
5    data: Vec<u8>,
6}
7
8impl NoPositionMessage {
9    pub fn decode(data: Vec<u8>) -> Self {
10        Self { data }
11    }
12
13    /// Surveillance Status (SS), 2 bits.
14    pub fn surveillance_status(&self) -> u8 {
15        (self.data[4] >> 1) & 0x03
16    }
17
18    /// NIC Supplement B (NICsb), 1 bit.
19    pub fn nic_supplement_b(&self) -> bool {
20        self.data[4] & 0x01 != 0
21    }
22
23    /// Encoded altitude (12-bit Gillham code); decode separately.
24    pub fn altitude(&self) -> u16 { todo!() }
25
26    /// UTC sync bit (T).
27    pub fn utc_sync(&self) -> bool {
28        (self.data[6] >> 3) & 0x01 != 0
29    }
30
31    /// CPR format: 0 = even, 1 = odd.
32    pub fn cpr_format(&self) -> u8 {
33        (self.data[6] >> 2) & 0x01
34    }
35}
36
37// ── Airborne Position ────────────────────────────────────────────────────────
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct AirbornePositionMessage {
41    data: Vec<u8>,
42}
43
44impl AirbornePositionMessage {
45    pub fn decode(data: Vec<u8>) -> Self {
46        Self { data }
47    }
48
49    pub fn altitude_mops01(&self) -> u16 { todo!() }
50    pub fn cpr_lat_mops01(&self) -> u32 { todo!() }
51    pub fn cpr_lon_mops01(&self) -> u32 { todo!() }
52
53    pub fn altitude_mops2(&self) -> u16 { todo!() }
54    pub fn cpr_lat_mops2(&self) -> u32 { todo!() }
55    pub fn cpr_lon_mops2(&self) -> u32 { todo!() }
56}
57
58// ── Surface Position ─────────────────────────────────────────────────────────
59
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct SurfacePositionMessage {
62    data: Vec<u8>,
63}
64
65impl SurfacePositionMessage {
66    pub fn decode(data: Vec<u8>) -> Self {
67        Self { data }
68    }
69
70    pub fn ground_speed_mops01(&self) -> u16 { todo!() }
71    pub fn cpr_lat_mops01(&self) -> u32 { todo!() }
72    pub fn cpr_lon_mops01(&self) -> u32 { todo!() }
73
74    pub fn ground_speed_mops2(&self) -> u16 { todo!() }
75    pub fn cpr_lat_mops2(&self) -> u32 { todo!() }
76    pub fn cpr_lon_mops2(&self) -> u32 { todo!() }
77}
78
79// ── Aircraft Identity and Category ───────────────────────────────────────────
80// Same mapping for all MOPS versions
81
82#[derive(Clone, PartialEq, Eq)]
83pub struct AircraftIdentityMessage {
84    data: Vec<u8>,
85}
86
87impl std::fmt::Debug for AircraftIdentityMessage {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        f.debug_struct("AircraftIdentityMessage")
90            .field("emitter_category", &self.emitter_category())
91            .field("callsign", &self.callsign())
92            .finish()
93    }
94}
95
96impl AircraftIdentityMessage {
97    pub fn decode(data: Vec<u8>) -> Self {
98        Self { data }
99    }
100
101    pub fn emitter_category(&self) -> u8 {
102        self.data[4] & 0x07
103    }
104
105    pub fn callsign(&self) -> String {
106        // 6-bit character set from The 1090 MHz Riddle, indices 0-63
107        const CHARSET: &[u8; 64] = b"#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######";
108
109        let d = &self.data;
110        let chars: [u8; 8] = [
111            (d[5] >> 2) & 0x3F,
112            ((d[5] & 0x03) << 4) | (d[6] >> 4),
113            ((d[6] & 0x0F) << 2) | (d[7] >> 6),
114            d[7] & 0x3F,
115            (d[8] >> 2) & 0x3F,
116            ((d[8] & 0x03) << 4) | (d[9] >> 4),
117            ((d[9] & 0x0F) << 2) | (d[10] >> 6),
118            d[10] & 0x3F,
119        ];
120
121        chars.iter()
122            .map(|&i| CHARSET[i as usize] as char)
123            .collect::<String>()
124            .trim_end_matches(['_', '#'])
125            .to_string()
126    }
127}
128
129// ── Airborne Velocity – Velocity over Ground ─────────────────────────────────
130
131#[derive(Debug, Clone, PartialEq, Eq)]
132pub struct AirborneVelocityGroundMessage {
133    data: Vec<u8>,
134}
135
136impl AirborneVelocityGroundMessage {
137    pub fn decode(data: Vec<u8>) -> Self {
138        Self { data }
139    }
140
141    pub fn velocity_ew_mops0(&self) -> i16 { todo!() }
142    pub fn velocity_ns_mops0(&self) -> i16 { todo!() }
143    pub fn vertical_rate_mops0(&self) -> i16 { todo!() }
144
145    pub fn velocity_ew_mops1(&self) -> i16 { todo!() }
146    pub fn velocity_ns_mops1(&self) -> i16 { todo!() }
147    pub fn vertical_rate_mops1(&self) -> i16 { todo!() }
148
149    pub fn velocity_ew_mops2(&self) -> i16 { todo!() }
150    pub fn velocity_ns_mops2(&self) -> i16 { todo!() }
151    pub fn vertical_rate_mops2(&self) -> i16 { todo!() }
152}
153
154// ── Airborne Velocity – Airspeed and Heading ─────────────────────────────────
155
156#[derive(Debug, Clone, PartialEq, Eq)]
157pub struct AirborneVelocityAirspeedMessage {
158    data: Vec<u8>,
159}
160
161impl AirborneVelocityAirspeedMessage {
162    pub fn decode(data: Vec<u8>) -> Self {
163        Self { data }
164    }
165
166    pub fn airspeed_mops0(&self) -> u16 { todo!() }
167    pub fn heading_mops0(&self) -> u16 { todo!() }
168    pub fn vertical_rate_mops0(&self) -> i16 { todo!() }
169
170    pub fn airspeed_mops1(&self) -> u16 { todo!() }
171    pub fn heading_mops1(&self) -> u16 { todo!() }
172    pub fn vertical_rate_mops1(&self) -> i16 { todo!() }
173
174    pub fn airspeed_mops2(&self) -> u16 { todo!() }
175    pub fn heading_mops2(&self) -> u16 { todo!() }
176    pub fn vertical_rate_mops2(&self) -> i16 { todo!() }
177}
178
179// ── Test Message (TC=23, subtype=7) ──────────────────────────────────────────
180// Defined for MOPS 1 only; subtype 7 carries a Mode-A (squawk) code
181
182#[derive(Clone, PartialEq, Eq)]
183pub struct TestModeAMessage {
184    data: Vec<u8>,
185}
186
187impl std::fmt::Debug for TestModeAMessage {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        f.debug_struct("TestModeAMessage")
190            .field("mode_a_code", &format_args!("{:04o}", self.mode_a_code()))
191            .finish()
192    }
193}
194
195impl TestModeAMessage {
196    pub fn decode(data: Vec<u8>) -> Self {
197        Self { data }
198    }
199
200    /// Mode-A (squawk) code, 13 bits.
201    pub fn mode_a_code(&self) -> u16 { todo!() }
202}
203
204// ── Aircraft Status – Emergency ───────────────────────────────────────────────
205
206#[derive(Debug, Clone, PartialEq, Eq)]
207pub struct AircraftStatusEmergencyMessage {
208    data: Vec<u8>,
209}
210
211impl AircraftStatusEmergencyMessage {
212    pub fn decode(data: Vec<u8>) -> Self {
213        Self { data }
214    }
215
216    pub fn emergency_state_mops01(&self) -> u8 { todo!() }
217    pub fn emergency_state_mops2(&self) -> u8 { todo!() }
218}
219
220// ── Aircraft Status – TCAS ────────────────────────────────────────────────────
221// MOPS 2 only
222
223#[derive(Debug, Clone, PartialEq, Eq)]
224pub struct AircraftStatusTcasMessage {
225    data: Vec<u8>,
226}
227
228impl AircraftStatusTcasMessage {
229    pub fn decode(data: Vec<u8>) -> Self {
230        Self { data }
231    }
232
233    pub fn ara(&self) -> u16 { todo!() }
234    pub fn rac(&self) -> u8 { todo!() }
235}
236
237// ── Target State and Status ───────────────────────────────────────────────────
238
239#[derive(Debug, Clone, PartialEq, Eq)]
240pub struct TargetStateStatusMessage {
241    data: Vec<u8>,
242}
243
244impl TargetStateStatusMessage {
245    pub fn decode(data: Vec<u8>) -> Self {
246        Self { data }
247    }
248
249    pub fn target_altitude_mops1(&self) -> u16 { todo!() }
250    pub fn vertical_mode_mops1(&self) -> u8 { todo!() }
251
252    pub fn target_altitude_mops2(&self) -> u16 { todo!() }
253    pub fn vertical_mode_mops2(&self) -> u8 { todo!() }
254}
255
256// ── Aircraft Operational Status – Airborne ────────────────────────────────────
257
258#[derive(Debug, Clone, PartialEq, Eq)]
259pub struct AircraftOperationalStatusAirborneMessage {
260    data: Vec<u8>,
261}
262
263impl AircraftOperationalStatusAirborneMessage {
264    pub fn decode(data: Vec<u8>) -> Self {
265        Self { data }
266    }
267
268    pub fn capability_codes_mops0(&self) -> u16 { todo!() }
269    pub fn operational_mode_codes_mops0(&self) -> u16 { todo!() }
270
271    pub fn capability_codes_mops1(&self) -> u16 { todo!() }
272    pub fn operational_mode_codes_mops1(&self) -> u16 { todo!() }
273
274    pub fn capability_codes_mops2(&self) -> u16 { todo!() }
275    pub fn operational_mode_codes_mops2(&self) -> u16 { todo!() }
276}
277
278// ── Aircraft Operational Status – Surface ─────────────────────────────────────
279
280#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct AircraftOperationalStatusSurfaceMessage {
282    data: Vec<u8>,
283}
284
285impl AircraftOperationalStatusSurfaceMessage {
286    pub fn decode(data: Vec<u8>) -> Self {
287        Self { data }
288    }
289
290    pub fn capability_codes_mops1(&self) -> u16 { todo!() }
291    pub fn operational_mode_codes_mops1(&self) -> u16 { todo!() }
292
293    pub fn capability_codes_mops2(&self) -> u16 { todo!() }
294    pub fn operational_mode_codes_mops2(&self) -> u16 { todo!() }
295}
296
297// ── ADS-B Message Enum ────────────────────────────────────────────────────────
298
299#[derive(Debug, Clone, PartialEq, Eq)]
300pub enum AdsbMessage {
301    NoPosition(NoPositionMessage),
302    AirbornePosition(AirbornePositionMessage),
303    SurfacePosition(SurfacePositionMessage),
304    AircraftIdentity(AircraftIdentityMessage),
305    AirborneVelocityGround(AirborneVelocityGroundMessage),
306    AirborneVelocityAirspeed(AirborneVelocityAirspeedMessage),
307    TestModeA(TestModeAMessage),
308    AircraftStatusEmergency(AircraftStatusEmergencyMessage),
309    AircraftStatusTcas(AircraftStatusTcasMessage),
310    TargetStateStatus(TargetStateStatusMessage),
311    AircraftOperationalStatusAirborne(AircraftOperationalStatusAirborneMessage),
312    AircraftOperationalStatusSurface(AircraftOperationalStatusSurfaceMessage),
313}
314
315impl AdsbMessage {
316    pub fn decode(data: Vec<u8>) -> Self {
317        let tc = (data[4] >> 3) & 0x1F;
318        let sub_type = data[4] & 0x07;
319
320        match tc {
321            0                 => AdsbMessage::NoPosition(NoPositionMessage::decode(data)),
322            1..=4             => AdsbMessage::AircraftIdentity(AircraftIdentityMessage::decode(data)),
323            5..=8             => AdsbMessage::SurfacePosition(SurfacePositionMessage::decode(data)),
324            9..=18 | 20..=22  => AdsbMessage::AirbornePosition(AirbornePositionMessage::decode(data)),
325            19 => match sub_type {
326                1..=2 => AdsbMessage::AirborneVelocityGround(AirborneVelocityGroundMessage::decode(data)),
327                3..=4 => AdsbMessage::AirborneVelocityAirspeed(AirborneVelocityAirspeedMessage::decode(data)),
328                _ => todo!("unknown velocity sub-type {sub_type}"),
329            },
330            23 => match sub_type {
331                7 => AdsbMessage::TestModeA(TestModeAMessage::decode(data)),
332                _ => todo!("unknown test sub-type {sub_type}"),
333            },
334            28 => match sub_type {
335                1 => AdsbMessage::AircraftStatusEmergency(AircraftStatusEmergencyMessage::decode(data)),
336                2 => AdsbMessage::AircraftStatusTcas(AircraftStatusTcasMessage::decode(data)),
337                _ => todo!("unknown aircraft status sub-type {sub_type}"),
338            },
339            29 => AdsbMessage::TargetStateStatus(TargetStateStatusMessage::decode(data)),
340            31 => match sub_type {
341                0 => AdsbMessage::AircraftOperationalStatusAirborne(AircraftOperationalStatusAirborneMessage::decode(data)),
342                1 => AdsbMessage::AircraftOperationalStatusSurface(AircraftOperationalStatusSurfaceMessage::decode(data)),
343                _ => todo!("unknown operational status sub-type {sub_type}"),
344            },
345            _ => todo!("unknown TC {tc}"),
346        }
347    }
348}