@airframes/acars-decoder 1.6.11 → 1.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -12,6 +12,74 @@ var IcaoDecoder = class {
12
12
  }
13
13
  };
14
14
 
15
+ // lib/DateTimeUtils.ts
16
+ var DateTimeUtils = class {
17
+ // Expects a four digit UTC time string (HHMM)
18
+ static UTCToString(UTCString) {
19
+ let utcDate = /* @__PURE__ */ new Date();
20
+ utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
21
+ return utcDate.toTimeString();
22
+ }
23
+ // Expects a six digit date string and a four digit UTC time string
24
+ // (DDMMYY) (HHMM)
25
+ static UTCDateTimeToString(dateString, timeString) {
26
+ let utcDate = /* @__PURE__ */ new Date();
27
+ utcDate.setUTCDate(+dateString.substr(0, 2));
28
+ utcDate.setUTCMonth(+dateString.substr(2, 2));
29
+ if (dateString.length === 6) {
30
+ utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
31
+ }
32
+ if (timeString.length === 6) {
33
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
34
+ } else {
35
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
36
+ }
37
+ return utcDate.toUTCString();
38
+ }
39
+ /**
40
+ *
41
+ * @param time HHMMSS
42
+ * @returns seconds since midnight
43
+ */
44
+ static convertHHMMSSToTod(time) {
45
+ const h = Number(time.substring(0, 2));
46
+ const m = Number(time.substring(2, 4));
47
+ const s = Number(time.substring(4, 6));
48
+ const tod = h * 3600 + m * 60 + s;
49
+ return tod;
50
+ }
51
+ /**
52
+ *
53
+ * @param time HHMMSS
54
+ * @param date MMDDYY or MMDDYYYY
55
+ * @returns seconds since epoch
56
+ */
57
+ static convertDateTimeToEpoch(time, date) {
58
+ if (date.length === 6) {
59
+ date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
60
+ }
61
+ const timestamp = `${date.substring(4, 8)}-${date.substring(0, 2)}-${date.substring(2, 4)}T${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}.000Z`;
62
+ const millis = Date.parse(timestamp);
63
+ return millis / 1e3;
64
+ }
65
+ /**
66
+ * Converts a timestamp to a string
67
+ *
68
+ * ISO-8601 format for 'epoch'
69
+ * HH:MM:SS for 'tod'
70
+ * @param time
71
+ * @param format
72
+ * @returns
73
+ */
74
+ static timestampToString(time, format) {
75
+ const date = new Date(time * 1e3);
76
+ if (format == "tod") {
77
+ return date.toISOString().slice(11, 19);
78
+ }
79
+ return date.toISOString().slice(0, -5) + "Z";
80
+ }
81
+ };
82
+
15
83
  // lib/DecoderPlugin.ts
16
84
  var DecoderPlugin = class {
17
85
  decoder;
@@ -130,8 +198,124 @@ var CoordinateUtils = class {
130
198
  }
131
199
  };
132
200
 
