Wave interference: adding two waves with opposite phases + using FFT - MATLAB problems - matlab

I have two main questions, but starting from the beginning:
I wanted to know how FFT (fast Fourier transform) works on real examples. I created two sinusoidal waves (for example Wave1 = 5 Hz and Wave2 = 15 Hz amplitude), then I added those two and made FFT from third "Wave3". It looks OK - I saw my "peaks" around 5 and 15 Hz.
Blue = 5 Hz, Red = 15 Hz, Yellow = Blue + Red. FFT from "Yellow" Wave, looks good:
OK, and then I have changed the data. Now I have two waves with identical amplitudes but opposite phases. If I add them, their amplitude is 0 - and that seems correct to me.
Two waves with opposite phases. Yellow - Wave1+Wave2 + Strange FFT of the yellow wave:
And now is the part that I don't understand at all. Here are my questions:
1) Even if I see on the picture that this third Yellow wave has an amplitude equal to 0, it's not like that in data tables. After adding two main waves (and they have opposite data!) I get the strange result.
Example: 5 first points in the data
Wave 1:
0,0627905195293128
0,125333233564304
0,187381314585724
0,248689887164855
0,309016994374947
Wave 2:
-0,0627905195293134
-0,125333233564304
-0,187381314585724
-0,248689887164855
-0,309016994374947
Wave 3 (Sum) :
-5,68989300120393e-16
-1,11022302462516e-16
-1,11022302462516e-16
3,05311331771918e-16
-1,11022302462516e-16
Why the sum of these waves is not equal 0, as it is shown in the picture? Why FFT looks so strange? Is there even a possibility that FFT will show us the real amplitudes of two identical waves with opposite phases? I thought it will not, but what's the truth?
Here is my MATLAB code:
THREE WAVES:
D = 1; % 1 second
S = 1000; % sampling rate
P = 0.5; % phase
T = 1/S; % sampling period
t = [T:T:D]; % time
myphi=2*pi*P;
myphi2=2*pi*1;
syn3 = sin(2*10*t*pi+myphi); % first wave
syn2 = sin(2*10*t*pi+myphi2); % second wave
sinmax=syn2+syn3; % yellow wave
figure; plot(t,syn3,t,syn2,t,sinmax,'LineWidth',2); grid on;
xlabel('Time (seconds)');
ylabel('Amplitude');
FFT CODE:
L = length(sinmax);
myFFT = fft(sinmax,S);
myFFT=myFFT/L; %scale the output to 1
freq = S/2*linspace(0,1,S/2);
figure; stem(freq,abs(myFFT(1:length(freq))));
xlabel('Frequency (Hz)');
ylabel('Amplitude');
Thank you very much in advance...
Mary

First things first, your calculations are correct.
Because the two graphs are auto-resized, it is easy to make a mistake interpreting them but the amplitudes are way smaller on the 2nd one than the 1st (10e-16 vs. 10e1, respectively).
On the 2nd graph, the one which leaves you puzzled, you are just victim of numerical errors : these numbers can be interpreted as 0.
From there, you have two simple solutions :
you are fine with the results you have, and you can just tweak the figures to display results on the same scale to avoid any misleading interpretation
you would like to have "proper zero values" instead of "small ones"
1) Set a limit for the Y-axis
You can just add something like this line (it is just an example - change it to meet your needs) when plotting your figures :
ymax = max(abs(my_fft));
ymin = - ymax;
ylim([ymin ymax])
2) Set a limit for filtering numerical errors
Like in a lot of numerical methods algorithms, you might want to consider as 0 values which are in between 0 and small interval, often called epsilon :
abs_fft = abs(my_fft);
epsilon = 10e-12 % your threshold
abs_fft(abs_fft < epsilon) = 0;
You might want to check out eps which is a built-in Matlab variable, meant for this kind of cases.

Related

What do the x axis really represent in this fourier transform and how to convert it?

