Skip to content

Commit

Permalink
IISIM 2.6.2; IIDT 1.1.5: Systolic Time Scaling for Waveform Plot Length
Browse files Browse the repository at this point in the history
#197

IIDT: Waveform Editor & Waveform Dictionary Builder
- Implemented SystoleLength in Editor, saving to .iiwf
- Carries through pipeline to Waveform.Dictionary.Plots.cs
IISIM: Scaling waveform plot length per SystoleLength
- Revised algorithm for Physiology.GetPulsatility_Seconds()
  - Based on 1/3 cardiac cycle for normocardia, 2 * QRS for brady/tachy
    - Clamped if QRS value is unset, prevents division by zero or unintended behavior
- Needed to adjust resolution coefficient implemented in 05889b3
  - Adjusted from 1-100 x scaling to 1-10
    - Poor iso/baseline resolution was cropping into plotted waveform concatenation
      - Visualized as early part of waveform being chopped off
      - Fixed by reducing hefty de-resolution scaling
  - Still fixes the problems from #208
IISIM: QRS/QTc defaults adjusted per #188
- Fine-tuned SVT for correct default morphology
- Ensured defaults exist for all rhythms that have ventricular activity (e.g. idioventricular)
IISIM: Intra-abdominal pressure waveforms tied to respiratory cycle
- Inflects on inspiration, deflects on expiration
IISIM: Intracranial pressure waveform dampened amplitude
- Draws best when not tied to systole
  • Loading branch information
tanjera committed Jun 3, 2024
1 parent 05889b3 commit 66511cb
Show file tree
Hide file tree
Showing 32 changed files with 474 additions and 336 deletions.
2 changes: 1 addition & 1 deletion II Development Tools/Versioning.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Current Version:

1.1.4
1.1.5

