Generated reference › Modal Parameter Fit — Control Systems/Vibration
kind: generated#block#control-systems-vibration

Modal Parameter Fit — Control Systems/Vibration

fn, ζ

Control_Systems/Vibration/Modal_Parameter_Fit · 2 input / 2 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.

Modal Parameter Fit

Control Systems / Vibration

The natural frequencies and damping ratios of a structure, read off a frequency-response function – MATLAB's modalfit at FitMethod 'pp', the peak-picking arm. The FRF arrives on two ports as its real and imaginary parts, one entry per frequency bin, and the block finds up to Modes resonances in it:

  • the local maxima of 20·log10|H| whose prominence exceeds 0.5 dB, tallest first;
  • around each one, nine bins and a three-parameter least squares – A = [Re H, −2ω Im H, −1], b = ω² Re H – giving fn = √X1/2π and ζ = X2/√X1;
  • a sort by frequency, and then modalfit's own filter: a fit whose pole has a positive real part is discarded. The sort runs before the filter, so a discarded mode leaves its gap in frequency order rather than closing it.

⚠ That filter is poles(~(real(poles)<=0 & ~isreal(poles))) = NaN, and isreal is a property of the whole array: one complex pole, or one slot no peak was found for – NaN + i·NaN, whose imaginary part is NaN and so is not zero – makes the test true for every pole at once. The block carries that as a single flag, because with it removed a critically damped peak is kept or dropped according to what the other slots hold.

Ports

  • Re – the real part of the FRF, [B, 1], bin m at m·Δf hertz.
  • Im – its imaginary part, the same size.
  • fn – the natural frequencies in hertz, [Modes, 1], ascending. A mode the fit did not find reports 0.
  • dr – the damping ratios, [Modes, 1], in the same order.

Parameters

  • Modes – how many resonances to look for, a whole number from 1 to 8. This is modalfit's mnum, and it sets the height of both output ports.
  • Bin Spacing – Δf, the frequency step between the FRF's bins, in hertz: bin m is at m·Δf. The same parameter, under the same name, that Welch PSD, Cross Power Spectral Density and the rest of this family carry.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Where the FRF comes from

Spectral Measurements / Transfer Function Estimate publishes exactly this pair: H1 = Pyx/Pxx on Re and Im ports, [L/2+1, 1], with its own Bin Spacing. Wire those two ports into these two, give both blocks the same Bin Spacing, and the result is modalfit(modalfrf(x, y), ...) one block at a time – the estimator that MATLAB's modalfrf also uses by default. Any other source of an FRF on two real ports serves equally: this block reads the numbers, not their provenance.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The peak search, the prominence rule, the nine-bin solve and the sort are emitted as loops over the input arrays; both settings are structural, since they fix the loop bounds and the output height.

⚠ The three HDL targets run it in simulation-only real arithmetic, quantizing only at the port boundaries: the fit divides by a determinant and takes square roots, which a Q16.16 datapath does not carry.

Simulink bridge

None (Support::None). modalfit is a Signal Processing Toolbox function and that toolbox ships no Simulink library; a search of 119 block libraries found no modal-fit block. 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 only on the FRF present at that step. Discrete by nature all the same (setDiscreteOnlyBlock(true)), because there is nothing to integrate.
  • ⚠ A mode that is not found reports 0, where modalfit reports NaN: three of the ten targets carry no NaN, and 0 Hz is not a frequency this fit can otherwise produce.
  • ⚠ Only the peak-picking arm. modalfit's default is LSCE, which needs the inverse transform of the FRF, a Hankel least squares and then the roots of a degree-2·Modes polynomial. Root finding has no bounded form in these ten targets, so it is not offered rather than approximated. LSRF is refused by MATLAB itself in code generation.
  • ⚠ No mode shapes. A shape is a vector across sensors and needs several FRF columns measured at once; one column fixes the pole and says nothing about the shape.
  • Peak picking is the crude estimator, and modalfit's documentation says so: it assumes the modes are well separated and lightly damped. Two modes inside one peak are reported as one.

Code facts#

