Generated reference › Model Fit Monitor — System Identification/Validation
kind: generated#block#system-identification-validation

Model Fit Monitor — System Identification/Validation

System_Identification/Validation/Model_Fit_Monitor · 2 input / 1 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.

Model Fit Monitor

System Identification / Validation

Reports how well a model's output tracks the signal it is meant to predict, as a running NRMSE fit updated every sample:

fit = 1 − √(Se ÷ (Sy + ε)), where Se is the exponentially weighted energy of the error y − ŷ and Sy the exponentially weighted variance of y about its own running mean.

  • 1 – the model reproduces y exactly.
  • 0 – the model is no better than predicting y's own mean.
  • Negative – the model is worse than that. This is deliberately not clamped: it is the most useful thing the block can tell you, and a floor at zero would hide it.

Because the sums are exponentially weighted rather than taken over the whole run, the number follows the model live: a plant that drifts away from its identified model shows the fit falling on the canvas while the simulation is still running, which a fit computed at the end cannot do.

Ports

  • y – the measured signal, a scalar. This is the reference: the denominator is its running variance, so it is not interchangeable with the other port.
  • yhat () – the model's output for the same instant, a scalar.
  • fit – the running fit, a scalar, as a fraction (not the percentage MATLAB's compare prints – multiply by 100).

Parameters

  • Forgetting Factorλ in (0, 1]. The memory is roughly 1 ÷ (1 − λ) samples: 0.98 averages over about 50, 0.9 over about 10. Smaller reacts faster and is noisier; 1 makes it an ordinary cumulative fit over the whole run with no forgetting at all.
  • Epsilon – a floor on the variance divisor, not a branch. A constant y has zero running variance, and without this the fit would divide by zero the moment a signal settles. Keep it small relative to the variance you expect; it biases the fit slightly upward.
  • Initial Reference Mean – the seed for y's running mean. It decays with λ like any other sample, so it matters only for the first few multiples of the memory; set it to y's expected level to stop the fit reading low while the mean converges from zero.
  • 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. λ, ε and the seed are baked into the emitted arithmetic rather than exposed as tunable parameters: they define what the number means, so retuning one on the target would silently change the metric rather than the model.

The three HDL targets are simulation-only real arithmetic. A square root and a per-sample division do not belong in a Q16.16 datapath, and this block needs both.

Simulink bridge

None. Simulink has no block that reports a running fit – the NRMSE conventions this follows live in System Identification Toolbox functions (goodnessOfFit, compare), which run after a simulation over a whole logged signal rather than inside one, and which no ParamRule could carry across the bridge in any case. The block therefore has no parity testbench, which is the documented consequence of Support::None rather than a gap.

Notes

  • Stateful, with three scalars: the running mean of y and the two weighted sums. It is a discrete-only block – a fit has no derivative to integrate.
  • The mean is updated before it is used. Sy takes the deviation from the new mean, not the previous one. The two conventions differ by order (1 − λ) every sample.
  • The output is the current sample's fit, not the previous one: the sums are advanced and then published, which is the opposite order to the filter blocks.
  • The fit is a ratio of two weighted sums, so the weighting constants cancel and no normalisation by the sample count is needed – which is also why a warm-up period is visible in the number rather than hidden by one.

Code facts#

FactValue
registered typeSystem_Identification/Validation/Model_Fit_Monitor
familySystem_Identification/Validation
solver environment classICoreBlock_0_System_Identification_1_Validation_2_Model_Fit_Monitor
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/System_Identification/Validation/Model_Fit_Monitor/ICoreBlock_0_System_Identification_1_Validation_2_Model_Fit_Monitor.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/System_Identification/Validation/Model_Fit_Monitor/ICoreBlock_0_System_Identification_1_Validation_2_Model_Fit_Monitor.h
default size on canvas128 × 80 px
ports at insert2 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubley
2inICoreDoubleyhat
3outICoreDoublefit

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
Forgetting Factor0.98
Epsilon1e-12
Initial Reference Mean0

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: it has no block that reports a running model fit. The NRMSE conventions this follows are System Identification Toolbox FUNCTIONS (goodnessOfFit, compare) that run after a simulation over a whole logged signal rather than inside one, so there is nothing for a ParamRule 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).

Model Fit Monitor — the running NRMSE fit between a measurement and a model's output m[k] = L*m[k-1] + (1-L)*y[k] Se[k] = L*Se[k-1] + (y[k] - yhat[k])^2 Sy[k] = L*Sy[k-1] + (y[k] - m[k])^2 fit = 1 - sqrt( Se[k] / (Sy[k] + eps) )

Three scalar states and no window, so a drifting model shows up live on the canvas rather than at the end of a run. The recursion is written ONCE in four *Expr() members and each generator supplies only a Dialect -- the update ORDER (mean first) and the floored divisor are the two things eleven evaluators have to agree on, and neither is visible in a diff.

The three HDL targets are simulation-only real: a square root and a per-sample division have no place in a Q16.16 datapath.

Sample results#

Model Fit Monitor — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sampleModel Fit Monitor — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-202012345t (s)in ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0
tin ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0
0-2-21
0.40.50.51
0.8-2-21
1.20.50.51
1.6-2-21
20.50.51
2.4-2-21
2.80.50.51
3.2-2-21
3.60.50.51
4-2-21
4.40.50.51
4.8-2-21
5.20.50.51

Every 4th of 60 samples, from the table stimulus.

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)1 … 1
rampRamp: slope 1 from t = 01 … 1
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias1 … 1
stepStep: 0 -> 1 at t = 1 s1 … 1

Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample

Category static · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/System_Identification__Validation__Model_Fit_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).