Skip to content

Commit

Permalink
Add python data loading script for neuropixels 1.0e headstage workflow (
Browse files Browse the repository at this point in the history
#135)

* Add spike interface python data loading script for np1, np2 headstage workflows

- save files with suffic in workflows
- also refactor some np hardware UIDs

---------

Co-authored-by: cjsha <[email protected]>
  • Loading branch information
jonnew and cjsha authored Nov 18, 2024
1 parent feeb22b commit c3b118c
Show file tree
Hide file tree
Showing 15 changed files with 495 additions and 42 deletions.
17 changes: 17 additions & 0 deletions articles/hardware/np1e/load-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
uid: np1e_load-data
title: Load NeuropixelsV1e Headstage Data
---

The following python script can be used to load and plot the data produced by the NeuropixelsV1e Headstage [example workflow](xref:np1e).

[!code-python[](../../../workflows/hardware/np1e/load-np1e.py)]

> [!NOTE]
> To plot probeinterface data, [save the probe configuration file](xref:np1e_gui#save-probeinterface-file) into the same directory of your data.
> [!NOTE]
> This script will attempt to load entire files into arrays. For long recordings, data will need to
> be split into more manageable chunks by:
> - Modifying this script to partially load files
> - Modifying the workflow to cyclically create new files after a certain duration
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
uid: np1e_npv1e
uid: np1e_np1
title: NeuropixelsV1e
hardware: NeuropixelsV1e Headstage
---

The following excerpt from the NeuropixelsV1e Headstage [example workflow](xref:np1e_npv1e-headstage) demonstrates NeuropixelsV1e functionality by streaming and saving probe data.
The following excerpt from the NeuropixelsV1e Headstage [example workflow](xref:np1e) demonstrates NeuropixelsV1e functionality by streaming and saving probe data.

::: workflow
![/workflows/hardware/breakout/np1.bonsai workflow](../../../workflows/hardware/np1e/np1.bonsai)
![/workflows/hardware/np1e/np1.bonsai workflow](../../../workflows/hardware/np1e/np1.bonsai)
:::

The <xref:OpenEphys.Onix1.NeuropixelsV1eData> operator generates a sequence of <xref:OpenEphys.Onix1.NeuropixelsV1DataFrame>s using the following settings:
Expand Down
4 changes: 2 additions & 2 deletions articles/hardware/np1e/overview.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
uid: np1e_npv1e-headstage
uid: np1e
title: NeuropixelsV1e Headstage
---

These are the devices available on the NeuropixelsV1e Headstage:

- One [NeuropixelsV1e](xref:np1e_npv1e):
- One [NeuropixelsV1e](xref:np1e_np1):
- A single 1cm long shank probe with a 70 x 24 µm shank cross-section.
- 960-electrode low-impedance TiN electrodes total.
- 384 parallel, dual-band (AP, LFP), low-noise recording channels.
Expand Down
17 changes: 17 additions & 0 deletions articles/hardware/np2e/load-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
uid: np2e_load-data
title: Load NeuropixelsV2e Headstage Data
---

The following python script can be used to load and plot the data produced by the NeuropixelsV1e Headstage [example workflow](xref:np2e).

[!code-python[](../../../workflows/hardware/np2e/load-np2e.py)]

> [!NOTE]
> To plot probeinterface data, [save the probe configuration file](xref:np2e_gui#save-probeinterface-file) into the same directory of your data.
> [!NOTE]
> This script will attempt to load entire files into arrays. For long recordings, data will need to
> be split into more manageable chunks by:
> - Modifying this script to partially load files
> - Modifying the workflow to cyclically create new files after a certain duration
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
uid: np2e_npv2e
uid: np2e_np2
title: NeuropixelsV2e
hardware: NeuropixelsV2e Headstage
---
Expand All @@ -10,7 +10,7 @@ hardware: NeuropixelsV2e Headstage
The following excerpt from the NeuropixelsV2e Headstage [example workflow](xref:np2e) demonstrates Neuropixels 2.0 probe functionality by streaming data and saves Neuropixels 2.0 probe data. The second chain is disabled by default, assuming that only one probe is connected to the headstage. If two probes are connected, the second `NeuropixelsV2eData` chain can be enabled to stream data from both probes simultaneously. To enable, select all nodes in the disabled chain and press <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>D</kbd>, or click `Enable` right-clicking the selected nodes.

::: workflow
![/workflows/hardware/breakout/np2.bonsai workflow](../../../workflows/hardware/np2e/np2.bonsai)
![/workflows/hardware/np2e/np2.bonsai workflow](../../../workflows/hardware/np2e/np2.bonsai)
:::

The <xref:OpenEphys.Onix1.NeuropixelsV2eData> operator generates a sequence of <xref:OpenEphys.Onix1.NeuropixelsV2eDataFrame>s. In the NeuropixelsV2e Headstage example workflow,
Expand Down
2 changes: 1 addition & 1 deletion articles/hardware/np2e/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: NeuropixelsV2e Headstage

These are the devices available on the NeuropixelsV2e Headstage:

- Supports up to two [IMEC Neuropixels 2.0 probes](xref:np2e_npv2e):
- Supports up to two [IMEC Neuropixels 2.0 probes](xref:np2e_np2):
- Either 1x or 4x silicon shanks with a 70 x 24 µm cross-section.
- 1280 electrodes low-impedance TiN electrodes per shank (5120 total for quad-shank probes).
- 384 parallel, full-band (AP, LFP), low-noise recording channels.
Expand Down
5 changes: 3 additions & 2 deletions articles/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,19 @@
- name: Port Status
href: hardware/np1e/port-status.md
- name: Neuropixels 1.0 Data
href: hardware/np1e/npv1e.md
href: hardware/np1e/np1.md
- name: Orientation Data & Commutation
href: hardware/np1e/bno055.md
- href: hardware/np1e/gui.md
- href: hardware/np1e/load-data.md
- href: hardware/np2e/overview.md
items:
- name: Configuration
href: hardware/np2e/configuration.md
- name: Port Status
href: hardware/np2e/port-status.md
- name: Neuropixels 2.0 Data
href: hardware/np2e/npv2e.md
href: hardware/np2e/np2.md
- name: Orientation Data & Commutation
href: hardware/np2e/bno055.md
- href: hardware/np2e/gui.md
Expand Down
5 changes: 3 additions & 2 deletions workflows/hardware/np1e/bno055.bonsai
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@
<Expression xsi:type="Combinator">
<Combinator xsi:type="onix1:PolledBno055Data">
<onix1:DeviceName>NeuropixelsV1eHeadstage/PolledBno055</onix1:DeviceName>
<onix1:PolledRegisters>All</onix1:PolledRegisters>
</Combinator>
</Expression>
<Expression xsi:type="io:CsvWriter">
<io:FileName>bno055_.csv</io:FileName>
<io:Append>false</io:Append>
<io:Overwrite>false</io:Overwrite>
<io:Suffix>Timestamp</io:Suffix>
<io:Suffix>FileCount</io:Suffix>
<io:IncludeHeader>false</io:IncludeHeader>
<io:Selector>it</io:Selector>
<io:Selector>Clock,EulerAngle,Quaternion,Acceleration,Gravity,Temperature</io:Selector>
</Expression>
<Expression xsi:type="MemberSelector">
<Selector>Quaternion</Selector>
Expand Down
82 changes: 76 additions & 6 deletions workflows/hardware/np1e/configuration.bonsai

Large diffs are not rendered by default.

127 changes: 127 additions & 0 deletions workflows/hardware/np1e/load-np1e.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import spikeinterface.extractors as se
import spikeinterface.widgets as sw
import probeinterface
import probeinterface.plotting

ap_gain = 500 # Change to the ap band gain used
lfp_gain = 500 # Change to the lfp band gain used
suffix = 0 # Change to match file names' suffix
num_channels = 384 # Decrease channels to expedite plotting and inspect fewer traces
# Change this to the directory of your data. In this example, data's in the same directory as this data loading Python script
data_directory = os.path.dirname(os.path.realpath(__file__))
mode = 'auto' # This uses colormap plot above 50 channels and line plot below 50 channels. Refer to the spikeinterface docs for more options
# Change this to the name of your probeinterface file (should be in the same directory as the rest of your data for this example script to work as-is)
probeinterface_filename = 'np1-config.json'


plt.close('all')

#%% Metadata
dt = {'names': ('time', 'acq_clk_hz', 'block_read_sz', 'block_write_sz'),
'formats': ('datetime64[us]', 'u4', 'u4', 'u4')}
meta = np.genfromtxt(os.path.join(data_directory, f'start-time_{suffix}.csv'), delimiter=',', dtype=dt, skip_header=1)
print(f"Recording was started at {meta['time']} GMT")

#%% Neuropixels 1.0 probeinterface
fig, ax = plt.subplots()
np1_config = probeinterface.io.read_probeinterface(os.path.join(data_directory, 'np1-config.json'))
contacts_colors = ['cyan' if device_channel_index > -1 else 'red' for device_channel_index in np1_config.probes[0].device_channel_indices]
probeinterface.plotting.plot_probegroup(np1_config, ax=ax, contacts_colors=contacts_colors, contacts_kargs={'alpha' : 1, 'zorder' : 10}, show_channel_on_click=True)
fig.set_size_inches(2, 9)
enabled = mpatches.Patch(color='cyan', label='Enabled')
disabled = mpatches.Patch(color='red', label='Disabled')
fig.legend(handles=[enabled, disabled], loc='outside upper center')
plt.tight_layout()


#%% Neuropixels 1.0 Data
bit_depth = 10
fig, ax = plt.subplots(1,2)
fig.suptitle('Neuropixels 1.0 Data')
fig.set_size_inches(9, 9)
plt.subplots_adjust(wspace=0.3)

ap_scalar = 1.2e6 / (2 ** bit_depth) / ap_gain
ap_offset = (2 ** (bit_depth - 1)) * ap_scalar
ap_recording = se.read_binary(os.path.join(data_directory, f"np1-spike_{suffix}.raw"),
3e5,
np.uint16,
num_channels,
gain_to_uV=ap_scalar,
offset_to_uV=-ap_offset)
ap_traces_plot = sw.plot_traces(ap_recording,
backend='matplotlib',
return_scaled=True,
mode=mode,
clim=(-ap_offset,ap_offset),
ax=ax[0])
ax[0].set_xlabel("time (sec)")
ax[0].set_ylabel("AP (µV)")

lfp_scalar = 1.2e6 / (2 ** bit_depth) / lfp_gain
lfp_offset = (2 ** (bit_depth - 1)) * lfp_scalar
lfp_recording = se.read_binary(os.path.join(data_directory, f"np1-lfp_{suffix}.raw"),
3e5/12,
np.uint16,
num_channels,
gain_to_uV=lfp_scalar,
offset_to_uV=-lfp_offset)
lfp_traces_plot = sw.plot_traces(lfp_recording,
backend='matplotlib',
return_scaled=True,
mode=mode,
clim=(-lfp_offset,lfp_offset),
ax=ax[1])
ax[1].set_xlabel("time (sec)")
ax[1].set_ylabel("LFP (µV)")

#%% Bno055
dt = {'names': ('euler', 'quat', 'is_quat_id', 'accel', 'grav', 'temp', 'calibration', 'clock'),
'formats': ('(1,3)f8', '(1,4)f8', '?', '(1,3)f8', '(1,3)f8', 'f8', '?', 'u8')}
bno055 = np.genfromtxt(os.path.join(data_directory, f'bno055_{suffix}.csv'), delimiter=',', dtype=dt)

bno055_time = bno055['clock'] / meta['acq_clk_hz']

plt.figure()
plt.subplot(231)
plt.plot(bno055_time, bno055['euler'].squeeze())
plt.xlabel("time (sec)")
plt.ylabel("angle (deg.)")
plt.ylim(-185, 365)
plt.legend(['yaw', 'pitch', 'roll'])
plt.title('Euler')

plt.subplot(232)
plt.plot(bno055_time, bno055['quat'].squeeze())
plt.xlabel("time (sec)")
plt.ylim(-1.1, 1.1)
plt.legend(['X', 'Y', 'Z', 'W'])
plt.title('Quaternion')

plt.subplot(233)
plt.plot(bno055_time, bno055['accel'].squeeze())
plt.xlabel("time (sec)")
plt.ylabel("accel. (m/s^2)")
plt.legend(['X', 'Y', 'Z'])
plt.title('Lin. Accel.')

plt.subplot(234)
plt.plot(bno055_time, bno055['grav'].squeeze())
plt.xlabel("time (sec)")
plt.ylabel("accel. (m/s^2)")
plt.legend(['X', 'Y', 'Z'])
plt.title('Gravity Vec.')

plt.subplot(235)
plt.plot(bno055_time, bno055['temp'].squeeze())
plt.xlabel("time (sec)")
plt.ylabel("temp. (°C)")
plt.title('Headstage Temp.')

plt.tight_layout()

plt.show()
31 changes: 31 additions & 0 deletions workflows/hardware/np1e/memory-monitor.bonsai
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<WorkflowBuilder Version="2.8.5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:onix1="clr-namespace:OpenEphys.Onix1;assembly=OpenEphys.Onix1"
xmlns:io="clr-namespace:Bonsai.IO;assembly=Bonsai.System"
xmlns="https://bonsai-rx.org/2018/workflow">
<Workflow>
<Nodes>
<Expression xsi:type="Combinator">
<Combinator xsi:type="onix1:MemoryMonitorData">
<onix1:DeviceName>BreakoutBoard/MemoryMonitor</onix1:DeviceName>
</Combinator>
</Expression>
<Expression xsi:type="io:CsvWriter">
<io:FileName>memory-use_.csv</io:FileName>
<io:Append>false</io:Append>
<io:Overwrite>false</io:Overwrite>
<io:Suffix>FileCount</io:Suffix>
<io:IncludeHeader>false</io:IncludeHeader>
<io:Selector>Clock,BytesUsed,PercentUsed</io:Selector>
</Expression>
<Expression xsi:type="MemberSelector">
<Selector>PercentUsed</Selector>
</Expression>
</Nodes>
<Edges>
<Edge From="0" To="1" Label="Source1" />
<Edge From="1" To="2" Label="Source1" />
</Edges>
</Workflow>
</WorkflowBuilder>
6 changes: 3 additions & 3 deletions workflows/hardware/np1e/np1.bonsai
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<Expression xsi:type="Combinator">
<Combinator xsi:type="dsp:MatrixWriter">
<dsp:Path>np1-clock_.raw</dsp:Path>
<dsp:Suffix>Timestamp</dsp:Suffix>
<dsp:Suffix>FileCount</dsp:Suffix>
<dsp:Overwrite>false</dsp:Overwrite>
<dsp:Layout>ColumnMajor</dsp:Layout>
</Combinator>
Expand All @@ -29,7 +29,7 @@
<Expression xsi:type="Combinator">
<Combinator xsi:type="dsp:MatrixWriter">
<dsp:Path>np1-spike_.raw</dsp:Path>
<dsp:Suffix>Timestamp</dsp:Suffix>
<dsp:Suffix>FileCount</dsp:Suffix>
<dsp:Overwrite>false</dsp:Overwrite>
<dsp:Layout>ColumnMajor</dsp:Layout>
</Combinator>
Expand All @@ -40,7 +40,7 @@
<Expression xsi:type="Combinator">
<Combinator xsi:type="dsp:MatrixWriter">
<dsp:Path>np1-lfp_.raw</dsp:Path>
<dsp:Suffix>Timestamp</dsp:Suffix>
<dsp:Suffix>FileCount</dsp:Suffix>
<dsp:Overwrite>false</dsp:Overwrite>
<dsp:Layout>ColumnMajor</dsp:Layout>
</Combinator>
Expand Down
Loading

0 comments on commit c3b118c

Please sign in to comment.