@dofek/training 0.1.40 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/cycling-workout-metrics.d.ts +112 -0
- package/dist/cycling-workout-metrics.d.ts.map +1 -0
- package/dist/cycling-workout-metrics.js +350 -0
- package/dist/cycling-workout-metrics.js.map +1 -0
- package/dist/power-analysis.d.ts +14 -0
- package/dist/power-analysis.d.ts.map +1 -1
- package/dist/power-analysis.js +20 -2
- package/dist/power-analysis.js.map +1 -1
- package/dist/power-duration-reference.d.ts +14 -0
- package/dist/power-duration-reference.d.ts.map +1 -0
- package/dist/power-duration-reference.js +70 -0
- package/dist/power-duration-reference.js.map +1 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -50,6 +50,7 @@ Every public module is imported as `@dofek/training/<subpath>`.
|
|
|
50
50
|
| --- | --- |
|
|
51
51
|
| `activity-icons` | Normalize activity names into framework-neutral icon categories |
|
|
52
52
|
| `climbing-grades` | Validate, order, and convert Sandbag-supported boulder and route climbing grades |
|
|
53
|
+
| `cycling-workout-metrics` | Coverage-aware per-activity power, heart-rate, cadence, drift, zone, load, and interval calculations |
|
|
53
54
|
| `derived-cardio` | Cycling and submaximal walking/running VO2 max estimates and validation |
|
|
54
55
|
| `endurance-types` | Endurance and indoor-cycling type guards |
|
|
55
56
|
| `grade-adjusted-pace` | Grade cost factor and adjusted running pace |
|
|
@@ -107,6 +108,32 @@ product heuristics.
|
|
|
107
108
|
[Monod and Scherrer (1965)](https://doi.org/10.1080/00140136508930810)
|
|
108
109
|
using efforts from 120 through 600 seconds.
|
|
109
110
|
|
|
111
|
+
### Cycling workout metrics
|
|
112
|
+
|
|
113
|
+
`computeCyclingWorkoutMetrics` resamples native power, heart-rate, and cadence streams to elapsed
|
|
114
|
+
one-second buckets without filling dropouts. A native observation is held only to the next native
|
|
115
|
+
timestamp when the gap is at most the bounded continuity tolerance; measured zero remains zero and
|
|
116
|
+
missing elapsed seconds remain missing. The result reports native observation counts, covered,
|
|
117
|
+
missing, and zero seconds, median sample interval, and largest gap independently for every stream.
|
|
118
|
+
|
|
119
|
+
Average power and work use covered power seconds only. Normalized power uses the package's canonical
|
|
120
|
+
30-second rolling/fourth-power calculation and is withheld when any elapsed power second is missing.
|
|
121
|
+
Intensity factor is normalized power divided by the FTP effective for that activity. Power TSS uses
|
|
122
|
+
`duration_hours × intensity_factor² × 100`, following the package's documented power-stress model;
|
|
123
|
+
both values are unavailable rather than manufactured when FTP or normalized power is missing.
|
|
124
|
+
|
|
125
|
+
Aerobic efficiency is mean synchronized power divided by mean synchronized heart rate. Cardiac drift
|
|
126
|
+
compares that ratio between equal elapsed-time halves as
|
|
127
|
+
`100 × (first_half_ratio - second_half_ratio) / first_half_ratio`; it requires at least 20 minutes
|
|
128
|
+
and 90% paired coverage in each half. This is a descriptive within-workout comparison, not a causal
|
|
129
|
+
or medical conclusion.
|
|
130
|
+
|
|
131
|
+
Recorded interval boundaries always take precedence. Without recorded intervals, the optional
|
|
132
|
+
detector identifies at least 30 seconds whose 30-second mean is at or above 105% of the activity's
|
|
133
|
+
effective FTP, merges interruptions shorter than 15 seconds, and exposes intervening recoveries.
|
|
134
|
+
That detector is a Dofek heuristic. Inferred intervals never receive invented targets or completion
|
|
135
|
+
scores.
|
|
136
|
+
|
|
110
137
|
### Terrain adjustment
|
|
111
138
|
|
|
112
139
|
Grade-adjusted pace uses a bounded approximation derived from the slope-cost
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export interface CyclingWorkoutSample {
|
|
2
|
+
elapsedSeconds: number;
|
|
3
|
+
powerWatts?: number | null;
|
|
4
|
+
heartRateBpm?: number | null;
|
|
5
|
+
cadenceRpm?: number | null;
|
|
6
|
+
}
|
|
7
|
+
export interface CyclingWorkoutSettings {
|
|
8
|
+
ftpWatts?: number | null;
|
|
9
|
+
thresholdHeartRateBpm?: number | null;
|
|
10
|
+
powerZoneUpperPcts?: number[] | null;
|
|
11
|
+
heartRateZoneUpperPcts?: number[] | null;
|
|
12
|
+
}
|
|
13
|
+
export interface CyclingWorkoutIntervalInput {
|
|
14
|
+
index: number;
|
|
15
|
+
type: "work" | "recovery" | "warmup" | "cooldown" | "other";
|
|
16
|
+
label?: string | null;
|
|
17
|
+
startOffsetSeconds: number;
|
|
18
|
+
endOffsetSeconds: number;
|
|
19
|
+
targetPowerWatts?: number | null;
|
|
20
|
+
}
|
|
21
|
+
interface StreamCoverage {
|
|
22
|
+
observedSamples: number;
|
|
23
|
+
coveredSeconds: number;
|
|
24
|
+
missingSeconds: number;
|
|
25
|
+
zeroSeconds: number;
|
|
26
|
+
coveragePct: number;
|
|
27
|
+
medianSampleIntervalSeconds: number | null;
|
|
28
|
+
largestGapSeconds: number | null;
|
|
29
|
+
}
|
|
30
|
+
interface ZoneDistribution {
|
|
31
|
+
threshold: number;
|
|
32
|
+
upperPcts: number[];
|
|
33
|
+
zones: Array<{
|
|
34
|
+
zone: number;
|
|
35
|
+
seconds: number;
|
|
36
|
+
percent: number;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export interface CyclingWorkoutMetricsInput {
|
|
40
|
+
durationSeconds: number;
|
|
41
|
+
samples: CyclingWorkoutSample[];
|
|
42
|
+
settings: CyclingWorkoutSettings | null;
|
|
43
|
+
intervals: CyclingWorkoutIntervalInput[];
|
|
44
|
+
}
|
|
45
|
+
export interface CyclingWorkoutMetrics {
|
|
46
|
+
durationSeconds: number;
|
|
47
|
+
power: {
|
|
48
|
+
averageWatts: number | null;
|
|
49
|
+
normalizedWatts: number | null;
|
|
50
|
+
variabilityIndex: number | null;
|
|
51
|
+
workKilojoules: number | null;
|
|
52
|
+
intensityFactor: number | null;
|
|
53
|
+
trainingStressScore: number | null;
|
|
54
|
+
};
|
|
55
|
+
heartRate: {
|
|
56
|
+
averageBpm: number | null;
|
|
57
|
+
maximumBpm: number | null;
|
|
58
|
+
};
|
|
59
|
+
cadence: {
|
|
60
|
+
averageRpm: number | null;
|
|
61
|
+
};
|
|
62
|
+
aerobicEfficiency: {
|
|
63
|
+
powerToHeartRateRatio: number | null;
|
|
64
|
+
pairedSeconds: number;
|
|
65
|
+
};
|
|
66
|
+
cardiacDrift: {
|
|
67
|
+
percent: number | null;
|
|
68
|
+
firstHalfPowerToHeartRate: number | null;
|
|
69
|
+
secondHalfPowerToHeartRate: number | null;
|
|
70
|
+
pairedSeconds: number;
|
|
71
|
+
method: "equal_elapsed_time_halves_power_to_heart_rate";
|
|
72
|
+
};
|
|
73
|
+
powerZones: ZoneDistribution | null;
|
|
74
|
+
heartRateZones: ZoneDistribution | null;
|
|
75
|
+
coverage: {
|
|
76
|
+
power: StreamCoverage;
|
|
77
|
+
heartRate: StreamCoverage;
|
|
78
|
+
cadence: StreamCoverage;
|
|
79
|
+
};
|
|
80
|
+
intervalSource: "recorded" | "inferred" | "none";
|
|
81
|
+
intervalDetection: {
|
|
82
|
+
method: "30_second_average_above_105_percent_ftp";
|
|
83
|
+
thresholdWatts: number;
|
|
84
|
+
minimumWorkSeconds: number;
|
|
85
|
+
} | null;
|
|
86
|
+
intervals: Array<{
|
|
87
|
+
index: number;
|
|
88
|
+
type: CyclingWorkoutIntervalInput["type"];
|
|
89
|
+
label: string | null;
|
|
90
|
+
source: "recorded" | "inferred";
|
|
91
|
+
startOffsetSeconds: number;
|
|
92
|
+
endOffsetSeconds: number;
|
|
93
|
+
durationSeconds: number;
|
|
94
|
+
averagePowerWatts: number | null;
|
|
95
|
+
normalizedPowerWatts: number | null;
|
|
96
|
+
averageHeartRateBpm: number | null;
|
|
97
|
+
averageCadenceRpm: number | null;
|
|
98
|
+
targetPowerWatts: number | null;
|
|
99
|
+
completionPct: number | null;
|
|
100
|
+
}>;
|
|
101
|
+
unavailableReasons: Array<{
|
|
102
|
+
metric: string;
|
|
103
|
+
reason: string;
|
|
104
|
+
}>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Compute per-activity cycling metrics from native samples without extrapolating
|
|
108
|
+
* through dropouts. Every coverage field is expressed against elapsed duration.
|
|
109
|
+
*/
|
|
110
|
+
export declare function computeCyclingWorkoutMetrics(input: CyclingWorkoutMetricsInput): CyclingWorkoutMetrics;
|
|
111
|
+
export {};
|
|
112
|
+
//# sourceMappingURL=cycling-workout-metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycling-workout-metrics.d.ts","sourceRoot":"","sources":["../src/cycling-workout-metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { computePowerTss } from "./pmc.js";
|
|
2
|
+
import { computeNormalizedPower } from "./power-analysis.js";
|
|
3
|
+
function round(value, decimals = 1) {
|
|
4
|
+
const multiplier = 10 ** decimals;
|
|
5
|
+
return Math.round(value * multiplier) / multiplier;
|
|
6
|
+
}
|
|
7
|
+
function average(values) {
|
|
8
|
+
let total = 0;
|
|
9
|
+
let count = 0;
|
|
10
|
+
for (const value of values) {
|
|
11
|
+
if (value == null)
|
|
12
|
+
continue;
|
|
13
|
+
total += value;
|
|
14
|
+
count++;
|
|
15
|
+
}
|
|
16
|
+
return count === 0 ? null : total / count;
|
|
17
|
+
}
|
|
18
|
+
function median(values) {
|
|
19
|
+
if (values.length === 0)
|
|
20
|
+
return null;
|
|
21
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
22
|
+
const middle = Math.floor(sorted.length / 2);
|
|
23
|
+
const lower = sorted[middle - 1];
|
|
24
|
+
const upper = sorted[middle];
|
|
25
|
+
if (upper == null)
|
|
26
|
+
return null;
|
|
27
|
+
return sorted.length % 2 === 0 && lower != null ? (lower + upper) / 2 : upper;
|
|
28
|
+
}
|
|
29
|
+
function resampleStream(samples, durationSeconds, select) {
|
|
30
|
+
const byOffset = new Map();
|
|
31
|
+
for (const sample of samples) {
|
|
32
|
+
const value = select(sample);
|
|
33
|
+
if (value == null || !Number.isFinite(value) || value < 0)
|
|
34
|
+
continue;
|
|
35
|
+
const offset = Math.floor(sample.elapsedSeconds);
|
|
36
|
+
if (offset >= 0 && offset < durationSeconds)
|
|
37
|
+
byOffset.set(offset, value);
|
|
38
|
+
}
|
|
39
|
+
const points = [...byOffset.entries()]
|
|
40
|
+
.map(([offset, value]) => ({ offset, value }))
|
|
41
|
+
.sort((left, right) => left.offset - right.offset);
|
|
42
|
+
const gaps = points.slice(1).map((point, index) => point.offset - (points[index]?.offset ?? 0));
|
|
43
|
+
const medianInterval = median(gaps);
|
|
44
|
+
const nativeInterval = medianInterval ?? 1;
|
|
45
|
+
const continuityTolerance = Math.min(10, Math.max(5, nativeInterval * 2));
|
|
46
|
+
const values = Array.from({ length: durationSeconds }, () => null);
|
|
47
|
+
for (let index = 0; index < points.length; index++) {
|
|
48
|
+
const point = points[index];
|
|
49
|
+
if (!point)
|
|
50
|
+
continue;
|
|
51
|
+
const next = points[index + 1];
|
|
52
|
+
const intervalEnd = next
|
|
53
|
+
? next.offset - point.offset <= continuityTolerance
|
|
54
|
+
? next.offset
|
|
55
|
+
: point.offset + nativeInterval
|
|
56
|
+
: point.offset + nativeInterval;
|
|
57
|
+
for (let second = point.offset; second < Math.min(durationSeconds, intervalEnd); second++) {
|
|
58
|
+
values[second] = point.value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const coveredSeconds = values.reduce((count, value) => count + (value == null ? 0 : 1), 0);
|
|
62
|
+
const zeroSeconds = values.reduce((count, value) => count + (value === 0 ? 1 : 0), 0);
|
|
63
|
+
return {
|
|
64
|
+
values,
|
|
65
|
+
coverage: {
|
|
66
|
+
observedSamples: points.length,
|
|
67
|
+
coveredSeconds,
|
|
68
|
+
missingSeconds: durationSeconds - coveredSeconds,
|
|
69
|
+
zeroSeconds,
|
|
70
|
+
coveragePct: durationSeconds > 0 ? round((coveredSeconds / durationSeconds) * 100) : 0,
|
|
71
|
+
medianSampleIntervalSeconds: medianInterval,
|
|
72
|
+
largestGapSeconds: gaps.length > 0 ? Math.max(...gaps) : null,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function computeNormalizedWatts(values) {
|
|
77
|
+
if (values.length < 30 || values.some((value) => value == null))
|
|
78
|
+
return null;
|
|
79
|
+
const result = computeNormalizedPower(values.map((power, index) => ({
|
|
80
|
+
activity_id: "activity",
|
|
81
|
+
activity_date: "1970-01-01",
|
|
82
|
+
activity_name: null,
|
|
83
|
+
power: power ?? 0,
|
|
84
|
+
interval_s: 1,
|
|
85
|
+
index,
|
|
86
|
+
})));
|
|
87
|
+
return result[0]?.normalizedPower ?? null;
|
|
88
|
+
}
|
|
89
|
+
function zoneDistribution(values, threshold, upperPcts) {
|
|
90
|
+
if (threshold == null || threshold <= 0 || !upperPcts || upperPcts.length === 0)
|
|
91
|
+
return null;
|
|
92
|
+
const counts = Array.from({ length: upperPcts.length + 1 }, () => 0);
|
|
93
|
+
for (const value of values) {
|
|
94
|
+
if (value == null)
|
|
95
|
+
continue;
|
|
96
|
+
const zoneIndex = upperPcts.findIndex((upperPct) => value < threshold * upperPct);
|
|
97
|
+
const resolvedIndex = zoneIndex === -1 ? counts.length - 1 : zoneIndex;
|
|
98
|
+
counts[resolvedIndex] = (counts[resolvedIndex] ?? 0) + 1;
|
|
99
|
+
}
|
|
100
|
+
const total = counts.reduce((sum, seconds) => sum + seconds, 0);
|
|
101
|
+
return {
|
|
102
|
+
threshold,
|
|
103
|
+
upperPcts: [...upperPcts],
|
|
104
|
+
zones: counts.map((seconds, index) => ({
|
|
105
|
+
zone: index + 1,
|
|
106
|
+
seconds,
|
|
107
|
+
percent: total > 0 ? round((seconds / total) * 100) : 0,
|
|
108
|
+
})),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function pairedEfficiency(power, heartRate, start = 0, end = power.length) {
|
|
112
|
+
let powerTotal = 0;
|
|
113
|
+
let heartRateTotal = 0;
|
|
114
|
+
let seconds = 0;
|
|
115
|
+
for (let index = start; index < end; index++) {
|
|
116
|
+
const watts = power[index];
|
|
117
|
+
const bpm = heartRate[index];
|
|
118
|
+
if (watts == null || bpm == null || bpm <= 0)
|
|
119
|
+
continue;
|
|
120
|
+
powerTotal += watts;
|
|
121
|
+
heartRateTotal += bpm;
|
|
122
|
+
seconds++;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
ratio: seconds > 0 && heartRateTotal > 0 ? powerTotal / heartRateTotal : null,
|
|
126
|
+
seconds,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function intervalMetrics(interval, source, power, heartRate, cadence) {
|
|
130
|
+
const start = Math.max(0, Math.floor(interval.startOffsetSeconds));
|
|
131
|
+
const end = Math.min(power.length, Math.ceil(interval.endOffsetSeconds));
|
|
132
|
+
const powerSlice = power.slice(start, end);
|
|
133
|
+
const averagePower = average(powerSlice);
|
|
134
|
+
const normalizedPower = computeNormalizedWatts(powerSlice);
|
|
135
|
+
const targetPower = interval.targetPowerWatts ?? null;
|
|
136
|
+
return {
|
|
137
|
+
index: interval.index,
|
|
138
|
+
type: interval.type,
|
|
139
|
+
label: interval.label ?? null,
|
|
140
|
+
source,
|
|
141
|
+
startOffsetSeconds: start,
|
|
142
|
+
endOffsetSeconds: end,
|
|
143
|
+
durationSeconds: Math.max(0, end - start),
|
|
144
|
+
averagePowerWatts: averagePower == null ? null : round(averagePower),
|
|
145
|
+
normalizedPowerWatts: normalizedPower,
|
|
146
|
+
averageHeartRateBpm: (() => {
|
|
147
|
+
const value = average(heartRate.slice(start, end));
|
|
148
|
+
return value == null ? null : round(value);
|
|
149
|
+
})(),
|
|
150
|
+
averageCadenceRpm: (() => {
|
|
151
|
+
const value = average(cadence.slice(start, end));
|
|
152
|
+
return value == null ? null : round(value);
|
|
153
|
+
})(),
|
|
154
|
+
targetPowerWatts: targetPower,
|
|
155
|
+
completionPct: targetPower != null && targetPower > 0 && averagePower != null
|
|
156
|
+
? round((averagePower / targetPower) * 100)
|
|
157
|
+
: null,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function detectIntervals(power, ftpWatts) {
|
|
161
|
+
if (ftpWatts == null || ftpWatts <= 0 || power.length < 30)
|
|
162
|
+
return null;
|
|
163
|
+
const thresholdWatts = ftpWatts * 1.05;
|
|
164
|
+
const candidates = [];
|
|
165
|
+
let start = null;
|
|
166
|
+
for (let index = 0; index <= power.length; index++) {
|
|
167
|
+
const isWork = index < power.length && power[index] != null && (power[index] ?? 0) >= thresholdWatts;
|
|
168
|
+
if (isWork && start == null)
|
|
169
|
+
start = index;
|
|
170
|
+
if ((!isWork || index === power.length) && start != null) {
|
|
171
|
+
const previous = candidates.at(-1);
|
|
172
|
+
if (previous && start - previous.end < 15)
|
|
173
|
+
previous.end = index;
|
|
174
|
+
else
|
|
175
|
+
candidates.push({ start, end: index });
|
|
176
|
+
start = null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const workIntervals = candidates.filter((candidate) => {
|
|
180
|
+
if (candidate.end - candidate.start < 30)
|
|
181
|
+
return false;
|
|
182
|
+
let rollingTotal = 0;
|
|
183
|
+
let rollingCount = 0;
|
|
184
|
+
for (let index = candidate.start; index < candidate.end; index++) {
|
|
185
|
+
const watts = power[index];
|
|
186
|
+
if (watts != null) {
|
|
187
|
+
rollingTotal += watts;
|
|
188
|
+
rollingCount++;
|
|
189
|
+
}
|
|
190
|
+
if (index - candidate.start >= 30) {
|
|
191
|
+
const expired = power[index - 30];
|
|
192
|
+
if (expired != null) {
|
|
193
|
+
rollingTotal -= expired;
|
|
194
|
+
rollingCount--;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (rollingCount === 30 && rollingTotal / 30 >= thresholdWatts)
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
});
|
|
202
|
+
if (workIntervals.length === 0)
|
|
203
|
+
return null;
|
|
204
|
+
const intervals = [];
|
|
205
|
+
for (let index = 0; index < workIntervals.length; index++) {
|
|
206
|
+
const work = workIntervals[index];
|
|
207
|
+
if (!work)
|
|
208
|
+
continue;
|
|
209
|
+
intervals.push({
|
|
210
|
+
index: intervals.length + 1,
|
|
211
|
+
type: "work",
|
|
212
|
+
label: null,
|
|
213
|
+
startOffsetSeconds: work.start,
|
|
214
|
+
endOffsetSeconds: work.end,
|
|
215
|
+
targetPowerWatts: null,
|
|
216
|
+
});
|
|
217
|
+
const next = workIntervals[index + 1];
|
|
218
|
+
if (next && next.start > work.end) {
|
|
219
|
+
intervals.push({
|
|
220
|
+
index: intervals.length + 1,
|
|
221
|
+
type: "recovery",
|
|
222
|
+
label: null,
|
|
223
|
+
startOffsetSeconds: work.end,
|
|
224
|
+
endOffsetSeconds: next.start,
|
|
225
|
+
targetPowerWatts: null,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return { intervals, thresholdWatts };
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Compute per-activity cycling metrics from native samples without extrapolating
|
|
233
|
+
* through dropouts. Every coverage field is expressed against elapsed duration.
|
|
234
|
+
*/
|
|
235
|
+
export function computeCyclingWorkoutMetrics(input) {
|
|
236
|
+
const durationSeconds = Math.max(0, Math.floor(input.durationSeconds));
|
|
237
|
+
const power = resampleStream(input.samples, durationSeconds, (sample) => sample.powerWatts);
|
|
238
|
+
const heartRate = resampleStream(input.samples, durationSeconds, (sample) => sample.heartRateBpm);
|
|
239
|
+
const cadence = resampleStream(input.samples, durationSeconds, (sample) => sample.cadenceRpm);
|
|
240
|
+
const unavailableReasons = [];
|
|
241
|
+
const addUnavailable = (metric, reason) => {
|
|
242
|
+
unavailableReasons.push({ metric, reason });
|
|
243
|
+
};
|
|
244
|
+
const averagePower = average(power.values);
|
|
245
|
+
const normalizedPower = computeNormalizedWatts(power.values);
|
|
246
|
+
if (normalizedPower == null) {
|
|
247
|
+
addUnavailable("normalized_power", power.coverage.coveragePct < 90
|
|
248
|
+
? "power coverage is below 90%"
|
|
249
|
+
: power.coverage.missingSeconds > 0
|
|
250
|
+
? "power samples contain missing elapsed seconds"
|
|
251
|
+
: "fewer than 30 seconds of power are available");
|
|
252
|
+
}
|
|
253
|
+
const ftpWatts = input.settings?.ftpWatts;
|
|
254
|
+
const validFtp = ftpWatts != null && Number.isFinite(ftpWatts) && ftpWatts > 0 ? ftpWatts : null;
|
|
255
|
+
if (validFtp == null) {
|
|
256
|
+
addUnavailable("intensity_factor", "no valid FTP was effective for this activity");
|
|
257
|
+
addUnavailable("training_stress_score", "no valid FTP was effective for this activity");
|
|
258
|
+
}
|
|
259
|
+
else if (normalizedPower == null) {
|
|
260
|
+
addUnavailable("intensity_factor", "normalized power is unavailable");
|
|
261
|
+
addUnavailable("training_stress_score", "normalized power is unavailable");
|
|
262
|
+
}
|
|
263
|
+
const averageHeartRate = average(heartRate.values);
|
|
264
|
+
if (averageHeartRate == null)
|
|
265
|
+
addUnavailable("heart_rate", "no heart-rate samples are available");
|
|
266
|
+
const averageCadence = average(cadence.values);
|
|
267
|
+
if (averageCadence == null)
|
|
268
|
+
addUnavailable("cadence", "no cadence samples are available");
|
|
269
|
+
const efficiency = pairedEfficiency(power.values, heartRate.values);
|
|
270
|
+
if (efficiency.ratio == null) {
|
|
271
|
+
addUnavailable("aerobic_efficiency", "no synchronized power and heart-rate samples are available");
|
|
272
|
+
}
|
|
273
|
+
const midpoint = Math.floor(durationSeconds / 2);
|
|
274
|
+
const firstHalf = pairedEfficiency(power.values, heartRate.values, 0, midpoint);
|
|
275
|
+
const secondHalf = pairedEfficiency(power.values, heartRate.values, midpoint, durationSeconds);
|
|
276
|
+
const driftCoverageRequired = midpoint * 0.9;
|
|
277
|
+
const driftAvailable = durationSeconds >= 1_200 &&
|
|
278
|
+
firstHalf.ratio != null &&
|
|
279
|
+
secondHalf.ratio != null &&
|
|
280
|
+
firstHalf.seconds >= driftCoverageRequired &&
|
|
281
|
+
secondHalf.seconds >= (durationSeconds - midpoint) * 0.9;
|
|
282
|
+
if (!driftAvailable) {
|
|
283
|
+
addUnavailable("cardiac_drift", durationSeconds < 1_200
|
|
284
|
+
? "activity is shorter than 20 minutes"
|
|
285
|
+
: "paired power and heart-rate coverage is below 90% in one or both halves");
|
|
286
|
+
}
|
|
287
|
+
const detected = input.intervals.length === 0 ? detectIntervals(power.values, validFtp) : null;
|
|
288
|
+
const intervalInputs = input.intervals.length > 0 ? input.intervals : (detected?.intervals ?? []);
|
|
289
|
+
const intervalSource = input.intervals.length > 0
|
|
290
|
+
? "recorded"
|
|
291
|
+
: detected && detected.intervals.length > 0
|
|
292
|
+
? "inferred"
|
|
293
|
+
: "none";
|
|
294
|
+
const intensityFactor = validFtp != null && normalizedPower != null ? normalizedPower / validFtp : null;
|
|
295
|
+
return {
|
|
296
|
+
durationSeconds,
|
|
297
|
+
power: {
|
|
298
|
+
averageWatts: averagePower == null ? null : round(averagePower),
|
|
299
|
+
normalizedWatts: normalizedPower,
|
|
300
|
+
variabilityIndex: normalizedPower != null && averagePower != null && averagePower > 0
|
|
301
|
+
? round(normalizedPower / averagePower, 3)
|
|
302
|
+
: null,
|
|
303
|
+
workKilojoules: averagePower == null
|
|
304
|
+
? null
|
|
305
|
+
: round(power.values.reduce((total, watts) => total + (watts ?? 0), 0) / 1_000),
|
|
306
|
+
intensityFactor: intensityFactor == null ? null : round(intensityFactor, 3),
|
|
307
|
+
trainingStressScore: validFtp != null && normalizedPower != null
|
|
308
|
+
? round(computePowerTss(normalizedPower, validFtp, durationSeconds / 60))
|
|
309
|
+
: null,
|
|
310
|
+
},
|
|
311
|
+
heartRate: {
|
|
312
|
+
averageBpm: averageHeartRate == null ? null : round(averageHeartRate),
|
|
313
|
+
maximumBpm: heartRate.coverage.coveredSeconds === 0
|
|
314
|
+
? null
|
|
315
|
+
: Math.max(...heartRate.values.flatMap((value) => (value == null ? [] : [value]))),
|
|
316
|
+
},
|
|
317
|
+
cadence: { averageRpm: averageCadence == null ? null : round(averageCadence) },
|
|
318
|
+
aerobicEfficiency: {
|
|
319
|
+
powerToHeartRateRatio: efficiency.ratio == null ? null : round(efficiency.ratio, 3),
|
|
320
|
+
pairedSeconds: efficiency.seconds,
|
|
321
|
+
},
|
|
322
|
+
cardiacDrift: {
|
|
323
|
+
percent: driftAvailable && firstHalf.ratio != null && secondHalf.ratio != null
|
|
324
|
+
? round(((firstHalf.ratio - secondHalf.ratio) / firstHalf.ratio) * 100)
|
|
325
|
+
: null,
|
|
326
|
+
firstHalfPowerToHeartRate: driftAvailable && firstHalf.ratio != null ? round(firstHalf.ratio, 3) : null,
|
|
327
|
+
secondHalfPowerToHeartRate: driftAvailable && secondHalf.ratio != null ? round(secondHalf.ratio, 3) : null,
|
|
328
|
+
pairedSeconds: driftAvailable ? firstHalf.seconds + secondHalf.seconds : 0,
|
|
329
|
+
method: "equal_elapsed_time_halves_power_to_heart_rate",
|
|
330
|
+
},
|
|
331
|
+
powerZones: zoneDistribution(power.values, validFtp, input.settings?.powerZoneUpperPcts),
|
|
332
|
+
heartRateZones: zoneDistribution(heartRate.values, input.settings?.thresholdHeartRateBpm, input.settings?.heartRateZoneUpperPcts),
|
|
333
|
+
coverage: {
|
|
334
|
+
power: power.coverage,
|
|
335
|
+
heartRate: heartRate.coverage,
|
|
336
|
+
cadence: cadence.coverage,
|
|
337
|
+
},
|
|
338
|
+
intervalSource,
|
|
339
|
+
intervalDetection: detected == null
|
|
340
|
+
? null
|
|
341
|
+
: {
|
|
342
|
+
method: "30_second_average_above_105_percent_ftp",
|
|
343
|
+
thresholdWatts: detected.thresholdWatts,
|
|
344
|
+
minimumWorkSeconds: 30,
|
|
345
|
+
},
|
|
346
|
+
intervals: intervalInputs.map((interval) => intervalMetrics(interval, intervalSource === "recorded" ? "recorded" : "inferred", power.values, heartRate.values, cadence.values)),
|
|
347
|
+
unavailableReasons,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
//# sourceMappingURL=cycling-workout-metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycling-workout-metrics.js","sourceRoot":"","sources":["../src/cycling-workout-metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AA2G7D,SAAS,KAAK,CAAC,KAAa,EAAE,QAAQ,GAAG,CAAC;IACxC,MAAM,UAAU,GAAG,EAAE,IAAI,QAAQ,CAAC;IAClC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,UAAU,CAAC;AACrD,CAAC;AAED,SAAS,OAAO,CAAC,MAA4B;IAC3C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,IAAI,IAAI;YAAE,SAAS;QAC5B,KAAK,IAAI,KAAK,CAAC;QACf,KAAK,EAAE,CAAC;IACV,CAAC;IACD,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC;AAC5C,CAAC;AAED,SAAS,MAAM,CAAC,MAAgB;IAC9B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAChF,CAAC;AAED,SAAS,cAAc,CACrB,OAA+B,EAC/B,eAAuB,EACvB,MAAmE;IAEnE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;YAAE,SAAS;QACpE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,MAAM,IAAI,CAAC,IAAI,MAAM,GAAG,eAAe;YAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;SACnC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;SAC7C,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;IAChG,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,cAAc,GAAG,cAAc,IAAI,CAAC,CAAC;IAC3C,MAAM,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,MAAM,GAAyB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAEzF,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACnD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC/B,MAAM,WAAW,GAAG,IAAI;YACtB,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,mBAAmB;gBACjD,CAAC,CAAC,IAAI,CAAC,MAAM;gBACb,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,cAAc;YACjC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,cAAc,CAAC;QAClC,KAAK,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;YAC1F,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAClC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACjD,CAAC,CACF,CAAC;IACF,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAS,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9F,OAAO;QACL,MAAM;QACN,QAAQ,EAAE;YACR,eAAe,EAAE,MAAM,CAAC,MAAM;YAC9B,cAAc;YACd,cAAc,EAAE,eAAe,GAAG,cAAc;YAChD,WAAW;YACX,WAAW,EAAE,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,cAAc,GAAG,eAAe,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACtF,2BAA2B,EAAE,cAAc;YAC3C,iBAAiB,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;SAC9D;KACF,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,MAA4B;IAC1D,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,MAAM,MAAM,GAAG,sBAAsB,CACnC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,WAAW,EAAE,UAAU;QACvB,aAAa,EAAE,YAAY;QAC3B,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,KAAK,IAAI,CAAC;QACjB,UAAU,EAAE,CAAC;QACb,KAAK;KACN,CAAC,CAAC,CACJ,CAAC;IACF,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,eAAe,IAAI,IAAI,CAAC;AAC5C,CAAC;AAED,SAAS,gBAAgB,CACvB,MAA4B,EAC5B,SAAoC,EACpC,SAAsC;IAEtC,IAAI,SAAS,IAAI,IAAI,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7F,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACrE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,IAAI,IAAI;YAAE,SAAS;QAC5B,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;QAClF,MAAM,aAAa,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvE,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;IAChE,OAAO;QACL,SAAS;QACT,SAAS,EAAE,CAAC,GAAG,SAAS,CAAC;QACzB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACrC,IAAI,EAAE,KAAK,GAAG,CAAC;YACf,OAAO;YACP,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SACxD,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CACvB,KAA2B,EAC3B,SAA+B,EAC/B,KAAK,GAAG,CAAC,EACT,GAAG,GAAG,KAAK,CAAC,MAAM;IAElB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,IAAI,KAAK,GAAG,KAAK,EAAE,KAAK,GAAG,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,KAAK,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC;YAAE,SAAS;QACvD,UAAU,IAAI,KAAK,CAAC;QACpB,cAAc,IAAI,GAAG,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO;QACL,KAAK,EAAE,OAAO,GAAG,CAAC,IAAI,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI;QAC7E,OAAO;KACR,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CACtB,QAAqC,EACrC,MAA+B,EAC/B,KAA2B,EAC3B,SAA+B,EAC/B,OAA6B;IAE7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACzE,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACzC,MAAM,eAAe,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,QAAQ,CAAC,gBAAgB,IAAI,IAAI,CAAC;IACtD,OAAO;QACL,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,IAAI;QAC7B,MAAM;QACN,kBAAkB,EAAE,KAAK;QACzB,gBAAgB,EAAE,GAAG;QACrB,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC;QACzC,iBAAiB,EAAE,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC;QACpE,oBAAoB,EAAE,eAAe;QACrC,mBAAmB,EAAE,CAAC,GAAG,EAAE;YACzB,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;YACnD,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC,CAAC,EAAE;QACJ,iBAAiB,EAAE,CAAC,GAAG,EAAE;YACvB,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;YACjD,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7C,CAAC,CAAC,EAAE;QACJ,gBAAgB,EAAE,WAAW;QAC7B,aAAa,EACX,WAAW,IAAI,IAAI,IAAI,WAAW,GAAG,CAAC,IAAI,YAAY,IAAI,IAAI;YAC5D,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC;YAC3C,CAAC,CAAC,IAAI;KACX,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CACtB,KAA2B,EAC3B,QAAuB;IAEvB,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IACxE,MAAM,cAAc,GAAG,QAAQ,GAAG,IAAI,CAAC;IACvC,MAAM,UAAU,GAA0C,EAAE,CAAC;IAC7D,IAAI,KAAK,GAAkB,IAAI,CAAC;IAEhC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACnD,MAAM,MAAM,GACV,KAAK,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,cAAc,CAAC;QACxF,IAAI,MAAM,IAAI,KAAK,IAAI,IAAI;YAAE,KAAK,GAAG,KAAK,CAAC;QAC3C,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YACzD,MAAM,QAAQ,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,IAAI,QAAQ,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,GAAG,EAAE;gBAAE,QAAQ,CAAC,GAAG,GAAG,KAAK,CAAC;;gBAC3D,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5C,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE;QACpD,IAAI,SAAS,CAAC,GAAG,GAAG,SAAS,CAAC,KAAK,GAAG,EAAE;YAAE,OAAO,KAAK,CAAC;QACvD,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,KAAK,IAAI,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC;YACjE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3B,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;gBAClB,YAAY,IAAI,KAAK,CAAC;gBACtB,YAAY,EAAE,CAAC;YACjB,CAAC;YACD,IAAI,KAAK,GAAG,SAAS,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;gBAClC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;gBAClC,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;oBACpB,YAAY,IAAI,OAAO,CAAC;oBACxB,YAAY,EAAE,CAAC;gBACjB,CAAC;YACH,CAAC;YACD,IAAI,YAAY,KAAK,EAAE,IAAI,YAAY,GAAG,EAAE,IAAI,cAAc;gBAAE,OAAO,IAAI,CAAC;QAC9E,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IACH,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5C,MAAM,SAAS,GAAkC,EAAE,CAAC;IACpD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,SAAS,CAAC,IAAI,CAAC;YACb,KAAK,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;YAC3B,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,IAAI;YACX,kBAAkB,EAAE,IAAI,CAAC,KAAK;YAC9B,gBAAgB,EAAE,IAAI,CAAC,GAAG;YAC1B,gBAAgB,EAAE,IAAI;SACvB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACtC,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAClC,SAAS,CAAC,IAAI,CAAC;gBACb,KAAK,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC;gBAC3B,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,IAAI;gBACX,kBAAkB,EAAE,IAAI,CAAC,GAAG;gBAC5B,gBAAgB,EAAE,IAAI,CAAC,KAAK;gBAC5B,gBAAgB,EAAE,IAAI;aACvB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAC1C,KAAiC;IAEjC,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5F,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAClG,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC9F,MAAM,kBAAkB,GAAgD,EAAE,CAAC;IAC3E,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,MAAc,EAAE,EAAE;QACxD,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,eAAe,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7D,IAAI,eAAe,IAAI,IAAI,EAAE,CAAC;QAC5B,cAAc,CACZ,kBAAkB,EAClB,KAAK,CAAC,QAAQ,CAAC,WAAW,GAAG,EAAE;YAC7B,CAAC,CAAC,6BAA6B;YAC/B,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,cAAc,GAAG,CAAC;gBACjC,CAAC,CAAC,+CAA+C;gBACjD,CAAC,CAAC,8CAA8C,CACrD,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC1C,MAAM,QAAQ,GAAG,QAAQ,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACjG,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,cAAc,CAAC,kBAAkB,EAAE,8CAA8C,CAAC,CAAC;QACnF,cAAc,CAAC,uBAAuB,EAAE,8CAA8C,CAAC,CAAC;IAC1F,CAAC;SAAM,IAAI,eAAe,IAAI,IAAI,EAAE,CAAC;QACnC,cAAc,CAAC,kBAAkB,EAAE,iCAAiC,CAAC,CAAC;QACtE,cAAc,CAAC,uBAAuB,EAAE,iCAAiC,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,gBAAgB,IAAI,IAAI;QAAE,cAAc,CAAC,YAAY,EAAE,qCAAqC,CAAC,CAAC;IAClG,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,cAAc,IAAI,IAAI;QAAE,cAAc,CAAC,SAAS,EAAE,kCAAkC,CAAC,CAAC;IAE1F,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACpE,IAAI,UAAU,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QAC7B,cAAc,CACZ,oBAAoB,EACpB,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;IAChF,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;IAC/F,MAAM,qBAAqB,GAAG,QAAQ,GAAG,GAAG,CAAC;IAC7C,MAAM,cAAc,GAClB,eAAe,IAAI,KAAK;QACxB,SAAS,CAAC,KAAK,IAAI,IAAI;QACvB,UAAU,CAAC,KAAK,IAAI,IAAI;QACxB,SAAS,CAAC,OAAO,IAAI,qBAAqB;QAC1C,UAAU,CAAC,OAAO,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC;IAC3D,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,cAAc,CACZ,eAAe,EACf,eAAe,GAAG,KAAK;YACrB,CAAC,CAAC,qCAAqC;YACvC,CAAC,CAAC,yEAAyE,CAC9E,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/F,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC;IAClG,MAAM,cAAc,GAClB,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;QACxB,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;YACzC,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,MAAM,CAAC;IAEf,MAAM,eAAe,GACnB,QAAQ,IAAI,IAAI,IAAI,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,OAAO;QACL,eAAe;QACf,KAAK,EAAE;YACL,YAAY,EAAE,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC;YAC/D,eAAe,EAAE,eAAe;YAChC,gBAAgB,EACd,eAAe,IAAI,IAAI,IAAI,YAAY,IAAI,IAAI,IAAI,YAAY,GAAG,CAAC;gBACjE,CAAC,CAAC,KAAK,CAAC,eAAe,GAAG,YAAY,EAAE,CAAC,CAAC;gBAC1C,CAAC,CAAC,IAAI;YACV,cAAc,EACZ,YAAY,IAAI,IAAI;gBAClB,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAS,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;YAC3F,eAAe,EAAE,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;YAC3E,mBAAmB,EACjB,QAAQ,IAAI,IAAI,IAAI,eAAe,IAAI,IAAI;gBACzC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,eAAe,EAAE,QAAQ,EAAE,eAAe,GAAG,EAAE,CAAC,CAAC;gBACzE,CAAC,CAAC,IAAI;SACX;QACD,SAAS,EAAE;YACT,UAAU,EAAE,gBAAgB,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC;YACrE,UAAU,EACR,SAAS,CAAC,QAAQ,CAAC,cAAc,KAAK,CAAC;gBACrC,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACvF;QACD,OAAO,EAAE,EAAE,UAAU,EAAE,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE;QAC9E,iBAAiB,EAAE;YACjB,qBAAqB,EAAE,UAAU,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;YACnF,aAAa,EAAE,UAAU,CAAC,OAAO;SAClC;QACD,YAAY,EAAE;YACZ,OAAO,EACL,cAAc,IAAI,SAAS,CAAC,KAAK,IAAI,IAAI,IAAI,UAAU,CAAC,KAAK,IAAI,IAAI;gBACnE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;gBACvE,CAAC,CAAC,IAAI;YACV,yBAAyB,EACvB,cAAc,IAAI,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;YAC9E,0BAA0B,EACxB,cAAc,IAAI,UAAU,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;YAChF,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1E,MAAM,EAAE,+CAA+C;SACxD;QACD,UAAU,EAAE,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,kBAAkB,CAAC;QACxF,cAAc,EAAE,gBAAgB,CAC9B,SAAS,CAAC,MAAM,EAChB,KAAK,CAAC,QAAQ,EAAE,qBAAqB,EACrC,KAAK,CAAC,QAAQ,EAAE,sBAAsB,CACvC;QACD,QAAQ,EAAE;YACR,KAAK,EAAE,KAAK,CAAC,QAAQ;YACrB,SAAS,EAAE,SAAS,CAAC,QAAQ;YAC7B,OAAO,EAAE,OAAO,CAAC,QAAQ;SAC1B;QACD,cAAc;QACd,iBAAiB,EACf,QAAQ,IAAI,IAAI;YACd,CAAC,CAAC,IAAI;YACN,CAAC,CAAC;gBACE,MAAM,EAAE,yCAAyC;gBACjD,cAAc,EAAE,QAAQ,CAAC,cAAc;gBACvC,kBAAkB,EAAE,EAAE;aACvB;QACP,SAAS,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CACzC,eAAe,CACb,QAAQ,EACR,cAAc,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EACvD,KAAK,CAAC,MAAM,EACZ,SAAS,CAAC,MAAM,EAChB,OAAO,CAAC,MAAM,CACf,CACF;QACD,kBAAkB;KACnB,CAAC;AACJ,CAAC"}
|
package/dist/power-analysis.d.ts
CHANGED
|
@@ -18,6 +18,15 @@ export interface CriticalPowerModel {
|
|
|
18
18
|
wPrime: number;
|
|
19
19
|
r2: number;
|
|
20
20
|
}
|
|
21
|
+
export interface CriticalPowerModelDiagnostics extends CriticalPowerModel {
|
|
22
|
+
rmse: number;
|
|
23
|
+
points: Array<{
|
|
24
|
+
durationSeconds: number;
|
|
25
|
+
observedPower: number;
|
|
26
|
+
predictedPower: number;
|
|
27
|
+
residualPower: number;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
21
30
|
/**
|
|
22
31
|
* Fit Morton's 2-parameter Critical Power model (Monod-Scherrer).
|
|
23
32
|
*
|
|
@@ -29,6 +38,11 @@ export interface CriticalPowerModel {
|
|
|
29
38
|
* efforts (<120s) and long-duration bests that are suppressed by interval
|
|
30
39
|
* training recovery periods (>600s).
|
|
31
40
|
*/
|
|
41
|
+
export declare function fitCriticalPowerWithDiagnostics(points: {
|
|
42
|
+
durationSeconds: number;
|
|
43
|
+
bestPower: number;
|
|
44
|
+
}[]): CriticalPowerModelDiagnostics | null;
|
|
45
|
+
/** Fit the stable three-field CP model used by existing analytics responses. */
|
|
32
46
|
export declare function fitCriticalPower(points: {
|
|
33
47
|
durationSeconds: number;
|
|
34
48
|
bestPower: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-analysis.d.ts","sourceRoot":"","sources":["../src/power-analysis.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,eAAO,MAAM,kBAAkB,UAE9B,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAelD,CAAC;AAIF,0DAA0D;AAC1D,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EAAE,EACZ,EAAE,EAAE,MAAM,EAAE,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAmBlD;AAID,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,GACvD,kBAAkB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"power-analysis.d.ts","sourceRoot":"","sources":["../src/power-analysis.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,eAAO,MAAM,kBAAkB,UAE9B,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAelD,CAAC;AAIF,0DAA0D;AAC1D,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EAAE,EACZ,EAAE,EAAE,MAAM,EAAE,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAmBlD;AAID,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,6BAA8B,SAAQ,kBAAkB;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;QACZ,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC,CAAC;CACJ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,GACvD,6BAA6B,GAAG,IAAI,CAkCtC;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,GACvD,kBAAkB,GAAG,IAAI,CAG3B;AAID,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,CAAC,SAAS;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAC5E,OAAO,EAAE,CAAC,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,CAiBlC;AAID,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,eAAe,EAAE,CAoChF;AAID,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,qBAAqB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,qBAAqB,EAAE,GAAG,qBAAqB,EAAE,CAiChG"}
|
package/dist/power-analysis.js
CHANGED
|
@@ -55,21 +55,39 @@ export function linearRegression(xs, ys) {
|
|
|
55
55
|
* efforts (<120s) and long-duration bests that are suppressed by interval
|
|
56
56
|
* training recovery periods (>600s).
|
|
57
57
|
*/
|
|
58
|
-
export function
|
|
58
|
+
export function fitCriticalPowerWithDiagnostics(points) {
|
|
59
59
|
const valid = points.filter((p) => p.durationSeconds >= 120 && p.durationSeconds <= 600 && p.bestPower > 0);
|
|
60
60
|
if (valid.length < 3)
|
|
61
61
|
return null;
|
|
62
62
|
const xs = valid.map((p) => p.durationSeconds);
|
|
63
63
|
const ys = valid.map((p) => p.bestPower * p.durationSeconds);
|
|
64
64
|
const { slope: cp, intercept: wPrime, r2 } = linearRegression(xs, ys);
|
|
65
|
-
if (cp <= 0)
|
|
65
|
+
if (cp <= 0 || wPrime <= 0)
|
|
66
66
|
return null;
|
|
67
|
+
const fittedPoints = valid.map((point) => {
|
|
68
|
+
const predictedPower = cp + wPrime / point.durationSeconds;
|
|
69
|
+
return {
|
|
70
|
+
durationSeconds: point.durationSeconds,
|
|
71
|
+
observedPower: point.bestPower,
|
|
72
|
+
predictedPower: Math.round(predictedPower * 10) / 10,
|
|
73
|
+
residualPower: Math.round((point.bestPower - predictedPower) * 10) / 10,
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
const rmse = Math.sqrt(fittedPoints.reduce((total, point) => total + point.residualPower ** 2, 0) /
|
|
77
|
+
fittedPoints.length);
|
|
67
78
|
return {
|
|
68
79
|
cp: Math.round(cp),
|
|
69
80
|
wPrime: Math.round(wPrime),
|
|
70
81
|
r2: Math.round(r2 * 1000) / 1000,
|
|
82
|
+
rmse: Math.round(rmse * 10) / 10,
|
|
83
|
+
points: fittedPoints,
|
|
71
84
|
};
|
|
72
85
|
}
|
|
86
|
+
/** Fit the stable three-field CP model used by existing analytics responses. */
|
|
87
|
+
export function fitCriticalPower(points) {
|
|
88
|
+
const model = fitCriticalPowerWithDiagnostics(points);
|
|
89
|
+
return model ? { cp: model.cp, wPrime: model.wPrime, r2: model.r2 } : null;
|
|
90
|
+
}
|
|
73
91
|
/** Group pre-sorted samples by activity_id in a single pass. */
|
|
74
92
|
export function groupByActivity(samples) {
|
|
75
93
|
const groups = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-analysis.js","sourceRoot":"","sources":["../src/power-analysis.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"power-analysis.js","sourceRoot":"","sources":["../src/power-analysis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface PowerDurationSample {
|
|
2
|
+
elapsedSeconds: number;
|
|
3
|
+
watts: number;
|
|
4
|
+
}
|
|
5
|
+
export interface PowerDurationResult {
|
|
6
|
+
watts: number;
|
|
7
|
+
startOffsetSeconds: number;
|
|
8
|
+
observedSamples: number;
|
|
9
|
+
medianSampleIntervalSeconds: number;
|
|
10
|
+
largestGapSeconds: number;
|
|
11
|
+
coveragePct: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function referenceBestPower(samples: readonly PowerDurationSample[], durationSeconds: number): PowerDurationResult | null;
|
|
14
|
+
//# sourceMappingURL=power-duration-reference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power-duration-reference.d.ts","sourceRoot":"","sources":["../src/power-duration-reference.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,2BAA2B,EAAE,MAAM,CAAC;IACpC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AASD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,mBAAmB,EAAE,EACvC,eAAe,EAAE,MAAM,GACtB,mBAAmB,GAAG,IAAI,CAiF5B"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
function median(values) {
|
|
2
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
3
|
+
const midpoint = Math.floor(sorted.length / 2);
|
|
4
|
+
if (sorted.length % 2 === 1)
|
|
5
|
+
return sorted[midpoint] ?? Number.NaN;
|
|
6
|
+
return ((sorted[midpoint - 1] ?? 0) + (sorted[midpoint] ?? 0)) / 2;
|
|
7
|
+
}
|
|
8
|
+
export function referenceBestPower(samples, durationSeconds) {
|
|
9
|
+
if (!Number.isFinite(durationSeconds) || durationSeconds <= 0 || samples.length < 2) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
if (samples.some(({ elapsedSeconds, watts }) => !Number.isFinite(elapsedSeconds) || !Number.isFinite(watts) || watts < 0)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const sorted = [...samples].sort((left, right) => left.elapsedSeconds - right.elapsedSeconds);
|
|
16
|
+
const intervals = sorted.slice(1).map((sample, index) => {
|
|
17
|
+
const previous = sorted[index];
|
|
18
|
+
return previous ? sample.elapsedSeconds - previous.elapsedSeconds : Number.NaN;
|
|
19
|
+
});
|
|
20
|
+
if (intervals.some((interval) => interval <= 0))
|
|
21
|
+
return null;
|
|
22
|
+
const medianSampleIntervalSeconds = median(intervals);
|
|
23
|
+
if (durationSeconds < medianSampleIntervalSeconds)
|
|
24
|
+
return null;
|
|
25
|
+
const cumulativeEnergy = [0];
|
|
26
|
+
for (let index = 0; index < intervals.length; index += 1) {
|
|
27
|
+
cumulativeEnergy.push((cumulativeEnergy[index] ?? 0) + (sorted[index]?.watts ?? 0) * (intervals[index] ?? 0));
|
|
28
|
+
}
|
|
29
|
+
const continuityToleranceSeconds = Math.max(5, medianSampleIntervalSeconds * 2);
|
|
30
|
+
const finalOffset = sorted.at(-1)?.elapsedSeconds ?? 0;
|
|
31
|
+
let best = null;
|
|
32
|
+
for (let startIndex = 0; startIndex < sorted.length - 1; startIndex += 1) {
|
|
33
|
+
const startOffsetSeconds = sorted[startIndex]?.elapsedSeconds ?? 0;
|
|
34
|
+
const endOffsetSeconds = startOffsetSeconds + durationSeconds;
|
|
35
|
+
if (endOffsetSeconds > finalOffset)
|
|
36
|
+
continue;
|
|
37
|
+
let endSegmentIndex = startIndex;
|
|
38
|
+
while (endSegmentIndex < intervals.length &&
|
|
39
|
+
(sorted[endSegmentIndex + 1]?.elapsedSeconds ?? Number.POSITIVE_INFINITY) < endOffsetSeconds) {
|
|
40
|
+
endSegmentIndex += 1;
|
|
41
|
+
}
|
|
42
|
+
const endSegment = sorted[endSegmentIndex];
|
|
43
|
+
if (!endSegment)
|
|
44
|
+
continue;
|
|
45
|
+
const usedIntervals = intervals.slice(startIndex, endSegmentIndex + 1);
|
|
46
|
+
const largestGapSeconds = Math.max(...usedIntervals);
|
|
47
|
+
if (largestGapSeconds > continuityToleranceSeconds)
|
|
48
|
+
continue;
|
|
49
|
+
const endEnergy = (cumulativeEnergy[endSegmentIndex] ?? 0) +
|
|
50
|
+
endSegment.watts * (endOffsetSeconds - endSegment.elapsedSeconds);
|
|
51
|
+
const startEnergy = cumulativeEnergy[startIndex] ?? 0;
|
|
52
|
+
const watts = (endEnergy - startEnergy) / durationSeconds;
|
|
53
|
+
const observedSamples = sorted.filter((sample) => sample.elapsedSeconds >= startOffsetSeconds && sample.elapsedSeconds <= endOffsetSeconds).length;
|
|
54
|
+
const candidate = {
|
|
55
|
+
coveragePct: 100,
|
|
56
|
+
largestGapSeconds,
|
|
57
|
+
medianSampleIntervalSeconds,
|
|
58
|
+
observedSamples,
|
|
59
|
+
startOffsetSeconds,
|
|
60
|
+
watts,
|
|
61
|
+
};
|
|
62
|
+
if (best === null ||
|
|
63
|
+
candidate.watts > best.watts ||
|
|
64
|
+
(candidate.watts === best.watts && candidate.startOffsetSeconds < best.startOffsetSeconds)) {
|
|
65
|
+
best = candidate;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return best;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=power-duration-reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power-duration-reference.js","sourceRoot":"","sources":["../src/power-duration-reference.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dofek/training",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.41",
|
|
4
4
|
"description": "Training-science calculations for power, load, performance, and workout planning",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"LICENSE"
|
|
31
31
|
],
|
|
32
32
|
"exports": {
|
|
33
|
+
"./power-duration-reference": {
|
|
34
|
+
"types": "./dist/power-duration-reference.d.ts",
|
|
35
|
+
"import": "./dist/power-duration-reference.js"
|
|
36
|
+
},
|
|
33
37
|
"./*": {
|
|
34
38
|
"types": "./dist/*.d.ts",
|
|
35
39
|
"import": "./dist/*.js"
|
|
@@ -41,8 +45,8 @@
|
|
|
41
45
|
"dependencies": {
|
|
42
46
|
"@openbeta/sandbag": "0.0.55",
|
|
43
47
|
"zod": "4.4.3",
|
|
44
|
-
"@dofek/scoring": "0.1.
|
|
45
|
-
"@dofek/zones": "0.1.
|
|
48
|
+
"@dofek/scoring": "0.1.41",
|
|
49
|
+
"@dofek/zones": "0.1.41"
|
|
46
50
|
},
|
|
47
51
|
"scripts": {
|
|
48
52
|
"build": "tsc",
|