Buggy Geolocation drivers, Part 2: Calculating speed when your driver doesn't

A couple of days ago I talked about my experiences with a geolocation driver bug that was not presenting a heading to the Geolocation sensor, and how to account for this problem in your app by calculating the value yourself. Today, I want to talk about a very similar problem I have encountered with geolocation drivers: zero and NULL speeds. Speed, like heading, is a value you can calculate based on the device's position shifts and it's pretty easy to check to see if the driver is providing it correctly. If your position known and it's changing then your speed should be non-NULL and non-zero. Once again, math and trigonometry are our friends.

A couple of caveats

Before we get started, however, we should understand two technical details of global positioning systems, and how those should affect your decision process.

The first is that a stationary device will have some amount of positional jitter because of errors that creep in to the positioning system. A nice discussion of error sources is given in the Wikipedia article on GPS errors and I encourage you to read it if you are interested in the technical details, but in general they can be summarized as:

  • atmospheric affects, which affect the speed of signals as they travel through the atmosphere
  • clock errors, which impacts the accuracy of time measurements
  • ephemeris errors, which lead to inaccurate satellite positiong information
  • geometric dilution of precision, a multiplicative scalar for the overall position error that is based on satellite geometry
  • multipath effects, such as when a reflected signal is received but the direct signal is not due to local obstructions

At the core of positioning systems is the ability to measure the length of time it takes for a signal sent from a satellite to reach the receiver, and where the signal originated from. The above error sources all impact the receiver's ability to accurately "know" these two pieces of information. As these effects fluctuate with time, a receiver-- particularly a consumer grade one-- will see fluctuations in its position with time even when standing still. What this means is, just because your position is changing that doesn't mean you are actually moving. Some GPS receivers actually rely on doppler shifts in the incoming signal to validate that the device is moving, and some will even use that to adjust or outright determine the estimated speed.

The second is that GPS receivers are presenting speeds to the geolocation driver as part of its NMEA output stream. When these values are calculated, when they are sent to the driver, and when the Location API sends them to your application can, in fact, be slightly different times. Fractions of a second matter when you are getting sample points roughly once a second, and the speeds you calculate may be off by as much as 10 to 15%.

Calculating speed

To calculate a speed you need to know two pieces of information: the distance traveled between points, and the time delta. The location API will feed you Geoposition objects that contain a Geocoordinate object, and in each Geocoordinate object is your latitude, longitude, and a timestamp at which it was generated. Now all we need is the math.

As with determining heading, using the great circle formula in my blog "Calculating geographic distances in location-aware apps" to find the distance is overkill, and we can go to the Pythagorean Theorem for an equirectangular approximation:

d = r * √ (x² + y²) 

x = Δλ cos( (Φ1 + Φ2) / 2)

y = ΔΦ

where r is the radius of the earth. We want our result in meters per second since that's what the Windows Runtime Location API uses, so:

r= 6371010

Also, remember that our latitudes and longitudes, Φ and λ, need to be in radians.

Once we have a distance, we simply devide by Δt to get our speed. Again, to stay consistent with the Location API, your time difference should be in seconds.

Putting it all together

As with heading, we must first determine whether or not our driver is giving us a valid and sensible speed. Checking for NULL is, of course, pretty easy, but what if speed is non-zero? Remember that our calculated speed might vary as much as 15%, perhaps more, so we should not casually assume it's broken. Only when speed is off by a significant amount should we discard the Location API's values and substitute our own.

It's also important to note that we will be introducing a delay into the system when we calculate speeds ourselves. This is not wrong or necessarily bad, but it will give the user the impression that their reported speed lags behind actual changes in motion.

Using the same track points from the previous blog entry, I have calculated speeds and compared them to the values reported by the Location API. The track points are one second apart so we don't even need to do any division:

Point Latitude Longitude Speed Calculated Speed Variance Heading
1 45.577 -122.970 24.8 n/a n/a 6.8
2 45.577 -122.970 24.1 24.8 3% 9.6
3 45.578 -122.970 23.8 23.9 0% 12.7
4 45.578 -122.970 22.6 21.9 3% 16.4
5 45.578 -122.969 21.4 21.0 2% 20.8
6 45.578 -122.969 20.5 20.2 1% 24.1
7 45.578 -122.969 20.1 20.0 0% 25.7
8 45.578 -122.969 17.5 18.0 3% 27.6
9 45.579 -122.969 15.0 16.4 9% 27.7
10 45.579 -122.969 12.5 13.7 10% 28.2
11 45.579 -122.969 10.8 11.5 7% 29.6
12 45.579 -122.969 9.1 9.0 2% 42.5
13 45.579 -122.969 8.6 7.9 8% 46.2
14 45.579 -122.969 8.3 8.7 5% 79.9
15 45.579 -122.968 8.7 8.8 1% 95.0
16 45.579 -122.968 10.7 10.3 3% 108.0
17 45.579 -122.968 12.5 13.3 7% 109.2
18 45.579 -122.968 14.2 15.4 9% 108.8
19 45.579 -122.968 15.5 17.8 15% 107.7
20 45.579 -122.968 16.5 17.1 4% 106.1
21 45.579 -122.967 18.3 19.0 4% 104.3
22 45.579 -122.967 18.9 19.6 3% 103.1
23 45.578 -122.967 20.3 20.9 3% 102.2
24 45.578 -122.967 21.0 22.0 4% 101.7
25 45.578 -122.966 21.4 21.2 1% 101.5
26 45.578 -122.966 22.2 21.8 2% 102.9
27 45.578 -122.966 22.6 23.3 3% 104.7
28 45.578 -122.965 22.7 23.3 3% 105.9
29 45.578 -122.965 23.0 23.3 1% 107.3
30 45.578 -122.965 23.6 23.7 1% 109.4

As you can see, our calculated speed is generally pretty close with the greatest variations, one as high as 15%, occuring during rapid acceleration and deceleration. This technique is quite practical.

And that's it. Heading and speed are two values that your app can calculate if it determines that they are not being provided by the driver/Location API, and these are useful techniques for ensuring your location-aware app still functions properly under those conditions.


§

Para obtener más información sobre las optimizaciones del compilador, consulte el aviso sobre la optimización.