rbed

Sports

A runner's speed and heart rate are recorded at 1 Hz during an interval session: after a one-minute warm-up, three reps of 30 s sprint at 7 m/s and 90 s jog. The heart rate climbs by 1 bpm/s from 140 while sprinting and drops by 1 bpm/s while jogging.

The live chart and its controls need JavaScript. The tables show the Events at the default parameters.

The site cannot run the default parameters: (JavaScript) TypeError: Cannot read properties of undefined (reading 'length')

import static io.github.romanhaendler.rbed.Conditions.*;

import io.github.romanhaendler.rbed.*;
import java.time.Duration;
import java.util.Map;

Channel hr = Channel.named("hr");
Channel speed = Channel.named("speed");

Rule sprint = Rule.named("Sprint")
    .when(speed.above(6)).forDuration(Duration.ofSeconds(5))
    .endWhen(speed.below(4));
Rule recovered = Rule.named("Recovered")
    .when(after(sprint).within(Duration.ofMinutes(2)).and(hr.below(120)));
Rule sessionDone = Rule.named("SessionDone").when(count(sprint).above(2));
Detector detector = Detector.builder()
    .rules(sprint, recovered, sessionDone)
    .listener(new EventListener() {
      private long lastSprintEnd;

      @Override public void onEnded(Event e) {
        if (e.is(sprint)) lastSprintEnd = e.end().getAsLong();
      }

      // The recovery time, from the end of a Sprint to Recovered
      @Override public void onConfirmed(Event e) {
        if (e.is(recovered)) System.out.println("recovered after " + (e.start() - lastSprintEnd) / 1_000 + " s");
      }
    })
    .build();

for (int s = 0; s < 420; s++) {   // at 1 Hz: a warm-up minute, then reps of 30 s sprint, 90 s jog
  int inRep = (s - 60) % 120;
  boolean sprinting = s >= 60 && inRep < 30;
  double heartRate = s < 60 ? 100 : inRep < 30 ? 140 + inRep : 170 - (inRep - 30);
  detector.push(s * 1_000L, Map.of(speed, sprinting ? 7.0 : 2.0, hr, heartRate));
}
detector.close();

The Rules

  • Sprint: faster than 6 m/s for 5 s, over below 4 m/s.
  • Recovered: the heart rate below 120 within 2 minutes after a Sprint. A listener computes the recovery time, from the end of a Sprint to Recovered, shown as a note in the table.
  • SessionDone: more than 2 Sprints, that is, the third Sprint was Confirmed.

At the defaults, the three Sprints run from 60 s, 180 s and 300 s for 30 s each, Confirmed 5 s after their start. Each time, 51 s after the Sprint's end, the heart rate is below 120: Recovered runs from 141 s, 261 s and 381 s until the next Sprint, and the last one still holds when the data ends at 419 s, so its end is Truncated. The third Sprint is Confirmed at 305 s, where SessionDone starts.

Things to try

  • Raise Recovered below to 140 bpm: the heart rate falls below it sooner, and each recovery takes 31 s instead of 51 s.

Reference

The example is IntervalTrainingExample, a test with its expected Events, listed in the README's Examples.

Methods used: Channel.named, Rule.named, Rule.when, Expression.above, Rule.forDuration, Rule.endWhen, Expression.below, Conditions.after, After.within, Condition.and, Conditions.count, Detector.builder, Detector.Builder.rules, Detector.Builder.listener, EventListener.onEnded, Event.is, Event.end, EventListener.onConfirmed, Event.start, Detector.Builder.build, Detector.push, Detector.close.

The concepts at work, each on a page of its own: After another Event, Counting Events, End Conditions and hysteresis.