This is a question for a hand in in one of my courses, just want to state that.
What I am trying to do is sampling a square wave, take the fourier transform (fft) and plot the answer to graph. This is how I have achieved this:
Fs = 100;
Ts = 1/Fs;
N = 8192;
Tmax = (N - 1)*Ts;
t = 0:Ts:Tmax;
x = square(t);
X = fft(x,N);
plot(t, abs(X))
What it return is a graph that looks like this
This looks almost as inspected, but since I do not know what to expect with the square wave I also try to do it with a $\sin(2*t)$ wave. If I take the fourier transform on this, I should get 2 spiks, each at 2 and -2 (right side). But what I get is something like this
(Note! I have zoomed in on the left hand side of the graph to show that the spike is not at 2) As you can see the spike is not where it is supposed to be. I can than conclude that probably the 1 graph is not eater how it should be.
Is it something wrong with my x axis representation? And if so, how do I convert the x axis into the frequency plane?
The frequencies resulting from the FFT range from 0 to the sampling frequency. Specifically, the horizontal axis of the FFT corresponds to frequencies 0, fs/N, 2*fs/N, ... ,(N-1)*fs/N, where fs is the sample frequency and N is the FFT size.
So, you should modify the horizontal axis in the plot to the following, where N is numel(t) and fs is computed as 1/(t(2)-t(1)):
freq_axis = (0:numel(t)-1)/numel(t)/(t(2)-t(1));
plot(freq_axis, abs(X))
You may also want to apply fftshift to observe frequencies from -fs/2 to fs/2, instead of from 0 to fs. In that case:
freq_axis = (-numel(t)/2:numel(t)/2-1)/numel(t)/(t(2)-t(1));
plot(freq_axis, fftshift(abs(X)))
As a check, with your example x = sin(2*t) the second plot gives:
Comparing your sin(2*t) with the generic expression sin(2*pi*f*t), the frequency f of that sinusoid is seen to be 1/pi = 0.3183, in agreement with the figure.

period of sawtooth from measurements

I have a series of 2D measurements (time on x-axis) that plot to a non-smooth (but pretty good) sawtooth wave. In an ideal world the data points would form a perfect sawtooth wave (with partial amplitude data points at either end). Is there a way of calculating the (average) period of the wave, using OCTAVE/MATLAB? I tried using the formula for a sawtooth from Wikipedia (Sawtooth_wave):
P = mean(time.*pi./acot(tan(y./4))), -pi < y < +pi
also tried:
P = mean(abs(time.*pi./acot(tan(y./4))))
but it didn't work, or at least it gave me an answer I know is out.
An example of the plotted data:
I've also tried the following method - should work - but it's NOT giving me what I know is close to the right answer. Probably something simple and wrong with my code. What?
slopes = diff(y)./diff(x); % form vector of slopes for each two adjacent points
for n = 1:length(diff(y)) % delete slope of any two points that form the 'cliff'
if abs(diff(y(n,1))) > pi
slopes(n,:) = [];
end
end
P = median((2*pi)./slopes); % Amplitude is 2*pi
Old post, but thought I'd offer my two-cent's worth. I think there are two reasonable ways to do this:
Perform a Fourier transform and calculate the fundamental
Do a curve-fitting of the phase, period, amplitude, and offset to an ideal square-wave.
Given curve-fitting will likely be difficult because of discontinuities in saw-wave, so I'd recommend Fourier transform. Self-contained example below:
f_s = 10; # Sampling freq. in Hz
record_length = 1000; # length of recording in sec.
% Create noisy saw-tooth wave, with known period and phase
saw_period = 50;
saw_phase = 10;
t = (1/f_s):(1/f_s):record_length;
saw_function = #(t) mod((t-saw_phase)*(2*pi/saw_period), 2*pi) - pi;
noise_lvl = 2.0;
saw_wave = saw_function(t) + noise_lvl*randn(size(t));
num_tsteps = length(t);
% Plot time-series data
figure();
plot(t, saw_wave, '*r', t, saw_function(t));
xlabel('Time [s]');
ylabel('Measurement');
legend('measurements', 'ideal');
% Perform fast-Fourier transform (and plot it)
dft = fft(saw_wave);
freq = 0:(f_s/length(saw_wave)):(f_s/2);
dft = dft(1:(length(saw_wave)/2+1));
figure();
plot(freq, abs(dft));
xlabel('Freqency [Hz]');
ylabel('FFT of Measurement');
% Estimate fundamental frequency:
[~, idx] = max(abs(dft));
peak_f = abs(freq(idx));
peak_period = 1/peak_f;
disp(strcat('Estimated period [s]: ', num2str(peak_period)))
Which outputs a couple of graphs, and also the estimated period of the saw-tooth wave. You can play around with the amount of noise and see that it correctly gets a period of 50 seconds till very high levels of noise.
Estimated period [s]: 50

Remove noise on a wav file

