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