edgetelemetrics / advanced-analytics
Advanced Analytics library for sensor data
Package info
github.com/lucasnetau/advanced-analytics
pkg:composer/edgetelemetrics/advanced-analytics
Requires
- php: ^8.3
Requires (Dev)
- phpstan/phpstan: ^2.2.13
- phpunit/phpunit: ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-05 03:31:45 UTC
README
PHP library for real-time feature detection on sensor data streams. Computes statistical features from raw measurements and applies a suite of detectors to identify trends, anomalies, process shifts, and sensor health issues.
Requirements
- PHP 8.3+
Installation
composer require edgetelemetrics/advanced-analytics
Usage
Computing Features
FeatureCalculator computes rolling statistics, regression, EMA, and control limits from raw values:
use EdgeTelemetrics\AdvancedAnalytics\FeatureCalculator; $calc = new FeatureCalculator(); // Feed measurements one at a time $features = $calc->compute(20.15); // Returns: ['raw' => 20.15, 'sma3' => ..., 'mean24h' => ..., 'regression_slope' => ..., ...]
Running Detectors
Each detector implements DetectorInterface and receives a FeatureVector:
use EdgeTelemetrics\AdvancedAnalytics\Features\FeatureVector; use EdgeTelemetrics\AdvancedAnalytics\Detector\TrendDetector; $detector = new TrendDetector(); $features['sensor_id'] = 'sensor-1'; $features['datetime'] = '2025-03-01T00:00:00Z'; $features['regression_slope'] = 0.15; $features['regression_r2'] = 0.85; $features['direction'] = 1; $features['stddev'] = 0.5; $findings = $detector->process(new FeatureVector($features)); foreach ($findings as $finding) { echo $finding->getFinding() . ': ' . $finding->getState()->value; // "trending_up: pending" }
Processing a Stream
use EdgeTelemetrics\AdvancedAnalytics\FeatureCalculator; use EdgeTelemetrics\AdvancedAnalytics\Detector\{EWMADetector, CUSUMDetector, SpikeDetector}; $calc = new FeatureCalculator(); $detectors = [new EWMADetector(), new CUSUMDetector(), new SpikeDetector()]; foreach ($measurements as $m) { $features = $calc->compute($m['value']); $features['sensor_id'] = $m['sensor_id']; $features['datetime'] = $m['datetime']; $vector = new FeatureVector($features); foreach ($detectors as $detector) { foreach ($detector->process($vector) as $finding) { // handle finding } } }
Gating Detectors by Parameter Definition
Every detector declares applicability through supports(). A ParameterDefinition describes the sensor's data characteristics (type, source, sampling, domain) and is typically loaded at engine startup:
use EdgeTelemetrics\AdvancedAnalytics\Detector\OscillationDetector; use EdgeTelemetrics\AdvancedAnalytics\Parameter\{ParameterDefinition, ParameterType, DataSource, SamplingType, ValueDomain}; $parameter = new ParameterDefinition( type: ParameterType::Continuous, dataSource: DataSource::Online, samplingType: SamplingType::Periodic, valueDomain: ValueDomain::Numeric, ); // Only attach detectors that accept this sensor's data $detectors = array_filter( [new OscillationDetector(), new RecoveryDetector(2.0, 8.0)], fn($d) => $d::supports($parameter), );
supports() is a static method so the filter can run before instantiating anything. All current L1/L2 detectors accept Continuous and Discrete parameters with Numeric or Percentage domains.
Finding Lifecycle
Detectors no longer re-emit the same finding on every evaluation. Each detector tracks its findings through a FindingStateMachine (via the TracksFindings trait) and process() returns only findings whose state changed:
unknown → pending → active → confirmed → recovering → cleared → archived
pending— condition just fired; promoted toactiveafter 3 consecutive evaluations,clearedif it stopsactive— confirmed active; promoted toconfirmedafter 10 consecutive evaluationsconfirmed— long-lived faultrecovering— condition easing; returns toactiveif it re-fires,clearedafter 3 quiet evaluationscleared/archived— history; re-firing restarts the cycle atpending
One-shot findings (e.g. recovery_completed) are returned directly and are not tracked.
Detectors
Level 1 — Generic Analytics
Signal analytics applicable to any sensor. All support Continuous/Discrete parameters with Numeric/Percentage domains.
| Detector | Detects | Findings |
|---|---|---|
TrendDetector |
Sustained upward/downward trends, stable periods | trending_up, trending_down, stable_trend |
NelsonRulesDetector |
Statistical process control violations (8 Nelson rules) | rule1 … rule8 |
EWMADetector |
Subtle process shifts via exponentially weighted moving average | ewma_high, ewma_low |
CUSUMDetector |
Small persistent shifts via cumulative sums | positive_cusum, negative_cusum |
DriftDetector |
Long-term baseline movement | baseline_drifting |
SpikeDetector |
Isolated abnormal samples | positive_spike, negative_spike |
StepDetector |
Sudden permanent level changes | step_increase, step_decrease |
NoiseDetector |
Signal instability / failing probes | noise_high, noise_increasing |
ForecastResidualDetector |
Deviations from linear forecast | forecast_deviation_high, forecast_deviation_low |
SensorHealthDetector |
Sample gaps, flatlines, stuck sensors | sample_gap, flatline, sensor_stuck |
Level 2 — Behaviour Analytics
Detectors that understand sensor behaviour patterns. Configurable via constructor options.
| Detector | Detects | Findings |
|---|---|---|
StabilityDetector |
Steady-state vs unstable operation (derivative magnitude, R², noise ratio) | stable, unstable |
OscillationDetector |
Repeated alternating behaviour around an operating point (direction reversals with consistent amplitude) | oscillation_active |
RateOfChangeDetector |
Rapid, slow, accelerating, or decelerating transitions | rapid_change, slow_change, increasing_rate, decreasing_rate |
RecoveryDetector |
Time to return to operating band after an excursion (own IDLE → EXCURSION → RECOVERING state machine) | recovery_active, recovery_slow, recovery_failed, recovery_completed |
TimeOutsideBandDetector |
Duration spent outside a configured operating band, severity escalating with time | band_exceeded |
Level 2 detectors require configured limits or thresholds:
use EdgeTelemetrics\AdvancedAnalytics\Detector\{TimeOutsideBandDetector, RecoveryDetector, RateOfChangeDetector}; // Band detectors need operating limits $timeOutside = new TimeOutsideBandDetector( lowerLimit: 2.0, upperLimit: 8.0, durationWarning: 300.0, // seconds before severity 1 durationCritical: 1800.0, // seconds before severity 2 ); $recovery = new RecoveryDetector( lowerLimit: 2.0, upperLimit: 8.0, slowThreshold: 600.0, // recovery longer than this is 'recovery_slow' failureTimeout: 1800.0, // recovery longer than this is 'recovery_failed' ); $rate = new RateOfChangeDetector( rapidThreshold: 3.0, // derivative > 3 stddev → rapid_change slowThreshold: 0.1, // derivative < 0.1 stddev with slope → slow_change accelerationFactor: 2.0, // recent rate 2x older rate → increasing_rate );
Detector Persistence
Detectors maintain internal state (rolling buffers, accumulators, finding state machines). getState() serialises it and restoreState() rebuilds a detector after restart:
$state = $detector->getState(); // ... persist $state ... $detector->restoreState($state);
Architecture
Raw Measurements
|
FeatureCalculator (rolling stats, regression, EMA, control limits)
|
FeatureVector
|
Detectors → AnalyticalFinding[] (state-changed only)
| |
| FindingStateMachine (pending→active→confirmed→recovering→cleared)
|
Layer 1 (generic) → Layer 2 (behaviour) → Layer 3 (process) → Layer 4 (diagnosis)
Each detector is stateless or maintains minimal internal state. Detectors declare which features they need via requires() and which sensors they accept via supports(). L3 (process-specific, e.g. refrigeration) and L4 (fault diagnosis from evidence) layers are planned — see architecture/detectors.md for the full pipeline design.
Testing
composer install vendor/bin/phpunit