Skip to content

Commit 3b7b413

Browse files
authored
Merge pull request #7 from adafruit/master
Merging 3.3.0 back to my fork
2 parents 77e4dc5 + 35483e9 commit 3b7b413

File tree

2 files changed

+97
-9
lines changed

2 files changed

+97
-9
lines changed

README.rst

+3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ These two lines are the lines that actually solve the issue:
8989
print('Latitude: {0:.6f} degrees'.format(gps.latitude))
9090
print('Longitude: {0:.6f} degrees'.format(gps.longitude))
9191
92+
93+
Note: Sending multiple PMTK314 packets with gps.send_command() will not work unless there is a substantial amount of time in-between each time gps.send_command() is called. A time.sleep() of 1 second or more should fix this.
94+
9295
About NMEA Data
9396
===============
9497
This GPS module uses the NMEA 0183 protocol.

adafruit_gps.py

+94-9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
# Internal helper parsing functions.
5151
# These handle input that might be none or null and return none instead of
5252
# throwing errors.
53+
54+
5355
def _parse_degrees(nmea_data):
5456
# Parse a NMEA lat/long data pair 'dddmm.mmmm' into a pure degrees value.
5557
# Where ddd is the degrees, mm.mmmm is the minutes.
@@ -60,26 +62,31 @@ def _parse_degrees(nmea_data):
6062
minutes = raw % 100
6163
return deg + minutes/60
6264

65+
6366
def _parse_int(nmea_data):
6467
if nmea_data is None or nmea_data == '':
6568
return None
6669
return int(nmea_data)
6770

71+
6872
def _parse_float(nmea_data):
6973
if nmea_data is None or nmea_data == '':
7074
return None
7175
return float(nmea_data)
7276

77+
7378
def _parse_str(nmea_data):
7479
if nmea_data is None or nmea_data == '':
7580
return None
7681
return str(nmea_data)
7782

