Hanning window and online FFT - matlab

I am learning DSP and I couldn't write a code to calculate and plot these figures (just magnitude of Hanning and triangular windows in frequency domain.) Could anyone please help me with the code?
I've read something related to online FFT, and, for example, they calculate online FFT with 1024 time steps. I don't understand what is 1024 time steps, and what are the influences of time step value to FFT analysis?

I hope the following piece of code is helpful to you.
L = 10;
win1 = hanning(L);
win2 = triang(L);
nfft = 64;
S1 = fft(win1,nfft);
S2 = fft(win2,nfft);
f = 1:nfft/2+1;
plot(f,10*log10(abs(S1(1:nfft/2+1))),'.-',f,10*log10(abs(S2(1:nfft/2+1))),'o-');
Annotation:
You can put win1 and win2 as Time-series signal. L is the length of win1 or win2. nfft is the length of FFT . if L < nfft , then the function fft() will add 0 to the rest of nfft. If L > nfft, then the function fft() will interception the length of L to equal nfft.

The frequency of the time steps or samples (Fs), divided by the number of time steps fed the FFT (the FFT length), gives you the frequency steps of the FFT result bins (up to Fs/2).

Regarding the 1024 "time steps" in your question, that is simply the number of samples that are taken from the time-domain signal.
As to how the 1024 time-domain samples influence the FFT, this involves the sampling frequency that was used to obtain the samples.
Sampling frequency is typically chosen to conform with the Nyquist-Shannon sampling theorem, which basically states that you must sample the time-domain signal at a frequency higher than "2F" if you want to resolve a frequency "F" via the FFT.
As for the Hann (Hanning) and Triangular window codes, they are as follows:
Hann window:
for( i=0; i<bufLen; i++ )
window[i] = 0.5 * ( 1 - cos( 2 * PI * i / (bufLen-1) ) )
Triangular window:
for( i=0; i<bufLen; i++ )
window[i] = 2/bufLen * ( (bufLen)/2 - abs( i-((bufLen-1)/2) ) )
...
The plot below is the frequency response of the Triangular window, in dB magnitude.
The plot below is the frequency response of the Triangular window, but now in linear magnitude.
You can plot the Hann, Triangular, and many other window functions here: Plot windows functions

Related

About zero-padding for the FFT in the 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.

Determining time-dependent frequency using a sliding-window FFT

I have an instrument which produces roughly sinusoidal data, but with frequency varying slightly in time. I am using MATLAB to prototype some code to characterize the time dependence, but I'm running into some issues.
I am generating an idealized approximation of my data, I(t) = sin(2 pi f(t) t), with f(t) variable but currently tested as linear or quadratic. I then implement a sliding Hamming window (of width w) to generate a set of Fourier transforms F[I(t), t'] corresponding to the data points in I(t), and each F[I(t), t'] is fit with a Gaussian to more precisely determine the peak location.
My current MATLAB code is:
fs = 1000; %Sample frequency (Hz)
tlim = [0,1];
t = (tlim(1)/fs:1/fs:tlim(2)-1/fs)'; %Sample domain (t)
N = numel(t);
f = #(t) 100-30*(t-0.5).^2; %Frequency function (Hz)
I = sin(2*pi*f(t).*t); %Sample function
w = 201; %window width
ww=floor(w/2); %window half-width
for i=0:2:N-w
%Take the FFT of a portion of I, convolved with a Hamming window
II = 1/(fs*N)*abs(fft(I((1:w)+i).*hamming(w))).^2;
II = II(1:floor(numel(II)/2));
p = (0:fs/w:(fs/2-fs/w))';
%Find approximate FFT maximum
[~,maxIx] = max(II);
maxLoc = p(maxIx);
%Fit the resulting FFT with a Gaussian function
gauss = #(c,x) c(1)*exp(-(x-c(2)).^2/(2*c(3)^2));
op = optimset('Display','off');
mdl = lsqcurvefit(gauss,[max(II),maxLoc,10],p,II,[],[],op);
%Generate diagnostic plots
subplot(3,1,1);plot(p,II,p,gauss(mdl,p))
line(f(t(i+ww))*[1,1],ylim,'color','r');
subplot(3,1,2);plot(t,I);
line(t(1+i)*[1,1],ylim,'color','r');line(t(w+i)*[1,1],ylim,'color','r')
subplot(3,1,3);plot(t(i+ww),f(t(i+ww)),'b.',t(i+ww),mdl(2),'r.');
hold on
xlim([0,max(t)])
drawnow
end
hold off
My thought process is that the peak location in each F[I(t), t'] should be a close approximation of the frequency at the center of the window which was used to produce it. However, this does not seem to be the case, experimentally.
I have had some success using discrete Fourier analysis for engineering problems in the past, but I've only done coursework on continuous Fourier transforms--so there may be something obvious that I'm missing. Also, this is my first question on StackExchange, so constructive criticism is welcome.
So it turns out that my problem was a poor understanding of the mathematics of the sine function. I had assumed that the frequency of the wave was equal to whatever was multiplied by the time variable (e.g. the f in sin(ft)). However, it turns out that the frequency is actually defined by the derivative of the entire argument of the sine function--the rate of change of the phase.
For constant f the two definitions are equal, since d(ft)/dt = f. But for, say, f(t) = sin(t):
d(f(t)t)/dt = d(sin(t) t)/dt = t cos(t) + sin(t)
The frequency varies as a function very different from f(t). Changing the function definition to the following fixed my problem:
f = #(t) 100-30*(t-0.5).^2; %Frequency function (Hz)
G = cumsum(f(t))/fs; %Phase function (Hz)
I = sin(2*pi*G); %Sampling function

How do I create band-limited (100-640 Hz) white Gaussian noise?

I would like to create 500 ms of band-limited (100-640 Hz) white Gaussian noise with a (relatively) flat frequency spectrum. The noise should be normally distributed with mean = ~0 and 99.7% of values between ± 2 (i.e. standard deviation = 2/3). My sample rate is 1280 Hz; thus, a new amplitude is generated for each frame.
duration = 500e-3;
rate = 1280;
amplitude = 2;
npoints = duration * rate;
noise = (amplitude/3)* randn( 1, npoints );
% Gaus distributed white noise; mean = ~0; 99.7% of amplitudes between ± 2.
time = (0:npoints-1) / rate
Could somebody please show me how to filter the signal for the desired result (i.e. 100-640 Hz)? In addition, I was hoping somebody could also show me how to generate a graph to illustrate that the frequency spectrum is indeed flat.
I intend on importing the waveform to Signal (CED) to output as a form of transcranial electrical stimulation.
The following is Matlab implementation of the method alluded to by "Some Guy" in a comment to your question.
% In frequency domain, white noise has constant amplitude but uniformly
% distributed random phase. We generate this here. Only half of the
% samples are generated here, the rest are computed later using the complex
% conjugate symmetry property of the FFT (of real signals).
X = [1; exp(i*2*pi*rand(npoints/2-1,1)); 1]; % X(1) and X(NFFT/2) must be real
% Identify the locations of frequency bins. These will be used to zero out
% the elements of X that are not in the desired band
freqbins = (0:npoints/2)'/npoints*rate;
% Zero out the frequency components outside the desired band
X(find((freqbins < 100) | (freqbins > 640))) = 0;
% Use the complex conjugate symmetry property of the FFT (for real signals) to
% generate the other half of the frequency-domain signal
X = [X; conj(flipud(X(2:end-1)))];
% IFFT to convert to time-domain
noise = real(ifft(X));
% Normalize such that 99.7% of the times signal lies between ±2
noise = 2*noise/prctile(noise, 99.7);
Statistical analysis of around a million samples generated using this method results in the following spectrum and distribution:
Firstly, the spectrum (using Welch method) is, as expected, flat in the band of interest:
Also, the distribution, estimated using histogram of the signal, matches the Gaussian PDF quite well.

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

Difficulty understanding the phase calculated by the FFT. Short matlab demo to illustrate

I'm testing the phase output of an fft of a sin signal and a cos signal.
The script below creates the signals and performs an FFT on them. Bins who's amplitude is below a threshold are zeroed for the phase spectrum because I am only interested in the phase of the signals.
% 10khz 10 second long time interval
t = 0:1 / 10000:10;
%1khz cos
c = cos(2 * pi * 1000 .* t);
%1khz sin
s = sin(2 * pi * 1000 .* t);
%ffts
C = fft(c)/length(c);
S = fft(s)/length(s);
%magnitude and phases of ffts
CA = abs(C); %cos magnitude
SA = abs(S); %sin magnitude
Cthresh = max(CA) * 0.5;
Sthresh = max(SA) * 0.5;
%find all indeces below the threshold
Crange = find(CA < Cthresh);
Srange = find(SA < Sthresh);
%set the indeces below the threshold to 0 - phase will be meaningless for
%noise values
CP = angle(C);
CP(Crange) = 0;
SP = angle(S);
SP(Srange) = 0;
If you plot CP - the phase of the cos - you will get a phase of 0.3142 in the bins corresponding to the frequency of the cos signal and zeros elsewhere. This is pi/10. I'm expecting to get pi. Why is this?
If you plot SP you get values of 1.2566. I'm expecting to get pi/2 or 1.5708. 80% of the expected value. What is causing these errors?
If your input signal is not perfectly periodic in the FFT aperture length (an exact integer number of full periods), the sinusoids will be discontinuous across the ends of the FFT aperture. Thus you will get a phase that is the average of the two different phases at both ends of the FFT input vector.
If you want a more sensible phase, reference the phase of your sinusoids to the center of the FFT input vector, and do an fft shift before the FFT. This will result in a continuous sinusoid at the zero phase reference position, with a single phase instead of a weird average value.
Also note that matlab may reference the phase to the second point in a sampled sinusoid, e.g. vectorElement[i=1], not the first, vectorElement[i=0]. This will have a phase of pi/10 for a sinusoid of period = 20 samples.
The issue you have is exactly what hotpaw2 has stated. You have 100001 samples in t, so you do not have a perfectly periodic signal, therefore you have leakage. That means that you've got a sin()/sin() function from your implicit rectangular window convolved with your solution. That's what changes your phase.
If instead you try the following:
t = 0:1 / 10000:9.9999;
c = cos(2 * pi * 1000 .* t);
%1khz sin
s = sin(2 * pi * 1000 .* t);
%ffts
C = fft(c)/length(c);
S = fft(s)/length(s);
you would find that the phase of the cosine is zero (which is what you would expect) and that the phase of sine is pi/2.
Performing a linear shift in the time domain (using fftshift) will merely introduce a linear phase term in the frequency domain, and will not resolve the original problem.
In practice, rather than trying to set the length of the sequence precisely to match the period of the signal, windowing should be applied if the signal is to be examined in the frequency domain. In that case you really should make sure that your signals are appropriately aligned so that the window attenuates the end points, thus smoothing out the discontinuity. This has the effect of broadening the main lobe of the FFT, but it also controls the leakage.