@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.mjs CHANGED
@@ -60,6 +60,192 @@ 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
+ };
121
+
122
+ // lib/utils/result_formatter.ts
123
+ var ResultFormatter = class {
124
+ static position(decodeResult, value) {
125
+ decodeResult.raw.position = value;
126
+ decodeResult.formatted.items.push({
127
+ type: "aircraft_position",
128
+ code: "POS",
129
+ label: "Aircraft Position",
130
+ value: CoordinateUtils.coordinateString(value)
131
+ });
132
+ }
133
+ static altitude(decodeResult, value) {
134
+ decodeResult.raw.altitude = value;
135
+ decodeResult.formatted.items.push({
136
+ type: "altitude",
137
+ code: "ALT",
138
+ label: "Altitude",
139
+ value: `${decodeResult.raw.altitude} feet`
140
+ });
141
+ }
142
+ static flightNumber(decodeResult, value) {
143
+ decodeResult.raw.flight_number = value;
144
+ }
145
+ static departureAirport(decodeResult, value) {
146
+ decodeResult.raw.departure_icao = value;
147
+ decodeResult.formatted.items.push({
148
+ type: "origin",
149
+ code: "ORG",
150
+ label: "Origin",
151
+ value: decodeResult.raw.departure_icao
152
+ });
153
+ }
154
+ static departureRunway(decodeResult, value) {
155
+ decodeResult.raw.departure_runway = value;
156
+ decodeResult.formatted.items.push({
157
+ type: "runway",
158
+ code: "DEPRWY",
159
+ label: "Departure Runway",
160
+ value: decodeResult.raw.departure_runway
161
+ });
162
+ }
163
+ static arrivalAirport(decodeResult, value) {
164
+ decodeResult.raw.arrival_icao = value;
165
+ decodeResult.formatted.items.push({
166
+ type: "destination",
167
+ code: "DST",
168
+ label: "Destination",
169
+ value: decodeResult.raw.arrival_icao
170
+ });
171
+ }
172
+ static eta(decodeResult, value) {
173
+ decodeResult.raw.eta_time = value;
174
+ decodeResult.formatted.items.push({
175
+ type: "eta",
176
+ code: "ETA",
177
+ label: "Estimated Time of Arrival",
178
+ value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
179
+ });
180
+ }
181
+ static arrivalRunway(decodeResult, value) {
182
+ decodeResult.raw.arrival_runway = value;
183
+ decodeResult.formatted.items.push({
184
+ type: "runway",
185
+ code: "ARWY",
186
+ label: "Arrival Runway",
187
+ value: decodeResult.raw.arrival_runway
188
+ });
189
+ }
190
+ static currentFuel(decodeResult, value) {
191
+ decodeResult.raw.fuel_on_board = value;
192
+ decodeResult.formatted.items.push({
193
+ type: "fuel_on_board",
194
+ code: "FOB",
195
+ label: "Fuel On Board",
196
+ value: decodeResult.raw.fuel_on_board.toString()
197
+ });
198
+ }
199
+ static remainingFuel(decodeResult, value) {
200
+ decodeResult.raw.fuel_remaining = value;
201
+ decodeResult.formatted.items.push({
202
+ type: "fuel_remaining",
203
+ code: "FUEL",
204
+ label: "Fuel Remaining",
205
+ value: decodeResult.raw.fuel_remaining.toString()
206
+ });
207
+ }
208
+ static checksum(decodeResult, value) {
209
+ decodeResult.raw.checksum = Number("0x" + value);
210
+ decodeResult.formatted.items.push({
211
+ type: "message_checksum",
212
+ code: "CHECKSUM",
213
+ label: "Message Checksum",
214
+ value: "0x" + ("0000" + decodeResult.raw.checksum.toString(16)).slice(-4)
215
+ });
216
+ }
217
+ static groundspeed(decodeResult, value) {
218
+ decodeResult.raw.groundspeed = value;
219
+ decodeResult.formatted.items.push({
220
+ type: "aircraft_groundspeed",
221
+ code: "GSPD",
222
+ label: "Aircraft Groundspeed",
223
+ value: `${decodeResult.raw.groundspeed}`
224
+ });
225
+ }
226
+ static temperature(decodeResult, value) {
227
+ decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
228
+ decodeResult.formatted.items.push({
229
+ type: "outside_air_temperature",
230
+ code: "OATEMP",
231
+ label: "Outside Air Temperature (C)",
232
+ value: `${decodeResult.raw.outside_air_temperature}`
233
+ });
234
+ }
235
+ static heading(decodeResult, value) {
236
+ decodeResult.raw.heading = value;
237
+ decodeResult.formatted.items.push({
238
+ type: "heading",
239
+ code: "HDG",
240
+ label: "Heading",
241
+ value: `${decodeResult.raw.heading}`
242
+ });
243
+ }
244
+ static unknown(decodeResult, value) {
245
+ decodeResult.remaining.text += "," + value;
246
+ }
247
+ };
248
+
63
249
  // lib/plugins/Label_5Z.ts
