FFT Plot of an Audio Signal - MATLAB - matlab

I'm using MATLAB to plot a recorded sound using the FFT. I want to take the log of the y-axis but I don't know what I did if correct.
Currently, my FFT plotting code looks like this:
nf=1024; %number of point in DTFT
Y = fft(y,nf);
f = fs/2*linspace(0,1,nf/2+1);
plot(f,abs(Y(1:nf/2+1)));
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')
What I did is: plot(f,log(Y(1:nf/2+1)));. I replaced abs with log. Is this correct?

Applying log on the coefficients itself doesn't make any sense... especially since the spectra will be complex-valued in nature. However, some people usually apply the log on the magnitude of the spectra (hence the abs call) mostly for visualization purposes so that large values of the magnitude don't overwhelm the smaller values. Applying log in this case will allow the larger values to taper off and the spectrum can be visualized easier. but applying the straight log in my opinion isn't correct. The code you have provided plots the magnitude of the single-sided spectrum and so there's no need to change anything.
If you provided more insight as to why you want to use the log, that would be helpful but right now, I would say that the straight log is incorrect. However, if you really must use the log, apply it on the magnitude instead. Also, to prevent undefined behaviour, make sure you add 1 to the magnitude before applying the log so that zero values of your magnitude get mapped to zero, rather than undefined.
As such, do this instead:
nf=1024; %number of point in DTFT
Y = fft(y,nf);
f = fs/2*linspace(0,1,nf/2+1);
plot(f,log(1 + abs(Y(1:nf/2+1)))); %// Change
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')

Related

How to Calculate the transfer function and plot it after making fourier transform in frequency domain in terms of magnitude and phase?

I have a trouble of plotting the transfer function in terms of magnitude and phase after making the Fourier transform of two signals.
First, I have used excel xlxs to read the column to plot in time domain and frequency domain, then I calculate the transfer function and this goes fine.
But for magnitude and phase, I have trouble for plotting them. I tired to plot them, but this is totally incorrect. Would someone help me with this?
Here is the code and excel file also.
solarcell1 = xlsread('solarcell.xlsx','A2:C100005');
t=solarcell1(:,1);
N=length(t);
Channel1V = solarcell1(:,2);
Channel2V = solarcell1(:,3);
sig1=Channel1V;
sig2=Channel2V;
fs=1/((solarcell1(3)-solarcell1(2))*1e-3);
FA1=fs/length(sig1);
FA2=fs/length(sig2);
frange1=-fs/2:FA1:fs/2-FA1;
frange2=-fs/2:FA2:fs/2-FA2;
subplot(3,2,1);
plot(t,sig1);
hold on
plot(t,sig2);
title('Input and Output of Solar cell');
xlabel('Time');ylabel('Amplitude');
subplot(3,2,2);
plot(t,sig2);
title('Output');
xlabel('Time');ylabel('Amplitude');
z1=fftshift(fft(sig1))/length(t);
subplot(3,2,3);
plot(frange1,abs(z1));
title('Input');
xlabel('Freq.');ylabel('Amplitude');
z2=fftshift(fft(sig2))/length(t);
subplot(3,2,4);
plot(frange2,abs(z2));
title('Output');
xlabel('Freq.');ylabel('Amplitude');
TFC=z2./z1;
magnitude=20*log(abs(TFC));
phase=atan2(imag(TFC),real(TFC));
subplot(3,2,5);
bode(frange1(1002:N),magnitude(1002:N));
title('Magnitude');
xlabel('Freq.');ylabel('Magnitude');
subplot(3,2,6);
semilogx(frange1(1002:N),phase(1002:N));
title('Phase');
xlabel('Freq.');ylabel('Phase');
I can see three issues in the code you provided.
First, your usage of bode is incorrect. This function takes a dynamic model system as argument, and you gave two vectors frange1(1002:N)and magnitude(1002:N). For now, I suggest you simply use plot instead.
subplot(3,2,5);
plot(frange1(1002:N),magnitude(1002:N));
title('Magnitude');
xlabel('Freq.');ylabel('Magnitude');
Then, your usage of semilogx is also risky when you have negative values for the x-axis. With frange1=-fs/2:FA1:fs/2-FA1; you shouldn't ask for semilogx of it. For now, I suggest plot.
Finally, I suggest you use unwrap to plot the phase.
subplot(3,2,6);
plot(frange1(1002:N),unwrap(phase(1002:N)));
title('Phase');
xlabel('Freq.');ylabel('Phase');
It plots:

