From 5e8d511d427348e17849444f898ae8312def19fe Mon Sep 17 00:00:00 2001 From: Rajan Patel Date: Mon, 28 Oct 2024 00:01:18 +0000 Subject: [PATCH 1/9] pull up c-band prefix --- lib/MessageDecoder.ts | 14 +++++++ lib/plugins/Label_1L_Slash.ts | 2 +- lib/plugins/Label_30_Slash_EA.test.ts | 2 +- lib/plugins/Label_4A.test.ts | 52 +++++++++++-------------- lib/plugins/Label_4A.ts | 13 +------ lib/plugins/Label_4N.test.ts | 41 +++++++++----------- lib/plugins/Label_4N.ts | 25 +++++------- lib/plugins/Label_83.test.ts | 56 ++++++++++++--------------- lib/plugins/Label_83.ts | 21 ++++------ 9 files changed, 100 insertions(+), 126 deletions(-) diff --git a/lib/MessageDecoder.ts b/lib/MessageDecoder.ts index 507acec..bb2c4c6 100644 --- a/lib/MessageDecoder.ts +++ b/lib/MessageDecoder.ts @@ -2,6 +2,7 @@ import { DecodeResult, DecoderPluginInterface, Message, Options } from './Decode import * as Plugins from './plugins/official'; import { MIAMCoreUtils } from './utils/miam'; +import { ResultFormatter } from './utils/result_formatter'; export class MessageDecoder { name: string; @@ -95,6 +96,14 @@ export class MessageDecoder { } } + // C-Band puts a 10 char header in front of some message types + // First 4 chars are some kind of message number + // Last 6 chars are the flight number + let cband = message.text.match(/^(?[A-Z]\d{2}[A-Z])(?[A-Z]{2})(?[0-9]{4})/); + if (cband?.groups) { + message.text = message.text.substring(10); + } + // console.log('All plugins'); // console.log(this.plugins); const usablePlugins = this.plugins.filter((plugin) => { @@ -150,6 +159,11 @@ export class MessageDecoder { } } + if (cband?.groups) { + ResultFormatter.flightNumber(result, cband.groups.airline + Number(cband.groups.number)); + message.text = cband.input; + } + if (options.debug) { console.log('Result'); console.log(result); diff --git a/lib/plugins/Label_1L_Slash.ts b/lib/plugins/Label_1L_Slash.ts index e47513a..4f59f23 100644 --- a/lib/plugins/Label_1L_Slash.ts +++ b/lib/plugins/Label_1L_Slash.ts @@ -80,4 +80,4 @@ export class Label_1L_Slash extends DecoderPlugin { // eslint-disable-line camel } } -export default {}; \ No newline at end of file +export default {}; diff --git a/lib/plugins/Label_30_Slash_EA.test.ts b/lib/plugins/Label_30_Slash_EA.test.ts index bd04040..ebe6a27 100644 --- a/lib/plugins/Label_30_Slash_EA.test.ts +++ b/lib/plugins/Label_30_Slash_EA.test.ts @@ -14,7 +14,7 @@ test('decodes Label 30 sample 1', () => { }); const text = '/EA1719/DSKSFO/SK23'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "30", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); diff --git a/lib/plugins/Label_4A.test.ts b/lib/plugins/Label_4A.test.ts index 9be952d..d099168 100644 --- a/lib/plugins/Label_4A.test.ts +++ b/lib/plugins/Label_4A.test.ts @@ -15,11 +15,10 @@ test('matches Label 4A qualifiers', () => { test('decodes Label 4A, variant 1', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A(decoder); // https://app.airframes.io/messages/3451492279 const text = '063200,1910,.N343FR,FFT2028,KSLC,KORD,1,0632,RT0,LT0,'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4A", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -27,7 +26,7 @@ test('decodes Label 4A, variant 1', () => { expect(decodeResult.formatted.description).toBe('Latest New Format'); expect(decodeResult.message.text).toBe(text); expect(decodeResult.remaining.text).toBe('RT0,LT0,'); - expect(decodeResult.formatted.items.length).toBe(6); + expect(decodeResult.formatted.items.length).toBe(5); expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); expect(decodeResult.formatted.items[0].value).toBe('06:32:00'); expect(decodeResult.formatted.items[1].code).toBe('TAIL'); @@ -42,11 +41,10 @@ test('decodes Label 4A, variant 1', () => { test('decodes Label 4A, variant 1, no callsign', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A(decoder); // https://app.airframes.io/messages/3452310240 const text = '101606,1910,.N317FR,,KMDW,----,1,1016,RT0,LT1,'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4A", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -54,7 +52,7 @@ test('decodes Label 4A, variant 1, no callsign', () => { expect(decodeResult.formatted.description).toBe('Latest New Format'); expect(decodeResult.message.text).toBe(text); expect(decodeResult.remaining.text).toBe('RT0,LT1,'); - expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items.length).toBe(4); expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); expect(decodeResult.formatted.items[0].value).toBe('10:16:06'); expect(decodeResult.formatted.items[1].code).toBe('TAIL'); @@ -67,11 +65,10 @@ test('decodes Label 4A, variant 1, no callsign', () => { test('decodes Label 4A, variant 2', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A(decoder); // https://app.airframes.io/messages/3461807403 const text = 'N45129W093113MSP/07 ,204436123VECTORS,,P04,268044858,46904221'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4A", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -92,11 +89,10 @@ test('decodes Label 4A, variant 2', () => { test('decodes Label 4A, variant 2, C-Band', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A(decoder); // https://app.airframes.io/messages/3461407615 const text = 'M60ALH0752N22456E077014OSE35 ,192027370VEX36 ,192316,M46,275043309,85220111'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4A", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -105,25 +101,24 @@ test('decodes Label 4A, variant 2, C-Band', () => { expect(decodeResult.message.text).toBe(text); expect(decodeResult.remaining.text).toBe('275043309,85220111'); expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].code).toBe('FLIGHT'); - expect(decodeResult.formatted.items[0].value).toBe('LH752'); - expect(decodeResult.formatted.items[1].code).toBe('POS'); - expect(decodeResult.formatted.items[1].value).toBe('22.456 N, 77.014 E'); - expect(decodeResult.formatted.items[2].code).toBe('ALT'); - expect(decodeResult.formatted.items[2].value).toBe('37000 feet'); - expect(decodeResult.formatted.items[3].code).toBe('ROUTE'); - expect(decodeResult.formatted.items[3].value).toBe('OSE35@19:20:27 > VEX36@19:23:16'); - expect(decodeResult.formatted.items[4].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[4].value).toBe('-46 degrees'); + expect(decodeResult.formatted.items[0].code).toBe('POS'); + expect(decodeResult.formatted.items[0].value).toBe('22.456 N, 77.014 E'); + expect(decodeResult.formatted.items[1].code).toBe('ALT'); + expect(decodeResult.formatted.items[1].value).toBe('37000 feet'); + expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); + expect(decodeResult.formatted.items[2].value).toBe('OSE35@19:20:27 > VEX36@19:23:16'); + expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); + expect(decodeResult.formatted.items[3].value).toBe('-46 degrees'); + expect(decodeResult.formatted.items[4].code).toBe('FLIGHT'); + expect(decodeResult.formatted.items[4].value).toBe('LH752'); }); test('decodes Label 4A, variant 3', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A(decoder); // https://globe.adsbexchange.com/?icao=A39AC6&showTrace=2024-09-22×tamp=1727009085 const text = '124442,1320, 138,33467,N 41.093,W 72.677'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4A", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -144,15 +139,14 @@ test('decodes Label 4A, variant 3', () => { test('decodes Label 4A_DIS ', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A(decoder); // https://app.airframes.io/messages/3449413366 const text = 'DIS01,182103,WEN3100,WRONG CREW HAHAHA'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4A", text: text }); - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.formatted.items.length).toBe(0); +// expect(decodeResult.decoded).toBe(false); +// expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).not.toBe('label-4a'); +// expect(decodeResult.formatted.description).toBe('Latest New Format'); +// expect(decodeResult.formatted.items.length).toBe(0); }); diff --git a/lib/plugins/Label_4A.ts b/lib/plugins/Label_4A.ts index 574e83c..c90d2ec 100644 --- a/lib/plugins/Label_4A.ts +++ b/lib/plugins/Label_4A.ts @@ -20,16 +20,8 @@ export class Label_4A extends DecoderPlugin { decodeResult.message = message; decodeResult.formatted.description = 'Latest New Format'; - - // Inmarsat C-band seems to prefix normal messages with a message number and flight number - let text = message.text; - if (text.match(/^M\d{2}A\w{6}/)) { - ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1")); - text = text.substring(10); - } - decodeResult.decoded = true; - const fields = text.split(","); + const fields = message.text.split(","); if (fields.length === 11) { // variant 1 ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0])); @@ -38,7 +30,6 @@ export class Label_4A extends DecoderPlugin { ResultFormatter.callsign(decodeResult, fields[3]); ResultFormatter.departureAirport(decodeResult, fields[4]); ResultFormatter.arrivalAirport(decodeResult, fields[5]); - ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100); ResultFormatter.unknownArr(decodeResult, fields.slice(8)); } else if (fields.length === 6) { if (fields[0].match(/^[NS]/)) { @@ -68,7 +59,7 @@ export class Label_4A extends DecoderPlugin { } } else { decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, text); + ResultFormatter.unknown(decodeResult, message.text); } if (decodeResult.decoded) { diff --git a/lib/plugins/Label_4N.test.ts b/lib/plugins/Label_4N.test.ts index 36b2b3d..0630194 100644 --- a/lib/plugins/Label_4N.test.ts +++ b/lib/plugins/Label_4N.test.ts @@ -11,11 +11,10 @@ test('matches Label 4N qualifiers', () => { test('decodes Label 4N variant 1', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4N(decoder); // https://globe.adsbexchange.com/?icao=A15027&showTrace=2024-09-23×tamp=1727057017 const text = '22024N MCI JFK1\r\n0013 0072 N040586 W074421 230'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4N", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -37,11 +36,10 @@ test('decodes Label 4N variant 1', () => { test('decodes Label 4N variant 2B', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4N(decoder); // https://app.airframes.io/messages/3421601874 const text = '285,B,69005074-507,10/12,+36.081,-094.810,35014,002.3,ELP,SDF,SDF,17R/,17L/,0,0,,,,,,0,0,0,0,1,,,,,247.0,014.2,261.2,421A'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4N", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -71,11 +69,10 @@ test('decodes Label 4N variant 2B', () => { test('decodes Label 4N variant 2C', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4N(decoder); // https://globe.adsbexchange.com/?icao=A3E08D&showTrace=2024-09-24×tamp=1727181643 const text = '285,C,,09/24,,,,,EWR,PHL,PHL,09R/,/,0,0,,,,,,1,0,0,0,1,0,,0,0,198.5,014.5,213.0,9BCD'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4N", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -99,11 +96,10 @@ test('decodes Label 4N variant 2C', () => { test('decodes Label 4N variant 2C (C-band)', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4N(decoder); // https://app.airframes.io/messages/3422221702 const text = 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4N", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -114,28 +110,27 @@ test('decodes Label 4N variant 2C (C-band)', () => { expect(decodeResult.raw.date).toBe('10/12'); expect(decodeResult.remaining.text).toBe('C,0,0,0,0,0,0,1,0,0,0,709.8,048.7,758.5'); expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].code).toBe('FLIGHT'); - expect(decodeResult.formatted.items[0].value).toBe('UP109'); - expect(decodeResult.formatted.items[1].code).toBe('ORG'); - expect(decodeResult.formatted.items[1].value).toBe('NRT'); - expect(decodeResult.formatted.items[2].code).toBe('DST'); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].value).toBe('NRT'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].value).toBe('ANC'); + expect(decodeResult.formatted.items[2].code).toBe('ALT_DST'); expect(decodeResult.formatted.items[2].value).toBe('ANC'); - expect(decodeResult.formatted.items[3].code).toBe('ALT_DST'); - expect(decodeResult.formatted.items[3].value).toBe('ANC'); - expect(decodeResult.formatted.items[4].code).toBe('ARWY'); - expect(decodeResult.formatted.items[4].value).toBe('07R'); - expect(decodeResult.formatted.items[5].code).toBe('ALT_ARWY'); - expect(decodeResult.formatted.items[5].value).toBe('33'); - expect(decodeResult.formatted.items[6].code).toBe('CHECKSUM'); - expect(decodeResult.formatted.items[6].value).toBe('0x75f3'); + expect(decodeResult.formatted.items[3].code).toBe('ARWY'); + expect(decodeResult.formatted.items[3].value).toBe('07R'); + expect(decodeResult.formatted.items[4].code).toBe('ALT_ARWY'); + expect(decodeResult.formatted.items[4].value).toBe('33'); + expect(decodeResult.formatted.items[5].code).toBe('CHECKSUM'); + expect(decodeResult.formatted.items[5].value).toBe('0x75f3'); + expect(decodeResult.formatted.items[6].code).toBe('FLIGHT'); + expect(decodeResult.formatted.items[6].value).toBe('UP109'); }); test('decodes Label 4N ', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4N(decoder); const text = '4N Bogus message'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "4N", text: text }); expect(decodeResult.decoded).toBe(false); expect(decodeResult.decoder.decodeLevel).toBe('none'); diff --git a/lib/plugins/Label_4N.ts b/lib/plugins/Label_4N.ts index 9606975..4267e31 100644 --- a/lib/plugins/Label_4N.ts +++ b/lib/plugins/Label_4N.ts @@ -18,23 +18,16 @@ export class Label_4N extends DecoderPlugin { decodeResult.message = message; decodeResult.formatted.description = 'Airline Defined'; - // Inmarsat C-band seems to prefix normal messages with a message number and flight number - let text = message.text; - if (text.match(/^M\d{2}A\w{6}/)) { - ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1")); - text = text.substring(10); - } - decodeResult.decoded = true; - const fields = text.split(","); - if (text.length === 51) { + const fields = message.text.split(","); + if (message.text.length === 51) { // variant 1 - decodeResult.raw.day = text.substring(0, 2); - ResultFormatter.departureAirport(decodeResult, text.substring(8, 11)); - ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16)); - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(30, 45).replace(/^(.)0/, "$1"))); - ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100); - ResultFormatter.unknownArr(decodeResult, [text.substring(2, 4), text.substring(19, 29)], " "); + decodeResult.raw.day = message.text.substring(0, 2); + ResultFormatter.departureAirport(decodeResult, message.text.substring(8, 11)); + ResultFormatter.arrivalAirport(decodeResult, message.text.substring(13, 16)); + ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(message.text.substring(30, 45).replace(/^(.)0/, "$1"))); + ResultFormatter.altitude(decodeResult, Number(messge.text.substring(48, 51)) * 100); + ResultFormatter.unknownArr(decodeResult, [message.text.substring(2, 4), message.text.substring(19, 29)], " "); } else if (fields.length === 33) { // variant 2 decodeResult.raw.date = fields[3]; @@ -53,7 +46,7 @@ export class Label_4N extends DecoderPlugin { ResultFormatter.unknownArr(decodeResult, [...fields.slice(1,3), fields[7], ...fields.slice(13, 32)].filter((f) => f != "")); } else { decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, text); + ResultFormatter.unknown(decodeResult, message.text); } if (decodeResult.decoded) { diff --git a/lib/plugins/Label_83.test.ts b/lib/plugins/Label_83.test.ts index b6f916d..f3335c0 100644 --- a/lib/plugins/Label_83.test.ts +++ b/lib/plugins/Label_83.test.ts @@ -11,11 +11,10 @@ test('matches Label 83 qualifiers', () => { test('decodes Label 83 variant 1', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); // https://globe.adsbexchange.com/?icao=A2A3B5&showTrace=2024-09-22×tamp=1726967032 const text = 'KLAX,KEWR,220103, 40.53,- 74.47, 3836,212, 140.0, 19700'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "83", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -48,11 +47,10 @@ test('decodes Label 83 variant 1', () => { test('decodes Label 83 variant 1 (C-band)', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); // https://app.airframes.io/messages/3413113024 const text = 'M05AUA0007KIAH,RJAA,110012, 39.12,-175.10,39001,265,-107.6, 64900'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "83", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -70,29 +68,28 @@ test('decodes Label 83 variant 1 (C-band)', () => { expect(decodeResult.raw.heading).toBe(-107.6); expect(decodeResult.remaining.text).toBe('64900'); expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].type).toBe('flight_number'); - expect(decodeResult.formatted.items[0].value).toBe('UA7'); + expect(decodeResult.formatted.items[0].type).toBe('icao'); + expect(decodeResult.formatted.items[0].value).toBe('KIAH'); expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].value).toBe('KIAH'); - expect(decodeResult.formatted.items[2].type).toBe('icao'); - expect(decodeResult.formatted.items[2].value).toBe('RJAA'); - expect(decodeResult.formatted.items[3].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[3].value).toBe('39.120 N, 175.100 W'); - expect(decodeResult.formatted.items[4].type).toBe('altitude'); - expect(decodeResult.formatted.items[4].value).toBe('39001 feet'); - expect(decodeResult.formatted.items[5].type).toBe('aircraft_groundspeed'); - expect(decodeResult.formatted.items[5].value).toBe('265 knots'); - expect(decodeResult.formatted.items[6].type).toBe('heading'); - expect(decodeResult.formatted.items[6].value).toBe('-107.6'); + expect(decodeResult.formatted.items[1].value).toBe('RJAA'); + expect(decodeResult.formatted.items[2].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[2].value).toBe('39.120 N, 175.100 W'); + expect(decodeResult.formatted.items[3].type).toBe('altitude'); + expect(decodeResult.formatted.items[3].value).toBe('39001 feet'); + expect(decodeResult.formatted.items[4].type).toBe('aircraft_groundspeed'); + expect(decodeResult.formatted.items[4].value).toBe('265 knots'); + expect(decodeResult.formatted.items[5].type).toBe('heading'); + expect(decodeResult.formatted.items[5].value).toBe('-107.6'); + expect(decodeResult.formatted.items[6].type).toBe('flight_number'); + expect(decodeResult.formatted.items[6].value).toBe('UA7'); }); test('decodes Label 83 variant 2', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); // https://globe.adsbexchange.com/?icao=478F43&showTrace=2024-09-22×tamp=1727022863 const text = '4DH3 ETAT2 0907/22 ENGM/KEWR .LN-RKO\r\n/ETA 1641'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "83", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -118,11 +115,10 @@ test('decodes Label 83 variant 2', () => { test('decodes Label 83 variant 3', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); // https://globe.adsbexchange.com/?icao=AC15A1&showTrace=2024-09-22×tamp=1726977342 const text = '001PR22035539N4038.6W07427.80292500008'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "83", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -143,11 +139,10 @@ test('decodes Label 83 variant 3', () => { test('decodes Label 83 variant 3 (C-band)', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); // https://app.airframes.io/messages/3413346742 const text = 'M09AXA0001001PR11013423N0556.6E11603.0000000----'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "83", text: text }); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -161,20 +156,19 @@ test('decodes Label 83 variant 3 (C-band)', () => { expect(decodeResult.raw.altitude).toBe(0); expect(decodeResult.remaining.text).toBe('0----'); expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].type).toBe('flight_number'); - expect(decodeResult.formatted.items[0].value).toBe('XA1'); - expect(decodeResult.formatted.items[1].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[1].value).toBe('5.943 N, 116.050 E'); - expect(decodeResult.formatted.items[2].type).toBe('altitude'); - expect(decodeResult.formatted.items[2].value).toBe('0 feet'); + expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[0].value).toBe('5.943 N, 116.050 E'); + expect(decodeResult.formatted.items[1].type).toBe('altitude'); + expect(decodeResult.formatted.items[1].value).toBe('0 feet'); + expect(decodeResult.formatted.items[2].type).toBe('flight_number'); + expect(decodeResult.formatted.items[2].value).toBe('XA1'); }); test('decodes Label 83 ', () => { const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); const text = '83 Bogus message'; - const decodeResult = decoderPlugin.decode({ text: text }); + const decodeResult = decoder.decode({ label: "83", text: text }); expect(decodeResult.decoded).toBe(false); expect(decodeResult.decoder.decodeLevel).toBe('none'); diff --git a/lib/plugins/Label_83.ts b/lib/plugins/Label_83.ts index 4687e13..41bb669 100644 --- a/lib/plugins/Label_83.ts +++ b/lib/plugins/Label_83.ts @@ -19,17 +19,10 @@ export class Label_83 extends DecoderPlugin { decodeResult.message = message; decodeResult.formatted.description = 'Airline Defined'; - // Inmarsat C-band seems to prefix normal messages with a message number and flight number - let text = message.text; - if (text.match(/^M\d{2}A\w{6}/)) { - ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1")); - text = text.substring(10); - } - decodeResult.decoded = true; - if (text.substring(0, 10) === "4DH3 ETAT2") { + if (message.text.substring(0, 10) === "4DH3 ETAT2") { // variant 2 - const fields = text.split(/\s+/); + const fields = message.text.split(/\s+/); if (fields[2].length > 5) { decodeResult.raw.day = fields[2].substring(5); } @@ -39,17 +32,17 @@ export class Label_83 extends DecoderPlugin { ResultFormatter.arrivalAirport(decodeResult, subfields[1]); ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, "")); ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + "00")); - } else if (text.substring(0, 5) === "001PR") { + } else if (message.text.substring(0, 5) === "001PR") { // variant 3 decodeResult.raw.day = text.substring(5, 7); - const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(13, 28).replace(/\./g, "")) + const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(message.text.substring(13, 28).replace(/\./g, "")) if (position) { ResultFormatter.position(decodeResult, position); } - ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33))); - ResultFormatter.unknown(decodeResult, text.substring(33)); + ResultFormatter.altitude(decodeResult, Number(message.text.substring(28, 33))); + decodeResult.remaining.text = message.text.substring(33); } else { - const fields = text.replace(/\s/g, "").split(','); + const fields = message.text.replace(/\s/g, "").split(','); if (fields.length === 9) { // variant 1 ResultFormatter.departureAirport(decodeResult, fields[0]); From 00ce6edb76c6d9bb000f3ecefe5296fafaf01a4c Mon Sep 17 00:00:00 2001 From: Rajan Patel Date: Tue, 29 Oct 2024 04:33:21 +0000 Subject: [PATCH 2/9] make it build --- lib/MessageDecoder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/MessageDecoder.ts b/lib/MessageDecoder.ts index bb2c4c6..67f35ef 100644 --- a/lib/MessageDecoder.ts +++ b/lib/MessageDecoder.ts @@ -159,7 +159,7 @@ export class MessageDecoder { } } - if (cband?.groups) { + if (cband?.groups && cband?.input) { ResultFormatter.flightNumber(result, cband.groups.airline + Number(cband.groups.number)); message.text = cband.input; } From fe1cdc71c03042f5c5680dbb74dfe430ad4f3797 Mon Sep 17 00:00:00 2001 From: Rajan Patel Date: Wed, 30 Oct 2024 02:15:03 +0000 Subject: [PATCH 3/9] allow numeric airline code --- lib/MessageDecoder.ts | 2 +- lib/plugins/Label_4N.ts | 2 +- lib/plugins/Label_83.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/MessageDecoder.ts b/lib/MessageDecoder.ts index 67f35ef..7cc4572 100644 --- a/lib/MessageDecoder.ts +++ b/lib/MessageDecoder.ts @@ -99,7 +99,7 @@ export class MessageDecoder { // C-Band puts a 10 char header in front of some message types // First 4 chars are some kind of message number // Last 6 chars are the flight number - let cband = message.text.match(/^(?[A-Z]\d{2}[A-Z])(?[A-Z]{2})(?[0-9]{4})/); + let cband = message.text.match(/^(?[A-Z]\d{2}[A-Z])(?[A-Z0-9]{2})(?[0-9]{4})/); if (cband?.groups) { message.text = message.text.substring(10); } diff --git a/lib/plugins/Label_4N.ts b/lib/plugins/Label_4N.ts index 4267e31..dc2da8a 100644 --- a/lib/plugins/Label_4N.ts +++ b/lib/plugins/Label_4N.ts @@ -26,7 +26,7 @@ export class Label_4N extends DecoderPlugin { ResultFormatter.departureAirport(decodeResult, message.text.substring(8, 11)); ResultFormatter.arrivalAirport(decodeResult, message.text.substring(13, 16)); ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(message.text.substring(30, 45).replace(/^(.)0/, "$1"))); - ResultFormatter.altitude(decodeResult, Number(messge.text.substring(48, 51)) * 100); + ResultFormatter.altitude(decodeResult, Number(message.text.substring(48, 51)) * 100); ResultFormatter.unknownArr(decodeResult, [message.text.substring(2, 4), message.text.substring(19, 29)], " "); } else if (fields.length === 33) { // variant 2 diff --git a/lib/plugins/Label_83.ts b/lib/plugins/Label_83.ts index 41bb669..804eea7 100644 --- a/lib/plugins/Label_83.ts +++ b/lib/plugins/Label_83.ts @@ -34,7 +34,7 @@ export class Label_83 extends DecoderPlugin { ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + "00")); } else if (message.text.substring(0, 5) === "001PR") { // variant 3 - decodeResult.raw.day = text.substring(5, 7); + decodeResult.raw.day = message.text.substring(5, 7); const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(message.text.substring(13, 28).replace(/\./g, "")) if (position) { ResultFormatter.position(decodeResult, position); From bb74b0f4d21520a8906148bbdb124a8169568148 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Thu, 5 Feb 2026 10:57:24 -0500 Subject: [PATCH 4/9] Pull C-Band decoding into it's own plugin * call other plugins * only process if it can be decoded --- lib/DecoderPluginInterface.ts | 4 +- lib/MessageDecoder.test.ts | 22 +++++++- lib/MessageDecoder.ts | 24 +------- lib/plugins/CBand.test.ts | 103 ++++++++++++++++++++++++++++++++++ lib/plugins/CBand.ts | 43 ++++++++++++++ lib/plugins/Label_4A.test.ts | 29 ---------- lib/plugins/Label_4A.ts | 8 +-- lib/plugins/Label_4N.test.ts | 32 ----------- lib/plugins/Label_83.test.ts | 66 ---------------------- lib/plugins/official.ts | 1 + 10 files changed, 175 insertions(+), 157 deletions(-) create mode 100644 lib/plugins/CBand.test.ts create mode 100644 lib/plugins/CBand.ts diff --git a/lib/DecoderPluginInterface.ts b/lib/DecoderPluginInterface.ts index 4302778..43a3438 100644 --- a/lib/DecoderPluginInterface.ts +++ b/lib/DecoderPluginInterface.ts @@ -2,7 +2,7 @@ * Representation of a Message */ export interface Message { - label?: string, + label: string, sublabel?: string, text: string, } @@ -45,5 +45,5 @@ export interface DecoderPluginInterface { decode(message: Message) : DecodeResult; meetsStateRequirements() : boolean; // onRegister(store: Store) : void; - qualifiers() : any; + qualifiers() : {labels: string[], preambles?: string[]}; } diff --git a/lib/MessageDecoder.test.ts b/lib/MessageDecoder.test.ts index 9c86b40..a96ffd7 100644 --- a/lib/MessageDecoder.test.ts +++ b/lib/MessageDecoder.test.ts @@ -13,8 +13,8 @@ describe('MessageDecoder', () => { text: 'T02!<<,:/k.E`;FOV@!\'s.16q6R+p(RK,|D2ujNJhRah?_qrNftWiI-V,@*RQs,tn,FYN$/V1!gNIc6CO;$D,1:.4?dF952;>XP$"B"Ok-Fr\'0^k?rP]3&UGoPX;\\ { expect(decodeResult.raw.text).toContain('A350,000354'); }) + test('C-band core seamless decode', () => { + const message = { + label: '4N', + text: 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3' + }; + + const decodeResult = decoder.decode(message); + + expect(decodeResult.decoded).toBe(true); + if(!decodeResult.message) { + expect(decodeResult.message).toBeDefined(); + return; + } + expect(decodeResult.message.label).toBe('4N'); + expect(decodeResult.message.sublabel).toBeUndefined(); + expect(decodeResult.message.text).toContain('M85AUP0109285'); + expect(decodeResult.formatted.items.length).toBe(7); + }) + test('Handles Multiple decodes', () => { const message = { label: 'H1', @@ -33,6 +52,7 @@ describe('MessageDecoder', () => { }; decoder.decode(message); + const decodeResult = decoder.decode(message); if(!decodeResult.message) { expect(decodeResult.message).toBeDefined(); diff --git a/lib/MessageDecoder.ts b/lib/MessageDecoder.ts index 2179956..2ec4dda 100644 --- a/lib/MessageDecoder.ts +++ b/lib/MessageDecoder.ts @@ -1,8 +1,6 @@ import { DecodeResult, DecoderPluginInterface, Message, Options } from './DecoderPluginInterface'; // eslint-disable-line import/no-cycle import * as Plugins from './plugins/official'; -import { MIAMCoreUtils } from './utils/miam'; -import { ResultFormatter } from './utils/result_formatter'; export class MessageDecoder { name: string; @@ -14,6 +12,7 @@ export class MessageDecoder { this.plugins = []; this.debug = false; + this.registerPlugin(new Plugins.CBand(this)); // first, for now, so it can wrap other plugins this.registerPlugin(new Plugins.Label_ColonComma(this)); this.registerPlugin(new Plugins.Label_5Z_Slash(this)); this.registerPlugin(new Plugins.Label_10_LDR(this)); @@ -88,27 +87,15 @@ export class MessageDecoder { } decode(message: Message, options: Options = {}): DecodeResult { - // C-Band puts a 10 char header in front of some message types - // First 4 chars are some kind of message number - // Last 6 chars are the flight number - let cband = message.text.match(/^(?[A-Z]\d{2}[A-Z])(?[A-Z0-9]{2})(?[0-9]{4})/); - if (cband?.groups) { - message.text = message.text.substring(10); - } - - // console.log('All plugins'); - // console.log(this.plugins); const usablePlugins = this.plugins.filter((plugin) => { const qualifiers = plugin.qualifiers(); - if (qualifiers.labels.includes(message.label)) { + if ((qualifiers.labels.includes(message.label)) + || (qualifiers.labels.length === 1 && qualifiers.labels[0] === '*')) { if (qualifiers.preambles && qualifiers.preambles.length > 0) { const matching = qualifiers.preambles.filter((preamble: string) => { // eslint-disable-line arrow-body-style,max-len - // console.log(message.text.substring(0, preamble.length)); - // console.log(preamble); return message.text.substring(0, preamble.length) === preamble; }); - // console.log(matching); return matching.length >= 1; } else { // eslint-disable-line no-else-return return true; @@ -151,11 +138,6 @@ export class MessageDecoder { } } - if (cband?.groups && cband?.input) { - ResultFormatter.flightNumber(result, cband.groups.airline + Number(cband.groups.number)); - message.text = cband.input; - } - if (options.debug) { console.log('Result'); console.log(result); diff --git a/lib/plugins/CBand.test.ts b/lib/plugins/CBand.test.ts new file mode 100644 index 0000000..701e7c1 --- /dev/null +++ b/lib/plugins/CBand.test.ts @@ -0,0 +1,103 @@ +import { MessageDecoder } from '../MessageDecoder'; +import { CBand } from './CBand'; + +describe('CBand', () => { + + let plugin: CBand; + + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new CBand(decoder); + }); + + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('c-band'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['*'], + }); + }); + +test('decodes Label 4A', () => { + // https://app.airframes.io/messages/3461407615 + const text = 'M60ALH0752N22456E077014OSE35 ,192027370VEX36 ,192316,M46,275043309,85220111'; + const decodeResult = plugin.decode({ label: "4A", text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('c-band-label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.raw.flight_number).toBe('LH752'); + expect(decodeResult.raw.position.latitude).toBe(22.456); + expect(decodeResult.raw.position.longitude).toBe(77.014); + expect(decodeResult.raw.route.waypoints.length).toBe(2); + expect(decodeResult.raw.route.waypoints[0].name).toBe('OSE35'); + expect(decodeResult.raw.route.waypoints[1].name).toBe('VEX36'); + expect(decodeResult.raw.outside_air_temperature).toBe(-46); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.remaining.text).toBe('275043309,85220111'); +}); + +test('decodes Label 4N variant 2C (C-band)', () => { + // https://app.airframes.io/messages/3422221702 + const text = 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3'; + const decodeResult = plugin.decode({ label: "4N", text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('c-band-label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.flight_number).toBe('UP109'); + expect(decodeResult.raw.date).toBe('10/12'); + expect(decodeResult.raw.departure_icao).toBe('NRT'); + expect(decodeResult.raw.arrival_icao).toBe('ANC'); + expect(decodeResult.raw.alternate_icao).toBe('ANC'); + expect(decodeResult.raw.arrival_runway).toBe('07R'); + expect(decodeResult.raw.alternate_runway).toBe('33'); + expect(decodeResult.raw.checksum).toBe(30195); + expect(decodeResult.remaining.text).toBe('C,0,0,0,0,0,0,1,0,0,0,709.8,048.7,758.5'); + expect(decodeResult.formatted.items.length).toBe(7); +}); + + +test('decodes Label 83 variant 1 (C-band)', () => { + // https://app.airframes.io/messages/3413113024 + const text = 'M05AUA0007KIAH,RJAA,110012, 39.12,-175.10,39001,265,-107.6, 64900'; + const decodeResult = plugin.decode({ label: "83", text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('c-band-label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.flight_number).toBe('UA7'); + expect(decodeResult.raw.departure_icao).toBe('KIAH'); + expect(decodeResult.raw.arrival_icao).toBe('RJAA'); + expect(decodeResult.raw.day).toBe('11'); + expect(decodeResult.raw.position.latitude).toBe(39.12); + expect(decodeResult.raw.position.longitude).toBe(-175.1); + expect(decodeResult.raw.altitude).toBe(39001); + expect(decodeResult.raw.groundspeed).toBe(265); + expect(decodeResult.raw.heading).toBe(-107.6); + expect(decodeResult.remaining.text).toBe('64900'); + expect(decodeResult.formatted.items.length).toBe(7); +}); + +test('decodes Label 83 variant 3 (C-band)', () => { + // https://app.airframes.io/messages/3413346742 + const text = 'M09AXA0001001PR11013423N0556.6E11603.0000000----'; + const decodeResult = plugin.decode({ label: "83", text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('c-band-label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.flight_number).toBe('XA1'); + expect(decodeResult.raw.day).toBe('11'); + expect(decodeResult.raw.position.latitude).toBe(5.943333333333333); + expect(decodeResult.raw.position.longitude).toBe(116.05); + expect(decodeResult.raw.altitude).toBe(0); + expect(decodeResult.remaining.text).toBe('0----'); + expect(decodeResult.formatted.items.length).toBe(3); +}); +}); \ No newline at end of file diff --git a/lib/plugins/CBand.ts b/lib/plugins/CBand.ts new file mode 100644 index 0000000..fe9e509 --- /dev/null +++ b/lib/plugins/CBand.ts @@ -0,0 +1,43 @@ +import { DecoderPlugin } from '../DecoderPlugin'; +import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; +import { MIAMCoreUtils } from '../utils/miam'; +import { ResultFormatter } from '../utils/result_formatter'; + +export class CBand extends DecoderPlugin { + name = 'c-band'; + qualifiers() { // eslint-disable-line class-methods-use-this + return { + labels: ['*'], + }; + } + + decode(message: Message, options: Options = {}) : DecodeResult { + let decodeResult = this.defaultResult(); + decodeResult.decoder.name = this.name; + decodeResult.message = message; + + // C-Band puts a 10 char header in front of some message types + // First 4 chars are some kind of message number + // Last 6 chars are the flight number + let cband = message.text.match(/^(?[A-Z]\d{2}[A-Z])(?[A-Z0-9]{2})(?[0-9]{4})/); + if (cband?.groups) { + const messageText = message.text.substring(10); + const decoded = this.decoder.decode({ + label: message.label, + sublabel: message.sublabel, + text: messageText, + }, options); + if(decoded.decoded) { + ResultFormatter.flightNumber(decodeResult, cband.groups.airline + Number(cband.groups.number)); + decodeResult.decoded = true; + decodeResult.decoder.decodeLevel = decoded.decoder.decodeLevel; + decodeResult.decoder.name = this.name + '-' + decoded.decoder.name; + decodeResult.raw = {...decodeResult.raw, ...decoded.raw }; + decodeResult.formatted.description = decoded.formatted.description; + decodeResult.formatted.items.push(...decoded.formatted.items); + decodeResult.remaining = decoded.remaining; + } + } + return decodeResult; + } +} diff --git a/lib/plugins/Label_4A.test.ts b/lib/plugins/Label_4A.test.ts index d099168..d7082e7 100644 --- a/lib/plugins/Label_4A.test.ts +++ b/lib/plugins/Label_4A.test.ts @@ -24,7 +24,6 @@ test('decodes Label 4A, variant 1', () => { expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.decoder.name).toBe('label-4a'); expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message.text).toBe(text); expect(decodeResult.remaining.text).toBe('RT0,LT0,'); expect(decodeResult.formatted.items.length).toBe(5); expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); @@ -50,7 +49,6 @@ test('decodes Label 4A, variant 1, no callsign', () => { expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.decoder.name).toBe('label-4a'); expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message.text).toBe(text); expect(decodeResult.remaining.text).toBe('RT0,LT1,'); expect(decodeResult.formatted.items.length).toBe(4); expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); @@ -74,7 +72,6 @@ test('decodes Label 4A, variant 2', () => { expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.decoder.name).toBe('label-4a'); expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message.text).toBe(text); expect(decodeResult.remaining.text).toBe('268044858,46904221'); expect(decodeResult.formatted.items.length).toBe(4); expect(decodeResult.formatted.items[0].code).toBe('POS'); @@ -87,31 +84,6 @@ test('decodes Label 4A, variant 2', () => { expect(decodeResult.formatted.items[3].value).toBe('4 degrees'); }); -test('decodes Label 4A, variant 2, C-Band', () => { - const decoder = new MessageDecoder(); - - // https://app.airframes.io/messages/3461407615 - const text = 'M60ALH0752N22456E077014OSE35 ,192027370VEX36 ,192316,M46,275043309,85220111'; - const decodeResult = decoder.decode({ label: "4A", text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.remaining.text).toBe('275043309,85220111'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].code).toBe('POS'); - expect(decodeResult.formatted.items[0].value).toBe('22.456 N, 77.014 E'); - expect(decodeResult.formatted.items[1].code).toBe('ALT'); - expect(decodeResult.formatted.items[1].value).toBe('37000 feet'); - expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); - expect(decodeResult.formatted.items[2].value).toBe('OSE35@19:20:27 > VEX36@19:23:16'); - expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].value).toBe('-46 degrees'); - expect(decodeResult.formatted.items[4].code).toBe('FLIGHT'); - expect(decodeResult.formatted.items[4].value).toBe('LH752'); -}); test('decodes Label 4A, variant 3', () => { const decoder = new MessageDecoder(); @@ -124,7 +96,6 @@ test('decodes Label 4A, variant 3', () => { expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.decoder.name).toBe('label-4a'); expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message.text).toBe(text); expect(decodeResult.remaining.text).toBe(' 138'); expect(decodeResult.formatted.items.length).toBe(4); expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); diff --git a/lib/plugins/Label_4A.ts b/lib/plugins/Label_4A.ts index b88b669..7862a45 100644 --- a/lib/plugins/Label_4A.ts +++ b/lib/plugins/Label_4A.ts @@ -26,14 +26,10 @@ export class Label_4A extends DecoderPlugin { // variant 1 ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0])); ResultFormatter.tail(decodeResult, fields[2].replace(".", "")); - if (fields[3]) - ResultFormatter.callsign(decodeResult, fields[3]); + if (fields[3]) ResultFormatter.callsign(decodeResult, fields[3]); ResultFormatter.departureAirport(decodeResult, fields[4]); ResultFormatter.arrivalAirport(decodeResult, fields[5]); - const alt = text.substring(48, 51); - if(alt !== '') { - ResultFormatter.altitude(decodeResult, Number(alt) * 100); - } + // ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100); ResultFormatter.unknownArr(decodeResult, fields.slice(8)); } else if (fields.length === 6) { if (fields[0].match(/^[NS]/)) { diff --git a/lib/plugins/Label_4N.test.ts b/lib/plugins/Label_4N.test.ts index 0630194..5cb3a29 100644 --- a/lib/plugins/Label_4N.test.ts +++ b/lib/plugins/Label_4N.test.ts @@ -94,38 +94,6 @@ test('decodes Label 4N variant 2C', () => { expect(decodeResult.formatted.items[4].value).toBe('0x9bcd'); }); -test('decodes Label 4N variant 2C (C-band)', () => { - const decoder = new MessageDecoder(); - - // https://app.airframes.io/messages/3422221702 - const text = 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3'; - const decodeResult = decoder.decode({ label: "4N", text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.flight_number).toBe('UP109'); - expect(decodeResult.raw.date).toBe('10/12'); - expect(decodeResult.remaining.text).toBe('C,0,0,0,0,0,0,1,0,0,0,709.8,048.7,758.5'); - expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].value).toBe('NRT'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].value).toBe('ANC'); - expect(decodeResult.formatted.items[2].code).toBe('ALT_DST'); - expect(decodeResult.formatted.items[2].value).toBe('ANC'); - expect(decodeResult.formatted.items[3].code).toBe('ARWY'); - expect(decodeResult.formatted.items[3].value).toBe('07R'); - expect(decodeResult.formatted.items[4].code).toBe('ALT_ARWY'); - expect(decodeResult.formatted.items[4].value).toBe('33'); - expect(decodeResult.formatted.items[5].code).toBe('CHECKSUM'); - expect(decodeResult.formatted.items[5].value).toBe('0x75f3'); - expect(decodeResult.formatted.items[6].code).toBe('FLIGHT'); - expect(decodeResult.formatted.items[6].value).toBe('UP109'); -}); - test('decodes Label 4N ', () => { const decoder = new MessageDecoder(); diff --git a/lib/plugins/Label_83.test.ts b/lib/plugins/Label_83.test.ts index f3335c0..2bbbe99 100644 --- a/lib/plugins/Label_83.test.ts +++ b/lib/plugins/Label_83.test.ts @@ -45,45 +45,6 @@ test('decodes Label 83 variant 1', () => { expect(decodeResult.formatted.items[5].value).toBe('140'); }); -test('decodes Label 83 variant 1 (C-band)', () => { - const decoder = new MessageDecoder(); - - // https://app.airframes.io/messages/3413113024 - const text = 'M05AUA0007KIAH,RJAA,110012, 39.12,-175.10,39001,265,-107.6, 64900'; - const decodeResult = decoder.decode({ label: "83", text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.flight_number).toBe('UA7'); - expect(decodeResult.raw.departure_icao).toBe('KIAH'); - expect(decodeResult.raw.arrival_icao).toBe('RJAA'); - expect(decodeResult.raw.day).toBe('11'); - expect(decodeResult.raw.position.latitude).toBe(39.12); - expect(decodeResult.raw.position.longitude).toBe(-175.1); - expect(decodeResult.raw.altitude).toBe(39001); - expect(decodeResult.raw.groundspeed).toBe(265); - expect(decodeResult.raw.heading).toBe(-107.6); - expect(decodeResult.remaining.text).toBe('64900'); - expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].type).toBe('icao'); - expect(decodeResult.formatted.items[0].value).toBe('KIAH'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].value).toBe('RJAA'); - expect(decodeResult.formatted.items[2].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[2].value).toBe('39.120 N, 175.100 W'); - expect(decodeResult.formatted.items[3].type).toBe('altitude'); - expect(decodeResult.formatted.items[3].value).toBe('39001 feet'); - expect(decodeResult.formatted.items[4].type).toBe('aircraft_groundspeed'); - expect(decodeResult.formatted.items[4].value).toBe('265 knots'); - expect(decodeResult.formatted.items[5].type).toBe('heading'); - expect(decodeResult.formatted.items[5].value).toBe('-107.6'); - expect(decodeResult.formatted.items[6].type).toBe('flight_number'); - expect(decodeResult.formatted.items[6].value).toBe('UA7'); -}); - test('decodes Label 83 variant 2', () => { const decoder = new MessageDecoder(); @@ -137,33 +98,6 @@ test('decodes Label 83 variant 3', () => { expect(decodeResult.formatted.items[1].value).toBe('2925 feet'); }); -test('decodes Label 83 variant 3 (C-band)', () => { - const decoder = new MessageDecoder(); - - // https://app.airframes.io/messages/3413346742 - const text = 'M09AXA0001001PR11013423N0556.6E11603.0000000----'; - const decodeResult = decoder.decode({ label: "83", text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.flight_number).toBe('XA1'); - expect(decodeResult.raw.day).toBe('11'); - expect(decodeResult.raw.position.latitude).toBe(5.943333333333333); - expect(decodeResult.raw.position.longitude).toBe(116.05); - expect(decodeResult.raw.altitude).toBe(0); - expect(decodeResult.remaining.text).toBe('0----'); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[0].value).toBe('5.943 N, 116.050 E'); - expect(decodeResult.formatted.items[1].type).toBe('altitude'); - expect(decodeResult.formatted.items[1].value).toBe('0 feet'); - expect(decodeResult.formatted.items[2].type).toBe('flight_number'); - expect(decodeResult.formatted.items[2].value).toBe('XA1'); -}); - test('decodes Label 83 ', () => { const decoder = new MessageDecoder(); diff --git a/lib/plugins/official.ts b/lib/plugins/official.ts index ea07375..1ce564a 100644 --- a/lib/plugins/official.ts +++ b/lib/plugins/official.ts @@ -1,3 +1,4 @@ +export * from './CBand'; export * from './Label_5Z_Slash'; export * from './Label_10_LDR'; export * from './Label_10_POS'; From c78110c8ab44defca7b70ccf14f523906ed02990 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Thu, 5 Feb 2026 11:05:50 -0500 Subject: [PATCH 5/9] cleanup messages --- lib/plugins/Label_4A.test.ts | 217 +++++++++++++++++------------------ lib/plugins/Label_4N.test.ts | 184 +++++++++++++++-------------- lib/plugins/Label_83.test.ts | 196 ++++++++++++++++--------------- 3 files changed, 294 insertions(+), 303 deletions(-) diff --git a/lib/plugins/Label_4A.test.ts b/lib/plugins/Label_4A.test.ts index d7082e7..422caa8 100644 --- a/lib/plugins/Label_4A.test.ts +++ b/lib/plugins/Label_4A.test.ts @@ -1,123 +1,120 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_4A } from './Label_4A'; -test('matches Label 4A qualifiers', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A(decoder); +describe('Label 4A', () => { - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-4a'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['4A'], - }); -}); - -test('decodes Label 4A, variant 1', () => { - const decoder = new MessageDecoder(); - - // https://app.airframes.io/messages/3451492279 - const text = '063200,1910,.N343FR,FFT2028,KSLC,KORD,1,0632,RT0,LT0,'; - const decodeResult = decoder.decode({ label: "4A", text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.remaining.text).toBe('RT0,LT0,'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[0].value).toBe('06:32:00'); - expect(decodeResult.formatted.items[1].code).toBe('TAIL'); - expect(decodeResult.formatted.items[1].value).toBe('N343FR'); - expect(decodeResult.formatted.items[2].code).toBe('CALLSIGN'); - expect(decodeResult.formatted.items[2].value).toBe('FFT2028'); - expect(decodeResult.formatted.items[3].code).toBe('ORG'); - expect(decodeResult.formatted.items[3].value).toBe('KSLC'); - expect(decodeResult.formatted.items[4].code).toBe('DST'); - expect(decodeResult.formatted.items[4].value).toBe('KORD'); -}); - -test('decodes Label 4A, variant 1, no callsign', () => { - const decoder = new MessageDecoder(); - - // https://app.airframes.io/messages/3452310240 - const text = '101606,1910,.N317FR,,KMDW,----,1,1016,RT0,LT1,'; - const decodeResult = decoder.decode({ label: "4A", text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.remaining.text).toBe('RT0,LT1,'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[0].value).toBe('10:16:06'); - expect(decodeResult.formatted.items[1].code).toBe('TAIL'); - expect(decodeResult.formatted.items[1].value).toBe('N317FR'); - expect(decodeResult.formatted.items[2].code).toBe('ORG'); - expect(decodeResult.formatted.items[2].value).toBe('KMDW'); - expect(decodeResult.formatted.items[3].code).toBe('DST'); - expect(decodeResult.formatted.items[3].value).toBe('----'); -}); + let plugin: Label_4A; + const message = { label: '4A', text: '' }; -test('decodes Label 4A, variant 2', () => { - const decoder = new MessageDecoder(); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_4A(decoder); + }); - // https://app.airframes.io/messages/3461807403 - const text = 'N45129W093113MSP/07 ,204436123VECTORS,,P04,268044858,46904221'; - const decodeResult = decoder.decode({ label: "4A", text: text }); + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-4a'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['4A'], + }); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.remaining.text).toBe('268044858,46904221'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('POS'); - expect(decodeResult.formatted.items[0].value).toBe('45.129 N, 93.113 W'); - expect(decodeResult.formatted.items[1].code).toBe('ALT'); - expect(decodeResult.formatted.items[1].value).toBe('12300 feet'); - expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); - expect(decodeResult.formatted.items[2].value).toBe('MSP/07@20:44:36 > VECTORS'); - expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].value).toBe('4 degrees'); -}); + test('decodes variant 1', () => { + // https://app.airframes.io/messages/3451492279 + message.text = '063200,1910,.N343FR,FFT2028,KSLC,KORD,1,0632,RT0,LT0,'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.remaining.text).toBe('RT0,LT0,'); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[0].value).toBe('06:32:00'); + expect(decodeResult.formatted.items[1].code).toBe('TAIL'); + expect(decodeResult.formatted.items[1].value).toBe('N343FR'); + expect(decodeResult.formatted.items[2].code).toBe('CALLSIGN'); + expect(decodeResult.formatted.items[2].value).toBe('FFT2028'); + expect(decodeResult.formatted.items[3].code).toBe('ORG'); + expect(decodeResult.formatted.items[3].value).toBe('KSLC'); + expect(decodeResult.formatted.items[4].code).toBe('DST'); + expect(decodeResult.formatted.items[4].value).toBe('KORD'); + }); + test('decodes variant 1, no callsign', () => { + // https://app.airframes.io/messages/3452310240 + message.text = '101606,1910,.N317FR,,KMDW,----,1,1016,RT0,LT1,'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.remaining.text).toBe('RT0,LT1,'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[0].value).toBe('10:16:06'); + expect(decodeResult.formatted.items[1].code).toBe('TAIL'); + expect(decodeResult.formatted.items[1].value).toBe('N317FR'); + expect(decodeResult.formatted.items[2].code).toBe('ORG'); + expect(decodeResult.formatted.items[2].value).toBe('KMDW'); + expect(decodeResult.formatted.items[3].code).toBe('DST'); + expect(decodeResult.formatted.items[3].value).toBe('----'); + }); -test('decodes Label 4A, variant 3', () => { - const decoder = new MessageDecoder(); + test('decodes variant 2', () => { + message.text = 'N45129W093113MSP/07 ,204436123VECTORS,,P04,268044858,46904221'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.remaining.text).toBe('268044858,46904221'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('POS'); + expect(decodeResult.formatted.items[0].value).toBe('45.129 N, 93.113 W'); + expect(decodeResult.formatted.items[1].code).toBe('ALT'); + expect(decodeResult.formatted.items[1].value).toBe('12300 feet'); + expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); + expect(decodeResult.formatted.items[2].value).toBe('MSP/07@20:44:36 > VECTORS'); + expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); + expect(decodeResult.formatted.items[3].value).toBe('4 degrees'); + }); - // https://globe.adsbexchange.com/?icao=A39AC6&showTrace=2024-09-22×tamp=1727009085 - const text = '124442,1320, 138,33467,N 41.093,W 72.677'; - const decodeResult = decoder.decode({ label: "4A", text: text }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.remaining.text).toBe(' 138'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[0].value).toBe('12:44:42'); - expect(decodeResult.formatted.items[1].code).toBe('ETA'); - expect(decodeResult.formatted.items[1].value).toBe('13:20:00'); - expect(decodeResult.formatted.items[2].code).toBe('ALT'); - expect(decodeResult.formatted.items[2].value).toBe('33467 feet'); - expect(decodeResult.formatted.items[3].code).toBe('POS'); - expect(decodeResult.formatted.items[3].value).toBe('41.093 N, 72.677 W'); -}); + test('decodes variant 3', () => { + // https://globe.adsbexchange.com/?icao=A39AC6&showTrace=2024-09-22×tamp=1727009085 + message.text = '124442,1320, 138,33467,N 41.093,W 72.677'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.remaining.text).toBe(' 138'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[0].value).toBe('12:44:42'); + expect(decodeResult.formatted.items[1].code).toBe('ETA'); + expect(decodeResult.formatted.items[1].value).toBe('13:20:00'); + expect(decodeResult.formatted.items[2].code).toBe('ALT'); + expect(decodeResult.formatted.items[2].value).toBe('33467 feet'); + expect(decodeResult.formatted.items[3].code).toBe('POS'); + expect(decodeResult.formatted.items[3].value).toBe('41.093 N, 72.677 W'); + }); -test('decodes Label 4A_DIS ', () => { - const decoder = new MessageDecoder(); + test('decodes Label 4A_DIS ', () => { // FIXME: this should not decode + // https://app.airframes.io/messages/3449413366 + message.text = 'DIS01,182103,WEN3100,WRONG CREW HAHAHA'; + const decodeResult = plugin.decode(message); - // https://app.airframes.io/messages/3449413366 - const text = 'DIS01,182103,WEN3100,WRONG CREW HAHAHA'; - const decodeResult = decoder.decode({ label: "4A", text: text }); - -// expect(decodeResult.decoded).toBe(false); -// expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).not.toBe('label-4a'); -// expect(decodeResult.formatted.description).toBe('Latest New Format'); -// expect(decodeResult.formatted.items.length).toBe(0); -}); + // expect(decodeResult.decoded).toBe(false); + // expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).not.toBe('label-4a'); + // expect(decodeResult.formatted.description).toBe('Latest New Format'); + // expect(decodeResult.formatted.items.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/lib/plugins/Label_4N.test.ts b/lib/plugins/Label_4N.test.ts index 5cb3a29..14693d5 100644 --- a/lib/plugins/Label_4N.test.ts +++ b/lib/plugins/Label_4N.test.ts @@ -1,108 +1,106 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_4N } from './Label_4N'; -test('matches Label 4N qualifiers', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4N(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-4n'); -}); +describe('Label 4N', () => { -test('decodes Label 4N variant 1', () => { - const decoder = new MessageDecoder(); + let plugin: Label_4N; + const message = { label: '4N', text: '' }; - // https://globe.adsbexchange.com/?icao=A15027&showTrace=2024-09-23×tamp=1727057017 - const text = '22024N MCI JFK1\r\n0013 0072 N040586 W074421 230'; - const decodeResult = decoder.decode({ label: "4N", text: text }); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_4N(decoder); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.day).toBe('22'); - expect(decodeResult.remaining.text).toBe('02 0013 0072'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].value).toBe('MCI'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].value).toBe('JFK'); - expect(decodeResult.formatted.items[2].code).toBe('POS'); - expect(decodeResult.formatted.items[2].value).toBe('40.977 N, 74.702 W'); - expect(decodeResult.formatted.items[3].code).toBe('ALT'); - expect(decodeResult.formatted.items[3].value).toBe('23000 feet'); -}); + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-4n'); + }); -test('decodes Label 4N variant 2B', () => { - const decoder = new MessageDecoder(); + test('decodes Label 4N variant 1', () => { + // https://globe.adsbexchange.com/?icao=A15027&showTrace=2024-09-23×tamp=1727057017 + message.text = '22024N MCI JFK1\r\n0013 0072 N040586 W074421 230'; + const decodeResult = plugin.decode(message); - // https://app.airframes.io/messages/3421601874 - const text = '285,B,69005074-507,10/12,+36.081,-094.810,35014,002.3,ELP,SDF,SDF,17R/,17L/,0,0,,,,,,0,0,0,0,1,,,,,247.0,014.2,261.2,421A'; - const decodeResult = decoder.decode({ label: "4N", text: text }); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.day).toBe('22'); + expect(decodeResult.remaining.text).toBe('02 0013 0072'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].value).toBe('MCI'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].value).toBe('JFK'); + expect(decodeResult.formatted.items[2].code).toBe('POS'); + expect(decodeResult.formatted.items[2].value).toBe('40.977 N, 74.702 W'); + expect(decodeResult.formatted.items[3].code).toBe('ALT'); + expect(decodeResult.formatted.items[3].value).toBe('23000 feet'); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.date).toBe('10/12'); - expect(decodeResult.remaining.text).toBe('B,69005074-507,002.3,0,0,0,0,0,0,1,247.0,014.2,261.2'); - expect(decodeResult.formatted.items.length).toBe(8); - expect(decodeResult.formatted.items[0].code).toBe('POS'); - expect(decodeResult.formatted.items[0].value).toBe('36.081 N, 94.810 W'); - expect(decodeResult.formatted.items[1].code).toBe('ALT'); - expect(decodeResult.formatted.items[1].value).toBe('35014 feet'); - expect(decodeResult.formatted.items[2].code).toBe('ORG'); - expect(decodeResult.formatted.items[2].value).toBe('ELP'); - expect(decodeResult.formatted.items[3].code).toBe('DST'); - expect(decodeResult.formatted.items[3].value).toBe('SDF'); - expect(decodeResult.formatted.items[4].code).toBe('ALT_DST'); - expect(decodeResult.formatted.items[4].value).toBe('SDF'); - expect(decodeResult.formatted.items[5].code).toBe('ARWY'); - expect(decodeResult.formatted.items[5].value).toBe('17R'); - expect(decodeResult.formatted.items[6].code).toBe('ALT_ARWY'); - expect(decodeResult.formatted.items[6].value).toBe('17L'); - expect(decodeResult.formatted.items[7].code).toBe('CHECKSUM'); - expect(decodeResult.formatted.items[7].value).toBe('0x421a'); -}); + test('decodes variant 2B', () => { + // https://app.airframes.io/messages/3421601874 + message.text = '285,B,69005074-507,10/12,+36.081,-094.810,35014,002.3,ELP,SDF,SDF,17R/,17L/,0,0,,,,,,0,0,0,0,1,,,,,247.0,014.2,261.2,421A'; + const decodeResult = plugin.decode(message); -test('decodes Label 4N variant 2C', () => { - const decoder = new MessageDecoder(); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.date).toBe('10/12'); + expect(decodeResult.remaining.text).toBe('B,69005074-507,002.3,0,0,0,0,0,0,1,247.0,014.2,261.2'); + expect(decodeResult.formatted.items.length).toBe(8); + expect(decodeResult.formatted.items[0].code).toBe('POS'); + expect(decodeResult.formatted.items[0].value).toBe('36.081 N, 94.810 W'); + expect(decodeResult.formatted.items[1].code).toBe('ALT'); + expect(decodeResult.formatted.items[1].value).toBe('35014 feet'); + expect(decodeResult.formatted.items[2].code).toBe('ORG'); + expect(decodeResult.formatted.items[2].value).toBe('ELP'); + expect(decodeResult.formatted.items[3].code).toBe('DST'); + expect(decodeResult.formatted.items[3].value).toBe('SDF'); + expect(decodeResult.formatted.items[4].code).toBe('ALT_DST'); + expect(decodeResult.formatted.items[4].value).toBe('SDF'); + expect(decodeResult.formatted.items[5].code).toBe('ARWY'); + expect(decodeResult.formatted.items[5].value).toBe('17R'); + expect(decodeResult.formatted.items[6].code).toBe('ALT_ARWY'); + expect(decodeResult.formatted.items[6].value).toBe('17L'); + expect(decodeResult.formatted.items[7].code).toBe('CHECKSUM'); + expect(decodeResult.formatted.items[7].value).toBe('0x421a'); + }); - // https://globe.adsbexchange.com/?icao=A3E08D&showTrace=2024-09-24×tamp=1727181643 - const text = '285,C,,09/24,,,,,EWR,PHL,PHL,09R/,/,0,0,,,,,,1,0,0,0,1,0,,0,0,198.5,014.5,213.0,9BCD'; - const decodeResult = decoder.decode({ label: "4N", text: text }); + test('decodes variant 2C', () => { + // https://globe.adsbexchange.com/?icao=A3E08D&showTrace=2024-09-24×tamp=1727181643 + message.text = '285,C,,09/24,,,,,EWR,PHL,PHL,09R/,/,0,0,,,,,,1,0,0,0,1,0,,0,0,198.5,014.5,213.0,9BCD'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.date).toBe('09/24'); - expect(decodeResult.remaining.text).toBe('C,0,0,1,0,0,0,1,0,0,0,198.5,014.5,213.0'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].value).toBe('EWR'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].value).toBe('PHL'); - expect(decodeResult.formatted.items[2].code).toBe('ALT_DST'); - expect(decodeResult.formatted.items[2].value).toBe('PHL'); - expect(decodeResult.formatted.items[3].code).toBe('ARWY'); - expect(decodeResult.formatted.items[3].value).toBe('09R'); - expect(decodeResult.formatted.items[4].code).toBe('CHECKSUM'); - expect(decodeResult.formatted.items[4].value).toBe('0x9bcd'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.date).toBe('09/24'); + expect(decodeResult.remaining.text).toBe('C,0,0,1,0,0,0,1,0,0,0,198.5,014.5,213.0'); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].value).toBe('EWR'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].value).toBe('PHL'); + expect(decodeResult.formatted.items[2].code).toBe('ALT_DST'); + expect(decodeResult.formatted.items[2].value).toBe('PHL'); + expect(decodeResult.formatted.items[3].code).toBe('ARWY'); + expect(decodeResult.formatted.items[3].value).toBe('09R'); + expect(decodeResult.formatted.items[4].code).toBe('CHECKSUM'); + expect(decodeResult.formatted.items[4].value).toBe('0x9bcd'); + }); -test('decodes Label 4N ', () => { - const decoder = new MessageDecoder(); + test('does not decode ', () => { + message.text = '4N Bogus message'; + const decodeResult = plugin.decode(message); - const text = '4N Bogus message'; - const decodeResult = decoder.decode({ label: "4N", text: text }); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.formatted.items.length).toBe(0); -}); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.formatted.items.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/lib/plugins/Label_83.test.ts b/lib/plugins/Label_83.test.ts index 2bbbe99..0edab2b 100644 --- a/lib/plugins/Label_83.test.ts +++ b/lib/plugins/Label_83.test.ts @@ -1,112 +1,108 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_83 } from './Label_83'; +describe('Label_83', () => { -test('matches Label 83 qualifiers', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); + let plugin: Label_83; + let message = { label: '83', text: '' }; - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-83'); -}); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_83(decoder); + }); + + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-83'); + }); -test('decodes Label 83 variant 1', () => { - const decoder = new MessageDecoder(); + test('decodes variant 1', () => { + // https://globe.adsbexchange.com/?icao=A2A3B5&showTrace=2024-09-22×tamp=1726967032 + message.text = 'KLAX,KEWR,220103, 40.53,- 74.47, 3836,212, 140.0, 19700'; + const decodeResult = plugin.decode(message); - // https://globe.adsbexchange.com/?icao=A2A3B5&showTrace=2024-09-22×tamp=1726967032 - const text = 'KLAX,KEWR,220103, 40.53,- 74.47, 3836,212, 140.0, 19700'; - const decodeResult = decoder.decode({ label: "83", text: text }); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.departure_icao).toBe('KLAX'); + expect(decodeResult.raw.arrival_icao).toBe('KEWR'); + expect(decodeResult.raw.day).toBe('22'); + expect(decodeResult.raw.position.latitude).toBe(40.53); + expect(decodeResult.raw.position.longitude).toBe(-74.47); + expect(decodeResult.raw.altitude).toBe(3836); + expect(decodeResult.raw.groundspeed).toBe(212); + expect(decodeResult.raw.heading).toBe(140); + expect(decodeResult.remaining.text).toBe('19700'); + expect(decodeResult.formatted.items.length).toBe(6); + expect(decodeResult.formatted.items[0].type).toBe('icao'); + expect(decodeResult.formatted.items[0].value).toBe('KLAX'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].value).toBe('KEWR'); + expect(decodeResult.formatted.items[2].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[2].value).toBe('40.530 N, 74.470 W'); + expect(decodeResult.formatted.items[3].type).toBe('altitude'); + expect(decodeResult.formatted.items[3].value).toBe('3836 feet'); + expect(decodeResult.formatted.items[4].type).toBe('aircraft_groundspeed'); + expect(decodeResult.formatted.items[4].value).toBe('212 knots'); + expect(decodeResult.formatted.items[5].type).toBe('heading'); + expect(decodeResult.formatted.items[5].value).toBe('140'); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.departure_icao).toBe('KLAX'); - expect(decodeResult.raw.arrival_icao).toBe('KEWR'); - expect(decodeResult.raw.day).toBe('22'); - expect(decodeResult.raw.position.latitude).toBe(40.53); - expect(decodeResult.raw.position.longitude).toBe(-74.47); - expect(decodeResult.raw.altitude).toBe(3836); - expect(decodeResult.raw.groundspeed).toBe(212); - expect(decodeResult.raw.heading).toBe(140); - expect(decodeResult.remaining.text).toBe('19700'); - expect(decodeResult.formatted.items.length).toBe(6); - expect(decodeResult.formatted.items[0].type).toBe('icao'); - expect(decodeResult.formatted.items[0].value).toBe('KLAX'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].value).toBe('KEWR'); - expect(decodeResult.formatted.items[2].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[2].value).toBe('40.530 N, 74.470 W'); - expect(decodeResult.formatted.items[3].type).toBe('altitude'); - expect(decodeResult.formatted.items[3].value).toBe('3836 feet'); - expect(decodeResult.formatted.items[4].type).toBe('aircraft_groundspeed'); - expect(decodeResult.formatted.items[4].value).toBe('212 knots'); - expect(decodeResult.formatted.items[5].type).toBe('heading'); - expect(decodeResult.formatted.items[5].value).toBe('140'); -}); + test('decodes variant 2', () => { + // https://globe.adsbexchange.com/?icao=478F43&showTrace=2024-09-22×tamp=1727022863 + message.text = '4DH3 ETAT2 0907/22 ENGM/KEWR .LN-RKO\r\n/ETA 1641'; + const decodeResult = plugin.decode(message); -test('decodes Label 83 variant 2', () => { - const decoder = new MessageDecoder(); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.departure_icao).toBe('ENGM'); + expect(decodeResult.raw.arrival_icao).toBe('KEWR'); + expect(decodeResult.raw.day).toBe('22'); + expect(decodeResult.raw.tail).toBe('LN-RKO'); + expect(decodeResult.raw.eta_time).toBe(60060); + expect(decodeResult.remaining.text).toBe('0907'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].value).toBe('ENGM'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].value).toBe('KEWR'); + expect(decodeResult.formatted.items[2].code).toBe('TAIL'); + expect(decodeResult.formatted.items[2].value).toBe('LN-RKO'); + expect(decodeResult.formatted.items[3].code).toBe('ETA'); + expect(decodeResult.formatted.items[3].value).toBe('16:41:00'); + }); - // https://globe.adsbexchange.com/?icao=478F43&showTrace=2024-09-22×tamp=1727022863 - const text = '4DH3 ETAT2 0907/22 ENGM/KEWR .LN-RKO\r\n/ETA 1641'; - const decodeResult = decoder.decode({ label: "83", text: text }); + test('decodes variant 3', () => { + // https://globe.adsbexchange.com/?icao=AC15A1&showTrace=2024-09-22×tamp=1726977342 + message.text = '001PR22035539N4038.6W07427.80292500008'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.departure_icao).toBe('ENGM'); - expect(decodeResult.raw.arrival_icao).toBe('KEWR'); - expect(decodeResult.raw.day).toBe('22'); - expect(decodeResult.raw.tail).toBe('LN-RKO'); - expect(decodeResult.raw.eta_time).toBe(60060); - expect(decodeResult.remaining.text).toBe('0907'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].value).toBe('ENGM'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].value).toBe('KEWR'); - expect(decodeResult.formatted.items[2].code).toBe('TAIL'); - expect(decodeResult.formatted.items[2].value).toBe('LN-RKO'); - expect(decodeResult.formatted.items[3].code).toBe('ETA'); - expect(decodeResult.formatted.items[3].value).toBe('16:41:00'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.day).toBe('22'); + expect(decodeResult.raw.position.latitude).toBe(40.64333333333333); + expect(decodeResult.raw.position.longitude).toBe(-74.46333333333334); + expect(decodeResult.raw.altitude).toBe(2925); + expect(decodeResult.remaining.text).toBe('00008'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[0].value).toBe('40.643 N, 74.463 W'); + expect(decodeResult.formatted.items[1].type).toBe('altitude'); + expect(decodeResult.formatted.items[1].value).toBe('2925 feet'); + }); -test('decodes Label 83 variant 3', () => { - const decoder = new MessageDecoder(); + test('does not decode ', () => { + message.text = '83 Bogus message'; + const decodeResult = plugin.decode(message); - // https://globe.adsbexchange.com/?icao=AC15A1&showTrace=2024-09-22×tamp=1726977342 - const text = '001PR22035539N4038.6W07427.80292500008'; - const decodeResult = decoder.decode({ label: "83", text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message.text).toBe(text); - expect(decodeResult.raw.day).toBe('22'); - expect(decodeResult.raw.position.latitude).toBe(40.64333333333333); - expect(decodeResult.raw.position.longitude).toBe(-74.46333333333334); - expect(decodeResult.raw.altitude).toBe(2925); - expect(decodeResult.remaining.text).toBe('00008'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[0].value).toBe('40.643 N, 74.463 W'); - expect(decodeResult.formatted.items[1].type).toBe('altitude'); - expect(decodeResult.formatted.items[1].value).toBe('2925 feet'); -}); - -test('decodes Label 83 ', () => { - const decoder = new MessageDecoder(); - - const text = '83 Bogus message'; - const decodeResult = decoder.decode({ label: "83", text: text }); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.formatted.items.length).toBe(0); -}); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.formatted.items.length).toBe(0); + }); +}); \ No newline at end of file From de30f33a1feb22bf94dc0e52d8ac6fcfb971be9e Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Thu, 5 Feb 2026 19:25:53 -0500 Subject: [PATCH 6/9] PR feedback --- lib/DecoderPluginInterface.ts | 2 +- lib/plugins/Label_4A.test.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/DecoderPluginInterface.ts b/lib/DecoderPluginInterface.ts index 43a3438..895da95 100644 --- a/lib/DecoderPluginInterface.ts +++ b/lib/DecoderPluginInterface.ts @@ -2,7 +2,7 @@ * Representation of a Message */ export interface Message { - label: string, + label?: string, sublabel?: string, text: string, } diff --git a/lib/plugins/Label_4A.test.ts b/lib/plugins/Label_4A.test.ts index 422caa8..5fab4d5 100644 --- a/lib/plugins/Label_4A.test.ts +++ b/lib/plugins/Label_4A.test.ts @@ -106,15 +106,14 @@ describe('Label 4A', () => { expect(decodeResult.formatted.items[3].value).toBe('41.093 N, 72.677 W'); }); - test('decodes Label 4A_DIS ', () => { // FIXME: this should not decode + test('decodes Label ', () => { // https://app.airframes.io/messages/3449413366 message.text = 'DIS01,182103,WEN3100,WRONG CREW HAHAHA'; const decodeResult = plugin.decode(message); - // expect(decodeResult.decoded).toBe(false); - // expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).not.toBe('label-4a'); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); // expect(decodeResult.formatted.description).toBe('Latest New Format'); - // expect(decodeResult.formatted.items.length).toBe(0); + expect(decodeResult.formatted.items.length).toBe(0); }); }); \ No newline at end of file From ae9655e442545d16e0a55db51c420ec833ca45b2 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sat, 7 Feb 2026 08:40:09 -0500 Subject: [PATCH 7/9] lint --- lib/MessageDecoder.test.ts | 10 +-- lib/MessageDecoder.ts | 6 +- lib/plugins/CBand.test.ts | 151 ++++++++++++++++++----------------- lib/plugins/CBand.ts | 40 ++++++---- lib/plugins/Label_4A.test.ts | 2 +- lib/plugins/Label_4A.ts | 30 +------ lib/plugins/Label_4N.test.ts | 1 - lib/plugins/Label_4N.ts | 5 +- lib/plugins/Label_83.test.ts | 34 -------- lib/plugins/Label_83.ts | 29 +++---- 10 files changed, 129 insertions(+), 179 deletions(-) diff --git a/lib/MessageDecoder.test.ts b/lib/MessageDecoder.test.ts index 0350b87..a679ed3 100644 --- a/lib/MessageDecoder.test.ts +++ b/lib/MessageDecoder.test.ts @@ -29,21 +29,21 @@ describe('MessageDecoder', () => { test('C-band core seamless decode', () => { const message = { label: '4N', - text: 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3' + text: 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3', }; const decodeResult = decoder.decode(message); expect(decodeResult.decoded).toBe(true); - if(!decodeResult.message) { + if (!decodeResult.message) { expect(decodeResult.message).toBeDefined(); - return; + return; } expect(decodeResult.message.label).toBe('4N'); expect(decodeResult.message.sublabel).toBeUndefined(); expect(decodeResult.message.text).toContain('M85AUP0109285'); expect(decodeResult.formatted.items.length).toBe(7); - }) + }); test('Handles Multiple decodes', () => { const message = { @@ -52,7 +52,7 @@ describe('MessageDecoder', () => { }; decoder.decode(message); - + const decodeResult = decoder.decode(message); if (!decodeResult.message) { expect(decodeResult.message).toBeDefined(); diff --git a/lib/MessageDecoder.ts b/lib/MessageDecoder.ts index 2306ef6..0d65f9c 100644 --- a/lib/MessageDecoder.ts +++ b/lib/MessageDecoder.ts @@ -93,8 +93,10 @@ export class MessageDecoder { const usablePlugins = this.plugins.filter((plugin) => { const qualifiers = plugin.qualifiers(); - if ((qualifiers.labels.includes(message.label)) - || (qualifiers.labels.length === 1 && qualifiers.labels[0] === '*')) { + if ( + qualifiers.labels.includes(message.label) || + (qualifiers.labels.length === 1 && qualifiers.labels[0] === '*') + ) { if (qualifiers.preambles && qualifiers.preambles.length > 0) { const matching = qualifiers.preambles.filter((preamble: string) => { // console.log(message.text.substring(0, preamble.length)); diff --git a/lib/plugins/CBand.test.ts b/lib/plugins/CBand.test.ts index 701e7c1..16998bf 100644 --- a/lib/plugins/CBand.test.ts +++ b/lib/plugins/CBand.test.ts @@ -2,7 +2,6 @@ import { MessageDecoder } from '../MessageDecoder'; import { CBand } from './CBand'; describe('CBand', () => { - let plugin: CBand; beforeEach(() => { @@ -19,85 +18,89 @@ describe('CBand', () => { }); }); -test('decodes Label 4A', () => { - // https://app.airframes.io/messages/3461407615 - const text = 'M60ALH0752N22456E077014OSE35 ,192027370VEX36 ,192316,M46,275043309,85220111'; - const decodeResult = plugin.decode({ label: "4A", text: text }); + test('decodes Label 4A', () => { + // https://app.airframes.io/messages/3461407615 + const text = + 'M60ALH0752N22456E077014OSE35 ,192027370VEX36 ,192316,M46,275043309,85220111'; + const decodeResult = plugin.decode({ label: '4A', text: text }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('c-band-label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.raw.flight_number).toBe('LH752'); - expect(decodeResult.raw.position.latitude).toBe(22.456); - expect(decodeResult.raw.position.longitude).toBe(77.014); - expect(decodeResult.raw.route.waypoints.length).toBe(2); - expect(decodeResult.raw.route.waypoints[0].name).toBe('OSE35'); - expect(decodeResult.raw.route.waypoints[1].name).toBe('VEX36'); - expect(decodeResult.raw.outside_air_temperature).toBe(-46); - expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('c-band-label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.raw.flight_number).toBe('LH752'); + expect(decodeResult.raw.position.latitude).toBe(22.456); + expect(decodeResult.raw.position.longitude).toBe(77.014); + expect(decodeResult.raw.route.waypoints.length).toBe(2); + expect(decodeResult.raw.route.waypoints[0].name).toBe('OSE35'); + expect(decodeResult.raw.route.waypoints[1].name).toBe('VEX36'); + expect(decodeResult.raw.outside_air_temperature).toBe(-46); + expect(decodeResult.formatted.items.length).toBe(5); expect(decodeResult.remaining.text).toBe('275043309,85220111'); -}); - -test('decodes Label 4N variant 2C (C-band)', () => { - // https://app.airframes.io/messages/3422221702 - const text = 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3'; - const decodeResult = plugin.decode({ label: "4N", text: text }); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('c-band-label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.raw.flight_number).toBe('UP109'); - expect(decodeResult.raw.date).toBe('10/12'); - expect(decodeResult.raw.departure_icao).toBe('NRT'); - expect(decodeResult.raw.arrival_icao).toBe('ANC'); - expect(decodeResult.raw.alternate_icao).toBe('ANC'); - expect(decodeResult.raw.arrival_runway).toBe('07R'); - expect(decodeResult.raw.alternate_runway).toBe('33'); - expect(decodeResult.raw.checksum).toBe(30195); - expect(decodeResult.remaining.text).toBe('C,0,0,0,0,0,0,1,0,0,0,709.8,048.7,758.5'); - expect(decodeResult.formatted.items.length).toBe(7); -}); + test('decodes Label 4N variant 2C (C-band)', () => { + // https://app.airframes.io/messages/3422221702 + const text = + 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3'; + const decodeResult = plugin.decode({ label: '4N', text: text }); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('c-band-label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.flight_number).toBe('UP109'); + expect(decodeResult.raw.date).toBe('10/12'); + expect(decodeResult.raw.departure_icao).toBe('NRT'); + expect(decodeResult.raw.arrival_icao).toBe('ANC'); + expect(decodeResult.raw.alternate_icao).toBe('ANC'); + expect(decodeResult.raw.arrival_runway).toBe('07R'); + expect(decodeResult.raw.alternate_runway).toBe('33'); + expect(decodeResult.raw.checksum).toBe(30195); + expect(decodeResult.remaining.text).toBe( + 'C,0,0,0,0,0,0,1,0,0,0,709.8,048.7,758.5', + ); + expect(decodeResult.formatted.items.length).toBe(7); + }); -test('decodes Label 83 variant 1 (C-band)', () => { - // https://app.airframes.io/messages/3413113024 - const text = 'M05AUA0007KIAH,RJAA,110012, 39.12,-175.10,39001,265,-107.6, 64900'; - const decodeResult = plugin.decode({ label: "83", text: text }); + test('decodes Label 83 variant 1 (C-band)', () => { + // https://app.airframes.io/messages/3413113024 + const text = + 'M05AUA0007KIAH,RJAA,110012, 39.12,-175.10,39001,265,-107.6, 64900'; + const decodeResult = plugin.decode({ label: '83', text: text }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('c-band-label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.raw.flight_number).toBe('UA7'); - expect(decodeResult.raw.departure_icao).toBe('KIAH'); - expect(decodeResult.raw.arrival_icao).toBe('RJAA'); - expect(decodeResult.raw.day).toBe('11'); - expect(decodeResult.raw.position.latitude).toBe(39.12); - expect(decodeResult.raw.position.longitude).toBe(-175.1); - expect(decodeResult.raw.altitude).toBe(39001); - expect(decodeResult.raw.groundspeed).toBe(265); - expect(decodeResult.raw.heading).toBe(-107.6); - expect(decodeResult.remaining.text).toBe('64900'); - expect(decodeResult.formatted.items.length).toBe(7); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('c-band-label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.flight_number).toBe('UA7'); + expect(decodeResult.raw.departure_icao).toBe('KIAH'); + expect(decodeResult.raw.arrival_icao).toBe('RJAA'); + expect(decodeResult.raw.day).toBe('11'); + expect(decodeResult.raw.position.latitude).toBe(39.12); + expect(decodeResult.raw.position.longitude).toBe(-175.1); + expect(decodeResult.raw.altitude).toBe(39001); + expect(decodeResult.raw.groundspeed).toBe(265); + expect(decodeResult.raw.heading).toBe(-107.6); + expect(decodeResult.remaining.text).toBe('64900'); + expect(decodeResult.formatted.items.length).toBe(7); + }); -test('decodes Label 83 variant 3 (C-band)', () => { - // https://app.airframes.io/messages/3413346742 - const text = 'M09AXA0001001PR11013423N0556.6E11603.0000000----'; - const decodeResult = plugin.decode({ label: "83", text: text }); + test('decodes Label 83 variant 3 (C-band)', () => { + // https://app.airframes.io/messages/3413346742 + const text = 'M09AXA0001001PR11013423N0556.6E11603.0000000----'; + const decodeResult = plugin.decode({ label: '83', text: text }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('c-band-label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.raw.flight_number).toBe('XA1'); - expect(decodeResult.raw.day).toBe('11'); - expect(decodeResult.raw.position.latitude).toBe(5.943333333333333); - expect(decodeResult.raw.position.longitude).toBe(116.05); - expect(decodeResult.raw.altitude).toBe(0); - expect(decodeResult.remaining.text).toBe('0----'); - expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('c-band-label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.raw.flight_number).toBe('XA1'); + expect(decodeResult.raw.day).toBe('11'); + expect(decodeResult.raw.position.latitude).toBe(5.943333333333333); + expect(decodeResult.raw.position.longitude).toBe(116.05); + expect(decodeResult.raw.altitude).toBe(0); + expect(decodeResult.remaining.text).toBe('0----'); + expect(decodeResult.formatted.items.length).toBe(3); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/CBand.ts b/lib/plugins/CBand.ts index fe9e509..8e2ec9c 100644 --- a/lib/plugins/CBand.ts +++ b/lib/plugins/CBand.ts @@ -5,13 +5,13 @@ import { ResultFormatter } from '../utils/result_formatter'; export class CBand extends DecoderPlugin { name = 'c-band'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['*'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; @@ -19,24 +19,32 @@ export class CBand extends DecoderPlugin { // C-Band puts a 10 char header in front of some message types // First 4 chars are some kind of message number // Last 6 chars are the flight number - let cband = message.text.match(/^(?[A-Z]\d{2}[A-Z])(?[A-Z0-9]{2})(?[0-9]{4})/); + let cband = message.text.match( + /^(?[A-Z]\d{2}[A-Z])(?[A-Z0-9]{2})(?[0-9]{4})/, + ); if (cband?.groups) { - const messageText = message.text.substring(10); - const decoded = this.decoder.decode({ + const messageText = message.text.substring(10); + const decoded = this.decoder.decode( + { label: message.label, sublabel: message.sublabel, text: messageText, - }, options); - if(decoded.decoded) { - ResultFormatter.flightNumber(decodeResult, cband.groups.airline + Number(cband.groups.number)); - decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = decoded.decoder.decodeLevel; - decodeResult.decoder.name = this.name + '-' + decoded.decoder.name; - decodeResult.raw = {...decodeResult.raw, ...decoded.raw }; - decodeResult.formatted.description = decoded.formatted.description; - decodeResult.formatted.items.push(...decoded.formatted.items); - decodeResult.remaining = decoded.remaining; - } + }, + options, + ); + if (decoded.decoded) { + ResultFormatter.flightNumber( + decodeResult, + cband.groups.airline + Number(cband.groups.number), + ); + decodeResult.decoded = true; + decodeResult.decoder.decodeLevel = decoded.decoder.decodeLevel; + decodeResult.decoder.name = this.name + '-' + decoded.decoder.name; + decodeResult.raw = { ...decodeResult.raw, ...decoded.raw }; + decodeResult.formatted.description = decoded.formatted.description; + decodeResult.formatted.items.push(...decoded.formatted.items); + decodeResult.remaining = decoded.remaining; + } } return decodeResult; } diff --git a/lib/plugins/Label_4A.test.ts b/lib/plugins/Label_4A.test.ts index c2c68b2..9d8abd7 100644 --- a/lib/plugins/Label_4A.test.ts +++ b/lib/plugins/Label_4A.test.ts @@ -123,4 +123,4 @@ describe('Label 4A', () => { expect(decodeResult.formatted.description).toBe('Latest New Format'); expect(decodeResult.formatted.items.length).toBe(0); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_4A.ts b/lib/plugins/Label_4A.ts index 1f0c0c9..cd473d5 100644 --- a/lib/plugins/Label_4A.ts +++ b/lib/plugins/Label_4A.ts @@ -20,32 +20,8 @@ export class Label_4A extends DecoderPlugin { decodeResult.message = message; decodeResult.formatted.description = 'Latest New Format'; -<<<<<<< HEAD decodeResult.decoded = true; - const fields = message.text.split(","); - if (fields.length === 11) { - // variant 1 - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0])); - ResultFormatter.tail(decodeResult, fields[2].replace(".", "")); - if (fields[3]) ResultFormatter.callsign(decodeResult, fields[3]); - ResultFormatter.departureAirport(decodeResult, fields[4]); - ResultFormatter.arrivalAirport(decodeResult, fields[5]); - // ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100); - ResultFormatter.unknownArr(decodeResult, fields.slice(8)); -======= - // Inmarsat C-band seems to prefix normal messages with a message number and flight number - let text = message.text; - if (text.match(/^M\d{2}A\w{6}/)) { - ResultFormatter.flightNumber( - decodeResult, - message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, '$1'), - ); - text = text.substring(10); - } - - decodeResult.decoded = true; - const fields = text.split(','); - const fields = message.text.split(","); + const fields = message.text.split(','); if (fields.length === 11) { // variant 1 ResultFormatter.time_of_day( @@ -102,8 +78,8 @@ export class Label_4A extends DecoderPlugin { ); } } else { - decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, message.text); + decodeResult.decoded = false; + ResultFormatter.unknown(decodeResult, message.text); } if (decodeResult.decoded) { diff --git a/lib/plugins/Label_4N.test.ts b/lib/plugins/Label_4N.test.ts index 336d382..ca3f442 100644 --- a/lib/plugins/Label_4N.test.ts +++ b/lib/plugins/Label_4N.test.ts @@ -103,7 +103,6 @@ describe('Label 4N', () => { expect(decodeResult.formatted.items[4].value).toBe('0x9bcd'); }); - test('decodes Label 4N ', () => { message.text = '4N Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_4N.ts b/lib/plugins/Label_4N.ts index b02201b..4e9d6fe 100644 --- a/lib/plugins/Label_4N.ts +++ b/lib/plugins/Label_4N.ts @@ -23,7 +23,10 @@ export class Label_4N extends DecoderPlugin { if (message.text.length === 51) { // variant 1 decodeResult.raw.day = message.text.substring(0, 2); - ResultFormatter.departureAirport(decodeResult, message.text.substring(8, 11)); + ResultFormatter.departureAirport( + decodeResult, + message.text.substring(8, 11), + ); ResultFormatter.arrivalAirport( decodeResult, message.text.substring(13, 16), diff --git a/lib/plugins/Label_83.test.ts b/lib/plugins/Label_83.test.ts index 40687f4..2711336 100644 --- a/lib/plugins/Label_83.test.ts +++ b/lib/plugins/Label_83.test.ts @@ -1,10 +1,5 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_83 } from './Label_83'; -describe('Label_83', () => { - - let plugin: Label_83; - let message = { label: '83', text: '' }; - describe('Label 83', () => { let plugin: Label_83; const message = { label: '83', text: '' }; @@ -53,7 +48,6 @@ describe('Label 83', () => { expect(decodeResult.formatted.items[5].value).toBe('140'); }); - test('decodes Label 83 variant 2', () => { // https://globe.adsbexchange.com/?icao=478F43&showTrace=2024-09-22×tamp=1727022863 message.text = '4DH3 ETAT2 0907/22 ENGM/KEWR .LN-RKO\r\n/ETA 1641'; @@ -106,34 +100,6 @@ describe('Label 83', () => { expect(decodeResult.formatted.items[1].value).toBe('2925 feet'); }); - test('decodes Label 83 variant 3 (C-band)', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); - - // https://app.airframes.io/messages/3413346742 - message.text = 'M09AXA0001001PR11013423N0556.6E11603.0000000----'; - const decodeResult = decoderPlugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.flight_number).toBe('XA1'); - expect(decodeResult.raw.day).toBe('11'); - expect(decodeResult.raw.position.latitude).toBe(5.943333333333333); - expect(decodeResult.raw.position.longitude).toBe(116.05); - expect(decodeResult.raw.altitude).toBe(0); - expect(decodeResult.remaining.text).toBe('0----'); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].type).toBe('flight_number'); - expect(decodeResult.formatted.items[0].value).toBe('XA1'); - expect(decodeResult.formatted.items[1].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[1].value).toBe('5.943 N, 116.050 E'); - expect(decodeResult.formatted.items[2].type).toBe('altitude'); - expect(decodeResult.formatted.items[2].value).toBe('0 feet'); - }); - test('decodes Label 83 ', () => { message.text = '83 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_83.ts b/lib/plugins/Label_83.ts index eb65c33..9af4131 100644 --- a/lib/plugins/Label_83.ts +++ b/lib/plugins/Label_83.ts @@ -19,20 +19,10 @@ export class Label_83 extends DecoderPlugin { decodeResult.message = message; decodeResult.formatted.description = 'Airline Defined'; - // Inmarsat C-band seems to prefix normal messages with a message number and flight number - let text = message.text; - if (text.match(/^M\d{2}A\w{6}/)) { - ResultFormatter.flightNumber( - decodeResult, - message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, '$1'), - ); - text = text.substring(10); - } - decodeResult.decoded = true; - if (text.substring(0, 10) === '4DH3 ETAT2') { + if (message.text.substring(0, 10) === '4DH3 ETAT2') { // variant 2 - const fields = text.split(/\s+/); + const fields = message.text.split(/\s+/); if (fields[2].length > 5) { decodeResult.raw.day = fields[2].substring(5); } @@ -45,19 +35,22 @@ export class Label_83 extends DecoderPlugin { decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + '00'), ); - } else if (text.substring(0, 5) === '001PR') { + } else if (message.text.substring(0, 5) === '001PR') { // variant 3 - decodeResult.raw.day = text.substring(5, 7); + decodeResult.raw.day = message.text.substring(5, 7); const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes( - text.substring(13, 28).replace(/\./g, ''), + message.text.substring(13, 28).replace(/\./g, ''), ); if (position) { ResultFormatter.position(decodeResult, position); } - ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33))); - ResultFormatter.unknown(decodeResult, text.substring(33)); + ResultFormatter.altitude( + decodeResult, + Number(message.text.substring(28, 33)), + ); + ResultFormatter.unknown(decodeResult, message.text.substring(33)); } else { - const fields = text.replace(/\s/g, '').split(','); + const fields = message.text.replace(/\s/g, '').split(','); if (fields.length === 9) { // variant 1 ResultFormatter.departureAirport(decodeResult, fields[0]); From a448c33a2b6386a4b5465171166d89b5420bc403 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sat, 7 Feb 2026 08:43:07 -0500 Subject: [PATCH 8/9] fix test --- lib/plugins/Label_4N.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/plugins/Label_4N.ts b/lib/plugins/Label_4N.ts index 4e9d6fe..3727147 100644 --- a/lib/plugins/Label_4N.ts +++ b/lib/plugins/Label_4N.ts @@ -19,18 +19,13 @@ export class Label_4N extends DecoderPlugin { decodeResult.formatted.description = 'Airline Defined'; decodeResult.decoded = true; - const fields = message.text.split(','); - if (message.text.length === 51) { + const text = message.text; + const fields = text.split(','); + if (text.length === 51) { // variant 1 - decodeResult.raw.day = message.text.substring(0, 2); - ResultFormatter.departureAirport( - decodeResult, - message.text.substring(8, 11), - ); - ResultFormatter.arrivalAirport( - decodeResult, - message.text.substring(13, 16), - ); + decodeResult.raw.day = text.substring(0, 2); + ResultFormatter.departureAirport(decodeResult, text.substring(8, 11)); + ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16)); ResultFormatter.position( decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes( @@ -72,7 +67,7 @@ export class Label_4N extends DecoderPlugin { ); } else { decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, message.text); + ResultFormatter.unknown(decodeResult, text); } if (decodeResult.decoded) { From 9bdea5fdf5ee8c71503e8ae47dc6041bc7f5d683 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sat, 7 Feb 2026 08:44:40 -0500 Subject: [PATCH 9/9] cleanup diff --- lib/plugins/Label_83.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/plugins/Label_83.ts b/lib/plugins/Label_83.ts index 9af4131..136b553 100644 --- a/lib/plugins/Label_83.ts +++ b/lib/plugins/Label_83.ts @@ -20,9 +20,10 @@ export class Label_83 extends DecoderPlugin { decodeResult.formatted.description = 'Airline Defined'; decodeResult.decoded = true; - if (message.text.substring(0, 10) === '4DH3 ETAT2') { + const text = message.text; + if (text.substring(0, 10) === '4DH3 ETAT2') { // variant 2 - const fields = message.text.split(/\s+/); + const fields = text.split(/\s+/); if (fields[2].length > 5) { decodeResult.raw.day = fields[2].substring(5); } @@ -35,22 +36,19 @@ export class Label_83 extends DecoderPlugin { decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + '00'), ); - } else if (message.text.substring(0, 5) === '001PR') { + } else if (text.substring(0, 5) === '001PR') { // variant 3 - decodeResult.raw.day = message.text.substring(5, 7); + decodeResult.raw.day = text.substring(5, 7); const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes( - message.text.substring(13, 28).replace(/\./g, ''), + text.substring(13, 28).replace(/\./g, ''), ); if (position) { ResultFormatter.position(decodeResult, position); } - ResultFormatter.altitude( - decodeResult, - Number(message.text.substring(28, 33)), - ); - ResultFormatter.unknown(decodeResult, message.text.substring(33)); + ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33))); + ResultFormatter.unknown(decodeResult, text.substring(33)); } else { - const fields = message.text.replace(/\s/g, '').split(','); + const fields = text.replace(/\s/g, '').split(','); if (fields.length === 9) { // variant 1 ResultFormatter.departureAirport(decodeResult, fields[0]); @@ -67,7 +65,7 @@ export class Label_83 extends DecoderPlugin { ResultFormatter.unknown(decodeResult, fields[8]); } else { decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, message.text); + ResultFormatter.unknown(decodeResult, text); } }