201
+ // lib/utils/route_utils.ts
202
+ var RouteUtils = class _RouteUtils {
203
+ static formatFlightState(state) {
204
+ switch (state) {
205
+ case "TO":
206
+ return "Takeoff";
207
+ case "IC":
208
+ return "Initial Climb";
209
+ case "CL":
210
+ return "Climb";
211
+ case "ER":
212
+ return "En Route";
213
+ case "DC":
214
+ return "Descent";
215
+ case "AP":
216
+ return "Approach";
217
+ default:
218
+ return `Unknown ${state}`;
219
+ }
220
+ }
221
+ static routeToString(route) {
222
+ let str = "";
223
+ if (route.name) {
224
+ str += route.name;
225
+ }
226
+ if (route.runway) {
227
+ str += `(${route.runway})`;
228
+ }
229
+ if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
230
+ str += " starting at ";
231
+ } else if (str.length !== 0 && route.waypoints) {
232
+ str += ": ";
233
+ }
234
+ if (route.waypoints) {
235
+ str += _RouteUtils.waypointsToString(route.waypoints);
236
+ }
237
+ return str;
238
+ }
239
+ static waypointToString(waypoint) {
240
+ let s = waypoint.name;
241
+ if (waypoint.latitude && waypoint.longitude) {
242
+ s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
243
+ }
244
+ if (waypoint.offset) {
245
+ s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
246
+ }
247
+ if (waypoint.time && waypoint.timeFormat) {
248
+ s += `@${DateTimeUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
249
+ }
250
+ return s;
251
+ }
252
+ static getWaypoint(leg) {
253
+ const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
254
+ if (regex?.length == 4) {
255
+ return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
256
+ }
257
+ const waypoint = leg.split(",");
258
+ if (waypoint.length == 2) {
259
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
260
+ if (position) {
261
+ return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
262
+ }
263
+ }
264
+ if (leg.length == 13 || leg.length == 14) {
265
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
266
+ const name = waypoint.length == 2 ? waypoint[0] : "";
267
+ if (position) {
268
+ return { name, latitude: position.latitude, longitude: position.longitude };
269
+ }
270
+ }
271
+ return { name: leg };
272
+ }
273
+ static waypointsToString(waypoints) {
274
+ let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
275
+ if (str.startsWith(" > ")) {
276
+ str = ">>" + str.slice(2);
277
+ }
278
+ return str;
279
+ }
280
+ };
281
+
133
282
  // lib/utils/result_formatter.ts
134
283
  var ResultFormatter = class {
284
+ static state_change(decodeResult, from, to) {
285
+ decodeResult.raw.state_change = {
286
+ from,
287
+ to
288
+ };
289
+ from = RouteUtils.formatFlightState(from);
290
+ to = RouteUtils.formatFlightState(to);
291
+ decodeResult.formatted.items.push({
292
+ type: "state_change",
293
+ code: "STATE_CHANGE",
294
+ label: "State Change",
295
+ value: `${from} -> ${to}`
296
+ });
297
+ }
298
+ static freetext(decodeResult, value) {
299
+ decodeResult.raw.freetext = value;
300
+ decodeResult.formatted.items.push({
301
+ type: "freetext",
302
+ code: "FREE_TEXT",
303
+ label: "Free Text",
304
+ value
305
+ });
306
+ }
307
+ static door_event(decodeResult, name, state) {
308
+ decodeResult.raw.door_event = {
309
+ door: name,
310
+ state
311
+ };
312
+ decodeResult.formatted.items.push({
313
+ type: "door_event",
314
+ code: "DOOR",
315
+ label: "Door Event",
316
+ value: `${name} ${state}`
317
+ });
318
+ }
135
319
  static position(decodeResult, value) {
136
320
  decodeResult.raw.position = value;
137
321
  decodeResult.formatted.items.push({
@@ -152,16 +336,41 @@ var ResultFormatter = class {
152
336
  }
153
337
  static flightNumber(decodeResult, value) {
154
338
  decodeResult.raw.flight_number = value;
339
+ decodeResult.formatted.items.push({
340
+ type: "flight_number",
341
+ code: "FLIGHT",
342
+ label: "Flight Number",
343
+ value: decodeResult.raw.flight_number
344
+ });
155
345
  }
156
- static departureAirport(decodeResult, value) {
157
- decodeResult.raw.departure_icao = value;
346
+ static callsign(decodeResult, value) {
347
+ decodeResult.raw.callsign = value;
158
348
  decodeResult.formatted.items.push({
159
- type: "origin",
160
- code: "ORG",
161
- label: "Origin",
162
- value: decodeResult.raw.departure_icao
349
+ type: "callsign",
350
+ code: "CALLSIGN",
351
+ label: "Callsign",
352
+ value: decodeResult.raw.callsign
163
353
  });
164
354
  }
355
+ static departureAirport(decodeResult, value, type = "ICAO") {
356
+ if (type === "ICAO") {
357
+ decodeResult.raw.departure_icao = value;
358
+ decodeResult.formatted.items.push({
359
+ type: "icao",
360
+ code: "ORG",
361
+ label: "Origin",
362
+ value
363
+ });
364
+ } else {
365
+ decodeResult.raw.departure_iata = value;
366
+ decodeResult.formatted.items.push({
367
+ type: "iata",
368
+ code: "ORG",
369
+ label: "Origin",
370
+ value
371
+ });
372
+ }
373
+ }
165
374
  static departureRunway(decodeResult, value) {
166
375
  decodeResult.raw.departure_runway = value;
167
376
  decodeResult.formatted.items.push({
@@ -171,33 +380,52 @@ var ResultFormatter = class {
171
380
  value: decodeResult.raw.departure_runway
172
381
  });
173
382
  }
174
- static arrivalAirport(decodeResult, value) {
175
- decodeResult.raw.arrival_icao = value;
176
- decodeResult.formatted.items.push({
177
- type: "destination",
178
- code: "DST",
179
- label: "Destination",
180
- value: decodeResult.raw.arrival_icao
181
- });
383
+ static arrivalAirport(decodeResult, value, type = "ICAO") {
384
+ if (type === "ICAO") {
385
+ decodeResult.raw.arrival_icao = value;
386
+ decodeResult.formatted.items.push({
387
+ type: "icao",
388
+ code: "DST",
389
+ label: "Destination",
390
+ value
391
+ });
392
+ } else {
393
+ decodeResult.raw.arrival_iata = value;
394
+ decodeResult.formatted.items.push({
395
+ type: "iata",
396
+ code: "DST",
397
+ label: "Destination",
398
+ value
399
+ });
400
+ }
182
401
  }
183
402
  static alternateAirport(decodeResult, value) {
184
403
  decodeResult.raw.alternate_icao = value;
185
404
  decodeResult.formatted.items.push({
186
- type: "destination",
405
+ type: "icao",
187
406
  code: "ALT_DST",
188
407
  label: "Alternate Destination",
189
408
  value: decodeResult.raw.alternate_icao
190
409
  });
191
410
  }
192
- // FIXME - make seconds since midnight for time of day
193
- static eta(decodeResult, value) {
194
- decodeResult.raw.eta_time = value;
195
- decodeResult.formatted.items.push({
196
- type: "eta",
197
- code: "ETA",
198
- label: "Estimated Time of Arrival",
199
- value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
200
- });
411
+ static eta(decodeResult, time, type = "tod") {
412
+ if (type === "tod") {
413
+ decodeResult.raw.eta_time = time;
414
+ decodeResult.formatted.items.push({
415
+ type: "time_of_day",
416
+ code: "ETA",
417
+ label: "Estimated Time of Arrival",
418
+ value: DateTimeUtils.timestampToString(time, "tod")
419
+ });
420
+ } else {
421
+ decodeResult.raw.eta_date = time;
422
+ decodeResult.formatted.items.push({
423
+ type: "epoch",
424
+ code: "ETA",
425
+ label: "Estimated Time of Arrival",
426
+ value: DateTimeUtils.timestampToString(time, "epoch")
427
+ });
428
+ }
201
429
  }
202
430
  static arrivalRunway(decodeResult, value) {
203
431
  decodeResult.raw.arrival_runway = value;
@@ -254,12 +482,12 @@ var ResultFormatter = class {
254
482
  });
255
483
  }
256
484
  static temperature(decodeResult, value) {
257
- decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
485
+ decodeResult.raw.outside_air_temperature = Number(value.replace("M", "-").replace("P", "+"));
258
486
  decodeResult.formatted.items.push({
259
487
  type: "outside_air_temperature",
260
488
  code: "OATEMP",
261
489
  label: "Outside Air Temperature (C)",
262
- value: `${decodeResult.raw.outside_air_temperature}`
490
+ value: `${decodeResult.raw.outside_air_temperature} degrees`
263
491
  });
264
492
  }
265
493
  static heading(decodeResult, value) {
@@ -282,42 +510,56 @@ var ResultFormatter = class {
282
510
  }
283
511
  static out(decodeResult, time) {
284
512
  decodeResult.raw.out_time = time;
285
- const date = new Date(time * 1e3);
286
513
  decodeResult.formatted.items.push({
287
514
  type: "time_of_day",
288
515
  code: "OUT",
289
516
  label: "Out of Gate Time",
290
- value: date.toISOString().slice(11, 19)
517
+ value: DateTimeUtils.timestampToString(time, "tod")
291
518
  });
292
519
  }
293
520
  static off(decodeResult, time) {
294
521
  decodeResult.raw.off_time = time;
295
- const date = new Date(time * 1e3);
296
522
  decodeResult.formatted.items.push({
297
523
  type: "time_of_day",
298
524
  code: "OFF",
299
525
  label: "Takeoff Time",
300
- value: date.toISOString().slice(11, 19)
526
+ value: DateTimeUtils.timestampToString(time, "tod")
301
527
  });
302
528
  }
303
529
  static on(decodeResult, time) {
304
530
  decodeResult.raw.on_time = time;
305
- const date = new Date(time * 1e3);
306
531
  decodeResult.formatted.items.push({
307
532
  type: "time_of_day",
308
533
  code: "ON",
309
534
  label: "Landing Time",
310
- value: date.toISOString().slice(11, 19)
535
+ value: DateTimeUtils.timestampToString(time, "tod")
311
536
  });
312
537
  }
313
538
  static in(decodeResult, time) {
314
539
  decodeResult.raw.in_time = time;
315
- const date = new Date(time * 1e3);
316
540
  decodeResult.formatted.items.push({
317
541
  type: "time_of_day",
318
542
  code: "IN",
319
543
  label: "In Gate Time",
320
- value: date.toISOString().slice(11, 19)
544
+ value: DateTimeUtils.timestampToString(time, "tod")
545
+ });
546
+ }
547
+ static time_of_day(decodeResult, time) {
548
+ decodeResult.raw.time_of_day = time;
549
+ decodeResult.formatted.items.push({
550
+ type: "time_of_day",
551
+ code: "MSG_TOD",
552
+ label: "Message Timestamp",
553
+ value: DateTimeUtils.timestampToString(time, "tod")
554
+ });
555
+ }
556
+ static text(decodeResult, text2) {
557
+ decodeResult.raw.text = text2;
558
+ decodeResult.formatted.items.push({
559
+ type: "text",
560
+ code: "TEXT",
561
+ label: "Text Message",
562
+ value: text2
321
563
  });
322
564
  }
323
565
  static unknown(decodeResult, value) {
@@ -325,9 +567,9 @@ var ResultFormatter = class {
325
567
  }
326
568
  };
327
569
 
328
- // lib/plugins/Label_5Z.ts
329
- var Label_5Z = class extends DecoderPlugin {
330
- name = "label-5z";
570
+ // lib/plugins/Label_5Z_Slash.ts
571
+ var Label_5Z_Slash = class extends DecoderPlugin {
572
+ name = "label-5z-slash";
331
573
  descriptions = {
332
574
  B1: "Request Weight and Balance",
333
575
  B3: "Request Departure Clearance",
@@ -345,6 +587,7 @@ var Label_5Z = class extends DecoderPlugin {
345
587
  D6: "From-To + Date",
346
588
  D7: "From-To + Alternate + Time",
347
589
  EO: "In Range",
590
+ ET: "Expected Time",
348
591
  PW: "Position Weather",
349
592
  RL: "Request Release",
350
593
  R3: "Request HOWGOZIT Message",
@@ -355,48 +598,82 @@ var Label_5Z = class extends DecoderPlugin {
355
598
  };
356
599
  qualifiers() {
357
600
  return {
358
- labels: ["5Z"]
601
+ labels: ["5Z"],
602
+ preambles: ["/"]
359
603
  };
360
604
  }
361
605
  decode(message, options = {}) {
362
606
  const decodeResult = this.defaultResult();
363
607
  decodeResult.decoder.name = this.name;
364
608
  decodeResult.formatted.description = "Airline Designated Downlink";
365
- const uaRegex = /^\/(?<type>\w+) (?<remainder>.+)/;
366
- let results = message.text.match(uaRegex);
367
- if (results && results.length >= 2) {
368
- const type = results.groups.type.split("/")[0];
369
- const { remainder } = results.groups;
370
- const typeDescription = this.descriptions[type] ? this.descriptions[type] : "Unknown";
609
+ const lines = message.text.split("\r\n");
610
+ if (lines[0] === "/TXT") {
611
+ ResultFormatter.text(decodeResult, lines.slice(1).join("\r\n"));
612
+ decodeResult.decoded = true;
613
+ decodeResult.decoder.decodeLevel = "full";
614
+ return decodeResult;
615
+ }
616
+ const data = lines[0].split("/");
617
+ const header = data[1].split(" ");
618
+ const type = header[0];
619
+ const typeDescription = this.descriptions[type];
620
+ if (typeDescription) {
371
621
  decodeResult.raw.airline = "United Airlines";
372
622
  decodeResult.formatted.items.push({
373
623
  type: "airline",
624
+ code: "AIRLINE",
374
625
  label: "Airline",
375
626
  value: "United Airlines"
376
627
  });
377
628
  decodeResult.raw.message_type = type;
378
629
  decodeResult.formatted.items.push({
379
630
  type: "message_type",
631
+ code: "MSG_TYPE",
380
632
  label: "Message Type",
381
633
  value: `${typeDescription} (${type})`
382
634
  });
383
- if (type === "B3") {
384
- const rdcRegex = /^(?<from>\w\w\w)(?<to>\w\w\w) (?<unknown1>\d\d) R(?<runway>.+) G(?<unknown2>.+)$/;
385
- results = remainder.match(rdcRegex);
386
- if (results) {
387
- ResultFormatter.departureAirport(decodeResult, results.groups.from);
388
- ResultFormatter.arrivalAirport(decodeResult, results.groups.to);
389
- ResultFormatter.arrivalRunway(decodeResult, results.groups.runway);
390
- } else {
391
- if (options.debug) {
392
- console.log(`Decoder: Unkown 5Z RDC format: ${remainder}`);
393
- }
635
+ if (type === "B3" && data[1] === "B3 TO DATA REQ ") {
636
+ const info = data[2].split(" ");
637
+ ResultFormatter.departureAirport(decodeResult, info[1]);
638
+ ResultFormatter.arrivalAirport(decodeResult, info[2]);
639
+ decodeResult.raw.day_of_month = Number(info[3]);
640
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(info[4]));
641
+ ResultFormatter.arrivalRunway(decodeResult, info[5].slice(1));
642
+ decodeResult.remaining.text = data.slice(3).join("/");
643
+ } else if (type === "B3") {
644
+ ResultFormatter.departureAirport(decodeResult, header[1].substring(0, 3), "IATA");
645
+ ResultFormatter.arrivalAirport(decodeResult, header[1].substring(3), "IATA");
646
+ decodeResult.raw.day_of_month = Number(header[2]);
647
+ ResultFormatter.arrivalRunway(decodeResult, header[3].slice(1));
648
+ if (header.length > 4) {
649
+ decodeResult.remaining.text = header.slice(4).join(" ");
394
650
  }
651
+ } else if (type === "C3" && data[1] === "C3 GATE REQ ") {
652
+ const info = data[2].split(" ");
653
+ ResultFormatter.departureAirport(decodeResult, info[1]);
654
+ ResultFormatter.arrivalAirport(decodeResult, info[2]);
655
+ decodeResult.raw.day_of_month = Number(info[3]);
656
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(info[4]));
657
+ decodeResult.remaining.text = info.slice(5).join(" ");
658
+ } else if (type === "C3") {
659
+ ResultFormatter.departureAirport(decodeResult, header[1].substring(0, 3), "IATA");
660
+ ResultFormatter.arrivalAirport(decodeResult, header[1].substring(3), "IATA");
661
+ } else if (type === "ET") {
662
+ const airports = data[2].split(" ");
663
+ ResultFormatter.departureAirport(decodeResult, airports[1]);
664
+ ResultFormatter.arrivalAirport(decodeResult, airports[2]);
665
+ decodeResult.raw.day_of_month = Number(airports[3]);
666
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(airports[4]));
667
+ const estimates = data[3].split(" ");
668
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(estimates[1] + "00"));
669
+ decodeResult.remaining.text = estimates[2];
395
670
  } else {
396
- decodeResult.remaining.text = remainder;
671
+ if (options.debug) {
672
+ console.log(`Decoder: Unkown 5Z RDC format: ${message.text}`);
673
+ }
397
674
  }
398
675
  decodeResult.decoded = true;
399
- decodeResult.decoder.decodeLevel = "partial";
676
+ decodeResult.decoder.decodeLevel = decodeResult.remaining.text ? "partial" : "full";
400
677
  } else {
401
678
  if (options.debug) {
402
679
  console.log(`Decoder: Unknown 5Z message: ${message.text}`);
@@ -497,129 +774,6 @@ var Label_10_POS = class extends DecoderPlugin {
497
774
  }
498
775
  };
499
776
 
500
- // lib/DateTimeUtils.ts
501
- var DateTimeUtils = class {
502
- // Expects a four digit UTC time string (HHMM)
503
- static UTCToString(UTCString) {
504
- let utcDate = /* @__PURE__ */ new Date();
505
- utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
506
- return utcDate.toTimeString();
507
- }
508
- // Expects a six digit date string and a four digit UTC time string
509
- // (DDMMYY) (HHMM)
510
- static UTCDateTimeToString(dateString, timeString) {
511
- let utcDate = /* @__PURE__ */ new Date();
512
- utcDate.setUTCDate(+dateString.substr(0, 2));
513
- utcDate.setUTCMonth(+dateString.substr(2, 2));
514
- if (dateString.length === 6) {
515
- utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
516
- }
517
- if (timeString.length === 6) {
518
- utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
519
- } else {
520
- utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
521
- }
522
- return utcDate.toUTCString();
523
- }
524
- /**
525
- *
526
- * @param time HHMMSS
527
- * @returns seconds since midnight
528
- */
529
- static convertHHMMSSToTod(time) {
530
- const h = Number(time.substring(0, 2));
531
- const m = Number(time.substring(2, 4));
532
- const s = Number(time.substring(4, 6));
533
- const tod = h * 3600 + m * 60 + s;
534
- return tod;
535
- }
536
- /**
537
- *
538
- * @param time HHMMSS
539
- * @param date MMDDYY or MMDDYYYY
540
- * @returns seconds since epoch
541
- */
542
- static convertDateTimeToEpoch(time, date) {
543
- if (date.length === 6) {
544
- date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
545
- }
546
- const timestamp = `${date.substring(4, 8)}-${date.substring(0, 2)}-${date.substring(2, 4)}T${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}.000Z`;
547
- const millis = Date.parse(timestamp);
548
- return millis / 1e3;
549
- }
550
- };
551
-
552
- // lib/utils/route_utils.ts
553
- var RouteUtils = class _RouteUtils {
554
- static routeToString(route) {
555
- let str = "";
556
- if (route.name) {
557
- str += route.name;
558
- }
559
- if (route.runway) {
560
- str += `(${route.runway})`;
561
- }
562
- if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
563
- str += " starting at ";
564
- } else if (str.length !== 0 && route.waypoints) {
565
- str += ": ";
566
- }
567
- if (route.waypoints) {
568
- str += _RouteUtils.waypointsToString(route.waypoints);
569
- }
570
- return str;
571
- }
572
- static waypointToString(waypoint) {
573
- let s = waypoint.name;
574
- if (waypoint.latitude && waypoint.longitude) {
575
- s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
576
- }
577
- if (waypoint.offset) {
578
- s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
579
- }
580
- if (waypoint.time && waypoint.timeFormat) {
581
- s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
582
- }
583
- return s;
584
- }
585
- static getWaypoint(leg) {
586
- const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
587
- if (regex?.length == 4) {
588
- return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
589
- }
590
- const waypoint = leg.split(",");
591
- if (waypoint.length == 2) {
592
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
593
- if (position) {
594
- return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
595
- }
596
- }
597
- if (leg.length == 13 || leg.length == 14) {
598
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
599
- const name = waypoint.length == 2 ? waypoint[0] : "";
600
- if (position) {
601
- return { name, latitude: position.latitude, longitude: position.longitude };
602
- }
603
- }
604
- return { name: leg };
605
- }
606
- // move out if we want public
607
- static timestampToString(time, format) {
608
- const date = new Date(time * 1e3);
609
- if (format == "tod") {
610
- return date.toISOString().slice(11, 19);
611
- }
612
- return date.toISOString().slice(0, -5) + "Z";
613
- }
614
- static waypointsToString(waypoints) {
615
- let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
616
- if (str.startsWith(" > ")) {
617
- str = ">>" + str.slice(2);
618
- }
619
- return str;
620
- }
621
- };
622
-
623
777
  // lib/plugins/Label_10_Slash.ts
624
778
  var Label_10_Slash = class extends DecoderPlugin {
625
779
  // eslint-disable-line camelcase
@@ -655,7 +809,7 @@ var Label_10_Slash = class extends DecoderPlugin {
655
809
  ResultFormatter.heading(decodeResult, Number(parts[5]));
656
810
  ResultFormatter.altitude(decodeResult, 100 * Number(parts[6]));
657
811
  ResultFormatter.arrivalAirport(decodeResult, parts[7]);
658
- ResultFormatter.eta(decodeResult, parts[8] + "00");
812
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[8] + "00"));
659
813
  const waypoints = [{
660
814
  name: parts[11]
661
815
  }, {
@@ -989,28 +1143,12 @@ var Label_1M_Slash = class extends DecoderPlugin {
989
1143
  console.log(results);
990
1144
  }
991
1145
  decodeResult.raw.flight_number = results[0];
992
- decodeResult.raw.departure_icao = results[3];
993
- decodeResult.raw.arrival_icao = results[4];
994
- decodeResult.raw.alternate_icao = results[5];
995
- decodeResult.raw.arrival_runway = results[8].replace(decodeResult.raw.arrival_icao, "");
996
- decodeResult.formatted.items.push({
997
- type: "eta",
998
- code: "ETA",
999
- label: "Estimated Time of Arrival",
1000
- value: DateTimeUtils.UTCDateTimeToString(results[2], results[7])
1001
- });
1002
- decodeResult.formatted.items.push({
1003
- type: "destination",
1004
- code: "DST",
1005
- label: "Destination",
1006
- value: decodeResult.raw.arrival_icao
1007
- });
1008
- decodeResult.formatted.items.push({
1009
- type: "origin",
1010
- code: "ORG",
1011
- label: "Origin",
1012
- value: decodeResult.raw.departure_icao
1013
- });
1146
+ ResultFormatter.departureAirport(decodeResult, results[3]);
1147
+ ResultFormatter.arrivalAirport(decodeResult, results[4]);
1148
+ ResultFormatter.alternateAirport(decodeResult, results[5]);
1149
+ ResultFormatter.arrivalRunway(decodeResult, results[8].replace(results[4], ""));
1150
+ const yymmdd = results[2];
1151
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertDateTimeToEpoch(results[7] + "00", yymmdd.substring(2, 4) + yymmdd.substring(4, 6) + yymmdd.substring(0, 2)), "epoch");
1014
1152
  }
1015
1153
  decodeResult.decoded = true;
1016
1154
  decodeResult.decoder.decodeLevel = "partial";
@@ -1097,15 +1235,15 @@ var Label_21_POS = class extends DecoderPlugin {
1097
1235
  const fields = content.split(",");
1098
1236
  if (fields.length == 9) {
1099
1237
  processPosition(decodeResult, fields[0].trim());
1100
- processAlt(decodeResult, fields[3]);
1101
- processTemp(decodeResult, fields[6]);
1102
- processArrvApt(decodeResult, fields[8]);
1238
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2]));
1239
+ ResultFormatter.altitude(decodeResult, Number(fields[3]));
1240
+ ResultFormatter.temperature(decodeResult, fields[6].replace(/ /g, ""));
1241
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[7]));
1242
+ ResultFormatter.arrivalAirport(decodeResult, fields[8]);
1103
1243
  decodeResult.remaining.text = [
1104
1244
  fields[1],
1105
- fields[2],
1106
1245
  fields[4],
1107
- fields[5],
1108
- fields[7]
1246
+ fields[5]
1109
1247
  ].join(",");
1110
1248
  decodeResult.decoded = true;
1111
1249
  decodeResult.decoder.decodeLevel = "partial";
@@ -1121,7 +1259,7 @@ var Label_21_POS = class extends DecoderPlugin {
1121
1259
  };
1122
1260
  function processPosition(decodeResult, value) {
1123
1261
  if (value.length !== 16 && value[0] !== "N" && value[0] !== "S" && value[8] !== "W" && value[8] !== "E") {
1124
- return false;
1262
+ return;
1125
1263
  }
1126
1264
  const latDir = value[0] === "N" ? 1 : -1;
1127
1265
  const lonDir = value[8] === "E" ? 1 : -1;
@@ -1129,45 +1267,52 @@ function processPosition(decodeResult, value) {
1129
1267
  latitude: latDir * Number(value.substring(1, 7)),
1130
1268
  longitude: lonDir * Number(value.substring(9, 15))
1131
1269
  };
1132
- if (position) {
1133
- decodeResult.raw.position = position;
1134
- decodeResult.formatted.items.push({
1135
- type: "aircraft_position",
1136
- code: "POS",
1137
- label: "Aircraft Position",
1138
- value: CoordinateUtils.coordinateString(position)
1139
- });
1140
- }
1141
- return !!position;
1142
- }
1143
- function processAlt(decodeResult, value) {
1144
- decodeResult.raw.altitude = Number(value);
1145
- decodeResult.formatted.items.push({
1146
- type: "altitude",
1147
- code: "ALT",
1148
- label: "Altitude",
1149
- value: `${decodeResult.raw.altitude} feet`
1150
- });
1151
- }
1152
- function processTemp(decodeResult, value) {
1153
- decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
1154
- decodeResult.formatted.items.push({
1155
- type: "outside_air_temperature",
1156
- code: "OATEMP",
1157
- label: "Outside Air Temperature (C)",
1158
- value: `${decodeResult.raw.outside_air_temperature}`
1159
- });
1160
- }
1161
- function processArrvApt(decodeResult, value) {
1162
- decodeResult.raw.arrival_icao = value;
1163
- decodeResult.formatted.items.push({
1164
- type: "destination",
1165
- code: "DST",
1166
- label: "Destination",
1167
- value: decodeResult.raw.arrival_icao
1168
- });
1270
+ ResultFormatter.position(decodeResult, position);
1169
1271
  }
1170
1272
 
1273
+ // lib/plugins/Label_24_Slash.ts
1274
+ var Label_24_Slash = class extends DecoderPlugin {
1275
+ name = "label-24-slash";
1276
+ qualifiers() {
1277
+ return {
1278
+ labels: ["24"],
1279
+ preambles: ["/"]
1280
+ };
1281
+ }
1282
+ decode(message, options = {}) {
1283
+ const decodeResult = this.defaultResult();
1284
+ decodeResult.decoder.name = this.name;
1285
+ decodeResult.formatted.description = "Position Report";
1286
+ decodeResult.message = message;
1287
+ const fields = message.text.split("/");
1288
+ if (fields.length == 10 && fields[0] == "" && fields[9] == "") {
1289
+ const mmddyy = fields[1].substring(4, 6) + fields[1].substring(2, 4) + fields[1].substring(0, 2);
1290
+ const hhmmss = fields[2] + "00";
1291
+ decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(hhmmss, mmddyy);
1292
+ ResultFormatter.flightNumber(decodeResult, fields[3]);
1293
+ ResultFormatter.altitude(decodeResult, Number(fields[4]));
1294
+ const lat = fields[5];
1295
+ const lon = fields[6];
1296
+ const position = {
1297
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1)),
1298
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1))
1299
+ };
1300
+ ResultFormatter.position(decodeResult, position);
1301
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[8] + "00"));
1302
+ decodeResult.remaining.text = fields[7];
1303
+ decodeResult.decoded = true;
1304
+ decodeResult.decoder.decodeLevel = "partial";
1305
+ } else {
1306
+ if (options.debug) {
1307
+ console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}. Message: ${message.text}`);
1308
+ }
1309
+ decodeResult.decoded = false;
1310
+ decodeResult.decoder.decodeLevel = "none";
1311
+ }
1312
+ return decodeResult;
1313
+ }
1314
+ };
1315
+
1171
1316
  // lib/plugins/Label_30_Slash_EA.ts
