Skip to content

Commit

Permalink
IISIM 2.5.2: Usability & Clinical Realism re: IABP
Browse files Browse the repository at this point in the history
- IABP GUI adjusted for resizing on various resolutions
  - Viewbox'es added around Textblocks to allow for auto-resizing text
    - Some various font sizes look janky, but better than elements being clipped!
- IABP balloon and IABP_ABP waveform times adjusted for adaptability
  - Gives realistic results on adapting to irregular rhythms e.g. afib
  - Maintains realism on regular rhythms without any breaking changes
  - closes #191
- IABP triggering re: physiology model & devices tweaked re: bugfixes
  - IABP triggering on non-pulsatile waveforms was broken (#192)
  - Fixed IABP balloon trigger (was on cardiac ventricular mechanical timer...)
    - Moved to cardiac ventricular electric timer
  - Fixed ABP tracing not being drawn
    - Was in Add_Beat__Cardiac_Ventricular_Mechanical ...
    - ABP tracing now drawn in Add_Beat__IABP_Balloon if non-pulsatile rhythm
  - Fixed triggering autoscaling iterations in DeviceIABP, Monitor, and Defib
  - Above changes closes #192
  • Loading branch information
tanjera committed Jun 12, 2023
1 parent c66a872 commit 2098dfd
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 143 deletions.
44 changes: 31 additions & 13 deletions II Library/Classes/Physiology.cs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,11 @@ public bool InLabor {

/* Methods for counting, calculating, and measuring vital signs, timing re: vital signs, etc. */

// Calculates R-to-R interval,
public static double CalculateHRInterval (int hr) {
return 60d / System.Math.Max (1, hr);
}

public static int CalculateMAP (int sbp, int dbp) {
return dbp + ((sbp - dbp) / 3);
}
Expand All @@ -370,14 +375,27 @@ public static int CalculateCPP (int? icp, int? map) {
return (int)(map - icp);
}

public double GetHR_Seconds { get { return 60d / System.Math.Max (1, VS_Actual.HR); } }
public double GetRR_Seconds { get { return 60d / System.Math.Max (1, VS_Actual.RR); } }
public double GetRR_Seconds_I { get { return (GetRR_Seconds / (RR_IE_I + RR_IE_E)) * RR_IE_I; } }
public double GetRR_Seconds_E { get { return (GetRR_Seconds / (RR_IE_I + RR_IE_E)) * RR_IE_E; } }
// Allows for pulling historical sets of vital signs; useful for calculating beat times after electromechanical delay has caused additional
// cardiac events to take over the newest set of vital signs!
public Vital_Signs GetLastVS (PhysiologyEventTypes pe, int amountEvents = 1) {
var peList = ListPhysiologyEvents.Where (p => p.EventType == pe).ToList ();
if (peList.Count < (2 + amountEvents))
return VS_Actual;

return peList [peList.Count - (1 + amountEvents)].Vitals;
}

public double GetHRInterval { get { return CalculateHRInterval (VS_Actual.HR); } }

// Gets Respiratory Rate interval... *not* to be confused with R-to-R interval (ECG)
public double GetRRInterval { get { return CalculateHRInterval (VS_Actual.RR); } }

public double GetRRInterval_Inspiratory { get { return (GetRRInterval / (RR_IE_I + RR_IE_E)) * RR_IE_I; } }
public double GetRRInterval_Expiratory { get { return (GetRRInterval / (RR_IE_I + RR_IE_E)) * RR_IE_E; } }

// A functional length in seconds of pulsatile rhythms; based on set heart rate (not actual!)
// for consistency in irregular rhythms
public double GetPulsatility_Seconds { get { return 60d / System.Math.Max (1, VS_Settings.HR) * 0.9d; } }
public double GetPulsatility_Seconds { get { return CalculateHRInterval (VS_Settings.HR) * 0.9d; } }

// Using Fridericia Formula for QT <-> QTc calculation
public double GetQTInterval { get { return System.Math.Pow ((60d / System.Math.Max (1, VS_Actual.HR)), (1 / 3)) * QTc_Interval; } }
Expand Down Expand Up @@ -927,7 +945,7 @@ private async Task ResetStartTimers () {
}

private async Task ResetStartTimers_Cardiac () {
await TimerCardiac_Baseline.ResetStart ((int)(GetHR_Seconds * 1000d));
await TimerCardiac_Baseline.ResetStart ((int)(GetHRInterval * 1000d));
await TimerCardiac_Atrial_Electric.Stop ();
await TimerCardiac_Ventricular_Electric.Stop ();
await TimerCardiac_Atrial_Mechanical.Stop ();
Expand All @@ -943,7 +961,7 @@ private async Task ResetStartTimers_Cardiac () {
}

private async Task ResetStartTimers_Respiratory () {
await TimerRespiratory_Baseline.ResetStart ((int)(GetRR_Seconds * 1000d));
await TimerRespiratory_Baseline.ResetStart ((int)(GetRRInterval * 1000d));
await TimerRespiratory_Inspiration.Stop ();
await TimerRespiratory_Expiration.Stop ();
}
Expand Down Expand Up @@ -1083,7 +1101,7 @@ private async Task OnPacemaker_Spike () {

private async Task OnCardiac_Baseline () {
await OnPhysiologyEvent (PhysiologyEventTypes.Cardiac_Baseline);
await TimerCardiac_Baseline.Set ((int)(GetHR_Seconds * 1000d));
await TimerCardiac_Baseline.Set ((int)(GetHRInterval * 1000d));

switch (Cardiac_Rhythm.Value) {
default:
Expand Down Expand Up @@ -1304,6 +1322,9 @@ private async Task OnCardiac_Atrial_Electric () {
private async Task OnCardiac_Ventricular_Electric () {
await OnPhysiologyEvent (PhysiologyEventTypes.Cardiac_Ventricular_Electric);

if (IABP_Active)
await TimerIABP_Balloon_Trigger.ResetStart ((int)(GetHRInterval * 1000d * 0.35d));

if (Cardiac_Rhythm.HasPulse_Ventricular)
await TimerCardiac_Ventricular_Mechanical.ResetStart (Default_Electromechanical_Delay);

Expand All @@ -1322,9 +1343,6 @@ private async Task OnCardiac_Atrial_Mechanical () {
private async Task OnCardiac_Ventricular_Mechanical () {
await OnPhysiologyEvent (PhysiologyEventTypes.Cardiac_Ventricular_Mechanical);

if (IABP_Active)
await TimerIABP_Balloon_Trigger.ResetStart ((int)(GetHR_Seconds * 1000d * 0.35d));

if (Pulsus_Alternans) {
VS_Actual.ASBP += Cardiac_Rhythm.AlternansBeat ? (int)(VS_Settings.ASBP * 0.15d) : -(int)(VS_Settings.ASBP * 0.15d);
VS_Actual.ADBP += Cardiac_Rhythm.AlternansBeat ? (int)(VS_Settings.ADBP * 0.15d) : -(int)(VS_Settings.ADBP * 0.15d);
Expand All @@ -1348,7 +1366,7 @@ private async Task OnIABP_Balloon_Inflate () {

private async Task OnRespiratory_Baseline () {
await OnPhysiologyEvent (PhysiologyEventTypes.Respiratory_Baseline);
await TimerRespiratory_Baseline.Set ((int)(GetRR_Seconds * 1000d));
await TimerRespiratory_Baseline.Set ((int)(GetRRInterval * 1000d));

double c;

Expand Down Expand Up @@ -1446,7 +1464,7 @@ private async Task OnRespiratory_Inspiration () {
case Respiratory_Rhythms.Values.Biot:
case Respiratory_Rhythms.Values.Cheyne_Stokes:
case Respiratory_Rhythms.Values.Regular:
await TimerRespiratory_Expiration.ResetStart ((int)(GetRR_Seconds_I * 1000d)); // Expiration.Interval marks end inspiration
await TimerRespiratory_Expiration.ResetStart ((int)(GetRRInterval_Inspiratory * 1000d)); // Expiration.Interval marks end inspiration
break;
}
}
Expand Down
48 changes: 30 additions & 18 deletions II Library/Classes/Rhythm.Strip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public void SetAutoScale (Physiology? _P) {
for (int i = _P.ListPhysiologyEvents.Count; i > 0 && j < DefaultAutoScale_Iterations;) {
i--;

if (_P.ListPhysiologyEvents [i].EventType != Physiology.PhysiologyEventTypes.Cardiac_Ventricular_Mechanical)
if (_P.ListPhysiologyEvents [i].EventType != Physiology.PhysiologyEventTypes.Cardiac_Ventricular_Electric)
continue;
else
j++;
Expand Down Expand Up @@ -266,10 +266,10 @@ private void SetOffset () {
private void SetForwardBuffer (Physiology patient, bool onClear = false) {
/* Set the forward edge buffer (a coefficient of lengthSeconds!) to be the length of 2 beats/breaths */
if (IsCardiac)
forwardBuffer = System.Math.Max (1 + (2 * ((double)patient.GetHR_Seconds / Length)),
forwardBuffer = System.Math.Max (1 + (2 * ((double)patient.GetHRInterval / Length)),
(onClear ? 1.1f : forwardBuffer));
else if (IsRespiratory)
forwardBuffer = System.Math.Max (1 + (2 * ((double)patient.GetRR_Seconds / Length)),
forwardBuffer = System.Math.Max (1 + (2 * ((double)patient.GetRRInterval / Length)),
(onClear ? 1.1f : forwardBuffer));
else if (IsObstetric)
forwardBuffer = System.Math.Max (1 + ((double)patient.ObstetricContractionFrequency / Length),
Expand Down Expand Up @@ -616,11 +616,11 @@ public void Add_Beat__Cardiac_Baseline (Physiology? p) {
p.Cardiac_Rhythm.ECG_Isoelectric (p, this);
} else if (CanScale) {
double fill = (Length * forwardBuffer) - Last (Points).X;
Concatenate (Scale (p, Draw.Flat_Line (fill > (double)p.GetHR_Seconds ? fill : (double)p.GetHR_Seconds, 0d)));
Concatenate (Scale (p, Draw.Flat_Line (fill > (double)p.GetHRInterval ? fill : (double)p.GetHRInterval, 0d)));
} else {
/* Fill waveform through to future buffer with flatline */
double fill = (Length * forwardBuffer) - Last (Points).X;
Concatenate (Draw.Flat_Line (fill > (double)p.GetHR_Seconds ? fill : (double)p.GetHR_Seconds, 0d));
Concatenate (Draw.Flat_Line (fill > (double)p.GetHRInterval ? fill : (double)p.GetHRInterval, 0d));
}

SortPoints ();
Expand Down Expand Up @@ -701,20 +701,32 @@ public void Add_Beat__IABP_Balloon (Physiology? p) {
if (p is null || Lead is null)
return;

if (Lead?.Value != Lead.Values.IABP || !p.IABP_Active)
return;
if (p.IABP_Active) {
switch (Lead?.Value) {
default: return;

case Lead.Values.IABP:
if (p.Cardiac_Rhythm.HasWaveform_Ventricular && p.IABP_Trigger == "ECG") {
/* ECG Trigger works only if ventricular ECG waveform */
// IABP causes important downward deflections- do not use ReplaceAtOver!
Replace (Draw.IABP_Balloon_Rhythm (p, 1d));
} else if (p.Cardiac_Rhythm.HasPulse_Ventricular && p.IABP_Trigger == "Pressure") {
/* Pressure Trigger works only if ventricular pressure impulse */
// IABP causes important downward deflections- do not use ReplaceAtOver!
Replace (Draw.IABP_Balloon_Rhythm (p, 1d));
}
break;

if (p.Cardiac_Rhythm.HasWaveform_Ventricular && p.IABP_Trigger == "ECG") {
/* ECG Trigger works only if ventricular ECG waveform */
// IABP causes important downward deflections- do not use ReplaceAtOver!
Replace (Draw.IABP_Balloon_Rhythm (p, 1d));
} else if (p.Cardiac_Rhythm.HasPulse_Ventricular && p.IABP_Trigger == "Pressure") {
/* Pressure Trigger works only if ventricular pressure impulse */
// IABP causes important downward deflections- do not use ReplaceAtOver!
Replace (Draw.IABP_Balloon_Rhythm (p, 1d));
}
case Lead.Values.ABP:
if (!p.Cardiac_Rhythm.HasPulse_Ventricular) {
// IABP causes important downward deflections- do not use ReplaceAtOver!
Replace (Scale (p, Draw.IABP_ABP_Rhythm (p, 1d)));
}
break;
}

SortPoints ();
SortPoints ();
}
}

public void Add_Beat__Cardiac_Defibrillation (Physiology? p) {
Expand Down Expand Up @@ -750,7 +762,7 @@ public void Add_Breath__Respiratory_Baseline (Physiology? p) {

/* Fill waveform through to future buffer with flatline */
double fill = (Length * forwardBuffer) - Last (Points).X;
Concatenate (Draw.Flat_Line (fill > (double)p.GetRR_Seconds ? fill : (double)p.GetRR_Seconds, 0d, Resolution_Respiratory));
Concatenate (Draw.Flat_Line (fill > (double)p.GetRRInterval ? fill : (double)p.GetRRInterval, 0d, Resolution_Respiratory));

SortPoints ();
}
Expand Down
54 changes: 27 additions & 27 deletions II Library/Classes/Rhythms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,35 +262,35 @@ public static Default_Vitals DefaultVitals (Values Rhythm) {

public void ECG_Isoelectric (Physiology p, Rhythm.Strip s) {
switch (Value) {
case Values.Asystole: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Asystole: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Atrial_Fibrillation: s.Concatenate (Draw.ECG_Isoelectric__Atrial_Fibrillation (p, s.Lead)); return;
case Values.Atrial_Flutter: s.Concatenate (Draw.ECG_Isoelectric__Atrial_Flutter (p, s.Lead)); return;
case Values.AV_Block__1st_Degree: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.AV_Block__3rd_Degree: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.AV_Block__Mobitz_II: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.AV_Block__Wenckebach: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Bundle_Branch_Block: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.CPR_Artifact: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Idioventricular: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Junctional: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Pulseless_Electrical_Activity: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sick_Sinus_Syndrome: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Arrhythmia: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Rhythm: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Rhythm_with_Arrest: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Rhythm_with_Bigeminy: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Rhythm_with_Trigeminy: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Rhythm_with_PACs: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Rhythm_with_PJCs: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Rhythm_with_PVCs_Multifocal: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Sinus_Rhythm_with_PVCs_Unifocal: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Supraventricular_Tachycardia: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Ventricular_Fibrillation_Coarse: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Ventricular_Fibrillation_Fine: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Ventricular_Standstill: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Ventricular_Tachycardia_Monomorphic_Pulsed: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Ventricular_Tachycardia_Monomorphic_Pulseless: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.Ventricular_Tachycardia_Polymorphic: s.Concatenate (Draw.Flat_Line ((float)p.GetHR_Seconds, 0f)); return;
case Values.AV_Block__1st_Degree: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.AV_Block__3rd_Degree: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.AV_Block__Mobitz_II: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.AV_Block__Wenckebach: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Bundle_Branch_Block: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.CPR_Artifact: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Idioventricular: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Junctional: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Pulseless_Electrical_Activity: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sick_Sinus_Syndrome: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Arrhythmia: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Rhythm: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Rhythm_with_Arrest: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Rhythm_with_Bigeminy: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Rhythm_with_Trigeminy: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Rhythm_with_PACs: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Rhythm_with_PJCs: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Rhythm_with_PVCs_Multifocal: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Sinus_Rhythm_with_PVCs_Unifocal: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Supraventricular_Tachycardia: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Ventricular_Fibrillation_Coarse: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Ventricular_Fibrillation_Fine: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Ventricular_Standstill: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Ventricular_Tachycardia_Monomorphic_Pulsed: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Ventricular_Tachycardia_Monomorphic_Pulseless: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
case Values.Ventricular_Tachycardia_Polymorphic: s.Concatenate (Draw.Flat_Line ((float)p.GetHRInterval, 0f)); return;
}
}

Expand Down
Loading

0 comments on commit 2098dfd

Please sign in to comment.