Skip to content

Commit

Permalink
Sensor data: use monotonic clock (for isRecent).
Browse files Browse the repository at this point in the history
Part of #1966.
  • Loading branch information
dennisguse committed Dec 28, 2024
1 parent cb9632c commit 3eb1f8f
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public void setUp() throws TimeoutException {
contentProviderUtils.insertMarker(new Marker("Marker 2", "Marker 2 desc", "Marker 2 category", null, trackId, service.getLastStoredTrackPointWithLocation(), ""));

trackPointCreator.setClock("2020-02-02T02:02:18Z");
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
service.endCurrentTrack();

trackPointCreator.setClock("2020-02-02T02:03:20Z");
Expand All @@ -172,7 +172,7 @@ public void setUp() throws TimeoutException {

sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0f);

trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
trackPointCreator.setClock("2020-02-02T02:04:00Z");
service.endCurrentTrack();

Expand Down Expand Up @@ -550,22 +550,22 @@ private void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Di

AggregatorCyclingPower cyclingPower = Mockito.mock(AggregatorCyclingPower.class);
Mockito.when(cyclingPower.hasValue()).thenReturn(true);
Mockito.when(cyclingPower.getValue()).thenReturn(Power.of(power));
Mockito.when(cyclingPower.getValue(Mockito.any())).thenReturn(Power.of(power));
sensorDataSet.add(cyclingPower);

AggregatorHeartRate agheartRate = Mockito.mock(AggregatorHeartRate.class);
Mockito.when(agheartRate.getValue()).thenReturn(HeartRate.of(heartRate));
sensorDataSet.add(agheartRate);
AggregatorHeartRate avgHeartRate = Mockito.mock(AggregatorHeartRate.class);
Mockito.when(avgHeartRate.getValue(Mockito.any())).thenReturn(HeartRate.of(heartRate));
sensorDataSet.add(avgHeartRate);

AggregatorCyclingCadence cyclingCadence = Mockito.mock(AggregatorCyclingCadence.class);
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
Mockito.when(cyclingCadence.getValue(Mockito.any())).thenReturn(Cadence.of(cadence));
sensorDataSet.add(cyclingCadence);

if (distance != null && speed != null) {
AggregatorCyclingDistanceSpeed distanceSpeed = Mockito.mock(AggregatorCyclingDistanceSpeed.class);
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
Mockito.when(distanceSpeed.getValue()).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed)));
Mockito.when(distanceSpeed.getValue(Mockito.any())).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed)));
sensorDataSet.add(distanceSpeed);
} else {
sensorDataSet.add(new AggregatorCyclingDistanceSpeed("", ""));
Expand All @@ -582,7 +582,7 @@ private void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altit
if (altitudeGain != null) {
AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
Mockito.when(barometer.hasValue()).thenReturn(true);
Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
Mockito.when(barometer.getValue(Mockito.any())).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
sensorDataSet.add(barometer);
} else {
sensorDataSet.add(new AggregatorBarometer("test", null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import org.junit.Assert;
import org.junit.Test;

import java.time.Instant;

import de.dennisguse.opentracks.data.models.AtmosphericPressure;

public class AggregatorBarometerTest {

private static void addSensorValue(AggregatorBarometer aggregatorBarometer, float[] values) {
for (float f : values) {
aggregatorBarometer.add(new Raw<>(AtmosphericPressure.ofHPA(f)));
aggregatorBarometer.add(new Raw<>(Instant.MIN, AtmosphericPressure.ofHPA(f)));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;

import java.time.Instant;

import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
Expand All @@ -23,35 +25,35 @@ public void compute_cadence_1() {
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");

// when
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 2048)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 2048)));

// then
assertEquals(60, current.getValue().getRPM(), 0.01);
assertEquals(60, current.getValue(Instant.MIN).getRPM(), 0.01);
}

@Test
public void compute_cadence_2() {
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");

// when
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 6184)));
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 8016)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 6184)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 8016)));

// then
assertEquals(33.53, current.getValue().getRPM(), 0.01);
assertEquals(33.53, current.getValue(Instant.MIN).getRPM(), 0.01);
}

@Test
public void compute_cadence_sameCount() {
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");

// when
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 2048)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 2048)));

// then
assertEquals(Cadence.of(0), current.getValue());
assertEquals(Cadence.of(0), current.getValue(Instant.MIN));
}


Expand All @@ -60,8 +62,8 @@ public void compute_cadence_sameTime() {
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");

// when
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 1024)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 1024)));

// then
assertFalse(current.hasValue()); //TODO Cadence should be 0?
Expand All @@ -72,11 +74,11 @@ public void compute_cadence_rollOverTime() {
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");

// when
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, UintUtils.UINT16_MAX - 1024)));
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 0)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, UintUtils.UINT16_MAX - 1024)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 0)));

