@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.js CHANGED
@@ -49,6 +49,74 @@ var IcaoDecoder = class {
49
49
  }
50
50
  };
51
51
 
52
+ // lib/DateTimeUtils.ts
53
+ var DateTimeUtils = class {
54
+ // Expects a four digit UTC time string (HHMM)
55
+ static UTCToString(UTCString) {
56
+ let utcDate = /* @__PURE__ */ new Date();
57
+ utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
58
+ return utcDate.toTimeString();
59
+ }
60
+ // Expects a six digit date string and a four digit UTC time string
61
+ // (DDMMYY) (HHMM)
62
+ static UTCDateTimeToString(dateString, timeString) {
63
+ let utcDate = /* @__PURE__ */ new Date();
64
+ utcDate.setUTCDate(+dateString.substr(0, 2));
65
+ utcDate.setUTCMonth(+dateString.substr(2, 2));
66
+ if (dateString.length === 6) {
67
+ utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
68
+ }
69
+ if (timeString.length === 6) {
70
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
71
+ } else {
72
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
73
+ }
74
+ return utcDate.toUTCString();
75
+ }
76
+ /**
77
+ *
78
+ * @param time HHMMSS
79
+ * @returns seconds since midnight
80
+ */
81
+ static convertHHMMSSToTod(time) {
82
+ const h = Number(time.substring(0, 2));
83
+ const m = Number(time.substring(2, 4));
84
+ const s = Number(time.substring(4, 6));
85
+ const tod = h * 3600 + m * 60 + s;
86
+ return tod;
87
+ }
88
+ /**
89
+ *
90
+ * @param time HHMMSS
91
+ * @param date MMDDYY or MMDDYYYY
92
+ * @returns seconds since epoch
93
+ */
94
+ static convertDateTimeToEpoch(time, date) {
95
+ if (date.length === 6) {
96
+ date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
97
+ }
98
+ 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`;
99
+ const millis = Date.parse(timestamp);
100
+ return millis / 1e3;
101
+ }
102
+ /**
103
+ * Converts a timestamp to a string
104
+ *
105
+ * ISO-8601 format for 'epoch'
106
+ * HH:MM:SS for 'tod'
107
+ * @param time
108
+ * @param format
109
+ * @returns
110
+ */
111
+ static timestampToString(time, format) {
112
+ const date = new Date(time * 1e3);
113
+ if (format == "tod") {
114
+ return date.toISOString().slice(11, 19);
115
+ }
116
+ return date.toISOString().slice(0, -5) + "Z";
117
+ }
118
+ };
119
+
52
120
  // lib/DecoderPlugin.ts
53
121
  var DecoderPlugin = class {
54
122
  decoder;
@@ -167,8 +235,124 @@ var CoordinateUtils = class {
167
235
  }
168
236
  };
169
237
 
238
+ // lib/utils/route_utils.ts
239
+ var RouteUtils = class _RouteUtils {
240
+ static formatFlightState(state) {
241
+ switch (state) {
242
+ case "TO":
243
+ return "Takeoff";
244
+ case "IC":
245
+ return "Initial Climb";
246
+ case "CL":
247
+ return "Climb";
248
+ case "ER":
249
+ return "En Route";
250
+ case "DC":
251
+ return "Descent";
252
+ case "AP":
253
+ return "Approach";
254
+ default:
255
+ return `Unknown ${state}`;
256
+ }
257
+ }
258
+ static routeToString(route) {
259
+ let str = "";
260
+ if (route.name) {
261
+ str += route.name;
262
+ }
263
+ if (route.runway) {
264
+ str += `(${route.runway})`;
265
+ }
266
+ if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
267
+ str += " starting at ";
268
+ } else if (str.length !== 0 && route.waypoints) {
269
+ str += ": ";
270
+ }
271
+ if (route.waypoints) {
272
+ str += _RouteUtils.waypointsToString(route.waypoints);
273
+ }
274
+ return str;
275
+ }
276
+ static waypointToString(waypoint) {
277
+ let s = waypoint.name;
278
+ if (waypoint.latitude && waypoint.longitude) {
279
+ s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
280
+ }
281
+ if (waypoint.offset) {
282
+ s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
283
+ }
284
+ if (waypoint.time && waypoint.timeFormat) {
285
+ s += `@${DateTimeUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
286
+ }
287
+ return s;
288
+ }
289
+ static getWaypoint(leg) {
290
+ const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
291
+ if (regex?.length == 4) {
292
+ return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
293
+ }
294
+ const waypoint = leg.split(",");
295
+ if (waypoint.length == 2) {
296
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
297
+ if (position) {
298
+ return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
299
+ }
300
+ }
301
+ if (leg.length == 13 || leg.length == 14) {
302
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
303
+ const name = waypoint.length == 2 ? waypoint[0] : "";
304
+ if (position) {
305
+ return { name, latitude: position.latitude, longitude: position.longitude };
306
+ }
307
+ }
308
+ return { name: leg };
309
+ }
310
+ static waypointsToString(waypoints) {
311
+ let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
312
+ if (str.startsWith(" > ")) {
313
+ str = ">>" + str.slice(2);
314
+ }
315
+ return str;
316
+ }
317
+ };
318
+
170
319
  // lib/utils/result_formatter.ts
