Skip to content

Commit b0018b2

Browse files
committed
separate sync as its own stream
1 parent 07fa5b3 commit b0018b2

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

neo/rawio/openephysbinaryrawio.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ def __init__(self, dirname="", load_sync_channel=False, experiment_names=None):
7272
experiment_names = [experiment_names]
7373
self.experiment_names = experiment_names
7474
self.load_sync_channel = load_sync_channel
75+
if load_sync_channel:
76+
warn(
77+
"The load_sync_channel=True option is deprecated and will be removed in a future version. "
78+
"Use load_sync_channel=False instead, which will add sync channels as separate streams.",
79+
DeprecationWarning, stacklevel=2
80+
)
7581
self.folder_structure = None
7682
self._use_direct_evt_timestamps = None
7783

@@ -123,7 +129,7 @@ def _parse_header(self):
123129
# signals zone
124130
# create signals channel map: several channel per stream
125131
signal_channels = []
126-
132+
sync_stream_id_to_buffer = {}
127133
for stream_index, stream_name in enumerate(sig_stream_names):
128134
# stream_index is the index in vector stream names
129135
stream_id = str(stream_index)
@@ -141,8 +147,9 @@ def _parse_header(self):
141147

142148
# Special cases for stream
143149
if "SYNC" in chan_id and not self.load_sync_channel:
144-
# the channel is removed from stream but not the buffer
145-
stream_id = ""
150+
# Every stream sync channel is added as its own stream
151+
stream_id = chan_id
152+
sync_stream_id_to_buffer[stream_id] = buffer_id
146153

147154
if "ADC" in chan_id:
148155
# These are non-neural channels and their stream should be separated
@@ -174,12 +181,18 @@ def _parse_header(self):
174181
signal_buffers = []
175182

176183
unique_streams_ids = np.unique(signal_channels["stream_id"])
184+
177185
for stream_id in unique_streams_ids:
178-
# Handle special case of Synch channel having stream_id empty
179-
if stream_id == "":
186+
187+
# Handle sync channel on a special way
188+
if "SYNC" in stream_id:
189+
# This is a sync channel and should not be added to the signal streams
190+
sync_stream_buffer = sync_stream_id_to_buffer[stream_id]
191+
signal_streams.append((stream_id, stream_id, sync_stream_buffer))
180192
continue
181-
stream_index = int(stream_id)
193+
182194
# Neural signal
195+
stream_index = int(stream_id)
183196
if stream_index < self._num_of_signal_streams:
184197
stream_name = sig_stream_names[stream_index]
185198
buffer_id = stream_id
@@ -254,7 +267,12 @@ def _parse_header(self):
254267

255268
if num_adc_channels == 0:
256269
if has_sync_trace and not self.load_sync_channel:
270+
# Exclude the sync channel from the main stream
257271
self._stream_buffer_slice[stream_id] = slice(None, -1)
272+
273+
# Add a buffer slice for the sync channel
274+
sync_channel_name = info["channels"][-1]["channel_name"]
275+
self._stream_buffer_slice[sync_channel_name] = slice(-1, None)
258276
else:
259277
self._stream_buffer_slice[stream_id] = None
260278
else:
@@ -264,7 +282,12 @@ def _parse_header(self):
264282
self._stream_buffer_slice[stream_id_neural] = slice(0, num_neural_channels)
265283

266284
if has_sync_trace and not self.load_sync_channel:
285+
# Exclude the sync channel from the non-neural stream
267286
self._stream_buffer_slice[stream_id_non_neural] = slice(num_neural_channels, -1)
287+
288+
# Add a buffer slice for the sync channel
289+
sync_channel_name = info["channels"][-1]["channel_name"]
290+
self._stream_buffer_slice[sync_channel_name] = slice(-1, None)
268291
else:
269292
self._stream_buffer_slice[stream_id_non_neural] = slice(num_neural_channels, None)
270293

neo/test/rawiotest/test_openephysbinaryrawio.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,28 @@ def test_sync(self):
4343
block_index=0, seg_index=0, i_start=0, i_stop=100, stream_index=stream_index
4444
)
4545
assert chunk.shape[1] == 384
46+
47+
def test_sync_channel_access(self):
48+
"""Test that sync channels can be accessed as separate streams when load_sync_channel=False."""
49+
rawio = OpenEphysBinaryRawIO(
50+
self.get_local_path("openephysbinary/v0.6.x_neuropixels_with_sync"), load_sync_channel=False
51+
)
52+
rawio.parse_header()
53+
54+
# Find sync channel streams
55+
sync_stream_names = [s_name for s_name in rawio.header["signal_streams"]["name"] if "SYNC" in s_name]
56+
assert len(sync_stream_names) > 0, "No sync channel streams found"
57+
58+
# Get the stream index for the first sync channel
59+
sync_stream_index = list(rawio.header["signal_streams"]["name"]).index(sync_stream_names[0])
60+
61+
# Check that we can access the sync channel data
62+
chunk = rawio.get_analogsignal_chunk(
63+
block_index=0, seg_index=0, i_start=0, i_stop=100, stream_index=sync_stream_index
64+
)
65+
66+
# Sync channel should have only one channel
67+
assert chunk.shape[1] == 1, f"Expected sync channel to have 1 channel, got {chunk.shape[1]}"
4668

4769
def test_no_sync(self):
4870
# requesting sync channel when there is none raises an error

0 commit comments

Comments
 (0)