diff --git a/Directory.Build.props b/Directory.Build.props
index 4c611c6a..20ad22c5 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -13,7 +13,7 @@
LICENSE
true
icon.png
- 0.6.1
+ 0.7.0
10.0
strict
diff --git a/OpenEphys.Onix1/ConfigureNeuropixelsV1e.cs b/OpenEphys.Onix1/ConfigureNeuropixelsV1e.cs
index 021a3e82..350ae4d9 100644
--- a/OpenEphys.Onix1/ConfigureNeuropixelsV1e.cs
+++ b/OpenEphys.Onix1/ConfigureNeuropixelsV1e.cs
@@ -183,7 +183,7 @@ public override IObservable Process(IObservable 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);
diff --git a/OpenEphys.Onix1/Neuropixels.cs b/OpenEphys.Onix1/Neuropixels.cs
new file mode 100644
index 00000000..2718db1e
--- /dev/null
+++ b/OpenEphys.Onix1/Neuropixels.cs
@@ -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();
+ 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;
+ }
+ }
+}
diff --git a/OpenEphys.Onix1/NeuropixelsV1eData.cs b/OpenEphys.Onix1/NeuropixelsV1eData.cs
index c42de3fd..0908c24f 100644
--- a/OpenEphys.Onix1/NeuropixelsV1eData.cs
+++ b/OpenEphys.Onix1/NeuropixelsV1eData.cs
@@ -44,6 +44,18 @@ public int BufferSize
set => bufferSize = (int)(Math.Ceiling((double)value / NeuropixelsV1.FramesPerRoundRobin) * NeuropixelsV1.FramesPerRoundRobin);
}
+ ///
+ /// Gets or sets a boolean value that controls if the channels are ordered by depth.
+ ///
+ ///
+ /// If is false, then channels are ordered from 0 to 383.
+ /// If is true, then channels are ordered based on the depth
+ /// of the electrodes
+ ///
+ [Description("Determines if the channels are returned ordered by depth.")]
+ [Category(DeviceFactory.ConfigurationCategory)]
+ public bool OrderByDepth { get; set; } = false;
+
///
/// Generates a sequence of objects.
///
@@ -52,6 +64,7 @@ public unsafe override IObservable Generate()
{
var spikeBufferSize = BufferSize;
var lfpBufferSize = spikeBufferSize / NeuropixelsV1.FramesPerRoundRobin;
+ var orderByDepth = OrderByDepth;
return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo =>
{
@@ -59,6 +72,7 @@ public unsafe override IObservable Generate()
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(observer =>
{
@@ -73,7 +87,7 @@ public unsafe override IObservable 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)
@@ -93,5 +107,43 @@ public unsafe override IObservable 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 } };
}
}
diff --git a/OpenEphys.Onix1/NeuropixelsV1eDataFrame.cs b/OpenEphys.Onix1/NeuropixelsV1eDataFrame.cs
index 82b71024..2adcbf77 100644
--- a/OpenEphys.Onix1/NeuropixelsV1eDataFrame.cs
+++ b/OpenEphys.Onix1/NeuropixelsV1eDataFrame.cs
@@ -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);
@@ -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"
@@ -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);
@@ -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"
@@ -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);
@@ -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)]
diff --git a/OpenEphys.Onix1/NeuropixelsV1eDeviceInfo.cs b/OpenEphys.Onix1/NeuropixelsV1eDeviceInfo.cs
index 50a54f5e..30eb5685 100644
--- a/OpenEphys.Onix1/NeuropixelsV1eDeviceInfo.cs
+++ b/OpenEphys.Onix1/NeuropixelsV1eDeviceInfo.cs
@@ -4,7 +4,7 @@ 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;
@@ -12,6 +12,7 @@ public NeuropixelsV1eDeviceInfo(ContextTask context, Type deviceType, uint devic
AdcThresholds = probeControl.AdcThresholds;
AdcOffsets = probeControl.AdcOffsets;
InvertPolarity = invertPolarity;
+ ProbeConfiguration = probeConfiguration;
}
public double ApGainCorrection { get; }
@@ -19,5 +20,6 @@ public NeuropixelsV1eDeviceInfo(ContextTask context, Type deviceType, uint devic
public ushort[] AdcThresholds { get; }
public ushort[] AdcOffsets { get; }
public bool InvertPolarity { get; }
+ public NeuropixelsV1ProbeConfiguration ProbeConfiguration { get; }
}
}