Introduce Intensity & IntensityRange classes
This commit is contained in:
parent
c516b80aff
commit
212df4e748
|
|
@ -0,0 +1,31 @@
|
|||
export class Intensity {
|
||||
constructor(private _value: number) {}
|
||||
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
get start() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
get end() {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
|
||||
export class IntensityRange {
|
||||
constructor(private _start: number, private _end: number) {}
|
||||
|
||||
get value() {
|
||||
return this._start;
|
||||
}
|
||||
|
||||
get start() {
|
||||
return this._start;
|
||||
}
|
||||
|
||||
get end() {
|
||||
return this._end;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { IntervalType } from "./parser/tokenizer";
|
||||
import { Duration } from "./Duration";
|
||||
import { Intensity, IntensityRange } from "./Intensity";
|
||||
|
||||
export type Workout = {
|
||||
name: string;
|
||||
|
|
@ -11,7 +12,7 @@ export type Workout = {
|
|||
export type Interval = {
|
||||
type: IntervalType;
|
||||
duration: Duration;
|
||||
intensity: { from: number; to: number };
|
||||
intensity: Intensity | IntensityRange;
|
||||
cadence?: number;
|
||||
comments: Comment[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Interval } from "./ast";
|
||||
import { detectRepeats } from "./detectRepeats";
|
||||
import { Duration } from "./Duration";
|
||||
import { Intensity, IntensityRange } from "./Intensity";
|
||||
|
||||
describe("detectRepeats()", () => {
|
||||
it("does nothing with empty array", () => {
|
||||
|
|
@ -9,32 +10,32 @@ describe("detectRepeats()", () => {
|
|||
|
||||
it("does nothing when no interval repeats", () => {
|
||||
const intervals: Interval[] = [
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(30), intensity: { from: 1.2, to: 1.2 }, comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(60), intensity: { from: 1, to: 0.5 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(30), intensity: new Intensity(1.2), comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
];
|
||||
expect(detectRepeats(intervals)).toEqual(intervals);
|
||||
});
|
||||
|
||||
it("detects whole workout consisting of repetitions", () => {
|
||||
const intervals: Interval[] = [
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
];
|
||||
expect(detectRepeats(intervals)).toEqual([
|
||||
{
|
||||
type: "repeat",
|
||||
times: 4,
|
||||
intervals: [
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
],
|
||||
comments: [],
|
||||
},
|
||||
|
|
@ -43,54 +44,54 @@ describe("detectRepeats()", () => {
|
|||
|
||||
it("detects repetitions in the middle of workout", () => {
|
||||
const intervals: Interval[] = [
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: { from: 0.5, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(120), intensity: { from: 0.2, to: 0.2 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(120), intensity: { from: 0.2, to: 0.2 }, comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(60), intensity: { from: 1, to: 0.5 }, comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: new IntensityRange(0.5, 1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(120), intensity: new Intensity(0.2), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(120), intensity: new Intensity(0.2), comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(60), intensity: new IntensityRange(1, 0.5), comments: [] },
|
||||
];
|
||||
expect(detectRepeats(intervals)).toEqual([
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: { from: 0.5, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(120), intensity: { from: 0.2, to: 0.2 }, comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: new IntensityRange(0.5, 1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(120), intensity: new Intensity(0.2), comments: [] },
|
||||
{
|
||||
type: "repeat",
|
||||
times: 4,
|
||||
intervals: [
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
],
|
||||
comments: [],
|
||||
},
|
||||
{ type: "Rest", duration: new Duration(120), intensity: { from: 0.2, to: 0.2 }, comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(60), intensity: { from: 1, to: 0.5 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(120), intensity: new Intensity(0.2), comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(60), intensity: new IntensityRange(1, 0.5), comments: [] },
|
||||
]);
|
||||
});
|
||||
|
||||
it("detects multiple repetitions", () => {
|
||||
const intervals: Interval[] = [
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(100), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(100), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(100), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(100), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(100), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(100), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(100), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(100), intensity: new Intensity(0.5), comments: [] },
|
||||
];
|
||||
expect(detectRepeats(intervals)).toEqual([
|
||||
{
|
||||
type: "repeat",
|
||||
times: 2,
|
||||
intervals: [
|
||||
{ type: "Interval", duration: new Duration(60), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(60), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
],
|
||||
comments: [],
|
||||
},
|
||||
|
|
@ -98,8 +99,8 @@ describe("detectRepeats()", () => {
|
|||
type: "repeat",
|
||||
times: 2,
|
||||
intervals: [
|
||||
{ type: "Interval", duration: new Duration(100), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(100), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(100), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(100), intensity: new Intensity(0.5), comments: [] },
|
||||
],
|
||||
comments: [],
|
||||
},
|
||||
|
|
@ -108,22 +109,22 @@ describe("detectRepeats()", () => {
|
|||
|
||||
it("takes cadence differences into account", () => {
|
||||
const intervals: Interval[] = [
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, cadence: 100, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, cadence: 80, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, cadence: 100, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, cadence: 80, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), cadence: 100, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), cadence: 80, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), cadence: 100, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), cadence: 80, comments: [] },
|
||||
];
|
||||
expect(detectRepeats(intervals)).toEqual([
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), comments: [] },
|
||||
{
|
||||
type: "repeat",
|
||||
times: 2,
|
||||
intervals: [
|
||||
{ type: "Interval", duration: new Duration(120), intensity: { from: 1, to: 1 }, cadence: 100, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: { from: 0.5, to: 0.5 }, cadence: 80, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(120), intensity: new Intensity(1), cadence: 100, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(60), intensity: new Intensity(0.5), cadence: 80, comments: [] },
|
||||
],
|
||||
comments: [],
|
||||
},
|
||||
|
|
@ -132,14 +133,14 @@ describe("detectRepeats()", () => {
|
|||
|
||||
it("does not consider warmup/cooldown-range intervals to be repeatable", () => {
|
||||
const intervals: Interval[] = [
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: { from: 0.1, to: 1 }, comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(120), intensity: { from: 1, to: 0.5 }, comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: { from: 0.1, to: 1 }, comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(120), intensity: { from: 1, to: 0.5 }, comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: { from: 0.1, to: 1 }, comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(120), intensity: { from: 1, to: 0.5 }, comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: { from: 0.1, to: 1 }, comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(120), intensity: { from: 1, to: 0.5 }, comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: new IntensityRange(0.1, 1), comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(120), intensity: new IntensityRange(1, 0.5), comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: new IntensityRange(0.1, 1), comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(120), intensity: new IntensityRange(1, 0.5), comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: new IntensityRange(0.1, 1), comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(120), intensity: new IntensityRange(1, 0.5), comments: [] },
|
||||
{ type: "Warmup", duration: new Duration(60), intensity: new IntensityRange(0.1, 1), comments: [] },
|
||||
{ type: "Cooldown", duration: new Duration(120), intensity: new IntensityRange(1, 0.5), comments: [] },
|
||||
];
|
||||
expect(detectRepeats(intervals)).toEqual(intervals);
|
||||
});
|
||||
|
|
@ -149,7 +150,7 @@ describe("detectRepeats()", () => {
|
|||
{
|
||||
type: "Interval",
|
||||
duration: new Duration(100),
|
||||
intensity: { from: 1, to: 1 },
|
||||
intensity: new Intensity(1),
|
||||
comments: [
|
||||
{ offset: new Duration(0), text: "Let's start" },
|
||||
{ offset: new Duration(20), text: "Stay strong!" },
|
||||
|
|
@ -159,7 +160,7 @@ describe("detectRepeats()", () => {
|
|||
{
|
||||
type: "Rest",
|
||||
duration: new Duration(100),
|
||||
intensity: { from: 0.5, to: 0.5 },
|
||||
intensity: new Intensity(0.5),
|
||||
comments: [
|
||||
{ offset: new Duration(0), text: "Huh... have a rest" },
|
||||
{ offset: new Duration(80), text: "Ready for next?" },
|
||||
|
|
@ -168,7 +169,7 @@ describe("detectRepeats()", () => {
|
|||
{
|
||||
type: "Interval",
|
||||
duration: new Duration(100),
|
||||
intensity: { from: 1, to: 1 },
|
||||
intensity: new Intensity(1),
|
||||
comments: [
|
||||
{ offset: new Duration(0), text: "Bring it on again!" },
|
||||
{ offset: new Duration(50), text: "Half way" },
|
||||
|
|
@ -178,7 +179,7 @@ describe("detectRepeats()", () => {
|
|||
{
|
||||
type: "Rest",
|
||||
duration: new Duration(100),
|
||||
intensity: { from: 0.5, to: 0.5 },
|
||||
intensity: new Intensity(0.5),
|
||||
comments: [
|
||||
{ offset: new Duration(30), text: "Wow... you did it!" },
|
||||
{ offset: new Duration(40), text: "Nice job." },
|
||||
|
|
@ -191,8 +192,8 @@ describe("detectRepeats()", () => {
|
|||
type: "repeat",
|
||||
times: 2,
|
||||
intervals: [
|
||||
{ type: "Interval", duration: new Duration(100), intensity: { from: 1, to: 1 }, comments: [] },
|
||||
{ type: "Rest", duration: new Duration(100), intensity: { from: 0.5, to: 0.5 }, comments: [] },
|
||||
{ type: "Interval", duration: new Duration(100), intensity: new Intensity(1), comments: [] },
|
||||
{ type: "Rest", duration: new Duration(100), intensity: new Intensity(0.5), comments: [] },
|
||||
],
|
||||
comments: [
|
||||
{ offset: new Duration(0), text: "Let's start" },
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { eqProps, flatten, zip } from "ramda";
|
||||
import { Interval, Comment } from "./ast";
|
||||
import { Duration } from "./Duration";
|
||||
import { IntensityRange } from "./Intensity";
|
||||
|
||||
export type RepeatedInterval = {
|
||||
type: "repeat";
|
||||
|
|
@ -54,7 +55,7 @@ const stripComments = (intervals: Interval[]): Interval[] => {
|
|||
return intervals.map(({ comments, ...rest }) => ({ comments: [], ...rest }));
|
||||
};
|
||||
|
||||
const isRangeInterval = (interval: Interval): boolean => interval.intensity.from !== interval.intensity.to;
|
||||
const isRangeInterval = (interval: Interval): boolean => interval.intensity instanceof IntensityRange;
|
||||
|
||||
export const detectRepeats = (intervals: Interval[]): (Interval | RepeatedInterval)[] => {
|
||||
if (intervals.length < windowSize) {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ const generateRangeInterval = (
|
|||
{
|
||||
_attr: {
|
||||
Duration: duration.seconds,
|
||||
PowerLow: intensity.from,
|
||||
PowerHigh: intensity.to,
|
||||
PowerLow: intensity.start,
|
||||
PowerHigh: intensity.end,
|
||||
...(cadence ? { Cadence: cadence } : {}),
|
||||
},
|
||||
},
|
||||
|
|
@ -35,7 +35,7 @@ const generateSteadyStateInterval = ({ duration, intensity, cadence, comments }:
|
|||
{
|
||||
_attr: {
|
||||
Duration: duration.seconds,
|
||||
Power: intensity.from,
|
||||
Power: intensity.value,
|
||||
...(cadence ? { Cadence: cadence } : {}),
|
||||
},
|
||||
},
|
||||
|
|
@ -53,11 +53,11 @@ const generateRepeatInterval = (repInterval: RepeatedInterval): xml.XmlObject =>
|
|||
Repeat: repInterval.times,
|
||||
|
||||
OnDuration: on.duration.seconds,
|
||||
OnPower: on.intensity.from,
|
||||
OnPower: on.intensity.start,
|
||||
...(on.cadence ? { Cadence: on.cadence } : {}),
|
||||
|
||||
OffDuration: off.duration.seconds,
|
||||
OffPower: off.intensity.from,
|
||||
OffPower: off.intensity.end,
|
||||
...(off.cadence ? { CadenceResting: off.cadence } : {}),
|
||||
},
|
||||
},
|
||||
|
|
@ -72,9 +72,9 @@ const generateInterval = (interval: Interval | RepeatedInterval): xml.XmlObject
|
|||
}
|
||||
|
||||
const { intensity } = interval;
|
||||
if (intensity.from < intensity.to) {
|
||||
if (intensity.start < intensity.end) {
|
||||
return generateRangeInterval("Warmup", interval);
|
||||
} else if (intensity.from > intensity.to) {
|
||||
} else if (intensity.start > intensity.end) {
|
||||
return generateRangeInterval("Cooldown", interval);
|
||||
} else {
|
||||
return generateSteadyStateInterval(interval);
|
||||
|
|
|
|||
|
|
@ -74,9 +74,8 @@ Rest: 5:00 45%
|
|||
"duration": Duration {
|
||||
"seconds": 300,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Rest",
|
||||
},
|
||||
|
|
@ -86,9 +85,8 @@ Rest: 5:00 45%
|
|||
"duration": Duration {
|
||||
"seconds": 600,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.8,
|
||||
"to": 0.8,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.8,
|
||||
},
|
||||
"type": "Interval",
|
||||
},
|
||||
|
|
@ -98,9 +96,8 @@ Rest: 5:00 45%
|
|||
"duration": Duration {
|
||||
"seconds": 300,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.45,
|
||||
"to": 0.45,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.45,
|
||||
},
|
||||
"type": "Rest",
|
||||
},
|
||||
|
|
@ -139,9 +136,8 @@ Interval: 5:00 50%
|
|||
"duration": Duration {
|
||||
"seconds": 300,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Interval",
|
||||
},
|
||||
|
|
@ -151,9 +147,8 @@ Interval: 5:00 50%
|
|||
"duration": Duration {
|
||||
"seconds": 600,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 1,
|
||||
"to": 1,
|
||||
"intensity": Intensity {
|
||||
"_value": 1,
|
||||
},
|
||||
"type": "Interval",
|
||||
},
|
||||
|
|
@ -163,9 +158,8 @@ Interval: 5:00 50%
|
|||
"duration": Duration {
|
||||
"seconds": 300,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Interval",
|
||||
},
|
||||
|
|
@ -191,9 +185,9 @@ Cooldown: 5:30 70%..45%
|
|||
"duration": Duration {
|
||||
"seconds": 330,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.8,
|
||||
"intensity": IntensityRange {
|
||||
"_end": 0.8,
|
||||
"_start": 0.5,
|
||||
},
|
||||
"type": "Warmup",
|
||||
},
|
||||
|
|
@ -203,9 +197,9 @@ Cooldown: 5:30 70%..45%
|
|||
"duration": Duration {
|
||||
"seconds": 330,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.7,
|
||||
"to": 0.45,
|
||||
"intensity": IntensityRange {
|
||||
"_end": 0.45,
|
||||
"_start": 0.7,
|
||||
},
|
||||
"type": "Cooldown",
|
||||
},
|
||||
|
|
@ -235,9 +229,8 @@ Cooldown: 5:30 70%..45%
|
|||
"duration": Duration {
|
||||
"seconds": 10,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Interval",
|
||||
}
|
||||
|
|
@ -249,9 +242,8 @@ Cooldown: 5:30 70%..45%
|
|||
"duration": Duration {
|
||||
"seconds": 10,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Interval",
|
||||
}
|
||||
|
|
@ -263,9 +255,8 @@ Cooldown: 5:30 70%..45%
|
|||
"duration": Duration {
|
||||
"seconds": 10,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Interval",
|
||||
}
|
||||
|
|
@ -280,9 +271,8 @@ Cooldown: 5:30 70%..45%
|
|||
"duration": Duration {
|
||||
"seconds": 10,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Interval",
|
||||
}
|
||||
|
|
@ -294,9 +284,8 @@ Cooldown: 5:30 70%..45%
|
|||
"duration": Duration {
|
||||
"seconds": 10,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Interval",
|
||||
}
|
||||
|
|
@ -406,9 +395,8 @@ Rest: 5:00 50%
|
|||
"duration": Duration {
|
||||
"seconds": 600,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.9,
|
||||
"to": 0.9,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.9,
|
||||
},
|
||||
"type": "Interval",
|
||||
},
|
||||
|
|
@ -431,9 +419,8 @@ Rest: 5:00 50%
|
|||
"duration": Duration {
|
||||
"seconds": 300,
|
||||
},
|
||||
"intensity": Object {
|
||||
"from": 0.5,
|
||||
"to": 0.5,
|
||||
"intensity": Intensity {
|
||||
"_value": 0.5,
|
||||
},
|
||||
"type": "Rest",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Interval, Workout, Comment } from "../ast";
|
||||
import { Duration } from "../Duration";
|
||||
import { Intensity, IntensityRange } from "../Intensity";
|
||||
import { ParseError } from "./ParseError";
|
||||
import { SourceLocation, Token } from "./tokenizer";
|
||||
|
||||
|
|
@ -91,10 +92,10 @@ const parseIntervalParams = (tokens: Token[], loc: SourceLocation): [IntervalDat
|
|||
data.cadence = token.value;
|
||||
tokens.shift();
|
||||
} else if (token.type === "intensity") {
|
||||
data.intensity = { from: token.value, to: token.value };
|
||||
data.intensity = new Intensity(token.value);
|
||||
tokens.shift();
|
||||
} else if (token.type === "intensity-range") {
|
||||
data.intensity = { from: token.value[0], to: token.value[1] };
|
||||
data.intensity = new IntensityRange(token.value[0], token.value[1]);
|
||||
tokens.shift();
|
||||
} else {
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { pipe } from "ramda";
|
||||
import { Interval } from "../ast";
|
||||
import { Intensity } from "../Intensity";
|
||||
import { average } from "./average";
|
||||
import { intervalsToIntensities } from "./intervalsToIntensities";
|
||||
|
||||
export const averageIntensity = (intervals: Interval[]): number => {
|
||||
return pipe(intervalsToIntensities, average)(intervals);
|
||||
export const averageIntensity = (intervals: Interval[]): Intensity => {
|
||||
return new Intensity(pipe(intervalsToIntensities, average)(intervals));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ export const stats = ({ intervals }: Workout): string => {
|
|||
return `
|
||||
Total duration: ${(duration.seconds / 60).toFixed()} minutes
|
||||
|
||||
Average intensity: ${(avgIntensity * 100).toFixed()}%
|
||||
Normalized intensity: ${(normIntensity * 100).toFixed()}%
|
||||
Average intensity: ${(avgIntensity.value * 100).toFixed()}%
|
||||
Normalized intensity: ${(normIntensity.value * 100).toFixed()}%
|
||||
|
||||
TSS #1: ${tss(intervals).toFixed()}
|
||||
TSS #2: ${tss2(duration, normIntensity).toFixed()}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ import { Interval } from "../ast";
|
|||
|
||||
// Converts interval to array of intensity values for each second
|
||||
const intervalToIntensities = ({ duration, intensity }: Interval): number[] => {
|
||||
const seconds = [];
|
||||
const { from, to } = intensity;
|
||||
const intensities: number[] = [];
|
||||
const [from, to] = [intensity.start, intensity.end];
|
||||
for (let i = 0; i < duration.seconds; i++) {
|
||||
// Intensity in a single second
|
||||
seconds.push(from + (to - from) * (i / duration.seconds));
|
||||
intensities.push(from + (to - from) * (i / duration.seconds));
|
||||
}
|
||||
return seconds;
|
||||
return intensities;
|
||||
};
|
||||
|
||||
export const intervalsToIntensities = chain(intervalToIntensities);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { pipe, sum } from "ramda";
|
||||
import { Interval } from "../ast";
|
||||
import { Intensity } from "../Intensity";
|
||||
import { average } from "./average";
|
||||
import { intervalsToIntensities } from "./intervalsToIntensities";
|
||||
|
||||
|
|
@ -24,12 +25,14 @@ const fourthPower = (x: number) => Math.pow(x, 4);
|
|||
|
||||
const fourthRoot = (x: number) => Math.pow(x, 1 / 4);
|
||||
|
||||
export const normalizedIntensity = (intervals: Interval[]): number => {
|
||||
return pipe(
|
||||
intervalsToIntensities,
|
||||
rollingAverages,
|
||||
(averages) => averages.map(fourthPower),
|
||||
average,
|
||||
fourthRoot,
|
||||
)(intervals);
|
||||
export const normalizedIntensity = (intervals: Interval[]): Intensity => {
|
||||
return new Intensity(
|
||||
pipe(
|
||||
intervalsToIntensities,
|
||||
rollingAverages,
|
||||
(averages) => averages.map(fourthPower),
|
||||
average,
|
||||
fourthRoot,
|
||||
)(intervals),
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ const rangeTss = (duration: Duration, from: number, to: number): number => {
|
|||
};
|
||||
|
||||
const intervalTss = ({ duration, intensity }: Interval): number => {
|
||||
if (intensity.from === intensity.to) {
|
||||
return steadyTss(duration, intensity.from);
|
||||
if (intensity.start === intensity.end) {
|
||||
return steadyTss(duration, intensity.value);
|
||||
} else {
|
||||
return rangeTss(duration, intensity.from, intensity.to);
|
||||
return rangeTss(duration, intensity.start, intensity.end);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Duration } from "../Duration";
|
||||
import { Intensity } from "../Intensity";
|
||||
|
||||
// Training Stress Score formula from Training and Racing with a Power Meter:
|
||||
//
|
||||
|
|
@ -13,6 +14,6 @@ import { Duration } from "../Duration";
|
|||
// TSS = (s * (FTP * IF) * IF) / (FTP * 3600) * 100
|
||||
// TSS = (s * IF * IF) / 3600 * 100
|
||||
|
||||
export const tss2 = (duration: Duration, intensity: number): number => {
|
||||
return ((duration.seconds * intensity * intensity) / 3600) * 100;
|
||||
export const tss2 = (duration: Duration, intensity: Intensity): number => {
|
||||
return ((duration.seconds * Math.pow(intensity.value, 2)) / 3600) * 100;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue