Giter Site home page Giter Site logo

knio / pynmea2 Goto Github PK

View Code? Open in Web Editor NEW
622.0 622.0 220.0 274 KB

Python library for parsing the NMEA 0183 protocol (GPS)

License: MIT License

Makefile 0.12% Python 99.88%
gps gps-data nmea-parser nmea-protocol nmea-sentences nmea0183 pynmea2 python

pynmea2's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pynmea2's Issues

No `repodata.json` in conda repository

While trying to install pynmea2 via anaconda 4.5.9 on Arch Linux I get the following error:

$ conda install -c knio pynmea2
Solving environment: failed

CondaHTTPError: HTTP 404 NOT FOUND for url <https://conda.anaconda.org/knio/noarch/repodata.json>
Elapsed: 00:00.093323
CF-RAY: 445b49fa0a385a62-BOS

The remote server could not find the noarch directory for the
requested channel with url: https://conda.anaconda.org/knio

As of conda 4.3, a valid channel must contain a `noarch/repodata.json` and
associated `noarch/repodata.json.bz2` file, even if `noarch/repodata.json` is
empty. please request that the channel administrator create
`noarch/repodata.json` and associated `noarch/repodata.json.bz2` files.
$ mkdir noarch
$ echo '{}' > noarch/repodata.json
$ bzip2 -k noarch/repodata.json

You will need to adjust your conda configuration to proceed.
Use `conda config --show channels` to view your configuration's current state.
Further configuration help can be found at <https://conda.io/docs/config.html>.

I will try pip install but would prefer the conda method as it gives tighter environment control.

read() vs readline()

So there are certain instances where readline() is non optimal; however, a simple read() also causes issue because it attempts to read an unknown amount of the stream. This is problematic for instances where an end of file does not occur in the stream and/or the data is intermittently available. Specifically, utilizing stdin as the nmea stream object.

Using a read(size) with size set to some value slightly larger than largest expected string size is a viable solution; however, determining the size value is necessary.

Python 3.8 and stream issues

I actually testing with a small usb device that give GPS, but didnt work the streamer with python 3.8, although with python2 works like a charm..

My code:

import serial
import pynmea2

ser = serial.Serial()
ser.port = "/dev/ttyUSB0"
ser.baudrate = 4800
ser.timeout = 1
ser.open()

reader = pynmea2.NMEAStreamReader()

while 1:
    data = ser.readline()
    for msg in reader.next(data):
        print(msg)

With python3.8:
Traceback (most recent call last): File "gps_test1.py", line 14, in <module> for msg in reader.next(data): File "/usr/lib/python3.8/site-packages/pynmea2/stream.py", line 48, in next lines = (self.buffer + data).split('\n') TypeError: can only concatenate str (not "bytes") to str

With python2:
$GPGGA,182116.305,,,,,0,00,,,M,0.0,M,,0000*5D $GPGSA,A,1,,,,,,,,,,,,,,,*1E $GPGSV,3,1,12,12,79,291,30,15,61,117,,24,47,001,,18,38,260,*7A

Any idea or fix?

pip doesn't download 'types' directory

Hello, not sure if this is the correct way to share the problem I was having, but here goes. pip install pynmea2 seemed to work fine for installing pynmea2. But when I went to even import pynmea2, I would get the error:

>>> import pynmea2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\WinPython-32bit-2.7.5.0\python-2.7.5\lib\site-packages\pynmea2\__init__.py", line 5, in <module>
     from .types import *
ImportError: No module named types

Bizzare. It took me a bit of poking around the repo and looking at the install directory to find out that, lo and behold, I had no types directory. I downloaded it and moved it into the right directory manually, and everything seems to work great. But pip didn't seem to get it right in the first place. This happened on both my Windows 8 install, and the install on my Beaglebone Black (vanilla Angstrom). I've got a workaround figured out, but it was a strange problem to come across.

UnicodeDecodeError when get non-ascii char from streaming

Hi, I use pyserial to open serial port from a GPS dongle, when I set wrong baudrate, NMEAStreamReader will read lots of non-ascii char and failed to decode to unicode, so Exception will be raised at line 30 from stream.py:

lines = (self.buffer + data).split('\n')

Pickling and ProprietarySentence classes

By accident I've found out that at least some instances of ProprietarySentences are picklable, but cannot be unpickled due to the way their constructor works (detected on Python 3.4.3 with Pickle Protocol 4 and SRF156 sentence).

Unfortunately I didn't have much opportunity to analyze further, but as the standard sentences are perfectly picklable, I think it could be considered as a bug.

RSD add

Hello
Will this work?

class RSD(TalkerSentence):
    """ RADAR System Data
    """
    fields = (("Origin 1 range", "origin_1_range"), # Origin 1 range, from own ship
              ("Origin 1 bearing", "origin_1_bearing", float), # Origin 1 bearing, degrees from 0
              ("Variable Range Marker 1", "VRM1"), # Variable Range Marker 1 (VRM1), range
              ("Bearing Line 1", "EBL1", float), # Bearing Line 1 (EBL1), degrees from 0
              ("Origin 2 range", "origin_2_range"), # Origin 2 range
              ("Origin 2 bearing", "origin_2_bearing", float), # Origin 2 bearing
              ("Variable Range Marker 2", "VRM2"), # VRM2, range
              ("Bearing Line 2", "EBL2"), # EBL2, degrees
              ("Cursor Range", "Cursor_range", Decimal), # Cursor Range From Own Ship
              ("Cursor Bearing Degrees", "cursor_bearing", float), # Cursor Bearing Degrees Clockwise From Zero
              ("Range Scale", "range_scale", Decimal), # Range Scale
              ("Range Unit", "dist_unit"), # Range Unit
              ("Display rotation", "display_rotation"), # Display rotation 1
              )

