From 9724fd1bc45fd90e35e964d80fe7547dabf4212d Mon Sep 17 00:00:00 2001 From: jonnew Date: Wed, 14 Aug 2024 18:47:01 -0400 Subject: [PATCH] Expose headstage port status data stream - Fixes #156 - I feel that FmcLinkController and ConfigureFmcLinkController should be renamed PortController and ConfigurePortController, respectively to match their implicity configurate via the "Port" property in headstages and miniscopes, as well as their labelling on the hardware --- OpenEphys.Onix1/ConfigureFmcLinkController.cs | 36 ++++++++++++ .../ConfigureNeuropixelsV2eLinkController.cs | 2 +- OpenEphys.Onix1/PortStatus.cs | 40 +++++++++++++ OpenEphys.Onix1/PortStatusFrame.cs | 56 +++++++++++++++++++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 OpenEphys.Onix1/PortStatus.cs create mode 100644 OpenEphys.Onix1/PortStatusFrame.cs diff --git a/OpenEphys.Onix1/ConfigureFmcLinkController.cs b/OpenEphys.Onix1/ConfigureFmcLinkController.cs index 818c9e7f..be0681fc 100644 --- a/OpenEphys.Onix1/ConfigureFmcLinkController.cs +++ b/OpenEphys.Onix1/ConfigureFmcLinkController.cs @@ -88,6 +88,14 @@ internal static class FmcLinkController public const uint LINKSTATE_PP = 0x2; // parity check pass bit public const uint LINKSTATE_SL = 0x1; // SERDES lock bit + + internal class NameConverter : DeviceNameConverter + { + public NameConverter() + : base(typeof(FmcLinkController)) + { + } + } } internal enum HubConfiguration @@ -95,4 +103,32 @@ internal enum HubConfiguration Standard, Passthrough } + + /// + /// Specifies the headstage port status codes. + /// + [Flags] + public enum PortStatusCode : byte + { + /// + /// Specifies nominal communication status. + /// + Nominal = 0x0, + /// + /// Specifies a cyclic redundancy check failure. + /// + CrcError = 0x1, + /// + /// Specifies too many devices were indicated in the hub device table. + /// + TooManyDevices = 0x2, + /// + /// Specifies a hub initialization error. + /// + InitializationError = 0x4, + /// + /// Specifies the receipt of a badly formed data packet. + /// + BadPacketFormat = 0x8, + } } diff --git a/OpenEphys.Onix1/ConfigureNeuropixelsV2eLinkController.cs b/OpenEphys.Onix1/ConfigureNeuropixelsV2eLinkController.cs index 86cce964..5af4fab5 100644 --- a/OpenEphys.Onix1/ConfigureNeuropixelsV2eLinkController.cs +++ b/OpenEphys.Onix1/ConfigureNeuropixelsV2eLinkController.cs @@ -29,7 +29,7 @@ protected override bool ConfigurePortVoltage(DeviceContext device) void SetVoltage(DeviceContext device, double voltage) { - device.WriteRegister(FmcLinkController.PORTVOLTAGE, 0); + device.WriteRegister(PortController.PORTVOLTAGE, 0); Thread.Sleep(200); device.WriteRegister(FmcLinkController.PORTVOLTAGE, (uint)(10 * voltage)); Thread.Sleep(200); diff --git a/OpenEphys.Onix1/PortStatus.cs b/OpenEphys.Onix1/PortStatus.cs new file mode 100644 index 00000000..5cd5ff06 --- /dev/null +++ b/OpenEphys.Onix1/PortStatus.cs @@ -0,0 +1,40 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Reactive.Linq; +using Bonsai; + +namespace OpenEphys.Onix1 +{ + /// + /// A class that produces a sequence of port status information. + /// + /// + /// This data stream class must be linked to an appropriate headstage, miniscope, or similar configuration. + /// + [Description("Produces a sequence of port status information.")] + public class PortStatus : Source + { + /// + [TypeConverter(typeof(FmcLinkController.NameConverter))] + [Description(SingleDeviceFactory.DeviceNameDescription)] + public string DeviceName { get; set; } + + /// + /// Generates a sequence of objects, which contains information + /// about the system's low-level first-in, first-out (FIFO) data buffer. + /// + /// A sequence of objects. + public override IObservable Generate() + { + return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo => + { + var device = deviceInfo.GetDeviceContext(typeof(FmcLinkController)); + + return deviceInfo.Context + .GetDeviceFrames(device.Address) + .Select(frame => new PortStatusFrame(frame)); + }); + } + } +} diff --git a/OpenEphys.Onix1/PortStatusFrame.cs b/OpenEphys.Onix1/PortStatusFrame.cs new file mode 100644 index 00000000..0d498cf9 --- /dev/null +++ b/OpenEphys.Onix1/PortStatusFrame.cs @@ -0,0 +1,56 @@ +using System.Runtime.InteropServices; + +namespace OpenEphys.Onix1 +{ + /// + /// A class that contains hardware memory use information. + /// + public class PortStatusFrame : DataFrame + { + /// + /// Initializes a new instance of the class. + /// + /// A data frame produced by a memory monitor device. + /// + /// The total amount of memory, in 32-bit words, on the hardware that is available for data buffering. + /// + public unsafe PortStatusFrame(oni.Frame frame) + : base(frame.Clock) + { + var payload = (PortStatusPayload*)frame.Data.ToPointer(); + HubClock = payload->HubClock; + StatusCode = payload->Code; + StatusCodeValid = (payload->DeserializerStatus & 0x0004) == 4; + SerdesLocked = (payload->DeserializerStatus & 0x0001) == 1; + SerdesPass = (payload->DeserializerStatus & 0x0002) == 2; + } + + /// + /// Gets the + /// + public PortStatusCode StatusCode { get; } + + /// + /// Gets the + /// + public bool StatusCodeValid { get; } + + /// + /// Gets the + /// + public bool SerdesLocked { get; } + + /// + /// Gets the + /// + public bool SerdesPass { get; } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct PortStatusPayload + { + public ulong HubClock; + public PortStatusCode Code; + public byte DeserializerStatus; + } +}