How to get the full Fourier spectrum in MATLAB? - 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);

Related

High-frequency spur after performing an FFT in MATLAB

I have a modulated signal, and now I want to perform an FFT. However, I am getting a spur at a high frequency, which should not be there (and if it should, I have no clue as to why).
Lvl=[0.5,0.9,0.5,0.5,0.1,0.1,0.9,0.5];
fa=60; %the frequency of the parasitic source in hertz
np=2; %number of periods per bit
kl=length(Lvl);
t=0:0.01*np/fa:np*kl/fa;
Sig=sin(2*pi*fa*t);
for n=1:1:101
Sig(n)=Sig(n)*Lvl(1);
end
for n=102:1:201
Sig(n)=Sig(n)*Lvl(2);
end
for n=202:1:301
Sig(n)=Sig(n)*Lvl(3);
end
for n=302:1:401
Sig(n)=Sig(n)*Lvl(4);
end
for n=402:1:501
Sig(n)=Sig(n)*Lvl(5);
end
for n=502:1:601
Sig(n)=Sig(n)*Lvl(6);
end
for n=602:1:701
Sig(n)=Sig(n)*Lvl(7);
end
for n=702:1:801
Sig(n)=Sig(n)*Lvl(8);
end
plot(t,Sig)
%FFT
y = fft(Sig);
f = (0:length(y)-1)*(1/(0.01*np/fa))/length(y);
plot(f,abs(y))
title('Magnitude')
I'm expecting just a spike at 60Hz with spurs around it, but instead I'm getting that and a large spike at almost 3kHz with spurs around it.
This peak at almost 3 kHz should be there, since the fft of a real is signal symmetric around the nyquist frequency (actually complex conjugate). The nyquist frequency is half the samping frequency, in your case sampling is done at 3000 Hz, thus the nyquist frequency is 1500 Hz. If you look closer at the peak, you will see that it is at 2940 Hz (which is 3000-60 Hz), due to the fact that the fft is mirrored around 1500 Hz.
There are plenty of sources that explain why this is a property of the Fourier transform (e.g. here).
The actual Fourier transform would be mirrored around the zero frequency, but the fft gives you the fast Fourier transform, which is mirrored around the nyquist frequency. You can use fftshift to center the spectrum around the zero frequency.
I took the liberty to shorten your code, by avoiding repetition of several for-loops, and added the fftshift. Since your signal is real, you can also choose to show only one side of the fft, but I'll leave that up to you.
Lvl=[0.5,0.9,0.5,0.5,0.1,0.1,0.9,0.5];
fa=60; % the frequency of the parasitic source in hertz
np=2; % number of periods per bit
kl = length(Lvl);
dt = 0.01*np/fa; % time step
Tend = np*kl/fa - dt; % time span
t = 0:dt:Tend; % time vector
N = length(t); % number samples
Sig=sin(2*pi*fa*t);
for n = 1:kl
ids = (1:100) + (n-1)*100;
Sig(ids) = Sig(ids).*Lvl(n);
end
% FFT
Y = fft(Sig);
fv = (0:N-1)/(N*dt); % frequency range
% FFT shift:
Y_shift = fftshift(Y);
fv_shift = (-N/2:N/2-1)/(N*dt); % zero centered frequency vector
% Plot
figure(1); clf
subplot(311)
plot(t,Sig)
title('Signal')
subplot(312)
plot(fv,abs(Y))
title('FFT Magnitude')
subplot(313)
plot(fv_shift,abs(Y_shift))
title('FFT Magnitude zero shift')

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

How to find the frequency of a periodic sound signal?

