Generated reference › Empirical Mode Decomposition — Control Systems/Time Frequency
kind: generated#block#control-systems-time-frequency

Empirical Mode Decomposition — Control Systems/Time Frequency

Control_Systems/Time_Frequency/Empirical_Mode_Decomposition · 1 input / 5 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.

Empirical Mode Decomposition

Control Systems / Time Frequency

Sifts the last L samples into intrinsic mode functions and a residual – MATLAB's emd – and reports each mode's instantaneous frequency and energy, which is what hht builds its Hilbert spectrum from. A sift is: find the local maxima and minima, run an envelope through each set, subtract the mean of the two envelopes, and repeat until

‖rprev − r‖² ÷ ‖rprev‖² < Sift Relative Tolerance

or the sift count runs out. What is left is one mode; it is subtracted and the next is sifted out of the remainder. Unlike a filter bank, the modes come from the signal's own extrema, so a mode can change frequency along the frame.

Ports

  • u – the sampled signal, scalar: one channel and its own frame.
  • IMF – the modes, [L, Max Number of IMFs]: column j is mode j over the frame, oldest sample first. Columns past the number actually found are zero, because a wire has one size where MATLAB returns a narrower matrix.
  • r – the residual, [L, 1]: the frame minus every mode found.
  • n – [1, 1], how many modes were found (how many columns of IMF carry one).
  • insf – instantaneous frequency, [L, Max Number of IMFs], in hertz: fs/(2π) times the centred difference of each mode's unwrapped analytic phase. Zero in the columns with no mode.
  • inse – instantaneous energy, [L, Max Number of IMFs]: the squared magnitude of each mode's analytic signal. Zero in the columns with no mode.

Parameters

  • Frame Length – L, the samples one decomposition is made from: a whole number from 8 to 256. The three HDL targets hold one register per sample.
  • Hop Size – the samples from one decomposition to the next, 1 to L. Between them all five outputs hold.
  • Max Number of IMFs – MATLAB's MaxNumIMF, 1 to 16: the ceiling on the decomposition loop and the width of the three matrix outputs. The default 10 is MATLAB's.
  • Sift Max Iterations – MATLAB's SiftMaxIterations, 1 to 400: the ceiling on the sift loop. The default 100 is MATLAB's, and a frame of a few dozen samples typically stops after 1 to 5 sifts.
  • Sift Relative Tolerance – MATLAB's SiftRelativeTolerance, greater than 0 and less than 1; default 0.2. The first test is made against a tolerance of exactly 1, so every mode is sifted at least once.
  • Max Number of Extrema – MATLAB's MaxNumExtrema, 0 or more; default 1. The decomposition stops when the residue has fewer extrema than this.
  • Max Energy Ratio (dB) – MATLAB's MaxEnergyRatio; default 20. The decomposition stops when 10·log10(‖x‖÷‖residue‖) passes it, i.e. when the residue is that many decibels below the frame.
  • Interpolation – how the envelope runs through the extrema:
    • spline – the not-a-knot cubic, MATLAB's default and its interp1(..., 'spline').
    • pchip – MATLAB's shape-preserving cubic, which cannot overshoot between two extrema and usually yields one more mode.
    The two are different code, not a retuning, so each is exported and verified on its own.
  • Sample Rate (Hz) – fs, read only by insf. MATLAB's hht(imf) with no rate is this block at 2π, which puts the instantaneous frequency in radians per sample.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Frames and timing

The frame is the last L samples, oldest first. The first decomposition is made when the register fills – at sample L − 1, counting from 0 – and then every Hop Size samples; between them the outputs hold, and before the first one they are all zero. The decomposition is of the WHOLE frame, so the newest sample is at its edge, where the envelope is an extrapolation rather than a fit; that is the same edge effect MATLAB's emd has, and it is the reason a frame is decomposed rather than a single sample published.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The sift loop, the decomposition loop and the envelope solve are emitted as loops whose bounds are the parameters above, so the generated file does not grow with the frame length and the worst-case cost of a frame is fixed – a decomposition MATLAB describes as "until converged" becomes a bounded program.

