Main Content

Measure Signal Similarities

R2026b

This example shows how to measure signal similarities using data from audio recordings, vibration sensors, and temperature monitors. You will learn how to:

  • Compare signals with different lengths or sample rates.

  • Determine if a signal is present in a measurement.

  • Measure and correct delays between signals.

  • Compare the frequency content of two signals.

  • Detect periodicities within a signal.

Resample and Compare Audio Signals

Suppose you want to identify a song by comparing it against a database of known recordings. To identify the song, you can compare it against the stored templates and find the best match. For this example, load a database of audio signals. Plot two reference signals and the signal under test to visually inspect the signals side by side.

load relatedsig

tiledlayout("vertical")
ax1 = nexttile;
plot((0:numel(T1)-1)/Fs1,T1,"k")
ylabel("Template 1")
grid on
ax2 = nexttile;
plot((0:numel(T2)-1)/Fs2,T2,"r")
ylabel("Template 2")
grid on
ax3 = nexttile;
plot((0:numel(S)-1)/Fs,S)
ylabel("Signal")
grid on
xlabel("Time (s)")
linkaxes([ax1 ax2 ax3],"x")
axis([0 1.61 -4 4])

Figure contains 3 axes objects. Axes object 1 with ylabel Template 1 contains an object of type line. Axes object 2 with ylabel Template 2 contains an object of type line. Axes object 3 with xlabel Time (s), ylabel Signal contains an object of type line.

At first glance, the signal under test does not appear to match either template. However, this apparent mismatch is misleading. The signals have different lengths and sample rates, which distorts the visual comparison. Also, audio in databases is commonly stored at a low sample rate to occupy less memory. List the sample rates of the audio signals from the database.

[Fs1 Fs2 Fs]
ans = 1×3

        4096        4096        8192

You can cross-correlate signals with different lengths, but they must have identical sample rates. To equalize the sample rates, use the resample function to resample the signal with the lower sample rate. The resample function applies an anti-aliasing (lowpass) FIR filter to the signal during the resampling process.

[P1,Q1] = rat(Fs/Fs1);          % Rational fraction approximation
[P2,Q2] = rat(Fs/Fs2);          % Rational fraction approximation
T1 = resample(T1,P1,Q1);        % Change sample rate by rational factor
T2 = resample(T2,P2,Q2);        % Change sample rate by rational factor

Identify Signal Using Cross-Correlation

Cross-correlate signal S to templates T1 and T2 using the xcorr function to determine if there is a match.

[C1,lag1] = xcorr(T1,S);        
[C2,lag2] = xcorr(T2,S);        

tiledlayout("vertical")
ax1 = nexttile;
plot(lag1/Fs,C1,"k")
grid on
title("Cross-Correlation Between Template 1 and Signal")
ax2 = nexttile;
plot(lag2/Fs,C2,"r")
grid on
title("Cross-Correlation Between Template 2 and Signal")
xlabel("Time(s)") 
axis([ax1 ax2],[-1.5 1.5 -700 700])

Figure contains 2 axes objects. Axes object 1 with title Cross-Correlation Between Template 1 and Signal contains an object of type line. Axes object 2 with title Cross-Correlation Between Template 2 and Signal, xlabel Time(s) contains an object of type line.

The cross-correlation with T1 shows no significant peak, indicating no match. The cross-correlation with T2 shows a strong peak, indicating that S is present in this template.

[~,I] = max(abs(C2));
SampleDiff = lag2(I)
SampleDiff = 
499
timeDiff = SampleDiff/Fs
timeDiff = 
0.0609

The peak of the cross-correlation implies that the signal is present in template T2 starting after 61 ms. In other words, template T2 leads signal S by 499 samples as indicated by SampleDiff. The next section shows how to use delay information to align signals.

Measure and Correct Delays in Vibration Sensor Data

Assume you are collecting data from three sensors recording vibrations caused by cars on both sides of a bridge. Assume that the sensors work at the same sample rate. Because the sensors are at different positions, signals from the three sensors arrive at different times. Plot the signals.

tiledlayout("vertical")
ax1 = nexttile;
plot(s1)
ylabel("s1")
grid on
ax2 = nexttile;
plot(s2,"k")
ylabel("s2")
grid on
ax3 = nexttile;
plot(s3,"r")
ylabel("s3")
grid on
xlabel("Samples")
linkaxes([ax1 ax2 ax3],"xy")

Figure contains 3 axes objects. Axes object 1 with ylabel s1 contains an object of type line. Axes object 2 with ylabel s2 contains an object of type line. Axes object 3 with xlabel Samples, ylabel s3 contains an object of type line.

Use the finddelay function to find the delay between two signals.

t21 = finddelay(s1,s2)
t21 = 
-350
t31 = finddelay(s1,s3)
t31 = 
150

t21 indicates that s2 is advanced with respect to s1 by 350 samples, and t31 indicates that s3 is delayed with respect to s1 by 150 samples. You can use these delay values to manually shift the signals and align them. You can also use the alignsignals function to align the signals by delaying the earliest signal.

[s1,~,~] = alignsignals(s1,s3);
[s2,~,~] = alignsignals(s2,s3);

tiledlayout("vertical")
ax1 = nexttile;
plot(s1)
grid on 
title("s1")
axis tight
ax2 = nexttile;
plot(s2)
grid on 
title("s2")
axis tight
ax3 = nexttile;
plot(s3)
grid on 
title("s3")
axis tight
linkaxes([ax1 ax2 ax3],"xy")

Figure contains 3 axes objects. Axes object 1 with title s1 contains an object of type line. Axes object 2 with title s2 contains an object of type line. Axes object 3 with title s3 contains an object of type line.

The three signals are now aligned in time and you can compare them directly.

