function [sig,varargout]=mcaf(x,varargin) % % %[sig,[mse],[xhat],[param]]=mcaf(x,d_ind,[CH],[Fs],[show],[verbose]) % %Input Arguments are: % x -NxM data matrix. Each column represent a channel % d_din -scalar index indicating the target column (to be predicted) % CH -Optional Mx1 cell array with the names of each channel % Fs -Optional scalar indicating sampling frequency in Hz (default is 125 Hz) % show -Optional logical argument indicating to plot final results % verbose -Optional logical argument indicating progress of the code % % %Output Arguments are: % sig -Nx1 reconstructed signal % mse -Optional output Nx1 mean square error of the final estimate and desired response % xhat -Optional output 1xM final Kalman weights % param -Optional detailed information of filter training % including final filter parameters and NxM Kalman weights as a function of time % %Multi Channel Adaptive Filtering %Based on the Physionet 2010 Challenge phys_est_phase2_v11 version. % Copyright (C) 2010 Ikaro Silva % % This library is free software; you can redistribute it and/or modify it under % the terms of the GNU Library General Public License as published by the Free % Software Foundation; either version 2 of the License, or (at your option) any % later version. % % This library 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 Library General Public License for more % details. % % You should have received a copy of the GNU Library General Public License along % with this library; if not, write to the Free Software Foundation, Inc., 59 % Temple Place - Suite 330, Boston, MA 02111-1307, USA. % % You may contact the author by e-mail (ikaro@ieee.org). % %NOTE: this function uses PARLOOPS, so make sure to type 'matlabpool' %prior to running it. %%Example % % clear all;clc;close all % load mat_c05 % d_ind=2; % CH={'RESP','PLETH','ABP','II','V','AVR','CVP'}; % verbose=1; % show=1; % [sig]=mcaf(mat_c05,d_ind,CH,[],show,verbose); Fs=125; pole_order=20; d_ind=4; CH={}; par_mode=1; show=1; verbose=1; par_name={'d_ind','CH','Fs','show','verbose'}; %Get optional parameters for n=2:nargin if(~isempty(varargin{n-1})) eval([par_name{n-1} '=varargin{n-1};']) end end if(verbose) display('***Running MCAF***'); end %targets=dlmread([data_dir fname '.missing']); Ntarget=round(Fs*30); [N,L]=size(x); if(isempty(CH)) CH=cell(L,1); end if(length(CH) ~= L) error(['CH is ' num2str(length(CH)) ' long, should be a cell array of size ' num2str(L)]) end %%% GET SIGNALS %%%% d=x(:,d_ind); sig_name=CH{d_ind}; %Replace original signal with a estimate of it base on past history sig=[d(end-2*Ntarget+1:end-Ntarget);d(1:end-Ntarget)]; x(:,d_ind)=sig; %Get Rid of any initial clipping clp=cumsum(abs(sign(diff(d)))); clp_end=(find(clp~=0)); if(~isempty(clp_end) && ~clp(Fs) && (clp_end(1) < N-2*Ntarget) ) d(1:clp_end-1)=[]; x(1:clp_end-1,:)=[]; N=length(d); warning('Getting rid of initial flat signal artifact'); end n2=N-Ntarget; n1=1; sig=zeros(N,1); mse=0; NCh=length(CH); param.CH=CH; param.Ntarget=Ntarget; param.w=NaN; param.XHAT=NaN; param.fin_best=' '; param.init=' '; param.CH=CH; param.Ntarget=Ntarget; param.w=NaN; param.XHAT=NaN; param.fin_best=' '; param.init=' '; param.n1=n1; param.n2=n2; maxy=max(d(1:N-Ntarget)); miny=min(d(1:N-Ntarget)); y=[1:L]; ws1=1; %sum sqrt ws2=1; %rms %Define GALL Parameters M=35; epsi=10^-2; R=zeros(L,1); H=zeros(L,M+2); K=zeros(L,M+1); D=zeros(n2-n1+1,L); %estimates for each signal P=zeros(L,1); B=zeros(L,1); G=zeros(L,1); %gain param.D=D; %%%%%%%% ANALYZE THE SIGNALS %%%%%%%%%%%%%%%%%%%% %Return DC of input if the signal is flat if(length(unique(x(1:N-Ntarget,d_ind)))==1) sig=ones(N,1).*x(1,d_ind); warning('Signal is flat. Returning DC as estimate') if(nargout >1) varargout(1)={NaN}; if(nargout>2) varargout(2)={NaN}; if(nargout>3) varargout(3)={zeros(1,L)}; end end end return end %%%%%%%%%%%%%%%%%% %Training Phase %%%%%%%%%%%%%%%%%% %Optimize xcross each signal POLE=[[1-0.0005.^[linspace(0,1,pole_order)]] ]; pad=d(n1:n2).*0; target=d(n1:n2); BETA=[[1-0.0001.^[linspace(0,1,6)]] ]; BETA(1)=0.5; err=zeros(1,L); std_tar=std(target); NBETA=length(BETA); NPOLE=length(POLE); tmp_err1=zeros(NBETA,NPOLE)+NaN; tmp_err2=zeros(NBETA,NPOLE)+NaN; for m=1:L if(verbose) display(['Getting reconstruction of CH ' num2str(m) ' ( out of ' num2str(L) ' CHs)']); end for beta=1:NBETA parfor p=1:NPOLE [trash,dhat_tmp,h,k]=gall(x(n1:n2,y(m)),target,M,BETA(beta),POLE(p),epsi,[],[]); [trash,dhat_tmp,trash,trash]=gall(x(n1:n2,y(m)),pad,[],[],POLE(p),epsi,k,h); %test phase %tmp_err=sqrt(mean((dhat_tmp(end-8*Ntarget:end)-target(end-8*Ntarget:end)).^2)); if(any(isnan(dhat_tmp)) || isnan(sum(diff(dhat_tmp))) ||~sum(diff(dhat_tmp))) dhat_tmp=x(n1:n2,y(m)); end tmp_err1(beta,p)=sum((dhat_tmp-target).^2); tmp_err2(beta,p)=sqrt(mean((dhat_tmp-target).^2)); end end %of optimization loop [best_err1,best_ind1]=nanmin(tmp_err1(:)); [best_err2,best_ind2]=nanmin(tmp_err2(:)); df1=tmp_err1(best_ind1)-tmp_err1(best_ind2); df2=tmp_err2(best_ind2)-tmp_err2(best_ind1); if((ws1*df1)<(ws2*df2)) [best_err,best_ind]=nanmin(tmp_err1(:)); else [best_err,best_ind]=nanmin(tmp_err2(:)); end [beta_opt,p_opt]=ind2sub([NBETA NPOLE],best_ind); [trash,dhat_tmp,h,k]=gall(x(n1:n2,y(m)),target,M,BETA(beta_opt),POLE(p_opt),epsi,[],[]); [trash,dhat_tmp,trash,trash]=gall(x(n1:n2,y(m)),pad,[],[],POLE(p_opt),epsi,k,h); %test phase if(any(isnan(dhat_tmp)) || isnan(sum(diff(dhat_tmp))) ||~sum(diff(dhat_tmp))) dhat_tmp=x(n1:n2,y(m)); end err(m)=best_err; dhat=dhat_tmp; H(m,:)=h; K(m,:)=k; P(m)=POLE(p_opt); B(m)=BETA(beta_opt); G(m)=std_tar/std(dhat); if(isinf(G(m)) || isnan(G(m))) G(m)=1; end D(:,m)=dhat'.*G(m); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %Combine each estimator using Kalman filter to obtain final estimate %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% bad=find(isnan(err)==1); if(~isempty(bad)) warning('NaN error found') D(:,bad)=-D(:,d_ind); end err(bad)=inf; %sort the signals according to the error best_ind=sortrows([err(:) [1:L]']); %Initiliaze state vector w0=ones(L,1)./L; Klamb=[0 0.75 0.9 0.97 0.997 0.9997 0.99997 0.999997 0.9999997 0.99999997]; NKlamb=length(Klamb); K0=[]; NOISE=[]; k_opt=[inf NaN]; wopt=NaN; XHAT=NaN; XHATopt=NaN; max_warn=0; Opt_st=10; mse1=zeros(NKlamb,1)+NaN; mse2=zeros(NKlamb,1)+NaN; if(verbose) display(['Combining individual reconstructions']); end parfor k=1:NKlamb k_tmp=Klamb(k); [wtmp,XHAT,ALPHA]=kallman(D,target,w0,k_tmp,K0,NOISE); dhat_tmp=D*wtmp; mse1(k)=sum((dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)).^2); mse2(k)=sqrt(mean((dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)).^2)); end [K_err1,best_k1]=nanmin(mse1); [K_err2,best_k2]=nanmin(mse2); df1=mse1(best_k1)-mse1(best_k2); df2=mse2(best_k2)-mse2(best_k1); if((ws1*df1)<(ws2*df2)) [K_err,best_k]=nanmin(mse1(:)); else [K_err,best_k]=nanmin(mse2(:)); end [wtmp,XHAT,ALPHA]=kallman(D,target,w0,Klamb(best_k),K0,NOISE); k_opt(1)=K_err; k_opt(2)=Klamb(best_k); wopt=wtmp; XHATopt=XHAT; [trash,Max_ind]=max(XHATopt(end-(60*2)*Fs:end,:),[],2); if(length(unique(Max_ind))>2) %did not have the unique statement before max_warn=1; end if(max_warn) warning('Attempting to stabilize weights') mse1_best=[Inf NaN]; mse2_best=[Inf NaN]; wopt2=wtmp; wopt1=wtmp; XHATopt1=[]; XHATopt2=[]; Kwin_max=15*Fs; for k=1:length(Klamb) k_tmp=Klamb(k); [wtmp,XHAT,ALPHA]=kallman(D,target,w0,k_tmp,K0,NOISE); %Apply averaging to XHAT for kal_win=[(5*Fs):(5*Fs):Kwin_max] wtmp=mean(XHAT(end-kal_win:end,:))'; dhat_tmp=D*wtmp; mse1=sum((dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)).^2); %mse1=mean(abs(dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end))); mse2=sqrt(mean((dhat_tmp(end-Opt_st:end)-target(end-Opt_st:end)).^2)); if(mse1 < mse1_best(1)) wopt1=wtmp; XHATopt1=XHAT; mse1_best(1)=mse1; mse2_best(2)=mse2; end if(mse2 < mse2_best(1)) wopt2=wtmp; XHATopt2=XHAT; mse1_best(2)=mse1; mse2_best(1)=mse2; end end end if(ws1*(mse1_best(1)-mse1_best(2)) < ws2*(mse2_best(1)-mse2_best(2))) wopt=wopt1; XHATopt=XHAT; mse_best=mse1; else wopt=wopt2; XHATopt=XHAT; mse_best=mse2; end end if(any(isnan(wopt))) warning('Kallman weightst not found.') wopt=ones(L,1)./L; end if(min(wopt)==0) warning('Weights normalized') wopt=ones(L,1)./L; end %Real Final Deal D2=zeros(N,L); %estimates for each signal parfor m=1:L [err_tmp,dhat2,trash,trash]=gall(x(:,y(m)),d.*0,... [],[],P(m),epsi,K(m,:),H(m,:)); %test phase if(any(isnan(H(m,:))) || ~sum(H(m,:)) || any(isnan(dhat2)) || ~sum(diff(dhat2))) dhat2=x(:,y(m)); end D2(:,m)=dhat2'.*G(m); end dhat2_A=D2*wopt; %Update dc component dhat2_B=dhat2_A-mean(dhat2_A)+mean(d(n2-3*Ntarget:n2)); mseA1=sum((dhat2_A(end-2*Ntarget:end-Ntarget)-d(end-2*Ntarget:end-Ntarget)).^2); mseA2=sqrt(mean((dhat2_A(end-2*Ntarget:end-Ntarget)-d(end-2*Ntarget:end-Ntarget)).^2)); mseB1=sum((dhat2_B(end-2*Ntarget:end-Ntarget)-d(end-2*Ntarget:end-Ntarget)).^2); mseB2=sqrt(mean((dhat2_B(end-2*Ntarget:end-Ntarget)-d(end-2*Ntarget:end-Ntarget)).^2)); df1=min(mseA1,mseB1)- (mseA1*(mseA2mseB2)); df2=min(mseA2,mseB2)- (mseA2*(mseA1mseB1)); if ((ws1*df1) < (ws2*df2)) if(mseA1<=mseB1) dhat2=dhat2_A; else dhat2=dhat2_B; end else if(mseA2<=mseB2) dhat2=dhat2_A; else dhat2=dhat2_B; end end %Pass final signal through a GALL filter %Optimize xcross each signal BETA2=[0 0.5 0.95 0.995 0.9995 0.99995 0.999995 0.9999997 ]; NBETA2=length(BETA2); opt=inf; dhat3_tmp=inf; H3=[]; K3=[]; B3=[]; dhat3=dhat2; st_ind=max(N-6*Ntarget,1); tmp_err1=zeros(NBETA2,1)+NaN; tmp_err2=zeros(NBETA2,1)+NaN; parfor beta=1:NBETA2 [trash,trash,h,k]=gall(dhat2(n1:n2),target,M,BETA2(beta),0,epsi,[],[]); [trash,dhat3_tmp,trash,trash]=gall(dhat2,d.*0,[],[],0,epsi,k,h); %test phase tmp_err1(beta)=sum((dhat3_tmp(st_ind:end-Ntarget)-d(st_ind:end-Ntarget)).^2); tmp_err2(beta)=sqrt(mean((dhat3_tmp(st_ind:end-Ntarget)-d(st_ind:end-Ntarget)).^2)); end %of optimization loop [beta1_err,best_beta1]=nanmin(tmp_err1(:)); [beta2_err,best_beta2]=nanmin(tmp_err2(:)); df1=tmp_err1(best_beta1)-tmp_err1(best_beta2); df2=tmp_err2(best_beta2)-tmp_err2(best_beta1); if((ws1*df1)<(ws2*df2)) [beta2_err,best_beta2]=nanmin(tmp_err1(:)); else [beta2_err,best_beta2]=nanmin(tmp_err2(:)); end [trash,trash,h,k]=gall(dhat2(n1:n2),target,M,BETA2(best_beta2),0,epsi,[],[]); [trash,dhat3_tmp,trash,trash]=gall(dhat2,d.*0,[],[],0,epsi,k,h); %test phase opt=beta2_err; dhat3=dhat3_tmp; H3=h; K3=k; B3=BETA2(best_beta2); dhat3(dhat3>maxy)=maxy; dhat3(dhat31) varargout(1)={mse}; if(nargout>2) varargout(2)={mean(XHAT,1)}; if(nargout>3) varargout(3)={param}; end end end if(verbose) display('->DONE running MCAF'); end %%%%%%%%%%%% END OF MAIN %%%%%%%%%%%%%%%%%%%%%% function plot_res(d,dhat3,n2,miny,maxy,CH,d_ind,mse,fin_best,ave_best,XHATopt,y,sig_name,N,init_best) %Plot Kallman Results figure subplot(311) plot(d) hold on;grid on plot(dhat3,'r') line([n2 n2],[miny maxy].*1.5,'Color','g','LineStyle','--','LineWidth',3) if(~isempty(CH{1})) legend(CH{d_ind},'Estimated') title(['CH= ' sig_name ' MSE= ' num2str(mse) ,' Final Best= ' CH{fin_best}]) end xlim([0 N]) subplot(312) plot(XHATopt) grid on if(~isempty(CH{1})) legend(CH(y)) title(['Ave Best Channel: ' CH{ave_best}]) end xlim([0 N]) subplot(313) plot((d(1:n2)-dhat3(1:n2)).^2) xlim([0 N])