Replace power% field with intensity fraction

This commit is contained in:
Rene Saarsoo 2020-09-20 13:56:55 +03:00
parent e6a7cc6e6f
commit 09e38cd615
4 changed files with 26 additions and 24 deletions

View File

@ -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;
}; };

View File

@ -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;

View File

@ -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}"`);
}; };

View File

@ -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);
} }
}; };