1172
1317
  var Label_30_Slash_EA = class extends DecoderPlugin {
1173
1318
  name = "label-30-slash-ea";
@@ -1182,27 +1327,16 @@ var Label_30_Slash_EA = class extends DecoderPlugin {
1182
1327
  decodeResult.decoder.name = this.name;
1183
1328
  decodeResult.formatted.description = "ETA Report";
1184
1329
  decodeResult.message = message;
1185
- const results = message.text.split(/\n|\//).slice(1);
1186
- if (results) {
1187
- if (options.debug) {
1188
- console.log(`Label 30 EA: results`);
1189
- console.log(results);
1190
- }
1191
- }
1192
- decodeResult.formatted.items.push({
1193
- type: "eta",
1194
- code: "ETA",
1195
- label: "Estimated Time of Arrival",
1196
- value: DateTimeUtils.UTCToString(results[0].substr(2, 4))
1197
- });
1198
- if (results[1].substr(0, 2) === "DS") {
1199
- decodeResult.raw.arrival_icao = results[1].substr(2, 4);
1200
- decodeResult.formatted.items.push({
1201
- type: "destination",
1202
- code: "DST",
1203
- label: "Destination",
1204
- value: decodeResult.raw.arrival_icao
1205
- });
1330
+ const results = message.text.split(/\n|\//).slice(1);
1331
+ if (results) {
1332
+ if (options.debug) {
1333
+ console.log(`Label 30 EA: results`);
1334
+ console.log(results);
1335
+ }
1336
+ }
1337
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results[0].substr(2, 4) + "00"));
1338
+ if (results[1].substring(0, 2) === "DS") {
1339
+ ResultFormatter.arrivalAirport(decodeResult, results[1].substring(2, 6));
1206
1340
  decodeResult.remaining.text = "/".concat(results[2]);
1207
1341
  } else {
1208
1342
  decodeResult.remaining.text = "/".concat(results[1], "/", results[2]);
@@ -1494,6 +1628,221 @@ var Label_44_POS = class extends DecoderPlugin {
1494
1628
  }
1495
1629
  };
1496
1630
 
1631
+ // lib/plugins/Label_4A.ts
1632
+ var Label_4A = class extends DecoderPlugin {
1633
+ name = "label-4a";
1634
+ qualifiers() {
1635
+ return {
1636
+ labels: ["4A"]
1637
+ };
1638
+ }
1639
+ decode(message, options = {}) {
1640
+ const decodeResult = this.defaultResult();
1641
+ decodeResult.decoder.name = this.name;
1642
+ decodeResult.message = message;
1643
+ decodeResult.formatted.description = "Latest New Format";
1644
+ let text2 = message.text;
1645
+ if (text2.match(/^M\d{2}A\w{6}/)) {
1646
+ ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1647
+ text2 = text2.substring(10);
1648
+ }
1649
+ decodeResult.decoded = true;
1650
+ const fields = text2.split(",");
1651
+ if (fields.length === 11) {
1652
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0]));
1653
+ ResultFormatter.tail(decodeResult, fields[2].replace(".", ""));
1654
+ if (fields[3])
1655
+ ResultFormatter.callsign(decodeResult, fields[3]);
1656
+ ResultFormatter.departureAirport(decodeResult, fields[4]);
1657
+ ResultFormatter.arrivalAirport(decodeResult, fields[5]);
1658
+ ResultFormatter.altitude(decodeResult, text2.substring(48, 51) * 100);
1659
+ decodeResult.remaining.text = fields.slice(8).join(",");
1660
+ } else if (fields.length === 6) {
1661
+ if (fields[0].match(/^[NS]/)) {
1662
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(fields[0].substring(0, 13)));
1663
+ let wp1 = {
1664
+ name: fields[0].substring(13).trim(),
1665
+ time: DateTimeUtils.convertHHMMSSToTod(fields[1].substring(0, 6)),
1666
+ timeFormat: "tod"
1667
+ };
1668
+ ResultFormatter.altitude(decodeResult, fields[1].substring(6, 9) * 100);
1669
+ let wp2 = {
1670
+ name: fields[1].substring(9).trim(),
1671
+ time: DateTimeUtils.convertHHMMSSToTod(fields[2]),
1672
+ timeFormat: "tod"
1673
+ };
1674
+ decodeResult.raw.route = { waypoints: [wp1, wp2] };
1675
+ decodeResult.formatted.items.push({
1676
+ type: "aircraft_route",
1677
+ code: "ROUTE",
1678
+ label: "Aircraft Route",
1679
+ value: RouteUtils.routeToString(decodeResult.raw.route)
1680
+ });
1681
+ ResultFormatter.temperature(decodeResult, fields[3]);
1682
+ decodeResult.remaining.text = fields.slice(4).join(",");
1683
+ } else {
1684
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0]));
1685
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1]));
1686
+ decodeResult.remaining.text = fields[2];
1687
+ ResultFormatter.altitude(decodeResult, fields[3]);
1688
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates((fields[4] + fields[5]).replace(/[ \.]/g, "")));
1689
+ }
1690
+ } else {
1691
+ decodeResult.decoded = false;
1692
+ decodeResult.remaining.text = text2;
1693
+ }
1694
+ if (decodeResult.decoded) {
1695
+ if (!decodeResult.remaining.text)
1696
+ decodeResult.decoder.decodeLevel = "full";
1697
+ else
1698
+ decodeResult.decoder.decodeLevel = "partial";
1699
+ } else {
1700
+ decodeResult.decoder.decodeLevel = "none";
1701
+ }
1702
+ return decodeResult;
1703
+ }
1704
+ };
1705
+
1706
+ // lib/plugins/Label_4A_01.ts
1707
+ var Label_4A_01 = class extends DecoderPlugin {
1708
+ name = "label-4a-01";
1709
+ qualifiers() {
1710
+ return {
1711
+ labels: ["4A"],
1712
+ preambles: ["01"]
1713
+ };
1714
+ }
1715
+ decode(message, options = {}) {
1716
+ const decodeResult = this.defaultResult();
1717
+ decodeResult.decoder.name = this.name;
1718
+ decodeResult.message = message;
1719
+ decodeResult.formatted.description = "Latest New Format";
1720
+ decodeResult.decoded = true;
1721
+ let rgx = message.text.match(/^01([A-Z]{2})([A-Z]{2})\s*(\w+)\/(\d{6})([A-Z]{4})([A-Z]{4})\r\n([+-]\s*\d+)(\d{3}\.\d)([+-]\s*\d+\.\d)/);
1722
+ if (rgx) {
1723
+ ResultFormatter.state_change(decodeResult, rgx[1], rgx[2]);
1724
+ ResultFormatter.callsign(decodeResult, rgx[3]);
1725
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(rgx[4] + "00"));
1726
+ ResultFormatter.departureAirport(decodeResult, rgx[5]);
1727
+ ResultFormatter.arrivalAirport(decodeResult, rgx[6]);
1728
+ ResultFormatter.altitude(decodeResult, Number(rgx[7].replace(/ /g, "")));
1729
+ decodeResult.remaining.text = rgx[8];
1730
+ ResultFormatter.temperature(decodeResult, rgx[9].replace(/ /g, ""));
1731
+ } else {
1732
+ decodeResult.decoded = false;
1733
+ decodeResult.remaining.text = message.text;
1734
+ }
1735
+ if (decodeResult.decoded) {
1736
+ if (!decodeResult.remaining.text)
1737
+ decodeResult.decoder.decodeLevel = "full";
1738
+ else
1739
+ decodeResult.decoder.decodeLevel = "partial";
1740
+ } else {
1741
+ decodeResult.decoder.decodeLevel = "none";
1742
+ }
1743
+ return decodeResult;
1744
+ }
1745
+ };
1746
+
1747
+ // lib/plugins/Label_4A_DIS.ts
1748
+ var Label_4A_DIS = class extends DecoderPlugin {
1749
+ name = "label-4a-dis";
1750
+ qualifiers() {
1751
+ return {
1752
+ labels: ["4A"],
1753
+ preambles: ["DIS"]
1754
+ };
1755
+ }
1756
+ decode(message, options = {}) {
1757
+ const decodeResult = this.defaultResult();
1758
+ decodeResult.decoder.name = this.name;
1759
+ decodeResult.message = message;
1760
+ decodeResult.formatted.description = "Latest New Format";
1761
+ decodeResult.decoded = true;
1762
+ const fields = message.text.split(",");
1763
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1].substring(2) + "00"));
1764
+ ResultFormatter.callsign(decodeResult, fields[2]);
1765
+ ResultFormatter.freetext(decodeResult, fields.slice(3).join(""));
1766
+ if (decodeResult.decoded) {
1767
+ if (!decodeResult.remaining.text)
1768
+ decodeResult.decoder.decodeLevel = "full";
1769
+ else
1770
+ decodeResult.decoder.decodeLevel = "partial";
1771
+ } else {
1772
+ decodeResult.decoder.decodeLevel = "none";
1773
+ }
1774
+ return decodeResult;
1775
+ }
1776
+ };
1777
+
1778
+ // lib/plugins/Label_4A_DOOR.ts
1779
+ var Label_4A_DOOR = class extends DecoderPlugin {
1780
+ name = "label-4a-door";
1781
+ qualifiers() {
1782
+ return {
1783
+ labels: ["4A"],
1784
+ preambles: ["DOOR"]
1785
+ };
1786
+ }
1787
+ decode(message, options = {}) {
1788
+ const decodeResult = this.defaultResult();
1789
+ decodeResult.decoder.name = this.name;
1790
+ decodeResult.message = message;
1791
+ decodeResult.formatted.description = "Latest New Format";
1792
+ decodeResult.decoded = true;
1793
+ const fields = message.text.split(" ");
1794
+ if (fields.length === 3) {
1795
+ ResultFormatter.door_event(decodeResult, fields[0].split("/")[1], fields[1]);
1796
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2] + "00"));
1797
+ } else {
1798
+ decodeResult.decoded = false;
1799
+ decodeResult.remaining.text = text;
1800
+ }
1801
+ if (decodeResult.decoded) {
1802
+ if (!decodeResult.remaining.text)
1803
+ decodeResult.decoder.decodeLevel = "full";
1804
+ else
1805
+ decodeResult.decoder.decodeLevel = "partial";
1806
+ } else {
1807
+ decodeResult.decoder.decodeLevel = "none";
1808
+ }
1809
+ return decodeResult;
1810
+ }
1811
+ };
1812
+
1813
+ // lib/plugins/Label_4A_Slash_01.ts
1814
+ var Label_4A_Slash_01 = class extends DecoderPlugin {
1815
+ name = "label-4a-slash-01";
1816
+ qualifiers() {
1817
+ return {
1818
+ labels: ["4A"],
1819
+ preambles: ["/01"]
1820
+ };
1821
+ }
1822
+ decode(message, options = {}) {
1823
+ const decodeResult = this.defaultResult();
1824
+ decodeResult.decoder.name = this.name;
1825
+ decodeResult.message = message;
1826
+ decodeResult.formatted.description = "Latest New Format";
1827
+ decodeResult.decoded = true;
1828
+ if (message.text.length === 5 && message.text.substring(0, 4) === "/01-") {
1829
+ decodeResult.remaining.text = message.text.substring(4);
1830
+ } else {
1831
+ decodeResult.decoded = false;
1832
+ decodeResult.remaining.text = message.text;
1833
+ }
1834
+ if (decodeResult.decoded) {
1835
+ if (!decodeResult.remaining.text)
1836
+ decodeResult.decoder.decodeLevel = "full";
1837
+ else
1838
+ decodeResult.decoder.decodeLevel = "partial";
1839
+ } else {
1840
+ decodeResult.decoder.decodeLevel = "none";
1841
+ }
1842
+ return decodeResult;
1843
+ }
1844
+ };
1845
+
1497
1846
  // lib/plugins/Label_4N.ts
