diff --git a/neo/rawio/plexonrawio.py b/neo/rawio/plexonrawio.py index fd80dcd81..bb2fd952b 100644 --- a/neo/rawio/plexonrawio.py +++ b/neo/rawio/plexonrawio.py @@ -24,6 +24,7 @@ import datetime from collections import OrderedDict +import re import numpy as np @@ -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() @@ -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: @@ -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" @@ -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) @@ -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) @@ -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 diff --git a/neo/test/iotest/test_plexonio.py b/neo/test/iotest/test_plexonio.py index cca5a469e..5fc286671 100644 --- a/neo/test/iotest/test_plexonio.py +++ b/neo/test/iotest/test_plexonio.py @@ -18,6 +18,7 @@ class TestPlexonIO( "plexon/File_plexon_1.plx", "plexon/File_plexon_2.plx", "plexon/File_plexon_3.plx", + "plexon/4chDemoPLX.plx" ] diff --git a/neo/test/rawiotest/test_plexonrawio.py b/neo/test/rawiotest/test_plexonrawio.py index 98bcbd303..06420c8c1 100644 --- a/neo/test/rawiotest/test_plexonrawio.py +++ b/neo/test/rawiotest/test_plexonrawio.py @@ -15,6 +15,7 @@ class TestPlexonRawIO( "plexon/File_plexon_1.plx", "plexon/File_plexon_2.plx", "plexon/File_plexon_3.plx", + "plexon/4chDemoPLX.plx" ]