Generated reference › Recursive Output Error — System Identification/Recursive
kind: generated#block#system-identification-recursive

Recursive Output Error — System Identification/Recursive

B/F

System_Identification/Recursive/Recursive_Output_Error · 2 input / 4 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.

Recursive Output Error

System Identification / Recursive

Streaming identification of an output-error model – the parallel-model recursion, in which the regressor carries the model's own past outputs rather than the measured ones:

φ[k] = [ u[k−d] … u[k−d−m], −ŷ[k−1] … −ŷ[k−n] ]
ŷ[k] = φ[k]′θ[k−1],   e[k] = y[k] − ŷ[k]

Why it exists beside the ARX block. With measured y in the regressor, output noise is correlated with the regressor and biases θ no matter how long the run is. With ŷ, it is not, and the estimate stays unbiased. That single substitution is the whole difference, and it is also the source of this block's one difficulty – see Predictor Bound.

Stateful and discrete by nature: it declares itself discrete-only and always steps at its own rate.

Ports

  • u – the plant input, a scalar.
  • y – the measured plant output, a scalar.
  • B – the numerator estimate, [m+1, 1], descending [b0 … bm].
  • F – the denominator estimate, [n+1, 1], descending and monic: the leading 1 is a constant, and the estimated coefficients follow it.
  • e – the a-priori prediction error y − ŷ, the residual a Residual Whiteness Monitor downstream is meant to consume.
  • stable1 on a normal sample, 0 on any sample where the predictor clamp engaged and the update was frozen. Per sample, not latched; put a Debounced Decision after it if a sticky verdict is wanted.

Parameters

  • Numerator Order (m) and Denominator Order (n) – m must be ≤ n, or the model is noncausal and the block refuses to run. The convention matches the rest of the library: an intrinsic input delay of d = n − m samples.
  • Forgetting Factor (λ) – in (0, 1]. Not a free tuning knob: the recursion divides the covariance by λ every step, so an unexcited direction grows as λ−k and the per-step Lyapunov exponent is ln(1/λ). At 0.97 that is e30 over a 1000-sample run; at 0.997 it is e3. Prefer 0.995–0.999.
  • Initial Covariance – the diagonal P is seeded with. Large means "trust the data, not the seed" and adapts fast.
  • Predictor Bound – the magnitude ŷ is clamped to. This is the stability guard, and what it promises is narrow: it keeps the RECURSION bounded, NOT the estimated denominator stable. Because ŷ feeds back into its own regressor, a momentarily unstable F̂ would otherwise make ŷ diverge, corrupt the regressor and diverge further. When the clamp engages the parameter update is frozen for that sample – adapting on saturated data is worse than not adapting – and stable drops to 0 so the intervention is visible rather than silent. Set it a few times larger than the plant output's working range.
  • Sampling Time (s) – zero or less inherits the solver's rate.

Code export

All ten targets. The orders, and therefore every array bound, are fixed at export time, so no backend allocates or sizes dynamically. Each emits the same six phases as the live solver: shift the u history, build φ from the u and ŷ histories, predict and clamp, form e, run the gated update, then shift the ŷ history and write the ports.

The warm-up gate is real and is not inherited. The update is withheld until both histories are full; without it the first n samples would adapt against zero padding. Recursive Least Squares needs no such gate because its regressor arrives on a port – this block builds its own, exactly as Recursive IIR Identification does.

The three HDL targets are SIMULATION-ONLY, by this family's standing rule: the recursion divides once per sample, and the covariance spans roughly twelve decades between its seed and its settled value. Neither is carryable in the Q16.16 signal format. They run the recursion in real arithmetic and quantize only on the way out.

Simulink bridge

None. The System Identification Toolbox's recursive estimators are MATLAB objects driven from a script, not a Simulink block with this port shape, and every block in this family registers Support::None for the same reason. It therefore has no parity testbench, which is the documented consequence; code export verification still covers it across all ten languages.

Notes

  • No state space: the recursion is nonlinear in its own state, so no A/B/C/D describes it and model reduction correctly refuses the block.
  • The seed is read from the config in every backend, never from the running estimate – export happens after the model build has already solved a step.
  • F is reported monic, with the constant 1 leading, so the port width is a function of the configured order alone and never of the values.

Code facts#

FactValue
registered typeSystem_Identification/Recursive/Recursive_Output_Error
familySystem_Identification/Recursive
solver environment classICoreBlock_0_System_Identification_1_Recursive_2_Recursive_Output_Error
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/System_Identification/Recursive/Output_Error/ICoreBlock_0_System_Identification_1_Recursive_2_Recursive_Output_Error.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/System_Identification/Recursive/Output_Error/ICoreBlock_0_System_Identification_1_Recursive_2_Recursive_Output_Error.h
default size on canvas150 × 104 px
ports at insert2 in, 4 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2inICoreDoubley
3outICoreDoubleB
4outICoreDoubleF
5outICoreDoublee
6outICoreDoublestable

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
Numerator Order1
Denominator Order2
Forgetting Factor0.997
Initial Covariance1e6
Predictor Bound1e4

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: the System Identification Toolbox's recursive estimators are MATLAB objects driven from a script rather than a block with this port shape, so there is no add_block target and no parameter set 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).

Recursive Output Error — the parallel-model recursion phi[k] = [ u[k-d] .. u[k-d-m] , -yhat[k-1] .. -yhat[k-n] ]' yhat[k] = phi[k]' * theta[k-1] e[k] = y[k] - yhat[k] K = P*phi / (lambda + phi'P*phi); theta += K*e; P = (P - K(P*phi)')/lambda

Read the header before this file: it records why the regressor holds the model's OWN past outputs (and so why this block cannot reuse ICoreRecursiveLeastSquares), the stability rule and the three alternatives rejected before any generator was written, why the warm-up gate is NOT inherited from Recursive_Least_Squares, and why the HDL targets are simulation-only.

Ten generators, one recursion. Each emits the same six phases in the same order as advance() below:

  1. shift the u history and push u_k (depth d+m+1)
  2. build phi from the u history and the YHAT history
  3. yhat = phi'theta, then CLAMP it to +/- bound (sat flag)
  4. e = y_k - yhat
  5. if the histories are full AND the clamp did not engage, run the RLS update
  6. shift the yhat history and push YHAT (never y), then write the four ports

Every bound is a compile-time constant, so the generated code needs no allocation and no dynamic sizing -- which is what makes the recursion expressible in the fixed-size HDL and ST cores at all.

Sample results#

Recursive Output Error — Step: 0 -> 1 at t = 1 sRecursive Output Error — Step: 0 -> 1 at t = 1 s00.51012345t (s)in ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0 [2x1] entry 0out ICoreDouble-Out-1 [3x1] entry 0out 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
rampRamp: slope 1 from t = 00 … 2
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 1.96
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-0.6001 … 2

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__Recursive__Recursive_Output_Error.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).