Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<UseArtifactsOutput>true</UseArtifactsOutput>
<PackageIcon>icon.png</PackageIcon>
<VersionPrefix>0.6.1</VersionPrefix>
<VersionPrefix>0.7.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
<LangVersion>10.0</LangVersion>
<Features>strict</Features>
Expand Down
2 changes: 1 addition & 1 deletion OpenEphys.Onix1/ConfigureNeuropixelsV1e.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public override IObservable<ContextTask> Process(IObservable<ContextTask> source
TurnOnLed(serializer, NeuropixelsV1e.DefaultGPO32Config);
}

var deviceInfo = new NeuropixelsV1eDeviceInfo(context, DeviceType, deviceAddress, probeControl, invertPolarity);
var deviceInfo = new NeuropixelsV1eDeviceInfo(context, DeviceType, deviceAddress, probeControl, invertPolarity, ProbeConfiguration);
var shutdown = Disposable.Create(() =>
{
serializer.WriteByte((uint)DS90UB933SerializerI2CRegister.Gpio10, NeuropixelsV1e.DefaultGPO10Config);
Expand Down
41 changes: 41 additions & 0 deletions OpenEphys.Onix1/Neuropixels.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace OpenEphys.Onix1
{
static class Neuropixels
{
internal static int[,] OrderChannelsByDepth(Electrode[] channelMap, int[,] frameIndex)
{
int rows = frameIndex.GetLength(0);
int cols = frameIndex.GetLength(1);

var channelToPosition = new Dictionary<int, (int row, int col)>();
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
channelToPosition[frameIndex[row, col]] = (row, col);
}
}

var spatiallyOrdered = channelMap
.OrderBy(x => x.Position.Y)
.ThenBy(x => x.Position.X)
.ToArray();

var spatialRawToChannel = new int[rows, cols];
int index = 0;

foreach (var e in spatiallyOrdered)
{
var (origRow, origCol) = channelToPosition[e.Channel];

spatialRawToChannel[origRow, origCol] = index++;
}

return spatialRawToChannel;
}
}
}
54 changes: 53 additions & 1 deletion OpenEphys.Onix1/NeuropixelsV1eData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ public int BufferSize
set => bufferSize = (int)(Math.Ceiling((double)value / NeuropixelsV1.FramesPerRoundRobin) * NeuropixelsV1.FramesPerRoundRobin);
}

/// <summary>
/// Gets or sets a boolean value that controls if the channels are ordered by depth.
/// </summary>
/// <remarks>
/// If <see cref="OrderByDepth"/> is false, then channels are ordered from 0 to 383.
/// If <see cref="OrderByDepth"/> is true, then channels are ordered based on the depth
/// of the electrodes
/// </remarks>
[Description("Determines if the channels are returned ordered by depth.")]
[Category(DeviceFactory.ConfigurationCategory)]
public bool OrderByDepth { get; set; } = false;

/// <summary>
/// Generates a sequence of <see cref="NeuropixelsV1DataFrame"/> objects.
/// </summary>
Expand All @@ -52,13 +64,15 @@ public unsafe override IObservable<NeuropixelsV1DataFrame> Generate()
{
var spikeBufferSize = BufferSize;
var lfpBufferSize = spikeBufferSize / NeuropixelsV1.FramesPerRoundRobin;
var orderByDepth = OrderByDepth;

return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo =>
{
var info = (NeuropixelsV1eDeviceInfo)deviceInfo;
var device = info.GetDeviceContext(typeof(NeuropixelsV1e));
var passthrough = device.GetPassthroughDeviceContext(typeof(DS90UB9x));
var probeData = device.Context.GetDeviceFrames(passthrough.Address);
int[,] channelOrder = orderByDepth ? Neuropixels.OrderChannelsByDepth(info.ProbeConfiguration.ChannelMap, RawToChannel) : RawToChannel;

return Observable.Create<NeuropixelsV1DataFrame>(observer =>
{
Expand All @@ -73,7 +87,7 @@ public unsafe override IObservable<NeuropixelsV1DataFrame> Generate()
frame =>
{
var payload = (NeuropixelsV1ePayload*)frame.Data.ToPointer();
NeuropixelsV1eDataFrame.CopyAmplifierBuffer(payload->AmplifierData, frameCountBuffer, spikeBuffer, lfpBuffer, sampleIndex, info.ApGainCorrection, info.LfpGainCorrection, info.AdcThresholds, info.AdcOffsets, info.InvertPolarity);
NeuropixelsV1eDataFrame.CopyAmplifierBuffer(payload->AmplifierData, frameCountBuffer, spikeBuffer, lfpBuffer, sampleIndex, info.ApGainCorrection, info.LfpGainCorrection, info.AdcThresholds, info.AdcOffsets, info.InvertPolarity, channelOrder);
hubClockBuffer[sampleIndex] = payload->HubClock;
clockBuffer[sampleIndex] = frame.Clock;
if (++sampleIndex >= spikeBufferSize)
Expand All @@ -93,5 +107,43 @@ public unsafe override IObservable<NeuropixelsV1DataFrame> Generate()
});
});
}

