diff --git a/lib/MessageDecoder.ts b/lib/MessageDecoder.ts index 476abb1..00b9385 100644 --- a/lib/MessageDecoder.ts +++ b/lib/MessageDecoder.ts @@ -28,7 +28,6 @@ export class MessageDecoder { this.registerPlugin(new Plugins.Label_44_POS(this)); this.registerPlugin(new Plugins.Label_B6_Forwardslash(this)); this.registerPlugin(new Plugins.Label_H1_FPN(this)); - this.registerPlugin(new Plugins.Label_H1_M1BPOS(this)); this.registerPlugin(new Plugins.Label_H1_POS(this)); this.registerPlugin(new Plugins.Label_80(this)); this.registerPlugin(new Plugins.Label_8E(this)); diff --git a/lib/plugins/Label_H1_M1BPOS.test.ts b/lib/plugins/Label_H1_M1BPOS.test.ts deleted file mode 100644 index 308bfa8..0000000 --- a/lib/plugins/Label_H1_M1BPOS.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { MessageDecoder } from '../MessageDecoder'; -import { Label_H1_M1BPOS } from './Label_H1_M1BPOS'; - -test('decodes Label H1 Preamble #M1BPOS', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_H1_M1BPOS(decoder); - - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-m1bpos'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['#M1BPOS'], - }); - - // https://app.airframes.io/messages/2366921571 - const text = '#M1BPOSN29510W098448,RW04,140407,188,TATAR,4,140445,ALISS,M12,246048,374K,282K,1223,133,KSAT,KELP,,70,151437,415,73/PR1223,222,240,133,,44,40,252074,M22,180,P0,P0/RI:DA:KSAT:AA:KELP..TATAR:D:ALISS6:F:ALISS..FST'; - const decodeResult = decoderPlugin.decode({ text: text }); - console.log(JSON.stringify(decodeResult, null, 2)); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('none'); //should be partial - expect(decodeResult.decoder.name).toBe('label-h1-m1bpos'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Position'); - expect(decodeResult.formatted.items[0].value).toBe('29.51 N, 98.448 W'); - expect(decodeResult.formatted.items[1].label).toBe('Route'); - expect(decodeResult.formatted.items[1].value).toBe('RW04 > TATAR > 4 > ALISS > M12 > KSAT > KELP > ?'); - expect(decodeResult.remaining.text).toBe('PR1223,222,240,133,,44,40,252074,M22,180,P0,P0'); -}); - -test('decodes Label H1 Preamble #M1BPOS ', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_H1_M1BPOS(decoder); - - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-m1bpos'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['#M1BPOS'], - }); - - const text = '#M1BPOS Bogus message'; - const decodeResult = decoderPlugin.decode({ text: text }); - console.log(JSON.stringify(decodeResult, null, 2)); - - expect(decodeResult.decoded).toBe(true); // expect false? - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-h1-m1bpos'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.formatted.items.length).toBe(1); // should be 0 - expect(decodeResult.formatted.items[0].label).toBe('Route'); - expect(decodeResult.formatted.items[0].value).toBe(''); - -}); diff --git a/lib/plugins/Label_H1_M1BPOS.ts b/lib/plugins/Label_H1_M1BPOS.ts deleted file mode 100644 index 0d3e0eb..0000000 --- a/lib/plugins/Label_H1_M1BPOS.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { DecoderPlugin } from '../DecoderPlugin'; -import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; -import { RouteUtils } from '../utils/route_utils'; - -export class Label_H1_M1BPOS extends DecoderPlugin { // eslint-disable-line camelcase - name = 'label-h1-m1bpos'; - - qualifiers() { // eslint-disable-line class-methods-use-this - return { - labels: ['H1'], - preambles: ['#M1BPOS'], - }; - } - - decode(message: Message, options: Options = {}) : DecodeResult { - const decodeResult: any = this.defaultResult; - decodeResult.decoder.name = this.name; - - if (options.debug) { - console.log('DECODER: #M1BPOS detected'); - } - const parts = message.text.replace('#M1BPOS', '').split('/'); - const firstHalf = parts[0]; - const secondHalf = parts[1]; - const items = firstHalf.split(','); - - decodeResult.raw.position = CoordinateUtils.decodeStringCoordinates(items[0]); - if(decodeResult.raw.position) { - decodeResult.formatted.items.push({ - type: 'position', - code: 'POS' , - label: 'Position', - value: CoordinateUtils.coordinateString(decodeResult.raw.position), - }); - } - - const route = items.slice(1).filter((part: any) => !/^\d(.+)$/.test(part)); - const waypoints = route.map((hop: any) => RouteUtils.getWaypoint(hop || '?')); - decodeResult.raw.route = {waypoints: waypoints}; - - decodeResult.formatted.description = 'Position Report'; - - decodeResult.formatted.items.push({ - type: 'route', - label: 'Route', - value: RouteUtils.routeToString(decodeResult.raw.route), - }); - - decodeResult.decoded = true; - decodeResult.decodeLevel = 'partial'; - decodeResult.remaining.text = secondHalf; - - return decodeResult; - } -} - -export default {}; diff --git a/lib/plugins/Label_H1_POS.test.ts b/lib/plugins/Label_H1_POS.test.ts index 509aa55..79c10fb 100644 --- a/lib/plugins/Label_H1_POS.test.ts +++ b/lib/plugins/Label_H1_POS.test.ts @@ -1,7 +1,7 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1_POS } from './Label_H1_POS'; -test('decodes Label H1 Preamble POS variant 1', () => { +test('matches Label H1 Preamble POS qualifiers', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); @@ -10,8 +10,12 @@ test('decodes Label H1 Preamble POS variant 1', () => { expect(decoderPlugin.qualifiers).toBeDefined(); expect(decoderPlugin.qualifiers()).toEqual({ labels: ['H1'], - preambles: ['POS'], + preambles: ['POS', '#M1BPOS'], }); +}); +test('decodes Label H1 Preamble POS variant 1', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_H1_POS(decoder); const text = 'POSN43312W123174,EASON,215754,370,EBINY,220601,ELENN,M48,02216,185/TS215754,0921227A40'; const decodeResult = decoderPlugin.decode({ text: text }); @@ -53,14 +57,6 @@ test('decodes Label H1 Preamble POS variant 2', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-pos'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['POS'], - }); - const text = 'POSN45209W122550,PEGTY,220309,134,MINNE,220424,HISKU,M6,060013,269,366,355K,292K,730A5B'; const decodeResult = decoderPlugin.decode({ text: text }); console.log(JSON.stringify(decodeResult, null, 2)); @@ -106,14 +102,6 @@ test('decodes Label H1 Preamble POS variant 3', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-pos'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['POS'], - }); - const text = 'POSN43030W122406,IBALL,220516,380,AARON,220816,MOXEE,M47,0047,86/TS220516,092122BF64'; const decodeResult = decoderPlugin.decode({ text: text }); console.log(JSON.stringify(decodeResult, null, 2)); @@ -154,14 +142,6 @@ test('decodes Label H1 Preamble POS variant 4', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-pos'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['POS'], - }); - const text = 'POSN33225W079428,SCOOB,232933,340,ENEME,235712,FETAL,M42,003051,15857F6'; const decodeResult = decoderPlugin.decode({ text: text }); console.log(JSON.stringify(decodeResult, null, 2)); @@ -203,14 +183,6 @@ test('decodes Label H1 Preamble POS variant 5', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-pos'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['POS'], - }); - // https://app.airframes.io/messages/2184441420 const text = 'POSN38531W078000,CSN-01,112309,310,CYN-02,114151,ACK,M40,26067,22479226'; const decodeResult = decoderPlugin.decode({ text: text }); @@ -252,29 +224,23 @@ test('decodes Label H1 Preamble POS variant 6', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-pos'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['POS'], - }); - // https://app.airframes.io/messages/2295027018 const text = 'POS/RFSCOOB.KEMPR.ECG.OHPEA.TOMMZ.OXANA.ZZTOP.OMALA.WILYY.KANUX.GALVN.KASAR.LNHOM.SLUKA.FIPEK.PUYYA.PLING.KOLAO.JETSSF2FC'; 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.decodeLevel).toBe('full'); expect(decodeResult.decoder.name).toBe('label-h1-pos'); expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message.text).toBe(text); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[0].value).toBe('SCOOB > KEMPR > ECG > OHPEA > TOMMZ > OXANA > ZZTOP > OMALA > WILYY > KANUX > GALVN > KASAR > LNHOM > SLUKA > FIPEK > PUYYA > PLING > KOLAO > JETSS'); - expect(decodeResult.formatted.items[1].label).toBe('Message Checksum'); - expect(decodeResult.formatted.items[1].value).toBe('0xf2fc'); + expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.formatted.items[0].label).toBe('Route Status'); + expect(decodeResult.formatted.items[0].value).toBe('Route Filed'); + expect(decodeResult.formatted.items[1].label).toBe('Aircraft Route'); + expect(decodeResult.formatted.items[1].value).toBe('SCOOB > KEMPR > ECG > OHPEA > TOMMZ > OXANA > ZZTOP > OMALA > WILYY > KANUX > GALVN > KASAR > LNHOM > SLUKA > FIPEK > PUYYA > PLING > KOLAO > JETSS'); + expect(decodeResult.formatted.items[2].label).toBe('Message Checksum'); + expect(decodeResult.formatted.items[2].value).toBe('0xf2fc'); }); @@ -282,14 +248,6 @@ test('decodes Label H1 Preamble POS ', () => { const decoder = new MessageDecoder(); const decoderPlugin = new Label_H1_POS(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-pos'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['POS'], - }); - const text = 'POS Bogus message'; const decodeResult = decoderPlugin.decode({ text: text }); console.log(JSON.stringify(decodeResult, null, 2)); @@ -300,3 +258,83 @@ test('decodes Label H1 Preamble POS ', () => { expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message.text).toBe(text); }); + +test('decodes Label H1 Preamble #M1BPOS short variant', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_H1_POS(decoder); + + // https://app.airframes.io/messages/2368445399 + const text = '#M1BPOSN37533W096476,ROKNE,185212,330,DOSOA,190059,BUM,M50,272100,1571541' + 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(5); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('37.533 N, 96.476 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('33000 feet'); + expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); + expect(decodeResult.formatted.items[2].value).toBe('ROKNE@18:52:12 > DOSOA@19:00:59 > BUM'); + expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[3].value).toBe('-50'); + expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); + expect(decodeResult.formatted.items[4].value).toBe('0x1541'); + + expect(decodeResult.remaining.text).toBe(',272100,157'); +}); + +test('decodes Label H1 Preamble #M1BPOS long variant', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_H1_POS(decoder); + + // https://app.airframes.io/messages/2366921571 + const text = '#M1BPOSN29510W098448,RW04,140407,188,TATAR,4,140445,ALISS,M12,246048,374K,282K,1223,133,KSAT,KELP,,70,151437,415,73/PR1223,222,240,133,,44,40,252074,M22,180,P0,P0/RI:DA:KSAT:AA:KELP..TATAR:D:ALISS6:F:ALISS..FST'; + 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(10); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('29.51 N, 98.448 W'); + expect(decodeResult.formatted.items[1].label).toBe('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'); + expect(decodeResult.formatted.items[3].label).toBe('Altitude'); + expect(decodeResult.formatted.items[3].value).toBe('24000 feet'); + expect(decodeResult.formatted.items[4].label).toBe('Route Status'); + expect(decodeResult.formatted.items[4].value).toBe('Route Inactive'); + expect(decodeResult.formatted.items[5].label).toBe('Origin'); + expect(decodeResult.formatted.items[5].value).toBe('KSAT'); + expect(decodeResult.formatted.items[6].label).toBe('Destination'); + expect(decodeResult.formatted.items[6].value).toBe('KELP..TATAR'); // should be just kelp + expect(decodeResult.formatted.items[7].label).toBe('Departure Procedure'); + expect(decodeResult.formatted.items[7].value).toBe('ALISS6'); + expect(decodeResult.formatted.items[8].label).toBe('Aircraft Route'); + expect(decodeResult.formatted.items[8].value).toBe('ALISS >> FST'); + expect(decodeResult.formatted.items[9].label).toBe('Aircraft Route'); + expect(decodeResult.formatted.items[9].value).toBe('TATAR@14:04:07 > ALISS@14:04:45 > ?'); // ? should be FST + 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 #M1BPOS ', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_H1_POS(decoder); + + const text = '#M1BPOS Bogus message'; + const decodeResult = decoderPlugin.decode({ text: text }); + console.log(JSON.stringify(decodeResult, null, 2)); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-h1-pos'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.formatted.items.length).toBe(0); +}); diff --git a/lib/plugins/Label_H1_POS.ts b/lib/plugins/Label_H1_POS.ts index e561415..5e386fd 100644 --- a/lib/plugins/Label_H1_POS.ts +++ b/lib/plugins/Label_H1_POS.ts @@ -2,6 +2,8 @@ import { DateTimeUtils } from '../DateTimeUtils'; import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { Waypoint } from '../types/waypoint'; +import { CoordinateUtils } from '../utils/coordinate_utils'; +import { FlightPlanUtils } from '../utils/flight_plan_utils'; import { RouteUtils } from '../utils/route_utils'; export class Label_H1_POS extends DecoderPlugin { @@ -10,7 +12,7 @@ export class Label_H1_POS extends DecoderPlugin { qualifiers() { // eslint-disable-line class-methods-use-this return { labels: ["H1"], - preambles: ['POS'], + preambles: ['POS', '#M1BPOS'], //TODO - support data before # }; } @@ -22,11 +24,18 @@ export class Label_H1_POS extends DecoderPlugin { const checksum = message.text.slice(-4); //strip POS and checksum - const data = message.text.substring(3, message.text.length-4); - const fields = data.split(','); - - if(fields.length==1 && data.startsWith('/RF')) { - decodeResult.raw.route = {waypoints: data.substring(3,data.length).split('.').map((leg: string) => RouteUtils.getWaypoint(leg))}; + const parts = message.text.replace('#M1B', '').replace('POS', '').slice(0,-4).split(','); + console.log(parts); + 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', @@ -34,66 +43,77 @@ export class Label_H1_POS extends DecoderPlugin { value: RouteUtils.routeToString(decodeResult.raw.route), }); - decodeResult.raw.checksum = Number("0x"+checksum); - decodeResult.formatted.items.push({ - type: 'message_checksum', - code: 'CHECKSUM', - label: 'Message Checksum', - value: '0x' + ('0000' + decodeResult.raw.checksum.toString(16)).slice(-4), - }); + processChecksum(decodeResult, checksum); + decodeResult.decoded = true; + decodeResult.decoder.decodeLevel = 'full'; + } 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]); + processUnknown(decodeResult, parts[8]); + processUnknown(decodeResult, parts[9]); + processChecksum(decodeResult, checksum); decodeResult.decoded = true; - // Once we know what RF stands for, I feel comfortable marking this full decodeResult.decoder.decodeLevel = 'partial'; - decodeResult.remaining.text += 'RF' - } else if(fields.length>9) { - // idx - value - // 0 - position in millidegrees - // 1 - waypoint 1 - // 2 - waypoint 1 valid at HHMMSS - // 3 - baro alititude - // 4 - waypoint 2 - // 5 - waypoint 2 eta HHMMSS - // 6 - waypoint 3 - // 7 - temp - // 8 - ? - // 9 - ? or variant 1 ? + /TS + valid at HHMMSS - // 10 - ? or variant 1 date MMDDYY or variant 2 gspd (opt) - // 11 - ? (opt) - // 12 - ? (opt) - // 13 - ? (opt) - this.decodePositionRoute(decodeResult, options, fields); - - decodeResult.remaining.text = `${fields[2]},${fields[5]},${fields[8]}`; + } 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]); + processUnknown(decodeResult, parts[8]); + processUnknown(decodeResult, parts[9]); + processChecksum(decodeResult, checksum); decodeResult.decoded = true; 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]); + processUnknown(decodeResult, parts[8]); + processUnknown(decodeResult, parts[9]); + processGndspd(decodeResult, parts[10]); + processUnknown(decodeResult, parts[11]); + processUnknown(decodeResult, parts[12]); + processUnknown(decodeResult, parts[13]); + processChecksum(decodeResult, checksum); - // variant 2 - if (fields.length==14) { - decodeResult.raw.groundspeed = Number(fields[10]); - - decodeResult.formatted.items.push({ - type: 'aircraft_groundspeed', - code: 'GSPD', - label: 'Aircraft Groundspeed', - value: `${decodeResult.raw.groundspeed}` - }); - - decodeResult.remaining.text += `,${fields[9]},${fields[11]},${fields[12]},${fields[13]}`; - } else { - for(let i=9; i