171
320
  var ResultFormatter = class {
321
+ static state_change(decodeResult, from, to) {
322
+ decodeResult.raw.state_change = {
323
+ from,
324
+ to
325
+ };
326
+ from = RouteUtils.formatFlightState(from);
327
+ to = RouteUtils.formatFlightState(to);
328
+ decodeResult.formatted.items.push({
329
+ type: "state_change",
330
+ code: "STATE_CHANGE",
331
+ label: "State Change",
332
+ value: `${from} -> ${to}`
333
+ });
334
+ }
335
+ static freetext(decodeResult, value) {
336
+ decodeResult.raw.freetext = value;
337
+ decodeResult.formatted.items.push({
338
+ type: "freetext",
339
+ code: "FREE_TEXT",
340
+ label: "Free Text",
341
+ value
342
+ });
343
+ }
344
+ static door_event(decodeResult, name, state) {
345
+ decodeResult.raw.door_event = {
346
+ door: name,
347
+ state
348
+ };
349
+ decodeResult.formatted.items.push({
350
+ type: "door_event",
351
+ code: "DOOR",
352
+ label: "Door Event",
353
+ value: `${name} ${state}`
354
+ });
355
+ }
172
356
  static position(decodeResult, value) {
173
357
  decodeResult.raw.position = value;
174
358
  decodeResult.formatted.items.push({
@@ -189,16 +373,41 @@ var ResultFormatter = class {
189
373
  }
190
374
  static flightNumber(decodeResult, value) {
191
375
  decodeResult.raw.flight_number = value;
376
+ decodeResult.formatted.items.push({
377
+ type: "flight_number",
378
+ code: "FLIGHT",
379
+ label: "Flight Number",
380
+ value: decodeResult.raw.flight_number
381
+ });
192
382
  }
193
- static departureAirport(decodeResult, value) {
194
- decodeResult.raw.departure_icao = value;
383
+ static callsign(decodeResult, value) {
384
+ decodeResult.raw.callsign = value;
195
385
  decodeResult.formatted.items.push({
196
- type: "origin",
197
- code: "ORG",
198
- label: "Origin",
199
- value: decodeResult.raw.departure_icao
386
+ type: "callsign",
387
+ code: "CALLSIGN",
388
+ label: "Callsign",
389
+ value: decodeResult.raw.callsign
200
390
  });
201
391
  }
392
+ static departureAirport(decodeResult, value, type = "ICAO") {
393
+ if (type === "ICAO") {
394
+ decodeResult.raw.departure_icao = value;
395
+ decodeResult.formatted.items.push({
396
+ type: "icao",
397
+ code: "ORG",
398
+ label: "Origin",
399
+ value
400
+ });
401
+ } else {
402
+ decodeResult.raw.departure_iata = value;
403
+ decodeResult.formatted.items.push({
404
+ type: "iata",
405
+ code: "ORG",
406
+ label: "Origin",
407
+ value
408
+ });
409
+ }
410
+ }
202
411
  static departureRunway(decodeResult, value) {
203
412
  decodeResult.raw.departure_runway = value;
204
413
  decodeResult.formatted.items.push({
@@ -208,33 +417,52 @@ var ResultFormatter = class {
208
417
  value: decodeResult.raw.departure_runway
209
418
  });
210
419
  }
211
- static arrivalAirport(decodeResult, value) {
212
- decodeResult.raw.arrival_icao = value;
213
- decodeResult.formatted.items.push({
214
- type: "destination",
215
- code: "DST",
216
- label: "Destination",
217
- value: decodeResult.raw.arrival_icao
218
- });
420
+ static arrivalAirport(decodeResult, value, type = "ICAO") {
421
+ if (type === "ICAO") {
422
+ decodeResult.raw.arrival_icao = value;
423
+ decodeResult.formatted.items.push({
424
+ type: "icao",
425
+ code: "DST",
426
+ label: "Destination",
427
+ value
428
+ });
429
+ } else {
430
+ decodeResult.raw.arrival_iata = value;
431
+ decodeResult.formatted.items.push({
432
+ type: "iata",
433
+ code: "DST",
434
+ label: "Destination",
435
+ value
436
+ });
437
+ }
219
438
  }
220
439
  static alternateAirport(decodeResult, value) {
221
440
  decodeResult.raw.alternate_icao = value;
222
441
  decodeResult.formatted.items.push({
223
- type: "destination",
442
+ type: "icao",
224
443
  code: "ALT_DST",
225
444
  label: "Alternate Destination",
226
445
  value: decodeResult.raw.alternate_icao
227
446
  });
228
447
  }
229
- // FIXME - make seconds since midnight for time of day
230
- static eta(decodeResult, value) {
231
- decodeResult.raw.eta_time = value;
232
- decodeResult.formatted.items.push({
233
- type: "eta",
234
- code: "ETA",
235
- label: "Estimated Time of Arrival",
236
- value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
237
- });
448
+ static eta(decodeResult, time, type = "tod") {
449
+ if (type === "tod") {
450
+ decodeResult.raw.eta_time = time;
451
+ decodeResult.formatted.items.push({
452
+ type: "time_of_day",
453
+ code: "ETA",
454
+ label: "Estimated Time of Arrival",
455
+ value: DateTimeUtils.timestampToString(time, "tod")
456
+ });
457
+ } else {
458
+ decodeResult.raw.eta_date = time;
459
+ decodeResult.formatted.items.push({
460
+ type: "epoch",
461
+ code: "ETA",
462
+ label: "Estimated Time of Arrival",
463
+ value: DateTimeUtils.timestampToString(time, "epoch")
464
+ });
465
+ }
238
466
  }
239
467
  static arrivalRunway(decodeResult, value) {
240
468
  decodeResult.raw.arrival_runway = value;
@@ -291,12 +519,12 @@ var ResultFormatter = class {
291
519
  });
292
520
  }
293
521
  static temperature(decodeResult, value) {
294
- decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
522
+ decodeResult.raw.outside_air_temperature = Number(value.replace("M", "-").replace("P", "+"));
295
523
  decodeResult.formatted.items.push({
296
524
  type: "outside_air_temperature",
297
525
  code: "OATEMP",
298
526
  label: "Outside Air Temperature (C)",
299
- value: `${decodeResult.raw.outside_air_temperature}`
527
+ value: `${decodeResult.raw.outside_air_temperature} degrees`
300
528
  });
301
529
  }
302
530
  static heading(decodeResult, value) {
@@ -319,42 +547,56 @@ var ResultFormatter = class {
319
547
  }
320
548
  static out(decodeResult, time) {
321
549
  decodeResult.raw.out_time = time;
322
- const date = new Date(time * 1e3);
323
550
  decodeResult.formatted.items.push({
324
551
  type: "time_of_day",
325
552
  code: "OUT",
326
553
  label: "Out of Gate Time",
327
- value: date.toISOString().slice(11, 19)
554
+ value: DateTimeUtils.timestampToString(time, "tod")
328
555
  });
329
556
  }
330
557
  static off(decodeResult, time) {
331
558
  decodeResult.raw.off_time = time;
332
- const date = new Date(time * 1e3);
333
559
  decodeResult.formatted.items.push({
334
560
  type: "time_of_day",
335
561
  code: "OFF",
336
562
  label: "Takeoff Time",
337
- value: date.toISOString().slice(11, 19)
563
+ value: DateTimeUtils.timestampToString(time, "tod")
338
564
  });
339
565
  }
340
566
  static on(decodeResult, time) {
341
567
  decodeResult.raw.on_time = time;
342
- const date = new Date(time * 1e3);
343
568
  decodeResult.formatted.items.push({
344
569
  type: "time_of_day",
345
570
  code: "ON",
346
571
  label: "Landing Time",
347
- value: date.toISOString().slice(11, 19)
572
+ value: DateTimeUtils.timestampToString(time, "tod")
348
573
  });
349
574
  }
350
575
  static in(decodeResult, time) {
351
576
  decodeResult.raw.in_time = time;
352
- const date = new Date(time * 1e3);
353
577
  decodeResult.formatted.items.push({
354
578
  type: "time_of_day",
355
579
  code: "IN",
356
580
  label: "In Gate Time",
357
- value: date.toISOString().slice(11, 19)
581
+ value: DateTimeUtils.timestampToString(time, "tod")
582
+ });
583
+ }
584
+ static time_of_day(decodeResult, time) {
585
+ decodeResult.raw.time_of_day = time;
586
+ decodeResult.formatted.items.push({
587
+ type: "time_of_day",
588
+ code: "MSG_TOD",
589
+ label: "Message Timestamp",
590
+ value: DateTimeUtils.timestampToString(time, "tod")
591
+ });
592
+ }
593
+ static text(decodeResult, text2) {
594
+ decodeResult.raw.text = text2;
595
+ decodeResult.formatted.items.push({
596
+ type: "text",
597
+ code: "TEXT",
598
+ label: "Text Message",
599
+ value: text2
358
600
  });
359
601
  }
360
602
  static unknown(decodeResult, value) {
@@ -362,9 +604,9 @@ var ResultFormatter = class {
362
604
  }
363
605
  };
364
606
 
365
- // lib/plugins/Label_5Z.ts
366
- var Label_5Z = class extends DecoderPlugin {
367
- name = "label-5z";
607
+ // lib/plugins/Label_5Z_Slash.ts
608
+ var Label_5Z_Slash = class extends DecoderPlugin {
609
+ name = "label-5z-slash";
368
610
  descriptions = {
369
611
  B1: "Request Weight and Balance",
370
612
  B3: "Request Departure Clearance",
@@ -382,6 +624,7 @@ var Label_5Z = class extends DecoderPlugin {
382
624
  D6: "From-To + Date",
383
625
  D7: "From-To + Alternate + Time",
384
626
  EO: "In Range",
627
+ ET: "Expected Time",
385
628
  PW: "Position Weather",
386
629
  RL: "Request Release",
387
630
  R3: "Request HOWGOZIT Message",
@@ -392,48 +635,82 @@ var Label_5Z = class extends DecoderPlugin {
392
635
  };
393
636
  qualifiers() {
394
637
  return {
395
- labels: ["5Z"]
638
+ labels: ["5Z"],
639
+ preambles: ["/"]
396
640
  };
397
641
  }
398
642
  decode(message, options = {}) {
399
643
  const decodeResult = this.defaultResult();
400
644
  decodeResult.decoder.name = this.name;
401
645
  decodeResult.formatted.description = "Airline Designated Downlink";
402
- const uaRegex = /^\/(?<type>\w+) (?<remainder>.+)/;
403
- let results = message.text.match(uaRegex);
404
- if (results && results.length >= 2) {
405
- const type = results.groups.type.split("/")[0];
406
- const { remainder } = results.groups;
407
- const typeDescription = this.descriptions[type] ? this.descriptions[type] : "Unknown";
646
+ const lines = message.text.split("\r\n");
647
+ if (lines[0] === "/TXT") {
648
+ ResultFormatter.text(decodeResult, lines.slice(1).join("\r\n"));
649
+ decodeResult.decoded = true;
650
+ decodeResult.decoder.decodeLevel = "full";
651
+ return decodeResult;
652
+ }
653
+ const data = lines[0].split("/");
654
+ const header = data[1].split(" ");
655
+ const type = header[0];
656
+ const typeDescription = this.descriptions[type];
657
+ if (typeDescription) {
408
658
  decodeResult.raw.airline = "United Airlines";
409
659
  decodeResult.formatted.items.push({
410
660
  type: "airline",
661
+ code: "AIRLINE",
411
662
  label: "Airline",
412
663
  value: "United Airlines"
413
664
  });
414
665
  decodeResult.raw.message_type = type;
415
666
  decodeResult.formatted.items.push({
416
667
  type: "message_type",
668
+ code: "MSG_TYPE",
417
669
  label: "Message Type",
418
670
  value: `${typeDescription} (${type})`
419
671
  });
420
- if (type === "B3") {
421
- const rdcRegex = /^(?<from>\w\w\w)(?<to>\w\w\w) (?<unknown1>\d\d) R(?<runway>.+) G(?<unknown2>.+)$/;
422
- results = remainder.match(rdcRegex);
423
- if (results) {
424
- ResultFormatter.departureAirport(decodeResult, results.groups.from);
425
- ResultFormatter.arrivalAirport(decodeResult, results.groups.to);
426
- ResultFormatter.arrivalRunway(decodeResult, results.groups.runway);
427
- } else {
428
- if (options.debug) {
429
- console.log(`Decoder: Unkown 5Z RDC format: ${remainder}`);
430
- }
672
+ if (type === "B3" && data[1] === "B3 TO DATA REQ ") {
673
+ const info = data[2].split(" ");
674
+ ResultFormatter.departureAirport(decodeResult, info[1]);
675
+ ResultFormatter.arrivalAirport(decodeResult, info[2]);
676
+ decodeResult.raw.day_of_month = Number(info[3]);
677
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(info[4]));
678
+ ResultFormatter.arrivalRunway(decodeResult, info[5].slice(1));
679
+ decodeResult.remaining.text = data.slice(3).join("/");
680
+ } else if (type === "B3") {
681
+ ResultFormatter.departureAirport(decodeResult, header[1].substring(0, 3), "IATA");
682
+ ResultFormatter.arrivalAirport(decodeResult, header[1].substring(3), "IATA");
683
+ decodeResult.raw.day_of_month = Number(header[2]);
684
+ ResultFormatter.arrivalRunway(decodeResult, header[3].slice(1));
685
+ if (header.length > 4) {
686
+ decodeResult.remaining.text = header.slice(4).join(" ");
431
687
  }
688
+ } else if (type === "C3" && data[1] === "C3 GATE REQ ") {
689
+ const info = data[2].split(" ");
690
+ ResultFormatter.departureAirport(decodeResult, info[1]);
691
+ ResultFormatter.arrivalAirport(decodeResult, info[2]);
692
+ decodeResult.raw.day_of_month = Number(info[3]);
693
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(info[4]));
694
+ decodeResult.remaining.text = info.slice(5).join(" ");
695
+ } else if (type === "C3") {
696
+ ResultFormatter.departureAirport(decodeResult, header[1].substring(0, 3), "IATA");
697
+ ResultFormatter.arrivalAirport(decodeResult, header[1].substring(3), "IATA");
698
+ } else if (type === "ET") {
699
+ const airports = data[2].split(" ");
700
+ ResultFormatter.departureAirport(decodeResult, airports[1]);
701
+ ResultFormatter.arrivalAirport(decodeResult, airports[2]);
702
+ decodeResult.raw.day_of_month = Number(airports[3]);
703
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(airports[4]));
704
+ const estimates = data[3].split(" ");
705
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(estimates[1] + "00"));
706
+ decodeResult.remaining.text = estimates[2];
432
707
  } else {
433
- decodeResult.remaining.text = remainder;
708
+ if (options.debug) {
709
+ console.log(`Decoder: Unkown 5Z RDC format: ${message.text}`);
710
+ }
434
711
  }
435
712
  decodeResult.decoded = true;
436
- decodeResult.decoder.decodeLevel = "partial";
713
+ decodeResult.decoder.decodeLevel = decodeResult.remaining.text ? "partial" : "full";
437
714
  } else {
438
715
  if (options.debug) {
439
716
  console.log(`Decoder: Unknown 5Z message: ${message.text}`);
@@ -534,129 +811,6 @@ var Label_10_POS = class extends DecoderPlugin {
534
811
  }
535
812
  };
536
813
 
537
- // lib/DateTimeUtils.ts
538
- var DateTimeUtils = class {
539
- // Expects a four digit UTC time string (HHMM)
540
- static UTCToString(UTCString) {
541
- let utcDate = /* @__PURE__ */ new Date();
542
- utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
543
- return utcDate.toTimeString();
544
- }
545
- // Expects a six digit date string and a four digit UTC time string
546
- // (DDMMYY) (HHMM)
547
- static UTCDateTimeToString(dateString, timeString) {
548
- let utcDate = /* @__PURE__ */ new Date();
549
- utcDate.setUTCDate(+dateString.substr(0, 2));
550
- utcDate.setUTCMonth(+dateString.substr(2, 2));
551
- if (dateString.length === 6) {
552
- utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
553
- }
554
- if (timeString.length === 6) {
555
- utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
556
- } else {
557
- utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
558
- }
559
- return utcDate.toUTCString();
560
- }
561
- /**
562
- *
563
- * @param time HHMMSS
564
- * @returns seconds since midnight
565
- */
566
- static convertHHMMSSToTod(time) {
567
- const h = Number(time.substring(0, 2));
568
- const m = Number(time.substring(2, 4));
569
- const s = Number(time.substring(4, 6));
570
- const tod = h * 3600 + m * 60 + s;
571
- return tod;
572
- }
573
- /**
574
- *
575
- * @param time HHMMSS
576
- * @param date MMDDYY or MMDDYYYY
577
- * @returns seconds since epoch
578
- */
579
- static convertDateTimeToEpoch(time, date) {
580
- if (date.length === 6) {
581
- date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
582
- }
583
- 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`;
584
- const millis = Date.parse(timestamp);
585
- return millis / 1e3;
586
- }
587
- };
588
-
589
- // lib/utils/route_utils.ts
590
- var RouteUtils = class _RouteUtils {
591
- static routeToString(route) {
592
- let str = "";
593
- if (route.name) {
594
- str += route.name;
595
- }
596
- if (route.runway) {
597
- str += `(${route.runway})`;
598
- }
599
- if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
600
- str += " starting at ";
601
- } else if (str.length !== 0 && route.waypoints) {
602
- str += ": ";
603
- }
604
- if (route.waypoints) {
605
- str += _RouteUtils.waypointsToString(route.waypoints);
606
- }
607
- return str;
608
- }
609
- static waypointToString(waypoint) {
610
- let s = waypoint.name;
611
- if (waypoint.latitude && waypoint.longitude) {
612
- s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
613
- }
614
- if (waypoint.offset) {
615
- s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
616
- }
617
- if (waypoint.time && waypoint.timeFormat) {
618
- s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
619
- }
620
- return s;
621
- }
622
- static getWaypoint(leg) {
623
- const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
624
- if (regex?.length == 4) {
625
- return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
626
- }
627
- const waypoint = leg.split(",");
628
- if (waypoint.length == 2) {
629
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
630
- if (position) {
631
- return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
632
- }
633
- }
634
- if (leg.length == 13 || leg.length == 14) {
635
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
636
- const name = waypoint.length == 2 ? waypoint[0] : "";
637
- if (position) {
638
- return { name, latitude: position.latitude, longitude: position.longitude };
639
- }
640
- }
641
- return { name: leg };
642
- }
643
- // move out if we want public
644
- static timestampToString(time, format) {
645
- const date = new Date(time * 1e3);
646
- if (format == "tod") {
647
- return date.toISOString().slice(11, 19);
648
- }
649
- return date.toISOString().slice(0, -5) + "Z";
650
- }
651
- static waypointsToString(waypoints) {
652
- let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
653
- if (str.startsWith(" > ")) {
654
- str = ">>" + str.slice(2);
655
- }
656
- return str;
657
- }
658
- };
659
-
660
814
  // lib/plugins/Label_10_Slash.ts
661
815
  var Label_10_Slash = class extends DecoderPlugin {
662
816
  // eslint-disable-line camelcase
@@ -692,7 +846,7 @@ var Label_10_Slash = class extends DecoderPlugin {
692
846
  ResultFormatter.heading(decodeResult, Number(parts[5]));
693
847
  ResultFormatter.altitude(decodeResult, 100 * Number(parts[6]));
694
848
  ResultFormatter.arrivalAirport(decodeResult, parts[7]);
695
- ResultFormatter.eta(decodeResult, parts[8] + "00");
849
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[8] + "00"));
696
850
  const waypoints = [{
697
851
  name: parts[11]
698
852
  }, {
@@ -1026,28 +1180,12 @@ var Label_1M_Slash = class extends DecoderPlugin {
1026
1180
  console.log(results);
1027
1181
  }
1028
1182
  decodeResult.raw.flight_number = results[0];
1029
- decodeResult.raw.departure_icao = results[3];
1030
- decodeResult.raw.arrival_icao = results[4];
1031
- decodeResult.raw.alternate_icao = results[5];
1032
- decodeResult.raw.arrival_runway = results[8].replace(decodeResult.raw.arrival_icao, "");
1033
- decodeResult.formatted.items.push({
1034
- type: "eta",
1035
- code: "ETA",
1036
- label: "Estimated Time of Arrival",
1037
- value: DateTimeUtils.UTCDateTimeToString(results[2], results[7])
1038
- });
1039
- decodeResult.formatted.items.push({
1040
- type: "destination",
1041
- code: "DST",
1042
- label: "Destination",
1043
- value: decodeResult.raw.arrival_icao
1044
- });
1045
- decodeResult.formatted.items.push({
1046
- type: "origin",
1047
- code: "ORG",
1048
- label: "Origin",
1049
- value: decodeResult.raw.departure_icao
1050
- });
1183
+ ResultFormatter.departureAirport(decodeResult, results[3]);
1184
+ ResultFormatter.arrivalAirport(decodeResult, results[4]);
1185
+ ResultFormatter.alternateAirport(decodeResult, results[5]);
1186
+ ResultFormatter.arrivalRunway(decodeResult, results[8].replace(results[4], ""));
1187
+ const yymmdd = results[2];
1188
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertDateTimeToEpoch(results[7] + "00", yymmdd.substring(2, 4) + yymmdd.substring(4, 6) + yymmdd.substring(0, 2)), "epoch");
1051
1189
  }
1052
1190
  decodeResult.decoded = true;
1053
1191
  decodeResult.decoder.decodeLevel = "partial";
@@ -1134,15 +1272,15 @@ var Label_21_POS = class extends DecoderPlugin {
1134
1272
  const fields = content.split(",");
1135
1273
  if (fields.length == 9) {
1136
1274
  processPosition(decodeResult, fields[0].trim());
1137
- processAlt(decodeResult, fields[3]);
1138
- processTemp(decodeResult, fields[6]);
1139
- processArrvApt(decodeResult, fields[8]);
1275
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2]));
1276
+ ResultFormatter.altitude(decodeResult, Number(fields[3]));
1277
+ ResultFormatter.temperature(decodeResult, fields[6].replace(/ /g, ""));
1278
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[7]));
1279
+ ResultFormatter.arrivalAirport(decodeResult, fields[8]);
1140
1280
  decodeResult.remaining.text = [
1141
1281
  fields[1],
1142
- fields[2],
1143
1282
  fields[4],
1144
- fields[5],
1145
- fields[7]
1283
+ fields[5]
1146
1284
  ].join(",");
1147
1285
  decodeResult.decoded = true;
1148
1286
  decodeResult.decoder.decodeLevel = "partial";
@@ -1158,7 +1296,7 @@ var Label_21_POS = class extends DecoderPlugin {
1158
1296
  };
1159
1297
  function processPosition(decodeResult, value) {
1160
1298
  if (value.length !== 16 && value[0] !== "N" && value[0] !== "S" && value[8] !== "W" && value[8] !== "E") {
1161
- return false;
1299
+ return;
1162
1300
  }
1163
1301
  const latDir = value[0] === "N" ? 1 : -1;
1164
1302
  const lonDir = value[8] === "E" ? 1 : -1;
@@ -1166,45 +1304,52 @@ function processPosition(decodeResult, value) {
1166
1304
  latitude: latDir * Number(value.substring(1, 7)),
1167
1305
  longitude: lonDir * Number(value.substring(9, 15))
1168
1306
  };
1169
- if (position) {
1170
- decodeResult.raw.position = position;
1171
- decodeResult.formatted.items.push({
1172
- type: "aircraft_position",
1173
- code: "POS",
1174
- label: "Aircraft Position",
1175
- value: CoordinateUtils.coordinateString(position)
1176
- });
1177
- }
1178
- return !!position;
1179
- }
1180
- function processAlt(decodeResult, value) {
1181
- decodeResult.raw.altitude = Number(value);
1182
- decodeResult.formatted.items.push({
1183
- type: "altitude",
1184
- code: "ALT",
1185
- label: "Altitude",
1186
- value: `${decodeResult.raw.altitude} feet`
1187
- });
1188
- }
1189
- function processTemp(decodeResult, value) {
1190
- decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
1191
- decodeResult.formatted.items.push({
1192
- type: "outside_air_temperature",
1193
- code: "OATEMP",
1194
- label: "Outside Air Temperature (C)",
1195
- value: `${decodeResult.raw.outside_air_temperature}`
1196
- });
1197
- }
1198
- function processArrvApt(decodeResult, value) {
1199
- decodeResult.raw.arrival_icao = value;
1200
- decodeResult.formatted.items.push({
1201
- type: "destination",
1202
- code: "DST",
1203
- label: "Destination",
1204
- value: decodeResult.raw.arrival_icao
1205
- });
1307
+ ResultFormatter.position(decodeResult, position);
1206
1308
  }
1207
1309
 
1310
+ // lib/plugins/Label_24_Slash.ts
1311
+ var Label_24_Slash = class extends DecoderPlugin {
1312
+ name = "label-24-slash";
1313
+ qualifiers() {
1314
+ return {
1315
+ labels: ["24"],
1316
+ preambles: ["/"]
1317
+ };
1318
+ }
1319
+ decode(message, options = {}) {
1320
+ const decodeResult = this.defaultResult();
1321
+ decodeResult.decoder.name = this.name;
1322
+ decodeResult.formatted.description = "Position Report";
1323
+ decodeResult.message = message;
1324
+ const fields = message.text.split("/");
1325
+ if (fields.length == 10 && fields[0] == "" && fields[9] == "") {
1326
+ const mmddyy = fields[1].substring(4, 6) + fields[1].substring(2, 4) + fields[1].substring(0, 2);
1327
+ const hhmmss = fields[2] + "00";
1328
+ decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(hhmmss, mmddyy);
1329
+ ResultFormatter.flightNumber(decodeResult, fields[3]);
1330
+ ResultFormatter.altitude(decodeResult, Number(fields[4]));
1331
+ const lat = fields[5];
1332
+ const lon = fields[6];
1333
+ const position = {
1334
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1)),
1335
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1))
1336
+ };
1337
+ ResultFormatter.position(decodeResult, position);
1338
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[8] + "00"));
1339
+ decodeResult.remaining.text = fields[7];
1340
+ decodeResult.decoded = true;
1341
+ decodeResult.decoder.decodeLevel = "partial";
1342
+ } else {
1343
+ if (options.debug) {
1344
+ console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}. Message: ${message.text}`);
1345
+ }
1346
+ decodeResult.decoded = false;
1347
+ decodeResult.decoder.decodeLevel = "none";
1348
+ }
1349
+ return decodeResult;
1350
+ }
1351
+ };
1352
+
1208
1353
  // lib/plugins/Label_30_Slash_EA.ts
1209
1354
  var Label_30_Slash_EA = class extends DecoderPlugin {
1210
1355
  name = "label-30-slash-ea";
@@ -1219,27 +1364,16 @@ var Label_30_Slash_EA = class extends DecoderPlugin {
1219
1364
  decodeResult.decoder.name = this.name;
1220
1365
  decodeResult.formatted.description = "ETA Report";
1221
1366
  decodeResult.message = message;
1222
- const results = message.text.split(/\n|\//).slice(1);
1223
- if (results) {
1224
- if (options.debug) {
1225
- console.log(`Label 30 EA: results`);
1226
- console.log(results);
1227
- }
1228
- }
1229
- decodeResult.formatted.items.push({
1230
- type: "eta",
1231
- code: "ETA",
1232
- label: "Estimated Time of Arrival",
1233
- value: DateTimeUtils.UTCToString(results[0].substr(2, 4))
1234
- });
1235
- if (results[1].substr(0, 2) === "DS") {
1236
- decodeResult.raw.arrival_icao = results[1].substr(2, 4);
1237
- decodeResult.formatted.items.push({
1238
- type: "destination",
1239
- code: "DST",
1240
- label: "Destination",
1241
- value: decodeResult.raw.arrival_icao
1242
- });
1367
+ const results = message.text.split(/\n|\//).slice(1);
1368
+ if (results) {
1369
+ if (options.debug) {
1370
+ console.log(`Label 30 EA: results`);
1371
+ console.log(results);
1372
+ }
1373
+ }
1374
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results[0].substr(2, 4) + "00"));
1375
+ if (results[1].substring(0, 2) === "DS") {
1376
+ ResultFormatter.arrivalAirport(decodeResult, results[1].substring(2, 6));
1243
1377
  decodeResult.remaining.text = "/".concat(results[2]);
1244
1378
  } else {
1245
1379
  decodeResult.remaining.text = "/".concat(results[1], "/", results[2]);
@@ -1531,6 +1665,221 @@ var Label_44_POS = class extends DecoderPlugin {
1531
1665
  }
1532
1666
  };
1533
1667
 
1668
+ // lib/plugins/Label_4A.ts
1669
+ var Label_4A = class extends DecoderPlugin {
1670
+ name = "label-4a";
1671
+ qualifiers() {
1672
+ return {
1673
+ labels: ["4A"]
1674
+ };
1675
+ }
1676
+ decode(message, options = {}) {
1677
+ const decodeResult = this.defaultResult();
1678
+ decodeResult.decoder.name = this.name;
1679
+ decodeResult.message = message;
1680
+ decodeResult.formatted.description = "Latest New Format";
1681
+ let text2 = message.text;
1682
+ if (text2.match(/^M\d{2}A\w{6}/)) {
1683
+ ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1684
+ text2 = text2.substring(10);
1685
+ }
1686
+ decodeResult.decoded = true;
1687
+ const fields = text2.split(",");
1688
+ if (fields.length === 11) {
1689
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0]));
1690
+ ResultFormatter.tail(decodeResult, fields[2].replace(".", ""));
1691
+ if (fields[3])
1692
+ ResultFormatter.callsign(decodeResult, fields[3]);
1693
+ ResultFormatter.departureAirport(decodeResult, fields[4]);
1694
+ ResultFormatter.arrivalAirport(decodeResult, fields[5]);
1695
+ ResultFormatter.altitude(decodeResult, text2.substring(48, 51) * 100);
1696
+ decodeResult.remaining.text = fields.slice(8).join(",");
1697
+ } else if (fields.length === 6) {
1698
+ if (fields[0].match(/^[NS]/)) {
1699
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(fields[0].substring(0, 13)));
1700
+ let wp1 = {
1701
+ name: fields[0].substring(13).trim(),
1702
+ time: DateTimeUtils.convertHHMMSSToTod(fields[1].substring(0, 6)),
1703
+ timeFormat: "tod"
1704
+ };
1705
+ ResultFormatter.altitude(decodeResult, fields[1].substring(6, 9) * 100);
1706
+ let wp2 = {
1707
+ name: fields[1].substring(9).trim(),
1708
+ time: DateTimeUtils.convertHHMMSSToTod(fields[2]),
1709
+ timeFormat: "tod"
1710
+ };
1711
+ decodeResult.raw.route = { waypoints: [wp1, wp2] };
1712
+ decodeResult.formatted.items.push({
1713
+ type: "aircraft_route",
1714
+ code: "ROUTE",
1715
+ label: "Aircraft Route",
1716
+ value: RouteUtils.routeToString(decodeResult.raw.route)
1717
+ });
1718
+ ResultFormatter.temperature(decodeResult, fields[3]);
1719
+ decodeResult.remaining.text = fields.slice(4).join(",");
1720
+ } else {
1721
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0]));
1722
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1]));
1723
+ decodeResult.remaining.text = fields[2];
1724
+ ResultFormatter.altitude(decodeResult, fields[3]);
1725
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates((fields[4] + fields[5]).replace(/[ \.]/g, "")));
1726
+ }
1727
+ } else {
1728
+ decodeResult.decoded = false;
1729
+ decodeResult.remaining.text = text2;
1730
+ }
1731
+ if (decodeResult.decoded) {
1732
+ if (!decodeResult.remaining.text)
1733
+ decodeResult.decoder.decodeLevel = "full";
1734
+ else
1735
+ decodeResult.decoder.decodeLevel = "partial";
1736
+ } else {
1737
+ decodeResult.decoder.decodeLevel = "none";
1738
+ }
1739
+ return decodeResult;
1740
+ }
1741
+ };
1742
+
1743
+ // lib/plugins/Label_4A_01.ts
1744
+ var Label_4A_01 = class extends DecoderPlugin {
1745
+ name = "label-4a-01";
1746
+ qualifiers() {
1747
+ return {
1748
+ labels: ["4A"],
1749
+ preambles: ["01"]
1750
+ };
1751
+ }
1752
+ decode(message, options = {}) {
1753
+ const decodeResult = this.defaultResult();
1754
+ decodeResult.decoder.name = this.name;
1755
+ decodeResult.message = message;
1756
+ decodeResult.formatted.description = "Latest New Format";
1757
+ decodeResult.decoded = true;
1758
+ 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)/);
1759
+ if (rgx) {
1760
+ ResultFormatter.state_change(decodeResult, rgx[1], rgx[2]);
1761
+ ResultFormatter.callsign(decodeResult, rgx[3]);
1762
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(rgx[4] + "00"));
1763
+ ResultFormatter.departureAirport(decodeResult, rgx[5]);
1764
+ ResultFormatter.arrivalAirport(decodeResult, rgx[6]);
1765
+ ResultFormatter.altitude(decodeResult, Number(rgx[7].replace(/ /g, "")));
1766
+ decodeResult.remaining.text = rgx[8];
1767
+ ResultFormatter.temperature(decodeResult, rgx[9].replace(/ /g, ""));
1768
+ } else {
1769
+ decodeResult.decoded = false;
1770
+ decodeResult.remaining.text = message.text;
1771
+ }
1772
+ if (decodeResult.decoded) {
1773
+ if (!decodeResult.remaining.text)
1774
+ decodeResult.decoder.decodeLevel = "full";
1775
+ else
1776
+ decodeResult.decoder.decodeLevel = "partial";
1777
+ } else {
1778
+ decodeResult.decoder.decodeLevel = "none";
1779
+ }
1780
+ return decodeResult;
1781
+ }
1782
+ };
1783
+
1784
+ // lib/plugins/Label_4A_DIS.ts
1785
+ var Label_4A_DIS = class extends DecoderPlugin {
1786
+ name = "label-4a-dis";
1787
+ qualifiers() {
1788
+ return {
1789
+ labels: ["4A"],
1790
+ preambles: ["DIS"]
1791
+ };
1792
+ }
1793
+ decode(message, options = {}) {
1794
+ const decodeResult = this.defaultResult();
1795
+ decodeResult.decoder.name = this.name;
1796
+ decodeResult.message = message;
1797
+ decodeResult.formatted.description = "Latest New Format";
1798
+ decodeResult.decoded = true;
1799
+ const fields = message.text.split(",");
1800
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1].substring(2) + "00"));
1801
+ ResultFormatter.callsign(decodeResult, fields[2]);
1802
+ ResultFormatter.freetext(decodeResult, fields.slice(3).join(""));
1803
+ if (decodeResult.decoded) {
1804
+ if (!decodeResult.remaining.text)
1805
+ decodeResult.decoder.decodeLevel = "full";
1806
+ else
1807
+ decodeResult.decoder.decodeLevel = "partial";
1808
+ } else {
1809
+ decodeResult.decoder.decodeLevel = "none";
1810
+ }
1811
+ return decodeResult;
1812
+ }
1813
+ };
1814
+
1815
+ // lib/plugins/Label_4A_DOOR.ts
1816
+ var Label_4A_DOOR = class extends DecoderPlugin {
1817
+ name = "label-4a-door";
1818
+ qualifiers() {
1819
+ return {
1820
+ labels: ["4A"],
1821
+ preambles: ["DOOR"]
1822
+ };
1823
+ }
1824
+ decode(message, options = {}) {
1825
+ const decodeResult = this.defaultResult();
1826
+ decodeResult.decoder.name = this.name;
1827
+ decodeResult.message = message;
1828
+ decodeResult.formatted.description = "Latest New Format";
1829
+ decodeResult.decoded = true;
1830
+ const fields = message.text.split(" ");
1831
+ if (fields.length === 3) {
1832
+ ResultFormatter.door_event(decodeResult, fields[0].split("/")[1], fields[1]);
1833
+ ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2] + "00"));
1834
+ } else {
1835
+ decodeResult.decoded = false;
1836
+ decodeResult.remaining.text = text;
1837
+ }
1838
+ if (decodeResult.decoded) {
1839
+ if (!decodeResult.remaining.text)
1840
+ decodeResult.decoder.decodeLevel = "full";
1841
+ else
1842
+ decodeResult.decoder.decodeLevel = "partial";
1843
+ } else {
1844
+ decodeResult.decoder.decodeLevel = "none";
1845
+ }
1846
+ return decodeResult;
1847
+ }
1848
+ };
1849
+
1850
+ // lib/plugins/Label_4A_Slash_01.ts
1851
+ var Label_4A_Slash_01 = class extends DecoderPlugin {
1852
+ name = "label-4a-slash-01";
1853
+ qualifiers() {
1854
+ return {
1855
+ labels: ["4A"],
1856
+ preambles: ["/01"]
1857
+ };
1858
+ }
1859
+ decode(message, options = {}) {
1860
+ const decodeResult = this.defaultResult();
1861
+ decodeResult.decoder.name = this.name;
1862
+ decodeResult.message = message;
1863
+ decodeResult.formatted.description = "Latest New Format";
1864
+ decodeResult.decoded = true;
1865
+ if (message.text.length === 5 && message.text.substring(0, 4) === "/01-") {
1866
+ decodeResult.remaining.text = message.text.substring(4);
1867
+ } else {
1868
+ decodeResult.decoded = false;
1869
+ decodeResult.remaining.text = message.text;
1870
+ }
1871
+ if (decodeResult.decoded) {
1872
+ if (!decodeResult.remaining.text)
1873
+ decodeResult.decoder.decodeLevel = "full";
1874
+ else
1875
+ decodeResult.decoder.decodeLevel = "partial";
1876
+ } else {
1877
+ decodeResult.decoder.decodeLevel = "none";
1878
+ }
1879
+ return decodeResult;
1880
+ }
1881
+ };
1882
+
1534
1883
  // lib/plugins/Label_4N.ts
