I was expecting the frequency component to be 1700 i.e. a spike at 1700 but the output shows multiple frequency:
fs = 44100;
t = 0:1/fs:0.001;
s = sin(2 * pi * 1700 * t);
subplot(211), stem(abs(fft(s))), title('abs(fft(s))')
subplot(212), stem(s), title('s')
Similarly when I tried the below code I did not got what I expected:
Fs = 8000;
dt = 1/Fs;
StopTime = 0.25;
t = (0:dt:StopTime-dt)';
Fc = 60;
x = cos(2*pi*Fc*t);
subplot(211), stem(abs(fft(x))), title('abs(fft(x))')
subplot(212), stem(x), title('x')
Why my frequency component are being displayed as multiples values as there should be exactly one frequency present in a signal in one steady sine / cos wave.
It's a single frequency, but it appears twice: positive and negative frequencies. You'll see this better with fftshift, which arranges the frequency samples so that they run from -fs/2 to fs/2:
subplot(211)
freq_axis = -fs/2+fs/numel(t):fs/numel(t):fs/2;
stem(freq_axis, abs(fftshift(fft(s))))
title('abs(fft(s))')
For example, in your first example this produces the following figure.
Note the two spikes around +1700 and -1700 Hz. Their location is not exact for two reasons:
Your time signal is of finite duration, which produces convolution with a sinc in the frequency domain. That is, the frequency spike is made wider.
The FFT gives frequency samples, and none of those samples falls exactly at +/-1700 Hz.
In your second example the time signal is longer (it contains more cycles), which reduces the width of the frequency spikes. This can be appreciated in your second figure (again the fftshift correction is needed to make the two spikes appear in symmetric frequency locations).
Since your signal is not an integer number of cycles there is a discontinuity (remember that the Fourier Transform assumes periodicity), which results in spectral leakage, which is visible as a "smearing" of the spectrum. To avoid this we usually apply a suitable window function (e.g. von Hann aka Hanning window)prior to the FFT - think of this as smoothing out the discontinuity. This reduces the smearing and makes peaks more distinct.
As noted in another answer, you also see a second peak because you're plotting the entire spectrum, and every component in the time domain has a positive and a negative frequency component in the frequency domain. For a real-valued signal the FFT is complex-conjugate symmetric in the frequency domain and so half of the spectrum is redundant. You would normally only plot N/2 values.
Related
I'm trying to find the maximum frequency of a periodic signal in Matlab and as i know when you convert a periodic signal to the frequency spectrum you get only delta functions however i get a few curves between the produced delta functions. Here is the code :
t=[-0.02:10^-3:0.02];
s=5.*(1+cos(2*pi*10*t)).*cos(2*pi*100*t);
figure, subplot(211), plot(t,s);
y=fft(s);
subplot(212), plot(t,y);
Here is a code-snippet to help you understand how to get the frequency-spectrum using fft in matlab.
Things to remember are:
You need to decide on a sampling frequency, which should be high enough, as per the Nyquist Criterion (You need the number of samples, at least more than twice the highest frequency or else we will have aliasing). That means, fs in this example cannot be below 2 * 110. Better to have it even higher to see a have a better appearance of the signal.
For a real signal, what you want is the power-spectrum obtained as the square of the absolute of the output of the fft() function. The imaginary part, which contains the phase should contain nothing but noise. (I didn't plot the phase here, but you can do this to check for yourself.)
Finally, we need to use fftshift to shift the signal such that we get the mirrored spectrum around the zero-frequency.
The peaks would be at the correct frequencies. Now considering only the positive frequencies, as you can see, we have the largest peak at 100Hz and two further lobs around 100Hz +- 10Hz i.e. 90Hz and 110Hz.
Apparently, 110Hz is the highest frequency, in your example.
The code:
fs = 500; % sampling frequency - Should be high enough! Remember Nyquist!
t=[-.2:1/fs:.2];
s= 5.*(1+cos(2*pi*10*t)).*cos(2*pi*100*t);
figure, subplot(311), plot(t,s);
n = length(s);
y=fft(s);
f = (0:n-1)*(fs/n); % frequency range
power = abs(y).^2/n;
subplot(312), plot(f, power);
Y = fftshift(y);
fshift = (-n/2:n/2-1)*(fs/n); % zero-centered frequency range
powershift = abs(Y).^2/n;
subplot(313), plot(fshift, powershift);
The output plots:
The first plot is the signal in the time domain
The signal in the frequency domain
The shifted fft signal
This code takes FFT of a signal and plots it on a new frequency axis.
f=600;
Fs=6000;
t=0:1/Fs:0.3;
n=0:1:length(t);
x=cos(2*pi*(400/Fs)*n)+2*sin(2*pi*(1100/Fs)*n);
y=fft(x,512);
freqaxis=Fs*(linspace(-0.5,0.5, length(y)));
subplot(211)
plot(freqaxis,fftshift(abs(y)));
I understand why we used fftshift because we wanted to see the signal centered at the 0 Hz (DC) value and it is better for observation.
However I seem to be confused about how the frequency axis is defined. Specifically, why did we especially multiply the range of [-0.5 0.5] with Fs and we obtain the [-3000 3000] range? It could be [-0.25 0.25].
The reason why the range is between [-Fs/2,Fs/2] is because Fs/2 is the Nyquist frequency. This is the largest possible frequency that has the ability of being visualized and what is ultimately present in your frequency decomposition. I also disagree with your comment where the range "could be between [-0.25,0.25]". This is contrary to the definition of the Nyquist frequency.
From signal processing theory, we know that we must sample by at least twice the bandwidth of the signal in order to properly reconstruct the signal. The bandwidth is defined as the largest possible frequency component that can be seen in your signal, which is also called the Nyquist Frequency. In other words:
Fs = 2*BW
The upper limit of where we can visualize the spectrum and ultimately the bandwidth / Nyquist frequency is defined as:
BW = Fs / 2;
Therefore because your sampling frequency is 6000 Hz, this means the Nyquist frequency is 3000 Hz, so the range of visualization is [-3000,3000] Hz which is correct in your magnitude graph.
BTW, your bin centres for each of the frequencies is incorrect. You specified the total number of bins in the FFT to be 512, yet the way you are specifying the bins is with respect to the total length of the signal. I'm surprised why you don't get a syntax error because the output of the fft function should give you 512 points yet your frequency axis variable will be an array that is larger than 512. In any case, that is not correct. The frequency at each bin i is supposed to be:
f = i * Fs / N, for i = 0, 1, 2, ..., N-1
N is the total number of points you have in your FFT, which is 512. You originally had it as length(y) and that is not correct... so this is probably why you have a source of confusion when examining the frequency axis. You can read up about why this is the case by referencing user Paul R's wonderful post here: How do I obtain the frequencies of each value in an FFT?
Note that we only specify bins from 0 up to N - 1. To account for this when you specify the bin centres of each frequency, you usually specify an additional point in your linspace command and remove the last point:
freqaxis=Fs*(linspace(-0.5,0.5, 513); %// Change
freqaxis(end) = []; %// Change
BTW, the way you've declared freqaxis is a bit obfuscated to me. This to me is more readable:
freqaxis = linspace(-Fs/2, Fs/2, 513);
freqaxis(end) = [];
I personally hate using length and I favour numel more.
In any case, when I run the corrected code to specify the bin centres, I now get this plot. Take note that I inserted multiple data cursors where the peaks of the spectrum are, which correspond to the frequencies for each of the cosines that you have declared (400 Hz and 1100 Hz):
You see that there are some slight inaccuracies, primarily due to the number of bins you have specified (i.e. 512). If you increased the total number of bins, you will see that the frequencies at each of the peaks will get more accurate.
I have the following matlab code, and I am trying to get 64 samples of various sinewave frequencies at 16KHz sampling frequency:
close all; clear; clc;
dt=1/16000;
freq = 8000;
t=-dt;
for i=1:64,
t=t+dt;a(i)=sin(2*pi*freq*t);
end
plot(a,'-o'); grid on;
for freq = 1000, the output graph is
The graph seems normal upto 2000, but at 3000, the graph is
We can see that the amplitude changes during every cycle
Again, at 4000 the graph is
Not exactly a sinewave, but the amplitude is as expected during every cycle and if I play it out it sounds like a single frequency tone
But again at 6000 we have
and at 8000 we have
Since the sampling frequency is 16000 I was assuming that I should be able to generate sinewave samples for upto 8000, and I was expecting the graph I got at 4000 to appear at 8000. Instead, even at 3000, the graph starts to look weird
If I change the sampling frequency to 32000 and the sinewave frequency to 16000, I get the same graph that I am getting now at 8000. Why does matlab behave this way?
EDIT:
at freq = 7900
This is just an artifact of aliasing. Notice how the vertical axis for the 8kHz graph only goes up to 1.5E-13? Ideally the graph should be all zeros; what you're seeing is rounding error.
Looking at the expression for computing the samples at 16kHz:
x(n) = sin(2 * pi * freq * n / 16000)
Where x is the signal, n is the integer sample number, and freq is the frequency in hertz. So, when freq is 8kHz, it's equivalent to:
x(n) = sin(2 * pi * 8000 * n / 16000) = sin(pi * n)
Because n is an integer, sin(pi * n) will always be zero. 8kHz is called the Nyquist frequency for a sampling rate of 16kHz for this reason; in general, the Nyquist frequency is always half the sample frequency.
At 3kHz, the signal "looks weird" because some of the peaks are at non-integer multiples of 16kHz, because 16 is not evenly divisible by 3. Same goes for the 6kHz signal.
The reason they still sound like pure sine tones is because of how the amplitude is interpolated between samples. The graph uses simple linear interpolation, which gives the impression of harsh edges at the samples. However, a physical loudspeaker (more precisely, the circuitry which drives it) does not use linear interpolation directly. Instead, a small filter circuit is used to smooth out those harsh edges (aka anti-aliasing) which removes the artificial frequencies above the aforementioned Nyquist frequency.
That is problem of matlab but a nature of sampling.
16KHz sampling makes 16K (16,000) sampled data per second. 8KHz signal has 8K (8000) cycles per second. So two sample data per a cycle.
Two is minimum number of data per cycle. This is know a part of "sampling theorem".
Let try to show two cycles with three points on graph, you may understand that its impossible to show two cycles by three points. In the same way, you can't show 2N cycles by (2N-1) points.
The effect seen for 8 kHz is as all other answers already mention aliasing effects and arises due to that the sine wave for 8 kHz is sin(2*pi*n*8000*1/16000) = sin(n*pi), which is explained in Drew McGovens answer. Luckily the amplitude is not the only parameter that defines the signal. The other parameter that is required to completely define the signal is the phase. This means that when doing for fourier analysis of the signal, it is still possible to find the right frequency. Try:
close all; clear; clc;
dt=1/16000;
freq = 7300;
t=-dt;
for i=1:64,
t=t+dt;a(i)=sin(2*pi*freq*t);
end
plot(a,'-o'); grid on;
figure; plot( linspace(1,16000,1000), abs(fft(a)) );
A side comment: some people might argue against using i as index variable since that can also be used as the imagiary number i. Personally I have nothing against using i since the runtime and overhead only is affected slightly and I always uses 1i. However, just make sure to use 1i consistently for the imaginary unit then.
I am doing FFT with matlab.the time period i am doing the fft on it is 1 second and it consist of 50000 equlay spaces samples. I want to test the FFT results. so I have given an input as below (wave) which is a complex of sinosuidal waves (and I have samples it by sampling frequency of 50 KHZ)and I expect to have the frequency magnitude results as I have given in the input. the results are ok for low frequency ranges but for the higher frequency (5752 Hz and 7993 Hz) results are 5.87 and 6.7 respectively (instead of 6 and ). what Is the origin of this big mistake ? how can I improve my results ?!
Here is the code:
t = 0:1/50000:1;
wave = 100*sin(2*pi*50*t)+1*sin (2*pi*123*t)+2*sin (2*pi*203*t)+3*sin(2*pi*223*t)+4*sin(2*pi*331*t)+5*sin(2*pi*2812*t)+6*sin(2*pi*5752*t)+7*sin(2*pi*7993*t);
SPEC = fft(wave);
L = size(SPEC,2);
x= (0:L/2-1);
Half_SPEC = abs(SPEC(1:L/2))/(L/2); %% removing the mirror side and ranging the domain
plot(x,Half_SPEC);
As Oli Charlesworth has pointed out, you are taking the FFT of 50001 points, which means that the frequency spacing is 1/50001.
Typically the FFT will give you the exact magnitude of your sinusoid only if its frequency is an exact multiple of the frequency spacing. Otherwise, the energy will be spread over multiple FFT bins in a process called spectral leakage.
You may confirm this by changing the number of samples such that the frequency of your sinusoids are a multiple of the frequency spacing:
t = 0:1/50000:1-1/50000;
Windowing the input signal can also help control the amount of leakage.
I am confused about the terminology used in scipy.signal.periodogram, namely:
scaling : { 'density', 'spectrum' }, optional
Selects between computing the power spectral density ('density')
where Pxx has units of V*2/Hz if x is measured in V and computing
the power spectrum ('spectrum') where Pxx has units of V*2 if x is
measured in V. Defaults to 'density'
(see: http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.periodogram.html)
1) a few tests show that result for option 'density' is dependent on signal and window length and sampling frequency (grows when signal length increases). How come? I would say that it is exactly density that should be not dependent on these things. If I take a longer signal I should just get more accurate estimation, not different result. Not to mention that dependence on window length is also very surprising.
Result diverges in the limit of infinite signal, which could be a feature of energy, but not power. Shouldn't the periodogram converge to real theoretical PSD when length increases? If, so, am I supposed to perform another normalisation outside of the signal.periodogram method?
2) to the contrary I see that alternative option 'spectrum' gives what I would previously call Power Spectrum Density, that is, it gives a resuls independent on window segment and window length and consistent with theoretical calculation. For instance for Asin(2PIft) a two sided solution yields two peaks at -f and f, each of height 0.25*A^2.
There is a lot of literature on this subject, but I get an impression that also there is a lot of incompatibile terminology, so I will be thankful for any clarification. The straightforward question is how to interpret these options and their units. (I am used to seeing V^2/Hz which are labeled "Power Spectrum Density").
Let's take a real array called data, of length N, and with sampling frequency fs. Let's call the time bin dt=1/fs, and T = N * dt. In frequency domain, the frequency bin df = 1/T = fs/N.
The power spectrum PS (scaling='spectrum' in scipy.periodogram) is calculated as follow:
import numpy as np
import scipy.fft as fft
dft = fft.fft(data)
PS = np.abs(dft)**2 / N ** 2
It has the units of V^2. It can be understood as follow. By analogy to the continuous Fourier transform, the energy E of the signal is:
E := np.sum(data**2) * dt = 1/N * np.sum(np.abs(dft)**2) * dt
(by Parseval's theorem). The power P of the signal is the total energy E divided by the duration of the signal T:
P := E/T = 1/N**2 * np.sum(np.abs(dft)**2)
The power P only depends on the Discrete Fourier Transform (DFT) and the number of samples N. Not directly on the sampling frequency fs or signal duration T. And the power per frequency channel, i.e., power spectrum SP, is thus given by the formula above:
PS = np.abs(dft)**2 / N ** 2
For the power spectrum density PSD (scaling='density' in scipy.periodogram), one needs to divide the PS by the frequency bin of the DFT, df:
PSD := PS/df = PS * N * dt = PS * N / fs
and thus:
PSD = np.abs(dft)**2 / N * dt
This has the units of V^2/Hz = V^2 * s, and now depends on the sampling frequency. That way, integrating the PSD over the frequency range gives the same result as summing the individual values of the PS.
This should explain the relations that you see when changing the window, sampling frequency, duration.
scipy.signal.peridogram uses the scipy.signal.welch function with 0 overlap. Therefore, the scaling is similar to the one provided by the welch function, density or spectrum.
In case of the density scaling, the amplitude will vary with window length, as the longer the window the higher the frequency resolution e.g. the \Delta_f is smaller. Since the estimated density is the average one, the smaller the \Delta_f the less zero energy is considered in the averaging.
As you have mentioned spectrum scaling is an integration of the energy density over the spectrum to produce the energy. Therefore, the integration over zero values does not affect the final value.
Fourier transform actually requires finite energy in an infinite duration of time series (like a decay). So, If you just make your time series sample longer by "duplicating", the energy will be infinite with an infinite duration.
My main confusion was on the "spectrum" option for scipy.signal.periodogram, which seems to create a constant energy spectrum even when the time series become longer.
Normally, 0.5*A^2=S(f)*delta_f, where S(f) is the power density spectrum. S(f)*delta_f, representing energy is constant if A is constant. But when using a longer duration of time series, delta_f (i.e. incremental frequency) is reduced accordingly, based on FFT procedure. For example, 100s time series will lead to a delta_f=0.01Hz, while 1000s time series will have a delta_f=0.001Hz. S(f) representing density will accordingly change.