1498
1847
  var Label_4N = class extends DecoderPlugin {
1499
1848
  name = "label-4n";
@@ -1507,20 +1856,20 @@ var Label_4N = class extends DecoderPlugin {
1507
1856
  decodeResult.decoder.name = this.name;
1508
1857
  decodeResult.message = message;
1509
1858
  decodeResult.formatted.description = "Airline Defined";
1510
- let text = message.text;
1511
- if (text.match(/^M\d{2}A\w{6}/)) {
1859
+ let text2 = message.text;
1860
+ if (text2.match(/^M\d{2}A\w{6}/)) {
1512
1861
  ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1513
- text = text.substring(10);
1862
+ text2 = text2.substring(10);
1514
1863
  }
1515
1864
  decodeResult.decoded = true;
1516
- const fields = text.split(",");
1517
- if (text.length === 51) {
1518
- decodeResult.raw.day_of_month = text.substring(0, 2);
1519
- ResultFormatter.departureAirport(decodeResult, text.substring(8, 11));
1520
- ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16));
1521
- ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(30, 45).replace(/^(.)0/, "$1")));
1522
- ResultFormatter.altitude(decodeResult, text.substring(48, 51) * 100);
1523
- decodeResult.remaining.text = [text.substring(2, 4), text.substring(19, 29)].join(" ");
1865
+ const fields = text2.split(",");
1866
+ if (text2.length === 51) {
1867
+ decodeResult.raw.day_of_month = text2.substring(0, 2);
1868
+ ResultFormatter.departureAirport(decodeResult, text2.substring(8, 11));
1869
+ ResultFormatter.arrivalAirport(decodeResult, text2.substring(13, 16));
1870
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text2.substring(30, 45).replace(/^(.)0/, "$1")));
1871
+ ResultFormatter.altitude(decodeResult, text2.substring(48, 51) * 100);
1872
+ decodeResult.remaining.text = [text2.substring(2, 4), text2.substring(19, 29)].join(" ");
1524
1873
  } else if (fields.length === 33) {
1525
1874
  decodeResult.raw.date = fields[3];
1526
1875
  if (fields[1] === "B") {
@@ -1538,10 +1887,10 @@ var Label_4N = class extends DecoderPlugin {
1538
1887
  decodeResult.remaining.text = [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter((f) => f != "").join(",");
1539
1888
  } else {
1540
1889
  decodeResult.decoded = false;
1541
- decodeResult.remaining.text = text;
1890
+ decodeResult.remaining.text = text2;
1542
1891
  }
1543
1892
  if (decodeResult.decoded) {
1544
- if (decodeResult.remaining.text === "")
1893
+ if (!decodeResult.remaining.text)
1545
1894
  decodeResult.decoder.decodeLevel = "full";
1546
1895
  else
1547
1896
  decodeResult.decoder.decodeLevel = "partial";
@@ -1729,14 +2078,14 @@ var Label_83 = class extends DecoderPlugin {
1729
2078
  decodeResult.decoder.name = this.name;
1730
2079
  decodeResult.message = message;
1731
2080
  decodeResult.formatted.description = "Airline Defined";
1732
- let text = message.text;
1733
- if (text.match(/^M\d{2}A\w{6}/)) {
2081
+ let text2 = message.text;
2082
+ if (text2.match(/^M\d{2}A\w{6}/)) {
1734
2083
  ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1735
- text = text.substring(10);
2084
+ text2 = text2.substring(10);
1736
2085
  }
1737
2086
  decodeResult.decoded = true;
1738
- if (text.substring(0, 10) === "4DH3 ETAT2") {
1739
- const fields = text.split(/\s+/);
2087
+ if (text2.substring(0, 10) === "4DH3 ETAT2") {
2088
+ const fields = text2.split(/\s+/);
1740
2089
  if (fields[2].length > 5) {
1741
2090
  decodeResult.raw.day_of_month = fields[2].substring(5);
1742
2091
  }
@@ -1745,14 +2094,17 @@ var Label_83 = class extends DecoderPlugin {
1745
2094
  ResultFormatter.departureAirport(decodeResult, subfields[0]);
1746
2095
  ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
1747
2096
  ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, ""));
1748
- ResultFormatter.eta(decodeResult, fields[6] + "00");
1749
- } else if (text.substring(0, 5) === "001PR") {
1750
- decodeResult.raw.day_of_month = text.substring(5, 7);
1751
- ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(13, 28).replace(/\./g, "")));
1752
- ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33)));
1753
- decodeResult.remaining.text = text.substring(33);
2097
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + "00"));
2098
+ } else if (text2.substring(0, 5) === "001PR") {
2099
+ decodeResult.raw.day_of_month = text2.substring(5, 7);
2100
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text2.substring(13, 28).replace(/\./g, ""));
2101
+ if (position) {
2102
+ ResultFormatter.position(decodeResult, position);
2103
+ }
2104
+ ResultFormatter.altitude(decodeResult, Number(text2.substring(28, 33)));
2105
+ decodeResult.remaining.text = text2.substring(33);
1754
2106
  } else {
1755
- const fields = text.replace(/\s/g, "").split(",");
2107
+ const fields = text2.replace(/\s/g, "").split(",");
1756
2108
  if (fields.length === 9) {
1757
2109
  ResultFormatter.departureAirport(decodeResult, fields[0]);
1758
2110
  ResultFormatter.arrivalAirport(decodeResult, fields[1]);
@@ -1765,7 +2117,7 @@ var Label_83 = class extends DecoderPlugin {
1765
2117
  longitude: Number(fields[4].replace(/\s/g, ""))
1766
2118
  }
1767
2119
  );
1768
- ResultFormatter.altitude(decodeResult, fields[5]);
2120
+ ResultFormatter.altitude(decodeResult, Number(fields[5]));
1769
2121
  ResultFormatter.groundspeed(decodeResult, fields[6]);
1770
2122
  ResultFormatter.heading(decodeResult, fields[7]);
1771
2123
  decodeResult.remaining.text = fields[8];
@@ -1775,7 +2127,7 @@ var Label_83 = class extends DecoderPlugin {
1775
2127
  }
1776
2128
  }
