From 01ee1a1e49f4ff36d5d239599aafbd9471c2c061 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Fri, 10 Apr 2020 22:34:27 +0200 Subject: [PATCH 01/13] Renamed DoubleBuffer to DoubleRingBuffer. --- .../stats/{DoubleBuffer.java => DoubleRingBuffer.java} | 6 +++--- .../dennisguse/opentracks/stats/TrackStatisticsUpdater.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/de/dennisguse/opentracks/stats/{DoubleBuffer.java => DoubleRingBuffer.java} (96%) diff --git a/src/main/java/de/dennisguse/opentracks/stats/DoubleBuffer.java b/src/main/java/de/dennisguse/opentracks/stats/DoubleRingBuffer.java similarity index 96% rename from src/main/java/de/dennisguse/opentracks/stats/DoubleBuffer.java rename to src/main/java/de/dennisguse/opentracks/stats/DoubleRingBuffer.java index 0ec4360c9..bc2167cfb 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/DoubleBuffer.java +++ b/src/main/java/de/dennisguse/opentracks/stats/DoubleRingBuffer.java @@ -19,13 +19,13 @@ package de.dennisguse.opentracks.stats; import androidx.annotation.NonNull; /** - * This class maintains a buffer of doubles. + * This class maintains a ring buffer of doubles. * This buffer is a convenient class for storing a series of doubles and calculating information about them. * This is a FIFO buffer. * * @author Sandor Dornbush */ -class DoubleBuffer { +class DoubleRingBuffer { // The sliding buffer of doubles. private final double[] buffer; @@ -41,7 +41,7 @@ class DoubleBuffer { * * @param size the size */ - DoubleBuffer(int size) { + DoubleRingBuffer(int size) { if (size < 1) { throw new IllegalArgumentException("The buffer size must be greater than 1."); } diff --git a/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java b/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java index 530e69161..dc7887619 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java +++ b/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java @@ -62,9 +62,9 @@ public class TrackStatisticsUpdater { private final TrackStatistics trackStatistics; // A buffer of the recent elevation readings (m) - private final DoubleBuffer elevationBuffer_m = new DoubleBuffer(ELEVATION_SMOOTHING_FACTOR); + private final DoubleRingBuffer elevationBuffer_m = new DoubleRingBuffer(ELEVATION_SMOOTHING_FACTOR); // A buffer of the recent speed readings (m/s) for calculating max speed - private final DoubleBuffer speedBuffer_ms = new DoubleBuffer(SPEED_SMOOTHING_FACTOR); + private final DoubleRingBuffer speedBuffer_ms = new DoubleRingBuffer(SPEED_SMOOTHING_FACTOR); // The current segment's statistics private TrackStatistics currentSegment; From 8a14bc46f614cff4feb67824dc6c10143b23e4bb Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Mon, 30 Mar 2020 20:03:04 +0200 Subject: [PATCH 02/13] Implement elevation gain via barometer. --- .../sensors/ElevationSumManagerTest.java | 34 ++++++ .../util/PressureSensorUtilsTest.java | 51 +++++++++ .../opentracks/content/data/TrackPoint.java | 13 +++ .../fragments/StatisticsRecordedFragment.java | 21 ++++ .../StatisticsRecordingFragment.java | 74 +++++++++---- .../services/TrackRecordingService.java | 43 +++++++- .../services/TrackRecordingServiceBinder.java | 5 + .../TrackRecordingServiceInterface.java | 11 +- .../services/sensors/ElevationSumManager.java | 102 ++++++++++++++++++ .../opentracks/stats/TrackStatistics.java | 2 +- .../stats/TrackStatisticsUpdater.java | 26 +++-- .../opentracks/util/PressureSensorUtils.java | 49 +++++++++ .../opentracks/util/UnitConversions.java | 3 +- src/main/res/layout/statistics_recorded.xml | 42 ++++++++ src/main/res/layout/statistics_recording.xml | 40 ++++++- 15 files changed, 480 insertions(+), 36 deletions(-) create mode 100644 src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java create mode 100644 src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java create mode 100644 src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java create mode 100644 src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java new file mode 100644 index 000000000..7259a04ec --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java @@ -0,0 +1,34 @@ +package de.dennisguse.opentracks.services.sensors; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * NOTE: Test data is completely artificial. + */ +public class ElevationSumManagerTest { + + private ElevationSumManager elevationSumManager = new ElevationSumManager(); + + private static void addSensorValue(ElevationSumManager elevationSumManager, float[] values) { + for (float f : values) { + elevationSumManager.onSensorValueChanged(f); + } + } + + @Before + public void setUp() { + elevationSumManager.reset(); + } + + @Test + public void getElevationGainLoss_downhill() { + // then + addSensorValue(elevationSumManager, 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}); + + // then + Assert.assertEquals(0f, elevationSumManager.getElevationGain_m(), 0.01); + Assert.assertEquals(-1.08, elevationSumManager.getElevationLoss_m(), 0.01); + } +} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java new file mode 100644 index 000000000..f0b61d341 --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java @@ -0,0 +1,51 @@ +package de.dennisguse.opentracks.util; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class PressureSensorUtilsTest { + + // real data at 500Hz stationary + @Test + public void elevationChanges_none() { + // given + float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f}; + float firstSensorValue = sensorValues_hPa[0]; + + // when // then + for (float v : sensorValues_hPa) { + assertNull(PressureSensorUtils.computeChanges_m(firstSensorValue, v)); + } + } + + // 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 firstSensorValue = sensorValues_hPa[0]; + + // when + float elevationGain_m = 0; + float elevationLoss_m = 0; + float lastUsedPressureValue_hPa = firstSensorValue; + + 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(); + } + 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); + } +} \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java b/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java index 17e2c2d0d..70fddb0a8 100644 --- a/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java +++ b/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java @@ -32,6 +32,7 @@ public class TrackPoint { private Float heartRate_bpm = null; private Float cyclingCadence_rpm = null; private Float power = null; + private float elevationGain = Float.NaN; public TrackPoint() { this.location = new Location(""); @@ -92,6 +93,18 @@ public class TrackPoint { return location; } + public boolean hasElevationGain() { + return !Float.isNaN(elevationGain); + } + + public float getElevationGain() { + return elevationGain; + } + + public void setElevationGain(float elevationGain) { + this.elevationGain = elevationGain; + } + public double getLatitude() { return location.getLatitude(); } diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java index eda807d19..adc5fb0ac 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java @@ -87,6 +87,10 @@ public class StatisticsRecordedFragment extends Fragment { private TextView speedMovingValue; private TextView speedMovingUnit; + private TextView elevationCurrentGroup; + private TextView elevationTotalGainValue; + private TextView elevationTotalGainUnit; + public static StatisticsRecordedFragment newInstance(Track.Id trackId) { Bundle bundle = new Bundle(); bundle.putParcelable(TRACK_ID_KEY, trackId); @@ -130,6 +134,11 @@ public class StatisticsRecordedFragment extends Fragment { speedMovingLabel = view.findViewById(R.id.stats_moving_speed_label); 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); } @Override @@ -187,6 +196,9 @@ public class StatisticsRecordedFragment extends Fragment { speedMovingLabel = null; speedMovingValue = null; speedMovingUnit = null; + + elevationTotalGainValue = null; + elevationTotalGainUnit = null; } public void loadStatistics() { @@ -264,5 +276,14 @@ public class StatisticsRecordedFragment extends Fragment { speedMovingValue.setText(parts.first); speedMovingUnit.setText(parts.second); } + + // Set elevation gain + { + double elevationGain_m = trackStatistics != null ? trackStatistics.getTotalElevationGain() : 0f; + + Pair parts = StringUtils.formatElevation(getContext(), elevationGain_m, metricUnits); + elevationTotalGainValue.setText(parts.first); + elevationTotalGainUnit.setText(parts.second); + } } } diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java index 45337dee3..c5cbe4828 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java @@ -132,12 +132,14 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi private TextView speedMovingLabel; private TextView speedMovingValue; private TextView speedMovingUnit; - private Group elevationGroup; private TextView speedLabel; private TextView speedValue; private TextView speedUnit; - private TextView elevationValue; - private TextView elevationUnit; + private Group elevationCurrentGroup; + private TextView elevationCurrentValue; + private TextView elevationCurrentUnit; + private TextView elevationTotalGainValue; + private TextView elevationTotalGainUnit; private Group coordinateGroup; private TextView latitudeValue; private TextView longitudeValue; @@ -178,14 +180,16 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi speedMovingValue = view.findViewById(R.id.stats_moving_speed_value); speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit); - elevationGroup = view.findViewById(R.id.stats_elevation_current_group); - speedLabel = view.findViewById(R.id.stats_speed_label); speedValue = view.findViewById(R.id.stats_speed_value); speedUnit = view.findViewById(R.id.stats_speed_unit); - elevationValue = view.findViewById(R.id.stats_elevation_current_value); - elevationUnit = view.findViewById(R.id.stats_elevation_current_unit); + elevationCurrentGroup = view.findViewById(R.id.stats_elevation_current_group); + elevationCurrentValue = view.findViewById(R.id.stats_elevation_current_value); + elevationCurrentUnit = view.findViewById(R.id.stats_elevation_current_unit); + + elevationTotalGainValue = view.findViewById(R.id.stats_elevation_gain_value); + elevationTotalGainUnit = view.findViewById(R.id.stats_elevation_gain_unit); coordinateGroup = view.findViewById(R.id.stats_coordinate_group); @@ -276,14 +280,16 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi speedMovingValue = null; speedMovingUnit = null; - elevationGroup = null; - speedLabel = null; speedValue = null; speedUnit = null; - elevationValue = null; - elevationUnit = null; + elevationCurrentGroup = null; + elevationCurrentValue = null; + elevationCurrentUnit = null; + + elevationTotalGainValue = null; + elevationTotalGainUnit = null; coordinateGroup = null; @@ -406,20 +412,25 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi /** * Tries to fetch most recent {@link SensorDataSet} from {@link de.dennisguse.opentracks.services.TrackRecordingService}. + * Also sets elevation gain. */ private void updateSensorDataUI() { TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound(); SensorDataSet sensorDataSet = null; + Float elevationGain_m = null; if (trackRecordingService == null) { Log.d(TAG, "Cannot get the track recording service."); } else { sensorDataSet = trackRecordingService.getSensorData(); + elevationGain_m = trackRecordingService.getElevationGain_m(); } setHeartRateSensorData(sensorDataSet); setCadenceSensorData(sensorDataSet); setSpeedSensorData(sensorDataSet, isSelectedTrackRecording()); + + setElevationGainFromSensor(elevationGain_m); } private void setHeartRateSensorData(SensorDataSet sensorDataSet) { @@ -468,6 +479,33 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi cadenceValueView.setText(sensorValue); } + // Set elevation gain + public void setTotalElevationGain(Float elevationGain_m) { + boolean metricUnits = PreferencesUtils.isMetricUnits(getContext()); + + if (elevationGain_m == null) { + elevationGain_m = 0f; + } + if (lastTrackStatistics != null) { + elevationGain_m += (float) lastTrackStatistics.getTotalElevationGain(); + } + + Pair parts = StringUtils.formatElevation(getContext(), elevationGain_m, metricUnits); + elevationTotalGainValue.setText(parts.first); + elevationTotalGainUnit.setText(parts.second); + } + + public void setElevationGainFromSensor(Float elevationGain_m) { + float totalElevationGain = 0; + if (elevationGain_m != null) { + totalElevationGain = elevationGain_m; + } + if (lastTrackStatistics != null) { + totalElevationGain += (float) lastTrackStatistics.getTotalElevationGain(); + } + setTotalElevationGain(totalElevationGain); + } + /** * If cadence and hear rate groups are invisible then sensor horizontal line hast to be invisible too. */ @@ -475,7 +513,6 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi if (cadenceGroup.getVisibility() != View.VISIBLE && heartRateGroup.getVisibility() != View.VISIBLE) { sensorHorizontalLine.setVisibility(View.GONE); } - } private void setSpeedSensorData(SensorDataSet sensorDataSet, boolean isRecording) { @@ -555,10 +592,11 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi speedMovingUnit.setText(parts.second); } - // Make elevation visible? + // Set elevation (gain) { + // Make elevation visible? boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext()); - elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); + elevationCurrentGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); } } @@ -594,14 +632,14 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi // Set elevation boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext()); - elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); + elevationCurrentGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); if (showElevation) { + // Current elevation double altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? lastTrackPoint.getAltitude() : Double.NaN; Pair parts = StringUtils.formatElevation(getContext(), altitude, metricUnits); - - elevationValue.setText(parts.first); - elevationUnit.setText(parts.second); + elevationCurrentValue.setText(parts.first); + elevationCurrentUnit.setText(parts.second); } // Set coordinate diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index 3fcb4d10d..16fcef666 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -49,6 +49,7 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet; import de.dennisguse.opentracks.services.handlers.GpsStatusValue; import de.dennisguse.opentracks.services.handlers.HandlerServer; import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager; +import de.dennisguse.opentracks.services.sensors.ElevationSumManager; import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory; import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor; import de.dennisguse.opentracks.stats.TrackStatistics; @@ -115,6 +116,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand // The following variables are set when recording: private WakeLock wakeLock; private BluetoothRemoteSensorManager remoteSensorManager; + private ElevationSumManager elevationSumManager; private TrackStatisticsUpdater trackStatisticsUpdater; private TrackPoint lastTrackPoint; @@ -174,6 +176,11 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand remoteSensorManager = null; } + if (elevationSumManager != null) { + elevationSumManager.stop(this); + elevationSumManager = null; + } + // Reverse order from onCreate showNotification(false); //TODO Why? @@ -371,6 +378,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand // Update instance variables remoteSensorManager = new BluetoothRemoteSensorManager(this); remoteSensorManager.start(); + + elevationSumManager = new ElevationSumManager(); + elevationSumManager.start(this); + lastTrackPoint = null; isIdle = false; @@ -456,6 +467,11 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand remoteSensorManager.stop(); remoteSensorManager = null; } + if (elevationSumManager != null) { + elevationSumManager.stop(this); + elevationSumManager = null; + } + lastTrackPoint = null; handlerServer.stop(this); @@ -533,6 +549,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand //TODO Figure out how to avoid loading the lastValidTrackPoint from the database TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId()); + if (elevationSumManager != null) { + trackPoint.setElevationGain(elevationSumManager.getElevationGain_m()); + } + //Storing trackPoint // Always insert the first segment location @@ -556,15 +576,19 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand insertTrackPoint(track, TrackPoint.createPause()); insertTrackPoint(track, trackPoint); - isIdle = false; + elevationSumManager.reset(); + isIdle = false; lastTrackPoint = trackPoint; return; } if (trackPoint.hasSensorData() || distanceToLastTrackLocation >= recordingDistanceInterval) { insertTrackPointIfNewer(track, lastTrackPoint); + insertTrackPoint(track, trackPoint); + elevationSumManager.reset(); + isIdle = false; lastTrackPoint = trackPoint; @@ -573,7 +597,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand if (!isIdle && !TrackPointUtils.isMoving(trackPoint)) { insertTrackPointIfNewer(track, lastTrackPoint); + insertTrackPoint(track, trackPoint); + elevationSumManager.reset(); + isIdle = true; lastTrackPoint = trackPoint; @@ -582,7 +609,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand if (isIdle && TrackPointUtils.isMoving(trackPoint)) { insertTrackPointIfNewer(track, lastTrackPoint); + insertTrackPoint(track, trackPoint); + elevationSumManager.reset(); + isIdle = false; lastTrackPoint = trackPoint; @@ -672,6 +702,17 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand } } + /** + * Returns the relative elevation gain (since last trackpoint). + */ + Float getElevationGain_m() { + if (elevationSumManager == null || !elevationSumManager.isConnected()) { + return null; + } + + return elevationSumManager.getElevationGain_m(); + } + private void showNotification(boolean isGpsStarted) { if (isRecording()) { Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class) diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java index db1c253fe..12a92ca0a 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceBinder.java @@ -99,6 +99,11 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco return trackRecordingService.getSensorDataSet(); } + @Override + public Float getElevationGain_m() { + return trackRecordingService.getElevationGain_m(); + } + @VisibleForTesting @Override public void setRemoteSensorManager(BluetoothRemoteSensorManager remoteSensorManager) { diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java index 61a557312..b56a704f3 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceInterface.java @@ -65,12 +65,19 @@ public interface TrackRecordingServiceInterface { Marker.Id insertMarker(String name, String category, String description, String photoUrl); /** - * Gets the current sensor data. Returns null if there is no data. + * Gets the current sensor data. * - * @return SensorDataSet object. + * @return SensorDataSet object or null. */ SensorDataSet getSensorData(); + /** + * Gets the current elevation gain. + * + * @return elevation gain in m or null. + */ + Float getElevationGain_m(); + @VisibleForTesting void setRemoteSensorManager(BluetoothRemoteSensorManager remoteSensorManager); diff --git a/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java b/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java new file mode 100644 index 000000000..d0e3b5cf7 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java @@ -0,0 +1,102 @@ +package de.dennisguse.opentracks.services.sensors; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; + +import de.dennisguse.opentracks.util.PressureSensorUtils; +import de.dennisguse.opentracks.util.UnitConversions; + +/** + * Estimates the elevation gain and elevation loss using the device's pressure sensor (i.e., barometer). + */ +public class ElevationSumManager implements SensorEventListener { + + private static final String TAG = ElevationSumManager.class.getSimpleName(); + + private static final int SAMPLING_RATE = 3 * (int) UnitConversions.ONE_SECOND_US; + + private boolean isConnected = false; + + private float lastUsedPressureValue_hPa; + + private float elevationGain_m; + private float elevationLoss_m; + + public void start(Context context) { + SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + + Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); + if (pressureSensor == null) { + Log.w(TAG, "No pressure sensor available."); + isConnected = false; + } + + isConnected = sensorManager.registerListener(this, pressureSensor, SAMPLING_RATE); + lastUsedPressureValue_hPa = Float.NaN; + reset(); + } + + public void stop(Context context) { + Log.d(TAG, "Stop"); + + SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + sensorManager.unregisterListener(this); + + isConnected = false; + reset(); + } + + public boolean isConnected() { + return isConnected; + } + + public float getElevationGain_m() { + return elevationGain_m; + } + + public float getElevationLoss_m() { + return elevationLoss_m; + } + + public void reset() { + Log.d(TAG, "Reset"); + elevationGain_m = 0; + elevationLoss_m = 0; + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + Log.w(TAG, "Sensor accuracy changes are (currently) ignored."); + } + + @Override + public void onSensorChanged(SensorEvent event) { + onSensorValueChanged(event.values[0]); + } + + @VisibleForTesting + void onSensorValueChanged(float value_hPa) { + if (Float.isNaN(lastUsedPressureValue_hPa)) { + lastUsedPressureValue_hPa = value_hPa; + return; + } + + PressureSensorUtils.ElevationChange elevationChange = PressureSensorUtils.computeChanges_m(lastUsedPressureValue_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(); + } + + Log.v(TAG, "elevation gain: " + elevationGain_m); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java b/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java index fa4fb271d..b9c0674c0 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java +++ b/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java @@ -228,7 +228,7 @@ public class TrackStatistics { } /** - * Gets the total elevation gain in meters. This is calculated as the sum of all positive differences in the smoothed elevation. + * Gets the total elevation gain in meters. */ public double getTotalElevationGain() { return totalElevationGain_m; diff --git a/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java b/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java index dc7887619..300833a50 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java +++ b/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java @@ -112,7 +112,7 @@ public class TrackStatisticsUpdater { * Adds a trackPoint. * TODO: This assume trackPoint has a valid time. * - * @param trackPoint the trackPoint + * @param trackPoint the trackPoint * @param minRecordingDistance the min recording distance */ public void addTrackPoint(TrackPoint trackPoint, int minRecordingDistance) { @@ -134,8 +134,16 @@ public class TrackStatisticsUpdater { return; } - //TODO Use Barometer to compute elevation gain. - double elevationDifference = trackPoint.hasAltitude() ? updateElevation(trackPoint.getAltitude()) : 0.0; + //Update absolute (GPS-based) elevation + if (trackPoint.hasAltitude()) { + updateAbsoluteElevation(trackPoint.getAltitude()); + } + + //Get elevation gain + if (trackPoint.hasElevationGain()) { + currentSegment.addTotalElevationGain(trackPoint.getElevationGain()); + Log.d(TAG, "elevation gain: " + trackPoint.getElevationGain()); + } if (lastTrackPoint == null || lastMovingTrackPoint == null) { lastTrackPoint = trackPoint; @@ -207,23 +215,21 @@ public class TrackStatisticsUpdater { } /** - * Updates an elevation reading. Returns the difference. + * Updates an elevation reading. * * @param elevation the elevation + * @return the difference */ @VisibleForTesting - private double updateElevation(double elevation) { + private double updateAbsoluteElevation(double elevation) { // Update elevation using the smoothed average double oldAverage = elevationBuffer_m.getAverage(); elevationBuffer_m.setNext(elevation); double newAverage = elevationBuffer_m.getAverage(); currentSegment.updateElevationExtremities(newAverage); - double difference = newAverage - oldAverage; - if (difference > 0) { - currentSegment.addTotalElevationGain(difference); - } - return difference; + + return newAverage - oldAverage; } private TrackStatistics init(long time) { diff --git a/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java b/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java new file mode 100644 index 000000000..af11d2e7f --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java @@ -0,0 +1,49 @@ +package de.dennisguse.opentracks.util; + +import android.hardware.SensorManager; + +public class PressureSensorUtils { + + //Everything above is considered a meaningful change in elevation. + private static float ELEVATION_CHANGE_DIFF_M = 5.0f; + + private PressureSensorUtils() { + } + + public static class ElevationChange { + + private float currentSensorValue_hPa; + + private float elevationChange_m; + + public ElevationChange(float currentSensorValue_hPa, float elevationChange_m) { + this.currentSensorValue_hPa = currentSensorValue_hPa; + this.elevationChange_m = elevationChange_m; + } + + public float getCurrentSensorValue_hPa() { + return currentSensorValue_hPa; + } + + public float getElevationChange_m() { + return elevationChange_m; + } + } + + /** + * Computes the elevation gain and elevation loss. + * + * @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); + float currentSensorValue_m = SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, currentSensorValue_hPa); + + float elevationChange_m = currentSensorValue_m - lastSensorValue_m; + if (Math.abs(elevationChange_m) < ELEVATION_CHANGE_DIFF_M) { + return null; + } + + return new ElevationChange(currentSensorValue_hPa, elevationChange_m); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java b/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java index 9b53bfde3..392b24f7b 100644 --- a/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java +++ b/src/main/java/de/dennisguse/opentracks/util/UnitConversions.java @@ -26,7 +26,8 @@ public class UnitConversions { public static final long S_TO_MS = 1000; // Time - // 1 second in milliseconds + // TODO Use int instead of long. + public static final long ONE_SECOND_US = UnitConversions.S_TO_MS * 1000; public static final long ONE_SECOND_MS = UnitConversions.S_TO_MS; public static final long ONE_MINUTE_MS = (long) (UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS); public static final long ONE_HOUR_MS = 60 * ONE_MINUTE_MS; diff --git a/src/main/res/layout/statistics_recorded.xml b/src/main/res/layout/statistics_recorded.xml index c580b7bb8..b960a47d6 100644 --- a/src/main/res/layout/statistics_recorded.xml +++ b/src/main/res/layout/statistics_recorded.xml @@ -209,6 +209,48 @@ app:layout_constraintRight_toLeftOf="@+id/guideline3" tools:text="km/h" /> + + + + + + + + + + + @@ -277,6 +277,40 @@ style="@style/StatsUnit" app:layout_constraintBottom_toBottomOf="@+id/stats_elevation_current_value" app:layout_constraintLeft_toRightOf="@+id/stats_elevation_current_value" + app:layout_constraintRight_toLeftOf="@+id/guideline2" + tools:text="m" /> + + + + + + + + + From c7db4f528e2b474df1994db8d529975db957d293 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 21 Jun 2020 20:01:47 +0200 Subject: [PATCH 03/13] Elevation gain: apply exponential smoothing and limit changes (defined change diff per sensor value). --- .../util/PressureSensorUtilsTest.java | 14 +++---- .../fragments/StatisticsRecordedFragment.java | 2 - .../services/sensors/ElevationSumManager.java | 24 ++++++----- .../opentracks/util/PressureSensorUtils.java | 40 +++++++++++++++++-- 4 files changed, 56 insertions(+), 24 deletions(-) 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)); } } From be7eb41cf3d0462780658d677912734b3cbce051 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 28 Jun 2020 08:27:23 +0200 Subject: [PATCH 04/13] Bugfix: previous total elevation gain was added twice. --- .../StatisticsRecordingFragment.java | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java index c5cbe4828..d68c13826 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java @@ -430,7 +430,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi setCadenceSensorData(sensorDataSet); setSpeedSensorData(sensorDataSet, isSelectedTrackRecording()); - setElevationGainFromSensor(elevationGain_m); + setTotalElevationGain(elevationGain_m); } private void setHeartRateSensorData(SensorDataSet sensorDataSet) { @@ -480,22 +480,9 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi } // Set elevation gain - public void setTotalElevationGain(Float elevationGain_m) { + private void setTotalElevationGain(Float elevationGain_m) { boolean metricUnits = PreferencesUtils.isMetricUnits(getContext()); - if (elevationGain_m == null) { - elevationGain_m = 0f; - } - if (lastTrackStatistics != null) { - elevationGain_m += (float) lastTrackStatistics.getTotalElevationGain(); - } - - Pair parts = StringUtils.formatElevation(getContext(), elevationGain_m, metricUnits); - elevationTotalGainValue.setText(parts.first); - elevationTotalGainUnit.setText(parts.second); - } - - public void setElevationGainFromSensor(Float elevationGain_m) { float totalElevationGain = 0; if (elevationGain_m != null) { totalElevationGain = elevationGain_m; @@ -503,7 +490,10 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi if (lastTrackStatistics != null) { totalElevationGain += (float) lastTrackStatistics.getTotalElevationGain(); } - setTotalElevationGain(totalElevationGain); + + Pair parts = StringUtils.formatElevation(getContext(), totalElevationGain, metricUnits); + elevationTotalGainValue.setText(parts.first); + elevationTotalGainUnit.setText(parts.second); } /** From 3bfb212dc9e00b00fd2aae10144c08eded714d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n?= Date: Wed, 29 Jul 2020 17:34:46 +0200 Subject: [PATCH 05/13] Bugfix: reset elevation every time track point is inserted. --- .../opentracks/services/TrackRecordingService.java | 12 ++++-------- .../opentracks/util/PressureSensorUtils.java | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index 16fcef666..fc2a9a035 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -549,10 +549,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand //TODO Figure out how to avoid loading the lastValidTrackPoint from the database TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId()); - if (elevationSumManager != null) { - trackPoint.setElevationGain(elevationSumManager.getElevationGain_m()); - } - //Storing trackPoint // Always insert the first segment location @@ -576,7 +572,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand insertTrackPoint(track, TrackPoint.createPause()); insertTrackPoint(track, trackPoint); - elevationSumManager.reset(); isIdle = false; lastTrackPoint = trackPoint; @@ -587,7 +582,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand insertTrackPointIfNewer(track, lastTrackPoint); insertTrackPoint(track, trackPoint); - elevationSumManager.reset(); isIdle = false; @@ -599,7 +593,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand insertTrackPointIfNewer(track, lastTrackPoint); insertTrackPoint(track, trackPoint); - elevationSumManager.reset(); isIdle = true; @@ -611,7 +604,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand insertTrackPointIfNewer(track, lastTrackPoint); insertTrackPoint(track, trackPoint); - elevationSumManager.reset(); isIdle = false; @@ -662,6 +654,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand */ private void insertTrackPoint(@NonNull Track track, @NonNull TrackPoint trackPoint) { try { + if (elevationSumManager != null) { + trackPoint.setElevationGain(elevationSumManager.getElevationGain_m()); + elevationSumManager.reset(); + } contentProviderUtils.insertTrackPoint(trackPoint, track.getId()); trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval); updateTrackTotalTime(track); diff --git a/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java b/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java index 3abf12a18..11aed2675 100644 --- a/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/PressureSensorUtils.java @@ -5,7 +5,7 @@ import android.hardware.SensorManager; public class PressureSensorUtils { //Everything above is considered a meaningful change in elevation. - private static float ELEVATION_CHANGE_DIFF_M = 5.0f; + private static float ELEVATION_CHANGE_DIFF_M = 3.0f; private static float EXPONENTIAL_SMOOTHING = 0.3f; From 4b7de188fd85f4cc6cd41dafd93bb0a6bb90f99d Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 2 Sep 2020 23:29:28 +0200 Subject: [PATCH 06/13] Elevation gain adheres to visibility setting. --- .../fragments/StatisticsRecordedFragment.java | 9 ++++++++- .../fragments/StatisticsRecordingFragment.java | 10 +++++----- src/main/res/layout/statistics_recorded.xml | 4 ++-- src/main/res/layout/statistics_recording.xml | 12 ++++-------- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java index f167098a2..ba4fb1bf3 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java @@ -29,6 +29,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.Group; import androidx.fragment.app.Fragment; import de.dennisguse.opentracks.R; @@ -87,7 +88,7 @@ public class StatisticsRecordedFragment extends Fragment { private TextView speedMovingValue; private TextView speedMovingUnit; - private TextView elevationCurrentGroup; + private Group elevationGroup; private TextView elevationTotalGainValue; private TextView elevationTotalGainUnit; @@ -135,6 +136,7 @@ public class StatisticsRecordedFragment extends Fragment { speedMovingValue = view.findViewById(R.id.stats_moving_speed_value); speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit); + elevationGroup = view.findViewById(R.id.stats_elevation_group); elevationTotalGainValue = view.findViewById(R.id.stats_elevation_gain_value); elevationTotalGainUnit = view.findViewById(R.id.stats_elevation_gain_unit); } @@ -195,6 +197,7 @@ public class StatisticsRecordedFragment extends Fragment { speedMovingValue = null; speedMovingUnit = null; + elevationGroup = null; elevationTotalGainValue = null; elevationTotalGainUnit = null; } @@ -277,6 +280,10 @@ public class StatisticsRecordedFragment extends Fragment { // Set elevation gain { + // Make elevation visible? + boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext()); + elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); + double elevationGain_m = trackStatistics != null ? trackStatistics.getTotalElevationGain() : 0f; Pair parts = StringUtils.formatElevation(getContext(), elevationGain_m, metricUnits); diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java index d68c13826..170c051f5 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java @@ -135,7 +135,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi private TextView speedLabel; private TextView speedValue; private TextView speedUnit; - private Group elevationCurrentGroup; + private Group elevationGroup; private TextView elevationCurrentValue; private TextView elevationCurrentUnit; private TextView elevationTotalGainValue; @@ -184,7 +184,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi speedValue = view.findViewById(R.id.stats_speed_value); speedUnit = view.findViewById(R.id.stats_speed_unit); - elevationCurrentGroup = view.findViewById(R.id.stats_elevation_current_group); + elevationGroup = view.findViewById(R.id.stats_elevation_group); elevationCurrentValue = view.findViewById(R.id.stats_elevation_current_value); elevationCurrentUnit = view.findViewById(R.id.stats_elevation_current_unit); @@ -284,7 +284,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi speedValue = null; speedUnit = null; - elevationCurrentGroup = null; + elevationGroup = null; elevationCurrentValue = null; elevationCurrentUnit = null; @@ -586,7 +586,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi { // Make elevation visible? boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext()); - elevationCurrentGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); + elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); } } @@ -622,7 +622,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi // Set elevation boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext()); - elevationCurrentGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); + elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); if (showElevation) { // Current elevation diff --git a/src/main/res/layout/statistics_recorded.xml b/src/main/res/layout/statistics_recorded.xml index b960a47d6..50e4aada6 100644 --- a/src/main/res/layout/statistics_recorded.xml +++ b/src/main/res/layout/statistics_recorded.xml @@ -219,10 +219,10 @@ app:layout_constraintTop_toBottomOf="@+id/stats_moving_speed_value" /> + app:constraint_referenced_ids="stats_elevation_horizontal_line,stats_elevation_gain_label,stats_elevation_gain_value,stats_elevation_gain_unit" /> - + + app:constraint_referenced_ids="stats_elevation_horizontal_line,stats_elevation_current_label,stats_elevation_current_value,stats_elevation_current_unit,stats_elevation_gain_label,stats_elevation_gain_value,stats_elevation_gain_unit" /> + - Date: Thu, 3 Sep 2020 14:55:12 +0200 Subject: [PATCH 07/13] Alter table trackpoints: added elevation gain to the trackpoints table. --- .../content/data/TrackPointsColumns.java | 4 ++- .../provider/ContentProviderUtils.java | 5 ++++ .../provider/CustomSQLiteOpenHelper.java | 30 ++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/content/data/TrackPointsColumns.java b/src/main/java/de/dennisguse/opentracks/content/data/TrackPointsColumns.java index d8d434853..59fa38806 100644 --- a/src/main/java/de/dennisguse/opentracks/content/data/TrackPointsColumns.java +++ b/src/main/java/de/dennisguse/opentracks/content/data/TrackPointsColumns.java @@ -51,6 +51,7 @@ public interface TrackPointsColumns extends BaseColumns { String SENSOR_HEARTRATE = "sensor_heartrate"; String SENSOR_CADENCE = "sensor_cadence"; String SENSOR_POWER = "sensor_power"; + String ELEVATION_GAIN = "elevation_gain"; String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " @@ -64,7 +65,8 @@ public interface TrackPointsColumns extends BaseColumns { + BEARING + " FLOAT, " + SENSOR_HEARTRATE + " FLOAT, " + SENSOR_CADENCE + " FLOAT, " - + SENSOR_POWER + " FLOAT)"; + + SENSOR_POWER + " FLOAT, " + + ELEVATION_GAIN + " FLOAT)"; String CREATE_TABLE_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + TRACKID + "_index ON " + TABLE_NAME + "(" + TRACKID + ")"; } diff --git a/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java b/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java index d0dc07e2f..4d67d2d30 100644 --- a/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java +++ b/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java @@ -836,6 +836,11 @@ public class ContentProviderUtils { if (trackPoint.hasPower()) { values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower()); } + + if (trackPoint.hasElevationGain()) { + values.put(TrackPointsColumns.ELEVATION_GAIN, trackPoint.getElevationGain()); + } + return values; } diff --git a/src/main/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelper.java b/src/main/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelper.java index 3d434132e..4c0b60113 100644 --- a/src/main/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelper.java +++ b/src/main/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelper.java @@ -25,7 +25,7 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper { private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName(); - private static final int DATABASE_VERSION = 26; + private static final int DATABASE_VERSION = 27; @VisibleForTesting public static final String DATABASE_NAME = "database.db"; @@ -70,6 +70,10 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper { case 26: upgradeFrom25to26(db); break; + case 27: + upgradeFrom26to27(db); + break; + default: throw new RuntimeException("Not implemented: upgrade to " + toVersion); @@ -91,6 +95,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper { case 25: downgradeFrom26to25(db); break; + case 26: + downgradeFrom27to26(db); + break; default: throw new RuntimeException("Not implemented: downgrade to " + toVersion); @@ -190,4 +197,25 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper { db.setTransactionSuccessful(); db.endTransaction(); } + + private void upgradeFrom26to27(SQLiteDatabase db) { + db.beginTransaction(); + + db.execSQL("ALTER TABLE trackpoints ADD COLUMN elevation_gain FLOAT"); + + db.setTransactionSuccessful(); + db.endTransaction(); + } + + private void downgradeFrom27to26(SQLiteDatabase db) { + db.beginTransaction(); + + db.execSQL("ALTER TABLE trackpoints RENAME TO trackpoints_old"); + db.execSQL("CREATE TABLE trackpoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackid INTEGER, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, speed FLOAT, bearing FLOAT, sensor_heartrate FLOAT, sensor_cadence FLOAT, sensor_power FLOAT)"); + db.execSQL("INSERT INTO trackpoints SELECT _id, trackid, longitude, latitude, time, elevation, accuracy, speed, bearing, sensor_heartrate, sensor_cadence, sensor_power FROM trackpoints_old"); + db.execSQL("DROP TABLE trackpoints_old"); + + db.setTransactionSuccessful(); + db.endTransaction(); + } } From fe3bf61acb6fe10981df0be2122c13679b4c4437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n?= Date: Thu, 3 Sep 2020 14:55:12 +0200 Subject: [PATCH 08/13] Squash: load TrackPoint elevationGain. --- .../opentracks/content/provider/CachedTrackPointsIndexes.java | 2 ++ .../opentracks/content/provider/ContentProviderUtils.java | 4 ++++ .../opentracks/content/provider/CustomSQLiteOpenHelper.java | 2 ++ 3 files changed, 8 insertions(+) diff --git a/src/main/java/de/dennisguse/opentracks/content/provider/CachedTrackPointsIndexes.java b/src/main/java/de/dennisguse/opentracks/content/provider/CachedTrackPointsIndexes.java index 73d6bff13..d63808c10 100644 --- a/src/main/java/de/dennisguse/opentracks/content/provider/CachedTrackPointsIndexes.java +++ b/src/main/java/de/dennisguse/opentracks/content/provider/CachedTrackPointsIndexes.java @@ -19,6 +19,7 @@ class CachedTrackPointsIndexes { final int sensorHeartRateIndex; final int sensorCadenceIndex; final int sensorPowerIndex; + final int elevationGainIndex; CachedTrackPointsIndexes(Cursor cursor) { idIndex = cursor.getColumnIndex(TrackPointsColumns._ID); @@ -32,5 +33,6 @@ class CachedTrackPointsIndexes { sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE); sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE); sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER); + elevationGainIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ELEVATION_GAIN); } } diff --git a/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java b/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java index 4d67d2d30..0721254ae 100644 --- a/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java +++ b/src/main/java/de/dennisguse/opentracks/content/provider/ContentProviderUtils.java @@ -648,6 +648,10 @@ public class ContentProviderUtils { trackPoint.setPower(cursor.getFloat(indexes.sensorPowerIndex)); } + if (!cursor.isNull(indexes.elevationGainIndex)) { + trackPoint.setElevationGain(cursor.getFloat(indexes.elevationGainIndex)); + } + return trackPoint; } diff --git a/src/main/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelper.java b/src/main/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelper.java index 4c0b60113..b5313da8b 100644 --- a/src/main/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelper.java +++ b/src/main/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelper.java @@ -215,6 +215,8 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper { db.execSQL("INSERT INTO trackpoints SELECT _id, trackid, longitude, latitude, time, elevation, accuracy, speed, bearing, sensor_heartrate, sensor_cadence, sensor_power FROM trackpoints_old"); db.execSQL("DROP TABLE trackpoints_old"); + db.execSQL("CREATE INDEX trackpoints_trackid_index ON trackpoints(trackid)"); + db.setTransactionSuccessful(); db.endTransaction(); } From 04b8d0f4a825b7b860a4ef4367ed53ce4d5b7eb2 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Thu, 3 Sep 2020 23:43:20 +0200 Subject: [PATCH 09/13] Import/Export: elevation gain per TrackPoint. --- .../opentracks/content/data/TestDataUtil.java | 2 +- .../io/file/importer/ExportImportTest.java | 11 +++++--- .../io/file/exporter/KmlTrackWriter.java | 8 ++++++ .../file/importer/KmlFileTrackImporter.java | 28 ++++++++++++------- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java b/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java index 83c272937..9ae50ac84 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java +++ b/src/androidTest/java/de/dennisguse/opentracks/content/data/TestDataUtil.java @@ -68,9 +68,9 @@ public class TestDataUtil { trackPoint.setSpeed(5f + (i / 10)); trackPoint.setHeartRate_bpm(100f + i); - trackPoint.setCyclingCadence_rpm(200f + i); trackPoint.setCyclingCadence_rpm(300f + i); trackPoint.setPower(400f + i); + trackPoint.setElevationGain(500f + i); return trackPoint; } diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index 4b58f68d4..7520f790f 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -133,7 +133,7 @@ public class ExportImportTest { assertMarkers(); // 3. trackpoints - assertTrackpoints(false, false, false); + assertTrackpoints(false, false, false, false); } @LargeTest @@ -168,7 +168,7 @@ public class ExportImportTest { assertMarkers(); // 3. trackpoints - assertTrackpoints(true, true, true); + assertTrackpoints(true, true, true, true); } @LargeTest @@ -257,7 +257,7 @@ public class ExportImportTest { assertMarkers(); // 3. trackpoints - assertTrackpoints(false, true, true); + assertTrackpoints(false, true, true, false); } @LargeTest @@ -304,7 +304,7 @@ public class ExportImportTest { } } - private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence) { + private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain) { List importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId); assertEquals(trackPoints.size(), importedTrackPoints.size()); @@ -330,6 +330,9 @@ public class ExportImportTest { if (verifyPower) { assertEquals(trackPoint.getPower(), importedTrackPoint.getPower(), 0.01); } + if (verifyElevationGain) { + assertEquals(trackPoint.getElevationGain(), importedTrackPoint.getElevationGain(), 0.01); + } } } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java index 3311e32c8..4e34c54d3 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java @@ -50,6 +50,7 @@ public class KmlTrackWriter implements TrackWriter { public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence"; public static final String EXTENDED_DATA_TYPE_HEART_RATE = "heart_rate"; public static final String EXTENDED_DATA_TYPE_POWER = "power"; + public static final String EXTENDED_DATA_TYPE_ELEVATION_GAIN = "elevation_gain"; private static final String WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png"; private static final String START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png"; @@ -68,6 +69,7 @@ public class KmlTrackWriter implements TrackWriter { private final List powerList = new ArrayList<>(); private final List cadenceList = new ArrayList<>(); private final List heartRateList = new ArrayList<>(); + private final List elevationGainList = new ArrayList<>(); private TrackPoint startTrackPoint; @@ -257,6 +259,9 @@ public class KmlTrackWriter implements TrackWriter { if (heartRateList.size() > 0) { writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE); } + if (elevationGainList.size() > 0) { + writeSimpleArrayData(elevationGainList, EXTENDED_DATA_TYPE_ELEVATION_GAIN); + } } printWriter.println(""); printWriter.println(""); @@ -287,6 +292,9 @@ public class KmlTrackWriter implements TrackWriter { if (trackPoint.hasPower()) { powerList.add(trackPoint.getPower()); } + if (trackPoint.hasElevationGain()) { + elevationGainList.add(trackPoint.getElevationGain()); + } } } } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java index 83bb2fa41..dbea4f76c 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java @@ -63,11 +63,12 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { private boolean trackStarted = false; private String extendedDataType; - private ArrayList trackPoints; - private ArrayList speedList; - private ArrayList cadenceList; - private ArrayList heartRateList; - private ArrayList powerList; + private ArrayList trackPoints = new ArrayList<>(); + private ArrayList speedList = new ArrayList<>(); + private ArrayList cadenceList = new ArrayList<>(); + private ArrayList heartRateList = new ArrayList<>(); + private ArrayList powerList = new ArrayList<>(); + private ArrayList elevationGainList = new ArrayList<>(); public KmlFileTrackImporter(Context context) { this(context, new ContentProviderUtils(context)); @@ -223,11 +224,12 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { @Override protected void onTrackSegmentStart() { super.onTrackSegmentStart(); - trackPoints = new ArrayList<>(); - speedList = new ArrayList<>(); - heartRateList = new ArrayList<>(); - cadenceList = new ArrayList<>(); - powerList = new ArrayList<>(); + trackPoints.clear(); + speedList.clear(); + heartRateList.clear(); + cadenceList.clear(); + powerList.clear(); + elevationGainList.clear(); } /** @@ -250,6 +252,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { if (i < powerList.size()) { trackPoint.setPower(powerList.get(i)); } + if (i < elevationGainList.size()) { + trackPoint.setElevationGain(elevationGainList.get(i)); + } insertTrackPoint(trackPoint); } @@ -316,6 +321,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { case KmlTrackWriter.EXTENDED_DATA_TYPE_CADENCE: cadenceList.add(value); break; + case KmlTrackWriter.EXTENDED_DATA_TYPE_ELEVATION_GAIN: + elevationGainList.add(value); + break; default: Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported."); } From e0d1ad4b52aa27b12ead51183469628f95b38180 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Thu, 3 Sep 2020 23:50:53 +0200 Subject: [PATCH 10/13] Squash: fixed tests. --- .../opentracks/services/sensors/ElevationSumManagerTest.java | 2 +- .../dennisguse/opentracks/util/PressureSensorUtilsTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java index 7259a04ec..720ebd749 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java @@ -29,6 +29,6 @@ public class ElevationSumManagerTest { // then Assert.assertEquals(0f, elevationSumManager.getElevationGain_m(), 0.01); - Assert.assertEquals(-1.08, elevationSumManager.getElevationLoss_m(), 0.01); + Assert.assertEquals(-48.0, elevationSumManager.getElevationLoss_m(), 0.01); } } \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java index 86c343a07..f3ee34a25 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/util/PressureSensorUtilsTest.java @@ -43,7 +43,7 @@ public class PressureSensorUtilsTest { // then assertEquals(0f, elevationGain_m, 0.01); - assertEquals(-25.0f, elevationLoss_m, 0.01); - assertEquals(1018.01f, lastUsedPressureValue_hPa, 0.01); + assertEquals(-15.0f, elevationLoss_m, 0.01); + assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing } } \ No newline at end of file From 6845ce498440346bacd6cf400e81cb6b0311837b Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 5 Sep 2020 11:45:54 +0200 Subject: [PATCH 11/13] Elevation gain: make it optional as Float. So if no sensor data is provided, it is not stored in the database and not exported. --- .../sensors/ElevationSumManagerTest.java | 16 +++++++++++++ .../opentracks/content/data/TrackPoint.java | 6 ++--- .../services/sensors/ElevationSumManager.java | 23 +++++++++++++++---- src/main/res/values/settings.xml | 2 +- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java index 720ebd749..6c1c2b40c 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/sensors/ElevationSumManagerTest.java @@ -24,6 +24,9 @@ public class ElevationSumManagerTest { @Test public void getElevationGainLoss_downhill() { + // given + elevationSumManager.setConnected(true); + // then addSensorValue(elevationSumManager, 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}); @@ -31,4 +34,17 @@ public class ElevationSumManagerTest { Assert.assertEquals(0f, elevationSumManager.getElevationGain_m(), 0.01); Assert.assertEquals(-48.0, elevationSumManager.getElevationLoss_m(), 0.01); } + + @Test + public void sensorUnavailable() { + // given + elevationSumManager.setConnected(false); + + // then + elevationSumManager.onSensorValueChanged(999f); + + // then + Assert.assertNull(elevationSumManager.getElevationGain_m()); + Assert.assertNull(elevationSumManager.getElevationLoss_m()); + } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java b/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java index 70fddb0a8..decb6a364 100644 --- a/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java +++ b/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java @@ -32,7 +32,7 @@ public class TrackPoint { private Float heartRate_bpm = null; private Float cyclingCadence_rpm = null; private Float power = null; - private float elevationGain = Float.NaN; + private Float elevationGain = null; public TrackPoint() { this.location = new Location(""); @@ -94,14 +94,14 @@ public class TrackPoint { } public boolean hasElevationGain() { - return !Float.isNaN(elevationGain); + return elevationGain != null; } public float getElevationGain() { return elevationGain; } - public void setElevationGain(float elevationGain) { + public void setElevationGain(Float elevationGain) { this.elevationGain = elevationGain; } 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 516c29c3d..f8ab382b9 100644 --- a/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java +++ b/src/main/java/de/dennisguse/opentracks/services/sensors/ElevationSumManager.java @@ -7,6 +7,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import de.dennisguse.opentracks.util.PressureSensorUtils; @@ -37,9 +38,10 @@ public class ElevationSumManager implements SensorEventListener { if (pressureSensor == null) { Log.w(TAG, "No pressure sensor available."); isConnected = false; + } else { + isConnected = sensorManager.registerListener(this, pressureSensor, SAMPLING_RATE); } - isConnected = sensorManager.registerListener(this, pressureSensor, SAMPLING_RATE); lastAcceptedPressureValue_hPa = Float.NaN; reset(); } @@ -58,12 +60,19 @@ public class ElevationSumManager implements SensorEventListener { return isConnected; } - public float getElevationGain_m() { - return elevationGain_m; + @VisibleForTesting + public void setConnected(boolean isConnected) { + this.isConnected = isConnected; } - public float getElevationLoss_m() { - return elevationLoss_m; + public @Nullable + Float getElevationGain_m() { + return isConnected ? elevationGain_m : null; + } + + public @Nullable + Float getElevationLoss_m() { + return isConnected ? elevationLoss_m : null; } public void reset() { @@ -79,6 +88,10 @@ public class ElevationSumManager implements SensorEventListener { @Override public void onSensorChanged(SensorEvent event) { + if (!isConnected) { + Log.w(TAG, "Not connected to sensor, cannot process data."); + return; + } onSensorValueChanged(event.values[0]); } diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index bc72b78a6..fef1d4469 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -5,7 +5,7 @@ false statsShowGradeElevation - false + true trackdetail_show_on_lockscreen_while_recording false From 5099118dd7b0b5a9f85abb0e6d14c4caae80de83 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 5 Sep 2020 12:36:12 +0200 Subject: [PATCH 12/13] Elevation gain: make it optional as Float - and if null, then the UI just shows "-". In TrackStatistics the elevation gain is now nullable. --- .../CustomContentProviderUtilsTest.java | 2 +- .../opentracks/stats/TrackStatisticsTest.java | 4 +-- .../viewmodels/AggregatedStatisticsTest.java | 2 +- .../content/DescriptionGenerator.java | 4 ++- .../fragments/StatisticsRecordedFragment.java | 2 +- .../StatisticsRecordingFragment.java | 17 +++++----- .../opentracks/stats/TrackStatistics.java | 31 ++++++++++++++----- .../opentracks/util/StringUtils.java | 14 ++++----- 8 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java index b7a517715..339c13e9f 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java @@ -455,7 +455,7 @@ public class CustomContentProviderUtilsTest { statistics.setTotalTime(1500L); statistics.setMovingTime(700L); statistics.setTotalDistance(750.0); - statistics.setTotalElevationGain(50.0); + statistics.setTotalElevationGain(50.0f); statistics.setMaxSpeed(60.0); statistics.setMaxElevation(1250.0); statistics.setMinElevation(1200.0); diff --git a/src/androidTest/java/de/dennisguse/opentracks/stats/TrackStatisticsTest.java b/src/androidTest/java/de/dennisguse/opentracks/stats/TrackStatisticsTest.java index 11bfd1239..a7fb3727a 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/stats/TrackStatisticsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/stats/TrackStatisticsTest.java @@ -51,8 +51,8 @@ public class TrackStatisticsTest { statistics2.setMovingTime(600L); // Result: 700+600 statistics.setTotalDistance(750.0); statistics2.setTotalDistance(350.0); // Result: 750+350 - statistics.setTotalElevationGain(50.0); - statistics2.setTotalElevationGain(850.0); // Result: 850+50 + statistics.setTotalElevationGain(50.0f); + statistics2.setTotalElevationGain(850.0f); // Result: 850+50 statistics.setMaxSpeed(60.0); // Resulting max speed statistics2.setMaxSpeed(30.0); statistics.setMaxElevation(1250.0); diff --git a/src/androidTest/java/de/dennisguse/opentracks/viewmodels/AggregatedStatisticsTest.java b/src/androidTest/java/de/dennisguse/opentracks/viewmodels/AggregatedStatisticsTest.java index b6148d3a4..4d67bfc64 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/viewmodels/AggregatedStatisticsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/viewmodels/AggregatedStatisticsTest.java @@ -35,7 +35,7 @@ public class AggregatedStatisticsTest { statistics.setTotalTime(totalTime); statistics.setMovingTime(totalTime); statistics.setTotalDistance(totalDistance); - statistics.setTotalElevationGain(50.0); + statistics.setTotalElevationGain(50.0f); statistics.setMaxSpeed(50.0); // Resulting max speed statistics.setMaxElevation(1250.0); statistics.setMinElevation(1200.0); // Resulting min elevation diff --git a/src/main/java/de/dennisguse/opentracks/content/DescriptionGenerator.java b/src/main/java/de/dennisguse/opentracks/content/DescriptionGenerator.java index bbd0c3f76..f73dd8d84 100644 --- a/src/main/java/de/dennisguse/opentracks/content/DescriptionGenerator.java +++ b/src/main/java/de/dennisguse/opentracks/content/DescriptionGenerator.java @@ -136,7 +136,9 @@ public class DescriptionGenerator { } // Elevation gain - writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak); + if (stats.hasTotalElevationGain()) { + writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak); + } // Recorded time builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTime(context, stats.getStartTime_ms()))); diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java index ba4fb1bf3..cbaa683b8 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordedFragment.java @@ -284,7 +284,7 @@ public class StatisticsRecordedFragment extends Fragment { boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext()); elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); - double elevationGain_m = trackStatistics != null ? trackStatistics.getTotalElevationGain() : 0f; + Float elevationGain_m = trackStatistics != null ? trackStatistics.getTotalElevationGain() : null; Pair parts = StringUtils.formatElevation(getContext(), elevationGain_m, metricUnits); elevationTotalGainValue.setText(parts.first); diff --git a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java index 170c051f5..b297f8d6d 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/StatisticsRecordingFragment.java @@ -481,14 +481,17 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi // Set elevation gain private void setTotalElevationGain(Float elevationGain_m) { + //TODO Check if we can distribute the total elevation gain via trackStatistics instead of doing some computation in the UI layer. boolean metricUnits = PreferencesUtils.isMetricUnits(getContext()); - float totalElevationGain = 0; - if (elevationGain_m != null) { - totalElevationGain = elevationGain_m; - } - if (lastTrackStatistics != null) { - totalElevationGain += (float) lastTrackStatistics.getTotalElevationGain(); + Float totalElevationGain = elevationGain_m; + + if (lastTrackStatistics != null && lastTrackStatistics.hasTotalElevationGain()) { + if (elevationGain_m == null) { + totalElevationGain = lastTrackStatistics.getTotalElevationGain(); + } else { + totalElevationGain += lastTrackStatistics.getTotalElevationGain(); + } } Pair parts = StringUtils.formatElevation(getContext(), totalElevationGain, metricUnits); @@ -626,7 +629,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi if (showElevation) { // Current elevation - double altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? lastTrackPoint.getAltitude() : Double.NaN; + Float altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? (float) lastTrackPoint.getAltitude() : null; Pair parts = StringUtils.formatElevation(getContext(), altitude, metricUnits); elevationCurrentValue.setText(parts.first); elevationCurrentUnit.setText(parts.second); diff --git a/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java b/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java index b9c0674c0..42bd19f7b 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java +++ b/src/main/java/de/dennisguse/opentracks/stats/TrackStatistics.java @@ -17,6 +17,7 @@ package de.dennisguse.opentracks.stats; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; /** * Statistical data about a {@link de.dennisguse.opentracks.content.data.Track}. @@ -24,6 +25,7 @@ import androidx.annotation.NonNull; * * @author Rodrigo Damazio */ +//TODO Use null instead of Double.isInfinite public class TrackStatistics { // The min and max elevation (meters) seen on this track. @@ -42,7 +44,7 @@ public class TrackStatistics { // The maximum speed (meters/second) that we believe is valid. private double maxSpeed_mps; // The total elevation gained (meters). - private double totalElevationGain_m; + private Float totalElevationGain_m = null; public TrackStatistics() { } @@ -80,7 +82,15 @@ public class TrackStatistics { elevationExtremities.update(other.elevationExtremities.getMin()); elevationExtremities.update(other.elevationExtremities.getMax()); } - totalElevationGain_m += other.totalElevationGain_m; + if (totalElevationGain_m == null) { + if (other.totalElevationGain_m != null) { + totalElevationGain_m = other.totalElevationGain_m; + } + } else { + if (other.totalElevationGain_m != null) { + totalElevationGain_m += other.totalElevationGain_m; + } + } } /** @@ -227,18 +237,23 @@ public class TrackStatistics { elevationExtremities.update(elevation_m); } - /** - * Gets the total elevation gain in meters. - */ - public double getTotalElevationGain() { + public boolean hasTotalElevationGain() { + return totalElevationGain_m != null; + } + + public @Nullable + Float getTotalElevationGain() { return totalElevationGain_m; } - public void setTotalElevationGain(double totalElevationGain_m) { + public void setTotalElevationGain(float totalElevationGain_m) { this.totalElevationGain_m = totalElevationGain_m; } - public void addTotalElevationGain(double gain_m) { + public void addTotalElevationGain(float gain_m) { + if (totalElevationGain_m == null) { + totalElevationGain_m = 0f; + } totalElevationGain_m += gain_m; } diff --git a/src/main/java/de/dennisguse/opentracks/util/StringUtils.java b/src/main/java/de/dennisguse/opentracks/util/StringUtils.java index c8c6239fa..583a317e9 100644 --- a/src/main/java/de/dennisguse/opentracks/util/StringUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/StringUtils.java @@ -324,21 +324,21 @@ public class StringUtils { } /** - * Sets an elevation value. + * Sets an elevation_m value. * * @param context the context - * @param elevation the elevation in meters + * @param elevation_m the elevation_m * @param metricUnits true if metric units - * @return the formatted elevation (or null) and it's unit as {@link Pair} + * @return the formatted elevation_m (or null) and it's unit as {@link Pair} */ - public static Pair formatElevation(Context context, double elevation, boolean metricUnits) { + public static Pair formatElevation(Context context, Float elevation_m, boolean metricUnits) { String value = context.getString(R.string.value_unknown); String unit = context.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet); - if (!Double.isNaN(elevation) && !Double.isInfinite(elevation)) { + if (elevation_m != null) { if (!metricUnits) { - elevation *= UnitConversions.M_TO_FT; + elevation_m *= (float) UnitConversions.M_TO_FT; } - value = StringUtils.formatDecimal(elevation, 0); + value = StringUtils.formatDecimal(elevation_m, 0); } return new Pair<>(value, unit); } From 6e1aba6fa017ae2e75058aa66ed6f7a1db660e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n?= Date: Sun, 6 Sep 2020 14:08:31 +0200 Subject: [PATCH 13/13] Added barriers in stats recorded layout and stats recording layout. --- src/main/res/layout/statistics_recorded.xml | 45 +++++++++++++------- src/main/res/layout/statistics_recording.xml | 41 +++++++++++++++--- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/src/main/res/layout/statistics_recorded.xml b/src/main/res/layout/statistics_recorded.xml index 50e4aada6..5aec7f478 100644 --- a/src/main/res/layout/statistics_recorded.xml +++ b/src/main/res/layout/statistics_recorded.xml @@ -116,6 +116,14 @@ app:layout_constraintTop_toBottomOf="@+id/stats_moving_time_label" tools:text="00:00:00" /> + + + + app:layout_constraintTop_toBottomOf="@+id/stats_time_barrier" /> - + + + + + app:layout_constraintTop_toBottomOf="@+id/stats_speed_barrier" /> + + + + - - + app:layout_constraintTop_toBottomOf="@+id/stats_elevation_barrier" /> \ No newline at end of file diff --git a/src/main/res/layout/statistics_recording.xml b/src/main/res/layout/statistics_recording.xml index 3f36e83cd..ebc79b33e 100644 --- a/src/main/res/layout/statistics_recording.xml +++ b/src/main/res/layout/statistics_recording.xml @@ -116,6 +116,14 @@ app:layout_constraintTop_toBottomOf="@+id/stats_moving_time_label" tools:text="00:00:00" /> + + + + app:layout_constraintTop_toBottomOf="@+id/stats_time_barrier" /> + + + + app:layout_constraintTop_toBottomOf="@+id/stats_speed_barrier" /> - + + + + app:layout_constraintTop_toBottomOf="@+id/stats_elevation_barrier" /> + + + + app:layout_constraintTop_toBottomOf="@+id/stats_location_barrier" />