Skip to content

Commit f03079a

Browse files
authored
Better H1 Parsing (#327)
* parsing slash messages * Add RES/REQ/REJ decoding
1 parent c1356dc commit f03079a

12 files changed

Lines changed: 373 additions & 150 deletions

lib/MessageDecoder.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ export class MessageDecoder {
6767
this.registerPlugin(new Plugins.Label_H1_OHMA(this));
6868
this.registerPlugin(new Plugins.Label_H1_WRN(this));
6969
this.registerPlugin(new Plugins.Label_H1(this));
70-
this.registerPlugin(new Plugins.Label_H1_Slash(this));
7170
this.registerPlugin(new Plugins.Label_H1_StarPOS(this));
7271
this.registerPlugin(new Plugins.Label_HX(this));
7372
this.registerPlugin(new Plugins.Label_58(this));

lib/plugins/Label_H1.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,21 @@ export class Label_H1 extends DecoderPlugin {
1919
const msg = message.text.replace(/\n|\r/g, '');
2020
const parts = msg.split('#');
2121
let decoded = false;
22-
if (parts.length === 1) {
22+
if (msg.startsWith('/')) {
23+
const headerData = msg.split('.');
24+
const decoded = H1Helper.decodeH1Message(
25+
decodeResult,
26+
headerData.slice(1).join('.'),
27+
); // skip up to # and then a little more
28+
if (decoded) {
29+
decodeResult.remaining.text =
30+
headerData[0] + '.' + decodeResult.remaining.text;
31+
decodeResult.decoded = decoded;
32+
decodeResult.decoder.decodeLevel = 'partial';
33+
}
34+
35+
return decodeResult;
36+
} else if (parts.length === 1) {
2337
decoded = H1Helper.decodeH1Message(decodeResult, msg);
2438
} else if (parts.length == 2) {
2539
const offset = isNaN(parseInt(parts[1][1])) ? 3 : 4;

lib/plugins/Label_H1_POS.test.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -488,15 +488,23 @@ describe('Label_H1 POS', () => {
488488
);
489489
});
490490

491-
test('does not decode /.POS', () => {
491+
test('decodes /.POS', () => {
492492
// https://app.airframes.io/messages/2500488708
493493
message.text =
494494
'/.POS/TS100316,210324/PSS35333W058220,,100316,250,S37131W059150,101916,S39387W060377,M23,27282,241,780,MANUAL,0,813E711';
495495
const decodeResult = plugin.decode(message);
496496

497-
expect(decodeResult.decoded).toBe(false);
498-
expect(decodeResult.decoder.decodeLevel).toBe('none');
499-
expect(decodeResult.formatted.description).toBe('Unknown');
497+
expect(decodeResult.decoded).toBe(true);
498+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
499+
expect(decodeResult.formatted.description).toBe('Position Report');
500+
expect(decodeResult.raw.message_timestamp).toBe(1711015396);
501+
expect(decodeResult.raw.position.latitude).toBeCloseTo(-35.555, 3);
502+
expect(decodeResult.raw.position.longitude).toBeCloseTo(-58.367, 3);
503+
expect(decodeResult.raw.altitude).toBe(25000);
504+
expect(decodeResult.raw.route.waypoints.length).toBe(3);
505+
expect(decodeResult.raw.checksum).toBe(0xe711);
506+
expect(decodeResult.formatted.items.length).toBe(5);
507+
expect(decodeResult.remaining.text).toBe('/.27282,241,780,MANUAL,0,813');
500508
});
501509

502510
test('decodes duplicate data', () => {

lib/plugins/Label_H1_REJ.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { MessageDecoder } from '../MessageDecoder';
2+
import { Label_H1 } from './Label_H1';
3+
4+
describe('Label H1 Preamble REJ', () => {
5+
let plugin: Label_H1;
6+
const message = { label: 'H1', text: '' };
7+
8+
beforeEach(() => {
9+
const decoder = new MessageDecoder();
10+
plugin = new Label_H1(decoder);
11+
});
12+
13+
test('decodes PWI variant 1', () => {
14+
message.text = 'REJPWI,141147,A,70,MCRAY,97E16';
15+
const decodeResult = plugin.decode(message);
16+
17+
expect(decodeResult.decoded).toBe(true);
18+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
19+
expect(decodeResult.raw.checksum).toBe(0x7e16);
20+
expect(decodeResult.formatted.items.length).toBe(1);
21+
expect(decodeResult.remaining.text).toBe('141147,A,70,MCRAY,9');
22+
});
23+
24+
test('decodes PWI variant 2', () => {
25+
message.text =
26+
'/HDQDLUA.REJPWI,104916,053,,CB007,CLIMB.053,,CB007,CLIMB.053,,CB007,CLIMB.053,,CB007,CLIMB.053,,CB007,CRUISE/GAHDQDLUA/TS113028,070226FE61';
27+
const decodeResult = plugin.decode(message);
28+
29+
expect(decodeResult.decoded).toBe(true);
30+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
31+
expect(decodeResult.raw.ground_address).toBe('HDQDLUA');
32+
expect(decodeResult.raw.checksum).toBe(0xfe61);
33+
expect(decodeResult.formatted.items.length).toBe(2);
34+
expect(decodeResult.remaining.text).toBe(
35+
'/HDQDLUA.104916,053,,CB007,CLIMB.053,,CB007,CLIMB.053,,CB007,CLIMB.053,,CB007,CLIMB.053,,CB007,CRUISE',
36+
);
37+
});
38+
39+
test('decodes POS variant 1', () => {
40+
message.text =
41+
'REJPOS,100719,130,219,RF005,N36089W076173/TS100719,090226E850';
42+
const decodeResult = plugin.decode(message);
43+
44+
expect(decodeResult.decoded).toBe(true);
45+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
46+
expect(decodeResult.raw.checksum).toBe(0xe850);
47+
expect(decodeResult.formatted.items.length).toBe(1);
48+
expect(decodeResult.remaining.text).toBe(
49+
'100719,130,219,RF005,N36089W076173',
50+
);
51+
});
52+
53+
test('decodes POS variant 2', () => {
54+
message.text =
55+
'REJPOS,041509,130,219,RF005,SWANN.130,219,RF005,BROSS.130,219,RF005,MYFOO.130,219,RF005,YAHOO.130,219,RF005,4650N.130,219,RF005,4840N.130,219,RF005,5030N.130,219,RF005,5120NA55A';
56+
const decodeResult = plugin.decode(message);
57+
58+
expect(decodeResult.decoded).toBe(true);
59+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
60+
expect(decodeResult.raw.checksum).toBe(0xa55a);
61+
expect(decodeResult.formatted.items.length).toBe(1);
62+
expect(decodeResult.remaining.text).toBe(
63+
'041509,130,219,RF005,SWANN.130,219,RF005,BROSS.130,219,RF005,MYFOO.130,219,RF005,YAHOO.130,219,RF005,4650N.130,219,RF005,4840N.130,219,RF005,5030N.130,219,RF005,5120N',
64+
);
65+
});
66+
});

lib/plugins/Label_H1_REQ.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { MessageDecoder } from '../MessageDecoder';
2+
import { Label_H1 } from './Label_H1';
3+
4+
describe('Label H1 preamble REQ', () => {
5+
let plugin: Label_H1;
6+
const message = { label: 'H1', text: '' };
7+
8+
beforeEach(() => {
9+
const decoder = new MessageDecoder();
10+
plugin = new Label_H1(decoder);
11+
});
12+
13+
test('decodes PWI', () => {
14+
message.text =
15+
'REQPWI/WQ320:GEQUE.HACKS.XUB.VHP.KK60K.KIVDE.KK60E.KOVJY.KD54Y.ACZES.KD48S.DVC.KITTN.KATTS.RUMPS.OAL.INYOE.DYAMD/DQ320/SPGEQUE77CE';
16+
const decodeResult = plugin.decode(message);
17+
18+
expect(decodeResult.decoded).toBe(true);
19+
expect(decodeResult.decoder.decodeLevel).toBe('full');
20+
expect(decodeResult.raw.requested_alts).toStrictEqual([32000]);
21+
expect(decodeResult.raw.desired_alt).toBe(32000);
22+
expect(decodeResult.raw.start_point).toBe('GEQUE');
23+
expect(decodeResult.raw.route.waypoints).toStrictEqual([
24+
{ name: 'GEQUE' },
25+
{ name: 'HACKS' },
26+
{ name: 'XUB' },
27+
{ name: 'VHP' },
28+
{ name: 'KK60K' },
29+
{ name: 'KIVDE' },
30+
{ name: 'KK60E' },
31+
{ name: 'KOVJY' },
32+
{ name: 'KD54Y' },
33+
{ name: 'ACZES' },
34+
{ name: 'KD48S' },
35+
{ name: 'DVC' },
36+
{ name: 'KITTN' },
37+
{ name: 'KATTS' },
38+
{ name: 'RUMPS' },
39+
{ name: 'OAL' },
40+
{ name: 'INYOE' },
41+
{ name: 'DYAMD' },
42+
]);
43+
expect(decodeResult.raw.checksum).toBe(0x77ce);
44+
expect(decodeResult.formatted.items.length).toBe(5);
45+
});
46+
47+
test('decodes POS', () => {
48+
message.text = 'REQPOS037B';
49+
const decodeResult = plugin.decode(message);
50+
51+
expect(decodeResult.decoded).toBe(true);
52+
expect(decodeResult.decoder.decodeLevel).toBe('full');
53+
expect(decodeResult.raw.checksum).toBe(0x037b);
54+
expect(decodeResult.formatted.items.length).toBe(1);
55+
expect(decodeResult.remaining.text).toBeUndefined();
56+
});
57+
58+
test('decodes FPN', () => {
59+
message.text = 'REQFPN/RN10001/RS:FP:Z5585 9736';
60+
const decodeResult = plugin.decode(message);
61+
62+
expect(decodeResult.decoded).toBe(true);
63+
expect(decodeResult.decoder.decodeLevel).toBe('full');
64+
expect(decodeResult.raw.route_number).toBe('10001');
65+
expect(decodeResult.raw.route_status).toBe('RS');
66+
expect(decodeResult.raw.flight_plan).toBe('Z5585');
67+
expect(decodeResult.raw.checksum).toBe(0x9736);
68+
expect(decodeResult.formatted.items.length).toBe(4);
69+
expect(decodeResult.remaining.text).toBeUndefined();
70+
});
71+
});

lib/plugins/Label_H1_RES.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { MessageDecoder } from '../MessageDecoder';
2+
import { Label_H1 } from './Label_H1';
3+
4+
describe('Label H1 Preamble RES', () => {
5+
let plugin: Label_H1;
6+
const message = { label: 'H1', text: '' };
7+
8+
beforeEach(() => {
9+
const decoder = new MessageDecoder();
10+
plugin = new Label_H1(decoder);
11+
});
12+
13+
test('decodes PWI', () => {
14+
message.text = 'RESPWI/AC,5B/TS140956,070226/DI140953,140956,140956128C';
15+
const decodeResult = plugin.decode(message);
16+
17+
expect(decodeResult.decoded).toBe(true);
18+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
19+
expect(decodeResult.raw.checksum).toBe(0x128c);
20+
expect(decodeResult.formatted.items.length).toBe(1);
21+
expect(decodeResult.remaining.text).toBe('AC,5B/DI140953,140956,140956');
22+
});
23+
24+
test('decodes POS', () => {
25+
message.text = 'RESPOS/AK,0711909';
26+
const decodeResult = plugin.decode(message);
27+
28+
expect(decodeResult.decoded).toBe(true);
29+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
30+
expect(decodeResult.raw.checksum).toBe(0x1909);
31+
expect(decodeResult.formatted.items.length).toBe(1);
32+
expect(decodeResult.remaining.text).toBe('AK,071');
33+
});
34+
});

lib/plugins/Label_H1_Slash.test.ts

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
import { MessageDecoder } from '../MessageDecoder';
2-
import { Label_H1_Slash } from './Label_H1_Slash';
2+
import { Label_H1 } from './Label_H1';
33

44
describe('Label H1 /', () => {
5-
let plugin: Label_H1_Slash;
5+
let plugin: Label_H1;
66
const message = { label: 'H1', text: '' };
77

88
beforeEach(() => {
99
const decoder = new MessageDecoder();
10-
plugin = new Label_H1_Slash(decoder);
11-
});
12-
13-
test('matches qualifiers', () => {
14-
expect(plugin.decode).toBeDefined();
15-
expect(plugin.name).toBe('label-h1-slash');
16-
expect(plugin.qualifiers).toBeDefined();
17-
expect(plugin.qualifiers()).toEqual({
18-
labels: ['H1'],
19-
preambles: ['/'],
20-
});
10+
plugin = new Label_H1(decoder);
2111
});
2212

2313
test('decodes variant 1', () => {
@@ -45,7 +35,7 @@ describe('Label H1 /', () => {
4535
expect(decodeResult.formatted.items[3].value).toBe('-23 degrees');
4636
expect(decodeResult.formatted.items[4].label).toBe('Message Checksum');
4737
expect(decodeResult.formatted.items[4].value).toBe('0xe711');
48-
expect(decodeResult.remaining.text).toBe('27282,241,780,MANUAL,0,813');
38+
expect(decodeResult.remaining.text).toBe('/.27282,241,780,MANUAL,0,813');
4939
});
5040

5141
test('decodes variant 2', () => {
@@ -57,22 +47,15 @@ describe('Label H1 /', () => {
5747
expect(decodeResult.decoder.decodeLevel).toBe('partial');
5848
expect(decodeResult.formatted.description).toBe('Position Report');
5949
expect(decodeResult.raw.message_timestamp).toBe(1731592673);
60-
expect(decodeResult.formatted.items.length).toBe(5);
61-
expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position');
62-
expect(decodeResult.formatted.items[0].value).toBe('38.553 N, 80.137 W');
63-
expect(decodeResult.formatted.items[1].label).toBe('Altitude');
64-
expect(decodeResult.formatted.items[1].value).toBe('32000 feet');
65-
expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route');
66-
expect(decodeResult.formatted.items[2].value).toBe(
67-
'RONZZ@13:57:53 > LEVII@14:04:54 > WISTA',
68-
);
69-
expect(decodeResult.formatted.items[3].label).toBe(
70-
'Outside Air Temperature (C)',
71-
);
72-
expect(decodeResult.formatted.items[3].value).toBe('-45 degrees');
73-
expect(decodeResult.formatted.items[4].label).toBe('Message Checksum');
74-
expect(decodeResult.formatted.items[4].value).toBe('0x0721');
75-
expect(decodeResult.remaining.text).toBe('HDQDLUA,20967,194/GAHDQDLUA/CA');
50+
expect(decodeResult.raw.position.latitude).toBeCloseTo(38.553, 3);
51+
expect(decodeResult.raw.position.longitude).toBeCloseTo(-80.137, 3);
52+
expect(decodeResult.raw.route.waypoints.length).toBe(3);
53+
expect(decodeResult.raw.altitude).toBe(32000);
54+
expect(decodeResult.raw.outside_air_temperature).toBe(-45);
55+
expect(decodeResult.raw.ground_address).toBe('HDQDLUA');
56+
expect(decodeResult.raw.checksum).toBe(0x0721);
57+
expect(decodeResult.formatted.items.length).toBe(6);
58+
expect(decodeResult.remaining.text).toBe('/HDQDLUA.20967,194/CA');
7659
});
7760

7861
test('decodes variant 3', () => {
@@ -99,7 +82,7 @@ describe('Label H1 /', () => {
9982
expect(decodeResult.formatted.items[3].value).toBe('-56 degrees');
10083
expect(decodeResult.formatted.items[4].label).toBe('Message Checksum');
10184
expect(decodeResult.formatted.items[4].value).toBe('0x6763');
102-
expect(decodeResult.remaining.text).toBe('24739,127');
85+
expect(decodeResult.remaining.text).toBe('/.24739,127');
10386
});
10487

10588
test('does not decode invalid', () => {

lib/plugins/Label_H1_Slash.ts

Lines changed: 0 additions & 88 deletions
This file was deleted.

0 commit comments

Comments
 (0)