In NMEAStreamReader: decode stream data to make it work in Python3

When using Python 3 it gives following error:

File "C:\Python33\lib\site-packages\pynmea2\stream.py", line 48, in next
lines = (self.buffer + data).split('\n')
TypeError: Can't convert 'bytes' object to str implicitly

To fix it just replace in stream.py line 48:
lines = (self.buffer + data).split('\n') with
lines = (self.buffer + data.decode('ascii')).split('\n')

Problems parsing RMC

@Knio Hello,

I have some trouble parsing an RMC sentence (pynmea2 1.4.2 installed through pip):

p.parse('$GPRMC,114130,A,3809.1250,N,02415.8050,E,12195.6,341.5,280915,5,E,A_1\r\n')
Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python2.7/dist-packages/pynmea2/nmea.py", line 106, in parse
raise ParseError('could not parse data: %r' % line)
pynmea2.nmea.ParseError: could not parse data: '$GPRMC,114130,A,3809.1250,N,02415.8050,E,12195.6,341.5,280915,5,E,A_1\r\n'

But if I try to parse:

p.parse('$GPRMC,114130,A,3809.1250,N,02415.8050,E,12195.6,341.5,280915,5,E\r\n')

That's the nmea sentence without the checksum field

I get (almost) the correct parsing:

<RMC(timestamp=datetime.time(11, 41, 30), status='A', lat='3809.1250', lat_dir='N', lon='02415.8050',
lon_dir='E', spd_over_grnd=12195.6, true_course=341.5, datestamp=datetime.date(2015, 9, 28), mag_variation='5', mag_var_dir='E\r\n')>

(notice the last field with \r\n characters)

anyway, parsing a GGA sentence both with checksum and newline chars is ok:

p.parse('$GPGGA,114130,3809.1250,N,02415.8050,E,1,04,5.6,1500.0,M,34.5,M,,*7E\r\n')
<GGA(timestamp=datetime.time(11, 41, 30), lat='3809.1250', lat_dir='N', lon='02415.8050', lon_dir='E', gps_qual=1, num_sats='04', horizontal_dil='5.6', altitude=1500.0, altitude_units='M', geo_sep='34.5', geo_sep_units='M', age_gps_data='', ref_station_id='')>

What could I do? Thanks

PGRME

I found that within the GRM.py file the number of fields was incorrect for the:
class GRME(GRM): """ GARMIN Estimated position error """ fields = ( ("Subtype", "subtype"), ("Estimated Horiz. Position Error", "hpe", Decimal), ("Estimated Horiz. Position Error Unit (M)", "hpe_unit"), ("Estimated Vert. Position Error", "vpe", Decimal), ("Estimated Vert. Position Error Unit (M)", "vpe_unit"), ("Estimated Horiz. Position Error", "osepe", Decimal), ("Overall Spherical Equiv. Position Error", "osepe_unit") )

Using the osepe_unit displaces the unit of horizontal position not the overal spherical eqv that is the next field over. I'm attempting to learn how to edit and recompile this so it works

I can not import pynmea2

Hello,
I'm new to Python and
I couldn't import "pynmea". What can i do?

I'm using Raspberry Pi 3 B+ with noobs 3

for install i used
pi@raspberrypi:~ $ pip install pynmea2
Collecting pynmea2
Downloading https://files.pythonhosted.org/packages/7c/82/f734e75dbb0aecbd9ea20489f364fdac9f124f17c4d4e26d2ab160c8ff58/pynmea2-1.12.0.tar.gz
Building wheels for collected packages: pynmea2
Running setup.py bdist_wheel for pynmea2 ... done
Stored in directory: /home/pi/.cache/pip/wheels/4f/3f/c4/4748d48cbe607279db39640f9295a9eaf12f37c21716272066
Successfully built pynmea2
Installing collected packages: pynmea2
Successfully installed pynmea2-1.12.0
pi@raspberrypi:~ $

in python i used this

pynmea to read

import pynmea

when i run it
Python 3.5.3 (/usr/bin/python3)

%Run screen7.py
Traceback (most recent call last):
File "/home/pi/Desktop/screen7.py", line 3, in
import pynmea
File "/usr/lib/python3/dist-packages/thonny/backend.py", line 317, in _custom_import
module = self._original_import(*args, **kw)
ImportError: No module named 'pynmea'

more adds?

class FSI(TalkerSentence):
    """ Frequency Set Information
    """
    fields = (
        ("Transmitting Frequency", "xmit_freq", Decimal),
        ("Receiving Frequency", "reciv_feq", Decimal),
        ("Communications Mode", "com_mode"),
        ("Power Level", "pwr_lvl"),
        )

# FSI   Frequency Set Information
#        1      2      3 4 5
#        |      |      | | |
# $--FSI,xxxxxx,xxxxxx,c,x*hh
# 1) Transmitting Frequency
# 2) Receiving Frequency
# 3) Communications Mode (NMEA Syntax 2)
# 4) Power Level
# 5) Checksum


