From dc4cf821781de92364f2d9927e1e0dc45f046510 Mon Sep 17 00:00:00 2001 From: jonnew Date: Fri, 30 Aug 2024 16:47:30 -0400 Subject: [PATCH] Complete UCLA miniscope V4 support - Tested on OEPS miniscope V4.4 - To finalize this, we must test on a large batch of scopesto ensure functionality of all features and reliablity of datastream --- OpenEphys.Onix1/Bno055DataFrame.cs | 7 +- OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs | 52 +++++++- .../ConfigureUclaMiniscopeV4Bno055.cs | 37 ++++- .../ConfigureUclaMiniscopeV4Camera.cs | 126 +++++++++++++++--- OpenEphys.Onix1/ContextTask.cs | 2 +- OpenEphys.Onix1/NeuropixelsV2eBno055Data.cs | 15 ++- OpenEphys.Onix1/UclaMiniscopeV4Bno055Data.cs | 34 ++++- OpenEphys.Onix1/UclaMiniscopeV4Camera.cs | 66 --------- OpenEphys.Onix1/UclaMiniscopeV4Image.cs | 28 ---- 9 files changed, 239 insertions(+), 128 deletions(-) delete mode 100644 OpenEphys.Onix1/UclaMiniscopeV4Camera.cs delete mode 100644 OpenEphys.Onix1/UclaMiniscopeV4Image.cs diff --git a/OpenEphys.Onix1/Bno055DataFrame.cs b/OpenEphys.Onix1/Bno055DataFrame.cs index f21364f4..c883050b 100644 --- a/OpenEphys.Onix1/Bno055DataFrame.cs +++ b/OpenEphys.Onix1/Bno055DataFrame.cs @@ -5,7 +5,8 @@ namespace OpenEphys.Onix1 { /// - /// A class that contains 3D orientation data produced by a Bosch BNO055 9-axis inertial measurement unit (IMU). + /// A class that contains 3D orientation data produced by a Bosch BNO055 9-axis inertial measurement unit + /// (IMU). /// public class Bno055DataFrame : DataFrame { @@ -51,14 +52,14 @@ internal unsafe Bno055DataFrame(ulong clock, Bno055DataPayload* payload) /// /// Gets the 3D orientation in Euler angle format with units of degrees. /// - /// + /// /// The Tait-Bryan formalism is used: /// /// Yaw: 0 to 360 degrees. /// Roll: -180 to 180 degrees /// Pitch: -90 to 90 degrees /// - /// + /// public Vector3 EulerAngle { get; } /// diff --git a/OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs b/OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs index cb35c4d1..74497aa9 100644 --- a/OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs +++ b/OpenEphys.Onix1/ConfigureUclaMiniscopeV4.cs @@ -4,25 +4,56 @@ namespace OpenEphys.Onix1 { + /// + /// Configures a UCLA Miniscope V4 on the specified port. + /// + /// + /// The UCLA Miniscope V4 is a e miniaturized fluorescent microscope for performing single-photon calcium + /// imaging in freely moving animals. It has the following features: + /// + /// A Python-480 0.48 Megapixel CMOS image sensor. + /// A BNO055 9-axis IMU for real-time, 3D orientation tracking. + /// An electrowetting lens for remote focal plane adjustment. + /// An excitation LED with adjustable brightness control and optional exposure-driven + /// interleaving to reduce photobleaching. + /// + /// public class ConfigureUclaMiniscopeV4 : MultiDeviceFactory { PortName port; readonly ConfigureUclaMiniscopeV4PortController PortControl = new(); + /// + /// Initialize a new instance of a class. + /// public ConfigureUclaMiniscopeV4() { Port = PortName.PortA; PortControl.HubConfiguration = HubConfiguration.Passthrough; } + /// + /// Gets or sets the Miniscope camera configuration. + /// [Category(DevicesCategory)] [TypeConverter(typeof(SingleDeviceFactoryConverter))] public ConfigureUclaMiniscopeV4Camera Camera { get; set; } = new(); + /// + /// Gets or sets the Bno055 9-axis inertial measurement unit configuration. + /// [Category(DevicesCategory)] [TypeConverter(typeof(SingleDeviceFactoryConverter))] public ConfigureUclaMiniscopeV4Bno055 Bno055 { get; set; } = new(); + /// + /// Gets or sets the port. + /// + /// + /// The port is the physical connection to the ONIX breakout board and must be specified prior to operation. + /// + [Description("Specifies the physical connection of the miniscope to the ONIX breakout board.")] + [Category(ConfigurationCategory)] public PortName Port { get { return port; } @@ -36,9 +67,26 @@ public PortName Port } } + /// + /// Gets or sets the port voltage override. + /// + /// + /// + /// If defined, it will override automated voltage discovery and apply the specified voltage to the miniscope. + /// If left blank, an automated headstage detection algorithm will attempt to communicate with the miniscope and + /// apply an appropriate voltage for stable operation. Because ONIX allows any coaxial tether to be used, some of + /// which are thin enough to result in a significant voltage drop, its may be required to manually specify the + /// port voltage. + /// + /// + /// Warning: this device requires 5.0V to 6.0V, measured at the miniscope, for proper operation. Supplying higher + /// voltages may result in damage. + /// + /// [Description("If defined, it will override automated voltage discovery and apply the specified voltage" + - "to the headstage. Warning: this device requires 5.0V to 6.0V for proper operation." + - "Supplying higher voltages may result in damage to the headstage.")] + "to the miniscope. Warning: this device requires 5.0V to 6.0V for proper operation." + + "Supplying higher voltages may result in damage to the miniscope.")] + [Category(ConfigurationCategory)] public double? PortVoltage { get => PortControl.PortVoltage; diff --git a/OpenEphys.Onix1/ConfigureUclaMiniscopeV4Bno055.cs b/OpenEphys.Onix1/ConfigureUclaMiniscopeV4Bno055.cs index 77400a3e..230bf0c5 100644 --- a/OpenEphys.Onix1/ConfigureUclaMiniscopeV4Bno055.cs +++ b/OpenEphys.Onix1/ConfigureUclaMiniscopeV4Bno055.cs @@ -3,17 +3,41 @@ namespace OpenEphys.Onix1 { + /// + /// Configures the Bno055 inertial measurement unit (IMU) on a UCLA Miniscope V4. + /// public class ConfigureUclaMiniscopeV4Bno055 : SingleDeviceFactory { + /// + /// Initialize a new instance of a class. + /// public ConfigureUclaMiniscopeV4Bno055() : base(typeof(UclaMiniscopeV4Bno055)) { } + /// + /// Gets or sets the device enable state. + /// + /// + /// If set to true, will produce data. If set to false, + /// will not produce data. + /// [Category(ConfigurationCategory)] [Description("Specifies whether the BNO055 device is enabled.")] public bool Enable { get; set; } = true; + /// + /// Configures the Bno055 inertial measurement unit (IMU) on a UCLA Miniscope V4. + /// + /// + /// This will schedule configuration actions to be applied by a node + /// prior to data acquisition. + /// + /// A sequence of instances that holds all configuration actions. + /// + /// The original sequence but with each instance now containing configuration actions required to use the miniscope's Bno055 IMU. + /// public override IObservable Process(IObservable source) { var enable = Enable; @@ -25,7 +49,7 @@ public override IObservable Process(IObservable source var device = context.GetPassthroughDeviceContext(deviceAddress, typeof(DS90UB9x)); ConfigureDeserializer(device); ConfigureBno055(device); - var deviceInfo = new DeviceInfo(context, DeviceType, deviceAddress); + var deviceInfo = new UclaMiniscopeV4Bno055DeviceInfo(context, DeviceType, deviceAddress, enable); return DeviceManager.RegisterDevice(deviceName, deviceInfo); }); } @@ -66,4 +90,15 @@ public NameConverter() } } } + + class UclaMiniscopeV4Bno055DeviceInfo : DeviceInfo + { + public UclaMiniscopeV4Bno055DeviceInfo(ContextTask context, Type deviceType, uint deviceAddress, bool enable) + : base(context, deviceType, deviceAddress) + { + Enable = enable; + } + + public bool Enable { get; } + } } diff --git a/OpenEphys.Onix1/ConfigureUclaMiniscopeV4Camera.cs b/OpenEphys.Onix1/ConfigureUclaMiniscopeV4Camera.cs index 03557318..5e99aff8 100644 --- a/OpenEphys.Onix1/ConfigureUclaMiniscopeV4Camera.cs +++ b/OpenEphys.Onix1/ConfigureUclaMiniscopeV4Camera.cs @@ -7,26 +7,46 @@ namespace OpenEphys.Onix1 { + /// + /// Configures the camera on a UCLA Miniscope V4. + /// public class ConfigureUclaMiniscopeV4Camera : SingleDeviceFactory { readonly BehaviorSubject ledBrightness = new(0); readonly BehaviorSubject sensorGain = new(UclaMiniscopeV4SensorGain.Low); readonly BehaviorSubject liquidLensVoltage = new(47); // NB: middle of range + /// + /// Initialize a new instance of a class. + /// public ConfigureUclaMiniscopeV4Camera() : base(typeof(UclaMiniscopeV4)) { } + /// + /// Gets or sets a value indicating whether the camera will produce image data. + /// + /// + /// If set to true, will produce image data. If set to false, will not produce image data. + /// [Category(ConfigurationCategory)] [Description("Specifies whether the camera is enabled.")] public bool Enable { get; set; } = true; + /// + /// Gets or sets the camera video rate in frames per second. + /// [Category(ConfigurationCategory)] [Description("Camera video rate in frames per second.")] public UclaMiniscopeV4FramesPerSecond FrameRate { get; set; } = UclaMiniscopeV4FramesPerSecond.Fps30Hz; + /// + /// Gets or sets the camera sensor's analog gain. + /// [Description("Camera sensor analog gain.")] + [Category(AcquisitionCategory)] [Editor(DesignTypes.SliderEditor, typeof(UITypeEditor))] public UclaMiniscopeV4SensorGain SensorGain { @@ -34,11 +54,24 @@ public UclaMiniscopeV4SensorGain SensorGain set => sensorGain.OnNext(value); } + /// + /// Gets or sets a value indicating whether the excitation LED should turn on only when the camera + /// shutter is open. + /// + /// + /// If set to true, the excitation LED will turn on briefly before, and turn off briefly after, the + /// camera begins photon collection on its photodiode array. If set to false, the excitation LED will + /// remain on at all times. + /// [Category(ConfigurationCategory)] [Description("Only turn on excitation LED during camera exposures.")] public bool InterleaveLed { get; set; } = false; - [Description("Excitation LED brightness (0-100%).")] + /// + /// Gets or sets the excitation LED brightness level (0-100%). + /// + [Description("Excitation LED brightness level (0-100%).")] + [Category(AcquisitionCategory)] [Range(0, 100)] [Precision(1, 1)] [Editor(DesignTypes.SliderEditor, typeof(UITypeEditor))] @@ -48,7 +81,18 @@ public double LedBrightness set => ledBrightness.OnNext(value); } - [Description("Liquid lens voltage (Volts RMS).")] + /// + /// Gets or sets the liquid lens driver voltage (Volts RMS). + /// + /// + /// The imaging focal plane is controlled by using a MAX14574 high-voltage liquid lens driver. This + /// chip produces pulse-width modulated, 1 kHz alternative electric field that deforms the miniscope's + /// liquid lens in order to change the focal plane. The strength of this field determines the degree + /// of deformation and therefore the focal depth. The default setting of 47 Volts RMS corresponds to + /// approximately mid-range. + /// + [Description("Liquid lens driver voltage (Volts RMS).")] + [Category(AcquisitionCategory)] [Range(24.4, 69.7)] [Precision(1, 1)] [Editor(DesignTypes.SliderEditor, typeof(UITypeEditor))] @@ -58,6 +102,19 @@ public double LiquidLensVoltage set => liquidLensVoltage.OnNext(value); } + /// + /// Configures the camera on a UCLA Miniscope V4. + /// + /// + /// This will schedule configuration actions to be applied by a node + /// prior to data acquisition. + /// + /// A sequence of instances that holds all + /// configuration actions. + /// + /// The original sequence but with each instance now containing + /// configuration actions required to use the miniscope's camera. + /// public override IObservable Process(IObservable source) { var enable = Enable; @@ -78,6 +135,8 @@ public override IObservable Process(IObservable source { // configure device via the DS90UB9x deserializer device var device = context.GetPassthroughDeviceContext(deviceAddress, typeof(DS90UB9x)); + + // TODO: early exit if false? device.WriteRegister(DS90UB9x.ENABLE, enable ? 1u : 0); // configure deserializer, chip states, and camera PLL @@ -89,7 +148,6 @@ public override IObservable Process(IObservable source WriteCameraRegister(atMega, 200, shutterWidth); var deviceInfo = new DeviceInfo(context, DeviceType, deviceAddress); - var disposable = DeviceManager.RegisterDevice(deviceName, deviceInfo); var shutdown = Disposable.Create(() => { // turn off EWL @@ -104,8 +162,8 @@ public override IObservable Process(IObservable source ledBrightness.Subscribe(value => SetLedBrightness(device, value)), sensorGain.Subscribe(value => SetSensorGain(device, value)), liquidLensVoltage.Subscribe(value => SetLiquidLensVoltage(device, value)), - shutdown, - disposable); + DeviceManager.RegisterDevice(deviceName, deviceInfo), + shutdown); }); } @@ -118,8 +176,8 @@ internal static void ConfigureMiniscope(DeviceContext device) device.WriteRegister(DS90UB9x.SYNCBITS, 0); device.WriteRegister(DS90UB9x.DATAGATE, (uint)DS90UB9xDataGate.VsyncPositive); - // NB: This is required because Bonsai is not garuenteed to capure every frame at the start of acqusition. - // For this reason, the frame start needs to be marked. + // NB: This is required because Bonsai is not guaranteed to capture every frame at the start of + // acquisition. For this reason, the frame start needs to be marked. device.WriteRegister(DS90UB9x.MARK, (uint)DS90UB9xMarkMode.VsyncRising); // configure deserializer I2C aliases @@ -152,13 +210,13 @@ internal static void ConfigureMiniscope(DeviceContext device) // turn on LED and setup Python480 var atMega = new I2CRegisterContext(device, UclaMiniscopeV4.AtMegaAddress); WriteCameraRegister(atMega, 16, 3); // Turn on PLL - WriteCameraRegister(atMega, 32, 0x7007); // Turn on clock managment + WriteCameraRegister(atMega, 32, 0x7007); // Turn on clock management WriteCameraRegister(atMega, 199, 666); // Defines granularity (unit = 1/PLL clock) of exposure and reset_length WriteCameraRegister(atMega, 200, 3300); // Set frame rate to 30 Hz WriteCameraRegister(atMega, 201, 3000); // Set Exposure } - private static void WriteCameraRegister(I2CRegisterContext i2c, uint register, uint value) + static void WriteCameraRegister(I2CRegisterContext i2c, uint register, uint value) { // ATMega -> Python480 passthrough protocol var regLow = register & 0xFF; @@ -173,28 +231,22 @@ private static void WriteCameraRegister(I2CRegisterContext i2c, uint register, u internal static void SetLedBrightness(DeviceContext device, double percent) { - var des = device.Context.GetPassthroughDeviceContext(device.Address, typeof(DS90UB9x)); - - var atMega = new I2CRegisterContext(des, UclaMiniscopeV4.AtMegaAddress); + var atMega = new I2CRegisterContext(device, UclaMiniscopeV4.AtMegaAddress); atMega.WriteByte(0x01, (uint)((percent == 0) ? 0xFF : 0x08)); - var tpl0102 = new I2CRegisterContext(des, UclaMiniscopeV4.Tpl0102Address); + var tpl0102 = new I2CRegisterContext(device, UclaMiniscopeV4.Tpl0102Address); tpl0102.WriteByte(0x01, (uint)(255 * ((100 - percent) / 100.0))); } internal static void SetSensorGain(DeviceContext device, UclaMiniscopeV4SensorGain gain) { - var des = device.Context.GetPassthroughDeviceContext(device.Address, typeof(DS90UB9x)); - - var atMega = new I2CRegisterContext(des, UclaMiniscopeV4.AtMegaAddress); + var atMega = new I2CRegisterContext(device, UclaMiniscopeV4.AtMegaAddress); WriteCameraRegister(atMega, 204, (uint)gain); } internal static void SetLiquidLensVoltage(DeviceContext device, double voltage) { - var des = device.Context.GetPassthroughDeviceContext(device.Address, typeof(DS90UB9x)); - - var max14574 = new I2CRegisterContext(des, UclaMiniscopeV4.Max14574Address); + var max14574 = new I2CRegisterContext(device, UclaMiniscopeV4.Max14574Address); max14574.WriteByte(0x08, (uint)((voltage - 24.4) / 0.0445) >> 2); max14574.WriteByte(0x09, 0x02); } @@ -218,19 +270,55 @@ public NameConverter() } } + /// + /// Specifies analog gain of the Python-480 image sensor on a UCLA Miniscope V4. + /// public enum UclaMiniscopeV4SensorGain { + /// + /// Specifies low gain. + /// Low = 0x00E1, + + /// + /// Specifies medium gain. + /// Medium = 0x00E4, + + /// + /// Specifies high gain. + /// High = 0x0024, } + /// + /// Specifies the video frame rate of the Python-480 image sensor on a UCLA Miniscope V4. + /// public enum UclaMiniscopeV4FramesPerSecond { + /// + /// Specifies 10 frames per second. + /// Fps10Hz, + + /// + /// Specifies 15 frames per second. + /// Fps15Hz, + + /// + /// Specifies 20 frames per second. + /// Fps20Hz, + + /// + /// Specifies 25 frames per second. + /// Fps25Hz, + + /// + /// Specifies 30 frames per second. + /// Fps30Hz, } } diff --git a/OpenEphys.Onix1/ContextTask.cs b/OpenEphys.Onix1/ContextTask.cs index 1cdc27c6..9b66b002 100644 --- a/OpenEphys.Onix1/ContextTask.cs +++ b/OpenEphys.Onix1/ContextTask.cs @@ -92,7 +92,7 @@ private void Initialize() DeviceTable = ctx.DeviceTable; } - private void Reset() + internal void Reset() { lock (disposeLock) lock (regLock) diff --git a/OpenEphys.Onix1/NeuropixelsV2eBno055Data.cs b/OpenEphys.Onix1/NeuropixelsV2eBno055Data.cs index 28b7955c..97958af4 100644 --- a/OpenEphys.Onix1/NeuropixelsV2eBno055Data.cs +++ b/OpenEphys.Onix1/NeuropixelsV2eBno055Data.cs @@ -23,8 +23,8 @@ public class NeuropixelsV2eBno055Data : Source /// /// A sequence of objects. /// - /// This will generate a sequence of objects at approximately 100 Hz. This rate - /// may be limited by the I2C bus. + /// This will generate a sequence of s at approximately 100 Hz. This rate + /// may be limited by the hardware I2C bus. /// public override IObservable Generate() { @@ -34,9 +34,16 @@ public override IObservable Generate() } /// - /// Generates a sequence of objects. + /// Generates a sequence of s. /// - /// A sequence of objects. + /// An input sequence that drives the production of s + /// A sequence of s. + /// + /// A will be produced each time an element is received from the + /// sequence. This rate is limited by the hardware I2C bus and has a + /// maximum of 100 Hz. + /// public unsafe IObservable Generate(IObservable source) { return DeviceManager.GetDevice(DeviceName).SelectMany( diff --git a/OpenEphys.Onix1/UclaMiniscopeV4Bno055Data.cs b/OpenEphys.Onix1/UclaMiniscopeV4Bno055Data.cs index a45cfaee..877467bb 100644 --- a/OpenEphys.Onix1/UclaMiniscopeV4Bno055Data.cs +++ b/OpenEphys.Onix1/UclaMiniscopeV4Bno055Data.cs @@ -6,11 +6,26 @@ namespace OpenEphys.Onix1 { + /// + /// Produces a sequence of s from the Bno055 9-axis inertial measurement unit + /// on a UCLA Miniscope V4. + /// public class UclaMiniscopeV4Bno055Data : Source { + /// [TypeConverter(typeof(UclaMiniscopeV4Bno055.NameConverter))] + [Description(SingleDeviceFactory.DeviceNameDescription)] + [Category(DeviceFactory.ConfigurationCategory)] public string DeviceName { get; set; } + /// + /// Generates a sequence of s at approximately 100 Hz. + /// + /// A sequence of s. + /// + /// This will generate a sequence of s at approximately 100 Hz. This rate + /// may be limited by the hardware I2C bus. + /// public override IObservable Generate() { // Max of 100 Hz, but limited by I2C bus @@ -18,24 +33,35 @@ public override IObservable Generate() return Generate(source); } + /// + /// Generates a sequence of s. + /// + /// An input sequence that drives the production of s + /// A sequence of s. + /// + /// A will be produced each time an element is received from the + /// sequence. This rate is limited by the hardware I2C bus and has a + /// maximum of 100 Hz. + /// public unsafe IObservable Generate(IObservable source) { return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo => { - return !((NeuropixelsV2eBno055DeviceInfo)deviceInfo).Enable + return !((UclaMiniscopeV4Bno055DeviceInfo)deviceInfo).Enable ? Observable.Empty() : Observable.Create(observer => { - var device = deviceInfo.GetDeviceContext(typeof(NeuropixelsV2eBno055)); + var device = deviceInfo.GetDeviceContext(typeof(UclaMiniscopeV4Bno055)); var passthrough = device.GetPassthroughDeviceContext(typeof(DS90UB9x)); - var i2c = new I2CRegisterContext(passthrough, NeuropixelsV2eBno055.BNO055Address); + var i2c = new I2CRegisterContext(passthrough, UclaMiniscopeV4Bno055.BNO055Address); return source.SubscribeSafe(observer, _ => { Bno055DataFrame frame = default; device.Context.EnsureContext(() => { - var data = i2c.ReadBytes(NeuropixelsV2eBno055.DataAddress, sizeof(Bno055DataPayload)); + var data = i2c.ReadBytes(UclaMiniscopeV4Bno055.DataAddress, sizeof(Bno055DataPayload)); ulong clock = passthrough.ReadRegister(DS90UB9x.LASTI2CL); clock += (ulong)passthrough.ReadRegister(DS90UB9x.LASTI2CH) << 32; fixed (byte* dataPtr = data) diff --git a/OpenEphys.Onix1/UclaMiniscopeV4Camera.cs b/OpenEphys.Onix1/UclaMiniscopeV4Camera.cs deleted file mode 100644 index 15acbf1a..00000000 --- a/OpenEphys.Onix1/UclaMiniscopeV4Camera.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.ComponentModel; -using System.Linq; -using System.Reactive; -using System.Reactive.Linq; -using System.Runtime.InteropServices; -using Bonsai; -using OpenCV.Net; - -namespace OpenEphys.Onix1 -{ - public class UclaMiniscopeV4Camera : Source - { - [TypeConverter(typeof(UclaMiniscopeV4.NameConverter))] - public string DeviceName { get; set; } - - public unsafe override IObservable Generate() - { - return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo => - { - var info = (NeuropixelsV2eDeviceInfo)deviceInfo; - var device = info.GetDeviceContext(typeof(NeuropixelsV2e)); - var passthrough = device.GetPassthroughDeviceContext(typeof(DS90UB9x)); - var scopeData = device.Context.GetDeviceFrames(passthrough.Address); - - return Observable.Create(observer => - { - var sampleIndex = 0; - var imageBuffer = new short[UclaMiniscopeV4.SensorRows * UclaMiniscopeV4.SensorColumns]; - var hubClockBuffer = new ulong[UclaMiniscopeV4.SensorRows]; - var clockBuffer = new ulong[UclaMiniscopeV4.SensorRows]; - var awaitingFrameStart = true; - - var frameObserver = Observer.Create( - frame => - { - var payload = (UclaMiniscopeV4ImagerPayload*)frame.Data.ToPointer(); - - // Wait for first row - if (awaitingFrameStart && (payload->ImageRow[0] & 0x8000) == 0) - return; - - awaitingFrameStart = false; - Marshal.Copy(new IntPtr(payload->ImageRow), imageBuffer, sampleIndex * UclaMiniscopeV4.SensorColumns, UclaMiniscopeV4.SensorColumns); - hubClockBuffer[sampleIndex] = payload->HubClock; - clockBuffer[sampleIndex] = frame.Clock; - if (++sampleIndex >= UclaMiniscopeV4.SensorRows) - { - var imageData = Mat.FromArray(imageBuffer, UclaMiniscopeV4.SensorRows, UclaMiniscopeV4.SensorColumns, Depth.U16, 1); - CV.ConvertScale(imageData.GetRow(0), imageData.GetRow(0), 1.0f, -32768.0f); // Get rid first row's mark bit - observer.OnNext(new UclaMiniscopeV4Image(clockBuffer, hubClockBuffer, imageData.GetImage())); - hubClockBuffer = new ulong[UclaMiniscopeV4.SensorRows]; - clockBuffer = new ulong[UclaMiniscopeV4.SensorRows]; - sampleIndex = 0; - awaitingFrameStart = true; - } - }, - observer.OnError, - observer.OnCompleted); - - return scopeData.SubscribeSafe(frameObserver); - }); - }); - } - } -} diff --git a/OpenEphys.Onix1/UclaMiniscopeV4Image.cs b/OpenEphys.Onix1/UclaMiniscopeV4Image.cs deleted file mode 100644 index d53d8df9..00000000 --- a/OpenEphys.Onix1/UclaMiniscopeV4Image.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Runtime.InteropServices; -using OpenCV.Net; - -namespace OpenEphys.Onix1 -{ - public class UclaMiniscopeV4Image - { - public UclaMiniscopeV4Image(ulong[] clock, ulong[] hubClock, IplImage image) - { - Clock = clock; - HubClock = hubClock; - Image = image; - } - - public ulong[] Clock { get; } - - public ulong[] HubClock { get; } - - public IplImage Image { get; } - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - unsafe struct UclaMiniscopeV4ImagerPayload - { - public ulong HubClock; - public fixed short ImageRow[UclaMiniscopeV4.SensorColumns]; - } -}