Skip to content

Commit 4cce1b2

Browse files
authored
Adding Label 1J and Label 2J decoding for FTX (#231)
* Adding Label 1J and Label 2J decoding for FTX * fix spec
1 parent d5f68ca commit 4cce1b2

File tree

7 files changed

+122
-7
lines changed

7 files changed

+122
-7
lines changed

lib/MessageDecoder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export class MessageDecoder {
2626
this.registerPlugin(new Plugins.Label_16_N_Space(this));
2727
this.registerPlugin(new Plugins.Label_16_POSA1(this));
2828
this.registerPlugin(new Plugins.Label_16_TOD(this));
29+
this.registerPlugin(new Plugins.Label_1J_2J_FTX(this));
2930
this.registerPlugin(new Plugins.Label_1L_3Line(this));
3031
this.registerPlugin(new Plugins.Label_1L_070(this));
3132
this.registerPlugin(new Plugins.Label_1L_660(this));
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_1J_2J_FTX } from './Label_1J_2J_FTX';
3+
4+
describe('Label 1J/2J FTX', () => {
5+
6+
let plugin: Label_1J_2J_FTX;
7+
8+
beforeEach(() => {
9+
const decoder = new MessageDecoder();
10+
plugin = new Label_1J_2J_FTX(decoder);
11+
});
12+
13+
14+
test('decodes Label 1J', () => {
15+
// https://app.airframes.io/messages/4178692503
16+
const text = 'FTX/ID50007B,RCH4086,ABB02R70E037/MR6,/FX4 QTR PHILLY UP 37-6307A'
17+
const decodeResult = plugin.decode({ text: text });
18+
19+
expect(decodeResult.decoded).toBe(true);
20+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
21+
expect(decodeResult.raw.mission_number).toBe('ABB02R70E037');
22+
expect(decodeResult.formatted.items.length).toBe(4);
23+
expect(decodeResult.formatted.items[0].label).toBe('Tail');
24+
expect(decodeResult.formatted.items[0].value).toBe('50007B');
25+
expect(decodeResult.formatted.items[1].label).toBe('Flight Number');
26+
expect(decodeResult.formatted.items[1].value).toBe('RCH4086');
27+
expect(decodeResult.formatted.items[2].label).toBe('Free Text');
28+
expect(decodeResult.formatted.items[2].value).toBe('4 QTR PHILLY UP 37-6');
29+
expect(decodeResult.formatted.items[3].label).toBe('Message Checksum');
30+
expect(decodeResult.formatted.items[3].value).toBe('0x307a');
31+
expect(decodeResult.remaining.text).toBe('MR6,');
32+
});
33+
34+
test('decodes Label 2J', () => {
35+
// https://app.airframes.io/messages/4178362466
36+
const text = 'M74AMC4086FTX/ID50007B,RCH4086,ABB02R70E037/DC10022025,011728/MR049,/FXGOOD EVENING PLEASE PASS US THE SUPER BOWL SCORE WHEN ABLE. THANK YOU/FB1791/VR0328D70'
37+
const decodeResult = plugin.decode({ text: text });
38+
39+
expect(decodeResult.decoded).toBe(true);
40+
expect(decodeResult.decoder.decodeLevel).toBe('partial');
41+
expect(decodeResult.raw.mission_number).toBe('ABB02R70E037');
42+
expect(decodeResult.formatted.items.length).toBe(5);
43+
expect(decodeResult.formatted.items[0].label).toBe('Flight Number');
44+
expect(decodeResult.formatted.items[0].value).toBe('MC4086');
45+
expect(decodeResult.formatted.items[1].label).toBe('Tail');
46+
expect(decodeResult.formatted.items[1].value).toBe('50007B');
47+
expect(decodeResult.formatted.items[2].label).toBe('Flight Number');
48+
expect(decodeResult.formatted.items[2].value).toBe('RCH4086');
49+
expect(decodeResult.formatted.items[3].label).toBe('Free Text');
50+
expect(decodeResult.formatted.items[3].value).toBe('GOOD EVENING PLEASE PASS US THE SUPER BOWL SCORE WHEN ABLE. THANK YOU');
51+
expect(decodeResult.formatted.items[4].label).toBe('Message Checksum');
52+
expect(decodeResult.formatted.items[4].value).toBe('0x8d70');
53+
expect(decodeResult.remaining.text).toBe('M74A/MR049,/FB1791/VR032');
54+
});
55+
56+
test('decodes <invalid>', () => {
57+
58+
const text = 'FTX Bogus message';
59+
const decodeResult = plugin.decode({ text: text });
60+
61+
expect(decodeResult.decoded).toBe(false);
62+
expect(decodeResult.decoder.decodeLevel).toBe('none');
63+
expect(decodeResult.formatted.description).toBe('Free Text');
64+
expect(decodeResult.message.text).toBe(text);
65+
});
66+
});

lib/plugins/Label_1J_2J_FTX.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { DecoderPlugin } from '../DecoderPlugin';
2+
import { DecodeResult, Message, Options } from '../DecoderPluginInterface';
3+
import { H1Helper } from '../utils/h1_helper';
4+
import { ResultFormatter } from '../utils/result_formatter';
5+
6+
export class Label_1J_2J_FTX extends DecoderPlugin {
7+
name = 'label-1j-2j-ftx';
8+
qualifiers() { // eslint-disable-line class-methods-use-this
9+
return {
10+
labels: ['1J', '2J'],
11+
};
12+
}
13+
14+
decode(message: Message, options: Options = {}) : DecodeResult {
15+
let decodeResult = this.defaultResult();
16+
decodeResult.decoder.name = this.name;
17+
decodeResult.message = message;
18+
19+
const msg = message.text.replace(/\n|\r/g, "");
20+
const decoded = H1Helper.decodeH1Message(decodeResult, msg);
21+
decodeResult.decoded = decoded;
22+
23+
decodeResult.decoder.decodeLevel = !decodeResult.remaining.text ? 'full' : 'partial';
24+
if (decodeResult.formatted.items.length === 0) {
25+
if (options.debug) {
26+
console.log(`Decoder: Unknown 1J/2J message: ${message.text}`);
27+
}
28+
ResultFormatter.unknown(decodeResult, message.text);
29+
decodeResult.decoded = false;
30+
decodeResult.decoder.decodeLevel = 'none';
31+
}
32+
return decodeResult;
33+
}
34+
}
35+
36+
export default {};

lib/plugins/Label_H1_FTX.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { MessageDecoder } from '../MessageDecoder';
22
import { Label_H1 } from './Label_H1';
33

4-
describe('Label_H1 FPN', () => {
4+
describe('Label_H1 FTX', () => {
55

66
let plugin: Label_H1;
77

lib/plugins/Label_H1_PWI.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ describe('Label H1 PWI', () => {
99
plugin = new Label_H1(decoder);
1010
});
1111

12+
test('matches Label H1 Preamble PWI qualifiers', () => {
13+
expect(plugin.decode).toBeDefined();
14+
expect(plugin.name).toBe('label-h1');
15+
expect(plugin.qualifiers).toBeDefined();
16+
expect(plugin.qualifiers()).toEqual({
17+
labels: ['H1'],
18+
});
19+
});
20+
1221

1322
test('decodes Label H1 Preamble PWI valid', () => {
1423
const text = 'PWI/WD390,COLZI,258070.AWYAT,252071.IPTAY,250065.CHOPZ,244069.MGMRY,234065.CATLN,230060/WD340,COLZI,256073,340M41.AWYAT,252070,340M41.IPTAY,244059,340M41.CHOPZ,240059,340M41.MGMRY,232056,340M41.CATLN,218053,340M40/WD300,COLZI,256065.AWYAT,254062.IPTAY,250051.CHOPZ,248050.MGMRY,232044.CATLN,222047/WD240,COLZI,260045.AWYAT,258048.IPTAY,254043.CHOPZ,256041.MGMRY,238035.CATLN,226034/DD300214059.240214040.180236024.100250018:,,,,/CB300246040.240246017.180226015.1002100080338'

lib/plugins/official.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export * from './Label_15_FST';
1010
export * from './Label_16_N_Space';
1111
export * from './Label_16_POSA1';
1212
export * from './Label_16_TOD';
13+
export * from './Label_1J_2J_FTX';
1314
export * from './Label_1M_Slash';
1415
export * from './Label_1L_3-line';
1516
export * from './Label_1L_070';

lib/utils/h1_helper.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,17 @@ function processLR(decodeResult: DecodeResult, data: string[]) {
209209
function parseMessageType(decodeResult: DecodeResult, messageType: string): boolean {
210210
const parts = messageType.split('#');
211211
if (parts.length == 1) {
212-
const type = parts[0].substring(0, 3);
213-
if (type === 'POS' && parts[0].length !== 3) {
212+
if (parts[0].startsWith('POS')) {
214213
H1Helper.processPosition(decodeResult, parts[0].substring(3).split(','));
215-
} else if(parts[0].length > 3 && parts[0].slice(-3) === 'POS') {
216-
ResultFormatter.unknown(decodeResult, parts[0].substring(0, 4));
217-
ResultFormatter.flightNumber(decodeResult, parts[0].slice(4, -3));
218214
return processMessageType(decodeResult, 'POS');
215+
} else if(parts[0].length === 13) {
216+
if(processMessageType(decodeResult, parts[0].substring(10))) {
217+
ResultFormatter.unknown(decodeResult, parts[0].substring(0, 4));
218+
ResultFormatter.flightNumber(decodeResult, parts[0].slice(4, 10));
219+
return true;
220+
}
219221
}
220-
return processMessageType(decodeResult, type);
222+
return processMessageType(decodeResult, parts[0].substring(0,3));
221223
} else if (parts.length == 2) {
222224
if (parts[0].length > 0) {
223225
ResultFormatter.unknown(decodeResult, parts[0].substring(0, 4));

0 commit comments

Comments
 (0)