diff options
Diffstat (limited to 'ride/ride.go')
| -rw-r--r-- | ride/ride.go | 72 |
1 files changed, 52 insertions, 20 deletions
diff --git a/ride/ride.go b/ride/ride.go index d5533ae..a694df9 100644 --- a/ride/ride.go +++ b/ride/ride.go @@ -22,7 +22,7 @@ var ErrNoTrackPoints = errors.New("ride has no track points") type Ride struct { distances []float64 elevations []float64 - Coords []geodist.Coord + coords []geodist.Coord timestamps []time.Time heartRates []float64 cadences []float64 @@ -32,7 +32,7 @@ type Ride struct { func (r *Ride) check() { kcore.Assert(r.Len() > 0, "no points in ride") kcore.Assert(len(r.distances) == len(r.elevations), "ride columns have different lengths") - kcore.Assert(len(r.distances) == len(r.Coords), "ride columns have different lengths") + kcore.Assert(len(r.distances) == len(r.coords), "ride columns have different lengths") kcore.Assert(len(r.distances) == len(r.timestamps), "ride columns have different lengths") for _, column := range [][]float64{r.heartRates, r.cadences, r.powers} { if len(column) > 0 { @@ -45,11 +45,25 @@ func (r Ride) Len() int { return len(r.distances) } +func (r Ride) Distances() []float64 { return r.distances } + +func (r Ride) Elevations() []float64 { return r.elevations } + +func (r Ride) Coords() []geodist.Coord { return r.coords } + +func (r Ride) Timestamps() []time.Time { return r.timestamps } + +func (r Ride) Powers() ([]float64, bool) { return optionalComponent(r.powers) } + +func (r Ride) Cadences() ([]float64, bool) { return optionalComponent(r.cadences) } + +func (r Ride) HeartRates() ([]float64, bool) { return optionalComponent(r.heartRates) } + func (r Ride) DistanceM(i int) float64 { return r.distances[i] } func (r Ride) ElevationM(i int) float64 { return r.elevations[i] } -func (r Ride) Coord(i int) geodist.Coord { return r.Coords[i] } +func (r Ride) Coord(i int) geodist.Coord { return r.coords[i] } func (r Ride) Timestamp(i int) time.Time { return r.timestamps[i] } @@ -65,6 +79,13 @@ func (r Ride) PowerW(i int) (float64, bool) { return optionalMetric(r.powers, i) } +func optionalComponent(column []float64) ([]float64, bool) { + if len(column) == 0 { + return nil, false + } + return column, true +} + func optionalMetric(column []float64, i int) (float64, bool) { if len(column) == 0 { return 0, false @@ -171,7 +192,7 @@ func fromColumns(distances []float64, elevations []float64, coords []geodist.Coo ride := Ride{ distances: distances, elevations: elevations, - Coords: coords, + coords: coords, timestamps: timestamps, heartRates: heartRates, cadences: cadences, @@ -235,15 +256,15 @@ const CotacolAlgorithmVersion = "v1" const cotacolSegmentLengthM = 100 func Cotacol(ride Ride) float64 { - return difficultyScore(ride, 0, ride.Len()-1) + return difficultyScore(ride.distances, ride.elevations, 0, ride.Len()-1) } -func difficultyScore(r Ride, startIndex, endIndex int) float64 { +func difficultyScore(distances, elevations []float64, startIndex, endIndex int) float64 { if endIndex-startIndex < 1 { return 0 } - startDistance := r.DistanceM(startIndex) - lastDistance := r.DistanceM(endIndex) + startDistance := distances[startIndex] + lastDistance := distances[endIndex] if lastDistance <= startDistance { return 0 } @@ -251,14 +272,14 @@ func difficultyScore(r Ride, startIndex, endIndex int) float64 { i := startIndex for start := startDistance; start < lastDistance; start += cotacolSegmentLengthM { end := math.Min(start+cotacolSegmentLengthM, lastDistance) - for i < endIndex && r.DistanceM(i+1) <= start { + for i < endIndex && distances[i+1] <= start { i++ } - startElevation := interpolateElevation(r, i, start) - for i < endIndex && r.DistanceM(i+1) < end { + startElevation := interpolateElevation(distances, elevations, i, start) + for i < endIndex && distances[i+1] < end { i++ } - endElevation := interpolateElevation(r, i, end) + endElevation := interpolateElevation(distances, elevations, i, end) slope := (endElevation - startElevation) / (end - start) if slope > 0 { cotacol += (end - start) / 1000 * (slope * 100) * (slope * 100) @@ -267,14 +288,14 @@ func difficultyScore(r Ride, startIndex, endIndex int) float64 { return cotacol } -func interpolateElevation(r Ride, i int, distance float64) float64 { - if i+1 >= r.Len() { - return r.ElevationM(i) +func interpolateElevation(distances, elevations []float64, i int, distance float64) float64 { + if i+1 >= len(distances) { + return elevations[i] } - startDistance := r.DistanceM(i) - endDistance := r.DistanceM(i + 1) + startDistance := distances[i] + endDistance := distances[i+1] t := (distance - startDistance) / (endDistance - startDistance) - return r.ElevationM(i) + t*(r.ElevationM(i+1)-r.ElevationM(i)) + return elevations[i] + t*(elevations[i+1]-elevations[i]) } func (r *Ride) ClimbFromDist(startDist, endDist float64) Climb { @@ -288,10 +309,21 @@ func (r *Ride) ClimbFromDist(startDist, endDist float64) Climb { break } } - return Climb{ride: *r, rideStart: start, rideEnd: end} + return r.climbFrom(start, end) } func (r *Ride) ClimbFromIndexes(startIndex, endIndex int) Climb { kcore.Assert(startIndex >= 0 && endIndex < r.Len() && startIndex < endIndex, "invalid climb indexes") - return Climb{ride: *r, rideStart: startIndex, rideEnd: endIndex} + return r.climbFrom(startIndex, endIndex) +} + +func (r *Ride) climbFrom(start, end int) Climb { + return Climb{ + distances: r.distances, + elevations: r.elevations, + coords: r.coords, + timestamps: r.timestamps, + rideStart: start, + rideEnd: end, + } } |
