aboutsummaryrefslogtreecommitdiff
path: root/ride/ride.go
diff options
context:
space:
mode:
Diffstat (limited to 'ride/ride.go')
-rw-r--r--ride/ride.go72
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,
+ }
}