@@ -177,6 +177,22 @@ def _parse_header(self):
177177
178178 # No events
179179 event_channels = []
180+ # This is true only in case of 'nidq' stream
181+ for stream_name in stream_names :
182+ if "nidq" in stream_name :
183+ info = self .signals_info_dict [0 , stream_name ]
184+ if len (info ["digital_channels" ]) > 0 :
185+ # add event channels
186+ for local_chan in info ["digital_channels" ]:
187+ chan_name = local_chan
188+ chan_id = f"{ stream_name } #{ chan_name } "
189+ event_channels .append ((chan_name , chan_id , "event" ))
190+ # add events_memmap
191+ data = np .memmap (info ["bin_file" ], dtype = "int16" , mode = "r" , offset = 0 , order = "C" )
192+ data = data .reshape (- 1 , info ["num_chan" ])
193+ # The digital word is usually the last channel, after all the individual analog channels
194+ extracted_word = data [:, len (info ["analog_channels" ])]
195+ self ._events_memmap = np .unpackbits (extracted_word .astype (np .uint8 )[:, None ], axis = 1 )
180196 event_channels = np .array (event_channels , dtype = _event_channel_dtype )
181197
182198 # No spikes
@@ -272,6 +288,44 @@ def _get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, strea
272288
273289 return raw_signals
274290
291+ def _event_count (self , event_channel_idx , block_index = None , seg_index = None ):
292+ timestamps , _ , _ = self ._get_event_timestamps (block_index , seg_index , event_channel_idx , None , None )
293+ return timestamps .size
294+
295+ def _get_event_timestamps (self , block_index , seg_index , event_channel_index , t_start = None , t_stop = None ):
296+ timestamps , durations , labels = [], None , []
297+ info = self .signals_info_dict [0 , "nidq" ] # There are no events that are not in the nidq stream
298+ dig_ch = info ["digital_channels" ]
299+ if len (dig_ch ) > 0 :
300+ event_data = self ._events_memmap
301+ channel = dig_ch [event_channel_index ]
302+ ch_idx = 7 - int (channel [2 :]) # They are in the reverse order
303+ this_stream = event_data [:, ch_idx ]
304+ this_rising = np .where (np .diff (this_stream ) == 1 )[0 ] + 1
305+ this_falling = (
306+ np .where (np .diff (this_stream ) == 255 )[0 ] + 1
307+ ) # because the data is in unsigned 8 bit, -1 = 255!
308+ if len (this_rising ) > 0 :
309+ timestamps .extend (this_rising )
310+ labels .extend ([f"{ channel } ON" ] * len (this_rising ))
311+ if len (this_falling ) > 0 :
312+ timestamps .extend (this_falling )
313+ labels .extend ([f"{ channel } OFF" ] * len (this_falling ))
314+ timestamps = np .asarray (timestamps )
315+ if len (labels ) == 0 :
316+ labels = np .asarray (labels , dtype = "U1" )
317+ else :
318+ labels = np .asarray (labels )
319+ return timestamps , durations , labels
320+
321+ def _rescale_event_timestamp (self , event_timestamps , dtype , event_channel_index ):
322+ info = self .signals_info_dict [0 , "nidq" ] # There are no events that are not in the nidq stream
323+ event_times = event_timestamps .astype (dtype ) / float (info ["sampling_rate" ])
324+ return event_times
325+
326+ def _rescale_epoch_duration (self , raw_duration , dtype , event_channel_index ):
327+ return None
328+
275329
276330def scan_files (dirname ):
277331 """
@@ -507,4 +561,14 @@ def extract_stream_info(meta_file, meta):
507561 info ["channel_offsets" ] = np .zeros (info ["num_chan" ])
508562 info ["has_sync_trace" ] = has_sync_trace
509563
564+ if "nidq" in device :
565+ info ["digital_channels" ] = []
566+ info ["analog_channels" ] = [channel for channel in info ["channel_names" ] if not channel .startswith ("XD" )]
567+ # Digital/event channels are encoded within the digital word, so that will need more handling
568+ for item in meta ["niXDChans1" ].split ("," ):
569+ if ":" in item :
570+ start , end = map (int , item .split (":" ))
571+ info ["digital_channels" ].extend ([f"XD{ i } " for i in range (start , end + 1 )])
572+ else :
573+ info ["digital_channels" ].append (f"XD{ int (item )} " )
510574 return info
0 commit comments