From 5a6ea2e993886a45e5f1e7834c6d29afe02d1529 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 21 Oct 2025 06:21:41 +0000 Subject: [PATCH 1/9] Optimize IcaoDecoder.isMilitary() by eliminating regex recompilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed a critical performance issue where 30+ regex patterns were being compiled on every call to isMilitary(). The patterns are now compiled once as static readonly class fields and reused via .test() method. Performance impact: - Before: 30+ regex compilations per isMilitary() call - After: Zero regex compilations (patterns compiled once at class load) This significantly improves performance for high-throughput ACARS message processing scenarios. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lib/IcaoDecoder.ts | 173 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 136 insertions(+), 37 deletions(-) diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index b8eb415..d9b49e4 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -2,13 +2,112 @@ export class IcaoDecoder { name : string; icao : string; + // Pre-compiled regex patterns for military ICAO address detection (performance optimization) + private static readonly MILITARY_PATTERNS = { + // US military + US_MIL_1: /^adf[7-9]/, + US_MIL_2: /^adf[a-f]/, + US_MIL_3: /^a[ef]/, + + // Egypt military + EGYPT_MIL: /^0100[78]/, + + // Algeria military + ALGERIA_MIL: /^0a4/, + + // Italy military + ITALY_MIL: /^33ff/, + + // France military + FRANCE_MIL_1: /^3a[89a-f]/, + FRANCE_MIL_2: /^3b/, + + // Germany military + GERMANY_MIL_1: /^3e[ab]/, + GERMANY_MIL_2: /^3f[4-9ab]/, + + // UK military + UK_MIL_1: /^4000[0-3]/, + UK_MIL_2: /^43c/, + + // Austria military + AUSTRIA_MIL: /^44[4-7]/, + + // Belgium military + BELGIUM_MIL: /^44f/, + + // Bulgaria military + BULGARIA_MIL: /^457/, + + // Denmark military + DENMARK_MIL: /^45f4/, + + // Greece military + GREECE_MIL: /^468[0-3]/, + + // Hungary military + HUNGARY_MIL: /^473c0/, + + // Norway military + NORWAY_MIL: /^4781/, + + // Netherlands military + NETHERLANDS_MIL: /^480/, + + // Poland military + POLAND_MIL: /^48d8[0-7]/, + + // Portugal military + PORTUGAL_MIL: /^497c/, + + // Czech Republic military + CZECH_MIL: /^49842/, + + // Switzerland military + SWITZERLAND_MIL: /^4b7/, + + // Turkey military + TURKEY_MIL: /^4b82/, + + // Slovenia military + SLOVENIA_MIL: /^506f/, + + // Oman military + OMAN_MIL: /^70c07/, + + // Saudi Arabia military + SAUDI_MIL_1: /^7102[5-8]/, + SAUDI_MIL_2: /^7103[89]/, + + // Israel military + ISRAEL_MIL: /^738a/, + + // Australia military + AUSTRALIA_MIL_1: /^7c8[2-48]/, + AUSTRALIA_MIL_2: /^7[def]/, + + // India military + INDIA_MIL: /^8002/, + + // Canada military + CANADA_MIL: /^c[23]/, + + // Brazil military + BRAZIL_MIL: /^e4[01]/, + + // Chile military + CHILE_MIL: /^e806/, + }; + constructor(icao: string) { this.name = 'icao-decoder-typescript'; this.icao = icao; } isMilitary() { - let i = this.icao; + const i = this.icao; + const p = IcaoDecoder.MILITARY_PATTERNS; + return ( false // us military @@ -16,89 +115,89 @@ export class IcaoDecoder { //adf7d0-adf7df = united states mil_4(uf) //adf7e0-adf7ff = united states mil_3(uf) //adf800-adffff = united states mil_2(uf) - || i.match(/^adf[7-9]/) - || i.match(/^adf[a-f]/) + || p.US_MIL_1.test(i) + || p.US_MIL_2.test(i) //ae0000-afffff = united states mil_1(uf) - || i.match(/^a(e|f)/) + || p.US_MIL_3.test(i) //010070-01008f = egypt_mil - || i.match(/^0100(7|8)/) + || p.EGYPT_MIL.test(i) //0a4000-0a4fff = algeria mil(ap) - || i.match(/^0a4/) + || p.ALGERIA_MIL.test(i) //33ff00-33ffff = italy mil(iy) - || i.match(/^33ff/) + || p.ITALY_MIL.test(i) //350000-37ffff = spain mil(sp) || (i >= '350000' && i <= '37ffff') //3a8000-3affff = france mil_1(fs) - || i.match(/^3a(8|9|[a-f])/) + || p.FRANCE_MIL_1.test(i) //3b0000-3bffff = france mil_2(fs) - || i.match(/^3b/) + || p.FRANCE_MIL_2.test(i) //3e8000-3ebfff = germany mil_1(df) // remove 8 and 9 from mil arnge - || i.match(/^3e(a|b)/) + || p.GERMANY_MIL_1.test(i) //3f4000-3f7fff = germany mil_2(df) //3f8000-3fbfff = germany mil_3(df) - || i.match(/^3f([4-9]|[a-b])/) + || p.GERMANY_MIL_2.test(i) //400000-40003f = united kingdom mil_1(ra) - || i.match(/^4000[0-3]/) + || p.UK_MIL_1.test(i) //43c000-43cfff = united kingdom mil(ra) - || i.match(/^43c/) + || p.UK_MIL_2.test(i) //444000-447fff = austria mil(aq) - || (i.match(/^44[4-7]/) && i != '447ac7') + || (p.AUSTRIA_MIL.test(i) && i != '447ac7') //44f000-44ffff = belgium mil(bc) - || i.match(/^44f/) + || p.BELGIUM_MIL.test(i) //457000-457fff = bulgaria mil(bu) - || i.match(/^457/) + || p.BULGARIA_MIL.test(i) //45f400-45f4ff = denmark mil(dg) - || i.match(/^45f4/) + || p.DENMARK_MIL.test(i) //468000-4683ff = greece mil(gc) - || i.match(/^468[0-3]/) + || p.GREECE_MIL.test(i) //473c00-473c0f = hungary mil(hm) - || i.match(/^473c0/) + || p.HUNGARY_MIL.test(i) //478100-4781ff = norway mil(nn) - || i.match(/^4781/) + || p.NORWAY_MIL.test(i) //480000-480fff = netherlands mil(nm) - || i.match(/^480/) + || p.NETHERLANDS_MIL.test(i) //48d800-48d87f = poland mil(po) - || i.match(/^48d8[0-7]/) + || p.POLAND_MIL.test(i) //497c00-497cff = portugal mil(pu) - || i.match(/^497c/) + || p.PORTUGAL_MIL.test(i) //498420-49842f = czech republic mil(ct) - || i.match(/^49842/) + || p.CZECH_MIL.test(i) //4b7000-4b7fff = switzerland mil(su) - || i.match(/^4b7/) + || p.SWITZERLAND_MIL.test(i) //4b8200-4b82ff = turkey mil(tq) - || i.match(/^4b82/) + || p.TURKEY_MIL.test(i) //506f00-506fff = slovenia mil(sj) - || i.match(/^506f/) + || p.SLOVENIA_MIL.test(i) //70c070-70c07f = oman mil(on) - || i.match(/^70c07/) + || p.OMAN_MIL.test(i) //710258-71025f = saudi arabia mil_1(sx) //710260-71027f = saudi arabia mil_2(sx) //710280-71028f = saudi arabia mil_3(sx) //710380-71039f = saudi arabia mil_4(sx) - || i.match(/^7102[5-8]/) - || i.match(/^7103[8-9]/) + || p.SAUDI_MIL_1.test(i) + || p.SAUDI_MIL_2.test(i) //738a00-738aff = israel mil(iz) - || i.match(/^738a/) + || p.ISRAEL_MIL.test(i) //7c822e-7c822f = australia mil_1(av) //7c8230-7c823f = australia mil_2(av) @@ -107,26 +206,26 @@ export class IcaoDecoder { //7c8300-7c83ff = australia mil_5(av) //7c8400-7c87ff = australia mil_6(av) //7c8800-7c8fff = australia mil_7(av) - || i.match(/^7c8([2-4]|8)/) + || p.AUSTRALIA_MIL_1.test(i) //7c9000-7c9fff = australia mil_8(av) //7ca000-7cbfff = australia mil_9(av) || (i >= '7c9000' && i <= '7cbfff') //7cc000-7cffff = australia mil_10(av) 7cc409 not mil, remove this range //7d0000-7dffff = australia mil_11(av) //7e0000-7fffff = australia mil_12(av) - || i.match(/^7[d-f]/) + || p.AUSTRALIA_MIL_2.test(i) //800200-8002ff = india mil(im) - || i.match(/^8002/) + || p.INDIA_MIL.test(i) //c20000-c3ffff = canada mil(cb) - || i.match(/^c[2-3]/) + || p.CANADA_MIL.test(i) //e40000-e41fff = brazil mil(bq) - || i.match(/^e4[0-1]/) + || p.BRAZIL_MIL.test(i) //e80600-e806ff = chile mil(cq) - || i.match(/^e806/) + || p.CHILE_MIL.test(i) ); } From cb805d953a8b6829c583fdc57056bd80a71e4700 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sun, 7 Dec 2025 18:35:30 -0500 Subject: [PATCH 2/9] adding tests --- lib/IcaoDecoder.test.ts | 293 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 lib/IcaoDecoder.test.ts diff --git a/lib/IcaoDecoder.test.ts b/lib/IcaoDecoder.test.ts new file mode 100644 index 0000000..dc3c95b --- /dev/null +++ b/lib/IcaoDecoder.test.ts @@ -0,0 +1,293 @@ +import { IcaoDecoder } from './IcaoDecoder'; + +describe('IcaoDecoder', () => { + it('should set name and icao properties', () => { + const decoder = new IcaoDecoder('adf7c8'); + expect(decoder.name).toBe('icao-decoder-typescript'); + expect(decoder.icao).toBe('adf7c8'); + }); + + describe('isMilitary', () => { + // US military + it('should match adf7c8-adf7cf', () => { + expect(!!new IcaoDecoder('adf7c8').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('adf7cf').isMilitary()).toBe(true); + }); + it('should match adf7d0-adf7df', () => { + expect(!!new IcaoDecoder('adf7d0').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('adf7df').isMilitary()).toBe(true); + }); + it('should match adf7e0-adf7ff', () => { + expect(!!new IcaoDecoder('adf7e0').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('adf7ff').isMilitary()).toBe(true); + }); + it('should match adf800-adffff', () => { + expect(!!new IcaoDecoder('adf800').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('adffff').isMilitary()).toBe(true); + }); + it('should match ae0000-afffff', () => { + expect(!!new IcaoDecoder('ae0000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('afffff').isMilitary()).toBe(true); + }); + + // Egypt + it('should match 010070-01008f', () => { + expect(!!new IcaoDecoder('010070').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('01008f').isMilitary()).toBe(true); + }); + + // Algeria + it('should match 0a4000-0a4fff', () => { + expect(!!new IcaoDecoder('0a4000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('0a4fff').isMilitary()).toBe(true); + }); + + // Italy + it('should match 33ff00-33ffff', () => { + expect(!!new IcaoDecoder('33ff00').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('33ffff').isMilitary()).toBe(true); + }); + + // Spain + it('should match 350000-37ffff', () => { + expect(!!new IcaoDecoder('350000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('37ffff').isMilitary()).toBe(true); + }); + it('should not match 349fff', () => { + expect(!!new IcaoDecoder('349fff').isMilitary()).toBe(false); + }); + it('should not match 380000', () => { + expect(!!new IcaoDecoder('380000').isMilitary()).toBe(false); + }); + + // France + it('should match 3a8000-3affff', () => { + expect(!!new IcaoDecoder('3a8000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('3affff').isMilitary()).toBe(true); + }); + it('should match 3b0000-3bffff', () => { + expect(!!new IcaoDecoder('3b0000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('3bffff').isMilitary()).toBe(true); + }); + + // Germany + it('should match 3e8000-3ebfff', () => { + expect(!!new IcaoDecoder('3ea000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('3ebfff').isMilitary()).toBe(true); + }); + it('should match 3f4000-3f7fff', () => { + expect(!!new IcaoDecoder('3f4000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('3f7fff').isMilitary()).toBe(true); + }); + it('should match 3f8000-3fbfff', () => { + expect(!!new IcaoDecoder('3f8000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('3fbfff').isMilitary()).toBe(true); + }); + + // United Kingdom + it('should match 400000-40003f', () => { + expect(!!new IcaoDecoder('400000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('40003f').isMilitary()).toBe(true); + }); + it('should match 43c000-43cfff', () => { + expect(!!new IcaoDecoder('43c000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('43cfff').isMilitary()).toBe(true); + }); + + // Austria + it('should match 444000-447fff except 447ac7', () => { + expect(!!new IcaoDecoder('444000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('447fff').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('447ac7').isMilitary()).toBe(false); + }); + + // Belgium + it('should match 44f000-44ffff', () => { + expect(!!new IcaoDecoder('44f000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('44ffff').isMilitary()).toBe(true); + }); + + // Bulgaria + it('should match 457000-457fff', () => { + expect(!!new IcaoDecoder('457000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('457fff').isMilitary()).toBe(true); + }); + + // Denmark + it('should match 45f400-45f4ff', () => { + expect(!!new IcaoDecoder('45f400').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('45f4ff').isMilitary()).toBe(true); + }); + + // Greece + it('should match 468000-4683ff', () => { + expect(!!new IcaoDecoder('468000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('4683ff').isMilitary()).toBe(true); + }); + + // Hungary + it('should match 473c00-473c0f', () => { + expect(!!new IcaoDecoder('473c00').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('473c0f').isMilitary()).toBe(true); + }); + + // Norway + it('should match 478100-4781ff', () => { + expect(!!new IcaoDecoder('478100').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('4781ff').isMilitary()).toBe(true); + }); + + // Netherlands + it('should match 480000-480fff', () => { + expect(!!new IcaoDecoder('480000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('480fff').isMilitary()).toBe(true); + }); + + // Poland + it('should match 48d800-48d87f', () => { + expect(!!new IcaoDecoder('48d800').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('48d87f').isMilitary()).toBe(true); + }); + + // Portugal + it('should match 497c00-497cff', () => { + expect(!!new IcaoDecoder('497c00').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('497cff').isMilitary()).toBe(true); + }); + + // Czech Republic + it('should match 498420-49842f', () => { + expect(!!new IcaoDecoder('498420').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('49842f').isMilitary()).toBe(true); + }); + + // Switzerland + it('should match 4b7000-4b7fff', () => { + expect(!!new IcaoDecoder('4b7000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('4b7fff').isMilitary()).toBe(true); + }); + + // Turkey + it('should match 4b8200-4b82ff', () => { + expect(!!new IcaoDecoder('4b8200').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('4b82ff').isMilitary()).toBe(true); + }); + + // Slovenia + it('should match 506f00-506fff', () => { + expect(!!new IcaoDecoder('506f00').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('506fff').isMilitary()).toBe(true); + }); + + // Oman + it('should match 70c070-70c07f', () => { + expect(!!new IcaoDecoder('70c070').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('70c07f').isMilitary()).toBe(true); + }); + + // Saudi Arabia + it('should match 710258-71025f', () => { + expect(!!new IcaoDecoder('710258').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('71025f').isMilitary()).toBe(true); + }); + it('should match 710260-71027f', () => { + expect(!!new IcaoDecoder('710260').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('71027f').isMilitary()).toBe(true); + }); + it('should match 710280-71028f', () => { + expect(!!new IcaoDecoder('710280').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('71028f').isMilitary()).toBe(true); + }); + it('should match 710380-71039f', () => { + expect(!!new IcaoDecoder('710380').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('71039f').isMilitary()).toBe(true); + }); + + // Israel + it('should match 738a00-738aff', () => { + expect(!!new IcaoDecoder('738a00').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('738aff').isMilitary()).toBe(true); + }); + + // Australia + it('should match 7c822e-7c822f', () => { + expect(!!new IcaoDecoder('7c822e').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7c822f').isMilitary()).toBe(true); + }); + it('should match 7c8230-7c823f', () => { + expect(!!new IcaoDecoder('7c8230').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7c823f').isMilitary()).toBe(true); + }); + it('should match 7c8240-7c827f', () => { + expect(!!new IcaoDecoder('7c8240').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7c827f').isMilitary()).toBe(true); + }); + it('should match 7c8280-7c82ff', () => { + expect(!!new IcaoDecoder('7c8280').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7c82ff').isMilitary()).toBe(true); + }); + it('should match 7c8300-7c83ff', () => { + expect(!!new IcaoDecoder('7c8300').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7c83ff').isMilitary()).toBe(true); + }); + it('should match 7c8400 and not 7c87ff', () => { + expect(!!new IcaoDecoder('7c8400').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7c87ff').isMilitary()).toBe(false); + }); + it('should match 7c8800 and not 7c8fff', () => { + expect(!!new IcaoDecoder('7c8800').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7c8fff').isMilitary()).toBe(false); + }); + it('should match 7c9000-7c9fff', () => { + expect(!!new IcaoDecoder('7c9000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7c9fff').isMilitary()).toBe(true); + }); + it('should match 7ca000-7cbfff', () => { + expect(!!new IcaoDecoder('7ca000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7cbfff').isMilitary()).toBe(true); + }); + it('should not match 7cc409', () => { + expect(!!new IcaoDecoder('7cc409').isMilitary()).toBe(false); + }); + it('should match 7d0000-7dffff', () => { + expect(!!new IcaoDecoder('7d0000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7dffff').isMilitary()).toBe(true); + }); + it('should match 7e0000-7fffff', () => { + expect(!!new IcaoDecoder('7e0000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('7fffff').isMilitary()).toBe(true); + }); + + // India + it('should match 800200-8002ff', () => { + expect(!!new IcaoDecoder('800200').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('8002ff').isMilitary()).toBe(true); + }); + + // Canada + it('should match c20000-c3ffff', () => { + expect(!!new IcaoDecoder('c20000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('c3ffff').isMilitary()).toBe(true); + }); + + // Brazil + it('should match e40000-e41fff', () => { + expect(!!new IcaoDecoder('e40000').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('e41fff').isMilitary()).toBe(true); + }); + + // Chile + it('should match e80600-e806ff', () => { + expect(!!new IcaoDecoder('e80600').isMilitary()).toBe(true); + expect(!!new IcaoDecoder('e806ff').isMilitary()).toBe(true); + }); + + // Negative cases + it('should return false for non-military ICAO', () => { + expect(!!new IcaoDecoder('a00000').isMilitary()).toBe(false); + expect(!!new IcaoDecoder('123456').isMilitary()).toBe(false); + expect(!!new IcaoDecoder('000000').isMilitary()).toBe(false); + expect(!!new IcaoDecoder('ffffff').isMilitary()).toBe(false); + }); + }); +}); From 04b8602d19501e03b960ce958baafed84447a9f9 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sun, 7 Dec 2025 18:37:49 -0500 Subject: [PATCH 3/9] PR feedback --- lib/IcaoDecoder.ts | 269 +++++++---------------------------- lib/plugins/Label_14.test.ts | 123 ++++++++++++++++ 2 files changed, 175 insertions(+), 217 deletions(-) create mode 100644 lib/plugins/Label_14.test.ts diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index d9b49e4..58d3977 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -3,101 +3,44 @@ export class IcaoDecoder { icao : string; // Pre-compiled regex patterns for military ICAO address detection (performance optimization) - private static readonly MILITARY_PATTERNS = { - // US military - US_MIL_1: /^adf[7-9]/, - US_MIL_2: /^adf[a-f]/, - US_MIL_3: /^a[ef]/, - - // Egypt military - EGYPT_MIL: /^0100[78]/, - - // Algeria military - ALGERIA_MIL: /^0a4/, - - // Italy military - ITALY_MIL: /^33ff/, - - // France military - FRANCE_MIL_1: /^3a[89a-f]/, - FRANCE_MIL_2: /^3b/, - - // Germany military - GERMANY_MIL_1: /^3e[ab]/, - GERMANY_MIL_2: /^3f[4-9ab]/, - - // UK military - UK_MIL_1: /^4000[0-3]/, - UK_MIL_2: /^43c/, - - // Austria military - AUSTRIA_MIL: /^44[4-7]/, - - // Belgium military - BELGIUM_MIL: /^44f/, - - // Bulgaria military - BULGARIA_MIL: /^457/, - - // Denmark military - DENMARK_MIL: /^45f4/, - - // Greece military - GREECE_MIL: /^468[0-3]/, - - // Hungary military - HUNGARY_MIL: /^473c0/, - - // Norway military - NORWAY_MIL: /^4781/, - - // Netherlands military - NETHERLANDS_MIL: /^480/, - - // Poland military - POLAND_MIL: /^48d8[0-7]/, - - // Portugal military - PORTUGAL_MIL: /^497c/, - - // Czech Republic military - CZECH_MIL: /^49842/, - - // Switzerland military - SWITZERLAND_MIL: /^4b7/, - - // Turkey military - TURKEY_MIL: /^4b82/, - - // Slovenia military - SLOVENIA_MIL: /^506f/, - - // Oman military - OMAN_MIL: /^70c07/, - - // Saudi Arabia military - SAUDI_MIL_1: /^7102[5-8]/, - SAUDI_MIL_2: /^7103[89]/, - - // Israel military - ISRAEL_MIL: /^738a/, - - // Australia military - AUSTRALIA_MIL_1: /^7c8[2-48]/, - AUSTRALIA_MIL_2: /^7[def]/, - - // India military - INDIA_MIL: /^8002/, - - // Canada military - CANADA_MIL: /^c[23]/, - - // Brazil military - BRAZIL_MIL: /^e4[01]/, - - // Chile military - CHILE_MIL: /^e806/, - }; + private static readonly MILITARY_PATTERNS: RegExp[] = [ + /^adf[7-9]/, // adf7c8-adf7cf = united states mil_5(uf) + /^adf[a-f]/, // adf7d0-adf7df = united states mil_4(uf) + /^a[ef]/, // adf7e0-adf7ff = united states mil_3(uf), adf800-adffff = united states mil_2(uf), ae0000-afffff = united states mil_1(uf) + /^0100[78]/, // 010070-01008f = egypt_mil + /^0a4/, // 0a4000-0a4fff = algeria mil(ap) + /^33ff/, // 33ff00-33ffff = italy mil(iy) + /^3a[89a-f]/, // 3a8000-3affff = france mil_1(fs) + /^3b/, // 3b0000-3bffff = france mil_2(fs) + /^3e[ab]/, // 3e8000-3ebfff = germany mil_1(df) + /^3f[4-9ab]/, // 3f4000-3f7fff = germany mil_2(df), 3f8000-3fbfff = germany mil_3(df) + /^4000[0-3]/, // 400000-40003f = united kingdom mil_1(ra) + /^43c/, // 43c000-43cfff = united kingdom mil(ra) + /^44[4-7]/, // 444000-447fff = austria mil(aq) + /^44f/, // 44f000-44ffff = belgium mil(bc) + /^457/, // 457000-457fff = bulgaria mil(bu) + /^45f4/, // 45f400-45f4ff = denmark mil(dg) + /^468[0-3]/, // 468000-4683ff = greece mil(gc) + /^473c0/, // 473c00-473c0f = hungary mil(hm) + /^4781/, // 478100-4781ff = norway mil(nn) + /^480/, // 480000-480fff = netherlands mil(nm) + /^48d8[0-7]/, // 48d800-48d87f = poland mil(po) + /^497c/, // 497c00-497cff = portugal mil(pu) + /^49842/, // 498420-49842f = czech republic mil(ct) + /^4b7/, // 4b7000-4b7fff = switzerland mil(su) + /^4b82/, // 4b8200-4b82ff = turkey mil(tq) + /^506f/, // 506f00-506fff = slovenia mil(sj) + /^70c07/, // 70c070-70c07f = oman mil(on) + /^7102[5-8]/, // 710258-71025f = saudi arabia mil_1(sx), 710260-71027f = saudi arabia mil_2(sx), 710280-71028f = saudi arabia mil_3(sx) + /^7103[89]/, // 710380-71039f = saudi arabia mil_4(sx) + /^738a/, // 738a00-738aff = israel mil(iz) + /^7c8[2-48]/, // 7c822e-7c822f = australia mil_1(av), 7c8230-7c823f = australia mil_2(av), 7c8240-7c827f = australia mil_3(av), 7c8280-7c82ff = australia mil_4(av), 7c8300-7c83ff = australia mil_5(av), 7c8400-7c87ff = australia mil_6(av), 7c8800-7c8fff = australia mil_7(av) + /^7[def]/, // 7d0000-7dffff = australia mil_11(av), 7e0000-7fffff = australia mil_12(av) + /^8002/, // 800200-8002ff = india mil(im) + /^c[23]/, // c20000-c3ffff = canada mil(cb) + /^e4[01]/, // e40000-e41fff = brazil mil(bq) + /^e806/ // e80600-e806ff = chile mil(cq) + ]; constructor(icao: string) { this.name = 'icao-decoder-typescript'; @@ -106,129 +49,21 @@ export class IcaoDecoder { isMilitary() { const i = this.icao; - const p = IcaoDecoder.MILITARY_PATTERNS; - - return ( - false - // us military - //adf7c8-adf7cf = united states mil_5(uf) - //adf7d0-adf7df = united states mil_4(uf) - //adf7e0-adf7ff = united states mil_3(uf) - //adf800-adffff = united states mil_2(uf) - || p.US_MIL_1.test(i) - || p.US_MIL_2.test(i) - //ae0000-afffff = united states mil_1(uf) - || p.US_MIL_3.test(i) - - //010070-01008f = egypt_mil - || p.EGYPT_MIL.test(i) - - //0a4000-0a4fff = algeria mil(ap) - || p.ALGERIA_MIL.test(i) - - //33ff00-33ffff = italy mil(iy) - || p.ITALY_MIL.test(i) - - //350000-37ffff = spain mil(sp) - || (i >= '350000' && i <= '37ffff') - - //3a8000-3affff = france mil_1(fs) - || p.FRANCE_MIL_1.test(i) - //3b0000-3bffff = france mil_2(fs) - || p.FRANCE_MIL_2.test(i) - - //3e8000-3ebfff = germany mil_1(df) - // remove 8 and 9 from mil arnge - || p.GERMANY_MIL_1.test(i) - //3f4000-3f7fff = germany mil_2(df) - //3f8000-3fbfff = germany mil_3(df) - || p.GERMANY_MIL_2.test(i) - - //400000-40003f = united kingdom mil_1(ra) - || p.UK_MIL_1.test(i) - //43c000-43cfff = united kingdom mil(ra) - || p.UK_MIL_2.test(i) - - //444000-447fff = austria mil(aq) - || (p.AUSTRIA_MIL.test(i) && i != '447ac7') - - //44f000-44ffff = belgium mil(bc) - || p.BELGIUM_MIL.test(i) - - //457000-457fff = bulgaria mil(bu) - || p.BULGARIA_MIL.test(i) - - //45f400-45f4ff = denmark mil(dg) - || p.DENMARK_MIL.test(i) - - //468000-4683ff = greece mil(gc) - || p.GREECE_MIL.test(i) - - //473c00-473c0f = hungary mil(hm) - || p.HUNGARY_MIL.test(i) - - //478100-4781ff = norway mil(nn) - || p.NORWAY_MIL.test(i) - //480000-480fff = netherlands mil(nm) - || p.NETHERLANDS_MIL.test(i) - //48d800-48d87f = poland mil(po) - || p.POLAND_MIL.test(i) - //497c00-497cff = portugal mil(pu) - || p.PORTUGAL_MIL.test(i) - //498420-49842f = czech republic mil(ct) - || p.CZECH_MIL.test(i) - - //4b7000-4b7fff = switzerland mil(su) - || p.SWITZERLAND_MIL.test(i) - //4b8200-4b82ff = turkey mil(tq) - || p.TURKEY_MIL.test(i) - - //506f00-506fff = slovenia mil(sj) - || p.SLOVENIA_MIL.test(i) - - //70c070-70c07f = oman mil(on) - || p.OMAN_MIL.test(i) - - //710258-71025f = saudi arabia mil_1(sx) - //710260-71027f = saudi arabia mil_2(sx) - //710280-71028f = saudi arabia mil_3(sx) - //710380-71039f = saudi arabia mil_4(sx) - || p.SAUDI_MIL_1.test(i) - || p.SAUDI_MIL_2.test(i) - - //738a00-738aff = israel mil(iz) - || p.ISRAEL_MIL.test(i) - - //7c822e-7c822f = australia mil_1(av) - //7c8230-7c823f = australia mil_2(av) - //7c8240-7c827f = australia mil_3(av) - //7c8280-7c82ff = australia mil_4(av) - //7c8300-7c83ff = australia mil_5(av) - //7c8400-7c87ff = australia mil_6(av) - //7c8800-7c8fff = australia mil_7(av) - || p.AUSTRALIA_MIL_1.test(i) - //7c9000-7c9fff = australia mil_8(av) - //7ca000-7cbfff = australia mil_9(av) - || (i >= '7c9000' && i <= '7cbfff') - //7cc000-7cffff = australia mil_10(av) 7cc409 not mil, remove this range - //7d0000-7dffff = australia mil_11(av) - //7e0000-7fffff = australia mil_12(av) - || p.AUSTRALIA_MIL_2.test(i) - - //800200-8002ff = india mil(im) - || p.INDIA_MIL.test(i) - - //c20000-c3ffff = canada mil(cb) - || p.CANADA_MIL.test(i) - - //e40000-e41fff = brazil mil(bq) - || p.BRAZIL_MIL.test(i) - - //e80600-e806ff = chile mil(cq) - || p.CHILE_MIL.test(i) - ); + // Range checks that cannot be expressed as regex + if ((i >= '350000' && i <= '37ffff') || + (i >= '7c9000' && i <= '7cbfff')) { + return true; + } + // Austria exception + if (/^44[4-7]/.test(i) && i === '447ac7') { + return false; + } + // Australia exception + if (i === '7cc409') { + return false; + } + return IcaoDecoder.MILITARY_PATTERNS.some((p) => p.test(i)); } - } export default { diff --git a/lib/plugins/Label_14.test.ts b/lib/plugins/Label_14.test.ts new file mode 100644 index 0000000..d99c6c0 --- /dev/null +++ b/lib/plugins/Label_14.test.ts @@ -0,0 +1,123 @@ +import { MessageDecoder } from '../MessageDecoder'; +import { Label_15 } from './Label_15'; + +describe('Label_15', () => { + let plugin: Label_15; + + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_15(decoder); + }); + + test('matches Label 15s qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-15'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['15'], + preambles: ['(2'], + }); + }); + + test('decodes short variant missing unkown', () => { + const text = '(2N38448W 77216--- 28 20 7(Z' + const decodeResult = plugin.decode({ text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('38.747 N, 77.360 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('2000 feet'); + expect(decodeResult.formatted.items[2].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe('7 degrees'); + expect(decodeResult.remaining.text).toBe('--- 28'); + }); + + test('decodes short variant all fields', () => { + const text = '(2N40492W 77179248 99380-53(Z' + const decodeResult = plugin.decode({ text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('40.820 N, 77.298 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('38000 feet'); + expect(decodeResult.formatted.items[2].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe('-53 degrees'); + expect(decodeResult.remaining.text).toBe('248 99'); + }); + + test('decodes short variant missing alt', () => { + const text = '(2N39269W 77374--- 42---- 5(Z' + const decodeResult = plugin.decode({ text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.448 N, 77.623 W'); + expect(decodeResult.formatted.items[1].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[1].value).toBe('-5 degrees'); + expect(decodeResult.remaining.text).toBe('--- 42'); + }); + + test('decodes off variant no unkown', () => { + const text = '(2N39018W 77284OFF11112418101313--------(Z' + const decodeResult = plugin.decode({ text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.030 N, 77.473 W'); + expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[1].value).toBe('2024-11-11T18:10:00Z'); + expect(decodeResult.remaining.text).toBe('1313--------'); + }); + + test('decodes off variant no date and unknown', () => { + // https://app.airframes.io/messages/3593342701 + const text = '(2N42589W 83520OFF------13280606--------(Z' + const decodeResult = plugin.decode({ text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('42.982 N, 83.867 W'); + expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[1].value).toBe('13:28:00'); + expect(decodeResult.remaining.text).toBe('0606--------'); + }); + + test('decodes off variant all fields', () => { + // https://app.airframes.io/messages/3603048708 + const text = '(2N39042W 77308OFF1311240327B1818 015(Z' + const decodeResult = plugin.decode({ text: text }); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.070 N, 77.513 W'); + expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[1].value).toBe('2024-11-13T03:27:00Z'); + expect(decodeResult.remaining.text).toBe('B1818 015'); + }); + + test('does not decode Label 15 ', () => { + + const text = '(2 Bogus message'; + const decodeResult = plugin.decode({ text: text }); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-15'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message.text).toBe(text); + }); +}); \ No newline at end of file From 27c0cc4170628d539c2746dac3c6fb4309699173 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sun, 7 Dec 2025 18:56:22 -0500 Subject: [PATCH 4/9] convert to numeric --- lib/IcaoDecoder.test.ts | 230 +++++++++++++++++++++------------------- lib/IcaoDecoder.ts | 114 +++++++++++--------- 2 files changed, 182 insertions(+), 162 deletions(-) diff --git a/lib/IcaoDecoder.test.ts b/lib/IcaoDecoder.test.ts index dc3c95b..be81ad6 100644 --- a/lib/IcaoDecoder.test.ts +++ b/lib/IcaoDecoder.test.ts @@ -8,286 +8,296 @@ describe('IcaoDecoder', () => { }); describe('isMilitary', () => { + // Germany mil_1(df) explicit tests + it('should match Germany mil_1(df) range 3ea000-3ebfff', () => { + expect(new IcaoDecoder('3ea000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3ebfff').isMilitary()).toBe(true); + expect(new IcaoDecoder('3eafff').isMilitary()).toBe(true); + }); + it('should not match just outside Germany mil_1(df) range', () => { + expect(new IcaoDecoder('3e9fff').isMilitary()).toBe(false); // just before + expect(new IcaoDecoder('3ec000').isMilitary()).toBe(false); // just after + }); // US military it('should match adf7c8-adf7cf', () => { - expect(!!new IcaoDecoder('adf7c8').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('adf7cf').isMilitary()).toBe(true); + expect(new IcaoDecoder('adf7c8').isMilitary()).toBe(true); + expect(new IcaoDecoder('adf7cf').isMilitary()).toBe(true); }); it('should match adf7d0-adf7df', () => { - expect(!!new IcaoDecoder('adf7d0').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('adf7df').isMilitary()).toBe(true); + expect(new IcaoDecoder('adf7d0').isMilitary()).toBe(true); + expect(new IcaoDecoder('adf7df').isMilitary()).toBe(true); }); it('should match adf7e0-adf7ff', () => { - expect(!!new IcaoDecoder('adf7e0').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('adf7ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('adf7e0').isMilitary()).toBe(true); + expect(new IcaoDecoder('adf7ff').isMilitary()).toBe(true); }); it('should match adf800-adffff', () => { - expect(!!new IcaoDecoder('adf800').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('adffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('adf800').isMilitary()).toBe(true); + expect(new IcaoDecoder('adffff').isMilitary()).toBe(true); }); it('should match ae0000-afffff', () => { - expect(!!new IcaoDecoder('ae0000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('afffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('ae0000').isMilitary()).toBe(true); + expect(new IcaoDecoder('afffff').isMilitary()).toBe(true); }); // Egypt it('should match 010070-01008f', () => { - expect(!!new IcaoDecoder('010070').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('01008f').isMilitary()).toBe(true); + expect(new IcaoDecoder('010070').isMilitary()).toBe(true); + expect(new IcaoDecoder('01008f').isMilitary()).toBe(true); }); // Algeria it('should match 0a4000-0a4fff', () => { - expect(!!new IcaoDecoder('0a4000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('0a4fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('0a4000').isMilitary()).toBe(true); + expect(new IcaoDecoder('0a4fff').isMilitary()).toBe(true); }); // Italy it('should match 33ff00-33ffff', () => { - expect(!!new IcaoDecoder('33ff00').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('33ffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('33ff00').isMilitary()).toBe(true); + expect(new IcaoDecoder('33ffff').isMilitary()).toBe(true); }); // Spain it('should match 350000-37ffff', () => { - expect(!!new IcaoDecoder('350000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('37ffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('350000').isMilitary()).toBe(true); + expect(new IcaoDecoder('37ffff').isMilitary()).toBe(true); }); it('should not match 349fff', () => { - expect(!!new IcaoDecoder('349fff').isMilitary()).toBe(false); + expect(new IcaoDecoder('349fff').isMilitary()).toBe(false); }); it('should not match 380000', () => { - expect(!!new IcaoDecoder('380000').isMilitary()).toBe(false); + expect(new IcaoDecoder('380000').isMilitary()).toBe(false); }); // France it('should match 3a8000-3affff', () => { - expect(!!new IcaoDecoder('3a8000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('3affff').isMilitary()).toBe(true); + expect(new IcaoDecoder('3a8000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3affff').isMilitary()).toBe(true); }); it('should match 3b0000-3bffff', () => { - expect(!!new IcaoDecoder('3b0000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('3bffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('3b0000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3bffff').isMilitary()).toBe(true); }); // Germany it('should match 3e8000-3ebfff', () => { - expect(!!new IcaoDecoder('3ea000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('3ebfff').isMilitary()).toBe(true); + expect(new IcaoDecoder('3ea000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3ebfff').isMilitary()).toBe(true); }); it('should match 3f4000-3f7fff', () => { - expect(!!new IcaoDecoder('3f4000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('3f7fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('3f4000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3f7fff').isMilitary()).toBe(true); }); it('should match 3f8000-3fbfff', () => { - expect(!!new IcaoDecoder('3f8000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('3fbfff').isMilitary()).toBe(true); + expect(new IcaoDecoder('3f8000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3fbfff').isMilitary()).toBe(true); }); // United Kingdom it('should match 400000-40003f', () => { - expect(!!new IcaoDecoder('400000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('40003f').isMilitary()).toBe(true); + expect(new IcaoDecoder('400000').isMilitary()).toBe(true); + expect(new IcaoDecoder('40003f').isMilitary()).toBe(true); }); it('should match 43c000-43cfff', () => { - expect(!!new IcaoDecoder('43c000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('43cfff').isMilitary()).toBe(true); + expect(new IcaoDecoder('43c000').isMilitary()).toBe(true); + expect(new IcaoDecoder('43cfff').isMilitary()).toBe(true); }); // Austria it('should match 444000-447fff except 447ac7', () => { - expect(!!new IcaoDecoder('444000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('447fff').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('447ac7').isMilitary()).toBe(false); + expect(new IcaoDecoder('444000').isMilitary()).toBe(true); + expect(new IcaoDecoder('447fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('447ac7').isMilitary()).toBe(false); }); // Belgium it('should match 44f000-44ffff', () => { - expect(!!new IcaoDecoder('44f000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('44ffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('44f000').isMilitary()).toBe(true); + expect(new IcaoDecoder('44ffff').isMilitary()).toBe(true); }); // Bulgaria it('should match 457000-457fff', () => { - expect(!!new IcaoDecoder('457000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('457fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('457000').isMilitary()).toBe(true); + expect(new IcaoDecoder('457fff').isMilitary()).toBe(true); }); // Denmark it('should match 45f400-45f4ff', () => { - expect(!!new IcaoDecoder('45f400').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('45f4ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('45f400').isMilitary()).toBe(true); + expect(new IcaoDecoder('45f4ff').isMilitary()).toBe(true); }); // Greece it('should match 468000-4683ff', () => { - expect(!!new IcaoDecoder('468000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('4683ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('468000').isMilitary()).toBe(true); + expect(new IcaoDecoder('4683ff').isMilitary()).toBe(true); }); // Hungary it('should match 473c00-473c0f', () => { - expect(!!new IcaoDecoder('473c00').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('473c0f').isMilitary()).toBe(true); + expect(new IcaoDecoder('473c00').isMilitary()).toBe(true); + expect(new IcaoDecoder('473c0f').isMilitary()).toBe(true); }); // Norway it('should match 478100-4781ff', () => { - expect(!!new IcaoDecoder('478100').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('4781ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('478100').isMilitary()).toBe(true); + expect(new IcaoDecoder('4781ff').isMilitary()).toBe(true); }); // Netherlands it('should match 480000-480fff', () => { - expect(!!new IcaoDecoder('480000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('480fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('480000').isMilitary()).toBe(true); + expect(new IcaoDecoder('480fff').isMilitary()).toBe(true); }); // Poland it('should match 48d800-48d87f', () => { - expect(!!new IcaoDecoder('48d800').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('48d87f').isMilitary()).toBe(true); + expect(new IcaoDecoder('48d800').isMilitary()).toBe(true); + expect(new IcaoDecoder('48d87f').isMilitary()).toBe(true); }); // Portugal it('should match 497c00-497cff', () => { - expect(!!new IcaoDecoder('497c00').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('497cff').isMilitary()).toBe(true); + expect(new IcaoDecoder('497c00').isMilitary()).toBe(true); + expect(new IcaoDecoder('497cff').isMilitary()).toBe(true); }); // Czech Republic it('should match 498420-49842f', () => { - expect(!!new IcaoDecoder('498420').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('49842f').isMilitary()).toBe(true); + expect(new IcaoDecoder('498420').isMilitary()).toBe(true); + expect(new IcaoDecoder('49842f').isMilitary()).toBe(true); }); // Switzerland it('should match 4b7000-4b7fff', () => { - expect(!!new IcaoDecoder('4b7000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('4b7fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('4b7000').isMilitary()).toBe(true); + expect(new IcaoDecoder('4b7fff').isMilitary()).toBe(true); }); // Turkey it('should match 4b8200-4b82ff', () => { - expect(!!new IcaoDecoder('4b8200').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('4b82ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('4b8200').isMilitary()).toBe(true); + expect(new IcaoDecoder('4b82ff').isMilitary()).toBe(true); }); // Slovenia it('should match 506f00-506fff', () => { - expect(!!new IcaoDecoder('506f00').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('506fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('506f00').isMilitary()).toBe(true); + expect(new IcaoDecoder('506fff').isMilitary()).toBe(true); }); // Oman it('should match 70c070-70c07f', () => { - expect(!!new IcaoDecoder('70c070').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('70c07f').isMilitary()).toBe(true); + expect(new IcaoDecoder('70c070').isMilitary()).toBe(true); + expect(new IcaoDecoder('70c07f').isMilitary()).toBe(true); }); // Saudi Arabia it('should match 710258-71025f', () => { - expect(!!new IcaoDecoder('710258').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('71025f').isMilitary()).toBe(true); + expect(new IcaoDecoder('710258').isMilitary()).toBe(true); + expect(new IcaoDecoder('71025f').isMilitary()).toBe(true); }); it('should match 710260-71027f', () => { - expect(!!new IcaoDecoder('710260').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('71027f').isMilitary()).toBe(true); + expect(new IcaoDecoder('710260').isMilitary()).toBe(true); + expect(new IcaoDecoder('71027f').isMilitary()).toBe(true); }); it('should match 710280-71028f', () => { - expect(!!new IcaoDecoder('710280').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('71028f').isMilitary()).toBe(true); + expect(new IcaoDecoder('710280').isMilitary()).toBe(true); + expect(new IcaoDecoder('71028f').isMilitary()).toBe(true); }); it('should match 710380-71039f', () => { - expect(!!new IcaoDecoder('710380').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('71039f').isMilitary()).toBe(true); + expect(new IcaoDecoder('710380').isMilitary()).toBe(true); + expect(new IcaoDecoder('71039f').isMilitary()).toBe(true); }); // Israel it('should match 738a00-738aff', () => { - expect(!!new IcaoDecoder('738a00').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('738aff').isMilitary()).toBe(true); + expect(new IcaoDecoder('738a00').isMilitary()).toBe(true); + expect(new IcaoDecoder('738aff').isMilitary()).toBe(true); }); // Australia it('should match 7c822e-7c822f', () => { - expect(!!new IcaoDecoder('7c822e').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7c822f').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c822e').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c822f').isMilitary()).toBe(true); }); it('should match 7c8230-7c823f', () => { - expect(!!new IcaoDecoder('7c8230').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7c823f').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c8230').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c823f').isMilitary()).toBe(true); }); it('should match 7c8240-7c827f', () => { - expect(!!new IcaoDecoder('7c8240').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7c827f').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c8240').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c827f').isMilitary()).toBe(true); }); it('should match 7c8280-7c82ff', () => { - expect(!!new IcaoDecoder('7c8280').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7c82ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c8280').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c82ff').isMilitary()).toBe(true); }); it('should match 7c8300-7c83ff', () => { - expect(!!new IcaoDecoder('7c8300').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7c83ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c8300').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c83ff').isMilitary()).toBe(true); }); it('should match 7c8400 and not 7c87ff', () => { - expect(!!new IcaoDecoder('7c8400').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7c87ff').isMilitary()).toBe(false); + expect(new IcaoDecoder('7c8400').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c87ff').isMilitary()).toBe(false); }); it('should match 7c8800 and not 7c8fff', () => { - expect(!!new IcaoDecoder('7c8800').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7c8fff').isMilitary()).toBe(false); + expect(new IcaoDecoder('7c8800').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c8fff').isMilitary()).toBe(false); }); it('should match 7c9000-7c9fff', () => { - expect(!!new IcaoDecoder('7c9000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7c9fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c9000').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c9fff').isMilitary()).toBe(true); }); it('should match 7ca000-7cbfff', () => { - expect(!!new IcaoDecoder('7ca000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7cbfff').isMilitary()).toBe(true); + expect(new IcaoDecoder('7ca000').isMilitary()).toBe(true); + expect(new IcaoDecoder('7cbfff').isMilitary()).toBe(true); }); it('should not match 7cc409', () => { - expect(!!new IcaoDecoder('7cc409').isMilitary()).toBe(false); + expect(new IcaoDecoder('7cc409').isMilitary()).toBe(false); }); it('should match 7d0000-7dffff', () => { - expect(!!new IcaoDecoder('7d0000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7dffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('7d0000').isMilitary()).toBe(true); + expect(new IcaoDecoder('7dffff').isMilitary()).toBe(true); }); it('should match 7e0000-7fffff', () => { - expect(!!new IcaoDecoder('7e0000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('7fffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('7e0000').isMilitary()).toBe(true); + expect(new IcaoDecoder('7fffff').isMilitary()).toBe(true); }); // India it('should match 800200-8002ff', () => { - expect(!!new IcaoDecoder('800200').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('8002ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('800200').isMilitary()).toBe(true); + expect(new IcaoDecoder('8002ff').isMilitary()).toBe(true); }); // Canada it('should match c20000-c3ffff', () => { - expect(!!new IcaoDecoder('c20000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('c3ffff').isMilitary()).toBe(true); + expect(new IcaoDecoder('c20000').isMilitary()).toBe(true); + expect(new IcaoDecoder('c3ffff').isMilitary()).toBe(true); }); // Brazil it('should match e40000-e41fff', () => { - expect(!!new IcaoDecoder('e40000').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('e41fff').isMilitary()).toBe(true); + expect(new IcaoDecoder('e40000').isMilitary()).toBe(true); + expect(new IcaoDecoder('e41fff').isMilitary()).toBe(true); }); // Chile it('should match e80600-e806ff', () => { - expect(!!new IcaoDecoder('e80600').isMilitary()).toBe(true); - expect(!!new IcaoDecoder('e806ff').isMilitary()).toBe(true); + expect(new IcaoDecoder('e80600').isMilitary()).toBe(true); + expect(new IcaoDecoder('e806ff').isMilitary()).toBe(true); }); // Negative cases it('should return false for non-military ICAO', () => { - expect(!!new IcaoDecoder('a00000').isMilitary()).toBe(false); - expect(!!new IcaoDecoder('123456').isMilitary()).toBe(false); - expect(!!new IcaoDecoder('000000').isMilitary()).toBe(false); - expect(!!new IcaoDecoder('ffffff').isMilitary()).toBe(false); + expect(new IcaoDecoder('a00000').isMilitary()).toBe(false); + expect(new IcaoDecoder('123456').isMilitary()).toBe(false); + expect(new IcaoDecoder('000000').isMilitary()).toBe(false); + expect(new IcaoDecoder('ffffff').isMilitary()).toBe(false); }); }); }); diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index 58d3977..53f2ab8 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -2,44 +2,60 @@ export class IcaoDecoder { name : string; icao : string; - // Pre-compiled regex patterns for military ICAO address detection (performance optimization) - private static readonly MILITARY_PATTERNS: RegExp[] = [ - /^adf[7-9]/, // adf7c8-adf7cf = united states mil_5(uf) - /^adf[a-f]/, // adf7d0-adf7df = united states mil_4(uf) - /^a[ef]/, // adf7e0-adf7ff = united states mil_3(uf), adf800-adffff = united states mil_2(uf), ae0000-afffff = united states mil_1(uf) - /^0100[78]/, // 010070-01008f = egypt_mil - /^0a4/, // 0a4000-0a4fff = algeria mil(ap) - /^33ff/, // 33ff00-33ffff = italy mil(iy) - /^3a[89a-f]/, // 3a8000-3affff = france mil_1(fs) - /^3b/, // 3b0000-3bffff = france mil_2(fs) - /^3e[ab]/, // 3e8000-3ebfff = germany mil_1(df) - /^3f[4-9ab]/, // 3f4000-3f7fff = germany mil_2(df), 3f8000-3fbfff = germany mil_3(df) - /^4000[0-3]/, // 400000-40003f = united kingdom mil_1(ra) - /^43c/, // 43c000-43cfff = united kingdom mil(ra) - /^44[4-7]/, // 444000-447fff = austria mil(aq) - /^44f/, // 44f000-44ffff = belgium mil(bc) - /^457/, // 457000-457fff = bulgaria mil(bu) - /^45f4/, // 45f400-45f4ff = denmark mil(dg) - /^468[0-3]/, // 468000-4683ff = greece mil(gc) - /^473c0/, // 473c00-473c0f = hungary mil(hm) - /^4781/, // 478100-4781ff = norway mil(nn) - /^480/, // 480000-480fff = netherlands mil(nm) - /^48d8[0-7]/, // 48d800-48d87f = poland mil(po) - /^497c/, // 497c00-497cff = portugal mil(pu) - /^49842/, // 498420-49842f = czech republic mil(ct) - /^4b7/, // 4b7000-4b7fff = switzerland mil(su) - /^4b82/, // 4b8200-4b82ff = turkey mil(tq) - /^506f/, // 506f00-506fff = slovenia mil(sj) - /^70c07/, // 70c070-70c07f = oman mil(on) - /^7102[5-8]/, // 710258-71025f = saudi arabia mil_1(sx), 710260-71027f = saudi arabia mil_2(sx), 710280-71028f = saudi arabia mil_3(sx) - /^7103[89]/, // 710380-71039f = saudi arabia mil_4(sx) - /^738a/, // 738a00-738aff = israel mil(iz) - /^7c8[2-48]/, // 7c822e-7c822f = australia mil_1(av), 7c8230-7c823f = australia mil_2(av), 7c8240-7c827f = australia mil_3(av), 7c8280-7c82ff = australia mil_4(av), 7c8300-7c83ff = australia mil_5(av), 7c8400-7c87ff = australia mil_6(av), 7c8800-7c8fff = australia mil_7(av) - /^7[def]/, // 7d0000-7dffff = australia mil_11(av), 7e0000-7fffff = australia mil_12(av) - /^8002/, // 800200-8002ff = india mil(im) - /^c[23]/, // c20000-c3ffff = canada mil(cb) - /^e4[01]/, // e40000-e41fff = brazil mil(bq) - /^e806/ // e80600-e806ff = chile mil(cq) + // Military ICAO address ranges (as [start, end] tuples, inclusive) + private static readonly MILITARY_RANGES: Array<[number, number]> = [ + [0x010070, 0x01008f], // Egypt + [0x0a4000, 0x0a4fff], // Algeria + [0x33ff00, 0x33ffff], // Italy + [0x350000, 0x37ffff], // Spain + [0x3a8000, 0x3affff], // France 1 + [0x3b0000, 0x3bffff], // France 2 + [0x3ea000, 0x3ebfff], // Germany 4 + [0x3f4000, 0x3f7fff], // Germany 2 + [0x3f8000, 0x3fbfff], // Germany 3 + [0x400000, 0x40003f], // UK 1 + [0x43c000, 0x43cfff], // UK 2 + [0x444000, 0x447ac6], // Austria (before exception) + [0x447ac8, 0x447fff], // Austria (after exception) + [0x44f000, 0x44ffff], // Belgium + [0x457000, 0x457fff], // Bulgaria + [0x45f400, 0x45f4ff], // Denmark + [0x468000, 0x4683ff], // Greece + [0x473c00, 0x473c0f], // Hungary + [0x478100, 0x4781ff], // Norway + [0x480000, 0x480fff], // Netherlands + [0x48d800, 0x48d87f], // Poland + [0x497c00, 0x497cff], // Portugal + [0x498420, 0x49842f], // Czech + [0x4b7000, 0x4b7fff], // Switzerland + [0x4b8200, 0x4b82ff], // Turkey + [0x506f00, 0x506fff], // Slovenia + [0x70c070, 0x70c07f], // Oman + [0x710258, 0x71025f], // Saudi 1 + [0x710260, 0x71027f], // Saudi 2 + [0x710280, 0x71028f], // Saudi 3 + [0x710380, 0x71039f], // Saudi 4 + [0x738a00, 0x738aff], // Israel + [0x7c822e, 0x7c822f], // Australia 1 + [0x7c8230, 0x7c823f], // Australia 2 + [0x7c8240, 0x7c827f], // Australia 3 + [0x7c8280, 0x7c82ff], // Australia 4 + [0x7c8300, 0x7c83ff], // Australia 5 + [0x7c8400, 0x7c87fe], // Australia 6 (before exception) + [0x7c8800, 0x7c8ffe], // Australia 7 (before exception) + [0x7c9000, 0x7c9fff], // Australia 8 + [0x7ca000, 0x7cbfff], // Australia 9 + [0x7d0000, 0x7dffff], // Australia 11 + [0x7e0000, 0x7fffff], // Australia 12 + [0x800200, 0x8002ff], // India + [0xadf7c8, 0xadf7cf], // US mil_5 + [0xadf7d0, 0xadf7df], // US mil_4 + [0xadf7e0, 0xadf7ff], // US mil_3 + [0xadf800, 0xadffff], // US mil_2 + [0xae0000, 0xafffff], // US mil_1 + [0xc20000, 0xc3ffff], // Canada + [0xe40000, 0xe41fff], // Brazil + [0xe80600, 0xe806ff], // Chile ]; constructor(icao: string) { @@ -47,22 +63,16 @@ export class IcaoDecoder { this.icao = icao; } - isMilitary() { + isMilitary() : boolean{ const i = this.icao; - // Range checks that cannot be expressed as regex - if ((i >= '350000' && i <= '37ffff') || - (i >= '7c9000' && i <= '7cbfff')) { - return true; + const n = parseInt(i, 16); + // Range checks only + for (const [start, end] of IcaoDecoder.MILITARY_RANGES) { + if (n >= start && n <= end) { + return true; + } } - // Austria exception - if (/^44[4-7]/.test(i) && i === '447ac7') { - return false; - } - // Australia exception - if (i === '7cc409') { - return false; - } - return IcaoDecoder.MILITARY_PATTERNS.some((p) => p.test(i)); + return false; } } From 9ff7f5ad20d03a68512e8bf7e0e29e832d00769a Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sun, 7 Dec 2025 18:58:09 -0500 Subject: [PATCH 5/9] Remove accidentally committed Label_14.test.ts --- lib/plugins/Label_14.test.ts | 123 ----------------------------------- 1 file changed, 123 deletions(-) delete mode 100644 lib/plugins/Label_14.test.ts diff --git a/lib/plugins/Label_14.test.ts b/lib/plugins/Label_14.test.ts deleted file mode 100644 index d99c6c0..0000000 --- a/lib/plugins/Label_14.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { MessageDecoder } from '../MessageDecoder'; -import { Label_15 } from './Label_15'; - -describe('Label_15', () => { - let plugin: Label_15; - - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_15(decoder); - }); - - test('matches Label 15s qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-15'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['15'], - preambles: ['(2'], - }); - }); - - test('decodes short variant missing unkown', () => { - const text = '(2N38448W 77216--- 28 20 7(Z' - const decodeResult = plugin.decode({ text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('38.747 N, 77.360 W'); - expect(decodeResult.formatted.items[1].label).toBe('Altitude'); - expect(decodeResult.formatted.items[1].value).toBe('2000 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Outside Air Temperature (C)'); - expect(decodeResult.formatted.items[2].value).toBe('7 degrees'); - expect(decodeResult.remaining.text).toBe('--- 28'); - }); - - test('decodes short variant all fields', () => { - const text = '(2N40492W 77179248 99380-53(Z' - const decodeResult = plugin.decode({ text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('40.820 N, 77.298 W'); - expect(decodeResult.formatted.items[1].label).toBe('Altitude'); - expect(decodeResult.formatted.items[1].value).toBe('38000 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Outside Air Temperature (C)'); - expect(decodeResult.formatted.items[2].value).toBe('-53 degrees'); - expect(decodeResult.remaining.text).toBe('248 99'); - }); - - test('decodes short variant missing alt', () => { - const text = '(2N39269W 77374--- 42---- 5(Z' - const decodeResult = plugin.decode({ text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.448 N, 77.623 W'); - expect(decodeResult.formatted.items[1].label).toBe('Outside Air Temperature (C)'); - expect(decodeResult.formatted.items[1].value).toBe('-5 degrees'); - expect(decodeResult.remaining.text).toBe('--- 42'); - }); - - test('decodes off variant no unkown', () => { - const text = '(2N39018W 77284OFF11112418101313--------(Z' - const decodeResult = plugin.decode({ text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.030 N, 77.473 W'); - expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[1].value).toBe('2024-11-11T18:10:00Z'); - expect(decodeResult.remaining.text).toBe('1313--------'); - }); - - test('decodes off variant no date and unknown', () => { - // https://app.airframes.io/messages/3593342701 - const text = '(2N42589W 83520OFF------13280606--------(Z' - const decodeResult = plugin.decode({ text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('42.982 N, 83.867 W'); - expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[1].value).toBe('13:28:00'); - expect(decodeResult.remaining.text).toBe('0606--------'); - }); - - test('decodes off variant all fields', () => { - // https://app.airframes.io/messages/3603048708 - const text = '(2N39042W 77308OFF1311240327B1818 015(Z' - const decodeResult = plugin.decode({ text: text }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.070 N, 77.513 W'); - expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[1].value).toBe('2024-11-13T03:27:00Z'); - expect(decodeResult.remaining.text).toBe('B1818 015'); - }); - - test('does not decode Label 15 ', () => { - - const text = '(2 Bogus message'; - const decodeResult = plugin.decode({ text: text }); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-15'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message.text).toBe(text); - }); -}); \ No newline at end of file From d6b0c65c8223fc5f4afe864dc7f22261a4b0e607 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sun, 7 Dec 2025 19:00:29 -0500 Subject: [PATCH 6/9] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/IcaoDecoder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index 53f2ab8..dfb5ed8 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -63,7 +63,7 @@ export class IcaoDecoder { this.icao = icao; } - isMilitary() : boolean{ + isMilitary(): boolean { const i = this.icao; const n = parseInt(i, 16); // Range checks only From 2ef83a88d43c5c7245eae2ed0e4b84ad15519e11 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sun, 7 Dec 2025 19:01:51 -0500 Subject: [PATCH 7/9] style change --- lib/IcaoDecoder.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index dfb5ed8..dac40fd 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -66,9 +66,8 @@ export class IcaoDecoder { isMilitary(): boolean { const i = this.icao; const n = parseInt(i, 16); - // Range checks only for (const [start, end] of IcaoDecoder.MILITARY_RANGES) { - if (n >= start && n <= end) { + if (start <= n && n <= end) { return true; } } From d52be0af059d3e448612ff2523898c5ba7257c15 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sun, 7 Dec 2025 19:03:01 -0500 Subject: [PATCH 8/9] merge US --- lib/IcaoDecoder.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index dac40fd..1611964 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -48,11 +48,7 @@ export class IcaoDecoder { [0x7d0000, 0x7dffff], // Australia 11 [0x7e0000, 0x7fffff], // Australia 12 [0x800200, 0x8002ff], // India - [0xadf7c8, 0xadf7cf], // US mil_5 - [0xadf7d0, 0xadf7df], // US mil_4 - [0xadf7e0, 0xadf7ff], // US mil_3 - [0xadf800, 0xadffff], // US mil_2 - [0xae0000, 0xafffff], // US mil_1 + [0xadf7c8, 0xafffff], // US military (merged) [0xc20000, 0xc3ffff], // Canada [0xe40000, 0xe41fff], // Brazil [0xe80600, 0xe806ff], // Chile From 75a216b06d516753d1e3e0942c6c670f46337498 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Sun, 7 Dec 2025 19:06:51 -0500 Subject: [PATCH 9/9] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/IcaoDecoder.test.ts | 8 +++++--- lib/IcaoDecoder.ts | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/IcaoDecoder.test.ts b/lib/IcaoDecoder.test.ts index be81ad6..e07a8b8 100644 --- a/lib/IcaoDecoder.test.ts +++ b/lib/IcaoDecoder.test.ts @@ -81,7 +81,7 @@ describe('IcaoDecoder', () => { }); // Germany - it('should match 3e8000-3ebfff', () => { + it('should match 3ea000-3ebfff', () => { expect(new IcaoDecoder('3ea000').isMilitary()).toBe(true); expect(new IcaoDecoder('3ebfff').isMilitary()).toBe(true); }); @@ -240,12 +240,14 @@ describe('IcaoDecoder', () => { expect(new IcaoDecoder('7c8300').isMilitary()).toBe(true); expect(new IcaoDecoder('7c83ff').isMilitary()).toBe(true); }); - it('should match 7c8400 and not 7c87ff', () => { + it('should match 7c8400-7c87fe and not 7c87ff', () => { expect(new IcaoDecoder('7c8400').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c87fe').isMilitary()).toBe(true); expect(new IcaoDecoder('7c87ff').isMilitary()).toBe(false); }); - it('should match 7c8800 and not 7c8fff', () => { + it('should match 7c8800-7c8ffe and not 7c8fff', () => { expect(new IcaoDecoder('7c8800').isMilitary()).toBe(true); + expect(new IcaoDecoder('7c8ffe').isMilitary()).toBe(true); expect(new IcaoDecoder('7c8fff').isMilitary()).toBe(false); }); it('should match 7c9000-7c9fff', () => { diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index 1611964..169b7b9 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -41,8 +41,8 @@ export class IcaoDecoder { [0x7c8240, 0x7c827f], // Australia 3 [0x7c8280, 0x7c82ff], // Australia 4 [0x7c8300, 0x7c83ff], // Australia 5 - [0x7c8400, 0x7c87fe], // Australia 6 (before exception) - [0x7c8800, 0x7c8ffe], // Australia 7 (before exception) + [0x7c8400, 0x7c87ff], // Australia 6 (before exception) + [0x7c8800, 0x7c8fff], // Australia 7 (before exception) [0x7c9000, 0x7c9fff], // Australia 8 [0x7ca000, 0x7cbfff], // Australia 9 [0x7d0000, 0x7dffff], // Australia 11