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