def _vflut_gconst(pattern, _): """ General constraints of the pattern, checked every time a new observation is added to the evidence. These constraints simply state that the majority of the leads must show a positive detection of a ventricular flutter. """ if not pattern.evidence[o.Cardiac_Rhythm]: return hyp = pattern.hypothesis ################## beg = int(hyp.earlystart) if beg < 0: beg = 0 end = int(hyp.earlyend) verify(not _contains_qrs(pattern), 'QRS detected during flutter') lpos = 0. ltot = 0. for lead in sig_buf.get_available_leads(): if _is_VF(sig_buf.get_signal_fragment(beg, end, lead=lead)[0]): lpos += 1 ltot += 1 verify(lpos/ltot > 0.5) defls = pattern.evidence[o.Deflection] if len(defls) > 1: rrs = np.diff([defl.earlystart for defl in defls]) hyp.meas = o.CycleMeasurements((np.mean(rrs), np.std(rrs)), (0, 0), (0, 0))
def _rhythm_obs_proc(pattern): """Observation procedure executed once the rhythm pattern has finished""" #Tachycardias should have at least 4 QRS complexes. if isinstance(pattern.hypothesis, o.Tachycardia): verify(len(pattern.evidence[o.QRS]) >= 4) #We asign the endpoint of the hypothesis. pattern.hypothesis.end.value = pattern.evidence[o.QRS][-1].time.value
def _prev_rhythm_gconst(pattern, rhythm): """General constraints of a cardiac rhythm with the preceden one.""" #We only accept the concatenation of the same rhythm for asystoles. verify(not isinstance(rhythm, o.Asystole) and type(pattern.hypothesis) != type(rhythm)) #A block keeps the reference measures. pattern.hypothesis.meas = copy.copy(rhythm.meas)
def _prev_afib_exists_gconst(pattern, _): """ Verification of the existence of a previous atrial fibrillation episode. """ prhythms = pattern.evidence[o.Cardiac_Rhythm] verify(prhythms and isinstance(prhythms[0], o.Atrial_Fibrillation)) pattern.hypothesis.morph = copy.deepcopy(prhythms[0].morph)
def _prev_asyst_gconst(pattern, asyst): """ Verification of the existence of a previous asystole episode. """ verify(isinstance(asyst, o.Asystole)) #A block keeps the reference measures. pattern.hypothesis.meas = copy.copy(asyst.meas)
def _prev_rhythm_gconst(pattern, rhythm): """General constraints of a cardiac rhythm with the preceden one.""" #We only accept the concatenation of the same rhythm for asystoles. verify( isinstance(pattern.hypothesis, o.Asystole) or type(pattern.hypothesis) != type(rhythm)) #The rhythm measurements are initially taken from the previous rhythm. pattern.hypothesis.meas = o.CycleMeasurements( rhythm.meas.rr, (rhythm.meas.rt[0], C.QT_ERR_STD), (rhythm.meas.pq[0], C.QT_ERR_STD))
def _verify_atrial_activity(pattern): """ Checks if the atrial activity is consistent with the definition of atrial fibrillation (that is, absence of constant P Waves or flutter-like baseline activity.) """ beats = pattern.evidence[o.QRS][-5:] obseq = pattern.obs_seq atr_sig = {lead: [] for lead in sig_buf.get_available_leads()} pw_lims = [] idx = pattern.get_step(beats[0]) #First we get all the signal fragments between ventricular observations, #which are the only recognized by this pattern. In these fragments is where #atrial activity may be recognized. for i in xrange(idx + 1, len(obseq)): if isinstance(obseq[i], o.QRS): beg = next(obs for obs in reversed(obseq[:i]) if obs is not None).lateend end = obseq[i].earlystart if end - beg > ms2sp(200): beg = end - ms2sp(200) pw_lims.append((beg, end)) for i in xrange(len(beats) - 1): beg, end = beats[i].lateend, beats[i + 1].earlystart for lead in atr_sig: atr_sig[lead].append( sig_buf.get_signal_fragment(beg, end, lead=lead)[0] - characterize_baseline(lead, beg, end)[0]) #Flutter check (only for atrial activity) aflut = set() for lead in atr_sig: sigfr = np.concatenate(atr_sig[lead]) if len(sigfr) > 15 and _is_VF(sigfr): aflut.add(lead) #FIXME improve flutter check, now is quite poor. #aflut = frozenset() #P waveform check (only for leads where flutters were not found.) pwaves = [] for beg, end in pw_lims: pwsig = _get_pwave_sig(beg, end) if pwsig is not None: for lead in aflut: pwsig.pop(lead, None) if not pwsig: continue for wave in pwaves: verify( abs(wave.values()[0].pr - pwsig.values()[0].pr) > C.TMARGIN or not signal_match(wave, pwsig)) pwaves.append(pwsig)
def _qrs_gconst(pattern, _): """ General constraints to be added when a new cycle is observed, which currently coincides with the observation of the T waves or a QRS complex not followed by an observed T wave. """ #We check that there are no missed beat forms. _check_missed_beats(pattern) beats = pattern.evidence[o.QRS] #Morphology check. We require the rhythm morphology to be matched #by the new beat in the sequence. ref = pattern.hypothesis.morph #We initialize the morphology with the first beat. if not ref: ref = copy.deepcopy(beats[0].shape) pattern.hypothesis.morph = ref verify(signal_match(ref, beats[-1].shape)) #This comparison avoids positive matchings with extrasystoles, #but we only check it if the beat before the first block is advanced. if len(beats) == 3: refrr, stdrr = pattern.hypothesis.meas.rr if (beats[1].time.start - beats[0].time.start < min(0.9 * refrr, refrr-stdrr)): verify(beats[2].time.start - beats[0].time.start > refrr * C.COMPAUSE_MAX_F) verify(beats[2].time.start - beats[1].time.start > refrr + C.COMPAUSE_MIN_DUR) #We require a significant change in consecutive RR intervals. if len(beats) >= 3: rr = beats[-1].time.start - beats[-2].time.start prevrr = beats[-2].time.start - beats[-3].time.start verify(abs(prevrr-rr) >= C.RR_MAX_DIFF)
def _ect0_gconst(pattern, qrs): """ General constraints to be satisfied by the first ectopic QRS complex of the bigeminy, which is the abduction point of the pattern. These constraints ensure that the ectopic beat is really advanced. This is needed because the first ectopic beat is the abduction point, and therefore the reference RR cannot be calculated when establishing the temporal constraints for the first time. """ if pattern.evidence[o.Cardiac_Rhythm]: mrr, stdrr = pattern.evidence[o.Cardiac_Rhythm][0].meas[0] if mrr > 0: ectrr = qrs.time.start - pattern.evidence[o.QRS][0].time.start mshort = min(0.1 * mrr, stdrr) verify(ectrr < mrr - mshort)
def _couplet_gconst(pattern, _): """ General constraints to be checked when the couplet finishes. """ _check_missed_beats(pattern) #The second extrasystole cannot be in rhythm with contextual beats, or in #such case it must have a different shape. beats = pattern.evidence[o.QRS] mpt = beats[0].time.start + (beats[-1].time.start - beats[0].time.start) / 2. verify( abs(mpt - beats[2].time.start) >= C.ICOUPLET_RCHANGE or signal_unmatch(beats[2].shape, beats[-1].shape)) pattern.hypothesis.meas = copy.copy( pattern.evidence[o.Cardiac_Rhythm][0].meas)
def _p_gconst(pattern, defl): """ General constraints of the P Wave abstraction pattern, once all the evidence has been observed. """ pwave = pattern.hypothesis if ((defl is not None and defl.earlystart != defl.latestart) or not pattern.evidence[o.QRS]): return qrs = pattern.evidence[o.QRS][0] beg = pwave.earlystart if beg < 0: beg = 0 #We try the delineation of the P-Wave endpoints = delineate_pwave(beg, int(pwave.latestart), int(pwave.earlyend), int(pwave.lateend), pwave) verify(endpoints is not None) #Now we obtain the amplitudes, and we ensure the QRS amplitude is at #least twice the P Wave amplitude in each lead pwave.start.set(beg + endpoints.start, beg + endpoints.start) pwave.end.set(beg + endpoints.end, beg + endpoints.end) for lead in pwave.amplitude: verify(pwave.amplitude[lead] <= C.PWAVE_AMP[lead]) verify(lead not in qrs.shape or pwave.amplitude[lead] < qrs.shape[lead].amplitude)
def _verify_afib_rhythm(rrs): """ Checks the rhythm constraints of the atrial fibrillation patterns. If these constraints are not satisfied, an InconsistentStateError is raised. Otherwise, the procedure finishes and returns nothing. Parameters ---------- rrs: RR sequence. """ if len(rrs) > 3: verify(np.std(rrs)/np.median(rrs) > 0.08) if len(rrs) < C.AFIB_MIN_NQRS-1: #We ensure that there are not consecutive constant RRs. We usually #consider the last 3 RRs, but if we have only two, they cannot be very #similar. lastrr = rrs[-3:] if len(lastrr) == 2: verify(abs(lastrr[1]-lastrr[0]) > C.TMARGIN) else: verify(not np.all(np.abs(lastrr-np.mean(lastrr)) < C.TMARGIN)) #Lian 2011 afib classification method. verify(is_afib_rhythm_lian(rrs))
def _qrs_gconst(pattern, _): """ General constraints to be added when a new cycle is observed, which currently coincides with the observation of the T waves or a QRS complex not followed by an observed T wave. """ #We update the measurements of the rhythm. _update_measures(pattern) #And check that there are no missed beat forms. _check_missed_beats(pattern) beats = pattern.evidence[o.QRS] #Morphology check. We require the rhythm morphology to be matched #by the new beat in the sequence. ref = pattern.hypothesis.morph #We initialize the morphology with the first beat. if not ref: ref = copy.deepcopy(beats[0].shape) pattern.hypothesis.morph = ref verify(signal_match(ref, beats[-1].shape)) #TODO improve morphology updating. #_update_morphology(pattern) envrhythms = pattern.evidence[o.Cardiac_Rhythm] prevafib = envrhythms and isinstance(envrhythms[0], o.Atrial_Fibrillation) if len(beats) == 2 and envrhythms: refrr, stdrr = envrhythms[-1].meas.rr #The first RR cannot be within the mean +- 2*std of the previous RR. #There must be a rhythm change. verify(not refrr - 2*stdrr <= beats[1].time.start - beats[0].time.start <= refrr + 2*stdrr) if len(beats) >= 3: verify(not beats[-1].paced) rpks = np.array([b.time.start for b in beats]) rrs = np.diff(rpks) _verify_afib_rhythm(rrs) #With this check, we avoid false positives with bigeminies, checking #the RR constraints with even and odd rrs. if len(beats) >= 6: _verify_afib_rhythm(rrs[0::2]) _verify_afib_rhythm(rrs[1::2]) _verify_afib_rhythm(np.diff(rpks[0::2])) #Atrial activity is only checked at the beginning of the pattern and if #there are not previous atrial fibrillation episodes. if 1 < len(beats) < 32 and not prevafib: _verify_atrial_activity(pattern)
def _prev_afib_gconst(pattern, afib): """ Verification of the existence of a previous atrial fibrillation episode. """ verify(isinstance(afib, o.Atrial_Fibrillation)) pattern.hypothesis.morph = copy.deepcopy(afib.morph)
def _delimit_t(signal, baseline, ls_lim, ee_lim, qrs_shape): """ This function performs the delineation of a possible T Wave present in the fragment. To obtain the endpoint of the T Wave, it uses a method based on the work by Zhang: 'An algorithm for robust and efficient location of T-wave ends in electrocardiograms'. To get the beginning, it uses a probabilistic approach with some basic morphology constraints. All the processing is made to a simplification of the signal fragment with at most 7 points. """ try: #We exclude the areas in which the slope of the signal exceeds limit. maxtslope = qrs_shape.maxslope * C.TQRS_MAX_DIFFR lidx, uidx = 0, len(signal) if ls_lim > 0: idx = np.where( np.max(np.abs(np.diff(signal[:ls_lim + 1]))) > maxtslope)[0] + 1 lidx = max(idx) if len(idx) > 0 else 0 if ee_lim < len(signal) - 1: idx = np.where( np.max(np.abs(np.diff(signal[ee_lim:]))) > maxtslope )[0] + ee_lim uidx = min(idx) if len(idx) > 0 else len(signal) - 1 if (uidx > 1 and abs(signal[uidx] - baseline) > C.TWEND_BASELINE_MAX_DIFF): dfsign = np.sign(np.diff(signal[:uidx + 1])) signchange = ((np.roll(dfsign, 1) - dfsign) != 0).astype(int) if np.any(signchange): uidx = np.where(signchange)[0][-1] verify(uidx >= lidx) signal = signal[lidx:uidx + 1] ls_lim -= lidx ee_lim -= lidx #Any T waveform should be representable with at most 7 points. points = DP.arrayRDP(signal, max(ph2dg(0.02), qrs_shape.amplitude / 20.0), 7) n = len(points) verify(n >= 3) #1. Endpoint estimation epts = points[points >= ee_lim] verify(len(epts) > 0) Tend, dum = _zhang_tendpoint(signal, epts) #2. Onset point estimation. bpts = points[np.logical_and(points < Tend, points <= ls_lim)] score = {} #Range to normalize differences in the signal values rang = max(baseline, signal.max()) - min(signal.min(), baseline) #There must be between one and 3 peaks in the T Wave. for i in xrange(len(bpts)): sigpt = signal[points[i:np.where(points == Tend)[0][0] + 1]] npks = len(get_peaks(sigpt)) if len(sigpt) >= 3 else 0 if (npks < 1 or npks > 2 or np.ptp(sigpt) <= ph2dg(0.05)): continue bl_dist = 1.0 - np.abs(signal[bpts[i]] - baseline) / rang tdur = sp2ms(Tend - bpts[i]) score[bpts[i]] = bl_dist * _check_histogram(_TDUR_HIST, tdur) verify(score) Tbeg = max(score, key=score.get) verify(score[Tbeg] > 0) verify(np.max(np.abs(np.diff(signal[Tbeg:Tend + 1]))) <= maxtslope) return (Iv(Tbeg + lidx, Tend + lidx), dum) except InconsistencyError: return None
def _check_missed_beats(pattern): """ Checks if a rhythm pattern has missed a QRS complex in the identification, by looking for a waveform "identical" to the last observed in the interval between the last two observations. """ qrs = pattern.evidence[o.QRS][-1] obseq = pattern.obs_seq idx = obseq.index(qrs) if idx > 0: prevobs = next( (obs for obs in reversed(obseq[:idx]) if obs is not None), None) if prevobs is not None: if isinstance(prevobs, o.QRS): limit = max(prevobs.lateend, prevobs.earlystart + qrs.lateend - qrs.earlystart) else: limit = prevobs.lateend else: limit = pattern.hypothesis.earlystart ulimit = qrs.earlystart - C.TACHY_RR.start if limit >= ulimit: return sig = {} #We take the signal fragment with maximum correlation with the QRS #signal in each lead, and we check if the two fragments can be #clustered as equal QRS complexes. qshape = {} corr = -np.Inf delay = 0 leads = sig_buf.get_available_leads() for lead in leads: qshape[lead] = o.QRSShape() sigfr = sig_buf.get_signal_fragment(qrs.earlystart, qrs.lateend + 1, lead=lead)[0] qshape[lead].sig = sigfr - sigfr[0] qshape[lead].amplitude = np.ptp(qshape[lead].sig) sig[lead] = sig_buf.get_signal_fragment(limit, ulimit, lead=lead)[0] lcorr, ldelay = xcorr_valid(sig[lead], qshape[lead].sig) if lcorr > corr: corr, delay = lcorr, ldelay if 0 <= delay < len(sig[lead]): sshape = {} for lead in leads: sshape[lead] = o.QRSShape() sshape[lead].sig = ( sig[lead][delay:delay + len(qshape[lead].sig)] - sig[lead][delay]) sshape[lead].amplitude = np.ptp(sshape[lead].sig) if isinstance(pattern.hypothesis, o.RegularCardiacRhythm): qref = pattern.evidence[o.QRS][-2] rr = float(qrs.earlystart - qref.earlystart) loc = (limit + delay - qref.earlystart) / rr #Check for one and two missed beats in regular positions if 0.45 <= loc <= 0.55: verify(signal_unmatch(sshape, qshape), 'Missed beat') elif 0.28 <= loc <= 0.38 and not signal_unmatch( sshape, qshape): corr = -np.Inf delay = 0 for lead in leads: sig[lead] = sig_buf.get_signal_fragment( int(qref.earlystart + 0.61 * rr), min( int(qref.earlystart + 0.71 * rr) + len(qshape[lead].sig), int(qrs.earlystart)), lead=lead)[0] lcorr, ldelay = xcorr_valid(sig[lead], qshape[lead].sig) if lcorr > corr: corr, delay = lcorr, ldelay sshape = {} for lead in leads: sshape[lead] = o.QRSShape() sshape[lead].sig = ( sig[lead][delay:delay + len(qshape[lead].sig)] - sig[lead][delay]) sshape[lead].amplitude = np.ptp(sshape[lead].sig) verify(signal_unmatch(sshape, qshape), 'Two missed beats') elif 0.61 <= loc <= 0.71 and not signal_unmatch( sshape, qshape): corr = -np.Inf delay = 0 for lead in leads: sig[lead] = sig_buf.get_signal_fragment( int(qref.earlystart + 0.28 * rr), min( int(qref.earlystart + 0.38 * rr) + len(qshape[lead].sig), int(qrs.earlystart)), lead=lead)[0] lcorr, ldelay = xcorr_valid(sig[lead], qshape[lead].sig) if lcorr > corr: corr, delay = lcorr, ldelay sshape = {} for lead in leads: sshape[lead] = o.QRSShape() sshape[lead].sig = ( sig[lead][delay:delay + len(qshape[lead].sig)] - sig[lead][delay]) sshape[lead].amplitude = np.ptp(sshape[lead].sig) verify(signal_unmatch(sshape, qshape), 'Two missed beats') else: verify(signal_unmatch(sshape, qshape), 'Missed beat')
def _cycle_finished_gconst(pattern, _): """ General constraints to be added when a new cycle is observed, which currently coincides with the observation of the T waves or a QRS complex not followed by an observed T wave. """ #We update the measurements and the morphology of the rhythm. _update_measures(pattern) _update_morphology(pattern) #And check that there are no missed beat forms. _check_missed_beats(pattern) beats = pattern.evidence[o.QRS] rrs = np.diff([b.time.start for b in beats[-32:]]) #HINT with this check, we avoid overlapping between sinus rhythms and #tachycardias and bradycardias at the beginning of the pattern. if len(beats) == 3: if pattern.automata is SINUS_PATTERN: verify(np.any(rrs < C.BRADY_RR.start)) elif pattern.automata is BRADYCARDIA_PATTERN: if (pattern.evidence[o.Cardiac_Rhythm] and isinstance( pattern.evidence[o.Cardiac_Rhythm][0], o.Sinus_Rhythm)): verify(any([rr not in C.SINUS_RR for rr in rrs])) elif len(beats) == 4: if pattern.automata is SINUS_PATTERN: verify(np.any(rrs > C.TACHY_RR.end)) elif pattern.automata is TACHYCARDIA_PATTERN: if (pattern.evidence[o.Cardiac_Rhythm] and isinstance( pattern.evidence[o.Cardiac_Rhythm][0], o.Sinus_Rhythm)): verify(any([rr not in C.SINUS_RR for rr in rrs])) #We impose some constraints in the evolution of the RR interval and #of the amplitude #TODO remove these lines to enable full check ###################################################################### if len(beats) >= 3: #The coefficient of variation within a regular rhythm has to be low verify(np.std(rrs) / np.mean(rrs) <= C.RR_MAX_CV) #RR evolution meanrr, stdrr = pattern.hypothesis.meas.rr verify(meanrr - 2 * stdrr <= rrs[-1] <= meanrr + 2 * stdrr or abs(rrs[-1] - rrs[-2]) <= C.RR_MAX_DIFF or 0.8 * rrs[-2] <= rrs[-1] <= 1.2 * rrs[-2]) return ####################################################################### #Morphology check. We require the rhythm morphology to be matched #by the new beat in the sequence. ref = pattern.hypothesis.morph #We initialize the morphology with the first beat. if not ref: for lead in beats[0].shape: ref[lead] = o.QRSShape() ref[lead].tag = beats[0].shape[lead].tag ref[lead].energy = beats[0].shape[lead].energy beat = beats[-1] #The leads matching morphology should sum more energy than the #unmatching. menerg = 0.0 uenerg = 0.0 perfect_match = False for lead in beat.shape: if lead in ref: bshape = beat.shape[lead] #If there is a "perfect" match in one lead, we accept clustering if (bshape.tag == ref[lead].tag and 0.75 <= bshape.energy / ref[lead].energy <= 1.25): perfect_match = True break #If there are at least 10 beats in the sequence, we require #match from the beat to the rhythm, else we are ok in both #directions. match = bshape.tag in QRS_SHAPES[ref[lead].tag] if len(beats) < 10: match = bool(match or ref[lead].tag in QRS_SHAPES[bshape.tag]) if match: menerg += ref[lead].energy else: uenerg += ref[lead].energy #If the matched energy is lower than unmatched, the hypothesis is #refuted. verify(perfect_match or menerg > uenerg) _update_morphology(pattern) if len(beats) >= 3: #RR evolution rr_prev = beats[-2].time.start - beats[-3].time.start rr_act = beats[-1].time.start - beats[-2].time.start verify(abs(rr_act - rr_prev) <= C.RR_MAX_DIFF)
def _pair_gconst(pattern, _): """ General constraints to be satisfied when a regular rhythm consists of only two beats. """ if pattern.evidence[o.Cardiac_Rhythm]: _check_missed_beats(pattern) prhythm = pattern.evidence[o.Cardiac_Rhythm][0] rhythm = pattern.hypothesis #Previous rhythm cannot be a regular rhythm. verify(not isinstance(prhythm, o.RegularCardiacRhythm)) mrr, stdrr = prhythm.meas.rr beats = pattern.evidence[o.QRS] rr = beats[-1].time.start - beats[0].time.start verify(rr in rr_bounds) #Avoid duplicate hypotheses with overlapping rhythms. if pattern.automata is SINUS_PATTERN: verify(C.TACHY_RR.end < rr < C.BRADY_RR.start) maxvar = max(C.TMARGIN, min(C.RR_MAX_DIFF, 2.5 * stdrr)) verify(rr in Iv(mrr - maxvar, mrr + maxvar)) #Besides being in rhythm, the two beats must share the morphology. verify(signal_match(beats[0].shape, beats[1].shape)) #The amplitude difference is also constrained for lead in beats[0].shape: if lead in beats[1].shape: samp, qamp = (beats[0].shape[lead].amplitude, beats[1].shape[lead].amplitude) verify( min(samp, qamp) / max(samp, qamp) >= C.MISSED_QRS_MAX_DIFF) rhythm.meas = o.CycleMeasurements( (rr, stdrr), (prhythm.meas.rt[0], C.QT_ERR_STD), (prhythm.meas.pq[0], C.QT_ERR_STD))
def _t_gconst(pattern, defl): """ T Wave abstraction pattern general constraints, checked when all the evidence has been observed. """ twave = pattern.hypothesis if defl.earlystart != defl.latestart or not pattern.evidence[o.QRS]: return qrs = pattern.evidence[o.QRS][0] #Wave limits beg = int(twave.earlystart) end = int(twave.lateend) ls_lim = int(twave.latestart - beg) ee_lim = int(twave.earlyend - beg) #Start and end estimation. endpoints = {} for lead in sorted(qrs.shape, key=lambda l: qrs.shape[l].amplitude, reverse=True): baseline, _ = characterize_baseline(lead, beg, end) sig = sig_buf.get_signal_fragment(beg, end, lead=lead)[0] verify(len(sig) == end - beg + 1) ep = _delimit_t(sig, baseline, ls_lim, ee_lim, qrs.shape[lead]) if ep is not None: endpoints[lead] = ep verify(endpoints) limits = max(endpoints.iteritems(), key=lambda ep: ep[1][1])[1][0] #We verify that in all leads the maximum slope of the T wave fragment does #not exceed the threshold. for lead in endpoints: sig = sig_buf.get_signal_fragment(beg + limits.start, beg + limits.end, lead=lead)[0] verify( np.max(np.abs(np.diff(sig))) <= qrs.shape[lead].maxslope * C.TQRS_MAX_DIFFR) #Amplitude measure if lead in endpoints: mx, mn = np.amax(sig), np.amin(sig) pol = (1.0 if max(mx - sig[0], mx - sig[-1]) >= -min(mn - sig[0], mn - sig[1]) else -1.0) twave.amplitude[lead] = pol * np.ptp(sig) twave.start.value = Iv(beg + limits.start, beg + limits.start) twave.end.value = Iv(beg + limits.end, beg + limits.end) #The duration of the T Wave must be greater than the QRS #(with a security margin) verify(twave.earlyend - twave.latestart > qrs.earlyend - qrs.latestart - C.TMARGIN) #The overlapping between the energy interval and the T Wave must be at #least the half of the duration of the energy interval. verify( Iv(twave.earlystart, twave.lateend).intersection( Iv(defl.earlystart, defl.lateend)).length >= (defl.lateend - defl.earlystart) / 2.0) #If the Deflection is a R-Deflection, we require a margin before #the end of the twave. if isinstance(defl, o.RDeflection): verify(twave.lateend - defl.time.end > C.TW_RDEF_MIN_DIST)
def _prev_rhythm_gconst(_, rhythm): """General constraints of a cardiac rhythm with the preceden one.""" #A bigeminy cannot be preceded by another bigeminy or an extrasystole. verify(not isinstance(rhythm, (o.Bigeminy, o.Extrasystole)))
def _prev_rhythm_gconst(_, rhythm): """General constraints of a cardiac rhythm with the precedent one.""" #An atrial fibrillation cannot be immediately preceded by another afib. verify(not isinstance(rhythm, o.Atrial_Fibrillation))