4. Frequency-Domain Correlation: Comparing Spectral Content

Why Frequency-Domain Correlation Matters

Time-domain CORA scores tell you how much two curves differ, but they rarely tell you where in the spectrum the disagreement lives. A CAE signal can match a test signal closely in overall amplitude yet diverge badly in a narrow band around a structural resonance — a discrepancy that a single time-domain proximity number will average away. Frequency-domain correlation exposes that structure. By transforming both signals with an FFT and comparing magnitude and phase as functions of frequency, you can localize poor agreement to specific bands and decide whether the root cause is a missing mode, a frequency shift, excessive damping, or a sensor/processing artifact.

This case builds a complete, reproducible frequency-domain comparison workflow: prepare matched CAE and test signals, compute single-sided amplitude spectra, derive magnitude and phase coherence metrics, and flag the bands where the two signals disagree. The example uses synthetic signals with a known ground truth so that every metric can be checked against an expected answer.

Inputs and Signal Setup

Both signals must share the same sampling rate and duration before any spectral comparison is meaningful — this is the precondition established during preprocessing. For this worked example, assume a sampling frequency fs = 2048 Hz and a record length of N = 4096 samples, giving a frequency resolution of df = fs / N = 0.5 Hz and a Nyquist limit of 1024 Hz. The test signal is modeled as a sum of three damped sinusoids plus broadband noise; the CAE signal reproduces two of them faithfully but shifts the third resonance upward by 6 Hz and under-predicts its amplitude by roughly 30%. That deliberate mismatch is what the metrics must detect.

import numpy as np

fs, N = 2048.0, 4096
t = np.arange(N) / fs
rng = np.random.default_rng(7)

# Ground-truth components: (frequency Hz, amplitude, damping ratio)
modes = [(120.0, 1.00, 0.02), (340.0, 0.60, 0.03), (610.0, 0.40, 0.015)]

def synth(modes, noise_std=0.0):
    sig = np.zeros_like(t)
    for f, a, z in modes:
        sig += a * np.exp(-z * 2*np.pi*f * t) * np.sin(2*np.pi*f*t)
    return sig + rng.normal(0.0, noise_std, t.size)

test = synth(modes, noise_std=0.02)
# CAE: mode 3 shifted +6 Hz and amplitude scaled to 0.70
cae_modes = [(120.0, 1.00, 0.02), (340.0, 0.60, 0.03), (616.0, 0.28, 0.015)]
cae = synth(cae_modes, noise_std=0.0)

The test signal carries measurement noise (σ = 0.02) while the CAE signal is noise-free, which mirrors the usual reality: the simulation is deterministic, the measurement is not. This asymmetry matters when interpreting coherence, because noise lowers coherence even where the underlying dynamics agree.

Computing Single-Sided Amplitude Spectra

Apply a Hann window before the FFT to suppress spectral leakage from the non-integer number of cycles in the record, then correct for the window's coherent gain so amplitudes remain physically meaningful. Only the positive-frequency half of the spectrum is retained, with interior bins doubled to preserve total energy.

def amplitude_spectrum(x, fs):
    w = np.hanning(x.size)
    cg = w.sum() / x.size                      # coherent gain of Hann window
    X = np.fft.rfft(x * w)
    amp = 2.0 * np.abs(X) / (x.size * cg)
    amp[0] /= 2.0                              # DC not doubled
    if x.size % 2 == 0:
        amp[-1] /= 2.0                         # Nyquist not doubled
    f = np.fft.rfftfreq(x.size, 1.0/fs)
    return f, amp

f, A_test = amplitude_spectrum(test, fs)
_, A_cae  = amplitude_spectrum(cae,  fs)

With this normalization, a pure sinusoid of amplitude 1.0 at a bin center returns a peak near 1.0 in the spectrum. Verify this on a quick sanity signal before trusting the comparison — a common failure is forgetting the coherent-gain correction, which silently scales every amplitude by about 0.5 and makes all magnitude ratios wrong by a constant factor.

Magnitude and Phase Coherence Metrics

Two complementary metrics localize disagreement. The magnitude ratio in decibels, 20·log10(A_cae / A_test), shows where the CAE over- or under-predicts energy. The phase difference, angle(X_cae · conj(X_test)), reveals timing or frequency shifts: a resonance moved by Δf produces a phase error that swings through 180° across the peak. Where the test spectrum is dominated by noise, the phase becomes meaningless, so gate the phase metric by a magnitude threshold.

