Obtain the envelop of a signal using MATLAB - matlab

I'm trying to extract the peaks of an audio file. I have the following code to extract the envelope of my amplitude spectrum. However I'm not getting the desired output graph. Can someone tell me what adjustment I need to make to get a proper graph. Here's my code:
[song,FS] = wavread('c scale fast.wav');
P=20000/44100*FS; % length of filter
N=length(song); % length of song
t=0:1/FS:(N-1)/FS; % define time period
% Plot time domain signal
figure(1);
subplot(3,1,1)
plot(t,(3*abs(song)))
title('Wave File')
ylabel('Amplitude')
xlabel('Length (in seconds)')
xlim([0 1.1])
xlim([0 N/FS])
% 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
% Plot Gaussian Filter
subplot(3,1,2)
plot(myFilter)
title('Edge Detection Filter')
% 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/2:N+P/2-1); % shift by P/2 so peaks line up w/ edges
tedges=tedges/max(abs(tedges)); % normalize
% Plot song filtered with edge detector
subplot(3,1,3)
plot(1/FS:1/FS:N/FS,tedges)
title('Song Filtered With Edge Detector')
xlabel('Time (s)')
ylabel('Amplitude')
ylim([-1 1.1])
xlim([0 N/FS])
This is the graph i get for the above code and im focusing on the "song filtered with edge detector" plot
And this is the "song filtered with edge detector" plot I NEED to get

You can use a Hilbert transform to get the envelope. Technically, this returns the analytic signal. You get the envelope with the following line:
envelope = abs(hilbert(Song));
What the Hilbert transform does is to take the fft of the input, zeroes out the negative frequencies, and then does an ifft. The real part of the transform is the original signal, the imaginary part is the transformed signal. The absolute value of the real and imaginary part is the envelope, and the argument (angle(hilbert(Song))) is the instantaneous phase.

I assume you want to obtain the envelope of the signal (not of its spectrum as you say). If that is the case, for envelope detection I would use a lowpass filter applied to abs(song) (you can substituye abs by a similar non-linear function). The cutoff frequency of the filter should be (slightly) above the highest frequency of the envelope variations.
You are applying the filter directly to the waveform, and the filter does not seem to be a lowpass filter. Perhaps if you explain your approach we can provide more focused help.
By the way, if your song is much longer than your filter, the zero-padding followed by fft followed by ifft seems to be a slow way of doing the filtering. Why not just use conv?

Related

Matlab FFT - Scaling y-axis

As a data basis, I have measured data the volts were recorded.
Matlab will now be used to perform an FFT.
I have the following questions:
- What unit do I have on the ordinate axis after the FFT? Also volts?
- How is scaled correctly? By hiding the negative frequencies (Nyquist), I would actually have to double the amplitude, right?
- Do I have to multiply all values of the FFT again with 20 * log10 (FFT) to represent the ordinate in db?
Thank you so much for your support!
Frank
Matlab example:
load('TimeDomain.mat')%loading of the time domain signal
L=2500; %length of the signal
Fs=500000;%sampling frequency
N=2^nextpow2(L);%scale factor
t=(0:L-1)*10^-3;%time domain array
f=linspace(0,Fs/2,length(t));%frequency domain array
FFT=abs(fft(Timedomain,N));
figure(1)
plot(f,FFT(1:2500))
Yes, after the FFT, the unit of the ordinate axis will still be volts.
You can scale it by dividing by the number of samples of your signal, then you can indeed multiplying by two (except the first and last elements that represents respectively the frequency 0 and Fs/2) if you want to plot all the spectrum in the positive side.
Then if you want to plot in dB, you can use the function mag2db which applies the formula that you said.
I found some weirdnesses in your code, so I suggest some fixes. My Timedomain signal is a 100 kHz sine at 1 V.
Fs = 500000; % sampling frequency
L = 2500; % length of the signal
t = (0:L-1)/Fs; % time domain array
f = linspace(0, Fs/2, L/2+1); % frequency domain array
Timedomain = cos(2*pi*100000*t); % Input signal
FFT = abs(fft(Timedomain)/L);
FFT(2:L/2) = 2*FFT(2:L/2);
%% Plots
subplot(2,1,1);
plot(f, FFT(1:L/2+1)); xlabel('Frequency (Hz)'); ylabel('Tension (V)');
subplot(2,1,2);
plot(f, mag2db(FFT(1:L/2+1))); xlabel('Frequency (Hz)'); ylabel('Tension (dBV)');
This returns:

