Sensor data: use monotonic clock (for isRecent).

Part of #1966.
This commit is contained in:
Dennis Guse
2024-12-28 13:53:40 +01:00
parent 333bc6f31b
commit 01f4359cdf
9 changed files with 82 additions and 64 deletions
@@ -23,7 +23,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
//TODO Should be final and not be visible for testing
@VisibleForTesting
public SensorDataSet sensorDataSet = new SensorDataSet();
public SensorDataSet sensorDataSet;
private final TrackPointCreator observer;
@@ -64,6 +64,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
public SensorManager(TrackPointCreator observer) {
this.observer = observer;
this.sensorDataSet = new SensorDataSet(observer);
}
public void start(Context context, Handler handler) {
@@ -42,11 +42,11 @@ public abstract class Aggregator<Input, Output> {
@NonNull
protected abstract Output getNoneValue();
public Output getValue() {
public Output getValue(Instant now) {
if (!hasValue()) {
return null; //TODO Check if this is a good idea!
}
if (isRecent()) {
if (isRecent(now)) {
return value;
}
return getNoneValue();
@@ -62,12 +62,12 @@ public abstract class Aggregator<Input, Output> {
/**
* Is the data recent considering the current time.
*/
private boolean isRecent() {
private boolean isRecent(Instant now) {
if (previous == null) {
return false;
}
return Instant.now()
return now
.isBefore(previous.time().plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE));
}
@@ -2,15 +2,21 @@ package de.dennisguse.opentracks.sensors.sensorData;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.time.Clock;
import java.time.Instant;
public record Raw<T>(
@NonNull Instant time,
@NonNull T value
) {
@Deprecated
public Raw(@NonNull T value) {
this(Instant.now(), value); //TODO We should be using the MonotonicClock
public Raw(@NonNull Clock clock, @NonNull T value) {
this(clock.instant(), value);
}
@VisibleForTesting
public Raw(@NonNull String time, @NonNull T value) {
this(Instant.parse(time), value);
}
}
@@ -17,6 +17,7 @@ import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed;
import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerCyclingPower;
import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence;
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
import de.dennisguse.opentracks.settings.PreferencesUtils;
public final class SensorDataSet {
@@ -43,11 +44,15 @@ public final class SensorDataSet {
public AggregatorGPS gps;
public SensorDataSet() {
private TrackPointCreator trackPointCreator;
public SensorDataSet(TrackPointCreator trackPointCreator) {
this.trackPointCreator = trackPointCreator;
}
@Deprecated //TODO This is not a copy constructor anymore, but it should be - aggregators are no value objects; best guess: can be removed
public SensorDataSet(SensorDataSet toCopy) {
//TODO This is not a copy constructor anymore, but it should be - aggregators are no value objects
this.trackPointCreator = toCopy.trackPointCreator;
this.heartRate = toCopy.heartRate;
this.cyclingCadence = toCopy.cyclingCadence;
this.cyclingDistanceSpeed = toCopy.cyclingDistanceSpeed;
@@ -59,7 +64,7 @@ public final class SensorDataSet {
public Pair<HeartRate, String> getHeartRate() {
if (heartRate != null) {
return new Pair<>(heartRate.getValue(), heartRate.getSensorNameOrAddress());
return new Pair<>(heartRate.getValue(trackPointCreator.createNow()), heartRate.getSensorNameOrAddress());
}
return null;
@@ -67,7 +72,7 @@ public final class SensorDataSet {
public Pair<Cadence, String> getCadence() {
if (cyclingCadence != null) {
return new Pair<>(cyclingCadence.getValue(), cyclingCadence.getSensorNameOrAddress());
return new Pair<>(cyclingCadence.getValue(trackPointCreator.createNow()), cyclingCadence.getSensorNameOrAddress());
}
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.value.cadence() != null) {
@@ -78,11 +83,11 @@ public final class SensorDataSet {
}
public Pair<Speed, String> getSpeed() {
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue() && cyclingDistanceSpeed.getValue().speed() != null) {
return new Pair<>(cyclingDistanceSpeed.getValue().speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue() && cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).speed() != null) {
return new Pair<>(cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
}
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue().speed() != null) {
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue(trackPointCreator.createNow()).speed() != null) {
return new Pair<>(runningDistanceSpeedCadence.value.speed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
}
@@ -152,7 +157,7 @@ public final class SensorDataSet {
public void fillTrackPoint(TrackPoint trackPoint) {
if (gps != null && gps.hasValue()) {
trackPoint.setPosition(gps.getValue());
trackPoint.setPosition(gps.getValue(trackPointCreator.createNow()));
}
if (getHeartRate() != null) {
@@ -168,20 +173,20 @@ public final class SensorDataSet {
}
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().distanceOverall());
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).distanceOverall());
}
if (cyclingPower != null && cyclingPower.hasValue()) {
trackPoint.setPower(cyclingPower.getValue());
trackPoint.setPower(cyclingPower.getValue(trackPointCreator.createNow()));
}
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) {
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue().distance());
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue(trackPointCreator.createNow()).distance());
}
if (barometer != null && barometer.hasValue()) {
trackPoint.setAltitudeGain(barometer.getValue().gain_m());
trackPoint.setAltitudeLoss(barometer.getValue().loss_m());
trackPoint.setAltitudeGain(barometer.getValue(trackPointCreator.createNow()).gain_m());
trackPoint.setAltitudeLoss(barometer.getValue(trackPointCreator.createNow()).loss_m());
}
}
@@ -3,6 +3,8 @@ package de.dennisguse.opentracks.viewmodels;
import android.util.Pair;
import android.view.LayoutInflater;
import java.time.Instant;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.HeartRateZones;
import de.dennisguse.opentracks.databinding.StatsSensorItemBinding;
@@ -92,7 +94,7 @@ public abstract class SensorStatisticsViewHolder extends StatisticViewHolder<Sta
Pair<String, String> valueAndUnit;
if (sensorDataSet != null && sensorDataSet.getCyclingPower() != null) {
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.getCyclingPower().getValue());
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.getCyclingPower().getValue(Instant.now())); //TODO Use MonotonicClock
sensorName = sensorDataSet.getCyclingPower().getSensorNameOrAddress();
} else {
valueAndUnit = StringUtils.getCadenceParts(getContext(), null);