Skip to content

API Reference: Event Detection

Hannes Suhr edited this page Mar 16, 2026 · 18 revisions

API Reference: Event Detection

The EventDetection library groups threshold violations into discrete events with statistics, provides console reporting, and includes a Gantt-style viewer UI with click-to-plot functionality. Features include severity escalation, persistent event storage with backups, and live auto-refresh from file.


EventDetector

Core detection engine. Groups consecutive threshold violation points into Event objects, applies debounce filtering, computes statistics, and invokes callbacks.

Constructor

det = EventDetector();
det = EventDetector('MinDuration', 0.5);
det = EventDetector('MinDuration', 2, 'OnEventStart', @myCallback, 'MaxCallsPerEvent', 3);

Properties

Property Type Default Description
MinDuration double 0 Debounce filter: discard events shorter than this (in time units matching your X data). Set to 0 to keep all events
OnEventStart function_handle [] Callback invoked as f(event) for each detected event
MaxCallsPerEvent double 1 Maximum callback invocations per event (prevents duplicate notifications)

Methods

detect(t, values, thresholdValue, direction, thresholdLabel, sensorName)

Detect events from raw time series data and a threshold.

events = det.detect(t, values, 4.5, 'high', 'High Limit', 'Pressure');
events = det.detect(t, values, -2.0, 'low', 'Low Limit', 'Temperature');
Parameter Type Description
t 1xN double Time stamps
values 1xN double Data values
thresholdValue double Threshold to check against
direction char 'high' (value > threshold) or 'low' (value < threshold)
thresholdLabel char Label for the threshold (e.g., 'Run HI')
sensorName char Name of the sensor/channel

Returns: Array of Event objects. Returns [] if no events are found.

Algorithm:

  1. Call the private helper groupViolations() to find all points where the value crosses the threshold in the given direction, and cluster consecutive violation points into groups (each group has a startIdx and endIdx)
  2. For each group, compute startTime, endTime, and duration
  3. Filter out events shorter than MinDuration (debounce)
  4. Compute statistics for each event over the data window values(startIdx:endIdx):
    • PeakValue: max for 'high' direction, min for 'low' direction
    • NumPoints, MinValue, MaxValue, MeanValue, RmsValue, StdValue
  5. Invoke OnEventStart callback for each event (respecting MaxCallsPerEvent)

Usage Example

% Simple detection
det = EventDetector('MinDuration', 0.5);
t = linspace(0, 100, 1e6);
y = randn(1, 1e6) * 10 + 50;

events = det.detect(t, y, 70, 'high', 'High', 'Pressure');
fprintf('Found %d events\n', numel(events));
% With live logging callback
det = EventDetector('MinDuration', 1.0, 'OnEventStart', eventLogger());
events = det.detect(t, y, 70, 'high', 'High', 'Pressure');
% Console output: [EVENT] Pressure | High | HIGH | 12.50 -> 14.20 (dur=1.70) | peak=82.30

Event

Value class holding metadata and statistics for a single detected event.

Constants

Constant Value Description
DIRECTIONS {'high', 'low'} Valid direction values

Properties (all read-only, SetAccess = private)

Property Type Description
StartTime double Timestamp of first violation point
EndTime double Timestamp of last violation point
Duration double EndTime - StartTime
SensorName char Name of the sensor/channel
ThresholdLabel char Label of the threshold that was violated
ThresholdValue double The threshold value
Direction char 'high' or 'low'
PeakValue double Worst violation value (furthest from threshold)
NumPoints double Number of data points within the event time window
MinValue double Minimum signal value during event
MaxValue double Maximum signal value during event
MeanValue double Mean signal value during event
RmsValue double RMS signal value during event
StdValue double Standard deviation during event

Constructor

e = Event(startTime, endTime, sensorName, thresholdLabel, thresholdValue, direction);
Parameter Type Description
startTime double First violation timestamp
endTime double Last violation timestamp (must be >= startTime)
sensorName char Sensor/channel name
thresholdLabel char Threshold label
thresholdValue double Threshold value
direction char Must be 'high' or 'low'

Errors:

  • Event:invalidDirection if direction is not 'high' or 'low'
  • Event:invalidTimeRange if endTime < startTime

Note: Event objects are normally created by EventDetector.detect(), not directly.

Methods

setStats(peakValue, numPoints, minVal, maxVal, meanVal, rmsVal, stdVal)

Set the statistical fields. Called internally by EventDetector. Since Event is a value class, this returns a new object with the stats set.

e = e.setStats(peakVal, nPts, minVal, maxVal, meanVal, rmsVal, stdVal);