class GLC(TalkerSentence):
    """ Geographic Position, Loran-C
    """
    fields = (
        ("GRI Microseconds/10", "gri_ms", Decimal),
        ("Master TOA Microseconds", "toa_ms", float),
        ("Master TOA Signal Status", "toa_sig_stat"),
        ("Time Difference 1 Microseconds", "td_1_ms", float),
        ("Time Difference 1 Signal Status", "td_1_sig_stat"),
        ("Time Difference 2 Microseconds", "td_2_ms", float),
        ("Time Difference 2 Signal Status", "td_2_sig_stat"),
        ("Time Difference 3 Microseconds", "td_3_ms", float),
        ("Time Difference 3 Signal Status", "td_3_sig_stat"),
        ("Time Difference 4 Microseconds", "td_4_ms", float),
        ("Time Difference 4 Signal Status", "td_4_sig_stat"),
        ("Time Difference 5 Microseconds", "td_5_ms", float),
        ("Time Difference 5 Signal Status", "td_5_sig_stat"),
     )

# GLC Geographic Position, Loran-C
#                                           12    14
#        1    2   3 4   5 6   7 8   9 10  11|   13|
#        |    |   | |   | |   | |   | |   | |   | |
# $--GLC,xxxx,x.x,a,x.x,a,x.x,a.x,x,a,x.x,a,x.x,a*hh
#  1) GRI Microseconds/10
#  2) Master TOA Microseconds
#  3) Master TOA Signal Status
#  4) Time Difference 1 Microseconds
#  5) Time Difference 1 Signal Status
#  6) Time Difference 2 Microseconds
#  7) Time Difference 2 Signal Status
#  8) Time Difference 3 Microseconds
#  9) Time Difference 3 Signal Status
# 10) Time Difference 4 Microseconds
# 11) Time Difference 4 Signal Status
# 12) Time Difference 5 Microseconds
# 13) Time Difference 5 Signal Status
# 14) Checksum
# 
# GXA TRANSIT Position – Latitude/Longitude, Location and Time of TRANSIT Fix at Waypoint
# obsolete

class GXA(TalkerSentence):
    """ TRANSIT Position
    """
    fields = (
        ("UTC of position fix", 'timestamp', timestamp),
        ('Latitude', 'lat', float),
        ('Latitude Direction', 'lat_dir'),
        ('Longitude', 'lon', float),
        ('Longitude Direction', 'lon_dir'),
        ("Waypoint ID", ""),
        ("Satelite number", "sat_numb"),
        )

# https://github.com/jcable/gpsd/www/NMEA.txt
# === GXA - TRANSIT Position - Latitude/Longitude ===
# 
# Location and time of TRANSIT fix at waypoint
# 
# ------------------------------------------------------------------------------
#         1         2       3 4        5 6    7 8
#         |         |       | |        | |    | |
#  $--GXA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,c--c,X*hh<CR><LF>
# ------------------------------------------------------------------------------
# 
# Field Number: 
# 
# 1. UTC of position fix
# 2. Latitude
# 3. East or West
# 4. Longitude
# 5. North or South
# 6. Waypoint ID
# 7. Satelite number
# 8. Checksum

class LCD(TalkerSentence):
    """ Loran-C Signal Data
    """
    fields = (
        ("GRI Microseconds/10", "gri_ms", Decimal),
        ("Master Relative SNR", "snr", Decimal),
        ("Master Relative ECD", "ecd", Decimal),
        ("Time Difference 1 Microseconds", "td_1_ms", Decimal),
        ("Time Difference 1 Signal Status", "td_1_sig_stat", Decimal),
        ("Time Difference 2 Microseconds", "td_2_ms", Decimal),
        ("Time Difference 2 Signal Status", "td_2_sig_stat", Decimal),
        ("Time Difference 3 Microseconds", "td_3_ms", Decimal),
        ("Time Difference 3 Signal Status", "td_3_sig_stat", Decimal),
        ("Time Difference 4 Microseconds", "td_4_ms", Decimal),
        ("Time Difference 4 Signal Status", "td_4_sig_stat", Decimal),
        ("Time Difference 5 Microseconds", "td_5_ms", Decimal),
        ("Time Difference 5 Signal Status", "td_5_sig_stat", Decimal),
     )

# LCD Loran-C Signal Data
#        1    2   3   4   5   6   7   8   9   10  11  12  13  14
#        |    |   |   |   |   |   |   |   |   |   |   |   |   |
# $--LCD,xxxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx*hh
#  1) GRI Microseconds/10
#  2) Master Relative SNR
#  3) Master Relative ECD
#  4) Time Difference 1 Microseconds
#  5) Time Difference 1 Signal Status
#  6) Time Difference 2 Microseconds
#  7) Time Difference 2 Signal Status
#  8) Time Difference 3 Microseconds
#  9) Time Difference 3 Signal Status
# 10) Time Difference 4 Microseconds
# 11) Time Difference 4 Signal Status
# 12) Time Difference 5 Microseconds
# 13) Time Difference 5 Signal Status
# 14) Checksum

class MTA(TalkerSentence):
    """ Air Temperature (to be phased out)
    """
    fields = (
        ('Air temperature', 'temperature', float),
        ('Unit of measurement', 'units'),
        )


# MTA Air Temperature (to be phased out) obsolete
#        1   2 3
#        |   | | 
# $--MTW,x.x,C*hh
# 1) Degrees
# 2) Unit of Measurement, Celcius
# 3) Checksum

