Occupied Bandwidth — Control Systems/Spectral Measurements
Control_Systems/Spectral_Measurements/Occupied_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.
Occupied Bandwidth
Control Systems / Spectral Measurements
The width of the band that holds a stated percentage of a spectrum's
power, placed so that equal power is left outside it on each side. With bin
k at fk = k·Δ carrying
Pk = Δ·pk, the block integrates the bins as
rectangles whose borders sit halfway between neighbouring bin frequencies, and
reports the two frequencies between which P % of the total lies.
This is the streaming counterpart of MATLAB's obw.
It is Median Frequency read twice – the same cumulative curve inverted at (100−P)÷200 and (100+P)÷200 of the total instead of at half – and both are found in one pass over the bins.
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 2 and 256. Bin k is the frequency k·Δ.
- bw – the occupied bandwidth, fhi − flo. Scalar.
- flo – the band's lower edge: the frequency below which (100−P)÷2 percent of the power lies. Scalar.
- fhi – the band's upper edge: the frequency below which (100+P)÷2 percent of the power lies. 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.
- Occupied Power (%) – P, how much of the total power the band must hold, strictly between 0 and 100. 99 is the default and the usual convention; a smaller value gives a narrower band, and the power left outside is split equally between the two sides whatever it is.
- Epsilon – ε, a degeneracy guard, not an accuracy knob. It is what makes a bin of exactly zero power behave as a step rather than a division of zero by zero, and the default of 1e−18 is far below any bin a real spectrum carries.
- 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, the border positions and the two power thresholds are structural and are inlined at export time rather than exposed as tunable parameters – changing any of them changes how many terms the core contains. Re-export after changing them.
The three HDL targets are simulation-only: the block divides
twice per bin, and a Q16.16 divider is not part of this tree's fixed-point base.
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 obw is one of its
MATLAB functions. DSP System Toolbox was searched block by block and carries no
occupied-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, so there is no startup transient and no history to seed.
- Verified against MATLAB on three spectra and two percentages. On an 8-bin spectrum at Δ = 0.5 the 99 % band is [0.059062500000000004, 3.3987500000000015] and the 90 % band is [0.34617647058823531, 2.7220833333333334]; on an 11-bin spectrum at Δ = 0.35 the 99 % band is [0.010864638518223464, 3.4924414800753754] and the 85 % band is [0.16296957777335189, 3.3866222011306326]; and on [0 0 3 0 0 1 0 0], which has four empty bins, the 99 % band is [0.7533333333333333, 2.7400000000000002]. R2026a answers all of these, and this block reproduces them.
- The band is not centred on a peak. It is centred on power: equal amounts are left outside on each side, so on a lopsided spectrum the band sits well away from the tallest bin. A block that finds the band around a peak instead is Power Bandwidth, which is a different measurement.
- The whole spectrum, always. Like Median Frequency and unlike Band Power, no frequency band is offered: MATLAB's banded form interpolates the cumulative curve at the band edges as well, which is a further rule again, and it is not offered rather than approximated.
- An all-zero spectrum gives a zero-width band at the middle of the axis, where MATLAB answers NaN. A spectrum with no power has no occupied band; a finite answer is the better thing to hand ten generated cores.
- The input is expected nonnegative. A power spectral density is, and nothing here checks it – but a negative bin makes the cumulative curve non-monotonic, and the inverse of a non-monotonic curve is not well defined.
- 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/Occupied_Bandwidth |
| family | Control_Systems/Spectral_Measurements |
| solver environment class | ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Occupied_Bandwidth |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Spectral_Measurements/Occupied_Bandwidth/ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Occupied_Bandwidth.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Spectral_Measurements/Occupied_Bandwidth/ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Occupied_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 | — |
Occupied Power (%) | 99 | — |
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: obw() 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 occupied-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:
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).
Occupied Bandwidth -- the width of the band holding a stated percentage of a spectrum's power, with equal power left outside on each side. MEDIAN FREQUENCY'S MACHINERY, READ TWICE. Transcribed from R2026a's obw.m, which is the same cumulative-rectangle integration and the same halfway borders, evaluated at (100-P)/200 and (100+P)/200 of the total instead of at half of it. Both walks run in ONE pass over the bins, carrying two remainders, so the emitted core has one loop rather than two.
MEASURED AGAINST R2026a on three spectra and two percentages, and every number reproduced:
p = [0.12 0.85 2.40 1.10 0.35 0.60 0.18 0.07], Delta = 0.5 99 % -> bw 3.3396875000000015 flo 0.059062500000000004 fhi 3.3987500000000015 90 % -> bw 2.3759068627450981 flo 0.34617647058823531 fhi 2.7220833333333334 q = eleven bins at Delta = 0.35 99 % -> bw 3.481576841557152 flo 0.010864638518223464 fhi 3.4924414800753754 85 % -> bw 3.2236526233572809 flo 0.16296957777335189 fhi 3.3866222011306326 p2 = [0 0 3 0 0 1 0 0], Delta = 0.5, four EMPTY bins 99 % -> bw 1.9866666666666668 flo 0.7533333333333333 fhi 2.7400000000000002
⚠ THE CLAMP TRAP FROM Median_Frequency APPLIES HERE TWICE OVER, which is why it is restated rather than cross-referenced. The obvious branch-free clamp01(x) = (|x| - |x-1| + 1)/2 loses its entire "- 1" once |x| passes 2^53, so a ratio formed from a zero-power bin answers 1/2 instead of 0 or 1. This block clamps the NUMERATOR to the segment before dividing, for BOTH thresholds, so no large quotient is ever formed. The p2 row above is the case that catches it: with the wrong spelling neither edge lands where MATLAB puts it.
⚠ AND TWO VHDL SPELLING TRAPS, both recorded on Median_Frequency and both live here because this block emits a function too:
remis a VHDL RESERVED WORD (so the remainders areremLo/remHi), and a function's parameter list takes COMMAS, not semicolons.
Sample results#
No stimulus produced a sampled output in this rig — Invalid input size at: ICore Blocks/Home/Occupied Bandwidth. 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 0e7317dde · produced by docsSample --out <folder> --blocks Occupied_Bandwidth --steps 60
Sample data: docs/generated/samples/Control_Systems__Spectral_Measurements__Occupied_Bandwidth.json