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