Skip to content
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

Fixing Label 44 decoding #188

Merged
merged 4 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion lib/DateTimeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ export class DateTimeUtils {

/**
*
* @param time HHMMSS
* @param time HHMMSS or HHMM
* @returns seconds since midnight
*/
public static convertHHMMSSToTod(time: string): number {
if(time.length === 4) {
time += '00';
}
const h = Number(time.substring(0, 2));
const m = Number(time.substring(2, 4));
const s = Number(time.substring(4, 6));
Expand Down
67 changes: 67 additions & 0 deletions lib/plugins/Label_44_ETA.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { MessageDecoder } from '../MessageDecoder';
import { Label_44_ETA } from './Label_44_ETA';

describe('Label 44 IN', () => {
let plugin: Label_44_ETA;

beforeEach(() => {
const decoder = new MessageDecoder();
plugin = new Label_44_ETA(decoder);
});

test('matches qualifiers', () => {
expect(plugin.decode).toBeDefined();
expect(plugin.name).toBe('label-44-eta');
expect(plugin.qualifiers).toBeDefined();
expect(plugin.qualifiers()).toEqual({
labels: ['44'],
preambles: ['00ETA01', '00ETA02', '00ETA03', 'ETA01', 'ETA02', 'ETA03'],
});
});

test('decodes variant 1', () => {
// https://app.airframes.io/messages/3569460297
const text = '00ETA03,N38241W081357,330,KBNA,KBWI,1107,0123,0208,008.1';
const decodeResult = plugin.decode({ text: text });
expect(decodeResult.decoded).toBe(true);
expect(decodeResult.decoder.decodeLevel).toBe('full');
expect(decodeResult.raw.position.latitude).toBe(38.401666666666664);
expect(decodeResult.raw.position.longitude).toBe(-81.595);
expect(decodeResult.raw.altitude).toBe(33000);
expect(decodeResult.raw.departure_icao).toBe('KBNA');
expect(decodeResult.raw.arrival_icao).toBe('KBWI');
expect(decodeResult.raw.month).toBe(11);
expect(decodeResult.raw.day).toBe(7);
expect(decodeResult.raw.time_of_day).toBe(4980);
expect(decodeResult.raw.eta_time).toBe(7680);
expect(decodeResult.formatted.items.length).toBe(9);
expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position');
expect(decodeResult.formatted.items[0].value).toBe('38.402 N, 81.595 W');
expect(decodeResult.formatted.items[1].label).toBe('Altitude');
expect(decodeResult.formatted.items[1].value).toBe('33000 feet');
expect(decodeResult.formatted.items[2].label).toBe('Origin');
expect(decodeResult.formatted.items[2].value).toBe('KBNA');
expect(decodeResult.formatted.items[3].label).toBe('Destination');
expect(decodeResult.formatted.items[3].value).toBe('KBWI');
expect(decodeResult.formatted.items[4].label).toBe('Month of Year');
expect(decodeResult.formatted.items[4].value).toBe('11');
expect(decodeResult.formatted.items[5].label).toBe('Day of Month');
expect(decodeResult.formatted.items[5].value).toBe('7');
expect(decodeResult.formatted.items[6].label).toBe('Message Timestamp');
expect(decodeResult.formatted.items[6].value).toBe('01:23:00');
expect(decodeResult.formatted.items[7].label).toBe('Estimated Time of Arrival');
expect(decodeResult.formatted.items[7].value).toBe('02:08:00');
expect(decodeResult.formatted.items[8].label).toBe('Fuel Remaining');
expect(decodeResult.formatted.items[8].value).toBe('8.1');
});

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

const text = '00OFF01 Bogus message';
const decodeResult = plugin.decode({ text: text });

expect(decodeResult.decoded).toBe(false);
expect(decodeResult.decoder.decodeLevel).toBe('none');
expect(decodeResult.message.text).toBe(text);
});
});
46 changes: 27 additions & 19 deletions lib/plugins/Label_44_ETA.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DateTimeUtils } from '../DateTimeUtils';
import { DecoderPlugin } from '../DecoderPlugin';
import { DecodeResult, Message, Options } from '../DecoderPluginInterface';
import { CoordinateUtils } from '../utils/coordinate_utils';
Expand All @@ -14,38 +15,45 @@ export class Label_44_ETA extends DecoderPlugin {
};
}

