From 955de67f84aadde90f8e4b1ef2e8b97785bc77ec Mon Sep 17 00:00:00 2001 From: bparks13 Date: Mon, 18 Nov 2024 17:36:58 -0500 Subject: [PATCH] - Remove unused methods, remove unnecessary virtual identifiers - Ensure that stimulus timings are correctly saved and applied automatically --- .../Rhs2116StimulusSequenceDialog.cs | 79 ++++++++++--------- OpenEphys.Onix1/Rhs2116Stimulus.cs | 70 +--------------- OpenEphys.Onix1/Rhs2116StimulusSequence.cs | 2 +- .../Rhs2116StimulusSequencePair.cs | 34 +++++++- 4 files changed, 76 insertions(+), 109 deletions(-) diff --git a/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.cs b/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.cs index 3a37448..ad5705c 100644 --- a/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.cs +++ b/OpenEphys.Onix1.Design/Rhs2116StimulusSequenceDialog.cs @@ -12,10 +12,18 @@ namespace OpenEphys.Onix1.Design /// public partial class Rhs2116StimulusSequenceDialog : Form { + /// + /// Double defining the length of a single time sample, in milliseconds. + /// + const double SamplePeriodMilliSeconds = 1e3 / Rhs2116.SampleFrequencyHz; + internal Rhs2116StimulusSequencePair Sequence { get; set; } private readonly Rhs2116StimulusSequencePair SequenceCopy = new(); + private readonly double[] RequestedAnodicAmplitudeuA; + private readonly double[] RequestedCathodicAmplitudeuA; + /// /// Holds the step size that is displayed in the text box of the GUI. This is not the step size that is saved for the stimulus sequence object. /// @@ -37,18 +45,13 @@ public Rhs2116StimulusSequenceDialog(Rhs2116StimulusSequencePair sequence, Rhs21 Shown += FormShown; Sequence = new Rhs2116StimulusSequencePair(sequence); + RequestedAnodicAmplitudeuA = new double[Sequence.Stimuli.Length]; + RequestedCathodicAmplitudeuA = new double[Sequence.Stimuli.Length]; - foreach (var stimulus in Sequence.Stimuli) + for (int i = 0; i < Sequence.Stimuli.Length; i++) { - if (stimulus.RequestedAnodicAmplitudeuA == 0.0 && stimulus.AnodicAmplitudeSteps > 0) - { - stimulus.RequestedAnodicAmplitudeuA = GetAmplitudeFromSample(stimulus.AnodicAmplitudeSteps, Sequence.CurrentStepSize); - } - - if (stimulus.RequestedCathodicAmplitudeuA == 0.0 && stimulus.CathodicAmplitudeSteps > 0) - { - stimulus.RequestedCathodicAmplitudeuA = GetAmplitudeFromSample(stimulus.CathodicAmplitudeSteps, Sequence.CurrentStepSize); - } + RequestedAnodicAmplitudeuA[i] = Sequence.Stimuli[i].AnodicAmplitudeSteps * Sequence.CurrentStepSizeuA; + RequestedCathodicAmplitudeuA[i] = Sequence.Stimuli[i].CathodicAmplitudeSteps * Sequence.CurrentStepSizeuA; } StepSize = Sequence.CurrentStepSize; @@ -382,31 +385,31 @@ private PointPairList CreateStimulusWaveform(Rhs2116Stimulus stimulus, double yO PointPairList points = new() { { 0, yOffset }, - { stimulus.DelaySamples * Rhs2116Stimulus.SamplePeriodMilliSeconds, yOffset } + { stimulus.DelaySamples * SamplePeriodMilliSeconds, yOffset } }; for (int i = 0; i < stimulus.NumberOfStimuli; i++) { double amplitude = (stimulus.AnodicFirst ? stimulus.AnodicAmplitudeSteps : -stimulus.CathodicAmplitudeSteps) * Sequence.CurrentStepSizeuA / peakToPeak + yOffset; - double width = (stimulus.AnodicFirst ? stimulus.AnodicWidthSamples : stimulus.CathodicWidthSamples) * Rhs2116Stimulus.SamplePeriodMilliSeconds; + double width = (stimulus.AnodicFirst ? stimulus.AnodicWidthSamples : stimulus.CathodicWidthSamples) * SamplePeriodMilliSeconds; points.Add(points[points.Count - 1].X, amplitude); points.Add(points[points.Count - 1].X + width, amplitude); points.Add(points[points.Count - 1].X, yOffset); - points.Add(points[points.Count - 1].X + stimulus.DwellSamples * Rhs2116Stimulus.SamplePeriodMilliSeconds, yOffset); + points.Add(points[points.Count - 1].X + stimulus.DwellSamples * SamplePeriodMilliSeconds, yOffset); amplitude = (stimulus.AnodicFirst ? -stimulus.CathodicAmplitudeSteps : stimulus.AnodicAmplitudeSteps) * Sequence.CurrentStepSizeuA / peakToPeak + yOffset; - width = (stimulus.AnodicFirst ? stimulus.CathodicWidthSamples : stimulus.AnodicWidthSamples) * Rhs2116Stimulus.SamplePeriodMilliSeconds; + width = (stimulus.AnodicFirst ? stimulus.CathodicWidthSamples : stimulus.AnodicWidthSamples) * SamplePeriodMilliSeconds; points.Add(points[points.Count - 1].X, amplitude); points.Add(points[points.Count - 1].X + width, amplitude); points.Add(points[points.Count - 1].X, yOffset); - points.Add(points[points.Count - 1].X + stimulus.InterStimulusIntervalSamples * Rhs2116Stimulus.SamplePeriodMilliSeconds, yOffset); + points.Add(points[points.Count - 1].X + stimulus.InterStimulusIntervalSamples * SamplePeriodMilliSeconds, yOffset); } - points.Add(Sequence.SequenceLengthSamples * Rhs2116Stimulus.SamplePeriodMilliSeconds, yOffset); + points.Add(Sequence.SequenceLengthSamples * SamplePeriodMilliSeconds, yOffset); return points; } @@ -642,20 +645,20 @@ private void ButtonAddPulses_Click(object sender, EventArgs e) .Where(s => s.Stimulus.Valid && (s.Stimulus.AnodicAmplitudeSteps != 0 || s.Stimulus.CathodicAmplitudeSteps != 0 - || (s.Stimulus.AnodicAmplitudeSteps == 0 && s.Stimulus.RequestedAnodicAmplitudeuA != 0.0) - || (s.Stimulus.CathodicAmplitudeSteps == 0 && s.Stimulus.RequestedCathodicAmplitudeuA != 0.0)) + || (s.Stimulus.AnodicAmplitudeSteps == 0 && RequestedAnodicAmplitudeuA[s.Index] != 0.0) + || (s.Stimulus.CathodicAmplitudeSteps == 0 && RequestedCathodicAmplitudeuA[s.Index] != 0.0)) && !ChannelDialog.SelectedContacts[s.Index]) .Select(s => { - GetSampleFromAmplitude(s.Stimulus.RequestedAnodicAmplitudeuA, out var requestedAnodicSteps); + GetSampleFromAmplitude(RequestedAnodicAmplitudeuA[s.Index], out var requestedAnodicSteps); var requestedAnodicError = s.Stimulus.AnodicAmplitudeSteps == 0 ? GetAmplitudeFromSample(requestedAnodicSteps, StepSize) - : CalculateAmplitudePercentError(s.Stimulus.RequestedAnodicAmplitudeuA, StepSize); + : CalculateAmplitudePercentError(RequestedAnodicAmplitudeuA[s.Index], StepSize); - GetSampleFromAmplitude(s.Stimulus.RequestedCathodicAmplitudeuA, out var requestedCathodicSteps); + GetSampleFromAmplitude(RequestedCathodicAmplitudeuA[s.Index], out var requestedCathodicSteps); var requestedCathodicError = s.Stimulus.CathodicAmplitudeSteps == 0 ? GetAmplitudeFromSample(requestedCathodicSteps, StepSize) - : CalculateAmplitudePercentError(s.Stimulus.RequestedCathodicAmplitudeuA, StepSize); + : CalculateAmplitudePercentError(RequestedCathodicAmplitudeuA[s.Index], StepSize); return (s.Index, ErrorAnodic: requestedAnodicError, @@ -702,15 +705,15 @@ private void ButtonAddPulses_Click(object sender, EventArgs e) { if (Sequence.Stimuli[Index].AnodicAmplitudeSteps != 0 && Sequence.Stimuli[Index].CathodicAmplitudeSteps != 0) { - SequenceCopy.Stimuli[Index].CopyStimulus(Sequence.Stimuli[Index]); // NB: Store the current pulse pattern before clearing - Sequence.Stimuli[Index].Clear(clearRequestedAmplitudes: false); + SequenceCopy.UpdateStimulus(Sequence.Stimuli[Index], Index); // NB: Store the current pulse pattern before clearing + Sequence.Stimuli[Index].Clear(); } } else { if (Sequence.Stimuli[Index].NumberOfStimuli == 0 && SequenceCopy.Stimuli[Index].IsValid() && SequenceCopy.Stimuli[Index].NumberOfStimuli != 0) { - Sequence.Stimuli[Index].CopyStimulus(SequenceCopy.Stimuli[Index]); // NB: Restore pulse timings before adding amplitude steps + Sequence.UpdateStimulus(SequenceCopy.Stimuli[Index], Index); // NB: Restore pulse timings before adding amplitude steps } else if (Sequence.Stimuli[Index].NumberOfStimuli == 0 && SequenceCopy.Stimuli[Index].NumberOfStimuli != 0) continue; @@ -719,6 +722,8 @@ private void ButtonAddPulses_Click(object sender, EventArgs e) } } + dataGridViewStimulusTable.DataSource = Sequence.Stimuli; // NB: Force an update in case pulse timings were restored + for (int i = 0; i < ChannelDialog.SelectedContacts.Length; i++) { if (ChannelDialog.SelectedContacts[i]) @@ -730,7 +735,7 @@ private void ButtonAddPulses_Click(object sender, EventArgs e) if (textboxAmplitudeAnodicRequested.Tag != null) { - Sequence.Stimuli[i].RequestedAnodicAmplitudeuA = (double)textboxAmplitudeAnodicRequested.Tag; + RequestedAnodicAmplitudeuA[i] = (double)textboxAmplitudeAnodicRequested.Tag; } if (textboxAmplitudeAnodic.Tag != null) @@ -750,7 +755,7 @@ private void ButtonAddPulses_Click(object sender, EventArgs e) if (textboxAmplitudeCathodicRequested.Tag != null) { - Sequence.Stimuli[i].RequestedCathodicAmplitudeuA = (double)textboxAmplitudeCathodicRequested.Tag; + RequestedCathodicAmplitudeuA[i] = (double)textboxAmplitudeCathodicRequested.Tag; } if (textboxAmplitudeCathodic.Tag != null) @@ -920,7 +925,7 @@ private void Samples_TextChanged(object sender, EventArgs e) private bool GetSampleFromTime(double value, out uint samples) { - var ratio = value / Rhs2116Stimulus.SamplePeriodMilliSeconds; + var ratio = value / SamplePeriodMilliSeconds; samples = (uint)Math.Round(ratio); return !(ratio > uint.MaxValue || ratio < uint.MinValue); @@ -957,7 +962,7 @@ private bool GetSampleFromAmplitude(double value, out byte samples) private double GetTimeFromSample(uint value) { - return value * Rhs2116Stimulus.SamplePeriodMilliSeconds; + return value * SamplePeriodMilliSeconds; } private double GetAmplitudeFromSample(byte value, Rhs2116StepSize stepSize) @@ -1149,7 +1154,9 @@ private void ButtonClearPulses_Click(object sender, EventArgs e) { if (ChannelDialog.SelectedContacts[i]) { - Sequence.Stimuli[i].Clear(clearRequestedAmplitudes: true); + Sequence.Stimuli[i].Clear(); + RequestedAnodicAmplitudeuA[i] = 0.0; + RequestedCathodicAmplitudeuA[i] = 0.0; } } @@ -1195,10 +1202,10 @@ private void ButtonReadPulses_Click(object sender, EventArgs e) textboxAmplitudeAnodic.Text = GetAmplitudeString(Sequence.Stimuli[index].AnodicAmplitudeSteps); textboxAmplitudeAnodic.Tag = Sequence.Stimuli[index].AnodicAmplitudeSteps; - if (Sequence.Stimuli[index].RequestedAnodicAmplitudeuA != 0.0) + if (RequestedAnodicAmplitudeuA[index] != 0.0) { - textboxAmplitudeAnodicRequested.Text = Sequence.Stimuli[index].RequestedAnodicAmplitudeuA.ToString(); - textboxAmplitudeAnodicRequested.Tag = Sequence.Stimuli[index].RequestedAnodicAmplitudeuA; + textboxAmplitudeAnodicRequested.Text = RequestedAnodicAmplitudeuA[index].ToString(); + textboxAmplitudeAnodicRequested.Tag = RequestedAnodicAmplitudeuA[index]; } else { @@ -1212,10 +1219,10 @@ private void ButtonReadPulses_Click(object sender, EventArgs e) textboxAmplitudeCathodic.Text = GetAmplitudeString(Sequence.Stimuli[index].CathodicAmplitudeSteps); textboxAmplitudeCathodic.Tag = Sequence.Stimuli[index].CathodicAmplitudeSteps; - if (Sequence.Stimuli[index].RequestedCathodicAmplitudeuA != 0.0) + if (RequestedCathodicAmplitudeuA[index] != 0.0) { - textboxAmplitudeCathodicRequested.Text = Sequence.Stimuli[index].RequestedCathodicAmplitudeuA.ToString(); - textboxAmplitudeCathodicRequested.Tag = Sequence.Stimuli[index].RequestedCathodicAmplitudeuA; + textboxAmplitudeCathodicRequested.Text = RequestedCathodicAmplitudeuA[index].ToString(); + textboxAmplitudeCathodicRequested.Tag = RequestedCathodicAmplitudeuA[index]; } else { diff --git a/OpenEphys.Onix1/Rhs2116Stimulus.cs b/OpenEphys.Onix1/Rhs2116Stimulus.cs index 6961db4..b5cbcda 100644 --- a/OpenEphys.Onix1/Rhs2116Stimulus.cs +++ b/OpenEphys.Onix1/Rhs2116Stimulus.cs @@ -9,11 +9,6 @@ namespace OpenEphys.Onix1 /// public class Rhs2116Stimulus { - /// - /// Double defining the length of a single time sample, in milliseconds. - /// - public static readonly double SamplePeriodMilliSeconds = 1e3 / 30.1932367151e3; - /// /// Initializes a new instance of the class. /// @@ -36,63 +31,6 @@ public Rhs2116Stimulus(Rhs2116Stimulus stimulus) InterStimulusIntervalSamples = stimulus.InterStimulusIntervalSamples; } - /// - /// Construct a new instance with the same parameters as the given object. - /// - /// Positive integer defining the number of stimuli. - /// Boolean defining if the anodic pulse is first. - /// Positive integer defining the delay before the first pulse is sent, in samples. See for the conversion to milliseconds. - /// Positive integer defining the delay between the first and second pulse (inter-pulse interval) in samples. See for the conversion to milliseconds. - /// Positive integer defining the width of the anodic pulse in samples. See for the conversion to milliseconds. - /// Byte value defining the amplitude of the anodic pulse in steps. Amplitude in microamps is dependent on the chosen. - /// Positive integer defining the width of the cathodic pulse in samples. See for the conversion to milliseconds. - /// Byte value defining the amplitude of the cathodic pulse in steps. Amplitude in microamps is dependent on the chosen. - /// Positive integer defining the delay between successive stimuli (inter-stimulus interval) in samples. See for the conversion to milliseconds. - public Rhs2116Stimulus(uint numberOfStimuli, bool anodicFirst, uint delaySamples, uint dwellSamples, - uint anodicWidthSamples, byte anodicAmplitudeSteps, uint cathodicWidthSamples, byte cathodicAmplitudeSteps, uint interStimIntervalSamples) - { - NumberOfStimuli = numberOfStimuli; - AnodicFirst = anodicFirst; - DelaySamples = delaySamples; - DwellSamples = dwellSamples; - AnodicAmplitudeSteps = anodicAmplitudeSteps; - AnodicWidthSamples = anodicWidthSamples; - CathodicAmplitudeSteps = cathodicAmplitudeSteps; - CathodicWidthSamples = cathodicWidthSamples; - InterStimulusIntervalSamples = interStimIntervalSamples; - } - - internal void CopyStimulus(Rhs2116Stimulus stimulus) - { - NumberOfStimuli = stimulus.NumberOfStimuli; - AnodicFirst = stimulus.AnodicFirst; - DelaySamples = stimulus.DelaySamples; - DwellSamples = stimulus.DwellSamples; - AnodicAmplitudeSteps = stimulus.AnodicAmplitudeSteps; - AnodicWidthSamples = stimulus.AnodicWidthSamples; - CathodicAmplitudeSteps = stimulus.CathodicAmplitudeSteps; - CathodicWidthSamples = stimulus.CathodicWidthSamples; - InterStimulusIntervalSamples = stimulus.InterStimulusIntervalSamples; - } - - /// - /// Requested amplitude of the anodic pulse in microamps - /// - [DisplayName("Requested Anodic Amplitude (µA)")] - [XmlIgnore] - [Browsable(false)] - [Externalizable(false)] - public double RequestedAnodicAmplitudeuA { get; set; } = 0.0; - - /// - /// Requested amplitude of the cathodic pulse in microamps - /// - [DisplayName("Requested Cathodic Amplitude (µA)")] - [XmlIgnore] - [Browsable(false)] - [Externalizable(false)] - public double RequestedCathodicAmplitudeuA { get; set; } = 0.0; - /// /// Number of stimuli delivered for each trigger. /// @@ -212,7 +150,7 @@ public bool IsValid(out string reasonInvalid) /// /// Resets all properties to their default values. /// - public void Clear(bool clearRequestedAmplitudes = false) + public void Clear() { NumberOfStimuli = 0; AnodicFirst = true; @@ -223,12 +161,6 @@ public void Clear(bool clearRequestedAmplitudes = false) CathodicAmplitudeSteps = 0; CathodicWidthSamples = 0; InterStimulusIntervalSamples = 0; - - if (clearRequestedAmplitudes) - { - RequestedAnodicAmplitudeuA = 0; - RequestedCathodicAmplitudeuA = 0; - } } [XmlIgnore] diff --git a/OpenEphys.Onix1/Rhs2116StimulusSequence.cs b/OpenEphys.Onix1/Rhs2116StimulusSequence.cs index 085884c..1258dc0 100644 --- a/OpenEphys.Onix1/Rhs2116StimulusSequence.cs +++ b/OpenEphys.Onix1/Rhs2116StimulusSequence.cs @@ -44,7 +44,7 @@ public Rhs2116StimulusSequence(Rhs2116StimulusSequence sequence) /// /// Gets or sets the array of sequences. /// - public virtual Rhs2116Stimulus[] Stimuli { get; set; } + public Rhs2116Stimulus[] Stimuli { get; set; } /// /// Gets or sets the . diff --git a/OpenEphys.Onix1/Rhs2116StimulusSequencePair.cs b/OpenEphys.Onix1/Rhs2116StimulusSequencePair.cs index 11cea7d..05bbb0f 100644 --- a/OpenEphys.Onix1/Rhs2116StimulusSequencePair.cs +++ b/OpenEphys.Onix1/Rhs2116StimulusSequencePair.cs @@ -12,8 +12,8 @@ namespace OpenEphys.Onix1 /// public class Rhs2116StimulusSequencePair { - internal virtual Rhs2116StimulusSequence StimulusSequenceA { get; } - internal virtual Rhs2116StimulusSequence StimulusSequenceB { get; } + internal Rhs2116StimulusSequence StimulusSequenceA { get; } + internal Rhs2116StimulusSequence StimulusSequenceB { get; } /// /// Initializes a new instance of the class with 16 default @@ -51,7 +51,7 @@ public Rhs2116StimulusSequencePair(Rhs2116StimulusSequence stimulusSequenceA, Rh /// /// Gets or sets the array of stimuli. /// - public virtual Rhs2116Stimulus[] Stimuli + public Rhs2116Stimulus[] Stimuli { get { return StimulusSequenceA.Stimuli.Concat(StimulusSequenceB.Stimuli).ToArray(); } set @@ -61,6 +61,34 @@ public virtual Rhs2116Stimulus[] Stimuli } } + /// + /// Updates the stimulus at the given index. + /// + /// + /// This is necessary to change the values of individual stimuli, since the implementation for getting does not + /// allow for the underlying to be updated. + /// + /// Current to copy. + /// Zero-indexed value of the channel to update. + /// Index must be between 0 and the sum of the number of elements in + /// and . + internal void UpdateStimulus(Rhs2116Stimulus stimulus, int index) + { + if (index >= Stimuli.Length || index < 0) + { + throw new IndexOutOfRangeException("Index is outside of the range of stimuli. Must be less than " + Stimuli.Length.ToString() + ", and greater than zero."); + } + + if (index < StimulusSequenceA.Stimuli.Length) + { + StimulusSequenceA.Stimuli[index] = stimulus.Clone(); + } + else + { + StimulusSequenceB.Stimuli[index - StimulusSequenceA.Stimuli.Length] = stimulus.Clone(); + } + } + /// /// Gets or sets the . ///