@airframes/acars-decoder 1.6.8 → 1.6.10

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,192 @@ 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
+ };
158
+
159
+ // lib/utils/result_formatter.ts
160
+ var ResultFormatter = class {
161
+ static position(decodeResult, value) {
162
+ decodeResult.raw.position = value;
163
+ decodeResult.formatted.items.push({
164
+ type: "aircraft_position",
165
+ code: "POS",
166
+ label: "Aircraft Position",
167
+ value: CoordinateUtils.coordinateString(value)
168
+ });
169
+ }
170
+ static altitude(decodeResult, value) {
171
+ decodeResult.raw.altitude = value;
172
+ decodeResult.formatted.items.push({
173
+ type: "altitude",
174
+ code: "ALT",
175
+ label: "Altitude",
176
+ value: `${decodeResult.raw.altitude} feet`
177
+ });
178
+ }
179
+ static flightNumber(decodeResult, value) {
180
+ decodeResult.raw.flight_number = value;
181
+ }
182
+ static departureAirport(decodeResult, value) {
183
+ decodeResult.raw.departure_icao = value;
184
+ decodeResult.formatted.items.push({
185
+ type: "origin",
186
+ code: "ORG",
187
+ label: "Origin",
188
+ value: decodeResult.raw.departure_icao
189
+ });
190
+ }
191
+ static departureRunway(decodeResult, value) {
192
+ decodeResult.raw.departure_runway = value;
193
+ decodeResult.formatted.items.push({
194
+ type: "runway",
195
+ code: "DEPRWY",
196
+ label: "Departure Runway",
197
+ value: decodeResult.raw.departure_runway
198
+ });
199
+ }
200
+ static arrivalAirport(decodeResult, value) {
201
+ decodeResult.raw.arrival_icao = value;
202
+ decodeResult.formatted.items.push({
203
+ type: "destination",
204
+ code: "DST",
205
+ label: "Destination",
206
+ value: decodeResult.raw.arrival_icao
207
+ });
208
+ }
209
+ static eta(decodeResult, value) {
210
+ decodeResult.raw.eta_time = value;
211
+ decodeResult.formatted.items.push({
212
+ type: "eta",
213
+ code: "ETA",
214
+ label: "Estimated Time of Arrival",
215
+ value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
216
+ });
217
+ }
218
+ static arrivalRunway(decodeResult, value) {
219
+ decodeResult.raw.arrival_runway = value;
220
+ decodeResult.formatted.items.push({
221
+ type: "runway",
222
+ code: "ARWY",
223
+ label: "Arrival Runway",
224
+ value: decodeResult.raw.arrival_runway
225
+ });
226
+ }
227
+ static currentFuel(decodeResult, value) {
228
+ decodeResult.raw.fuel_on_board = value;
229
+ decodeResult.formatted.items.push({
230
+ type: "fuel_on_board",
231
+ code: "FOB",
232
+ label: "Fuel On Board",
233
+ value: decodeResult.raw.fuel_on_board.toString()
234
+ });
235
+ }
236
+ static remainingFuel(decodeResult, value) {
237
+ decodeResult.raw.fuel_remaining = value;
238
+ decodeResult.formatted.items.push({
239
+ type: "fuel_remaining",
240
+ code: "FUEL",
241
+ label: "Fuel Remaining",
242
+ value: decodeResult.raw.fuel_remaining.toString()
243
+ });
244
+ }
245
+ static checksum(decodeResult, value) {
246
+ decodeResult.raw.checksum = Number("0x" + value);
247
+ decodeResult.formatted.items.push({
248
+ type: "message_checksum",
249
+ code: "CHECKSUM",
250
+ label: "Message Checksum",
251
+ value: "0x" + ("0000" + decodeResult.raw.checksum.toString(16)).slice(-4)
252
+ });
253
+ }
254
+ static groundspeed(decodeResult, value) {
255
+ decodeResult.raw.groundspeed = value;
256
+ decodeResult.formatted.items.push({
257
+ type: "aircraft_groundspeed",
258
+ code: "GSPD",
259
+ label: "Aircraft Groundspeed",
260
+ value: `${decodeResult.raw.groundspeed}`
261
+ });
262
+ }
263
+ static temperature(decodeResult, value) {
264
+ decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
265
+ decodeResult.formatted.items.push({
266
+ type: "outside_air_temperature",
267
+ code: "OATEMP",
268
+ label: "Outside Air Temperature (C)",
269
+ value: `${decodeResult.raw.outside_air_temperature}`
270
+ });
271
+ }
272
+ static heading(decodeResult, value) {
273
+ decodeResult.raw.heading = value;
274
+ decodeResult.formatted.items.push({
275
+ type: "heading",
276
+ code: "HDG",
277
+ label: "Heading",
278
+ value: `${decodeResult.raw.heading}`
279
+ });
280
+ }
281
+ static unknown(decodeResult, value) {
282
+ decodeResult.remaining.text += "," + value;
283
+ }
284
+ };
285
+
100
286
  // lib/plugins/Label_5Z.ts
