@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.mjs CHANGED
@@ -60,6 +60,271 @@ var DecoderPlugin = class {
60
60
  }
61
61
  };
62
62
 
63
+ // lib/utils/coordinate_utils.ts
64
+ var CoordinateUtils = class {
65
+ /**
66
+ * Decode a string of coordinates into an object with latitude and longitude in millidegrees
67
+ * @param stringCoords - The string of coordinates to decode
68
+ *
69
+ * @returns An object with latitude and longitude properties
70
+ */
71
+ static decodeStringCoordinates(stringCoords) {
72
+ var results = {};
73
+ const firstChar = stringCoords.substring(0, 1);
74
+ let middleChar = stringCoords.substring(6, 7);
75
+ let longitudeChars = stringCoords.substring(7, 13);
76
+ if (middleChar == " ") {
77
+ middleChar = stringCoords.substring(7, 8);
78
+ longitudeChars = stringCoords.substring(8, 14);
79
+ }
80
+ if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
81
+ results.latitude = Number(stringCoords.substring(1, 6)) / 1e3 * (firstChar === "S" ? -1 : 1);
82
+ results.longitude = Number(longitudeChars) / 1e3 * (middleChar === "W" ? -1 : 1);
83
+ } else {
84
+ return;
85
+ }
86
+ return results;
87
+ }
88
+ /**
89
+ * Decode a string of coordinates into an object with latitude and longitude in degrees and decimal minutes
90
+ * @param stringCoords - The string of coordinates to decode
91
+ *
92
+ * @returns An object with latitude and longitude properties
93
+ */
94
+ static decodeStringCoordinatesDecimalMinutes(stringCoords) {
95
+ var results = {};
96
+ const firstChar = stringCoords.substring(0, 1);
97
+ let middleChar = stringCoords.substring(6, 7);
98
+ let longitudeChars = stringCoords.substring(7, 13);
99
+ if (middleChar == " ") {
100
+ middleChar = stringCoords.substring(7, 8);
101
+ longitudeChars = stringCoords.substring(8, 14);
102
+ }
103
+ const latDeg = Math.trunc(Number(stringCoords.substring(1, 6)) / 1e3);
104
+ const latMin = Number(stringCoords.substring(1, 6)) % 1e3 / 10;
105
+ const lonDeg = Math.trunc(Number(longitudeChars) / 1e3);
106
+ const lonMin = Number(longitudeChars) % 1e3 / 10;
107
+ if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
108
+ results.latitude = (latDeg + latMin / 60) * (firstChar === "S" ? -1 : 1);
109
+ results.longitude = (lonDeg + lonMin / 60) * (middleChar === "W" ? -1 : 1);
110
+ } else {
111
+ return;
112
+ }
113
+ return results;
114
+ }
115
+ static coordinateString(coords) {
116
+ const latDir = coords.latitude > 0 ? "N" : "S";
117
+ const lonDir = coords.longitude > 0 ? "E" : "W";
118
+ return `${Math.abs(coords.latitude).toFixed(3)} ${latDir}, ${Math.abs(coords.longitude).toFixed(3)} ${lonDir}`;
119
+ }
120
+ static getDirection(coord) {
121
+ if (coord.startsWith("N") || coord.startsWith("E")) {
122
+ return 1;
123
+ } else if (coord.startsWith("S") || coord.startsWith("W")) {
124
+ return -1;
125
+ }
126
+ return NaN;
127
+ }
128
+ static dmsToDecimalDegrees(degrees, minutes, seconds) {
129
+ return degrees + minutes / 60 + seconds / 3600;
130
+ }
131
+ };
132
+
133
+ // lib/utils/result_formatter.ts
134
+ var ResultFormatter = class {
135
+ static position(decodeResult, value) {
136
+ decodeResult.raw.position = value;
137
+ decodeResult.formatted.items.push({
138
+ type: "aircraft_position",
139
+ code: "POS",
140
+ label: "Aircraft Position",
141
+ value: CoordinateUtils.coordinateString(value)
142
+ });
143
+ }
144
+ static altitude(decodeResult, value) {
145
+ decodeResult.raw.altitude = value;
146
+ decodeResult.formatted.items.push({
147
+ type: "altitude",
148
+ code: "ALT",
149
+ label: "Altitude",
150
+ value: `${decodeResult.raw.altitude} feet`
151
+ });
152
+ }
153
+ static flightNumber(decodeResult, value) {
154
+ decodeResult.raw.flight_number = value;
155
+ }
156
+ static departureAirport(decodeResult, value) {
157
+ decodeResult.raw.departure_icao = value;
158
+ decodeResult.formatted.items.push({
159
+ type: "origin",
160
+ code: "ORG",
161
+ label: "Origin",
162
+ value: decodeResult.raw.departure_icao
163
+ });
164
+ }
165
+ static departureRunway(decodeResult, value) {
166
+ decodeResult.raw.departure_runway = value;
167
+ decodeResult.formatted.items.push({
168
+ type: "runway",
169
+ code: "DEPRWY",
170
+ label: "Departure Runway",
171
+ value: decodeResult.raw.departure_runway
172
+ });
173
+ }
174
+ static arrivalAirport(decodeResult, value) {
175
+ decodeResult.raw.arrival_icao = value;
176
+ decodeResult.formatted.items.push({
177
+ type: "destination",
178
+ code: "DST",
179
+ label: "Destination",
180
+ value: decodeResult.raw.arrival_icao
181
+ });
182
+ }
183
+ static alternateAirport(decodeResult, value) {
184
+ decodeResult.raw.alternate_icao = value;
185
+ decodeResult.formatted.items.push({
186
+ type: "destination",
187
+ code: "ALT_DST",
188
+ label: "Alternate Destination",
189
+ value: decodeResult.raw.alternate_icao
190
+ });
191
+ }
192
+ // FIXME - make seconds since midnight for time of day
193
+ static eta(decodeResult, value) {
194
+ decodeResult.raw.eta_time = value;
195
+ decodeResult.formatted.items.push({
196
+ type: "eta",
197
+ code: "ETA",
198
+ label: "Estimated Time of Arrival",
199
+ value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
200
+ });
201
+ }
202
+ static arrivalRunway(decodeResult, value) {
203
+ decodeResult.raw.arrival_runway = value;
204
+ decodeResult.formatted.items.push({
205
+ type: "runway",
206
+ code: "ARWY",
207
+ label: "Arrival Runway",
208
+ value: decodeResult.raw.arrival_runway
209
+ });
210
+ }
211
+ static alternateRunway(decodeResult, value) {
212
+ decodeResult.raw.alternate_runway = value;
213
+ decodeResult.formatted.items.push({
214
+ type: "runway",
215
+ code: "ALT_ARWY",
216
+ label: "Alternate Runway",
217
+ value: decodeResult.raw.alternate_runway
218
+ });
219
+ }
220
+ static currentFuel(decodeResult, value) {
221
+ decodeResult.raw.fuel_on_board = value;
222
+ decodeResult.formatted.items.push({
223
+ type: "fuel_on_board",
224
+ code: "FOB",
225
+ label: "Fuel On Board",
226
+ value: decodeResult.raw.fuel_on_board.toString()
227
+ });
228
+ }
229
+ static remainingFuel(decodeResult, value) {
230
+ decodeResult.raw.fuel_remaining = value;
231
+ decodeResult.formatted.items.push({
232
+ type: "fuel_remaining",
233
+ code: "FUEL",
234
+ label: "Fuel Remaining",
235
+ value: decodeResult.raw.fuel_remaining.toString()
236
+ });
237
+ }
238
+ static checksum(decodeResult, value) {
239
+ decodeResult.raw.checksum = Number("0x" + value);
240
+ decodeResult.formatted.items.push({
241
+ type: "message_checksum",
242
+ code: "CHECKSUM",
243
+ label: "Message Checksum",
244
+ value: "0x" + ("0000" + decodeResult.raw.checksum.toString(16)).slice(-4)
245
+ });
246
+ }
247
+ static groundspeed(decodeResult, value) {
248
+ decodeResult.raw.groundspeed = value;
249
+ decodeResult.formatted.items.push({
250
+ type: "aircraft_groundspeed",
251
+ code: "GSPD",
252
+ label: "Aircraft Groundspeed",
253
+ value: `${decodeResult.raw.groundspeed} knots`
254
+ });
255
+ }
256
+ static temperature(decodeResult, value) {
257
+ decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
258
+ decodeResult.formatted.items.push({
259
+ type: "outside_air_temperature",
260
+ code: "OATEMP",
261
+ label: "Outside Air Temperature (C)",
262
+ value: `${decodeResult.raw.outside_air_temperature}`
263
+ });
264
+ }
265
+ static heading(decodeResult, value) {
266
+ decodeResult.raw.heading = value;
267
+ decodeResult.formatted.items.push({
268
+ type: "heading",
269
+ code: "HDG",
270
+ label: "Heading",
271
+ value: `${decodeResult.raw.heading}`
272
+ });
273
+ }
274
+ static tail(decodeResult, value) {
275
+ decodeResult.raw.tail = value;
276
+ decodeResult.formatted.items.push({
277
+ type: "tail",
278
+ code: "TAIL",
279
+ label: "Tail",
280
+ value: decodeResult.raw.tail
281
+ });
282
+ }
283
+ static out(decodeResult, time) {
284
+ decodeResult.raw.out_time = time;
285
+ const date = new Date(time * 1e3);
286
+ decodeResult.formatted.items.push({
287
+ type: "time_of_day",
288
+ code: "OUT",
289
+ label: "Out of Gate Time",
290
+ value: date.toISOString().slice(11, 19)
291
+ });
292
+ }
293
+ static off(decodeResult, time) {
294
+ decodeResult.raw.off_time = time;
295
+ const date = new Date(time * 1e3);
296
+ decodeResult.formatted.items.push({
297
+ type: "time_of_day",
298
+ code: "OFF",
299
+ label: "Takeoff Time",
300
+ value: date.toISOString().slice(11, 19)
301
+ });
302
+ }
303
+ static on(decodeResult, time) {
304
+ decodeResult.raw.on_time = time;
305
+ const date = new Date(time * 1e3);
306
+ decodeResult.formatted.items.push({
307
+ type: "time_of_day",
308
+ code: "ON",
309
+ label: "Landing Time",
310
+ value: date.toISOString().slice(11, 19)
311
+ });
312
+ }
313
+ static in(decodeResult, time) {
314
+ decodeResult.raw.in_time = time;
315
+ const date = new Date(time * 1e3);
316
+ decodeResult.formatted.items.push({
317
+ type: "time_of_day",
318
+ code: "IN",
319
+ label: "In Gate Time",
320
+ value: date.toISOString().slice(11, 19)
321
+ });
322
+ }
323
+ static unknown(decodeResult, value) {
324
+ decodeResult.remaining.text += "," + value;
325
+ }
326
+ };
327
+
63
328
  // lib/plugins/Label_5Z.ts
