Last active
December 5, 2018 19:40
-
-
Save TheRayTracer/2d5ef07ac08b77e7388732c1906aa837 to your computer and use it in GitHub Desktop.
A simple snippet of Python to parse a few NMEA sentences read from a MTK3339 device on a Raspberry Pi v3.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import serial #sudo apt-get install python-serial | |
import time | |
import RPi.GPIO as gpio | |
PMTK_ACK = '$PMTK001' | |
PMTK_INVALID = '0' | |
PMTK_UNSUPPORTED = '1' | |
PMTK_VALID_FAILED = '2' | |
PMTK_VALID_SUCCESS = '3' | |
PMTK_SET_NMEA_UPDATE_1HZ = b'$PMTK220,1000*1F\r\n' | |
PMTK_SET_NMEA_UPDATE_5HZ = b'$PMTK220,200*2C\r\n' | |
PMTK_SET_NMEA_UPDATE_10HZ = b'$PMTK220,100*2F\r\n' | |
PMTK_SET_NMEA_OUTPUT_RMC_ONLY = b'$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29\r\n' | |
PMTK_SET_NMEA_OUTPUT_RMC_GGA = b'$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n' | |
PMTK_SET_NMEA_OUTPUT_ALL_ON = b'$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1*28\r\n' | |
PMTK_SET_NMEA_OUTPUT_ALL_OFF = b'$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n' | |
PMTK_ERASE_FLASH = b'$PMTK184,1*22\r\n' | |
PMTK_STOP_LOGGER = b'$PMTK185,1*23\r\n' | |
PMTK_STRT_LOGGER = b'$PMTK186,1*20\r\n' | |
PMTK_DUMP_LOGGER = b'$PMTK622,0*28\r\n' | |
class MTK3339: | |
def __init__(self, device, enable_pin): | |
self.utc = 'unknown' | |
self.lat_decimal = 0 | |
self.lng_decimal = 0 | |
self.en = enable_pin | |
# Setup GPIO. | |
gpio.setmode(gpio.BCM) | |
gpio.setup(self.en, gpio.OUT) | |
gpio.output(self.en, gpio.HIGH) | |
# Setup the serial device. | |
self.tx = serial.Serial(port = device, baudrate = 9600, timeout = 3.0) | |
time.sleep(0.2) | |
if self.IsOpen(): | |
print('GPS device successfully connected.') | |
self.Enable() | |
self.TurnOffAllOutput() # Turn of all output to receive a clear ACK. | |
self.SetFastOutput() | |
return | |
def Remove(self): | |
self.tx.close() | |
if self.IsOpen(): | |
print('GPS device failed to close.') | |
else: | |
print('GPS device successfully closed.') | |
gpio.cleanup() | |
return | |
def Enable(self, enable = True): | |
if enable: | |
gpio.output(self.en, gpio.HIGH) | |
else: | |
gpio.output(self.en, gpio.LOW) | |
return | |
def IsOpen(self): | |
return self.tx.isOpen() | |
def __AckFlag(self, command, timeout_seconds = 2.0): | |
flg = PMTK_INVALID | |
ack = False | |
tout = time.time() + timeout_seconds | |
while ack == False and time.time() < tout: | |
line = self.GetNextSentence().split(',') | |
ack = (line[0].startswith(PMTK_ACK) and line[1].startswith(command)) | |
if ack: | |
flg = line[2].split('*')[0] # Fetch flag value since we have an ACK for the correct command. | |
return flg | |
def SetSlowOutput(self): | |
self.tx.write(PMTK_SET_NMEA_UPDATE_1HZ) | |
return self.__AckFlag('220') | |
def SetFastOutput(self): | |
self.tx.write(PMTK_SET_NMEA_UPDATE_5HZ) | |
return self.__AckFlag('220') | |
def SetVeryFastOutput(self): | |
self.tx.write(PMTK_SET_NMEA_UPDATE_10HZ) | |
return self.__AckFlag('220') | |
def TurnOffAllOutput(self): | |
self.tx.write(PMTK_SET_NMEA_OUTPUT_ALL_OFF) | |
return self.__AckFlag('314') | |
def TurnOnAllOutput(self): | |
self.tx.write(PMTK_SET_NMEA_OUTPUT_ALL_ON) | |
return self.__AckFlag('314') | |
def TurnOnRmcGgaOutput(self): | |
self.tx.write(PMTK_SET_NMEA_OUTPUT_RMC_GGA) | |
return self.__AckFlag('314') | |
def TurnOnRmcOutput(self): | |
self.tx.write(PMTK_SET_NMEA_OUTPUT_RMC_ONLY) | |
return self.__AckFlag('314') | |
def GetNextSentence(self): | |
return self.tx.readline() | |
def GetLastLocation(self): | |
return self.utc, self.lat_decimal, self.lng_decimal | |
def GetLastLocationGoogleMapLink(self): | |
s = 'http://maps.google.com/?ll=unknown,unknown' | |
if self.utc != 'unknown': | |
s = 'http://maps.google.com/?ll=' + str(self.lat_decimal)[:8] + ',' + str(self.lng_decimal)[:9] | |
return s | |
def GetLocation(self, timeout_seconds = 2.0): | |
valid = False | |
zulu = 'unknown' | |
lat = '0' | |
lng = '0' | |
north = False | |
east = False | |
tout = time.time() + timeout_seconds | |
while valid == False and time.time() < tout: | |
line = self.GetNextSentence().split(',') | |
if line[0] == '$GPRMC': | |
valid = (line[2] == 'A') | |
if valid: | |
zulu = line[1].split('.')[0] | |
lat = line[3] | |
north = (line[4] == 'N') | |
lng = line[5] | |
east = (line[6] == 'E') | |
elif line[0] == '$GPGGA': | |
valid = (line[4] == '1' or line[4] == '2') | |
if valid: | |
zulu = line[1].split('.')[0] | |
lat = line[2] | |
north = (line[3] == 'N') | |
lng = line[4] | |
east = (line[5] == 'E') | |
if valid == False: | |
raise ValueError('Unable to detect fix.') | |
utc = zulu[0:2] + ':' + zulu[2:4] + ':' + zulu[4:6] + 'Z' | |
lat_degree = int(lat[0:2]) | |
lat_minute = float(lat[2:9]) / 60.0 | |
lat_decimal = lat_degree + lat_minute | |
if north == False: | |
lat_decimal = lat_decimal * -1.0 | |
lng_degree = int(lng[0:3]) | |
lng_minute = float(lng[3:10]) / 60.0 | |
lng_decimal = lng_degree + lng_minute | |
if east == False: | |
lng_decimal = lng_decimal * -1.0 | |
self.utc, self.lat_decimal, self.lng_decimal = utc, lat_decimal, lng_decimal | |
return utc, lat_decimal, lng_decimal | |
def EraseLog(self): | |
self.tx.write(PMTK_ERASE_FLASH) | |
return self.__AckFlag('184') | |
def StopLogging(self): | |
self.tx.write(PMTK_STOP_LOGGER) | |
return self.__AckFlag('185') | |
def StartLogging(self): | |
self.tx.write(PMTK_STRT_LOGGER) | |
return self.__AckFlag('186') | |
def DumpLog(self): | |
self.tx.write(PMTK_DUMP_LOGGER) | |
return self.__AckFlag('622') | |
GPS_PIN_EN = 17 | |
if __name__ == '__main__': | |
my_gps = MTK3339('/dev/ttyS0', GPS_PIN_EN) # The default for Raspberry Pi 2 is: '/dev/ttyAMA0' | |
my_gps.Enable(True) | |
my_gps.EraseLog() | |
my_gps.StartLogging() | |
my_gps.TurnOnRmcGgaOutput() #or TurnOnRmcOutput() | |
try: | |
while True: | |
try: | |
print(my_gps.GetLocation()) | |
time.sleep(1.0) | |
except Exception as e: | |
print(e) | |
except KeyboardInterrupt as kb: | |
pass | |
finally: | |
print(my_gps.GetLastLocationGoogleMapLink()) | |
my_gps.StopLogging() | |
my_gps.Remove() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment