update modeslive command

pull/47/head
Junzi Sun 5 years ago
parent 785584aff5
commit 4906a49e9c

@ -1,9 +1,7 @@
The Python ADS-B/Mode-S Decoder The Python ADS-B/Mode-S Decoder
=============================== ===============================
If you find this project useful for your research, please cite our work (bibtex format): If you find this project useful for your research, please considering cite this tool as::
::
@article{sun2019pymodes, @article{sun2019pymodes,
author={J. {Sun} and H. {V\^u} and J. {Ellerbroek} and J. M. {Hoekstra}}, author={J. {Sun} and H. {V\^u} and J. {Ellerbroek} and J. M. {Hoekstra}},
@ -18,11 +16,11 @@ If you find this project useful for your research, please cite our work (bibtex
Introduction Introduction
--------------------- ---------------------
PyModeS is a Python library designed to decode Mode-S (including ADS-B) message. PyModeS is a Python library designed to decode Mode-S (including ADS-B) message. It can be imported to your python project or be used as a standalone tool to view and save live traffic data.
Message with following Downlink Formats (DF) are supported:
Messages with following Downlink Formats (DF) are supported:
**DF17 / DF18: Automatic Dependent Surveillance - Broadcast (ADS-B)** **DF17 / DF18: Automatic Dependent Surveillance-Broadcast (ADS-B)**
- TC=1-4 / BDS 0,8: Aircraft identification and category - TC=1-4 / BDS 0,8: Aircraft identification and category
- TC=5-8 / BDS 0,6: Surface position - TC=5-8 / BDS 0,6: Surface position
@ -53,13 +51,13 @@ Message with following Downlink Formats (DF) are supported:
Resources Resources
----------- -----------
Checkout and contribute to this open-source project at: Check out and contribute to this open-source project at:
https://github.com/junzis/pyModeS https://github.com/junzis/pyModeS
Detailed manual on Mode-S decoding is published at: Detailed manual on Mode-S decoding is published at:
https://mode-s.org/decode. https://mode-s.org/decode.
API documentation of pyModeS is at: The API documentation of pyModeS is at:
http://pymodes.readthedocs.io http://pymodes.readthedocs.io
@ -67,7 +65,7 @@ http://pymodes.readthedocs.io
Install Install
------- -------
To install latest version from the GitHub: To install the latest version development from the GitHub:
:: ::
@ -82,31 +80,43 @@ To install the stable version (2.0) from pip:
Live view traffic (modeslive) View live traffic (modeslive)
---------------------------------------------------- ----------------------------------------------------
Supports **Mode-S Beast** and **AVR** raw stream
:: General usage::
modeslive --source tcp --server [server_address] --port [tcp_port] \ $ modeslive [-h] --source SOURCE [--connect SERVER PORT DATAYPE]
--rawtype [beast,avr,skysense] --latlon [lat] [lon] --dumpto [folder] [--latlon LAT LON] [--show-uncertainty] [--dumpto DUMPTO]
Arguments: arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
--source SOURCE data source: rtlsdr or tcp --source SOURCE Choose data source, "rtlsdr" or "net"
--server SERVER server address or IP --connect SERVER PORT DATATYPE
--port PORT raw data port Define server, port and data type. Supported data
--rawtype RAWTYPE TCP data format: beast, avr or skysense types are: ['raw', 'beast', 'skysense']
--latlon LAT LON receiver position --latlon LAT LON Receiver latitude and longitude, needed for the surface
--show-uncertainty display uncertaint values, default off position, default none
--dumpto folder to dump decoded output --show-uncertainty Display uncertainty values, default off
--dumpto DUMPTO Folder to dump decoded output, default none
[experimental] If you have a RTL-SDR receiver, you can connect it directly to pyModeS: Live with RTL-SDR
*******************
If you have an RTL-SDR receiver plugged to the computer, you can connect it with ``rtlsdr`` source switch, shown as follows::
$ modeslive --source rtlsdr
Live with network data
***************************
If you want to connect to a TCP server that broadcast raw data. use can use ``net`` source switch, for example::
$ modeslive --source net --connect localhost 30002 avr
$ modeslive --source net --connect 127.0.0.1 30005 beast
::
$ modeslive --source rtlsdr --latlon [lat] [lon]
Example screenshot: Example screenshot:
@ -166,11 +176,7 @@ Core functions for ADS-B decoding
pms.adsb.airborne_velocity(msg) pms.adsb.airborne_velocity(msg)
Note: When you have a fix position of the aircraft, it is convenient to Note: When you have a fix position of the aircraft, it is convenient to use `position_with_ref()` method to decode with only one position message (either odd or even). This works with both airborne and surface position messages. But the reference position shall be within 180NM (airborne) or 45NM (surface) of the true position.
use `position_with_ref()` method to decode with only one position message
(either odd or even). This works with both airborne and surface position
messages. But the reference position shall be with in 180NM (airborne)
or 45NM (surface) of the true position.
Decode altitude replies in DF4 / DF20 Decode altitude replies in DF4 / DF20
@ -277,9 +283,7 @@ Meteorological hazard air report (MHR) [Experimental]
Customize the streaming module Customize the streaming module
****************************** ******************************
The TCP client module from pyModeS can be re-used to stream and process Mode-S The TCP client module from pyModeS can be re-used to stream and process Mode-S data as you like. You need to re-implement the ``handle_messages()`` function from the ``BaseClient`` class to write your own logic to handle the messages.
data as your like. You need to re-implement the ``handle_messages()`` function from
the ``BaseClient`` class to write your own logic to handle the messages.
Here is an example: Here is an example:
@ -319,7 +323,7 @@ Here is an example:
Unit test Unit test
--------- ---------
To perform unit tests. First install ``tox`` through pip, Then, run the following commands: To perform unit tests. First, install ``tox`` through pip. Then, run the following commands:
.. code:: bash .. code:: bash

@ -18,15 +18,15 @@ else:
class BaseClient(Thread): class BaseClient(Thread):
def __init__(self, host, port, rawtype): def __init__(self, host, port, datatype):
Thread.__init__(self) Thread.__init__(self)
self.host = host self.host = host
self.port = port self.port = port
self.buffer = [] self.buffer = []
self.socket = None self.socket = None
self.rawtype = rawtype self.datatype = datatype
if self.rawtype not in ["avr", "beast", "skysense"]: if self.datatype not in ["raw", "beast", "skysense"]:
print("rawtype must be either avr, beast or skysense") print("datatype must be either raw, beast or skysense")
os._exit(1) os._exit(1)
def connect(self): def connect(self):
@ -35,16 +35,18 @@ class BaseClient(Thread):
self.socket.setsockopt(zmq.RCVTIMEO, 2000) self.socket.setsockopt(zmq.RCVTIMEO, 2000)
self.socket.connect("tcp://%s:%s" % (self.host, self.port)) self.socket.connect("tcp://%s:%s" % (self.host, self.port))
def read_avr_buffer(self): def read_raw_buffer(self):
# -- testing -- """ Read raw ADS-B data type.
# for b in self.buffer:
# print(chr(b), b)
# Append message with 0-9,A-F,a-f, until stop sign
String strats with "*" and ends with ";". For example:
*5d484ba898f8c6;
*8d400cd5990d7e9a10043e5e6da0;
*a0001498be800030aa0000c7a75f;
"""
messages = [] messages = []
msg_stop = False msg_stop = False
self.current_msg = ""
for b in self.buffer: for b in self.buffer:
if b == 59: if b == 59:
msg_stop = True msg_stop = True
@ -54,7 +56,9 @@ class BaseClient(Thread):
msg_stop = False 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.current_msg = self.current_msg + chr(b)
self.buffer = [] self.buffer = []
@ -62,7 +66,8 @@ class BaseClient(Thread):
return messages return messages
def read_beast_buffer(self): def read_beast_buffer(self):
""" """Handle mode-s beast data type.
<esc> "1" : 6 byte MLAT timestamp, 1 byte signal level, <esc> "1" : 6 byte MLAT timestamp, 1 byte signal level,
2 byte Mode-AC 2 byte Mode-AC
<esc> "2" : 6 byte MLAT timestamp, 1 byte signal level, <esc> "2" : 6 byte MLAT timestamp, 1 byte signal level,
@ -77,7 +82,6 @@ class BaseClient(Thread):
timestamp: timestamp:
wiki.modesbeast.com/Radarcape:Firmware_Versions#The_GPS_timestamp wiki.modesbeast.com/Radarcape:Firmware_Versions#The_GPS_timestamp
""" """
messages_mlat = [] messages_mlat = []
msg = [] msg = []
i = 0 i = 0
@ -224,7 +228,11 @@ class BaseClient(Thread):
msg = "".join("%02X" % j for j in payload) msg = "".join("%02X" % j for j in payload)
i += 14 # Both message types use 14 bytes i += 14 # Both message types use 14 bytes
tsbin = self.buffer[i : i + 6] tsbin = self.buffer[i : i + 6]
sec = ((tsbin[0] & 0x7F) << 10) | (tsbin[1] << 2) | (tsbin[2] >> 6) sec = (
((tsbin[0] & 0x7F) << 10)
| (tsbin[1] << 2)
| (tsbin[2] >> 6)
)
nano = ( nano = (
((tsbin[2] & 0x3F) << 24) ((tsbin[2] & 0x3F) << 24)
| (tsbin[3] << 16) | (tsbin[3] << 16)
@ -264,11 +272,11 @@ class BaseClient(Thread):
# continue # continue
# -- Removed!! Cause delay in low data rate scenario -- # -- Removed!! Cause delay in low data rate scenario --
if self.rawtype == "beast": if self.datatype == "beast":
messages = self.read_beast_buffer() messages = self.read_beast_buffer()
elif self.rawtype == "avr": elif self.datatype == "raw":
messages = self.read_avr_buffer() messages = self.read_raw_buffer()
elif self.rawtype == "skysense": elif self.datatype == "skysense":
messages = self.read_skysense_buffer() messages = self.read_skysense_buffer()
if not messages: if not messages:
@ -299,7 +307,7 @@ if __name__ == "__main__":
# for testing purpose only # for testing purpose only
host = sys.argv[1] host = sys.argv[1]
port = int(sys.argv[2]) port = int(sys.argv[2])
rawtype = sys.argv[3] datatype = sys.argv[3]
client = BaseClient(host=host, port=port, rawtype=rawtype) client = BaseClient(host=host, port=port, datatype=datatype)
client.daemon = True client.daemon = True
client.run() client.run()

@ -19,59 +19,68 @@ ADSB_TS = []
COMMB_MSG = [] COMMB_MSG = []
COMMB_TS = [] COMMB_TS = []
support_rawtypes = ["raw", "beast", "skysense"]
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
"--source", help="rtlsdr or tcp", required=True, default="tcp" "--source",
help='Choose data source, "rtlsdr" or "net"',
required=True,
default="net",
)
parser.add_argument(
"--connect",
help="Define server, port and data type. Supported data types are: %s"
% support_rawtypes,
nargs=3,
metavar=("SERVER", "PORT", "DATATYPE"),
default=None,
required=False,
) )
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( parser.add_argument(
"--latlon", "--latlon",
help="receiver position", help="Receiver latitude and longitude, needed for the surface position, default none",
nargs=2, nargs=2,
metavar=("LAT", "LON"), metavar=("LAT", "LON"),
required=True, default=None,
required=False,
) )
parser.add_argument( parser.add_argument(
"--show-uncertainty", "--show-uncertainty",
dest="uncertainty", dest="uncertainty",
help="display uncertaint values, default off", help="Display uncertainty values, default off",
action="store_true", action="store_true",
required=False, required=False,
default=False, default=False,
) )
parser.add_argument( parser.add_argument(
"--dumpto", "--dumpto",
help="folder to dump decoded output", help="Folder to dump decoded output, default none",
required=False, required=False,
default=None, default=None,
) )
args = parser.parse_args() args = parser.parse_args()
SOURCE = args.source SOURCE = args.source
SERVER = args.server LATLON = args.latlon
PORT = args.port
RAWTYPE = args.rawtype
LAT0 = float(args.latlon[0])
LON0 = float(args.latlon[1])
UNCERTAINTY = args.uncertainty UNCERTAINTY = args.uncertainty
DUMPTO = args.dumpto DUMPTO = args.dumpto
if SOURCE == "rtlsdr": if SOURCE == "rtlsdr":
pass pass
elif SOURCE == "tcp": elif SOURCE == "net":
if SERVER is None: if args.connect is None:
print("You must specify the server for TCP source.") print("Error: --connect argument must not be empty.")
sys.exit(1) else:
if PORT is None: SERVER, PORT, DATATYPE = args.connect
print("You must specify the port for TCP source.") if DATATYPE not in support_rawtypes:
sys.exit(1) print(
if RAWTYPE is None: "Data type not supported, avaiable ones are %s"
print("You must specify the rawtype for TCP source.") % support_rawtypes
sys.exit(1) )
else: else:
print("Source must be rtlsdr or tcp.") print('Source must be "rtlsdr" or "net".')
sys.exit(1) sys.exit(1)
if DUMPTO is not None: if DUMPTO is not None:
@ -157,8 +166,8 @@ class ModesRtlReader(RtlReader):
# redirect all stdout to null, avoiding messing up with the screen # redirect all stdout to null, avoiding messing up with the screen
sys.stdout = open(os.devnull, "w") sys.stdout = open(os.devnull, "w")
if SOURCE == "tcp": if SOURCE == "net":
client = ModesClient(host=SERVER, port=PORT, rawtype=RAWTYPE) client = ModesClient(host=SERVER, port=PORT, rawtype=DATATYPE)
client.daemon = True client.daemon = True
client.start() client.start()
elif SOURCE == "rtlsdr": elif SOURCE == "rtlsdr":
@ -167,7 +176,7 @@ elif SOURCE == "rtlsdr":
rtl.daemon = True rtl.daemon = True
rtl.start() rtl.start()
stream = Stream(lat0=LAT0, lon0=LON0, dumpto=DUMPTO) stream = Stream(latlon=LATLON, dumpto=DUMPTO)
try: try:
screen = Screen(uncertainty=UNCERTAINTY) screen = Screen(uncertainty=UNCERTAINTY)

@ -7,12 +7,16 @@ import pyModeS as pms
class Stream: class Stream:
def __init__(self, lat0, lon0, dumpto=None): def __init__(self, latlon=None, dumpto=None):
self.acs = dict() self.acs = dict()
self.lat0 = lat0 if latlon is not None:
self.lon0 = lon0 self.lat0 = float(latlon[0])
self.lon0 = float(latlon[1])
else:
self.lat0 = None
self.lon0 = None
self.t = 0 self.t = 0
self.cache_timeout = 60 # seconds self.cache_timeout = 60 # seconds

Loading…
Cancel
Save