N Number & ICAO address

If I download and recompile now, it will work? Or does the FA site still need to be updated to accept the request?

edit: I should just look more closely. It looks like it’s ready to go…

Yeah, it should be live. It’s just a javascript change on the dump1090 side.

That’s a great enhancement, though it seems the “/live/modes/” url is not working for MLAT flights. For example:

“Could not find airborne flight”: flightaware.com/live/modes/a4934a/redirect
[flight]N394SW[/flight]

I can confirm as well.

I haven’t been able to crack the code for the math on it, however it is a mathematically linked function that I believe I have generated a list based on once. I got really bored one day and wrote a bash script to scrape the FAAs registration database and update my basestation.sqb file. It would probably be somewhat trivial to set up dump1090 to query the SQLite database for the information, however I don’t use the dump1090 interface and really don’t have much interest in doing so.

If anyone wants the script to download the DB, I can probably give it to you, however it’s coded to ‘bypass’ the function that seems to deter scripted downloads, so I don’t really want to put it out on the internet.

There is room for improvement on the whole thing (instead of starting with a blank sqb file, copy the running one, MERGE the info from the FAA and replace the running one) but it worked well enough for my purposes.

It DOES appear that VRS now can dynamically obtain the information automatically and update the sqb file, so that’s what I’m using now.

Here is some Python code that I wrote to go to / from N to ICAO. It is based on looking at the structure of BartJr’s table. Spot-checking a few flights on the air it seems to match. This has not been extensively tested and may have errors. Also there is no protection against invalid inputs in the tail to ICAO routine.

The general principle is that an N number has one to three digits after the N. The first digit is never a zero. These are most significant in setting the ICAO number. A number N2nnxx wiil always be exactly 101711 (decimal) away from its N1nnxx counterpart. After this one, two, or three digit number, a suffix of zero to two letters can be applied. If there are 3 digits after the N, the suffix may contain numbers, but if the first digit of the suffix is a letter, the second one (if present) must also be a letter (“N123A1” is illegal). This gives the effect of a 4 or 5 digit N number, but actually internally these are extensions of a 3 digit number with what I call a “base 35” suffix.


base9 = '123456789'  # The first digit (after the "N") is always one of these.
base10 = '0123456789' # The possible second and third digits are one of these.
# Note that "I" and "O" are never used as letters, to prevent confusion with "1" and "0"
base34 = 'ABCDEFGHJKLMNPQRSTUVWXYZ0123456789' 
icaooffset = 0xA00001 # The lowest possible number, N1, is this.
b1 = 101711 # basis between N1... and N2...
b2 = 10111 # basis between N10.... and N11....

def suffix(rem):
    """ Produces the alpha(numeric) suffix from a number 0 - 950 """
    if rem == 0:
        suf = ''
    else:
        if rem <= 600: #Class A suffix -- only letters.
            rem = rem - 1
            suf = base34[rem // 25]
            if rem % 25 > 0:
                suf = suf + base34(rem % 25) - 1] # second class A letter, if present.
        else:  #rem > 600 : First digit of suffix is a number.  Second digit may be blank, letter, or number.
            rem = rem - 601
            suf = base10[rem // 35]
            if rem % 35 > 0:
                suf = suf + base34(rem % 35) - 1]
    return suf
    
def enc_suffix(suf):
    """ Produces a remainder from a 0 - 2 digit suffix. 
    No error checking.  Using illegal strings will have strange results."""
    if len(suf) == 0:
        return 0
    r0 = base34.find(suf[0])
    if len(suf) == 1:
        r1 = 0
    else:
        r1 = base34.find(suf[1]) + 1
    if r0 < 24: # first char is a letter, use base 25
        return r0 * 25 + r1 + 1
    else:  # first is a number -- base 35.
        return r0 * 35 + r1 - 239   
    
def icao_to_tail(icao):
    if (icao < 0) or (icao > 0xadf7c7):
        return "Undefined"
    icao = icao - icaooffset
    d1 = icao // b1
    nnum = 'N' + base9[d1]
    r1 = icao % b1
    if r1 < 601:
        nnum = nnum + suffix(r1) # of the form N1ZZ
    else:
        d2 = (r1 - 601) // b2  # find second digit.
        nnum = nnum + base10[d2]
        r2 = (r1 - 601) % b2  # and residue after that
        if r2 < 601:  # No third digit. (form N12ZZ)
            nnum = nnum + suffix(r2)
        else:
            d3 = (r2 - 601) // 951 # Three-digits have extended suffix.
            r3 = (r2 - 601) % 951   
            nnum = nnum + base10[d3] + suffix(r3)
    return nnum 
    
