diff --git a/OpenEphys.Onix1.Design/NeuropixelsV1eDialog.cs b/OpenEphys.Onix1.Design/NeuropixelsV1eDialog.cs index e831865..72ac286 100644 --- a/OpenEphys.Onix1.Design/NeuropixelsV1eDialog.cs +++ b/OpenEphys.Onix1.Design/NeuropixelsV1eDialog.cs @@ -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) { diff --git a/OpenEphys.Onix1/ConfigureNric1384.cs b/OpenEphys.Onix1/ConfigureNric1384.cs index c37d7cc..6b195a1 100644 --- a/OpenEphys.Onix1/ConfigureNric1384.cs +++ b/OpenEphys.Onix1/ConfigureNric1384.cs @@ -5,7 +5,7 @@ namespace OpenEphys.Onix1 { /// - /// Configures a Nric184 bioacquisition device. + /// Configures a Nric184 bioacquisition chip. /// public class ConfigureNric1384 : SingleDeviceFactory { @@ -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 diff --git a/OpenEphys.Onix1/NeuropixelsV1Helper.cs b/OpenEphys.Onix1/NeuropixelsV1Helper.cs index 7344e2d..7da1bf6 100644 --- a/OpenEphys.Onix1/NeuropixelsV1Helper.cs +++ b/OpenEphys.Onix1/NeuropixelsV1Helper.cs @@ -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), @@ -99,13 +99,13 @@ public static class NeuropixelsV1Helper /// Current for the AP data. /// Current for the LFP data. /// object that contains the AP and LFP gain correction values. This object is null if the file was not successfully parsed. - public static NeuropixelsV1eGainCorrection? TryParseGainCalibrationFile(string gainCalibrationFile, NeuropixelsV1Gain apGain, NeuropixelsV1Gain lfpGain) + public static NeuropixelsV1eGainCorrection? TryParseGainCalibrationFile(string gainCalibrationFile, NeuropixelsV1Gain apGain, NeuropixelsV1Gain lfpGain, int electrodeCount) { 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 @@ -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 diff --git a/OpenEphys.Onix1/NeuropixelsV1eRegisterContext.cs b/OpenEphys.Onix1/NeuropixelsV1eRegisterContext.cs index d44d51f..f51b490 100644 --- a/OpenEphys.Onix1/NeuropixelsV1eRegisterContext.cs +++ b/OpenEphys.Onix1/NeuropixelsV1eRegisterContext.cs @@ -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) { diff --git a/OpenEphys.Onix1/Nric1384RegisterContext.cs b/OpenEphys.Onix1/Nric1384RegisterContext.cs index fe9c8d4..b04eba7 100644 --- a/OpenEphys.Onix1/Nric1384RegisterContext.cs +++ b/OpenEphys.Onix1/Nric1384RegisterContext.cs @@ -1,6 +1,6 @@ using System; using System.Collections; -using System.Linq; +using System.IO; namespace OpenEphys.Onix1 { @@ -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; @@ -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++) { @@ -100,6 +90,8 @@ public Nric1384RegisterContext(DeviceContext deviceContext, NeuropixelsV1Gain ap } + Adcs = adcCalibration.Value.Adcs; + int k = 0; foreach (var adc in Adcs) { @@ -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]; - } } @@ -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); @@ -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))); } } }