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

Downsample — Control Systems/Resampling

Control_Systems/Resampling/Downsample · 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.

Downsample

Control Systems / Resampling

Keeps one sample in every K and holds it until the next one is kept, so the output runs at the block's own rate rather than at a slower one:

y[k] = u[k] when k mod K = offset, and y[k] = y[k−1] on the K−1 samples in between. Before the first kept sample – that is, while k < offset – the block emits its Initial Condition. The sample index k counts from 0 at the start of the run.

Ports

  • Input – the signal u to decimate, of any size [m,n]. Every entry is latched and held on the same samples; the block does not stagger them and the entries do not interact.
  • Output – the decimated-and-held signal y, the SAME size [m,n] as the input.

Parameters

  • Downsampling Factor – K, the number of samples in one period. A whole number of 1 or more; at K = 1 every sample is kept and the block is a wire. Defaults to 2.
  • Sample Offset – which sample of each period is the kept one, as a whole number from 0 to K−1. At 0 the very first sample of the run is kept and the initial condition is never seen; at a larger value the block emits the initial condition for exactly that many samples first. Defaults to 0.
  • Initial Condition – a scalar, what the block emits before the first kept sample. It is broadcast to every entry of the signal, so one value seeds a matrix signal whatever its size. Defaults to 0, and is only ever visible when the offset is nonzero.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period. On this block it also sets what one "sample" means: K counts steps of this rate, so the retained stream comes out at one Kth of it.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The factor and the offset are baked into the core at export time rather than exposed as tunable parameters: they are structural, and a decimator whose period could be retuned on a built core would be a different block. The initial condition is baked in for the same reason – it seeds a state rather than scaling arithmetic.

The three HDL targets are not simulation-only here: the block does no arithmetic at all, so a core is an enable-gated register plus a small counter, and the Q-format word is copied rather than computed on – not one bit of it is disturbed between the input port and the output. What those columns do still carry is the port's own quantization, which is a property of an HDL signal and not of this block: measured over a 1001-sample export-verification run, the three of them come back at 0.00076–0.00095 % residual, the same figures a block that only passes its input through scores in the same run, while all seven software targets are exact at residual 0.

What every target does have to write the same way is the kept sample: it publishes the input directly rather than reading the register it is storing into, because an HDL register write is deferred to the clock edge and reading it back in the same tick would return the PREVIOUS kept sample.

Simulink bridge

Import and export, mapped to dspsigops/Downsample – the DSP System Toolbox block, not a core Simulink one. "Downsampling Factor" to DownsamplingFactor, "Sample Offset" to SampleOffset and "Initial Condition" to InitialConditions, all three pass-through.

Three of that block's parameters are always emitted with a fixed value, because this block offers no choice behind them: FactorSource = Dialog parameter (the factor is a config here, never a port), InputProcessing = Elements as channels (sample based) and RateOptions = Enforce single-rate processing. That last pair is what makes the crossing exact: it is the one configuration in which the Simulink block's output port runs at its input's rate, which is the only kind of block an ICore wire can carry.

"Sampling Time (s)" does not cross. dspsigops/Downsample defines no SampleTime parameter at all – verified against the R2026a block dialog – and set_param on a parameter a block does not define is a hard error in MATLAB that aborts the whole generated script rather than degrading. The rate stays on the ICore side, and a block configured with an explicit positive rate reports that it did not cross.

Notes

  • Stateful: the retained sample and the phase counter. Both start from the configured initial condition and 0 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.
  • Downsample vs. Zero-Order Hold. Both hold a value between updates, and the difference is what decides when an update happens. Zero-Order Hold updates on its own "Sampling Time (s)" – a period in seconds – and has neither a phase nor a seed. Downsample updates every Kth sample of the rate it is running at, counts that phase from the start of the run, and carries an initial condition for the samples before the first update. Reach for the first to model a sampler and for this one to decimate a stream by a whole factor.
  • No anti-alias filter. This block keeps samples; it does not filter first, so anything above half the retained rate folds back into the band exactly as it does in the Simulink block. Put a low-pass in front of it if that matters.
  • It is not MATLAB's downsample function, which returns a SHORTER vector. A wire cannot get shorter, so the retained samples are held instead – which is what the Simulink block does, and what this block is compared against.

Code facts#

