Healthcare
A bedside monitor reports heart rate and oxygen saturation (SpO2) at 1 Hz, and here its sensor drops out for 20 s. While no data arrives, the monitor's clock advances the Detector every second with advanceTo, so the dropout is noticed while it lasts, not only once data resumes.
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 spo2 = Channel.named("spo2");
Rule tachycardia = Rule.named("Tachycardia")
.when(hr.above(100)).forDuration(Duration.ofSeconds(30))
.endWhen(hr.below(95)).forDuration(Duration.ofSeconds(30))
.maxGap(Duration.ofSeconds(10));
Rule desaturation = Rule.named("Desaturation")
.when(spo2.below(90)).forDuration(Duration.ofSeconds(10))
.endWhen(spo2.below(92).not());
Rule hypoxicTachycardia = Rule.named("HypoxicTachycardia")
.when(during(tachycardia).and(during(desaturation)));
Rule sensorLost = Rule.named("SensorLost")
.when(hr.age().above(Duration.ofSeconds(5).toMillis()));
Detector detector = Detector.builder()
.rules(tachycardia, desaturation, hypoxicTachycardia, sensorLost)
.build();
for (long s = 0; s < 600; s++) { // every second
if (s >= 150 && s < 170) {
detector.advanceTo(s * 1_000); // the sensor drops out; the monitor's clock advances time
} else {
detector.push(s * 1_000, Map.of(
hr, s < 60 ? 80.0 : s < 300 ? 120.0 : 85.0,
spo2, s < 120 ? 97.0 : s < 200 ? 86.0 : 96.0));
}
}
detector.close();
The Rules
- Tachycardia: heart rate above 100 for 30 s, over below 95 for 30 s. A dropout longer than 10 s interrupts it: the Event Ends Truncated, and the one after the dropout starts Truncated because nobody knows what happened in between.
- Desaturation: SpO2 below 90 % for 10 s, over at 92 % or more.
- HypoxicTachycardia: both at the same time.
- SensorLost: no heart rate for more than 5 s, measured with
age().
At the defaults, the heart rate is 120 from 60 s, and Tachycardia is Confirmed at 90 s. The SpO2 is 86 % from 120 s to 200 s: Desaturation is Confirmed at 130 s, and HypoxicTachycardia with it.
The last Sample before the dropout is at 149 s. At the advance to 160 s, 11 s have passed since the last Sample, more than the maxGap of 10 s: Tachycardia Ends Truncated at 159 s, the last advance that still lay within 10 s of the last Sample. HypoxicTachycardia, which has no maxGap of its own, Ends at 160 s, the advance at which during(tachycardia) stops holding. SensorLost runs from 155 s, when the heart rate is 6 s old, until data resumes at 170 s, where a new Tachycardia starts Truncated. It is Confirmed at 200 s and Ends at 300 s, where the heart rate falls to 85.
Things to try
- Raise Max gap to 30 s: the 20 s dropout no longer interrupts
Tachycardia, which runs as one Event from 60 s to 300 s, andHypoxicTachycardialasts untilDesaturationEnds at 200 s.
Reference
The example is VitalSignsExample, 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, Rule.maxGap, Condition.not, Conditions.during, Condition.and, ChannelExpression.age, Detector.builder, Detector.Builder.rules, Detector.Builder.build, Detector.advanceTo, Detector.push, Detector.close.
The concepts at work, each on a page of its own: Silent sensors: advancing time, Data gaps, During another Event, End Conditions and hysteresis.