diff --git a/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java index f0b61d341..86c343a07 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java @@ -20,10 +20,11 @@ public class PressureSensorUtilsTest { } } + // Simulate a sudden drop of elevation. // test data @Test public void computeChanges_downhill() { - float[] sensorValues_hPa = new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f}; + float[] sensorValues_hPa = new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f}; float firstSensorValue = sensorValues_hPa[0]; // when @@ -34,18 +35,15 @@ public class PressureSensorUtilsTest { for (float v : sensorValues_hPa) { PressureSensorUtils.ElevationChange elevationChange = PressureSensorUtils.computeChanges_m(lastUsedPressureValue_hPa, v); if (elevationChange != null) { - if (elevationChange.getElevationChange_m() > 0) { - elevationGain_m += elevationChange.getElevationChange_m(); - } else { - elevationLoss_m += elevationChange.getElevationChange_m(); - } + elevationGain_m += elevationChange.getElevationGain_m(); + elevationLoss_m += elevationChange.getElevationLoss_m(); lastUsedPressureValue_hPa = elevationChange.getCurrentSensorValue_hPa(); } } // then assertEquals(0f, elevationGain_m, 0.01); - assertEquals(-1.08f, elevationLoss_m, 0.01); - assertEquals(1015.13, lastUsedPressureValue_hPa, 0.01); + assertEquals(-25.0f, elevationLoss_m, 0.01); + assertEquals(1018.01f, lastUsedPressureValue_hPa, 0.01); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java index adc5fb0ac..f167098a2 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java @@ -135,8 +135,6 @@ public class StatisticsRecordedFragment extends Fragment { speedMovingValue = view.findViewById(R.id.stats_moving_speed_value); speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit); - elevationCurrentGroup = view.findViewById(R.id.stats_elevation_current_group); - elevationTotalGainValue = view.findViewById(R.id.stats_elevation_gain_value); elevationTotalGainUnit = view.findViewById(R.id.stats_elevation_gain_unit); } diff --git a/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java b/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java index d0e3b5cf7..516c29c3d 100644 --- a/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java +++ b/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java @@ -23,7 +23,9 @@ public class ElevationSumManager implements SensorEventListener { private boolean isConnected = false; - private float lastUsedPressureValue_hPa; + private float lastAcceptedPressureValue_hPa; + + private float lastSeenSensorValue_hPa; private float elevationGain_m; private float elevationLoss_m; @@ -38,7 +40,7 @@ public class ElevationSumManager implements SensorEventListener { } isConnected = sensorManager.registerListener(this, pressureSensor, SAMPLING_RATE); - lastUsedPressureValue_hPa = Float.NaN; + lastAcceptedPressureValue_hPa = Float.NaN; reset(); } @@ -82,21 +84,21 @@ public class ElevationSumManager implements SensorEventListener { @VisibleForTesting void onSensorValueChanged(float value_hPa) { - if (Float.isNaN(lastUsedPressureValue_hPa)) { - lastUsedPressureValue_hPa = value_hPa; + if (Float.isNaN(lastAcceptedPressureValue_hPa)) { + lastAcceptedPressureValue_hPa = value_hPa; + lastSeenSensorValue_hPa = value_hPa; return; } - PressureSensorUtils.ElevationChange elevationChange = PressureSensorUtils.computeChanges_m(lastUsedPressureValue_hPa, value_hPa); + PressureSensorUtils.ElevationChange elevationChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedPressureValue_hPa, lastSeenSensorValue_hPa, value_hPa); if (elevationChange != null) { - if (elevationChange.getElevationChange_m() > 0) { - elevationGain_m += elevationChange.getElevationChange_m(); - } else { - elevationLoss_m += elevationChange.getElevationChange_m(); - } - lastUsedPressureValue_hPa = elevationChange.getCurrentSensorValue_hPa(); + elevationGain_m += elevationChange.getElevationGain_m(); + elevationLoss_m += elevationChange.getElevationLoss_m(); + lastAcceptedPressureValue_hPa = elevationChange.getCurrentSensorValue_hPa(); } + lastSeenSensorValue_hPa = value_hPa; + Log.v(TAG, "elevation gain: " + elevationGain_m); } } diff --git a/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java b/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java index af11d2e7f..3abf12a18 100644 --- a/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java @@ -7,6 +7,8 @@ public class PressureSensorUtils { //Everything above is considered a meaningful change in elevation. private static float ELEVATION_CHANGE_DIFF_M = 5.0f; + private static float EXPONENTIAL_SMOOTHING = 0.3f; + private PressureSensorUtils() { } @@ -28,6 +30,23 @@ public class PressureSensorUtils { public float getElevationChange_m() { return elevationChange_m; } + + public float getElevationGain_m() { + return elevationChange_m > 0 ? elevationChange_m : 0; + } + + public float getElevationLoss_m() { + return elevationChange_m < 0 ? elevationChange_m : 0; + } + } + + /** + * Applies exponential smoothing to sensor value before computation. + */ + public static ElevationChange computeChangesWithSmoothing_m(float lastAcceptedSensorValue_hPa, float lastSeenSensorValue_hPa, float currentSensorValue_hPa) { + float nextSensorValue_hPa = EXPONENTIAL_SMOOTHING * currentSensorValue_hPa + (1 - EXPONENTIAL_SMOOTHING) * lastSeenSensorValue_hPa; + + return computeChanges_m(lastAcceptedSensorValue_hPa, nextSensorValue_hPa); } /** @@ -35,8 +54,8 @@ public class PressureSensorUtils { * * @return null if no meaningful elevation change occurred. */ - public static ElevationChange computeChanges_m(float lastSensorValue_hPa, float currentSensorValue_hPa) { - float lastSensorValue_m = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, lastSensorValue_hPa); + public static ElevationChange computeChanges_m(float lastAcceptedSensorValue_hPa, float currentSensorValue_hPa) { + float lastSensorValue_m = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, lastAcceptedSensorValue_hPa); float currentSensorValue_m = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, currentSensorValue_hPa); float elevationChange_m = currentSensorValue_m - lastSensorValue_m; @@ -44,6 +63,21 @@ public class PressureSensorUtils { return null; } - return new ElevationChange(currentSensorValue_hPa, elevationChange_m); + // Limit elevation change by ELEVATION_CHANGE_DIFF and computes pressure value accordingly. + ElevationChange elevationChange = new ElevationChange(currentSensorValue_hPa, elevationChange_m); + if (elevationChange.getElevationGain_m() > 0) { + return new ElevationChange(getBarometricPressure(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, lastSensorValue_m + ELEVATION_CHANGE_DIFF_M), ELEVATION_CHANGE_DIFF_M); + } else { + return new ElevationChange(getBarometricPressure(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, lastSensorValue_m - ELEVATION_CHANGE_DIFF_M), -1 * ELEVATION_CHANGE_DIFF_M); + } + } + + /* + * Barometeric pressure to elevation estimation; inverts of SensorManager.getAltitude(float, float) + * https://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel#Internationale_H%C3%B6henformel + * {\color{White} p(h)} = p_0 \cdot \left( 1 - \frac{0{,}0065 \frac{\mathrm K}{\mathrm m} \cdot h}{T_0\ } \right)^{5{,}255} + */ + private static float getBarometricPressure(float p0, float altitude_m) { + return (float) (p0 * Math.pow(1.0 - 0.0065 * altitude_m / 288.15, 5.255f)); } }