I started to add support for some of the generation 9 messages, but right after it, the original author started to do the same 🤷♂️
Thus, this fork has ben stalled: you can go to the original project or have a look at a different approach I took to cover my needs.
pyubx2
is an original python library for the UBX protocol.
UBX is a proprietary binary protocol implemented on u-blox © GPS/GNSS receiver modules.
The pyubx2
homepage is located at http://github.com/semuconsulting/pyubx2.
This is an independent project and we have no affiliation whatsoever with u-blox ©.
At time of writing the library implements a complete set of inbound and outbound messages as defined in the u-blox generation 8 protocol, but is readily extensible for later generations.
Constructive feedback and feature requests welcome.
pyubx2
is compatible with Python 3.6+ and has no third-party library dependencies.
The recommended way to install the latest version of pyubx2
is with
pip:
python -m pip install --upgrade pyubx2
You can create a UBXReader
object by calling the constructor with an active stream object.
The stream object can be any data stream which supports a read(n) -> bytes
method (e.g. File or Serial, with
or without a buffer wrapper).
Individual input UBX messages can then be read using the UBXReader.read()
function, which returns both the raw binary
data (as bytes) and the parsed data (as a UBXMessage
object). The function is thread-safe in so far as the incoming
data stream object is thread-safe. UBXReader
also implements an iterator.
The UBXReader
constructor includes an optional validate
flag which governs behaviour if the stream includes non-UBX data.
If set to 'False' (the default), it will ignore such data and continue with the next valid UBX message. If set to 'True', it
will raise a UBXStreamError
.
Examples:
- Serial input - this example will ignore any non-UBX data.
>>> from serial import Serial
>>> from pyubx2 import UBXReader
>>> stream = Serial('COM6', 9600, timeout=3)
>>> ubr = UBXReader(stream)
>>> (raw_data, parsed_data) = ubr.read()
- File input (using iterator) - this example will produce a
UBXStreamError
if non-UBX data is encountered.
>>> import os
>>> from pyubx2 import UBXReader
>>> file = os.path.join(os.path.dirname(__file__), 'ubxdata.bin')
>>> stream = open(file, 'rb')
>>> ubr = UBXReader(stream, True)
>>> for (raw_data, parsed_data) in ubr: print(parsed_data)
...
You can parse individual UBX messages using the UBXMessage.parse(data, validate=False)
function, which takes a bytes array containing a binary UBX message and returns a UBXMessage
object.
If the optional 'validate' parameter is set to True
, parse
will validate the supplied UBX message header, payload length and checksum.
If any of these are not consistent with the message content, it will raise a UBXParseError
. Otherwise, the function will automatically
generate the appropriate payload length and checksum.
Example:
>>> from pyubx2 import UBXMessage
>>> msg = UBXMessage.parse(b'\xb5b\x05\x01\x02\x00\x06\x01\x0f\x38', True)
>>> print(msg)
<UBX(ACK-ACK, clsID=CFG, msgID=CFG-MSG)>
>>> msg = UBXMessage.parse(b'\xb5b\x01\x12$\x000D\n\x18\xfd\xff\xff\xff\xf1\xff\xff\xff\xfc\xff\xff\xff\x10\x00\x00\x00\x0f\x00\x00\x00\x83\xf5\x01\x00A\x00\x00\x00\xf0\xdfz\x00\xd0\xa6')
>>> print(msg)
<UBX(NAV-VELNED, iTOW=403327000, velN=-1, velE=-21, velD=-4, speed=22, gSpeed=21, heading=128387, sAcc=67, cAcc=8056455)>
The UBXMessage
object exposes different public properties depending on its message type or 'identity',
e.g. the NAV-POSLLH
message has the following properties:
>>> msg
<UBX(NAV-POSLLH, iTOW=403667000, lon=-21601284, lat=526206345, height=86327, hMSL=37844, hAcc=38885, vAcc=16557)>
>>>msg.identity
'NAV-POSLLH'
>>>msg.lat/10**7, msg.lon/10**7
(52.6206345, -2.1601284)
>>>msg.hMSL/10**3
37.844
You can create a UBXMessage
object by calling the constructor with the following parameters:
- ubxClass
- ubxID
- mode (0=GET, 1=SET, 2=POLL)
- (optional) a series of keyword parameters representing the message payload
NB: Once instantiated, a UBXMessage
object is immutable.
The 'ubxClass' and 'ubxID' parameters may be passed as lookup strings, integers or bytes.
The 'mode' parameter signifies whether the message payload refers to a:
- GET message (i.e. output from the receiver)
- SET message (i.e. input to the receiver)
- POLL message (i.e. input to the receiver in anticipation of a response back)
The message payload can be defined via keyword parameters in one of three ways:
- A single keyword parameter of
payload
containing the full payload as a sequence of bytes (any other keyword parameters will be ignored). - One or more keyword parameters corresponding to individual message attributes. Any attributes not explicitly provided as keyword parameters will be set to a nominal value according to their type.
- If no keyword parameters are passed, the payload is assumed to be null.
e.g. to generate a CFG-MSG which polls the 'VTG' NMEA message rate on the current port, any of the following constructor formats will work:
>>> from pyubx2 import UBXMessage, POLL
>>> msg1 = UBXMessage(b'\x06', b'\x01', POLL, payload=b'\xf0\x05')
>>> print(msg1)
<UBX(CFG-MSG, msgClass=NMEA-Standard, msgID=VTG)>
>>> from pyubx2 import UBXMessage, POLL
>>> msg2 = UBXMessage(6, 1, POLL, msgClass=240, msgID=5)
>>> print(msg2)
<UBX(CFG-MSG, msgClass=NMEA-Standard, msgID=VTG)>
>>> from pyubx2 import UBXMessage, POLL
>>> msg3 = UBXMessage('CFG','CFG-MSG', POLL, msgClass=240, msgID=5)
>>> print(msg3)
<UBX(CFG-MSG, msgClass=NMEA-Standard, msgID=VTG)>
The UBXMessage
class implements a serialize()
method to convert a UBXMessage
object to a bytes array suitable for writing to an output stream.
e.g. to create and send a CFG-MSG
message which sets the NMEA GLL message rate to '1' on the receiver's UART1 and USB ports (assuming an output serial stream has been created as serialOut
):
>>> from pyubx2 import UBXMessage, SET
>>> msg = UBXMessage('CFG','CFG-MSG', SET, msgClass=240, msgID=1, rateUART1=1, rateUSB=1)
>>> print(msg)
<UBX(CFG-MSG, msgClass=NMEA-Standard, msgID=GLL, rateDDC=0, rateUART1=1, rateUART2=0, rateUSB=1, rateSPI=0, reserved=0)>
>>> output = msg.serialize()
>>> output
b'\xb5b\x06\x01\x08\x00\xf0\x01\x00\x01\x00\x01\x00\x00\x036'
>>> serialOut.write(output)
The following examples can be found in the \examples
folder:
-
ubxstreamer.py
illustrates how to implement a threaded serial reader for UBX messages using pyubx2.UBXReader. -
ubxfile.py
illustrates how to implement a binary file reader for UBX messages using the pyubx2.UBXReader iterator function. -
ubxconfig.py
illustrates how to implement a simple configuration utility which sets output UBX-NAV message rates on the receiver's UART and USB ports (on a non-permanent basis). You can see the results usingubxstreamer.py
. -
ubxprotocol.py
illustrates how to set the outbound protocols on the receiver's USB port to NMEA, UBX or both (on a non-permanent basis). You can see the results usingubxstreamer.py
. -
ubxtracker.py
illustrates a simple CLI tool to convert a binary UBX data dump (e.g. as produced by the PyGPSClient's data logging facility) to a*.gpx
track file using pyubx2.UBXReader.
The UBX protocol is principally defined in the modules ubxtypes_*.py
as a series of dictionaries. Additional message types
can be readily added to the appropriate dictionary. Message payload definitions must conform to the following rules:
- attribute names must be unique within each message class
- attribute types must be one of the valid types (I1, U2, X4, etc.)
- repeating groups must be defined as a tuple ('numr', {dict}), where 'numr' is the name of the preceding attribute containing the number of repeats (or 'None' if there isn't one), and {dict} is the nested dictionary of repeating items. See NAV-SVINFO by way of example.
- repeating attribute names are parsed with a two-digit suffix (svid_01, svid_02, etc.)
A python/tkinter graphical GPS client which supports both NMEA and UBX protocols (via pynmea2 and pyubx2 respectively) is under development at: