Residual Whiteness Monitor — System Identification/Validation
System_Identification/Validation/Residual_Whiteness_Monitor · 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.
Residual Whiteness Monitor
System Identification / Validation
The standard model-adequacy check, live on the canvas. Over the last N residuals it correlates the signal with a delayed copy of itself at each configured lag, and asks whether every one of those correlations is inside the confidence band a white signal would stay in:
|ρ(l)| ≤ z ÷ √N for every configured lag l
White residuals mean the model took everything predictable out of the signal. A peak outside the band at lag l says there is structure left that a model of the right order would have captured – and the lag tells you roughly where to look.
Stateful and discrete by nature. The window advances one sample per step and has no derivative to integrate, so the block declares itself discrete-only and always steps at its own rate.
Ports
- e – the residual, a scalar [1,1]. The lags index TIME, so one block follows one residual; use one block per channel.
- white – 1 when every configured lag is inside the band, 0 when any one of them is outside. This is the verdict to gate on.
- worst – the largest correlation magnitude over the configured lags, so a diagram can show how far from white rather than only whether. What it is measured in follows Magnitude below.
- lag – the lag at which that largest magnitude occurred. It
carries the configured lag value, not its position in the list: with
Lags of
[1; 5; 12], a peak in the third slot reports 12.
Parameters
- Window Length – N, the number of residuals the correlation is taken over. It sets the band as well as the window: the band is z ÷ √N, so a longer window is a tighter test. Keep N several times the largest lag, or the longest lags rest on a handful of products.
- Lags – the lags to test, as a vector such as
[1; 2; 3]. Each is a whole number of samples from 1 to N−1. Lag 0 is rejected rather than accepted: it is the window's own energy, ρ(0) is 1 by construction, and including it would put the block permanently outside its own band. - Band Multiplier – z, the standard-normal multiplier that sets the confidence level. 1.96 is the usual 95% band and the default; 2.576 is 99%. Raising it makes the monitor harder to trip.
- Magnitude – what the worst port reports. The verdict
and the lag are the same in both; only this one output changes:
- Coefficient – the true |ρ|, in [0, 1] and directly comparable with the band. This is the readable choice, and it costs one divide per sample.
- Covariance – the unnormalized products averaged over N, keeping the signal's units squared. No divide anywhere, which is what keeps the three HDL targets synthesizable.
- Remove Mean – whether the window's mean is subtracted before correlating. This block defaults it to Off, unlike Autocorrelation Features, and the difference is deliberate: a residual is a model error and is zero-mean when the model is right, so subtracting the mean would hide a systematically biased model behind a "white" verdict – which is the most useful failure this block can report. Turn it On for the textbook mean-removed ACF, or when the input is a general signal rather than a residual.
- Initial Window Value – what every slot holds before the first residual arrives. The window is full from the very first step rather than growing, for the reason Rolling Statistics gives: a partial window costs a sample counter and a divide-by-count branch in all ten exported languages, for a startup transient that a validation display discards anyway.
- Epsilon – a floor on the divisor of the Coefficient magnitude, and on nothing else. The verdict never divides, so it needs no floor: see Code export below.
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period. Being discrete-only, a non-positive value falls back to the model's global sampling time rather than to the surrounding rate.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The window, every lag's product sum and the comparison chain are unrolled at export time – N and the lag list are both known then – so no backend carries a loop bound, a modulo or a ring-buffer index.
The verdict never forms the ratio. |ρ(l)| ≤ k is exactly |num(l)| ≤ k·den, and den is a sum of squares and so never negative, which means the inequality survives the multiplication with no sign case-split. The comparison is therefore multiply-accumulate and nothing else. It also removes the need for an Epsilon floor on the verdict: a constant window has den = 0, where the test degenerates to |num(l)| ≤ 0 with num(l) = 0, and passes cleanly instead of dividing by zero.
The AND over the lags and the search for the worst lag are one scan, because "every lag is inside the band" is the same statement as "the largest magnitude is inside the band". No backend carries a separate reduction.
The three HDL targets split by magnitude, not by verdict:
Covariance is genuine synthesizable Q16.16 throughout –
multiply-accumulate, a comparison chain, and the 1/N folded into a
constant at export time. Coefficient divides by the window's own energy
for that one output, a signal-dependent divisor that cannot be precomputed, so
that mode is emitted as simulation-only real arithmetic.
Even there the verdict and the lag stay exact: both are decided before
the divide and neither depends on it.
Simulink bridge
None. The System Identification Toolbox checks residual whiteness with
resid – a FUNCTION that runs after a simulation over a whole
logged signal and draws a correlation plot – not with a block that reports
a running verdict sample by sample. Every block in this family registers
Support::None for the same reason. The bridge reports the block
rather than dropping it silently, and it has no parity testbench, which
is the documented consequence of Support::None. Code export
verification still covers it across all ten languages.
Notes
- Stateful: the answer depends on the N−1 residuals before the current one. The window is re-seeded at the start of every run.
- No state space: the correlation is quadratic in the input, so no A/B/C/D describes it and model reduction correctly refuses the block.
- The state is read before it is written. Every lag is computed from the window as it stood, and only then does the window shift – on the three HDL targets that comes free from the registered write, and the other seven shift from the far end downwards.
- Ties go to the LOWEST lag. The scan replaces its running best only on a strictly greater magnitude, scanning the lags in written order – Argmax Decision's convention, spelled identically in all ten backends.
- The lag output is discontinuous, as any argmax is: two lags whose magnitudes are within a fixed-point quantum can rank the other way round in the three HDL targets, moving the reported lag by a whole step. That is a property of the block, not of the codegen – the same caveat Argmax Decision and Time Delay Estimator carry.
- It is a monitor, not a filter: it passes judgement on the residual and never alters it. Feed the residual on to whatever consumes it and take white as a gate.
Code facts#
| Fact | Value |
|---|---|
| registered type | System_Identification/Validation/Residual_Whiteness_Monitor |
| family | System_Identification/Validation |
| solver environment class | ICoreBlock_0_System_Identification_1_Validation_2_Residual_Whiteness_Monitor |
| source | src/ICoreSDK/ICoreBlockLibrary/Blocks/System_Identification/Validation/Residual_Whiteness_Monitor/ICoreBlock_0_System_Identification_1_Validation_2_Residual_Whiteness_Monitor.cpp |
| header | src/ICoreSDK/ICoreBlockLibrary/Blocks/System_Identification/Validation/Residual_Whiteness_Monitor/ICoreBlock_0_System_Identification_1_Validation_2_Residual_Whiteness_Monitor.h |
| default size on canvas | 146 × 96 px |
| ports at insert | 1 in, 3 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreDouble | e |
| 2 | out | ICoreDouble | white |
| 3 | out | ICoreDouble | worst |
| 4 | out | ICoreDouble | lag |
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 variable | Default | Simulink parameter |
|---|---|---|
Window Length | 12 | — |
Lags | [1; 2; 3] | — |
Band Multiplier | 1.96 | — |
Magnitude | Coefficient%~%Covariance~~Coefficient | — |
Remove Mean | Off%~%On~~Off | — |
Initial Window Value | 0 | — |
Epsilon | 1e-12 | — |
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.
Simulink bridge#
| support | Support::None |
| Simulink path | — |
| port-count rule | PortsParam::None |
SampleTime parameter | yes |
Caveat (shown to the user): no Simulink equivalent: it has no block that reports a running whiteness verdict. The residual-correlation test this follows is a System Identification Toolbox FUNCTION (resid) that runs after a simulation over a whole logged signal and draws a correlation plot, so there is nothing sample-by-sample to map onto
Catalog contract: src/ICoreSDK/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.
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).
Residual Whiteness Monitor — is the residual's autocorrelation inside the confidence band? num(l) = SUM_{k=0..N-1-l} (e[k] - m)(e[k+l] - m) e[0] the current sample den = num(0) = SUM_k (e[k] - m)^2 rho(l) = num(l) / den
white = 1 when |rho(l)| <= k for EVERY configured lag, k = z / sqrt(N) worst = max_l |rho(l)| (Coefficient) or max_l |num(l)| * (1/N) (Covariance) lag = the CONFIGURED lag value at that maximum
Read the header before this file: it records why the test is cross-multiplied (and so why this block is not a wiring pattern over Autocorrelation_Features), why the AND-reduction and the argmax are one scan, why the mode splits only the magnitude output, and why Remove Mean defaults to Off here where the ACF block defaults it On.
⚠ The window is UNROLLED at export time and the mean, when it is removed at all, is INLINED into every deviation rather than held in a temporary — Autocorrelation_Features' reason: the HDL targets have no spare scratch of the right type, and one emission path shared by ten backends is worth more than the repeated text. N is small by design so it stays so.
⚠ The three HDL targets carry exactly three scratch variables (acc, acc2, iacc), which is what fixes the ORDER of the emitted body: the lag scan first (acc working, acc2 the running maximum, iacc the winning lag), then
deninto acc, and only then the verdict — sodenis still live for the Coefficient divide and nothing needs a fourth variable.
Sample results#
The same rig also ran:
| Stimulus | What it is | Output range |
|---|---|---|
impulse | Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair) | 1 … 1 |
ramp | Ramp: slope 1 from t = 0 | 0 … 1 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | 0 … 1 |
table | Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample | 1 … 1 |
Plotted: step — Step: 0 -> 1 at t = 1 s
Category dynamic · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/System_Identification__Validation__Residual_Whiteness_Monitor.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).