class OLN(TalkerSentence):
    """ Omega Lane Numbers
    """
    fields = (
        ("Omega Pair 1", "omg_pr_1"),
        ("Omega Pair 1 quontity", "omg_pr_1_qtu"),
        ("Omega Pair 1 total", "omg_pr_1_tot"),
        ("Omega Pair 2", "omg_pr_2"),
        ("Omega Pair 2 quontity", "omg_pr_2_qtu"),
        ("Omega Pair 2 total", "omg_pr_2_tot"),
        ("Omega Pair 3", "omg_pr_3"),
        ("Omega Pair 3 quontity", "omg_pr_3_qtu"),
        ("Omega Pair 3 total", "omg_pr_3_tot"),
        )

# === OLN - Omega Lane Numbers ===
# (The OLN sentence is obsolete as of 2.30)
# 
# ------------------------------------------------------------------------------
#         1          2          3          4
#         |--------+ |--------+ |--------+ |
#  $--OLN,aa,xxx,xxx,aa,xxx,xxx,aa,xxx,xxx*hh<CR><LF>
# ------------------------------------------------------------------------------
# 
# Field Number: 
# 
# 1. Omega Pair 1
# 2. Omega Pair 1
# 3. Omega Pair 1
# 4. Checksum


class SFI(TalkerSentence):
    """ Scanning Frequency Information
    """
    fields = (
        ("Total number of messages", "total_num_msgs"),
        ("Message number", "msg_num"),
        ("Frequency 1", "freq"),
        ("Mode 1", "mode"),
        )

# SFI Scanning Frequency Information
#        1   2   3      4                     n
#        |   |   |      |                     |
# $--SFI,x.x,x.x,xxxxxx,c .......... xxxxxx,c*hh
# 1) Total Number Of Messages
# 2) Message Number
# 3) Frequency 1
# 4) Mode 1
# n) Checksum



class XTR(TalkerSentence):
    """ Cross-Track Error, Dead Reckoning
    """
    fields = (
        ("Magnitude of cross track error", "cross_track_error", float),
        ("Direction to steer, L or R", "l_r"), # L or R
        ("Units", "unit"), # N = Nautical Miles
        )

# === XTR - Cross Track Error - Dead Reckoning ===
# 
# ------------------------------------------------------------------------------
#         1   2 3 4
#         |   | | |
#  $--XTR,x.x,a,N*hh<CR><LF>
# ------------------------------------------------------------------------------
# 
# Field Number: 
# 
# 1. Magnitude of cross track error
# 2. Direction to steer, L or R
# 3. Units, N = Nautical Miles
# 4. Checksum


class ZFO(TalkerSentence):
    """ UTC & Time from Origin Waypoint
    """
    fields = (
        ("Time (UTC)", "timestamp", timestamp),
        ("Elapsed Time",  "elap_time", timestamp),
        ("Origin Waypoint ID", "id"),
        )

# ZFO UTC & Time from Origin Waypoint
#        1         2         3    4
#        |         |         |    |
# $--ZFO,hhmmss.ss,hhmmss.ss,c--c*hh
# 1) Time (UTC)
# 2) Elapsed Time
# 3) Origin Waypoint ID
# 4) Checksum

class ZTG(TalkerSentence):
    """ UTC & Time to Destination Waypoint
    """
    fields = (
        ("Time (UTC)", "timestamp", timestamp),
        ("Elapsed Time",  "elap_time", timestamp),
        ("Destination Waypoint ID", "id"),
        )

# ZTG UTC & Time to Destination Waypoint
#        1         2         3    4
#        |         |         |    |
# $--ZTG,hhmmss.ss,hhmmss.ss,c--c*hh
# 1) Time (UTC)
# 2) Time Remaining
# 3) Destination Waypoint ID
# 4) Checksum


Garmin (and possibly other proprietary) sentences don't parse correctly

My Garmin GPSMAP 78s outputs the following proprietary sentences:
$PGRME,27.5,M,46.0,M,53.6,M*1C
$PGRMZ,50,f,*1D
$PGRMM,WGS 84*06

These are not correctly parsed by pynmea2 because it puts the sentence subtype ('E', 'Z', or 'M', respectively) as the first data item. Thus I get the following output for the above messages, respectively:
<GRME(hpe='E', hpe_unit='27.5', vpe='M', vpe_unit='46.0', osepe='M', osepe_unit='53.6') data=['M']>
<GRMZ(altitude='Z', altitude_unit='50', pos_fix_dim='f') data=['']>
<GRMM(datum='M') data=['WGS 84']>

Continious listen for data from serial and parse all sentences for all the data

I've tried ALOT already, but still nothing works or gives me the wanted results.

Currently I tried to read data usin the streamin docs, however, this throws

pynmea2.nmea.ParseError: ('could not parse data', u'*72\r')

I implemented it like this:

import serial
import pynmea2

ser = serial.Serial()
ser.port = "/dev/ttyS0"
ser.baudrate = 9600
ser.timeout = 1
ser.open()

reader = pynmea2.NMEAStreamReader()

while 1:
    data = ser.readline()
    for msg in reader.next(data):
        print(msg)

Sometimes I get the sream to work, however, it's never ever instant.

I just want to have a continious stream of GPS data to be stored and displayed.

I need the latitude, longitude, speed, time, date.

Someone please help me out here...

RMC message is parsed without a timezone

Per the spec: https://www.trimble.com/OEM_ReceiverHelp/V4.44/en/NMEA-0183messages_RMC.html the date time in the RMC message is UTC, however, pynmea2 parses it and creates a date time with no timezone, thus calling timestamp() on the returned date returns the wrong timestamp.

To reproduce:

msg = '$GPRMC,184446.000,A,3720.18653,N,12153.38874,W,0.0,0.0,130220,,,A*7E'
parsed = pynmea2.parse(msg)
assert parsed.datetime.timestamp()  == 1581619486.0

