I’ve seen some pretty good results so far with my first stab at filtering–I no longer had lots of random hits several km out during the day when the car was parked in the same place.
I should note, I’m using the on-shield antenna as I’ve previous stated my external GPS antenna experience has been poor–possibly due to some issue w/ either the pigtail or antenna itself.
In the code below I’m building up 5 location hits with ~3s delay between each. Over ~15s we are trying to capture a picture of where the GPS is saying we are. If any of the 5 points disagrees with others by more than a certain amount (accounting for movement too) then we consider the cycle bad and don’t allow it to be used. Otherwise it’s considered good and available for use if we happen to be publishing our result on that go around in the loop.
This is built originally off the GPS_Features code found in the AssetTracker library. I’ll define the specific variables and functions I use, but I’ll assume you’ve got the ones that came w/ the AssetTracker already, like ‘t’ for the AssetTracker.
Up in the variable declaration area–
// An array to store several sets of lat/lon
float locations[5][2];
// The time since the GPS location was checked--used as a timer to prevent blocking
// the other loop code from running
unsigned long lastGPS = 0;
// Seconds between checking for GPS location data
int gpsDelay = 3000;
int locationIndex = 0;
bool goodGPS = false;
float goodLat = 0;
float goodLon = 0;
In the loop–
// Fill the array with 5 positions once t.gpsFix() returns true
if (t.gpsFix() && (millis() - lastGPS > gpsDelay)){
locations[locationIndex][0] = t.readDecLat();
locations[locationIndex][1] = t.readDecLon();
locationIndex++;
lastGPS = millis();
}
// After 5 locations are recorded, check each against each other for relative distance to decide
// if we should keep this as a valid location, or toss it out
if (locationIndex == 5){
locationIndex = 0;
goodLat = 0;
goodLon = 0;
// Assume goodGPS until we learn otherwise
goodGPS = 1;
for (int x = 0; x < 5; x++){
for (int y = 0; y < 5; y++){
// if any point is further than 1000m from another let's assume this is a bad
// Note: at 100MPH you will travel roughly 672m in 15s, so this should handle
// most people's asset tracking needs in a moving vehicle up to about 150MPH
if (distance(locations[x][0], locations[y][0], locations[x][1], locations[y][1]) > 1000){
goodGPS = 0;
}
}
goodLat = goodLat + locations[x][0];
goodLon = goodLon + locations[x][1];
}
// Consider our goodLat,goodLon combination to the average of the last 5 points
goodLat = goodLat / 5;
goodLon = goodLon / 5;
}
Additional functions for calculating distance between points, you need to include Math.h for these–
float distance(float lat0, float lat1, float lon0, float lon1){
float deltaLat = fabs(lat0 - lat1) * 111194.9;
float deltaLon = 111194.9*fabs(lon0 - lon1) * cos(radians((lat0 + lat1)/2));
float distance = sqrt(pow(deltaLat,2) + pow(deltaLon,2));
return distance;
}
float radians(float degrees){
//return (degrees * (3.141593 / 180))
return ((degrees * 71) / 4068);
}
I decided to post just these snippets because the rest of my sketch is a bit enormous–I have a ton of other code pulling together data from other sensors, some Particle functions to allow customizing my debug output remotely and changing my timers and other parameters. I also have code related to building up a GET request w/ URL parameters which I’m using with a PHP script to get the data into a MySQL database.
…But the take away is that ‘goodGPS’ indicates that your ‘goodLat’ and ‘goodLon’ values represent consistent positions for last 15 seconds which can help weed out outliers.
That said, one possible problem is that if the GPS data does not change over those 15 seconds you may still get erroneous readings. Increasing the time between checks could help this, but then it’s balancing act in terms of how accurate you want to be and how often you want to publish–you also have to account for motion of the device when collecting points. In my case I was seeing points that were within 5-10km of my actual, so if I were to increase the total time I’m collecting points I’d have to increase the range of the potential legitimate distance I’m traveling which might allow in some of those wrong points.
Some ideas for expansion of this logic:
-
Keep using the 3s timer, but do not consider a point identical to the previous point as new–this will weed out the GPS function reporting the same data multiple times
-
Use the previous goodLat and goodLon values at the start of next cycle as the initial point
-
Averaging your position over time to determine outlier values
The attached screenshot represents my travels today. The large cluster around “Newton” is where I am parked at work. It’s a bit of a jumbled mess at this zoom level but it’s reasonably accurate when zoomed. There are several other points South where I was parked for ~hour each during the day.
Prior to the filtering if I were near a building or other GPS signal blocking object I’d have way off inaccurate dots that would be several km’s off–or maybe even around the globe if the parser misses a negative sign or drops the decimal portion.
Hopefully some of my ideas help others to weed out inaccurate values.
This device is ultimately going on a sail boat that my co-worker bought. In the next few weeks several of us will be sailing it near-shore along about 600 miles of the eastern US coast and we’d like to capture our trip as well as some other data along the way. We’re grabbing water/cabin temperatures, relative humidity, some accelerometer peak values, light levels, etc. This chart above shows the position data, and each point is clickable to reveal the other data collected. I’ve also got some jChart pages to show the data/trends over time for the other values.
Our families can also check the page to see where we are as we move along. I’ve got the cellular radio of the Particle connected to an external antenna which is mounted about 10’ above deck, so it should be able to get signal in marginal conditions where our cellphones are not able.
I’m also feeding a portion of this data to a TNC and VHF transceiver for APRS broadcast to any amateur radio operators who might be receiving along the coast.