a b/pan_tompkin.m
1
function [qrs_amp_raw,qrs_i_raw,delay]=pan_tompkin(ecg,fs,gr)
2
3
%% function [qrs_amp_raw,qrs_i_raw,delay]=pan_tompkin(ecg,fs)
4
% Complete implementation of Pan-Tompkins algorithm
5
6
%% Inputs
7
% ecg : raw ecg vector signal 1d signal
8
% fs : sampling frequency e.g. 200Hz, 400Hz and etc
9
% gr : flag to plot or not plot (set it 1 to have a plot or set it zero not
10
% to see any plots
11
%% Outputs
12
% qrs_amp_raw : amplitude of R waves amplitudes
13
% qrs_i_raw : index of R waves
14
% delay : number of samples which the signal is delayed due to the
15
% filtering
16
%% Method :
17
18
%% PreProcessing
19
% 1) Signal is preprocessed , if the sampling frequency is higher then it is downsampled
20
% and if it is lower upsampled to make the sampling frequency 200 Hz
21
% with the same filtering setups introduced in Pan
22
% tompkins paper (a combination of low pass and high pass filter 5-15 Hz)
23
% to get rid of the baseline wander and muscle noise. 
24
25
% 2) The filtered signal
26
% is derivated using a derivating filter to high light the QRS complex.
27
28
% 3) Signal is squared.4)Signal is averaged with a moving window to get rid
29
% of noise (0.150 seconds length).
30
31
% 5) depending on the sampling frequency of your signal the filtering
32
% options are changed to best match the characteristics of your ecg signal
33
34
% 6) Unlike the other implementations in this implementation the desicion
35
% rule of the Pan tompkins is implemented completely.
36
37
%% Decision Rule 
38
% At this point in the algorithm, the preceding stages have produced a roughly pulse-shaped
39
% waveform at the output of the MWI . The determination as to whether this pulse
40
% corresponds to a QRS complex (as opposed to a high-sloped T-wave or a noise artefact) is
41
% performed with an adaptive thresholding operation and other decision
42
% rules outlined below;
43
44
% a) FIDUCIAL MARK - The waveform is first processed to produce a set of weighted unit
45
% samples at the location of the MWI maxima. This is done in order to localize the QRS
46
% complex to a single instant of time. The w[k] weighting is the maxima value.
47
48
% b) THRESHOLDING - When analyzing the amplitude of the MWI output, the algorithm uses
49
% two threshold values (THR_SIG and THR_NOISE, appropriately initialized during a brief
50
% 2 second training phase) that continuously adapt to changing ECG signal quality. The
51
% first pass through y[n] uses these thresholds to classify the each non-zero sample
52
% (CURRENTPEAK) as either signal or noise:
53
% If CURRENTPEAK > THR_SIG, that location is identified as a QRS complex
54
% candidate?and the signal level (SIG_LEV) is updated:
55
% SIG _ LEV = 0.125 CURRENTPEAK + 0.875?SIG _ LEV
56
57
% If THR_NOISE < CURRENTPEAK < THR_SIG, then that location is identified as a
58
% Noise peak?and the noise level (NOISE_LEV) is updated:
59
% NOISE _ LEV = 0.125CURRENTPEAK + 0.875?NOISE _ LEV
60
% Based on new estimates of the signal and noise levels (SIG_LEV and NOISE_LEV,
61
% respectively) at that point in the ECG, the thresholds are adjusted as follows:
62
% THR _ SIG = NOISE _ LEV + 0.25 ?(SIG _ LEV-NOISE _ LEV )
63
% THR _ NOISE = 0.5?(THR _ SIG)
64
% These adjustments lower the threshold gradually in signal segments that are deemed to
65
% be of poorer quality.
66
67
68
% c) SEARCHBACK FOR MISSED QRS COMPLEXES - In the thresholding step above, if
69
% CURRENTPEAK < THR_SIG, the peak is deemed not to have resulted from a QRS
70
% complex. If however, an unreasonably long period has expired without an abovethreshold
71
% peak, the algorithm will assume a QRS has been missed and perform a
72
% searchback. This limits the number of false negatives. The minimum time used to trigger
73
% a searchback is 1.66 times the current R peak to R peak time period (called the RR
74
% interval). This value has a physiological origin - the time value between adjacent
75
% heartbeats cannot change more quickly than this. The missed QRS complex is assumed
76
% to occur at the location of the highest peak in the interval that lies between THR_SIG and
77
% THR_NOISE. In this algorithm, two average RR intervals are stored,the first RR interval is 
78
% calculated as an average of the last eight QRS locations in order to adapt to changing heart 
79
% rate and the second RR interval mean is the mean 
80
% of the most regular RR intervals . The threshold is lowered if the heart rate is not regular 
81
% to improve detection.
82
83
% d) ELIMINATION OF MULTIPLE DETECTIONS WITHIN REFRACTORY PERIOD - It is
84
% impossible for a legitimate QRS complex to occur if it lies within 200ms after a previously
85
% detected one. This constraint is a physiological one ?due to the refractory period during
86
% which ventricular depolarization cannot occur despite a stimulus[1]. As QRS complex
87
% candidates are generated, the algorithm eliminates such physically impossible events,
88
% thereby reducing false positives.
89
90
% e) T WAVE DISCRIMINATION - Finally, if a QRS candidate occurs after the 200ms
91
% refractory period but within 360ms of the previous QRS, the algorithm determines
92
% whether this is a genuine QRS complex of the next heartbeat or an abnormally prominent
93
% T wave. This decision is based on the mean slope of the waveform at that position. A slope of
94
% less than one half that of the previous QRS complex is consistent with the slower
95
% changing behaviour of a T wave ?otherwise, it becomes a QRS detection.
96
% Extra concept : beside the points mentioned in the paper, this code also
97
% checks if the occured peak which is less than 360 msec latency has also a
98
% latency less than 0,5*mean_RR if yes this is counted as noise
99
100
% f) In the final stage , the output of R waves detected in smoothed signal is analyzed and double
101
% checked with the help of the output of the bandpass signal to improve
102
% detection and find the original index of the real R waves on the raw ecg
103
% signal
104
105
%% References :
106
107
%[1]PAN.J, TOMPKINS. W.J,"A Real-Time QRS Detection Algorithm" IEEE
108
%TRANSACTIONS ON BIOMEDICAL ENGINEERING, VOL. BME-32, NO. 3, MARCH 1985.
109
110
%% Author : Hooman Sedghamiz
111
% Linkoping university 
112
% email : hoose792@student.liu.se
113
% hooman.sedghamiz@medel.com
114
115
% Any direct or indirect use of this code should be referenced 
116
% Copyright march 2014
117
%%
118
if ~isvector(ecg)
119
  error('ecg must be a row or column vector');
