Skip to content

Commit ca40bd6

Browse files
sprengerJuliaSprenger
authored andcommitted
[Neuralynx] First steps towards multiple streams
1 parent 7c12115 commit ca40bd6

File tree

1 file changed

+43
-17
lines changed

1 file changed

+43
-17
lines changed

neo/rawio/neuralynxrawio/neuralynxrawio.py

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ def _parse_header(self):
166166
if excl_file in filenames:
167167
filenames.remove(excl_file)
168168

169+
ncs_sampling_rates = []
170+
stream_id = -1 # will be increased to 0 for first signal
171+
169172
for filename in filenames:
170173
filename = os.path.join(dirname, filename)
171174

@@ -191,13 +194,17 @@ def _parse_header(self):
191194

192195
chan_uid = (chan_name, chan_id)
193196
if ext == 'ncs':
197+
if info['sampling_rate'] not in ncs_sampling_rates:
198+
ncs_sampling_rates.append(info['sampling_rate'])
199+
stream_id += 1
200+
194201
# a sampled signal channel
195202
units = 'uV'
196203
gain = info['bit_to_microVolt'][idx]
197204
if info.get('input_inverted', False):
198205
gain *= -1
199206
offset = 0.
200-
stream_id = 0
207+
stream_id = stream_id
201208
signal_channels.append((chan_name, str(chan_id), info['sampling_rate'],
202209
'int16', units, gain, offset, stream_id))
203210
self.ncs_filenames[chan_uid] = filename
@@ -283,10 +290,8 @@ def _parse_header(self):
283290

284291
# require all sampled signals, ncs files, to have the same sampling rate
285292
if signal_channels.size > 0:
286-
sampling_rate = np.unique(signal_channels['sampling_rate'])
287-
assert sampling_rate.size == 1
288-
self._sigs_sampling_rate = sampling_rate[0]
289-
signal_streams = [('signals', '0')]
293+
names = [f'signals ({sr}Hz)' for sr in ncs_sampling_rates]
294+
signal_streams = list(zip(names, range(len(names))))
290295
else:
291296
signal_streams = []
292297
signal_streams = np.array(signal_streams, dtype=_signal_stream_dtype)
@@ -301,7 +306,6 @@ def _parse_header(self):
301306
if ncsSegTimestampLimits:
302307
self._ncs_seg_timestamp_limits = ncsSegTimestampLimits # save copy
303308
self._nb_segment = ncsSegTimestampLimits.nb_segment
304-
self._sigs_length = ncsSegTimestampLimits.length.copy()
305309
self._timestamp_limits = ncsSegTimestampLimits.timestamp_limits.copy()
306310
self._sigs_t_start = ncsSegTimestampLimits.t_start.copy()
307311
self._sigs_t_stop = ncsSegTimestampLimits.t_stop.copy()
@@ -362,19 +366,20 @@ def _parse_header(self):
362366
for seg_index in range(self._nb_segment):
363367
seg_annotations = bl_annotations['segments'][seg_index]
364368

365-
for c in range(signal_streams.size):
369+
for stream_id in range(signal_streams.size):
366370
# one or no signal stream
367-
sig_ann = seg_annotations['signals'][c]
371+
stream_ann = seg_annotations['signals'][stream_id]
368372
# handle array annotations
369373
for key in signal_annotations[0].keys():
370374
values = []
371-
for c in range(signal_channels.size):
372-
value = signal_annotations[0][key]
375+
# only collect values from channels belonging to current stream
376+
for d in np.where(signal_channels['stream_id'] == f'{stream_id}')[0]:
377+
value = signal_annotations[d][key]
373378
values.append(value)
374379
values = np.array(values)
375380
if values.ndim == 1:
376381
# 'InputRange': is 2D and make bugs
377-
sig_ann['__array_annotations__'][key] = values
382+
stream_ann['__array_annotations__'][key] = values
378383

379384
for c in range(spike_channels.size):
380385
unit_ann = seg_annotations['spikes'][c]
@@ -432,7 +437,15 @@ def _segment_t_stop(self, block_index, seg_index):
432437
return self._seg_t_stops[seg_index] - self.global_t_start
433438

434439
def _get_signal_size(self, block_index, seg_index, stream_index):
435-
return self._sigs_length[seg_index]
440+
stream_id = self.header['signal_streams'][stream_index]['id']
441+
channel_indexes = np.where(self.header['signal_channels']['stream_id'] == stream_id)[0]
442+
443+
if len(channel_indexes):
444+
channel_index = channel_indexes[0]
445+
chan = self.header['signal_channels'][channel_index]
446+
return self._sigs_memmaps[seg_index][(chan['name'], int(chan['id']))]
447+
else:
448+
return None
436449

437450
def _get_signal_t_start(self, block_index, seg_index, stream_index):
438451
return self._sigs_t_start[seg_index] - self.global_t_start
@@ -472,8 +485,12 @@ def _get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop,
472485
if channel_indexes is None:
473486
channel_indexes = slice(None)
474487

475-
channel_ids = self.header['signal_channels'][channel_indexes]['id'].astype(int)
476-
channel_names = self.header['signal_channels'][channel_indexes]['name']
488+
stream_id = self.header['signal_streams'][stream_index]['id']
489+
stream_mask = self.header['signal_channels']['stream_id'] == stream_id
490+
491+
# channel_streams = self.
492+
channel_ids = self.header['signal_channels'][stream_mask][channel_indexes]['id'].astype(int)
493+
channel_names = self.header['signal_channels'][stream_mask][channel_indexes]['name']
477494

478495
# create buffer for samples
479496
sigs_chunk = np.zeros((i_stop - i_start, len(channel_ids)), dtype='int16')
@@ -622,9 +639,18 @@ def scan_ncs_files(self, ncs_filenames):
622639
del data
623640

624641
# Construct an inverse dictionary from NcsSections to list of associated chan_uids
625-
revSectMap = dict()
626-
for k, v in chanSectMap.items():
627-
revSectMap.setdefault(v[0], []).append(k)
642+
# consider channels
643+
revSectMap = {}
644+
for i, (k, v) in enumerate(chanSectMap.items()):
645+
if i == 0: # start initially with first Ncssections
646+
latest_sections = v[0]
647+
# time tolerance of +- one data package (in microsec)
648+
tolerance = 512 / min(v[0].sampFreqUsed, latest_sections.sampFreqUsed) *1e6
649+
if v[0].is_equivalent(latest_sections, abs_tol=tolerance):
650+
revSectMap.setdefault(latest_sections, []).append(k)
651+
else:
652+
revSectMap[v[0]] = [k]
653+
latest_sections = v[0]
628654

629655
# If there is only one NcsSections structure in the set of ncs files, there should only
630656
# be one entry. Otherwise this is presently unsupported.

0 commit comments

Comments
 (0)