From f8ecda1d5bd14bc56d8b5bd11cf068e4ea17e63f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:56:04 +0000 Subject: [PATCH 1/2] Initial plan From 5153b66bbb6d574231bf6a4c414c5c78b9e042d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 17:00:30 +0000 Subject: [PATCH 2/2] Add gradient aware pacing Co-authored-by: simonech <61557+simonech@users.noreply.github.com> --- VirtualSpeed/VirtualSpeed/Program.cs | 6 ++++- .../VirtualSpeed/Services/RideCalculator.cs | 22 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/VirtualSpeed/VirtualSpeed/Program.cs b/VirtualSpeed/VirtualSpeed/Program.cs index 96c54d9..c2578d0 100644 --- a/VirtualSpeed/VirtualSpeed/Program.cs +++ b/VirtualSpeed/VirtualSpeed/Program.cs @@ -67,11 +67,15 @@ static void Main(string[] args) double avgSpeedKmh = totalTimeSeconds > 0 ? (totalDistance / totalTimeSeconds) * 3.6 : 0; + double avgPowerWatts = totalTimeSeconds > 0 + ? rideSegments.Sum(r => r.PowerWatts * r.DurationSeconds) / totalTimeSeconds + : 0; Console.WriteLine($"Total Distance: {totalDistance:F0} m"); Console.WriteLine($"Total Elevation Gain: {totalElevationGain:F0} m"); Console.WriteLine($"Total Time: {TimeSpan.FromSeconds(totalTimeSeconds):hh\\:mm\\:ss}"); Console.WriteLine($"Avg Speed: {avgSpeedKmh:F1} km/h"); + Console.WriteLine($"Avg Power: {avgPowerWatts:F1} W"); Console.WriteLine(); Console.WriteLine("Segments:"); @@ -79,7 +83,7 @@ static void Main(string[] args) { var seg = ride.RouteSegment; double gradientPct = seg.AverageGradient * 100; - Console.WriteLine($"{seg.StartDistanceMeters:F0}m - {seg.StartDistanceMeters + seg.LengthMeters:F0}m | Gradient: {gradientPct:F1}% | Speed: {ride.SpeedKmh:F1} km/h | Time: {ride.DurationSeconds:F0} s"); + Console.WriteLine($"{seg.StartDistanceMeters:F0}m - {seg.StartDistanceMeters + seg.LengthMeters:F0}m | Gradient: {gradientPct:F1}% | Power: {ride.PowerWatts:F0} W | Speed: {ride.SpeedKmh:F1} km/h | Time: {ride.DurationSeconds:F0} s"); } } } diff --git a/VirtualSpeed/VirtualSpeed/Services/RideCalculator.cs b/VirtualSpeed/VirtualSpeed/Services/RideCalculator.cs index 2c12fb9..eb21fe1 100644 --- a/VirtualSpeed/VirtualSpeed/Services/RideCalculator.cs +++ b/VirtualSpeed/VirtualSpeed/Services/RideCalculator.cs @@ -12,6 +12,23 @@ public RideCalculator(Parameters parameters) _parameters = parameters; } + public static double GetGradientPacingMultiplier(double gradient) + { + double gradientPct = gradient * 100.0; + + if (gradientPct < -5.0) + return 0.85; + if (gradientPct < -1.0) + return 0.90; + if (gradientPct < 1.0) + return 1.00; + if (gradientPct < 3.0) + return 1.02; + if (gradientPct < 6.0) + return 1.07; + return 1.10; + } + public IReadOnlyList Calculate(IReadOnlyList routeSegments, double powerWatts) { var rideSegments = new List(); @@ -19,11 +36,12 @@ public IReadOnlyList Calculate(IReadOnlyList routeSeg foreach (var routeSegment in routeSegments) { - double speedKmh = calculator.CalculateVelocity(powerWatts, routeSegment.AverageGradient); + double adjustedPower = powerWatts * GetGradientPacingMultiplier(routeSegment.AverageGradient); + double speedKmh = calculator.CalculateVelocity(adjustedPower, routeSegment.AverageGradient); double speedMs = calculator.ConvertKmhToMS(speedKmh); double durationSeconds = speedMs > 0 ? routeSegment.LengthMeters / speedMs : 0; - rideSegments.Add(new RideSegment(durationSeconds, speedKmh, powerWatts, routeSegment)); + rideSegments.Add(new RideSegment(durationSeconds, speedKmh, adjustedPower, routeSegment)); } return rideSegments;