I'm currently studying the following book: "Fourier Transform Spectroscopy Instrumentation Engineering", by Vidi Saptari. My question is related to the code below, based on the code from the book, Appendix C. The code below computes the interferogram of 3 waves with wavenumbers [cm-1] 5000, 10000 and 15000, respectively, and than performs an FFT to retrieve the information. The unscaled output has a magnitude of 1600, instead of 1.
clear;
% Sampling clock signal generation
samp_period_nm = 632.8 / 4; % sampling period in nm. 632.8 is the HeNe laser's wavelength
samp_period = 1 * samp_period_nm * 10^-7; % sampling period in cm.
scan_dist = 0.1; % mirror scan distance in cm.
no_elements = floor(scan_dist/samp_period);
x_samp = 0:samp_period:samp_period*no_elements; %Vector of clock signals in cm
xn_samp = x_samp .* (1 + rand(1, length(x_samp)));
v1 = 5000;
v2 = 10000;
v3 = 15000;
arg = 4 * pi * x_samp;
y = cos(arg*v1) + cos(arg*v2) + cos(arg*v3) ;
total_data = 2^18;
no_zero_fills=[total_data - length(y)];
zero_fills=zeros(1, no_zero_fills);
%triangular apodization
n_y = length(y);
points = 1:1:n_y;
tri = 1 - 1/(n_y) * points(1:n_y);
y = y.*tri; %dot product of interferogram with triangular apodization function
y = [y zero_fills]; %zero filling
% FFT operation
fft_y = fft(y);
% fft_y = fft_y / n_y;
% fft_y = fft_y * samp_period;
fft_y(1) = [];
n_fft=length(fft_y);
spec_y = abs(fft_y(1:n_fft/2)); %spectrum generation
nyquist = 1 / (samp_period * 4);
freq = (1:n_fft/2)/(n_fft/2)*nyquist; %frequency scale generation
figure();
plot(freq, spec_y); % plot of spectrum vs wave number
xlabel('Wavenumber [cm-1]');
ylabel('Intesity [V]');
By multiplying the result of the fft (fft_y) with dt = samp_period, as suggested here, the peak is as 0.025.
Following the same link's second solution, by dividing fft_y by n_y (the length of y), the magnitude is 0.25.
Clearly, I'm doing something wrong. Any help is appreciated.
Thanks,
The only thing you're doing wrong here is expecting the peaks in the spectrum to be 1. According to Parseval's theorem of DFT the energy of the time domain signal is equal to the energy of the frequency domain signal divided by the lenght of the sequence N. You can check this in your example:
td_energy = sum( abs(y).^2 )
fd_energy = sum( abs(fft_y).^2 )
td_energy - fd_energy / length(y) % won't be exactly zero because you deleted the zero frequency bin.
So the peaks in your spectrum don't represent the amplitudes of cosine waves in the time domain but their energy. Also note at this point that the energy is lower than you might expect as you padded a lot of zeros.
In practice, the average power of a certain frequency is often of greater interest. Consider the following code example
t = linspace(-4*pi, 4*pi, 2^16);
N = length(t); % DFT length
y = cos(t); % single cosine wave
y_pow = sum( abs(y).^2 ) / N; % is 0.5
fft_y = fft(y);
fft_y_pow = (sum( abs(fft_y).^2 ) / N) /N; % is 0.5
figure; plot(abs(fft_y)./N);
The power is obtained by averaging the energy by the length of the sequence N. If you divide the spectrum by N you obtain the average power per frequency. In the above example you recognize a single peak with height 0.5 that represents the single cos wave of amplitude 1 (and hence power 0.5).
Personally, I prefer scaling MATLAB's FFT output by 1/sqrt(N) and its IFFT output by sqrt(N). In this way, the energy of the time and frequency domain sequence are always equal.
if you want the same energy in the IFFT input and ouput you have to multiply
the IFFT output (time signal) by sqrt(N) where N is the size of the transform.
here's the code :
same thing for the FFT (divide output by sqrt(N));
hope it helps
Felix
N = 4096;
Freq = randn(N,1)+1j*randn(N,1);
Time = sqrt(N)*ifft(Freq,N);
FreqEn = sum(real(Freq).^2 + imag(Freq).^2);
TimeEn = sum(real(Time).^2 + imag(Time).^2);
TimeEn/Freq
Related
I have written a matlab function (Version 7.10.0.499 (R2010a)) to evaluate incoming FT signal and calculate the morlet wavelet for the signal. I have a similar program, but I needed to make it more readable and closer to mathematical lingo. The output plot is supposed to be a 2D plot with colour showing the intensity of the frequencies. My plot seems to have all frequencies the same per time. The program does make an fft per row of time for each frequency, so I suppose another way to look at it is that the same line repeats itself per step in my for loop. The issue is I have checked with the original program, which does return the correct plot, and I cannot locate any difference beyond what I named the values and how I organized the code.
function[msg] = mile01_wlt(FT_y, f_mn, f_mx, K, N, F_s)
%{
Fucntion to perform a full wlt of a morlet wavelett.
optimization of the number of frequencies to be included.
FT_y satisfies the FT(x) of 1 envelope and is our ft signal.
f min and max enter into the analysis and are decided from
the f-image for optimal values.
While performing the transformation there are different scalings
on the resulting "intensity".
Plot is made with a 2D array and a colour code for intensity.
version 05.05.2016
%}
%--------------------------------------------------------------%
%{
tableofcontents:
1: determining nr. of analysis f, prints and readies f's to be used.
2: ensuring correct orientation of FT_y
3:defining arrays
4: declaring waveletdiagram and storage of frequencies
5: for-loop over all frequencies:
6: reducing file to manageable size by truncating time.
7: marking plot to highlight ("randproblemer")
8: plotting waveletdiagram
%}
%--------------------------------------------------------------%
%1: determining nr. of analysis f, prints and readies f's to be used.
DF = floor( log(f_mx/f_mn) / log(1+( 1/(8*K) ) ) ) + 1;% f-spectre analysed
nr_f_analysed = DF %output to commandline
f_step = (f_mx/f_mn)^(1/(DF-1)); % multiplicative step for new f_a
f_a = f_mn; %[Hz] frequency of analysis
T = N/F_s; %[s] total time sampled
C = 2.0; % factor to scale Psi
%--------------------------------------------------------------%
%2: ensuring correct orientation of FT_y
siz = size(FT_y);
if (siz(2)>siz(1))
FT_y = transpose(FT_y);
end;
%--------------------------------------------------------------%
%3:defining arrays
t = linspace(0, T*(N-1)/N, N); %[s] timespan
f = linspace(0, F_s*(N-1)/N, N); %[Hz] f-specter
%--------------------------------------------------------------%
%4: declaring waveletdiagram and storage of frequencies
WLd = zeros(DF,N); % matrix of DF rows and N columns for storing our wlt
f_store = zeros(1,DF); % horizontal array for storing DF frequencies
%--------------------------------------------------------------%
%5: for-loop over all frequencies:
for jj = 1:DF
o = (K/f_a)*(K/f_a); %factor sigma
Psi = exp(- 0*(f-f_a).*(f-f_a)); % FT(\psi) for 1 envelope
Psi = Psi - exp(-K*K)*exp(- o*(f.*f)); % correctional element
Psi = C*Psi; %factor. not set in stone
%next step fits 1 row in the WLd (3 alternatives)
%WLd(jj,:) = abs(ifft(Psi.*transpose(FT_y)));
WLd(jj,:) = sqrt(abs(ifft(Psi.*transpose(FT_y))));
%WLd(jj,:) = sqrt(abs(ifft(Psi.*FT_y))); %for different array sizes
%and emphasizes weaker parts.
%prep for next round
f_store (jj) = f_a; % storing used frequencies
f_a = f_a*f_step; % determines the next step
end;
%--------------------------------------------------------------%
%6: reducing file to manageable size by truncating time.
P = floor( (K*F_s) / (24*f_mx) );%24 not set in stone
using_every_P_point = P %printout to cmdline for monitoring
N_P = floor(N/P);
points_in_time = N_P %printout to cmdline for monitoring
% truncating WLd and time
WLd2 = zeros(DF,N_P);
for jj = 1:DF
for ii = 1:N_P
WLd2(jj,ii) = WLd(jj,ii*P);
end
end
t_P = zeros(1,N_P);
for ii = 1:N_P % set outside the initial loop to reduce redundancy
t_P(ii) = t(ii*P);
end
%--------------------------------------------------------------%
%7: marking plot to highlight boundary value problems
maxval = max(WLd2);%setting an intensity
mxv = max(maxval);
% marks in wl matrix
for jj= 1:DF
m = floor( K*F_s / (P*pi*f_store(jj)) ); %finding edges of envelope
WLd2(jj,m) = mxv/2; % lower limit
WLd2(jj,N_P-m) = mxv/2;% upper limit
end
%--------------------------------------------------------------%
%8: plotting waveletdiagram
figure;
imagesc(t_P, log10(f_store), WLd2, 'Ydata', [1 size(WLd2,1)]);
set(gca, 'Ydir', 'normal');
xlabel('Time [s]');
ylabel('log10(frequency [Hz])');
%title('wavelet power spectrum'); % for non-sqrt inensities
title('sqrt(wavelet power spectrum)'); %when calculating using sqrt
colorbar('location', 'southoutside');
msg = 'done.';
There are no error message, so I am uncertain what exactly I am doing wrong.
Hope I followed all the guidelines. Otherwise, I apologize.
edit:
my calling program:
% establishing parameters
N = 2^(16); % | number of points to sample
F_s = 3.2e6; % Hz | samplings frequency
T_t = N/F_s; % s | length in seconds of sample time
f_c = 2.0e5; % Hz | carrying wave frequency
f_m = 8./T_t; % Hz | modulating wave frequency
w_c = 2pif_c; % Hz | angular frequency("omega") of carrying wave
w_m = 2pif_m; % Hz | angular frequency("omega") of modulating wave
% establishing parameter arrays
t = linspace(0, T_t, N);
% function variables
T_h = 2*f_m.*t; % dimless | 1/2 of the period for square signal
% combined carry and modulated wave
% y(t) eq. 1):
y_t = 0.5.*cos(w_c.*t).*(1+cos(w_m.*t));
% y(t) eq. 2):
% y_t = 0.5.*cos(w_c.*t)+0.25*cos((w_c+w_m).*t)+0.25*cos((w_c-w_m).*t);
%square wave
sq_t = cos(w_c.*t).*(1 - mod(floor(t./T_h), 2)); % sq(t)
% the following can be exchanged between sq(t) and y(t)
plot(t, y_t)
% plot(t, sq_t)
xlabel('time [s]');
ylabel('signal amplitude');
title('plot of harmonically modulated signal with carrying wave');
% title('plot of square modulated signal with carrying wave');
figure()
hold on
% Fourier transform and plot of freq-image
FT_y = mile01_fftplot(y_t, N, F_s);
% FT_sq = mile01_fftplot(sq_t, N, F_s);
% Morlet wavelet transform and plot of WLdiagram
%determining K, check t-image
K_h = 57*4; % approximation based on 1/4 of an envelope, harmonious
%determining f min and max, from f-image
f_m = 1.995e5; % minimum frequency. chosen to showcase all relevant f
f_M = 2.005e5; % maximum frequency. chosen to showcase all relevant f
%calling wlt function.
name = 'mile'
msg = mile01_wlt(FT_y, f_m, f_M, K_h, N, F_s)
siz = size(FT_y);
if (siz(2)>siz(1))
FT_y = transpose(FT_y);
end;
name = 'arnt'
msg = arnt_wltransf(FT_y, f_m, f_M, K_h, N, F_s)
The time image has a constant frequency, but the amplitude oscillates resempling a gaussian curve. My code returns a sharply segmented image over time, where each point in time holds only 1 frequency. It should reflect a change in intensity across the spectra over time.
hope that helps and thanks!
I found the error. There is a 0 rather than an o in the first instance of Psi. Thinking I'll maybe rename the value as sig or something. besides this the code works. sorry for the trouble there
in the below code i am trying to get the spectrogram of the non-stationary signalx
after running the code, i expected to see some thing like the posted inage "image_2" , frequency vs time representation. but the resut of the posted code is image_1.
can any one please guide me to get the correct spectrogram?
Code
% Time specifications:
Fs = 8000; % samples per second
dt = 1/Fs; % seconds per sample
StopTime = 1; % seconds
t = (0:dt:StopTime-dt); % seconds
t1 = (0:dt:.25);
t2 = (.25:dt:.50);
t3 = (.5:dt:.75);
t4 = (.75:dt:1);
%get a full-length example of each signal component
x1 = (10)*sin(2*pi*100*t);
x2 = (10)*sin(2*pi*200*t);
x3 = (10)*sin(2*pi*300*t);
x4 = (10)*sin(2*pi*400*t);
%construct a composite signal
x = zeros(size(t));
I = find((t >= t1(1)) & (t <= t1(end)));
x(I) = x1(I);
I = find((t >= t2(1)) & (t <= t2(end)));
x(I) = x2(I);
I = find((t >= t3(1)) & (t <= t3(end)));
x(I) = x3(I);
I = find((t >= t4(1)) & (t <= t4(end)));
x(I) = x4(I);
NFFT = 2 ^ nextpow2(length(t)); % Next power of 2 from length of y
Y = fft(x, NFFT);
f = Fs / 2 * linspace(0, 1, NFFT/2 + 1);
figure;
plot(f(1:200), 2 * abs( Y( 1:200) ) );
T = 0:.001:1;
spectrogram(x,10,9);
ylabel('Frequency');
axis(get(gcf,'children'), [0, 1, 1, 100]);
result of the posted code: Spectrogram_Image_1:
what i am trying to get: Image_2:
Update_1, image
Code:
%now call the spectrogram
spectrogram(x, window, noverlap, Nfft, Fs);
ylabel('Frequency');
axis(get(gcf,'children'), [0, 1]);
First, as with the first time that you asked this question, have you plotted your data in the time-domain (ie, plot(t, x)) and zoomed in on the transitions to ensure that your signal is what you think it is? Does it have the four different periods with distinct frequencies as you intend?
Assuming that it does, I'm pretty sure that your problem is that your spectrogram call is not doing what you want. I think that you are only getting an NFFT of 10, which means that your bins are 800 Hz wide, which is insufficient for resolving your frequencies that are only 100 Hz apart.
In my opinion, you should specify more parameters so that you know what it is doing. You'd specify an Nfft that would give the frequency resolution that you need. Something with more resolution than 100 Hz (let's try 25 Hz), but not requiring so many points that it is longer than the duration where you have stable frequencies (so, less than 0.25 sec, which means less than 2000 points).
To see how to specify the length of the FFT, I looked at the documentation: http://www.mathworks.com/help/signal/ref/spectrogram.html
Based on the docs I'd try the five parameter version: spectrogram(x,window,noverlap,nfft,fs)
For you code, where Fs and x are as you have already defined them, the spectrogram call would look like:
%define FFT parameters
des_df_Hz = 25; %desired frequency resolution for the display, Hz
Nfft = round(FS / des_df_Hz); %general rule for FFT resolution
Nfft = 2*Nfft; %double the bins to account for spreading due to windowing
Nfft = 2*round(0.5*Nfft); %make Nfft an even number
window = Nfft; %make your window the same length as your FFT
noverlap = round(0.95); %overlap a lot to make the plot pretty
%now call the spectrogram
spectrogram(x, window, noverlap, Nfft, Fs,'yaxis');
I have a simple sinus signal with some noise.
If I try a simple FFT algorithm I get the amplitude of the signal(23) and the freq(2).
If I try the pwelch from Matlab with hanning window I am getting the right freq but the amplitude is wrong. How can I obtain the real amplitude?
This is the code I am using for the pwelch:
time = 0:0.01:50;
frequency = 2;
amplitude = 23;
y = amplitude * sin (2 * pi * frequency * time);
y= y + 6 * randn(size(time));
y = y - mean(y);
N = length(y);
Fs = 100;
NFFT = 2^nextpow2(N);
M = 4396;
w = hanning(M);
[Pyy,Fy] = pwelch(y, w,[],M,Fs);
plot(Fy,Pyy);
IMHO, you cannot due to the spectral leakage. If you see the Hanning window that you are using, it is a low pass filter (when centred and normalised). It will reduce the power at the main frequencies and it will introduce power at other frequencies, i.e. smoothing. But, it is an aperiodic signal with infinite duration in the frequency domain, which cannot be computed.
figure;
plot(w);
title(['Hanning window with ', num2str(M), 'points']);
You can read more here.
I want to recover a time signals from a given power spectral density, assuming a normal distribution of the original signal:
PSD; % [(m/s)^2/Hz] given spectrum
T = 60; % [s] length of original signal
dt = 0.005; % [s] time step of original signal
N = T/dt; % [-] number of samples
NFFT = 2^nextpow2(N); % [-] number of bins for FFT
fs = 1/dt; % [Hz] sampling frequency
ASD = sqrt(PSD); % [(m/s)/sqrt(Hz)] get amplitude spectrum
omega = 2*pi*rand(NFFT/2,1); % [rad] generate phase vector
Z = ASD.*exp(1i*omega); % create complex amplitude vector
Z = [0;Z;flipud(conj(Z))]; % extend to satisfy symmetry
Y = real(ifft(Z)); % inverse FFT
[PSDY,f] = pwelch(Y,[],[],NFFT,fs); % generate PSD from Y to compare
The results show a power spectrum several orders of magnitude lower than the original, but the shape matches very good. I guess there is something wrong with the units or there might be a scaling factor missing. I'm not sure about the units of the time signal after ifft, since the amplitude has [(m/s)/sqrt(Hz)].
I believe there are two problems here.
First, I think that the PSD as you define it (or rather, as you use it) is in the wrong units.
When you define the signal as
Z = ASD.*exp(1i*omega);
then ASD should be in m/s and not in (m/s)/Hz.
So you should do something like that:
ASD = sqrt(PSD*fs/2)
Now, since PSD is in (m/s)^2/Hz, ASD is in units of m/s.
Next, the ifft should be normalised. That is, you should define the Y as
Y = ifft(Z)*sqrt(NFFT);
One more thing, I am not sure if this is on purpose, but the following line
[PSDY,f] = pwelch(Y,[],[],NFFT,fs);
results in Y being divided into 8 parts (with length <NFFT) with 50% overlap. Each part is zero padded to length of NFFT.
A better practice would be to use something like
[PSDY,f] = pwelch(Y,L,L/2,L,fs);
for some L or
[PSDY,f] = pwelch(Y,NFFT,[],NFFT,fs);
if you insist. To find out more, go to http://www.mathworks.com/help/signal/ref/pwelch.html
In conclusion, this is your (modified) code:
PSD = 5; % [(m/s)^2/Hz] given spectrum
T = 60; % [s] length of original signal
dt = 0.005; % [s] time step of original signal
N = T/dt; % [-] number of samples
NFFT = 2^nextpow2(N); % [-] number of bins for FFT
fs = 1/dt; % [Hz] sampling frequency
ASD = sqrt(PSD*fs/2); % [(m/s)] get amplitude spectrum
omega = 2*pi*rand(NFFT/2,1); % [rad] generate phase vector
Z = ASD.*exp(1i*omega); % create complex amplitude vector
Z = [0;Z;flipud(conj(Z))]; % extend to satisfy symmetry
Y = ifft(Z)*sqrt(NFFT); % inverse FFT
[PSDY,f] = pwelch(Y,256,128,256,fs); % generate PSD from Y to compare
which results in
where the blue line is the estimate PSD.
Im writing a MATLAB code to detect frequencies in piano recording.
I used a C scale audio file that i recorded using my keyboard (C4 D4 E4 F4 G4 A4 B4 C5)
When i simply perform an FFT (without breaking into windows) then the fundamental frequencies have a higher amplitude which is perfectly fine.
However to be more accurate i did the following steps
1. Did a fast convolution of my audio signal with a Gaussian edge detection filter to obtain the envelope.
2. Implemented a peak detecting algorithm to find the note onsets.
3. Taking each Onset, i performed an FFT on each, so as to get the FFT of each note.
However, when i do this for the above mentioned audio file, i get wrong results, at times the harmonics have a higher amplitude than the 1st.
clear all;
clear max;
clc;
%% create 5s sample at 10kHz with tone from 1s to 2s
FS = 10000; % 10kHz
N=5*FS;
song = randn(N,2)/10;
song(FS:2*FS,:)=10*repmat(sin(261*pi*2*(0:FS)/FS)',1,2)+song(FS:2*FS,:);
P = 2000;
t=0:1/FS:(N-1)/FS; % define time period
song = sum(song,2);
song=abs(song);
%----------------------Finding the envelope of the signal-----------------%
% Gaussian Filter
x = linspace( -1, 1, P); % create a vector of P values between -1 and 1 inclusive
sigma = 0.335; % standard deviation used in Gaussian formula
myFilter = -x .* exp( -(x.^2)/(2*sigma.^2)); % compute first derivative, but leave constants out
myFilter = myFilter / sum( abs( myFilter ) ); % normalize
% fft convolution
myFilter = myFilter(:); % create a column vector
song(length(song)+length(myFilter)-1) = 0; %zero pad song
myFilter(length(song)) = 0; %zero pad myFilter
edges =ifft(fft(song).*fft(myFilter));
tedges=edges(P:N+P-1); % shift by P/2 so peaks line up w/ edges
tedges=tedges/max(abs(tedges)); % normalize
%---------------------------Onset Detection-------------------------------%
% This section does the peak picking algorithm
max_col = maxtab(:,1);
peaks_det = max_col/FS;
No_of_peaks = length(peaks_det);
%---------------------------Performing FFT--------------------------------
song_seg = song(max_col(1):max_col(2)-1);
L = length(song_seg);
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
seg_fft = fft(song_seg,NFFT);%/L;
f = FS/2*linspace(0,1,NFFT/2+1);
seg_fft2 = 2*abs(seg_fft(1:NFFT/2+1));
L5 = length(song_seg);
fmin = 60;
fmax = 1000;
region_of_interest = fmax>f & f>fmin;
froi = f(region_of_interest);
[p_max,loc] = max(seg_fft2(region_of_interest));
% index into froi to find the frequency of the peaks
p_max;
f_p_max = froi(loc);
[points, locatn] = findpeaks(seg_fft2(region_of_interest));
aboveMax = points > 0.4*p_max;
if any(aboveMax)
peak_points = points(aboveMax)
f_peak = froi(locatn(aboveMax))
end
end
What am I doing wrong here??? Really REALLY in need of some help here......
It can be seen that the f0 of D4 hasn't been detected at all, while the f0 of C4 and E4 have less amplitudes compared to their harmonics
Using the FFT, i found out the peak in the Frequency Domain. This may correspond to the fundamental note, if we are very lucky. Else, this may be the 1st harmonic generally.
I do not know whether the peak is at the fundamental or at the 1st harmonic.
My job is to find this out.
What i did was...
I have the "max_user_freq" variable that corresponds to the frequency at which the peak occurs in the FFT.
Note: "Spectrum[]" is the magnitude spec variable.
I calculate the maximum of the spectrum[m] where m ranges from frequency
Note : frequencies must be scaled to m. In my case : m=freq*length(fraw)/22050;
Complete Code:
f1=(max_freq/2)-0.05*max_freq;
f1=round(f1);
f2=(max_freq/2)+0.05*max_freq;
f2=round(f2);
m=[];
spec_index_fund_note=m;
spec_fund_max=spectrum(f1*length(fraw)/22050;
for m=f1:f2
spec_index_fund_note(m)=m*length(fraw)/22050;
if(spectrum(spec_index_fund_note(m))>spec_fund_max)
spec_fund_max=spectrum(spec_index_fund_note(m));
end
end
if((spectrum(max_freq_index)-specval_fund_note)/spectrum(max_freq_index)>)
display('1st Harmonic exceeds the fundamental, Retry');
end
Filtering abs(song) is fine for finding the envelope, but when it comes to finding the peaks you need to compute the fft the original signal (without the "abs").
To see the difference, try:
clear;
FS = 10000;
s=sin(261*pi*2*(0:FS)/FS);
NFFT = 2^nextpow2(length(s));
S1=fft(s,NFFT);
S2=fft(abs(s),NFFT);
f = FS/2*linspace(0,1,NFFT/2+1);
plot(f,abs(S1(1:NFFT/2+1))); % peak near 261
plot(f,abs(S2(1:NFFT/2+1))); % peak near 522
I code in matlab and develop applications that are useful for a Musician. I myself am a musician. So, i know the music theory t hat runs my algorithms.
I developed and algorithm in matlab that tells u the exact scale in which u are playing the piano. Have a Look
MATLAB MUSICIAN's BLOG