Generated reference › Interpolate — Control Systems/Resampling
kind: generated#block#control-systems-resampling

Interpolate — Control Systems/Resampling

Control_Systems/Resampling/Interpolate · 1 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

Control Systems / Resampling

Inserts L−1 zeros between input samples and filters the result, which is how a stream is raised to a higher rate without the spectral images the zeros create. The block runs at the fast rate – one tick per output sample – and takes a new input sample on one tick in every L:

v[k] = u[k] when k mod L = 0 and v[k] = 0 otherwise, then y[k] = c0·v[k] + c1·v[k−1] + … + cn·v[k−n]. The sample index k counts from 0 at the start of the run, so the very first sample is a new one, and the filter's delay line starts empty.

Drive it from a source running at L times this block's "Sampling Time (s)" and the two grids agree sample for sample, which is the arrangement the reference block produces.

Ports

  • Input – the signal u to raise, of any size [m,n]. Every entry is stuffed and filtered on the same ticks, each carrying its own delay line; the entries do not interact.
  • Output – the interpolated signal y, the SAME size [m,n] as the input, at the block's own (fast) rate.

Parameters

  • FIR Coefficients – c0…cn, the interpolation filter's impulse response as a non-empty vector, first entry multiplying the current stuffed sample and last the oldest. This block does not design the filter: pair it with FIR Window Design, or pass a response designed elsewhere. Defaults to [0.5 1 0.5], which at the default factor of 2 is exactly linear interpolation between input samples.
  • Interpolation Factor – L, the number of output samples per input sample, a whole number of 1 or more. At L = 1 nothing is stuffed and the block is a plain FIR filter. Defaults to 2.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period. On this block that is the fast rate, the one output samples come out at; a new input sample is taken every Lth step of it.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The coefficients and the factor are baked into the core at export time rather than exposed as tunable parameters: the tap count also fixes how many state words the core carries, and an interpolator whose factor could be retuned on a built core would be a different block.

The three HDL targets are not simulation-only. The arithmetic is one multiply-accumulate over a short constant tap list, which the Q16.16 datapath carries without difficulty; what those columns do carry is the port's own quantization, which is a property of an HDL signal rather than of this block. Measured over a 1001-sample export-verification run at four taps and factor 4, the three of them come back at 0.0028–0.0037 % residual while every software target is exact at residual 0. The delay line is emitted unrolled, so a long filter on a large signal is a large entity: the state word count is n×m×p for a filter with n taps behind the current sample on an [m,p] signal.

What every target writes the same way is the stuffed zero: it is shifted into the delay line explicitly on the L−1 ticks between input samples, because a wire holds its upstream value and a body that read the port every tick would filter a held staircase rather than a stuffed stream.

Simulink bridge

Neither imported nor exported, and the reason is the rate rather than a missing counterpart. dspmlti4/FIR Interpolation is a real DSP System Toolbox block that computes exactly this, and it is named here rather than declared absent – but its output port runs at L times its input port's, even under framing = Enforce single-rate processing, which for that block raises the whole model to the fast rate rather than making the block single-rate. An ICore wire carries one rate, and no config on either side carries a multiplication, so a bridge would emit a connection into a model whose rates no longer agree. Reproduce it instead by running the SOURCE at L times this block's "Sampling Time (s)": the two then agree sample for sample.

Its outputBufInitCond has no counterpart here for the same reason Upsample's does not: with no phase offset the first tick is itself a new-sample tick, so there is nothing for a seed to be visible on.

Notes

  • Stateful: the filter's delay line over the stuffed stream, and the phase counter. Both start from zero at the beginning of every run, so a re-run reproduces the stream exactly.
  • Discrete by nature – the phase advances once per sample, so the block always takes its period from its own "Sampling Time (s)" and is never pushed through a continuous solver's intermediate stages.
  • No state space, deliberately. Whether a sample enters the filter depends on WHEN it arrives, so the block is periodically time-varying and no single A/B/C/D describes it; fabricating one would let the model-reduction commands absorb it into a neighbour as an ordinary filter.
  • Interpolate vs. Upsample. Upsample stuffs the zeros and stops, leaving the images in. This block is that followed by the filter that removes them. Reach for Upsample when something downstream will filter, and for this block when nothing will.
  • The filter's gain is yours to choose. Stuffing divides the average power by L, so an interpolation filter is usually given a passband gain of L to put it back – which is why the default [0.5 1 0.5] sums to 2 at the default factor of 2 rather than to 1.

