Use system time instead of GPS-provided time.

#316.
This commit is contained in:
Dennis Guse
2020-12-22 21:41:04 +01:00
parent fcc17413e3
commit bc66cd33a9
8 changed files with 49 additions and 106 deletions
@@ -190,9 +190,15 @@ public class ChartFragmentTest {
*/
@Test
public void testCreatePendingPoint_time() {
// given
chartFragment.setChartByDistance(false);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setTime(TrackStubUtils.INITIAL_TIME); //Keep old TrackPoint behavior of having time=0 for this test
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
// then
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
long timeSpan = 222;
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
@@ -3,10 +3,12 @@ package de.dennisguse.opentracks.services.handlers;
import android.content.Context;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
@@ -32,6 +34,12 @@ public class LocationHandlerTest {
@InjectMocks
private LocationHandler locationHandler;
@BeforeClass
public static void preSetUp() {
// Prepare looper for Android's message queue
if (Looper.myLooper() == null) Looper.prepare();
}
@Before
public void setUp() {
// Let's use default values.
@@ -80,7 +88,6 @@ public class LocationHandlerTest {
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
// when
// poor latitude
locationHandler.onLocationChanged(createLocation(45f, 35f, prefAccuracy + 1, 5, System.currentTimeMillis()));
// then
@@ -1,53 +0,0 @@
package de.dennisguse.opentracks.util;
import org.junit.Test;
import de.dennisguse.opentracks.content.data.TrackPoint;
import static org.junit.Assert.assertEquals;
public class TrackPointUtilsTest {
@Test
public void fixTime_none() {
// given
long time = System.currentTimeMillis();
TrackPoint trackPoint = new TrackPoint();
trackPoint.setTime(time);
// when
TrackPointUtils.fixTime(trackPoint);
// then
assertEquals(time, trackPoint.getTime());
}
@Test
public void fixTime_0() {
// given
long time = System.currentTimeMillis();
TrackPoint trackPoint = new TrackPoint();
trackPoint.setTime(0);
// when
TrackPointUtils.fixTime(trackPoint);
// then
assertEquals(time, trackPoint.getTime(), 1000);
}
@Test
public void fixTime_gpsWeekRollover() {
// given
long time = System.currentTimeMillis();
TrackPoint trackPoint = new TrackPoint();
trackPoint.setTime(time - 1024 * UnitConversions.ONE_WEEK_MS);
// when
TrackPointUtils.fixTime(trackPoint);
// then
assertEquals(time, trackPoint.getTime());
}
}
@@ -27,7 +27,8 @@ import java.util.Objects;
/**
* This class extends the standard Android location with extra information.
* <p>
* NOTE: default location will be latitude=0.0 and longitude=0.0 (this is not meaningful).
* NOTE: default location will be latitude=0.0 and longitude=0.0.
* For {@link TrackPoint}s with `type == null` this is not meaningful.
* <p>
* NOTE: For Locations provided by the GPS.
* We are replacing the GPS-provided time using the system time.
@@ -99,6 +100,7 @@ public class TrackPoint {
public TrackPoint(@NonNull Location location) {
this.type = Type.TRACKPOINT;
this.location = location;
setTime(System.currentTimeMillis());
}
public TrackPoint(@NonNull Type type, @NonNull Location location) {
@@ -554,8 +554,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
notificationManager.updateTrackPoint(this, trackPoint, recordingGpsAccuracy);
TrackPointUtils.fixTime(trackPoint);
//TODO Figure out how to avoid loading the lastValidTrackPoint from the database
TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId());
@@ -1,10 +1,12 @@
package de.dennisguse.opentracks.services.handlers;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.Handler;
import androidx.annotation.Nullable;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.UnitConversions;
@@ -27,10 +29,12 @@ class GpsStatus {
private GpsStatusListener client;
private final Context context;
// Last location. It can be null.
private Location lastLocation = null;
@Nullable
private TrackPoint lastTrackPoint = null;
@Nullable
// The last valid (not null) location. Null value means that there have not been any location yet.
private Location lastValidLocation = null;
private TrackPoint lastValidTrackPoint = null;
// Flag to prevent GpsStatus checks two or more locations at the same time.
private boolean checking = false;
@@ -97,28 +101,28 @@ class GpsStatus {
}
/**
* This method must be called from the client every time a new location is received.
* Receive new location and calculate the new status if needed.
* This method must be called from the client every time a new trackPoint is received.
* Receive new trackPoint and calculate the new status if needed.
* It look for GPS changes in lastLocation if it's not null. If it's null then look for in lastValidLocation if any.
*/
public void onLocationChanged(final Location location) {
public void onLocationChanged(final TrackPoint trackPoint) {
if (checking) {
return;
}
checking = true;
if (lastLocation != null) {
if (lastTrackPoint != null) {
checkStatusFromLastLocation();
} else if (lastValidLocation != null) {
} else if (lastValidTrackPoint != null) {
checkStatusFromLastValidLocation();
}
if (location != null) {
// Update location's time to the current time millis when location has been received.
location.setTime(System.currentTimeMillis());
lastValidLocation = location;
if (trackPoint != null) {
// Update trackPoint's time to the current time millis when trackPoint has been received.
trackPoint.setTime(System.currentTimeMillis());
lastValidTrackPoint = trackPoint;
}
lastLocation = location;
lastTrackPoint = trackPoint;
checking = false;
}
@@ -129,19 +133,19 @@ class GpsStatus {
* Also, it'll run the runnable if signal is bad or stop it if the signal is lost.
*/
private void checkStatusFromLastLocation() {
if (System.currentTimeMillis() - lastLocation.getTime() > signalLostThreshold && gpsStatus != GpsStatusValue.GPS_SIGNAL_LOST) {
if (System.currentTimeMillis() - lastTrackPoint.getTime() > signalLostThreshold && gpsStatus != GpsStatusValue.GPS_SIGNAL_LOST) {
// Too much time without receiving signal -> signal lost.
GpsStatusValue oldStatus = gpsStatus;
gpsStatus = GpsStatusValue.GPS_SIGNAL_LOST;
sendStatus(oldStatus, gpsStatus);
stopStatusRunner();
} else if (lastLocation.getAccuracy() > signalBadThreshold && gpsStatus != GpsStatusValue.GPS_SIGNAL_BAD) {
} else if (lastTrackPoint.getAccuracy() > signalBadThreshold && gpsStatus != GpsStatusValue.GPS_SIGNAL_BAD) {
// Too little accuracy -> bad signal.
GpsStatusValue oldStatus = gpsStatus;
gpsStatus = GpsStatusValue.GPS_SIGNAL_BAD;
sendStatus(oldStatus, gpsStatus);
startStatusRunner();
} else if (lastLocation.getAccuracy() <= signalBadThreshold && gpsStatus != GpsStatusValue.GPS_SIGNAL_FIX) {
} else if (lastTrackPoint.getAccuracy() <= signalBadThreshold && gpsStatus != GpsStatusValue.GPS_SIGNAL_FIX) {
// Gps okay.
GpsStatusValue oldStatus = gpsStatus;
gpsStatus = GpsStatusValue.GPS_SIGNAL_FIX;
@@ -156,13 +160,13 @@ class GpsStatus {
* If there is any change then it does the change.
*/
private void checkStatusFromLastValidLocation() {
if (System.currentTimeMillis() - lastValidLocation.getTime() > signalLostThreshold) {
if (System.currentTimeMillis() - lastValidTrackPoint.getTime() > signalLostThreshold) {
// Too much time without locations -> lost signal? (wait signalLostThreshold from last valid location).
GpsStatusValue oldStatus = gpsStatus;
gpsStatus = GpsStatusValue.GPS_SIGNAL_LOST;
sendStatus(oldStatus, gpsStatus);
stopStatusRunner();
lastValidLocation = null;
lastValidTrackPoint = null;
}
}
@@ -192,8 +196,8 @@ class GpsStatus {
GpsStatusValue oldStatus = gpsStatus;
gpsStatus = GpsStatusValue.GPS_DISABLED;
sendStatus(oldStatus, gpsStatus);
lastLocation = null;
lastValidLocation = null;
lastTrackPoint = null;
lastValidTrackPoint = null;
stopStatusRunner();
}
}
@@ -90,18 +90,20 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
*/
@Override
public void onLocationChanged(@NonNull Location location) {
TrackPoint trackPoint = new TrackPoint(location);
boolean isAccurate = TrackPointUtils.fulfillsAccuracy(trackPoint, recordingGpsAccuracy);
boolean isValid = LocationUtils.isValidLocation(location);
if (gpsStatus != null) {
gpsStatus.onLocationChanged(location);
gpsStatus.onLocationChanged(trackPoint);
}
if (!LocationUtils.isValidLocation(location)) {
if (!isValid) {
Log.w(TAG, "Ignore newTrackPoint. location is invalid.");
return;
}
TrackPoint trackPoint = new TrackPoint(location);
if (!TrackPointUtils.fulfillsAccuracy(trackPoint, recordingGpsAccuracy)) {
if (!isAccurate) {
Log.d(TAG, "Ignore newTrackPoint. Poor accuracy.");
return;
}
@@ -1,7 +1,5 @@
package de.dennisguse.opentracks.util;
import android.util.Log;
import androidx.annotation.NonNull;
import de.dennisguse.opentracks.content.data.TrackPoint;
@@ -16,27 +14,6 @@ public class TrackPointUtils {
private TrackPointUtils() {
}
/**
* 1. Ancient fix for phones that do not set the time in {@link android.location.Location}.
* 2. Fix for GPS time rollover happening every 19.7 years: https://en.wikipedia.org/wiki/GPS_Week_Number_Rollover
*/
public static void fixTime(@NonNull TrackPoint trackPoint) {
if (trackPoint.getTime() == 0L) {
Log.w(TAG, "Time of provided location was 0. Using current time.");
trackPoint.setTime(System.currentTimeMillis());
return;
}
{
long timeDiff = Math.abs(trackPoint.getTime() - System.currentTimeMillis());
if (timeDiff > 1023 * UnitConversions.ONE_WEEK_MS) {
Log.w(TAG, "GPS week rollover.");
trackPoint.setTime(trackPoint.getTime() + 1024 * UnitConversions.ONE_WEEK_MS);
}
}
}
public static boolean isMoving(@NonNull TrackPoint trackPoint) {
return trackPoint.hasSpeed() && trackPoint.getSpeed() >= MAX_NO_MOVEMENT_SPEED;
}