@airframes/acars-decoder 1.8.7 → 1.8.9

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
@@ -290,14 +290,14 @@ var DateTimeUtils = class {
290
290
  /**
291
291
  *
292
292
  * @param time HHMMSS
293
- * @param date MMDDYY or MMDDYYYY
293
+ * @param date DDMMYY or DDMMYYYY
294
294
  * @returns seconds since epoch
295
295
  */
296
296
  static convertDateTimeToEpoch(time, date) {
297
297
  if (date.length === 6) {
298
298
  date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
299
299
  }
300
- 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`;
300
+ const timestamp = `${date.substring(4, 8)}-${date.substring(2, 4)}-${date.substring(0, 2)}T${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}.000Z`;
301
301
  const millis = Date.parse(timestamp);
302
302
  return millis / 1e3;
303
303
  }
@@ -627,8 +627,11 @@ var ResultFormatter = class {
627
627
  value: decodeResult.raw.fuel_remaining.toString()
628
628
  });
629
629
  }
630
+ static checksumAlgorithm(decodeResult, value) {
631
+ decodeResult.raw.checksum_algorithm = value;
632
+ }
630
633
  static checksum(decodeResult, value) {
631
- decodeResult.raw.checksum = Number("0x" + value);
634
+ decodeResult.raw.checksum = value;
632
635
  decodeResult.formatted.items.push({
633
636
  type: "message_checksum",
634
637
  code: "CHECKSUM",
@@ -677,6 +680,20 @@ var ResultFormatter = class {
677
680
  value: `${decodeResult.raw.outside_air_temperature} degrees`
678
681
  });
679
682
  }
683
+ static totalAirTemp(decodeResult, value) {
684
+ if (value.length === 0) {
685
+ return;
686
+ }
687
+ decodeResult.raw.total_air_temperature = Number(
688
+ value.replace("M", "-").replace("P", "+")
689
+ );
690
+ decodeResult.formatted.items.push({
691
+ type: "temperature",
692
+ code: "TATEMP",
693
+ label: "Total Air Temperature (C)",
694
+ value: `${decodeResult.raw.total_air_temperature} degrees`
695
+ });
696
+ }
680
697
  static heading(decodeResult, value) {
681
698
  decodeResult.raw.heading = value;
682
699
  decodeResult.formatted.items.push({
@@ -940,6 +957,15 @@ var ResultFormatter = class {
940
957
  value: `${decodeResult.raw.ground_address}`
941
958
  });
942
959
  }
960
+ static timestamp(decodeResult, value) {
961
+ decodeResult.raw.message_timestamp = value;
962
+ decodeResult.formatted.items.push({
963
+ type: "epoch",
964
+ code: "TIMESTAMP",
965
+ label: "Message Timestamp",
966
+ value: DateTimeUtils.timestampToString(value, "epoch")
967
+ });
968
+ }
943
969
  static unknown(decodeResult, value, sep = ",") {
944
970
  if (!decodeResult.remaining.text) decodeResult.remaining.text = value;
945
971
  else decodeResult.remaining.text += sep + value;
@@ -1542,10 +1568,9 @@ var Label_15 = class extends DecoderPlugin {
1542
1568
  const ddmmyy = between.substring(16, 22);
1543
1569
  const hhmm = between.substring(22, 26);
1544
1570
  if (ddmmyy != "------") {
1545
- const mmddyy = ddmmyy.substring(2, 4) + ddmmyy.substring(0, 2) + ddmmyy.substring(4);
1546
1571
  ResultFormatter.off(
1547
1572
  decodeResult,
1548
- DateTimeUtils.convertDateTimeToEpoch(hhmm + "00", mmddyy),
1573
+ DateTimeUtils.convertDateTimeToEpoch(hhmm + "00", ddmmyy),
1549
1574
  "epoch"
1550
1575
  );
1551
1576
  } else {
@@ -2012,9 +2037,14 @@ function addCompanyRoute(decodeResult, value) {
2012
2037
  // lib/utils/h1_helper.ts
2013
2038
  var H1Helper = class _H1Helper {
2014
2039
  static decodeH1Message(decodeResult, message) {
2015
- const checksum = message.slice(-4);
2040
+ const checksum = parseInt(message.slice(-4), 16);
2016
2041
  const data = message.slice(0, message.length - 4);
2017
- if (calculateChecksum(data) !== checksum) {
2042
+ let checksumAlgorithmUsed = "";
2043
+ if (crc16IbmSdlcRev(data) === checksum) {
2044
+ checksumAlgorithmUsed = "IBM-SDLC reversed";
2045
+ } else if (crc16Genibus(data) === checksum) {
2046
+ checksumAlgorithmUsed = "GENIBUS";
2047
+ } else {
2018
2048
  decodeResult.decoded = false;
2019
2049
  decodeResult.decoder.decodeLevel = "none";
2020
2050
  return false;
@@ -2091,6 +2121,9 @@ var H1Helper = class _H1Helper {
2091
2121
  case "RN":
2092
2122
  ResultFormatter.routeNumber(decodeResult, data2);
2093
2123
  break;
2124
+ case "RW":
2125
+ processRunway(decodeResult, data2.split(":"));
2126
+ break;
2094
2127
  case "SN":
2095
2128
  decodeResult.raw.serial_number = data2;
2096
2129
  break;
@@ -2116,6 +2149,7 @@ var H1Helper = class _H1Helper {
2116
2149
  ResultFormatter.unknown(decodeResult, fields[i], "/");
2117
2150
  }
2118
2151
  }
2152
+ ResultFormatter.checksumAlgorithm(decodeResult, checksumAlgorithmUsed);
2119
2153
  ResultFormatter.checksum(decodeResult, checksum);
2120
2154
  return true;
2121
2155
  }
@@ -2230,29 +2264,42 @@ function processIdentification(decodeResult, data) {
2230
2264
  }
2231
2265
  }
2232
2266
  function processDT(decodeResult, data) {
2233
- if (!decodeResult.raw.arrival_icao) {
2234
- ResultFormatter.arrivalAirport(decodeResult, data[0]);
2235
- } else if (decodeResult.raw.arrival_icao != data[0]) {
2236
- ResultFormatter.unknownArr(decodeResult, data);
2267
+ if (data.length === 9) {
2268
+ ResultFormatter.unknown(decodeResult, data[0]);
2269
+ if (!decodeResult.raw.arrival_icao) {
2270
+ ResultFormatter.arrivalAirport(decodeResult, data[1]);
2271
+ }
2272
+ ResultFormatter.arrivalRunway(decodeResult, data[2]);
2273
+ ResultFormatter.currentFuel(decodeResult, parseInt(data[3], 10));
2274
+ ResultFormatter.eta(
2275
+ decodeResult,
2276
+ DateTimeUtils.convertHHMMSSToTod(data[4])
2277
+ );
2278
+ ResultFormatter.unknown(decodeResult, data[5]);
2279
+ ResultFormatter.position(
2280
+ decodeResult,
2281
+ CoordinateUtils.decodeStringCoordinates(data[6])
2282
+ );
2283
+ ResultFormatter.unknown(decodeResult, data[7]);
2284
+ ResultFormatter.unknown(decodeResult, data[8]);
2285
+ return;
2237
2286
  }
2238
- if (data.length > 1) {
2287
+ if (data.length === 4 || data.length === 5) {
2288
+ if (!decodeResult.raw.arrival_icao) {
2289
+ ResultFormatter.arrivalAirport(decodeResult, data[0]);
2290
+ }
2239
2291
  ResultFormatter.arrivalRunway(decodeResult, data[1]);
2240
- }
2241
- if (data.length > 2) {
2242
- ResultFormatter.currentFuel(decodeResult, Number(data[2]));
2243
- }
2244
- if (data.length > 3) {
2292
+ ResultFormatter.currentFuel(decodeResult, parseInt(data[2], 10));
2245
2293
  ResultFormatter.eta(
2246
2294
  decodeResult,
2247
2295
  DateTimeUtils.convertHHMMSSToTod(data[3])
2248
2296
  );
2297
+ if (data.length > 4) {
2298
+ ResultFormatter.remainingFuel(decodeResult, Number(data[4]));
2299
+ }
2300
+ return;
2249
2301
  }
2250
- if (data.length > 4) {
2251
- ResultFormatter.remainingFuel(decodeResult, Number(data[4]));
2252
- }
2253
- if (data.length > 5) {
2254
- ResultFormatter.unknownArr(decodeResult, data);
2255
- }
2302
+ ResultFormatter.unknownArr(decodeResult, data, ",");
2256
2303
  }
2257
2304
  function processLandingReport(decodeResult, data) {
2258
2305
  if (data.length === 19) {
@@ -2316,7 +2363,11 @@ function processMessageType(decodeResult, type) {
2316
2363
  } else if (type === "FTX") {
2317
2364
  decodeResult.formatted.description = "Free Text";
2318
2365
  } else if (type === "INI") {
2319
- decodeResult.formatted.description = "Flight Plan Initial Report";
2366
+ decodeResult.formatted.description = "Initial Report";
2367
+ } else if (type === "INR") {
2368
+ decodeResult.formatted.description = "In-Range Report";
2369
+ } else if (type === "LDI") {
2370
+ decodeResult.formatted.description = "Load Distribution Information";
2320
2371
  } else if (type === "PER") {
2321
2372
  decodeResult.formatted.description = "Performance Report";
2322
2373
  } else if (type === "POS") {
@@ -2325,6 +2376,8 @@ function processMessageType(decodeResult, type) {
2325
2376
  decodeResult.formatted.description = "Progress Report";
2326
2377
  } else if (type === "PWI") {
2327
2378
  decodeResult.formatted.description = "Pilot Weather Information";
2379
+ } else if (type === "WXR") {
2380
+ decodeResult.formatted.description = "Weather Report";
2328
2381
  } else if (type === "REJ") {
2329
2382
  decodeResult.formatted.description = "Reject";
2330
2383
  } else if (type === "REQ") {
@@ -2338,7 +2391,6 @@ function processMessageType(decodeResult, type) {
2338
2391
  return true;
2339
2392
  }
2340
2393
  function processDateCode(decodeResult, data) {
2341
- decodeResult.raw.message_date = data[0];
2342
2394
  if (data.length === 1) {
2343
2395
  } else if (data.length === 2) {
2344
2396
  const date = data[0].substring(2, 4) + data[0].substring(0, 2) + data[0].substring(4, 6);
@@ -2381,11 +2433,14 @@ function processWindData(decodeResult, message) {
2381
2433
  const flightLevel = Number(message.slice(0, 3));
2382
2434
  const fields = message.slice(4).split(".");
2383
2435
  fields.forEach((field) => {
2436
+ if (field.length < 4) {
2437
+ return;
2438
+ }
2384
2439
  const data = field.split(",");
2385
2440
  const waypoint = { name: data[0] };
2386
2441
  const windData = data[1];
2387
- const windDirection = Number(windData.slice(0, 3));
2388
- const windSpeed = Number(windData.slice(3));
2442
+ const windDirection = parseInt(windData.slice(0, 3), 10);
2443
+ const windSpeed = parseInt(windData.slice(3), 10);
2389
2444
  if (data.length === 3) {
2390
2445
  const tempData = data[2];
2391
2446
  const tempFlightLevel = Number(tempData.slice(0, 3));
@@ -2412,7 +2467,16 @@ function processWindData(decodeResult, message) {
2412
2467
  });
2413
2468
  ResultFormatter.windData(decodeResult, wind);
2414
2469
  }
2415
- function calculateChecksum(data) {
2470
+ function processRunway(decodeResult, data) {
2471
+ const parts = data[0].split(".");
2472
+ const more = parts[0].split(",");
2473
+ ResultFormatter.departureRunway(decodeResult, more[0]);
2474
+ ResultFormatter.unknownArr(decodeResult, more.slice(1), ",");
2475
+ ResultFormatter.unknownArr(decodeResult, parts.slice(1), ".");
2476
+ ResultFormatter.departureAirport(decodeResult, data[1].split(",")[0]);
2477
+ ResultFormatter.unknownArr(decodeResult, data[1].split(",").slice(1), ",");
2478
+ }
2479
+ function crc16IbmSdlcRev(data) {
2416
2480
  let crc = 65535;
2417
2481
  const bytes = Buffer.from(data, "ascii");
2418
2482
  for (const byte of bytes) {
@@ -2430,7 +2494,23 @@ function calculateChecksum(data) {
2430
2494
  const nibble2 = crc >> 8 & 15;
2431
2495
  const nibble3 = crc >> 4 & 15;
2432
2496
  const nibble4 = crc & 15;
2433
- return `${nibble4.toString(16)}${nibble3.toString(16)}${nibble2.toString(16)}${nibble1.toString(16)}`.toUpperCase();
2497
+ return nibble4 << 12 | nibble3 << 8 | nibble2 << 4 | nibble1;
2498
+ }
2499
+ function crc16Genibus(data) {
2500
+ let crc = 65535;
2501
+ const polynomial = 4129;
2502
+ const bytes = Buffer.from(data, "ascii");
2503
+ for (const byte of bytes) {
2504
+ crc ^= byte << 8;
2505
+ for (let i = 0; i < 8; i++) {
2506
+ if ((crc & 32768) !== 0) {
2507
+ crc = (crc << 1 ^ polynomial) & 65535;
2508
+ } else {
2509
+ crc = crc << 1 & 65535;
2510
+ }
2511
+ }
2512
+ }
2513
+ return (crc ^ 65535) & 65535;
2434
2514
  }
2435
2515
 
2436
2516
  // lib/plugins/Label_1J_2J_FTX.ts
@@ -2504,7 +2584,7 @@ var Label_1M_Slash = class extends DecoderPlugin {
2504
2584
  decodeResult,
2505
2585
  DateTimeUtils.convertDateTimeToEpoch(
2506
2586
  results[7] + "00",
2507
- yymmdd.substring(2, 4) + yymmdd.substring(4, 6) + yymmdd.substring(0, 2)
2587
+ yymmdd.substring(4, 6) + yymmdd.substring(2, 4) + yymmdd.substring(0, 2)
2508
2588
  ),
2509
2589
  "epoch"
2510
2590
  );
@@ -3289,11 +3369,11 @@ var Label_24_Slash = class extends DecoderPlugin {
3289
3369
  decodeResult.message = message;
3290
3370
  const fields = message.text.split("/");
3291
3371
  if (fields.length == 10 && fields[0] == "" && fields[9] == "") {
3292
- const mmddyy = fields[1].substring(4, 6) + fields[1].substring(2, 4) + fields[1].substring(0, 2);
3372
+ const ddmmyy = fields[1].substring(2, 4) + fields[1].substring(4, 6) + fields[1].substring(0, 2);
3293
3373
  const hhmmss = fields[2] + "00";
3294
3374
  decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(
3295
3375
  hhmmss,
3296
- mmddyy
3376
+ ddmmyy
3297
3377
  );
3298
3378
  ResultFormatter.flightNumber(decodeResult, fields[3]);
3299
3379
  ResultFormatter.altitude(decodeResult, Number(fields[4]));
@@ -4029,7 +4109,7 @@ var Label_4N = class extends DecoderPlugin {
4029
4109
  if (fields[12].length > 1) {
4030
4110
  ResultFormatter.alternateRunway(decodeResult, fields[12].split("/")[0]);
4031
4111
  }
4032
- ResultFormatter.checksum(decodeResult, fields[32]);
4112
+ ResultFormatter.checksum(decodeResult, parseInt(fields[32], 16));
4033
4113
  ResultFormatter.unknownArr(
4034
4114
  decodeResult,
4035
4115
  [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter(
@@ -4538,7 +4618,7 @@ var Label_H1 = class extends DecoderPlugin {
4538
4618
  } else if (parts.length === 1) {
4539
4619
  decoded = H1Helper.decodeH1Message(decodeResult, msg);
4540
4620
  } else if (parts.length == 2) {
4541
- const offset = isNaN(parseInt(parts[1][1])) ? 3 : 4;
4621
+ const offset = parts[0] === "- " || isNaN(parseInt(parts[1][1])) ? 3 : 4;
4542
4622
  decoded = H1Helper.decodeH1Message(
4543
4623
  decodeResult,
4544
4624
  msg.slice(parts[0].length + offset)
@@ -4759,6 +4839,66 @@ var Label_H1_OHMA = class extends DecoderPlugin {
4759
4839
  }
4760
4840
  };
4761
4841
 
4842
+ // lib/plugins/Label_H1_Paren.ts
4843
+ var Label_H1_Paren = class extends DecoderPlugin {
4844
+ name = "label-h1-paren";
4845
+ qualifiers() {
4846
+ return {
4847
+ labels: ["H1"],
4848
+ preambles: ["("]
4849
+ };
4850
+ }
4851
+ decode(message) {
4852
+ let decodeResult = this.defaultResult();
4853
+ decodeResult.decoder.name = this.name;
4854
+ decodeResult.message = message;
4855
+ if (!message.text || !message.text.startsWith("(")) {
4856
+ decodeResult.decoded = false;
4857
+ return decodeResult;
4858
+ }
4859
+ const regex = /^\(POS-(?<flight>\w+)\s+(?<lat>-?\d{4,5}[NS])(?<lon>\d{5}[EW])\/(?<timestamp>\d{6})\s+F(?<alt>\d{3})\r?\nRMK\/FUEL\s+(?<fuel>\d{2,3}\.\d)\s+M(?<mach>\d\.\d{2})\)/;
4860
+ const match = message.text.match(regex);
4861
+ if (match && match.groups) {
4862
+ decodeResult.decoded = true;
4863
+ decodeResult.decoder.decodeLevel = "partial";
4864
+ decodeResult.formatted.description = "Position Report";
4865
+ ResultFormatter.flightNumber(decodeResult, match.groups.flight);
4866
+ ResultFormatter.position(decodeResult, {
4867
+ latitude: parseLat(match.groups.lat),
4868
+ longitude: parseLon(match.groups.lon)
4869
+ });
4870
+ ResultFormatter.time_of_day(
4871
+ decodeResult,
4872
+ DateTimeUtils.convertHHMMSSToTod(match.groups.timestamp)
4873
+ );
4874
+ ResultFormatter.altitude(
4875
+ decodeResult,
4876
+ parseInt(match.groups.alt, 10) * 100
4877
+ );
4878
+ ResultFormatter.currentFuel(decodeResult, parseFloat(match.groups.fuel));
4879
+ ResultFormatter.mach(decodeResult, parseFloat(match.groups.mach));
4880
+ ResultFormatter.unknown(decodeResult, "RMK");
4881
+ }
4882
+ return decodeResult;
4883
+ }
4884
+ };
4885
+ function parseLat(latStr) {
4886
+ const match = latStr.match(/(-?)(\d{2})(\d{2})([NS])/);
4887
+ if (!match) return NaN;
4888
+ let deg = parseInt(match[2]);
4889
+ let min = parseInt(match[3]);
4890
+ let sign = match[4] === "S" ? -1 : 1;
4891
+ return sign * (deg + min / 60);
4892
+ }
4893
+ function parseLon(lonStr) {
4894
+ const match = lonStr.match(/(\d{3})(\d{2})([EW])/);
4895
+ if (!match) return NaN;
4896
+ let deg = parseInt(match[1]);
4897
+ let min = parseInt(match[2]);
4898
+ let sign = match[3] === "W" ? -1 : 1;
4899
+ return sign * (deg + min / 60);
4900
+ }
4901
+
4762
4902
  // lib/plugins/Label_H1_StarPOS.ts
4763
4903
  var Label_H1_StarPOS = class extends DecoderPlugin {
4764
4904
  name = "label-h1-star-pos";
@@ -6154,6 +6294,7 @@ var MessageDecoder = class {
6154
6294
  this.registerPlugin(new Label_H2_02E(this));
6155
6295
  this.registerPlugin(new Label_H1_FLR(this));
6156
6296
  this.registerPlugin(new Label_H1_OHMA(this));
6297
+ this.registerPlugin(new Label_H1_Paren(this));
6157
6298
  this.registerPlugin(new Label_H1_WRN(this));
6158
6299
  this.registerPlugin(new Label_H1(this));
6159
6300
  this.registerPlugin(new Label_H1_StarPOS(this));