64
329
  var Label_5Z = class extends DecoderPlugin {
65
330
  name = "label-5z";
@@ -119,34 +384,9 @@ var Label_5Z = class extends DecoderPlugin {
119
384
  const rdcRegex = /^(?<from>\w\w\w)(?<to>\w\w\w) (?<unknown1>\d\d) R(?<runway>.+) G(?<unknown2>.+)$/;
120
385
  results = remainder.match(rdcRegex);
121
386
  if (results) {
122
- decodeResult.raw.origin = results.groups.from;
123
- decodeResult.formatted.items.push({
124
- type: "origin",
125
- label: "Origin",
126
- value: `${results.groups.from}`
127
- });
128
- decodeResult.raw.destination = results.groups.to;
129
- decodeResult.formatted.items.push({
130
- type: "destination",
131
- label: "Destination",
132
- value: `${results.groups.to}`
133
- });
134
- decodeResult.formatted.items.push({
135
- type: "unknown1",
136
- label: "Unknown Field 1",
137
- value: `${results.groups.unknown1}`
138
- });
139
- decodeResult.raw.runway = results.groups.runway;
140
- decodeResult.formatted.items.push({
141
- type: "runway",
142
- label: "Runway",
143
- value: `${results.groups.runway}`
144
- });
145
- decodeResult.formatted.items.push({
146
- type: "unknown2",
147
- label: "Unknown Field 2",
148
- value: `${results.groups.unknown2}`
149
- });
387
+ ResultFormatter.departureAirport(decodeResult, results.groups.from);
388
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.to);
389
+ ResultFormatter.arrivalRunway(decodeResult, results.groups.runway);
150
390
  } else {
151
391
  if (options.debug) {
152
392
  console.log(`Decoder: Unkown 5Z RDC format: ${remainder}`);
@@ -169,62 +409,278 @@ var Label_5Z = class extends DecoderPlugin {
169
409
  }
170
410
  };
171
411
 
172
- // lib/utils/coordinate_utils.ts
173
- var CoordinateUtils = class {
412
+ // lib/plugins/Label_10_LDR.ts
413
+ var Label_10_LDR = class extends DecoderPlugin {
414
+ // eslint-disable-line camelcase
415
+ name = "label-10-ldr";
416
+ qualifiers() {
417
+ return {
418
+ labels: ["10"],
419
+ preambles: ["LDR"]
420
+ };
421
+ }
422
+ decode(message, options = {}) {
423
+ const decodeResult = this.defaultResult();
424
+ decodeResult.decoder.name = this.name;
425
+ decodeResult.formatted.description = "Position Report";
426
+ decodeResult.message = message;
427
+ const parts = message.text.split(",");
428
+ if (parts.length < 17) {
429
+ if (options.debug) {
430
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
431
+ }
432
+ decodeResult.remaining.text = message.text;
433
+ decodeResult.decoded = false;
434
+ decodeResult.decoder.decodeLevel = "none";
435
+ return decodeResult;
436
+ }
437
+ const lat = parts[5];
438
+ const lon = parts[6];
439
+ const position = {
440
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1).trim()),
441
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim())
442
+ };
443
+ ResultFormatter.position(decodeResult, position);
444
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
445
+ ResultFormatter.departureAirport(decodeResult, parts[9]);
446
+ ResultFormatter.arrivalAirport(decodeResult, parts[10]);
447
+ ResultFormatter.alternateAirport(decodeResult, parts[11]);
448
+ ResultFormatter.arrivalRunway(decodeResult, parts[12].split("/")[0]);
449
+ const altRwy = [parts[13].split("/")[0], parts[14].split("/")[0]].filter((r) => r != "").join(",");
450
+ if (altRwy != "") {
451
+ ResultFormatter.alternateRunway(decodeResult, altRwy);
452
+ }
453
+ decodeResult.remaining.text = [...parts.slice(0, 5), ...parts.slice(15)].join(",");
454
+ decodeResult.decoded = true;
455
+ decodeResult.decoder.decodeLevel = "partial";
456
+ return decodeResult;
457
+ }
458
+ };
459
+
460
+ // lib/plugins/Label_10_POS.ts
461
+ var Label_10_POS = class extends DecoderPlugin {
462
+ // eslint-disable-line camelcase
463
+ name = "label-10-pos";
464
+ qualifiers() {
465
+ return {
466
+ labels: ["10"],
467
+ preambles: ["POS"]
468
+ };
469
+ }
470
+ decode(message, options = {}) {
471
+ const decodeResult = this.defaultResult();
472
+ decodeResult.decoder.name = this.name;
473
+ decodeResult.formatted.description = "Position Report";
474
+ decodeResult.message = message;
475
+ const parts = message.text.split(",");
476
+ if (parts.length !== 12) {
477
+ if (options.debug) {
478
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
479
+ }
480
+ decodeResult.remaining.text = message.text;
481
+ decodeResult.decoded = false;
482
+ decodeResult.decoder.decodeLevel = "none";
483
+ return decodeResult;
484
+ }
485
+ const lat = parts[1].trim();
486
+ const lon = parts[2].trim();
487
+ const position = {
488
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1).trim()) / 100,
489
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim()) / 100
490
+ };
491
+ ResultFormatter.position(decodeResult, position);
492
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
493
+ decodeResult.remaining.text = [parts[0], ...parts.slice(3, 7), ...parts.slice(8)].join(",");
494
+ decodeResult.decoded = true;
495
+ decodeResult.decoder.decodeLevel = "partial";
496
+ return decodeResult;
497
+ }
498
+ };
499
+
500
+ // lib/DateTimeUtils.ts
501
+ var DateTimeUtils = class {
502
+ // Expects a four digit UTC time string (HHMM)
503
+ static UTCToString(UTCString) {
504
+ let utcDate = /* @__PURE__ */ new Date();
505
+ utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
506
+ return utcDate.toTimeString();
507
+ }
508
+ // Expects a six digit date string and a four digit UTC time string
509
+ // (DDMMYY) (HHMM)
510
+ static UTCDateTimeToString(dateString, timeString) {
511
+ let utcDate = /* @__PURE__ */ new Date();
512
+ utcDate.setUTCDate(+dateString.substr(0, 2));
513
+ utcDate.setUTCMonth(+dateString.substr(2, 2));
514
+ if (dateString.length === 6) {
515
+ utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
516
+ }
517
+ if (timeString.length === 6) {
518
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
519
+ } else {
520
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
521
+ }
522
+ return utcDate.toUTCString();
523
+ }
174
524
  /**
175
- * Decode a string of coordinates into an object with latitude and longitude in millidegrees
176
- * @param stringCoords - The string of coordinates to decode
177
525
  *
178
- * @returns An object with latitude and longitude properties
526
+ * @param time HHMMSS
527
+ * @returns seconds since midnight
179
528
  */
180
- static decodeStringCoordinates(stringCoords) {
181
- var results = {};
182
- const firstChar = stringCoords.substring(0, 1);
183
- let middleChar = stringCoords.substring(6, 7);
184
- let longitudeChars = stringCoords.substring(7, 13);
185
- if (middleChar == " ") {
186
- middleChar = stringCoords.substring(7, 8);
187
- longitudeChars = stringCoords.substring(8, 14);
529
+ static convertHHMMSSToTod(time) {
530
+ const h = Number(time.substring(0, 2));
531
+ const m = Number(time.substring(2, 4));
532
+ const s = Number(time.substring(4, 6));
533
+ const tod = h * 3600 + m * 60 + s;
534
+ return tod;
535
+ }
536
+ /**
537
+ *
538
+ * @param time HHMMSS
539
+ * @param date MMDDYY or MMDDYYYY
540
+ * @returns seconds since epoch
541
+ */
542
+ static convertDateTimeToEpoch(time, date) {
543
+ if (date.length === 6) {
544
+ date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
188
545
  }
189
- if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
190
- results.latitude = Number(stringCoords.substring(1, 6)) / 1e3 * (firstChar === "S" ? -1 : 1);
191
- results.longitude = Number(longitudeChars) / 1e3 * (middleChar === "W" ? -1 : 1);
192
- } else {
193
- return;
546
+ const timestamp = `${date.substring(4, 8)}-${date.substring(0, 2)}-${date.substring(2, 4)}T${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}.000Z`;
547
+ const millis = Date.parse(timestamp);
548
+ return millis / 1e3;
549
+ }
550
+ };
551
+
552
+ // lib/utils/route_utils.ts
553
+ var RouteUtils = class _RouteUtils {
554
+ static routeToString(route) {
555
+ let str = "";
556
+ if (route.name) {
557
+ str += route.name;
558
+ }
559
+ if (route.runway) {
560
+ str += `(${route.runway})`;
561
+ }
562
+ if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
563
+ str += " starting at ";
564
+ } else if (str.length !== 0 && route.waypoints) {
565
+ str += ": ";
566
+ }
567
+ if (route.waypoints) {
568
+ str += _RouteUtils.waypointsToString(route.waypoints);
569
+ }
570
+ return str;
571
+ }
572
+ static waypointToString(waypoint) {
573
+ let s = waypoint.name;
574
+ if (waypoint.latitude && waypoint.longitude) {
575
+ s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
576
+ }
577
+ if (waypoint.offset) {
578
+ s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
579
+ }
580
+ if (waypoint.time && waypoint.timeFormat) {
581
+ s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
582
+ }
583
+ return s;
584
+ }
585
+ static getWaypoint(leg) {
586
+ const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
587
+ if (regex?.length == 4) {
588
+ return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
589
+ }
590
+ const waypoint = leg.split(",");
591
+ if (waypoint.length == 2) {
592
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
593
+ if (position) {
594
+ return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
595
+ }
596
+ }
597
+ if (leg.length == 13 || leg.length == 14) {
598
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
599
+ const name = waypoint.length == 2 ? waypoint[0] : "";
600
+ if (position) {
601
+ return { name, latitude: position.latitude, longitude: position.longitude };
602
+ }
603
+ }
604
+ return { name: leg };
605
+ }
606
+ // move out if we want public
607
+ static timestampToString(time, format) {
608
+ const date = new Date(time * 1e3);
609
+ if (format == "tod") {
610
+ return date.toISOString().slice(11, 19);
611
+ }
612
+ return date.toISOString().slice(0, -5) + "Z";
613
+ }
614
+ static waypointsToString(waypoints) {
615
+ let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
616
+ if (str.startsWith(" > ")) {
617
+ str = ">>" + str.slice(2);
194
618
  }
195
- return results;
619
+ return str;
196
620
  }
197
- /**
198
- * Decode a string of coordinates into an object with latitude and longitude in degrees and decimal minutes
199
- * @param stringCoords - The string of coordinates to decode
200
- *
201
- * @returns An object with latitude and longitude properties
202
- */
203
- static decodeStringCoordinatesDecimalMinutes(stringCoords) {
204
- var results = {};
205
- const firstChar = stringCoords.substring(0, 1);
206
- let middleChar = stringCoords.substring(6, 7);
207
- let longitudeChars = stringCoords.substring(7, 13);
208
- if (middleChar == " ") {
209
- middleChar = stringCoords.substring(7, 8);
210
- longitudeChars = stringCoords.substring(8, 14);
621
+ };
622
+
623
+ // lib/plugins/Label_10_Slash.ts
624
+ var Label_10_Slash = class extends DecoderPlugin {
625
+ // eslint-disable-line camelcase
626
+ name = "label-10-slash";
627
+ qualifiers() {
628
+ return {
629
+ labels: ["10"],
630
+ preambles: ["/"]
631
+ };
632
+ }
633
+ decode(message, options = {}) {
634
+ const decodeResult = this.defaultResult();
635
+ decodeResult.decoder.name = this.name;
636
+ decodeResult.formatted.description = "Position Report";
637
+ decodeResult.message = message;
638
+ const parts = message.text.split("/");
639
+ if (parts.length < 17) {
640
+ if (options.debug) {
641
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
642
+ }
643
+ decodeResult.remaining.text = message.text;
644
+ decodeResult.decoded = false;
645
+ decodeResult.decoder.decodeLevel = "none";
646
+ return decodeResult;
211
647
  }
212
- const latDeg = Math.trunc(Number(stringCoords.substring(1, 6)) / 1e3);
213
- const latMin = Number(stringCoords.substring(1, 6)) % 1e3 / 10;
214
- const lonDeg = Math.trunc(Number(longitudeChars) / 1e3);
215
- const lonMin = Number(longitudeChars) % 1e3 / 10;
216
- if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
217
- results.latitude = (latDeg + latMin / 60) * (firstChar === "S" ? -1 : 1);
218
- results.longitude = (lonDeg + lonMin / 60) * (middleChar === "W" ? -1 : 1);
219
- } else {
220
- return;
648
+ const lat = parts[1];
649
+ const lon = parts[2];
650
+ const position = {
651
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1)),
652
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1))
653
+ };
654
+ ResultFormatter.position(decodeResult, position);
655
+ ResultFormatter.heading(decodeResult, Number(parts[5]));
656
+ ResultFormatter.altitude(decodeResult, 100 * Number(parts[6]));
657
+ ResultFormatter.arrivalAirport(decodeResult, parts[7]);
658
+ ResultFormatter.eta(decodeResult, parts[8] + "00");
659
+ const waypoints = [{
660
+ name: parts[11]
661
+ }, {
662
+ name: parts[12],
663
+ time: DateTimeUtils.convertHHMMSSToTod(parts[13] + "00"),
664
+ timeFormat: "tod"
665
+ }, {
666
+ name: parts[14],
667
+ time: DateTimeUtils.convertHHMMSSToTod(parts[15] + "00"),
668
+ timeFormat: "tod"
669
+ }];
670
+ decodeResult.raw.route = { waypoints };
671
+ decodeResult.formatted.items.push({
672
+ type: "aircraft_route",
673
+ code: "ROUTE",
674
+ label: "Aircraft Route",
675
+ value: RouteUtils.routeToString(decodeResult.raw.route)
676
+ });
677
+ if (parts[16]) {
678
+ ResultFormatter.departureAirport(decodeResult, parts[16]);
221
679
  }
222
- return results;
223
- }
224
- static coordinateString(coords) {
225
- const latDir = coords.latitude > 0 ? "N" : "S";
226
- const lonDir = coords.longitude > 0 ? "E" : "W";
227
- return `${Math.abs(coords.latitude).toFixed(3)} ${latDir}, ${Math.abs(coords.longitude).toFixed(3)} ${lonDir}`;
680
+ decodeResult.remaining.text = [parts[3], parts[4], ...parts.slice(9, 11), ...parts.slice(17)].join("/");
681
+ decodeResult.decoded = true;
682
+ decodeResult.decoder.decodeLevel = "partial";
683
+ return decodeResult;
228
684
  }
