diff --git a/OpenEphys.Onix1.Design/GenericStimulusSequenceDialog.cs b/OpenEphys.Onix1.Design/GenericStimulusSequenceDialog.cs index 4176c064..d7232e1c 100644 --- a/OpenEphys.Onix1.Design/GenericStimulusSequenceDialog.cs +++ b/OpenEphys.Onix1.Design/GenericStimulusSequenceDialog.cs @@ -122,9 +122,9 @@ internal void OnSelect(object sender, EventArgs e) void OnZoom_Waveform(ZedGraphControl sender, ZoomState oldState, ZoomState newState) { - if (newState.Type == ZoomState.StateType.WheelZoom && sender.IsEnableHZoom && sender.IsEnableVZoom) + if (newState.Type == ZoomState.StateType.WheelZoom) { - CenterAxesOnCursor(sender); + CenterAxesOnCursor(sender, sender.IsEnableHZoom, sender.IsEnableVZoom); } DrawScale(); @@ -207,7 +207,7 @@ internal void DrawStimulusWaveform(bool setZoomState = true) zedGraphWaveform.GraphPane.YAxis.ScaleFormatEvent += (gp, axis, val, index) => { - return Math.Abs(val).ToString("0"); + return val <= 0 ? Math.Abs(val).ToString("0") : ""; }; dataGridViewStimulusTable.Refresh(); @@ -226,10 +226,10 @@ internal void DrawStimulusWaveform(bool setZoomState = true) zedGraphWaveform.Refresh(); } + internal string yAxisScaleUnits = "µA"; + internal string xAxisScaleUnits = "ms"; internal virtual double GetPeakToPeakAmplitudeInMicroAmps() => throw new NotImplementedException(); - internal string yAxisScale = "µA"; - void DrawScale() { const string scaleString = "scale"; @@ -282,13 +282,13 @@ void DrawScale() const double TextObjScaleFactor = 1.02; - TextObj timeScale = new(GetTimeScaleString(x) + " ms", zeroOffsetX + x * TextObjScaleFactor, zeroOffsetY, CoordType.AxisXYScale, AlignH.Left, AlignV.Center); + TextObj timeScale = new(GetTimeScaleString(x) + " " + xAxisScaleUnits, zeroOffsetX + x * TextObjScaleFactor, zeroOffsetY, CoordType.AxisXYScale, AlignH.Left, AlignV.Center); timeScale.FontSpec.Border.IsVisible = false; timeScale.FontSpec.Fill.IsVisible = false; timeScale.ZOrder = ZOrder.A_InFront; zedGraphWaveform.GraphPane.GraphObjList.Add(timeScale); - TextObj amplitudeScale = new(yScaleValue.ToString("0.##") + " µA", zeroOffsetX, zeroOffsetY + y * TextObjScaleFactor, CoordType.AxisXYScale, AlignH.Center, AlignV.Bottom); + TextObj amplitudeScale = new(yScaleValue.ToString("0.##") + " " + yAxisScaleUnits, zeroOffsetX, zeroOffsetY + y * TextObjScaleFactor, CoordType.AxisXYScale, AlignH.Center, AlignV.Bottom); amplitudeScale.FontSpec.Border.IsVisible = false; amplitudeScale.FontSpec.Fill.IsVisible = false; amplitudeScale.ZOrder = ZOrder.A_InFront; @@ -306,8 +306,7 @@ double GetTimeScaleString(double time) < 10 => Math.Round(time, 1), < 100 => Math.Round(time / 10, 1) * 10, < 1000 => Math.Round(time / 100, 1) * 100, - < 10000 => Math.Round(time / 1000, 1) * 1000, - _ => time + _ => Math.Round(time / 1000, 1) * 1000 }; } @@ -347,7 +346,6 @@ void InitializeZedGraphWaveform() zedGraphWaveform.GraphPane.YAxis.Scale.IsSkipLastLabel = true; zedGraphWaveform.GraphPane.YAxis.Scale.IsSkipFirstLabel = true; - zedGraphWaveform.GraphPane.XAxis.Title.Text = "Time [ms]"; zedGraphWaveform.GraphPane.YAxis.Title.Text = "Channel Number"; zedGraphWaveform.IsAutoScrollRange = true; @@ -383,25 +381,30 @@ static PointD TransformPixelsToCoordinates(Point pixels, GraphPane graphPane) return new PointD(x, y); } - void CenterAxesOnCursor(ZedGraphControl zedGraphControl) + void CenterAxesOnCursor(ZedGraphControl zedGraphControl, bool hZoomEnabled, bool vZoomEnabled) { var mouseClientPosition = PointToClient(Cursor.Position); - mouseClientPosition.X -= (zedGraphControl.Parent.Width - zedGraphControl.Width) / 2; - mouseClientPosition.Y += (zedGraphControl.Parent.Height - zedGraphControl.Height) / 2; - var currentMousePosition = TransformPixelsToCoordinates(mouseClientPosition, zedGraphControl.GraphPane); - var centerX = CalculateScaleRange(zedGraphControl.GraphPane.XAxis.Scale) / 2 + zedGraphControl.GraphPane.XAxis.Scale.Min; - var centerY = CalculateScaleRange(zedGraphControl.GraphPane.YAxis.Scale) / 2 + zedGraphControl.GraphPane.YAxis.Scale.Min; + if (hZoomEnabled) + { + mouseClientPosition.X -= (zedGraphControl.Parent.Width - zedGraphControl.Width) / 2; + var centerX = CalculateScaleRange(zedGraphControl.GraphPane.XAxis.Scale) / 2 + zedGraphControl.GraphPane.XAxis.Scale.Min; + var diffX = centerX - currentMousePosition.X; - var diffX = centerX - currentMousePosition.X; - var diffY = centerY - currentMousePosition.Y; + zedGraphControl.GraphPane.XAxis.Scale.Min += diffX; + zedGraphControl.GraphPane.XAxis.Scale.Max += diffX; + } - zedGraphControl.GraphPane.XAxis.Scale.Min += diffX; - zedGraphControl.GraphPane.XAxis.Scale.Max += diffX; + if (vZoomEnabled) + { + mouseClientPosition.Y += (zedGraphControl.Parent.Height - zedGraphControl.Height) / 2; + var centerY = CalculateScaleRange(zedGraphControl.GraphPane.YAxis.Scale) / 2 + zedGraphControl.GraphPane.YAxis.Scale.Min; + var diffY = centerY - currentMousePosition.Y; - zedGraphControl.GraphPane.YAxis.Scale.Min += diffY; - zedGraphControl.GraphPane.YAxis.Scale.Max += diffY; + zedGraphControl.GraphPane.YAxis.Scale.Min += diffY; + zedGraphControl.GraphPane.YAxis.Scale.Max += diffY; + } } internal virtual bool IsSequenceValid() diff --git a/OpenEphys.Onix1.Design/Headstage64Dialog.Designer.cs b/OpenEphys.Onix1.Design/Headstage64Dialog.Designer.cs index 1b64c413..96efd691 100644 --- a/OpenEphys.Onix1.Design/Headstage64Dialog.Designer.cs +++ b/OpenEphys.Onix1.Design/Headstage64Dialog.Designer.cs @@ -41,6 +41,7 @@ private void InitializeComponent() this.tabPageTS4231 = new System.Windows.Forms.TabPage(); this.menuStrip1 = new System.Windows.Forms.MenuStrip(); this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.tabPageHeartbeat = new System.Windows.Forms.TabPage(); this.tableLayoutPanel1.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.tabControl.SuspendLayout(); @@ -104,6 +105,7 @@ private void InitializeComponent() this.tabControl.Controls.Add(this.tabPageRhd2164); this.tabControl.Controls.Add(this.tabPageBno055); this.tabControl.Controls.Add(this.tabPageTS4231); + this.tabControl.Controls.Add(this.tabPageHeartbeat); this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl.Location = new System.Drawing.Point(3, 2); this.tabControl.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); @@ -178,6 +180,16 @@ private void InitializeComponent() this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); this.fileToolStripMenuItem.Text = "File"; // + // tabPageHeartbeat + // + this.tabPageHeartbeat.Location = new System.Drawing.Point(4, 25); + this.tabPageHeartbeat.Name = "tabPageHeartbeat"; + this.tabPageHeartbeat.Padding = new System.Windows.Forms.Padding(3); + this.tabPageHeartbeat.Size = new System.Drawing.Size(1170, 512); + this.tabPageHeartbeat.TabIndex = 6; + this.tabPageHeartbeat.Text = "Heartbeat"; + this.tabPageHeartbeat.UseVisualStyleBackColor = true; + // // Headstage64Dialog // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); @@ -214,5 +226,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; private System.Windows.Forms.TabPage tabPageElectricalStimulator; private System.Windows.Forms.TabPage tabPageOpticalStimulator; + private System.Windows.Forms.TabPage tabPageHeartbeat; } } \ No newline at end of file diff --git a/OpenEphys.Onix1.Design/Headstage64Dialog.cs b/OpenEphys.Onix1.Design/Headstage64Dialog.cs index 613d7558..9ae6e6d7 100644 --- a/OpenEphys.Onix1.Design/Headstage64Dialog.cs +++ b/OpenEphys.Onix1.Design/Headstage64Dialog.cs @@ -11,6 +11,7 @@ public partial class Headstage64Dialog : Form internal readonly GenericDeviceDialog Rhd2164Dialog; internal readonly GenericDeviceDialog Bno055Dialog; internal readonly GenericDeviceDialog TS4231V1Dialog; + internal readonly GenericDeviceDialog HeartbeatDialog; internal readonly Headstage64ElectricalStimulatorSequenceDialog ElectricalStimulatorSequenceDialog; internal readonly Headstage64OpticalStimulatorSequenceDialog OpticalStimulatorSequenceDialog; @@ -31,6 +32,9 @@ public Headstage64Dialog(ConfigureHeadstage64 configureNode) TS4231V1Dialog = new(new ConfigureTS4231V1(configureNode.TS4231)); TS4231V1Dialog.SetChildFormProperties(this).AddDialogToTab(tabPageTS4231); + HeartbeatDialog = new(new ConfigurePersistentHeartbeat(configureNode.Heartbeat)); + HeartbeatDialog.SetChildFormProperties(this).AddDialogToTab(tabPageHeartbeat); + ElectricalStimulatorSequenceDialog = new(configureNode.ElectricalStimulator); ElectricalStimulatorSequenceDialog.SetChildFormProperties(this).AddDialogToTab(tabPageElectricalStimulator); diff --git a/OpenEphys.Onix1.Design/Headstage64Editor.cs b/OpenEphys.Onix1.Design/Headstage64Editor.cs index a3cb6774..26a572d9 100644 --- a/OpenEphys.Onix1.Design/Headstage64Editor.cs +++ b/OpenEphys.Onix1.Design/Headstage64Editor.cs @@ -25,6 +25,7 @@ public override bool EditComponent(ITypeDescriptorContext context, object compon DesignHelper.CopyProperties((ConfigureRhd2164)editorDialog.Rhd2164Dialog.Device, configureNode.Rhd2164, DesignHelper.PropertiesToIgnore); DesignHelper.CopyProperties((ConfigureBno055)editorDialog.Bno055Dialog.Device, configureNode.Bno055, DesignHelper.PropertiesToIgnore); DesignHelper.CopyProperties((ConfigureTS4231V1)editorDialog.TS4231V1Dialog.Device, configureNode.TS4231, DesignHelper.PropertiesToIgnore); + DesignHelper.CopyProperties((ConfigurePersistentHeartbeat)editorDialog.HeartbeatDialog.Device, configureNode.Heartbeat, DesignHelper.PropertiesToIgnore); if (editorDialog.ElectricalStimulatorSequenceDialog.DialogResult == DialogResult.OK) configureNode.ElectricalStimulator = editorDialog.ElectricalStimulatorSequenceDialog.ElectricalStimulator; diff --git a/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorOptions.Designer.cs b/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorOptions.Designer.cs index 9abba431..8e3d42ff 100644 --- a/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorOptions.Designer.cs +++ b/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorOptions.Designer.cs @@ -48,8 +48,6 @@ private void InitializeComponent() this.labelInterBurstInterval = new System.Windows.Forms.Label(); this.textBoxTrainBurstCount = new System.Windows.Forms.TextBox(); this.labelTrainBurstCount = new System.Windows.Forms.Label(); - this.textBoxTrainDelay = new System.Windows.Forms.TextBox(); - this.labelTriggerDelay = new System.Windows.Forms.Label(); this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // @@ -57,9 +55,10 @@ private void InitializeComponent() // this.labelPulseCurrent.AutoSize = true; this.labelPulseCurrent.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelPulseCurrent.Location = new System.Drawing.Point(3, 31); + this.labelPulseCurrent.Location = new System.Drawing.Point(2, 25); + this.labelPulseCurrent.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelPulseCurrent.Name = "labelPulseCurrent"; - this.labelPulseCurrent.Size = new System.Drawing.Size(99, 31); + this.labelPulseCurrent.Size = new System.Drawing.Size(74, 25); this.labelPulseCurrent.TabIndex = 0; this.labelPulseCurrent.Text = "Current [µA]"; this.labelPulseCurrent.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -68,9 +67,10 @@ private void InitializeComponent() // this.labelPhaseOne.AutoSize = true; this.labelPhaseOne.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelPhaseOne.Location = new System.Drawing.Point(108, 0); + this.labelPhaseOne.Location = new System.Drawing.Point(80, 0); + this.labelPhaseOne.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelPhaseOne.Name = "labelPhaseOne"; - this.labelPhaseOne.Size = new System.Drawing.Size(99, 31); + this.labelPhaseOne.Size = new System.Drawing.Size(74, 25); this.labelPhaseOne.TabIndex = 2; this.labelPhaseOne.Text = "Phase One"; this.labelPhaseOne.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -93,22 +93,24 @@ private void InitializeComponent() this.tableLayoutPanel1.Controls.Add(this.textBoxPhaseOneDuration, 1, 2); this.tableLayoutPanel1.Controls.Add(this.textBoxInterPhaseDuration, 2, 2); this.tableLayoutPanel1.Controls.Add(this.textBoxPhaseTwoDuration, 3, 2); - this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 31); + this.tableLayoutPanel1.Location = new System.Drawing.Point(9, 25); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 3; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(420, 95); + this.tableLayoutPanel1.Size = new System.Drawing.Size(315, 77); this.tableLayoutPanel1.TabIndex = 0; // // labelPulseDuration // this.labelPulseDuration.AutoSize = true; this.labelPulseDuration.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelPulseDuration.Location = new System.Drawing.Point(3, 62); + this.labelPulseDuration.Location = new System.Drawing.Point(2, 50); + this.labelPulseDuration.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelPulseDuration.Name = "labelPulseDuration"; - this.labelPulseDuration.Size = new System.Drawing.Size(99, 33); + this.labelPulseDuration.Size = new System.Drawing.Size(74, 27); this.labelPulseDuration.TabIndex = 1; this.labelPulseDuration.Text = "Duration [µs]"; this.labelPulseDuration.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -117,9 +119,10 @@ private void InitializeComponent() // this.labelInterPhase.AutoSize = true; this.labelInterPhase.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelInterPhase.Location = new System.Drawing.Point(213, 0); + this.labelInterPhase.Location = new System.Drawing.Point(158, 0); + this.labelInterPhase.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelInterPhase.Name = "labelInterPhase"; - this.labelInterPhase.Size = new System.Drawing.Size(99, 31); + this.labelInterPhase.Size = new System.Drawing.Size(74, 25); this.labelInterPhase.TabIndex = 3; this.labelInterPhase.Text = "Inter-Phase"; this.labelInterPhase.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -128,9 +131,10 @@ private void InitializeComponent() // this.labelPhaseTwo.AutoSize = true; this.labelPhaseTwo.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelPhaseTwo.Location = new System.Drawing.Point(318, 0); + this.labelPhaseTwo.Location = new System.Drawing.Point(236, 0); + this.labelPhaseTwo.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelPhaseTwo.Name = "labelPhaseTwo"; - this.labelPhaseTwo.Size = new System.Drawing.Size(99, 31); + this.labelPhaseTwo.Size = new System.Drawing.Size(77, 25); this.labelPhaseTwo.TabIndex = 4; this.labelPhaseTwo.Text = "Phase Two"; this.labelPhaseTwo.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -138,146 +142,136 @@ private void InitializeComponent() // textBoxPhaseOneCurrent // this.textBoxPhaseOneCurrent.Dock = System.Windows.Forms.DockStyle.Fill; - this.textBoxPhaseOneCurrent.Location = new System.Drawing.Point(120, 34); - this.textBoxPhaseOneCurrent.Margin = new System.Windows.Forms.Padding(15, 3, 15, 3); + this.textBoxPhaseOneCurrent.Location = new System.Drawing.Point(89, 27); + this.textBoxPhaseOneCurrent.Margin = new System.Windows.Forms.Padding(11, 2, 11, 2); this.textBoxPhaseOneCurrent.Name = "textBoxPhaseOneCurrent"; - this.textBoxPhaseOneCurrent.Size = new System.Drawing.Size(75, 22); + this.textBoxPhaseOneCurrent.Size = new System.Drawing.Size(56, 20); this.textBoxPhaseOneCurrent.TabIndex = 0; // // textBoxInterPhaseCurrent // this.textBoxInterPhaseCurrent.Dock = System.Windows.Forms.DockStyle.Fill; - this.textBoxInterPhaseCurrent.Location = new System.Drawing.Point(225, 34); - this.textBoxInterPhaseCurrent.Margin = new System.Windows.Forms.Padding(15, 3, 15, 3); + this.textBoxInterPhaseCurrent.Location = new System.Drawing.Point(167, 27); + this.textBoxInterPhaseCurrent.Margin = new System.Windows.Forms.Padding(11, 2, 11, 2); this.textBoxInterPhaseCurrent.Name = "textBoxInterPhaseCurrent"; - this.textBoxInterPhaseCurrent.Size = new System.Drawing.Size(75, 22); + this.textBoxInterPhaseCurrent.Size = new System.Drawing.Size(56, 20); this.textBoxInterPhaseCurrent.TabIndex = 1; // // textBoxPhaseTwoCurrent // this.textBoxPhaseTwoCurrent.Dock = System.Windows.Forms.DockStyle.Fill; - this.textBoxPhaseTwoCurrent.Location = new System.Drawing.Point(330, 34); - this.textBoxPhaseTwoCurrent.Margin = new System.Windows.Forms.Padding(15, 3, 15, 3); + this.textBoxPhaseTwoCurrent.Location = new System.Drawing.Point(245, 27); + this.textBoxPhaseTwoCurrent.Margin = new System.Windows.Forms.Padding(11, 2, 11, 2); this.textBoxPhaseTwoCurrent.Name = "textBoxPhaseTwoCurrent"; - this.textBoxPhaseTwoCurrent.Size = new System.Drawing.Size(75, 22); + this.textBoxPhaseTwoCurrent.Size = new System.Drawing.Size(59, 20); this.textBoxPhaseTwoCurrent.TabIndex = 2; // // textBoxPhaseOneDuration // this.textBoxPhaseOneDuration.Dock = System.Windows.Forms.DockStyle.Fill; - this.textBoxPhaseOneDuration.Location = new System.Drawing.Point(120, 65); - this.textBoxPhaseOneDuration.Margin = new System.Windows.Forms.Padding(15, 3, 15, 3); + this.textBoxPhaseOneDuration.Location = new System.Drawing.Point(89, 52); + this.textBoxPhaseOneDuration.Margin = new System.Windows.Forms.Padding(11, 2, 11, 2); this.textBoxPhaseOneDuration.Name = "textBoxPhaseOneDuration"; - this.textBoxPhaseOneDuration.Size = new System.Drawing.Size(75, 22); + this.textBoxPhaseOneDuration.Size = new System.Drawing.Size(56, 20); this.textBoxPhaseOneDuration.TabIndex = 3; // // textBoxInterPhaseDuration // this.textBoxInterPhaseDuration.Dock = System.Windows.Forms.DockStyle.Fill; - this.textBoxInterPhaseDuration.Location = new System.Drawing.Point(225, 65); - this.textBoxInterPhaseDuration.Margin = new System.Windows.Forms.Padding(15, 3, 15, 3); + this.textBoxInterPhaseDuration.Location = new System.Drawing.Point(167, 52); + this.textBoxInterPhaseDuration.Margin = new System.Windows.Forms.Padding(11, 2, 11, 2); this.textBoxInterPhaseDuration.Name = "textBoxInterPhaseDuration"; - this.textBoxInterPhaseDuration.Size = new System.Drawing.Size(75, 22); + this.textBoxInterPhaseDuration.Size = new System.Drawing.Size(56, 20); this.textBoxInterPhaseDuration.TabIndex = 4; // // textBoxPhaseTwoDuration // this.textBoxPhaseTwoDuration.Dock = System.Windows.Forms.DockStyle.Fill; - this.textBoxPhaseTwoDuration.Location = new System.Drawing.Point(330, 65); - this.textBoxPhaseTwoDuration.Margin = new System.Windows.Forms.Padding(15, 3, 15, 3); + this.textBoxPhaseTwoDuration.Location = new System.Drawing.Point(245, 52); + this.textBoxPhaseTwoDuration.Margin = new System.Windows.Forms.Padding(11, 2, 11, 2); this.textBoxPhaseTwoDuration.Name = "textBoxPhaseTwoDuration"; - this.textBoxPhaseTwoDuration.Size = new System.Drawing.Size(75, 22); + this.textBoxPhaseTwoDuration.Size = new System.Drawing.Size(59, 20); this.textBoxPhaseTwoDuration.TabIndex = 5; // // labelPulsePeriod // this.labelPulsePeriod.AutoSize = true; - this.labelPulsePeriod.Location = new System.Drawing.Point(106, 146); + this.labelPulsePeriod.Location = new System.Drawing.Point(80, 119); + this.labelPulsePeriod.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelPulsePeriod.Name = "labelPulsePeriod"; - this.labelPulsePeriod.Size = new System.Drawing.Size(109, 16); + this.labelPulsePeriod.Size = new System.Drawing.Size(86, 13); this.labelPulsePeriod.TabIndex = 4; this.labelPulsePeriod.Text = "Pulse Period [µs]"; // // textBoxBurstPulseCount // - this.textBoxBurstPulseCount.Location = new System.Drawing.Point(132, 200); + this.textBoxBurstPulseCount.Location = new System.Drawing.Point(99, 162); + this.textBoxBurstPulseCount.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.textBoxBurstPulseCount.Name = "textBoxBurstPulseCount"; - this.textBoxBurstPulseCount.Size = new System.Drawing.Size(75, 22); + this.textBoxBurstPulseCount.Size = new System.Drawing.Size(57, 20); this.textBoxBurstPulseCount.TabIndex = 2; this.textBoxBurstPulseCount.TextChanged += new System.EventHandler(this.BurstPulseCountChanged); // // labelBurstPulseCount // this.labelBurstPulseCount.AutoSize = true; - this.labelBurstPulseCount.Location = new System.Drawing.Point(15, 203); + this.labelBurstPulseCount.Location = new System.Drawing.Point(11, 165); + this.labelBurstPulseCount.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelBurstPulseCount.Name = "labelBurstPulseCount"; - this.labelBurstPulseCount.Size = new System.Drawing.Size(111, 16); + this.labelBurstPulseCount.Size = new System.Drawing.Size(91, 13); this.labelBurstPulseCount.TabIndex = 6; this.labelBurstPulseCount.Text = "Burst Pulse Count"; // // textBoxPulsePeriod // - this.textBoxPulsePeriod.Location = new System.Drawing.Point(237, 146); + this.textBoxPulsePeriod.Location = new System.Drawing.Point(178, 119); + this.textBoxPulsePeriod.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.textBoxPulsePeriod.Name = "textBoxPulsePeriod"; - this.textBoxPulsePeriod.Size = new System.Drawing.Size(75, 22); + this.textBoxPulsePeriod.Size = new System.Drawing.Size(57, 20); this.textBoxPulsePeriod.TabIndex = 1; // // textBoxInterBurstInterval // - this.textBoxInterBurstInterval.Location = new System.Drawing.Point(342, 203); + this.textBoxInterBurstInterval.Location = new System.Drawing.Point(256, 165); + this.textBoxInterBurstInterval.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.textBoxInterBurstInterval.Name = "textBoxInterBurstInterval"; - this.textBoxInterBurstInterval.Size = new System.Drawing.Size(75, 22); + this.textBoxInterBurstInterval.Size = new System.Drawing.Size(57, 20); this.textBoxInterBurstInterval.TabIndex = 3; // // labelInterBurstInterval // this.labelInterBurstInterval.AutoSize = true; - this.labelInterBurstInterval.Location = new System.Drawing.Point(245, 200); + this.labelInterBurstInterval.Location = new System.Drawing.Point(184, 162); + this.labelInterBurstInterval.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelInterBurstInterval.Name = "labelInterBurstInterval"; - this.labelInterBurstInterval.Size = new System.Drawing.Size(75, 32); + this.labelInterBurstInterval.Size = new System.Drawing.Size(62, 26); this.labelInterBurstInterval.TabIndex = 8; this.labelInterBurstInterval.Text = "Inter-Burst \r\nInterval [µs]"; // // textBoxTrainBurstCount // - this.textBoxTrainBurstCount.Location = new System.Drawing.Point(132, 251); + this.textBoxTrainBurstCount.Location = new System.Drawing.Point(99, 204); + this.textBoxTrainBurstCount.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.textBoxTrainBurstCount.Name = "textBoxTrainBurstCount"; - this.textBoxTrainBurstCount.Size = new System.Drawing.Size(75, 22); + this.textBoxTrainBurstCount.Size = new System.Drawing.Size(57, 20); this.textBoxTrainBurstCount.TabIndex = 4; this.textBoxTrainBurstCount.TextChanged += new System.EventHandler(this.TrainBurstCountChanged); // // labelTrainBurstCount // this.labelTrainBurstCount.AutoSize = true; - this.labelTrainBurstCount.Location = new System.Drawing.Point(15, 254); + this.labelTrainBurstCount.Location = new System.Drawing.Point(11, 206); + this.labelTrainBurstCount.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelTrainBurstCount.Name = "labelTrainBurstCount"; - this.labelTrainBurstCount.Size = new System.Drawing.Size(108, 16); + this.labelTrainBurstCount.Size = new System.Drawing.Size(89, 13); this.labelTrainBurstCount.TabIndex = 10; this.labelTrainBurstCount.Text = "Train Burst Count"; // - // textBoxTrainDelay - // - this.textBoxTrainDelay.Location = new System.Drawing.Point(342, 251); - this.textBoxTrainDelay.Name = "textBoxTrainDelay"; - this.textBoxTrainDelay.Size = new System.Drawing.Size(75, 22); - this.textBoxTrainDelay.TabIndex = 5; - // - // labelTriggerDelay - // - this.labelTriggerDelay.AutoSize = true; - this.labelTriggerDelay.Location = new System.Drawing.Point(225, 254); - this.labelTriggerDelay.Name = "labelTriggerDelay"; - this.labelTriggerDelay.Size = new System.Drawing.Size(102, 16); - this.labelTriggerDelay.TabIndex = 12; - this.labelTriggerDelay.Text = "Train Delay [µs]"; - // // Headstage64ElectricalStimulatorOptions // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(438, 289); - this.Controls.Add(this.textBoxTrainDelay); - this.Controls.Add(this.labelTriggerDelay); + this.ClientSize = new System.Drawing.Size(328, 235); this.Controls.Add(this.textBoxTrainBurstCount); this.Controls.Add(this.labelTrainBurstCount); this.Controls.Add(this.textBoxInterBurstInterval); @@ -287,6 +281,7 @@ private void InitializeComponent() this.Controls.Add(this.textBoxPulsePeriod); this.Controls.Add(this.labelPulsePeriod); this.Controls.Add(this.tableLayoutPanel1); + this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.Name = "Headstage64ElectricalStimulatorOptions"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Headstage64ElectricalStimulatorOptions"; @@ -319,7 +314,5 @@ private void InitializeComponent() private System.Windows.Forms.Label labelInterBurstInterval; internal System.Windows.Forms.TextBox textBoxTrainBurstCount; private System.Windows.Forms.Label labelTrainBurstCount; - internal System.Windows.Forms.TextBox textBoxTrainDelay; - private System.Windows.Forms.Label labelTriggerDelay; } } diff --git a/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorOptions.cs b/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorOptions.cs index d5968551..4438cf10 100644 --- a/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorOptions.cs +++ b/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorOptions.cs @@ -35,7 +35,6 @@ public Headstage64ElectricalStimulatorOptions(ConfigureHeadstage64ElectricalStim textBoxBurstPulseCount.Text = electricalStimulator.BurstPulseCount.ToString(); textBoxInterBurstInterval.Text = electricalStimulator.InterBurstInterval.ToString(); textBoxTrainBurstCount.Text = electricalStimulator.TrainBurstCount.ToString(); - textBoxTrainDelay.Text = electricalStimulator.TriggerDelay.ToString(); } void BurstPulseCountChanged(object sender, System.EventArgs e) diff --git a/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorSequenceDialog.cs b/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorSequenceDialog.cs index c5b749fd..31b4c843 100644 --- a/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorSequenceDialog.cs +++ b/OpenEphys.Onix1.Design/Headstage64ElectricalStimulatorSequenceDialog.cs @@ -83,11 +83,6 @@ public Headstage64ElectricalStimulatorSequenceDialog(ConfigureHeadstage64Electri new TextBoxBinding( StimulusSequenceOptions.textBoxPulsePeriod, value => { ElectricalStimulator.InterPulseInterval = value; return ElectricalStimulator.InterPulseInterval; }, - uint.Parse) }, - { StimulusSequenceOptions.textBoxTrainDelay, - new TextBoxBinding( - StimulusSequenceOptions.textBoxTrainDelay, - value => { ElectricalStimulator.TriggerDelay = value; return ElectricalStimulator.TriggerDelay; }, uint.Parse) } }; @@ -127,7 +122,8 @@ public Headstage64ElectricalStimulatorSequenceDialog(ConfigureHeadstage64Electri toolStripStatusIsValid.BorderSides = ToolStripStatusLabelBorderSides.None; - SetXAxisTitle("Time [µs]"); + xAxisScaleUnits = "µs"; + SetXAxisTitle($"Time [{xAxisScaleUnits}]"); SetYAxisTitle(""); RemoveYAxisLabels(); @@ -270,7 +266,7 @@ internal override PointPairList[] CreateStimulusWaveforms() { for (int channel = 0; channel < NumberOfChannels; channel++) { - waveforms[channel] = new PointPairList { new PointPair(0, 0), new PointPair(ElectricalStimulator.TriggerDelay, 0) }; + waveforms[channel] = new PointPairList { new PointPair(0, 0), new PointPair(0, 0) }; for (int i = 0; i < ElectricalStimulator.TrainBurstCount; i++) { diff --git a/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorOptions.Designer.cs b/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorOptions.Designer.cs index 9c3c103b..0c3cc3de 100644 --- a/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorOptions.Designer.cs +++ b/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorOptions.Designer.cs @@ -28,8 +28,6 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.textBoxDelay = new System.Windows.Forms.TextBox(); - this.labelDelay = new System.Windows.Forms.Label(); this.textBoxBurstsPerTrain = new System.Windows.Forms.TextBox(); this.labelBurstsPerTrain = new System.Windows.Forms.Label(); this.textBoxInterBurstInterval = new System.Windows.Forms.TextBox(); @@ -46,117 +44,111 @@ private void InitializeComponent() this.trackBarChannelTwoPercent = new System.Windows.Forms.TrackBar(); this.textBoxChannelTwoPercent = new System.Windows.Forms.TextBox(); this.labelChannelTwoPercent = new System.Windows.Forms.Label(); - this.textBoxPulsePeriod = new System.Windows.Forms.TextBox(); - this.labelPulsePeriod = new System.Windows.Forms.Label(); + this.textBoxPulseFrequencyHz = new System.Windows.Forms.TextBox(); + this.labelPulseFrequencyHz = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.trackBarChannelOnePercent)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarChannelTwoPercent)).BeginInit(); this.SuspendLayout(); // - // textBoxDelay - // - this.textBoxDelay.Location = new System.Drawing.Point(356, 244); - this.textBoxDelay.Name = "textBoxDelay"; - this.textBoxDelay.Size = new System.Drawing.Size(75, 22); - this.textBoxDelay.TabIndex = 19; - // - // labelDelay - // - this.labelDelay.AutoSize = true; - this.labelDelay.Location = new System.Drawing.Point(218, 247); - this.labelDelay.Name = "labelDelay"; - this.labelDelay.Size = new System.Drawing.Size(72, 16); - this.labelDelay.TabIndex = 18; - this.labelDelay.Text = "Delay [ms]"; - // // textBoxBurstsPerTrain // - this.textBoxBurstsPerTrain.Location = new System.Drawing.Point(133, 244); + this.textBoxBurstsPerTrain.Location = new System.Drawing.Point(100, 198); + this.textBoxBurstsPerTrain.Margin = new System.Windows.Forms.Padding(2); this.textBoxBurstsPerTrain.Name = "textBoxBurstsPerTrain"; - this.textBoxBurstsPerTrain.Size = new System.Drawing.Size(75, 22); + this.textBoxBurstsPerTrain.Size = new System.Drawing.Size(57, 20); this.textBoxBurstsPerTrain.TabIndex = 17; this.textBoxBurstsPerTrain.TextChanged += new System.EventHandler(this.BurstsPerTrainChanged); // // labelBurstsPerTrain // this.labelBurstsPerTrain.AutoSize = true; - this.labelBurstsPerTrain.Location = new System.Drawing.Point(6, 247); + this.labelBurstsPerTrain.Location = new System.Drawing.Point(4, 201); + this.labelBurstsPerTrain.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelBurstsPerTrain.Name = "labelBurstsPerTrain"; - this.labelBurstsPerTrain.Size = new System.Drawing.Size(102, 16); + this.labelBurstsPerTrain.Size = new System.Drawing.Size(82, 13); this.labelBurstsPerTrain.TabIndex = 16; this.labelBurstsPerTrain.Text = "Bursts Per Train"; // // textBoxInterBurstInterval // - this.textBoxInterBurstInterval.Location = new System.Drawing.Point(356, 195); + this.textBoxInterBurstInterval.Location = new System.Drawing.Point(267, 158); + this.textBoxInterBurstInterval.Margin = new System.Windows.Forms.Padding(2); this.textBoxInterBurstInterval.Name = "textBoxInterBurstInterval"; - this.textBoxInterBurstInterval.Size = new System.Drawing.Size(75, 22); + this.textBoxInterBurstInterval.Size = new System.Drawing.Size(57, 20); this.textBoxInterBurstInterval.TabIndex = 15; // // labelInterBurstInterval // - this.labelInterBurstInterval.Location = new System.Drawing.Point(216, 198); + this.labelInterBurstInterval.Location = new System.Drawing.Point(162, 161); + this.labelInterBurstInterval.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelInterBurstInterval.Name = "labelInterBurstInterval"; - this.labelInterBurstInterval.Size = new System.Drawing.Size(146, 19); + this.labelInterBurstInterval.Size = new System.Drawing.Size(110, 15); this.labelInterBurstInterval.TabIndex = 14; this.labelInterBurstInterval.Text = "Inter-Burst Interval [ms]"; // // textBoxPulsesPerBurst // - this.textBoxPulsesPerBurst.Location = new System.Drawing.Point(133, 195); + this.textBoxPulsesPerBurst.Location = new System.Drawing.Point(100, 158); + this.textBoxPulsesPerBurst.Margin = new System.Windows.Forms.Padding(2); this.textBoxPulsesPerBurst.Name = "textBoxPulsesPerBurst"; - this.textBoxPulsesPerBurst.Size = new System.Drawing.Size(75, 22); + this.textBoxPulsesPerBurst.Size = new System.Drawing.Size(57, 20); this.textBoxPulsesPerBurst.TabIndex = 13; this.textBoxPulsesPerBurst.TextChanged += new System.EventHandler(this.PulsesPerBurstChanged); // // labelPulsesPerBurst // this.labelPulsesPerBurst.AutoSize = true; - this.labelPulsesPerBurst.Location = new System.Drawing.Point(6, 198); + this.labelPulsesPerBurst.Location = new System.Drawing.Point(4, 161); + this.labelPulsesPerBurst.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelPulsesPerBurst.Name = "labelPulsesPerBurst"; - this.labelPulsesPerBurst.Size = new System.Drawing.Size(105, 16); + this.labelPulsesPerBurst.Size = new System.Drawing.Size(84, 13); this.labelPulsesPerBurst.TabIndex = 12; this.labelPulsesPerBurst.Text = "Pulses Per Burst"; // // labelMaxCurrent // this.labelMaxCurrent.AutoSize = true; - this.labelMaxCurrent.Location = new System.Drawing.Point(6, 20); + this.labelMaxCurrent.Location = new System.Drawing.Point(4, 16); + this.labelMaxCurrent.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelMaxCurrent.Name = "labelMaxCurrent"; - this.labelMaxCurrent.Size = new System.Drawing.Size(108, 16); + this.labelMaxCurrent.Size = new System.Drawing.Size(88, 13); this.labelMaxCurrent.TabIndex = 0; this.labelMaxCurrent.Text = "Max Current [mA]"; // // textBoxMaxCurrent // - this.textBoxMaxCurrent.Location = new System.Drawing.Point(133, 17); + this.textBoxMaxCurrent.Location = new System.Drawing.Point(100, 14); + this.textBoxMaxCurrent.Margin = new System.Windows.Forms.Padding(2); this.textBoxMaxCurrent.Name = "textBoxMaxCurrent"; - this.textBoxMaxCurrent.Size = new System.Drawing.Size(75, 22); + this.textBoxMaxCurrent.Size = new System.Drawing.Size(57, 20); this.textBoxMaxCurrent.TabIndex = 1; // // textBoxChannelOnePercent // - this.textBoxChannelOnePercent.Location = new System.Drawing.Point(133, 58); + this.textBoxChannelOnePercent.Location = new System.Drawing.Point(100, 47); + this.textBoxChannelOnePercent.Margin = new System.Windows.Forms.Padding(2); this.textBoxChannelOnePercent.Name = "textBoxChannelOnePercent"; - this.textBoxChannelOnePercent.Size = new System.Drawing.Size(75, 22); + this.textBoxChannelOnePercent.Size = new System.Drawing.Size(57, 20); this.textBoxChannelOnePercent.TabIndex = 3; // // labelChannelOnePercent // this.labelChannelOnePercent.AutoSize = true; - this.labelChannelOnePercent.Location = new System.Drawing.Point(6, 61); + this.labelChannelOnePercent.Location = new System.Drawing.Point(4, 50); + this.labelChannelOnePercent.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelChannelOnePercent.Name = "labelChannelOnePercent"; - this.labelChannelOnePercent.Size = new System.Drawing.Size(107, 16); + this.labelChannelOnePercent.Size = new System.Drawing.Size(86, 13); this.labelChannelOnePercent.TabIndex = 2; this.labelChannelOnePercent.Text = "Channel One [%]"; // // trackBarChannelOnePercent // this.trackBarChannelOnePercent.LargeChange = 125; - this.trackBarChannelOnePercent.Location = new System.Drawing.Point(11, 83); + this.trackBarChannelOnePercent.Location = new System.Drawing.Point(8, 67); this.trackBarChannelOnePercent.Margin = new System.Windows.Forms.Padding(0); this.trackBarChannelOnePercent.Maximum = 1000; this.trackBarChannelOnePercent.Name = "trackBarChannelOnePercent"; - this.trackBarChannelOnePercent.Size = new System.Drawing.Size(197, 45); + this.trackBarChannelOnePercent.Size = new System.Drawing.Size(148, 45); this.trackBarChannelOnePercent.SmallChange = 125; this.trackBarChannelOnePercent.TabIndex = 6; this.trackBarChannelOnePercent.TickFrequency = 125; @@ -165,28 +157,30 @@ private void InitializeComponent() // // textBoxPulseDuration // - this.textBoxPulseDuration.Location = new System.Drawing.Point(133, 146); + this.textBoxPulseDuration.Location = new System.Drawing.Point(100, 119); + this.textBoxPulseDuration.Margin = new System.Windows.Forms.Padding(2); this.textBoxPulseDuration.Name = "textBoxPulseDuration"; - this.textBoxPulseDuration.Size = new System.Drawing.Size(75, 22); + this.textBoxPulseDuration.Size = new System.Drawing.Size(57, 20); this.textBoxPulseDuration.TabIndex = 9; // // labelPulseDuration // this.labelPulseDuration.AutoSize = true; - this.labelPulseDuration.Location = new System.Drawing.Point(6, 149); + this.labelPulseDuration.Location = new System.Drawing.Point(4, 121); + this.labelPulseDuration.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelPulseDuration.Name = "labelPulseDuration"; - this.labelPulseDuration.Size = new System.Drawing.Size(123, 16); + this.labelPulseDuration.Size = new System.Drawing.Size(98, 13); this.labelPulseDuration.TabIndex = 8; this.labelPulseDuration.Text = "Pulse Duration [ms]"; // // trackBarChannelTwoPercent // this.trackBarChannelTwoPercent.LargeChange = 125; - this.trackBarChannelTwoPercent.Location = new System.Drawing.Point(219, 83); + this.trackBarChannelTwoPercent.Location = new System.Drawing.Point(164, 67); this.trackBarChannelTwoPercent.Margin = new System.Windows.Forms.Padding(0); this.trackBarChannelTwoPercent.Maximum = 1000; this.trackBarChannelTwoPercent.Name = "trackBarChannelTwoPercent"; - this.trackBarChannelTwoPercent.Size = new System.Drawing.Size(212, 45); + this.trackBarChannelTwoPercent.Size = new System.Drawing.Size(159, 45); this.trackBarChannelTwoPercent.SmallChange = 125; this.trackBarChannelTwoPercent.TabIndex = 7; this.trackBarChannelTwoPercent.TickFrequency = 125; @@ -194,43 +188,47 @@ private void InitializeComponent() // // textBoxChannelTwoPercent // - this.textBoxChannelTwoPercent.Location = new System.Drawing.Point(356, 58); + this.textBoxChannelTwoPercent.Location = new System.Drawing.Point(267, 47); + this.textBoxChannelTwoPercent.Margin = new System.Windows.Forms.Padding(2); this.textBoxChannelTwoPercent.Name = "textBoxChannelTwoPercent"; - this.textBoxChannelTwoPercent.Size = new System.Drawing.Size(75, 22); + this.textBoxChannelTwoPercent.Size = new System.Drawing.Size(57, 20); this.textBoxChannelTwoPercent.TabIndex = 5; // // labelChannelTwoPercent // this.labelChannelTwoPercent.AutoSize = true; - this.labelChannelTwoPercent.Location = new System.Drawing.Point(216, 61); + this.labelChannelTwoPercent.Location = new System.Drawing.Point(162, 50); + this.labelChannelTwoPercent.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.labelChannelTwoPercent.Name = "labelChannelTwoPercent"; - this.labelChannelTwoPercent.Size = new System.Drawing.Size(108, 16); + this.labelChannelTwoPercent.Size = new System.Drawing.Size(87, 13); this.labelChannelTwoPercent.TabIndex = 4; this.labelChannelTwoPercent.Text = "Channel Two [%]"; // - // textBoxPulsePeriod + // textBoxPulseFrequencyHz // - this.textBoxPulsePeriod.Location = new System.Drawing.Point(356, 146); - this.textBoxPulsePeriod.Name = "textBoxPulsePeriod"; - this.textBoxPulsePeriod.Size = new System.Drawing.Size(75, 22); - this.textBoxPulsePeriod.TabIndex = 11; + this.textBoxPulseFrequencyHz.Location = new System.Drawing.Point(267, 119); + this.textBoxPulseFrequencyHz.Margin = new System.Windows.Forms.Padding(2); + this.textBoxPulseFrequencyHz.Name = "textBoxPulseFrequencyHz"; + this.textBoxPulseFrequencyHz.Size = new System.Drawing.Size(57, 20); + this.textBoxPulseFrequencyHz.TabIndex = 11; // - // labelPulsePeriod + // labelPulseFrequencyHz // - this.labelPulsePeriod.AutoSize = true; - this.labelPulsePeriod.Location = new System.Drawing.Point(216, 149); - this.labelPulsePeriod.Name = "labelPulsePeriod"; - this.labelPulsePeriod.Size = new System.Drawing.Size(113, 16); - this.labelPulsePeriod.TabIndex = 10; - this.labelPulsePeriod.Text = "Pulse Period [ms]"; + this.labelPulseFrequencyHz.AutoSize = true; + this.labelPulseFrequencyHz.Location = new System.Drawing.Point(162, 121); + this.labelPulseFrequencyHz.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labelPulseFrequencyHz.Name = "labelPulseFrequencyHz"; + this.labelPulseFrequencyHz.Size = new System.Drawing.Size(82, 13); + this.labelPulseFrequencyHz.TabIndex = 10; + this.labelPulseFrequencyHz.Text = "Pulse Freq. [Hz]"; // // Headstage64OpticalStimulatorOptions // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(438, 289); - this.Controls.Add(this.textBoxPulsePeriod); - this.Controls.Add(this.labelPulsePeriod); + this.ClientSize = new System.Drawing.Size(328, 235); + this.Controls.Add(this.textBoxPulseFrequencyHz); + this.Controls.Add(this.labelPulseFrequencyHz); this.Controls.Add(this.trackBarChannelTwoPercent); this.Controls.Add(this.textBoxChannelTwoPercent); this.Controls.Add(this.labelChannelTwoPercent); @@ -241,14 +239,13 @@ private void InitializeComponent() this.Controls.Add(this.labelChannelOnePercent); this.Controls.Add(this.textBoxMaxCurrent); this.Controls.Add(this.labelMaxCurrent); - this.Controls.Add(this.textBoxDelay); - this.Controls.Add(this.labelDelay); this.Controls.Add(this.textBoxBurstsPerTrain); this.Controls.Add(this.labelBurstsPerTrain); this.Controls.Add(this.textBoxInterBurstInterval); this.Controls.Add(this.labelInterBurstInterval); this.Controls.Add(this.textBoxPulsesPerBurst); this.Controls.Add(this.labelPulsesPerBurst); + this.Margin = new System.Windows.Forms.Padding(2); this.Name = "Headstage64OpticalStimulatorOptions"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Headstage64OpticalStimulatorOptions"; @@ -260,9 +257,6 @@ private void InitializeComponent() } #endregion - - internal System.Windows.Forms.TextBox textBoxDelay; - private System.Windows.Forms.Label labelDelay; internal System.Windows.Forms.TextBox textBoxBurstsPerTrain; private System.Windows.Forms.Label labelBurstsPerTrain; internal System.Windows.Forms.TextBox textBoxInterBurstInterval; @@ -279,7 +273,7 @@ private void InitializeComponent() private System.Windows.Forms.Label labelChannelTwoPercent; internal System.Windows.Forms.TrackBar trackBarChannelOnePercent; internal System.Windows.Forms.TrackBar trackBarChannelTwoPercent; - internal System.Windows.Forms.TextBox textBoxPulsePeriod; - private System.Windows.Forms.Label labelPulsePeriod; + internal System.Windows.Forms.TextBox textBoxPulseFrequencyHz; + private System.Windows.Forms.Label labelPulseFrequencyHz; } } diff --git a/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorOptions.cs b/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorOptions.cs index 83ff20df..00d64e16 100644 --- a/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorOptions.cs +++ b/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorOptions.cs @@ -32,8 +32,8 @@ internal void UpdateSequenceParameters(ConfigureHeadstage64OpticalStimulator opt { textBoxMaxCurrent.Text = opticalStimulator.MaxCurrent.ToString(); textBoxPulseDuration.Text = opticalStimulator.PulseDuration.ToString(); - textBoxPulsePeriod.Text = opticalStimulator.PulsesPerSecond.ToString(); - textBoxPulsePeriod.Enabled = opticalStimulator.PulsesPerBurst > 1; + textBoxPulseFrequencyHz.Text = opticalStimulator.PulsesPerSecond.ToString(); + textBoxPulseFrequencyHz.Enabled = opticalStimulator.PulsesPerBurst > 1; textBoxChannelOnePercent.Text = opticalStimulator.ChannelOneCurrent.ToString(); trackBarChannelOnePercent.Value = (int)(opticalStimulator.ChannelOneCurrent * channelOneScalingFactor); @@ -44,7 +44,6 @@ internal void UpdateSequenceParameters(ConfigureHeadstage64OpticalStimulator opt textBoxInterBurstInterval.Text = opticalStimulator.InterBurstInterval.ToString(); textBoxInterBurstInterval.Enabled = opticalStimulator.BurstsPerTrain > 1; textBoxBurstsPerTrain.Text = opticalStimulator.BurstsPerTrain.ToString(); - textBoxDelay.Text = opticalStimulator.Delay.ToString(); } internal readonly double channelOneScalingFactor; @@ -54,7 +53,7 @@ void PulsesPerBurstChanged(object sender, System.EventArgs e) { if (int.TryParse(textBoxPulsesPerBurst.Text, out int result)) { - textBoxPulsePeriod.Enabled = result > 1; + textBoxPulseFrequencyHz.Enabled = result > 1; } } diff --git a/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorSequenceDialog.cs b/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorSequenceDialog.cs index 66611490..e858f7da 100644 --- a/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorSequenceDialog.cs +++ b/OpenEphys.Onix1.Design/Headstage64OpticalStimulatorSequenceDialog.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.IO; using System.Linq; using System.Windows.Forms; using ZedGraph; @@ -78,19 +77,14 @@ public Headstage64OpticalStimulatorSequenceDialog(ConfigureHeadstage64OpticalSti StimulusSequenceOptions.textBoxInterBurstInterval, value => { OpticalStimulator.InterBurstInterval = value; return OpticalStimulator.InterBurstInterval; }, double.Parse) }, - { StimulusSequenceOptions.textBoxDelay, - new TextBoxBinding( - StimulusSequenceOptions.textBoxDelay, - value => { OpticalStimulator.Delay = value; return OpticalStimulator.Delay; }, - double.Parse) }, { StimulusSequenceOptions.textBoxPulseDuration, new TextBoxBinding( StimulusSequenceOptions.textBoxPulseDuration, value => { OpticalStimulator.PulseDuration = value; return OpticalStimulator.PulseDuration; }, double.Parse) }, - { StimulusSequenceOptions.textBoxPulsePeriod, + { StimulusSequenceOptions.textBoxPulseFrequencyHz, new TextBoxBinding( - StimulusSequenceOptions.textBoxPulsePeriod, + StimulusSequenceOptions.textBoxPulseFrequencyHz, value => { OpticalStimulator.PulsesPerSecond = value; return OpticalStimulator.PulsesPerSecond; }, double.Parse) }, }; @@ -131,8 +125,9 @@ public Headstage64OpticalStimulatorSequenceDialog(ConfigureHeadstage64OpticalSti toolStripStatusIsValid.BorderSides = ToolStripStatusLabelBorderSides.None; - SetXAxisTitle("Time [µs]"); - yAxisScale = "mA"; + xAxisScaleUnits = "ms"; + SetXAxisTitle($"Time [{xAxisScaleUnits}]"); + yAxisScaleUnits = "mA"; DisableVerticalZoom(); @@ -227,7 +222,7 @@ internal override PointPairList[] CreateStimulusWaveforms() waveforms[channel] = new PointPairList { - new PointPairList { new PointPair(0, offset), new PointPair(OpticalStimulator.Delay, offset) } + new PointPairList { new PointPair(0, offset), new PointPair(0, offset) } }; var stimulusCurrent = offset + GetChannelCurrentScaled(OpticalStimulator.MaxCurrent, @@ -244,7 +239,7 @@ internal override PointPairList[] CreateStimulusWaveforms() if (j != OpticalStimulator.PulsesPerBurst - 1) { - waveforms[channel].Add(new PointPair(waveforms[channel].Last().X + OpticalStimulator.PulsesPerSecond - OpticalStimulator.PulseDuration, offset)); + waveforms[channel].Add(new PointPair(waveforms[channel].Last().X + 1000.0 / OpticalStimulator.PulsesPerSecond - OpticalStimulator.PulseDuration, offset)); } } @@ -283,9 +278,9 @@ static bool IsSequenceValid(ConfigureHeadstage64OpticalStimulator sequence, out reason = "Maximum current is invalid."; return false; } - else if (sequence.PulsesPerBurst > 1 && sequence.PulsesPerSecond <= sequence.PulseDuration) + else if (sequence.PulsesPerBurst > 1 && 1000.0 / sequence.PulsesPerSecond <= sequence.PulseDuration) { - reason = "Pulse period is too short compared to the pulse duration."; + reason = "Pulse frequency is too high compared to the pulse duration."; return false; } diff --git a/OpenEphys.Onix1/ConfigureHeadstage64.cs b/OpenEphys.Onix1/ConfigureHeadstage64.cs index 321ed807..d231f110 100644 --- a/OpenEphys.Onix1/ConfigureHeadstage64.cs +++ b/OpenEphys.Onix1/ConfigureHeadstage64.cs @@ -88,6 +88,17 @@ public ConfigureHeadstage64() [Editor("OpenEphys.Onix1.Design.Headstage64OpticalStimulatorUITypeEditor, OpenEphys.Onix1.Design", typeof(UITypeEditor))] public ConfigureHeadstage64OpticalStimulator OpticalStimulator { get; set; } = new(); + /// + /// Gets or sets the heartbeat configuration. + /// + /// + /// This heartbeat is always enabled and beats at a minimum of 10 Hz. + /// + [Category(DevicesCategory)] + [TypeConverter(typeof(SingleDeviceFactoryConverter))] + [Description("Specifies the configuration for the heartbeat device in the headstage-64.")] + public ConfigurePersistentHeartbeat Heartbeat { get; set; } = new ConfigurePersistentHeartbeat { BeatsPerSecond = 10 }; + /// /// Gets or sets the port. /// @@ -110,6 +121,7 @@ public PortName Port TS4231.DeviceAddress = offset + 2; ElectricalStimulator.DeviceAddress = offset + 3; OpticalStimulator.DeviceAddress = offset + 4; + Heartbeat.DeviceAddress = offset + 5; } } @@ -148,10 +160,16 @@ internal override IEnumerable GetDevices() yield return TS4231; yield return ElectricalStimulator; yield return OpticalStimulator; + yield return Heartbeat; } class ConfigureHeadstage64PortController : ConfigurePortController { + public ConfigureHeadstage64PortController() + : base(typeof(Headstage64PortController)) + { + } + protected override bool ConfigurePortVoltageOverride(DeviceContext device, double voltage) { // NB: Wait for 1 second to discharge the headstage in the case that they have e.g. just @@ -202,5 +220,20 @@ protected override bool ConfigurePortVoltage(DeviceContext device, out double vo return CheckLinkState(device); } } + + [EquivalentDataSource(typeof(PortController))] + internal static class Headstage64PortController + { + public const int ID = PortController.ID; + public const uint MinimumVersion = PortController.MinimumVersion; + + internal class NameConverter : DeviceNameConverter + { + public NameConverter() + : base(typeof(Headstage64PortController)) + { + } + } + } } } diff --git a/OpenEphys.Onix1/ConfigureHeadstage64ElectricalStimulator.cs b/OpenEphys.Onix1/ConfigureHeadstage64ElectricalStimulator.cs index 6f5caae3..8ac4a9b0 100644 --- a/OpenEphys.Onix1/ConfigureHeadstage64ElectricalStimulator.cs +++ b/OpenEphys.Onix1/ConfigureHeadstage64ElectricalStimulator.cs @@ -30,8 +30,6 @@ public class ConfigureHeadstage64ElectricalStimulator : SingleDeviceFactory readonly BehaviorSubject burstPulseCount = new(0); readonly BehaviorSubject interBurstInterval = new(0); readonly BehaviorSubject trainBurstCount = new(0); - readonly BehaviorSubject triggerDelay = new(0); - readonly BehaviorSubject powerEnable = new(false); /// /// Initializes a new instance of the class. @@ -50,9 +48,7 @@ public ConfigureHeadstage64ElectricalStimulator(ConfigureHeadstage64ElectricalSt DeviceName = electricalStimulator.DeviceName; DeviceAddress = electricalStimulator.DeviceAddress; Enable = electricalStimulator.Enable; - StimEnable = electricalStimulator.StimEnable; - PowerEnable = electricalStimulator.PowerEnable; - TriggerDelay = electricalStimulator.TriggerDelay; + Arm = electricalStimulator.Arm; PhaseOneCurrent = electricalStimulator.PhaseOneCurrent; InterPhaseCurrent = electricalStimulator.InterPhaseCurrent; PhaseTwoCurrent = electricalStimulator.PhaseTwoCurrent; @@ -77,42 +73,20 @@ public ConfigureHeadstage64ElectricalStimulator(ConfigureHeadstage64ElectricalSt public bool Enable { get; set; } /// - /// Gets or sets the device enable state. + /// Gets or sets the device arm state. /// /// - /// If set to true, then the electrical stimulator circuit will respect triggers. If set to false, triggers will be ignored. + /// If set to true, then the electrical stimulator's ±15V power supplies will be turned on and the + /// electrical stimulator circuit will respect triggers. If set to false, the power supplies will be + /// shut down and triggers will be ignored. It takes ~10 milliseconds for the power supplies to to + /// stabilize. /// [Description("Specifies whether the electrical stimulator will respect triggers.")] [Category(AcquisitionCategory)] - public bool StimEnable { get; set; } = true; - - /// - /// Gets or sets the electrical stimulator's power state. - /// - /// - /// If set to true, then the electrical stimulator's ±15V power supplies will be turned on. If set to false, - /// they will be turned off. It may be desirable to power down the electrical stimulator's power supplies outside - /// of stimulation windows to reduce power consumption and electrical noise. This property must be set to true - /// in order for electrical stimuli to be delivered properly. It takes ~10 milliseconds for these supplies to stabilize. - /// - [Description("Stimulator power on/off.")] - [Category(AcquisitionCategory)] - public bool PowerEnable - { - get => powerEnable.Value; - set => powerEnable.OnNext(value); - } - - /// - /// Gets or sets a delay from receiving a trigger to the start of stimulus sequence application in μsec. - /// - [Description("A delay from receiving a trigger to the start of stimulus sequence application (uSec).")] - [Range(0, uint.MaxValue)] - [Category(AcquisitionCategory)] - public uint TriggerDelay + public bool Arm { - get => triggerDelay.Value; - set => triggerDelay.OnNext(value); + get => stimEnable.Value; + set => stimEnable.OnNext(value); } static double ClampCurrent(double value) @@ -275,14 +249,13 @@ public override IObservable Process(IObservable source return new CompositeDisposable( stimEnable.SubscribeSafe(observer, value => - device.WriteRegister(Headstage64ElectricalStimulator.STIMENABLE, value ? 1u : 0u)), + device.WriteRegister(Headstage64ElectricalStimulator.STIMENABLE, value ? 3u : 0u)), phaseOneCurrent.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.CURRENT1, Headstage64ElectricalStimulator.MicroampsToCode(value))), - interPhaseCurrent.SubscribeSafe(observer, value => - device.WriteRegister(Headstage64ElectricalStimulator.RESTCURR, Headstage64ElectricalStimulator.MicroampsToCode(value))), + interPhaseCurrent.SubscribeSafe(observer, value => + device.WriteRegister(Headstage64ElectricalStimulator.RESTCURRENT, Headstage64ElectricalStimulator.MicroampsToCode(value))), phaseTwoCurrent.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.CURRENT2, Headstage64ElectricalStimulator.MicroampsToCode(value))), - triggerDelay.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.TRAINDELAY, value)), phaseOneDuration.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.PULSEDUR1, value)), interPhaseInterval.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.INTERPHASEINTERVAL, value)), phaseTwoDuration.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.PULSEDUR2, value)), @@ -290,7 +263,6 @@ public override IObservable Process(IObservable source interBurstInterval.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.INTERBURSTINTERVAL, value)), burstPulseCount.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.BURSTCOUNT, value)), trainBurstCount.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.TRAINCOUNT, value)), - powerEnable.SubscribeSafe(observer, value => device.WriteRegister(Headstage64ElectricalStimulator.POWERON, value ? 1u : 0u)), DeviceManager.RegisterDevice(deviceName, device, DeviceType)); }); } @@ -307,23 +279,19 @@ static class Headstage64ElectricalStimulator // managed registers public const uint ENABLE = 0; // Enable stimulus report stream - public const uint BIPHASIC = 1; // Biphasic pulse (0 = monophasic, 1 = biphasic; NB: currently ignored) - public const uint CURRENT1 = 2; // Phase 1 current - public const uint CURRENT2 = 3; // Phase 2 current - public const uint PULSEDUR1 = 4; // Phase 1 duration, 1 microsecond steps - public const uint INTERPHASEINTERVAL = 5; // Inter-phase interval, 10 microsecond steps - public const uint PULSEDUR2 = 6; // Phase 2 duration, 1 microsecond steps - public const uint INTERPULSEINTERVAL = 7; // Inter-pulse interval, 10 microsecond steps - public const uint BURSTCOUNT = 8; // Burst duration, number of pulses in burst - public const uint INTERBURSTINTERVAL = 9; // Inter-burst interval, microseconds - public const uint TRAINCOUNT = 10; // Pulse train duration, number of bursts in train - public const uint TRAINDELAY = 11; // Pulse train delay, microseconds - public const uint TRIGGER = 12; // Trigger stimulation (1 = deliver) - public const uint POWERON = 13; // Control estim sub-circuit power (0 = off, 1 = on) - public const uint STIMENABLE = 14; // If 0 then stimulation triggers will be ignored, otherwise they will be applied - public const uint RESTCURR = 15; // Resting current between pulse phases - public const uint RESET = 16; // Reset all parameters to default - public const uint REZ = 17; // Internal DAC resolution in bits + public const uint CURRENT1 = 1; // Phase 1 current + public const uint CURRENT2 = 2; // Phase 2 current + public const uint PULSEDUR1 = 3; // Phase 1 duration, 1 microsecond steps + public const uint INTERPHASEINTERVAL = 4; // Inter-phase interval, 10 microsecond steps + public const uint PULSEDUR2 = 5; // Phase 2 duration, 1 microsecond steps + public const uint INTERPULSEINTERVAL = 6; // Inter-pulse interval, 10 microsecond steps + public const uint BURSTCOUNT = 7; // Burst duration, number of pulses in burst + public const uint INTERBURSTINTERVAL = 8; // Inter-burst interval, microseconds + public const uint TRAINCOUNT = 9; // Pulse train duration, number of bursts in train + public const uint TRIGGER = 10; // Trigger stimulation (1 = deliver) + public const uint STIMENABLE = 11; // If 0 then stimulation triggers will be ignored, otherwise they will be applied + public const uint RESTCURRENT = 12; // Resting current between pulse phases + public const uint REZ = 13; // Internal DAC resolution in bits internal static uint MicroampsToCode(double currentuA) { diff --git a/OpenEphys.Onix1/ConfigureHeadstage64OpticalStimulator.cs b/OpenEphys.Onix1/ConfigureHeadstage64OpticalStimulator.cs index 0bd34413..2b2723df 100644 --- a/OpenEphys.Onix1/ConfigureHeadstage64OpticalStimulator.cs +++ b/OpenEphys.Onix1/ConfigureHeadstage64OpticalStimulator.cs @@ -19,16 +19,16 @@ namespace OpenEphys.Onix1 [Editor("OpenEphys.Onix1.Design.Headstage64OpticalStimulatorComponentEditor, OpenEphys.Onix1.Design", typeof(ComponentEditor))] public class ConfigureHeadstage64OpticalStimulator : SingleDeviceFactory { - readonly BehaviorSubject stimEnable = new(true); - readonly BehaviorSubject maxCurrent = new(100); - readonly BehaviorSubject channelOneCurrent = new(100); + readonly BehaviorSubject stimEnable = new(false); + readonly BehaviorSubject enableIndicationLed = new(false); + readonly BehaviorSubject maxCurrent = new(0); + readonly BehaviorSubject channelOneCurrent = new(0); readonly BehaviorSubject channelTwoCurrent = new(0); - readonly BehaviorSubject pulseDuration = new(5); - readonly BehaviorSubject pulsesPerSecond = new(50); - readonly BehaviorSubject pulsesPerBurst = new(20); + readonly BehaviorSubject pulseDuration = new(0); + readonly BehaviorSubject pulsesPerSecond = new(0); + readonly BehaviorSubject pulsesPerBurst = new(0); readonly BehaviorSubject interBurstInterval = new(0); - readonly BehaviorSubject burstsPerTrain = new(1); - readonly BehaviorSubject delay = new(0); + readonly BehaviorSubject burstsPerTrain = new(0); /// /// Initializes a new instance of the class. @@ -47,8 +47,7 @@ public ConfigureHeadstage64OpticalStimulator(ConfigureHeadstage64OpticalStimulat DeviceName = opticalStimulator.DeviceName; DeviceAddress = opticalStimulator.DeviceAddress; Enable = opticalStimulator.Enable; - StimEnable = opticalStimulator.StimEnable; - Delay = opticalStimulator.Delay; + Arm = opticalStimulator.Arm; MaxCurrent = opticalStimulator.MaxCurrent; ChannelOneCurrent = opticalStimulator.ChannelOneCurrent; ChannelTwoCurrent = opticalStimulator.ChannelTwoCurrent; @@ -71,31 +70,31 @@ public ConfigureHeadstage64OpticalStimulator(ConfigureHeadstage64OpticalStimulat public bool Enable { get; set; } /// - /// Gets or sets the device enable state. + /// Gets or sets the indication LED enable state. /// /// - /// If set to true, then the optical stimulator circuit will respect triggers. If set to false, triggers will be ignored. + /// If set to true, the headstage's indication LED will turn on. When set to false, it will turn off. /// - [Description("Specifies whether the optical stimulator will respect triggers.")] + [Description("Specifies the state of the headstage indication LED")] [Category(AcquisitionCategory)] - public bool StimEnable + public bool EnableIndicationLed { - get => stimEnable.Value; - set => stimEnable.OnNext(value); + get => enableIndicationLed.Value; + set => enableIndicationLed.OnNext(value); } /// - /// Gets or sets a delay from receiving a trigger to the start of stimulus sequence application in msec. + /// Gets or sets the device arm state. /// - [Description("A delay from receiving a trigger to the start of stimulus sequence application (msec).")] - [Editor(DesignTypes.NumericUpDownEditor, DesignTypes.UITypeEditor)] - [Range(Headstage64OpticalStimulator.MinDelay, Headstage64OpticalStimulator.MaxDelay)] - [Precision(3, 1)] + /// + /// If set to true, then the optical stimulator circuit will respect triggers. If set to false, triggers will be ignored. + /// + [Description("Specifies whether the optical stimulator will respect triggers.")] [Category(AcquisitionCategory)] - public double Delay + public bool Arm { - get => delay.Value; - set => delay.OnNext(Clamp(value, Headstage64OpticalStimulator.MinDelay, Headstage64OpticalStimulator.MaxDelay)); + get => stimEnable.Value; + set => stimEnable.OnNext(value); } /// @@ -165,27 +164,27 @@ public double ChannelTwoCurrent /// [Description("The duration of each pulse (msec).")] [Editor(DesignTypes.NumericUpDownEditor, DesignTypes.UITypeEditor)] - [Range(Headstage64OpticalStimulator.MinPulseDuration, Headstage64OpticalStimulator.MaxPulseDuration)] + [Range(Headstage64OpticalStimulator.MinPulseDurationMilliseconds, Headstage64OpticalStimulator.MaxPulseDurationMilliseconds)] [Precision(3, 1)] [Category(AcquisitionCategory)] public double PulseDuration { get => pulseDuration.Value; - set => pulseDuration.OnNext(Clamp(value, Headstage64OpticalStimulator.MinPulseDuration, Headstage64OpticalStimulator.MaxPulseDuration)); + set => pulseDuration.OnNext(Clamp(value, Headstage64OpticalStimulator.MinPulseDurationMilliseconds, Headstage64OpticalStimulator.MaxPulseDurationMilliseconds)); } /// - /// Gets or sets the pulse period within a burst in msec. + /// Gets or sets the pulse frequency within a burst in Hz. /// - [Description("The pulse period within a burst (msec).")] + [Description("The pulse frequency within a burst (Hz).")] [Editor(DesignTypes.NumericUpDownEditor, DesignTypes.UITypeEditor)] - [Range(Headstage64OpticalStimulator.MinPulsePeriod, Headstage64OpticalStimulator.MaxPulsePeriod)] + [Range(Headstage64OpticalStimulator.MinPulseFrequencyHz, Headstage64OpticalStimulator.MaxPulseFrequencyHz)] [Precision(3, 1)] [Category(AcquisitionCategory)] public double PulsesPerSecond { get => pulsesPerSecond.Value; - set => pulsesPerSecond.OnNext(Clamp(value, Headstage64OpticalStimulator.MinPulsePeriod, Headstage64OpticalStimulator.MaxPulsePeriod)); + set => pulsesPerSecond.OnNext(Clamp(value, Headstage64OpticalStimulator.MinPulseFrequencyHz, Headstage64OpticalStimulator.MaxPulseFrequencyHz)); } /// @@ -207,13 +206,13 @@ public uint PulsesPerBurst /// [Description("The duration of the inter-burst interval within a stimulus train (msec).")] [Editor(DesignTypes.NumericUpDownEditor, DesignTypes.UITypeEditor)] - [Range(Headstage64OpticalStimulator.MinInterBurstInterval, Headstage64OpticalStimulator.MaxInterBurstInterval)] + [Range(Headstage64OpticalStimulator.MinInterBurstIntervalMilliseconds, Headstage64OpticalStimulator.MaxInterBurstIntervalMilliseconds)] [Precision(3, 1)] [Category(AcquisitionCategory)] public double InterBurstInterval { get => interBurstInterval.Value; - set => interBurstInterval.OnNext(Clamp(value, Headstage64OpticalStimulator.MinInterBurstInterval, Headstage64OpticalStimulator.MaxInterBurstInterval)); + set => interBurstInterval.OnNext(Clamp(value, Headstage64OpticalStimulator.MinInterBurstIntervalMilliseconds, Headstage64OpticalStimulator.MaxInterBurstIntervalMilliseconds)); } /// @@ -295,9 +294,25 @@ static uint pulseFrequencyToRegister(double pulseHz, double pulseDuration) return pulsePeriod > pulseDuration ? (uint)(1000 * pulsePeriod) : (uint)(1000 * pulseDuration + 1); } + uint stimEnableValue = 0; + return new CompositeDisposable( + enableIndicationLed.SubscribeSafe(observer, value => + { + if (value) + stimEnableValue |= (1u << 8); + else + stimEnableValue &= ~(1u << 8); + device.WriteRegister(Headstage64OpticalStimulator.STIMENABLE, stimEnableValue); + }), stimEnable.SubscribeSafe(observer, value => - device.WriteRegister(Headstage64OpticalStimulator.STIMENABLE, value ? 1u : 0u)), + { + if (value) + stimEnableValue |= 1u; + else + stimEnableValue &= ~1u; + device.WriteRegister(Headstage64OpticalStimulator.STIMENABLE, stimEnableValue); + }), maxCurrent.SubscribeSafe(observer, value => device.WriteRegister(Headstage64OpticalStimulator.MAXCURRENT, Headstage64OpticalStimulator.MilliampsToPotSetting(value))), channelOneCurrent.SubscribeSafe(observer, value => @@ -320,8 +335,6 @@ static uint pulseFrequencyToRegister(double pulseHz, double pulseDuration) device.WriteRegister(Headstage64OpticalStimulator.IBI, (uint)(1000 * value))), burstsPerTrain.SubscribeSafe(observer, value => device.WriteRegister(Headstage64OpticalStimulator.TRAINCOUNT, value)), - delay.SubscribeSafe(observer, value => - device.WriteRegister(Headstage64OpticalStimulator.TRAINDELAY, (uint)(1000 * value))), DeviceManager.RegisterDevice(deviceName, device, DeviceType)); }); } @@ -336,24 +349,21 @@ static class Headstage64OpticalStimulator public const uint MinRheostatResistanceOhms = 590; public const uint PotResistanceOhms = 100_000; - public const double MinDelay = 0.0; - public const double MaxDelay = 1000.0; - - public const double MinCurrent = 0.0; - public const double MaxCurrent = 300.0; + public const double MinCurrent = 5.822; // NB: This is the lowest allowable maximum current + public const double MaxCurrent = 150.0; // NB: this is not the physical limit, but its a reasonable practical upper boundary public const double MinChannelPercentage = 0.0; public const double MaxChannelPercentage = 100.0; public const double ChannelPercentageStep = 12.5; - public const double MinPulseDuration = 0.001; - public const double MaxPulseDuration = 1000.0; + public const double MinPulseDurationMilliseconds = 0.001; + public const double MaxPulseDurationMilliseconds = 500.0; - public const double MinPulsePeriod = 0.01; - public const double MaxPulsePeriod = 10000.0; + public const double MinPulseFrequencyHz = 0.1; + public const double MaxPulseFrequencyHz = 10000.0; - public const double MinInterBurstInterval = 0.0; - public const double MaxInterBurstInterval = 10000.0; + public const double MinInterBurstIntervalMilliseconds = 0.0; + public const double MaxInterBurstIntervalMilliseconds = 10000.0; // managed registers public const uint ENABLE = 0; // Enable stimulus report stream @@ -364,13 +374,10 @@ static class Headstage64OpticalStimulator public const uint BURSTCOUNT = 5; // Number of pulses in burst public const uint IBI = 6; // Inter-burst interval, microseconds public const uint TRAINCOUNT = 7; // Number of bursts in train - public const uint TRAINDELAY = 8; // Stimulus start delay, microseconds - public const uint TRIGGER = 9; // Trigger stimulation (0 = off, 1 = deliver) - public const uint STIMENABLE = 10; // 1: enables the stimulator, 0: stimulator ignores triggers (so that a common trigger can be used) - public const uint RESTMASK = 11; // Bitmask determining the off state of the up to 32 current channels - public const uint RESET = 12; // None If 1, Reset all parameters to default (not implemented) - public const uint MINRHEOR = 13; // The series resistor between the potentiometer (rheostat) and RSET bin on the CAT4016 - public const uint POTRES = 14; // The resistance value of the potentiometer connected in rheostat config to RSET on CAT4016 + public const uint TRIGGER = 8; // Trigger stimulation (0 = off, 1 = deliver) + public const uint STIMENABLE = 9; // 1: enables the stimulator, 0: stimulator ignores triggers (so that a common trigger can be used) + public const uint MINRHEOR = 10; // The series resistor between the potentiometer (rheostat) and RSET bin on the CAT4016 + public const uint POTRES = 11; // The resistance value of the potentiometer connected in rheostat config to RSET on CAT4016 // NB: fit from Fig. 10 of CAT4016 datasheet // x = (y/a)^(1/b) @@ -385,7 +392,7 @@ internal static uint MilliampsToPotSetting(double currentMa) internal static double PotSettingToMilliamps(uint potSetting) { - var R = MinRheostatResistanceOhms + PotResistanceOhms * potSetting / 256; + var R = MinRheostatResistanceOhms + PotResistanceOhms * potSetting / 255; return 3.833e+05 * Math.Pow(R, -0.9632); } diff --git a/OpenEphys.Onix1/ConfigureHeadstageNeuropixelsV1e.cs b/OpenEphys.Onix1/ConfigureHeadstageNeuropixelsV1e.cs index 768c9d84..217a461d 100644 --- a/OpenEphys.Onix1/ConfigureHeadstageNeuropixelsV1e.cs +++ b/OpenEphys.Onix1/ConfigureHeadstageNeuropixelsV1e.cs @@ -105,6 +105,11 @@ internal override IEnumerable GetDevices() class ConfigureNeuropixelsV1ePortController : ConfigurePortController { + public ConfigureNeuropixelsV1ePortController() + : base(typeof(PortController)) + { + } + protected override bool ConfigurePortVoltage(DeviceContext device, out double voltage) { const double MinVoltage = 3.3; diff --git a/OpenEphys.Onix1/ConfigureHeadstageNeuropixelsV1f.cs b/OpenEphys.Onix1/ConfigureHeadstageNeuropixelsV1f.cs index 9fde2bc8..36623e29 100644 --- a/OpenEphys.Onix1/ConfigureHeadstageNeuropixelsV1f.cs +++ b/OpenEphys.Onix1/ConfigureHeadstageNeuropixelsV1f.cs @@ -135,6 +135,12 @@ internal override IEnumerable GetDevices() class ConfigureNeuropixels1fHeadstageLinkController : ConfigurePortController { + + public ConfigureNeuropixels1fHeadstageLinkController() + : base(typeof(PortController)) + { + } + protected override bool ConfigurePortVoltage(DeviceContext device, out double voltage) { const double MinVoltage = 5.0; diff --git a/OpenEphys.Onix1/ConfigureHeadstageNric1384.cs b/OpenEphys.Onix1/ConfigureHeadstageNric1384.cs index 85b7d838..10704f37 100644 --- a/OpenEphys.Onix1/ConfigureHeadstageNric1384.cs +++ b/OpenEphys.Onix1/ConfigureHeadstageNric1384.cs @@ -111,6 +111,12 @@ internal override IEnumerable GetDevices() class ConfigureHeadstageNric1384PortController : ConfigurePortController { + + public ConfigureHeadstageNric1384PortController() + : base(typeof(PortController)) + { + } + protected override bool ConfigurePortVoltage(DeviceContext device, out double voltage) { const double MinVoltage = 3.8; diff --git a/OpenEphys.Onix1/ConfigureHeadstageRhs2116.cs b/OpenEphys.Onix1/ConfigureHeadstageRhs2116.cs index 25c1df9d..48c892d1 100644 --- a/OpenEphys.Onix1/ConfigureHeadstageRhs2116.cs +++ b/OpenEphys.Onix1/ConfigureHeadstageRhs2116.cs @@ -116,6 +116,12 @@ internal override IEnumerable GetDevices() class ConfigureHeadstageRhs2116LinkController : ConfigurePortController { + + public ConfigureHeadstageRhs2116LinkController() + : base(typeof(PortController)) + { + } + protected override bool ConfigurePortVoltage(DeviceContext device, out double voltage) { const double MinVoltage = 3.3; diff --git a/OpenEphys.Onix1/ConfigureNeuropixelsV2ePortController.cs b/OpenEphys.Onix1/ConfigureNeuropixelsV2ePortController.cs index 96f2433c..9bc111cd 100644 --- a/OpenEphys.Onix1/ConfigureNeuropixelsV2ePortController.cs +++ b/OpenEphys.Onix1/ConfigureNeuropixelsV2ePortController.cs @@ -4,6 +4,12 @@ namespace OpenEphys.Onix1 { class ConfigureNeuropixelsV2ePortController : ConfigurePortController { + + public ConfigureNeuropixelsV2ePortController() + : base(typeof(PortController)) + { + } + protected override bool ConfigurePortVoltage(DeviceContext device, out double voltage) { const double MinVoltage = 3.3; diff --git a/OpenEphys.Onix1/ConfigurePersistentHeartbeat.cs b/OpenEphys.Onix1/ConfigurePersistentHeartbeat.cs index 9f6f4388..94f192aa 100644 --- a/OpenEphys.Onix1/ConfigurePersistentHeartbeat.cs +++ b/OpenEphys.Onix1/ConfigurePersistentHeartbeat.cs @@ -25,10 +25,21 @@ public ConfigurePersistentHeartbeat() { } + /// + /// Initializes a copy instance of the class with the given values. + /// + /// Existing configuration settings. + public ConfigurePersistentHeartbeat(ConfigurePersistentHeartbeat configureHeartbeat) + : this() + { + DeviceAddress = configureHeartbeat.DeviceAddress; + DeviceName = configureHeartbeat.DeviceName; + BeatsPerSecond = configureHeartbeat.BeatsPerSecond; + } + /// /// Gets or sets the rate at which beats are produced in Hz. /// - [Range(100, 10e6)] [Category(AcquisitionCategory)] [Description("Rate at which beats are produced (Hz).")] public uint BeatsPerSecond @@ -50,7 +61,6 @@ public uint BeatsPerSecond /// configure a persistent heartbeat device./> public override IObservable Process(IObservable source) { - //var enable = Enable; var deviceName = DeviceName; var deviceAddress = DeviceAddress; return source.ConfigureDevice((context, observer) => diff --git a/OpenEphys.Onix1/ConfigurePortController.cs b/OpenEphys.Onix1/ConfigurePortController.cs index 0bad98f8..97989289 100644 --- a/OpenEphys.Onix1/ConfigurePortController.cs +++ b/OpenEphys.Onix1/ConfigurePortController.cs @@ -8,8 +8,8 @@ namespace OpenEphys.Onix1 { internal abstract class ConfigurePortController : SingleDeviceFactory { - public ConfigurePortController() - : base(typeof(PortController)) + public ConfigurePortController(Type deviceType) + : base(deviceType) { } @@ -107,7 +107,7 @@ internal static class PortController public const uint MinimumVersion = 2; public const uint ENABLE = 0; // The LSB is used to enable or disable the device data stream - public const uint GPOSTATE = 1; // GPO output state (bits 31 downto 3: ignore. bits 2 downto 0: ‘1’ = high, ‘0’ = low) + public const uint GPOSTATE = 1; // Controls the GPO output state 32 bits: [X, X, ..., X, GPO 3, GPO 2, GPO 1] public const uint DESPWR = 2; // Set link deserializer PDB state, 0 = deserializer power off else on. Does not affect port voltage. public const uint PORTVOLTAGE = 3; // 10 * link voltage public const uint SAVEVOLTAGE = 4; // Save link voltage to non-volatile EEPROM if greater than 0. This voltage will be applied after POR. @@ -193,4 +193,28 @@ public enum PortName [Description("Port B")] PortB = 2 } + + /// + /// Specifies the state of a port controller's GPIO pins. + /// + /// + /// Pin 0 is inaccessible because it is used for issuing hardware resets. + /// + [Flags] + public enum PortControllerGpioState : byte + { + /// + /// Specifies that pin 1 is high. + /// + Pin1 = 0x1, + /// + /// Specifies that pin 2 is high. + /// + Pin2 = 0x2, + /// + /// Specifies that pin 3 is high. + /// + Pin3 = 0x4, + } + } diff --git a/OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs b/OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs index b2df4ec6..82ce4db8 100644 --- a/OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs +++ b/OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs @@ -117,6 +117,11 @@ class ConfigureUclaMiniscopeV4PortController : ConfigurePortController { internal ConfigureUclaMiniscopeV4Camera Camera; + public ConfigureUclaMiniscopeV4PortController() + : base(typeof(PortController)) + { + } + protected override bool ConfigurePortVoltage(DeviceContext device, out double voltage) { const double MinVoltage = 5.2; diff --git a/OpenEphys.Onix1/Headstage64ElectricalStimulatorTrigger.cs b/OpenEphys.Onix1/Headstage64ElectricalStimulatorTrigger.cs index 7cf0f497..f89fe072 100644 --- a/OpenEphys.Onix1/Headstage64ElectricalStimulatorTrigger.cs +++ b/OpenEphys.Onix1/Headstage64ElectricalStimulatorTrigger.cs @@ -8,7 +8,7 @@ namespace OpenEphys.Onix1 { /// - /// Controls a headstage-64 onboard electrical stimulus sequencer. + /// Trigger a headstage-64 onboard electrical stimulus sequencer. /// /// /// This data IO operator must be linked to an appropriate configuration, such as a [Description("Controls a headstage-64 onboard electrical stimulus sequencer.")] - public class Headstage64ElectricalStimulatorTrigger : Sink + public class Headstage64ElectricalStimulatorTrigger : Sink { /// [TypeConverter(typeof(Headstage64ElectricalStimulator.NameConverter))] @@ -27,20 +27,23 @@ public class Headstage64ElectricalStimulatorTrigger : Sink public string DeviceName { get; set; } /// - /// Start an electrical stimulus sequence. + /// Start an electrical stimulus sequence with an optional hardware delay. /// - /// A sequence of boolean values indicating the start of a stimulus sequence when true. - /// A sequence of boolean values that is identical to - public override IObservable Process(IObservable source) + /// A sequence of double values that serve as a combined stimulus trigger and + /// delay in microseconds. A value of 0 results in immediate stimulus delivery. A value of 100 results in + /// stimulus delivery following a 100 microsecond delay. Delays are implemented in hardware and are + /// exact. + /// A sequence of double values that is identical to + public override IObservable Process(IObservable source) { return DeviceManager.GetDevice(DeviceName).SelectMany( - deviceInfo => Observable.Create(observer => + deviceInfo => Observable.Create(observer => { var device = deviceInfo.GetDeviceContext(typeof(Headstage64ElectricalStimulator)); - var triggerObserver = Observer.Create( + var triggerObserver = Observer.Create( value => { - device.WriteRegister(Headstage64ElectricalStimulator.TRIGGER, value ? 1u : 0u); + device.WriteRegister(Headstage64ElectricalStimulator.TRIGGER, (uint)value << 8 | 0x1); observer.OnNext(value); }, observer.OnError, diff --git a/OpenEphys.Onix1/Headstage64GpoTrigger.cs b/OpenEphys.Onix1/Headstage64GpoTrigger.cs new file mode 100644 index 00000000..69c3efdc --- /dev/null +++ b/OpenEphys.Onix1/Headstage64GpoTrigger.cs @@ -0,0 +1,52 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Reactive.Linq; +using Bonsai; + +namespace OpenEphys.Onix1 +{ + /// + /// Trigger a headstage-64 stimulator using the port controller's general purpose output (GPO). + /// + /// + /// This data IO operator must be linked to an appropriate configuration, such as a , using a shared DeviceName. Each + /// headstage port has a GPO interface for sending digital signals to the headstage with low latency. This + /// operator uses a dedicated GPO line rather than a register write to trigger stimulation and therefore + /// has lower latencies than or . However, the trigger will be delivered to both of the + /// stimulators so care must be taken to ensure only the appropriate stimulator is armed when the trigger + /// is sent. + /// + [Description("Triggers a headstage-64 stimulator using the port controller's general purpose output (GPO)")] + public class Headstage64GpoTrigger : Sink + { + /// + [TypeConverter(typeof(ConfigureHeadstage64.Headstage64PortController.NameConverter))] + [Description(SingleDeviceFactory.DeviceNameDescription)] + [Category(DeviceFactory.ConfigurationCategory)] + public string DeviceName { get; set; } + + /// + /// Issue a stimulus trigger using the port controller's GPO. + /// + /// A sequence of boolean values indicating if a stimulus trigger should be + /// issued. + /// A sequence that is identical to . + public override IObservable Process(IObservable source) + { + return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo => + { + var device = deviceInfo.GetDeviceContext(typeof(ConfigureHeadstage64.Headstage64PortController)); + return source.Do(value => { + if (value) + { + device.WriteRegister(PortController.GPOSTATE, (byte)PortControllerGpioState.Pin1); + device.WriteRegister(PortController.GPOSTATE, 0); + } + }); + }); + } + } +} diff --git a/OpenEphys.Onix1/Headstage64OpticalStimulatorDataFrame.cs b/OpenEphys.Onix1/Headstage64OpticalStimulatorDataFrame.cs index 69b20f3b..2be79667 100644 --- a/OpenEphys.Onix1/Headstage64OpticalStimulatorDataFrame.cs +++ b/OpenEphys.Onix1/Headstage64OpticalStimulatorDataFrame.cs @@ -22,8 +22,6 @@ public unsafe Headstage64OpticalStimulatorDataFrame(oni.Frame frame) HubClock = payload->HubClock; Origin = (Headstage64StimulatorTriggerOrigin)(payload->DelayAndOrigin & 0x000F); Delay = (payload->DelayAndOrigin & 0xFFF0) >> 8; - ChannelOneRestCurrent = CodeToMilliamps(payload->MaxCurrent, (byte)(payload->RestMask & 0x00FF)); - ChannelTwoRestCurrent = CodeToMilliamps(payload->MaxCurrent, (byte)((payload->RestMask & 0xFF00) >> 8)); ChannelOneCurrent = CodeToMilliamps(payload->MaxCurrent, (byte)(payload->PulseMask & 0x00FF)); ChannelTwoCurrent = CodeToMilliamps(payload->MaxCurrent, (byte)((payload->PulseMask & 0xFF00) >> 8)); PulseDuration = payload->PulseDuration / 1e3; @@ -44,16 +42,6 @@ public unsafe Headstage64OpticalStimulatorDataFrame(oni.Frame frame) /// public uint Delay { get; } - /// - /// Gets the channel one rest current in milliamps. - /// - public double ChannelOneRestCurrent { get; } - - /// - /// Gets the channel two rest current in milliamps. - /// - public double ChannelTwoRestCurrent { get; } - /// /// Gets the channel one pulse current in milliamps. /// @@ -114,7 +102,6 @@ unsafe struct Headstage64OpticalStimulatorPayload { public ulong HubClock; public uint DelayAndOrigin; - public uint RestMask; public uint MaxCurrent; public uint PulseMask; public uint PulseDuration; diff --git a/OpenEphys.Onix1/Headstage64OpticalStimulatorTrigger.cs b/OpenEphys.Onix1/Headstage64OpticalStimulatorTrigger.cs index 6af266bb..cc2f5d55 100644 --- a/OpenEphys.Onix1/Headstage64OpticalStimulatorTrigger.cs +++ b/OpenEphys.Onix1/Headstage64OpticalStimulatorTrigger.cs @@ -8,7 +8,7 @@ namespace OpenEphys.Onix1 { /// - /// Controls a headstage-64 onboard optical stimulus sequencer. + /// Trigger a headstage-64 onboard optical stimulus sequencer. /// /// /// This data IO operator must be linked to an appropriate configuration, such as a [Description("Controls a headstage-64 onboard optical stimulus sequencer.")] - public class Headstage64OpticalStimulatorTrigger : Sink + public class Headstage64OpticalStimulatorTrigger : Sink { /// [TypeConverter(typeof(Headstage64OpticalStimulator.NameConverter))] @@ -26,20 +26,23 @@ public class Headstage64OpticalStimulatorTrigger : Sink public string DeviceName { get; set; } /// - /// Start an optical stimulus sequence. + /// Start an optical stimulus sequence with an optional hardware delay. /// - /// A sequence of boolean values indicating the start of a stimulus sequence when true. - /// A sequence of boolean values that is identical to - public override IObservable Process(IObservable source) + /// A sequence of double values that serve as a combined stimulus trigger and + /// delay in microseconds. For instance, a value of 0 results in immediate stimulus delivery, a value + /// of 100 results in stimulus delivery following a 100 microsecond delay, etc. Delays are implemented in + /// hardware and are exact. + /// A sequence of double values that is identical to + public override IObservable Process(IObservable source) { return DeviceManager.GetDevice(DeviceName).SelectMany( - deviceInfo => Observable.Create(observer => + deviceInfo => Observable.Create(observer => { var device = deviceInfo.GetDeviceContext(typeof(Headstage64OpticalStimulator)); - var triggerObserver = Observer.Create( + var triggerObserver = Observer.Create( value => { - device.WriteRegister(Headstage64OpticalStimulator.TRIGGER, value ? 1u : 0u); + device.WriteRegister(Headstage64OpticalStimulator.TRIGGER, (uint)value << 8 | 0x1); observer.OnNext(value); }, observer.OnError, diff --git a/OpenEphys.Onix1/Rhs2116StimulusTrigger.cs b/OpenEphys.Onix1/Rhs2116StimulusTrigger.cs index 4d7293a9..89d7c4ed 100644 --- a/OpenEphys.Onix1/Rhs2116StimulusTrigger.cs +++ b/OpenEphys.Onix1/Rhs2116StimulusTrigger.cs @@ -23,9 +23,9 @@ public class Rhs2116StimulusTrigger : Sink /// Start an electrical stimulus sequence with an optional hardware delay. /// /// A sequence of double values that serve as a combined stimulus trigger and - /// delay in microseconds. A value of 0 results in immediate stimulus delivery. A value of 100 results in - /// stimulus delivery following a 100 microsecond delay. Delays are implemented in hardware and are - /// exact. + /// delay in microseconds. For instance, a value of 0 results in immediate stimulus delivery, a value + /// of 100 results in stimulus delivery following a 100 microsecond delay, etc. Delays are implemented in + /// hardware and are exact. /// A sequence of double values that is identical to public override IObservable Process(IObservable source) {