The three HDL targets carry the whole decomposition in real arithmetic and are therefore simulation-only: a tridiagonal solve, a division by an envelope difference and an arctangent do not belong in a Q16.16 datapath. The frame itself is held in the port's fixed-point format, so quantization happens only at the port boundary.

⚠ Sifting makes DISCRETE decisions – which samples are extrema, when the tolerance is met, how many modes are found – so a frame in which one of those decisions is within a quantum of going the other way decomposes differently in fixed point than in double precision, and the modes then differ by much more than the quantum. Measured on this block's own reference over 40 runs of 30 frames each with the input quantized to Q16.16: about 2.5% of runs contain such a frame. That is arithmetic, not a code-generation defect, and a re-run is the first thing to try when one of the three HDL columns is red.

Simulink bridge

None (Support::None). emd and hht are functions of the Signal Processing and Wavelet toolboxes and neither ships a Simulink library block for them, 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. Sampling Time (s) → SampleTime, as on every block, has no counterpart here for the same reason.

Notes

  • Stateful, and discrete by nature: a register of L samples, a frame counter and the held decomposition.
  • Verified against R2026a: 44 frames over eight option sets, worst 1.6e-15 on the modes, 7.2e-16 on the residual, with the same mode count and sift count on every frame (the source banner carries what was checked).
  • It is a frame decomposition, not a filter. Each frame is decomposed on its own, so two overlapping frames may split a signal differently; that is a property of the method, and it is why the hop size is worth choosing deliberately.
  • No state space. Sifting is not linear in the frame, so model reduction correctly declines to merge it.

Code facts#

FactValue
registered typeControl_Systems/Time_Frequency/Empirical_Mode_Decomposition
familyControl_Systems/Time_Frequency
solver environment classICoreBlock_0_Control_Systems_1_Time_Frequency_2_Empirical_Mode_Decomposition
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Time_Frequency/Empirical_Mode_Decomposition/ICoreBlock_0_Control_Systems_1_Time_Frequency_2_Empirical_Mode_Decomposition.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Time_Frequency/Empirical_Mode_Decomposition/ICoreBlock_0_Control_Systems_1_Time_Frequency_2_Empirical_Mode_Decomposition.h
default size on canvas170 × 120 px
ports at insert1 in, 5 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoubleIMF
3outICoreDoubler
4outICoreDoublen
5outICoreDoubleinsf
6outICoreDoubleinse

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 variableDefaultSimulink parameter
Frame Length64—
Hop Size32—
Max Number of IMFs10—
Sift Max Iterations100—
Sift Relative Tolerance0.2—
Max Number of Extrema1—
Max Energy Ratio (dB)20—
Interpolationspline%~%pchip~~spline—
Sample Rate (Hz)1—

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.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): emd and hht are Signal Processing / Wavelet toolbox functions, not Simulink library blocks -- neither toolbox ships a library block that decomposes a signal into intrinsic mode functions -- 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).

Empirical Mode Decomposition -- MATLAB's emd over a frame, with hht's transform on top The last L samples are sifted: the local maxima and minima are found, a spline (or pchip) envelope is run through each set, their mean is subtracted, and that repeats until the relative tolerance ||prev - now||^2 / ||prev||^2 falls below SiftRelativeTolerance or the sift count reaches SiftMaxIterations. What is left is one intrinsic mode function; it is subtracted and the next mode is sifted out of the remainder, until MaxNumIMF modes are found, the residue has fewer than MaxNumExtrema extrema, or 10*log10(||x||/||residue||) passes MaxEnergyRatio. Every mode then gets hht's own transform: the analytic signal's squared magnitude as the instantaneous energy, and fs/(2*pi) times the centred difference of its unwrapped phase as the instantaneous frequency.

