function varargout = wfdbRecordViewer(varargin) % WFDBRECORDVIEWER MATLAB code for wfdbRecordViewer.fig % WFDBRECORDVIEWER, by itself, creates a new WFDBRECORDVIEWER or raises the existing % singleton*. % % H = WFDBRECORDVIEWER returns the handle to a new WFDBRECORDVIEWER or the handle to % the existing singleton*. % % WFDBRECORDVIEWER('CALLBACK',hObject,eventData,handles,...) calls the local % function named CALLBACK in WFDBRECORDVIEWER.M with the given input arguments. % % WFDBRECORDVIEWER('Property','Value',...) creates a new WFDBRECORDVIEWER or raises the % existing singleton*. Starting from the left, property value pairs are % applied to the GUI before wfdbRecordViewer_OpeningFcn gets called. An % unrecognized property name or invalid value makes property application % stop. All inputs are passed to wfdbRecordViewer_OpeningFcn via varargin. % % *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one % instance to run (singleton)". % % See also: GUIDE, GUIDATA, GUIHANDLES % Edit the above text to modify the response to help wfdbRecordViewer % Last Modified by GUIDE v2.5 05-Feb-2015 15:38:57 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... 'gui_Singleton', gui_Singleton, ... 'gui_OpeningFcn', @wfdbRecordViewer_OpeningFcn, ... 'gui_OutputFcn', @wfdbRecordViewer_OutputFcn, ... 'gui_LayoutFcn', [] , ... 'gui_Callback', []); if nargin && ischar(varargin{1}) gui_State.gui_Callback = str2func(varargin{1}); end if nargout [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); else gui_mainfcn(gui_State, varargin{:}); end % End initialization code - DO NOT EDIT % --- Executes just before wfdbRecordViewer is made visible. function wfdbRecordViewer_OpeningFcn(hObject, ~, handles, varargin) % This function has no output args, see OutputFcn. % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to wfdbRecordViewer (see VARARGIN) global current_record records tm signal info tm_step exportFigure physionetAnn fext ann1 ann1Type ann1LabelsDisplaySetting exportFigure=0; physionetAnn={}; % Choose default command line output for wfdbRecordViewer handles.output = hObject; % Update handles structure guidata(hObject, handles); current_tmp=1; isWorkspace=0; ButtonName = questdlg('Select Database Location', 'Database Location',... 'Local Directory','PhysioNet','MATLAB Workspace','Local Directory'); switch ButtonName, case 'Local Directory', [filename,directoryname] = uigetfile('*.hea;*.edf;*.mat','Select signal header file:'); cd(directoryname) fext=filename(end-3:end); tmp=dir(['*' fext]); N=length(tmp); records=cell(N,1); for n=1:N fname=tmp(n).name; if(strcmp(fext,'.edf')) records(n)={fname}; else records(n)={fname(1:end-4)}; end if(strcmp(fname,filename)) current_tmp=n; end end case 'MATLAB Workspace', records={'MALTAB Workspace'}; [tm,signal,info,ann1,ann1Type]=loadWorkspaceRecord(handles); isWorkspace=1; case 'PhysioNet', %To implement %Select Database name h = waitbar(0,'Getting List of Databases from PhysioNet. Please wait...'); dbs=physionetdb; D=length(dbs); IND=zeros(D,1); for n=1:D str=char(dbs{n}); str=regexprep(str,'\n',' '); ind=strfind(str,'Description:'); IND(n)=ind; pad=30-ind(1); str=regexprep(str,'Description:',blanks(pad)); dbs{n}=str; end close(h) [db_ind,ok] = listdlg('PromptString','Select a database:',... 'SelectionMode','single',... 'ListString',dbs,'ListSize',[550 600]); if(~ok) return end h = waitbar(0,'Loading list of records. Please wait...'); dbname=dbs{db_ind}; dbname=dbname(1:IND(db_ind)); dbname=regexprep(dbname,' ',''); dbname=regexprep(dbname,'\n',''); dbname=regexprep(dbname,'\t',''); [tmp,status]=urlread(['http://physionet.org/physiobank/database/' dbname '/RECORDS']); if(status==1) records=regexp(tmp,'\n','split'); if(isempty(records{end}(:))) records(end)=[]; end for n=1:length(records) records{n}=['/' dbname '/' records{n}]; end close(h) h = waitbar(0,'Loading list of annotations. Please wait...'); [tmp,status]=urlread(['http://physionet.org/physiobank/database/' dbname '/ANNOTATORS']); if(status==1) ann=regexp(tmp,'\n','split'); for n=1:length(ann(:,1)) tmpInd=regexp(ann{1},'\s'); physionetAnn(end+1)={ann{1}(1:tmpInd-1)}; end end close(h) else errordlg(['Could not connect to PhysioNet server. Exiting!']) return end end % switch set(handles.RecorListMenu,'String',records) current_record=current_tmp; set(handles.RecorListMenu,'Value',current_record) if(isWorkspace==0) loadRecord(records{current_record},handles); loadAnnotationList(records{current_record},handles); end set(handles.slider1,'Max',tm(end)) set(handles.slider1,'Min',tm(1)) set(handles.slider1,'SliderStep',[1 1]); sliderStep=get(handles.slider1,'SliderStep'); tm_step=(tm(end)-tm(1)).*sliderStep(1); wfdbplot(handles) function varargout = wfdbRecordViewer_OutputFcn(~,~, handles) % varargout cell array for returning output args (see VARARGOUT); % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Get default command line output from handles structure varargout{1} = handles.output; function PreviousButton_Callback(hObject, eventdata, handles) global current_record records current_record=current_record - 1; set(handles.RecorListMenu,'Value',current_record); Refresh(hObject, eventdata, handles) function NextButton_Callback(hObject, eventdata, handles) global current_record records current_record=current_record + 1; set(handles.RecorListMenu,'Value',current_record); Refresh(hObject, eventdata, handles) % -------------------------------------------------------------------- function FileMenu_Callback(hObject, eventdata, handles) % hObject handle to FileMenu (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % -------------------------------------------------------------------- function OpenMenuItem_Callback(hObject, eventdata, handles) file = uigetfile('*.fig'); if ~isequal(file, 0) open(file); end % -------------------------------------------------------------------- function PrintMenuItem_Callback(hObject, eventdata, handles) printdlg(handles.figure1) % -------------------------------------------------------------------- function CloseMenuItem_Callback(hObject, eventdata, handles) selection = questdlg(['Close ' get(handles.figure1,'Name') '?'],... ['Close ' get(handles.figure1,'Name') '...'],... 'Yes','No','Yes'); if strcmp(selection,'No') return; end delete(handles.figure1) % --- Executes on selection change in RecorListMenu. function RecorListMenu_Callback(hObject, eventdata, handles) global current_record records current_record=get(handles.RecorListMenu,'Value'); Refresh(hObject, eventdata, handles) % --- Executes during object creation, after setting all properties. function RecorListMenu_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes on slider movement. function slider1_Callback(hObject, eventdata, handles) wfdbplot(handles) % --- Executes during object creation, after setting all properties. function slider1_CreateFcn(hObject, eventdata, handles) if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor',[.9 .9 .9]); end function loadRecord(fname,handles) global tm signal info analysisSignal analysisTime h = waitbar(0,'Loading Data. Please wait...'); [signal,Fs,tm]=rdsamp(fname); signal=single(signal); info=wfdbdesc(fname); R=length(info); analysisSignal=[]; analysisTime=[]; signalDescription=cell(R,1); for r=R:-1:1 signalDescription(r)={info(r).Description}; end set(handles.signalList,'String',signalDescription) close(h) return function loadAnn1(fname,annName) global ann1 ann1Labels h = waitbar(0,'Loading Annotations. Please wait...'); if(strcmp(fname,'none')) ann1=[]; else [ann1,type,subtype,chan,num,comment]=rdann(fname,annName); end ann1Labels.type=type; ann1Labels.subtype=subtype; ann1Labels.chan=chan; ann1Labels.num=num; ann1Labels.comment=comment; %Load other parameters for the ann1Labels structure wfdbShowAnn1Labels(0); close(h) function loadAnn2(fname,annName) global ann2 ann2Labels h = waitbar(0,'Loading Annotations. Please wait...'); if(strcmp(fname,'none')) ann1=[]; else [ann2,type,subtype,chan,num,comment]=rdann(fname,annName); ann2Labels.type=type; ann2Labels.subtype=subtype; ann2Labels.chan=chan; ann2Labels.num=num; ann2Labels.comment=comment; end close(h) function loadAnnotationList(fname,handles) global ann1 ann2 annDiff physionetAnn ann1=[]; ann2=[]; annDiff=[]; tmp=dir([fname '*']); annotations={'none'}; if(isempty(physionetAnn)) exclude={'dat','hea','edf','mat'}; for i=1:length(tmp) name=tmp(i).name; st=strfind(name,'.'); if(~isempty(st)) tmp_ann=name(st+1:end); enter=1; for k=1:length(exclude) if(strcmp(tmp_ann,exclude{k})) enter=0; end end if(enter) annotations(end+1)={tmp_ann}; end end end else annotations=[annotations;physionetAnn]; end set(handles.Ann1Menu,'String',annotations) set(handles.Ann2Menu,'String',annotations) function wfdbplot(handles) global tm signal info tm_step ann1 ann2 annDiff ann1RR analysisSignal global specEstimation analysisTime analysisUnits analysisYAxis ann1Labels global exportFigure ann1LabelsDisplaySetting if(exportFigure) figure subplot(211) else axes(handles.axes1); cla; end %Normalize each signal and plot them with an offset [N,CH]=size(signal); offset=0.5; %Get time info center=get(handles.slider1,'Value'); maxSlide=get(handles.slider1,'Max'); minSlide=get(handles.slider1,'Min'); if(tm_step == ( tm(end)-tm(1) )) tm_start=tm(1); tm_end=tm(end); elseif(center==maxSlide) tm_end=tm(end); tm_start=tm_end - tm_step; elseif(center==minSlide) tm_start=tm(1); tm_end=tm_start + tm_step; else tm_start=center - tm_step/2; tm_end=center + tm_step/2; end [~,ind_start]=min(abs(tm-tm_start)); [~,ind_end]=min(abs(tm-tm_end)); DC=min(signal(ind_start:ind_end,:),[],1); sig=signal - repmat(DC,[N 1]); SCALE=max(sig(ind_start:ind_end,:),[],1); SCALE(SCALE==0)=1; sig=offset.*sig./repmat(SCALE,[N 1]); OFFSET=offset.*[1:CH]; sig=sig + repmat(OFFSET,[N 1]); if(~isempty(ann1)) showThreshold=str2num(ann1LabelsDisplaySetting.threshold); end for ch=1:CH; plot(tm(ind_start:ind_end),sig(ind_start:ind_end,ch)) hold on ; grid on if(~isempty(ann1)) tmp_ann1=ann1((ann1>ind_start) & (ann1ind_start) & (ann1ind_start) & (ann2ind_start) & (ann1ind_start) & (ann1(1:end) Nann) ind(end)=[]; end tm_ind=ann1(ind); del_ind=find(tm_ind>N); if(~isempty(del_ind)) ind(ann1(ind)==tm_ind(del_ind))=[]; tm_ind(del_ind)=[]; end if(~isempty(ind) && ind(end)>length(ann1RR)) del_ind=find(ind>length(ann1RR)); ind(del_ind)=[]; tm_ind(del_ind)=[]; end plot(tm(tm_ind),ann1RR(ind),'k*-') text(tm(tm_ind(1)),max(ann1RR(ind)),'RR Series','FontWeight','bold','FontSize',12) grid on ylabel('Interval (seconds)') if(~isnan(ind_start) && ~isnan(ind_end) && ~(ind_start==ind_end)) xlim([tm(ind_start) tm(ind_end)]) end end end end exportFigure=0; end % --- Executes on selection change in TimeScaleSelection. function TimeScaleSelection_Callback(hObject, eventdata, handles) global tm_step tm TM_SC=[tm(end)-tm(1) 120 60 30 15 10 5 1]; index = get(handles.TimeScaleSelection, 'Value'); %Normalize step to time range if(TM_SC(index)>TM_SC(1)) index=1; end stp=TM_SC(index)/TM_SC(1); set(handles.slider1,'SliderStep',[stp stp*10]); tm_step=TM_SC(1).*stp(1); axes(handles.axes1); cla; wfdbplot(handles) % --- Executes during object creation, after setting all properties. function TimeScaleSelection_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes on selection change in AmplitudeScale. function AmplitudeScale_Callback(hObject, eventdata, handles) wfdbplot(handles) % --- Executes during object creation, after setting all properties. function AmplitudeScale_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes on selection change in Ann1Menu. function Ann1Menu_Callback(hObject, eventdata, handles) global ann1 records current_record ind = get(handles.Ann1Menu, 'Value'); annStr=get(handles.Ann1Menu, 'String'); loadAnn1(records{current_record},annStr{ind}) wfdbplot(handles) % --- Executes during object creation, after setting all properties. function Ann1Menu_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes on selection change in Ann2Menu. function Ann2Menu_Callback(hObject, eventdata, handles) global ann2 records current_record ind = get(handles.Ann2Menu, 'Value'); annStr=get(handles.Ann2Menu, 'String'); loadAnn2(records{current_record},annStr{ind}) wfdbplot(handles) % --- Executes during object creation, after setting all properties. function Ann2Menu_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function AnnotationMenu_Callback(hObject, eventdata, handles) global ann1 ann1Labels ann1RR info ann2 ann2Labels tm specEstimation analysisSignal exportFigure ann1LabelsDisplaySetting exportFigure=0; tips=0; Fs=double(info(1).SamplingFrequency); annStr=get(handles.AnnotationMenu,'String'); index=get(handles.AnnotationMenu,'Value'); switch(annStr{index}) case 'Plot Annotation Differences' h = waitbar(0,'Comparing Annotations. Please wait...'); annDiff=[]; %Compare annotation with ann1menu being the reference N=length(ann1); if(~isempty(ann2)) [A1,A2]=meshgrid(ann1,ann2); annDiff=min(abs(A1-A2))./Fs; end close(h) specEstimation=[]; wfdbplot(handles) case 'Plot RR Series Ann1' h = waitbar(0,'Generating RR Series. Please wait...'); %Compare annotation with ann1menu being the reference ann1RR=diff(ann1)./double(info(1).SamplingFrequency); close(h) specEstimation=[]; analysisSignal=[]; wfdbplot(handles) case 'Add annotations to Ann1' %Get closest sample using ginput if(tips) helpdlg('Left click to add multiple annotations. Hit Enter when done.','Adding Annotations'); end axes(handles.axes1); [x,~]= ginput; %Convert to samples ann to ann1 x=round(x*Fs); x=sort(x); N=length(x); %Get annotation info (which will be the same for multiple %annotations [annType,annSubtype,annChan,annNum,annComments]=getAnnFields(); for n=1:N %[~,tmp_ind]=min(abs(x(n)-samp)); ann1(end+1)=x(n); ann1Labels(end+1).type=annType; ann1Labels(end).subtype=annSubtype; ann1Labels(end).chan=annChan; ann1Labels(end).num=annNum; ann1Labels(end).comment=annComments; end if(isempty(ann1LabelsDisplaySetting)) %Define/set display parameters if this is the first annotation wfdbShowAnn1Labels(1); end %Refresh annotation plot wfdbplot(handles) case 'Show Ann1 Labels' wfdbShowAnn1Labels(1); case 'Delete annotations from Ann1' %Get closest sample using ginput if(tips) helpdlg('Left click on annotations to remove multiple. Hit Enter when done.','Removing Annotations'); end axes(handles.axes1); [x,~]= ginput; rmN=length(x); rm_ind=zeros(rmN,1); for n=1:rmN [~,tmp_ind]=min(abs(x(n)-tm(ann1))); rm_ind(n)=tmp_ind; end if~(isempty(rm_ind)) ann1(rm_ind)=[]; end %Refresh annotation plot wfdbplot(handles) case 'Delete annotations in a range from Ann1' %Get closest sample using ginput if(tips) helpdlg('Left click on start and end regions. Hit Enter when done.','Removing Annotations'); end axes(handles.axes1); [x,~]= ginput; [~,start_ind]=min(abs(x(end-1)-tm(ann1))); [~,end_ind]=min(abs(x(end)-tm(ann1))); ann1(start_ind:end_ind)=[]; %Refresh annotation plot wfdbplot(handles) case 'Edit annotations in Ann1' %Modify closest sample using ginput if(tips) helpdlg('Left click on waveform will shift closest annotation to the clicked point. Hit Enter when done.','Adding Annotations'); end axes(handles.axes1); [x,~]= ginput; editN=length(x); edit_ind=zeros(editN,1); for n=1:editN [~,tmp_ind]=min(abs(x(n)-tm(ann1))); edit_ind(n)=tmp_ind; end if~(isempty(edit_ind)) ann1(edit_ind)=round(x*Fs); end %Refresh annotation plot wfdbplot(handles) case 'Add annotations in a range from Ann2 to Ann2' if(tips) helpdlg('Left click on waveform to select start and end of region to add from Ann2 to Ann1. Hit Enter when done.','Adding Annotations'); end axes(handles.axes1); [x,~]= ginput; ind=[1:length(x)]'; X=sortrows([ind x(:)],2); x=X(:,2); ann2Labels=ann2Labels(X(:,1)); [~,start_ind]=min(abs(x(1)-tm(ann2))); [~,end_ind]=min(abs(x(2)-tm(ann2))); ann1=sort([ann1;ann2(start_ind:end_ind)]); ann1Labels=[ann1Labels;ann2Labels]; %Refresh annotation plot wfdbplot(handles) case 'Save modified annotations of Ann1' global records current_record defaultAnn=get(handles.Ann1Menu,'String'); defaultInd=get(handles.Ann1Menu,'Value'); defName={[defaultAnn{defaultInd} '_x']}; newAnn=inputdlg('Enter new annotation name:','Save Annotation',1,defName); h=waitbar(0,['Saving annotation file: ' records{current_record} '.' newAnn{1}]); wrann(records{current_record},newAnn{1},ann1); close(h) case 'Save Ann1 to matfile' defName={'new_label.mat'}; newAnn=inputdlg('Enter new MAT file name:','Save to mat file: ',1,defName); h=waitbar(0,['Saving annotation file: ' newAnn]); save(newAnn,'ann1','ann1Labels') close(h) case 'Launch PhysioNet Label Definitions' web('http://www.physionet.org/physiobank/annotations.shtml'); end function AnnotationMenu_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function Refresh(hObject, eventdata, handles) global records current_record loadRecord(records{current_record},handles); loadAnnotationList(records{current_record},handles) Ann1Menu_Callback(hObject, eventdata, handles) Ann2Menu_Callback(hObject, eventdata, handles) %AnalysisMenu_Callback(hObject, eventdata, handles) % --- Executes on selection change in SignalMenu. function SignalMenu_Callback(hObject, eventdata, handles) % hObject handle to SignalMenu (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: contents = cellstr(get(hObject,'String')) returns SignalMenu contents as cell array % contents{get(hObject,'Value')} returns selected item from SignalMenu global tm signal info analysisSignal analysisTime analysisUnits analysisYAxis specEstimation global exportFigure contents = cellstr(get(hObject,'String')); ind=get(handles.signalList,'Value'); str= contents{get(hObject,'Value')}; exportFigure=0; if(strcmp(str,'Overwrite with Analyzed Signal')) if(length(analysisTime) == length(tm)) signal(:,ind)=analysisSignal; else errordlg(['Analysis signal must be same size as original signal to be overwritten.']) end else %Get Raw Signal analysisTime=tm; analysisSignal=signal(:,ind); analysisUnits=strsplit(info(ind).Gain,'/'); if(length(analysisUnits)>1) analysisUnits=analysisUnits{2}; else analysisUnits=[]; end Fs=double(info(ind).SamplingFrequency); analysisYAxis=[]; specEstimation=[]; switch str case 'Plot Raw Signal' %default to plot after switch case 'Apply General Filter' [analysisSignal]=wfdbFilter(analysisSignal,Fs); case '60/50 Hz Notch Filter' [analysisSignal]=wfdbNotch(analysisSignal,Fs); case 'Resonator Filter' [analysisSignal]=wfdbResonator(analysisSignal,Fs); case 'Savitzky-Golay Filter' [analysisSignal]=wfdbSgolayfilt(analysisSignal); case 'Custom Function' try [analysisSignal,analysisTime]=wfdbFunction(analysisSignal,analysisTime,Fs); catch errordlg(lasterr) end case 'Spectogram Analysis' [analysisSignal,analysisTime,analysisYAxis,analysisUnits]=wfdbSpect(analysisSignal,Fs); case 'Wavelets Analysis' [analysisSignal,analysisYAxis,analysisUnits]=wfdbWavelets(analysisSignal,Fs); case 'Spatial PCA' [analysisSignal,analysisUnits]=wfdbPCA(signal); case 'Harmonic Filter' [analysisSignal]=wfdbHarmonicFilter(analysisSignal,Fs); case 'Karhunen-Loeve Expansion' [analysisSignal,analysisUnits]=wfdbKL(analysisSignal); case 'Track Fundamental' [analysisSignal,analysisUnits]=wfdbF1Track(analysisSignal,Fs); case 'Spectral Estimation' specEstimation=wfdbPwelch(); specEstimation.sigInd=ind; specEstimation.Fs=Fs; case 'Spectral Coherence' specEstimation=wfdbCohere(); specEstimation.sigInd=ind; specEstimation.Fs=Fs; case 'Export as Separate Figure' exportFigure=1; case 'Add Analyzed Signal' try signal(:,end+1)=analysisSignal; catch errordlg(lasterr); end info(end+1).Description='Analyzed Signal'; signalDescription=cell(R,1); for r=R:-1:1 signalDescription(r)={info(r).Description}; end set(handles.signalList,'String',signalDescription) case 'Delete Selected Signal' info(ind)=[]; signal(:,ind)=[]; signalDescription=cell(R,1); for r=R:-1:1 signalDescription(r)={info(r).Description}; end set(handles.signalList,'String',signalDescription) wfdbplot(handles); end end if(~isempty(analysisSignal) || ~isempty(specEstimation)) %If analysisSignal is empty, command has been cancelled, else go ahead %and plot wfdbplot(handles); end % --- Executes during object creation, after setting all properties. function SignalMenu_CreateFcn(hObject, eventdata, handles) % hObject handle to SignalMenu (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: popupmenu controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end % --- Executes on selection change in signalList. function signalList_Callback(hObject, eventdata, handles) % hObject handle to signalList (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Hints: contents = cellstr(get(hObject,'String')) returns signalList contents as cell array % contents{get(hObject,'Value')} returns selected item from signalList % --- Executes during object creation, after setting all properties. function signalList_CreateFcn(hObject, eventdata, handles) % hObject handle to signalList (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called % Hint: popupmenu controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end function [annType,annSubtype,annChan,annNum,annComments]=getAnnFields() %Set Low-pass default values persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'Annotation Type:', 'Annotation Subtype:','Annotation Channel:','Annotation Number:'... 'Annotation Commentes:'}; dlgParam.annType='N'; dlgParam.annSubtype='0'; dlgParam.annChan='1'; dlgParam.annNum='0'; dlgParam.annComments=''; dlgParam.name='Enter Optional Annotation Info'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,{dlgParam.annType,... dlgParam.annSubtype,dlgParam.annChan,dlgParam.annNum,dlgParam.annComments}); dlgParam.annType=answer{1}; dlgParam.annSubtype=answer{2}; dlgParam.annChan=answer{3}; dlgParam.annNum=answer{4}; dlgParam.annComments=answer{5}; annType=answer{1}; annSubtype=num2str(answer{2}); annChan=num2str(answer{3}); annNum=num2str(answer{4}); annComments=answer(5); function [analysisSignal]=wfdbFilter(analysisSignal,Fs) %Set Low-pass default values persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'Filter Design Function (should return "a" and "b", for use by FILTFILT ):'}; dlgParam.answer=['Fs= ' num2str(Fs) ';b=fir1(48,[10 40]./(0.5*Fs));a=1;']; dlgParam.name='Filter Design Command'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,{dlgParam.answer}); if(isempty(answer)) analysisSignal=[]; return; end h = waitbar(0,'Filtering Data. Please wait...'); dlgParam.answer=answer{1}; try eval([dlgParam.answer ';']); analysisSignal=filtfilt(b,a,double(analysisSignal)); catch errordlg(['Unable to filter data! Error: ' lasterr]) end close(h) function x=wfdbPwelch() persistent specEstimation if(isempty(specEstimation)) specEstimation.prompt={'Window Size','Samples that Overlap','FFT Size'... 'Plot scale (linear, semilogx, semilogy, loglog)'}; specEstimation.WINDOW='[]'; specEstimation.NOVERLAP='[]'; specEstimation.NFFT='[]'; specEstimation.scale='semilogy'; specEstimation.name='PWLECH Spectral Estimation Parameters'; specEstimation.numlines=1; end answer=inputdlg(specEstimation.prompt,specEstimation.name,specEstimation.numlines,... {specEstimation.WINDOW, specEstimation.NOVERLAP,specEstimation.NFFT, specEstimation.scale}); specEstimation.WINDOW = answer{1}; specEstimation.NOVERLAP= answer{2}; specEstimation.NFFT= answer{3}; specEstimation.scale= answer{4}; x.WINDOW =str2num(answer{1}); x.NOVERLAP=str2num(answer{2}); x.NFFT= str2num(answer{3}); x.scale= answer{4}; x.isCohere=0; function x=wfdbCohere() persistent specEstimation if(isempty(specEstimation)) specEstimation.prompt={'Window Size','Samples that Overlap','FFT Size'... 'Plot scale (linear, semilogx, semilogy, loglog)','Index of Second Signal:'}; specEstimation.WINDOW='[]'; specEstimation.NOVERLAP='[]'; specEstimation.NFFT='[]'; specEstimation.scale='linear'; specEstimation.ind2='1'; specEstimation.name='PWLECH Spectral Estimation Parameters'; specEstimation.numlines=1; end answer=inputdlg(specEstimation.prompt,specEstimation.name,specEstimation.numlines,... {specEstimation.WINDOW, specEstimation.NOVERLAP,specEstimation.NFFT,... specEstimation.scale,specEstimation.ind2}); specEstimation.WINDOW = answer{1}; specEstimation.NOVERLAP= answer{2}; specEstimation.NFFT= answer{3}; specEstimation.scale= answer{4}; specEstimation.ind2= answer{5}; x.WINDOW =str2num(answer{1}); x.NOVERLAP=str2num(answer{2}); x.NFFT= str2num(answer{3}); x.scale= answer{4}; x.ind2= str2num(answer{5}); x.isCohere=1; function [analysisSignal]=wfdbNotch(analysisSignal,Fs) % References: % *Rangayyan (2002), "Biomedical Signal Analysis", IEEE Press Series in BME % % *Hayes (1999), "Digital Signal Processing", Schaum's Outline %Set Low-pass default values persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'Control Paramter (0 < r < 1 ):','Notch Frequency (Hz):'}; dlgParam.r='0.995'; dlgParam.fn='60'; dlgParam.name='Notch Filter Design'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,... {dlgParam.r, dlgParam.fn}); if(isempty(answer)) analysisSignal=[]; return; end h = waitbar(0,'Filtering Data. Please wait...'); dlgParam.r = answer{1}; % Control parameter. 0 < r < 1. dlgParam.fn= answer{2}; r = str2num(dlgParam.r); % Control parameter. 0 < r < 1. fn= str2num(dlgParam.fn); cW = cos(2*pi*fn/Fs); b=[1 -2*cW 1]; a=[1 -2*r*cW r^2]; try eval([answer{1} ';']); analysisSignal=filtfilt(b,a,double(analysisSignal)); catch errordlg(['Unable to filter data! Error: ' lasterr]) end close(h) function [analysisSignal]=wfdbResonator(analysisSignal,Fs) % References: % *Rangayyan (2002), "Biomedical Signal Analysis", IEEE Press Series in BME % % *Hayes (1999), "Digital Signal Processing", Schaum's Outline %Set Low-pass default values persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'Resonating Frequency (Hz):','Q factor:'}; dlgParam.fn=num2str(Fs/5); dlgParam.K='50'; dlgParam.name='Resonator Filter Design'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,... {dlgParam.fn,dlgParam.fn}); if(isempty(answer)) analysisSignal=[]; return; end h = waitbar(0,'Filtering Data. Please wait...'); dlgParam.fn= answer{1}; dlgParam.K= answer{2}; %Similar to 'Q1' but more accurate %For details see IEEE SP 2008 (5), pg 113 K=str2num(dlgParam.K); fn=str2num(dlgParam.fn); beta=1+K; f=pi*fn/Fs; numA=tan(pi/4 - f); denA=sin(2*f)+cos(2*f)*numA; A=numA/denA; b=[1 -2*A A.^2]; a=[ (beta + K*(A^2)) -2*A*(beta+K) ((A^2)*beta + K)]; try eval([answer{1} ';']); analysisSignal=filtfilt(b,a,double(analysisSignal)); catch errordlg(['Unable to filter data! Error: ' lasterr]) end close(h) function [analysisSignal]=wfdbHarmonicFilter(analysisSignal,Fs) % References: % *Rangayyan (2002), "Biomedical Signal Analysis", IEEE Press Series in BME % % *Hayes (1999), "Digital Signal Processing", Schaum's Outline %Set Low-pass default values persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'Fundamental Frequency (Hz). If empty, will be estimated:','Stop Frequency (Hz)','Q factor:'}; dlgParam.fn=''; dlgParam.stop=num2str(Fs); dlgParam.K='50'; dlgParam.name='Harmonic Filter Design'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,... {dlgParam.fn,dlgParam.stop,dlgParam.K}); if(isempty(answer)) analysisSignal=[]; return; end h = waitbar(0,'Filtering Data. Please wait...'); dlgParam.fn= answer{1}; dlgParam.stop=answer{2}; dlgParam.K= answer{3}; if(isempty(dlgParam.fn)) %Estimate fundamental from spectrum N=length(analysisSignal); [Pxx,F] = pwelch(analysisSignal,N,0,[],Fs); [~,minInd]=min(abs(F-1));% Kill all frequencies below 1 Hz L=length(Pxx); [acor] = xcorr(Pxx-mean(Pxx)); acor(1:L-1)=[]; %Find where first peak stops ind=find(sign(diff(acor))>0); acor(1:max(ind,minInd))=0; [~,offset]=max(acor); fn=F(offset); dlgParam.fn=num2str(fn); %Store in dialog box end %Similar to 'Q1' but more accurate %For details see IEEE SP 2008 (5), pg 113 K=str2num(dlgParam.K); fn=str2num(dlgParam.fn); stop=str2num(dlgParam.stop); beta=1+K; M=floor(stop/fn); A=[]; B=[]; for i=1:M f=pi*fn/Fs; numA=tan(pi/4 - f); denA=sin(2*f)+cos(2*f)*numA; c=numA/denA; b=[1 -2*c c.^2]; a=[ (beta + K*(c^2)) -2*c*(beta+K) ((c^2)*beta + K)]; B=[B b]; A=[A a]; end try analysisSignal=filtfilt(B,A,analysisSignal); catch errordlg(['Unable to filter data! Error: ' lasterr]) end close(h) function [analysisSignal]=wfdbSgolayfilt(analysisSignal) persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'Polynomial Order (K)','Window Size in samples (F):'}; dlgParam.K='3'; dlgParam.F='21'; dlgParam.name='Savitzky-Golay Filtering Options'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,... {dlgParam.K,dlgParam.F}); if(isempty(answer)) analysisSignal=[]; return; end h = waitbar(0,'Filtering Data. Please wait...'); dlgParam.K= answer{1}; dlgParam.F= answer{2}; %Similar to 'Q1' but more accurate %For details see IEEE SP 2008 (5), pg 113 K=str2num(dlgParam.K); F=str2num(dlgParam.F); try analysisSignal=sgolayfilt(analysisSignal,K,F); catch errordlg(['Unable to filter data! Error: ' lasterr]) end close(h) function [analysisSignal,analysisTime]=wfdbFunction(analysisSignal,analysisTime,Fs) persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'Custom Function must output variables ''analysisSignal'' and ''analysisTime'''}; dlgParam.answer={'[analysisSignal,analysisTime]=foo(analysisSignal,analysisTime,Fs)'}; dlgParam.name='Evaluate Command:'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,dlgParam.answer); if(isempty(answer)) analysisSignal=[]; analysisTime=[]; return; end dlgParam.answer=answer(1); h = waitbar(0,'Executing code on signal. Please wait...'); try eval([dlgParam.answer{1} ';']); catch errordlg(['Error: ' lasterr]) end close(h) function [analysisSignal,analysisTime,analysisYAxis,analysisUnits]=wfdbSpect(analysisSignal,Fs) persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'window size','overlap size','Min Frequency (Hz)','Max Frequency (Hz)','colormap'}; dlgParam.window=2^10; dlgParam.minY= 0; dlgParam.maxY= floor(Fs/2); dlgParam.noverlap=round(dlgParam.window/2); dlgParam.map='jet'; dlgParam.name='Spectogram Parameters'; dlgParam.numlines=1; end dlgParam.defaultanswer={num2str(dlgParam.window),num2str(dlgParam.noverlap),... num2str(dlgParam.minY),num2str(dlgParam.maxY),dlgParam.map}; answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,dlgParam.defaultanswer); if(isempty(answer)) analysisSignal=[]; analysisUnits=[]; analysisYAxis=[]; analysisTime=[]; return; end h = waitbar(0,'Calculating spectogram. Please wait...'); dlgParam.window= str2num(answer{1}); dlgParam.noverlap= str2num(answer{2}); analysisYAxis.minY= str2num(answer{3}); analysisYAxis.maxY= str2num(answer{4}); analysisYAxis.map=answer{5}; dlgParam.minY=analysisYAxis.minY; dlgParam.maxY=analysisYAxis.maxY; dlgParam.map=analysisYAxis.map; [~,F,analysisTime,analysisSignal] = spectrogram(analysisSignal,dlgParam.window,... dlgParam.noverlap,dlgParam.window,Fs,'yaxis'); analysisSignal=10*log10(abs(analysisSignal)); analysisYAxis.values=F; analysisUnits='Frequency (Hz)'; close(h) function [analysisSignal,analysisYAxis,analysisUnits]=wfdbWavelets(analysisSignal,Fs) persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'wavelet','scales','colormap','logScale'}; dlgParam.wavelet='coif2'; dlgParam.scales='1:28'; dlgParam.map='jet'; dlgParam.log='false'; dlgParam.name='Wavelet Parameters'; dlgParam.numlines=1; end dlgParam.defaultanswer={num2str(dlgParam.wavelet),num2str(dlgParam.scales),dlgParam.map,dlgParam.log}; answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines,dlgParam.defaultanswer); if(isempty(answer)) analysisSignal=[]; analysisUnits=[]; analysisYAxis=[]; return; end h = waitbar(0,'Calculating wavelets. Please wait...'); dlgParam.wavelet= answer{1}; dlgParam.scales = str2num(answer{2}); dlgParam.map= answer{3}; dlgParam.log= answer{4}; analysisYAxis.minY= dlgParam.scales(1); analysisYAxis.maxY= dlgParam.scales(end); analysisYAxis.map=dlgParam.map; analysisYAxis.isImage=1; coefs = cwt(analysisSignal,dlgParam.scales,dlgParam.wavelet); analysisSignal = wscalogram('',coefs); if(strcmp(dlgParam.log,'true')) analysisSignal=log(analysisSignal); end analysisYAxis.values=dlgParam.scales; analysisUnits='Scale'; close(h) function [analysisSignal,analysisUnits]=wfdbPCA(signal) persistent dlgParam if(isempty(dlgParam)) dlgParam.M=['1:' num2str(size(signal,2))]; dlgParam.P='1'; dlgParam.prompt={'Indices of signals to include in PCA:',... 'Selecta a Principal Component (equal or less than the number of indices above):'}; dlgParam.name='Parameters for ploting principal component'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines, {dlgParam.M, ... dlgParam.P}); if(isempty(answer)) analysisSignal=[]; analysisUnits=[]; return; end dlgParam.M=answer{1}; dlgParam.P=answer{2}; h = waitbar(0,'Estimating K-L Basis. Please wait...'); analysisUnits='Amplitude'; signal=signal(:,str2num(dlgParam.M)); [u,~,~]=svd(signal,0); ind=str2num(dlgParam.P); analysisSignal=u(:,ind); close(h) function [analysisSignal,analysisUnits]=wfdbKL(signal) persistent dlgParam maxM=11; if(isempty(dlgParam)) dlgParam.P='1'; dlgParam.prompt={['Select index of desired Principal Component (<= ' num2str(maxM) ') :']}; dlgParam.numlines=1; dlgParam.name='Parameters for K-L Expansion'; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines, {dlgParam.P}); if(isempty(answer)) analysisSignal=[]; analysisUnits=[]; return; end dlgParam.P=answer{1}; h = waitbar(0,'Estimating fundamental frequency. Please wait...'); analysisUnits='Amplitude'; signal=signal-mean(signal); ind=num2str(dlgParam.P); R=corrmtx(signal,maxM); [u,s,v]=svd(R,0); N=length(signal); ind=str2num(dlgParam.P); analysisSignal=u(1:N,ind); close(h) function [tm,signal,info,ann, annLabels]=loadWorkspaceRecord(handles) dlgParam.prompt={'Enter List of variable names (ie: x,y,z )', ... 'Sampling Frequency in Hz (ie: 250)',... 'Enter list of label names (ie: ECG,BP,EEG} )',... 'Enter Annotation name: '}; dlgParam.name='Select signals to load from workspace:'; dlgParam.numlines=1; answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines); %Convert variables to format expected by the GUI varnames=regexp(answer{1},',','split'); tentative_Fs=answer{2}; Fs=[]; try Fs=str2num(tentative_Fs); catch %Maybe it is a variable, evaluate it! Fs=evalin('base',tentative_Fs); end tags=regexp(answer{3},',','split'); M=length(varnames); signal=[]; info=[]; ann=[]; annLabels=[]; if(length(answer{4})>1) ann=evalin('base',answer{4}); if(~isempty(ann)) wfdbShowAnn1Labels(1) [annType,annSubtype,annChan,annNum,annComments]=getAnnFields(); annLabels=struct([]); for n=1:length(ann) annLabels(end+1).type=annType; annLabels(end).subtype=annSubtype; annLabels(end).chan=annChan; annLabels(end).num=annNum; annLabels(end).comment=annComments; end end end signalDescription=cell(M,1); for m=1:M signal(:,m)=evalin('base',varnames{m}); info(m).Description=tags{m}; info(m).SamplingFrequency=Fs; info(m).Gain='1'; info(m).Baseline=0; signalDescription(m)={info(m).Description}; end N=length(signal); tm=[0:N-1]'./Fs; set(handles.signalList,'String',signalDescription) function [analysisSignal,analysisUnits]=wfdbF1Track(analysisSignal,Fs) persistent dlgParam if(isempty(dlgParam)) dlgParam.prompt={'Order of how many harmonics to track:',... 'Initial guess of the fundamental frequency (Hz)', 'Step size of the LMS algorithm',... 'Magnitude of the pole for the harmonic comb filter'}; dlgParam.P='3'; dlgParam.theta='[]'; dlgParam.mu='10^-3'; dlgParam.r='0.85'; dlgParam.answer={'[analysisSigal,analysisTime]=foo(analysisSignal,analysisTime,Fs)'}; dlgParam.name='Parameters for tracking fundamental frequency'; dlgParam.numlines=1; end answer=inputdlg(dlgParam.prompt,dlgParam.name,dlgParam.numlines, {dlgParam.P, ... dlgParam.theta,dlgParam.mu,dlgParam.r}); if(isempty(answer)) analysisSignal=[]; analysisUnits=[]; return; end dlgParam.P=answer{1}; dlgParam.theta=answer{2}; dlgParam.mu=answer{3}; dlgParam.r=answer{4}; if(strcmp(dlgParam.theta,'[]')) %use peak on low range of FFT as an estimate [Pxx,F] = pwelch(analysisSignal,[],[],[],Fs); FUP=100; [~,maxInd]=min(abs(F-FUP)); [~,maxP]=max(Pxx(1:maxInd)); dlgParam.theta=num2str(F(maxP)); end h = waitbar(0,'Estimating fundamental frequency. Please wait...'); analysisSignal=harmonic_est(analysisSignal,str2num(dlgParam.P),Fs,... str2num(dlgParam.theta),str2num(dlgParam.mu),str2num(dlgParam.r)); analysisUnits='Hz'; close(h) function wfdbShowAnn1Labels(promptMe) global ann1LabelsDisplaySetting if(~isfield(ann1LabelsDisplaySetting,'prompt')) ann1LabelsDisplaySetting.prompt={'Ann1 Types (if empty, show all types, if -1 don''t display):',... 'Show Comments (true/false):','Display label on original channel only (true/false):',... 'Display Label Types & Comments when there are at most N labels:', ... 'Display abnormal labels only (true/false):'}; ann1LabelsDisplaySetting.showType='[]'; ann1LabelsDisplaySetting.showComment='true'; ann1LabelsDisplaySetting.channelSpecific='false'; ann1LabelsDisplaySetting.threshold='100'; ann1LabelsDisplaySetting.abnormalOnly='false'; ann1LabelsDisplaySetting.name='Displays Ann1 Type and Comments'; ann1LabelsDisplaySetting.numlines=1; end if(promptMe) answer=inputdlg(ann1LabelsDisplaySetting.prompt,ann1LabelsDisplaySetting.name,ann1LabelsDisplaySetting.numlines,... {ann1LabelsDisplaySetting.showType,ann1LabelsDisplaySetting.showComment, ann1LabelsDisplaySetting.channelSpecific,... ann1LabelsDisplaySetting.threshold,ann1LabelsDisplaySetting.abnormalOnly}); ann1LabelsDisplaySetting.showType= answer{1}; ann1LabelsDisplaySetting.showComment= answer{2}; ann1LabelsDisplaySetting.channelSpecific=answer{3}; ann1LabelsDisplaySetting.threshold=answer{4}; ann1LabelsDisplaySetting.abnormalOnly=answer{5}; end function theta_curve=harmonic_est(x,varargin) % %[theta_curve]=harmonic_est(x,P,Fs,theta,mu,r) % Implements harmonic frequency tracking algorithm as described in % IEEE Signal Processing Magazine (189) 11/09 by Tan and Jiang. % Parameters are: % % Input: % x - (Nx1, required) Signal to be tracked % P - (1x1, required) Order of how many harmonics (including fundamental) % to be estimated. % Fs - (1x1, optional) Sampling frequency (Hz). If present output is returned % in Hertz, if absent output is returned in radians. % theta - (1x1, optional) Initial guess of the fundamental frequency capture range. % mu - (1x1, optional) Step size of the LMS algorithm. % r - (1x1, optinoal) Magnitude of the pole for the harmonic comb filter % (02 && ~isempty(varargin{2}) ) Fs=varargin{2}; end if(nargin>3 && ~isempty(varargin{3}) ) theta=varargin{3}; end if(nargin>4 && ~isempty(varargin{4}) ) mu=varargin{4}; end if(nargin>5 && ~isempty(varargin{5}) ) r=varargin{5}; end THETA=linspace(0,pi/P,step); F=THETA; Ntheta=length(THETA); MSE=zeros(Ntheta,1); MSE1=zeros(Ntheta,1); if(isempty(theta)) %Default guess to a frequency on the low range theta=60; end %Step 1- Convert theta to radians theta=theta*2*pi/Fs; %Step 2 - Apply LMS to optimize Theta beta=zeros(P+1,1); ym=zeros(P+1,1); theta_curve=zeros(N,1)+NaN; ym_old=zeros(P+1,2); beta_old=zeros(P+1,2); ym_const=zeros(P+1,2); ym_old(1,:)=[0 0]; for n=1:N ym=rec_step(ym_old,ym_const,x(n),theta,P+1,r); beta=rec_step(beta_old,ym_old(:,1),0,theta,P+1,r); ym_old=[ym ym_old(:,1)]; beta_old=[beta beta_old(:,1)]; theta_curve(n)=theta; theta= theta - 2*mu*ym(end)*beta(end); end theta_curve=theta_curve*Fs/(2*pi); function out = rec_step(out_old,const,init,theta,P,r) out=zeros(P,1); out(1)=init; for p=2:P w=(p-1)*theta; out(p)= out(p-1) - 2*cos(w)*out_old(p-1,1) + ... 2*(p-1)*sin(w)*const(p-1) + out_old(p-1,2) + ... 2*r*cos(w)*out_old(p,1) - (r^2)*out_old(p,2) - ... 2*r*(p-1)*sin(w)*const(p); end