@airframes/acars-decoder 1.6.9 → 1.6.11

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
@@ -97,6 +97,271 @@ var DecoderPlugin = class {
97
97
  }
98
98
  };
99
99
 
100
+ // lib/utils/coordinate_utils.ts
101
+ var CoordinateUtils = class {
102
+ /**
103
+ * Decode a string of coordinates into an object with latitude and longitude in millidegrees
104
+ * @param stringCoords - The string of coordinates to decode
105
+ *
106
+ * @returns An object with latitude and longitude properties
107
+ */
108
+ static decodeStringCoordinates(stringCoords) {
109
+ var results = {};
110
+ const firstChar = stringCoords.substring(0, 1);
111
+ let middleChar = stringCoords.substring(6, 7);
112
+ let longitudeChars = stringCoords.substring(7, 13);
113
+ if (middleChar == " ") {
114
+ middleChar = stringCoords.substring(7, 8);
115
+ longitudeChars = stringCoords.substring(8, 14);
116
+ }
117
+ if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
118
+ results.latitude = Number(stringCoords.substring(1, 6)) / 1e3 * (firstChar === "S" ? -1 : 1);
119
+ results.longitude = Number(longitudeChars) / 1e3 * (middleChar === "W" ? -1 : 1);
120
+ } else {
121
+ return;
122
+ }
123
+ return results;
124
+ }
125
+ /**
126
+ * Decode a string of coordinates into an object with latitude and longitude in degrees and decimal minutes
127
+ * @param stringCoords - The string of coordinates to decode
128
+ *
129
+ * @returns An object with latitude and longitude properties
130
+ */
131
+ static decodeStringCoordinatesDecimalMinutes(stringCoords) {
132
+ var results = {};
133
+ const firstChar = stringCoords.substring(0, 1);
134
+ let middleChar = stringCoords.substring(6, 7);
135
+ let longitudeChars = stringCoords.substring(7, 13);
136
+ if (middleChar == " ") {
137
+ middleChar = stringCoords.substring(7, 8);
138
+ longitudeChars = stringCoords.substring(8, 14);
139
+ }
140
+ const latDeg = Math.trunc(Number(stringCoords.substring(1, 6)) / 1e3);
141
+ const latMin = Number(stringCoords.substring(1, 6)) % 1e3 / 10;
142
+ const lonDeg = Math.trunc(Number(longitudeChars) / 1e3);
143
+ const lonMin = Number(longitudeChars) % 1e3 / 10;
144
+ if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
145
+ results.latitude = (latDeg + latMin / 60) * (firstChar === "S" ? -1 : 1);
146
+ results.longitude = (lonDeg + lonMin / 60) * (middleChar === "W" ? -1 : 1);
147
+ } else {
148
+ return;
149
+ }
150
+ return results;
151
+ }
152
+ static coordinateString(coords) {
153
+ const latDir = coords.latitude > 0 ? "N" : "S";
154
+ const lonDir = coords.longitude > 0 ? "E" : "W";
155
+ return `${Math.abs(coords.latitude).toFixed(3)} ${latDir}, ${Math.abs(coords.longitude).toFixed(3)} ${lonDir}`;
156
+ }
157
+ static getDirection(coord) {
158
+ if (coord.startsWith("N") || coord.startsWith("E")) {
159
+ return 1;
160
+ } else if (coord.startsWith("S") || coord.startsWith("W")) {
161
+ return -1;
162
+ }
163
+ return NaN;
164
+ }
165
+ static dmsToDecimalDegrees(degrees, minutes, seconds) {
166
+ return degrees + minutes / 60 + seconds / 3600;
167
+ }
168
+ };
169
+
170
+ // lib/utils/result_formatter.ts
171
+ var ResultFormatter = class {
172
+ static position(decodeResult, value) {
173
+ decodeResult.raw.position = value;
174
+ decodeResult.formatted.items.push({
175
+ type: "aircraft_position",
176
+ code: "POS",
177
+ label: "Aircraft Position",
178
+ value: CoordinateUtils.coordinateString(value)
179
+ });
180
+ }
181
+ static altitude(decodeResult, value) {
182
+ decodeResult.raw.altitude = value;
183
+ decodeResult.formatted.items.push({
184
+ type: "altitude",
185
+ code: "ALT",
186
+ label: "Altitude",
187
+ value: `${decodeResult.raw.altitude} feet`
188
+ });
189
+ }
190
+ static flightNumber(decodeResult, value) {
191
+ decodeResult.raw.flight_number = value;
192
+ }
193
+ static departureAirport(decodeResult, value) {
194
+ decodeResult.raw.departure_icao = value;
195
+ decodeResult.formatted.items.push({
196
+ type: "origin",
197
+ code: "ORG",
198
+ label: "Origin",
199
+ value: decodeResult.raw.departure_icao
200
+ });
201
+ }
202
+ static departureRunway(decodeResult, value) {
203
+ decodeResult.raw.departure_runway = value;
204
+ decodeResult.formatted.items.push({
205
+ type: "runway",
206
+ code: "DEPRWY",
207
+ label: "Departure Runway",
208
+ value: decodeResult.raw.departure_runway
209
+ });
210
+ }
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
+ });
219
+ }
220
+ static alternateAirport(decodeResult, value) {
221
+ decodeResult.raw.alternate_icao = value;
222
+ decodeResult.formatted.items.push({
223
+ type: "destination",
224
+ code: "ALT_DST",
225
+ label: "Alternate Destination",
226
+ value: decodeResult.raw.alternate_icao
227
+ });
228
+ }
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
+ });
238
+ }
239
+ static arrivalRunway(decodeResult, value) {
240
+ decodeResult.raw.arrival_runway = value;
241
+ decodeResult.formatted.items.push({
242
+ type: "runway",
243
+ code: "ARWY",
244
+ label: "Arrival Runway",
245
+ value: decodeResult.raw.arrival_runway
246
+ });
247
+ }
248
+ static alternateRunway(decodeResult, value) {
249
+ decodeResult.raw.alternate_runway = value;
250
+ decodeResult.formatted.items.push({
251
+ type: "runway",
252
+ code: "ALT_ARWY",
253
+ label: "Alternate Runway",
254
+ value: decodeResult.raw.alternate_runway
255
+ });
256
+ }
257
+ static currentFuel(decodeResult, value) {
258
+ decodeResult.raw.fuel_on_board = value;
259
+ decodeResult.formatted.items.push({
260
+ type: "fuel_on_board",
261
+ code: "FOB",
262
+ label: "Fuel On Board",
263
+ value: decodeResult.raw.fuel_on_board.toString()
264
+ });
265
+ }
266
+ static remainingFuel(decodeResult, value) {
267
+ decodeResult.raw.fuel_remaining = value;
268
+ decodeResult.formatted.items.push({
269
+ type: "fuel_remaining",
270
+ code: "FUEL",
271
+ label: "Fuel Remaining",
272
+ value: decodeResult.raw.fuel_remaining.toString()
273
+ });
274
+ }
275
+ static checksum(decodeResult, value) {
276
+ decodeResult.raw.checksum = Number("0x" + value);
277
+ decodeResult.formatted.items.push({
278
+ type: "message_checksum",
279
+ code: "CHECKSUM",
280
+ label: "Message Checksum",
281
+ value: "0x" + ("0000" + decodeResult.raw.checksum.toString(16)).slice(-4)
282
+ });
283
+ }
284
+ static groundspeed(decodeResult, value) {
285
+ decodeResult.raw.groundspeed = value;
286
+ decodeResult.formatted.items.push({
287
+ type: "aircraft_groundspeed",
288
+ code: "GSPD",
289
+ label: "Aircraft Groundspeed",
290
+ value: `${decodeResult.raw.groundspeed} knots`
291
+ });
292
+ }
293
+ static temperature(decodeResult, value) {
294
+ decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
295
+ decodeResult.formatted.items.push({
296
+ type: "outside_air_temperature",
297
+ code: "OATEMP",
298
+ label: "Outside Air Temperature (C)",
299
+ value: `${decodeResult.raw.outside_air_temperature}`
300
+ });
301
+ }
302
+ static heading(decodeResult, value) {
303
+ decodeResult.raw.heading = value;
304
+ decodeResult.formatted.items.push({
305
+ type: "heading",
306
+ code: "HDG",
307
+ label: "Heading",
308
+ value: `${decodeResult.raw.heading}`
309
+ });
310
+ }
311
+ static tail(decodeResult, value) {
312
+ decodeResult.raw.tail = value;
313
+ decodeResult.formatted.items.push({
314
+ type: "tail",
315
+ code: "TAIL",
316
+ label: "Tail",
317
+ value: decodeResult.raw.tail
318
+ });
319
+ }
320
+ static out(decodeResult, time) {
321
+ decodeResult.raw.out_time = time;
322
+ const date = new Date(time * 1e3);
323
+ decodeResult.formatted.items.push({
324
+ type: "time_of_day",
325
+ code: "OUT",
326
+ label: "Out of Gate Time",
327
+ value: date.toISOString().slice(11, 19)
328
+ });
329
+ }
330
+ static off(decodeResult, time) {
331
+ decodeResult.raw.off_time = time;
332
+ const date = new Date(time * 1e3);
333
+ decodeResult.formatted.items.push({
334
+ type: "time_of_day",
335
+ code: "OFF",
336
+ label: "Takeoff Time",
337
+ value: date.toISOString().slice(11, 19)
338
+ });
339
+ }
340
+ static on(decodeResult, time) {
341
+ decodeResult.raw.on_time = time;
342
+ const date = new Date(time * 1e3);
343
+ decodeResult.formatted.items.push({
344
+ type: "time_of_day",
345
+ code: "ON",
346
+ label: "Landing Time",
347
+ value: date.toISOString().slice(11, 19)
348
+ });
349
+ }
350
+ static in(decodeResult, time) {
351
+ decodeResult.raw.in_time = time;
352
+ const date = new Date(time * 1e3);
353
+ decodeResult.formatted.items.push({
354
+ type: "time_of_day",
355
+ code: "IN",
356
+ label: "In Gate Time",
357
+ value: date.toISOString().slice(11, 19)
358
+ });
359
+ }
360
+ static unknown(decodeResult, value) {
361
+ decodeResult.remaining.text += "," + value;
362
+ }
363
+ };
364
+
100
365
  // lib/plugins/Label_5Z.ts
