Goertzel — Control Systems/Transforms
Control_Systems/Transforms/Goertzel · 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.
Goertzel
Control Systems / Transforms
One bin of a DFT, computed by a second-order recursion rather than a sum of products – the cheapest tone detector there is. It costs N multiply-accumulates for a bin, against the 2N a direct sum costs and the N·log₂N a whole FFT costs to find bins nobody asked for. For a bin index k over a window of N, with w = 2π(k−1)/N:
s[n] = u[n] + 2·cos(w)·s[n−1] −
s[n−2], started from zero, then
Re = s[N]·cos(w(N−1)) − s[N−1]·cos(wN) and
Im = −s[N]·sin(w(N−1)) +
s[N−1]·sin(wN).
The answer is the same complex DFT coefficient the FFT would give at that bin, to rounding.
Ports
- u – the window, [N,1] or [1,N], with N any length of at least 1. There is no power-of-two rule: a recursion has no radix, and that is one of the reasons to reach for this block. Build the window with a Tapped Delay if the signal arrives one sample at a time.
- Re – the real part of the coefficient, one entry per index in Frequency Indices, in that order, with the same orientation as u.
- Im – the imaginary part, same size and order.
Two outputs rather than one magnitude: a DFT coefficient is complex and an ICore signal is not, so folding it to |X| here would discard the phase and duplicate what FFT Magnitude already does. Square and add the two for power.
Parameters
- Frequency Indices – which bins to evaluate, as a vector. One-based, exactly like MATLAB's index argument, so 1 is DC and 2 is the first non-zero frequency. Its length sets the height of both outputs. Entries need not be whole numbers: a fractional index is the generalised Goertzel and interpolates between bins. Default [2].
- 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 five constants a bin needs are evaluated once at export time and baked in as literals, and the recursion is emitted unrolled, so no sine or cosine is evaluated at run time and no target needs a loop bound or a scratch array.
The three HDL targets are genuinely fixed-point and synthesizable – not simulation-only, unlike the transform blocks beside them. A two-tap recursion with two baked coefficients is what tone detection looks like in hardware, and it is the reason to have this block there at all. Each carries s[n−1] and s[n−2] in two Q-format registers; they are scratch for the within-sample loop and hold nothing between samples.
Simulink bridge
No equivalent, so nothing crosses in either direction.
goertzel is a Signal Processing Toolbox MATLAB function with no
Simulink block behind it, and the DSP System Toolbox's transform library –
FFT, IFFT, Magnitude FFT, DCT, IDCT, the two cepstra, the two wavelet
transforms, Analytic Signal, the two Short-Time FFTs and Zoom FFT – has no
Goertzel block either. Its Zoom FFT is the nearest thing and is a
different algorithm.
Notes
- Algebraic and stateless. The window arrives whole on the input port, so the recursion runs to completion inside one step; nothing is carried between samples.
- No state space. The map from window to coefficient is a genuine D matrix, but the recursion is the point of the block and a dense D would throw it away.
- The generalised form is implemented, matching R2026a: a fractional bin index is legal and interpolates between bins.
- A very long window makes the recursion ill-conditioned near w = 0 and w = π, where 2cos(w) approaches ±2 and the intermediate values grow. That is a property of the algorithm and not of this implementation.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Transforms/Goertzel |
| family | Control_Systems/Transforms |
| solver environment class | ICoreBlock_0_Control_Systems_1_Transforms_2_Goertzel |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/Goertzel/ICoreBlock_0_Control_Systems_1_Transforms_2_Goertzel.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/Goertzel/ICoreBlock_0_Control_Systems_1_Transforms_2_Goertzel.h |
| default size on canvas | 130 × 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 |
|---|---|---|
Frequency Indices | [2] | — |
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. goertzel is a Signal Processing Toolbox MATLAB function, not a block, and the DSP System Toolbox's transform library carries no Goertzel block -- its Zoom FFT is the nearest thing and is a different algorithm
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The checker has a blind spot here — it could not resolve something (a grouped port bullet, a computed config name), which is reported and never counted as a pass. A reader has to settle it:
B0every stimulus in the sample errored — cross-checks skipped
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).
Goertzel -- one DFT bin by a second-order recursion (MATLAB goertzel) Per bin k (ONE-BASED, exactly like MATLAB's INDVEC), over a window of N:
w = 2*pi*(k-1)/N s[n] = u[n] + 2*cos(w)s[n-1] - s[n-2] s[-1] = s[-2] = 0 Re = s[N]*cos(w(N-1)) - s[N-1]cos(w*N) Im = -s[N]*sin(w(N-1)) + s[N-1]*sin(w*N)
N multiply-accumulates for a bin, against 2N for a direct sum and N*log2(N) for a whole FFT that finds bins nobody asked for. That is the block's whole reason to exist, and it is why the HDL targets here are REAL fixed-point rather than simulation-only: a two-tap recursion with two baked constants is exactly what tone detection looks like in hardware.
⚠ THE LAST TWO LINES ARE AN ALGEBRAIC REDUCTION of what goertzelImpl.m writes as (s[N] - exp(-i*w)s[N-1]) * exp(-i*w(N-1)). Measured: the two agree to ~1e-15 on every case checked against R2026a. The reduced form is used because it needs TWO products and NO intermediate, and a VHDL block body has two fixed-point process variables and no
realscratch at all -- so the literal complex form has nowhere to put its partial results.⚠ THE BIN INDEX NEED NOT BE AN INTEGER: this is the GENERALIZED Goertzel that R2026a ships (Sysel & Rajmic, 2012). Verified against MATLAB at k = 2.5, where the answer interpolates between bins and the recursion is unchanged.
⚠ N IS NOT CONSTRAINED TO A POWER OF TWO. A recursion has no radix.
The window is presented whole on the input port, so the recursion runs to completion inside ONE step and the block is ALGEBRAIC -- it holds no state between samples. The two registers the HDL targets declare are scratch for that within-tick loop, nothing more.
Sample results#
No stimulus produced a sampled output in this rig — Bin index out of range at: ICore Blocks/Home/Goertzel. That is a fact about the single-block rig, not a verdict on the block: an offline batch fit, a block whose output only appears at onSolverFinish, or one that needs a driven environment cannot be exercised alone.
Category unsampled · sample time 0.1 · 60 steps · commit 2b8440534 · produced by docsSample --out <folder> --blocks FFT_Shift Bit_Reverse_Order Walsh_Hadamard_Transform Goertzel --steps 60
Sample data: docs/generated/samples/Control_Systems__Transforms__Goertzel.json