Skip to content

Commit 7193171

Browse files
authored
Merge pull request #1033 from OpenTracksApp/indoor#500
Storing data from sensors without GPS
2 parents 6f822af + 7d0d87e commit 7193171

23 files changed

+414
-223
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
7878
* __Photos and Markers:__ mark interesting locations while tracking
7979
* __Export:__
8080
* export tracks either as KMZ (incl. photos), KML, or GPX
81-
* export automatically after each recording (e.g., to sync)
81+
* export automatically after each recording (e.g., to sync via [Nextcloud](https://nextcloud.com/))
8282
* avoid duplication: each exported file contain a random unique identifier (i.e., `opentracks:trackid`)
8383
* __Altitude:__
8484
* gain/loss via barometric sensor (if present)
@@ -89,6 +89,8 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
8989
* cycling: cadence
9090
* cycling: power meter
9191
* running: speed and cadence
92+
* support for BLE sensor training only (i.e., without GPS) for indoor training
93+
9294
An overview of tested sensors: [README_TESTED_SENSORS.md](README_TESTED_SENSORS.md)
9395

9496
### Gadgetbridge integration

src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderUtilsTest.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
*/
1616
package de.dennisguse.opentracks.content.provider;
1717

18+
import static org.junit.Assert.assertEquals;
19+
import static org.junit.Assert.assertFalse;
20+
import static org.junit.Assert.assertNotNull;
21+
import static org.junit.Assert.assertNull;
22+
import static org.junit.Assert.assertTrue;
23+
import static org.mockito.Mockito.when;
24+
1825
import android.content.ContentResolver;
1926
import android.content.ContentUris;
2027
import android.content.ContentValues;
@@ -57,13 +64,6 @@
5764
import de.dennisguse.opentracks.util.FileUtils;
5865
import de.dennisguse.opentracks.util.UUIDUtils;
5966

60-
import static org.junit.Assert.assertEquals;
61-
import static org.junit.Assert.assertFalse;
62-
import static org.junit.Assert.assertNotNull;
63-
import static org.junit.Assert.assertNull;
64-
import static org.junit.Assert.assertTrue;
65-
import static org.mockito.Mockito.when;
66-
6767
/**
6868
* A unit test for {@link ContentProviderUtils}.
6969
*
@@ -220,6 +220,8 @@ public void testDeleteAllTracks() {
220220
assertEquals(0, tracksPointsCursor.getCount());
221221
markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
222222
assertEquals(0, markerCursor.getCount());
223+
224+
//TODO Close all cursors
223225
}
224226

225227
/**

src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java

+86-50
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import de.dennisguse.opentracks.io.file.TrackFileFormat;
5656
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
5757
import de.dennisguse.opentracks.services.TrackRecordingService;
58+
import de.dennisguse.opentracks.services.TrackRecordingServiceTest;
5859
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
5960
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
6061
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
@@ -98,38 +99,51 @@ public static void preSetUp() {
9899
private TrackImporter trackImporter;
99100

100101
@Before
101-
public void fileSetup() throws IOException {
102+
public void fileSetup() throws IOException, TimeoutException {
103+
TrackRecordingServiceTest.resetService(mServiceRule, context);
104+
102105
tmpFile = File.createTempFile("test", "test", context.getFilesDir());
103106
tmpFileUri = Uri.fromFile(tmpFile);
104107

105108
trackImporter = new TrackImporter(context, contentProviderUtils, Distance.of(10), Distance.of(200), true);
109+
110+
TrackRecordingServiceTest.resetService(mServiceRule, context);
106111
}
107112

108113
@After
109-
public void FileTearDown() throws TimeoutException {
114+
public void tearDown() throws TimeoutException {
110115
tmpFile.deleteOnExit();
111116
tmpFileUri = null;
112117

113-
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
114-
.getService();
115-
service.getTrackPointCreator().setClock(Clock.systemUTC());
118+
// Ensure that the database is empty after every test
119+
contentProviderUtils.deleteAllTracks(context);
120+
121+
TrackRecordingServiceTest.resetService(mServiceRule, context);
116122
}
117123

118-
public void setUp(boolean hasSensorDistance) throws TimeoutException {
124+
public void setUp() throws TimeoutException {
119125
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
120126
.getService();
121127

122128
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
123129

124130
trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:02Z"), ZoneId.of("CET")));
125131
trackId = service.startNewTrack();
132+
service.stopUpdateRecordingData();
126133

127-
Distance sensorDistance = hasSensorDistance ? Distance.of(10) : null; // recording distance interval
134+
Distance sensorDistance = Distance.of(10); // recording distance interval
128135

129-
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:03Z"), 3, 14, 10, 15, 10, 1, 66, 3, 50, sensorDistance);
136+
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
137+
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:03Z"), 3, 14, 10, 15, 10, 1);
130138
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
131-
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:04Z"), 3, 14.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
132-
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:05Z"), 3, 14.002, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
139+
140+
// A sensor-only TrackPoint
141+
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
142+
mockAltitudeChange(trackPointCreator, 1);
143+
sendSensor(trackPointCreator, Instant.parse("2020-02-02T02:02:04Z"));
144+
145+
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
146+
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:05Z"), 3, 14.002, 10, 15, 10, 0);
133147
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
134148

135149
trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:06Z"), ZoneId.of("CET")));
@@ -138,12 +152,17 @@ public void setUp(boolean hasSensorDistance) throws TimeoutException {
138152

139153
trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:20Z"), ZoneId.of("CET")));
140154
service.resumeCurrentTrack();
155+
service.stopUpdateRecordingData();
141156

142157
trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:21Z"), ZoneId.of("CET")));
143-
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:21Z"), 3, 14.003, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
158+
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
159+
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:21Z"), 3, 14.003, 10, 15, 10, 0);
160+
161+
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
162+
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:22Z"), 3, 16, 10, 15, 10, 0);
144163

145-
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:22Z"), 3, 16, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
146-
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:23Z"), 3, 16.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
164+
mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
165+
sendLocation(trackPointCreator, Instant.parse("2020-02-02T02:02:23Z"), 3, 16.001, 10, 15, 10, 0);
147166

148167
trackPointCreator.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:24Z"), ZoneId.of("CET")));
149168
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context));
@@ -162,21 +181,11 @@ public void setUp(boolean hasSensorDistance) throws TimeoutException {
162181
assertEquals(2, markers.size());
163182
}
164183

165-
@After
166-
public void tearDown() {
167-
if (trackId != null) {
168-
contentProviderUtils.deleteTrack(context, trackId);
169-
}
170-
if (importTrackId != null) {
171-
contentProviderUtils.deleteTrack(context, importTrackId);
172-
}
173-
}
174-
175184
//TODO Does not test images
176185
@LargeTest
177186
@Test
178187
public void kmz_with_trackdetail_and_sensordata() throws TimeoutException, IOException {
179-
setUp(true);
188+
setUp();
180189

181190
// given
182191
Track track = contentProviderUtils.getTrack(trackId);
@@ -213,23 +222,40 @@ public void kmz_with_trackdetail_and_sensordata() throws TimeoutException, IOExc
213222
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
214223
assertEquals(Instant.parse("2020-02-02T02:02:24Z"), importedTrackStatistics.getStopTime());
215224

216-
assertEquals(track.getTrackStatistics().getTotalTime(), importedTrackStatistics.getTotalTime());
225+
TrackStatistics originalTrackStatistics = track.getTrackStatistics();
226+
227+
assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
217228
assertEquals(Duration.ofSeconds(8), importedTrackStatistics.getTotalTime());
229+
230+
assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
218231
assertEquals(Duration.ofSeconds(4), importedTrackStatistics.getMovingTime());
219232

220233
// Distance
234+
assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
221235
assertEquals(Distance.of(60), importedTrackStatistics.getTotalDistance());
222236

223237
// Speed
238+
assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
224239
assertEquals(Speed.of(15), importedTrackStatistics.getMaxSpeed());
240+
241+
assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
225242
assertEquals(Speed.of(7.5), importedTrackStatistics.getAverageSpeed());
243+
244+
assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
226245
assertEquals(Speed.of(15), importedTrackStatistics.getAverageMovingSpeed());
227246

228247
// Altitude
248+
assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
229249
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
250+
251+
assertEquals(originalTrackStatistics.getMaxAltitude(), importedTrackStatistics.getMaxAltitude(), 0.01);
230252
assertEquals(10, importedTrackStatistics.getMaxAltitude(), 0.01);
231-
assertEquals(1, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
232-
assertEquals(1, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
253+
254+
assertEquals(originalTrackStatistics.getTotalAltitudeGain(), importedTrackStatistics.getTotalAltitudeGain(), 0.01);
255+
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
256+
257+
assertEquals(originalTrackStatistics.getTotalAltitudeLoss(), importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
258+
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
233259

234260
// 4. markers
235261
assertMarkers();
@@ -238,7 +264,7 @@ public void kmz_with_trackdetail_and_sensordata() throws TimeoutException, IOExc
238264
@LargeTest
239265
@Test(expected = ImportAlreadyExistsException.class)
240266
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws TimeoutException, IOException {
241-
setUp(false);
267+
setUp();
242268

243269
// given
244270
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
@@ -264,7 +290,7 @@ public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws Tim
264290
@LargeTest
265291
@Test
266292
public void gpx() throws TimeoutException, IOException {
267-
setUp(true);
293+
setUp();
268294

269295
// given
270296
Track track = contentProviderUtils.getTrack(trackId);
@@ -297,7 +323,10 @@ public void gpx() throws TimeoutException, IOException {
297323
// Therefore, the track segmentation is changes.
298324
List<TrackPoint> trackPointsWithCoordinates = trackPoints.stream().filter(it -> TrackPoint.Type.SEGMENT_START_AUTOMATIC.equals(it.getType()) || TrackPoint.Type.TRACKPOINT.equals(it.getType())).collect(Collectors.toList());
299325
trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
300-
trackPointsWithCoordinates.get(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
326+
trackPointsWithCoordinates.get(1).setSensorDistance(Distance.of(20));
327+
trackPointsWithCoordinates.get(1).setAltitudeGain(1f);
328+
trackPointsWithCoordinates.get(1).setAltitudeLoss(1f);
329+
trackPointsWithCoordinates.get(2).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
301330

302331
TrackPointAssert a = new TrackPointAssert()
303332
.setDelta(0.05)
@@ -326,8 +355,8 @@ public void gpx() throws TimeoutException, IOException {
326355
// Altitude
327356
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
328357
assertEquals(10, importedTrackStatistics.getMaxAltitude(), 0.01);
329-
assertEquals(1, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
330-
assertEquals(1, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
358+
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
359+
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
331360

332361
// 4. markers
333362
assertMarkers();
@@ -336,7 +365,7 @@ public void gpx() throws TimeoutException, IOException {
336365
@LargeTest
337366
@Test(expected = ImportAlreadyExistsException.class)
338367
public void gpx_duplicate_trackUUID() throws TimeoutException, IOException {
339-
setUp(false);
368+
setUp();
340369

341370
// given
342371
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
@@ -380,22 +409,7 @@ private void assertMarkers() {
380409
}
381410
}
382411

383-
private void sendLocation(TrackPointCreator trackPointCreator, Instant time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain, float heartRate, float cyclingCadence, float power, Distance distance) {
384-
Location location = new Location("mock");
385-
location.setLatitude(latitude);
386-
location.setLongitude(longitude);
387-
location.setAccuracy(accuracy);
388-
location.setSpeed(speed);
389-
location.setAltitude(altitude);
390-
391-
trackPointCreator.setAltitudeSumManager(new AltitudeSumManager() {
392-
@Override
393-
public void fill(@NonNull TrackPoint trackPoint) {
394-
trackPoint.setAltitudeGain(altitudeGain);
395-
trackPoint.setAltitudeLoss(altitudeGain);
396-
}
397-
});
398-
412+
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, Float heartRate, Float cyclingCadence, Float power) {
399413
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context) {
400414
@Override
401415
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
@@ -421,8 +435,30 @@ public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
421435
return sensorDataSet;
422436
}
423437
});
438+
}
439+
440+
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
441+
AltitudeSumManager altitudeSumManager = trackPointCreator.getAltitudeSumManager();
442+
altitudeSumManager.setAltitudeGain_m(altitudeGain);
443+
altitudeSumManager.setAltitudeLoss_m(altitudeGain);
444+
}
445+
446+
private void sendSensor(TrackPointCreator trackPointCreator, Instant time) {
447+
trackPointCreator.setClock(Clock.fixed(time, ZoneId.of("CET")));
448+
trackPointCreator.onNewTrackPointWithoutGPS();
449+
}
450+
451+
private void sendLocation(TrackPointCreator trackPointCreator, Instant time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain) {
452+
Location location = new Location("mock");
453+
location.setLatitude(latitude);
454+
location.setLongitude(longitude);
455+
location.setAccuracy(accuracy);
456+
location.setSpeed(speed);
457+
location.setAltitude(altitude);
458+
459+
mockAltitudeChange(trackPointCreator, altitudeGain);
424460

425461
trackPointCreator.setClock(Clock.fixed(time, ZoneId.of("CET")));
426-
trackPointCreator.getLocationHandler().onLocationChanged(location);
462+
trackPointCreator.getGpsHandler().onLocationChanged(location);
427463
}
428464
}

0 commit comments

Comments
 (0)