Compare Frequency Content of Audio Signals Using Spectral Coherence

The previous sections compared signals in the time domain. You can also compare the frequency content of signals by computing the power spectra of two signals to see which frequencies are present in each signal. Plot two audio signals, sig1 and sig2, and their power spectra.

Fs = FsSig;

[P1,f1] = periodogram(sig1,[],[],Fs,"power");
[P2,f2] = periodogram(sig2,[],[],Fs,"power");

tiledlayout
t = (0:numel(sig1)-1)/Fs;
nexttile(1)
plot(t,sig1,"k")
ylabel("s1")
grid on
title("Time Series")
nexttile(3)
plot(t,sig2)
ylabel("s2")
grid on
xlabel("Time (s)")
nexttile(2)
plot(f1,P1,"k")
ylabel("P1")
grid on
axis tight
title("Power Spectrum")
nexttile(4)
plot(f2,P2)
ylabel("P2")
grid on
axis tight
xlabel("Frequency (Hz)")

Figure contains 4 axes objects. Axes object 1 with title Time Series, ylabel s1 contains an object of type line. Axes object 2 with xlabel Time (s), ylabel s2 contains an object of type line. Axes object 3 with title Power Spectrum, ylabel P1 contains an object of type line. Axes object 4 with xlabel Frequency (Hz), ylabel P2 contains an object of type line.

The power spectra show which frequencies are present in each signal individually, but not whether those components are related across the two signals. Spectral coherence enables you to identify frequency-domain correlation between signals. Use the mscohere function to compute spectral coherence. Values close to 1 indicate correlated components, values close to 0 indicate uncorrelated components. To further characterize the relationship, use the cpsd function to compute the cross-spectrum phase. Cross-spectrum phase estimates the relative phase lag between correlated components.

[Cxy,f] = mscohere(sig1,sig2,[],[],[],Fs);
Pxy = cpsd(sig1,sig2,[],[],[],Fs);
phase = -angle(Pxy)/pi*180;
[pks,locs] = findpeaks(Cxy,MinPeakHeight=0.75);

tiledlayout("vertical")
nexttile
plot(f,Cxy)
title("Coherence Estimate")
grid on
hgca = gca;
hgca.XTick = f(locs);
hgca.YTick = 0.75;
axis([0 200 0 1])
nexttile
plot(f,phase)
title("Cross-Spectrum Phase (deg)")
grid on
hgca = gca;
hgca.XTick = f(locs); 
hgca.YTick = round(phase(locs));
xlabel("Frequency (Hz)")
axis([0 200 -180 180])

Figure contains 2 axes objects. Axes object 1 with title Coherence Estimate contains an object of type line. Axes object 2 with title Cross-Spectrum Phase (deg), xlabel Frequency (Hz) contains an object of type line.

The coherence estimate confirms that sig1 and sig2 have two correlated components around 35 Hz and 165 Hz. The phase lag at 35 Hz is close to –90 degrees, and the phase lag at 165 Hz is close to –60 degrees.

Detect Periodicities in Temperature Data

The previous sections compared two or more signals to each other. You can also compare a signal to itself to detect repeating patterns. Consider a set of temperature measurements taken every 30 minutes for about 16.5 weeks in an office building during winter.

load officetemp.mat  

Fs = 1/(60*30); % Sample rate is 1 sample every 30 minutes
days = (0:length(temp)-1)/(Fs*60*60*24); 

figure
plot(days,temp)
title("Temperature Data")
xlabel("Time (days)")
ylabel("Temperature (Fahrenheit)")
grid on

Figure contains an axes object. The axes object with title Temperature Data, xlabel Time (days), ylabel Temperature (Fahrenheit) contains an object of type line.

Because the temperatures in this data set hover in the low 70s, the large absolute values can dominate correlation analysis and obscure the small periodic fluctuations of interest. Removing the mean isolates these fluctuations. The xcov function removes the mean of the signal before computing the cross-correlation and returns the cross-covariance. Limit the maximum lag to 50% of the signal to get a good estimate of the cross-covariance.

maxlags = numel(temp)*0.5;
[xc,lag] = xcov(temp,maxlags);         

[~,df] = findpeaks(xc,MinPeakDistance=5*2*24);
[~,mf] = findpeaks(xc);

figure
plot(lag/(2*24),xc,"k", ...
     lag(df)/(2*24),xc(df),"kv",MarkerFaceColor="r")
grid on
xlim([-15 15])
xlabel("Time (days)")
title("Auto-Covariance")

Figure contains an axes object. The axes object with title Auto-Covariance, xlabel Time (days) contains 2 objects of type line. One or more of the lines displays its values using only markers

Observe dominant and minor fluctuations in the auto-covariance. Dominant and minor peaks appear equidistant. To verify if the peaks are equidistant, compute and plot the difference between the locations of subsequent peaks.

cycle1 = diff(df)/(2*24);
cycle2 = diff(mf)/(2*24);

tiledlayout
nexttile
plot(cycle1)
ylabel("Days")
grid on
title("Dominant Peak Distance")
nexttile
plot(cycle2,"r")
ylabel("Days")
grid on
title("Minor Peak Distance")

Figure contains 2 axes objects. Axes object 1 with title Dominant Peak Distance, ylabel Days contains an object of type line. Axes object 2 with title Minor Peak Distance, ylabel Days contains an object of type line.

mean(cycle1)
ans = 
7
mean(cycle2)
ans = 
1

The dominant peaks are spaced seven days apart, reflecting weekly cyclic behavior of temperatures dropping during weekends and rising during weekdays. The minor peaks are spaced one day apart, reflecting daily cyclic behavior of temperatures dropping at night and rising during the day. Both patterns are consistent with a temperature-controlled building on a seven-day calendar.

See Also

| | | | | |