Versioning for this solution is kind of silly, but it will help me keep track
of future changes and give an indexing system...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ private void ProcessFolder (object sender, DoWorkEventArgs e) {
string WaveName = "";
int DrawResolution = 0;
int IndexOffset = 0;
int SystoleLength = 0;
List<Vertex> Vertices = new List<Vertex> ();

/* Load individual .iiwf file */
Expand All @@ -106,6 +107,7 @@ private void ProcessFolder (object sender, DoWorkEventArgs e) {
case "WaveName": WaveName = pValue; break;
case "DrawResolution": DrawResolution = int.Parse (pValue); break;
case "IndexOffset": IndexOffset = int.Parse (pValue); break;
case "SystoleLength": SystoleLength = int.Parse (pValue); break;

case "Vertices":
Vertices = new List<Vertex> ();
Expand Down Expand Up @@ -135,6 +137,7 @@ private void ProcessFolder (object sender, DoWorkEventArgs e) {
dictOut.AppendLine (String.Format ("\t\tpublic static Plot {0} = new Plot () {{", WaveName));
dictOut.AppendLine (String.Format ("\t\t\tDrawResolution = {0},", DrawResolution));
dictOut.AppendLine (String.Format ("\t\t\tIndexOffset = {0},", IndexOffset));
dictOut.AppendLine (String.Format ("\t\t\tSystoleLength = {0},", SystoleLength));
dictOut.AppendLine (String.Format ("\t\t\tVertices = new double[] {{", IndexOffset));

for (int v = 0; v < Vertices.Count; v++) {
Expand Down
32 changes: 25 additions & 7 deletions II Development Tools/Waveform Editor/Editor.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Grid Grid.Row="0" Grid.Column="0">
Expand All @@ -127,22 +128,39 @@
HorizontalAlignment="Stretch" VerticalContentAlignment="Center" />
</Grid>

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
Grid.Row="0" Grid.Column="1">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Grid.Row="0"
Grid.Column="1">
<Label Content="Draw Length: " />
<xctk:DecimalUpDown Name="dblDrawLength" Value="1" Minimum="0.1" />
<Label Content=" seconds" />
<xctk:IntegerUpDown Name="intDrawLength"
Value="1000"
Minimum="100" />
<Label Content=" msec" />
</StackPanel>

<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Grid.Row="0"
Grid.Column="2">
<Label Content="Systole Length: " />
<xctk:IntegerUpDown Name="intSystoleLength"
Value="300"
Minimum="0"
ValueChanged="intSystoleLength_ValueChanged" />
<Label Content=" msec" />

</StackPanel>

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
Grid.Row="0" Grid.Column="2">
Grid.Row="0" Grid.Column="3">
<Label Content="Draw Resolution (X Axis): " />
<xctk:IntegerUpDown Name="intDrawResolution" Value="10" />
<Label Content=" milliseconds per point" />
<Label Content=" msec per point" />
</StackPanel>

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
Grid.Row="0" Grid.Column="3">
Grid.Row="0" Grid.Column="4">
<Label Content="Index (X Axis) Offset: " />
<xctk:IntegerUpDown Name="intIndexOffset" Value="0" Minimum="0" />
<Label Content=" vertices" />
Expand Down
43 changes: 27 additions & 16 deletions II Development Tools/Waveform Editor/Editor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ public partial class Editor : Window {
private List<Vertex> Vertices;

/* Waveform settings */
private int DrawResolution;
private double DrawLength;
private int DrawResolution; // In milliseconds
private int DrawLength; // In milliseconds
private int SystoleLength; // In milliseconds
private int IndexOffset;
private string WaveName;

Expand Down Expand Up @@ -76,14 +77,17 @@ private void NewFile () {
DrawResolution = 10;
intDrawResolution.Value = 10;

DrawLength = 1;
dblDrawLength.Value = 1;
DrawLength = 1000;
intDrawLength.Value = 1000;

SystoleLength = 330;
intSystoleLength.Value = 330;

IndexOffset = 0;
intIndexOffset.Value = 0;

Vertices = new List<Vertex> ();
for (int i = 0; i < ((DrawLength * 1000) / DrawResolution); i++)
for (int i = 0; i < (DrawLength / DrawResolution); i++)
Vertices.Add (new Vertex () { Y = 0 });

UpdateWave ();
Expand Down Expand Up @@ -111,6 +115,7 @@ private void SaveFile () {
sb.AppendLine (String.Format ("{0}:{1}", "WaveName", WaveName));
sb.AppendLine (String.Format ("{0}:{1}", "DrawResolution", DrawResolution));
sb.AppendLine (String.Format ("{0}:{1}", "IndexOffset", IndexOffset));
sb.AppendLine (String.Format ("{0}:{1}", "SystoleLength", SystoleLength));

StringBuilder sbVert = new StringBuilder ();
for (int i = 0; i < Vertices.Count; i++)
Expand Down Expand Up @@ -171,6 +176,7 @@ private void LoadFile () {
case "WaveName": WaveName = pValue; break;
case "DrawResolution": DrawResolution = int.Parse (pValue); break;
case "IndexOffset": IndexOffset = int.Parse (pValue); break;
case "SystoleLength": SystoleLength = int.Parse (pValue); break;

case "Vertices":
Vertices = new List<Vertex> ();
Expand Down Expand Up @@ -198,7 +204,7 @@ private void LoadFile () {
}
}
}
DrawLength = (Vertices.Count * (double)DrawResolution) / 1000;
DrawLength = Vertices.Count * DrawResolution;
FilePath = dlgLoad.FileName;
} catch {
LoadFail ();
Expand Down Expand Up @@ -253,7 +259,8 @@ private void RemoveBackground () {
private void UpdateUI () {
txtWaveName.Text = WaveName;
intDrawResolution.Value = DrawResolution;
dblDrawLength.Value = Math.Round ((decimal)DrawLength, 1);
intDrawLength.Value = DrawLength;
intSystoleLength.Value = SystoleLength;
intIndexOffset.Value = IndexOffset;
}

Expand All @@ -273,7 +280,7 @@ private void UpdateWave () {

private void CalculateDrawOffsets () {
/* +2 accounts for beginning and end margin */
drawXMultiplier = cnvDrawing.ActualWidth / (((DrawLength * 1000) / DrawResolution) + 2);
drawXMultiplier = cnvDrawing.ActualWidth / ((DrawLength / DrawResolution) + 2);
drawYMultiplier = -cnvDrawing.ActualHeight / 2;

drawXOffset = (int)drawXMultiplier;
Expand Down Expand Up @@ -366,8 +373,8 @@ private void TrimWaveStart () {
IndexOffset -= removed;
intIndexOffset.Value = IndexOffset;

DrawLength = ((double)Vertices.Count * (double)DrawResolution) / 1000;
dblDrawLength.Value = (decimal)DrawLength;
DrawLength = Vertices.Count * DrawResolution;
intDrawLength.Value = DrawLength;

UpdateWave ();
}
Expand All @@ -381,8 +388,8 @@ private void TrimWaveEnd () {
}
}

DrawLength = ((double)Vertices.Count * (double)DrawResolution) / 1000;
dblDrawLength.Value = (decimal)DrawLength;
DrawLength = Vertices.Count * DrawResolution;
intDrawLength.Value = DrawLength;
UpdateWave ();
}

Expand All @@ -393,8 +400,8 @@ private void TrimWaveOffset () {
}

intIndexOffset.Value = IndexOffset;
DrawLength = ((double)Vertices.Count * (double)DrawResolution) / 1000;
dblDrawLength.Value = (decimal)DrawLength;
DrawLength = Vertices.Count * DrawResolution;
intDrawLength.Value = DrawLength;
UpdateWave ();
}

Expand Down Expand Up @@ -542,11 +549,11 @@ private void btnApplyResolutions_Click (object sender, RoutedEventArgs e) {
txtWaveName.Text = txtWaveName.Text.Trim ().Replace (' ', '_');
WaveName = txtWaveName.Text;
DrawResolution = intDrawResolution.Value ?? 10;
DrawLength = (double)(dblDrawLength.Value ?? 1);
DrawLength = intDrawLength.Value ?? 1000;
IndexOffset = intIndexOffset.Value ?? 0;

Vertices = new List<Vertex> ();
for (int i = 0; i < ((DrawLength * 1000) / DrawResolution); i++)
for (int i = 0; i < (DrawLength / DrawResolution); i++)
Vertices.Add (new Vertex () { Y = 0 });

UpdateWave ();
Expand All @@ -558,6 +565,10 @@ private void menuFilterNormalize_Click (object sender, RoutedEventArgs e)
private void menuFilterNormalizePositive_Click (object sender, RoutedEventArgs e)
=> Filter_Normalize (0, 1);

private void intSystoleLength_ValueChanged (object sender, RoutedPropertyChangedEventArgs<object> e) {
SystoleLength = intSystoleLength.Value ?? 330;
}

private void cnvDrawing_KeyDown (object sender, KeyEventArgs e) {
if (Keyboard.IsKeyDown (Key.LeftCtrl) || Keyboard.IsKeyDown (Key.RightCtrl))
return; // Prevent keyboard shortcuts from being "handled" and not actually running!
Expand Down
31 changes: 26 additions & 5 deletions II Library/Classes/Physiology.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class Physiology {
Electrical_Alternans = false;

public Cardiac_Axes Cardiac_Axis = new ();
public double QRS_Interval, QTc_Interval;
public double QRS_Interval, QTc_Interval; // In seconds (e.g. 0.1)
public double []? ST_Elevation, T_Elevation;

/* Obstetric profile */
Expand Down Expand Up @@ -401,12 +401,33 @@ public Vital_Signs GetLastVS (PhysiologyEventTypes pe, int amountEvents = 1) {
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 CalculateHRInterval (VS_Settings.HR) * 0.9d; } }
/* A functional length in seconds of pulsatile rhythms:
- Normocardia (60-100): 1/3 of cardiac cycle (RR interval) is systole; 0.4 bridges better
- Bradycardia (<60): RR interval is elongated and would generate stretched waveforms if used
... approximate with 3.3 * QRS interval (bridges @ 60 and holds consistent downward)
- Tachycardia: (>100): RR interval is short and would cause overdrawing abnormally narrowed waveforms
... approximate with 2.7 * QRS interval (bridges @ 100 and mimics increased ino/dromotropy
... Goal coefficient should have allow for normal diastolic times @ 100 but have drastically
decreased distolic times > 180-200 to mimic this cause of poor cardiac output
*/
public double GetSystole_Seconds {
get {
if (VS_Settings.HR < 60) { // Brady
return Math.Clamp (3.3 * QRS_Interval, 0.1, 1); // Clamp to prevent GIGO
} else if (VS_Settings.HR > 100) { // Tachy
return Math.Clamp (2.7 * QRS_Interval, 0.1, 1); // Clamp to prevent GIGO
} else { // Normocardia
return CalculateHRInterval (VS_Settings.HR) * 0.4d; // Bridges w/ brady<->tachy well
}
}
}

// 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; } }
public double GetQTInterval {
get {
return System.Math.Pow ((60d / System.Math.Max (1, VS_Actual.HR)), (1 / 3)) * QTc_Interval;
}
}

public double GetSTInterval { get { return GetQTInterval - QRS_Interval; } }
public double GetSTSegment { get { return GetSTInterval * (1d / 3d); } }
Expand Down
26 changes: 16 additions & 10 deletions II Library/Classes/Rhythm.Strip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -622,22 +622,24 @@ public void Add_Beat__Cardiac_Baseline (Physiology? p) {
} else if (CanScale) {
double fill = (Length * forwardBuffer) - Last (Points).X;

// Interpolate HR Interval (.75 - 5 seconds) to a resolution coefficient of 1 - 100
// w/ default resolution of 10ms: @ HR 60 bpm, resolve @ 10ms, @ ~12 bpm, resolve @ 1000ms
// Interpolate HR Interval (.75 - 5 seconds) to a resolution coefficient of 1 - 10
// w/ default resolution of 10ms: @ HR 60 bpm, resolve @ 10ms, @ ~12 bpm, resolve @ 100ms
// Note: high resolution times (e.g. 100ms) cuts into drawing of following concatenation!
double length = fill > p.GetHRInterval ? fill : p.GetHRInterval;
double ilerpHRi = Math.Clamp(Math.InverseLerp (.75, 5, length), 0, 1);
double scaleRes = Math.Lerp(1, 100, ilerpHRi);
double scaleRes = Math.Lerp(1, 10, ilerpHRi);

Concatenate (Scale (p, Draw.Flat_Line (length, 0d, scaleRes)));
} else {
/* Fill waveform through to future buffer with flatline */
double fill = (Length * forwardBuffer) - Last (Points).X;

// Interpolate HR Interval (.75 - 5 seconds) to a resolution coefficient of 1 - 100
// Interpolate HR Interval (.75 - 5 seconds) to a resolution coefficient of 1 - 10
// w/ default resolution of 10ms: @ HR 60 bpm, resolve @ 10ms, @ ~12 bpm, resolve @ 100ms
// Note: high resolution times (e.g. 100ms) cuts into drawing of following concatenation!
double length = fill > p.GetHRInterval ? fill : p.GetHRInterval;
double ilerpHRi = Math.Clamp(Math.InverseLerp (.75, 5, length), 0, 1);
double scaleRes = Math.Lerp(1, 100, ilerpHRi);
double scaleRes = Math.Lerp(1, 10, ilerpHRi);

Concatenate (Draw.Flat_Line (length, 0d, scaleRes));
}
Expand Down Expand Up @@ -705,11 +707,7 @@ public void Add_Beat__Cardiac_Ventricular_Mechanical (Physiology? p) {
break;

case Lead.Values.ICP:
ReplaceAtOver (Draw.ICP_Rhythm (p, 1d));
break;

case Lead.Values.IAP:
ReplaceAtOver (Draw.IAP_Rhythm (p, 1d));
ReplaceAtOver (Draw.ICP_Rhythm (p, .3d));
break;
}

Expand Down Expand Up @@ -794,6 +792,10 @@ public void Add_Breath__Respiratory_Inspiration (Physiology? p) {
default: return;
case Lead.Values.RR: Replace (Draw.RR_Rhythm (p, true, Resolution_Respiratory)); break;
case Lead.Values.ETCO2: break; // End-tidal waveform is only present on expiration!! Is flatline on inspiration.

case Lead.Values.IAP:
ReplaceAtOver (Draw.IAP_Rhythm (true, p, 1d));
break;
}

SortPoints ();
Expand All @@ -807,6 +809,10 @@ public void Add_Breath__Respiratory_Expiration (Physiology? p) {
default: break;
case Lead.Values.RR: Replace (Draw.RR_Rhythm (p, false, Resolution_Respiratory)); break;
case Lead.Values.ETCO2: Replace (Draw.ETCO2_Rhythm (p)); break;

case Lead.Values.IAP:
Replace (Draw.IAP_Rhythm (false, p, -1d));
break;
}

SortPoints ();
Expand Down
Loading

0 comments on commit 66511cb

Please sign in to comment.