Skip to content

Commit 42ea120

Browse files
committed
blackrockrawio filespec 3.0 - add support for nev.
1 parent 979ce2b commit 42ea120

File tree

1 file changed

+121
-18
lines changed

1 file changed

+121
-18
lines changed

neo/rawio/blackrockrawio.py

Lines changed: 121 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ def __init__(self, filename=None, nsx_override=None, nev_override=None,
211211
'2.1': self.__read_nev_data_variant_a,
212212
'2.2': self.__read_nev_data_variant_a,
213213
'2.3': self.__read_nev_data_variant_b,
214-
'3.0': self.__read_nev_data_variant_b
214+
'3.0': self.__read_nev_data_variant_c
215215
}
216216
self.__waveform_size = {
217217
'2.1': self.__get_waveform_size_variant_a,
@@ -770,12 +770,12 @@ def __extract_nev_file_spec(self):
770770
('ver_minor', 'uint8')]
771771

772772
nev_file_id = np.fromfile(filename, count=1, dtype=dt0)[0]
773-
if nev_file_id['file_id'].decode() == 'NEURALEV':
773+
if nev_file_id['file_id'].decode() in ['NEURALEV', 'BREVENTS']:
774774
spec = '{}.{}'.format(
775775
nev_file_id['ver_major'], nev_file_id['ver_minor'])
776776
else:
777777
raise IOError('NEV file type {} is not supported'.format(
778-
nev_file_id['file_id']))
778+
nev_file_id['file_id'].decode()))
779779

780780
return spec
781781

@@ -1097,11 +1097,18 @@ def __read_nev_data(self, nev_data_masks, nev_data_types):
10971097
data_size = self.__nev_basic_header['bytes_in_data_packets']
10981098
header_size = self.__nev_basic_header['bytes_in_headers']
10991099

1100+
if self.__nev_basic_header['ver_major'] >= 3:
1101+
ts_format = 'uint64'
1102+
header_skip = 10
1103+
else:
1104+
ts_format = 'uint32'
1105+
header_skip = 6
1106+
11001107
# read all raw data packets and markers
11011108
dt0 = [
1102-
('timestamp', 'uint32'),
1109+
('timestamp', ts_format),
11031110
('packet_id', 'uint16'),
1104-
('value', 'S{}'.format(data_size - 6))]
1111+
('value', 'S{}'.format(data_size - header_skip))]
11051112

11061113
raw_data = np.memmap(filename, offset=header_size, dtype=dt0, mode='r')
11071114

@@ -1136,14 +1143,13 @@ def __get_event_segment_ids(self, raw_event_data, masks, nev_data_masks):
11361143
# No pause or reset mechanism present for file version 2.1 and 2.2
11371144
return np.zeros(len(raw_event_data), dtype=int)
11381145

1139-
elif self.__nev_spec == '2.3':
1146+
elif self.__nev_spec in ['2.3', '3.0']:
11401147
reset_ev_mask = self.__get_reset_event_mask(raw_event_data, masks, nev_data_masks)
11411148
reset_ev_ids = np.where(reset_ev_mask)[0]
11421149

11431150
# consistency check for monotone increasing time stamps
1144-
# explicitly converting to int to allow for negative diff values
1145-
jump_ids = \
1146-
np.where(np.diff(np.asarray(raw_event_data['timestamp'], dtype=int)) < 0)[0] + 1
1151+
# Use logical comparator (instead of np.diff) to avoid unsigned dtype issues.
1152+
jump_ids = np.where(raw_event_data['timestamp'][1:] < raw_event_data['timestamp'][:-1])[0] + 1
11471153
overlap = np.in1d(jump_ids, reset_ev_ids)
11481154
if not all(overlap):
11491155
# additional resets occurred without a reset event being stored
@@ -1160,6 +1166,9 @@ def __get_event_segment_ids(self, raw_event_data, masks, nev_data_masks):
11601166
self._nb_segment_nev = len(reset_ev_ids) + 1
11611167
return event_segment_ids
11621168

1169+
else:
1170+
raise ValueError("Unknown File Spec {}".formate(self.__nev_spec))
1171+
11631172
def __match_nsx_and_nev_segment_ids(self, nsx_nb):
11641173
"""
11651174
Ensure matching ids of segments detected in nsx and nev file for version 2.3
@@ -1301,6 +1310,30 @@ def __read_nev_data_variant_b(self):
13011310

13021311
return self.__read_nev_data(nev_data_masks, nev_data_types)
13031312

1313+
def __read_nev_data_variant_c(self):
1314+
"""
1315+
Extract nev data from a 3.0 .nev file
1316+
"""
1317+
nev_data_masks = {
1318+
'NonNeural': 'a',
1319+
'Spikes': 'b',
1320+
'Comments': 'a',
1321+
'VideoSync': 'a',
1322+
'TrackingEvents': 'a',
1323+
'ButtonTrigger': 'a',
1324+
'ConfigEvent': 'a'}
1325+
1326+
nev_data_types = {
1327+
'NonNeural': 'c',
1328+
'Spikes': 'b',
1329+
'Comments': 'b',
1330+
'VideoSync': 'b',
1331+
'TrackingEvents': 'b',
1332+
'ButtonTrigger': 'b',
1333+
'ConfigEvent': 'b'}
1334+
1335+
return self.__read_nev_data(nev_data_masks, nev_data_types)
1336+
13041337
def __nev_ext_header_types(self):
13051338
"""
13061339
Defines extended header types for different .nev file specifications.
@@ -1463,30 +1496,61 @@ def __nev_data_types(self, data_size):
14631496
('analog_input_channel_3', 'int16'),
14641497
('analog_input_channel_4', 'int16'),
14651498
('analog_input_channel_5', 'int16'),
1466-
('unused', 'S{}'.format(data_size - 20))],
1467-
# Version>=2.3
1499+
('unused', 'S{}'.format(data_size - 20))
1500+
],
1501+
# Version=2.3
14681502
'b': [
14691503
('timestamp', 'uint32'),
14701504
('packet_id', 'uint16'),
14711505
('packet_insertion_reason', 'uint8'),
14721506
('reserved', 'uint8'),
14731507
('digital_input', 'uint16'),
1474-
('unused', 'S{}'.format(data_size - 10))]},
1508+
('unused', 'S{}'.format(data_size - 10))
1509+
],
1510+
# Version >= 3.0
1511+
'c': [
1512+
('timestamp', 'uint64'),
1513+
('packet_id', 'uint16'),
1514+
('packet_insertion_reason', 'uint8'),
1515+
('dlen', 'uint8'),
1516+
('digital_input', 'uint16'),
1517+
('unused', 'S{}'.format(data_size - 14))
1518+
]
1519+
},
14751520
'Spikes': {
14761521
'a': [
14771522
('timestamp', 'uint32'),
14781523
('packet_id', 'uint16'),
14791524
('unit_class_nb', 'uint8'),
14801525
('reserved', 'uint8'),
1481-
('waveform', 'S{}'.format(data_size - 8))]},
1526+
('waveform', 'S{}'.format(data_size - 8))
1527+
],
1528+
'b': [
1529+
('timestamp', 'uint64'),
1530+
('packet_id', 'uint16'),
1531+
('unit_class_nb', 'uint8'),
1532+
('dlen', 'uint8'),
1533+
('waveform', 'S{}'.format(data_size - 12))
1534+
]
1535+
},
14821536
'Comments': {
14831537
'a': [
14841538
('timestamp', 'uint32'),
14851539
('packet_id', 'uint16'),
14861540
('char_set', 'uint8'),
14871541
('flag', 'uint8'),
14881542
('color', 'uint32'),
1489-
('comment', 'S{}'.format(data_size - 12))]},
1543+
('comment', 'S{}'.format(data_size - 12))
1544+
],
1545+
'b': [
1546+
('timestamp', 'uint64'),
1547+
('packet_id', 'uint16'),
1548+
('char_set', 'uint8'),
1549+
('flag', 'uint8'),
1550+
('color', 'uint32'),
1551+
('comment', 'S{}'.format(data_size - 16))
1552+
]
1553+
},
14901554
'VideoSync': {
14911555
'a': [
14921556
('timestamp', 'uint32'),
@@ -1495,7 +1559,18 @@ def __nev_data_types(self, data_size):
14951559
('video_frame_nb', 'uint32'),
14961560
('video_elapsed_time', 'uint32'),
14971561
('video_source_id', 'uint32'),
1498-
('unused', 'int8', (data_size - 20,))]},
1562+
('unused', 'int8', (data_size - 20,))
1563+
],
1564+
'b': [
1565+
('timestamp', 'uint64'),
1566+
('packet_id', 'uint16'),
1567+
('video_file_nb', 'uint16'),
1568+
('video_frame_nb', 'uint32'),
1569+
('video_elapsed_time', 'uint32'),
1570+
('video_source_id', 'uint32'),
1571+
('unused', 'int8', (data_size - 24,))
1572+
]
1573+
},
14991574
'TrackingEvents': {
15001575
'a': [
15011576
('timestamp', 'uint32'),
@@ -1504,19 +1579,47 @@ def __nev_data_types(self, data_size):
15041579
('node_id', 'uint16'),
15051580
('node_count', 'uint16'),
15061581
('point_count', 'uint16'),
1507-
('tracking_points', 'uint16', ((data_size - 14) // 2,))]},
1582+
('tracking_points', 'uint16', ((data_size - 14) // 2,))
1583+
],
1584+
'b': [
1585+
('timestamp', 'uint64'),
1586+
('packet_id', 'uint16'),
1587+
('parent_id', 'uint16'),
1588+
('node_id', 'uint16'),
1589+
('node_count', 'uint16'),
1590+
('point_count', 'uint16'),
1591+
('tracking_points', 'uint16', ((data_size - 18) // 2,))
1592+
]
1593+
},
15081594
'ButtonTrigger': {
15091595
'a': [
15101596
('timestamp', 'uint32'),
15111597
('packet_id', 'uint16'),
15121598
('trigger_type', 'uint16'),
1513-
('unused', 'int8', (data_size - 8,))]},
1599+
('unused', 'int8', (data_size - 8,))
1600+
],
1601+
'b': [
1602+
('timestamp', 'uint64'),
1603+
('packet_id', 'uint16'),
1604+
('trigger_type', 'uint16'),
1605+
('unused', 'int8', (data_size - 12,))
1606+
]
1607+
},
15141608
'ConfigEvent': {
15151609
'a': [
15161610
('timestamp', 'uint32'),
15171611
('packet_id', 'uint16'),
15181612
('config_change_type', 'uint16'),
1519-
('config_changed', 'S{}'.format(data_size - 8))]}}
1613+
('config_changed', 'S{}'.format(data_size - 8))
1614+
],
1615+
'b': [
1616+
('timestamp', 'uint64'),
1617+
('packet_id', 'uint16'),
1618+
('config_change_type', 'uint16'),
1619+
('config_changed', 'S{}'.format(data_size - 12))
1620+
]
1621+
}
1622+
}
15201623

15211624
return __nev_data_types
15221625

0 commit comments

Comments
 (0)