Replace power% field with intensity fraction
This commit is contained in:
parent
e6a7cc6e6f
commit
09e38cd615
|
|
@ -10,6 +10,6 @@ export type Workout = {
|
||||||
export type Interval = {
|
export type Interval = {
|
||||||
type: IntervalLabelTokenValue;
|
type: IntervalLabelTokenValue;
|
||||||
duration: number;
|
duration: number;
|
||||||
power: { from: number; to: number };
|
intensity: { from: number; to: number };
|
||||||
cadence?: number;
|
cadence?: number;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,11 @@ const parseIntervalParams = (tokens: Token[]): [IntervalData, Token[]] => {
|
||||||
} else if (token.type === "cadence") {
|
} else if (token.type === "cadence") {
|
||||||
data.cadence = token.value;
|
data.cadence = token.value;
|
||||||
tokens.shift();
|
tokens.shift();
|
||||||
} else if (token.type === "power") {
|
} else if (token.type === "intensity") {
|
||||||
data.power = { from: token.value, to: token.value };
|
data.intensity = { from: token.value, to: token.value };
|
||||||
tokens.shift();
|
tokens.shift();
|
||||||
} else if (token.type === "power-range") {
|
} else if (token.type === "intensity-range") {
|
||||||
data.power = { from: token.value[0], to: token.value[1] };
|
data.intensity = { from: token.value[0], to: token.value[1] };
|
||||||
tokens.shift();
|
tokens.shift();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
@ -72,7 +72,7 @@ const parseIntervalParams = (tokens: Token[]): [IntervalData, Token[]] => {
|
||||||
if (!("duration" in data)) {
|
if (!("duration" in data)) {
|
||||||
throw new Error("Duration not specified");
|
throw new Error("Duration not specified");
|
||||||
}
|
}
|
||||||
if (!("power" in data)) {
|
if (!("intensity" in data)) {
|
||||||
throw new Error("Power not specified");
|
throw new Error("Power not specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,11 +85,13 @@ const parseIntervals = (tokens: Token[]): Interval[] => {
|
||||||
while (tokens[0]) {
|
while (tokens[0]) {
|
||||||
const token = tokens.shift() as Token;
|
const token = tokens.shift() as Token;
|
||||||
if (token.type === "label" && isIntervalLabelTokenValue(token.value)) {
|
if (token.type === "label" && isIntervalLabelTokenValue(token.value)) {
|
||||||
const [{ duration, power, cadence }, rest] = parseIntervalParams(tokens);
|
const [{ duration, intensity, cadence }, rest] = parseIntervalParams(
|
||||||
|
tokens
|
||||||
|
);
|
||||||
intervals.push({
|
intervals.push({
|
||||||
type: token.value,
|
type: token.value,
|
||||||
duration,
|
duration,
|
||||||
power,
|
intensity,
|
||||||
cadence,
|
cadence,
|
||||||
});
|
});
|
||||||
tokens = rest;
|
tokens = rest;
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@ export type TextToken = {
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
export type NumberToken = {
|
export type NumberToken = {
|
||||||
type: "power" | "cadence" | "duration";
|
type: "intensity" | "cadence" | "duration";
|
||||||
value: number;
|
value: number;
|
||||||
};
|
};
|
||||||
export type PowerRangeToken = {
|
export type IntensityRangeToken = {
|
||||||
type: "power-range";
|
type: "intensity-range";
|
||||||
value: [number, number];
|
value: [number, number];
|
||||||
};
|
};
|
||||||
export type Token = LabelToken | TextToken | NumberToken | PowerRangeToken;
|
export type Token = LabelToken | TextToken | NumberToken | IntensityRangeToken;
|
||||||
|
|
||||||
const toInteger = (str: string): number => {
|
const toInteger = (str: string): number => {
|
||||||
return parseInt(str.replace(/[^0-9]/, ""), 10);
|
return parseInt(str.replace(/[^0-9]/, ""), 10);
|
||||||
|
|
@ -47,6 +47,8 @@ const toSeconds = (str: string): number => {
|
||||||
return seconds + minutes * 60 + (hours || 0) * 60 * 60;
|
return seconds + minutes * 60 + (hours || 0) * 60 * 60;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toFraction = (percentage: number): number => percentage / 100;
|
||||||
|
|
||||||
const tokenizeValueParam = (text: string): Token => {
|
const tokenizeValueParam = (text: string): Token => {
|
||||||
if (/^[0-9:]+$/.test(text)) {
|
if (/^[0-9:]+$/.test(text)) {
|
||||||
return { type: "duration", value: toSeconds(text) };
|
return { type: "duration", value: toSeconds(text) };
|
||||||
|
|
@ -55,11 +57,11 @@ const tokenizeValueParam = (text: string): Token => {
|
||||||
return { type: "cadence", value: toInteger(text) };
|
return { type: "cadence", value: toInteger(text) };
|
||||||
}
|
}
|
||||||
if (/^[0-9]+%..[0-9]+%$/.test(text)) {
|
if (/^[0-9]+%..[0-9]+%$/.test(text)) {
|
||||||
const [from, to] = text.split("..").map(toInteger);
|
const [from, to] = text.split("..").map(toInteger).map(toFraction);
|
||||||
return { type: "power-range", value: [from, to] };
|
return { type: "intensity-range", value: [from, to] };
|
||||||
}
|
}
|
||||||
if (/^[0-9]+%$/.test(text)) {
|
if (/^[0-9]+%$/.test(text)) {
|
||||||
return { type: "power", value: toInteger(text) };
|
return { type: "intensity", value: toFraction(toInteger(text)) };
|
||||||
}
|
}
|
||||||
throw new Error(`Unrecognized parameter "${text}"`);
|
throw new Error(`Unrecognized parameter "${text}"`);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
16
src/tss.ts
16
src/tss.ts
|
|
@ -8,9 +8,7 @@ import { Interval } from "./ast";
|
||||||
// W - power in watts
|
// W - power in watts
|
||||||
// IF - intensity factor (power / FTP)
|
// IF - intensity factor (power / FTP)
|
||||||
|
|
||||||
const steadyTss = (duration: number, power: number): number => {
|
const steadyTss = (duration: number, intensity: number): number => {
|
||||||
const intensity = power / 100;
|
|
||||||
|
|
||||||
return ((duration * intensity * intensity) / 3600) * 100;
|
return ((duration * intensity * intensity) / 3600) * 100;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -18,17 +16,17 @@ const rangeTss = (duration: number, from: number, to: number): number => {
|
||||||
let score = 0;
|
let score = 0;
|
||||||
const step = 1;
|
const step = 1;
|
||||||
for (let i = 0; i < duration; i += step) {
|
for (let i = 0; i < duration; i += step) {
|
||||||
let power = from + (to - from) * (i / duration);
|
let intensity = from + (to - from) * (i / duration);
|
||||||
score += steadyTss(step, power);
|
score += steadyTss(step, intensity);
|
||||||
}
|
}
|
||||||
return score;
|
return score;
|
||||||
};
|
};
|
||||||
|
|
||||||
const intervalTss = ({ duration, power }: Interval): number => {
|
const intervalTss = ({ duration, intensity }: Interval): number => {
|
||||||
if (power.from === power.to) {
|
if (intensity.from === intensity.to) {
|
||||||
return steadyTss(duration, power.from);
|
return steadyTss(duration, intensity.from);
|
||||||
} else {
|
} else {
|
||||||
return rangeTss(duration, power.from, power.to);
|
return rangeTss(duration, intensity.from, intensity.to);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue