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
1 change: 1 addition & 0 deletions lib/MessageDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const pluginClasses = [
Plugins.Label_QQ,
Plugins.Label_QR,
Plugins.Label_QS,
Plugins.Label_52_AGM,
];

export class MessageDecoder {
Expand Down
121 changes: 121 additions & 0 deletions lib/plugins/Label_52_AGM.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { DecoderPlugin } from '../DecoderPlugin';
import { DecodeResult, Message, Options } from '../DecoderPluginInterface';

/**
* Label 52 — AGM (Ground UTC Request)
*
* Ground-to-air uplink asking the aircraft's ACARS Management Unit (MU)
* to report / synchronise its UTC clock. Paired with label 51 (Ground
* GMT Request response).
*
* Wire format (only the first three fields — the calendar date in
* YYMMDD — are decoded here; the remaining characters carry an
* anomalous time portion and a trailing byte that are not formally
* documented and are intentionally left unparsed per analyst guidance):
*
* 26 04 20 1091039
* | | | |
* | | | └ (ignored — undocumented time / status portion)
* | | └─── Day of month (DD)
* | └────── Month (MM)
* └───────── Year (YY, 20YY)
*/
export class Label_52_AGM extends DecoderPlugin {
name = 'label-52-agm';

qualifiers() {
return {
labels: ['52'],
};
}

decode(message: Message, options: Options = {}): DecodeResult {
const decodeResult = this.initResult(
message,
'AGM — Ground UTC Request',
);

const text = (message.text || '').trim();
if (!text || text.length < 6) {
this.setDecodeLevel(decodeResult, false);
return decodeResult;
}

// First 6 chars must be YYMMDD digits.
const m = text.match(/^(?<yy>\d{2})(?<mm>\d{2})(?<dd>\d{2})/);
if (!m?.groups) {
this.setDecodeLevel(decodeResult, false);
return decodeResult;
}

const { yy, mm, dd } = m.groups;
const monthN = Number(mm);
const dayN = Number(dd);
if (monthN < 1 || monthN > 12 || dayN < 1 || dayN > 31) {
this.setDecodeLevel(decodeResult, false);
return decodeResult;
}

const isoDate = `20${yy}-${mm}-${dd}`;
const monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
const monthName = monthNames[monthN - 1];

// ── raw ──
decodeResult.raw.year = Number(`20${yy}`);
decodeResult.raw.month = monthN;
decodeResult.raw.day = dayN;
decodeResult.raw.date = isoDate;

// ── formatted ──
decodeResult.formatted.items.unshift(
{
type: 'message_type',
code: 'MSGTYP',
label: 'Message Type',
value: 'AGM — Ground UTC Request',
},
{
type: 'direction',
code: 'DIR',
label: 'Direction',
value: 'Uplink (ground → aircraft)',
},
);
decodeResult.formatted.items.push(
{
type: 'year',
code: 'YEAR',
label: 'Year',
value: `20${yy}`,
},
{
type: 'month',
code: 'MONTH',
label: 'Month',
value: `${mm} (${monthName})`,
},
{
type: 'day',
code: 'DAY',
label: 'Day',
value: dd,
},
);

this.setDecodeLevel(decodeResult, true, 'full');
return decodeResult;
}
}
1 change: 1 addition & 0 deletions lib/plugins/official.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ export * from './Label_QR';
export * from './Label_QP';
export * from './Label_QS';
export * from './Label_QQ';
export * from './Label_52_AGM';