diff --git a/lib/IcaoDecoder.test.ts b/lib/IcaoDecoder.test.ts new file mode 100644 index 0000000..e07a8b8 --- /dev/null +++ b/lib/IcaoDecoder.test.ts @@ -0,0 +1,305 @@ +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', () => { + // 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); + }); + 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 3ea000-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-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-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', () => { + 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); + }); + }); +}); diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index b8eb415..169b7b9 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -2,134 +2,73 @@ export class IcaoDecoder { name : string; icao : string; + // 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, 0x7c87ff], // Australia 6 (before exception) + [0x7c8800, 0x7c8fff], // Australia 7 (before exception) + [0x7c9000, 0x7c9fff], // Australia 8 + [0x7ca000, 0x7cbfff], // Australia 9 + [0x7d0000, 0x7dffff], // Australia 11 + [0x7e0000, 0x7fffff], // Australia 12 + [0x800200, 0x8002ff], // India + [0xadf7c8, 0xafffff], // US military (merged) + [0xc20000, 0xc3ffff], // Canada + [0xe40000, 0xe41fff], // Brazil + [0xe80600, 0xe806ff], // Chile + ]; + constructor(icao: string) { this.name = 'icao-decoder-typescript'; this.icao = icao; } - isMilitary() { - let i = this.icao; - 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) - || i.match(/^adf[7-9]/) - || i.match(/^adf[a-f]/) - //ae0000-afffff = united states mil_1(uf) - || i.match(/^a(e|f)/) - - //010070-01008f = egypt_mil - || i.match(/^0100(7|8)/) - - //0a4000-0a4fff = algeria mil(ap) - || i.match(/^0a4/) - - //33ff00-33ffff = italy mil(iy) - || i.match(/^33ff/) - - //350000-37ffff = spain mil(sp) - || (i >= '350000' && i <= '37ffff') - - //3a8000-3affff = france mil_1(fs) - || i.match(/^3a(8|9|[a-f])/) - //3b0000-3bffff = france mil_2(fs) - || i.match(/^3b/) - - //3e8000-3ebfff = germany mil_1(df) - // remove 8 and 9 from mil arnge - || i.match(/^3e(a|b)/) - //3f4000-3f7fff = germany mil_2(df) - //3f8000-3fbfff = germany mil_3(df) - || i.match(/^3f([4-9]|[a-b])/) - - //400000-40003f = united kingdom mil_1(ra) - || i.match(/^4000[0-3]/) - //43c000-43cfff = united kingdom mil(ra) - || i.match(/^43c/) - - //444000-447fff = austria mil(aq) - || (i.match(/^44[4-7]/) && i != '447ac7') - - //44f000-44ffff = belgium mil(bc) - || i.match(/^44f/) - - //457000-457fff = bulgaria mil(bu) - || i.match(/^457/) - - //45f400-45f4ff = denmark mil(dg) - || i.match(/^45f4/) - - //468000-4683ff = greece mil(gc) - || i.match(/^468[0-3]/) - - //473c00-473c0f = hungary mil(hm) - || i.match(/^473c0/) - - //478100-4781ff = norway mil(nn) - || i.match(/^4781/) - //480000-480fff = netherlands mil(nm) - || i.match(/^480/) - //48d800-48d87f = poland mil(po) - || i.match(/^48d8[0-7]/) - //497c00-497cff = portugal mil(pu) - || i.match(/^497c/) - //498420-49842f = czech republic mil(ct) - || i.match(/^49842/) - - //4b7000-4b7fff = switzerland mil(su) - || i.match(/^4b7/) - //4b8200-4b82ff = turkey mil(tq) - || i.match(/^4b82/) - - //506f00-506fff = slovenia mil(sj) - || i.match(/^506f/) - - //70c070-70c07f = oman mil(on) - || i.match(/^70c07/) - - //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]/) - - //738a00-738aff = israel mil(iz) - || i.match(/^738a/) - - //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) - || i.match(/^7c8([2-4]|8)/) - //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]/) - - //800200-8002ff = india mil(im) - || i.match(/^8002/) - - //c20000-c3ffff = canada mil(cb) - || i.match(/^c[2-3]/) - - //e40000-e41fff = brazil mil(bq) - || i.match(/^e4[0-1]/) - - //e80600-e806ff = chile mil(cq) - || i.match(/^e806/) - ); + isMilitary(): boolean { + const i = this.icao; + const n = parseInt(i, 16); + for (const [start, end] of IcaoDecoder.MILITARY_RANGES) { + if (start <= n && n <= end) { + return true; + } + } + return false; } - } export default {