Generated reference › Welch PSD — Control Systems/Spectral Measurements
kind: generated#block#control-systems-spectral-measurements

Welch PSD — Control Systems/Spectral Measurements

Control_Systems/Spectral_Measurements/Welch_PSD · 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.

Welch PSD

Control Systems / Spectral Measurements

The one-sided power spectral density of the signal, by Welch's method – the average of K windowed periodograms taken from overlapping segments of a running window:

P(f) = (1 ÷ (K·fs·Σw²)) · Σk |FFT(w·segmentk)|²

This is MATLAB's pwelch. Averaging is what separates it from a single periodogram: one periodogram's variance does not fall however long the record, and K of them averaged reduce it by K.

Ports

  • u – the sampled signal. Scalar: one channel and its own window.
  • p – the density, [L/2+1, 1], one-sided with DC first and Nyquist last. This is the vector every other block in this family takes on its own p port, so it wires straight into Band Power, Occupied Bandwidth, Spectral Entropy and the rest.

Parameters

  • Segment Length – L, the transform length. An even whole number from 4 to 32; even because a one-sided spectrum needs a Nyquist bin. Bounded above because the transform is unrolled at export and its cost grows as L².
  • Segments – K, how many are averaged, from 1 to 8. More segments cut the variance and lengthen the window the block looks back over. K = 1 is allowed and is an ordinary windowed periodogram – it is not refused here, unlike on Magnitude-Squared Coherence, because a single periodogram is still a meaningful density.
  • Segment Overlap – how far each segment is advanced:
    • None (0%) – segments do not share samples.
    • Half (50%) – MATLAB's default, and the one that recovers the most information from a given window.
    • Three quarters (75%) – more segments from the same samples, at the cost of correlating them.
    The window the block looks back over is L + (K−1)·hop samples.
  • Window – Hamming (MATLAB's default), Hann or Rectangular. These are the symmetric windows, which is what MATLAB's hamming() and hann() return.
  • Bin Spacing – Δf, the frequency step between output bins, in hertz. The sampling rate follows as fs = L·Δf and is what makes the answer a density rather than a power. It is the same parameter, under the same name, that Band Power and the rest of this family read.
  • 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.

No sine, no cosine and no division survives into a generated core. The window, the transform's basis and the whole 1÷(K·fs·Σw²) scale are fixed by the configuration, so they are computed once at export time and a core carries the resulting numbers: two dot products per bin per segment, a square each, and one scale. All five settings are structural – they decide how many multiplies and how many outputs the core contains – so re-export after changing any of them.

The three HDL targets are genuine synthesizable Q16.16. ⚠ What to watch there is range rather than precision: the block squares, so an input that reaches ±180 already reaches the format's ceiling before any scaling, and a small Bin Spacing divides the answer down into the quantum from the other end. Scale the signal into roughly ±1 first.

Simulink bridge

None (Support::None). pwelch 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 (setDiscreteOnlyBlock(true)): one shift register of L + (K−1)·hop samples.
  • ⚠ It is the producer this family was missing. Band Power, Occupied Bandwidth, Mean Frequency, Spectral Entropy, SNR, THD and the rest all take a spectrum on a p port and answer a measurement about it; this block makes that vector.
  • ⚠ It is not FFT Magnitude, and the difference is not cosmetic. FFT Magnitude emits a single periodogram: full length rather than one-sided, unwindowed, and with no scaling at all, so it is a spectrum but not a density – and its variance does not fall however long the record is. Averaging K windowed segments is exactly what fixes that, and it is the whole point of Welch's method.
  • ⚠ Three conventions are MATLAB's and each is a silent wrong answer if guessed. The divisor is K·fs·Σw² and not the segment length; the interior bins are doubled while DC and Nyquist are not; and the windows are the symmetric ones, so hamming(8) starts at 0.08. All three were measured against R2026a rather than remembered – see this block's source banner for the five-bin comparison, which agrees to 6.9e−18.
  • The window is zero-prefilled, so the first L + (K−1)·hop − 1 spectra of a run are a startup transient.
  • Scalar only. One channel and its own history; wire one block per channel.
  • No state space. |X|² is not linear in the window, so model reduction correctly declines to merge it.

Code facts#

FactValue
registered typeControl_Systems/Spectral_Measurements/Welch_PSD
familyControl_Systems/Spectral_Measurements
solver environment classICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Welch_PSD
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Spectral_Measurements/Welch_PSD/ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Welch_PSD.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Spectral_Measurements/Welch_PSD/ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Welch_PSD.h
default size on canvas140 × 76 px
ports at insert1 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoublep

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 variableDefaultSimulink parameter
Segment Length8—
Segments4—
Segment OverlapHalf (50%)%~%None (0%)%~%Three quarters (75%)~~Half (50%)—
WindowHamming%~%Hann%~%Rectangular~~Hamming—
Bin Spacing1—

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.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): pwelch 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).

Welch PSD -- the one-sided power spectral density of a stream, MATLAB's pwelch Pxx(f) = ( 1 / (K * fs * SUM w[n]^2) ) * SUM(k = 1..K) | FFT(w .* segment_k) |^2

with the interior bins doubled to fold the negative frequencies onto the positive ones. The window runs over the last L + (K-1)*hop samples, split into K overlapping segments.

MEASURED AGAINST R2026a AND THE FORMULATION IS WHAT WAS CHECKED, not just the answer. At L = 8, K = 4, 50 % overlap, a Hamming window and fs = 100, over the twenty-sample record

x = [1.2 0.7 2.5 0.3 0.12 0.8 1.1 -0.22 0.45 1.7 0.35 0.62 0.09 -1.4 0.66 0.2 -0.9 1.05 0.4 -0.55]

pwelch(x, hamming(8), 4, 8, 100) answers

[0.013796868366291216 0.0089225319785261516 0.015031589143953035 0.017827976986234836 0.0018547979135150844]

and the expression above, written out segment by segment, reproduces all five bins to 6.9e-18. That is what pins the three conventions a reader would otherwise guess: the divisor is K * fs * SUM(w^2) and NOT the window length; the interior bins are doubled and DC and Nyquist are not; and MATLAB's hamming(L) is the SYMMETRIC window, denominator L-1, so hamming(8) starts at 0.08 -- the same convention Magnitude_Squared_Coherence already uses.

ONE SOLVE, THEN ARITHMETIC. The window, the DFT basis and the whole scale are fixed by the configuration, so a generated core carries numbers: no sine, no cosine, no division. What does survive is a square per bin per segment, so the three HDL targets are genuine Q16.16 with a RANGE to watch rather than a precision problem.

Sample results#

Welch PSD — Step: 0 -> 1 at t = 1 sWelch PSD — Step: 0 -> 1 at t = 1 s00.51012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0 [5x1] entry 0

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0.01028
rampRamp: slope 1 from t = 00 … 15.85
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 0.312
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample2.869e-4 … 0.5499

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit b4ab100a9db12a091c23261d81c2f3298500a06a · produced by docsSample --out <folder> --blocks Welch_PSD --steps 60 · data docs/generated/samples/Control_Systems__Spectral_Measurements__Welch_PSD.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).