Package: pynmeagps
pynmeagpsCurrent Status|Installation|Reading|Parsing|Generating|Serializing|Utilities|Examples|Extensibility|Command Line Utility|Graphical Client|Author & Licensepynmeagpsis an original Python 3 parser aimedprimarilyat the subset of the NMEA 0183 © v4 protocol relevant to GNSS/GPS receivers.The intention is to make it as easy as possible to read, parse and utilise NMEA GNSS/GPS messages in Python applications.Thepynmeagpshomepage is located athttps://github.com/semuconsulting/pynmeagps.Companion libraries are available which handle UBX © and RTCM3 © messages:pyubx2(installingpyubx2via pip also installspynmeagpsandpyrtcm)pyrtcmCurrent StatusThe library implements a comprehensive set of outbound (GET) and inbound (SET/POLL) GNSS NMEA messages relating to GNSS/GPS devices, but is readilyextensible. Refer toNMEA_MSGIDSandNMEA_MSGIDS_PROPinnmeatypes_core.pyfor the complete dictionary of messages currently supported. While theNMEA 0183 protocol itself is proprietary, the definitions here have been collated from public domain sources.Sphinx API Documentation in HTML format is available athttps://www.semuconsulting.com/pynmeagps.Contributions welcome - please refer toCONTRIBUTING.MD.Bug reportsandFeature requests- please use the templates provided. For general queries and advice, post a message to one of thepynmeagps Discussionschannels.Installationpynmeagpsis compatible with Python >=3.8 and has no third-party library dependencies.In the following,python3&piprefer to the Python 3 executables. You may need to typepythonorpip3, depending on your particular environment.The recommended way to install the latest version ofpynmeagpsis withpip:python3-mpipinstall--upgradepynmeagpsIf required,pynmeagpscan also be installed into a virtual environment, e.g.:python3-mpipinstall--user--upgradevirtualenv
python3-mvirtualenvenvsourceenv/bin/activate(orenv\Scripts\activateonWindows)(env)python3-mpipinstall--upgradepynmeagps
...
deactivateForCondausers,pynmeagpsis also available fromconda forge:condainstall-cconda-forgepynmeagpsReading (Streaming)class pynmeagps.nmeareader.NMEAReader(stream, **kwargs)You can create anNMEAReaderobject by calling the constructor with an active stream object.
The stream object can be any data stream which supports aread(n) -> bytesmethod (e.g. File or Serial, with
or without a buffer wrapper).pynmeagpsimplements an internalSocketStreamclass 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 theNMEAReader.read()function, which returns both the raw data (as bytes) and the parsed data (as anNMEAMessageobject, via theparse()method). The function is thread-safe in so far as the incoming data stream object is thread-safe.NMEAReaderalso 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. outputfromreceiver), 1 = SET (i.e. inputtoreceiver), 2 = POLL (i.e. querytoreceiver in anticipation of response back)Examples:Serial input - this example will ignore any non-NMEA data.>>>fromserialimportSerial>>>frompynmeagpsimportNMEAReader>>>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 aNMEAStreamErrorif non-NMEA data is encountered.>>>frompynmeagpsimportNMEAReader>>>stream=open('nmeadata.log','rb')>>>nmr=NMEAReader(stream,nmeaonly=True)>>>for(raw_data,parsed_data)innmr:print(parsed_data)...Example - Socket input (using iterator):>>>importsocket>>>frompynmeagpsimportNMEAReader>>>stream=socket.socket(socket.AF_INET,socket.SOCK_STREAM):>>>stream.connect(("localhost",50007))>>>nmr=NMEAReader(stream)>>>for(raw_data,parsed_data)innmr:print(parsed_data)ParsingYou can parse individual NMEA messages using the staticNMEAReader.parse(message)function, which takes a string or bytes containing an NMEA message and returns anNMEAMessageobject.Note that latitude and longitude are parsed as signed decimal values for ease of use. Helper methodslatlon2dmsandlatlon2dmmare 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.).Theparse()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 = POLLExample:>>>frompynmeagpsimportNMEAReader>>>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)>TheNMEAMessageobject exposes different public attributes depending on its message ID,
e.g. theRMCmessage has the following attributes:frompynmeagpsimportlatlon2dms,latlon2dmm...>>>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.spd37.84>>>latlon2dms((msg.lat,msg.lon))('52°37′14.268″N','2°9′36.432″W')>>>latlon2dmm((msg.lat,msg.lon))('52°37.2378′N','2°9.6072′W')Generatingclass pynmeagps.nmeamessage.NMEAMessage(talker: str, msgID: str, msgmode: int, **kwargs)You can create anNMEAMessageobject by calling the constructor with the following parameters:talker (must be a valid talker frompynmeagps.NMEA_TALKERS)message id (must be a valid id frompynmeagps.NMEA_MSGIDSorpynmeagps.NMEA_MSGIDS_PROP)msgmode (0=GET, 1=SET, 2=POLL)(optional) a series of keyword parameters representing the message payloadThe '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 ofpayloadcontaining 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:>>>frompynmeagpsimportNMEAMessage,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):>>>frompynmeagpsimportNMEAMessage,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')>>>>frompynmeagpsimportNMEAMessage,POLL>>>msg=NMEAMessage('EI','GNQ',POLL,msgId='RMC')>>>print(msg)<NMEA(EIGNQ,msgId=RMC)>For position messages, if the values oflatandlonare provided but the correspondingNSorEWvalues are omitted, their values will be derived from the sign oflatorlon(e.g.lon< 0 =>EW= "W"). If theNSorEWvalues are provided explicitly, they will take precedence and the sign of thelatorlonattributes 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 argumenthpnmeamodeincreases this to 7dp (i.e. (d)ddmm.mmmmmmm) when set to True, e.g.>>>frompynmeagpsimportNMEAMessage,GET>>>msgsp=NMEAMessage('GN','GLL',GET,lat=43.123456789,lon=-2.987654321,status='A',posMode='A',hpnmeamode=0)# standard precision>>>msgspNMEAMessage('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>>>msghpNMEAMessage('GN','GLL',0,payload=['4307.4074073','S','00259.2592593','E','094824.88','A','A'])NB:Once instantiated, anNMEAMessageobject is immutable.SerializingTheNMEAMessageclass implements aserialize()method to convert anNMEAMessageobject to a bytes array suitable for writing to an output stream.>>>fromserialimportSerial>>>frompynmeagpsimportNMEAMessage,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())Utility Methodspynmeagpsprovides the following utility methods:latlon2dms- converts decimal lat/lon to degrees, minutes, decimal seconds format e.g. "53°20′45.6″N", "2°32′46.68″W"latlon2dmm- converts decimal lat/lon to degrees, decimal minutes format e.g. "53°20.76′N", "2°32.778′W"llh2iso6709- converts lat/lon and altitude (hMSL) to ISO6709 format e.g. "+27.5916+086.5640+8850CRSWGS_84/"ecef2llh- converts ECEF (X, Y, Z) coordinates to geodetic (lat, lon, ellipsoidal height) coordinatesllh2ecef- converts geodetic (lat, lon, ellipsoidal height) coordinates to ECEF (X, Y, Z) coordinateshaversine- finds spherical distance in km between two sets of (lat, lon) coordinatesbearing- finds bearing in degrees between two sets of (lat, lon) coordinatesSeeSphinx documentationfor details.ExamplesThe following command line examples can be found in the/examplesfolder:nmeapoller.pyillustrates how to read, write and display NMEA messages 'concurrently' using threads and queues. This represents a useful generic pattern for many end user applications.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 pagehttp://localhost:8080or a RESTful APIhttp://localhost:8080/gps.utilities.pyillustrates how to use variouspynmeagpsutility methods.ExtensibilityThe NMEA protocol is principally defined in the modulesnmeatypes_*.pyas 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 itemsCommand Line UtilityA command line utilitygnssdumpis available via thepygnssutilspackage. 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. Seehttps://github.com/semuconsulting/pygnssutilsfor further details.To installpygnssutils:python3 -m pip install --upgrade pygnssutilsFor help with thegnssdumputility, type:gnssdump -hGraphical ClientA python/tkinter graphical GPS client which supports NMEA, UBX and RTCM3 protocols is available at:https://github.com/semuconsulting/PyGPSClientAuthor & License Informationsemuadmin@semuconsulting.compynmeagpsis 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!