120
end
121
122
123
if nargin < 3
124
    gr = 1;   % on default the function always plots
125
end
126
ecg = ecg(:); % vectorize
127
128
%% Initialize
129
qrs_c =[]; %amplitude of R
130
qrs_i =[]; %index
131
SIG_LEV = 0; 
132
nois_c =[];
133
nois_i =[];
134
delay = 0;
135
skip = 0; % becomes one when a T wave is detected
136
not_nois = 0; % it is not noise when not_nois = 1
137
selected_RR =[]; % Selected RR intervals
138
m_selected_RR = 0;
139
mean_RR = 0;
140
qrs_i_raw =[];
141
qrs_amp_raw=[];
142
ser_back = 0; 
143
test_m = 0;
144
SIGL_buf = [];
145
NOISL_buf = [];
146
THRS_buf = [];
147
SIGL_buf1 = [];
148
NOISL_buf1 = [];
149
THRS_buf1 = [];
150
151
152
%% Plot differently based on filtering settings
153
if gr
154
 if fs == 200
155
  figure,  ax(1)=subplot(321);plot(ecg);axis tight;title('Raw ECG Signal');
156
 else
157
  figure,  ax(1)=subplot(3,2,[1 2]);plot(ecg);axis tight;title('Raw ECG Signal');