I'm working on a small code to learn signal processing on Matlab. I have got a .wav sound with some noise and I just want to remove the noise. I tried the code under but noise is not removed correctly. My idea is to do a cut band filter to remove the different noise components on the fft. After a lot of researches, I don't understant where is my problem. Here my code :
clear all;
clc;
close all;
% We load and plot the signal
[sig,Fs] = audioread('11.wav');
N = length(sig);
figure,plot(sig); title 'signal'
% FFT of the signal
fft_sig = abs(fft(sig));
% Normalisation of the frequencies for the plot
k = 0 : length(sig) - 1;
f = k*Fs/length(sig);
plot(f,fft_sig);
% Loop with 2 elements because there are 2 amplitudes (not sure about
% this)
for i = 1:2
% I put noise components in an array
[y(i),x(i)] = max(fft_sig);
% Normalisation of the frequencies to eliminate
f(i) = x(i) * (Fs/N);
% Cut band filter with elimination of f from f-20 to f+20 Hz
b = fir1(2 , 2 * [f(i)-20 f(i)+20] / Fs, 'stop')
sig = filter(b,1,sig);
figure,plot(abs(fft(sig)));title 'fft of the signal'
end
Here the images I got, the fft plot is exactly the same before and after applying the filter, there is a modification only on the x axis :
The sampling frequency is Fs = 22050.
Thanks in advance for your help, I hope I'm clear enough in my description
Since you haven't explicitly said so, the code you provided essentially defines your noise as narrowband interference at two frequencies (even though that interference may look less 'noisy').
The first thing to notice is that the value x(i) obtained from max(fft_sig) corresponds to the 1-based index of the located maximum. It won't make a huge difference for large N, but it may have an impact for smaller values especially when trying to design a very narrow notch filter. The corresponding frequency would then be:
freq = (x(i)-1) * (Fs/N);
Also if you are going to iteratively attenuate frequency components you would need to update fft_sig which you use to pick the frequency components to be attenuated (otherwise you will always pick the same component). The simplest would be to recompute fft_sig from the filtered sig:
sig = filter(b,1,sig);
fft_sig = abs(fft(sig));
Finally, since you are trying to attenuate a very narrow frequency range, you may find that you need to increase the FIR filter order by a few orders of magnitudes to get a good attenuation at the desired frequency without attenuating everything else. As was pointed out in comments such a narrow notch filter can often be better implemented using an IIR filter.
The updated code would then look somewhat like:
% Loop with 2 elements because there are 2 amplitudes
for i = 1:2
% I put noise components in an array
% Limit peak search to the lower-half of the spectrum since the
% upper half is symmetric
[y(i),x(i)] = max(fft_sig(1:floor(N/2)+1));
% Normalisation of the frequencies to eliminate
freq = (x(i)-1) * (Fs/N);
% Cut band filter with elimination of f from f-20 to f+20 Hz
b = fir1(2000 , 2 * [freq-20 freq+20] / Fs, 'stop')
sig = filter(b,1,sig);
fft_sig = abs(fft(sig));
%figure;plot(f, abs(fft_sig));title 'fft of the signal'
figure;plot(f, 20*log10(abs(fft_sig)));title 'fft of the signal'
end
As far as the last FFT plot showing a different x-axis, it is simply because you omitted to provide an x-axis variable (in occurrence f used in the previous plot), so the plot shows the array index (instead of frequencies) as the x-axis.

Find the height and length of waves in noisy data

