RPM Frequency Map — Control Systems/Vibration
Control_Systems/Vibration/RPM_Frequency_Map · 2 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.
RPM-Frequency Map
Control Systems / Vibration
The frequency map versus speed of a machine that is running up or down
– MATLAB's rpmfreqmap, one column at a time. Every
hop samples the block takes the last L samples, windows them and
publishes the one-sided spectrum of that frame together with the shaft speed at
the frame's centre, so a recorder builds the whole map over the run:
columnj[m] = c(m)·|Σn w[n]·x[j·hop+n]·e−i2πmn/N|, m = 0 … N/2, N = max(256, L),
with the window normalized to sum 1 and c the one-sided fold: 1 at DC and at Nyquist, √2 between. The window length L is not a parameter – it follows from the resolution asked for.
Ports
- u – the sampled signal, scalar: one channel and its own window.
- rpm – the shaft speed in revolutions per minute at the same instant, scalar and positive. It is a signal, not a parameter, because it is what the map is drawn against.
- map – the current column, [N/2+1, 1], one-sided with DC first and Nyquist last; bin m is at m·fs/N hertz. It changes only on a frame sample and holds in between.
- rpm – scalar: the speed at that column's centre, the map's horizontal axis. Interpolated between the two samples either side of the centre when the window is odd.
- t – scalar: the time of that centre in seconds from the run's first sample, (j·hop + L/2)/fs.
Parameters
- Sample Rate (Hz) – fs, the rate the signal was sampled at: the resolution, the window length and the output bins are all in hertz through it. Positive; defaults to 1000.
- Resolution (Hz) – the frequency resolution asked for, which is
what sets the window length: L is the length whose equivalent noise
bandwidth gives that resolution, found by the same iteration
rpmfreqmapuses. A finer resolution means a longer window and a slower map. Defaults to 7.8125, which isrpmfreqmap's own default of fs/128 at the default rate, and gives L = 193 with a Hann window. A resolution too fine for a window of 1024 samples, or too coarse for one of 4, is clamped to that window rather than refused – asrpmfreqmapclamps it to the record. - Window – the shape the frame is multiplied by, normalized to
sum 1:
- Hann –
rpmfreqmap's default. - Hamming – a lower first sidelobe, a wider main lobe.
- Flat Top – amplitude-accurate on a line that falls between
bins, and the widest of the four;
rpmordermap's default. - Rectangular – no window at all.
rpmfreqmapalso takes Kaiser and Chebyshev windows, which need a second parameter each; those two are not offered here. - Hann –
- Overlap Percent – how much of one frame the next one repeats,
0 to 100. The overlap in samples is
min(ceil(percent/100·L), L−1) and the hop is L minus that,
exactly as in
rpmfreqmap. Defaults to 50. - Amplitude – what a bin carries:
- RMS – the root-mean-square amplitude of that line: the default.
- Peak – the peak amplitude, √2 times the RMS one (and at Nyquist, √2 times the unfolded magnitude).
- Power – the square of the RMS value.
- Scale – Linear, or dB: 20·log10 of an amplitude, 10·log10 of a power. A bin that is exactly zero is −∞ in dB, as it is in MATLAB.
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
Frames and timing
The first column is published when the window first fills – at sample
L−1, counting from 0 – and then every hop samples, so
the output at sample L−1+j·hop is column j of
rpmfreqmap over the same record. Between columns all three outputs
hold; before the first they are zero. The block is exactly
rpmfreqmap here: a column depends on L samples and on nothing
else, so nothing about it needs the rest of the record.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The window is computed once at export time and carried as numbers; the transform, the fold and the scaling run as loops in the core, so the emitted program does not grow with the number of bins. Every parameter is structural – re-export after changing one.
⚠ The three HDL targets run those loops in simulation-only real arithmetic, quantizing only at the port boundaries: a transform of a 1024-sample window and a square root do not belong in a Q16.16 datapath. They are not offered as synthesizable.
Simulink bridge
None (Support::None). rpmfreqmap 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)): two shift registers of L samples, a frame counter and the held column. - The speed axis is a signal, not a parameter. Feed rpm from a tachometer block (Tacho Pulse RPM) or from a recorded speed channel; the map is only as good as that speed.
- Not Spectrogram. That block publishes |X| at a transform length equal
to its window; this one follows
rpmfreqmap– the window length comes from a resolution, the transform is at least 256 long, the window sums to 1, and the speed and time of each column come out beside it. - No state space. A magnitude is not linear in the window, so model reduction correctly declines to merge it.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Vibration/RPM_Frequency_Map |
| family | Control_Systems/Vibration |
| solver environment class | ICoreBlock_0_Control_Systems_1_Vibration_2_RPM_Frequency_Map |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/RPM_Frequency_Map/ICoreBlock_0_Control_Systems_1_Vibration_2_RPM_Frequency_Map.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/RPM_Frequency_Map/ICoreBlock_0_Control_Systems_1_Vibration_2_RPM_Frequency_Map.h |
| default size on canvas | 150 × 90 px |
| ports at insert | 2 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 | u |
| 2 | in | ICoreDouble | rpm |
| 3 | out | ICoreDouble | map |
| 4 | out | ICoreDouble | rpm |
| 5 | out | ICoreDouble | t |
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 |
|---|---|---|
Sample Rate (Hz) | 1000 | — |
Resolution (Hz) | 7.8125 | — |
Window | RM::WIN_HANN%~%RM::WIN_HAMMING%~%RM::WIN_FLATTOP%~%RM::WI… | — |
Overlap Percent | 50 | — |
Amplitude | RM::AMP_RMS%~%RM::AMP_PEAK%~%RM::AMP_POWER~~RM::AMP_RMS | — |
Scale | RM::SCALE_LINEAR%~%RM::SCALE_DB~~RM::SCALE_LINEAR | — |
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): rpmfreqmap 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).
RPM-Frequency Map -- MATLAB's rpmfreqmap, one column at a time A machine that runs up or down in speed writes its story in a map whose rows are FREQUENCY and whose columns are the speed the shaft had while that column was measured. This block publishes that map one column at a time:
L <- the window length the RESOLUTION asks for (rpmmap.m's ENBW iteration) hop <- L - min(ceil(OverlapPercent/100*L), L-1) nfft <- max(256, L) column j <- |DFT(w .* x[j*hop .. j*hop+L-1], nfft)| over bins 0 .. nfft/2, with the window normalized to sum 1, every bin but DC and Nyquist multiplied by sqrt(2), and the Amplitude and Scale rules on top of that rpm j <- the speed at the column's CENTRE, sample j*hop + L/2 t j <- (j*hop + L/2) / fs
⚠ THE FRAMING AND THE SCALING WERE READ OUT OF R2026a's rpmmap.m AND THEN MEASURED, not guessed. On a 600-sample record at fs = 1000 with the speed ramping 1500 -> 3000 rpm, the default configuration (resolution fs/128, Hann, 50 % overlap, rms, linear) gives a 129 x 5 map; the five columns of this block's framing -- a frame at sample L-1 and every hop after -- reproduce rpmfreqmap's five columns at max |error| = 0 (bit for bit, the same FFT), and the interpolated speeds at the column centres reproduce its rpm axis at max |error| = 0. What that pinned, each one a silent wrong answer if guessed:
- THE WINDOW LENGTH IS NOT ceil(ENBW*fs/resolution) BUT A FIXED POINT OF IT. The ENBW
depends on the length being chosen, so rpmmap iterates and, when the iteration cycles, takes the length whose resolution is closest. At fs = 1000 and resolution fs/128 with a Hann window that is 193 samples, not 192.
- nfft IS max(256, L), so the map has at least 129 rows however short the window is, and
the frequency axis is k*fs/nfft rather than k*fs/L.
- THE WINDOW IS NORMALIZED TO SUM 1, which is what makes a bin read as an amplitude.
- 'peak' MULTIPLIES BY sqrt(2) TWICE (once in the one-sided fold, once more) rather than
by 2 -- and the two are not the same double. The Nyquist bin gets the second factor and not the first.
Support::None: rpmfreqmap 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#
| t | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 [129x1] entry 0 | out ICoreDouble-Out-1 | out ICoreDouble-Out-2 |
|---|---|---|---|---|---|
| 0 | -2 | -2 | [0, 0, 0, 0]… | 0 | 0 |
| 0.4 | 0.5 | 0.5 | [0, 0, 0, 0]… | 0 | 0 |
| 0.8 | -2 | -2 | [0, 0, 0, 0]… | 0 | 0 |
| 1.2 | 0.5 | 0.5 | [0, 0, 0, 0]… | 0 | 0 |
| 1.6 | -2 | -2 | [0, 0, 0, 0]… | 0 | 0 |
| 2 | 0.5 | 0.5 | [0, 0, 0, 0]… | 0 | 0 |
| 2.4 | -2 | -2 | [0, 0, 0, 0]… | 0 | 0 |
| 2.8 | 0.5 | 0.5 | [0, 0, 0, 0]… | 0 | 0 |
| 3.2 | -2 | -2 | [0, 0, 0, 0]… | 0 | 0 |
| 3.6 | 0.5 | 0.5 | [0, 0, 0, 0]… | 0 | 0 |
| 4 | -2 | -2 | [0, 0, 0, 0]… | 0 | 0 |
| 4.4 | 0.5 | 0.5 | [0, 0, 0, 0]… | 0 | 0 |
| 4.8 | -2 | -2 | [0, 0, 0, 0]… | 0 | 0 |
| 5.2 | 0.5 | 0.5 | [0, 0, 0, 0]… | 0 | 0 |
Every 4th of 60 samples, from the table stimulus.
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 |
ramp | Ramp: slope 1 from t = 0 | 0 … 0 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | 0 … 0 |
step | Step: 0 -> 1 at t = 1 s | 0 … 0 |
Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample
Category static · sample time 0.1 · 60 steps · commit 6b0471a23bd6163d7d7f33f764df08a614c2b6b8 · produced by docsSample --out <folder> --blocks Scalar_Root_Find Scalar_Bounded_Minimization Order_Waveform Order_Track RPM_Frequency_Map RPM_Order_Map --steps 60 · data docs/generated/samples/Control_Systems__Vibration__RPM_Frequency_Map.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).