158
 end
159
end    
160
%% Noise cancelation(Filtering) % Filters (Filter in between 5-15 Hz)
161
if fs == 200
162
%% Low Pass Filter  H(z) = ((1 - z^(-6))^2)/(1 - z^(-1))^2
163
b = [1 0 0 0 0 0 -2 0 0 0 0 0 1];
164
a = [1 -2 1];
165
h_l = filter(b,a,[1 zeros(1,12)]); 
166
ecg_l = conv (ecg ,h_l);
167
ecg_l = ecg_l/ max( abs(ecg_l));
168
delay = 6; %based on the paper
169
if gr
170
ax(2)=subplot(322);plot(ecg_l);axis tight;title('Low pass filtered');
171
end
172
%% High Pass filter H(z) = (-1+32z^(-16)+z^(-32))/(1+z^(-1))
173
b = [-1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 -32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1];
174
a = [1 -1];
175
h_h = filter(b,a,[1 zeros(1,32)]); 
176
ecg_h = conv (ecg_l ,h_h);
177
ecg_h = ecg_h/ max( abs(ecg_h));
178
delay = delay + 16; % 16 samples for highpass filtering
179
if gr
180
ax(3)=subplot(323);plot(ecg_h);axis tight;title('High Pass Filtered');
181
end
182
else
183
%% bandpass filter for Noise cancelation of other sampling frequencies(Filtering)
184
f1=5; %cuttoff low frequency to get rid of baseline wander
185
f2=15; %cuttoff frequency to discard high frequency noise
186
Wn=[f1 f2]*2/fs; % cutt off based on fs
187
N = 3; % order of 3 less processing
188
[a,b] = butter(N,Wn); %bandpass filtering
189
ecg_h = filtfilt(a,b,ecg);
190
ecg_h = ecg_h/ max( abs(ecg_h));
191
if gr
192
ax(3)=subplot(323);plot(ecg_h);axis tight;title('Band Pass Filtered');
193
end
194
end
195
%% derivative filter H(z) = (1/8T)(-z^(-2) - 2z^(-1) + 2z + z^(2))
196
h_d = [-1 -2 0 2 1]*(1/8);%1/8*fs
197
ecg_d = conv (ecg_h ,h_d);
198
ecg_d = ecg_d/max(ecg_d);
199
delay = delay + 2; % delay of derivative filter 2 samples
200
if gr
201
ax(4)=subplot(324);plot(ecg_d);axis tight;title('Filtered with the derivative filter');
202
end
203
%% Squaring nonlinearly enhance the dominant peaks
204
ecg_s = ecg_d.^2;
205
if gr
206
ax(5)=subplot(325);plot(ecg_s);axis tight;title('Squared');
207
end
208
209
210
211
%% Moving average Y(nt) = (1/N)[x(nT-(N - 1)T)+ x(nT - (N - 2)T)+...+x(nT)]
212
ecg_m = conv(ecg_s ,ones(1 ,round(0.150*fs))/round(0.150*fs));
213
delay = delay + 15;
214
215
if gr
216
ax(6)=subplot(326);plot(ecg_m);axis tight;title('Averaged with 30 samples length,Black noise,Green Adaptive Threshold,RED Sig Level,Red circles QRS adaptive threshold');
217
axis tight;
218
end
219
220
%% Fiducial Mark 
221
% Note : a minimum distance of 40 samples is considered between each R wave
222
% since in physiological point of view no RR wave can occur in less than
223
% 200 msec distance
224
[pks,locs] = findpeaks(ecg_m,'MINPEAKDISTANCE',round(0.2*fs));
225
226
227
228
229
%% initialize the training phase (2 seconds of the signal) to determine the THR_SIG and THR_NOISE
230
THR_SIG = max(ecg_m(1:2*fs))*1/3; % 0.25 of the max amplitude 
231
THR_NOISE = mean(ecg_m(1:2*fs))*1/2; % 0.5 of the mean signal is considered to be noise
232
SIG_LEV= THR_SIG;
233
NOISE_LEV = THR_NOISE;
234
235
236
%% Initialize bandpath filter threshold(2 seconds of the bandpass signal)
237
THR_SIG1 = max(ecg_h(1:2*fs))*1/3; % 0.25 of the max amplitude 
238
THR_NOISE1 = mean(ecg_h(1:2*fs))*1/2; %
239
SIG_LEV1 = THR_SIG1; % Signal level in Bandpassed filter
240
NOISE_LEV1 = THR_NOISE1; % Noise level in Bandpassed filter
241
%% Thresholding and online desicion rule
242
243
for i = 1 : length(pks)
244
    
