Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion lib/DateTimeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class DateTimeUtils {
* @returns seconds since midnight
*/
public static convertHHMMSSToTod(time: string): number {
if(time.length === 4) { // add seconds if not present
if(time.length === 4) {
time += '00';
}
const h = Number(time.substring(0, 2));
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