From 73f2bcbd778bb98edbbed727c012d9cde39a109a Mon Sep 17 00:00:00 2001 From: Mark Millward Date: Tue, 23 Jun 2026 08:40:50 -0400 Subject: [PATCH 1/3] Add ACARS free-text cleanup helper --- lib/plugins/Label_5Z_Slash.ts | 16 +++++++---- lib/utils/acars_free_text.test.ts | 13 +++++++++ lib/utils/acars_free_text.ts | 4 +++ lib/utils/emergency_squawk.test.ts | 44 ++++++++++++++++++++++++++++++ lib/utils/emergency_squawk.ts | 27 ++++++++++++++++++ 5 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 lib/utils/acars_free_text.test.ts create mode 100644 lib/utils/acars_free_text.ts create mode 100644 lib/utils/emergency_squawk.test.ts create mode 100644 lib/utils/emergency_squawk.ts diff --git a/lib/plugins/Label_5Z_Slash.ts b/lib/plugins/Label_5Z_Slash.ts index 7329b5b4..88d801a1 100644 --- a/lib/plugins/Label_5Z_Slash.ts +++ b/lib/plugins/Label_5Z_Slash.ts @@ -2,6 +2,7 @@ import { DateTimeUtils } from '../DateTimeUtils'; import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { ResultFormatter } from '../utils/result_formatter'; +import { formatAcarsFreeText } from '../utils/acars_free_text'; export class Label_5Z_Slash extends DecoderPlugin { name = 'label-5z-slash'; @@ -48,12 +49,15 @@ export class Label_5Z_Slash extends DecoderPlugin { const lines = message.text.split('\r\n'); if (lines[0] === '/TXT') { - // not UA, but starts with `/` - ResultFormatter.text(decodeResult, lines.slice(1).join('\r\n')); - decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = 'full'; - return decodeResult; - } + // not UA, but starts with `/` + ResultFormatter.text( + decodeResult, + formatAcarsFreeText(lines.slice(1).join('\r\n')), + ); + decodeResult.decoded = true; + decodeResult.decoder.decodeLevel = 'full'; + return decodeResult; +} const data = lines[0].split('/'); const header = data[1].split(' '); //data[0] is blank diff --git a/lib/utils/acars_free_text.test.ts b/lib/utils/acars_free_text.test.ts new file mode 100644 index 00000000..8322fff2 --- /dev/null +++ b/lib/utils/acars_free_text.test.ts @@ -0,0 +1,13 @@ +import { formatAcarsFreeText } from './acars_free_text'; + +describe('acars free text helper', () => { + test('cleans whitespace', () => { + expect(formatAcarsFreeText(' HELLO WORLD ')).toBe('HELLO WORLD'); + }); + + test('handles empty input', () => { + expect(formatAcarsFreeText('')).toBe(''); + expect(formatAcarsFreeText(null)).toBe(''); + expect(formatAcarsFreeText(undefined)).toBe(''); + }); +}); \ No newline at end of file diff --git a/lib/utils/acars_free_text.ts b/lib/utils/acars_free_text.ts new file mode 100644 index 00000000..1e3b2bf8 --- /dev/null +++ b/lib/utils/acars_free_text.ts @@ -0,0 +1,4 @@ +export function formatAcarsFreeText(text?: string | null): string { + if (!text) return ''; + return text.trim().replace(/\s+/g, ' '); +} \ No newline at end of file diff --git a/lib/utils/emergency_squawk.test.ts b/lib/utils/emergency_squawk.test.ts new file mode 100644 index 00000000..d918c7b3 --- /dev/null +++ b/lib/utils/emergency_squawk.test.ts @@ -0,0 +1,44 @@ +import { + getEmergencySquawkInfo, + isEmergencySquawk, + formatEmergencySquawk, +} from './emergency_squawk'; + +describe('emergency squawk helpers', () => { + test('recognizes 7500', () => { + expect(isEmergencySquawk(7500)).toBe(true); + expect(getEmergencySquawkInfo(7500)).toEqual({ + code: 7500, + meaning: 'Unlawful interference / hijacking', + }); + expect(formatEmergencySquawk(7500)).toBe( + '7500: Unlawful interference / hijacking', + ); + }); + + test('recognizes 7600', () => { + expect(isEmergencySquawk(7600)).toBe(true); + expect(getEmergencySquawkInfo(7600)).toEqual({ + code: 7600, + meaning: 'Radio communication failure', + }); + expect(formatEmergencySquawk(7600)).toBe( + '7600: Radio communication failure', + ); + }); + + test('recognizes 7700', () => { + expect(isEmergencySquawk(7700)).toBe(true); + expect(getEmergencySquawkInfo(7700)).toEqual({ + code: 7700, + meaning: 'General emergency', + }); + expect(formatEmergencySquawk(7700)).toBe('7700: General emergency'); + }); + + test('rejects unknown codes', () => { + expect(isEmergencySquawk(1234)).toBe(false); + expect(getEmergencySquawkInfo(1234)).toBeNull(); + expect(formatEmergencySquawk(1234)).toBe('Unknown squawk code: 1234'); + }); +}); \ No newline at end of file diff --git a/lib/utils/emergency_squawk.ts b/lib/utils/emergency_squawk.ts new file mode 100644 index 00000000..139f5de7 --- /dev/null +++ b/lib/utils/emergency_squawk.ts @@ -0,0 +1,27 @@ +export type EmergencySquawkCode = 7500 | 7600 | 7700; + +export interface EmergencySquawkInfo { + code: number; + meaning: string; +} + +const SQUAWK_MAP: Record = { + 7500: 'Unlawful interference / hijacking', + 7600: 'Radio communication failure', + 7700: 'General emergency', +}; + +export function getEmergencySquawkInfo(code: number): EmergencySquawkInfo | null { + const meaning = SQUAWK_MAP[code]; + if (!meaning) return null; + return { code, meaning }; +} + +export function isEmergencySquawk(code: number): boolean { + return code === 7500 || code === 7600 || code === 7700; +} + +export function formatEmergencySquawk(code: number): string { + const info = getEmergencySquawkInfo(code); + return info ? `${info.code}: ${info.meaning}` : `Unknown squawk code: ${code}`; +} \ No newline at end of file From 719b69139b81d9a0c8498c9b19fabaa790c933a9 Mon Sep 17 00:00:00 2001 From: Mark Millward Date: Tue, 23 Jun 2026 09:16:39 -0400 Subject: [PATCH 2/3] Address PR review feedback --- lib/plugins/Label_5Z_Slash.ts | 18 +++++++++--------- lib/utils/acars_free_text.test.ts | 2 +- lib/utils/acars_free_text.ts | 2 +- lib/utils/emergency_squawk.test.ts | 2 +- lib/utils/emergency_squawk.ts | 12 ++++++++---- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/plugins/Label_5Z_Slash.ts b/lib/plugins/Label_5Z_Slash.ts index 88d801a1..6249162d 100644 --- a/lib/plugins/Label_5Z_Slash.ts +++ b/lib/plugins/Label_5Z_Slash.ts @@ -49,15 +49,15 @@ export class Label_5Z_Slash extends DecoderPlugin { const lines = message.text.split('\r\n'); if (lines[0] === '/TXT') { - // not UA, but starts with `/` - ResultFormatter.text( - decodeResult, - formatAcarsFreeText(lines.slice(1).join('\r\n')), - ); - decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = 'full'; - return decodeResult; -} + // not UA, but starts with `/` + ResultFormatter.text( + decodeResult, + formatAcarsFreeText(lines.slice(1).join('\r\n')), + ); + decodeResult.decoded = true; + decodeResult.decoder.decodeLevel = 'full'; + return decodeResult; + } const data = lines[0].split('/'); const header = data[1].split(' '); //data[0] is blank diff --git a/lib/utils/acars_free_text.test.ts b/lib/utils/acars_free_text.test.ts index 8322fff2..f3b33e06 100644 --- a/lib/utils/acars_free_text.test.ts +++ b/lib/utils/acars_free_text.test.ts @@ -10,4 +10,4 @@ describe('acars free text helper', () => { expect(formatAcarsFreeText(null)).toBe(''); expect(formatAcarsFreeText(undefined)).toBe(''); }); -}); \ No newline at end of file +}); diff --git a/lib/utils/acars_free_text.ts b/lib/utils/acars_free_text.ts index 1e3b2bf8..c0c28f1c 100644 --- a/lib/utils/acars_free_text.ts +++ b/lib/utils/acars_free_text.ts @@ -1,4 +1,4 @@ export function formatAcarsFreeText(text?: string | null): string { if (!text) return ''; return text.trim().replace(/\s+/g, ' '); -} \ No newline at end of file +} diff --git a/lib/utils/emergency_squawk.test.ts b/lib/utils/emergency_squawk.test.ts index d918c7b3..d9529020 100644 --- a/lib/utils/emergency_squawk.test.ts +++ b/lib/utils/emergency_squawk.test.ts @@ -41,4 +41,4 @@ describe('emergency squawk helpers', () => { expect(getEmergencySquawkInfo(1234)).toBeNull(); expect(formatEmergencySquawk(1234)).toBe('Unknown squawk code: 1234'); }); -}); \ No newline at end of file +}); diff --git a/lib/utils/emergency_squawk.ts b/lib/utils/emergency_squawk.ts index 139f5de7..bb230714 100644 --- a/lib/utils/emergency_squawk.ts +++ b/lib/utils/emergency_squawk.ts @@ -11,17 +11,21 @@ const SQUAWK_MAP: Record = { 7700: 'General emergency', }; -export function getEmergencySquawkInfo(code: number): EmergencySquawkInfo | null { +export function getEmergencySquawkInfo( + code: number, +): EmergencySquawkInfo | null { const meaning = SQUAWK_MAP[code]; if (!meaning) return null; return { code, meaning }; } export function isEmergencySquawk(code: number): boolean { - return code === 7500 || code === 7600 || code === 7700; + return code in SQUAWK_MAP; } export function formatEmergencySquawk(code: number): string { const info = getEmergencySquawkInfo(code); - return info ? `${info.code}: ${info.meaning}` : `Unknown squawk code: ${code}`; -} \ No newline at end of file + return info + ? `${info.code}: ${info.meaning}` + : `Unknown squawk code: ${code}`; +} From 486a35c91517c15d39730a017dcfb29b4a796b44 Mon Sep 17 00:00:00 2001 From: Mark Millward Date: Tue, 23 Jun 2026 09:28:11 -0400 Subject: [PATCH 3/3] Address PR review feedback --- lib/plugins/Label_5Z_Slash.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/Label_5Z_Slash.ts b/lib/plugins/Label_5Z_Slash.ts index 6249162d..cac75d98 100644 --- a/lib/plugins/Label_5Z_Slash.ts +++ b/lib/plugins/Label_5Z_Slash.ts @@ -1,8 +1,8 @@ import { DateTimeUtils } from '../DateTimeUtils'; import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { ResultFormatter } from '../utils/result_formatter'; import { formatAcarsFreeText } from '../utils/acars_free_text'; +import { ResultFormatter } from '../utils/result_formatter'; export class Label_5Z_Slash extends DecoderPlugin { name = 'label-5z-slash';