229
685
  };
230
686
 
@@ -253,19 +709,14 @@ var Label_12_N_Space = class extends DecoderPlugin {
253
709
  latitude: Number(results.groups.lat_coord) * (results.groups.lat == "N" ? 1 : -1),
254
710
  longitude: Number(results.groups.long_coord) * (results.groups.long == "E" ? 1 : -1)
255
711
  };
256
- decodeResult.raw.flight_level = results.groups.alt == "GRD" || results.groups.alt == "***" ? "0" : Number(results.groups.alt);
712
+ const altitude = results.groups.alt == "GRD" || results.groups.alt == "***" ? 0 : Number(results.groups.alt);
257
713
  decodeResult.formatted.items.push({
258
714
  type: "aircraft_position",
259
715
  code: "POS",
260
716
  label: "Aircraft Position",
261
717
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
262
718
  });
263
- decodeResult.formatted.items.push({
264
- type: "flight_level",
265
- code: "FL",
266
- label: "Flight Level",
267
- value: decodeResult.raw.flight_level
268
- });
719
+ ResultFormatter.altitude(decodeResult, altitude);
269
720
  decodeResult.remaining.text = `,${results.groups.unkwn1} ,${results.groups.unkwn2}, ${results.groups.unkwn3}`;
270
721
  decodeResult.decoded = true;
271
722
  decodeResult.decoder.decodeLevel = "partial";
@@ -281,6 +732,97 @@ var Label_12_N_Space = class extends DecoderPlugin {
281
732
  }
282
733
  };
283
734
 