101
366
  var Label_5Z = class extends DecoderPlugin {
102
367
  name = "label-5z";
@@ -156,34 +421,9 @@ var Label_5Z = class extends DecoderPlugin {
156
421
  const rdcRegex = /^(?<from>\w\w\w)(?<to>\w\w\w) (?<unknown1>\d\d) R(?<runway>.+) G(?<unknown2>.+)$/;
157
422
  results = remainder.match(rdcRegex);
158
423
  if (results) {
159
- decodeResult.raw.origin = results.groups.from;
160
- decodeResult.formatted.items.push({
161
- type: "origin",
162
- label: "Origin",
163
- value: `${results.groups.from}`
164
- });
165
- decodeResult.raw.destination = results.groups.to;
166
- decodeResult.formatted.items.push({
167
- type: "destination",
168
- label: "Destination",
169
- value: `${results.groups.to}`
170
- });
171
- decodeResult.formatted.items.push({
172
- type: "unknown1",
173
- label: "Unknown Field 1",
174
- value: `${results.groups.unknown1}`
175
- });
176
- decodeResult.raw.runway = results.groups.runway;
177
- decodeResult.formatted.items.push({
178
- type: "runway",
179
- label: "Runway",
180
- value: `${results.groups.runway}`
181
- });
182
- decodeResult.formatted.items.push({
183
- type: "unknown2",
184
- label: "Unknown Field 2",
185
- value: `${results.groups.unknown2}`
186
- });
424
+ ResultFormatter.departureAirport(decodeResult, results.groups.from);
425
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.to);
426
+ ResultFormatter.arrivalRunway(decodeResult, results.groups.runway);
187
427
  } else {
188
428
  if (options.debug) {
189
429
  console.log(`Decoder: Unkown 5Z RDC format: ${remainder}`);
@@ -206,62 +446,278 @@ var Label_5Z = class extends DecoderPlugin {
206
446
  }
207
447
  };
208
448
 
209
- // lib/utils/coordinate_utils.ts
210
- var CoordinateUtils = class {
449
+ // lib/plugins/Label_10_LDR.ts
450
+ var Label_10_LDR = class extends DecoderPlugin {
451
+ // eslint-disable-line camelcase
452
+ name = "label-10-ldr";
453
+ qualifiers() {
454
+ return {
455
+ labels: ["10"],
456
+ preambles: ["LDR"]
457
+ };
458
+ }
459
+ decode(message, options = {}) {
460
+ const decodeResult = this.defaultResult();
461
+ decodeResult.decoder.name = this.name;
462
+ decodeResult.formatted.description = "Position Report";
463
+ decodeResult.message = message;
464
+ const parts = message.text.split(",");
465
+ if (parts.length < 17) {
466
+ if (options.debug) {
467
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
468
+ }
469
+ decodeResult.remaining.text = message.text;
470
+ decodeResult.decoded = false;
471
+ decodeResult.decoder.decodeLevel = "none";
472
+ return decodeResult;
473
+ }
474
+ const lat = parts[5];
475
+ const lon = parts[6];
476
+ const position = {
477
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1).trim()),
478
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim())
479
+ };
480
+ ResultFormatter.position(decodeResult, position);
481
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
482
+ ResultFormatter.departureAirport(decodeResult, parts[9]);
483
+ ResultFormatter.arrivalAirport(decodeResult, parts[10]);
484
+ ResultFormatter.alternateAirport(decodeResult, parts[11]);
485
+ ResultFormatter.arrivalRunway(decodeResult, parts[12].split("/")[0]);
486
+ const altRwy = [parts[13].split("/")[0], parts[14].split("/")[0]].filter((r) => r != "").join(",");
487
+ if (altRwy != "") {
488
+ ResultFormatter.alternateRunway(decodeResult, altRwy);
489
+ }
490
+ decodeResult.remaining.text = [...parts.slice(0, 5), ...parts.slice(15)].join(",");
491
+ decodeResult.decoded = true;
492
+ decodeResult.decoder.decodeLevel = "partial";
493
+ return decodeResult;
494
+ }
495
+ };
496
+
497
+ // lib/plugins/Label_10_POS.ts
498
+ var Label_10_POS = class extends DecoderPlugin {
499
+ // eslint-disable-line camelcase
500
+ name = "label-10-pos";
501
+ qualifiers() {
502
+ return {
503
+ labels: ["10"],
504
+ preambles: ["POS"]
505
+ };
506
+ }
507
+ decode(message, options = {}) {
508
+ const decodeResult = this.defaultResult();
509
+ decodeResult.decoder.name = this.name;
510
+ decodeResult.formatted.description = "Position Report";
511
+ decodeResult.message = message;
512
+ const parts = message.text.split(",");
513
+ if (parts.length !== 12) {
514
+ if (options.debug) {
515
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
516
+ }
517
+ decodeResult.remaining.text = message.text;
518
+ decodeResult.decoded = false;
519
+ decodeResult.decoder.decodeLevel = "none";
520
+ return decodeResult;
521
+ }
522
+ const lat = parts[1].trim();
523
+ const lon = parts[2].trim();
524
+ const position = {
525
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1).trim()) / 100,
526
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim()) / 100
527
+ };
528
+ ResultFormatter.position(decodeResult, position);
529
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
530
+ decodeResult.remaining.text = [parts[0], ...parts.slice(3, 7), ...parts.slice(8)].join(",");
531
+ decodeResult.decoded = true;
532
+ decodeResult.decoder.decodeLevel = "partial";
533
+ return decodeResult;
534
+ }
535
+ };
536
+
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
+ }
211
561
  /**
212
- * Decode a string of coordinates into an object with latitude and longitude in millidegrees
213
- * @param stringCoords - The string of coordinates to decode
214
562
  *
215
- * @returns An object with latitude and longitude properties
563
+ * @param time HHMMSS
564
+ * @returns seconds since midnight
216
565
  */
