Skip to content

Commit

Permalink
Make use of NeuropixelsV1Helper in Nric1384 configuration
Browse files Browse the repository at this point in the history
- Remove some redundancy when parsing calibration files
- Fix error in NeuropixelsV1eAdcCalibration that is preventing any valid
  file from being parsed
  • Loading branch information
jonnew committed Sep 16, 2024
1 parent 6f74071 commit 4ab4c87
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 64 deletions.
3 changes: 2 additions & 1 deletion OpenEphys.Onix1.Design/NeuropixelsV1eDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ private void CheckStatus()
{
gainCorrection = NeuropixelsV1Helper.TryParseGainCalibrationFile(ConfigureNode.GainCalibrationFile,
ConfigureNode.ProbeConfiguration.SpikeAmplifierGain,
ConfigureNode.ProbeConfiguration.LfpAmplifierGain);
ConfigureNode.ProbeConfiguration.LfpAmplifierGain,
960);
}
catch (IOException ex)
{
Expand Down
4 changes: 3 additions & 1 deletion OpenEphys.Onix1/ConfigureNric1384.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace OpenEphys.Onix1
{
/// <summary>
/// Configures a Nric184 bioacquisition device.
/// Configures a Nric184 bioacquisition chip.
/// </summary>
public class ConfigureNric1384 : SingleDeviceFactory
{
Expand Down Expand Up @@ -141,6 +141,8 @@ 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
Expand Down
8 changes: 4 additions & 4 deletions OpenEphys.Onix1/NeuropixelsV1Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static class NeuropixelsV1Helper
.Where(l => l.Ok)
.Select(l => l.Param);
return calibrationValues.Count() == NumberOfAdcParameters
return calibrationValues.Count() != NumberOfAdcParameters
? null
: new NeuropixelsV1eAdc {
CompP = calibrationValues.ElementAt(0),
Expand Down Expand Up @@ -99,13 +99,13 @@ public static class NeuropixelsV1Helper
/// <param name="apGain">Current <see cref="NeuropixelsV1Gain"/> for the AP data.</param>
/// <param name="lfpGain">Current <see cref="NeuropixelsV1Gain"/> for the LFP data.</param>
/// <returns><see cref="NeuropixelsV1eGainCorrection"/> object that contains the AP and LFP gain correction values. This object is null if the file was not successfully parsed.</returns>
public static NeuropixelsV1eGainCorrection? TryParseGainCalibrationFile(string gainCalibrationFile, NeuropixelsV1Gain apGain, NeuropixelsV1Gain lfpGain)
public static NeuropixelsV1eGainCorrection? TryParseGainCalibrationFile(string gainCalibrationFile, NeuropixelsV1Gain apGain, NeuropixelsV1Gain lfpGain, int electrodeCount)

Check warning on line 102 in OpenEphys.Onix1/NeuropixelsV1Helper.cs

View workflow job for this annotation

GitHub Actions / build (debug, ubuntu-latest)

Parameter 'electrodeCount' has no matching param tag in the XML comment for 'NeuropixelsV1Helper.TryParseGainCalibrationFile(string, NeuropixelsV1Gain, NeuropixelsV1Gain, int)' (but other parameters do)

Check warning on line 102 in OpenEphys.Onix1/NeuropixelsV1Helper.cs

View workflow job for this annotation

GitHub Actions / build (debug, ubuntu-latest)

Parameter 'electrodeCount' has no matching param tag in the XML comment for 'NeuropixelsV1Helper.TryParseGainCalibrationFile(string, NeuropixelsV1Gain, NeuropixelsV1Gain, int)' (but other parameters do)

Check warning on line 102 in OpenEphys.Onix1/NeuropixelsV1Helper.cs

View workflow job for this annotation

GitHub Actions / build (debug, windows-latest)

Parameter 'electrodeCount' has no matching param tag in the XML comment for 'NeuropixelsV1Helper.TryParseGainCalibrationFile(string, NeuropixelsV1Gain, NeuropixelsV1Gain, int)' (but other parameters do)

Check warning on line 102 in OpenEphys.Onix1/NeuropixelsV1Helper.cs

View workflow job for this annotation

GitHub Actions / build (debug, windows-latest)

Parameter 'electrodeCount' has no matching param tag in the XML comment for 'NeuropixelsV1Helper.TryParseGainCalibrationFile(string, NeuropixelsV1Gain, NeuropixelsV1Gain, int)' (but other parameters do)

Check warning on line 102 in OpenEphys.Onix1/NeuropixelsV1Helper.cs

View workflow job for this annotation

GitHub Actions / build (release, ubuntu-latest)

Parameter 'electrodeCount' has no matching param tag in the XML comment for 'NeuropixelsV1Helper.TryParseGainCalibrationFile(string, NeuropixelsV1Gain, NeuropixelsV1Gain, int)' (but other parameters do)

Check warning on line 102 in OpenEphys.Onix1/NeuropixelsV1Helper.cs

View workflow job for this annotation

GitHub Actions / build (release, ubuntu-latest)

Parameter 'electrodeCount' has no matching param tag in the XML comment for 'NeuropixelsV1Helper.TryParseGainCalibrationFile(string, NeuropixelsV1Gain, NeuropixelsV1Gain, int)' (but other parameters do)

Check warning on line 102 in OpenEphys.Onix1/NeuropixelsV1Helper.cs

View workflow job for this annotation

GitHub Actions / build (release, windows-latest)

Parameter 'electrodeCount' has no matching param tag in the XML comment for 'NeuropixelsV1Helper.TryParseGainCalibrationFile(string, NeuropixelsV1Gain, NeuropixelsV1Gain, int)' (but other parameters do)

Check warning on line 102 in OpenEphys.Onix1/NeuropixelsV1Helper.cs

View workflow job for this annotation

GitHub Actions / build (release, windows-latest)

Parameter 'electrodeCount' has no matching param tag in the XML comment for 'NeuropixelsV1Helper.TryParseGainCalibrationFile(string, NeuropixelsV1Gain, NeuropixelsV1Gain, int)' (but other parameters do)
{
if (!File.Exists(gainCalibrationFile)) return null;

var lines = File.ReadLines(gainCalibrationFile);

if (lines.Count() != NeuropixelsV1e.ElectrodeCount + 1) return null;
if (lines.Count() != electrodeCount + 1) return null;
if (!ulong.TryParse(lines.ElementAt(0), out var serialNumber)) return null;

if (!lines
Expand All @@ -117,7 +117,7 @@ public static class NeuropixelsV1Helper
})
.Where(l => l.Ok)
.Select(l => l.Channel)
.SequenceEqual(Enumerable.Range(0, NeuropixelsV1e.ElectrodeCount))) return null;
.SequenceEqual(Enumerable.Range(0, electrodeCount))) return null;

var apIndex = Array.IndexOf(Enum.GetValues(typeof(NeuropixelsV1Gain)), apGain);
var apGainCorrections = lines
Expand Down
3 changes: 2 additions & 1 deletion OpenEphys.Onix1/NeuropixelsV1eRegisterContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ public NeuropixelsV1eRegisterContext(DeviceContext deviceContext, uint i2cAddres
$"match the ADC calibration file serial number: {adcCalibration.Value.SerialNumber}.");
}

var gainCorrection = NeuropixelsV1Helper.TryParseGainCalibrationFile(gainCalibrationFile, probeConfiguration.SpikeAmplifierGain, probeConfiguration.LfpAmplifierGain);
var gainCorrection = NeuropixelsV1Helper.TryParseGainCalibrationFile(gainCalibrationFile,
probeConfiguration.SpikeAmplifierGain, probeConfiguration.LfpAmplifierGain, NeuropixelsV1e.ElectrodeCount);

if (!gainCorrection.HasValue)
{
Expand Down
92 changes: 35 additions & 57 deletions OpenEphys.Onix1/Nric1384RegisterContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections;
using System.Linq;
using System.IO;

namespace OpenEphys.Onix1
{
Expand All @@ -13,7 +13,6 @@ class Nric1384RegisterContext : I2CRegisterContext
const byte ReferenceSource = 0b001; // All, hardcoded
const int BaseConfigurationBitCount = 2448;
const int BaseConfigurationConfigOffset = 576;
const int NumberOfGains = 8;
const uint ShiftRegisterSuccess = 1 << 7;

readonly DeviceContext device;
Expand All @@ -24,51 +23,42 @@ class Nric1384RegisterContext : I2CRegisterContext
public Nric1384RegisterContext(DeviceContext deviceContext, NeuropixelsV1Gain apGain, NeuropixelsV1Gain lfpGain, bool apFilter, string gainCalibrationFile, string adcCalibrationFile)
: base(deviceContext, Nric1384.I2cAddress)
{

device = deviceContext;

if (gainCalibrationFile == null || adcCalibrationFile == null)
if (!File.Exists(gainCalibrationFile))
{
throw new ArgumentException("Calibration files must be specified.");
throw new ArgumentException("A gain calibration file must be specified for the Nric1384 chip.");
}

System.IO.StreamReader gainFile = new(gainCalibrationFile);
var sn = UInt64.Parse(gainFile.ReadLine());

System.IO.StreamReader adcFile = new(adcCalibrationFile);
if (sn != UInt64.Parse(adcFile.ReadLine()))
throw new ArgumentException("Calibration file serial numbers do not match.");
if (!File.Exists(adcCalibrationFile))
{
throw new ArgumentException("An ADC calibration file must be specified for the Nric1384 chip.");
}

// parse gain correction file
var gainCorrections = gainFile.ReadLine().Split(',').Skip(1);
var adcCalibration = NeuropixelsV1Helper.TryParseAdcCalibrationFile(adcCalibrationFile);

if (gainCorrections.Count() != 2 * NumberOfGains)
throw new ArgumentException("Incorrectly formatted gain correction calibration file.");
if (!adcCalibration.HasValue)
{
throw new ArgumentException($"The calibration file \"{adcCalibrationFile}\" is invalid.");
}

ApGainCorrection = double.Parse(gainCorrections.ElementAt(Array.IndexOf(Enum.GetValues(typeof(NeuropixelsV1Gain)), apGain)));
LfpGainCorrection = double.Parse(gainCorrections.ElementAt(Array.IndexOf(Enum.GetValues(typeof(NeuropixelsV1Gain)), lfpGain) + 8));
var gainCorrection = NeuropixelsV1Helper.TryParseGainCalibrationFile(gainCalibrationFile,apGain, lfpGain, Nric1384.ElectrodeCount);

// parse ADC calibration file
for (var i = 0; i < NeuropixelsV1e.AdcCount; i++)
if (!gainCorrection.HasValue)
{
var adcCal = adcFile.ReadLine().Split(',').Skip(1);
if (adcCal.Count() != NumberOfGains)
{
throw new ArgumentException("Incorrectly formatted ADC calibration file.");
}
throw new ArgumentException($"The calibration file \"{gainCalibrationFile}\" is invalid.");
}

Adcs[i] = new NeuropixelsV1eAdc
{
CompP = int.Parse(adcCal.ElementAt(0)),
CompN = int.Parse(adcCal.ElementAt(1)),
Slope = int.Parse(adcCal.ElementAt(2)),
Coarse = int.Parse(adcCal.ElementAt(3)),
Fine = int.Parse(adcCal.ElementAt(4)),
Cfix = int.Parse(adcCal.ElementAt(5)),
Offset = int.Parse(adcCal.ElementAt(6)),
Threshold = int.Parse(adcCal.ElementAt(7))
};
if (adcCalibration.Value.SerialNumber != gainCorrection.Value.SerialNumber)
{
throw new ArgumentException($"The ADC calibration file's serial number ({adcCalibration.Value.SerialNumber}) " +
$"does not match the gain calibration file's serial number ({gainCorrection.Value.SerialNumber}).");
}

ApGainCorrection = gainCorrection.Value.ApGainCorrectionFactor;
LfpGainCorrection = gainCorrection.Value.LfpGainCorrectionFactor;

// create shift-register bit arrays
for (int i = 0; i < NeuropixelsV1e.ChannelCount; i++)
{
Expand Down Expand Up @@ -100,6 +90,8 @@ public Nric1384RegisterContext(DeviceContext deviceContext, NeuropixelsV1Gain ap

}

Adcs = adcCalibration.Value.Adcs;

int k = 0;
foreach (var adc in Adcs)
{
Expand Down Expand Up @@ -172,7 +164,6 @@ public Nric1384RegisterContext(DeviceContext deviceContext, NeuropixelsV1Gain ap
BaseConfigs[configIdx][slopeOffset + 8] = cfix[1];
BaseConfigs[configIdx][slopeOffset + 9] = cfix[2];
BaseConfigs[configIdx][slopeOffset + 10] = cfix[3];

}
}

Expand Down Expand Up @@ -201,7 +192,7 @@ public void WriteShiftRegisters()

for (int j = 0; j < 2; j++)
{
var baseBytes = BitArrayToBytes(BaseConfigs[i]);
var baseBytes = BitHelper.ToBitReversedBytes(BaseConfigs[i]);

WriteByte(Nric1384.SR_LENGTH1, (uint)baseBytes.Length % 0x100);
WriteByte(Nric1384.SR_LENGTH2, (uint)baseBytes.Length / 0x100);
Expand All @@ -218,30 +209,17 @@ public void WriteShiftRegisters()
}
}

// gain corrections
device.WriteRegister(Nric1384.LFP_GAIN, (uint)(LfpGainCorrection * (1 << 14)));
device.WriteRegister(Nric1384.AP_GAIN, (uint)(ApGainCorrection * (1 << 14)));
}

// Bits go into the shift registers MSB first
// This creates a *bit-reversed* byte array from a bit array
private static byte[] BitArrayToBytes(BitArray bits)
{
if (bits.Length == 0)
// write adc thresholds and offsets
for (uint i = 0; i < Adcs.Length; i++)
{
throw new ArgumentException("Shift register data is empty", nameof(bits));
var thresh = (uint)Adcs[i].Threshold;
var offset = (uint)Adcs[i].Offset;
device.WriteRegister(Nric1384.ADC00_OFF_THRESH + i, offset << 10 | thresh);
}

var bytes = new byte[(bits.Length - 1) / 8 + 1];
bits.CopyTo(bytes, 0);

for (int i = 0; i < bytes.Length; i++)
{
// NB: http://graphics.stanford.edu/~seander/bithacks.html
bytes[i] = (byte)((bytes[i] * 0x0202020202ul & 0x010884422010ul) % 1023);
}

return bytes;
// gain corrections
device.WriteRegister(Nric1384.LFP_GAIN, (uint)(LfpGainCorrection * (1 << 14)));
device.WriteRegister(Nric1384.AP_GAIN, (uint)(ApGainCorrection * (1 << 14)));
}
}
}

0 comments on commit 4ab4c87

Please sign in to comment.