@airframes/acars-decoder 1.9.4 → 1.9.5

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
@@ -175,8 +175,8 @@ var CoordinateUtils = class _CoordinateUtils {
175
175
  };
176
176
  }
177
177
  static coordinateString(coords) {
178
- const latDir = coords.latitude > 0 ? "N" : "S";
179
- const lonDir = coords.longitude > 0 ? "E" : "W";
178
+ const latDir = coords.latitude >= 0 ? "N" : "S";
179
+ const lonDir = coords.longitude >= 0 ? "E" : "W";
180
180
  return `${Math.abs(coords.latitude).toFixed(3)} ${latDir}, ${Math.abs(coords.longitude).toFixed(3)} ${lonDir}`;
181
181
  }
182
182
  static getDirection(coord) {
@@ -226,16 +226,16 @@ var DateTimeUtils = class {
226
226
  const h = Number(time.substring(0, 2));
227
227
  const m = Number(time.substring(2, 4));
228
228
  const s = Number(time.substring(4, 6));
229
- const tod = h * 3600 + m * 60 + s;
230
- return tod;
229
+ const tod2 = h * 3600 + m * 60 + s;
230
+ return tod2;
231
231
  }
232
232
  static convertDayTimeToTod(time) {
233
233
  const d = Number(time.substring(0, 2));
234
234
  const h = Number(time.substring(2, 4));
235
235
  const m = Number(time.substring(4, 6));
236
236
  const s = Number(time.substring(6, 8));
237
- const tod = d * 86400 + h * 3600 + m * 60 + s;
238
- return tod;
237
+ const tod2 = d * 86400 + h * 3600 + m * 60 + s;
238
+ return tod2;
239
239
  }
240
240
  /**
241
241
  *
@@ -511,6 +511,16 @@ var ResultFormatter = class {
511
511
  value: decodeResult.raw.alternate_icao
512
512
  });
513
513
  }
514
+ /** The airport landing data is for: the destination, or the airport of a divert. */
515
+ static landingAirport(decodeResult, value) {
516
+ decodeResult.raw.landing_icao = value;
517
+ decodeResult.formatted.items.push({
518
+ type: "icao",
519
+ code: "LAND",
520
+ label: "Landing Airport",
521
+ value: decodeResult.raw.landing_icao
522
+ });
523
+ }
514
524
  static eta(decodeResult, time) {
515
525
  if (isNaN(time)) {
516
526
  return;
@@ -701,6 +711,144 @@ var ResultFormatter = class {
701
711
  value: `${decodeResult.raw.heading}`
702
712
  });
703
713
  }
714
+ static indicatedAirspeed(decodeResult, value) {
715
+ if (isNaN(value)) {
716
+ return;
717
+ }
718
+ decodeResult.raw.indicated_airspeed = value;
719
+ decodeResult.formatted.items.push({
720
+ type: "indicated_airspeed",
721
+ code: "IAS",
722
+ label: "Indicated Airspeed",
723
+ value: `${decodeResult.raw.indicated_airspeed} knots`
724
+ });
725
+ }
726
+ static track(decodeResult, value) {
727
+ if (isNaN(value)) {
728
+ return;
729
+ }
730
+ decodeResult.raw.track = value;
731
+ decodeResult.formatted.items.push({
732
+ type: "track",
733
+ code: "TRK",
734
+ label: "Track",
735
+ value: `${decodeResult.raw.track} degrees`
736
+ });
737
+ }
738
+ /** Wind at the aircraft; either half may be missing. */
739
+ static wind(decodeResult, direction, speed) {
740
+ const hasDirection = direction !== void 0 && !isNaN(direction);
741
+ const hasSpeed = speed !== void 0 && !isNaN(speed);
742
+ if (hasDirection) decodeResult.raw.wind_direction = direction;
743
+ if (hasSpeed) decodeResult.raw.wind_speed = speed;
744
+ if (!hasDirection && !hasSpeed) {
745
+ return;
746
+ }
747
+ decodeResult.formatted.items.push({
748
+ type: "wind",
749
+ code: "WIND",
750
+ label: !hasSpeed ? "Wind Direction" : !hasDirection ? "Wind Speed" : "Wind",
751
+ value: [
752
+ hasDirection ? `${direction} degrees` : "",
753
+ hasSpeed ? `${speed} knots` : ""
754
+ ].filter((s) => s !== "").join(" at ")
755
+ });
756
+ }
757
+ static variableWind(decodeResult, speed) {
758
+ if (isNaN(speed)) {
759
+ return;
760
+ }
761
+ decodeResult.raw.wind_speed = speed;
762
+ decodeResult.raw.wind_variable = true;
763
+ decodeResult.formatted.items.push({
764
+ type: "wind",
765
+ code: "WIND",
766
+ label: "Wind",
767
+ value: `variable at ${speed} knots`
768
+ });
769
+ }
770
+ /**
771
+ * One point of a recorded track. The track is the route as flown, so each point is added to
772
+ * the route's waypoints; the item shows what the aircraft measured there as well.
773
+ */
774
+ static trackPoint(decodeResult, point) {
775
+ decodeResult.raw.route = {
776
+ ...decodeResult.raw.route,
777
+ waypoints: [...decodeResult.raw.route?.waypoints ?? [], point]
778
+ };
779
+ decodeResult.formatted.items.push({
780
+ type: "track_point",
781
+ code: "TRKPT",
782
+ label: "Track Point",
783
+ value: `${CoordinateUtils.coordinateString(point)} on day ${point.day} at ${DateTimeUtils.timestampToString(point.time)}, ${point.altitude} feet, ${point.temperature} C, wind ${point.windDirection} degrees at ${point.windSpeed} knots, ${point.phase}` + (point.waterVapor === void 0 ? "" : `, water vapor ${point.waterVapor} ppmv`)
784
+ });
785
+ }
786
+ static runwayLandingData(decodeResult, data) {
787
+ decodeResult.raw.landing_runways = [
788
+ ...decodeResult.raw.landing_runways ?? [],
789
+ data
790
+ ];
791
+ const parts = [];
792
+ if (data.length !== void 0) parts.push(`${data.length} feet`);
793
+ if (data.flaps !== void 0) parts.push(`flaps ${data.flaps}`);
794
+ if (data.headwind !== void 0) {
795
+ parts.push(
796
+ data.headwind < 0 ? `tailwind ${-data.headwind} knots` : `headwind ${data.headwind} knots`
797
+ );
798
+ }
799
+ if (data.crosswind !== void 0) {
800
+ const side = { L: " left", R: " right" }[data.crosswindSide ?? "L"];
801
+ parts.push(
802
+ `crosswind ${data.crosswind} knots${data.crosswindSide ? side : ""}`
803
+ );
804
+ }
805
+ if (data.vref !== void 0) parts.push(`VREF ${data.vref} knots`);
806
+ if (data.vtarget !== void 0) {
807
+ const add = data.additive === void 0 ? "" : ` (${data.additive < 0 ? "" : "+"}${data.additive})`;
808
+ parts.push(`VTGT ${data.vtarget} knots${add}`);
809
+ }
810
+ if (data.maxLandingWeight !== void 0) {
811
+ parts.push(`MRLW ${data.maxLandingWeight}`);
812
+ }
813
+ if (data.landingWeight !== void 0) {
814
+ parts.push(`LDW ${data.landingWeight} pounds`);
815
+ }
816
+ const margins = Object.entries(data.stoppingMargins).map(
817
+ ([setting, feet]) => `${setting} ${feet} feet`
818
+ );
819
+ if (margins.length > 0) {
820
+ parts.push(`stopping margin ${margins.join(", ")}`);
821
+ }
822
+ parts.push(...data.notes ?? []);
823
+ const variant2 = data.variant === void 0 ? "" : ` (${data.variant})`;
824
+ decodeResult.formatted.items.push({
825
+ type: "runway_landing_data",
826
+ code: "RWYLDG",
827
+ label: "Runway Landing Data",
828
+ value: `${data.runway}${variant2}: ${parts.join(", ")}`
829
+ });
830
+ }
831
+ static grossWeight(decodeResult, pounds3) {
832
+ if (isNaN(pounds3)) {
833
+ return;
834
+ }
835
+ decodeResult.raw.gross_weight = pounds3;
836
+ decodeResult.formatted.items.push({
837
+ type: "gross_weight",
838
+ code: "GWT",
839
+ label: "Gross Weight",
840
+ value: `${decodeResult.raw.gross_weight} pounds`
841
+ });
842
+ }
843
+ static arrivalGate(decodeResult, value) {
844
+ decodeResult.raw.arrival_gate = value;
845
+ decodeResult.formatted.items.push({
846
+ type: "gate",
847
+ code: "AGATE",
848
+ label: "Arrival Gate",
849
+ value: decodeResult.raw.arrival_gate
850
+ });
851
+ }
704
852
  static tail(decodeResult, value) {
705
853
  decodeResult.raw.tail = value;
706
854
  decodeResult.formatted.items.push({
@@ -1020,6 +1168,14 @@ var ResultFormatter = class {
1020
1168
  value: decodeResult.raw.sequence_response.toString()
1021
1169
  });
1022
1170
  }
1171
+ /**
1172
+ * A field with no specific helper: `key` is its name in `raw` and its item type. `rawValue`
1173
+ * is stored in `raw` when it differs from the text shown (a number, for example).
1174
+ */
1175
+ static raw(decodeResult, key, code, label, value, rawValue = value) {
1176
+ decodeResult.raw[key] = rawValue;
1177
+ decodeResult.formatted.items.push({ type: key, code, label, value });
1178
+ }
1023
1179
  static unknown(decodeResult, value, sep = ",") {
1024
1180
  if (!decodeResult.remaining.text) decodeResult.remaining.text = value;
1025
1181
  else decodeResult.remaining.text += sep + value;
@@ -1365,13 +1521,13 @@ function addCompanyRoute(decodeResult, value) {
1365
1521
  const segments = value.split(".");
1366
1522
  const parens_idx = segments[0].indexOf("(");
1367
1523
  let name;
1368
- let runway;
1524
+ let runway2;
1369
1525
  if (parens_idx === -1) {
1370
1526
  name = segments[0];
1371
1527
  } else {
1372
1528
  name = segments[0].slice(0, parens_idx);
1373
1529
  const close_paren_idx = segments[0].indexOf(")");
1374
- runway = segments[0].slice(
1530
+ runway2 = segments[0].slice(
1375
1531
  parens_idx + 1,
1376
1532
  close_paren_idx === -1 ? void 0 : close_paren_idx
1377
1533
  );
@@ -1382,7 +1538,7 @@ function addCompanyRoute(decodeResult, value) {
1382
1538
  }
1383
1539
  decodeResult.raw.company_route = {
1384
1540
  name,
1385
- runway,
1541
+ runway: runway2,
1386
1542
  waypoints
1387
1543
  };
1388
1544
  decodeResult.formatted.items.push({
@@ -2218,7 +2374,54 @@ var Label_5Z_Slash = class extends DecoderPlugin {
2218
2374
  }
2219
2375
  };
2220
2376
 
2377
+ // lib/utils/swa_request.ts
2378
+ var AIRLINES = { "189": "Southwest" };
2379
+ function swaRequestHeader(decodeResult, airlineId, layout, software) {
2380
+ const airline = AIRLINES[airlineId];
2381
+ ResultFormatter.raw(
2382
+ decodeResult,
2383
+ "airline_id",
2384
+ "AIRLINE",
2385
+ "Airline ID",
2386
+ airline ? `${airlineId} (${airline})` : airlineId,
2387
+ airlineId
2388
+ );
2389
+ ResultFormatter.raw(
2390
+ decodeResult,
2391
+ "request_layout",
2392
+ "LAYOUT",
2393
+ "Request Layout",
2394
+ layout
2395
+ );
2396
+ ResultFormatter.raw(
2397
+ decodeResult,
2398
+ "software_version",
2399
+ "SWVER",
2400
+ "Software Version",
2401
+ software
2402
+ );
2403
+ }
2404
+
2221
2405
  // lib/plugins/Label_10_LDR.ts
2406
+ function pounds(value) {
2407
+ if (value.trim() === "") return void 0;
2408
+ const thousands = Number(value);
2409
+ return Number.isFinite(thousands) ? Math.round(thousands * 1e3) : void 0;
2410
+ }
2411
+ function runway(value) {
2412
+ const [rwy, suffix = ""] = value.trim().split("/");
2413
+ return suffix.trim() === "" ? rwy.trim() : `${rwy.trim()}/${suffix.trim()}`;
2414
+ }
2415
+ var RUNWAY_CONDITION = {
2416
+ 6: "dry",
2417
+ 5: "good",
2418
+ 4: "good to medium",
2419
+ 3: "medium",
2420
+ 2: "medium to poor",
2421
+ 1: "poor",
2422
+ 0: "nil"
2423
+ };
2424
+ var FLAPS = { "0": 30, "1": 40, "2": 15 };
2222
2425
  var Label_10_LDR = class extends DecoderPlugin {
2223
2426
  name = "label-10-ldr";
2224
2427
  qualifiers() {
@@ -2230,18 +2433,13 @@ var Label_10_LDR = class extends DecoderPlugin {
2230
2433
  decode(message, options = {}) {
2231
2434
  const decodeResult = this.defaultResult();
2232
2435
  decodeResult.decoder.name = this.name;
2233
- decodeResult.formatted.description = "Position Report";
2436
+ decodeResult.formatted.description = "Landing Data Request";
2234
2437
  decodeResult.message = message;
2235
2438
  const parts = message.text.split(",");
2236
- if (parts.length < 17) {
2237
- if (options.debug) {
2238
- console.log(`Decoder: Unknown 10 message: ${message.text}`);
2239
- }
2240
- ResultFormatter.unknown(decodeResult, message.text);
2241
- decodeResult.decoded = false;
2242
- decodeResult.decoder.decodeLevel = "none";
2243
- return decodeResult;
2439
+ if (parts.length < 31) {
2440
+ return this.failUnknown(decodeResult, message.text, options);
2244
2441
  }
2442
+ swaRequestHeader(decodeResult, parts[1], parts[2], parts[3]);
2245
2443
  const lat = parts[5];
2246
2444
  const lon = parts[6];
2247
2445
  const position = {
@@ -2249,25 +2447,209 @@ var Label_10_LDR = class extends DecoderPlugin {
2249
2447
  longitude: (lon[0] === "E" ? 1 : -1) * Number(lon.substring(1).trim())
2250
2448
  };
2251
2449
  ResultFormatter.position(decodeResult, position);
2252
- ResultFormatter.altitude(decodeResult, Number(parts[7]));
2450
+ if (parts[7].trim() !== "") {
2451
+ ResultFormatter.altitude(decodeResult, Number(parts[7]));
2452
+ }
2453
+ const fuel = pounds(parts[8]);
2454
+ if (fuel !== void 0) ResultFormatter.currentFuel(decodeResult, fuel);
2253
2455
  ResultFormatter.departureAirport(decodeResult, parts[9]);
2254
2456
  ResultFormatter.arrivalAirport(decodeResult, parts[10]);
2255
- ResultFormatter.alternateAirport(decodeResult, parts[11]);
2256
- ResultFormatter.arrivalRunway(decodeResult, parts[12].split("/")[0]);
2257
- const altRwy = [parts[13].split("/")[0], parts[14].split("/")[0]].filter((r) => r != "").join(",");
2457
+ ResultFormatter.landingAirport(decodeResult, parts[11]);
2458
+ ResultFormatter.arrivalRunway(decodeResult, runway(parts[12]));
2459
+ const altRwy = [runway(parts[13]), runway(parts[14])].filter((r) => r != "").join(",");
2258
2460
  if (altRwy != "") {
2259
2461
  ResultFormatter.alternateRunway(decodeResult, altRwy);
2260
2462
  }
2261
- ResultFormatter.unknownArr(decodeResult, [
2262
- ...parts.slice(0, 5),
2263
- ...parts.slice(15)
2264
- ]);
2463
+ const unknown = [parts[4]];
2464
+ const condition = parts[15].trim();
2465
+ if (/^[1-6]$/.test(condition)) {
2466
+ const code = 6 - Number(condition);
2467
+ ResultFormatter.raw(
2468
+ decodeResult,
2469
+ "runway_condition_code",
2470
+ "RCC",
2471
+ "Runway Condition Code",
2472
+ `${code} (${RUNWAY_CONDITION[code]})`,
2473
+ code
2474
+ );
2475
+ } else if (condition !== "0") {
2476
+ unknown.push(parts[15]);
2477
+ }
2478
+ const flaps = FLAPS[parts[16].trim()];
2479
+ if (flaps !== void 0) {
2480
+ ResultFormatter.raw(
2481
+ decodeResult,
2482
+ "landing_flaps",
2483
+ "FLAPS",
2484
+ "Landing Flaps",
2485
+ `${flaps}`,
2486
+ flaps
2487
+ );
2488
+ } else {
2489
+ unknown.push(parts[16]);
2490
+ }
2491
+ const [windDir, windSpeed, gust, temp, dew, altimeter] = parts.slice(17, 23).map((field) => {
2492
+ const value = field.trim();
2493
+ if (/^-?\d{1,4}$/.test(value)) return Number(value);
2494
+ if (value !== "") unknown.push(field);
2495
+ return void 0;
2496
+ });
2497
+ if (windDir !== void 0 || windSpeed !== void 0) {
2498
+ ResultFormatter.wind(decodeResult, windDir, windSpeed);
2499
+ }
2500
+ if (gust !== void 0) {
2501
+ ResultFormatter.raw(
2502
+ decodeResult,
2503
+ "wind_gust",
2504
+ "GUST",
2505
+ "Wind Gust",
2506
+ `${gust} knots`,
2507
+ gust
2508
+ );
2509
+ }
2510
+ ResultFormatter.temperature(
2511
+ decodeResult,
2512
+ temp === void 0 ? "" : `${temp}`
2513
+ );
2514
+ if (dew !== void 0) {
2515
+ ResultFormatter.raw(
2516
+ decodeResult,
2517
+ "dew_point",
2518
+ "DEWPT",
2519
+ "Dew Point (C)",
2520
+ `${dew} degrees`,
2521
+ dew
2522
+ );
2523
+ }
2524
+ if (altimeter !== void 0) {
2525
+ const inHg = altimeter / 100;
2526
+ ResultFormatter.raw(
2527
+ decodeResult,
2528
+ "altimeter",
2529
+ "ALTIM",
2530
+ "Altimeter",
2531
+ `${inHg.toFixed(2)} inHg`,
2532
+ inHg
2533
+ );
2534
+ }
2535
+ const antiIce = parts[23].trim();
2536
+ if (/^[1-9]$/.test(antiIce)) {
2537
+ ResultFormatter.raw(
2538
+ decodeResult,
2539
+ "anti_ice",
2540
+ "ANTIICE",
2541
+ "Anti-Ice",
2542
+ antiIce === "1" ? "engine" : antiIce,
2543
+ Number(antiIce)
2544
+ );
2545
+ } else if (antiIce !== "0") {
2546
+ unknown.push(parts[23]);
2547
+ }
2548
+ const flags = [
2549
+ [parts[24], "enroute_icing", "ICING", "En Route Icing"],
2550
+ // the card says HGS AIII: 113 of 113 pairs, 32 requests
2551
+ [parts[25], "hgs_approach", "HGS", "HGS Approach"]
2552
+ ];
2553
+ for (const [value, key, code, label] of flags) {
2554
+ if (value.trim() === "1") {
2555
+ ResultFormatter.raw(decodeResult, key, code, label, "yes", true);
2556
+ } else if (value.trim() !== "0") {
2557
+ unknown.push(value);
2558
+ }
2559
+ }
2560
+ const nonNormal = parts[26].trim();
2561
+ if (/^\d+$/.test(nonNormal) && Number(nonNormal) !== 0) {
2562
+ ResultFormatter.raw(
2563
+ decodeResult,
2564
+ "non_normal_configuration",
2565
+ "NNC",
2566
+ "Non-Normal Configuration",
2567
+ `${Number(nonNormal)}`,
2568
+ Number(nonNormal)
2569
+ );
2570
+ } else if (!/^0+$/.test(nonNormal)) {
2571
+ unknown.push(parts[26]);
2572
+ }
2573
+ if (parts[27].trim() !== "") unknown.push(parts[27]);
2574
+ const weights = [
2575
+ ["zero_fuel_weight", "ZFW", "Zero Fuel Weight", parts[28]],
2576
+ ["landing_fuel", "LFUEL", "Landing Fuel", parts[29]],
2577
+ ["landing_weight", "LDW", "Landing Weight", parts[30]]
2578
+ ];
2579
+ for (const [key, code, label, value] of weights) {
2580
+ const amount = pounds(value);
2581
+ if (amount === void 0) continue;
2582
+ ResultFormatter.raw(
2583
+ decodeResult,
2584
+ key,
2585
+ code,
2586
+ label,
2587
+ `${amount} pounds`,
2588
+ amount
2589
+ );
2590
+ }
2591
+ const gradient = (parts[31] ?? "").trim();
2592
+ if (/^\d+$/.test(gradient)) {
2593
+ ResultFormatter.raw(
2594
+ decodeResult,
2595
+ "missed_approach_gradient",
2596
+ "MAGRAD",
2597
+ "Missed Approach Climb Gradient",
2598
+ `${Number(gradient)} feet per nautical mile`,
2599
+ Number(gradient)
2600
+ );
2601
+ } else if (gradient !== "" && gradient !== "----.") {
2602
+ unknown.push(parts[31]);
2603
+ }
2604
+ const missedAltitude = (parts[32] ?? "").trim();
2605
+ if (/^\d+$/.test(missedAltitude)) {
2606
+ ResultFormatter.raw(
2607
+ decodeResult,
2608
+ "missed_approach_altitude",
2609
+ "MAALT",
2610
+ "Missed Approach Altitude",
2611
+ `${Number(missedAltitude)} feet`,
2612
+ Number(missedAltitude)
2613
+ );
2614
+ } else if (missedAltitude !== "") {
2615
+ unknown.push(parts[32]);
2616
+ }
2617
+ unknown.push(...parts.slice(33).filter((field) => field.trim() !== ""));
2618
+ ResultFormatter.unknownArr(decodeResult, unknown);
2265
2619
  decodeResult.decoded = true;
2266
2620
  decodeResult.decoder.decodeLevel = "partial";
2267
2621
  return decodeResult;
2268
2622
  }
2269
2623
  };
2270
2624
 
2625
+ // lib/plugins/Label_10_LRW.ts
2626
+ var Label_10_LRW = class extends DecoderPlugin {
2627
+ name = "label-10-lrw";
2628
+ qualifiers() {
2629
+ return {
2630
+ labels: ["10"],
2631
+ preambles: ["LRW01,"]
2632
+ };
2633
+ }
2634
+ decode(message, options = {}) {
2635
+ const decodeResult = this.initResult(message, "Landing Runways Request");
2636
+ const parts = message.text.split(",");
2637
+ if (parts.length < 8 || parts[7].trim() === "") {
2638
+ return this.failUnknown(decodeResult, message.text, options);
2639
+ }
2640
+ swaRequestHeader(decodeResult, parts[1], parts[2], parts[3]);
2641
+ ResultFormatter.departureAirport(decodeResult, parts[6]);
2642
+ ResultFormatter.arrivalAirport(decodeResult, parts[7]);
2643
+ ResultFormatter.unknownArr(decodeResult, [
2644
+ parts[4],
2645
+ parts[5],
2646
+ ...parts.slice(8).filter((field) => field.trim() !== "")
2647
+ ]);
2648
+ this.setDecodeLevel(decodeResult, true);
2649
+ return decodeResult;
2650
+ }
2651
+ };
2652
+
2271
2653
  // lib/plugins/Label_10_POS.ts
2272
2654
  var Label_10_POS = class extends DecoderPlugin {
2273
2655
  name = "label-10-pos";
@@ -2367,6 +2749,364 @@ var Label_10_Slash = class extends DecoderPlugin {
2367
2749
  }
2368
2750
  };
2369
2751
 
2752
+ // lib/utils/runway_utils.ts
2753
+ var SIDES = {
2754
+ N: "north",
2755
+ NE: "northeast",
2756
+ E: "east",
2757
+ SE: "southeast",
2758
+ S: "south",
2759
+ SW: "southwest",
2760
+ W: "west",
2761
+ NW: "northwest"
2762
+ };
2763
+ var RunwayUtils = class {
2764
+ /**
2765
+ * Describe the condition that names a runway variant in Southwest landing data, e.g.
2766
+ * `11/AIB -E1804F`: the runway, the variant's code and its condition.
2767
+ *
2768
+ * -E1804F is 1804 feet taken off the east end, -SWCBL the arresting cable at the southwest
2769
+ * end. Checked on the LRW01 runway lists of six airports, where every variant's length is
2770
+ * the runway's less the feet given, or less 1500 feet for a cable. LAHSO is sent as written.
2771
+ *
2772
+ * @param name - the runway as sent, with its variant
2773
+ * @returns a description of the condition, or undefined when the name has none
2774
+ */
2775
+ static variantDescription(name) {
2776
+ const text = name.trim();
2777
+ const cut = text.match(/-(NE|NW|SE|SW|N|E|S|W)(\d+)F$/);
2778
+ if (cut) {
2779
+ return `${Number(cut[2])} feet off the ${SIDES[cut[1]]} end`;
2780
+ }
2781
+ const cable = text.match(/-(NE|NW|SE|SW|N|E|S|W)CBL$/);
2782
+ if (cable) {
2783
+ return `arresting cable at the ${SIDES[cable[1]]} end`;
2784
+ }
2785
+ if (/(^|\s)LAHSO$/.test(text)) {
2786
+ return "land and hold short";
2787
+ }
2788
+ return void 0;
2789
+ }
2790
+ };
2791
+
2792
+ // lib/plugins/Label_11_LDR.ts
2793
+ function tod(hours, minutes) {
2794
+ const h = Number(hours);
2795
+ const m = Number(minutes);
2796
+ return h <= 23 && m <= 59 ? h * 3600 + m * 60 : void 0;
2797
+ }
2798
+ function pounds2(value) {
2799
+ return Math.round(Number(value) * 1e3);
2800
+ }
2801
+ function variant(runway2) {
2802
+ const description = RunwayUtils.variantDescription(runway2);
2803
+ return description === void 0 ? {} : { variant: description };
2804
+ }
2805
+ var HEADER = [
2806
+ /^LDR01,(\w{4}) LANDING DATA,FLT(?:\s+METAR-(\d{2})(\d{2})Z)?\s*$/,
2807
+ /^(\d{1,4})\s+WIND\s+LDW\s*$/,
2808
+ /^TIME\s+(\d{3}|VRB)\/(\d{2,3})G(\d{2,3}|--)\s+(\d+\.\d)\s*$/,
2809
+ /^(\d{2})(\d{2})Z\s+OAT\/DEW\s*(\S*)\s*$/,
2810
+ /^\s+(-?\d{2,3})\/(-?\d{2,3})\s*(\S*)\s*$/,
2811
+ /^\s+QNH\s*(\S*)\s*$/,
2812
+ // QNH in inches of mercury, or hectopascals outside the US
2813
+ /^\s+(\d{2}\.\d{2}|\d{4})\s*(\S*)\s*$/
2814
+ ];
2815
+ var RUNWAY = /^(\d{1,2}[LRC]?(?:\/\S+)?(?: -\S+)?)\s+(\d{1,5})\s*$/;
2816
+ var RUNWAY_START = /^\d{1,2}[LRC]?(?:\/\S+)?(?: -\S+)?(?:\s{2,}\d*)?\s*$/;
2817
+ var ALTERNATIVES = "--- ALTERNATIVE RWYS ---";
2818
+ var ALTERNATIVE = /^(?:(.+?)\s{2,})?(\d{1,2}[LRC]?(?:\/\S+)?)\s*$/;
2819
+ var BLOCK = [
2820
+ /^FLAPS (\d{2}|--)\s*$/,
2821
+ /^VREF\/VTGT\s+(?:(\d{2})([HT])\/(\d{2})([LR])|---\/---)\s*$/,
2822
+ /^\s*(?:(\d{3})\/(\d{3}) ([+-]\d+)|---\/---)\s+(AB\d)\s+(\d+|----)\s*$/,
2823
+ /^MRLW (\d+\.\d\/\S|-+\/-)\s+(AB\d)\s+(\d+|----)\s*$/,
2824
+ /^\s*LDW (\d+\.\d)\s+(ABM)\s+(\d+|----)\s*$/
2825
+ ];
2826
+ var Label_11_LDR = class extends DecoderPlugin {
2827
+ name = "label-11-ldr";
2828
+ qualifiers() {
2829
+ return {
2830
+ labels: ["11"],
2831
+ preambles: ["LDR01,"]
2832
+ };
2833
+ }
2834
+ decode(message, options = {}) {
2835
+ const decodeResult = this.initResult(message, "Landing Data");
2836
+ const nl = message.text.includes("\r\n") ? "\r\n" : "\n";
2837
+ const lines = message.text.split(/\r?\n/);
2838
+ const first = lines[0].match(HEADER[0]);
2839
+ if (!first) {
2840
+ return this.failUnknown(decodeResult, message.text, options);
2841
+ }
2842
+ const unknown = [];
2843
+ ResultFormatter.landingAirport(decodeResult, first[1]);
2844
+ const metar = first[2] === void 0 ? void 0 : tod(first[2], first[3]);
2845
+ if (first[2] !== void 0 && metar === void 0) {
2846
+ unknown.push(`METAR-${first[2]}${first[3]}Z`);
2847
+ }
2848
+ if (metar !== void 0) {
2849
+ ResultFormatter.raw(
2850
+ decodeResult,
2851
+ "metar_time",
2852
+ "METAR",
2853
+ "METAR Time",
2854
+ DateTimeUtils.timestampToString(metar),
2855
+ metar
2856
+ );
2857
+ }
2858
+ const runways = [];
2859
+ let i = 1;
2860
+ for (; i < HEADER.length && i < lines.length; i++) {
2861
+ const m = lines[i].match(HEADER[i]);
2862
+ if (!m) break;
2863
+ if (i === 1) {
2864
+ ResultFormatter.flightNumber(decodeResult, m[1]);
2865
+ } else if (i === 2) {
2866
+ if (m[1] === "VRB") {
2867
+ ResultFormatter.variableWind(decodeResult, Number(m[2]));
2868
+ } else {
2869
+ ResultFormatter.wind(decodeResult, Number(m[1]), Number(m[2]));
2870
+ }
2871
+ if (m[3] !== "--") {
2872
+ ResultFormatter.raw(
2873
+ decodeResult,
2874
+ "wind_gust",
2875
+ "GUST",
2876
+ "Wind Gust",
2877
+ `${Number(m[3])} knots`,
2878
+ Number(m[3])
2879
+ );
2880
+ }
2881
+ ResultFormatter.raw(
2882
+ decodeResult,
2883
+ "landing_weight",
2884
+ "LDW",
2885
+ "Landing Weight",
2886
+ `${pounds2(m[4])} pounds`,
2887
+ pounds2(m[4])
2888
+ );
2889
+ } else if (i === 3) {
2890
+ const time = tod(m[1], m[2]);
2891
+ if (time === void 0) {
2892
+ unknown.push(`${m[1]}${m[2]}Z`);
2893
+ } else {
2894
+ ResultFormatter.timestamp(decodeResult, time);
2895
+ }
2896
+ runways.push(m[3]);
2897
+ } else if (i === 4) {
2898
+ ResultFormatter.temperature(decodeResult, `${Number(m[1])}`);
2899
+ ResultFormatter.raw(
2900
+ decodeResult,
2901
+ "dew_point",
2902
+ "DEWPT",
2903
+ "Dew Point (C)",
2904
+ `${Number(m[2])} degrees`,
2905
+ Number(m[2])
2906
+ );
2907
+ runways.push(m[3]);
2908
+ } else if (i === 5) {
2909
+ runways.push(m[1]);
2910
+ } else {
2911
+ ResultFormatter.raw(
2912
+ decodeResult,
2913
+ "altimeter",
2914
+ "ALTIM",
2915
+ "Altimeter",
2916
+ `${m[1]} ${m[1].includes(".") ? "inHg" : "hPa"}`,
2917
+ Number(m[1])
2918
+ );
2919
+ runways.push(m[2]);
2920
+ }
2921
+ }
2922
+ const listed = runways.filter((r) => r !== "");
2923
+ if (listed.length > 0) {
2924
+ ResultFormatter.raw(
2925
+ decodeResult,
2926
+ "runways",
2927
+ "RWYS",
2928
+ "Runways",
2929
+ listed.join(", "),
2930
+ listed
2931
+ );
2932
+ }
2933
+ const rest = lines.slice(i);
2934
+ let at = 0;
2935
+ const alternatives = [];
2936
+ if (rest[0]?.trim() === "REMARKS") {
2937
+ const remarks = [];
2938
+ for (at = 1; at < rest.length && !RUNWAY_START.test(rest[at]); at++) {
2939
+ if (rest[at].trim() === ALTERNATIVES) {
2940
+ at = this.alternatives(rest, at + 1, alternatives, unknown);
2941
+ break;
2942
+ }
2943
+ if (rest[at].trim() !== "") remarks.push(rest[at].trim());
2944
+ }
2945
+ if (remarks.length > 0) {
2946
+ ResultFormatter.raw(
2947
+ decodeResult,
2948
+ "remarks",
2949
+ "RMK",
2950
+ "Remarks",
2951
+ remarks.join(" ")
2952
+ );
2953
+ }
2954
+ }
2955
+ if (alternatives.length > 0) {
2956
+ ResultFormatter.raw(
2957
+ decodeResult,
2958
+ "alternative_runways",
2959
+ "ALTRWY",
2960
+ "Alternative Runways",
2961
+ alternatives.map((a) => a.variant ? `${a.runway} (${a.variant})` : a.runway).join(", "),
2962
+ alternatives
2963
+ );
2964
+ }
2965
+ const blocks = [];
2966
+ while (at < rest.length) {
2967
+ const block = this.runwayBlock(rest.slice(at, at + 1 + BLOCK.length));
2968
+ if (block) {
2969
+ blocks.push(block);
2970
+ at += 1 + BLOCK.length;
2971
+ continue;
2972
+ }
2973
+ const line = rest[at];
2974
+ const head = line.match(RUNWAY);
2975
+ const next = rest.slice(at + 1).find((l) => l.trim() !== "");
2976
+ if (head && next !== void 0 && !next.startsWith("FLAPS") && // a FLAPS line cut off by the end of the block
2977
+ !"FLAPS".startsWith(next.trim()) && !RUNWAY_START.test(next)) {
2978
+ blocks.push({
2979
+ runway: head[1],
2980
+ ...variant(head[1]),
2981
+ length: Number(head[2]),
2982
+ stoppingMargins: {}
2983
+ });
2984
+ at++;
2985
+ continue;
2986
+ }
2987
+ const last = blocks[blocks.length - 1];
2988
+ if (line.trim() !== "") {
2989
+ if (last && !RUNWAY_START.test(line)) {
2990
+ last.notes = [...last.notes ?? [], line.trim()];
2991
+ } else {
2992
+ unknown.push(...rest.slice(at).filter((l) => l.trim() !== ""));
2993
+ break;
2994
+ }
2995
+ }
2996
+ at++;
2997
+ }
2998
+ for (const block of blocks) {
2999
+ ResultFormatter.runwayLandingData(decodeResult, block);
3000
+ }
3001
+ if (unknown.length > 0) {
3002
+ ResultFormatter.unknown(decodeResult, unknown.join(nl), nl);
3003
+ }
3004
+ this.setDecodeLevel(
3005
+ decodeResult,
3006
+ true,
3007
+ i < HEADER.length ? "partial" : void 0
3008
+ );
3009
+ return decodeResult;
3010
+ }
3011
+ /** The six lines of one runway, or undefined if they are not a whole block. */
3012
+ runwayBlock(lines) {
3013
+ const head = lines[0]?.match(RUNWAY);
3014
+ if (!head || lines.length < 1 + BLOCK.length) return void 0;
3015
+ const m = BLOCK.map((re, n) => lines[n + 1].match(re));
3016
+ if (m.some((x) => !x)) return void 0;
3017
+ const [flaps, wind, speeds, mrlw, ldw] = m;
3018
+ const data = {
3019
+ runway: head[1],
3020
+ ...variant(head[1]),
3021
+ length: Number(head[2]),
3022
+ stoppingMargins: {}
3023
+ };
3024
+ if (flaps[1] !== "--") data.flaps = Number(flaps[1]);
3025
+ if (wind[1] !== void 0) {
3026
+ data.headwind = Number(wind[1]) * (wind[2] === "T" ? -1 : 1);
3027
+ data.crosswind = Number(wind[3]);
3028
+ data.crosswindSide = wind[4];
3029
+ }
3030
+ if (speeds[1] !== void 0) {
3031
+ data.vref = Number(speeds[1]);
3032
+ data.vtarget = Number(speeds[2]);
3033
+ data.additive = Number(speeds[3]);
3034
+ }
3035
+ if (!mrlw[1].startsWith("-")) data.maxLandingWeight = mrlw[1];
3036
+ data.landingWeight = pounds2(ldw[1]);
3037
+ for (const [setting, feet] of [
3038
+ [speeds[4], speeds[5]],
3039
+ [mrlw[2], mrlw[3]],
3040
+ [ldw[2], ldw[3]]
3041
+ ]) {
3042
+ if (feet !== "----") data.stoppingMargins[setting] = Number(feet);
3043
+ }
3044
+ return data;
3045
+ }
3046
+ /**
3047
+ * The runways listed after ALTERNATIVE RWYS, which run to the end of the card. Returns the
3048
+ * index of the line after them.
3049
+ */
3050
+ alternatives(lines, from, out, unknown) {
3051
+ for (let n = from; n < lines.length; n++) {
3052
+ const line = lines[n];
3053
+ if (line.trim() === "") continue;
3054
+ const m = line.match(ALTERNATIVE);
3055
+ if (!m || n === lines.length - 1) {
3056
+ unknown.push(line.trim());
3057
+ continue;
3058
+ }
3059
+ const name = m[1]?.trim();
3060
+ out.push({
3061
+ runway: m[2],
3062
+ ...name === void 0 ? {} : { name },
3063
+ ...variant(name ?? m[2])
3064
+ });
3065
+ }
3066
+ return lines.length;
3067
+ }
3068
+ };
3069
+
3070
+ // lib/plugins/Label_11_LRW.ts
3071
+ var RUNWAY2 = /^(\d{1,2}[LRC]?)\s?(?:\/(\S+))?\s+(?:(-\S+|LAHSO)\s+)?(\d{3,5})\s*$/;
3072
+ var Label_11_LRW = class extends DecoderPlugin {
3073
+ name = "label-11-lrw";
3074
+ qualifiers() {
3075
+ return {
3076
+ labels: ["11"],
3077
+ preambles: ["LRW01,"]
3078
+ };
3079
+ }
3080
+ decode(message, options = {}) {
3081
+ const decodeResult = this.initResult(message, "Landing Runways");
3082
+ const nl = message.text.includes("\r\n") ? "\r\n" : "\n";
3083
+ const first = message.text.match(/^LRW01,(\w{4}) LANDING RUNWAYS,/);
3084
+ if (!first) {
3085
+ return this.failUnknown(decodeResult, message.text, options);
3086
+ }
3087
+ ResultFormatter.landingAirport(decodeResult, first[1]);
3088
+ const lines = message.text.slice(first[0].length).split(/\r?\n/);
3089
+ for (const line of lines) {
3090
+ if (line.trim() === "") continue;
3091
+ const m = line.match(RUNWAY2);
3092
+ if (!m) {
3093
+ ResultFormatter.unknown(decodeResult, line.trim(), nl);
3094
+ continue;
3095
+ }
3096
+ const runway2 = [m[2] ? `${m[1]}/${m[2]}` : m[1], m[3]].filter((part) => part !== void 0).join(" ");
3097
+ const description = RunwayUtils.variantDescription(runway2);
3098
+ ResultFormatter.runwayLandingData(decodeResult, {
3099
+ runway: runway2,
3100
+ ...description === void 0 ? {} : { variant: description },
3101
+ length: Number(m[4]),
3102
+ stoppingMargins: {}
3103
+ });
3104
+ }
3105
+ this.setDecodeLevel(decodeResult, true);
3106
+ return decodeResult;
3107
+ }
3108
+ };
3109
+
2370
3110
  // lib/plugins/Label_12_N_Space.ts
2371
3111
  var Label_12_N_Space = class extends DecoderPlugin {
2372
3112
  name = "label-12-n-space";
@@ -3794,6 +4534,713 @@ var Label_30_Slash_EA = class extends DecoderPlugin {
3794
4534
  }
3795
4535
  };
3796
4536
 
4537
+ // lib/utils/swa_compressed.ts
4538
+ var SWA_ALPHABET = " (),-./0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ";
4539
+ var SWA_UNREAD = "\xA4";
4540
+ var SWA_LABEL_37_TABLES = {
4541
+ "01": "VIK86MRN5-0PZS4UYXACEHLOTW BGD.123Q(7J9:F)/,",
4542
+ "02": "HG.KZL97QY15WRVTN4JA06(D ,:3MPBICU/8EOFSX)2-",
4543
+ "03": "4GXE/QU5FT)20P8RSJ-169AWHLB.,37MNVCYODIKZ (:",
4544
+ "04": "1BEHMPSUY,048DG269JLOQTWZ (-/37:ACFIKNRVX).5",
4545
+ "05": "3579BDFHJLNPRTVXZ(,.02468:ACEGIKMOQSUWY )-/1",
4546
+ "06": "AGFEDCBRQPONMLKJIH:9876543210/.-,)( ZYXWVUTS",
4547
+ "07": ":.02468ACEGIKMOQSUWY )-/13579BDFHJLNPRTVXZ(,",
4548
+ "08": "1MDS/JQ.OWZ79LNR532-6(: ,APEFTVIKYB08H)U4CXG",
4549
+ "09": "6LQMSR UZYVX(7:3-5KPTACB)021,894NODGJEFIHW./"
4550
+ };
4551
+ var SWA_LABEL_40_TABLES = {
4552
+ "01": "IRXZ2MY3NOP71-S,UVAJBLCWKD(T)E.0F4Q/6G8 H:95",
4553
+ "02": "GEXHZ)QC3YJ:4D0R/IBMOFSU( NA,.K9TL16V8P75W2-",
4554
+ "03": "XY3KAJ-5B4L 0CM7DZEIQT,1(GU:VHNOS6.892/PF)RW",
4555
+ "04": "HIX2JYK3 8L4Z9M5:NO(P6)Q7,RASB-TC.DU/E0VFW1G",
4556
+ "05": "V:WAXBYCZD E(F)G,HI-J.K/L0M1N2O3P4Q5R6S7T8U9",
4557
+ "06": "RQPONMLKJIHGFEDCBA /.-,)(:9876543210ZYXWVUTS",
4558
+ "07": "CYDZE(F)G,H-I.J/K 0L1M2N3O4P5Q6R7S8T9U:VAWBX",
4559
+ "08": "FDUGB0-R A:W9C4S5EHQX)JKZTN.O6(71I/8,LVM2YP3",
4560
+ "09": "/5GK9YZHJI8N: 6LM7DFEQTURWVSA(,OPB).-C03X421"
4561
+ };
4562
+ var PASS_37 = { "*": "*", ">": "+" };
4563
+ var PASS_40 = {
4564
+ "*": "*",
4565
+ "?": "?",
4566
+ "'": "'",
4567
+ "#": "#",
4568
+ _: "_"
4569
+ };
4570
+ function lookup(table, c) {
4571
+ const i = SWA_ALPHABET.indexOf(c);
4572
+ return i < 0 ? void 0 : table[i];
4573
+ }
4574
+ function plain(table, body, pass) {
4575
+ let out = "";
4576
+ for (const c of body) {
4577
+ if (c === "\n") out += "|";
4578
+ else out += lookup(table, c) ?? pass[c] ?? SWA_UNREAD;
4579
+ }
4580
+ return out;
4581
+ }
4582
+ function decodeSwaLabel37(body) {
4583
+ const b = body.replace(/\r/g, "");
4584
+ const table = SWA_LABEL_37_TABLES[b.substring(0, 2)];
4585
+ if (!table) return void 0;
4586
+ let nl = b.indexOf("\n");
4587
+ if (nl !== 7 && b.length > 7 && lookup(table, b[7]) === ",") nl = 7;
4588
+ if (nl < 0 || nl > 8) return void 0;
4589
+ let type = "";
4590
+ for (const c of b.substring(2, Math.min(nl, 7))) {
4591
+ type += lookup(table, c) ?? SWA_UNREAD;
4592
+ }
4593
+ return { type, text: plain(table, b.substring(nl + 1), PASS_37) };
4594
+ }
4595
+ function decodeSwaLabel40(body) {
4596
+ const b = body.replace(/\r/g, "");
4597
+ if (b.startsWith("POS01,")) {
4598
+ return { type: "POS01", text: b.trim() };
4599
+ }
4600
+ const code = b[5] === "," ? b.substring(6, 8) : "";
4601
+ if (code === "00") {
4602
+ return {
4603
+ type: b.substring(0, 5),
4604
+ text: b.substring(8).replace(/\n/g, "|")
4605
+ };
4606
+ }
4607
+ const table = SWA_LABEL_40_TABLES[code];
4608
+ if (!table) return void 0;
4609
+ return {
4610
+ type: b.substring(0, 5),
4611
+ text: plain(table, b.substring(8), PASS_40)
4612
+ };
4613
+ }
4614
+ var SWA_TYPES = {
4615
+ "37": /* @__PURE__ */ new Set([
4616
+ "ADV01",
4617
+ "CLB02",
4618
+ "DIV02",
4619
+ "DVC01",
4620
+ "ETA03",
4621
+ "FCM01",
4622
+ "FCR03",
4623
+ "FFD01",
4624
+ "FHD04",
4625
+ "FLI01",
4626
+ "FTD02",
4627
+ "GAM02",
4628
+ "GSR02",
4629
+ "HGD03",
4630
+ "HLD01",
4631
+ "INN03",
4632
+ "INR04",
4633
+ "MXR03",
4634
+ "OFF02",
4635
+ "OFP01",
4636
+ "ONN02",
4637
+ "OUT03",
4638
+ "PFR03",
4639
+ "PFR04",
4640
+ "PJS01",
4641
+ "PNS01",
4642
+ "POS01",
4643
+ "POS02",
4644
+ "PRM01",
4645
+ "RRR01",
4646
+ "RTG03",
4647
+ "RTG04",
4648
+ "RTG05",
4649
+ "TAR02",
4650
+ "TDM01",
4651
+ "TOD03",
4652
+ "TRB02",
4653
+ "TXI02",
4654
+ "VXR02"
4655
+ ]),
4656
+ "40": /* @__PURE__ */ new Set([
4657
+ "ASR01",
4658
+ "DVP01",
4659
+ "FTX01",
4660
+ "GAR03",
4661
+ "GEN01",
4662
+ "PIC01",
4663
+ "POS01",
4664
+ "WFC01"
4665
+ ])
4666
+ };
4667
+ function isSwaType(type, label) {
4668
+ return SWA_TYPES[label].has(type);
4669
+ }
4670
+
4671
+ // lib/utils/swa_fields.ts
4672
+ var APT = "(?:[A-Z]{4}| {4}|)";
4673
+ var LAT = "(?:[NS] ?\\d{1,2}\\.\\d{3}|-{4}\\.-{3}|)";
4674
+ var LON = "(?:[EW] ?\\d{1,3}\\.\\d{3}|-{4}\\.-{3}|)";
4675
+ var T4 = "(?:\\d{4}|\\*{4}|-{4})";
4676
+ var FOB = "(?: ?\\d{1,2}\\.\\d|\\*\\*\\.\\*)";
4677
+ var INT = "(?: *-? *\\d+| *|-+|\\*+)";
4678
+ var MACH = "\\.\\d{3}";
4679
+ var HMS = "\\d{6}";
4680
+ var OC = "[OC]";
4681
+ var POS = [
4682
+ APT,
4683
+ APT,
4684
+ LAT,
4685
+ LON,
4686
+ INT,
4687
+ INT,
4688
+ INT,
4689
+ MACH,
4690
+ FOB,
4691
+ T4,
4692
+ INT,
4693
+ INT,
4694
+ INT,
4695
+ INT
4696
+ ];
4697
+ var ASR = " *-?\\d+(?:\\.[\\d-])?|XX\\.X";
4698
+ var SWA_LAYOUTS = {
4699
+ ASR01: [[ASR, ASR, ASR, ASR, ASR, ASR, ASR, ASR, "", ""]],
4700
+ CLB02: [[APT, APT, LAT, "", LON, FOB, T4]],
4701
+ ETA03: [[APT, APT, T4, FOB, LAT, LON]],
4702
+ FCM01: [["L[\\d-]", "R[\\d-]", "A[\\d-]", "B[\\d-]", "\\d+", T4]],
4703
+ FHD04: [
4704
+ [
4705
+ ...POS,
4706
+ "(?:OUT|OFF|ON|IN|RET IN)",
4707
+ "X",
4708
+ INT,
4709
+ " *\\d*",
4710
+ HMS,
4711
+ "(?:\\d{6}|-{6})",
4712
+ "\\d{5}"
4713
+ ]
4714
+ ],
4715
+ FLI01: [[APT, APT, T4, FOB, LAT, LON]],
4716
+ GAM02: [[APT, APT, T4, LAT, LON]],
4717
+ GAR03: [
4718
+ [
4719
+ "[A-Z0-9]{1,4} *",
4720
+ "[YN]",
4721
+ "[YN]",
4722
+ "[YN]",
4723
+ "[YN]",
4724
+ "[YN]",
4725
+ "(?:1[23]\\d\\.\\d{1,3}|999\\.999)",
4726
+ "",
4727
+ ""
4728
+ ]
4729
+ ],
4730
+ HGD03: [[...POS, INT]],
4731
+ INN03: [[APT, APT, T4, T4, FOB, OC, OC, OC, OC]],
4732
+ INR04: [[APT, APT, T4, T4, FOB, LAT, LON]],
4733
+ OUT03: [[APT, APT, T4, FOB, FOB, T4]],
4734
+ POS01: [
4735
+ [...POS, INT],
4736
+ [...POS, INT, HMS]
4737
+ ],
4738
+ POS02: [[...POS, INT, HMS]],
4739
+ RTG04: [[APT, APT, T4, T4, T4, FOB, OC, OC, OC, OC]],
4740
+ RTG05: [[APT, APT, T4, T4, T4, FOB, OC, OC, OC, OC]],
4741
+ TDM01: [[APT, APT, LAT, LON, "(?:L1|L2|CR|SV)", OC, T4, HMS]],
4742
+ TXI02: [[APT, APT, T4, FOB]],
4743
+ VXR02: [["[A-Z]{3}"]]
4744
+ };
4745
+ var SWA_PREFIX_LAYOUTS = {
4746
+ MXR03: [APT, "(?:\\d{4}|)", "[0-3]"]
4747
+ };
4748
+ function swaFields(text) {
4749
+ return text.replace(/\|/g, ",").split(",");
4750
+ }
4751
+ function swaMatchesLayout(fields, layouts) {
4752
+ return layouts.some(
4753
+ (layout) => layout.length === fields.length && layout.every((rx, i) => new RegExp(`^(?:${rx})$`).test(fields[i]))
4754
+ );
4755
+ }
4756
+ var SwaFieldWriter = class {
4757
+ constructor(decodeResult, fields) {
4758
+ this.decodeResult = decodeResult;
4759
+ this.fields = fields;
4760
+ }
4761
+ decodeResult;
4762
+ fields;
4763
+ used = /* @__PURE__ */ new Set();
4764
+ take(i) {
4765
+ this.used.add(i);
4766
+ return this.fields[i] ?? "";
4767
+ }
4768
+ /** A field with no specific ResultFormatter helper; `key` is its name in `raw`. */
4769
+ item(i, key, code, label, value) {
4770
+ this.used.add(i);
4771
+ ResultFormatter.raw(this.decodeResult, key, code, label, value);
4772
+ }
4773
+ /** Marks a field as read without writing it (it was written together with another). */
4774
+ use(i) {
4775
+ return this.take(i);
4776
+ }
4777
+ departure(i) {
4778
+ const v = this.take(i);
4779
+ if (/^[A-Z]{4}$/.test(v))
4780
+ ResultFormatter.departureAirport(this.decodeResult, v);
4781
+ }
4782
+ arrival(i) {
4783
+ const v = this.take(i);
4784
+ if (/^[A-Z]{4}$/.test(v))
4785
+ ResultFormatter.arrivalAirport(this.decodeResult, v);
4786
+ }
4787
+ /**
4788
+ * Writes the position when both coordinates are present and in range. Otherwise the
4789
+ * placeholders are consumed and any coordinate that was sent stays for `remaining`.
4790
+ */
4791
+ position(latIndex, lonIndex) {
4792
+ const lat = this.fields[latIndex] ?? "";
4793
+ const lon = this.fields[lonIndex] ?? "";
4794
+ const latitude = /^[NS] ?\d/.test(lat) ? CoordinateUtils.getDirection(lat) * Number(lat.substring(1).trim()) : NaN;
4795
+ const longitude = /^[EW] ?\d/.test(lon) ? CoordinateUtils.getDirection(lon) * Number(lon.substring(1).trim()) : NaN;
4796
+ if (Math.abs(latitude) <= 90 && Math.abs(longitude) <= 180) {
4797
+ this.used.add(latIndex);
4798
+ this.used.add(lonIndex);
4799
+ ResultFormatter.position(this.decodeResult, { latitude, longitude });
4800
+ return;
4801
+ }
4802
+ if (!/^[NS] ?\d/.test(lat)) this.used.add(latIndex);
4803
+ if (!/^[EW] ?\d/.test(lon)) this.used.add(lonIndex);
4804
+ }
4805
+ /** A whole number, or undefined for a placeholder. */
4806
+ int(i) {
4807
+ const v = this.take(i).replace(/ /g, "");
4808
+ return /^-?\d+$/.test(v) ? Number(v) : void 0;
4809
+ }
4810
+ altitude(i) {
4811
+ const v = this.int(i);
4812
+ if (v !== void 0) ResultFormatter.altitude(this.decodeResult, v);
4813
+ }
4814
+ /** Fuel on board, sent in thousands of pounds; written in pounds. */
4815
+ fuel(i) {
4816
+ const v = this.take(i).trim();
4817
+ if (/^\d+\.\d$/.test(v)) {
4818
+ ResultFormatter.currentFuel(
4819
+ this.decodeResult,
4820
+ Math.round(Number(v) * 1e3)
4821
+ );
4822
+ }
4823
+ }
4824
+ /**
4825
+ * HHMM or HHMMSS as seconds of the day. A placeholder is consumed; a time out of range is
4826
+ * left for `remaining`.
4827
+ */
4828
+ tod(i) {
4829
+ const v = this.fields[i] ?? "";
4830
+ if (/^(?:[01]\d|2[0-3])[0-5]\d(?:[0-5]\d)?$/.test(v)) {
4831
+ this.used.add(i);
4832
+ return DateTimeUtils.convertHHMMSSToTod(v);
4833
+ }
4834
+ if (!/^\d+$/.test(v)) this.used.add(i);
4835
+ return void 0;
4836
+ }
4837
+ /** Writes a time through a ResultFormatter helper such as `out`, `in` or `timestamp`. */
4838
+ time(i, write) {
4839
+ const t = this.tod(i);
4840
+ if (t !== void 0) write(this.decodeResult, t);
4841
+ }
4842
+ /** A time with no ResultFormatter helper, written as HH:MM:SS. */
4843
+ timeItem(i, key, code, label) {
4844
+ const t = this.tod(i);
4845
+ if (t !== void 0) {
4846
+ this.item(i, key, code, label, DateTimeUtils.timestampToString(t));
4847
+ }
4848
+ }
4849
+ eta(i) {
4850
+ this.time(i, (r, t) => ResultFormatter.eta(r, t));
4851
+ }
4852
+ /** Fields not written above, non-empty, to `remaining`. */
4853
+ finish() {
4854
+ const rest = this.fields.filter(
4855
+ (v, i) => !this.used.has(i) && v.trim() !== ""
4856
+ );
4857
+ if (rest.length > 0) ResultFormatter.unknownArr(this.decodeResult, rest);
4858
+ return rest.length;
4859
+ }
4860
+ };
4861
+
4862
+ // lib/plugins/Label_37_SWA.ts
4863
+ var DESCRIPTIONS = {
4864
+ ADV01: "Ride Conditions Report",
4865
+ CLB02: "Climb Report",
4866
+ DVC01: "Divert Plan (Crew Copy)",
4867
+ ETA03: "ETA Report",
4868
+ FCM01: "Engine and Fuel State",
4869
+ FCR03: "Runway Condition Report",
4870
+ FHD04: "Frequency Handoff Report",
4871
+ FLI01: "Flight Initialization",
4872
+ FTD02: "Free Text to Dispatch",
4873
+ GAM02: "Gate Assignment Request",
4874
+ HGD03: "HowGozit Report",
4875
+ HLD01: "Holding Report",
4876
+ INN03: "In Report",
4877
+ INR04: "In Range Report",
4878
+ MXR03: "Maintenance Report",
4879
+ OFF02: "Off Report",
4880
+ ONN02: "On Report",
4881
+ OUT03: "Out Report",
4882
+ PFR03: "Post Flight Report",
4883
+ POS01: "Position Report",
4884
+ POS02: "Position Report",
4885
+ RRR01: "Reroute Report",
4886
+ RTG04: "Return to Gate Report",
4887
+ RTG05: "Return to Gate Report",
4888
+ TDM01: "Door Report",
4889
+ TRB02: "Turbulence Report",
4890
+ TXI02: "Taxi Report",
4891
+ VXR02: "Voice Call Request"
4892
+ };
4893
+ var DOORS = {
4894
+ L1: "L1 (forward entry)",
4895
+ L2: "L2 (aft entry)",
4896
+ CR: "Cargo",
4897
+ SV: "Service"
4898
+ };
4899
+ var MXR_CODES = {
4900
+ "0": "Nothing to report",
4901
+ "1": "Status option",
4902
+ "2": "Category and text",
4903
+ "3": "Free text"
4904
+ };
4905
+ var Label_37_SWA = class extends DecoderPlugin {
4906
+ name = "label-37-swa";
4907
+ qualifiers() {
4908
+ return {
4909
+ labels: ["37"],
4910
+ preambles: ["01", "02", "03", "04", "05", "06", "07", "08", "09"]
4911
+ };
4912
+ }
4913
+ decode(message, options = {}) {
4914
+ const decodeResult = this.defaultResult();
4915
+ decodeResult.decoder.name = this.name;
4916
+ decodeResult.message = message;
4917
+ const d = decodeSwaLabel37(message.text);
4918
+ if (!d || !isSwaType(d.type, "37")) {
4919
+ if (options.debug) {
4920
+ console.log(`Decoder: Unknown 37 message: ${message.text}`);
4921
+ }
4922
+ ResultFormatter.unknown(decodeResult, message.text);
4923
+ decodeResult.formatted.description = "Unknown";
4924
+ return decodeResult;
4925
+ }
4926
+ decodeResult.decoded = true;
4927
+ decodeResult.formatted.description = DESCRIPTIONS[d.type] ?? "Southwest Message";
4928
+ decodeResult.raw.preamble = d.type;
4929
+ const text = d.text;
4930
+ const fields = swaFields(text);
4931
+ const layouts = SWA_LAYOUTS[d.type];
4932
+ const prefix = SWA_PREFIX_LAYOUTS[d.type];
4933
+ const clean = !text.includes(SWA_UNREAD);
4934
+ if (clean && layouts && swaMatchesLayout(fields, layouts)) {
4935
+ const rest = this.writeFields(decodeResult, d.type, fields);
4936
+ decodeResult.decoder.decodeLevel = rest === 0 ? "full" : "partial";
4937
+ return decodeResult;
4938
+ }
4939
+ if (clean && prefix && swaMatchesLayout(fields.slice(0, prefix.length), [prefix])) {
4940
+ const rest = this.writeMaintenance(decodeResult, text, fields);
4941
+ decodeResult.decoder.decodeLevel = rest === 0 ? "full" : "partial";
4942
+ return decodeResult;
4943
+ }
4944
+ if (clean && !layouts && !prefix) {
4945
+ const lines = text.split("|");
4946
+ const route = lines[0].match(/^([A-Z]{4}),([A-Z]{4})$/);
4947
+ if (route && d.type === "FTD02") {
4948
+ ResultFormatter.departureAirport(decodeResult, route[1]);
4949
+ ResultFormatter.arrivalAirport(decodeResult, route[2]);
4950
+ lines.shift();
4951
+ }
4952
+ ResultFormatter.text(decodeResult, lines.join("\n"));
4953
+ decodeResult.decoder.decodeLevel = "full";
4954
+ return decodeResult;
4955
+ }
4956
+ ResultFormatter.text(decodeResult, text.split("|").join("\n"));
4957
+ decodeResult.decoder.decodeLevel = "partial";
4958
+ return decodeResult;
4959
+ }
4960
+ /** Writes a laid-out type's fields; returns how many went to `remaining`. */
4961
+ writeFields(decodeResult, type, fields) {
4962
+ const w = new SwaFieldWriter(decodeResult, fields);
4963
+ switch (type) {
4964
+ case "POS01":
4965
+ case "POS02":
4966
+ case "HGD03":
4967
+ case "FHD04": {
4968
+ w.departure(0);
4969
+ w.arrival(1);
4970
+ w.position(2, 3);
4971
+ w.altitude(4);
4972
+ const ias = w.int(5);
4973
+ if (ias !== void 0)
4974
+ ResultFormatter.indicatedAirspeed(decodeResult, ias);
4975
+ const tas = w.int(6);
4976
+ if (tas !== void 0) ResultFormatter.airspeed(decodeResult, tas);
4977
+ ResultFormatter.mach(decodeResult, Number(w.use(7)));
4978
+ w.fuel(8);
4979
+ w.eta(9);
4980
+ const track = w.int(10);
4981
+ if (track !== void 0) ResultFormatter.track(decodeResult, track);
4982
+ ResultFormatter.wind(decodeResult, w.int(11), w.int(12));
4983
+ const sat = w.int(13);
4984
+ if (sat !== void 0)
4985
+ ResultFormatter.temperature(decodeResult, `${sat}`);
4986
+ const gwIndex = type === "FHD04" ? 16 : 14;
4987
+ const gw = w.int(gwIndex);
4988
+ if (gw !== void 0) ResultFormatter.grossWeight(decodeResult, gw);
4989
+ if (type !== "FHD04" && fields.length === 16) {
4990
+ w.time(15, (r, t) => ResultFormatter.timestamp(r, t));
4991
+ }
4992
+ if (type === "FHD04") {
4993
+ w.item(14, "event", "EVT", "Event", fields[14]);
4994
+ w.use(15);
4995
+ const freq = w.use(19);
4996
+ if (/^1\d{5}$/.test(freq)) {
4997
+ w.item(
4998
+ 19,
4999
+ "frequency",
5000
+ "FREQ",
5001
+ "ACARS Frequency",
5002
+ `${freq.substring(0, 3)}.${freq.substring(3)} MHz`
5003
+ );
5004
+ }
5005
+ }
5006
+ break;
5007
+ }
5008
+ case "GAM02":
5009
+ w.departure(0);
5010
+ w.arrival(1);
5011
+ w.eta(2);
5012
+ w.position(3, 4);
5013
+ break;
5014
+ case "CLB02":
5015
+ w.departure(0);
5016
+ w.arrival(1);
5017
+ w.position(2, 4);
5018
+ w.fuel(5);
5019
+ w.eta(6);
5020
+ break;
5021
+ case "ETA03":
5022
+ w.departure(0);
5023
+ w.arrival(1);
5024
+ w.eta(2);
5025
+ w.fuel(3);
5026
+ w.position(4, 5);
5027
+ break;
5028
+ case "INR04":
5029
+ w.departure(0);
5030
+ w.arrival(1);
5031
+ w.eta(2);
5032
+ w.time(3, (r, t) => ResultFormatter.timestamp(r, t));
5033
+ w.fuel(4);
5034
+ w.position(5, 6);
5035
+ break;
5036
+ case "FLI01":
5037
+ w.departure(0);
5038
+ w.arrival(1);
5039
+ w.time(2, (r, t) => ResultFormatter.timestamp(r, t));
5040
+ w.fuel(3);
5041
+ w.position(4, 5);
5042
+ break;
5043
+ case "OUT03":
5044
+ w.departure(0);
5045
+ w.arrival(1);
5046
+ w.time(2, (r, t) => ResultFormatter.out(r, t));
5047
+ w.fuel(3);
5048
+ w.timeItem(5, "front_door_closed_time", "L1CLS", "Front Door Closed");
5049
+ break;
5050
+ case "TXI02":
5051
+ w.departure(0);
5052
+ w.arrival(1);
5053
+ w.timeItem(2, "taxi_start_time", "TAXI", "Taxi Start Time");
5054
+ w.fuel(3);
5055
+ break;
5056
+ case "INN03":
5057
+ w.departure(0);
5058
+ w.arrival(1);
5059
+ w.time(2, (r, t) => ResultFormatter.in(r, t));
5060
+ w.timeItem(3, "front_door_open_time", "L1OPN", "Front Door Opened");
5061
+ w.fuel(4);
5062
+ break;
5063
+ case "RTG04":
5064
+ case "RTG05":
5065
+ w.departure(0);
5066
+ w.arrival(1);
5067
+ w.time(2, (r, t) => ResultFormatter.out(r, t));
5068
+ w.time(3, (r, t) => ResultFormatter.in(r, t));
5069
+ w.time(4, (r, t) => ResultFormatter.timestamp(r, t));
5070
+ w.fuel(5);
5071
+ break;
5072
+ case "TDM01": {
5073
+ w.departure(0);
5074
+ w.arrival(1);
5075
+ w.position(2, 3);
5076
+ ResultFormatter.door_event(
5077
+ decodeResult,
5078
+ DOORS[w.use(4)],
5079
+ w.use(5) === "O" ? "open" : "closed"
5080
+ );
5081
+ const md = fields[6];
5082
+ if (/^(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])$/.test(md)) {
5083
+ w.use(6);
5084
+ ResultFormatter.month(decodeResult, Number(md.substring(0, 2)));
5085
+ ResultFormatter.day(decodeResult, Number(md.substring(2)));
5086
+ } else if (!/^\d+$/.test(md)) {
5087
+ w.use(6);
5088
+ }
5089
+ w.time(7, (r, t) => ResultFormatter.timestamp(r, t));
5090
+ break;
5091
+ }
5092
+ case "FCM01": {
5093
+ const names = {
5094
+ L: "Left Engine",
5095
+ R: "Right Engine",
5096
+ A: "APU"
5097
+ };
5098
+ for (let i = 0; i < 3; i++) {
5099
+ const v = fields[i];
5100
+ if (v[1] === "0" || v[1] === "1") {
5101
+ w.item(
5102
+ i,
5103
+ `${names[v[0]].toLowerCase().replace(" ", "_")}`,
5104
+ v[0],
5105
+ names[v[0]],
5106
+ v[1] === "1" ? "running" : "off"
5107
+ );
5108
+ }
5109
+ }
5110
+ ResultFormatter.currentFuel(decodeResult, Number(w.use(4)) * 100);
5111
+ w.time(5, (r, t) => ResultFormatter.timestamp(r, t));
5112
+ break;
5113
+ }
5114
+ case "VXR02":
5115
+ w.item(0, "station_iata", "STN", "Station", fields[0]);
5116
+ break;
5117
+ }
5118
+ return w.finish();
5119
+ }
5120
+ /** MXR03: destination, ETA, code, then the category and text; returns how many header
5121
+ * fields went to `remaining`. */
5122
+ writeMaintenance(decodeResult, text, fields) {
5123
+ const w = new SwaFieldWriter(decodeResult, fields.slice(0, 3));
5124
+ w.arrival(0);
5125
+ w.eta(1);
5126
+ w.item(
5127
+ 2,
5128
+ "maintenance_code",
5129
+ "MXCODE",
5130
+ "Report Code",
5131
+ `${fields[2]} (${MXR_CODES[fields[2]]})`
5132
+ );
5133
+ const header = text.match(/^[^,|]*[,|][^,|]*[,|][^,|]*[,|]?/);
5134
+ const body = text.substring(header ? header[0].length : 0);
5135
+ if (body.trim() !== "") {
5136
+ ResultFormatter.text(decodeResult, body.split("|").join("\n"));
5137
+ }
5138
+ return w.finish();
5139
+ }
5140
+ };
5141
+
5142
+ // lib/plugins/Label_40_SWA.ts
5143
+ var DESCRIPTIONS2 = {
5144
+ ASR01: "Approach Stability Report",
5145
+ DVP01: "Divert Plan",
5146
+ FTX01: "Dispatch Free Text",
5147
+ GAR03: "Gate Assignment Response",
5148
+ GEN01: "Company Message",
5149
+ PIC01: "Part 117 Duty Extension Request",
5150
+ POS01: "Position Report Request",
5151
+ WFC01: "Field Condition Report"
5152
+ };
5153
+ var ASR01 = [
5154
+ [
5155
+ "reverser_distance",
5156
+ "REVDIST",
5157
+ "Threshold to Thrust Reverser Deployment (feet)"
5158
+ ],
5159
+ ["max_ias", "MAXIAS", "Highest IAS, 1000 feet to Touchdown (knots)"],
5160
+ ["min_ias", "MINIAS", "Lowest IAS, to Touchdown (knots)"],
5161
+ ["touchdown_distance", "TDDIST", "Threshold to Touchdown (feet)"],
5162
+ ["glideslope_above", "GSHIGH", "Largest Deviation Above Glideslope (dots)"],
5163
+ ["glideslope_below", "GSLOW", "Largest Deviation Below Glideslope (dots)"],
5164
+ ["max_pitch", "MAXPITCH", "Highest Pitch, Flare and Landing (degrees)"],
5165
+ ["tailstrike_pitch", "TSPITCH", "Tail Strike Pitch Limit (degrees)"]
5166
+ ];
5167
+ var Label_40_SWA = class extends DecoderPlugin {
5168
+ name = "label-40-swa";
5169
+ qualifiers() {
5170
+ return {
5171
+ labels: ["40"]
5172
+ };
5173
+ }
5174
+ decode(message, options = {}) {
5175
+ const decodeResult = this.defaultResult();
5176
+ decodeResult.decoder.name = this.name;
5177
+ decodeResult.message = message;
5178
+ const d = decodeSwaLabel40(message.text);
5179
+ if (!d || !isSwaType(d.type, "40")) {
5180
+ if (options.debug) {
5181
+ console.log(`Decoder: Unknown 40 message: ${message.text}`);
5182
+ }
5183
+ ResultFormatter.unknown(decodeResult, message.text);
5184
+ decodeResult.formatted.description = "Unknown";
5185
+ return decodeResult;
5186
+ }
5187
+ decodeResult.decoded = true;
5188
+ decodeResult.formatted.description = DESCRIPTIONS2[d.type] ?? "Southwest Message";
5189
+ decodeResult.raw.preamble = d.type;
5190
+ if (d.type === "POS01") {
5191
+ const m = d.text.match(/^POS01,Y,(\d{2})$/);
5192
+ if (m) {
5193
+ ResultFormatter.raw(
5194
+ decodeResult,
5195
+ "report_interval",
5196
+ "RPTINT",
5197
+ "Position Report Interval",
5198
+ `${Number(m[1])} minutes`,
5199
+ Number(m[1])
5200
+ );
5201
+ decodeResult.decoder.decodeLevel = "full";
5202
+ } else {
5203
+ ResultFormatter.unknown(decodeResult, d.text);
5204
+ decodeResult.decoder.decodeLevel = "partial";
5205
+ }
5206
+ return decodeResult;
5207
+ }
5208
+ const text = d.text;
5209
+ const fields = swaFields(text);
5210
+ const layouts = SWA_LAYOUTS[d.type];
5211
+ const clean = !text.includes(SWA_UNREAD);
5212
+ if (clean && layouts && swaMatchesLayout(fields, layouts)) {
5213
+ const w = new SwaFieldWriter(decodeResult, fields);
5214
+ if (d.type === "ASR01") {
5215
+ ASR01.forEach(([key, code, label], i) => {
5216
+ const v = fields[i].trim();
5217
+ if (v !== "XX.X") w.item(i, key, code, label, v);
5218
+ else w.use(i);
5219
+ });
5220
+ } else if (d.type === "GAR03") {
5221
+ ResultFormatter.arrivalGate(decodeResult, w.use(0).trim());
5222
+ if (fields[6] !== "999.999") {
5223
+ w.item(
5224
+ 6,
5225
+ "ramp_frequency",
5226
+ "RAMPFREQ",
5227
+ "Ramp Frequency",
5228
+ `${fields[6]} MHz`
5229
+ );
5230
+ } else {
5231
+ w.use(6);
5232
+ }
5233
+ }
5234
+ const rest = w.finish();
5235
+ decodeResult.decoder.decodeLevel = rest === 0 ? "full" : "partial";
5236
+ return decodeResult;
5237
+ }
5238
+ ResultFormatter.text(decodeResult, text.split("|").join("\n"));
5239
+ decodeResult.decoder.decodeLevel = clean && !layouts ? "full" : "partial";
5240
+ return decodeResult;
5241
+ }
5242
+ };
5243
+
3797
5244
  // lib/plugins/Label_44_ETA.ts
3798
5245
  var Label_44_ETA = class extends DecoderPlugin {
3799
5246
  name = "label-44-eta";
@@ -4832,7 +6279,7 @@ var Label_H1_76401 = class extends DecoderPlugin {
4832
6279
  );
4833
6280
  if (position) {
4834
6281
  waypoints.push({
4835
- name: CoordinateUtils.coordinateString(position),
6282
+ name: line.substring(0, 13),
4836
6283
  latitude: position.latitude,
4837
6284
  longitude: position.longitude,
4838
6285
  time: DateTimeUtils.convertHHMMSSToTod(line.substring(13, 19))
@@ -5079,11 +6526,11 @@ var Label_H1_M_POS = class extends DecoderPlugin {
5079
6526
  const timestamp = fields[2].trim();
5080
6527
  if (timestamp.length === 6) {
5081
6528
  const day = Number(timestamp.substring(0, 2));
5082
- const tod = DateTimeUtils.convertHHMMSSToTod(
6529
+ const tod2 = DateTimeUtils.convertHHMMSSToTod(
5083
6530
  timestamp.substring(2) + "00"
5084
6531
  );
5085
6532
  ResultFormatter.day(decodeResult, day);
5086
- ResultFormatter.timestamp(decodeResult, tod);
6533
+ ResultFormatter.timestamp(decodeResult, tod2);
5087
6534
  }
5088
6535
  const lat = parseFloat(fields[3].replace(/\s/g, ""));
5089
6536
  const lon = parseFloat(fields[4].replace(/\s/g, ""));
@@ -5477,6 +6924,135 @@ var Label_H1_StarPOS = class extends DecoderPlugin {
5477
6924
  }
5478
6925
  };
5479
6926
 
6927
+ // lib/plugins/Label_H1_SWA_Track.ts
6928
+ var PHASES = {
6929
+ TO: "Takeoff",
6930
+ IC: "Initial Climb",
6931
+ CL: "Climb",
6932
+ CR: "Cruise",
6933
+ ER: "En Route",
6934
+ DC: "Descent",
6935
+ AP: "Approach",
6936
+ RO: "Rollout"
6937
+ };
6938
+ var POINT = /^([NS]\d{4})\.(\d),([EW]\d{5})\.(\d),(\d{2})(\d{2})(\d{2}),\s?(-?\d+),\s?(-?\d+\.\d),(\d{3}),(\d{3}),([A-Z]{2}),(\d{5}),([01]),$/;
6939
+ var Label_H1_SWA_Track = class extends DecoderPlugin {
6940
+ name = "label-h1-swa-track";
6941
+ qualifiers() {
6942
+ return {
6943
+ labels: ["H1"],
6944
+ preambles: ["++76502,", "++86501,", "#DFB++76502,", "#DFB++86501,"]
6945
+ };
6946
+ }
6947
+ decode(message, options = {}) {
6948
+ const decodeResult = this.initResult(message, "Track Report");
6949
+ const nl = message.text.includes("\r\n") ? "\r\n" : "\n";
6950
+ const lines = message.text.replace(/#DFB/g, "").split(/\r?\n/);
6951
+ const header = lines[0].replace(/^\+\+/, "").split(",");
6952
+ if (header.length !== 9 || !/^\d{6}$/.test(header[3])) {
6953
+ return this.failUnknown(decodeResult, message.text, options);
6954
+ }
6955
+ const unknown = [];
6956
+ if (/^N\w+$/.test(header[1])) {
6957
+ ResultFormatter.tail(decodeResult, header[1]);
6958
+ } else if (/^\d{1,4}$/.test(header[1])) {
6959
+ ResultFormatter.raw(
6960
+ decodeResult,
6961
+ "ship_number",
6962
+ "SHIP",
6963
+ "Ship Number",
6964
+ header[1]
6965
+ );
6966
+ } else if (header[1] !== "XXX") {
6967
+ unknown.push(header[1]);
6968
+ }
6969
+ ResultFormatter.raw(
6970
+ decodeResult,
6971
+ "aircraft_type",
6972
+ "ACTYPE",
6973
+ "Aircraft Type",
6974
+ header[2]
6975
+ );
6976
+ const d = header[3];
6977
+ ResultFormatter.raw(
6978
+ decodeResult,
6979
+ "report_date",
6980
+ "DATE",
6981
+ "Report Date",
6982
+ `20${d.slice(0, 2)}-${d.slice(2, 4)}-${d.slice(4, 6)}`
6983
+ );
6984
+ ResultFormatter.flightNumber(decodeResult, header[4]);
6985
+ ResultFormatter.departureAirport(decodeResult, header[5]);
6986
+ ResultFormatter.arrivalAirport(decodeResult, header[6]);
6987
+ if (/^\d{1,15}$/.test(header[7])) {
6988
+ ResultFormatter.raw(
6989
+ decodeResult,
6990
+ "leg_counter",
6991
+ "LEGS",
6992
+ "Leg Counter",
6993
+ `${Number(header[7])}`,
6994
+ Number(header[7])
6995
+ );
6996
+ } else {
6997
+ unknown.push(header[7]);
6998
+ }
6999
+ ResultFormatter.raw(
7000
+ decodeResult,
7001
+ "software_version",
7002
+ "SWVER",
7003
+ "Software Version",
7004
+ header[8]
7005
+ );
7006
+ if (unknown.length > 0) ResultFormatter.unknownArr(decodeResult, unknown);
7007
+ const count = lines[1]?.match(/^(\d+),?$/)?.[1];
7008
+ if (lines[1] !== void 0 && count === void 0) {
7009
+ ResultFormatter.unknown(decodeResult, lines[1], nl);
7010
+ }
7011
+ for (const line of lines.slice(2)) {
7012
+ if (line === "" || line === ":") continue;
7013
+ const m = line.match(POINT);
7014
+ if (!m || !(Number(m[5]) >= 1 && Number(m[5]) <= 31) || Number(m[6]) > 23 || Number(m[7]) > 59 || Number(m[10]) > 360 || Number(m[1].slice(3)) > 59 || Number(m[3].slice(4)) > 59) {
7015
+ ResultFormatter.unknown(decodeResult, line, nl);
7016
+ continue;
7017
+ }
7018
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(
7019
+ `${m[1]}${m[2]}${m[3]}${m[4]}`
7020
+ );
7021
+ if (!position || Math.abs(position.latitude) > 90 || Math.abs(position.longitude) > 180) {
7022
+ ResultFormatter.unknown(decodeResult, line, nl);
7023
+ continue;
7024
+ }
7025
+ ResultFormatter.trackPoint(decodeResult, {
7026
+ name: `${m[1]}.${m[2]}${m[3]}.${m[4]}`,
7027
+ ...position,
7028
+ day: Number(m[5]),
7029
+ time: Number(m[6]) * 3600 + Number(m[7]) * 60,
7030
+ altitude: Number(m[8]),
7031
+ // -00.0 is sent; keep it as 0
7032
+ temperature: Number(m[9]) || 0,
7033
+ windDirection: Number(m[10]),
7034
+ windSpeed: Number(m[11]),
7035
+ phase: PHASES[m[12]] ?? m[12],
7036
+ humiditySensor: m[14] === "1",
7037
+ ...m[14] === "1" && Number(m[13]) > 0 ? { waterVapor: Number(m[13]) } : {}
7038
+ });
7039
+ }
7040
+ const points = decodeResult.raw.route?.waypoints ?? [];
7041
+ const last = points[points.length - 1];
7042
+ if (last) {
7043
+ ResultFormatter.position(decodeResult, {
7044
+ latitude: last.latitude,
7045
+ longitude: last.longitude
7046
+ });
7047
+ ResultFormatter.altitude(decodeResult, last.altitude);
7048
+ ResultFormatter.timestamp(decodeResult, last.time);
7049
+ }
7050
+ const counted = count !== void 0 && Number(count) === points.length;
7051
+ this.setDecodeLevel(decodeResult, true, counted ? void 0 : "partial");
7052
+ return decodeResult;
7053
+ }
7054
+ };
7055
+
5480
7056
  // lib/plugins/Label_H1_WRN.ts
5481
7057
  var Label_H1_WRN = class extends DecoderPlugin {
5482
7058
  name = "label-h1-wrn";
@@ -5575,7 +7151,7 @@ var Label_H2_02E = class extends DecoderPlugin {
5575
7151
  if (text.length !== 32 || !pos) {
5576
7152
  return null;
5577
7153
  }
5578
- const tod = DateTimeUtils.convertHHMMSSToTod(text.substring(13, 17));
7154
+ const tod2 = DateTimeUtils.convertHHMMSSToTod(text.substring(13, 17));
5579
7155
  const flightLevel = parseInt(text.substring(17, 20), 10);
5580
7156
  const tempSign = text[21] === "M" ? -1 : 1;
5581
7157
  const tempDegreesRaw = parseInt(text.substring(22, 25), 10);
@@ -5590,7 +7166,7 @@ var Label_H2_02E = class extends DecoderPlugin {
5590
7166
  name: posString,
5591
7167
  latitude: pos.latitude,
5592
7168
  longitude: pos.longitude,
5593
- time: tod
7169
+ time: tod2
5594
7170
  },
5595
7171
  flightLevel,
5596
7172
  windDirection,
@@ -6898,8 +8474,11 @@ var pluginClasses = [
6898
8474
  Label_ColonComma,
6899
8475
  Label_5Z_Slash,
6900
8476
  Label_10_LDR,
8477
+ Label_10_LRW,
6901
8478
  Label_10_POS,
6902
8479
  Label_10_Slash,
8480
+ Label_11_LDR,
8481
+ Label_11_LRW,
6903
8482
  Label_12_N_Space,
6904
8483
  Label_12_POS,
6905
8484
  Label_13Through18_Slash,
@@ -6923,6 +8502,8 @@ var pluginClasses = [
6923
8502
  Label_2P_FM4,
6924
8503
  Label_2P_FM5,
6925
8504
  Label_30_Slash_EA,
8505
+ Label_37_SWA,
8506
+ Label_40_SWA,
6926
8507
  Label_44_ETA,
6927
8508
  Label_44_IN,
6928
8509
  Label_44_OFF,
@@ -6948,6 +8529,7 @@ var pluginClasses = [
6948
8529
  Label_H1_Paren,
6949
8530
  Label_H1_WRN,
6950
8531
  Label_H1_StarPOS,
8532
+ Label_H1_SWA_Track,
6951
8533
  Label_H2_02E,
6952
8534
  Label_HX,
6953
8535
  Label_58,