-
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* non-4A updates * add Label 4A * add degrees to OATEMP formatting * fixes * make 4A invalid test case a real DIS01 message
- Loading branch information
1 parent
c440fd4
commit 463b3fd
Showing
30 changed files
with
810 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
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); | ||
|
||
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(); | ||
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 }); | ||
|
||
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('RT0,LT0,'); | ||
expect(decodeResult.formatted.items.length).toBe(6); | ||
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(); | ||
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 }); | ||
|
||
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('RT0,LT1,'); | ||
expect(decodeResult.formatted.items.length).toBe(5); | ||
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 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 }); | ||
|
||
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('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 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 }); | ||
|
||
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('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'); | ||
}); | ||
|
||
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 }); | ||
|
||
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(' 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 <invalid>', () => { | ||
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 }); | ||
|
||
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); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { DecoderPlugin } from '../DecoderPlugin'; | ||
import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; | ||
import { CoordinateUtils } from '../utils/coordinate_utils'; | ||
import { DateTimeUtils } from '../DateTimeUtils'; | ||
import { RouteUtils } from '../utils/route_utils'; | ||
import { ResultFormatter } from '../utils/result_formatter'; | ||
|
||
export class Label_4A extends DecoderPlugin { | ||
name = 'label-4a'; | ||
|
||
qualifiers() { // eslint-disable-line class-methods-use-this | ||
return { | ||
labels: ['4A'], | ||
}; | ||
} | ||
|
||
decode(message: Message, options: Options = {}) : DecodeResult { | ||
const decodeResult = this.defaultResult(); | ||
decodeResult.decoder.name = this.name; | ||
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(","); | ||
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, text.substring(48, 51) * 100); | ||
decodeResult.remaining.text = fields.slice(8).join(","); | ||
} else if (fields.length === 6) { | ||
if (fields[0].match(/^[NS]/)) { | ||
// variant 2 | ||
ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(fields[0].substring(0, 13))); | ||
let wp1 = { | ||
name: fields[0].substring(13).trim(), | ||
time: DateTimeUtils.convertHHMMSSToTod(fields[1].substring(0, 6)), | ||
timeFormat: 'tod', | ||
}; | ||
ResultFormatter.altitude(decodeResult, fields[1].substring(6, 9) * 100); | ||
let wp2 = { | ||
name: fields[1].substring(9).trim(), | ||
time: DateTimeUtils.convertHHMMSSToTod(fields[2]), | ||
timeFormat: 'tod', | ||
}; | ||
decodeResult.raw.route = {waypoints: [wp1, wp2]}; | ||
decodeResult.formatted.items.push({ | ||
type: 'aircraft_route', | ||
code: 'ROUTE', | ||
label: 'Aircraft Route', | ||
value: RouteUtils.routeToString(decodeResult.raw.route), | ||
}); | ||
ResultFormatter.temperature(decodeResult, fields[3]); | ||
decodeResult.remaining.text = fields.slice(4).join(","); | ||
} else { | ||
// variant 3 | ||
ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0])); | ||
ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1])); | ||
decodeResult.remaining.text = fields[2]; | ||
ResultFormatter.altitude(decodeResult, fields[3]); | ||
ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates((fields[4]+fields[5]).replace(/[ \.]/g, ""))); | ||
} | ||
} else { | ||
decodeResult.decoded = false; | ||
decodeResult.remaining.text = text; | ||
} | ||
|
||
if (decodeResult.decoded) { | ||
if(!decodeResult.remaining.text) | ||
decodeResult.decoder.decodeLevel = 'full'; | ||
else | ||
decodeResult.decoder.decodeLevel = 'partial'; | ||
} else { | ||
decodeResult.decoder.decodeLevel = 'none'; | ||
} | ||
|
||
return decodeResult; | ||
} | ||
} | ||
|
||
export default {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { MessageDecoder } from '../MessageDecoder'; | ||
import { Label_4A_01 } from './Label_4A_01'; | ||
|
||
test('matches Label 4A_01 qualifiers', () => { | ||
const decoder = new MessageDecoder(); | ||
const decoderPlugin = new Label_4A_01(decoder); | ||
|
||
expect(decoderPlugin.decode).toBeDefined(); | ||
expect(decoderPlugin.name).toBe('label-4a-01'); | ||
expect(decoderPlugin.qualifiers).toBeDefined(); | ||
expect(decoderPlugin.qualifiers()).toEqual({ | ||
labels: ['4A'], | ||
preambles: ['01'], | ||
}); | ||
}); | ||
|
||
test('decodes Label 4A_01', () => { | ||
const decoder = new MessageDecoder(); | ||
const decoderPlugin = new Label_4A_01(decoder); | ||
|
||
// https://app.airframes.io/messages/3450562911 | ||
const text = '01DCAP VIR41R/190203EGLLKSFO\r\n+ 1418158.0+ 24.8'; | ||
const decodeResult = decoderPlugin.decode({ text: text }); | ||
|
||
expect(decodeResult.decoded).toBe(true); | ||
expect(decodeResult.decoder.decodeLevel).toBe('partial'); | ||
expect(decodeResult.decoder.name).toBe('label-4a-01'); | ||
expect(decodeResult.formatted.description).toBe('Latest New Format'); | ||
expect(decodeResult.message.text).toBe(text); | ||
expect(decodeResult.remaining.text).toBe('158.0'); | ||
expect(decodeResult.formatted.items.length).toBe(7); | ||
expect(decodeResult.formatted.items[0].code).toBe('STATE_CHANGE'); | ||
expect(decodeResult.formatted.items[0].value).toBe('Descent -> Approach'); | ||
expect(decodeResult.formatted.items[1].code).toBe('CALLSIGN'); | ||
expect(decodeResult.formatted.items[1].value).toBe('VIR41R'); | ||
expect(decodeResult.formatted.items[2].code).toBe('MSG_TOD'); | ||
expect(decodeResult.formatted.items[2].value).toBe('19:02:03'); | ||
expect(decodeResult.formatted.items[3].code).toBe('ORG'); | ||
expect(decodeResult.formatted.items[3].value).toBe('EGLL'); | ||
expect(decodeResult.formatted.items[4].code).toBe('DST'); | ||
expect(decodeResult.formatted.items[4].value).toBe('KSFO'); | ||
expect(decodeResult.formatted.items[5].code).toBe('ALT'); | ||
expect(decodeResult.formatted.items[5].value).toBe("1418 feet"); | ||
expect(decodeResult.formatted.items[6].code).toBe('OATEMP'); | ||
expect(decodeResult.formatted.items[6].value).toBe('24.8 degrees'); | ||
}); | ||
|
||
// disabled because all messages should decode | ||
xtest('decodes Label 4A_01 <invalid>', () => { | ||
const decoder = new MessageDecoder(); | ||
const decoderPlugin = new Label_4A_01(decoder); | ||
|
||
const text = '4A_01 Bogus message'; | ||
const decodeResult = decoderPlugin.decode({ text: text }); | ||
|
||
expect(decodeResult.decoded).toBe(false); | ||
expect(decodeResult.decoder.decodeLevel).toBe('none'); | ||
expect(decodeResult.decoder.name).toBe('label-4a-01'); | ||
expect(decodeResult.formatted.description).toBe('Latest New Format'); | ||
expect(decodeResult.formatted.items.length).toBe(0); | ||
}); |
Oops, something went wrong.