|
6 | 6 | import pyModeS as pms
|
7 | 7 | import traceback
|
8 | 8 | import zmq
|
| 9 | +import math |
9 | 10 |
|
10 | 11 |
|
11 | 12 | class TcpClient(object):
|
@@ -149,6 +150,103 @@ def read_beast_buffer(self):
|
149 | 150 | messages.append([msg, ts])
|
150 | 151 | return messages
|
151 | 152 |
|
| 153 | + def read_beast_buffer_rssi_piaware(self): |
| 154 | + """Handle mode-s beast data type. |
| 155 | +
|
| 156 | + <esc> "1" : 6 byte MLAT timestamp, 1 byte signal level, |
| 157 | + 2 byte Mode-AC |
| 158 | + <esc> "2" : 6 byte MLAT timestamp, 1 byte signal level, |
| 159 | + 7 byte Mode-S short frame |
| 160 | + <esc> "3" : 6 byte MLAT timestamp, 1 byte signal level, |
| 161 | + 14 byte Mode-S long frame |
| 162 | + <esc> "4" : 6 byte MLAT timestamp, status data, DIP switch |
| 163 | + configuration settings (not on Mode-S Beast classic) |
| 164 | + <esc><esc>: true 0x1a |
| 165 | + <esc> is 0x1a, and "1", "2" and "3" are 0x31, 0x32 and 0x33 |
| 166 | +
|
| 167 | + timestamp: |
| 168 | + wiki.modesbeast.com/Radarcape:Firmware_Versions#The_GPS_timestamp |
| 169 | + """ |
| 170 | + messages_mlat = [] |
| 171 | + msg = [] |
| 172 | + i = 0 |
| 173 | + |
| 174 | + # process the buffer until the last divider <esc> 0x1a |
| 175 | + # then, reset the self.buffer with the remainder |
| 176 | + |
| 177 | + while i < len(self.buffer): |
| 178 | + if self.buffer[i : i + 2] == [0x1A, 0x1A]: |
| 179 | + msg.append(0x1A) |
| 180 | + i += 1 |
| 181 | + elif (i == len(self.buffer) - 1) and (self.buffer[i] == 0x1A): |
| 182 | + # special case where the last bit is 0x1a |
| 183 | + msg.append(0x1A) |
| 184 | + elif self.buffer[i] == 0x1A: |
| 185 | + if i == len(self.buffer) - 1: |
| 186 | + # special case where the last bit is 0x1a |
| 187 | + msg.append(0x1A) |
| 188 | + elif len(msg) > 0: |
| 189 | + messages_mlat.append(msg) |
| 190 | + msg = [] |
| 191 | + else: |
| 192 | + msg.append(self.buffer[i]) |
| 193 | + i += 1 |
| 194 | + |
| 195 | + # save the reminder for next reading cycle, if not empty |
| 196 | + if len(msg) > 0: |
| 197 | + reminder = [] |
| 198 | + for i, m in enumerate(msg): |
| 199 | + if (m == 0x1A) and (i < len(msg) - 1): |
| 200 | + # rewind 0x1a, except when it is at the last bit |
| 201 | + reminder.extend([m, m]) |
| 202 | + else: |
| 203 | + reminder.append(m) |
| 204 | + self.buffer = [0x1A] + msg |
| 205 | + else: |
| 206 | + self.buffer = [] |
| 207 | + |
| 208 | + # extract messages |
| 209 | + messages = [] |
| 210 | + for mm in messages_mlat: |
| 211 | + ts = time.time() |
| 212 | + |
| 213 | + msgtype = mm[0] |
| 214 | + # print(''.join('%02X' % i for i in mm)) |
| 215 | + |
| 216 | + if msgtype == 0x32: |
| 217 | + # Mode-S Short Message, 7 byte, 14-len hexstr |
| 218 | + msg = "".join("%02X" % i for i in mm[8:15]) |
| 219 | + elif msgtype == 0x33: |
| 220 | + # Mode-S Long Message, 14 byte, 28-len hexstr |
| 221 | + msg = "".join("%02X" % i for i in mm[8:22]) |
| 222 | + else: |
| 223 | + # Other message tupe |
| 224 | + continue |
| 225 | + |
| 226 | + if len(msg) not in [14, 28]: |
| 227 | + continue |
| 228 | + |
| 229 | + ''' |
| 230 | + we get the raw 0-255 byte value (raw_rssi = mm[7]) |
| 231 | + we scale it to 0.0 - 1.0 (voltage = raw_rssi / 255) |
| 232 | + we convert it to a dBFS power value (rolling the squaring of the voltage into the dB calculation) |
| 233 | + ''' |
| 234 | + |
| 235 | + df = pms.df(msg) |
| 236 | + raw_rssi = mm[7] # eighth byte of Mode-S message should contain RSSI value |
| 237 | + rssi_ratio = raw_rssi / 255 |
| 238 | + signalLevel = rssi_ratio ** 2 |
| 239 | + dbfs_rssi = 10 * math.log10(signalLevel) |
| 240 | + |
| 241 | + # skip incomplete message |
| 242 | + if df in [0, 4, 5, 11] and len(msg) != 14: |
| 243 | + continue |
| 244 | + if df in [16, 17, 18, 19, 20, 21, 24] and len(msg) != 28: |
| 245 | + continue |
| 246 | + |
| 247 | + messages.append([msg, dbfs_rssi, ts]) |
| 248 | + return messages |
| 249 | + |
152 | 250 | def read_skysense_buffer(self):
|
153 | 251 | """Skysense stream format.
|
154 | 252 |
|
|
0 commit comments