Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
39 changes: 33 additions & 6 deletions neo/rawio/openephysbinaryrawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ def __init__(self, dirname="", load_sync_channel=False, experiment_names=None):
experiment_names = [experiment_names]
self.experiment_names = experiment_names
self.load_sync_channel = load_sync_channel
if load_sync_channel:
warn(
"The load_sync_channel=True option is deprecated and will be removed in version 0.15. "
"Use load_sync_channel=False instead, which will add sync channels as separate streams.",
DeprecationWarning, stacklevel=2
)
self.folder_structure = None
self._use_direct_evt_timestamps = None

Expand Down Expand Up @@ -123,7 +129,7 @@ def _parse_header(self):
# signals zone
# create signals channel map: several channel per stream
signal_channels = []

sync_stream_id_to_buffer_id = {}
for stream_index, stream_name in enumerate(sig_stream_names):
# stream_index is the index in vector stream names
stream_id = str(stream_index)
Expand All @@ -141,8 +147,9 @@ def _parse_header(self):

# Special cases for stream
if "SYNC" in chan_id and not self.load_sync_channel:
# the channel is removed from stream but not the buffer
stream_id = ""
# Every stream sync channel is added as its own stream
stream_id = f"{chan_id}-{str(stream_index)}"
sync_stream_id_to_buffer_id[stream_id] = buffer_id

if "ADC" in chan_id:
# These are non-neural channels and their stream should be separated
Expand Down Expand Up @@ -174,12 +181,21 @@ def _parse_header(self):
signal_buffers = []

unique_streams_ids = np.unique(signal_channels["stream_id"])

# This is getting too complicated, we probably should just have a table which would be easier to read
# And for users to understand
for stream_id in unique_streams_ids:
# Handle special case of Synch channel having stream_id empty
if stream_id == "":

# Handle sync channel on a special way
if "SYNC" in stream_id:
# This is a sync channel and should not be added to the signal streams
buffer_id = sync_stream_id_to_buffer_id[stream_id]
stream_name = stream_id
signal_streams.append((stream_name, stream_id, buffer_id))
continue
stream_index = int(stream_id)

# Neural signal
stream_index = int(stream_id)
if stream_index < self._num_of_signal_streams:
stream_name = sig_stream_names[stream_index]
buffer_id = stream_id
Expand Down Expand Up @@ -254,7 +270,13 @@ def _parse_header(self):

if num_adc_channels == 0:
if has_sync_trace and not self.load_sync_channel:
# Exclude the sync channel from the main stream
self._stream_buffer_slice[stream_id] = slice(None, -1)

# Add a buffer slice for the sync channel
sync_channel_name = info["channels"][-1]["channel_name"]
stream_name = f"{sync_channel_name}-{str(stream_id)}"
self._stream_buffer_slice[stream_name] = slice(-1, None)
else:
self._stream_buffer_slice[stream_id] = None
else:
Expand All @@ -264,7 +286,12 @@ def _parse_header(self):
self._stream_buffer_slice[stream_id_neural] = slice(0, num_neural_channels)

if has_sync_trace and not self.load_sync_channel:
# Exclude the sync channel from the non-neural stream
self._stream_buffer_slice[stream_id_non_neural] = slice(num_neural_channels, -1)

# Add a buffer slice for the sync channel
sync_channel_name = info["channels"][-1]["channel_name"]
self._stream_buffer_slice[sync_channel_name] = slice(-1, None)
else:
self._stream_buffer_slice[stream_id_non_neural] = slice(num_neural_channels, None)

Expand Down
22 changes: 22 additions & 0 deletions neo/test/rawiotest/test_openephysbinaryrawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ def test_sync(self):
block_index=0, seg_index=0, i_start=0, i_stop=100, stream_index=stream_index
)
assert chunk.shape[1] == 384

def test_sync_channel_access(self):
"""Test that sync channels can be accessed as separate streams when load_sync_channel=False."""
rawio = OpenEphysBinaryRawIO(
self.get_local_path("openephysbinary/v0.6.x_neuropixels_with_sync"), load_sync_channel=False
)
rawio.parse_header()

# Find sync channel streams
sync_stream_names = [s_name for s_name in rawio.header["signal_streams"]["name"] if "SYNC" in s_name]
assert len(sync_stream_names) > 0, "No sync channel streams found"

# Get the stream index for the first sync channel
sync_stream_index = list(rawio.header["signal_streams"]["name"]).index(sync_stream_names[0])

# Check that we can access the sync channel data
chunk = rawio.get_analogsignal_chunk(
block_index=0, seg_index=0, i_start=0, i_stop=100, stream_index=sync_stream_index
)

# Sync channel should have only one channel
assert chunk.shape[1] == 1, f"Expected sync channel to have 1 channel, got {chunk.shape[1]}"

def test_no_sync(self):
# requesting sync channel when there is none raises an error
Expand Down
Loading