forked from upstream-mirrors/OpenTracks
@@ -1,5 +1,7 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -10,7 +12,7 @@ public abstract class Aggregator<Input, Output> {
|
||||
|
||||
protected Raw<Input> previous;
|
||||
|
||||
protected Output value;
|
||||
protected Output aggregatedValue;
|
||||
|
||||
private final String sensorAddress;
|
||||
private final String sensorName;
|
||||
@@ -35,27 +37,35 @@ public abstract class Aggregator<Input, Output> {
|
||||
|
||||
protected abstract void computeValue(Raw<Input> current);
|
||||
|
||||
public boolean hasValue() {
|
||||
return value != null;
|
||||
public boolean hasAggregatedValue() {
|
||||
return aggregatedValue != null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected abstract Output getNoneValue();
|
||||
|
||||
public Output getValue(Instant now) {
|
||||
if (!hasValue()) {
|
||||
return null; //TODO Check if this is a good idea!
|
||||
@NonNull
|
||||
public Output getAggregatedValue(Instant now) {
|
||||
if (!hasAggregatedValue()) {
|
||||
return getNoneValue();
|
||||
}
|
||||
//TODO This should only affect measured data (like heartrate), but not aggregated values.
|
||||
//Remove current measurements, but provide aggregates?
|
||||
if (isRecent(now)) {
|
||||
return value;
|
||||
return aggregatedValue;
|
||||
}
|
||||
return getNoneValue();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Pair<Output, String> getAggregatedValueWithSensorName(Instant now) {
|
||||
return new Pair<>(getAggregatedValue(now), getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset long term aggregated values (more than derived from previous SensorData). e.g. overall distance.
|
||||
*/
|
||||
public void reset() {}
|
||||
public abstract void reset();
|
||||
|
||||
/**
|
||||
* Is the data recent considering the current time.
|
||||
@@ -66,12 +76,12 @@ public abstract class Aggregator<Input, Output> {
|
||||
}
|
||||
|
||||
return now
|
||||
.isBefore(previous.time().plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE));
|
||||
.isBefore(previous.time().plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE)); //TODO Per Sensor!
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "sensorAddress=" + sensorAddress + " data=" + value;
|
||||
return "sensorAddress=" + sensorAddress + " data=" + aggregatedValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,28 +19,28 @@ public class AggregatorBarometer extends Aggregator<AtmosphericPressure, Altitud
|
||||
protected void computeValue(Raw<AtmosphericPressure> current) {
|
||||
if (previous == null) {
|
||||
lastAcceptedSensorValue = current.value();
|
||||
value = getNoneValue();
|
||||
aggregatedValue = 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());
|
||||
aggregatedValue = new AltitudeGainLoss(aggregatedValue.gain_m() + altitudeChange.getAltitudeGain_m(), aggregatedValue.loss_m() + altitudeChange.getAltitudeLoss_m());
|
||||
|
||||
lastAcceptedSensorValue = altitudeChange.currentSensorValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
aggregatedValue = getNoneValue();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected AltitudeGainLoss getNoneValue() {
|
||||
return new AltitudeGainLoss(0f, 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
value = getNoneValue();
|
||||
}
|
||||
|
||||
public record Data(Altitude gain, Altitude loss) {}
|
||||
}
|
||||
|
||||
+27
-21
@@ -20,28 +20,34 @@ public class AggregatorCyclingCadence extends Aggregator<BluetoothHandlerCycling
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<BluetoothHandlerCyclingCadence.CrankData> current) {
|
||||
if (previous != null) {
|
||||
float timeDiff_ms = UintUtils.diff(current.value().crankRevolutionsTime(), previous.value().crankRevolutionsTime(), UintUtils.UINT16_MAX) / 1024f * 1000;
|
||||
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
|
||||
|
||||
if (timeDiff.isZero()) {
|
||||
return;
|
||||
}
|
||||
if (timeDiff.isNegative()) {
|
||||
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
|
||||
value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO We have to treat with overflow according to the documentation: read https://github.com/OpenTracksApp/OpenTracks/pull/953#discussion_r711625268
|
||||
if (current.value().crankRevolutionsCount() < previous.value().crankRevolutionsCount()) {
|
||||
Log.e(TAG, "Crank revolutions count difference is invalid: cannot compute cadence.");
|
||||
return;
|
||||
}
|
||||
|
||||
long crankDiff = UintUtils.diff(current.value().crankRevolutionsCount(), previous.value().crankRevolutionsCount(), UintUtils.UINT32_MAX);
|
||||
value = Cadence.of(crankDiff, timeDiff);
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float timeDiff_ms = UintUtils.diff(current.value().crankRevolutionsTime(), previous.value().crankRevolutionsTime(), UintUtils.UINT16_MAX) / 1024f * 1000;
|
||||
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
|
||||
|
||||
if (timeDiff.isZero()) {
|
||||
return;
|
||||
}
|
||||
if (timeDiff.isNegative()) {
|
||||
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
|
||||
aggregatedValue = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO We have to treat with overflow according to the documentation: read https://github.com/OpenTracksApp/OpenTracks/pull/953#discussion_r711625268
|
||||
if (current.value().crankRevolutionsCount() < previous.value().crankRevolutionsCount()) {
|
||||
Log.e(TAG, "Crank revolutions count difference is invalid: cannot compute cadence.");
|
||||
return;
|
||||
}
|
||||
|
||||
long crankDiff = UintUtils.diff(current.value().crankRevolutionsCount(), previous.value().crankRevolutionsCount(), UintUtils.UINT32_MAX);
|
||||
aggregatedValue = Cadence.of(crankDiff, timeDiff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
+32
-30
@@ -23,47 +23,49 @@ public class AggregatorCyclingDistanceSpeed extends Aggregator<BluetoothHandlerC
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<BluetoothHandlerCyclingDistanceSpeed.WheelData> current) {
|
||||
if (previous != null) {
|
||||
float timeDiff_ms = UintUtils.diff(current.value().wheelRevolutionsTime(), previous.value().wheelRevolutionsTime(), UintUtils.UINT16_MAX) / 1024f * 1000;
|
||||
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
|
||||
|
||||
if (timeDiff.isZero()) {
|
||||
return;
|
||||
}
|
||||
if (timeDiff.isNegative()) {
|
||||
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
|
||||
value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (current.value().wheelRevolutionsCount() < previous.value().wheelRevolutionsCount()) {
|
||||
Log.e(TAG, "Wheel revolutions count difference is invalid: cannot compute speed.");
|
||||
return;
|
||||
}
|
||||
long wheelDiff = UintUtils.diff(current.value().wheelRevolutionsCount(), previous.value().wheelRevolutionsCount(), UintUtils.UINT32_MAX);
|
||||
|
||||
Distance distance = wheelCircumference.multipliedBy(wheelDiff);
|
||||
Distance distanceOverall = distance;
|
||||
if (value != null) {
|
||||
distanceOverall = distance.plus(value.distanceOverall);
|
||||
}
|
||||
Speed speed_mps = Speed.of(distance, timeDiff);
|
||||
value = new Data(distance, distanceOverall, speed_mps);
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float timeDiff_ms = UintUtils.diff(current.value().wheelRevolutionsTime(), previous.value().wheelRevolutionsTime(), UintUtils.UINT16_MAX) / 1024f * 1000;
|
||||
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
|
||||
|
||||
if (timeDiff.isZero()) {
|
||||
return;
|
||||
}
|
||||
if (timeDiff.isNegative()) {
|
||||
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
|
||||
aggregatedValue = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (current.value().wheelRevolutionsCount() < previous.value().wheelRevolutionsCount()) {
|
||||
Log.e(TAG, "Wheel revolutions count difference is invalid: cannot compute speed.");
|
||||
return;
|
||||
}
|
||||
long wheelDiff = UintUtils.diff(current.value().wheelRevolutionsCount(), previous.value().wheelRevolutionsCount(), UintUtils.UINT32_MAX);
|
||||
|
||||
Distance distance = wheelCircumference.multipliedBy(wheelDiff);
|
||||
Distance distanceOverall = distance;
|
||||
if (aggregatedValue != null) {
|
||||
distanceOverall = distance.plus(aggregatedValue.distanceOverall);
|
||||
}
|
||||
Speed speed_mps = Speed.of(distance, timeDiff);
|
||||
aggregatedValue = new Data(distance, distanceOverall, speed_mps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (value != null) {
|
||||
value = new Data(value.distance, Distance.of(0), value.speed);
|
||||
if (aggregatedValue != null) {
|
||||
aggregatedValue = new Data(aggregatedValue.distance, Distance.of(0), aggregatedValue.speed);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Data getNoneValue() {
|
||||
if (value != null) {
|
||||
return new Data(value.distance, value.distanceOverall, Speed.zero());
|
||||
if (aggregatedValue != null) {
|
||||
return new Data(aggregatedValue.distance, aggregatedValue.distanceOverall, Speed.zero());
|
||||
} else {
|
||||
return new Data(Distance.of(0), Distance.of(0), Speed.zero());
|
||||
}
|
||||
|
||||
+5
-1
@@ -13,7 +13,11 @@ public class AggregatorCyclingPower extends Aggregator<BluetoothHandlerManagerCy
|
||||
|
||||
@Override
|
||||
public void computeValue(Raw<BluetoothHandlerManagerCyclingPower.Data> current) {
|
||||
this.value = current.value().power();
|
||||
this.aggregatedValue = current.value().power();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -13,12 +13,11 @@ public class AggregatorGPS extends Aggregator<Position, Position> {
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<Position> current) {
|
||||
value = current.value();
|
||||
aggregatedValue = current.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
value = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -12,7 +12,11 @@ public class AggregatorHeartRate extends Aggregator<HeartRate, HeartRate> {
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<HeartRate> current) {
|
||||
this.value = current.value();
|
||||
this.aggregatedValue = current.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -20,32 +20,33 @@ public final class AggregatorRunning extends Aggregator<BluetoothHandlerRunningS
|
||||
|
||||
@Override
|
||||
public void computeValue(Raw<BluetoothHandlerRunningSpeedAndCadence.Data> current) {
|
||||
if (previous != null) {
|
||||
|
||||
Distance distance = null;
|
||||
if (previous.value().totalDistance() != null && current.value().totalDistance() != null) {
|
||||
distance = current.value().totalDistance().minus(previous.value().totalDistance());
|
||||
if (value != null) {
|
||||
distance = distance.plus(value.distance);
|
||||
}
|
||||
}
|
||||
|
||||
value = new Data(current.value().speed(), current.value().cadence(), distance);
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Distance distance = null;
|
||||
if (previous.value().totalDistance() != null && current.value().totalDistance() != null) {
|
||||
distance = current.value().totalDistance().minus(previous.value().totalDistance());
|
||||
if (aggregatedValue != null) {
|
||||
distance = distance.plus(aggregatedValue.distance);
|
||||
}
|
||||
}
|
||||
|
||||
aggregatedValue = new Data(current.value().speed(), current.value().cadence(), distance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (value != null) {
|
||||
value = new Data(value.speed, value.cadence, Distance.of(0));
|
||||
if (aggregatedValue != null) {
|
||||
aggregatedValue = new Data(aggregatedValue.speed, aggregatedValue.cadence, Distance.of(0));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Data getNoneValue() {
|
||||
if (value != null) {
|
||||
return new Data(Speed.zero(), Cadence.of(0f), value.distance);
|
||||
if (aggregatedValue != null) {
|
||||
return new Data(Speed.zero(), Cadence.of(0f), aggregatedValue.distance);
|
||||
} else {
|
||||
return new Data(Speed.zero(), Cadence.of(0f), Distance.of(0));
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class SensorDataSet {
|
||||
|
||||
public AggregatorGPS gps;
|
||||
|
||||
private TrackPointCreator trackPointCreator;
|
||||
private final TrackPointCreator trackPointCreator;
|
||||
|
||||
public SensorDataSet(TrackPointCreator trackPointCreator) {
|
||||
this.trackPointCreator = trackPointCreator;
|
||||
@@ -64,7 +64,7 @@ public class SensorDataSet {
|
||||
|
||||
public Pair<HeartRate, String> getHeartRate() {
|
||||
if (heartRate != null) {
|
||||
return new Pair<>(heartRate.getValue(trackPointCreator.createNow()), heartRate.getSensorNameOrAddress());
|
||||
return heartRate.getAggregatedValueWithSensorName(trackPointCreator.createNow());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -72,23 +72,23 @@ public class SensorDataSet {
|
||||
|
||||
public Pair<Cadence, String> getCadence() {
|
||||
if (cyclingCadence != null) {
|
||||
return new Pair<>(cyclingCadence.getValue(trackPointCreator.createNow()), cyclingCadence.getSensorNameOrAddress());
|
||||
return cyclingCadence.getAggregatedValueWithSensorName(trackPointCreator.createNow());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.value.cadence() != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.value.cadence(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasAggregatedValue() && runningDistanceSpeedCadence.aggregatedValue.cadence() != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.aggregatedValue.cadence(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Pair<Speed, String> getSpeed() {
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue() && cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).speed() != null) {
|
||||
return new Pair<>(cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasAggregatedValue() && cyclingDistanceSpeed.getAggregatedValue(trackPointCreator.createNow()).speed() != null) {
|
||||
return new Pair<>(cyclingDistanceSpeed.getAggregatedValue(trackPointCreator.createNow()).speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue(trackPointCreator.createNow()).speed() != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.value.speed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasAggregatedValue() && runningDistanceSpeedCadence.getAggregatedValue(trackPointCreator.createNow()).speed() != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.aggregatedValue.speed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -156,8 +156,8 @@ public class SensorDataSet {
|
||||
}
|
||||
|
||||
public void fillTrackPoint(TrackPoint trackPoint) {
|
||||
if (gps != null && gps.hasValue()) {
|
||||
trackPoint.setPosition(gps.getValue(trackPointCreator.createNow()));
|
||||
if (gps != null && gps.hasAggregatedValue()) {
|
||||
trackPoint.setPosition(gps.getAggregatedValue(trackPointCreator.createNow()));
|
||||
}
|
||||
|
||||
if (getHeartRate() != null) {
|
||||
@@ -172,21 +172,21 @@ public class SensorDataSet {
|
||||
trackPoint.setSpeed(getSpeed().first);
|
||||
}
|
||||
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
|
||||
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).distanceOverall());
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasAggregatedValue()) {
|
||||
trackPoint.setSensorDistance(cyclingDistanceSpeed.getAggregatedValue(trackPointCreator.createNow()).distanceOverall());
|
||||
}
|
||||
|
||||
if (cyclingPower != null && cyclingPower.hasValue()) {
|
||||
trackPoint.setPower(cyclingPower.getValue(trackPointCreator.createNow()));
|
||||
if (cyclingPower != null && cyclingPower.hasAggregatedValue()) {
|
||||
trackPoint.setPower(cyclingPower.getAggregatedValue(trackPointCreator.createNow()));
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) {
|
||||
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue(trackPointCreator.createNow()).distance());
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasAggregatedValue()) {
|
||||
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getAggregatedValue(trackPointCreator.createNow()).distance());
|
||||
}
|
||||
|
||||
if (barometer != null && barometer.hasValue()) {
|
||||
trackPoint.setAltitudeGain(barometer.getValue(trackPointCreator.createNow()).gain_m());
|
||||
trackPoint.setAltitudeLoss(barometer.getValue(trackPointCreator.createNow()).loss_m());
|
||||
if (barometer != null && barometer.hasAggregatedValue()) {
|
||||
trackPoint.setAltitudeGain(barometer.getAggregatedValue(trackPointCreator.createNow()).gain_m());
|
||||
trackPoint.setAltitudeLoss(barometer.getAggregatedValue(trackPointCreator.createNow()).loss_m());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ public abstract class SensorStatisticsViewHolder extends StatisticViewHolder<Sta
|
||||
|
||||
Pair<String, String> valueAndUnit;
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingPower() != null) {
|
||||
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.getCyclingPower().getValue(Instant.now())); //TODO Use MonotonicClock
|
||||
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.getCyclingPower().getAggregatedValue(Instant.now())); //TODO Use MonotonicClock
|
||||
sensorName = sensorDataSet.getCyclingPower().getSensorNameOrAddress();
|
||||
} else {
|
||||
valueAndUnit = StringUtils.getCadenceParts(getContext(), null);
|
||||
|
||||
Reference in New Issue
Block a user