# 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.

Parameters (in the page address as ?sprint=…):

- Sprint above (`sprint`): 3 to 8 m/s in steps of 0.5, default 6
- Sprint for (`sprintSeconds`): 1 to 30 s in steps of 1, default 5
- Recovered below (`recoveredHr`): 100 to 160 bpm in steps of 5, default 120
- Recovered within (`recoveryMinutes`): 1 to 5 min in steps of 1, default 2
- Done after more than (`sprints`): 0 to 3 Sprints in steps of 1, default 2

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

Java source at the default parameters:

```java
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](https://github.com/romanhaendler/rbed/blob/main/src/test/java/io/github/romanhaendler/rbed/examples/IntervalTrainingExample.java), a test with its expected Events, listed in the README's [Examples](https://github.com/romanhaendler/rbed#examples).

Methods used: [`Channel.named`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Channel.html#named%28java.lang.String%29), [`Rule.named`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Rule.html#named%28java.lang.String%29), [`Rule.when`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Rule.html#when%28io.github.romanhaendler.rbed.Condition%29), [`Expression.above`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Expression.html#above%28double%29), [`Rule.forDuration`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Rule.html#forDuration%28java.time.Duration%29), [`Rule.endWhen`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Rule.html#endWhen%28io.github.romanhaendler.rbed.Condition%29), [`Expression.below`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Expression.html#below%28double%29), [`Conditions.after`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Conditions.html#after%28io.github.romanhaendler.rbed.Rule%29), [`After.within`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/After.html#within%28java.time.Duration%29), [`Condition.and`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Condition.html#and%28io.github.romanhaendler.rbed.Condition%29), [`Conditions.count`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Conditions.html#count%28io.github.romanhaendler.rbed.Rule%29), [`Detector.builder`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.html#builder%28%29), [`Detector.Builder.rules`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.Builder.html#rules%28io.github.romanhaendler.rbed.Rule...%29), [`Detector.Builder.listener`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.Builder.html#listener%28io.github.romanhaendler.rbed.EventListener%29), [`EventListener.onEnded`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/EventListener.html#onEnded%28io.github.romanhaendler.rbed.Event%29), [`Event.is`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Event.html#is%28io.github.romanhaendler.rbed.Rule%29), [`Event.end`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Event.html#end%28%29), [`EventListener.onConfirmed`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/EventListener.html#onConfirmed%28io.github.romanhaendler.rbed.Event%29), [`Event.start`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Event.html#start%28%29), [`Detector.Builder.build`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.Builder.html#build%28%29), [`Detector.push`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.html#push%28long,java.util.Map%29), [`Detector.close`](https://romanhaendler.github.io/rbed/api/io.github.romanhaendler.rbed/io/github/romanhaendler/rbed/Detector.html#close%28%29).

The concepts at work, each on a page of its own: [After another Event](https://romanhaendler.github.io/rbed/after/), [Counting Events](https://romanhaendler.github.io/rbed/count/), [End Conditions and hysteresis](https://romanhaendler.github.io/rbed/end-conditions/).