64
250
  var Label_5Z = class extends DecoderPlugin {
65
251
  name = "label-5z";
@@ -119,34 +305,9 @@ var Label_5Z = class extends DecoderPlugin {
119
305
  const rdcRegex = /^(?<from>\w\w\w)(?<to>\w\w\w) (?<unknown1>\d\d) R(?<runway>.+) G(?<unknown2>.+)$/;
120
306
  results = remainder.match(rdcRegex);
121
307
  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
- });
308
+ ResultFormatter.departureAirport(decodeResult, results.groups.from);
309
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.to);
310
+ ResultFormatter.arrivalRunway(decodeResult, results.groups.runway);
150
311
  } else {
151
312
  if (options.debug) {
152
313
  console.log(`Decoder: Unkown 5Z RDC format: ${remainder}`);
@@ -169,62 +330,273 @@ var Label_5Z = class extends DecoderPlugin {
169
330
  }
170
331
  };
171
332
 
172
- // lib/utils/coordinate_utils.ts
173
- var CoordinateUtils = class {
333
+ // lib/plugins/Label_10_LDR.ts
334
+ var Label_10_LDR = class extends DecoderPlugin {
335
+ // eslint-disable-line camelcase
336
+ name = "label-10-ldr";
337
+ qualifiers() {
338
+ return {
339
+ labels: ["10"],
340
+ preambles: ["LDR"]
341
+ };
342
+ }
343
+ decode(message, options = {}) {
344
+ const decodeResult = this.defaultResult();
345
+ decodeResult.decoder.name = this.name;
346
+ decodeResult.formatted.description = "Position Report";
347
+ decodeResult.message = message;
348
+ const parts = message.text.split(",");
349
+ if (parts.length < 17) {
350
+ if (options?.debug) {
351
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
352
+ }
353
+ decodeResult.remaining.text = message.text;
354
+ decodeResult.decoded = false;
355
+ decodeResult.decoder.decodeLevel = "none";
356
+ return decodeResult;
357
+ }
358
+ const lat = parts[5];
359
+ const lon = parts[6];
360
+ const position = {
361
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1).trim()),
362
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim())
363
+ };
364
+ ResultFormatter.position(decodeResult, position);
365
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
366
+ ResultFormatter.departureAirport(decodeResult, parts[9]);
367
+ ResultFormatter.arrivalAirport(decodeResult, parts[10]);
368
+ ResultFormatter.arrivalRunway(decodeResult, parts[12].split("/")[0]);
369
+ decodeResult.remaining.text = [...parts.slice(0, 5), parts[11], ...parts.slice(13)].join(",");
370
+ decodeResult.decoded = true;
371
+ decodeResult.decoder.decodeLevel = "partial";
372
+ return decodeResult;
373
+ }
374
+ };
375
+
376
+ // lib/plugins/Label_10_POS.ts
377
+ var Label_10_POS = class extends DecoderPlugin {
378
+ // eslint-disable-line camelcase
379
+ name = "label-10-pos";
380
+ qualifiers() {
381
+ return {
382
+ labels: ["10"],
383
+ preambles: ["POS"]
384
+ };
385
+ }
386
+ decode(message, options = {}) {
387
+ const decodeResult = this.defaultResult();
388
+ decodeResult.decoder.name = this.name;
389
+ decodeResult.formatted.description = "Position Report";
390
+ decodeResult.message = message;
391
+ const parts = message.text.split(",");
392
+ if (parts.length !== 12) {
393
+ if (options?.debug) {
394
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
395
+ }
396
+ decodeResult.remaining.text = message.text;
397
+ decodeResult.decoded = false;
398
+ decodeResult.decoder.decodeLevel = "none";
399
+ return decodeResult;
400
+ }
401
+ const lat = parts[1].trim();
402
+ const lon = parts[2].trim();
403
+ const position = {
404
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1).trim()) / 100,
405
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim()) / 100
406
+ };
407
+ ResultFormatter.position(decodeResult, position);
408
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
409
+ decodeResult.remaining.text = [parts[0], ...parts.slice(3, 7), ...parts.slice(8)].join(",");
410
+ decodeResult.decoded = true;
411
+ decodeResult.decoder.decodeLevel = "partial";
412
+ return decodeResult;
413
+ }
414
+ };
415
+
416
+ // lib/DateTimeUtils.ts
417
+ var DateTimeUtils = class {
418
+ // Expects a four digit UTC time string (HHMM)
419
+ static UTCToString(UTCString) {
420
+ let utcDate = /* @__PURE__ */ new Date();
421
+ utcDate.setUTCHours(+UTCString.substr(0, 2), +UTCString.substr(2, 2), 0);
422
+ return utcDate.toTimeString();
423
+ }
424
+ // Expects a six digit date string and a four digit UTC time string
425
+ // (DDMMYY) (HHMM)
426
+ static UTCDateTimeToString(dateString, timeString) {
427
+ let utcDate = /* @__PURE__ */ new Date();
428
+ utcDate.setUTCDate(+dateString.substr(0, 2));
429
+ utcDate.setUTCMonth(+dateString.substr(2, 2));
430
+ if (dateString.length === 6) {
431
+ utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
432
+ }
433
+ if (timeString.length === 6) {
434
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2));
435
+ } else {
436
+ utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0);
437
+ }
438
+ return utcDate.toUTCString();
439
+ }
174
440
  /**
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
441
  *
178
- * @returns An object with latitude and longitude properties
442
+ * @param time HHMMSS
443
+ * @returns seconds since midnight
179
444
  */
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);
445
+ static convertHHMMSSToTod(time) {
446
+ const h = Number(time.substring(0, 2));
447
+ const m = Number(time.substring(2, 4));
448
+ const s = Number(time.substring(4, 6));
449
+ const tod = h * 3600 + m * 60 + s;
450
+ return tod;
451
+ }
452
+ /**
453
+ *
454
+ * @param time HHMMSS
455
+ * @param date MMDDYY or MMDDYYYY
456
+ * @returns seconds since epoch
457
+ */
458
+ static convertDateTimeToEpoch(time, date) {
459
+ if (date.length === 6) {
460
+ date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
188
461
  }
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;
462
+ 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`;
463
+ const millis = Date.parse(timestamp);
464
+ return millis / 1e3;
465
+ }
466
+ };
467
+
468
+ // lib/utils/route_utils.ts
469
+ var RouteUtils = class _RouteUtils {
470
+ static routeToString(route) {
471
+ let str = "";
472
+ if (route.name) {
473
+ str += route.name;
194
474
  }
195
- return results;
475
+ if (route.runway) {
476
+ str += `(${route.runway})`;
477
+ }
478
+ if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
479
+ str += " starting at ";
480
+ } else if (str.length !== 0 && route.waypoints) {
481
+ str += ": ";
482
+ }
483
+ if (route.waypoints) {
484
+ str += _RouteUtils.waypointsToString(route.waypoints);
485
+ }
486
+ return str;
196
487
  }
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);
488
+ static waypointToString(waypoint) {
489
+ let s = waypoint.name;
490
+ if (waypoint.latitude && waypoint.longitude) {
491
+ s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
492
+ }
493
+ if (waypoint.offset) {
494
+ s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
495
+ }
496
+ if (waypoint.time && waypoint.timeFormat) {
497
+ s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
498
+ }
499
+ return s;
500
+ }
501
+ static getWaypoint(leg) {
502
+ const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
503
+ if (regex?.length == 4) {
504
+ return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
505
+ }
506
+ const waypoint = leg.split(",");
507
+ if (waypoint.length == 2) {
508
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
509
+ if (position) {
510
+ return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
511
+ }
512
+ }
513
+ if (leg.length == 13 || leg.length == 14) {
514
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
515
+ const name = waypoint.length == 2 ? waypoint[0] : "";
516
+ if (position) {
517
+ return { name, latitude: position.latitude, longitude: position.longitude };
518
+ }
519
+ }
520
+ return { name: leg };
521
+ }
522
+ // move out if we want public
523
+ static timestampToString(time, format) {
524
+ const date = new Date(time * 1e3);
525
+ if (format == "tod") {
526
+ return date.toISOString().slice(11, 19);
211
527
  }
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;
528
+ return date.toISOString().slice(0, -5) + "Z";
529
+ }
530
+ static waypointsToString(waypoints) {
531
+ let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
532
+ if (str.startsWith(" > ")) {
533
+ str = ">>" + str.slice(2);
221
534
  }
222
- return results;
535
+ return str;
223
536
  }
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}`;
537
+ };
538
+
539
+ // lib/plugins/Label_10_Slash.ts
540
+ var Label_10_Slash = class extends DecoderPlugin {
541
+ // eslint-disable-line camelcase
542
+ name = "label-10-slash";
543
+ qualifiers() {
544
+ return {
545
+ labels: ["10"],
546
+ preambles: ["/"]
547
+ };
548
+ }
549
+ decode(message, options = {}) {
550
+ const decodeResult = this.defaultResult();
551
+ decodeResult.decoder.name = this.name;
552
+ decodeResult.formatted.description = "Position Report";
553
+ decodeResult.message = message;
554
+ const parts = message.text.split("/");
555
+ if (parts.length < 17) {
556
+ if (options?.debug) {
557
+ console.log(`Decoder: Unknown 10 message: ${message.text}`);
558
+ }
559
+ decodeResult.remaining.text = message.text;
560
+ decodeResult.decoded = false;
561
+ decodeResult.decoder.decodeLevel = "none";
562
+ return decodeResult;
563
+ }
564
+ const lat = parts[1];
565
+ const lon = parts[2];
566
+ const position = {
567
+ latitude: (lat[0] === "N" ? 1 : -1) * Number(lat.substring(1)),
568
+ longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1))
569
+ };
570
+ ResultFormatter.position(decodeResult, position);
571
+ ResultFormatter.heading(decodeResult, Number(parts[5]));
572
+ ResultFormatter.altitude(decodeResult, 100 * Number(parts[6]));
573
+ ResultFormatter.arrivalAirport(decodeResult, parts[7]);
574
+ ResultFormatter.eta(decodeResult, parts[8] + "00");
575
+ const waypoints = [{
576
+ name: parts[11]
577
+ }, {
578
+ name: parts[12],
579
+ time: DateTimeUtils.convertHHMMSSToTod(parts[13] + "00"),
580
+ timeFormat: "tod"
581
+ }, {
582
+ name: parts[14],
583
+ time: DateTimeUtils.convertHHMMSSToTod(parts[15] + "00"),
584
+ timeFormat: "tod"
585
+ }];
586
+ decodeResult.raw.route = { waypoints };
587
+ decodeResult.formatted.items.push({
588
+ type: "aircraft_route",
589
+ code: "ROUTE",
590
+ label: "Aircraft Route",
591
+ value: RouteUtils.routeToString(decodeResult.raw.route)
592
+ });
593
+ if (parts[16]) {
594
+ ResultFormatter.departureAirport(decodeResult, parts[16]);
595
+ }
596
+ decodeResult.remaining.text = [parts[3], parts[4], ...parts.slice(9, 11), ...parts.slice(17)].join("/");
597
+ decodeResult.decoded = true;
598
+ decodeResult.decoder.decodeLevel = "partial";
599
+ return decodeResult;
228
600
  }
