Cross-Correlation Analysis (cross-cutting/numerics/cross-correlation-analysis)
Use when the task is measuring how one sampled signal sequence relates
to another: the raw cross-correlation over every integer lag with zero
padding, the normalized correlation coefficient in [-1, 1], the lag of
the peak as a time-delay estimate, and the even-symmetry check of the
autocorrelation. This leaf is the generic discrete correlation utility
for sampled sequences: all coefficients are computed, none are lookup
values. It pairs with cross-cutting/numerics/fast-fourier-transform
(the frequency-domain view of the same signals) and with
cross-cutting/numerics/digital-filter-design (prefiltering before
correlation), and it shares the correlation-style statistics spirit of
cross-cutting/numerics/least-squares-regression. It does not smooth or
time-align raw flight-test traces (flight-test-operations/planning/
flight-test-data-reduction owns moving-average smoothing and test-data
alignment) and it does not match test-to-analysis mode shapes
(ground-vibration-testing owns MAC-based modal correlation).
Domain quick reference
- Convention: rxy[k] = sum over n of x[n] * y[n - k], for every integer
lag k in [-(Ny - 1), Nx - 1]; terms whose index falls outside a
sequence contribute zero. Nx = len(x), Ny = len(y).
- Sign convention: a positive peak lag means x leads y. If y is x
delayed by d samples, the peak sits at lag -d (as in the worked
example). In delay_estimate, delay_samples = -peak_lag, so a positive
delay_samples means y is delayed relative to x.
- Modes: raw returns the plain sums; biased divides every value by Nx;
unbiased divides by the number of overlapping samples at each lag.
- Normalized coefficient: value / sqrt(rxx0 * ryy0) with rxx0 = sum
x[n]^2 and ryy0 = sum y[n]^2 the zero-lag energies. Coefficients lie
in [-1, 1]; an identical shape gives 1.0 at the matching lag.
- Autocorrelation rxx[k] = cross_correlation(x, x): even, rxx[k] =
rxx[-k], with the zero-lag value rxx[0] = sum x[n]^2 the signal
energy.
- Peak selection: lag of the maximum absolute value; ties resolve to
the smaller absolute lag, then the first encountered.
- Zero-lag coefficient: sum x[n] y[n] / sqrt(rxx0 ryy0), the normalized
similarity at k = 0.
- NACA TR-824 frames the numerics-pack reference set; the relations
above are standard discrete-signal methodology, summary-only.
Workflow
- Load the two sampled sequences x and y as lists of floats (SI
samples, any physical unit carried by the caller).
- Run the raw correlation: lags, values = cross_correlation(x, y);
lags runs -(Ny-1) .. Nx-1 with one value per lag.
- Inspect the peak: peak_lag(lags, values) gives the lag of the
maximum absolute correlation and the delay sign per the convention
above.
- Get the bounded similarity view: lags, coeffs =
normalized_cross_correlation(x, y); read the coefficient at the peak
lag, 1.0 for a perfect delayed match.
- For the compact delay result, run delay_estimate(x, y) and read
peak_lag, peak_value, normalized_peak and delay_samples.
- Compare two channels at zero offset with
zero_lag_coefficient(x, y).
- For a single channel, run autocorrelation(x) and verify evenness
rxx[k] == rxx[-k] and the zero-lag energy rxx[0] = sum x[n]^2.
- Repeat with mode = "biased" or "unbiased" when the caller needs the
scaled conventions for spectral-density-style work.
- Confirm the deterministic checks with the contract test
scripts/test_cross_correlation_analysis.py.
Worked example
x = [1, 2, 3, 4, 5]; y = [0, 0, 1, 2, 3, 4, 5] (y is x delayed by 2
samples, Nx = 5, Ny = 7).
- cross_correlation(x, y): lags -6..4; raw values [5, 14, 26, 40, 55,
40, 26, 14, 5, 0, 0]. Peak at lag -2 with value 55.
- Sign check: peak lag -2 (negative), so x leads y; delay_samples =
-(-2) = +2, y is delayed by 2 samples relative to x.
- normalized_cross_correlation at the peak lag: 55 / sqrt(55 * 55) =
1.0, a perfect delayed match.
- delay_estimate(x, y): {peak_lag: -2, peak_value: 55.0,
normalized_peak: 1.0, delay_samples: 2}.
- autocorrelation([1, 2, 3]): rxx[0] = 14, rxx[1] = rxx[-1] = 8,
rxx[2] = rxx[-2] = 3; the sequence [3, 8, 14, 8, 3] is even.
- autocorrelation([1, 1, 1, 1], "biased"): 1.0 at lag 0, 0.75 at lag
+1 (3/4); "unbiased": 1.0 at both lags (3 overlapping samples give
3/3).
- zero_lag_coefficient([1, 2, 3], [3, 2, 1]) = 10/14 = 0.7143.
Pitfalls
- Misreading the delay sign: a peak at lag -2 means x leads y and
delay_estimate returns delay_samples +2 (y is delayed by 2 relative
to x); swapping the inputs flips the peak lag sign.
- Comparing raw values across different lags or lengths: raw mode is
the plain sum over the overlap, so its magnitude grows with the
overlap count; use the normalized coefficients (in [-1, 1]) when the
scale matters.
- Forgetting what unbiased normalization does at the ragged lags:
unbiased divides by the number of overlapping samples at each lag
(3 overlapping samples give 3/3 = 1.0 for [1,1,1,1]), while biased
divides every value by Nx (0.75 at lag +1 for the same signal).
- Passing empty, non-numeric, non-finite, or zero-energy inputs: empty
sequences, non-numeric or non-finite entries, unknown modes, and
zero-energy normalization all raise ValueError.
- Treating autocorrelation as a delay finder on the wrong signal:
the autocorrelation is even with rxx[0] the energy, and an
identical-signal cross-correlation peaks at lag 0; a peak elsewhere
is the delay of the copy.
- Calling this leaf for what siblings own: flight-test-data-reduction
owns smoothing and time alignment of raw flight-test traces, and
ground-vibration-testing owns MAC-based modal correlation; this leaf
is the generic discrete time-delay utility.
Verification
- Confirm cross_correlation(x, y) on the worked example returns the raw
values list above with peak_lag -2 and peak value 55.
- Confirm the normalized peak coefficient is 1.0 within 1e-9 and every
normalized coefficient lies in [-1, 1].
- Confirm delay_estimate(x, y) returns delay_samples +2 and that
swapping the inputs flips the peak lag sign (delay_samples -2).
- Confirm autocorrelation([1, 2, 3]) is even with rxx[0] = 14 and that
the identical-signal cross-correlation peaks at lag 0.
- Confirm biased divides by Nx and unbiased divides by the overlap
count: [1, 1, 1, 1] gives 0.75 biased and 1.0 unbiased at lag +1.
- Confirm every invalid input raises ValueError: empty sequences,
non-numeric or non-finite entries, unknown modes, and zero-energy
normalization.
- Run the contract test offline: python3
scripts/test_cross_correlation_analysis.py (41 tests, deterministic).
Related leaves
- cross-cutting/numerics/fast-fourier-transform: spectrum of the same
sampled signals, the frequency-domain partner to time-domain
correlation.
- cross-cutting/numerics/digital-filter-design: prefilter the channels
before correlating so out-of-band content does not mask the peak.
- cross-cutting/numerics/least-squares-regression: regression fitting
that consumes correlation-style statistics between variables.
- flight-test-operations/planning/flight-test-data-reduction: domain
smoothing and time alignment of raw flight-test traces, distinct from
this generic discrete correlation utility.
- flight-test-operations/flutter/ground-vibration-testing owns MAC-based
modal correlation of test and analysis mode shapes, which is
mode-shape matching, not time-series delay estimation.
Behavior contract (gate 3)
Run the deterministic contract test (stdlib unittest, offline):
python3 scripts/test_cross_correlation_analysis.py
The test covers the spec worked example anchors (lag range -6..4, raw
values list, peak lag -2, peak value 55, normalized peak 1.0, delay
estimate with delay_samples +2, zero-lag coefficient 10/14),
autocorrelation evenness with the 14/8/3 values, biased and unbiased
scaling (0.75 and 1.0 at lag +1 on four ones), the identical-signal
peak at lag 0, cross-correlation reversal symmetry, the Cauchy-Schwarz
peak identity, and ValueError rejection of empty, non-finite,
non-numeric, unknown-mode and zero-energy inputs. Runs in well under a
second.
Compliance
- Standards referenced, not reproduced: NACA TR-824 anchors the
numerics-pack public-domain reference set; discrete cross-correlation
is standard signal-analysis methodology (Bendat and Piersol style
summary), paraphrase-only per standards-map.yaml.
- compliance: STANDARDS-REF, gated: false.
1---2name: cross-correlation-analysis3description: Use when you must compute the cross-correlation or autocorrelation of sampled signal sequences to quantify channel similarity and time delay: evaluate the raw cross-correlation over the full lag range with zero padding, normalize it to a correlation coefficient in [-1, 1] from the zero-lag energies, estimate the delay between channels from the peak lag, apply the biased or unbiased convention, and verify the even symmetry of the autocorrelation. Produces the correlation sequence, the peak lag, the normalized coefficient, and the delay in samples that gate time-delay analysis. Trigger: cross-correlation, autocorrelation, time-delay-estimation, lag, normalized-correlation-coefficient, channel-similarity, delay-between-signals.4license: Apache-2.05---67# Cross-Correlation Analysis (cross-cutting/numerics/cross-correlation-analysis)89Use when the task is measuring how one sampled signal sequence relates10to another: the raw cross-correlation over every integer lag with zero11padding, the normalized correlation coefficient in [-1, 1], the lag of12the peak as a time-delay estimate, and the even-symmetry check of the13autocorrelation. This leaf is the generic discrete correlation utility14for sampled sequences: all coefficients are computed, none are lookup15values. It pairs with cross-cutting/numerics/fast-fourier-transform16(the frequency-domain view of the same signals) and with17cross-cutting/numerics/digital-filter-design (prefiltering before18correlation), and it shares the correlation-style statistics spirit of19cross-cutting/numerics/least-squares-regression. It does not smooth or20time-align raw flight-test traces (flight-test-operations/planning/21flight-test-data-reduction owns moving-average smoothing and test-data22alignment) and it does not match test-to-analysis mode shapes23(ground-vibration-testing owns MAC-based modal correlation).2425## Domain quick reference2627- Convention: rxy[k] = sum over n of x[n] * y[n - k], for every integer28 lag k in [-(Ny - 1), Nx - 1]; terms whose index falls outside a29 sequence contribute zero. Nx = len(x), Ny = len(y).30- Sign convention: a positive peak lag means x leads y. If y is x31 delayed by d samples, the peak sits at lag -d (as in the worked32 example). In delay_estimate, delay_samples = -peak_lag, so a positive33 delay_samples means y is delayed relative to x.34- Modes: raw returns the plain sums; biased divides every value by Nx;35 unbiased divides by the number of overlapping samples at each lag.36- Normalized coefficient: value / sqrt(rxx0 * ryy0) with rxx0 = sum37 x[n]^2 and ryy0 = sum y[n]^2 the zero-lag energies. Coefficients lie38 in [-1, 1]; an identical shape gives 1.0 at the matching lag.39- Autocorrelation rxx[k] = cross_correlation(x, x): even, rxx[k] =40 rxx[-k], with the zero-lag value rxx[0] = sum x[n]^2 the signal41 energy.42- Peak selection: lag of the maximum absolute value; ties resolve to43 the smaller absolute lag, then the first encountered.44- Zero-lag coefficient: sum x[n] y[n] / sqrt(rxx0 ryy0), the normalized45 similarity at k = 0.46- NACA TR-824 frames the numerics-pack reference set; the relations47 above are standard discrete-signal methodology, summary-only.4849## Workflow50511. Load the two sampled sequences x and y as lists of floats (SI52 samples, any physical unit carried by the caller).532. Run the raw correlation: lags, values = cross_correlation(x, y);54 lags runs -(Ny-1) .. Nx-1 with one value per lag.553. Inspect the peak: peak_lag(lags, values) gives the lag of the56 maximum absolute correlation and the delay sign per the convention57 above.584. Get the bounded similarity view: lags, coeffs =59 normalized_cross_correlation(x, y); read the coefficient at the peak60 lag, 1.0 for a perfect delayed match.615. For the compact delay result, run delay_estimate(x, y) and read62 peak_lag, peak_value, normalized_peak and delay_samples.636. Compare two channels at zero offset with64 zero_lag_coefficient(x, y).657. For a single channel, run autocorrelation(x) and verify evenness66 rxx[k] == rxx[-k] and the zero-lag energy rxx[0] = sum x[n]^2.678. Repeat with mode = "biased" or "unbiased" when the caller needs the68 scaled conventions for spectral-density-style work.699. Confirm the deterministic checks with the contract test70 scripts/test_cross_correlation_analysis.py.7172## Worked example7374x = [1, 2, 3, 4, 5]; y = [0, 0, 1, 2, 3, 4, 5] (y is x delayed by 275samples, Nx = 5, Ny = 7).7677- cross_correlation(x, y): lags -6..4; raw values [5, 14, 26, 40, 55,78 40, 26, 14, 5, 0, 0]. Peak at lag -2 with value 55.79- Sign check: peak lag -2 (negative), so x leads y; delay_samples =80 -(-2) = +2, y is delayed by 2 samples relative to x.81- normalized_cross_correlation at the peak lag: 55 / sqrt(55 * 55) =82 1.0, a perfect delayed match.83- delay_estimate(x, y): {peak_lag: -2, peak_value: 55.0,84 normalized_peak: 1.0, delay_samples: 2}.85- autocorrelation([1, 2, 3]): rxx[0] = 14, rxx[1] = rxx[-1] = 8,86 rxx[2] = rxx[-2] = 3; the sequence [3, 8, 14, 8, 3] is even.87- autocorrelation([1, 1, 1, 1], "biased"): 1.0 at lag 0, 0.75 at lag88 +1 (3/4); "unbiased": 1.0 at both lags (3 overlapping samples give89 3/3).90- zero_lag_coefficient([1, 2, 3], [3, 2, 1]) = 10/14 = 0.7143.9192## Pitfalls9394- Misreading the delay sign: a peak at lag -2 means x leads y and95 delay_estimate returns delay_samples +2 (y is delayed by 2 relative96 to x); swapping the inputs flips the peak lag sign.97- Comparing raw values across different lags or lengths: raw mode is98 the plain sum over the overlap, so its magnitude grows with the99 overlap count; use the normalized coefficients (in [-1, 1]) when the100 scale matters.101- Forgetting what unbiased normalization does at the ragged lags:102 unbiased divides by the number of overlapping samples at each lag103 (3 overlapping samples give 3/3 = 1.0 for [1,1,1,1]), while biased104 divides every value by Nx (0.75 at lag +1 for the same signal).105- Passing empty, non-numeric, non-finite, or zero-energy inputs: empty106 sequences, non-numeric or non-finite entries, unknown modes, and107 zero-energy normalization all raise ValueError.108- Treating autocorrelation as a delay finder on the wrong signal:109 the autocorrelation is even with rxx[0] the energy, and an110 identical-signal cross-correlation peaks at lag 0; a peak elsewhere111 is the delay of the copy.112- Calling this leaf for what siblings own: flight-test-data-reduction113 owns smoothing and time alignment of raw flight-test traces, and114 ground-vibration-testing owns MAC-based modal correlation; this leaf115 is the generic discrete time-delay utility.116117## Verification118119- Confirm cross_correlation(x, y) on the worked example returns the raw120 values list above with peak_lag -2 and peak value 55.121- Confirm the normalized peak coefficient is 1.0 within 1e-9 and every122 normalized coefficient lies in [-1, 1].123- Confirm delay_estimate(x, y) returns delay_samples +2 and that124 swapping the inputs flips the peak lag sign (delay_samples -2).125- Confirm autocorrelation([1, 2, 3]) is even with rxx[0] = 14 and that126 the identical-signal cross-correlation peaks at lag 0.127- Confirm biased divides by Nx and unbiased divides by the overlap128 count: [1, 1, 1, 1] gives 0.75 biased and 1.0 unbiased at lag +1.129- Confirm every invalid input raises ValueError: empty sequences,130 non-numeric or non-finite entries, unknown modes, and zero-energy131 normalization.132- Run the contract test offline: python3133 scripts/test_cross_correlation_analysis.py (41 tests, deterministic).134135## Related leaves136137- cross-cutting/numerics/fast-fourier-transform: spectrum of the same138 sampled signals, the frequency-domain partner to time-domain139 correlation.140- cross-cutting/numerics/digital-filter-design: prefilter the channels141 before correlating so out-of-band content does not mask the peak.142- cross-cutting/numerics/least-squares-regression: regression fitting143 that consumes correlation-style statistics between variables.144- flight-test-operations/planning/flight-test-data-reduction: domain145 smoothing and time alignment of raw flight-test traces, distinct from146 this generic discrete correlation utility.147- flight-test-operations/flutter/ground-vibration-testing owns MAC-based148 modal correlation of test and analysis mode shapes, which is149 mode-shape matching, not time-series delay estimation.150151## Behavior contract (gate 3)152153Run the deterministic contract test (stdlib unittest, offline):154155 python3 scripts/test_cross_correlation_analysis.py156157The test covers the spec worked example anchors (lag range -6..4, raw158values list, peak lag -2, peak value 55, normalized peak 1.0, delay159estimate with delay_samples +2, zero-lag coefficient 10/14),160autocorrelation evenness with the 14/8/3 values, biased and unbiased161scaling (0.75 and 1.0 at lag +1 on four ones), the identical-signal162peak at lag 0, cross-correlation reversal symmetry, the Cauchy-Schwarz163peak identity, and ValueError rejection of empty, non-finite,164non-numeric, unknown-mode and zero-energy inputs. Runs in well under a165second.166167## Compliance168169- Standards referenced, not reproduced: NACA TR-824 anchors the170 numerics-pack public-domain reference set; discrete cross-correlation171 is standard signal-analysis methodology (Bendat and Piersol style172 summary), paraphrase-only per standards-map.yaml.173- compliance: STANDARDS-REF, gated: false.