FactValue
registered typeControl_Systems/Vibration/Modal_Parameter_Fit
familyControl_Systems/Vibration
solver environment classICoreBlock_0_Control_Systems_1_Vibration_2_Modal_Parameter_Fit
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/Modal_Parameter_Fit/ICoreBlock_0_Control_Systems_1_Vibration_2_Modal_Parameter_Fit.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/Modal_Parameter_Fit/ICoreBlock_0_Control_Systems_1_Vibration_2_Modal_Parameter_Fit.h
default size on canvas150 × 86 px
ports at insert2 in, 2 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleRe
2inICoreDoubleIm
3outICoreDoublefn
4outICoreDoubledr

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
Modes2not crossed
Bin Spacing1not 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.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes
deliberately not crossedModes, Bin Spacing

Caveat (shown to the user): modalfit is a Signal Processing Toolbox function and that toolbox ships no Simulink library; a name search of 119 block libraries (dsp, dsphdl, signal, simulink, hdlcoder, comm, audio, soc) found no modal-fit or modal-parameter block, so there is no path a diagram could name

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:

  • B0 every 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).

Modal Parameter Fit -- MATLAB's modalfit, peak-picking arm, on an FRF that arrives on ports In: the frequency-response function as Re and Im, one entry per bin, bin m at m*df hertz. Out: the natural frequencies fn and the damping ratios dr of up to Modes modes.

The arm is modalfit's 'pp', written out:

  1. dB = 20 log10 |H|.
  2. The local maxima whose PROMINENCE exceeds 0.5 dB, tallest first, at most Modes of

them -- findpeaks(dB, 'NPeaks', mnum, 'MinPeakProminence', 0.5, 'SortStr', 'descend').

  1. Nine bins around each peak, and a three-parameter least squares there:

A = [Re H, -2w Im H, -1], b = w^2 Re H, X = A\b, fn = sqrt(X1)/2pi, dr = X2/sqrt(X1).

  1. Sort, then turn each (fn, dr) into a pole and keep only the poles with real part <= 0.

⚠ EVERY ONE OF THOSE FOUR STEPS WAS MEASURED AGAINST R2026a, by a prototype written in the loops this block emits -- no findpeaks, no pinv, no complex type -- and run against modalfit on 300 random FRFs (24 to 96 bins, 1 to 5 modes, a third of them noisy): worst relative disagreement 2.8e-12 on fn and 3.8e-13 on dr, with the peak SELECTION identical in every case. Four details carry that agreement, and each was a measured correction:

  • THE LEAST SQUARES IS ON THE REAL PARTS. modalfit writes pinv(real(A))*real(b) with a

complex A, so the second column is -2w Im H and the right-hand side is w^2 Re H. Here it is the 3x3 normal equations solved by the adjugate -- measured against MATLAB's own backslash on the same 300 cases, same agreement to 2.8e-12.

  • A NEGATIVE FITTED wn^2 IS NOT AN ERROR TO MATLAB. sqrt(X1) is then imaginary, and every

quantity after it is complex; the block carries the same numbers as (re, im) pairs, so it follows modalfit into that branch instead of dropping the mode. On a third of the noisy cases that is the difference between agreeing and not.

  • modalfit SORTS BEFORE IT FILTERS. The frequencies are sorted, then unphysical poles are

replaced by NaN, so a discarded mode leaves its gap where its frequency put it -- not at the end. A complex fitted frequency sorts by magnitude and then by phase, which is why the sort key here is the magnitude with a tie-break on a nonzero imaginary part.

  • THE POLE FILTER IS real(poles)<=0 & ~isreal(poles), and isreal() IS A PROPERTY OF THE

WHOLE ARRAY IN MATLAB, not of an element: one complex pole makes the second test true for every pole in the vector. The block does the same -- one flag for the whole answer. ⚠ AND A SLOT NO PEAK WAS FOUND FOR COUNTS AS COMPLEX. MATLAB leaves it NaN, and NaN + 1i*NaN has an imaginary part of NaN, which is not zero, so an unfilled slot makes isreal(poles) false exactly as a genuine complex pole does. Measured: a lone critically damped peak (dr = 1, a real pole) is KEPT when another slot is empty and DROPPED when every slot is filled and every pole is real. Missing that one clause cost 4 ticks in 800 on the stream check and 1 case in 300 on the stress; with it, both are exact.

Sample results#

No stimulus produced a sampled output in this rig — Invalid input size at: ICore Blocks/Home/Modal Parameter Fit. 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 351dc4dde · produced by docsSample --out <folder> --blocks Control_Systems/Vibration/Modal_Parameter_Fit,Control_Systems/Optimization/Nelder_Mead_Search --steps 60

Sample data: docs/generated/samples/Control_Systems__Vibration__Modal_Parameter_Fit.json