Median Frequency — Control Systems/Spectral Measurements
Control_Systems/Spectral_Measurements/Median_Frequency · 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.
Median Frequency
Control Systems / Spectral Measurements
The half-power frequency of a power spectral density – the point
that divides its integrated power in two, so that as much power lies below it as
above. 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 frequency at which the running total reaches
half the whole. This is the streaming counterpart of MATLAB's
medfreq.
It is not a weighted average: it inverts the cumulative power curve rather than summing against it, which is why a spectrum can have a mean frequency and a median frequency far apart.
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·Δ.
- f – the half-power frequency, in the same units as Bin Spacing. Always scalar, whatever the input's length. It lies between 0 and (N−1)·Δ and is generally not on a bin centre.
Parameters
- Bin Spacing – Δ, the frequency step between neighbouring bins, a positive number. Left at 1 the answer comes out in bins; set it to fs÷(2·(N−1)) for a one-sided spectrum reaching the Nyquist frequency and the answer comes out in Hz.
- 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. Raising it biases the answer; there is no reason to.
- 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 border positions 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, which no runtime parameter can do. Re-export after changing them.
The three HDL targets are simulation-only: the block divides
once per bin, and a Q16.16 divider is not part of this tree's fixed-point base.
They compute in real arithmetic – VHDL through an emitted
function, Verilog and SystemVerilog through declared real scratch – 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 medfreq is one of its
MATLAB functions. DSP System Toolbox was searched block by block and carries no
half-power 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 different kinds of spectrum. An 8-bin spectrum [0.12 0.85 2.40 1.10 0.35 0.60 0.18 0.07] at Δ = 0.5 gives 1.1385416666666666; an 11-bin spectrum [2.5105 2.9801 3.191 3.7727 2.9726 3.6932 0.1646 1.8892 3.7763 2.6134 3.6086] at Δ = 0.35 gives 1.5900871872630777; and [0 0 3 0 0 1 0 0] at Δ = 0.5, which has four empty bins, gives 1.0833333333333333. R2026a answers all three, and this block reproduces them.
- The whole spectrum, always. Unlike Band Power and Mean Frequency this block offers no frequency band: MATLAB's banded form interpolates the cumulative curve at both band edges as well as at the half-power level, which is a third rule again, and it is not offered rather than approximated.
- An all-zero spectrum answers the middle of the axis, (N−1)·Δ÷2, where MATLAB answers NaN. A spectrum with no power has no half-power point; a finite number is the better thing to hand ten generated cores, and it is the value the ramp form gives with no special case anywhere.
- The input is expected nonnegative. A power spectral density is, and nothing here checks it – but note that a negative bin makes the cumulative curve non-monotonic, and the inverse of a non-monotonic curve is not well defined. Feed this block something other than a density and the answer means nothing in particular.
- No state space. The block is not linear in its input at all – it inverts a curve built from it – so it carries none and model reduction correctly declines to merge it.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Spectral_Measurements/Median_Frequency |
| family | Control_Systems/Spectral_Measurements |
| solver environment class | ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Median_Frequency |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Spectral_Measurements/Median_Frequency/ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Median_Frequency.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Spectral_Measurements/Median_Frequency/ICoreBlock_0_Control_Systems_1_Spectral_Measurements_2_Median_Frequency.h |
| default size on canvas | 140 × 72 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 | p |
| 2 | out | ICoreDouble | f |
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 | — |
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: medfreq() 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 half-power 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).
Median Frequency -- the half-power point of a power spectral density. THE INVERSE OF A CUMULATIVE CURVE, WRITTEN AS A SUM OF RAMPS. Transcribed from R2026a's medfreq.m: integrate the bins as rectangles, place the borders halfway between neighbouring bin frequencies, and read the frequency at which the running total reaches half the whole. Written as a search that is what it sounds like; written as a sum of clamped ramps it is the same number with a fixed number of operations, no data-dependent index and no branch, which is what lets all ten targets emit one formulation instead of two.
MEASURED AGAINST R2026a rather than asserted, on three spectra chosen to be different KINDS of input rather than three of a kind:
p = [0.12 0.85 2.40 1.10 0.35 0.60 0.18 0.07] at Delta = 0.5 -> 1.1385416666666666 q = eleven bins at Delta = 0.35 -> 1.5900871872630777 p2 = [0 0 3 0 0 1 0 0] at Delta = 0.5, four EMPTY bins -> 1.0833333333333333
This block reproduces all three; the exact values of q are in the block's own description.
⚠ THE THIRD ONE IS THE INTERESTING ONE, AND IT BROKE THE FIRST IMPLEMENTATION. A zero-power bin makes a segment of zero WIDTH, and the natural branch-free clamp -- clamp01(x) spelled (|x| - |x-1| + 1)/2 -- is catastrophically wrong there. The ratio it is handed is enormous, and once |x| passes 2^53 the subtraction |x| - |x-1| rounds to ZERO instead of one, so the clamp answers 1/2 rather than 1. Measured: that spelling answered 1.5833 on p2, where MATLAB answers 1.0833 -- a whole bin and a half out, on a spectrum that is not exotic. Every other test passed.
What this block emits instead never forms a large ratio at all: the NUMERATOR is clamped to the segment before the division, so the quotient is in [0,1] by construction, and a separate step term -- built from the same sign() spelling Zero Crossing Rate uses -- carries the zero-width case. Both are exact in IEEE arithmetic.
📌 A branch-free spelling is not automatically a portable one. The arithmetic has to stay in range as well as stay branch-free.
Sample results#
No stimulus produced a sampled output in this rig — Invalid input size at: ICore Blocks/Home/Median Frequency. 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 26971862e · produced by docsSample --out <folder> --blocks Median_Frequency --steps 60
Sample data: docs/generated/samples/Control_Systems__Spectral_Measurements__Median_Frequency.json