-
-
Notifications
You must be signed in to change notification settings - Fork 15
Adding Label H2 header 02E parser #294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
116c288
30dc18b
0648bd2
b8f2be4
2cc4fe3
b7b0837
c66a6a2
735359f
0ad0f48
68250e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| import { MessageDecoder } from "../MessageDecoder"; | ||
| import { Label_H1_02E20 } from "./Label_H1_02E20"; | ||
|
|
||
| describe("Label_H1 02E20", () => { | ||
| let plugin: Label_H1_02E20; | ||
|
|
||
| beforeEach(() => { | ||
| const decoder = new MessageDecoder(); | ||
| plugin = new Label_H1_02E20(decoder); | ||
| }); | ||
| test("matches qualifiers", () => { | ||
| expect(plugin.decode).toBeDefined(); | ||
| expect(plugin.name).toBe("label-h1-02e20"); | ||
| expect(plugin.qualifiers).toBeDefined(); | ||
| expect(plugin.qualifiers()).toEqual({ | ||
| labels: ["H1"], | ||
| preambles: ["02E20"], | ||
| }); | ||
| }); | ||
|
|
||
| test("decodes discord example 1", () => { | ||
| const text = | ||
| "02E20HEGNLKPRN40359E02208116253601M627259020G QN41179E02134316323599M617247037G QN41591E02100516393603M610266040G QN42393E02026716463602M600276033G QN43197E01954316533598M592299037G QN44023E01929517003596M587313033G Q"; | ||
| const decodeResult = plugin.decode({ text: text }); | ||
| /* | ||
| Route: HEGN-LKPR | ||
| 1 40°35.9'N, 022°08.1'E 16:25 FL360 36,000 ft -62.7°C 259°/20kts | ||
| 2 41°17.9'N, 021°34.3'E 16:32 FL359 35,900 ft -61.7°C 247°/37kts | ||
| 3 41°59.1'N, 021°00.5'E 16:39 FL360 36,000 ft -61.0°C 266°/40kts | ||
| 4 42°39.3'N, 020°26.7'E 16:46 FL360 36,000 ft -60.0°C 276°/33kts | ||
| 5 43°19.7'N, 019°54.3'E 16:53 FL359 35,900 ft -59.2°C 299°/37kts | ||
| 6 44°02.3'N, 019°29.5'E 17:00 FL359 35,900 ft -58.7°C 313°/33kts | ||
| */ | ||
| expect(decodeResult.decoded).toBe(true); | ||
| expect(decodeResult.decoder.decodeLevel).toBe("full"); | ||
| expect(decodeResult.formatted.description).toBe("Weather Report"); | ||
| expect(decodeResult.message.text).toBe(text); | ||
| const weather = decodeResult.raw.wind_data; | ||
| expect(weather.length).toBe(6); | ||
| expect(decodeResult.formatted.items[0].label).toBe("Origin"); | ||
| expect(decodeResult.formatted.items[0].value).toBe("HEGN"); | ||
| expect(decodeResult.formatted.items[1].label).toBe("Destination"); | ||
| expect(decodeResult.formatted.items[1].value).toBe("LKPR"); | ||
| expect(decodeResult.formatted.items[2].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[2].value).toBe( | ||
| "N40359E02208(40.598 N, 22.135 E)@16:25:00 at FL360: 259° at 20kt, -62.7°C at FL360" | ||
| ); | ||
| expect(decodeResult.formatted.items[3].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[3].value).toBe( | ||
| "N41179E02134(41.298 N, 21.572 E)@16:32:00 at FL359: 247° at 37kt, -61.7°C at FL359" | ||
| ); | ||
| expect(decodeResult.formatted.items[4].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[4].value).toBe( | ||
| "N41591E02100(41.985 N, 21.008 E)@16:39:00 at FL360: 266° at 40kt, -61°C at FL360" | ||
| ); | ||
| expect(decodeResult.formatted.items[5].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[5].value).toBe( | ||
| "N42393E02026(42.655 N, 20.445 E)@16:46:00 at FL360: 276° at 33kt, -60°C at FL360" | ||
| ); | ||
| expect(decodeResult.formatted.items[6].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[6].value).toBe( | ||
| "N43197E01954(43.328 N, 19.905 E)@16:53:00 at FL359: 299° at 37kt, -59.2°C at FL359" | ||
| ); | ||
| expect(decodeResult.formatted.items[7].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[7].value).toBe( | ||
| "N44023E01929(44.038 N, 19.492 E)@17:00:00 at FL359: 313° at 33kt, -58.7°C at FL359" | ||
| ); | ||
| }); | ||
|
|
||
| test("decodes discord example 2", () => { | ||
| const text = | ||
| "02E20EGKKLBSFN45081E01757116493501M577327021G QN44401E01903016563499M575352028G QN44115E02008017033468M550319029G QN43420E02112317103296M525299036G QN43125E02214517172023M277271022G Q"; | ||
| const decodeResult = plugin.decode({ text: text }); | ||
|
|
||
| /* | ||
| Route: EGKK-LBSF | ||
| 1 FL350 ~35,000 ft -57.7°C 327°/21kts | ||
| 2 FL349 ~34,900 ft -57.5°C 352°/28kts | ||
| 3 FL346 ~34,600 ft -55.0°C 319°/29kts | ||
| 4 FL329 ~32,900 ft -52.5°C 299°/36kts | ||
| 5 FL202 ~20,200 ft -27.7°C 271°/22kts | ||
| */ | ||
| expect(decodeResult.decoded).toBe(true); | ||
| expect(decodeResult.decoder.decodeLevel).toBe("full"); | ||
| expect(decodeResult.formatted.description).toBe("Weather Report"); | ||
| expect(decodeResult.message.text).toBe(text); | ||
| const weather = decodeResult.raw.wind_data; | ||
| expect(weather.length).toBe(5); | ||
| expect(decodeResult.formatted.items[0].label).toBe("Origin"); | ||
| expect(decodeResult.formatted.items[0].value).toBe("EGKK"); | ||
| expect(decodeResult.formatted.items[1].label).toBe("Destination"); | ||
| expect(decodeResult.formatted.items[1].value).toBe("LBSF"); | ||
| expect(decodeResult.formatted.items[2].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[2].value).toBe( | ||
| "N45081E01757(45.135 N, 17.952 E)@16:49:00 at FL350: 327° at 21kt, -57.7°C at FL350" | ||
| ); | ||
| expect(decodeResult.formatted.items[3].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[3].value).toBe( | ||
| "N44401E01903(44.668 N, 19.050 E)@16:56:00 at FL349: 352° at 28kt, -57.5°C at FL349" | ||
| ); | ||
| expect(decodeResult.formatted.items[4].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[4].value).toBe( | ||
| "N44115E02008(44.192 N, 20.133 E)@17:03:00 at FL346: 319° at 29kt, -55°C at FL346" | ||
| ); | ||
| expect(decodeResult.formatted.items[5].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[5].value).toBe( | ||
| "N43420E02112(43.700 N, 21.205 E)@17:10:00 at FL329: 299° at 36kt, -52.5°C at FL329" | ||
| ); | ||
| expect(decodeResult.formatted.items[6].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[6].value).toBe( | ||
| "N43125E02214(43.208 N, 22.242 E)@17:17:00 at FL202: 271° at 22kt, -27.7°C at FL202" | ||
| ); | ||
| }); | ||
|
|
||
| test("decodes website example", () => { | ||
| // https://app.airframes.io/messages/6025352132 | ||
| const text = | ||
| "02E20EIDWKORDN44087W08505523383800M470251091G QN43210W08520623452813M442251113G QN42461W08539523522189M295256121G QN42380W08623723591780M227266100G Q"; | ||
| const decodeResult = plugin.decode({ text: text }); | ||
|
|
||
| expect(decodeResult.decoded).toBe(true); | ||
| expect(decodeResult.decoder.decodeLevel).toBe("full"); | ||
| expect(decodeResult.formatted.description).toBe("Weather Report"); | ||
| expect(decodeResult.message.text).toBe(text); | ||
| const weather = decodeResult.raw.wind_data; | ||
| expect(weather.length).toBe(4); | ||
| expect(decodeResult.formatted.items[0].label).toBe("Origin"); | ||
| expect(decodeResult.formatted.items[0].value).toBe("EIDW"); | ||
| expect(decodeResult.formatted.items[1].label).toBe("Destination"); | ||
| expect(decodeResult.formatted.items[1].value).toBe("KORD"); | ||
| expect(decodeResult.formatted.items[2].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[2].value).toBe( | ||
| "N44087W08505(44.145 N, 85.092 W)@23:38:00 at FL380: 251° at 91kt, -47°C at FL380" | ||
| ); | ||
| expect(decodeResult.formatted.items[3].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[3].value).toBe( | ||
| "N43210W08520(43.350 N, 85.343 W)@23:45:00 at FL281: 251° at 113kt, -44.2°C at FL281" | ||
| ); | ||
| expect(decodeResult.formatted.items[4].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[4].value).toBe( | ||
| "N42461W08539(42.768 N, 85.658 W)@23:52:00 at FL218: 256° at 121kt, -29.5°C at FL218" | ||
| ); | ||
| expect(decodeResult.formatted.items[5].label).toBe("Wind Data"); | ||
| expect(decodeResult.formatted.items[5].value).toBe( | ||
| "N42380W08623(42.633 N, 86.395 W)@23:59:00 at FL178: 266° at 100kt, -22.7°C at FL178" | ||
| ); | ||
| }); | ||
|
|
||
| test("decodes invalid message", () => { | ||
| const text = "02E20INVALID MESSAGE TEXT"; | ||
| const decodeResult = plugin.decode({ text: text }); | ||
|
|
||
| expect(decodeResult.decoded).toBe(false); | ||
| expect(decodeResult.decoder.decodeLevel).toBe("none"); | ||
| expect(decodeResult.formatted.description).toBe("Weather Report"); | ||
| expect(decodeResult.message.text).toBe(text); | ||
| expect(decodeResult.remaining.text).toBe("02E20INVALID MESSAGE TEXT"); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| import { DateTimeUtils } from '../DateTimeUtils'; | ||
| import { DecoderPlugin } from '../DecoderPlugin'; | ||
| import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; | ||
| import { Wind } from '../types/wind'; | ||
| import { CoordinateUtils } from '../utils/coordinate_utils'; | ||
| import { ResultFormatter } from '../utils/result_formatter'; | ||
|
|
||
| export class Label_H1_02E20 extends DecoderPlugin { | ||
| name = 'label-h1-02e20'; | ||
|
|
||
| qualifiers() { // eslint-disable-line class-methods-use-this | ||
| return { | ||
| labels: ["H1"], | ||
| preambles: ['02E20'], | ||
| }; | ||
| } | ||
|
|
||
| decode(message: Message, options: Options = {}) : DecodeResult { | ||
| let decodeResult = this.defaultResult(); | ||
| decodeResult.decoder.name = this.name; | ||
| decodeResult.formatted.description = 'Weather Report'; | ||
| decodeResult.message = message; | ||
|
|
||
| const parts = message.text.split(' '); | ||
|
|
||
| if(parts[parts.length-1] !== 'Q') { | ||
| // not a valid message | ||
| decodeResult.remaining.text = message.text; | ||
| decodeResult.decoded = false; | ||
| decodeResult.decoder.decodeLevel = 'none'; | ||
| return decodeResult; | ||
| } | ||
|
|
||
| const windData: Wind[] = []; | ||
| decodeResult.remaining.text = ''; | ||
|
|
||
| const header = parts[0]; | ||
| // header.substring(0,5) is '02E20' | ||
| ResultFormatter.departureAirport(decodeResult, header.substring(5,9)); | ||
| ResultFormatter.arrivalAirport(decodeResult, header.substring(9,13)); | ||
| const firstWind = this.parseWeatherReport(header.substring(13)); | ||
| if(firstWind) { | ||
| windData.push(firstWind); | ||
| } else { | ||
| decodeResult.remaining.text += (decodeResult.remaining.text ? ' ' : '') + header.substring(13); | ||
| } | ||
|
|
||
| for(let i=1; i<parts.length-1; i++) { | ||
| const part = parts[i]; | ||
| if(part[0] !== 'Q') { | ||
| decodeResult.remaining.text += (decodeResult.remaining.text ? ' ' : '') + part; | ||
| continue | ||
| } | ||
| const wind = this.parseWeatherReport(part.substring(1)); | ||
| if(wind) { | ||
| windData.push(wind); | ||
| } else { | ||
| decodeResult.remaining.text += (decodeResult.remaining.text ? ' ' : '') + part; | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
| ResultFormatter.windData(decodeResult, windData); | ||
| decodeResult.decoded = true; | ||
| decodeResult.decoder.decodeLevel = decodeResult.remaining.text.length === 0 ? 'full' : 'partial'; | ||
| return decodeResult; | ||
| } | ||
|
|
||
| parseWeatherReport(text: string): Wind | null { | ||
makrsmark marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| //N40359E02208116253601M627259020G | ||
|
|
||
| //40°35.9'N, 022°08.1'E 16:25 FL360 36,000 ft -62.7°C 259°/20kts | ||
|
|
||
| const pos = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(0,13)); | ||
| if (text.length !== 32 || !pos) { | ||
| return null; | ||
| } | ||
| const hhmm = text.substring(13,17); | ||
| const flightLevel = parseInt(text.substring(17,20), 10); | ||
| // const altitude = parseInt(text.substring(17,21), 10) * 10; // use FL instead | ||
| const tempSign = text[21] === 'M' ? -1 : 1; | ||
| const tempDegreesRaw = parseInt(text.substring(22,25), 10); | ||
| const tempDegrees = tempSign * (tempDegreesRaw / 10); | ||
| const windDirection = parseInt(text.substring(25,28), 10); | ||
| const windSpeed = parseInt(text.substring(28,31), 10); | ||
| // G? | ||
| if(text[31] !== 'G') { | ||
| return null; | ||
| } | ||
| return { | ||
| waypoint: { | ||
| name: text.substring(0,12), | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| latitude: pos.latitude, | ||
| longitude: pos.longitude, | ||
| time: DateTimeUtils.convertHHMMSSToTod(hhmm), | ||
| timeFormat: 'tod', | ||
| }, | ||
| flightLevel: flightLevel, | ||
| windDirection: windDirection, | ||
| windSpeed: windSpeed, | ||
| temperature: { | ||
| flightLevel: flightLevel, | ||
| degreesC: tempDegrees, | ||
| }, | ||
| }; | ||
| } | ||
| } | ||
| export default {}; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { Waypoint } from "./waypoint"; | ||
|
|
||
| export interface Wind { | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm tempted to make this
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes me think further about us needing to find a standard unit (with conversion capability) in everything we do! |
||
| waypoint: Waypoint; | ||
| flightLevel: number; | ||
| windDirection: number; | ||
| windSpeed: number; | ||
| temperature?: { | ||
| flightLevel: number; | ||
| degreesC: number; | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.