update modeslive command

This commit is contained in:
Junzi Sun 2019-08-23 14:46:13 +02:00
parent 785584aff5
commit 4906a49e9c
4 changed files with 110 additions and 85 deletions

View File

@ -1,9 +1,7 @@
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,
author={J. {Sun} and H. {V\^u} and J. {Ellerbroek} and J. M. {Hoekstra}},
@ -18,9 +16,9 @@ If you find this project useful for your research, please cite our work (bibtex
Introduction
---------------------
PyModeS is a Python library designed to decode Mode-S (including ADS-B) message.
Message with following Downlink Formats (DF) are supported:
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.
Messages with following Downlink Formats (DF) are supported:
**DF17 / DF18: Automatic Dependent Surveillance-Broadcast (ADS-B)**
@ -59,7 +57,7 @@ https://github.com/junzis/pyModeS
Detailed manual on Mode-S decoding is published at:
https://mode-s.org/decode.
API documentation of pyModeS is at:
The API documentation of pyModeS is at:
http://pymodes.readthedocs.io
@ -67,7 +65,7 @@ http://pymodes.readthedocs.io
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] \
--rawtype [beast,avr,skysense] --latlon [lat] [lon] --dumpto [folder]
$ modeslive [-h] --source SOURCE [--connect SERVER PORT DATAYPE]
[--latlon LAT LON] [--show-uncertainty] [--dumpto DUMPTO]
Arguments:
arguments:
-h, --help show this help message and exit
--source SOURCE data source: rtlsdr or tcp
--server SERVER server address or IP
--port PORT raw data port
--rawtype RAWTYPE TCP data format: beast, avr or skysense
--latlon LAT LON receiver position
--show-uncertainty display uncertaint values, default off
--dumpto folder to dump decoded output
--source SOURCE Choose data source, "rtlsdr" or "net"
--connect SERVER PORT DATATYPE
Define server, port and data type. Supported data
types are: ['raw', 'beast', 'skysense']
--latlon LAT LON Receiver latitude and longitude, needed for the surface
position, default none
--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:
@ -166,11 +176,7 @@ Core functions for ADS-B decoding
pms.adsb.airborne_velocity(msg)
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 with in 180NM (airborne)
or 45NM (surface) of the true position.
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.
Decode altitude replies in DF4 / DF20
@ -277,9 +283,7 @@ Meteorological hazard air report (MHR) [Experimental]
Customize the streaming module
******************************
The TCP client module from pyModeS can be re-used to stream and process Mode-S
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.
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.
Here is an example:
@ -319,7 +323,7 @@ Here is an example:
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

View File

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

View File

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

View File

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