Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculating Speed for a navigation app without getSpeed() method

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!

like image 633
Aman Alam Avatar asked Jan 28 '14 16:01

Aman Alam


2 Answers

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)

like image 145
Pierre-Luc Paour Avatar answered Sep 28 '22 08:09

Pierre-Luc Paour


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.

like image 34
T Driver Avatar answered Sep 28 '22 08:09

T Driver