decode(message: Message, options: Options = {}) : DecodeResult {
decode(message: Message, options: Options = {}): DecodeResult {
const decodeResult = this.defaultResult();
decodeResult.decoder.name = this.name;
decodeResult.formatted.description = 'ETA Report';
decodeResult.message = message;

// Style: IN02,N38338W121179,KMHR,KPDX,0806,2355,005.1
// Match: IN02,coords,departure_icao,arrival_icao,current_date,current_time,fuel_in_tons
const regex = /^.*,(?<unsplit_coords>.*),(?<departure_icao>.*),(?<arrival_icao>.*),(?<current_date>.*),(?<current_time>.*),(?<fuel_in_tons>.*)$/;
const results = message.text.match(regex);
if (results?.groups) {
const data = message.text.split(',');
if (data.length >= 9) {
if (options.debug) {
console.log(`Label 44 ETA Report: groups`);
console.log(results.groups);
console.log(data);
}

ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(results.groups.unsplit_coords));
ResultFormatter.departureAirport(decodeResult, results.groups.departure_icao);
ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);
ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1]));
ResultFormatter.altitude(decodeResult, 100 * Number(data[2]));
ResultFormatter.departureAirport(decodeResult, data[3]);
ResultFormatter.arrivalAirport(decodeResult, data[4]);

decodeResult.raw.current_time = Date.parse(
new Date().getFullYear() + "-" +
results.groups.current_date.substr(0, 2) + "-" +
results.groups.current_date.substr(2, 2) + "T" +
results.groups.current_time.substr(0, 2) + ":" +
results.groups.current_time.substr(2, 2) + ":00Z"
);
ResultFormatter.month(decodeResult, Number(data[5].substring(0, 2)));
ResultFormatter.day(decodeResult, Number(data[5].substring(2, 4)));
ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[6]));
ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[7]));
const fuel = Number(data[8]);
if (!isNaN(fuel)) {
ResultFormatter.remainingFuel(decodeResult, Number(fuel));
}

if (results.groups.fuel_in_tons != '***' && results.groups.fuel_in_tons != '****') {
ResultFormatter.remainingFuel(decodeResult, Number(results.groups.fuel_in_tons));
if (data.length > 9) {
ResultFormatter.unknownArr(decodeResult, data.slice(9));
}

} else {
if (options.debug) {
console.log(`Decoder: Unknown 44 message: ${message.text}`);
}
ResultFormatter.unknown(decodeResult, message.text);
decodeResult.decoded = false;
decodeResult.decoder.decodeLevel = 'none';
return decodeResult;
}

decodeResult.decoded = true;
Expand Down
89 changes: 89 additions & 0 deletions lib/plugins/Label_44_IN.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { decode } from 'punycode';
import { MessageDecoder } from '../MessageDecoder';
import { Label_44_IN } from './Label_44_IN';

