Wigner Ville Distribution — Control Systems/Time Frequency
Control_Systems/Time_Frequency/Wigner_Ville_Distribution · 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.
Wigner-Ville Distribution
Control Systems / Time Frequency
The Wigner-Ville distribution of a stream, one time column per sample –
MATLAB's wvd of the last N samples:
W[k] = |z(n)|² + 2·Σp=1..N/4 Re{z(n+p)·z*(n−p)·e−i2πkp/N}, k = 0 … N−1
where z is the analytic signal of the window and n = N/2: the
output is column N+1 of wvd of the window, the time at its
middle.
Ports
- u – the sampled signal. Scalar: one channel and its own window.
- W – the distribution at one instant, [N, 1]: row k is the frequency k·fs/(2N), DC first. It is real, and it can be negative – interference between components is what the distribution is known for.
Parameters
- Window Length – N, the samples the distribution is
formed over and the number of output rows: an even whole number from 4 to
16. Even because
wvdpads an odd record to the next even length; bounded above because every row is a quadratic form of the window, unrolled at export, and the cost grows as N³. - Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
Frames and timing
Once the window has filled – from sample N − 1, counting from
0 – the output at sample j is column N+1 of
wvd(x(j−N+1 : j)): the distribution at the window's middle, time
N/2, which is N/2 − 1 samples behind the newest input. It is
recomputed on every sample; before the window fills it is all zeros.
The middle, and not the newest sample, because a finite record's newest column has no lag support at all: it is |z|² in every row.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The analytic-signal transform, the lag products and the frequency transform are folded into one fixed quadratic form per row at export time, so a generated core is products and sums only – no sine, no division, no square root. The window length is structural: re-export after changing it.
The three HDL targets are genuine Q16.16 and synthesizable: each product of two window samples is formed once per tick and every row is a multiply-accumulate over those products. Scale the signal into roughly ±1 first: the output is a sum of products of samples.
Simulink bridge
None (Support::None). wvd is a Signal Processing
Toolbox function 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 therefore has no parity testbench; code export verification
still covers it across all ten languages.
Notes
- Stateful, and discrete by nature: a register of the last N samples and a fill count.
- Verified against R2026a: over every window of a 30-sample record at
N = 4, 8, 10 and 16 the output matches column N+1 of
wvdto 8.4×10−15 (the source banner carries the numbers). - Not reproduced:
wvd's'smoothedPseudo'windows, itsNumFrequencyPointsandMinThresholdoptions, and a complex input (a real input is always turned into its analytic signal, aswvddoes). The half-sample columnswvdalso returns are not published: one column per input sample is the integer-time one. - MATLAB's frequency labels differ from the rows:
wvd's second output names row k k·fs/(2(N−1)), while the transform puts it at k·fs/(2N) – a tone at fs/4 peaks exactly on row N/2. The matrix is MATLAB's either way. - For two signals use Cross Wigner-Ville Distribution, which is
xwvd– a different construction, not this one with a second input. - No state space. The distribution is quadratic in the input, so model reduction correctly declines to merge it.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Time_Frequency/Wigner_Ville_Distribution |
| family | Control_Systems/Time_Frequency |
| solver environment class | ICoreBlock_0_Control_Systems_1_Time_Frequency_2_Wigner_Ville_Distribution |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Time_Frequency/Wigner_Ville_Distribution/ICoreBlock_0_Control_Systems_1_Time_Frequency_2_Wigner_Ville_Distribution.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Time_Frequency/Wigner_Ville_Distribution/ICoreBlock_0_Control_Systems_1_Time_Frequency_2_Wigner_Ville_Distribution.h |
| default size on canvas | 150 × 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 | W |
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 |
|---|---|---|
Window Length | 8 | — |
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): wvd is a Signal Processing Toolbox function, not a Simulink library block -- 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 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).
Wigner-Ville Distribution -- MATLAB's wvd, one time column per sample W[k] = |z(n)|^2 + 2 * SUM(p = 1..floor(N/4)) Re{ z(n+p) conj(z(n-p)) e^(-j 2 pi k p / N) }
over the last N samples, with z the analytic signal of that window (zero-padded to 2N, as wvd forms it) and n = N/2: the output at sample k is column N+1 of wvd(x(k-N+1 : k)).
MEASURED AGAINST R2026a before a line was written, and the conventions are what was checked:
- wvd of an N-sample record is N-by-2N -- its time axis is a HALF-SAMPLE grid, 0, 0.5, ...,
N-0.5 -- and the even columns (integer times) are exactly the sum above, reproduced to 1.3e-14 on every even column at N = 4, 8, 10, 12 and 16. The lag sum stops at floor(N/4): that is wvdImpl's maxLag = floor(N/2) counted in half-samples.
- The NEWEST column of a record is degenerate: at time N-1 no lag has support, and the column
is |z(N-1)|^2 in every row (0.955372 flat on the 16-sample check). So a streaming block cannot publish it; it publishes time N/2, where the lag support is whole, N/2 - 1 samples behind the newest input.
- Row k is frequency k*fs/(2N): a tone at fs/4 peaks on row N/2 exactly. MATLAB's second
output labels the same rows linspace(0, fs/2, N), i.e. k*fs/(2(N-1)) -- a label, not the transform, and it names the fs/4 row 0.533*fs/2.
- For the record x = [1.2 0.7 2.5 0.3 0.12 0.8 1.1 -0.22], column 9 of wvd(x) is
[6.0508790114570 2.0514242084229 -8.0985740311937 -5.4494751059774 5.3812266007568 5.0451436285106 -3.1951630146658 -1.5087241646017]; the block's quadratic forms give it to 2.7e-15, and to 8.4e-15 over every window of a 30-sample record at N = 4, 8, 10, 16.
Support::None: wvd is a Signal Processing Toolbox FUNCTION and that toolbox ships no Simulink library, so there is no counterpart to bridge to or run a parity testbench against.
Sample results#
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 … 0.2351 |
ramp | Ramp: slope 1 from t = 0 | 0 … 151.6 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | 0 … 4.324 |
table | Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample | -3.325 … 6.316 |
Plotted: step — Step: 0 -> 1 at t = 1 s
Category dynamic · 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 · data docs/generated/samples/Control_Systems__Time_Frequency__Wigner_Ville_Distribution.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).