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
@@ -30,6 +30,9 @@ import androidx.annotation.NonNull;
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.SensorDataCycling;
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);
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);
}
@@ -176,23 +179,23 @@ public abstract class BluetoothConnectionManager<DataType> {
protected SensorDataHeartRate parsePayload(String sensorName, String address, BluetoothGattCharacteristic 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) {
super(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, observer);
}
@Override
protected SensorDataCycling.Cadence createEmptySensorData(String address) {
return new SensorDataCycling.Cadence(address);
protected SensorDataCycling.CyclingCadence createEmptySensorData(String address) {
return new SensorDataCycling.CyclingCadence(address);
}
@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);
if (cadenceAndSpeed == 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) {
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) {
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 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.CyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingDistanceSpeed(this);
private final BluetoothConnectionManager.CyclingPower cyclingPower = new BluetoothConnectionManager.CyclingPower(this);
@@ -180,15 +180,15 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
@Override
public synchronized void onChanged(SensorData<?> sensorData) {
if (sensorData instanceof SensorDataCycling.Cadence) {
SensorDataCycling.Cadence previous = sensorDataSet.getCyclingCadence();
if (sensorData instanceof SensorDataCycling.CyclingCadence) {
SensorDataCycling.CyclingCadence previous = sensorDataSet.getCyclingCadence();
Log.d(TAG, "Previous: " + previous + "; current: " + sensorData);
if (sensorData.equals(previous)) {
Log.d(TAG, "onChanged: cadence data repeated.");
return;
}
((SensorDataCycling.Cadence) sensorData).compute(previous);
((SensorDataCycling.CyclingCadence) sensorData).compute(previous);
}
if (sensorData instanceof SensorDataCycling.DistanceSpeed) {
SensorDataCycling.DistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed();
@@ -28,6 +28,7 @@ import java.util.Collections;
import java.util.List;
import java.util.UUID;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling;
@@ -128,13 +129,13 @@ public class BluetoothUtils {
index += 2;
}
SensorDataCycling.Cadence cadence = null;
SensorDataCycling.CyclingCadence cadence = null;
if (hasCrank && valueLength - index >= 4) {
long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index);
index += 2;
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);
@@ -153,7 +154,7 @@ public class BluetoothUtils {
boolean hasStatus = (flags & 0x03) > 0; // walking vs running
Speed speed = null;
Float cadence = null;
Cadence cadence = null;
Distance totalDistance = null;
int index = 1;
@@ -163,7 +164,7 @@ public class BluetoothUtils {
index = 3;
if (valueLength - index >= 1) {
cadence = Float.valueOf(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index));
cadence = Cadence.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index));
}
index = 4;
@@ -8,6 +8,7 @@ import androidx.core.util.Pair;
import java.time.Duration;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.UnitConversions;
@@ -23,18 +24,18 @@ public final class SensorDataCycling {
private SensorDataCycling() {
}
public static class Cadence extends SensorData<Float> {
public static class CyclingCadence extends SensorData<Cadence> {
private final Long crankRevolutionsCount; // UINT32
private final Integer crankRevolutionsTime; // UINT16; 1/1024s
public Cadence(String sensorAddress) {
public CyclingCadence(String sensorAddress) {
super(sensorAddress);
this.crankRevolutionsCount = 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);
this.crankRevolutionsCount = crankRevolutionsCount;
this.crankRevolutionsTime = crankRevolutionsTime;
@@ -43,7 +44,7 @@ public final class SensorDataCycling {
/**
* 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);
}
@@ -61,11 +62,11 @@ public final class SensorDataCycling {
@NonNull
@Override
protected Float getNoneValue() {
return 0f;
protected Cadence getNoneValue() {
return Cadence.of(0);
}
public void compute(Cadence previous) {
public void compute(CyclingCadence previous) {
if (hasData() && previous != null && previous.hasData()) {
float timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
if (timeDiff_ms <= 0) {
@@ -82,7 +83,7 @@ public final class SensorDataCycling {
long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX);
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
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()) {
return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime();
} 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);
this.value = new Pair<>(cadence, distanceSpeed);
}
public Cadence getCadence() {
public CyclingCadence getCadence() {
return this.value != null ? this.value.first : null;
}
@@ -247,7 +248,7 @@ public final class SensorDataCycling {
@NonNull
@Override
protected Pair<Cadence, DistanceSpeed> getNoneValue() {
protected Pair<CyclingCadence, DistanceSpeed> getNoneValue() {
return new Pair<>(null, null);
}
}
@@ -2,15 +2,17 @@ package de.dennisguse.opentracks.sensors.sensorData;
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) {
super(address);
}
public SensorDataCyclingPower(String name, String address, float power_w) {
public SensorDataCyclingPower(String name, String address, Power power) {
super(name, address);
this.value = power_w;
this.value = power;
}
@NonNull
@@ -21,7 +23,7 @@ public class SensorDataCyclingPower extends SensorData<Float> {
@NonNull
@Override
protected Float getNoneValue() {
return 0f;
protected Power getNoneValue() {
return Power.of(0f);
}
}
@@ -2,15 +2,17 @@ package de.dennisguse.opentracks.sensors.sensorData;
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) {
super(address);
}
public SensorDataHeartRate(String name, String address, float heartRate_bpm) {
public SensorDataHeartRate(String name, String address, @NonNull HeartRate heartRate) {
super(name, address);
this.value = heartRate_bpm;
this.value = heartRate;
}
@NonNull
@@ -21,7 +23,7 @@ public class SensorDataHeartRate extends SensorData<Float> {
@NonNull
@Override
protected Float getNoneValue() {
return 0f;
protected HeartRate getNoneValue() {
return HeartRate.of(0);
}
}
@@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
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 Float cadence;
private final Cadence cadence;
private final Distance totalDistance;
@@ -27,7 +28,7 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
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);
this.speed = speed;
this.cadence = cadence;
@@ -39,7 +40,7 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
}
public Float getCadence() {
public Cadence getCadence() {
return cadence;
}
@@ -56,9 +57,9 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
@Override
protected Data getNoneValue() {
if (value != null) {
return new Data(Speed.zero(), 0f, value.distance);
return new Data(Speed.zero(), Cadence.of(0f), value.distance);
} 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 {
private final Speed speed;
private final Float cadence;
private final Cadence cadence;
@Nullable
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.cadence = cadence;
this.distance = distance;
@@ -100,7 +101,7 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
return speed;
}
public Float getCadence() {
public Cadence getCadence() {
return cadence;
}
@@ -4,6 +4,8 @@ import android.util.Pair;
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.TrackPoint;
@@ -11,7 +13,7 @@ public final class SensorDataSet {
private SensorDataHeartRate heartRate;
private SensorDataCycling.Cadence cyclingCadence;
private SensorDataCycling.CyclingCadence cyclingCadence;
private SensorDataCycling.DistanceSpeed cyclingDistanceSpeed;
@@ -30,7 +32,7 @@ public final class SensorDataSet {
this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence;
}
public Pair<Float, String> getHeartRate() {
public Pair<HeartRate, String> getHeartRate() {
if (heartRate != null) {
return new Pair<>(heartRate.getValue(), heartRate.getSensorNameOrAddress());
}
@@ -38,7 +40,7 @@ public final class SensorDataSet {
return null;
}
public Pair<Float, String> getCadence() {
public Pair<Cadence, String> getCadence() {
if (cyclingCadence != null) {
return new Pair<>(cyclingCadence.getValue(), cyclingCadence.getSensorNameOrAddress());
}
@@ -62,7 +64,7 @@ public final class SensorDataSet {
return null;
}
public SensorDataCycling.Cadence getCyclingCadence() {
public SensorDataCycling.CyclingCadence getCyclingCadence() {
return cyclingCadence;
}
@@ -96,11 +98,11 @@ public final class SensorDataSet {
public void fillTrackPoint(TrackPoint trackPoint) {
if (getHeartRate() != null) {
trackPoint.setHeartRate_bpm(getHeartRate().first);
trackPoint.setHeartRate(getHeartRate().first);
}
if (getCadence() != null) {
trackPoint.setCadence_rpm(getCadence().first);
trackPoint.setCadence(getCadence().first);
}
if (getSpeed() != null) {
@@ -144,8 +146,8 @@ public final class SensorDataSet {
return;
}
if (type instanceof SensorDataCycling.Cadence) {
this.cyclingCadence = (SensorDataCycling.Cadence) data;
if (type instanceof SensorDataCycling.CyclingCadence) {
this.cyclingCadence = (SensorDataCycling.CyclingCadence) data;
return;
}
if (type instanceof SensorDataCycling.DistanceSpeed) {