Discrete Varying Observer Form — Control Systems/Linear Parameter Varying
Control_Systems/Linear_Parameter_Varying/Discrete_Varying_Observer_Form · 7 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.
Discrete Varying Observer Form
Control Systems / Linear Parameter Varying
A discrete observer with state feedback, in which every matrix is a signal rather than a setting. Each sample, in this order:
u[k] = −K[k]·x[k]
ε[k] = y[k] − C[k]·x[k] − D[k]·u[k]
x[k+1] = A[k]·x[k] + B[k]·u[k] + L[k]·ε[k]
The block estimates a plant's state from its measured output y and drives it with the feedback it computes, so a scheduling signal can move the plant model, the observer gain and the feedback gain together while it runs.
Ports
- y – the measured plant output, [p,1].
- A – the state matrix, [n,n]. Its size defines n.
- B – the input matrix, [n,m].
- C – the output matrix, [p,n].
- D – the feedthrough matrix, [p,m].
- K – the state-feedback gain, [m,n]. Note the minus sign is in the block: u = −K·x, so a stabilizing K is the one you would compute for that convention, not its negation.
- L – the observer gain, [n,p], multiplying the innovation.
- u – the computed plant input, [m,1].
- x – the current state estimate x[k], [n,1].
- x+ – the next state estimate x[k+1], [n,1].
The seven sizes must agree exactly, and a disagreement names both offending shapes once the model's port sizes have settled. Nothing is scalar-expanded: these are matrices with a meaning, and expanding one silently would hide a mis-wired schedule.
Parameters
- Initial State Vector – the estimate x[0]. A scalar is
used for every state, which is how Simulink's own
x0behaves and the only form that can be written before n is known; an [n,1] vector sets each state separately. Defaults to0. - 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.
Only the dimensions n, m and p are fixed at export time, so every loop is statically sized while all seven matrices are read from their signals each step. There is consequently no tunable parameter on the exported core: retuning is what the gain ports are for, and they stay live on the deployed core. Only the initial estimate is emitted as a constant.
Every target emits the three lines in the order above – feedback, then innovation, then update – so the exported core cannot disagree with the simulation about which u the innovation saw.
The three HDL targets carry the arithmetic in Q16.16 fixed point and are fully synthesizable. A gain arriving on a port is already quantized to one Q16.16 quantum (1.5×10-5) before the block sees it, which is worth knowing for an observer gain L: the innovation it multiplies is a difference of two nearby quantities, so it is the term in this block most sensitive to that quantization.
Simulink bridge
Import and export, mapped to cstblocks/Linear Parameter Varying/Discrete
Varying Observer Form, with "Initial State Vector" going to x0
as a pass-through value.
The rate crosses under a different name. This Simulink block calls its
rate parameter Ts, not SampleTime, so the catalog entry
names it explicitly. Emitting the usual name would be a hard
set_param error that aborts the entire generated script rather than
a warning that degrades.
OutputX and OutputDX are always implied on:
both move ports in Simulink, and the ICore block's port list is fixed at 7 in /
3 out.
Notes
- Discrete only, and stateful: the state is the [n,1] estimate x.
- No algebraic loop, despite D multiplying a signal the block itself produces. u depends on the state alone, so the innovation's D·u term is already known by the time it is needed – which is exactly why the three lines have to run in the order given.
- No state space, deliberately, for the same reason as Discrete Varying State Space: linear at every instant, time-varying overall, so model reduction is right to refuse it rather than merge a snapshot of whatever the gain ports happened to carry.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Linear_Parameter_Varying/Discrete_Varying_Observer_Form |
| family | Control_Systems/Linear_Parameter_Varying |
| solver environment class | ICoreBlock_0_Control_Systems_1_Linear_Parameter_Varying_2_Discrete_Varying_Observer_Form |
| source | src/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Linear_Parameter_Varying/Discrete_Varying_Observer_Form/ICoreBlock_0_Control_Systems_1_Linear_Parameter_Varying_2_Discrete_Varying_Observer_Form.cpp |
| header | src/ICoreSDK/ICoreBlockLibrary/Blocks/Control_Systems/Linear_Parameter_Varying/Discrete_Varying_Observer_Form/ICoreBlock_0_Control_Systems_1_Linear_Parameter_Varying_2_Discrete_Varying_Observer_Form.h |
| default size on canvas | 160 × 150 px |
| ports at insert | 7 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 | y |
| 2 | in | ICoreDouble | A |
| 3 | in | ICoreDouble | B |
| 4 | in | ICoreDouble | C |
| 5 | in | ICoreDouble | D |
| 6 | in | ICoreDouble | K |
| 7 | in | ICoreDouble | L |
| 8 | out | ICoreDouble | u |
| 9 | out | ICoreDouble | x |
| 10 | out | ICoreDouble | x+ |
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 |
|---|---|---|
Initial State Vector | 0 | x0 |
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::Both |
| Simulink path | cstblocks/Linear Parameter Varying/Discrete Varying Observer Form |
| port-count rule | PortsParam::None |
SampleTime parameter | yes |
| rate parameter name | Ts |
| always set | OutputX = on, OutputDX = on |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Initial State Vector | x0 | passes through |
Caveat (shown to the user): all seven matrices are signals on both sides, so none of them is a parameter to carry across
Catalog contract: src/ICoreSDK/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:
B01 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).
Discrete Varying Observer Form block -- observer + state feedback, all matrices varying u[k] = -K[k] x[k] (state feedback, from the state alone) eps[k] = y[k] - C[k] x[k] - D[k] u[k] (innovation, using THAT u) x[k+1] = A[k] x[k] + B[k] u[k] + L[k] eps[k] (observer update)
Seven inputs (y, A, B, C, D, K, L), three outputs (u, x[k], x[k+1]), matching cstblocks/Linear Parameter Varying/Discrete Varying Observer Form port for port.
THE ORDER OF THE THREE LINES IS THE CONTRACT. u is computed first from the current state alone, and eps uses that same u -- which is what stops the D term closing an algebraic loop even though D multiplies a signal this block produces itself. All ten generators emit the three in this order for that reason; getting it wrong shows up as a one-sample lag rather than as an obviously broken output, so it is not the kind of mistake a smoke test finds.
Like its sibling Discrete Varying State Space: no ICoreStateSpace (linear at every instant, time-varying overall), discrete by nature, and every generated loop statically sized while the coefficients themselves are read fresh each step.
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) | 0 … 0 |
ramp | Ramp: slope 1 from t = 0 | -2.302e55 … 0 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -56.78 … 70.38 |
table | Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample | -1454 … 2908 |
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/Control_Systems__Linear_Parameter_Varying__Discrete_Varying_Observer_Form.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).