Power Bandwidth — Control Systems/Spectral Measurements
Control_Systems/Spectral_Measurements/Power_Bandwidth · 1 input / 3 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.
Power Bandwidth
Control Systems / Spectral Measurements
The width of the band around a spectrum's peak within which the power
spectral density stays above a stated roll-off below that peak. The
reference level is max(p)·10−R/10; the block finds where
the density last falls to it on the peak's left and first falls to it on its
right, and reports those two frequencies and their difference. This is the
streaming counterpart of MATLAB's powerbw.
It is not Occupied Bandwidth. That block is centred on power – equal power left outside on each side – and never looks for the peak. This one is centred on the peak and never counts the power inside. On a lopsided spectrum they land in different places.
Ports
- p – the power spectral density, one nonnegative value per bin. A vector, either an [N,1] column or a [1,N] row, with N between 3 and 256. Bin k is the frequency k·Δ. Three is the smallest N that can hold a peak with a bin on each side of it.
- bw – the power bandwidth, fhi − flo. Scalar.
- flo – the lower edge, interpolated in dB between the two bins that straddle the reference level on the peak's left. Scalar.
- fhi – the upper edge, the same on the peak's right. Scalar.
Parameters
- Bin Spacing – Δ, the frequency step between neighbouring bins, a positive number. Left at 1 the answers come out in bins; set it to fs÷(2·(N−1)) for a one-sided spectrum reaching the Nyquist frequency and they come out in Hz.
- Reference Rolloff (dB) – R, how far below the peak the band's edges sit, a positive number of decibels. The default is 3.0102999566398120, which is 10·log102 exactly and is therefore the true half-power point – typing a round 3 instead gives 10−0.3 = 0.50119 of the peak rather than one half.
- Epsilon – ε, a degeneracy guard, not an accuracy knob, added to the interpolation's denominator so that two neighbouring bins of identical level cannot divide by zero. The default of 1e−18 is far below any dB difference a real spectrum produces.
- 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 bin count, the bin spacing and the reference factor are structural and are inlined at export time rather than exposed as tunable parameters. Re-export after changing them.
The three HDL targets are simulation-only: the block takes a
base-10 logarithm per bin and divides, neither of which belongs in a Q16.16
datapath. They compute in real arithmetic and quantize only at the
port boundary, so they simulate correctly and are not offered as
synthesizable.
Simulink bridge
No equivalent (Support::None). Signal Processing Toolbox
ships no Simulink library at all, and powerbw is one of its
MATLAB functions. DSP System Toolbox was searched block by block and carries no
power-bandwidth measurement. 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
- Stateless. The answer depends on this sample's spectrum and nothing else.
- The DC bin is doubled before the peak is looked for, whenever the axis starts at 0. This is MATLAB's rule for this measurement and not for Occupied Bandwidth, which does no such thing – on a spectrum whose largest bin is DC it alone decides where the peak is.
- The interpolation is in dB, not in power. Each edge is found by interpolating log10 of the density between the two bins that straddle the reference, with a floor of realmin underneath the logarithm. That floor is what keeps a spectrum with empty bins finite instead of NaN, and its exact value shows through in the answer.
- Both edges fall back to the axis end. If the density never drops to the reference on one side – a peak sitting against the end of the axis – that edge is the axis end itself rather than an extrapolation.
- Verified against MATLAB on four spectra. An 8-bin spectrum at Δ = 0.5 gives a half-power band of [0.66611010771335399, 1.4442348121752797] and a 6 dB band of [0.36416096465070874, 1.7625864696974454]; an 11-bin spectrum at Δ = 0.35 whose first bin already exceeds the reference gives [0, 1.7934315938109551], the fallback; [0 0 3 0 0 1 0 0] at Δ = 0.5, with four empty bins, gives [0.99951152076445282, 1.0004884792355471]; and [0.4 1.2 3.0 1.1 0.35 0.9 0.2 0.05] at Δ = 0.25 gives [0.31088230065849248, 0.67271646656482109]. R2026a answers all of these, and this block reproduces them.
- A flat or monotonic spectrum has no interior peak, and the block does not pretend otherwise: the maximum is then an end bin, one side never crosses the reference, and that edge is reported as the axis end. The answer is a half-band, which is what the measurement means there.
- No state space. The block is not linear in its input at all, so it carries none and model reduction correctly declines to merge it.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Spectral_Measurements/Power_Bandwidth |
| family | Control_Systems/Spectral_Measurements |
| solver environment class | ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Power_Bandwidth |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Spectral_Measurements/Power_Bandwidth/ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Power_Bandwidth.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Spectral_Measurements/Power_Bandwidth/ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Power_Bandwidth.h |
| default size on canvas | 148 × 88 px |
| ports at insert | 1 in, 3 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 | p |
| 2 | out | ICoreDouble | bw |
| 3 | out | ICoreDouble | flo |
| 4 | out | ICoreDouble | fhi |
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 |
|---|---|---|
Bin Spacing | 1 | — |
Reference Rolloff (dB) | pbFmt(HALF_POWER_DB) | — |
Epsilon | 1e-18 | — |
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: powerbw() is a MATLAB function and Signal Processing Toolbox ships no Simulink library at all. DSP System Toolbox was searched block by block and carries no power-bandwidth measurement. Reported rather than dropped, and it carries no parity testbench
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:
B0no sample under docs/generated/samples/ — nothing to cross-check (P8.1)
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).
Power Bandwidth -- the width of the band around a spectrum's PEAK within which the density stays above a stated roll-off below that peak. ⚠ IT IS NOT Occupied_Bandwidth. That block is centred on POWER -- equal power outside on each side -- and never looks for the peak; this one is centred on the PEAK and never counts the power inside. On a lopsided spectrum the two land in different places, and neither is a tuning of the other.
Transcribed from R2026a's computePowerBW.m (the no-range branch) and measured against a run of it on four spectra, every number reproduced:
p = [0.12 0.85 2.40 1.10 0.35 0.60 0.18 0.07], Delta = 0.5 half-power -> bw 0.77812470446192572 flo 0.66611010771335399 fhi 1.4442348121752797 6 dB -> bw 1.3984255050467367 flo 0.36416096465070874 fhi 1.7625864696974454 q = eleven bins at Delta = 0.35 half-power -> bw 1.7934315938109551 flo 0 fhi 1.7934315938109551 9 dB -> bw 1.9486082717581821 flo 0 fhi 1.9486082717581821 p2 = [0 0 3 0 0 1 0 0], Delta = 0.5, four EMPTY bins half-power -> bw 0.00097695847109424694 flo 0.99951152076445282 fhi 1.0004884792355471 r = [0.4 1.2 3.0 1.1 0.35 0.9 0.2 0.05], Delta = 0.25 half-power -> bw 0.36183416590632861 flo 0.31088230065849248 fhi 0.67271646656482109
Three of those rows are there because each pins a rule that is easy to get wrong:
⚠ THE DC BIN IS DOUBLED before the peak is looked for, whenever the axis starts at 0. obw.m does NOT do this and powerbw.m does; on a spectrum whose largest bin is DC that alone decides where the peak is.
⚠ THE INTERPOLATION IS LOGARITHMIC -- straight in dB, not in power -- on log10 of the density with a floor of REALMIN. The floor is what makes the empty-bin row above finite instead of a NaN, and its exact value is load bearing: log10(realmin) is about -307.65, and the answer 0.99951... is that number showing through. A floor of 1e-300 answers something else.
⚠ BOTH EDGES HAVE A FALLBACK, AND IT REALLY FIRES. If the density never drops to the reference on one side -- a peak sitting against the end of the axis -- that edge is the axis end itself. The q rows above are that case: their first bin already exceeds the reference, so flo comes back as exactly 0 rather than as an extrapolation off the left of the axis.
The search is ONE unrolled walk with no data-dependent array index anywhere: a running maximum, then a second pass carrying two flags -- whether the peak has been passed, and
Sample results#
No sample run is committed for this block. Samples come from the headless harness (DOCS_PLAN.md P8.1) into docs/generated/samples/; until one exists this block's behaviour is witnessed by the parity and export-verification suites, not by a plot here.