describe('Label 44 IN', () => {
let plugin: Label_44_IN;

beforeEach(() => {
const decoder = new MessageDecoder();
plugin = new Label_44_IN(decoder);
});

test('matches qualifiers', () => {
expect(plugin.decode).toBeDefined();
expect(plugin.name).toBe('label-44-in');
expect(plugin.qualifiers).toBeDefined();
expect(plugin.qualifiers()).toEqual({
labels: ['44'],
preambles: ['00IN01', '00IN02', '00IN03', 'IN01', 'IN02', 'IN03'],
});
});

test('decodes variant 1', () => {
// https://app.airframes.io/messages/3563679070
const text = 'IN01,N33528W084181,KCLT,KPDK,1106,0045,---.-'
const decodeResult = plugin.decode({ text: text });
expect(decodeResult.decoded).toBe(true);
expect(decodeResult.decoder.decodeLevel).toBe('full');
expect(decodeResult.raw.position.latitude).toBe(33.88);
expect(decodeResult.raw.position.longitude).toBe(-84.30166666666666);
expect(decodeResult.raw.departure_icao).toBe('KCLT');
expect(decodeResult.raw.arrival_icao).toBe('KPDK');
expect(decodeResult.raw.month).toBe(11);
expect(decodeResult.raw.day).toBe(6);
expect(decodeResult.raw.in_time).toBe(2700);
expect(decodeResult.formatted.items.length).toBe(6);
expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position');
expect(decodeResult.formatted.items[0].value).toBe('33.880 N, 84.302 W');
expect(decodeResult.formatted.items[1].label).toBe('Origin');
expect(decodeResult.formatted.items[1].value).toBe('KCLT');
expect(decodeResult.formatted.items[2].label).toBe('Destination');
expect(decodeResult.formatted.items[2].value).toBe('KPDK');
expect(decodeResult.formatted.items[3].label).toBe('Month of Year');
expect(decodeResult.formatted.items[3].value).toBe('11');
expect(decodeResult.formatted.items[4].label).toBe('Day of Month');
expect(decodeResult.formatted.items[4].value).toBe('6');
expect(decodeResult.formatted.items[5].label).toBe('In Gate Time');
expect(decodeResult.formatted.items[5].value).toBe('00:45:00');
});

test('decodes variant 2', () => {
const text = 'IN02,N38338W121179,KMHR,KPDX,0806,2355,005.1'
const decodeResult = plugin.decode({ text: text });
expect(decodeResult.decoded).toBe(true);
expect(decodeResult.decoder.decodeLevel).toBe('full');
expect(decodeResult.raw.position.latitude).toBe(38.56333333333333);
expect(decodeResult.raw.position.longitude).toBe(-121.29833333333333);
expect(decodeResult.raw.departure_icao).toBe('KMHR');
expect(decodeResult.raw.arrival_icao).toBe('KPDX');
expect(decodeResult.raw.month).toBe(8);
expect(decodeResult.raw.day).toBe(6);
expect(decodeResult.raw.in_time).toBe(86100);
expect(decodeResult.formatted.items.length).toBe(7);
expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position');
expect(decodeResult.formatted.items[0].value).toBe('38.563 N, 121.298 W');
expect(decodeResult.formatted.items[1].label).toBe('Origin');
expect(decodeResult.formatted.items[1].value).toBe('KMHR');
expect(decodeResult.formatted.items[2].label).toBe('Destination');
expect(decodeResult.formatted.items[2].value).toBe('KPDX');
expect(decodeResult.formatted.items[3].label).toBe('Month of Year');
expect(decodeResult.formatted.items[3].value).toBe('8');
expect(decodeResult.formatted.items[4].label).toBe('Day of Month');
expect(decodeResult.formatted.items[4].value).toBe('6');
expect(decodeResult.formatted.items[5].label).toBe('In Gate Time');
expect(decodeResult.formatted.items[5].value).toBe('23:55:00');
expect(decodeResult.formatted.items[6].label).toBe('Fuel Remaining');
expect(decodeResult.formatted.items[6].value).toBe('5.1');
});

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

const text = '00OFF01 Bogus message';
const decodeResult = plugin.decode({ text: text });

expect(decodeResult.decoded).toBe(false);
expect(decodeResult.decoder.decodeLevel).toBe('none');
expect(decodeResult.message.text).toBe(text);
});
});
45 changes: 25 additions & 20 deletions lib/plugins/Label_44_IN.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DateTimeUtils } from '../DateTimeUtils';
import { DecoderPlugin } from '../DecoderPlugin';
import { DecodeResult, Message, Options } from '../DecoderPluginInterface';
import { CoordinateUtils } from '../utils/coordinate_utils';
Expand All @@ -14,38 +15,42 @@ export class Label_44_IN extends DecoderPlugin {
};
}

decode(message: Message, options: Options = {}) : DecodeResult {
decode(message: Message, options: Options = {}): DecodeResult {
const decodeResult = this.defaultResult();
decodeResult.decoder.name = this.name;
decodeResult.formatted.description = 'In Air Report';
decodeResult.message = message;

// Style: IN02,N38338W121179,KMHR,KPDX,0806,2355,005.1
// Match: IN02,coords,departure_icao,arrival_icao,current_date,current_time,fuel_in_tons
const regex = /^.*,(?<unsplit_coords>.*),(?<departure_icao>.*),(?<arrival_icao>.*),(?<current_date>.*),(?<current_time>.*),(?<fuel_in_tons>.*)$/;
const results = message.text.match(regex);
if (results?.groups) {
const data = message.text.split(',');
if (data.length >= 7) {
if (options.debug) {
console.log(`Label 44 In Air Report: groups`);
console.log(results.groups);
console.log(data);
}

ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(results.groups.unsplit_coords));
ResultFormatter.departureAirport(decodeResult, results.groups.departure_icao);
ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao);

decodeResult.raw.current_time = Date.parse(
new Date().getFullYear() + "-" +
results.groups.current_date.substr(0, 2) + "-" +
results.groups.current_date.substr(2, 2) + "T" +
results.groups.current_time.substr(0, 2) + ":" +
results.groups.current_time.substr(2, 2) + ":00Z"
);
ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1]));
ResultFormatter.departureAirport(decodeResult, data[2]);
ResultFormatter.arrivalAirport(decodeResult, data[3]);
ResultFormatter.month(decodeResult, Number(data[4].substring(0, 2)));
ResultFormatter.day(decodeResult, Number(data[4].substring(2, 4)));
ResultFormatter.in(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[5]));
const fuel = Number(data[6]);
if (!isNaN(fuel)) {
ResultFormatter.remainingFuel(decodeResult, Number(fuel));
}

