RPM Order Map — Control Systems/Vibration
Control_Systems/Vibration/RPM_Order_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-Order Map
Control Systems / Vibration
The order map versus speed of a machine that is running up or down
– MATLAB's rpmordermap, one column at a time. An order
is a multiple of the shaft rate, so a component locked to the shaft stays on one
row of this map while the machine changes speed, where on a frequency map it
would smear across many. The block resamples the signal into the angular
domain – a fixed number of samples per revolution, however fast the
shaft is turning – and publishes the spectrum of the last L angular
samples every hop of them:
Omax = fs·30/rpmmax, fsp = 8·Omax angular samples per revolution; the signal is upsampled 15 times, the speed integrated into a phase (φ = ∫rpm/60 dt) and the signal read off at every 1/fsp of a revolution; then
columnj[m] = c(m)·|Σn w[n]·xp[j·hop+n]·e−i2πmn/N|, N = max(256, L), rows m = 0 … the first order past Omax,
with the window normalized to sum 1 and c the one-sided fold: 1 at DC, √2 above it. Row m is order m·fsp/N.
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 what the angular resampling integrates, so this block uses it in the arithmetic rather than only as an axis.
- map – the current column, one entry per order row (the first order past Omax, so [N/8+2, 1] at the usual N). It changes only when a column completes and holds in between.
- rpm – scalar: the speed at that column's centre, interpolated at the centre's own phase; the map's horizontal axis.
- t – scalar: the time of that centre in seconds from the run's first sample. Unlike a frequency map's, it is not a fixed multiple of the hop: a column covers a fixed number of REVOLUTIONS, which take less time the faster the shaft turns.
Parameters
- Sample Rate (Hz) – fs, the rate the signal was sampled at. Positive; defaults to 1000.
- Maximum RPM – the largest speed the run will reach. It sets the
angular sampling rate fsp and the largest order the map carries,
Omax = fs·30/rpmmax; a signal component
above that order cannot be resolved at this sampling rate at all. Positive;
defaults to 3000. This is the one number
rpmordermaptakes from the record and this block takes from you – it uses max(rpm) over the whole signal, which no stream knows in advance, and the two agree exactly when this parameter equals that maximum. Set it too high and the map is finer and shorter than MATLAB's; set it below the speed actually reached and orders above Omax alias. - Resolution (Orders) – the order resolution asked for, which is
what sets the window length L in angular samples, through the same
iteration on the window's equivalent noise bandwidth that
rpmordermapuses. Defaults to 0.3125, which is that function's own default of fsp/256 at the default rate and maximum speed. A resolution too fine for a window of 1024 angular samples, or too coarse for one of 4, is clamped to that window rather than refused. The resolution and the overlap together must leave a hop of at least five angular samples, because one sample of the signal can advance the shaft by four; a configuration that does not is reported. - Window – the shape the frame is multiplied by, normalized to
sum 1:
- Flat Top –
rpmordermap's default: amplitude-accurate on an order that falls between rows, at the cost of the widest main lobe. - Hann –
rpmfreqmap's default, and the shortest window of the four at a given resolution. - Hamming – a lower first sidelobe, a wider main lobe.
- Rectangular – no window at all.
rpmordermapalso takes Kaiser and Chebyshev windows, which need a second parameter each; those two are not offered here. - Flat Top –
- Overlap Percent – how much of one frame the next one repeats, 0 to 100, in ANGULAR samples. The overlap is min(ceil(percent/100·L), L−1) and the hop is L minus that. Defaults to 50.
- Amplitude – what a row carries:
- RMS – the root-mean-square amplitude of that order: the default.
- Peak – the peak amplitude, √2 times the RMS one.
- Power – the square of the RMS value.
- Scale – Linear, or dB: 20·log10 of an amplitude, 10·log10 of a power.
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
Latency, and what a stream cannot reach
The angular resampling is zero-phase: MATLAB reads the middle of a
301-tap filter, which is 10 input samples after the sample it belongs to. This
block therefore starts publishing 10 samples late, and it never publishes
the columns rpmordermap would build out of a record's last 10
samples – the ones where it pads the filter with zeros and extrapolates
the speed past the end. Everything before them is the same map: measured against
R2026a on a 600-sample ramp from 1500 to 3000 rpm, 57 of 59 columns at max
|error| 8.3e-15, the speed axis to 4.6e-13 and the time axis to
1.1e-16. A column also arrives when the SHAFT has turned far enough
rather than on a fixed tick, so the gap between columns shortens as the machine
speeds up.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The window and the 301 resampling taps are computed once at export time and carried as numbers; the resampler, the phase integration, the angular interpolation, the transform and the scaling run as loops in the core. 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 phase integrated over a whole run, a division by an interpolation interval and a square root do not belong in a Q16.16 datapath. They are not offered as synthesizable.
Simulink bridge
None (Support::None). rpmordermap 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)): 21 samples of signal and speed history for the resampler, a phase accumulator, the angular ring of L samples, a short queue of column centres and the held column. - The speed must be positive and should be smooth. The phase is the integral of it, so a speed that goes to zero stalls the map and a negative one is meaningless. Feed it from a tachometer block (Tacho Pulse RPM) or a recorded speed channel.
- Not RPM-Frequency Map. That block's rows are hertz and a shaft-locked component walks across them as the machine runs up; here the rows are orders and it stays put, which is the whole reason to pay for the resampling.
- 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_Order_Map |
| family | Control_Systems/Vibration |
| solver environment class | ICoreBlock_0_Control_Systems_1_Vibration_2_RPM_Order_Map |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/RPM_Order_Map/ICoreBlock_0_Control_Systems_1_Vibration_2_RPM_Order_Map.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/RPM_Order_Map/ICoreBlock_0_Control_Systems_1_Vibration_2_RPM_Order_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 | — |
Maximum RPM | 3000 | — |
Resolution (Orders) | 0.3125 | — |
Window | RM::WIN_FLATTOP%~%RM::WIN_HANN%~%RM::WIN_HAMMING%~%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): rpmordermap 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-Order Map -- MATLAB's rpmordermap, one column at a time An order map is a frequency map drawn against the SHAFT rather than against the clock: the signal is put into the angular domain first, so a component locked to the shaft stays on one row while the machine runs up. rpmmap.m does it in four stages, and this block runs the same four on a stream:
fsp <- 4*(2*Omax), Omax = fs/(2*max(rpm)/60) angular samples per revolution xUp <- resample(x, 15, 1) MATLAB's 301-tap zero-phase read phase <- cumtrapz(rpmUp/(60*15*fs)) revolutions, integrated sample by sample xp <- xUp interpolated at every 1/fsp of a revolution column <- the windowed one-sided transform of the last L ANGULAR samples, every hop of them, truncated at Omax and scaled by Amplitude and Scale
⚠ TWO THINGS THE WHOLE RECORD GAVE rpmordermap AND A STREAM CANNOT, both measured:
- max(rpm) IS A PARAMETER HERE. It sets fsp and therefore the order axis and the window's
meaning, and no stream knows it in advance -- exactly the fork Time Synchronous Average met with its period. Maximum RPM carries it, and the block IS rpmordermap whenever that parameter equals the record's own maximum.
- THE RESAMPLING IS ZERO-PHASE, SO IT COSTS TEN SAMPLES OF LATENCY. resample(x, 15, 1)
reads the middle of a 301-tap filter, i.e. 150 upsampled samples = 10 input samples after the sample it belongs to. This block therefore begins publishing 10 samples late, and the columns rpmordermap builds out of a record's LAST 10 samples -- where it pads the filter with zeros and extrapolates the speed -- are the ones a stream never reaches.
⚠ MEASURED, AND THAT IS WHAT SAYS THE OTHER COLUMNS ARE rpmordermap's. On a 600-sample record at fs = 1000 with the speed ramping 1500 -> 3000 rpm, a prototype running exactly these loops reproduces rpmordermap's map at max |error| 8.3e-15 on values up to 1.5 (the coarse case: resolution 2 orders, Hann, peak -- 57 of its 59 columns, the missing two being the record's last), its rpm axis to 4.6e-13 on values near 2000 and its time axis to 1.1e-16. At the function's own defaults (resolution fsp/256, flattopwin, rms) both its columns come back at 1.4e-15 on values up to 0.71.
Support::None: rpmordermap 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 [122x1] 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_Order_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).