summaryrefslogtreecommitdiff
path: root/ADSBDecoder/Decoder.swift
blob: 65a9ddd48f38871473ac2ec564a5b5f3dfa20c6f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
//
//  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])
            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])
            print(String(format:"=====ADSB MESSSGE %02d ======= AA:%04d", tc_byte, AddressAnnounced))
            print(msg)
            print("============================")
            
            messageAirbornPositon = msg
        //airborn velocity
        } else if (tc_byte == 19) {
            print("=====ADSB MESSSGE 19 =======")
            print("=====VELOCITY =======")
        } else {
            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 ""
    }
}