function ecg2hr(ihbv, sdt, waveq, stopt, ihbt, hrdt, hbwt, nswt) %ECG2HR(ihbv, sdt, waveq, stopt, ihbt, hrdt, hbwt, nswt) % % ihbv (.6): initial heart beat values; an average initial-heartbeat y-value; a filter % sdt (75): slope differential threshold; larger means more filter; a major heartbeat filter % waveq (3): wave quality of the ecg: use a higher number with a more wavy ecg % stopt (300) : stop execution after (stopt) seconds of input have passed % ihbt ([]): initial heart beat times: array of initial-heartbeat x-values, e.g., [.085 .143] % hrdt (50): output a heart rate measurement every (hrdt) milliseconds % hbwt (60): heart beat warning threshold; closer to zero means more warnings % nswt (10): noisy signal warning threshold; closer to zero means more warnings % %Examples: % ecg2hr % ecg2hr(20, 25, 18); %From here on, a number in parenthesis will denote a time dependent number %These numbers would likely change with a Sampling Frequency [fs] < 1000 %or if this ECG program were used with, say, humans rather than mice % Tests on the input arguments [ecgfilename, cdfilename, rrfilename, hrfilename, ecgfilenameonly] = chooser('useold'); if (nargin==0) ihbv = (.6); end %if if (nargin<=1) sdt = (75); end %if if (nargin<=2) waveq = (3); end %if if (nargin<=3) stopt = (300); end %if if (nargin<=4) ihbt = []; end %if if (nargin<=5) hrdt = (50); end %if if (nargin<=6) hbwt = 60; end %if if (nargin<=7) nswt = 10; end %if if (ihbv==0 | sdt==0 | waveq==0 | hrdt==0 | hbwt==0 | nswt == 0) error('Parameters ihbv, sdt, waveq, hrdt, hbwt, and nswt cannot equal 0; check your parameters.'); end %if %used to convert between seconds and milliseconds TIMEBASIS = .001; INVTIMEBASIS = 1000; %heartbeat boundaries in ms MINRINTERVAL = (66); MAXRINTERVAL = (400); %set up running averages of the given length AVGLENGTH = (200); SLOPERANGE = (2); RRAVGLENGTH = 5; %used to read and store the ecg t = (-inf); prevecg = 0; ecgread = 0; tims = 0; %used for basic calculations on the ecg avg = 0; avgdiv = 0; ema = 0; ecgmult = 1/ihbv; %used to find the fs of the ecg t0 = 0; tcount = 0; edt = 0; edtims = 0; fs = 1000; %used to analyze the slope of the ecg avgslope = 0; slopediv = 0; curslope = 0; %used to analyze the change in slope of the ecg slopediff = 0; prevecg = 0; prevt = 0; dt = (1); %used to find rr intervals recentrt = -inf; rrcount = -1; prevema = 0; ishb = 0; %used for some rr calculations and output avgrrint = 0; minrravg = inf; i = 0; rtoffset = 0; %used to analyze the rr intervals prevrr = 0; bestrt = 0; bestrr = 0; bestema = 0; pbema = 0; %used to find best hb match fdde = 0; hbmatchdiff = 0; minfdde = inf; minhbmatchdiff = inf; %used to warn user about possibly bad output prevnscount = 0; nscount = 0; wrt = 3.5; iwt = 0; pwt = 0; %file ids ecgfid = fopen(ecgfilename,'r'); cdfid = fopen(cdfilename,'w'); rrfid = fopen(rrfilename,'w'); hrfid = fopen(hrfilename,'w'); while (t < stopt & ~feof(ecgfid)) %reading and analyzing the ecg prevt = t; prevecg = ecgread; ecgline = fgetl(ecgfid); if (isempty(ecgline)) if (tcount == 0) if ~isempty(sscanf(ecgline,'%[# b-df-mo-zB-DF-MO-Z]')) error('Data from ECG input file is not valid. Please run Splice if appropriate.'); return; end %if while (isempty(ecgline)) ecgline = fscanf(ecgfid,'%f %f',[1 2]); end %while else break; end %if end %if ecgline = sscanf(ecgline,'%f %f',[1 2]); t = ecgline(1) - t0; ecgread = ecgline(2)*ecgmult; if (t <= prevt) error('ECG time column must increase continuously.'); end %if if (tcount >= 0) if (tcount == 0) t0 = t; t = 0; prevt = 0; prevecg = ecgread; else dt = t - prevt; edt = edt + (dt - edt)/tcount; end %if tcount = tcount+1; end %if tims = (round(t*fs)/fs)*INVTIMEBASIS; prevema = ema; ema = ecgread - avg; avgdiv = avgdiv + (AVGLENGTH-avgdiv)/AVGLENGTH; avg = avg + ema/avgdiv; curslope = (ecgread - prevecg)/dt; slopediff = curslope - avgslope; if (slopediv < SLOPERANGE) slopediv = slopediv + 1; end %if avgslope = avgslope + slopediff/slopediv; temprrint = tims - recentrt; %is this ecg value a heartbeat? if (size(ihbt, 2) >= 2 & rrcount <= size(ihbt, 2) - 2) ishb = sum(t == ihbt); else ishb = rrcount > 0 | abs(slopediff*prevema) > ihbv*(125); ishb = abs(slopediff) > sdt & temprrint > MINRINTERVAL & (ishb); end %if if (ishb) %a new possible heartbeat has been found if (rrcount == -1) %first heartbeat, need one more to calculate heart rate rrcount = 0; recentrt = tims; %should be tims-edtims, but we don't know edtims yet (see below) elseif (rrcount == 0) %second heartbeat, now heart rate (and edtims) is known rrcount = 1; %calculate final fs, edt fs = round(.1/edt)*10; edt = 1/fs; edtims = INVTIMEBASIS/fs; tcount = -1; %output fs fprintf(cdfid, '%5g\n', fs); fprintf(rrfid, '%5g\n', fs); fprintf(hrfid, '%5g\n', fs); %set rr variables bestrt = tims-edtims; recentrt = recentrt-edtims; %-edtims to account for above discrepancy bestrr = temprrint; %(1-1)*edtims to account for recentrt discrepancy avgrrint = bestrr; if (bestrr > MAXRINTERVAL) fclose('all'); error(['Heartbeat interval=' num2str(bestrr*TIMEBASIS) ' is too long. Try with ihbv=' ... num2str(round(ihbv*.75)) '.']); end %if %output this rr interval fprintf(rrfid, '%10g %10g\n', bestrt, bestrr); for i=(0:edtims:recentrt-edtims) fprintf(cdfid, '%11g %10g\n', i*TIMEBASIS, 0); end %for fprintf(cdfid, '%11g %10g\n', recentrt*TIMEBASIS, 1); for i=(recentrt+edtims:edtims:bestrt-edtims) fprintf(cdfid, '%11g %10g\n', i*TIMEBASIS, 0); end %for fprintf(cdfid, '%11g %10g\n', bestrt*TIMEBASIS, 1); for i=(recentrt:hrdt:bestrt) fprintf(hrfid, '%10g %10g\n', i, INVTIMEBASIS/bestrr); end %for rtoffset = mod(bestrt-recentrt+rtoffset-hrdt,hrdt); prevrr = bestrr; recentrt = bestrt; else %all other heartbeats execute these statements nscount = nscount + 1; if (rrcount <= size(ihbt, 2) - 2 | (temprrint > avgrrint * 1.5) | ... (temprrint > avgrrint * 1.4125 & nscount > 1)) % past likely RR interval boundary, so calculate heart rate if (rrcount < RRAVGLENGTH) rrcount = rrcount + 1; end %if avgrrint = avgrrint + (bestrr-avgrrint)/rrcount; if (avgrrint < minrravg) minrravg = avgrrint; elseif (minrravg == 0) elseif (avgrrint > minrravg*2.125) disp('Please check both the output as well as your data.'); disp(['Around or before t=' num2str(t) ', this program may']); disp('have skipped and continued to skip heartbeats.'); disp('Quick fix: Fudge data to make heartbeats obvious.'); minrravg = 0; end %if if (bestrr > MAXRINTERVAL) fclose('all'); error(['Heartbeat interval=' num2str(bestrr*TIMEBASIS) ... ' is too long. Adjust parameters ihbv and sdt (type ''help ecg2hr'' for more info).']); end %if if (minfdde > hbwt | (prevnscount > nswt & minfdde > hbwt/(1.1))) %list all possibly bad output points if (iwt == 0) disp('Ecg2hr generated questionable output during the following time ranges:'); iwt = floor((recentrt*TIMEBASIS-.3)*2)/2; pwt = iwt; elseif (t-pwt < wrt) pwt = ceil((recentrt*TIMEBASIS)*2+.2)/2; else if (pwt == iwt) disp(num2str(iwt)); else disp([num2str(iwt) '-' num2str(pwt)]); end %if iwt = floor((recentrt*TIMEBASIS-.3)*2)/2; pwt = iwt; end %if end %if %output rr interval fprintf(rrfid, '%10g %10g\n', bestrt, bestrr); for i=(recentrt+edtims:edtims:bestrt-edtims) fprintf(cdfid, '%11g %10g\n', i*TIMEBASIS, 0); end %for fprintf(cdfid, '%11g %10g\n', bestrt*TIMEBASIS, .5*bestema); for i=(recentrt-rtoffset+hrdt:hrdt:bestrt) fprintf(hrfid, '%10g %10g\n', i, INVTIMEBASIS/bestrr+... (i<=recentrt+hrdt)*(INVTIMEBASIS/prevrr-INVTIMEBASIS/bestrr)/2); end %for rtoffset = mod(bestrt-recentrt+rtoffset-hrdt,hrdt); prevrr = bestrr; recentrt = bestrt; %reset rr variables temprrint = tims - recentrt; pbema = bestema; bestema = 0; minhbmatchdiff = inf; prevnscount = nscount; nscount = 0; AVGLENGTH = avgrrint/waveq; end %if if (prevema~=0 & temprrint > MINRINTERVAL) %test heartbeat candidate to see if it's a good match fdde = abs((abs(INVTIMEBASIS/(temprrint-edtims)-INVTIMEBASIS/prevrr)+10)/prevema); if (prevnscount == 0) prevnscount = 1; end %if pnscdw = ceil(prevnscount/nswt); hbmatchdiff = (abs(INVTIMEBASIS/(temprrint-edtims)-INVTIMEBASIS/prevrr)+10)*... (-atan(pnscdw*nswt*abs(prevema)-.5)/pi+.5+.28/pnscdw)*... (-2*atan(prevnscount/nswt)/pi*(sign(prevema) == sign(pbema))+1); if (hbmatchdiff <= minhbmatchdiff) minfdde = fdde; minhbmatchdiff = hbmatchdiff; bestema = prevema; bestrt = tims-edtims; bestrr = temprrint-edtims; end %if end %if end %if end %if end %while if (iwt ~= 0) %output final questionable points if (pwt <= iwt) disp(num2str(iwt)); else disp([num2str(iwt) '-' num2str(pwt)]); end %if end %if disp(['Finished at t = ' num2str(t)]); fclose(ecgfid); fclose(hrfid); fclose(rrfid); fclose(cdfid);