adding support for rtl-sdr tunner

This commit is contained in:
Junzi Sun 2019-08-22 10:45:12 +02:00
parent 7267859548
commit 4a3c9438f7
5 changed files with 464 additions and 225 deletions

140
pyModeS/extra/rtlreader.py Normal file
View File

@ -0,0 +1,140 @@
import sys
import numpy as np
import pyModeS as pms
from rtlsdr import RtlSdr
from threading import Thread
import time
amplitude_threshold = 0.2
modes_sample_rate = 2e6
modes_frequency = 1090e6
buffer_size = 1024 * 100
read_size = 1024 * 8
pbits = 8
fbits = 112
preamble = "1010000101000000"
class RtlReader(Thread):
def __init__(self, debug=False):
super(RtlReader, self).__init__()
self.signal_buffer = np.array([])
self.debug = debug
self.sdr = RtlSdr()
self.sdr.sample_rate = modes_sample_rate
self.sdr.center_freq = modes_frequency
self.sdr.gain = "auto"
# sdr.freq_correction = 75
def _process_buffer(self):
messages = []
pulses_array = np.where(self.signal_buffer < amplitude_threshold, 0, 1)
pulses = "".join(str(x) for x in pulses_array)
i = 0
while i < len(pulses):
if pulses[i] == 0:
i += 1
continue
if pulses[i : i + pbits * 2] == preamble:
frame_start = i + pbits * 2
frame_end = i + pbits * 2 + (fbits + 1) * 2
frame_pulses = pulses[frame_start:frame_end]
msgbin = ""
for j in range(0, len(frame_pulses), 2):
p2 = frame_pulses[j : j + 2]
if p2 == "10":
c = "1"
elif p2 == "01":
c = "0"
elif p2 == "11":
a2 = self.signal_buffer[
frame_start + j : frame_start + j + 2
]
if a2[0] > a2[1]:
c = "1"
else:
c = "0"
elif p2 == "00":
break
else:
msgbin = ""
break
msgbin += c
# advance i with a jump
i = frame_start + j
if len(msgbin) > 0:
msghex = pms.bin2hex(msgbin)
if self._check_msg(msghex):
messages.append([msghex, time.time()])
if self.debug:
self._debug_msg(msghex)
elif i > len(self.signal_buffer) - pbits * 2 - fbits * 2:
break
else:
i += 1
# keep reminder of buffer for next iteration
self.signal_buffer = self.signal_buffer[i:]
return messages
def _check_msg(self, msg):
df = pms.df(msg)
msglen = len(msg)
if df == 17 and msglen == 28:
if pms.crc(msg) == 0:
return True
elif df in [20, 21] and msglen == 28:
return True
elif df in [4, 5, 11] and msglen == 14:
return True
def _debug_msg(self, msg):
df = pms.df(msg)
msglen = len(msg)
if df == 17 and msglen == 28:
print(msg, pms.icao(msg), pms.crc(msg))
elif df in [20, 21] and msglen == 28:
print(msg, pms.icao(msg))
elif df in [4, 5, 11] and msglen == 14:
print(msg, pms.icao(msg))
else:
print("[*]", msg)
pass
def _read_callback(self, data, rtlsdr_obj):
self.signal_buffer = np.concatenate(
(self.signal_buffer, np.absolute(data))
)
if len(self.signal_buffer) >= buffer_size:
try:
messages = self._process_buffer()
self.handle_messages(messages)
except KeyboardInterrupt:
sys.exit(1)
def handle_messages(self, messages):
"""re-implement this method to handle the messages"""
for msg, t in messages:
pass
# print("%15.9f %s" % (t, msg))
def run(self):
self.sdr.read_samples_async(self._read_callback, read_size)
# while True:
# data = self.sdr.read_samples(read_size)
# self._read_callback(data, None)
if __name__ == "__main__":
rtl = RtlReader()
rtl.debug = True
# rtl.daemon = True
rtl.start()

View File

