1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package ride
import "time"
const MovingTimeAlgorithmVersion = "v2"
// movingTimeMinSpeedMps is the minimum implied speed between two consecutive
// track points for the interval to count as moving (0.4 km/h).
const movingTimeMinSpeedMps = 0.4 / 3.6
// movingTimeMinPowerW is the minimum power output for an interval to count as
// moving when GPS speed is below the minimum speed.
const movingTimeMinPowerW = 20
// 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 (117 rides, `-backtest`, distance >= 10 km):
// absMean 0.6%, stderr 0.2, p50 0.2, p75 0.4, p90 1.2, p95 2.2, p99 4.8.
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 || dt > maxGap {
continue
}
dd := r.DistanceM(i) - r.DistanceM(i-1)
gpsMoving := dd/dt.Seconds() > minSpeedMps
powerMoving := false
if power, ok := r.PowerW(i); ok {
powerMoving = power >= movingTimeMinPowerW
}
cadenceMoving := false
if cadence, ok := r.CadenceRpm(i); ok {
cadenceMoving = cadence > 0
}
if gpsMoving || powerMoving || cadenceMoving {
moving += dt
}
}
return moving
}
|