My goal is to find the maximum values of wave heights and wave lengths.
dwcL01 though dwcL10 is arrays of <3001x2 double> with output from a numerical wave model.
Part of my script:
%% Plotting results from SWASH
% Examination of phase velocity on deep water with different number of layers
% Wave height 3 meters, wave peroid 8 sec on a depth of 30 meters
clear all; close all; clc;
T=8;
L0=1.56*T^2;
%% Loading results tabels.
load dwcL01.tbl; load dwcL02.tbl; load dwcL03.tbl; load dwcL04.tbl;
load dwcL05.tbl; load dwcL06.tbl; load dwcL07.tbl; load dwcL08.tbl;
load dwcL09.tbl; load dwcL10.tbl;
M(:,:,1) = dwcL01; M(:,:,2) = dwcL02; M(:,:,3) = dwcL03; M(:,:,4) = dwcL04;
M(:,:,5) = dwcL05; M(:,:,6) = dwcL06; M(:,:,7) = dwcL07; M(:,:,8) = dwcL08;
M(:,:,9) = dwcL09; M(:,:,10) = dwcL10;
%% Finding position of wave crest using diff and sign.
for i=1:10
Tp(:,1,i) = diff(sign(diff([M(1,2,i);M(:,2,i)]))) < 0;
Wc(:,:,i) = M(Tp,:,i);
L(:,i) = diff(Wc(:,1,i))
end
This works fine for finding the maximum values, if the data is "smooth". The following image shows a section of my data. I get all peaks, when I only need the one around x = 40. How do I filter so I only get the "real" wave crests. The solution needs to be general so that it still works if I change the domain size, wave height or wave period.
If you're basically trying to fit this curve of data to a sine wave, have you considered performing Fourier analysis (FFT in Matlab), then checking the magnitude of that fundamental frequency? The frequency will tell you the wave spacing, and the magnitude the height, and when used over multiple periods will find an average.
See the Matlab help page for an example of the usage
but the basic gist is:
y = [...] %vector of wave data points
N=length(y); %Make sure this is an even number
Y = fft(y); %Convert into frequency domain
figure;
plot(y(1:N)); %Plot original wave data
figure;
plot(abs(Y(1:N/2))./N); %Plot only the lower half of frequencies to hide aliasing
I have one more solution that might work for you. It involves computing the 2nd-order derivative using a 5-point central difference instead of the 2-point finite differences. When using diff twice you are performing two first-order derivatives consecutively (finite 2-point differences) which are very susceptible to noise/oscillations. The advantage of using a higher-order approximation is that the neighboring points help filter out the small oscillations, and this may work for your case.
Let f(:) = squeeze(M(:,2,i)) be the array of data points, and h is the uniform spacing distance between the points:
%Better approximation of the 2nd derivative using neighboring points:
for j=3:length(f)-2
Tp(j,i) = (-f(j-2) + 16*f(j-1) - 30*f(j) + 16*f(j+1) - f(j+2))/(12*h^2);
end
Note that since this 2nd-order derivative requires the 2 neighboring points to the left and right, that the range of the loop must start at the 3rd index and end 2 short of the array length.

MATLAB FFT xaxis limits messing up and fftshift

