Skip to content
Open
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
3 changes: 2 additions & 1 deletion lib/MessageDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ export class MessageDecoder {
this.registerPlugin(new Plugins.Label_44_IN(this));
this.registerPlugin(new Plugins.Label_44_OFF(this));
this.registerPlugin(new Plugins.Label_44_ON(this));
this.registerPlugin(new Plugins.Label_44_POS(this));
this.registerPlugin(new Plugins.Label_44_POS(this));
this.registerPlugin(new Plugins.Label_44_Slash(this));
this.registerPlugin(new Plugins.Label_4A(this));
this.registerPlugin(new Plugins.Label_4A_01(this));
this.registerPlugin(new Plugins.Label_4A_DIS(this));
Expand Down
63 changes: 63 additions & 0 deletions lib/plugins/Label_44_Slash.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { MessageDecoder } from "../MessageDecoder";
import { Label_44_Slash } from "./Label_44_Slash";

describe("Label 44 Slash", () => {
let plugin: Label_44_Slash;

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

test("matches qualifiers", () => {
expect(plugin.decode).toBeDefined();
expect(plugin.name).toBe("label-44-slash");
expect(plugin.qualifiers).toBeDefined();
expect(plugin.qualifiers()).toEqual({
labels: ["44"],
preambles: [" /FB"],
});
});

test("decodes variant 1", () => {
// https://app.airframes.io/messages/3563679058
const text = " /FB 0160/AD KORH/N 38.655,W 75.325,JBU2834,INA03,KORH,2043";
const decodeResult = plugin.decode({ text: text });
expect(decodeResult.decoded).toBe(true);
expect(decodeResult.decoder.decodeLevel).toBe("partial");
expect(decodeResult.raw.flight_number).toBe("JBU2834");
expect(decodeResult.raw.arrival_icao).toBe("KORH");
expect(decodeResult.raw.eta_time).toBe(74580);
expect(decodeResult.raw.position.latitude).toBeCloseTo(38.655, 3);
expect(decodeResult.raw.position.longitude).toBeCloseTo(-75.325, 3);
expect(decodeResult.formatted.items.length).toBe(4);
expect(decodeResult.remaining.text).toBe(" /FB 0160,INA03");
});

test("decodes variant 2", () => {
const text =
" /FB ----/AD KTPA/N 27.971,W 82.558,JBU91,FLS03,KTPA,53988,53988,----,1943,1943,1,1L,VIS1L,0,2,0,,";
const decodeResult = plugin.decode({ text: text });
expect(decodeResult.decoded).toBe(true);
expect(decodeResult.decoder.decodeLevel).toBe("partial");
expect(decodeResult.raw.flight_number).toBe("JBU91");
expect(decodeResult.raw.arrival_icao).toBe("KTPA");
expect(decodeResult.raw.eta_time).toBe(196688);
expect(decodeResult.raw.arrival_runway).toBe("1L");
expect(decodeResult.raw.procedures[0].route.name).toBe("VIS1L");
expect(decodeResult.raw.fuel_remaining).toBe(1943);
expect(decodeResult.raw.position.latitude).toBeCloseTo(27.971, 3);
expect(decodeResult.raw.position.longitude).toBeCloseTo(-82.558, 3);
expect(decodeResult.formatted.items.length).toBe(7);
expect(decodeResult.remaining.text).toBe(" /FB ----,FLS03,53988,----,1943,1,0,2,0,,");
});

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);
});
});
85 changes: 85 additions & 0 deletions lib/plugins/Label_44_Slash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { DateTimeUtils } from "../DateTimeUtils";
import { DecoderPlugin } from "../DecoderPlugin";
import { DecodeResult, Message, Options } from "../DecoderPluginInterface";
import { FlightPlanUtils } from "../utils/flight_plan_utils";
import { ResultFormatter } from "../utils/result_formatter";