245
   %% locate the corresponding peak in the filtered signal 
246
    if locs(i)-round(0.150*fs)>= 1 && locs(i)<= length(ecg_h)
247
          [y_i x_i] = max(ecg_h(locs(i)-round(0.150*fs):locs(i)));
248
       else
249
          if i == 1
250
            [y_i x_i] = max(ecg_h(1:locs(i)));
251
            ser_back = 1;
252
          elseif locs(i)>= length(ecg_h)
253
            [y_i x_i] = max(ecg_h(locs(i)-round(0.150*fs):end));
254
          end
255
        
256
     end
257
    
258
    
259
  %% update the heart_rate (Two heart rate means one the moste recent and the other selected)
260
    if length(qrs_c) >= 9 
261
        
262
        diffRR = diff(qrs_i(end-8:end)); %calculate RR interval
263
        mean_RR = mean(diffRR); % calculate the mean of 8 previous R waves interval
264
        comp =qrs_i(end)-qrs_i(end-1); %latest RR
265
        if comp <= 0.92*mean_RR || comp >= 1.16*mean_RR
266
            % lower down thresholds to detect better in MVI
267
                THR_SIG = 0.5*(THR_SIG);
268
                %THR_NOISE = 0.5*(THR_SIG);  
269
               % lower down thresholds to detect better in Bandpass filtered 
270
                THR_SIG1 = 0.5*(THR_SIG1);
271
                %THR_NOISE1 = 0.5*(THR_SIG1); 
272
                
273
        else
274
            m_selected_RR = mean_RR; %the latest regular beats mean
275
        end 
276
          
277
    end
278
    
279
      %% calculate the mean of the last 8 R waves to make sure that QRS is not
280
       % missing(If no R detected , trigger a search back) 1.66*mean
281
       
282
       if m_selected_RR
283
           test_m = m_selected_RR; %if the regular RR availabe use it   
284
       elseif mean_RR && m_selected_RR == 0
285
           test_m = mean_RR;   
286
       else
287
           test_m = 0;
288
       end
289
        
290
    if test_m
291
          if (locs(i) - qrs_i(end)) >= round(1.66*test_m)% it shows a QRS is missed 
292
              [pks_temp,locs_temp] = max(ecg_m(qrs_i(end)+ round(0.200*fs):locs(i)-round(0.200*fs))); % search back and locate the max in this interval
293
              locs_temp = qrs_i(end)+ round(0.200*fs) + locs_temp -1; %location 
294
             
295
              if pks_temp > THR_NOISE
296
               qrs_c = [qrs_c pks_temp];
297
               qrs_i = [qrs_i locs_temp];
298
              
299
               % find the location in filtered sig
300
               if locs_temp <= length(ecg_h)
301
                [y_i_t x_i_t] = max(ecg_h(locs_temp-round(0.150*fs):locs_temp));
