Suppress the target band for laps that are just an unplanned continuation
Confirmed against Garmin Connect's own workout view: a lap whose IntensityType merely repeats the immediately preceding lap's (e.g. a second "cool-down" lap right after the first) means the recording continued past the end of that step, not that the workout prescribed a second target for it. Only the first lap of such a run keeps its target/HR fields; the continuation shows the actual trace with no expectation overlay. Doesn't affect normal interval structure, since Effort/Recovery always alternate and never appear back-to-back.
This commit is contained in:
@@ -147,10 +147,20 @@ interface Point {
|
|||||||
function buildLapWindows(laps: Lap[]): LapWindow[] {
|
function buildLapWindows(laps: Lap[]): LapWindow[] {
|
||||||
const windows: LapWindow[] = [];
|
const windows: LapWindow[] = [];
|
||||||
let elapsedMin = 0;
|
let elapsedMin = 0;
|
||||||
for (const l of laps) {
|
laps.forEach((l, i) => {
|
||||||
const start = elapsedMin;
|
const start = elapsedMin;
|
||||||
elapsedMin += l.DurationSeconds / 60;
|
elapsedMin += l.DurationSeconds / 60;
|
||||||
|
|
||||||
|
// A lap whose type merely repeats the previous lap's (e.g. a second
|
||||||
|
// "cool-down" lap right after the first) means the recording continued
|
||||||
|
// past the end of that step -- confirmed against Garmin Connect's own
|
||||||
|
// workout view for a real activity where this happened: the run kept
|
||||||
|
// going well past the prescribed cool-down, and Garmin logged the extra
|
||||||
|
// time as a further same-type lap that was never actually part of the
|
||||||
|
// workout. Only the first lap of such a run carries a real prescribed
|
||||||
|
// target; the continuation's target/HR fields are suppressed.
|
||||||
|
const isContinuation = i > 0 && laps[i - 1].IntensityType === l.IntensityType;
|
||||||
|
|
||||||
// Pace (sec/km) is inverted vs speed (m/s): the *faster* (higher) speed
|
// Pace (sec/km) is inverted vs speed (m/s): the *faster* (higher) speed
|
||||||
// bound is the *lower* (faster) pace bound.
|
// bound is the *lower* (faster) pace bound.
|
||||||
const paceLow = l.TargetPaceHighMps != null ? paceSecPerKm(l.TargetPaceHighMps) : null;
|
const paceLow = l.TargetPaceHighMps != null ? paceSecPerKm(l.TargetPaceHighMps) : null;
|
||||||
@@ -160,13 +170,13 @@ function buildLapWindows(laps: Lap[]): LapWindow[] {
|
|||||||
start,
|
start,
|
||||||
end: elapsedMin,
|
end: elapsedMin,
|
||||||
intensityType: l.IntensityType,
|
intensityType: l.IntensityType,
|
||||||
targetPaceRange: paceLow != null && paceHigh != null ? [paceLow, paceHigh] : null,
|
targetPaceRange: !isContinuation && paceLow != null && paceHigh != null ? [paceLow, paceHigh] : null,
|
||||||
targetHRRange: l.TargetHRLowBpm != null && l.TargetHRHighBpm != null ? [l.TargetHRLowBpm, l.TargetHRHighBpm] : null,
|
targetHRRange: !isContinuation && l.TargetHRLowBpm != null && l.TargetHRHighBpm != null ? [l.TargetHRLowBpm, l.TargetHRHighBpm] : null,
|
||||||
avgPace: paceSecPerKm(l.AvgSpeedMps),
|
avgPace: paceSecPerKm(l.AvgSpeedMps),
|
||||||
avgHR: l.AvgHR,
|
avgHR: l.AvgHR,
|
||||||
color: PHASE_COLORS[l.IntensityType],
|
color: PHASE_COLORS[l.IntensityType],
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
return windows;
|
return windows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user