735
+ // lib/plugins/Label_13Through18_Slash.ts
736
+ var Label_13Through18_Slash = class extends DecoderPlugin {
737
+ // eslint-disable-line camelcase
738
+ name = "label-13-18-slash";
739
+ qualifiers() {
740
+ return {
741
+ labels: ["13", "14", "15", "16", "17", "18"],
742
+ preambles: ["/"]
743
+ };
744
+ }
745
+ decode(message, options = {}) {
746
+ const decodeResult = this.defaultResult();
747
+ decodeResult.decoder.name = this.name;
748
+ decodeResult.message = message;
749
+ const lines = message.text.split("\r\n");
750
+ const parts = lines[0].split("/");
751
+ const labelNumber = Number(parts[1].substring(0, 2));
752
+ decodeResult.formatted.description = getMsgType(labelNumber);
753
+ if (labelNumber !== 18 && parts.length !== 4 || labelNumber === 18 && parts.length !== 7) {
754
+ if (options?.debug) {
755
+ console.log(`Decoder: Unknown OOOI message: ${message.text}`);
756
+ }
757
+ decodeResult.remaining.text = message.text;
758
+ decodeResult.decoded = false;
759
+ decodeResult.decoder.decodeLevel = "none";
760
+ return decodeResult;
761
+ }
762
+ decodeResult.remaining.text = "";
763
+ const data = parts[2].split(" ");
764
+ ResultFormatter.departureAirport(decodeResult, data[1]);
765
+ ResultFormatter.arrivalAirport(decodeResult, data[2]);
766
+ decodeResult.raw.day_of_month = Number(data[3]);
767
+ const time = DateTimeUtils.convertHHMMSSToTod(data[4]);
768
+ if (labelNumber === 13) {
769
+ ResultFormatter.out(decodeResult, time);
770
+ } else if (labelNumber === 14) {
771
+ ResultFormatter.off(decodeResult, time);
772
+ } else if (labelNumber === 15) {
773
+ ResultFormatter.on(decodeResult, time);
774
+ } else if (labelNumber === 16) {
775
+ ResultFormatter.in(decodeResult, time);
776
+ }
777
+ if (parts.length === 7) {
778
+ decodeResult.remaining.text += parts.slice(4).join("/");
779
+ }
780
+ for (let i = 1; i < lines.length; i++) {
781
+ if (lines[i].startsWith("/LOC")) {
782
+ const location = lines[i].substring(5).split(",");
783
+ let position;
784
+ if (location[0].startsWith("+") || location[0].startsWith("-")) {
785
+ position = { latitude: Number(location[0]), longitude: Number(location[1]) };
786
+ } else {
787
+ position = {
788
+ 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))),
789
+ 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)))
790
+ };
791
+ }
792
+ if (!isNaN(position.latitude) && !isNaN(position.longitude)) {
793
+ ResultFormatter.position(decodeResult, position);
794
+ }
795
+ } else {
796
+ decodeResult.remaining.text += "\r\n" + lines[i];
797
+ }
798
+ }
799
+ decodeResult.decoded = true;
800
+ decodeResult.decoder.decodeLevel = decodeResult.remaining.text ? "partial" : "full";
801
+ return decodeResult;
802
+ }
803
+ };
804
+ function getMsgType(labelNumber) {
805
+ if (labelNumber === 13) {
806
+ return "Out of Gate Report";
807
+ }
808
+ if (labelNumber === 14) {
809
+ return "Takeoff Report";
810
+ }
811
+ if (labelNumber === 15) {
812
+ return "On Ground Report";
813
+ }
814
+ if (labelNumber === 16) {
815
+ return "In Gate Report";
816
+ }
817
+ if (labelNumber === 17) {
818
+ return "Post Report";
819
+ }
820
+ if (labelNumber === 18) {
821
+ return "Post Times Report";
822
+ }
823
+ return "Unknown";
824
+ }
825
+
284
826
  // lib/plugins/Label_15.ts
285
827
  var Label_15 = class extends DecoderPlugin {
286
828
  name = "label-5z";
@@ -327,39 +869,35 @@ var Label_15_FST = class extends DecoderPlugin {
327
869
  decodeResult.decoder.name = this.name;
328
870
  decodeResult.formatted.description = "Position Report";
329
871
  decodeResult.message = message;
330
- decodeResult.raw.departure_icao = message.text.substring(5, 9);
331
- decodeResult.raw.arrival_icao = message.text.substring(9, 13);
332
- const stringCoords = message.text.substring(13);
872
+ const parts = message.text.split(" ");
873
+ const header = parts[0];
874
+ decodeResult.raw.departure_icao = header.substring(5, 9);
875
+ decodeResult.raw.arrival_icao = header.substring(9, 13);
876
+ const stringCoords = header.substring(13);
333
877
  const firstChar = stringCoords.substring(0, 1);
334
878
  const middleChar = stringCoords.substring(7, 8);
335
879
  decodeResult.raw.position = {};
336
880
  if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
337
881
  decodeResult.raw.position.latitude = Number(stringCoords.substring(1, 7)) / 1e4 * (firstChar === "S" ? -1 : 1);
338
- decodeResult.raw.position.longitude = Number(stringCoords.substring(8, 26)) / 1e5 * (middleChar === "W" ? -1 : 1);
882
+ decodeResult.raw.position.longitude = Number(stringCoords.substring(8, 15)) / 1e4 * (middleChar === "W" ? -1 : 1);
883
+ decodeResult.raw.altitude = Number(stringCoords.substring(15)) * 100;
339
884
  } else {
340
885
  decodeResult.decoded = false;
341
886
  decodeResult.decoder.decodeLevel = "none";
342
887
  return decodeResult;
343
888
  }
344
889
  decodeResult.formatted.items.push({
345
- type: "position",
346
- label: "Position",
890
+ type: "aircraft_position",
891
+ code: "POS",
892
+ label: "Aircraft Position",
347
893
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
348
894
  });
349
- decodeResult.formatted.items.push({
350
- type: "origin",
351
- code: "ORG",
352
- label: "Origin",
353
- value: decodeResult.raw.departure_icao
354
- });
355
- decodeResult.formatted.items.push({
356
- type: "destination",
357
- code: "DST",
358
- label: "Destination",
359
- value: decodeResult.raw.arrival_icao
360
- });
895
+ ResultFormatter.altitude(decodeResult, decodeResult.raw.altitude);
896
+ ResultFormatter.departureAirport(decodeResult, decodeResult.raw.departure_icao);
897
+ ResultFormatter.arrivalAirport(decodeResult, decodeResult.raw.arrival_icao);
898
+ decodeResult.remaining.text = parts.slice(1).join(" ");
361
899
  decodeResult.decoded = true;
362
- decodeResult.decoder.decodeLevel = "full";
900
+ decodeResult.decoder.decodeLevel = "partial";
363
901
  return decodeResult;
364
902
  }
365
903
  };
@@ -390,19 +928,14 @@ var Label_16_N_Space = class extends DecoderPlugin {
390
928
  latitude: Number(results.groups.lat_coord) * (results.groups.lat == "N" ? 1 : -1),
391
929
  longitude: Number(results.groups.long_coord) * (results.groups.long == "E" ? 1 : -1)
392
930
  };
393
- decodeResult.raw.flight_level = results.groups.alt == "GRD" || results.groups.alt == "***" ? "0" : Number(results.groups.alt);
931
+ const altitude = results.groups.alt == "GRD" || results.groups.alt == "***" ? 0 : Number(results.groups.alt);
394
932
  decodeResult.formatted.items.push({
395
933
  type: "aircraft_position",
396
934
  code: "POS",
397
935
  label: "Aircraft Position",
398
936
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
399
937
  });
400
- decodeResult.formatted.items.push({
401
- type: "flight_level",
402
- code: "FL",
403
- label: "Flight Level",
404
- value: decodeResult.raw.flight_level
405
- });
938
+ ResultFormatter.altitude(decodeResult, altitude);
406
939
  decodeResult.remaining.text = `,${results.groups.unkwn1} ,${results.groups.unkwn2}`;
407
940
  decodeResult.decoded = true;
408
941
  decodeResult.decoder.decodeLevel = "partial";
@@ -435,58 +968,6 @@ var Label_16_N_Space = class extends DecoderPlugin {
435
968
  }
436
969
  };
437
970
 
438
- // lib/DateTimeUtils.ts
439
- var DateTimeUtils = class {
440
- // Expects a four digit UTC time string (HHMM)
441
- static UTCToString(UTCString) {
442
- let utcDate = /* @__PURE__ */ new Date();
443
- utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
444
- return utcDate.toTimeString();
445
- }
446
- // Expects a six digit date string and a four digit UTC time string
447
- // (DDMMYY) (HHMM)
448
- static UTCDateTimeToString(dateString, timeString) {
449
- let utcDate = /* @__PURE__ */ new Date();
450
- utcDate.setUTCDate(+dateString.substr(0, 2));
451
- utcDate.setUTCMonth(+dateString.substr(2, 2));
452
- if (dateString.length === 6) {
453
- utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
454
- }
455
- if (timeString.length === 6) {
456
- utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
457
- } else {
458
- utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
459
- }
460
- return utcDate.toUTCString();
461
- }
462
- /**
463
- *
464
- * @param time HHMMSS
465
- * @returns seconds since midnight
466
- */
467
- static convertHHMMSSToTod(time) {
468
- const h = Number(time.substring(0, 2));
469
- const m = Number(time.substring(2, 4));
470
- const s = Number(time.substring(4, 6));
471
- const tod = h * 3600 + m * 60 + s;
472
- return tod;
473
- }
474
- /**
475
- *
476
- * @param time HHMMSS
477
- * @param date MMDDYY or MMDDYYYY
478
- * @returns seconds since epoch
479
- */
480
- static convertDateTimeToEpoch(time, date) {
481
- if (date.length === 6) {
482
- date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
483
- }
484
- 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`;
485
- const millis = Date.parse(timestamp);
486
- return millis / 1e3;
487
- }
488
- };
489
-
490
971
  // lib/plugins/Label_1M_Slash.ts
491
972
  var Label_1M_Slash = class extends DecoderPlugin {
492
973
  name = "label-1m-slash";
@@ -553,16 +1034,17 @@ var Label_20_POS = class extends DecoderPlugin {
553
1034
  decodeResult.message = message;
554
1035
  decodeResult.raw.preamble = message.text.substring(0, 3);
555
1036
  const content = message.text.substring(3);
556
- console.log("Content: " + content);
557
1037
  const fields = content.split(",");
558
- console.log("Field Count: " + fields.length);
559
1038
  if (fields.length == 11) {
560
- console.log(`DEBUG: ${this.name}: Variation 1 detected`);
1039
+ if (options.debug) {
1040
+ console.log(`DEBUG: ${this.name}: Variation 1 detected`);
1041
+ }
561
1042
  const rawCoords = fields[0];
562
1043
  decodeResult.raw.position = CoordinateUtils.decodeStringCoordinates(rawCoords);
563
1044
  if (decodeResult.raw.position) {
564
1045
  decodeResult.formatted.items.push({
565
1046
  type: "position",
1047
+ code: "POS",
566
1048
  label: "Position",
567
1049
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
568
1050
  });
@@ -570,12 +1052,15 @@ var Label_20_POS = class extends DecoderPlugin {
570
1052
  decodeResult.decoded = true;
571
1053
  decodeResult.decoder.decodeLevel = "full";
572
1054
  } else if (fields.length == 5) {
573
- console.log(`DEBUG: ${this.name}: Variation 2 detected`);
1055
+ if (options.debug) {
1056
+ console.log(`DEBUG: ${this.name}: Variation 2 detected`);
1057
+ }
574
1058
  const rawCoords = fields[0];
575
1059
  decodeResult.raw.position = CoordinateUtils.decodeStringCoordinates(rawCoords);
576
1060
  if (decodeResult.raw.position) {
577
1061
  decodeResult.formatted.items.push({
578
1062
  type: "position",
1063
+ code: "POS",
579
1064
  label: "Position",
580
1065
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
581
1066
  });
@@ -583,7 +1068,9 @@ var Label_20_POS = class extends DecoderPlugin {
583
1068
  decodeResult.decoded = true;
584
1069
  decodeResult.decoder.decodeLevel = "full";
585
1070
  } else {
586
- console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`);
1071
+ if (options.debug) {
1072
+ console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`);
1073
+ }
587
1074
  decodeResult.decoded = false;
588
1075
  decodeResult.decoder.decodeLevel = "none";
589
1076
  }
@@ -607,15 +1094,13 @@ var Label_21_POS = class extends DecoderPlugin {
607
1094
  decodeResult.message = message;
608
1095
  decodeResult.raw.preamble = message.text.substring(0, 3);
609
1096
  const content = message.text.substring(3);
610
- console.log("Content: " + content);
611
1097
  const fields = content.split(",");
612
- console.log("Field Count: " + fields.length);
613
1098
  if (fields.length == 9) {
614
1099
  processPosition(decodeResult, fields[0].trim());
615
1100
  processAlt(decodeResult, fields[3]);
616
1101
  processTemp(decodeResult, fields[6]);
617
1102
  processArrvApt(decodeResult, fields[8]);
618
- decodeResult.raw.remaining = [
1103
+ decodeResult.remaining.text = [
619
1104
  fields[1],
620
1105
  fields[2],
621
1106
  fields[4],
@@ -625,7 +1110,9 @@ var Label_21_POS = class extends DecoderPlugin {
625
1110
  decodeResult.decoded = true;
626
1111
  decodeResult.decoder.decodeLevel = "partial";
627
1112
  } else {
628
- console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`);
1113
+ if (options.debug) {
1114
+ console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`);
1115
+ }
629
1116
  decodeResult.decoded = false;
630
1117
  decodeResult.decoder.decodeLevel = "none";
631
1118
  }
@@ -979,9 +1466,7 @@ var Label_44_POS = class extends DecoderPlugin {
979
1466
  console.log(results.groups);
980
1467
  }
981
1468
  decodeResult.raw.position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(results.groups.unsplit_coords);
982
- 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);
983
- decodeResult.raw.departure_icao = results.groups.departure_icao;
984
- decodeResult.raw.arrival_icao = results.groups.arrival_icao;
1469
+ const flight_level = results.groups.flight_level_or_ground == "GRD" || results.groups.flight_level_or_ground == "***" ? 0 : Number(results.groups.flight_level_or_ground);
985
1470
  decodeResult.raw.current_time = Date.parse(
986
1471
  (/* @__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"
987
1472
  );
@@ -999,24 +1484,9 @@ var Label_44_POS = class extends DecoderPlugin {
999
1484
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
1000
1485
  });
1001
1486
  }
1002
- decodeResult.formatted.items.push({
1003
- type: "origin",
1004
- code: "ORG",
1005
- label: "Origin",
1006
- value: decodeResult.raw.departure_icao
1007
- });
1008
- decodeResult.formatted.items.push({
1009
- type: "destination",
1010
- code: "DST",
1011
- label: "Destination",
1012
- value: decodeResult.raw.arrival_icao
1013
- });
1014
- decodeResult.formatted.items.push({
1015
- type: "flight_level",
1016
- code: "FL",
1017
- label: "Flight Level",
1018
- value: decodeResult.raw.flight_level
1019
- });
1487
+ ResultFormatter.departureAirport(decodeResult, results.groups.departure_icao);
1488
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
1489
+ ResultFormatter.altitude(decodeResult, flight_level * 100);
1020
1490
  }
1021
1491
  decodeResult.decoded = true;
1022
1492
  decodeResult.decoder.decodeLevel = "full";
@@ -1024,6 +1494,64 @@ var Label_44_POS = class extends DecoderPlugin {
1024
1494
  }
1025
1495
  };
1026
1496
 
1497
+ // lib/plugins/Label_4N.ts
1498
+ var Label_4N = class extends DecoderPlugin {
1499
+ name = "label-4n";
1500
+ qualifiers() {
1501
+ return {
1502
+ labels: ["4N"]
1503
+ };
1504
+ }
1505
+ decode(message, options = {}) {
1506
+ const decodeResult = this.defaultResult();
1507
+ decodeResult.decoder.name = this.name;
1508
+ decodeResult.message = message;
1509
+ decodeResult.formatted.description = "Airline Defined";
1510
+ let text = message.text;
1511
+ if (text.match(/^M\d{2}A\w{6}/)) {
1512
+ ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1513
+ text = text.substring(10);
1514
+ }
1515
+ decodeResult.decoded = true;
1516
+ const fields = text.split(",");
1517
+ if (text.length === 51) {
1518
+ decodeResult.raw.day_of_month = text.substring(0, 2);
1519
+ ResultFormatter.departureAirport(decodeResult, text.substring(8, 11));
1520
+ ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16));
1521
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(30, 45).replace(/^(.)0/, "$1")));
1522
+ ResultFormatter.altitude(decodeResult, text.substring(48, 51) * 100);
1523
+ decodeResult.remaining.text = [text.substring(2, 4), text.substring(19, 29)].join(" ");
1524
+ } else if (fields.length === 33) {
1525
+ decodeResult.raw.date = fields[3];
1526
+ if (fields[1] === "B") {
1527
+ ResultFormatter.position(decodeResult, { latitude: Number(fields[4]), longitude: Number(fields[5]) });
1528
+ ResultFormatter.altitude(decodeResult, fields[6]);
1529
+ }
1530
+ ResultFormatter.departureAirport(decodeResult, fields[8]);
1531
+ ResultFormatter.arrivalAirport(decodeResult, fields[9]);
1532
+ ResultFormatter.alternateAirport(decodeResult, fields[10]);
1533
+ ResultFormatter.arrivalRunway(decodeResult, fields[11].split("/")[0]);
1534
+ if (fields[12].length > 1) {
1535
+ ResultFormatter.alternateRunway(decodeResult, fields[12].split("/")[0]);
1536
+ }
1537
+ ResultFormatter.checksum(decodeResult, fields[32]);
1538
+ decodeResult.remaining.text = [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter((f) => f != "").join(",");
1539
+ } else {
1540
+ decodeResult.decoded = false;
1541
+ decodeResult.remaining.text = text;
1542
+ }
1543
+ if (decodeResult.decoded) {
1544
+ if (decodeResult.remaining.text === "")
1545
+ decodeResult.decoder.decodeLevel = "full";
1546
+ else
1547
+ decodeResult.decoder.decodeLevel = "partial";
1548
+ } else {
1549
+ decodeResult.decoder.decodeLevel = "none";
1550
+ }
1551
+ return decodeResult;
1552
+ }
1553
+ };
1554
+
1027
1555
  // lib/plugins/Label_80.ts
1028
1556
  var Label_80 = class extends DecoderPlugin {
1029
1557
  name = "label-80";
@@ -1055,27 +1583,15 @@ var Label_80 = class extends DecoderPlugin {
1055
1583
  const parts = message.text.split("\n");
1056
1584
  let posRptRegex = /^3N01 POSRPT \d\d\d\d\/\d\d (?<orig>\w+)\/(?<dest>\w+) \.(?<tail>[\w-]+)(\/(?<agate>.+) (?<sta>\w+:\w+))*/;
1057
1585
  let results = parts[0].match(posRptRegex);
1586
+ if (!results?.groups) {
1587
+ decodeResult.decoded = false;
1588
+ decodeResult.decoder.decodeLevel = "none";
1589
+ return decodeResult;
1590
+ }
1058
1591
  if (results && results.length > 0) {
1059
- decodeResult.raw.origin = results.groups.orig;
1060
- decodeResult.formatted.items.push({
1061
- type: "origin",
1062
- code: "ORG",
1063
- label: "Origin",
1064
- value: `${results.groups.orig}`
1065
- });
1066
- decodeResult.raw.destination = results.groups.dest;
1067
- decodeResult.formatted.items.push({
1068
- type: "destination",
1069
- code: "DST",
1070
- label: "Destination",
1071
- value: `${results.groups.dest}`
1072
- });
1073
- decodeResult.raw.tail = results.groups.tail;
1074
- decodeResult.formatted.items.push({
1075
- type: "tail",
1076
- label: "Tail",
1077
- value: `${results.groups.tail}`
1078
- });
1592
+ ResultFormatter.departureAirport(decodeResult, results.groups.orig);
1593
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.dest);
1594
+ ResultFormatter.tail(decodeResult, results.groups.tail);
1079
1595
  if (results.groups.agate) {
1080
1596
  decodeResult.raw.arrival_gate = results.groups.agate;
1081
1597
  decodeResult.formatted.items.push({
@@ -1097,15 +1613,9 @@ var Label_80 = class extends DecoderPlugin {
1097
1613
  for (const part of remainingParts) {
1098
1614
  const matches = part.matchAll(posRptRegex);
1099
1615
  for (const match of matches) {
1100
- switch (match.groups.field) {
1616
+ switch (match.groups?.field) {
1101
1617
  case "ALT": {
1102
- decodeResult.raw.altitude = match.groups.value;
1103
- decodeResult.formatted.items.push({
1104
- type: "altitude",
1105
- code: "ALT",
1106
- label: this.descriptions[match.groups.field],
1107
- value: `${decodeResult.raw.altitude} feet`
1108
- });
1618
+ ResultFormatter.altitude(decodeResult, Number(match.groups.value));
1109
1619
  break;
1110
1620
  }
1111
1621
  case "DWND": {
@@ -1119,13 +1629,8 @@ var Label_80 = class extends DecoderPlugin {
1119
1629
  break;
1120
1630
  }
1121
1631
  case "FL": {
1122
- decodeResult.raw.flight_level = match.groups.value;
1123
- decodeResult.formatted.items.push({
1124
- type: "flight_level",
1125
- code: "FL",
1126
- label: this.descriptions[match.groups.field],
1127
- value: decodeResult.raw.flight_level
1128
- });
1632
+ const flight_level = Number(match.groups.value);
1633
+ ResultFormatter.altitude(decodeResult, flight_level * 100);
1129
1634
  break;
1130
1635
  }
1131
1636
  case "FOB": {
@@ -1149,7 +1654,7 @@ var Label_80 = class extends DecoderPlugin {
1149
1654
  break;
1150
1655
  }
1151
1656
  case "MCH": {
1152
- decodeResult.raw.mach = match.groups.value / 1e3;
1657
+ decodeResult.raw.mach = Number(match.groups.value) / 1e3;
1153
1658
  decodeResult.formatted.items.push({
1154
1659
  type: "mach",
1155
1660
  code: "MCH",
@@ -1171,20 +1676,13 @@ var Label_80 = class extends DecoderPlugin {
1171
1676
  case "POS": {
1172
1677
  const posRegex = /^(?<latd>[NS])(?<lat>.+)(?<lngd>[EW])(?<lng>.+)/;
1173
1678
  const posResult = match.groups.value.match(posRegex);
1174
- const lat = Number(posResult.groups.lat) * (posResult.groups.lngd === "S" ? -1 : 1);
1175
- const lon = Number(posResult.groups.lng) * (posResult.groups.lngd === "W" ? -1 : 1);
1176
- const latitude = Number.isInteger(lat) ? lat / 1e3 : lat / 100;
1177
- const longitude = Number.isInteger(lon) ? lon / 1e3 : lon / 100;
1178
- decodeResult.raw.position = {
1179
- latitude,
1180
- longitude
1679
+ const lat = Number(posResult?.groups?.lat) * (posResult?.groups?.lngd === "S" ? -1 : 1);
1680
+ const lon = Number(posResult?.groups?.lng) * (posResult?.groups?.lngd === "W" ? -1 : 1);
1681
+ const position = {
1682
+ latitude: Number.isInteger(lat) ? lat / 1e3 : lat / 100,
1683
+ longitude: Number.isInteger(lon) ? lon / 1e3 : lon / 100
1181
1684
  };
1182
- decodeResult.formatted.items.push({
1183
- type: "position",
1184
- code: "POS",
1185
- label: "Position",
1186
- value: CoordinateUtils.coordinateString(decodeResult.raw.position)
1187
- });
1685
+ ResultFormatter.position(decodeResult, position);
1188
1686
  break;
1189
1687
  }
1190
1688
  case "SWND": {
@@ -1198,7 +1696,7 @@ var Label_80 = class extends DecoderPlugin {
1198
1696
  break;
1199
1697
  }
1200
1698
  default: {
1201
- if (match.groups.field != void 0) {
1699
+ if (match.groups?.field != void 0) {
1202
1700
  const description = this.descriptions[match.groups.field] ? this.descriptions[match.groups.field] : "Unknown";
1203
1701
  decodeResult.formatted.items.push({
1204
1702
  type: match.groups.field,
@@ -1209,10 +1707,80 @@ var Label_80 = class extends DecoderPlugin {
1209
1707
  }
1210
1708
  }
1211
1709
  }
1212
- }
1710
+ }
1711
+ }
1712
+ decodeResult.decoded = true;
1713
+ decodeResult.decoder.decodeLevel = "partial";
1714
+ }
1715
+ return decodeResult;
1716
+ }
1717
+ };
1718
+
1719
+ // lib/plugins/Label_83.ts
1720
+ var Label_83 = class extends DecoderPlugin {
1721
+ name = "label-83";
1722
+ qualifiers() {
1723
+ return {
1724
+ labels: ["83"]
1725
+ };
1726
+ }
1727
+ decode(message, options = {}) {
1728
+ const decodeResult = this.defaultResult();
1729
+ decodeResult.decoder.name = this.name;
1730
+ decodeResult.message = message;
1731
+ decodeResult.formatted.description = "Airline Defined";
1732
+ let text = message.text;
1733
+ if (text.match(/^M\d{2}A\w{6}/)) {
1734
+ ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
1735
+ text = text.substring(10);
1736
+ }
1737
+ decodeResult.decoded = true;
1738
+ if (text.substring(0, 10) === "4DH3 ETAT2") {
1739
+ const fields = text.split(/\s+/);
1740
+ if (fields[2].length > 5) {
1741
+ decodeResult.raw.day_of_month = fields[2].substring(5);
1742
+ }
1743
+ decodeResult.remaining.text = fields[2].substring(0, 4);
1744
+ const subfields = fields[3].split("/");
1745
+ ResultFormatter.departureAirport(decodeResult, subfields[0]);
1746
+ ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
1747
+ ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, ""));
1748
+ ResultFormatter.eta(decodeResult, fields[6] + "00");
1749
+ } else if (text.substring(0, 5) === "001PR") {
1750
+ decodeResult.raw.day_of_month = text.substring(5, 7);
1751
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(13, 28).replace(/\./g, "")));
1752
+ ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33)));
1753
+ decodeResult.remaining.text = text.substring(33);
1754
+ } else {
1755
+ const fields = text.replace(/\s/g, "").split(",");
1756
+ if (fields.length === 9) {
1757
+ ResultFormatter.departureAirport(decodeResult, fields[0]);
1758
+ ResultFormatter.arrivalAirport(decodeResult, fields[1]);
1759
+ decodeResult.raw.day_of_month = fields[2].substring(0, 2);
1760
+ decodeResult.raw.time = fields[2].substring(2);
1761
+ ResultFormatter.position(
1762
+ decodeResult,
1763
+ {
1764
+ latitude: Number(fields[3].replace(/\s/g, "")),
1765
+ longitude: Number(fields[4].replace(/\s/g, ""))
1766
+ }
1767
+ );
1768
+ ResultFormatter.altitude(decodeResult, fields[5]);
1769
+ ResultFormatter.groundspeed(decodeResult, fields[6]);
1770
+ ResultFormatter.heading(decodeResult, fields[7]);
1771
+ decodeResult.remaining.text = fields[8];
1772
+ } else {
1773
+ decodeResult.decoded = false;
1774
+ decodeResult.remaining.text = message.text;
1213
1775
  }
1214
- decodeResult.decoded = true;
1215
- decodeResult.decodeLevel = "partial";
1776
+ }
1777
+ if (decodeResult.decoded) {
1778
+ if (decodeResult.remaining.text === "")
1779
+ decodeResult.decoder.decodeLevel = "full";
1780
+ else
1781
+ decodeResult.decoder.decodeLevel = "partial";
1782
+ } else {
1783
+ decodeResult.decoder.decodeLevel = "none";
1216
1784
  }
1217
1785
  return decodeResult;
1218
1786
  }
@@ -1359,183 +1927,6 @@ function processUnknown(decodeResult, value) {
1359
1927
  decodeResult.remaining.text += value;
1360
1928
  }
1361
1929
 
1362
- // lib/utils/result_formatter.ts
1363
- var ResultFormatter = class {
1364
- static altitude(decodeResult, value) {
1365
- decodeResult.raw.altitude = value;
1366
- decodeResult.formatted.items.push({
1367
- type: "altitude",
1368
- code: "ALT",
1369
- label: "Altitude",
1370
- value: `${decodeResult.raw.altitude} feet`
1371
- });
1372
- }
1373
- static flightNumber(decodeResult, value) {
1374
- decodeResult.raw.flight_number = value;
1375
- }
1376
- static departureAirport(decodeResult, value) {
1377
- decodeResult.raw.departure_icao = value;
1378
- decodeResult.formatted.items.push({
1379
- type: "origin",
1380
- code: "ORG",
1381
- label: "Origin",
1382
- value: decodeResult.raw.departure_icao
1383
- });
1384
- }
1385
- static departureRunway(decodeResult, value) {
1386
- decodeResult.raw.departure_runway = value;
1387
- decodeResult.formatted.items.push({
1388
- type: "runway",
1389
- code: "DEPRWY",
1390
- label: "Departure Runway",
1391
- value: decodeResult.raw.departure_runway
1392
- });
1393
- }
1394
- static arrivalAirport(decodeResult, value) {
1395
- decodeResult.raw.arrival_icao = value;
1396
- decodeResult.formatted.items.push({
1397
- type: "destination",
1398
- code: "DST",
1399
- label: "Destination",
1400
- value: decodeResult.raw.arrival_icao
1401
- });
1402
- }
1403
- static eta(decodeResult, value) {
1404
- decodeResult.formatted.items.push({
1405
- type: "eta",
1406
- code: "ETA",
1407
- label: "Estimated Time of Arrival",
1408
- value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
1409
- });
1410
- }
1411
- static arrivalRunway(decodeResult, value) {
1412
- decodeResult.raw.arrival_runway = value;
1413
- decodeResult.formatted.items.push({
1414
- type: "runway",
1415
- label: "Arrival Runway",
1416
- value: decodeResult.raw.arrival_runway
1417
- });
1418
- }
1419
- static currentFuel(decodeResult, value) {
1420
- decodeResult.raw.fuel_on_board = value;
1421
- decodeResult.formatted.items.push({
1422
- type: "fuel_on_board",
1423
- code: "FOB",
1424
- label: "Fuel On Board",
1425
- value: decodeResult.raw.fuel_on_board.toString()
1426
- });
1427
- }
1428
- static remainingFuel(decodeResult, value) {
1429
- decodeResult.raw.fuel_remaining = value;
1430
- decodeResult.formatted.items.push({
1431
- type: "fuel_remaining",
1432
- label: "Fuel Remaining",
1433
- value: decodeResult.raw.fuel_remaining.toString()
1434
- });
1435
- }
1436
- static checksum(decodeResult, value) {
1437
- decodeResult.raw.checksum = Number("0x" + value);
1438
- decodeResult.formatted.items.push({
1439
- type: "message_checksum",
1440
- code: "CHECKSUM",
1441
- label: "Message Checksum",
1442
- value: "0x" + ("0000" + decodeResult.raw.checksum.toString(16)).slice(-4)
1443
- });
1444
- }
1445
- static groundspeed(decodeResult, value) {
1446
- decodeResult.raw.groundspeed = value;
1447
- decodeResult.formatted.items.push({
1448
- type: "aircraft_groundspeed",
1449
- code: "GSPD",
1450
- label: "Aircraft Groundspeed",
1451
- value: `${decodeResult.raw.groundspeed}`
1452
- });
1453
- }
1454
- static temperature(decodeResult, value) {
1455
- decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
1456
- decodeResult.formatted.items.push({
1457
- type: "outside_air_temperature",
1458
- code: "OATEMP",
1459
- label: "Outside Air Temperature (C)",
1460
- value: `${decodeResult.raw.outside_air_temperature}`
1461
- });
1462
- }
1463
- static unknown(decodeResult, value) {
1464
- decodeResult.remaining.text += "," + value;
1465
- }
1466
- };
1467
-
1468
- // lib/utils/route_utils.ts
1469
- var RouteUtils = class _RouteUtils {
1470
- static routeToString(route) {
1471
- let str = "";
1472
- if (route.name) {
1473
- str += route.name;
1474
- }
1475
- if (route.runway) {
1476
- str += `(${route.runway})`;
1477
- }
1478
- if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
1479
- str += " starting at ";
1480
- } else if (str.length !== 0 && route.waypoints) {
1481
- str += ": ";
1482
- }
1483
- if (route.waypoints) {
1484
- str += _RouteUtils.waypointsToString(route.waypoints);
1485
- }
1486
- return str;
1487
- }
1488
- static waypointToString(waypoint) {
1489
- let s = waypoint.name;
1490
- if (waypoint.latitude && waypoint.longitude) {
1491
- s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
1492
- }
1493
- if (waypoint.offset) {
1494
- s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
1495
- }
1496
- if (waypoint.time && waypoint.timeFormat) {
1497
- s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
1498
- }
1499
- return s;
1500
- }
1501
- static getWaypoint(leg) {
1502
- const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
1503
- if (regex?.length == 4) {
1504
- return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
1505
- }
1506
- const waypoint = leg.split(",");
1507
- if (waypoint.length == 2) {
1508
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
1509
- if (position) {
1510
- return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
1511
- }
1512
- }
1513
- if (leg.length == 13 || leg.length == 14) {
1514
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
1515
- const name = waypoint.length == 2 ? waypoint[0] : "";
1516
- if (position) {
1517
- return { name, latitude: position.latitude, longitude: position.longitude };
1518
- }
1519
- }
1520
- return { name: leg };
1521
- }
1522
- // move out if we want public
1523
- static timestampToString(time, format) {
1524
- const date = new Date(time * 1e3);
1525
- if (format == "tod") {
1526
- return date.toISOString().slice(11, 19);
1527
- }
1528
- return date.toISOString().slice(0, -5) + "Z";
1529
- }
1530
- static waypointsToString(waypoints) {
1531
- let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
1532
- if (str.startsWith(" > ")) {
1533
- str = ">>" + str.slice(2);
1534
- }
1535
- return str;
1536
- }
1537
- };
1538
-
1539
1930
  // lib/utils/flight_plan_utils.ts
1540
1931
  var FlightPlanUtils = class _FlightPlanUtils {
1541
1932
  /**
@@ -1797,13 +2188,7 @@ function processTimeOfDeparture(decodeResult, data) {
1797
2188
  return false;
1798
2189
  }
1799
2190
  function processIdentification(decodeResult, data) {
1800
- decodeResult.raw.tail = data[0];
1801
- decodeResult.formatted.items.push({
1802
- type: "tail",
1803
- code: "TAIL",
1804
- label: "Tail",
1805
- value: decodeResult.raw.tail
1806
- });
2191
+ ResultFormatter.tail(decodeResult, data[0]);
1807
2192
  if (data.length > 1) {
1808
2193
  decodeResult.raw.flight_number = data[1];
1809
2194
  }
@@ -1900,8 +2285,6 @@ function processPS(decodeResult, data) {
1900
2285
  } else {
1901
2286
  allKnownFields = false;
1902
2287
  }
1903
- console.log("PS data.length: ", data.length);
1904
- console.log("PS data: ", data);
1905
2288
  if (data.length === 9) {
1906
2289
  processRoute(decodeResult, data[3], data[1], data[5], data[4], void 0);
1907
2290
  ResultFormatter.altitude(decodeResult, Number(data[2]) * 100);
@@ -1936,8 +2319,6 @@ function processPosition2(decodeResult, data) {
1936
2319
  } else {
1937
2320
  allKnownFields = false;
1938
2321
  }
1939
- console.log("data.length: ", data.length);
1940
- console.log("data: ", data);
1941
2322
  if (data.length >= 10) {
1942
2323
  ResultFormatter.altitude(decodeResult, Number(data[3]) * 100);
1943
2324
  processRoute(decodeResult, data[1], data[2], data[4], data[5], data[6]);
@@ -1995,7 +2376,7 @@ var Label_H1_FPN = class extends DecoderPlugin {
1995
2376
  decodeResult.decoded = true;
1996
2377
  decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
1997
2378
  if (decodeResult.formatted.items.length === 0) {
1998
- if (options?.debug) {
2379
+ if (options.debug) {
1999
2380
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
2000
2381
  }
2001
2382
  decodeResult.remaining.text = message.text;
@@ -2025,7 +2406,7 @@ var Label_H1_FTX = class extends DecoderPlugin {
2025
2406
  decodeResult.decoded = true;
2026
2407
  decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2027
2408
  if (decodeResult.formatted.items.length === 0) {
2028
- if (options?.debug) {
2409
+ if (options.debug) {
2029
2410
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
2030
2411
  }
2031
2412
  decodeResult.remaining.text = message.text;
@@ -2056,7 +2437,7 @@ var Label_H1_INI = class extends DecoderPlugin {
2056
2437
  decodeResult.decoded = true;
2057
2438
  decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2058
2439
  if (decodeResult.formatted.items.length === 0) {
2059
- if (options?.debug) {
2440
+ if (options.debug) {
2060
2441
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
2061
2442
  }
2062
2443
  decodeResult.remaining.text = message.text;
@@ -2145,7 +2526,7 @@ var Label_H1_POS = class extends DecoderPlugin {
2145
2526
  decodeResult.decoded = true;
2146
2527
  decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2147
2528
  if (decodeResult.formatted.items.length === 0) {
2148
- if (options?.debug) {
2529
+ if (options.debug) {
2149
2530
  console.log(`Decoder: Unknown H1 message: ${message.text}`);
2150
2531
  }
2151
2532
  decodeResult.remaining.text = message.text;
@@ -2212,6 +2593,60 @@ function processUnknown2(decodeResult, value) {
2212
2593
  decodeResult.remaining.text += value;
2213
2594
  }
2214
2595
 
2596
+ // lib/plugins/Label_HX.ts
2597
+ var Label_HX = class extends DecoderPlugin {
2598
+ name = "label-hx";
2599
+ qualifiers() {
2600
+ return {
2601
+ labels: ["HX"],
2602
+ preambles: ["RA FMT LOCATION", "RA FMT 43"]
2603
+ };
2604
+ }
2605
+ decode(message, options = {}) {
2606
+ const decodeResult = this.defaultResult();
2607
+ decodeResult.decoder.name = this.name;
2608
+ decodeResult.message = message;
2609
+ decodeResult.formatted.description = "Undelivered Uplink Report";
2610
+ const parts = message.text.split(" ");
2611
+ decodeResult.decoded = true;
2612
+ if (parts[2] === "LOCATION") {
2613
+ let latdir = parts[3].substring(0, 1);
2614
+ let latdeg = Number(parts[3].substring(1, 3));
2615
+ let latmin = Number(parts[3].substring(3, 7));
2616
+ let londir = parts[4].substring(0, 1);
2617
+ let londeg = Number(parts[4].substring(1, 4));
2618
+ let lonmin = Number(parts[4].substring(4, 8));
2619
+ decodeResult.raw.position = {
2620
+ latitude: (latdeg + latmin / 60) * (latdir === "N" ? 1 : -1),
2621
+ longitude: (londeg + lonmin / 60) * (londir === "E" ? 1 : -1)
2622
+ };
2623
+ decodeResult.remaining.text = parts.slice(5).join(" ");
2624
+ } else if (parts[2] === "43") {
2625
+ ResultFormatter.departureAirport(decodeResult, parts[3]);
2626
+ decodeResult.remaining.text = parts.slice(4).join(" ");
2627
+ } else {
2628
+ decodeResult.decoded = false;
2629
+ }
2630
+ if (decodeResult.raw.position) {
2631
+ decodeResult.formatted.items.push({
2632
+ type: "aircraft_position",
2633
+ code: "POS",
2634
+ label: "Aircraft Position",
2635
+ value: CoordinateUtils.coordinateString(decodeResult.raw.position)
2636
+ });
2637
+ }
2638
+ if (decodeResult.decoded) {
2639
+ if (decodeResult.remaining.text === "")
2640
+ decodeResult.decoder.decodeLevel = "full";
2641
+ else
2642
+ decodeResult.decoder.decodeLevel = "partial";
2643
+ } else {
2644
+ decodeResult.decoder.decodeLevel = "none";
2645
+ }
2646
+ return decodeResult;
2647
+ }
2648
+ };
2649
+
2215
2650
  // lib/plugins/Label_SQ.ts
2216
2651
  var Label_SQ = class extends DecoderPlugin {
2217
2652
  name = "label-sq";
@@ -2252,11 +2687,13 @@ var Label_SQ = class extends DecoderPlugin {
2252
2687
  decodeResult.formatted.items = [
2253
2688
  {
2254
2689
  type: "network",
2690
+ code: "NETT",
2255
2691
  label: "Network",
2256
2692
  value: formattedNetwork
2257
2693
  },
2258
2694
  {
2259
2695
  type: "version",
2696
+ code: "VER",
2260
2697
  label: "Version",
2261
2698
  value: decodeResult.raw.version
2262
2699
  }
@@ -2265,6 +2702,7 @@ var Label_SQ = class extends DecoderPlugin {
2265
2702
  if (decodeResult.raw.groundStation.icaoCode && decodeResult.raw.groundStation.number) {
2266
2703
  decodeResult.formatted.items.push({
2267
2704
  type: "ground_station",
2705
+ code: "GNDSTN",
2268
2706
  label: "Ground Station",
2269
2707
  value: `${decodeResult.raw.groundStation.icaoCode}${decodeResult.raw.groundStation.number}`
2270
2708
  });
@@ -2272,6 +2710,7 @@ var Label_SQ = class extends DecoderPlugin {
2272
2710
  if (decodeResult.raw.groundStation.iataCode) {
2273
2711
  decodeResult.formatted.items.push({
2274
2712
  type: "iataCode",
2713
+ code: "IATA",
2275
2714
  label: "IATA",
2276
2715
  value: decodeResult.raw.groundStation.iataCode
2277
2716
  });
@@ -2279,6 +2718,7 @@ var Label_SQ = class extends DecoderPlugin {
2279
2718
  if (decodeResult.raw.groundStation.icaoCode) {
2280
2719
  decodeResult.formatted.items.push({
2281
2720
  type: "icaoCode",
2721
+ code: "ICAO",
2282
2722
  label: "ICAO",
2283
2723
  value: decodeResult.raw.groundStation.icaoCode
2284
2724
  });
@@ -2286,6 +2726,7 @@ var Label_SQ = class extends DecoderPlugin {
2286
2726
  if (decodeResult.raw.groundStation.coordinates.latitude) {
2287
2727
  decodeResult.formatted.items.push({
2288
2728
  type: "coordinates",
2729
+ code: "COORD",
2289
2730
  label: "Ground Station Location",
2290
2731
  value: `${decodeResult.raw.groundStation.coordinates.latitude}, ${decodeResult.raw.groundStation.coordinates.longitude}`
2291
2732
  });
@@ -2293,6 +2734,7 @@ var Label_SQ = class extends DecoderPlugin {
2293
2734
  if (decodeResult.raw.groundStation.airport) {
2294
2735
  decodeResult.formatted.items.push({
2295
2736
  type: "airport",
2737
+ code: "APT",
2296
2738
  label: "Airport",
2297
2739
  value: `${decodeResult.raw.groundStation.airport.name} (${decodeResult.raw.groundStation.airport.icao}) in ${decodeResult.raw.groundStation.airport.location}`
2298
2740
  });
@@ -2301,6 +2743,7 @@ var Label_SQ = class extends DecoderPlugin {
2301
2743
  if (decodeResult.raw.vdlFrequency) {
2302
2744
  decodeResult.formatted.items.push({
2303
2745
  type: "vdlFrequency",
2746
+ code: "VDLFRQ",
2304
2747
  label: "VDL Frequency",
2305
2748
  value: `${decodeResult.raw.vdlFrequency} MHz`
2306
2749
  });
@@ -2457,31 +2900,48 @@ var Label_QQ = class extends DecoderPlugin {
2457
2900
  decode(message, options = {}) {
2458
2901
  const decodeResult = this.defaultResult();
2459
2902
  decodeResult.decoder.name = this.name;
2460
- decodeResult.raw.origin = message.text.substring(0, 4);
2461
- decodeResult.raw.destination = message.text.substring(4, 8);
2462
- decodeResult.raw.wheels_off = message.text.substring(8, 12);
2463
- decodeResult.remaining.text = message.text.substring(12);
2903
+ decodeResult.message = message;
2464
2904
  decodeResult.formatted.description = "OFF Report";
2465
- decodeResult.formatted.items = [
2466
- {
2467
- type: "origin",
2468
- code: "ORG",
2469
- label: "Origin",
2470
- value: decodeResult.raw.origin
2471
- },
2472
- {
2473
- type: "destination",
2474
- code: "DST",
2475
- label: "Destination",
2476
- value: decodeResult.raw.destination
2477
- },
2478
- {
2479
- type: "wheels_off",
2480
- code: "WOFF",
2481
- label: "Wheels OFF",
2482
- value: decodeResult.raw.wheels_off
2905
+ ResultFormatter.departureAirport(decodeResult, message.text.substring(0, 4));
2906
+ ResultFormatter.arrivalAirport(decodeResult, message.text.substring(4, 8));
2907
+ decodeResult.raw.wheels_off = message.text.substring(8, 12);
2908
+ if (message.text.substring(12, 19) === "\r\n001FE") {
2909
+ decodeResult.raw.day_of_month = message.text.substring(19, 21);
2910
+ decodeResult.raw.wheels_off = message.text.substring(21, 27);
2911
+ let latdir = message.text.substring(27, 28);
2912
+ let latdeg = Number(message.text.substring(28, 30));
2913
+ let latmin = Number(message.text.substring(30, 34));
2914
+ let londir = message.text.substring(34, 35);
2915
+ let londeg = Number(message.text.substring(35, 38));
2916
+ let lonmin = Number(message.text.substring(38, 42));
2917
+ decodeResult.raw.position = {
2918
+ latitude: (latdeg + latmin / 60) * (latdir === "N" ? 1 : -1),
2919
+ longitude: (londeg + lonmin / 60) * (londir === "E" ? 1 : -1)
2920
+ };
2921
+ decodeResult.remaining.text = message.text.substring(42, 45);
2922
+ if (decodeResult.remaining.text !== "---") {
2923
+ ResultFormatter.groundspeed(decodeResult, message.text.substring(45, 48));
2924
+ } else {
2925
+ ResultFormatter.unknown(decodeResult, message.text.substring(45, 48));
2483
2926
  }
2484
- ];
2927
+ ResultFormatter.unknown(decodeResult, message.text.substring(48));
2928
+ } else {
2929
+ decodeResult.remaining.text = message.text.substring(12);
2930
+ }
2931
+ decodeResult.formatted.items.push({
2932
+ type: "wheels_off",
2933
+ code: "WOFF",
2934
+ label: "Wheels OFF",
2935
+ value: decodeResult.raw.wheels_off
2936
+ });
2937
+ if (decodeResult.raw.position) {
2938
+ decodeResult.formatted.items.push({
2939
+ type: "aircraft_position",
2940
+ code: "POS",
2941
+ label: "Aircraft Position",
2942
+ value: CoordinateUtils.coordinateString(decodeResult.raw.position)
2943
+ });
2944
+ }
2485
2945
  decodeResult.decoded = true;
2486
2946
  if (decodeResult.remaining.text === "")
2487
2947
  decodeResult.decoder.decodeLevel = "full";
@@ -3304,7 +3764,11 @@ var MessageDecoder = class {
3304
3764
  this.debug = false;
3305
3765
  this.registerPlugin(new Label_ColonComma(this));
3306
3766
  this.registerPlugin(new Label_5Z(this));
3767
+ this.registerPlugin(new Label_10_LDR(this));
3768
+ this.registerPlugin(new Label_10_POS(this));
3769
+ this.registerPlugin(new Label_10_Slash(this));
3307
3770
  this.registerPlugin(new Label_12_N_Space(this));
3771
+ this.registerPlugin(new Label_13Through18_Slash(this));
3308
3772
  this.registerPlugin(new Label_15(this));
3309
3773
  this.registerPlugin(new Label_15_FST(this));
3310
3774
  this.registerPlugin(new Label_16_N_Space(this));
@@ -3316,6 +3780,7 @@ var MessageDecoder = class {
3316
3780
  this.registerPlugin(new Label_44_OFF(this));
3317
3781
  this.registerPlugin(new Label_44_ON(this));
3318
3782
  this.registerPlugin(new Label_44_POS(this));
3783
+ this.registerPlugin(new Label_4N(this));
3319
3784
  this.registerPlugin(new Label_B6_Forwardslash(this));
3320
3785
  this.registerPlugin(new Label_H1_FPN(this));
3321
3786
  this.registerPlugin(new Label_H1_FLR(this));
@@ -3324,7 +3789,9 @@ var MessageDecoder = class {
3324
3789
  this.registerPlugin(new Label_H1_OHMA(this));
3325
3790
  this.registerPlugin(new Label_H1_POS(this));
3326
3791
  this.registerPlugin(new Label_H1_WRN(this));
3792
+ this.registerPlugin(new Label_HX(this));
3327
3793
  this.registerPlugin(new Label_80(this));
3794
+ this.registerPlugin(new Label_83(this));
3328
3795
  this.registerPlugin(new Label_8E(this));
3329
3796
  this.registerPlugin(new Label_1M_Slash(this));
3330
3797
  this.registerPlugin(new Label_SQ(this));