About zero-padding for the FFT in the Matlab - matlab

I have a question about the zero-padding for the fft. I ran fft with zero-padding & without zero-padding and compared.
sf = 100; %sampling frequency
dt=1/sf; %time sampling interval
L = 10; %Length of signal
t = linspace(0,L,L/dt+1);
%zero-padding
nfft = 2^nextpow2(length(t)); %expansion of the data length for fft
t(length(t)+1:nfft) = 0; L = dt*nfft; t = linspace(0,L,L/dt+1);
t(end)=[];
fr = 4; %frequency
data = cos(2*pi*fr1*t);
df = sf/length(data); %frequency increment
f = (0:length(data)/2)*df;
fft_result =fft(data)/length(data);
spec_fft = 2*abs(fft_result); %spectrum
pha_fft = angle(fft_result); %phase
pha_fft = rad2deg(pha_fft);
subplot(2,1,1);
stem(f,spec_fft(1:length(f)));
subplot(2,1,2);
stem(f,pha_fft(1:length(f)));
And I could see the difference between the two result images.
When I did fft without zero-padding, the amplitude was displayed clear, and the phase also clear (I think other frequencies' phase is due to a very small amplitude value not zero). But When I used zero-padding, I could see amplitudes of nearby frequency that I input(4Hz) show different aspects and the result of the phase is strange in my opinion. Is there some problem in my code when I used zero-padding?
*Additional question for comments of Cris Luengo
I tried to pad zeros to the data by the extended length.
nfft = 2^nextpow2(length(t)); %expansion of the data length for fft
data(length(t)+1:nfft) = 0;
When I plot the data, I got
As you can see, values over 10 are zeros.
And I got this result.
I wonder whether my results are okay or not.

If the FFT magnitude result is close to zero, it might just be numerical noise (random quantization and rounding errors). The phase of this numerical noise is nonsense. I usually set the phase to zero unless the corresponding magnitude is above some threshold for actual non-zero spectrum.

Related

Scaling problems with IFFT in Matlab

I'm studying the IFFT in Matlab by applying it to a Gaussian. According to Wikipedia tables, the Fourier transform pair would be
F(w) = sqrt(pi/a) * exp(-w^2/(4a))
in frequency, and
f(t) = exp(-at^2)
in time. I modified the code in a previous question plus Cris Luengo's answer to perform this IFFT.
a = 0.333;
ts = 1e4; % time sampling
L = 1000*ts; % no. sample points
ds = 1/ts;
f = -floor(L/2):floor((L-1)/2); % freq vector
f = f/ts;
w = 2*pi*f; % angular freq
Y = sqrt(pi/a)*exp(-w.^2/(4*a));
y = ts*ifftshift(ifft(fftshift(Y)));
t = (-L/2:L/2-1)*ts/L; % time vector
f = exp(-a*t.^2); % analytical solution
figure; subplot(1,2,1); hold on
plot(t,real(y),'.--')
plot(t,real(f),'-')
xlabel('time, t')
title('real')
legend('numerical','analytic')
xlim([-5,5])
subplot(1,2,2); hold on
plot(w,imag(y),'.--')
plot(w,imag(f),'-')
xlabel('time, t')
title('imag')
legend('numerical','analytic')
xlim([-5,5])
When I compare the result of IFFT with the analytical expression, they don't seem to agree:
I'm not sure where the mistake is. Have I scaled the IFFT properly? Is there an error in how I define the linear/angular frequency?
Edit: For some reason, when I define L=ts^2 the analytical and numerical solutions seem to agree (L = no. sampling points, ts = time sample).
Starting from the analytical solution, let's rephrase things a bit. You have a sampling of the function f(t) = exp(-a*t^2), and the way you've constructed the analytical answer you're collecting L=1000*ts=1e7 samples at a sampling rate of Ts=ts/L=1e-3. This means that your sampling frequency is Fs=1/Ts=1e3.
Since you want to compare against results obtained with fft/ifft, you should be considering digital or discrete frequencies, meaning the values you define for your transform will correspond to the digital frequencies
frd = (-L/2:L/2-1)/L;
Mapping this to angular frequencies, we have:
w = 2*pi*frd;
But when you're trying to compute the values, you also need to keep in mind that these frequencies should represent samples of the continuous time spectrum you're expecting. So you scale these values by your sampling frequency:
Y = sqrt(pi/a)*exp(-(Fs*w).^2/(4*a));
y = Fs*ifftshift(ifft(fftshift(Y)));
When you compare the analytical and computed answers, they now match.
The short answer to your question, given this, is that you are scaling y incorrectly at the end. You're scaling it by ts, which is 1e4, but you need to be scaling it by the sampling frequency which is Fs=1e3. That's why you end up off by a factor of 10.

How to get the full Fourier spectrum in MATLAB?

I created a 6-bit quantizer and passed a signal through it, but when I plot the DFT, it peaks at 200 MHz and then stops; I'm not seeing the whole spectrum. What's preventing me in my code from getting the rest of the points at the higher frequencies?
Here is my code:
bits = 6; %6-bit
fs = 400e6; %sampling frequency
amp = 1; %amplitude
f = 200e6; %actual frequency
vpp = 2; peak-to-peak voltage
LSB = vpp/(2^bits); %least-significant bit
cycles = 1000;
duration = cycles/f;
values = 0:1/fs:duration;
party = LSB:LSB:(vpp-LSB); %partition
blocker = 0:1:(2^bits - 1); %codebook
biblocker = fliplr(decimaltobinary(blocker)); %I created a function that converts decimal to binary
qtone = amp + amp*sin(2*pi*f*values); %tone
[index, q] = quantization(qtone,party,blocker); %I created a quantizing function
ftq = fft(q)/length(q); % Fourier Transform (Scaled)
qf = linspace(0, 1, fix(length(q))/2+1)*(fs/2); % Frequency Vector
qi = 1:length(qf); % Index Vector
qa = abs(ftq(qi))*2/.7562;
figure
plot(qf/1e6, qa) % One-Sided Amplitude Plot
xlim([100 500]);
xlabel('Frequency [MHz]')
ylabel('Amplitude')
Here is what I get:
Since you selected your sampling frequency, fs = 400e6 i.e. 400 MHz, you can only observe the spectrum up to 200e6, half of the sampling frequency. You can read the theory behind it using Nyquist Sampling Theorem.
As a solution you need to set your twice the frequency you want to observe on the spectrum. It is impossible to observe whole frequency, you need to set a finite frequency limit.
For base-band sampled data, everything in the spectrum above half the sample rate is redundant, just aliases for the spectrum below half the sample rate. So there's no need to display the same spectrum repeated.
When sampling for a finite length of time (less than the age of the Earth, etc.), you have to sample at a rate higher than twice the highest frequency in the signal. 2X (400Msps for a 200MHz signal) often won't work.
qf = linspace(0, 1, fix(length(q)))*(fs);

Finding peak frequency in a complex signal using Matlab

I'm trying to find the peak frequency for two signals 'CA1' and 'PFC', within a specified range (25-140Hz).
In Matlab, so far I have plotted an FFT for each of these signals (see pictures below). These FFTs suggest that the peak frequency between 25-140Hz is different for each signal, but I would like to quantify this (e.g. CA1 peaks at 80Hz, whereas PFC peaks at 55Hz). However, I think the FFT is not smooth enough, so when I try and extract the peak frequencies it doesn't make sense as my code pulls out loads of values. I was only expecting a few values - one each time the FFT peaks (around 2Hz, 5Hz and ~60Hz).
I want to know, between 25-140Hz, what is the peak frequency in 'CA1' compared with 'PFC'. 'CA1' and 'PFC' are both 152401 x 7 matrices of EEG data, recorded
from 7 separate individuals. I want the MEAN peak frequency for each data set (i.e. averaged across the 7 test subjects for CA1 and PFC).
My code so far (based on Matlab help files and code I've scrabbled together online):
Fs = 508;
%notch filter
[b50,a50] = iirnotch(50/(Fs/2), (50/(Fs/2))/70);
CA1 = filtfilt(b50,a50,CA1);
PFC = filtfilt(b50,a50,PFC);
%FFT
L = length(CA1);
NFFT = 2^nextpow2(L);
%FFT for each of the 7 subjects
for i = 1:size(CA1,2);
CA1_FFT(:,i) = fft(CA1(:,i),NFFT)/L;
PFC_FFT(:,i) = fft(PFC(:,i),NFFT)/L;
end
%Average FFT across all 7 subjects - CA1
Mean_CA1_FFT = mean(CA1_FFT,2);
% Mean_CA1_FFT_abs = 2*abs(Mean_CA1_FFT(1:NFFT/2+1));
%Average FFT across all 7 subjects - PFC
Mean_PFC_FFT = mean(PFC_FFT,2);
% Mean_PFC_FFT_abs = 2*abs(Mean_PFC_FFT(1:NFFT/2+1));
f = Fs/2*linspace(0,1,NFFT/2+1);
%LEFT HAND SIDE FIGURE
plot(f,2*abs(Mean_CA1_FFT(1:NFFT/2+1)),'r');
set(gca,'ylim', [0 2]);
set(gca,'xlim', [0 200]);
[C,cInd] = sort(2*abs(Mean_CA1_FFT(1:NFFT/2+1)));
CFloor = 0.1; %CFloor is the minimum amplitude value (ignore small values)
Amplitudes_CA1 = C(C>=CFloor); %find all amplitudes above the CFloor
Frequencies_CA1 = f(cInd(1+end-numel(Amplitudes_CA1):end)); %frequency of the peaks
%RIGHT HAND SIDE FIGURE
figure;plot(f,2*abs(Mean_PFC_FFT(1:NFFT/2+1)),'r');
set(gca,'ylim', [0 2]);
set(gca,'xlim', [0 200]);
[P,pInd] = sort(2*abs(Mean_PFC_FFT(1:NFFT/2+1)));
PFloor = 0.1; %PFloor is the minimum amplitude value (ignore small values)
Amplitudes_PFC = P(P>=PFloor); %find all amplitudes above the PFloor
Frequencies_PFC = f(pInd(1+end-numel(Amplitudes_PFC):end)); %frequency of the peaks
Please help!! How do I calculate the 'major' peak frequencies from an FFT, and ignore all the 'minor' peaks (because the FFT is not smoothed).
FFTs assume that the signal has no trend (this is called a stationary signal), if it does then this will give a dominant frequency component at 0Hz as you have here. Try using the MATLAB function detrend, you may find this solves your problem.
Something along the lines of:
x = x - mean(x)
y = detrend(x, 'constant')

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

Spectrogram and what it is

I am very interested to know how the top right figure in :http://en.wikipedia.org/wiki/Spectrogram
is generated (the script) and how to analyse it i.e what information does it convey?I would appreciate a simplified answer with minimum mathematical jargons. Thank you.
The plot shows time along the horizontal axis, and frequency along the vertical axis. With pixel color showing the intensity of each frequency at each time.
A spectrogram is generated by taking a signal and chopping it into small time segments, doing a Fourier series on each segment.
here is some matlab code to generate one.
Notice how plotting the signal directly, it looks like garbage, but plotting the spectrogram, we can clearly see the frequencies of the component signals.
%%%%%%%%
%% setup
%%%%%%%%
%signal length in seconds
signalLength = 60+10*randn();
%100Hz sampling rate
sampleRate = 100;
dt = 1/sampleRate;
%total number of samples, and all time tags
Nsamples = round(sampleRate*signalLength);
time = linspace(0,signalLength,Nsamples);
%%%%%%%%%%%%%%%%%%%%%
%create a test signal
%%%%%%%%%%%%%%%%%%%%%
%function for converting from time to frequency in this test signal
F1 = #(T)0+40*T/signalLength; #frequency increasing with time
M1 = #(T)1-T/signalLength; #amplitude decreasing with time
F2 = #(T)20+10*sin(2*pi()*T/signalLength); #oscilating frequenct over time
M2 = #(T)1/2; #constant low amplitude
%Signal frequency as a function of time
signal1Frequency = F1(time);
signal1Mag = M1(time);
signal2Frequency = F2(time);
signal2Mag = M2(time);
%integrate frequency to get angle
signal1Angle = 2*pi()*dt*cumsum(signal1Frequency);
signal2Angle = 2*pi()*dt*cumsum(signal2Frequency);
%sin of the angle to get the signal value
signal = signal1Mag.*sin(signal1Angle+randn()) + signal2Mag.*sin(signal2Angle+randn());
figure();
plot(time,signal)
%%%%%%%%%%%%%%%%%%%%%%%
%processing starts here
%%%%%%%%%%%%%%%%%%%%%%%
frequencyResolution = 1
%time resolution, binWidth, is inversly proportional to frequency resolution
binWidth = 1/frequencyResolution;
%number of resulting samples per bin
binSize = sampleRate*binWidth;
%number of bins
Nbins = ceil(Nsamples/binSize);
%pad the data with zeros so that it fills Nbins
signal(Nbins*binSize+1)=0;
signal(end) = [];
%reshape the data to binSize by Nbins
signal = reshape(signal,[binSize,Nbins]);
%calculate the fourier transform
fourierResult = fft(signal);
%convert the cos+j*sin, encoded in the complex numbers into magnitude.^2
mags= fourierResult.*conj(fourierResult);
binTimes = linspace(0,signalLength,Nbins);
frequencies = (0:frequencyResolution:binSize*frequencyResolution);
frequencies = frequencies(1:end-1);
%the upper frequencies are just aliasing, you can ignore them in this example.
slice = frequencies<max(frequencies)/2;
%plot the spectrogram
figure();
pcolor(binTimes,frequencies(slice),mags(slice,:));
The inverse Fourier transform of the fourierResult matrix, will return the original signal.
Just to add to Suki's answer, here is a great tutorial that walks you through, step by step, reading Matlab spectrograms, touching on only enough math and physics to explain the main concepts intuitively:
http://www.caam.rice.edu/~yad1/data/EEG_Rice/Literature/Spectrograms.pdf