1535
1884
  var Label_4N = class extends DecoderPlugin {
1536
1885
  name = "label-4n";
@@ -1544,20 +1893,20 @@ var Label_4N = class extends DecoderPlugin {
1544
1893
  decodeResult.decoder.name = this.name;
1545
1894
  decodeResult.message = message;
1546
1895
  decodeResult.formatted.description = "Airline Defined";
1547
- let text = message.text;
1548
- if (text.match(/^M\d{2}A\w{6}/)) {
1896
+ let text2 = message.text;
1897
+ if (text2.match(/^M\d{2}A\w{6}/)) {
1549
1898
  ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1550
- text = text.substring(10);
1899
+ text2 = text2.substring(10);
1551
1900
  }
1552
1901
  decodeResult.decoded = true;
1553
- const fields = text.split(",");
1554
- if (text.length === 51) {
1555
- decodeResult.raw.day_of_month = text.substring(0, 2);
1556
- ResultFormatter.departureAirport(decodeResult, text.substring(8, 11));
1557
- ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16));
1558
- ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(30, 45).replace(/^(.)0/, "$1")));
1559
- ResultFormatter.altitude(decodeResult, text.substring(48, 51) * 100);
1560
- decodeResult.remaining.text = [text.substring(2, 4), text.substring(19, 29)].join(" ");
1902
+ const fields = text2.split(",");
1903
+ if (text2.length === 51) {
1904
+ decodeResult.raw.day_of_month = text2.substring(0, 2);
1905
+ ResultFormatter.departureAirport(decodeResult, text2.substring(8, 11));
1906
+ ResultFormatter.arrivalAirport(decodeResult, text2.substring(13, 16));
1907
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text2.substring(30, 45).replace(/^(.)0/, "$1")));
1908
+ ResultFormatter.altitude(decodeResult, text2.substring(48, 51) * 100);
1909
+ decodeResult.remaining.text = [text2.substring(2, 4), text2.substring(19, 29)].join(" ");
1561
1910
  } else if (fields.length === 33) {
1562
1911
  decodeResult.raw.date = fields[3];
1563
1912
  if (fields[1] === "B") {
@@ -1575,10 +1924,10 @@ var Label_4N = class extends DecoderPlugin {
1575
1924
  decodeResult.remaining.text = [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter((f) => f != "").join(",");
1576
1925
  } else {
1577
1926
  decodeResult.decoded = false;
1578
- decodeResult.remaining.text = text;
1927
+ decodeResult.remaining.text = text2;
1579
1928
  }
1580
1929
  if (decodeResult.decoded) {
1581
- if (decodeResult.remaining.text === "")
1930
+ if (!decodeResult.remaining.text)
1582
1931
  decodeResult.decoder.decodeLevel = "full";
1583
1932
  else
1584
1933
  decodeResult.decoder.decodeLevel = "partial";
@@ -1766,14 +2115,14 @@ var Label_83 = class extends DecoderPlugin {
1766
2115
  decodeResult.decoder.name = this.name;
1767
2116
  decodeResult.message = message;
1768
2117
  decodeResult.formatted.description = "Airline Defined";
1769
- let text = message.text;
1770
- if (text.match(/^M\d{2}A\w{6}/)) {
2118
+ let text2 = message.text;
2119
+ if (text2.match(/^M\d{2}A\w{6}/)) {
1771
2120
  ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1772
- text = text.substring(10);
2121
+ text2 = text2.substring(10);
1773
2122
  }
1774
2123
  decodeResult.decoded = true;
1775
- if (text.substring(0, 10) === "4DH3 ETAT2") {
1776
- const fields = text.split(/\s+/);
2124
+ if (text2.substring(0, 10) === "4DH3 ETAT2") {
2125
+ const fields = text2.split(/\s+/);
1777
2126
  if (fields[2].length > 5) {
1778
2127
  decodeResult.raw.day_of_month = fields[2].substring(5);
1779
2128
  }
@@ -1782,14 +2131,17 @@ var Label_83 = class extends DecoderPlugin {
1782
2131
  ResultFormatter.departureAirport(decodeResult, subfields[0]);
1783
2132
  ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
1784
2133
  ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, ""));
1785
- ResultFormatter.eta(decodeResult, fields[6] + "00");
1786
- } else if (text.substring(0, 5) === "001PR") {
1787
- decodeResult.raw.day_of_month = text.substring(5, 7);
1788
- ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(13, 28).replace(/\./g, "")));
1789
- ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33)));
1790
- decodeResult.remaining.text = text.substring(33);
2134
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + "00"));
2135
+ } else if (text2.substring(0, 5) === "001PR") {
2136
+ decodeResult.raw.day_of_month = text2.substring(5, 7);
2137
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text2.substring(13, 28).replace(/\./g, ""));
2138
+ if (position) {
2139
+ ResultFormatter.position(decodeResult, position);
2140
+ }
2141
+ ResultFormatter.altitude(decodeResult, Number(text2.substring(28, 33)));
2142
+ decodeResult.remaining.text = text2.substring(33);
1791
2143
  } else {
1792
- const fields = text.replace(/\s/g, "").split(",");
2144
+ const fields = text2.replace(/\s/g, "").split(",");
1793
2145
  if (fields.length === 9) {
1794
2146
  ResultFormatter.departureAirport(decodeResult, fields[0]);
1795
2147
  ResultFormatter.arrivalAirport(decodeResult, fields[1]);
@@ -1802,7 +2154,7 @@ var Label_83 = class extends DecoderPlugin {
1802
2154
  longitude: Number(fields[4].replace(/\s/g, ""))
1803
2155
  }
1804
2156
  );
1805
- ResultFormatter.altitude(decodeResult, fields[5]);
2157
+ ResultFormatter.altitude(decodeResult, Number(fields[5]));
1806
2158
  ResultFormatter.groundspeed(decodeResult, fields[6]);
1807
2159
  ResultFormatter.heading(decodeResult, fields[7]);
1808
2160
  decodeResult.remaining.text = fields[8];
@@ -1812,7 +2164,7 @@ var Label_83 = class extends DecoderPlugin {
1812
2164
  }
1813
2165
  }
