@airframes/acars-decoder 1.6.13 → 1.6.14

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
@@ -2113,775 +2113,807 @@ var Label_4A_Slash_01 = class extends DecoderPlugin {
2113
2113
  }
2114
2114
  };
2115
2115
 
2116
- // lib/plugins/Label_4N.ts
2117
- var Label_4N = class extends DecoderPlugin {
2118
- name = "label-4n";
2119
- qualifiers() {
2120
- return {
2121
- labels: ["4N"]
2122
- };
2123
- }
2124
- decode(message, options = {}) {
2125
- const decodeResult = this.defaultResult();
2126
- decodeResult.decoder.name = this.name;
2127
- decodeResult.message = message;
2128
- decodeResult.formatted.description = "Airline Defined";
2129
- let text = message.text;
2130
- if (text.match(/^M\d{2}A\w{6}/)) {
2131
- ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
2132
- text = text.substring(10);
2133
- }
2134
- decodeResult.decoded = true;
2135
- const fields = text.split(",");
2136
- if (text.length === 51) {
2137
- decodeResult.raw.day_of_month = text.substring(0, 2);
2138
- ResultFormatter.departureAirport(decodeResult, text.substring(8, 11));
2139
- ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16));
2140
- ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(30, 45).replace(/^(.)0/, "$1")));
2141
- ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100);
2142
- ResultFormatter.unknownArr(decodeResult, [text.substring(2, 4), text.substring(19, 29)], " ");
2143
- } else if (fields.length === 33) {
2144
- decodeResult.raw.date = fields[3];
2145
- if (fields[1] === "B") {
2146
- ResultFormatter.position(decodeResult, { latitude: Number(fields[4]), longitude: Number(fields[5]) });
2147
- ResultFormatter.altitude(decodeResult, Number(fields[6]));
2148
- }
2149
- ResultFormatter.departureAirport(decodeResult, fields[8]);
2150
- ResultFormatter.arrivalAirport(decodeResult, fields[9]);
2151
- ResultFormatter.alternateAirport(decodeResult, fields[10]);
2152
- ResultFormatter.arrivalRunway(decodeResult, fields[11].split("/")[0]);
2153
- if (fields[12].length > 1) {
2154
- ResultFormatter.alternateRunway(decodeResult, fields[12].split("/")[0]);
2116
+ // lib/utils/flight_plan_utils.ts
2117
+ var FlightPlanUtils = class _FlightPlanUtils {
2118
+ /**
2119
+ * Processes flight plan data
2120
+ *
2121
+ * Expected format is [header, key1, val1, ... keyN, valN]
2122
+ *
2123
+ * @param decodeResult - results
2124
+ * @param data - original message split by ':'
2125
+ * @returns whether all fields were processed or not
2126
+ */
2127
+ static processFlightPlan(decodeResult, data) {
2128
+ let allKnownFields = _FlightPlanUtils.parseHeader(decodeResult, data[0]);
2129
+ for (let i = 1; i < data.length; i += 2) {
2130
+ const key = data[i];
2131
+ const value = data[i + 1];
2132
+ switch (key) {
2133
+ case "A":
2134
+ addProcedure(decodeResult, value, "arrival");
2135
+ break;
2136
+ case "AA":
2137
+ addArrivalAirport(decodeResult, value);
2138
+ break;
2139
+ case "AP":
2140
+ addProcedure(decodeResult, value, "approach");
2141
+ break;
2142
+ case "CR":
2143
+ addCompanyRoute(decodeResult, value);
2144
+ break;
2145
+ case "D":
2146
+ addProcedure(decodeResult, value, "departure");
2147
+ break;
2148
+ case "DA":
2149
+ addDepartureAirport(decodeResult, value);
2150
+ break;
2151
+ case "F":
2152
+ addRoute(decodeResult, value);
2153
+ break;
2154
+ case "R":
2155
+ addRunway(decodeResult, value);
2156
+ break;
2157
+ // case 'WS': // something about routes, has altitude, so current parsing won't work
2158
+ // break;
2159
+ default:
2160
+ if (allKnownFields) {
2161
+ decodeResult.remaining.text = "";
2162
+ allKnownFields = false;
2163
+ }
2164
+ decodeResult.remaining.text += `:${key}:${value}`;
2165
+ decodeResult.decoder.decodeLevel = "partial";
2155
2166
  }
2156
- ResultFormatter.checksum(decodeResult, fields[32]);
2157
- ResultFormatter.unknownArr(decodeResult, [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter((f) => f != ""));
2158
- } else {
2159
- decodeResult.decoded = false;
2160
- ResultFormatter.unknown(decodeResult, text);
2161
2167
  }
2162
- if (decodeResult.decoded) {
2163
- if (!decodeResult.remaining.text)
2164
- decodeResult.decoder.decodeLevel = "full";
2165
- else
2166
- decodeResult.decoder.decodeLevel = "partial";
2168
+ return allKnownFields;
2169
+ }
2170
+ static parseHeader(decodeResult, header) {
2171
+ let allKnownFields = true;
2172
+ if (header.startsWith("RF")) {
2173
+ decodeResult.formatted.items.push({
2174
+ type: "status",
2175
+ code: "ROUTE_STATUS",
2176
+ label: "Route Status",
2177
+ value: "Route Filed"
2178
+ });
2179
+ decodeResult.raw.route_status = "RF";
2180
+ if (header.length > 2) {
2181
+ addRoute(decodeResult, header.substring(2));
2182
+ }
2183
+ } else if (header.startsWith("RP")) {
2184
+ decodeResult.raw.route_status = "RP";
2185
+ decodeResult.formatted.items.push({
2186
+ type: "status",
2187
+ code: "ROUTE_STATUS",
2188
+ label: "Route Status",
2189
+ value: "Route Planned"
2190
+ });
2191
+ decodeResult.raw.route_status = header;
2192
+ } else if (header.startsWith("RI")) {
2193
+ decodeResult.raw.route_status = "RI";
2194
+ decodeResult.formatted.items.push({
2195
+ type: "status",
2196
+ code: "ROUTE_STATUS",
2197
+ label: "Route Status",
2198
+ value: "Route Inactive"
2199
+ });
2167
2200
  } else {
2168
- decodeResult.decoder.decodeLevel = "none";
2201
+ decodeResult.remaining.text += header;
2202
+ allKnownFields = false;
2169
2203
  }
2170
- return decodeResult;
2204
+ return allKnownFields;
2171
2205
  }
2172
2206
  };
2173
-
2174
- // lib/plugins/Label_80.ts
2175
- var Label_80 = class extends DecoderPlugin {
2176
- name = "label-80";
2177
- descriptions = {
2178
- ALT: "Altitude",
2179
- DWND: "Wind Direction",
2180
- ETA: "Estimated Time of Arrival",
2181
- FOB: "Fuel on Board",
2182
- FL: "Flight Level",
2183
- HDG: "Heading",
2184
- MCH: "Aircraft Speed",
2185
- NWYP: "Next Waypoint",
2186
- POS: "Aircraft Position",
2187
- SAT: "Static Air Temperature",
2188
- SWND: "Wind Speed",
2189
- TAS: "True Airspeed",
2190
- WYP: "Waypoint"
2191
- };
2192
- qualifiers() {
2193
- return {
2194
- labels: ["80"],
2195
- preambles: ["3N01 POSRPT"]
2196
- };
2207
+ function addArrivalAirport(decodeResult, value) {
2208
+ ResultFormatter.arrivalAirport(decodeResult, value);
2209
+ }
2210
+ function addDepartureAirport(decodeResult, value) {
2211
+ ResultFormatter.departureAirport(decodeResult, value);
2212
+ }
2213
+ function addRunway(decodeResult, value) {
2214
+ if (value.length === 8) {
2215
+ ResultFormatter.arrivalRunway(decodeResult, value.substring(4, 7));
2197
2216
  }
2198
- decode(message, options = {}) {
2199
- const decodeResult = this.defaultResult();
2200
- decodeResult.decoder.name = this.name;
2201
- decodeResult.formatted.description = "Airline Defined Position Report";
2202
- const parts = message.text.split("\n");
2203
- let posRptRegex = /^3N01 POSRPT \d\d\d\d\/\d\d (?<orig>\w+)\/(?<dest>\w+) \.(?<tail>[\w-]+)(\/(?<agate>.+) (?<sta>\w+:\w+))*/;
2204
- let results = parts[0].match(posRptRegex);
2205
- if (!results?.groups) {
2206
- decodeResult.decoded = false;
2207
- decodeResult.decoder.decodeLevel = "none";
2208
- return decodeResult;
2209
- }
2210
- if (results && results.length > 0) {
2211
- ResultFormatter.departureAirport(decodeResult, results.groups.orig);
2212
- ResultFormatter.arrivalAirport(decodeResult, results.groups.dest);
2213
- ResultFormatter.tail(decodeResult, results.groups.tail);
2214
- if (results.groups.agate) {
2215
- decodeResult.raw.arrival_gate = results.groups.agate;
2216
- decodeResult.formatted.items.push({
2217
- type: "arrival_gate",
2218
- code: "ARG",
2219
- label: "Arrival Gate",
2220
- value: `${results.groups.agate}`
2221
- });
2222
- decodeResult.raw.scheduled_time_of_arrival = results.groups.sta;
2223
- decodeResult.formatted.items.push({
2224
- type: "scheduled_time_of_arrival",
2225
- code: "STA",
2226
- label: "Scheduled Time of Arrival",
2227
- value: `${results.groups.sta}`
2228
- });
2229
- }
2230
- posRptRegex = /\/(?<field>\w+)\s(?<value>[\w\+\-:\.]+)\s*/gi;
2231
- const remainingParts = parts.slice(1);
2232
- for (const part of remainingParts) {
2233
- const matches = part.matchAll(posRptRegex);
2234
- for (const match of matches) {
2235
- switch (match.groups?.field) {
2236
- case "ALT": {
2237
- ResultFormatter.altitude(decodeResult, Number(match.groups.value));
2238
- break;
2239
- }
2240
- case "DWND": {
2241
- decodeResult.raw.wind_direction = Number(match.groups.value);
2242
- decodeResult.formatted.items.push({
2243
- type: "wind_direction",
2244
- code: "DWND",
2245
- label: this.descriptions[match.groups.field],
2246
- value: decodeResult.raw.wind_direction
2247
- });
2248
- break;
2249
- }
2250
- case "FL": {
2251
- const flight_level = Number(match.groups.value);
2252
- ResultFormatter.altitude(decodeResult, flight_level * 100);
2253
- break;
2254
- }
2255
- case "FOB": {
2256
- const fob = Number(match.groups.value);
2257
- if (!isNaN(fob)) {
2258
- ResultFormatter.currentFuel(decodeResult, fob);
2259
- }
2260
- break;
2261
- }
2262
- case "HDG": {
2263
- ResultFormatter.heading(decodeResult, Number(match.groups.value));
2264
- break;
2265
- }
2266
- case "MCH": {
2267
- decodeResult.raw.mach = Number(match.groups.value) / 1e3;
2268
- decodeResult.formatted.items.push({
2269
- type: "mach",
2270
- code: "MCH",
2271
- label: this.descriptions[match.groups.field],
2272
- value: `${decodeResult.raw.mach} Mach`
2273
- });
2274
- break;
2275
- }
2276
- case "NWYP": {
2277
- decodeResult.raw.next_waypoint = match.groups.value;
2278
- decodeResult.formatted.items.push({
2279
- type: "next_waypoint",
2280
- code: "NWYP",
2281
- label: this.descriptions[match.groups.field],
2282
- value: decodeResult.raw.next_waypoint
2283
- });
2284
- break;
2285
- }
2286
- case "POS": {
2287
- const posRegex = /^(?<latd>[NS])(?<lat>.+)(?<lngd>[EW])(?<lng>.+)/;
2288
- const posResult = match.groups.value.match(posRegex);
2289
- const lat = Number(posResult?.groups?.lat) * (posResult?.groups?.lngd === "S" ? -1 : 1);
2290
- const lon = Number(posResult?.groups?.lng) * (posResult?.groups?.lngd === "W" ? -1 : 1);
2291
- const position = {
2292
- latitude: Number.isInteger(lat) ? lat / 1e3 : lat / 100,
2293
- longitude: Number.isInteger(lon) ? lon / 1e3 : lon / 100
2294
- };
2295
- ResultFormatter.position(decodeResult, position);
2296
- break;
2297
- }
2298
- case "SWND": {
2299
- decodeResult.raw.wind_speed = Number(match.groups.value);
2300
- decodeResult.formatted.items.push({
2301
- type: "wind_speed",
2302
- code: "SWND",
2303
- label: this.descriptions[match.groups.field],
2304
- value: decodeResult.raw.wind_speed
2305
- });
2306
- break;
2307
- }
2308
- default: {
2309
- if (match.groups?.field != void 0) {
2310
- const description = this.descriptions[match.groups.field] ? this.descriptions[match.groups.field] : "Unknown";
2311
- decodeResult.formatted.items.push({
2312
- type: match.groups.field,
2313
- code: match.groups.field,
2314
- label: description || `Unknown (${match.groups.field})`,
2315
- value: `${match.groups.value}`
2316
- });
2317
- }
2318
- }
2319
- }
2320
- }
2321
- }
2322
- decodeResult.decoded = true;
2323
- decodeResult.decoder.decodeLevel = "partial";
2324
- }
2325
- return decodeResult;
2217
+ ResultFormatter.departureRunway(decodeResult, value.substring(0, 3));
2218
+ }
2219
+ function addRoute(decodeResult, value) {
2220
+ const route = value.split(".");
2221
+ ResultFormatter.route(decodeResult, { waypoints: route.map((leg) => RouteUtils.getWaypoint(leg)) });
2222
+ }
2223
+ function addProcedure(decodeResult, value, type) {
2224
+ if (decodeResult.raw.procedures === void 0) {
2225
+ decodeResult.raw.procedures = [];
2326
2226
  }
2327
- };
2328
-
2329
- // lib/plugins/Label_83.ts
2330
- var Label_83 = class extends DecoderPlugin {
2331
- name = "label-83";
2332
- qualifiers() {
2333
- return {
2334
- labels: ["83"]
2335
- };
2227
+ const data = value.split(".");
2228
+ let waypoints;
2229
+ if (data.length > 1) {
2230
+ waypoints = data.slice(1).map((leg) => RouteUtils.getWaypoint(leg));
2336
2231
  }
2337
- decode(message, options = {}) {
2338
- const decodeResult = this.defaultResult();
2339
- decodeResult.decoder.name = this.name;
2340
- decodeResult.message = message;
2341
- decodeResult.formatted.description = "Airline Defined";
2342
- let text = message.text;
2343
- if (text.match(/^M\d{2}A\w{6}/)) {
2344
- ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
2345
- text = text.substring(10);
2346
- }
2347
- decodeResult.decoded = true;
2348
- if (text.substring(0, 10) === "4DH3 ETAT2") {
2349
- const fields = text.split(/\s+/);
2350
- if (fields[2].length > 5) {
2351
- decodeResult.raw.day_of_month = fields[2].substring(5);
2352
- }
2353
- ResultFormatter.unknown(decodeResult, fields[2].substring(0, 4));
2354
- const subfields = fields[3].split("/");
2355
- ResultFormatter.departureAirport(decodeResult, subfields[0]);
2356
- ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
2357
- ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, ""));
2358
- ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + "00"));
2359
- } else if (text.substring(0, 5) === "001PR") {
2360
- decodeResult.raw.day_of_month = text.substring(5, 7);
2361
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(13, 28).replace(/\./g, ""));
2362
- if (position) {
2363
- ResultFormatter.position(decodeResult, position);
2364
- }
2365
- ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33)));
2366
- ResultFormatter.unknown(decodeResult, text.substring(33));
2367
- } else {
2368
- const fields = text.replace(/\s/g, "").split(",");
2369
- if (fields.length === 9) {
2370
- ResultFormatter.departureAirport(decodeResult, fields[0]);
2371
- ResultFormatter.arrivalAirport(decodeResult, fields[1]);
2372
- decodeResult.raw.day_of_month = fields[2].substring(0, 2);
2373
- decodeResult.raw.time = fields[2].substring(2);
2374
- ResultFormatter.position(
2375
- decodeResult,
2376
- {
2377
- latitude: Number(fields[3].replace(/\s/g, "")),
2378
- longitude: Number(fields[4].replace(/\s/g, ""))
2379
- }
2380
- );
2381
- ResultFormatter.altitude(decodeResult, Number(fields[5]));
2382
- ResultFormatter.groundspeed(decodeResult, Number(fields[6]));
2383
- ResultFormatter.heading(decodeResult, Number(fields[7]));
2384
- ResultFormatter.unknown(decodeResult, fields[8]);
2232
+ const route = { name: data[0], waypoints };
2233
+ decodeResult.raw.procedures.push({ type, route });
2234
+ const procedureName = type.substring(0, 1).toUpperCase() + type.slice(1);
2235
+ let procedureValue = route.name;
2236
+ decodeResult.formatted.items.push({
2237
+ type: `procedure`,
2238
+ code: "proc",
2239
+ label: `${procedureName} Procedure`,
2240
+ value: RouteUtils.routeToString(route)
2241
+ });
2242
+ }
2243
+ function addCompanyRoute(decodeResult, value) {
2244
+ const segments = value.split(".");
2245
+ const parens_idx = segments[0].indexOf("(");
2246
+ let name;
2247
+ let runway;
2248
+ if (parens_idx === -1) {
2249
+ name = segments[0];
2250
+ } else {
2251
+ name = segments[0].slice(0, parens_idx);
2252
+ runway = segments[0].slice(parens_idx + 1, segments[0].indexOf(")"));
2253
+ }
2254
+ let waypoints;
2255
+ if (segments.length > 1) {
2256
+ waypoints = segments.slice(1).map((leg) => RouteUtils.getWaypoint(leg));
2257
+ }
2258
+ decodeResult.raw.company_route = {
2259
+ name,
2260
+ runway,
2261
+ waypoints
2262
+ };
2263
+ decodeResult.formatted.items.push({
2264
+ type: "company_route",
2265
+ code: "CR",
2266
+ label: "Company Route",
2267
+ value: RouteUtils.routeToString(decodeResult.raw.company_route)
2268
+ });
2269
+ }
2270
+
2271
+ // lib/utils/h1_helper.ts
2272
+ var H1Helper = class {
2273
+ static decodeH1Message(decodeResult, message) {
2274
+ const checksum = message.slice(-4);
2275
+ const data = message.slice(0, message.length - 4);
2276
+ const fields = data.split("/");
2277
+ parseMessageType(decodeResult, fields[0]);
2278
+ for (let i = 1; i < fields.length; ++i) {
2279
+ if (fields[i].startsWith("FN")) {
2280
+ decodeResult.raw.flight_number = fields[i].substring(2);
2281
+ } else if (fields[i].startsWith("SN")) {
2282
+ decodeResult.raw.serial_number = fields[i].substring(2);
2283
+ } else if (fields[i].startsWith("DC")) {
2284
+ processDC(decodeResult, fields[i].substring(2).split(","));
2285
+ } else if (fields[i].startsWith("TS")) {
2286
+ const ts = fields[i].substring(2).split(",");
2287
+ let time = DateTimeUtils.convertDateTimeToEpoch(ts[0], ts[1]);
2288
+ if (Number.isNaN(time)) {
2289
+ const date = ts[1].substring(2, 4) + ts[1].substring(0, 2) + ts[1].substring(4, 6);
2290
+ time = DateTimeUtils.convertDateTimeToEpoch(ts[0], date);
2291
+ }
2292
+ decodeResult.raw.message_date = ts[1];
2293
+ decodeResult.raw.message_timestamp = time;
2294
+ } else if (fields[i].startsWith("PS")) {
2295
+ const pos = processPS(decodeResult, fields[i].substring(2).split(","));
2296
+ } else if (fields[i].startsWith("DT")) {
2297
+ const data2 = fields[i].substring(2).split(",");
2298
+ processDT(decodeResult, data2);
2299
+ } else if (fields[i].startsWith("ID")) {
2300
+ processIdentification(decodeResult, fields[i].substring(2).split(","));
2301
+ } else if (fields[i].startsWith("LR")) {
2302
+ const data2 = fields[i].substring(2).split(",");
2303
+ processLR(decodeResult, data2);
2304
+ } else if (fields[i].startsWith("RI") || fields[i].startsWith("RF") || fields[i].startsWith("RP")) {
2305
+ FlightPlanUtils.processFlightPlan(decodeResult, fields[i].split(":"));
2306
+ } else if (fields[i].startsWith("PR")) {
2307
+ ResultFormatter.unknown(decodeResult, fields[i], "/");
2308
+ } else if (fields[i].startsWith("AF")) {
2309
+ processAirField(decodeResult, fields[i].substring(2).split(","));
2310
+ } else if (fields[i].startsWith("TD")) {
2311
+ processTimeOfDeparture(decodeResult, fields[i].substring(2).split(","));
2312
+ } else if (fields[i].startsWith("FX")) {
2313
+ ResultFormatter.freetext(decodeResult, fields[i].substring(2));
2314
+ } else if (fields[i].startsWith("ET")) {
2315
+ if (fields[i].length === 7) {
2316
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[i].substring(3) + "00"));
2317
+ } else if (fields[i].length === 8) {
2318
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[i].substring(4) + "00"));
2319
+ } else {
2320
+ ResultFormatter.unknown(decodeResult, fields[i], "/");
2321
+ }
2385
2322
  } else {
2386
- decodeResult.decoded = false;
2387
- ResultFormatter.unknown(decodeResult, message.text);
2323
+ ResultFormatter.unknown(decodeResult, fields[i], "/");
2388
2324
  }
2389
2325
  }
2390
- if (decodeResult.decoded) {
2391
- if (!decodeResult.remaining.text)
2392
- decodeResult.decoder.decodeLevel = "full";
2393
- else
2394
- decodeResult.decoder.decodeLevel = "partial";
2395
- } else {
2396
- decodeResult.decoder.decodeLevel = "none";
2326
+ if (decodeResult.formatted.items.length > 0) {
2327
+ ResultFormatter.checksum(decodeResult, checksum);
2397
2328
  }
2398
- return decodeResult;
2329
+ return true;
2399
2330
  }
2400
2331
  };