// then
assertEquals(60, current.getValue().getRPM(), 0.01);
assertEquals(60, current.getValue(Instant.MIN).getRPM(), 0.01);
}

@Test
Expand All @@ -85,13 +87,13 @@ public void compute_cadence_rollOverCount() {
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");

// when
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(UintUtils.UINT32_MAX - 1, 1024)));
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(0, 2048)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(UintUtils.UINT32_MAX - 1, 1024)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(0, 2048)));

// then
// TODO See #953
// assertEquals(60, current.getValue().getRPM(), 0.01);
assertNull(current.getValue());
assertNull(current.getValue(Instant.MIN));
}

@Test
Expand All @@ -100,12 +102,12 @@ public void compute_speed() {
current.setWheelCircumference(Distance.ofMM(2150));

// when
current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(1, 6184)));
current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(2, 8016)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(1, 6184)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(2, 8016)));

// then
assertEquals(2.15, current.getValue().distance().toM(), 0.01);
assertEquals(1.20, current.getValue().speed().toMPS(), 0.01);
assertEquals(2.15, current.getValue(Instant.MIN).distance().toM(), 0.01);
assertEquals(1.20, current.getValue(Instant.MIN).speed().toMPS(), 0.01);
}

@Test
Expand All @@ -115,14 +117,14 @@ public void compute_speed_rollOverCount() {
current.setWheelCircumference(Distance.ofMM(2000));

// when
current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(UintUtils.UINT32_MAX - 1, 1024)));
current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(0, 2048)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(UintUtils.UINT32_MAX - 1, 1024)));
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(0, 2048)));


// then
// TODO See #953
// assertEquals(2, current.getValue().getDistance().toM(), 0.01);
// assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01);
assertNull(current.getValue());
assertNull(current.getValue(Instant.MIN));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,11 @@ public void testRecording_blesensor_only_no_distance() {
String sensor1 = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(sensor1);

sensorManager.onChanged(new Raw<>(HeartRate.of(5))); //Should be ignored
sensorManager.onChanged(new Raw<>(sensor1, HeartRate.of(5))); //Should be ignored

String sensor3 = "2020-02-02T02:02:13Z";
trackPointCreator.setClock(sensor3);
sensorManager.onChanged(new Raw<>(HeartRate.of(7)));
sensorManager.onChanged(new Raw<>(sensor3, HeartRate.of(7)));

String stopTime = "2020-02-02T02:02:15Z";
trackPointCreator.setClock(stopTime);
Expand Down Expand Up @@ -646,12 +646,12 @@ public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() {
// when
String sensor1 = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(sensor1);
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(0)))); //Should be ignored
sensorManager.onChanged(new Raw<>(sensor1, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(0)))); //Should be ignored

// when
String sensor2 = "2020-02-02T02:02:04Z";
trackPointCreator.setClock(sensor2);
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(2))));
sensorManager.onChanged(new Raw<>(sensor2, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(2))));

// when
String gps1 = "2020-02-02T02:02:05Z";
Expand All @@ -660,12 +660,12 @@ public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() {
// when
String sensor3 = "2020-02-02T02:02:06Z";
trackPointCreator.setClock(sensor3);
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(12))));
sensorManager.onChanged(new Raw<>(sensor3, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(12))));

// when
String sensor4 = "2020-02-02T02:02:07Z";
trackPointCreator.setClock(sensor4);
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(14)))); //Should be ignored
sensorManager.onChanged(new Raw<>(sensor4, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(14)))); //Should be ignored

// when
String gps2 = "2020-02-02T02:02:08Z";
Expand All @@ -674,7 +674,7 @@ public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() {
// when
String sensor5 = "2020-02-02T02:02:10Z";
trackPointCreator.setClock(sensor5);
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(16)))); //Should be ignored
sensorManager.onChanged(new Raw<>(sensor5, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(16)))); //Should be ignored

// when
String gps3 = "2020-02-02T02:02:12Z";
Expand Down Expand Up @@ -730,7 +730,7 @@ public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() {
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
Mockito.when(barometer.hasValue()).thenReturn(true);
Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
Mockito.when(barometer.getValue(Mockito.any())).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));

trackPointCreator.getSensorManager().sensorDataSet.barometer = barometer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -64,6 +64,7 @@ public Instant getNow() {

public SensorManager(TrackPointCreator observer) {
this.observer = observer;
this.sensorDataSet = new SensorDataSet(observer);
}

public void start(Context context, Handler handler) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ public boolean hasValue() {
@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();
Expand All @@ -62,12 +62,12 @@ public void reset() {
/**
* 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));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@


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);
}
}
Loading

0 comments on commit 3eb1f8f

Please sign in to comment.