Order Waveform — Control Systems/Vibration
Control_Systems/Vibration/Order_Waveform · 2 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.
Order Waveform
Control Systems / Vibration
The time-domain waveform of one or more shaft orders – MATLAB's
orderwaveform, the Vold-Kalman tracking filter – over a
running window of the last W samples. An order is the component of a vibration
whose frequency follows the shaft: order k rides at
k·rpm÷60 hertz, so it sweeps as the machine runs up and a
fixed filter cannot follow it. The filter finds, over the window, the complex
envelope X that makes 2·Re(X·ejθ) closest to
the signal while keeping X smooth – a least-squares problem in which the
phase θ[n] = (2πk÷60fs)·Σrpm comes from the
speed port and the smoothness weight comes from the bandwidth. The waveform is
published for the window's centre sample.
Ports
- x – the sampled vibration signal, scalar: one channel and its own window.
- rpm – the shaft speed at the same instant, in revolutions per minute, scalar. Any speed profile is allowed; it is the sample-by-sample phase that the filter tracks.
- xr – the extracted waveforms, [K, 1] with one row per entry of Order List, in the signal's own units. Row k is the value at the sample (W−1)÷2 ticks ago, not at this tick.
Parameters
- Order List – the orders to extract, a vector of up to eight positive numbers; they need not be whole (a 0.5 order is a half-shaft rattle). Defaults to [1 2]. K is its length and sets the output height.
- Bandwidth (Hz) – how fast the extracted envelope is allowed to change, read as the filter's smoothness weight exactly as MATLAB reads it: r = (1.58·fs÷2πbw)2 at filter order 1 and (1.7·fs÷2πbw)3 at order 2. A narrow bandwidth rejects neighbouring orders and follows a run-up slowly; a wide one does the opposite. Positive; defaults to 40. See the Notes for how it must be sized against the window.
- Window Length – W, the record the filter solves over: an ODD whole number from 17 to 513, odd so that the window has a centre. Defaults to 129. Every generated core carries W-sized arrays and W sine-cosine pairs per order per sample, which is what the upper limit is about.
- Sample Rate (Hz) – fs, the rate the two signals were sampled at: the bandwidth and the order frequencies are in hertz through it. Positive; defaults to 1000.
- Filter Order – the structural equation, 1 or 2: 1 keeps the envelope's second difference small, 2 its third difference. Order 2 follows a curved amplitude more closely at the price of a worse-conditioned solve. Defaults to 1, which is MATLAB's.
- 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 least-squares matrix does not contain the data, so it is solved ONCE at export time and the exported core carries its W solution weights as numbers; each sample is then the window's cumulative speed, one sine-cosine pair per sample per order, and one accumulation. All five settings are structural, so re-export after changing any of them.
⚠ The three HDL targets run those loops in simulation-only real arithmetic, quantizing only at the port boundaries: a Q16.16 datapath carries neither the sine and cosine of an accumulated phase nor a window of a thousand samples. They are not offered as synthesizable.
Simulink bridge
None (Support::None). orderwaveform 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 W samples, zero at the start of the run, so the first W−1 samples of a run are a window that is still filling. - ⚠ The output lags by (W−1)÷2 samples – 64 at the default window. The least-squares envelope is unconstrained at the edge of its own record, so the window's newest sample is not a usable estimate: measured against the whole-record answer on a 600 to 1800 rpm sweep, the window's centre converges to it (9.0e−4 at (W−1)·bw÷fs = 5.1, 7.0e−7 at 10.2) while its newest sample stays at 1.0 to 2.4 on a true amplitude of 1.4 whatever the window length. The block reads the centre for that reason and states the latency rather than hiding it.
- ⚠ Size the window against the bandwidth. The agreement above is governed by (W−1)·bw÷fs: about 1e−3 at 5, 1e−6 at 10, 1e−12 at 20 – so a narrow bandwidth needs a long window, and W < 5·fs÷bw is a window that cannot hold the filter's own response. The defaults sit at 5.1.
- ⚠ This is the whole-record filter applied to a window, and MATLAB's own
answer carries a solver tolerance.
orderwaveformsolves by preconditioned conjugate gradients at a relative residual of 1e−3; this block solves exactly, by a banded Cholesky. On identical windows the two differ by 1.2e−6 to 1.5e−4 for that reason alone. - Orders are extracted independently of one another, which is
orderwaveform's default (Decouplefalse). The coupled solve, which separates orders that cross, needs a K·W system whose matrix DOES contain the data, so it is not offered here. - An order whose frequency passes fs÷2 aliases, exactly as it would in MATLAB, which refuses such an order list outright; the speed arrives on a port here, so it cannot be checked at configuration time.
- Not Order Track: that block publishes the same filter's AMPLITUDE against time, in rms, peak or power units.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Vibration/Order_Waveform |
| family | Control_Systems/Vibration |
| solver environment class | ICoreBlock_0_Control_Systems_1_Vibration_2_Order_Waveform |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/Order_Waveform/ICoreBlock_0_Control_Systems_1_Vibration_2_Order_Waveform.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/Order_Waveform/ICoreBlock_0_Control_Systems_1_Vibration_2_Order_Waveform.h |
| default size on canvas | 140 × 76 px |
| ports at insert | 2 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 | x |
| 2 | in | ICoreDouble | rpm |
| 3 | out | ICoreDouble | xr |
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 |
|---|---|---|
Order List | [1 2] | not crossed |
Bandwidth (Hz) | 40 | not crossed |
Window Length | 129 | not crossed |
Sample Rate (Hz) | 1000 | not crossed |
Filter Order | 1 | not crossed |
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 |
| deliberately not crossed | Order List, Bandwidth (Hz), Window Length, Sample Rate (Hz), Filter Order |
Caveat (shown to the user): orderwaveform is a Signal Processing Toolbox function and that toolbox ships no Simulink library, so there is no path a diagram could name; the Vold-Kalman order filter has no counterpart in any shipped block library
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).
Order Waveform -- MATLAB's orderwaveform (the Vold-Kalman filter) over a running window Over the last W samples of the signal x and of the shaft speed rpm, both oldest first, with the phase the speed implies,
theta[n] = (2 pi order / (60 fs)) * SUM(k <= n) rpm[k]
the Vold-Kalman filter finds the complex envelope X that minimises
|| x - Re(2 X e^{j theta}) ||^2 subject to a smooth envelope, r^2 || S X ||^2 small,
where S is the second difference [1 -2 1] at filter order 1 and the third difference [1 -3 3 -1] at filter order 2, and r is the bandwidth read as a weight, (1.58 fs / (2 pi bw))^2 or (1.7 fs / (2 pi bw))^3. The published waveform is the real part 2 Re(X e^{j theta}) at the WINDOW'S CENTRE.
⚠ THE MATRIX IS A CONSTANT OF THE CONFIGURATION, so the solve is done ONCE. The normal equations are (r^2 S'S + I) X = conj(e^{j theta}) . x, and with the orders uncoupled -- orderwaveform's default, 'Decouple' false -- the matrix does not contain the data at all. The block therefore solves A g = e_centre once, by a banded Cholesky, and every tick is SUM g[n] x[n] cos(theta[centre] - theta[n]) and its sine twin: W multiplies and W sine-cosine pairs per order, no linear algebra in any generated core.
⚠ EVERY NUMBER BELOW WAS MEASURED AGAINST R2026a, not read off a page:
- THE WINDOWED SOLVE IS MATLAB'S SOLVE, on the same window: against
signal.internal.vk (the function orderwaveform itself calls) driven to a tight tolerance over W = 33 and 129, bandwidths 20/40/80 Hz and both filter orders, the largest disagreement is 3.2e-14 at filter order 1 and 2.1e-8 at filter order 2, where the matrix is conditioned by r^2 with r cubed.
- ⚠ MATLAB'S OWN DEFAULT IS FURTHER FROM EXACT THAN THIS BLOCK IS. orderwaveform solves
by preconditioned conjugate gradients at a relative-residual tolerance of 1e-3, and on the same windows its answer sits 1.2e-6 to 1.5e-4 from the exact least-squares solution. A user comparing the two should expect to meet that difference, and it is MATLAB's solver setting rather than a disagreement about the filter.
- THE CENTRE IS THE ONLY HONEST READ POINT. Against the whole-record solve on a 4 s
sweep from 600 to 1800 rpm, the window's centre converges as the window grows -- 9.0e-4 at (W-1)*bw/fs = 5.1, 7.0e-7 at 10.2, 7.0e-13 at 20.5 (order 1) -- while the window's NEWEST sample never does: it sits at 1.0 to 2.4 against a true amplitude of 1.4, at every window length tried, because the least-squares envelope is unconstrained at the record's own edge. That is what buys the (W-1)/2 samples of latency.
Sample results#
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.004844 … 0.04861 |
ramp | Ramp: slope 1 from t = 0 | -0.09239 … 0.07829 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -0.03578 … 0.3583 |
table | Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample | -0.1267 … 0.005774 |
Plotted: step — Step: 0 -> 1 at t = 1 s
Category dynamic · 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__Order_Waveform.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).