aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ride/moving_time.go26
1 files changed, 16 insertions, 10 deletions
diff --git a/ride/moving_time.go b/ride/moving_time.go
index 8403772..ea9ec24 100644
--- a/ride/moving_time.go
+++ b/ride/moving_time.go
@@ -5,24 +5,30 @@ import "time"
const MovingTimeAlgorithmVersion = "v1"
// movingTimeMinSpeedMps is the minimum implied speed between two consecutive
-// track points for the interval to count as moving (1 km/h).
-const movingTimeMinSpeedMps = 1.0 / 3.6
+// track points for the interval to count as moving (0.4 km/h).
+const movingTimeMinSpeedMps = 0.4 / 3.6
-// Baseline backtest (356 rides, `-backtest`): mean err 3.9% (stderr 0.4),
-// p50 1.6, p75 4.6, p90 8.4, p95 12.1, p99 44.9.
-// Worst offenders overcount stationary GPS jitter (lunch stops) and
-// undercount trainer rides with little GPS movement. Tuned in future commits.
+// movingTimeMaxGap is the maximum time gap between consecutive track points
+// that can count as moving. Longer gaps are pauses (stops, lost signal).
+const movingTimeMaxGap = 120 * time.Second
+
+// Bench (356 rides, `-backtest`):
+// absMean 2.5%, bias -1.3, p50 0.8, p75 2.9, p90 6.0, p95 9.3, p99 26.2.
func MovingTime(r Ride) time.Duration {
+ return movingTime(r, movingTimeMinSpeedMps, movingTimeMaxGap)
+}
+
+func movingTime(r Ride, minSpeedMps float64, maxGap time.Duration) time.Duration {
var moving time.Duration
for i := 1; i < r.Len(); i++ {
dt := r.Timestamp(i).Sub(r.Timestamp(i - 1))
- if dt <= 0 {
+ if dt <= 0 || dt > maxGap {
continue
}
- dd := r.DistanceM(i) - r.DistanceM(i - 1)
- if dd/dt.Seconds() > movingTimeMinSpeedMps {
+ dd := r.DistanceM(i) - r.DistanceM(i-1)
+ if dd/dt.Seconds() > minSpeedMps {
moving += dt
}
}
return moving
-} \ No newline at end of file
+}