rs1090_wasm/
lib.rs

1#![allow(rustdoc::missing_crate_level_docs)]
2
3mod utils;
4
5use js_sys::Object;
6use rs1090::data::airports::AIRPORTS;
7use rs1090::data::patterns::aircraft_information as patterns;
8use rs1090::decode::bds::bds05::AirbornePosition;
9use rs1090::decode::bds::bds10::DataLinkCapability;
10use rs1090::decode::bds::bds17::CommonUsageGICBCapabilityReport;
11use rs1090::decode::bds::bds18::GICBCapabilityReportPart1;
12use rs1090::decode::bds::bds19::GICBCapabilityReportPart2;
13use rs1090::decode::bds::bds20::AircraftIdentification;
14use rs1090::decode::bds::bds21::AircraftAndAirlineRegistrationMarkings;
15use rs1090::decode::bds::bds30::ACASResolutionAdvisory;
16use rs1090::decode::bds::bds40::SelectedVerticalIntention;
17use rs1090::decode::bds::bds44::MeteorologicalRoutineAirReport;
18use rs1090::decode::bds::bds45::MeteorologicalHazardReport;
19use rs1090::decode::bds::bds50::TrackAndTurnReport;
20use rs1090::decode::bds::bds60::HeadingAndSpeedReport;
21use rs1090::decode::cpr::{
22    airborne_position_with_reference, surface_position_with_reference,
23};
24use rs1090::prelude::*;
25use utils::set_panic_hook;
26use wasm_bindgen::prelude::*;
27
28#[wasm_bindgen]
29pub fn run() -> Result<(), JsValue> {
30    set_panic_hook();
31    Ok(())
32}
33
34struct DecodeError(DekuError);
35
36impl From<DecodeError> for JsError {
37    fn from(error: DecodeError) -> Self {
38        JsError::new(&format!("{}", error.0))
39    }
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#[wasm_bindgen]
70pub fn decode(
71    msg: &str,
72    reference: Option<Vec<f64>>,
73) -> Result<JsValue, JsError> {
74    let bytes = hex::decode(msg)?;
75    match Message::try_from(bytes.as_slice()) {
76        Ok(mut msg) => {
77            if let Some(reference) = reference.map(|v| [v[0], v[1]]) {
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(
84                            &mut adsb.message,
85                            reference,
86                        )
87                    }
88                    _ => {}
89                }
90            }
91            let map_result = serde_wasm_bindgen::to_value(&msg)?;
92            Ok(Object::from_entries(&map_result).unwrap().into())
93        }
94        Err(e) => Err(DecodeError(e).into()),
95    }
96}
97
98#[wasm_bindgen]
99pub fn decode_bds05(msg: &str) -> Result<JsValue, JsError> {
100    let bytes = hex::decode(msg)?;
101    let tc = &bytes[4] >> 3;
102    if (9..22).contains(&tc) && tc != 19 {
103        match AirbornePosition::from_bytes((&bytes[4..], 0)) {
104            Ok((_, msg)) => {
105                let map_result = serde_wasm_bindgen::to_value(&msg)?;
106                Ok(map_result)
107            }
108            Err(e) => Err(DecodeError(e).into()),
109        }
110    } else {
111        let msg = format!(
112            "Invalid typecode {} for BDS 0,5 (9 to 18 or 20 to 22)",
113            tc
114        );
115        Err(DecodeError(DekuError::Assertion(msg.into())).into())
116    }
117}
118
119#[wasm_bindgen]
120pub fn decode_bds10(msg: &str) -> Result<JsValue, JsError> {
121    let bytes = hex::decode(msg)?;
122    match DataLinkCapability::from_bytes((&bytes[4..], 0)) {
123        Ok((_, msg)) => {
124            let map_result = serde_wasm_bindgen::to_value(&msg)?;
125            Ok(map_result)
126        }
127        Err(e) => Err(DecodeError(e).into()),
128    }
129}
130
131#[wasm_bindgen]
132pub fn decode_bds17(msg: &str) -> Result<JsValue, JsError> {
133    let bytes = hex::decode(msg)?;
134    match CommonUsageGICBCapabilityReport::from_bytes((&bytes[4..], 0)) {
135        Ok((_, msg)) => {
136            let map_result = serde_wasm_bindgen::to_value(&msg)?;
137            Ok(map_result)
138        }
139        Err(e) => Err(DecodeError(e).into()),
140    }
141}
142
143#[wasm_bindgen]
144pub fn decode_bds18(msg: &str) -> Result<JsValue, JsError> {
145    let bytes = hex::decode(msg)?;
146    match GICBCapabilityReportPart1::from_bytes((&bytes[4..], 0)) {
147        Ok((_, msg)) => {
148            let map_result = serde_wasm_bindgen::to_value(&msg)?;
149            Ok(map_result)
150        }
151        Err(e) => Err(DecodeError(e).into()),
152    }
153}
154
155#[wasm_bindgen]
156pub fn decode_bds19(msg: &str) -> Result<JsValue, JsError> {
157    let bytes = hex::decode(msg)?;
158    match GICBCapabilityReportPart2::from_bytes((&bytes[4..], 0)) {
159        Ok((_, msg)) => {
160            let map_result = serde_wasm_bindgen::to_value(&msg)?;
161            Ok(map_result)
162        }
163        Err(e) => Err(DecodeError(e).into()),
164    }
165}
166
167#[wasm_bindgen]
168pub fn decode_bds20(msg: &str) -> Result<JsValue, JsError> {
169    let bytes = hex::decode(msg)?;
170    match AircraftIdentification::from_bytes((&bytes[4..], 0)) {
171        Ok((_, msg)) => {
172            let map_result = serde_wasm_bindgen::to_value(&msg)?;
173            Ok(map_result)
174        }
175        Err(e) => Err(DecodeError(e).into()),
176    }
177}
178
179#[wasm_bindgen]
180pub fn decode_bds21(msg: &str) -> Result<JsValue, JsError> {
181    let bytes = hex::decode(msg)?;
182    match AircraftAndAirlineRegistrationMarkings::from_bytes((&bytes[4..], 0)) {
183        Ok((_, msg)) => {
184            let map_result = serde_wasm_bindgen::to_value(&msg)?;
185            Ok(map_result)
186        }
187        Err(e) => Err(DecodeError(e).into()),
188    }
189}
190
191#[wasm_bindgen]
192pub fn decode_bds30(msg: &str) -> Result<JsValue, JsError> {
193    let bytes = hex::decode(msg)?;
194    match ACASResolutionAdvisory::from_bytes((&bytes[4..], 0)) {
195        Ok((_, msg)) => {
196            let map_result = serde_wasm_bindgen::to_value(&msg)?;
197            Ok(map_result)
198        }
199        Err(e) => Err(DecodeError(e).into()),
200    }
201}
202
203#[wasm_bindgen]
204pub fn decode_bds40(msg: &str) -> Result<JsValue, JsError> {
205    let bytes = hex::decode(msg)?;
206    match SelectedVerticalIntention::from_bytes((&bytes[4..], 0)) {
207        Ok((_, msg)) => {
208            let map_result = serde_wasm_bindgen::to_value(&msg)?;
209            Ok(map_result)
210        }
211        Err(e) => Err(DecodeError(e).into()),
212    }
213}
214
215#[wasm_bindgen]
216pub fn decode_bds44(msg: &str) -> Result<JsValue, JsError> {
217    let bytes = hex::decode(msg)?;
218    match MeteorologicalRoutineAirReport::from_bytes((&bytes[4..], 0)) {
219        Ok((_, msg)) => {
220            let map_result = serde_wasm_bindgen::to_value(&msg)?;
221            Ok(map_result)
222        }
223        Err(e) => Err(DecodeError(e).into()),
224    }
225}
226
227#[wasm_bindgen]
228pub fn decode_bds45(msg: &str) -> Result<JsValue, JsError> {
229    let bytes = hex::decode(msg)?;
230    match MeteorologicalHazardReport::from_bytes((&bytes[4..], 0)) {
231        Ok((_, msg)) => {
232            let map_result = serde_wasm_bindgen::to_value(&msg)?;
233            Ok(map_result)
234        }
235        Err(e) => Err(DecodeError(e).into()),
236    }
237}
238
239#[wasm_bindgen]
240pub fn decode_bds50(msg: &str) -> Result<JsValue, JsError> {
241    let bytes = hex::decode(msg)?;
242    match TrackAndTurnReport::from_bytes((&bytes[4..], 0)) {
243        Ok((_, msg)) => {
244            let map_result = serde_wasm_bindgen::to_value(&msg)?;
245            Ok(map_result)
246        }
247        Err(e) => Err(DecodeError(e).into()),
248    }
249}
250
251#[wasm_bindgen]
252pub fn decode_bds60(msg: &str) -> Result<JsValue, JsError> {
253    let bytes = hex::decode(msg)?;
254    match HeadingAndSpeedReport::from_bytes((&bytes[4..], 0)) {
255        Ok((_, msg)) => {
256            let map_result = serde_wasm_bindgen::to_value(&msg)?;
257            Ok(map_result)
258        }
259        Err(e) => Err(DecodeError(e).into()),
260    }
261}
262
263#[wasm_bindgen]
264pub fn decode_bds65(msg: &str) -> Result<JsValue, JsError> {
265    let bytes = hex::decode(msg)?;
266    let tc = &bytes[4] >> 3;
267    let enum_id = &bytes[4] & 0b111;
268    match (tc, enum_id) {
269        (31, id) if id < 2 => {
270            match AircraftOperationStatus::from_bytes((&bytes[4..], 0)) {
271                Ok((_, msg)) => {
272                    let map_result = serde_wasm_bindgen::to_value(&msg)?;
273                    Ok(map_result)
274                }
275                Err(e) => Err(DecodeError(e).into()),
276            }
277        }
278        _ => {
279            let msg = format!(
280                "Invalid typecode {} (31) or category {} (0 or 1) for BDS 6,5",
281                tc, enum_id
282            );
283            Err(DecodeError(DekuError::Assertion(msg.into())).into())
284        }
285    }
286}
287
288#[wasm_bindgen]
289pub fn aircraft_information(
290    icao24: &str,
291    registration: Option<String>,
292) -> Result<JsValue, JsError> {
293    match patterns(icao24, registration.as_deref()) {
294        Ok(res) => {
295            let js_result = serde_wasm_bindgen::to_value(&res)?;
296            Ok(js_result)
297        }
298        Err(_) => Err(JsError::new("invalid icao24 value")),
299    }
300}
301
302#[wasm_bindgen]
303pub fn airport_information(query: &str) -> Result<JsValue, JsError> {
304    let lowercase = query.to_lowercase();
305    let res: Vec<_> = AIRPORTS
306        .iter()
307        .filter(|a| {
308            a.name.to_lowercase().contains(&lowercase)
309                || a.city.to_lowercase().contains(&lowercase)
310                || a.icao.to_lowercase().contains(&lowercase)
311                || a.iata.to_lowercase().contains(&lowercase)
312        })
313        .collect();
314    Ok(serde_wasm_bindgen::to_value(&res)?)
315}