As we all know GPS system is far from perfect and when you go around with your app using the gps when the GPS loses the signal and it starts calculating again. And if you try to calculate the distance correctly it will start calculating and for example if you were 30 meters away from your destination it will calculate for example 130 meters. Which messes my how calculation of the distance up. So I found some suggestion that I should filter the GPS coordination using the speed.
I want to calculate the speed without the location.getSpeed()
I want to calculate the speed by comparing the distance from the last known coordinates to the coordinates at some given point and I get the speed of the device moving. And if the speed is for example greater than 15 m/h coordinates are invalid and don't re-calculate the distance.
It is as simple as V = (Distance) / (Elapsed time).
So lets say you read your location1 at time X.
Than at some point after X lets say at time Y you read your location again.
Than you'll have
float distance = location1.distanceTo(location2);
which is float in meters (see this http://developer.android.com/reference/android/location/Location.html#distanceTo(android.location.Location)
To have your velocity as in meters per seconds if you calculate X - Y as
float X = System.currentTimeMillis(); //When you get the first location
float Y = System.currentTimeMillis(); //When you get the second location
//...
//Than when calculating
float timeElapsed= (Y - X)/1000; //In seconds
Than the velocity in meters per second will be
float V = distance/timeElapsed;
If you want to calculate average velocity, you need to store the velocities in a list lets say, and than calculate average from all those velocities calculated between each two points. So if you have location l1, l2, l3, l4 ..... ln, V1 will be velocity between l1 and l2, V2 will be between l2 and l3, and Vn-1 will be between ln-1 and ln. You will store all Vn in a list (for example) than you caluclate the average as
Vavg = (V1 + V2 + V3 ... + Vn)/n
UPDATE:
In your activity
Location previousLocation = null;
float previousTime = 0;
float velocity = 0;
Than:
public void onLocationChanged(Location loc) {
boolean hasPrevious = true;
if (previousLocation == null || previousTime == 0) {
hasPrevious = false;
}
float currentTime = System.currentTimeMillis();
if (hasPrevious) {
float timeElapsed = (currentTime - previousTime)/1000;
velocity = loc.distanceTo(previousLocation)/timeElapsed;
}
storeToPrevious(loc, currentTime);
}
In a different function
private void storeToPrevious(Location l, float time) {
previousLocation = new Location(l);
previousTime = time;
}
This may do what you want. It is written in Kotlin. It applies a weighted moving average. Most recent locations have a heavier weight. It can "smooth" out the speed, at the cost of adding more lag. This was to get around a bug in certain situations that I could not use getSpeed(). But normally if you use "Fused" location on Android, the speed is quite stable and accurate on a typical modern, ACTIVE phone.
var recentGPSLocationSegments = listOf<Pair<android.location.Location, android.location.Location>>()
fun applyWeightedMovingAverageSpeed(location: android.location.Location, previous: android.location.Location): Double
{
recentGPSLocationSegments += Pair(location, previous)
val cachedLocationsNs = location.elapsedRealtimeNanos - 4500000000 // 4.5 seconds, This will typically get 4 entries (1 second apart)
val targetZeroWeightNs = location.elapsedRealtimeNanos - 5000000000 // 5.0 seconds, Weights will be approx 5000000000, 4000000000, 3000000000, 1000000000
// Toss old locations
recentGPSLocationSegments = recentGPSLocationSegments.filter { it -> it.first.elapsedRealtimeNanos > cachedLocationsNs }
// Total up all the weights. Weight is based on age, younger has higher weight
val weights = recentGPSLocationSegments.map { it.first.elapsedRealtimeNanos - targetZeroWeightNs }.sum()
// Apply the weights and get average speed in meters/second
return recentGPSLocationSegments.map { speedFromGPS(it.first, it.second) * (it.first.elapsedRealtimeNanos - targetZeroWeightNs) }.sum() / weights
}
fun speedFromGPS(location: android.location.Location, previous: android.location.Location): Double
{
val dist = location.distanceTo(previous)
val time = (location.elapsedRealtimeNanos - previous.elapsedRealtimeNanos) / 1000000000.0
return dist / time
}
val locationManagerExample: LocationListener = object : LocationListener
{
var lastLocation: android.location.Location? = null
var lastPreviousLocation: android.location.Location? = null
override fun onLocationChanged(location: android.location.Location?)
{
if (location != null)
{
if (lastPreviousLocation != null)
{
currentSpeed = applyWeightedMovingAverageSpeed(location, lastPreviousLocation!!)
lastPreviousLocation = lastLocation
}
lastLocation = location
if (currentSpeed < 0.0)
{
currentSpeed = 0.0
}
}
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle)
{
}
override fun onProviderEnabled(provider: String)
{
}
override fun onProviderDisabled(provider: String)
{
}
}
Try this
@Override
public void onLocationChanged(Location location) {
try {
if (location != null) {
if (current_lat != null && current_lat > 0) {
latitude = current_lat;
}
if (current_long != null && current_long > 0) {
longitude = current_long;
}
current_lat = location.getLatitude();
current_long = location.getLongitude();
distanceBetweenTwoPoint = getDistance(latitude, longitude, current_lat, current_long);
if ((current_lat > 0 && current_long > 0) && distanceBetweenTwoPoint > IjoomerApplicationConfiguration.track_DistanceBetweenPoints_IN_METERS) {
if (location.hasSpeed()) {
speedInKm = location.getSpeed() * 3.6;
} else {
speedInKm = 0.0;
}
row = new HashMap<String, String>();
row.put(TRACKID, IN_TRACKID + "");
row.put(LATITUDE, current_lat.toString());
row.put(LONGITUDE, current_long.toString());
row.put(SPEED, speedInKm + "");
row.put(TIMESTAMP, System.currentTimeMillis() + "");
row.put(STATUS, status);
distance = distance + (distanceBetweenTwoPoint / 1000);
row.put(DISTANCE, "" + distance);
dataProvider.InsertRow("TrackDetail", row);
row.put(LASTKNOWNLATITUDE, latitude.toString());
row.put(LASTKNOWNLONGITUDE, longitude.toString());
int seconds = (int) ((System.currentTimeMillis() - trackStartTime) / 1000) % 60;
int minutes = (int) (((System.currentTimeMillis() - trackStartTime) / (1000 * 60)) % 60);
int hours = (int) (((System.currentTimeMillis() - trackStartTime) / (1000 * 60 * 60)) % 24);
row.put(DURATION, String.valueOf(hours) + " : " + String.valueOf(minutes) + " : " + String.valueOf(seconds));
setNotification(speedInKm, String.valueOf(hours) + " : " + String.valueOf(minutes) + " : " + String.valueOf(seconds));
if (status.equalsIgnoreCase("1")) {
builder.append("|" + current_lat + "," + current_long);
trackDuration = String.valueOf(hours) + " : " + String.valueOf(minutes) + " : " + String.valueOf(seconds);
if (speedInKm > maxSpeed) {
maxSpeed = speedInKm;
}
totalSpeed = totalSpeed + speedInKm;
++totalTrackPoint;
sendBrodcastToActivity();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}