getting the delay/lag of Ultrasonic pulse velocity matlab - matlab

I am currently doing a thesis that needs Ultrasonic pulse velocity(UPV). UPV can easily be attained via the machines but the data we acquired didn't have UPV so we are tasked to get it manually.
Essentially in the data we have 2 channels, one for the transmitter transducer and another for a receiver transducer.
We need to get the time from wave from the transmitter is emitted and the time it arrives to the receiver.
Using matlab, I've tried finddelay and xcorr but doesnt quite get the right result.
Here is a picture of the points I would want to get. The plot is of the transmitter(blue) and receiver(red)
So I am trying to find the two points in the picture but with the aid of matlab. The two would determine the time and further the UPV.
I am relatively a new MATLAB user so your help would be of great assistance.
Here is the code I have tried
[cc, lags] = xcorr(signal1,signal2);
d2 = -(lags(cc == max(cc))) / Fs;
#xenoclast hi there! so far the code i used are these.
close all
clc
Fs = input('input Fs = ');
T = 1/Fs;
L = input('input L = ');
t = (0:L-1)*T;
time = transpose(t);
i = input('input number of steploads = ');
% construct test sequences
%dataupv is the signal1 & datathesis is the signal2
for m=1:i
y1 = (dataupv(:,m) - mean(dataupv(:,m))) / std(dataupv(:,m));
y2 = (datathesis(:,m) - mean(datathesis(:,m))) / std(datathesis(:,m));
offset = 166;
tt = time;
% correlate the two sequences
[cc, lags] = xcorr(y2, y1,);
% find the in4dex of the maximum
[maxval, maxI] = max(cc);
[minval, minI] = min(cc);
% use that index to obtain the lag in samples
lagsamples(m,1) = lags(maxI);
lagsamples2(m,1) = lags(minI);
% plot again without timebase to verify visually
end
the resulting value is off by 70 samples compared to when i visually inspect the waves. the lag resulted in 244 but visually it should be 176 here are the data(there are 19 sets of data but i only used the 1st column) https://www.dropbox.com/s/ng5uq8f7oyap0tq/datatrans-dec-2014.xlsx?dl=0 https://www.dropbox.com/s/1x7en0x7elnbg42/datarec-dec-2014.xlsx?dl=0

Your example code doesn't specify Fs so I don't know for sure but I'm guessing that it's an issue of sample rate(s). All the examples of cross correlation start out by constructing test sequences according to a specific sample rate that they usually call Fs, not to be confused with the frequency of the test tone, which you may see called Fc.
If you construct the test signals in terms of Fs and Fc then this works fine but when you get real data from an instrument they often give you the data and the timebase as two vectors, so you have to work out the sample rate from the timebase. This may not be the same as the operating frequency or the components of the signal, so confusion is easy here.
But the sample rate is only required in the second part of the problem, where you work out the offset in time. First you have to get the offset in samples and that's a lot easier to verify.
Your example will give you the offset in samples if you remove the '/ Fs' term and you can verify it by plotting the two signals without a timebase and inspecting the sample positions.
I'm sure you've looked at dozens of examples but here's one that attempts to not confuse the issue by tying it to sample rates - you'll note that nowhere is it specified what the 'sample rate' is, it's just tied to samples (although if you treat the 5 in the y1 definition as a frequency in Hz then you will be able to infer one).
% construct test sequences
offset = 23;
tt = 0:0.01:1;
y1 = sin(2*pi*5*tt);
y2 = 0.8 * [zeros(1, offset) y1];
figure(1); clf; hold on
plot(tt, y1)
plot(tt, y2(1:numel(tt)), 'r')
% correlate the two sequences
[cc, lags] = xcorr(y2, y1);
figure(2); clf;
plot(cc)
% find the index of the maximum
[maxval, maxI] = max(cc);
% use that index to obtain the lag in samples
lagsamples = lags(maxI);
% plot again without timebase to verify visually
figure(3); clf; hold on
plot(y1)
plot(y2, 'r')
plot([offset offset], [-1 1], 'k:')
Once you've got the offset in samples you can probably deduce the required conversion factor, but if you have timebase data from the instrument then the inverse of the diff of any two consecutive entries will give it you.
UPDATE
When you correlate the two signals you can visualise it as overlaying them and summing the product of corresponding elements. This gives you a single value. Then you move one signal by a sample and do it again. Continue until you have done it at every possible arrangement of the two signals.
The value obtained at each step is the correlation, but the 'lag' is computed starting with one signal all the way over to the left and the other overlapping by only one sample. You slide the second signal all the way over until it's only overlapping the other end by a sample. Hence the number of values returned by the correlation is related to the length of both the original signals, and relating any given point in the correlation output, such as the max value, to the arrangement of the two signals that produced it requires you to do a calculation involving those lengths. The xcorr function makes this easier by outputting the lags variable, which tracks the alignment of the two signals. People may also talk about this as an offset so watch out for that.