2401
-
2402
- // lib/plugins/Label_8E.ts
2403
- var Label_8E = class extends DecoderPlugin {
2404
- name = "label-8e";
2405
- qualifiers() {
2406
- return {
2407
- labels: ["8E"]
2408
- };
2409
- }
2410
- decode(message, options = {}) {
2411
- const decodeResult = this.defaultResult();
2412
- decodeResult.decoder.name = this.name;
2413
- decodeResult.formatted.description = "ETA Report";
2414
- decodeResult.message = message;
2415
- const regex = /^(?<arrival_icao>\w{4}),(?<arrival_eta>\d{4})$/;
2416
- const results = message.text.match(regex);
2417
- if (results?.groups) {
2418
- if (options.debug) {
2419
- console.log(`Label 8E ETA: groups`);
2420
- console.log(results.groups);
2421
- }
2422
- ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results.groups.arrival_eta + "00"));
2423
- ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
2424
- }
2425
- decodeResult.decoded = true;
2426
- decodeResult.decoder.decodeLevel = "full";
2427
- return decodeResult;
2332
+ function processAirField(decodeResult, data) {
2333
+ if (data.length === 2) {
2334
+ ResultFormatter.departureAirport(decodeResult, data[0]);
2335
+ ResultFormatter.arrivalAirport(decodeResult, data[1]);
2336
+ } else {
2337
+ ResultFormatter.unknown(decodeResult, data.join(","), "AF/");
2428
2338
  }
2429
- };
2430
-
2431
- // lib/plugins/Label_B6.ts
2432
- var Label_B6_Forwardslash = class extends DecoderPlugin {
2433
- name = "label-b6-forwardslash";
2434
- qualifiers() {
2435
- return {
2436
- labels: ["B6"],
2437
- preambles: ["/"]
2339
+ }
2340
+ function processTimeOfDeparture(decodeResult, data) {
2341
+ if (data.length === 2) {
2342
+ decodeResult.raw.plannedDepartureTime = data[0];
2343
+ decodeResult.formatted.items.push({
2344
+ type: "ptd",
2345
+ code: "ptd",
2346
+ label: "Planned Departure Time",
2347
+ value: `YYYY-MM-${data[0].substring(0, 2)}T${data[0].substring(2, 4)}:${data[0].substring(4)}:00Z`
2348
+ });
2349
+ decodeResult.raw.plannedDepartureTime = data[1];
2350
+ decodeResult.formatted.items.push({
2351
+ type: "etd",
2352
+ code: "etd",
2353
+ label: "Estimated Departure Time",
2354
+ value: `${data[1].substring(0, 2)}:${data[1].substring(2)}`
2355
+ });
2356
+ } else {
2357
+ ResultFormatter.unknown(decodeResult, data.join(","), "/TD");
2358
+ }
2359
+ }
2360
+ function processIdentification(decodeResult, data) {
2361
+ ResultFormatter.tail(decodeResult, data[0]);
2362
+ if (data.length > 1) {
2363
+ decodeResult.raw.flight_number = data[1];
2364
+ }
2365
+ if (data.length > 2) {
2366
+ decodeResult.raw.mission_number = data[2];
2367
+ }
2368
+ }
2369
+ function processDT(decodeResult, data) {
2370
+ if (!decodeResult.raw.arrival_icao) {
2371
+ ResultFormatter.arrivalAirport(decodeResult, data[0]);
2372
+ } else if (decodeResult.raw.arrival_icao != data[0]) {
2373
+ ResultFormatter.unknownArr(decodeResult, data);
2374
+ }
2375
+ if (data.length > 1) {
2376
+ ResultFormatter.arrivalRunway(decodeResult, data[1]);
2377
+ }
2378
+ if (data.length > 2) {
2379
+ ResultFormatter.currentFuel(decodeResult, Number(data[2]));
2380
+ }
2381
+ if (data.length > 3) {
2382
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[3]));
2383
+ }
2384
+ if (data.length > 4) {
2385
+ ResultFormatter.remainingFuel(decodeResult, Number(data[4]));
2386
+ }
2387
+ if (data.length > 5) {
2388
+ ResultFormatter.unknownArr(decodeResult, data);
2389
+ }
2390
+ }
2391
+ function processLR(decodeResult, data) {
2392
+ if (data.length === 19) {
2393
+ ResultFormatter.unknown(decodeResult, data[1]);
2394
+ ResultFormatter.flightNumber(decodeResult, data[2]);
2395
+ ResultFormatter.departureAirport(decodeResult, data[3]);
2396
+ ResultFormatter.arrivalAirport(decodeResult, data[4]);
2397
+ ResultFormatter.arrivalRunway(decodeResult, data[5]);
2398
+ ResultFormatter.unknown(decodeResult, data.slice(6, 19).join(","));
2399
+ } else {
2400
+ ResultFormatter.unknown(decodeResult, data.join(","));
2401
+ }
2402
+ }
2403
+ function parseMessageType(decodeResult, messageType) {
2404
+ const parts = messageType.split("#");
2405
+ if (parts.length == 1) {
2406
+ const type = parts[0].substring(0, 3);
2407
+ if (type === "POS" && parts[0].length !== 3) {
2408
+ processPosition2(decodeResult, parts[0].substring(3).split(","));
2409
+ }
2410
+ return processMessageType(decodeResult, type);
2411
+ } else if (parts.length == 2) {
2412
+ if (parts[0].length > 0) {
2413
+ ResultFormatter.unknown(decodeResult, parts[0].substring(0, 3));
2414
+ decodeResult.raw.flight_number = parts[0].substring(3);
2415
+ ResultFormatter.unknown(decodeResult, parts[1].length == 5 ? parts[1].substring(0, 2) : parts[1].substring(0, 3), "#");
2416
+ }
2417
+ const type = parts[1].length == 5 ? parts[1].substring(2, 5) : parts[1].substring(3, 6);
2418
+ if (parts[1].substring(3, 6) === "POS" && parts[1].length > 6) {
2419
+ processPosition2(decodeResult, parts[1].substring(6).split(","));
2420
+ }
2421
+ processMessageType(decodeResult, type);
2422
+ } else {
2423
+ ResultFormatter.unknown(decodeResult, messageType);
2424
+ }
2425
+ }
2426
+ function processMessageType(decodeResult, type) {
2427
+ if (type === "FPN") {
2428
+ decodeResult.formatted.description = "Flight Plan";
2429
+ } else if (type === "FTX") {
2430
+ decodeResult.formatted.description = "Free Text";
2431
+ } else if (type === "INI") {
2432
+ decodeResult.formatted.description = "Flight Plan Initial Report";
2433
+ } else if (type === "POS") {
2434
+ decodeResult.formatted.description = "Position Report";
2435
+ } else if (type === "PRG") {
2436
+ decodeResult.formatted.description = "Progress Report";
2437
+ } else {
2438
+ decodeResult.formatted.description = "Unknown H1 Message";
2439
+ }
2440
+ }
2441
+ function processDC(decodeResult, data) {
2442
+ decodeResult.raw.message_date = data[0];
2443
+ if (data.length === 1) {
2444
+ } else if (data.length === 2) {
2445
+ const date = data[0].substring(2, 4) + data[0].substring(0, 2) + data[0].substring(4, 6);
2446
+ const time = DateTimeUtils.convertDateTimeToEpoch(data[1], data[0]);
2447
+ decodeResult.raw.message_timestamp = time;
2448
+ }
2449
+ }
2450
+ function processPS(decodeResult, data) {
2451
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
2452
+ if (position) {
2453
+ decodeResult.raw.position = position;
2454
+ decodeResult.formatted.items.push({
2455
+ type: "aircraft_position",
2456
+ code: "POS",
2457
+ label: "Aircraft Position",
2458
+ value: CoordinateUtils.coordinateString(position)
2459
+ });
2460
+ }
2461
+ if (data.length === 9) {
2462
+ processRoute(decodeResult, data[3], data[1], data[5], data[4], void 0);
2463
+ ResultFormatter.altitude(decodeResult, Number(data[2]) * 100);
2464
+ ResultFormatter.temperature(decodeResult, data[6]);
2465
+ ResultFormatter.unknown(decodeResult, data[7]);
2466
+ ResultFormatter.unknown(decodeResult, data[8]);
2467
+ }
2468
+ if (data.length === 14) {
2469
+ ResultFormatter.altitude(decodeResult, Number(data[3]) * 100);
2470
+ processRoute(decodeResult, data[4], data[2], data[6], data[5], void 0);
2471
+ ResultFormatter.temperature(decodeResult, data[7]);
2472
+ ResultFormatter.groundspeed(decodeResult, Number(data[10]));
2473
+ ResultFormatter.unknown(decodeResult, data[1]);
2474
+ ResultFormatter.unknown(decodeResult, data[8]);
2475
+ ResultFormatter.unknown(decodeResult, data[9]);
2476
+ ResultFormatter.unknown(decodeResult, data.slice(11).join(","));
2477
+ }
2478
+ }
2479
+ function processPosition2(decodeResult, data) {
2480
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
2481
+ if (position) {
2482
+ decodeResult.raw.position = position;
2483
+ decodeResult.formatted.items.push({
2484
+ type: "aircraft_position",
2485
+ code: "POS",
2486
+ label: "Aircraft Position",
2487
+ value: CoordinateUtils.coordinateString(position)
2488
+ });
2489
+ }
2490
+ if (data.length >= 10) {
2491
+ ResultFormatter.altitude(decodeResult, Number(data[3]) * 100);
2492
+ processRoute(decodeResult, data[1], data[2], data[4], data[5], data[6]);
2493
+ ResultFormatter.temperature(decodeResult, data[7]);
2494
+ ResultFormatter.unknown(decodeResult, data[8]);
2495
+ ResultFormatter.unknown(decodeResult, data[9]);
2496
+ }
2497
+ if (data.length >= 14) {
2498
+ ResultFormatter.groundspeed(decodeResult, Number(data[10]));
2499
+ ResultFormatter.unknown(decodeResult, data[11]);
2500
+ ResultFormatter.unknown(decodeResult, data[12]);
2501
+ ResultFormatter.unknown(decodeResult, data[13]);
2502
+ }
2503
+ }
2504
+ function processRoute(decodeResult, last, time, next, eta, then, date) {
2505
+ const lastTime = date ? DateTimeUtils.convertDateTimeToEpoch(time, date) : DateTimeUtils.convertHHMMSSToTod(time);
2506
+ const nextTime = date ? DateTimeUtils.convertDateTimeToEpoch(eta, date) : DateTimeUtils.convertHHMMSSToTod(eta);
2507
+ const timeFormat = date ? "epoch" : "tod";
2508
+ const lastWaypoint = RouteUtils.getWaypoint(last);
2509
+ lastWaypoint.time = lastTime;
2510
+ lastWaypoint.timeFormat = timeFormat;
2511
+ const nextWaypoint = RouteUtils.getWaypoint(next);
2512
+ nextWaypoint.time = nextTime;
2513
+ nextWaypoint.timeFormat = timeFormat;
2514
+ const thenWaypoint = RouteUtils.getWaypoint(then || "?");
2515
+ const waypoints = [lastWaypoint, nextWaypoint, thenWaypoint];
2516
+ decodeResult.raw.route = { waypoints };
2517
+ decodeResult.formatted.items.push({
2518
+ type: "aircraft_route",
2519
+ code: "ROUTE",
2520
+ label: "Aircraft Route",
2521
+ value: RouteUtils.routeToString(decodeResult.raw.route)
2522
+ });
2523
+ }
2524
+
2525
+ // lib/plugins/Label_4J_POS.ts
2526
+ var Label_4J_POS = class extends DecoderPlugin {
2527
+ name = "label-4j-pos";
2528
+ qualifiers() {
2529
+ return {
2530
+ labels: ["4J"],
2531
+ preambles: ["POS/"]
2438
2532
  };
2439
2533
  }
2534
+ // copied from Label_H1.ts since i don't really want to have to have
2535
+ // something named like that decode more than 1 type
2536
+ // if we figure out a good name, i'll combine them
2440
2537
  decode(message, options = {}) {
2441
- const decodeResult = this.defaultResult();
2538
+ let decodeResult = this.defaultResult();
2442
2539
  decodeResult.decoder.name = this.name;
2443
- decodeResult.formatted.description = "CPDLC Message";
2444
2540
  decodeResult.message = message;
2445
- if (options.debug) {
2446
- console.log("CPDLC: " + message);
2541
+ const msg = message.text.replace(/\n|\r/g, "");
2542
+ const decoded = H1Helper.decodeH1Message(decodeResult, msg);
2543
+ decodeResult.decoded = decoded;
2544
+ decodeResult.decoder.decodeLevel = !decodeResult.remaining.text ? "full" : "partial";
2545
+ if (decodeResult.formatted.items.length === 0) {
2546
+ if (options.debug) {
2547
+ console.log(`Decoder: Unknown H1 message: ${message.text}`);
2548
+ }
2549
+ ResultFormatter.unknown(decodeResult, message.text);
2550
+ decodeResult.decoded = false;
2551
+ decodeResult.decoder.decodeLevel = "none";
2447
2552
  }
2448
2553
  return decodeResult;
2449
2554
  }
2450
2555
  };
2451
2556
 
2452
- // lib/plugins/Label_ColonComma.ts
2453
- var Label_ColonComma = class extends DecoderPlugin {
2454
- name = "label-colon-comma";
2557
+ // lib/plugins/Label_4N.ts
2558
+ var Label_4N = class extends DecoderPlugin {
2559
+ name = "label-4n";
2455
2560
  qualifiers() {
2456
2561
  return {
2457
- labels: [":;"]
2562
+ labels: ["4N"]
2458
2563
  };
2459
2564
  }
2460
2565
  decode(message, options = {}) {
2461
2566
  const decodeResult = this.defaultResult();
2462
2567
  decodeResult.decoder.name = this.name;
2463
- decodeResult.raw.frequency = Number(message.text) / 1e3;
2464
- decodeResult.formatted.description = "Aircraft Transceiver Frequency Change";
2465
- decodeResult.formatted.items.push({
2466
- type: "frequency",
2467
- label: "Frequency",
2468
- value: `${decodeResult.raw.frequency} MHz`,
2469
- code: "FREQ"
2470
- });
2568
+ decodeResult.message = message;
2569
+ decodeResult.formatted.description = "Airline Defined";
2570
+ let text = message.text;
2571
+ if (text.match(/^M\d{2}A\w{6}/)) {
2572
+ ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
2573
+ text = text.substring(10);
2574
+ }
2471
2575
  decodeResult.decoded = true;
2472
- decodeResult.decoder.decodeLevel = "full";
2473
- return decodeResult;
2474
- }
2475
- };
2476
-
2477
- // lib/utils/flight_plan_utils.ts
2478
- var FlightPlanUtils = class _FlightPlanUtils {
2479
- /**
2480
- * Processes flight plan data
2481
- *
2482
- * Expected format is [header, key1, val1, ... keyN, valN]
2483
- *
2484
- * @param decodeResult - results
2485
- * @param data - original message split by ':'
2486
- * @returns whether all fields were processed or not
2487
- */
2488
- static processFlightPlan(decodeResult, data) {
2489
- let allKnownFields = _FlightPlanUtils.parseHeader(decodeResult, data[0]);
2490
- for (let i = 1; i < data.length; i += 2) {
2491
- const key = data[i];
2492
- const value = data[i + 1];
2493
- switch (key) {
2494
- case "A":
2495
- addProcedure(decodeResult, value, "arrival");
2496
- break;
2497
- case "AA":
2498
- addArrivalAirport(decodeResult, value);
2499
- break;
2500
- case "AP":
2501
- addProcedure(decodeResult, value, "approach");
2502
- break;
2503
- case "CR":
2504
- addCompanyRoute(decodeResult, value);
2505
- break;
2506
- case "D":
2507
- addProcedure(decodeResult, value, "departure");
2508
- break;
2509
- case "DA":
2510
- addDepartureAirport(decodeResult, value);
2511
- break;
2512
- case "F":
2513
- addRoute(decodeResult, value);
2514
- break;
2515
- case "R":
2516
- addRunway(decodeResult, value);
2517
- break;
2518
- // case 'WS': // something about routes, has altitude, so current parsing won't work
2519
- // break;
2520
- default:
2521
- if (allKnownFields) {
2522
- decodeResult.remaining.text = "";
2523
- allKnownFields = false;
2524
- }
2525
- decodeResult.remaining.text += `:${key}:${value}`;
2526
- decodeResult.decoder.decodeLevel = "partial";
2576
+ const fields = text.split(",");
2577
+ if (text.length === 51) {
2578
+ decodeResult.raw.day_of_month = text.substring(0, 2);
2579
+ ResultFormatter.departureAirport(decodeResult, text.substring(8, 11));
2580
+ ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16));
2581
+ ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(30, 45).replace(/^(.)0/, "$1")));
2582
+ ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100);
2583
+ ResultFormatter.unknownArr(decodeResult, [text.substring(2, 4), text.substring(19, 29)], " ");
2584
+ } else if (fields.length === 33) {
2585
+ decodeResult.raw.date = fields[3];
2586
+ if (fields[1] === "B") {
2587
+ ResultFormatter.position(decodeResult, { latitude: Number(fields[4]), longitude: Number(fields[5]) });
2588
+ ResultFormatter.altitude(decodeResult, Number(fields[6]));
2527
2589
  }
2528
- }
2529
- return allKnownFields;
2530
- }
2531
- static parseHeader(decodeResult, header) {
2532
- let allKnownFields = true;
2533
- if (header.startsWith("RF")) {
2534
- decodeResult.formatted.items.push({
2535
- type: "status",
2536
- code: "ROUTE_STATUS",
2537
- label: "Route Status",
2538
- value: "Route Filed"
2539
- });
2540
- decodeResult.raw.route_status = "RF";
2541
- if (header.length > 2) {
2542
- addRoute(decodeResult, header.substring(2));
2590
+ ResultFormatter.departureAirport(decodeResult, fields[8]);
2591
+ ResultFormatter.arrivalAirport(decodeResult, fields[9]);
2592
+ ResultFormatter.alternateAirport(decodeResult, fields[10]);
2593
+ ResultFormatter.arrivalRunway(decodeResult, fields[11].split("/")[0]);
2594
+ if (fields[12].length > 1) {
2595
+ ResultFormatter.alternateRunway(decodeResult, fields[12].split("/")[0]);
2543
2596
  }
2544
- } else if (header.startsWith("RP")) {
2545
- decodeResult.raw.route_status = "RP";
2546
- decodeResult.formatted.items.push({
2547
- type: "status",
2548
- code: "ROUTE_STATUS",
2549
- label: "Route Status",
2550
- value: "Route Planned"
2551
- });
2552
- decodeResult.raw.route_status = header;
2553
- } else if (header.startsWith("RI")) {
2554
- decodeResult.raw.route_status = "RI";
2555
- decodeResult.formatted.items.push({
2556
- type: "status",
2557
- code: "ROUTE_STATUS",
2558
- label: "Route Status",
2559
- value: "Route Inactive"
2560
- });
2597
+ ResultFormatter.checksum(decodeResult, fields[32]);
2598
+ ResultFormatter.unknownArr(decodeResult, [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter((f) => f != ""));
2561
2599
  } else {
2562
- decodeResult.remaining.text += header;
2563
- allKnownFields = false;
2564
- }
2565
- return allKnownFields;
2566
- }
2567
- };
2568
- function addArrivalAirport(decodeResult, value) {
2569
- ResultFormatter.arrivalAirport(decodeResult, value);
2570
- }
2571
- function addDepartureAirport(decodeResult, value) {
2572
- ResultFormatter.departureAirport(decodeResult, value);
2573
- }
2574
- function addRunway(decodeResult, value) {
2575
- if (value.length === 8) {
2576
- ResultFormatter.arrivalRunway(decodeResult, value.substring(4, 7));
2577
- }
2578
- ResultFormatter.departureRunway(decodeResult, value.substring(0, 3));
2579
- }
2580
- function addRoute(decodeResult, value) {
2581
- const route = value.split(".");
2582
- ResultFormatter.route(decodeResult, { waypoints: route.map((leg) => RouteUtils.getWaypoint(leg)) });
2583
- }
2584
- function addProcedure(decodeResult, value, type) {
2585
- if (decodeResult.raw.procedures === void 0) {
2586
- decodeResult.raw.procedures = [];
2587
- }
2588
- const data = value.split(".");
2589
- let waypoints;
2590
- if (data.length > 1) {
2591
- waypoints = data.slice(1).map((leg) => RouteUtils.getWaypoint(leg));
2592
- }
2593
- const route = { name: data[0], waypoints };
2594
- decodeResult.raw.procedures.push({ type, route });
2595
- const procedureName = type.substring(0, 1).toUpperCase() + type.slice(1);
2596
- let procedureValue = route.name;
2597
- decodeResult.formatted.items.push({
2598
- type: `procedure`,
2599
- code: "proc",
2600
- label: `${procedureName} Procedure`,
2601
- value: RouteUtils.routeToString(route)
2602
- });
2603
- }
2604
- function addCompanyRoute(decodeResult, value) {
2605
- const segments = value.split(".");
2606
- const parens_idx = segments[0].indexOf("(");
2607
- let name;
2608
- let runway;
2609
- if (parens_idx === -1) {
2610
- name = segments[0];
2611
- } else {
2612
- name = segments[0].slice(0, parens_idx);
2613
- runway = segments[0].slice(parens_idx + 1, segments[0].indexOf(")"));
2614
- }
2615
- let waypoints;
2616
- if (segments.length > 1) {
2617
- waypoints = segments.slice(1).map((leg) => RouteUtils.getWaypoint(leg));
2618
- }
2619
- decodeResult.raw.company_route = {
2620
- name,
2621
- runway,
2622
- waypoints
2623
- };
2624
- decodeResult.formatted.items.push({
2625
- type: "company_route",
2626
- code: "CR",
2627
- label: "Company Route",
2628
- value: RouteUtils.routeToString(decodeResult.raw.company_route)
2629
- });
2630
- }
2631
-
2632
- // lib/utils/h1_helper.ts
2633
- var H1Helper = class {
2634
- static decodeH1Message(decodeResult, message) {
2635
- const checksum = message.slice(-4);
2636
- const data = message.slice(0, message.length - 4);
2637
- const fields = data.split("/");
2638
- parseMessageType(decodeResult, fields[0]);
2639
- for (let i = 1; i < fields.length; ++i) {
2640
- if (fields[i].startsWith("FN")) {
2641
- decodeResult.raw.flight_number = fields[i].substring(2);
2642
- } else if (fields[i].startsWith("SN")) {
2643
- decodeResult.raw.serial_number = fields[i].substring(2);
2644
- } else if (fields[i].startsWith("DC")) {
2645
- processDC(decodeResult, fields[i].substring(2).split(","));
2646
- } else if (fields[i].startsWith("TS")) {
2647
- const ts = fields[i].substring(2).split(",");
2648
- let time = DateTimeUtils.convertDateTimeToEpoch(ts[0], ts[1]);
2649
- if (Number.isNaN(time)) {
2650
- const date = ts[1].substring(2, 4) + ts[1].substring(0, 2) + ts[1].substring(4, 6);
2651
- time = DateTimeUtils.convertDateTimeToEpoch(ts[0], date);
2652
- }
2653
- decodeResult.raw.message_date = ts[1];
2654
- decodeResult.raw.message_timestamp = time;
2655
- } else if (fields[i].startsWith("PS")) {
2656
- const pos = processPS(decodeResult, fields[i].substring(2).split(","));
2657
- } else if (fields[i].startsWith("DT")) {
2658
- const data2 = fields[i].substring(2).split(",");
2659
- processDT(decodeResult, data2);
2660
- } else if (fields[i].startsWith("ID")) {
2661
- processIdentification(decodeResult, fields[i].substring(2).split(","));
2662
- } else if (fields[i].startsWith("LR")) {
2663
- const data2 = fields[i].substring(2).split(",");
2664
- processLR(decodeResult, data2);
2665
- } else if (fields[i].startsWith("RI") || fields[i].startsWith("RF") || fields[i].startsWith("RP")) {
2666
- FlightPlanUtils.processFlightPlan(decodeResult, fields[i].split(":"));
2667
- } else if (fields[i].startsWith("PR")) {
2668
- ResultFormatter.unknown(decodeResult, fields[i], "/");
2669
- } else if (fields[i].startsWith("AF")) {
2670
- processAirField(decodeResult, fields[i].substring(2).split(","));
2671
- } else if (fields[i].startsWith("TD")) {
2672
- processTimeOfDeparture(decodeResult, fields[i].substring(2).split(","));
2673
- } else if (fields[i].startsWith("FX")) {
2674
- ResultFormatter.freetext(decodeResult, fields[i].substring(2));
2675
- } else if (fields[i].startsWith("ET")) {
2676
- if (fields[i].length === 7) {
2677
- ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[i].substring(3) + "00"));
2678
- } else if (fields[i].length === 8) {
2679
- ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[i].substring(4) + "00"));
2680
- } else {
2681
- ResultFormatter.unknown(decodeResult, fields[i], "/");
2682
- }
2683
- } else {
2684
- ResultFormatter.unknown(decodeResult, fields[i], "/");
2685
- }
2600
+ decodeResult.decoded = false;
2601
+ ResultFormatter.unknown(decodeResult, text);
2686
2602
  }