how to retrieve original signal from power spectrum

I have calculated power spectrum of signal. the steps are:
FFT of time signal
square of absolute value of FFT/length of signal i.e power spectrum
Now I want to convert it into time domain. What steps should I follow.
The reconstruction of the original signal from the frequency-domain requires both the magnitude and the phase information. So, as you compute the power spectrum and keep only the magnitude, you no longer have all the required information to uniquely reconstruct the original signal.
In other words, we can find examples where different signals have the exact same power spectrum. In that case retrieving which one of those different signals was the original one would thus not be possible.
As a simple illustration, let's say the original signal x is:
x = [0.862209 0.43418 0.216947544 0.14497645];
For sake of argument, let's consider some other signal y, which I've specially crafted for the purpose of this example as:
y = [-0.252234 -0.0835824 -0.826926341 -0.495571572];
As show in the following plots, those two signals might appear completely unrelated:
They do however share the same power spectrum:
f = [0:N-1]/N;
Xf = fft(x,N);
Yf = fft(y,N);
hold off; plot(f, Xf.*conj(Xf)/N, 'b');
hold on; plot(f, Yf.*conj(Yf)/N, 'r:');
xlabel('Normalized frequency');
legend('Px', 'Py')
title('Power spectrum');
As a result, someone who only sees the power spectrum and doesn't know that you started with x, could very well guess that you instead started with y.
That said, the fact that those signals have the same power spectrum could tell you that those signals aren't as unrelated as you might think. In fact those signals also share the same autocorrelation function in the time-domain:
Rx = xcorr(x);
Ry = xcorr(y);
t = [0:length(Rx)-1] - length(x) + 1;
hold off; stem(t, Rx, 'bo');
hold on; stem(t, Ry, 'rx');
legend('Rxx', 'Ryy');
xlabel('lag');
title('Autocorrelation');
This is to be expected since the autocorrelation can be obtained by computing the inverse transform (with ifft) of the power spectrum. This, however, is about as much as you can recover in the time domain. Any signal with this autocorrelation function would be as good a guess as any for the original signal. If you are very motivated you could attempt to solve the set of non-linear equations that are obtained from the definition of the autocorrelation and obtain a list of possibles signals. That would still not be sufficient to tell which one was the original, and as you noticed when comparing my example x and y, there wouldn't be a whole lot to make of it.
The easiest way to see the non-uniqueness of the power (or amplitude) spectrum for describing the time domain signal is that both white noise and the delta function in the time domain have the same power (or amplitude) spectrum - a constant - in the frequency domain.

Matlab not plotting the exact fourier signal

I'm trying to plot a simple signal in fourier domain using Matlab. It's not plotting the correct signal. Here is my code:
clc;
clear all;
close all;
x=1:0.001:10;
f1=sin(2*pi*10*x);
f2=sin(2*pi*15*x);
f3=sin(2*pi*30*x);
f=f1+f2+f3;
plot(2*pi*x,fft(f1));
figure
plot(x,fft(f1));
I've expected a peak at 10 since the frequency is 10. But it is giving a peak at some other point
Here are the two plot images:
This is the image for plot(x,fft(f1))
This is the image for plot(2*pi*x,fft(f1))
It is not showing the peak at 10.I even tried using abs(fft(f1)). No luck :/
Isn't it the correct way to plot signal in fourier domain?
The fft function assumes unit time step. In order to correct for non unit time step you need to define the frequency component based on the nyquist rate. The following code plots the magnitude of the fft with the correct frequency axis.
clc;
clear all;
close all;
x=1:0.001:10;
% ^ this is your sampling time step
f1=sin(2*pi*10*x);
f2=sin(2*pi*15*x);
f3=sin(2*pi*30*x);
% bounds of fourier transform based on sampling rate
Fs = 1/0.001;
ff = linspace(-Fs/2,Fs/2,numel(x));
F1 = fftshift(fft(f1)/numel(x));
F2 = fftshift(fft(f2)/numel(x));
F3 = fftshift(fft(f3)/numel(x));
figure();
plot(ff,abs(F1),'-r'); hold on;
plot(ff,abs(F2),'-b');
plot(ff,abs(F3),'-k');
Edit: To answer OPs question in the comment.
Speaking in normalized frequency units (assuming sampling rate of 1). The fft function returns the frequency response from 0 to 2*pi radians, but due to some signal processing properties and the way that discrete signals are interpreted when performing an FFT, the signal is actually periodic so the pi to 2*pi section is identical to the -pi to 0 section. To display the plot with the DC component (0 frequency) in the center we use fftshift which does a circular shift equal to 1/2 the length of the signal on the data returned by fft. Before you take the ifft make sure you use ifftshift to put it back in the right place.
Edit2: The normalization term (/numel(x)) is necessary to estimate the continuous time fourier transform using the discrete fourier transform. I don't remember the precise mathematical reason off the top of my head but the examples in the MATLAB documentation also imply the necessity of this normalization.
Edit 3: The original link that I had is down. I may come back to add a more detailed answer but in the mean time I definitely recommend that anyone interested in understanding the relationship between the fundamentals of the FS, FT, DTFT, and DFT watch Professor Oppenheim's hilariously old, but amazingly informative and straightforward lectures on MIT OpenCourseWare.