1777
2129
  if (decodeResult.decoded) {
1778
- if (decodeResult.remaining.text === "")
2130
+ if (!decodeResult.remaining.text)
1779
2131
  decodeResult.decoder.decodeLevel = "full";
1780
2132
  else
1781
2133
  decodeResult.decoder.decodeLevel = "partial";
@@ -1806,19 +2158,8 @@ var Label_8E = class extends DecoderPlugin {
1806
2158
  console.log(`Label 8E ETA: groups`);
1807
2159
  console.log(results.groups);
1808
2160
  }
1809
- decodeResult.formatted.items.push({
1810
- type: "eta",
1811
- code: "ETA",
1812
- label: "Estimated Time of Arrival",
1813
- value: DateTimeUtils.UTCToString(results.groups.arrival_eta)
1814
- });
1815
- decodeResult.raw.arrival_icao = results.groups.arrival_icao;
1816
- decodeResult.formatted.items.push({
1817
- type: "destination",
1818
- code: "DST",
1819
- label: "Destination",
1820
- value: decodeResult.raw.arrival_icao
1821
- });
2161
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results.groups.arrival_eta + "00"));
2162
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
1822
2163
  }
1823
2164
  decodeResult.decoded = true;
1824
2165
  decodeResult.decoder.decodeLevel = "full";
@@ -1871,44 +2212,158 @@ var Label_ColonComma = class extends DecoderPlugin {
1871
2212
  }
1872
2213
  };
1873
2214
 
