I have 2 800x1 arrays in Matlab which contain my amplitude vs. frequency data, one array contains the magnitude, the other contains the corresponding values for frequency. I want to find the frequency at which the amplitude has reduced to half of its maximum value.
What would be the best way to do this? I suppose my two main concerns are: if the 'half amplitude' value lies between two data points, how can I find it? (e.g. if the value I'm looking for is 5, how can I "find it in my data" if it lies between two data points such as 4 and 6?)
and if I find the 'half amplitude' value, how do I then find the corresponding value for frequency?
Thanks in advance for your help!
You can find the index near your point of interest by doing
idx = magnitudes >= (max(magnitude)/2);
And then you can see all the corresponding frequencies, including the peak, by doing
disp(frequencies(idx))
You can add more conditions to the idx calculation if you want to see less extraneous stuff.
However, your concern about finding the exact frequency is harder to answer. It will depend heavily on the nature of the signal and also on the lineshape of your window function. In general, you might be better off trying to characterize your peak with a few points and then doing a curvefit of some kind. Are you trying to calculate Q of a resonant filter, by any chance?
If it's ok, you can do simple linear interpolation. Find segments where the drop occurs and calculate intermediate values. That will be no good, if you expect noise in the signal.
idx = find(magnitudes(2:end) <= (max(magnitudes)/2) & ...
magnitudes(1:end-1) >= (max(magnitudes)/2));
mag1 = magnitudes(idx); % magnitudes of points before drop
mag2 = magnitudes(idx+1); % magnitudes of points after drop below max/2
fr1 = frequencies(idx); % frequencies just before drop
fr2 = frequencies(idx+1); % frequencies after drop below max/2
magx = max(magnitudes)/2; % max/2
frx = (magx-mag2).*(fr1-fr2)./(mag1-mag2) + fr2; % estimated frequencies
You can also use INTERP1 function.
Related
I am working on a script which performs a FFT of given short audio file in a loop. I also want to store the peak frequency but I do not know how to do that.
The code looks similar to this:
n = ...
Frequencies = zeros(1,n); % Allocating memory for the peak frequencies
for k = 1:n
str(k)
textFileName = [num2str(k) '.m4a'];
[data,fs] = audioread(textFileName);
%...
% Fast Fourier transform and plotting part works ok
%...
[peaks,frequencies] = findpeaks(abs(cutP2),cutf,'MinPeakHeight',10e-3);
% Here starts the problem
maximum_Peak = max(peaks);
Frequencies(k) = ... % I need to store the frequency which is coupled
% with the maximum amplitude but I do not know how
end
close(figure(n)) %The loop opens one redundant blank plot, I could not
%find out any other way to close it
I do not want to store the amplitudes of peak frequencies, but frequencies of peak amplitudes. If you could help me with the redundant figure, I would be happy. I tried to implement an if statement but did not work.
max contains a second output which returns the index of the maximum value. Use this second value to stores the value of interest.
[maximum_Peak,I] = max(peaks); %Note I Use 'I' for index - personal habit
Frequencies(k) = frequencies(I);
Also, if your goal is only to find the max point findpeaks may be overkill and you could potentially use:
[maximum_Peak,I] = max(abs(cutP2));
%Might want to check that max is high enough
Frequencies(k) = cutf(I);
Note although the code is similar it is not the same and depends on what you want to do.
Finally, some unsolicited advice, your use of frequencies and Frequencies is a bit of a red flag. Generally differences based on capitalization are not a good idea. Consider renaming the latter to freq_of_max_amp
Let's say that I have a signal in Matlab like this
x = cos(2*pi*10*t) + cos(2*pi*20*t) + cos(2*pi*50*t);
And I want to change the values between 20 and 30 hz into 0. How can I do that? I mean, those values generated from the x formula, I want to change them a little bit.
You can do it by performing FFT over x and setting to zero those values that are between 20 and 30 Hz then applying the FFT inverse on the previous values and you should get the signal without those frequencies. However, you may lose valuable information or the signal might just not look as you wish. Therefore, I recommend you to use a "Bandstop filter". The band stop filter will receive the cutoff frequencies (the limit frequencies you want to work with) and some other parameters. The bandstop filter basically removes from the signal the frequencies that you specify. And the good part is that it can be done as easy as doing what follows:
First you have to build the filter. To do so, you need to indicate the filter order which can be defined as you wish. Usually a second order works good. Also, you have to be aware of your sampling rate Fs.
d = designfilt('bandstopiir','FilterOrder',2, ...
'HalfPowerFrequency1',20,'HalfPowerFrequency2',30, ...
'SampleRate',Fs);
Now you only need to apply the filter to your desired signal.
filtered_signal_x = filtfilt(d, x)
Now, filtered_signal_x should not have the frequencies you wanted to delete. By using the bandstop you don't have to mess with the FFT and that kind of stuff and is a way faster so I think its the best option.
You can either use a filter, or you can filter it by yourself by going into Fourier space and explicitly setting the signal on the frequencies you need to zero. After that, you need to go back to the time domain. Here is a code:
t=0:0.01:0.99; % time
x = cos(2*pi*10*t) + cos(2*pi*20*t) + cos(2*pi*50*t); %signal
xf=fftshift(fft(x)); %Fourier signal
N=size(x,2); % Size of the signal
frequency=2*pi*[-N/2:N/2-1]; %frequency range
frequencyrangeplus=find(frequency/(2*pi)>=20 & frequency/(2*pi)<=30); %find positive frequencies in the required range
frequencyrangeminus=find(frequency/(2*pi)<=-20 & frequency/(2*pi)>=-30); %find negative frequencies in the required range
xf(frequencyrangeplus)=0; %set signal to zero at positive frequencies range
xf(frequencyrangeminus)=0; %set signal to zero at nagative frequencies range
xnew=ifft(ifftshift(xf)); %get the new signal in time domain
xcheck= cos(2*pi*10*t) + cos(2*pi*50*t); % to check the code
max(abs(xcheck-xnew)) % maximum difference
I have asked this question on DSP.SE before, but my question has got no attention. Maybe it was not so related to signal processing.
I needed to divide a discrete audio signal into segments to have some statistical processing and analysis on them. Therefore, segments with fixed local mean would be very helpful for my case. Length of segments are predefined, e.g. 512 samples.
I have tried several things. I do use reshape() function to divide audio signal into segments, and then calculate means of every segment as:
L = 512; % Length of segment
N = floor(length(audio(:,1))/L); % Number of segments
seg = reshape(audio(1:N*L,1), L, N); % Reshape into LxN sized matrix
x = mean(seg); % Calculate mean of each column
Subtracting x(k) from each seg(:,k) would make each local mean zero, yet it would distort audio signal a lot when segments are joined back.
So, since mean of hanning window is almost 0.5, substracting 2*x(k)*hann(L) from each seg(:,k) was the first thing I tried. But this time multiplying by 2 (to make the mean of hanning window be almost equal to 1) distorted the neighborhood of midpoints in each segments itself.
Then, I have used convolution by a smaller hanning window instead of multiplying directly, and subtracting these (as shown in figure below) from each seg(:,k).
This last step gives better results, yet it is still not very useful when segments are smaller. I have seen many amazing approaches here on this site for different problems. So I just wonder if there is any clever ways or existing methods to obtain zero local means which distorts an audio signal less. I read that, this property is useful in some decompositions such as EMD. So maybe I need such decompositions?
You can try to use a moving average filter:
x = cumsum(rand(15*512, 1)-0.5); % generate a random input signal
mean_filter = 1/512 * ones(1, 512); % generate a mean filter
mean = filtfilt(mean_filter, 1, x); % filtfilt is used instead of filter to obtain a symmetric moving average.
% plot the result
figure
subplot(2,1,1)
plot(x);
hold on
plot(mean);
subplot(2,1,2)
plot(x - mean);
You can tune the filter by changing the interval of the mean filter. Using a smaller interval, results in lower means inside each interval, but filters also more low frequencies out of your signal.
Good evening guys,
I wanna ask you a question regarding the analysis of a function in the domain of frequencies (Fourier). I have two vectors: one containing 7700 values for pressure, and the other one containing 7700 values (same number) for time.
For example, I call the firt vector "a" and the second one "b". With the command "figure(1),plot(a,b)" I obtain the curve in the domain of time.
How can I do to plot this curve in the domain of frequency, to make Fourier transform?
I've read about the function "fft", but I've not understood very well how it can be used...can anyone help me?
Thanks in advance for your attention!
fft returns spectrum as complex numbers. In order to analyze it you have to use its absolute value or phase. In general, it should look like this (let's assume that t is vector containing time and y is the one with actual signal, N is the number of samples):
fY = fft(y) / (N/2) % scale it to amplitude, typically by N/2
amp_fY = abs(fY)
phs_fY = angle(fY)
Additionally, it would be nice to have FFT with known frequency resolution. For that, you need sampling period/frequency. Let's call that frequency fs:
fs = 1/(t(1) - t(0))
and the vector of frequencies for FFT (F)
should be:
F = (0:fs/N:(N-1)*fs/N)
and finally plots:
plot(F, amp_fY)
% or plot(F, phs_fy) according to what you need
I you can use stem instead of plot to get some other type of chart.
Note that the DC component (the average value) will be doubled on the plot.
Hope it helps
I have a matrix of data X where rows are time stamps and columns are measurements. I can easily find the lowest sum path through the matrix by:
[r c]=size(X)
for w=1:r
Y(w)=min(X(w,:))
end
result = sum(Y)
this is useful, but what would be really useful is if there were a function that could tell me different paths for a specified frequency. For example if i group 2 rows together this halves the frequency...... If there was a function that could find me different paths with varying frequencies for a specified tolerance then rank them this would be perfect!
A lot to ask but there must be a statistical or mathematical tool that does this......
Not sure if I entirely understand the question, but if I read what you want this should do the trick for a fixed frequency:
frequency = 2;
r = size(X,1);
Y = zeros(r,1);
for w=1:frequency:r
Y(w)=min(min(X(w:w+frequency-1,:)))
end
result = sum(Y)
You can loop over frequencies to find the best path length for each frequency.
Note that finding the optimal path with varying frequencies (so for example first 2 then 3 then 2 again) would be a completely different problem. I think this is much more complex and that you may want to look into linear programming.