Calculate the speed from two longitude and latitude GPS coordinates

Viewed 125

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.

1 Answers

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
     ....
Related