function FQRSc = smooth_rr(FQRS,rMQRS,ecg,fs,debug) % look for smoothing RR time series to remove additional FQRS and fill % missing FQRS. This showed to improve the PCinC scoring results in term % of F1 measure by >1% on the training set-a. % This function takes into account a certain number of cases % where there might be missing or extra QRS and given context and prior % on physiology decides where to add/remove QRS. % % inputs % FQRS: FQRS time series (required) % rMQRS: reference MQRS (optional). If rMQRS is inputted then the % algorithm looks for FQRS that are overlapping with the MQRS. If % that is the case then the FQRS fiducial is moved to the centre % between the previous and following FQRS. This is thought to % be more accurate. % ecg: residual signal for context (optional). Instead of guessing the % FQRS location out of complete context, we look at the sign of % the known FQRS and look for a local max/min around the expected % position where we should have a FQRS. % fs: sampling frequency (optional, default: 1kHz) % % output % FQRS: outputed smoothed FQRS time series. % (empty nargout: in the case no output is specified the results % are plotted) % % IMPORTANT NOTE: this code is designed for foetal ecg which means that the % constants used to assess whether a FQRS position is missing or extra are % based on known typical foetal heart rate. If you want to use this code for % adult ecg then you should change the constants that are listed in the % code under 'constants' to reflect adult ECG physiology. % % FECG extraction toolbox, version 1.0, Sept 2013 % Released under the GNU General Public License % % Copyright (C) 2013 Joachim Behar % Oxford university, Intelligent Patient Monitoring Group - Oxford 2013 % joachim.behar@eng.ox.ac.uk % % Last updated : 02-08-2013 % % 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. % == managing inputs if nargin<1; error('smooth_rr: wrong number of input arguments \n'); end; if nargin<2; rMQRS=[]; end; if nargin<3; ecg=[]; end; if nargin<4; fs=1000; end; if nargin<5; debug=0; end; % == constants WINDOW = 0.020*fs; % window in which looking for local max/min in case of missing QRS MIN_FRR = 0.35*fs; % minimal foetal RR interval corresponds to 171.4bpm MAX_FRR = 0.5*fs; % maximal foetal RR interval corresponds to 120bpm EXTRA_BEAT_THRES = 0.7; % at what threshold do we consider the QRS to be a FP MISSED_BEAT_THRES = 1.75; % at what threshold do we consider the QRS to be a FN if ~isempty(ecg); SIGN = sign(median(ecg(FQRS))); end; % sign of the peaks MAX_NB_ITER = 5000; % this is to avoid infine loops MQRS_FQRS_TOL = 0.050; % tolerance for assuming MQRS and MQRS overlaps (in sec) % == core function try FQRSc = FQRS; qrsnb = 2; compt = 0; while qrsnb6 med = median(diff(FQRSc(qrsnb-5:qrsnb))); else med = median(diff(FQRSc(qrsnb:qrsnb+5))); end % check that the med computed makes sense (i.e. should be between % 350 and 500ms i.e. 120 and 172bpm) otherwise do nothing % because prediction might be rubbish if med>MIN_FRR && medMISSED_BEAT_THRES*med && dTminus>0.7*med % == case 2: missed beat if ~isempty(ecg) % if we have context then look for a local max/min if SIGN>0 [~,mind] = max(ecg(FQRSc(qrsnb)+med-WINDOW:FQRSc(qrsnb)+med+WINDOW)); else [~,mind] = min(ecg(FQRSc(qrsnb)+med-WINDOW:FQRSc(qrsnb)+med+WINDOW)); end MissedFQRS = (mind-WINDOW-1)+FQRSc(qrsnb)+med; else % otherwise guess location if dTplus<3*med && dTplus>1.5*med % if the dTplus makes sense then better to guess in % the middle of current and next peak MissedFQRS = round(FQRSc(qrsnb)+(dTplus)/2); else % otherwise use the med to predict (less precise) MissedFQRS = round(FQRSc(qrsnb)+med); end end % insert missed FQRS where it belongs FQRSc = [FQRSc(1:qrsnb) MissedFQRS FQRSc(qrsnb+1:end)]; qrsnb = qrsnb+1; else % == case 3: normal detection qrsnb = qrsnb+1; end else qrsnb = qrsnb+1; end compt = compt+1; end % == managing MQRS/FQRS overlap if ~isempty(rMQRS) FQRStemp = FQRSc; [~,d] = dsearchn(rMQRS',FQRStemp'); ind2cor = find(d