The above assertion fails unless your computer is set to UTC timezone.

The workaround to the bug for anyone else bumping against this is to replace the datetime with one that has the proper timezone:

msg = '$GPRMC,184446.000,A,3720.18653,N,12153.38874,W,0.0,0.0,130220,,,A*7E'
parsed = pynmea2.parse(msg)
assert parsed.datetime.replace(tzinfo=timezone.utc).timestamp()  == 1581619486.0

Add default constructors for NMEA types

It would be convenient to have default constructors for the NMEA types. When using pynmea2 for encoding NMEA messages we now have to provide constructor parameters which are already known to the NMEA types itself.

An example of how the default constructor would look like for the HDG type;

class HDG(TalkerSentence):
    """ NOTE! This is a GUESS as I cannot find an actual spec
        telling me the fields. Updates are welcome!
    """
    fields = (
        ("Heading", "heading", Decimal),
        ("Deviation", "deviation", Decimal),
        ("Deviation Direction", "dev_dir"),
        ("Variation", "variation", Decimal),
        ("Variation Direction", "var_dir")
    )

    def __init__(self, talker=None, sentence_type=None, data=None):
        if talker == None: 
            talker = 'HC'
        if sentence_type == None:
            sentence_type = 'HDG'
        if data == None:
            data = [''] * len(self.fields)

        super(HDG, self).__init__(talker, sentence_type, data)

The usage would be as followed;

class NmeaTests(unittest.TestCase):
    def testCreateHDG(self):
        hdg = HDG()
        hdg.heading = 180.0
        hdg.deviation = 1.0
        hdg.dev_dir = 'E'
        hdg.variation = 90.0
        hdg.var_dir = 'W'

        msg = str(hdg)
        self.assertEqual('$HCHDG,180.0,1.0,E,90.0,W*61', msg)

XDR sentence transducer name field problem

Have stumbled into several XDR sentences where the transducer name contains <space(s)>
i.e
$ERXDR,G,0,,SHD CANCEL*57
decoding this returns

{'units': '', 'type': 'G', 'id': 'SHD', 'value': '0'}

while the correct id should have been "SHD CANCEL"
The standard does not seem to specify (as far as i can see) if the space is an allowed character in the transducer name

XDR - Transducer Measurements

Measurement data from transducers that measure physical quantities such as temperature, force, pressure, frequency, angular or linear displacement, etc. Data from a variable number transducers measuring the same or different quantities can be mixed in the same sentence. This sentence is designed for use by integrated systems as well as transducers that may be connected in a 'chain' where each transducer receives the sentence as an input and adds its own data fields on before retransmitting the sentence.

$--XDR,a,x.x,a,c--c,...…………...a,x.x,a,c--c*hh

Transducer 'n'1 Data for variable # of transducers

Transducer #1 ID

Units of measure, Transducer #12

Measurement data, Transducer #1 Transducer type, Transducer #12

Notes:

  1. Sets of the four fields 'Type-Data-Units-ID' are allowed for an undefined number of transducers.

Up to 'n' transducers may be included within the limits of allowed sentence length, null fields are not required except where portions of the 'Type-Data-Units-ID' combination are not available.

  1. Allowed transducer types and their units of measure are:

Transducer Type Field Units Field Comments temperature C C = degrees Celsius angular displacement A D = degrees "-" = anti-clockwise linear displacement D M = meters "-" = compression frequency F H = Hertz force N N = Newton "-" = compression pressure P B = Bars, P = Pascal "-" = vacuum flow rate R l = liters/second tachometer T R = RPM humidity H P = Percent volume V M = cubic meters generic G none (null) x.x = variable data current I A = Amperes voltage U V = Volts switch or valve S none (null) 1 = ON/ CLOSED, 0 = OFF/ OPEN salinity L S = ppt ppt = parts per thousand

Proprietary commands?

I'm using this library with an Airmar 200WX weather station. (Thank you!) It doesn't transmit proprietary data sentences, but does have set/query commands. For example:

screenshot from 2015-12-24 11 34 44

I started writing the code for these sentences and think I've realized it will probably require new sentence classes. Also, the manufacturer code needs to go 3 -> 4 characters. What else should be considered?

Stream reader could defer raising exceptions to end of input buffer

Just a thought but you might want to consider allowing the stream reader to defer parsing exceptions rather than raising them immediately to the caller. The problem with raising the exceptions immediately is that you then drop out and lose the remainder of the stream buffer even though it might contain legitimate data after an item that caused the parser to fail.

decimal.InvalidOperation: Invalid literal for Decimal: ''

Hi!

First of all thank you for your good work in this project, then...

msg = pynmea2.parse("$GPVTG,108.53,T,,M,0.04,N,0.07,K,A*31")
print msg.mag_track

  File "./test.py", line 6, in <module>
    print msg.mag_track     
  File "/usr/local/lib/python2.7/dist-packages/pynmea2/nmea.py", line 88, in __getattr__
    return f[2](v)
  File "/usr/lib/python2.7/decimal.py", line 548, in __new__
    "Invalid literal for Decimal: %r" % value)
  File "/usr/lib/python2.7/decimal.py", line 3866, in _raise_error
    raise error(explanation)
decimal.InvalidOperation: Invalid literal for Decimal: ''

I think it would be better to check for empty data and return None or Decimal('NaN') rather that throwing and exception.

Thanks again.

nmea crashes when RMC values are blank