X_test = np.fft.rfft(test * np.hanning(N))
X_cae  = np.fft.rfft(cae  * np.hanning(N))

mag_ratio_db = 20*np.log10(np.maximum(A_cae, 1e-12) / np.maximum(A_test, 1e-12))
phase_diff   = np.angle(X_cae * np.conj(X_test))

# Only trust phase where test energy is significant
floor = A_test.max() * 1e-2
valid = A_test > floor

# Flag bands with poor agreement
poor_mag   = valid & (np.abs(mag_ratio_db) > 3.0)   # >3 dB mismatch
poor_phase = valid & (np.abs(phase_diff) > np.deg2rad(30))
poor_band  = poor_mag | poor_phase

print("Bands with poor magnitude agreement (Hz):",
      f[poor_mag][:5], "...")
print("Bands with poor phase agreement (Hz):",
      f[poor_phase][:5], "...")

For the synthetic case, expect the magnitude flag to light up around 610–616 Hz (the 30% amplitude deficit is about −3.1 dB, just over the threshold) and the phase flag to cluster across the shifted peak, where the 6 Hz offset rotates the phase well beyond 30°. The 120 Hz and 340 Hz modes should pass both tests, confirming the metrics isolate the injected fault rather than smearing it across the whole spectrum.

Choose thresholds from the physics, not by habit. A 3 dB magnitude gate corresponds to a factor-of-√2 amplitude error; a 30° phase gate corresponds to roughly one-twelfth of a cycle of timing error at that frequency. Tighten them for qualification work, loosen them for early screening.

Reading the Correlation Map

The figure below summarizes how the two spectra relate across frequency. It plots the test and CAE amplitude envelopes, marks the three modal regions, and shades the band where the shifted third resonance causes both a magnitude deficit and a phase excursion. The key visual takeaway is that agreement is excellent below 500 Hz and collapses in a narrow window near 610 Hz — exactly the localization a time-domain score cannot provide.

Frequency-Domain Correlation Map: Test vs CAE poor band 120 Hz 340 Hz 610/616 Hz Frequency (Hz) Amplitude 0 256 512 768 1024 Test spectrum CAE spectrum Magnitude & phase mismatch

Interpreting the Result and Acting on It

The correlation map points to a single actionable finding: the third mode is both shifted and under-predicted. A frequency shift of 6 Hz at 610 Hz is about 1%, which typically indicates a stiffness or mass discrepancy in the CAE model rather than a damping error — damping errors change peak width and height but leave the center frequency nearly fixed. The amplitude deficit of roughly 30% suggests the model is also missing participation from that mode, possibly because a component is modeled too stiffly or a connection is over-constrained.

Before adjusting the model, rule out the measurement side. Confirm that the test spectrum's peak is not an artifact of a resonance in the sensor mount or a filter roll-off near the band edge. A quick check: recompute the test spectrum with a different window (e.g., a flat-top window) and confirm the peak location is stable to within one bin. If it moves, the peak is a processing artifact, not a physical mode.

MetricDefinitionTypical pass gateWhat a failure implies
Magnitude ratio (dB)20·log10(A_cae / A_test)|Δ| ≤ 3 dBAmplitude scaling, missing/extra mode participation
Phase differenceangle(X_cae · conj(X_test))|Δφ| ≤ 30°Frequency shift, time lag, damping mismatch
Coherence (γ²)|S_xy|² / (S_xx · S_yy)≥ 0.8 in bandNoise dominance, nonlinearity, nonstationarity
Phase is only meaningful where the test signal has real energy. Applying a phase gate in noise-dominated bins produces false "poor agreement" flags that send you chasing phantom discrepancies. Always gate phase by a magnitude floor, as done above with valid = A_test > floor.

Verification Checklist

  • Confirm fs and N are identical for both signals; a mismatch invalidates every bin index.
  • Validate the amplitude normalization on a unit sinusoid before comparing real data.
  • Check that flagged bands coincide with physical modes, not with window or filter artifacts.
  • Re-run with a second window type; stable peak locations confirm physical content.
  • Record the magnitude and phase gates used, so the correlation result is reproducible by another engineer.

Once the poor-agreement band is localized and confirmed physical, the next step is to trace it back to the model — but that diagnosis belongs to modal correlation, where mode shapes and frequencies are compared directly. Here the deliverable is the localization itself: a frequency-resolved map of where CAE and test diverge, with the metrics and thresholds that justify each flag.