229
601
  };
230
602
 
@@ -253,19 +625,14 @@ var Label_12_N_Space = class extends DecoderPlugin {
253
625
  latitude: Number(results.groups.lat_coord) * (results.groups.lat == "N" ? 1 : -1),
254
626
  longitude: Number(results.groups.long_coord) * (results.groups.long == "E" ? 1 : -1)
255
627
  };
256
- decodeResult.raw.flight_level = results.groups.alt == "GRD" || results.groups.alt == "***" ? "0" : Number(results.groups.alt);
628
+ const altitude = results.groups.alt == "GRD" || results.groups.alt == "***" ? 0 : Number(results.groups.alt);
257
629
  decodeResult.formatted.items.push({
258
630
  type: "aircraft_position",
259
631
  code: "POS",
260
632
  label: "Aircraft Position",
261
633
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
262
634
  });
263
- decodeResult.formatted.items.push({
264
- type: "flight_level",
265
- code: "FL",
266
- label: "Flight Level",
267
- value: decodeResult.raw.flight_level
268
- });
635
+ ResultFormatter.altitude(decodeResult, altitude);
269
636
  decodeResult.remaining.text = `,${results.groups.unkwn1} ,${results.groups.unkwn2}, ${results.groups.unkwn3}`;
270
637
  decodeResult.decoded = true;
271
638
  decodeResult.decoder.decodeLevel = "partial";
@@ -327,39 +694,35 @@ var Label_15_FST = class extends DecoderPlugin {
327
694
  decodeResult.decoder.name = this.name;
328
695
  decodeResult.formatted.description = "Position Report";
329
696
  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);
697
+ const parts = message.text.split(" ");
698
+ const header = parts[0];
699
+ decodeResult.raw.departure_icao = header.substring(5, 9);
700
+ decodeResult.raw.arrival_icao = header.substring(9, 13);
701
+ const stringCoords = header.substring(13);
333
702
  const firstChar = stringCoords.substring(0, 1);
334
703
  const middleChar = stringCoords.substring(7, 8);
335
704
  decodeResult.raw.position = {};
336
705
  if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
337
706
  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);
707
+ decodeResult.raw.position.longitude = Number(stringCoords.substring(8, 15)) / 1e4 * (middleChar === "W" ? -1 : 1);
708
+ decodeResult.raw.altitude = Number(stringCoords.substring(15)) * 100;
339
709
  } else {
340
710
  decodeResult.decoded = false;
341
711
  decodeResult.decoder.decodeLevel = "none";
342
712
  return decodeResult;
343
713
  }
344
714
  decodeResult.formatted.items.push({
345
- type: "position",
346
- label: "Position",
715
+ type: "aircraft_position",
716
+ code: "POS",
717
+ label: "Aircraft Position",
347
718
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
348
719
  });
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
- });
720
+ ResultFormatter.altitude(decodeResult, decodeResult.raw.altitude);
721
+ ResultFormatter.departureAirport(decodeResult, decodeResult.raw.departure_icao);
722
+ ResultFormatter.arrivalAirport(decodeResult, decodeResult.raw.arrival_icao);
723
+ decodeResult.remaining.text = parts.slice(1).join(" ");
361
724
  decodeResult.decoded = true;
362
- decodeResult.decoder.decodeLevel = "full";
725
+ decodeResult.decoder.decodeLevel = "partial";
363
726
  return decodeResult;
364
727
  }
365
728
  };
@@ -390,19 +753,14 @@ var Label_16_N_Space = class extends DecoderPlugin {
390
753
  latitude: Number(results.groups.lat_coord) * (results.groups.lat == "N" ? 1 : -1),
391
754
  longitude: Number(results.groups.long_coord) * (results.groups.long == "E" ? 1 : -1)
392
755
  };
393
- decodeResult.raw.flight_level = results.groups.alt == "GRD" || results.groups.alt == "***" ? "0" : Number(results.groups.alt);
756
+ const altitude = results.groups.alt == "GRD" || results.groups.alt == "***" ? 0 : Number(results.groups.alt);
394
757
  decodeResult.formatted.items.push({
395
758
  type: "aircraft_position",
396
759
  code: "POS",
397
760
  label: "Aircraft Position",
398
761
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
399
762
  });
400
- decodeResult.formatted.items.push({
401
- type: "flight_level",
402
- code: "FL",
403
- label: "Flight Level",
404
- value: decodeResult.raw.flight_level
405
- });
763
+ ResultFormatter.altitude(decodeResult, altitude);
406
764
  decodeResult.remaining.text = `,${results.groups.unkwn1} ,${results.groups.unkwn2}`;