Measuring peaks and the distance between them in my plot

I will keep the explanation, how my codes works, very short. I advise you to try this code yourself, so perhaps you understand it better that way. I have an audio-file and read it in my code. Now i switch from the time domain to the frequency domain by using the function FFT. But the only difference is, that i am performing an STFT on my audio signal. I do it every 30ms, until to the length of my signal. I am aware, that there are many different function in matlab, which also can perform this easily, but there are not giving me the results i need. Now, i am plotting many different frequency spectrums every 30ms. But i split up my signal in three frequency bands. They are called LOW, MEDIUM and HIGH. Basically, this means I have 3 different spectrums plotting every 30ms. The next step I do, is summing all the magnitudes from ONE frequency spectrum together, this means I have ONE VALUE per frequency spectrum, which are being squared.
Now, i have the power from every spectrum ! And all of these values are being plotted in my code. I am only plotting the power values, otherwise my code performance time would be extremely slow.
Btw, the code looks long, but there are two for loop. In the first, i read the low spectrum and when it is finished, the second starts with the medium and high spectrum. Basically they are the same. I am aware, i can probably do that with findpeaks or something similar. But how can i write/pull that of? Or what the necessary steps to do that. At the end, i included a file, Hopefully you can see that.
I want to measure the peaks and get the distance between them from the red plot.
EDIT:
Ok, i got the peaks, but not in the way i imagined. I want to show the peaks, which are above 5000-line. Sorry for not being clear at the beginning. See my plot, what i mean. I want to say my code, that only the peaks should be measured, which are above the 5000-line.
[pks, locs] = findpeaks(ValuesOfYc);
p=plot(x,ValuesOfYc,'r-' ,x(locs), pks,'ob');
This is, what I did above, in my first loop. How should i go on from there?
clear;
clc;
%% MATLAB
%% read file
%_________________________________________
[y,fs]=audioread('Undertale - Megalovania.wav');
% audioread = read wav -file
% y = contains the audio signal
% fs = 44100
% 'UnchainMyHeart' = name of the wav-file
%_________________________________________
%% PARAMETER FOR STFT
%_________________________________________
t_seg=0.03; % length of segment in ms
fftlen = 4096; %FFT-Points
% Defining size of frequency bands
f_low= 1:200; %lower frequencies
f_medium= 201:600; %medium frequencies
f_high= 601:1000; %higher frequencies
%__________________________________________
%% CODE
segl =floor(t_seg*fs);
windowshift=segl/2;
% defining the size of the window shift
window=hann(segl);
% apply hann function on segment length (30 ms)
window=window.';
% transpose vector
si=1;
% defining start index
ei=segl;
% defining end index
N=floor( length(y)/windowshift - 1);
% Calculates the number, how often the window has to shift
% until to length of the audio signal
f1=figure;
% Generating new window
f=0:1:fftlen-1;
f=f/fftlen*fs;
% defining frequency vector
Ya=zeros(1,fftlen);
ValuesOfYc = NaN(1,N);
ValuesOfYd = NaN(1,N);
ValuesOfYe = NaN(1,N);
x =(1:N)*windowshift/fs;
% defining x-axis
for m= 1:1:N
y_a = y(si:ei);
% a segment is taken out from audio signal length(30ms)
y_a= y_a.*window;
% multiplying segment with window (hanning)
Ya=fft(y_a, fftlen);
% Applying fft on segment
Yb=abs(Ya(1:end/2)).^2;
% Squaring the magnitudes from one-sided spectrum
drawnow; % Updating the graphical values
figure(f1);
% Showing the power values
%% frequency bands
y_low = Yb(f_low); % LOW frequency spectrum
Yc=sum(y_low);
% Summing all the power values from one frequency spectrum together
% so you get one power value from one spectrum
ValuesOfYc(m) = Yc;
%Output values are being saved here, which are generated from the for
%loop
% m = start variable from for loop
[pks, locs] = findpeaks(ValuesOfYc);
subplot(2,1,1)
p=plot(x,ValuesOfYc,'r-', x(locs(pks>=5000)), pks(pks>=5000),'ob');
p(1).LineWidth =0.5;
xlabel('time (Audio length)')
ylabel('Power')
grid on
si=si+windowshift;
% Updating start index
ei=ei+windowshift;
% Updating end index
end
for o= 1:1:N
y_a = y(si:ei);
% a segment is taken out from audio signal length(30ms)
y_a= y_a.*window;
% multiplying segment with window (hanning)
Ya=fft(y_a, fftlen);
% Applying fft on segment
Yb=abs(Ya(1:end/2)).^2;
% Squaring the magnitudes from one-sided spectrum
drawnow; % Updating the graphical values
figure(f1);
% Showing the power values
[![enter image description here][1]][1]
%% frequency bands
y_medium = Yb(f_medium); % MEDIUM frequency spectrum
y_high = Yb(f_high); % HIGH frequency spectrum
Yd=sum(y_medium);
Ye=sum(y_high);
% Summing all the power values from one frequency spectrum together
% so you get one power value from one spectrum
ValuesOfYd(o) = Yd;
ValuesOfYe(o) = Ye;
%Output values are being saved here, which are generated from the for
%loop
% m = start variable from for loop
subplot(2,1,2)
p=plot(x, ValuesOfYd,'g-', x, ValuesOfYe,'b-' );
p(1).LineWidth =0.5;
xlabel('time (Audio length)')
ylabel('Power')
grid on
si=si+windowshift;
% Updating start index
ei=ei+windowshift;
% Updating end index
end

