rbed

Batch processing and sampling rates

A recorded series does not need a loop of push calls: detect runs it through a fresh Detector in one call. And regularly sampled data needs no timestamps at all: declare the sampling rate, and the Detector derives each Sample's time from its position.

A recording in one call

detect pushes every Sample, closes the Detector and returns every Ended Event, in the order they Ended:

Rule loadLifted = Rule.named("LoadLifted").when(weight.above(500));
Detector detector = Detector.builder()
    .rules(loadLifted)
    .build();

List<Event> events = detector.detect(List.of(
    new Sample(0,     Map.of(weight, 0.0)),
    new Sample(500,   Map.of(weight, 1200.0)),
    new Sample(1_000, Map.of(weight, 0.0))));   // [LoadLifted 500..1000]

It accepts any Iterable<Sample>, so a file read lazily works as well. A listener, if configured, is still called. A Detector can be used only once: detect on a Detector that has already seen Samples throws.

Samples without timestamps

For regularly sampled data without timestamps, declare the rate on the builder with samplingRate(hz) and push only the values, with push(values) or, for a whole recording, detectValues. Sample i (counting from 0) is then at round(i * 1000 / hz) milliseconds: at 50 Hz, the Samples are 20 ms apart.

The rate is at most 1000 Hz, since timestamps have millisecond resolution. With a sampling rate, push(timestamp, values) and advanceTo throw, because time advances with the Samples; without one, push(values) throws.

Durations count positions, not clock time

With a sampling rate, every duration is measured in Sample positions converted to time. That is exact for truly regular data, and wrong by exactly as much as the data is not regular.

Below, an accelerometer was logged at 10 Hz, but the logger lost the Samples from 2.1 to 2.9 s. Vibration requires accel above 3 for 1.5 s. Pushed with their timestamps, both vibrations last long enough: the first from 1.5 to 3.5 s, Confirmed at 3 s. Choose "Values at a sampling rate", and the same Samples are pushed as values with detectValues at a declared 10 Hz: every Sample after the loss now lies 0.9 s early. The first vibration seems to stop at 2.6 s, before its 1.5 s are reached, and is Discarded; the second is still Confirmed, but 0.9 s earlier than it happened.

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.ArrayList;
import java.util.List;
import java.util.Map;

Channel accel = Channel.named("accel");
Rule vibration = Rule.named("Vibration")
    .when(accel.above(3)).forDuration(Duration.ofMillis(1_500));

// the recording: logged at 10 Hz from 0 to 8 s; the Samples from 2.1 to 2.9 s were lost
List<Sample> recording = new ArrayList<>();
for (int tenth = 0; tenth <= 80; tenth++) {
  if (tenth > 20 && tenth < 30) continue;
  boolean shaking = tenth >= 15 && tenth < 35 || tenth >= 50 && tenth < 70;
  recording.add(new Sample(tenth * 100L, Map.of(accel, (shaking ? 4.0 : 0.5) + tenth % 3 * 0.2)));
}

Detector detector = Detector.builder()
    .rules(vibration)
    .build();
List<Event> events = detector.detect(recording);

Things to try

  • With "Values at a sampling rate" chosen, declare a different rate, and every duration stretches or shrinks with it: at 5 Hz the Samples are 200 ms apart, so the first vibration lasts 2.2 s and is Confirmed again, at 4.6 s, although on the clock it lasted 2 s.

forSamples counts Samples in either mode, so only forDuration and the other time-based parts of a Rule depend on the rate.

Use a sampling rate when the data really is regular and has no timestamps. When Samples can go missing, push them with their timestamps.

Reference

Methods used: Channel.named, Rule.named, Rule.when, Expression.above, Rule.forDuration, Detector.builder, Detector.Builder.rules, Detector.Builder.build, Detector.detect, Detector.Builder.samplingRate, Detector.detectValues.