1#![allow(rustdoc::missing_crate_level_docs)]
2
3use pyo3::exceptions::{PyAssertionError, PyValueError};
4use pyo3::prelude::*;
5use pyo3::types::PyDict;
6use rayon::prelude::*;
7use rs1090::data::patterns::{
8 aircraft_information as patterns, AircraftInformation,
9};
10use rs1090::decode::bds::bds05::AirbornePosition;
11use rs1090::decode::bds::bds10::DataLinkCapability;
12use rs1090::decode::bds::bds17::CommonUsageGICBCapabilityReport;
13use rs1090::decode::bds::bds18::GICBCapabilityReportPart1;
14use rs1090::decode::bds::bds19::GICBCapabilityReportPart2;
15use rs1090::decode::bds::bds20::AircraftIdentification;
16use rs1090::decode::bds::bds21::AircraftAndAirlineRegistrationMarkings;
17use rs1090::decode::bds::bds30::ACASResolutionAdvisory;
18use rs1090::decode::bds::bds40::SelectedVerticalIntention;
19use rs1090::decode::bds::bds44::MeteorologicalRoutineAirReport;
20use rs1090::decode::bds::bds45::MeteorologicalHazardReport;
21use rs1090::decode::bds::bds50::TrackAndTurnReport;
22use rs1090::decode::bds::bds60::HeadingAndSpeedReport;
23use rs1090::decode::bds::bds65::AircraftOperationStatus;
24use rs1090::decode::cpr::{
25 airborne_position_with_reference, decode_positions,
26 surface_position_with_reference, Position,
27};
28use rs1090::decode::flarm::Flarm;
29use rs1090::prelude::*;
30
31#[pyfunction]
32fn decode_1090(msg: String) -> PyResult<Vec<u8>> {
33 let bytes = hex::decode(msg).unwrap();
34 if let Ok((_, msg)) = Message::from_bytes((&bytes, 0)) {
35 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
36 Ok(pkl)
37 } else {
38 Ok([128, 4, 78, 46].to_vec()) }
40}
41
42fn decode_message_with_reference(me: &mut ME, reference: [f64; 2]) {
43 let [latitude_ref, longitude_ref] = reference;
44 match me {
45 ME::BDS05(airborne) => {
46 if let Some(pos) = airborne_position_with_reference(
47 airborne,
48 latitude_ref,
49 longitude_ref,
50 ) {
51 airborne.latitude = Some(pos.latitude);
52 airborne.longitude = Some(pos.longitude);
53 }
54 }
55 ME::BDS06(surface) => {
56 if let Some(pos) = surface_position_with_reference(
57 surface,
58 latitude_ref,
59 longitude_ref,
60 ) {
61 surface.latitude = Some(pos.latitude);
62 surface.longitude = Some(pos.longitude);
63 }
64 }
65 _ => (),
66 }
67}
68
69#[pyfunction]
70fn decode_1090_with_reference(
71 msg: String,
72 reference: [f64; 2],
73) -> PyResult<Vec<u8>> {
74 let bytes = hex::decode(msg).unwrap();
75 if let Ok((_, mut msg)) = Message::from_bytes((&bytes, 0)) {
76 match &mut msg.df {
77 ExtendedSquitterTisB { cf, .. } => {
78 decode_message_with_reference(&mut cf.me, reference)
79 }
80 ExtendedSquitterADSB(adsb) => {
81 decode_message_with_reference(&mut adsb.message, reference)
82 }
83 _ => {}
84 }
85 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
86 Ok(pkl)
87 } else {
88 Ok([128, 4, 78, 46].to_vec()) }
90}
91
92struct DecodeError(DekuError);
93
94impl From<DecodeError> for PyErr {
95 fn from(error: DecodeError) -> Self {
96 match error.0 {
97 DekuError::Assertion(msg) => PyAssertionError::new_err(msg),
98 _ => PyValueError::new_err(error.0.to_string()),
99 }
100 }
101}
102
103#[pyfunction]
104fn decode_bds05(msg: String) -> PyResult<Vec<u8>> {
105 let bytes = hex::decode(msg).unwrap();
106 let tc = &bytes[4] >> 3;
107 if (9..22).contains(&tc) && tc != 19 {
108 match AirbornePosition::from_bytes((&bytes[4..], 0)) {
109 Ok((_, msg)) => {
110 let pkl =
111 serde_pickle::to_vec(&msg, Default::default()).unwrap();
112 Ok(pkl)
113 }
114 Err(e) => Err(DecodeError(e).into()),
115 }
116 } else {
117 let msg =
118 format!("Invalid typecode {tc} for BDS 0,5 (9 to 18 or 20 to 22)");
119 Err(PyAssertionError::new_err(msg))
120 }
121}
122
123#[pyfunction]
124fn decode_bds10(msg: String) -> PyResult<Vec<u8>> {
125 let bytes = hex::decode(msg).unwrap();
126 match DataLinkCapability::from_bytes((&bytes[4..], 0)) {
127 Ok((_, msg)) => {
128 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
129 Ok(pkl)
130 }
131 Err(e) => Err(DecodeError(e).into()),
132 }
133}
134
135#[pyfunction]
136fn decode_bds17(msg: String) -> PyResult<Vec<u8>> {
137 let bytes = hex::decode(msg).unwrap();
138 match CommonUsageGICBCapabilityReport::from_bytes((&bytes[4..], 0)) {
139 Ok((_, msg)) => {
140 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
141 Ok(pkl)
142 }
143 Err(e) => Err(DecodeError(e).into()),
144 }
145}
146
147#[pyfunction]
148fn decode_bds18(msg: String) -> PyResult<Vec<u8>> {
149 let bytes = hex::decode(msg).unwrap();
150 match GICBCapabilityReportPart1::from_bytes((&bytes[4..], 0)) {
151 Ok((_, msg)) => {
152 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
153 Ok(pkl)
154 }
155 Err(e) => Err(DecodeError(e).into()),
156 }
157}
158
159#[pyfunction]
160fn decode_bds19(msg: String) -> PyResult<Vec<u8>> {
161 let bytes = hex::decode(msg).unwrap();
162 match GICBCapabilityReportPart2::from_bytes((&bytes[4..], 0)) {
163 Ok((_, msg)) => {
164 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
165 Ok(pkl)
166 }
167 Err(e) => Err(DecodeError(e).into()),
168 }
169}
170
171#[pyfunction]
172fn decode_bds20(msg: String) -> PyResult<Vec<u8>> {
173 let bytes = hex::decode(msg).unwrap();
174 match AircraftIdentification::from_bytes((&bytes[4..], 0)) {
175 Ok((_, msg)) => {
176 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
177 Ok(pkl)
178 }
179 Err(e) => Err(DecodeError(e).into()),
180 }
181}
182
183#[pyfunction]
184fn decode_bds21(msg: String) -> PyResult<Vec<u8>> {
185 let bytes = hex::decode(msg).unwrap();
186 match AircraftAndAirlineRegistrationMarkings::from_bytes((&bytes[4..], 0)) {
187 Ok((_, msg)) => {
188 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
189 Ok(pkl)
190 }
191 Err(e) => Err(DecodeError(e).into()),
192 }
193}
194
195#[pyfunction]
196fn decode_bds30(msg: String) -> PyResult<Vec<u8>> {
197 let bytes = hex::decode(msg).unwrap();
198 match ACASResolutionAdvisory::from_bytes((&bytes[4..], 0)) {
199 Ok((_, msg)) => {
200 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
201 Ok(pkl)
202 }
203 Err(e) => Err(DecodeError(e).into()),
204 }
205}
206
207#[pyfunction]
208fn decode_bds40(msg: String) -> PyResult<Vec<u8>> {
209 let bytes = hex::decode(msg).unwrap();
210 match SelectedVerticalIntention::from_bytes((&bytes[4..], 0)) {
211 Ok((_, msg)) => {
212 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
213 Ok(pkl)
214 }
215 Err(e) => Err(DecodeError(e).into()),
216 }
217}
218
219#[pyfunction]
220fn decode_bds44(msg: String) -> PyResult<Vec<u8>> {
221 let bytes = hex::decode(msg).unwrap();
222 match MeteorologicalRoutineAirReport::from_bytes((&bytes[4..], 0)) {
223 Ok((_, msg)) => {
224 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
225 Ok(pkl)
226 }
227 Err(e) => Err(DecodeError(e).into()),
228 }
229}
230#[pyfunction]
231fn decode_bds45(msg: String) -> PyResult<Vec<u8>> {
232 let bytes = hex::decode(msg).unwrap();
233 match MeteorologicalHazardReport::from_bytes((&bytes[4..], 0)) {
234 Ok((_, msg)) => {
235 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
236 Ok(pkl)
237 }
238 Err(e) => Err(DecodeError(e).into()),
239 }
240}
241
242#[pyfunction]
243fn decode_bds50(msg: String) -> PyResult<Vec<u8>> {
244 let bytes = hex::decode(msg).unwrap();
245 match TrackAndTurnReport::from_bytes((&bytes[4..], 0)) {
246 Ok((_, msg)) => {
247 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
248 Ok(pkl)
249 }
250 Err(e) => Err(DecodeError(e).into()),
251 }
252}
253
254#[pyfunction]
255fn decode_bds60(msg: String) -> PyResult<Vec<u8>> {
256 let bytes = hex::decode(msg).unwrap();
257 match HeadingAndSpeedReport::from_bytes((&bytes[4..], 0)) {
258 Ok((_, msg)) => {
259 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
260 Ok(pkl)
261 }
262 Err(e) => Err(DecodeError(e).into()),
263 }
264}
265
266#[pyfunction]
267fn decode_bds65(msg: String) -> PyResult<Vec<u8>> {
268 let bytes = hex::decode(msg).unwrap();
269 let tc = &bytes[4] >> 3;
270 let enum_id = &bytes[4] & 0b111;
271 match (tc, enum_id) {
272 (31, id) if id < 2 => {
273 match AircraftOperationStatus::from_bytes((&bytes[4..], 0)) {
274 Ok((_, msg)) => {
275 let pkl =
276 serde_pickle::to_vec(&msg, Default::default()).unwrap();
277 Ok(pkl)
278 }
279 Err(e) => Err(DecodeError(e).into()),
280 }
281 }
282 _ => Err(PyAssertionError::new_err(format!(
283 "Invalid typecode {tc} (31) or category {enum_id} (0 or 1) for BDS 6,5"
284 ))),
285 }
286}
287
288#[pyfunction]
289fn decode_1090_vec(msgs_set: Vec<Vec<String>>) -> PyResult<Vec<u8>> {
290 let res: Vec<Option<Message>> = msgs_set
291 .par_iter()
292 .map(|msgs| {
293 msgs.iter()
294 .map(|msg| {
295 let bytes = hex::decode(msg).unwrap();
296 if let Ok((_, msg)) = Message::from_bytes((&bytes, 0)) {
297 Some(msg)
298 } else {
299 None
300 }
301 })
302 .collect()
303 })
304 .flat_map(|v: Vec<Option<Message>>| v)
305 .collect();
306 let pkl = serde_pickle::to_vec(&res, Default::default()).unwrap();
307 Ok(pkl)
308}
309
310#[pyfunction]
311#[pyo3(signature = (msgs_set, ts_set, reference=None))]
312fn decode_1090t_vec(
313 msgs_set: Vec<Vec<String>>,
314 ts_set: Vec<Vec<f64>>,
315 reference: Option<[f64; 2]>,
316) -> PyResult<Vec<u8>> {
317 let mut res: Vec<TimedMessage> = msgs_set
318 .par_iter()
319 .zip(ts_set)
320 .map(|(msgs, ts)| {
321 msgs.iter()
322 .zip(ts)
323 .filter_map(|(msg, timestamp)| {
324 let bytes = hex::decode(msg).unwrap();
325 if let Ok((_, message)) = Message::from_bytes((&bytes, 0)) {
326 Some(TimedMessage {
327 timestamp,
328 frame: bytes,
329 message: Some(message),
330 metadata: vec![],
331 decode_time: None,
332 })
333 } else {
334 None
335 }
336 })
337 .collect()
338 })
339 .flat_map(|v: Vec<TimedMessage>| v)
340 .collect();
341
342 let position = reference.map(|[latitude, longitude]| Position {
343 latitude,
344 longitude,
345 });
346 decode_positions(&mut res, position, &None);
347
348 let pkl = serde_pickle::to_vec(&res, Default::default()).unwrap();
349 Ok(pkl)
350}
351
352#[pyfunction]
353fn decode_flarm(
354 msg: String,
355 ts: u32,
356 reflat: f64,
357 reflon: f64,
358) -> PyResult<Vec<u8>> {
359 let bytes = hex::decode(msg).unwrap();
360 let reference = [reflat, reflon];
361 if let Ok(msg) = Flarm::from_record(ts, &reference, &bytes) {
362 let pkl = serde_pickle::to_vec(&msg, Default::default()).unwrap();
363 Ok(pkl)
364 } else {
365 Ok([128, 4, 78, 46].to_vec()) }
367}
368
369#[pyfunction]
370fn decode_flarm_vec(
371 msgs_set: Vec<Vec<String>>,
372 ts_set: Vec<Vec<u32>>,
373 ref_lat: Vec<Vec<f64>>,
374 ref_lon: Vec<Vec<f64>>,
375) -> PyResult<Vec<u8>> {
376 let reference: Vec<Vec<[f64; 2]>> = ref_lat
377 .iter()
378 .zip(ref_lon.iter())
379 .map(|(lat, lon)| {
380 lat.iter()
381 .zip(lon.iter())
382 .map(|(lat, lon)| [*lat, *lon])
383 .collect()
384 })
385 .collect();
386 let res: Vec<Flarm> = msgs_set
387 .par_iter()
388 .zip(ts_set)
389 .zip(reference)
390 .map(|((msgs, ts), reference)| {
391 msgs.iter()
392 .zip(ts)
393 .zip(reference)
394 .filter_map(|((msg, timestamp), reference)| {
395 let bytes = hex::decode(msg).unwrap();
396 Flarm::from_record(timestamp, &reference, &bytes).ok()
397 })
398 .collect()
399 })
400 .flat_map(|v: Vec<Flarm>| v)
401 .collect();
402
403 let pkl = serde_pickle::to_vec(&res, Default::default()).unwrap();
404 Ok(pkl)
405}
406
407struct WrapAircraftInfo(AircraftInformation);
408
409impl<'a> IntoPyObject<'a> for WrapAircraftInfo {
410 type Target = PyDict;
411 type Output = Bound<'a, Self::Target>;
412 type Error = PyErr;
413
414 fn into_pyobject(
415 self,
416 py: Python<'a>,
417 ) -> Result<Self::Output, Self::Error> {
418 let dict = PyDict::new(py);
419 dict.set_item("icao24", self.0.icao24)?;
420 if let Some(registration) = self.0.registration {
421 dict.set_item("registration", registration)?;
422 }
423 dict.set_item(
424 "country",
425 self.0.country.or(Some("Unknown".to_string())),
426 )?;
427 dict.set_item("flag", self.0.flag.or(Some("🏳".to_string())))?;
428 if let Some(pattern) = self.0.pattern {
429 dict.set_item("pattern", pattern)?;
430 }
431 if let Some(category) = self.0.category {
432 dict.set_item("category", category)?;
433 }
434 if let Some(comment) = self.0.comment {
435 dict.set_item("comment", comment)?;
436 }
437 Ok(dict)
438 }
439}
440
441#[pyfunction]
442#[pyo3(signature = (icao24, registration=None))]
443fn aircraft_information(
444 icao24: &str,
445 registration: Option<&str>,
446) -> PyResult<WrapAircraftInfo> {
447 Ok(WrapAircraftInfo(patterns(icao24, registration)?))
448}
449
450#[pymodule]
452fn _rust(m: &Bound<'_, PyModule>) -> PyResult<()> {
453 m.add_function(wrap_pyfunction!(decode_1090, m)?)?;
455 m.add_function(wrap_pyfunction!(decode_1090_with_reference, m)?)?;
456 m.add_function(wrap_pyfunction!(decode_1090_vec, m)?)?;
457 m.add_function(wrap_pyfunction!(decode_1090t_vec, m)?)?;
458 m.add_function(wrap_pyfunction!(decode_flarm, m)?)?;
459 m.add_function(wrap_pyfunction!(decode_flarm_vec, m)?)?;
460
461 m.add_function(wrap_pyfunction!(decode_bds05, m)?)?;
463 m.add_function(wrap_pyfunction!(decode_bds10, m)?)?;
464 m.add_function(wrap_pyfunction!(decode_bds17, m)?)?;
465 m.add_function(wrap_pyfunction!(decode_bds18, m)?)?;
466 m.add_function(wrap_pyfunction!(decode_bds19, m)?)?;
467 m.add_function(wrap_pyfunction!(decode_bds20, m)?)?;
468 m.add_function(wrap_pyfunction!(decode_bds21, m)?)?;
469 m.add_function(wrap_pyfunction!(decode_bds30, m)?)?;
470 m.add_function(wrap_pyfunction!(decode_bds40, m)?)?;
471 m.add_function(wrap_pyfunction!(decode_bds44, m)?)?;
472 m.add_function(wrap_pyfunction!(decode_bds45, m)?)?;
473 m.add_function(wrap_pyfunction!(decode_bds50, m)?)?;
474 m.add_function(wrap_pyfunction!(decode_bds60, m)?)?;
475 m.add_function(wrap_pyfunction!(decode_bds65, m)?)?;
476
477 m.add_function(wrap_pyfunction!(aircraft_information, m)?)?;
479
480 Ok(())
481}