2687
- if (decodeResult.formatted.items.length > 0) {
2688
- ResultFormatter.checksum(decodeResult, checksum);
2603
+ if (decodeResult.decoded) {
2604
+ if (!decodeResult.remaining.text)
2605
+ decodeResult.decoder.decodeLevel = "full";
2606
+ else
2607
+ decodeResult.decoder.decodeLevel = "partial";
2608
+ } else {
2609
+ decodeResult.decoder.decodeLevel = "none";
2689
2610
  }
2690
- return true;
2611
+ return decodeResult;
2691
2612
  }
2692
2613
  };
2693
- function processAirField(decodeResult, data) {
2694
- if (data.length === 2) {
2695
- ResultFormatter.departureAirport(decodeResult, data[0]);
2696
- ResultFormatter.arrivalAirport(decodeResult, data[1]);
2697
- } else {
2698
- ResultFormatter.unknown(decodeResult, data.join(","), "AF/");
2699
- }
2700
- }
2701
- function processTimeOfDeparture(decodeResult, data) {
2702
- if (data.length === 2) {
2703
- decodeResult.raw.plannedDepartureTime = data[0];
2704
- decodeResult.formatted.items.push({
2705
- type: "ptd",
2706
- code: "ptd",
2707
- label: "Planned Departure Time",
2708
- value: `YYYY-MM-${data[0].substring(0, 2)}T${data[0].substring(2, 4)}:${data[0].substring(4)}:00Z`
2709
- });
2710
- decodeResult.raw.plannedDepartureTime = data[1];
2711
- decodeResult.formatted.items.push({
2712
- type: "etd",
2713
- code: "etd",
2714
- label: "Estimated Departure Time",
2715
- value: `${data[1].substring(0, 2)}:${data[1].substring(2)}`
2716
- });
2717
- } else {
2718
- ResultFormatter.unknown(decodeResult, data.join(","), "/TD");
2719
- }
2720
- }
2721
- function processIdentification(decodeResult, data) {
2722
- ResultFormatter.tail(decodeResult, data[0]);
2723
- if (data.length > 1) {
2724
- decodeResult.raw.flight_number = data[1];
2725
- }
2726
- if (data.length > 2) {
2727
- decodeResult.raw.mission_number = data[2];
2728
- }
2729
- }
2730
- function processDT(decodeResult, data) {
2731
- if (!decodeResult.raw.arrival_icao) {
2732
- ResultFormatter.arrivalAirport(decodeResult, data[0]);
2733
- } else if (decodeResult.raw.arrival_icao != data[0]) {
2734
- ResultFormatter.unknownArr(decodeResult, data);
2735
- }
2736
- if (data.length > 1) {
2737
- ResultFormatter.arrivalRunway(decodeResult, data[1]);
2738
- }
2739
- if (data.length > 2) {
2740
- ResultFormatter.currentFuel(decodeResult, Number(data[2]));
2741
- }
2742
- if (data.length > 3) {
2743
- ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[3]));
2744
- }
2745
- if (data.length > 4) {
2746
- ResultFormatter.remainingFuel(decodeResult, Number(data[4]));
2747
- }
2748
- if (data.length > 5) {
2749
- ResultFormatter.unknownArr(decodeResult, data);
2614
+
2615
+ // lib/plugins/Label_80.ts
2616
+ var Label_80 = class extends DecoderPlugin {
2617
+ name = "label-80";
2618
+ descriptions = {
2619
+ ALT: "Altitude",
2620
+ DWND: "Wind Direction",
2621
+ ETA: "Estimated Time of Arrival",
2622
+ FOB: "Fuel on Board",
2623
+ FL: "Flight Level",
2624
+ HDG: "Heading",
2625
+ MCH: "Aircraft Speed",
2626
+ NWYP: "Next Waypoint",
2627
+ POS: "Aircraft Position",
2628
+ SAT: "Static Air Temperature",
2629
+ SWND: "Wind Speed",
2630
+ TAS: "True Airspeed",
2631
+ WYP: "Waypoint"
2632
+ };
2633
+ qualifiers() {
2634
+ return {
2635
+ labels: ["80"],
2636
+ preambles: ["3N01 POSRPT"]
2637
+ };
2750
2638
  }
2751
- }
2752
- function processLR(decodeResult, data) {
2753
- if (data.length === 19) {
2754
- ResultFormatter.unknown(decodeResult, data[1]);
2755
- ResultFormatter.flightNumber(decodeResult, data[2]);
2756
- ResultFormatter.departureAirport(decodeResult, data[3]);
2757
- ResultFormatter.arrivalAirport(decodeResult, data[4]);
2758
- ResultFormatter.arrivalRunway(decodeResult, data[5]);
2759
- ResultFormatter.unknown(decodeResult, data.slice(6, 19).join(","));
2760
- } else {
2761
- ResultFormatter.unknown(decodeResult, data.join(","));
2639
+ decode(message, options = {}) {
2640
+ const decodeResult = this.defaultResult();
2641
+ decodeResult.decoder.name = this.name;
2642
+ decodeResult.formatted.description = "Airline Defined Position Report";
2643
+ const parts = message.text.split("\n");
2644
+ let posRptRegex = /^3N01 POSRPT \d\d\d\d\/\d\d (?<orig>\w+)\/(?<dest>\w+) \.(?<tail>[\w-]+)(\/(?<agate>.+) (?<sta>\w+:\w+))*/;
2645
+ let results = parts[0].match(posRptRegex);
2646
+ if (!results?.groups) {
2647
+ decodeResult.decoded = false;
2648
+ decodeResult.decoder.decodeLevel = "none";
2649
+ return decodeResult;
2650
+ }
2651
+ if (results && results.length > 0) {
2652
+ ResultFormatter.departureAirport(decodeResult, results.groups.orig);
2653
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.dest);
2654
+ ResultFormatter.tail(decodeResult, results.groups.tail);
2655
+ if (results.groups.agate) {
2656
+ decodeResult.raw.arrival_gate = results.groups.agate;
2657
+ decodeResult.formatted.items.push({
2658
+ type: "arrival_gate",
2659
+ code: "ARG",
2660
+ label: "Arrival Gate",
2661
+ value: `${results.groups.agate}`
2662
+ });
2663
+ decodeResult.raw.scheduled_time_of_arrival = results.groups.sta;
2664
+ decodeResult.formatted.items.push({
2665
+ type: "scheduled_time_of_arrival",
2666
+ code: "STA",
2667
+ label: "Scheduled Time of Arrival",
2668
+ value: `${results.groups.sta}`
2669
+ });
2670
+ }
2671
+ posRptRegex = /\/(?<field>\w+)\s(?<value>[\w\+\-:\.]+)\s*/gi;
2672
+ const remainingParts = parts.slice(1);
2673
+ for (const part of remainingParts) {
2674
+ const matches = part.matchAll(posRptRegex);
2675
+ for (const match of matches) {
2676
+ switch (match.groups?.field) {
2677
+ case "ALT": {
2678
+ ResultFormatter.altitude(decodeResult, Number(match.groups.value));
2679
+ break;
2680
+ }
2681
+ case "DWND": {
2682
+ decodeResult.raw.wind_direction = Number(match.groups.value);
2683
+ decodeResult.formatted.items.push({
2684
+ type: "wind_direction",
2685
+ code: "DWND",
2686
+ label: this.descriptions[match.groups.field],
2687
+ value: decodeResult.raw.wind_direction
2688
+ });
2689
+ break;
2690
+ }
2691
+ case "FL": {
2692
+ const flight_level = Number(match.groups.value);
2693
+ ResultFormatter.altitude(decodeResult, flight_level * 100);
2694
+ break;
2695
+ }
2696
+ case "FOB": {
2697
+ const fob = Number(match.groups.value);
2698
+ if (!isNaN(fob)) {
2699
+ ResultFormatter.currentFuel(decodeResult, fob);
2700
+ }
2701
+ break;
2702
+ }
2703
+ case "HDG": {
2704
+ ResultFormatter.heading(decodeResult, Number(match.groups.value));
2705
+ break;
2706
+ }
2707
+ case "MCH": {
2708
+ decodeResult.raw.mach = Number(match.groups.value) / 1e3;
2709
+ decodeResult.formatted.items.push({
2710
+ type: "mach",
2711
+ code: "MCH",
2712
+ label: this.descriptions[match.groups.field],
2713
+ value: `${decodeResult.raw.mach} Mach`
2714
+ });
2715
+ break;
2716
+ }
2717
+ case "NWYP": {
2718
+ decodeResult.raw.next_waypoint = match.groups.value;
2719
+ decodeResult.formatted.items.push({
2720
+ type: "next_waypoint",
2721
+ code: "NWYP",
2722
+ label: this.descriptions[match.groups.field],
2723
+ value: decodeResult.raw.next_waypoint
2724
+ });
2725
+ break;
2726
+ }
2727
+ case "POS": {
2728
+ const posRegex = /^(?<latd>[NS])(?<lat>.+)(?<lngd>[EW])(?<lng>.+)/;
2729
+ const posResult = match.groups.value.match(posRegex);
2730
+ const lat = Number(posResult?.groups?.lat) * (posResult?.groups?.lngd === "S" ? -1 : 1);
2731
+ const lon = Number(posResult?.groups?.lng) * (posResult?.groups?.lngd === "W" ? -1 : 1);
2732
+ const position = {
2733
+ latitude: Number.isInteger(lat) ? lat / 1e3 : lat / 100,
2734
+ longitude: Number.isInteger(lon) ? lon / 1e3 : lon / 100
2735
+ };
2736
+ ResultFormatter.position(decodeResult, position);
2737
+ break;
2738
+ }
2739
+ case "SWND": {
2740
+ decodeResult.raw.wind_speed = Number(match.groups.value);
2741
+ decodeResult.formatted.items.push({
2742
+ type: "wind_speed",
2743
+ code: "SWND",
2744
+ label: this.descriptions[match.groups.field],
2745
+ value: decodeResult.raw.wind_speed
2746
+ });
2747
+ break;
2748
+ }
2749
+ default: {
2750
+ if (match.groups?.field != void 0) {
2751
+ const description = this.descriptions[match.groups.field] ? this.descriptions[match.groups.field] : "Unknown";
2752
+ decodeResult.formatted.items.push({
2753
+ type: match.groups.field,
2754
+ code: match.groups.field,
2755
+ label: description || `Unknown (${match.groups.field})`,
2756
+ value: `${match.groups.value}`
2757
+ });
2758
+ }
2759
+ }
2760
+ }
2761
+ }
2762
+ }
2763
+ decodeResult.decoded = true;
2764
+ decodeResult.decoder.decodeLevel = "partial";
2765
+ }
2766
+ return decodeResult;
2762
2767
  }
2763
- }
2764
- function parseMessageType(decodeResult, messageType) {
2765
- const parts = messageType.split("#");
2766
- if (parts.length == 1) {
2767
- const type = parts[0].substring(0, 3);
2768
- if (type === "POS" && parts[0].length !== 3) {
2769
- processPosition2(decodeResult, parts[0].substring(3).split(","));
2768
+ };
2769
+
2770
+ // lib/plugins/Label_83.ts
2771
+ var Label_83 = class extends DecoderPlugin {
2772
+ name = "label-83";
2773
+ qualifiers() {
2774
+ return {
2775
+ labels: ["83"]
2776
+ };
2777
+ }
2778
+ decode(message, options = {}) {
2779
+ const decodeResult = this.defaultResult();
2780
+ decodeResult.decoder.name = this.name;
2781
+ decodeResult.message = message;
2782
+ decodeResult.formatted.description = "Airline Defined";
2783
+ let text = message.text;
2784
+ if (text.match(/^M\d{2}A\w{6}/)) {
2785
+ ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
2786
+ text = text.substring(10);
2770
2787
  }
2771
- return processMessageType(decodeResult, type);
2772
- } else if (parts.length == 2) {
2773
- if (parts[0].length > 0) {
2774
- ResultFormatter.unknown(decodeResult, parts[0].substring(0, 3));
2775
- decodeResult.raw.flight_number = parts[0].substring(3);
2776
- ResultFormatter.unknown(decodeResult, parts[1].length == 5 ? parts[1].substring(0, 2) : parts[1].substring(0, 3), "#");
2788
+ decodeResult.decoded = true;
2789
+ if (text.substring(0, 10) === "4DH3 ETAT2") {
2790
+ const fields = text.split(/\s+/);
2791
+ if (fields[2].length > 5) {
2792
+ decodeResult.raw.day_of_month = fields[2].substring(5);
2793
+ }
2794
+ ResultFormatter.unknown(decodeResult, fields[2].substring(0, 4));
2795
+ const subfields = fields[3].split("/");
2796
+ ResultFormatter.departureAirport(decodeResult, subfields[0]);
2797
+ ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
2798
+ ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, ""));
2799
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + "00"));
2800
+ } else if (text.substring(0, 5) === "001PR") {
2801
+ decodeResult.raw.day_of_month = text.substring(5, 7);
2802
+ const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(13, 28).replace(/\./g, ""));
2803
+ if (position) {
2804
+ ResultFormatter.position(decodeResult, position);
2805
+ }
2806
+ ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33)));
2807
+ ResultFormatter.unknown(decodeResult, text.substring(33));
2808
+ } else {
2809
+ const fields = text.replace(/\s/g, "").split(",");
2810
+ if (fields.length === 9) {
2811
+ ResultFormatter.departureAirport(decodeResult, fields[0]);
2812
+ ResultFormatter.arrivalAirport(decodeResult, fields[1]);
2813
+ decodeResult.raw.day_of_month = fields[2].substring(0, 2);
2814
+ decodeResult.raw.time = fields[2].substring(2);
2815
+ ResultFormatter.position(
2816
+ decodeResult,
2817
+ {
2818
+ latitude: Number(fields[3].replace(/\s/g, "")),
2819
+ longitude: Number(fields[4].replace(/\s/g, ""))
2820
+ }
2821
+ );
2822
+ ResultFormatter.altitude(decodeResult, Number(fields[5]));
2823
+ ResultFormatter.groundspeed(decodeResult, Number(fields[6]));
2824
+ ResultFormatter.heading(decodeResult, Number(fields[7]));
2825
+ ResultFormatter.unknown(decodeResult, fields[8]);
2826
+ } else {
2827
+ decodeResult.decoded = false;
2828
+ ResultFormatter.unknown(decodeResult, message.text);
2829
+ }
2777
2830
  }
