Skip to content

Commit 5e8d511

Browse files
committed
pull up c-band prefix
1 parent 1e877a4 commit 5e8d511

File tree

9 files changed

+100
-126
lines changed

9 files changed

+100
-126
lines changed

lib/MessageDecoder.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DecodeResult, DecoderPluginInterface, Message, Options } from './Decode
22

33
import * as Plugins from './plugins/official';
44
import { MIAMCoreUtils } from './utils/miam';
5+
import { ResultFormatter } from './utils/result_formatter';
56

67
export class MessageDecoder {
78
name: string;
@@ -95,6 +96,14 @@ export class MessageDecoder {
9596
}
9697
}
9798

99+
// C-Band puts a 10 char header in front of some message types
100+
// First 4 chars are some kind of message number
101+
// Last 6 chars are the flight number
102+
let cband = message.text.match(/^(?<msgno>[A-Z]\d{2}[A-Z])(?<airline>[A-Z]{2})(?<number>[0-9]{4})/);
103+
if (cband?.groups) {
104+
message.text = message.text.substring(10);
105+
}
106+
98107
// console.log('All plugins');
99108
// console.log(this.plugins);
100109
const usablePlugins = this.plugins.filter((plugin) => {
@@ -150,6 +159,11 @@ export class MessageDecoder {
150159
}
151160
}
152161

162+
if (cband?.groups) {
163+
ResultFormatter.flightNumber(result, cband.groups.airline + Number(cband.groups.number));
164+
message.text = cband.input;
165+
}
166+
153167
if (options.debug) {
154168
console.log('Result');
155169
console.log(result);

lib/plugins/Label_1L_Slash.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,4 @@ export class Label_1L_Slash extends DecoderPlugin { // eslint-disable-line camel
8080
}
8181
}
8282

83-
export default {};
83+
export default {};

lib/plugins/Label_30_Slash_EA.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ test('decodes Label 30 sample 1', () => {
1414
});
1515

1616
const text = '/EA1719/DSKSFO/SK23';
17-
const decodeResult = decoderPlugin.decode({ text: text });
17+
const decodeResult = decoder.decode({ label: "30", text: text });
1818

1919
expect(decodeResult.decoded).toBe(true);
2020
expect(decodeResult.decoder.decodeLevel).toBe('partial');

lib/plugins/Label_4A.test.ts

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,18 @@ test('matches Label 4A qualifiers', () => {
1515

1616
test('decodes Label 4A, variant 1', () => {
1717
const decoder = new MessageDecoder();
18-
const decoderPlugin = new Label_4A(decoder);
1918

2019
// https://app.airframes.io/messages/3451492279
2120
const text = '063200,1910,.N343FR,FFT2028,KSLC,KORD,1,0632,RT0,LT0,';
22-
const decodeResult = decoderPlugin.decode({ text: text });
21+
const decodeResult = decoder.decode({ label: "4A", text: text });
2322

2423
expect(decodeResult.decoded).toBe(true);
2524
expect(decodeResult.decoder.decodeLevel).toBe('partial');
2625
expect(decodeResult.decoder.name).toBe('label-4a');
2726
expect(decodeResult.formatted.description).toBe('Latest New Format');
2827
expect(decodeResult.message.text).toBe(text);
2928
expect(decodeResult.remaining.text).toBe('RT0,LT0,');
30-
expect(decodeResult.formatted.items.length).toBe(6);
29+
expect(decodeResult.formatted.items.length).toBe(5);
3130
expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD');
3231
expect(decodeResult.formatted.items[0].value).toBe('06:32:00');
3332
expect(decodeResult.formatted.items[1].code).toBe('TAIL');
@@ -42,19 +41,18 @@ test('decodes Label 4A, variant 1', () => {
4241

4342
test('decodes Label 4A, variant 1, no callsign', () => {
4443
const decoder = new MessageDecoder();
45-
const decoderPlugin = new Label_4A(decoder);
4644

4745
// https://app.airframes.io/messages/3452310240
4846
const text = '101606,1910,.N317FR,,KMDW,----,1,1016,RT0,LT1,';
49-
const decodeResult = decoderPlugin.decode({ text: text });
47+
const decodeResult = decoder.decode({ label: "4A", text: text });
5048

5149
expect(decodeResult.decoded).toBe(true);
5250
expect(decodeResult.decoder.decodeLevel).toBe('partial');
5351
expect(decodeResult.decoder.name).toBe('label-4a');
5452
expect(decodeResult.formatted.description).toBe('Latest New Format');
5553
expect(decodeResult.message.text).toBe(text);
5654
expect(decodeResult.remaining.text).toBe('RT0,LT1,');
57-
expect(decodeResult.formatted.items.length).toBe(5);
55+
expect(decodeResult.formatted.items.length).toBe(4);
5856
expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD');
5957
expect(decodeResult.formatted.items[0].value).toBe('10:16:06');
6058
expect(decodeResult.formatted.items[1].code).toBe('TAIL');
@@ -67,11 +65,10 @@ test('decodes Label 4A, variant 1, no callsign', () => {
6765

6866
test('decodes Label 4A, variant 2', () => {
6967
const decoder = new MessageDecoder();
70-
const decoderPlugin = new Label_4A(decoder);
7168

7269
// https://app.airframes.io/messages/3461807403
7370
const text = 'N45129W093113MSP/07 ,204436123VECTORS,,P04,268044858,46904221';
74-
const decodeResult = decoderPlugin.decode({ text: text });
71+
const decodeResult = decoder.decode({ label: "4A", text: text });
7572

7673
expect(decodeResult.decoded).toBe(true);
7774
expect(decodeResult.decoder.decodeLevel).toBe('partial');
@@ -92,11 +89,10 @@ test('decodes Label 4A, variant 2', () => {
9289

9390
test('decodes Label 4A, variant 2, C-Band', () => {
9491
const decoder = new MessageDecoder();
95-
const decoderPlugin = new Label_4A(decoder);
9692

9793
// https://app.airframes.io/messages/3461407615
9894
const text = 'M60ALH0752N22456E077014OSE35 ,192027370VEX36 ,192316,M46,275043309,85220111';
99-
const decodeResult = decoderPlugin.decode({ text: text });
95+
const decodeResult = decoder.decode({ label: "4A", text: text });
10096

10197
expect(decodeResult.decoded).toBe(true);
10298
expect(decodeResult.decoder.decodeLevel).toBe('partial');
@@ -105,25 +101,24 @@ test('decodes Label 4A, variant 2, C-Band', () => {
105101
expect(decodeResult.message.text).toBe(text);
106102
expect(decodeResult.remaining.text).toBe('275043309,85220111');
107103
expect(decodeResult.formatted.items.length).toBe(5);
108-
expect(decodeResult.formatted.items[0].code).toBe('FLIGHT');
109-
expect(decodeResult.formatted.items[0].value).toBe('LH752');
110-
expect(decodeResult.formatted.items[1].code).toBe('POS');
111-
expect(decodeResult.formatted.items[1].value).toBe('22.456 N, 77.014 E');
112-
expect(decodeResult.formatted.items[2].code).toBe('ALT');
113-
expect(decodeResult.formatted.items[2].value).toBe('37000 feet');
114-
expect(decodeResult.formatted.items[3].code).toBe('ROUTE');
115-
expect(decodeResult.formatted.items[3].value).toBe('OSE35@19:20:27 > VEX36@19:23:16');
116-
expect(decodeResult.formatted.items[4].code).toBe('OATEMP');
117-
expect(decodeResult.formatted.items[4].value).toBe('-46 degrees');
104+
expect(decodeResult.formatted.items[0].code).toBe('POS');
105+
expect(decodeResult.formatted.items[0].value).toBe('22.456 N, 77.014 E');
106+
expect(decodeResult.formatted.items[1].code).toBe('ALT');
107+
expect(decodeResult.formatted.items[1].value).toBe('37000 feet');
108+
expect(decodeResult.formatted.items[2].code).toBe('ROUTE');
109+
expect(decodeResult.formatted.items[2].value).toBe('OSE35@19:20:27 > VEX36@19:23:16');
110+
expect(decodeResult.formatted.items[3].code).toBe('OATEMP');
111+
expect(decodeResult.formatted.items[3].value).toBe('-46 degrees');
112+
expect(decodeResult.formatted.items[4].code).toBe('FLIGHT');
113+
expect(decodeResult.formatted.items[4].value).toBe('LH752');
118114
});
119115

120116
test('decodes Label 4A, variant 3', () => {
121117
const decoder = new MessageDecoder();
122-
const decoderPlugin = new Label_4A(decoder);
123118

124119
// https://globe.adsbexchange.com/?icao=A39AC6&showTrace=2024-09-22&timestamp=1727009085
125120
const text = '124442,1320, 138,33467,N 41.093,W 72.677';
126-
const decodeResult = decoderPlugin.decode({ text: text });
121+
const decodeResult = decoder.decode({ label: "4A", text: text });
127122

128123
expect(decodeResult.decoded).toBe(true);
129124
expect(decodeResult.decoder.decodeLevel).toBe('partial');
@@ -144,15 +139,14 @@ test('decodes Label 4A, variant 3', () => {
144139

145140
test('decodes Label 4A_DIS <invalid>', () => {
146141
const decoder = new MessageDecoder();
147-
const decoderPlugin = new Label_4A(decoder);
148142

149143
// https://app.airframes.io/messages/3449413366
150144
const text = 'DIS01,182103,WEN3100,WRONG CREW HAHAHA';
151-
const decodeResult = decoderPlugin.decode({ text: text });
145+
const decodeResult = decoder.decode({ label: "4A", text: text });
152146

153-
expect(decodeResult.decoded).toBe(false);
154-
expect(decodeResult.decoder.decodeLevel).toBe('none');
155-
expect(decodeResult.decoder.name).toBe('label-4a');
156-
expect(decodeResult.formatted.description).toBe('Latest New Format');
157-
expect(decodeResult.formatted.items.length).toBe(0);
147+
// expect(decodeResult.decoded).toBe(false);
148+
// expect(decodeResult.decoder.decodeLevel).toBe('none');
149+
expect(decodeResult.decoder.name).not.toBe('label-4a');
150+
// expect(decodeResult.formatted.description).toBe('Latest New Format');
151+
// expect(decodeResult.formatted.items.length).toBe(0);
158152
});

lib/plugins/Label_4A.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,8 @@ export class Label_4A extends DecoderPlugin {
2020
decodeResult.message = message;
2121
decodeResult.formatted.description = 'Latest New Format';
2222

23-
24-
// Inmarsat C-band seems to prefix normal messages with a message number and flight number
25-
let text = message.text;
26-
if (text.match(/^M\d{2}A\w{6}/)) {
27-
ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
28-
text = text.substring(10);
29-
}
30-
3123
decodeResult.decoded = true;
32-
const fields = text.split(",");
24+
const fields = message.text.split(",");
3325
if (fields.length === 11) {
3426
// variant 1
3527
ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0]));
@@ -38,7 +30,6 @@ export class Label_4A extends DecoderPlugin {
3830
ResultFormatter.callsign(decodeResult, fields[3]);
3931
ResultFormatter.departureAirport(decodeResult, fields[4]);
4032
ResultFormatter.arrivalAirport(decodeResult, fields[5]);
41-
ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100);
4233
ResultFormatter.unknownArr(decodeResult, fields.slice(8));
4334
} else if (fields.length === 6) {
4435
if (fields[0].match(/^[NS]/)) {
@@ -68,7 +59,7 @@ export class Label_4A extends DecoderPlugin {
6859
}
6960
} else {
7061
decodeResult.decoded = false;
71-
ResultFormatter.unknown(decodeResult, text);
62+
ResultFormatter.unknown(decodeResult, message.text);
7263
}
7364

7465
if (decodeResult.decoded) {

lib/plugins/Label_4N.test.ts

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ test('matches Label 4N qualifiers', () => {
1111

1212
test('decodes Label 4N variant 1', () => {
1313
const decoder = new MessageDecoder();
14-
const decoderPlugin = new Label_4N(decoder);
1514

1615
// https://globe.adsbexchange.com/?icao=A15027&showTrace=2024-09-23&timestamp=1727057017
1716
const text = '22024N MCI JFK1\r\n0013 0072 N040586 W074421 230';
18-
const decodeResult = decoderPlugin.decode({ text: text });
17+
const decodeResult = decoder.decode({ label: "4N", text: text });
1918

2019
expect(decodeResult.decoded).toBe(true);
2120
expect(decodeResult.decoder.decodeLevel).toBe('partial');
@@ -37,11 +36,10 @@ test('decodes Label 4N variant 1', () => {
3736

3837
test('decodes Label 4N variant 2B', () => {
3938
const decoder = new MessageDecoder();
40-
const decoderPlugin = new Label_4N(decoder);
4139

4240
// https://app.airframes.io/messages/3421601874
4341
const text = '285,B,69005074-507,10/12,+36.081,-094.810,35014,002.3,ELP,SDF,SDF,17R/,17L/,0,0,,,,,,0,0,0,0,1,,,,,247.0,014.2,261.2,421A';
44-
const decodeResult = decoderPlugin.decode({ text: text });
42+
const decodeResult = decoder.decode({ label: "4N", text: text });
4543

4644
expect(decodeResult.decoded).toBe(true);
4745
expect(decodeResult.decoder.decodeLevel).toBe('partial');
@@ -71,11 +69,10 @@ test('decodes Label 4N variant 2B', () => {
7169

7270
test('decodes Label 4N variant 2C', () => {
7371
const decoder = new MessageDecoder();
74-
const decoderPlugin = new Label_4N(decoder);
7572

7673
// https://globe.adsbexchange.com/?icao=A3E08D&showTrace=2024-09-24&timestamp=1727181643
7774
const text = '285,C,,09/24,,,,,EWR,PHL,PHL,09R/,/,0,0,,,,,,1,0,0,0,1,0,,0,0,198.5,014.5,213.0,9BCD';
78-
const decodeResult = decoderPlugin.decode({ text: text });
75+
const decodeResult = decoder.decode({ label: "4N", text: text });
7976

8077
expect(decodeResult.decoded).toBe(true);
8178
expect(decodeResult.decoder.decodeLevel).toBe('partial');
@@ -99,11 +96,10 @@ test('decodes Label 4N variant 2C', () => {
9996

10097
test('decodes Label 4N variant 2C (C-band)', () => {
10198
const decoder = new MessageDecoder();
102-
const decoderPlugin = new Label_4N(decoder);
10399

104100
// https://app.airframes.io/messages/3422221702
105101
const text = 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3';
106-
const decodeResult = decoderPlugin.decode({ text: text });
102+
const decodeResult = decoder.decode({ label: "4N", text: text });
107103

108104
expect(decodeResult.decoded).toBe(true);
109105
expect(decodeResult.decoder.decodeLevel).toBe('partial');
@@ -114,28 +110,27 @@ test('decodes Label 4N variant 2C (C-band)', () => {
114110
expect(decodeResult.raw.date).toBe('10/12');
115111
expect(decodeResult.remaining.text).toBe('C,0,0,0,0,0,0,1,0,0,0,709.8,048.7,758.5');
116112
expect(decodeResult.formatted.items.length).toBe(7);
117-
expect(decodeResult.formatted.items[0].code).toBe('FLIGHT');
118-
expect(decodeResult.formatted.items[0].value).toBe('UP109');
119-
expect(decodeResult.formatted.items[1].code).toBe('ORG');
120-
expect(decodeResult.formatted.items[1].value).toBe('NRT');
121-
expect(decodeResult.formatted.items[2].code).toBe('DST');
113+
expect(decodeResult.formatted.items[0].code).toBe('ORG');
114+
expect(decodeResult.formatted.items[0].value).toBe('NRT');
115+
expect(decodeResult.formatted.items[1].code).toBe('DST');
116+
expect(decodeResult.formatted.items[1].value).toBe('ANC');
117+
expect(decodeResult.formatted.items[2].code).toBe('ALT_DST');
122118
expect(decodeResult.formatted.items[2].value).toBe('ANC');
123-
expect(decodeResult.formatted.items[3].code).toBe('ALT_DST');
124-
expect(decodeResult.formatted.items[3].value).toBe('ANC');
125-
expect(decodeResult.formatted.items[4].code).toBe('ARWY');
126-
expect(decodeResult.formatted.items[4].value).toBe('07R');
127-
expect(decodeResult.formatted.items[5].code).toBe('ALT_ARWY');
128-
expect(decodeResult.formatted.items[5].value).toBe('33');
129-
expect(decodeResult.formatted.items[6].code).toBe('CHECKSUM');
130-
expect(decodeResult.formatted.items[6].value).toBe('0x75f3');
119+
expect(decodeResult.formatted.items[3].code).toBe('ARWY');
120+
expect(decodeResult.formatted.items[3].value).toBe('07R');
121+
expect(decodeResult.formatted.items[4].code).toBe('ALT_ARWY');
122+
expect(decodeResult.formatted.items[4].value).toBe('33');
123+
expect(decodeResult.formatted.items[5].code).toBe('CHECKSUM');
124+
expect(decodeResult.formatted.items[5].value).toBe('0x75f3');
125+
expect(decodeResult.formatted.items[6].code).toBe('FLIGHT');
126+
expect(decodeResult.formatted.items[6].value).toBe('UP109');
131127
});
132128

133129
test('decodes Label 4N <invalid>', () => {
134130
const decoder = new MessageDecoder();
135-
const decoderPlugin = new Label_4N(decoder);
136131

137132
const text = '4N Bogus message';
138-
const decodeResult = decoderPlugin.decode({ text: text });
133+
const decodeResult = decoder.decode({ label: "4N", text: text });
139134

140135
expect(decodeResult.decoded).toBe(false);
141136
expect(decodeResult.decoder.decodeLevel).toBe('none');

lib/plugins/Label_4N.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,16 @@ export class Label_4N extends DecoderPlugin {
1818
decodeResult.message = message;
1919
decodeResult.formatted.description = 'Airline Defined';
2020

21-
// Inmarsat C-band seems to prefix normal messages with a message number and flight number
22-
let text = message.text;
23-
if (text.match(/^M\d{2}A\w{6}/)) {
24-
ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1"));
25-
text = text.substring(10);
26-
}
27-
2821
decodeResult.decoded = true;
29-
const fields = text.split(",");
30-
if (text.length === 51) {
22+
const fields = message.text.split(",");
23+
if (message.text.length === 51) {
3124
// variant 1
32-
decodeResult.raw.day = text.substring(0, 2);
33-
ResultFormatter.departureAirport(decodeResult, text.substring(8, 11));
34-
ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16));
35-
ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(30, 45).replace(/^(.)0/, "$1")));
36-
ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100);
37-
ResultFormatter.unknownArr(decodeResult, [text.substring(2, 4), text.substring(19, 29)], " ");
25+
decodeResult.raw.day = message.text.substring(0, 2);
26+
ResultFormatter.departureAirport(decodeResult, message.text.substring(8, 11));
27+
ResultFormatter.arrivalAirport(decodeResult, message.text.substring(13, 16));
28+
ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(message.text.substring(30, 45).replace(/^(.)0/, "$1")));
29+
ResultFormatter.altitude(decodeResult, Number(messge.text.substring(48, 51)) * 100);
30+
ResultFormatter.unknownArr(decodeResult, [message.text.substring(2, 4), message.text.substring(19, 29)], " ");
3831
} else if (fields.length === 33) {
3932
// variant 2
4033
decodeResult.raw.date = fields[3];
@@ -53,7 +46,7 @@ export class Label_4N extends DecoderPlugin {
5346
ResultFormatter.unknownArr(decodeResult, [...fields.slice(1,3), fields[7], ...fields.slice(13, 32)].filter((f) => f != ""));
5447
} else {
5548
decodeResult.decoded = false;
56-
ResultFormatter.unknown(decodeResult, text);
49+
ResultFormatter.unknown(decodeResult, message.text);
5750
}
5851

5952
if (decodeResult.decoded) {

0 commit comments

Comments
 (0)