rbed

Automotive

A vehicle reports its speed (m/s) and engine rpm at 1 Hz. Here the engine runs while the vehicle stands, then the vehicle accelerates at 2 m/s² to 20 m/s, drives, brakes at 5 m/s² to a stop and stands again with the engine running.

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 io.github.romanhaendler.rbed.*;
import java.time.Duration;
import java.util.Map;

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

Rule trip = Rule.named("Trip")
    .when(speed.above(1)).forDuration(Duration.ofSeconds(10))
    .endWhen(speed.below(0.5)).forDuration(Duration.ofMinutes(3));
Rule harshBraking = Rule.named("HarshBraking").when(speed.rate().below(-4));
Rule idling = Rule.named("Idling")
    .when(speed.below(0.5).and(rpm.above(600)))
    .forDuration(Duration.ofMinutes(2));
Detector detector = Detector.builder()
    .rules(trip, harshBraking, idling)
    .listener(new EventListener() {
      @Override public void onEnded(Event e) {
        if (e.is(trip)) System.out.println("a trip of " + km(e, speed) + " km");
      }
    })
    .build();

for (int s = 0; s < 1_500; s++) {   // at 1 Hz
  detector.push(s * 1_000L, Map.of(speed, speedAt(s), rpm, s < 60 ? 0.0 : 800.0));
}
detector.close();

// In the enclosing class:
// Distance of a Trip in km: the integral of speed (m/s) over time
static double km(Event trip, Channel speed) {
  return trip.aggregates(speed).integral() / 1000;
}

static double speedAt(int s) {   // m/s
  if (s < 300) return 0;
  if (s < 310) return 2 * (s - 300);       // accelerating at 2 m/s²
  if (s < 900) return 20;
  if (s < 904) return 20 - 5 * (s - 900);  // braking at 5 m/s²
  return 0;
}

The Rules

  • Trip: moving faster than 1 m/s for 10 s, over after standing still (below 0.5 m/s) for 3 minutes. Its distance, the integral of speed over time, is computed by a plain method that the listener calls for each Ended Trip, and shown as a note in the table.
  • HarshBraking: decelerating faster than 4 m/s², a rate below -4.
  • Idling: standing (below 0.5 m/s) with the engine above 600 rpm for 2 minutes.

At the defaults, the engine starts at 60 s, and the standing vehicle is Idling from 60 s, Confirmed at 180 s, until it moves off at 301 s. The Trip runs from 301 s, Confirmed at 311 s, to 904 s, where the vehicle stands still: each speed from 301 to 903 s holds for 1 s, 11.94 km in all. Braking at 5 m/s² is HarshBraking from 901 s to 905 s. From 904 s the vehicle is Idling again, Confirmed at 1024 s; it still runs when the data ends at 1499 s, so its end is Truncated.

Things to try

  • Raise Trip over after standing to 10 minutes: the vehicle stands for less than that before the data ends, so the Trip runs to the end with a Truncated end.

Reference

The example is FleetTelematicsExample, 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, ChannelExpression.rate, Condition.and, Detector.builder, Detector.Builder.rules, Detector.Builder.listener, EventListener.onEnded, Event.is, Detector.Builder.build, Detector.push, Detector.close, Event.aggregates, Aggregates.integral.

The concepts at work, each on a page of its own: Conditions and Expressions, Aggregates, End Conditions and hysteresis.