mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-09-30 16:52:21 +02:00
@@ -1,8 +1,32 @@
|
||||
<?xml version="1.0"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://opentracksapp.com/xmlschemas/v1">
|
||||
|
||||
<xs:element name="trackuuid"
|
||||
type="xs:string" />
|
||||
<xsd:element name="trackuuid"
|
||||
type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation xml:lang="en">
|
||||
A unique track identifier stored as UUID.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
</xs:schema>
|
||||
<xsd:element name="loss"
|
||||
type="xsd:float">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation xml:lang="en">
|
||||
The lost elevation in meters from the previous to the current TrackPoint.
|
||||
Only used in GPX.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="gain"
|
||||
type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation xml:lang="en">
|
||||
The gained elevation in meters from the previous to the current TrackPoint.
|
||||
Only used in GPX.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
+10
-8
@@ -96,34 +96,36 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_speed() {
|
||||
// given
|
||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress", "sensorName", 1, 6184);
|
||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 2, 8016);
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 1, 6184);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 2, 8016);
|
||||
|
||||
// when
|
||||
current.compute(previous, 2150);
|
||||
|
||||
// then
|
||||
assertEquals(1.20, current.getValue(), 0.01);
|
||||
assertEquals(1.20, current.getValue().distance_m, 2150);
|
||||
assertEquals(1.20, current.getValue().speed_mps, 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute_speed_rollOverCount() {
|
||||
// given
|
||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress", "sensorName", UintUtils.UINT16_MAX - 1, 1024);
|
||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT16_MAX - 1, 1024);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous, 2000);
|
||||
|
||||
// then
|
||||
assertEquals(2, current.getValue(), 0.01);
|
||||
assertEquals(1.20, current.getValue().distance_m, 2000);
|
||||
assertEquals(2, current.getValue().speed_mps, 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_speed_with_no_data() {
|
||||
// given
|
||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress");
|
||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress");
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
previous.toString();
|
||||
|
||||
+26
-58
@@ -3,7 +3,6 @@ package de.dennisguse.opentracks.io.file.importer;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -24,6 +23,7 @@ import org.junit.runners.JUnit4;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -91,17 +91,17 @@ public class ExportImportTest {
|
||||
|
||||
trackId = service.startNewTrack();
|
||||
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.001, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.002, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.001, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.002, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
service.resumeCurrentTrack();
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.003, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16.001, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.003, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16.001, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.endCurrentTrack();
|
||||
|
||||
track = contentProviderUtils.getTrack(trackId);
|
||||
@@ -163,10 +163,10 @@ public class ExportImportTest {
|
||||
assertEquals(track.getUuid(), importedTrack.getUuid());
|
||||
|
||||
// 2. trackpoints
|
||||
assertTrackpoints(trackPoints, false, false, false, false, false);
|
||||
assertTrackpoints(trackPoints, false, false, false, false, false, false);
|
||||
|
||||
// 3. trackstatistics
|
||||
assertTrackStatistics(false, false);
|
||||
assertTrackStatistics(false, false, false);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -201,10 +201,10 @@ public class ExportImportTest {
|
||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||
|
||||
// 2. trackpoints
|
||||
assertTrackpoints(trackPoints, true, true, true, true, true);
|
||||
assertTrackpoints(trackPoints, true, true, true, true, true, true);
|
||||
|
||||
// 2. trackstatistics
|
||||
assertTrackStatistics(false, true);
|
||||
assertTrackStatistics(false, true, true);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -234,38 +234,6 @@ public class ExportImportTest {
|
||||
assertNull(importedTrack);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_only_track() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata_and_pictures() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx() {
|
||||
@@ -303,10 +271,10 @@ public class ExportImportTest {
|
||||
trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
trackPointsWithCoordinates.get(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
|
||||
assertTrackpoints(trackPointsWithCoordinates, true, true, true, true, true);
|
||||
assertTrackpoints(trackPointsWithCoordinates, true, true, true, true, true, false);
|
||||
|
||||
// 3. trackstatistics
|
||||
assertTrackStatistics(true, true);
|
||||
assertTrackStatistics(true, true, false);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -356,7 +324,7 @@ public class ExportImportTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTrackpoints(List<TrackPoint> trackPoints, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss) {
|
||||
private void assertTrackpoints(List<TrackPoint> trackPoints, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss, boolean verifyDistance) {
|
||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
||||
|
||||
assertEquals(trackPoints.size(), importedTrackPoints.size());
|
||||
@@ -407,10 +375,13 @@ public class ExportImportTest {
|
||||
if (verifyElevationLoss) {
|
||||
assertEquals(trackPoint.getElevationLoss(), importedTrackPoint.getElevationLoss(), 0.01);
|
||||
}
|
||||
if (verifyDistance) {
|
||||
assertEquals(trackPoint.getSensorDistance(), importedTrackPoint.getSensorDistance(), 0.01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTrackStatistics(boolean isGpx, boolean verifyElevationGainAndLoss) {
|
||||
private void assertTrackStatistics(boolean isGpx, boolean verifyElevationGainAndLoss, boolean verifyDistance) {
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
|
||||
assertNotNull(importedTrack.getTrackStatistics());
|
||||
@@ -428,7 +399,9 @@ public class ExportImportTest {
|
||||
assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01);
|
||||
if (verifyDistance) {
|
||||
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
// Speed
|
||||
assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed(), 0.01);
|
||||
@@ -448,20 +421,15 @@ public class ExportImportTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static TrackPoint createTrackPoint(long time, double latitude, double longitude, float accuracy, long speed, long altitude, float elevationGain, float heartRate, float cyclingCadence, float power) {
|
||||
Location location = new Location("");
|
||||
location.setTime(time);
|
||||
location.setLongitude(longitude);
|
||||
location.setLatitude(latitude);
|
||||
location.setAccuracy(accuracy);
|
||||
location.setAltitude(altitude);
|
||||
location.setSpeed(speed);
|
||||
|
||||
TrackPoint tp = new TrackPoint(location);
|
||||
private static TrackPoint createTrackPoint(long time, double latitude, double longitude, float accuracy, float speed, float altitude, float elevationGain, float heartRate, float cyclingCadence, float power, float distance) {
|
||||
TrackPoint tp = new TrackPoint(latitude, longitude, (double) altitude, Instant.ofEpochMilli(time));
|
||||
tp.setAccuracy(accuracy);
|
||||
tp.setSpeed(speed);
|
||||
tp.setHeartRate_bpm(heartRate);
|
||||
tp.setCyclingCadence_rpm(cyclingCadence);
|
||||
tp.setPower(power);
|
||||
tp.setElevationGain(elevationGain);
|
||||
tp.setSensorDistance(distance);
|
||||
return tp;
|
||||
}
|
||||
}
|
||||
+131
-4
@@ -1,27 +1,36 @@
|
||||
package de.dennisguse.opentracks.stats;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackStatisticsUpdaterTest {
|
||||
|
||||
private static final int GPS_DISTANCE = 50;
|
||||
|
||||
@Test
|
||||
public void addTrackPoint() {
|
||||
public void addTrackPoint_TestingTrack() {
|
||||
// given
|
||||
TestDataUtil.TrackData data = TestDataUtil.createTestingTrack(new Track.Id(1));
|
||||
|
||||
// when
|
||||
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
|
||||
data.trackPoints.forEach(it -> updater.addTrackPoint(it, 50));
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
data.trackPoints.forEach(it -> subject.addTrackPoint(it, GPS_DISTANCE));
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = updater.getTrackStatistics();
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
assertEquals(85.35, statistics.getTotalDistance(), 0.01);
|
||||
assertEquals(Duration.ofMillis(13999), statistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(6), statistics.getMovingTime());
|
||||
@@ -35,4 +44,122 @@ public class TrackStatisticsUpdaterTest {
|
||||
assertEquals(14.226, statistics.getAverageMovingSpeed(), 0.01);
|
||||
assertEquals(6.566, statistics.getAverageSpeed(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_distance_from_GPS_not_moving() {
|
||||
// given
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(0, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_distance_from_GPS_moving() {
|
||||
// given
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||
tp3.setSpeed(5f);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(1.10, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_distance_from_GPS_moving_and_sensor_moving() {
|
||||
// given
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||
tp2.setSpeed(5f);
|
||||
TrackPoint tp3 = new TrackPoint(0.001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||
tp2.setSpeed(5f);
|
||||
TrackPoint tp4 = new TrackPoint(0.001, 0, 5.0, Instant.ofEpochMilli(4000));
|
||||
tp2.setSpeed(5f);
|
||||
tp4.setSensorDistance(5f);
|
||||
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||
tp5.setSensorDistance(10f);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(110.57, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp4, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp5, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(125.57, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_distance_from_GPS_not_moving_and_sensor_moving() {
|
||||
// given
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||
TrackPoint tp4 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(4000));
|
||||
tp4.setSensorDistance(5f);
|
||||
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||
tp5.setSensorDistance(10f);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(0, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp4, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp5, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(15, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_not_moving() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_moving() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_not_moving_and_sensor_speed() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_moving_and_sensor_speed() {
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
// then
|
||||
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
||||
assertNull(sensor.getSpeed());
|
||||
assertNull(sensor.getDistanceSpeed());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -60,7 +60,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
// then
|
||||
assertNull(sensor.getCadence());
|
||||
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
||||
assertEquals(225, sensor.getDistanceSpeed().getWheelRevolutionsCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,7 +73,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
// then
|
||||
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
||||
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
||||
assertEquals(225, sensor.getDistanceSpeed().getWheelRevolutionsCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -57,6 +57,7 @@ public class TrackPoint {
|
||||
private Double altitude_m;
|
||||
private Float speed_mps;
|
||||
private Float bearing;
|
||||
private Float sensorDistance_m;
|
||||
|
||||
public enum Type {
|
||||
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume)
|
||||
@@ -224,7 +225,7 @@ public class TrackPoint {
|
||||
return elevationGain != null;
|
||||
}
|
||||
|
||||
public Float getElevationGain() {
|
||||
public float getElevationGain() {
|
||||
return elevationGain;
|
||||
}
|
||||
|
||||
@@ -310,8 +311,12 @@ public class TrackPoint {
|
||||
this.accuracy = horizontalAccuracy;
|
||||
}
|
||||
|
||||
public float distanceTo(@NonNull TrackPoint dest) {
|
||||
return getLocation().distanceTo(dest.getLocation());
|
||||
public float distanceToPrevious(@NonNull TrackPoint previous) {
|
||||
if (hasSensorDistance()) {
|
||||
return getSensorDistance();
|
||||
}
|
||||
|
||||
return getLocation().distanceTo(previous.getLocation());
|
||||
}
|
||||
|
||||
public boolean fulfillsAccuracy(int poorAccuracy) {
|
||||
@@ -327,6 +332,18 @@ public class TrackPoint {
|
||||
}
|
||||
|
||||
// Sensor data
|
||||
public boolean hasSensorDistance() {
|
||||
return sensorDistance_m != null;
|
||||
}
|
||||
|
||||
public Float getSensorDistance() {
|
||||
return sensorDistance_m;
|
||||
}
|
||||
|
||||
public void setSensorDistance(Float distance_m) {
|
||||
this.sensorDistance_m = distance_m;
|
||||
}
|
||||
|
||||
public boolean hasSensorData() {
|
||||
return hasHeartRate() || hasCyclingCadence() || hasPower();
|
||||
}
|
||||
@@ -347,7 +364,7 @@ public class TrackPoint {
|
||||
return cyclingCadence_rpm != null;
|
||||
}
|
||||
|
||||
public Float getCyclingCadence_rpm() {
|
||||
public float getCyclingCadence_rpm() {
|
||||
return cyclingCadence_rpm;
|
||||
}
|
||||
|
||||
@@ -359,7 +376,7 @@ public class TrackPoint {
|
||||
return power != null;
|
||||
}
|
||||
|
||||
public Float getPower() {
|
||||
public float getPower() {
|
||||
return power;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
String BEARING = "bearing";
|
||||
String SENSOR_HEARTRATE = "sensor_heartrate";
|
||||
String SENSOR_CADENCE = "sensor_cadence";
|
||||
String SENSOR_DISTANCE = "sensor_distance"; //DISTANCE from previous TrackPoint
|
||||
String SENSOR_POWER = "sensor_power";
|
||||
String ELEVATION_GAIN = "elevation_gain";
|
||||
String ELEVATION_LOSS = "elevation_loss";
|
||||
@@ -77,6 +78,7 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
+ ELEVATION_GAIN + " FLOAT, "
|
||||
+ ELEVATION_LOSS + " FLOAT, "
|
||||
+ TYPE + " TEXT CHECK(type IN (-2, -1, 0, 1)), "
|
||||
+ SENSOR_DISTANCE + " FLOAT, "
|
||||
+ "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ class CachedTrackPointsIndexes {
|
||||
final int bearingIndex;
|
||||
final int sensorHeartRateIndex;
|
||||
final int sensorCadenceIndex;
|
||||
final int sensorDistanceIndex;
|
||||
final int sensorPowerIndex;
|
||||
final int elevationGainIndex;
|
||||
final int elevationLossIndex;
|
||||
@@ -35,6 +36,7 @@ class CachedTrackPointsIndexes {
|
||||
bearingIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
||||
sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE);
|
||||
sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE);
|
||||
sensorDistanceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_DISTANCE);
|
||||
sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER);
|
||||
elevationGainIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ELEVATION_GAIN);
|
||||
elevationLossIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ELEVATION_LOSS);
|
||||
|
||||
@@ -556,6 +556,9 @@ public class ContentProviderUtils {
|
||||
if (!cursor.isNull(indexes.sensorCadenceIndex)) {
|
||||
trackPoint.setCyclingCadence_rpm(cursor.getFloat(indexes.sensorCadenceIndex));
|
||||
}
|
||||
if (!cursor.isNull(indexes.sensorDistanceIndex)) {
|
||||
trackPoint.setSensorDistance(cursor.getFloat(indexes.sensorDistanceIndex));
|
||||
}
|
||||
if (!cursor.isNull(indexes.sensorPowerIndex)) {
|
||||
trackPoint.setPower(cursor.getFloat(indexes.sensorPowerIndex));
|
||||
}
|
||||
@@ -708,6 +711,9 @@ public class ContentProviderUtils {
|
||||
if (trackPoint.hasCyclingCadence()) {
|
||||
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCyclingCadence_rpm());
|
||||
}
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
values.put(TrackPointsColumns.SENSOR_DISTANCE, trackPoint.getSensorDistance());
|
||||
}
|
||||
if (trackPoint.hasPower()) {
|
||||
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower());
|
||||
}
|
||||
|
||||
+35
-2
@@ -25,7 +25,7 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
|
||||
|
||||
private static final int DATABASE_VERSION = 30;
|
||||
private static final int DATABASE_VERSION = 31;
|
||||
|
||||
@VisibleForTesting
|
||||
public static final String DATABASE_NAME = "database.db";
|
||||
@@ -82,6 +82,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
case 30:
|
||||
upgradeFrom29to30(db);
|
||||
break;
|
||||
case 31:
|
||||
upgradeFrom30to31(db);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||
@@ -115,7 +118,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
case 29:
|
||||
downgradeFrom30to29(db);
|
||||
break;
|
||||
|
||||
case 30:
|
||||
downgradeFrom31to30(db);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||
}
|
||||
@@ -394,4 +399,32 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add distance column to TrackPoint.
|
||||
*/
|
||||
private void upgradeFrom30to31(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
// TrackPoints
|
||||
db.execSQL("ALTER TABLE trackpoints ADD COLUMN sensor_distance FLOAT");
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
private void downgradeFrom31to30(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
// TrackPoints; identical to upgradeFrom27to28()
|
||||
db.execSQL("ALTER TABLE trackpoints RENAME TO trackpoints_old");
|
||||
db.execSQL("CREATE TABLE trackpoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackid INTEGER NOT NULL, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, speed FLOAT, bearing FLOAT, sensor_heartrate FLOAT, sensor_cadence FLOAT, sensor_power FLOAT, elevation_gain FLOAT, elevation_loss FLOAT, type TEXT CHECK(type IN (-2, -1, 0, 1)), FOREIGN KEY (trackid) REFERENCES tracks(_id) ON UPDATE CASCADE ON DELETE CASCADE)");
|
||||
db.execSQL("INSERT INTO trackpoints SELECT _id, trackid, longitude, latitude, time, elevation, accuracy, speed, bearing, sensor_heartrate, sensor_cadence, sensor_power, elevation_gain, elevation_gain, type FROM trackpoints_old");
|
||||
db.execSQL("DROP TABLE trackpoints_old");
|
||||
|
||||
db.execSQL("CREATE INDEX trackpoints_trackid_index ON trackpoints(trackid)");
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,12 @@ public abstract class SensorData<T> {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset long term aggregated values (more than derived from previous SensorData). e.g. overall distance.
|
||||
*/
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the data recent considering the current time.
|
||||
*/
|
||||
@@ -61,4 +67,10 @@ public abstract class SensorData<T> {
|
||||
return Instant.now()
|
||||
.isBefore(time.plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE_MS));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "sensorAddress='" + sensorAddress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public final class SensorDataCycling {
|
||||
/**
|
||||
* Workaround for Wahoo CADENCE: provides speed instead of cadence
|
||||
*/
|
||||
public Cadence(@NonNull SensorDataCycling.Speed speed) {
|
||||
public Cadence(@NonNull DistanceSpeed speed) {
|
||||
this(speed.getSensorAddress(), speed.getSensorName(), speed.wheelRevolutionsCount, speed.wheelRevolutionsTime);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public final class SensorDataCycling {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount;
|
||||
return super.toString() + " cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,18 +90,18 @@ public final class SensorDataCycling {
|
||||
}
|
||||
}
|
||||
|
||||
public static class Speed extends SensorData<Float> {
|
||||
public static class DistanceSpeed extends SensorData<DistanceSpeed.Data> {
|
||||
|
||||
private final Integer wheelRevolutionsCount; // UINT16
|
||||
private final Integer wheelRevolutionsTime; // UINT16; 1/1024s
|
||||
|
||||
public Speed(String sensorAddress) {
|
||||
public DistanceSpeed(String sensorAddress) {
|
||||
super(sensorAddress);
|
||||
this.wheelRevolutionsCount = null;
|
||||
this.wheelRevolutionsTime = null;
|
||||
}
|
||||
|
||||
public Speed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
|
||||
public DistanceSpeed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
|
||||
super(sensorAddress, sensorName);
|
||||
this.wheelRevolutionsCount = wheelRevolutionsCount;
|
||||
this.wheelRevolutionsTime = wheelRevolutionsTime;
|
||||
@@ -119,7 +119,7 @@ public final class SensorDataCycling {
|
||||
return wheelRevolutionsTime;
|
||||
}
|
||||
|
||||
public void compute(Speed previous, int wheel_circumference_mm) {
|
||||
public void compute(DistanceSpeed previous, int wheel_circumference_mm) {
|
||||
if (hasData() && previous != null && previous.hasData()) {
|
||||
float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
|
||||
if (timeDiff_ms <= 0) {
|
||||
@@ -128,42 +128,76 @@ public final class SensorDataCycling {
|
||||
} else {
|
||||
long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT16_MAX);
|
||||
double timeDiff_s = timeDiff_ms * UnitConversions.MS_TO_S;
|
||||
value = (float) (wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M / timeDiff_s);
|
||||
float distance_m = (float) (wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M);
|
||||
float distance_overall_m = distance_m;
|
||||
if (previous.hasValue()) {
|
||||
distance_overall_m += previous.getValue().distance_overall_m;
|
||||
}
|
||||
float speed_mps = (float) (distance_m / timeDiff_s);
|
||||
value = new Data(distance_m, distance_overall_m, speed_mps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (value != null) {
|
||||
value = new Data(value.distance_m, 0, value.speed_mps);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "speed=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount;
|
||||
return super.toString() + " data=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof Speed)) return false;
|
||||
if (!(obj instanceof DistanceSpeed)) return false;
|
||||
|
||||
Speed comp = (Speed) obj;
|
||||
DistanceSpeed comp = (DistanceSpeed) obj;
|
||||
if (hasData() && comp.hasData() == hasData()) {
|
||||
return getWheelRevolutionsCount() == comp.getWheelRevolutionsCount() && getWheelRevolutionsTime() == comp.getWheelRevolutionsTime();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Data {
|
||||
public final float distance_m;
|
||||
public final float distance_overall_m;
|
||||
public final float speed_mps;
|
||||
|
||||
private Data(float distance_m, float distance_overall_m, float speed_mps) {
|
||||
this.distance_m = distance_m;
|
||||
this.distance_overall_m = distance_overall_m;
|
||||
this.speed_mps = speed_mps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Data{" +
|
||||
"distance_m=" + distance_m +
|
||||
", distance_overall_m=" + distance_overall_m +
|
||||
", speed_mps=" + speed_mps +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CadenceAndSpeed extends SensorData<Pair<Cadence, Speed>> {
|
||||
public static class CadenceAndSpeed extends SensorData<Pair<Cadence, DistanceSpeed>> {
|
||||
|
||||
public CadenceAndSpeed(String sensorAddress, String sensorName, @NonNull Cadence cadence, @NonNull Speed speed) {
|
||||
public CadenceAndSpeed(String sensorAddress, String sensorName, @Nullable Cadence cadence, @Nullable DistanceSpeed distanceSpeed) {
|
||||
super(sensorAddress, sensorName);
|
||||
this.value = new Pair<>(cadence, speed);
|
||||
this.value = new Pair<>(cadence, distanceSpeed);
|
||||
}
|
||||
|
||||
public Cadence getCadence() {
|
||||
return this.value != null ? this.value.first : null;
|
||||
}
|
||||
|
||||
public Speed getSpeed() {
|
||||
public DistanceSpeed getDistanceSpeed() {
|
||||
return this.value != null ? this.value.second : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ public class SensorDataCyclingPower extends SensorData<Float> {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "power=" + value;
|
||||
return super.toString() + " power=" + value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ public class SensorDataHeartRate extends SensorData<Float> {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "heart=" + value;
|
||||
return super.toString() + " heart=" + value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public final class SensorDataSet {
|
||||
|
||||
private SensorDataCycling.Cadence cyclingCadence;
|
||||
|
||||
private SensorDataCycling.Speed cyclingSpeed;
|
||||
private SensorDataCycling.DistanceSpeed cyclingDistanceSpeed;
|
||||
|
||||
private SensorDataCyclingPower cyclingPower;
|
||||
|
||||
@@ -25,8 +25,8 @@ public final class SensorDataSet {
|
||||
return cyclingCadence;
|
||||
}
|
||||
|
||||
public SensorDataCycling.Speed getCyclingSpeed() {
|
||||
return cyclingSpeed;
|
||||
public SensorDataCycling.DistanceSpeed getCyclingDistanceSpeed() {
|
||||
return cyclingDistanceSpeed;
|
||||
}
|
||||
|
||||
public SensorDataCyclingPower getCyclingPower() {
|
||||
@@ -44,7 +44,7 @@ public final class SensorDataSet {
|
||||
public void clear() {
|
||||
this.heartRate = null;
|
||||
this.cyclingCadence = null;
|
||||
this.cyclingSpeed = null;
|
||||
this.cyclingDistanceSpeed = null;
|
||||
this.cyclingPower = null;
|
||||
}
|
||||
|
||||
@@ -57,8 +57,9 @@ public final class SensorDataSet {
|
||||
trackPoint.setCyclingCadence_rpm(cyclingCadence.getValue());
|
||||
}
|
||||
|
||||
if (cyclingSpeed != null && cyclingSpeed.hasValue()) {
|
||||
trackPoint.setSpeed(cyclingSpeed.getValue());
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
|
||||
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().distance_overall_m);
|
||||
trackPoint.setSpeed(cyclingDistanceSpeed.getValue().speed_mps);
|
||||
}
|
||||
|
||||
if (cyclingPower != null && cyclingPower.hasValue()) {
|
||||
@@ -66,12 +67,19 @@ public final class SensorDataSet {
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
if (heartRate != null) heartRate.reset();
|
||||
if (cyclingCadence != null) cyclingCadence.reset();
|
||||
if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.reset();
|
||||
if (cyclingPower != null) cyclingPower.reset();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return (getHeartRate() != null ? "" + getHeartRate() : "")
|
||||
+ (getCyclingCadence() != null ? " " + getCyclingCadence() : "")
|
||||
+ (getCyclingSpeed() != null ? " " + getCyclingSpeed() : "")
|
||||
+ (getCyclingDistanceSpeed() != null ? " " + getCyclingDistanceSpeed() : "")
|
||||
+ (getCyclingPower() != null ? " " + getCyclingPower() : "");
|
||||
}
|
||||
|
||||
@@ -85,8 +93,8 @@ public final class SensorDataSet {
|
||||
this.cyclingCadence = (SensorDataCycling.Cadence) data;
|
||||
return;
|
||||
}
|
||||
if (type instanceof SensorDataCycling.Speed) {
|
||||
this.cyclingSpeed = (SensorDataCycling.Speed) data;
|
||||
if (type instanceof SensorDataCycling.DistanceSpeed) {
|
||||
this.cyclingDistanceSpeed = (SensorDataCycling.DistanceSpeed) data;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
|
||||
private TrackPoint lastTrackPoint;
|
||||
|
||||
//TODO Should not be nullable
|
||||
private TrackStatistics lastTrackStatistics;
|
||||
|
||||
private String category = "";
|
||||
@@ -366,29 +367,26 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
}
|
||||
|
||||
private void setSpeedSensorData(SensorDataSet sensorDataSet) {
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingSpeed() != null) {
|
||||
SensorDataCycling.Speed data = sensorDataSet.getCyclingSpeed();
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingDistanceSpeed() != null) {
|
||||
SensorDataCycling.DistanceSpeed data = sensorDataSet.getCyclingDistanceSpeed();
|
||||
|
||||
if (data.hasValue() && data.isRecent()) {
|
||||
setSpeed(data.getValue());
|
||||
setTotalDistance(data.getValue().distance_overall_m);
|
||||
setSpeed(data.getValue().speed_mps);
|
||||
}
|
||||
if (data.hasValue() && data.isRecent()) {
|
||||
setSpeed(data.getValue().speed_mps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStats() {
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
|
||||
|
||||
// Set total distance
|
||||
{
|
||||
double totalDistance = lastTrackStatistics == null ? Double.NaN : lastTrackStatistics.getTotalDistance();
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
||||
|
||||
viewBinding.statsDistanceValue.setText(parts.first);
|
||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||
}
|
||||
setTotalDistance(0);
|
||||
|
||||
// Set activity type
|
||||
{
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
|
||||
viewBinding.statsActivityTypeIcon.setEnabled(isSelectedTrackRecording());
|
||||
viewBinding.statsActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(trackIconValue));
|
||||
}
|
||||
@@ -479,6 +477,14 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
}
|
||||
}
|
||||
|
||||
private void setTotalDistance(double sensorDistanceSinceLastTrackpoint) {
|
||||
double totalDistance = lastTrackStatistics != null ? (lastTrackStatistics.getTotalDistance() + sensorDistanceSinceLastTrackpoint) : Double.NaN;
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
||||
|
||||
viewBinding.statsDistanceValue.setText(parts.first);
|
||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
private void setSpeed(double speed) {
|
||||
viewBinding.statsSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat SPEED_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat DISTANCE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat HEARTRATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat CADENCE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat POWER_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
@@ -312,6 +313,10 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
printWriter.println("<opentracks:loss>" + ELEVATION_FORMAT.format(trackPoint.getElevationLoss()) + "</opentracks:loss>");
|
||||
}
|
||||
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
printWriter.println("<opentracks:distance>" + DISTANCE_FORMAT.format(trackPoint.getElevationLoss()) + "</opentracks:distance>");
|
||||
}
|
||||
|
||||
printWriter.println("</gpxtpx:TrackPointExtension></extensions>");
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
private static final String SCHEMA_ID = "schema";
|
||||
|
||||
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
|
||||
public static final String EXTENDED_DATA_TYPE_DISTANCE = "distance";
|
||||
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";
|
||||
@@ -74,6 +75,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
|
||||
private PrintWriter printWriter;
|
||||
private final List<Float> speedList = new ArrayList<>();
|
||||
private final List<Float> distanceList = new ArrayList<>();
|
||||
private final List<Float> powerList = new ArrayList<>();
|
||||
private final List<Float> cadenceList = new ArrayList<>();
|
||||
private final List<Float> heartRateList = new ArrayList<>();
|
||||
@@ -338,6 +340,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<gx:Track>");
|
||||
speedList.clear();
|
||||
distanceList.clear();
|
||||
powerList.clear();
|
||||
cadenceList.clear();
|
||||
heartRateList.clear();
|
||||
@@ -355,6 +358,9 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED);
|
||||
}
|
||||
if (exportSensorData) {
|
||||
if (distanceList.stream().anyMatch(Objects::nonNull)) {
|
||||
writeSimpleArrayData(distanceList, EXTENDED_DATA_TYPE_DISTANCE);
|
||||
}
|
||||
if (powerList.stream().anyMatch(Objects::nonNull)) {
|
||||
writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER);
|
||||
}
|
||||
@@ -392,6 +398,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
speedList.add(trackPoint.hasSpeed() ? trackPoint.getSpeed() : null);
|
||||
|
||||
if (exportSensorData) {
|
||||
distanceList.add(trackPoint.hasSensorDistance() ? trackPoint.getSensorDistance() : null);
|
||||
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate_bpm() : null);
|
||||
cadenceList.add(trackPoint.hasCyclingCadence() ? trackPoint.getCyclingCadence_rpm() : null);
|
||||
powerList.add(trackPoint.hasPower() ? trackPoint.getPower() : null);
|
||||
|
||||
+10
-5
@@ -91,6 +91,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
protected String uuid;
|
||||
protected String gain;
|
||||
protected String loss;
|
||||
protected String distance;
|
||||
|
||||
// The current track data
|
||||
private TrackData trackData;
|
||||
@@ -141,9 +142,6 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On file end.
|
||||
*/
|
||||
protected void onFileEnd() {
|
||||
// Add markers to the last imported track
|
||||
int size = trackIds.size();
|
||||
@@ -376,7 +374,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
* GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
|
||||
*/
|
||||
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
||||
float speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / timeDifference.toMillis();
|
||||
float speed = trackData.lastLocationInCurrentSegment.distanceToPrevious(trackPoint) / timeDifference.toMillis();
|
||||
trackPoint.setSpeed(speed);
|
||||
}
|
||||
}
|
||||
@@ -385,7 +383,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
trackPoint.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(trackPoint));
|
||||
|
||||
long maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, context); //TODO Should only be read once!
|
||||
double distanceToLastTrackLocation = trackPoint.distanceTo(trackData.lastLocationInCurrentSegment);
|
||||
double distanceToLastTrackLocation = trackPoint.distanceToPrevious(trackData.lastLocationInCurrentSegment);
|
||||
if (distanceToLastTrackLocation > maxRecordingDistance) {
|
||||
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
}
|
||||
@@ -505,6 +503,13 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse elevation loss: %s", loss)), e);
|
||||
}
|
||||
}
|
||||
if (distance != null) {
|
||||
try {
|
||||
trackPoint.setSensorDistance(Float.parseFloat(distance));
|
||||
} catch (Exception e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse distance: %s", distance)), e);
|
||||
}
|
||||
}
|
||||
|
||||
return trackPoint;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
|
||||
private static final String TAG_EXTENSION_GAIN = "opentracks:gain";
|
||||
private static final String TAG_EXTENSION_LOSS = "opentracks:loss";
|
||||
private static final String TAG_EXTENSION_DISTANCE = "opentracks:distance";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -179,6 +180,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
loss = content.trim();
|
||||
}
|
||||
break;
|
||||
case TAG_EXTENSION_DISTANCE:
|
||||
if (content != null) {
|
||||
distance = content.trim();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset element content
|
||||
|
||||
@@ -66,6 +66,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
private String extendedDataType;
|
||||
private final ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
private final ArrayList<Float> speedList = new ArrayList<>();
|
||||
private final ArrayList<Float> distanceList = new ArrayList<>();
|
||||
private final ArrayList<Float> cadenceList = new ArrayList<>();
|
||||
private final ArrayList<Float> heartRateList = new ArrayList<>();
|
||||
private final ArrayList<Float> powerList = new ArrayList<>();
|
||||
@@ -216,6 +217,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
super.onTrackSegmentStart();
|
||||
trackPoints.clear();
|
||||
speedList.clear();
|
||||
distanceList.clear();
|
||||
heartRateList.clear();
|
||||
cadenceList.clear();
|
||||
powerList.clear();
|
||||
@@ -232,6 +234,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
if (i < speedList.size()) {
|
||||
trackPoint.setSpeed(speedList.get(i));
|
||||
}
|
||||
if (i < distanceList.size()) {
|
||||
trackPoint.setSensorDistance(distanceList.get(i));
|
||||
}
|
||||
if (i < heartRateList.size()) {
|
||||
trackPoint.setHeartRate_bpm(heartRateList.get(i));
|
||||
}
|
||||
@@ -310,6 +315,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
||||
speedList.add(value);
|
||||
break;
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE:
|
||||
distanceList.add(value);
|
||||
break;
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
||||
powerList.add(value);
|
||||
break;
|
||||
|
||||
@@ -571,7 +571,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
return;
|
||||
}
|
||||
|
||||
double distanceToLastTrackLocation = trackPoint.distanceTo(lastValidTrackPoint);
|
||||
double distanceToLastTrackLocation = trackPoint.distanceToPrevious(lastValidTrackPoint);
|
||||
if (distanceToLastTrackLocation > maxRecordingDistance) {
|
||||
insertTrackPointIfNewer(track, lastTrackPoint);
|
||||
|
||||
@@ -664,6 +664,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
trackPoint.setElevationLoss(elevationSumManager.getElevationLoss_m());
|
||||
elevationSumManager.reset();
|
||||
}
|
||||
if (remoteSensorManager != null) {
|
||||
fillWithSensorDataSet(trackPoint);
|
||||
remoteSensorManager.reset();
|
||||
}
|
||||
contentProviderUtils.insertTrackPoint(trackPoint, track.getId());
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
||||
|
||||
|
||||
+19
-19
@@ -101,11 +101,11 @@ public abstract class BluetoothConnectionManager {
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic) {
|
||||
String sensorName = gatt.getDevice().getName();
|
||||
Log.d(TAG, "Received data from " + sensorName);
|
||||
Log.d(TAG, "Received data from " + gatt.getDevice().getAddress());
|
||||
|
||||
SensorData sensorData = parsePayload(sensorName, gatt.getDevice().getAddress(), characteristic);
|
||||
SensorData<?> sensorData = parsePayload(gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic);
|
||||
if (sensorData != null) {
|
||||
Log.d(TAG, "Decoded data from " + gatt.getDevice().getAddress() + ": " + sensorData);
|
||||
observer.onChanged(sensorData);
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public abstract class BluetoothConnectionManager {
|
||||
Log.d(TAG, "Connecting to: " + device);
|
||||
|
||||
bluetoothGatt = device.connectGatt(context, true, connectCallback);
|
||||
SensorData sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress());
|
||||
SensorData<?> sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress());
|
||||
observer.onChanged(sensorData);
|
||||
}
|
||||
|
||||
@@ -148,12 +148,12 @@ public abstract class BluetoothConnectionManager {
|
||||
return address.equals(bluetoothGatt.getDevice().getAddress());
|
||||
}
|
||||
|
||||
protected abstract SensorData createEmptySensorData(String address);
|
||||
protected abstract SensorData<?> createEmptySensorData(String address);
|
||||
|
||||
/**
|
||||
* @return null if data could not be parsed.
|
||||
*/
|
||||
protected abstract SensorData parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic);
|
||||
protected abstract SensorData<?> parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic);
|
||||
|
||||
public static class HeartRate extends BluetoothConnectionManager {
|
||||
|
||||
@@ -162,7 +162,7 @@ public abstract class BluetoothConnectionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorData createEmptySensorData(String address) {
|
||||
protected SensorDataHeartRate createEmptySensorData(String address) {
|
||||
return new SensorDataHeartRate(address);
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public abstract class BluetoothConnectionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorData createEmptySensorData(String address) {
|
||||
protected SensorDataCycling.Cadence createEmptySensorData(String address) {
|
||||
return new SensorDataCycling.Cadence(address);
|
||||
}
|
||||
|
||||
@@ -197,30 +197,30 @@ public abstract class BluetoothConnectionManager {
|
||||
}
|
||||
|
||||
//Workaround for Wahoo CADENCE: this sensor reports speed (instead of cadence)
|
||||
if (cadenceAndSpeed.getSpeed() != null) {
|
||||
return new SensorDataCycling.Cadence(cadenceAndSpeed.getSpeed());
|
||||
if (cadenceAndSpeed.getDistanceSpeed() != null) {
|
||||
return new SensorDataCycling.Cadence(cadenceAndSpeed.getDistanceSpeed());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CyclingSpeed extends BluetoothConnectionManager {
|
||||
public static class CyclingDistanceSpeed extends BluetoothConnectionManager {
|
||||
|
||||
CyclingSpeed(SensorDataObserver observer) {
|
||||
CyclingDistanceSpeed(SensorDataObserver observer) {
|
||||
super(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorData createEmptySensorData(String address) {
|
||||
return new SensorDataCycling.Speed(address);
|
||||
protected SensorDataCycling.DistanceSpeed createEmptySensorData(String address) {
|
||||
return new SensorDataCycling.DistanceSpeed(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataCycling.Speed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
protected SensorDataCycling.DistanceSpeed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
||||
if (cadenceAndSpeed != null) {
|
||||
return cadenceAndSpeed.getSpeed();
|
||||
return cadenceAndSpeed.getDistanceSpeed();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -233,7 +233,7 @@ public abstract class BluetoothConnectionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorData createEmptySensorData(String address) {
|
||||
protected SensorDataCyclingPower createEmptySensorData(String address) {
|
||||
return new SensorDataCyclingPower(address);
|
||||
}
|
||||
|
||||
@@ -247,8 +247,8 @@ public abstract class BluetoothConnectionManager {
|
||||
|
||||
interface SensorDataObserver {
|
||||
|
||||
void onChanged(SensorData sensorData);
|
||||
void onChanged(SensorData<?> sensorData);
|
||||
|
||||
void onDisconnecting(SensorData sensorData);
|
||||
void onDisconnecting(SensorData<?> sensorData);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-9
@@ -53,13 +53,15 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
|
||||
private final BluetoothAdapter bluetoothAdapter;
|
||||
private final Context context;
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
private boolean started = false;
|
||||
|
||||
private final SharedPreferences sharedPreferences;
|
||||
private int preferenceWheelCircumference;
|
||||
|
||||
private final BluetoothConnectionManager.HeartRate heartRate = new BluetoothConnectionManager.HeartRate(this);
|
||||
private final BluetoothConnectionManager.CyclingCadence cyclingCadence = new BluetoothConnectionManager.CyclingCadence(this);
|
||||
private final BluetoothConnectionManager.CyclingSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingSpeed(this);
|
||||
private final BluetoothConnectionManager.CyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingDistanceSpeed(this);
|
||||
private final BluetoothConnectionManager.CyclingPower cyclingPower = new BluetoothConnectionManager.CyclingPower(this);
|
||||
|
||||
private final SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
@@ -90,6 +92,11 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
|
||||
connect(cyclingPower, address);
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_power_key, key)) {
|
||||
preferenceWheelCircumference = PreferencesUtils.getWheelCircumference(sharedPreferences, context);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -155,11 +162,15 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
sensorDataSet.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onChanged(SensorData sensorData) {
|
||||
public synchronized void onChanged(SensorData<?> sensorData) {
|
||||
if (sensorData instanceof SensorDataCycling.Cadence) {
|
||||
SensorDataCycling.Cadence previous = sensorDataSet.getCyclingCadence();
|
||||
Log.d(TAG, "previous " + previous + "; current" + sensorData);
|
||||
Log.d(TAG, "Previous: " + previous + "; current: " + sensorData);
|
||||
|
||||
if (sensorData.equals(previous)) {
|
||||
Log.d(TAG, "onChanged: cadence data repeated.");
|
||||
@@ -167,21 +178,21 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
}
|
||||
((SensorDataCycling.Cadence) sensorData).compute(previous);
|
||||
}
|
||||
if (sensorData instanceof SensorDataCycling.Speed) {
|
||||
SensorDataCycling.Speed previous = sensorDataSet.getCyclingSpeed();
|
||||
Log.d(TAG, "previous " + previous + "; current" + sensorData);
|
||||
if (sensorData instanceof SensorDataCycling.DistanceSpeed) {
|
||||
SensorDataCycling.DistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed();
|
||||
Log.d(TAG, "Previous: " + previous + "; Current" + sensorData);
|
||||
if (sensorData.equals(previous)) {
|
||||
Log.d(TAG, "onChanged: speed data repeated.");
|
||||
return;
|
||||
}
|
||||
((SensorDataCycling.Speed) sensorData).compute(previous, PreferencesUtils.getWheelCircumference(sharedPreferences, context));
|
||||
((SensorDataCycling.DistanceSpeed) sensorData).compute(previous, PreferencesUtils.getWheelCircumference(sharedPreferences, context));
|
||||
}
|
||||
|
||||
sensorDataSet.set(sensorData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnecting(SensorData sensorData) {
|
||||
public void onDisconnecting(SensorData<?> sensorData) {
|
||||
sensorDataSet.remove(sensorData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,10 +103,15 @@ public class TrackStatisticsUpdater {
|
||||
/**
|
||||
* Adds a trackPoint.
|
||||
*
|
||||
* @param trackPoint the trackPoint
|
||||
* @param minRecordingDistance the min recording distance
|
||||
* @param trackPoint the trackPoint
|
||||
* @param minGPSDistance the min recording distance
|
||||
*/
|
||||
public void addTrackPoint(TrackPoint trackPoint, int minRecordingDistance) {
|
||||
public void addTrackPoint(TrackPoint trackPoint, int minGPSDistance) {
|
||||
internalAddTrackPoint(trackPoint, minGPSDistance);
|
||||
Log.d(TAG, this.toString());
|
||||
}
|
||||
|
||||
private void internalAddTrackPoint(TrackPoint trackPoint, int minGPSDistance) {
|
||||
if (!trackInitialized) {
|
||||
trackStatistics.setStartTime(trackPoint.getTime());
|
||||
trackInitialized = true;
|
||||
@@ -120,19 +125,26 @@ public class TrackStatisticsUpdater {
|
||||
currentSegment.setStopTime(trackPoint.getTime());
|
||||
currentSegment.setTotalTime(Duration.between(currentSegment.getStartTime(), trackPoint.getTime()));
|
||||
|
||||
if (trackPoint.isSegmentStart() || trackPoint.isSegmentEnd()) {
|
||||
if (trackPoint.isSegmentEnd()) {
|
||||
if (lastTrackPoint != null && lastMovingTrackPoint != null && lastTrackPoint != lastMovingTrackPoint) {
|
||||
currentSegment.addTotalDistance(lastMovingTrackPoint.distanceTo(lastTrackPoint));
|
||||
}
|
||||
}
|
||||
trackStatistics.merge(currentSegment);
|
||||
currentSegment.reset(trackPoint.getTime());
|
||||
if (trackPoint.isSegmentStart()) {
|
||||
reset(trackPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
lastTrackPoint = null;
|
||||
lastMovingTrackPoint = null;
|
||||
elevationBuffer_m.reset();
|
||||
speedBuffer_ms.reset();
|
||||
// Process sensor data
|
||||
if (trackPoint.hasElevationGain()) {
|
||||
currentSegment.addTotalElevationGain(trackPoint.getElevationGain());
|
||||
}
|
||||
|
||||
if (trackPoint.hasElevationLoss()) {
|
||||
currentSegment.addTotalElevationLoss(trackPoint.getElevationLoss());
|
||||
}
|
||||
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
currentSegment.addTotalDistance(trackPoint.getSensorDistance());
|
||||
}
|
||||
|
||||
if (trackPoint.isSegmentEnd()) {
|
||||
reset(trackPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,39 +153,30 @@ public class TrackStatisticsUpdater {
|
||||
updateAbsoluteElevation(trackPoint.getAltitude());
|
||||
}
|
||||
|
||||
//Get elevation gain
|
||||
if (trackPoint.hasElevationGain()) {
|
||||
currentSegment.addTotalElevationGain(trackPoint.getElevationGain());
|
||||
Log.d(TAG, "elevation gain: " + trackPoint.getElevationGain());
|
||||
}
|
||||
|
||||
//Get elevation loss
|
||||
if (trackPoint.hasElevationLoss()) {
|
||||
currentSegment.addTotalElevationLoss(trackPoint.getElevationLoss());
|
||||
Log.d(TAG, "elevation loss: " + trackPoint.getElevationLoss());
|
||||
}
|
||||
|
||||
if (lastTrackPoint == null || lastMovingTrackPoint == null) {
|
||||
lastTrackPoint = trackPoint;
|
||||
lastMovingTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
|
||||
double movingDistance = lastMovingTrackPoint.distanceTo(trackPoint);
|
||||
if (movingDistance < minRecordingDistance && !trackPoint.isMoving()) {
|
||||
speedBuffer_ms.reset();
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
if (!trackPoint.hasSensorDistance()) {
|
||||
// GPS-based distance/speed
|
||||
float movingDistance = lastMovingTrackPoint.distanceToPrevious(trackPoint);
|
||||
if (movingDistance < minGPSDistance && !trackPoint.isMoving()) {
|
||||
speedBuffer_ms.reset();
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
// Update total distance
|
||||
currentSegment.addTotalDistance(movingDistance);
|
||||
}
|
||||
|
||||
Duration movingTime = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
|
||||
if (movingTime.isNegative()) {
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update total distance
|
||||
currentSegment.addTotalDistance(movingDistance);
|
||||
|
||||
// Update moving time
|
||||
currentSegment.addMovingTime(movingTime);
|
||||
|
||||
@@ -186,6 +189,16 @@ public class TrackStatisticsUpdater {
|
||||
lastMovingTrackPoint = trackPoint;
|
||||
}
|
||||
|
||||
private void reset(TrackPoint trackPoint) {
|
||||
trackStatistics.merge(currentSegment);
|
||||
currentSegment.reset(trackPoint.getTime());
|
||||
|
||||
lastTrackPoint = null;
|
||||
lastMovingTrackPoint = null;
|
||||
elevationBuffer_m.reset();
|
||||
speedBuffer_ms.reset();
|
||||
}
|
||||
|
||||
public void addTrackPoint(TrackPointIterator iterator, int minRecordingDistance) {
|
||||
while (iterator.hasNext()) {
|
||||
TrackPoint location = iterator.next();
|
||||
@@ -224,20 +237,14 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
/**
|
||||
* Updates an elevation reading.
|
||||
*
|
||||
* @param elevation the elevation
|
||||
* @return the difference
|
||||
*/
|
||||
@VisibleForTesting
|
||||
private double updateAbsoluteElevation(double elevation) {
|
||||
private void 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);
|
||||
|
||||
return newAverage - oldAverage;
|
||||
}
|
||||
|
||||
private boolean isValidSpeed(@NonNull TrackPoint trackPoint, @NonNull TrackPoint lastTrackPoint) {
|
||||
@@ -267,4 +274,11 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TrackStatisticsUpdater{" +
|
||||
"trackStatistics=" + trackStatistics +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,12 +126,12 @@ public class BluetoothUtils {
|
||||
cadence = new SensorDataCycling.Cadence(address, sensorName, crankCount, crankTime);
|
||||
}
|
||||
|
||||
SensorDataCycling.Speed speed = null;
|
||||
SensorDataCycling.DistanceSpeed speed = null;
|
||||
if (hasWheel && valueLength - index >= 4) {
|
||||
int wheelCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index);
|
||||
index += 2;
|
||||
int wheelTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s
|
||||
speed = new SensorDataCycling.Speed(address, sensorName, wheelCount, wheelTime);
|
||||
speed = new SensorDataCycling.DistanceSpeed(address, sensorName, wheelCount, wheelTime);
|
||||
}
|
||||
|
||||
return new SensorDataCycling.CadenceAndSpeed(address, sensorName, cadence, speed);
|
||||
|
||||
@@ -42,6 +42,7 @@ public class PreferencesUtils {
|
||||
private PreferencesUtils() {
|
||||
}
|
||||
|
||||
@Deprecated //Should only be used to get a sharedPreference for more than one interaction!
|
||||
public static SharedPreferences getSharedPreferences(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
@@ -102,7 +103,6 @@ public class PreferencesUtils {
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
|
||||
private static int getInt(SharedPreferences sharedPreferences, Context context, int keyId, int defaultValue) {
|
||||
try {
|
||||
return sharedPreferences.getInt(getKey(context, keyId), defaultValue);
|
||||
@@ -206,7 +206,6 @@ public class PreferencesUtils {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_power_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
|
||||
public static boolean shouldShowStatsOnLockscreen(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean STATS_SHOW_ON_LOCKSCREEN_DEFAULT = context.getResources().getBoolean(R.bool.stats_show_on_lockscreen_while_recording_default);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class IntervalStatistics {
|
||||
TrackPoint trackPoint = trackPoints.get(i);
|
||||
|
||||
if (trackPoint.hasLocation() && prevTrackPoint.hasLocation()) {
|
||||
interval.distance_m += prevTrackPoint.distanceTo(trackPoint);
|
||||
interval.distance_m += prevTrackPoint.distanceToPrevious(trackPoint);
|
||||
interval.time = interval.time.plus(Duration.between(prevTrackPoint.getTime(), trackPoint.getTime()));
|
||||
interval.gain_m += trackPoint.hasElevationGain() ? trackPoint.getElevationGain() : 0;
|
||||
interval.loss_m += trackPoint.hasElevationLoss() ? trackPoint.getElevationLoss() : 0;
|
||||
|
||||
Reference in New Issue
Block a user