316 lines
12 KiB
JavaScript
316 lines
12 KiB
JavaScript
// 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: "RA-00000" },
|
|
{ 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;
|
|
})();
|
|
|
|
// make nodejs happy:
|
|
if (module) {
|
|
module.exports = registration_from_hexid;
|
|
}
|