if (results.groups.fuel_in_tons != '***' && results.groups.fuel_in_tons != '****') {
decodeResult.raw.fuel_in_tons = Number(results.groups.fuel_in_tons);
if (data.length > 7) {
ResultFormatter.unknownArr(decodeResult, data.slice(7));
}

} else {
if (options.debug) {
console.log(`Decoder: Unknown 44 message: ${message.text}`);
}
ResultFormatter.unknown(decodeResult, message.text);
decodeResult.decoded = false;
decodeResult.decoder.decodeLevel = 'none';
return decodeResult;
}

decodeResult.decoded = true;
Expand Down
65 changes: 65 additions & 0 deletions lib/plugins/Label_44_OFF.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { decode } from 'punycode';
import { MessageDecoder } from '../MessageDecoder';
import { Label_44_OFF } from './Label_44_OFF';

describe('Label 44 OFF', () => {
let plugin: Label_44_OFF;

beforeEach(() => {
const decoder = new MessageDecoder();
plugin = new Label_44_OFF(decoder);
});

test('matches qualifiers', () => {
expect(plugin.decode).toBeDefined();
expect(plugin.name).toBe('label-44-off');
expect(plugin.qualifiers).toBeDefined();
expect(plugin.qualifiers()).toEqual({
labels: ['44'],
preambles: ['00OFF01', '00OFF02', '00OFF03', 'OFF01', 'OFF02', 'OFF03'],
});
});

test('decodes variant 1', () => {
const text = 'OFF02,N39247W077226,KFDK,KSNA,1106,2124,0248,011.1'
const decodeResult = plugin.decode({ text: text });
expect(decodeResult.decoded).toBe(true);
expect(decodeResult.decoder.decodeLevel).toBe('full');
expect(decodeResult.raw.position.latitude).toBe(39.41166666666667);
expect(decodeResult.raw.position.longitude).toBe(-77.37666666666667);
expect(decodeResult.raw.departure_icao).toBe('KFDK');
expect(decodeResult.raw.arrival_icao).toBe('KSNA');
expect(decodeResult.raw.month).toBe(11);
expect(decodeResult.raw.day).toBe(6);
expect(decodeResult.raw.off_time).toBe(77040);
expect(decodeResult.raw.eta_time).toBe(10080);
expect(decodeResult.raw.fuel_remaining).toBe(11.1);
expect(decodeResult.formatted.items.length).toBe(8);
expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position');
expect(decodeResult.formatted.items[0].value).toBe('39.412 N, 77.377 W');
expect(decodeResult.formatted.items[1].label).toBe('Origin');
expect(decodeResult.formatted.items[1].value).toBe('KFDK');
expect(decodeResult.formatted.items[2].label).toBe('Destination');
expect(decodeResult.formatted.items[2].value).toBe('KSNA');
expect(decodeResult.formatted.items[3].label).toBe('Month of Year');
expect(decodeResult.formatted.items[3].value).toBe('11');
expect(decodeResult.formatted.items[4].label).toBe('Day of Month');
expect(decodeResult.formatted.items[4].value).toBe('6');
expect(decodeResult.formatted.items[5].label).toBe('Takeoff Time');
expect(decodeResult.formatted.items[5].value).toBe('21:24:00');
expect(decodeResult.formatted.items[6].label).toBe('Estimated Time of Arrival');
expect(decodeResult.formatted.items[6].value).toBe('02:48:00');
expect(decodeResult.formatted.items[7].label).toBe('Fuel Remaining');
expect(decodeResult.formatted.items[7].value).toBe('11.1');
});

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

const text = '00OFF01 Bogus message';
const decodeResult = plugin.decode({ text: text });

expect(decodeResult.decoded).toBe(false);
expect(decodeResult.decoder.decodeLevel).toBe('none');
expect(decodeResult.message.text).toBe(text);
});
});
Loading
Loading