NMEA Protocol Parser
Reason this release was yanked:
obsolete
Project description
pynmeagps
Current Status | Installation | Reading | Parsing | Generating | Serializing | Examples | Extensibility | Command Line Utility | Graphical Client | Author & License
pynmeagps is an original Python 3 parser aimed primarily at the subset of the NMEA 0183 © v4 protocol relevant to GNSS/GPS receivers - that is, NMEA 0183 'talkers' 'Gx' (standard) or 'Px' (proprietary).
The intention is to make it as easy as possible to read, parse and utilise NMEA GNSS/GPS messages in Python applications.
The pynmeagps homepage is located at https://github.com/semuconsulting/pynmeagps.
FYI There are companion libraries which handle UBX © and RTCM3 © messages:
Current Status
The library implements a comprehensive set of outbound (GET) and inbound (SET/POLL) GNSS NMEA messages relating to GNSS/GPS devices, but is readily extensible. Refer to NMEA_MSGIDS and NMEA_MSGIDS_PROP in nmeatypes_core.py for the complete dictionary of messages currently supported. While the NMEA 0183 protocol itself is proprietary, the definitions here have been collated from public domain sources.
Sphinx API Documentation in HTML format is available at https://www.semuconsulting.com/pynmeagps.
Contributions welcome - please refer to CONTRIBUTING.MD.
Bug reports and Feature requests - please use the templates provided.
Installation
pynmeagps is compatible with Python >=3.7 and has no third-party library dependencies.
In the following, python & pip refer to the Python 3 executables. You may need to type
python3 or pip3, depending on your particular environment.
The recommended way to install the latest version of pynmeagps is with
pip:
python -m pip install --upgrade pynmeagps
If required, pynmeagps can also be installed into a virtual environment, e.g.:
python -m pip install --user --upgrade virtualenv
python -m virtualenv env
source env/bin/activate (or env\Scripts\activate on Windows)
(env) python -m pip install --upgrade pynmeagps
...
deactivate
Reading (Streaming)
class pynmeagps.nmeareader.NMEAReader(stream, **kwargs)
You can create an NMEAReader 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). pynmeagps implements an internal SocketStream class to allow sockets to be read in the same way as other streams (see example below).
Individual input NMEA messages can then be read using the NMEAReader.read() function, which returns both the raw data (as bytes) and the parsed data (as an NMEAMessage object, via the parse() method). The function is thread-safe in so far as the incoming data stream object is thread-safe. NMEAReader also implements an iterator.
The constructor accepts the following optional keyword arguments:
nmeaonly: True = raise error if stream contains non-NMEA data, False = ignore non-NMEA data (default)validate: bitfield validation flags (can be used in combination):
VALCKSUM(0x01) = validate checksum (default)VALMSGID(0x02) = validate msgId (i.e. raise error if unknown NMEA message is received)
msgmode: 0 = GET (default, i.e. output from receiver), 1 = SET (i.e. input to receiver), 2 = POLL (i.e. query to receiver in anticipation of response back)
Examples:
- Serial input - this example will ignore any non-NMEA data.
>>> from serial import Serial
>>> from pynmeagps import NMEAReader
>>> stream = Serial('/dev/tty.usbmodem14101', 9600, timeout=3)
>>> nmr = NMEAReader(stream)
>>> (raw_data, parsed_data) = nmr.read()
>>> print(parsed_data)
- File input (using iterator) - this example will produce a
NMEAStreamErrorif non-NMEA data is encountered.
>>> from pynmeagps import NMEAReader
>>> stream = open('nmeadata.log', 'rb')
>>> nmr = NMEAReader(stream, nmeaonly=True)
>>> for (raw_data, parsed_data) in nmr: print(parsed_data)
...
Example - Socket input (using enhanced iterator):
>>> import socket
>>> from pynmeagps import NMEAReader
>>> stream = socket.socket(socket.AF_INET, socket.SOCK_STREAM):
>>> stream.connect(("localhost", 50007))
>>> nmr = NMEAReader(stream)
>>> for (raw_data, parsed_data) in nmr.iterate(): print(parsed_data)
Parsing
You can parse individual NMEA messages using the static NMEAReader.parse(message) function, which takes a string or bytes containing an NMEA message and returns an NMEAMessage object.
Note that latitude and longitude are parsed as signed decimal values for ease of use. Helper methods deg2dms and deg2dmm are available to convert decimal degrees to d°m′s.s″ or d°m.m′ display format.
Attributes within repeating groups are parsed with a two-digit suffix (svid_01, svid_02, etc.).
The parse() function accepts the following optional keyword arguments:
validate: bitfield validation flags (can be used in combination):
VALCKSUM(0x01) = validate checksum (default)VALMSGID(0x02) = validate msgId (i.e. raise error if unknown NMEA message is received)
msgmode: 0 = GET (default), 1 = SET, 2 = POLL
Example:
>>> from pynmeagps import NMEAReader
>>> msg = NMEAReader.parse('$GNGLL,5327.04319,S,00214.41396,E,223232.00,A,A*68\r\n')
>>> print(msg)
<NMEA(GNGLL, lat=-53.45072, NS=S, lon=2.240233, EW=E, time=22:32:32, status=A, posMode=A)>
The NMEAMessage object exposes different public attributes depending on its message ID,
e.g. the RMC message has the following attributes:
>>> print(msg)
<NMEA(GNRMC, time=22:18:38, status=A, lat=52.62063, NS=N, lon=-2.16012, EW=W, spd=37.84, cog=, date=2021-03-05, mv=, mvEW=, posMode=A)>
>>> msg.msgID
'RMC'
>>> msg.lat, msg.lon
(52.62063, -2.16012)
>>> msg.spd
37.84
Generating
class pynmeagps.nmeamessage.NMEAMessage(talker: str, msgID: str, msgmode: int, **kwargs)
You can create an NMEAMessage object by calling the constructor with the following parameters:
- talker (must be a valid talker from
pynmeagps.NMEA_TALKERS) - message id (must be a valid id from
pynmeagps.NMEA_MSGIDSorpynmeagps.NMEA_MSGIDS_PROP) - msgmode (0=GET, 1=SET, 2=POLL)
- (optional) a series of keyword parameters representing the message payload
The 'msgmode' parameter signifies whether the message payload refers to a:
- GET message (i.e. output from the receiver - NB these would normally be generated via the NMEAReader.read() or NMEAReader.parse() methods but can also be created manually)
- SET message (i.e. command input to the receiver)
- POLL message (i.e. query input to the receiver in anticipation of a response back)
The message payload can be defined via keyword arguments in one of two ways:
- A single keyword parameter of
payloadcontaining the full payload as a list of string values (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.
e.g. Create a GLL message, passing the entire payload as a list of strings in native NMEA format:
>>> from pynmeagps import NMEAMessage, GET
>>> pyld=['4330.00000','N','00245.000000','W','120425.234','A','A']
>>> msg = NMEAMessage('GN', 'GLL', GET, payload=pyld)
print(msg)
<NMEA(GNGLL, lat=43.5, NS=N, lon=-2.75, EW=W, time=12:04:25.234000, status=A, posMode=A)>
e.g. Create GLL (GET) and GNQ (POLL) message, passing individual typed values as keywords, with any omitted keywords defaulting to nominal values (in the GLL example, the 'time' parameter has been omitted and has defaulted to the current time):
>>> from pynmeagps import NMEAMessage, GET
>>> msg = NMEAMessage('GN', 'GLL', GET, lat=43.5, NS='N', lon=2.75, EW='W', status='A', posMode='A')
>>> print(msg)
<NMEA(GNGLL, lat=43.5, NS='N', lon=-2.75, EW='W', time='12:04:25.234745', status='A', posMode='A')>
>>> from pynmeagps import NMEAMessage, POLL
>>> msg = NMEAMessage('EI', 'GNQ', POLL, msgId='RMC')
>>> print(msg)
<NMEA(EIGNQ, msgId=RMC)>
For position messages, if the values of lat and lon are provided but the corresponding NS or EW values are omitted, their values will be derived from the sign of lat or lon (e.g. lon < 0 => EW = "W"). If the NS or EW values are provided explicitly, they will take precedence and the sign of the lat or lon attributes will be adjusted accordingly. By default, NMEA position message payloads store lat/lon to 5dp of minutes (i.e. (d)ddmm.mmmmm). An optional boolean keyword argument hpnmeamode increases this to 7dp (i.e. (d)ddmm.mmmmmmm) when set to True, e.g.
>>> from pynmeagps import NMEAMessage, GET
>>> msgsp = NMEAMessage('GN', 'GLL', GET, lat=43.123456789, lon=-2.987654321, status='A', posMode='A', hpnmeamode=0) # standard precision
>>> msgsp
NMEAMessage('GN','GLL', 0, payload=['4307.40741', 'N', '00259.25926', 'W', '095045.78', 'A', 'A'])
>>> msghp = NMEAMessage('GN', 'GLL', GET, lat=-43.123456789, lon=2.987654321, status='A', posMode='A', hpnmeamode=1) # high precision
>>> msghp
NMEAMessage('GN','GLL', 0, payload=['4307.4074073', 'S', '00259.2592593', 'E', '094824.88', 'A', 'A'])
NB: Once instantiated, an NMEAMessage object is immutable.
Serializing
The NMEAMessage class implements a serialize() method to convert an NMEAMessage object to a bytes array suitable for writing to an output stream.
>>> from serial import Serial
>>> from pynmeagps import NMEAMessage, POLL
>>> stream = Serial('COM6', 38400, timeout=3)
>>> msg = NMEAMessage('EI','GNQ', POLL, msgId='RMC')
>>> msg.serialize()
b'$EIGNQ,RMC*24\r\n'
>>> stream.write(msg.serialize())
Examples
The following command line examples can be found in the /examples folder:
-
nmeapoller.pyillustrates how to implement a threaded serial reader for NMEA messages usingpynmeagps.NMEAReaderand send poll requests for a variety of NMEA message types. -
nmeafile.pyillustrates how to implement an NMEA datalog file reader usingpynmeagps.NMEAReaderiterator functionality. -
nmeasocket.pyillustrates how to implement a TCP Socket reader for NMEA messages using NMEAReader iterator functionality. -
gpxtracker.pyillustrates a simple utility to convert an NMEA datalog file to a*.gpxtrack file usingpynmeagps.NMEAReader. -
/webserver/nmeaserver.pyillustrates a simple HTTP web server wrapper aroundpynmeagps.NMEAReader; it presents data from selected NMEA messages as a web page http://localhost:8080 or a RESTful API http://localhost:8080/gps.
Extensibility
The NMEA protocol is principally defined in the modules nmeatypes_*.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:
1. attribute names must be unique within each message class
2. avoid reserved names 'msgID', 'talker', 'payload', 'checksum'.
3. attribute types must be one of the valid types (IN, DE, CH, etc.)
4. repeating groups must be defined as a tuple ('numr', {dict}), where:
'numr' is either:
a. an integer representing a fixed number of repeats e.g. 32
b. a string representing the name of a preceding attribute containing the number of repeats e.g. 'numSv'
c. 'None' for an indeterminate repeating group. Only one such group is permitted per payload and it must be at the end.
{dict} is the nested dictionary of repeating items
Command Line Utility
A command line utility gnssdump is available via the pygnssutils package. This is capable of reading and parsing NMEA, UBX and RTCM3 data from a variety of input sources (e.g. serial, socket and file) and outputting to a variety of media in a variety of formats. See https://github.com/semuconsulting/pygnssutils for further details.
To install pygnssutils:
python3 -m pip install --upgrade pygnssutils
For help with the gnssdump utility, type:
gnssdump -h
Graphical Client
A python/tkinter graphical GPS client which supports both NMEA and UBX protocols is available at:
https://github.com/semuconsulting/PyGPSClient
Author & License Information
pynmeagps is maintained entirely by unpaid volunteers. It receives no funding from advertising or corporate sponsorship. If you find the library useful, a small donation would be greatly appreciated!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pynmeagps-1.0.16.tar.gz.
File metadata
- Download URL: pynmeagps-1.0.16.tar.gz
- Upload date:
- Size: 29.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.10.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b490145fb7b4baa5be8714f154f1151e208770c99a62f0a35b57ecd1562934dd
|
|
| MD5 |
57416b67930b7e3335e61764f736e463
|
|
| BLAKE2b-256 |
5c9bbb4eb4bbc3a8cf2476c0ce0102bf5d1d3fb185650d1bdb6f4755afcfaa32
|
File details
Details for the file pynmeagps-1.0.16-py3-none-any.whl.
File metadata
- Download URL: pynmeagps-1.0.16-py3-none-any.whl
- Upload date:
- Size: 27.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.10.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6642661b100a2d8cff7a61aa82af6e590f2799da665332e8fe53c97f52a791d
|
|
| MD5 |
75a646bd7a8d0cc6137832659ce1a631
|
|
| BLAKE2b-256 |
5488b4e994befa8c1c0482b867ebc0f865203acf6daea66aa162d6f01efc3bab
|