Code facts#

FactValue
registered typeControl_Systems/Resampling/Interpolate
familyControl_Systems/Resampling
solver environment classICoreBlock_0_Control_Systems_1_Resampling_2_Interpolate
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Resampling/Interpolate/ICoreBlock_0_Control_Systems_1_Resampling_2_Interpolate.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Resampling/Interpolate/ICoreBlock_0_Control_Systems_1_Resampling_2_Interpolate.h
default size on canvas100 × 70 px
ports at insert1 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDouble—
2outICoreDouble—

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
FIR Coefficients[0.5 1 0.5]not crossed
Interpolation Factor2not crossed

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
deliberately not crossedFIR Coefficients, Interpolation Factor

Caveat (shown to the user): dspmlti4/FIR Interpolation computes exactly this block's arithmetic, and it is named here rather than declared absent - but its OUTPUT PORT RUNS AT L TIMES ITS INPUT PORT'S, even under framing = 'Enforce single-rate processing', which for that block raises the whole model to the fast rate instead of making the block single-rate. Measured: a probe at a fixed step equal to the source's rate fails with "All sample times in your model must be an integer multiple of the fixed-step size". An ICore wire carries one rate and no config on either side carries a multiplication, so a bridge would emit a connection into a model whose rates no longer agree. Reproduce it by running the SOURCE at L times this block's "Sampling Time (s)"

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 block — insert L-1 zeros between input samples, then FIR filter the result The zero-stuffing half of a rate change, followed by the interpolation filter that turns the images the stuffing created into a smooth stream. The block runs at the FAST rate — one tick per OUTPUT sample — and takes a new input sample on one tick in every L:

v[k] = u[k] when k mod L = 0 (a new input sample enters) v[k] = 0 otherwise (a stuffed zero) y[k] = c0*v[k] + c1*v[k-1] + ... + cn*v[k-n]

k counts SAMPLES from the start of the run, beginning at 0, and the filter's delay line starts empty (zeros), exactly as the reference block's does.

⚠ THE SEMANTICS ARE MEASURED AGAINST R2026a, NOT INFERRED FROM THE FUNCTION interp. Measured on dspmlti4/FIR Interpolation with FilterSource = 'Dialog parameters', InputProcessing = 'Elements as channels (sample based)', h = [1 2 3], L = 3 and u = 1..8 on the slow grid, sampled on the fast grid:

1 2 3 2 4 6 3 6 9 4 8 12 ...

which is the recursion above term for term: at k = 1 the only nonzero tap is c1*v[0] = 2, and at k = 3 a new sample enters and c0*v[3] = 2.

⚠ THIS BLOCK IS Support::None, AND THE REASON IS THE RATE RATHER THAN A MISSING COUNTERPART. dspmlti4/FIR Interpolation is a real block that computes exactly this, and the catalog entry NAMES it rather than declaring it absent. What stops the mapping is that its output port runs at L times its input port's — and it does so even under framing = 'Enforce single-rate processing', which for that block pulls the whole MODEL up to the fast rate rather than making the block single-rate. Measured, and the measurement is what refuted the obvious plan: a probe of it at a fixed step equal to the source's rate failed with "All sample times in your model must be an integer multiple of the fixed-step size", which is the rate multiplication announcing itself. An ICore wire carries one rate and no parameter on either side carries a multiplication, so Support::Both would export an add_line into a model whose rates no longer agree. This is the same finding Upsample recorded, one block over in this family.

⚠ THE STUFFED ZERO IS TAKEN EXPLICITLY, NOT BY OMISSION. In ICore a wire HOLDS its upstream value between the upstream block's own ticks, so on the L-1 ticks between input samples this block's input port still carries the previous sample rather than nothing. A body that simply read the port every tick would therefore filter a zero-order-held staircase, not a stuffed stream — a different signal with a different spectrum. Every one of the ten targets branches

Sample results#

Interpolate — Step: 0 -> 1 at t = 1 sInterpolate — Step: 0 -> 1 at t = 1 s00.51012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0

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 … 5.7
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-0.9962 … 0.9996
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-2 … 2

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit 9237993cf · produced by docsSample --out <folder> --blocks Decimate Interpolate Upfirdn --steps 60 · data docs/generated/samples/Control_Systems__Resampling__Interpolate.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).