302
               else
303
                [y_i_t x_i_t] = max(ecg_h(locs_temp-round(0.150*fs):end));
304
               end
305
               % take care of bandpass signal threshold
306
               if y_i_t > THR_NOISE1 
307
                        
308
                      qrs_i_raw = [qrs_i_raw locs_temp-round(0.150*fs)+ (x_i_t - 1)];% save index of bandpass 
309
                      qrs_amp_raw =[qrs_amp_raw y_i_t]; %save amplitude of bandpass 
310
                      SIG_LEV1 = 0.25*y_i_t + 0.75*SIG_LEV1; %when found with the second thres 
311
               end
312
               
313
               not_nois = 1;
314
               SIG_LEV = 0.25*pks_temp + 0.75*SIG_LEV ;  %when found with the second threshold             
315
             end 
316
              
317
          else
318
              not_nois = 0;
319
              
320
          end
321
    end
322
      
323
    
324
    
325
    
326
    %%  find noise and QRS peaks
327
    if pks(i) >= THR_SIG
328
        
329
                 % if a QRS candidate occurs within 360ms of the previous QRS
330
                 % ,the algorithm determines if its T wave or QRS
331
                 if length(qrs_c) >= 3
332
                      if (locs(i)-qrs_i(end)) <= round(0.3600*fs)
333
                        Slope1 = mean(diff(ecg_m(locs(i)-round(0.075*fs):locs(i)))); %mean slope of the waveform at that position
334
                        Slope2 = mean(diff(ecg_m(qrs_i(end)-round(0.075*fs):qrs_i(end)))); %mean slope of previous R wave
335
                             if abs(Slope1) <= abs(0.5*(Slope2))  % slope less then 0.5 of previous R
336
                                 nois_c = [nois_c pks(i)];
337
                                 nois_i = [nois_i locs(i)];
338
                                 skip = 1; % T wave identification
339
                                 % adjust noise level in both filtered and
340
                                 % MVI
341
                                 NOISE_LEV1 = 0.125*y_i + 0.875*NOISE_LEV1;
342
                                 NOISE_LEV = 0.125*pks(i) + 0.875*NOISE_LEV; 
343
                             else
344
                                 skip = 0;
345
                             end
346
            
347
                      end
348
                 end
349
        
350
        if skip == 0  % skip is 1 when a T wave is detected       
351
        qrs_c = [qrs_c pks(i)];
352
        qrs_i = [qrs_i locs(i)];
353
        
354
        % bandpass filter check threshold
355
         if y_i >= THR_SIG1
356
                        if ser_back 
357
                           qrs_i_raw = [qrs_i_raw x_i];  % save index of bandpass 
358
                        else
359
                           qrs_i_raw = [qrs_i_raw locs(i)-round(0.150*fs)+ (x_i - 1)];% save index of bandpass 
360
                        end
361
                           qrs_amp_raw =[qrs_amp_raw y_i];% save amplitude of bandpass 
362
          SIG_LEV1 = 0.125*y_i + 0.875*SIG_LEV1;% adjust threshold for bandpass filtered sig
363
         end
364
         
365
        % adjust Signal level
366
        SIG_LEV = 0.125*pks(i) + 0.875*SIG_LEV ;
367
        end
368
        
369
        
370
    elseif THR_NOISE <= pks(i) && pks(i)<THR_SIG
371
        
372
         %adjust Noise level in filtered sig
373
         NOISE_LEV1 = 0.125*y_i + 0.875*NOISE_LEV1;
374
         %adjust Noise level in MVI
375
         NOISE_LEV = 0.125*pks(i) + 0.875*NOISE_LEV; 
376
        
377
        
378
      
379
    elseif pks(i) < THR_NOISE
380
        nois_c = [nois_c pks(i)];
381
        nois_i = [nois_i locs(i)];
382
        
383
        % noise level in filtered signal
