Frequency Domain Filter Identification — Control Systems/Signal Modeling
Control_Systems/Signal_Modeling/Frequency_Domain_Filter_Identification · 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.
Frequency-Domain Filter Identification
Control Systems / Signal Modeling
Fits a filter b/a of numerator order nb and denominator order
na to a measured frequency response, sample by sample – MATLAB's
invfreqz(h, w, nb, na, wt) in the discrete domain and
invfreqs(h, w, nb, na, wt) in the continuous one. The response
arrives as its real and imaginary parts; the frequency grid w and the
weights wt are parameters, not signals. With a(1) = 1, the fit
minimizes the weighted equation error over the grid:
Σi wt(i)·|a(wi)·h(wi) − b(wi)|², with z−1 = e−jω in the discrete domain and s = jω in the continuous one
which is one linear least-squares problem whose design matrix is fixed by the grid, solved through the normal equations.
Ports
- Re – the real part of the response, an [nf,1] column with one entry per frequency in the order the Frequencies parameter lists them. nf is read off the wire and must equal the number of frequencies, at most 64.
- Im – the imaginary part, the same size as Re. A response that is real on the whole grid is fed zeros here.
- b – the numerator coefficients, [nb+1, 1]: in descending powers of z−1 in the discrete domain, of s in the continuous one.
- a – the denominator coefficients, [na+1, 1], with a(1) = 1.
Parameters
- Domain – which basis the fit is written in.
- Discrete –
invfreqz: powers of z−1 = e−jω, so the frequencies are in radians per sample and normally lie in [0, π]. - Continuous –
invfreqs: powers of s = jω, so the frequencies are in radians per second and unbounded above.
- Discrete –
- Numerator Order – nb, 0 to 8. It sets the height of b.
- Denominator Order – na, 1 to 8. It sets the height of a.
- Frequencies – the grid, a row or column vector of 1 to 64 values, in radians per sample or radians per second as Domain decides. Its length is the required height of Re and Im, and 2·nf must be at least nb + na + 1 so the fit has as many real equations as unknowns.
- Weights – wt, MATLAB's weighting vector: one positive value per frequency, or a single value for all of them (which changes nothing, since a common factor cancels). The row of the design matrix at each frequency is scaled by √wt, as both .m files do.
- 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, from one program, so every target does the same arithmetic in the same order as the simulation. The grid, the weights and the orders are structural: the whole design matrix is folded into the exported code as constants, and only the response is read from the ports. Re-export after changing any of them.
The three HDL targets run the fit in real arithmetic and
quantize only at the ports: simulation-only, not offered as synthesizable.
A least-squares solve has a division by a pivot and a data-dependent row
exchange, neither of which belongs in a Q16.16 datapath.
Simulink bridge
None (Support::None). invfreqz and
invfreqs are Signal Processing Toolbox functions and that
toolbox ships no Simulink library, so there is no path a diagram could name. The
bridge reports this block rather than dropping it silently, and it has no
parity testbench; code export verification covers all ten languages.
Notes
- Verified against R2026a: see the source banner for the responses and
the measured agreement with
invfreqzandinvfreqs. - ⚠ The one-shot equation-error fit is the whole block. MATLAB's two functions also offer an iterative Gauss-Newton refinement of the output error, reached by passing a maximum iteration count, and that is not offered here: each step re-stabilizes the denominator through its roots, and a root finder is not a bounded program the ten export targets can share. The two answers are different fits, not one approximating the other – on the responses this block was measured over, the refinement moves the coefficients by up to 0.059 (numerator) and 0.204 (denominator) in the discrete domain, and 0.77 and 1.04 in the continuous one.
- Real coefficients only, which is MATLAB's default. The
'complex'option of those functions – which keeps the complex normal equations instead of their real part, and admits frequencies outside [0, π] – is not offered. - The least-squares step is the normal equations solved by Gaussian elimination with partial pivoting. MATLAB forms the same normal equations and factors them instead; the two differ by rounding, which grows with how ill-conditioned the grid and the response are.
- A grid that does not determine the filter – too few distinct frequencies, a response of all zeros, a repeated point – makes the elimination divide by zero, and both outputs are NaN for that sample. MATLAB's backslash answers Inf or NaN there too, with a warning.
- Algebraic: every output depends on this sample's response alone. It is a fit, not a filter, so it carries no state space.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Signal_Modeling/Frequency_Domain_Filter_Identification |
| family | Control_Systems/Signal_Modeling |
| solver environment class | ICoreBlock_0_Control_Systems_1_Signal_Modeling_2_Frequency_Domain_Filter_Identification |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Signal_Modeling/Frequency_Domain_Filter_Identification/ICoreBlock_0_Control_Systems_1_Signal_Modeling_2_Frequency_Domain_Filter_Identification.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Signal_Modeling/Frequency_Domain_Filter_Identification/ICoreBlock_0_Control_Systems_1_Signal_Modeling_2_Frequency_Domain_Filter_Identification.h |
| default size on canvas | 170 × 90 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 | b |
| 4 | out | ICoreDouble | a |
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 |
|---|---|---|
Domain | Discrete%~%Continuous~~Discrete | — |
Numerator Order | 2 | — |
Denominator Order | 2 | — |
Frequencies | [0.2 0.6 1 1.4 1.8 2.2 2.6 3] | — |
Weights | 1 | — |
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): invfreqz and invfreqs are Signal Processing Toolbox functions, not Simulink library blocks -- that toolbox ships no Simulink library at all -- so there is no path a diagram could name; the block is reported rather than dropped when a model crosses
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).
Frequency-Domain Filter Identification -- MATLAB's invfreqz and invfreqs on a fixed grid A filter b(z)/a(z) (or b(s)/a(s)) fitted to a measured frequency response, one fit per sample, from the response's real and imaginary parts on two input ports and a frequency grid that is configuration rather than signal. With sqrt(wt) weighting the rows, a(1) = 1 and the response h(i) at the grid point w(i), the equations solved are
discrete SUM(c=1..na) a(c+1) e^(-j c w) h - SUM(k=0..nb) b(k+1) e^(-j k w) = -h continuous SUM(c=1..na) a(c+1) (jw)^(na-c) h - SUM(k=0..nb) b(k+1) (jw)^(nb-k) = -(jw)^na h
in the least-squares sense over their real and imaginary halves -- which is exactly what invfreqz.m and invfreqs.m form, and what they return when called with no iteration count.
Measured against R2026a on a 16-point grid 0.11 .. 2.81 weighted 1.6 down to 0.7, over the 41 responses H0 + H1*u the verification rig itself builds -- the EXPORTED Python core, not just the live run: discrete b and a agree with invfreqz(h, w, 2, 3, wt) to 2.1e-13 and 8.0e-13 continuous b and a agree with invfreqs(h, w, 2, 3, wt) to 1.5e-12 and 2.2e-12 and a response taken FROM a filter of the configured orders is recovered as that filter: 9.7e-15 on b and 3.8e-14 on a (discrete), 7.2e-13 and 1.1e-12 (continuous).
What is not offered: the iterative Gauss-Newton refinement those two functions run when given a maximum iteration count. It re-stabilizes the denominator through its ROOTS at every step, and a root finder is not a bounded program ten languages can share. Over the same 41 responses the refinement moves the answer by up to 0.059 (numerator) and 0.204 (denominator) in the discrete domain, and 0.77 and 1.04 in the continuous one -- so the one-shot fit is a different answer from the refined one, not an approximation of it. The description says so where a user reads it.
Support::None: invfreqz and invfreqs are Signal Processing Toolbox functions and that toolbox ships no Simulink library.
Sample results#
No stimulus produced a sampled output in this rig — Invalid input size at Frequency-Domain Filter Identification block: ICore Blocks/Home/Frequency Domain Filter Identification. 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 875fdbf564cf31a145283edf6a75dc1d64d1090a · produced by docsSample --out <folder> --blocks Wigner_Ville_Distribution Cross_Wigner_Ville_Distribution Inverse_STFT Fourier_Synchrosqueezed_Transform Time_Frequency_Ridges Frequency_Domain_Filter_Identification Fill_Gaps EOM_6DOF_ECEF_Quaternion EOM_6DOF_Custom_Variable_Mass_ECEF_Quaternion EOM_6DOF_Simple_Variable_Mass_ECEF_Quaternion ECI_To_ECEF_Rotation_Matrix ECI_Position_To_LLA LLA_To_ECI_Position ECI_Position_To_AER --steps 60
Sample data: docs/generated/samples/Control_Systems__Signal_Modeling__Frequency_Domain_Filter_Identification.json