How to produce a log scale FFT with MatLab

It's my first time performing an FFT within MatLab by experimenting with some example code from the MathWorks website. I was wondering if it was possible to take the code I have and transform the x axis to a log-scale representation rather than linear. I understand most of the code, but it is the x axis line of code that I'm still not 100% sure exactly what it is doing apart from the +1 at the end of the line, which is that fact that MatLab's indexing structure doesn't start on 0.
My code so far is:
[y,fs] = wavread('Wav/800Hz_2sec.wav');
NFFT = 4096;
Y = fft(y,NFFT)/length(y);
f = fs/2*linspace(0,1,NFFT/2+1);
plot(f,2*abs(Y(1:NFFT/2+1))
frequency usually comes out in linear scale from Discrete Fourier Transform. if you want, you can make a new frequency vector in log scale and interpolate the results you already have
fnew=fs/2.*logspace(log10(fs/length(y)),0,npts);
Ynew= interp1(f,Y(1:NFFT/2+1),fnew);
where npts is the length of your new frequency vector. for just plotting
loglog(f,2*abs(Y(1:NFFT/2+1));
honestly IMO, the interpolation thing doesn't work very well because FFT of real signals produces strong peaks and troughs in spectra, so unless you smooth your spectrum first, the interpolated spectrum won't look as nice

Confusion in figuring out the relation between actual frequency values and FFT plot indexes in MATLAB

I know that there are a lot of similar questions to this, I am still unable to figure out the answer.
Let's say we have time signal in MATLAB:
t=0:1/44100:1
and a cosine signal with frequency 500Hz:
x=cos(2*pi*500*t);
Now, I am trying to plot the magnitude spectrum obtained using the fft command on signal x
FFT=abs(fft(x))
plot(FFT)
According to the theory, we should get two peaks in the plot, one at -500 Hz and the other at 500Hz.
What I don't understand is that I do get two peaks but I can't figure out at what frequencies these peaks are. I know there is a way to figure out the frequency using the FFT index, length of the input signal and the sampling frequency but I still can't calculate the frequency.
I know that there are methods to align the FFT plots so that the peaks lie at the index number of the frequency they represent by using the fftshift function, but what I want is to figure out the frequency using the the plot resulting from simply calling this function:
FFT=fft(x)
In this case, I already know that signal contains a cosine of 500Hz, but what if the signal that we want to get the FFT of is not known before time. How can we get the frequency values of the peaks in that sample using the output from the fft function?
You need to generate the frequency array yourself and plot your FFT result against it.
Like this:
function [Ycomp, fHz] = getFFT(data, Fs)
len = length(data);
NFFT = 2^nextpow2(len);
Ydouble = fft(data, NFFT)/len; % Double-sided FFT
Ycomp = Ydouble(1:NFFT/2+1); % Single-sided FFT, complex
fHz = Fs/2*linspace(0,1,NFFT/2+1); % Frequency array in Hertz.
semilogx(fHz, abs(Ycomp))
end
You will see peaks at 500 Hz and Fs - 500 Hz (i.e. 44100 - 500 = 43600 Hz in your particular case).
This is because the real-to-complex FFT output is complex conjugate symmetric - the top half of the spectrum is a "mirror image" of the bottom half when you are just looking at the magnitude and is therefore redundant.
Note that of plotting power spectra you can usually save yourself a lot of work by using MATLAB's periodogram function rather than dealing directly with all the details of FFT, window functions, plotting, etc.