Calculating Speed for a navigation app without getSpeed() method

11,185

Solution 1

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)

Solution 2

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.

Solution 3

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.

Solution 4

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.

Share:
11,185
Aman Alam
Author by

Aman Alam

I like building things which people can use and take help from. Have been into Mobility and building Android apps since quite some time. Biker, Gamer, Humanist. And interested in philosophy, poetry and philanthropy. Ask to learn; Answer to help is what I enjoy, that's why I am here! :-)

Updated on June 24, 2022

Comments

  • Aman Alam
    Aman Alam almost 2 years

    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!

  • Aman Alam
    Aman Alam over 10 years
    That's exactly what's happening. Since the points are too close, and everything is in meters/seconds, it evaluates to zero. I was thinking of going further down to centimeters/miliseconds. Do you think that will be a good approach?
  • AlexWien
    AlexWien over 10 years
    No, never use centimerts, stay with Si units, but use double meters.
  • Pierre-Luc Paour
    Pierre-Luc Paour over 10 years
    The rounding is happening, but it shouldn't be significant: if the distance is sub-meter the GPS error (usually > 5m) is greater than what you're using. This is why I'm using the approach I outlined in my answer.
  • Aman Alam
    Aman Alam over 10 years
    I'll try this aproach out. I think it will slow down the update rate of the speed on the UI a bit, but as long as its non-zero, it should work. Would you know about the logic getSpeed() uses? I tried searching for it in the source code, but couldn't find it. It seems the speed its returning is being set somewhere else.
  • Aman Alam
    Aman Alam over 10 years
    Yes, that's the haversine formula. Can you please tell which are the other formulas which are faster for small distances, since the distance between two points is going to be pretty small. Okay, I am sticking to SI units, I think I am already using meter in floating points. OHH, iOS is wayyy better. The iPhone counterpart of this app works butter smooth, and I am expected to deliver similar smoothness, if not the same. Sorry, not displaying speeds at slow speed is not an option that I have :-/
  • Pierre-Luc Paour
    Pierre-Luc Paour over 10 years
    This is data returned by the GPS. You can get the raw data by registering an NmeaListener. Don't forget to accept one of the answers you received :-)
  • Pierre-Luc Paour
    Pierre-Luc Paour over 10 years
    I hope you don't need to implement a compass: if you thought the Android API is less rich than iOS', you'll be appalled at how crude and low-level the magnetometer API is (low-level is not bad in itself, but lack of a higher-level API is).
  • AlexWien
    AlexWien over 10 years
    @Pierre-LucPaour I have implemented a compass, the ios API delivers the heading between 0.0 and 360.0°. This is a very high Level API: For more info Look at - (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
  • AlexWien
    AlexWien over 10 years
    @SheikhAman faster formulas: e.g transform coordinates to cartesian space using a Cyclindrical EquidDistante Projection (one multiplication and one cosinus), then calculate square of distance using pythagoras. >> Sorry, not displaying speeds at slow speed is not an option that I have: I doubt that, there is always an option, if it is not possible, then it is not. Point.
  • Aman Alam
    Aman Alam over 10 years
    I am trying and tweaking your methodology, I'd surely accept an answer as soon as I find one useful :) Can you please tell me what will be advantages of using NMEA values?
  • Pierre-Luc Paour
    Pierre-Luc Paour over 10 years
    I'm not suggesting you use the NMEA values, just offering that if you wanted to get the raw GPS data out of curiosity, that was how you would do it. There is one important reason to get the NMEA data: to get the geoid for your location (the difference between the altitude above sea level and the altitude above the WGS84 ellipsoid), since the altitude reported by Location.getAltitude is based on the ellipsoid and may be over 100m off.
  • Aman Alam
    Aman Alam about 10 years
    Although I am now using a different approach to trick the UI and keep using getSpeed() , your answer took me very close. Accepting it. Thanks!
  • Rameshbabu
    Rameshbabu over 7 years
    @Pierre-LucPaour MyTrails hyper link was broken. Please update it for further reference.
  • Pierre-Luc Paour
    Pierre-Luc Paour over 7 years
    @Rameshababu thanks, the server was unstable for while, it's back up now.