8181from neo .core import NeoReadWriteError
8282
8383
84-
8584class BlackrockRawIO (BaseRawIO ):
8685 """
8786 Class for reading data in from a file set recorded by the Blackrock (Cerebus) recording system.
@@ -157,8 +156,14 @@ class BlackrockRawIO(BaseRawIO):
157156 main_sampling_rate = 30000.0
158157
159158 def __init__ (
160- self , filename = None , nsx_override = None , nev_override = None , nsx_to_load = None , load_nev = True ,
161- verbose = False , gap_tolerance_ms = None
159+ self ,
160+ filename = None ,
161+ nsx_override = None ,
162+ nev_override = None ,
163+ nsx_to_load = None ,
164+ load_nev = True ,
165+ verbose = False ,
166+ gap_tolerance_ms = None ,
162167 ):
163168 BaseRawIO .__init__ (self )
164169
@@ -398,13 +403,9 @@ def _parse_header(self):
398403
399404 # Store in existing structures for backward compatibility
400405 self ._nsx_data_header [nsx_nb ] = {
401- seg_idx : {k : v for k , v in seg .items () if k != 'data' }
402- for seg_idx , seg in segments .items ()
403- }
404- self .nsx_datas [nsx_nb ] = {
405- seg_idx : seg ['data' ]
406- for seg_idx , seg in segments .items ()
406+ seg_idx : {k : v for k , v in seg .items () if k != "data" } for seg_idx , seg in segments .items ()
407407 }
408+ self .nsx_datas [nsx_nb ] = {seg_idx : seg ["data" ] for seg_idx , seg in segments .items ()}
408409
409410 # Match NSX and NEV segments for v2.3
410411 if self ._avail_files ["nev" ]:
@@ -856,14 +857,14 @@ def _extract_nev_file_spec(self):
856857 def _read_nsx_header (self , spec , nsx_nb ):
857858 """
858859 Extract nsx header information for any specification version.
859-
860+
860861 Parameters
861862 ----------
862863 spec : str
863864 The specification version (e.g., "2.1", "2.2", "2.3", "3.0")
864865 nsx_nb : int
865866 The NSX file number (e.g., 5 for ns5)
866-
867+
867868 Returns
868869 -------
869870 nsx_basic_header : numpy structured array
@@ -873,23 +874,23 @@ def _read_nsx_header(self, spec, nsx_nb):
873874 """
874875 # Construct filename
875876 filename = f"{ self ._filenames ['nsx' ]} .ns{ nsx_nb } "
876-
877+
877878 # Get basic header structure for this spec
878879 basic_header_dtype = NSX_BASIC_HEADER_TYPES [spec ]
879880 nsx_basic_header = np .fromfile (filename , count = 1 , dtype = basic_header_dtype )[0 ]
880-
881- # Get extended header structure for this spec
881+
882+ # Get extended header structure for this spec
882883 ext_header_dtype = NSX_EXT_HEADER_TYPES [spec ]
883884 offset_dt0 = np .dtype (basic_header_dtype ).itemsize
884885 channel_count = int (nsx_basic_header ["channel_count" ])
885886 nsx_ext_header = np .memmap (filename , shape = channel_count , offset = offset_dt0 , dtype = ext_header_dtype , mode = "r" )
886-
887+
887888 return nsx_basic_header , nsx_ext_header
888889
889890 def _read_nsx_dataheader (self , spec , nsx_nb , offset ):
890891 """
891892 Reads data header following the given offset of an nsx file.
892-
893+
893894 Parameters
894895 ----------
895896 spec : str
@@ -905,7 +906,7 @@ def _read_nsx_dataheader(self, spec, nsx_nb, offset):
905906 data_header_dtype = NSX_DATA_HEADER_TYPES [spec ]
906907 if data_header_dtype is None :
907908 return None # v2.1 has no data headers
908-
909+
909910 nsx_basic_header = np .memmap (filename , dtype = data_header_dtype , shape = 1 , offset = offset , mode = "r" )[0 ]
910911
911912 return nsx_basic_header
@@ -1057,24 +1058,18 @@ def _parse_nsx_data_v21(self, nsx_nb):
10571058 filename = f"{ self ._filenames ['nsx' ]} .ns{ nsx_nb } "
10581059
10591060 # Create file memmap
1060- file_memmap = np .memmap (filename , dtype = ' uint8' , mode = 'r' )
1061+ file_memmap = np .memmap (filename , dtype = " uint8" , mode = "r" )
10611062
10621063 # Calculate header size and data points for v2.1
10631064 channels = int (self ._nsx_basic_header [nsx_nb ]["channel_count" ])
10641065 bytes_in_headers = (
1065- self ._nsx_basic_header [nsx_nb ].dtype .itemsize
1066- + self ._nsx_ext_header [nsx_nb ].dtype .itemsize * channels
1066+ self ._nsx_basic_header [nsx_nb ].dtype .itemsize + self ._nsx_ext_header [nsx_nb ].dtype .itemsize * channels
10671067 )
10681068 filesize = self ._get_file_size (filename )
10691069 num_samples = int ((filesize - bytes_in_headers ) / (2 * channels ) - 1 )
10701070 offset = bytes_in_headers
10711071 # Create data view into memmap
1072- data = np .ndarray (
1073- shape = (num_samples , channels ),
1074- dtype = 'int16' ,
1075- buffer = file_memmap ,
1076- offset = offset
1077- )
1072+ data = np .ndarray (shape = (num_samples , channels ), dtype = "int16" , buffer = file_memmap , offset = offset )
10781073
10791074 return {
10801075 0 : {
@@ -1100,7 +1095,7 @@ def _parse_nsx_data_v22_v30(self, spec, nsx_nb):
11001095 filename = f"{ self ._filenames ['nsx' ]} .ns{ nsx_nb } "
11011096
11021097 # Create file memmap
1103- file_memmap = np .memmap (filename , dtype = ' uint8' , mode = 'r' )
1098+ file_memmap = np .memmap (filename , dtype = " uint8" , mode = "r" )
11041099
11051100 # Get file parameters
11061101 filesize = self ._get_file_size (filename )
@@ -1126,12 +1121,7 @@ def _parse_nsx_data_v22_v30(self, spec, nsx_nb):
11261121 timestamp = header ["timestamp" ]
11271122
11281123 # Create data view into memmap for this block
1129- data = np .ndarray (
1130- shape = (num_samples , channels ),
1131- dtype = 'int16' ,
1132- buffer = file_memmap ,
1133- offset = data_offset
1134- )
1124+ data = np .ndarray (shape = (num_samples , channels ), dtype = "int16" , buffer = file_memmap , offset = data_offset )
11351125
11361126 data_blocks [block_idx ] = {
11371127 "data" : data ,
@@ -1169,13 +1159,7 @@ def _parse_nsx_data_v30_ptp(self, nsx_nb):
11691159 # Create structured memmap (timestamp + samples per packet)
11701160 ptp_dt = NSX_DATA_HEADER_TYPES ["3.0-ptp" ](channel_count )
11711161 npackets = int ((filesize - header_size ) / np .dtype (ptp_dt ).itemsize )
1172- file_memmap = np .memmap (
1173- filename ,
1174- dtype = ptp_dt ,
1175- shape = npackets ,
1176- offset = header_size ,
1177- mode = 'r'
1178- )
1162+ file_memmap = np .memmap (filename , dtype = ptp_dt , shape = npackets , offset = header_size , mode = "r" )
11791163
11801164 # Verify this is truly PTP (all packets should have 1 sample)
11811165 if not np .all (file_memmap ["num_data_points" ] == 1 ):
@@ -1303,9 +1287,7 @@ def _segment_nsx_data(self, data_blocks_dict, nsx_nb):
13031287
13041288 # If gaps found, check user's tolerance
13051289 if len (gap_indices ) > 0 :
1306- gap_report = self ._format_gap_report (
1307- gap_indices , timestamps_in_seconds , time_differences , nsx_nb
1308- )
1290+ gap_report = self ._format_gap_report (gap_indices , timestamps_in_seconds , time_differences , nsx_nb )
13091291
13101292 # Error by default - user must opt-in to segmentation
13111293 if self .gap_tolerance_ms is None :
@@ -1386,28 +1368,30 @@ def _build_nsx_v21_ext_header(self, nsx_nb):
13861368 else :
13871369 label = f"ainp{ (elid - 129 + 1 )} "
13881370
1389- ext_header .append ({
1390- "labels" : label ,
1391- "units" : units ,
1392- "min_analog_val" : - float (dig_factor ),
1393- "max_analog_val" : float (dig_factor ),
1394- "min_digital_val" : - 1000 ,
1395- "max_digital_val" : 1000 ,
1396- })
1371+ ext_header .append (
1372+ {
1373+ "labels" : label ,
1374+ "units" : units ,
1375+ "min_analog_val" : - float (dig_factor ),
1376+ "max_analog_val" : float (dig_factor ),
1377+ "min_digital_val" : - 1000 ,
1378+ "max_digital_val" : 1000 ,
1379+ }
1380+ )
13971381
13981382 return ext_header
13991383
14001384 def _read_nev_header (self , spec , filename ):
14011385 """
14021386 Extract nev header information for any specification version.
1403-
1387+
14041388 Parameters
14051389 ----------
14061390 spec : str
14071391 The specification version (e.g., "2.1", "2.2", "2.3", "3.0")
14081392 filename : str
14091393 The NEV filename to read from
1410-
1394+
14111395 Returns
14121396 -------
14131397 nev_basic_header : np.ndarray
@@ -1417,7 +1401,7 @@ def _read_nev_header(self, spec, filename):
14171401 """
14181402 # Note: This function only uses the passed parameters, not self attributes
14191403 # This makes it easy to convert to @staticmethod later
1420-
1404+
14211405 # basic header (same for all versions)
14221406 dt0 = [
14231407 # Set to "NEURALEV"
@@ -1458,10 +1442,9 @@ def _read_nev_header(self, spec, filename):
14581442
14591443 raw_ext_header = np .memmap (filename , offset = offset_dt0 , dtype = dt1 , shape = shape , mode = "r" )
14601444
1461-
14621445 # Get extended header types for this spec
14631446 header_types = NEV_EXT_HEADER_TYPES_BY_SPEC [spec ]
1464-
1447+
14651448 # Parse extended headers by packet type
14661449 # Strategy: view() entire array first, then mask for efficiency
14671450 # Since all NEV extended header packets are fixed-width (32 bytes), temporarily
@@ -1474,10 +1457,11 @@ def _read_nev_header(self, spec, filename):
14741457 nev_ext_header [packet_id ] = raw_ext_header .view (dtype_def )[mask ]
14751458
14761459 return nev_basic_header , nev_ext_header
1460+
14771461 def _read_nev_data (self , spec , filename ):
14781462 """
14791463 Extract nev data for any specification version.
1480-
1464+
14811465 Parameters
14821466 ----------
14831467 spec : str
@@ -1527,8 +1511,8 @@ def _read_nev_data(self, spec, filename):
15271511 masks [data_type ] = (min_val <= raw_data ["packet_id" ]) & (raw_data ["packet_id" ] <= max_val )
15281512 else :
15291513 # Equality check
1530- masks [data_type ] = ( raw_data ["packet_id" ] == packet_id_spec )
1531-
1514+ masks [data_type ] = raw_data ["packet_id" ] == packet_id_spec
1515+
15321516 types [data_type ] = data_types [data_type ](packet_size_bytes )
15331517
15341518 event_segment_ids = self ._get_event_segment_ids (raw_data , masks , spec )
@@ -1545,14 +1529,13 @@ def _read_nev_data(self, spec, filename):
15451529
15461530 return data
15471531
1548-
15491532 def _get_reset_event_mask (self , raw_event_data , masks , spec ):
15501533 """
15511534 Extract mask for reset comment events in 2.3 .nev file
15521535 """
15531536 if "Comments" not in masks :
15541537 return np .zeros (len (raw_event_data ), dtype = bool )
1555-
1538+
15561539 restart_mask = np .logical_and (
15571540 masks ["Comments" ],
15581541 raw_event_data ["value" ] == b"\x00 \x00 \x00 \x00 \x00 \x00 critical load restart" ,
@@ -1713,8 +1696,6 @@ def _match_nsx_and_nev_segment_ids(self, nsx_nb):
17131696 if len (ev_ids ):
17141697 ev_ids [:] = np .vectorize (new_nev_segment_id_mapping .__getitem__ )(ev_ids )
17151698
1716-
1717-
17181699 def _nev_params (self , param_name ):
17191700 """
17201701 Returns wanted nev parameter.
@@ -2613,6 +2594,6 @@ def _is_set(self, flag, pos):
26132594 ("reserved" , "uint8" ),
26142595 ("timestamps" , "uint64" ),
26152596 ("num_data_points" , "uint32" ),
2616- ("samples" , "int16" , (channel_count ,))
2617- ]
2597+ ("samples" , "int16" , (channel_count ,)),
2598+ ],
26182599}
0 commit comments