// Flight Briefing Report
export class Label_44_Slash extends DecoderPlugin {
name = "label-44-slash";

qualifiers() {
// eslint-disable-line class-methods-use-this
return {
labels: ["44"],
preambles: [" /FB"],
};
}

decode(message: Message, options: Options = {}): DecodeResult {
const decodeResult = this.defaultResult();
decodeResult.decoder.name = this.name;
decodeResult.formatted.description = "Flight Briefing";
decodeResult.message = message;

const fields = message.text.split("/");
if (fields.length !== 4) {
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;
}
ResultFormatter.unknownArr(decodeResult, fields.slice(0, 2), "/"); // 0 is a space
// 1 is the briefing id
// 2 is arrival airport, but repeated later
const data = fields[3].split(",");
if (data.length >= 6) {
if (options.debug) {
console.log(`Label 44 Slash: groups`);
console.log(data);
}

ResultFormatter.position(decodeResult, {
latitude:
(data[0].charAt(0) === "S" ? -1 : 1) *
parseFloat(data[0].slice(1).trim()),
longitude:
(data[1].charAt(0) === "W" ? -1 : 1) *
parseFloat(data[1].slice(1).trim()),
});
ResultFormatter.flightNumber(decodeResult, data[2]);
ResultFormatter.unknown(decodeResult, data[3]); // Status - need more info to decode
ResultFormatter.arrivalAirport(decodeResult, data[4]);
ResultFormatter.eta(
decodeResult,
DateTimeUtils.convertHHMMSSToTod(data[5]),
);
if (data.length === 18) {
ResultFormatter.unknownArr(decodeResult, data.slice(6, 8), ","); // 6 is repeated arrival airport
ResultFormatter.remainingFuel(decodeResult, parseFloat(data[8]));
ResultFormatter.unknownArr(decodeResult, data.slice(9, 11), ",");
ResultFormatter.arrivalRunway(decodeResult, data[11]);
FlightPlanUtils.addProcedure(decodeResult, data[12], "arrival");
ResultFormatter.unknownArr(decodeResult, data.slice(13, 18), ",");
}
} 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;
decodeResult.decoder.decodeLevel = "partial";

return decodeResult;
}
}

export default {};
1 change: 1 addition & 0 deletions lib/plugins/official.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export * from './Label_44_IN';
export * from './Label_44_OFF';
export * from './Label_44_ON';
export * from './Label_44_POS';
export * from './Label_44_Slash';
export * from './Label_4A';
export * from './Label_4A_01';
export * from './Label_4A_DIS';
Expand Down
48 changes: 24 additions & 24 deletions lib/utils/flight_plan_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ export class FlightPlanUtils {
// TODO: discuss how store commented out bits as both raw and formatted
switch (key) {
case 'A': // Arrival Procedure (?)
addProcedure(decodeResult, value, 'arrival');
FlightPlanUtils.addProcedure(decodeResult, value, 'arrival');
break;
case 'AA':
addArrivalAirport(decodeResult, value);
break;
case 'AP':
addProcedure(decodeResult, value, 'approach');
FlightPlanUtils.addProcedure(decodeResult, value, 'approach');
break;
case 'CR':
addCompanyRoute(decodeResult, value);
break;
case 'D': // Departure Procedure
addProcedure(decodeResult, value, 'departure');
FlightPlanUtils.addProcedure(decodeResult, value, 'departure');
break;
case 'DA':
addDepartureAirport(decodeResult, value);
Expand Down Expand Up @@ -92,6 +92,27 @@ export class FlightPlanUtils {
}
return allKnownFields;
};


public static addProcedure(decodeResult: DecodeResult, value: string, type: string) {
if (decodeResult.raw.procedures === undefined) {
decodeResult.raw.procedures = [];
}
const data = value.split('.');
let waypoints;
if (data.length > 1) {
waypoints = data.slice(1).map((leg) => RouteUtils.getWaypoint(leg));
}
const route = { name: data[0], waypoints: waypoints };
decodeResult.raw.procedures.push({ type: type, route: route });
const procedureName = type.substring(0, 1).toUpperCase() + type.slice(1);
decodeResult.formatted.items.push({
type: `procedure`,
code: 'proc',
label: `${procedureName} Procedure`,
value: RouteUtils.routeToString(route),
});
};
}

function addArrivalAirport(decodeResult: DecodeResult, value: string) {
Expand All @@ -116,27 +137,6 @@ function addRoute(decodeResult: DecodeResult, value: string) {
ResultFormatter.route(decodeResult, { waypoints: route.map((leg) => RouteUtils.getWaypoint(leg)) });
};

function addProcedure(decodeResult: DecodeResult, value: string, type: string) {
if (decodeResult.raw.procedures === undefined) {
decodeResult.raw.procedures = [];
}
const data = value.split('.');
let waypoints;
if (data.length > 1) {
waypoints = data.slice(1).map((leg) => RouteUtils.getWaypoint(leg));
}
const route = { name: data[0], waypoints: waypoints };
decodeResult.raw.procedures.push({ type: type, route: route });
const procedureName = type.substring(0, 1).toUpperCase() + type.slice(1);
let procedureValue = route.name;
decodeResult.formatted.items.push({
type: `procedure`,
code: 'proc',
label: `${procedureName} Procedure`,
value: RouteUtils.routeToString(route),
});
};

function addCompanyRoute(decodeResult: DecodeResult, value: string) {
const segments = value.split('.');
const parens_idx = segments[0].indexOf('(');
Expand Down