1814
2166
  if (decodeResult.decoded) {
1815
- if (decodeResult.remaining.text === "")
2167
+ if (!decodeResult.remaining.text)
1816
2168
  decodeResult.decoder.decodeLevel = "full";
1817
2169
  else
1818
2170
  decodeResult.decoder.decodeLevel = "partial";
@@ -1843,19 +2195,8 @@ var Label_8E = class extends DecoderPlugin {
1843
2195
  console.log(`Label 8E ETA: groups`);
1844
2196
  console.log(results.groups);
1845
2197
  }
1846
- decodeResult.formatted.items.push({
1847
- type: "eta",
1848
- code: "ETA",
1849
- label: "Estimated Time of Arrival",
1850
- value: DateTimeUtils.UTCToString(results.groups.arrival_eta)
1851
- });
1852
- decodeResult.raw.arrival_icao = results.groups.arrival_icao;
1853
- decodeResult.formatted.items.push({
1854
- type: "destination",
1855
- code: "DST",
1856
- label: "Destination",
1857
- value: decodeResult.raw.arrival_icao
1858
- });
2198
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results.groups.arrival_eta + "00"));
2199
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
1859
2200
  }
1860
2201
  decodeResult.decoded = true;
1861
2202
  decodeResult.decoder.decodeLevel = "full";