101
287
  var Label_5Z = class extends DecoderPlugin {
102
288
  name = "label-5z";
@@ -156,34 +342,9 @@ var Label_5Z = class extends DecoderPlugin {
156
342
  const rdcRegex = /^(?<from>\w\w\w)(?<to>\w\w\w) (?<unknown1>\d\d) R(?<runway>.+) G(?<unknown2>.+)$/;
157
343
  results = remainder.match(rdcRegex);
158
344
  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
- });
345
+ ResultFormatter.departureAirport(decodeResult, results.groups.from);
346
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.to);
347
+ ResultFormatter.arrivalRunway(decodeResult, results.groups.runway);
187
348
  } else {
188
349
  if (options.debug) {
189
350
  console.log(`Decoder: Unkown 5Z RDC format: ${remainder}`);
@@ -206,62 +367,273 @@ var Label_5Z = class extends DecoderPlugin {
206
367
  }
207
368
  };
208
369
 
209
- // lib/utils/coordinate_utils.ts
210
- var CoordinateUtils = class {
370
+ // lib/plugins/Label_10_LDR.ts
371
+ var Label_10_LDR = class extends DecoderPlugin {
372
+ // eslint-disable-line camelcase
373
+ name = "label-10-ldr";
374
+ qualifiers() {
375
+ return {
376
+ labels: ["10"],
377
+ preambles: ["LDR"]
378
+ };
379
+ }
380
+ decode(message, options = {}) {
381
+ const decodeResult = this.defaultResult();
382
+ decodeResult.decoder.name = this.name;
383
+ decodeResult.formatted.description = "Position Report";
384
+ decodeResult.message = message;
385
+ const parts = message.text.split(",");
386
+ if (parts.length < 17) {
387
+ if (options?.debug) {
388
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
389
+ }
390
+ decodeResult.remaining.text = message.text;
391
+ decodeResult.decoded = false;
392
+ decodeResult.decoder.decodeLevel = "none";
393
+ return decodeResult;
394
+ }
395
+ const lat = parts[5];
396
+ const lon = parts[6];
397
+ const position = {
398
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1).trim()),
399
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim())
400
+ };
401
+ ResultFormatter.position(decodeResult, position);
402
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
403
+ ResultFormatter.departureAirport(decodeResult, parts[9]);
404
+ ResultFormatter.arrivalAirport(decodeResult, parts[10]);
405
+ ResultFormatter.arrivalRunway(decodeResult, parts[12].split("/")[0]);
406
+ decodeResult.remaining.text = [...parts.slice(0, 5), parts[11], ...parts.slice(13)].join(",");
407
+ decodeResult.decoded = true;
408
+ decodeResult.decoder.decodeLevel = "partial";
409
+ return decodeResult;
410
+ }
411
+ };
412
+
413
+ // lib/plugins/Label_10_POS.ts
414
+ var Label_10_POS = class extends DecoderPlugin {
415
+ // eslint-disable-line camelcase
416
+ name = "label-10-pos";
417
+ qualifiers() {
418
+ return {
419
+ labels: ["10"],
420
+ preambles: ["POS"]
421
+ };
422
+ }
423
+ decode(message, options = {}) {
424
+ const decodeResult = this.defaultResult();
425
+ decodeResult.decoder.name = this.name;
426
+ decodeResult.formatted.description = "Position Report";
427
+ decodeResult.message = message;
428
+ const parts = message.text.split(",");
429
+ if (parts.length !== 12) {
430
+ if (options?.debug) {
431
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
432
+ }
433
+ decodeResult.remaining.text = message.text;
434
+ decodeResult.decoded = false;
435
+ decodeResult.decoder.decodeLevel = "none";
436
+ return decodeResult;
437
+ }
438
+ const lat = parts[1].trim();
439
+ const lon = parts[2].trim();
440
+ const position = {
441
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1).trim()) / 100,
442
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim()) / 100
443
+ };
444
+ ResultFormatter.position(decodeResult, position);
445
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
446
+ decodeResult.remaining.text = [parts[0], ...parts.slice(3, 7), ...parts.slice(8)].join(",");
447
+ decodeResult.decoded = true;
448
+ decodeResult.decoder.decodeLevel = "partial";
449
+ return decodeResult;
450
+ }
451
+ };
452
+
453
+ // lib/DateTimeUtils.ts
454
+ var DateTimeUtils = class {
455
+ // Expects a four digit UTC time string (HHMM)
456
+ static UTCToString(UTCString) {
457
+ let utcDate = /* @__PURE__ */ new Date();
458
+ utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
459
+ return utcDate.toTimeString();
460
+ }
461
+ // Expects a six digit date string and a four digit UTC time string
462
+ // (DDMMYY) (HHMM)
463
+ static UTCDateTimeToString(dateString, timeString) {
464
+ let utcDate = /* @__PURE__ */ new Date();
465
+ utcDate.setUTCDate(+dateString.substr(0, 2));
466
+ utcDate.setUTCMonth(+dateString.substr(2, 2));
467
+ if (dateString.length === 6) {
468
+ utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
469
+ }
470
+ if (timeString.length === 6) {
471
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
472
+ } else {
473
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
474
+ }
475
+ return utcDate.toUTCString();
476
+ }
211
477
  /**
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
478
  *
215
- * @returns An object with latitude and longitude properties
479
+ * @param time HHMMSS
480
+ * @returns seconds since midnight
216
481
  */
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);
482
+ static convertHHMMSSToTod(time) {
483
+ const h = Number(time.substring(0, 2));
484
+ const m = Number(time.substring(2, 4));
485
+ const s = Number(time.substring(4, 6));
486
+ const tod = h * 3600 + m * 60 + s;
487
+ return tod;
488
+ }
489
+ /**
490
+ *
491
+ * @param time HHMMSS
492
+ * @param date MMDDYY or MMDDYYYY
493
+ * @returns seconds since epoch
494
+ */
495
+ static convertDateTimeToEpoch(time, date) {
496
+ if (date.length === 6) {
497
+ date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
225
498
  }
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;
499
+ 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`;
500
+ const millis = Date.parse(timestamp);
501
+ return millis / 1e3;
502
+ }
503
+ };
504
+
505
+ // lib/utils/route_utils.ts
506
+ var RouteUtils = class _RouteUtils {
507
+ static routeToString(route) {
508
+ let str = "";
509
+ if (route.name) {
510
+ str += route.name;
231
511
  }
232
- return results;
512
+ if (route.runway) {
513
+ str += `(${route.runway})`;
514
+ }
515
+ if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
516
+ str += " starting at ";
517
+ } else if (str.length !== 0 && route.waypoints) {
518
+ str += ": ";
519
+ }
520
+ if (route.waypoints) {
521
+ str += _RouteUtils.waypointsToString(route.waypoints);
522
+ }
523
+ return str;
233
524
  }
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);
525
+ static waypointToString(waypoint) {
526
+ let s = waypoint.name;
527
+ if (waypoint.latitude && waypoint.longitude) {
528
+ s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
529
+ }
530
+ if (waypoint.offset) {
531
+ s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
532
+ }
533
+ if (waypoint.time && waypoint.timeFormat) {
534
+ s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
535
+ }
536
+ return s;
537
+ }
538
+ static getWaypoint(leg) {
539
+ const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
540
+ if (regex?.length == 4) {
541
+ return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
542
+ }
543
+ const waypoint = leg.split(",");
544
+ if (waypoint.length == 2) {
545
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
546
+ if (position) {
547
+ return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
548
+ }
549
+ }
550
+ if (leg.length == 13 || leg.length == 14) {
551
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
552
+ const name = waypoint.length == 2 ? waypoint[0] : "";
553
+ if (position) {
554
+ return { name, latitude: position.latitude, longitude: position.longitude };
555
+ }
556
+ }
557
+ return { name: leg };
558
+ }
559
+ // move out if we want public
560
+ static timestampToString(time, format) {
561
+ const date = new Date(time * 1e3);
562
+ if (format == "tod") {
563
+ return date.toISOString().slice(11, 19);
248
564
  }
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;
565
+ return date.toISOString().slice(0, -5) + "Z";
566
+ }
567
+ static waypointsToString(waypoints) {
568
+ let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
569
+ if (str.startsWith(" > ")) {
570
+ str = ">>" + str.slice(2);
258
571
  }
259
- return results;
572
+ return str;
260
573
  }
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}`;
574
+ };
575
+
576
+ // lib/plugins/Label_10_Slash.ts
577
+ var Label_10_Slash = class extends DecoderPlugin {
578
+ // eslint-disable-line camelcase
579
+ name = "label-10-slash";
580
+ qualifiers() {
581
+ return {
582
+ labels: ["10"],
583
+ preambles: ["/"]
584
+ };
585
+ }
586
+ decode(message, options = {}) {
587
+ const decodeResult = this.defaultResult();
588
+ decodeResult.decoder.name = this.name;
589
+ decodeResult.formatted.description = "Position Report";
590
+ decodeResult.message = message;
591
+ const parts = message.text.split("/");
592
+ if (parts.length < 17) {
593
+ if (options?.debug) {
594
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
595
+ }
596
+ decodeResult.remaining.text = message.text;
597
+ decodeResult.decoded = false;
598
+ decodeResult.decoder.decodeLevel = "none";
599
+ return decodeResult;
600
+ }
601
+ const lat = parts[1];
602
+ const lon = parts[2];
603
+ const position = {
604
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1)),
605
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1))
606
+ };
607
+ ResultFormatter.position(decodeResult, position);
608
+ ResultFormatter.heading(decodeResult, Number(parts[5]));
609
+ ResultFormatter.altitude(decodeResult, 100 * Number(parts[6]));
610
+ ResultFormatter.arrivalAirport(decodeResult, parts[7]);
611
+ ResultFormatter.eta(decodeResult, parts[8] + "00");
612
+ const waypoints = [{
613
+ name: parts[11]
614
+ }, {
615
+ name: parts[12],
616
+ time: DateTimeUtils.convertHHMMSSToTod(parts[13] + "00"),
617
+ timeFormat: "tod"
618
+ }, {
619
+ name: parts[14],
620
+ time: DateTimeUtils.convertHHMMSSToTod(parts[15] + "00"),
621
+ timeFormat: "tod"
622
+ }];
623
+ decodeResult.raw.route = { waypoints };
624
+ decodeResult.formatted.items.push({
625
+ type: "aircraft_route",
626
+ code: "ROUTE",
627
+ label: "Aircraft Route",
628
+ value: RouteUtils.routeToString(decodeResult.raw.route)
629
+ });
630
+ if (parts[16]) {
631
+ ResultFormatter.departureAirport(decodeResult, parts[16]);
632
+ }
633
+ decodeResult.remaining.text = [parts[3], parts[4], ...parts.slice(9, 11), ...parts.slice(17)].join("/");
634
+ decodeResult.decoded = true;
635
+ decodeResult.decoder.decodeLevel = "partial";
636
+ return decodeResult;
265
637
  }
