def calc_metrics(rrs): buf_duration_scnds = 300 tacho_buf_dur = 300 min_rr = 0.3 max_rr = 1.7 sp_skip = 10 buf_size = int(max(buf_duration_scnds, tacho_buf_dur) / min_rr) + 1 buf = Buffer(buf_size) print "[+] Calculating all usefull metrics: " si = SI(buf, buf_duration_scnds, min_rr, max_rr) sis = np.zeros(len(rrs)) # stress indices shs = np.zeros( (len(si.get_histogram()), len(rrs) / sp_skip + 1)) # stress histograms si_ready = 0 sp = RRTachogrammEnergySpectrum(buf, tacho_buf_dur, min_rr, max_rr) sp_ready = 0 sps = np.zeros((len(sp.get_spectrum()), len(rrs) / sp_skip + 1)) ics = np.zeros(len(rrs)) iscas = np.zeros(len(rrs)) vbs = np.zeros(len(rrs)) hrvs = np.zeros(len(rrs)) cnt = -1 ls = len(rrs) md = ls / 10 for r in rrs: if cnt % md == 0: print "[+] Done {0:.2f}%".format(float(100 * cnt) / ls) cnt += 1 si.update(r) sp.update(r) buf.add_sample(r) # ## Calculating stress indices si.calc_si() if si.is_ready(): si_ready += 1 sis[cnt] = si.get_stress() if cnt % sp_skip == 0: shs[:, cnt / sp_skip] = si.get_histogram() # ## Calculating RR-Tachogram spectrums if sp.is_ready(): sp.calc_energy_spectrum() if cnt % sp_skip == 0: sps[:, cnt / sp_skip] = sp.get_spectrum() ics[cnt] = sp.get_IC() iscas[cnt] = sp.get_ISCA() vbs[cnt] = sp.get_vegetative_balance() sp_ready += 1 # ## Fulfil heart rate buffer print "[+] Calculation finnished: SI ready " + "{0:.2f}%".format( float(si_ready * 100) / cnt) + " SP ready: {0:.2f}%".format( float(sp_ready * 100) / cnt) return sis, shs, sps, ics, iscas, vbs
class RRTachogrammEnergySpectrum: def __init__(self, buf, buf_duration_scnds=300, min_rr=0.3, max_rr=1.71): self.time_span = TimeSpanBuffer( buf, buf_duration_scnds) # remembers last 300 sec buf_size = int(buf_duration_scnds / min_rr) + 1 # energy spectrum self.cum_times_buf = Buffer(buf_size) self.time_step = 1.0 / 67 #min_rr self.spectr_intervals_cnt = int( buf_duration_scnds / self.time_step) # assume 1 heart beat per second self.freqs = np.fft.fftfreq(self.spectr_intervals_cnt, self.time_step) idx = np.argsort(self.freqs) self.idx = [i for i in idx if 0.0 <= self.freqs[i] < 0.5] self.last_spectrum = np.zeros(len(self.idx), dtype=float) self._wnd = np.hamming(self.spectr_intervals_cnt) self.may_calc = 1.0 - max_rr / buf_duration_scnds # when 90% of RR times collected in buffer - may start calculations def _get_section_params(self, ps): return np.sum(ps), np.max(ps) def get_total_power(self): return self._get_section_params(self.last_spectrum) def get_hf_power(self): ids = [i for i in self.idx if 0.15 <= self.freqs[i] < 0.4] return self._get_section_params(self.last_spectrum[ids]) def get_lf_power(self): ids = [i for i in self.idx if 0.04 <= self.freqs[i] < 0.15] return self._get_section_params(self.last_spectrum[ids]) def get_vlf_power(self): ids = [i for i in self.idx if 0.015 <= self.freqs[i] < 0.04] return self._get_section_params(self.last_spectrum[ids]) def get_ulf_power(self): ids = [i for i in self.idx if 0.0 <= self.freqs[i] < 0.015] return self._get_section_params(self.last_spectrum[ids]) def get_IC(self): """ Index of Centralization -IC (Index of centralization, IC = (VLF +LF / HF) IC reflects a degree of prevalence of non-respiratory sinus arrhythmia over the respiratory one. Actually - this is quantitative characteristic of ratio between central and independent contours of heart rhythm regulation. """ vlf = self.get_vlf_power()[0] lf = self.get_lf_power()[0] hf = self.get_hf_power()[0] if hf > 0 and self.is_vlf_ready(): return vlf + float(lf) / hf return 0 def get_ISCA(self): """ index of activation of sub cortical nervous centers ISCA (Index of Subcortical Centers Activity, ISCA = VLF / LF). ISCA characterizes activity of cardiovascular subcortical nervous center in relation to higher levels of management. The increased activity of sub cortical nervous centers is played by growth of ISCA. With help of this index processes the brain inhibiting effect can be supervised. """ vlf = self.get_vlf_power()[0] lf = self.get_lf_power()[0] if lf > 0 and self.is_vlf_ready(): return vlf / float(lf) return 0 def get_vegetative_balance(self): """HF/LF is interpreted as a parameter of vegetative balance""" hf = self.get_hf_power()[0] lf = self.get_lf_power()[0] if lf > 0 and self.is_lf_ready(): return hf / float(lf) return 0 def get_freq(self): return self.freqs[self.idx] def get_spectrum(self): return self.last_spectrum def calc_energy_spectrum(self): #http://stackoverflow.com/questions/15382076/plotting-power-spectrum-in-python # get unidistant sampled times use_N_last_RRs = self.time_span.get_N() unitimes = np.linspace(self.cum_times_buf.samples[-use_N_last_RRs], self.cum_times_buf.samples[-1], self.spectr_intervals_cnt) # interpolate rr_ms_sqr uni_rr_ms_sqr = np.interp( unitimes, self.cum_times_buf.samples[-use_N_last_RRs:], #1.0/np.array(self.time_span.get_samples())) # energy levels over heart frequency np.array(self.time_span.get_samples() )) # 1.0/energy levels over 1.0/heart_frequency uni_rr_ms_sqr -= np.mean(uni_rr_ms_sqr) # calculate spectrum ps = np.abs(np.fft.fft(uni_rr_ms_sqr * self._wnd))**2 #plt.plot(freqs[self.idx], ps[self.idx]) #s = np.sum(ps[self.idx]) #if s < self.zero_eps: # s = 1.0 self.last_spectrum = ps[self.idx] #/ s #return self.last_spectrum def is_hf_ready(self): return self.time_span.buf_real_duration > 14. def is_lf_ready(self): return self.time_span.buf_real_duration > 50. def is_vlf_ready(self): return self.time_span.buf_real_duration > 132.0 def is_ulf_ready(self): """ Different buffer duration allows to perform different calculations ULF Less than 0,015 More than 66 secs """ return self.time_span.buf_real_duration > 300. # standard time def is_ready(self): return self.is_vlf_ready() #return self.get_progress() >= self.may_calc def get_progress(self): return self.time_span.get_progress() def update(self, rr): self.time_span.update_buf_duration(rr) if len(self.cum_times_buf.samples) > 0: self.cum_times_buf.add_sample(self.cum_times_buf.samples[-1] + rr) else: self.cum_times_buf.add_sample(rr)