407
765
  decodeResult.decoded = true;
408
766
  decodeResult.decoder.decodeLevel = "partial";
@@ -435,58 +793,6 @@ var Label_16_N_Space = class extends DecoderPlugin {
435
793
  }
436
794
  };
437
795
 
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
796
  // lib/plugins/Label_1M_Slash.ts
491
797
  var Label_1M_Slash = class extends DecoderPlugin {
492
798
  name = "label-1m-slash";
@@ -563,6 +869,7 @@ var Label_20_POS = class extends DecoderPlugin {
563
869
  if (decodeResult.raw.position) {
564
870
  decodeResult.formatted.items.push({
565
871
  type: "position",
872
+ code: "POS",
566
873
  label: "Position",
567
874
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
568
875
  });
@@ -576,6 +883,7 @@ var Label_20_POS = class extends DecoderPlugin {
576
883
  if (decodeResult.raw.position) {
577
884
  decodeResult.formatted.items.push({
578
885
  type: "position",
886
+ code: "POS",
579
887
  label: "Position",
580
888
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
581
889
  });
@@ -615,7 +923,7 @@ var Label_21_POS = class extends DecoderPlugin {
615
923
  processAlt(decodeResult, fields[3]);
616
924
  processTemp(decodeResult, fields[6]);
617
925
  processArrvApt(decodeResult, fields[8]);
618
- decodeResult.raw.remaining = [
926
+ decodeResult.remaining.text = [
619
927
  fields[1],
620
928
  fields[2],
621
929
  fields[4],
@@ -979,9 +1287,7 @@ var Label_44_POS = class extends DecoderPlugin {
979
1287
  console.log(results.groups);
980
1288
  }
981
1289
  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;
1290
+ 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
1291
  decodeResult.raw.current_time = Date.parse(
986
1292
  (/* @__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
1293
  );
@@ -999,24 +1305,9 @@ var Label_44_POS = class extends DecoderPlugin {
999
1305
  value: CoordinateUtils.coordinateString(decodeResult.raw.position)
1000
1306
  });
1001
1307
  }
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
- });
1308
+ ResultFormatter.departureAirport(decodeResult, results.groups.departure_icao);
1309
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
1310
+ ResultFormatter.altitude(decodeResult, flight_level * 100);
1020
1311
  }
1021
1312
  decodeResult.decoded = true;
1022
1313
  decodeResult.decoder.decodeLevel = "full";
@@ -1119,13 +1410,9 @@ var Label_80 = class extends DecoderPlugin {
1119
1410
  break;
1120
1411
  }
1121
1412
  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
- });
1413
+ const flight_level = match.groups.value;
1414
+ decodeResult.raw.altitude = flight_level * 100;
1415
+ ResultFormatter.altitude(decodeResult, decodeResult.raw.altitude);
1129
1416
  break;
1130
1417
  }
1131
1418
  case "FOB": {
@@ -1291,194 +1578,73 @@ var Label_ColonComma = class extends DecoderPlugin {
1291
1578
  const decodeResult = this.defaultResult();
1292
1579
  decodeResult.decoder.name = this.name;
1293
1580
  decodeResult.raw.frequency = Number(message.text) / 1e3;
1294
- decodeResult.formatted.description = "Aircraft Transceiver Frequency Change";
1295
- decodeResult.formatted.items.push({
1296
- type: "frequency",
1297
- label: "Frequency",
1298
- value: `${decodeResult.raw.frequency} MHz`
1299
- });
1300
- decodeResult.decoded = true;
1301
- decodeResult.decoder.decodeLevel = "full";
1302
- return decodeResult;
1303
- }
1304
- };
1305
-
1306
- // lib/utils/result_formatter.ts
1307
- var ResultFormatter = class {
1308
- static altitude(decodeResult, value) {
1309
- decodeResult.raw.altitude = value;
1310
- decodeResult.formatted.items.push({
1311
- type: "altitude",
1312
- code: "ALT",
1313
- label: "Altitude",
1314
- value: `${decodeResult.raw.altitude} feet`
1315
- });
1316
- }
1317
- static flightNumber(decodeResult, value) {
1318
- decodeResult.raw.flight_number = value;
1319
- }
1320
- static departureAirport(decodeResult, value) {
1321
- decodeResult.raw.departure_icao = value;
1322
- decodeResult.formatted.items.push({
1323
- type: "origin",
1324
- code: "ORG",
1325
- label: "Origin",
1326
- value: decodeResult.raw.departure_icao
1327
- });
1328
- }
1329
- static departureRunway(decodeResult, value) {
1330
- decodeResult.raw.departure_runway = value;
1331
- decodeResult.formatted.items.push({
1332
- type: "runway",
1333
- code: "DEPRWY",
1334
- label: "Departure Runway",
1335
- value: decodeResult.raw.departure_runway
1336
- });
1337
- }
1338
- static arrivalAirport(decodeResult, value) {
1339
- decodeResult.raw.arrival_icao = value;
1340
- decodeResult.formatted.items.push({
1341
- type: "destination",
1342
- code: "DST",
1343
- label: "Destination",
1344
- value: decodeResult.raw.arrival_icao
1345
- });
1346
- }
1347
- static eta(decodeResult, value) {
1348
- decodeResult.formatted.items.push({
1349
- type: "eta",
1350
- code: "ETA",
1351
- label: "Estimated Time of Arrival",
1352
- value: value.substring(0, 2) + ":" + value.substring(2, 4) + ":" + value.substring(4, 6)
1353
- });
1354
- }
1355
- static arrivalRunway(decodeResult, value) {
1356
- decodeResult.raw.arrival_runway = value;
1357
- decodeResult.formatted.items.push({
1358
- type: "runway",
1359
- label: "Arrival Runway",
1360
- value: decodeResult.raw.arrival_runway
1361
- });
1362
- }
1363
- static currentFuel(decodeResult, value) {
1364
- decodeResult.raw.fuel_on_board = value;
1365
- decodeResult.formatted.items.push({
1366
- type: "fuel_on_board",
1367
- code: "FOB",
1368
- label: "Fuel On Board",
1369
- value: decodeResult.raw.fuel_on_board.toString()
1370
- });
1371
- }
1372
- static remainingFuel(decodeResult, value) {
1373
- decodeResult.raw.fuel_remaining = value;
1374
- decodeResult.formatted.items.push({
1375
- type: "fuel_remaining",
1376
- label: "Fuel Remaining",
1377
- value: decodeResult.raw.fuel_remaining.toString()
1378
- });
1379
- }
1380
- static checksum(decodeResult, value) {
1381
- decodeResult.raw.checksum = Number("0x" + value);
1382
- decodeResult.formatted.items.push({
1383
- type: "message_checksum",
1384
- code: "CHECKSUM",
1385
- label: "Message Checksum",
1386
- value: "0x" + ("0000" + decodeResult.raw.checksum.toString(16)).slice(-4)
1387
- });
1388
- }
1389
- static groundspeed(decodeResult, value) {
1390
- decodeResult.raw.groundspeed = value;
1391
- decodeResult.formatted.items.push({
1392
- type: "aircraft_groundspeed",
1393
- code: "GSPD",
1394
- label: "Aircraft Groundspeed",
1395
- value: `${decodeResult.raw.groundspeed}`
1396
- });
1397
- }
1398
- static temperature(decodeResult, value) {
1399
- decodeResult.raw.outside_air_temperature = Number(value.substring(1)) * (value.charAt(0) === "M" ? -1 : 1);
1400
- decodeResult.formatted.items.push({
1401
- type: "outside_air_temperature",
1402
- code: "OATEMP",
1403
- label: "Outside Air Temperature (C)",
1404
- value: `${decodeResult.raw.outside_air_temperature}`
1581
+ decodeResult.formatted.description = "Aircraft Transceiver Frequency Change";
1582
+ decodeResult.formatted.items.push({
1583
+ type: "frequency",
1584
+ label: "Frequency",
1585
+ value: `${decodeResult.raw.frequency} MHz`
1405
1586
  });
1406
- }
1407
- static unknown(decodeResult, value) {
1408
- decodeResult.remaining.text += "," + value;
1587
+ decodeResult.decoded = true;
1588
+ decodeResult.decoder.decodeLevel = "full";
1589
+ return decodeResult;
1409
1590
  }
1410
1591
  };
1411
1592
 
1412
- // lib/utils/route_utils.ts
1413
- var RouteUtils = class _RouteUtils {
1414
- static routeToString(route) {
1415
- let str = "";
1416
- if (route.name) {
1417
- str += route.name;
1418
- }
1419
- if (route.runway) {
1420
- str += `(${route.runway})`;
1421
- }
1422
- if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) {
1423
- str += " starting at ";
1424
- } else if (str.length !== 0 && route.waypoints) {
1425
- str += ": ";
1426
- }
1427
- if (route.waypoints) {
1428
- str += _RouteUtils.waypointsToString(route.waypoints);
1429
- }
1430
- return str;
1431
- }
1432
- static waypointToString(waypoint) {
1433
- let s = waypoint.name;
1434
- if (waypoint.latitude && waypoint.longitude) {
1435
- s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`;
1436
- }
1437
- if (waypoint.offset) {
1438
- s += `[${waypoint.offset.bearing}\xB0 ${waypoint.offset.distance}nm]`;
1439
- }
1440
- if (waypoint.time && waypoint.timeFormat) {
1441
- s += `@${_RouteUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`;
1442
- }
1443
- return s;
1593
+ // lib/plugins/Label_H1_FLR.ts
1594
+ var Label_H1_FLR = class extends DecoderPlugin {
1595
+ name = "label-h1-flr";
1596
+ qualifiers() {
1597
+ return {
1598
+ labels: ["H1"],
1599
+ preambles: ["FLR", "#CFBFLR"]
1600
+ };
1444
1601
  }
1445
- static getWaypoint(leg) {
1446
- const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/);
1447
- if (regex?.length == 4) {
1448
- return { name: regex[1], offset: { bearing: parseInt(regex[2]), distance: parseInt(regex[3]) / 10 } };
1449
- }
1450
- const waypoint = leg.split(",");
1451
- if (waypoint.length == 2) {
1452
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]);
1453
- if (position) {
1454
- return { name: waypoint[0], latitude: position.latitude, longitude: position.longitude };
1602
+ decode(message, options = {}) {
1603
+ let decodeResult = this.defaultResult();
1604
+ decodeResult.decoder.name = this.name;
1605
+ decodeResult.formatted.description = "Fault Log Report";
1606
+ decodeResult.message = message;
1607
+ const parts = message.text.split("/FR");
1608
+ if (parts.length > 1) {
1609
+ decodeResult.remaining.text = "";
1610
+ const fields = parts[0].split("/");
1611
+ for (let i = 1; i < fields.length; i++) {
1612
+ const field = fields[i];
1613
+ if (field.startsWith("PN")) {
1614
+ processUnknown(decodeResult, "/" + field);
1615
+ } else {
1616
+ processUnknown(decodeResult, "/" + field);
1617
+ }
1455
1618
  }
1456
- }
1457
- if (leg.length == 13 || leg.length == 14) {
1458
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg);
1459
- const name = waypoint.length == 2 ? waypoint[0] : "";
1460
- if (position) {
1461
- return { name, latitude: position.latitude, longitude: position.longitude };
1619
+ const data = parts[1].substring(0, 20);
1620
+ const msg = parts[1].substring(20);
1621
+ const datetime = data.substring(0, 12);
1622
+ const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
1623
+ processUnknown(decodeResult, data.substring(12));
1624
+ decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
1625
+ decodeResult.raw.fault_message = msg;
1626
+ decodeResult.formatted.items.push({
1627
+ type: "fault",
1628
+ code: "FR",
1629
+ label: "Fault Report",
1630
+ value: decodeResult.raw.fault_message
1631
+ });
1632
+ decodeResult.decoded = true;
1633
+ decodeResult.decoder.decodeLevel = "partial";
1634
+ } else {
1635
+ if (options.debug) {
1636
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
1462
1637
  }
1638
+ decodeResult.remaining.text = message.text;
1639
+ decodeResult.decoded = false;
1640
+ decodeResult.decoder.decodeLevel = "none";
1463
1641
  }
1464
- return { name: leg };
1465
- }
1466
- // move out if we want public
1467
- static timestampToString(time, format) {
1468
- const date = new Date(time * 1e3);
1469
- if (format == "tod") {
1470
- return date.toISOString().slice(11, 19);
1471
- }
1472
- return date.toISOString().slice(0, -5) + "Z";
1473
- }
1474
- static waypointsToString(waypoints) {
1475
- let str = waypoints.map((x) => _RouteUtils.waypointToString(x)).join(" > ").replaceAll("> >", ">>");
1476
- if (str.startsWith(" > ")) {
1477
- str = ">>" + str.slice(2);
1478
- }
1479
- return str;
1642
+ return decodeResult;
1480
1643
  }
1481
1644
  };
1645
+ function processUnknown(decodeResult, value) {
1646
+ decodeResult.remaining.text += value;
1647
+ }
1482
1648
 
1483
1649
  // lib/utils/flight_plan_utils.ts
1484
1650
  var FlightPlanUtils = class _FlightPlanUtils {
@@ -1671,21 +1837,7 @@ var H1Helper = class {
1671
1837
  const dt = processDT(decodeResult, data2);
1672
1838
  allKnownFields = allKnownFields && dt;
1673
1839
  } else if (fields[i].startsWith("ID")) {
1674
- const data2 = fields[i].substring(2).split(",");
1675
- decodeResult.raw.tail = data2[0];
1676
- decodeResult.formatted.items.push({
1677
- type: "tail",
1678
- code: "TAIL",
1679
- label: "Tail",
1680
- value: decodeResult.raw.tail
1681
- });
1682
- if (data2.length > 1) {
1683
- decodeResult.raw.flight_number = data2[1];
1684
- }
1685
- if (data2.length > 2) {
1686
- allKnownFields = false;
1687
- decodeResult.remaining.text += "," + data2.slice(2).join(",");
1688
- }
1840
+ processIdentification(decodeResult, fields[i].substring(2).split(","));
1689
1841
  } else if (fields[i].startsWith("LR")) {
1690
1842
  const data2 = fields[i].substring(2).split(",");
1691
1843
  const lr = processLR(decodeResult, data2);
@@ -1699,6 +1851,20 @@ var H1Helper = class {
1699
1851
  } else if (fields[i].startsWith("PS")) {
1700
1852
  allKnownFields = false;
1701
1853
  decodeResult.remaining.text += fields[i];
1854
+ } else if (fields[i].startsWith("AF")) {
1855
+ const af = processAirField(decodeResult, fields[i].substring(2).split(","));
1856
+ allKnownFields = allKnownFields && af;
1857
+ } else if (fields[i].startsWith("TD")) {
1858
+ const td = processTimeOfDeparture(decodeResult, fields[i].substring(2).split(","));
1859
+ allKnownFields = allKnownFields && td;
1860
+ } else if (fields[i].startsWith("FX")) {
1861
+ decodeResult.raw.free_text = fields[i].substring(2);
1862
+ decodeResult.formatted.items.push({
1863
+ type: "text",
1864
+ code: "TEXT",
1865
+ label: "Free Text",
1866
+ value: decodeResult.raw.free_text
1867
+ });
1702
1868
  } else {
1703
1869
  decodeResult.remaining.text += "/" + fields[i];
1704
1870
  allKnownFields = false;
@@ -1710,6 +1876,51 @@ var H1Helper = class {
1710
1876
  return allKnownFields;
1711
1877
  }
1712
1878
  };
1879
+ function processAirField(decodeResult, data) {
1880
+ if (data.length === 2) {
1881
+ ResultFormatter.departureAirport(decodeResult, data[0]);
1882
+ ResultFormatter.arrivalAirport(decodeResult, data[1]);
1883
+ return true;
1884
+ }
1885
+ decodeResult.remaining.text += "AF/" + data.join(",");
1886
+ return false;
1887
+ }
1888
+ function processTimeOfDeparture(decodeResult, data) {
1889
+ if (data.length === 2) {
1890
+ decodeResult.raw.plannedDepartureTime = data[0];
1891
+ decodeResult.formatted.items.push({
1892
+ type: "ptd",
1893
+ code: "ptd",
1894
+ label: "Planned Departure Time",
1895
+ value: `YYYY-MM-${data[0].substring(0, 2)}T${data[0].substring(2, 4)}:${data[0].substring(4)}:00Z`
1896
+ });
1897
+ decodeResult.raw.plannedDepartureTime = data[1];
1898
+ decodeResult.formatted.items.push({
1899
+ type: "etd",
1900
+ code: "etd",
1901
+ label: "Estimated Departure Time",
1902
+ value: `${data[1].substring(0, 2)}:${data[1].substring(2)}`
1903
+ });
1904
+ return true;
1905
+ }
1906
+ decodeResult.remaining.text += "/TD" + data.join(",");
1907
+ return false;
1908
+ }
1909
+ function processIdentification(decodeResult, data) {
1910
+ decodeResult.raw.tail = data[0];
1911
+ decodeResult.formatted.items.push({
1912
+ type: "tail",
1913
+ code: "TAIL",
1914
+ label: "Tail",
1915
+ value: decodeResult.raw.tail
1916
+ });
1917
+ if (data.length > 1) {
1918
+ decodeResult.raw.flight_number = data[1];
1919
+ }
1920
+ if (data.length > 2) {
1921
+ decodeResult.raw.mission_number = data[2];
1922
+ }
1923
+ }
1713
1924
  function processDT(decodeResult, data) {
1714
1925
  let allKnownFields = true;
1715
1926
  if (!decodeResult.raw.arrival_icao) {
@@ -1787,7 +1998,7 @@ function processDC(decodeResult, data) {
1787
1998
  }
1788
1999
  function processPS(decodeResult, data) {
1789
2000
  let allKnownFields = true;
1790
- const position = CoordinateUtils.decodeStringCoordinates(data[0]);
2001
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
1791
2002
  if (position) {
1792
2003
  decodeResult.raw.position = position;
1793
2004
  decodeResult.formatted.items.push({
@@ -1823,7 +2034,7 @@ function processPS(decodeResult, data) {
1823
2034
  }
1824
2035
  function processPosition2(decodeResult, data) {
1825
2036
  let allKnownFields = true;
1826
- const position = CoordinateUtils.decodeStringCoordinates(data[0]);
2037
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
1827
2038
  if (position) {
1828
2039
  decodeResult.raw.position = position;
1829
2040
  decodeResult.formatted.items.push({
@@ -1905,6 +2116,67 @@ var Label_H1_FPN = class extends DecoderPlugin {
1905
2116
  }
1906
2117
  };
1907
2118
 
2119
+ // lib/plugins/Label_H1_FTX.ts
2120
+ var Label_H1_FTX = class extends DecoderPlugin {
2121
+ name = "label-h1-ftx";
2122
+ qualifiers() {
2123
+ return {
2124
+ labels: ["H1"],
2125
+ preambles: ["FTX", "- #MDFTX"]
2126
+ };
2127
+ }
2128
+ decode(message, options = {}) {
2129
+ let decodeResult = this.defaultResult();
2130
+ decodeResult.decoder.name = this.name;
2131
+ decodeResult.formatted.description = "Free Text";
2132
+ decodeResult.message = message;
2133
+ decodeResult.remaining.text = "";
2134
+ const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2135
+ decodeResult.decoded = true;
2136
+ decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2137
+ if (decodeResult.formatted.items.length === 0) {
2138
+ if (options?.debug) {
2139
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
2140
+ }
2141
+ decodeResult.remaining.text = message.text;
2142
+ decodeResult.decoded = false;
2143
+ decodeResult.decoder.decodeLevel = "none";
2144
+ }
2145
+ return decodeResult;
2146
+ }
2147
+ };
2148
+
2149
+ // lib/plugins/Label_H1_INI.ts
2150
+ var Label_H1_INI = class extends DecoderPlugin {
2151
+ // eslint-disable-line camelcase
2152
+ name = "label-h1-ini";
2153
+ qualifiers() {
2154
+ return {
2155
+ labels: ["H1"],
2156
+ preambles: ["INI", "- #MDINI"]
2157
+ };
2158
+ }
2159
+ decode(message, options = {}) {
2160
+ const decodeResult = this.defaultResult();
2161
+ decodeResult.decoder.name = this.name;
2162
+ decodeResult.formatted.description = "Flight Plan Initial Report";
2163
+ decodeResult.message = message;
2164
+ decodeResult.remaining.text = "";
2165
+ const fulllyDecoded = H1Helper.decodeH1Message(decodeResult, message.text);
2166
+ decodeResult.decoded = true;
2167
+ decodeResult.decoder.decodeLevel = fulllyDecoded ? "full" : "partial";
2168
+ if (decodeResult.formatted.items.length === 0) {
2169
+ if (options?.debug) {
2170
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
2171
+ }
2172
+ decodeResult.remaining.text = message.text;
2173
+ decodeResult.decoded = false;
2174
+ decodeResult.decoder.decodeLevel = "none";
2175
+ }
2176
+ return decodeResult;
2177
+ }
2178
+ };
2179
+
1908
2180
  // lib/plugins/Label_H1_OHMA.ts
1909
2181
  import * as zlib from "minizlib";
1910
2182
  var Label_H1_OHMA = class extends DecoderPlugin {
@@ -2015,16 +2287,16 @@ var Label_H1_WRN = class extends DecoderPlugin {
2015
2287
  for (let i = 1; i < fields.length; i++) {
2016
2288
  const field = fields[i];
2017
2289
  if (field.startsWith("PN")) {
2018
- processUnknown(decodeResult, "/" + field);
2290
+ processUnknown2(decodeResult, "/" + field);
2019
2291
  } else {
2020
- processUnknown(decodeResult, "/" + field);
2292
+ processUnknown2(decodeResult, "/" + field);
2021
2293
  }
2022
2294
  }
2023
2295
  const data = parts[1].substring(0, 20);
2024
2296
  const msg = parts[1].substring(20);
2025
2297
  const datetime = data.substring(0, 12);
2026
2298
  const date = datetime.substring(4, 6) + datetime.substring(2, 4) + datetime.substring(0, 2);
2027
- processUnknown(decodeResult, data.substring(12));
2299
+ processUnknown2(decodeResult, data.substring(12));
2028
2300
  decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date);
2029
2301
  decodeResult.raw.warning_message = msg;
2030
2302
  decodeResult.formatted.items.push({
@@ -2046,10 +2318,64 @@ var Label_H1_WRN = class extends DecoderPlugin {
2046
2318
  return decodeResult;
2047
2319
  }
2048
2320
  };
2049
- function processUnknown(decodeResult, value) {
2321
+ function processUnknown2(decodeResult, value) {
2050
2322
  decodeResult.remaining.text += value;
2051
2323
  }
2052
2324
 
2325
+ // lib/plugins/Label_HX.ts
2326
+ var Label_HX = class extends DecoderPlugin {
2327
+ name = "label-hx";
2328
+ qualifiers() {
2329
+ return {
2330
+ labels: ["HX"],
2331
+ preambles: ["RA FMT LOCATION", "RA FMT 43"]
2332
+ };
2333
+ }
2334
+ decode(message, options = {}) {
2335
+ const decodeResult = this.defaultResult();
2336
+ decodeResult.decoder.name = this.name;
2337
+ decodeResult.message = message;
2338
+ decodeResult.formatted.description = "Undelivered Uplink Report";
2339
+ const parts = message.text.split(" ");
2340
+ decodeResult.decoded = true;
2341
+ if (parts[2] === "LOCATION") {
2342
+ let latdir = parts[3].substring(0, 1);
2343
+ let latdeg = Number(parts[3].substring(1, 3));
2344
+ let latmin = Number(parts[3].substring(3, 7));
2345
+ let londir = parts[4].substring(0, 1);
2346
+ let londeg = Number(parts[4].substring(1, 4));
2347
+ let lonmin = Number(parts[4].substring(4, 8));
2348
+ decodeResult.raw.position = {
2349
+ latitude: (latdeg + latmin / 60) * (latdir === "N" ? 1 : -1),
2350
+ longitude: (londeg + lonmin / 60) * (londir === "E" ? 1 : -1)
2351
+ };
2352
+ decodeResult.remaining.text = parts.slice(5).join(" ");
2353
+ } else if (parts[2] === "43") {
2354
+ ResultFormatter.departureAirport(decodeResult, parts[3]);
2355
+ decodeResult.remaining.text = parts.slice(4).join(" ");
2356
+ } else {
2357
+ decodeResult.decoded = false;
2358
+ }
2359
+ if (decodeResult.raw.position) {
2360
+ decodeResult.formatted.items.push({
2361
+ type: "aircraft_position",
2362
+ code: "POS",
2363
+ label: "Aircraft Position",
2364
+ value: CoordinateUtils.coordinateString(decodeResult.raw.position)
2365
+ });
2366
+ }
2367
+ if (decodeResult.decoded) {
2368
+ if (decodeResult.remaining.text === "")
2369
+ decodeResult.decoder.decodeLevel = "full";
2370
+ else
2371
+ decodeResult.decoder.decodeLevel = "partial";
2372
+ } else {
2373
+ decodeResult.decoder.decodeLevel = "none";
2374
+ }
2375
+ return decodeResult;
2376
+ }
2377
+ };
2378
+
2053
2379
  // lib/plugins/Label_SQ.ts
2054
2380
  var Label_SQ = class extends DecoderPlugin {
2055
2381
  name = "label-sq";
@@ -2090,11 +2416,13 @@ var Label_SQ = class extends DecoderPlugin {
2090
2416
  decodeResult.formatted.items = [
2091
2417
  {
2092
2418
  type: "network",
2419
+ code: "NETT",
2093
2420
  label: "Network",
2094
2421
  value: formattedNetwork
2095
2422
  },
2096
2423
  {
2097
2424
  type: "version",
2425
+ code: "VER",
2098
2426
  label: "Version",
2099
2427
  value: decodeResult.raw.version
2100
2428
  }
@@ -2103,6 +2431,7 @@ var Label_SQ = class extends DecoderPlugin {
2103
2431
  if (decodeResult.raw.groundStation.icaoCode && decodeResult.raw.groundStation.number) {
2104
2432
  decodeResult.formatted.items.push({
2105
2433
  type: "ground_station",
2434
+ code: "GNDSTN",
2106
2435
  label: "Ground Station",
2107
2436
  value: `${decodeResult.raw.groundStation.icaoCode}${decodeResult.raw.groundStation.number}`
2108
2437
  });
@@ -2110,6 +2439,7 @@ var Label_SQ = class extends DecoderPlugin {
2110
2439
  if (decodeResult.raw.groundStation.iataCode) {
2111
2440
  decodeResult.formatted.items.push({
2112
2441
  type: "iataCode",
2442
+ code: "IATA",
2113
2443
  label: "IATA",
2114
2444
  value: decodeResult.raw.groundStation.iataCode
2115
2445
  });
@@ -2117,6 +2447,7 @@ var Label_SQ = class extends DecoderPlugin {
2117
2447
  if (decodeResult.raw.groundStation.icaoCode) {
2118
2448
  decodeResult.formatted.items.push({
2119
2449
  type: "icaoCode",
2450
+ code: "ICAO",
2120
2451
  label: "ICAO",
2121
2452
  value: decodeResult.raw.groundStation.icaoCode
2122
2453
  });
@@ -2124,6 +2455,7 @@ var Label_SQ = class extends DecoderPlugin {
2124
2455
  if (decodeResult.raw.groundStation.coordinates.latitude) {
2125
2456
  decodeResult.formatted.items.push({
2126
2457
  type: "coordinates",
2458
+ code: "COORD",
2127
2459
  label: "Ground Station Location",
2128
2460
  value: `${decodeResult.raw.groundStation.coordinates.latitude}, ${decodeResult.raw.groundStation.coordinates.longitude}`
2129
2461
  });
@@ -2131,6 +2463,7 @@ var Label_SQ = class extends DecoderPlugin {
2131
2463
  if (decodeResult.raw.groundStation.airport) {
2132
2464
  decodeResult.formatted.items.push({
2133
2465
  type: "airport",
2466
+ code: "APT",
2134
2467
  label: "Airport",
2135
2468
  value: `${decodeResult.raw.groundStation.airport.name} (${decodeResult.raw.groundStation.airport.icao}) in ${decodeResult.raw.groundStation.airport.location}`
2136
2469
  });
@@ -2139,6 +2472,7 @@ var Label_SQ = class extends DecoderPlugin {
2139
2472
  if (decodeResult.raw.vdlFrequency) {
2140
2473
  decodeResult.formatted.items.push({
2141
2474
  type: "vdlFrequency",
2475
+ code: "VDLFRQ",
2142
2476
  label: "VDL Frequency",
2143
2477
  value: `${decodeResult.raw.vdlFrequency} MHz`
2144
2478
  });
@@ -2295,31 +2629,48 @@ var Label_QQ = class extends DecoderPlugin {
2295
2629
  decode(message, options = {}) {
2296
2630
  const decodeResult = this.defaultResult();
2297
2631
  decodeResult.decoder.name = this.name;
2298
- decodeResult.raw.origin = message.text.substring(0, 4);
2299
- decodeResult.raw.destination = message.text.substring(4, 8);
2300
- decodeResult.raw.wheels_off = message.text.substring(8, 12);
2301
- decodeResult.remaining.text = message.text.substring(12);
2632
+ decodeResult.message = message;
2302
2633
  decodeResult.formatted.description = "OFF Report";
2303
- decodeResult.formatted.items = [
2304
- {
2305
- type: "origin",
2306
- code: "ORG",
2307
- label: "Origin",
2308
- value: decodeResult.raw.origin
2309
- },
2310
- {
2311
- type: "destination",
2312
- code: "DST",
2313
- label: "Destination",
2314
- value: decodeResult.raw.destination
2315
- },
2316
- {
2317
- type: "wheels_off",
2318
- code: "WOFF",
2319
- label: "Wheels OFF",
2320
- value: decodeResult.raw.wheels_off
2634
+ ResultFormatter.departureAirport(decodeResult, message.text.substring(0, 4));
2635
+ ResultFormatter.arrivalAirport(decodeResult, message.text.substring(4, 8));
2636
+ decodeResult.raw.wheels_off = message.text.substring(8, 12);
2637
+ if (message.text.substring(12, 19) === "\r\n001FE") {
2638
+ decodeResult.raw.day_of_month = message.text.substring(19, 21);
2639
+ decodeResult.raw.wheels_off = message.text.substring(21, 27);
2640
+ let latdir = message.text.substring(27, 28);
2641
+ let latdeg = Number(message.text.substring(28, 30));
2642
+ let latmin = Number(message.text.substring(30, 34));
2643
+ let londir = message.text.substring(34, 35);
2644
+ let londeg = Number(message.text.substring(35, 38));
2645
+ let lonmin = Number(message.text.substring(38, 42));
2646
+ decodeResult.raw.position = {
2647
+ latitude: (latdeg + latmin / 60) * (latdir === "N" ? 1 : -1),
2648
+ longitude: (londeg + lonmin / 60) * (londir === "E" ? 1 : -1)
2649
+ };
2650
+ decodeResult.remaining.text = message.text.substring(42, 45);
2651
+ if (decodeResult.remaining.text !== "---") {
2652
+ ResultFormatter.groundspeed(decodeResult, message.text.substring(45, 48));
2653
+ } else {
2654
+ ResultFormatter.unknown(decodeResult, message.text.substring(45, 48));
2321
2655
  }
2322
- ];
2656
+ ResultFormatter.unknown(decodeResult, message.text.substring(48));
2657
+ } else {
2658
+ decodeResult.remaining.text = message.text.substring(12);
2659
+ }
2660
+ decodeResult.formatted.items.push({
2661
+ type: "wheels_off",
2662
+ code: "WOFF",
2663
+ label: "Wheels OFF",
2664
+ value: decodeResult.raw.wheels_off
2665
+ });
2666
+ if (decodeResult.raw.position) {
2667
+ decodeResult.formatted.items.push({
2668
+ type: "aircraft_position",
2669
+ code: "POS",
2670
+ label: "Aircraft Position",
2671
+ value: CoordinateUtils.coordinateString(decodeResult.raw.position)
2672
+ });
2673
+ }
2323
2674
  decodeResult.decoded = true;
2324
2675
  if (decodeResult.remaining.text === "")
2325
2676
  decodeResult.decoder.decodeLevel = "full";
@@ -3142,6 +3493,9 @@ var MessageDecoder = class {
3142
3493
  this.debug = false;
3143
3494
  this.registerPlugin(new Label_ColonComma(this));
3144
3495
  this.registerPlugin(new Label_5Z(this));
3496
+ this.registerPlugin(new Label_10_LDR(this));
3497
+ this.registerPlugin(new Label_10_POS(this));
3498
+ this.registerPlugin(new Label_10_Slash(this));
3145
3499
  this.registerPlugin(new Label_12_N_Space(this));
3146
3500
  this.registerPlugin(new Label_15(this));
3147
3501
  this.registerPlugin(new Label_15_FST(this));
@@ -3156,9 +3510,13 @@ var MessageDecoder = class {
3156
3510
  this.registerPlugin(new Label_44_POS(this));
3157
3511
  this.registerPlugin(new Label_B6_Forwardslash(this));
3158
3512
  this.registerPlugin(new Label_H1_FPN(this));
3513
+ this.registerPlugin(new Label_H1_FLR(this));
3514
+ this.registerPlugin(new Label_H1_FTX(this));
3515
+ this.registerPlugin(new Label_H1_INI(this));
3159
3516
  this.registerPlugin(new Label_H1_OHMA(this));
3160
3517
  this.registerPlugin(new Label_H1_POS(this));
3161
3518
  this.registerPlugin(new Label_H1_WRN(this));
3519
+ this.registerPlugin(new Label_HX(this));
3162
3520
  this.registerPlugin(new Label_80(this));
3163
3521
  this.registerPlugin(new Label_8E(this));
3164
3522
  this.registerPlugin(new Label_1M_Slash(this));