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