pynmea2-1.12.0/0000775000175100017510000000000013170047315012573 5ustar tomtom00000000000000pynmea2-1.12.0/pynmea2.egg-info/0000775000175100017510000000000013170047315015640 5ustar tomtom00000000000000pynmea2-1.12.0/pynmea2.egg-info/dependency_links.txt0000660000175100017510000000000113170047315021702 0ustar tomtom00000000000000 pynmea2-1.12.0/pynmea2.egg-info/SOURCES.txt0000660000175100017510000000142313170047315017520 0ustar tomtom00000000000000MANIFEST.in README.md setup.py examples/serial.py pynmea2/__init__.py pynmea2/_version.py pynmea2/nmea.py pynmea2/nmea_file.py pynmea2/nmea_utils.py pynmea2/seatalk_utils.py pynmea2/stream.py pynmea2.egg-info/PKG-INFO pynmea2.egg-info/SOURCES.txt pynmea2.egg-info/dependency_links.txt pynmea2.egg-info/top_level.txt pynmea2/types/__init__.py pynmea2/types/talker.py pynmea2/types/proprietary/__init__.py pynmea2/types/proprietary/ash.py pynmea2/types/proprietary/grm.py pynmea2/types/proprietary/rdi.py pynmea2/types/proprietary/srf.py pynmea2/types/proprietary/sxn.py pynmea2/types/proprietary/tnl.py pynmea2/types/proprietary/ubx.py test/test_ash.py test/test_file.py test/test_proprietary.py test/test_pynmea.py test/test_rdi.py test/test_stream.py test/test_sxn.py test/test_types.pypynmea2-1.12.0/pynmea2.egg-info/top_level.txt0000660000175100017510000000001013170047315020355 0ustar tomtom00000000000000pynmea2 pynmea2-1.12.0/pynmea2.egg-info/PKG-INFO0000660000175100017510000000175613170047315016742 0ustar tomtom00000000000000Metadata-Version: 1.1 Name: pynmea2 Version: 1.12.0 Summary: Python library for the NMEA 0183 protcol Home-page: https://github.com/Knio/pynmea2 Author: Tom Flanagan Author-email: tom@zkpq.ca License: MIT Description-Content-Type: UNKNOWN Description: UNKNOWN Keywords: python nmea gps parse parsing nmea0183 0183 Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Scientific/Engineering :: GIS Classifier: Topic :: Software Development :: Libraries :: Python Modules pynmea2-1.12.0/pynmea2/0000775000175100017510000000000013170047315014146 5ustar tomtom00000000000000pynmea2-1.12.0/pynmea2/stream.py0000664000175100017510000000407713023406655016026 0ustar tomtom00000000000000from __future__ import unicode_literals from . import nmea __all__ = ['NMEAStreamReader'] ERRORS = ('raise', 'yield', 'ignore') class NMEAStreamReader(object): ''' Reads NMEA sentences from a stream. ''' def __init__(self, stream=None, errors='raise'): ''' Create NMEAStreamReader object. `stream`: file-like object to read from, can be omitted to pass data to `next` manually. must support `.readline()` which returns a string `errors`: behaviour when a parse error is encountered. can be one of: `'raise'` (default) raise an exception immediately `'yield'` yield the ParseError as an element in the stream, and continue reading at the next line `'ignore'` completely ignore and suppress the error, and continue reading at the next line ''' if errors not in ERRORS: raise ValueError('errors must be one of {!r} (was: {!r})' .format(ERRORS, errors)) self.errors = errors self.stream = stream self.buffer = '' def next(self, data=None): ''' consume `data` (if given, or calls `stream.read()` if `stream` was given in the constructor) and yield a list of `NMEASentence` objects parsed from the stream (may be empty) ''' if data is None: if self.stream: data = self.stream.readline() else: return lines = (self.buffer + data).split('\n') self.buffer = lines.pop() for line in lines: try: msg = nmea.NMEASentence.parse(line) yield msg except nmea.ParseError as e: if self.errors == 'raise': raise e if self.errors == 'yield': yield e if self.errors == 'ignore': pass pynmea2-1.12.0/pynmea2/nmea_file.py0000664000175100017510000000373613023406655016453 0ustar tomtom00000000000000try: # pylint: disable=used-before-assignment basestring = basestring except NameError: # py3 basestring = str from .nmea import NMEASentence class NMEAFile(object): """ Reads NMEA sentences from a file similar to a standard python file object. """ def __init__(self, f, *args, **kwargs): super(NMEAFile, self).__init__() if isinstance(f, basestring) or args or kwargs: self._file = self.open(f, *args, **kwargs) else: self._file = f self._context = None def open(self, fp, mode='r'): """ Open the NMEAFile. """ self._file = open(fp, mode=mode) return self._file def close(self): """ Close the NMEAFile. """ self._file.close() def __iter__(self): """ Iterate through the file yielding NMEASentences :return: """ for line in self._file: yield self.parse(line) def __enter__(self): if hasattr(self._file, '__enter__'): self._context = self._file.__enter__() return self def __exit__(self, exc_type, exc_val, exc_tb): if self._context: ctx = self._context self._context = None ctx.__exit__(exc_type, exc_val, exc_tb) def next(self): """ Iterate through the file object returning NMEASentence objects :return: NMEASentence """ data = self._file.readline() return self.parse(data) def parse(self, s): return NMEASentence.parse(s) def readline(self): """ Return the next NMEASentence in the file object :return: NMEASentence """ data = self._file.readline() s = self.parse(data) return s def read(self): """ Return a list of NMEASentence objects for each line in the file :return: list of NMEASentence objects """ return [s for s in self] pynmea2-1.12.0/pynmea2/nmea_utils.py0000664000175100017510000000674513023406655016677 0ustar tomtom00000000000000#pylint: disable=invalid-name import datetime def timestamp(s): ''' Converts a timestamp given in "hhmmss[.ss]" ASCII format to a datetime.time object ''' ms_s = s[6:] ms = ms_s and int(float(ms_s) * 1000000) or 0 t = datetime.time( hour=int(s[0:2]), minute=int(s[2:4]), second=int(s[4:6]), microsecond=ms) return t def datestamp(s): ''' Converts a datestamp given in "DDMMYY" ASCII format to a datetime.datetime object ''' return datetime.datetime.strptime(s, '%d%m%y').date() import re def dm_to_sd(dm): ''' Converts a geographic coordiante given in "degres/minutes" dddmm.mmmm format (ie, "12319.943281" = 123 degrees, 19.953281 minutes) to a signed decimal (python float) format ''' # '12319.943281' if not dm or dm == '0': return 0. d, m = re.match(r'^(\d+)(\d\d\.\d+)$', dm).groups() return float(d) + float(m) / 60 class LatLonFix(object): '''Mixin to add `lattitude` and `longitude` properties as signed decimals to NMEA sentences which have coordiantes given as degrees/minutes (lat, lon) and cardinal directions (lat_dir, lon_dir)''' #pylint: disable=no-member @property def latitude(self): '''Lattitude in signed degrees (python float)''' sd = dm_to_sd(self.lat) if self.lat_dir == 'N': return +sd elif self.lat_dir == 'S': return -sd else: return 0. @property def longitude(self): '''Longitude in signed degrees (python float)''' sd = dm_to_sd(self.lon) if self.lon_dir == 'E': return +sd elif self.lon_dir == 'W': return -sd else: return 0. @staticmethod def _minutes(x): return abs(x * 60.) % 60. @staticmethod def _seconds(x): return abs(x * 3600.) % 60. @property def latitude_minutes(self): return self._minutes(self.latitude) @property def longitude_minutes(self): return self._minutes(self.longitude) @property def latitude_seconds(self): return self._seconds(self.latitude) @property def longitude_seconds(self): return self._seconds(self.longitude) class DatetimeFix(object): #pylint: disable=no-member @property def datetime(self): return datetime.datetime.combine(self.datestamp, self.timestamp) class ValidStatusFix(object): #pylint: disable=no-member @property def is_valid(self): return self.status == 'A' class ValidGSAFix(object): #pylint: disable=no-member @property def is_valid(self): return int(self.mode_fix_type) in [2, 3] class ValidGGAFix(object): #pylint: disable=no-member @property def is_valid(self): return self.gps_qual in range(1,6) class ValidVBWFix(object): #pylint: disable=no-member @property def is_valid(self): return self.data_validity_water_spd == self.data_validity_grnd_spd == 'A' class TZInfo(datetime.tzinfo): def __init__(self, hh, mm): self.hh = hh self.mm = mm super(TZInfo, self).__init__() def tzname(self, dt): return '' def dst(self, dt): return datetime.timedelta(0) def utcoffset(self, dt): return datetime.timedelta(hours=self.hh, minutes=self.mm) pynmea2-1.12.0/pynmea2/types/0000775000175100017510000000000013170047315015312 5ustar tomtom00000000000000pynmea2-1.12.0/pynmea2/types/proprietary/0000775000175100017510000000000013170047315017672 5ustar tomtom00000000000000pynmea2-1.12.0/pynmea2/types/proprietary/srf.py0000664000175100017510000000234013023406655021040 0ustar tomtom00000000000000# SiRF from ... import nmea class SRF(nmea.ProprietarySentence): sentence_types = {} def __new__(_cls, manufacturer, data): name = manufacturer + data[0] cls = _cls.sentence_types.get(name, _cls) return super(SRF, cls).__new__(cls) def __init__(self, manufacturer, data): self.sentence_type = manufacturer + data[0] super(SRF, self).__init__(manufacturer, data) class SRF103(SRF): fields = ( ("Subtype", "subtype"), ('Sentence type', 'sentence'), # 00=GGA # 01=GLL # 02=GSA # 03=GSV # 04=RMC # 05=VTG ('Command', 'command'), # 0=Set # 1=Query ('Rate', 'rate'), ('Checksum', 'checksum'), # 0=No, 1=Yes ) class SRF100(SRF): fields = ( ("Subtype", "subtype"), ('Protocol', 'protocol'), # 0 = SiRF Binary # 1 = NMEA ('Baud Rate', 'baud'), # 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 ('Data bits', 'databits'), # 8 (, 7 in NMEA) ('Stop bits', 'stopbits'), # 0, 1 ('Parity', 'parity'), # 0, 1=Odd, 2=Even ) pynmea2-1.12.0/pynmea2/types/proprietary/ubx.py0000664000175100017510000000406313023406655021050 0ustar tomtom00000000000000# pylint: disable=wildcard-import,unused-wildcard-import from decimal import Decimal from ... import nmea from ...nmea_utils import * # u-blox class UBX(nmea.ProprietarySentence): sentence_types = {} def __new__(_cls, manufacturer, data): name = manufacturer + data[1] cls = _cls.sentence_types.get(name, _cls) return super(UBX, cls).__new__(cls) def __init__(self, manufacturer, data): self.sentence_type = manufacturer + data[1] super(UBX, self).__init__(manufacturer, data[2:]) class UBX00(UBX, LatLonFix): """ Lat/Long Position Data """ fields = ( ("Timestamp (UTC)", "timestamp", timestamp), ("Latitude", "lat"), ("Latitude Direction", "lat_dir"), ("Longitude", "lon"), ("Longitude Direction", "lon_dir"), ("Altitude above user datum ellipsoid", "alt_ref"), ("Navigation Status", "nav_stat"), ("Horizontal Accuracy Estimate", "h_acc"), ("Vertical Accuracy Estimate", "v_acc"), ("Speed over Ground", "sog"), ("Course over Ground", "cog"), ("Vertical Velocity", "v_vel"), ("Age of Differential Corrections", "diff_age"), ("Horizontal Dilution of Precision", "hdop"), ("Vertical Dilution of Precision", "vdop"), ("Time Dilution of Precision", "tdop"), ("Number of Satellites Used", "num_svs"), ("Reserved", "reserved") ) class UBX03(UBX): """ Satellite Status """ fields = ( ("Number of GNSS Satellites Tracked", "num_sv", int), ) @property def satellite_list(self): return self.data[1:] class UBX04(UBX): """ Time and Day Clock Information """ fields = ( ("UTC Time", "time", timestamp), ("UTC Date", "date", datestamp), ("UTC Time of Week", "utc_tow"), ("UTC Week Number", "utc_wk"), ("Leap Seconds", "leap_sec"), ("Receiver Clock Bias", "clk_bias", int), ("Receiver Clock Drift", "clk_drift", Decimal), ("Time Pulse Granularity", "tp_gran", int), ) pynmea2-1.12.0/pynmea2/types/proprietary/rdi.py0000664000175100017510000000122613170047266021030 0ustar tomtom00000000000000''' Support for proprietary message(s) from RD Instruments ''' from ... import nmea class RDI(nmea.ProprietarySentence): ''' RD Instruments message. Only one sentence known? ''' sentence_types = {} def __new__(_cls, manufacturer, data): name = manufacturer + data[0] cls = _cls.sentence_types.get(name, _cls) return super(RDI, cls).__new__(cls) class RDID(RDI): ''' RD Instruments heading, pitch and roll data ''' fields = ( ('Subtype', 'subtype'), ("Pitch", "pitch", float), ("Roll", "roll", float), ("Heading", "heading", float) ) pynmea2-1.12.0/pynmea2/types/proprietary/ash.py0000664000175100017510000000754313170047266021035 0ustar tomtom00000000000000''' Support for proprietary messages from Ashtech receivers. ''' # pylint: disable=wildcard-import,unused-wildcard-import from decimal import Decimal import re from ... import nmea from ...nmea_utils import * class ASH(nmea.ProprietarySentence): ''' Generic Ashtech Response Message ''' sentence_types = {} def __new__(_cls, manufacturer, data): ''' Return the correct sentence type based on the first field ''' sentence_type = data[1] name = manufacturer + 'R' + sentence_type if name not in _cls.sentence_types: # ASHRATT does not have a sentence type if ASHRATT.match(data): return super(ASH, ASHRATT).__new__(ASHRATT) cls = _cls.sentence_types.get(name, ASH) return super(ASH, cls).__new__(cls) class ASHRATT(ASH): ''' RT300 proprietary attitude sentence ''' @staticmethod def match(data): return re.match(r'^\d{6}\.\d{3}$', data[1]) def __init__(self, *args, **kwargs): self.subtype = 'ATT' super(ASHRATT, self).__init__(*args, **kwargs) fields = ( ('R', '_r'), ('Timestamp', 'timestamp', timestamp), ('Heading Angle', 'true_heading', float), ('Is True Heading', 'is_true_heading'), ('Roll Angle', 'roll', float), ('Pitch Angle', 'pitch', float), ('Heave', 'heading', float), ('Roll Accuracy Estimate', 'roll_accuracy', float), ('Pitch Accuracy Estimate', 'pitch_accuracy', float), ('Heading Accuracy Estimate', 'heading_accuracy', float), ('Aiding Status', 'aiding_status', Decimal), ('IMU Status', 'imu_status', Decimal), ) class ASHRHPR(ASH): ''' Ashtech HPR Message ''' fields = ( ('R', '_r'), ('Subtype', 'subtype'), ('Timestamp', 'timestamp', timestamp), ('Heading Angle', 'heading', Decimal), ('Pitch Angle', 'pitch', Decimal), ('Roll Angle', 'roll', Decimal), ('Carrier measurement RMS', 'carrier_rms', Decimal), ('Baseline measurement RMS', 'baseline_rms', Decimal), ('Integer Ambiguity', 'integer_ambiguity'), ('Mode', 'mode'), ('Status', 'status'), ('PDOP', 'pdop', float), ) class ASHRLTN(ASH): ''' Ashtech LTN Message ''' fields = ( ('R', '_r'), ('Subtype', 'subtype'), ('Latency (ms)', 'latency', int), ) class ASHRPOS(ASH, LatLonFix): ''' Ashtech POS Message ''' fields = ( ('R', '_r'), ('Subtype', 'subtype'), ('Solution Type', 'mode', int), ('Satellites used in Solution', 'sat_count', int), ('Timestamp', 'timestamp', timestamp), ('Latitude', 'lat'), ('Latitude Direction', 'lat_dir'), ('Longitude', 'lon'), ('Longitude Direction', 'lon_dir'), ('Altitude above WGS84 ellipsoid, meters', 'altitude'), ('Empty', '__'), ("True Track/Course Over Ground", "course", float), ("Speed Over Ground", "spd_over_grnd", float), ('Vertical Velocity', 'vertical_velocity', Decimal), ('PDOP', 'pdop', float), ('HDOP', 'hdop', float), ('VDOP', 'vdop', float), ('TDOP', 'tdop', float), ('Base station ID', 'station_id', int) ) class ASHRVEL(ASH): ''' Ashtech VEL Message ''' fields = ( ('R', '_r'), ('Subtype', 'subtype'), ('ENU', 'enu', int), ('Timestamp', 'timestamp', timestamp), ('Easting', 'easting', Decimal), ('Northing', 'northing', Decimal), ('Vertical Velocity', 'vertical', Decimal), ('Easting RMS', 'easting_rms', Decimal), ('Northing RMS', 'northing_rms', Decimal), ('Vertical RMS', 'vertical_rms', Decimal), ('Applied effective velocity smoothing interval (ms)', 'smoothing', Decimal), ) pynmea2-1.12.0/pynmea2/types/proprietary/grm.py0000664000175100017510000000263413023406655021041 0ustar tomtom00000000000000# Garmin from decimal import Decimal from ... import nmea class GRM(nmea.ProprietarySentence): sentence_types = {} def __new__(_cls, manufacturer, data): name = manufacturer + data[0] cls = _cls.sentence_types.get(name, _cls) return super(GRM, cls).__new__(cls) def __init__(self, manufacturer, data): self.sentence_type = manufacturer + data[0] super(GRM, self).__init__(manufacturer, data) 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") ) class GRMM(GRM): """ GARMIN Map Datum """ fields = ( ("Subtype", "subtype"), ('Currently Active Datum', 'datum'), ) class GRMZ(GRM): """ GARMIN Altitude Information """ fields = ( ("Subtype", "subtype"), ("Altitude", "altitude", Decimal), ("Altitude Units (Feet)", "altitude_unit"), ("Positional Fix Dimension (2=user, 3=GPS)", "pos_fix_dim") ) pynmea2-1.12.0/pynmea2/types/proprietary/tnl.py0000660000175100017510000000651412551253255021047 0ustar tomtom00000000000000# -- TRIMBLE -- # # pylint: disable=wildcard-import,unused-wildcard-import from ... import nmea from ...nmea_utils import * """ Support for proprietary messages from BD9xx recievers. Documentation: www.trimble.com/OEM_ReceiverHelp/v4.85/en/ """ class TNL(nmea.ProprietarySentence): sentence_types = {} """ Generic Trimble Message """ def __new__(_cls, manufacturer, data): ''' Return the correct sentence type based on the first field ''' sentence_type = data[0] or data[1] name = manufacturer + sentence_type cls = _cls.sentence_types.get(name, _cls) return super(TNL, cls).__new__(cls) def __init__(self, manufacturer, data): self.sentence_type = data[0] or data[1] super(TNL, self).__init__(manufacturer, data) class TNLAVR(TNL): """ Trimble AVR Message """ fields = ( ('Empty', '_'), ('Sentence Type', 'type'), ('Timestamp', 'timestamp', timestamp), ('Yaw Angle', 'yaw_angle'), ('Yaw', 'yaw'), ('Tilt Angle', 'tilt_angle'), ('Tilt', 'tilt'), ('Roll Angle', 'roll_angle'), ('Roll', 'roll'), ('Baseline Range', 'baseline'), ('GPS Quality', 'gps_quality'), ('PDOP', 'pdop'), ('Total number of satelites in use', 'num_sats'), ) class TNLBPQ(TNL, LatLonFix, DatetimeFix): """ Trimble BPQ Message """ fields = ( ('Empty', '_'), ('Sentence Type', 'type'), ('Timestamp', 'timestamp', timestamp), ("Datestamp", "datestamp", datestamp), ("Latitude", "lat"), ("Latitude Direction", "lat_dir"), ("Longitude", "lon"), ("Longitude Direction", "lon_dir"), ('Height Ellipsoid', 'height'), ('Meters', 'meters'), ('Mode fix type', 'mode_fix_type'), ('Total number of satelites in use', 'num_sats'), ) class TNLGGK(TNL, LatLonFix, DatetimeFix): """ Trimble GGK Message """ fields = ( ('Empty', '_'), ('Sentence Type', 'type'), ('Timestamp', 'timestamp', timestamp), ("Datestamp", "datestamp", datestamp), ("Latitude", "lat"), ("Latitude Direction", "lat_dir"), ("Longitude", "lon"), ("Longitude Direction", "lon_dir"), ('GPS Quality', 'quality'), ('Total number of satelites in use', 'num_sats'), ('DOP', 'dop'), ('Height Ellipsoid', 'height'), ('Meters', 'meters'), ('Mode fix type', 'mode_fix_type'), ) class TNLVHD(TNL, DatetimeFix): """ Trimble VHD Message """ fields = ( ('Empty', '_'), ('Sentence Type', 'type'), ('Timestamp', 'timestamp', timestamp), ("Datestamp", "datestamp", datestamp), ('Azimuth Angle', 'azimuth'), ('AzimuthTime', 'azdt'), ('Vertical Angle', 'vertical'), ('VerticalTime', 'vertdt'), ('Range', 'range'), ('RangeTime', 'rdt'), ('GPS Quality', 'gps_quality'), ('Total number of satelites in use', 'num_sats'), ('PDOP', 'pdop'), ) class TNLPJT(TNL): """ Trimble PJT Message """ fields = ( ('Empty', '_'), ('Sentence Type', 'type'), ('Coordinate System', 'coord_name'), ('Project Name', 'project_name'), ) pynmea2-1.12.0/pynmea2/types/proprietary/sxn.py0000664000175100017510000000713313170047266021065 0ustar tomtom00000000000000''' Seapath Message types: $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). Samples: $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 ''' from ... import nmea class SXN(nmea.ProprietarySentence): sentence_types = {} def __new__(_cls, manufacturer, data): name = manufacturer + data[1] cls = _cls.sentence_types.get(name, _cls) return super(SXN, cls).__new__(cls) class SXN20(SXN): fields = ( ('Blank', '_blank'), ('Message Type', 'message_type', int), ('Horizontal position and velocity quality', 'horiz_qual', int), ('Height and vertical velocity quality', 'hgt_qual', int), ('Heading quality', 'head_qual', int), ('Roll and pitch quality', 'rp_qual', int), ) class SXN21(SXN): fields = ( ('Blank', '_blank'), ('Message Type', 'message_type', int), ('Event code: 1 = system restart.', 'event', int), ) class SXN22(SXN): fields = ( ('Blank', '_blank'), ('Message Type', 'message_type', int), ('Gyro calibration value since system start-up in degrees', 'gyro_calib', float), ('Short-term gyro offset in degrees', 'gyro_ffs', float), ) class SXN23(SXN): fields = ( ('Blank', '_blank'), ('Message Type', 'message_type', int), ('Roll in degrees. Positive with port side up.', 'roll', float), ('Pitch in degrees. Positive with bow up.', 'pitch', float), ('Heading, degrees true (0.00 - 359.99).', 'head', float), ('Heave in metres. Positive down.', 'heave', float) ) class SXN24(SXN): fields = ( ('Blank', '_blank'), ('Message Type', 'message_type', int), ('Roll rate in degrees/second. Positive when port side is moving upwards.', 'roll_rate', float), ('Pitch rate in degrees/second. Positive when bow is moving upwards.', 'pitch_rate', float), ('Yaw rate in degrees/second. Positive when bow is moving towards starboard.', 'yaw_rate', float), ('Vertical velocity in metres/second. Positive when moving downwards.', 'vertical_vel', float) ) pynmea2-1.12.0/pynmea2/types/proprietary/__init__.py0000664000175100017510000000020713170047266022007 0ustar tomtom00000000000000from . import ash from . import grm from . import rdi from . import srf from . import sxn from . import tnl from . import ubx pynmea2-1.12.0/pynmea2/types/talker.py0000664000175100017510000010476313152023630017153 0ustar tomtom00000000000000# pylint: disable=wildcard-import,unused-wildcard-import from ..nmea import TalkerSentence from ..nmea_utils import * from ..seatalk_utils import * from collections import namedtuple from decimal import Decimal #pylint: disable=missing-docstring #pylint: disable=no-init #pylint: disable=too-few-public-methods class AAM(TalkerSentence): """ Waypoint Arrival Alarm """ fields = ( ("Arrival Circle Entered", "arrival_circ_entered"), ("Perpendicular Passed", "perp_passed"), ("Circle Radius", "circle_rad"), ("Nautical Miles", "circle_rad_unit"), ("Waypoint ID", "waypoint_id"), ) class ALM(TalkerSentence): """ GPS Almanac data """ fields = ( ("Total number of messages", "total_num_msgs"), ("Message number", "msg_num"), ("Satellite PRN number", "sat_prn_num"), # 01 - 32 ("GPS week number", "gps_week_num"), # Week since Jan 6 1980 ("SV Health, bits 17-24 of each almanac page", "sv_health"), ("Eccentricity", "eccentricity"), ("Almanac Reference Time", "alamanac_ref_time"), ("Inclination Angle", "inc_angle"), ("Rate of right ascension", "rate_right_asc"), ("Root of semi-major axis", "root_semi_major_axis"), ("Argument of perigee", "arg_perigee"), ("Longitude of ascension node", "lat_asc_node"), ("Mean anomaly", "mean_anom"), ("F0 Clock parameter", "f0_clock_param"), ("F1 Clock parameter", "f1_clock_param"), ) class APA(TalkerSentence): """ Autopilot Sentence "A" """ fields = ( ("General Status", "status_gen"), ("Cycle lock Status", "status_cycle_lock"), ("Cross Track Error Magnitude", "cross_track_err_mag"), ("Direction to Steer (L or R)", "dir_steer"), ("Cross Track Units (Nautical Miles or KM)", "cross_track_unit"), ("Arrival Circle Entered", "arr_circle_entered"), # A = True ("Perpendicular passed at waypoint", "perp_passed"), # A = True ("Bearing origin to destination", "bearing_to_dest"), ("Bearing type", "bearing_type"), # M = Magnetic, T = True ("Destination waypoint ID", "dest_waypoint_id"), ) class APB(TalkerSentence): """ Autopilot Sentence "B" """ fields = ( ("General Status", "status_gen"), ("Cycle lock Status", "status_cycle_lock"), ("Cross Track Error Magnitude", "cross_track_err_mag"), ("Direction to Steer (L or R)", "dir_steer"), ("Cross Track Units (Nautical Miles or KM)", "cross_track_unit"), ("Arrival Circle Entered", "arr_circle_entered"), # A = True ("Perpendicular passed at waypoint", "perp_passed"), # A = True ("Bearing origin to destination", "bearing_to_dest"), ("Bearing type", "bearing_type"), # M = Magnetic, T = True ("Destination waypoint ID", "dest_waypoint_id"), ("Bearing, present position to dest", "bearing_pres_dest"), ("Bearing to destination, type", "bearing_pres_dest_type"), # M = Magnetic, T = True ("Heading to steer to destination", "heading_to_dest"), ("Heading to steer to destination type", "heading_to_dest_type"), ) # M = Magnetic, T = True class BEC(TalkerSentence): """ Bearing & Distance to Waypoint, Dead Reckoning """ fields = ( ("Timestamp", "timestamp", timestamp), ("Waypoint Latitude", "waypoint_lat"), ("Waypoint Latitude direction", "waypoint_lat_dir"), ("Waypoint Longitude", "waypoint_lon"), ("Waypoint Longitude direction", "waypoint_lon_dir"), ("Bearing, true", "bearing_true"), ("Bearing True symbol", "bearing_true_sym"), # T = true ("Bearing Magnetic", "bearing_mag"), ("Bearing Magnetic symbol", "bearing_mag_sym"), ("Nautical Miles", "nautical_miles"), ("Nautical Miles symbol", "nautical_miles_sym"), ("Waypoint ID", "waypoint_id"), ("FAA mode indicator", "faa_mode"), ) class BOD(TalkerSentence): # 045.,T,023.,M,DEST,START fields = ( ('Bearing True', 'bearing_t', Decimal), ('Bearing True Type', 'bearing_t_type'), ('Bearing Magnetic', 'bearing_mag', Decimal), ('Bearing Magnetic Type', 'bearing_mag_type'), ('Destination', 'dest'), ('Start', 'start'), ) class BWC(TalkerSentence): fields = ( ('Timestamp', 'timestamp', timestamp), ('Latitude of next Waypoint', 'lat_next'), ('Latitude of next Waypoint Direction', 'lat_next_direction'), ('Longitude of next Waypoint', 'lon_next'), ('Longitude of next Waypoint Direction', 'lon_next_direction'), ('True track to waypoint', 'true_track'), ('True Track Symbol', 'true_track_sym'), ('Magnetic track to waypoint', 'mag_track'), ('Magnetic Symbol', 'mag_sym'), ('Range to waypoint', 'range_next'), ('Unit of range', 'range_unit'), ('Waypoint Name', 'waypoint_name'), ) class BWR(TalkerSentence): fields = ( ('Timestamp', 'timestamp', timestamp), ('Latitude of next Waypoint', 'lat_next'), ('Latitude of next Waypoint Direction', 'lat_next_direction'), ('Longitude of next Waypoint', 'lon_next'), ('Longitude of next Waypoint Direction', 'lon_next_direction'), ('True track to waypoint', 'true_track'), ('True Track Symbol', 'true_track_sym'), ('Magnetic track to waypoint', 'mag_track'), ('Magnetic Symbol', 'mag_sym'), ('Range to waypoint', 'range_next'), ('Unit of range', 'range_unit'), ('Waypoint Name', 'waypoint_name'), ) class GGA(TalkerSentence, ValidGGAFix, LatLonFix): fields = ( ('Timestamp', 'timestamp', timestamp), ('Latitude', 'lat'), ('Latitude Direction', 'lat_dir'), ('Longitude', 'lon'), ('Longitude Direction', 'lon_dir'), ('GPS Quality Indicator', 'gps_qual', int), ('Number of Satellites in use', 'num_sats'), ('Horizontal Dilution of Precision', 'horizontal_dil'), ('Antenna Alt above sea level (mean)', 'altitude', float), ('Units of altitude (meters)', 'altitude_units'), ('Geoidal Separation', 'geo_sep'), ('Units of Geoidal Separation (meters)', 'geo_sep_units'), ('Age of Differential GPS Data (secs)', 'age_gps_data'), ('Differential Reference Station ID', 'ref_station_id'), ) class GNS(TalkerSentence, LatLonFix): fields = ( ('Timestamp', 'timestamp', timestamp), ('Latitude', 'lat'), ('Latitude Direction', 'lat_dir'), ('Longitude', 'lon'), ('Longitude Direction', 'lon_dir'), ('Mode indicator', 'mode_indicator'), ('Total number of satelites in use', 'num_sats'), ('HDROP', 'hdop'), ('Antenna altitude, meters', 'altitude'), ('Goeidal separation meters', 'geo_sep'), ('Age of diferential data', 'age_gps_data'), ('Differential reference station ID', 'diferential'), ) class BWW(TalkerSentence): """ Bearing, Waypoint to Waypoint """ fields = ( ("Bearing degrees True", "bearing_deg_true"), ("Bearing degrees True Symbol", "bearing_deg_true_sym"), ("Bearing degrees Magnitude", "bearing_deg_mag"), ("Bearing degrees Magnitude Symbol", "bearing_deg_mag_sym"), ("Destination Waypoint ID", "waypoint_id_dest"), ("Origin Waypoint ID", "waypoint_id_orig"), ) class GLL(TalkerSentence, ValidStatusFix, LatLonFix): fields = ( ('Latitude', 'lat'), ('Latitude Direction', 'lat_dir'), ('Longitude', 'lon'), ('Longitude Direction', 'lon_dir'), ('Timestamp', 'timestamp', timestamp), ('Status', 'status'), # contains the 'A' or 'V' flag ("FAA mode indicator", "faa_mode"), ) class GSA(TalkerSentence, ValidGSAFix): fields = ( ('Mode', 'mode'), ('Mode fix type', 'mode_fix_type'), ('SV ID01', 'sv_id01'), ('SV ID02', 'sv_id02'), ('SV ID03', 'sv_id03'), ('SV ID04', 'sv_id04'), ('SV ID05', 'sv_id05'), ('SV ID06', 'sv_id06'), ('SV ID07', 'sv_id07'), ('SV ID08', 'sv_id08'), ('SV ID09', 'sv_id09'), ('SV ID10', 'sv_id10'), ('SV ID11', 'sv_id11'), ('SV ID12', 'sv_id12'), ('PDOP (Dilution of precision)', 'pdop'), ('HDOP (Horizontal DOP)', 'hdop'), ('VDOP (Vertical DOP)', 'vdop'), ) class GST(TalkerSentence): fields = ( ('UTC time of the GGA or GNS fix associated with this sentence.', 'timestamp', timestamp), ('RMS value of the standard deviation of the range inputs to the navigation process. Range inputs include preudoranges & DGNSS corrections.', 'rms', float), ('Standard deviation of semi-major axis of error ellipse (meters)', 'std_dev_major', float), ('Standard deviation of semi-minor axis of error ellipse (meters)', 'std_dev_minor', float), ('Orientation of semi-major axis of error ellipse (degrees from true north)', 'orientation', float), ('Standard deviation of latitude error (meters)', 'std_dev_latitude', float), ('Standard deviation of longitude error (meters)', 'std_dev_longitude', float), ('Standard deviation of altitude error (meters)', 'std_dev_altitude', float), ) class GSV(TalkerSentence): fields = ( ('Number of messages of type in cycle', 'num_messages'), ('Message Number', 'msg_num'), ('Total number of SVs in view', 'num_sv_in_view'), ('SV PRN number 1', 'sv_prn_num_1'), ('Elevation in degrees 1', 'elevation_deg_1'), # 90 max ('Azimuth, deg from true north 1', 'azimuth_1'), # 000 to 159 ('SNR 1', 'snr_1'), # 00-99 dB ('SV PRN number 2', 'sv_prn_num_2'), ('Elevation in degrees 2', 'elevation_deg_2'), # 90 max ('Azimuth, deg from true north 2', 'azimuth_2'), # 000 to 159 ('SNR 2', 'snr_2'), # 00-99 dB ('SV PRN number 3', 'sv_prn_num_3'), ('Elevation in degrees 3', 'elevation_deg_3'), # 90 max ('Azimuth, deg from true north 3', 'azimuth_3'), # 000 to 159 ('SNR 3', 'snr_3'), # 00-99 dB ('SV PRN number 4', 'sv_prn_num_4'), ('Elevation in degrees 4', 'elevation_deg_4'), # 90 max ('Azimuth, deg from true north 4', 'azimuth_4'), # 000 to 159 ('SNR 4', 'snr_4'), ) # 00-99 dB class HDG(TalkerSentence): """ NMEA 0183 standard Heading, Deviation and Variation Format: $HCHDG,<1>,<2>,<3>,<4>,<5>*hh <1> Magnetic sensor heading, degrees, to the nearest 0.1 degree. <2> Magnetic deviation, degrees east or west, to the nearest 0.1 degree. <3> E if field <2> is degrees East W if field <2> is degrees West <4> Magnetic variation, degrees east or west, to the nearest 0.1 degree. <5> E if field <4> is degrees East W if field <4> is degrees West """ fields = ( ("Heading", "heading", Decimal), ("Deviation", "deviation", Decimal), ("Deviation Direction", "dev_dir"), ("Variation", "variation", Decimal), ("Variation Direction", "var_dir"), ) class HDT(TalkerSentence): fields = ( ("Heading", "heading", Decimal), ("True", "hdg_true"), ) class RMA(TalkerSentence): fields = ( ("Data status", "data_status"), ("Latitude", "lat"), ("Latitude Direction", "lat_dir"), ("Longitude", "lon"), ("Longitude Direction", "lon_dir"), ("Not Used 1", "not_used_1"), ("Not Used 2", "not_used_2"), ("Speed over ground", "spd_over_grnd"), # Knots ("Course over ground", "crse_over_grnd"), ("Variation", "variation"), ("Variation Direction", "var_dir"), ) class RMB(TalkerSentence, ValidStatusFix): """ Recommended Minimum Navigation Information """ fields = ( ('Status', 'status'), # contains the 'A' or 'V' flag ("Cross Track Error", "cross_track_error"), # nautical miles, 9.9 max ("Cross Track Error, direction to corrent", "cte_correction_dir"), ("Origin Waypoint ID", "origin_waypoint_id"), ("Destination Waypoint ID", "dest_waypoint_id"), ("Destination Waypoint Latitude", "dest_lat"), ("Destination Waypoint Lat Direction", "dest_lat_dir"), ("Destination Waypoint Longitude", "dest_lon"), ("Destination Waypoint Lon Direction", "dest_lon_dir"), ("Range to Destination", "dest_range"), # Nautical Miles ("True Bearing to Destination", "dest_true_bearing"), ("Velocity Towards Destination", "dest_velocity"), # Knots ("Arrival Alarm", "arrival_alarm"), ) # A = Arrived, V = Not arrived class RMC(TalkerSentence, ValidStatusFix, LatLonFix, DatetimeFix): """ Recommended Minimum Specific GPS/TRANSIT Data """ fields = ( ("Timestamp", "timestamp", timestamp), ('Status', 'status'), # contains the 'A' or 'V' flag ("Latitude", "lat"), ("Latitude Direction", "lat_dir"), ("Longitude", "lon"), ("Longitude Direction", "lon_dir"), ("Speed Over Ground", "spd_over_grnd", float), ("True Course", "true_course", float), ("Datestamp", "datestamp", datestamp), ("Magnetic Variation", "mag_variation"), ("Magnetic Variation Direction", "mag_var_dir"), ) class RTE(TalkerSentence): """ Routes """ fields = ( ("Number of sentences in sequence", "num_in_seq"), ("Sentence Number", "sen_num"), ("Start Type", "start_type"), # The first in the list is either current route or waypoint ("Name or Number of Active Route", "active_route_id"), ) @property def waypoint_list(self): return self.data[4:] @waypoint_list.setter def waypoint_list(self, val): self.data[4:] = val class R00(TalkerSentence): fields = () @property def waypoint_list(self): return self.data[:] @waypoint_list.setter def waypoint_list(self, val): self.data[:] = val class STN(TalkerSentence): """ NOTE: No real data could be found for examples of the actual spec so it is a guess that there may be a checksum on the end """ fields = ( ("Talker ID Number", "talker_id_num"), ) # 00 - 99 class TRF(TalkerSentence): """ Transit Fix Data """ fields = ( ("Timestamp (UTC)", "timestamp", timestamp), ("Date (DD/MM/YY", "date"), ("Latitude", "lat"), ("Latitude Direction", "lat_dir"), ("Longitude", "lon"), ("Longitude Direction", "lon_dir"), ("Elevation Angle", "ele_angle"), ("Number of Iterations", "num_iterations"), ("Number of Doppler Intervals", "num_doppler_intervals"), ("Update Distance", "update_dist"), # Nautical Miles ("Satellite ID", "sat_id"), ) class TXT(TalkerSentence): """ Text Transmission """ fields = ( ("Number of Messages", "num_msg"), ("Message Number", "msg_num"), ("Type of Message", "msg_type"), ("Text", "text"), ) class VBW(TalkerSentence, ValidVBWFix): """ Dual Ground/Water Speed """ fields = ( ("Longitudinal Water Speed", "lon_water_spd", Decimal), # Knots ("Transverse Water Speed", "trans_water_spd", Decimal), # Knots ("Water Speed Data Validity", "data_validity_water_spd"), ("Longitudinal Ground Speed", "lon_grnd_spd", Decimal), # Knots ("Transverse Ground Speed", "trans_grnd_spd", Decimal), # Knots ("Ground Speed Data Validity", "data_validity_grnd_spd"), ) class VTG(TalkerSentence): """ Track Made Good and Ground Speed """ fields = ( ("True Track made good", "true_track", float), ("True Track made good symbol", "true_track_sym"), ("Magnetic Track made good", "mag_track", Decimal), ("Magnetic Track symbol", "mag_track_sym"), ("Speed over ground knots", "spd_over_grnd_kts", Decimal), ("Speed over ground symbol", "spd_over_grnd_kts_sym"), ("Speed over ground kmph", "spd_over_grnd_kmph", float), ("Speed over ground kmph symbol", "spd_over_grnd_kmph_sym"), ("FAA mode indicator", "faa_mode"), ) class WCV(TalkerSentence): """ Waypoint Closure Velocity """ fields = ( ("Velocity", "velocity"), ("Velocity Units", "vel_units"), # Knots ("Waypoint ID", "waypoint_id"), ) class WNC(TalkerSentence): """ Distance, Waypoint to Waypoint """ fields = ( ("Distance, Nautical Miles", "dist_nautical_miles"), ("Distance Nautical Miles Unit", "dist_naut_unit"), ("Distance, Kilometers", "dist_km"), ("Distance, Kilometers Unit", "dist_km_unit"), ("Origin Waypoint ID", "waypoint_origin_id"), ("Destination Waypoint ID", "waypoint_dest_id"), ) class WPL(TalkerSentence): """ Waypoint Location """ fields = ( ("Latitude", "lat"), ("Latitude Direction", "lat_dir"), ("Longitude", "lon"), ("Longitude Direction", "lon_dir"), ("Waypoint ID", "waypoint_id"), ) class XTE(TalkerSentence): """ Cross-Track Error, Measured """ fields = ( ("General Warning Flag", "warning_flag"), ("Lock flag (Not Used)", "lock_flag"), ("Cross Track Error Distance", "cross_track_err_dist"), ("Correction Direction (L or R)", "correction_dir"), ("Distance Units", "dist_units"), ) class ZDA(TalkerSentence): fields = ( ("Timestamp", "timestamp", timestamp), # hhmmss.ss = UTC ("Day", "day", int), # 01 to 31 ("Month", "month", int), # 01 to 12 ("Year", "year", int), # Year = YYYY ("Local Zone Description", "local_zone", int), # 00 to +/- 13 hours ("Local Zone Minutes Description", "local_zone_minutes", int), # same sign as hours ) @property def datestamp(self): return datetime.date(year=self.year, month=self.month, day=self.day) @property def tzinfo(self): return TZInfo(self.local_zone, self.local_zone_minutes) @property def datetime(self): d = datetime.datetime.combine(self.datestamp, self.timestamp) return d.replace(tzinfo=self.tzinfo) # Implemented by Janez Stupar for Visionect class RSA(TalkerSentence): """ Rudder Sensor Angle """ fields = ( ("Starboard rudder sensor", "rsa_starboard", Decimal), ("Starboard rudder sensor status", "rsa_starboard_status"), ("Port rudder sensor", "rsa_port", Decimal), ("Port rudder sensor status", "rsa_port_status"), ) class HSC(TalkerSentence): """ Heading Steering Command """ fields = ( ("Heading", "heading_true", Decimal), ("True", "true"), ("Heading Magnetic", "heading_magnetic", Decimal), ("Magnetic", "magnetic"), ) class MWD(TalkerSentence): """ Wind Direction NMEA 0183 standard Wind Direction and Speed, with respect to north. """ fields = ( ("Wind direction true", "direction_true", Decimal), ("True", "true"), ("Wind direction magnetic", "direction_magnetic", Decimal), ("Magnetic", "magnetic"), ("Wind speed knots", "wind_speed_knots", Decimal), ("Knots", "knots"), ("Wind speed meters/second", "wind_speed_meters", Decimal), ("Wind speed", "meters"), ) class MWV(TalkerSentence, ValidStatusFix): """ Wind Speed and Angle NMEA 0183 standard Wind Speed and Angle, in relation to the vessel's bow/centerline. """ fields = ( ("Wind angle", "wind_angle", Decimal), # in relation to vessel's centerline ("Reference", "reference"), # relative (R)/true(T) ("Wind speed", "wind_speed", Decimal), ("Wind speed units", "wind_speed_units"), # K/M/N ("Status", "status"), ) # DBT - Depth below trasducer, depth referenced to the transducer # Used by simrad devices (f.e. EK500) class DBT(TalkerSentence): """ Depth Below Transducer """ fields = ( ("Depth below surface, feet", "depth_feet", Decimal), ("Feet", "unit_feet"), ("Depth below surface, meters", "depth_meters", Decimal), ("Meters", "unit_meters"), ("Depth below surface, fathoms", "depth_fathoms", Decimal), ("fathoms", "unit_fathoms"), ) class HDM(TalkerSentence): """ Heading, Magnetic """ fields = ( ("Heading degrees", "heading", Decimal), ("Magnetic", "magnetic"), ) class MTW(TalkerSentence): """ Water Temperature """ fields = ( ('Water temperature', 'temperature', Decimal), ('Unit of measurement', 'units'), ) class VHW(TalkerSentence): """ Water Speed and Heading """ fields = ( ('Heading true degrees', 'heading_true', Decimal), ('heading true', 'true'), ('Heading Magnetic degrees', 'heading_magnetic', Decimal), ('Magnetic', 'magnetic'), ('Water speed knots', 'water_speed_knots', Decimal), ('Knots', 'knots'), ('Water speed kilometers', 'water_speed_km', Decimal), ('Kilometers', 'kilometers'), ) class VLW(TalkerSentence): """ Distance Traveled through the Water """ fields = ( ('Water trip distance', 'trip_distance', Decimal), ('Trip distance nautical miles', 'trip_distance_miles'), ('Water trip distance since reset', 'trip_distance_reset', Decimal), ('Trip distance nautical miles since reset', 'trip_distance_reset_miles'), ) # --------------------- Implemented by Joachim Bakke (joabakk)---------------- # # ---------------------------------------------------------------------------- # class ROT(TalkerSentence, ValidStatusFix): """ Rate of Turn """ fields = ( ("Rate of turn", "rate_of_turn"), #- indicates bow turn to port ('Status', 'status'), # contains the 'A' or 'B' flag ) class RPM(TalkerSentence, ValidStatusFix): """ Revolutions """ # 1 2 3 4 5 6 # | | | | | | # $--RPM,a,x,x.x,x.x,A*hh # Field Number: # 1) Sourse, S = Shaft, E = Engine # 2) Engine or shaft number # 3) Speed, Revolutions per minute # 4) Propeller pitch, % of maximum, "-" means astern # 5) Status, A means data is valid # 6) Checksum fields = ( ("Source", "source"), #S = Shaft, E = Engine ("Engine or shaft number", "engine_no", int), ("Speed", "speed", float), #RPM ("Propeller pitch", "pitch"), #- means astern ("Status", "status"), #A means valid ) class VPW(TalkerSentence): """ Speed, Measured Parallel to Wind """ fields = ( ("Speed knots", "speed_kn", float),#- means downwind ("Unit knots", "unit_knots"),#N means knots ("Speed m/s", "speed_ms", float), ("Unit m/s", "unit_ms"),#M means m/s ) # VPW - Speed - Measured Parallel to Wind # 1 2 3 4 5 # | | | | | #$--VPW,x.x,N,x.x,M*hh # Field Number: # 1) Speed, "-" means downwind # 2) N = Knots # 3) Speed, "-" means downwind # 4) M = Meters per second # 5) Checksum class VDR(TalkerSentence): fields = ( ("Degrees True", "deg_t", float), ("TRUE", "true"),#T means true ("Degrees Magnetic", "deg_m", float), ("Magnetic", "magnetic"),#M means magnetic ("Speed of Current", "current", float), ("Unit", "unit_kn"), #N means knots ) # VDR - Set and Drift # 1 2 3 4 5 6 7 # | | | | | | | # $--VDR,x.x,T,x.x,M,x.x,N*hh # Field Number: # 1) Degress True # 2) T = True # 3) Degrees Magnetic # 4) M = Magnetic # 5) Knots (speed of current) # 6) N = Knots # 7) Checksum class VWR(TalkerSentence): fields = ( ("Degrees Rel", "deg_r", float), ("Left/Right", "l_r"),#R means right ("Wind speed kn", "wind_speed_kn", float), ("Knots", "unit_knots"),#N means knots ("Wind Speed m/s", "wind_speed_ms", float), ("m/s", "unit_ms"),#M means m/s ("Wind Speed Km/h", "wind_speed_km", float), ("Knots", "unit_km"), #K means Km ) # TODO # getters/setters that normalize units, # apply L/R sign, and sync all fields # when setting the speed #VWR - Relative Wind Speed and Angle # 1 2 3 4 5 6 7 8 9 # | | | | | | | | | # $--VWR,x.x,a,x.x,N,x.x,M,x.x,K*hh # Field Number: # 1) Wind direction magnitude in degrees # 2) Wind direction Left/Right of bow # 3) Speed # 4) N = Knots # 5) Speed # 6) M = Meters Per Second # 7) Speed # 8) K = Kilometers Per Hour # 9) Checksum Transducer = namedtuple("Transducer", [ "type", "value", "units", "id", ]) class XDR(TalkerSentence): fields = ( ("Transducer type", "type"), ("Transducer data value", "value"), ("Transducer data units", "units"), ("Transducer ID", "id"), ) @property def num_transducers(self): return len(self.data) // 4 def get_transducer(self, i): return Transducer(*self.data[i*4:i*4+4]) # ------------------------- Implemented by Casper Vertregt ------------------- # # ---------------------------------------------------------------------------- # class OSD(TalkerSentence, ValidStatusFix): """ Own Ship Data """ fields = ( ("True Heading", "heading", Decimal), ("Status", "status"), # A / V ("Vessel Course true degrees", "course", Decimal), ("Course True", "course_true"), # T / R (True / Relative) ("Vessel Speed", "speed", Decimal), ("Speed Reference", "speed_ref"), ("Vessel Set true degrees", "set", Decimal), ("Vessel Drift(speed)", "drift", Decimal), ("Speed Units", "speed_unit"), ) class TLL(TalkerSentence, LatLonFix): """ Target Latitude & Longitude """ fields = ( ("Target Number", "target_number", int), ("Target Latitude", "lat"), ("Latitude Direction", "lat_dir"), ("Target Longitude", "lon"), ("Longitude Direction", "lon_dir"), ("Target Name", "target_name"), ("Timestamp (UTC)", "timestamp", timestamp), ("Target Status", "target_status"), ("Reference Target", "reference"), ) class TTM(TalkerSentence): """ Tracked Target Message """ fields = ( ("Target Number", "target_number", int), ("Target Distance", "distance", Decimal), ("Bearing from Own Ship", "bearing", Decimal), ("Bearing Reference", "brg_ref"), # T / R (True / Relative) ("Target Speed", "speed", Decimal), ("Target Course over Ground", "cog", Decimal), ("Course Units", "cog_unit"), # T / R (True / Relative) ("Distance of CPA", "dist_cpa", Decimal), ("Time until CPA", "time_cpa", Decimal), ("Distance Units", "dist_unit"), # K / N / S (Kilometers / Knots / Statute miles) ("Target Name", "name"), ("Target Status", "status"), # L / Q / T (Lost from tracking process / Query - in process of acquisition / Tracking at the present time) ("Target Reference", "reference"), # R, null otherwise ("Timestamp (UTC)", "timestamp", timestamp), ("Acquisition Type", "acquisition"), # A / M (Automatic / Manual) ) # ---------------------------------- Not Yet Implemented --------------------- # # ---------------------------------------------------------------------------- # #class FSI(TalkerSentence): # """ Frequency Set Information # """ # fields = ( # ) #class GLC(TalkerSentence): # """ Geographic Position, Loran-C # """ # fields = ( # ) #class GXA(TalkerSentence): # """ TRANSIT Position # """ # fields = ( # ) #class LCD(TalkerSentence): # """ Loran-C Signal Data # """ # fields = ( # ) #class MTA(TalkerSentence): # """ Air Temperature (to be phased out) # """ # fields = ( # ) #class OLN(TalkerSentence): # """ Omega Lane Numbers # """ # fields = ( # ) #class RSD(TalkerSentence): # """ RADAR System Data # """ # fields = ( # ) #class SFI(TalkerSentence): # """ Scanning Frequency Information # """ # fields = ( # ) #class XTR(TalkerSentence): # """ Cross-Track Error, Dead Reckoning # """ # fields = ( # ) #class ZFO(TalkerSentence): # """ UTC & Time from Origin Waypoint # """ # fields = ( # ) #class ZTG(TalkerSentence): # """ UTC & Time to Destination Waypoint # """ # fields = ( # ) # ---------------------------------------------------------------------------- # # -------------------------- Unknown Formats --------------------------------- # # ---------------------------------------------------------------------------- # #class ASD(TalkerSentence): # """ Auto-pilot system data (Unknown format) # """ # fields = ( # ) # ---------------------------------------------------------------------------- # # -------------------------- Obsolete Formats -------------------------------- # # ---------------------------------------------------------------------------- # #class DCN(TalkerSentence): # """ Decca Position (obsolete) # """ # fields = ( # ) # ------------- Implemented by Rocco De Marco (CNR/ISMAR ITALY) -------------- # # ---------------------------------------------------------------------------- # # DTM - NMEA 0183 standard Datum Reference class DTM(TalkerSentence): fields = ( ('Local datum', 'datum'), ('Subdivision datum', 'subd_datum'), ('Latitude', 'lat'), ('Latitude Direction', 'lat_dir'), ('Longitude', 'lon'), ('Longitude Direction', 'lon_dir'), ('Signed altitude', 'altitude'), ('Datum code', 'datum_code'), ) # Examples: $GPDTM,W84,,0.0,N,0.0,E,0.0,W84*6F # $GPDTM,999,CH95,0.08,N,0.07,E,-47.7,W84*1C # MDA - NMEA 0183 standard Meteorological Composite # Used by Airmar PB 150 weather station class MDA(TalkerSentence): # Example: # $WIMDA,30.2269,I,1.0236,B,17.7,C,,,43.3,,5.0,C,131.5,T,128.6,M,0.8,N,0.4,M*54 fields = ( ('Barometric pressure, inches of mercury', 'b_pressure_inch', Decimal), ('Inches', 'inches'), # I = Inches ('Barometric pressure, bars', 'b_pressure_bar', Decimal), ('Bars', 'bars'), # B = bars ('Air temperature, degrees C', 'air_temp', Decimal), ('Celsius', 'a_celsius'), # C = Celsius ('Water temperature, degrees C', 'water_temp', Decimal), ('Celsius', 'w_celsius'), # C = Celsius ('Relative humidity, percent', 'rel_humidity', Decimal), ('Absolute humidity, percent', 'abs_humidity', Decimal), ('Dew point, degrees C', 'dew_point', Decimal), ('Celsius', 'd_celsius'), # C = Celsius ('Wind direction true', 'direction_true', Decimal), ('True', 'true'), # T = True ('Wind direction magnetic', 'direction_magnetic', Decimal), ('Magnetic', 'magnetic'), # M = Magnetic ('Wind speed knots', 'wind_speed_knots', Decimal), ('Knots', 'knots'), # N = Knots ('Wind speed meters/second', 'wind_speed_meters', Decimal), ('Meters', 'meters'), # M = Meters/second ) # VWT - NMEA 0183 True wind angle in relation to the vessel's heading, and true wind # speed referenced to the water. class VWT(TalkerSentence): fields = ( ('Wind angle relative to the vessel', 'wind_angle_vessel', Decimal), ('Direction, L=Left, R=Right, relative to the vessel head', 'direction'), ('Wind speed knots', 'wind_speed_knots', Decimal), ('Knots', 'knots'), # N = Knots ('Wind speed meters/second', 'wind_speed_meters', Decimal), ('Meters', 'meters'), # M = Meters/second ('Wind speed km/h', 'wind_speed_km', Decimal), ('Km', 'km'), # K = km/h ) # DBS - Depth below surface # Used by simrad devices (f.e. EK500) # Deprecated and replaced by DPT class DBS(TalkerSentence): fields = ( ('Depth below surface, feet', 'depth_feet', Decimal), ('Feets', 'feets'), ('Depth below surface, meters', 'depth_meter', Decimal), ('Meters', 'meters'), ('Depth below surface, fathoms', 'depth_ fathoms', Decimal), ('Fathoms', 'fathoms'), ) # DPT - water depth relative to the transducer and offset of the measuring # transducer # Used by simrad devices (f.e. EK500) class DPT(TalkerSentence): fields = ( ('Water depth, in meters', 'depth', Decimal), ('Offset from the trasducer, in meters', 'offset', Decimal), ('Maximum range scale in use', 'range', Decimal), ) #GBS - GPS Satellite Fault Detection #used by devices such as [MX521] [NV08C-CSM] #description retreived from: "https://www.xj3.nl/download/99/NMEA.txt" class GBS(TalkerSentence): fields = ( ('Timestamp','timestamp', timestamp), ('Expected error in latitude', 'lat_err'), ('Expected error in longitude', 'lon_err'), ('Expected error in altitude', 'alt_err'), ('PRN of most likely failed satellite','sat_prn_num_f'), ('Probability of missed detection for most likely failed satellite','pro_miss', Decimal), ('Estimate of bias in meters on most likely failed satellite','est_bias'), ('Standard deviation of bias estimate','est_bias_dev'), ) # STALK Message - # Used by gadgetpool Seatalk to nmea devices class ALK(TalkerSentence,SeaTalk): fields = ( ("Command", "cmd"), ("Data Byte 1", "data_byte1"), ("Data Byte 2", "data_byte2"), ("Data Byte 3", "data_byte3"), ("Data Byte 4", "data_byte4"), ("Data Byte 5", "data_byte5"), ("Data Byte 6", "data_byte6"), ("Data Byte 7", "data_byte7"), ("Data Byte 8", "data_byte8"), ("Data Byte 9", "data_byte9") )pynmea2-1.12.0/pynmea2/types/__init__.py0000660000175100017510000000013212551253255017417 0ustar tomtom00000000000000# pylint: disable=wildcard-import from .talker import * from .proprietary import * pynmea2-1.12.0/pynmea2/seatalk_utils.py0000664000175100017510000000160613152023630017361 0ustar tomtom00000000000000# pylint: disable=invalid-name class SeaTalk(object): '''Mixin to add Seatalk functionality. Based on Thomas knauf's work http://www.thomasknauf.de/seatalk.htm''' byte_to_command = { '00': 'Depth below transducer', '01': 'Equipment ID', '05': 'Engine RPM and PITCH', '10': 'Apparent Wind Angle', '11': 'Apparent Wind Speed', '20': 'Speed through water', '50': 'LAT position', '51': 'LON position', '52': 'Speed over Ground', '53': 'Course over Ground', '82': 'Target waypoint name', '84': 'Compass heading Autopilot course and Rudder position', '9C': 'Compass heading and Rudder position' } # pylint: disable=no-member @property def command_name(self): '''Get seatalk command's meaning''' return self.byte_to_command.get(self.cmd, 'Unknown Command') pynmea2-1.12.0/pynmea2/_version.py0000664000175100017510000000003013170047266016342 0ustar tomtom00000000000000__version__ = '1.12.0' pynmea2-1.12.0/pynmea2/nmea.py0000664000175100017510000001640213170047266015450 0ustar tomtom00000000000000import re import operator from functools import reduce class ParseError(ValueError): def __init__(self, message, data): super(ParseError, self).__init__((message, data)) class SentenceTypeError(ParseError): pass class ChecksumError(ParseError): pass class NMEASentenceType(type): sentence_types = {} def __init__(cls, name, bases, dct): type.__init__(cls, name, bases, dct) base = bases[0] if base is object: return base.sentence_types[name] = cls cls.name_to_idx = dict((f[1], i) for i, f in enumerate(cls.fields)) # http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ NMEASentenceBase = NMEASentenceType('NMEASentenceBase', (object,), {}) class NMEASentence(NMEASentenceBase): ''' Base NMEA Sentence Parses and generates NMEA strings Examples: >>> s = NMEASentence.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D") >>> print(s) ''' sentence_re = re.compile(r''' # start of string, optional whitespace, optional '$' ^\s*\$? # message (from '$' or start to checksum or end, non-inclusve) (?P # sentence type identifier (?P # proprietary sentence (P\w{3})| # query sentence, ie: 'CCGPQ,GGA' # NOTE: this should have no data (\w{2}\w{2}Q,\w{3})| # taker sentence, ie: 'GPGGA' (\w{2}\w{3},) ) # rest of message (?P[^*]*) ) # checksum: *HH (?:[*](?P[A-F0-9]{2}))? # optional trailing whitespace \s*[\r\n]*$ ''', re.X | re.IGNORECASE) talker_re = \ re.compile(r'^(?P\w{2})(?P\w{3}),$') query_re = \ re.compile(r'^(?P\w{2})(?P\w{2})Q,(?P\w{3})$') proprietary_re = \ re.compile(r'^P(?P\w{3})$') name_to_idx = {} fields = () @staticmethod def checksum(nmea_str): return reduce(operator.xor, map(ord, nmea_str), 0) @staticmethod def parse(line, check=False): ''' parse(line) Parses a string representing a NMEA 0183 sentence, and returns a NMEASentence object Raises ValueError if the string could not be parsed, or if the checksum did not match. ''' match = NMEASentence.sentence_re.match(line) if not match: raise ParseError('could not parse data', line) # pylint: disable=bad-whitespace nmea_str = match.group('nmea_str') data_str = match.group('data') checksum = match.group('checksum') sentence_type = match.group('sentence_type').upper() data = data_str.split(',') if checksum: cs1 = int(checksum, 16) cs2 = NMEASentence.checksum(nmea_str) if cs1 != cs2: raise ChecksumError( 'checksum does not match: %02X != %02X' % (cs1, cs2), data) elif check: raise ChecksumError( 'strict checking requested but checksum missing', data) talker_match = NMEASentence.talker_re.match(sentence_type) if talker_match: talker = talker_match.group('talker') sentence = talker_match.group('sentence') cls = TalkerSentence.sentence_types.get(sentence) if not cls: # TODO instantiate base type instead of fail raise SentenceTypeError( 'Unknown sentence type %s' % sentence_type, line) return cls(talker, sentence, data) query_match = NMEASentence.query_re.match(sentence_type) if query_match and not data_str: talker = query_match.group('talker') listener = query_match.group('listener') sentence = query_match.group('sentence') return QuerySentence(talker, listener, sentence) proprietary_match = NMEASentence.proprietary_re.match(sentence_type) if proprietary_match: manufacturer = proprietary_match.group('manufacturer') cls = ProprietarySentence.sentence_types.get(manufacturer, ProprietarySentence) return cls(manufacturer, data) raise ParseError( 'could not parse sentence type: %r' % sentence_type, line) def __getattr__(self, name): #pylint: disable=invalid-name t = type(self) try: i = t.name_to_idx[name] except KeyError: raise AttributeError(name) f = t.fields[i] if i < len(self.data): v = self.data[i] else: v = '' if len(f) >= 3: if v == '': return None try: return f[2](v) except: return v else: return v def __setattr__(self, name, value): #pylint: disable=invalid-name t = type(self) if name not in t.name_to_idx: return object.__setattr__(self, name, value) i = t.name_to_idx[name] self.data[i] = str(value) def __repr__(self): #pylint: disable=invalid-name r = [] d = [] t = type(self) for i, v in enumerate(self.data): if i >= len(t.fields): d.append(v) continue name = t.fields[i][1] r.append('%s=%r' % (name, getattr(self, name))) return '<%s(%s)%s>' % ( type(self).__name__, ', '.join(r), d and ' data=%r' % d or '' ) def identifier(self): raise NotImplementedError def render(self, checksum=True, dollar=True, newline=False): res = self.identifier() + ','.join(self.data) if checksum: res += '*%02X' % NMEASentence.checksum(res) if dollar: res = '$' + res if newline: res += (newline is True) and '\r\n' or newline return res def __str__(self): return self.render() class TalkerSentence(NMEASentence): sentence_types = {} def __init__(self, talker, sentence_type, data): self.talker = talker self.sentence_type = sentence_type self.data = list(data) def identifier(self): return '%s%s,' % (self.talker, self.sentence_type) class QuerySentence(NMEASentence): sentence_types = {} def __init__(self, talker, listener, sentence_type): self.talker = talker self.listener = listener self.sentence_type = sentence_type self.data = [] def identifier(self): return '%s%sQ,%s,' % (self.talker, self.listener, self.sentence_type) class ProprietarySentence(NMEASentence): sentence_types = {} def __init__(self, manufacturer, data): self.manufacturer = manufacturer self.data = list(data) def identifier(self): return 'P%s' % (self.manufacturer) pynmea2-1.12.0/pynmea2/__init__.py0000664000175100017510000000065313023406655016266 0ustar tomtom00000000000000# pylint: disable=missing-docstring # pylint: disable=wildcard-import # pylint: disable=invalid-name from ._version import __version__ version = __version__ from .nmea import NMEASentence, ProprietarySentence, QuerySentence from .nmea import ChecksumError, ParseError, SentenceTypeError parse = NMEASentence.parse from .types import * from .stream import NMEAStreamReader from .nmea_file import NMEAFile pynmea2-1.12.0/test/0000775000175100017510000000000013170047315013552 5ustar tomtom00000000000000pynmea2-1.12.0/test/test_pynmea.py0000664000175100017510000001055713170047266016471 0ustar tomtom00000000000000import pytest import pynmea2 data = "$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D" def test_version(): version = '1.12.0' assert pynmea2.version == version assert pynmea2.__version__ == version def test_sentence(): msg = pynmea2.parse(data) assert msg.talker == 'GP' assert msg.sentence_type == 'GGA' assert str(msg) == data def test_checksum(): d = data[:-2] + '00' with pytest.raises(pynmea2.ChecksumError): msg = pynmea2.parse(d) def test_attribute(): msg = pynmea2.parse(data) with pytest.raises(AttributeError): msg.foobar def test_fail(): with pytest.raises(pynmea2.ParseError): pynmea2.parse('FOOBAR') with pytest.raises(pynmea2.SentenceTypeError): pynmea2.parse('$GPABC,1,2,3') def test_mixin(): msg = pynmea2.parse(data) assert msg.latitude == -19.484083333333334 assert msg.longitude == 24.175100000000000 assert msg.latitude_minutes == 29.045000000000073 assert msg.longitude_minutes == 10.506000000000085 assert msg.latitude_seconds == 2.6999999999970896 assert msg.longitude_seconds == 30.360000000000582 def test_missing(): msg = pynmea2.parse("$GPVTG,108.53,T,,M,0.04,N,0.07,K,A*31") assert msg.mag_track == None def test_missing_2(): # $GPGSV,3,1,09,12,28,063,33,14,63,000,32,22,68,150,26,25,40,109,23*7B # $GPGSV,3,2,09,31,42,227,19,32,17,313,20,01,09,316,,11,08,292,*73 # $GPGSV,3,3,09,24,03,046,*47 msg = pynmea2.parse('$GPGSV,3,3,09,24,03,046,*47') assert msg.snr_4 == '' def test_missing_3(): data = '$GPVTG,,T,,M,0.00,N*1B' msg = pynmea2.parse(data) assert None == msg.spd_over_grnd_kmph assert msg.render() == data def test_missing_4(): data = '$GPVTG,,T,,M,0.00,N*1B' msg = pynmea2.parse(data) assert None == msg.spd_over_grnd_kmph assert msg.render() == data def test_dollar(): data = 'GPGSV,3,3,09,24,03,046,*47\r\n' msg = pynmea2.parse(data) assert msg.render(dollar=False, newline=True) == data def test_whitespace(): data = ' GPGSV,3,3,09,24,03,046,*47 \r\n ' msg = pynmea2.parse(data) assert msg.render(dollar=False) == data.strip() def test_nmea_util(): assert pynmea2.nmea_utils.dm_to_sd('0') == 0. assert pynmea2.nmea_utils.dm_to_sd('12108.1') == 121.135 def test_missing_latlon(): data = '$GPGGA,201716.684,,,,,0,00,,,M,0.0,M,,0000*5F' msg = pynmea2.parse(data) print(msg) assert msg.latitude == 0. def test_query(): data = 'CCGPQ,GGA' msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.QuerySentence) assert msg.talker == 'CC' assert msg.listener == 'GP' assert msg.sentence_type == 'GGA' def test_slash(): with pytest.raises(pynmea2.nmea.ParseError): msg = pynmea2.parse('$GPGSV,3,3,09,24,03,046,*47\\') def test_timestamp(): t = pynmea2.nmea_utils.timestamp("115919") assert t.hour == 11 assert t.minute == 59 assert t.second == 19 assert t.microsecond == 0 assert pynmea2.nmea_utils.timestamp('115919.1' ).microsecond == 100000 assert pynmea2.nmea_utils.timestamp('115919.12' ).microsecond == 120000 assert pynmea2.nmea_utils.timestamp('115919.123' ).microsecond == 123000 assert pynmea2.nmea_utils.timestamp('115919.1234' ).microsecond == 123400 assert pynmea2.nmea_utils.timestamp('115919.12345' ).microsecond == 123450 assert pynmea2.nmea_utils.timestamp('115919.123456' ).microsecond == 123456 assert pynmea2.nmea_utils.timestamp('115919.1234567').microsecond == 123456 def test_corrupt_message(): # data is corrupt starting here ------------------------------v data = '$GPRMC,172142.00,A,4805.30256324,N,11629.09084774,W,0.D' # fails with strict parsing with pytest.raises(pynmea2.ChecksumError): msg = pynmea2.parse(data, check=True) # lazy parsing succeeds msg = pynmea2.parse(data, check=False) assert isinstance(msg, pynmea2.types.RMC) # corrupt data assert msg.spd_over_grnd == '0.D' # missing data assert msg.true_course == None # renders unchanged assert msg.render(checksum=False) == data # # ^o^ # |\ ship it! # | \ / # /| \ # __/_|___\_ # ~~\________/~~ # ~~~~~~~~~~~~ pynmea2-1.12.0/test/test_stream.py0000664000175100017510000000323013023406655016457 0ustar tomtom00000000000000import pytest try: from StringIO import StringIO except ImportError: from io import StringIO import pynmea2 DATA = "$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D\n" def test_stream(): sr = pynmea2.NMEAStreamReader() assert len(list(sr.next(''))) == 0 assert len(list(sr.next(DATA))) == 1 assert len(list(sr.next(DATA))) == 1 sr = pynmea2.NMEAStreamReader() assert len(list(sr.next(DATA))) == 1 assert len(list(sr.next(DATA[:10]))) == 0 assert len(list(sr.next(DATA[10:]))) == 1 sr = pynmea2.NMEAStreamReader() assert list(sr.next()) == [] f = StringIO(DATA * 2) sr = pynmea2.NMEAStreamReader(f) assert len(list(sr.next())) == 1 assert len(list(sr.next())) == 1 assert len(list(sr.next())) == 0 def test_raise_errors(): sr = pynmea2.NMEAStreamReader(errors='raise') assert list(sr.next('foobar')) == [] with pytest.raises(pynmea2.ParseError): assert list(sr.next('foo\n')) def test_yield_errors(): sr = pynmea2.NMEAStreamReader(errors='yield') assert list(sr.next('foobar')) == [] data = list(sr.next('foo\n' + DATA)) assert len(data) == 2 assert isinstance(data[0], pynmea2.ParseError) assert isinstance(data[1], pynmea2.GGA) def test_ignore_errors(): sr = pynmea2.NMEAStreamReader(errors='ignore') assert list(sr.next('foobar')) == [] data = list(sr.next('foo\n' + DATA)) assert len(data) == 1 assert isinstance(data[0], pynmea2.GGA) def test_bad_error_value(): with pytest.raises(ValueError): sr = pynmea2.NMEAStreamReader(errors='bad') pynmea2-1.12.0/test/test_sxn.py0000664000175100017510000000123313170047266015777 0ustar tomtom00000000000000import pynmea2 def test_sxn20(): data = '$PSXN,20,0,0,0,0*3B' msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.types.sxn.SXN20) assert msg.message_type == 20 assert msg.horiz_qual == 0 assert msg.hgt_qual == 0 assert msg.head_qual == 0 assert msg.rp_qual == 0 assert msg.render() == data def test_sxn23(): data = '$PSXN,23,0.30,-0.97,298.57,0.13*1B' msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.types.sxn.SXN23) assert msg.message_type == 23 assert msg.roll == 0.30 assert msg.pitch == -0.97 assert msg.head == 298.57 assert msg.heave == 0.13 assert msg.render() == data pynmea2-1.12.0/test/test_ash.py0000664000175100017510000000260213170047266015743 0ustar tomtom00000000000000import datetime import pynmea2 def test_ashrltn(): data = '$PASHR,LTN,3*3D' msg = pynmea2.parse(data) assert type(msg) == pynmea2.ash.ASHRLTN assert msg.manufacturer == 'ASH' assert msg.subtype == 'LTN' assert msg.latency == 3 assert msg.render() == data def test_ashratt(): data = '$PASHR,130533.620,0.311,T,-80.467,-1.395,,0.066,0.067,0.215,2,3*0B' msg = pynmea2.parse(data) assert type(msg) == pynmea2.ash.ASHRATT assert msg.data == ['R', '130533.620', '0.311', 'T', '-80.467', '-1.395', '', '0.066', '0.067', '0.215', '2', '3'] assert msg.manufacturer == 'ASH' assert msg.timestamp == datetime.time(13, 5, 33, 620000) assert msg.true_heading == 0.311 assert msg.is_true_heading == 'T' assert msg.roll == -80.467 assert msg.pitch == -1.395 assert msg.roll_accuracy == 0.066 assert msg.pitch_accuracy == 0.067 assert msg.heading_accuracy == 0.215 assert msg.aiding_status == 2 assert msg.imu_status == 3 assert msg.render() == data def test_ash_undefined(): ''' Test that non-ATT messages still fall back to the generic ASH type ''' data = '$PASHR,XYZ,123' msg = pynmea2.parse(data) assert type(msg) == pynmea2.ash.ASH assert msg.manufacturer == 'ASH' # assert msg.sentence_type == 'XYZ' assert msg.data == ['R', 'XYZ', '123'] assert msg.render(checksum=False) == data pynmea2-1.12.0/test/test_types.py0000664000175100017510000001546313152023630016332 0ustar tomtom00000000000000import pytest import pynmea2 import datetime from decimal import Decimal def test_GGA(): data = "$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D" msg = pynmea2.parse(data) assert msg.talker == 'GP' assert msg.sentence_type == 'GGA' assert isinstance(msg, pynmea2.GGA) # Timestamp assert msg.timestamp == datetime.time(18, 43, 53, 70000) # Latitude assert msg.lat == '1929.045' # Latitude Direction assert msg.lat_dir == 'S' # Longitude assert msg.lon == '02410.506' # Longitude Direction assert msg.lon_dir == 'E' # GPS Quality Indicator assert msg.gps_qual == 1 # Number of Satellites in use assert msg.num_sats == '04' # Horizontal Dilution of Precision assert msg.horizontal_dil == '2.6' # Antenna Alt above sea level (mean) assert msg.altitude == 100.0 # Units of altitude (meters) assert msg.altitude_units == 'M' # Geoidal Separation assert msg.geo_sep == '-33.9' # Units of Geoidal Separation (meters) assert msg.geo_sep_units == 'M' # Age of Differential GPS Data (secs) assert msg.age_gps_data == '' # Differential Reference Station ID assert msg.ref_station_id == '0000' assert msg.is_valid == True msg.altitude = 200.0 assert str(msg) == "$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,200.0,M,-33.9,M,,0000*5E" def test_RTE(): data = "$GPRTE,2,1,c,0,PBRCPK,PBRTO,PTELGR,PPLAND,PYAMBU,PPFAIR,PWARRN,PMORTL,PLISMR*73" msg = pynmea2.parse(data) assert msg.talker == 'GP' assert msg.sentence_type == 'RTE' assert "2" == msg.num_in_seq assert "1" == msg.sen_num assert "c" == msg.start_type assert "0" == msg.active_route_id assert msg.waypoint_list == [ "PBRCPK", "PBRTO", "PTELGR", "PPLAND", "PYAMBU", "PPFAIR", "PWARRN", "PMORTL", "PLISMR"] msg.waypoint_list = ['ABC', 'DEF'] assert str(msg) == "$GPRTE,2,1,c,0,ABC,DEF*03" def test_R00(): data = "$GPR00,A,B,C*29" msg = pynmea2.parse(data) assert msg.talker == 'GP' assert msg.sentence_type == 'R00' assert msg.waypoint_list == ['A', 'B', 'C'] msg.waypoint_list = ['ABC', 'DEF'] assert str(msg) == "$GPR00,ABC,DEF*42" def test_MWV(): data = "$IIMWV,271.0,R,000.2,N,A*3B" msg = pynmea2.parse(data) assert msg.talker == 'II' assert msg.sentence_type == 'MWV' # Wind angle in degrees assert msg.wind_angle == Decimal('271.0') # Reference type assert msg.reference == 'R' # Wind speed assert msg.wind_speed == Decimal('0.2') # Wind speed units assert msg.wind_speed_units == 'N' # Device status assert msg.status == 'A' assert msg.render() == data def test_GST(): data = "$GPGST,172814.0,0.006,0.023,0.020,273.6,0.023,0.020,0.031*6A" msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.GST) assert msg.timestamp == datetime.time(hour=17, minute=28, second=14) assert msg.rms == 0.006 assert msg.std_dev_major == 0.023 assert msg.std_dev_minor == 0.020 assert msg.orientation == 273.6 assert msg.std_dev_latitude == 0.023 assert msg.std_dev_longitude == 0.020 assert msg.std_dev_altitude == 0.031 assert msg.render() == data def test_RMC(): data = '''$GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*68''' msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.RMC) assert msg.timestamp == datetime.time(hour=22, minute=54, second=46) assert msg.datestamp == datetime.date(1994, 11, 19) assert msg.latitude == 49.274166666666666 assert msg.longitude == -123.18533333333333 assert msg.datetime == datetime.datetime(1994, 11, 19, 22, 54, 46) assert msg.is_valid == True assert msg.render() == data def test_TXT(): data = '$GNTXT,01,01,02,ROM BASE 2.01 (75331) Oct 29 2013 13:28:17*44' msg = pynmea2.parse(data) assert type(msg) == pynmea2.talker.TXT assert msg.text == 'ROM BASE 2.01 (75331) Oct 29 2013 13:28:17' def test_ZDA(): data = '''$GPZDA,010203.05,06,07,2008,-08,30''' msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.ZDA) assert msg.timestamp == datetime.time(hour=1, minute=2, second=3, microsecond=50000) assert msg.day == 6 assert msg.month == 7 assert msg.year == 2008 assert msg.local_zone == -8 assert msg.local_zone_minutes == 30 assert msg.datestamp == datetime.date(2008, 7, 6) assert msg.datetime == datetime.datetime(2008, 7, 6, 1, 2, 3, 50000, msg.tzinfo) def test_VPW(): data = "$XXVPW,1.2,N,3.4,M" msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.VPW) assert msg.talker == 'XX' assert msg.speed_kn == 1.2 assert msg.unit_knots == 'N' assert msg.speed_ms == 3.4 assert msg.unit_ms == 'M' def test_BOD(): data = "XXBOD,045.,T,023.,M,DEST,START" msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.BOD) assert msg.talker == 'XX' def test_XDR(): data = "$YXXDR,A,-64.437,M,N,A,054.454,D,E,C,17.09,C,T-N1052*46" msg = pynmea2.parse(data) assert isinstance(msg, pynmea2.XDR) assert msg.talker == 'YX' assert msg.type == 'A' assert msg.value == '-64.437' assert msg.units == 'M' assert msg.id == 'N' assert msg.num_transducers == 3 t0 = msg.get_transducer(0) assert t0.type == 'A' assert t0.value == '-64.437' assert t0.units == 'M' assert t0.id == 'N' t1 = msg.get_transducer(1) assert t1.type == 'A' assert t1.value == '054.454' assert t1.units == 'D' assert t1.id == 'E' t2 = msg.get_transducer(2) assert t2.type == 'C' assert t2.value == '17.09' assert t2.units == 'C' assert t2.id == 'T-N1052' def test_GLL(): data = "$GPGLL,4916.45,N,12311.12,W,225444,A,*1D" msg = pynmea2.parse(data) assert msg.is_valid == True assert msg.render() == data def test_GSA(): data = "$GPGSA,A,3,02,,,07,,09,24,26,,,,,1.6,1.6,1.0*3D" msg = pynmea2.parse(data) assert msg.is_valid == True assert msg.render() == data def test_VBW(): data = "XXVBW,1.2,3.4,A,5.6,7.8,A" msg = pynmea2.parse(data) assert msg.is_valid == True assert msg.render(checksum=False, dollar=False) == data def test_STALK(): data = "$STALK,9C,C1,2A,E5*4A" msg = pynmea2.parse(data) assert msg.render() == data assert msg.command_name == 'Compass heading and Rudder position' def test_STALK_unidentified_command(): data = "$STALK,AA,C1,2A,E5*30" msg = pynmea2.parse(data) assert msg.render() == data assert msg.command_name == 'Unknown Command' pynmea2-1.12.0/test/test_file.py0000664000175100017510000000314013023406655016103 0ustar tomtom00000000000000try: from StringIO import StringIO except ImportError: from io import StringIO import pynmea2 TEST_DATA = """$GPRMC,181031.576,V,3926.276,N,07739.361,W,99.7,18.30,250915,,E*79 $GPGGA,181032.576,3926.276,N,07739.361,W,0,00,,,M,,M,,*5F $GPGLL,3926.276,N,07739.361,W,181033.576,V*3E $GPRMC,181034.576,V,3949.797,N,07809.854,W,18.8,34.66,250915,,E*75 $GPGGA,181035.576,3949.797,N,07809.854,W,0,00,,,M,,M,,*5A $GPGLL,3949.797,N,07809.854,W,181036.576,V*39 $GPRMC,181037.576,V,4040.018,N,07808.022,W,32.9,16.43,250915,,E*77 $GPGGA,181038.576,4040.018,N,07808.022,W,0,00,,,M,,M,,*58 $GPGLL,4040.018,N,07808.022,W,181039.576,V*39 $GPRMC,181040.576,V,4133.618,N,07725.034,W,96.8,44.47,250915,,E*7F""" def test_file(): nmeafile = pynmea2.NMEAFile(StringIO(TEST_DATA)) nmea_strings = nmeafile.read() assert len(nmea_strings) == 10 assert all([isinstance(s, pynmea2.NMEASentence) for s in nmea_strings]) del nmeafile with pynmea2.NMEAFile(StringIO(TEST_DATA)) as _f: nmea_strings = [_f.readline() for i in range(10)] assert len(nmea_strings) == 10 assert all([isinstance(s, pynmea2.NMEASentence) for s in nmea_strings]) with pynmea2.NMEAFile(StringIO(TEST_DATA)) as _f: nmea_strings = [s for s in _f] assert len(nmea_strings) == 10 assert all([isinstance(s, pynmea2.NMEASentence) for s in nmea_strings]) with pynmea2.NMEAFile(StringIO(TEST_DATA)) as _f: nmea_strings = [_f.next() for i in range(10)] assert len(nmea_strings) == 10 assert all([isinstance(s, pynmea2.NMEASentence) for s in nmea_strings]) if __name__ == '__main__': test_file() pynmea2-1.12.0/test/test_rdi.py0000664000175100017510000000051513170047266015747 0ustar tomtom00000000000000import pynmea2 def test_rdid(): data = '$PRDID,-1.31,7.81,47.31*68' msg = pynmea2.parse(data) assert type(msg) == pynmea2.rdi.RDID assert msg.manufacturer == 'RDI' assert msg.subtype == 'D' assert msg.pitch == -1.31 assert msg.roll == 7.81 assert msg.heading == 47.31 assert msg.render() == data pynmea2-1.12.0/test/test_proprietary.py0000664000175100017510000001340013170047266017546 0ustar tomtom00000000000000import pynmea2 import datetime def test_proprietary_1(): # A sample proprietary sentence from a LCJ Capteurs # anemometer. data = "$PLCJ,5F01,66FC,AA,9390,6373" msg = pynmea2.parse(data) assert msg.manufacturer == "LCJ" assert msg.data == ['','5F01','66FC','AA','9390','6373'] assert msg.render(checksum=False) == data def test_proprietary_2(): # A sample proprietary sentence from a LCJ Capteurs anemometer. # Note: This sample is the main reason why we can't assume # anything about the content of the proprietary sentences # due to the lack of a comma after the manufacturer ID and # extra comma at the end. data = "$PLCJE81B8,64A0,2800,2162,0E," msg = pynmea2.parse(data) assert msg.manufacturer == 'LCJ' assert msg.data == ['E81B8', '64A0', '2800', '2162', '0E', ''] assert repr(msg) == "" assert msg.render(checksum=False) == data def test_proprietary_3(): # A sample proprietary sentence from a Magellan device # (via ). data = "$PMGNST,02.12,3,T,534,05.0,+03327,00*40" msg = pynmea2.parse(data) assert msg.manufacturer == 'MGN' assert msg.data == ['ST','02.12','3','T','534','05.0','+03327','00'] assert msg.render() == data def test_extra_comma(): # extra comma after name data = "$PTNL,AVR,212604.30,+52.1800,Yaw,,,-0.0807,Roll,12.579,3,1.4,16*21" msg = pynmea2.parse(data) assert msg.manufacturer == 'TNL' assert msg.data == ['', 'AVR','212604.30','+52.1800','Yaw','','','-0.0807','Roll','12.579','3','1.4','16'] assert msg.render() == data def test_proprietary_type(): class ABC(pynmea2.ProprietarySentence): fields = ( ('Empty', '_'), ('First', 'a'), ('Second', 'b'), ) data = '$PABC,1,2*13' msg = pynmea2.parse(data) assert isinstance(msg, ABC) assert msg.manufacturer == 'ABC' assert msg.a == '1' assert msg.b == '2' assert repr(msg) == "" assert str(msg) == data def test_proprietary_with_comma(): # class with no extra comma class TNLDG(pynmea2.tnl.TNL): fields = () # raise Exception(TNL.sentence_types) # raise Exception(pynmea2.ProprietarySentence.sentence_types) data = "$PTNLDG,44.0,33.0,287.0,100,0,4,1,0,,,*3E" msg = pynmea2.parse(data) assert isinstance(msg, TNLDG) assert msg.data == ['DG', '44.0', '33.0', '287.0', '100', '0', '4', '1', '0', '', '', ''] assert str(msg) == data # type with extra comma data = '$PTNL,PJT,NAD83(Conus),CaliforniaZone 4 0404*51' msg = pynmea2.parse(data) assert type(msg) == pynmea2.tnl.TNLPJT assert msg.manufacturer == 'TNL' assert msg.sentence_type == 'PJT' assert msg.coord_name == 'NAD83(Conus)' assert msg.project_name == 'CaliforniaZone 4 0404' assert str(msg) == data def test_srf(): # implemented sentence data = '$PSRF100,0,1200,8,1,1' msg = pynmea2.parse(data) assert type(msg) == pynmea2.srf.SRF100 assert msg.sentence_type == 'SRF100' assert msg.protocol == '0' assert msg.baud == '1200' assert msg.databits == '8' assert msg.stopbits == '1' assert msg.parity == '1' # unimplemented sentence data = '$PSRF999,0,1200,8,1,1' msg = pynmea2.parse(data) assert type(msg) == pynmea2.srf.SRF def test_grm(): data = ' $PGRME,15.0,M,45.0,M,25.0,M*1C' msg = pynmea2.parse(data) assert type(msg) == pynmea2.grm.GRME assert msg.sentence_type == 'GRME' assert msg.hpe == 15.0 assert msg.hpe_unit == 'M' assert msg.vpe == 45.0 assert msg.vpe_unit == 'M' assert msg.osepe == 25.0 assert msg.osepe_unit == 'M' def test_tnl(): data = '$PTNL,BPQ,224445.06,021207,3723.09383914,N,12200.32620132,W,EHT-5.923,M,5*60' msg = pynmea2.parse(data) assert type(msg) == pynmea2.tnl.TNLBPQ assert msg.datestamp == datetime.date(2007,12,2) assert msg.latitude == 37.384897319 assert msg.longitude == -122.00543668866666 def test_ubx00(): data = '$PUBX,00,074440.00,4703.74203,N,00736.82976,E,576.991,D3,2.0,2.0,0.091,0.00,-0.032,,0.76,1.05,0.65,14,0,0*70' msg = pynmea2.parse(data) assert type(msg) == pynmea2.ubx.UBX00 assert msg.timestamp == datetime.time(7, 44, 40) assert msg.latitude == 47.06236716666667 assert msg.lat_dir == 'N' def test_ubx03(): data = '$PUBX,03,20,3,e,281,72,36,062,5,e,034,10,23,000,8,U,328,11,44,064,9,-,323,-2,,000,13,-,341,01,,000,16,U,307,45,49,064,18,e,144,18,,000,21,U,150,74,35,037,25,e,134,06,,000,27,U,271,20,52,064,29,U,074,36,36,063,31,U,209,26,37,040,120,e,210,31,,000,126,-,157,33,,000,66,U,036,19,34,015,67,e,090,20,22,000,68,-,136,00,,000,73,e,273,60,47,064,74,U,330,24,44,064,80,U,193,36,36,023*33' msg = pynmea2.parse(data) assert type(msg) == pynmea2.ubx.UBX03 assert msg.num_sv == 20 def test_ubx04(): data = '$PUBX,04,073824.00,131014,113903.99,1814,16,495176,342.504,21*18' msg = pynmea2.parse(data) assert type(msg) == pynmea2.ubx.UBX04 assert msg.date == datetime.date(2014, 10, 13) assert msg.time == datetime.time(7, 38, 24) assert msg.clk_bias == 495176 def test_create(): sentence = pynmea2.srf.SRF100('SRF', [ '100', '%d' % 1, '%d' % 9600, '%d' % 7, '%d' % 1, '%d' % 0]) data = sentence.render(checksum=True, dollar=True, newline=False) assert data == '$PSRF100,1,9600,7,1,0*02' def test_unknown_sentence(): data = 'PZZZABC,1,2,3' msg = pynmea2.parse(data) assert type(msg) == pynmea2.ProprietarySentence assert msg.manufacturer == 'ZZZ' assert msg.data == ['ABC', '1', '2', '3'] assert msg.render(checksum=False, dollar=False) == data pynmea2-1.12.0/examples/0000775000175100017510000000000013170047315014411 5ustar tomtom00000000000000pynmea2-1.12.0/examples/serial.py0000660000175100017510000000116012466464474016254 0ustar tomtom00000000000000import time import serial import pynmea2 def read(filename): f = open(filename) reader = pynmea2.NMEAStreamReader(f) while 1: for msg in reader.next(): print(msg) def read_serial(filename): com = None reader = pynmea2.NMEAStreamReader() while 1: if com is None: try: com = serial.Serial(filename, timeout=5.0) except serial.SerialException: print('could not connect to %s' % filename) time.sleep(5.0) continue data = com.read(16) for msg in reader.next(data): print(msg) pynmea2-1.12.0/setup.py0000664000175100017510000000226613023406655014316 0ustar tomtom00000000000000from setuptools import setup import imp _version = imp.load_source("pynmea2._version", "pynmea2/_version.py") setup( name='pynmea2', version=_version.__version__, author='Tom Flanagan', author_email='tom@zkpq.ca', license='MIT', url='https://github.com/Knio/pynmea2', description='Python library for the NMEA 0183 protcol', packages=['pynmea2','pynmea2.types','pynmea2.types.proprietary'], keywords='python nmea gps parse parsing nmea0183 0183', classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Scientific/Engineering :: GIS', 'Topic :: Software Development :: Libraries :: Python Modules', ] ) pynmea2-1.12.0/MANIFEST.in0000664000175100017510000000021413125503417014326 0ustar tomtom00000000000000include README.md include LICENSE.txt recursive-include test * recursive-include examples * global-exclude __pycache__ global-exclude *.pyc pynmea2-1.12.0/setup.cfg0000664000175100017510000000004613170047315014414 0ustar tomtom00000000000000[egg_info] tag_build = tag_date = 0 pynmea2-1.12.0/PKG-INFO0000664000175100017510000000175613170047315013701 0ustar tomtom00000000000000Metadata-Version: 1.1 Name: pynmea2 Version: 1.12.0 Summary: Python library for the NMEA 0183 protcol Home-page: https://github.com/Knio/pynmea2 Author: Tom Flanagan Author-email: tom@zkpq.ca License: MIT Description-Content-Type: UNKNOWN Description: UNKNOWN Keywords: python nmea gps parse parsing nmea0183 0183 Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Scientific/Engineering :: GIS Classifier: Topic :: Software Development :: Libraries :: Python Modules pynmea2-1.12.0/README.md0000664000175100017510000001031113032021173014035 0ustar tomtom00000000000000pynmea2 ======= `pynmea2` is a python library for the [NMEA 0183](http://en.wikipedia.org/wiki/NMEA_0183) protocol `pynmea2` is based on [`pynmea`](https://code.google.com/p/pynmea/) by Becky Lewis The `pynmea2` homepage is located at http://github.com/Knio/pynmea2 ### Compatibility `pynmea2` is compatable with Python 2.7 and Python 3.3 [![Build Status](https://travis-ci.org/Knio/pynmea2.png?branch=master)](https://travis-ci.org/Knio/pynmea2) [![Coverage Status](https://coveralls.io/repos/Knio/pynmea2/badge.png?branch=master)](https://coveralls.io/r/Knio/pynmea2?branch=master) [![Code Health](https://landscape.io/github/Knio/pynmea2/master/landscape.svg?style=flat)](https://landscape.io/github/Knio/pynmea2/master) ### Installation The recommended way to install `pynmea2` is with [pip](http://pypi.python.org/pypi/pip/): pip install pynmea2 [![PyPI version](https://badge.fury.io/py/pynmea2.png)](http://badge.fury.io/py/pynmea2) Parsing ------- You can parse individual NMEA sentences using the `parse(data, check=False)` function, which takes a string containing a NMEA 0183 sentence and returns a `NMEASentence` object. Note that the leading '$' is optional and trailing whitespace is ignored when parsing a sentence. With `check=False`, `parse` will accept NMEA messages that do not have checksums, however it will still raise `pynmea2.ChecksumError` if they are present. `check=True` will also raise `ChecksumError` if the checksum is missing. Example: ```python >>> import pynmea2 >>> msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D") >>> msg ``` The `NMEASentence` object has different properties, depending on its sentence type. The `GGA` message has the following properties: ```python >>> msg.timestamp datetime.time(18, 43, 53) >>> msg.lat '1929.045' >>> msg.lat_dir 'S' >>> msg.lon '02410.506' >>> msg.lon_dir 'E' >>> msg.gps_qual '1' >>> msg.num_sats '04' >>> msg.horizontal_dil '2.6' >>> msg.altitude 100.0 >>> msg.altitude_units 'M' >>> msg.geo_sep '-33.9' >>> msg.geo_sep_units 'M' >>> msg.age_gps_data '' >>> msg.ref_station_id '0000' ``` Additional properties besides the ones explicitly in the message data may also exist. For example, `latitude` and `longitude` properties exist as helpers to access the geographic coordinates as python floats ([DD](http://en.wikipedia.org/wiki/Decimal_degrees), "decimal degrees") instead of the DDDMM.MMMM ("Degrees, minutes, seconds") format used in the NMEA protocol. `latitude_minutes`, `latitude_seconds`, `longitude_minutes`, and `longitude_seconds` are also supported and allow easy creation of differently formatted location strings. ```python >>> msg.latitude -19.4840833333 >>> msg.longitude 24.1751 >>> '%02d°%07.4f′' % (msg.latitude, msg.latitude_minutes) '-19°29.0450′' >>> '%02d°%02d′%07.4f″' % (msg.latitude, msg.latitude_minutes, msg.latitude_seconds) "-19°29′02.7000″" ``` Generating ---------- You can create a `NMEASentence` object by calling the constructor with talker, message type, and data fields: ```python >>> msg = pynmea2.GGA('GP', 'GGA', ('184353.07', '1929.045', 'S', '02410.506', 'E', '1', '04', '2.6', '100.00', 'M', '-33.9', 'M', '', '0000')) ``` and generate a NMEA string from a `NMEASentence` object: ```python >>> str(msg) '$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D' ``` Streaming --------- `pynmea2` can also process streams of NMEA sentences like so, by feeding chunks of data manually: ```python streamreader = pynmea2.NMEAStreamReader() while 1: data = input.read() for msg in streamreader.next(data): print msg ``` or given a file-like device, automatically: ```python streamreader = pynmea2.NMEAStreamReader(input) while 1: for msg in streamreader.next(): print msg ``` If your stream is noisy and contains errors, you can set some basic error handling with the [`errors` parameter of the `NMEAStreamReader` constructor.](pynmea2/stream.py#L12)