diff --git a/lib/plugins/Label_H1_FPN.test.ts b/lib/plugins/Label_H1_FPN.test.ts index 8860212..0c42de8 100644 --- a/lib/plugins/Label_H1_FPN.test.ts +++ b/lib/plugins/Label_H1_FPN.test.ts @@ -183,7 +183,7 @@ test('decodes Label H1 Preamble FPN with SN and TS', () => { expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); expect(decodeResult.decoder.name).toBe('label-h1-fpn'); - expect(decodeResult.raw.message_timestamp).toBe(Number.NaN); // DDMMYY instead of MMDDYY - need to figure out how to determine + expect(decodeResult.raw.message_timestamp).toBe(1708185391); expect(decodeResult.raw.serial_number).toBe('155631'); expect(decodeResult.formatted.description).toBe('Flight Plan'); expect(decodeResult.formatted.items.length).toBe(6); @@ -247,6 +247,7 @@ test('decodes Label H1 #M1BFPN No Preamble', () => { expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); // should be partial expect(decodeResult.decoder.name).toBe('label-h1-fpn'); + expect(decodeResult.raw.flight_number).toBe('AKL0767'); expect(decodeResult.raw.message_timestamp).toBe(1708730408); expect(decodeResult.formatted.description).toBe('Flight Plan'); expect(decodeResult.formatted.items.length).toBe(7); diff --git a/lib/plugins/Label_H1_POS.test.ts b/lib/plugins/Label_H1_POS.test.ts index 81df1a7..6bb3abc 100644 --- a/lib/plugins/Label_H1_POS.test.ts +++ b/lib/plugins/Label_H1_POS.test.ts @@ -10,7 +10,7 @@ test('matches Label H1 Preamble POS qualifiers', () => { expect(decoderPlugin.qualifiers).toBeDefined(); expect(decoderPlugin.qualifiers()).toEqual({ labels: ['H1'], - preambles: ['POS', '#M1BPOS'], + preambles: ['POS', '#M1BPOS', '/.POS'], }); }); test('decodes Label H1 Preamble POS variant 1', () => { @@ -293,7 +293,7 @@ test('decodes Label H1 Preamble #M1BPOS long variant', () => { expect(decodeResult.formatted.items.length).toBe(10); expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[0].value).toBe('29.510 N, 98.448 W'); - expect(decodeResult.formatted.items[1].label).toBe('Departure Runway'); + expect(decodeResult.formatted.items[1].label).toBe('Arrival Runway'); expect(decodeResult.formatted.items[1].value).toBe('04'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Groundspeed'); expect(decodeResult.formatted.items[2].value).toBe('415'); @@ -314,7 +314,7 @@ test('decodes Label H1 Preamble #M1BPOS long variant', () => { expect(decodeResult.remaining.text).toBe(',188,4,M12,246048,374K,282K,1223,133,,70,151437,73/PR1223,222,133,,44,40,252074,M22,180,P0'); }); -test('decodes Label H1 Preamble POS variant 6', () => { +test('decodes Label H1 Preamble POS variant 7', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); @@ -338,10 +338,10 @@ test('decodes Label H1 Preamble POS variant 6', () => { expect(decodeResult.formatted.items[3].value).toBe('-28'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x9071'); - expect(decodeResult.remaining.text).toBe(',/ID91459S,BANKR31,142813/MR64,0,/ET31539,27619,MT370/CG311,160,350/FB732/VR32'); + expect(decodeResult.remaining.text).toBe('/ID91459S,BANKR31,142813/MR64,0,/ET31539,27619,MT370/CG311,160,350/FB732/VR32'); }); -test('decodes Label H1 Preamble #M1BPOS variant 6', () => { +test('decodes Label H1 Preamble #M1BPOS variant 7', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); @@ -354,6 +354,7 @@ test('decodes Label H1 Preamble #M1BPOS variant 6', () => { expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.decoder.name).toBe('label-h1-pos'); expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.raw.flight_number).toBe('AMCLL93'); expect(decodeResult.formatted.items.length).toBe(5); expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[0].value).toBe('42.579 N, 108.090 W'); @@ -365,7 +366,75 @@ test('decodes Label H1 Preamble #M1BPOS variant 6', () => { expect(decodeResult.formatted.items[3].value).toBe('-49'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x4e17'); - expect(decodeResult.remaining.text).toBe(',F37AMCLL93/ID746026,,173207/MR1,,/ET031846,267070,T468/CG264,110,360/FB742/VR32'); + expect(decodeResult.remaining.text).toBe('F37#M1B/ID746026,,173207/MR1,,/ET031846,267070,T468/CG264,110,360/FB742/VR32'); +}); + +test('decodes Label H1 Preamble POS variant 8', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_H1_POS(decoder); + + // https://app.airframes.io/messages/2500335076 + const text = 'POS/TS080616,210324/DTMMGL,29O,64,103316/PR1754,231,350,189,,0,0,,M45,185,,,P16,P0,36000,,1565,250/RP:DA:MMTJ:AA:MMGL:R:27O:D:TUMA2B..SANFE.UT4..LMM:A:LONV1D:AP:ILSZ29.PLADE(29O)9D1C'; + const decodeResult = decoderPlugin.decode({ text: text }); + console.log(JSON.stringify(decodeResult, null, 2)); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-pos'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.formatted.items.length).toBe(11); + expect(decodeResult.formatted.items[0].label).toBe('Destination'); + expect(decodeResult.formatted.items[0].value).toBe('MMGL'); + expect(decodeResult.formatted.items[1].label).toBe('Arrival Runway'); + expect(decodeResult.formatted.items[1].value).toBe('29O'); + expect(decodeResult.formatted.items[2].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe('-45'); + expect(decodeResult.formatted.items[3].label).toBe('Route Status'); + expect(decodeResult.formatted.items[3].value).toBe('Route Planned'); + expect(decodeResult.formatted.items[4].label).toBe('Origin'); + expect(decodeResult.formatted.items[4].value).toBe('MMTJ'); + expect(decodeResult.formatted.items[5].label).toBe('Destination'); + expect(decodeResult.formatted.items[5].value).toBe('MMGL'); + expect(decodeResult.formatted.items[6].label).toBe('Departure Runway'); + expect(decodeResult.formatted.items[6].value).toBe('27O'); + expect(decodeResult.formatted.items[7].label).toBe('Departure Procedure'); + expect(decodeResult.formatted.items[7].value).toBe('TUMA2B: >> SANFE > UT4 >> LMM'); + expect(decodeResult.formatted.items[8].label).toBe('Arrival Procedure'); + expect(decodeResult.formatted.items[8].value).toBe('LONV1D'); + expect(decodeResult.formatted.items[9].label).toBe('Approach Procedure'); + expect(decodeResult.formatted.items[9].value).toBe('ILSZ29 starting at PLADE(29O)'); + expect(decodeResult.formatted.items[10].label).toBe('Message Checksum'); + expect(decodeResult.formatted.items[10].value).toBe('0x9d1c'); + expect(decodeResult.remaining.text).toBe(',64,103316/PR1754,231,350,189,,0,0,,185,,,P16,P0,36000,,1565'); +}); +test('decodes Label H1 Preamble /.POS variant 2', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_H1_POS(decoder); + + // https://app.airframes.io/messages/2500488708 + const text = '/.POS/TS100316,210324/PSS35333W058220,,100316,250,S37131W059150,101916,S39387W060377,M23,27282,241,780,MANUAL,0,813E711'; + const decodeResult = decoderPlugin.decode({ text: text }); + console.log(JSON.stringify(decodeResult, null, 2)); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-pos'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.raw.message_timestamp).toBe(1711015396); + expect(decodeResult.formatted.items.length).toBe(6); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('35.333 S, 58.220 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('25000 feet'); + expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); + expect(decodeResult.formatted.items[2].value).toBe('@10:03:16 > (37.131 S, 59.150 W)@10:19:16 > (39.387 S, 60.377 W)'); + expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[3].value).toBe('-23'); + expect(decodeResult.formatted.items[4].label).toBe('Aircraft Groundspeed'); + expect(decodeResult.formatted.items[4].value).toBe('780'); + expect(decodeResult.formatted.items[5].label).toBe('Message Checksum'); + expect(decodeResult.formatted.items[5].value).toBe('0xe711'); + expect(decodeResult.remaining.text).toBe('/.POS,27282,241,MANUAL,0,813'); }); test('decodes Label H1 Preamble #M1BPOS ', () => { diff --git a/lib/plugins/Label_H1_POS.ts b/lib/plugins/Label_H1_POS.ts index 4ba735c..bd4e451 100644 --- a/lib/plugins/Label_H1_POS.ts +++ b/lib/plugins/Label_H1_POS.ts @@ -12,7 +12,7 @@ export class Label_H1_POS extends DecoderPlugin { qualifiers() { // eslint-disable-line class-methods-use-this return { labels: ["H1"], - preambles: ['POS', '#M1BPOS'], //TODO - support data before # + preambles: ['POS', '#M1BPOS', '/.POS'], //TODO - support data before # }; } @@ -21,33 +21,27 @@ export class Label_H1_POS extends DecoderPlugin { decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; decodeResult.message = message; + decodeResult.remaining.text = ''; const checksum = message.text.slice(-4); + const header = findHeader(message.text.slice(0,-4)); + const decoded = FlightPlanUtils.processFlightPlan(decodeResult, header.split(':')); //strip POS and checksum - const parts = message.text.replace('#M1B', '').replace('POS', '').slice(0,-4).split(','); - if(parts.length==1 && parts[0].startsWith('/RF')) { - // TODO - use FlightPlanUtils to parse - decodeResult.raw.route_status == 'RF' - decodeResult.formatted.items.push({ - type: 'status', - code: 'ROUTE_STATUS', - label: 'Route Status', - value: 'Route Filed', - }); - decodeResult.raw.route = {waypoints: parts[0].substring(3,parts[0].length).split('.').map((leg: string) => RouteUtils.getWaypoint(leg))}; - decodeResult.formatted.items.push({ - type: 'aircraft_route', - code: 'ROUTE', - label: 'Aircraft Route', - value: RouteUtils.routeToString(decodeResult.raw.route), - }); - - processChecksum(decodeResult, checksum); - decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = 'full'; + const parts = message.text.replace(header, '').slice(0,-4).split(','); + if(parts.length==1) { + if(decoded) { + processChecksum(decodeResult, checksum); + decodeResult.decoded = true; + decodeResult.decoder.decodeLevel = 'full'; + } else if(decodeResult.remaining.text.length > 0) { + processChecksum(decodeResult, checksum); + decodeResult.decoded = true; + decodeResult.decoder.decodeLevel = 'partial'; + } else { + decodeResult.decoded = false; + decodeResult.decoder.decodeLevel = 'none'; + } } else if(parts.length === 10) { // variant 4 - decodeResult.remaining.text = '' - processPosition(decodeResult, parts[0]); processAlt(decodeResult, parts[3]); processRoute(decodeResult, parts[1], parts[2], parts[4], parts[5], parts[6]); processTemp(decodeResult, parts[7]); @@ -59,8 +53,6 @@ export class Label_H1_POS extends DecoderPlugin { decodeResult.decoder.decodeLevel = 'partial'; } else if(parts.length === 11) { // variant 1 - decodeResult.remaining.text = '' - processPosition(decodeResult, parts[0]); processAlt(decodeResult, parts[3]); processRoute(decodeResult, parts[1], parts[2], parts[4], parts[5], parts[6], parts[10]); processTemp(decodeResult, parts[7]); @@ -72,8 +64,6 @@ export class Label_H1_POS extends DecoderPlugin { decodeResult.decoder.decodeLevel = 'partial'; } else if(parts.length === 14) { // variant 2 - decodeResult.remaining.text = '' - processPosition(decodeResult, parts[0]); processAlt(decodeResult, parts[3]); processRoute(decodeResult, parts[1], parts[2], parts[4], parts[5], parts[6]); processTemp(decodeResult, parts[7]); @@ -88,8 +78,6 @@ export class Label_H1_POS extends DecoderPlugin { decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; } else if(parts.length === 15) { // variant 6 - decodeResult.remaining.text = '' - processUnknown(decodeResult, parts[0]); processUnknown(decodeResult, parts[1]); let date = undefined; if(parts[2].startsWith('/DC')) { @@ -122,9 +110,17 @@ export class Label_H1_POS extends DecoderPlugin { decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - } else if(parts.length === 32) { // #M1B long variant - decodeResult.remaining.text = '' - processPosition(decodeResult, parts[0]); + } else if(parts.length === 21) { // variant 8 + processRunway(decodeResult, parts[1]); + processUnknown(decodeResult,parts.slice(2,11).join(',')); + processTemp(decodeResult, parts[11]); + processUnknown(decodeResult,parts.slice(12,20).join(',')); + FlightPlanUtils.processFlightPlan(decodeResult, parts[20].split(':')); + processChecksum(decodeResult, checksum); + + decodeResult.decoded = true; + decodeResult.decoder.decodeLevel = 'partial'; + }else if(parts.length === 32) { // #M1B long variant processRunway(decodeResult, parts[1]); const time = parts[2]; processUnknown(decodeResult, parts[3]); @@ -153,7 +149,7 @@ export class Label_H1_POS extends DecoderPlugin { if (options.debug) { console.log(`Decoder: Unknown H1 message: ${message.text}`); } - decodeResult.remaining.text = message.text; + decodeResult.remaining.text += message.text; decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; } @@ -200,31 +196,11 @@ function processTemp(decodeResult: any, value: string) { } function processRunway(decodeResult: any, value: string) { - decodeResult.raw.departure_runway = value.replace('RW', ''); + decodeResult.raw.arrival_runway = value.replace('RW', ''); decodeResult.formatted.items.push({ type: 'runway', - label: 'Departure Runway', - value: decodeResult.raw.departure_runway, - }); -}; - -function processDeptApt(decodeResult: any, value: string) { - decodeResult.raw.departure_icao = value; - decodeResult.formatted.items.push({ - type: 'origin', - code: 'ORG', - label: 'Origin', - value: decodeResult.raw.departure_icao, - }); -}; - -function processArrvApt(decodeResult: any, value: string) { - decodeResult.raw.arrival_icao = value; - decodeResult.formatted.items.push({ - type: 'destination', - code: 'DST', - label: 'Destination', - value: decodeResult.raw.arrival_icao, + label: 'Arrival Runway', + value: decodeResult.raw.arrival_runway, }); }; @@ -241,6 +217,7 @@ function processGndspd(decodeResult: any, value: string) { function processRoute(decodeResult: any, last: string, time: string, next: string, eta: string, then?: string, date?: string) { const lastCoords = CoordinateUtils.decodeStringCoordinates(last); const nextCoords = CoordinateUtils.decodeStringCoordinates(next); + const thenCoords = then? CoordinateUtils.decodeStringCoordinates(then) : undefined; const lastTime = date ? DateTimeUtils.convertDateTimeToEpoch(time, date) : DateTimeUtils.convertHHMMSSToTod(time); const nextTime = date ? DateTimeUtils.convertDateTimeToEpoch(eta, date) : DateTimeUtils.convertHHMMSSToTod(eta); const timeFormat = date ? 'epoch' : 'tod'; @@ -248,8 +225,8 @@ function processRoute(decodeResult: any, last: string, time: string, next: strin : {name: last, time: lastTime, timeFormat: timeFormat}; const nextWaypoint: Waypoint = nextCoords ? {name: '', latitude: nextCoords.latitude, longitude: nextCoords.longitude, time: nextTime, timeFormat: timeFormat} : {name: next, time: nextTime, timeFormat: timeFormat}; - - const waypoints : Waypoint[] = [lastWaypoint, nextWaypoint, {name: then || '?'}]; + const thenWaypoint: Waypoint = thenCoords ? {name: '', latitude: thenCoords.latitude, longitude: thenCoords.longitude} : {name: then || '?'} + const waypoints : Waypoint[] = [lastWaypoint, nextWaypoint, thenWaypoint]; decodeResult.raw.route = {waypoints: waypoints}; decodeResult.formatted.items.push({ type: 'aircraft_route', @@ -268,3 +245,19 @@ function processChecksum(decodeResult: any, value: string) { value: '0x' + ('0000' + decodeResult.raw.checksum.toString(16)).slice(-4), }); } + +/** + * naive implementation to find the end of the + * + * corrently only lookint at `/TS in which the format is `/TSHHMMSS,DDMMYY + * @param text + * @returns + */ +function findHeader(text: string) { + const parts = text.split(','); + const header = parts[0]; + if(header.indexOf('/TS') === -1) { + return header; + } + return parts[0] + ',' + parts[1]; +} diff --git a/lib/utils/flight_plan_utils.ts b/lib/utils/flight_plan_utils.ts index 2ded418..612229f 100644 --- a/lib/utils/flight_plan_utils.ts +++ b/lib/utils/flight_plan_utils.ts @@ -1,4 +1,5 @@ import { DateTimeUtils } from "../DateTimeUtils"; +import { CoordinateUtils } from "./coordinate_utils"; import { RouteUtils } from "./route_utils"; export class FlightPlanUtils { @@ -11,110 +12,178 @@ export class FlightPlanUtils { * @param data - original message split by ':' * @returns whether all fields were processed or not */ - public static processFlightPlan(decodeResult: any, data: string[]): boolean { - let allKnownFields = parseHeader(decodeResult, data[0]); - for (let i = 1; i < data.length; i += 2) { - const key = data[i]; - const value = data[i + 1]; - // TODO: discuss how store commented out bits as both raw and formatted - switch (key) { - case 'A': // Arrival Procedure (?) - addProcedure(decodeResult, value, 'arrival'); - break; - case 'AA': - addArrivalAirport(decodeResult, value); - break; - case 'AP': - addProcedure(decodeResult, value, 'approach'); - break; - case 'CR': - addCompanyRoute(decodeResult, value); - break; - case 'D': // Departure Procedure - addProcedure(decodeResult, value, 'departure'); - break; - case 'DA': - addDepartureAirport(decodeResult, value); - break; - case 'F': // First Waypoint - addRoute(decodeResult, value); - break; - case 'R': - addRunway(decodeResult, value); - break; - // case 'WS': // something about routes, has altitude, so current parsing won't work - // break; - default: - if (allKnownFields) { - decodeResult.remaining.text = ''; - allKnownFields = false; - } - decodeResult.remaining.text += `:${key}:${value}`; - decodeResult.decoder.decodeLevel = 'partial'; - } - } - return allKnownFields; + public static processFlightPlan(decodeResult: any, data: string[]): boolean { + let allKnownFields = parseHeader(decodeResult, data[0]); + for (let i = 1; i < data.length; i += 2) { + const key = data[i]; + const value = data[i + 1]; + // TODO: discuss how store commented out bits as both raw and formatted + switch (key) { + case 'A': // Arrival Procedure (?) + addProcedure(decodeResult, value, 'arrival'); + break; + case 'AA': + addArrivalAirport(decodeResult, value); + break; + case 'AP': + addProcedure(decodeResult, value, 'approach'); + break; + case 'CR': + addCompanyRoute(decodeResult, value); + break; + case 'D': // Departure Procedure + addProcedure(decodeResult, value, 'departure'); + break; + case 'DA': + addDepartureAirport(decodeResult, value); + break; + case 'F': // First Waypoint + addRoute(decodeResult, value); + break; + case 'R': + addRunway(decodeResult, value); + break; + // case 'WS': // something about routes, has altitude, so current parsing won't work + // break; + default: + if (allKnownFields) { + decodeResult.remaining.text = ''; + allKnownFields = false; + } + decodeResult.remaining.text += `:${key}:${value}`; + decodeResult.decoder.decodeLevel = 'partial'; + } } + return allKnownFields; + } } -function parseHeader(decodeResult: any, header: string): boolean { - let allKnownFields = true; - const fields = header.split('/'); - // fields[0] is msg type - we already know this - for(let i=1; i 0) { + decodeResult.remaining.text += parts[0].substring(0, 3); + decodeResult.raw.flight_number = parts[0].substring(3); + decodeResult.remaining.text += '#' + parts[1].substring(0, 3); + } + if (parts[1].substring(3, 6) === 'POS' && parts[1].length !== 6 && parts[1].substring(3, 7) !== 'POS/') { + decoded = processPosition(decodeResult, parts[1].substring(6)); } - decodeResult.formatted.items.push({ - type: 'status', - code: 'ROUTE_STATUS', - label: 'Route Status', - value: text, - }); - return allKnownFields; -}; -function addArrivalAirport(decodeResult: any, value: string) { - decodeResult.raw.arrival_icao = value; - decodeResult.formatted.items.push({ + decodeResult.raw.message_type = messageType; + return decoded; + } + + decodeResult.remaining.text += messageType; + return false; +} + +function parseHeader(decodeResult: any, header: string): boolean { + let allKnownFields = true; + const fields = header.split('/'); + allKnownFields = allKnownFields && parseMessageType(decodeResult, fields[0]); + for (let i = 1; i < fields.length; ++i) { + if (fields[i].startsWith('FN')) { + decodeResult.raw.flight_number = fields[i].substring(2); // Strip off 'FN' + } else if (fields[i].startsWith('SN')) { + decodeResult.raw.serial_number = fields[i].substring(2); // Strip off 'SN' + } else if (fields[i].startsWith('TS')) { + const ts = fields[i].substring(2).split(','); // Strip off PS + let time = DateTimeUtils.convertDateTimeToEpoch(ts[0], ts[1]); + + if(Number.isNaN(time)) { // convert DDMMYY to MMDDYY - TODO figure out a better way to determine + const date = ts[1].substring(2,4) + ts[1].substring(0,2) + ts[1].substring(4,6); + time = DateTimeUtils.convertDateTimeToEpoch(ts[0], date); + } + decodeResult.raw.message_timestamp = time; + } else if (fields[i].startsWith('PS')) { + const pos = fields[i].substring(2); // Strip off PS + allKnownFields == allKnownFields && processPosition(decodeResult, pos); + } else if (fields[i].startsWith('DT')) { + const icao = fields[i].substring(2); // Strip off DT + decodeResult.raw.arrival_icao = icao; + decodeResult.formatted.items.push({ type: 'destination', code: 'DST', label: 'Destination', value: decodeResult.raw.arrival_icao, - }); + }); + } else if (fields[i].startsWith('RF')) { + decodeResult.formatted.items.push({ + type: 'status', + code: 'ROUTE_STATUS', + label: 'Route Status', + value: 'Route Filed', + }); + decodeResult.raw.route_status = 'RF'; + if (fields[i].length > 2) { + addRoute(decodeResult, fields[i].substring(2)); + } + } else if (fields[i] == 'RP') { + decodeResult.raw.route_status = 'RP'; + decodeResult.formatted.items.push({ + type: 'status', + code: 'ROUTE_STATUS', + label: 'Route Status', + value: 'Route Planned', + }); + decodeResult.raw.route_status = fields[i]; + } else if (fields[i] == 'RI') { + decodeResult.raw.route_status = 'RI'; + decodeResult.formatted.items.push({ + type: 'status', + code: 'ROUTE_STATUS', + label: 'Route Status', + value: 'Route Inactive', + }); + } else { + decodeResult.remaining.text += '/' + fields[i]; + allKnownFields = false + } + } + return allKnownFields; }; -function addDepartureAirport(decodeResult: any, value: string) { - decodeResult.raw.departure_icao = value; + +function processPosition(decodeResult: any, value: string): boolean { + const position = CoordinateUtils.decodeStringCoordinates(value); + if (position) { + decodeResult.raw.position = position decodeResult.formatted.items.push({ - type: 'origin', - code: 'ORG', - label: 'Origin', - value: decodeResult.raw.departure_icao, + type: 'aircraft_position', + code: 'POS', + label: 'Aircraft Position', + value: CoordinateUtils.coordinateString(position), }); + } + return !!position; +} + +function addArrivalAirport(decodeResult: any, value: string) { + decodeResult.raw.arrival_icao = value; + decodeResult.formatted.items.push({ + type: 'destination', + code: 'DST', + label: 'Destination', + value: decodeResult.raw.arrival_icao, + }); }; +function addDepartureAirport(decodeResult: any, value: string) { + decodeResult.raw.departure_icao = value; + decodeResult.formatted.items.push({ + type: 'origin', + code: 'ORG', + label: 'Origin', + value: decodeResult.raw.departure_icao, + }); +}; function addRunway(decodeResult: any, value: string) { // xxx(yyy) where xxx is the departure runway and yyy is the arrival runway if(value.length === 8) { @@ -135,61 +204,61 @@ function addRunway(decodeResult: any, value: string) { }; function addRoute(decodeResult: any, value: string) { - const route = value.split('.'); - decodeResult.raw.route = {waypoints: route.map((leg)=> RouteUtils.getWaypoint(leg))}; - decodeResult.formatted.items.push({ - type: 'aircraft_route', - code: 'ROUTE', - label: 'Aircraft Route', - value: RouteUtils.routeToString(decodeResult.raw.route), - }); - }; + const route = value.split('.'); + decodeResult.raw.route = { waypoints: route.map((leg) => RouteUtils.getWaypoint(leg)) }; + decodeResult.formatted.items.push({ + type: 'aircraft_route', + code: 'ROUTE', + label: 'Aircraft Route', + value: RouteUtils.routeToString(decodeResult.raw.route), + }); +}; function addProcedure(decodeResult: any, value: string, type: string) { - if(decodeResult.raw.procedures === undefined) { - decodeResult.raw.procedures = []; - } - const data = value.split('.'); - let waypoints; - if(data.length>1) { - waypoints = data.slice(1).map((leg)=> RouteUtils.getWaypoint(leg)); - } - const route = {name: data[0], waypoints: waypoints}; - decodeResult.raw.procedures.push({type: type, route: route}); - const procedureName = type.substring(0,1).toUpperCase() + type.slice(1); - let procedureValue = route.name; - decodeResult.formatted.items.push({ - type: `procedure`, - code: 'proc', - label: `${procedureName} Procedure`, - value: RouteUtils.routeToString(route), - }); + if (decodeResult.raw.procedures === undefined) { + decodeResult.raw.procedures = []; + } + const data = value.split('.'); + let waypoints; + if (data.length > 1) { + waypoints = data.slice(1).map((leg) => RouteUtils.getWaypoint(leg)); + } + const route = { name: data[0], waypoints: waypoints }; + decodeResult.raw.procedures.push({ type: type, route: route }); + const procedureName = type.substring(0, 1).toUpperCase() + type.slice(1); + let procedureValue = route.name; + decodeResult.formatted.items.push({ + type: `procedure`, + code: 'proc', + label: `${procedureName} Procedure`, + value: RouteUtils.routeToString(route), + }); +}; + +function addCompanyRoute(decodeResult: any, value: string) { + const segments = value.split('.'); + const parens_idx = segments[0].indexOf('('); + let name; + let runway; + if (parens_idx === -1) { + name = segments[0]; + } else { + name = segments[0].slice(0, parens_idx); + runway = segments[0].slice(parens_idx + 1, segments[0].indexOf(')')); + } + let waypoints; + if (segments.length > 1) { + waypoints = segments.slice(1).map((leg) => RouteUtils.getWaypoint(leg)); + } + decodeResult.raw.company_route = { + name: name, + runway: runway, + waypoints: waypoints, }; - - function addCompanyRoute(decodeResult: any, value: string) { - const segments = value.split('.'); - const parens_idx = segments[0].indexOf('('); - let name; - let runway; - if(parens_idx === -1) { - name = segments[0]; - } else { - name = segments[0].slice(0, parens_idx); - runway = segments[0].slice(parens_idx+1, segments[0].indexOf(')')); - } - let waypoints; - if(segments.length > 1) { - waypoints = segments.slice(1).map((leg) => RouteUtils.getWaypoint(leg)); - } - decodeResult.raw.company_route = { - name: name, - runway: runway, - waypoints: waypoints, - }; - decodeResult.formatted.items.push({ - type: 'company_route', - code: 'CR', - label: 'Company Route', - value: RouteUtils.routeToString(decodeResult.raw.company_route), - }); - }; \ No newline at end of file + decodeResult.formatted.items.push({ + type: 'company_route', + code: 'CR', + label: 'Company Route', + value: RouteUtils.routeToString(decodeResult.raw.company_route), + }); +}; \ No newline at end of file