Skip to content

Commit 3b79116

Browse files
authored
Merge pull request #5 from dherrada/format-adding
Added ways to handle all sentences. Currently, only _parse_gpgll actually works
2 parents 91e1ddf + b5c5752 commit 3b79116

File tree

2 files changed

+166
-29
lines changed

2 files changed

+166
-29
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ bundles
99
*.DS_Store
1010
.eggs
1111
dist
12-
**/*.egg-info
12+
**/*.egg-info
13+
tester.py

adafruit_gps.py

Lines changed: 164 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ def _parse_float(nmea_data):
7070
return None
7171
return float(nmea_data)
7272

73+
def _parse_str(nmea_data):
74+
if nmea_data is None or nmea_data == '':
75+
return None
76+
return str(nmea_data)
77+
7378
# lint warning about too many attributes disabled
7479
#pylint: disable-msg=R0902
7580
class GPS:
@@ -83,13 +88,20 @@ def __init__(self, uart, debug=False):
8388
self.latitude = None
8489
self.longitude = None
8590
self.fix_quality = None
91+
self.fix_quality_3d = None
8692
self.satellites = None
8793
self.horizontal_dilution = None
8894
self.altitude_m = None
8995
self.height_geoid = None
90-
self.velocity_knots = None
9196
self.speed_knots = None
97+
self.speed_kmh = None
9298
self.track_angle_deg = None
99+
self.total_mess_num = None
100+
self.mess_num = None
101+
self.sats = None
102+
self.isactivedata = None
103+
self.true_track = None
104+
self.mag_track = None
93105
self.debug = debug
94106

95107
def update(self):
@@ -102,17 +114,27 @@ def update(self):
102114
try:
103115
sentence = self._parse_sentence()
104116
except UnicodeError:
117+
print("UnicodeError")
105118
return None
106119
if sentence is None:
107120
return False
108121
if self.debug:
109122
print(sentence)
110123
data_type, args = sentence
111124
data_type = bytes(data_type.upper(), "ascii")
112-
if data_type == b'GPGGA': # GGA, 3d location fix
113-
self._parse_gpgga(args)
114-
elif data_type == b'GPRMC': # RMC, minimum location info
125+
#return sentence
126+
if data_type == b'GPGLL': # GLL, Geographic Position – Latitude/Longitude
127+
self._parse_gpgll(args)
128+
elif data_type == b'GPRMC': # RMC, minimum location info
115129
self._parse_gprmc(args)
130+
elif data_type == b'GPVTG': # VTG, Track Made Good and Ground Speed
131+
self._parse_gpvtg(args)
132+
elif data_type == b'GPGGA': # GGA, 3d location fix
133+
self._parse_gpgga(args)
134+
elif data_type == b'GPGSA': # GSA, GPS DOP and active satellites
135+
self._parse_gpgsa(args)
136+
elif data_type == b'GPGSV': # GSV, Satellites in view
137+
self._parse_gpgsv(args)
116138
return True
117139

118140
def send_command(self, command, add_checksum=True):
@@ -135,6 +157,10 @@ def send_command(self, command, add_checksum=True):
135157
def has_fix(self):
136158
"""True if a current fix for location information is available."""
137159
return self.fix_quality is not None and self.fix_quality >= 1
160+
161+
@property
162+
def has_3d_fix(self):
163+
return self.fix_quality_3d is not None and self.fix_quality_3d >= 2
138164

139165
@property
140166
def datetime(self):
@@ -147,15 +173,19 @@ def _parse_sentence(self):
147173
# This needs to be refactored when it can be tested.
148174

149175
# Only continue if we have at least 64 bytes in the input buffer
176+
"""
150177
if self._uart.in_waiting < 64:
151178
return None
179+
"""
152180