def tail_to_icao(tail):
    if tail[0] != 'N':
        return -1
    icao = icaooffset
    icao = icao + base9.find(tail[1]) * b1
    if len(tail) == 2: # simple 'N3' etc.
        return icao
    d2 = base10.find(tail[2])
    if d2 == -1: # Form N1A
        icao = icao + enc_suffix(tail[2:4])
        return icao
    else: # Form N11... or N111..
        icao = icao + d2 * b2 + 601
        d3 = base10.find(tail[3])
        if d3 > -1: #Form N111 Suffix is base 35.
            icao = icao + d3 * 951 + 601
            icao = icao + enc_suffix(tail[4:6])
            return icao
        else:  #Form N11A
            icao = icao + enc_suffix(tail[3:5])
            return icao

That looks like the same basic algorithm as my python code which I used to generate the table (though yours is better commented :mrgreen: )



#1: 1-9 * 101711
#2: _=601, 0-9*(9510+601)
az = list('ABCDEFGHJKLMNPQRSTUVWXYZ')
baz = ''] + az
na = map(str,range(10))
bazn = ''] + az + map(str,range(10))

def az1(i): #601
    if i == 0:
        return ''
    else:
        i -= 1
        return az* + baz*

def hex_to_n(h):
    s = 'N'
    i = int(h)-0xa00001
    s += str(i/101711 + 1)
    i %= 101711
    if i < 601:
        s += az1(i)
    else:
        i -= 601
        s += str(i/10111)
        i %= 10111
        if i < 601:
            s += az1(i)
        else:
            i -= 601
            s += str(i/951)
            i %= 951
            if i < 601:
                s += az1(i)
            else:
                i -= 601
                s += na* + bazn*
    return s



No need to scrape. They have a direct download of the entire FAA database, though you have to do some minor database-foo to correlate the interesting data from multiple files.

faa.gov/licenses_certificate … _download/

It fails the “N11” case. This seems to fix it:



    else: # Form N11... or N111..
        icao = icao + d2 * b2 + 601
+       if len(tail) == 3: # simple 'N34' etc.
+           return icao
        d3 = base10.find(tail[3])


My method doesn’t require any manual intervention. 8)

So is there a script I can run that would convert ICAO numbers to N numbers which would display on my local network connection instated of have the N field blank? Would that also mean that more planes would be trackable with the airplane icon and tail instead of just airliners? I guess what I am missing and would like to track is VFR traffic that is sending out ADS-B while they do appear sometimes I am not able to determine most of the times what my receiver is picking up if they are sq 1200

Thanks

This is hit or miss for me too. Not sure why it works when it does/doesn’t. ?

From the publicly available FAA tie-up data one can easily figure out what the algorithms are that relate American N numbers to their ICAO hex address - there are 24 algorithms for the 12 categories of 915399 possible N numbers. I turned these into a little and superfast Windows commandline application which works two-ways, N number <–> ICAO address. If somebody is interested, just ask for it : mailto:blackbird@inboxalias.com. The derived algorithms are available too (in Dutch only). They were thoroughly tested : the FAA data were generated both ways and all matched.

Thanks for responding @obj your suggestion for the d1090 adjustment.
I wonder if any user has changed the json and has this working to show reg numbers in the table instead of the default hex code.
I think this would be a great enhancement to d1090 mutability.

Here’s a Java class (translated from the python):

Probably can convert it to C but would need a lot of glue to simulate the cool python and java built-in methods…



public class NConverter {

    private final String base9;         // The first digit (after the "N")
                                        // is always one of these.
    private final String base10;        // The possible second and third digits
                                        // are one of these.

    // Note that "I" and "O" are never used as letters,
    // to prevent confusion with "1" and "0"

    private final String base34;
    private final int icaooffset;       // The lowest possible number
    private final int b1;               // basis between N1... and N2...
    private final int b2;               // basis between N10.... and N11....

    public NConverter() {
        base9 = "123456789";
        base10 = "0123456789";
        base34 = "ABCDEFGHJKLMNPQRSTUVWXYZ0123456789";
        icaooffset = 0xA00001;
        b1 = 101711;
        b2 = 10111;
    }

    private String suffix(int rem) {
        String suf;
        
        // Produces the alpha(numeric) suffix from a number 0 - 950
        if (rem == 0) {
            suf = "";
        } else if (rem <= 600) {  // Class A suffix -- only letters.
            rem--;
            suf = Character.toString(base34.charAt(rem / 25));

            if (rem % 25 > 0) {
                suf += Character.toString(base34.charAt((rem % 25) - 1)); // second class A letter, if present.
            }
        } else {    // rem > 600 : First digit of suffix is a number.  Second digit may be blank, letter, or number.
            rem -= 601;
            suf = Character.toString(base10.charAt(rem / 35));
            
            if (rem % 35 > 0) {
                suf += Character.toString(base34.charAt((rem % 35) - 1));
            }
        }

        return suf;
    }

