1

Im fiddling around with a GPS i bought a while back, a "GlobalSat GPS Receiver" model BU-S353S4. And it seems to be working well!

I have been able to read the GPS signal from the device with the help from "Cody Wilsons" excellent explanation! And convert the GPGGA output to a longitude and latitude coordinate with the help of the excellent python package "Pynmea2".

But how can I calculate my current speed, from two positions? I have found this thread that refers to this thread on how to calculate the distance with the Haversine formula.

My code looks like this:

from math import radians, cos, sin, asin, sqrt
import serial
import pynmea2

ser = serial.Serial('/dev/ttyUSB0', 4800, timeout = 5)

def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance in kilometers between two points
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a))
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles. Determines return value units.
    return c * r

counter = 0

while 1:
    line = ser.readline().decode('UTF-8')
    splitline = line.split(',')

    if splitline[0] == '$GPGGA':
        counter += 1
        msg = line
        data = pynmea2.parse(msg)
        lat1 = data.latitude
        lon1 = data.longitude
        if counter % 2:
            distance = haversine(lon1, lat1, data.longitude, data.latitude)
            print(distance)

This outputs approx every the distance i have traveled. But heres the problem, it always returns 0.0. I maybe havent traveled far enough?

And how should i proceed to calculate the speed?

I know the formula speed = distance/time. Pynmea2 have a time property (msg.timestamp). But frankly, i dont know how to do this.

Final result that seems to be working

from math import radians, cos, sin, asin, sqrt, atan2, degrees
import serial
import pynmea2

ser = serial.Serial('/dev/ttyUSB0', 4800, timeout = 5)

def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance in kilometers between two points
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a))
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles. Determines return value units.
    return c * r

prev_data = None

while 1:
    line = ser.readline().decode('UTF-8')
    splitline = line.split(',')

    if splitline[0] == '$GPGGA':
        msg = line
        data = pynmea2.parse(msg)
        if prev_data is not None:
            distance = haversine(data.longitude, data.latitude, prev_data.longitude, prev_data.latitude)
            print('distance', distance)
            print('speed', round(distance*3600, 2))
        prev_data = data

The interface updates once every second hence the speed formula.

Adam
  • 1,231
  • 1
  • 13
  • 37

1 Answers1

1

Your formula for haversine is correct.

You have a bug in your program that you are setting lat1 and lon1 to data.longtitude and data.latitude, and then immediately using these to test the distance from data.longitude and data.latitude. Of course you're going to get a distance of zero. You need to separate the "current" longitude and latitude from the "previous" longitude and latitude.

(Though minor nit. You're not calculating the haversine, you're calculating the distance using haversine. You probably just want to name your function distance. hav(x) = sin(x/2)**2'

prev_data = None
while True:
     ....
     if prev_data is not None:
        distance = haversine(data.longitude, data.latitude, 
                             prev_data.longitude, prev_data.latitude)
        pre_data = data
     ....
Frank Yellin
  • 9,127
  • 1
  • 12
  • 22
  • 1
    Yes you are correct. How could i miss that bug Im reading your answer, and im trying to figure out were i populate `pre_data`. Isent your example doing what i did previosuly, setting prev_data to current data? – Adam Jul 16 '22 at 20:27
  • I set `prev_data` to `None` before the loop so that I don't calculate distance the first time I get data. After that, I calculate distance between the current data and the value from the previous iteration. – Frank Yellin Jul 16 '22 at 20:39
  • Thanks! it seems to be working. I will accepts your answer, and make and edit with the final result. – Adam Jul 16 '22 at 20:45