FactValue
registered typeControl_Systems/Resampling/Downsample
familyControl_Systems/Resampling
solver environment classICoreBlock_0_Control_Systems_1_Resampling_2_Downsample
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Resampling/Downsample/ICoreBlock_0_Control_Systems_1_Resampling_2_Downsample.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Resampling/Downsample/ICoreBlock_0_Control_Systems_1_Resampling_2_Downsample.h
default size on canvas90 × 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
Downsampling Factor2DownsamplingFactor
Sample Offset0SampleOffset
Initial Condition0InitialConditions

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::Both
Simulink pathdspsigops/Downsample
port-count rulePortsParam::None
SampleTime parameterno — the counterpart defines none; the rate stays on the ICore side
always setFactorSource = Dialog parameter, InputProcessing = Elements as channels (sample based), RateOptions = Enforce single-rate processing
ICore configSimulink parameterValue translation
Downsampling FactorDownsamplingFactorpasses through
Sample OffsetSampleOffsetpasses through
Initial ConditionInitialConditionspasses through

Caveat (shown to the user): dspsigops/Downsample has NO SampleTime parameter (verified against the R2026a block dialog), so "Sampling Time (s)" does not cross. The entry also pins the block to sample-based, single-rate processing - the one configuration whose output port runs at its input's rate, and therefore the only one an ICore wire can carry

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

Downsample block — keep every Kth sample and hold it, with a phase offset A single-rate decimator. It latches its input on one sample in every K and holds that value on the K-1 samples in between, so the output carries the retained samples at the SAME rate the block runs at rather than at a slower one:

y[k] = u[k] when k mod K = offset (a latch sample) y[k] = y[k-1] otherwise (a hold sample) y[k] = x0 before the first latch sample, i.e. while k < offset

k counts SAMPLES from the start of the run, beginning at 0.

⚠ THE SEMANTICS ARE MEASURED AGAINST R2026a, NOT INFERRED FROM THE FUNCTION downsample. The MATLAB function returns a SHORTER vector; a wire cannot. What this block reproduces is the real Simulink block dspsigops/Downsample driven with InputProcessing = 'Elements as channels (sample based)' and RateOptions = 'Enforce single-rate processing', which is the one configuration of it whose output port runs at the input's own rate. Driven with u = 1..12 at K = 3 that block answers

offset 0 : 1 1 1 4 4 4 7 7 7 10 10 10 offset 1 : 0 2 2 2 5 5 5 8 8 8 11 11 (0 = InitialConditions) offset 2 : 0 0 3 3 3 6 6 6 9 9 9 12

and with InitialConditions = -9 the first row of offset 1 becomes -9. That is the recursion above, term for term, and it is what the block's Simulink entry claims.

⚠ THE LATCH SAMPLE PUBLISHES u, NOT THE REGISTER — and that is a rule the three HDL targets impose on the other seven rather than a choice. A held value written on a clocked tick is a REGISTERED assignment, so reading the register back in the same tick returns its PRE-clock contents: a body that stored u and then emitted the register would emit the PREVIOUS retained sample on every latch, and lag the reference by a whole interval on one sample in K. So every target writes the same branch — publish u on a latch sample, publish the register otherwise — and no target ever reads a value it is in the middle of writing.

DOWNSAMPLE vs. ZERO-ORDER HOLD. Both hold a value between updates and they are the most confusable pair in the library. The difference is what decides WHEN an update happens: Zero-Order Hold updates on its own "Sampling Time (s)", a PERIOD IN SECONDS, and has no phase and no initial condition; Downsample updates every Kth sample of whatever rate it is running at, counts that phase from the start of the run, and carries a seed for the samples before the first update. Reach for Zero-Order Hold to model a sampler, and for Downsample to decimate a stream by a whole factor.

Sample results#

Downsample — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sampleDownsample — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-2-1012-2-10123inputoutput
tin ICoreDouble-Out-0out ICoreDouble-Out-0
0-2-2
0.40.50.5
0.8-2-2
1.20.50.5
1.6-2-2
20.50.5
2.4-2-2
2.80.50.5
3.2-2-2
3.60.50.5
4-2-2
4.40.50.5
4.8-2-2
5.20.50.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)0 … 0
rampRamp: slope 1 from t = 00 … 5.8
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-0.9962 … 0.9996
stepStep: 0 -> 1 at t = 1 s0 … 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 513f52f58 · produced by docsSample --out <folder> --blocks Downsample Upsample --steps 60 · data docs/generated/samples/Control_Systems__Resampling__Downsample.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).