Skip to content

Commit 583fbd0

Browse files
authored
Merge pull request #80 from gamblor21/precision
Add handling with extra precision in lat/long
2 parents 3e9b741 + 872ce6e commit 583fbd0

File tree

3 files changed

+46
-12
lines changed

3 files changed

+46
-12
lines changed

adafruit_gps.py

+29-5
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,15 @@ def _parse_degrees(nmea_data):
8080
# Where ddd is the degrees, mm.mmmm is the minutes.
8181
if nmea_data is None or len(nmea_data) < 3:
8282
return None
83-
raw = float(nmea_data)
84-
deg = raw // 100
85-
minutes = raw % 100
86-
return deg + minutes / 60
83+
# To avoid losing precision handle degrees and minutes separately
84+
# Return the final value as an integer. Further functions can parse
85+
# this into a float or separate parts to retain the precision
86+
raw = nmea_data.split(".")
87+
degrees = int(raw[0]) // 100 * 1000000 # the ddd
88+
minutes = int(raw[0]) % 100 # the mm.
89+
minutes += int(f"{raw[1][:4]:0<4}") / 10000
90+
minutes = int(minutes / 60 * 1000000)
91+
return degrees + minutes # return parsed string in the format dddmmmmmm
8792

8893

8994
def _parse_int(nmea_data):
@@ -105,12 +110,21 @@ def _parse_str(nmea_data):
105110

106111

107112
def _read_degrees(data, index, neg):
108-
x = data[index]
113+
# This function loses precision with float32
114+
x = data[index] / 1000000
109115
if data[index + 1].lower() == neg:
110116
x *= -1.0
111117
return x
112118

113119

120+
def _read_int_degrees(data, index, neg):
121+
deg = data[index] // 1000000
122+
minutes = data[index] % 1000000 / 10000
123+
if data[index + 1].lower() == neg:
124+
deg *= -1
125+
return (deg, minutes)
126+
127+
114128
def _parse_talker(data_type):
115129
# Split the data_type into talker and sentence_type
116130
if data_type[:1] == b"P": # Proprietary codes
@@ -208,7 +222,11 @@ def __init__(self, uart, debug=False):
208222
# Initialize null starting values for GPS attributes.
209223
self.timestamp_utc = None
210224
self.latitude = None
225+
self.latitude_degrees = None
226+
self.latitude_minutes = None # Use for full precision minutes
211227
self.longitude = None
228+
self.longitude_degrees = None
229+
self.longitude_minutes = None # Use for full precision minutes
212230
self.fix_quality = 0
213231
self.fix_quality_3d = 0
214232
self.satellites = None
@@ -424,9 +442,11 @@ def _parse_gll(self, data):
424442

425443
# Latitude
426444
self.latitude = _read_degrees(data, 0, "s")
445+
self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 0, "s")
427446

428447
# Longitude
429448
self.longitude = _read_degrees(data, 2, "w")
449+
self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 2, "w")
430450

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

463483
# Latitude
464484
self.latitude = _read_degrees(data, 2, "s")
485+
self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 2, "s")
465486

466487
# Longitude
467488
self.longitude = _read_degrees(data, 4, "w")
489+
self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 4, "w")
468490

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

499521
# Latitude
500522
self.latitude = _read_degrees(data, 1, "s")
523+
self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 1, "s")
501524

502525
# Longitude
503526
self.longitude = _read_degrees(data, 3, "w")
527+
self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 3, "w")
504528

505529
# GPS quality indicator
506530
# 0 - fix not available,

examples/gps_simpletest.py

+10
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@
8282
)
8383
print("Latitude: {0:.6f} degrees".format(gps.latitude))
8484
print("Longitude: {0:.6f} degrees".format(gps.longitude))
85+
print(
86+
"Precise Latitude: {:2.}{:2.4f} degrees".format(
87+
gps.latitude_degrees, gps.latitude_minutes
88+
)
89+
)
90+
print(
91+
"Precise Longitude: {:2.}{:2.4f} degrees".format(
92+
gps.longitude_degrees, gps.longitude_minutes
93+
)
94+
)
8595
print("Fix quality: {}".format(gps.fix_quality))
8696
# Some attributes beyond latitude, longitude and timestamp are optional
8797
# and might not be present. Check if they're None before trying to use!

tests/adafruit_gps_test.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
@pytest.mark.parametrize(
2222
("val", "exp"),
2323
(
24-
pytest.param("0023.456", 0.390933, id="leading zero"),
25-
pytest.param("6413.9369", 64.23228, id="regular value"),
26-
pytest.param("2747.416122087989", 27.79027, id="long value"),
24+
pytest.param("0023.456", 390933, id="leading zero"),
25+
pytest.param("6413.9369", 64232281, id="regular value"),
26+
pytest.param("2747.416122087989", 27790268, id="long value"),
2727
),
2828
)
2929
def test_parse_degrees(val, exp):
@@ -61,10 +61,10 @@ def test_parse_float_invalid(val):
6161
@pytest.mark.parametrize(
6262
("data", "neg", "exp"),
6363
(
64-
pytest.param([27.79027, "S"], "s", -27.79027, id="south negative"),
65-
pytest.param([64.23228, "N"], "s", 64.23228, id="north not negative"),
66-
pytest.param([123.4567, "W"], "w", -123.4567, id="west negative"),
67-
pytest.param([10.7891, "E"], "w", 10.7891, id="east not negative"),
64+
pytest.param([27790270, "S"], "s", -27.79027, id="south negative"),
65+
pytest.param([64232280, "N"], "s", 64.23228, id="north not negative"),
66+
pytest.param([123456700, "W"], "w", -123.4567, id="west negative"),
67+
pytest.param([10789100, "E"], "w", 10.7891, id="east not negative"),
6868
),
6969
)
7070
def test_read_degrees(data, neg, exp):

0 commit comments

Comments
 (0)