# Techniques for point-level speed calculation from GPS traces

I am working with a GPS dataset where GPS speed is not available, only the `lat/lon` coordinates and `timestamp`.

Currently, I calculate point-level speed from rate of distance between two consecutive points (`distance/delta_time`). However, the result usually gave large errors due to the errors in GPS.

Are the other means or techniques to estimate the point-point speed?

EDIT

Currently, I calculate the speed in python using the `geopy` package, like so:

``````from geopy.distance import vincenty
A = (Data[i, 0], Data[i, 1])
B = (Data[i + 1, 0], Data[i + 1, 1])
pointSpeed = vincenty(A, B).meters/delta_time[i]
``````
• Which software are you working with?
– Erik
Commented Jul 23, 2020 at 10:16
• I do this in Python using `geopy.vincenty` package to calculate the speed as `speed =vincenty(A, B).meters/delta_time`. Commented Jul 23, 2020 at 10:49
• Please Edit the Question in response to requests for clarification. Commented Jul 23, 2020 at 11:02
• Perhaps try calculating a moving average instead of each pair of successive points individually? It could help smooth some of the jumps in speed that you get. Commented Jul 23, 2020 at 16:29
• Over what sort of distances are you recording these GPS track points? And what kind of time deltas? Knowing where the scale of the errors is coming from (i.e. multipath / atmospheric range issues which cause 'movement' where there is none but usually only the scale of metres, vs. limitations in the GPS source) would inform how you deal with the 'large errors' you see. Commented Jul 30, 2020 at 16:43

I took a shot at @FSimardGIS 's suggestion for moving average.
Inputs: pandas dataframe `df` of the gps data containing timestamp, latitude, longitude columns, sorted by timestamp and with pure index (0 to end).
And a `time_interval` in mins, default: 5 mins

``````import pandas as pd
# main function
def findSpeed(df, time_interval = 5):
computeDistance(df)
df['offset'] = getTimeOffsets(df)
df['dspan'] = df['tspan'] = df['cspeed'] = df['diff'] = None

for N in range(len(df)):
if N == 0: continue
backi = N
totOffset = df.at[N,'offset']
while totOffset <= time_interval*60 :
backi -= 1
totOffset += df.at[backi,'offset']
if backi == 0: break

tspan = df.at[N,'timestamp'] - df.at[backi,'timestamp']
df.at[N,'tspan'] = tspan.seconds
distance = df.at[N,'ll_dist_traveled'] - df.at[backi,'ll_dist_traveled']
df.at[N,'dspan'] = round(distance,3)
if distance == 0:
speed = df.at[N, 'cspeed'] = 0
else:
speed = round(distance / tspan.seconds * 3600,2)
df.at[N, 'cspeed'] = speed
speed_diff = round(df.at[N,'speed'] - speed, 2)
df.at[N,'diff'] = speed_diff
return df

# calling the function
df1 = findSpeed(df, time_interval = 5)

``````

There are some supporting functions I've developed in earlier projects: `computeDistance`: finds distance between consecutive lat-longs and adds it up to a cumulative distance column `ll_dist_traveled`
`getTimeOffsets`: gives a column of time offset between timestamps in seconds

These are used by the program to identify [time_interval] (or lower) time period correspondng to each row and calculate speed from that group of data.

``````from math import sin, cos, sqrt, atan2, radians
def lat_long_dist(lat1,lon1,lat2,lon2):
# function for calculating ground distance between two lat-long locations
R = 6373.0 # approximate radius of earth in km.
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
distance = round(R * c, 6)
return distance

def computeDistance(sequencedf):
prevLat = prevLon = 0 # dummy initiation
total_dist = 0
for N in range(len(sequencedf)):
lat = float(sequencedf.at[N,'latitude'])
lon = float(sequencedf.at[N,'longitude'])
if N == 0:
sequencedf.at[N,'ll_dist'] = 0
else:
sequencedf.at[N,'ll_dist'] = lat_long_dist(lat,lon, prevLat,prevLon)
total_dist += sequencedf.at[N,'ll_dist']
sequencedf.at[N,'ll_dist_traveled'] = round(total_dist,6)
prevLat = lat
prevLon = lon
return round(total_dist,6)
# also the original df gets ll_dist and ll_dist_traveled columns added to it

def getTimeOffsets(df, timestamp_col='timestamp'):
timeOffsets = (df[timestamp_col] - df[timestamp_col].shift()).fillna(pd.Timedelta(seconds=0))
return timeOffsets.dt.total_seconds().astype(int)
``````

Feel free to change the `lat_long_dist` function to your preferred method (or use a routing tool like OSRM).

Output: same df (table / dataframe), with some middle-step new columns and a 'cspeed' column with calculated speed in km/hr.

I had a gps stream with original speed values from the device. Calculated with different time interval settings and plotted.

As observable, even the original data (in orange) is quite jittery. Moving average helps smoothen it out, but there's a tradeoff with accuracy. I'm still not clear about which time interval value to take. Hope this helps someone!

• Interesting... this is really helpful. Thanks for the detailed analysis. Commented Jun 10, 2021 at 16:59
• @arilwan thanks, pls upvote the answer if you can Commented Jun 11, 2021 at 6:04