Related

Store maximum frequency

I am working on a script which performs a FFT of given short audio file in a loop. I also want to store the peak frequency but I do not know how to do that.
The code looks similar to this:
n = ...
Frequencies = zeros(1,n); % Allocating memory for the peak frequencies
for k = 1:n
str(k)
textFileName = [num2str(k) '.m4a'];
[data,fs] = audioread(textFileName);
%...
% Fast Fourier transform and plotting part works ok
%...
[peaks,frequencies] = findpeaks(abs(cutP2),cutf,'MinPeakHeight',10e-3);
% Here starts the problem
maximum_Peak = max(peaks);
Frequencies(k) = ... % I need to store the frequency which is coupled
% with the maximum amplitude but I do not know how
end
close(figure(n)) %The loop opens one redundant blank plot, I could not
%find out any other way to close it
I do not want to store the amplitudes of peak frequencies, but frequencies of peak amplitudes. If you could help me with the redundant figure, I would be happy. I tried to implement an if statement but did not work.
max contains a second output which returns the index of the maximum value. Use this second value to stores the value of interest.
[maximum_Peak,I] = max(peaks); %Note I Use 'I' for index - personal habit
Frequencies(k) = frequencies(I);
Also, if your goal is only to find the max point findpeaks may be overkill and you could potentially use:
[maximum_Peak,I] = max(abs(cutP2));
%Might want to check that max is high enough
Frequencies(k) = cutf(I);
Note although the code is similar it is not the same and depends on what you want to do.
Finally, some unsolicited advice, your use of frequencies and Frequencies is a bit of a red flag. Generally differences based on capitalization are not a good idea. Consider renaming the latter to freq_of_max_amp

Any good ways to obtain zero local means in audio signals?

I have asked this question on DSP.SE before, but my question has got no attention. Maybe it was not so related to signal processing.
I needed to divide a discrete audio signal into segments to have some statistical processing and analysis on them. Therefore, segments with fixed local mean would be very helpful for my case. Length of segments are predefined, e.g. 512 samples.
I have tried several things. I do use reshape() function to divide audio signal into segments, and then calculate means of every segment as:
L = 512; % Length of segment
N = floor(length(audio(:,1))/L); % Number of segments
seg = reshape(audio(1:N*L,1), L, N); % Reshape into LxN sized matrix
x = mean(seg); % Calculate mean of each column
Subtracting x(k) from each seg(:,k) would make each local mean zero, yet it would distort audio signal a lot when segments are joined back.
So, since mean of hanning window is almost 0.5, substracting 2*x(k)*hann(L) from each seg(:,k) was the first thing I tried. But this time multiplying by 2 (to make the mean of hanning window be almost equal to 1) distorted the neighborhood of midpoints in each segments itself.
Then, I have used convolution by a smaller hanning window instead of multiplying directly, and subtracting these (as shown in figure below) from each seg(:,k).
This last step gives better results, yet it is still not very useful when segments are smaller. I have seen many amazing approaches here on this site for different problems. So I just wonder if there is any clever ways or existing methods to obtain zero local means which distorts an audio signal less. I read that, this property is useful in some decompositions such as EMD. So maybe I need such decompositions?
You can try to use a moving average filter:
x = cumsum(rand(15*512, 1)-0.5); % generate a random input signal
mean_filter = 1/512 * ones(1, 512); % generate a mean filter
mean = filtfilt(mean_filter, 1, x); % filtfilt is used instead of filter to obtain a symmetric moving average.
% plot the result
figure
subplot(2,1,1)
plot(x);
hold on
plot(mean);
subplot(2,1,2)
plot(x - mean);
You can tune the filter by changing the interval of the mean filter. Using a smaller interval, results in lower means inside each interval, but filters also more low frequencies out of your signal.

How do I align two signals in MATLAB [duplicate]

