--- a +++ b/preprocessOfApneaECG/BioSigKit/Algorithms/nqrsdetect.m @@ -0,0 +1,430 @@ +function QRS=nqrsdetect(S,fs) +% nqrsdetect - detection of QRS-complexes +% +% QRS=nqrsdetect(S,fs); +% +% INPUT +% S ecg signal data +% fs sample rate +% +% OUTPUT +% QRS fiducial points of qrs complexes +% +% +% see also: QRSDETECT +% +% Copyright (C) 2006 by Rupert Ortner +% +%% This program is free software; you can redistribute it and/or modify +%% it under the terms of the GNU General Public License as published by +%% the Free Software Foundation; either version 2 of the License, or +%% (at your option) any later version. +%% +%% This program is distributed in the hope that it will be useful, ... +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU General Public License +%% along with this program; if not, write to the Free Software +%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +%% USA +%% ============== Now Part of BioSigToolkit ============= %% +%% Modified and imporved by Hooman Sedghamiz, Feb 2018. + +S=S(:); +S=full(S); +N=round(fs); %Filter order +%--------------------------------------- +%Replaces filter bank in [1] +Bw=5.6; %filter bandwidth +Bwn=1/(fs/2)*Bw; +M=round((fs/2)/Bw); %downsampling rate + +Wn0=Bwn; %bandwidth of the first filter +Wn1=[Bwn 2*Bwn]; %bandwidth of the second filter +Wn2=[2*Bwn 3*Bwn]; +Wn3=[3*Bwn 4*Bwn]; +Wn4=[4*Bwn 5*Bwn]; + +h0=fir1(N,Wn0); %impulse response of the first filter +h1=fir1(N,Wn1,'bandpass'); +h2=fir1(N,Wn2,'bandpass'); +h3=fir1(N,Wn3,'bandpass'); +h4=fir1(N,Wn4,'bandpass'); + +%Polyphase implementation of the filters +y=cell(1,5); +y{1}=polyphase_imp(S,h0,M); %W0 (see [1]) filtered and downsampled signal +y{2}=polyphase_imp(S,h1,M); %W1 +y{3}=polyphase_imp(S,h2,M); %W2 +y{4}=polyphase_imp(S,h3,M); %W3 +y{5}=polyphase_imp(S,h4,M); %W4 +%---------------------------------------------- + +cut=ceil(N/M); %Cutting off of initial transient because of the filtering +y1=[zeros(cut,1);y{1}(cut:length(y{1}))]; +y2=[zeros(cut,1);y{2}(cut:length(y{2}))]; +y3=[zeros(cut,1);y{3}(cut:length(y{3}))]; +y4=[zeros(cut,1);y{4}(cut:length(y{4}))]; +y5=[zeros(cut,1);y{5}(cut:length(y{5}))]; +%---------------------------------------- + +P1=sum([abs(y2) abs(y3) abs(y4)],2); %see [1] equation (13) +P2=sum([abs(y2) abs(y3) abs(y4) abs(y5)],2); +P4=sum([abs(y3) abs(y4) abs(y5)],2); + +FL1=MWI(P1); %Feature 1 according to Level 1 in [1] +FL2=MWI(P2); %Feature 2 according to Level 2 +FL4=MWI(P4); %Feature 4 according to Level 4 +%-------------------------------------- +%Level 1 [1] +d=sign(diff(FL1)); +d1=[0;d]; +d2=[d;0]; +f1=find(d1==1); +f2=find(d2==-1); +EventsL1=intersect(f1,f2); %Detected events +%------------------------------------------------------- +%Level 2 [1] +meanL1=sum(FL2(EventsL1),1)/length(EventsL1); +NL=meanL1-meanL1*0.1; %Start Noise Level +SL=meanL1+meanL1*0.1; %Start Signal Level +threshold1=0.08; %Threshold detection block 1 +threshold2=0.7; %Threshold detection block 2 +[~,~,DS1,Class1]=detectionblock(FL2,EventsL1,NL,SL,threshold1); +[~,~,DS2,Class2]=detectionblock(FL2,EventsL1,NL,SL,threshold2); +%--------------------------------------------------- +ClassL3=[]; +for i=1:length(EventsL1) + C1=Class1(i); + C2=Class2(i); + if C1==1 + if C2==1 + ClassL3=[ClassL3 1]; %Classification as Signal + else + delta1=(DS1(i)-threshold1)/(1-threshold1); + delta2=(threshold2-DS2(i))/threshold2; + if delta1>delta2 + ClassL3=[ClassL3 1]; %Classification as Signal + else + ClassL3=[ClassL3 0]; %Classification as Noise + end + end + else + if C2==1 + ClassL3=[ClassL3 1]; %Classification as Signal + else + ClassL3=[ClassL3 0]; %Classification as Noise + end + end +end +SignalL3=EventsL1(find(ClassL3)); %Signal Level 3 +NoiseL3=EventsL1(find(ClassL3==0)); %Noise Level 3 +%-------------------------------------------- +%Level 4 [1] +threshold=0.3; +VSL=(sum(FL4(SignalL3),1))/length(SignalL3); +VNL=(sum(FL4(NoiseL3),1))/length(NoiseL3); +SL=(sum(FL4(SignalL3),1))/length(SignalL3); %Initial Signal Level +NL=(sum(FL4(NoiseL3),1))/length(NoiseL3); %Initial Noise Level +SignalL4=[]; +NoiseL4=[]; +DsL4=[]; %Detection strength Level 4 +for i=1:length(EventsL1) + Pkt=EventsL1(i); + if ClassL3(i)==1; %Classification after Level 3 as Signal + SignalL4=[SignalL4,EventsL1(i)]; + SL=history(SL,FL4(Pkt)); + Ds=(FL4(Pkt)-NL)/(SL-NL); %Detection strength + if Ds<0 + Ds=0; + elseif Ds>1 + Ds=1; + end + DsL4=[DsL4 Ds]; + else %Classification after Level 3 as Noise + Ds=(FL4(Pkt)-NL)/(SL-NL); + if Ds<0 + Ds=0; + elseif Ds>1 + Ds=1; + end + DsL4=[DsL4 Ds]; + if Ds>threshold %new classification as Signal + SignalL4=[SignalL4,EventsL1(i)]; + SL=history(SL,FL4(Pkt)); + else %new classification as Noise + NoiseL4=[NoiseL4,EventsL1(i)]; + NL=history(NL,FL4(Pkt)); + end + end +end +%------------------------------------------------ +%Level 5 +%if the time between two RR complexes is too long => go back and check the +%events again with lower threshold +SignalL5=SignalL4; +NoiseL5=NoiseL4; +periods=diff(SignalL4); +M1=100; +a=1; +b=1/(M1)*ones(M1,1); +meanperiod=filter(b,a,periods); %mean of the RR intervals +SL=sum(FL4(SignalL4))/length(SignalL4); +NL=sum(FL4(NoiseL4))/length(NoiseL4); +threshold=0.2; +for i=1:length(periods) + if periods(i)>meanperiod*1.5 %if RR-interval is to long + intervall=SignalL4(i):SignalL4(i+1); + critical=intersect(intervall,NoiseL4); + for j=1:length(critical) + Ds=(FL4(critical(j))-NL)/(SL-NL); + if Ds>threshold %Classification as Signal + SignalL5=union(SignalL5,critical(j)); + NoiseL5=setxor(NoiseL5,critical(j)); + end + end + end +end +%--------------------------------------------------- +%Umrechnung auf Originalsignal (nicht downgesamplet) +Signaln=conversion(S,FL2,SignalL5,M,N,fs); +%---------------------------------------------------- +%Level 6 If interval of two RR-complexes <0.24 => go back and delete one of them +height=FL2(SignalL5); +Signal=Signaln; +temp=round(0.1*fs); +difference=diff(Signaln); %Difference between two signal points +k=find(difference<temp); +for i=1:length(k) + pkt1=SignalL5(k(i)); + pkt2=SignalL5(k(i)+1); + verg=[height(k(i)),height(k(i)+1)]; + [x,j]=max(verg); + if j==1 + Signal=setxor(Signal,Signaln(k(i)+1)); %Deleting first Event + else + Signal=setxor(Signal,Signaln(k(i))); %Deleting second Event + end +end +QRS=Signal; + + +%------------------------------------------------------------------- +%------------------------------------------------------------------- +%------------------------------------------------------------------- +%subfunctions + +function y=MWI(S) + +% MWI - Moving window integrator, computes the mean of two samples +% y=MWI(S) +% +% INPUT +% S Signal +% +% OUTPUT +% y output signal +a=[0;S]; +b=[S;0]; +c=[a,b]; +y=sum(c,2)/2; +y=y(1:length(y)-1); +%------------------------------------------------ +function y=polyphase_imp(S,h,M) + +% polyphase_imp - polyphase implementation of decimation filters [2] +% y=polyphase_imp(S,h,M) +% +% INPUT +% S ecg signal data +% h filter coefficients +% M downsampling rate +% +% OUTPUT +% y filtered signal +% + +%Determining polyphase components ek +e=cell(M,1); +l=1; +m=mod(length(h),M); +while m>0 + for n=1:ceil(length(h)/M) + el(n)=h(M*(n-1)+l); + end + e{l}=el; + l=l+1; + m=m-1; +end +clear el; +for i=l:M + for n=1:floor(length(h)/M) + el(n)=h(M*(n-1)+i); + end + e{i}=el; +end +%Filtering +max=ceil((length(S)+M)/M); +Sdelay=S; +for i=1:M + Sd=downsample(Sdelay,M); + a=filter(e{i},1,Sd); + if length(a)<max + a=[a;zeros(max-length(a),1)]; + end + w(:,i)=a; + Sdelay=[zeros(i,1);S]; +end +y=sum(w,2); +%---------------------------------------------------------- +function [Signal,Noise,VDs,Class]=detectionblock(mwi,Events,NL,SL,threshold) + +% detectionblock - computation of one detection block +% +% [Signal,Noise,VDs,Class]=detectionblock(mwi,Events,NL,SL,threshold) +% +% INPUT +% mwi Output of the MWI +% Events Events of Level 1 (see [1]) +% NL Initial Noise Level +% SL Initial Signal Level +% threshold Detection threshold (between [0,1]) +% +% OUTPUT +% Signal Events which are computed as Signal +% Noise Events which are computed as Noise +% VDs Detection strength of the Events +% Class Classification: 0=noise, 1=signal + +Signal=[]; +Noise=[]; +VDs=[]; +Class=[]; +sumsignal=SL; +sumnoise=NL; +for i=1:length(Events) + P=Events(i); + Ds=(mwi(P)-NL)/(SL-NL); %Detection strength + if Ds<0 + Ds=0; + elseif Ds>1 + Ds=1; + end + VDs=[VDs Ds]; + if Ds>threshold %Classification as Signal + Signal=[Signal P]; + Class=[Class;1]; + sumsignal=sumsignal+mwi(P); + SL=sumsignal/(length(Signal)+1); %Updating the Signal Level + else %Classification as Noise + Noise=[Noise P]; + Class=[Class;0]; + sumnoise=sumnoise+mwi(P); + NL=sumnoise/(length(Noise)+1); %Updating the Noise Level + end +end +%------------------------------------------------------------ +function [pnew]=conversion(S,FL2,pold,M,N,fs) + +% conversion - sets the fiducial points of the downsampled Signal on the +% samplepoints of the original Signal +% +% [pnew]=conversion(S,FL2,pold,M,N,fs) +% +% INPUT +% S Original ECG Signal +% FL2 Feature of Level 2 [1] +% pold old fiducial points +% M M downsampling rate +% N filter order +% fs sample rate +% +% OUTPUT +% pnew new fiducial points +% + +Signaln=pold; +P=M; +Q=1; +FL2res=resample(FL2,P,Q); %Resampling +nans1=isnan(S); +nans=find(nans1==1); +S(nans)=mean(S); %Replaces NaNs in Signal +for i=1:length(Signaln) + Signaln1(i)=Signaln(i)+(M-1)*(Signaln(i)-1); +end +%------------------- Sets the fiducial points on the maximum of FL2 +Signaln2=Signaln1; +Signaln2=Signaln2'; +int=2*M; %Window length for the new fiducial point +range=1:length(FL2res); +for i=1:length(Signaln2) + start=Signaln2(i)-int/2; + if start<1 + start=1; + end + stop=Signaln2(i)+int/2; + if stop>length(FL2res) + stop=length(FL2res); + end + intervall=start:stop; %interval + FL2int=FL2res(intervall); + pkt=find(FL2int==max(FL2int)); %Setting point on maximum of FL2 + if length(pkt)==0 % if pkt=[]; + pkt=Signaln2(i)-start; + else + pkt=pkt(1); + end + delay=N/2+M; + Signaln3(i)=pkt+Signaln2(i)-int/2-delay; %fiducial points according to FL2 +end +%Sets the fiducial points on the maximum or minimum +%of the signal +Bw=5.6; +Bwn=1/(fs/2)*Bw; +Wn=[Bwn 5*Bwn]; +N1=32; +b=fir1(N1,Wn,'bandpass'); +Sf=filtfilt(b,1,S); %Filtered Signal with bandwidth 5.6-28 Hz +beg=round(1.5*M); +fin=1*M; +for i=1:length(Signaln3) + start=Signaln3(i)-beg; + if start<1 + start=1; + end + stop=Signaln3(i)+fin; + if stop>length(Sf) + stop=length(Sf); + end + intervall=start:stop; %Window for the new fiducial point + Sfint=abs(detrend(Sf(intervall),0)); + pkt=find(Sfint==max(Sfint)); %Setting point on maximum of Sfint + if length(pkt)==0 %if pkt=[]; + pkt=Signaln3(i)-start; + else + pkt=pkt(1); + end + pkt=pkt(1); + Signaln4(i)=pkt+Signaln3(i)-beg-1; +end +Signal=Signaln4'; %New fiducial points according to the original signal + +cutbeginning=find(Signal<N); %Cutting out the first points because of initial transient of the filter in polyphase_imp +fpointsb=Signal(cutbeginning); +cutend=find(Signal>length(S)-N); %Cutting out the last points +fpointse=Signal(cutend); +pnew=setxor(Signal,[fpointsb;fpointse]); +%------------------------------------------- +function yn=history(ynm1,xn) + +% history - computes y[n]=(1-lambda)*x[n]+lambda*y[n-1] +% +% yn=history(ynm1,xn) + +lambda=0.8; %forgetting factor +yn=(1-lambda)*xn+lambda*ynm1; + +