153181
sentence = self._uart.readline()
154182
if sentence is None or sentence == b'' or len(sentence) < 1:
183+
print("Sentence is none")
155184
return None
156185
try:
157186
sentence = str(sentence, 'ascii').strip()
158187
except UnicodeError:
188+
print("UnicodeError")
159189
return None
160190
# Look for a checksum and validate it if present.
161191
if len(sentence) > 7 and sentence[-3] == '*':
@@ -165,25 +195,34 @@ def _parse_sentence(self):
165195
for i in range(1, len(sentence)-3):
166196
actual ^= ord(sentence[i])
167197
if actual != expected:
198+
print("Actual != expected")
168199
return None # Failed to validate checksum.
169200
# Remove checksum once validated.
170201
sentence = sentence[:-3]
171202
# Parse out the type of sentence (first string after $ up to comma)
172203
# and then grab the rest as data within the sentence.
173204
delineator = sentence.find(',')
174205
if delineator == -1:
206+
print("Bad delineator")
175207
return None # Invalid sentence, no comma after data type.
176208
data_type = sentence[1:delineator]
177209
return (data_type, sentence[delineator+1:])
178210

179-
def _parse_gpgga(self, args):
180-
# Parse the arguments (everything after data type) for NMEA GPGGA
181-
# 3D location fix sentence.
211+
def _parse_gpgll(self, args):
182212
data = args.split(',')
183-
if data is None or len(data) != 14:
184-
return # Unexpected number of params.
185-
# Parse fix time.
186-
time_utc = int(_parse_float(data[0]))
213+
if data is None or data[0] is None:
214+
return # Unexpected number of params.
215+
216+
# Parse latitude and longitude.
217+
self.latitude = _parse_degrees(data[0])
218+
if self.latitude is not None and \
219+
data[1] is not None and data[1].lower() == 's':
220+
self.latitude *= -1.0
221+
self.longitude = _parse_degrees(data[2])
222+
if self.longitude is not None and \
223+
data[3] is not None and data[3].lower() == 'w':
224+
self.longitude *= -1.0
225+
time_utc = int(_parse_int(float(data[4])))
187226
if time_utc is not None:
188227
hours = time_utc // 10000
189228
mins = (time_utc // 100) % 100
@@ -195,23 +234,10 @@ def _parse_gpgga(self, args):
195234
self.timestamp_utc.tm_mday, hours, mins, secs, 0, 0, -1))
196235
else:
197236
self.timestamp_utc = time.struct_time((0, 0, 0, hours, mins,
198-
secs, 0, 0, -1))
199-
# Parse latitude and longitude.
200-
self.latitude = _parse_degrees(data[1])
201-
if self.latitude is not None and \
202-
data[2] is not None and data[2].lower() == 's':
203-
self.latitude *= -1.0
204-
self.longitude = _parse_degrees(data[3])
205-
if self.longitude is not None and \
206-
data[4] is not None and data[4].lower() == 'w':
207-
self.longitude *= -1.0
208-
# Parse out fix quality and other simple numeric values.
209-
self.fix_quality = _parse_int(data[5])
210-
self.satellites = _parse_int(data[6])
211-
self.horizontal_dilution = _parse_float(data[7])
212-
self.altitude_m = _parse_float(data[8])
213-
self.height_geoid = _parse_float(data[10])
214-
237+
secs, 0, 0, -1))
238+
# Parse data active or void
239+
self.isactivedata = _parse_str(data[5])
240+
215241
def _parse_gprmc(self, args):
216242
# Parse the arguments (everything after data type) for NMEA GPRMC
217243
# minimum location fix sentence.
@@ -270,3 +296,113 @@ def _parse_gprmc(self, args):
270296
# Time hasn't been set so create it.
271297
self.timestamp_utc = time.struct_time((year, month, day, 0, 0,
272298
0, 0, 0, -1))
299+
300+
def _parse_gpvtg(self, args):
301+
data = args.split(',')
302+
303+
# Parse true track made good (degrees)
304+
self.true_track = _parse_float(data[0])
305+
306+
# Parse magnetic track made good
307+
self.mag_track = _parse_float(data[2])
308+
309+
# Parse speed
310+
self.speed_knots = _parse_float(data[4])
311+
self.speed_kmh = _parse_float(data[6])
312+
313+
def _parse_gpgga(self, args):
314+
# Parse the arguments (everything after data type) for NMEA GPGGA
315+
# 3D location fix sentence.
316+
data = args.split(',')
317+
if data is None or len(data) != 14:
318+
return # Unexpected number of params.
319+
# Parse fix time.
320+
time_utc = int(_parse_float(data[0]))
321+
if time_utc is not None:
322+
hours = time_utc // 10000
323+
mins = (time_utc // 100) % 100
324+
secs = time_utc % 100
325+
# Set or update time to a friendly python time struct.
326+
if self.timestamp_utc is not None:
327+
self.timestamp_utc = time.struct_time((
328+
self.timestamp_utc.tm_year, self.timestamp_utc.tm_mon,
329+
self.timestamp_utc.tm_mday, hours, mins, secs, 0, 0, -1))
330+
else:
331+
self.timestamp_utc = time.struct_time((0, 0, 0, hours, mins,
332+
secs, 0, 0, -1))
333+
# Parse latitude and longitude.
334+
self.latitude = _parse_degrees(data[1])
335+
if self.latitude is not None and \
336+
data[2] is not None and data[2].lower() == 's':
337+
self.latitude *= -1.0
338+
self.longitude = _parse_degrees(data[3])
339+
if self.longitude is not None and \
340+
data[4] is not None and data[4].lower() == 'w':
341+
self.longitude *= -1.0
342+
# Parse out fix quality and other simple numeric values.
343+
self.fix_quality = _parse_int(data[5])
344+
self.satellites = _parse_int(data[6])
345+
self.horizontal_dilution = _parse_float(data[7])
346+
self.altitude_m = _parse_float(data[8])
347+
self.height_geoid = _parse_float(data[10])
348+
349+
def _parse_gpgsa(self, args):
350+
data = args.split(',')
351+
if data is None:
352+
return # Unexpected number of params
353+
354+
# Parse selection mode
355+
self.sel_mode = _parse_str(data[0])
356+
# Parse 3d fix
357+
self.fix_quality_3d = _parse_int(data[1])
358+
sats = list(filter(None, data[2:-4]))
359+
satdict = {}
360+
for i in range(len(sats)):
361+
satdict["self.gps{}".format(i)] = _parse_int(sats[i])
362+
363+
globals().update(satdict)
364+
365+
# Parse PDOP, dilution of precision
366+
self.pdop = _parse_float(data[-3])
367+
# Parse HDOP, horizontal dilution of precision
368+
self.hdop = _parse_float(data[-2])
369+
# Parse VDOP, vertical dilution of precision
370+
self.vdop = _parse_float(data[-1])
371+
372+
def _parse_gpgsv(self, args):
373+
# Parse the arguments (everything after data type) for NMEA GPGGA
374+
# 3D location fix sentence.
375+
data = args.split(',')
376+
if data is None:
377+
return # Unexpected number of params.
378+
379+
# Parse number of messages
380+
self.total_mess_num = _parse_int(data[0]) # Total number of messages
381+
# Parse message number
382+
self.mess_num = _parse_int(data[1]) # Message number
383+
# Parse number of satellites in view
384+
self.satellites = _parse_int(data[2]) # Number of satellites
385+
try:
386+
satlist
387+
except NameError:
388+
satlist = [None] * self.total_mess_num
389+
390+
sat_tup = data[3:]
391+
392+
satdict = {}
393+
for i in range(len(sat_tup)/4):
394+
j = i*4
395+
key = "gps{}".format(i+(4*(self.mess_num-1)))
396+
satnum = _parse_int(sat_tup[0+j]) # Satellite number
397+
satdeg = _parse_int(sat_tup[1+j]) # Elevation in degrees
398+
satazim = _parse_int(sat_tup[2+j]) # Azimuth in degrees
399+
satsnr = _parse_int(sat_tup[3+j]) # SNR (signal-to-noise ratio) in dB
400+
value = (satnum, satdeg, satazim, satsnr)
401+
satdict[key] = value
402+
403+
satlist[self.mess_num-1] = satdict
404+
satlist = list(filter(None, satlist))
405+
self.sats = {}
406+
for satdict in satlist:
407+
self.sats.update(satdict)
408+
print(self.sats)

0 commit comments

Comments
 (0)