If I send a RMC to nmea that has lots of blank values and then I try to get the datatime back, an exception is thrown. The question I have, is should pynmea2 handle this, or is there code that I can call to check if it's valid before I call, p.datetime where p is the RMC instance. Since, datetime is a derived attribute, should it have checks in there to determine if the timestamp in the record is valid or not? and if so, I don't know what to return, None? Or is there a way for a user to check this before calling datetime?

This is what my gps returns when it's doesn't have any signal (like when I'm testing my code in a building)...
'$GPGGA,,,,,,0,00,,,M,0.0,M,,0000*48'

And I get this exception thrown:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/opt/fh/deployed/src/gps_sink.py", line 153, in run
    c(s)
  File "/opt/fh/deployed/src/gps_sink.py", line 50, in set_time
    self.__gps_time = p.datetime
  File "/opt/fh/env/local/lib/python2.7/site-packages/pynmea2/nmea_utils.py", line 98, in datetime
    return datetime.datetime.combine(self.datestamp, self.timestamp)
TypeError: combine() argument 1 must be datetime.date, not None

Parsing multiple sentences together?

Hey,

How can I parse multiple sentence? is that possible?
Eg:
I have the multiple sentences in a buffer, can I feed it directly to pynmea?

$GPRMC,041626.000,A,1009.9738,N,07618.6555,E,0.00,115.25,100517,,,A6F$GPVTG,115.25,T,,M,0.00,N,0.00,K,A3F$GPGGA,041626.000,1009.9738,N,07618.6555,E,1,6,1.24,10.6,M,-93.4,M,,48$GPGSA,A,3,27,16,23,03,09,08,,,,,,,1.56,1.24,0.9609$GPGSV,4,1,13,23,58,007,23,03,52,210,21,08,48,131,18,27,37,081,16*71$GPGSV,4,2,13,22,34,178,,09,30,335,25,16,23,029,30,07,22,308,*7E$GPGSV,4,3,13,11,19,171,,28,11,223,,01,11,186,,30,02,278,*78$GPGSV,4,4,13,193,,,*40$GLGSV,3,1,10,70,65,312,,69,49,167,,73,32,312,16,80,22,006,*6C$GLGSV,3,2,10,83,18,136,,71,15,331,,82,15,083,,74,08,258,61$GLGSV,3,3,10,68,01,157,,84,01,185,69$GPGLL,1009.9738,N,07618.6555,E,041626.000,A,A58$GPTXT,01,01,02,ANTSTATUS=OPEN2B

Decimal lat and lon ?

Hi,

I have installed this library via pip, but have a problem accessing decimal lat and lon....

The below:
msg = pynmea2.parse(rcv)

print msg.gps_qual
print rcv
print msg.lat+" "+msg.lon
print msg.latitude+" "+msg.longitude

Gives the following output:
1
$GPGGA,185953.00,5123.94863,N,00009.08705,W,1,08,1.18,47.3,M,45.6,M,,*7E
5131.94862 00008.08693

In other words the un-converted lat/lon is printed, but not the decimal version. Any idea why...?

Correct method for parsing proprietary sentences?

I have a Seapath MRU. It outputs the following proprietary sentences:

$PSXN,20,horiz-qual,hgt-qual,head-qual,rp-qual*csum term
$PSXN,22,gyro-calib,gyro-offs*csum term
$PSXN,23,roll,pitch,head,heave*csum term
$PSXN,24,roll-rate,pitch-rate,yaw-rate,vertical-vel*csum term
$PSXN,21,event*csum term

where:

horiz-qual: Horizontal position and velocity quality: 0 = normal, 1 = reduced performance, 2= invalid data.
hgt-qual: Height and vertical velocity quality: 0 = normal, 1 = reduced performance, 2 =invalid data.
head-qual: Heading quality: 0 = normal, 1 = reduced performance, 2 = invalid data.
rp-qual: Roll and pitch quality: 0 = normal, 1 = reduced performance, 2 = invalid data.
gyro-calib: Gyro calibration value since system start-up in degrees on format d.dd.
gyro-offs: Short-term gyro offset in degrees on format d.dd.
roll: Roll in degrees on format d.dd. Positive with port side up.
pitch: Pitch in degrees on format d.dd. Positive with bow up.
heave: Heave in metres on format d.dd. Positive down.
roll-rate: Roll rate in degrees per second on format d.dd. Positive when port side is moving upwards.
pitch-rate: Pitch rate in degrees per second on format d.dd. Positive when bow is moving upwards.
yaw-rate: Yaw rate in degrees per second on format d.dd. Positive when bow is moving towards starboard.
vertical-vel: Vertical velocity in metres per second on format d.dd. Positive when moving downwards.
event: Event code: 1 = system restart.
csum: Checksum (exclusive or) of all characters between, but not including, the preceding $ and * , hexadecimal (00 - FF).
term: CR-LF (2 bytes, values 13 and 10).

with sample data:

'$PSXN,20,0,0,0,0*3B',
'$PSXN,23,0.30,-0.97,298.57,0.13*1B',
'$PSXN,26,0,44.5000,0.7800,-0.9000,NRP*6D',

So I created the following class:

class SXN(pynmea2.ProprietarySentence):
    def __init__(self, manufacturer, data):
        super(SXN, self).__init__(manufacturer, data)
        self.sentence_type = data[1]
        if self.sentence_type == '23':
            SXN.fields = (
                ('Empty', '_'),
                ('sentence', 's'),
                ('roll', 'roll'),
                ('pitch', 'pitch'),
                ('head', 'head'),
                ('heave', 'heave')
            )
            SXN.name_to_idx = dict((f[1], i) for i, f in enumerate(SXN.fields))

This seems to work for the $PSXN,23 message, but as I think about supporting the others it seems kludgey. Is there a better approach?

Iterate through a Talker Sentence

I'm trying to figure out the best way to parse through the fields in a talker.

type(pynmea2.parse("$GPGSV,...")) produces <class 'pynmea2.types.talker.GSV'>

A bit of exploring didn't seem to come up with a good solution, without having to follow how the test code is setup where you have to know before you do.

>>> msg = pynmea2.parse("$GPGSV,...")
>>> msg.__dict__.keys()
dict_keys(['talker', 'sentence_type', 'data'])
>>> variables = [i for i in dir(msg) if not callable(i)]
>>> variables
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'checksum', 'data', 'fields', 'identifier', 'name_to_idx', 'parse', 'proprietary_re', 'query_re', 'render', 'sentence_re', 'sentence_type', 'sentence_types', 'talker', 'talker_re']

