mirror of
https://github.com/sstent/aicyclingcoach-go.git
synced 2026-01-27 17:42:23 +00:00
96 lines
2.3 KiB
Go
96 lines
2.3 KiB
Go
package analysis
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// DownsampledPoint represents a single data point in a downsampled metric series
|
|
type DownsampledPoint struct {
|
|
TimeOffset int `json:"time_offset"` // Seconds from activity start
|
|
Value float64 `json:"value"` // Average value during this segment
|
|
Min float64 `json:"min,omitempty"`
|
|
Max float64 `json:"max,omitempty"`
|
|
}
|
|
|
|
// DownsampleMetric downsamples a metric array to the specified number of points
|
|
func DownsampleMetric(data []float64, duration time.Duration, targetPoints int) []DownsampledPoint {
|
|
if len(data) == 0 || targetPoints <= 0 {
|
|
return nil
|
|
}
|
|
|
|
totalSeconds := int(duration.Seconds())
|
|
if totalSeconds <= 0 {
|
|
return nil
|
|
}
|
|
|
|
// Calculate segment duration in seconds (floating point for accuracy)
|
|
segmentDuration := float64(totalSeconds) / float64(targetPoints)
|
|
if segmentDuration < 1 {
|
|
segmentDuration = 1
|
|
}
|
|
|
|
// Preallocate segments array
|
|
segments := make([]struct {
|
|
sum float64
|
|
count int
|
|
min float64
|
|
max float64
|
|
}, targetPoints)
|
|
|
|
// Initialize min/max with zero values - we'll update them when we see data
|
|
|
|
// Assign data points to segments
|
|
for i, value := range data {
|
|
// Calculate time offset for this data point
|
|
timeOffset := float64(i) * float64(totalSeconds) / float64(len(data))
|
|
segmentIndex := int(timeOffset / segmentDuration)
|
|
|
|
// Clamp to valid segment range
|
|
if segmentIndex >= targetPoints {
|
|
segmentIndex = targetPoints - 1
|
|
}
|
|
|
|
seg := &segments[segmentIndex]
|
|
seg.sum += value
|
|
seg.count++
|
|
|
|
// Initialize min/max on first value in segment
|
|
if seg.count == 1 {
|
|
seg.min = value
|
|
seg.max = value
|
|
} else {
|
|
if value < seg.min {
|
|
seg.min = value
|
|
}
|
|
if value > seg.max {
|
|
seg.max = value
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build results array
|
|
results := make([]DownsampledPoint, targetPoints)
|
|
for j := 0; j < targetPoints; j++ {
|
|
seg := &segments[j]
|
|
timeOffset := int(float64(j) * segmentDuration)
|
|
|
|
if seg.count == 0 {
|
|
// For empty segments, omit min/max
|
|
results[j] = DownsampledPoint{
|
|
TimeOffset: timeOffset,
|
|
Value: 0,
|
|
}
|
|
} else {
|
|
avg := seg.sum / float64(seg.count)
|
|
results[j] = DownsampledPoint{
|
|
TimeOffset: timeOffset,
|
|
Value: avg,
|
|
Min: seg.min,
|
|
Max: seg.max,
|
|
}
|
|
}
|
|
}
|
|
|
|
return results
|
|
}
|