правки по графикам
Gitea Actions Demo / build_and_push (push) Successful in 45s
Details
Gitea Actions Demo / build_and_push (push) Successful in 45s
Details
This commit is contained in:
parent
2cfd863dbf
commit
f573aff39c
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* Computes Normalized Power (NP) over a slice of a workout, matching the
|
||||
* canonical Strava/TrainingPeaks algorithm and tolerating uneven sample dt.
|
||||
*
|
||||
* Exponential 4th-power smoothing with a 30 s time constant; points with
|
||||
* null power or non-finite timestamps are skipped, as are samples with
|
||||
* non-positive dt. Returns null when fewer than 2 valid points exist.
|
||||
*
|
||||
* @param times timestamps (ISO strings or ms numbers), 1 Hz
|
||||
* @param powers power in watts, null where the sensor has no data
|
||||
* @param startIdx inclusive start index
|
||||
* @param endIdx exclusive end index
|
||||
*/
|
||||
export function normalizedPower(
|
||||
times: Array<string | number>,
|
||||
powers: Array<number | null>,
|
||||
startIdx: number,
|
||||
endIdx: number,
|
||||
): number | null {
|
||||
const tau = 30;
|
||||
const end = Math.min(endIdx, times.length, powers.length);
|
||||
const start = Math.max(0, startIdx);
|
||||
|
||||
// Collect valid points: finite timestamp + non-null power.
|
||||
const t: number[] = [];
|
||||
const p: number[] = [];
|
||||
for (let i = start; i < end; i++) {
|
||||
const raw = times[i];
|
||||
const ms =
|
||||
typeof raw === "number" && Number.isFinite(raw)
|
||||
? raw
|
||||
: Number(new Date(raw as string));
|
||||
const w = powers[i];
|
||||
if (!Number.isFinite(ms) || w === null || !Number.isFinite(w)) {
|
||||
continue;
|
||||
}
|
||||
t.push(ms);
|
||||
p.push(w);
|
||||
}
|
||||
|
||||
const n = p.length;
|
||||
if (n < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Exponential smoothing of p^4; the last sample has no forward dt, so its
|
||||
// 4th power is added to the sum as its own smoothing value.
|
||||
let sm = Math.pow(p[0], 4);
|
||||
let sum = sm;
|
||||
let count = 1;
|
||||
for (let i = 1; i < n; i++) {
|
||||
const dt = (t[i] - t[i - 1]) / 1000;
|
||||
if (dt <= 0) {
|
||||
continue;
|
||||
}
|
||||
sm = sm + (Math.pow(p[i], 4) - sm) * (1 - Math.exp(-dt / tau));
|
||||
sum += sm;
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return Math.pow(sum / count, 0.25);
|
||||
}
|
||||
Loading…
Reference in New Issue