7883
# lint warning about too many attributes disabled
7984
#pylint: disable-msg=R0902
85+
86+
8087
class GPS:
81-
"""GPS parsing module. Can parse simple NMEA data sentences from serial GPS
82-
modules to read latitude, longitude, and more.
88+
"""GPS parsing module. Can parse simple NMEA data sentences from serial
89+
GPS modules to read latitude, longitude, and more.
8390
"""
8491
def __init__(self, uart, debug=False):
8592
self._uart = uart
@@ -90,13 +97,21 @@ def __init__(self, uart, debug=False):
9097
self.fix_quality = None
9198
self.fix_quality_3d = None
9299
self.satellites = None
100+
self.satellites_prev = None
93101
self.horizontal_dilution = None
94102
self.altitude_m = None
95103
self.height_geoid = None
96104
self.speed_knots = None
97105
self.track_angle_deg = None
98106
self.sats = None
99107
self.isactivedata = None
108+
self.true_track = None
109+
self.mag_track = None
110+
self.sat_prns = None
111+
self.sel_mode = None
112+
self.pdop = None
113+
self.hdop = None
114+
self.vdop = None
100115
self.debug = debug
101116

102117
def update(self):
@@ -109,15 +124,14 @@ def update(self):
109124
try:
110125
sentence = self._parse_sentence()
111126
except UnicodeError:
112-
print("UnicodeError")
113127
return None
114128
if sentence is None:
115129
return False
116130
if self.debug:
117131
print(sentence)
118132
data_type, args = sentence
119133
data_type = bytes(data_type.upper(), "ascii")
120-
#return sentence
134+
# return sentence
121135
if data_type == b'GPGLL': # GLL, Geographic Position – Latitude/Longitude
122136
self._parse_gpgll(args)
123137
elif data_type == b'GPRMC': # RMC, minimum location info
@@ -149,7 +163,9 @@ def has_fix(self):
149163

150164
@property
151165
def has_3d_fix(self):
152-
"""True if a current 3d fix for location information is available"""
166+
"""Returns true if there is a 3d fix available.
167+
use has_fix to determine if a 2d fix is available,
168+
passing it the same data"""
153169
return self.fix_quality_3d is not None and self.fix_quality_3d >= 2
154170

155171
@property
@@ -162,8 +178,8 @@ def _parse_sentence(self):
162178
# pylint: disable=len-as-condition
163179
# This needs to be refactored when it can be tested.
164180

165-
# Only continue if we have at least 64 bytes in the input buffer
166-
if self._uart.in_waiting < 64:
181+
# Only continue if we have at least 32 bytes in the input buffer
182+
if self._uart.in_waiting < 32:
167183
return None
168184

169185
sentence = self._uart.readline()
@@ -195,7 +211,7 @@ def _parse_sentence(self):
195211
def _parse_gpgll(self, args):
196212
data = args.split(',')
197213
if data is None or data[0] is None:
198-
return # Unexpected number of params.
214+
return # Unexpected number of params.
199215

200216
# Parse latitude and longitude.
201217
self.latitude = _parse_degrees(data[0])
@@ -262,7 +278,7 @@ def _parse_gprmc(self, args):
262278
if data[8] is not None and len(data[8]) == 6:
263279
day = int(data[8][0:2])
264280
month = int(data[8][2:4])
265-
year = 2000 + int(data[8][4:6]) # Y2k bug, 2 digit date assumption.
281+
year = 2000 + int(data[8][4:6]) # Y2k bug, 2 digit year assumption.
266282
# This is a problem with the NMEA
267283
# spec and not this code.
268284
if self.timestamp_utc is not None:
@@ -315,3 +331,72 @@ def _parse_gpgga(self, args):
315331
self.horizontal_dilution = _parse_float(data[7])
316332
self.altitude_m = _parse_float(data[8])
317333
self.height_geoid = _parse_float(data[10])
334+
335+
def _parse_gpgsa(self, args):
336+
data = args.split(',')
337+
if data is None:
338+
return # Unexpected number of params
339+
340+
# Parse selection mode
341+
self.sel_mode = _parse_str(data[0])
342+
# Parse 3d fix
343+
self.fix_quality_3d = _parse_int(data[1])
344+
satlist = list(filter(None, data[2:-4]))
345+
self.sat_prns = {}
346+
for i, sat in enumerate(satlist, 1):
347+
self.sat_prns["gps{}".format(i)] = _parse_int(sat)
348+
349+
# Parse PDOP, dilution of precision
350+
self.pdop = _parse_float(data[-3])
351+
# Parse HDOP, horizontal dilution of precision
352+
self.hdop = _parse_float(data[-2])
353+
# Parse VDOP, vertical dilution of precision
354+
self.vdop = _parse_float(data[-1])
355+
356+
def _parse_gpgsv(self, args):
357+
# Parse the arguments (everything after data type) for NMEA GPGGA
358+
# 3D location fix sentence.
359+
data = args.split(',')
360+
if data is None:
361+
return # Unexpected number of params.
362+
363+
# Parse number of messages
364+
self.total_mess_num = _parse_int(data[0]) # Total number of messages
365+
# Parse message number
366+
self.mess_num = _parse_int(data[1]) # Message number
367+
# Parse number of satellites in view
368+
self.satellites = _parse_int(data[2]) # Number of satellites
369+
370+
if len(data) < 3:
371+
return
372+
373+
sat_tup = data[3:]
374+
375+
satdict = {}
376+
for i in range(len(sat_tup)/4):
377+
j = i*4
378+
key = "gps{}".format(i+(4*(self.mess_num-1)))
379+
satnum = _parse_int(sat_tup[0+j]) # Satellite number
380+
satdeg = _parse_int(sat_tup[1+j]) # Elevation in degrees
381+
satazim = _parse_int(sat_tup[2+j]) # Azimuth in degrees
382+
satsnr = _parse_int(sat_tup[3+j]) # signal-to-noise ratio in dB
383+
value = (satnum, satdeg, satazim, satsnr)
384+
satdict[key] = value
385+
386+
if self.sats is None:
387+
self.sats = {}
388+
for satnum in satdict:
389+
self.sats[satnum] = satdict[satnum]
390+
391+
try:
392+
if self.satellites < self.satellites_prev:
393+
for i in self.sats:
394+
try:
395+
if int(i[-2]) >= self.satellites:
396+
del self.sats[i]
397+
except ValueError:
398+
if int(i[-1]) >= self.satellites:
399+
del self.sats[i]
400+
except TypeError:
401+
pass
402+
self.satellites_prev = self.satellites

0 commit comments

Comments
 (0)