'resample' to have the same size of signals - matlab

i have 4 (2 Rates and 2 Times) signals and i need to equalize their sizes. I cut them first off, because i need that too. Size of first time and rate signal is 3901, another 830. But not just to remove elements, i want to keep the curve. I thought i need interpolation and tried "resample" but it is not perfect. Looks like photo. How should i improve my codes? Any idea?
index=time >= 9.6 & tsyn <= 13.5; %time boundaries of first time signal
time1=tsyn(index); %first time signal
time_f=resample(time1,830,3901);
Rate1=CLU_YR1(index) %first rate signal
Rate_f=resample(Rate1,830,3901);
index2 = cm.Time.data >= 26.3 & cm.Time.data <= 30.45; %time boundaries of second time signal
time2=cm.Time.data(index2) %second time signal
Rat2=cm.BodySensor_SC1_Omega_B_z.data*(-180/pi) %second rate signal
Rate_p=Rat2(index2)

I suppose some of the misfit of your curves originates from the fact that the original sequence is not (close to) 0 at the end of the vector. From the matlab resample documentation:
When filtering, resample assumes that the input sequence, x, is zero before and after the samples it is given. Large deviations from zero at the endpoints of x can result in unexpected values for y.
What the best alternative is depends on what it is you want to do next. If you want to have a new, upsampled version (i.e. both signals of length 3901), you could look at interp1, which supports several different methods. If you choose to do this, keep in mind that the values in y(t) will be interpolated according to the values of t you provide. Since your time arrays do not seem to be aligned (one is between 9.6 and 13 sec, the other between 26 and 30), you are probably best off doing something along the lines of:
y_new = interp1( linspace(1,100,830), rate_p, linspace(1,100,3901), 'linear');
and the same for the time array.

Related

How can I split the signal in half?

I have a lot of signals loaded into a .mat file as double. As seen in the graph, there is a noise signal in the environment until the 53rd second. After 53 seconds there is noise + signal. I want to separate noise and noise + signal in matlab. How can I write a code for this? I can see it directly as the 53rd second in this graph, but since I have many different signals, I want to automatically get only the noise part of each signal, so the window length in the array.
The find() function can be used to retrieve the index that corresponds to the 53rd second and split the signal into two arrays at that point. Just in case the sample point does not land on exactly 53s it is good to find the smallest value of t that is greater or equal to 53.
%Test signal and time, t vector (replace with your vectors)%
t = (0: 0.5: 180);
Test_Signal = rand(1,length(t));
Index = find(t >= 53,1);
%Seperating the two regions of the test signal%
Noise = Test_Signal(1:Index);
Signal_Plus_Noise = Test_Signal(Index+1:end);

How to change value of a signal's range in Matlab

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

Any good ways to obtain zero local means in audio signals?

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.

QRS detection(peaks) of a raw ecg signal in matlab