266
638
  };
267
639
 
@@ -290,19 +662,14 @@ var Label_12_N_Space = class extends DecoderPlugin {
290
662
  latitude: Number(results.groups.lat_coord) * (results.groups.lat == "N" ? 1 : -1),
291
663
  longitude: Number(results.groups.long_coord) * (results.groups.long == "E" ? 1 : -1)
292
664
  };
293
- decodeResult.raw.flight_level = results.groups.alt == "GRD" || results.groups.alt == "***" ? "0" : Number(results.groups.alt);
665
+ const altitude = results.groups.alt == "GRD" || results.groups.alt == "***" ? 0 : Number(results.groups.alt);
294
666
  decodeResult.formatted.items.push({
295
667
  type: "aircraft_position",
296
668
  code: "POS",
297
669
  label: "Aircraft Position",
298
670
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
299
671
  });
300
- decodeResult.formatted.items.push({
301
- type: "flight_level",
302
- code: "FL",
303
- label: "Flight Level",
304
- value: decodeResult.raw.flight_level
305
- });
672
+ ResultFormatter.altitude(decodeResult, altitude);
306
673
  decodeResult.remaining.text = `,${results.groups.unkwn1} ,${results.groups.unkwn2}, ${results.groups.unkwn3}`;
307
674
  decodeResult.decoded = true;
308
675
  decodeResult.decoder.decodeLevel = "partial";
@@ -364,39 +731,35 @@ var Label_15_FST = class extends DecoderPlugin {
364
731
  decodeResult.decoder.name = this.name;
365
732
  decodeResult.formatted.description = "Position Report";
366
733
  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);
734
+ const parts = message.text.split(" ");
735
+ const header = parts[0];
736
+ decodeResult.raw.departure_icao = header.substring(5, 9);
737
+ decodeResult.raw.arrival_icao = header.substring(9, 13);
738
+ const stringCoords = header.substring(13);
370
739
  const firstChar = stringCoords.substring(0, 1);
371
740
  const middleChar = stringCoords.substring(7, 8);
372
741
  decodeResult.raw.position = {};
373
742
  if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
374
743
  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);
744
+ decodeResult.raw.position.longitude = Number(stringCoords.substring(8, 15)) / 1e4 * (middleChar === "W" ? -1 : 1);
745
+ decodeResult.raw.altitude = Number(stringCoords.substring(15)) * 100;
376
746
  } else {
377
747
  decodeResult.decoded = false;
378
748
  decodeResult.decoder.decodeLevel = "none";
379
749
  return decodeResult;
380
750
  }
381
751
  decodeResult.formatted.items.push({
382
- type: "position",
383
- label: "Position",
752
+ type: "aircraft_position",
753
+ code: "POS",
754
+ label: "Aircraft Position",
384
755
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
385
756
  });
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
- });
757
+ ResultFormatter.altitude(decodeResult, decodeResult.raw.altitude);
758
+ ResultFormatter.departureAirport(decodeResult, decodeResult.raw.departure_icao);
759
+ ResultFormatter.arrivalAirport(decodeResult, decodeResult.raw.arrival_icao);
760
+ decodeResult.remaining.text = parts.slice(1).join(" ");
398
761
  decodeResult.decoded = true;
399
- decodeResult.decoder.decodeLevel = "full";
762
+ decodeResult.decoder.decodeLevel = "partial";
400
763
  return decodeResult;
401
764
  }
402
765
  };
@@ -427,19 +790,14 @@ var Label_16_N_Space = class extends DecoderPlugin {
427
790
  latitude: Number(results.groups.lat_coord) * (results.groups.lat == "N" ? 1 : -1),
428
791
  longitude: Number(results.groups.long_coord) * (results.groups.long == "E" ? 1 : -1)
429
792
  };
430
- decodeResult.raw.flight_level = results.groups.alt == "GRD" || results.groups.alt == "***" ? "0" : Number(results.groups.alt);
793
+ const altitude = results.groups.alt == "GRD" || results.groups.alt == "***" ? 0 : Number(results.groups.alt);
431
794
  decodeResult.formatted.items.push({
432
795
  type: "aircraft_position",
433
796
  code: "POS",
434
797
  label: "Aircraft Position",
435
798
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
436
799
  });
