Skip to content

Commit

Permalink
Merge pull request #80 from gamblor21/precision
Browse files Browse the repository at this point in the history
Add handling with extra precision in lat/long
  • Loading branch information
dhalbert authored Jun 17, 2022
2 parents 3e9b741 + 872ce6e commit 583fbd0
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 12 deletions.
34 changes: 29 additions & 5 deletions adafruit_gps.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,15 @@ def _parse_degrees(nmea_data):
# Where ddd is the degrees, mm.mmmm is the minutes.
if nmea_data is None or len(nmea_data) < 3:
return None
raw = float(nmea_data)
deg = raw // 100
minutes = raw % 100
return deg + minutes / 60
# To avoid losing precision handle degrees and minutes separately
# Return the final value as an integer. Further functions can parse
# this into a float or separate parts to retain the precision
raw = nmea_data.split(".")
degrees = int(raw[0]) // 100 * 1000000 # the ddd
minutes = int(raw[0]) % 100 # the mm.
minutes += int(f"{raw[1][:4]:0<4}") / 10000
minutes = int(minutes / 60 * 1000000)
return degrees + minutes # return parsed string in the format dddmmmmmm


def _parse_int(nmea_data):
Expand All @@ -105,12 +110,21 @@ def _parse_str(nmea_data):


def _read_degrees(data, index, neg):
x = data[index]
# This function loses precision with float32
x = data[index] / 1000000
if data[index + 1].lower() == neg:
x *= -1.0
return x


def _read_int_degrees(data, index, neg):
deg = data[index] // 1000000
minutes = data[index] % 1000000 / 10000
if data[index + 1].lower() == neg:
deg *= -1
return (deg, minutes)


def _parse_talker(data_type):
# Split the data_type into talker and sentence_type
if data_type[:1] == b"P": # Proprietary codes
Expand Down Expand Up @@ -208,7 +222,11 @@ def __init__(self, uart, debug=False):
# Initialize null starting values for GPS attributes.
self.timestamp_utc = None
self.latitude = None
self.latitude_degrees = None
self.latitude_minutes = None # Use for full precision minutes
self.longitude = None
self.longitude_degrees = None
self.longitude_minutes = None # Use for full precision minutes
self.fix_quality = 0
self.fix_quality_3d = 0
self.satellites = None
Expand Down Expand Up @@ -424,9 +442,11 @@ def _parse_gll(self, data):

# Latitude
self.latitude = _read_degrees(data, 0, "s")
self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 0, "s")

# Longitude
self.longitude = _read_degrees(data, 2, "w")
self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 2, "w")

# UTC time of position
self._update_timestamp_utc(data[4])
Expand Down Expand Up @@ -462,9 +482,11 @@ def _parse_rmc(self, data):

# Latitude
self.latitude = _read_degrees(data, 2, "s")
self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 2, "s")

# Longitude
self.longitude = _read_degrees(data, 4, "w")
self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 4, "w")

# Speed over ground, knots
self.speed_knots = data[6]
Expand Down Expand Up @@ -498,9 +520,11 @@ def _parse_gga(self, data):

# Latitude
self.latitude = _read_degrees(data, 1, "s")
self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 1, "s")

# Longitude
self.longitude = _read_degrees(data, 3, "w")
self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 3, "w")

# GPS quality indicator
# 0 - fix not available,
Expand Down
10 changes: 10 additions & 0 deletions examples/gps_simpletest.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@
)
print("Latitude: {0:.6f} degrees".format(gps.latitude))
print("Longitude: {0:.6f} degrees".format(gps.longitude))
print(
"Precise Latitude: {:2.}{:2.4f} degrees".format(
gps.latitude_degrees, gps.latitude_minutes
)
)
print(
"Precise Longitude: {:2.}{:2.4f} degrees".format(
gps.longitude_degrees, gps.longitude_minutes
)
)
print("Fix quality: {}".format(gps.fix_quality))
# Some attributes beyond latitude, longitude and timestamp are optional
# and might not be present. Check if they're None before trying to use!
Expand Down
14 changes: 7 additions & 7 deletions tests/adafruit_gps_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
@pytest.mark.parametrize(
("val", "exp"),
(
pytest.param("0023.456", 0.390933, id="leading zero"),
pytest.param("6413.9369", 64.23228, id="regular value"),
pytest.param("2747.416122087989", 27.79027, id="long value"),
pytest.param("0023.456", 390933, id="leading zero"),
pytest.param("6413.9369", 64232281, id="regular value"),
pytest.param("2747.416122087989", 27790268, id="long value"),
),
)
def test_parse_degrees(val, exp):
Expand Down Expand Up @@ -61,10 +61,10 @@ def test_parse_float_invalid(val):
@pytest.mark.parametrize(
("data", "neg", "exp"),
(
pytest.param([27.79027, "S"], "s", -27.79027, id="south negative"),
pytest.param([64.23228, "N"], "s", 64.23228, id="north not negative"),
pytest.param([123.4567, "W"], "w", -123.4567, id="west negative"),
pytest.param([10.7891, "E"], "w", 10.7891, id="east not negative"),
pytest.param([27790270, "S"], "s", -27.79027, id="south negative"),
pytest.param([64232280, "N"], "s", 64.23228, id="north not negative"),
pytest.param([123456700, "W"], "w", -123.4567, id="west negative"),
pytest.param([10789100, "E"], "w", 10.7891, id="east not negative"),
),
)
def test_read_degrees(data, neg, exp):
Expand Down

0 comments on commit 583fbd0

Please sign in to comment.