Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion VirtualSpeed/VirtualSpeed/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,23 @@ 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:");

foreach (var ride in rideSegments)
{
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");
}
}
}
Expand Down
22 changes: 20 additions & 2 deletions VirtualSpeed/VirtualSpeed/Services/RideCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,36 @@ public RideCalculator(Parameters parameters)
_parameters = parameters;
}

public static double GetGradientPacingMultiplier(double gradient)
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetGradientPacingMultiplier is public but is only used internally in this class. If it isn't intended as part of the public API surface, consider making it private/internal to avoid committing to a public contract unnecessarily.

Suggested change
public static double GetGradientPacingMultiplier(double gradient)
private static double GetGradientPacingMultiplier(double gradient)

Copilot uses AI. Check for mistakes.
{
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)
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gradient banding has an off-by-one at the 6% boundary: gradientPct < 6.0 returns 1.07, but exactly 6.0% will fall through to 1.10. The requirement states 3%–6% => 107% and only >6% => 110%, so 6.0% should still map to 1.07 (adjust the comparison/ordering accordingly).

Suggested change
if (gradientPct < 6.0)
if (gradientPct <= 6.0)

Copilot uses AI. Check for mistakes.
return 1.07;
return 1.10;
}

public IReadOnlyList<RideSegment> Calculate(IReadOnlyList<RouteSegment> routeSegments, double powerWatts)
{
var rideSegments = new List<RideSegment>();
var calculator = new VirtualSpeedCalculator(_parameters);

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;
Expand Down