Generated reference › CUSUM Detector — Control Systems/Signal Measurements
kind: generated#block#control-systems-signal-measurements

CUSUM Detector — Control Systems/Signal Measurements

Control_Systems/Signal_Measurements/CUSUM_Detector · 1 input / 3 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

CUSUM Detector

Control Systems / Signal Measurements

Detects a small, sustained shift in the mean of a stream – the thing a threshold cannot see, because a drift of a fraction of a standard deviation never trips a level test. Two cumulative sums are kept over the standardized input z = (u − mean)/deviation:

  • S⁺ = max(0, S⁺ + z − k/2), which climbs while the signal runs above the target mean;
  • S⁻ = min(0, S⁻ + z + k/2), which falls while it runs below.

The half-shift k/2 subtracted from each is what makes a signal sitting exactly at the target drift back toward zero instead of wandering away from it. An alarm is raised when either sum leaves the control limit.

Ports

  • u – the signal to watch. Scalar – see Notes.
  • S+ – the upper cumulative sum, never negative.
  • S- – the lower cumulative sum, never positive.
  • alarm – +1 when the upper sum has passed the control limit, −1 when the lower sum has passed −the limit, and 0 otherwise. If both fire on the same sample the upper one is reported – it is checked first.

Parameters

  • Target Mean – the value the signal is expected to sit at while nothing has changed.
  • Target Deviation – the standard deviation expected at that mean. It is what turns the input into standard deviations, so it sets the units of everything below. Zero or less is treated as 1, which leaves the input unscaled.
  • Control Limit – how many standard deviations a sum may accumulate before the alarm fires. MATLAB's default is 5. Lower detects sooner and raises more false alarms.
  • Minimum Shift – k, the smallest shift worth detecting, in standard deviations. MATLAB's default is 1. It is the half of this that is subtracted from each sum every sample, so a larger value makes the block deafer to small drifts and faster to forget noise.
  • Reset On Alarm – whether both sums return to zero on the sample the alarm fires:
    • yes – the block re-arms, so a second alarm means a second excursion. Default.
    • no – the sums keep climbing, so the alarm stays raised for as long as the shift lasts.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.

Every parameter is resolved at export time and inlined into the arithmetic, and the reciprocal of Target Deviation is taken once when the configuration is read – so nothing in any emitted core divides, which is what keeps the three HDL targets genuine synthesizable Q16.16. Re-export after changing a parameter.

Simulink bridge

No equivalent (Support::None). The Signal Processing Toolbox ships no Simulink library at all, and a scan of the DSP System Toolbox libraries together with simulink and simulink_extras finds no cumulative-sum detector. The bridge reports this block rather than dropping it silently, and it therefore has no parity testbench. Code export verification still covers it across all ten languages.

Notes

  • Stateful (two scalars: the two sums, both seeded at zero), and discrete by nature (setDiscreteOnlyBlock(true)).
  • The mean and deviation are parameters here and are TRAINED in MATLAB. cusum takes the first 25 samples of the signal and uses their mean and standard deviation – measured, by matching its default output against an explicit call at that pair, which agrees exactly where 20 and 30 samples do not. A stream has no first 25 samples in hand before it starts.
  • The sums are the documented recursion, not cusum's reported sums. On a 100-sample probe with a +2 step at sample 51, this recursion alarms at exactly the sample cusum does (54), while the upper sum that function reports differs from the recursion by up to 0.33. Four candidate deviation estimators were tried and none reproduces it. So the block matches the detection – which is what it is for – and does not claim to reproduce those two outputs.
  • The alarm is a level, not an edge. With Reset On Alarm off it stays raised while the sum is outside the limit; with it on it fires for one sample and the sums re-arm.
  • Scalar only. One channel and its own pair of sums; wire one block per channel.
  • No state space. Two clamps and a threshold are nonlinear, so no A/B/C/D describes the block and model reduction correctly declines to merge it.

Code facts#