escalateTo(newLabel, newThresholdValue)

Escalate an event to a higher severity threshold. Used by the severity escalation system. Returns a new Event with updated label and threshold value while preserving all other fields and statistics.

e = e.escalateTo('temp critical', 95);
Parameter Type Description
newLabel char New threshold label
newThresholdValue double New threshold value

EventConfig

Configuration container that orchestrates event detection across multiple sensors. Bridges the SensorThreshold and EventDetection libraries. Supports severity escalation, persistent event storage with backups, and auto-open viewer.

Constructor

cfg = EventConfig();

Properties

Property Type Default Description
Sensors cell array {} Registered Sensor objects
SensorData struct array [] Sensor data for viewer (fields: name, t, y)
MinDuration double 0 Debounce threshold
MaxCallsPerEvent double 1 Callback limit
OnEventStart function_handle [] Callback f(event)
ThresholdColors containers.Map (empty) Maps threshold labels to RGB color vectors
AutoOpenViewer logical false Auto-open EventViewer after detection
EscalateSeverity logical true Escalate events whose peak exceeds a higher threshold on the same sensor/direction
EventFile char '' Path to .mat file for auto-saving events. Empty = disabled
MaxBackups double 5 Number of timestamped backup files to keep before pruning. 0 = no backups

Methods

addSensor(sensor)

Register a sensor. Automatically calls sensor.resolve() and stores the sensor's data (X, Y, and display name) for the viewer's click-to-plot feature.

cfg.addSensor(sensor);

setColor(label, rgb)

Set the display color for a threshold label in the viewer.

cfg.setColor('Run HI', [1 0 0]);
cfg.setColor('Boost HI', [1 0.5 0]);

buildDetector()

Create a configured EventDetector from the config properties.

det = cfg.buildDetector();

runDetection()

Detect events across all registered sensors, then:

  1. Severity escalation (if EscalateSeverity is true): for each event, check if its peak exceeds a higher threshold on the same sensor and direction. If so, escalate the event's label and threshold value to the higher one. Remove duplicate events that are contained within an escalated event with the same label.
  2. Auto-save (if EventFile is set): create a timestamped backup of the existing file, then save events, sensor data, threshold colors, and a timestamp to the .mat file. Old backups are pruned to MaxBackups.
  3. Auto-open viewer (if AutoOpenViewer is true).
events = cfg.runDetection();

Returns: Combined Event array across all sensors, after escalation.

Severity Escalation

When nested thresholds exist (e.g., warning at 85, critical at 95), detection runs each threshold independently. A brief spike above 95 may get debounced, but the longer warning event (above 85) survives. Without escalation, the user sees a "temp warning" with peak=96 — misleading.

With EscalateSeverity = true (default), the post-detection step checks the warning event's peak against all higher thresholds for the same sensor and direction. Since 96 > 95, the event is escalated from "temp warning" to "temp critical".

cfg.EscalateSeverity = true;   % default — escalate
cfg.EscalateSeverity = false;  % disable — keep original labels

How it works for each direction:

  • 'high': thresholds sorted ascending. Peak must exceed (>=) the higher threshold value.
  • 'low': thresholds sorted descending. Peak must be at or below (<=) the lower threshold value.

Event Store (Persistent Storage)

Set EventFile to auto-save events to a .mat file after every runDetection() call.

cfg.EventFile = 'my_events.mat';
cfg.MaxBackups = 5;              % keep 5 timestamped backups (default)
cfg.MaxBackups = 0;              % disable backups
events = cfg.runDetection();     % saves automatically

The .mat file contains:

Field Type Description
events Event array All detected events
sensorData struct array Sensor time series (name, t, y)
thresholdColors struct Color map serialized as struct (label + rgb per entry)
timestamp datetime When the file was saved

Backup files are created with a timestamp suffix before each overwrite:

my_events.mat                    ← current
my_events_20260309_143025.mat    ← backup 1
my_events_20260309_142512.mat    ← backup 2

Oldest backups are pruned when the count exceeds MaxBackups.

Usage Example

cfg = EventConfig();
cfg.MinDuration = 0.5;
cfg.OnEventStart = eventLogger();
cfg.EventFile = 'events.mat';
cfg.MaxBackups = 3;

cfg.addSensor(pressureSensor);
cfg.addSensor(temperatureSensor);

cfg.setColor('Run HI', [1 0 0]);
cfg.setColor('Boost HI', [1 0.5 0]);

events = cfg.runDetection();
% Events saved to events.mat with backup of previous version

EventViewer