437
- decodeResult.formatted.items.push({
438
- type: "flight_level",
439
- code: "FL",
440
- label: "Flight Level",
441
- value: decodeResult.raw.flight_level
442
- });
800
+ ResultFormatter.altitude(decodeResult, altitude);
443
801
  decodeResult.remaining.text = `,${results.groups.unkwn1} ,${results.groups.unkwn2}`;
444
802
  decodeResult.decoded = true;
445
803
  decodeResult.decoder.decodeLevel = "partial";
@@ -472,58 +830,6 @@ var Label_16_N_Space = class extends DecoderPlugin {
472
830
  }
473
831
  };
474
832
 
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
833
  // lib/plugins/Label_1M_Slash.ts
528
834
  var Label_1M_Slash = class extends DecoderPlugin {
529
835
  name = "label-1m-slash";
@@ -600,6 +906,7 @@ var Label_20_POS = class extends DecoderPlugin {
600
906
  if (decodeResult.raw.position) {
601
907
  decodeResult.formatted.items.push({
602
908
  type: "position",
909
+ code: "POS",
603
910
  label: "Position",
604
911
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
605
912
  });
@@ -613,6 +920,7 @@ var Label_20_POS = class extends DecoderPlugin {
613
920
  if (decodeResult.raw.position) {
614
921
  decodeResult.formatted.items.push({
615
922
  type: "position",
923
+ code: "POS",
616
924
  label: "Position",
617
925
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
618
926
  });
@@ -652,7 +960,7 @@ var Label_21_POS = class extends DecoderPlugin {
652
960
  processAlt(decodeResult, fields[3]);
653
961
  processTemp(decodeResult, fields[6]);
654
962
  processArrvApt(decodeResult, fields[8]);
655
- decodeResult.raw.remaining = [
963
+ decodeResult.remaining.text = [
656
964
  fields[1],
657
965
  fields[2],
658
966
  fields[4],
@@ -1016,9 +1324,7 @@ var Label_44_POS = class extends DecoderPlugin {
1016
1324
  console.log(results.groups);
1017
1325
  }
1018
1326
  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;
1327
+ 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
1328
  decodeResult.raw.current_time = Date.parse(
1023
1329
  (/* @__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
1330
  );
@@ -1036,24 +1342,9 @@ var Label_44_POS = class extends DecoderPlugin {
1036
1342
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
1037
1343
  });
1038
1344
  }
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
- });
1345
+ ResultFormatter.departureAirport(decodeResult, results.groups.departure_icao);
1346
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
1347
+ ResultFormatter.altitude(decodeResult, flight_level * 100);
1057
1348
  }
1058
1349
  decodeResult.decoded = true;
1059
1350
  decodeResult.decoder.decodeLevel = "full";
@@ -1156,13 +1447,9 @@ var Label_80 = class extends DecoderPlugin {
1156
1447
  break;
1157
1448
  }
1158
1449
  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
- });
1450
+ const flight_level = match.groups.value;
1451
+ decodeResult.raw.altitude = flight_level * 100;
1452
+ ResultFormatter.altitude(decodeResult, decodeResult.raw.altitude);
1166
1453
  break;
1167
1454
  }
1168
1455
  case "FOB": {
@@ -1328,194 +1615,73 @@ var Label_ColonComma = class extends DecoderPlugin {
1328
1615
  const decodeResult = this.defaultResult();
1329
1616
  decodeResult.decoder.name = this.name;
1330
1617
  decodeResult.raw.frequency = Number(message.text) / 1e3;
1331
- decodeResult.formatted.description = "Aircraft Transceiver Frequency Change";
1332
- decodeResult.formatted.items.push({
1333
- type: "frequency",
1334
- label: "Frequency",
1335
- value: `${decodeResult.raw.frequency} MHz`
1336
- });
1337
- decodeResult.decoded = true;
1338
- decodeResult.decoder.decodeLevel = "full";
1339
- return decodeResult;
1340
- }
1341
- };
1342
-
1343
- // lib/utils/result_formatter.ts
1344
- var ResultFormatter = class {
1345
- static altitude(decodeResult, value) {
1346
- decodeResult.raw.altitude = value;
1347
- decodeResult.formatted.items.push({
1348
- type: "altitude",
1349
- code: "ALT",
1350
- label: "Altitude",
1351
- value: `${decodeResult.raw.altitude} feet`
1352
- });
1353
- }
1354
- static flightNumber(decodeResult, value) {
1355
- decodeResult.raw.flight_number = value;
1356
- }
1357
- static departureAirport(decodeResult, value) {
1358
- decodeResult.raw.departure_icao = value;
1359
- decodeResult.formatted.items.push({
1360
- type: "origin",
1361
- code: "ORG",
1362
- label: "Origin",
1363
- value: decodeResult.raw.departure_icao
1364
- });
1365
- }
1366
- static departureRunway(decodeResult, value) {
1367
- decodeResult.raw.departure_runway = value;
1368
- decodeResult.formatted.items.push({
1369
- type: "runway",
1370
- code: "DEPRWY",
1371
- label: "Departure Runway",
1372
- value: decodeResult.raw.departure_runway
1373
- });
1374
- }
1375
- static arrivalAirport(decodeResult, value) {
1376
- decodeResult.raw.arrival_icao = value;
1377
- decodeResult.formatted.items.push({
1378
- type: "destination",
1379
- code: "DST",
1380
- label: "Destination",
1381
- value: decodeResult.raw.arrival_icao
1382
- });
1383
- }
1384
- static eta(decodeResult, value) {
1385
- decodeResult.formatted.items.push({
1386
- type: "eta",
1387
- code: "ETA",
1388
- label: "Estimated Time of Arrival",
1389
- value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
1390
- });
1391
- }
1392
- static arrivalRunway(decodeResult, value) {
1393
- decodeResult.raw.arrival_runway = value;
1394
- decodeResult.formatted.items.push({
1395
- type: "runway",
1396
- label: "Arrival Runway",
1397
- value: decodeResult.raw.arrival_runway
1398
- });
1399
- }
1400
- static currentFuel(decodeResult, value) {
1401
- decodeResult.raw.fuel_on_board = value;
1402
- decodeResult.formatted.items.push({
1403
- type: "fuel_on_board",
1404
- code: "FOB",
1405
- label: "Fuel On Board",
1406
- value: decodeResult.raw.fuel_on_board.toString()
1407
- });
1408
- }
1409
- static remainingFuel(decodeResult, value) {
1410
- decodeResult.raw.fuel_remaining = value;
1411
- decodeResult.formatted.items.push({
1412
- type: "fuel_remaining",
1413
- label: "Fuel Remaining",
1414
- value: decodeResult.raw.fuel_remaining.toString()
1415
- });
1416
- }
1417
- static checksum(decodeResult, value) {
1418
- decodeResult.raw.checksum = Number("0x" + value);
1419
- decodeResult.formatted.items.push({
1420
- type: "message_checksum",
1421
- code: "CHECKSUM",
1422
- label: "Message Checksum",
1423
- value: "0x" + ("0000" + decodeResult.raw.checksum.toString(16)).slice(-4)
1424
- });
1425
- }
1426
- static groundspeed(decodeResult, value) {
1427
- decodeResult.raw.groundspeed = value;
1428
- decodeResult.formatted.items.push({
1429
- type: "aircraft_groundspeed",
1430
- code: "GSPD",
1431
- label: "Aircraft Groundspeed",
1432
- value: `${decodeResult.raw.groundspeed}`
1433
- });
1434
- }
1435
- static temperature(decodeResult, value) {
1436
- decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
1437
- decodeResult.formatted.items.push({
1438
- type: "outside_air_temperature",
1439
- code: "OATEMP",
1440
- label: "Outside Air Temperature (C)",
1441
- value: `${decodeResult.raw.outside_air_temperature}`
1618
+ decodeResult.formatted.description = "Aircraft Transceiver Frequency Change";
1619
+ decodeResult.formatted.items.push({
1620
+ type: "frequency",
1621
+ label: "Frequency",
1622
+ value: `${decodeResult.raw.frequency} MHz`
1442
1623
  });
1443
- }
1444
- static unknown(decodeResult, value) {
1445
- decodeResult.remaining.text += "," + value;
1624
+ decodeResult.decoded = true;
1625
+ decodeResult.decoder.decodeLevel = "full";
1626
+ return decodeResult;
1446
1627
  }
1447
1628
  };
1448
1629
 
1449
- // lib/utils/route_utils.ts
1450
- var RouteUtils = class _RouteUtils {
1451
- static routeToString(route) {
1452
- let str = "";
1453
- if (route.name) {
1454
- str += route.name;
1455
- }
1456
- if (route.runway) {
1457
- str += `(${route.runway})`;
1458
- }
1459
- if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
1460
- str += " starting at ";
1461
- } else if (str.length !== 0 && route.waypoints) {
1462
- str += ": ";
1463
- }
1464
- if (route.waypoints) {
1465
- str += _RouteUtils.waypointsToString(route.waypoints);
1466
- }
1467
- return str;
1468
- }
1469
- static waypointToString(waypoint) {
1470
- let s = waypoint.name;
1471
- if (waypoint.latitude && waypoint.longitude) {
1472
- s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
1473
- }
1474
- if (waypoint.offset) {
1475
- s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
1476
- }
1477
- if (waypoint.time && waypoint.timeFormat) {
1478
- s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
1479
- }
1480
- return s;
1630
+ // lib/plugins/Label_H1_FLR.ts
1631
+ var Label_H1_FLR = class extends DecoderPlugin {
1632
+ name = "label-h1-flr";
1633
+ qualifiers() {
1634
+ return {
1635
+ labels: ["H1"],
1636
+ preambles: ["FLR", "#CFBFLR"]
1637
+ };
1481
1638
  }
1482
- static getWaypoint(leg) {
1483
- const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
1484
- if (regex?.length == 4) {
1485
- return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
1486
- }
1487
- const waypoint = leg.split(",");
1488
- if (waypoint.length == 2) {
1489
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
1490
- if (position) {
1491
- return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
1639
+ decode(message, options = {}) {
1640
+ let decodeResult = this.defaultResult();
1641
+ decodeResult.decoder.name = this.name;
1642
+ decodeResult.formatted.description = "Fault Log Report";
1643
+ decodeResult.message = message;
1644
+ const parts = message.text.split("/FR");
1645
+ if (parts.length > 1) {
1646
+ decodeResult.remaining.text = "";
1647
+ const fields = parts[0].split("/");
1648
+ for (let i = 1; i < fields.length; i++) {
1649
+ const field = fields[i];
1650
+ if (field.startsWith("PN")) {
1651
+ processUnknown(decodeResult, "/" + field);
1652
+ } else {
1653
+ processUnknown(decodeResult, "/" + field);
1654
+ }
1492
1655
  }
1493
- }
1494
- if (leg.length == 13 || leg.length == 14) {
1495
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
1496
- const name = waypoint.length == 2 ? waypoint[0] : "";
1497
- if (position) {
1498
- return { name, latitude: position.latitude, longitude: position.longitude };
1656
+ const data = parts[1].substring(0, 20);
1657
+ const msg = parts[1].substring(20);
1658
+ const datetime = data.substring(0, 12);
1659
+ const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
1660
+ processUnknown(decodeResult, data.substring(12));
1661
+ decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
1662
+ decodeResult.raw.fault_message = msg;
1663
+ decodeResult.formatted.items.push({
1664
+ type: "fault",
1665
+ code: "FR",
1666
+ label: "Fault Report",
1667
+ value: decodeResult.raw.fault_message
1668
+ });
1669
+ decodeResult.decoded = true;
1670
+ decodeResult.decoder.decodeLevel = "partial";
1671
+ } else {
1672
+ if (options.debug) {
1673
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
1499
1674
  }
1675
+ decodeResult.remaining.text = message.text;
1676
+ decodeResult.decoded = false;
1677
+ decodeResult.decoder.decodeLevel = "none";
1500
1678
  }
1501
- return { name: leg };
1502
- }
1503
- // move out if we want public
1504
- static timestampToString(time, format) {
1505
- const date = new Date(time * 1e3);
1506
- if (format == "tod") {
1507
- return date.toISOString().slice(11, 19);
1508
- }
1509
- return date.toISOString().slice(0, -5) + "Z";
1510
- }
1511
- static waypointsToString(waypoints) {
1512
- let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
1513
- if (str.startsWith(" > ")) {
1514
- str = ">>" + str.slice(2);
1515
- }
1516
- return str;
1679
+ return decodeResult;
1517
1680
  }
1518
1681
  };
1682
+ function processUnknown(decodeResult, value) {
1683
+ decodeResult.remaining.text += value;
1684
+ }
1519
1685
 
1520
1686
  // lib/utils/flight_plan_utils.ts
1521
1687
  var FlightPlanUtils = class _FlightPlanUtils {
@@ -1708,21 +1874,7 @@ var H1Helper = class {
1708
1874
  const dt = processDT(decodeResult, data2);
1709
1875
  allKnownFields = allKnownFields && dt;
1710
1876
  } else if (fields[i].startsWith("ID")) {
1711
- const data2 = fields[i].substring(2).split(",");
1712
- decodeResult.raw.tail = data2[0];
1713
- decodeResult.formatted.items.push({
1714
- type: "tail",
1715
- code: "TAIL",
1716
- label: "Tail",
1717
- value: decodeResult.raw.tail
1718
- });
1719
- if (data2.length > 1) {
1720
- decodeResult.raw.flight_number = data2[1];
1721
- }
1722
- if (data2.length > 2) {
1723
- allKnownFields = false;
1724
- decodeResult.remaining.text += "," + data2.slice(2).join(",");
1725
- }
1877
+ processIdentification(decodeResult, fields[i].substring(2).split(","));
1726
1878
  } else if (fields[i].startsWith("LR")) {
1727
1879
  const data2 = fields[i].substring(2).split(",");
1728
1880
  const lr = processLR(decodeResult, data2);
@@ -1736,6 +1888,20 @@ var H1Helper = class {
1736
1888
  } else if (fields[i].startsWith("PS")) {
1737
1889
  allKnownFields = false;
1738
1890
  decodeResult.remaining.text += fields[i];
1891
+ } else if (fields[i].startsWith("AF")) {
1892
+ const af = processAirField(decodeResult, fields[i].substring(2).split(","));
1893
+ allKnownFields = allKnownFields && af;
1894
+ } else if (fields[i].startsWith("TD")) {
1895
+ const td = processTimeOfDeparture(decodeResult, fields[i].substring(2).split(","));
1896
+ allKnownFields = allKnownFields && td;
1897
+ } else if (fields[i].startsWith("FX")) {
1898
+ decodeResult.raw.free_text = fields[i].substring(2);
1899
+ decodeResult.formatted.items.push({
1900
+ type: "text",
1901
+ code: "TEXT",
1902
+ label: "Free Text",
1903
+ value: decodeResult.raw.free_text
1904
+ });
1739
1905
  } else {
1740
1906
  decodeResult.remaining.text += "/" + fields[i];
1741
1907
  allKnownFields = false;
@@ -1747,6 +1913,51 @@ var H1Helper = class {
1747
1913
  return allKnownFields;
1748
1914
  }
1749
1915
  };
1916
+ function processAirField(decodeResult, data) {
1917
+ if (data.length === 2) {
1918
+ ResultFormatter.departureAirport(decodeResult, data[0]);
1919
+ ResultFormatter.arrivalAirport(decodeResult, data[1]);
1920
+ return true;
1921
+ }
1922
+ decodeResult.remaining.text += "AF/" + data.join(",");
1923
+ return false;
1924
+ }
1925
+ function processTimeOfDeparture(decodeResult, data) {
1926
+ if (data.length === 2) {
1927
+ decodeResult.raw.plannedDepartureTime = data[0];
1928
+ decodeResult.formatted.items.push({
1929
+ type: "ptd",
1930
+ code: "ptd",
1931
+ label: "Planned Departure Time",
1932
+ value: `YYYY-MM-${data[0].substring(0, 2)}T${data[0].substring(2, 4)}:${data[0].substring(4)}:00Z`
1933
+ });
1934
+ decodeResult.raw.plannedDepartureTime = data[1];
1935
+ decodeResult.formatted.items.push({
1936
+ type: "etd",
1937
+ code: "etd",
1938
+ label: "Estimated Departure Time",
1939
+ value: `${data[1].substring(0, 2)}:${data[1].substring(2)}`
1940
+ });
1941
+ return true;
1942
+ }
1943
+ decodeResult.remaining.text += "/TD" + data.join(",");
1944
+ return false;
1945
+ }
1946
+ function processIdentification(decodeResult, data) {
1947
+ decodeResult.raw.tail = data[0];
1948
+ decodeResult.formatted.items.push({
1949
+ type: "tail",
1950
+ code: "TAIL",
1951
+ label: "Tail",
1952
+ value: decodeResult.raw.tail
1953
+ });
1954
+ if (data.length > 1) {
1955
+ decodeResult.raw.flight_number = data[1];
1956
+ }
1957
+ if (data.length > 2) {
1958
+ decodeResult.raw.mission_number = data[2];
1959
+ }
1960
+ }
1750
1961
  function processDT(decodeResult, data) {
1751
1962
  let allKnownFields = true;
1752
1963
  if (!decodeResult.raw.arrival_icao) {
@@ -1824,7 +2035,7 @@ function processDC(decodeResult, data) {
1824
2035
  }
1825
2036
  function processPS(decodeResult, data) {
1826
2037
  let allKnownFields = true;
1827
- const position = CoordinateUtils.decodeStringCoordinates(data[0]);
2038
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
1828
2039
  if (position) {
1829
2040
  decodeResult.raw.position = position;
1830
2041
  decodeResult.formatted.items.push({
@@ -1860,7 +2071,7 @@ function processPS(decodeResult, data) {
1860
2071
  }
1861
2072
  function processPosition2(decodeResult, data) {
1862
2073
  let allKnownFields = true;
1863
- const position = CoordinateUtils.decodeStringCoordinates(data[0]);
2074
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
1864
2075
  if (position) {
1865
2076
  decodeResult.raw.position = position;
1866
2077
  decodeResult.formatted.items.push({
@@ -1942,6 +2153,67 @@ var Label_H1_FPN = class extends DecoderPlugin {
1942
2153
  }
1943
2154
  };
1944
2155
 
2156
+ // lib/plugins/Label_H1_FTX.ts
2157
+ var Label_H1_FTX = class extends DecoderPlugin {
2158
+ name = "label-h1-ftx";
2159
+ qualifiers() {
2160
+ return {
2161
+ labels: ["H1"],
2162
+ preambles: ["FTX", "- #MDFTX"]
2163
+ };
2164
+ }
2165
+ decode(message, options = {}) {
2166
+ let decodeResult = this.defaultResult();
2167
+ decodeResult.decoder.name = this.name;
2168
+ decodeResult.formatted.description = "Free Text";
2169
+ decodeResult.message = message;
2170
+ decodeResult.remaining.text = "";
2171
+ const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2172
+ decodeResult.decoded = true;
2173
+ decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2174
+ if (decodeResult.formatted.items.length === 0) {
2175
+ if (options?.debug) {
2176
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
2177
+ }
2178
+ decodeResult.remaining.text = message.text;
2179
+ decodeResult.decoded = false;
2180
+ decodeResult.decoder.decodeLevel = "none";
2181
+ }
2182
+ return decodeResult;
2183
+ }
2184
+ };
2185
+
2186
+ // lib/plugins/Label_H1_INI.ts
2187
+ var Label_H1_INI = class extends DecoderPlugin {
2188
+ // eslint-disable-line camelcase
2189
+ name = "label-h1-ini";
2190
+ qualifiers() {
2191
+ return {
2192
+ labels: ["H1"],
2193
+ preambles: ["INI", "- #MDINI"]
2194
+ };
2195
+ }
2196
+ decode(message, options = {}) {
2197
+ const decodeResult = this.defaultResult();
2198
+ decodeResult.decoder.name = this.name;
2199
+ decodeResult.formatted.description = "Flight Plan Initial Report";
2200
+ decodeResult.message = message;
2201
+ decodeResult.remaining.text = "";
2202
+ const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2203
+ decodeResult.decoded = true;
2204
+ decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2205
+ if (decodeResult.formatted.items.length === 0) {
2206
+ if (options?.debug) {
2207
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
2208
+ }
2209
+ decodeResult.remaining.text = message.text;
2210
+ decodeResult.decoded = false;
2211
+ decodeResult.decoder.decodeLevel = "none";
2212
+ }
2213
+ return decodeResult;
2214
+ }
2215
+ };
2216
+
1945
2217
  // lib/plugins/Label_H1_OHMA.ts
1946
2218
  var zlib = __toESM(require("minizlib"));
1947
2219
  var Label_H1_OHMA = class extends DecoderPlugin {
@@ -2052,16 +2324,16 @@ var Label_H1_WRN = class extends DecoderPlugin {
2052
2324
  for (let i = 1; i < fields.length; i++) {
2053
2325
  const field = fields[i];
2054
2326
  if (field.startsWith("PN")) {
2055
- processUnknown(decodeResult, "/" + field);
2327
+ processUnknown2(decodeResult, "/" + field);
2056
2328
  } else {
2057
- processUnknown(decodeResult, "/" + field);
2329
+ processUnknown2(decodeResult, "/" + field);
2058
2330
  }
2059
2331
  }
2060
2332
  const data = parts[1].substring(0, 20);
2061
2333
  const msg = parts[1].substring(20);
2062
2334
  const datetime = data.substring(0, 12);
2063
2335
  const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
2064
- processUnknown(decodeResult, data.substring(12));
2336
+ processUnknown2(decodeResult, data.substring(12));
2065
2337
  decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
2066
2338
  decodeResult.raw.warning_message = msg;
2067
2339
  decodeResult.formatted.items.push({
@@ -2083,10 +2355,64 @@ var Label_H1_WRN = class extends DecoderPlugin {
2083
2355
  return decodeResult;
2084
2356
  }
2085
2357
  };
2086
- function processUnknown(decodeResult, value) {
2358
+ function processUnknown2(decodeResult, value) {
2087
2359
  decodeResult.remaining.text += value;
2088
2360
  }
2089
2361
 
2362
+ // lib/plugins/Label_HX.ts
2363
+ var Label_HX = class extends DecoderPlugin {
2364
+ name = "label-hx";
2365
+ qualifiers() {
2366
+ return {
2367
+ labels: ["HX"],
2368
+ preambles: ["RA FMT LOCATION", "RA FMT 43"]
2369
+ };
2370
+ }
2371
+ decode(message, options = {}) {
2372
+ const decodeResult = this.defaultResult();
2373
+ decodeResult.decoder.name = this.name;
2374
+ decodeResult.message = message;
2375
+ decodeResult.formatted.description = "Undelivered Uplink Report";
2376
+ const parts = message.text.split(" ");
2377
+ decodeResult.decoded = true;
2378
+ if (parts[2] === "LOCATION") {
2379
+ let latdir = parts[3].substring(0, 1);
2380
+ let latdeg = Number(parts[3].substring(1, 3));
2381
+ let latmin = Number(parts[3].substring(3, 7));
2382
+ let londir = parts[4].substring(0, 1);
2383
+ let londeg = Number(parts[4].substring(1, 4));
2384
+ let lonmin = Number(parts[4].substring(4, 8));
2385
+ decodeResult.raw.position = {
2386
+ latitude: (latdeg + latmin / 60) * (latdir === "N" ? 1 : -1),
2387
+ longitude: (londeg + lonmin / 60) * (londir === "E" ? 1 : -1)
2388
+ };
2389
+ decodeResult.remaining.text = parts.slice(5).join(" ");
2390
+ } else if (parts[2] === "43") {
2391
+ ResultFormatter.departureAirport(decodeResult, parts[3]);
2392
+ decodeResult.remaining.text = parts.slice(4).join(" ");
2393
+ } else {
2394
+ decodeResult.decoded = false;
2395
+ }
2396
+ if (decodeResult.raw.position) {
2397
+ decodeResult.formatted.items.push({
2398
+ type: "aircraft_position",
2399
+ code: "POS",
2400
+ label: "Aircraft Position",
2401
+ value: CoordinateUtils.coordinateString(decodeResult.raw.position)
2402
+ });
2403
+ }
2404
+ if (decodeResult.decoded) {
2405
+ if (decodeResult.remaining.text === "")
2406
+ decodeResult.decoder.decodeLevel = "full";
2407
+ else
2408
+ decodeResult.decoder.decodeLevel = "partial";
2409
+ } else {
2410
+ decodeResult.decoder.decodeLevel = "none";
2411
+ }
2412
+ return decodeResult;
2413
+ }
2414
+ };
2415
+
2090
2416
  // lib/plugins/Label_SQ.ts
2091
2417
  var Label_SQ = class extends DecoderPlugin {
2092
2418
  name = "label-sq";
@@ -2127,11 +2453,13 @@ var Label_SQ = class extends DecoderPlugin {
2127
2453
  decodeResult.formatted.items = [
2128
2454
  {
2129
2455
  type: "network",
2456
+ code: "NETT",
2130
2457
  label: "Network",
2131
2458
  value: formattedNetwork
2132
2459
  },
2133
2460
  {
2134
2461
  type: "version",
2462
+ code: "VER",
2135
2463
  label: "Version",
2136
2464
  value: decodeResult.raw.version
2137
2465
  }
@@ -2140,6 +2468,7 @@ var Label_SQ = class extends DecoderPlugin {
2140
2468
  if (decodeResult.raw.groundStation.icaoCode && decodeResult.raw.groundStation.number) {
2141
2469
  decodeResult.formatted.items.push({
2142
2470
  type: "ground_station",
2471
+ code: "GNDSTN",
2143
2472
  label: "Ground Station",
2144
2473
  value: `${decodeResult.raw.groundStation.icaoCode}${decodeResult.raw.groundStation.number}`
2145
2474
  });
@@ -2147,6 +2476,7 @@ var Label_SQ = class extends DecoderPlugin {
2147
2476
  if (decodeResult.raw.groundStation.iataCode) {
2148
2477
  decodeResult.formatted.items.push({
2149
2478
  type: "iataCode",
2479
+ code: "IATA",
2150
2480
  label: "IATA",
2151
2481
  value: decodeResult.raw.groundStation.iataCode
2152
2482
  });
@@ -2154,6 +2484,7 @@ var Label_SQ = class extends DecoderPlugin {
2154
2484
  if (decodeResult.raw.groundStation.icaoCode) {
2155
2485
  decodeResult.formatted.items.push({
2156
2486
  type: "icaoCode",
2487
+ code: "ICAO",
2157
2488
  label: "ICAO",
2158
2489
  value: decodeResult.raw.groundStation.icaoCode
2159
2490
  });
@@ -2161,6 +2492,7 @@ var Label_SQ = class extends DecoderPlugin {
2161
2492
  if (decodeResult.raw.groundStation.coordinates.latitude) {
2162
2493
  decodeResult.formatted.items.push({
2163
2494
  type: "coordinates",
2495
+ code: "COORD",
2164
2496
  label: "Ground Station Location",
2165
2497
  value: `${decodeResult.raw.groundStation.coordinates.latitude}, ${decodeResult.raw.groundStation.coordinates.longitude}`
2166
2498
  });
@@ -2168,6 +2500,7 @@ var Label_SQ = class extends DecoderPlugin {
2168
2500
  if (decodeResult.raw.groundStation.airport) {
2169
2501
  decodeResult.formatted.items.push({
2170
2502
  type: "airport",
2503
+ code: "APT",
2171
2504
  label: "Airport",
2172
2505
  value: `${decodeResult.raw.groundStation.airport.name} (${decodeResult.raw.groundStation.airport.icao}) in ${decodeResult.raw.groundStation.airport.location}`
2173
2506
  });
@@ -2176,6 +2509,7 @@ var Label_SQ = class extends DecoderPlugin {
2176
2509
  if (decodeResult.raw.vdlFrequency) {
2177
2510
  decodeResult.formatted.items.push({
2178
2511
  type: "vdlFrequency",
2512
+ code: "VDLFRQ",
2179
2513
  label: "VDL Frequency",
2180
2514
  value: `${decodeResult.raw.vdlFrequency} MHz`
2181
2515
  });
@@ -2332,31 +2666,48 @@ var Label_QQ = class extends DecoderPlugin {
2332
2666
  decode(message, options = {}) {
2333
2667
  const decodeResult = this.defaultResult();
2334
2668
  decodeResult.decoder.name = this.name;
2335
- decodeResult.raw.origin = message.text.substring(0, 4);
2336
- decodeResult.raw.destination = message.text.substring(4, 8);
2337
- decodeResult.raw.wheels_off = message.text.substring(8, 12);
2338
- decodeResult.remaining.text = message.text.substring(12);
2669
+ decodeResult.message = message;
2339
2670
  decodeResult.formatted.description = "OFF Report";
2340
- decodeResult.formatted.items = [
2341
- {
2342
- type: "origin",
2343
- code: "ORG",
2344
- label: "Origin",
2345
- value: decodeResult.raw.origin
2346
- },
2347
- {
2348
- type: "destination",
2349
- code: "DST",
2350
- label: "Destination",
2351
- value: decodeResult.raw.destination
2352
- },
2353
- {
2354
- type: "wheels_off",
2355
- code: "WOFF",
2356
- label: "Wheels OFF",
2357
- value: decodeResult.raw.wheels_off
2671
+ ResultFormatter.departureAirport(decodeResult, message.text.substring(0, 4));
2672
+ ResultFormatter.arrivalAirport(decodeResult, message.text.substring(4, 8));
2673
+ decodeResult.raw.wheels_off = message.text.substring(8, 12);
2674
+ if (message.text.substring(12, 19) === "\r\n001FE") {
2675
+ decodeResult.raw.day_of_month = message.text.substring(19, 21);
2676
+ decodeResult.raw.wheels_off = message.text.substring(21, 27);
2677
+ let latdir = message.text.substring(27, 28);
2678
+ let latdeg = Number(message.text.substring(28, 30));
2679
+ let latmin = Number(message.text.substring(30, 34));
2680
+ let londir = message.text.substring(34, 35);
2681
+ let londeg = Number(message.text.substring(35, 38));
2682
+ let lonmin = Number(message.text.substring(38, 42));
2683
+ decodeResult.raw.position = {
2684
+ latitude: (latdeg + latmin / 60) * (latdir === "N" ? 1 : -1),
2685
+ longitude: (londeg + lonmin / 60) * (londir === "E" ? 1 : -1)
2686
+ };
2687
+ decodeResult.remaining.text = message.text.substring(42, 45);
2688
+ if (decodeResult.remaining.text !== "---") {
2689
+ ResultFormatter.groundspeed(decodeResult, message.text.substring(45, 48));
2690
+ } else {
2691
+ ResultFormatter.unknown(decodeResult, message.text.substring(45, 48));
2358
2692
  }
2359
- ];
2693
+ ResultFormatter.unknown(decodeResult, message.text.substring(48));
2694
+ } else {
2695
+ decodeResult.remaining.text = message.text.substring(12);
2696
+ }
2697
+ decodeResult.formatted.items.push({
2698
+ type: "wheels_off",
2699
+ code: "WOFF",
2700
+ label: "Wheels OFF",
2701
+ value: decodeResult.raw.wheels_off
2702
+ });
2703
+ if (decodeResult.raw.position) {
2704
+ decodeResult.formatted.items.push({
2705
+ type: "aircraft_position",
2706
+ code: "POS",
2707
+ label: "Aircraft Position",
2708
+ value: CoordinateUtils.coordinateString(decodeResult.raw.position)
2709
+ });
2710
+ }
2360
2711
  decodeResult.decoded = true;
2361
2712
  if (decodeResult.remaining.text === "")
2362
2713
  decodeResult.decoder.decodeLevel = "full";
@@ -3179,6 +3530,9 @@ var MessageDecoder = class {
3179
3530
  this.debug = false;
3180
3531
  this.registerPlugin(new Label_ColonComma(this));
3181
3532
  this.registerPlugin(new Label_5Z(this));
3533
+ this.registerPlugin(new Label_10_LDR(this));
3534
+ this.registerPlugin(new Label_10_POS(this));
3535
+ this.registerPlugin(new Label_10_Slash(this));
3182
3536
  this.registerPlugin(new Label_12_N_Space(this));
3183
3537
  this.registerPlugin(new Label_15(this));
3184
3538
  this.registerPlugin(new Label_15_FST(this));
@@ -3193,9 +3547,13 @@ var MessageDecoder = class {
3193
3547
  this.registerPlugin(new Label_44_POS(this));
3194
3548
  this.registerPlugin(new Label_B6_Forwardslash(this));
3195
3549
  this.registerPlugin(new Label_H1_FPN(this));
3550
+ this.registerPlugin(new Label_H1_FLR(this));
3551
+ this.registerPlugin(new Label_H1_FTX(this));
3552
+ this.registerPlugin(new Label_H1_INI(this));
3196
3553
  this.registerPlugin(new Label_H1_OHMA(this));
3197
3554
  this.registerPlugin(new Label_H1_POS(this));
3198
3555
  this.registerPlugin(new Label_H1_WRN(this));
3556
+ this.registerPlugin(new Label_HX(this));
3199
3557
  this.registerPlugin(new Label_80(this));
3200
3558
  this.registerPlugin(new Label_8E(this));
3201
3559
  this.registerPlugin(new Label_1M_Slash(this));