Ways to Compute Spectrum Matlab

I have a question while computing the spectrum of a time series in Matlab. I have read the documentations concerning 'fft' function. However I have seen two ways of implementation and both wgive me different results. I would appreciate to have some answer about this difference:
1st Method:
nPoints=length(timeSeries);
Time specifications:
Fs = 1; % samples per second
Fs = 50;
freq = 0:nPoints-1; %Numerators of frequency series
freq = freq.*Fs./nPoints;
% Fourier Transform:
X = fft(timeSeries)/nPoints; % normalize the data
% find find nuquist frequency
cutOff = ceil(nPoints./2);
% take only the first half of the spectrum
X = abs(X(1:cutOff));
% Frequency specifications:
freq = freq(1:cutOff);
%Plot spectrum
semilogy(handles.plotLoadSeries,freq,X);
2nd Method:
NFFT = 2^nextpow2(nPoints); % Next power of 2 from length of y
Y = fft(timeSeries,NFFT)/nPoints;
f = 1/2*linspace(0,1,NFFT/2+1);
% % Plot single-sided amplitude spectrum.
% plot(handles.plotLoadSeries, f,2*abs(Y(1:NFFT/2+1)))
semilogy(handles.plotLoadSeries,f,2*abs(Y(1:NFFT/2+1)));
I thought that it is not necessary to use 'nextpow' function in 'fft' function in Matlab. Finally, which is the good one?
THanks
The short answer: you need windowing for spectrum analysis.
Now for the long answer... In the second approach, you are using an optimised FFT algorithm useful when the length of the input vector is a power of two. Let's assume that your original signal has 401 samples (as in my example below) from an infinitely long signal; nextpow2() will give you NFFT=512 samples. When you feed the shorter, 401-sample signal into the fft() function, it is implicitly zero-padded to match the requested length of 512 (NFFT). But (here comes the tricky part): zero-padding your signal is equivalent to multiplying an infinitely long signal by a rectangular function, an operation that in the frequency domain translates to a convolution with a sinc function. This would be the reason behind the increased noise floor at the bottom of your semilogarithmic plot.
A way to avoid this noise increase is to create manually the 512-sample signal you want to feed into fft(), using a smoother window function instead of the default rectangular one. Windowing means just multiplying your signal by a tapered, symmetric one. There are tons of literature on choosing a good windowing function, but a typically accurate one with low sidelobes (low noise increase) is the Hamming function, implemented in MATLAB as hamming().
Here is a figure illustrating the issue (in the frequency domain and time domain):
...and the code to generate this figure:
clear
% Create signal
fs = 40; % sampling freq.
Ts = 1/fs; % sampling period
t = 0:Ts:10; % time vector
s = sin(2*pi*3*t); % original signal
N = length(s);
% FFT (length not power of 2)
S = abs(fft(s)/N);
freq = fs*(0:N-1)/N;
% FFT (length power of 2)
N2 = 2^nextpow2(N);
S2 = abs(fft(s, N2)/N2);
freq2 = fs*(0:N2-1)/N2;
t2 = (0:N2-1)*Ts; % longer time vector
s2 = [s,zeros(1,N2-N)]; % signal that was implicitly created for this FFT
% FFT (windowing before FFT)
s3 = [s.*hamming(N).',zeros(1,N2-N)];
S3 = abs(fft(s3, N2)/N2);
% Frequency-domain plot
figure(1)
subplot(211)
cla
semilogy(freq,S);
hold on
semilogy(freq2,S2,'r');
semilogy(freq2,S3,'g');
xlabel('Frequency [Hz]')
ylabel('FFT')
grid on
legend( 'FFT[401]', 'FFT[512]', 'FFT[512] with windowing' )
% Time-domain plot
subplot(212)
cla
plot(s)
hold on
plot(s3,'g')
xlabel('Index')
ylabel('Amplitude')
grid on
legend( 'Original samples', 'Windowed samples' )

How to plot frequency response for a transfer function of a band-pass filter in Matlab?

i want write a script to plot a graph for the transfer function [H(f)] for a band pass filter, |H(f)| against frequency and the phase of H(f) (degrees) against frequency, im very new to matlab so the syntax is not 100%, im getting confused because everything is auto formatted in matrix form.
below is my script:
% RCL circuit: band-pass filter
R=55590; L=0.9571; C=48.811*10.^(-9); % values of the Resistor and Capacitor
f=(0:60e3); w=2*pi*f; % frequency (f) range of measurements
H=(R./(sqrt(R^2+(w*L-(1./(w*C))).^2))); % Transfer Function
% Magnitude (absolute value) of the transfer function
plot(f,abs(H),'LineWidth',2); grid on; hold on
xlabel('Frequency [Hz]','FontSize',20); ylabel('|H(f)|','FontSize',20)
figure
plot(f,angle(H)*180/pi,'LineWidth',2); grid on; hold on
xlabel('Frequency [Hz]','FontSize',18);
ylabel('phase of H(f) [degrees]','FontSize',20)
this is the transfer function formula im using
below is another pic of what my experimental results were and an expected graph, i just dont understand why MATLAB isnt ploting what i want?
Are you aware of the bodeplot function?
The transfer function for a simple second order bandpass is:
You just need to insert your values into Matlab's tf function and plot it with bodeplot:
R = 55590;
L = 0.9571;
C = 48.811*10.^(-9);
% tf creates transfer function object
sys = tf( [R*C 0] , [L*C R*C 1]); % [R*C 0] vector of numerator coeffcients
% R*C*s + 0*1
% [L*C R*C 1] vector of denominator coeff.
% L*C*s^2 + R*C*s + 0*1
bodeplot(sys) % plot command to plot frequency response
% of magnitude and phase
and it plots:
Alternatively, if you need more outputs like magnitude and phase as variables, use bode, the plot is equal. But bodeplot offers more additional plot customization options.
Regarding your comment, that you need a linear axis:
you just need to add the following lines before the plot command:
P = bodeoptions; % handle to plot options
P.MagScale = 'linear';
P.MagUnits = 'abs';
bodeplot(sys,P) % plot command with custom options
so it looks as follows:
For adjusting the frequency axis limits, use:
P.XLim = [1 60e3];
or analogue for the magnitude:
P.YLim = [0 1];
I would advise against a linear frequency axis, but if you REALLY want it, you can use:
P.FreqScale = 'linear';
P.FreqUnits = 'Hz'; % optional
If you want to plot your experimental data together with the plot above, follow this example.
Use bode to get equally formatted data from your transfer function, like your experimental data and use semilogx to plot it.
freqVec = logspace(-1, 3, 5000);
[mag, phs] = bode(sys, freqVec * (2*pi));
mag = db(mag(:));
phs = phs(:);
figure;
subplot(211)
semilogx(freqVec, mag); hold on
semilogx(freqVec, experimentalDataMagnitude); hold off
grid on
title('System Bode Plot')
ylabel('Magnitude (dB)')
subplot(212)
semilogx(freqVec, phs); hold on
semilogx(freqVec, experimentalDataPhase); hold off
grid on
ylabel('Phase (deg)')
xlabel('Frequency (Hz)')

How do I obtain Energy spectrum of a signal after FFT in Matlab?

EDIT:
I stumbled on this explanation to obtain the energy spectrum from an IEEE paper(Open Circuit Fault Diagnosis in 3 phase uncontrolled rectifiers, Rahiminejad, Diduch, Stevenson, Chang).
"A recorded sample of the signal containing a number of samples equivalent to 4T is captured and its FFT is determined using an FFT size equal to the record length (where T is the fundamental period).
Assuming the FFT size is matched to 4 periods of a periodic waveform, every 4th FFT bin will coincide with a harmonic frequency, in particular the center of FFT bin 4k+1 will coincide with the kth harmonic frequency.
The energy of the kth harmonic is calculated as the sum of the squared magnitudes of the 5 consecutive FFT values centred at bin 4k+1. The additional FFT values are included in the harmonic energy calculation so as to reduce the sensitivity of the calculated energy to an error in the frequency estimate which oculd result in the kth harmonic peak shifting away from bin 4k+1."
I do not fully understand the passage above. In my limited understanding, the bold line
refers to the sum of the squared magnitudes of the output of function fft(), i.e. the complex fourier series coefficients.
Can someone please show some light into obtaining the energy spectrum ?
#fpe : I am not sure if ESD performs the same as energy spectrum. BTW, thanks alot for your answer:)
I am trying to plot the energy spectrum of a signal to look at the for example Normalised energy contained first three harmonics, energy ratio of fundamental to 2nd harmonics etc....
Here I have managed to get the Hanning window FFT amplitude-Hz and power-Hz. But, I have no idea how to get energy-Hz for each frequency components.
Any help is much appreciated !
function [f,Xall_Wnd]=fftplotExxx(time,X_input)
Fs = 20000; % Sampling frequency
x = X_input;
% Fast Fourier Transform
L = length (X_input); % Length of FFT
nfft = 2^nextpow2(L); % Next power of 2 from length of signal
%wave = wave.*hamming(length(wave));
x_HammingWnd = x.*hamming(L);
% Take fft, padding with zeros so that length(X)
%is equal to nfft
Xall_Wnd = fft(x_HammingWnd, nfft)/L; %hamming window fft
% FFT is symmetric, throw away second half
% Take the magnitude of fft of x
mx_Wnd = 2*abs(Xall_Wnd(1:nfft/2+1));
% To get Power of x(t) by sqr of magnitude
m2x_Wnd = mx_Wnd.^2;
% I am Not sure how to get energy spectrum
for i=1:L:nfft-L
E(i) = sum(Xall_Wnd(1:nfft/2+1).^2);
end
% Frequency vector
f = Fs/2*linspace(0,1,nfft/2+1);
% Generate the plot, title and labels.
subplot(2,2,1)
plot(time,X_input);
title('Time Domain')
xlabel('Time(s)')
subplot(2,2,2)
plot(f,m2x_Wnd);
title('Power Spectrum of x(t)');
xlabel('Frequency (Hz)');
ylabel('Normalised Power of fft');
subplot(2,2,3)
plot(f,mx_Wnd);
title('Hamming Window_ Spectrum of x(t)');
xlabel('Frequency (Hz)');
ylabel('Normalised Magunitude of fft');
subplot(2,2,4)
plot(f,E);
title('Energy Spectrum of x(t)');
xlabel('Frequency (Hz)');
ylabel('Energy');
end
Generally you can calculate the spectrum in this way:
h = spectrum.welch('hamming',2048,50);
PSD = psd(h,x(t),'nfft',2048,'fs',Fs);