From Chunky Dove, 6 Years ago, written in PHP.
Embed
  1. #!/usr/lib/python3
  2. # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 fileencoding=utf-8 :
  3.  
  4. import datetime, operator, functools, pprint, serial
  5.  
  6. class InvalidSentence(Exception):
  7.     pass
  8.  
  9. class InvalidChecksum(Exception):
  10.     pass
  11.  
  12. class Nmea:
  13.     nmeadict = {}
  14.     nmeadict['sat'] = []
  15.  
  16.     def __init__(self):
  17.         pass
  18.  
  19.     def getchecksum(self, message):
  20.         return(functools.reduce(operator.xor, map(ord, message)))
  21.  
  22.     def parse(self, nmeastring):
  23.         if not nmeastring.startswith('$'):
  24.             raise InvalidSentence
  25.         message, checksum = nmeastring[1:].strip().split('*')
  26.  
  27.         calculated_checksum = self.getchecksum(message)
  28.         checksum = int(checksum, 16)
  29.         if checksum != calculated_checksum:
  30.             raise InvalidChecksum("Transmitted checksum {} doesn't match "
  31.                   "calculated checksum {}".format(checksum,
  32.                   calculated_checksum))
  33.  
  34.         elems = message.split(',')
  35.         nmeatype = elems[0]
  36.  
  37.         if nmeatype == 'GPGGA':
  38.             fields = [
  39.                 'time',
  40.                 'latitude',
  41.                 'lat_dir',
  42.                 'longitude',
  43.                 'lon_dir',
  44.                 'quality',
  45.                 'satcount',
  46.                 'hdop',
  47.                 'altitude',
  48.                 'altitude_unit',
  49.                 'geoid_height',
  50.                 'geoid_height_unit',
  51.                 'dgps_timedelta',
  52.                 'dgps_stationid',
  53.             ]
  54.         elif nmeatype == 'GPGSA':
  55.             fields = None
  56.             self.nmeadict['manual_auto'] = elems[1]
  57.             self.nmeadict['2D_3D'] = elems[2]
  58.             self.nmeadict['pdop'] = elems[15]
  59.             self.nmeadict['vdop'] = elems[16]
  60.             self.nmeadict['hdop'] = elems[17]
  61.             self.nmeadict['sat_ids'] = []
  62.             for e in elems[2:15]:
  63.                 self.nmeadict['sat_ids'].append(e)
  64.  
  65.         elif nmeatype == 'GPGLL':
  66.             fields = [
  67.                 'latitude',
  68.                 'lat_dir',
  69.                 'longitude',
  70.                 'lon_dir',
  71.                 'time',
  72.                 'validity',
  73.             ]
  74.         elif nmeatype == 'GPZDA':
  75.             fields = [
  76.                 'utc',
  77.                 'day',
  78.                 'month',
  79.                 'year',
  80.                 'timezone_h',
  81.                 'timezone_min'
  82.             ]
  83.         elif nmeatype == 'GPRMC':
  84.             fields = [
  85.                 'time',
  86.                 'validity',
  87.                 'latitude',
  88.                 'lat_dir',
  89.                 'longitude',
  90.                 'lon_dir',
  91.                 'speed',
  92.                 'course',
  93.                 'date',
  94.                 'magnetic_var',
  95.                 'magnetic_var_dir',
  96.             ]
  97.         elif nmeatype == 'GPGSV':
  98.             # Beware, dragons ahead!
  99.            fields = None # We don't want this to be parsed the default way
  100.            self.nmeadict['total_sv'] = elems[3] # Total sats in view
  101.            i = int(elems[2])-1 # message id (0..n)
  102.            if i == 0: # first message
  103.                self.nmeadict['sat'] = [] # clear the old SVs
  104.            # 3 global elements, after that 4 per satelite
  105.            sv_cnt = (len(elems) - 3) // 4
  106.             for sv_num in range(0, sv_cnt-1): # for each sat in the msg
  107.                satdict = {
  108.                     'sv_prn': elems[4+(sv_num*4)], # Fields 4/8/12/16
  109.                    'elevation': elems[5+(sv_num*4)], # Fields 5/9/13/17
  110.                    'azimuth': elems[6+(sv_num*4)], # Fields 6/10/14/18
  111.                    'snr': elems[7+(sv_num*4)], # Fields 7/11/15/19
  112.                }
  113.                 # Append the sats to the nmeadict
  114.                self.nmeadict['sat'].append(satdict)
  115.            
  116.         else:
  117.             fields = None
  118.  
  119.         if fields is not None:
  120.             for elem in enumerate(elems[1:]):
  121.                 i = elem[0]
  122.                 if len(fields) > i:
  123.                     fieldname = fields[i]
  124.                     self.nmeadict[fieldname] = elem[1]
  125.  
  126. ser = serial.Serial("/dev/ttyACM0")
  127. foo = Nmea()
  128. pp = pprint.PrettyPrinter()
  129.  
  130. while True:
  131.     line = ser.readline()
  132.     try:
  133.         line = line.decode('ASCII')
  134.     except UnicodeDecodeError:
  135.         pass
  136.     else:
  137.         foo.parse(line)
  138.         print(chr(27) + "[2J") # Clear screen
  139.        print("\x1b[2J\x1b[H") # Return cursor
  140. #        pp.pprint(foo.nmeadict)
  141.        pprint.pprint(foo.nmeadict)