def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ data = arr.interp_undefined(inp['pleth']['vals']) srate = inp['pleth']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: return [{}, {}, {}, {}, {}, [], []] minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # estimates the upper ue(n) and lower le(n) envelopes xa = np.array([data[idx] for idx in minlist]) le = np.array([0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in minlist]) s = sum(be) if s != 0: le[i] = np.dot(xa, be) / s xb = np.array([data[idx] for idx in maxlist]) ue = np.array([0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in maxlist]) s = sum(be) if s != 0: ue[i] = np.dot(xb, be) / s re = ue - le re[re < 0] = 0 # estimates resp rate rr = arr.estimate_resp_rate(re, srate) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist pps = [] for i in range(m - 1): imax = arr.max_idx(re, i * nsamp_in_breath, (i+2) * nsamp_in_breath) # 50% overlapping imin = arr.min_idx(re, i * nsamp_in_breath, (i+2) * nsamp_in_breath) ppmax = re[imax] ppmin = re[imin] ppe = 2 * (ppmax - ppmin) / (ppmax + ppmin) * 100 # estimate if ppe > 50 or ppe < 0: continue pp = cfg['pp'] if pp == 0: pp = ppe err = abs(ppe - pp) if err < 1: pp = ppe elif err < 25: pp = (pp + ppe) / 2 else: pass # dont update cfg['pp'] = pp pps.append({'dt': (i * nsamp_in_breath) / srate, 'val': pp}) return [ [{'dt': cfg['interval'], 'val': rr}], pps ]
def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ global last_ppv data = arr.interp_undefined(inp['pleth']['vals']) srate = inp['pleth']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: print('hr < 30') return # beat detection minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # beat lengths beatlens = [] beats_128 = [] beats_128_valid = [] for i in range(0, len(minlist) - 1): beatlen = minlist[i + 1] - minlist[i] # in samps if not 30 < beatlen < 300: beats_128.append(None) continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure if not 20 < pp < 100: beats_128.append(None) continue beatlens.append(beatlen) beat = data[minlist[i]:minlist[i + 1]] resampled = arr.resample(beat, 128) beats_128.append(resampled) beats_128_valid.append(resampled) if not beats_128_valid: return avgbeat = np.array(beats_128_valid).mean(axis=0) meanlen = np.mean(beatlens) stdlen = np.std(beatlens) if stdlen > meanlen * 0.2: # irregular rhythm return # remove beats with correlation < 0.9 pulse_vals = [] for i in range(0, len(minlist) - 1): if not beats_128[i]: continue if np.corrcoef(avgbeat, beats_128[i])[0, 1] < 0.9: continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure pulse_vals.append({'dt': minlist[i] / srate, 'val': pp}) # estimates the upper env(n) and lower env(n) envelopes xa = np.array([data[idx] for idx in minlist]) lower_env = np.array([0.0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in minlist]) s = sum(be) if s != 0: lower_env[i] = np.dot(xa, be) / s xb = np.array([data[idx] for idx in maxlist]) upper_env = np.array([0.0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in maxlist]) s = sum(be) if s != 0: upper_env[i] = np.dot(xb, be) / s pulse_env = upper_env - lower_env pulse_env[pulse_env < 0.0] = 0.0 # estimates resp rate rr = arr.estimate_resp_rate(pulse_env, srate) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist raw_pps = [] pps = [] for ibreath in np.arange(0, m - 1, 0.5): pps_breath = [] for ppe in pulse_vals: if ibreath * nsamp_in_breath < ppe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: pps_breath.append(ppe['val']) if len(pps_breath) < 4: continue pp_min = min(pps_breath) pp_max = max(pps_breath) ppv = 2 * (pp_max - pp_min) / (pp_max + pp_min) * 100 # estimate if not 0 < ppv < 50: continue # raw_pps.append({'dt': (ibreath * nsamp_in_breath) / srate, 'val': pp}) # # kalman filter if last_ppv == 0: # first time last_ppv = ppv elif abs(last_ppv - ppv) <= 1.0: ppv = last_ppv elif abs(last_ppv - ppv) <= 25.0: # ppv cannot be changed abruptly ppv = (ppv + last_ppv) * 0.5 last_ppv = ppv else: continue # no update pps.append({ 'dt': ((ibreath + 1) * nsamp_in_breath) / srate, 'val': int(ppv) }) return [pps, pulse_vals, [{'dt': cfg['interval'], 'val': rr}]]
def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ global last_ppv, last_spv data = arr.interp_undefined(inp['ART']['vals']) srate = inp['ART']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: print('hr < 30') return # beat detection minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # beat lengths beatlens = [] beats_128 = [] beats_128_valid = [] for i in range(0, len(minlist) - 1): beatlen = minlist[i + 1] - minlist[i] # in samps if not 30 < beatlen < 300: beats_128.append(None) continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure if not 20 < pp < 100: beats_128.append(None) continue beatlens.append(beatlen) beat = data[minlist[i]:minlist[i + 1]] resampled = arr.resample(beat, 128) beats_128.append(resampled) beats_128_valid.append(resampled) if not beats_128_valid: return avgbeat = np.array(beats_128_valid).mean(axis=0) meanlen = np.mean(beatlens) stdlen = np.std(beatlens) if stdlen > meanlen * 0.2: # irregular rhythm return # remove beats with correlation < 0.9 pp_vals = [] sp_vals = [] for i in range(0, len(minlist) - 1): if beats_128[i] is None or not len(beats_128[i]): continue if np.corrcoef(avgbeat, beats_128[i])[0, 1] < 0.9: continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure sp = data[maxlist[i]] pp_vals.append({'dt': minlist[i] / srate, 'val': pp}) sp_vals.append({'dt': minlist[i] / srate, 'val': sp}) dtstart = time.time() # estimates resp rate # upper env idx_start = max(min(minlist), min(maxlist)) idx_end = min(max(minlist), max(maxlist)) xa = scipy.interpolate.CubicSpline( maxlist, [data[idx] for idx in maxlist])(np.arange(idx_start, idx_end)) # lower env xb = scipy.interpolate.CubicSpline( minlist, [data[idx] for idx in minlist])(np.arange(idx_start, idx_end)) rr = arr.estimate_resp_rate(xa - xb, srate) dtend = time.time() #print('rr {}'.format(rr)) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist raw_pps = [] raw_sps = [] ppvs = [] spvs = [] for ibreath in np.arange(0, m - 1, 0.5): pps_breath = [] sps_breath = [] for ppe in pp_vals: if ibreath * nsamp_in_breath < ppe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: pps_breath.append(ppe['val']) for spe in sp_vals: if ibreath * nsamp_in_breath < spe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: sps_breath.append(spe['val']) if len(pps_breath) < 4: continue if len(sps_breath) < 4: continue pp_min = min(pps_breath) pp_max = max(pps_breath) sp_min = min(sps_breath) sp_max = max(sps_breath) ppv = (pp_max - pp_min) / (pp_max + pp_min) * 200 if not 0 < ppv < 50: continue spv = (sp_max - sp_min) / (sp_max + sp_min) * 200 if not 0 < spv < 50: continue # kalman filter if last_ppv == 0: # first time last_ppv = ppv elif abs(last_ppv - ppv) <= 1.0: ppv = last_ppv elif abs(last_ppv - ppv) <= 25.0: # ppv cannot be changed abruptly ppv = (ppv + last_ppv) * 0.5 last_ppv = ppv else: continue if last_spv == 0: # first time last_spv = spv elif abs(last_spv - spv) <= 1.0: spv = last_spv elif abs(last_spv - spv) <= 25.0: # ppv cannot be changed abruptly spv = (spv + last_spv) * 0.5 last_spv = spv else: continue ppvs.append(ppv) spvs.append(spv) median_ppv = np.median(ppvs) median_spv = np.median(spvs) return [[{ 'dt': cfg['interval'], 'val': median_ppv }], [{ 'dt': cfg['interval'], 'val': median_spv }], [{ 'dt': cfg['interval'], 'val': rr }]]