I want to use fft in MATLAB to analize some exprimental data saved as an excell file.
my code:
A=xlsread('Book.xls'); G=A'; x=G(2, : );
N=length(x);
F=[-N/2:N/2-1]/N;
X = abs(fft(x-mean(x),N))
X = fftshift(X);
plot(F,X)
But it plots a graph with a large 0Hz wrong component, my true frequency is about 395Hz and it is not shown in the plotted graph.
Please tell me what is wrong.
Any help would be appreciated.
Assume we read the signal from file:
G = xlsread('Book.xls');
t = G(:,1);
x = G(:,2);
N = length(x);
First we estimate the sampling frequency from the time axis, and build the frequency vector:
Fs = 1 ./ abs( t(2)-t(1) );
F = (-N/2:N/2-1)*Fs/N;
then compute the FFT and plot:
X = abs( fft(x-mean(x),N) );
X = fftshift(X);
stem(F,X)
finally find the peak and the corresponding frequency:
>> [~,ind] = max(X);
>> F(ind)
ans =
-400
you might want to zoom-in near the origin to see things more clearly:
xlim([-1000 1000])
Related
How can I get the correct frequency vector to plot using the FFT of MATLAB?
My problem:
N = 64;
n = 0:N-1;
phi1 = 2*(rand-0.5)*pi;
omega1 = pi/6;
phi2 = 2*(rand-0.5)*pi;
omega2 = 5*pi/6;
w = randn(1,N); % noise
x = 2*exp(1i*(n*omega1+phi1))+4*sin(n*omega2+phi2);
h = rectwin(N).';
x = x.*h;
X = abs(fft(x));
Normally I'd do this :
f = f = Fs/Nsamples*(0:Nsamples/2-1); % Prepare freq data for plot
The problem is this time I do not have a Fs (sample frequency).
How can I do it correctly in this case?
If you don't have a Fs, simply set it to 1 (as in one sample per sample). This is the typical solution I've always used and seen everybody else use. Your frequencies will run from 0 to 1 (or -0.5 to 0.5), without units. This will be recognized by everyone as meaning "periods per sample".
Edit
From your comment I conclude that you are interested in radial frequencies. In that case you want to set your plot x-axis to
omega = 2*pi*f;
I have a 6ms-long signal with three frequency components sampled at 60kHz:
fs = 60000;
T = 0.006;
t = 0:1/fs:T;
x = 0.3*sin(2*pi*2000*t) + sin(2*pi*5000*t) + 0.4*sin(2*pi*8000*t);
I have a bandpass filter with impulse response being the difference between two sinc functions:
M = 151;
N = 303;
n = 0:(N-1);
h = (sin(0.5760*pi*(n-M))-sin(0.3665*pi*(n-M)))./pi./(n-M);
h(n==M) = 0.2094;
I designed a function that convolves the input with filter:
function y = fir_filter(h,x)
y = zeros(1,length(x)+length(h)-1);
for i = 1:length(x)
for j = 1:length(h)
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j);
end
end
And then applied the filter:
y = fir_filter(h,x);
This produced strange results:
figure(21)
ax1 = subplot(311);
plot(x);
title('Input Signal');
ax2 = subplot(312);
plot(h);
title('FIR');
ax3 = subplot(313);
plot(y);
title('Output Signal');
linkaxes([ax1,ax2,ax3],'x')
ax2.XLim = [0,length(y)];
Since the filter is bandpass, only one frequency component was expected to survive.
I tried to use yy = filter(h,1,[x,zeros(1,length(h)-1)]); and yyy = conv(h,x); and got the same results.
Please, can anybody explain me what I am doing wrong? Thank you!
Your passband does not cover any of the three signal frequency components. This can be seen directly on the graph (the second figure, containing the impulse response, has too fast variations compared with the signal). Or it can be seen from the numbers 0.5760 and 0.3655 in
h = (sin(0.5760*pi*(n-M))-sin(0.3665*pi*(n-M)))./pi./(n-M);
Why did you choose those numbers? The normalized frecuencies of the signal are [2 5 8]/60, that is, 0.0333 0.0833 0.1333. They are all below the passband [.3665 .5760]. As a result, the filter greatly attenuates the three components of the input signal.
Say you want to isolate the center frequency component (f = 5000 Hz, or f/fs = 0.08333 normalized frequency). You need a bandpass filter than lets that frequency through, and rejects the others. So you would use for example a normalized passband [.06 .1]:
h = (sin(0.06*pi*(n-M))-sin(0.1*pi*(n-M)))./pi./(n-M);
h(n==M) = (h(n==M+1)+h(n==M-1))/2; %// auto adjustment to avoid the 0/0 sample
A second problem with your code is that it gives two errors because you index h with 0. To solve this, change
n = 0:(N-1);
to
n = 1:N;
and
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j);
to
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j+1);
So, the modified code to isolate the central frequency component is:
fs = 60000;
T = 0.006;
t = 0:1/fs:T;
x = 0.3*sin(2*pi*2000*t) + sin(2*pi*5000*t) + 0.4*sin(2*pi*8000*t);
M = 151;
N = 303;
n = 1:N; %// modified
h = (sin(0.06*pi*(n-M))-sin(0.1*pi*(n-M)))./pi./(n-M); %// modified
h(n==M) = (h(n==M+1)+h(n==M-1))/2; %// modified
y = zeros(1,length(x)+length(h)-1);
for i = 1:length(x)
for j = 1:length(h)
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j+1); %// modified
end
end
figure(21)
ax1 = subplot(311);
plot(x);
title('Input Signal');
ax2 = subplot(312);
plot(h);
title('FIR');
ax3 = subplot(313);
plot(y);
title('Output Signal');
linkaxes([ax1,ax2,ax3],'x')
ax2.XLim = [0,length(y)];
The result is as follows.
As can be seen, only the central frequency component is present in the output signal.
It's also observed that the envelope of the output signal is not constant. That's because the duration of the input signal is comparable to the filter lenght. That is, you are seeing the transient response of the filter. Note that the envelop rise times is approximately the length of the filter's impulse response h.
It's interesting to note an interesting trade-off here (signal processing is full of trade-offs). To make the transient shorter you could use a wider passband (filter length is inversely proportional to passband). But then the filter would be less selective, that is, it would have less attenuation at the unwanted frequencies. For example, see the result with passband [.04 .12]:
As expected, the transient is now shorter, but the desired frequency is less pure: you can see some "modulation" caused by remains of the other frequencies.
I've been working on 2 sensors signals measuring vibrations of a rotating shaft. Since there is residual noise in the signal. I tried to filter it by detrending, zero padding and applying low pass filter. Below I'm attaching the graphs of the signal before and after filtering. There is a huge variation in the signal after filtering that makes me think if I'm really doing it in the right way.
My Matlab code is
X = xlsread(filename,'F:F');
Y = xlsread(filename,'G:G');
%Calculate frequency axis
fs = 1e6 ; % Sampling frequency (Hz)
NFFT = 2^nextpow2(length(X)); % Zero padding to nearest N power 2
df = fs/NFFT;
dt = 1/df;
%Frequency Axis defintion
f = (-(fs-df)/2:df:(fs-df)/2)';
X(2^ceil(log2(length(X))))=0;
Y(2^ceil(log2(length(Y))))=0;
%calculate time axis
T = (dt:dt:(length(X)*dt))';
subplot(2,2,1)
plot(T,X);
xlabel('Time(s)')
ylabel('X amplitude')
title('X signal before filtering')
subplot(2,2,2)
plot(T,Y);
xlabel('Time(s)')
ylabel('Y amplitude')
title('Y signal before filtering')
X = detrend(X,0); % Removing DC Offset
Y = detrend(Y,0); % Removing DC Offset
% Filter parameters:
M = length(X); % signal length
L = M; % filter length
fc = 2*(38000/60); % cutoff frequency
% Design the filter using the window method:
hsupp = (-(L-1)/2:(L-1)/2);
hideal = (2*fc/fs)*sinc(2*fc*hsupp/fs);
h = hamming(L)' .* hideal; % h is our filter
% Zero pad the signal and impulse response:
X(2^ceil(log2(M)))=0;
xzp = X;
hzp = [ h zeros(1,NFFT-L) ];
% Transform the signal and the filter:
X = fft(xzp);
H = fft(hzp)';
X = X .* H;
X = ifft(X);
relrmserrX = norm(imag(X))/norm(X); % checked... this for zero
X = real(X)';
% Zero pad the signal and impulse response:
Y(2^ceil(log2(M)))=0;
xzp = Y;
hzp = [ h zeros(1,NFFT-L) ];
% Transform the signal and the filter:
Y = fft(xzp);
H = fft(hzp)';
Y = Y .* H;
Y = ifft(Y);
relrmserrY = norm(imag(Y))/norm(Y); % check... should be zero
Y = real(Y)';
I plotted the after filtering and as you can see in the picture there is a clear deviation. I want to only filter noise but the signal seem to loose other components and I'm little confused if thats the right way of doing filtering.
Any suggestion, hints or ideas would be helpful.
In the end I want to plot X vs Y to give orbits of the shaft vibration. Please also find below another picture of the unfiltered and filtered orbit. As you can see in the picture there is also change in the orbits from the original one (left image with lot of noise).
P.S.: I don't have DSP tool box
There is no problem with your FFT and IFFT.
You can test your code with a simple sine wave, with a very low frequency. The final output should be (almost) the same sine wave, since you are low-pass filtering it.
You've defined X (and Y) from 0 to some value. However, you've defined H from - (L-1)/2 to some positive value. Mathematically, this is fine, but you are simply taking an fft of H. Matlab thinks this has the same time-scale as X, when you multiply the ffts together!
So, in reality, you've taken the fft of XHfft(delta(t-d)), where d is the resulting time-shift. You can either undo this time-shift in the frequency domain, or the time-domain.
in the below code i am trying to get the spectrogram of the non-stationary signalx
after running the code, i expected to see some thing like the posted inage "image_2" , frequency vs time representation. but the resut of the posted code is image_1.
can any one please guide me to get the correct spectrogram?
Code
% Time specifications:
Fs = 8000; % samples per second
dt = 1/Fs; % seconds per sample
StopTime = 1; % seconds
t = (0:dt:StopTime-dt); % seconds
t1 = (0:dt:.25);
t2 = (.25:dt:.50);
t3 = (.5:dt:.75);
t4 = (.75:dt:1);
%get a full-length example of each signal component
x1 = (10)*sin(2*pi*100*t);
x2 = (10)*sin(2*pi*200*t);
x3 = (10)*sin(2*pi*300*t);
x4 = (10)*sin(2*pi*400*t);
%construct a composite signal
x = zeros(size(t));
I = find((t >= t1(1)) & (t <= t1(end)));
x(I) = x1(I);
I = find((t >= t2(1)) & (t <= t2(end)));
x(I) = x2(I);
I = find((t >= t3(1)) & (t <= t3(end)));
x(I) = x3(I);
I = find((t >= t4(1)) & (t <= t4(end)));
x(I) = x4(I);
NFFT = 2 ^ nextpow2(length(t)); % Next power of 2 from length of y
Y = fft(x, NFFT);
f = Fs / 2 * linspace(0, 1, NFFT/2 + 1);
figure;
plot(f(1:200), 2 * abs( Y( 1:200) ) );
T = 0:.001:1;
spectrogram(x,10,9);
ylabel('Frequency');
axis(get(gcf,'children'), [0, 1, 1, 100]);
result of the posted code: Spectrogram_Image_1:
what i am trying to get: Image_2:
Update_1, image
Code:
%now call the spectrogram
spectrogram(x, window, noverlap, Nfft, Fs);
ylabel('Frequency');
axis(get(gcf,'children'), [0, 1]);
First, as with the first time that you asked this question, have you plotted your data in the time-domain (ie, plot(t, x)) and zoomed in on the transitions to ensure that your signal is what you think it is? Does it have the four different periods with distinct frequencies as you intend?
Assuming that it does, I'm pretty sure that your problem is that your spectrogram call is not doing what you want. I think that you are only getting an NFFT of 10, which means that your bins are 800 Hz wide, which is insufficient for resolving your frequencies that are only 100 Hz apart.
In my opinion, you should specify more parameters so that you know what it is doing. You'd specify an Nfft that would give the frequency resolution that you need. Something with more resolution than 100 Hz (let's try 25 Hz), but not requiring so many points that it is longer than the duration where you have stable frequencies (so, less than 0.25 sec, which means less than 2000 points).
To see how to specify the length of the FFT, I looked at the documentation: http://www.mathworks.com/help/signal/ref/spectrogram.html
Based on the docs I'd try the five parameter version: spectrogram(x,window,noverlap,nfft,fs)
For you code, where Fs and x are as you have already defined them, the spectrogram call would look like:
%define FFT parameters
des_df_Hz = 25; %desired frequency resolution for the display, Hz
Nfft = round(FS / des_df_Hz); %general rule for FFT resolution
Nfft = 2*Nfft; %double the bins to account for spreading due to windowing
Nfft = 2*round(0.5*Nfft); %make Nfft an even number
window = Nfft; %make your window the same length as your FFT
noverlap = round(0.95); %overlap a lot to make the plot pretty
%now call the spectrogram
spectrogram(x, window, noverlap, Nfft, Fs,'yaxis');
I have a problem with the plot of the spectrum, I have an audio file on which I run the fft function and try to plot its spectrum :
fileName = 'stuff.wav' ;
[y, Fs] = audioread(fileName);
Y = fft(y(:,1));
Y = Y / length(y);
plot(fftshift(abs(Y));
and here is what I get :
the magnitude is correct but the frequencies aren't.
so I've wrote a small script to test this :
fs =8000;
t = 0:1/fs:10;
x = 3*sin(2*pi*5*t);
U = abs(fft(x));
stem(t,fftshift(U));
axis([-20 20 -5 5]);
and the result is :
why the peak is in the right place meaning 5 but I was excpecting a second one in -5 negative frequencieswhy I can see it and how can I scale correctly the X axis ?
thanks for your help !
You are ploting the FFT result (U) versus time, which doesn't make much sense. First generate the frequency axis values, and then use that when plotting U:
%// Same as in your code:
fs = 8000;
t = 0:1/fs:10;
x = 3*sin(2*pi*5*t);
U = abs(fft(x));
%// Do the following changes to your code:
f = -fs/2:fs/(numel(t)-1):fs/2; %// frequency axis
stem(f, fftshift(U)); %// plot U versus frequency
axis([-10 10 0 15e4]); %// zoom in to see -5 Hz and 5 Hz values