Figure-based UI with a Gantt-style timeline, filter dropdowns, and a filterable event table. Supports click-to-plot for zooming into individual events via FastPlot with violation markers and toolbar.

Constructor

viewer = EventViewer(events);
viewer = EventViewer(events, sensorData);
viewer = EventViewer(events, sensorData, thresholdColors);
Parameter Type Description
events Event array Detected events to display
sensorData struct array Fields: name, t, y (enables click-to-plot). Optional
thresholdColors containers.Map Maps threshold labels to [R G B] color vectors. Optional

Properties

Property Type Description
Events Event array All events (unfiltered)
SensorData struct array Sensor time series data for click-to-plot
ThresholdColors containers.Map Color mapping for threshold labels
hFigure figure handle Handle to the viewer figure

Public Methods

update(newEvents)

Refresh the viewer with new events. Re-applies current filters, redraws the timeline, and repopulates the table.

viewer.update(newEvents);

getSensorNames() / getThresholdLabels()

Get unique sensor names or threshold labels from the current events.

names = viewer.getSensorNames();
labels = viewer.getThresholdLabels();

refreshFromFile()

Reload events, sensor data, and colors from the source .mat file. Only available when the viewer was opened via fromFile(). Updates the Gantt chart, table, and status bar.

viewer.refreshFromFile();

startAutoRefresh(interval)

Start polling the source .mat file at the given interval (in seconds).

viewer.startAutoRefresh(5);   % refresh every 5 seconds

stopAutoRefresh()

Stop the auto-refresh timer.

viewer.stopAutoRefresh();

Static Methods

EventViewer.fromFile(filepath)

Open an EventViewer directly from a saved .mat event store file (created by EventConfig with EventFile set). No sensors or config needed — all data is loaded from the file.

viewer = EventViewer.fromFile('events.mat');

The viewer includes a refresh toolbar (only when opened via fromFile):

Control Description
[Refresh] button Manual one-shot reload from file
[Auto] checkbox Enable/disable auto-refresh timer
Interval input Refresh interval in seconds (default 5)
Status bar Shows last refresh time, event count, and source file path

The figure title displays the timestamp from the saved file.

UI Layout

The viewer opens a figure window (1200x700, dark theme) with three sections:

Top Panel — Gantt Timeline:

  • One row per sensor on the Y-axis, bars fill the full lane height (no gaps)
  • Colored horizontal bars for each event
  • Bar color is determined by ThresholdColors if a mapping exists, otherwise auto-assigned
  • X and Y grid lines in subtle gray for visual alignment
  • Hover tooltips: hovering over a bar shows sensor name, threshold, direction, start/end times, duration, peak value, and point count
  • Click-to-select: clicking a Gantt bar highlights it with a white border and highlights the corresponding table row with a blue background

Filter Dropdowns (middle):

  • Sensor dropdown: filter by sensor name (or "All")
  • Threshold dropdown: filter by threshold label (or "All")
  • Changing either filter immediately redraws the timeline and table

Refresh Toolbar (when opened via fromFile):

  • Refresh button, Auto checkbox, interval input, status bar

Bottom Panel — Event Table:

Column Description
Start Event start time
End Event end time
Duration Event duration
Sensor Sensor name
Threshold Threshold label
Dir Direction (high or low)
Peak Peak violation value
#Pts Number of data points in event
Min Minimum value during event
Max Maximum value during event
Mean Mean value during event
RMS RMS value during event
Std Standard deviation during event

Click-to-Plot:

Clicking any row in the table opens a new FastPlot window with:

  • The sensor's full signal via addLine
  • The violated threshold via addThreshold with ShowViolations enabled
  • FastPlotToolbar for interactive exploration
  • View zoomed to the event's time range with 20% padding

Requires SensorData to be provided.

Usage Examples

% Direct usage with events
viewer = EventViewer(events, sensorData, colors);
viewer.update(newEvents);  % live update
% Open from saved file (e.g., from background detection process)
viewer = EventViewer.fromFile('events.mat');

% Manual refresh
viewer.refreshFromFile();

% Auto-refresh every 5 seconds
viewer.startAutoRefresh(5);

% Stop auto-refresh
viewer.stopAutoRefresh();
% Background process writes events, viewer polls them:
%
% Process A (background):
%   cfg.EventFile = '/shared/events.mat';
%   while running
%       cfg.runDetection();   % auto-saves to file
%       pause(10);
%   end
%
% Process B (viewer):
%   viewer = EventViewer.fromFile('/shared/events.mat');
%   viewer.startAutoRefresh(5);   % polls every 5s

Convenience Functions

detectEventsFromSensor(sensor, detector)