FactValue
registered typeControl_Systems/Signal_Measurements/CUSUM_Detector
familyControl_Systems/Signal_Measurements
solver environment classICoreBlock_0_Control_Systems_1_Signal_Measurements_2_CUSUM_Detector
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Signal_Measurements/CUSUM_Detector/ICoreBlock_0_Control_Systems_1_Signal_Measurements_2_CUSUM_Detector.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Signal_Measurements/CUSUM_Detector/ICoreBlock_0_Control_Systems_1_Signal_Measurements_2_CUSUM_Detector.h
default size on canvas126 × 92 px
ports at insert1 in, 3 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoubleS+
3outICoreDoubleS-
4outICoreDoublealarm

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
Target Mean0—
Target Deviation1—
Control Limit5—
Minimum Shift1—
Reset On Alarmyes%~%no~~yes—

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): no Simulink equivalent: cusum() is a MATLAB function and the Signal Processing Toolbox ships no Simulink library at all. Measured rather than assumed -- a scan of dspmathops, dspstat3, dspsigops, dspxfrm3, dsparch4, dspsrcs4, dspsnks4, simulink and simulink_extras finds no cumulative-sum detector. Reported rather than dropped, and it carries no parity testbench

Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The checker has a blind spot here — it could not resolve something (a grouped port bullet, a computed config name), which is reported and never counted as a pass. A reader has to settle it:

  • B0 1 port label(s) this tool cannot resolve

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

CUSUM Detector -- two cumulative sums and a control limit, for a SMALL sustained shift The point of the block is the thing a threshold cannot see. A drift of a fraction of a standard deviation never trips a level test; here it accumulates until it does.

z[k] = (u[k] - mean) * (1 / deviation) S+[k] = max(0, S+[k-1] + z[k] - mshift/2) S-[k] = min(0, S-[k-1] + z[k] + mshift/2)

The half-shift subtracted from each sum is what makes a signal sitting AT the target mean drift back toward zero instead of random-walking away from it.

⚠ THE MEAN AND DEVIATION ARE PARAMETERS HERE AND ARE TRAINED IN MATLAB. cusum() takes the first 25 samples of the signal and uses their mean and standard deviation -- MEASURED, not read off the documentation: an explicit cusum(x, 5, 1, mean(x(1:25)), std(x(1:25))) matches the default output exactly, where the same call at n0 = 20 and n0 = 30 does not. A stream has no first 25 samples in hand before it starts, so this block asks for the pair instead.

⚠ THE SUMS ARE THE DOCUMENTED RECURSION, NOT cusum()'s REPORTED SUMS, and the difference was measured rather than assumed. On a 100-sample probe with a +2 step at k = 51 the recursion above alarms at EXACTLY the sample cusum() does (54), while the uppersum it reports differs from the recursion by up to 0.33 -- its standardization is not quite the one it documents. Four candidate deviation estimators were tried (sample std, population std, mean and median absolute successive difference) and none reproduces its sums. So this block matches the DETECTION, which is what the block is for, and says so rather than claiming the sums.

⚠ THE RECIPROCAL OF THE DEVIATION IS RESOLVED WHEN THE CONFIGURATION IS READ. Nothing in any emitted core divides, which is what keeps the three hardware targets genuine Q16.16 -- the same reasoning the affine blocks in this tree use. ⚠ RENAMED AT THE 2026-09-10 MERGE: slots is a Qt keyword MACRO and Qt is in the build again. This block was written while Qt was out, so the name was free. QT_NO_KEYWORDS is not the fix -- main's Q_OBJECT code uses them.

Sample results#

CUSUM Detector — Step: 0 -> 1 at t = 1 sCUSUM Detector — Step: 0 -> 1 at t = 1 s024012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0out ICoreDouble-Out-1out ICoreDouble-Out-2

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0.5
rampRamp: slope 1 from t = 00 … 9.9
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 3.423
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample0 … 4.5

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit 608c87ca1 · produced by docsSample --out <folder> --blocks Duty_Cycle CUSUM_Detector --steps 60 · data docs/generated/samples/Control_Systems__Signal_Measurements__CUSUM_Detector.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).