// ADC to channel
// First dimension: ADC index
// Second dimension: frame index within super frame
// Output: channel number
static readonly int[,] RawToChannel = {
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22 },
{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 },
{24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46 },
{25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47 },
{48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 },
{49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71 },
{72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94 },
{73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95 },
{96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118 },
{97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119 },
{120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142 },
{121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143 },
{144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166 },
{145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167 },
{168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190 },
{169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191 },
{192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214 },
{193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215 },
{216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238 },
{217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239 },
{240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262 },
{241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263 },
{264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286 },
{265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287 },
{288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310 },
{289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311 },
{312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334 },
{313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335 },
{336, 338, 340, 342, 344, 346, 348, 350, 352, 354, 356, 358 },
{337, 339, 341, 343, 345, 347, 349, 351, 353, 355, 357, 359 },
{360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382 },
{361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, 383 } };
}
}
48 changes: 5 additions & 43 deletions OpenEphys.Onix1/NeuropixelsV1eDataFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace OpenEphys.Onix1
{
class NeuropixelsV1eDataFrame
{
internal static unsafe void CopyAmplifierBuffer(ushort* amplifierData, int[] frameCountBuffer, ushort[,] spikeBuffer, ushort[,] lfpBuffer, int index, double apGainCorrection, double lfpGainCorrection, ushort[] thresholds, ushort[] offsets, bool invertPolarity)
internal static unsafe void CopyAmplifierBuffer(ushort* amplifierData, int[] frameCountBuffer, ushort[,] spikeBuffer, ushort[,] lfpBuffer, int index, double apGainCorrection, double lfpGainCorrection, ushort[] thresholds, ushort[] offsets, bool invertPolarity, int[,] channelOrder)
{
var frameCountStartIndex = index * NeuropixelsV1.FramesPerSuperFrame;
frameCountBuffer[frameCountStartIndex] = (amplifierData[31] << 10) | (amplifierData[39] << 0);
Expand All @@ -23,7 +23,7 @@ internal static unsafe void CopyAmplifierBuffer(ushort* amplifierData, int[] fra
for (int k = 0; k < NeuropixelsV1.AdcCount; k++)
{
var a = amplifierData[adcToFrameIndex[k]];
lfpBuffer[RawToChannel[k, lfpFrameIndex], lfpBufferIndex] = (ushort)(lfpInversionOffset - lfpGainCorrection * (a > thresholds[k] ? a - offsets[k] : a));
lfpBuffer[channelOrder[k, lfpFrameIndex], lfpBufferIndex] = (ushort)(lfpInversionOffset - lfpGainCorrection * (a > thresholds[k] ? a - offsets[k] : a));
}

// Loop over 12 AP frames within each "super-frame"
Expand All @@ -35,7 +35,7 @@ internal static unsafe void CopyAmplifierBuffer(ushort* amplifierData, int[] fra
for (int k = 0; k < NeuropixelsV1.AdcCount; k++)
{
var a = amplifierData[adcToFrameIndex[k] + adcDataOffset];
spikeBuffer[RawToChannel[k, i], index] = (ushort)(apInversionOffset - apGainCorrection * (a > thresholds[k] ? a - offsets[k] : a));
spikeBuffer[channelOrder[k, i], index] = (ushort)(apInversionOffset - apGainCorrection * (a > thresholds[k] ? a - offsets[k] : a));
}

frameCountBuffer[frameCountStartIndex + i + 1] = (amplifierData[adcDataOffset + 31] << 10) | (amplifierData[adcDataOffset + 39] << 0);
Expand All @@ -46,7 +46,7 @@ internal static unsafe void CopyAmplifierBuffer(ushort* amplifierData, int[] fra
for (int k = 0; k < NeuropixelsV1.AdcCount; k++)
{
var a = amplifierData[adcToFrameIndex[k]];
lfpBuffer[RawToChannel[k, lfpFrameIndex], lfpBufferIndex] = (ushort)(lfpGainCorrection * (a > thresholds[k] ? a - offsets[k] : a));
lfpBuffer[channelOrder[k, lfpFrameIndex], lfpBufferIndex] = (ushort)(lfpGainCorrection * (a > thresholds[k] ? a - offsets[k] : a));
}

// Loop over 12 AP frames within each "super-frame"
Expand All @@ -58,7 +58,7 @@ internal static unsafe void CopyAmplifierBuffer(ushort* amplifierData, int[] fra
for (int k = 0; k < NeuropixelsV1.AdcCount; k++)
{
var a = amplifierData[adcToFrameIndex[k] + adcDataOffset];
spikeBuffer[RawToChannel[k, i], index] = (ushort)(apGainCorrection * (a > thresholds[k] ? a - offsets[k] : a));
spikeBuffer[channelOrder[k, i], index] = (ushort)(apGainCorrection * (a > thresholds[k] ? a - offsets[k] : a));
}

frameCountBuffer[frameCountStartIndex + i + 1] = (amplifierData[adcDataOffset + 31] << 10) | (amplifierData[adcDataOffset + 39] << 0);
Expand All @@ -76,44 +76,6 @@ internal static unsafe void CopyAmplifierBuffer(ushort* amplifierData, int[] fra
5, 13, 21, 29, 37,
6, 14, 22, 30, 38,
7, 15 };

// ADC to channel
// First dimension: ADC index
// Second dimension: frame index within super frame
// Output: channel number
static readonly int[,] RawToChannel = {
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22 },
{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 },
{24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46 },
{25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47 },
{48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 },
{49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71 },
{72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94 },
{73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95 },
{96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118 },
{97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119 },
{120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142 },
{121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143 },
{144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166 },
{145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167 },
{168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190 },
{169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191 },
{192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214 },
{193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215 },
{216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238 },
{217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239 },
{240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262 },
{241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263 },
{264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286 },
{265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287 },
{288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310 },
{289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311 },
{312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334 },
{313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335 },
{336, 338, 340, 342, 344, 346, 348, 350, 352, 354, 356, 358 },
{337, 339, 341, 343, 345, 347, 349, 351, 353, 355, 357, 359 },
{360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382 },
{361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, 383 } };
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
Expand Down
4 changes: 3 additions & 1 deletion OpenEphys.Onix1/NeuropixelsV1eDeviceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@ namespace OpenEphys.Onix1
{
class NeuropixelsV1eDeviceInfo : DeviceInfo
{
public NeuropixelsV1eDeviceInfo(ContextTask context, Type deviceType, uint deviceAddress, NeuropixelsV1eRegisterContext probeControl, bool invertPolarity)
public NeuropixelsV1eDeviceInfo(ContextTask context, Type deviceType, uint deviceAddress, NeuropixelsV1eRegisterContext probeControl, bool invertPolarity, NeuropixelsV1ProbeConfiguration probeConfiguration)
: base(context, deviceType, deviceAddress)
{
ApGainCorrection = probeControl.ApGainCorrection;
LfpGainCorrection = probeControl.LfpGainCorrection;
AdcThresholds = probeControl.AdcThresholds;
AdcOffsets = probeControl.AdcOffsets;
InvertPolarity = invertPolarity;
ProbeConfiguration = probeConfiguration;
}

public double ApGainCorrection { get; }
public double LfpGainCorrection { get; }
public ushort[] AdcThresholds { get; }
public ushort[] AdcOffsets { get; }
public bool InvertPolarity { get; }
public NeuropixelsV1ProbeConfiguration ProbeConfiguration { get; }
}
}