Introduced value objects for heartrate, cadence, and power.

Fixes #896.
This commit is contained in:
Dennis Guse
2022-01-16 15:11:35 +01:00
parent 1df1fe699a
commit 0eda1eed1d
30 changed files with 397 additions and 212 deletions
@@ -77,8 +77,8 @@ public class ChartPointTest {
public void create_sensorAvailable() { public void create_sensorAvailable() {
// given // given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setHeartRate_bpm(100f); trackPoint.setHeartRate(100f);
trackPoint.setCadence_rpm(101f); trackPoint.setCadence(101f);
trackPoint.setPower(102f); trackPoint.setPower(102f);
TrackStatistics statistics = new TrackStatistics(); TrackStatistics statistics = new TrackStatistics();
@@ -124,8 +124,8 @@ public class TestDataUtil {
trackPoint.setAltitude(i * ALTITUDE_INTERVAL); trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
trackPoint.setSpeed(Speed.of(5f + (i / 10f))); trackPoint.setSpeed(Speed.of(5f + (i / 10f)));
trackPoint.setHeartRate_bpm(100f + i); trackPoint.setHeartRate(100f + i);
trackPoint.setCadence_rpm(300f + i); trackPoint.setCadence(300f + i);
trackPoint.setPower(400f + i); trackPoint.setPower(400f + i);
trackPoint.setAltitudeGain(ALTITUDE_GAIN); trackPoint.setAltitudeGain(ALTITUDE_GAIN);
trackPoint.setAltitudeLoss(ALTITUDE_LOSS); trackPoint.setAltitudeLoss(ALTITUDE_LOSS);
@@ -20,9 +20,15 @@ public class TestSensorDataUtil {
int i = trackPointList.size() + 1; int i = trackPointList.size() + 1;
tp.setLatitude(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0); tp.setLatitude(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0);
tp.setLongitude(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0); tp.setLongitude(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0);
tp.setHeartRate_bpm(hr); if (hr != null) {
tp.setCadence_rpm(cadence); tp.setHeartRate(hr);
tp.setPower(power); }
if (cadence != null) {
tp.setCadence(cadence);
}
if (power != null) {
tp.setPower(power);
}
tp.setHorizontalAccuracy(Distance.of(1f)); tp.setHorizontalAccuracy(Distance.of(1f));
tp.setAltitude(1f); tp.setAltitude(1f);
tp.setSpeed(Speed.of(5f + (i / 10f))); tp.setSpeed(Speed.of(5f + (i / 10f)));
@@ -52,6 +52,7 @@ import java.util.stream.Collectors;
import de.dennisguse.opentracks.content.data.TestDataUtil; import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.TestSensorDataUtil; import de.dennisguse.opentracks.content.data.TestSensorDataUtil;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
@@ -776,6 +777,7 @@ public class CustomContentProviderUtilsTest {
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE)).thenReturn(6); when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE)).thenReturn(6);
when(cursorMock.isNull(6)).thenReturn(false); when(cursorMock.isNull(6)).thenReturn(false);
when(cursorMock.getFloat(6)).thenReturn(75f);
// when // when
TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock); TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock);
@@ -785,7 +787,7 @@ public class CustomContentProviderUtilsTest {
assertEquals(latitude, trackPoint.getLatitude(), 0.01); assertEquals(latitude, trackPoint.getLatitude(), 0.01);
assertEquals(time, trackPoint.getTime().toEpochMilli()); assertEquals(time, trackPoint.getTime().toEpochMilli());
assertEquals(speed, trackPoint.getSpeed().toMPS(), 0.01); assertEquals(speed, trackPoint.getSpeed().toMPS(), 0.01);
assertFalse(trackPoint.hasHeartRate()); assertEquals(HeartRate.of(75f), trackPoint.getHeartRate());
} }
/** /**
@@ -809,8 +811,8 @@ public class CustomContentProviderUtilsTest {
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5); TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
trackPoint.setHeartRate_bpm(1F); trackPoint.setHeartRate(1F);
trackPoint.setCadence_rpm(2F); trackPoint.setCadence(2F);
trackPoint.setPower(3F); trackPoint.setPower(3F);
// when // when
@@ -819,9 +821,9 @@ public class CustomContentProviderUtilsTest {
// then // then
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId); List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
assertTrue(trackPoints.get(10).hasHeartRate()); assertTrue(trackPoints.get(10).hasHeartRate());
assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01); assertEquals(trackPoint.getHeartRate(), trackPoints.get(10).getHeartRate());
assertEquals(trackPoint.getCadence_rpm(), trackPoints.get(10).getCadence_rpm(), 0.01); assertEquals(trackPoint.getCadence(), trackPoints.get(10).getCadence());
assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower(), 0.01); assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower());
} }
/** /**
@@ -909,8 +911,8 @@ public class CustomContentProviderUtilsTest {
TrackPoint trackPoint = TestDataUtil.createTrackPoint(1); TrackPoint trackPoint = TestDataUtil.createTrackPoint(1);
trackPoint.setType(TrackPoint.Type.TRACKPOINT); trackPoint.setType(TrackPoint.Type.TRACKPOINT);
trackPoint.setPower(null); trackPoint.setPower(null);
trackPoint.setCadence_rpm(null); trackPoint.setCadence(null);
trackPoint.setHeartRate_bpm(null); trackPoint.setHeartRate(null);
trackPointList.add(trackPoint); trackPointList.add(trackPoint);
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
Track track = TestDataUtil.createTrack(trackId); Track track = TestDataUtil.createTrack(trackId);
@@ -972,13 +974,13 @@ public class CustomContentProviderUtilsTest {
// then // then
assertTrue(sensorStatistics.hasHeartRate()); assertTrue(sensorStatistics.hasHeartRate());
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f); assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f); assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
assertTrue(sensorStatistics.hasCadence()); assertTrue(sensorStatistics.hasCadence());
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f); assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f); assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
assertTrue(sensorStatistics.hasPower()); assertTrue(sensorStatistics.hasPower());
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f); assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
} }
@Test @Test
@@ -1004,8 +1006,8 @@ public class CustomContentProviderUtilsTest {
// then // then
assertTrue(sensorStatistics.hasHeartRate()); assertTrue(sensorStatistics.hasHeartRate());
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f); assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f); assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
assertFalse(sensorStatistics.hasCadence()); assertFalse(sensorStatistics.hasCadence());
assertFalse(sensorStatistics.hasPower()); assertFalse(sensorStatistics.hasPower());
} }
@@ -1034,8 +1036,8 @@ public class CustomContentProviderUtilsTest {
// then // then
assertFalse(sensorStatistics.hasHeartRate()); assertFalse(sensorStatistics.hasHeartRate());
assertTrue(sensorStatistics.hasCadence()); assertTrue(sensorStatistics.hasCadence());
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f); assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f); assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
assertFalse(sensorStatistics.hasPower()); assertFalse(sensorStatistics.hasPower());
} }
@@ -1064,7 +1066,7 @@ public class CustomContentProviderUtilsTest {
assertFalse(sensorStatistics.hasHeartRate()); assertFalse(sensorStatistics.hasHeartRate());
assertFalse(sensorStatistics.hasCadence()); assertFalse(sensorStatistics.hasCadence());
assertTrue(sensorStatistics.hasPower()); assertTrue(sensorStatistics.hasPower());
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f); assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
} }
@Test @Test
@@ -1097,11 +1099,11 @@ public class CustomContentProviderUtilsTest {
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats(); TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
// then // then
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f); assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f); assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f); assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f); assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f); assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
} }
@Test @Test
@@ -1142,11 +1144,11 @@ public class CustomContentProviderUtilsTest {
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats(); TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
// then // then
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f); assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f); assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f); assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f); assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f); assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
} }
@Test @Test
@@ -1187,11 +1189,11 @@ public class CustomContentProviderUtilsTest {
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats(); TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
// then // then
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f); assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f); assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f); assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f); assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f); assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
} }
private void testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) { private void testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) {
@@ -1218,11 +1220,11 @@ public class CustomContentProviderUtilsTest {
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats(); TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
// then // then
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0.01f); assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0.01f);
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0.01f); assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0.01f);
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0.01f); assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0.01f);
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0.01f); assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0.01f);
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0.01f); assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0.01f);
} }
@Test @Test
@@ -40,8 +40,11 @@ import java.util.concurrent.TimeoutException;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.TestDataUtil; import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -336,9 +339,9 @@ public class ExportImportTest {
.setAltitudeLoss(1f) .setAltitudeLoss(1f)
.setAltitudeGain(1f) .setAltitudeGain(1f)
.setSensorDistance(Distance.of(12)) .setSensorDistance(Distance.of(12))
.setHeartRate_bpm(66f) .setHeartRate(66f)
.setPower(50f) .setPower(50f)
.setCadence_rpm(3f), .setCadence(3f),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:21Z")) new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:02:21Z"))
.setLatitude(3) .setLatitude(3)
.setLongitude(14.002) .setLongitude(14.002)
@@ -439,18 +442,18 @@ public class ExportImportTest {
} }
} }
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, Float heartRate, Float cyclingCadence, Float power) { private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator) { trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, trackPointCreator) {
@Override @Override
public SensorDataSet fill(@NonNull TrackPoint trackPoint) { public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
SensorDataSet sensorDataSet = new SensorDataSet(); SensorDataSet sensorDataSet = new SensorDataSet();
sensorDataSet.set(new SensorDataCyclingPower("power", "power", power)); sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", heartRate)); sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
SensorDataCycling.Cadence cadence = Mockito.mock(SensorDataCycling.Cadence.class); SensorDataCycling.CyclingCadence cyclingCadence = Mockito.mock(SensorDataCycling.CyclingCadence.class);
Mockito.when(cadence.hasValue()).thenReturn(true); Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
Mockito.when(cadence.getValue()).thenReturn(cyclingCadence); Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
sensorDataSet.set(cadence); sensorDataSet.set(cyclingCadence);
if (distance != null && speed != null) { if (distance != null && speed != null) {
SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class); SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class);
@@ -61,17 +61,17 @@ public class TrackPointAssert {
Assert.assertEquals(expected.hasHeartRate(), actual.hasHeartRate()); Assert.assertEquals(expected.hasHeartRate(), actual.hasHeartRate());
if (expected.hasHeartRate()) { if (expected.hasHeartRate()) {
Assert.assertEquals(expected.getHeartRate_bpm(), actual.getHeartRate_bpm(), delta); Assert.assertEquals(expected.getHeartRate(), actual.getHeartRate());
} }
Assert.assertEquals(expected.hasPower(), actual.hasPower()); Assert.assertEquals(expected.hasPower(), actual.hasPower());
if (expected.hasPower()) { if (expected.hasPower()) {
Assert.assertEquals(expected.getPower(), actual.getPower(), delta); Assert.assertEquals(expected.getPower(), actual.getPower());
} }
Assert.assertEquals(expected.hasCadence(), actual.hasCadence()); Assert.assertEquals(expected.hasCadence(), actual.hasCadence());
if (expected.hasCadence()) { if (expected.hasCadence()) {
Assert.assertEquals(expected.getCadence_rpm(), actual.getCadence_rpm(), delta); Assert.assertEquals(expected.getCadence(), actual.getCadence());
} }
} }
@@ -7,6 +7,7 @@ import android.bluetooth.BluetoothGattCharacteristic;
import org.junit.Test; import org.junit.Test;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
@@ -101,7 +102,7 @@ public class BluetoothUtilsTest {
// then // then
assertEquals(Speed.of(5), sensor.getSpeed()); assertEquals(Speed.of(5), sensor.getSpeed());
assertEquals(80, sensor.getCadence(), 0.01); assertEquals(Cadence.of(80), sensor.getCadence());
assertEquals(Distance.of(6553.5 + 1677721.6), sensor.getTotalDistance()); assertEquals(Distance.of(6553.5 + 1677721.6), sensor.getTotalDistance());
} }
} }
@@ -11,6 +11,7 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.sensors.UintUtils; import de.dennisguse.opentracks.sensors.UintUtils;
@@ -20,48 +21,48 @@ public class SensorDataCyclingTest {
@Test @Test
public void compute_cadence_1() { public void compute_cadence_1() {
// given // given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 1024); // 1s SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 2048); // 2s SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s
// when // when
current.compute(previous); current.compute(previous);
// then // then
assertEquals(60, current.getValue(), 0.01); assertEquals(60, current.getValue().getRPM(), 0.01);
} }
@Test @Test
public void compute_cadence_2() { public void compute_cadence_2() {
// given // given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 6184); SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 6184);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 8016); SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 8016);
// when // when
current.compute(previous); current.compute(previous);
// then // then
assertEquals(33.53, current.getValue(), 0.01); assertEquals(33.53, current.getValue().getRPM(), 0.01);
} }
@Test @Test
public void compute_cadence_sameCount() { public void compute_cadence_sameCount() {
// given // given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 1024); SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 2048); SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 2048);
// when // when
current.compute(previous); current.compute(previous);
// then // then
assertEquals(0, current.getValue(), 0.01); assertEquals(Cadence.of(0), current.getValue());
} }
@Test @Test
public void compute_cadence_sameTime() { public void compute_cadence_sameTime() {
// given // given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 1024); SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 1024); SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 1024);
// when // when
current.compute(previous); current.compute(previous);
@@ -73,14 +74,14 @@ public class SensorDataCyclingTest {
@Test @Test
public void compute_cadence_rollOverTime() { public void compute_cadence_rollOverTime() {
// given // given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024); SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 0); SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 0);
// when // when
current.compute(previous); current.compute(previous);
// then // then
assertEquals(60, current.getValue(), 0.01); assertEquals(60, current.getValue().getRPM(), 0.01);
} }
@Ignore("Disabled from #953") @Ignore("Disabled from #953")
@@ -88,21 +89,21 @@ public class SensorDataCyclingTest {
@Deprecated @Deprecated
public void compute_cadence_rollOverCount() { public void compute_cadence_rollOverCount() {
// given // given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 0, 2048); SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
// when // when
current.compute(previous); current.compute(previous);
// then // then
assertEquals(60, current.getValue(), 0.01); assertEquals(60, current.getValue().getRPM(), 0.01);
} }
@Test @Test
public void compute_cadence_overflow() { public void compute_cadence_overflow() {
// given // given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 0, 2048); SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
// when // when
current.compute(previous); current.compute(previous);
@@ -171,8 +172,8 @@ public class SensorDataCyclingTest {
@Test @Test
public void equals_cadence_with_no_data() { public void equals_cadence_with_no_data() {
// given // given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress"); SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress");
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 0, 2048); SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048);
// when // when
previous.toString(); previous.toString();
@@ -17,7 +17,10 @@ import java.time.Duration;
import java.util.List; import java.util.List;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
@@ -124,8 +127,8 @@ public class StatisticDataBuilderTest extends TestCase {
@Test @Test
public void testFromSensorStatistics_onlyHeartRate() { public void testFromSensorStatistics_onlyHeartRate() {
when(sensorStatisticsMock.hasHeartRate()).thenReturn(true); when(sensorStatisticsMock.hasHeartRate()).thenReturn(true);
when(sensorStatisticsMock.getMaxHeartRate()).thenReturn(200f); when(sensorStatisticsMock.getMaxHeartRate()).thenReturn(HeartRate.of(200f));
when(sensorStatisticsMock.getAvgHeartRate()).thenReturn(150f); when(sensorStatisticsMock.getAvgHeartRate()).thenReturn(HeartRate.of(150f));
when(sensorStatisticsMock.hasCadence()).thenReturn(false); when(sensorStatisticsMock.hasCadence()).thenReturn(false);
when(sensorStatisticsMock.hasPower()).thenReturn(false); when(sensorStatisticsMock.hasPower()).thenReturn(false);
@@ -142,8 +145,8 @@ public class StatisticDataBuilderTest extends TestCase {
public void testFromSensorStatistics_onlyCadence() { public void testFromSensorStatistics_onlyCadence() {
when(sensorStatisticsMock.hasHeartRate()).thenReturn(false); when(sensorStatisticsMock.hasHeartRate()).thenReturn(false);
when(sensorStatisticsMock.hasCadence()).thenReturn(true); when(sensorStatisticsMock.hasCadence()).thenReturn(true);
when(sensorStatisticsMock.getAvgCadence()).thenReturn(90f); when(sensorStatisticsMock.getAvgCadence()).thenReturn(Cadence.of(90f));
when(sensorStatisticsMock.getMaxCadence()).thenReturn(110f); when(sensorStatisticsMock.getMaxCadence()).thenReturn(Cadence.of(110f));
when(sensorStatisticsMock.hasPower()).thenReturn(false); when(sensorStatisticsMock.hasPower()).thenReturn(false);
// when // when
@@ -160,7 +163,7 @@ public class StatisticDataBuilderTest extends TestCase {
when(sensorStatisticsMock.hasHeartRate()).thenReturn(false); when(sensorStatisticsMock.hasHeartRate()).thenReturn(false);
when(sensorStatisticsMock.hasCadence()).thenReturn(false); when(sensorStatisticsMock.hasCadence()).thenReturn(false);
when(sensorStatisticsMock.hasPower()).thenReturn(true); when(sensorStatisticsMock.hasPower()).thenReturn(true);
when(sensorStatisticsMock.getAvgPower()).thenReturn(300f); when(sensorStatisticsMock.getAvgPower()).thenReturn(Power.of(300f));
// when // when
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock); List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
@@ -173,13 +176,13 @@ public class StatisticDataBuilderTest extends TestCase {
@Test @Test
public void testFromSensorStatistics() { public void testFromSensorStatistics() {
when(sensorStatisticsMock.hasHeartRate()).thenReturn(true); when(sensorStatisticsMock.hasHeartRate()).thenReturn(true);
when(sensorStatisticsMock.getMaxHeartRate()).thenReturn(200f); when(sensorStatisticsMock.getMaxHeartRate()).thenReturn(HeartRate.of(200f));
when(sensorStatisticsMock.getAvgHeartRate()).thenReturn(150f); when(sensorStatisticsMock.getAvgHeartRate()).thenReturn(HeartRate.of(150f));
when(sensorStatisticsMock.hasCadence()).thenReturn(true); when(sensorStatisticsMock.hasCadence()).thenReturn(true);
when(sensorStatisticsMock.getAvgCadence()).thenReturn(90f); when(sensorStatisticsMock.getAvgCadence()).thenReturn(Cadence.of(90f));
when(sensorStatisticsMock.getMaxCadence()).thenReturn(110f); when(sensorStatisticsMock.getMaxCadence()).thenReturn(Cadence.of(110f));
when(sensorStatisticsMock.hasPower()).thenReturn(true); when(sensorStatisticsMock.hasPower()).thenReturn(true);
when(sensorStatisticsMock.getAvgPower()).thenReturn(300f); when(sensorStatisticsMock.getAvgPower()).thenReturn(Power.of(300f));
// when // when
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock); List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
@@ -44,13 +44,13 @@ public class ChartPoint {
pace = smoothedSpeed.toPace(metricUnits).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN; pace = smoothedSpeed.toPace(metricUnits).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN;
} }
if (trackPoint.hasHeartRate()) { if (trackPoint.hasHeartRate()) {
heartRate = (double) trackPoint.getHeartRate_bpm(); heartRate = (double) trackPoint.getHeartRate().getBPM();
} }
if (trackPoint.hasCadence()) { if (trackPoint.hasCadence()) {
cadence = (double) trackPoint.getCadence_rpm(); cadence = (double) trackPoint.getCadence().getRPM();
} }
if (trackPoint.hasPower()) { if (trackPoint.hasPower()) {
power = (double) trackPoint.getPower(); power = (double) trackPoint.getPower().getW();
} }
} }
@@ -41,8 +41,11 @@ import java.util.UUID;
import de.dennisguse.opentracks.BuildConfig; import de.dennisguse.opentracks.BuildConfig;
import de.dennisguse.opentracks.data.models.Altitude; import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -577,10 +580,10 @@ public class ContentProviderUtils {
} }
if (!cursor.isNull(indexes.sensorHeartRateIndex)) { if (!cursor.isNull(indexes.sensorHeartRateIndex)) {
trackPoint.setHeartRate_bpm(cursor.getFloat(indexes.sensorHeartRateIndex)); trackPoint.setHeartRate(cursor.getFloat(indexes.sensorHeartRateIndex));
} }
if (!cursor.isNull(indexes.sensorCadenceIndex)) { if (!cursor.isNull(indexes.sensorCadenceIndex)) {
trackPoint.setCadence_rpm(cursor.getFloat(indexes.sensorCadenceIndex)); trackPoint.setCadence(cursor.getFloat(indexes.sensorCadenceIndex));
} }
if (!cursor.isNull(indexes.sensorDistanceIndex)) { if (!cursor.isNull(indexes.sensorDistanceIndex)) {
trackPoint.setSensorDistance(Distance.of(cursor.getFloat(indexes.sensorDistanceIndex))); trackPoint.setSensorDistance(Distance.of(cursor.getFloat(indexes.sensorDistanceIndex)));
@@ -739,16 +742,16 @@ public class ContentProviderUtils {
} }
if (trackPoint.hasHeartRate()) { if (trackPoint.hasHeartRate()) {
values.put(TrackPointsColumns.SENSOR_HEARTRATE, trackPoint.getHeartRate_bpm()); values.put(TrackPointsColumns.SENSOR_HEARTRATE, trackPoint.getHeartRate().getBPM());
} }
if (trackPoint.hasCadence()) { if (trackPoint.hasCadence()) {
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCadence_rpm()); values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCadence().getRPM());
} }
if (trackPoint.hasSensorDistance()) { if (trackPoint.hasSensorDistance()) {
values.put(TrackPointsColumns.SENSOR_DISTANCE, trackPoint.getSensorDistance().toM()); values.put(TrackPointsColumns.SENSOR_DISTANCE, trackPoint.getSensorDistance().toM());
} }
if (trackPoint.hasPower()) { if (trackPoint.hasPower()) {
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower()); values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower().getW());
} }
if (trackPoint.hasAltitudeGain()) { if (trackPoint.hasAltitudeGain()) {
@@ -831,12 +834,17 @@ public class ContentProviderUtils {
SensorStatistics sensorStatistics = null; SensorStatistics sensorStatistics = null;
try (Cursor cursor = contentResolver.query(ContentUris.withAppendedId(TracksColumns.CONTENT_URI_SENSOR_STATS, trackId.getId()), null, null, null, null)) { try (Cursor cursor = contentResolver.query(ContentUris.withAppendedId(TracksColumns.CONTENT_URI_SENSOR_STATS, trackId.getId()), null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
final int MAX_HR_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_MAX_HR);
final int AVG_HR_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_HR);
final int MAX_CADENCE_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_MAX_CADENCE);
final int AVG_CADENCE_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_CADENCE);
final int AVG_POWER_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_POWER);
sensorStatistics = new SensorStatistics( sensorStatistics = new SensorStatistics(
!cursor.isNull(cursor.getColumnIndexOrThrow("max_hr")) ? cursor.getFloat(cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_MAX_HR)) : null, !cursor.isNull(MAX_HR_INDEX) ? HeartRate.of(cursor.getFloat(MAX_HR_INDEX)) : null,
!cursor.isNull(cursor.getColumnIndexOrThrow("avg_hr")) ? cursor.getFloat(cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_HR)) : null, !cursor.isNull(AVG_HR_INDEX) ? HeartRate.of(cursor.getFloat(AVG_HR_INDEX)) : null,
!cursor.isNull(cursor.getColumnIndexOrThrow("max_cadence")) ? cursor.getFloat(cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_MAX_CADENCE)) : null, !cursor.isNull(MAX_CADENCE_INDEX) ? Cadence.of(cursor.getFloat(MAX_CADENCE_INDEX)) : null,
!cursor.isNull(cursor.getColumnIndexOrThrow("avg_cadence")) ? cursor.getFloat(cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_CADENCE)) : null, !cursor.isNull(AVG_CADENCE_INDEX) ? Cadence.of(cursor.getFloat(AVG_CADENCE_INDEX)) : null,
!cursor.isNull(cursor.getColumnIndexOrThrow("avg_power")) ? cursor.getFloat(cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_POWER)) : null !cursor.isNull(AVG_POWER_INDEX) ? Power.of(cursor.getFloat(AVG_POWER_INDEX)) : null
); );
} }
@@ -0,0 +1,43 @@
package de.dennisguse.opentracks.data.models;
import androidx.annotation.NonNull;
import java.util.Objects;
public class Cadence {
public static Cadence of(float value) {
return new Cadence(value);
}
private final float value;
private Cadence(float value) {
this.value = value;
}
public float getRPM() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cadence cadence = (Cadence) o;
return Float.compare(cadence.value, value) == 0;
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@NonNull
@Override
public String toString() {
return "Cadence{" +
"value=" + value + " rpm" +
'}';
}
}
@@ -0,0 +1,43 @@
package de.dennisguse.opentracks.data.models;
import androidx.annotation.NonNull;
import java.util.Objects;
public class HeartRate {
public static HeartRate of(float value) {
return new HeartRate(value);
}
private final float value;
private HeartRate(float value) {
this.value = value;
}
public float getBPM() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HeartRate heartRate = (HeartRate) o;
return Float.compare(heartRate.value, value) == 0;
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@NonNull
@Override
public String toString() {
return "HeartRate{" +
"value=" + value + " bpm" +
'}';
}
}
@@ -0,0 +1,43 @@
package de.dennisguse.opentracks.data.models;
import androidx.annotation.NonNull;
import java.util.Objects;
public class Power {
public static Power of(float value) {
return new Power(value);
}
private final float value;
private Power(float value) {
this.value = value;
}
public float getW() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Power power = (Power) o;
return Float.compare(power.value, value) == 0;
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@NonNull
@Override
public String toString() {
return "Power{" +
"value=" + value + " W" +
'}';
}
}
@@ -91,9 +91,9 @@ public class TrackPoint {
@NonNull @NonNull
private Type type; private Type type;
private Float heartRate_bpm = null; private HeartRate heartRate = null;
private Float cadence_rpm = null; private Cadence cadence = null;
private Float power = null; private Power power = null;
private Float altitudeGain_m = null; private Float altitudeGain_m = null;
private Float altitudeLoss_m = null; private Float altitudeLoss_m = null;
@@ -382,44 +382,56 @@ public class TrackPoint {
} }
public boolean hasHeartRate() { public boolean hasHeartRate() {
return heartRate_bpm != null && heartRate_bpm > 0; return heartRate != null;
} }
public float getHeartRate_bpm() { public HeartRate getHeartRate() {
return heartRate_bpm; return heartRate;
} }
public TrackPoint setHeartRate_bpm(Float heartRate_bpm) { public TrackPoint setHeartRate(HeartRate heartRate) {
this.heartRate_bpm = heartRate_bpm; this.heartRate = heartRate;
return this; return this;
} }
public TrackPoint setHeartRate(float heartRate) {
return setHeartRate(HeartRate.of(heartRate));
}
public boolean hasCadence() { public boolean hasCadence() {
return cadence_rpm != null; return cadence != null;
} }
public float getCadence_rpm() { public Cadence getCadence() {
return cadence_rpm; return cadence;
} }
public TrackPoint setCadence_rpm(Float cadence_rpm) { public TrackPoint setCadence(Cadence cadence) {
this.cadence_rpm = cadence_rpm; this.cadence = cadence;
return this; return this;
} }
public TrackPoint setCadence(float cadence) {
return setCadence(Cadence.of(cadence));
}
public boolean hasPower() { public boolean hasPower() {
return power != null; return power != null;
} }
public float getPower() { public Power getPower() {
return power; return power;
} }
public TrackPoint setPower(Float power) { public TrackPoint setPower(Power power) {
this.power = power; this.power = power;
return this; return this;
} }
public TrackPoint setPower(float power) {
return setPower(Power.of(power));
}
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
@@ -434,8 +446,8 @@ public class TrackPoint {
", bearing=" + bearing + ", bearing=" + bearing +
", sensorDistance=" + sensorDistance + ", sensorDistance=" + sensorDistance +
", type=" + type + ", type=" + type +
", heartRate_bpm=" + heartRate_bpm + ", heartRate_bpm=" + heartRate +
", cadence_rpm=" + cadence_rpm + ", cadence_rpm=" + cadence +
", power=" + power + ", power=" + power +
", altitudeGain_m=" + altitudeGain_m + ", altitudeGain_m=" + altitudeGain_m +
", altitudeLoss_m=" + altitudeLoss_m + ", altitudeLoss_m=" + altitudeLoss_m +
@@ -331,15 +331,15 @@ public class GPXTrackExporter implements TrackExporter {
} }
if (trackPoint.hasHeartRate()) { if (trackPoint.hasHeartRate()) {
trackPointExtensionContent += "<gpxtpx:hr>" + HEARTRATE_FORMAT.format(trackPoint.getHeartRate_bpm()) + "</gpxtpx:hr>\n"; trackPointExtensionContent += "<gpxtpx:hr>" + HEARTRATE_FORMAT.format(trackPoint.getHeartRate().getBPM()) + "</gpxtpx:hr>\n";
} }
if (trackPoint.hasCadence()) { if (trackPoint.hasCadence()) {
trackPointExtensionContent += "<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCadence_rpm()) + "</gpxtpx:cad>\n"; trackPointExtensionContent += "<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCadence().getRPM()) + "</gpxtpx:cad>\n";
} }
if (trackPoint.hasPower()) { if (trackPoint.hasPower()) {
trackPointExtensionContent += "<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.getPower()) + "</pwr:PowerInWatts>\n"; trackPointExtensionContent += "<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.getPower().getW()) + "</pwr:PowerInWatts>\n";
} }
Double cumulativeGain = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGain() ? (double) tp.getAltitudeGain() : null); Double cumulativeGain = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGain() ? (double) tp.getAltitudeGain() : null);
@@ -381,9 +381,9 @@ public class KMLTrackExporter implements TrackExporter {
speedList.add(trackPoint.hasSpeed() ? (float) trackPoint.getSpeed().toMPS() : null); speedList.add(trackPoint.hasSpeed() ? (float) trackPoint.getSpeed().toMPS() : null);
distanceList.add(trackPoint.hasSensorDistance() ? (float) trackPoint.getSensorDistance().toM() : null); distanceList.add(trackPoint.hasSensorDistance() ? (float) trackPoint.getSensorDistance().toM() : null);
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate_bpm() : null); heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate().getBPM() : null);
cadenceList.add(trackPoint.hasCadence() ? trackPoint.getCadence_rpm() : null); cadenceList.add(trackPoint.hasCadence() ? trackPoint.getCadence().getRPM() : null);
powerList.add(trackPoint.hasPower() ? trackPoint.getPower() : null); powerList.add(trackPoint.hasPower() ? trackPoint.getPower().getW() : null);
altitudeGainList.add(trackPoint.hasAltitudeGain() ? trackPoint.getAltitudeGain() : null); altitudeGainList.add(trackPoint.hasAltitudeGain() ? trackPoint.getAltitudeGain() : null);
altitudeLossList.add(trackPoint.hasAltitudeLoss() ? trackPoint.getAltitudeLoss() : null); altitudeLossList.add(trackPoint.hasAltitudeLoss() ? trackPoint.getAltitudeLoss() : null);
@@ -299,7 +299,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
} }
if (heartrate != null) { if (heartrate != null) {
try { try {
trackPoint.setHeartRate_bpm(Float.parseFloat(heartrate)); trackPoint.setHeartRate(Float.parseFloat(heartrate));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), e); throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), e);
} }
@@ -307,7 +307,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
if (cadence != null) { if (cadence != null) {
try { try {
trackPoint.setCadence_rpm(Float.parseFloat(cadence)); trackPoint.setCadence(Float.parseFloat(cadence));
} catch (Exception e) { } catch (Exception e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse cadence: %s", cadence)), e); throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse cadence: %s", cadence)), e);
} }
@@ -318,13 +318,13 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
if (i < sensorDistanceList.size() && sensorDistanceList.get(i) != null) { if (i < sensorDistanceList.size() && sensorDistanceList.get(i) != null) {
trackPoint.setSensorDistance(Distance.of(sensorDistanceList.get(i))); trackPoint.setSensorDistance(Distance.of(sensorDistanceList.get(i)));
} }
if (i < sensorHeartRateList.size()) { if (i < sensorHeartRateList.size() && sensorHeartRateList.get(i) != null) {
trackPoint.setHeartRate_bpm(sensorHeartRateList.get(i)); trackPoint.setHeartRate(sensorHeartRateList.get(i));
} }
if (i < sensorCadenceList.size()) { if (i < sensorCadenceList.size() && sensorCadenceList.get(i) != null) {
trackPoint.setCadence_rpm(sensorCadenceList.get(i)); trackPoint.setCadence(sensorCadenceList.get(i));
} }
if (i < sensorPowerList.size()) { if (i < sensorPowerList.size() && sensorPowerList.get(i) != null) {
trackPoint.setPower(sensorPowerList.get(i)); trackPoint.setPower(sensorPowerList.get(i));
} }
if (i < altitudeGainList.size()) { if (i < altitudeGainList.size()) {
@@ -30,6 +30,9 @@ import androidx.annotation.NonNull;
import java.util.UUID; import java.util.UUID;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.sensors.sensorData.SensorData;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
@@ -161,9 +164,9 @@ public abstract class BluetoothConnectionManager<DataType> {
*/ */
protected abstract SensorData<DataType> parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic); protected abstract SensorData<DataType> parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic);
public static class HeartRate extends BluetoothConnectionManager<Float> { public static class HeartRateConnectionManager extends BluetoothConnectionManager<HeartRate> {
HeartRate(@NonNull SensorDataObserver observer) { HeartRateConnectionManager(@NonNull SensorDataObserver observer) {
super(BluetoothUtils.HEART_RATE_SERVICE_UUID, BluetoothUtils.HEART_RATE_MEASUREMENT_CHAR_UUID, observer); super(BluetoothUtils.HEART_RATE_SERVICE_UUID, BluetoothUtils.HEART_RATE_MEASUREMENT_CHAR_UUID, observer);
} }
@@ -176,23 +179,23 @@ public abstract class BluetoothConnectionManager<DataType> {
protected SensorDataHeartRate parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { protected SensorDataHeartRate parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
Integer heartRate = BluetoothUtils.parseHeartRate(characteristic); Integer heartRate = BluetoothUtils.parseHeartRate(characteristic);
return heartRate != null ? new SensorDataHeartRate(address, sensorName, heartRate) : null; return heartRate != null ? new SensorDataHeartRate(address, sensorName, HeartRate.of(heartRate)) : null;
} }
} }
public static class CyclingCadence extends BluetoothConnectionManager<Float> { public static class CyclingCadence extends BluetoothConnectionManager<Cadence> {
CyclingCadence(SensorDataObserver observer) { CyclingCadence(SensorDataObserver observer) {
super(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, observer); super(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, observer);
} }
@Override @Override
protected SensorDataCycling.Cadence createEmptySensorData(String address) { protected SensorDataCycling.CyclingCadence createEmptySensorData(String address) {
return new SensorDataCycling.Cadence(address); return new SensorDataCycling.CyclingCadence(address);
} }
@Override @Override
protected SensorDataCycling.Cadence parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { protected SensorDataCycling.CyclingCadence parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic);
if (cadenceAndSpeed == null) { if (cadenceAndSpeed == null) {
return null; return null;
@@ -232,7 +235,7 @@ public abstract class BluetoothConnectionManager<DataType> {
} }
} }
public static class CyclingPower extends BluetoothConnectionManager<Float> { public static class CyclingPower extends BluetoothConnectionManager<Power> {
CyclingPower(@NonNull SensorDataObserver observer) { CyclingPower(@NonNull SensorDataObserver observer) {
super(BluetoothUtils.CYCLING_POWER_UUID, BluetoothUtils.CYCLING_POWER_MEASUREMENT_CHAR_UUID, observer); super(BluetoothUtils.CYCLING_POWER_UUID, BluetoothUtils.CYCLING_POWER_MEASUREMENT_CHAR_UUID, observer);
@@ -247,7 +250,7 @@ public abstract class BluetoothConnectionManager<DataType> {
protected SensorDataCyclingPower parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { protected SensorDataCyclingPower parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
Integer cyclingPower = BluetoothUtils.parseCyclingPower(characteristic); Integer cyclingPower = BluetoothUtils.parseCyclingPower(characteristic);
return cyclingPower != null ? new SensorDataCyclingPower(address, sensorName, cyclingPower) : null; return cyclingPower != null ? new SensorDataCyclingPower(address, sensorName, Power.of(cyclingPower)) : null;
} }
} }
@@ -62,7 +62,7 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
private Distance preferenceWheelCircumference; private Distance preferenceWheelCircumference;
private final BluetoothConnectionManager.HeartRate heartRate = new BluetoothConnectionManager.HeartRate(this); private final BluetoothConnectionManager.HeartRateConnectionManager heartRate = new BluetoothConnectionManager.HeartRateConnectionManager(this);
private final BluetoothConnectionManager.CyclingCadence cyclingCadence = new BluetoothConnectionManager.CyclingCadence(this); private final BluetoothConnectionManager.CyclingCadence cyclingCadence = new BluetoothConnectionManager.CyclingCadence(this);
private final BluetoothConnectionManager.CyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingDistanceSpeed(this); private final BluetoothConnectionManager.CyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingDistanceSpeed(this);
private final BluetoothConnectionManager.CyclingPower cyclingPower = new BluetoothConnectionManager.CyclingPower(this); private final BluetoothConnectionManager.CyclingPower cyclingPower = new BluetoothConnectionManager.CyclingPower(this);
@@ -180,15 +180,15 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
@Override @Override
public synchronized void onChanged(SensorData<?> sensorData) { public synchronized void onChanged(SensorData<?> sensorData) {
if (sensorData instanceof SensorDataCycling.Cadence) { if (sensorData instanceof SensorDataCycling.CyclingCadence) {
SensorDataCycling.Cadence previous = sensorDataSet.getCyclingCadence(); SensorDataCycling.CyclingCadence previous = sensorDataSet.getCyclingCadence();
Log.d(TAG, "Previous: " + previous + "; current: " + sensorData); Log.d(TAG, "Previous: " + previous + "; current: " + sensorData);
if (sensorData.equals(previous)) { if (sensorData.equals(previous)) {
Log.d(TAG, "onChanged: cadence data repeated."); Log.d(TAG, "onChanged: cadence data repeated.");
return; return;
} }
((SensorDataCycling.Cadence) sensorData).compute(previous); ((SensorDataCycling.CyclingCadence) sensorData).compute(previous);
} }
if (sensorData instanceof SensorDataCycling.DistanceSpeed) { if (sensorData instanceof SensorDataCycling.DistanceSpeed) {
SensorDataCycling.DistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed(); SensorDataCycling.DistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed();
@@ -28,6 +28,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
@@ -128,13 +129,13 @@ public class BluetoothUtils {
index += 2; index += 2;
} }
SensorDataCycling.Cadence cadence = null; SensorDataCycling.CyclingCadence cadence = null;
if (hasCrank && valueLength - index >= 4) { if (hasCrank && valueLength - index >= 4) {
long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index);
index += 2; index += 2;
int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s
cadence = new SensorDataCycling.Cadence(address, sensorName, crankCount, crankTime); cadence = new SensorDataCycling.CyclingCadence(address, sensorName, crankCount, crankTime);
} }
return new SensorDataCycling.CadenceAndSpeed(address, sensorName, cadence, speed); return new SensorDataCycling.CadenceAndSpeed(address, sensorName, cadence, speed);
@@ -153,7 +154,7 @@ public class BluetoothUtils {
boolean hasStatus = (flags & 0x03) > 0; // walking vs running boolean hasStatus = (flags & 0x03) > 0; // walking vs running
Speed speed = null; Speed speed = null;
Float cadence = null; Cadence cadence = null;
Distance totalDistance = null; Distance totalDistance = null;
int index = 1; int index = 1;
@@ -163,7 +164,7 @@ public class BluetoothUtils {
index = 3; index = 3;
if (valueLength - index >= 1) { if (valueLength - index >= 1) {
cadence = Float.valueOf(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index)); cadence = Cadence.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index));
} }
index = 4; index = 4;
@@ -8,6 +8,7 @@ import androidx.core.util.Pair;
import java.time.Duration; import java.time.Duration;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.UnitConversions; import de.dennisguse.opentracks.data.models.UnitConversions;
@@ -23,18 +24,18 @@ public final class SensorDataCycling {
private SensorDataCycling() { private SensorDataCycling() {
} }
public static class Cadence extends SensorData<Float> { public static class CyclingCadence extends SensorData<Cadence> {
private final Long crankRevolutionsCount; // UINT32 private final Long crankRevolutionsCount; // UINT32
private final Integer crankRevolutionsTime; // UINT16; 1/1024s private final Integer crankRevolutionsTime; // UINT16; 1/1024s
public Cadence(String sensorAddress) { public CyclingCadence(String sensorAddress) {
super(sensorAddress); super(sensorAddress);
this.crankRevolutionsCount = null; this.crankRevolutionsCount = null;
this.crankRevolutionsTime = null; this.crankRevolutionsTime = null;
} }
public Cadence(String sensorAddress, String sensorName, long crankRevolutionsCount, int crankRevolutionsTime) { public CyclingCadence(String sensorAddress, String sensorName, long crankRevolutionsCount, int crankRevolutionsTime) {
super(sensorAddress, sensorName); super(sensorAddress, sensorName);
this.crankRevolutionsCount = crankRevolutionsCount; this.crankRevolutionsCount = crankRevolutionsCount;
this.crankRevolutionsTime = crankRevolutionsTime; this.crankRevolutionsTime = crankRevolutionsTime;
@@ -43,7 +44,7 @@ public final class SensorDataCycling {
/** /**
* Workaround for Wahoo CADENCE: provides speed instead of cadence * Workaround for Wahoo CADENCE: provides speed instead of cadence
*/ */
public Cadence(@NonNull DistanceSpeed speed) { public CyclingCadence(@NonNull DistanceSpeed speed) {
this(speed.getSensorAddress(), speed.getSensorName(), speed.wheelRevolutionsCount, speed.wheelRevolutionsTime); this(speed.getSensorAddress(), speed.getSensorName(), speed.wheelRevolutionsCount, speed.wheelRevolutionsTime);
} }
@@ -61,11 +62,11 @@ public final class SensorDataCycling {
@NonNull @NonNull
@Override @Override
protected Float getNoneValue() { protected Cadence getNoneValue() {
return 0f; return Cadence.of(0);
} }
public void compute(Cadence previous) { public void compute(CyclingCadence previous) {
if (hasData() && previous != null && previous.hasData()) { if (hasData() && previous != null && previous.hasData()) {
float timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS; float timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
if (timeDiff_ms <= 0) { if (timeDiff_ms <= 0) {
@@ -82,7 +83,7 @@ public final class SensorDataCycling {
long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX); long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX);
float cadence_ms = crankDiff / timeDiff_ms; float cadence_ms = crankDiff / timeDiff_ms;
value = (float) (cadence_ms / UnitConversions.MS_TO_S / UnitConversions.S_TO_MIN); value = Cadence.of((float) (cadence_ms / UnitConversions.MS_TO_S / UnitConversions.S_TO_MIN));
} }
} }
@@ -94,9 +95,9 @@ public final class SensorDataCycling {
@Override @Override
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Cadence)) return false; if (!(obj instanceof CyclingCadence)) return false;
Cadence comp = (Cadence) obj; CyclingCadence comp = (CyclingCadence) obj;
if (hasData() && comp.hasData() == hasData()) { if (hasData() && comp.hasData() == hasData()) {
return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime(); return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime();
} else { } else {
@@ -230,14 +231,14 @@ public final class SensorDataCycling {
} }
} }
public static class CadenceAndSpeed extends SensorData<Pair<Cadence, DistanceSpeed>> { public static class CadenceAndSpeed extends SensorData<Pair<CyclingCadence, DistanceSpeed>> {
public CadenceAndSpeed(String sensorAddress, String sensorName, @Nullable Cadence cadence, @Nullable DistanceSpeed distanceSpeed) { public CadenceAndSpeed(String sensorAddress, String sensorName, @Nullable CyclingCadence cadence, @Nullable DistanceSpeed distanceSpeed) {
super(sensorAddress, sensorName); super(sensorAddress, sensorName);
this.value = new Pair<>(cadence, distanceSpeed); this.value = new Pair<>(cadence, distanceSpeed);
} }
public Cadence getCadence() { public CyclingCadence getCadence() {
return this.value != null ? this.value.first : null; return this.value != null ? this.value.first : null;
} }
@@ -247,7 +248,7 @@ public final class SensorDataCycling {
@NonNull @NonNull
@Override @Override
protected Pair<Cadence, DistanceSpeed> getNoneValue() { protected Pair<CyclingCadence, DistanceSpeed> getNoneValue() {
return new Pair<>(null, null); return new Pair<>(null, null);
} }
} }
@@ -2,15 +2,17 @@ package de.dennisguse.opentracks.sensors.sensorData;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
public class SensorDataCyclingPower extends SensorData<Float> { import de.dennisguse.opentracks.data.models.Power;
public class SensorDataCyclingPower extends SensorData<Power> {
public SensorDataCyclingPower(String address) { public SensorDataCyclingPower(String address) {
super(address); super(address);
} }
public SensorDataCyclingPower(String name, String address, float power_w) { public SensorDataCyclingPower(String name, String address, Power power) {
super(name, address); super(name, address);
this.value = power_w; this.value = power;
} }
@NonNull @NonNull
@@ -21,7 +23,7 @@ public class SensorDataCyclingPower extends SensorData<Float> {
@NonNull @NonNull
@Override @Override
protected Float getNoneValue() { protected Power getNoneValue() {
return 0f; return Power.of(0f);
} }
} }
@@ -2,15 +2,17 @@ package de.dennisguse.opentracks.sensors.sensorData;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
public class SensorDataHeartRate extends SensorData<Float> { import de.dennisguse.opentracks.data.models.HeartRate;
public class SensorDataHeartRate extends SensorData<HeartRate> {
public SensorDataHeartRate(String address) { public SensorDataHeartRate(String address) {
super(address); super(address);
} }
public SensorDataHeartRate(String name, String address, float heartRate_bpm) { public SensorDataHeartRate(String name, String address, @NonNull HeartRate heartRate) {
super(name, address); super(name, address);
this.value = heartRate_bpm; this.value = heartRate;
} }
@NonNull @NonNull
@@ -21,7 +23,7 @@ public class SensorDataHeartRate extends SensorData<Float> {
@NonNull @NonNull
@Override @Override
protected Float getNoneValue() { protected HeartRate getNoneValue() {
return 0f; return HeartRate.of(0);
} }
} }
@@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
@@ -16,7 +17,7 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
private final Speed speed; private final Speed speed;
private final Float cadence; private final Cadence cadence;
private final Distance totalDistance; private final Distance totalDistance;
@@ -27,7 +28,7 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
this.totalDistance = null; this.totalDistance = null;
} }
public SensorDataRunning(String sensorAddress, String sensorName, Speed speed, Float cadence, Distance totalDistance) { public SensorDataRunning(String sensorAddress, String sensorName, Speed speed, Cadence cadence, Distance totalDistance) {
super(sensorAddress, sensorName); super(sensorAddress, sensorName);
this.speed = speed; this.speed = speed;
this.cadence = cadence; this.cadence = cadence;
@@ -39,7 +40,7 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
} }
public Float getCadence() { public Cadence getCadence() {
return cadence; return cadence;
} }
@@ -56,9 +57,9 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
@Override @Override
protected Data getNoneValue() { protected Data getNoneValue() {
if (value != null) { if (value != null) {
return new Data(Speed.zero(), 0f, value.distance); return new Data(Speed.zero(), Cadence.of(0f), value.distance);
} else { } else {
return new Data(Speed.zero(), 0f, Distance.of(0)); return new Data(Speed.zero(), Cadence.of(0f), Distance.of(0));
} }
} }
@@ -85,12 +86,12 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
public static class Data { public static class Data {
private final Speed speed; private final Speed speed;
private final Float cadence; private final Cadence cadence;
@Nullable @Nullable
private final Distance distance; private final Distance distance;
public Data(Speed speed, Float cadence, @Nullable Distance distance) { public Data(Speed speed, Cadence cadence, @Nullable Distance distance) {
this.speed = speed; this.speed = speed;
this.cadence = cadence; this.cadence = cadence;
this.distance = distance; this.distance = distance;
@@ -100,7 +101,7 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
return speed; return speed;
} }
public Float getCadence() { public Cadence getCadence() {
return cadence; return cadence;
} }
@@ -4,6 +4,8 @@ import android.util.Pair;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -11,7 +13,7 @@ public final class SensorDataSet {
private SensorDataHeartRate heartRate; private SensorDataHeartRate heartRate;
private SensorDataCycling.Cadence cyclingCadence; private SensorDataCycling.CyclingCadence cyclingCadence;
private SensorDataCycling.DistanceSpeed cyclingDistanceSpeed; private SensorDataCycling.DistanceSpeed cyclingDistanceSpeed;
@@ -30,7 +32,7 @@ public final class SensorDataSet {
this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence; this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence;
} }
public Pair<Float, String> getHeartRate() { public Pair<HeartRate, String> getHeartRate() {
if (heartRate != null) { if (heartRate != null) {
return new Pair<>(heartRate.getValue(), heartRate.getSensorNameOrAddress()); return new Pair<>(heartRate.getValue(), heartRate.getSensorNameOrAddress());
} }
@@ -38,7 +40,7 @@ public final class SensorDataSet {
return null; return null;
} }
public Pair<Float, String> getCadence() { public Pair<Cadence, String> getCadence() {
if (cyclingCadence != null) { if (cyclingCadence != null) {
return new Pair<>(cyclingCadence.getValue(), cyclingCadence.getSensorNameOrAddress()); return new Pair<>(cyclingCadence.getValue(), cyclingCadence.getSensorNameOrAddress());
} }
@@ -62,7 +64,7 @@ public final class SensorDataSet {
return null; return null;
} }
public SensorDataCycling.Cadence getCyclingCadence() { public SensorDataCycling.CyclingCadence getCyclingCadence() {
return cyclingCadence; return cyclingCadence;
} }
@@ -96,11 +98,11 @@ public final class SensorDataSet {
public void fillTrackPoint(TrackPoint trackPoint) { public void fillTrackPoint(TrackPoint trackPoint) {
if (getHeartRate() != null) { if (getHeartRate() != null) {
trackPoint.setHeartRate_bpm(getHeartRate().first); trackPoint.setHeartRate(getHeartRate().first);
} }
if (getCadence() != null) { if (getCadence() != null) {
trackPoint.setCadence_rpm(getCadence().first); trackPoint.setCadence(getCadence().first);
} }
if (getSpeed() != null) { if (getSpeed() != null) {
@@ -144,8 +146,8 @@ public final class SensorDataSet {
return; return;
} }
if (type instanceof SensorDataCycling.Cadence) { if (type instanceof SensorDataCycling.CyclingCadence) {
this.cyclingCadence = (SensorDataCycling.Cadence) data; this.cyclingCadence = (SensorDataCycling.CyclingCadence) data;
return; return;
} }
if (type instanceof SensorDataCycling.DistanceSpeed) { if (type instanceof SensorDataCycling.DistanceSpeed) {
@@ -1,13 +1,17 @@
package de.dennisguse.opentracks.stats; package de.dennisguse.opentracks.stats;
public class SensorStatistics { import de.dennisguse.opentracks.data.models.Cadence;
private final Float maxHr; import de.dennisguse.opentracks.data.models.HeartRate;
private final Float avgHr; import de.dennisguse.opentracks.data.models.Power;
private final Float maxCadence;
private final Float avgCadence;
private final Float avgPower;
public SensorStatistics(Float maxHr, Float avgHr, Float maxCadence, Float avgCadence, Float avgPower) { public class SensorStatistics {
private final HeartRate maxHr;
private final HeartRate avgHr;
private final Cadence maxCadence;
private final Cadence avgCadence;
private final Power avgPower;
public SensorStatistics(HeartRate maxHr, HeartRate avgHr, Cadence maxCadence, Cadence avgCadence, Power avgPower) {
this.maxHr = maxHr; this.maxHr = maxHr;
this.avgHr = avgHr; this.avgHr = avgHr;
this.maxCadence = maxCadence; this.maxCadence = maxCadence;
@@ -19,11 +23,11 @@ public class SensorStatistics {
return avgHr != null && maxHr != null; return avgHr != null && maxHr != null;
} }
public float getMaxHeartRate() { public HeartRate getMaxHeartRate() {
return maxHr; return maxHr;
} }
public float getAvgHeartRate() { public HeartRate getAvgHeartRate() {
return avgHr; return avgHr;
} }
@@ -31,11 +35,11 @@ public class SensorStatistics {
return avgCadence != null && maxCadence != null; return avgCadence != null && maxCadence != null;
} }
public float getMaxCadence() { public Cadence getMaxCadence() {
return maxCadence; return maxCadence;
} }
public float getAvgCadence() { public Cadence getAvgCadence() {
return avgCadence; return avgCadence;
} }
@@ -43,7 +47,7 @@ public class SensorStatistics {
return avgPower != null; return avgPower != null;
} }
public float getAvgPower() { public Power getAvgPower() {
return avgPower; return avgPower;
} }
} }
@@ -41,7 +41,10 @@ import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
/** /**
@@ -245,28 +248,28 @@ public class StringUtils {
return new Pair<>(context.getString(R.string.time, minutes, seconds), unitString); return new Pair<>(context.getString(R.string.time, minutes, seconds), unitString);
} }
public static Pair<String, String> getHeartRateParts(Context context, Float heartrate_bpm) { public static Pair<String, String> getHeartRateParts(Context context, HeartRate heartrate) {
String value = context.getString(R.string.value_unknown); String value = context.getString(R.string.value_unknown);
if (heartrate_bpm != null) { if (heartrate != null) {
value = StringUtils.formatDecimal(heartrate_bpm, 0); value = StringUtils.formatDecimal(heartrate.getBPM(), 0);
} }
return new Pair<>(value, context.getString(R.string.sensor_unit_beats_per_minute)); return new Pair<>(value, context.getString(R.string.sensor_unit_beats_per_minute));
} }
public static Pair<String, String> getCadenceParts(Context context, Float cadence_rpm) { public static Pair<String, String> getCadenceParts(Context context, Cadence cadence) {
String value = context.getString(R.string.value_unknown); String value = context.getString(R.string.value_unknown);
if (cadence_rpm != null) { if (cadence != null) {
value = StringUtils.formatDecimal(cadence_rpm, 0); value = StringUtils.formatDecimal(cadence.getRPM(), 0);
} }
return new Pair<>(value, context.getString(R.string.sensor_unit_rounds_per_minute)); return new Pair<>(value, context.getString(R.string.sensor_unit_rounds_per_minute));
} }
public static Pair<String, String> getPowerParts(Context context, Float power_w) { public static Pair<String, String> getPowerParts(Context context, Power power) {
String value = context.getString(R.string.value_unknown); String value = context.getString(R.string.value_unknown);
if (power_w != null) { if (power != null) {
value = StringUtils.formatDecimal(power_w, 0); value = StringUtils.formatDecimal(power.getW(), 0);
} }
return new Pair<>(value, context.getString(R.string.sensor_unit_power)); return new Pair<>(value, context.getString(R.string.sensor_unit_power));
@@ -11,6 +11,7 @@ import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
@@ -115,9 +116,9 @@ public class StatisticDataBuilder {
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_cadence_key))) { } else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_cadence_key))) {
title = context.getString(R.string.stats_sensors_cadence); title = context.getString(R.string.stats_sensors_cadence);
Float cadence = null; Cadence cadence = null;
if (sensorDataSet != null) { if (sensorDataSet != null) {
Pair<Float, String> cadenceData = sensorDataSet.getCadence(); Pair<Cadence, String> cadenceData = sensorDataSet.getCadence();
if (cadenceData != null) { if (cadenceData != null) {
cadence = cadenceData.first; cadence = cadenceData.first;
description = cadenceData.second; description = cadenceData.second;