@@ -70,6 +70,11 @@ def _parse_float(nmea_data):
70
70
return None
71
71
return float (nmea_data )
72
72
73
+ def _parse_str (nmea_data ):
74
+ if nmea_data is None or nmea_data == '' :
75
+ return None
76
+ return str (nmea_data )
77
+
73
78
# lint warning about too many attributes disabled
74
79
#pylint: disable-msg=R0902
75
80
class GPS :
@@ -83,18 +88,20 @@ def __init__(self, uart, debug=False):
83
88
self .latitude = None
84
89
self .longitude = None
85
90
self .fix_quality = None
91
+ self .fix_quality_3d = None
86
92
self .satellites = None
87
93
self .horizontal_dilution = None
88
94
self .altitude_m = None
89
95
self .height_geoid = None
90
- self .velocity_knots = None
91
96
self .speed_knots = None
97
+ self .speed_kmh = None
92
98
self .track_angle_deg = None
93
99
self .total_mess_num = None
94
100
self .mess_num = None
95
- self .gps0 = None
96
- self .gps1 = None
97
- self .gps2 = None
101
+ self .sats = None
102
+ self .isactivedata = None
103
+ self .true_track = None
104
+ self .mag_track = None
98
105
self .debug = debug
99
106
100
107
def update (self ):
@@ -115,11 +122,18 @@ def update(self):
115
122
data_type , args = sentence
116
123
data_type = bytes (data_type .upper (), "ascii" )
117
124
#return sentence
118
- if data_type == b'GPGGA' : # GGA, 3d location fix
119
- self ._parse_gpgga (args )
120
- elif data_type == b'GPRMC' : # RMC, minimum location info
125
+
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
121
129
self ._parse_gprmc (args )
122
- elif data_type == b'GPGSV' :
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
123
137
self ._parse_gpgsv (args )
124
138
return True
125
139
@@ -143,6 +157,10 @@ def send_command(self, command, add_checksum=True):
143
157
def has_fix (self ):
144
158
"""True if a current fix for location information is available."""
145
159
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
146
164
147
165
@property
148
166
def datetime (self ):
@@ -179,15 +197,22 @@ def _parse_sentence(self):
179
197
data_type = sentence [1 :delineator ]
180
198
return (data_type , sentence [delineator + 1 :])
181
199
182
-
183
- def _parse_gpgga (self , args ):
184
- # Parse the arguments (everything after data type) for NMEA GPGGA
185
- # 3D location fix sentence.
200
+ def _parse_gpgll (self , args ):
186
201
data = args .split (',' )
187
- if data is None or len (data ) != 14 :
188
- return # Unexpected number of params.
189
- # Parse fix time.
190
- time_utc = int (_parse_float (data [0 ]))
202
+ if data is None or len (data ) < 11 or data [0 ] is None :
203
+ return # Unexpected number of params.
204
+
205
+ # Parse latitude and longitude.
206
+ self .latitude = _parse_degrees (data [0 ])
207
+ if self .latitude is not None and \
208
+ data [1 ] is not None and data [1 ].lower () == 's' :
209
+ self .latitude *= - 1.0
210
+ self .longitude = _parse_degrees (data [2 ])
211
+ if self .longitude is not None and \
212
+ data [3 ] is not None and data [3 ].lower () == 'w' :
213
+ self .longitude *= - 1.0
214
+
215
+ time_utc = int (_parse_int (data [4 ]))
191
216
if time_utc is not None :
192
217
hours = time_utc // 10000
193
218
mins = (time_utc // 100 ) % 100
@@ -199,23 +224,10 @@ def _parse_gpgga(self, args):
199
224
self .timestamp_utc .tm_mday , hours , mins , secs , 0 , 0 , - 1 ))
200
225
else :
201
226
self .timestamp_utc = time .struct_time ((0 , 0 , 0 , hours , mins ,
202
- secs , 0 , 0 , - 1 ))
203
- # Parse latitude and longitude.
204
- self .latitude = _parse_degrees (data [1 ])
205
- if self .latitude is not None and \
206
- data [2 ] is not None and data [2 ].lower () == 's' :
207
- self .latitude *= - 1.0
208
- self .longitude = _parse_degrees (data [3 ])
209
- if self .longitude is not None and \
210
- data [4 ] is not None and data [4 ].lower () == 'w' :
211
- self .longitude *= - 1.0
212
- # Parse out fix quality and other simple numeric values.
213
- self .fix_quality = _parse_int (data [5 ])
214
- self .satellites = _parse_int (data [6 ])
215
- self .horizontal_dilution = _parse_float (data [7 ])
216
- self .altitude_m = _parse_float (data [8 ])
217
- self .height_geoid = _parse_float (data [10 ])
218
-
227
+ secs , 0 , 0 , - 1 ))
228
+ # Parse data active or void
229
+ self .isactivedata = _parse_str (data [5 ])
230
+
219
231
def _parse_gprmc (self , args ):
220
232
# Parse the arguments (everything after data type) for NMEA GPRMC
221
233
# minimum location fix sentence.
@@ -275,43 +287,111 @@ def _parse_gprmc(self, args):
275
287
self .timestamp_utc = time .struct_time ((year , month , day , 0 , 0 ,
276
288
0 , 0 , 0 , - 1 ))
277
289
290
+ def _parse_gpvtg (self , args ):
291
+ data = args .split (',' )
292
+
293
+ # Parse true track made good (degrees)
294
+ self .true_track = _parse_float (data [0 ])
295
+
296
+ # Parse magnetic track made good
297
+ self .mag_track = _parse_float (data [2 ])
298
+
299
+ # Parse speed
300
+ self .speed_knots = _parse_float (data [4 ])
301
+ self .speed_kmh = _parse_float (data [6 ])
302
+
303
+ def _parse_gpgga (self , args ):
304
+ # Parse the arguments (everything after data type) for NMEA GPGGA
305
+ # 3D location fix sentence.
306
+ data = args .split (',' )
307
+ if data is None or len (data ) != 14 :
308
+ return # Unexpected number of params.
309
+ # Parse fix time.
310
+ time_utc = int (_parse_float (data [0 ]))
311
+ if time_utc is not None :
312
+ hours = time_utc // 10000
313
+ mins = (time_utc // 100 ) % 100
314
+ secs = time_utc % 100
315
+ # Set or update time to a friendly python time struct.
316
+ if self .timestamp_utc is not None :
317
+ self .timestamp_utc = time .struct_time ((
318
+ self .timestamp_utc .tm_year , self .timestamp_utc .tm_mon ,
319
+ self .timestamp_utc .tm_mday , hours , mins , secs , 0 , 0 , - 1 ))
320
+ else :
321
+ self .timestamp_utc = time .struct_time ((0 , 0 , 0 , hours , mins ,
322
+ secs , 0 , 0 , - 1 ))
323
+ # Parse latitude and longitude.
324
+ self .latitude = _parse_degrees (data [1 ])
325
+ if self .latitude is not None and \
326
+ data [2 ] is not None and data [2 ].lower () == 's' :
327
+ self .latitude *= - 1.0
328
+ self .longitude = _parse_degrees (data [3 ])
329
+ if self .longitude is not None and \
330
+ data [4 ] is not None and data [4 ].lower () == 'w' :
331
+ self .longitude *= - 1.0
332
+ # Parse out fix quality and other simple numeric values.
333
+ self .fix_quality = _parse_int (data [5 ])
334
+ self .satellites = _parse_int (data [6 ])
335
+ self .horizontal_dilution = _parse_float (data [7 ])
336
+ self .altitude_m = _parse_float (data [8 ])
337
+ self .height_geoid = _parse_float (data [10 ])
338
+
339
+ def _parse_gpgsa (self , args ):
340
+ data = args .split (',' )
341
+ if data is None :
342
+ return # Unexpected number of params
343
+
344
+ # Parse selection mode
345
+ self .sel_mode = _parse_str (data [0 ])
346
+ # Parse 3d fix
347
+ self .fix_quality_3d = _parse_int (data [1 ])
348
+ sats = list (filter (None , data [2 :- 4 ]))
349
+ satdict = {}
350
+ for i in range (len (sats )):
351
+ satdict ["self.gps{}" .format (i )] = _parse_int (sats [i ])
352
+
353
+ globals ().update (satdict )
354
+
355
+ # Parse PDOP, dilution of precision
356
+ self .pdop = _parse_float (data [- 3 ])
357
+ # Parse HDOP, horizontal dilution of precision
358
+ self .hdop = _parse_float (data [- 2 ])
359
+ # Parse VDOP, vertical dilution of precision
360
+ self .vdop = _parse_float (data [- 1 ])
361
+
278
362
def _parse_gpgsv (self , args ):
279
363
# Parse the arguments (everything after data type) for NMEA GPGGA
280
364
# 3D location fix sentence.
281
365
data = args .split (',' )
282
366
if data is None :
283
367
return # Unexpected number of params.
368
+
284
369
# Parse number of messages
285
370
self .total_mess_num = _parse_int (data [0 ]) # Total number of messages
286
371
# Parse message number
287
372
self .mess_num = _parse_int (data [1 ]) # Message number
288
373
# Parse number of satellites in view
289
374
self .satellites = _parse_int (data [2 ]) # Number of satellites
290
- """
291
- if self.satellites > 0:
292
- self.has_fix = 1
293
- """
375
+ try :
376
+ satlist
377
+ except NameError :
378
+ satlist = [None ] * self .total_mess_num
379
+
380
+ sat_tup = data [3 :]
294
381
295
- sats = data [3 :]
296
382
satdict = {}
297
- for i in range (len (sats ) / 4 ):
383
+ for i in range (len (sat_tup ) / 4 ):
298
384
j = i * 4
299
- key = "self. gps{}" .format (i )
300
- satnum = _parse_int (sats [0 + j ]) # Satellite number
301
- satdeg = _parse_int (sats [1 + j ]) # Elevation in degrees
302
- satazim = _parse_int (sats [2 + j ]) # Azimuth in degrees
303
- satsnr = _parse_int (sats [3 + j ]) # SNR (signal-to-noise ratio) in dB
385
+ key = "gps{}" .format (i )
386
+ satnum = _parse_int (sat_tup [0 + j ]) # Satellite number
387
+ satdeg = _parse_int (sat_tup [1 + j ]) # Elevation in degrees
388
+ satazim = _parse_int (sat_tup [2 + j ]) # Azimuth in degrees
389
+ satsnr = _parse_int (sat_tup [3 + j ]) # SNR (signal-to-noise ratio) in dB
304
390
value = (satnum , satdeg , satazim , satsnr )
305
391
satdict [key ] = value
306
- """
307
- params = {'self': self}
308
- for k, v in satdict.items():
309
- exec("%s=%s" % (k, v), params, params)
310
- """
311
- globals ().update (satdict )
312
- # Should be self.gps0, self.gps1, self.gps2, etc
313
- # Each should be a tuple with 4 values
314
- # gpsx[0] = satellite number
315
- # gpsx[1] = elevation in degrees
316
- # gpsx[2] = azimuth in degrees to true
317
- # gpsx[3] = Signal-to-noise ratio in dB
392
+
393
+ satlist [self .mess_num - 1 ] = satdict
394
+ satlist = list (filter (None , satlist ))
395
+ self .sats = {}
396
+ for satdict in satlist :
397
+ self .sats .update (satdict )
0 commit comments