From 9e029511def2560979485b7189557daaef454b6c Mon Sep 17 00:00:00 2001 From: tomas-fryza Date: Mon, 24 Feb 2025 12:12:53 +0100 Subject: [PATCH] Methods _parse_gpgll, _parse_gpvtg added to Adafruit GPS class --- examples/11-gps/adafruit_gps.py | 120 ++++++++++++++++++------ examples/11-gps/main.py | 118 ------------------------ examples/11-gps/micropython-gps.py | 107 ++++++++++++---------- examples/11-gps/rtc-from-gps.py | 73 +++++++++++++++ examples/11-gps/test_sentences.py | 141 +++++++++++++++++++++++++++++ 5 files changed, 365 insertions(+), 194 deletions(-) delete mode 100644 examples/11-gps/main.py create mode 100644 examples/11-gps/rtc-from-gps.py create mode 100644 examples/11-gps/test_sentences.py diff --git a/examples/11-gps/adafruit_gps.py b/examples/11-gps/adafruit_gps.py index 03b3ab6..57c2eb2 100644 --- a/examples/11-gps/adafruit_gps.py +++ b/examples/11-gps/adafruit_gps.py @@ -26,7 +26,11 @@ GPS parsing module. Can parse simple NMEA data sentences from serial GPS modules to read latitude, longitude, and more. -* Author(s): Tony DiCola, Alexandre Marquet. +* Author(s): Tony DiCola, Alexandre Marque + https://github.com/alexmrqt/Adafruit_CircuitPython_GPS + Tomas Fryza: + 2025-02-25: Added _parse_gpgll, _parse_gpvtg + 2025-02-25: Small modifs Implementation Notes -------------------- @@ -42,8 +46,6 @@ https://github.com/micropython/micropython """ -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/alexmrqt/Adafruit_CircuitPython_GPS.git" # Internal helper parsing functions. # These handle input that might be none or null and return none instead of @@ -87,8 +89,8 @@ def __init__(self, uart): self.horizontal_dilution = None self.altitude_m = None self.height_geoid = None - self.velocity_knots = None - self.speed_knots = None + # self.velocity_knots = None + self.speed = None self.track_angle_deg = None def update(self): @@ -97,16 +99,21 @@ def update(self): nothing new was received. """ # Grab a sentence and check its data type to call the appropriate - # parsing function. + # parsing function sentence = self._parse_sentence() if sentence is None: return False data_type, args = sentence data_type = data_type.upper() + if data_type == b'GPGGA': # GGA, 3d location fix self._parse_gpgga(args) elif data_type == b'GPRMC': # RMC, minimum location info self._parse_gprmc(args) + elif data_type == b'GPGLL': # GLL, geographic position + self._parse_gpgll(args) + elif data_type == b'GPVTG': # VTG, track and ground speed + self._parse_gpvtg(args) return True def send_command(self, command, add_checksum=True): @@ -133,6 +140,7 @@ def has_fix(self): def _parse_sentence(self): # Parse any NMEA sentence that is available. sentence = self._uart.readline() + # print(sentence) if sentence is None or sentence == b'' or len(sentence) < 1: return None sentence = sentence.strip() @@ -157,11 +165,12 @@ def _parse_sentence(self): def _parse_gpgga(self, args): # Parse the arguments (everything after data type) for NMEA GPGGA - # 3D location fix sentence. + # 3D location fix sentence data = args.split(b',') if data is None or len(data) != 14: - return # Unexpected number of params. - # Parse fix time. + return # Unexpected number of params + + # Parse fix time time_utc = int(_parse_float(data[0])) if time_utc is not None: hours = time_utc // 10000 @@ -174,7 +183,8 @@ def _parse_gpgga(self, args): self.timestamp_utc[2], hours, mins, secs, 0, 0) else: self.timestamp_utc = (0, 0, 0, hours, mins, secs, 0, 0) - # Parse latitude and longitude. + + # Parse latitude and longitude self.latitude = _parse_degrees(data[1]) if self.latitude is not None and \ data[2] is not None and data[2].lower() == b's': @@ -183,7 +193,8 @@ def _parse_gpgga(self, args): if self.longitude is not None and \ data[4] is not None and data[4].lower() == b'w': self.longitude *= -1.0 - # Parse out fix quality and other simple numeric values. + + # Parse out fix quality and other simple numeric values self.fix_quality = _parse_int(data[5]) self.satellites = _parse_int(data[6]) self.horizontal_dilution = _parse_float(data[7]) @@ -192,29 +203,32 @@ def _parse_gpgga(self, args): def _parse_gprmc(self, args): # Parse the arguments (everything after data type) for NMEA GPRMC - # minimum location fix sentence. + # minimum location fix sentence data = args.split(b',') if data is None or len(data) < 11 or data[0] is None: - return # Unexpected number of params. - # Parse fix time. + return # Unexpected number of params + + # Parse fix time time_utc = int(_parse_float(data[0])) if time_utc is not None: hours = time_utc // 10000 mins = (time_utc // 100) % 100 secs = time_utc % 100 - # Set or update time to a friendly python time struct. + # Set or update time to a friendly python time struct if self.timestamp_utc is not None: self.timestamp_utc = ( self.timestamp_utc[0], self.timestamp_utc[1], self.timestamp_utc[2], hours, mins, secs, 0, 0) else: self.timestamp_utc = (0, 0, 0, hours, mins, secs, 0, 0) - # Parse status (active/fixed or void). + + # Parse status (active/fixed or void) status = data[1] self.fix_quality = 0 if status is not None and status.lower() == b'a': self.fix_quality = 1 - # Parse latitude and longitude. + + # Parse latitude and longitude self.latitude = _parse_degrees(data[2]) if self.latitude is not None and \ data[3] is not None and data[3].lower() == b's': @@ -223,10 +237,12 @@ def _parse_gprmc(self, args): if self.longitude is not None and \ data[5] is not None and data[5].lower() == b'w': self.longitude *= -1.0 - # Parse out speed and other simple numeric values. - self.speed_knots = _parse_float(data[6]) + + # Parse out speed and other simple numeric values + self.speed = _parse_float(data[6]) * 1.852 # kilometers per hour = knots × 1.852 self.track_angle_deg = _parse_float(data[7]) - # Parse date. + + # Parse date if data[8] is not None and len(data[8]) == 6: day = int(data[8][0:2]) month = int(data[8][2:4]) @@ -234,13 +250,61 @@ def _parse_gprmc(self, args): # This is a problem with the NMEA # spec and not this code. if self.timestamp_utc is not None: - # Replace the timestamp with an updated one. + # Replace the timestamp with an updated one self.timestamp_utc = (year, month, day, - self.timestamp_utc[3], - self.timestamp_utc[4], - self.timestamp_utc[5], - 0, - 0) + self.timestamp_utc[3], + self.timestamp_utc[4], + self.timestamp_utc[5], + 0, + 0) else: - # Time hasn't been set so create it. - self.timestamp_utc = (year, month, day, 0, 0, 0, 0, 0) \ No newline at end of file + # Time hasn't been set so create it + self.timestamp_utc = (year, month, day, 0, 0, 0, 0, 0) + + def _parse_gpgll(self, args): + # Parse the arguments (everything after data type) for NMEA GPGLL + # geographic position + data = args.split(b',') + if data is None or len(data) < 6 or data[0] is None: + return # Unexpected number of params + + # Parse latitude and longitude + self.latitude = _parse_degrees(data[0]) + if self.latitude is not None and \ + data[1] is not None and data[1].lower() == b's': + self.latitude *= -1.0 + self.longitude = _parse_degrees(data[2]) + if self.longitude is not None and \ + data[3] is not None and data[3].lower() == b'w': + self.longitude *= -1.0 + + # Parse fix time + time_utc = int(_parse_float(data[4])) + if time_utc is not None: + hours = time_utc // 10000 + mins = (time_utc // 100) % 100 + secs = time_utc % 100 + # Set or update time to a friendly python time struct + if self.timestamp_utc is not None: + self.timestamp_utc = ( + self.timestamp_utc[0], self.timestamp_utc[1], + self.timestamp_utc[2], hours, mins, secs, 0, 0) + else: + self.timestamp_utc = (0, 0, 0, hours, mins, secs, 0, 0) + + # Parse status (active/fixed or void) + status = data[5] + self.fix_quality = 0 + if status is not None and status.lower() == b'a': + self.fix_quality = 1 + + def _parse_gpvtg(self, args): + # Parse the arguments (everything after data type) for NMEA GPVTG + # track made good and ground speed + data = args.split(b',') + if data is None or len(data) < 8 or data[0] is None: + return # Unexpected number of params + + # Parse out speed and other simple numeric values + self.track_angle_deg = _parse_float(data[0]) + self.speed = _parse_float(data[6]) # kilometers/hour diff --git a/examples/11-gps/main.py b/examples/11-gps/main.py deleted file mode 100644 index 34363d3..0000000 --- a/examples/11-gps/main.py +++ /dev/null @@ -1,118 +0,0 @@ -# https://RandomNerdTutorials.com/micropython-esp32-neo-6m-gps/ -# https://microcontrollerslab.com/neo-6m-gps-module-esp32-micropython/ - -# GPGGA: -# https://docs.novatel.com/OEM7/Content/Logs/GPGGA.htm?tocpath=Commands%20%2526%20Logs%7CLogs%7CGNSS%20Logs%7C_____59 -# -# GPRMC: -# https://docs.novatel.com/OEM7/Content/Logs/GPRMC.htm?tocpath=Commands%20%2526%20Logs%7CLogs%7CGNSS%20Logs%7C_____69 - -from machine import Pin, UART -import utime, time - -gpsModule = UART(2, baudrate=9600) # tx=17 (D10), rx=16 (D11) -print(gpsModule) - -buff = bytearray(255) - -TIMEOUT = False -FIX_STATUS = False - -latitude = "" -longitude = "" -satellites = "" -GPStime = "" -GPSdate = "" - - -def getGPS(gpsModule): - global FIX_STATUS, TIMEOUT, latitude, longitude, satellites, GPStime, GPSdate - - timeout = time.time() + 8 - - while True: - gpsModule.readline() - buff = str(gpsModule.readline()) - parts = buff.split(',') -# print(parts, len(parts)) - - # b'$GPGGA,185406.000,4911.7140,N,01646.2131,E,2,06,1.76,263.0,M,43.4,M,,*\n' - if (parts[0] == "b'$GPGGA" and len(parts) == 15): - if(parts[1] and parts[2] and parts[3] and parts[4] and parts[5] and parts[6] and parts[7]): - print() - print(buff) - - latitude = convertToDegree(parts[2]) - if (parts[3] == 'S'): - latitude = -latitude - longitude = convertToDegree(parts[4]) - if (parts[5] == 'W'): - longitude = -longitude - satellites = parts[7] - GPStime = parts[1][0:2] + ":" + parts[1][2:4] + ":" + parts[1][4:] - GPSdate = "" - FIX_STATUS = True - break - - # b'$GPRMC,185420.000,A,4911.7142,N,01646.2135,E,0.36,228.26,180225,,,D*69\r\n' - if (parts[0] == "b'$GPRMC" and len(parts) == 13): - if(parts[1] and parts[3] and parts[4] and parts[5] and parts[6] and parts[9]): -# print() -# print(buff) - - latitude = convertToDegree(parts[3]) - if (parts[4] == 'S'): - latitude = -latitude - longitude = convertToDegree(parts[5]) - if (parts[6] == 'W'): - longitude = -longitude - - GPStime = parts[1][0:2] + ":" + parts[1][2:4] + ":" + parts[1][4:] - GPSdate = parts[9][0:2] + "." + parts[9][2:4] + ".20" + parts[9][4:] - - FIX_STATUS = True - break - - if (time.time() > timeout): - TIMEOUT = True - break - utime.sleep_ms(500) - - -def convertToDegree(RawDegrees): - - RawAsFloat = float(RawDegrees) - firstdigits = int(RawAsFloat/100) - nexttwodigits = RawAsFloat - float(firstdigits*100) - - Converted = float(firstdigits + nexttwodigits/60.0) - Converted = '{0:.6f}'.format(Converted) - return str(Converted) - - -print("\r\nPress `Ctrl+C` to stop") -print("date;time_utc;latitude;longitude") - -try: - while True: - getGPS(gpsModule) - - if(FIX_STATUS == True): - print(f"{GPSdate};{GPStime};{latitude};{longitude}") -# print(f"Date : {GPSdate}") -# print(f"Time (UTC): {GPStime}") -# print(f"Latitude : {latitude}") -# print(f"Longitude : {longitude}") - # print("Satellites: " +satellites) - - FIX_STATUS = False - - if(TIMEOUT == True): - # print("No GPS data is found.") - TIMEOUT = False - -except KeyboardInterrupt: - # This part runs when Ctrl+C is pressed - print("Program stopped. Exiting...") - - # Optional cleanup code diff --git a/examples/11-gps/micropython-gps.py b/examples/11-gps/micropython-gps.py index 3451e17..176ad90 100644 --- a/examples/11-gps/micropython-gps.py +++ b/examples/11-gps/micropython-gps.py @@ -23,13 +23,13 @@ # https://cdn-shop.adafruit.com/datasheets/PMTK_A11.pdf # Turn on the basic GGA and RMC info (what you typically want) -gps.send_command('PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0') +# gps.send_command('PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0') # Turn on just minimum info (RMC only, location): # gps.send_command('PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0') # Turn off everything: # gps.send_command('PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0') # Turn on everything (not all of it is parsed!) -# gps.send_command('PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0') +gps.send_command('PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0') # Set update rate to once a second (1hz) which is what you typically want. gps.send_command('PMTK220,1000') @@ -43,49 +43,60 @@ # Main loop runs forever printing the location, etc. every second. last_print = time.ticks_ms() -while True: - # Make sure to call gps.update() every loop iteration and at least twice - # as fast as data comes from the GPS unit (usually every second). - # This returns a bool that's true if it parsed new data (you can ignore it - # though if you don't care and instead look at the has_fix property). - status = gps.update() - - # Every second print out current location details if there's a fix. - current = time.ticks_ms() - - if (time.ticks_diff(current, last_print) >= 1000) and status: - last_print = current - print(last_print, status) - - if not gps.has_fix: - # Try again if we don't have a fix yet. - print('Waiting for fix...') - continue - - # We have a fix! (gps.has_fix is true) - # Print out details about the fix like location, date, etc. - print('=' * 40) # Print a separator line. - print('Fix timestamp: {}/{}/{} {:02}:{:02}:{:02}'.format( - gps.timestamp_utc[1], # Grab parts of the time from the - gps.timestamp_utc[2], # struct_time object that holds - gps.timestamp_utc[0], # the fix time. Note you might - gps.timestamp_utc[3], # not get all data like year, day, - gps.timestamp_utc[4], # month! - gps.timestamp_utc[5])) - print('Latitude: {} degrees'.format(gps.latitude)) - print('Longitude: {} degrees'.format(gps.longitude)) - 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! - if gps.satellites is not None: - print('# satellites: {}'.format(gps.satellites)) - if gps.altitude_m is not None: - print('Altitude: {} meters'.format(gps.altitude_m)) - if gps.track_angle_deg is not None: - print('Speed: {} knots'.format(gps.speed_knots)) - if gps.track_angle_deg is not None: - print('Track angle: {} degrees'.format(gps.track_angle_deg)) - if gps.horizontal_dilution is not None: - print('Horizontal dilution: {}'.format(gps.horizontal_dilution)) - if gps.height_geoid is not None: - print('Height geo ID: {} meters'.format(gps.height_geoid)) +print("\nPress `Ctrl+C` to stop\n") +print("datetime;latitude;longitude;speed;track;alt") + +try: + while True: + # Make sure to call gps.update() every loop iteration and at least twice + # as fast as data comes from the GPS unit (usually every second). + # This returns a bool that's true if it parsed new data (you can ignore it + # though if you don't care and instead look at the has_fix property). + time.sleep_ms(500) + status = gps.update() + + # Every second print out current location details if there's a fix. + current = time.ticks_ms() + + if (time.ticks_diff(current, last_print) >= 1000): + last_print = current + print(f"{gps.timestamp_utc};{gps.latitude};{gps.longitude};{gps.speed};{gps.track_angle_deg};{gps.altitude_m}") + + if not gps.has_fix: + # Try again if we don't have a fix yet. + print('Waiting for fix...') + continue + + # We have a fix! (gps.has_fix is true) + # Print out details about the fix like location, date, etc. + # print(f"Fix timestamp: {gps.timestamp_utc}") + # print('Fix timestamp: {}-{}-{} {:02}:{:02}:{:02}'.format( + # gps.timestamp_utc[0], # Grab parts of the time from the + # gps.timestamp_utc[1], # struct_time object that holds + # gps.timestamp_utc[2], # the fix time. Note you might + # gps.timestamp_utc[3], # not get all data like year, day, + # gps.timestamp_utc[4], # month! + # gps.timestamp_utc[5])) + # print('Latitude: {} degrees'.format(gps.latitude)) + # print('Longitude: {} degrees'.format(gps.longitude)) + # 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! + # if gps.satellites is not None: + # print('# satellites: {}'.format(gps.satellites)) + # if gps.altitude_m is not None: + # print('Altitude: {} meters'.format(gps.altitude_m)) + # if gps.speed is not None: + # print('Speed: {} km/h'.format(gps.speed)) + # if gps.track_angle_deg is not None: + # print('Track angle: {} degrees'.format(gps.track_angle_deg)) + # if gps.horizontal_dilution is not None: + # print('Horizontal dilution: {}'.format(gps.horizontal_dilution)) + # if gps.height_geoid is not None: + # print('Height geo ID: {} meters'.format(gps.height_geoid)) + +except KeyboardInterrupt: + # This part runs when Ctrl+C is pressed + print("\nProgram stopped. Exiting...") + + # Optional cleanup code diff --git a/examples/11-gps/rtc-from-gps.py b/examples/11-gps/rtc-from-gps.py new file mode 100644 index 0000000..487121e --- /dev/null +++ b/examples/11-gps/rtc-from-gps.py @@ -0,0 +1,73 @@ +from machine import Pin, UART, RTC +import time + +# Initialize RTC (ESP32 internal RTC) +rtc = RTC() +uart = UART(2, baudrate=9600) # tx=17 (D10), rx=16 (D11) +led = Pin(2, Pin.OUT) + +# Helper function to parse GPS NMEA sentences (basic for GPRMC) +def parse_gprmc(parts): + if parts[2] == 'A': # Check if fix is valid + # Extract UTC time (hhmmss.sss) and date (ddmmyy) + time_str = parts[1] + date_str = parts[9] + hours = int(time_str[0:2]) + minutes = int(time_str[2:4]) + seconds = int(time_str[4:6]) + year = 2000 + int(date_str[4:6]) + month = int(date_str[2:4]) + day = int(date_str[0:2]) + + # Return parsed date and time as a tuple + return (year, month, day, hours, minutes, seconds) + return None + +# Function to read GPS and update RTC +def update_rtc_from_gps(): + while True: + # Read data from GPS + gps_data = str(uart.readline()) + data = gps_data.split(',') + + if (data[0] == "b'$GPRMC" and len(data) == 13): + if (data[1] and data[2] and data[9]): + # Parse the GPS data + gps_time = parse_gprmc(data) + if gps_time: + # Set RTC using GPS time (UTC) + rtc.datetime((gps_time[0], gps_time[1], gps_time[2], 0, gps_time[3], gps_time[4], gps_time[5], 0)) + print(f" Done") + return + + print(".", end="") + time.sleep(1) + +# Main function +def main(): + (year, month, day, wday, hrs, mins, secs, subsecs) = rtc.datetime() + print(f"Current RTC: {year}-{month}-{day} {hrs}:{mins}:{secs}.{subsecs}") + + print("Waiting for GPS signal (NMEA GPRMC sentence)...", end="") + led.on() + update_rtc_from_gps() + led.off() + + print("\nStart using RTC. Press `Ctrl+C` to stop\n") + + try: + # Forever loop + while True: + (year, month, day, wday, hrs, mins, secs, subsecs) = rtc.datetime() + print(f"{year}-{month}-{day} {hrs}:{mins}:{secs}.{subsecs}") + time.sleep(1) + + except KeyboardInterrupt: + # This part runs when Ctrl+C is pressed + print("\nProgram stopped. Exiting...") + + # Optional cleanup code + led.off() + +# Run the main function +main() diff --git a/examples/11-gps/test_sentences.py b/examples/11-gps/test_sentences.py new file mode 100644 index 0000000..d80b3b1 --- /dev/null +++ b/examples/11-gps/test_sentences.py @@ -0,0 +1,141 @@ +# https://RandomNerdTutorials.com/micropython-esp32-neo-6m-gps/ +# https://microcontrollerslab.com/neo-6m-gps-module-esp32-micropython/ + +# NMEA sentence information +# https://aprs.gids.nl/nmea/ + +# GPGGA: +# https://docs.novatel.com/OEM7/Content/Logs/GPGGA.htm?tocpath=Commands%20%2526%20Logs%7CLogs%7CGNSS%20Logs%7C_____59 +# +# GPRMC: +# https://docs.novatel.com/OEM7/Content/Logs/GPRMC.htm?tocpath=Commands%20%2526%20Logs%7CLogs%7CGNSS%20Logs%7C_____69 + +from machine import Pin, UART +import utime, time + +gpsModule = UART(2, baudrate=9600) # tx=17 (D10), rx=16 (D11) +print(gpsModule) + +buff = bytearray(255) + +FIX_STATUS = False + +utc = "" +date = "" +lat = "" +lon = "" +sats = "" +alt = "" +speed = 0.0 +track = "" + + +def getGPS(gpsModule): + global FIX_STATUS, TIMEOUT, utc, date, lat, lon, sats, alt, speed, track + + timeout = time.time() + 8 + + while True: + # gpsModule.readline() + buff = str(gpsModule.readline()) + data = buff.split(',') + + # if (data[0] != "b'$GPGGA" and + # data[0] != "b'$GPRMC" and + # data[0] != "b'$GPGSA" and + # data[0] != "b'$GPGSV" and + # data[0] != "b'$GPGLL" and + # data[0] != "b'$GPVTG"): + # print(data[0], len(data)) + # print(data) + + # GPGGA (GPS fix data and undulation) + if (data[0] == "b'$GPGGA" and len(data) == 15): + if (data[1] and data[2] and data[3] and data[4] and data[5] and data[7] and data[9]): + + utc = data[1][0:2] + ":" + data[1][2:4] + ":" + data[1][4:] + + lat = convertToDegree(data[2]) + lat += data[3] + lon = convertToDegree(data[4]) + lon += data[5] + + sats = data[7] # Number of satellites in use + alt = data[9] # Antenna altitude above/below mean sea level (in metres) + + FIX_STATUS = True + break + + # GPRMC (Recommended minimum specific GPS/transit data) + if (data[0] == "b'$GPRMC" and len(data) == 13): + if (data[1] and data[3] and data[4] and data[5] and + data[6] and data[7] and data[8] and data[9]): + + utc = data[1][0:2] + ":" + data[1][2:4] + ":" + data[1][4:] + date = "20"+data[9][4:] +"-"+ data[9][2:4] +"-"+ data[9][0:2] + + lat = convertToDegree(data[3]) + lat += data[4] + lon = convertToDegree(data[5]) + lon += data[6] + + speed = float(data[7]) * 1.852 # kilometers per hour = knots × 1.852 + track = data[8] # Degrees + + FIX_STATUS = True + break + + # GPGLL (Geographic position) + if (data[0] == "b'$GPGLL" and len(data) == 8): + if (data[1] and data[2] and data[3] and data[4] and + data[5]): + + utc = data[5][0:2] + ":" + data[5][2:4] + ":" + data[5][4:] + + lat = convertToDegree(data[1]) + lat += data[2] + lon = convertToDegree(data[3]) + lon += data[4] + + FIX_STATUS = True + break + + # $GPVTG, Track made good and ground speed + if (data[0] == "b'$GPVTG" and len(data) == 10): + if (data[1] and data[7]): + + track = data[1] # Degrees + speed = float(data[7]) # kilometers per hour + + FIX_STATUS = True + break + + utime.sleep_ms(500) + + +def convertToDegree(RawDegrees): + RawAsFloat = float(RawDegrees) + firstdigits = int(RawAsFloat/100) + nexttwodigits = RawAsFloat - float(firstdigits*100) + + Converted = float(firstdigits + nexttwodigits/60.0) + Converted = '{0:.6f}'.format(Converted) + return str(Converted) + + +print("\nPress `Ctrl+C` to stop\n") +print("datetime;latitude;longitude;sats;alt;speed;track") + +try: + while True: + getGPS(gpsModule) + + if(FIX_STATUS == True): + print(f"{date} {utc};{lat};{lon};{sats};{alt};{speed:.2f};{track}") + FIX_STATUS = False + +except KeyboardInterrupt: + # This part runs when Ctrl+C is pressed + print("\nProgram stopped. Exiting...") + + # Optional cleanup code