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}