Bridge function that connects the SensorThreshold and EventDetection libraries.

events = detectEventsFromSensor(sensor);           % Default EventDetector
events = detectEventsFromSensor(sensor, detector);  % Custom detector

Behavior:

  1. Determines the sensor display name (sensor.Name if set, otherwise sensor.Key)
  2. Iterates over sensor.ResolvedViolations
  3. Maps SensorThreshold directions: 'upper''high', 'lower''low'
  4. Looks up threshold value from sensor.ResolvedThresholds
  5. Calls detector.detect() on the full sensor signal for each threshold

printEventSummary(events)

Print a formatted event summary table to the console.

printEventSummary(events);

Output format:

Start        End          Duration   Sensor           Threshold          Dir       Peak   #Pts       Mean        Std
------------------------------------------------------------------------------------------------------------------------
12.50        14.20        1.70       Pressure         Run HI             high      82.30    170      75.40       4.21
...
2 event(s) total.

eventLogger()

Factory function returning a callback for live console logging of events.

det = EventDetector('OnEventStart', eventLogger());

Output: [EVENT] Pressure | Run HI | HIGH | 12.50 -> 14.20 (dur=1.70) | peak=82.30


Private Helpers

groupViolations(t, values, thresholdValue, direction)

Clusters consecutive threshold violations into groups using diff([0, mask, 0]) transition detection.

Returns: Struct array with startIdx and endIdx per group. Returns [] if no violations.


Complete Workflow Example

%% 1. Create sensors with thresholds
sTemp = Sensor('temperature', 'Name', 'Temperature');
sTemp.X = t; sTemp.Y = temp;
sTemp.addThresholdRule(struct(), 85, 'Direction', 'upper', 'Label', 'temp warning');
sTemp.addThresholdRule(struct(), 95, 'Direction', 'upper', 'Label', 'temp critical');

sPres = Sensor('pressure', 'Name', 'Pressure');
sPres.X = t; sPres.Y = pressure;
sPres.addThresholdRule(struct(), 4, 'Direction', 'lower', 'Label', 'pressure low');

%% 2. Configure detection with auto-save and escalation
cfg = EventConfig();
cfg.MinDuration = 0.5;
cfg.OnEventStart = eventLogger();
cfg.EventFile = 'events.mat';   % persistent storage
cfg.MaxBackups = 3;              % keep 3 backups

cfg.addSensor(sTemp);
cfg.addSensor(sPres);

cfg.setColor('temp warning',  [1.0 0.8 0.0]);
cfg.setColor('temp critical', [1.0 0.2 0.0]);
cfg.setColor('pressure low',  [0.2 0.5 1.0]);

%% 3. Detect — events are escalated and saved to file
events = cfg.runDetection();
printEventSummary(events);

%% 4. View from file (e.g., in another session or by another user)
viewer = EventViewer.fromFile('events.mat');
viewer.startAutoRefresh(5);   % poll for updates every 5s

See Also


IncrementalEventDetector

Wraps EventDetector with incremental state for streaming / chunk-based processing. Tracks the last-processed index per sensor and carries over open events across chunks.

Superclass: handle

Source: libs/EventDetection/IncrementalEventDetector.m

Constructor

ied = IncrementalEventDetector();
ied = IncrementalEventDetector('MinDuration', 2, 'OnEventStart', @myCallback);

Properties

Property Type Default Description
MinDuration double 0 Debounce filter: discard events shorter than this
MaxCallsPerEvent double 1 Maximum callback invocations per event
OnEventStart function_handle [] Callback invoked as f(event) for each completed event
EscalateSeverity logical true Escalate events whose peak exceeds a higher threshold

Methods

process(sensorKey, sensor, newX, newY, newStateX, newStateY)

Process a new chunk of data for a sensor. Internally accumulates data, builds a temporary Sensor on the relevant slice, runs EventDetector, handles open/merged events, and returns newly completed events.

newEvents = ied.process('temperature', sTemp, newX, newY, stateX, stateY);
Parameter Type Description
sensorKey char Unique sensor identifier
sensor Sensor Source sensor (for threshold rules and state channels)
newX 1xN double New time stamps
newY 1xN double New data values
newStateX 1xM double New state transition times (can be empty)
newStateY 1xM New state values (can be empty)

Returns: Array of completed Event objects (may be empty if all events are still open).

Behavior:

  1. Appends new data to the accumulated buffer for the sensor
  2. Determines the slice start (open event start or new data start)
  3. Builds a temporary Sensor with threshold rules and state channels on the slice
  4. Runs EventDetector on the slice
  5. Merges events with any previously open event
  6. Carries over events that are still ongoing at the end of the chunk
  7. Fires OnEventStart callback for completed events

