From e5912c322fdfaa626255022811aaa62bb7cf2d7f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 9 Sep 2016 17:16:17 +0100 Subject: [PATCH] Add some mechanical hexid->registration conversions. --- public_html/gmap.html | 1 + public_html/planeObject.js | 7 +- public_html/registrations.js | 310 +++++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+), 2 deletions(-) create mode 100644 public_html/registrations.js diff --git a/public_html/gmap.html b/public_html/gmap.html index 98cbd4a..ecc7103 100644 --- a/public_html/gmap.html +++ b/public_html/gmap.html @@ -16,6 +16,7 @@ + diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 92da329..0c07f1d 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -44,9 +44,12 @@ function PlaneObject(icao) { this.markerStyleKey = null; this.markerSvgKey = null; - // request metadata - this.registration = null; + // start from a computed registration, let the DB override it + // if it has something else. + this.registration = registration_from_hexid(this.icao); this.icaotype = null; + + // request metadata getAircraftData(this.icao).done(function(data) { if ("r" in data) { this.registration = data.r; diff --git a/public_html/registrations.js b/public_html/registrations.js new file mode 100644 index 0000000..299554e --- /dev/null +++ b/public_html/registrations.js @@ -0,0 +1,310 @@ +// Various reverse-engineered versions of the allocation algorithms +// used by different countries to allocate 24-bit ICAO addresses based +// on the aircraft registration. +// +// These were worked out by looking at the allocation patterns and +// working backwards to an algorithm that generates that pattern, +// spot-checking aircraft to see if it worked. +// YMMV. + +registration_from_hexid = (function () { + // hide the guts in a closure + + var limited_alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ"; // 24 chars; no I, O + var full_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 26 chars + + // handles 3-letter suffixes assigned with a regular pattern + // + // start: first hexid of range + // s1: major stride (interval between different first letters) + // s2: minor stride (interval between different second letters) + // prefix: the registration prefix + // + // optionally: + // alphabet: the alphabet to use (defaults full_alphabet) + // first: the suffix to use at the start of the range (default: AAA) + // last: the last valid suffix in the range (default: ZZZ) + + var stride_mappings = [ + { start: 0x008011, s1: 26*26, s2: 26, prefix: "ZS-" }, + + { start: 0x390000, s1: 1024, s2: 32, prefix: "F-G" }, + { start: 0x398000, s1: 1024, s2: 32, prefix: "F-H" }, + + { start: 0x3C4421, s1: 1024, s2: 32, prefix: "D-A", first: 'AAA', last: 'OZZ' }, + { start: 0x3C0001, s1: 26*26, s2: 26, prefix: "D-A", first: 'PAA', last: 'ZZZ' }, + { start: 0x3C8421, s1: 1024, s2: 32, prefix: "D-B", first: 'AAA', last: 'OZZ' }, + { start: 0x3C2001, s1: 26*26, s2: 26, prefix: "D-B", first: 'PAA', last: 'ZZZ' }, + { start: 0x3CC000, s1: 26*26, s2: 26, prefix: "D-C" }, + { start: 0x3D04A8, s1: 26*26, s2: 26, prefix: "D-E" }, + { start: 0x3D4950, s1: 26*26, s2: 26, prefix: "D-F" }, + { start: 0x3D8DF8, s1: 26*26, s2: 26, prefix: "D-G" }, + { start: 0x3DD2A0, s1: 26*26, s2: 26, prefix: "D-H" }, + { start: 0x3E1748, s1: 26*26, s2: 26, prefix: "D-I" }, + + { start: 0x448421, s1: 1024, s2: 32, prefix: "OO-" }, + { start: 0x458421, s1: 1024, s2: 32, prefix: "OY-" }, + { start: 0x460000, s1: 26*26, s2: 26, prefix: "OH-" }, + { start: 0x468421, s1: 1024, s2: 32, prefix: "SX-" }, + { start: 0x490421, s1: 1024, s2: 32, prefix: "CS-" }, + { start: 0x4A0421, s1: 1024, s2: 32, prefix: "YR-" }, + { start: 0x4B8421, s1: 1024, s2: 32, prefix: "TC-" }, + { start: 0x740421, s1: 1024, s2: 32, prefix: "JY-" }, + { start: 0x760421, s1: 1024, s2: 32, prefix: "AP-" }, + { start: 0x768421, s1: 1024, s2: 32, prefix: "9V-" }, + { start: 0x778421, s1: 1024, s2: 32, prefix: "YK-" }, + { start: 0xC00001, s1: 26*26, s2: 26, prefix: "C-F" }, + { start: 0xC044A9, s1: 26*26, s2: 26, prefix: "C-G" }, + { start: 0xE01041, s1: 4096, s2: 64, prefix: "LV-" } + ]; + + // numeric registrations + // start: start hexid in range + // first: first numeric registration + // count: number of numeric registrations + // template: registration template, trailing characters are replaced with the numeric registration + var numeric_mappings = [ + { start: 0x140000, first: 0, count: 100000, template: "RA0000" }, + { start: 0x0B03E8, first: 1000, count: 1000, template: "CUT0000" } + ]; + + // fill in some derived data + for (var i = 0; i < stride_mappings.length; ++i) { + var mapping = stride_mappings[i]; + + if (!mapping.alphabet) { + mapping.alphabet = full_alphabet; + } + + if (mapping.first) { + var c1 = mapping.alphabet.indexOf(mapping.first.charAt(0)); + var c2 = mapping.alphabet.indexOf(mapping.first.charAt(1)); + var c3 = mapping.alphabet.indexOf(mapping.first.charAt(2)); + mapping.offset = c1 * mapping.s1 + c2 * mapping.s2 + c3; + } else { + mapping.offset = 0; + } + + if (mapping.last) { + var c1 = mapping.alphabet.indexOf(mapping.last.charAt(0)); + var c2 = mapping.alphabet.indexOf(mapping.last.charAt(1)); + var c3 = mapping.alphabet.indexOf(mapping.last.charAt(2)); + mapping.end = mapping.start - mapping.offset + + c1 * mapping.s1 + + c2 * mapping.s2 + + c3 - mapping.offset; + } else { + mapping.end = mapping.start - mapping.offset + + (mapping.alphabet.length - 1) * mapping.s1 + + (mapping.alphabet.length - 1) * mapping.s2 + + (mapping.alphabet.length - 1); + } + } + + for (var i = 0; i < numeric_mappings.length; ++i) { + numeric_mappings[i].end = numeric_mappings[i].start + numeric_mappings[i].count - 1; + } + + function lookup(hexid) { + var hexid = +("0x" + hexid); + + reg = n_reg(hexid); + if (reg) + return reg; + + reg = ja_reg(hexid); + if (reg) + return reg; + + reg = hl_reg(hexid); + if (reg) + return reg; + + reg = numeric_reg(hexid); + if (reg) + return reg; + + reg = stride_reg(hexid); + if (reg) + return reg; + + return null; + } + + function stride_reg(hexid) { + // try the mappings in stride_mappings + var i; + for (i = 0; i < stride_mappings.length; ++i) { + var mapping = stride_mappings[i]; + if (hexid < mapping.start || hexid > mapping.end) + continue; + + var offset = hexid - mapping.start + mapping.offset; + + var i1 = Math.floor(offset / mapping.s1); + offset = offset % mapping.s1; + var i2 = Math.floor(offset / mapping.s2); + offset = offset % mapping.s2; + var i3 = offset; + + if (i1 < 0 || i1 >= mapping.alphabet.length || + i2 < 0 || i2 >= mapping.alphabet.length || + i3 < 0 || i3 >= mapping.alphabet.length) + continue; + + return mapping.prefix + mapping.alphabet.charAt(i1) + mapping.alphabet.charAt(i2) + mapping.alphabet.charAt(i3); + } + + // nothing + return null; + } + + function numeric_reg(hexid) { + // try the mappings in numeric_mappings + var i; + for (i = 0; i < numeric_mappings.length; ++i) { + var mapping = numeric_mappings[i]; + if (hexid < mapping.start || hexid > mapping.end) + continue; + + var reg = (hexid - mapping.start + mapping.first) + ""; + return mapping.template.substring(0, mapping.template.length - reg.length) + reg; + } + } + + // + // US N-numbers + // + + function n_letters(rem) { + if (rem == 0) + return ""; + + --rem; + return limited_alphabet.charAt(Math.floor(rem / 25)) + n_letter(rem % 25); + } + + function n_letter(rem) { + if (rem == 0) + return ""; + + --rem; + return limited_alphabet.charAt(rem); + } + + function n_reg(hexid) { + var offset = hexid - 0xA00001; + if (offset < 0 || offset >= 915399) { + return null; + } + + var digit1 = Math.floor(offset / 101711) + 1; + var reg = "N" + digit1; + offset = offset % 101711; + if (offset <= 600) { + // Na, NaA .. NaZ, NaAA .. NaZZ + return reg + n_letters(offset); + } + + // Na0* .. Na9* + offset -= 601; + + var digit2 = Math.floor(offset / 10111); + reg += digit2; + offset = offset % 10111; + + if (offset <= 600) { + // Nab, NabA..NabZ, NabAA..NabZZ + return reg + n_letters(offset); + } + + // Nab0* .. Nab9* + offset -= 601; + + var digit3 = Math.floor(offset / 951); + reg += digit3; + offset = offset % 951; + + if (offset <= 600) { + // Nabc, NabcA .. NabcZ, NabcAA .. NabcZZ + return reg + n_letters(offset); + } + + // Nabc0* .. Nabc9* + offset -= 601; + + var digit4 = Math.floor(offset / 35); + reg += digit4.toFixed(0); + offset = offset % 35; + + if (offset <= 24) { + // Nabcd, NabcdA .. NabcdZ + return reg + n_letter(offset); + } + + // Nabcd0 .. Nabcd9 + offset -= 25; + return reg + offset.toFixed(0); + } + + // South Korea + function hl_reg(hexid) { + if (hexid >= 0x71BA00 && hexid <= 0x71bf99) { + return "HL" + (hexid - 0x71BA00 + 0x7200).toString(16); + } + + if (hexid >= 0x71C000 && hexid <= 0x71C099) { + return "HL" + (hexid - 0x71C000 + 0x8000).toString(16); + } + + if (hexid >= 0x71C200 && hexid <= 0x71C299) { + return "HL" + (hexid - 0x71C200 + 0x8200).toString(16); + } + + return null; + } + + // Japan + function ja_reg(hexid) { + var offset = hexid - 0x840000; + if (offset < 0 || offset >= 229840) + return null; + + var reg = "JA"; + + var digit1 = Math.floor(offset / 22984); + if (digit1 < 0 || digit1 > 9) + return null; + reg += digit1; + offset = offset % 22984; + + var digit2 = Math.floor(offset / 916); + if (digit2 < 0 || digit2 > 9) + return null; + reg += digit2; + offset = offset % 916; + + if (offset < 340) { + // 3rd is a digit, 4th is a digit or letter + var digit3 = Math.floor(offset / 34); + reg += digit3; + offset = offset % 34; + + if (offset < 10) { + // 4th is a digit + return reg + offset; + } + + // 4th is a letter + offset -= 10; + return reg + limited_alphabet.charAt(offset); + } + + // 3rd and 4th are letters + offset -= 340; + var letter3 = Math.floor(offset / 24); + return reg + limited_alphabet.charAt(letter3) + limited_alphabet.charAt(offset % 24); + } + + return lookup; +})();