Skip to content

Commit

Permalink
Merge pull request #1524 from h-mayorquin/fix_plexon_streams
Browse files Browse the repository at this point in the history
Fix-ate plexon signal streams
  • Loading branch information
zm711 authored Aug 28, 2024
2 parents 643fedf + 87a79a6 commit 9afcc15
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 10 deletions.
50 changes: 40 additions & 10 deletions neo/rawio/plexonrawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import datetime
from collections import OrderedDict
import re

import numpy as np

Expand Down Expand Up @@ -146,7 +147,7 @@ def _parse_header(self):

# Update tqdm with the number of bytes processed in this iteration
if self.progress_bar:
progress_bar.update(length)
progress_bar.update(length) # This was clever, Sam : )

if self.progress_bar:
progress_bar.close()
Expand Down Expand Up @@ -231,6 +232,7 @@ def _parse_header(self):
# signals channels
sig_channels = []
all_sig_length = []
source_id = []
if self.progress_bar:
chan_loop = trange(nb_sig_chan, desc="Parsing signal channels", leave=True)
else:
Expand All @@ -242,6 +244,7 @@ def _parse_header(self):
length = self._data_blocks[5][chan_id]["size"].sum() // 2
if length == 0:
continue # channel not added
source_id.append(h["SrcId"])
all_sig_length.append(length)
sampling_rate = float(h["ADFreq"])
sig_dtype = "int16"
Expand All @@ -255,7 +258,7 @@ def _parse_header(self):
0.5 * (2 ** global_header["BitsPerSpikeSample"]) * h["Gain"] * h["PreampGain"]
)
offset = 0.0
stream_id = "0"
stream_id = "0" # This is overwritten later
sig_channels.append((name, str(chan_id), sampling_rate, sig_dtype, units, gain, offset, stream_id))

sig_channels = np.array(sig_channels, dtype=_signal_channel_dtype)
Expand All @@ -264,22 +267,49 @@ def _parse_header(self):
signal_streams = np.array([], dtype=_signal_stream_dtype)

else:
# detect groups (aka streams)
all_sig_length = all_sig_length = np.array(all_sig_length)
groups = set(zip(sig_channels["sampling_rate"], all_sig_length))
# Detect streams
all_sig_length = np.asarray(all_sig_length)

# names are WB{number}, FPX{number}, SPKCX{number}, AI{number}, etc
pattern = r"^\D+" # Match any non-digit character at the beginning of the string
channels_prefixes = np.asarray([re.match(pattern, name).group(0) for name in sig_channels["name"]])
buffer_stream_groups = set(zip(channels_prefixes, sig_channels["sampling_rate"], all_sig_length))

# There are explanations of the streams based on channel names
# provided by a Plexon Engineer, see here:
# https://github.com/NeuralEnsemble/python-neo/pull/1495#issuecomment-2184256894
channel_prefix_to_stream_name = {
"WB": "WB-Wideband",
"FP": "FPl-Low Pass Filtered ",
"SP": "SPKC-High Pass Filtered",
"AI": "AI-Auxiliary Input",
}

# Using a mapping to ensure consistent order of stream_index
channel_prefix_to_stream_id = {
"WB": "0",
"FP": "1",
"SP": "2",
"AI": "3",
}

signal_streams = []
self._signal_length = {}
self._sig_sampling_rate = {}
for stream_index, (sr, length) in enumerate(groups):
stream_id = str(stream_index)

for stream_index, (channel_prefix, sr, length) in enumerate(buffer_stream_groups):
# The users of plexon can modify the prefix of the channel names (e.g. `my_prefix` instead of `WB`). This is not common but in that case
# We assign the channel_prefix both as stream_name and stream_id
stream_name = channel_prefix_to_stream_name.get(channel_prefix, channel_prefix)
stream_id = channel_prefix_to_stream_id.get(channel_prefix, channel_prefix)

mask = (sig_channels["sampling_rate"] == sr) & (all_sig_length == length)
sig_channels["stream_id"][mask] = stream_id

self._sig_sampling_rate[stream_index] = sr
self._signal_length[stream_index] = length

signal_streams.append(("Signals " + stream_id, stream_id))
signal_streams.append((stream_name, stream_id))

signal_streams = np.array(signal_streams, dtype=_signal_stream_dtype)

Expand Down Expand Up @@ -363,8 +393,8 @@ def _segment_t_start(self, block_index, seg_index):
def _segment_t_stop(self, block_index, seg_index):
t_stop = float(self._last_timestamps) / self._global_ssampling_rate
if hasattr(self, "_signal_length"):
for stream_id in self._signal_length:
t_stop_sig = self._signal_length[stream_id] / self._sig_sampling_rate[stream_id]
for stream_index in self._signal_length.keys():
t_stop_sig = self._signal_length[stream_index] / self._sig_sampling_rate[stream_index]
t_stop = max(t_stop, t_stop_sig)
return t_stop

Expand Down
1 change: 1 addition & 0 deletions neo/test/iotest/test_plexonio.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class TestPlexonIO(
"plexon/File_plexon_1.plx",
"plexon/File_plexon_2.plx",
"plexon/File_plexon_3.plx",
"plexon/4chDemoPLX.plx"
]


Expand Down
1 change: 1 addition & 0 deletions neo/test/rawiotest/test_plexonrawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class TestPlexonRawIO(
"plexon/File_plexon_1.plx",
"plexon/File_plexon_2.plx",
"plexon/File_plexon_3.plx",
"plexon/4chDemoPLX.plx"
]


Expand Down

0 comments on commit 9afcc15

Please sign in to comment.