I was trying to target something like the following.

import sys
import os
import time
import pynmea2

def read(filename):
    f = open(filename)
    reader = pynmea2.NMEAStreamReader(f)

    while 1:
        for msg in reader.next():
            decode_msg = pynmea2.parse(str(msg))
            print(msg)
            for key in decode_msg:
                value = decode_msg[key]
                print(key, " :: ", value)

def main():
    read(sys.argv[1])

main()

However I get the following response.

Traceback (most recent call last):
  File "convert.py", line 21, in <module>
    main()
  File "convert.py", line 19, in main
    read(sys.argv[1])
  File "convert.py", line 14, in read
    for key in msg:
TypeError: 'GSV' object is not iterable

I also tried a variant with

for talker, sentence_type, data in decode_msg.items():
                print(talker, " :: ", sentence_type , " :: ", data)

but got

Traceback (most recent call last):
  File "/anaconda3/lib/python3.7/site-packages/pynmea2/nmea.py", line 153, in __getattr__
    i = t.name_to_idx[name]
KeyError: 'items'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "convert.py", line 20, in <module>
    main()
  File "convert.py", line 18, in main
    read(sys.argv[1])
  File "convert.py", line 14, in read
    for talker, sentence_type, data in decode_msg.items():
  File "/anaconda3/lib/python3.7/site-packages/pynmea2/nmea.py", line 155, in __getattr__
    raise AttributeError(name)
AttributeError: items

suggestions welcome!

GGA example fails

>>> import pynmea2
>>> pynmea2.__version__
'1.3.3'
>>> msg = pynmea2.GGA('GP', 'GGA', '184353.07', '1929.045', 'S', '02410.506', 'E', '1', '04', '2.6', '100.00', 'M', '-33.9','M', '', '0000')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 4 arguments (17 given)

sub-packages are not installed

running pip install pynmea2 does not include the sub-packages in the install directory:

C:\Users\i7\Downloads\pynmea2>pip install pynmea2
Downloading/unpacking pynmea2
  Downloading pynmea2-1.1.1.tar.gz
  Running setup.py (path:c:\windows\temp\pip_build_i7\pynmea2\setup.py) egg_info for package pynmea2

Installing collected packages: pynmea2
  Running setup.py install for pynmea2

Successfully installed pynmea2
Cleaning up...
C:\Users\i7\Downloads\pynmea2>ls -l C:\Python27\Lib\site-packages\pynmea2
total 27
-rw-r--r-- 1 i7 Administrators  203 Mar 21 10:39 __init__.py
-rw-r--r-- 1 i7 Administrators  419 Mar 21 10:39 __init__.pyc
-rw-r--r-- 1 i7 Administrators   23 Mar 21 10:39 _version.py
-rw-r--r-- 1 i7 Administrators  168 Mar 21 10:39 _version.pyc
-rw-r--r-- 1 i7 Administrators 6427 Mar 21 10:39 nmea.py
-rw-r--r-- 1 i7 Administrators 7826 Mar 21 10:39 nmea.pyc
-rw-r--r-- 1 i7 Administrators 1561 Mar 21 10:39 nmea_utils.py
-rw-r--r-- 1 i7 Administrators 2447 Mar 21 10:39 nmea_utils.pyc
-rw-r--r-- 1 i7 Administrators  989 Mar 21 10:39 stream.py
-rw-r--r-- 1 i7 Administrators 1504 Mar 21 10:39 stream.pyc

time in microseconds ?

I have not found an information of how to enable microseconds in the .timestamp, except when I changed the code within the module 'nmea.utils' to:
ms = (len(s) == 10) and 10000 * int(s[7:9]) or 0

Is it an error or a feature len(s) == 9? s is i.e.: str: 204820.200
NMEA sends 10 characters as 'time', including the comma.

