-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #254 from open-ephys/issue-70-2
Support for headstage-nric1384-203390
- Loading branch information
Showing
10 changed files
with
749 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Threading; | ||
|
||
namespace OpenEphys.Onix1 | ||
{ | ||
/// <summary> | ||
/// Configures a Nric1384 headstage on the specified port. | ||
/// </summary> | ||
/// <remarks> | ||
/// The Nric1384 Headstage is a 2.5g serialized, multifunction headstage for small animals built around the | ||
/// IMEC Nric1384 bioacquisition chip. This headstage is designed to function with passive probes of the | ||
/// user's choosing (e.g. silicon probe arrays, high-density tetrode drives, etc). It provides the | ||
/// following features: | ||
/// <list type="bullet"> | ||
/// <item><description>384 analog ephys channels sampled at 30 kHz per channel and exposed via an array of | ||
/// 12x ultra-high density Molex 203390-0323 quad-row connectors. </description></item> | ||
/// <item><description>A BNO055 9-axis IMU for real-time, 3D orientation tracking at 100 | ||
/// Hz.</description></item> | ||
/// <item><description>Two TS4231 light to digital converters for real-time, 3D position tracking with HTC | ||
/// Vive base stations.</description></item> | ||
/// <item><description>A single electrical stimulator (current controlled, +/-15V compliance, automatic | ||
/// electrode discharge).</description></item> | ||
/// </list> | ||
/// </remarks> | ||
[Description("Configures a Nric1384 Headstage headstage.")] | ||
public class ConfigureHeadstageNric1384 : MultiDeviceFactory | ||
{ | ||
PortName port; | ||
readonly ConfigureHeadstageNric1384PortController PortControl = new(); | ||
|
||
/// <summary> | ||
/// Initialize a new instance of a <see cref="ConfigureHeadstageNric1384"/> class. | ||
/// </summary> | ||
public ConfigureHeadstageNric1384() | ||
{ | ||
Port = PortName.PortA; | ||
PortControl.HubConfiguration = HubConfiguration.Standard; | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the Nric1384 bioacquisition chip configuration. | ||
/// </summary> | ||
[Category(DevicesCategory)] | ||
[TypeConverter(typeof(SingleDeviceFactoryConverter))] | ||
[Description("Specifies the configuration for the Nric1384 bioacquisition device.")] | ||
public ConfigureNric1384 Nric1384 { get; set; } = new(); | ||
|
||
/// <summary> | ||
/// Gets or sets the BNO055 9-axis inertial measurement unit configuration. | ||
/// </summary> | ||
[Category(DevicesCategory)] | ||
[TypeConverter(typeof(SingleDeviceFactoryConverter))] | ||
[Description("Specifies the configuration for the Bno055 device.")] | ||
public ConfigureBno055 Bno055 { get; set; } = new(); | ||
|
||
/// <summary> | ||
/// Gets or sets the port. | ||
/// </summary> | ||
/// <remarks> | ||
/// The port is the physical connection to the ONIX breakout board and must be specified prior to operation. | ||
/// </remarks> | ||
[Description("Specifies the physical connection of the headstage to the ONIX breakout board.")] | ||
[Category(ConfigurationCategory)] | ||
public PortName Port | ||
{ | ||
get { return port; } | ||
set | ||
{ | ||
port = value; | ||
var offset = (uint)port << 8; | ||
PortControl.DeviceAddress = (uint)port; | ||
Nric1384.DeviceAddress = offset + 0; | ||
Bno055.DeviceAddress = offset + 1; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the port voltage. | ||
/// </summary> | ||
/// <remarks> | ||
/// <para> | ||
/// If defined, it will override automated voltage discovery and apply the specified voltage to the headstage. | ||
/// If left blank, an automated headstage detection algorithm will attempt to communicate with the headstage 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. | ||
/// </para> | ||
/// <para> | ||
/// Warning: This device requires 3.8V to 5.5V for proper operation. Voltages higher than 5.5V can | ||
/// damage the headstage. | ||
/// </para> | ||
/// </remarks> | ||
[Description("If defined, overrides automated voltage discovery and applies " + | ||
"the specified voltage to the headstage. Warning: this device requires 3.8V to 5.5V " + | ||
"for proper operation. Higher voltages can damage the headstage.")] | ||
[Category(ConfigurationCategory)] | ||
public double? PortVoltage | ||
{ | ||
get => PortControl.PortVoltage; | ||
set => PortControl.PortVoltage = value; | ||
} | ||
|
||
internal override IEnumerable<IDeviceConfiguration> GetDevices() | ||
{ | ||
yield return PortControl; | ||
yield return Nric1384; | ||
yield return Bno055; | ||
} | ||
|
||
class ConfigureHeadstageNric1384PortController : ConfigurePortController | ||
{ | ||
protected override bool ConfigurePortVoltage(DeviceContext device) | ||
{ | ||
const double MinVoltage = 3.8; | ||
const double MaxVoltage = 5.5; | ||
const double VoltageOffset = 0.7; | ||
const double VoltageIncrement = 0.2; | ||
|
||
for (var voltage = MinVoltage; voltage <= MaxVoltage; voltage += VoltageIncrement) | ||
{ | ||
SetPortVoltage(device, voltage); | ||
if (base.CheckLinkState(device)) | ||
{ | ||
SetPortVoltage(device, voltage + VoltageOffset); | ||
return CheckLinkState(device); | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private void SetPortVoltage(DeviceContext device, double voltage) | ||
{ | ||
device.WriteRegister(PortController.PORTVOLTAGE, 0); | ||
Thread.Sleep(500); | ||
device.WriteRegister(PortController.PORTVOLTAGE, (uint)(10 * voltage)); | ||
Thread.Sleep(500); | ||
} | ||
|
||
protected override bool CheckLinkState(DeviceContext device) | ||
{ | ||
// NB: needs an additional reset after power on to provide its device table. | ||
device.Context.Reset(); | ||
var linkState = device.ReadRegister(PortController.LINKSTATE); | ||
return (linkState & PortController.LINKSTATE_SL) != 0; | ||
} | ||
|
||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
using System; | ||
using System.ComponentModel; | ||
using Bonsai; | ||
|
||
namespace OpenEphys.Onix1 | ||
{ | ||
/// <summary> | ||
/// Configures a Nric184 bioacquisition chip. | ||
/// </summary> | ||
public class ConfigureNric1384 : SingleDeviceFactory | ||
{ | ||
/// <summary> | ||
/// Initialize a new instance of a <see cref="ConfigureNric1384"/> class. | ||
/// </summary> | ||
public ConfigureNric1384() | ||
: base(typeof(Nric1384)) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the device enable state. | ||
/// </summary> | ||
/// <remarks> | ||
/// If set to true, <see cref="Nric1384Data"/> will produce data. If set to false, | ||
/// <see cref="Nric1384Data"/> will not produce data. | ||
/// </remarks> | ||
[Category(ConfigurationCategory)] | ||
[Description("Specifies whether the Nric1384 data stream is enabled.")] | ||
public bool Enable { get; set; } = true; | ||
|
||
/// <summary> | ||
/// Gets or sets the amplifier gain for the spike-band. | ||
/// </summary> | ||
/// <remarks> | ||
/// The spike-band is from DC to 10 kHz if <see cref="SpikeFilter"/> is set to false, while the | ||
/// spike-band is from 300 Hz to 10 kHz if <see cref="SpikeFilter"/> is set to true. | ||
/// </remarks> | ||
[Category(ConfigurationCategory)] | ||
[Description("Amplifier gain for spike-band.")] | ||
public NeuropixelsV1Gain SpikeAmplifierGain { get; set; } = NeuropixelsV1Gain.Gain1000; | ||
|
||
/// <summary> | ||
/// Gets or sets the amplifier gain for the LFP-band. | ||
/// </summary> | ||
/// <remarks> | ||
/// The LFP band is from 0.5 to 500 Hz. | ||
/// </remarks> | ||
[Category(ConfigurationCategory)] | ||
[Description("Amplifier gain for LFP-band.")] | ||
public NeuropixelsV1Gain LfpAmplifierGain { get; set; } = NeuropixelsV1Gain.Gain50; | ||
|
||
/// <summary> | ||
/// Gets or sets the state of the spike-band filter. | ||
/// </summary> | ||
/// <remarks> | ||
/// If set to true, the spike-band has a 300 Hz high-pass filter which will be activated. If set to | ||
/// false, the high-pass filter will not to be activated. | ||
/// </remarks> | ||
[Category(ConfigurationCategory)] | ||
[Description("If true, activates a 300 Hz high-pass in the spike-band data stream.")] | ||
public bool SpikeFilter { get; set; } = true; | ||
|
||
/// <summary> | ||
/// Gets or sets the path to the gain calibration file. | ||
/// </summary> | ||
/// <remarks> | ||
/// <para> | ||
/// Each chip is linked to a gain calibration file that contains gain adjustments determined by IMEC during | ||
/// factory testing. Electrode voltages are scaled using these values to ensure they can be accurately compared | ||
/// across chips. Therefore, using the correct gain calibration file is mandatory to create standardized recordings. | ||
/// </para> | ||
/// <para> | ||
/// Calibration files are chip-specific and not interchangeable across chips. Calibration files must contain the | ||
/// serial number of the corresponding chip on their first line of text. If you have lost track of a calibration | ||
/// file for your chip, email IMEC at neuropixels.info@imec.be with the chip serial number to retrieve a new copy. | ||
/// </para> | ||
/// </remarks> | ||
[FileNameFilter("Gain calibration files (*_gainCalValues.csv)|*_gainCalValues.csv")] | ||
[Description("Path to the Nric1384 gain calibraiton file.")] | ||
[Editor("Bonsai.Design.OpenFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)] | ||
public string GainCalibrationFile { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the path to the ADC calibration file. | ||
/// </summary> | ||
/// <remarks> | ||
/// <para> | ||
/// Each chip must be provided with an ADC calibration file that contains chip-specific hardware settings that is | ||
/// created by IMEC during factory calibration. These files are used to set internal bias currents, correct for ADC | ||
/// nonlinearities, correct ADC-zero crossing non-monotonicities, etc. Using the correct calibration file is mandatory | ||
/// for the chip to operate correctly. | ||
/// </para> | ||
/// <para> | ||
/// Calibration files are chip-specific and not interchangeable across chips. Calibration files must contain the | ||
/// serial number of the corresponding chip on their first line of text. If you have lost track of a calibration | ||
/// file for your chip, email IMEC at neuropixels.info@imec.be with the chip serial number to retrieve a new copy. | ||
/// </para> | ||
/// </remarks> | ||
[FileNameFilter("ADC calibration files (*_ADCCalibration.csv)|*_ADCCalibration.csv")] | ||
[Description("Path to the Nric1384 ADC calibraiton file.")] | ||
[Editor("Bonsai.Design.OpenFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)] | ||
public string AdcCalibrationFile { get; set; } | ||
|
||
/// <summary> | ||
/// Configures a Nric1384 bioacquisition device. | ||
/// </summary> | ||
/// <remarks> | ||
/// This will schedule configuration actions to be applied by a <see cref="StartAcquisition"/> node | ||
/// prior to data acquisition. | ||
/// </remarks> | ||
/// <param name="source">A sequence of <see cref="ContextTask"/> that holds all configuration actions.</param> | ||
/// <returns> | ||
/// The original sequence with the side effect of an additional configuration action to configure | ||
/// a Nric1384 device. | ||
/// </returns> | ||
public override IObservable<ContextTask> Process(IObservable<ContextTask> source) | ||
{ | ||
var enable = Enable; | ||
var deviceName = DeviceName; | ||
var deviceAddress = DeviceAddress; | ||
return source.ConfigureDevice(context => | ||
{ | ||
var device = context.GetDeviceContext(deviceAddress, typeof(Nric1384)); | ||
device.WriteRegister(Nric1384.ENABLE, enable ? 1u : 0); | ||
if (enable) | ||
{ | ||
var probeControl = new Nric1384RegisterContext(device, SpikeAmplifierGain, LfpAmplifierGain, SpikeFilter, GainCalibrationFile, AdcCalibrationFile); | ||
probeControl.InitializeChip(); | ||
probeControl.WriteShiftRegisters(); | ||
} | ||
return DeviceManager.RegisterDevice(deviceName, device, DeviceType); | ||
}); | ||
} | ||
} | ||
|
||
static class Nric1384 | ||
{ | ||
public const int ID = 33; | ||
|
||
public const int I2cAddress = 0x70; | ||
public const int ChannelCount = 384; | ||
public const int ElectrodeCount = 384; | ||
|
||
// managed registers | ||
public const uint ENABLE = 0x8000; // Enable or disable the data output stream | ||
public const uint ADC00_OFF_THRESH = 0x8001; // ADC 0 offset and threshold parameters: [6-bit ADC 00 Offset, 10-bit ADC 00 Threshold] | ||
public const uint ADC01_OFF_THRESH = 0x8002; | ||
public const uint ADC02_OFF_THRESH = 0x8003; | ||
public const uint ADC03_OFF_THRESH = 0x8004; | ||
public const uint ADC04_OFF_THRESH = 0x8005; | ||
public const uint ADC05_OFF_THRESH = 0x8006; | ||
public const uint ADC06_OFF_THRESH = 0x8007; | ||
public const uint ADC07_OFF_THRESH = 0x8008; | ||
public const uint ADC08_OFF_THRESH = 0x8009; | ||
public const uint ADC09_OFF_THRESH = 0x800a; | ||
public const uint ADC10_OFF_THRESH = 0x800b; | ||
public const uint ADC11_OFF_THRESH = 0x800c; | ||
public const uint ADC12_OFF_THRESH = 0x800d; | ||
public const uint ADC13_OFF_THRESH = 0x800e; | ||
public const uint ADC14_OFF_THRESH = 0x800f; | ||
public const uint ADC15_OFF_THRESH = 0x8010; | ||
public const uint ADC16_OFF_THRESH = 0x8011; | ||
public const uint ADC17_OFF_THRESH = 0x8012; | ||
public const uint ADC18_OFF_THRESH = 0x8013; | ||
public const uint ADC19_OFF_THRESH = 0x8014; | ||
public const uint ADC20_OFF_THRESH = 0x8015; | ||
public const uint ADC21_OFF_THRESH = 0x8016; | ||
public const uint ADC22_OFF_THRESH = 0x8017; | ||
public const uint ADC23_OFF_THRESH = 0x8018; | ||
public const uint ADC24_OFF_THRESH = 0x8019; | ||
public const uint ADC25_OFF_THRESH = 0x801a; | ||
public const uint ADC26_OFF_THRESH = 0x801b; | ||
public const uint ADC27_OFF_THRESH = 0x801c; | ||
public const uint ADC28_OFF_THRESH = 0x801d; | ||
public const uint ADC29_OFF_THRESH = 0x801e; | ||
public const uint ADC30_OFF_THRESH = 0x801f; | ||
public const uint ADC31_OFF_THRESH = 0x8020; // ADC 31 offset and threshold parameters: [6-bit ADC 31 Offset , 10-bit ADC 31 Threshold] | ||
public const uint LFP_GAIN = 0x8021; // LFP gain correction parameter: [X Q1.14] | ||
public const uint AP_GAIN = 0x8022; // AP gain correction parameter: [X Q1.14] | ||
|
||
// unmanaged regiseters | ||
public const uint OP_MODE = 0X00; | ||
public const uint REC_MOD = 0X01; | ||
public const uint CAL_MOD = 0X02; | ||
public const uint STATUS = 0X08; | ||
public const uint SYNC = 0X09; | ||
public const uint SR_CHAIN3 = 0X0C; // Odd channels | ||
public const uint SR_CHAIN2 = 0X0D; // Even channels | ||
public const uint SR_LENGTH2 = 0X0F; | ||
public const uint SR_LENGTH1 = 0X10; | ||
public const uint SOFT_RESET = 0X11; | ||
|
||
internal class NameConverter : DeviceNameConverter | ||
{ | ||
public NameConverter() | ||
: base(typeof(Nric1384)) | ||
{ | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.