8
8
# Copyright (c) 2018 Peter Hinch
9
9
# Released under the MIT License (MIT) - see LICENSE file
10
10
11
+ # astests.py runs under CPython but not MicroPython because mktime is missing
12
+ # from Unix build of utime
13
+
11
14
try :
12
15
import uasyncio as asyncio
13
16
except ImportError :
45
48
DATE = const (RMC )
46
49
COURSE = const (RMC | VTG )
47
50
51
+
48
52
class AS_GPS (object ):
49
53
_SENTENCE_LIMIT = 76 # Max sentence length (based on GGA sentence)
50
54
_NO_FIX = 1
@@ -54,6 +58,25 @@ class AS_GPS(object):
54
58
'June' , 'July' , 'August' , 'September' , 'October' ,
55
59
'November' , 'December' )
56
60
61
+ # Return day of week from date. Pyboard RTC format: 1-7 for Monday through Sunday.
62
+ # https://stackoverflow.com/questions/9847213/how-do-i-get-the-day-of-week-given-a-date-in-python?noredirect=1&lq=1
63
+ # Adapted for Python 3 and Pyboard RTC format.
64
+ @staticmethod
65
+ def _week_day (year , month , day ):
66
+ offset = [0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 ]
67
+ aux = year - 1700 - (1 if month <= 2 else 0 )
68
+ # day_of_week for 1700/1/1 = 5, Friday
69
+ day_of_week = 5
70
+ # partial sum of days betweem current date and 1700/1/1
71
+ day_of_week += (aux + (1 if month <= 2 else 0 )) * 365
72
+ # leap year correction
73
+ day_of_week += aux // 4 - aux // 100 + (aux + 100 ) // 400
74
+ # sum monthly and day offsets
75
+ day_of_week += offset [month - 1 ] + (day - 1 )
76
+ day_of_week %= 7
77
+ day_of_week = day_of_week if day_of_week else 7
78
+ return day_of_week
79
+
57
80
# 8-bit xor of characters between "$" and "*"
58
81
@staticmethod
59
82
def _crc_check (res , ascii_crc ):
@@ -74,17 +97,21 @@ def __init__(self, sreader, local_offset=0, fix_cb=lambda *_ : None, cb_mask=RMC
74
97
self .cb_mask = cb_mask
75
98
self ._fix_cb_args = fix_cb_args
76
99
77
- # Import utime or time for fix time handling
100
+ # CPython compatibility. Import utime or time for fix time handling.
78
101
try :
79
102
import utime
80
103
self ._get_time = utime .ticks_ms
81
104
self ._time_diff = utime .ticks_diff
105
+ self ._localtime = utime .localtime
106
+ self ._mktime = utime .mktime
82
107
except ImportError :
83
108
# Otherwise default to time module for non-embedded implementations
84
109
# Should still support millisecond resolution.
85
110
import time
86
111
self ._get_time = time .time
87
112
self ._time_diff = lambda start , end : 1000 * (start - end )
113
+ self ._localtime = time .localtime
114
+ self ._mktime = time .mktime
88
115
89
116
# Key: currently supported NMEA sentences. Value: parse method.
90
117
self .supported_sentences = {'GPRMC' : self ._gprmc , 'GLRMC' : self ._gprmc ,
@@ -112,8 +139,9 @@ def __init__(self, sreader, local_offset=0, fix_cb=lambda *_ : None, cb_mask=RMC
112
139
# Data From Sentences
113
140
# Time. Ignore http://www.gpsinformation.org/dale/nmea.htm, hardware
114
141
# returns a float.
115
- self .timestamp = [0 , 0 , 0.0 ] # [h, m, s]
116
- self .date = [0 , 0 , 0 ] # [d, m, y]
142
+ self .utc = [0 , 0 , 0.0 ] # [h: int, m: int, s: float]
143
+ self .local_time = [0 , 0 , 0.0 ] # [h: int, m: int, s: float]
144
+ self .date = [0 , 0 , 0 ] # [dd: int, mm: int, yy: int]
117
145
self .local_offset = local_offset # hrs
118
146
119
147
# Position/Motion
@@ -232,15 +260,46 @@ def _fix(self, gps_segments, idx_lat, idx_long):
232
260
233
261
# Set timestamp. If time/date not present retain last reading (if any).
234
262
def _set_timestamp (self , utc_string ):
235
- if utc_string : # Possible timestamp found
236
- try :
237
- self .timestamp [0 ] = int (utc_string [0 :2 ]) + self .local_offset # h
238
- self .timestamp [1 ] = int (utc_string [2 :4 ]) # mins
239
- self .timestamp [2 ] = float (utc_string [4 :]) # secs from chip is a float
240
- return True
241
- except ValueError :
242
- pass
243
- return False
263
+ if not utc_string :
264
+ return False
265
+ # Possible timestamp found
266
+ try :
267
+ self .utc [0 ] = int (utc_string [0 :2 ]) # h
268
+ self .utc [1 ] = int (utc_string [2 :4 ]) # mins
269
+ self .utc [2 ] = float (utc_string [4 :]) # secs from chip is a float
270
+ return True
271
+ except ValueError :
272
+ return False
273
+ for idx in range (3 ):
274
+ self .local_time [idx ] = self .utc [idx ]
275
+ return True
276
+
277
+ # A local offset may exist so check for date rollover. Local offsets can
278
+ # include fractions of an hour but not seconds (AFAIK).
279
+ def _set_date (self , date_string ):
280
+ if not date_string :
281
+ return False
282
+ try :
283
+ d = int (date_string [0 :2 ]) # day
284
+ m = int (date_string [2 :4 ]) # month
285
+ y = int (date_string [4 :6 ]) + 2000 # year
286
+ except ValueError : # Bad Date stamp value present
287
+ return False
288
+ hrs = self .utc [0 ]
289
+ mins = self .utc [1 ]
290
+ secs = self .utc [2 ]
291
+ wday = self ._week_day (y , m , d ) - 1
292
+ t = self ._mktime ((y , m , d , hrs , mins , int (secs ), wday , 0 , 0 ))
293
+ t += int (3600 * self .local_offset )
294
+ y , m , d , hrs , mins , * _ = self ._localtime (t ) # Preserve float seconds
295
+ y -= 2000
296
+ self .local_time [0 ] = hrs
297
+ self .local_time [1 ] = mins
298
+ self .local_time [2 ] = secs
299
+ self .date [0 ] = d
300
+ self .date [1 ] = m
301
+ self .date [2 ] = y
302
+ return True
244
303
245
304
########################################
246
305
# Sentence Parsers
@@ -255,20 +314,9 @@ def _set_timestamp(self, utc_string):
255
314
def _gprmc (self , gps_segments ): # Parse RMC sentence
256
315
self ._valid &= ~ RMC
257
316
# UTC Timestamp.
258
- try :
259
- self ._set_timestamp (gps_segments [1 ])
260
- except ValueError : # Bad Timestamp value present
261
- return False
262
-
263
- # Date stamp
264
- try :
265
- date_string = gps_segments [9 ]
266
- if date_string : # Possible date stamp found
267
- self .date [0 ] = int (date_string [0 :2 ]) # day
268
- self .date [1 ] = int (date_string [2 :4 ]) # month
269
- self .date [2 ] = int (date_string [4 :6 ]) # year 18 == 2018
270
-
271
- except ValueError : # Bad Date stamp value present
317
+ if not self ._set_timestamp (gps_segments [1 ]):
318
+ return False # Bad Timestamp value present
319
+ if not self ._set_date (gps_segments [9 ]):
272
320
return False
273
321
274
322
# Check Receiver Data Valid Flag
@@ -599,8 +647,9 @@ def speed_string(self, unit=KPH):
599
647
return sform .format (speed , 'knots' )
600
648
return sform .format (speed , 'km/h' )
601
649
602
- def time (self ):
603
- return '{:02d}:{:02d}:{:2.3f}' .format (* self .timestamp )
650
+ def time (self , local = True ):
651
+ t = self .local_time if local else self .utc
652
+ return '{:02d}:{:02d}:{:2.3f}' .format (* t )
604
653
605
654
def date_string (self , formatting = MDY ):
606
655
day , month , year = self .date
0 commit comments