diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdater.java b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdater.java index f58ea4993..7a2d20f14 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdater.java +++ b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdater.java @@ -35,12 +35,6 @@ import android.util.Log; */ public class TripStatisticsUpdater { - /** - * The number of distance readings to smooth to get a stable signal. - */ - @VisibleForTesting - static final int DISTANCE_SMOOTHING_FACTOR = 25; - /** * The number of elevation readings to smooth to get a somewhat accurate * signal. @@ -48,6 +42,18 @@ public class TripStatisticsUpdater { @VisibleForTesting static final int ELEVATION_SMOOTHING_FACTOR = 25; + /** + * The number of run readings to smooth for calculating grade. + */ + @VisibleForTesting + static final int RUN_SMOOTHING_FACTOR = 25; + + /** + * The number of rise readings to smooth for calculating grade. + */ + @VisibleForTesting + static final int RISE_SMOOTHING_FACTOR = 25; + /** * The number of grade readings to smooth to get a somewhat accurate signal. */ @@ -84,18 +90,21 @@ public class TripStatisticsUpdater { // Current segment's last moving location private Location lastMovingLocation; - // A buffer of the recent speed readings (m/s) for calculating max speed - private final DoubleBuffer speedBuffer = new DoubleBuffer(SPEED_SMOOTHING_FACTOR); - // A buffer of the recent elevation readings (m) private final DoubleBuffer elevationBuffer = new DoubleBuffer(ELEVATION_SMOOTHING_FACTOR); - // A buffer of the recent distance readings (m) for calculating grade - private final DoubleBuffer distanceBuffer = new DoubleBuffer(DISTANCE_SMOOTHING_FACTOR); + // A buffer of the recent run readings (m) for calculating grade + private final DoubleBuffer runBuffer = new DoubleBuffer(RUN_SMOOTHING_FACTOR); + + // A buffer of the recent rise readings (m) for calculating grade + private final DoubleBuffer riseBuffer = new DoubleBuffer(RISE_SMOOTHING_FACTOR); // A buffer of the recent grade calculations (%) private final DoubleBuffer gradeBuffer = new DoubleBuffer(GRADE_SMOOTHING_FACTOR); + // A buffer of the recent speed readings (m/s) for calculating max speed + private final DoubleBuffer speedBuffer = new DoubleBuffer(SPEED_SMOOTHING_FACTOR); + /** * Creates a new trip statistics updater. * @@ -128,8 +137,11 @@ public class TripStatisticsUpdater { * @param minRecordingDistance the min recording distance */ public void addLocation(Location location, int minRecordingDistance) { + // Always update time + updateTime(location.getTime()); + if (!LocationUtils.isValidLocation(location)) { - updateTime(location.getTime()); + // Either pause or resume marker if (location.getLatitude() == TrackRecordingService.PAUSE_LATITUDE) { if (lastLocation != null && lastMovingLocation != null && lastLocation != lastMovingLocation) { @@ -140,40 +152,56 @@ public class TripStatisticsUpdater { currentSegment = init(location.getTime()); lastLocation = null; lastMovingLocation = null; - speedBuffer.reset(); elevationBuffer.reset(); - distanceBuffer.reset(); + runBuffer.reset(); + riseBuffer.reset(); gradeBuffer.reset(); + speedBuffer.reset(); return; } - double elevationDifference = updateElevation(location.getAltitude()); currentSegment.updateLatitudeExtremities(location.getLatitude()); currentSegment.updateLongitudeExtremities(location.getLongitude()); + if (location.hasAltitude()) { + updateElevation(location.getAltitude()); + } + if (lastLocation == null || lastMovingLocation == null) { - updateTime(location.getTime()); lastLocation = location; lastMovingLocation = location; return; } + double movingDistance = lastMovingLocation.distanceTo(location); - if (movingDistance < minRecordingDistance && location.getSpeed() < MAX_NO_MOVEMENT_SPEED) { - updateTime(location.getTime()); - lastLocation = location; - return; + if (movingDistance < minRecordingDistance) { + if (!location.hasSpeed() || location.getSpeed() < MAX_NO_MOVEMENT_SPEED) { + lastLocation = location; + return; + } } long movingTime = location.getTime() - lastLocation.getTime(); if (movingTime < 0) { - updateTime(location.getTime()); lastLocation = location; return; } + + // Update total distance currentSegment.addTotalDistance(movingDistance); + + // Update moving time currentSegment.addMovingTime(movingTime); + + // Update grade + if (lastLocation.hasAltitude() && location.hasAltitude()) { + float run = lastLocation.distanceTo(location); + double rise = location.getAltitude() - lastLocation.getAltitude(); + updateGrade(run, rise); + } + + // Update max speed updateSpeed( location.getTime(), location.getSpeed(), lastLocation.getTime(), lastLocation.getSpeed()); - updateGrade(lastLocation.distanceTo(location), elevationDifference); - updateTime(location.getTime()); + lastLocation = location; lastMovingLocation = location; } @@ -217,38 +245,46 @@ public class TripStatisticsUpdater { * @param elevation the elevation */ @VisibleForTesting - double updateElevation(double elevation) { + void updateElevation(double elevation) { + currentSegment.updateElevationExtremities(elevation); + + // update elevation gain double oldAverage = elevationBuffer.getAverage(); elevationBuffer.setNext(elevation); double newAverage = elevationBuffer.getAverage(); - currentSegment.updateElevationExtremities(newAverage); - double elevationDifference = elevationBuffer.isFull() ? newAverage - oldAverage : 0.0; - if (elevationDifference > 0) { - currentSegment.addTotalElevationGain(elevationDifference); + if (elevationBuffer.isFull()) { + double difference = newAverage - oldAverage; + if (difference > 0) { + currentSegment.addTotalElevationGain(difference); + } } - return elevationDifference; } /** * Updates a grade reading. * - * @param distance the distance the user just traveled - * @param elevationDifference the elevation difference between the current - * reading and the previous reading + * @param run the run + * @param rise the rise */ @VisibleForTesting - void updateGrade(double distance, double elevationDifference) { - distanceBuffer.setNext(distance); - double smoothedDistance = distanceBuffer.getAverage(); + void updateGrade(double run, double rise) { + runBuffer.setNext(run); + riseBuffer.setNext(rise); + + if (!runBuffer.isFull() || !riseBuffer.isFull()) { + return; + } + + double smoothedRun = runBuffer.getAverage(); /* * With the error in the altitude measurement it is dangerous to divide by * anything less than 5. */ - if (!elevationBuffer.isFull() || !distanceBuffer.isFull() || smoothedDistance < 5.0) { + if (smoothedRun < 5.0) { return; } - gradeBuffer.setNext(elevationDifference / smoothedDistance); + gradeBuffer.setNext(riseBuffer.getAverage() / smoothedRun); currentSegment.updateGradeExtremities(gradeBuffer.getAverage()); } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdaterTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdaterTest.java index be497c561..d47cba89e 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdaterTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdaterTest.java @@ -84,7 +84,7 @@ public class TripStatisticsUpdaterTest extends TestCase { for (double elevation = 0; elevation < 1000; elevation += 10) { tripStatisticsUpdater = new TripStatisticsUpdater(System.currentTimeMillis()); for (int i = 0; i < 100; i++) { - assertEquals(0.0, tripStatisticsUpdater.updateElevation(elevation)); + tripStatisticsUpdater.updateElevation(elevation); assertEquals(elevation, tripStatisticsUpdater.getSmoothedElevation()); TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics(); @@ -109,7 +109,7 @@ public class TripStatisticsUpdaterTest extends TestCase { } else { expectedGain = 1.0; } - assertEquals(expectedGain, tripStatisticsUpdater.updateElevation(i)); + tripStatisticsUpdater.updateElevation(i); assertEquals(i, tripStatisticsUpdater.getSmoothedElevation(), TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR / 2); @@ -127,29 +127,22 @@ public class TripStatisticsUpdaterTest extends TestCase { */ public void testGradeSimple() throws Exception { for (double i = 0; i < 1000; i++) { - /* - * The value of the elevation does not matter. This is just to fill the - * elevation buffer. - */ - tripStatisticsUpdater.updateElevation(i); tripStatisticsUpdater.updateGrade(100, 100); if (i >= TripStatisticsUpdater.GRADE_SMOOTHING_FACTOR - && i >= TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR) { + && i >= TripStatisticsUpdater.RISE_SMOOTHING_FACTOR + && i >= TripStatisticsUpdater.RUN_SMOOTHING_FACTOR) { assertEquals(1.0, tripStatisticsUpdater.getTripStatistics().getMaxGrade()); assertEquals(1.0, tripStatisticsUpdater.getTripStatistics().getMinGrade()); } } for (double i = 0; i < 1000; i++) { - /* - * The value of the elevation does not matter. This is just to fill the - * elevation buffer. - */ - tripStatisticsUpdater.updateElevation(i); tripStatisticsUpdater.updateGrade(100, -100); if (i >= TripStatisticsUpdater.GRADE_SMOOTHING_FACTOR - && i >= TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR) { + && i >= TripStatisticsUpdater.RISE_SMOOTHING_FACTOR + && i >= TripStatisticsUpdater.RUN_SMOOTHING_FACTOR) { assertEquals(1.0, tripStatisticsUpdater.getTripStatistics().getMaxGrade()); - assertEquals(-1.0, tripStatisticsUpdater.getTripStatistics().getMinGrade()); + // add 0.1 delta since changing min grade from 1 to -1 + assertEquals(-1.0, tripStatisticsUpdater.getTripStatistics().getMinGrade(), 0.1); } } } @@ -255,7 +248,7 @@ public class TripStatisticsUpdaterTest extends TestCase { // If there are only moving locations in the track. if (locationOffset == 0 - && (i + locationOffset) >= TripStatisticsUpdater.DISTANCE_SMOOTHING_FACTOR + && (i + locationOffset) >= TripStatisticsUpdater.RUN_SMOOTHING_FACTOR && (i + locationOffset) >= TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR) { // 1 m / 111 m = .009 assertEquals(0.009, tripStatistics.getMinGrade(), 0.0001);