Monday, September 9, 2013

NMEA string parser in Python - if you want to develop GPS tracker software in Python

GPS devices usually spit out location information in NMEA format. This format is quite standard. Here is the example of NMEA strings:

$GPGGA,002153.000,3342.6618,N,11751.3858,W,1,10,1.2,27.0,M,-34.2,M,,0000*5E
$GPRMC,161229.487,A,3723.2475,N,12158.3416,W,0.13,309.62,120598, ,*10

Those two lines contains geo-location coordinates, bearing, speed and also some information about the number of satellites being used.

I wrote this simple utilities to parse out those strings to kill time and hopefully this little piece of code inspires other to work on geo-location related services.

# NMEA messages decoding library
# Main decoder of the string messages
def nmea_decode(str):
ret = dict({'type': 'unknown'});
data = str.split(',');
if (len(data)>0):
# start decoding messages
if (data[0] == '$GPGGA'):
ret['type']='GGA';
ret.update(decode_gga(data));
elif (data[0] == '$GPRMC'):
ret['type']='RMC';
ret.update(decode_rmc(data));
#elif (data[0] == '$GPGSA'):
#print(data[0]+' not decoded')
#elif (data[0] == '$GPGSV'):
#print(data[0]+' not decoded')
# fall back
return ret;

def decode_gga(da):
ret = dict()
if (da[0]=='$GPGGA' and len(da)==15):
ret['utc_time']= da[1][0:2]+':'+da[1][2:4]+':'+da[1][4:]
ret['lat_deg']=int(da[2][0:2])+(float(da[2][2:])/60)
ret['lat_dm']=da[2][0:2]+' '+da[2][2:]
ret['ns']=da[3]
ret['lon_deg']=int(da[4][0:3])+(float(da[4][3:])/60)
ret['lon_dm']=da[4][0:3]+' '+da[4][3:]
ret['ew']=da[5]
ret['pfi']=da[6]
ret['sat_used']=da[7]
ret['hdop']=da[8]
ret['msl_alt']=da[9]
ret['alt_unit']=da[10]
ret['geoid_sep']=da[11]
ret['sep_unit']=da[12]
ret['age_diff_cor']=da[13]
ret['diff_ref_sta_id']=da[14][0:4]
ret['csum']=da[14][4:]
return ret;

def decode_rmc(da):
ret = dict()
if (da[0]=='$GPRMC' and len(da)==12):
ret['utc_time']= da[1][0:2]+':'+da[1][2:4]+':'+da[1][4:]
ret['status']=da[2]
ret['lat_deg']=int(da[3][0:2])+(float(da[3][2:])/60)
ret['lat_dm']=da[3][0:2]+' '+da[3][2:]
ret['ns']=da[4]
ret['lon_deg']=int(da[5][0:3])+(float(da[5][3:])/60)
ret['lon_dm']=da[5][0:3]+' '+da[5][3:]
ret['ew']=da[6]
ret['speed_knot']=da[7]
ret['speed_km']=float(da[7])*1.852
ret['course']=da[8]
ret['date']=da[9][4:]+'-'+da[9][2:4]+'-'+da[9][0:2]
ret['mode']=da[10]
ret['csum']=da[11]
return ret;