IFFT — Control Systems/Transforms
Control_Systems/Transforms/IFFT · 2 input / 2 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.
IFFT
Control Systems / Transforms
The complex inverse discrete Fourier transform of the spectrum on its
inputs, which is MATLAB's ifft(X):
u[n] = (1/N)·Σₖ X[k]·e+2πi·kn/N
for n = 0 … N−1.
A spectrum is complex on both sides, so the block has four vector ports: the real and imaginary parts in, and the real and imaginary parts of the reconstructed window out. Feeding it the two outputs of the FFT block returns the original window on Re, with Im at rounding noise.
Ports
- Re (input) – the real part of the spectrum X, a vector: an [N,1] column or a [1,N] row, N from 1 to 32. Entry k is Re(X[k]).
- Im (input) – the imaginary part of the same X, and it must be the same size and orientation as Re. Wire a zero constant here to invert a purely real spectrum.
- Re (output) – the real part of the reconstructed window, N long, in the input's orientation.
- Im (output) – its imaginary part, the same size. For a conjugate-symmetric spectrum this is zero to within rounding, and reading it is the cheapest check that the spectrum was symmetric.
Parameters
- Scaling – what multiplies the sum.
- 1/N (ifft) – the inverse transform exactly as
ifftdefines it, so that IFFT after FFT is the identity. The default. - None – the same sum left unscaled, N times larger. Use it when the forward transform already carried the 1/N, so that exactly one of the two does.
- 1/N (ifft) – the inverse transform exactly as
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
N is not a parameter: it is the width of the input ports, which must agree with each other.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.
The N×N twiddle pairs are structural and are inlined into the generated arithmetic, so every target emits four products per term – two accumulations, each reading both inputs – and computes no sine or cosine at run time.
The three HDL targets are genuine synthesizable Q16.16: only multiplies and adds, accumulated at double width and shifted back once per entry. With the default 1/N scaling every twiddle has magnitude at most 1/N, which keeps the accumulation comfortably inside the format.
Simulink bridge
No equivalent a bridge can write (Support::None), and the
reason is
about the wire rather than the block. dspxfrm3/IFFT exists and
computes the same transform – simulated on an 8-point complex frame, it
agreed with ifft exactly – but it takes its whole spectrum on
one complex port, while this block reads the real and imaginary parts on
two real ports because an ICore signal carries doubles. There is no port-to-port
mapping for the bridge to write. In Simulink, feed dspxfrm3/IFFT
through simulink/Math Operations/Real-Imag to Complex on the way in
and Complex to Real-Imag on the way out. The bridge reports the
block rather than dropping it, and code export verification still covers it
across all ten languages.
Notes
- Algebraic, with no state. The whole spectrum arrives on the ports, so one step is one transform and nothing carries over.
- Measured against R2026a. The coefficients reproduce
ifftto 5.6e−17 on a spectrum whose imaginary part is zero and to 1.1e−16 on one whose two halves are independent – the case that reaches the cross terms – anddspxfrm3/IFFTsimulated on an 8-point complex frame agreed withifftexactly. - Both inputs must be the same size. A mismatch is reported by name once the port sizes have settled rather than silently padded.
- Four products per term. Both operands are complex, so the cross terms −Xi·sin and +Xi·cos are part of the answer; a core that drops them is right only while the imaginary input is zero.
- A direct sum, not a radix-2 algorithm. N need not be a power of two, and the cost is N² products; the 32-entry limit exists because those products are unrolled into the exported core.
- No state space. A fixed matrix over N presented samples is not an A/B/C/D pair evolving in time, so model reduction correctly declines to merge it.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Transforms/IFFT |
| family | Control_Systems/Transforms |
| solver environment class | ICoreBlock_0_Control_Systems_1_Transforms_2_IFFT |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/IFFT/ICoreBlock_0_Control_Systems_1_Transforms_2_IFFT.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/IFFT/ICoreBlock_0_Control_Systems_1_Transforms_2_IFFT.h |
| default size on canvas | 130 × 100 px |
| ports at insert | 2 in, 2 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 | Re |
| 2 | in | ICoreDouble | Im |
| 3 | out | ICoreDouble | Re |
| 4 | out | ICoreDouble | Im |
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 |
|---|---|---|
Scaling | 1/N (ifft)%~%None~~1/N (ifft) | — |
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::None |
| Simulink path | — |
| port-count rule | PortsParam::None |
SampleTime parameter | yes |
Caveat (shown to the user): no single-block equivalent, and the reason is the WIRE rather than the block. dspxfrm3/IFFT exists in the DSP System Toolbox and computes the same transform -- simulated on an 8-point complex frame, it agreed with MATLAB's ifft exactly -- but it takes its whole spectrum on ONE COMPLEX port, while this block reads the real and imaginary parts on two real ports because an ICore signal carries doubles. There is no port-to-port mapping to write. In Simulink, put simulink/Math Operations/Real-Imag to Complex in front of dspxfrm3/IFFT and Complex to Real-Imag after it. See the FFT block for the forward half
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).
IFFT -- the complex inverse discrete Fourier transform of a spectrum (MATLAB ifft) u[n] = (1/N) * SUM over k of X[k] * exp(+2*pi*i*k*n/N), n = 0 .. N-1
Four vector ports: Re and Im in, Re and Im out. A spectrum is complex on both sides, and an ICore wire carries doubles.
TWO SIGNS AND A SCALE ARE THE WHOLE DIFFERENCE FROM THE FFT BLOCK: the exponent is positive and the sum is divided by N. The modular angle reduction, the unrolled products and the accumulation order are the forward block's, deliberately, so the two files read side by side.
⚠ FOUR PRODUCTS PER TERM, NOT TWO, because both operands are complex: Re += Xr*cos - Xi*sin Im += Xr*sin + Xi*cos Dropping the cross terms gives an answer that is right whenever the imaginary input is zero and wrong the moment it is not -- which a rig driving both inputs from one shared waveform would never reach. The rig here drives them from INDEPENDENT windows for exactly that reason.
⚠ THE ANGLE IS REDUCED MODULO N BEFORE THE TRIG CALL, as in the forward block: k*n reaches (N-1)^2 = 961 at the longest window, and a cosine of ~6000 radians has already lost most of its low bits to argument reduction.
⚠ MEASURED AGAINST R2026a rather than asserted. The coefficients here were run through a stub harness on the same vectors a MATLAB batch was given: over an 8-point spectrum with a ZERO imaginary part they reproduce ifft() to 5.6e-17, and over one with an INDEPENDENT imaginary window -- the case that reaches the cross terms at all -- to 1.1e-16. Separately, dspxfrm3/IFFT was SIMULATED on an 8-point complex frame and agreed with ifft() EXACTLY.
ALGEBRAIC: the whole spectrum is presented on the input ports, so one step completes one transform and nothing is held between samples. No state space -- the map is linear in the inputs, but a fixed matrix over a WINDOW is not an A/B/C/D pair evolving in time.
Sample results#
| t | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 | out ICoreDouble-Out-1 |
|---|---|---|---|---|
| 0 | -2 | -2 | -2 | -2 |
| 0.4 | 0.5 | 0.5 | 0.5 | 0.5 |
| 0.8 | -2 | -2 | -2 | -2 |
| 1.2 | 0.5 | 0.5 | 0.5 | 0.5 |
| 1.6 | -2 | -2 | -2 | -2 |
| 2 | 0.5 | 0.5 | 0.5 | 0.5 |
| 2.4 | -2 | -2 | -2 | -2 |
| 2.8 | 0.5 | 0.5 | 0.5 | 0.5 |
| 3.2 | -2 | -2 | -2 | -2 |
| 3.6 | 0.5 | 0.5 | 0.5 | 0.5 |
| 4 | -2 | -2 | -2 | -2 |
| 4.4 | 0.5 | 0.5 | 0.5 | 0.5 |
| 4.8 | -2 | -2 | -2 | -2 |
| 5.2 | 0.5 | 0.5 | 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__IFFT.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).