I want to find the peaks of the raw ecg signal so that I can calculate the beats per minute(bpm).
I Have written a code in matlab which I have attached below.In the code below I am unable to find threshold point correctly which will help me in finding the peaks and hence the bpm.
%input the signal into matlab
[x,fs]=wavread('heartbeat.wav');
subplot(2,1,1)
plot(x(1:10000),'r-')
grid on
%lowpass filter the input signal with cutoff at 100hz
h=fir1(30,0.3126); %normalized cutoff freq=0.3126
y=filter(h,1,x);
subplot(2,1,2)
plot(y(1:10000),'b-')
grid on
% peaks are seen as pulses(heart beats)
beat_count=0;
for p=2:length(y)-1
th(p)=abs(max(y(p)));
if(y(p) >y(p-1) && y(p) >y(p+1) && y(p)>th(p))
beat_count=beat_count+1;
end
end
N = length(y);
duration_seconds=N/fs;
duration_minutes=duration_seconds/60;
BPM=beat_count/duration_minutes;
bpm=ceil(BPM);
Please help me as I am new to matlab
I suggest changing this section of your code
beat_count=0;
for p=2:length(y)-1
th(p)=abs(max(y(p)));
if(y(p) >y(p-1) && y(p) >y(p+1) && y(p)>th(p))
beat_count=beat_count+1;
end
end
This is definitely flawed. I'm not sure of your logic here but what about this. We are looking for peaks, but only the high peaks, so first lets set a threshold value (you'll have to tweak this to a sensible number) and cull everything below that value to get rid of the smaller peaks:
th = max(y) * 0.9; %So here I'm considering anything less than 90% of the max as not a real peak... this bit really depends on your logic of finding peaks though which you haven't explained
Yth = zeros(length(y), 1);
Yth(y > th) = y(y > th);
OK so I suggest you now plot y and Yth to see what that code did. Now to find the peaks my logic is we are looking for local maxima i.e. points at which the first derivative of the function change from being positive to being negative. So I'm going to find a very simple numerical approximation to the first derivative by finding the difference between each consecutive point on the signal:
Ydiff = diff(Yth);
No I want to find where the signal goes from being positive to being negative. So I'm going to make all the positive values equal zero, and all the negative values equal one:
Ydiff_logical = Ydiff < 0;
finally I want to find where this signal changes from a zero to a one (but not the other way around)
Ypeaks = diff(Ydiff_logical) == 1;
Now count the peaks:
sum(Ypeaks)
note that for plotting purpouse because of the use of diff we should pad a false to either side of Ypeaks so
Ypeaks = [false; Ypeaks; false];
OK so there is quite a lot of matlab there, I suggest you run each line, one by one and inspect the variable by both plotting the result of each line and also by double clicking the variable in the matlab workspace to understand what is happening at each step.
Example: (signal PeakSig taken from http://www.mathworks.com/help/signal/ref/findpeaks.html) and plotting with:
plot(x(Ypeaks),PeakSig(Ypeaks),'k^','markerfacecolor',[1 0 0]);
What do you think about the built-in
findpeaks(data,'Name',value)
function? You can choose among different logics for peak detection:
'MINPEAKHEIGHT'
'MINPEAKDISTANCE'
'THRESHOLD'
'NPEAKS'
'SORTSTR'
I hope this helps.
You know, the QRS complex does not always have the maximum amplitude, for pathologic ECG it can be present as several minor oscillations instead of one high-amplitude peak.
Thus, you can try one good algothythm, tested by me: the detection criterion is assumed to be high absolute rate of change in the signal, averaged within the given interval.
Algorithm:
- 50/60 Hz filter (e.g. for 50 Hz sliding window of 20 msec will be fine)
- adaptive hipass filter (for baseline drift)
- find signal's first derivate x'
- fing squared derivate (x')^2
- apply sliding average window with the width of QRS complex - approx 100-150 msec (you will get some signal with 'rectangles', which have width of QRS)
- use simple threshold (e.g. 1/3 of maximum of the first 3 seconds) to determine approximate positions or R
- in the source ECG find local maximum within +-100 msec of that R position.
However, you still have to eliminate artifacts and outliers (e.g. surges, when the electrod connection fails).
Also, you can find a lot of helpful information from this book: "R.M. Rangayyan - Biomedical Signal Analysis"

How to resample with interp1 in Matlab when input vectors are of different length

I have two variables in a .mat file here:
https://www.yousendit.com/download/UW13UGhVQXA4NVVQWWNUQw
testz is a vector of cumulative distance (in meters, monotonically and regularly increasing)
testSDT is a vector of integrated (cumulative) sound wave travel time (in milliseconds) generated using the distance vector and a vector of velocities
(there is an intermediate step of creating interval travel times)
Since velocity is a continuously variable function the resulting interval travelt times and also the integrated travel times are non integers and variable in magnitude
What I want is to resample the distance vector at regular time intervals (e.g. 1 ms, 2 ms, ..., n ms)
What makes it difficult is that the maximum travel time, 994.6659, is less than the number of samples in the 2 vectors, therefore it is not straightforward to use interp1.
i.e.:
X=testSDT -> 1680 samples
Y=testz -> 1680 samples
XI=[1:1:994] -> 994 samples
This is the code I've come up with. It is a working code and it is not too bad I think.
%% Initial chores
M=fix(max(testSDT));
L=(1:1:M);
%% Create indices
% this loops finds the samples in the integrated travel time vector
% that are closest to integer milliseconds and their sample number
for i=1:M
[cl(i) ind(i)] = min(abs(testSDT-L(i)));
nearest(i) = testSDT(ind(i));
end
%% Remove duplicates
% this is necessary to remove duplicates in the index vector (happens in this test).
% For example: 2.5 ms would be the closest to both 2 ms and 2 ms
[clsst,ia,ic] = unique(nearest);
idx=(ind(ia));
%% Interpolation
% this uses the index vectors to resample the depth vectors at
% integer times
newz=interp1(clsst,testz(idx),[1:1:length(idx)],'cubic')';
As far as I can see there is one issue with this code:
I rely on the vector idx as my XI for interpolation. Vector idx is 1 sample shorter than vector ind (one duplicate was removed).
Therefore my new times will stop one millisecond short. This is a very small issue, and duplicate are unlikely but I am wondering if anybody can think of a workaround, or of a different way to approach the problem altogether.
Thank you
If I understand you correctly, you want to extrapolate to that extra point.
you can do this is many ways, one is to add that extra point to the interp1 line.
If you have some function you expect to follow your data you can use it by fitting it to the data and then obtaining that extra point or with a tool like fnxtr.
But I have a problem understanding what you want because of the way you used the line. The third argument you use, [1:1:length(idx)], is just the series [1 2 3 ...], usually when interpolating, one uses some vector x_i of points of interest, though I doubt your points of interest happen to be the series of integers 1:length(idx), what you want is just [1:length(idx) xi], where xi is that extra point x-axis value.
EDIT:
Instead of the loop just produce matrix forms out of L and testSDT, then matrix operation is somewhat faster in doing the min(abs(...:
MM=ones(numel(testSDT),1)*L;
TT=testSDT*ones(1,numel(L));
[cl ind]=(min(abs(TT-MM)));
nearest=testSDT(ind);