2778
- const type = parts[1].length == 5 ? parts[1].substring(2, 5) : parts[1].substring(3, 6);
2779
- if (parts[1].substring(3, 6) === "POS" && parts[1].length > 6) {
2780
- processPosition2(decodeResult, parts[1].substring(6).split(","));
2831
+ if (decodeResult.decoded) {
2832
+ if (!decodeResult.remaining.text)
2833
+ decodeResult.decoder.decodeLevel = "full";
2834
+ else
2835
+ decodeResult.decoder.decodeLevel = "partial";
2836
+ } else {
2837
+ decodeResult.decoder.decodeLevel = "none";
2781
2838
  }
2782
- processMessageType(decodeResult, type);
2783
- } else {
2784
- ResultFormatter.unknown(decodeResult, messageType);
2839
+ return decodeResult;
2785
2840
  }
2786
- }
2787
- function processMessageType(decodeResult, type) {
2788
- if (type === "FPN") {
2789
- decodeResult.formatted.description = "Flight Plan";
2790
- } else if (type === "FTX") {
2791
- decodeResult.formatted.description = "Free Text";
2792
- } else if (type === "INI") {
2793
- decodeResult.formatted.description = "Flight Plan Initial Report";
2794
- } else if (type === "POS") {
2795
- decodeResult.formatted.description = "Position Report";
2796
- } else if (type === "PRG") {
2797
- decodeResult.formatted.description = "Progress Report";
2798
- } else {
2799
- decodeResult.formatted.description = "Unknown H1 Message";
2841
+ };
2842
+
2843
+ // lib/plugins/Label_8E.ts
2844
+ var Label_8E = class extends DecoderPlugin {
2845
+ name = "label-8e";
2846
+ qualifiers() {
2847
+ return {
2848
+ labels: ["8E"]
2849
+ };
2800
2850
  }
2801
- }
2802
- function processDC(decodeResult, data) {
2803
- decodeResult.raw.message_date = data[0];
2804
- if (data.length === 1) {
2805
- } else if (data.length === 2) {
2806
- const date = data[0].substring(2, 4) + data[0].substring(0, 2) + data[0].substring(4, 6);
2807
- const time = DateTimeUtils.convertDateTimeToEpoch(data[1], data[0]);
2808
- decodeResult.raw.message_timestamp = time;
2851
+ decode(message, options = {}) {
2852
+ const decodeResult = this.defaultResult();
2853
+ decodeResult.decoder.name = this.name;
2854
+ decodeResult.formatted.description = "ETA Report";
2855
+ decodeResult.message = message;
2856
+ const regex = /^(?<arrival_icao>\w{4}),(?<arrival_eta>\d{4})$/;
2857
+ const results = message.text.match(regex);
2858
+ if (results?.groups) {
2859
+ if (options.debug) {
2860
+ console.log(`Label 8E ETA: groups`);
2861
+ console.log(results.groups);
2862
+ }
2863
+ ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results.groups.arrival_eta + "00"));
2864
+ ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
2865
+ }
2866
+ decodeResult.decoded = true;
2867
+ decodeResult.decoder.decodeLevel = "full";
2868
+ return decodeResult;
2809
2869
  }
2810
- }
2811
- function processPS(decodeResult, data) {
2812
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
2813
- if (position) {
2814
- decodeResult.raw.position = position;
2815
- decodeResult.formatted.items.push({
2816
- type: "aircraft_position",
2817
- code: "POS",
2818
- label: "Aircraft Position",
2819
- value: CoordinateUtils.coordinateString(position)
2820
- });
2870
+ };
2871
+
2872
+ // lib/plugins/Label_B6.ts
2873
+ var Label_B6_Forwardslash = class extends DecoderPlugin {
2874
+ name = "label-b6-forwardslash";
2875
+ qualifiers() {
2876
+ return {
2877
+ labels: ["B6"],
2878
+ preambles: ["/"]
2879
+ };
2821
2880
  }
2822
- if (data.length === 9) {
2823
- processRoute(decodeResult, data[3], data[1], data[5], data[4], void 0);
2824
- ResultFormatter.altitude(decodeResult, Number(data[2]) * 100);
2825
- ResultFormatter.temperature(decodeResult, data[6]);
2826
- ResultFormatter.unknown(decodeResult, data[7]);
2827
- ResultFormatter.unknown(decodeResult, data[8]);
2881
+ decode(message, options = {}) {
2882
+ const decodeResult = this.defaultResult();
2883
+ decodeResult.decoder.name = this.name;
2884
+ decodeResult.formatted.description = "CPDLC Message";
2885
+ decodeResult.message = message;
2886
+ if (options.debug) {
2887
+ console.log("CPDLC: " + message);
2888
+ }
2889
+ return decodeResult;
2828
2890
  }
2829
- if (data.length === 14) {
2830
- ResultFormatter.altitude(decodeResult, Number(data[3]) * 100);
2831
- processRoute(decodeResult, data[4], data[2], data[6], data[5], void 0);
2832
- ResultFormatter.temperature(decodeResult, data[7]);
2833
- ResultFormatter.groundspeed(decodeResult, Number(data[10]));
2834
- ResultFormatter.unknown(decodeResult, data[1]);
2835
- ResultFormatter.unknown(decodeResult, data[8]);
2836
- ResultFormatter.unknown(decodeResult, data[9]);
2837
- ResultFormatter.unknown(decodeResult, data.slice(11).join(","));
2891
+ };
2892
+
2893
+ // lib/plugins/Label_ColonComma.ts
2894
+ var Label_ColonComma = class extends DecoderPlugin {
2895
+ name = "label-colon-comma";
2896
+ qualifiers() {
2897
+ return {
2898
+ labels: [":;"]
2899
+ };
2838
2900
  }
2839
- }
2840
- function processPosition2(decodeResult, data) {
2841
- const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]);
2842
- if (position) {
2843
- decodeResult.raw.position = position;
2901
+ decode(message, options = {}) {
2902
+ const decodeResult = this.defaultResult();
2903
+ decodeResult.decoder.name = this.name;
2904
+ decodeResult.raw.frequency = Number(message.text) / 1e3;
2905
+ decodeResult.formatted.description = "Aircraft Transceiver Frequency Change";
2844
2906
  decodeResult.formatted.items.push({
2845
- type: "aircraft_position",
2846
- code: "POS",
2847
- label: "Aircraft Position",
2848
- value: CoordinateUtils.coordinateString(position)
2907
+ type: "frequency",
2908
+ label: "Frequency",
2909
+ value: `${decodeResult.raw.frequency} MHz`,
2910
+ code: "FREQ"
2849
2911
  });
2912
+ decodeResult.decoded = true;
2913
+ decodeResult.decoder.decodeLevel = "full";
2914
+ return decodeResult;
2850
2915
  }
2851
- if (data.length >= 10) {
2852
- ResultFormatter.altitude(decodeResult, Number(data[3]) * 100);
2853
- processRoute(decodeResult, data[1], data[2], data[4], data[5], data[6]);
2854
- ResultFormatter.temperature(decodeResult, data[7]);
2855
- ResultFormatter.unknown(decodeResult, data[8]);
2856
- ResultFormatter.unknown(decodeResult, data[9]);
2857
- }
2858
- if (data.length >= 14) {
2859
- ResultFormatter.groundspeed(decodeResult, Number(data[10]));
2860
- ResultFormatter.unknown(decodeResult, data[11]);
2861
- ResultFormatter.unknown(decodeResult, data[12]);
2862
- ResultFormatter.unknown(decodeResult, data[13]);
2863
- }
2864
- }
2865
- function processRoute(decodeResult, last, time, next, eta, then, date) {
2866
- const lastTime = date ? DateTimeUtils.convertDateTimeToEpoch(time, date) : DateTimeUtils.convertHHMMSSToTod(time);
2867
- const nextTime = date ? DateTimeUtils.convertDateTimeToEpoch(eta, date) : DateTimeUtils.convertHHMMSSToTod(eta);
2868
- const timeFormat = date ? "epoch" : "tod";
2869
- const lastWaypoint = RouteUtils.getWaypoint(last);
2870
- lastWaypoint.time = lastTime;
2871
- lastWaypoint.timeFormat = timeFormat;
2872
- const nextWaypoint = RouteUtils.getWaypoint(next);
2873
- nextWaypoint.time = nextTime;
2874
- nextWaypoint.timeFormat = timeFormat;
2875
- const thenWaypoint = RouteUtils.getWaypoint(then || "?");
2876
- const waypoints = [lastWaypoint, nextWaypoint, thenWaypoint];
2877
- decodeResult.raw.route = { waypoints };
2878
- decodeResult.formatted.items.push({
2879
- type: "aircraft_route",
2880
- code: "ROUTE",
2881
- label: "Aircraft Route",
2882
- value: RouteUtils.routeToString(decodeResult.raw.route)
2883
- });
2884
- }
2916
+ };
2885
2917
 
2886
2918
  // lib/plugins/Label_H1.ts
2887
2919
  var Label_H1 = class extends DecoderPlugin {
@@ -4220,6 +4252,7 @@ var MessageDecoder = class {
4220
4252
  this.registerPlugin(new Label_4A_DIS(this));
4221
4253
  this.registerPlugin(new Label_4A_DOOR(this));
4222
4254
  this.registerPlugin(new Label_4A_Slash_01(this));
4255
+ this.registerPlugin(new Label_4J_POS(this));
4223
4256
  this.registerPlugin(new Label_4N(this));
4224
4257
  this.registerPlugin(new Label_B6_Forwardslash(this));
4225
4258
  this.registerPlugin(new Label_H1_FLR(this));