diff --git a/README.rst b/README.rst index eee2313..4fd66ee 100644 --- a/README.rst +++ b/README.rst @@ -72,13 +72,13 @@ Supports **Mode-S Beast** and **AVR** raw stream :: - modeslive --server [server_address] --port [tcp_port] --rawtype [beast_or_avr] --latlon [lat] [lon] + modeslive --server [server_address] --port [tcp_port] --rawtype [beast,avr,skysense] --latlon [lat] [lon] Arguments: -h, --help show this help message and exit --server SERVER server address or IP --port PORT raw data port - --rawtype RAWTYPE beast or avr + --rawtype RAWTYPE beast, avr or skysense --latlon LAT LON receiver position --show-uncertainty display uncertaint values, default off diff --git a/pyModeS/decoder/common.py b/pyModeS/decoder/common.py index 99384c4..942e4fb 100644 --- a/pyModeS/decoder/common.py +++ b/pyModeS/decoder/common.py @@ -31,7 +31,7 @@ def np2bin(npbin): def df(msg): """Decode Downlink Format vaule, bits 1 to 5.""" msgbin = hex2bin(msg) - return bin2int(msgbin[0:5]) + return min( bin2int(msgbin[0:5]) , 24 ) def crc(msg, encode=False): diff --git a/pyModeS/extra/tcpclient.py b/pyModeS/extra/tcpclient.py index 6510325..82eb5ca 100644 --- a/pyModeS/extra/tcpclient.py +++ b/pyModeS/extra/tcpclient.py @@ -20,8 +20,8 @@ class BaseClient(Thread): self.port = port self.buffer = [] self.rawtype = rawtype - if self.rawtype not in ['avr', 'beast']: - print("rawtype must be either avr or beast") + if self.rawtype not in ['avr', 'beast', 'skysense']: + print("rawtype must be either avr, beast or skysense") os._exit(1) def connect(self): @@ -144,11 +144,89 @@ class BaseClient(Thread): messages.append([msg, ts]) return messages + def read_skysense_buffer(self): + """ + ---------------------------------------------------------------------------------- + Field SS MS MS MS MS MS MS MS MS MS MS MS MS MS MS TS TS TS TS TS TS RS RS RS + ---------------------------------------------------------------------------------- + Position: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + ---------------------------------------------------------------------------------- + + SS field - Start character + Position 0: + 1 byte = 8 bits + Start character '$' + + MS field - Payload + Postion 1 through 14: + 14 bytes = 112 bits + Mode-S payload + In case of DF types that only carry 7 bytes of information position 8 through 14 are set to 0x00. + + TS field - Time stamp + Position 15 through 20: + 6 bytes = 48 bits + Time stamp with fields as: + + Lock Status - Status of internal time keeping mechanism + Equal to 1 if operating normally + Bit 47 - 1 bit + + Time of day in UTC seconds, between 0 and 86399 + Bits 46 through 30 - 17 bits + + Nanoseconds into current second, between 0 and 999999999 + Bits 29 through 0 - 30 bits + + RS field - Signal Level + Position 21 through 23: + 3 bytes = 24 bits + RSSI (received signal strength indication) and relative noise level with fields + + RNL, Q12.4 unsigned fixed point binary with 4 fractional bits and 8 integer bits. + This is and indication of the noise level of the message. Roughly 40 counts per 10dBm. + Bits 23 through 12 - 12 bits + + RSSI, Q12.4 unsigned fixed point binary with 4 fractional bits and 8 integer bits. + This is an indication of the signal level of the received message in ADC counts. Roughly 40 counts per 10dBm. + Bits 11 through 0 - 12 bits + """ + SS_MSGLENGTH = 24 + SS_STARTCHAR = 0x24 + + if len(self.buffer) <= SS_MSGLENGTH: + return None + + messages = [] + while len(self.buffer) > SS_MSGLENGTH: + i = 0 + 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] + 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 + i += 6 + #Signal and noise level - Don't care for now + i += 3 + self.buffer = self.buffer[SS_MSGLENGTH:] + messages.append( [msg,ts] ) + else: + self.buffer = self.buffer[1:] + return messages def handle_messages(self, messages): """re-implement this method to handle the messages""" for msg, t in messages: - print("%f %s" % (t, msg)) + print("%15.9f %s" % (t, msg)) def run(self): sock = self.connect() @@ -172,6 +250,8 @@ class BaseClient(Thread): messages = self.read_beast_buffer() elif self.rawtype == 'avr': messages = self.read_avr_buffer() + elif self.rawtype == 'skysense': + messages = self.read_skysense_buffer() if not messages: continue @@ -188,11 +268,11 @@ class BaseClient(Thread): print("Unexpected Error:", e) - if __name__ == '__main__': # for testing purpose only host = sys.argv[1] port = int(sys.argv[2]) - client = BaseClient(host=host, port=port) + rawtype = sys.argv[3] + client = BaseClient(host=host, port=port, rawtype=rawtype) client.daemon = True client.run() diff --git a/pyModeS/streamer/modeslive b/pyModeS/streamer/modeslive old mode 100644 new mode 100755 index 5ee6438..d16724b --- a/pyModeS/streamer/modeslive +++ b/pyModeS/streamer/modeslive @@ -21,7 +21,7 @@ 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 or avr', 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) args = parser.parse_args()