@@ -1908,44 +2249,158 @@ var Label_ColonComma = class extends DecoderPlugin {
1908
2249
  }
1909
2250
  };
1910
2251
 
1911
- // lib/plugins/Label_H1_FLR.ts
1912
- var Label_H1_FLR = class extends DecoderPlugin {
1913
- name = "label-h1-flr";
2252
+ // lib/plugins/Label_H1_FLR.ts
2253
+ var Label_H1_FLR = class extends DecoderPlugin {
2254
+ name = "label-h1-flr";
2255
+ qualifiers() {
2256
+ return {
2257
+ labels: ["H1"],
2258
+ preambles: ["FLR", "#CFBFLR"]
2259
+ };
2260
+ }
2261
+ decode(message, options = {}) {
2262
+ let decodeResult = this.defaultResult();
2263
+ decodeResult.decoder.name = this.name;
2264
+ decodeResult.formatted.description = "Fault Log Report";
2265
+ decodeResult.message = message;
2266
+ const parts = message.text.split("/FR");
2267
+ if (parts.length > 1) {
2268
+ decodeResult.remaining.text = "";
2269
+ const fields = parts[0].split("/");
2270
+ for (let i = 1; i < fields.length; i++) {
2271
+ const field = fields[i];
2272
+ if (field.startsWith("PN")) {
2273
+ processUnknown(decodeResult, "/" + field);
2274
+ } else {
2275
+ processUnknown(decodeResult, "/" + field);
2276
+ }
2277
+ }
2278
+ const data = parts[1].substring(0, 20);
2279
+ const msg = parts[1].substring(20);
2280
+ const datetime = data.substring(0, 12);
2281
+ const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
2282
+ processUnknown(decodeResult, data.substring(12));
2283
+ decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
2284
+ decodeResult.raw.fault_message = msg;
2285
+ decodeResult.formatted.items.push({
2286
+ type: "fault",
2287
+ code: "FR",
2288
+ label: "Fault Report",
2289
+ value: decodeResult.raw.fault_message
2290
+ });
2291
+ decodeResult.decoded = true;
2292
+ decodeResult.decoder.decodeLevel = "partial";
2293
+ } else {
2294
+ if (options.debug) {
2295
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
2296
+ }
2297
+ decodeResult.remaining.text = message.text;
2298
+ decodeResult.decoded = false;
2299
+ decodeResult.decoder.decodeLevel = "none";
2300
+ }
2301
+ return decodeResult;
2302
+ }
2303
+ };
2304
+ function processUnknown(decodeResult, value) {
2305
+ decodeResult.remaining.text += value;
2306
+ }
2307
+
2308
+ // lib/plugins/Label_H1_OHMA.ts
2309
+ var zlib = __toESM(require("minizlib"));
2310
+ var Label_H1_OHMA = class extends DecoderPlugin {
2311
+ name = "label-h1-ohma";
2312
+ qualifiers() {
2313
+ return {
2314
+ labels: ["H1"],
2315
+ preambles: ["OHMA", "/RTNBOCR.OHMA", "#T1B/RTNBOCR.OHMA"]
2316
+ };
2317
+ }
2318
+ decode(message, options = {}) {
2319
+ let decodeResult = this.defaultResult();
2320
+ decodeResult.decoder.name = this.name;
2321
+ decodeResult.formatted.description = "OHMA Message";
2322
+ decodeResult.message = message;
2323
+ decodeResult.remaining.text = "";
2324
+ const data = message.text.split("OHMA")[1];
2325
+ try {
2326
+ const compressedBuffer = Buffer.from(data, "base64");
2327
+ const decompress = new zlib.Inflate({ windowBits: 15 });
2328
+ decompress.write(compressedBuffer);
2329
+ decompress.flush(zlib.constants.Z_SYNC_FLUSH);
2330
+ const result = decompress.read();
2331
+ const jsonText = result.toString();
2332
+ let formattedMsg;
2333
+ let jsonMessage;
2334
+ try {
2335
+ jsonMessage = JSON.parse(jsonText).message;
2336
+ } catch {
2337
+ jsonMessage = jsonText;
2338
+ }
2339
+ try {
2340
+ const ohmaMsg = JSON.parse(jsonMessage);
2341
+ formattedMsg = JSON.stringify(ohmaMsg, null, 2);
2342
+ } catch {
2343
+ formattedMsg = jsonMessage;
2344
+ }
2345
+ decodeResult.decoded = true;
2346
+ decodeResult.decoder.decodeLevel = "full";
2347
+ decodeResult.raw.ohma = jsonText;
2348
+ decodeResult.formatted.items.push({
2349
+ type: "ohma",
2350
+ code: "OHMA",
2351
+ label: "OHMA Downlink",
2352
+ value: formattedMsg
2353
+ });
2354
+ } catch (e) {
2355
+ if (options.debug) {
2356
+ console.log(`Decoder: Unknown H1 OHMA message: ${message.text}`, e);
2357
+ }
2358
+ decodeResult.remaining.text += message.text;
2359
+ decodeResult.decoded = false;
2360
+ decodeResult.decoder.decodeLevel = "none";
2361
+ }
2362
+ return decodeResult;
2363
+ }
2364
+ };
2365
+
2366
+ // lib/plugins/Label_H1_WRN.ts
2367
+ var Label_H1_WRN = class extends DecoderPlugin {
2368
+ name = "label-h1-wrn";
1914
2369
  qualifiers() {
1915
2370
  return {
1916
2371
  labels: ["H1"],
1917
- preambles: ["FLR", "#CFBFLR"]
2372
+ preambles: ["WRN", "#CFBWRN"]
1918
2373
  };
1919
2374
  }
1920
2375
  decode(message, options = {}) {
1921
2376
  let decodeResult = this.defaultResult();
1922
2377
  decodeResult.decoder.name = this.name;
1923
- decodeResult.formatted.description = "Fault Log Report";
2378
+ decodeResult.formatted.description = "Warning Message";
1924
2379
  decodeResult.message = message;
1925
- const parts = message.text.split("/FR");
2380
+ const parts = message.text.split("/WN");
1926
2381
  if (parts.length > 1) {
1927
2382
  decodeResult.remaining.text = "";
1928
2383
  const fields = parts[0].split("/");
1929
2384
  for (let i = 1; i < fields.length; i++) {
1930
2385
  const field = fields[i];
1931
2386
  if (field.startsWith("PN")) {
1932
- processUnknown(decodeResult, "/" + field);
2387
+ processUnknown2(decodeResult, "/" + field);
1933
2388
  } else {
1934
- processUnknown(decodeResult, "/" + field);
2389
+ processUnknown2(decodeResult, "/" + field);
1935
2390
  }
1936
2391
  }
1937
2392
  const data = parts[1].substring(0, 20);
1938
2393
  const msg = parts[1].substring(20);
1939
2394
  const datetime = data.substring(0, 12);
1940
2395
  const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
1941
- processUnknown(decodeResult, data.substring(12));
2396
+ processUnknown2(decodeResult, data.substring(12));
1942
2397
  decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
1943
- decodeResult.raw.fault_message = msg;
2398
+ decodeResult.raw.warning_message = msg;
1944
2399
  decodeResult.formatted.items.push({
1945
- type: "fault",
1946
- code: "FR",
1947
- label: "Fault Report",
1948
- value: decodeResult.raw.fault_message
2400
+ type: "warning",
2401
+ code: "WRN",
2402
+ label: "Warning Message",
2403
+ value: decodeResult.raw.warning_message
1949
2404
  });
1950
2405
  decodeResult.decoded = true;
1951
2406
  decodeResult.decoder.decodeLevel = "partial";
@@ -1960,7 +2415,7 @@ var Label_H1_FLR = class extends DecoderPlugin {
1960
2415
  return decodeResult;
1961
2416
  }
1962
2417
  };
1963
- function processUnknown(decodeResult, value) {
2418
+ function processUnknown2(decodeResult, value) {
1964
2419
  decodeResult.remaining.text += value;
1965
2420
  }
1966
2421
 
@@ -2126,11 +2581,10 @@ function addCompanyRoute(decodeResult, value) {
2126
2581
  // lib/utils/h1_helper.ts
2127
2582
  var H1Helper = class {
2128
2583
  static decodeH1Message(decodeResult, message) {
2129
- let allKnownFields = true;
2130
2584
  const checksum = message.slice(-4);
2131
2585
  const data = message.slice(0, message.length - 4);
2132
2586
  const fields = data.split("/");
2133
- allKnownFields = allKnownFields && parseMessageType(decodeResult, fields[0]);
2587
+ parseMessageType(decodeResult, fields[0]);
2134
2588
  for (let i = 1; i < fields.length; ++i) {
2135
2589
  if (fields[i].startsWith("FN")) {
2136
2590
  decodeResult.raw.flight_number = fields[i].substring(2);
@@ -2149,32 +2603,22 @@ var H1Helper = class {
2149
2603
  decodeResult.raw.message_timestamp = time;
2150
2604
  } else if (fields[i].startsWith("PS")) {
2151
2605
  const pos = processPS(decodeResult, fields[i].substring(2).split(","));
2152
- allKnownFields == allKnownFields && pos;
2153
2606
  } else if (fields[i].startsWith("DT")) {
2154
2607
  const data2 = fields[i].substring(2).split(",");
2155
- const dt = processDT(decodeResult, data2);
2156
- allKnownFields = allKnownFields && dt;
2608
+ processDT(decodeResult, data2);
2157
2609
  } else if (fields[i].startsWith("ID")) {
2158
2610
  processIdentification(decodeResult, fields[i].substring(2).split(","));
2159
2611
  } else if (fields[i].startsWith("LR")) {
2160
2612
  const data2 = fields[i].substring(2).split(",");
2161
- const lr = processLR(decodeResult, data2);
2162
- allKnownFields = allKnownFields && lr;
2613
+ processLR(decodeResult, data2);
2163
2614
  } else if (fields[i].startsWith("RI") || fields[i].startsWith("RF") || fields[i].startsWith("RP")) {
2164
- const fp = FlightPlanUtils.processFlightPlan(decodeResult, fields[i].split(":"));
2165
- allKnownFields = allKnownFields && fp;
2615
+ FlightPlanUtils.processFlightPlan(decodeResult, fields[i].split(":"));
2166
2616
  } else if (fields[i].startsWith("PR")) {
2167
- allKnownFields = false;
2168
2617
  decodeResult.remaining.text += "/" + fields[i];
2169
- } else if (fields[i].startsWith("PS")) {
2170
- allKnownFields = false;
2171
- decodeResult.remaining.text += fields[i];
2172
2618
  } else if (fields[i].startsWith("AF")) {
2173
- const af = processAirField(decodeResult, fields[i].substring(2).split(","));
2174
- allKnownFields = allKnownFields && af;
2619
+ processAirField(decodeResult, fields[i].substring(2).split(","));
2175
2620
  } else if (fields[i].startsWith("TD")) {
2176
- const td = processTimeOfDeparture(decodeResult, fields[i].substring(2).split(","));
2177
- allKnownFields = allKnownFields && td;
2621
+ processTimeOfDeparture(decodeResult, fields[i].substring(2).split(","));
2178
2622
  } else if (fields[i].startsWith("FX")) {
2179
2623
  decodeResult.raw.free_text = fields[i].substring(2);
2180
2624
  decodeResult.formatted.items.push({
@@ -2185,23 +2629,21 @@ var H1Helper = class {
2185
2629
  });
2186
2630
  } else {
2187
2631
  decodeResult.remaining.text += "/" + fields[i];
2188
- allKnownFields = false;
2189
2632
  }
2190
2633
  }
2191
2634
  if (decodeResult.formatted.items.length > 0) {
2192
2635
  ResultFormatter.checksum(decodeResult, checksum);
2193
2636
  }
2194
- return allKnownFields;
2637
+ return true;
2195
2638
  }
2196
2639
  };
2197
2640
  function processAirField(decodeResult, data) {
2198
2641
  if (data.length === 2) {
2199
2642
  ResultFormatter.departureAirport(decodeResult, data[0]);
2200
2643
  ResultFormatter.arrivalAirport(decodeResult, data[1]);
2201
- return true;
2644
+ } else {
2645
+ decodeResult.remaining.text += "AF/" + data.join(",");
2202
2646
  }
2203
- decodeResult.remaining.text += "AF/" + data.join(",");
2204
- return false;
2205
2647
  }
2206
2648
  function processTimeOfDeparture(decodeResult, data) {
2207
2649
  if (data.length === 2) {
@@ -2219,10 +2661,9 @@ function processTimeOfDeparture(decodeResult, data) {
2219
2661
  label: "Estimated Departure Time",
2220
2662
  value: `${data[1].substring(0, 2)}:${data[1].substring(2)}`
2221
2663
  });
2222
- return true;
2664
+ } else {
2665
+ decodeResult.remaining.text += "/TD" + data.join(",");
2223
2666
  }
2224
- decodeResult.remaining.text += "/TD" + data.join(",");
2225
- return false;
2226
2667
  }
2227
2668
  function processIdentification(decodeResult, data) {
2228
2669
  ResultFormatter.tail(decodeResult, data[0]);
@@ -2234,7 +2675,6 @@ function processIdentification(decodeResult, data) {
2234
2675
  }
2235
2676
  }
2236
2677
  function processDT(decodeResult, data) {
2237
- let allKnownFields = true;
2238
2678
  if (!decodeResult.raw.arrival_icao) {
2239
2679
  ResultFormatter.arrivalAirport(decodeResult, data[0]);
2240
2680
  } else if (decodeResult.raw.arrival_icao != data[0]) {
@@ -2247,19 +2687,16 @@ function processDT(decodeResult, data) {
2247
2687
  ResultFormatter.currentFuel(decodeResult, Number(data[2]));
2248
2688
  }
2249
2689
  if (data.length > 3) {
2250
- ResultFormatter.eta(decodeResult, data[3]);
2690
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[3]));
2251
2691
  }
2252
2692
  if (data.length > 4) {
2253
2693
  ResultFormatter.remainingFuel(decodeResult, Number(data[4]));
2254
2694
  }
2255
2695
  if (data.length > 5) {
2256
- allKnownFields = false;
2257
2696
  decodeResult.remaining.text += "," + data.slice(5).join(",");
2258
2697
  }
2259
- return allKnownFields;
2260
2698
  }
2261
2699
  function processLR(decodeResult, data) {
2262
- let allKnownFields = true;
2263
2700
  if (data.length === 19) {
2264
2701
  ResultFormatter.unknown(decodeResult, data[1]);
2265
2702
  ResultFormatter.flightNumber(decodeResult, data[2]);
@@ -2267,34 +2704,47 @@ function processLR(decodeResult, data) {
2267
2704
  ResultFormatter.arrivalAirport(decodeResult, data[4]);
2268
2705
  ResultFormatter.arrivalRunway(decodeResult, data[5]);
2269
2706
  ResultFormatter.unknown(decodeResult, data.slice(6, 19).join(","));
2270
- allKnownFields = false;
2271
2707
  } else {
2272
- allKnownFields = false;
2708
+ ResultFormatter.unknown(decodeResult, data.join(","));
2273
2709
  }
2274
- return allKnownFields;
2275
2710
  }
2276
2711
  function parseMessageType(decodeResult, messageType) {
2277
- let decoded = true;
2278
2712
  const parts = messageType.split("#");
2279
2713
  if (parts.length == 1) {
2280
- if (parts[0].startsWith("POS") && parts[0].length !== 3 && !parts[0].startsWith("POS/")) {
2281
- decoded = processPosition2(decodeResult, parts[0].substring(3).split(","));
2714
+ const type = parts[0].substring(0, 3);
2715
+ if (type === "POS" && parts[0].length !== 3) {
2716
+ processPosition2(decodeResult, parts[0].substring(3).split(","));
2282
2717
  }
2283
- return decoded;
2718
+ return processMessageType(decodeResult, type);
2284
2719
  } else if (parts.length == 2) {
2285
2720
  if (parts[0].length > 0) {
2286
2721
  decodeResult.remaining.text += parts[0].substring(0, 3);
2287
2722
  decodeResult.raw.flight_number = parts[0].substring(3);
2288
- decodeResult.remaining.text += "#" + parts[1].substring(0, 3);
2723
+ decodeResult.remaining.text += "#" + (parts[1].length == 5 ? parts[1].substring(0, 2) : parts[1].substring(0, 3));
2289
2724
  }
2290
- if (parts[1].substring(3, 6) === "POS" && parts[1].length !== 6 && parts[1].substring(3, 7) !== "POS/") {
2291
- decoded = processPosition2(decodeResult, parts[1].substring(6).split(","));
2725
+ const type = parts[1].length == 5 ? parts[1].substring(2, 5) : parts[1].substring(3, 6);
2726
+ if (parts[1].substring(3, 6) === "POS" && parts[1].length > 6) {
2727
+ processPosition2(decodeResult, parts[1].substring(6).split(","));
2292
2728
  }
2293
- decodeResult.raw.message_type = messageType;
2294
- return decoded;
2729
+ processMessageType(decodeResult, type);
2730
+ } else {
2731
+ decodeResult.remaining.text += messageType;
2732
+ }
2733
+ }
2734
+ function processMessageType(decodeResult, type) {
2735
+ if (type === "FPN") {
2736
+ decodeResult.formatted.description = "Flight Plan";
2737
+ } else if (type === "FTX") {
2738
+ decodeResult.formatted.description = "Free Text";
2739
+ } else if (type === "INI") {
2740
+ decodeResult.formatted.description = "Flight Plan Initial Report";
2741
+ } else if (type === "POS") {
2742
+ decodeResult.formatted.description = "Position Report";
2743
+ } else if (type === "PRG") {
2744
+ decodeResult.formatted.description = "Progress Report";
2745
+ } else {
2746
+ decodeResult.formatted.description = "Unknown H1 Message";
2295
2747
  }
2296
- decodeResult.remaining.text += messageType;
2297
- return false;
2298
2748
  }
2299
2749
  function processDC(decodeResult, data) {
2300
2750
  decodeResult.raw.message_date = data[0];
@@ -2303,13 +2753,9 @@ function processDC(decodeResult, data) {
2303
2753
  const date = data[0].substring(2, 4) + data[0].substring(0, 2) + data[0].substring(4, 6);
2304
2754
  const time = DateTimeUtils.convertDateTimeToEpoch(data[1], data[0]);
2305
2755
  decodeResult.raw.message_timestamp = time;
2306
- } else {
2307
- return false;
2308
2756
  }
2309
- return true;
2310
2757
  }
2311
2758
  function processPS(decodeResult, data) {
2312
- let allKnownFields = true;
2313
2759
  const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
2314
2760
  if (position) {
2315
2761
  decodeResult.raw.position = position;
@@ -2319,8 +2765,6 @@ function processPS(decodeResult, data) {
2319
2765
  label: "Aircraft Position",
2320
2766
  value: CoordinateUtils.coordinateString(position)
2321
2767
  });
2322
- } else {
2323
- allKnownFields = false;
2324
2768
  }
2325
2769
  if (data.length === 9) {
2326
2770
  processRoute(decodeResult, data[3], data[1], data[5], data[4], void 0);
@@ -2339,11 +2783,8 @@ function processPS(decodeResult, data) {
2339
2783
  ResultFormatter.unknown(decodeResult, data[9]);
2340
2784
  ResultFormatter.unknown(decodeResult, data.slice(11).join(","));
2341
2785
  }
2342
- allKnownFields = false;
2343
- return allKnownFields;
2344
2786
  }
2345
2787
  function processPosition2(decodeResult, data) {
2346
- let allKnownFields = true;
2347
2788
  const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
2348
2789
  if (position) {
2349
2790
  decodeResult.raw.position = position;
@@ -2353,8 +2794,6 @@ function processPosition2(decodeResult, data) {
2353
2794
  label: "Aircraft Position",
2354
2795
  value: CoordinateUtils.coordinateString(position)
2355
2796
  });
2356
- } else {
2357
- allKnownFields = false;
2358
2797
  }
2359
2798
  if (data.length >= 10) {
2360
2799
  ResultFormatter.altitude(decodeResult, Number(data[3]) * 100);
@@ -2369,8 +2808,6 @@ function processPosition2(decodeResult, data) {
2369
2808
  ResultFormatter.unknown(decodeResult, data[12]);
2370
2809
  ResultFormatter.unknown(decodeResult, data[13]);
2371
2810
  }
2372
- allKnownFields = false;
2373
- return allKnownFields;
2374
2811
  }
2375
2812
  function processRoute(decodeResult, last, time, next, eta, then, date) {
2376
2813
  const lastTime = date ? DateTimeUtils.convertDateTimeToEpoch(time, date) : DateTimeUtils.convertHHMMSSToTod(time);
@@ -2393,175 +2830,23 @@ function processRoute(decodeResult, last, time, next, eta, then, date) {
2393
2830
  });
2394
2831
  }
2395
2832
 
2396
- // lib/plugins/Label_H1_FPN.ts
2397
- var Label_H1_FPN = class extends DecoderPlugin {
2398
- name = "label-h1-fpn";
2833
+ // lib/plugins/Label_H1.ts
2834
+ var Label_H1 = class extends DecoderPlugin {
2835
+ name = "label-h1";
2399
2836
  qualifiers() {
2400
2837
  return {
2401
- labels: ["H1"],
2402
- preambles: ["FPN", "#M1BFPN"]
2838
+ labels: ["H1"]
2403
2839
  };
2404
2840
  }
2405
2841
  decode(message, options = {}) {
2406
2842
  let decodeResult = this.defaultResult();
2407
2843
  decodeResult.decoder.name = this.name;
2408
- decodeResult.formatted.description = "Flight Plan";
2409
2844
  decodeResult.message = message;
2410
2845
  decodeResult.remaining.text = "";
2411
2846
  const msg = message.text.replace(/\n|\r/g, "");
2412
- const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, msg);
2413
- decodeResult.decoded = true;
2414
- decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2415
- if (decodeResult.formatted.items.length === 0) {
2416
- if (options.debug) {
2417
- console.log(`Decoder: Unknown H1 message: ${message.text}`);
2418
- }
2419
- decodeResult.remaining.text = message.text;
2420
- decodeResult.decoded = false;
2421
- decodeResult.decoder.decodeLevel = "none";
2422
- }
2423
- return decodeResult;
2424
- }
2425
- };
2426
-
2427
- // lib/plugins/Label_H1_FTX.ts
2428
- var Label_H1_FTX = class extends DecoderPlugin {
2429
- name = "label-h1-ftx";
2430
- qualifiers() {
2431
- return {
2432
- labels: ["H1"],
2433
- preambles: ["FTX", "- #MDFTX"]
2434
- };
2435
- }
2436
- decode(message, options = {}) {
2437
- let decodeResult = this.defaultResult();
2438
- decodeResult.decoder.name = this.name;
2439
- decodeResult.formatted.description = "Free Text";
2440
- decodeResult.message = message;
2441
- decodeResult.remaining.text = "";
2442
- const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2443
- decodeResult.decoded = true;
2444
- decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2445
- if (decodeResult.formatted.items.length === 0) {
2446
- if (options.debug) {
2447
- console.log(`Decoder: Unknown H1 message: ${message.text}`);
2448
- }
2449
- decodeResult.remaining.text = message.text;
2450
- decodeResult.decoded = false;
2451
- decodeResult.decoder.decodeLevel = "none";
2452
- }
2453
- return decodeResult;
2454
- }
2455
- };
2456
-
2457
- // lib/plugins/Label_H1_INI.ts
2458
- var Label_H1_INI = class extends DecoderPlugin {
2459
- // eslint-disable-line camelcase
2460
- name = "label-h1-ini";
2461
- qualifiers() {
2462
- return {
2463
- labels: ["H1"],
2464
- preambles: ["INI", "- #MDINI"]
2465
- };
2466
- }
2467
- decode(message, options = {}) {
2468
- const decodeResult = this.defaultResult();
2469
- decodeResult.decoder.name = this.name;
2470
- decodeResult.formatted.description = "Flight Plan Initial Report";
2471
- decodeResult.message = message;
2472
- decodeResult.remaining.text = "";
2473
- const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2474
- decodeResult.decoded = true;
2475
- decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2476
- if (decodeResult.formatted.items.length === 0) {
2477
- if (options.debug) {
2478
- console.log(`Decoder: Unknown H1 message: ${message.text}`);
2479
- }
2480
- decodeResult.remaining.text = message.text;
2481
- decodeResult.decoded = false;
2482
- decodeResult.decoder.decodeLevel = "none";
2483
- }
2484
- return decodeResult;
2485
- }
2486
- };
2487
-
2488
- // lib/plugins/Label_H1_OHMA.ts
2489
- var zlib = __toESM(require("minizlib"));
2490
- var Label_H1_OHMA = class extends DecoderPlugin {
2491
- name = "label-h1-ohma";
2492
- qualifiers() {
2493
- return {
2494
- labels: ["H1"],
2495
- preambles: ["OHMA", "/RTNBOCR.OHMA", "#T1B/RTNBOCR.OHMA"]
2496
- };
2497
- }
2498
- decode(message, options = {}) {
2499
- let decodeResult = this.defaultResult();
2500
- decodeResult.decoder.name = this.name;
2501
- decodeResult.formatted.description = "OHMA Message";
2502
- decodeResult.message = message;
2503
- decodeResult.remaining.text = "";
2504
- const data = message.text.split("OHMA")[1];
2505
- try {
2506
- const compressedBuffer = Buffer.from(data, "base64");
2507
- const decompress = new zlib.Inflate({ windowBits: 15 });
2508
- decompress.write(compressedBuffer);
2509
- decompress.flush(zlib.constants.Z_SYNC_FLUSH);
2510
- const result = decompress.read();
2511
- const jsonText = result.toString();
2512
- let formattedMsg;
2513
- let jsonMessage;
2514
- try {
2515
- jsonMessage = JSON.parse(jsonText).message;
2516
- } catch {
2517
- jsonMessage = jsonText;
2518
- }
2519
- try {
2520
- const ohmaMsg = JSON.parse(jsonMessage);
2521
- formattedMsg = JSON.stringify(ohmaMsg, null, 2);
2522
- } catch {
2523
- formattedMsg = jsonMessage;
2524
- }
2525
- decodeResult.decoded = true;
2526
- decodeResult.decoder.decodeLevel = "full";
2527
- decodeResult.raw.ohma = jsonText;
2528
- decodeResult.formatted.items.push({
2529
- type: "ohma",
2530
- code: "OHMA",
2531
- label: "OHMA Downlink",
2532
- value: formattedMsg
2533
- });
2534
- } catch (e) {
2535
- if (options.debug) {
2536
- console.log(`Decoder: Unknown H1 OHMA message: ${message.text}`, e);
2537
- }
2538
- decodeResult.remaining.text += message.text;
2539
- decodeResult.decoded = false;
2540
- decodeResult.decoder.decodeLevel = "none";
2541
- }
2542
- return decodeResult;
2543
- }
2544
- };
2545
-
2546
- // lib/plugins/Label_H1_POS.ts
2547
- var Label_H1_POS = class extends DecoderPlugin {
2548
- name = "label-h1-pos";
2549
- qualifiers() {
2550
- return {
2551
- labels: ["H1", "4J"],
2552
- preambles: ["POS", "#M1BPOS", "/.POS"]
2553
- //TODO - support data before #
2554
- };
2555
- }
2556
- decode(message, options = {}) {
2557
- let decodeResult = this.defaultResult();
2558
- decodeResult.decoder.name = this.name;
2559
- decodeResult.formatted.description = "Position Report";
2560
- decodeResult.message = message;
2561
- decodeResult.remaining.text = "";
2562
- const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2563
- decodeResult.decoded = true;
2564
- decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2847
+ const decoded = H1Helper.decodeH1Message(decodeResult, msg);
2848
+ decodeResult.decoded = decoded;
2849
+ decodeResult.decoder.decodeLevel = decodeResult.remaining.text.length === 0 ? "full" : "partial";
2565
2850
  if (decodeResult.formatted.items.length === 0) {
2566
2851
  if (options.debug) {
2567
2852
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
@@ -2574,62 +2859,6 @@ var Label_H1_POS = class extends DecoderPlugin {
2574
2859
  }
2575
2860
  };
2576
2861
 
2577
- // lib/plugins/Label_H1_WRN.ts
2578
- var Label_H1_WRN = class extends DecoderPlugin {
2579
- name = "label-h1-wrn";
2580
- qualifiers() {
2581
- return {
2582
- labels: ["H1"],
2583
- preambles: ["WRN", "#CFBWRN"]
2584
- };
2585
- }
2586
- decode(message, options = {}) {
2587
- let decodeResult = this.defaultResult();
2588
- decodeResult.decoder.name = this.name;
2589
- decodeResult.formatted.description = "Warning Message";
2590
- decodeResult.message = message;
2591
- const parts = message.text.split("/WN");
2592
- if (parts.length > 1) {
2593
- decodeResult.remaining.text = "";
2594
- const fields = parts[0].split("/");
2595
- for (let i = 1; i < fields.length; i++) {
2596
- const field = fields[i];
2597
- if (field.startsWith("PN")) {
2598
- processUnknown2(decodeResult, "/" + field);
2599
- } else {
2600
- processUnknown2(decodeResult, "/" + field);
2601
- }
2602
- }
2603
- const data = parts[1].substring(0, 20);
2604
- const msg = parts[1].substring(20);
2605
- const datetime = data.substring(0, 12);
2606
- const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
2607
- processUnknown2(decodeResult, data.substring(12));
2608
- decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
2609
- decodeResult.raw.warning_message = msg;
2610
- decodeResult.formatted.items.push({
2611
- type: "warning",
2612
- code: "WRN",
2613
- label: "Warning Message",
2614
- value: decodeResult.raw.warning_message
2615
- });
2616
- decodeResult.decoded = true;
2617
- decodeResult.decoder.decodeLevel = "partial";
2618
- } else {
2619
- if (options.debug) {
2620
- console.log(`Decoder: Unknown H1 message: ${message.text}`);
2621
- }
2622
- decodeResult.remaining.text = message.text;
2623
- decodeResult.decoded = false;
2624
- decodeResult.decoder.decodeLevel = "none";
2625
- }
2626
- return decodeResult;
2627
- }
2628
- };
2629
- function processUnknown2(decodeResult, value) {
2630
- decodeResult.remaining.text += value;
2631
- }
2632
-
2633
2862
  // lib/plugins/Label_HX.ts
2634
2863
  var Label_HX = class extends DecoderPlugin {
2635
2864
  name = "label-hx";
@@ -2673,7 +2902,7 @@ var Label_HX = class extends DecoderPlugin {
2673
2902
  });
2674
2903
  }
2675
2904
  if (decodeResult.decoded) {
2676
- if (decodeResult.remaining.text === "")
2905
+ if (!decodeResult.remaining.text)
2677
2906
  decodeResult.decoder.decodeLevel = "full";
2678
2907
  else
2679
2908
  decodeResult.decoder.decodeLevel = "partial";
@@ -2828,7 +3057,7 @@ var Label_QR = class extends DecoderPlugin {
2828
3057
  }
2829
3058
  ];
2830
3059
  decodeResult.decoded = true;
2831
- if (decodeResult.remaining.text === "")
3060
+ if (!decodeResult.remaining.text)
2832
3061
  decodeResult.decoder.decodeLevel = "full";
2833
3062
  else
2834
3063
  decodeResult.decoder.decodeLevel = "partial";
@@ -2873,7 +3102,7 @@ var Label_QP = class extends DecoderPlugin {
2873
3102
  }
2874
3103
  ];
2875
3104
  decodeResult.decoded = true;
2876
- if (decodeResult.remaining.text === "")
3105
+ if (!decodeResult.remaining.text)
2877
3106
  decodeResult.decoder.decodeLevel = "full";
2878
3107
  else
2879
3108
  decodeResult.decoder.decodeLevel = "partial";
@@ -2918,7 +3147,7 @@ var Label_QS = class extends DecoderPlugin {
2918
3147
  }
2919
3148
  ];
2920
3149
  decodeResult.decoded = true;
2921
- if (decodeResult.remaining.text === "")
3150
+ if (!decodeResult.remaining.text)
2922
3151
  decodeResult.decoder.decodeLevel = "full";
2923
3152
  else
2924
3153
  decodeResult.decoder.decodeLevel = "partial";
@@ -2980,7 +3209,7 @@ var Label_QQ = class extends DecoderPlugin {
2980
3209
  });
2981
3210
  }
2982
3211
  decodeResult.decoded = true;
2983
- if (decodeResult.remaining.text === "")
3212
+ if (!decodeResult.remaining.text)
2984
3213
  decodeResult.decoder.decodeLevel = "full";
2985
3214
  else
2986
3215
  decodeResult.decoder.decodeLevel = "partial";
@@ -3800,7 +4029,7 @@ var MessageDecoder = class {
3800
4029
  this.plugins = [];
3801
4030
  this.debug = false;
3802
4031
  this.registerPlugin(new Label_ColonComma(this));
3803
- this.registerPlugin(new Label_5Z(this));
4032
+ this.registerPlugin(new Label_5Z_Slash(this));
3804
4033
  this.registerPlugin(new Label_10_LDR(this));
3805
4034
  this.registerPlugin(new Label_10_POS(this));
3806
4035
  this.registerPlugin(new Label_10_Slash(this));
@@ -3811,21 +4040,24 @@ var MessageDecoder = class {
3811
4040
  this.registerPlugin(new Label_16_N_Space(this));
3812
4041
  this.registerPlugin(new Label_20_POS(this));
3813
4042
  this.registerPlugin(new Label_21_POS(this));
4043
+ this.registerPlugin(new Label_24_Slash(this));
3814
4044
  this.registerPlugin(new Label_30_Slash_EA(this));
3815
4045
  this.registerPlugin(new Label_44_ETA(this));
3816
4046
  this.registerPlugin(new Label_44_IN(this));
3817
4047
  this.registerPlugin(new Label_44_OFF(this));
3818
4048
  this.registerPlugin(new Label_44_ON(this));
3819
4049
  this.registerPlugin(new Label_44_POS(this));
4050
+ this.registerPlugin(new Label_4A(this));
4051
+ this.registerPlugin(new Label_4A_01(this));
4052
+ this.registerPlugin(new Label_4A_DIS(this));
4053
+ this.registerPlugin(new Label_4A_DOOR(this));
4054
+ this.registerPlugin(new Label_4A_Slash_01(this));
3820
4055
  this.registerPlugin(new Label_4N(this));
3821
4056
  this.registerPlugin(new Label_B6_Forwardslash(this));
3822
- this.registerPlugin(new Label_H1_FPN(this));
3823
4057
  this.registerPlugin(new Label_H1_FLR(this));
3824
- this.registerPlugin(new Label_H1_FTX(this));
3825
- this.registerPlugin(new Label_H1_INI(this));
3826
4058
  this.registerPlugin(new Label_H1_OHMA(this));
3827
- this.registerPlugin(new Label_H1_POS(this));
3828
4059
  this.registerPlugin(new Label_H1_WRN(this));
4060
+ this.registerPlugin(new Label_H1(this));
3829
4061
  this.registerPlugin(new Label_HX(this));
3830
4062
  this.registerPlugin(new Label_80(this));
3831
4063
  this.registerPlugin(new Label_83(this));
@@ -3873,29 +4105,30 @@ var MessageDecoder = class {
3873
4105
  console.log("Usable plugins");
3874
4106
  console.log(usablePlugins);
3875
4107
  }
3876
- let result;
3877
- if (usablePlugins.length > 0) {
3878
- const plugin = usablePlugins[0];
4108
+ let result = {
4109
+ decoded: false,
4110
+ error: "No known decoder plugin for this message",
4111
+ decoder: {
4112
+ name: "none",
4113
+ type: "none",
4114
+ decodeLevel: "none"
4115
+ },
4116
+ message,
4117
+ remaining: {
4118
+ text: message.text
4119
+ },
4120
+ raw: {},
4121
+ formatted: {
4122
+ description: "Not Decoded",
4123
+ items: []
4124
+ }
4125
+ };
4126
+ for (let i = 0; i < usablePlugins.length; i++) {
4127
+ const plugin = usablePlugins[i];
3879
4128
  result = plugin.decode(message);
3880
- } else {
3881
- result = {
3882
- decoded: false,
3883
- error: "No known decoder plugin for this message",
3884
- decoder: {
3885
- name: "none",
3886
- type: "none",
3887
- decodeLevel: "none"
3888
- },
3889
- message,
3890
- remaining: {
3891
- text: message.text
3892
- },
3893
- raw: {},
3894
- formatted: {
3895
- description: "Not Decoded",
3896
- items: []
3897
- }
3898
- };
4129
+ if (result.decoded) {
4130
+ break;
4131
+ }
3899
4132
  }
3900
4133
  if (options.debug) {
3901
4134
  console.log("Result");