function sgolaydemo(action);
%SGOLAYDEMO Demonstrates Savitzky-Golay filtering.
%   This GUI demonstrates the smoothing obtained        
%   with Savitzky-Golay filters on noisy ECG signals.  
%                                                      
%   Three plots are shown: The noisy ECG signal, 
%   the smoothed signal and the noiseless signal.              
%                                                      
%   The noise level can be changed with a slider                 
%   control. The Savitzky-Golay parameters can be      
%   varied; in particular the degree of the polynomial 
%   used to fit the data, and the frame size can be
%   changed.
 
%   Author(s): R. Losada
%   Copyright 1988-2000 The MathWorks, Inc.
%   $Revision: 1.7 $  $Date: 2000/06/08 19:35:15 $
if nargin==0,
   action = 'initialize';
end

x1 = ecg(500);
x = [x1 x1 x1 x1 x1 x1];
y = sgolayfilt(x,0,15); %noiseless signal

switch action, 
case 'initialize',
   % Initialize Graphics
   hfig = figure('NumberTitle','off',...
      'Name','Savitzky-Golay Filtering Demo');  
   % Frame for the all uicontrols
   framepos = [.74 .05 .25 .85];
   uicontrol( ...
      'Style','frame',...
      'Units','normalized',...
      'BackgroundColor',[.6,.6,.6],...
      'Position',framepos);   
   % Frame for the parameters section
   framepos2 = [.75 .36 .23 .23];
   uicontrol( ...
      'Style','frame',...
      'Units','normalized',...
      'ForegroundColor','black',...
      'Position',framepos2);   
   % Savitzky-Golay Filter Parameters Text
   titlepos = [.76 .57 .17 .03];
   titlelabel = 'Filter Parameters';
   uicontrol( ...
      'String',titlelabel,...
      'HorizontalAlignment','left',...
      'Style','text',...
      'Units','normalized',...
      'BackgroundColor',[.72,.72,.72],...
      'ForegroundColor','black',...
      'Position',titlepos); 
   % SG degree menu
   menuname1 = 'Polynomial Degree';
   tposition1 = [.77 .5 .2 .05];
   uicontrol( ...
      'String',menuname1,...
      'HorizontalAlignment','left',...
      'Style','text',...
      'Units','normalized',...
      'ForegroundColor','black',...
      'Position',tposition1);
   mposition1 = [.77 .46 .1 .05];
   popstr1 = {'0','1','2','3','4'};
   degreehndl = uicontrol('String',popstr1,...
      'Style','Popup',...
      'Tag','degreepopup',...
      'Units','normalized',...
      'Position',mposition1,...
      'UserData',str2double(popstr1),...
      'CallBack','sgolaydemo(''recal'')'); 
   % SG frame size menu
   menuname2 = 'Frame Size';
   tposition2 = [.77 .4 .2 .05];
   uicontrol( ...
      'String',menuname2,...
      'Style','text',...
      'HorizontalAlignment','left',...
      'Units','normalized',...
      'ForegroundColor','black',...
      'Position',tposition2);
   mposition2 = [.77 .37 .1 .05];
   popstr2 = {'5','15','25','55'};
   framehndl = uicontrol('String',popstr2,...
      'Style','Popup',...
      'Tag','framepopup',...
      'Units','normalized',...
      'Position',mposition2,...
      'UserData',str2double(popstr2),...
      'CallBack','sgolaydemo(''recal'')');
   % Noise variance slider
   sliname1 = 'Noise Level';
   tposition4 = [.76 .8 .21 .05];
   uicontrol(...
      'String',sliname1,...
      'Style','text',...
      'Units','normalized',...
      'BackgroundColor',[.62,.62,.62],...
      'ForegroundColor','black',...
      'Position',tposition4);
   mposition4 = [.76 .75 .21 .05];
   sliderhndl = uicontrol('Style','slider',...
      'Tag','noiseslider',...
      'Units','normalized',...
      'Value',0.7,...
      'Min',0,'Max',1.4,...
      'Position',mposition4,...
      'CallBack','sgolaydemo(''recal'')');
   slimin = 'Less';
   tposition5 = [.76 .68 .07 .07];
   uicontrol(...
      'String',slimin,...
      'Style','text',...
      'Units','normalized',...
      'HorizontalAlignment','left',...
      'ForegroundColor','black',...
      'BackgroundColor',[.62,.62,.62],...
      'Position',tposition5);
   slimax = 'More';
   tposition6 = [.9 .68 .07 .07];
   uicontrol(...
      'String',slimax,...
      'Style','text',...
      'Units','normalized',...
      'HorizontalAlignment','right',...
      'ForegroundColor','black',...
      'BackgroundColor',[.62,.62,.62],...
      'Position',tposition6);
   % Info and Close buttons
   tposition7 = [.77 .18 .19 .1];
   uicontrol(...
      'String','Info',...
      'Units','normalized',...
      'Position',tposition7,...
      'Callback','sgolaydemo(''info'')');
   tposition8 = [.77 .06 .19 .1];
   uicontrol(...
      'String','Close',...
      'Units','normalized',...
      'ForegroundColor','black',...
      'Position',tposition8,...
      'Callback','sgolaydemo(''done'')');
      
   % Initial values
   noise = 0.7;
   degree = 0;
   frame = 5;
   subplot('Position',[.07 .05 .65 .23]), plot(y)
   set(gca,'XTick',[])
   axis([0 3000 -.6 .8])
   title('Noiseless ECG')
   ha1 = subplot('Position',[.07 .67 .65 .23]); plot(y)
   set(gca,'XTick',[])
   axis([0 3000 -.6 .8])
   title('Noisy ECG')
   ha2 = subplot('Position',[.07 .36 .65 .23]); plot(y)
   set(gca,'XTick',[])
   axis([0 3000 -.6 .8])
   title('Filtered ECG')
   ud.handles.ax = [ha1 ha2];
   ud.handles.uis = [degreehndl framehndl sliderhndl];
   set(hfig,'UserData',ud);
   update_gui(y,noise,degree,frame)
   
