summaryrefslogtreecommitdiff
path: root/ADSBDecoderFramework
diff options
context:
space:
mode:
authorArturs Artamonovs <arturs.artamonovs@protonmail.com>2026-09-24 11:02:35 +0100
committerArturs Artamonovs <arturs.artamonovs@protonmail.com>2026-09-24 11:02:35 +0100
commit505b23afc68ecbb63b8f0a7d44c0b0ad063d5cfd (patch)
treed38b29a0cf23041571eb08fd79f49fdad8deb85f /ADSBDecoderFramework
parent056ee0c3355db995d7ea571e0635ebe8b09bf8ee (diff)
downloadADSBDecoder-505b23afc68ecbb63b8f0a7d44c0b0ad063d5cfd.tar.gz
ADSBDecoder-505b23afc68ecbb63b8f0a7d44c0b0ad063d5cfd.zip
rearrange source, move common parts to framework
Diffstat (limited to 'ADSBDecoderFramework')
-rw-r--r--ADSBDecoderFramework/ADSBDecoderFramework.docc/ADSBDecoderFramework.md13
-rw-r--r--ADSBDecoderFramework/ADSBDecoderFramework.swift9
-rw-r--r--ADSBDecoderFramework/ADSBRegEx.swift87
-rw-r--r--ADSBDecoderFramework/Configs.swift10
-rw-r--r--ADSBDecoderFramework/Decoder.swift249
-rw-r--r--ADSBDecoderFramework/PositionDecoder.swift221
-rw-r--r--ADSBDecoderFramework/Query.swift126
7 files changed, 715 insertions, 0 deletions
diff --git a/ADSBDecoderFramework/ADSBDecoderFramework.docc/ADSBDecoderFramework.md b/ADSBDecoderFramework/ADSBDecoderFramework.docc/ADSBDecoderFramework.md
new file mode 100644
index 0000000..588e73d
--- /dev/null
+++ b/ADSBDecoderFramework/ADSBDecoderFramework.docc/ADSBDecoderFramework.md
@@ -0,0 +1,13 @@
+# ``ADSBDecoderFramework``
+
+<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
+
+## Overview
+
+<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
+
+## Topics
+
+### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
+
+- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@--> \ No newline at end of file
diff --git a/ADSBDecoderFramework/ADSBDecoderFramework.swift b/ADSBDecoderFramework/ADSBDecoderFramework.swift
new file mode 100644
index 0000000..c2407a0
--- /dev/null
+++ b/ADSBDecoderFramework/ADSBDecoderFramework.swift
@@ -0,0 +1,9 @@
+//
+// ADSBDecoderFramework.swift
+// ADSBDecoderFramework
+//
+// Created by Jacky Jack on 24/09/2026.
+//
+
+import Foundation
+
diff --git a/ADSBDecoderFramework/ADSBRegEx.swift b/ADSBDecoderFramework/ADSBRegEx.swift
new file mode 100644
index 0000000..2841467
--- /dev/null
+++ b/ADSBDecoderFramework/ADSBRegEx.swift
@@ -0,0 +1,87 @@
+//
+// ADSBRegEx.swift
+// ADSBDecoder
+//
+// Created by Jacky Jack on 30/06/2024.
+//
+
+import Foundation
+import RegexBuilder
+/*
+let matchADSBLong = Regex {
+ Anchor.startOfLine
+ "*"
+
+ Repeat(
+ CharacterClass(
+ ("a"..."f"),
+ ("0"..."9")
+ )
+ ,count:28)
+
+ ";"
+}*/
+class MatchADSBLong {
+ let matchADSBLong = Regex {
+ Anchor.startOfLine
+ "*"
+
+ Repeat(
+ CharacterClass(
+ ("a"..."f"),
+ ("0"..."9")
+ )
+ ,count:28)
+
+ ";"
+ }
+
+ func prefixMatch(_ line: String) -> Regex<Regex<Substring>.RegexOutput>.Match? {
+ do {
+ if let match = try matchADSBLong.prefixMatch(in: line) {
+ return match
+ }
+ } catch {
+ return nil
+ }
+ return nil
+ }
+}
+
+/*
+let matchADSBShort = Regex {
+ Anchor.startOfLine
+ "*"
+ Repeat(
+ CharacterClass(
+ ("a"..."f"),
+ ("0"..."9")
+ )
+ ,count:14)
+ ";"
+}*/
+
+class MatchADSBShort {
+ let matchADSBShort = Regex {
+ Anchor.startOfLine
+ "*"
+ Repeat(
+ CharacterClass(
+ ("a"..."f"),
+ ("0"..."9")
+ )
+ ,count:14)
+ ";"
+ }
+
+ func prefixMatch(_ line: String) -> Regex<Regex<Substring>.RegexOutput>.Match? {
+ do {
+ if let match = try matchADSBShort.prefixMatch(in: line) {
+ return match
+ }
+ } catch {
+ return nil
+ }
+ return nil
+ }
+}
diff --git a/ADSBDecoderFramework/Configs.swift b/ADSBDecoderFramework/Configs.swift
new file mode 100644
index 0000000..a42e745
--- /dev/null
+++ b/ADSBDecoderFramework/Configs.swift
@@ -0,0 +1,10 @@
+//
+// Configs.swift
+// ADSBDecoder
+//
+// Created by Jacky Jack on 09/07/2024.
+//
+
+import Foundation
+
+let decoder_debug_mode = false
diff --git a/ADSBDecoderFramework/Decoder.swift b/ADSBDecoderFramework/Decoder.swift
new file mode 100644
index 0000000..a9724b6
--- /dev/null
+++ b/ADSBDecoderFramework/Decoder.swift
@@ -0,0 +1,249 @@
+//
+// Decoder.swift
+// ADSBDecoder
+//
+// Created by Jacky Jack on 28/05/2024.
+//
+
+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
+
+ //all avaliable message types
+ var messageIdentification:ADSBTypeCodeIndentification? = nil
+ var messageAirbornPositon:ADSBTypeCodeAirbonePositon? = nil
+
+ 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)
+
+ //aircraft indentification and category
+ if (tc_byte == 4) {
+ let msg = ADSBTypeCodeIndentification(bindata[4...10])
+ if decoder_debug_mode {
+ print("=====ADSB MESSSGE 04 =======")
+ print(msg)
+ print("============================")
+ }
+
+ messageIdentification = msg
+ //airborn position
+ } else if ((tc_byte >= 8) && (tc_byte <= 18)) {
+ let msg = ADSBTypeCodeAirbonePositon(bindata[4...10])
+ if decoder_debug_mode {
+ print(String(format:"=====ADSB MESSSGE %02d ======= AA:%04d", tc_byte, AddressAnnounced))
+ print(msg)
+ print("============================")
+ }
+
+ messageAirbornPositon = msg
+ //airborn velocity
+ } else if (tc_byte == 19) {
+ if decoder_debug_mode {
+ print("=====ADSB MESSSGE 19 =======")
+ print("=====VELOCITY =======")
+ }
+ } else {
+ if decoder_debug_mode {
+ print("=====ADSB MESSSGE UNKNOWN =======")
+ }
+ }
+ }
+}
+
+class ADSBTypeCodeIndentification: 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
+ }
+}
+
+//DF17 message types codes 8 to 18
+class ADSBTypeCodeAirbonePositon: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)"
+ if (CPRFormat == 0) {
+ description += "even"
+ } else {
+ description += "odd"
+ }
+ description += "\n"
+ description += "Lat \(Latitude) Long \(Longitude)"
+ return description
+ }
+}
+
+class DataFormat19:CustomStringConvertible {
+ init(_ bindata:ArraySlice<UInt8>) {
+ print("Dataformat: 19")
+ }
+
+ var description: String {
+ return ""
+ }
+}
diff --git a/ADSBDecoderFramework/PositionDecoder.swift b/ADSBDecoderFramework/PositionDecoder.swift
new file mode 100644
index 0000000..85b9d49
--- /dev/null
+++ b/ADSBDecoderFramework/PositionDecoder.swift
@@ -0,0 +1,221 @@
+//
+// PositionDecoder.swift
+// ADSBDecoder
+//
+// Created by Jacky Jack on 14/06/2024.
+//
+
+import Foundation
+import Collections
+
+struct PositionData {
+ var cpr_lat: UInt32
+ var cpr_long: UInt32
+ var even: Bool
+ var cprtime: Int64
+}
+
+func cpr_mod(_ a: Int, _ b: Int) -> Int {
+ var res:Int = a % b;
+ if (res < 0) {
+ res += b
+ }
+ return res;
+}
+
+/* The NL function uses the precomputed table from 1090-WP-9-14 */
+func cpr_NLFunction(_ _lat: Double) -> Int {
+ var lat:Double = _lat
+ if (lat < 0) {
+ lat = -lat
+ } /* Table is simmetric about the equator. */
+ if (lat < 10.47047130) {return 59}
+ if (lat < 14.82817437) {return 58}
+ if (lat < 18.18626357) {return 57}
+ if (lat < 21.02939493) {return 56}
+ if (lat < 23.54504487) {return 55}
+ if (lat < 25.82924707) {return 54}
+ if (lat < 27.93898710) {return 53}
+ if (lat < 29.91135686) {return 52}
+ if (lat < 31.77209708) {return 51}
+ if (lat < 33.53993436) {return 50}
+ if (lat < 35.22899598) {return 49}
+ if (lat < 36.85025108) {return 48}
+ if (lat < 38.41241892) {return 47}
+ if (lat < 39.92256684) {return 46}
+ if (lat < 41.38651832) {return 45}
+ if (lat < 42.80914012) {return 44}
+ if (lat < 44.19454951) {return 43}
+ if (lat < 45.54626723) {return 42}
+ if (lat < 46.86733252) {return 41}
+ if (lat < 48.16039128) {return 40}
+ if (lat < 49.42776439) {return 39}
+ if (lat < 50.67150166) {return 38}
+ if (lat < 51.89342469) {return 37}
+ if (lat < 53.09516153) {return 36}
+ if (lat < 54.27817472) {return 35}
+ if (lat < 55.44378444) {return 34}
+ if (lat < 56.59318756) {return 33}
+ if (lat < 57.72747354) {return 32}
+ if (lat < 58.84763776) {return 31}
+ if (lat < 59.95459277) {return 30}
+ if (lat < 61.04917774) {return 29}
+ if (lat < 62.13216659) {return 28}
+ if (lat < 63.20427479) {return 27}
+ if (lat < 64.26616523) {return 26}
+ if (lat < 65.31845310) {return 25}
+ if (lat < 66.36171008) {return 24}
+ if (lat < 67.39646774) {return 23}
+ if (lat < 68.42322022) {return 22}
+ if (lat < 69.44242631) {return 21}
+ if (lat < 70.45451075) {return 20}
+ if (lat < 71.45986473) {return 19}
+ if (lat < 72.45884545) {return 18}
+ if (lat < 73.45177442) {return 17}
+ if (lat < 74.43893416) {return 16}
+ if (lat < 75.42056257) {return 15}
+ if (lat < 76.39684391) {return 14}
+ if (lat < 77.36789461) {return 13}
+ if (lat < 78.33374083) {return 12}
+ if (lat < 79.29428225) {return 11}
+ if (lat < 80.24923213) {return 10}
+ if (lat < 81.19801349) {return 9}
+ if (lat < 82.13956981) {return 8}
+ if (lat < 83.07199445) {return 7}
+ if (lat < 83.99173563) {return 6}
+ if (lat < 84.89166191) {return 5}
+ if (lat < 85.75541621) {return 4}
+ if (lat < 86.53536998) {return 3}
+ if (lat < 87.00000000) {return 2}
+ else {return 1}
+}
+
+func cpr_NFunction(_ lat: Double, _ isodd: Int) -> Int {
+ var nl:Int = cpr_NLFunction(lat) - isodd;
+ if (nl < 1) {
+ nl = 1
+ }
+ return nl;
+}
+
+func cpr_DlonFunction(_ lat: Double, _ isodd: Int) -> Double {
+ return 360.0 / Double(cpr_NFunction(lat, isodd));
+}
+
+func mstime() -> Int64 {
+ var tv:timeval = timeval(tv_sec: 0, tv_usec: 0)
+ var mst:Int64=0;
+
+ gettimeofday(&tv, nil);
+ mst = Int64(tv.tv_sec)*1000;
+ mst += Int64(tv.tv_usec)/1000;
+ return mst;
+}
+
+class PositionDecoder {
+
+ var long: Double = 0.0
+ var lat: Double = 0.0
+ var ready: Bool = false
+ var newposition: Bool = false
+
+ //initialise first values so this values is nonsence
+ var queue: Deque<PositionData> = []
+
+ init() {
+
+ }
+
+ func addEvenPosition(_ cpr_lat: UInt32,_ cpr_long: UInt32, _ cprtime: Int64) {
+ queue.append(PositionData(cpr_lat:cpr_lat, cpr_long:cpr_long, even:true, cprtime: cprtime))
+ }
+
+ func addOddPosition(_ cpr_lat: UInt32,_ cpr_long: UInt32, _ cprtime: Int64) {
+ queue.append(PositionData(cpr_lat:cpr_lat,cpr_long:cpr_long,even:false, cprtime: cprtime))
+ }
+
+ func getPosition() -> (Double, Double)? {
+ if (ready) {
+ return (lat, long)
+ }
+ return nil
+ }
+
+ //TODO: allways adds to queue newer frees the queue
+ func calcPosition() -> Bool {
+ if (queue.count > 2) {
+ let el1 = queue[queue.count-1]
+ let el2 = queue[queue.count-2]
+ if (el1.even == el2.even) {
+ //ready = false
+ return false
+ }
+ } else {
+ ready = false
+ print("Position queue to short to calculate values")
+ return false
+ }
+
+ //last to elements are evan and odd
+ let el1 = queue[queue.count-1]
+ let el2 = queue[queue.count-2]
+ let cpr_even = el1.even ? el1 : el2
+ let cpr_odd = (!el1.even) ? el1 : el2
+
+ //print("Position queue is ready to calculate location \(cpr_even) \(cpr_odd)")
+ // from here https://github.com/antirez/dump1090/blob/master/dump1090.c#L1718
+ let AirDlat0:Double = 360.0/60.0
+ let AirDlat1:Double = 360.0/59.0
+
+ let lat0 = Int(cpr_even.cpr_lat)
+ let lat1 = Int(cpr_odd.cpr_lat)
+ let lon0 = Int(cpr_even.cpr_long)
+ let lon1 = Int(cpr_odd.cpr_long)
+
+ //latitude index
+ let j:Int = Int(floor(((59.0*Double(lat0) - 60.0*Double(lat1)) / 131072.0) + 0.5))
+
+ var rlat0:Double = AirDlat0 * (Double(cpr_mod(j,60)) + Double(lat0) / 131072.0)
+ var rlat1:Double = AirDlat1 * (Double(cpr_mod(j,59)) + Double(lat1) / 131072.0)
+
+ if (rlat0 >= 270.0) {
+ rlat0 -= 360.0
+ }
+ if (rlat1 >= 270.0) {
+ rlat1 -= 360.0
+ }
+
+ /* Check that both are in the same latitude zone, or abort. */
+ if (cpr_NLFunction(rlat0) != cpr_NLFunction(rlat1)) {
+ print("Not same lat?!")
+ return false
+ }
+
+ /* Compute ni and the longitude index m */
+ if (cpr_even.cprtime > cpr_odd.cprtime) {
+ /* Use even packet. */
+ let ni:Int = cpr_NFunction(rlat0,0);
+ let m:Int = Int(
+ floor((((Double(lon0) * (Double(cpr_NLFunction(rlat0))-1.0)) -
+ (Double(lon1) * Double(cpr_NLFunction(rlat0)))) / 131072.0) + 0.5)
+ );
+ self.long = Double(cpr_DlonFunction(rlat0,0)) * (Double(cpr_mod(m,ni))+Double(lon0)/131072.0);
+ self.lat = rlat0;
+ } else {
+ /* Use odd packet. */
+ let ni:Int = cpr_NFunction(rlat1,1);
+ let m:Int = Int(floor((((Double(lon0) * (Double(cpr_NLFunction(rlat1))-1.0)) -
+ (Double(lon1) * Double(cpr_NLFunction(rlat1)))) / 131072.0) + 0.5));
+ self.long = cpr_DlonFunction(rlat1,1) * (Double(cpr_mod(m,ni))+Double(lon1)/131072.0);
+ self.lat = rlat1;
+ }
+ if (self.long > 180) {
+ self.long -= 360
+ }
+
+ ready = true
+ return true
+ }
+
+
+}
diff --git a/ADSBDecoderFramework/Query.swift b/ADSBDecoderFramework/Query.swift
new file mode 100644
index 0000000..a92d919
--- /dev/null
+++ b/ADSBDecoderFramework/Query.swift
@@ -0,0 +1,126 @@
+//
+// Query.swift
+// ADSBDecoder
+//
+// Created by Jacky Jack on 31/05/2024.
+//
+
+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 = "DF17 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
+ }
+}
+
+class QueryDecodedMessages: CustomStringConvertible {
+
+ var decoded:Int = 0
+ var total:Int = 0
+
+ init() {
+
+ }
+
+ func addDecoded() {
+ decoded += 1
+ total += 1
+ }
+
+ func addUndecoded() {
+ total = total + 1
+ }
+
+ var description: String {
+ var description = "Total messages:\n"
+ description += "Decoded:\(decoded)\n"
+ description += "Total :\(total) \(String(format:"%02.01f",Float(Float(decoded)/Float(total)*100.0)))%\n"
+ return description
+ }
+
+}
+
+
+class QueryDF17TC_decoded: CustomStringConvertible {
+ var TClist_decoded:[Int] = Array(repeating: 0, count: 32)
+ var TClist_total:[Int] = Array(repeating: 0, count: 32)
+
+ init() {
+
+ }
+
+ func addDecoded(_ typecode: Int) {
+ TClist_decoded[typecode] += 1
+ TClist_total[typecode] += 1
+ }
+
+ func addUndecode(_ typecode: Int) {
+ TClist_total[typecode] += 1
+ }
+
+ var description: String {
+ var description = ""
+ var decoded:Int = 0
+ var total:Int = 0
+ for i in 0...(TClist_decoded.count-1) {
+ total += TClist_total[i]
+ decoded += TClist_decoded[i]
+ if TClist_total[i] != 0 {
+ description += String(format: " %02d: %04d\n", i, TClist_decoded[i])
+ }
+ }
+ description += String(format:"Total %d/%d %.1f",decoded,total,Double(Double(decoded)/Double(total))*100)
+ description += "%\n"
+ return description
+ }
+}
+