From cc2521e985abd107133d4b8468c349b15c75e41a Mon Sep 17 00:00:00 2001 From: bparks13 Date: Tue, 22 Oct 2024 14:04:28 -0400 Subject: [PATCH] Search through possible step sizes to find global step size - Instead of choosing the smallest step size for a new amplitude, search through all existing amplitudes and all possible step sizes to see if one fits all amplitudes simultaneously - Modified the existing error message to be more clear why the current amplitudes are incompatible with the new step size - When loading or saving files, check if the stimulus sequence is invalid, and throw an error message if it is invalid. --- Directory.Build.props | 2 +- .../Rhs2116StimulusSequenceDialog.Designer.cs | 14 +-- .../Rhs2116StimulusSequenceDialog.cs | 105 +++++++++++------- 3 files changed, 70 insertions(+), 51 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index d12448f..6266d68 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,7 +13,7 @@ LICENSE true icon.png - 0.4.0 + 0.4.1 10.0 strict diff --git a/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.Designer.cs b/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.Designer.cs index e98c78b..a73db59 100644 --- a/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.Designer.cs +++ b/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.Designer.cs @@ -192,9 +192,9 @@ private void InitializeComponent() this.groupBoxCathode.Controls.Add(this.textboxPulseWidthCathodic); this.groupBoxCathode.Controls.Add(this.textboxAmplitudeCathodic); this.groupBoxCathode.Location = new System.Drawing.Point(231, 94); - this.groupBoxCathode.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.groupBoxCathode.Margin = new System.Windows.Forms.Padding(4); this.groupBoxCathode.Name = "groupBoxCathode"; - this.groupBoxCathode.Padding = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.groupBoxCathode.Padding = new System.Windows.Forms.Padding(4); this.groupBoxCathode.Size = new System.Drawing.Size(195, 69); this.groupBoxCathode.TabIndex = 3; this.groupBoxCathode.TabStop = false; @@ -236,7 +236,6 @@ private void InitializeComponent() this.textboxAmplitudeCathodic.Name = "textboxAmplitudeCathodic"; this.textboxAmplitudeCathodic.Size = new System.Drawing.Size(55, 22); this.textboxAmplitudeCathodic.TabIndex = 5; - this.textboxAmplitudeCathodic.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.ParameterKeyPress_Amplitude); this.textboxAmplitudeCathodic.Leave += new System.EventHandler(this.Amplitude_TextChanged); // // groupBoxAnode @@ -246,9 +245,9 @@ private void InitializeComponent() this.groupBoxAnode.Controls.Add(this.textboxPulseWidthAnodic); this.groupBoxAnode.Controls.Add(this.textboxAmplitudeAnodic); this.groupBoxAnode.Location = new System.Drawing.Point(13, 94); - this.groupBoxAnode.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.groupBoxAnode.Margin = new System.Windows.Forms.Padding(4); this.groupBoxAnode.Name = "groupBoxAnode"; - this.groupBoxAnode.Padding = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.groupBoxAnode.Padding = new System.Windows.Forms.Padding(4); this.groupBoxAnode.Size = new System.Drawing.Size(195, 69); this.groupBoxAnode.TabIndex = 2; this.groupBoxAnode.TabStop = false; @@ -289,7 +288,6 @@ private void InitializeComponent() this.textboxAmplitudeAnodic.Name = "textboxAmplitudeAnodic"; this.textboxAmplitudeAnodic.Size = new System.Drawing.Size(55, 22); this.textboxAmplitudeAnodic.TabIndex = 3; - this.textboxAmplitudeAnodic.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.ParameterKeyPress_Amplitude); this.textboxAmplitudeAnodic.Leave += new System.EventHandler(this.Amplitude_TextChanged); // // buttonClearPulses @@ -501,7 +499,7 @@ private void InitializeComponent() this.tabPageTable.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.tabPageTable.Name = "tabPageTable"; this.tabPageTable.Padding = new System.Windows.Forms.Padding(3, 2, 3, 2); - this.tabPageTable.Size = new System.Drawing.Size(1083, 644); + this.tabPageTable.Size = new System.Drawing.Size(1082, 646); this.tabPageTable.TabIndex = 1; this.tabPageTable.Text = "Table"; this.tabPageTable.UseVisualStyleBackColor = true; @@ -519,7 +517,7 @@ private void InitializeComponent() this.dataGridViewStimulusTable.Name = "dataGridViewStimulusTable"; this.dataGridViewStimulusTable.RowHeadersWidth = 62; this.dataGridViewStimulusTable.RowTemplate.Height = 28; - this.dataGridViewStimulusTable.Size = new System.Drawing.Size(1077, 640); + this.dataGridViewStimulusTable.Size = new System.Drawing.Size(1076, 642); this.dataGridViewStimulusTable.TabIndex = 0; this.dataGridViewStimulusTable.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridViewStimulusTable_CellEndEdit); this.dataGridViewStimulusTable.DataBindingComplete += new System.Windows.Forms.DataGridViewBindingCompleteEventHandler(this.DataGridViewStimulusTable_DataBindingComplete); diff --git a/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.cs b/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.cs index 34de646..5ea07c4 100644 --- a/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.cs +++ b/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.cs @@ -652,11 +652,12 @@ private void ButtonAddPulses_Click(object sender, EventArgs e) return (ValidAmplitudes: validAnodicAmplitude && newAnodicSteps != 0 && validCathodicAmplitude && newCathodicSteps != 0, s.Index, + s.Stimulus, NewAnodicSteps: newAnodicSteps, NewCathodicSteps: newCathodicSteps); }); - foreach (var (ValidAmplitudes, Index, NewAnodicSteps, NewCathodicSteps) in stimuli) + foreach (var (ValidAmplitudes, Index, Stimulus, NewAnodicSteps, NewCathodicSteps) in stimuli) { if (ValidAmplitudes) { @@ -665,9 +666,9 @@ private void ButtonAddPulses_Click(object sender, EventArgs e) } else { - var result = MessageBox.Show($"To produce this new sequence, the step size needs to be {GetStepSizeStringuA(StepSize)}," + - $" but the stimulus on channel {Index} cannot be defined with this step size. " + - $"Press Ok to clear the stimulus from channel {Index}, or Cancel to stop adding this sequence.", + var result = MessageBox.Show($"The new amplitude ({GetAmplitudeString((byte)textboxAmplitudeAnodic.Tag) + " µA"}) is using a step size of {GetStepSizeStringuA(StepSize)}," + + $" but channel {Index} ({GetAmplitudeString(Stimulus.AnodicAmplitudeSteps, Sequence.CurrentStepSize) + " µA"}) cannot be defined with this step size. " + + $"Press Ok to clear channel {Index}, or Cancel to stop adding this sequence.", "Amplitude Out of Range", MessageBoxButtons.OKCancel); if (result == DialogResult.Cancel) @@ -764,14 +765,6 @@ private void ParameterKeyPress_Time(object sender, KeyPressEventArgs e) } } - private void ParameterKeyPress_Amplitude(object sender, KeyPressEventArgs e) - { - if (e.KeyChar == '\r') - { - Amplitude_TextChanged(sender, e); - } - } - private void DataGridViewStimulusTable_CellEndEdit(object sender, DataGridViewCellEventArgs e) { dataGridViewStimulusTable.BindingContext[dataGridViewStimulusTable.DataSource].EndCurrentEdit(); @@ -808,34 +801,21 @@ private string GetStepSizeStringuA(Rhs2116StepSize stepSize) return Rhs2116StimulusSequence.GetStepSizeuA(stepSize).ToString() + " µA"; } - private double GetStepSizeuA(Rhs2116StepSize stepSize) + private string GetAmplitudeString(byte amplitude) { - return stepSize switch - { - Rhs2116StepSize.Step10nA => 0.01, - Rhs2116StepSize.Step20nA => 0.02, - Rhs2116StepSize.Step50nA => 0.05, - Rhs2116StepSize.Step100nA => 0.1, - Rhs2116StepSize.Step200nA => 0.2, - Rhs2116StepSize.Step500nA => 0.5, - Rhs2116StepSize.Step1000nA => 1.0, - Rhs2116StepSize.Step2000nA => 2.0, - Rhs2116StepSize.Step5000nA => 5.0, - Rhs2116StepSize.Step10000nA => 10.0, - _ => throw new ArgumentException("Invalid stimulus step size selection."), - }; + return GetAmplitudeString(amplitude, StepSize); } - private string GetAmplitudeString(byte amplitude) + private string GetAmplitudeString(byte amplitude, Rhs2116StepSize stepSize) { - string format = StepSize switch + string format = stepSize switch { Rhs2116StepSize.Step10nA or Rhs2116StepSize.Step20nA or Rhs2116StepSize.Step50nA => "{0:F2}", Rhs2116StepSize.Step100nA or Rhs2116StepSize.Step200nA or Rhs2116StepSize.Step500nA => "{0:F1}", Rhs2116StepSize.Step1000nA or Rhs2116StepSize.Step2000nA or Rhs2116StepSize.Step5000nA or Rhs2116StepSize.Step10000nA => "{0:F0}", _ => "{0:F3}", }; - return string.Format(format, GetAmplitudeFromSample(amplitude)); + return string.Format(format, GetAmplitudeFromSample(amplitude, stepSize)); } private string GetTimeString(uint time) @@ -927,11 +907,6 @@ private double GetTimeFromSample(uint value) return value * SamplePeriodMilliSeconds; } - private double GetAmplitudeFromSample(byte value) - { - return GetAmplitudeFromSample(value, StepSize); - } - private double GetAmplitudeFromSample(byte value, Rhs2116StepSize stepSize) { return value * Rhs2116StimulusSequence.GetStepSizeuA(stepSize); @@ -1054,8 +1029,7 @@ private bool UpdateStepSizeFromAmplitude(double amplitude) .Cast() .Where(s => { - var numSteps = (int)(amplitude / GetStepSizeuA(s)); - return numSteps > 0 && numSteps <= 255; + return IsValidNumberOfSteps(GetNumberOfSteps(amplitude, s)); }); if (possibleStepSizes.Count() == 1) @@ -1070,7 +1044,33 @@ private bool UpdateStepSizeFromAmplitude(double amplitude) } else { - StepSize = possibleStepSizes.First(); + // NB: Search through the possible step sizes and try to find one that matches all current amplitudes + var validStepSizes = possibleStepSizes.Where(s => + { + var numberOfStimuli = Sequence.Stimuli.Length; + + bool[] isValid = new bool[numberOfStimuli]; + + for (int i = 0; i < numberOfStimuli; i++) + { + isValid[i] = IsValidNumberOfSteps(GetNumberOfSteps(GetAmplitudeFromSample(Sequence.Stimuli[i].AnodicAmplitudeSteps, Sequence.CurrentStepSize), s)); + } + + return isValid.All(i => i); + }); + + if (!validStepSizes.Any()) + { + MessageBox.Show("No step size found that fits all existing and new amplitudes. " + + "Either clear existing stimuli that fall outside the range of the new step size, or modify " + + "the new amplitude.", "Invalid Amplitude"); + + StepSize = possibleStepSizes.First(); + } + else + { + StepSize = validStepSizes.First(); + } } } @@ -1079,6 +1079,16 @@ private bool UpdateStepSizeFromAmplitude(double amplitude) return true; } + private bool IsValidNumberOfSteps(int numberOfSteps) + { + return numberOfSteps > 0 && numberOfSteps <= 255; + } + + private int GetNumberOfSteps(double amplitude, Rhs2116StepSize stepSize) + { + return (int)(amplitude / Rhs2116StimulusSequence.GetStepSizeuA(stepSize)); + } + private void Checkbox_CheckedChanged(object sender, EventArgs e) { if (checkboxBiphasicSymmetrical.Checked) @@ -1144,9 +1154,6 @@ private void ButtonReadPulses_Click(object sender, EventArgs e) .Select(c => c.Ind) .First(); - if (Sequence.Stimuli[index].NumberOfStimuli == 0 || !Sequence.Stimuli[index].IsValid()) - return; - if (Sequence.Stimuli[index].AnodicAmplitudeSteps == Sequence.Stimuli[index].CathodicAmplitudeSteps && Sequence.Stimuli[index].AnodicWidthSamples == Sequence.Stimuli[index].CathodicWidthSamples) { @@ -1190,6 +1197,14 @@ private void ButtonReadPulses_Click(object sender, EventArgs e) private void MenuItemSaveFile_Click(object sender, EventArgs e) { + if (!Sequence.Valid) + { + var result = MessageBox.Show("Warning: Not all stimuli are valid; are you sure you want to save this file?", + "Invalid Stimuli", MessageBoxButtons.YesNo, MessageBoxIcon.Error); + + if (result == DialogResult.No) return; + } + using SaveFileDialog sfd = new(); sfd.Filter = "Stimulus Sequence Files (*.json)|*.json"; sfd.FilterIndex = 1; @@ -1226,10 +1241,16 @@ private void MenuItemLoadFile_Click(object sender, EventArgs e) { Sequence = sequence; dataGridViewStimulusTable.DataSource = Sequence.Stimuli; + + if (!Sequence.Valid) + { + MessageBox.Show("Warning: Invalid stimuli found in the recently opened file. Check all values to ensure they are what is expected.", + "Invalid Stimuli", MessageBoxButtons.OK, MessageBoxIcon.Error); + } } else { - MessageBox.Show("Incoming sequence is not valid. Check file for validity."); + MessageBox.Show("Incoming file is not valid. Check file for validity."); } DrawStimulusWaveform();