Skip to content

Commit 540abbe

Browse files
authored
Merge pull request #1924 from cboulay/nev-reset-per-packet-type
Blackrock: detect NEV clock resets per packet type
2 parents 2b8c5e3 + d082c48 commit 540abbe

2 files changed

Lines changed: 50 additions & 15 deletions

File tree

‎neo/rawio/blackrockrawio.py‎

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,30 +2038,42 @@ def _get_event_segment_ids(self, raw_event_data, masks, spec):
20382038
# consistency check for monotone increasing time stamps
20392039
# - Use logical comparator (instead of np.diff) to avoid unsigned dtype issues.
20402040
# - Only consider handled/known event types.
2041-
mask_handled = np.any([mask for mask in masks.values()], axis=0)
2042-
jump_ids_handled = (
2043-
np.where(
2044-
raw_event_data["timestamp"][mask_handled][1:] < raw_event_data["timestamp"][mask_handled][:-1]
2045-
)[0]
2046-
+ 1
2047-
)
2048-
jump_ids = np.where(mask_handled)[0][jump_ids_handled] # jump ids in full set of events (incl. unhandled)
2049-
overlap = np.isin(jump_ids, reset_ev_ids)
2050-
if not all(overlap):
2041+
# - Compare only packets of the same type: the file does not order packets of different
2042+
# types (e.g. a comment is stamped when the user starts typing but written when they
2043+
# finish, so it can be later than the spikes written after it).
2044+
# jumps maps the position of a packet earlier than the previous packet of its type to the
2045+
# position of that previous packet, in the full set of events (incl. unhandled).
2046+
jumps = {}
2047+
for mask in masks.values():
2048+
ids = np.flatnonzero(mask)
2049+
ts = raw_event_data["timestamp"][ids]
2050+
earlier = np.flatnonzero(ts[1:] < ts[:-1])
2051+
jumps.update(zip(ids[earlier + 1].tolist(), ids[earlier].tolist()))
2052+
2053+
# A reset sends the clock of every packet type back, so each type jumps at its first packet
2054+
# after it. Count a jump only if the packet it is compared with is in the current segment,
2055+
# so that one reset is one boundary.
2056+
reset_set = set(reset_ev_ids.tolist())
2057+
boundaries = []
2058+
segment_start = 0
2059+
for position in sorted(reset_set | jumps.keys()):
2060+
if position in reset_set or jumps[position] >= segment_start:
2061+
boundaries.append(position)
2062+
segment_start = position
2063+
additional_ids = np.array([b for b in boundaries if b not in reset_set])
2064+
if len(additional_ids):
20512065
# additional resets occurred without a reset event being stored
2052-
additional_ids = jump_ids[np.invert(overlap)]
20532066
warnings.warn(
20542067
f"Detected {len(additional_ids)} undocumented segments within "
20552068
f"nev data after timestamps {additional_ids}."
20562069
""
20572070
)
2058-
reset_ev_ids = sorted(np.unique(np.concatenate((reset_ev_ids, jump_ids))))
20592071

20602072
event_segment_ids = np.zeros(len(raw_event_data), dtype=int)
2061-
for reset_event_id in reset_ev_ids:
2062-
event_segment_ids[reset_event_id:] += 1
2073+
event_segment_ids[boundaries] = 1
2074+
event_segment_ids = np.cumsum(event_segment_ids)
20632075

2064-
self._nb_segment_nev = len(reset_ev_ids) + 1
2076+
self._nb_segment_nev = len(boundaries) + 1
20652077
return event_segment_ids
20662078

20672079
else:

‎neo/test/rawiotest/test_blackrockrawio.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,29 @@ def test_nev_spikes_at_segment_edges_ptp(self):
628628
returned += reader.get_spike_timestamps(block_index, seg_index, unit_index, None, None).size
629629
self.assertEqual(returned, 999)
630630

631+
def test_nev_segments_ignore_order_between_packet_types(self):
632+
"""
633+
Only a packet earlier than the previous packet of its own type is a clock reset.
634+
635+
Central writes a comment a few ms after stamping it, so in this file two comments are
636+
written before 96 spikes stamped 2.1 ms earlier. Comparing packets of different types
637+
split the nev into two segments, and with the ns5 only the 384 spikes of the first one
638+
were returned.
639+
"""
640+
filename = self.get_local_path(
641+
"blackrock/version_3_0/time_structure/comment_written_before_earlier_spikes/"
642+
"comment_written_before_earlier_spikes"
643+
)
644+
for nsx_to_load in ([], [5]):
645+
reader = BlackrockRawIO(filename=filename, nsx_to_load=nsx_to_load)
646+
reader.parse_header()
647+
self.assertEqual(reader.segment_count(0), 1)
648+
returned = sum(
649+
reader.get_spike_timestamps(0, 0, unit_index, None, None).size
650+
for unit_index in range(reader.spike_channels_count())
651+
)
652+
self.assertEqual(returned, 960)
653+
631654

632655
if __name__ == "__main__":
633656
unittest.main()

0 commit comments

Comments
 (0)