Skip to content

Instantly share code, notes, and snippets.

@TheRayTracer
Last active December 5, 2018 19:40
Show Gist options
  • Save TheRayTracer/2d5ef07ac08b77e7388732c1906aa837 to your computer and use it in GitHub Desktop.
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.
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