I'm working on sound signals of a walking pattern, which has obvious regular patterns:
Then I thought I can get the frequency of walking (approximately 1.7Hz from the image) using FFT function:
x = walk_5; % Walking sound with a size of 711680x2 double
Fs = 48000; % sound frquency
L=length(x);
t=(1:L)/Fs; %time base
plot(t,x);
figure;
NFFT=2^nextpow2(L);
X=fft(x,NFFT);
Px=X.*conj(X)/(NFFT*L); %Power of each freq components
fVals=Fs*(0:NFFT/2-1)/NFFT;
plot(fVals,Px(1:NFFT/2),'b','LineSmoothing','on','LineWidth',1);
title('One Sided Power Spectral Density');
xlabel('Frequency (Hz)')
ylabel('PSD');
But then it doesn't give me what I expected:
FFT result:
zoom image has lots of noises:
and there is no information near 1.7Hz
Here is the graph from log domain using
semilogy(fVals,Px(1:NFFT));
It's pretty symmetric though:
I couldn't find anything wrong with my code. Do you have any solutions to easily extract the 1.7Hz from the walking pattern?
here is the link for the audio file in mat
https://www.dropbox.com/s/craof8qkz9n5dr1/walk_sound.mat?dl=0
Thank you very much!
Kai
I suggest you to forget about DFT approach since your signal is not appropriate for this type of analysis due to many reasons. Even by looking on the spectrum in range of frequencies that you are interested in, there is no easy way to estimate the peak:
Of course you could try with PSD/STFT and other funky methods, but this is an overkill. I can think of two, rather simple methods, for this task.
First one is based simply on the Auto Correlation Function.
Calculate the ACF
Define the minimum distance between them. Since you know that expected frequency is around 1.7Hz, then it corresponds to 0.58s. Let's make it 0.5s as the minimum distance.
Calculate the average distance between peaks found.
This gave me an approximate frequency of 1.72 Hz .
Second approach is based on the observation to your signal already has some peaks which are periodic. Therefore we can simply search for them using findpeaks function.
Define the minimum peak distance in a same way as before.
Define the minimum peak height. For example 10% of maximum peak.
Get the average difference.
This gave me an average frequency of 1.7 Hz.
Easy and fast method. There are obviously some things that can be improved, such as:
Refining thresholds
Finding both positive and negative peaks
Taking care of some missing peaks, i.e. due to low amplitude
Anyway that should get you started, instead of being stuck with crappy FFT and lazy semilogx.
Code snippet:
load walk_sound
fs = 48000;
dt = 1/fs;
x = walk_5(:,1);
x = x - mean(x);
N = length(x);
t = 0:dt:(N-1)*dt;
% FFT based
win = hamming(N);
X = abs(fft(x.*win));
X = 2*X(1:N/2+1)/sum(win);
X = 20*log10(X/max(abs(X)));
f = 0:fs/N:fs/2;
subplot(2,1,1)
plot(t, x)
grid on
xlabel('t [s]')
ylabel('A')
title('Time domain signal')
subplot(2,1,2)
plot(f, X)
grid on
xlabel('f [Hz]')
ylabel('A [dB]')
title('Signal Spectrum')
% Autocorrelation
[ac, lag] = xcorr(x);
min_dist = ceil(0.5*fs);
[pks, loc] = findpeaks(ac, 'MinPeakDistance', min_dist);
% Average distance/frequency
avg_dt = mean(gradient(loc))*dt;
avg_f = 1/avg_dt;
figure
plot(lag*dt, ac);
hold on
grid on
plot(lag(loc)*dt, pks, 'xr')
title(sprintf('ACF - Average frequency: %.2f Hz', avg_f))
% Simple peak finding in time domain
[pkst, loct] = findpeaks(x, 'MinPeakDistance', min_dist, ...
'MinPeakHeight', 0.1*max(x));
avg_dt2 = mean(gradient(loct))*dt;
avg_f2 = 1/avg_dt2;
figure
plot(t, x)
grid on
hold on
plot(loct*dt, pkst, 'xr')
xlabel('t [s]')
ylabel('A')
title(sprintf('Peak search in time domain - Average frequency: %.2f Hz', avg_f2))
Here's a nifty solution:
Take the absolute value of your raw data before taking the FFT. The data has a ton of high frequency noise that is drowning out whatever low frequency periodicity is present in the signal. The amplitude of the high frequency noise gets bigger every 1.7 seconds, and the increase in amplitude is visible to the eye, and periodic, but when you multiply the signal by a low frequency sine wave and sum everything you still end up with something close to zero. Taking the absolute value changes this, making those amplitude modulations periodic at low frequencies.
Try the following code comparing the FFT of the regular data with the FFT of abs(data). Note that I took a few liberties with your code, such as combining what I assume were the two stereo channels into a single mono channel.
x = (walk_5(:,1)+walk_5(:,2))/2; % Convert from sterio to mono
Fs = 48000; % sampling frquency
L=length(x); % length of sample
fVals=(0:L-1)*(Fs/L); % frequency range for FFT
walk5abs=abs(x); % Take the absolute value of the raw data
Xold=abs(fft(x)); % FFT of the data (abs in Matlab takes complex magnitude)
Xnew=abs(fft(walk5abs-mean(walk5abs))); % FFT of the absolute value of the data, with average value subtracted
figure;
plot(fVals,Xold/max(Xold),'r',fVals,Xnew/max(Xnew),'b')
axis([0 10 0 1])
legend('old method','new method')
[~,maxInd]=max(Xnew); % Index of maximum value of FFT
walkingFrequency=fVals(maxInd) % print max value
And plotting the FFT for both the old method and the new, from 0 to 10 Hz gives:
As you can see it detects a peak at about 1.686 Hz, and for this data, that's the highest peak in the FFT spectrum.

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')

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