    private int enc_suffix(String suf) {
        int r0;
        int r1;

        // Produces a remainder from a 0 - 2 digit suffix.
        // No error checking.  Using illegal strings will have strange results."""

        if (suf.length() == 0) {
            return 0;
        }
        
        r0 = base34.indexOf(suf.charAt(0));
        
        if (suf.length() == 1) {
            r1 = 0;
        } else {
            r1 = base34.indexOf(suf.charAt(1)) + 1;
        }

        if (r0 < 24) {
            return r0 * 25 + r1 + 1;    // first char is a letter, use base 25
        } else {  
            return r0 * 35 + r1 - 239;  // first is a number -- base 35.
        }
    }

    public String icao_to_n(String val) {
        String nnum;
        int d1;
        int d2;
        int d3;
        int r1;
        int r2;
        int r3;
        
        int icao = Integer.parseInt(val.toUpperCase(), 16);

        /*
         * N Numbers fit in this range. Other ICAO not decoded.
         */
        if ((icao < 0xA00001) || (icao > 0xADF7C7)) {
            return "";
        }

        icao -= icaooffset;     // A00001
        d1 = icao / b1;
        nnum = "N" + Character.toString(base9.charAt(d1));
        r1 = icao % b1;

        if (r1 < 601) {
            nnum += suffix(r1); // of the form N1ZZ
        } else {
            d2 = (r1 - 601) / b2; // find second digit.
            nnum += Character.toString(base10.charAt(d2));
            r2 = (r1 - 601) % b2;  // and residue after that

            if (r2 < 601) {
                nnum += suffix(r2);   // No third digit.(form N12ZZ
            } else {
                d3 = (r2 - 601) / 951; // Three-digits have extended suffix.
                r3 = (r2 - 601) % 951;
                nnum += Character.toString(base10.charAt(d3)) + suffix(r3);
            }
        }

        return nnum;
    }

    public int n_to_icao(String tail) {
        int d2;
        int d3;
        int icao;
        
        tail = tail.toUpperCase();
        
        if (!tail.startsWith("N")) {
            return -1;
        }

        icao = icaooffset;
        icao += base9.indexOf(tail.charAt(1)) * b1;

        if (tail.length() == 2) { // simple 'N3' etc.
            return icao;
        }

        d2 = base10.indexOf(tail.charAt(2));

        if (d2 == -1) {
            icao += enc_suffix(tail.substring(2, 4));    // Form N1A
        } else {
            icao += d2 * b2 + 601;    // Form N11... or N111..

            if (tail.length() != 3) { // simple 'N34' etc.
                d3 = base10.indexOf(tail.charAt(3));

                if (d3 > -1) {  // Form N111 Suffix is base 35.
                    icao += d3 * 951 + 601;
                    icao += enc_suffix(tail.substring(4, 6));
                } else {    // Form N11A
                    icao += enc_suffix(tail.substring(3, 5));
                }
            }
        }

        return icao;
    }
}


Can anyone advise how to connect a registration, callsign to an ICAO ID?
I see lots of ICAO ID’s with the letter N … next to it but flight aware does not have the
registration or call sign. I just go to the aircraft registration data base and pull up the Registration for the ICAO ID, and then listen to the radio for the call sign.

Is there anywhere we can add that information so that it shows up automatically for everyone?

If not Flight Aware is there any other site that does facilitate this info exchange?

VRS should give you the details you need.

You can setup the display to provide only the details that you want.

They even have pics of the actual plane.

CG tend to be government aircraft… c-gmpb c065e5… an RCMP aircraft.

I wrote a light, documented, and tested python script to translate ICAO addresses to N-Number and reciprocally: GitHub - guillaumemichel/icao-nnumber_converter: Script converting ICAO addresses to N-Numbers (Tail Numbers) and reciprocally. Only works for United States aircraft registrations.

1 Like

I don’t know how relevant this is but I was reading up about what ADS-B actually is (fascinating) and came across this line in System Design Considerations here: Automatic Dependent Surveillance–Broadcast - Wikipedia

“However, the FAA is allowing UAT-equipped aircraft to utilize a random self-assigned temporary ICAO address in conjunction with the use of beacon code 1200. 1090 ES equipped aircraft using ADS-B will not have this option.”

There is also this: New program extends ADS-B privacy to 1090ES - AOPA

“The FAA has announced a process that will allow operators of aircraft equipped with ADS-B Out using 1090 MHz Extended Squitter (Mode S transponder) technology to obtain real-time opt-out of ADS-B flight tracking.”

Again, I don’t know if this is important to this discussion but it sounded like it might be. I don’t know enough yet about the details to know.

1 Like