From 6eee6a2817aa1c1e8ad3902deedb71f64b2a1f54 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 15 Nov 2023 21:28:20 +0100 Subject: [PATCH] Barometer: use sensor aggregator. Part of #1424. --- .../io/file/importer/ExportImportTest.java | 75 ++++++------ .../sensors/AltitudeSumManagerTest.java | 58 ---------- .../sensorData/AggregatorBarometerTest.java | 28 +++++ .../TrackRecordingServiceRecordingTest.java | 60 +++++----- src/androidTest/res/raw/csv_export.csv | 2 +- .../data/models/AltitudeGainLoss.java | 3 + .../sensors/AltitudeSumManager.java | 108 ------------------ .../opentracks/sensors/GainManager.java | 46 ++++++++ .../opentracks/sensors/SensorManager.java | 10 +- .../sensors/driver/BarometerInternal.java | 7 +- .../sensorData/AggregatorBarometer.java | 46 ++++++++ .../sensors/sensorData/SensorDataSet.java | 22 +++- .../services/handlers/TrackPointCreator.java | 7 +- 13 files changed, 228 insertions(+), 244 deletions(-) delete mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java create mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java create mode 100644 src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java delete mode 100644 src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/GainManager.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index 3d8da5ae02..97b61dda07 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -48,6 +48,7 @@ import de.dennisguse.opentracks.content.data.TestDataUtil; import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.models.ActivityType; +import de.dennisguse.opentracks.data.models.AltitudeGainLoss; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.HeartRate; @@ -58,8 +59,8 @@ import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.exporter.TrackExporter; -import de.dennisguse.opentracks.sensors.AltitudeSumManager; import de.dennisguse.opentracks.sensors.sensorData.Aggregator; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence; import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower; @@ -78,8 +79,6 @@ @RunWith(AndroidJUnit4.class) public class ExportImportTest { - private static final String TAG = ExportImportTest.class.getSimpleName(); - @Rule public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS); @@ -140,23 +139,22 @@ public void setUp() throws TimeoutException { Distance sensorDistance = Distance.of(10); // recording distance interval - sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1); + sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1f); service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null); // A sensor-only TrackPoint trackPointCreator.setClock("2020-02-02T02:02:04Z"); - mockAltitudeChange(trackPointCreator, 1); - mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f); + mockSensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f, 1f); trackPointCreator.setClock("2020-02-02T02:02:14Z"); - mockBLESensorData(trackPointCreator, 15f, null, 67f, 3f, 50f); + mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null); trackPointCreator.setClock("2020-02-02T02:02:15Z"); - mockBLESensorData(trackPointCreator, null, null, 68f, 3f, 50f); + mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null); trackPointCreator.setClock("2020-02-02T02:02:16Z"); - mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f); // Distance will be added to next TrackPoint + mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint - sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0); + sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0f); service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null); trackPointCreator.setClock("2020-02-02T02:02:18Z"); @@ -166,15 +164,16 @@ public void setUp() throws TimeoutException { trackPointCreator.setClock("2020-02-02T02:03:20Z"); service.resumeTrack(trackId); - sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0); + sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0f); - sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0); + sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0f); trackPointCreator.setClock("2020-02-02T02:03:30Z"); service.getTrackRecordingManager().onIdle(); - sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0); + sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0f); + trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(); trackPointCreator.setClock("2020-02-02T02:04:00Z"); service.endCurrentTrack(); @@ -272,7 +271,9 @@ public void track() throws TimeoutException { .setAltitudeLoss(0f) .setAltitudeGain(0f) .setHorizontalAccuracy(Distance.of(10)), - new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z")), + new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z")) + .setAltitudeLoss(0f) + .setAltitudeGain(0f), new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:50Z")) .setLatitude(3) .setLongitude(16.001) @@ -282,7 +283,6 @@ public void track() throws TimeoutException { .setAltitudeGain(0f) .setHorizontalAccuracy(Distance.of(10)), new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z")) - ), actual); } @@ -546,42 +546,51 @@ private void assertMarkers() { } } - private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) { - - SensorDataSet sensorDataSet = new SensorDataSet(); + private void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain) { + SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet; - sensorDataSet.cyclingPower = Mockito.mock(AggregatorCyclingPower.class); - Mockito.when( sensorDataSet.cyclingPower.hasValue()).thenReturn(true); - Mockito.when(sensorDataSet.cyclingPower.getValue()).thenReturn(Power.of(power)); + AggregatorCyclingPower cyclingPower = Mockito.mock(AggregatorCyclingPower.class); + Mockito.when(cyclingPower.hasValue()).thenReturn(true); + Mockito.when(cyclingPower.getValue()).thenReturn(Power.of(power)); + sensorDataSet.add(cyclingPower); - sensorDataSet.heartRate = Mockito.mock(AggregatorHeartRate.class); - Mockito.when(sensorDataSet.heartRate.getValue()).thenReturn(HeartRate.of(heartRate)); + AggregatorHeartRate agheartRate = Mockito.mock(AggregatorHeartRate.class); + Mockito.when(agheartRate.getValue()).thenReturn(HeartRate.of(heartRate)); + sensorDataSet.add(agheartRate); AggregatorCyclingCadence cyclingCadence = Mockito.mock(AggregatorCyclingCadence.class); Mockito.when(cyclingCadence.hasValue()).thenReturn(true); Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence)); - sensorDataSet.cyclingCadence = cyclingCadence; + 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))); - sensorDataSet.cyclingDistanceSpeed = distanceSpeed; + sensorDataSet.add(distanceSpeed); + } else { + sensorDataSet.add(new AggregatorCyclingDistanceSpeed("", "")); } - trackPointCreator.getSensorManager().sensorDataSet = sensorDataSet; - trackPointCreator.onChange(sensorDataSet); + mockAltitudeChange(trackPointCreator, altitudeGain); - //TODO Might require: getSensorManager().sensorDataSet = new SensorDataSet() + trackPointCreator.onChange(sensorDataSet); } - private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) { - AltitudeSumManager altitudeSumManager = trackPointCreator.getSensorManager().getAltitudeSumManager(); - altitudeSumManager.setAltitudeGain_m(altitudeGain); - altitudeSumManager.setAltitudeLoss_m(altitudeGain); + private void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) { + SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet; + + if (altitudeGain != null) { + AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class); + Mockito.when(barometer.hasValue()).thenReturn(true); + Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain)); + sensorDataSet.add(barometer); + } else { + sensorDataSet.add(new AggregatorBarometer("")); + } } - private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, float altitudeGain) { + private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, Float altitudeGain) { Location location = new Location("mock"); location.setLatitude(latitude); location.setLongitude(longitude); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java deleted file mode 100644 index 557d3ff71e..0000000000 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import static org.mockito.Mockito.when; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.sensors.driver.BarometerInternal; - -/** - * NOTE: Test data is completely artificial. - */ -@RunWith(MockitoJUnitRunner.class) -public class AltitudeSumManagerTest { - - private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) { - for (float f : values) { - altitudeSumManager.onSensorValueChanged(AtmosphericPressure.ofHPA(f)); - } - } - - @Mock - BarometerInternal driver; - - @Test - public void getAltitudeGainLoss_downhill() { - // given - AltitudeSumManager subject = new AltitudeSumManager(driver); - - when(driver.isConnected()).thenReturn(true); - - // then - addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f}); - - // then - Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01); - Assert.assertEquals(15f, subject.getAltitudeLoss_m(), 0.01); - } - - @Test - public void sensorUnavailable() { - // given - AltitudeSumManager subject = new AltitudeSumManager(driver); - - when(driver.isConnected()).thenReturn(true); - - // then - subject.onSensorValueChanged(AtmosphericPressure.ofHPA(999f)); - - // then - Assert.assertNull(subject.getAltitudeGain_m()); - Assert.assertNull(subject.getAltitudeLoss_m()); - } -} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java new file mode 100644 index 0000000000..5ef2b51c62 --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java @@ -0,0 +1,28 @@ +package de.dennisguse.opentracks.sensors.sensorData; + +import org.junit.Assert; +import org.junit.Test; + +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))); + } + } + + @Test + public void getAltitudeGainLoss_downhill() { + // given + AggregatorBarometer subject = new AggregatorBarometer(""); + + // then + addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f}); + + // then + Assert.assertEquals(0f, subject.value.gain_m(), 0.01); + Assert.assertEquals(15f, subject.value.loss_m(), 0.01); + } +} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java index 6a0b045a71..82c75ce8e6 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.os.Looper; -import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; @@ -19,6 +18,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import java.time.Instant; import java.util.List; @@ -28,15 +28,16 @@ import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.content.data.TestDataUtil; import de.dennisguse.opentracks.data.ContentProviderUtils; +import de.dennisguse.opentracks.data.models.AltitudeGainLoss; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.io.file.importer.TrackPointAssert; -import de.dennisguse.opentracks.sensors.AltitudeSumManager; import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence; import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; import de.dennisguse.opentracks.sensors.sensorData.AggregatorRunning; import de.dennisguse.opentracks.sensors.sensorData.Raw; @@ -46,8 +47,6 @@ /** * Tests insert location. - * Note: on API23, the TrackRecordingService may not be stopped properly before the next test. - * So, if something fails, subsequent tests may be affected. */ @RunWith(AndroidJUnit4.class) public class TrackRecordingServiceRecordingTest { @@ -60,14 +59,6 @@ public class TrackRecordingServiceRecordingTest { private TrackRecordingService service; - private final AltitudeSumManager altitudeSumManager = new AltitudeSumManager() { - @Override - public void fill(@NonNull TrackPoint trackPoint) { - trackPoint.setAltitudeGain(0f); - trackPoint.setAltitudeLoss(0f); - } - }; - @BeforeClass public static void preSetUp() { // Prepare looper for Android's message queue @@ -107,7 +98,8 @@ public void recording_startStop() { String startTime = "2020-02-02T02:02:02Z"; trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // then assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null) @@ -144,7 +136,8 @@ public void testRecording_startPauseResume() { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // when String pauseTime = "2020-02-02T02:02:03Z"; @@ -190,7 +183,8 @@ public void testRecording_startPauseStop() { String starTime = "2020-02-02T02:02:02Z"; trackPointCreator.setClock(starTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + String pauseTime = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(pauseTime); @@ -222,7 +216,8 @@ public void testRecording_startStopResumeStop() { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + String stopTime = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(stopTime); @@ -232,7 +227,8 @@ public void testRecording_startStopResumeStop() { String resumeTime = "2020-02-02T02:02:04Z"; trackPointCreator.setClock(resumeTime); service.resumeTrack(trackId); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // then new TrackPointAssert().assertEquals(List.of( @@ -253,7 +249,8 @@ public void testRecording_blesensor_only_no_distance() { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + SensorManager sensorManager = trackPointCreator.getSensorManager(); sensorManager.sensorDataSet.add(new AggregatorHeartRate("", "")); // when @@ -284,7 +281,6 @@ public void testRecording_blesensor_only_no_distance() { ), TestDataUtil.getTrackPoints(contentProviderUtils, trackId)); } - @MediumTest @Test public void testRecording_gpsOnly_recordingDistance_above() { @@ -293,7 +289,7 @@ public void testRecording_gpsOnly_recordingDistance_above() { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); // when @@ -367,7 +363,8 @@ public void testRecording_gpsOnly_recordingDistance_below() { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // when String gps1 = "2020-02-02T02:02:03Z"; @@ -431,7 +428,8 @@ public void testRecording_gpsOnly_recordingDistance_movement_non_idle() { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // when String gps1 = "2020-02-02T02:02:03Z"; @@ -481,7 +479,8 @@ public void testRecording_gpsOnly_ignore_inaccurate() { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // when String gps1 = "2020-02-02T02:02:03Z"; @@ -527,7 +526,7 @@ public void testRecording_gpsOnly_segment() { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); // when String gps1 = "2020-02-02T02:02:03Z"; @@ -583,16 +582,15 @@ public void testRecording_gpsOnly_segment() { @Test public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() { // TODO Check TrackStatistics - AltitudeSumManager altitudeSumManager = new AltitudeSumManager(); - // given String startTime = "2020-02-02T02:02:02Z"; TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + SensorManager sensorManager = trackPointCreator.getSensorManager(); sensorManager.sensorDataSet.add(new AggregatorRunning("", "")); + sensorManager.sensorDataSet.barometer = null; // when String sensor1 = "2020-02-02T02:02:03Z"; @@ -674,4 +672,12 @@ public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() { .setSensorDistance(Distance.of(0)) ), TestDataUtil.getTrackPoints(contentProviderUtils, trackId)); } + + 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)); + + trackPointCreator.getSensorManager().sensorDataSet.barometer = barometer; + } } diff --git a/src/androidTest/res/raw/csv_export.csv b/src/androidTest/res/raw/csv_export.csv index cdefb81a11..f4344cd208 100644 --- a/src/androidTest/res/raw/csv_export.csv +++ b/src/androidTest/res/raw/csv_export.csv @@ -8,6 +8,6 @@ "2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,, "2020-02-02T03:03:21+01:00","TRACKPOINT",3,14.002,10,10,,54,0,0,,,, "2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3,16,10,10,,54,0,0,,,, -"2020-02-02T03:03:30+01:00","IDLE",,,,,,,,,,,, +"2020-02-02T03:03:30+01:00","IDLE",,,,,,,0,0,,,, "2020-02-02T03:03:50+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,, "2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,, \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java b/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java new file mode 100644 index 0000000000..a330473937 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java @@ -0,0 +1,3 @@ +package de.dennisguse.opentracks.data.models; + +public record AltitudeGainLoss(float gain_m, float loss_m) {} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java deleted file mode 100644 index 63587e0331..0000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java +++ /dev/null @@ -1,108 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import android.content.Context; -import android.os.Handler; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.data.models.TrackPoint; -import de.dennisguse.opentracks.sensors.driver.BarometerInternal; - -/** - * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer). - */ -public class AltitudeSumManager implements SensorConnector { - - private static final String TAG = AltitudeSumManager.class.getSimpleName(); - - private final BarometerInternal driver; - - private AtmosphericPressure lastAcceptedSensorValue; - - private AtmosphericPressure lastSeenSensorValue; - - private Float altitudeGain_m; - private Float altitudeLoss_m; - - public AltitudeSumManager() { - driver = new BarometerInternal(); - } - - @VisibleForTesting - public AltitudeSumManager(BarometerInternal mock) { - this.driver = mock; - } - - public void start(Context context, Handler handler) { - driver.connect(context, handler, this); - - lastAcceptedSensorValue = null; - reset(); - } - - public void stop(Context context) { - Log.d(TAG, "Stop"); - - driver.disconnect(context); - reset(); - } - - public void fill(@NonNull TrackPoint trackPoint) { - trackPoint.setAltitudeGain(altitudeGain_m); - trackPoint.setAltitudeLoss(altitudeLoss_m); - } - - @Nullable - public Float getAltitudeGain_m() { - return driver.isConnected() ? altitudeGain_m : null; - } - - @VisibleForTesting - public void setAltitudeGain_m(float altitudeGain_m) { - this.altitudeGain_m = altitudeGain_m; - } - - @Nullable - public Float getAltitudeLoss_m() { - return driver.isConnected() ? altitudeLoss_m : null; - } - - @VisibleForTesting - public void setAltitudeLoss_m(float altitudeLoss_m) { - this.altitudeLoss_m = altitudeLoss_m; - } - - public void reset() { - Log.d(TAG, "Reset"); - altitudeGain_m = null; - altitudeLoss_m = null; - } - - public void onSensorValueChanged(AtmosphericPressure currentSensorValue) { - if (lastAcceptedSensorValue == null) { - lastAcceptedSensorValue = currentSensorValue; - lastSeenSensorValue = currentSensorValue; - return; - } - - altitudeGain_m = altitudeGain_m != null ? altitudeGain_m : 0; - altitudeLoss_m = altitudeLoss_m != null ? altitudeLoss_m : 0; - - PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, lastSeenSensorValue, currentSensorValue); - if (altitudeChange != null) { - altitudeGain_m += altitudeChange.getAltitudeGain_m(); - - altitudeLoss_m += altitudeChange.getAltitudeLoss_m(); - - lastAcceptedSensorValue = altitudeChange.currentSensorValue(); - } - - lastSeenSensorValue = currentSensorValue; - - Log.v(TAG, "altitude gain: " + altitudeGain_m + ", altitude loss: " + altitudeLoss_m); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java new file mode 100644 index 0000000000..40e1749f93 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java @@ -0,0 +1,46 @@ +package de.dennisguse.opentracks.sensors; + +import android.content.Context; +import android.os.Handler; +import android.util.Log; + +import de.dennisguse.opentracks.data.models.AtmosphericPressure; +import de.dennisguse.opentracks.sensors.driver.BarometerInternal; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; +import de.dennisguse.opentracks.sensors.sensorData.Raw; + +/** + * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer). + */ +public class GainManager implements SensorConnector { + + private static final String TAG = GainManager.class.getSimpleName(); + + private final BarometerInternal driver; + + private final SensorManager.SensorDataChangedObserver listener; + + public GainManager(SensorManager.SensorDataChangedObserver listener) { + this.listener = listener; + driver = new BarometerInternal(); + } + + public void start(Context context, Handler handler) { + driver.connect(context, handler, this); + + if (driver.isConnected()) { + listener.onConnect(new AggregatorBarometer("internal")); + } + } + + public void stop(Context context) { + Log.d(TAG, "Stop"); + + driver.disconnect(context); + listener.onDisconnect(new AggregatorBarometer("internal")); + } + + public void onSensorValueChanged(AtmosphericPressure currentSensorValue) { + listener.onChange(new Raw<>(currentSensorValue)); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index 1f1535a6fa..385d69ca78 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -48,7 +48,7 @@ public void onDisconnect(Aggregator sensorData) { private BluetoothRemoteSensorManager bluetoothSensorManager; - private AltitudeSumManager altitudeSumManager; + private GainManager altitudeSumManager; private GPSManager gpsManager; @@ -58,7 +58,7 @@ public SensorManager(TrackPointCreator observer) { public void start(Context context, Handler handler) { gpsManager = new GPSManager(observer); //TODO Pass listener - altitudeSumManager = new AltitudeSumManager(); + altitudeSumManager = new GainManager(listener); bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener); onSharedPreferenceChanged(null, null); @@ -82,7 +82,6 @@ public void stop(Context context) { } public SensorDataSet fill(TrackPoint trackPoint) { - altitudeSumManager.fill(trackPoint); sensorDataSet.fillTrackPoint(trackPoint); return new SensorDataSet(sensorDataSet); } @@ -93,7 +92,6 @@ public void reset() { return; } sensorDataSet.reset(); - altitudeSumManager.reset(); } @Deprecated @@ -108,13 +106,13 @@ public GPSManager getGpsManager() { @Deprecated @VisibleForTesting - public AltitudeSumManager getAltitudeSumManager() { + public GainManager getAltitudeSumManager() { return altitudeSumManager; } @Deprecated @VisibleForTesting - public void setAltitudeSumManager(AltitudeSumManager altitudeSumManager) { + public void setAltitudeSumManager(GainManager altitudeSumManager) { this.altitudeSumManager = altitudeSumManager; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java index 8a405f7200..913be02235 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java @@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit; import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.sensors.AltitudeSumManager; +import de.dennisguse.opentracks.sensors.GainManager; public class BarometerInternal { @@ -19,7 +19,7 @@ public class BarometerInternal { private static final int SAMPLING_PERIOD = (int) TimeUnit.SECONDS.toMicros(5); - private AltitudeSumManager observer; + private GainManager observer; private final SensorEventListener listener = new SensorEventListener() { @Override @@ -38,14 +38,13 @@ public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; - public void connect(Context context, Handler handler, AltitudeSumManager observer) { + public void connect(Context context, Handler handler, GainManager observer) { this.observer = observer; SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); if (pressureSensor == null) { Log.w(TAG, "No pressure sensor available."); this.observer = null; - return; } if (sensorManager.registerListener(listener, pressureSensor, SAMPLING_PERIOD, handler)) { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java new file mode 100644 index 0000000000..7e5ed7c575 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java @@ -0,0 +1,46 @@ +package de.dennisguse.opentracks.sensors.sensorData; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.data.models.Altitude; +import de.dennisguse.opentracks.data.models.AltitudeGainLoss; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; +import de.dennisguse.opentracks.sensors.PressureSensorUtils; + +public class AggregatorBarometer extends Aggregator { + + private AtmosphericPressure lastAcceptedSensorValue; + + public AggregatorBarometer(String sensorAddress) { + super(sensorAddress); + } + + @Override + protected void computeValue(Raw current) { + if (previous == null) { + lastAcceptedSensorValue = current.value(); + value = getNoneValue(); + return; + } + + PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, previous.value(), current.value()); + if (altitudeChange != null) { + value = new AltitudeGainLoss(value.gain_m() + altitudeChange.getAltitudeGain_m(), value.loss_m() + altitudeChange.getAltitudeLoss_m()); + + lastAcceptedSensorValue = altitudeChange.currentSensorValue(); + } + } + + @NonNull + @Override + protected AltitudeGainLoss getNoneValue() { + return new AltitudeGainLoss(0f, 0f); + } + + @Override + public void reset() { + value = getNoneValue(); + } + + public record Data(Altitude gain, Altitude loss) {} +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java index 50ed66799a..b6dab49319 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java @@ -6,6 +6,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.Power; @@ -36,6 +37,9 @@ public final class SensorDataSet { @VisibleForTesting public AggregatorRunning runningDistanceSpeedCadence; + @VisibleForTesting + public AggregatorBarometer barometer; + public SensorDataSet() { } @@ -45,6 +49,7 @@ public SensorDataSet(SensorDataSet toCopy) { this.cyclingDistanceSpeed = toCopy.cyclingDistanceSpeed; this.cyclingPower = toCopy.cyclingPower; this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence; + this.barometer = toCopy.barometer; } public Pair getHeartRate() { @@ -113,8 +118,12 @@ public void update(@NonNull Raw data) { this.cyclingPower.add((Raw) data); return; } + if (value instanceof AtmosphericPressure) { + this.barometer.add((Raw) data); + return; + } - throw new UnsupportedOperationException(data.getClass().getCanonicalName()); + throw new UnsupportedOperationException(data.getClass().getCanonicalName() + " " + data.value().getClass().getCanonicalName()); } public void remove(@NonNull Aggregator type) { @@ -127,6 +136,7 @@ public void clear() { this.cyclingDistanceSpeed = null; this.cyclingPower = null; this.runningDistanceSpeedCadence = null; + this.barometer = null; } public void fillTrackPoint(TrackPoint trackPoint) { @@ -153,6 +163,11 @@ public void fillTrackPoint(TrackPoint trackPoint) { if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) { trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue().distance()); } + + if (barometer != null && barometer.hasValue()) { + trackPoint.setAltitudeGain(barometer.getValue().gain_m()); + trackPoint.setAltitudeLoss(barometer.getValue().loss_m()); + } } public void reset() { @@ -161,6 +176,7 @@ public void reset() { if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.reset(); if (cyclingPower != null) cyclingPower.reset(); if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.reset(); + if (barometer != null) barometer.reset(); } private void set(@NonNull Aggregator type, @Nullable Aggregator sensorData) { @@ -184,6 +200,10 @@ private void set(@NonNull Aggregator type, @Nullable Aggregator sens runningDistanceSpeedCadence = (AggregatorRunning) sensorData; return; } + if (type instanceof AggregatorBarometer) { + barometer = (AggregatorBarometer) sensorData; + return; + } throw new UnsupportedOperationException(type.getClass().getCanonicalName()); } diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java index 49384b2919..35826be7ce 100644 --- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java +++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java @@ -34,7 +34,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh @NonNull private Clock clock = new MonotonicClock(); - private SensorManager sensorManager; + private final SensorManager sensorManager; public TrackPointCreator(Callback service) { this.service = service; @@ -139,11 +139,6 @@ public SensorManager getSensorManager() { return sensorManager; } - @VisibleForTesting - public void setSensorManager(SensorManager sensorManager) { - this.sensorManager = sensorManager; - } - @VisibleForTesting public void setClock(@NonNull String time) { this.clock = Clock.fixed(Instant.parse(time), ZoneId.of("CET"));