Search by

edgetelemetrics / advanced-analytics

lucasnetau

Advanced Analytics library for sensor data

Package info

github.com/lucasnetau/advanced-analytics

pkg:composer/edgetelemetrics/advanced-analytics

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-09-05 00:40 UTC

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 to active after 3 consecutive evaluations, cleared if it stops
  • active — confirmed active; promoted to confirmed after 10 consecutive evaluations
  • confirmed — long-lived fault
  • recovering — condition easing; returns to active if it re-fires, cleared after 3 quiet evaluations
  • cleared / archived — history; re-firing restarts the cycle at pending

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) rule1rule8
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