From fb4cef70850b9cb8c1926c5ecaff963bc51c3619 Mon Sep 17 00:00:00 2001 From: Magnus Lundmark Date: Fri, 18 Jan 2019 16:09:04 +0100 Subject: [PATCH 1/5] Initial Skysense support --- pyModeS/extra/tcpclient.py | 71 ++++++++++++++++++++++++++++++++++++-- pyModeS/streamer/modeslive | 2 +- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/pyModeS/extra/tcpclient.py b/pyModeS/extra/tcpclient.py index 6510325..7f9c9e0 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,6 +144,71 @@ 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 +---------------------------------------------------------------------------------- +Example: 24 8d 4c a5 c0 20 24 22 f1 28 38 20 f3 24 04 be 8d 0d d8 e0 80 66 ed b9 +Example: 24 5d 3c 48 c8 ad 3d f9 00 00 00 00 00 00 00 be 8d 66 c4 c5 7c 4c d7 2d + + +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. + Decoding of payload information is not within the scope of this document. + +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 + + """ + + messages = [] + msg = [] + i = 0 + while i < len(self.buffer): + if self.buffer[i] == 0x24: + + + + return messages + def handle_messages(self, messages): """re-implement this method to handle the messages""" @@ -172,6 +237,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 diff --git a/pyModeS/streamer/modeslive b/pyModeS/streamer/modeslive index 5ee6438..d16724b 100644 --- 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() From 1795d2f3c038194f026eb5b85506332d66512255 Mon Sep 17 00:00:00 2001 From: Magnus Lundmark Date: Fri, 18 Jan 2019 17:44:19 +0100 Subject: [PATCH 2/5] updates to handler --- pyModeS/decoder/common.py | 2 +- pyModeS/extra/tcpclient.py | 126 +++++++++++++++++++++---------------- 2 files changed, 74 insertions(+), 54 deletions(-) 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 7f9c9e0..36b363e 100644 --- a/pyModeS/extra/tcpclient.py +++ b/pyModeS/extra/tcpclient.py @@ -146,64 +146,84 @@ class BaseClient(Thread): 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 + ---------------------------------------------------------------------------------- + Example: 24 8d 4c a5 c0 20 24 22 f1 28 38 20 f3 24 04 be 8d 0d d8 e0 80 66 ed b9 + Example: 24 5d 3c 48 c8 ad 3d f9 00 00 00 00 00 00 00 be 8d 66 c4 c5 7c 4c d7 2d + + + 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. + Decoding of payload information is not within the scope of this document. + + 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 ----------------------------------------------------------------------------------- -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 ----------------------------------------------------------------------------------- -Example: 24 8d 4c a5 c0 20 24 22 f1 28 38 20 f3 24 04 be 8d 0d d8 e0 80 66 ed b9 -Example: 24 5d 3c 48 c8 ad 3d f9 00 00 00 00 00 00 00 be 8d 66 c4 c5 7c 4c d7 2d - - -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. - Decoding of payload information is not within the scope of this document. - -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_STARTCHAR = 0x24 messages = [] msg = [] i = 0 while i < len(self.buffer): - if self.buffer[i] == 0x24: + if self.buffer[i] == 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' % i for i in payload) + + i += 14 + + tsbin = self.buffer[i:i+6] + sec = (tsbin[0] << 10) | (tsbin[1] << 2) | (tsbin[2] >> 6) + nano = (tsbin[2] << 24) | (tsbin[3] << 16) | (tsbin[4] << 8) | tsbin[5] + ts = sec + nano*1.0e-9 + i += 6 + #Signal level - Don't care + i += 3 + messages.append( [msg,ts] ) From c252647e7722d1d663afe937d54061cfa1099f19 Mon Sep 17 00:00:00 2001 From: Magnus Lundmark Date: Mon, 21 Jan 2019 16:16:41 +0100 Subject: [PATCH 3/5] Bugfixes --- pyModeS/extra/tcpclient.py | 41 ++++++++++++++++++++------------------ pyModeS/streamer/modeslive | 0 2 files changed, 22 insertions(+), 19 deletions(-) mode change 100644 => 100755 pyModeS/streamer/modeslive diff --git a/pyModeS/extra/tcpclient.py b/pyModeS/extra/tcpclient.py index 36b363e..bfcb28c 100644 --- a/pyModeS/extra/tcpclient.py +++ b/pyModeS/extra/tcpclient.py @@ -152,8 +152,11 @@ class BaseClient(Thread): 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 ---------------------------------------------------------------------------------- Example: 24 8d 4c a5 c0 20 24 22 f1 28 38 20 f3 24 04 be 8d 0d d8 e0 80 66 ed b9 + 24 8d 4a b5 68 99 10 6f 13 90 40 93 7f 9f 61 2f 1d b7 5b 46 e0 98 4a c8 + Example: 24 5d 3c 48 c8 ad 3d f9 00 00 00 00 00 00 00 be 8d 66 c4 c5 7c 4c d7 2d - + 24 5d 4a b5 68 88 90 db 00 00 00 00 00 00 00 2f 1b 1e eb 80 ad 9a 9a 75 + SS field - Start character Position 0: @@ -197,13 +200,15 @@ class BaseClient(Thread): Bits 11 through 0 - 12 bits """ SS_MSGLENGTH = 24 - SS_STARTCHAR = 0x24 + + if len(self.buffer) < SS_MSGLENGTH: + return None + messages = [] - msg = [] - i = 0 - while i < len(self.buffer): - if self.buffer[i] == SS_STARTCHAR: + while len(self.buffer) > SS_MSGLENGTH + 1: + 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 @@ -211,29 +216,26 @@ class BaseClient(Thread): else: #Short message payload = self.buffer[i:i+7] - - msg = ''.join('%02X' % i for i in payload) - + msg = ''.join('%02X' % j for j in payload) i += 14 - tsbin = self.buffer[i:i+6] - sec = (tsbin[0] << 10) | (tsbin[1] << 2) | (tsbin[2] >> 6) - nano = (tsbin[2] << 24) | (tsbin[3] << 16) | (tsbin[4] << 8) | tsbin[5] + #print(tsbin) + 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 level - Don't care 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() @@ -257,7 +259,7 @@ class BaseClient(Thread): messages = self.read_beast_buffer() 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: @@ -280,6 +282,7 @@ 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 From 3f6389a67d0e60ba736c422e5e417c7081ece04b Mon Sep 17 00:00:00 2001 From: Magnus Lundmark Date: Tue, 22 Jan 2019 09:44:58 +0100 Subject: [PATCH 4/5] Clarifications of Skysense format --- pyModeS/extra/tcpclient.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/pyModeS/extra/tcpclient.py b/pyModeS/extra/tcpclient.py index bfcb28c..82eb5ca 100644 --- a/pyModeS/extra/tcpclient.py +++ b/pyModeS/extra/tcpclient.py @@ -151,13 +151,7 @@ class BaseClient(Thread): ---------------------------------------------------------------------------------- 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 ---------------------------------------------------------------------------------- - Example: 24 8d 4c a5 c0 20 24 22 f1 28 38 20 f3 24 04 be 8d 0d d8 e0 80 66 ed b9 - 24 8d 4a b5 68 99 10 6f 13 90 40 93 7f 9f 61 2f 1d b7 5b 46 e0 98 4a c8 - - Example: 24 5d 3c 48 c8 ad 3d f9 00 00 00 00 00 00 00 be 8d 66 c4 c5 7c 4c d7 2d - 24 5d 4a b5 68 88 90 db 00 00 00 00 00 00 00 2f 1b 1e eb 80 ad 9a 9a 75 - - + SS field - Start character Position 0: 1 byte = 8 bits @@ -168,7 +162,6 @@ class BaseClient(Thread): 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. - Decoding of payload information is not within the scope of this document. TS field - Time stamp Position 15 through 20: @@ -183,8 +176,7 @@ class BaseClient(Thread): Bits 46 through 30 - 17 bits Nanoseconds into current second, between 0 and 999999999 - Bits 29 through 0 - 30 bits - + Bits 29 through 0 - 30 bits RS field - Signal Level Position 21 through 23: @@ -202,11 +194,11 @@ class BaseClient(Thread): SS_MSGLENGTH = 24 SS_STARTCHAR = 0x24 - if len(self.buffer) < SS_MSGLENGTH: + if len(self.buffer) <= SS_MSGLENGTH: return None messages = [] - while len(self.buffer) > SS_MSGLENGTH + 1: + while len(self.buffer) > SS_MSGLENGTH: i = 0 if self.buffer[i] == SS_STARTCHAR and self.buffer[i+SS_MSGLENGTH] == SS_STARTCHAR: i += 1 @@ -217,14 +209,13 @@ class BaseClient(Thread): #Short message payload = self.buffer[i:i+7] msg = ''.join('%02X' % j for j in payload) - i += 14 + i += 14 #Both message types use 14 bytes tsbin = self.buffer[i:i+6] - #print(tsbin) 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 level - Don't care + #Signal and noise level - Don't care for now i += 3 self.buffer = self.buffer[SS_MSGLENGTH:] messages.append( [msg,ts] ) @@ -277,7 +268,6 @@ class BaseClient(Thread): print("Unexpected Error:", e) - if __name__ == '__main__': # for testing purpose only host = sys.argv[1] From 759380b5a94c03c4633e425ac48c8976a855ccca Mon Sep 17 00:00:00 2001 From: Magnus Lundmark Date: Tue, 22 Jan 2019 09:52:01 +0100 Subject: [PATCH 5/5] Updated README --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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