From 6d19f57e0e9f42be61628ec7b0c65e4d76dc1365 Mon Sep 17 00:00:00 2001 From: AvSquirrel Date: Mon, 20 Jun 2016 05:03:09 +0000 Subject: [PATCH] Add decoding of Canadian tail numbers --- main/traffic.go | 173 ++++++++++++++++++++++++++++----------------- test/icao2reg.go | 177 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+), 65 deletions(-) create mode 100644 test/icao2reg.go diff --git a/main/traffic.go b/main/traffic.go index 3a4dcedb..df408f9a 100644 --- a/main/traffic.go +++ b/main/traffic.go @@ -352,7 +352,7 @@ func parseDownlinkReport(s string, signalLevel int) { ti.Icao_addr = icao_addr ti.ExtrapolatedPosition = false - thisReg, validReg := icao2faa(icao_addr) + thisReg, validReg := icao2reg(icao_addr) if validReg { ti.Reg = thisReg ti.Tail = thisReg @@ -659,7 +659,7 @@ func esListen() { ti.ExtrapolatedPosition = false ti.Last_source = TRAFFIC_SOURCE_1090ES - thisReg, validReg := icao2faa(icao) + thisReg, validReg := icao2reg(icao) if validReg { ti.Reg = thisReg ti.Tail = thisReg @@ -1015,100 +1015,143 @@ func updateDemoTraffic(icao uint32, tail string, relAlt float32, gs float64, off } /* - icao2faa() : Converts 24-bit Mode S addresses to N-numbers. + icao2reg() : Converts 24-bit Mode S addresses to N-numbers and C-numbers. Input: uint32 representing the Mode S address. Valid range for translation is 0xA00001 - 0xADF7C7, inclusive. - Values outside the range A000001-AFFFFFF are flagged as non-US. + Values outside the range A000001-AFFFFFF or C00001-C3FFFF + are flagged as foreign. + Values between ADF7C8 - AFFFFF are allocated to the United States, but are not used for aicraft on the civil registry. These could be - military, other public aircraft, or (future use) temporary - anonymous addresses. + military, other public aircraft, or future use. + + + Values between C0CDF9 - C3FFFF are allocated to Canada, + but are not used for aicraft on the civil registry. These could be + military, other public aircraft, or future use. Output: string: String containing the decoded tail number (if decoding succeeded), - "NON-US" (for non-US allocation), and "MIL" for non-civil US allocation. + "NON-NA" (for non-US / non Canada allocation), and "US-MIL" or "CA-MIL" for non-civil US / Canada allocation. bool: True if the Mode S address successfully translated to an N number. False for all other conditions. */ -func icao2faa(icao_addr uint32) (string, bool) { +func icao2reg(icao_addr uint32) (string, bool) { // Initialize local variables base34alphabet := string("ABCDEFGHJKLMNPQRSTUVWXYZ0123456789") - faaOffset := uint32(0xA00001) + nationalOffset := uint32(0xA00001) // default is US tail := "" + nation := "" - // Discard non-US ICAO codes - if (icao_addr < 0xA00001) || (icao_addr > 0xAFFFFF) { - //fmt.Printf("%X is a non-US address.\n", icao_addr) - return "NON-US", false + // Determine nationality + if (icao_addr >= 0xA00001) && (icao_addr <= 0xAFFFFF) { + nation = "US" + } else if (icao_addr >= 0xC00001) && (icao_addr <= 0xC3FFFF) { + nation = "CA" + } else { + // future national decoding is TO-DO + return "NON-NA", false } - // Discard addresses that are not assigned to aircraft on the civil registry (public aircraft: USAF, USN, USMC, USCG, ANG, FBI, etc.) - if icao_addr > 0xADF7C7 { - //fmt.Printf("%X is a US aircraft, but not on the civil registry.\n", icao_addr) - return "MIL", false - } - - serial := int32(icao_addr - faaOffset) - // First digit - a := (serial / 101711) + 1 - - // Second digit - a_remainder := serial % 101711 - b := ((a_remainder + 9510) / 10111) - 1 - - // Third digit - b_remainder := (a_remainder + 9510) % 10111 - c := ((b_remainder + 350) / 951) - 1 - - // This next bit is more convoluted. First, figure out if we're using the "short" method of - // decoding the last two digits (two letters, one letter and one blank, or two blanks). - // This will be the case if digit "B" or "C" are calculated as negative, or if c_remainder - // is less than 601. - - c_remainder := (b_remainder + 350) % 951 - var d, e int32 - - if (b >= 0) && (c >= 0) && (c_remainder > 600) { // alphanumeric decoding method - d = 24 + (c_remainder-601)/35 - e = (c_remainder - 601) % 35 - - } else { // two-letter decoding method - if (b < 0) || (c < 0) { - c_remainder -= 350 // otherwise " " == 350, "A " == 351, "AA" == 352, etc. + if nation == "CA" { // Canada decoding + // First, discard addresses that are not assigned to aircraft on the civil registry + if icao_addr > 0xC0CDF8 { + //fmt.Printf("%X is a Canada aircraft, but not a CF-, CG-, or CI- registration.\n", icao_addr) + return "CA-MIL", false } - d = (c_remainder - 1) / 25 - e = (c_remainder - 1) % 25 + nationalOffset := uint32(0xC00001) + serial := int32(icao_addr - nationalOffset) - if e < 0 { - d -= 1 - e += 25 + // Fifth letter + e := serial % 26 + + // Fourth letter + d := (serial / 26) % 26 + + // Third letter + c := (serial / 676) % 26 // 676 == 26*26 + + // Second letter + b := (serial / 17576) % 26 // 17576 == 26*26*26 + + b_str := "FGI" + + //fmt.Printf("B = %d, C = %d, D = %d, E = %d\n",b,c,d,e) + tail = fmt.Sprintf("C%c%c%c%c", b_str[b], c+65, d+65, e+65) + } + + if nation == "US" { // FAA decoding + // First, discard addresses that are not assigned to aircraft on the civil registry + if icao_addr > 0xADF7C7 { + //fmt.Printf("%X is a US aircraft, but not on the civil registry.\n", icao_addr) + return "US-MIL", false } - } - a_char := fmt.Sprintf("%d", a) - var b_char, c_char, d_char, e_char string + serial := int32(icao_addr - nationalOffset) + // First digit + a := (serial / 101711) + 1 - if b >= 0 { - b_char = fmt.Sprintf("%d", b) - } + // Second digit + a_remainder := serial % 101711 + b := ((a_remainder + 9510) / 10111) - 1 - if b >= 0 && c >= 0 { - c_char = fmt.Sprintf("%d", c) - } + // Third digit + b_remainder := (a_remainder + 9510) % 10111 + c := ((b_remainder + 350) / 951) - 1 - if d > -1 { - d_char = string(base34alphabet[d]) - if e > 0 { - e_char = string(base34alphabet[e-1]) + // This next bit is more convoluted. First, figure out if we're using the "short" method of + // decoding the last two digits (two letters, one letter and one blank, or two blanks). + // This will be the case if digit "B" or "C" are calculated as negative, or if c_remainder + // is less than 601. + + c_remainder := (b_remainder + 350) % 951 + var d, e int32 + + if (b >= 0) && (c >= 0) && (c_remainder > 600) { // alphanumeric decoding method + d = 24 + (c_remainder-601)/35 + e = (c_remainder - 601) % 35 + + } else { // two-letter decoding method + if (b < 0) || (c < 0) { + c_remainder -= 350 // otherwise " " == 350, "A " == 351, "AA" == 352, etc. + } + + d = (c_remainder - 1) / 25 + e = (c_remainder - 1) % 25 + + if e < 0 { + d -= 1 + e += 25 + } } + + a_char := fmt.Sprintf("%d", a) + var b_char, c_char, d_char, e_char string + + if b >= 0 { + b_char = fmt.Sprintf("%d", b) + } + + if b >= 0 && c >= 0 { + c_char = fmt.Sprintf("%d", c) + } + + if d > -1 { + d_char = string(base34alphabet[d]) + if e > 0 { + e_char = string(base34alphabet[e-1]) + } + } + + tail = "N" + a_char + b_char + c_char + d_char + e_char + } - tail = "N" + a_char + b_char + c_char + d_char + e_char return tail, true } diff --git a/test/icao2reg.go b/test/icao2reg.go new file mode 100644 index 00000000..a8b6f573 --- /dev/null +++ b/test/icao2reg.go @@ -0,0 +1,177 @@ +/* + + icao2reg: Converts a 24-bit numeric value to a tail number of FAA + or Canadian registry. + + (c) 2016 AvSquirrel + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +package main + +import ( + "fmt" + "os" + "strconv" +) + +func main() { + icao := uint32(0xAC82EC) + args := os.Args + if len(args) > 1 { + code, err := strconv.ParseInt(args[1], 16, 32) + if err != nil { + fmt.Printf("Error parsing argument %s. Input should be 24-bit hexadecimal, e.g. 'A00001'\n", args[1]) + fmt.Printf("Showing example decoding for Mode S code %X,\n", icao) + } else { + icao = uint32(code) + } + } else { + fmt.Printf("Usage: ./icao2faa [code], where [code] is a 24-bit hexadecimal string, e.g. A00001\n") + fmt.Printf("Showing example decoding for Mode S code %X,\n", icao) + } + + tail, valid := icao2reg(icao) + + if valid { + fmt.Printf("ICAO %X successfully decodes as %s\n", icao, tail) + } else { + fmt.Printf("ICAO %X did not successfully decode. Response is `%s`\n", icao, tail) + } +} + +func icao2reg(icao_addr uint32) (string, bool) { + // Initialize local variables + base34alphabet := string("ABCDEFGHJKLMNPQRSTUVWXYZ0123456789") + nationalOffset := uint32(0xA00001) // default is US + tail := "" + nation := "" + + + // Determine nationality + if (icao_addr >= 0xA00001) && (icao_addr <= 0xAFFFFF) { + nation = "US" + } else if (icao_addr >= 0xC00001) && (icao_addr <= 0xC3FFFF) { + nation = "CA" + } else { + // future national decoding is TO-DO + return "NON-NA", false + } + + if (nation =="CA") { // Canada decoding + // First, discard addresses that are not assigned to aircraft on the civil registry + if icao_addr > 0xC0CDF8 { + //fmt.Printf("%X is a Canada aircraft, but not a CF-, CG-, or CI- registration.\n", icao_addr) + return "CA-MIL", false + } + + nationalOffset := uint32(0xC00001) + serial := int32(icao_addr - nationalOffset) + + // Fifth letter + e := serial % 26 + + // Fourth letter + d := (serial/26) % 26 + + // Third letter + c := (serial/676) % 26 // 676 == 26*26 + + // Second letter + b := (serial/17576) % 26 // 17576 == 26*26*26 + + b_str :="FGI" + + fmt.Printf("B = %d, C = %d, D = %d, E = %d\n",b,c,d,e) + tail = fmt.Sprintf("C%c%c%c%c",b_str[b],c+65,d+65,e+65) + } + + if (nation == "US") { // FAA decoding + // First, discard addresses that are not assigned to aircraft on the civil registry + if icao_addr > 0xADF7C7 { + //fmt.Printf("%X is a US aircraft, but not on the civil registry.\n", icao_addr) + return "US-MIL", false + } + + + serial := int32(icao_addr - nationalOffset) + // First digit + a := (serial / 101711) + 1 + + // Second digit + a_remainder := serial % 101711 + b := ((a_remainder + 9510) / 10111) - 1 + + // Third digit + b_remainder := (a_remainder + 9510) % 10111 + c := ((b_remainder + 350) / 951) - 1 + + // This next bit is more convoluted. First, figure out if we're using the "short" method of + // decoding the last two digits (two letters, one letter and one blank, or two blanks). + // This will be the case if digit "B" or "C" are calculated as negative, or if c_remainder + // is less than 601. + + c_remainder := (b_remainder + 350) % 951 + var d, e int32 + + if (b >= 0) && (c >= 0) && (c_remainder > 600) { // alphanumeric decoding method + d = 24 + (c_remainder-601)/35 + e = (c_remainder - 601) % 35 + + } else { // two-letter decoding method + if (b < 0) || (c < 0) { + c_remainder -= 350 // otherwise " " == 350, "A " == 351, "AA" == 352, etc. + } + + d = (c_remainder - 1) / 25 + e = (c_remainder - 1) % 25 + + if e < 0 { + d -= 1 + e += 25 + } + } + + a_char := fmt.Sprintf("%d", a) + var b_char, c_char, d_char, e_char string + + if b >= 0 { + b_char = fmt.Sprintf("%d", b) + } + + if b >= 0 && c >= 0 { + c_char = fmt.Sprintf("%d", c) + } + + if d > -1 { + d_char = string(base34alphabet[d]) + if e > 0 { + e_char = string(base34alphabet[e-1]) + } + } + + + tail = "N" + a_char + b_char + c_char + d_char + e_char + + } + + + return tail, true +}