@ -1,6 +1,6 @@
'''
"""
Stream beast raw data from a TCP server, convert to mode-s messages
'''
"""
from __future__ import print_function, division
import os
import sys
@ -9,36 +9,31 @@ import time
import pyModeS as pms
from threading import Thread
import traceback
import zmq
if (sys.version_info > (3, 0)):
if sys.version_info > (3, 0):
PY_VERSION = 3
else:
PY_VERSION = 2
class BaseClient(Thread):
def __init__(self, host, port, rawtype):
Thread.__init__(self)
self.host = host
self.port = port
self.buffer = []
self.socket = None
self.rawtype = rawtype
if self.rawtype not in ['avr', 'beast', 'skysense']:
if self.rawtype not in ["avr", "beast", "skysense"]:
print("rawtype must be either avr, beast or skysense")
os._exit(1)
def connect(self):
while True:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(10) # 10 second timeout
s.connect((self.host, self.port))
print("Server connected - %s:%s" % (self.host, self.port))
print("collecting ADS-B messages...")
return s
except socket.error as err:
print("Socket connection error: %s. reconnecting..." % err)
time.sleep(3)
self.socket = zmq.Context().socket(zmq.STREAM)
self.socket.setsockopt(zmq.LINGER, 0)
self.socket.setsockopt(zmq.RCVTIMEO, 2000)
self.socket.connect("tcp://%s:%s" % (self.host, self.port))
def read_avr_buffer(self):
# -- testing --
@ -57,9 +52,9 @@ class BaseClient(Thread):
messages.append([self.current_msg, ts])
if b == 42:
msg_stop = False
self.current_msg = ''
self.current_msg = ""
if (not msg_stop) and (48<=b<=57 or 65<=b<=70 or 97<=b<=102):
if (not msg_stop) and (48 <= b <= 57 or 65 <= b <= 70 or 97 <= b <= 102):
self.current_msg = self.current_msg + chr(b)
self.buffer = []
@ -67,7 +62,7 @@ class BaseClient(Thread):
return messages
def read_beast_buffer(self):
'''
"""
<esc> "1" : 6 byte MLAT timestamp, 1 byte signal level,
2 byte Mode-AC
<esc> "2" : 6 byte MLAT timestamp, 1 byte signal level,
@ -81,7 +76,7 @@ class BaseClient(Thread):
timestamp:
wiki.modesbeast.com/Radarcape:Firmware_Versions#The_GPS_timestamp
'''
"""
messages_mlat = []
msg = []
@ -91,16 +86,16 @@ class BaseClient(Thread):
# then, reset the self.buffer with the remainder
while i < len(self.buffer):
if (self.buffer[i:i+2] == [0x1a, 0x1a]):
msg.append(0x1a)
if self.buffer[i : i + 2] == [0x1A, 0x1A]:
msg.append(0x1A)
i += 1
elif (i == len(self.buffer) - 1) and (self.buffer[i] == 0x1a):
elif (i == len(self.buffer) - 1) and (self.buffer[i] == 0x1A):
# special case where the last bit is 0x1a
msg.append(0x1a)
elif self.buffer[i] == 0x1a:
msg.append(0x1A)
elif self.buffer[i] == 0x1A:
if i == len(self.buffer) - 1:
# special case where the last bit is 0x1a
msg.append(0x1a)
msg.append(0x1A)
elif len(msg) > 0:
messages_mlat.append(msg)
msg = []
@ -112,12 +107,12 @@ class BaseClient(Thread):
if len(msg) > 0:
reminder = []
for i, m in enumerate(msg):
if (m == 0x1a) and (i < len(msg)-1):
if (m == 0x1A) and (i < len(msg) - 1):
# rewind 0x1a, except when it is at the last bit
reminder.extend([m, m])
else:
reminder.append(m)
self.buffer = [0x1a] + msg
self.buffer = [0x1A] + msg
else:
self.buffer = []
@ -131,10 +126,10 @@ class BaseClient(Thread):
if msgtype == 0x32:
# Mode-S Short Message, 7 byte, 14-len hexstr
msg = ''.join('%02X' % i for i in mm[8:15])
msg = "".join("%02X" % i for i in mm[8:15])
elif msgtype == 0x33:
# Mode-S Long Message, 14 byte, 28-len hexstr
msg = ''.join('%02X' % i for i in mm[8:22])
msg = "".join("%02X" % i for i in mm[8:22])
else:
# Other message tupe
continue
@ -215,25 +210,33 @@ class BaseClient(Thread):
messages = []
while len(self.buffer) > SS_MSGLENGTH:
i = 0
if self.buffer[i] == SS_STARTCHAR and self.buffer[i+SS_MSGLENGTH] == SS_STARTCHAR:
if (
self.buffer[i] == SS_STARTCHAR
and self.buffer[i + SS_MSGLENGTH] == SS_STARTCHAR
):
i += 1
if (self.buffer[i]>>7):
#Long message
payload = self.buffer[i:i+14]
if self.buffer[i] >> 7:
# Long message
payload = self.buffer[i : i + 14]
else:
#Short message
payload = self.buffer[i:i+7]
msg = ''.join('%02X' % j for j in payload)
i += 14 #Both message types use 14 bytes
tsbin = self.buffer[i:i+6]
sec = ( (tsbin[0] & 0x7f) << 10) | (tsbin[1] << 2 ) | (tsbin[2] >> 6)
nano = ( (tsbin[2] & 0x3f) << 24) | (tsbin[3] << 16) | (tsbin[4] << 8) | tsbin[5]
ts = sec + nano*1.0e-9
# Short message
payload = self.buffer[i : i + 7]
msg = "".join("%02X" % j for j in payload)
i += 14 # Both message types use 14 bytes
tsbin = self.buffer[i : i + 6]
sec = ((tsbin[0] & 0x7F) << 10) | (tsbin[1] << 2) | (tsbin[2] >> 6)
nano = (
((tsbin[2] & 0x3F) << 24)
| (tsbin[3] << 16)
| (tsbin[4] << 8)
| tsbin[5]
)
ts = sec + nano * 1.0e-9
i += 6
#Signal and noise level - Don't care for now
# Signal and noise level - Don't care for now
i += 3
self.buffer = self.buffer[SS_MSGLENGTH:]
messages.append( [msg,ts] )
messages.append([msg, ts])
else:
self.buffer = self.buffer[1:]
return messages
@ -244,11 +247,11 @@ class BaseClient(Thread):
print("%15.9f %s" % (t, msg))
def run(self):
sock = self.connect()
self.connect()
while True:
try:
received = sock.recv(1024)
received = [i for i in self.socket.recv(4096)]
if PY_VERSION == 2:
received = [ord(i) for i in received]
@ -261,11 +264,11 @@ class BaseClient(Thread):
# continue
# -- Removed!! Cause delay in low data rate scenario --
if self.rawtype == 'beast':
if self.rawtype == "beast":
messages = self.read_beast_buffer()
elif self.rawtype == 'avr':
elif self.rawtype == "avr":
messages = self.read_avr_buffer()
elif self.rawtype == 'skysense':
elif self.rawtype == "skysense":
messages = self.read_skysense_buffer()
if not messages:
@ -279,8 +282,8 @@ class BaseClient(Thread):
# Provides the user an option to supply the environment
# variable PYMODES_DEBUG to halt the execution
# for debugging purposes
debug_intent = os.environ.get('PYMODES_DEBUG', 'false')
if debug_intent.lower() == 'true':
debug_intent = os.environ.get("PYMODES_DEBUG", "false")
if debug_intent.lower() == "true":
traceback.print_exc()
sys.exit()
else:
@ -292,7 +295,7 @@ class BaseClient(Thread):
print("Unexpected Error:", e)
if __name__ == '__main__':
if __name__ == "__main__":
# for testing purpose only
host = sys.argv[1]
port = int(sys.argv[2])

View File

@ -9,6 +9,7 @@ import curses
from threading import Lock
import pyModeS as pms
from pyModeS.extra.tcpclient import BaseClient
from pyModeS.extra.rtlreader import RtlReader
from pyModeS.streamer.stream import Stream
from pyModeS.streamer.screen import Screen
@ -19,29 +20,67 @@ COMMB_MSG = []
COMMB_TS = []
parser = argparse.ArgumentParser()
parser.add_argument('--server', help='server address or IP', required=True)
parser.add_argument('--port', help='raw data port', required=True)
parser.add_argument('--rawtype', help='beast, avr or skysense', required=True)
parser.add_argument('--latlon', help='receiver position', nargs=2, metavar=('LAT', 'LON'), required=True)
parser.add_argument('--show-uncertainty', dest='uncertainty', help='display uncertaint values, default off', action='store_true', required=False, default=False)
parser.add_argument('--dumpto', help='folder to dump decoded output', required=False, default=None)
parser.add_argument(
"--source", help="rtlsdr or tcp", required=True, default="tcp"
)
parser.add_argument("--server", help="server address or IP", default=None)
parser.add_argument("--port", help="raw data port", default=None)
parser.add_argument("--rawtype", help="beast, avr or skysense", default=None)
parser.add_argument(
"--latlon",
help="receiver position",
nargs=2,
metavar=("LAT", "LON"),
required=True,
)
parser.add_argument(
"--show-uncertainty",
dest="uncertainty",
help="display uncertaint values, default off",
action="store_true",
required=False,
default=False,
)
parser.add_argument(
"--dumpto",
help="folder to dump decoded output",
required=False,
default=None,
)
args = parser.parse_args()
SOURCE = args.source
SERVER = args.server
PORT = int(args.port)
PORT = args.port
RAWTYPE = args.rawtype
LAT0 = float(args.latlon[0])
LON0 = float(args.latlon[1])
UNCERTAINTY = args.uncertainty
DUMPTO = args.dumpto
if SOURCE == "rtlsdr":
pass
elif SOURCE == "tcp":
if SERVER is None:
print("You must specify the server for TCP source.")
sys.exit(1)
if PORT is None:
print("You must specify the port for TCP source.")
sys.exit(1)
if RAWTYPE is None:
print("You must specify the rawtype for TCP source.")
sys.exit(1)
else:
print("Source must be rtlsdr or tcp.")
sys.exit(1)
if DUMPTO is not None:
# append to current folder except root is given
if DUMPTO[0] != '/':
DUMPTO = os.getcwd() + '/' + DUMPTO
if DUMPTO[0] != "/":
DUMPTO = os.getcwd() + "/" + DUMPTO
if not os.path.isdir(DUMPTO):
print('Error: dump folder (%s) does not exist' % DUMPTO)
print("Error: dump folder (%s) does not exist" % DUMPTO)
sys.exit(1)
@ -56,7 +95,7 @@ class ModesClient(BaseClient):
local_buffer_ehs_ts = []
for msg, t in messages:
if len(msg) < 28: # only process long messages
if len(msg) < 28: # only process long messages
continue
df = pms.df(msg)
@ -70,7 +109,6 @@ class ModesClient(BaseClient):
else:
continue
LOCK.acquire()
ADSB_MSG.extend(local_buffer_adsb_msg)
ADSB_TS.extend(local_buffer_adsb_ts)
@ -79,12 +117,55 @@ class ModesClient(BaseClient):
LOCK.release()
# redirect all stdout to null, avoiding messing up with the screen
sys.stdout = open(os.devnull, 'w')
class ModesRtlReader(RtlReader):
"""docstring for ModesRtlReader."""
client = ModesClient(host=SERVER, port=PORT, rawtype=RAWTYPE)
client.daemon = True
client.start()
def __init__(self):
super(ModesRtlReader, self).__init__()
def handle_messages(self, messages):
local_buffer_adsb_msg = []
local_buffer_adsb_ts = []
local_buffer_ehs_msg = []
local_buffer_ehs_ts = []
for msg, t in messages:
if len(msg) < 28: # only process long messages
continue
df = pms.df(msg)
if df == 17 or df == 18:
local_buffer_adsb_msg.append(msg)
local_buffer_adsb_ts.append(t)
elif df == 20 or df == 21:
local_buffer_ehs_msg.append(msg)
local_buffer_ehs_ts.append(t)
else:
continue
LOCK.acquire()
ADSB_MSG.extend(local_buffer_adsb_msg)
ADSB_TS.extend(local_buffer_adsb_ts)
COMMB_MSG.extend(local_buffer_ehs_msg)
COMMB_TS.extend(local_buffer_ehs_ts)
# print(len(ADSB_MSG))
# print(len(COMMB_MSG))
LOCK.release()
# redirect all stdout to null, avoiding messing up with the screen
sys.stdout = open(os.devnull, "w")
if SOURCE == "tcp":
client = ModesClient(host=SERVER, port=PORT, rawtype=RAWTYPE)
client.daemon = True
client.start()
elif SOURCE == "rtlsdr":
rtl = ModesRtlReader()
# rtl.debug = True
rtl.daemon = True
rtl.start()
stream = Stream(lat0=LAT0, lon0=LON0, dumpto=DUMPTO)
@ -94,7 +175,7 @@ try:
screen.start()
while True:
if len(ADSB_MSG) > 200:
if len(ADSB_MSG) > 1:
LOCK.acquire()
stream.process_raw(ADSB_TS, ADSB_MSG, COMMB_TS, COMMB_MSG)
ADSB_MSG = []
@ -107,7 +188,7 @@ try:
try:
screen.update_data(acs)
screen.update()
time.sleep(0.02)
time.sleep(1)
except KeyboardInterrupt:
raise
except:

View File

@ -6,38 +6,39 @@ import time
from threading import Thread
COLUMNS = [
('call', 10),
('lat', 10),
('lon', 10),
('alt', 7),
('gs', 5),
('tas', 5),
('ias', 5),
('mach', 7),
('roc', 7),
('trk', 10),
('hdg', 10),
('live', 6),
("call", 10),
("lat", 10),
("lon", 10),
("alt", 7),
("gs", 5),
("tas", 5),
("ias", 5),
("mach", 7),
("roc", 7),
("trk", 10),
("hdg", 10),
("live", 6),
]
UNCERTAINTY_COLUMNS = [
('|', 5),
('ver', 4),
('HPL', 5),
('RCu', 5),
('RCv', 5),
('HVE', 5),
('VVE', 5),
('Rc', 4),
('VPL', 5),
('EPU', 5),
('VEPU', 6),
('HFOMr', 7),
('VFOMr', 7),
('PE_RCu', 8),
('PE_VPL', 8),
("|", 5),
("ver", 4),
("HPL", 5),
("RCu", 5),
("RCv", 5),
("HVE", 5),
("VVE", 5),
("Rc", 4),
("VPL", 5),
("EPU", 5),
("VEPU", 6),
("HFOMr", 7),
("VFOMr", 7),
("PE_RCu", 8),
("PE_VPL", 8),
]
class Screen(Thread):
def __init__(self, uncertainty=False):
Thread.__init__(self)
@ -55,7 +56,6 @@ class Screen(Thread):
if uncertainty:
self.columns.extend(UNCERTAINTY_COLUMNS)
def reset_cursor_pos(self):
self.screen.move(self.y, self.x)
@ -64,7 +64,12 @@ class Screen(Thread):
def draw_frame(self):
self.screen.border(0)
self.screen.addstr(0, 2, "Online aircraft [%d] ('Ctrl+C' to exit, 'Enter' to lock one)" % len(self.acs))
self.screen.addstr(
0,
2,
"Online aircraft [%d] ('Ctrl+C' to exit, 'Enter' to lock one)"
% len(self.acs),
)
def update(self):
if len(self.acs) == 0:
@ -81,21 +86,20 @@ class Screen(Thread):
row = 1
header = ' icao'
header = " icao"
for c, cw in self.columns:
header += (cw-len(c))*' ' + c
header += (cw - len(c)) * " " + c
# fill end with spaces
header += (self.scr_w - 2 - len(header)) * ' '
header += (self.scr_w - 2 - len(header)) * " "
if len(header) > self.scr_w - 2:
header = header[:self.scr_w-3] + '>'
header = header[: self.scr_w - 3] + ">"
self.screen.addstr(row, 1, header)
row +=1
self.screen.addstr(row, 1, '-'*(self.scr_w-2))
row += 1
self.screen.addstr(row, 1, "-" * (self.scr_w - 2))
icaos = np.array(list(self.acs.keys()))
icaos = np.sort(icaos)
@ -105,10 +109,10 @@ class Screen(Thread):
idx = row + self.offset - 3
if idx > len(icaos) - 1:
line = ' '*(self.scr_w-2)
line = " " * (self.scr_w - 2)
else:
line = ''
line = ""
icao = icaos[idx]
ac = self.acs[icao]
@ -116,22 +120,22 @@ class Screen(Thread):
line += icao
for c, cw in self.columns:
if c=='|':
val = '|'
elif c=='live':
val = str(int(time.time() - ac[c]))+'s'
if c == "|":
val = "|"
elif c == "live":
val = str(ac[c] - int(time.time())) + "s"
elif ac[c] is None:
val = ''
val = ""
else:
val = ac[c]
val_str = str(val)
line += (cw-len(val_str))*' ' + val_str
line += (cw - len(val_str)) * " " + val_str
# fill end with spaces
line += (self.scr_w - 2 - len(line)) * ' '
line += (self.scr_w - 2 - len(line)) * " "
if len(line) > self.scr_w - 2:
line = line[:self.scr_w-3] + '>'
line = line[: self.scr_w - 3] + ">"
if (icao is not None) and (self.lock_icao == icao):
self.screen.addstr(row, 1, line, curses.A_STANDOUT)
@ -140,11 +144,13 @@ class Screen(Thread):
else:
self.screen.addstr(row, 1, line)
self.screen.addstr(self.scr_h-3, 1, '-'*(self.scr_w-2))
self.screen.addstr(self.scr_h - 3, 1, "-" * (self.scr_w - 2))
total_page = len(icaos) // (self.scr_h - 4) + 1
current_page = self.offset // (self.scr_h - 4) + 1
self.screen.addstr(self.scr_h-2, 1, '(%d / %d)' % (current_page, total_page))
self.screen.addstr(
self.scr_h - 2, 1, "(%d / %d)" % (current_page, total_page)
)
self.reset_cursor_pos()
@ -168,7 +174,7 @@ class Screen(Thread):
self.offset = offset_intent
else:
self.offset = 0
elif c == curses.KEY_DOWN :
elif c == curses.KEY_DOWN:
y_intent = self.y + 1
if y_intent < self.scr_h - 3:
self.y = y_intent

View File

@ -5,7 +5,8 @@ import datetime
import csv
import pyModeS as pms
class Stream():
class Stream:
def __init__(self, lat0, lon0, dumpto=None):
self.acs = dict()
@ -14,15 +15,13 @@ class Stream():
self.lon0 = lon0
self.t = 0
self.cache_timeout = 60 # seconds
self.cache_timeout = 60 # seconds
if dumpto is not None and os.path.isdir(dumpto):
self.dumpto = dumpto
else:
self.dumpto = None
def process_raw(self, adsb_ts, adsb_msgs, commb_ts, commb_msgs, tnow=None):
"""process a chunk of adsb and commb messages recieved in the same
time period.
@ -42,43 +41,43 @@ class Stream():
if icao not in self.acs:
self.acs[icao] = {
'live': None,
'call': None,
'lat': None,
'lon': None,
'alt': None,
'gs': None,
'trk': None,
'roc': None,
'tas': None,
'roll': None,
'rtrk': None,
'ias': None,
'mach': None,
'hdg': None,
'ver' : None,
'HPL' : None,
'RCu' : None,
'RCv' : None,
'HVE' : None,
'VVE' : None,
'Rc' : None,
'VPL' : None,
'EPU' : None,
'VEPU' : None,
'HFOMr' : None,
'VFOMr' : None,
'PE_RCu' : None,
'PE_VPL' : None,
"live": None,
"call": None,
"lat": None,
"lon": None,
"alt": None,
"gs": None,
"trk": None,
"roc": None,
"tas": None,
"roll": None,
"rtrk": None,
"ias": None,
"mach": None,
"hdg": None,
"ver": None,
"HPL": None,
"RCu": None,
"RCv": None,
"HVE": None,
"VVE": None,
"Rc": None,
"VPL": None,
"EPU": None,
"VEPU": None,
"HFOMr": None,
"VFOMr": None,
"PE_RCu": None,
"PE_VPL": None,
}
self.acs[icao]['t'] = t
self.acs[icao]['live'] = int(t)
self.acs[icao]["t"] = t
self.acs[icao]["live"] = int(t)
if 1 <= tc <= 4:
cs = pms.adsb.callsign(msg)
self.acs[icao]['call'] = cs
output_buffer.append([t, icao, 'cs', cs])
self.acs[icao]["call"] = cs
output_buffer.append([t, icao, "cs", cs])
if (5 <= tc <= 8) or (tc == 19):
vdata = pms.adsb.velocity(msg)
@ -86,42 +85,47 @@ class Stream():
continue
spd, trk, roc, tag = vdata
if tag != 'GS':
if tag != "GS":
continue
if (spd is None) or (trk is None):
continue
self.acs[icao]['gs'] = spd
self.acs[icao]['trk'] = trk
self.acs[icao]['roc'] = roc
self.acs[icao]['tv'] = t
self.acs[icao]["gs"] = spd
self.acs[icao]["trk"] = trk
self.acs[icao]["roc"] = roc
self.acs[icao]["tv"] = t
output_buffer.append([t, icao, 'gs', spd])
output_buffer.append([t, icao, 'trk', trk])
output_buffer.append([t, icao, 'roc', roc])
output_buffer.append([t, icao, "gs", spd])
output_buffer.append([t, icao, "trk", trk])
output_buffer.append([t, icao, "roc", roc])
if (5 <= tc <= 18):
if 5 <= tc <= 18:
oe = pms.adsb.oe_flag(msg)
self.acs[icao][oe] = msg
self.acs[icao]['t'+str(oe)] = t
self.acs[icao]["t" + str(oe)] = t
if ('tpos' in self.acs[icao]) and (t - self.acs[icao]['tpos'] < 180):
if ("tpos" in self.acs[icao]) and (
t - self.acs[icao]["tpos"] < 180
):
# use single message decoding
rlat = self.acs[icao]['lat']
rlon = self.acs[icao]['lon']
rlat = self.acs[icao]["lat"]
rlon = self.acs[icao]["lon"]
latlon = pms.adsb.position_with_ref(msg, rlat, rlon)
elif ('t0' in self.acs[icao]) and ('t1' in self.acs[icao]) and \
(abs(self.acs[icao]['t0'] - self.acs[icao]['t1']) < 10):
elif (
("t0" in self.acs[icao])
and ("t1" in self.acs[icao])
and (abs(self.acs[icao]["t0"] - self.acs[icao]["t1"]) < 10)
):
# use multi message decoding
try:
latlon = pms.adsb.position(
self.acs[icao][0],
self.acs[icao][1],
self.acs[icao]['t0'],
self.acs[icao]['t1'],
self.lat0, self.lon0
)
self.acs[icao]["t0"],
self.acs[icao]["t1"],
self.lat0,
self.lon0,
)
except:
# mix of surface and airborne position message
continue
@ -129,16 +133,16 @@ class Stream():
latlon = None
if latlon is not None:
self.acs[icao]['tpos'] = t
self.acs[icao]['lat'] = latlon[0]
self.acs[icao]['lon'] = latlon[1]
self.acs[icao]["tpos"] = t
self.acs[icao]["lat"] = latlon[0]
self.acs[icao]["lon"] = latlon[1]
alt = pms.adsb.altitude(msg)
self.acs[icao]['alt'] = alt
self.acs[icao]["alt"] = alt
output_buffer.append([t, icao, 'lat', latlon[0]])
output_buffer.append([t, icao, 'lon', latlon[1]])
output_buffer.append([t, icao, 'alt', alt])
output_buffer.append([t, icao, "lat", latlon[0]])
output_buffer.append([t, icao, "lon", latlon[1]])
output_buffer.append([t, icao, "alt", alt])
local_updated_acs_buffer.append(icao)
@ -146,35 +150,42 @@ class Stream():
ac = self.acs[icao]
if 9 <= tc <= 18:
ac['nic_bc'] = pms.adsb.nic_b(msg)
ac["nic_bc"] = pms.adsb.nic_b(msg)
if (5 <= tc <= 8) or (9 <= tc <= 18) or (20 <= tc <= 22):
ac['HPL'], ac['RCu'], ac['RCv'] = pms.adsb.nuc_p(msg)
ac["HPL"], ac["RCu"], ac["RCv"] = pms.adsb.nuc_p(msg)
if (ac['ver'] == 1) and ('nic_s' in ac.keys()):
ac['Rc'], ac['VPL'] = pms.adsb.nic_v1(msg, ac['nic_s'])
elif (ac['ver'] == 2) and ('nic_a' in ac.keys()) and ('nic_bc' in ac.keys()):
ac['Rc'] = pms.adsb.nic_v2(msg, ac['nic_a'], ac['nic_bc'])
if (ac["ver"] == 1) and ("nic_s" in ac.keys()):
ac["Rc"], ac["VPL"] = pms.adsb.nic_v1(msg, ac["nic_s"])
elif (
(ac["ver"] == 2)
and ("nic_a" in ac.keys())
and ("nic_bc" in ac.keys())
):
ac["Rc"] = pms.adsb.nic_v2(msg, ac["nic_a"], ac["nic_bc"])
if tc == 19:
ac['HVE'], ac['VVE'] = pms.adsb.nuc_v(msg)
if ac['ver'] in [1, 2]:
ac['HFOMr'], ac['VFOMr'] = pms.adsb.nac_v(msg)
ac["HVE"], ac["VVE"] = pms.adsb.nuc_v(msg)
if ac["ver"] in [1, 2]:
ac["HFOMr"], ac["VFOMr"] = pms.adsb.nac_v(msg)
if tc == 29:
ac['PE_RCu'], ac['PE_VPL'], ac['base'] = pms.adsb.sil(msg, ac['ver'])
ac['EPU'], ac['VEPU'] = pms.adsb.nac_p(msg)
ac["PE_RCu"], ac["PE_VPL"], ac["base"] = pms.adsb.sil(
msg, ac["ver"]
)
ac["EPU"], ac["VEPU"] = pms.adsb.nac_p(msg)
if tc == 31:
ac['ver'] = pms.adsb.version(msg)
ac['EPU'], ac['VEPU'] = pms.adsb.nac_p(msg)
ac['PE_RCu'], ac['PE_VPL'], ac['sil_base'] = pms.adsb.sil(msg, ac['ver'])
if ac['ver'] == 1:
ac['nic_s'] = pms.adsb.nic_s(msg)
elif ac['ver'] == 2:
ac['nic_a'], ac['nic_bc'] = pms.adsb.nic_a_c(msg)
ac["ver"] = pms.adsb.version(msg)
ac["EPU"], ac["VEPU"] = pms.adsb.nac_p(msg)
ac["PE_RCu"], ac["PE_VPL"], ac["sil_base"] = pms.adsb.sil(
msg, ac["ver"]
)
if ac["ver"] == 1:
ac["nic_s"] = pms.adsb.nic_s(msg)
elif ac["ver"] == 2:
ac["nic_a"], ac["nic_bc"] = pms.adsb.nic_a_c(msg)
# process commb message
for t, msg in zip(commb_ts, commb_msgs):
@ -183,32 +194,34 @@ class Stream():
if icao not in self.acs:
continue
self.acs[icao]["live"] = int(t)
bds = pms.bds.infer(msg)
if bds == 'BDS50':
if bds == "BDS50":
roll50 = pms.commb.roll50(msg)
trk50 = pms.commb.trk50(msg)
rtrk50 = pms.commb.rtrk50(msg)
gs50 = pms.commb.gs50(msg)
tas50 = pms.commb.tas50(msg)
self.acs[icao]['t50'] = t
self.acs[icao]["t50"] = t
if tas50:
self.acs[icao]['tas'] = tas50
output_buffer.append([t, icao, 'tas50', tas50])
self.acs[icao]["tas"] = tas50
output_buffer.append([t, icao, "tas50", tas50])
if roll50:
self.acs[icao]['roll'] = roll50
output_buffer.append([t, icao, 'roll50', roll50])
self.acs[icao]["roll"] = roll50
output_buffer.append([t, icao, "roll50", roll50])
if rtrk50:
self.acs[icao]['rtrk'] = rtrk50
output_buffer.append([t, icao, 'rtrk50', rtrk50])
self.acs[icao]["rtrk"] = rtrk50
output_buffer.append([t, icao, "rtrk50", rtrk50])
if trk50:
output_buffer.append([t, icao, 'trk50', trk50])
output_buffer.append([t, icao, "trk50", trk50])
if gs50:
output_buffer.append([t, icao, 'gs50', gs50])
output_buffer.append([t, icao, "gs50", gs50])
elif bds == 'BDS60':
elif bds == "BDS60":
ias60 = pms.commb.ias60(msg)
hdg60 = pms.commb.hdg60(msg)
mach60 = pms.commb.mach60(msg)
@ -216,28 +229,28 @@ class Stream():
roc60ins = pms.commb.vr60ins(msg)
if ias60 or hdg60 or mach60:
self.acs[icao]['t60'] = t
self.acs[icao]["t60"] = t
if ias60:
self.acs[icao]['ias'] = ias60
self.acs[icao]["ias"] = ias60
if hdg60:
self.acs[icao]['hdg'] = hdg60
self.acs[icao]["hdg"] = hdg60
if mach60:
self.acs[icao]['mach'] = mach60
self.acs[icao]["mach"] = mach60
if roc60baro:
output_buffer.append([t, icao, 'roc60baro', roc60baro])
output_buffer.append([t, icao, "roc60baro", roc60baro])
if roc60ins:
output_buffer.append([t, icao, 'roc60ins', roc60ins])
output_buffer.append([t, icao, "roc60ins", roc60ins])
# clear up old data
for icao in list(self.acs.keys()):
if self.t - self.acs[icao]['live'] > self.cache_timeout:
if self.t - self.acs[icao]["live"] > self.cache_timeout:
del self.acs[icao]
continue
if self.dumpto is not None:
dh = str(datetime.datetime.now().strftime("%Y%m%d_%H"))
fn = self.dumpto + '/pymodes_dump_%s.csv' % dh
fn = self.dumpto + "/pymodes_dump_%s.csv" % dh
output_buffer.sort(key=lambda x: x[0])
with open(fn, "a") as f:
writer = csv.writer(f)
@ -248,8 +261,4 @@ class Stream():
def get_aircraft(self):
"""all aircraft that are stored in memeory"""
acs = self.acs
icaos = list(acs.keys())
for icao in icaos:
if acs[icao]['lat'] is None:
acs.pop(icao)
return acs