217
- static decodeStringCoordinates(stringCoords) {
218
- var results = {};
219
- const firstChar = stringCoords.substring(0, 1);
220
- let middleChar = stringCoords.substring(6, 7);
221
- let longitudeChars = stringCoords.substring(7, 13);
222
- if (middleChar == " ") {
223
- middleChar = stringCoords.substring(7, 8);
224
- longitudeChars = stringCoords.substring(8, 14);
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)}`;
225
582
  }
226
- if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
227
- results.latitude = Number(stringCoords.substring(1, 6)) / 1e3 * (firstChar === "S" ? -1 : 1);
228
- results.longitude = Number(longitudeChars) / 1e3 * (middleChar === "W" ? -1 : 1);
229
- } else {
230
- return;
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);
231
655
  }
232
- return results;
656
+ return str;
233
657
  }
234
- /**
235
- * Decode a string of coordinates into an object with latitude and longitude in degrees and decimal minutes
236
- * @param stringCoords - The string of coordinates to decode
237
- *
238
- * @returns An object with latitude and longitude properties
239
- */
240
- static decodeStringCoordinatesDecimalMinutes(stringCoords) {
241
- var results = {};
242
- const firstChar = stringCoords.substring(0, 1);
243
- let middleChar = stringCoords.substring(6, 7);
244
- let longitudeChars = stringCoords.substring(7, 13);
245
- if (middleChar == " ") {
246
- middleChar = stringCoords.substring(7, 8);
247
- longitudeChars = stringCoords.substring(8, 14);
658
+ };
659
+
660
+ // lib/plugins/Label_10_Slash.ts
661
+ var Label_10_Slash = class extends DecoderPlugin {
662
+ // eslint-disable-line camelcase
663
+ name = "label-10-slash";
664
+ qualifiers() {
665
+ return {
666
+ labels: ["10"],
667
+ preambles: ["/"]
668
+ };
669
+ }
670
+ decode(message, options = {}) {
671
+ const decodeResult = this.defaultResult();
672
+ decodeResult.decoder.name = this.name;
673
+ decodeResult.formatted.description = "Position Report";
674
+ decodeResult.message = message;
675
+ const parts = message.text.split("/");
676
+ if (parts.length < 17) {
677
+ if (options.debug) {
678
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
679
+ }
680
+ decodeResult.remaining.text = message.text;
681
+ decodeResult.decoded = false;
682
+ decodeResult.decoder.decodeLevel = "none";
683
+ return decodeResult;
248
684
  }
249
- const latDeg = Math.trunc(Number(stringCoords.substring(1, 6)) / 1e3);
250
- const latMin = Number(stringCoords.substring(1, 6)) % 1e3 / 10;
251
- const lonDeg = Math.trunc(Number(longitudeChars) / 1e3);
252
- const lonMin = Number(longitudeChars) % 1e3 / 10;
253
- if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
254
- results.latitude = (latDeg + latMin / 60) * (firstChar === "S" ? -1 : 1);
255
- results.longitude = (lonDeg + lonMin / 60) * (middleChar === "W" ? -1 : 1);
256
- } else {
257
- return;
685
+ const lat = parts[1];
686
+ const lon = parts[2];
687
+ const position = {
688
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1)),
689
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1))
690
+ };
691
+ ResultFormatter.position(decodeResult, position);
692
+ ResultFormatter.heading(decodeResult, Number(parts[5]));
693
+ ResultFormatter.altitude(decodeResult, 100 * Number(parts[6]));
694
+ ResultFormatter.arrivalAirport(decodeResult, parts[7]);
695
+ ResultFormatter.eta(decodeResult, parts[8] + "00");
696
+ const waypoints = [{
697
+ name: parts[11]
698
+ }, {
699
+ name: parts[12],
700
+ time: DateTimeUtils.convertHHMMSSToTod(parts[13] + "00"),
701
+ timeFormat: "tod"
702
+ }, {
703
+ name: parts[14],
704
+ time: DateTimeUtils.convertHHMMSSToTod(parts[15] + "00"),
705
+ timeFormat: "tod"
706
+ }];
707
+ decodeResult.raw.route = { waypoints };
708
+ decodeResult.formatted.items.push({
709
+ type: "aircraft_route",
710
+ code: "ROUTE",
711
+ label: "Aircraft Route",
712
+ value: RouteUtils.routeToString(decodeResult.raw.route)
713
+ });
714
+ if (parts[16]) {
715
+ ResultFormatter.departureAirport(decodeResult, parts[16]);
258
716
  }
259
- return results;
260
- }
261
- static coordinateString(coords) {
262
- const latDir = coords.latitude > 0 ? "N" : "S";
263
- const lonDir = coords.longitude > 0 ? "E" : "W";
264
- return `${Math.abs(coords.latitude).toFixed(3)} ${latDir}, ${Math.abs(coords.longitude).toFixed(3)} ${lonDir}`;
717
+ decodeResult.remaining.text = [parts[3], parts[4], ...parts.slice(9, 11), ...parts.slice(17)].join("/");
718
+ decodeResult.decoded = true;
719
+ decodeResult.decoder.decodeLevel = "partial";
720
+ return decodeResult;
265
721
  }
266
722
  };
267
723
 
@@ -290,19 +746,14 @@ var Label_12_N_Space = class extends DecoderPlugin {
290
746
  latitude: Number(results.groups.lat_coord) * (results.groups.lat == "N" ? 1 : -1),
291
747
  longitude: Number(results.groups.long_coord) * (results.groups.long == "E" ? 1 : -1)
292
748
  };
293
- decodeResult.raw.flight_level = results.groups.alt == "GRD" || results.groups.alt == "***" ? "0" : Number(results.groups.alt);
749
+ const altitude = results.groups.alt == "GRD" || results.groups.alt == "***" ? 0 : Number(results.groups.alt);
294
750
  decodeResult.formatted.items.push({
295
751
  type: "aircraft_position",
296
752
  code: "POS",
297
753
  label: "Aircraft Position",
298
754
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
299
755
  });
300
- decodeResult.formatted.items.push({
301
- type: "flight_level",
302
- code: "FL",
303
- label: "Flight Level",
304
- value: decodeResult.raw.flight_level
305
- });
756
+ ResultFormatter.altitude(decodeResult, altitude);
306
757
  decodeResult.remaining.text = `,${results.groups.unkwn1} ,${results.groups.unkwn2}, ${results.groups.unkwn3}`;
307
758
  decodeResult.decoded = true;
308
759
  decodeResult.decoder.decodeLevel = "partial";
@@ -318,6 +769,97 @@ var Label_12_N_Space = class extends DecoderPlugin {
318
769
  }
319
770
  };
320
771
 
772
+ // lib/plugins/Label_13Through18_Slash.ts
773
+ var Label_13Through18_Slash = class extends DecoderPlugin {
774
+ // eslint-disable-line camelcase
775
+ name = "label-13-18-slash";
776
+ qualifiers() {
777
+ return {
778
+ labels: ["13", "14", "15", "16", "17", "18"],
779
+ preambles: ["/"]
780
+ };
781
+ }
782
+ decode(message, options = {}) {
783
+ const decodeResult = this.defaultResult();
784
+ decodeResult.decoder.name = this.name;
785
+ decodeResult.message = message;
786
+ const lines = message.text.split("\r\n");
787
+ const parts = lines[0].split("/");
788
+ const labelNumber = Number(parts[1].substring(0, 2));
789
+ decodeResult.formatted.description = getMsgType(labelNumber);
790
+ if (labelNumber !== 18 && parts.length !== 4 || labelNumber === 18 && parts.length !== 7) {
791
+ if (options?.debug) {
792
+ console.log(`Decoder: Unknown OOOI message: ${message.text}`);
793
+ }
794
+ decodeResult.remaining.text = message.text;
795
+ decodeResult.decoded = false;
796
+ decodeResult.decoder.decodeLevel = "none";
797
+ return decodeResult;
798
+ }
799
+ decodeResult.remaining.text = "";
800
+ const data = parts[2].split(" ");
801
+ ResultFormatter.departureAirport(decodeResult, data[1]);
802
+ ResultFormatter.arrivalAirport(decodeResult, data[2]);
803
+ decodeResult.raw.day_of_month = Number(data[3]);
804
+ const time = DateTimeUtils.convertHHMMSSToTod(data[4]);
805
+ if (labelNumber === 13) {
806
+ ResultFormatter.out(decodeResult, time);
807
+ } else if (labelNumber === 14) {
808
+ ResultFormatter.off(decodeResult, time);
809
+ } else if (labelNumber === 15) {
810
+ ResultFormatter.on(decodeResult, time);
811
+ } else if (labelNumber === 16) {
812
+ ResultFormatter.in(decodeResult, time);
813
+ }
814
+ if (parts.length === 7) {
815
+ decodeResult.remaining.text += parts.slice(4).join("/");
816
+ }
817
+ for (let i = 1; i < lines.length; i++) {
818
+ if (lines[i].startsWith("/LOC")) {
819
+ const location = lines[i].substring(5).split(",");
820
+ let position;
821
+ if (location[0].startsWith("+") || location[0].startsWith("-")) {
822
+ position = { latitude: Number(location[0]), longitude: Number(location[1]) };
823
+ } else {
824
+ position = {
825
+ latitude: CoordinateUtils.getDirection(location[0][0]) * CoordinateUtils.dmsToDecimalDegrees(Number(location[0].substring(1, 3)), Number(location[0].substring(3, 5)), Number(location[0].substring(5, 7))),
826
+ longitude: CoordinateUtils.getDirection(location[1][0]) * CoordinateUtils.dmsToDecimalDegrees(Number(location[1].substring(1, 4)), Number(location[1].substring(4, 6)), Number(location[1].substring(6, 8)))
827
+ };
828
+ }
829
+ if (!isNaN(position.latitude) && !isNaN(position.longitude)) {
830
+ ResultFormatter.position(decodeResult, position);
831
+ }
832
+ } else {
833
+ decodeResult.remaining.text += "\r\n" + lines[i];
834
+ }
835
+ }
836
+ decodeResult.decoded = true;
837
+ decodeResult.decoder.decodeLevel = decodeResult.remaining.text ? "partial" : "full";
838
+ return decodeResult;
839
+ }
840
+ };
841
+ function getMsgType(labelNumber) {
842
+ if (labelNumber === 13) {
843
+ return "Out of Gate Report";
844
+ }
845
+ if (labelNumber === 14) {
846
+ return "Takeoff Report";
847
+ }
848
+ if (labelNumber === 15) {
849
+ return "On Ground Report";
850
+ }
851
+ if (labelNumber === 16) {
852
+ return "In Gate Report";
853
+ }
854
+ if (labelNumber === 17) {
855
+ return "Post Report";
856
+ }
857
+ if (labelNumber === 18) {
858
+ return "Post Times Report";
859
+ }
860
+ return "Unknown";
861
+ }
862
+
321
863
  // lib/plugins/Label_15.ts
322
864
  var Label_15 = class extends DecoderPlugin {
323
865
  name = "label-5z";
@@ -364,39 +906,35 @@ var Label_15_FST = class extends DecoderPlugin {
364
906
  decodeResult.decoder.name = this.name;
365
907
  decodeResult.formatted.description = "Position Report";
366
908
  decodeResult.message = message;
367
- decodeResult.raw.departure_icao = message.text.substring(5, 9);
368
- decodeResult.raw.arrival_icao = message.text.substring(9, 13);
369
- const stringCoords = message.text.substring(13);
909
+ const parts = message.text.split(" ");
910
+ const header = parts[0];
911
+ decodeResult.raw.departure_icao = header.substring(5, 9);
912
+ decodeResult.raw.arrival_icao = header.substring(9, 13);
913
+ const stringCoords = header.substring(13);
370
914
  const firstChar = stringCoords.substring(0, 1);
371
915
  const middleChar = stringCoords.substring(7, 8);
372
916
  decodeResult.raw.position = {};
373
917
  if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
374
918
  decodeResult.raw.position.latitude = Number(stringCoords.substring(1, 7)) / 1e4 * (firstChar === "S" ? -1 : 1);
375
- decodeResult.raw.position.longitude = Number(stringCoords.substring(8, 26)) / 1e5 * (middleChar === "W" ? -1 : 1);
919
+ decodeResult.raw.position.longitude = Number(stringCoords.substring(8, 15)) / 1e4 * (middleChar === "W" ? -1 : 1);
920
+ decodeResult.raw.altitude = Number(stringCoords.substring(15)) * 100;
376
921
  } else {
377
922
  decodeResult.decoded = false;
378
923
  decodeResult.decoder.decodeLevel = "none";
379
924
  return decodeResult;
380
925
  }
381
926
  decodeResult.formatted.items.push({
382
- type: "position",
383
- label: "Position",
927
+ type: "aircraft_position",
928
+ code: "POS",
929
+ label: "Aircraft Position",
384
930
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
385
931
  });
386
- decodeResult.formatted.items.push({
387
- type: "origin",
388
- code: "ORG",
389
- label: "Origin",
390
- value: decodeResult.raw.departure_icao
391
- });
392
- decodeResult.formatted.items.push({
393
- type: "destination",
394
- code: "DST",
395
- label: "Destination",
396
- value: decodeResult.raw.arrival_icao
397
- });
932
+ ResultFormatter.altitude(decodeResult, decodeResult.raw.altitude);
933
+ ResultFormatter.departureAirport(decodeResult, decodeResult.raw.departure_icao);
934
+ ResultFormatter.arrivalAirport(decodeResult, decodeResult.raw.arrival_icao);
935
+ decodeResult.remaining.text = parts.slice(1).join(" ");
398
936
  decodeResult.decoded = true;
399
- decodeResult.decoder.decodeLevel = "full";
937
+ decodeResult.decoder.decodeLevel = "partial";
400
938
  return decodeResult;
401
939
  }
402
940
  };
@@ -427,19 +965,14 @@ var Label_16_N_Space = class extends DecoderPlugin {
427
965
  latitude: Number(results.groups.lat_coord) * (results.groups.lat == "N" ? 1 : -1),
428
966
  longitude: Number(results.groups.long_coord) * (results.groups.long == "E" ? 1 : -1)
429
967
  };
430
- decodeResult.raw.flight_level = results.groups.alt == "GRD" || results.groups.alt == "***" ? "0" : Number(results.groups.alt);
968
+ const altitude = results.groups.alt == "GRD" || results.groups.alt == "***" ? 0 : Number(results.groups.alt);
431
969
  decodeResult.formatted.items.push({
432
970
  type: "aircraft_position",
433
971
  code: "POS",
434
972
  label: "Aircraft Position",
435
973
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
436
974
  });
437
- decodeResult.formatted.items.push({
438
- type: "flight_level",
439
- code: "FL",
440
- label: "Flight Level",
441
- value: decodeResult.raw.flight_level
442
- });
975
+ ResultFormatter.altitude(decodeResult, altitude);
443
976
  decodeResult.remaining.text = `,${results.groups.unkwn1} ,${results.groups.unkwn2}`;
444
977
  decodeResult.decoded = true;
445
978
  decodeResult.decoder.decodeLevel = "partial";
@@ -472,58 +1005,6 @@ var Label_16_N_Space = class extends DecoderPlugin {
472
1005
  }
473
1006
  };
474
1007
 
475
- // lib/DateTimeUtils.ts
476
- var DateTimeUtils = class {
477
- // Expects a four digit UTC time string (HHMM)
478
- static UTCToString(UTCString) {
479
- let utcDate = /* @__PURE__ */ new Date();
480
- utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
481
- return utcDate.toTimeString();
482
- }
483
- // Expects a six digit date string and a four digit UTC time string
484
- // (DDMMYY) (HHMM)
485
- static UTCDateTimeToString(dateString, timeString) {
486
- let utcDate = /* @__PURE__ */ new Date();
487
- utcDate.setUTCDate(+dateString.substr(0, 2));
488
- utcDate.setUTCMonth(+dateString.substr(2, 2));
489
- if (dateString.length === 6) {
490
- utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
491
- }
492
- if (timeString.length === 6) {
493
- utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
494
- } else {
495
- utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
496
- }
497
- return utcDate.toUTCString();
498
- }
499
- /**
500
- *
501
- * @param time HHMMSS
502
- * @returns seconds since midnight
503
- */
504
- static convertHHMMSSToTod(time) {
505
- const h = Number(time.substring(0, 2));
506
- const m = Number(time.substring(2, 4));
507
- const s = Number(time.substring(4, 6));
508
- const tod = h * 3600 + m * 60 + s;
509
- return tod;
510
- }
511
- /**
512
- *
513
- * @param time HHMMSS
514
- * @param date MMDDYY or MMDDYYYY
515
- * @returns seconds since epoch
516
- */
517
- static convertDateTimeToEpoch(time, date) {
518
- if (date.length === 6) {
519
- date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
520
- }
521
- 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`;
522
- const millis = Date.parse(timestamp);
523
- return millis / 1e3;
524
- }
525
- };
526
-
527
1008
  // lib/plugins/Label_1M_Slash.ts
