DCT — Control Systems/Transforms
Control_Systems/Transforms/DCT · 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.
DCT
Control Systems / Transforms
The orthonormal type-II discrete cosine transform of the window on its
input, which is MATLAB's dct(u):
y[k] = w[k]·Σₙ u[n]·cos(π(2n+1)k / 2N), with
w[0] = √(1/N) and w[k] = √(2/N) for every other k.
The two weights are what make the transform orthonormal – the coefficient matrix satisfies AT = A−1, so the IDCT block is its transpose and the pair round-trips exactly. A plain cosine sum without them is a different transform, larger by √(2N) on the first entry and by √(N/2) on the rest.
Ports
- u – the window to transform, a vector: an [N,1] column or a [1,N] row, N from 1 to 32. Entry n is u[n] in the sum above, oldest first.
- y – the N cosine coefficients, the same size and orientation as the input. Entry 0 is the DC term.
Parameters
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
There is nothing else to set, deliberately: N is the width of the input port, not a configuration value, so the block and the signal reaching it cannot disagree about the transform length. Simulink's own DCT dialog carries no semantic parameter either – only a table-lookup implementation switch.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.
The N×N cosine coefficients are structural and are inlined into the generated arithmetic: they follow from the window length alone, so every target emits one dot product per output entry and computes no cosine and no square root at run time – those happened at export.
The three HDL targets are genuine synthesizable Q16.16: only multiplies and adds, accumulated at double width and shifted back once per entry. Every coefficient has magnitude at most √(2/N) ≤ 1, comfortably inside the format, so no part of the matrix quantizes away.
Simulink bridge
Both directions, mapping to dspxfrm3/DCT from the DSP
System Toolbox. There are no parameters to map: the block's only dialog
entry chooses between a trigonometric and a table-lookup implementation, which
does not change the answer, and the transform length is inherited from the frame
on both sides.
Two measured caveats. Simulink's DCT requires the window length to be a power of two – a 5-point frame stops the simulation with "The length of the dimension being transformed must be a power of two" – while this block accepts any length from 1 to 32, so a window of 5, 6 or 12 entries has no counterpart to cross to. And the Simulink block has no SampleTime parameter, so an explicit rate set here stays on the ICore side and is reported rather than written into the generated script.
Notes
- Algebraic, with no state. The whole window arrives on the port, so one step is one transform and nothing carries over.
- Measured against R2026a. Over an 8-point window the coefficients here
reproduce
dctto 2.1e−15, anddspxfrm3/DCTsimulated on the same window agreed withdctto 2.2e−16. - The exact inverse is the IDCT block, which is this matrix transposed.
- No state space. The map is linear in the window, but a fixed matrix over N presented samples is not an A/B/C/D pair evolving in time, so the block carries none and model reduction correctly declines to merge it.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Transforms/DCT |
| family | Control_Systems/Transforms |
| solver environment class | ICoreBlock_0_Control_Systems_1_Transforms_2_DCT |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/DCT/ICoreBlock_0_Control_Systems_1_Transforms_2_DCT.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/DCT/ICoreBlock_0_Control_Systems_1_Transforms_2_DCT.h |
| default size on canvas | 130 × 80 px |
| ports at insert | 1 in, 1 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 | u |
| 2 | out | ICoreDouble | y |
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#
No config variable beyond the Sampling Time (s) every block carries.
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 | dspxfrm3/DCT |
| port-count rule | PortsParam::None |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
Caveat (shown to the user): maps to the DSP System Toolbox's DCT, which computes the same orthonormal type-II transform (simulated on an 8-point window and agreeing with MATLAB's dct to 2.2e-16). The transform length is not a parameter on either side: it is inherited from the frame in Simulink exactly as it is taken from the input port here. Two measured caveats -- Simulink's DCT requires a POWER-OF-TWO window and stops the simulation on any other length, while this block accepts 1 to 32; and the Simulink block has no SampleTime parameter, so an explicit rate does not cross the bridge
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).
DCT -- the orthonormal type-II discrete cosine transform of a window (MATLAB dct) y[k] = w[k] * SUM over n of u[n] * cos( pi * (2n+1) * k / (2N) ), k = 0 .. N-1 w[0] = sqrt(1/N), w[k] = sqrt(2/N) for k > 0
THE TWO WEIGHTS ARE THE TRANSFORM, not a cosmetic scale. They are what makes the coefficient matrix A orthonormal, i.e. A' = inv(A) -- which is why IDCT is A' and why the two blocks round-trip exactly. Drop them and the answer is a different transform, too big by sqrt(2N) on the first row and by sqrt(N/2) on the rest.
ONE MATRIX PRODUCT. A[k][n] depends on the window LENGTH and on nothing else: no config variable, no signal. So the N*N coefficients are derived once, when the input port's size settles, and each of the ten targets inlines them as one dot product per output entry. No emitted core computes a cosine or a square root; it multiplies and adds.
⚠ MEASURED AGAINST R2026a rather than asserted. Over the rig's window [0.9 -0.35 1.25 0.4 -0.8 0.15 0.55 -1.1] the coefficients derived here reproduce dct() to 2.1e-15, and dspxfrm3/DCT was SIMULATED on the same window (From Workspace frame, fixed-step discrete) and agreed with dct() to 2.2e-16. Neither number was taken from a doc page.
⚠ SIMULINK'S DCT NEEDS A POWER-OF-TWO WINDOW AND THIS ONE DOES NOT. Measured: a 5-point frame stops the simulation with "The length of the dimension being transformed must be a power of two". A sum has no radix, so this block accepts 1 .. 32 and the catalog entry says in as many words that a non-power-of-two window has nothing to cross to.
⚠ AND THE SIMULINK BLOCK HAS NO SampleTime PARAMETER. Measured with set_param, which is the only way to know: "DCT block (mask) does not have a parameter named 'SampleTime'". The entry therefore sets hasSampleTimeParam = false -- a set_param on a name a block does not define is a hard MATLAB error that aborts the whole generated script.
ALGEBRAIC: the whole window is presented on the input port, so one step completes one transform and nothing is held between samples. No state space -- the map is linear in the input, but a fixed matrix over a WINDOW is not an A/B/C/D pair evolving in time, which is the same reason Chirp-Z Transform carries none.
Sample results#
| t | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 |
|---|---|---|
| 0 | -2 | -2 |
| 0.4 | 0.5 | 0.5 |
| 0.8 | -2 | -2 |
| 1.2 | 0.5 | 0.5 |
| 1.6 | -2 | -2 |
| 2 | 0.5 | 0.5 |
| 2.4 | -2 | -2 |
| 2.8 | 0.5 | 0.5 |
| 3.2 | -2 | -2 |
| 3.6 | 0.5 | 0.5 |
| 4 | -2 | -2 |
| 4.4 | 0.5 | 0.5 |
| 4.8 | -2 | -2 |
| 5.2 | 0.5 | 0.5 |
Every 4th of 60 samples, from the table stimulus.
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 … 1 |
ramp | Ramp: slope 1 from t = 0 | 0 … 5.9 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -1 … 0.9996 |
step | Step: 0 -> 1 at t = 1 s | 0 … 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 3c100aff6f27235305db4ad4d572f32e342718ad · produced by docsSample --out <folder> --blocks DCT IDCT FFT IFFT --steps 60 · data docs/generated/samples/Control_Systems__Transforms__DCT.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).