@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.mjs CHANGED
@@ -253,14 +253,14 @@ var DateTimeUtils = class {
253
253
  /**
254
254
  *
255
255
  * @param time HHMMSS
256
- * @param date MMDDYY or MMDDYYYY
256
+ * @param date DDMMYY or DDMMYYYY
257
257
  * @returns seconds since epoch
258
258
  */
259
259
  static convertDateTimeToEpoch(time, date) {
260
260
  if (date.length === 6) {
261
261
  date = date.substring(0, 4) + `20${date.substring(4, 6)}`;
262
262
  }
263
- 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`;
263
+ 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`;
264
264
  const millis = Date.parse(timestamp);
265
265
  return millis / 1e3;
266
266
  }
@@ -590,8 +590,11 @@ var ResultFormatter = class {
590
590
  value: decodeResult.raw.fuel_remaining.toString()
591
591
  });
592
592
  }
593
+ static checksumAlgorithm(decodeResult, value) {
594
+ decodeResult.raw.checksum_algorithm = value;
595
+ }
593
596
  static checksum(decodeResult, value) {
594
- decodeResult.raw.checksum = Number("0x" + value);
597
+ decodeResult.raw.checksum = value;
595
598
  decodeResult.formatted.items.push({
596
599
  type: "message_checksum",
597
600
  code: "CHECKSUM",
@@ -640,6 +643,20 @@ var ResultFormatter = class {
640
643
  value: `${decodeResult.raw.outside_air_temperature} degrees`
641
644
  });
642
645
  }
646
+ static totalAirTemp(decodeResult, value) {
647
+ if (value.length === 0) {
648
+ return;
649
+ }
650
+ decodeResult.raw.total_air_temperature = Number(
651
+ value.replace("M", "-").replace("P", "+")
652
+ );
653
+ decodeResult.formatted.items.push({
654
+ type: "temperature",
655
+ code: "TATEMP",
656
+ label: "Total Air Temperature (C)",
657
+ value: `${decodeResult.raw.total_air_temperature} degrees`
658
+ });
659
+ }
643
660
  static heading(decodeResult, value) {
644
661
  decodeResult.raw.heading = value;
645
662
  decodeResult.formatted.items.push({
@@ -903,6 +920,15 @@ var ResultFormatter = class {
903
920
  value: `${decodeResult.raw.ground_address}`
904
921
  });
905
922
  }
923
+ static timestamp(decodeResult, value) {
924
+ decodeResult.raw.message_timestamp = value;
925
+ decodeResult.formatted.items.push({
926
+ type: "epoch",
927
+ code: "TIMESTAMP",
928
+ label: "Message Timestamp",
929
+ value: DateTimeUtils.timestampToString(value, "epoch")
930
+ });
931
+ }
906
932
  static unknown(decodeResult, value, sep = ",") {
907
933
  if (!decodeResult.remaining.text) decodeResult.remaining.text = value;
908
934
  else decodeResult.remaining.text += sep + value;
@@ -1505,10 +1531,9 @@ var Label_15 = class extends DecoderPlugin {
1505
1531
  const ddmmyy = between.substring(16, 22);
1506
1532
  const hhmm = between.substring(22, 26);
1507
1533
  if (ddmmyy != "------") {
1508
- const mmddyy = ddmmyy.substring(2, 4) + ddmmyy.substring(0, 2) + ddmmyy.substring(4);
1509
1534
  ResultFormatter.off(
1510
1535
  decodeResult,
1511
- DateTimeUtils.convertDateTimeToEpoch(hhmm + "00", mmddyy),
1536
+ DateTimeUtils.convertDateTimeToEpoch(hhmm + "00", ddmmyy),
1512
1537
  "epoch"
1513
1538
  );
1514
1539
  } else {
@@ -1975,9 +2000,14 @@ function addCompanyRoute(decodeResult, value) {
1975
2000
  // lib/utils/h1_helper.ts
1976
2001
  var H1Helper = class _H1Helper {
1977
2002
  static decodeH1Message(decodeResult, message) {
1978
- const checksum = message.slice(-4);
2003
+ const checksum = parseInt(message.slice(-4), 16);
1979
2004
  const data = message.slice(0, message.length - 4);
1980
- if (calculateChecksum(data) !== checksum) {
2005
+ let checksumAlgorithmUsed = "";
2006
+ if (crc16IbmSdlcRev(data) === checksum) {
2007
+ checksumAlgorithmUsed = "IBM-SDLC reversed";
2008
+ } else if (crc16Genibus(data) === checksum) {
2009
+ checksumAlgorithmUsed = "GENIBUS";
2010
+ } else {
1981
2011
  decodeResult.decoded = false;
1982
2012
  decodeResult.decoder.decodeLevel = "none";
1983
2013
  return false;
@@ -2054,6 +2084,9 @@ var H1Helper = class _H1Helper {
2054
2084
  case "RN":
2055
2085
  ResultFormatter.routeNumber(decodeResult, data2);
2056
2086
  break;
2087
+ case "RW":
2088
+ processRunway(decodeResult, data2.split(":"));
2089
+ break;
2057
2090
  case "SN":
2058
2091
  decodeResult.raw.serial_number = data2;
2059
2092
  break;
@@ -2079,6 +2112,7 @@ var H1Helper = class _H1Helper {
2079
2112
  ResultFormatter.unknown(decodeResult, fields[i], "/");
2080
2113
  }
2081
2114
  }
2115
+ ResultFormatter.checksumAlgorithm(decodeResult, checksumAlgorithmUsed);
2082
2116
  ResultFormatter.checksum(decodeResult, checksum);
2083
2117
  return true;
2084
2118
  }
@@ -2193,29 +2227,42 @@ function processIdentification(decodeResult, data) {
2193
2227
  }
2194
2228
  }
2195
2229
  function processDT(decodeResult, data) {
2196
- if (!decodeResult.raw.arrival_icao) {
2197
- ResultFormatter.arrivalAirport(decodeResult, data[0]);
2198
- } else if (decodeResult.raw.arrival_icao != data[0]) {
2199
- ResultFormatter.unknownArr(decodeResult, data);
2230
+ if (data.length === 9) {
2231
+ ResultFormatter.unknown(decodeResult, data[0]);
2232
+ if (!decodeResult.raw.arrival_icao) {
2233
+ ResultFormatter.arrivalAirport(decodeResult, data[1]);
2234
+ }
2235
+ ResultFormatter.arrivalRunway(decodeResult, data[2]);
2236
+ ResultFormatter.currentFuel(decodeResult, parseInt(data[3], 10));
2237
+ ResultFormatter.eta(
2238
+ decodeResult,
2239
+ DateTimeUtils.convertHHMMSSToTod(data[4])
2240
+ );
2241
+ ResultFormatter.unknown(decodeResult, data[5]);
2242
+ ResultFormatter.position(
2243
+ decodeResult,
2244
+ CoordinateUtils.decodeStringCoordinates(data[6])
2245
+ );
2246
+ ResultFormatter.unknown(decodeResult, data[7]);
2247
+ ResultFormatter.unknown(decodeResult, data[8]);
2248
+ return;
2200
2249
  }
2201
- if (data.length > 1) {
2250
+ if (data.length === 4 || data.length === 5) {
2251
+ if (!decodeResult.raw.arrival_icao) {
2252
+ ResultFormatter.arrivalAirport(decodeResult, data[0]);
2253
+ }
2202
2254
  ResultFormatter.arrivalRunway(decodeResult, data[1]);
2203
- }
2204
- if (data.length > 2) {
2205
- ResultFormatter.currentFuel(decodeResult, Number(data[2]));
2206
- }
2207
- if (data.length > 3) {
2255
+ ResultFormatter.currentFuel(decodeResult, parseInt(data[2], 10));
2208
2256
  ResultFormatter.eta(
2209
2257
  decodeResult,
2210
2258
  DateTimeUtils.convertHHMMSSToTod(data[3])
2211
2259
  );
2260
+ if (data.length > 4) {
2261
+ ResultFormatter.remainingFuel(decodeResult, Number(data[4]));
2262
+ }
2263
+ return;
2212
2264
  }
2213
- if (data.length > 4) {
2214
- ResultFormatter.remainingFuel(decodeResult, Number(data[4]));
2215
- }
2216
- if (data.length > 5) {
2217
- ResultFormatter.unknownArr(decodeResult, data);
2218
- }
2265
+ ResultFormatter.unknownArr(decodeResult, data, ",");
2219
2266
  }
2220
2267
  function processLandingReport(decodeResult, data) {
2221
2268
  if (data.length === 19) {
@@ -2279,7 +2326,11 @@ function processMessageType(decodeResult, type) {
2279
2326
  } else if (type === "FTX") {
2280
2327
  decodeResult.formatted.description = "Free Text";
2281
2328
  } else if (type === "INI") {
2282
- decodeResult.formatted.description = "Flight Plan Initial Report";
2329
+ decodeResult.formatted.description = "Initial Report";
2330
+ } else if (type === "INR") {
2331
+ decodeResult.formatted.description = "In-Range Report";
2332
+ } else if (type === "LDI") {
2333
+ decodeResult.formatted.description = "Load Distribution Information";
2283
2334
  } else if (type === "PER") {
2284
2335
  decodeResult.formatted.description = "Performance Report";
2285
2336
  } else if (type === "POS") {
@@ -2288,6 +2339,8 @@ function processMessageType(decodeResult, type) {
2288
2339
  decodeResult.formatted.description = "Progress Report";
2289
2340
  } else if (type === "PWI") {
2290
2341
  decodeResult.formatted.description = "Pilot Weather Information";
2342
+ } else if (type === "WXR") {
2343
+ decodeResult.formatted.description = "Weather Report";
2291
2344
  } else if (type === "REJ") {
2292
2345
  decodeResult.formatted.description = "Reject";
2293
2346
  } else if (type === "REQ") {
@@ -2301,7 +2354,6 @@ function processMessageType(decodeResult, type) {
2301
2354
  return true;
2302
2355
  }
2303
2356
  function processDateCode(decodeResult, data) {
2304
- decodeResult.raw.message_date = data[0];
2305
2357
  if (data.length === 1) {
2306
2358
  } else if (data.length === 2) {
2307
2359
  const date = data[0].substring(2, 4) + data[0].substring(0, 2) + data[0].substring(4, 6);
@@ -2344,11 +2396,14 @@ function processWindData(decodeResult, message) {
2344
2396
  const flightLevel = Number(message.slice(0, 3));
2345
2397
  const fields = message.slice(4).split(".");
2346
2398
  fields.forEach((field) => {
2399
+ if (field.length < 4) {
2400
+ return;
2401
+ }
2347
2402
  const data = field.split(",");
2348
2403
  const waypoint = { name: data[0] };
2349
2404
  const windData = data[1];
2350
- const windDirection = Number(windData.slice(0, 3));
2351
- const windSpeed = Number(windData.slice(3));
2405
+ const windDirection = parseInt(windData.slice(0, 3), 10);
2406
+ const windSpeed = parseInt(windData.slice(3), 10);
2352
2407
  if (data.length === 3) {
2353
2408
  const tempData = data[2];
2354
2409
  const tempFlightLevel = Number(tempData.slice(0, 3));
@@ -2375,7 +2430,16 @@ function processWindData(decodeResult, message) {
2375
2430
  });
2376
2431
  ResultFormatter.windData(decodeResult, wind);
2377
2432
  }
2378
- function calculateChecksum(data) {
2433
+ function processRunway(decodeResult, data) {
2434
+ const parts = data[0].split(".");
2435
+ const more = parts[0].split(",");
2436
+ ResultFormatter.departureRunway(decodeResult, more[0]);
2437
+ ResultFormatter.unknownArr(decodeResult, more.slice(1), ",");
2438
+ ResultFormatter.unknownArr(decodeResult, parts.slice(1), ".");
2439
+ ResultFormatter.departureAirport(decodeResult, data[1].split(",")[0]);
2440
+ ResultFormatter.unknownArr(decodeResult, data[1].split(",").slice(1), ",");
2441
+ }
2442
+ function crc16IbmSdlcRev(data) {
2379
2443
  let crc = 65535;
2380
2444
  const bytes = Buffer.from(data, "ascii");
2381
2445
  for (const byte of bytes) {
@@ -2393,7 +2457,23 @@ function calculateChecksum(data) {
2393
2457
  const nibble2 = crc >> 8 & 15;
2394
2458
  const nibble3 = crc >> 4 & 15;
2395
2459
  const nibble4 = crc & 15;
2396
- return `${nibble4.toString(16)}${nibble3.toString(16)}${nibble2.toString(16)}${nibble1.toString(16)}`.toUpperCase();
2460
+ return nibble4 << 12 | nibble3 << 8 | nibble2 << 4 | nibble1;
2461
+ }
2462
+ function crc16Genibus(data) {
2463
+ let crc = 65535;
2464
+ const polynomial = 4129;
2465
+ const bytes = Buffer.from(data, "ascii");
2466
+ for (const byte of bytes) {
2467
+ crc ^= byte << 8;
2468
+ for (let i = 0; i < 8; i++) {
2469
+ if ((crc & 32768) !== 0) {
2470
+ crc = (crc << 1 ^ polynomial) & 65535;
2471
+ } else {
2472
+ crc = crc << 1 & 65535;
2473
+ }
2474
+ }
2475
+ }
2476
+ return (crc ^ 65535) & 65535;
2397
2477
  }
2398
2478
 
2399
2479
  // lib/plugins/Label_1J_2J_FTX.ts
@@ -2467,7 +2547,7 @@ var Label_1M_Slash = class extends DecoderPlugin {
2467
2547
  decodeResult,
2468
2548
  DateTimeUtils.convertDateTimeToEpoch(
2469
2549
  results[7] + "00",
2470
- yymmdd.substring(2, 4) + yymmdd.substring(4, 6) + yymmdd.substring(0, 2)
2550
+ yymmdd.substring(4, 6) + yymmdd.substring(2, 4) + yymmdd.substring(0, 2)
2471
2551
  ),
2472
2552
  "epoch"
2473
2553
  );
@@ -3252,11 +3332,11 @@ var Label_24_Slash = class extends DecoderPlugin {
3252
3332
  decodeResult.message = message;
3253
3333
  const fields = message.text.split("/");
3254
3334
  if (fields.length == 10 && fields[0] == "" && fields[9] == "") {
3255
- const mmddyy = fields[1].substring(4, 6) + fields[1].substring(2, 4) + fields[1].substring(0, 2);
3335
+ const ddmmyy = fields[1].substring(2, 4) + fields[1].substring(4, 6) + fields[1].substring(0, 2);
3256
3336
  const hhmmss = fields[2] + "00";
3257
3337
  decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(
3258
3338
  hhmmss,
3259
- mmddyy
3339
+ ddmmyy
3260
3340
  );
3261
3341
  ResultFormatter.flightNumber(decodeResult, fields[3]);
3262
3342
  ResultFormatter.altitude(decodeResult, Number(fields[4]));
@@ -3992,7 +4072,7 @@ var Label_4N = class extends DecoderPlugin {
3992
4072
  if (fields[12].length > 1) {
3993
4073
  ResultFormatter.alternateRunway(decodeResult, fields[12].split("/")[0]);
3994
4074
  }
3995
- ResultFormatter.checksum(decodeResult, fields[32]);
4075
+ ResultFormatter.checksum(decodeResult, parseInt(fields[32], 16));
3996
4076
  ResultFormatter.unknownArr(
3997
4077
  decodeResult,
3998
4078
  [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter(
@@ -4501,7 +4581,7 @@ var Label_H1 = class extends DecoderPlugin {
4501
4581
  } else if (parts.length === 1) {
4502
4582
  decoded = H1Helper.decodeH1Message(decodeResult, msg);
4503
4583
  } else if (parts.length == 2) {
4504
- const offset = isNaN(parseInt(parts[1][1])) ? 3 : 4;
4584
+ const offset = parts[0] === "- " || isNaN(parseInt(parts[1][1])) ? 3 : 4;
4505
4585
  decoded = H1Helper.decodeH1Message(
4506
4586
  decodeResult,
4507
4587
  msg.slice(parts[0].length + offset)
@@ -4722,6 +4802,66 @@ var Label_H1_OHMA = class extends DecoderPlugin {
4722
4802
  }
4723
4803
  };
4724
4804
 
4805
+ // lib/plugins/Label_H1_Paren.ts
4806
+ var Label_H1_Paren = class extends DecoderPlugin {
4807
+ name = "label-h1-paren";
4808
+ qualifiers() {
4809
+ return {
4810
+ labels: ["H1"],
4811
+ preambles: ["("]
4812
+ };
4813
+ }
4814
+ decode(message) {
4815
+ let decodeResult = this.defaultResult();
4816
+ decodeResult.decoder.name = this.name;
4817
+ decodeResult.message = message;
4818
+ if (!message.text || !message.text.startsWith("(")) {
4819
+ decodeResult.decoded = false;
4820
+ return decodeResult;
4821
+ }
4822
+ 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})\)/;
4823
+ const match = message.text.match(regex);
4824
+ if (match && match.groups) {
4825
+ decodeResult.decoded = true;
4826
+ decodeResult.decoder.decodeLevel = "partial";
4827
+ decodeResult.formatted.description = "Position Report";
4828
+ ResultFormatter.flightNumber(decodeResult, match.groups.flight);
4829
+ ResultFormatter.position(decodeResult, {
4830
+ latitude: parseLat(match.groups.lat),
4831
+ longitude: parseLon(match.groups.lon)
4832
+ });
4833
+ ResultFormatter.time_of_day(
4834
+ decodeResult,
4835
+ DateTimeUtils.convertHHMMSSToTod(match.groups.timestamp)
4836
+ );
4837
+ ResultFormatter.altitude(
4838
+ decodeResult,
4839
+ parseInt(match.groups.alt, 10) * 100
4840
+ );
4841
+ ResultFormatter.currentFuel(decodeResult, parseFloat(match.groups.fuel));
4842
+ ResultFormatter.mach(decodeResult, parseFloat(match.groups.mach));
4843
+ ResultFormatter.unknown(decodeResult, "RMK");
4844
+ }
4845
+ return decodeResult;
4846
+ }
4847
+ };
4848
+ function parseLat(latStr) {
4849
+ const match = latStr.match(/(-?)(\d{2})(\d{2})([NS])/);
4850
+ if (!match) return NaN;
4851
+ let deg = parseInt(match[2]);
4852
+ let min = parseInt(match[3]);
4853
+ let sign = match[4] === "S" ? -1 : 1;
4854
+ return sign * (deg + min / 60);
4855
+ }
4856
+ function parseLon(lonStr) {
4857
+ const match = lonStr.match(/(\d{3})(\d{2})([EW])/);
4858
+ if (!match) return NaN;
4859
+ let deg = parseInt(match[1]);
4860
+ let min = parseInt(match[2]);
4861
+ let sign = match[3] === "W" ? -1 : 1;
4862
+ return sign * (deg + min / 60);
4863
+ }
4864
+
4725
4865
  // lib/plugins/Label_H1_StarPOS.ts
4726
4866
  var Label_H1_StarPOS = class extends DecoderPlugin {
4727
4867
  name = "label-h1-star-pos";
@@ -6117,6 +6257,7 @@ var MessageDecoder = class {
6117
6257
  this.registerPlugin(new Label_H2_02E(this));
6118
6258
  this.registerPlugin(new Label_H1_FLR(this));
6119
6259
  this.registerPlugin(new Label_H1_OHMA(this));
6260
+ this.registerPlugin(new Label_H1_Paren(this));
6120
6261
  this.registerPlugin(new Label_H1_WRN(this));
6121
6262
  this.registerPlugin(new Label_H1(this));
6122
6263
  this.registerPlugin(new Label_H1_StarPOS(this));