I want to get the offset in samples between two datasets in Matlab (getting them synced in time), a quite common issue. Therefore I use the cross correlation function xcorr or the cross covariance function xcov (both provide similar results in most cases for this purpose). With artificial data it works fine, but I struggle with "real" data, even though it should be pretty much the same. Matlab always says the offset would be zero. I'm using this simple piece of code:
[crossCorr] = xcov(b, c);
[~, peakIndex] = max(crossCorr())
offset = peakIndex - length(b)
I've posted a fully runable example m-file with a downsampled data excerpt on pastebin:
Code with data on pastebin
EDIT: The downsampled excerpt seems to be not fully suitable for evaluating the effect. Here's a much larger sample with the original frequency, pease use this one instead. Unfortunately it was too big for pastebin.
As the plot shows it should be no problem at all to get the offset via cross covariance. I also tried to scale the data nicer in order to avoid numerical problems, but that didn't change anything at all.
Would be great if someone could tell me my mistake.
There's nothing wrong with your method in principle, I used exactly the same approach successfully for temporally aligning different audio recordings of the same signal.
However, it appears that for your time series, correlation (or covariance) is simply not the right measure to compare shifted versions – possibly because they contain components of a time scale comparable to the total length. An alternative is to use residual variance, i.e. the variance of the difference between shifted versions. Here is a (not particularly elegant) implementation of this idea:
lags = -1000 : 1000;
v = nan(size(lags));
for i = 1 : numel(lags)
lag = lags(i);
if lag >= 0
v(i) = var(b(1 + lag : end) - c(1 : end - lag));
else
v(i) = var(b(1 : end + lag) - c(1 - lag : end));
end
end
[~, ind] = min(v);
minlag = lags(ind);
For your (longer) data set, this results in minlag = 169. Plotting residual variance over lags gives:
Your data has a minor peak around 5 and a major peak around 101.
If I knew something about my data then I could might window around an acceptable range of offsets as shown below.
Code for initial exploration:
figure; clc;
subplot(2,1,1)
plot(1:numel(b), b);
hold on
plot(1:numel(c), c, 'r');
legend('b','c')
subplot(2,1,2)
plot(crossCorr,'.b-')
hold on
plot(peakIndex,crossCorr(peakIndex),'or')
legend('crossCorr','peak')
Initial Image:
If you zoom into the first peak you can see that it is not only high around 5, but it is polynomial "enough" to allow sub-element offsets. That is convenient.
Image showing :
Here is what the curve-fitting tool gives as the analytic for a cubic:
Linear model Poly3:
f(x) = p1*x^3 + p2*x^2 + p3*x + p4
Coefficients (with 95% confidence bounds):
p1 = 8.515e-013 (8.214e-013, 8.816e-013)
p2 = -3.319e-011 (-3.369e-011, -3.269e-011)
p3 = 2.253e-010 (2.229e-010, 2.277e-010)
p4 = -4.226e-012 (-7.47e-012, -9.82e-013)
Goodness of fit:
SSE: 2.799e-024
R-square: 1
Adjusted R-square: 1
RMSE: 6.831e-013
You can note that the SSE fits to roundoff.
If you compute the root (near n=4) you use the following matlab code:
% Coefficients
p1 = 8.515e-013
p2 = -3.319e-011
p3 = 2.253e-010
p4 = -4.226e-012
% Linear model Poly3:
syms('x')
f = p1*x^3 + p2*x^2 + p3*x + p4
xz1=fzero(#(y) subs(diff(f),'x',y), 4)
and you get the analytic root at 4.01420240431444.
EDIT:
Hmmm. How about fitting a gaussian mixture model to the convolution? You sweep through a good range of component count, you do between 10 and 30 repeats, and you find which component count has the best/lowest BIC. So you fit a gmdistribution to the lower subplot of the first figure, then test the covariance at the means of the components in decreasing order.
I would try the offset at the means, and just look at sum squared error. I would then pick the offset that has the lowest error.
Procedure:
compute cross correlation
fit cross correlation to Gaussian Mixture model
sweep a reasonable range of components (start with 1-10)
use a reasonable number of repeats (10 to 30 depending on run-to-run variation)
compute Bayes Information Criterion (BIC) for each level, pick the lowest because it indicates a reasonable balance of error and parameter count
each component is going to have a mean, evaluate that mean as a candidate offset and compute sum-squared error (sse) when you offset like that.
pick the offset of the component that gives best SSE
Let me know how well that works.
If the two signals misalign by non-integer number of samples, e.g. 3.7 samples, then the xcorr method may find the max value at 4 samples, it won't be able to find the accurate time shift. In this case, you should try a method called "unified change detection". The web-link for the paper is:
[http://www.phmsociety.org/node/1404/]
Good Luck.

Given an array of data, extract possible frequencies with fft, how to?

I am fairly new to vibrations and using matalb fft.I am given a set of data (1D array) of length 15000 (not sure if this is relevant) and I am trying to figure out if there is any wave buried in this data at all. I was instructed to possibly use matlab fft. Is that the correct way to do it? what shall I expect to see? I am really not sure how to interpret any results I would get.
Please let me know what you all think.
Thanks and if more details are needed I will provide them.
Example:
% Df=[array is given to me and it is of size 15000];
% t=[time used for the above array, and if it is of the same size, also provided to me]
N_0= length(t);
fs_0=length(Dfxz);
Y_0=fft(Dfxz,N_0);
k_0=-N_0/2:N_0/2-1;
%Find the phase angle
p_0 = (angle(Y_0));
R_0 = norm(Y_0);
ff_0 = (0:length(Y_0)-1)'/length(Y_0)*100; % Frequency vector
FT_power1_0 = abs(Y_0);
plot(k_0*fs_0/N_0,fftshift(abs(Y_0)))
I only see 1 peek at frequency = 0 but I am sure that there are non zero frequencies, what am I doing wrong?
Thanks!
PS:I am not sure how to pick the sampling frequency either? any tips please (keep in mind that I do not know the original frequency)
Try my version. It looks to me like you have all the information you need to find frequency peaks in your data if they exist. If all you can see is a big peak at zero frequency, you probably have a massive DC offset that is drowning out all your other data. I have put a remedy in my code . .
x = randn(15000,1); %//This is the data you were given - I'll use noise for demonstration - replace x with your Df data
%//If youre getting a massive peak at zero that dwarfs everything else, you
%//probably have a large DC offset. Easily removed in the time domain using
%//the following ..
x = x-mean(x);
tAxis = linspace(3/15000,3,15000); %//You said you have this too - I'll make up something for demonstration - make sure you replace this with your t data
dt = diff(tAxis(1:2)); %//sample period from time axis
fs = 1/dt;%//sample rate from sample period
NFFT = numel(x); %//number of fft bins - change if you like
Y = abs(fft(x, NFFT)).^2; %power spectrum
%//Calculate frequency axis
df = fs/NFFT;
fAxis = 0:df:(fs-df);
%//Plot it all
figure; plot(fAxis(1:NFFT/2), Y(1:NFFT/2))
xlabel('Frequency in Hz')
ylabel('Power')
If that works out OK, you can go into more depth by checking out another FFT answer on stackoverflow.

the Length of signal in calculating FFT

I want to ask some questions related to the last question of mine so I don't want to post in another thread. My question contains a code, I therefore can't post it as a comment. So I have to edit my old question into a new one. Please take a look and help. Thank you.
I'm new to FFT and DSP and I want to ask you some questions about calculating FFT in Matlab. The following code is from Matlab help, I just removed the noise.
Can I choose the length of signal L different from NFFT?
I'm not sure if I used window correctly. But when I use window (hanning in the following code), I can't get the exact values of amplitudes?
When L and NFFT get different values, then the values of amplitudes were different too. How can I get the exact value of amplitude of input signal? (in the following code, I used a already known signal to check if the code work correctly. But in case, I got the signal from a sensor and I dont know ahead its amplitude, how can I check?)
I thank you very much and look forward to hearing from you :)
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sample time
L = 512; % Length of signal
NFFT=1024; % number of fft points
t = (0:L-1)*T; % Time vector
x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t); input signal
X = fft(hann(L).*x', NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
plot(f,2*abs(X(1:NFFT/2+1))) % Plot single-sided amplitude spectrum.
L is the number of samples in your input signal. If L < NFFT then the difference is zero-padded.
I would recommend you do some reading on the effect of zero-padding on FFTs. Typically it is best to use L = NFFT as this will give you the best representation of your data.
An excepted answer on the use of zero-padding and FFTs is given here:
https://dsp.stackexchange.com/questions/741/why-should-i-zero-pad-a-signal-before-taking-the-fourier-transform
In your experiment you are seeing different amplitudes because you will have different amount of spectral leakage with each different L.
You need to apply a window function prior to the FFT to get consistent results with frequency components that have non-integral number of periods within your sampling window.
You might also want to consider using periodogram instead of using the FFT directly - it takes care of window functions and a lot of the other housekeeping for you.