1874
- // lib/plugins/Label_H1_FLR.ts
1875
- var Label_H1_FLR = class extends DecoderPlugin {
1876
- name = "label-h1-flr";
2215
+ // lib/plugins/Label_H1_FLR.ts
2216
+ var Label_H1_FLR = class extends DecoderPlugin {
2217
+ name = "label-h1-flr";
2218
+ qualifiers() {
2219
+ return {
2220
+ labels: ["H1"],
2221
+ preambles: ["FLR", "#CFBFLR"]
2222
+ };
2223
+ }
2224
+ decode(message, options = {}) {
2225
+ let decodeResult = this.defaultResult();
2226
+ decodeResult.decoder.name = this.name;
2227
+ decodeResult.formatted.description = "Fault Log Report";
2228
+ decodeResult.message = message;
2229
+ const parts = message.text.split("/FR");
2230
+ if (parts.length > 1) {
2231
+ decodeResult.remaining.text = "";
2232
+ const fields = parts[0].split("/");
2233
+ for (let i = 1; i < fields.length; i++) {
2234
+ const field = fields[i];
2235
+ if (field.startsWith("PN")) {
2236
+ processUnknown(decodeResult, "/" + field);
2237
+ } else {
2238
+ processUnknown(decodeResult, "/" + field);
2239
+ }
2240
+ }
2241
+ const data = parts[1].substring(0, 20);
2242
+ const msg = parts[1].substring(20);
2243
+ const datetime = data.substring(0, 12);
2244
+ const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
2245
+ processUnknown(decodeResult, data.substring(12));
2246
+ decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
2247
+ decodeResult.raw.fault_message = msg;
2248
+ decodeResult.formatted.items.push({
2249
+ type: "fault",
2250
+ code: "FR",
2251
+ label: "Fault Report",
2252
+ value: decodeResult.raw.fault_message
2253
+ });
2254
+ decodeResult.decoded = true;
2255
+ decodeResult.decoder.decodeLevel = "partial";
2256
+ } else {
2257
+ if (options.debug) {
2258
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
2259
+ }
2260
+ decodeResult.remaining.text = message.text;
2261
+ decodeResult.decoded = false;
2262
+ decodeResult.decoder.decodeLevel = "none";
2263
+ }
2264
+ return decodeResult;
2265
+ }
2266
+ };
2267
+ function processUnknown(decodeResult, value) {
2268
+ decodeResult.remaining.text += value;
2269
+ }
2270
+
2271
+ // lib/plugins/Label_H1_OHMA.ts
2272
+ import * as zlib from "minizlib";
2273
+ var Label_H1_OHMA = class extends DecoderPlugin {
2274
+ name = "label-h1-ohma";
2275
+ qualifiers() {
2276
+ return {
2277
+ labels: ["H1"],
2278
+ preambles: ["OHMA", "/RTNBOCR.OHMA", "#T1B/RTNBOCR.OHMA"]
2279
+ };
2280
+ }
2281
+ decode(message, options = {}) {
2282
+ let decodeResult = this.defaultResult();
2283
+ decodeResult.decoder.name = this.name;
2284
+ decodeResult.formatted.description = "OHMA Message";
2285
+ decodeResult.message = message;
2286
+ decodeResult.remaining.text = "";
2287
+ const data = message.text.split("OHMA")[1];
2288
+ try {
2289
+ const compressedBuffer = Buffer.from(data, "base64");
2290
+ const decompress = new zlib.Inflate({ windowBits: 15 });
2291
+ decompress.write(compressedBuffer);
2292
+ decompress.flush(zlib.constants.Z_SYNC_FLUSH);
2293
+ const result = decompress.read();
2294
+ const jsonText = result.toString();
2295
+ let formattedMsg;
2296
+ let jsonMessage;
2297
+ try {
2298
+ jsonMessage = JSON.parse(jsonText).message;
2299
+ } catch {
2300
+ jsonMessage = jsonText;
2301
+ }
2302
+ try {
2303
+ const ohmaMsg = JSON.parse(jsonMessage);
2304
+ formattedMsg = JSON.stringify(ohmaMsg, null, 2);
2305
+ } catch {
2306
+ formattedMsg = jsonMessage;
2307
+ }
2308
+ decodeResult.decoded = true;
2309
+ decodeResult.decoder.decodeLevel = "full";
2310
+ decodeResult.raw.ohma = jsonText;
2311
+ decodeResult.formatted.items.push({
2312
+ type: "ohma",
2313
+ code: "OHMA",
2314
+ label: "OHMA Downlink",
2315
+ value: formattedMsg
2316
+ });
2317
+ } catch (e) {
2318
+ if (options.debug) {
2319
+ console.log(`Decoder: Unknown H1 OHMA message: ${message.text}`, e);
2320
+ }
2321
+ decodeResult.remaining.text += message.text;
2322
+ decodeResult.decoded = false;
2323
+ decodeResult.decoder.decodeLevel = "none";
2324
+ }
2325
+ return decodeResult;
2326
+ }
2327
+ };
2328
+
2329
+ // lib/plugins/Label_H1_WRN.ts
2330
+ var Label_H1_WRN = class extends DecoderPlugin {
2331
+ name = "label-h1-wrn";
1877
2332
  qualifiers() {
1878
2333
  return {
1879
2334
  labels: ["H1"],
1880
- preambles: ["FLR", "#CFBFLR"]
2335
+ preambles: ["WRN", "#CFBWRN"]
1881
2336
  };
1882
2337
  }
1883
2338
  decode(message, options = {}) {
1884
2339
  let decodeResult = this.defaultResult();
1885
2340
  decodeResult.decoder.name = this.name;
1886
- decodeResult.formatted.description = "Fault Log Report";
2341
+ decodeResult.formatted.description = "Warning Message";
1887
2342
  decodeResult.message = message;
1888
- const parts = message.text.split("/FR");
2343
+ const parts = message.text.split("/WN");
1889
2344
  if (parts.length > 1) {
1890
2345
  decodeResult.remaining.text = "";
1891
2346
  const fields = parts[0].split("/");
1892
2347
  for (let i = 1; i < fields.length; i++) {
1893
2348
  const field = fields[i];
1894
2349
  if (field.startsWith("PN")) {
1895
- processUnknown(decodeResult, "/" + field);
2350
+ processUnknown2(decodeResult, "/" + field);
1896
2351
  } else {
1897
- processUnknown(decodeResult, "/" + field);
2352
+ processUnknown2(decodeResult, "/" + field);
1898
2353
  }
1899
2354
  }
1900
2355
  const data = parts[1].substring(0, 20);
1901
2356
  const msg = parts[1].substring(20);
1902
2357
  const datetime = data.substring(0, 12);
1903
2358
  const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
1904
- processUnknown(decodeResult, data.substring(12));
2359
+ processUnknown2(decodeResult, data.substring(12));
1905
2360
  decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
1906
- decodeResult.raw.fault_message = msg;
2361
+ decodeResult.raw.warning_message = msg;
1907
2362
  decodeResult.formatted.items.push({
1908
- type: "fault",
1909
- code: "FR",
1910
- label: "Fault Report",
1911
- value: decodeResult.raw.fault_message
2363
+ type: "warning",
2364
+ code: "WRN",
2365
+ label: "Warning Message",
2366
+ value: decodeResult.raw.warning_message
1912
2367
  });
1913
2368
  decodeResult.decoded = true;
1914
2369
  decodeResult.decoder.decodeLevel = "partial";
@@ -1923,7 +2378,7 @@ var Label_H1_FLR = class extends DecoderPlugin {
1923
2378
  return decodeResult;
1924
2379
  }
1925
2380
  };
1926
- function processUnknown(decodeResult, value) {
2381
+ function processUnknown2(decodeResult, value) {
1927
2382
  decodeResult.remaining.text += value;
1928
2383
  }
1929
2384
 
@@ -2089,11 +2544,10 @@ function addCompanyRoute(decodeResult, value) {
2089
2544
  // lib/utils/h1_helper.ts
2090
2545
  var H1Helper = class {
2091
2546
  static decodeH1Message(decodeResult, message) {
2092
- let allKnownFields = true;
2093
2547
  const checksum = message.slice(-4);
2094
2548
  const data = message.slice(0, message.length - 4);
2095
2549
  const fields = data.split("/");
2096
- allKnownFields = allKnownFields && parseMessageType(decodeResult, fields[0]);
2550
+ parseMessageType(decodeResult, fields[0]);
2097
2551
  for (let i = 1; i < fields.length; ++i) {
2098
2552
  if (fields[i].startsWith("FN")) {
2099
2553
  decodeResult.raw.flight_number = fields[i].substring(2);
@@ -2112,32 +2566,22 @@ var H1Helper = class {
2112
2566
  decodeResult.raw.message_timestamp = time;
2113
2567
  } else if (fields[i].startsWith("PS")) {
2114
2568
  const pos = processPS(decodeResult, fields[i].substring(2).split(","));
2115
- allKnownFields == allKnownFields && pos;
2116
2569
  } else if (fields[i].startsWith("DT")) {
2117
2570
  const data2 = fields[i].substring(2).split(",");
2118
- const dt = processDT(decodeResult, data2);
2119
- allKnownFields = allKnownFields && dt;
2571
+ processDT(decodeResult, data2);
2120
2572
  } else if (fields[i].startsWith("ID")) {
2121
2573
  processIdentification(decodeResult, fields[i].substring(2).split(","));
2122
2574
  } else if (fields[i].startsWith("LR")) {
2123
2575
  const data2 = fields[i].substring(2).split(",");
2124
- const lr = processLR(decodeResult, data2);
2125
- allKnownFields = allKnownFields && lr;
2576
+ processLR(decodeResult, data2);
2126
2577
  } else if (fields[i].startsWith("RI") || fields[i].startsWith("RF") || fields[i].startsWith("RP")) {
2127
- const fp = FlightPlanUtils.processFlightPlan(decodeResult, fields[i].split(":"));
2128
- allKnownFields = allKnownFields && fp;
2578
+ FlightPlanUtils.processFlightPlan(decodeResult, fields[i].split(":"));
2129
2579
  } else if (fields[i].startsWith("PR")) {
2130
- allKnownFields = false;
2131
2580
  decodeResult.remaining.text += "/" + fields[i];
2132
- } else if (fields[i].startsWith("PS")) {
2133
- allKnownFields = false;
2134
- decodeResult.remaining.text += fields[i];
2135
2581
  } else if (fields[i].startsWith("AF")) {
2136
- const af = processAirField(decodeResult, fields[i].substring(2).split(","));
2137
- allKnownFields = allKnownFields && af;
2582
+ processAirField(decodeResult, fields[i].substring(2).split(","));
2138
2583
  } else if (fields[i].startsWith("TD")) {
2139
- const td = processTimeOfDeparture(decodeResult, fields[i].substring(2).split(","));
2140
- allKnownFields = allKnownFields && td;
2584
+ processTimeOfDeparture(decodeResult, fields[i].substring(2).split(","));
2141
2585
  } else if (fields[i].startsWith("FX")) {
2142
2586
  decodeResult.raw.free_text = fields[i].substring(2);
2143
2587
  decodeResult.formatted.items.push({
@@ -2148,23 +2592,21 @@ var H1Helper = class {
2148
2592
  });
2149
2593
  } else {
2150
2594
  decodeResult.remaining.text += "/" + fields[i];
2151
- allKnownFields = false;
2152
2595
  }
2153
2596
  }
2154
2597
  if (decodeResult.formatted.items.length > 0) {
2155
2598
  ResultFormatter.checksum(decodeResult, checksum);
2156
2599
  }
2157
- return allKnownFields;
2600
+ return true;
2158
2601
  }
2159
2602
  };
2160
2603
  function processAirField(decodeResult, data) {
2161
2604
  if (data.length === 2) {
2162
2605
  ResultFormatter.departureAirport(decodeResult, data[0]);
2163
2606
  ResultFormatter.arrivalAirport(decodeResult, data[1]);
2164
- return true;
2607
+ } else {
2608
+ decodeResult.remaining.text += "AF/" + data.join(",");
2165
2609
  }
2166
- decodeResult.remaining.text += "AF/" + data.join(",");
2167
- return false;
2168
2610
  }
2169
2611
  function processTimeOfDeparture(decodeResult, data) {
2170
2612
  if (data.length === 2) {
@@ -2182,10 +2624,9 @@ function processTimeOfDeparture(decodeResult, data) {
2182
2624
  label: "Estimated Departure Time",
2183
2625
  value: `${data[1].substring(0, 2)}:${data[1].substring(2)}`
2184
2626
  });
2185
- return true;
2627
+ } else {
2628
+ decodeResult.remaining.text += "/TD" + data.join(",");
2186
2629
  }
2187
- decodeResult.remaining.text += "/TD" + data.join(",");
2188
- return false;
2189
2630
  }
2190
2631
  function processIdentification(decodeResult, data) {
2191
2632
  ResultFormatter.tail(decodeResult, data[0]);
@@ -2197,7 +2638,6 @@ function processIdentification(decodeResult, data) {
2197
2638
  }
2198
2639
  }
2199
2640
  function processDT(decodeResult, data) {
2200
- let allKnownFields = true;
2201
2641
  if (!decodeResult.raw.arrival_icao) {
2202
2642
  ResultFormatter.arrivalAirport(decodeResult, data[0]);
2203
2643
  } else if (decodeResult.raw.arrival_icao != data[0]) {
@@ -2210,19 +2650,16 @@ function processDT(decodeResult, data) {
2210
2650
  ResultFormatter.currentFuel(decodeResult, Number(data[2]));
2211
2651
  }
2212
2652
  if (data.length > 3) {
2213
- ResultFormatter.eta(decodeResult, data[3]);
2653
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[3]));
2214
2654
  }
2215
2655
  if (data.length > 4) {
2216
2656
  ResultFormatter.remainingFuel(decodeResult, Number(data[4]));
2217
2657
  }
2218
2658
  if (data.length > 5) {
2219
- allKnownFields = false;
2220
2659
  decodeResult.remaining.text += "," + data.slice(5).join(",");
2221
2660
  }
2222
- return allKnownFields;
2223
2661
  }
2224
2662
  function processLR(decodeResult, data) {
2225
- let allKnownFields = true;
2226
2663
  if (data.length === 19) {
2227
2664
  ResultFormatter.unknown(decodeResult, data[1]);
2228
2665
  ResultFormatter.flightNumber(decodeResult, data[2]);
@@ -2230,34 +2667,47 @@ function processLR(decodeResult, data) {
2230
2667
  ResultFormatter.arrivalAirport(decodeResult, data[4]);
2231
2668
  ResultFormatter.arrivalRunway(decodeResult, data[5]);
2232
2669
  ResultFormatter.unknown(decodeResult, data.slice(6, 19).join(","));
2233
- allKnownFields = false;
2234
2670
  } else {
2235
- allKnownFields = false;
2671
+ ResultFormatter.unknown(decodeResult, data.join(","));
2236
2672
  }
2237
- return allKnownFields;
2238
2673
  }
2239
2674
  function parseMessageType(decodeResult, messageType) {
2240
- let decoded = true;
2241
2675
  const parts = messageType.split("#");
2242
2676
  if (parts.length == 1) {
2243
- if (parts[0].startsWith("POS") && parts[0].length !== 3 && !parts[0].startsWith("POS/")) {
2244
- decoded = processPosition2(decodeResult, parts[0].substring(3).split(","));
2677
+ const type = parts[0].substring(0, 3);
2678
+ if (type === "POS" && parts[0].length !== 3) {
2679
+ processPosition2(decodeResult, parts[0].substring(3).split(","));
2245
2680
  }
2246
- return decoded;
2681
+ return processMessageType(decodeResult, type);
2247
2682
  } else if (parts.length == 2) {
2248
2683
  if (parts[0].length > 0) {
2249
2684
  decodeResult.remaining.text += parts[0].substring(0, 3);
2250
2685
  decodeResult.raw.flight_number = parts[0].substring(3);
2251
- decodeResult.remaining.text += "#" + parts[1].substring(0, 3);
2686
+ decodeResult.remaining.text += "#" + (parts[1].length == 5 ? parts[1].substring(0, 2) : parts[1].substring(0, 3));
2252
2687
  }
2253
- if (parts[1].substring(3, 6) === "POS" && parts[1].length !== 6 && parts[1].substring(3, 7) !== "POS/") {
2254
- decoded = processPosition2(decodeResult, parts[1].substring(6).split(","));
2688
+ const type = parts[1].length == 5 ? parts[1].substring(2, 5) : parts[1].substring(3, 6);
2689
+ if (parts[1].substring(3, 6) === "POS" && parts[1].length > 6) {
2690
+ processPosition2(decodeResult, parts[1].substring(6).split(","));
2255
2691
  }
2256
- decodeResult.raw.message_type = messageType;
2257
- return decoded;
2692
+ processMessageType(decodeResult, type);
2693
+ } else {
2694
+ decodeResult.remaining.text += messageType;
2695
+ }
2696
+ }
2697
+ function processMessageType(decodeResult, type) {
2698
+ if (type === "FPN") {
2699
+ decodeResult.formatted.description = "Flight Plan";
2700
+ } else if (type === "FTX") {
2701
+ decodeResult.formatted.description = "Free Text";
2702
+ } else if (type === "INI") {
2703
+ decodeResult.formatted.description = "Flight Plan Initial Report";
2704
+ } else if (type === "POS") {
2705
+ decodeResult.formatted.description = "Position Report";
2706
+ } else if (type === "PRG") {
2707
+ decodeResult.formatted.description = "Progress Report";
2708
+ } else {
2709
+ decodeResult.formatted.description = "Unknown H1 Message";
2258
2710
  }
2259
- decodeResult.remaining.text += messageType;
2260
- return false;
2261
2711
  }
2262
2712
  function processDC(decodeResult, data) {
2263
2713
  decodeResult.raw.message_date = data[0];
@@ -2266,13 +2716,9 @@ function processDC(decodeResult, data) {
2266
2716
  const date = data[0].substring(2, 4) + data[0].substring(0, 2) + data[0].substring(4, 6);
2267
2717
  const time = DateTimeUtils.convertDateTimeToEpoch(data[1], data[0]);
2268
2718
  decodeResult.raw.message_timestamp = time;
2269
- } else {
2270
- return false;
2271
2719
  }
2272
- return true;
2273
2720
  }
2274
2721
  function processPS(decodeResult, data) {
2275
- let allKnownFields = true;
2276
2722
  const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
2277
2723
  if (position) {
2278
2724
  decodeResult.raw.position = position;
@@ -2282,8 +2728,6 @@ function processPS(decodeResult, data) {
2282
2728
  label: "Aircraft Position",
2283
2729
  value: CoordinateUtils.coordinateString(position)
2284
2730
  });
2285
- } else {
2286
- allKnownFields = false;
2287
2731
  }
2288
2732
  if (data.length === 9) {
2289
2733
  processRoute(decodeResult, data[3], data[1], data[5], data[4], void 0);
@@ -2302,11 +2746,8 @@ function processPS(decodeResult, data) {
2302
2746
  ResultFormatter.unknown(decodeResult, data[9]);
2303
2747
  ResultFormatter.unknown(decodeResult, data.slice(11).join(","));
2304
2748
  }
2305
- allKnownFields = false;
2306
- return allKnownFields;
2307
2749
  }
2308
2750
  function processPosition2(decodeResult, data) {
2309
- let allKnownFields = true;
2310
2751
  const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
2311
2752
  if (position) {
2312
2753
  decodeResult.raw.position = position;
@@ -2316,8 +2757,6 @@ function processPosition2(decodeResult, data) {
2316
2757
  label: "Aircraft Position",
2317
2758
  value: CoordinateUtils.coordinateString(position)
2318
2759
  });
2319
- } else {
2320
- allKnownFields = false;
2321
2760
  }
2322
2761
  if (data.length >= 10) {
2323
2762
  ResultFormatter.altitude(decodeResult, Number(data[3]) * 100);
@@ -2332,8 +2771,6 @@ function processPosition2(decodeResult, data) {
2332
2771
  ResultFormatter.unknown(decodeResult, data[12]);
2333
2772
  ResultFormatter.unknown(decodeResult, data[13]);
2334
2773
  }
2335
- allKnownFields = false;
2336
- return allKnownFields;
2337
2774
  }
2338
2775
  function processRoute(decodeResult, last, time, next, eta, then, date) {
2339
2776
  const lastTime = date ? DateTimeUtils.convertDateTimeToEpoch(time, date) : DateTimeUtils.convertHHMMSSToTod(time);
@@ -2356,175 +2793,23 @@ function processRoute(decodeResult, last, time, next, eta, then, date) {
2356
2793
  });
2357
2794
  }
2358
2795
 
2359
- // lib/plugins/Label_H1_FPN.ts
2360
- var Label_H1_FPN = class extends DecoderPlugin {
2361
- name = "label-h1-fpn";
2796
+ // lib/plugins/Label_H1.ts
2797
+ var Label_H1 = class extends DecoderPlugin {
2798
+ name = "label-h1";
2362
2799
  qualifiers() {
2363
2800
  return {
2364
- labels: ["H1"],
2365
- preambles: ["FPN", "#M1BFPN"]
2801
+ labels: ["H1"]
2366
2802
  };
2367
2803
  }
2368
2804
  decode(message, options = {}) {
2369
2805
  let decodeResult = this.defaultResult();
2370
2806
  decodeResult.decoder.name = this.name;
2371
- decodeResult.formatted.description = "Flight Plan";
2372
2807
  decodeResult.message = message;
2373
2808
  decodeResult.remaining.text = "";
2374
2809
  const msg = message.text.replace(/\n|\r/g, "");
2375
- const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, msg);
2376
- decodeResult.decoded = true;
2377
- decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2378
- if (decodeResult.formatted.items.length === 0) {
2379
- if (options.debug) {
2380
- console.log(`Decoder: Unknown H1 message: ${message.text}`);
2381
- }
2382
- decodeResult.remaining.text = message.text;
2383
- decodeResult.decoded = false;
2384
- decodeResult.decoder.decodeLevel = "none";
2385
- }
2386
- return decodeResult;
2387
- }
2388
- };
2389
-
2390
- // lib/plugins/Label_H1_FTX.ts
2391
- var Label_H1_FTX = class extends DecoderPlugin {
2392
- name = "label-h1-ftx";
2393
- qualifiers() {
2394
- return {
2395
- labels: ["H1"],
2396
- preambles: ["FTX", "- #MDFTX"]
2397
- };
2398
- }
2399
- decode(message, options = {}) {
2400
- let decodeResult = this.defaultResult();
2401
- decodeResult.decoder.name = this.name;
2402
- decodeResult.formatted.description = "Free Text";
2403
- decodeResult.message = message;
2404
- decodeResult.remaining.text = "";
2405
- const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2406
- decodeResult.decoded = true;
2407
- decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2408
- if (decodeResult.formatted.items.length === 0) {
2409
- if (options.debug) {
2410
- console.log(`Decoder: Unknown H1 message: ${message.text}`);
2411
- }
2412
- decodeResult.remaining.text = message.text;
2413
- decodeResult.decoded = false;
2414
- decodeResult.decoder.decodeLevel = "none";
2415
- }
2416
- return decodeResult;
2417
- }
2418
- };
2419
-
2420
- // lib/plugins/Label_H1_INI.ts
2421
- var Label_H1_INI = class extends DecoderPlugin {
2422
- // eslint-disable-line camelcase
2423
- name = "label-h1-ini";
2424
- qualifiers() {
2425
- return {
2426
- labels: ["H1"],
2427
- preambles: ["INI", "- #MDINI"]
2428
- };
2429
- }
2430
- decode(message, options = {}) {
2431
- const decodeResult = this.defaultResult();
2432
- decodeResult.decoder.name = this.name;
2433
- decodeResult.formatted.description = "Flight Plan Initial Report";
2434
- decodeResult.message = message;
2435
- decodeResult.remaining.text = "";
2436
- const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2437
- decodeResult.decoded = true;
2438
- decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2439
- if (decodeResult.formatted.items.length === 0) {
2440
- if (options.debug) {
2441
- console.log(`Decoder: Unknown H1 message: ${message.text}`);
2442
- }
2443
- decodeResult.remaining.text = message.text;
2444
- decodeResult.decoded = false;
2445
- decodeResult.decoder.decodeLevel = "none";
2446
- }
2447
- return decodeResult;
2448
- }
2449
- };
2450
-
2451
- // lib/plugins/Label_H1_OHMA.ts
2452
- import * as zlib from "minizlib";
2453
- var Label_H1_OHMA = class extends DecoderPlugin {
2454
- name = "label-h1-ohma";
2455
- qualifiers() {
2456
- return {
2457
- labels: ["H1"],
2458
- preambles: ["OHMA", "/RTNBOCR.OHMA", "#T1B/RTNBOCR.OHMA"]
2459
- };
2460
- }
2461
- decode(message, options = {}) {
2462
- let decodeResult = this.defaultResult();
2463
- decodeResult.decoder.name = this.name;
2464
- decodeResult.formatted.description = "OHMA Message";
2465
- decodeResult.message = message;
2466
- decodeResult.remaining.text = "";
2467
- const data = message.text.split("OHMA")[1];
2468
- try {
2469
- const compressedBuffer = Buffer.from(data, "base64");
2470
- const decompress = new zlib.Inflate({ windowBits: 15 });
2471
- decompress.write(compressedBuffer);
2472
- decompress.flush(zlib.constants.Z_SYNC_FLUSH);
2473
- const result = decompress.read();
2474
- const jsonText = result.toString();
2475
- let formattedMsg;
2476
- let jsonMessage;
2477
- try {
2478
- jsonMessage = JSON.parse(jsonText).message;
2479
- } catch {
2480
- jsonMessage = jsonText;
2481
- }
2482
- try {
2483
- const ohmaMsg = JSON.parse(jsonMessage);
2484
- formattedMsg = JSON.stringify(ohmaMsg, null, 2);
2485
- } catch {
2486
- formattedMsg = jsonMessage;
2487
- }
2488
- decodeResult.decoded = true;
2489
- decodeResult.decoder.decodeLevel = "full";
2490
- decodeResult.raw.ohma = jsonText;
2491
- decodeResult.formatted.items.push({
2492
- type: "ohma",
2493
- code: "OHMA",
2494
- label: "OHMA Downlink",
2495
- value: formattedMsg
2496
- });
2497
- } catch (e) {
2498
- if (options.debug) {
2499
- console.log(`Decoder: Unknown H1 OHMA message: ${message.text}`, e);
2500
- }
2501
- decodeResult.remaining.text += message.text;
2502
- decodeResult.decoded = false;
2503
- decodeResult.decoder.decodeLevel = "none";
2504
- }
2505
- return decodeResult;
2506
- }
2507
- };
2508
-
2509
- // lib/plugins/Label_H1_POS.ts
2510
- var Label_H1_POS = class extends DecoderPlugin {
2511
- name = "label-h1-pos";
2512
- qualifiers() {
2513
- return {
2514
- labels: ["H1", "4J"],
2515
- preambles: ["POS", "#M1BPOS", "/.POS"]
2516
- //TODO - support data before #
2517
- };
2518
- }
2519
- decode(message, options = {}) {
2520
- let decodeResult = this.defaultResult();
2521
- decodeResult.decoder.name = this.name;
2522
- decodeResult.formatted.description = "Position Report";
2523
- decodeResult.message = message;
2524
- decodeResult.remaining.text = "";
2525
- const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2526
- decodeResult.decoded = true;
2527
- decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2810
+ const decoded = H1Helper.decodeH1Message(decodeResult, msg);
2811
+ decodeResult.decoded = decoded;
2812
+ decodeResult.decoder.decodeLevel = decodeResult.remaining.text.length === 0 ? "full" : "partial";
2528
2813
  if (decodeResult.formatted.items.length === 0) {
2529
2814
  if (options.debug) {
2530
2815
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
@@ -2537,62 +2822,6 @@ var Label_H1_POS = class extends DecoderPlugin {
2537
2822
  }
2538
2823
  };
2539
2824
 
2540
- // lib/plugins/Label_H1_WRN.ts
2541
- var Label_H1_WRN = class extends DecoderPlugin {
2542
- name = "label-h1-wrn";
2543
- qualifiers() {
2544
- return {
2545
- labels: ["H1"],
2546
- preambles: ["WRN", "#CFBWRN"]
2547
- };
2548
- }
2549
- decode(message, options = {}) {
2550
- let decodeResult = this.defaultResult();
2551
- decodeResult.decoder.name = this.name;
2552
- decodeResult.formatted.description = "Warning Message";
2553
- decodeResult.message = message;
2554
- const parts = message.text.split("/WN");
2555
- if (parts.length > 1) {
2556
- decodeResult.remaining.text = "";
2557
- const fields = parts[0].split("/");
2558
- for (let i = 1; i < fields.length; i++) {
2559
- const field = fields[i];
2560
- if (field.startsWith("PN")) {
2561
- processUnknown2(decodeResult, "/" + field);
2562
- } else {
2563
- processUnknown2(decodeResult, "/" + field);
2564
- }
2565
- }
2566
- const data = parts[1].substring(0, 20);
2567
- const msg = parts[1].substring(20);
2568
- const datetime = data.substring(0, 12);
2569
- const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
2570
- processUnknown2(decodeResult, data.substring(12));
2571
- decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
2572
- decodeResult.raw.warning_message = msg;
2573
- decodeResult.formatted.items.push({
2574
- type: "warning",
2575
- code: "WRN",
2576
- label: "Warning Message",
2577
- value: decodeResult.raw.warning_message
2578
- });
2579
- decodeResult.decoded = true;
2580
- decodeResult.decoder.decodeLevel = "partial";
2581
- } else {
2582
- if (options.debug) {
2583
- console.log(`Decoder: Unknown H1 message: ${message.text}`);
2584
- }
2585
- decodeResult.remaining.text = message.text;
2586
- decodeResult.decoded = false;
2587
- decodeResult.decoder.decodeLevel = "none";
2588
- }
2589
- return decodeResult;
2590
- }
2591
- };
2592
- function processUnknown2(decodeResult, value) {
2593
- decodeResult.remaining.text += value;
2594
- }
2595
-
2596
2825
  // lib/plugins/Label_HX.ts
2597
2826
  var Label_HX = class extends DecoderPlugin {
2598
2827
  name = "label-hx";
@@ -2636,7 +2865,7 @@ var Label_HX = class extends DecoderPlugin {
2636
2865
  });
2637
2866
  }
2638
2867
  if (decodeResult.decoded) {
2639
- if (decodeResult.remaining.text === "")
2868
+ if (!decodeResult.remaining.text)
2640
2869
  decodeResult.decoder.decodeLevel = "full";
2641
2870
  else
2642
2871
  decodeResult.decoder.decodeLevel = "partial";
@@ -2791,7 +3020,7 @@ var Label_QR = class extends DecoderPlugin {
2791
3020
  }
2792
3021
  ];
2793
3022
  decodeResult.decoded = true;
2794
- if (decodeResult.remaining.text === "")
3023
+ if (!decodeResult.remaining.text)
2795
3024
  decodeResult.decoder.decodeLevel = "full";
2796
3025
  else
2797
3026
  decodeResult.decoder.decodeLevel = "partial";
@@ -2836,7 +3065,7 @@ var Label_QP = class extends DecoderPlugin {
2836
3065
  }
2837
3066
  ];
2838
3067
  decodeResult.decoded = true;
2839
- if (decodeResult.remaining.text === "")
3068
+ if (!decodeResult.remaining.text)
2840
3069
  decodeResult.decoder.decodeLevel = "full";
2841
3070
  else
2842
3071
  decodeResult.decoder.decodeLevel = "partial";
@@ -2881,7 +3110,7 @@ var Label_QS = class extends DecoderPlugin {
2881
3110
  }
2882
3111
  ];
2883
3112
  decodeResult.decoded = true;
2884
- if (decodeResult.remaining.text === "")
3113
+ if (!decodeResult.remaining.text)
2885
3114
  decodeResult.decoder.decodeLevel = "full";
2886
3115
  else
2887
3116
  decodeResult.decoder.decodeLevel = "partial";
@@ -2943,7 +3172,7 @@ var Label_QQ = class extends DecoderPlugin {
2943
3172
  });
2944
3173
  }
2945
3174
  decodeResult.decoded = true;
2946
- if (decodeResult.remaining.text === "")
3175
+ if (!decodeResult.remaining.text)
2947
3176
  decodeResult.decoder.decodeLevel = "full";
2948
3177
  else
2949
3178
  decodeResult.decoder.decodeLevel = "partial";
@@ -3763,7 +3992,7 @@ var MessageDecoder = class {
3763
3992
  this.plugins = [];
3764
3993
  this.debug = false;
3765
3994
  this.registerPlugin(new Label_ColonComma(this));
3766
- this.registerPlugin(new Label_5Z(this));
3995
+ this.registerPlugin(new Label_5Z_Slash(this));
3767
3996
  this.registerPlugin(new Label_10_LDR(this));
3768
3997
  this.registerPlugin(new Label_10_POS(this));
3769
3998
  this.registerPlugin(new Label_10_Slash(this));
@@ -3774,21 +4003,24 @@ var MessageDecoder = class {
3774
4003
  this.registerPlugin(new Label_16_N_Space(this));
3775
4004
  this.registerPlugin(new Label_20_POS(this));
3776
4005
  this.registerPlugin(new Label_21_POS(this));
4006
+ this.registerPlugin(new Label_24_Slash(this));
3777
4007
  this.registerPlugin(new Label_30_Slash_EA(this));
3778
4008
  this.registerPlugin(new Label_44_ETA(this));
3779
4009
  this.registerPlugin(new Label_44_IN(this));
3780
4010
  this.registerPlugin(new Label_44_OFF(this));
3781
4011
  this.registerPlugin(new Label_44_ON(this));
3782
4012
  this.registerPlugin(new Label_44_POS(this));
4013
+ this.registerPlugin(new Label_4A(this));
4014
+ this.registerPlugin(new Label_4A_01(this));
4015
+ this.registerPlugin(new Label_4A_DIS(this));
4016
+ this.registerPlugin(new Label_4A_DOOR(this));
4017
+ this.registerPlugin(new Label_4A_Slash_01(this));
3783
4018
  this.registerPlugin(new Label_4N(this));
3784
4019
  this.registerPlugin(new Label_B6_Forwardslash(this));
3785
- this.registerPlugin(new Label_H1_FPN(this));
3786
4020
  this.registerPlugin(new Label_H1_FLR(this));
3787
- this.registerPlugin(new Label_H1_FTX(this));
3788
- this.registerPlugin(new Label_H1_INI(this));
3789
4021
  this.registerPlugin(new Label_H1_OHMA(this));
3790
- this.registerPlugin(new Label_H1_POS(this));
3791
4022
  this.registerPlugin(new Label_H1_WRN(this));
4023
+ this.registerPlugin(new Label_H1(this));
3792
4024
  this.registerPlugin(new Label_HX(this));
3793
4025
  this.registerPlugin(new Label_80(this));
3794
4026
  this.registerPlugin(new Label_83(this));
@@ -3836,29 +4068,30 @@ var MessageDecoder = class {
3836
4068
  console.log("Usable plugins");
3837
4069
  console.log(usablePlugins);
3838
4070
  }
3839
- let result;
3840
- if (usablePlugins.length > 0) {
3841
- const plugin = usablePlugins[0];
4071
+ let result = {
4072
+ decoded: false,
4073
+ error: "No known decoder plugin for this message",
4074
+ decoder: {
4075
+ name: "none",
4076
+ type: "none",
4077
+ decodeLevel: "none"
4078
+ },
4079
+ message,
4080
+ remaining: {
4081
+ text: message.text
4082
+ },
4083
+ raw: {},
4084
+ formatted: {
4085
+ description: "Not Decoded",
4086
+ items: []
4087
+ }
4088
+ };
4089
+ for (let i = 0; i < usablePlugins.length; i++) {
4090
+ const plugin = usablePlugins[i];
3842
4091
  result = plugin.decode(message);
3843
- } else {
3844
- result = {
3845
- decoded: false,
3846
- error: "No known decoder plugin for this message",
3847
- decoder: {
3848
- name: "none",
3849
- type: "none",
3850
- decodeLevel: "none"
3851
- },
3852
- message,
3853
- remaining: {
3854
- text: message.text
3855
- },
3856
- raw: {},
3857
- formatted: {
3858
- description: "Not Decoded",
3859
- items: []
3860
- }
3861
- };
4092
+ if (result.decoded) {
4093
+ break;
4094
+ }
3862
4095
  }
3863
4096
  if (options.debug) {
3864
4097
  console.log("Result");