case 'recal',
   hfig = gcf;
   ud = get(hfig,'UserData');
   hn = ud.handles.uis(3);
   hd = ud.handles.uis(1);
   hf = ud.handles.uis(2);
   
   noise = get(hn,'Value');
   degindx = get(hd,'Value');
   deg = get(hd,'UserData');
   degree = deg(degindx);
   
   frmindx = get(hf,'Value');
   frm = get(hf,'UserData');
   frame = frm(frmindx);
   update_gui(y,noise,degree,frame)
   
case 'info',
   ttlStr = 'Savitzky-Golay Filtering Demo'; 
   
   hlpStr1= ...                                              
      ['This demo shows the smoothing of an electrocardiogram (ECG) signal' sprintf('\n')...
         'by filtering the noisy ECG with a Savitzky-Golay FIR filter.' sprintf('\n')...
         'You can vary the noise level of the ECG signal to be filtered with the' sprintf('\n')...
         'slider control on the upper right of the GUI. The parameters of the' sprintf('\n')...
         'Savitzky-Golay filter can be changed through the two popup menus provided.' sprintf('\n')...
         '' sprintf('\n')...
         'Savitzky-Golay filters act as smoothers by performing a least squares' sprintf('\n')...
         'fitting of a frame of data to a polynomial of a given degree.' sprintf('\n')...
         '' sprintf('\n')...
         'The frame size indicates the number of samples you are using to perform'  sprintf('\n')...
         'the smoothing for each data point. The degree is the order of the polynomial' sprintf('\n')...
         'to which each frame of data is fitted.' sprintf('\n')...
         '' sprintf('\n')...
         'The noiseless or ideal ECG is shown at the bottom of the figure for' sprintf('\n')...
         'comparison purposes'];
             
   helpwin(hlpStr1, ttlStr);
   
case 'done',
   close(gcf);
end        

function [s,f] = update_signal(y,noise,degree,frame)
%UPDATE_SIGNAL Recalculates the noisy ECG and the filtered ECG signals.
v = 0.05*noise*randn(3000,1);    % noise
s = y + v;         % noisy ecg
f = sgolayfilt(s,degree,frame);

function update_gui(y,noise,degree,frame)
%UPDATE_GUI This function is called to update the GUI with the new values.
hfig = gcf;
[s,f] = update_signal(y,noise,degree,frame);
ud = get(hfig,'UserData');
axhdlnp = ud.handles.ax(1);
axhdlfp = ud.handles.ax(2);
hdn = get(axhdlnp,'Children');
hdf = get(axhdlfp,'Children');
set(hdn,'YData',s);
set(hdf,'YData',f);

function x = ecg(L)
%ECG generator.
%   x = ecg(L) 
%   generates piecewise linear ECG signal of length L
%   must post-smooth it with an N-point smoother:
%   y = sgolayfilt(x, d, F), usually with d=0, and N=3,5,9, etc.

a0 = [0,1,40,1,0,-34,118,-99,0,2,21,2,0,0,0];               % template
d0 = [0,27,59,91,131,141,163,185,195,275,307,339,357,390,440];
a = a0 / max(a0);
d = round(d0 * L / d0(15));            % scale them to fit in length L
d(15)=L;

for i=1:14,
       m = d(i) : d(i+1) - 1;
       slope = (a(i+1) - a(i)) / (d(i+1) - d(i));
       x(m+1) = a(i) + slope * (m - d(i));
end



