GPS data out of NEO M8N using python

I was thinking of building own low stratum NTP server for home. Stratum 0 NTP services are available out of atomic clocks, satellites or via some radio links.

Any GPS module can easily get datetime (UTC) from satellite transmissions along with latitude, longitude (for geo positioning), speed, altitude etc.

To build the project, I bought NEO N8N GPS module for about 1400 INR and a USB UART (CP2102 chip based) for another 150 INR.

NEO M8N datasheet

GPS modules do receive byte strings, which are essentially NMEA 183 standard sentences. Parsing these sentences can yield you variety of information, such as latitude, longitude, date, time (in UTC), altitude, speed, number visible satellites and so on.

I wrote a small python code to get this data and in future, I will use time data to build a NTP for home. Which I can publish in my DHCP server for DHCP clients (mobiles, laptops etc at home).

In the code you have to mention usb port where UART module is inserted. To find port, use dmesg command as following.

manish@finch:~ $ dmesg
....
....
[311156.396726] usb 1-1.3: new full-speed USB device number 3 using xhci_hcd
[311156.584201] usb 1-1.3: New USB device found, idVendor=10c4, idProduct=ea60, bcdDevice= 1.00
[311156.584223] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[311156.584242] usb 1-1.3: Product: CP2102 USB to UART Bridge Controller
[311156.584259] usb 1-1.3: Manufacturer: Silicon Labs
[311156.584275] usb 1-1.3: SerialNumber: 0001
[311156.953112] usbcore: registered new interface driver usbserial_generic
[311156.954512] usbserial: USB Serial support registered for generic
[311156.984981] usbcore: registered new interface driver cp210x
[311156.985055] usbserial: USB Serial support registered for cp210x
[311156.985182] cp210x 1-1.3:1.0: cp210x converter detected
[311156.991117] usb 1-1.3: cp210x converter now attached to ttyUSB0
....
....
#!/usr/bin/python3
import serial
import datetime

def dec2deg(value):
   dec = value/100.00
   deg = int(dec)
   min = (dec - int(dec))/0.6
   position = deg + min
   position = "%.7f" %(position)
   return position

mapscale = 18
while True:
  port="/dev/ttyUSB0"
  ser=serial.Serial(port, baudrate=9600, timeout=0.5)
  gpsdata=ser.readline()
  try:
    #print(gpsdata)
    gpsdata = gpsdata.decode("utf8")
    try:
      #print(gpsdata)
      gpsdata = gpsdata.split(',')
      #print(gpsdata)
      if "GNRMC" in gpsdata[0]:
        hrs, min, sec = gpsdata[1][0:2], gpsdata[1][2:4], gpsdata[1][4:6]
        day, month, year = gpsdata[9][0:2], gpsdata[9][2:4], gpsdata[9][4:6]
        datetimeutc = "{}:{}:{} {}/{}/{}".format(hrs, min, sec, day, month, year)
        datetimeutc = datetime.datetime.strptime(datetimeutc, '%H:%M:%S %d/%m/%y')
        speed = round(float(gpsdata[7])*1.852,2)
        message = "Datetime={} ,speed={} kmph".format(datetimeutc, speed)
        print(message)
      if "GNGGA" in gpsdata[0]:
        #print(gpsdata)
        lat = dec2deg(float(gpsdata[2]))
        lon = dec2deg(float(gpsdata[4]))
        alt = gpsdata[9]
        satcount = gpsdata[7]
        message = "Altitude={}, Satellites={}\n".format(alt, satcount)
        gearth = "https://earth.google.com/web/search/@{},{},{}\n".format(lat,lon,alt)
        mapsapp = "geo:{},{}\n".format(lat, lon)
        map = "https://www.openstreetmap.org/#map={}/{}/{}\n\n".format(mapscale, lat, lon)
        print(message, gearth, mapsapp, map)
    except:
      print("unable to process", gpsdata)
  except:
    print("unable to decode", gpsdata)

Sample output:

Datetime=2022-02-06 12:25:00 ,speed=1.28 kmph
Altitude=241.6, Satellites=06
 https://earth.google.com/web/search/@29.9646572,76.8888248,241.6
 geo:29.9646572,76.8888248
 https://www.openstreetmap.org/#map=18/29.9646572/76.8888248


Datetime=2022-02-06 12:25:01 ,speed=1.0 kmph
Altitude=242.0, Satellites=06
 https://earth.google.com/web/search/@29.9646555,76.8888285,242.0
 geo:29.9646555,76.8888285
 https://www.openstreetmap.org/#map=18/29.9646555/76.8888285


Datetime=2022-02-06 12:25:02 ,speed=0.09 kmph
Altitude=241.9, Satellites=06
 https://earth.google.com/web/search/@29.9646563,76.8888293,241.9
 geo:29.9646563,76.8888293
 https://www.openstreetmap.org/#map=18/29.9646563/76.8888293


Datetime=2022-02-06 12:25:03 ,speed=0.16 kmph
Altitude=242.0, Satellites=06
 https://earth.google.com/web/search/@29.9646567,76.8888295,242.0
 geo:29.9646567,76.8888295
 https://www.openstreetmap.org/#map=18/29.9646567/76.8888295