I know this question has been asked a lot, but not yet to my satisfaction. I am trying to use the GPS of an Android device to calculate speed. A lot of people seem to reply by saying to simply use the getSpeed() function of the Location object. From what I understand though, getSpeed() will only work on certain devices that have a speed sensor built into the GPS receiver chip. I want my application to work regardless of this, so I am using the following haversine formula:
private double CalculateHaversineMI(double lat1, double long1, double lat2,double long2) {
double dlong = (long2 - long1) * (Math.PI / 180.0f);
double dlat = (lat2 - lat1) * (Math.PI / 180.0f);
double a = Math.pow(Math.sin(dlat / 2.0), 2)
+ Math.cos(lat1 * (Math.PI / 180.0f))
* Math.cos(lat2 * (Math.PI / 180.0f))
* Math.pow(Math.sin(dlong / 2.0), 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = 3956 * c;
return d;
}
Now what I'm trying to do is figure out how to calculate speed from this. Can anyone help me please?
What I can see is that your function returns correct path distance between 2 points on a sphere - it is d. But, this formula is needed only if you have 2 points on a sphere that are not close to each other (means central angle of their separation is not small, central angle of 1 degree corresponds to distance of 111 km approx, just to get feeling). If they are close to each other (which is the case for people moving and slow speed vehicles), then you do not need this formula. You can simply and very accurately approximate arc on the sphere with the straight line, and then calculation becomes trivial.
Sample GPS position at regular time periods. Calculate distance from the last position obtained. For that purpose you may use distanceTo() function from android.location.Location.
Calculate speed by dividing distance with time elapsed between 2 measurements.
Average calculated speeds for more accurate results, but ensure that you do not lose sensitivity to speed changes. So, you would need some trade-off on number of samples averaged.
That calculates the distance. Now as you may recall, speed = distance / time, so somewhere along the line you need to capture the time as well as the position.
On another note, the formula that you are using is way OTT for what you are trying to do. You would be better off making a number of approximations based on the fact that the path you are traversing is much less than the circumference of the earth. Then you could arrive at a much simpler formula.
Related
Is it possible to track the speed and the acceleration through an android app using the GPS data it provides? I'm planning to create a react native app with these features. But I want to know whether it's possible to do it with the data we can get. Even in native android?
You can do it in pure JS.
For the speed part you have the core React Native Geolocation API where you can find the method watchPosition. On each position update, an object is returned with the current speed amongst other attributes (location, altitude...).
For the acceleration part you have the react-native-motion-manager library. You can listen to the accelerometer updates and get the acceleration.
Is it possible to track the speed and the acceleration through an
android app using the GPS data it provides?
Answer: yes
location.getSpeed() only returns what was set with
location.setSpeed(). This is a value that you can set for a location
object.
To calculate the speed using GPS, you'll have to do a little math:
Speed = distance / time
so how to do that
(currentGPSPoint - lastGPSPoint) / (time between GPS points)
Each location provided by all LocationProviders has the instant speed at the point where the location was taken, which is unstable and it can differ between devices. The average speed is more precise, you have to calculate distance and time. This is how I calculate distance between 2 locations:
static double distance (Location in1, Location in2) {
double R = 6371000;
double la1 = in1.getLatitude()* Math.PI/180;
double la2 = in2.getLatitude()* Math.PI/180;
double lo1 = in1.getLongitude()* Math.PI/180;
double lo2 = in2.getLongitude()* Math.PI/180;
double tmp1 = Math.sin((la1-la2)/2)*Math.sin((la1-la2)/2) + Math.cos(la1)*Math.cos(la2) * Math.sin((lo1-lo2)/2) * Math.sin((lo1-lo2)/2);
double tmp2 = Math.sqrt(tmp1);
double d = Math.abs(2 * R * Math.asin(tmp2) * 100000) / 100000;
return d;
}
You can use this function as well, but I'd rather use the other one, which stores the result in "results" :)
Location.distanceBetween(in1.getLatitude(),in1.getLongitude(),in2.getLatitude(),in2.getLongitude(),results);
So getting the speed in m/s (that's the 1000* for) is quite straightforward:
double avgSpeed = 1000 * distance(loc1,loc2) / (loc2.getTime()-loc1.getTime)
I am developing an app which is more of a time-shift racing between your friends.
I need to calculate speed of a moving vehicle, and I don't want to use Location.getSpeed() method. (Explained in detail in the bottom why I don't want to use it)
I am trying to calculate speed with the help of Latitude and Longitude available to me, and this is where I need help.
The help needed: I would want to know is:
If the algorithm is correct
Should I calculate in Centimeters instead of meters
And if there's any code/library already available which does it.
I am using the following code:
This gives me distance between two LatLng points:
long getDistanceBetweenPoints(double lat1, double lng1, double lat2, double lng2 ){
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lng2 - lng1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1))
* Math.cos(Math.toRadians(lat2)) * Math.sin(dLon / 2)
* Math.sin(dLon / 2);
double c = 2 * Math.asin(Math.sqrt(a));
long distanceInMeters = Math.round(6371000 * c);
return distanceInMeters;
}
And the following code is how it is being used:
if(lastLat == -1 && lastLng == -1){
lastLat = location.getLatitude();
lastLng = location.getLongitude();
lastTimeStamp = location.getTime();
return;
}
long distanceInMeters = getDistanceBetweenPointsAndSetTotal(lastLat, lastLng, location.getLatitude(), location.getLongitude());
long timeDelta = (location.getTime() - lastTimeStamp)/1000;
long speed = 0;
if(timeDelta > 0){
speed = (distanceInMeters/timeDelta);
}
Log.d("Calculations","Distance: "+distanceInMeters+", TimeDelta: "+timeDelta+" seconds"+",speed: "+speed+" Accuracy: "+location.getAccuracy());
lastLat = location.getLatitude();
lastLng = location.getLongitude();
lastTimeStamp = location.getTime();
When I run it, I get following output from that LogCat:
Distance: 0, TimeDelta: 0 seconds,speed: 0 Accuracy: 5.0
Detailed Reasons
The target consumers are not supposed to have high quality devices with high-quality GPS chips, thus always getting a very accurate fix when the device is on the move is not possible.
I thus don't want to depend on the Location.getSpeed() method, since I have observed it gives out speed values only when the accuracy is in the range of 5~8 metres.
The normal accuracy ranges I am getting in general circumstances is 10-15 metres, and getSpeed() doesn't give any speed. Even hasSpeed() starts returning false.
I have been tinkering my head around this thing for more than 3 days, any help in this would be deeply appreciated.
Much Thanks in Advance!
I develop MyTrails, an Android mapping and tracking app, and like you I struggled at first with the very crude location APIs Google has seen fit to include in Android.
hasSpeed() is false when the GPS chip doesn't have a good enough fix to compute speed based on dopler effect. Even when it does, I usually don't trust the speed if it's less than 5km/h or thereabouts.
The way I handle speed calculations is by using a crude low-pass filter: I record a trackpoint every second (and a minimum of 5m apart, based on LocationManager.requestLocationUpdates(), and to calculate the recent speed, I go back a few samples to get one that is a sufficient distance apart (but no more than 30s prior), and perform the averaging you're doing.
I'm using Location.distanceBetween() for the actual distance calculation. Beware that it fails on a very small (but unfortunate) number of devices, so the haversine method you have may be a better bet. You may want to check it though, what I have is
/**
* Gets distance in meters, coordinates in RADIAN
*/
private static double getDistance(double lat1, double lon1, double lat2, double lon2) {
double R = 6371000; // for haversine use R = 6372.8 km instead of 6371 km
double dLat = lat2 - lat1;
double dLon = lon2 - lon1;
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1) * Math.cos(lat2) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
//double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
// simplify haversine:
//return 2 * R * 1000 * Math.asin(Math.sqrt(a));
}
(note the 1000 factor)
I agree with Pierre, also you are rounding the results. If the points are not far enough apart, you're rounding may just provide 0. I don't see how the rounding tolerance is defined.
I always calculate in meters - it makes things much easier. I'd suggest following the SI standard units in your code.
Your output also shows that the timeDelta is zero, so no distance was actually calculated.
Omg.....
Location
Check this method - it allows you to calculate distance between 2 geo points, just divide it by your time. It should be much more accurate than yours as it calculates distances with much better approximation (WGS83 instead of using sin, cos and rest of this stuff).
Good, general idea is to keep all your data in plain units like m, s, kg etc. and make changes only for showing data to user.
If the algorithm is correct?
The distance calculation looks like the haversine formula which is correct.
(There are much faster formulas for small distances (and you only use small distances), but haversine will work)
Should I calculate in Centimeters instead of meters?
Never use centimeter, stay with SI units, the unit of speed is meter/s. just use meter as floating point (double).
Ios, where I am developping a tracking App, seems to be more friendly related to the getSpeed() nethod from Location class.
But I (again?) warn you to use speeds at slow speed. If you get an invalid speed, then just stop calculation or mark it as invalid in your app.
If the GPS chip cannot deliver a valid speed, it has good reason to do so, and it's an interesting question whether you will do it better.
At low speed GPS tends to randomly jump around the true position, giving 5-30m jumps.
It's very likely that your self calculated speed shows much more speed that the device is (not) moving.
Try to fix your app, that it does not need speeds at low speeds.
I am using GPS with Android and need to have the location compensated for speed to remove the error due to the lag of the calculation of location. My speeds of interest are in the 5 to 10 mph range as this is for a sailboat.
Here is my code:
public double[] correctLocation(double pLat,double pLong,double pAngle,double pSpeed, double factor){
double[] latLong = new double[2];
latLong[0] = pLat + factor * pSpeed * Math.cos(pAngle * Math.PI / 180);
latLong[1] = pLong + factor * pSpeed * Math.sin(pAngle * Math.PI / 180) / Math.cos(pLat * Math.PI / 180);
return latLong;
}
I did a test run at 25mph in a car and calculated that the factor should be 3E-6 but in trying it out at running and walking speed it is clear that a factor more like 6E-6 is needed. I can't find any references to this through Google although I know people do this for example in the Google self driving car. I assume others do these kind of corrections.
Can anyone help me out here? The GPS is a Qstarz A1000XT with a MTK chipset. There are probable things I am missing with this simplistic approach. (I am assuming I don't need acceleration as this is a heavy boat with a 1 minute time constant for acceleration).
The 6E-6 was correct. I had a couple of errors and was confused for some time on this. I ended up testing by traveling at a spot from both directions at 25MPH and pinging the spot. I interpolated between GPS readings so that the timing and update rate were not a factor. I did this 10 times. That gave me two clusters of points and using a graphical method I adjusted the correction factor until the clusters overlapped. An alternate method is to use the difference in latitude and longitude between the last two readings, which are 1 second apart, and apply a correction factor in seconds. In my case that was 1.25 seconds. Both methods produced the same result in terms of the clusters. The speed correction was a tad closer in the scatter but not by much. Speed on GPS is measured off the carrier and not derived from differences in position and is more accurate.
I'm making an application for tracking a user as they run, cycle or walk and display it to the screen.
I know that there is distanceBetween function built into android but is this accurate enough for a fittness application?
I was considering using Haversine formula or other such formula for calculating distances between points the only problem I see about these formulas is that it usually is straight line or as the crow flys distances.
Does anyone have an idea about this?
The Haversine formula is accurate for most distances, but it suffers from rounding errors when the points are (nearly) antipodal. The following formula is accurate for all distances.
> double delta = G1 - G2;
> double p1 = cos(L2) * sin(delta);
> double p2 = cos(L1) * sin(L2) - sin(L1) * cos(L2) * cos(delta);
> double p3 = sin(L1) * sin(L2) + cos(L1) * cos(L2) * cos(delta);
> distance = 60 * Math.atan2(Math.sqrt(p1*p1 + p2*p2), p3);
Here's an example and the implementation.
resource : Here
Tracking their route as they walk is going to involve a series of way points. If you sample at say 1 way point every 10 seconds then you can calculate the distance between the previous point and the new point using either a crow-flys technique like haversine or just make some webservice requests to an external service like google maps and their distance matrix which can give you the snapped-to-streets distance between 2 points using suggested best path.
http://code.google.com/apis/maps/documentation/distancematrix/
You just have to make sure that your sample rate isn't too high or you can go over the 2500 API calls/24-hour-period rate limiter (which is per IP I believe). But 2500 will give you 7 hours at 1 sample per 10 seconds, or you can try a lower rate like 1 per 35 seconds and risk having the route be guessed wrong, but guarantee that your device won't go over 2500 samples.
Personally, if location polling is free, I would just go with distanceBetween and poll every 1-2 seconds. That should be short enough that the accuracy of GPS becomes your biggest source of error.
I have an application of augmented reality in which I have stored information such us metro, gas stations, places of interest, etc. with the corresponding latitude and longitude.
Now, according to the orientation of the device, I would show a marker for each site in the camera view of the device. Similar to Layar and Wikitude.
It takes three days searching without stopping and have not found anyone to explain how to solve this problem.
Since information on this topic is very sparse, and I recently solved this problem on the iPhone, I thought I would share my method for anyone that can make it work with Android (there's nothing really specific to iPhone in this answer except for the Math functions sin, cos, and fmod, which can be found in java.lang.Math). These are the steps I took:
Obtain your own lat/lon and your current compass heading (lat1, lon1 and heading). On the iPhone, CLLocation returns these in degrees, but for these calculations they MUST be in radians (i.e. multiply by PI/180)
Obtain lat/lon of Points of Interest (POI) in radians (lat2 and lon2).
Calculate the distance between lat1/lon1 and lat2/lon2 using formula found here: http://www.movable-type.co.uk/scripts/latlong.html
Calculate angle to lat2/lon2 in relation to north. This is also described in the link above but I had a little bit of trouble getting this to work, here is C code for this:
double latDelta = (lat2 - lat1);
double lonDelta = (lon2 - lon1);
double y = sin(lonDelta) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2)* cos(lonDelta);
double angle = atan2(y, x); //not finished here yet
double headingDeg = compass.currentHeading;
double angleDeg = angle * 180/PI;
double heading = headingDeg*PI/180;
angle = fmod(angleDeg + 360, 360) * PI/180; //normalize to 0 to 360 (instead of -180 to 180), then convert back to radians
angleDeg = angle * 180/PI;
Using standard trigonometry, I calculate x and y. Remember, these coordinates are in 3D space, so we are not finished here yet because you still have to map them to 2D:
x = sin(angle-heading) * distance;
z = cos(angle-heading) * distance; //typically, z faces into the screen, but in our 2D map, it is a y-coordinate, as if you are looking from the bottom down on the world, like Google Maps
Finally, using the projection formula, you can calculate screen x ( I didn't do y because it was not necessary for my project, but you would need to get accelerator data and figure out if the device is perpendicular to the ground). The projection formula is found here (scroll to the very bottom): http://membres.multimania.fr/amycoders/tutorials/3dbasics.html
double screenX = (x * 256) / z
Now you can use this x coordinate to move an image or a marker on your screen. Remember a few points:
Everything must be in radians
The angle from you to the POI relative to North is angleBeteweenPoints - currentHeading
(For some reason I can't properly format the code on this computer, so if anyone wants to edit this answer, feel free).