PhysioNet Cardiovascular Signal Toolbox 1.0.0

function out = run_sqrs(ecg,HRVparams,rs)
%out = run_sqrs(ecg,s,rs)
%	OVERVIEW:
%       QRS onset detector
%   INPUT:
%       ecg = single channel of ECG in digital values
%       s   = settings struct
%       rs  = resampling option; 1 = resample original signal, 0 = don't
%   OUTPUT:
%       out = sample points of onset of each QRS complex
%   DEPENDENCIES & LIBRARIES:
%   REFERENCE: 
%	REPO:       
%       https://github.com/cliffordlab/hrv_toolbox
%   ORIGINAL SOURCE AND AUTHORS:     
%       Converted from sqrs.c at:
%       http://www.physionet.org/physiotools/wfdb/app/sqrs.c
%       to  sqrs.m (Matlab)     by J. Perry,    July        2006
%           sqrs.c	(C)         by	G.B. Moody  27 October  1990
%
%	Last revised by John:  25 February 2006
%	Last revised by Gari:  07 February 2007
%   
%   03-06-2017
%   Edited by Adriana Vest
%   Now requires settings struct HRVparams, which defines debug mode and 
%   sampling frequency, and resampling option rs.
%   LICENSE AND COPYRIGHT:    
%       See Below
% 
% if debug>0, then plot final data (default; debug=0)
% 
% This algorithm is sensitive to high frequency noise, so you 
% might want to use an FIR band-pass filter first.
% 
% Avaialble under the GPL (see below)
%-------------------------------------------------------------------------------
% sqrs: Single-channel QRS detector
% Copyright (C) 1990-2006 George B. Moody
%
% 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.
%
% You may contact the author by e-mail (george@mit.edu) or postal
% mail (MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to
% this software, please visit PhysioNet (http://www.physionet.org/).
% _________________________________________________________________
%
% The detector algorithm is based on example 10 in the WFDB
% Programmer's Guide, which in turn is based on a Pascal program
% written by W.A.H. Engelse and C. Zeelenberg, "A single scan
% algorithm for QRS-detection and feature extraction", Computers in
% Cardiology 6:37-42 (1979).  `sqrs' does not include the feature
% extraction capability of the Pascal program.  The output of `sqrs'
% is an annotation file (with annotator name `qrs') in which all
% detected beats are labelled normal;  the annotation file may also
% contain `artifact' annotations at locations which `sqrs' believes
% are noise-corrupted.
%
% 'sqrs' has been optimized for adult human ECGs. For other ECGs,
% it may be necessary to experiment with the input sampling
% frequency and the time constants indicated below. 
%
% This program is provided as an example only, and is not intended
% for any clinical application.  At the time the algorithm was
% originally published, its performance was typical of
% state-of-the-art QRS detectors.  Recent designs, particularly
% those that can analyze two or more input signals, may exhibit
% significantly better performance.
%%

if nargin<2
    error('Incorrect number of input arguments: must provide ECG signal and HRV paramters struct')
end
if nargin < 3
    rs = 1;
end

%debug = s.debug;
debug = 0;
fs = HRVparams.Fs;

if ~rs
    %% 1. Use Fs of Input
    time = 0;
    now = 10;

    %freq = 256;
    freq = fs;
    ms160 = ceil(0.16*freq);
    ms200 = ceil(0.2*freq);
    s2 = ceil(2*freq);
    scmin = 500;
    % number of ADC units corresponding to scmin microvolts
    % scmin = muvadu(signal, scmin); 
    scmax = 10 * scmin;
    slopecrit = 10 * scmin;
    maxslope = 0;
    nslope = 0;
    out = [];

    while (now < length(ecg)) %   && (to == 0)
        filter = [1 4 6 4 1 -1 -4 -6 -4 -1] * ecg((now-9):now);
        if (mod(time, s2) == 0)
	    % Adjust slope 
            if (nslope == 0)
	      slopecrit = max(slopecrit - slopecrit/16, scmin);
            elseif (nslope >= 5)
	      slopecrit = min(slopecrit + slopecrit/16, scmax);
            end
        end
        if (nslope == 0  && abs(filter) > slopecrit)
            nslope = nslope + 1; 
	    maxtime = ms160;
	    if (filter > 0) 
	      sign = 1;
	    else
	      sign = -1;
	    end
            qtime = time;
        end
        if (nslope ~= 0)
            if (filter * sign < -slopecrit)
                sign = -sign;
                nslope = nslope + 1;
                if (nslope > 4) 
                  maxtime = ms200;
                else
                  maxtime = ms160;
                end
            elseif (filter * sign > slopecrit &&  abs(filter) > maxslope)
                maxslope = abs(filter);
            end
            
            if (maxtime < 0)
                if (2 <= nslope && nslope <= 4)
                    slopecrit = slopecrit + ((maxslope/4) - slopecrit)/8;
                    if (slopecrit < scmin)
                        slopecrit = scmin;
                    elseif (slopecrit > scmax) 
                        slopecrit = scmax;
                    end
                    out = [out; now - (time - qtime) - 4];
                    %annot.anntyp = NORMAL; 
                    time = 0;
                elseif (nslope >= 5)
                    out = [out; now - (time - qtime) - 4];
                    %annot.anntyp = ARFCT; 
                end
                nslope = 0;
            end
            maxtime = maxtime - 1;
        end
	time = time + 1;
	now = now + 1;
    end

    out=out-1; % adjust for 1 sample offset problem.

    if debug > 0
       plot(ecg,'b');
       hold on;
       plot(out,ecg(out),'m*'); 
    end
elseif rs
    %% 2. Resample at 256 Hz
    % These time constants may need adjustment for pediatric or
    % small mammal ECGs.
    ecg_rs = resample(ecg,256,fs);
    
    time = 0;
    now = 10;

    freq = 256;
    ms160 = ceil(0.16*freq);
    ms200 = ceil(0.2*freq);
    s2 = ceil(2*freq);
    %scmin = 500;
    % ANV changed this to work with real units
    scmin = 500;
    % number of ADC units corresponding to scmin microvolts
    % scmin = muvadu(ecg_rs, scmin); 
    scmax = 10 * scmin;
    slopecrit = 10 * scmin;
    maxslope = 0;
    nslope = 0;
    out = [];

    while (now < length(ecg_rs)) %   && (to == 0)
        filter = [1 4 6 4 1 -1 -4 -6 -4 -1] * ecg_rs((now-9):now);
        if (mod(time, s2) == 0)
            % Adjust slope 
            if (nslope == 0)
                slopecrit = max(slopecrit - slopecrit/16, scmin);
            elseif (nslope >= 5)
                slopecrit = min(slopecrit + slopecrit/16, scmax);
            end
        end
        if (nslope == 0  && abs(filter) > slopecrit)
            nslope = nslope + 1; 
            maxtime = ms160;
            if (filter > 0) 
                sign = 1;
            else
                sign = -1;
            end
            qtime = time;
        end
        if (nslope ~= 0)
            if (filter * sign < -slopecrit)
                sign = -sign;
                nslope = nslope + 1;
                if (nslope > 4) 
                    maxtime = ms200;
                else
                  maxtime = ms160;
                end
            elseif (filter * sign > slopecrit &&  abs(filter) > maxslope)
                maxslope = abs(filter);
            end
            if (maxtime < 0)
                if (2 <= nslope && nslope <= 4)
                    slopecrit = slopecrit + ((maxslope/4) - slopecrit)/8;
                    if (slopecrit < scmin)
                        slopecrit = scmin;
                    elseif (slopecrit > scmax) 
                        slopecrit = scmax;
                    end
                    out = [out; now - (time - qtime) - 4];
                    %annot.anntyp = NORMAL; 
                    time = 0;
                elseif (nslope >= 5)
                    out = [out; now - (time - qtime) - 4];
                    %annot.anntyp = ARFCT; 
                end
                nslope = 0;
            end
            maxtime = maxtime - 1;
        end
	time = time + 1;
	now = now + 1;
    end

    out=out-1; % adjust for 1 sample offset problem.

    if debug > 0
       plot(ecg_rs,'b');
       hold on;
       plot(out,ecg_rs(out),'m*'); 
    end

end