Calculate area under power spectrum for certain frequency range - matlab

Below I present my code how I calculate power spectrum for my signal:
Fs=100;
n = length(x) (in my example always n=160);
whann = window(#hann,n).';
x = whann.*x';
xdft = fft(x);
xdft = (2/length(x))*xdft(1:length(x)/2+1);
xdft = abs(xdft);
xdft=xdft.^2;
freq = 0:Fs/length(x):Fs/2;
Now, I would like to calculate area under power spectrum but only for frequency range 4-6 Hz. The 32 first elements of vector freq look like this:
freq = [0,00 0,28 0,56 0,83 1,11 1,39 1,67 1,94 2,22 2,50 2,78 3,06 3,33 3,61 3,89 4,17 4,44 4,72 5,00 5,28 5,56 5,83 6,11 6,39 6,67 6,94 7,22 7,50 7,78 8,06 8,33]
So, I can find only area between 4,17 Hz- 6,11 Hz.
Could you advice me, how to calculate area under spectrum for specific frequency range (as I mentioned above for example 4-6 Hz)?
Thanks in advance for any help

I would proceed as follows:
idx = find(freq>=4 & freq<=6);
trapz(freq(idx),spectrum(idx))
If I understood your question right, what stated above should lead you to the results you wanna estimate.
EDIT
Since you don't have spectrum values for freq=4Hz and freq=6Hz, I would suggest to interpolate values like this:
int_spec = exp(interp1(log(freq),log(spec),log(4:.1:6),'linear','extrap'))
and then call
trapz(4:.1:6,int_spec)

Related

Calculate 3dB bandwidth at a peak from magnitude-frequency plot

I need to calculate the 3dB bandwidth from data containing Power in dB vs Frequency in Hz. For instance:
X =
2.9640 -5.0568
2.9705 -4.5819
2.9770 -4.1277
2.9835 -3.7016
2.9900 -3.3095
2.9965 -2.9560
3.0030 -2.6446
3.0095 -2.3776
3.0160 -2.1569
3.0225 -1.9839
3.0290 -1.8596
3.0355 -1.7847
3.0420 -1.7596
3.0485 -1.7849
3.0550 -1.8609
3.0615 -1.9877
3.0680 -2.1655
3.0745 -2.3944
3.0810 -2.6741
3.0875 -3.0044
3.0940 -3.3843
3.1005 -3.8126
3.1070 -4.2872
3.1135 -4.8051
3.1200 -5.3616
3.1265 -5.9505
I get the peak I am interested in with findpeaks builtin function:
[pks, locs, w, p] = findpeaks(X.data(:,2), 'MinPeakProminence',3);
fstpeak = locs(1);
frequency = X(fstpeak,1);
peak_magnitude = X(fstpeak,2);
I can obviously make a for loop and look forward and backward from fstpeak until I get a value of magnitude below peak_magnitude - 3, and then interpolate if more precision is required.
It seems a pretty common operation, but I have tried to find a builtin matlab function with no success. Is there a builtin function I can use, or a faster approach to the custom for loop?
I think your problem with doing this is going to be that your data is not monotonically increasing. Having said that, it does follow a nice curve - it rises to a maximum and then starts to decrease, and there is no noise. As such, you can split the curve in two shorter curves that are monotonically increasing/decreasing and use `interp1' to find the -3dB point.
frequency = X(:,1);
magnitude = (X:,2);
magnitude = magnitude - max(magnitude); % Normalise to maximum
indmax = find(magnitude == max(magnitude));
f1 = interp1(magnitude(1:indmax), frequency(1:indmax), -3);
f2 = interp1( magnitude(indmax:end), frequency(indmax:end), -3);
BW = f2 - f1;
This approach will fall down if you apply it to data that does not rise and then fall, or if you apply it to noisy data.

Summing Values based on Area in Matlab

Im trying to write a code in Matlab to calculate an area of influence type question. This is an exert from my data (Weighting, x-coord, y-coord):
M =
15072.00 486.00 -292
13269.00 486.00 -292
12843.00 414.00 -267
10969.00 496.00 -287
9907.00 411.00 -274
9718.00 440.00 -265
9233.00 446.00 -253
9138.00 462.00 -275
8830.00 496.00 -257
8632.00 432.00 -253
R =
-13891.00 452.00 -398
-13471.00 461.00 -356
-12035.00 492.00 -329
-11309.00 413.00 -353
-11079.00 467.00 -375
-10659.00 493.00 -333
-10643.00 495.00 -338
-10121.00 455.00 -346
-9795.00 456.00 -367
-8927.00 485.00 -361
-8765.00 467.00 -351
I want to make a function to calculate the sum of the weightings at any given position based on a circle of influence of 30 for each coordinate.
I have thought of using a for loop to calculate each point independently and summing the result but seems unnecessarily complicated and inefficient.
I also thought of assigning an intensity of color to each circle and overlaying them but I dont know how to change color intensity based on value here is my attempt so far (I would like to have a visual of the result):
function [] = Influence()
M = xlsread('MR.xlsx','A4:C310');
R = xlsread('MR.xlsx','E4:G368');
%these are my values around 300 coordinates
%M are negative values and R positive, I want to see which are dominant in their regions
hold on
scatter(M(:,2),M(:,3),3000,'b','filled')
scatter(R(:,2),R(:,3),3000,'y','filled')
axis([350 650 -450 -200])
hold off
end
%had to use a scalar of 3000 for some reason as it isnt correlated to the graph size
I'd appreciate any ideas/solutions thank you
This is the same but with ca. 2000 data points
How about this:
r_influence = 30; % radius of influence
r = #(p,A) sqrt((p(1)-A(:,2)).^2 + (p(2)-A(:,3)).^2); % distance
wsum = #(p,A) sum(A(find(r(p,A)<=r_influence),1)); % sum where distance less than roi
% compute sum on a grid
xrange = linspace(350,550,201);
yrange = linspace(-200,-450,201);
[XY,YX] = meshgrid(xrange,yrange);
map_M = arrayfun(#(p1,p2) wsum([p1,p2],M),XY,YX);
map_R = arrayfun(#(p1,p2) wsum([p1,p2],R),XY,YX);
figure(1);
clf;
imagesc(xrange,yrange,map_M + map_R);
colorbar;
Gives a picture like this:
Is that what you are looking for?

MATLAB code for calculating MFCC

I have a question if that's ok. I was recently looking for algorithm to calculate MFCCs. I found a good tutorial rather than code so I tried to code it by myself. I still feel like I am missing one thing. In the code below I took FFT of a signal, calculated normalized power, filter a signal using triangular shapes and eventually sum energies corresponding to each bank to obtain MFCCs.
function output = mfcc(x,M,fbegin,fs)
MF = #(f) 2595.*log10(1 + f./700);
invMF = #(m) 700.*(10.^(m/2595)-1);
M = M+2; % number of triangular filers
mm = linspace(MF(fbegin),MF(fs/2),M); % equal space in mel-frequency
ff = invMF(mm); % convert mel-frequencies into frequency
X = fft(x);
N = length(X); % length of a short time window
N2 = max([floor(N+1)/2 floor(N/2)+1]); %
P = abs(X(1:N2,:)).^2./N; % NoFr no. of periodograms
mfccShapes = triangularFilterShape(ff,N,fs); %
output = log(mfccShapes'*P);
end
function [out,k] = triangularFilterShape(f,N,fs)
N2 = max([floor(N+1)/2 floor(N/2)+1]);
M = length(f);
k = linspace(0,fs/2,N2);
out = zeros(N2,M-2);
for m=2:M-1
I = k >= f(m-1) & k <= f(m);
J = k >= f(m) & k <= f(m+1);
out(I,m-1) = (k(I) - f(m-1))./(f(m) - f(m-1));
out(J,m-1) = (f(m+1) - k(J))./(f(m+1) - f(m));
end
end
Could someone please confirm that this is all right or direct me if I made mistake> I tested it on a simple pure tone and it gives me, in my opinion, reasonable answers.
Any help greatly appreciated :)
PS. I am working on how to apply vectorized Cosinus Transform. It looks like I would need a matrix of MxM of transform coefficients but I did not find any source that would explain how to do it.
You can test it yourself by comparing your results against other implementations like this one here
you will find a fully configurable matlab toolbox incl. MFCCs and even a function to reverse MFCC back to a time signal, which is quite handy for testing purposes:
melfcc.m - main function for calculating PLP and MFCCs from sound waveforms, supports many options.
invmelfcc.m - main function for inverting back from cepstral coefficients to spectrograms and (noise-excited) waveforms, options exactly match melfcc (to invert that processing).
the page itself has a lot of information on the usage of the package.

How to store values in an array in MATLAB

Say I have performed FFT on a set of data and i have the frequency and amplitude values.
I want to find the highest amplitude in each FFT block. The I need to find the frequency and amplitude of the points that are atleast greater than 0.4 times the max amplitude and save them in an array.. How can i do this??
I tried the following method by I keep getting an empty matrix....
% the code after FFT
peak_points = [];
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));
p_max;
fpeaks = froi(loc);
[points, locatn] = findpeaks(seg_fft2(region_of_interest));
if points > 0.4*p_max
peak_points = [peak_points ; points locatn]
end
Im bad with arrays.. So I cant seem to figure this out. Would appreciate someone's help in this... Thanks in advance...
Are you intending to do the seg_ffr2 on the region of interest logical array or on froi?
or maybe points is vector and you should have:
aboveMax = points > 0.4*p_max;
if any(aboveMax)
peak_points = [peak_points ; points(aboveMax) locatn(aboveMax)]
end

Finding the difference between two signals

I have two signals, let's call them 'a' and 'b'. They are both nearly identical signals (recorded from the same input and contain the same information) however, because I recorded them at two different 'b' is time shifted by an unknown amount. Obviously, there is random noise in each.
Currently, I am using cross correlation to compute the time shift, however, I am still getting improper results.
Here is the code I am using to calculate the time shift:
function [ diff ] = FindDiff( signal1, signal2 )
%FINDDIFF Finds the difference between two signals of equal frequency
%after an appropritate time shift is applied
% Calculates the time shift between two signals of equal frequency
% using cross correlation, shifts the second signal and subtracts the
% shifted signal from the first signal. This difference is returned.
length = size(signal1);
if (length ~= size(signal2))
error('Vectors must be equal size');
end
t = 1:length;
tx = (-length+1):length;
x = xcorr(signal1,signal2);
[mx,ix] = max(x);
lag = abs(tx(ix));
shifted_signal2 = timeshift(signal2,lag);
diff = signal1 - shifted_signal2;
end
function [ shifted ] = timeshift( input_signal, shift_amount )
input_size = size(input_signal);
shifted = (1:input_size)';
for i = 1:input_size
if i <= shift_amount
shifted(i) = 0;
else
shifted(i) = input_signal(i-shift_amount);
end
end
end
plot(FindDiff(a,b));
However the result from the function is a period wave, rather than random noise, so the lag must still be off. I would post an image of the plot, but imgur is currently not cooperating.
Is there a more accurate way to calculate lag other than cross correlation, or is there a way to improve the results from cross correlation?
Cross-correlation is usually the simplest way to determine the time lag between two signals. The position of peak value indicates the time offset at which the two signals are the most similar.
%// Normalize signals to zero mean and unit variance
s1 = (signal1 - mean(signal1)) / std(signal1);
s2 = (signal2 - mean(signal2)) / std(signal2);
%// Compute time lag between signals
c = xcorr(s1, s2); %// Cross correlation
lag = mod(find(c == max(c)), length(s2)) %// Find the position of the peak
Note that the two signals have to be normalized first to the same energy level, so that the results are not biased.
By the way, don't use diff as a name for a variable. There's already a built-in function in MATLAB with the same name.
Now there are two functions in Matlab:
one called finddelay
and another called alignsignals that can do what you want, I believe.
corr finds a dot product between vectors (v1, v2). If it works bad with your signal, I'd try to minimize a sum of squares of differences (i.e. abs(v1 - v2)).
signal = sin(1:100);
signal1 = [zeros(1, 10) signal];
signal2 = [signal zeros(1, 10)];
for i = 1:length(signal1)
signal1shifted = [signal1 zeros(1, i)];
signal2shifted = [zeros(1, i) signal2];
d2(i) = sum((signal1shifted - signal2shifted).^2);
end
[fval lag2] = min(d2);
lag2
It is computationally worse than cross-calculation which can be speeded up by using FFT. As far as I know you can't do this with euclidean distance.
UPD. Deleted wrong idea about cross-correlation with periodic signals
You can try matched filtering in frequency domain
function [corr_output] = pc_corr_processor (target_signal, ref_signal)
L = length(ref_signal);
N = length(target_signal);
matched_filter = flipud(ref_signal')';
matched_filter_Res = fft(matched_filter,N);
corr_fft = matched_filter_Res.*fft(target_signal);
corr_out = abs(ifft(corr_fft));
The peak of the matched filter maximum-index of corr_out above should give you the lag amount.