aboutsummaryrefslogtreecommitdiff
path: root/ride/moving_time.go
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-20 15:47:52 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-20 15:47:52 +0200
commit57e48b23987630936c84300d87ea5be682cb64bd (patch)
tree96e840749bb658c2162788cedca627165747a254 /ride/moving_time.go
parent992c36c6c7ce7e1e732866b3cd4fb8269a905875 (diff)
feat(ride): compute moving time from GPX with backtest
Add ride.MovingTime, a speed-threshold algorithm over track timestamps. Add a -backtest CLI command comparing computed values against Strava's reference across all stored rides, with a percentile distribution summary. Baseline: mean err 3.9%, p50 1.6, p75 4.6, p90 8.4, p95 12.1, p99 44.9.
Diffstat (limited to 'ride/moving_time.go')
-rw-r--r--ride/moving_time.go28
1 files changed, 28 insertions, 0 deletions
diff --git a/ride/moving_time.go b/ride/moving_time.go
new file mode 100644
index 0000000..8403772
--- /dev/null
+++ b/ride/moving_time.go
@@ -0,0 +1,28 @@
+package ride
+
+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
+
+// 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.
+func MovingTime(r Ride) 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 {
+ continue
+ }
+ dd := r.DistanceM(i) - r.DistanceM(i - 1)
+ if dd/dt.Seconds() > movingTimeMinSpeedMps {
+ moving += dt
+ }
+ }
+ return moving
+} \ No newline at end of file