def timestamp(s):
    '''
    Converts a timestamp given in "hhmmss[.ss]" ASCII format to a
    datetime.time object
    '''
    ms = (len(s) == 9) and 10000 * int(s[7:9]) or 0
    a =int(s[7:9])
    t = datetime.time(

Does pynmea2 support python 3.5.3?

Does pynmea2 support pythong 3.5.3? I'm having no luck getting it to run. It doesnt throw me any errors it just doesnt give me an output. Any ideas?

It does successfully run in python 2.7.

The `identifier` method returns sentence name with coma

The identifier method returns sentence name with coma.

The script

import pynmea2

s = '$GPGSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75'
item = pynmea2.parse(s)
print(item.identifier())

outputs

GPGSV,

I would expect

GPGSV

Live stream example

Hi,

Would you be able to add an example of a script streaming and parsing a live feed from a pty or serial interface? I guess that is something many would use pynmea2 for. Excellent work by the way

Error on AIS messages

My NMEA stream contains AIS messages in the following format:

!AIVDM,1,1,,A,ENkb9ONaR@@@@@@@@@@@@@@@@@@;kaD09EE>`00003vP000,4*2C

When I try to parse this message I get a ParseError.

Any way to enable parsing AIS messages?

Runtime error on Python 3 in NMEA Stream Reader

<class 'bytes'> <class 'str'>
Traceback (most recent call last):
File "GPS_nmea.py", line 112, in
main()
File "GPS_nmea.py", line 106, in main
pos=nmeatty.getNMEAPosition()
File "GPS_nmea.py", line 69, in getNMEAPosition
for msg in self._reader.next():
File "/usr/local/lib/python3.4/dist-packages/pynmea2/stream.py", line 49, in next
lines = (self.buffer + data).split('\n')
TypeError: Can't convert 'bytes' object to str implicitly

Distinct parse exceptions

Currently the pynmea2.parse method raises a ValueError on each kind of parser failure. It would be more convenient if there is a distinction in the type of exception so the user-code can decide how to handle each failure type;

Invalid checksum
Unknown sentence type
Sentence not parseable

Ascii and binarry data in one file,how work?

when a file including some ascii data (likes GPGGA) and binarry data(maybe RANGEB),how the pynmea2 work?
In my try, pynmea2 match string at the beign,if I add any string, pynmea2 will match failed?

[Enhancement] parsing data from multiple sentences into a single object

Scenario:
The user wishes to parse the entire file (that is, multiple sentences, same type), and have the data stored in a single object. After parsing the file and returning data to msg, one would get the, say, altitude data as:

>>>msg.altitude
( 100, 200, 300 ) 

msg.altitude returns a tuple

Fails on noisy input

I get a failure trying to access a property on a bad input... Looks like checksum is checked only if it exists in string. If no checksum is in the string the package will still try to parse but fail when trying to access properties. For example attached file will fail when trying to access the properties.

test_bad_rmc.py.txt

Version 1.5.3

streamreader problem

I am getting lines like the following

$GPGGA,183800.0,3203.121,N,03446.075,E,1,03,4.90,00038,M,017,M,,*5A
$GPVTG,000.0,T,357.3,M,000.00,N,000.00,K*4C

from serial read of a trimble gps . I tried

streamreader = pynmea2.NMEAStreamReader()
while 1:
        line = ser.readline().decode('ascii', errors='replace')
#       print (line.strip())
        print (line)
        if line[0:6]=='$GPGGA':
                msg = pynmea2.parse(line.strip())
                print('msg1:'+str(msg))
        for msg in streamreader.next(line.strip()):
                print('msg:'+str(msg))
                print('lat {} long {}'.format(msg.latitude,msg.longitude))

The pynmea2.parse spits back the input, while the streamreader.next does not get any msgs. Any clues?

Further fix on proper parsing of proprietary messages

My fix for issue #44 had the wrong indexes since I foolishly copied it directly from the ubx.py code.

the proper __init__ method should have been the following:

    def __init__(self, manufacturer, data):
        self.sentence_type = manufacturer + data[0]
        super(GRM, self).__init__(manufacturer, data[1:])

This same method (with SRF) should be added to srf.py

Any interest in backwards compatibility to Python 2.6?

I realize the library is listed as 2.7 and 3, but so far the only change I needed to make to allow it to work (in my application) using Python 2.6 was to change nmea.py line 29 to:

    cls.name_to_idx = dict((f[1], i) for i, f in enumerate(cls.fields))

If this is indeed the only change required to add additional backwards compatibility, then perhaps its trivial enough to include in the baseline?

Unknown sentence type GPPWR

I'm using a Dual XGPS160 and getting the following issue

Traceback (most recent call last):
  File "./test.py", line 14, in <module>
    msg = pynmea2.parse( data )
  File "/Library/Python/2.7/site-packages/pynmea2/nmea.py", line 130, in parse
    raise SentenceTypeError('Unknown sentence type %s' % sentence_type)
pynmea2.nmea.SentenceTypeError: Unknown sentence type GPPWR,

The sentence is:

$GPPWR,0922,1,0,0,0,00,0,S,97, 1 5 ,000*1F

Thanks,
Michael

RTE and R00 types are broken / have dead code

Hi,

Until I dug into things I hadn't realised that pynmea2 isn't a straight fork of pynmea--it appears there was some sort of refactoring of pynmea done before it was imported into this repository as pynmea2?

When the refactoring was done it appears the types that implement variable length sentences (RTE and R00) weren't converted to work correctly. Specifically, RTE only ever stores one item in the waypoint_list field and puts the rest in the data field (I haven't looked closely at R00).

Also, the RTE class has a now unused parse method (https://github.com/Knio/pynmea2/blob/master/pynmea2/types.py#L377) which is supposed to override another method (originally https://code.google.com/p/pynmea/source/browse/pynmea/nmea.py#39) but doesn't and never gets called. When the method is called manually it fails because it refers to self._parse() which doesn't exist anymore (originally it was here: https://code.google.com/p/pynmea/source/browse/pynmea/nmea.py#14).

I discovered all this when looking for the "correct" way to implement XDR which is another variable length sentence.

It seems the correct approach is probably to override the __init__, render & __repr__ methods of the variable length sentence classes. I'm going to attempt this with the XDR class.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.