Modal FRF — Control Systems/Vibration
Control_Systems/Vibration/Modal_FRF · 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 FRF
Control Systems / Vibration
The frequency-response function of a structure, estimated from the force
that excites it (x) and the response it produces (y), over a sliding
window – MATLAB's modalfrf(x, y, fs, window, noverlap). The
block keeps the last W samples of each input, splits them into K
segments, windows and transforms every one, and averages the spectra
Pxx = Σ|X|², Pyy = Σ|Y|² and
Pyx = ΣY·X*. From them it forms one of three
estimates bin by bin:
- H1 = Pyx / Pxx – unbiased by noise on the response.
- H2 = Pyy / Pyx* – unbiased by noise on the excitation.
- Hv = (Pyx/|Pyx|)·√(Pyy / Pxx) – the phase of H1 with the geometric mean of the two magnitudes.
and converts it to dynamic flexibility (displacement per unit force) from the quantity the response sensor measured: an accelerance is multiplied by −1/(2πf)², a mobility divided by j·2πf, a displacement left alone – with f = m·fs/L, and bin 0 taking bin 1's factor, as MATLAB does.
Ports
- x – the excitation (force), a scalar stream.
- y – the response, a scalar stream, in the unit the Sensor parameter names.
- Re – the real part of the FRF, a column of L/2 + 1 entries: one per frequency bin from DC to Nyquist. Entry m is the bin at m·fs/L hertz.
- Im – the imaginary part, the same size and the same bins.
Parameters
- Segment Length – L, the transform length, an even whole number from 4 to 32. MATLAB's window length, which modalfrf also uses as the transform length.
- Segments – K, how many segments are averaged, from 1 to 8. One segment is a valid estimate (a single impact), and there all three estimators coincide at Y/X.
- Segment Overlap – how far the segments overlap.
- None (0%) – the hop is L. modalfrf's default, and this block's.
- Half (50%) – the hop is L/2.
- Three quarters (75%) – the hop is L/4; needs L divisible by four.
noverlapis L − hop. - Window – the taper applied to each segment:
Rectangular (what modalfrf uses when its window argument is a length, and
this block's default), Hann or Hamming. Both tapers are the
symmetric form that
hannandhammingreturn. - Estimator – which ratio the answer is.
- H1 – modalfrf's default, and this block's.
- H2
- Hv
- Sensor – what the response y measures.
- Acceleration – modalfrf's default, and this block's.
- Velocity
- Displacement
- Sample Rate (Hz) – fs, positive; it sets the frequency of each bin, and so the acceleration and velocity factors. It is not read for a displacement sensor.
- 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. Every transform is a fixed weighted sum of window taps and the weights and sensor factors are computed once at export time, so no emitted core contains a sine, a cosine or a loop over data; the two windows become shift registers. Nothing is exposed as a tunable parameter: every setting changes the coefficient table or the arithmetic, which is structural.
The three HDL targets are simulation-only: they carry the
arithmetic in real and quantize only at the port boundary. The
answer is a ratio of two run-time spectra spanning several decades, which does
not belong in a Q16.16 datapath – and an accelerance converted to
displacement is often far below the Q16.16 quantum of 1.5×10−5,
so scale the output before it reaches a fixed-point port. VHDL carries each bin in
an emitted function, because a clocked process here has no place to keep
the running sums a bin needs.
Simulink bridge
No equivalent (Support::None), so nothing crosses in
either direction, and no parity testbench is owed. modalfrf is
a Signal Processing Toolbox function, that toolbox ships no Simulink
library, and no library block estimates a frequency-response function from two
streams. No configuration crosses either, including "Sampling Time (s)", which has
no counterpart to be written to. Code export verification still covers the block
across all ten languages.
Notes
- Stateful and inherently discrete: two shift registers of W
samples, advanced once per sample. The block runs on the discrete path
whatever the model's solver is (
setDiscreteOnlyBlock(true)), and its period comes from its own "Sampling Time (s)" – or, when that is zero or less, from the model's global sampling time. The output depends on the current sample, so a loop through the block is an algebraic loop. - Verified against R2026a: the arithmetic here reproduces
modalfrfover 324 configurations – two segment lengths, three segment counts, two overlaps, three windows, all three estimators and all three sensors – to 5×10−13 relative in the worst bin. At any instant the output is modalfrf of the last W samples of each input. - The first W−1 samples of a run are read against a zero-prefilled window, as every sliding-window block here is, so the estimate is complete only once the window has filled.
- A silent window answers zero, not a NaN: each denominator is floored at 10−30, which only an exactly zero spectrum reaches (MATLAB answers NaN or Inf there).
- Welch's normalizing constants are not computed, because all three estimators cancel them; the answer matches MATLAB's, the intermediate spectra do not.
- Not offered: modalfrf's subspace estimator, which identifies a state-space model instead of dividing spectra, and its Measurement option, which only arranges multi-channel answers.
- Related blocks. Spectral Measurements / Magnitude-Squared Coherence takes the same segmentation parameters and gives the coherence modalfrf returns as its third output – wire both to the same two streams to judge where the FRF can be trusted.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Vibration/Modal_FRF |
| family | Control_Systems/Vibration |
| solver environment class | ICoreBlock_0_Control_Systems_1_Vibration_2_Modal_FRF |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/Modal_FRF/ICoreBlock_0_Control_Systems_1_Vibration_2_Modal_FRF.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Vibration/Modal_FRF/ICoreBlock_0_Control_Systems_1_Vibration_2_Modal_FRF.h |
| default size on canvas | 160 × 86 px |
| ports at insert | 2 in, 2 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 | y |
| 3 | out | ICoreDouble | Re |
| 4 | out | ICoreDouble | Im |
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 |
|---|---|---|
Segment Length | 16 | — |
Segments | 4 | — |
Segment Overlap | None (0%)%~%Half (50%)%~%Three quarters (75%)~~None (0%) | — |
Window | Rectangular%~%Hann%~%Hamming~~Rectangular | — |
Estimator | H1%~%H2%~%Hv~~H1 | — |
Sensor | Acceleration%~%Velocity%~%Displacement~~Acceleration | — |
Sample Rate (Hz) | 1000 | — |
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): no Simulink equivalent. modalfrf is a Signal Processing Toolbox MATLAB function, not a block; that toolbox ships no Simulink library, and no library block estimates a frequency-response function from an excitation and a response stream
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).
Modal FRF -- the frequency-response function of a structure from an excitation and a response, MATLAB's modalfrf(x, y, fs, window, noverlap, 'Estimator', e, 'Sensor', s) over a running window of the last W samples of both streams. With K segments of L samples, hop h, window w, and X_s, Y_s the windowed L-point transforms of segment s (bins m = 0..L/2):
Pxx = SUM_s |X_s|^2 Pyy = SUM_s |Y_s|^2 Pyx = SUM_s Y_s . conj(X_s)
H1 = Pyx / Pxx H2 = Pyy / conj(Pyx) Hv = (Pyx / |Pyx|) . sqrt(Pyy / Pxx)
then converted to DYNAMIC FLEXIBILITY (displacement per unit force) from the sensor:
acceleration: FRF = H . ( -1 / (2 pi f)^2 ) velocity: FRF = H / (j 2 pi f) displacement: FRF = H f = m . fs / L, and bin 0 takes bin 1's factor
⚠ ALL OF IT READ OUT OF R2026a's SOURCE AND THEN MEASURED. modalfrf.m hands H1 and H2 to tfestimate and computes Hv itself from cpsd and two pwelch calls (signal.internal.modal.computeFRF), with nfft = the window length; tfestimate's H1 is welch's 'tfe' -- the cross spectrum of (y, x), i.e. SUM Y.conj(X), over Pxx -- and its H2 is 'tfeh2', Pyy over SUM X.conj(Y). signal.internal.modal.toDynamicFlex then multiplies by the sensor factor, whose FIRST entry is overwritten by its second, so DC is not a division by zero. A Python transcription of exactly the statements below reproduced modalfrf over 324 configurations -- L 8 and 16, K 1, 3 and 4, 0 % and 50 % overlap, the rectangular (scalar window argument), Hann and Hamming windows, all three estimators, all three sensors, fs 50 and 3 -- to 5.2e-13 relative in the worst bin (a bin of magnitude 6e-8, where the acceleration factor is smallest) and 8.5e-12 absolute.
⚠ WELCH'S NORMALISING CONSTANTS ARE NOT COMPUTED, BECAUSE ALL THREE ESTIMATORS CANCEL THEM. Each averaged spectrum carries the same 1/(K . fs . SUM w^2) and the same one-sided doubling; H1 and H2 are ratios of two such spectra and Hv is a unit phasor times the root of a third ratio. So the RAW sums give MATLAB's numbers, exactly as they do for Magnitude-Squared Coherence, whose segmentation, windows and coefficient tables this block shares by design.
⚠ THE SUBSPACE ESTIMATOR IS NOT OFFERED. modalfrf's fourth estimator fits a state-space model by stochastic subspace identification and reads the FRF off it; it is a different algorithm, not a fourth ratio of the same spectra, and a streaming core cannot carry it. The 'Measurement' option (fixed / roving input / roving output) only reshapes multi-channel answers, and two scalar streams are one channel, so it has nothing to select here.
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) | -6.485e-6 … 0 |
ramp | Ramp: slope 1 from t = 0 | -6.485e-6 … 0 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -6.485e-6 … 0 |
table | Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample | -6.485e-6 … 0 |
Plotted: step — Step: 0 -> 1 at t = 1 s
Category dynamic · 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__Vibration__Modal_FRF.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).