528
1009
  var Label_1M_Slash = class extends DecoderPlugin {
529
1010
  name = "label-1m-slash";
@@ -590,16 +1071,17 @@ var Label_20_POS = class extends DecoderPlugin {
590
1071
  decodeResult.message = message;
591
1072
  decodeResult.raw.preamble = message.text.substring(0, 3);
592
1073
  const content = message.text.substring(3);
593
- console.log("Content: " + content);
594
1074
  const fields = content.split(",");
595
- console.log("Field Count: " + fields.length);
596
1075
  if (fields.length == 11) {
597
- console.log(`DEBUG: ${this.name}: Variation 1 detected`);
1076
+ if (options.debug) {
1077
+ console.log(`DEBUG: ${this.name}: Variation 1 detected`);
1078
+ }
598
1079
  const rawCoords = fields[0];
599
1080
  decodeResult.raw.position = CoordinateUtils.decodeStringCoordinates(rawCoords);
600
1081
  if (decodeResult.raw.position) {
601
1082
  decodeResult.formatted.items.push({
602
1083
  type: "position",
1084
+ code: "POS",
603
1085
  label: "Position",
604
1086
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
605
1087
  });
@@ -607,12 +1089,15 @@ var Label_20_POS = class extends DecoderPlugin {
607
1089
  decodeResult.decoded = true;
608
1090
  decodeResult.decoder.decodeLevel = "full";
609
1091
  } else if (fields.length == 5) {
610
- console.log(`DEBUG: ${this.name}: Variation 2 detected`);
1092
+ if (options.debug) {
1093
+ console.log(`DEBUG: ${this.name}: Variation 2 detected`);
1094
+ }
611
1095
  const rawCoords = fields[0];
612
1096
  decodeResult.raw.position = CoordinateUtils.decodeStringCoordinates(rawCoords);
613
1097
  if (decodeResult.raw.position) {
614
1098
  decodeResult.formatted.items.push({
615
1099
  type: "position",
1100
+ code: "POS",
616
1101
  label: "Position",
617
1102
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
618
1103
  });
@@ -620,7 +1105,9 @@ var Label_20_POS = class extends DecoderPlugin {
620
1105
  decodeResult.decoded = true;
621
1106
  decodeResult.decoder.decodeLevel = "full";
622
1107
  } else {
623
- console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`);
1108
+ if (options.debug) {
1109
+ console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`);
1110
+ }
624
1111
  decodeResult.decoded = false;
625
1112
  decodeResult.decoder.decodeLevel = "none";
626
1113
  }
@@ -644,15 +1131,13 @@ var Label_21_POS = class extends DecoderPlugin {
644
1131
  decodeResult.message = message;
645
1132
  decodeResult.raw.preamble = message.text.substring(0, 3);
646
1133
  const content = message.text.substring(3);
647
- console.log("Content: " + content);
648
1134
  const fields = content.split(",");
649
- console.log("Field Count: " + fields.length);
650
1135
  if (fields.length == 9) {
651
1136
  processPosition(decodeResult, fields[0].trim());
652
1137
  processAlt(decodeResult, fields[3]);
653
1138
  processTemp(decodeResult, fields[6]);
654
1139
  processArrvApt(decodeResult, fields[8]);
655
- decodeResult.raw.remaining = [
1140
+ decodeResult.remaining.text = [
656
1141
  fields[1],
657
1142
  fields[2],
658
1143
  fields[4],
@@ -662,7 +1147,9 @@ var Label_21_POS = class extends DecoderPlugin {
662
1147
  decodeResult.decoded = true;
663
1148
  decodeResult.decoder.decodeLevel = "partial";
664
1149
  } else {
665
- console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`);
1150
+ if (options.debug) {
1151
+ console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`);
1152
+ }
666
1153
  decodeResult.decoded = false;
667
1154
  decodeResult.decoder.decodeLevel = "none";
668
1155
  }
@@ -1016,9 +1503,7 @@ var Label_44_POS = class extends DecoderPlugin {
1016
1503
  console.log(results.groups);
1017
1504
  }
1018
1505
  decodeResult.raw.position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(results.groups.unsplit_coords);
1019
- decodeResult.raw.flight_level = results.groups.flight_level_or_ground == "GRD" || results.groups.flight_level_or_ground == "***" ? "0" : Number(results.groups.flight_level_or_ground);
1020
- decodeResult.raw.departure_icao = results.groups.departure_icao;
1021
- decodeResult.raw.arrival_icao = results.groups.arrival_icao;
1506
+ const flight_level = results.groups.flight_level_or_ground == "GRD" || results.groups.flight_level_or_ground == "***" ? 0 : Number(results.groups.flight_level_or_ground);
1022
1507
  decodeResult.raw.current_time = Date.parse(
1023
1508
  (/* @__PURE__ */ new Date()).getFullYear() + "-" + results.groups.current_date.substr(0, 2) + "-" + results.groups.current_date.substr(2, 2) + "T" + results.groups.current_time.substr(0, 2) + ":" + results.groups.current_time.substr(2, 2) + ":00Z"
1024
1509
  );
@@ -1036,24 +1521,9 @@ var Label_44_POS = class extends DecoderPlugin {
1036
1521
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
1037
1522
  });
1038
1523
  }
1039
- decodeResult.formatted.items.push({
1040
- type: "origin",
1041
- code: "ORG",
1042
- label: "Origin",
1043
- value: decodeResult.raw.departure_icao
1044
- });
1045
- decodeResult.formatted.items.push({
1046
- type: "destination",
1047
- code: "DST",
1048
- label: "Destination",
1049
- value: decodeResult.raw.arrival_icao
1050
- });
1051
- decodeResult.formatted.items.push({
1052
- type: "flight_level",
1053
- code: "FL",
1054
- label: "Flight Level",
1055
- value: decodeResult.raw.flight_level
1056
- });
1524
+ ResultFormatter.departureAirport(decodeResult, results.groups.departure_icao);
1525
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
1526
+ ResultFormatter.altitude(decodeResult, flight_level * 100);
1057
1527
  }
1058
1528
  decodeResult.decoded = true;
1059
1529
  decodeResult.decoder.decodeLevel = "full";
@@ -1061,6 +1531,64 @@ var Label_44_POS = class extends DecoderPlugin {
1061
1531
  }
1062
1532
  };
1063
1533
 
1534
+ // lib/plugins/Label_4N.ts
1535
+ var Label_4N = class extends DecoderPlugin {
1536
+ name = "label-4n";
1537
+ qualifiers() {
1538
+ return {
1539
+ labels: ["4N"]
1540
+ };
1541
+ }
1542
+ decode(message, options = {}) {
1543
+ const decodeResult = this.defaultResult();
1544
+ decodeResult.decoder.name = this.name;
1545
+ decodeResult.message = message;
1546
+ decodeResult.formatted.description = "Airline Defined";
1547
+ let text = message.text;
1548
+ if (text.match(/^M\d{2}A\w{6}/)) {
1549
+ ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1550
+ text = text.substring(10);
1551
+ }
1552
+ 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(" ");
1561
+ } else if (fields.length === 33) {
1562
+ decodeResult.raw.date = fields[3];
1563
+ if (fields[1] === "B") {
1564
+ ResultFormatter.position(decodeResult, { latitude: Number(fields[4]), longitude: Number(fields[5]) });
1565
+ ResultFormatter.altitude(decodeResult, fields[6]);
1566
+ }
1567
+ ResultFormatter.departureAirport(decodeResult, fields[8]);
1568
+ ResultFormatter.arrivalAirport(decodeResult, fields[9]);
1569
+ ResultFormatter.alternateAirport(decodeResult, fields[10]);
1570
+ ResultFormatter.arrivalRunway(decodeResult, fields[11].split("/")[0]);
1571
+ if (fields[12].length > 1) {
1572
+ ResultFormatter.alternateRunway(decodeResult, fields[12].split("/")[0]);
1573
+ }
1574
+ ResultFormatter.checksum(decodeResult, fields[32]);
1575
+ decodeResult.remaining.text = [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter((f) => f != "").join(",");
1576
+ } else {
1577
+ decodeResult.decoded = false;
1578
+ decodeResult.remaining.text = text;
1579
+ }
1580
+ if (decodeResult.decoded) {
1581
+ if (decodeResult.remaining.text === "")
1582
+ decodeResult.decoder.decodeLevel = "full";
1583
+ else
1584
+ decodeResult.decoder.decodeLevel = "partial";
1585
+ } else {
1586
+ decodeResult.decoder.decodeLevel = "none";
1587
+ }
1588
+ return decodeResult;
1589
+ }
1590
+ };
1591
+
1064
1592
  // lib/plugins/Label_80.ts
1065
1593
  var Label_80 = class extends DecoderPlugin {
1066
1594
  name = "label-80";
@@ -1092,27 +1620,15 @@ var Label_80 = class extends DecoderPlugin {
1092
1620
  const parts = message.text.split("\n");
1093
1621
  let posRptRegex = /^3N01 POSRPT \d\d\d\d\/\d\d (?<orig>\w+)\/(?<dest>\w+) \.(?<tail>[\w-]+)(\/(?<agate>.+) (?<sta>\w+:\w+))*/;
1094
1622
  let results = parts[0].match(posRptRegex);
1623
+ if (!results?.groups) {
1624
+ decodeResult.decoded = false;
1625
+ decodeResult.decoder.decodeLevel = "none";
1626
+ return decodeResult;
1627
+ }
1095
1628
  if (results && results.length > 0) {
1096
- decodeResult.raw.origin = results.groups.orig;
1097
- decodeResult.formatted.items.push({
1098
- type: "origin",
1099
- code: "ORG",
1100
- label: "Origin",
1101
- value: `${results.groups.orig}`
1102
- });
1103
- decodeResult.raw.destination = results.groups.dest;
1104
- decodeResult.formatted.items.push({
1105
- type: "destination",
1106
- code: "DST",
1107
- label: "Destination",
1108
- value: `${results.groups.dest}`
1109
- });
1110
- decodeResult.raw.tail = results.groups.tail;
1111
- decodeResult.formatted.items.push({
1112
- type: "tail",
1113
- label: "Tail",
1114
- value: `${results.groups.tail}`
1115
- });
1629
+ ResultFormatter.departureAirport(decodeResult, results.groups.orig);
1630
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.dest);
1631
+ ResultFormatter.tail(decodeResult, results.groups.tail);
1116
1632
  if (results.groups.agate) {
1117
1633
  decodeResult.raw.arrival_gate = results.groups.agate;
1118
1634
  decodeResult.formatted.items.push({
@@ -1134,15 +1650,9 @@ var Label_80 = class extends DecoderPlugin {
1134
1650
  for (const part of remainingParts) {
1135
1651
  const matches = part.matchAll(posRptRegex);
1136
1652
  for (const match of matches) {
1137
- switch (match.groups.field) {
1653
+ switch (match.groups?.field) {
1138
1654
  case "ALT": {
1139
- decodeResult.raw.altitude = match.groups.value;
1140
- decodeResult.formatted.items.push({
1141
- type: "altitude",
1142
- code: "ALT",
1143
- label: this.descriptions[match.groups.field],
1144
- value: `${decodeResult.raw.altitude} feet`
1145
- });
1655
+ ResultFormatter.altitude(decodeResult, Number(match.groups.value));
1146
1656
  break;
1147
1657
  }
1148
1658
  case "DWND": {
@@ -1156,13 +1666,8 @@ var Label_80 = class extends DecoderPlugin {
1156
1666
  break;
1157
1667
  }
1158
1668
  case "FL": {
1159
- decodeResult.raw.flight_level = match.groups.value;
1160
- decodeResult.formatted.items.push({
1161
- type: "flight_level",
1162
- code: "FL",
1163
- label: this.descriptions[match.groups.field],
1164
- value: decodeResult.raw.flight_level
1165
- });
1669
+ const flight_level = Number(match.groups.value);
1670
+ ResultFormatter.altitude(decodeResult, flight_level * 100);
1166
1671
  break;
1167
1672
  }
1168
1673
  case "FOB": {
@@ -1186,7 +1691,7 @@ var Label_80 = class extends DecoderPlugin {
1186
1691
  break;
1187
1692
  }
1188
1693
  case "MCH": {
1189
- decodeResult.raw.mach = match.groups.value / 1e3;
1694
+ decodeResult.raw.mach = Number(match.groups.value) / 1e3;
1190
1695
  decodeResult.formatted.items.push({
1191
1696
  type: "mach",
1192
1697
  code: "MCH",
@@ -1208,20 +1713,13 @@ var Label_80 = class extends DecoderPlugin {
1208
1713
  case "POS": {
1209
1714
  const posRegex = /^(?<latd>[NS])(?<lat>.+)(?<lngd>[EW])(?<lng>.+)/;
1210
1715
  const posResult = match.groups.value.match(posRegex);
1211
- const lat = Number(posResult.groups.lat) * (posResult.groups.lngd === "S" ? -1 : 1);
1212
- const lon = Number(posResult.groups.lng) * (posResult.groups.lngd === "W" ? -1 : 1);
1213
- const latitude = Number.isInteger(lat) ? lat / 1e3 : lat / 100;
1214
- const longitude = Number.isInteger(lon) ? lon / 1e3 : lon / 100;
1215
- decodeResult.raw.position = {
1216
- latitude,
1217
- longitude
1716
+ const lat = Number(posResult?.groups?.lat) * (posResult?.groups?.lngd === "S" ? -1 : 1);
1717
+ const lon = Number(posResult?.groups?.lng) * (posResult?.groups?.lngd === "W" ? -1 : 1);
1718
+ const position = {
1719
+ latitude: Number.isInteger(lat) ? lat / 1e3 : lat / 100,
1720
+ longitude: Number.isInteger(lon) ? lon / 1e3 : lon / 100
1218
1721
  };
1219
- decodeResult.formatted.items.push({
1220
- type: "position",
1221
- code: "POS",
1222
- label: "Position",
1223
- value: CoordinateUtils.coordinateString(decodeResult.raw.position)
1224
- });
1722
+ ResultFormatter.position(decodeResult, position);
1225
1723
  break;
1226
1724
  }
1227
1725
  case "SWND": {
@@ -1235,7 +1733,7 @@ var Label_80 = class extends DecoderPlugin {
1235
1733
  break;
1236
1734
  }
1237
1735
  default: {
1238
- if (match.groups.field != void 0) {
1736
+ if (match.groups?.field != void 0) {
1239
1737
  const description = this.descriptions[match.groups.field] ? this.descriptions[match.groups.field] : "Unknown";
1240
1738
  decodeResult.formatted.items.push({
1241
1739
  type: match.groups.field,
@@ -1246,10 +1744,80 @@ var Label_80 = class extends DecoderPlugin {
1246
1744
  }
1247
1745
  }
1248
1746
  }
1249
- }
1747
+ }
1748
+ }
1749
+ decodeResult.decoded = true;
1750
+ decodeResult.decoder.decodeLevel = "partial";
1751
+ }
1752
+ return decodeResult;
1753
+ }
1754
+ };
1755
+
1756
+ // lib/plugins/Label_83.ts
1757
+ var Label_83 = class extends DecoderPlugin {
1758
+ name = "label-83";
1759
+ qualifiers() {
1760
+ return {
1761
+ labels: ["83"]
1762
+ };
1763
+ }
1764
+ decode(message, options = {}) {
1765
+ const decodeResult = this.defaultResult();
1766
+ decodeResult.decoder.name = this.name;
1767
+ decodeResult.message = message;
1768
+ decodeResult.formatted.description = "Airline Defined";
1769
+ let text = message.text;
1770
+ if (text.match(/^M\d{2}A\w{6}/)) {
1771
+ ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1772
+ text = text.substring(10);
1773
+ }
1774
+ decodeResult.decoded = true;
1775
+ if (text.substring(0, 10) === "4DH3 ETAT2") {
1776
+ const fields = text.split(/\s+/);
1777
+ if (fields[2].length > 5) {
1778
+ decodeResult.raw.day_of_month = fields[2].substring(5);
1779
+ }
1780
+ decodeResult.remaining.text = fields[2].substring(0, 4);
1781
+ const subfields = fields[3].split("/");
1782
+ ResultFormatter.departureAirport(decodeResult, subfields[0]);
1783
+ ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
1784
+ 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);
1791
+ } else {
1792
+ const fields = text.replace(/\s/g, "").split(",");
1793
+ if (fields.length === 9) {
1794
+ ResultFormatter.departureAirport(decodeResult, fields[0]);
1795
+ ResultFormatter.arrivalAirport(decodeResult, fields[1]);
1796
+ decodeResult.raw.day_of_month = fields[2].substring(0, 2);
1797
+ decodeResult.raw.time = fields[2].substring(2);
1798
+ ResultFormatter.position(
1799
+ decodeResult,
1800
+ {
1801
+ latitude: Number(fields[3].replace(/\s/g, "")),
1802
+ longitude: Number(fields[4].replace(/\s/g, ""))
1803
+ }
1804
+ );
1805
+ ResultFormatter.altitude(decodeResult, fields[5]);
1806
+ ResultFormatter.groundspeed(decodeResult, fields[6]);
1807
+ ResultFormatter.heading(decodeResult, fields[7]);
1808
+ decodeResult.remaining.text = fields[8];
1809
+ } else {
1810
+ decodeResult.decoded = false;
1811
+ decodeResult.remaining.text = message.text;
1250
1812
  }
1251
- decodeResult.decoded = true;
1252
- decodeResult.decodeLevel = "partial";
1813
+ }
1814
+ if (decodeResult.decoded) {
1815
+ if (decodeResult.remaining.text === "")
1816
+ decodeResult.decoder.decodeLevel = "full";
1817
+ else
1818
+ decodeResult.decoder.decodeLevel = "partial";
1819
+ } else {
1820
+ decodeResult.decoder.decodeLevel = "none";
1253
1821
  }
1254
1822
  return decodeResult;
1255
1823
  }
@@ -1396,183 +1964,6 @@ function processUnknown(decodeResult, value) {
1396
1964
  decodeResult.remaining.text += value;
1397
1965
  }
1398
1966
 
1399
- // lib/utils/result_formatter.ts
1400
- var ResultFormatter = class {
1401
- static altitude(decodeResult, value) {
1402
- decodeResult.raw.altitude = value;
1403
- decodeResult.formatted.items.push({
1404
- type: "altitude",
1405
- code: "ALT",
1406
- label: "Altitude",
1407
- value: `${decodeResult.raw.altitude} feet`
1408
- });
1409
- }
1410
- static flightNumber(decodeResult, value) {
1411
- decodeResult.raw.flight_number = value;
1412
- }
1413
- static departureAirport(decodeResult, value) {
1414
- decodeResult.raw.departure_icao = value;
1415
- decodeResult.formatted.items.push({
1416
- type: "origin",
1417
- code: "ORG",
1418
- label: "Origin",
1419
- value: decodeResult.raw.departure_icao
1420
- });
1421
- }
1422
- static departureRunway(decodeResult, value) {
1423
- decodeResult.raw.departure_runway = value;
1424
- decodeResult.formatted.items.push({
1425
- type: "runway",
1426
- code: "DEPRWY",
1427
- label: "Departure Runway",
1428
- value: decodeResult.raw.departure_runway
1429
- });
1430
- }
1431
- static arrivalAirport(decodeResult, value) {
1432
- decodeResult.raw.arrival_icao = value;
1433
- decodeResult.formatted.items.push({
1434
- type: "destination",
1435
- code: "DST",
1436
- label: "Destination",
1437
- value: decodeResult.raw.arrival_icao
1438
- });
1439
- }
1440
- static eta(decodeResult, value) {
1441
- decodeResult.formatted.items.push({
1442
- type: "eta",
1443
- code: "ETA",
1444
- label: "Estimated Time of Arrival",
1445
- value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
1446
- });
1447
- }
1448
- static arrivalRunway(decodeResult, value) {
1449
- decodeResult.raw.arrival_runway = value;
1450
- decodeResult.formatted.items.push({
1451
- type: "runway",
1452
- label: "Arrival Runway",
1453
- value: decodeResult.raw.arrival_runway
1454
- });
1455
- }
1456
- static currentFuel(decodeResult, value) {
1457
- decodeResult.raw.fuel_on_board = value;
1458
- decodeResult.formatted.items.push({
1459
- type: "fuel_on_board",
1460
- code: "FOB",
1461
- label: "Fuel On Board",
1462
- value: decodeResult.raw.fuel_on_board.toString()
1463
- });
1464
- }
1465
- static remainingFuel(decodeResult, value) {
1466
- decodeResult.raw.fuel_remaining = value;
1467
- decodeResult.formatted.items.push({
1468
- type: "fuel_remaining",
1469
- label: "Fuel Remaining",
1470
- value: decodeResult.raw.fuel_remaining.toString()
1471
- });
1472
- }
1473
- static checksum(decodeResult, value) {
1474
- decodeResult.raw.checksum = Number("0x" + value);
1475
- decodeResult.formatted.items.push({
1476
- type: "message_checksum",
1477
- code: "CHECKSUM",
1478
- label: "Message Checksum",
1479
- value: "0x" + ("0000" + decodeResult.raw.checksum.toString(16)).slice(-4)
1480
- });
1481
- }
1482
- static groundspeed(decodeResult, value) {
1483
- decodeResult.raw.groundspeed = value;
1484
- decodeResult.formatted.items.push({
1485
- type: "aircraft_groundspeed",
1486
- code: "GSPD",
1487
- label: "Aircraft Groundspeed",
1488
- value: `${decodeResult.raw.groundspeed}`
1489
- });
1490
- }
1491
- static temperature(decodeResult, value) {
1492
- decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
1493
- decodeResult.formatted.items.push({
1494
- type: "outside_air_temperature",
1495
- code: "OATEMP",
1496
- label: "Outside Air Temperature (C)",
1497
- value: `${decodeResult.raw.outside_air_temperature}`
1498
- });
1499
- }
1500
- static unknown(decodeResult, value) {
1501
- decodeResult.remaining.text += "," + value;
1502
- }
1503
- };
1504
-
1505
- // lib/utils/route_utils.ts
1506
- var RouteUtils = class _RouteUtils {
1507
- static routeToString(route) {
1508
- let str = "";
1509
- if (route.name) {
1510
- str += route.name;
1511
- }
1512
- if (route.runway) {
1513
- str += `(${route.runway})`;
1514
- }
1515
- if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
1516
- str += " starting at ";
1517
- } else if (str.length !== 0 && route.waypoints) {
1518
- str += ": ";
1519
- }
1520
- if (route.waypoints) {
1521
- str += _RouteUtils.waypointsToString(route.waypoints);
1522
- }
1523
- return str;
1524
- }
1525
- static waypointToString(waypoint) {
1526
- let s = waypoint.name;
1527
- if (waypoint.latitude && waypoint.longitude) {
1528
- s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
1529
- }
1530
- if (waypoint.offset) {
1531
- s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
1532
- }
1533
- if (waypoint.time && waypoint.timeFormat) {
1534
- s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
1535
- }
1536
- return s;
1537
- }
1538
- static getWaypoint(leg) {
1539
- const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
1540
- if (regex?.length == 4) {
1541
- return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
1542
- }
1543
- const waypoint = leg.split(",");
1544
- if (waypoint.length == 2) {
1545
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
1546
- if (position) {
1547
- return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
1548
- }
1549
- }
1550
- if (leg.length == 13 || leg.length == 14) {
1551
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
1552
- const name = waypoint.length == 2 ? waypoint[0] : "";
1553
- if (position) {
1554
- return { name, latitude: position.latitude, longitude: position.longitude };
1555
- }
1556
- }
1557
- return { name: leg };
1558
- }
1559
- // move out if we want public
1560
- static timestampToString(time, format) {
1561
- const date = new Date(time * 1e3);
1562
- if (format == "tod") {
1563
- return date.toISOString().slice(11, 19);
1564
- }
1565
- return date.toISOString().slice(0, -5) + "Z";
1566
- }
1567
- static waypointsToString(waypoints) {
1568
- let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
1569
- if (str.startsWith(" > ")) {
1570
- str = ">>" + str.slice(2);
1571
- }
1572
- return str;
1573
- }
1574
- };
1575
-
1576
1967
  // lib/utils/flight_plan_utils.ts
1577
1968
  var FlightPlanUtils = class _FlightPlanUtils {
1578
1969
  /**
@@ -1834,13 +2225,7 @@ function processTimeOfDeparture(decodeResult, data) {
1834
2225
  return false;
1835
2226
  }
1836
2227
  function processIdentification(decodeResult, data) {
1837
- decodeResult.raw.tail = data[0];
1838
- decodeResult.formatted.items.push({
1839
- type: "tail",
1840
- code: "TAIL",
1841
- label: "Tail",
1842
- value: decodeResult.raw.tail
1843
- });
2228
+ ResultFormatter.tail(decodeResult, data[0]);
1844
2229
  if (data.length > 1) {
1845
2230
  decodeResult.raw.flight_number = data[1];
1846
2231
  }
@@ -1937,8 +2322,6 @@ function processPS(decodeResult, data) {
1937
2322
  } else {
1938
2323
  allKnownFields = false;
1939
2324
  }
1940
- console.log("PS data.length: ", data.length);
1941
- console.log("PS data: ", data);
1942
2325
  if (data.length === 9) {
1943
2326
  processRoute(decodeResult, data[3], data[1], data[5], data[4], void 0);
1944
2327
  ResultFormatter.altitude(decodeResult, Number(data[2]) * 100);
@@ -1973,8 +2356,6 @@ function processPosition2(decodeResult, data) {
1973
2356
  } else {
1974
2357
  allKnownFields = false;
1975
2358
  }
1976
- console.log("data.length: ", data.length);
1977
- console.log("data: ", data);
1978
2359
  if (data.length >= 10) {
1979
2360
  ResultFormatter.altitude(decodeResult, Number(data[3]) * 100);
1980
2361
  processRoute(decodeResult, data[1], data[2], data[4], data[5], data[6]);
@@ -2032,7 +2413,7 @@ var Label_H1_FPN = class extends DecoderPlugin {
2032
2413
  decodeResult.decoded = true;
2033
2414
  decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2034
2415
  if (decodeResult.formatted.items.length === 0) {
2035
- if (options?.debug) {
2416
+ if (options.debug) {
2036
2417
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
2037
2418
  }
2038
2419
  decodeResult.remaining.text = message.text;
@@ -2062,7 +2443,7 @@ var Label_H1_FTX = class extends DecoderPlugin {
2062
2443
  decodeResult.decoded = true;
2063
2444
  decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2064
2445
  if (decodeResult.formatted.items.length === 0) {
2065
- if (options?.debug) {
2446
+ if (options.debug) {
2066
2447
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
2067
2448
  }
2068
2449
  decodeResult.remaining.text = message.text;
@@ -2093,7 +2474,7 @@ var Label_H1_INI = class extends DecoderPlugin {
2093
2474
  decodeResult.decoded = true;
2094
2475
  decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2095
2476
  if (decodeResult.formatted.items.length === 0) {
2096
- if (options?.debug) {
2477
+ if (options.debug) {
2097
2478
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
2098
2479
  }
2099
2480
  decodeResult.remaining.text = message.text;
@@ -2182,7 +2563,7 @@ var Label_H1_POS = class extends DecoderPlugin {
2182
2563
  decodeResult.decoded = true;
2183
2564
  decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2184
2565
  if (decodeResult.formatted.items.length === 0) {
2185
- if (options?.debug) {
2566
+ if (options.debug) {
2186
2567
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
2187
2568
  }
2188
2569
  decodeResult.remaining.text = message.text;
@@ -2249,6 +2630,60 @@ function processUnknown2(decodeResult, value) {
2249
2630
  decodeResult.remaining.text += value;
2250
2631
  }
2251
2632
 
2633
+ // lib/plugins/Label_HX.ts
2634
+ var Label_HX = class extends DecoderPlugin {
2635
+ name = "label-hx";
2636
+ qualifiers() {
2637
+ return {
2638
+ labels: ["HX"],
2639
+ preambles: ["RA FMT LOCATION", "RA FMT 43"]
2640
+ };
2641
+ }
2642
+ decode(message, options = {}) {
2643
+ const decodeResult = this.defaultResult();
2644
+ decodeResult.decoder.name = this.name;
2645
+ decodeResult.message = message;
2646
+ decodeResult.formatted.description = "Undelivered Uplink Report";
2647
+ const parts = message.text.split(" ");
2648
+ decodeResult.decoded = true;
2649
+ if (parts[2] === "LOCATION") {
2650
+ let latdir = parts[3].substring(0, 1);
2651
+ let latdeg = Number(parts[3].substring(1, 3));
2652
+ let latmin = Number(parts[3].substring(3, 7));
2653
+ let londir = parts[4].substring(0, 1);
2654
+ let londeg = Number(parts[4].substring(1, 4));
2655
+ let lonmin = Number(parts[4].substring(4, 8));
2656
+ decodeResult.raw.position = {
2657
+ latitude: (latdeg + latmin / 60) * (latdir === "N" ? 1 : -1),
2658
+ longitude: (londeg + lonmin / 60) * (londir === "E" ? 1 : -1)
2659
+ };
2660
+ decodeResult.remaining.text = parts.slice(5).join(" ");
2661
+ } else if (parts[2] === "43") {
2662
+ ResultFormatter.departureAirport(decodeResult, parts[3]);
2663
+ decodeResult.remaining.text = parts.slice(4).join(" ");
2664
+ } else {
2665
+ decodeResult.decoded = false;
2666
+ }
2667
+ if (decodeResult.raw.position) {
2668
+ decodeResult.formatted.items.push({
2669
+ type: "aircraft_position",
2670
+ code: "POS",
2671
+ label: "Aircraft Position",
2672
+ value: CoordinateUtils.coordinateString(decodeResult.raw.position)
2673
+ });
2674
+ }
2675
+ if (decodeResult.decoded) {
2676
+ if (decodeResult.remaining.text === "")
2677
+ decodeResult.decoder.decodeLevel = "full";
2678
+ else
2679
+ decodeResult.decoder.decodeLevel = "partial";
2680
+ } else {
2681
+ decodeResult.decoder.decodeLevel = "none";
2682
+ }
2683
+ return decodeResult;
2684
+ }
2685
+ };
2686
+
2252
2687
  // lib/plugins/Label_SQ.ts
2253
2688
  var Label_SQ = class extends DecoderPlugin {
2254
2689
  name = "label-sq";
@@ -2289,11 +2724,13 @@ var Label_SQ = class extends DecoderPlugin {
2289
2724
  decodeResult.formatted.items = [
2290
2725
  {
2291
2726
  type: "network",
2727
+ code: "NETT",
2292
2728
  label: "Network",
2293
2729
  value: formattedNetwork
2294
2730
  },
2295
2731
  {
2296
2732
  type: "version",
2733
+ code: "VER",
2297
2734
  label: "Version",
2298
2735
  value: decodeResult.raw.version
2299
2736
  }
@@ -2302,6 +2739,7 @@ var Label_SQ = class extends DecoderPlugin {
2302
2739
  if (decodeResult.raw.groundStation.icaoCode && decodeResult.raw.groundStation.number) {
2303
2740
  decodeResult.formatted.items.push({
2304
2741
  type: "ground_station",
2742
+ code: "GNDSTN",
2305
2743
  label: "Ground Station",
2306
2744
  value: `${decodeResult.raw.groundStation.icaoCode}${decodeResult.raw.groundStation.number}`
2307
2745
  });
@@ -2309,6 +2747,7 @@ var Label_SQ = class extends DecoderPlugin {
2309
2747
  if (decodeResult.raw.groundStation.iataCode) {
2310
2748
  decodeResult.formatted.items.push({
2311
2749
  type: "iataCode",
2750
+ code: "IATA",
2312
2751
  label: "IATA",
2313
2752
  value: decodeResult.raw.groundStation.iataCode
2314
2753
  });
@@ -2316,6 +2755,7 @@ var Label_SQ = class extends DecoderPlugin {
2316
2755
  if (decodeResult.raw.groundStation.icaoCode) {
2317
2756
  decodeResult.formatted.items.push({
2318
2757
  type: "icaoCode",
2758
+ code: "ICAO",
2319
2759
  label: "ICAO",
2320
2760
  value: decodeResult.raw.groundStation.icaoCode
2321
2761
  });
@@ -2323,6 +2763,7 @@ var Label_SQ = class extends DecoderPlugin {
2323
2763
  if (decodeResult.raw.groundStation.coordinates.latitude) {
2324
2764
  decodeResult.formatted.items.push({
2325
2765
  type: "coordinates",
2766
+ code: "COORD",
2326
2767
  label: "Ground Station Location",
2327
2768
  value: `${decodeResult.raw.groundStation.coordinates.latitude}, ${decodeResult.raw.groundStation.coordinates.longitude}`
2328
2769
  });
@@ -2330,6 +2771,7 @@ var Label_SQ = class extends DecoderPlugin {
2330
2771
  if (decodeResult.raw.groundStation.airport) {
2331
2772
  decodeResult.formatted.items.push({
2332
2773
  type: "airport",
2774
+ code: "APT",
2333
2775
  label: "Airport",
2334
2776
  value: `${decodeResult.raw.groundStation.airport.name} (${decodeResult.raw.groundStation.airport.icao}) in ${decodeResult.raw.groundStation.airport.location}`
2335
2777
  });
@@ -2338,6 +2780,7 @@ var Label_SQ = class extends DecoderPlugin {
2338
2780
  if (decodeResult.raw.vdlFrequency) {
2339
2781
  decodeResult.formatted.items.push({
2340
2782
  type: "vdlFrequency",
2783
+ code: "VDLFRQ",
2341
2784
  label: "VDL Frequency",
2342
2785
  value: `${decodeResult.raw.vdlFrequency} MHz`
2343
2786
  });
@@ -2494,31 +2937,48 @@ var Label_QQ = class extends DecoderPlugin {
2494
2937
  decode(message, options = {}) {
2495
2938
  const decodeResult = this.defaultResult();
2496
2939
  decodeResult.decoder.name = this.name;
2497
- decodeResult.raw.origin = message.text.substring(0, 4);
2498
- decodeResult.raw.destination = message.text.substring(4, 8);
2499
- decodeResult.raw.wheels_off = message.text.substring(8, 12);
2500
- decodeResult.remaining.text = message.text.substring(12);
2940
+ decodeResult.message = message;
2501
2941
  decodeResult.formatted.description = "OFF Report";
2502
- decodeResult.formatted.items = [
2503
- {
2504
- type: "origin",
2505
- code: "ORG",
2506
- label: "Origin",
2507
- value: decodeResult.raw.origin
2508
- },
2509
- {
2510
- type: "destination",
2511
- code: "DST",
2512
- label: "Destination",
2513
- value: decodeResult.raw.destination
2514
- },
2515
- {
2516
- type: "wheels_off",
2517
- code: "WOFF",
2518
- label: "Wheels OFF",
2519
- value: decodeResult.raw.wheels_off
2942
+ ResultFormatter.departureAirport(decodeResult, message.text.substring(0, 4));
2943
+ ResultFormatter.arrivalAirport(decodeResult, message.text.substring(4, 8));
2944
+ decodeResult.raw.wheels_off = message.text.substring(8, 12);
2945
+ if (message.text.substring(12, 19) === "\r\n001FE") {
2946
+ decodeResult.raw.day_of_month = message.text.substring(19, 21);
2947
+ decodeResult.raw.wheels_off = message.text.substring(21, 27);
2948
+ let latdir = message.text.substring(27, 28);
2949
+ let latdeg = Number(message.text.substring(28, 30));
2950
+ let latmin = Number(message.text.substring(30, 34));
2951
+ let londir = message.text.substring(34, 35);
2952
+ let londeg = Number(message.text.substring(35, 38));
2953
+ let lonmin = Number(message.text.substring(38, 42));
2954
+ decodeResult.raw.position = {
2955
+ latitude: (latdeg + latmin / 60) * (latdir === "N" ? 1 : -1),
2956
+ longitude: (londeg + lonmin / 60) * (londir === "E" ? 1 : -1)
2957
+ };
2958
+ decodeResult.remaining.text = message.text.substring(42, 45);
2959
+ if (decodeResult.remaining.text !== "---") {
2960
+ ResultFormatter.groundspeed(decodeResult, message.text.substring(45, 48));
2961
+ } else {
2962
+ ResultFormatter.unknown(decodeResult, message.text.substring(45, 48));
2520
2963
  }
2521
- ];
2964
+ ResultFormatter.unknown(decodeResult, message.text.substring(48));
2965
+ } else {
2966
+ decodeResult.remaining.text = message.text.substring(12);
2967
+ }
2968
+ decodeResult.formatted.items.push({
2969
+ type: "wheels_off",
2970
+ code: "WOFF",
2971
+ label: "Wheels OFF",
2972
+ value: decodeResult.raw.wheels_off
2973
+ });
2974
+ if (decodeResult.raw.position) {
2975
+ decodeResult.formatted.items.push({
2976
+ type: "aircraft_position",
2977
+ code: "POS",
2978
+ label: "Aircraft Position",
2979
+ value: CoordinateUtils.coordinateString(decodeResult.raw.position)
2980
+ });
2981
+ }
2522
2982
  decodeResult.decoded = true;
2523
2983
  if (decodeResult.remaining.text === "")
2524
2984
  decodeResult.decoder.decodeLevel = "full";
@@ -3341,7 +3801,11 @@ var MessageDecoder = class {
3341
3801
  this.debug = false;
3342
3802
  this.registerPlugin(new Label_ColonComma(this));
3343
3803
  this.registerPlugin(new Label_5Z(this));
3804
+ this.registerPlugin(new Label_10_LDR(this));
3805
+ this.registerPlugin(new Label_10_POS(this));
3806
+ this.registerPlugin(new Label_10_Slash(this));
3344
3807
  this.registerPlugin(new Label_12_N_Space(this));
3808
+ this.registerPlugin(new Label_13Through18_Slash(this));
3345
3809
  this.registerPlugin(new Label_15(this));
3346
3810
  this.registerPlugin(new Label_15_FST(this));
3347
3811
  this.registerPlugin(new Label_16_N_Space(this));
@@ -3353,6 +3817,7 @@ var MessageDecoder = class {
3353
3817
  this.registerPlugin(new Label_44_OFF(this));
3354
3818
  this.registerPlugin(new Label_44_ON(this));
3355
3819
  this.registerPlugin(new Label_44_POS(this));
3820
+ this.registerPlugin(new Label_4N(this));
3356
3821
  this.registerPlugin(new Label_B6_Forwardslash(this));
3357
3822
  this.registerPlugin(new Label_H1_FPN(this));
3358
3823
  this.registerPlugin(new Label_H1_FLR(this));
@@ -3361,7 +3826,9 @@ var MessageDecoder = class {
3361
3826
  this.registerPlugin(new Label_H1_OHMA(this));
3362
3827
  this.registerPlugin(new Label_H1_POS(this));
3363
3828
  this.registerPlugin(new Label_H1_WRN(this));
3829
+ this.registerPlugin(new Label_HX(this));
3364
3830
  this.registerPlugin(new Label_80(this));
3831
+ this.registerPlugin(new Label_83(this));
3365
3832
  this.registerPlugin(new Label_8E(this));
3366
3833
  this.registerPlugin(new Label_1M_Slash(this));
3367
3834
  this.registerPlugin(new Label_SQ(this));