MEASURED AGAINST R2026a BEFORE A LINE WAS WRITTEN, and against the FULL output rather than a summary: over 44 frames (lengths 33 to 128, eight option sets covering pchip, capped sift counts, a tightened tolerance, an early energy-ratio stop and a raised extrema floor) this block's reference run reproduces emd's IMF matrix to a worst 1.6e-15 and its residual to 7.2e-16, WITH THE SAME NUMBER OF MODES AND THE SAME SIFT COUNT ON EVERY FRAME, and hht's instantaneous frequency to 4.6e-13 at fs = 50 (2e-14 relative) and energy to 2.5e-15 relative. The facts a wrong implementation would get wrong, each one measured:

  • a maximum is a sample whose left difference is > 0 and right difference <= 0 (a minimum

the mirror), so a flat pair belongs to whichever side the strict test falls on;

  • both envelopes are extended past the frame by emdWaveExtension -- a sine of amplitude

|peak - trough|/2 and period 2*|peak location - trough location|, contributing up to three knots per side, dropped where one would land exactly on the end sample;

  • the envelope is interp1's 'spline', which is the NOT-A-KNOT cubic (its end conditions

come from MATLAB's spline(), not from a natural or clamped one), evaluated by ppval's Horner form;

  • the first sift test compares a tolerance of exactly 1, so a mode is always sifted at

least once, whatever the tolerance;

  • the residual is x minus the modes found, and the unfilled columns of the IMF matrix are

zeros rather than a shorter matrix, because a wire has one size.

Support::None: emd and hht are Signal Processing / Wavelet functions and neither toolbox ships a Simulink library block for them, so there is no counterpart to bridge to.

Sample results#

Empirical Mode Decomposition — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sampleEmpirical Mode Decomposition — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-202012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0 [64x10] entry 0out ICoreDouble-Out-1 [64x1] entry 0out ICoreDouble-Out-2
tin ICoreDouble-Out-0out ICoreDouble-Out-0 [64x10] entry 0out ICoreDouble-Out-1 [64x1] entry 0out ICoreDouble-Out-2
0-2[0, 0, 0, 0]…[0, 0, 0, 0]…0
0.40.5[0, 0, 0, 0]…[0, 0, 0, 0]…0
0.8-2[0, 0, 0, 0]…[0, 0, 0, 0]…0
1.20.5[0, 0, 0, 0]…[0, 0, 0, 0]…0
1.6-2[0, 0, 0, 0]…[0, 0, 0, 0]…0
20.5[0, 0, 0, 0]…[0, 0, 0, 0]…0
2.4-2[0, 0, 0, 0]…[0, 0, 0, 0]…0
2.80.5[0, 0, 0, 0]…[0, 0, 0, 0]…0
3.2-2[0, 0, 0, 0]…[0, 0, 0, 0]…0
3.60.5[0, 0, 0, 0]…[0, 0, 0, 0]…0
4-2[0, 0, 0, 0]…[0, 0, 0, 0]…0
4.40.5[0, 0, 0, 0]…[0, 0, 0, 0]…0
4.8-2[0, 0, 0, 0]…[0, 0, 0, 0]…0
5.20.5[0, 0, 0, 0]…[0, 0, 0, 0]…0

Every 4th of 60 samples, from the table stimulus.

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0
rampRamp: slope 1 from t = 00 … 0
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 0
stepStep: 0 -> 1 at t = 1 s0 … 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 23d8841c6561ca4bb64cd9b5b64da9638de12629 · produced by docsSample --out <folder> --blocks Fixed_Wing_Point_Mass Kernel_Classifier_Predictor Kernel_Regression_Predictor Rotor Rotor_With_Flap_Effects Multirotor Multirotor_With_Flap_Effects Dynamic_Inflow_3_State Kurtogram Empirical_Mode_Decomposition Modal_FRF Order_Spectrum --steps 60 · data docs/generated/samples/Control_Systems__Time_Frequency__Empirical_Mode_Decomposition.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).