384
        NOISE_LEV1 = 0.125*y_i + 0.875*NOISE_LEV1;
385
        %end
386
        
387
         %adjust Noise level in MVI
388
        NOISE_LEV = 0.125*pks(i) + 0.875*NOISE_LEV;  
389
        
390
           
391
    end
392
    
393
    
394
    
395
 
396
    
397
    %% adjust the threshold with SNR
398
    if NOISE_LEV ~= 0 || SIG_LEV ~= 0
399
        THR_SIG = NOISE_LEV + 0.25*(abs(SIG_LEV - NOISE_LEV));
400
        THR_NOISE = 0.5*(THR_SIG);
401
    end
402
    
403
    % adjust the threshold with SNR for bandpassed signal
404
    if NOISE_LEV1 ~= 0 || SIG_LEV1 ~= 0
405
        THR_SIG1 = NOISE_LEV1 + 0.25*(abs(SIG_LEV1 - NOISE_LEV1));
406
        THR_NOISE1 = 0.5*(THR_SIG1);
407
    end
408
    
409
    
410
% take a track of thresholds of smoothed signal
411
SIGL_buf = [SIGL_buf SIG_LEV];
412
NOISL_buf = [NOISL_buf NOISE_LEV];
413
THRS_buf = [THRS_buf THR_SIG];
414
415
% take a track of thresholds of filtered signal
416
SIGL_buf1 = [SIGL_buf1 SIG_LEV1];
417
NOISL_buf1 = [NOISL_buf1 NOISE_LEV1];
418
THRS_buf1 = [THRS_buf1 THR_SIG1];
419
420
421
422
    
423
 skip = 0; %reset parameters
424
 not_nois = 0; %reset parameters
425
 ser_back = 0;  %reset bandpass param   
426
end
427
428
if gr
429
hold on,scatter(qrs_i,qrs_c,'m');
430
hold on,plot(locs,NOISL_buf,'--k','LineWidth',2);
431
hold on,plot(locs,SIGL_buf,'--r','LineWidth',2);
432
hold on,plot(locs,THRS_buf,'--g','LineWidth',2);
433
if ax(:)
434
linkaxes(ax,'x');
435
zoom on;
436
end
437
end
438
439
440
441
442
%% overlay on the signals
443
if gr
444
figure,az(1)=subplot(311);plot(ecg_h);title('QRS on Filtered Signal');axis tight;
445
hold on,scatter(qrs_i_raw,qrs_amp_raw,'m');
446
hold on,plot(locs,NOISL_buf1,'LineWidth',2,'Linestyle','--','color','k');
447
hold on,plot(locs,SIGL_buf1,'LineWidth',2,'Linestyle','-.','color','r');
448
hold on,plot(locs,THRS_buf1,'LineWidth',2,'Linestyle','-.','color','g');
449
az(2)=subplot(312);plot(ecg_m);title('QRS on MVI signal and Noise level(black),Signal Level (red) and Adaptive Threshold(green)');axis tight;
450
hold on,scatter(qrs_i,qrs_c,'m');
451
hold on,plot(locs,NOISL_buf,'LineWidth',2,'Linestyle','--','color','k');
452
hold on,plot(locs,SIGL_buf,'LineWidth',2,'Linestyle','-.','color','r');
453
hold on,plot(locs,THRS_buf,'LineWidth',2,'Linestyle','-.','color','g');
454
az(3)=subplot(313);plot(ecg-mean(ecg));title('Pulse train of the found QRS on ECG signal');axis tight;
455
line(repmat(qrs_i_raw,[2 1]),repmat([min(ecg-mean(ecg))/2; max(ecg-mean(ecg))/2],size(qrs_i_raw)),'LineWidth',2.5,'LineStyle','-.','Color','r');
456
linkaxes(az,'x');
457
zoom on;
458
end
459
end
460
 
461
462
463
464
465
466
467
468
469