hasOpenEvent(sensorKey)

Check whether a sensor currently has an unfinished event that spans the end of the last processed chunk.

tf = ied.hasOpenEvent('temperature');

getSensorState(sensorKey)

Get the internal state struct for a sensor (accumulated data, open event, etc.).

st = ied.getSensorState('temperature');

Usage Example

ied = IncrementalEventDetector('MinDuration', 1.0, 'OnEventStart', eventLogger());

% Process data in chunks (e.g., from a live pipeline)
while hasMoreData
    [newX, newY] = readChunk();
    events = ied.process('pressure', sPressure, newX, newY, [], []);
    if ~isempty(events)
        fprintf('Detected %d new events\n', numel(events));
    end
end

DataSourceMap

Maps sensor keys to DataSource instances. Used by live pipelines to manage multiple data sources.

Superclass: handle

Source: libs/EventDetection/DataSourceMap.m

Constructor

dsm = DataSourceMap();

Methods

Method Signature Description
add add(key, dataSource) Register a DataSource under a string key. Throws if not a DataSource subclass.
get get(key) Retrieve the DataSource for a key. Throws DataSourceMap:unknownKey if not found.
keys keys() Return cell array of all registered keys.
has has(key) Return true if the key exists.
remove remove(key) Remove a key (no-op if not found).

Usage Example

dsm = DataSourceMap();
dsm.add('temperature', myTempSource);
dsm.add('pressure', myPressSource);

allKeys = dsm.keys();
ds = dsm.get('temperature');

NotificationRule

Configures notification settings for sensor/threshold events. Matched against events by sensor key and threshold label. Supports template-based subject/message formatting.

Superclass: handle

Source: libs/EventDetection/NotificationRule.m

Constructor

rule = NotificationRule('SensorKey', 'temperature', 'ThresholdLabel', 'Hi Alarm', ...
    'Recipients', {{'ops@example.com'}});

Properties

Property Type Default Description
SensorKey char '' Filter by sensor key (empty = match all)
ThresholdLabel char '' Filter by threshold label (empty = match all)
Recipients cell {{}} Email recipients
Subject char 'Event: {sensor} - {threshold}' Email subject template
Message char (see source) Email body template
IncludeSnapshot logical true Attach a FastPlot snapshot image
ContextHours double 2 Hours of context data around event in snapshot
SnapshotPadding double 0.1 Padding around event in snapshot
SnapshotSize 1x2 double [800 400] Snapshot image size in pixels

Template Variables

Templates support these placeholders: {sensor}, {threshold}, {direction}, {startTime}, {endTime}, {duration}, {peak}, {mean}, {rms}, {std}, {thresholdValue}.

Methods

matches(event)

Returns a match score: 3 = sensor + threshold match, 2 = sensor match, 1 = default (no filters), 0 = no match.

fillTemplate(template, event)

Replace placeholders in a template string with event data.


NotificationService

Rule-based email notification service with event snapshot generation.

Superclass: handle

Source: libs/EventDetection/NotificationService.m

Constructor

ns = NotificationService('Enabled', true, 'DryRun', true);
ns = NotificationService('SmtpServer', 'smtp.example.com', 'FromAddress', 'alerts@example.com');

Properties

Property Type Default Description
Rules NotificationRule array [] Registered notification rules
DefaultRule NotificationRule [] Fallback rule when no specific rule matches
Enabled logical true Master enable/disable
DryRun logical false Print notifications to console instead of sending email
SnapshotDir char (tempdir) Directory for snapshot images
SnapshotRetention double 7 Days to keep snapshots before cleanup
SmtpServer char '' SMTP server address
FromAddress char 'fastplot@noreply.com' Sender email address
NotificationCount double 0 Running count of notifications sent

Methods

Method Description
addRule(rule) Register a NotificationRule
setDefaultRule(rule) Set the fallback rule
findBestRule(event) Find the highest-scoring matching rule for an event
notify(event, sensorData) Send notification for an event (generates snapshot, sends email)
cleanupSnapshots() Delete snapshot files older than SnapshotRetention days

Usage Example

ns = NotificationService('DryRun', true);

rule = NotificationRule('SensorKey', 'temperature', ...
    'Recipients', {{'ops@example.com'}});
ns.addRule(rule);

% Called from event detection callback:
ns.notify(event, sensorData);
% [NOTIFY DRY-RUN] To: ops@example.com | Subject: Event: temperature - Hi Alarm

Clone this wiki locally