Chirp Z Transform — Control Systems/Transforms
Control_Systems/Transforms/Chirp_Z_Transform · 1 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.
Chirp-Z Transform
Control Systems / Transforms
Evaluates the z-transform of the window on its input along a spiral
contour:
X[k] = Σₙ u[n]·a−n·wnk
for k = 0 … M−1, which is MATLAB's
czt(u, M, w, a). a is where the contour starts and w
is the ratio between one point on it and the next; each is given as a
magnitude and an angle rather than as a complex number.
The contour is the whole point. With |w| = 1 and an angle of −2π/M the contour is the unit circle and the block is the DFT. Make the angle smaller and it walks a short arc of that circle, so M points resolve a narrow band as finely as you like – a zoom the DFT cannot do, whose resolution is fixed at 2π/N. Take |w| ≠ 1 and it leaves the circle for a spiral into or out of the z-plane.
Ports
- u – the window to transform, a vector: an [N,1] column or a [1,N] row, with N from 1 to 32. Entry n is u[n] in the sum above, oldest first.
- Re – the real part of X, one entry per contour point, so M long and in the same orientation as the input.
- Im – the imaginary part of the same X, the same size. Two real ports rather than one complex one, because an ICore signal carries doubles.
Parameters
- Output Points – M, how many points of the contour to evaluate. A whole number from 1 to 32. It is independent of the window length: M may be smaller than N, equal to it, or larger.
- Ratio Magnitude – |w|. Exactly 1 keeps the contour on a circle; less than 1 spirals outward from it and more than 1 inward, since the exponent carries a minus sign in the transform's own convention.
- Ratio Angle (rad) – arg(w), the angle stepped between consecutive points. −2π/M is the DFT; a value of smaller magnitude zooms into a narrower band.
- Start Magnitude – |a|, the radius the contour starts at. 1 starts on the unit circle.
- Start Angle (rad) – arg(a), the angle it starts at. 0 starts at z = |a|, i.e. at DC when |a| = 1.
- 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 M×N complex weights are structural and are inlined into the generated arithmetic: they follow from the parameters and from both lengths, and how many of them there are is what the emitted core is shaped by. Every target emits two real dot products per output entry and computes no power, sine or cosine 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. Whether that is useful is decided by the contour, not by the block: the weights carry |w|nk, and nk reaches (N−1)(M−1), so |w| = 0.97 over an 8-point window bottoms out at 0.9728 = 0.43 – far above the format's 1.5×10−5 quantum – while |w| = 0.8 over 32 points reaches 10−97 and that whole corner of the matrix quantizes to zero. Keep |w| near 1 in fixed point, or export to a software target.
Simulink bridge
No equivalent (Support::None). czt is a
Signal Processing Toolbox MATLAB function, and the DSP System Toolbox's
transform library carries FFT, IFFT, Magnitude FFT, DCT, IDCT, the two cepstra,
the wavelet pair, Analytic Signal, the Short-Time FFTs and Zoom FFT –
measured on the installed R2026a, and no chirp-z among them. Zoom FFT is the
nearest thing and is a different algorithm (decimate-and-FFT rather than a
contour). The bridge reports this block rather than dropping it, and it
therefore carries no parity testbench; code export verification still
covers it across all ten languages.
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 with M = 5,
w = 0.97·e−0.7i and a = 1.05·e0.2i the
weights reproduce
cztto 1.3e−15, and the DFT special case matchesfftto 7e−15. - N need not be a power of two, and M need not equal N. A sum has no radix.
- No state space. The map is linear in the window, but a fixed matrix over N past 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/Chirp_Z_Transform |
| family | Control_Systems/Transforms |
| solver environment class | ICoreBlock_0_Control_Systems_1_Transforms_2_Chirp_Z_Transform |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/Chirp_Z_Transform/ICoreBlock_0_Control_Systems_1_Transforms_2_Chirp_Z_Transform.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/Chirp_Z_Transform/ICoreBlock_0_Control_Systems_1_Transforms_2_Chirp_Z_Transform.h |
| default size on canvas | 140 × 90 px |
| ports at insert | 1 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 | u |
| 2 | out | ICoreDouble | Re |
| 3 | 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 |
|---|---|---|
Output Points | 8 | — |
Ratio Magnitude | 1 | — |
Ratio Angle (rad) | -0.78539816339744828 | — |
Start Magnitude | 1 | — |
Start Angle (rad) | 0 | — |
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 Simulink equivalent. czt is a Signal Processing Toolbox MATLAB function, not a block, and the DSP System Toolbox transform library was searched on the installed R2026a rather than assumed: it carries FFT, IFFT, Magnitude FFT, DCT, IDCT, the two cepstra, DWT/IDWT, Analytic Signal, the Short-Time FFTs and Zoom FFT, and no chirp-z. Zoom FFT is the nearest name and is a different algorithm
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).
Chirp-Z Transform -- the z-transform of a window on a spiral contour (MATLAB czt) X[k] = SUM over n of u[n] * a^(-n) * w^(n*k), k = 0 .. M-1
a is where the contour starts and w is the ratio between consecutive points on it. Both are complex; both arrive here as a MAGNITUDE and an ANGLE, so no configuration variable has to carry a complex number and no target has to do complex arithmetic to read one.
ONE MATRIX PRODUCT, TEN TIMES. a^(-n)*w^(n*k) depends on the parameters and the two lengths and never on the signal, so the M*N complex weights are derived once -- when the configuration is read and the input width is known -- and every target inlines them as two real dot products per output element. No emitted core computes a power, a sine or a cosine; it multiplies and adds.
⚠ MEASURED AGAINST R2026a rather than asserted. Over u = [0.9 -0.35 1.25 0.4 -0.8 0.15 0.55 -1.1] with M = 5, w = 0.97*exp(-0.7i) and a = 1.05*exp(0.2i), the weights derived here reproduce czt's five complex answers to 1.3e-15. The DFT special case was measured too: czt(x) with its own defaults and fft(x) agree to 7e-15, which is the identity the description's "reproduces the DFT" sentence rests on.
⚠ THE HDL TARGETS ARE GENUINE Q16.16 AND THE CONTOUR IS WHAT DECIDES WHETHER THAT IS USEFUL. The weights carry |w|^(n*k), and n*k reaches (N-1)*(M-1): at |w| = 0.97 over an 8-point window that is 0.97^28 = 0.43, comfortably above the format's 1.5e-5 quantum, while at |w| = 0.8 and 32 points it is 1e-97 and every weight in the far corner of the matrix quantizes to zero. That is a property of the CONTOUR, not of the block, and the description says so rather than hiding it behind a "simulation only" label the arithmetic does not need.
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 it is a fixed matrix over a WINDOW rather than an A/B/C/D pair evolving in time, which is the same reason Savitzky-Golay carries none.
Sample results#
| t | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 [8x1] entry 0 | out ICoreDouble-Out-1 [8x1] entry 0 |
|---|---|---|---|
| 0 | -2 | [-2, -2, -2, -2]… | [0, 0, 0, 0]… |
| 0.4 | 0.5 | [0.5, 0.5, 0.5, 0.5]… | [0, 0, 0, 0]… |
| 0.8 | -2 | [-2, -2, -2, -2]… | [0, 0, 0, 0]… |
| 1.2 | 0.5 | [0.5, 0.5, 0.5, 0.5]… | [0, 0, 0, 0]… |
| 1.6 | -2 | [-2, -2, -2, -2]… | [0, 0, 0, 0]… |
| 2 | 0.5 | [0.5, 0.5, 0.5, 0.5]… | [0, 0, 0, 0]… |
| 2.4 | -2 | [-2, -2, -2, -2]… | [0, 0, 0, 0]… |
| 2.8 | 0.5 | [0.5, 0.5, 0.5, 0.5]… | [0, 0, 0, 0]… |
| 3.2 | -2 | [-2, -2, -2, -2]… | [0, 0, 0, 0]… |
| 3.6 | 0.5 | [0.5, 0.5, 0.5, 0.5]… | [0, 0, 0, 0]… |
| 4 | -2 | [-2, -2, -2, -2]… | [0, 0, 0, 0]… |
| 4.4 | 0.5 | [0.5, 0.5, 0.5, 0.5]… | [0, 0, 0, 0]… |
| 4.8 | -2 | [-2, -2, -2, -2]… | [0, 0, 0, 0]… |
| 5.2 | 0.5 | [0.5, 0.5, 0.5, 0.5]… | [0, 0, 0, 0]… |
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 Chirp_Z_Transform --steps 60 · data docs/generated/samples/Control_Systems__Transforms__Chirp_Z_Transform.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).