From 9c9c37edd447fb5963efcae877fad1f15cb78f3c Mon Sep 17 00:00:00 2001 From: bparks13 Date: Wed, 29 Oct 2025 17:53:52 -0400 Subject: [PATCH] Allow NeuropixelsV2 headstages to run without any probes - Instead of checking in the Configure node for the probe connection, move the check to the data node --- OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs | 8 +------- OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs | 8 +------- OpenEphys.Onix1/INeuropixelsV2eMetadata.cs | 13 +++++++++++++ OpenEphys.Onix1/NeuropixelsV2eBetaData.cs | 14 +++++++++++++- OpenEphys.Onix1/NeuropixelsV2eBetaMetadata.cs | 3 +-- OpenEphys.Onix1/NeuropixelsV2eData.cs | 14 +++++++++++++- OpenEphys.Onix1/NeuropixelsV2eDeviceInfo.cs | 10 ++++++++-- OpenEphys.Onix1/NeuropixelsV2eMetadata.cs | 3 +-- 8 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 OpenEphys.Onix1/INeuropixelsV2eMetadata.cs diff --git a/OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs b/OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs index 4855b0b0..fcc98d53 100644 --- a/OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs +++ b/OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs @@ -114,12 +114,6 @@ public override IObservable Process(IObservable source var probeAMetadata = ReadProbeMetadata(serializer, NeuropixelsV2e.ProbeASelected); var probeBMetadata = ReadProbeMetadata(serializer, NeuropixelsV2e.ProbeBSelected); - if (probeAMetadata.ProbeSerialNumber == null && probeBMetadata.ProbeSerialNumber == null) - { - throw new InvalidOperationException("No probes were detected. Ensure that the " + - "flex connection is properly seated."); - } - // issue full reset to both probes ResetProbes(serializer, gpo10Config); @@ -177,7 +171,7 @@ public override IObservable Process(IObservable source // disconnect i2c bus from both probes to prevent digital interference during acquisition SelectProbe(serializer, NeuropixelsV2e.NoProbeSelected); - var deviceInfo = new NeuropixelsV2eDeviceInfo(context, DeviceType, deviceAddress, gainCorrectionA, gainCorrectionB, invertPolarity); + var deviceInfo = new NeuropixelsV2eDeviceInfo(context, DeviceType, deviceAddress, gainCorrectionA, gainCorrectionB, invertPolarity, probeAMetadata, probeBMetadata); var shutdown = Disposable.Create(() => { serializer.WriteByte((uint)DS90UB933SerializerI2CRegister.Gpio10, NeuropixelsV2e.DefaultGPO10Config); diff --git a/OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs b/OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs index a3c0a009..dc661b64 100644 --- a/OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs +++ b/OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs @@ -131,12 +131,6 @@ public override IObservable Process(IObservable source var probeAMetadata = ReadProbeMetadata(serializer, ref gpo32Config, NeuropixelsV2eBeta.SelectProbeA); var probeBMetadata = ReadProbeMetadata(serializer, ref gpo32Config, NeuropixelsV2eBeta.SelectProbeB); - if (probeAMetadata.ProbeSerialNumber == null && probeBMetadata.ProbeSerialNumber == null) - { - throw new InvalidOperationException("No probes were detected. Ensure that the " + - "flex connection is properly seated."); - } - // REC_NRESET and NRESET go high on both probes to take the ASIC out of reset // TODO: not sure if REC_NRESET and NRESET are tied together on flex gpo10Config |= NeuropixelsV2eBeta.GPO10ResetMask | NeuropixelsV2eBeta.GPO10NResetMask; @@ -217,7 +211,7 @@ public override IObservable Process(IObservable source // Still its good to get them roughly (i.e. within 10 PCLKs) started at the same time. SyncProbes(serializer, gpo10Config); - var deviceInfo = new NeuropixelsV2eDeviceInfo(context, DeviceType, deviceAddress, gainCorrectionA, gainCorrectionB, invertPolarity); + var deviceInfo = new NeuropixelsV2eDeviceInfo(context, DeviceType, deviceAddress, gainCorrectionA, gainCorrectionB, invertPolarity, probeAMetadata, probeBMetadata); var shutdown = Disposable.Create(() => { serializer.WriteByte((uint)DS90UB933SerializerI2CRegister.Gpio10, NeuropixelsV2eBeta.DefaultGPO10Config); diff --git a/OpenEphys.Onix1/INeuropixelsV2eMetadata.cs b/OpenEphys.Onix1/INeuropixelsV2eMetadata.cs new file mode 100644 index 00000000..25c2f04a --- /dev/null +++ b/OpenEphys.Onix1/INeuropixelsV2eMetadata.cs @@ -0,0 +1,13 @@ +namespace OpenEphys.Onix1 +{ + interface INeuropixelsV2eMetadata + { + public string ProbePartNumber { get; } + + public ulong? ProbeSerialNumber { get; } + + public string FlexPartNumber { get; } + + public string FlexVersion { get; } + } +} diff --git a/OpenEphys.Onix1/NeuropixelsV2eBetaData.cs b/OpenEphys.Onix1/NeuropixelsV2eBetaData.cs index 40a6cd73..846e1a5d 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eBetaData.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eBetaData.cs @@ -51,6 +51,18 @@ public unsafe override IObservable Generate() return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo => { var info = (NeuropixelsV2eDeviceInfo)deviceInfo; + var metadata = ProbeIndex switch + { + NeuropixelsV2Probe.ProbeA => info.ProbeMetadataA, + NeuropixelsV2Probe.ProbeB => info.ProbeMetadataB, + _ => throw new InvalidEnumArgumentException($"Unexpected {nameof(ProbeIndex)} value: {ProbeIndex}") + }; + + if (metadata.ProbeSerialNumber == null) + { + throw new InvalidOperationException($"{ProbeIndex} is not detected. Ensure that the flex connection is properly seated."); + } + var device = info.GetDeviceContext(typeof(NeuropixelsV2eBeta)); var passthrough = device.GetPassthroughDeviceContext(typeof(DS90UB9x)); var probeData = device.Context @@ -62,7 +74,7 @@ public unsafe override IObservable Generate() { NeuropixelsV2Probe.ProbeA => (double)info.GainCorrectionA, NeuropixelsV2Probe.ProbeB => (double)info.GainCorrectionB, - _ => throw new ArgumentOutOfRangeException(nameof(ProbeIndex), $"Unexpected {nameof(ProbeIndex)} value: {ProbeIndex}"), + _ => throw new InvalidEnumArgumentException($"Unexpected {nameof(ProbeIndex)} value: {ProbeIndex}") }; return Observable.Create(observer => diff --git a/OpenEphys.Onix1/NeuropixelsV2eBetaMetadata.cs b/OpenEphys.Onix1/NeuropixelsV2eBetaMetadata.cs index a85b044e..e7fd8e6e 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eBetaMetadata.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eBetaMetadata.cs @@ -2,7 +2,7 @@ namespace OpenEphys.Onix1 { - class NeuropixelsV2eBetaMetadata + class NeuropixelsV2eBetaMetadata : INeuropixelsV2eMetadata { const uint OFFSET_FLEX_VERSION = 0x00; const uint OFFSET_FLEX_REVISION = 0x01; @@ -39,6 +39,5 @@ public NeuropixelsV2eBetaMetadata(I2CRegisterContext serializer) public string FlexPartNumber { get; } public string FlexVersion { get; } - } } diff --git a/OpenEphys.Onix1/NeuropixelsV2eData.cs b/OpenEphys.Onix1/NeuropixelsV2eData.cs index b2251f8e..c01a0d15 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eData.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eData.cs @@ -55,6 +55,18 @@ public unsafe override IObservable Generate() return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo => { var info = (NeuropixelsV2eDeviceInfo)deviceInfo; + var metadata = ProbeIndex switch + { + NeuropixelsV2Probe.ProbeA => info.ProbeMetadataA, + NeuropixelsV2Probe.ProbeB => info.ProbeMetadataB, + _ => throw new InvalidEnumArgumentException($"Unexpected {nameof(ProbeIndex)} value: {ProbeIndex}") + }; + + if (metadata.ProbeSerialNumber == null) + { + throw new InvalidOperationException($"{ProbeIndex} is not detected. Ensure that the flex connection is properly seated."); + } + var device = info.GetDeviceContext(typeof(NeuropixelsV2e)); var passthrough = device.GetPassthroughDeviceContext(typeof(DS90UB9x)); var probeData = device.Context @@ -66,7 +78,7 @@ public unsafe override IObservable Generate() { NeuropixelsV2Probe.ProbeA => (double)info.GainCorrectionA, NeuropixelsV2Probe.ProbeB => (double)info.GainCorrectionB, - _ => throw new ArgumentOutOfRangeException(nameof(ProbeIndex), $"Unexpected {nameof(ProbeIndex)} value: {ProbeIndex}"), + _ => throw new InvalidEnumArgumentException($"Unexpected {nameof(ProbeIndex)} value: {ProbeIndex}") }; return Observable.Create(observer => diff --git a/OpenEphys.Onix1/NeuropixelsV2eDeviceInfo.cs b/OpenEphys.Onix1/NeuropixelsV2eDeviceInfo.cs index a539bc78..32be6ee8 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eDeviceInfo.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eDeviceInfo.cs @@ -4,18 +4,24 @@ namespace OpenEphys.Onix1 { class NeuropixelsV2eDeviceInfo : DeviceInfo { - public NeuropixelsV2eDeviceInfo(ContextTask context, Type deviceType, uint deviceAddress, double? gainCorrectionA, double? gainCorrectionB, bool invertPolarity) + public NeuropixelsV2eDeviceInfo(ContextTask context, Type deviceType, uint deviceAddress, double? gainCorrectionA, double? gainCorrectionB, bool invertPolarity, INeuropixelsV2eMetadata probeMetadataA, INeuropixelsV2eMetadata probeMetadataB) : base(context, deviceType, deviceAddress) { GainCorrectionA = gainCorrectionA; GainCorrectionB = gainCorrectionB; InvertPolarity = invertPolarity; + ProbeMetadataA = probeMetadataA; + ProbeMetadataB = probeMetadataB; } public double? GainCorrectionA { get; } public double? GainCorrectionB { get; } - public bool InvertPolarity { get; } + public bool InvertPolarity { get; } + + public INeuropixelsV2eMetadata ProbeMetadataA { get; } + + public INeuropixelsV2eMetadata ProbeMetadataB { get; } } } diff --git a/OpenEphys.Onix1/NeuropixelsV2eMetadata.cs b/OpenEphys.Onix1/NeuropixelsV2eMetadata.cs index d33204ca..682dc625 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eMetadata.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eMetadata.cs @@ -2,7 +2,7 @@ namespace OpenEphys.Onix1 { - class NeuropixelsV2eMetadata + class NeuropixelsV2eMetadata : INeuropixelsV2eMetadata { const uint OFFSET_PROBE_SN = 0x00; const uint OFFSET_FLEX_VERSION = 0x10; @@ -39,6 +39,5 @@ public NeuropixelsV2eMetadata(I2CRegisterContext serializer) public string FlexPartNumber { get; } public string FlexVersion { get; } - } }