This is the first time I'm using the fft function and I'm trying to plot the frequency spectrum of a simple cosine function:
f = cos(2*pi*300*t)
The sampling rate is 220500. I'm plotting one second of the function f.
Here is my attempt:
time = 1;
freq = 220500;
t = 0 : 1/freq : 1 - 1/freq;
N = length(t);
df = freq/(N*time);
F = fftshift(fft(cos(2*pi*300*t))/N);
faxis = -N/2 / time : df : (N/2-1) / time;
plot(faxis, real(F));
grid on;
xlim([-500, 500]);
Why do I get odd results when I increase the frequency to 900Hz? These odd results can be fixed by increasing the x-axis limits from, say, 500Hz to 1000Hz. Also, is this the correct approach? I noticed many other people didn't use fftshift(X) (but I think they only did a single sided spectrum analysis).
Thank you.
Here is my response as promised.
The first or your questions related to why you "get odd results when you increase the frequency to 900 Hz" is related to the Matlab's plot rescaling functionality as described by #Castilho. When you change the range of the x-axis, Matlab will try to be helpful and rescale the y-axis. If the peaks lie outside of your specified range, matlab will zoom in on the small numerical errors generated in the process. You can remedy this with the 'ylim' command if it bothers you.
However, your second, more open question "is this the correct approach?" requires a deeper discussion. Allow me to tell you how I would go about making a more flexible solution to achieve your goal of plotting a cosine wave.
You begin with the following:
time = 1;
freq = 220500;
This raises an alarm in my head immediately. Looking at the rest of the post, you appear to be interested in frequencies in the sub-kHz range. If that is the case, then this sampling rate is excessive as the Nyquist limit (sr/2) for this rate is above 100 kHz. I'm guessing you meant to use the common audio sampling rate of 22050 Hz (but I could be wrong here)?
Either way, your analysis works out numerically OK in the end. However, you are not helping yourself to understand how the FFT can be used most effectively for analysis in real-world situations.
Allow me to post how I would do this. The following script does almost exactly what your script does, but opens some potential on which we can build . .
%// These are the user parameters
durT = 1;
fs = 22050;
NFFT = durT*fs;
sigFreq = 300;
%//Calculate time axis
dt = 1/fs;
tAxis = 0:dt:(durT-dt);
%//Calculate frequency axis
df = fs/NFFT;
fAxis = 0:df:(fs-df);
%//Calculate time domain signal and convert to frequency domain
x = cos( 2*pi*sigFreq*tAxis );
F = abs( fft(x, NFFT) / NFFT );
subplot(2,1,1);
plot( fAxis, 2*F )
xlim([0 2*sigFreq])
title('single sided spectrum')
subplot(2,1,2);
plot( fAxis-fs/2, fftshift(F) )
xlim([-2*sigFreq 2*sigFreq])
title('whole fft-shifted spectrum')
You calculate a time axis and calculate your number of FFT points from the length of the time axis. This is very odd. The problem with this approach, is that the frequency resolution of the fft changes as you change the duration of your input signal, because N is dependent on your "time" variable. The matlab fft command will use an FFT size that matches the size of the input signal.
In my example, I calculate the frequency axis directly from the NFFT. This is somewhat irrelevant in the context of the above example, as I set the NFFT to equal the number of samples in the signal. However, using this format helps to demystify your thinking and it becomes very important in my next example.
** SIDE NOTE: You use real(F) in your example. Unless you have a very good reason to only be extracting the real part of the FFT result, then it is much more common to extract the magnitude of the FFT using abs(F). This is the equivalent of sqrt(real(F).^2 + imag(F).^2).**
Most of the time you will want to use a shorter NFFT. This might be because you are perhaps running the analysis in a real time system, or because you want to average the result of many FFTs together to get an idea of the average spectrum for a time varying signal, or because you want to compare spectra of signals that have different duration without wasting information. Just using the fft command with a value of NFFT < the number of elements in your signal will result in an fft calculated from the last NFFT points of the signal. This is a bit wasteful.
The following example is much more relevant to useful application. It shows how you would split a signal into blocks and then process each block and average the result:
%//These are the user parameters
durT = 1;
fs = 22050;
NFFT = 2048;
sigFreq = 300;
%//Calculate time axis
dt = 1/fs;
tAxis = dt:dt:(durT-dt);
%//Calculate frequency axis
df = fs/NFFT;
fAxis = 0:df:(fs-df);
%//Calculate time domain signal
x = cos( 2*pi*sigFreq*tAxis );
%//Buffer it and window
win = hamming(NFFT);%//chose window type based on your application
x = buffer(x, NFFT, NFFT/2); %// 50% overlap between frames in this instance
x = x(:, 2:end-1); %//optional step to remove zero padded frames
x = ( x' * diag(win) )'; %//efficiently window each frame using matrix algebra
%// Calculate mean FFT
F = abs( fft(x, NFFT) / sum(win) );
F = mean(F,2);
subplot(2,1,1);
plot( fAxis, 2*F )
xlim([0 2*sigFreq])
title('single sided spectrum')
subplot(2,1,2);
plot( fAxis-fs/2, fftshift(F) )
xlim([-2*sigFreq 2*sigFreq])
title('whole fft-shifted spectrum')
I use a hamming window in the above example. The window that you choose should suit the application http://en.wikipedia.org/wiki/Window_function
The overlap amount that you choose will depend somewhat on the type of window you use. In the above example, the Hamming window weights the samples in each buffer towards zero away from the centre of each frame. In order to use all of the information in the input signal, it is important to use some overlap. However, if you just use a plain rectangular window, the overlap becomes pointless as all samples are weighted equally. The more overlap you use, the more processing is required to calculate the mean spectrum.
Hope this helps your understanding.
Your result is perfectly right. Your frequency axis calculation is also right. The problem lies on the y axis scale. When you use the function xlims, matlab automatically recalculates the y scale so that you can see "meaningful" data. When the cosine peaks lie outside the limit you chose (when f>500Hz), there are no peaks to show, so the scale is calculated based on some veeeery small noise (here at my computer, with matlab 2011a, the y scale was 10-16).
Changing the limit is indeed the correct approach, because if you don't change it you can't see the peaks on the frequency spectrum.
One thing I noticed, however. Is there a reason for you to plot the real part of the transform? Usually, it is abs(F) that gets plotted, and not the real part.
edit: Actually, you're frequency axis is only right because df, in this case, is 1. The faxis line is right, but the df calculation isn't.
The FFT calculates N points from -Fs/2 to Fs/2. So N points over a range of Fs yields a df of Fs/N. As N/time = Fs => time = N/Fs. Substituting that on the expression of df you used: your_df = Fs/N*(N/Fs) = (Fs/N)^2. As Fs/N = 1, the final result was right :P