Generated reference › Interpolate Matrix X — Control Systems/Gain Scheduling
kind: generated#block#control-systems-gain-scheduling

Interpolate Matrix X — Control Systems/Gain Scheduling

Control_Systems/Gain_Scheduling/Interpolate_Matrix_X · 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.

Interpolate Matrix(x)

Control Systems / Gain Scheduling

Blends a stored stack of matrices along one axis and answers a single matrix: Y = M[lo] + f·(M[hi] − M[lo]), where f is the fraction across an interval and the two slice indices are lo = clamp(floor(k), 0, P−1) and hi = clamp(floor(k)+1, 0, P−1) for a stack of P matrices. It is the primitive a gain-scheduled controller is built from – the schedule holds one matrix per operating point and this block reads it at the operating point you are at.

Pair it with a Prelookup on the scheduling variable: that block turns a value into exactly the (k, f) pair this one consumes, and several schedules sharing one scheduling variable can then search it once and read it many times.

Ports

  • x_k – the interval index k, counted from zero, as a scalar. It is truncated, not rounded, and the two slice indices it selects are clamped separately into [0, P−1]. So the axis has three kinds of region: below zero the first matrix is answered flatly, between two indices the pair either side of k is blended, and at or past the last index the last matrix is answered flatly. In both flat regions the fraction cannot change the answer.
  • x_f – the fraction f across the interval, as a scalar. It is not held in [0,1]: inside the stack a value outside it extrapolates along the line through the two matrices, which is what a Prelookup set to linear extrapolation produces.
  • Output – the blended matrix Y, of the shape of ONE matrix of the stack: Matrix Rows tall and as wide as the Matrix Table. Its size comes from the configuration and not from the inputs, so it is known before the model runs.

Parameters

  • Matrix Table – the stack, with the matrices stacked vertically: P·R rows by C columns for P matrices of R×C. A matrix config cannot hold a three-dimensional array, so this is the one spelling available; the count P is derived from the height and never stated. From a MATLAB array M of size [R C P] the config is reshape(permute(M,[1 3 2]), R*P, C).
  • Matrix Rows – R, how tall one matrix of the stack is. A whole-number scalar of at least 1; it is what cuts the table into slices, and the table's height must be a whole multiple of it. Defaults to 2.
  • 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. The stack is resolved at export time into the constants of a decision chain – one branch per interval, each carrying that interval's matrix and its difference to the next – and inlined rather than exposed as a tunable parameter. That is not a preference: the number of matrices decides how many branches the generated code has, so it cannot be retuned after export. The emitted body grows as P·R·C.

The three HDL targets are not simulation-only: every branch is one multiply and one add on constants known at export time, so no divider is emitted. Two caveats, both about range rather than about the block: a signal is carried in Q16.16 there, so a matrix entry outside ±32767 cannot be represented; and the index and fraction arrive already quantized to about 1.5×10−5, which can move an index that sits exactly on a whole number to the interval below.

Simulink bridge

None (Support::None). Simulink's counterpart is aerolibschedule/Interpolate Matrix(x) in the Aerospace Blockset and it computes the same thing – the measurements this block is written from were taken off it – but its one parameter, matrix, is a three-dimensional array, and neither MATLAB's matrix literal syntax nor an ICore matrix config has a three-dimensional form. So the table cannot cross in either direction: an exported model would carry the ports and the wiring and no schedule at all, which is worse than a block the bridge reports. It is reported rather than dropped silently, and it therefore has no parity testbench; code export verification still covers it across all ten languages.

The Simulink block defines no SampleTime parameter either – measured, its only parameter is the array – so nothing about the rate would have crossed had the table done so.

Notes

  • Algebraic, with no state: the output depends only on the current inputs.
  • The index truncates, it does not round, and that was measured rather than assumed: k = 1.5 answers exactly what k = 1 answers. Rounding agrees with this on every whole-number index and disagrees on every fractional one.
  • Both slice indices are clamped separately, so the upper one is not always lo + 1. Below the table both collapse onto the first matrix and the answer is flat, the mirror of what happens above it – measured, k = −0.5 with f = 0.5 answers the first matrix and not halfway to the second. In either flat region the blend collapses and no multiply is emitted at all.
  • The fraction is not clamped. Outside [0,1] it is let through and extrapolates along the line through the two matrices – but only where there are two: in a flat region there is nothing to extrapolate along.
  • Not linear – selecting between stored matrices is a step function of the index – so the block deliberately carries no state space and model reduction reports it as unmergeable. That holds even when what it is selecting between IS a set of state-space matrices.
  • The inputs are scalars. One index and one fraction select ONE matrix out of the stack; a per-entry index would have to answer a stack rather than a matrix, which is a different block. For a table of VALUES read entry by entry, use Lookup Tables / Interpolation Using Prelookup.
  • For two or three scheduling variables, use Interpolate Matrix(x,y) or Interpolate Matrix(x,y,z), which blend the same stack bilinearly and trilinearly.

Code facts#

FactValue
registered typeControl_Systems/Gain_Scheduling/Interpolate_Matrix_X
familyControl_Systems/Gain_Scheduling
solver environment classICoreBlock_0_Control_Systems_1_Gain_Scheduling_2_Interpolate_Matrix_X
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Gain_Scheduling/Interpolate_Matrix_X/ICoreBlock_0_Control_Systems_1_Gain_Scheduling_2_Interpolate_Matrix_X.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Gain_Scheduling/Interpolate_Matrix_X/ICoreBlock_0_Control_Systems_1_Gain_Scheduling_2_Interpolate_Matrix_X.h
default size on canvas110 × 70 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
1inICoreDoublex_k
2inICoreDoublex_f
3outICoreDouble—

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
Matrix Table[1 0; 0 1; 2 0; 0 2]—
Matrix Rows2—

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): aerolibschedule/Interpolate Matrix(x) computes the same blend -- this block's index and clamping rules were measured off it -- but its ONE parameter, matrix, is a THREE-DIMENSIONAL array, and neither MATLAB's matrix literal syntax nor an ICore matrix config has a three-dimensional form. So the schedule itself cannot cross in either direction, and an exported model would carry the ports, the wiring and no table at all. Reported rather than exported to a counterpart that would come up with an undefined schedule. The Simulink block also defines no SampleTime parameter, measured with set_param

Catalog contract: src/ICoreBlocks/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).

Interpolate Matrix(x) — blend a stored stack of matrices along ONE axis lo = clamp(floor(k), 0, P-1), hi = clamp(floor(k) + 1, 0, P-1) Y = M[lo] + f * (M[hi] - M[lo])

(k, f) is an interval index and the fraction across it, which is what a Prelookup publishes for a scheduling variable. The stack, the index rule, every diagnostic and all ten code generators live in ICoreScheduleMatrixBlockBase, shared with the two- and three-axis siblings; this file is what is genuinely this block's: its axis count, its ports, its configs, its description, its icon and its Simulink entry.

MEASURED IN R2026a, driving the real aerolibschedule block with constants and reading the output back. Ten of the rows, on a stack of four matrices whose first rows are [1 3 5], [11 13 15], [21 23 25] and [31 33 35]:

k = 0, f = 0 -> [1 3 5] the first matrix, unblended k = 0, f = 0.5 -> [6 8 10] halfway to the second k = 1, f = 0.25 -> [13.5 15.5 17.5] k = 1.5, f = 0.25 -> [13.5 15.5 17.5] the SAME answer: the index TRUNCATES k = 2.7, f = 0.3 -> [24 26 28] = 0.7*M[2] + 0.3*M[3] k = 3, f = 0.5 -> [31 33 35] clamped above; f cannot matter there k = 1, f = 2 -> [31 33 35] f is NOT clamped -- it extrapolates k = 1, f = -0.5 -> [6 8 10] and it extrapolates backwards too k = -0.5, f = 0.5 -> [1 3 5] clamped BELOW -- flat, and f cannot act k = -0.001, f = 0.5 -> [1 3 5] still flat, right up to zero

⚠ THE FOURTH ROW PINS THE INDEX RULE DOWN. Reading k by ROUNDING gives 2 for 1.5 and answers [23.5 25.5 27.5] there, and it agrees with truncation on every whole-number index -- so a rig driven by whole numbers alone would never see the difference.

⚠ THE SEVENTH IS WHY NOTHING CLAMPS THE FRACTION. f = 2 answers a value no slice holds. Clamping it "for safety" would silently change the answer on every out-of-range sample, and an out-of-range fraction is exactly what a Prelookup set to linear extrapolation produces.

⚠⚠ AND THE LAST TWO ARE WHY THE FORMULA CLAMPS hi SEPARATELY INSTEAD OF TAKING lo + 1. Below the table both indices collapse onto slice 0 and the answer is flatly M[0], the mirror of what happens above it. The plausible reading -- clamp the LOWER index and use lo + 1 as the upper -- blends M[0] with M[1] at k = -0.5 and answers [6 8 10]; it agrees with the block on every non-negative sample and disagrees on every negative one. That is what the first version of this block did, and a 120-sample random sweep against the real block over five slices caught it at a worst absolute error of 8.06 on a table whose entries span

Sample results#

Interpolate Matrix X — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sampleInterpolate Matrix X — 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 [2x2] entry 0
tin ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0 [2x2] entry 0
0-2-2[1, 0, 0, 1]
0.40.50.5[1.5, 0, 0, 1.5]
0.8-2-2[1, 0, 0, 1]
1.20.50.5[1.5, 0, 0, 1.5]
1.6-2-2[1, 0, 0, 1]
20.50.5[1.5, 0, 0, 1.5]
2.4-2-2[1, 0, 0, 1]
2.80.50.5[1.5, 0, 0, 1.5]
3.2-2-2[1, 0, 0, 1]
3.60.50.5[1.5, 0, 0, 1.5]
4-2-2[1, 0, 0, 1]
4.40.50.5[1.5, 0, 0, 1.5]
4.8-2-2[1, 0, 0, 1]
5.20.50.5[1.5, 0, 0, 1.5]

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 … 2
rampRamp: slope 1 from t = 01 … 2
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias1 … 2
stepStep: 0 -> 1 at t = 1 s1 … 2

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 acb91cb2cdfb90f6fc9d13f8bab94cd95d7dd9d5 · produced by docsSample --out <folder> --blocks Interpolate_Matrix_X Interpolate_Matrix_XY Interpolate_Matrix_XYZ --steps 60 · data docs/generated/samples/Control_Systems__Gain_Scheduling__Interpolate_Matrix_X.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).