summaryrefslogtreecommitdiff
path: root/ADSBDecoder
diff options
context:
space:
mode:
Diffstat (limited to 'ADSBDecoder')
-rw-r--r--ADSBDecoder/Decoder.swift236
-rw-r--r--ADSBDecoder/Query.swift55
-rw-r--r--ADSBDecoder/main.swift119
3 files changed, 409 insertions, 1 deletions
diff --git a/ADSBDecoder/Decoder.swift b/ADSBDecoder/Decoder.swift
index 425797a..95f798b 100644
--- a/ADSBDecoder/Decoder.swift
+++ b/ADSBDecoder/Decoder.swift
@@ -6,3 +6,239 @@
//
import Foundation
+
+func BarometricAltitudeFeat(_ altitude: UInt16) -> Int {
+ let QBit = (altitude>>4)&0x1
+ if QBit == 1 {
+ //remove qbit
+ let part1:UInt16 = altitude&0xf
+ let part2:UInt16 = (altitude>>1)&0x7ff0
+ let altitude25 = part1 + part2
+ print("altitude2 \(altitude25) ")
+ return Int(altitude25)*25-1000
+ }
+ return Int(altitude)*100-1000
+}
+
+class Decoder {
+ var adsb_data: String = ""
+ var DataFormat: UInt32 = 0;
+
+ init (_ adsb_data: String) {
+ print(adsb_data)
+ self.adsb_data = adsb_data
+ //get the first 8 bits as integer
+ let startI = adsb_data.startIndex
+ let endI = adsb_data.index(startI, offsetBy: 1)
+ let firstByteString = adsb_data[startI...endI]
+ //print("\(firstByteString)")
+ if let ControlMsg = UInt32(firstByteString, radix: 16) {
+ //print("ControlMsg = \(ControlMsg)")
+ let CM_DataFormat = (ControlMsg&0xF8)>>3
+ DataFormat = CM_DataFormat
+ //let CM_TranspoderCapability = ControlMsg&(0x7)
+ }
+ print("Data Format \(DataFormat)")
+ }
+
+ func getDataFormat17() -> DataFormat17? {
+
+ if (DataFormat != 17) {
+ return nil
+ }
+ //let startI = adsb_data.index(adsb_data.startIndex, offsetBy: 1)
+ //let endI = adsb_data.index(adsb_data.endIndex, offsetBy: -1)
+ //let adsbData = adsb_data[startI...endI]
+ let ret:DataFormat17 = DataFormat17(adsb_data)
+
+
+
+ return ret;
+ }
+
+ func getDataFormat18() {
+ print("Not implemented")
+ }
+
+}
+
+func ICAOAlphabet(_ code: UInt8) -> String {
+ if ((code>=1) && (code<=26)) {
+ return String(UnicodeScalar(code+64))
+ } else if ((code >= 48)&&(code<=57)) {
+ return String(UnicodeScalar(code))
+ } else if (code==32) {
+ return " "
+ }
+ return "#"
+}
+
+func ICAO2String(_ b1: UInt8, _ b2: UInt8, _ b3: UInt8, _ b4: UInt8, _ b5: UInt8, _ b6: UInt8, _ b7: UInt8, _ b8: UInt8) -> String {
+ return ICAOAlphabet(b1)+ICAOAlphabet(b2)+ICAOAlphabet(b3)+ICAOAlphabet(b4)+ICAOAlphabet(b5)+ICAOAlphabet(b6)+ICAOAlphabet(b7)+ICAOAlphabet(b8)
+}
+
+class DataFormat17 {
+
+ let DataFormat=17 //0:5
+ var Capability=0 //5:7
+ var AddressAnnounced=0 //8:31
+ var TypeCode=0 //32:36
+ var MovementField=0//37:43
+ var HeadingBit=0//44
+ var HeadingField=0//45:51
+ var CPROddEven=0//53
+ var CPRlat=0//54:70
+ var CPRlon=0//71:87
+ var ParityIntegrity=0//88-111
+
+ init(_ adsb_data: String) {
+ //print("Dataformat: 17!")
+ //print(adsb_data)
+
+ var bindata:[UInt8] = []
+
+ var startN = adsb_data.startIndex
+ var endN = adsb_data.index(startN, offsetBy: 1)
+ var count=0//start index value
+ while (count<adsb_data.count) {
+ let u8 = UInt8(adsb_data[startN...endN], radix: 16)!
+ //print(adsb_data[startN...endN])
+ bindata.append(u8)
+ count += 2
+ if (count<adsb_data.count) {
+ startN = adsb_data.index(startN, offsetBy: 2)
+ endN = adsb_data.index(startN, offsetBy: 1)
+ }
+ }
+ print(bindata)
+
+ //Decode Capability
+ let cap = (bindata[0]>>1)&0x7
+ print(String(format: "cap %02x", cap))
+ Capability = Int(cap)
+
+ //Decode Address Announcement
+ let address_ann = UInt32(bindata[1])<<16 + UInt32(bindata[2])<<8 + UInt32(bindata[3])
+ print(String(format: "address %06x", address_ann))
+
+ AddressAnnounced = Int(address_ann)
+
+ //Decode Type Code
+ let tc_byte = bindata[4] >> 3
+ print(String(format: "tc %02d", tc_byte))
+
+ TypeCode = Int(tc_byte)
+
+ if (tc_byte == 4) {
+ let msg = ADSBTypeCode4(bindata[4...10])
+ print("=====ADSB MESSSGE 04 =======")
+ print(msg)
+ print("============================")
+ } else if (tc_byte == 11) {
+ let msg = ADSBTypeCode11(bindata[4...10])
+ print("=====ADSB MESSSGE 11 =======")
+ print(msg)
+ print("============================")
+ } else if (tc_byte == 19) {
+ print("Byte 19")
+ } else {
+ print("Unknow TC byte")
+ /*
+ //Decode Movement Field
+ let mov_byte = (bindata[4]&0x7) + (bindata[5]>>5)
+ print(String(format: "mov %02x", mov_byte))
+
+ //Heading Bit
+ let heading_bit = (bindata[5]>>3)&0x1
+ print(String(format: "heading %02x", heading_bit))
+
+ //Heading direction
+ let heading_direction = (bindata[5]&0xF)<<3+(bindata[6]>>4)&0xF
+ print(String(format: "hdir %02x", heading_direction))
+
+ //CPR Odd/Even
+ let cpr_odd_even = (bindata[6]>>2)&0x1;
+ print(String(format: "cpr %02x", cpr_odd_even))
+
+ //CPR lat
+ let cpr_lat = (bindata[6]&0x7)<<14 + (bindata[7]<<7) + (bindata[8]>>1)
+ print(String(format: "cpr lat %06x", cpr_lat))
+
+ //CPR lon
+ let cpr_lon = ((bindata[8]&0x1)<<16) + (bindata[9]<<8) + (bindata[10])
+ print(String(format: "cpr lon %06x", cpr_lon))
+ */
+ }
+ }
+}
+
+class ADSBTypeCode4: CustomStringConvertible {
+ var TypeCode:Int = 4
+ var Category:Int = 0
+ var ICAOName:String
+
+ init(_ bindata:ArraySlice<UInt8>) {
+ let cat = bindata[4]&0x7
+ Category = Int(cat)
+ let char_0 = bindata[5]>>2
+ let char_1 = (bindata[5]&0x3)<<4 + bindata[6]>>4
+ let char_2 = (bindata[6]&0xf)<<2 + (bindata[7]>>6)&0x3
+ let char_3 = (bindata[7])&0x3f
+ let char_4 = bindata[8]>>2
+ let char_5 = (bindata[8]&0x3)<<4 + bindata[9]>>4
+ let char_6 = (bindata[9]&0xf)<<2 + (bindata[10]>>6)
+ let char_7 = bindata[10]&0x3f
+
+ print(char_0, char_1, char_2,char_3,char_4,char_5,char_6,char_7)
+ ICAOName = ICAO2String(char_0, char_1, char_2, char_3, char_4, char_5, char_6, char_7)
+ //print("ICAO name \(ICAOName)")
+ }
+
+ var description: String {
+ let description = "TypeCode \(TypeCode) Cat \(Category) Flight name \(ICAOName)"
+ return description
+ }
+}
+
+class ADSBTypeCode11:CustomStringConvertible {
+ var TypeCode:Int = 11
+ var SurveillanceStatus: Int = 0
+ var SingleAntennaFlag: Int = 0
+ var Altitude: Int = 0
+ var Time: Int = 0
+ var CPRFormat: Int = 0
+ var Latitude: Int = 0
+ var Longitude: Int = 0
+
+ init(_ bindata:ArraySlice<UInt8>) {
+ let ss = (bindata[4]>>1)&(0x3)
+ SurveillanceStatus = Int(ss)
+ let saf = bindata[5]&0x1
+ SingleAntennaFlag = Int(saf)
+ let altitude = UInt16(bindata[5])<<4 + (UInt16(bindata[6])>>4)&0xf
+ print(altitude)
+ Altitude = BarometricAltitudeFeat(altitude)
+ let time = (bindata[6]>>3)&0x1
+ Time = Int(time)
+ let cpr = (bindata[6]>>2)&0x1
+ CPRFormat = Int(cpr)
+ let lat:UInt32 = UInt32(bindata[6]&0x3)<<15 + UInt32(bindata[7])<<7 + UInt32(bindata[8]>>1)
+ Latitude = Int(lat)
+ let lon:UInt32 = UInt32(bindata[8]&0x1)<<16 + UInt32(bindata[9])<<8 + UInt32(bindata[10])
+ Longitude = Int(lon)
+ }
+
+ var description: String {
+ var description = "SS \(SurveillanceStatus) SAF \(SingleAntennaFlag) Altitude \(Altitude)ft \n"
+ description += "Time \(Time) CPR \(CPRFormat) \n"
+ description += "Lat \(Latitude) Long \(Longitude)"
+ return description
+ }
+}
+
+class DataFormat18 {
+ init(_ adsb_data: String) {
+ print("Dataformat: 18")
+ print(adsb_data)
+ }
+}
diff --git a/ADSBDecoder/Query.swift b/ADSBDecoder/Query.swift
index 4906221..99d9075 100644
--- a/ADSBDecoder/Query.swift
+++ b/ADSBDecoder/Query.swift
@@ -6,3 +6,58 @@
//
import Foundation
+
+class QueryDF: CustomStringConvertible {
+
+ //max I see is 24
+ var DFlist:[Int] = Array(repeating: 0, count: 30)
+
+ init() {
+
+ }
+
+ func addDF(_ dataformat: UInt32) {
+ DFlist[Int(dataformat)] += 1
+ }
+
+ func showStat() {
+ print("DataFormat Count")
+ for i in 0...(DFlist.count)-1 {
+ if DFlist[i] != 0 {
+ print(String(format:" %02d: %05d", i, DFlist[i]))
+ }
+ }
+ }
+
+ var description: String {
+ var description = "DataFormat Count\n"
+ for i in 0...(DFlist.count)-1 {
+ if DFlist[i] != 0 {
+ description += String(format:" %02d: %04d\n", i, DFlist[i])
+ }
+ }
+ return description
+ }
+}
+
+class QueryDF17TC: CustomStringConvertible {
+ var TClist:[Int] = Array(repeating: 0, count: 32)
+
+ init() {
+
+ }
+
+ func addTC(_ typecode: Int) {
+ TClist[typecode] += 1
+ }
+
+ var description: String {
+ var description = "TypeCode Count\n"
+ for i in 0...(TClist.count-1) {
+ if TClist[i] != 0 {
+ description += String(format: " %02d: %04d\n", i, TClist[i])
+ }
+ }
+ return description
+ }
+}
diff --git a/ADSBDecoder/main.swift b/ADSBDecoder/main.swift
index fa84962..a324a0f 100644
--- a/ADSBDecoder/main.swift
+++ b/ADSBDecoder/main.swift
@@ -6,6 +6,123 @@
//
import Foundation
+import ArgumentParser
+import RegexBuilder
-print("Hello, World!")
+//return true if file excists
+func checkIfFileExists(_ fname: String) -> Bool {
+ let fm = FileManager.default
+ if fm.fileExists(atPath: fname) {
+ return true
+ }
+ return false
+}
+//get current run directory
+func getCurrentDirPath() -> String {
+ return Process().currentDirectoryPath
+}
+
+struct CommandLineArgs: ParsableCommand {
+ @Option(name: .shortAndLong) var inputfile: String
+ @Flag(name: .shortAndLong) var debug:Bool = false
+ @Flag(name: .shortAndLong) var version:Bool = false
+ @Flag(name: .shortAndLong) var show_stats:Bool = false
+}
+
+let args = CommandLineArgs.parseOrExit()
+
+let fileUrl = URL(fileURLWithPath:args.inputfile)
+print("File location [\(fileUrl.absoluteString)]")
+
+//check if file excists
+if (checkIfFileExists(fileUrl.path) == false) {
+ print("Supplied path \(fileUrl.path) doesnt exists")
+ exit(1)
+}
+
+//load the file with adsb data
+var adsb_source = ""
+do {
+ adsb_source = try String(contentsOfFile: fileUrl.path)
+ print("Loaded \(adsb_source.count) bytes")
+} catch {
+ print("Couldn't load text from a file \(fileUrl.path)")
+ exit(1)
+}
+
+
+
+let matchADSBLong = Regex {
+ Anchor.startOfLine
+ "*"
+
+ Repeat(
+ CharacterClass(
+ ("a"..."f"),
+ ("0"..."9")
+ )
+ ,count:28)
+
+ ";"
+}
+
+let matchADSBShort = Regex {
+ Anchor.startOfLine
+ "*"
+ Repeat(
+ CharacterClass(
+ ("a"..."f"),
+ ("0"..."9")
+ )
+ ,count:14)
+ ";"
+}
+
+//gather stats
+var q_df = QueryDF()
+var q_dftc = QueryDF17TC()
+
+//parse line by line
+var cnt=0
+for line in adsb_source.components(separatedBy: .newlines) {
+ var found=false
+ //print("\(cnt) \(line)")
+ //cnt += 1
+ if let tokenMatch = try matchADSBLong.prefixMatch(in: line) {
+ //print("\(String(tokenMatch.output))")
+ found = true
+ let str = String(tokenMatch.output)
+ let startIndex = str.index(str.startIndex, offsetBy: 1)
+ let endIndex = str.index(str.endIndex, offsetBy: -2)
+ let decoder = Decoder(String(str[startIndex...endIndex]))
+ if decoder.DataFormat == 17 {
+ if let d17 = decoder.getDataFormat17() {
+ //print(d17)
+ q_dftc.addTC(d17.TypeCode)
+ }
+ } else {
+
+ }
+ q_df.addDF(decoder.DataFormat)
+ };
+
+ if let tokenMatch = try matchADSBShort.prefixMatch(in: line) {
+ print("\(tokenMatch.output)")
+ found = true
+ };
+
+ if (found == false) {
+ print("Unknown adsb data line \(line)")
+ }
+
+
+}
+
+
+if args.show_stats {
+ print("----STAT----")
+ //q_df.showStat()
+ print(q_df)
+ print(q_dftc)
+}