adafruit-circuitpython-gps 3.11.7__tar.gz → 3.11.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.pre-commit-config.yaml +1 -1
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/PKG-INFO +1 -1
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/adafruit_circuitpython_gps.egg-info/PKG-INFO +1 -1
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/adafruit_gps.py +67 -19
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/pyproject.toml +1 -1
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/tests/adafruit_gps_test.py +2 -2
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.coveragerc +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.gitattributes +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.github/workflows/build.yml +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.github/workflows/failure-help-text.yml +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.github/workflows/release_gh.yml +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.github/workflows/release_pypi.yml +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.gitignore +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.readthedocs.yaml +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/CODE_OF_CONDUCT.md +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/LICENSE +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/LICENSES/CC-BY-4.0.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/LICENSES/MIT.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/LICENSES/Unlicense.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/README.rst +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/README.rst.license +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/adafruit_circuitpython_gps.egg-info/SOURCES.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/adafruit_circuitpython_gps.egg-info/dependency_links.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/adafruit_circuitpython_gps.egg-info/requires.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/adafruit_circuitpython_gps.egg-info/top_level.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/_static/custom.css +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/_static/favicon.ico +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/_static/favicon.ico.license +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/api.rst +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/api.rst.license +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/conf.py +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/details.rst +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/details.rst.license +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/examples.rst +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/examples.rst.license +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/index.rst +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/index.rst.license +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/requirements.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_datalogging.py +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_displayio_simpletest.py +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_echotest.py +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_satellitefix.py +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_simpletest.py +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_time_source.py +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/optional_requirements.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/requirements.txt +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/ruff.toml +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/setup.cfg +0 -0
- {adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/tests/__init__.py +0 -0
|
@@ -40,7 +40,7 @@ try:
|
|
|
40
40
|
except ImportError:
|
|
41
41
|
pass
|
|
42
42
|
|
|
43
|
-
__version__ = "3.11.
|
|
43
|
+
__version__ = "3.11.9"
|
|
44
44
|
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_GPS.git"
|
|
45
45
|
|
|
46
46
|
|
|
@@ -89,6 +89,22 @@ _SENTENCE_PARAMS = (
|
|
|
89
89
|
# Internal helper parsing functions.
|
|
90
90
|
# These handle input that might be none or null and return none instead of
|
|
91
91
|
# throwing errors.
|
|
92
|
+
def _split_decimal(nmea_data: str) -> Tuple[Optional[str], str]:
|
|
93
|
+
# Split a numeric NMEA field into its integer and fractional digits.
|
|
94
|
+
# Returns (None, "") if either part is missing or non-numeric. The
|
|
95
|
+
# fractional part is optional; some receivers omit it entirely.
|
|
96
|
+
dot = nmea_data.find(".")
|
|
97
|
+
if dot == -1:
|
|
98
|
+
int_part, minutes_decimal = nmea_data, "0"
|
|
99
|
+
else:
|
|
100
|
+
int_part, minutes_decimal = nmea_data[:dot], nmea_data[dot + 1 :]
|
|
101
|
+
if not minutes_decimal:
|
|
102
|
+
minutes_decimal = "0"
|
|
103
|
+
if not int_part.isdigit() or not minutes_decimal.isdigit():
|
|
104
|
+
return None, ""
|
|
105
|
+
return int_part, minutes_decimal
|
|
106
|
+
|
|
107
|
+
|
|
92
108
|
def _parse_degrees(nmea_data: str) -> int:
|
|
93
109
|
# Parse a NMEA lat/long data pair 'dddmm.mmmm' into a pure degrees value.
|
|
94
110
|
# Where ddd is the degrees, mm.mmmm is the minutes.
|
|
@@ -97,10 +113,12 @@ def _parse_degrees(nmea_data: str) -> int:
|
|
|
97
113
|
# To avoid losing precision handle degrees and minutes separately
|
|
98
114
|
# Return the final value as an integer. Further functions can parse
|
|
99
115
|
# this into a float or separate parts to retain the precision
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
116
|
+
int_part, minutes_decimal = _split_decimal(nmea_data)
|
|
117
|
+
if int_part is None:
|
|
118
|
+
return None
|
|
119
|
+
degrees = int(int_part) // 100 * 1000000 # the ddd
|
|
120
|
+
minutes = int(int_part) % 100 # the mm.
|
|
121
|
+
minutes += int(f"{minutes_decimal[:4]:0<4}") / 10000
|
|
104
122
|
minutes = int((minutes * 1000000) / 60)
|
|
105
123
|
return degrees + minutes
|
|
106
124
|
|
|
@@ -125,6 +143,8 @@ def _parse_str(nmea_data: str) -> str:
|
|
|
125
143
|
|
|
126
144
|
def _read_degrees(data: List[float], index: int, neg: str) -> float:
|
|
127
145
|
# This function loses precision with float32
|
|
146
|
+
if data[index] is None or data[index + 1] is None:
|
|
147
|
+
return None
|
|
128
148
|
x = data[index] / 1000000
|
|
129
149
|
if data[index + 1].lower() == neg:
|
|
130
150
|
x *= -1.0
|
|
@@ -136,12 +156,14 @@ def _read_deg_mins(data: List[str], index: int, neg: str) -> Tuple[int, float]:
|
|
|
136
156
|
# longitudes, which makes parsing tricky:
|
|
137
157
|
# for latitudes: ddmm,mmmm (0 - 7 decimal places, not zero padded)
|
|
138
158
|
# for longitudes: dddmm,mmmm (0 - 7 decimal places, not zero padded)
|
|
139
|
-
if
|
|
140
|
-
|
|
141
|
-
else:
|
|
142
|
-
int_part, minutes_decimal = data[index], 0
|
|
159
|
+
if data[index] is None or len(data[index]) < 3:
|
|
160
|
+
return None, None
|
|
143
161
|
|
|
144
162
|
# we need to parse from right to left, minutes can only have 2 digits
|
|
163
|
+
int_part, minutes_decimal = _split_decimal(data[index])
|
|
164
|
+
if int_part is None or len(int_part) < 3:
|
|
165
|
+
return None, None
|
|
166
|
+
|
|
145
167
|
minutes_int = int_part[-2:]
|
|
146
168
|
# the rest must be degrees which are either 2 or 3 digits
|
|
147
169
|
deg = int(int_part[:-2])
|
|
@@ -161,7 +183,8 @@ def _parse_talker(data_type: bytes) -> Tuple[bytes, bytes]:
|
|
|
161
183
|
return (data_type[:2], data_type[2:])
|
|
162
184
|
|
|
163
185
|
|
|
164
|
-
def _parse_data(sentence_type: int, data: List[str]) -> Optional[List]:
|
|
186
|
+
def _parse_data(sentence_type: int, data: List[str]) -> Optional[List]: # noqa: PLR0911
|
|
187
|
+
# PLR0911: each early return is a distinct, self-documenting rejection reason ...
|
|
165
188
|
"""Parse sentence data for the specified sentence type and
|
|
166
189
|
return a list of parameters in the correct format, or return None.
|
|
167
190
|
"""
|
|
@@ -196,8 +219,14 @@ def _parse_data(sentence_type: int, data: List[str]) -> Optional[List]:
|
|
|
196
219
|
else:
|
|
197
220
|
params.append(dti)
|
|
198
221
|
elif pti == "d":
|
|
199
|
-
# A number parseable as degrees
|
|
200
|
-
|
|
222
|
+
# A number parseable as degrees.
|
|
223
|
+
# Required field so unparseable or empty should reject whole sentence.
|
|
224
|
+
if nothing:
|
|
225
|
+
return None
|
|
226
|
+
parsed_degrees = _parse_degrees(dti)
|
|
227
|
+
if parsed_degrees is None:
|
|
228
|
+
return None
|
|
229
|
+
params.append(parsed_degrees)
|
|
201
230
|
elif pti == "D":
|
|
202
231
|
# A number parseable as degrees or Nothing
|
|
203
232
|
if nothing:
|
|
@@ -490,11 +519,32 @@ class GPS:
|
|
|
490
519
|
data_type = sentence[1:delimiter]
|
|
491
520
|
return (data_type, sentence[delimiter + 1 :])
|
|
492
521
|
|
|
522
|
+
@staticmethod
|
|
523
|
+
def _parse_hhmmss(time_utc: Optional[str]) -> Optional[Tuple[int, int, int]]:
|
|
524
|
+
# Parse an NMEA hhmmss(.sss) field into (hours, mins, secs).
|
|
525
|
+
# Returns None if the field is absent, short, or non-numeric.
|
|
526
|
+
if time_utc is None or len(time_utc) < 6 or not time_utc[0:6].isdigit():
|
|
527
|
+
return None
|
|
528
|
+
return (int(time_utc[0:2]), int(time_utc[2:4]), int(time_utc[4:6]))
|
|
529
|
+
|
|
530
|
+
@staticmethod
|
|
531
|
+
def _parse_ddmmyy(date: Optional[str]) -> Optional[Tuple[int, int, int]]:
|
|
532
|
+
# Parse an NMEA ddmmyy field into (day, month, year).
|
|
533
|
+
# Returns None if the field is absent, short, or non-numeric.
|
|
534
|
+
if date is None or len(date) < 6 or not date[0:6].isdigit():
|
|
535
|
+
return None
|
|
536
|
+
return (int(date[0:2]), int(date[2:4]), 2000 + int(date[4:6]))
|
|
537
|
+
|
|
493
538
|
def _update_timestamp_utc(self, time_utc: str, date: Optional[str] = None) -> None:
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
539
|
+
parsed_time = self._parse_hhmmss(time_utc)
|
|
540
|
+
if parsed_time is None:
|
|
541
|
+
# Malformed time field; leave the previous timestamp untouched.
|
|
542
|
+
return
|
|
543
|
+
hours, mins, secs = parsed_time
|
|
544
|
+
|
|
545
|
+
parsed_date = self._parse_ddmmyy(date)
|
|
546
|
+
if parsed_date is None:
|
|
547
|
+
# No usable date field; carry forward the date we already have.
|
|
498
548
|
if self.timestamp_utc is None:
|
|
499
549
|
day, month, year = 0, 0, 0
|
|
500
550
|
else:
|
|
@@ -502,9 +552,7 @@ class GPS:
|
|
|
502
552
|
month = self.timestamp_utc.tm_mon
|
|
503
553
|
year = self.timestamp_utc.tm_year
|
|
504
554
|
else:
|
|
505
|
-
day =
|
|
506
|
-
month = int(date[2:4])
|
|
507
|
-
year = 2000 + int(date[4:6])
|
|
555
|
+
day, month, year = parsed_date
|
|
508
556
|
|
|
509
557
|
self.timestamp_utc = time.struct_time((year, month, day, hours, mins, secs, 0, 0, -1))
|
|
510
558
|
|
|
@@ -12,7 +12,7 @@ requires = [
|
|
|
12
12
|
[project]
|
|
13
13
|
name = "adafruit-circuitpython-gps"
|
|
14
14
|
description = "CircuitPython library for GPS modules."
|
|
15
|
-
version = "3.11.
|
|
15
|
+
version = "3.11.9"
|
|
16
16
|
readme = "README.rst"
|
|
17
17
|
authors = [
|
|
18
18
|
{name = "Adafruit Industries", email = "circuitpython@adafruit.com"}
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/tests/adafruit_gps_test.py
RENAMED
|
@@ -301,7 +301,7 @@ def test_GPS_send_command_with_checksum(capsys):
|
|
|
301
301
|
gps.send_command(command=b"$PMTK001,314,3\r\n", add_checksum=True)
|
|
302
302
|
out, err = capsys.readouterr()
|
|
303
303
|
assert not err
|
|
304
|
-
assert out == ("b'$'
|
|
304
|
+
assert out == ("b'$'b'$PMTK001,314,3\\r\\n'b'*'b'15'b'\\r\\n'")
|
|
305
305
|
|
|
306
306
|
|
|
307
307
|
def test_GPS_send_command_without_checksum(capsys):
|
|
@@ -309,7 +309,7 @@ def test_GPS_send_command_without_checksum(capsys):
|
|
|
309
309
|
gps.send_command(command=b"$PMTK001,314,3\r\n", add_checksum=False)
|
|
310
310
|
out, err = capsys.readouterr()
|
|
311
311
|
assert not err
|
|
312
|
-
assert out == ("b'$'
|
|
312
|
+
assert out == ("b'$'b'$PMTK001,314,3\\r\\n'b'\\r\\n'")
|
|
313
313
|
|
|
314
314
|
|
|
315
315
|
def test_GPS_update_from_GLL():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/.github/workflows/build.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/LICENSES/CC-BY-4.0.txt
RENAMED
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/LICENSES/Unlicense.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/_static/custom.css
RENAMED
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/_static/favicon.ico
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/api.rst.license
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/details.rst.license
RENAMED
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/examples.rst.license
RENAMED
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/index.rst.license
RENAMED
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/docs/requirements.txt
RENAMED
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_datalogging.py
RENAMED
|
File without changes
|
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_echotest.py
RENAMED
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_satellitefix.py
RENAMED
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_simpletest.py
RENAMED
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/examples/gps_time_source.py
RENAMED
|
File without changes
|
{adafruit_circuitpython_gps-3.11.7 → adafruit_circuitpython_gps-3.11.9}/optional_requirements.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|