diff --git a/OpenEphys.Onix1.Design/ChannelConfigurationDialog.cs b/OpenEphys.Onix1.Design/ChannelConfigurationDialog.cs index 09fb44a9..54fb7ae9 100644 --- a/OpenEphys.Onix1.Design/ChannelConfigurationDialog.cs +++ b/OpenEphys.Onix1.Design/ChannelConfigurationDialog.cs @@ -18,11 +18,21 @@ public abstract partial class ChannelConfigurationDialog : Form { internal event EventHandler OnResizeZedGraph; - internal ProbeGroup ProbeGroup; + ProbeGroup probeGroup; + + internal ProbeGroup ProbeGroup + { + get => probeGroup; + set + { + probeGroup = value; + SelectedContacts = new bool[probeGroup.NumberOfContacts]; + } + } internal readonly List ReferenceContacts = new(); - internal readonly bool[] SelectedContacts = null; + internal bool[] SelectedContacts { get; private set; } = null; /// /// Constructs the dialog window using the given probe group, and plots all contacts after loading. @@ -42,8 +52,6 @@ public ChannelConfigurationDialog(ProbeGroup probeGroup) ProbeGroup = probeGroup; } - SelectedContacts = new bool[ProbeGroup.NumberOfContacts]; - ReferenceContacts = new List(); zedGraphChannels.MouseDownEvent += MouseDownEvent; @@ -432,13 +440,25 @@ internal virtual bool OpenFile() where T : ProbeGroup return false; } - if (ProbeGroup.NumberOfContacts == newConfiguration.NumberOfContacts) + bool skipContactNumberMismatchCheck = false; + + if (ProbeGroup.Probes.First().Annotations.Name != newConfiguration.Probes.First().Annotations.Name) + { + var result = MessageBox.Show($"There is a mismatch between the current probe type ({ProbeGroup.Probes.First().Annotations.Name})" + + $" and the new probe type ({newConfiguration.Probes.First().Annotations.Name}). Continue loading?", "Probe Type Mismatch", MessageBoxButtons.YesNo); + + if (result == DialogResult.No) + return false; + + skipContactNumberMismatchCheck = true; // NB: If the probe names do not match, skip the check to see if the number of contacts match. + // Example: loading a Neuropixels single-shank 2.0 probe, but the current probe is a quad-shank 2.0 probe. + } + + if (skipContactNumberMismatchCheck || ProbeGroup.NumberOfContacts == newConfiguration.NumberOfContacts) { newConfiguration.Validate(); ProbeGroup = newConfiguration; - DrawProbeGroup(); - RefreshZedGraph(); return true; } @@ -1075,6 +1095,8 @@ private void MenuItemOpenFile(object sender, EventArgs e) if (OpenFile()) { DrawProbeGroup(); + ResetZoom(); + UpdateFontSize(); RefreshZedGraph(); } } @@ -1083,6 +1105,7 @@ private void MenuItemLoadDefaultConfig(object sender, EventArgs e) { LoadDefaultChannelLayout(); DrawProbeGroup(); + ResetZoom(); UpdateFontSize(); RefreshZedGraph(); } @@ -1342,8 +1365,8 @@ internal static bool HasContactAnnotations(ProbeGroup probeGroup) { foreach (var probe in probeGroup.Probes) { - if (probe.ContactAnnotations != null - && probe.ContactAnnotations.Annotations != null + if (probe.ContactAnnotations != null + && probe.ContactAnnotations.Annotations != null && probe.ContactAnnotations.Annotations.Length > 0) { return true; diff --git a/OpenEphys.Onix1.Design/NeuropixelsV2eChannelConfigurationDialog.cs b/OpenEphys.Onix1.Design/NeuropixelsV2eChannelConfigurationDialog.cs index b9fdceaa..28ba915e 100644 --- a/OpenEphys.Onix1.Design/NeuropixelsV2eChannelConfigurationDialog.cs +++ b/OpenEphys.Onix1.Design/NeuropixelsV2eChannelConfigurationDialog.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; @@ -17,16 +16,16 @@ public partial class NeuropixelsV2eChannelConfigurationDialog : ChannelConfigura internal event EventHandler OnFileLoad; /// - /// Public object that is manipulated by + /// Public object that is manipulated by /// . /// - public NeuropixelsV2QuadShankProbeConfiguration ProbeConfiguration; + public NeuropixelsV2ProbeConfiguration ProbeConfiguration; /// /// Initializes a new instance of . /// - /// A object holding the current configuration settings. - public NeuropixelsV2eChannelConfigurationDialog(NeuropixelsV2QuadShankProbeConfiguration probeConfiguration) + /// A object holding the current configuration settings. + public NeuropixelsV2eChannelConfigurationDialog(NeuropixelsV2ProbeConfiguration probeConfiguration) : base(probeConfiguration.ProbeGroup) { zedGraphChannels.ZoomButtons = MouseButtons.None; @@ -34,7 +33,7 @@ public NeuropixelsV2eChannelConfigurationDialog(NeuropixelsV2QuadShankProbeConfi zedGraphChannels.ZoomStepFraction = 0.5; - ProbeConfiguration = probeConfiguration; + ProbeConfiguration = new(probeConfiguration); ZoomInBoundaryX = 600; ZoomInBoundaryY = 600; @@ -46,13 +45,16 @@ public NeuropixelsV2eChannelConfigurationDialog(NeuropixelsV2QuadShankProbeConfi internal override ProbeGroup DefaultChannelLayout() { - return new NeuropixelsV2eProbeGroup(); + return new NeuropixelsV2eProbeGroup(ProbeConfiguration.ProbeType); } internal override void LoadDefaultChannelLayout() { - ProbeConfiguration = new(ProbeConfiguration.Probe, ProbeConfiguration.Reference); - ProbeGroup = ProbeConfiguration.ProbeGroup; + base.LoadDefaultChannelLayout(); + ProbeConfiguration = new((NeuropixelsV2eProbeGroup)ProbeGroup, + ProbeConfiguration.Probe, + ProbeConfiguration.ProbeType, + ProbeConfiguration.Reference); OnFileOpenHandler(); } @@ -61,8 +63,6 @@ internal override bool OpenFile() { if (base.OpenFile()) { - ProbeConfiguration = new((NeuropixelsV2eProbeGroup)ProbeGroup, ProbeConfiguration.Reference, ProbeConfiguration.Probe); - OnFileOpenHandler(); return true; @@ -173,7 +173,7 @@ internal override void DrawScale() const float scaleBarWidth = 1; - curve.Line.Width = scaleBarWidth; + curve.Line.Width = scaleBarWidth; curve.Label.IsVisible = false; curve.Symbol.IsVisible = false; curve.Tag = ScalePointsTag; @@ -197,7 +197,7 @@ internal override void HighlightEnabledContacts() var contactsToEnable = contactObjects.Where(c => { var tag = c.Tag as ContactTag; - var channel = NeuropixelsV2QuadShankElectrode.GetChannelNumber(tag.ContactIndex); + var channel = NeuropixelsV2Electrode.GetChannelNumber(tag.ContactIndex, ProbeConfiguration.ProbeType); return ProbeConfiguration.ChannelMap[channel].Index == tag.ContactIndex; }); @@ -227,7 +227,7 @@ internal override void UpdateContactLabels() textObjsToUpdate = textObjs.Where(c => { var tag = c.Tag as ContactTag; - var channel = NeuropixelsV2QuadShankElectrode.GetChannelNumber(tag.ContactIndex); + var channel = NeuropixelsV2Electrode.GetChannelNumber(tag.ContactIndex, ProbeConfiguration.ProbeType); return ProbeConfiguration.ChannelMap[channel].Index == tag.ContactIndex; }); @@ -242,7 +242,7 @@ internal override string ContactString(int deviceChannelIndex, int index) return index.ToString(); } - internal void EnableElectrodes(NeuropixelsV2QuadShankElectrode[] electrodes) + internal void EnableElectrodes(NeuropixelsV2Electrode[] electrodes) { ProbeConfiguration.SelectElectrodes(electrodes); } diff --git a/OpenEphys.Onix1.Design/NeuropixelsV2eDialog.cs b/OpenEphys.Onix1.Design/NeuropixelsV2eDialog.cs index b80302d9..134a2192 100644 --- a/OpenEphys.Onix1.Design/NeuropixelsV2eDialog.cs +++ b/OpenEphys.Onix1.Design/NeuropixelsV2eDialog.cs @@ -26,8 +26,11 @@ public NeuropixelsV2eDialog(IConfigureNeuropixelsV2 configureNode) InitializeComponent(); Shown += FormShown; + bool isBeta = false; + if (configureNode is ConfigureNeuropixelsV2eBeta configureV2eBeta) { + isBeta = true; ConfigureNode = new ConfigureNeuropixelsV2eBeta(configureV2eBeta); Text = Text.Replace("NeuropixelsV2e ", "NeuropixelsV2eBeta "); } @@ -38,7 +41,7 @@ public NeuropixelsV2eDialog(IConfigureNeuropixelsV2 configureNode) ProbeConfigurations = new List { - new(ConfigureNode.ProbeConfigurationA, ConfigureNode.GainCalibrationFileA, ConfigureNode.InvertPolarity) + new(ConfigureNode.ProbeConfigurationA, ConfigureNode.GainCalibrationFileA, ConfigureNode.InvertPolarity, isBeta) { TopLevel = false, FormBorderStyle = FormBorderStyle.None, @@ -46,7 +49,7 @@ public NeuropixelsV2eDialog(IConfigureNeuropixelsV2 configureNode) Parent = this, Tag = NeuropixelsV2Probe.ProbeA }, - new(ConfigureNode.ProbeConfigurationB, ConfigureNode.GainCalibrationFileB, ConfigureNode.InvertPolarity) + new(ConfigureNode.ProbeConfigurationB, ConfigureNode.GainCalibrationFileB, ConfigureNode.InvertPolarity, isBeta) { TopLevel = false, FormBorderStyle = FormBorderStyle.None, diff --git a/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.Designer.cs b/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.Designer.cs index d5856629..6b94c199 100644 --- a/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.Designer.cs +++ b/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.Designer.cs @@ -36,6 +36,7 @@ private void InitializeComponent() System.Windows.Forms.Label labelPresets; System.Windows.Forms.Label label1; System.Windows.Forms.Label invertPolarity; + System.Windows.Forms.Label labelProbeType; System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(NeuropixelsV2eProbeConfigurationDialog)); this.toolStripLabelGainCalibrationSN = new System.Windows.Forms.ToolStripStatusLabel(); this.menuStrip = new System.Windows.Forms.MenuStrip(); @@ -50,6 +51,7 @@ private void InitializeComponent() this.trackBarProbePosition = new System.Windows.Forms.TrackBar(); this.panelChannelOptions = new System.Windows.Forms.Panel(); this.checkBoxInvertPolarity = new System.Windows.Forms.CheckBox(); + this.checkBoxInvertPolarity = new System.Windows.Forms.CheckBox(); this.textBoxGainCorrection = new System.Windows.Forms.TextBox(); this.textBoxProbeCalibrationFile = new System.Windows.Forms.TextBox(); this.comboBoxReference = new System.Windows.Forms.ComboBox(); @@ -60,6 +62,7 @@ private void InitializeComponent() this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.toolStripGainCalSN = new System.Windows.Forms.ToolStripStatusLabel(); + this.comboBoxProbeType = new System.Windows.Forms.ComboBox(); label6 = new System.Windows.Forms.Label(); label7 = new System.Windows.Forms.Label(); probeCalibrationFile = new System.Windows.Forms.Label(); @@ -67,6 +70,7 @@ private void InitializeComponent() labelPresets = new System.Windows.Forms.Label(); label1 = new System.Windows.Forms.Label(); invertPolarity = new System.Windows.Forms.Label(); + labelProbeType = new System.Windows.Forms.Label(); this.menuStrip.SuspendLayout(); this.panelProbe.SuspendLayout(); this.panelTrackBar.SuspendLayout(); @@ -82,8 +86,10 @@ private void InitializeComponent() label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); label6.AutoSize = true; label6.Location = new System.Drawing.Point(0, 542); + label6.Location = new System.Drawing.Point(0, 542); label6.Name = "label6"; label6.Size = new System.Drawing.Size(39, 16); + label6.Size = new System.Drawing.Size(39, 16); label6.TabIndex = 28; label6.Text = "0 mm"; // @@ -94,6 +100,7 @@ private void InitializeComponent() label7.Location = new System.Drawing.Point(0, 0); label7.Name = "label7"; label7.Size = new System.Drawing.Size(46, 16); + label7.Size = new System.Drawing.Size(46, 16); label7.TabIndex = 29; label7.Text = "10 mm"; label7.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; @@ -103,26 +110,31 @@ private void InitializeComponent() probeCalibrationFile.AutoSize = true; probeCalibrationFile.Location = new System.Drawing.Point(15, 11); probeCalibrationFile.MaximumSize = new System.Drawing.Size(177, 36); + probeCalibrationFile.Location = new System.Drawing.Point(15, 11); + probeCalibrationFile.MaximumSize = new System.Drawing.Size(177, 36); probeCalibrationFile.Name = "probeCalibrationFile"; probeCalibrationFile.Size = new System.Drawing.Size(139, 16); + probeCalibrationFile.Size = new System.Drawing.Size(139, 16); probeCalibrationFile.TabIndex = 32; probeCalibrationFile.Text = "Probe Calibration File:"; // // Reference // Reference.AutoSize = true; - Reference.Location = new System.Drawing.Point(15, 114); + Reference.Location = new System.Drawing.Point(15, 142); Reference.Name = "Reference"; Reference.Size = new System.Drawing.Size(73, 16); + Reference.Size = new System.Drawing.Size(73, 16); Reference.TabIndex = 30; Reference.Text = "Reference:"; // // labelPresets // labelPresets.AutoSize = true; - labelPresets.Location = new System.Drawing.Point(15, 146); + labelPresets.Location = new System.Drawing.Point(15, 170); labelPresets.Name = "labelPresets"; labelPresets.Size = new System.Drawing.Size(59, 32); + labelPresets.Size = new System.Drawing.Size(59, 32); labelPresets.TabIndex = 23; labelPresets.Text = "Channel \nPresets:"; // @@ -130,15 +142,17 @@ private void InitializeComponent() // label1.AutoSize = true; label1.Location = new System.Drawing.Point(15, 63); + label1.Location = new System.Drawing.Point(15, 63); label1.Name = "label1"; label1.Size = new System.Drawing.Size(71, 32); + label1.Size = new System.Drawing.Size(71, 32); label1.TabIndex = 35; label1.Text = "Gain\r\nCorrection:"; // // invertPolarity // invertPolarity.AutoSize = true; - invertPolarity.Location = new System.Drawing.Point(15, 187); + invertPolarity.Location = new System.Drawing.Point(15, 213); invertPolarity.Name = "invertPolarity"; invertPolarity.Size = new System.Drawing.Size(55, 32); invertPolarity.TabIndex = 44; @@ -162,6 +176,8 @@ private void InitializeComponent() this.menuStrip.Name = "menuStrip"; this.menuStrip.Padding = new System.Windows.Forms.Padding(5, 1, 0, 1); this.menuStrip.Size = new System.Drawing.Size(1112, 24); + this.menuStrip.Padding = new System.Windows.Forms.Padding(5, 1, 0, 1); + this.menuStrip.Size = new System.Drawing.Size(1112, 24); this.menuStrip.TabIndex = 0; this.menuStrip.Text = "menuStripNeuropixelsV2e"; // @@ -175,11 +191,11 @@ private void InitializeComponent() // this.buttonEnableContacts.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.buttonEnableContacts.Location = new System.Drawing.Point(15, 226); + this.buttonEnableContacts.Location = new System.Drawing.Point(15, 252); this.buttonEnableContacts.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.buttonEnableContacts.Name = "buttonEnableContacts"; this.buttonEnableContacts.Size = new System.Drawing.Size(243, 44); - this.buttonEnableContacts.TabIndex = 4; + this.buttonEnableContacts.TabIndex = 20; this.buttonEnableContacts.Text = "Enable Selected Electrodes"; this.toolTip.SetToolTip(this.buttonEnableContacts, "Click and drag to select electrodes in the probe view. \r\nPress this button to ena" + "ble the selected electrodes. \r\nNot all electrode combinations are possible."); @@ -190,11 +206,11 @@ private void InitializeComponent() // this.buttonClearSelections.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.buttonClearSelections.Location = new System.Drawing.Point(15, 276); + this.buttonClearSelections.Location = new System.Drawing.Point(15, 302); this.buttonClearSelections.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.buttonClearSelections.Name = "buttonClearSelections"; this.buttonClearSelections.Size = new System.Drawing.Size(243, 44); - this.buttonClearSelections.TabIndex = 5; + this.buttonClearSelections.TabIndex = 19; this.buttonClearSelections.Text = "Clear Electrode Selection"; this.toolTip.SetToolTip(this.buttonClearSelections, "Deselect all electrodes in the probe view. \r\nNote that this does not disable elec" + "trodes, but simply deselects them."); @@ -205,11 +221,11 @@ private void InitializeComponent() // this.buttonResetZoom.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.buttonResetZoom.Location = new System.Drawing.Point(15, 325); + this.buttonResetZoom.Location = new System.Drawing.Point(15, 351); this.buttonResetZoom.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.buttonResetZoom.Name = "buttonResetZoom"; this.buttonResetZoom.Size = new System.Drawing.Size(243, 44); - this.buttonResetZoom.TabIndex = 6; + this.buttonResetZoom.TabIndex = 4; this.buttonResetZoom.Text = "Reset Zoom"; this.toolTip.SetToolTip(this.buttonResetZoom, "Reset the zoom in the probe view so that the probe is zoomed out and centered."); this.buttonResetZoom.UseVisualStyleBackColor = true; @@ -220,9 +236,11 @@ private void InitializeComponent() this.buttonChooseCalibrationFile.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.buttonChooseCalibrationFile.Location = new System.Drawing.Point(220, 30); this.buttonChooseCalibrationFile.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.buttonChooseCalibrationFile.Location = new System.Drawing.Point(220, 30); + this.buttonChooseCalibrationFile.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.buttonChooseCalibrationFile.Name = "buttonChooseCalibrationFile"; this.buttonChooseCalibrationFile.Size = new System.Drawing.Size(37, 25); - this.buttonChooseCalibrationFile.TabIndex = 0; + this.buttonChooseCalibrationFile.TabIndex = 34; this.buttonChooseCalibrationFile.Text = "..."; this.toolTip.SetToolTip(this.buttonChooseCalibrationFile, "Browse for a gain calibration file."); this.buttonChooseCalibrationFile.UseVisualStyleBackColor = true; @@ -235,8 +253,11 @@ private void InitializeComponent() this.panelProbe.Dock = System.Windows.Forms.DockStyle.Fill; this.panelProbe.Location = new System.Drawing.Point(3, 2); this.panelProbe.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.panelProbe.Location = new System.Drawing.Point(3, 2); + this.panelProbe.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.panelProbe.Name = "panelProbe"; this.panelProbe.Size = new System.Drawing.Size(828, 557); + this.panelProbe.Size = new System.Drawing.Size(828, 557); this.panelProbe.TabIndex = 1; // // panelTrackBar @@ -247,8 +268,11 @@ private void InitializeComponent() this.panelTrackBar.Controls.Add(this.trackBarProbePosition); this.panelTrackBar.Location = new System.Drawing.Point(775, 0); this.panelTrackBar.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.panelTrackBar.Location = new System.Drawing.Point(775, 0); + this.panelTrackBar.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.panelTrackBar.Name = "panelTrackBar"; this.panelTrackBar.Size = new System.Drawing.Size(49, 559); + this.panelTrackBar.Size = new System.Drawing.Size(49, 559); this.panelTrackBar.TabIndex = 30; // // trackBarProbePosition @@ -258,10 +282,13 @@ private void InitializeComponent() this.trackBarProbePosition.AutoSize = false; this.trackBarProbePosition.Location = new System.Drawing.Point(-8, 11); this.trackBarProbePosition.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.trackBarProbePosition.Location = new System.Drawing.Point(-8, 11); + this.trackBarProbePosition.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.trackBarProbePosition.Maximum = 100; this.trackBarProbePosition.Name = "trackBarProbePosition"; this.trackBarProbePosition.Orientation = System.Windows.Forms.Orientation.Vertical; this.trackBarProbePosition.Size = new System.Drawing.Size(49, 535); + this.trackBarProbePosition.Size = new System.Drawing.Size(49, 535); this.trackBarProbePosition.TabIndex = 22; this.trackBarProbePosition.TabStop = false; this.trackBarProbePosition.TickFrequency = 2; @@ -274,6 +301,8 @@ private void InitializeComponent() this.panelChannelOptions.AutoSize = true; this.panelChannelOptions.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.panelChannelOptions.BackColor = System.Drawing.SystemColors.ControlLightLight; + this.panelChannelOptions.Controls.Add(this.comboBoxProbeType); + this.panelChannelOptions.Controls.Add(labelProbeType); this.panelChannelOptions.Controls.Add(this.checkBoxInvertPolarity); this.panelChannelOptions.Controls.Add(invertPolarity); this.panelChannelOptions.Controls.Add(this.textBoxGainCorrection); @@ -291,18 +320,21 @@ private void InitializeComponent() this.panelChannelOptions.Dock = System.Windows.Forms.DockStyle.Fill; this.panelChannelOptions.Location = new System.Drawing.Point(837, 2); this.panelChannelOptions.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.panelChannelOptions.Location = new System.Drawing.Point(837, 2); + this.panelChannelOptions.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.panelChannelOptions.Name = "panelChannelOptions"; this.panelChannelOptions.Size = new System.Drawing.Size(272, 557); + this.panelChannelOptions.Size = new System.Drawing.Size(272, 557); this.panelChannelOptions.TabIndex = 1; // // checkBoxInvertPolarity // this.checkBoxInvertPolarity.AutoSize = true; - this.checkBoxInvertPolarity.Location = new System.Drawing.Point(107, 193); + this.checkBoxInvertPolarity.Location = new System.Drawing.Point(107, 219); this.checkBoxInvertPolarity.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.checkBoxInvertPolarity.Name = "checkBoxInvertPolarity"; this.checkBoxInvertPolarity.Size = new System.Drawing.Size(77, 20); - this.checkBoxInvertPolarity.TabIndex = 3; + this.checkBoxInvertPolarity.TabIndex = 45; this.checkBoxInvertPolarity.Text = "Enabled"; this.checkBoxInvertPolarity.UseVisualStyleBackColor = true; // @@ -312,9 +344,12 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.textBoxGainCorrection.Location = new System.Drawing.Point(107, 68); this.textBoxGainCorrection.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.textBoxGainCorrection.Location = new System.Drawing.Point(107, 68); + this.textBoxGainCorrection.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.textBoxGainCorrection.Name = "textBoxGainCorrection"; this.textBoxGainCorrection.ReadOnly = true; this.textBoxGainCorrection.Size = new System.Drawing.Size(151, 22); + this.textBoxGainCorrection.Size = new System.Drawing.Size(151, 22); this.textBoxGainCorrection.TabIndex = 36; this.textBoxGainCorrection.TabStop = false; // @@ -324,8 +359,9 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.textBoxProbeCalibrationFile.Location = new System.Drawing.Point(15, 30); this.textBoxProbeCalibrationFile.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.textBoxProbeCalibrationFile.Location = new System.Drawing.Point(15, 30); + this.textBoxProbeCalibrationFile.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.textBoxProbeCalibrationFile.Name = "textBoxProbeCalibrationFile"; - this.textBoxProbeCalibrationFile.ReadOnly = true; this.textBoxProbeCalibrationFile.Size = new System.Drawing.Size(198, 22); this.textBoxProbeCalibrationFile.TabIndex = 33; this.textBoxProbeCalibrationFile.TabStop = false; @@ -337,11 +373,11 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.comboBoxReference.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxReference.FormattingEnabled = true; - this.comboBoxReference.Location = new System.Drawing.Point(107, 110); + this.comboBoxReference.Location = new System.Drawing.Point(107, 138); this.comboBoxReference.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.comboBoxReference.Name = "comboBoxReference"; this.comboBoxReference.Size = new System.Drawing.Size(151, 24); - this.comboBoxReference.TabIndex = 1; + this.comboBoxReference.TabIndex = 31; // // comboBoxChannelPresets // @@ -349,11 +385,11 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.comboBoxChannelPresets.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxChannelPresets.FormattingEnabled = true; - this.comboBoxChannelPresets.Location = new System.Drawing.Point(107, 150); + this.comboBoxChannelPresets.Location = new System.Drawing.Point(107, 174); this.comboBoxChannelPresets.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.comboBoxChannelPresets.Name = "comboBoxChannelPresets"; this.comboBoxChannelPresets.Size = new System.Drawing.Size(151, 24); - this.comboBoxChannelPresets.TabIndex = 2; + this.comboBoxChannelPresets.TabIndex = 24; // // buttonCancel // @@ -362,8 +398,11 @@ private void InitializeComponent() this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.buttonCancel.Location = new System.Drawing.Point(990, 2); this.buttonCancel.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.buttonCancel.Location = new System.Drawing.Point(990, 2); + this.buttonCancel.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.buttonCancel.Name = "buttonCancel"; this.buttonCancel.Size = new System.Drawing.Size(111, 34); + this.buttonCancel.Size = new System.Drawing.Size(111, 34); this.buttonCancel.TabIndex = 1; this.buttonCancel.Text = "Cancel"; this.buttonCancel.UseVisualStyleBackColor = true; @@ -375,8 +414,11 @@ private void InitializeComponent() this.buttonOkay.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonOkay.Location = new System.Drawing.Point(873, 2); this.buttonOkay.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.buttonOkay.Location = new System.Drawing.Point(873, 2); + this.buttonOkay.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.buttonOkay.Name = "buttonOkay"; this.buttonOkay.Size = new System.Drawing.Size(111, 34); + this.buttonOkay.Size = new System.Drawing.Size(111, 34); this.buttonOkay.TabIndex = 0; this.buttonOkay.Text = "OK"; this.buttonOkay.UseVisualStyleBackColor = true; @@ -394,13 +436,14 @@ private void InitializeComponent() this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 24); this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 2; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 46F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel1.Size = new System.Drawing.Size(1112, 607); - this.tableLayoutPanel1.TabIndex = 0; + this.tableLayoutPanel1.TabIndex = 3; // // flowLayoutPanel1 // @@ -411,8 +454,11 @@ private void InitializeComponent() this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(4, 565); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.flowLayoutPanel1.Location = new System.Drawing.Point(4, 565); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(1104, 38); + this.flowLayoutPanel1.Size = new System.Drawing.Size(1104, 38); this.flowLayoutPanel1.TabIndex = 2; // // statusStrip1 @@ -422,9 +468,12 @@ private void InitializeComponent() this.toolStripLabelGainCalibrationSN, this.toolStripGainCalSN}); this.statusStrip1.Location = new System.Drawing.Point(0, 631); + this.statusStrip1.Location = new System.Drawing.Point(0, 631); this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 13, 0); this.statusStrip1.Size = new System.Drawing.Size(1112, 25); + this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 13, 0); + this.statusStrip1.Size = new System.Drawing.Size(1112, 25); this.statusStrip1.TabIndex = 3; this.statusStrip1.Text = "statusStrip1"; // @@ -435,11 +484,34 @@ private void InitializeComponent() this.toolStripGainCalSN.Text = "No file selected."; this.toolStripGainCalSN.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // + // comboBoxProbeType + // + this.comboBoxProbeType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.comboBoxProbeType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxProbeType.FormattingEnabled = true; + this.comboBoxProbeType.Location = new System.Drawing.Point(107, 102); + this.comboBoxProbeType.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.comboBoxProbeType.Name = "comboBoxProbeType"; + this.comboBoxProbeType.Size = new System.Drawing.Size(151, 24); + this.comboBoxProbeType.TabIndex = 47; + // + // labelProbeType + // + labelProbeType.AutoSize = true; + labelProbeType.Location = new System.Drawing.Point(15, 106); + labelProbeType.Name = "labelProbeType"; + labelProbeType.Size = new System.Drawing.Size(82, 16); + labelProbeType.TabIndex = 46; + labelProbeType.Text = "Probe Type:"; + // // NeuropixelsV2eProbeConfigurationDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1112, 656); + this.ClientSize = new System.Drawing.Size(1112, 656); this.Controls.Add(this.tableLayoutPanel1); this.Controls.Add(this.statusStrip1); this.Controls.Add(this.menuStrip); @@ -447,6 +519,7 @@ private void InitializeComponent() this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MainMenuStrip = this.menuStrip; this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.Name = "NeuropixelsV2eProbeConfigurationDialog"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "NeuropixelsV2e Probe Configuration"; @@ -493,5 +566,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripStatusLabel toolStripGainCalSN; private System.Windows.Forms.ToolStripStatusLabel toolStripLabelGainCalibrationSN; private System.Windows.Forms.CheckBox checkBoxInvertPolarity; + private System.Windows.Forms.ComboBox comboBoxProbeType; } } diff --git a/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.cs b/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.cs index 8f497b44..bc6f482d 100644 --- a/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.cs +++ b/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.cs @@ -1,21 +1,25 @@ using System; -using System.Linq; -using System.Windows.Forms; +using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; using System.IO; +using System.Linq; +using System.Windows.Forms; namespace OpenEphys.Onix1.Design { /// - /// Partial class to create a GUI for . + /// Partial class to create a GUI for . /// public partial class NeuropixelsV2eProbeConfigurationDialog : Form { + const int BankDStartIndex = 896; + readonly NeuropixelsV2eChannelConfigurationDialog ChannelConfiguration; internal event EventHandler InvertPolarityChanged; - private enum ChannelPreset + enum QuadShankChannelPreset { Shank0BankA, Shank0BankB, @@ -49,31 +53,52 @@ private enum ChannelPreset None } + enum SingleShankChannelPreset + { + BankA, + BankB, + BankC, + BankD, + None + } + /// - /// Public object that is manipulated by - /// . + /// Public object that is manipulated by + /// . /// - public NeuropixelsV2QuadShankProbeConfiguration ProbeConfiguration { get; set; } + public NeuropixelsV2ProbeConfiguration ProbeConfiguration + { + get => ChannelConfiguration.ProbeConfiguration; + } + + readonly Dictionary probeConfigurations; /// public bool InvertPolarity { get; set; } /// - /// Initializes a new instance of . + /// Initializes a new instance of . /// - /// A object holding the current configuration settings. + /// A object holding the current configuration settings. /// String containing the path to the calibration file for this probe. /// Boolean denoting whether or not to invert the polarity of neural data. - public NeuropixelsV2eProbeConfigurationDialog(NeuropixelsV2QuadShankProbeConfiguration configuration, string calibrationFile, bool invertPolarity) + /// Boolean indicating if this is a beta probe or not. + public NeuropixelsV2eProbeConfigurationDialog(NeuropixelsV2ProbeConfiguration configuration, string calibrationFile, bool invertPolarity, bool isBeta) { InitializeComponent(); Shown += FormShown; - ProbeConfiguration = new(configuration); - textBoxProbeCalibrationFile.Text = calibrationFile; - ChannelConfiguration = new(ProbeConfiguration) + probeConfigurations = new() + { + [NeuropixelsV2ProbeType.SingleShank] = new(configuration.Probe, NeuropixelsV2ProbeType.SingleShank, configuration.Reference), + [NeuropixelsV2ProbeType.QuadShank] = new(configuration.Probe, NeuropixelsV2ProbeType.QuadShank, configuration.Reference) + }; + + probeConfigurations[configuration.ProbeType].SelectElectrodes(configuration.ChannelMap); + + ChannelConfiguration = new(probeConfigurations[configuration.ProbeType]) { TopLevel = false, FormBorderStyle = FormBorderStyle.None, @@ -89,21 +114,35 @@ public NeuropixelsV2eProbeConfigurationDialog(NeuropixelsV2QuadShankProbeConfigu ChannelConfiguration.OnZoom += UpdateTrackBarLocation; ChannelConfiguration.OnFileLoad += OnFileLoadEvent; - comboBoxReference.DataSource = Enum.GetValues(typeof(NeuropixelsV2QuadShankReference)); - comboBoxReference.SelectedItem = ProbeConfiguration.Reference; - comboBoxReference.SelectedIndexChanged += SelectedReferenceChanged; + comboBoxProbeType.DataSource = Enum.GetValues(typeof(NeuropixelsV2ProbeType)); + comboBoxProbeType.SelectedItem = ProbeConfiguration.ProbeType; + + if (isBeta) + comboBoxProbeType.Enabled = false; + else + comboBoxProbeType.SelectedIndexChanged += SelectedProbeTypeChanged; - comboBoxChannelPresets.DataSource = Enum.GetValues(typeof(ChannelPreset)); + comboBoxChannelPresets.DataSource = GetComboBoxChannelPresets(ProbeConfiguration.ProbeType); comboBoxChannelPresets.SelectedIndexChanged += SelectedChannelPresetChanged; checkBoxInvertPolarity.Checked = InvertPolarity; checkBoxInvertPolarity.CheckedChanged += InvertPolarityIndexChanged; - CheckForExistingChannelPreset(); - CheckStatus(); Text += ": " + ProbeConfiguration.Probe.ToString(); + + UpdateProbeConfiguration(); + } + + static Array GetComboBoxChannelPresets(NeuropixelsV2ProbeType probeType) + { + return probeType switch + { + NeuropixelsV2ProbeType.SingleShank => Enum.GetValues(typeof(SingleShankChannelPreset)), + NeuropixelsV2ProbeType.QuadShank => Enum.GetValues(typeof(QuadShankChannelPreset)), + _ => throw new InvalidEnumArgumentException(nameof(NeuropixelsV2ProbeType)) + }; } private void InvertPolarityIndexChanged(object sender, EventArgs e) @@ -150,393 +189,504 @@ private void ResizeTrackBar(object sender, EventArgs e) } } + void UpdateProbeConfiguration() + { + var probeType = (NeuropixelsV2ProbeType)comboBoxProbeType.SelectedItem; + + ChannelConfiguration.ProbeConfiguration = probeConfigurations[probeType]; + ChannelConfiguration.ProbeGroup = ProbeConfiguration.ProbeGroup; + + ChannelConfiguration.DrawProbeGroup(); + ChannelConfiguration.ResetZoom(); + ChannelConfiguration.RefreshZedGraph(); + + comboBoxChannelPresets.SelectedIndexChanged -= SelectedChannelPresetChanged; // NB: Temporarily detach handler so the loaded electrode configuration is respected + comboBoxChannelPresets.DataSource = GetComboBoxChannelPresets(ProbeConfiguration.ProbeType); + comboBoxChannelPresets.SelectedIndexChanged += SelectedChannelPresetChanged; + + comboBoxReference.SelectedIndexChanged -= SelectedReferenceChanged; + comboBoxReference.DataSource = NeuropixelsV2ProbeConfiguration.FilterNeuropixelsV2ShankReference(ProbeConfiguration.ProbeType); + comboBoxReference.SelectedItem = ProbeConfiguration.Reference; + comboBoxReference.SelectedIndexChanged += SelectedReferenceChanged; + + CheckForExistingChannelPreset(); + } + + void SelectedProbeTypeChanged(object sender, EventArgs e) + { + UpdateProbeConfiguration(); + } + private void SelectedReferenceChanged(object sender, EventArgs e) { - ProbeConfiguration.Reference = (NeuropixelsV2QuadShankReference)((ComboBox)sender).SelectedItem; + ProbeConfiguration.Reference = (NeuropixelsV2ShankReference)((ComboBox)sender).SelectedItem; } private void SelectedChannelPresetChanged(object sender, EventArgs e) { - var channelPreset = (ChannelPreset)((ComboBox)sender).SelectedItem; + switch (ProbeConfiguration.ProbeType) + { + case NeuropixelsV2ProbeType.SingleShank: + SetSingleShankChannelPreset((SingleShankChannelPreset)((ComboBox)sender).SelectedItem); + break; + case NeuropixelsV2ProbeType.QuadShank: + SetQuadShankChannelPreset((QuadShankChannelPreset)((ComboBox)sender).SelectedItem); + break; + default: + throw new NotSupportedException($"Unknown probe configuration found."); + } + + ChannelConfiguration.HighlightEnabledContacts(); + ChannelConfiguration.HighlightSelectedContacts(); + ChannelConfiguration.UpdateContactLabels(); + ChannelConfiguration.RefreshZedGraph(); + } + + void SetSingleShankChannelPreset(SingleShankChannelPreset preset) + { + var electrodes = NeuropixelsV2eProbeGroup.ToElectrodes(ProbeConfiguration.ProbeGroup, NeuropixelsV2ProbeType.SingleShank); - if (channelPreset != ChannelPreset.None) + switch (preset) { - SetChannelPreset(channelPreset); + case SingleShankChannelPreset.BankA: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A).ToArray()); + break; + + case SingleShankChannelPreset.BankB: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B).ToArray()); + break; + + case SingleShankChannelPreset.BankC: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.D || + (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)).ToArray()); + break; + + case SingleShankChannelPreset.BankD: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.D || + (e.Bank == NeuropixelsV2Bank.C && e.Index >= 896)).ToArray()); + break; } + } - private void SetChannelPreset(ChannelPreset preset) + void SetQuadShankChannelPreset(QuadShankChannelPreset preset) { - var probeConfiguration = ChannelConfiguration.ProbeConfiguration; - var electrodes = NeuropixelsV2eProbeGroup.ToElectrodes(ChannelConfiguration.ProbeConfiguration.ProbeGroup); + var electrodes = NeuropixelsV2eProbeGroup.ToElectrodes(ProbeConfiguration.ProbeGroup, NeuropixelsV2ProbeType.QuadShank); switch (preset) { - case ChannelPreset.Shank0BankA: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.A && + case QuadShankChannelPreset.Shank0BankA: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 0).ToArray()); break; - case ChannelPreset.Shank0BankB: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.B && + case QuadShankChannelPreset.Shank0BankB: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 0).ToArray()); break; - case ChannelPreset.Shank0BankC: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.C && + case QuadShankChannelPreset.Shank0BankC: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 0).ToArray()); break; - case ChannelPreset.Shank0BankD: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Bank == NeuropixelsV2QuadShankBank.D || - (e.Bank == NeuropixelsV2QuadShankBank.C && e.Index >= 896)) && + case QuadShankChannelPreset.Shank0BankD: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Bank == NeuropixelsV2Bank.D || + (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)) && e.Shank == 0).ToArray()); break; - case ChannelPreset.Shank1BankA: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.A && + case QuadShankChannelPreset.Shank1BankA: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 1).ToArray()); break; - case ChannelPreset.Shank1BankB: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.B && + case QuadShankChannelPreset.Shank1BankB: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 1).ToArray()); break; - case ChannelPreset.Shank1BankC: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.C && - e.Shank == 1).ToArray()); + case QuadShankChannelPreset.Shank1BankC: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.C && + e.Shank == 1).ToArray()); break; - case ChannelPreset.Shank1BankD: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Bank == NeuropixelsV2QuadShankBank.D || - (e.Bank == NeuropixelsV2QuadShankBank.C && e.Index >= 896)) && + case QuadShankChannelPreset.Shank1BankD: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Bank == NeuropixelsV2Bank.D || + (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)) && e.Shank == 1).ToArray()); break; - case ChannelPreset.Shank2BankA: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.A && + case QuadShankChannelPreset.Shank2BankA: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 2).ToArray()); break; - case ChannelPreset.Shank2BankB: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.B && + case QuadShankChannelPreset.Shank2BankB: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 2).ToArray()); break; - case ChannelPreset.Shank2BankC: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.C && - e.Shank == 2).ToArray()); + case QuadShankChannelPreset.Shank2BankC: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.C && + e.Shank == 2).ToArray()); break; - case ChannelPreset.Shank2BankD: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Bank == NeuropixelsV2QuadShankBank.D || - (e.Bank == NeuropixelsV2QuadShankBank.C && e.Index >= 896)) && + case QuadShankChannelPreset.Shank2BankD: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Bank == NeuropixelsV2Bank.D || + (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)) && e.Shank == 2).ToArray()); break; - case ChannelPreset.Shank3BankA: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.A && + case QuadShankChannelPreset.Shank3BankA: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 3).ToArray()); break; - case ChannelPreset.Shank3BankB: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.B && + case QuadShankChannelPreset.Shank3BankB: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 3).ToArray()); break; - case ChannelPreset.Shank3BankC: - probeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2QuadShankBank.C && - e.Shank == 3).ToArray()); + case QuadShankChannelPreset.Shank3BankC: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => e.Bank == NeuropixelsV2Bank.C && + e.Shank == 3).ToArray()); break; - case ChannelPreset.Shank3BankD: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Bank == NeuropixelsV2QuadShankBank.D || - (e.Bank == NeuropixelsV2QuadShankBank.C && e.Index >= 896)) && + case QuadShankChannelPreset.Shank3BankD: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Bank == NeuropixelsV2Bank.D || + (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)) && e.Shank == 3).ToArray()); break; - case ChannelPreset.AllShanks0_95: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95) || + case QuadShankChannelPreset.AllShanks0_95: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95)).ToArray()); break; - case ChannelPreset.AllShanks96_191: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 96 && e.IntraShankElectrodeIndex <= 191) || + case QuadShankChannelPreset.AllShanks96_191: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 96 && e.IntraShankElectrodeIndex <= 191) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 96 && e.IntraShankElectrodeIndex <= 191) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 96 && e.IntraShankElectrodeIndex <= 191) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 96 && e.IntraShankElectrodeIndex <= 191)).ToArray()); break; - case ChannelPreset.AllShanks192_287: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287) || + case QuadShankChannelPreset.AllShanks192_287: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287)).ToArray()); break; - case ChannelPreset.AllShanks288_383: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383) || + case QuadShankChannelPreset.AllShanks288_383: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383)).ToArray()); break; - case ChannelPreset.AllShanks384_479: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 384 && e.IntraShankElectrodeIndex <= 479) || + case QuadShankChannelPreset.AllShanks384_479: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 384 && e.IntraShankElectrodeIndex <= 479) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 384 && e.IntraShankElectrodeIndex <= 479) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 384 && e.IntraShankElectrodeIndex <= 479) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 384 && e.IntraShankElectrodeIndex <= 479)).ToArray()); break; - case ChannelPreset.AllShanks480_575: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575) || + case QuadShankChannelPreset.AllShanks480_575: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575)).ToArray()); break; - case ChannelPreset.AllShanks576_671: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671) || + case QuadShankChannelPreset.AllShanks576_671: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671)).ToArray()); break; - case ChannelPreset.AllShanks672_767: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767) || + case QuadShankChannelPreset.AllShanks672_767: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767)).ToArray()); break; - case ChannelPreset.AllShanks768_863: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863) || + case QuadShankChannelPreset.AllShanks768_863: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863)).ToArray()); break; - case ChannelPreset.AllShanks864_959: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959) || + case QuadShankChannelPreset.AllShanks864_959: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959)).ToArray()); break; - case ChannelPreset.AllShanks960_1055: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055) || + case QuadShankChannelPreset.AllShanks960_1055: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055)).ToArray()); break; - case ChannelPreset.AllShanks1056_1151: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151) || + case QuadShankChannelPreset.AllShanks1056_1151: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151)).ToArray()); break; - case ChannelPreset.AllShanks1152_1247: - probeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247) || + case QuadShankChannelPreset.AllShanks1152_1247: + ProbeConfiguration.SelectElectrodes(electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247)).ToArray()); break; } + } - ChannelConfiguration.HighlightEnabledContacts(); - ChannelConfiguration.HighlightSelectedContacts(); - ChannelConfiguration.UpdateContactLabels(); - ChannelConfiguration.RefreshZedGraph(); + void CheckForExistingChannelPreset() + { + switch (ProbeConfiguration.ProbeType) + { + case NeuropixelsV2ProbeType.SingleShank: + CheckSingleShankForChannelPreset(ProbeConfiguration.ChannelMap); break; + case NeuropixelsV2ProbeType.QuadShank: + CheckQuadShankForChannelPreset(ProbeConfiguration.ChannelMap); break; + default: + throw new NotSupportedException($"Unknown probe configuration found."); + } } - private void CheckForExistingChannelPreset() + void CheckSingleShankForChannelPreset(NeuropixelsV2Electrode[] channelMap) { - var channelMap = ChannelConfiguration.ProbeConfiguration.ChannelMap; + if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.A)) + { + comboBoxChannelPresets.SelectedItem = SingleShankChannelPreset.BankA; + } + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.B)) + { + comboBoxChannelPresets.SelectedItem = SingleShankChannelPreset.BankB; + } + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.D || + (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex))) + { + comboBoxChannelPresets.SelectedItem = SingleShankChannelPreset.BankD; + } + else + { + comboBoxChannelPresets.SelectedItem = SingleShankChannelPreset.None; + } + } - if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.A && + void CheckQuadShankForChannelPreset(NeuropixelsV2Electrode[] channelMap) + { + if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 0)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank0BankA; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank0BankA; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.B && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 0)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank0BankB; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank0BankB; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.C && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 0)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank0BankC; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank0BankC; } - else if (channelMap.All(e => (e.Bank == NeuropixelsV2QuadShankBank.D || - (e.Bank == NeuropixelsV2QuadShankBank.C && e.Index >= 896)) && - e.Shank == 0)) + else if (channelMap.All(e => (e.Bank == NeuropixelsV2Bank.D + || (e.Bank == NeuropixelsV2Bank.C + && e.IntraShankElectrodeIndex >= BankDStartIndex)) + && e.Shank == 0)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank0BankD; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank0BankD; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.A && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 1)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank1BankA; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank1BankA; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.B && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 1)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank1BankB; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank1BankB; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.C && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 1)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank1BankC; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank1BankC; } - else if (channelMap.All(e => (e.Bank == NeuropixelsV2QuadShankBank.D || - (e.Bank == NeuropixelsV2QuadShankBank.C && e.Index >= 896)) && - e.Shank == 1)) + else if (channelMap.All(e => (e.Bank == NeuropixelsV2Bank.D + || (e.Bank == NeuropixelsV2Bank.C + && e.IntraShankElectrodeIndex >= BankDStartIndex)) + && e.Shank == 1)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank1BankD; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank1BankD; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.A && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 2)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank2BankA; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank2BankA; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.B && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 2)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank2BankB; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank2BankB; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.C && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 2)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank2BankC; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank2BankC; } - else if (channelMap.All(e => (e.Bank == NeuropixelsV2QuadShankBank.D || - (e.Bank == NeuropixelsV2QuadShankBank.C && e.Index >= 896)) && - e.Shank == 2)) + else if (channelMap.All(e => (e.Bank == NeuropixelsV2Bank.D + || (e.Bank == NeuropixelsV2Bank.C + && e.IntraShankElectrodeIndex >= BankDStartIndex)) + && e.Shank == 2)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank2BankD; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank2BankD; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.A && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 3)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank3BankA; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank3BankA; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.B && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 3)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank3BankB; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank3BankB; } - else if (channelMap.All(e => e.Bank == NeuropixelsV2QuadShankBank.C && + else if (channelMap.All(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 3)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank3BankC; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank3BankC; } - else if (channelMap.All(e => (e.Bank == NeuropixelsV2QuadShankBank.D || - (e.Bank == NeuropixelsV2QuadShankBank.C && e.Index >= 896)) && - e.Shank == 3)) + else if (channelMap.All(e => (e.Bank == NeuropixelsV2Bank.D + || (e.Bank == NeuropixelsV2Bank.C + && e.IntraShankElectrodeIndex >= BankDStartIndex)) + && e.Shank == 3)) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.Shank3BankD; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.Shank3BankD; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 0 && e.IntraShankElectrodeIndex <= 95))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks0_95; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks0_95; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 192 && e.IntraShankElectrodeIndex <= 287))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks192_287; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks192_287; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 288 && e.IntraShankElectrodeIndex <= 383))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks288_383; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks288_383; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 394 && e.IntraShankElectrodeIndex <= 479) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 394 && e.IntraShankElectrodeIndex <= 479) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 394 && e.IntraShankElectrodeIndex <= 479) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 394 && e.IntraShankElectrodeIndex <= 479))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks384_479; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks384_479; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 480 && e.IntraShankElectrodeIndex <= 575))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks480_575; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks480_575; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 576 && e.IntraShankElectrodeIndex <= 671))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks576_671; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks576_671; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 672 && e.IntraShankElectrodeIndex <= 767))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks672_767; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks672_767; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 768 && e.IntraShankElectrodeIndex <= 863))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks768_863; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks768_863; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 864 && e.IntraShankElectrodeIndex <= 959))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks864_959; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks864_959; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 960 && e.IntraShankElectrodeIndex <= 1055))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks960_1055; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks960_1055; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 1056 && e.IntraShankElectrodeIndex <= 1151))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks1056_1151; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks1056_1151; } else if (channelMap.All(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247) || (e.Shank == 1 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247) || (e.Shank == 2 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247) || (e.Shank == 3 && e.IntraShankElectrodeIndex >= 1152 && e.IntraShankElectrodeIndex <= 1247))) { - comboBoxChannelPresets.SelectedItem = ChannelPreset.AllShanks1152_1247; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.AllShanks1152_1247; } else { - comboBoxChannelPresets.SelectedItem = ChannelPreset.None; + comboBoxChannelPresets.SelectedItem = QuadShankChannelPreset.None; } } private void OnFileLoadEvent(object sender, EventArgs e) { - // NB: Ensure that the newly loaded ProbeConfiguration in the ChannelConfigurationDialog is reflected here. - ProbeConfiguration = ChannelConfiguration.ProbeConfiguration; - CheckForExistingChannelPreset(); + NeuropixelsV2ProbeType probeType; + + try + { + probeType = NeuropixelsV2eProbeGroup.GetProbeTypeFromProbeName(ChannelConfiguration.ProbeGroup.Probes.First().Annotations.Name); + } + catch (ArgumentException ex) + { + MessageBox.Show(ex.Message); + return; + } + + probeConfigurations[probeType] = new((NeuropixelsV2eProbeGroup)ChannelConfiguration.ProbeGroup, + probeConfigurations[probeType].Probe, + probeConfigurations[probeType].ProbeType, + probeConfigurations[probeType].Reference); + + comboBoxProbeType.SelectedItem = probeType; + UpdateProbeConfiguration(); } private void FileTextChanged(object sender, EventArgs e) @@ -622,9 +772,10 @@ internal void EnableContacts_Click(object sender, EventArgs e) private void EnableSelectedContacts() { - var selected = NeuropixelsV2eProbeGroup.ToElectrodes(ChannelConfiguration.ProbeConfiguration.ProbeGroup) - .Where((e, ind) => ChannelConfiguration.SelectedContacts[ind]) - .ToArray(); + var selected = NeuropixelsV2eProbeGroup + .ToElectrodes(ProbeConfiguration.ProbeGroup, ProbeConfiguration.ProbeType) + .Where((e, ind) => ChannelConfiguration.SelectedContacts[ind]) + .ToArray(); ChannelConfiguration.EnableElectrodes(selected); diff --git a/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.resx b/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.resx index 5d721569..d6c55e8d 100644 --- a/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.resx +++ b/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.resx @@ -135,13 +135,16 @@ False + + False + 17, 17 274, 17 - + False diff --git a/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationEditor.cs b/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationEditor.cs index db034bf6..8db2a3d4 100644 --- a/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationEditor.cs +++ b/OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationEditor.cs @@ -8,7 +8,7 @@ namespace OpenEphys.Onix1.Design { /// - /// Class that opens a new dialog for a . + /// Class that opens a new dialog for a . /// public class NeuropixelsV2eProbeConfigurationEditor : UITypeEditor { @@ -27,15 +27,17 @@ public override object EditValue(ITypeDescriptorContext context, IServiceProvide var editorState = (IWorkflowEditorState)provider.GetService(typeof(IWorkflowEditorState)); if (editorService != null && editorState != null && !editorState.WorkflowRunning && - value is NeuropixelsV2QuadShankProbeConfiguration configuration) + value is NeuropixelsV2ProbeConfiguration configuration) { var instance = (IConfigureNeuropixelsV2)context.Instance; var calibrationFile = configuration.Probe == NeuropixelsV2Probe.ProbeA ? instance.GainCalibrationFileA : instance.GainCalibrationFileB; - using var editorDialog = new NeuropixelsV2eProbeConfigurationDialog(configuration, calibrationFile, instance.InvertPolarity); + bool isBeta = instance is ConfigureNeuropixelsV2eBeta; - if (instance is ConfigureNeuropixelsV2eBeta) + using var editorDialog = new NeuropixelsV2eProbeConfigurationDialog(configuration, calibrationFile, instance.InvertPolarity, isBeta); + + if (isBeta) { editorDialog.Text = editorDialog.Text.Replace("NeuropixelsV2e ", "NeuropixelsV2eBeta "); } diff --git a/OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs b/OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs index b89eb484..d4f945bb 100644 --- a/OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs +++ b/OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs @@ -74,7 +74,7 @@ public ConfigureNeuropixelsV2e(ConfigureNeuropixelsV2e configureNode) [Category(ConfigurationCategory)] [Description("Probe A electrode configuration.")] [Editor("OpenEphys.Onix1.Design.NeuropixelsV2eProbeConfigurationEditor, OpenEphys.Onix1.Design", typeof(UITypeEditor))] - public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationA { get; set; } = new(NeuropixelsV2Probe.ProbeA); + public NeuropixelsV2ProbeConfiguration ProbeConfigurationA { get; set; } = new(NeuropixelsV2Probe.ProbeA, NeuropixelsV2ProbeType.QuadShank, NeuropixelsV2ShankReference.External); /// /// @@ -105,7 +105,7 @@ public ConfigureNeuropixelsV2e(ConfigureNeuropixelsV2e configureNode) [Category(ConfigurationCategory)] [Description("Probe B electrode configuration.")] [Editor("OpenEphys.Onix1.Design.NeuropixelsV2eProbeConfigurationEditor, OpenEphys.Onix1.Design", typeof(UITypeEditor))] - public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationB { get; set; } = new(NeuropixelsV2Probe.ProbeB); + public NeuropixelsV2ProbeConfiguration ProbeConfigurationB { get; set; } = new(NeuropixelsV2Probe.ProbeB, NeuropixelsV2ProbeType.QuadShank, NeuropixelsV2ShankReference.External); /// /// diff --git a/OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs b/OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs index 9ab77e01..622e15d0 100644 --- a/OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs +++ b/OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs @@ -89,7 +89,7 @@ public ConfigureNeuropixelsV2eBeta(ConfigureNeuropixelsV2eBeta configureNode) [Category(ConfigurationCategory)] [Description("Probe A electrode configuration.")] [Editor("OpenEphys.Onix1.Design.NeuropixelsV2eProbeConfigurationEditor, OpenEphys.Onix1.Design", typeof(UITypeEditor))] - public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationA { get; set; } = new(NeuropixelsV2Probe.ProbeA); + public NeuropixelsV2ProbeConfiguration ProbeConfigurationA { get; set; } = new(NeuropixelsV2Probe.ProbeA, NeuropixelsV2ProbeType.QuadShank, NeuropixelsV2ShankReference.External); /// /// @@ -120,7 +120,7 @@ public ConfigureNeuropixelsV2eBeta(ConfigureNeuropixelsV2eBeta configureNode) [Category(ConfigurationCategory)] [Description("Probe B electrode configuration.")] [Editor("OpenEphys.Onix1.Design.NeuropixelsV2eProbeConfigurationEditor, OpenEphys.Onix1.Design", typeof(UITypeEditor))] - public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationB { get; set; } = new(NeuropixelsV2Probe.ProbeB); + public NeuropixelsV2ProbeConfiguration ProbeConfigurationB { get; set; } = new(NeuropixelsV2Probe.ProbeB, NeuropixelsV2ProbeType.QuadShank, NeuropixelsV2ShankReference.External); /// /// @@ -200,7 +200,7 @@ public override IObservable Process(IObservable source // configure probe A streaming if (probeAMetadata.ProbeSerialNumber != null) { - if (ProbeConfigurationA.Reference == NeuropixelsV2QuadShankReference.Ground) + if (ProbeConfigurationA.Reference == NeuropixelsV2ShankReference.Ground) { throw new InvalidOperationException($"Neuropixels 2.0-Beta probes do not provide a Ground reference selection. Please select a different reference" + $" for {NeuropixelsV2Probe.ProbeA}."); @@ -229,7 +229,7 @@ public override IObservable Process(IObservable source // configure probe B streaming if (probeBMetadata.ProbeSerialNumber != null) { - if (ProbeConfigurationB.Reference == NeuropixelsV2QuadShankReference.Ground) + if (ProbeConfigurationB.Reference == NeuropixelsV2ShankReference.Ground) { throw new InvalidOperationException($"Neuropixels 2.0-Beta probes do not provide a Ground reference selection. Please select a different reference" + $" for {NeuropixelsV2Probe.ProbeB}."); diff --git a/OpenEphys.Onix1/IConfigureNeuropixelsV2.cs b/OpenEphys.Onix1/IConfigureNeuropixelsV2.cs index 0124313e..cb1f3af1 100644 --- a/OpenEphys.Onix1/IConfigureNeuropixelsV2.cs +++ b/OpenEphys.Onix1/IConfigureNeuropixelsV2.cs @@ -13,7 +13,7 @@ public interface IConfigureNeuropixelsV2 /// /// Gets or sets the electrode configuration for Probe A. /// - public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationA { get; set; } + public NeuropixelsV2ProbeConfiguration ProbeConfigurationA { get; set; } /// /// Gets or sets the path to the gain calibration file for Probe A. @@ -23,7 +23,7 @@ public interface IConfigureNeuropixelsV2 /// /// Gets or sets the electrode configuration for Probe B. /// - public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationB { get; set; } + public NeuropixelsV2ProbeConfiguration ProbeConfigurationB { get; set; } /// /// Gets or sets the path to the gain calibration file for Probe B. diff --git a/OpenEphys.Onix1/NeuropixelsV2.cs b/OpenEphys.Onix1/NeuropixelsV2.cs index b76db70d..216d728d 100644 --- a/OpenEphys.Onix1/NeuropixelsV2.cs +++ b/OpenEphys.Onix1/NeuropixelsV2.cs @@ -29,43 +29,86 @@ static class NeuropixelsV2 public const int ChannelCount = 384; public const int BaseBitsPerChannel = 4; public const int ElectrodePerShank = 1280; - public const int ElectrodePerBlock = 48; + public const int ElectrodePerBlockQuadShank = 48; + public const int ElectrodePerBlockSingleShank = 32; public const int ReferencePixelCount = 4; public const int DummyRegisterCount = 4; public const int RegistersPerShank = ElectrodePerShank + ReferencePixelCount + DummyRegisterCount; - internal static BitArray[] GenerateShankBits(NeuropixelsV2QuadShankProbeConfiguration probe) + internal static BitArray[] GenerateShankBits(NeuropixelsV2ProbeConfiguration probe) { - BitArray[] shankBits = - { - new(RegistersPerShank, false), - new(RegistersPerShank, false), - new(RegistersPerShank, false), - new(RegistersPerShank, false) - }; + BitArray[] shankBits; + + const int ShiftRegisterBitExternalElectrode0 = 1285; + const int ShiftRegisterBitExternalElectrode1 = 2; + const int ShiftRegisterBitTipElectrode0 = 644; + const int ShiftRegisterBitTipElectrode1 = 643; - if (probe.Reference != NeuropixelsV2QuadShankReference.External) + if (probe.ProbeType == NeuropixelsV2ProbeType.SingleShank) { - // If tip reference is used, activate the tip electrodes - shankBits[(int)probe.Reference - 1][643] = true; - shankBits[(int)probe.Reference - 1][644] = true; + shankBits = new BitArray[] + { + new(RegistersPerShank, false) + }; + const int Shank = 0; + + if (probe.Reference == NeuropixelsV2ShankReference.Tip) + { + shankBits[Shank][ShiftRegisterBitTipElectrode1] = true; + shankBits[Shank][ShiftRegisterBitTipElectrode0] = true; + } + else if (probe.Reference == NeuropixelsV2ShankReference.External) + { + shankBits[Shank][ShiftRegisterBitExternalElectrode0] = true; + shankBits[Shank][ShiftRegisterBitExternalElectrode1] = true; + } + } + else if (probe.ProbeType == NeuropixelsV2ProbeType.QuadShank) + { + shankBits = new BitArray[] + { + new(RegistersPerShank, false), + new(RegistersPerShank, false), + new(RegistersPerShank, false), + new(RegistersPerShank, false) + }; + + if (probe.Reference != NeuropixelsV2ShankReference.External && probe.Reference != NeuropixelsV2ShankReference.Ground) + { + var shank = probe.Reference switch + { + NeuropixelsV2ShankReference.Tip1 => 0, + NeuropixelsV2ShankReference.Tip2 => 1, + NeuropixelsV2ShankReference.Tip3 => 2, + NeuropixelsV2ShankReference.Tip4 => 3, + _ => throw new InvalidOperationException($"Invalid reference chosen for {probe.ProbeType} probe.") + }; + + // If tip reference is used, activate the tip electrode + shankBits[shank][ShiftRegisterBitTipElectrode1] = true; + shankBits[shank][ShiftRegisterBitTipElectrode0] = true; + } + else if (probe.Reference == NeuropixelsV2ShankReference.External) + { + // TODO: is this the right approach or should only those + // connections to external reference on shanks with active + // electrodes be activated? + + // If external electrode is used, activate on each Shank + shankBits[0][ShiftRegisterBitExternalElectrode1] = true; + shankBits[0][ShiftRegisterBitExternalElectrode0] = true; + shankBits[1][ShiftRegisterBitExternalElectrode1] = true; + shankBits[1][ShiftRegisterBitExternalElectrode0] = true; + shankBits[2][ShiftRegisterBitExternalElectrode1] = true; + shankBits[2][ShiftRegisterBitExternalElectrode0] = true; + shankBits[3][ShiftRegisterBitExternalElectrode1] = true; + shankBits[3][ShiftRegisterBitExternalElectrode0] = true; + } } else { - // TODO: is this the right approach or should only those - // connections to external reference on shanks with active - // electrodes be activated? - - // If external electrode is used, activate on each shank - shankBits[0][2] = true; - shankBits[0][1285] = true; - shankBits[1][2] = true; - shankBits[1][1285] = true; - shankBits[2][2] = true; - shankBits[2][1285] = true; - shankBits[3][2] = true; - shankBits[3][1285] = true; + throw new InvalidOperationException("Unknown probe configuration type given."); } const int PixelOffset = (ElectrodePerShank - 1) / 2; @@ -84,7 +127,7 @@ internal static BitArray[] GenerateShankBits(NeuropixelsV2QuadShankProbeConfigur return shankBits; } - internal static BitArray[] GenerateBaseBits(NeuropixelsV2QuadShankProbeConfiguration probe) + internal static BitArray[] GenerateBaseBits(NeuropixelsV2ProbeConfiguration probe) { BitArray[] baseBits = { @@ -92,15 +135,26 @@ internal static BitArray[] GenerateBaseBits(NeuropixelsV2QuadShankProbeConfigura new(ChannelCount * BaseBitsPerChannel / 2, false) }; - var referenceBit = probe.Reference switch + var referenceBit = probe.ProbeType switch { - NeuropixelsV2QuadShankReference.External => 1, - NeuropixelsV2QuadShankReference.Tip1 => 2, - NeuropixelsV2QuadShankReference.Tip2 => 2, - NeuropixelsV2QuadShankReference.Tip3 => 2, - NeuropixelsV2QuadShankReference.Tip4 => 2, - NeuropixelsV2QuadShankReference.Ground => 3, - _ => throw new InvalidOperationException("Invalid reference selection."), + NeuropixelsV2ProbeType.SingleShank => probe.Reference switch + { + NeuropixelsV2ShankReference.External => 1, + NeuropixelsV2ShankReference.Tip => 2, + NeuropixelsV2ShankReference.Ground => 3, + _ => throw new InvalidOperationException("Invalid reference selection."), + }, + NeuropixelsV2ProbeType.QuadShank => probe.Reference switch + { + NeuropixelsV2ShankReference.External => 1, + NeuropixelsV2ShankReference.Tip1 => 2, + NeuropixelsV2ShankReference.Tip2 => 2, + NeuropixelsV2ShankReference.Tip3 => 2, + NeuropixelsV2ShankReference.Tip4 => 2, + NeuropixelsV2ShankReference.Ground => 3, + _ => throw new InvalidOperationException("Invalid reference selection."), + }, + _ => throw new InvalidOperationException("Invalid probe type given.") }; for (int i = 0; i < ChannelCount; i++) diff --git a/OpenEphys.Onix1/NeuropixelsV2Electrode.cs b/OpenEphys.Onix1/NeuropixelsV2Electrode.cs new file mode 100644 index 00000000..6cffd1f1 --- /dev/null +++ b/OpenEphys.Onix1/NeuropixelsV2Electrode.cs @@ -0,0 +1,198 @@ +using System; +using System.Drawing; +using System.Xml.Serialization; + +namespace OpenEphys.Onix1 +{ + /// + /// Class defining a . + /// + public class NeuropixelsV2Electrode : Electrode + { + /// + /// Gets the bank, or logical block of channels, this electrode belongs to. + /// + [XmlIgnore] + public NeuropixelsV2Bank Bank { get; private set; } + + /// + /// Gets the block this electrode belongs to. + /// + [XmlIgnore] + public int Block { get; private set; } + + /// + /// Gets the index within the block this electrode belongs to. + /// + [XmlIgnore] + public int BlockIndex { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// Integer defining the index of the contact. + /// + /// Probe type that this electrode is a part of. Some potential values + /// are , or . + /// + public NeuropixelsV2Electrode(int index, NeuropixelsV2ProbeType probeType) + { + Index = index; + Shank = GetShank(index); + IntraShankElectrodeIndex = GetIntraShankIndex(index); + Bank = GetBank(index); + Block = GetBlock(index, probeType); + BlockIndex = GetBlockIndex(index, probeType); + Position = GetPosition(index); + + if (probeType == NeuropixelsV2ProbeType.SingleShank) + { + Channel = GetSingleShankChannelNumber(Bank, Block, GetRow(index), index % 2 == 0); + } + else if (probeType == NeuropixelsV2ProbeType.QuadShank) + { + Channel = GetQuadShankChannelNumber(Shank, Block, BlockIndex); + } + else + throw new InvalidOperationException("Unknown probe type given."); + } + + private PointF GetPosition(int electrodeNumber) + { + var position = NeuropixelsV2eProbeGroup.DefaultContactPosition(electrodeNumber); + return new PointF(x: position[0], y: position[1]); + } + + static NeuropixelsV2Bank GetBank(int index) => (NeuropixelsV2Bank)(GetIntraShankIndex(index) / NeuropixelsV2.ChannelCount); + + internal static int GetShank(int index) => index / NeuropixelsV2.ElectrodePerShank; + + internal static int GetIntraShankIndex(int index) => index % NeuropixelsV2.ElectrodePerShank; + + static int GetBlock(int index, NeuropixelsV2ProbeType probeType) + { + if (probeType == NeuropixelsV2ProbeType.SingleShank) + return (index % NeuropixelsV2.ChannelCount) / NeuropixelsV2.ElectrodePerBlockSingleShank; + + else if (probeType == NeuropixelsV2ProbeType.QuadShank) + return (GetIntraShankIndex(index) % NeuropixelsV2.ChannelCount) / NeuropixelsV2.ElectrodePerBlockQuadShank; + + else + throw new InvalidOperationException("Invalid probe type given."); + } + + const int ElectrodesPerRow = 2; + + static int GetRow(int index) => (index % NeuropixelsV2.ElectrodePerBlockSingleShank) / ElectrodesPerRow; + + static int GetBlockIndex(int index, NeuropixelsV2ProbeType probeType) + { + if (probeType == NeuropixelsV2ProbeType.SingleShank) + return GetIntraShankIndex(index) % NeuropixelsV2.ElectrodePerBlockSingleShank; + + else if (probeType == NeuropixelsV2ProbeType.QuadShank) + return GetIntraShankIndex(index) % NeuropixelsV2.ElectrodePerBlockQuadShank; + + else + throw new InvalidOperationException("Invalid probe type given."); + } + + /// + /// Static method returning the channel number of a given electrode. + /// + /// Integer defining the index of the electrode in the probe. + /// + /// Probe type that this electrode is a part of. Some potential values + /// are , or . + /// + /// An integer between 0 and 383 defining the channel number. + public static int GetChannelNumber(int electrodeIndex, NeuropixelsV2ProbeType probeType) + { + if (probeType == NeuropixelsV2ProbeType.SingleShank) + { + var bank = GetBank(electrodeIndex); + var block = GetBlock(electrodeIndex, probeType); + var row = GetRow(electrodeIndex); + bool isEven = electrodeIndex % 2 == 0; + + return GetSingleShankChannelNumber(bank, block, row, isEven); + } + else if (probeType == NeuropixelsV2ProbeType.QuadShank) + { + var shank = GetShank(electrodeIndex); + var block = GetBlock(electrodeIndex, probeType); + var blockIndex = GetBlockIndex(electrodeIndex, probeType); + + return GetQuadShankChannelNumber(shank, block, blockIndex); + } + else + throw new InvalidOperationException("Unknown probe type given."); + } + + internal static int GetSingleShankChannelNumber(NeuropixelsV2Bank bank, int block, int row, bool even) + { + const int MaxBlockValue = 11; + const int MaxRowValue = 15; + + if (block > MaxBlockValue || block < 0) + throw new ArgumentOutOfRangeException($"Block value is out of range. Expected to be between 0 and {MaxBlockValue}, but value is {block}"); + + if (row > MaxRowValue || row < 0) + throw new ArgumentOutOfRangeException($"Row value is out of range. Expected to be between 0 and {MaxRowValue}, but value is {row}"); + + int offset = even ? 0 : 1; // NB: Left electrodes (even numbers) have no offset, while right electrodes (odd numbers) have +1 + + const int HalfBlock = 16; + + return bank switch + { + NeuropixelsV2Bank.A => row * ElectrodesPerRow + block * NeuropixelsV2.ElectrodePerBlockSingleShank + offset, + NeuropixelsV2Bank.B => (row * 7 % HalfBlock) * ElectrodesPerRow + block * NeuropixelsV2.ElectrodePerBlockSingleShank + offset, + NeuropixelsV2Bank.C => (row * 5 % HalfBlock) * ElectrodesPerRow + block * NeuropixelsV2.ElectrodePerBlockSingleShank + offset, + NeuropixelsV2Bank.D => (row * 3 % HalfBlock) * ElectrodesPerRow + block * NeuropixelsV2.ElectrodePerBlockSingleShank + offset, + _ => throw new NotImplementedException($"Invalid {nameof(NeuropixelsV2Bank)} value given.") + }; + } + + internal static int GetQuadShankChannelNumber(int shank, int block, int blockIndex) => (shank, block) switch + { + (0, 0) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 0, + (0, 1) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 2, + (0, 2) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 4, + (0, 3) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 6, + (0, 4) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 5, + (0, 5) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 7, + (0, 6) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 1, + (0, 7) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 3, + + (1, 0) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 1, + (1, 1) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 3, + (1, 2) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 5, + (1, 3) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 7, + (1, 4) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 4, + (1, 5) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 6, + (1, 6) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 0, + (1, 7) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 2, + + (2, 0) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 4, + (2, 1) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 6, + (2, 2) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 0, + (2, 3) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 2, + (2, 4) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 1, + (2, 5) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 3, + (2, 6) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 5, + (2, 7) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 7, + + (3, 0) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 5, + (3, 1) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 7, + (3, 2) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 1, + (3, 3) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 3, + (3, 4) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 0, + (3, 5) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 2, + (3, 6) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 4, + (3, 7) => blockIndex + NeuropixelsV2.ElectrodePerBlockQuadShank * 6, + + _ => throw new ArgumentOutOfRangeException($"Invalid shank and/or electrode value: {(shank, block)}"), + }; + } +} diff --git a/OpenEphys.Onix1/NeuropixelsV2ProbeConfiguration.cs b/OpenEphys.Onix1/NeuropixelsV2ProbeConfiguration.cs new file mode 100644 index 00000000..2e718da0 --- /dev/null +++ b/OpenEphys.Onix1/NeuropixelsV2ProbeConfiguration.cs @@ -0,0 +1,296 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using Bonsai; +using Newtonsoft.Json; + +namespace OpenEphys.Onix1 +{ + /// + /// Specifies the reference for a quad-shank probe. + /// + public enum NeuropixelsV2ShankReference : uint + { + /// + /// Specifies that the External reference will be used. + /// + External, + /// + /// Specifies that the tip reference of shank 1 will be used. + /// + Tip1, + /// + /// Specifies that the tip reference of shank 2 will be used. + /// + Tip2, + /// + /// Specifies that the tip reference of shank 3 will be used. + /// + Tip3, + /// + /// Specifies that the tip reference of shank 4 will be used. + /// + Tip4, + /// + /// Specifies that the tip reference of a single-shank probe will be used. + /// + Tip, + /// + /// Specifies that the Ground reference will be used. + /// + Ground + } + + /// + /// Specifies the bank of electrodes within each shank. + /// + public enum NeuropixelsV2Bank + { + /// + /// Specifies that Bank A is the current bank. + /// + /// Bank A is defined as shank index 0 to 383 along each shank. + A, + /// + /// Specifies that Bank B is the current bank. + /// + /// Bank B is defined as shank index 384 to 767 along each shank. + B, + /// + /// Specifies that Bank C is the current bank. + /// + /// Bank C is defined as shank index 768 to 1151 along each shank. + C, + /// + /// Specifies that Bank D is the current bank. + /// + /// + /// Bank D is defined as shank index 1152 to 1279 along each shank. Note that Bank D is not a full contingent + /// of 384 channels; to compensate for this, electrodes from Bank C (starting at shank index 896) are used to + /// generate a full 384 channel map. + /// + D, + } + + /// + /// Specifies the current probe type. + /// + public enum NeuropixelsV2ProbeType + { + /// + /// Specifies that there is one shank. + /// + SingleShank = 0, + /// + /// Specifies that there are four shanks. + /// + QuadShank, + } + + /// + /// Defines a configuration for Neuropixels 2.0 and 2.0-beta probes. + /// + public class NeuropixelsV2ProbeConfiguration + { + private NeuropixelsV2ProbeConfiguration() + { + ChannelMap = NeuropixelsV2eProbeGroup.ToChannelMap(ProbeGroup, ProbeType); + } + + /// + /// Initializes a new instance of the class. + /// + public NeuropixelsV2ProbeConfiguration(NeuropixelsV2Probe probe, NeuropixelsV2ProbeType type, NeuropixelsV2ShankReference reference) + { + Probe = probe; + ProbeType = type; + Reference = reference; + ProbeGroup = new(ProbeType); + ChannelMap = NeuropixelsV2eProbeGroup.ToChannelMap(ProbeGroup, ProbeType); + } + + /// + /// Copy constructor for the class. + /// + /// The existing object to copy. + public NeuropixelsV2ProbeConfiguration(NeuropixelsV2ProbeConfiguration probeConfiguration) + { + ProbeType = probeConfiguration.ProbeType; + Reference = probeConfiguration.Reference; + ProbeGroup = new(probeConfiguration.ProbeGroup); + ChannelMap = NeuropixelsV2eProbeGroup.ToChannelMap(ProbeGroup, ProbeType); + Probe = probeConfiguration.Probe; + } + + /// + /// Initializes a new instance of the class with the given + /// channel configuration. The is automatically + /// generated from the . + /// + /// The existing instance to use. + /// The reference value. + /// The for this probe. + /// The for this probe. + [JsonConstructor] + public NeuropixelsV2ProbeConfiguration(NeuropixelsV2eProbeGroup probeGroup, NeuropixelsV2Probe probe, NeuropixelsV2ProbeType type, NeuropixelsV2ShankReference reference) + { + ProbeType = type; + ProbeGroup = probeGroup; + ChannelMap = NeuropixelsV2eProbeGroup.ToChannelMap(probeGroup, ProbeType); + Reference = reference; + Probe = probe; + } + + /// + /// Gets or sets the for this probe. + /// + [Browsable(false)] + public NeuropixelsV2Probe Probe { get; set; } = NeuropixelsV2Probe.ProbeA; + + /// + /// Gets or sets the for this probe. + /// + [Browsable(false)] + [Description("Defines the type of probe, differentiated by the number of shanks present.")] + public NeuropixelsV2ProbeType ProbeType { get; set; } = NeuropixelsV2ProbeType.QuadShank; + + internal static Array FilterNeuropixelsV2ShankReference(NeuropixelsV2ProbeType probeType) + { + if (probeType == NeuropixelsV2ProbeType.SingleShank) + { + return Enum.GetValues(typeof(NeuropixelsV2ShankReference)) + .Cast() + .Where(r => + { + return r == NeuropixelsV2ShankReference.External + || r == NeuropixelsV2ShankReference.Tip + || r == NeuropixelsV2ShankReference.Ground; + }) + .ToArray(); + } + else if (probeType == NeuropixelsV2ProbeType.QuadShank) + { + return Enum.GetValues(typeof(NeuropixelsV2ShankReference)) + .Cast() + .Where(r => + { + return r == NeuropixelsV2ShankReference.External + || r == NeuropixelsV2ShankReference.Tip1 + || r == NeuropixelsV2ShankReference.Tip2 + || r == NeuropixelsV2ShankReference.Tip3 + || r == NeuropixelsV2ShankReference.Tip4 + || r == NeuropixelsV2ShankReference.Ground; + }) + .ToArray(); + } + + throw new InvalidEnumArgumentException("Unknown probe type given."); + } + + /// + /// Gets or sets the reference for all electrodes. + /// + /// + /// All electrodes are set to the same reference, which can be + /// or any of the tip references + /// (, , etc.). + /// Setting to will use the external reference, while + /// sets the reference to the electrode at the tip of the first shank. + /// + [Description("Defines what the reference for the probe will be, whether it is external, on a shank tip, or the ground reference.")] + [TypeConverter(typeof(ReferenceTypeConverter))] + public NeuropixelsV2ShankReference Reference { get; set; } = NeuropixelsV2ShankReference.External; + + /// + /// Gets the existing channel map listing all currently enabled electrodes. + /// + /// + /// The channel map will always be 384 channels, and will return the 384 enabled electrodes. + /// + [XmlIgnore] + public NeuropixelsV2Electrode[] ChannelMap { get; } + + /// + /// Update the with the selected electrodes. + /// + /// List of selected electrodes that are being added to the + public void SelectElectrodes(NeuropixelsV2Electrode[] electrodes) + { + foreach (var e in electrodes) + { + try + { + ChannelMap[e.Channel] = e; + } + catch (IndexOutOfRangeException ex) + { + throw new IndexOutOfRangeException($"Electrode {e.Index} specifies channel {e.Channel} but only channels " + + $"0 to {ChannelMap.Length - 1} are supported.", ex); + } + } + + ProbeGroup.UpdateDeviceChannelIndices(ChannelMap); + } + + /// + /// Gets the channel configuration for this probe. + /// + [XmlIgnore] + [Category("Configuration")] + [Description("Defines the shape of the probe, and which contacts are currently selected for streaming")] + public NeuropixelsV2eProbeGroup ProbeGroup { get; private set; } = new(NeuropixelsV2ProbeType.QuadShank); + + /// + /// Gets or sets a string defining the in Base64. + /// This variable is needed to properly save a workflow in Bonsai, but it is not + /// directly accessible in the Bonsai editor. + /// + [Browsable(false)] + [Externalizable(false)] + [XmlElement(nameof(ProbeGroup))] + public string ProbeGroupString + { + get + { + var jsonString = JsonConvert.SerializeObject(ProbeGroup); + return Convert.ToBase64String(Encoding.UTF8.GetBytes(jsonString)); + } + set + { + var jsonString = Encoding.UTF8.GetString(Convert.FromBase64String(value)); + ProbeGroup = JsonConvert.DeserializeObject(jsonString); + SelectElectrodes(NeuropixelsV2eProbeGroup.ToChannelMap(ProbeGroup, ProbeType)); + } + } + } + + internal class ReferenceTypeConverter : EnumConverter + { + public ReferenceTypeConverter() + : base(typeof(NeuropixelsV2ShankReference)) + { + } + + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) + { + return true; + } + + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) + { + return true; + } + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + { + if (context?.Instance == null) + return base.GetStandardValues(context); + + var probeConfiguration = (NeuropixelsV2ProbeConfiguration)context.Instance; + + return new StandardValuesCollection(NeuropixelsV2ProbeConfiguration.FilterNeuropixelsV2ShankReference(probeConfiguration.ProbeType)); + } + } +} diff --git a/OpenEphys.Onix1/NeuropixelsV2QuadShankElectrode.cs b/OpenEphys.Onix1/NeuropixelsV2QuadShankElectrode.cs index a24c01e9..226d55da 100644 --- a/OpenEphys.Onix1/NeuropixelsV2QuadShankElectrode.cs +++ b/OpenEphys.Onix1/NeuropixelsV2QuadShankElectrode.cs @@ -1,108 +1,17 @@ using System; -using System.Drawing; -using System.Xml.Serialization; namespace OpenEphys.Onix1 { /// /// Class defining a . /// - public class NeuropixelsV2QuadShankElectrode : Electrode + [Obsolete($"Use {nameof(NeuropixelsV2Electrode)} instead. This class is obsolete and will be removed.")] + public class NeuropixelsV2QuadShankElectrode : NeuropixelsV2Electrode { - /// - /// Gets the bank, or logical block of channels, this electrode belongs to. - /// - [XmlIgnore] - public NeuropixelsV2QuadShankBank Bank { get; private set; } - - /// - /// Gets the block this electrode belongs to. - /// - [XmlIgnore] - public int Block { get; private set; } - - /// - /// Gets the index within the block this electrode belongs to. - /// - [XmlIgnore] - public int BlockIndex { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Integer defining the index of the contact. - public NeuropixelsV2QuadShankElectrode(int index) - { - Index = index; - Shank = index / NeuropixelsV2.ElectrodePerShank; - IntraShankElectrodeIndex = index % NeuropixelsV2.ElectrodePerShank; - Bank = (NeuropixelsV2QuadShankBank)(IntraShankElectrodeIndex / NeuropixelsV2.ChannelCount); - Block = IntraShankElectrodeIndex % NeuropixelsV2.ChannelCount / NeuropixelsV2.ElectrodePerBlock; - BlockIndex = IntraShankElectrodeIndex % NeuropixelsV2.ElectrodePerBlock; - Channel = GetChannelNumber(Shank, Block, BlockIndex); - Position = GetPosition(index); - } - - private PointF GetPosition(int electrodeNumber) - { - var position = NeuropixelsV2eProbeGroup.DefaultContactPosition(electrodeNumber); - return new PointF(x: position[0], y: position[1]); - } - - /// - /// Static method returning the channel number of a given electrode. - /// - /// Integer defining the index of the electrode in the probe. - /// An integer between 0 and 383 defining the channel number. - public static int GetChannelNumber(int electrodeIndex) + /// + [Obsolete($"Use {nameof(NeuropixelsV2Electrode)} instead. This class is obsolete and will be removed.")] + public NeuropixelsV2QuadShankElectrode(int index) : base(index, NeuropixelsV2ProbeType.QuadShank) { - var shank = electrodeIndex / NeuropixelsV2.ElectrodePerShank; - var shankIndex = electrodeIndex % NeuropixelsV2.ElectrodePerShank; - var block = shankIndex % NeuropixelsV2.ChannelCount / NeuropixelsV2.ElectrodePerBlock; - var blockIndex = shankIndex % NeuropixelsV2.ElectrodePerBlock; - - return GetChannelNumber(shank, block, blockIndex); } - - internal static int GetChannelNumber(int shank, int block, int blockIndex) => (shank, block) switch - { - (0, 0) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 0, - (0, 1) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 2, - (0, 2) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 4, - (0, 3) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 6, - (0, 4) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 5, - (0, 5) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 7, - (0, 6) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 1, - (0, 7) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 3, - - (1, 0) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 1, - (1, 1) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 3, - (1, 2) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 5, - (1, 3) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 7, - (1, 4) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 4, - (1, 5) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 6, - (1, 6) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 0, - (1, 7) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 2, - - (2, 0) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 4, - (2, 1) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 6, - (2, 2) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 0, - (2, 3) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 2, - (2, 4) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 1, - (2, 5) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 3, - (2, 6) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 5, - (2, 7) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 7, - - (3, 0) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 5, - (3, 1) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 7, - (3, 2) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 1, - (3, 3) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 3, - (3, 4) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 0, - (3, 5) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 2, - (3, 6) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 4, - (3, 7) => blockIndex + NeuropixelsV2.ElectrodePerBlock * 6, - - _ => throw new ArgumentOutOfRangeException($"Invalid shank and/or electrode value: {(shank, block)}"), - }; } } diff --git a/OpenEphys.Onix1/NeuropixelsV2QuadShankProbeConfiguration.cs b/OpenEphys.Onix1/NeuropixelsV2QuadShankProbeConfiguration.cs index 6a3e9280..2cd72f31 100644 --- a/OpenEphys.Onix1/NeuropixelsV2QuadShankProbeConfiguration.cs +++ b/OpenEphys.Onix1/NeuropixelsV2QuadShankProbeConfiguration.cs @@ -1,234 +1,20 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using Bonsai; -using Newtonsoft.Json; -using System.Text; -using System.Xml.Serialization; -using System.Linq; -using OpenEphys.ProbeInterface.NET; namespace OpenEphys.Onix1 { - /// - /// Specifies the reference for a quad-shank probe. - /// - public enum NeuropixelsV2QuadShankReference : uint - { - /// - /// Specifies that the External reference will be used. - /// - External, - /// - /// Specifies that the tip reference of shank 1 will be used. - /// - Tip1, - /// - /// Specifies that the tip reference of shank 2 will be used. - /// - Tip2, - /// - /// Specifies that the tip reference of shank 3 will be used. - /// - Tip3, - /// - /// Specifies that the tip reference of shank 4 will be used. - /// - Tip4, - /// - /// Specifies that the ground reference will be used. - /// - Ground - } - - /// - /// Specifies the bank of electrodes within each shank. - /// - public enum NeuropixelsV2QuadShankBank - { - /// - /// Specifies that Bank A is the current bank. - /// - /// Bank A is defined as shank index 0 to 383 along each shank. - A, - /// - /// Specifies that Bank B is the current bank. - /// - /// Bank B is defined as shank index 384 to 767 along each shank. - B, - /// - /// Specifies that Bank C is the current bank. - /// - /// Bank C is defined as shank index 768 to 1151 along each shank. - C, - /// - /// Specifies that Bank D is the current bank. - /// - /// - /// Bank D is defined as shank index 1152 to 1279 along each shank. Note that Bank D is not a full contingent - /// of 384 channels; to compensate for this, electrodes from Bank C (starting at shank index 896) are used to - /// generate a full 384 channel map. - /// - D, - } - /// /// Defines a configuration for quad-shank, Neuropixels 2.0 and 2.0-beta probes. /// - public class NeuropixelsV2QuadShankProbeConfiguration + [Obsolete($"Use {nameof(NeuropixelsV2ProbeConfiguration)} instead. This class will be removed.")] + public class NeuropixelsV2QuadShankProbeConfiguration : NeuropixelsV2ProbeConfiguration { - /// - /// Creates a model of the probe with all electrodes instantiated. - /// - [XmlIgnore] - public static readonly IReadOnlyList ProbeModel = CreateProbeModel(); - - /// - /// Initializes a new instance of the class. - /// - public NeuropixelsV2QuadShankProbeConfiguration() - { - ChannelMap = new NeuropixelsV2QuadShankElectrode[NeuropixelsV2.ChannelCount]; - for (int i = 0; i < ChannelMap.Length; i++) - { - ChannelMap[i] = ProbeModel.FirstOrDefault(e => e.Channel == i); - } - } - /// /// Initializes a new instance of the class. /// - public NeuropixelsV2QuadShankProbeConfiguration(NeuropixelsV2Probe probe) : this() - { - Probe = probe; - } - - /// - /// Initializes a new instance of the class. - /// - public NeuropixelsV2QuadShankProbeConfiguration(NeuropixelsV2Probe probe, NeuropixelsV2QuadShankReference reference) : this() - { - Probe = probe; - Reference = reference; - } - - /// - /// Copy constructor for the class. - /// - /// The existing object to copy. - public NeuropixelsV2QuadShankProbeConfiguration(NeuropixelsV2QuadShankProbeConfiguration probeConfiguration) - { - Reference = probeConfiguration.Reference; - var probes = probeConfiguration.ProbeGroup.Probes.ToList().Select(probe => new Probe(probe)); - ProbeGroup = new(probeConfiguration.ProbeGroup.Specification, probeConfiguration.ProbeGroup.Version, probes.ToArray()); - ChannelMap = NeuropixelsV2eProbeGroup.ToChannelMap(ProbeGroup); - Probe = probeConfiguration.Probe; - } - - /// - /// Initializes a new instance of the class with the given - /// channel configuration. The is automatically - /// generated from the . - /// - /// The existing instance to use. - /// The reference value. - /// The for this probe. - [JsonConstructor] - public NeuropixelsV2QuadShankProbeConfiguration(NeuropixelsV2eProbeGroup probeGroup, NeuropixelsV2QuadShankReference reference, NeuropixelsV2Probe probe) - { - ChannelMap = NeuropixelsV2eProbeGroup.ToChannelMap(probeGroup); - ProbeGroup = probeGroup; - Reference = reference; - Probe = probe; - } - - private static List CreateProbeModel() - { - var electrodes = new List(NeuropixelsV2.ElectrodePerShank * 4); - for (int i = 0; i < NeuropixelsV2.ElectrodePerShank * 4; i++) - { - electrodes.Add(new NeuropixelsV2QuadShankElectrode(i)); - } - return electrodes; - } - - /// - /// Gets or sets the for this probe. - /// - public NeuropixelsV2Probe Probe { get; set; } = NeuropixelsV2Probe.ProbeA; - - /// - /// Gets or sets the reference for all electrodes. - /// - /// - /// All electrodes are set to the same reference, which can be - /// or any of the tip references - /// (, , etc.). - /// Setting to will use the external reference, while - /// sets the reference to the electrode at the tip of the first shank. - /// - public NeuropixelsV2QuadShankReference Reference { get; set; } = NeuropixelsV2QuadShankReference.External; - - /// - /// Gets the existing channel map listing all currently enabled electrodes. - /// - /// - /// The channel map will always be 384 channels, and will return the 384 enabled electrodes. - /// - [XmlIgnore] - public NeuropixelsV2QuadShankElectrode[] ChannelMap { get; } - - /// - /// Update the with the selected electrodes. - /// - /// List of selected electrodes that are being added to the - public void SelectElectrodes(NeuropixelsV2QuadShankElectrode[] electrodes) - { - foreach (var e in electrodes) - { - try - { - ChannelMap[e.Channel] = e; - } - catch (IndexOutOfRangeException ex) - { - throw new IndexOutOfRangeException($"Electrode {e.Index} specifies channel {e.Channel} but only channels " + - $"0 to {ChannelMap.Length - 1} are supported.", ex); - } - } - - ProbeGroup.UpdateDeviceChannelIndices(ChannelMap); - } - - /// - /// Gets the channel configuration for this probe. - /// - [XmlIgnore] - [Category("Configuration")] - [Description("Defines the shape of the probe, and which contacts are currently selected for streaming")] - public NeuropixelsV2eProbeGroup ProbeGroup { get; private set; } = new(); - - /// - /// Gets or sets a string defining the in Base64. - /// This variable is needed to properly save a workflow in Bonsai, but it is not - /// directly accessible in the Bonsai editor. - /// - [Browsable(false)] - [Externalizable(false)] - [XmlElement(nameof(ProbeGroup))] - public string ProbeGroupString + [Obsolete($"Use {nameof(NeuropixelsV2ProbeConfiguration)} instead. This class will be removed.")] + public NeuropixelsV2QuadShankProbeConfiguration(NeuropixelsV2Probe probe, NeuropixelsV2ShankReference reference) + : base(probe, NeuropixelsV2ProbeType.QuadShank, reference) { - get - { - var jsonString = JsonConvert.SerializeObject(ProbeGroup); - return Convert.ToBase64String(Encoding.UTF8.GetBytes(jsonString)); - } - set - { - var jsonString = Encoding.UTF8.GetString(Convert.FromBase64String(value)); - ProbeGroup = JsonConvert.DeserializeObject(jsonString); - SelectElectrodes(NeuropixelsV2eProbeGroup.ToChannelMap(ProbeGroup)); - } } } } diff --git a/OpenEphys.Onix1/NeuropixelsV2eBetaRegisterContext.cs b/OpenEphys.Onix1/NeuropixelsV2eBetaRegisterContext.cs index 48b30a26..f9d76d28 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eBetaRegisterContext.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eBetaRegisterContext.cs @@ -15,7 +15,7 @@ public NeuropixelsV2eBetaRegisterContext(DeviceContext deviceContext, uint i2cAd { } - public void WriteConfiguration(NeuropixelsV2QuadShankProbeConfiguration probe) + public void WriteConfiguration(NeuropixelsV2ProbeConfiguration probe) { var baseBits = NeuropixelsV2.GenerateBaseBits(probe); WriteShiftRegister(NeuropixelsV2eBeta.SR_CHAIN5, baseBits[0]); diff --git a/OpenEphys.Onix1/NeuropixelsV2eProbeGroup.cs b/OpenEphys.Onix1/NeuropixelsV2eProbeGroup.cs index ccc4cc20..5230d8ed 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eProbeGroup.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eProbeGroup.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using Newtonsoft.Json; using OpenEphys.ProbeInterface.NET; @@ -14,7 +15,6 @@ public class NeuropixelsV2eProbeGroup : ProbeGroup const float shankOffsetX = 200f; const float shankWidthX = 70f; const float shankPitchX = 250f; - const int numberOfShanks = 4; /// /// Initializes a new instance of the class. @@ -23,25 +23,59 @@ public class NeuropixelsV2eProbeGroup : ProbeGroup /// The default constructor will initialize the new with /// the default settings for all contacts, including their positions, shapes, and IDs. /// - public NeuropixelsV2eProbeGroup() - : base("probeinterface", "0.2.21", DefaultProbes()) + public NeuropixelsV2eProbeGroup(NeuropixelsV2ProbeType probeType) + : base("probeinterface", "0.2.21", DefaultProbes(probeType)) { } - private static Probe[] DefaultProbes() + internal static string SingleShankProbeName = "Neuropixels 2.0 - single shank"; + internal static string QuadShankProbeName = "Neuropixels 2.0 - multishank"; + + internal static string GetProbeName(NeuropixelsV2ProbeType probeType) + { + return probeType switch + { + NeuropixelsV2ProbeType.SingleShank => SingleShankProbeName, + NeuropixelsV2ProbeType.QuadShank => QuadShankProbeName, + _ => throw new InvalidEnumArgumentException("Unknown probe type given.") + }; + } + + internal static NeuropixelsV2ProbeType GetProbeTypeFromProbeName(string name) + { + if (name == SingleShankProbeName) + return NeuropixelsV2ProbeType.SingleShank; + + else if (name == QuadShankProbeName) + return NeuropixelsV2ProbeType.QuadShank; + + else + throw new ArgumentException($"The type '{name}' does not match any implemented Neuropixels 2.0 probe types."); + } + + private static Probe[] DefaultProbes(NeuropixelsV2ProbeType probeType) { var probe = new Probe[1]; + int numberOfShanks = probeType switch + { + NeuropixelsV2ProbeType.SingleShank => 1, + NeuropixelsV2ProbeType.QuadShank => 4, + _ => throw new InvalidEnumArgumentException("Unknown probe type given.") + }; + + string probeName = GetProbeName(probeType); + probe[0] = new(ProbeNdim.Two, ProbeSiUnits.um, - new ProbeAnnotations("Neuropixels 2.0 - Multishank", "IMEC"), + new ProbeAnnotations(probeName, "IMEC"), null, DefaultContactPositions(NeuropixelsV2.ElectrodePerShank * numberOfShanks), Probe.DefaultContactPlaneAxes(NeuropixelsV2.ElectrodePerShank * numberOfShanks), Probe.DefaultContactShapes(NeuropixelsV2.ElectrodePerShank * numberOfShanks, ContactShape.Square), Probe.DefaultSquareParams(NeuropixelsV2.ElectrodePerShank * numberOfShanks, 12.0f), - DefaultProbePlanarContourQuadShank(), - DefaultDeviceChannelIndices(NeuropixelsV2.ChannelCount, NeuropixelsV2.ElectrodePerShank * numberOfShanks), + DefaultProbePlanarContour(probeType), + DefaultDeviceChannelIndices(NeuropixelsV2.ChannelCount, NeuropixelsV2.ElectrodePerShank * numberOfShanks, probeType), Probe.DefaultContactIds(NeuropixelsV2.ElectrodePerShank * numberOfShanks), DefaultShankIds(NeuropixelsV2.ElectrodePerShank * numberOfShanks)); @@ -116,6 +150,23 @@ private static float ContactPositionX(int index) }; } + /// + /// Generates a default planar contour for the type of probe that is given. + /// + /// Type of probe, can be either or . + /// + /// + public static float[][] DefaultProbePlanarContour(NeuropixelsV2ProbeType probeType) + { + if (probeType == NeuropixelsV2ProbeType.SingleShank) + return DefaultProbePlanarContourSingleShank(); + + else if (probeType == NeuropixelsV2ProbeType.QuadShank) + return DefaultProbePlanarContourQuadShank(); + + throw new InvalidEnumArgumentException(nameof(probeType)); + } + /// /// Generates a default planar contour for the probe, based on the given probe index /// @@ -155,14 +206,20 @@ public static float[][] DefaultProbePlanarContourQuadShank() /// public static float[][] DefaultProbePlanarContourSingleShank() { + const float shankTipY = 0f; + const float shankBaseY = 155f; + const float shankLengthY = 9770f; + const float shankWidthX = 70f; + const float shankMidX = 35f; + float[][] probePlanarContour = new float[6][]; - probePlanarContour[0] = new float[2] { -11f, 155f }; - probePlanarContour[1] = new float[2] { 24f, 0f }; - probePlanarContour[2] = new float[2] { 59f, 155f }; - probePlanarContour[3] = new float[2] { 59f, 10000f }; - probePlanarContour[4] = new float[2] { -11f, 10000f }; - probePlanarContour[5] = new float[2] { -11f, 155f }; + probePlanarContour[0] = new float[2] { shankOffsetX + 0f, shankBaseY }; + probePlanarContour[1] = new float[2] { shankOffsetX + shankMidX, shankTipY }; + probePlanarContour[2] = new float[2] { shankOffsetX + shankWidthX, shankBaseY }; + probePlanarContour[3] = new float[2] { shankOffsetX + shankWidthX, shankLengthY }; + probePlanarContour[4] = new float[2] { shankOffsetX + 0f, shankLengthY }; + probePlanarContour[5] = new float[2] { shankOffsetX + 0f, shankBaseY }; return probePlanarContour; } @@ -173,16 +230,15 @@ public static float[][] DefaultProbePlanarContourSingleShank() /// /// Number of contacts that are connected for recording /// Total number of physical contacts on the probe + /// Type of probe, can be either or . /// - public static int[] DefaultDeviceChannelIndices(int channelCount, int electrodeCount) + public static int[] DefaultDeviceChannelIndices(int channelCount, int electrodeCount, NeuropixelsV2ProbeType probeType) { int[] deviceChannelIndices = new int[electrodeCount]; for (int i = 0; i < channelCount; i++) { - deviceChannelIndices[i] = NeuropixelsV2QuadShankElectrode.GetChannelNumber(i / NeuropixelsV2.ElectrodePerShank, - i % NeuropixelsV2.ChannelCount / NeuropixelsV2.ElectrodePerBlock, - i % NeuropixelsV2.ElectrodePerBlock); + deviceChannelIndices[i] = NeuropixelsV2Electrode.GetChannelNumber(i, probeType); } for (int i = channelCount; i < electrodeCount; i++) @@ -222,14 +278,15 @@ public static string[] DefaultShankIds(int numberOfContacts) /// Convert a ProbeInterface object to a list of electrodes, which includes all possible electrodes /// /// A object - /// List of electrodes - public static List ToElectrodes(NeuropixelsV2eProbeGroup probeGroup) + /// Type of probe, can be either or . + /// List of electrodes + public static List ToElectrodes(NeuropixelsV2eProbeGroup probeGroup, NeuropixelsV2ProbeType probeType) { - List electrodes = new(); + List electrodes = new(); foreach (var c in probeGroup.GetContacts()) { - electrodes.Add(new NeuropixelsV2QuadShankElectrode(c.Index)); + electrodes.Add(new NeuropixelsV2Electrode(c.Index, probeType)); } return electrodes; @@ -239,10 +296,13 @@ public static List ToElectrodes(NeuropixelsV2eP /// Convert a object to a list of electrodes, which only includes currently enabled electrodes /// /// A object - /// List of electrodes that are enabled - public static NeuropixelsV2QuadShankElectrode[] ToChannelMap(NeuropixelsV2eProbeGroup probeGroup) + /// + /// Probe type that this electrode is a part of. Some potential values + /// are , or . + /// + /// List of electrodes that are enabled + public static NeuropixelsV2Electrode[] ToChannelMap(NeuropixelsV2eProbeGroup probeGroup, NeuropixelsV2ProbeType probeType) { - var enabledContacts = probeGroup.GetContacts().Where(c => c.DeviceId != -1); if (enabledContacts.Count() != NeuropixelsV2.ChannelCount) @@ -252,16 +312,12 @@ public static NeuropixelsV2QuadShankElectrode[] ToChannelMap(NeuropixelsV2eProbe $"index >= 0."); } - return enabledContacts.Select(c => new NeuropixelsV2QuadShankElectrode(c.Index)) + return enabledContacts.Select(c => new NeuropixelsV2Electrode(c.Index, probeType)) .OrderBy(e => e.Channel) .ToArray(); } - /// - /// Updates the based on the given channel map. - /// - /// Existing . - internal void UpdateDeviceChannelIndices(NeuropixelsV2QuadShankElectrode[] channelMap) + internal void UpdateDeviceChannelIndices(Electrode[] channelMap) { int[] newDeviceChannelIndices = new int[NumberOfContacts]; diff --git a/OpenEphys.Onix1/NeuropixelsV2eRegisterContext.cs b/OpenEphys.Onix1/NeuropixelsV2eRegisterContext.cs index 19c5b41a..b7af937b 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eRegisterContext.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eRegisterContext.cs @@ -15,7 +15,7 @@ public NeuropixelsV2eRegisterContext(DeviceContext deviceContext, uint i2cAddres { } - public void WriteConfiguration(NeuropixelsV2QuadShankProbeConfiguration probe) + public void WriteConfiguration(NeuropixelsV2ProbeConfiguration probe) { var baseBits = NeuropixelsV2.GenerateBaseBits(probe); WriteShiftRegister(NeuropixelsV2e.SR_CHAIN5, baseBits[0]);