Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions adafruit_gps.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
https://github.com/adafruit/circuitpython/releases

"""

import time
from micropython import const

Expand Down Expand Up @@ -61,7 +62,7 @@
# 0 - _GLL
"dcdcscC",
# 1 - _RMC
"scdcdcffsDCC",
"scDCDCFFsDCC",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One q: what does this change do?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya! as I recall this change is to ensure that _RMC sentences with missing location data will still get parsed and returned in case they contain other data, eg: datetime.

This string format is interpreted in _parse_data below and generally capital letters indicate an optional value and lowcase indicate a required value. (rereading the code now, this is clearly the case for c vs C but I'm uncertain if the changes to d and f are strictly necessary).

This string in particular corresponds to the RMC format (ie: table 1-11 in this pdf: https://cdn.sparkfun.com/assets/a/3/2/f/a/NMEA_Reference_Manual-Rev2.1-Dec07.pdf ). The changed characters mark latitude, N/S indicator, longitude, E/W indicator and speed+heading as optional values. These values are all absent if the chip has no GPS fix. But other values (eg: UTC time indicated by the first s) may still be present if the chip has an onboard battery clock. And in such a case a partial sentence can now be returned.

# 2 - _GGA
"sdcdciiffsfsIS",
# 3 - _GSA
Expand All @@ -77,7 +78,7 @@
# 8 - _GSV19
"iiiiiiIiiiIiiiIiiiI",
# 9 - _RMC_4_1
"scdcdcffsDCCC",
"scDCDCFFsDCCC",
# 10 - _VTG
"fcFCfcfcC",
)
Expand Down Expand Up @@ -588,6 +589,7 @@ def _parse_rmc(self, data: List[str]) -> bool:
self.fix_quality = 1
else:
self.fix_quality = 0
return True # break early since no fix means no following values will be populated

# Latitude
self.latitude = _read_degrees(parsed_data, 2, "s")
Expand Down
25 changes: 25 additions & 0 deletions tests/adafruit_gps_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,31 @@ def test_GPS_update_timestamp_timestamp_utc_was_not_none_new_date_none():
assert gps.timestamp_utc == exp_struct


def test_GPS_update_time_from_RTC_without_fix():
r = b"$GPRMC,210648.000,V,,,,,0.71,105.86,050425,,,N*4E\r\n"
with mock.patch.object(GPS, "readline", return_value=r):
gps = GPS(uart=UartMock())
assert gps.update() is True
exp_time = time.struct_time((2025, 4, 5, 21, 6, 48, 0, 0, -1))
assert gps.has_fix is False
assert gps.timestamp_utc == exp_time
assert gps.datetime == exp_time
assert gps.nmea_sentence == "$GPRMC,210648.000,V,,,,,0.71,105.86,050425,,,N*4E"


def test_GPS_update_time_from_RTC_without_fix_RMC_4_1():
# RMC_4_1 has an extra "Mode" parameter right before the checksum.
r = b"$GPRMC,210648.000,V,,,,,0.71,105.86,050425,,,A,N*23\r\n"
with mock.patch.object(GPS, "readline", return_value=r):
gps = GPS(uart=UartMock())
assert gps.update() is True
exp_time = time.struct_time((2025, 4, 5, 21, 6, 48, 0, 0, -1))
assert gps.has_fix is False
assert gps.timestamp_utc == exp_time
assert gps.datetime == exp_time
assert gps.nmea_sentence == "$GPRMC,210648.000,V,,,,,0.71,105.86,050425,,,A,N*23"


def test_GPS_update_with_unknown_talker():
r = b"$XYRMC,215032.086,A,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A*7c\r\n"
with mock.patch.object(GPS, "readline", return_value=r):
Expand Down