flight_tracker/
tracker.rs

1use adsb::*;
2use std::collections::HashMap;
3use std::time::{Duration, SystemTime};
4use MessageKind::*;
5
6/// A tracked aircraft
7#[derive(Debug, Clone)]
8pub struct Aircraft {
9    /// Unique 24-bit ICAO address assigned to an aircraft upon national registration
10    pub icao_address: ICAOAddress,
11    /// Current aircraft callsign
12    pub callsign: Option<String>,
13    /// Current altitude (feet)
14    pub altitude: Option<u16>,
15    /// Current heading (degrees)
16    pub heading: Option<f64>,
17    /// Current ground speed (knots)
18    pub ground_speed: Option<f64>,
19    /// Current vertical rate (feet per minute)
20    pub vertical_rate: Option<i16>,
21    /// Current latitude (degrees)
22    pub latitude: Option<f64>,
23    /// Current longitude (degrees)
24    pub longitude: Option<f64>,
25    /// Source for vertical rate information
26    pub vertical_rate_source: Option<VerticalRateSource>,
27    /// Timestamp for last received message
28    pub last_seen: SystemTime,
29    /// Last squawk code
30    pub last_squawk: Option<Squawk>,
31    last_cpr_even: Option<CPRFrame>,
32    last_cpr_odd: Option<CPRFrame>,
33}
34
35impl Aircraft {
36    fn new(icao_address: ICAOAddress) -> Self {
37        Aircraft {
38            icao_address,
39            callsign: None,
40            altitude: None,
41            heading: None,
42            ground_speed: None,
43            vertical_rate: None,
44            latitude: None,
45            longitude: None,
46            vertical_rate_source: None,
47            last_seen: SystemTime::now(),
48            last_cpr_even: None,
49            last_cpr_odd: None,
50            last_squawk: None,
51        }
52    }
53
54    fn update_position(&mut self, cpr_frame: CPRFrame) {
55        let last_parity = cpr_frame.parity.clone();
56        match last_parity {
57            Parity::Even => {
58                self.last_cpr_even = Some(cpr_frame);
59            }
60            Parity::Odd => {
61                self.last_cpr_odd = Some(cpr_frame);
62            }
63        }
64        if let (Some(even), Some(odd)) = (&self.last_cpr_even, &self.last_cpr_odd) {
65            let position = match last_parity {
66                Parity::Even => cpr::get_position((odd, even)),
67                Parity::Odd => cpr::get_position((even, odd)),
68            };
69            if let Some(Position {
70                latitude,
71                longitude,
72            }) = position
73            {
74                self.latitude = Some(latitude);
75                self.longitude = Some(longitude);
76            }
77        }
78    }
79}
80
81/// Stores the set of currently tracked aircraft
82#[derive(Default)]
83pub struct Tracker {
84    map: HashMap<ICAOAddress, Aircraft>,
85}
86
87impl Tracker {
88    /// Create a new tracker
89    pub fn new() -> Self {
90        Tracker::default()
91    }
92
93    /// Update the tracker with a received ADSB message in AVR format
94    pub fn update_with_avr(&mut self, frame: &str) -> Result<(), adsb::ParserError> {
95        let (message, _) = adsb::parse_avr(frame)?;
96        self.update_with_message(message);
97        Ok(())
98    }
99
100    /// Update the tracker with a received ADSB message in binary format
101    pub fn update_with_binary(&mut self, frame: &[u8]) -> Result<(), adsb::ParserError> {
102        let (message, _) = adsb::parse_binary(frame)?;
103        self.update_with_message(message);
104        Ok(())
105    }
106
107    fn update_with_message(&mut self, message: Message) {
108        match message {
109            Message {
110                kind: ADSBMessage {
111                    icao_address, kind, ..
112                },
113                ..
114            } => self.update_with_adsb_message(icao_address, kind),
115            Message {
116                kind: ModeSMessage { icao_address, kind },
117                ..
118            } => self.update_with_mode_s_message(icao_address, kind),
119            _ => (),
120        };
121    }
122
123    fn update_with_adsb_message(&mut self, icao_address: ICAOAddress, kind: ADSBMessageKind) {
124        use ADSBMessageKind::*;
125
126        let aircraft = self.get_or_create_aircraft(icao_address);
127
128        match kind {
129            AircraftIdentification { callsign, .. } => {
130                aircraft.callsign = Some(callsign.trim().to_string());
131            }
132            AirbornePosition {
133                altitude,
134                cpr_frame,
135            } => {
136                aircraft.altitude = Some(altitude);
137                aircraft.update_position(cpr_frame);
138            }
139            AirborneVelocity {
140                heading,
141                ground_speed,
142                vertical_rate,
143                vertical_rate_source,
144            } => {
145                aircraft.heading = Some(heading);
146                aircraft.ground_speed = Some(ground_speed);
147                aircraft.vertical_rate = Some(vertical_rate);
148                aircraft.vertical_rate_source = Some(vertical_rate_source);
149            }
150        }
151
152        aircraft.last_seen = SystemTime::now();
153    }
154
155    fn update_with_mode_s_message(&mut self, icao_address: ICAOAddress, kind: ModeSMessageKind) {
156        use ModeSMessageKind::*;
157
158        let aircraft = self.get_or_create_aircraft(icao_address);
159
160        match kind {
161            SurveillanceIdentity { squawk } => {
162                aircraft.last_squawk = Some(squawk);
163            }
164        }
165
166        aircraft.last_seen = SystemTime::now();
167    }
168
169    fn get_or_create_aircraft(&mut self, icao_address: ICAOAddress) -> &mut Aircraft {
170        self.map
171            .entry(icao_address)
172            .or_insert_with(|| Aircraft::new(icao_address))
173    }
174
175    /// Get a list of aircraft last seen in the given interval
176    pub fn get_current_aircraft(&self, interval: &Duration) -> Vec<&Aircraft> {
177        self.map
178            .values()
179            .filter(|a| match a.last_seen.elapsed() {
180                Ok(elapsed) => elapsed < *interval,
181                Err(_) => false,
182            })
183            .collect()
184    }
185
186    // Get a list of all tracked aircraft
187    pub fn get_all_aircraft(&self) -> Vec<&Aircraft> {
188        self.map.values().collect()
189    }
190}