@@ -462,28 +462,22 @@ def _get_analogsignal_chunk_header_attached(self, i_start, i_stop, stream_index,
462462 sl0 = i_start % block_size
463463 sl1 = sl0 + (i_stop - i_start )
464464
465- # For all streams raw_data is a structured memmap with a field for each channel_id
466- if stream_is_stim :
467- # For stim data, we need to extract the raw data first, then demultiplex it
468- stim_data = np .zeros ((i_stop - i_start , len (channel_ids )), dtype = dtype )
469- for chunk_index , channel_id in enumerate (channel_ids ):
470- data_chan = self ._raw_data [channel_id ]
471- if multiple_samples_per_block :
472- stim_data [:, chunk_index ] = data_chan [block_start :block_stop ].flatten ()[sl0 :sl1 ]
473- else :
474- stim_data [:, chunk_index ] = data_chan [i_start :i_stop ]
475- # Now demultiplex the stim data
476- sigs_chunk = self ._decode_current_from_stim_data (stim_data , 0 , stim_data .shape [0 ])
477- elif not stream_is_digital :
478- sigs_chunk = np .zeros ((i_stop - i_start , len (channel_ids )), dtype = dtype )
479-
465+ if not stream_is_digital :
466+ # For all streams raw_data is a structured memmap with a field for each channel_id
467+ sigs_chunk = np .zeros ((i_stop - i_start , len (channel_ids )), dtype = dtype )
480468 for chunk_index , channel_id in enumerate (channel_ids ):
481469 data_chan = self ._raw_data [channel_id ]
470+
482471 if multiple_samples_per_block :
483472 sigs_chunk [:, chunk_index ] = data_chan [block_start :block_stop ].flatten ()[sl0 :sl1 ]
484473 else :
485474 sigs_chunk [:, chunk_index ] = data_chan [i_start :i_stop ]
486- else : # For digital data the channels come interleaved in a single field so we need to demultiplex
475+
476+ if stream_is_stim :
477+ sigs_chunk = self ._decode_current_from_stim_data (sigs_chunk , 0 , sigs_chunk .shape [0 ])
478+
479+ else :
480+ # For digital data the channels come interleaved in a single field so we need to demultiplex
487481 digital_raw_data = self ._raw_data [field_name ].flatten ()
488482 sigs_chunk = self ._demultiplex_digital_data (digital_raw_data , channel_ids , i_start , i_stop )
489483 return sigs_chunk
@@ -493,7 +487,7 @@ def _get_analogsignal_chunk_one_file_per_channel(self, i_start, i_stop, stream_i
493487 stream_name = self .header ["signal_streams" ][stream_index ]["name" ][:]
494488 signal_data_memmap_list = self ._raw_data [stream_name ]
495489 stream_is_stim = stream_name == "Stim channel"
496-
490+
497491 channel_indexes_are_slice = isinstance (channel_indexes , slice )
498492 if channel_indexes_are_slice :
499493 num_channels = len (signal_data_memmap_list )
@@ -510,8 +504,8 @@ def _get_analogsignal_chunk_one_file_per_channel(self, i_start, i_stop, stream_i
510504 for chunk_index , channel_index in enumerate (channel_indexes ):
511505 channel_memmap = signal_data_memmap_list [channel_index ]
512506 sigs_chunk [:, chunk_index ] = channel_memmap [i_start :i_stop ]
513-
514- # If this is stim data, we need to demultiplex it
507+
508+ # If this is stim data, we need to decode the current values
515509 if stream_is_stim :
516510 sigs_chunk = self ._decode_current_from_stim_data (sigs_chunk , 0 , sigs_chunk .shape [0 ])
517511
@@ -524,7 +518,7 @@ def _get_analogsignal_chunk_one_file_per_signal(self, i_start, i_stop, stream_in
524518
525519 stream_is_digital = stream_name in digital_stream_names
526520 stream_is_stim = stream_name == "Stim channel"
527-
521+
528522 if stream_is_digital :
529523 stream_id = self .header ["signal_streams" ][stream_index ]["id" ]
530524 mask = self .header ["signal_channels" ]["stream_id" ] == stream_id
@@ -543,11 +537,11 @@ def _get_analogsignal_chunk_one_file_per_signal(self, i_start, i_stop, stream_in
543537 def _demultiplex_digital_data (self , raw_digital_data , channel_ids , i_start , i_stop ):
544538 """
545539 Demultiplex digital data by extracting individual channel values from packed 16-bit format.
546-
540+
547541 According to the Intan format, digital input/output data is stored with all 16 channels
548542 encoded bit-by-bit in each 16-bit word. This method extracts the specified digital channels
549543 from the packed format into separate uint16 arrays of 0 and 1.
550-
544+
551545 Parameters
552546 ----------
553547 raw_digital_data : ndarray
@@ -559,19 +553,19 @@ def _demultiplex_digital_data(self, raw_digital_data, channel_ids, i_start, i_st
559553 Starting sample index for demultiplexing.
560554 i_stop : int
561555 Ending sample index for demultiplexing (exclusive).
562-
556+
563557 Returns
564558 -------
565559 ndarray
566- Demultiplexed digital data with shape (i_stop-i_start, len(channel_ids)),
560+ Demultiplexed digital data with shape (i_stop-i_start, len(channel_ids)),
567561 containing 0 or 1 values for each requested channel.
568-
562+
569563 Notes
570564 -----
571565 In the Intan format, digital channels are packed into 16-bit words where each bit position
572566 corresponds to a specific channel number. For example, with digital inputs 0, 4, and 5
573567 set high and the rest low, the 16-bit word would be 2^0 + 2^4 + 2^5 = 1 + 16 + 32 = 49.
574-
568+
575569 The native_order property for each channel corresponds to its bit position in the packed word.
576570 """
577571 dtype = np .uint16 # We fix this to match the memmap dtype
@@ -584,15 +578,15 @@ def _demultiplex_digital_data(self, raw_digital_data, channel_ids, i_start, i_st
584578 output [:, channel_index ] = demultiplex_data [i_start :i_stop ].flatten ()
585579
586580 return output
587-
581+
588582 def _decode_current_from_stim_data (self , raw_stim_data , i_start , i_stop ):
589583 """
590584 Demultiplex stimulation data by extracting current values from packed 16-bit format.
591-
585+
592586 According to the Intan RHS data format, stimulation current is stored in the lower 9 bits
593587 of each 16-bit word: 8 bits for magnitude and 1 bit for sign. The upper bits contain
594588 flags for compliance limit, charge recovery, and amplifier settle.
595-
589+
596590 Parameters
597591 ----------
598592 raw_stim_data : ndarray
@@ -601,47 +595,47 @@ def _decode_current_from_stim_data(self, raw_stim_data, i_start, i_stop):
601595 Starting sample index for demultiplexing.
602596 i_stop : int
603597 Ending sample index for demultiplexing (exclusive).
604-
598+
605599 Returns
606600 -------
607601 ndarray
608602 Demultiplexed stimulation current values in amperes, preserving the original
609603 array dimensions. The output values need to be multiplied by the stim_step_size
610604 parameter (from header) to obtain the actual current in amperes.
611-
605+
612606 Notes
613607 -----
614608 Bit structure of each 16-bit stimulation word:
615609 - Bits 0-7: Current magnitude
616610 - Bit 8: Sign bit (1 = negative current)
617611 - Bits 9-13: Unused (always zero)
618612 - Bit 14: Amplifier settle flag (1 = activated)
619- - Bit 15: Charge recovery flag (1 = activated)
613+ - Bit 15: Charge recovery flag (1 = activated)
620614 - Bit 16 (MSB): Compliance limit flag (1 = limit reached)
621-
615+
622616 The actual current value in amperes is obtained by multiplying the
623- output by the 'stim_step_size' parameter from the file header. These scaled values can be
617+ output by the 'stim_step_size' parameter from the file header. These scaled values can be
624618 obtained with the `rescale_signal_raw_to_float` function.
625619 """
626620 # Get the relevant portion of the data
627621 data = raw_stim_data [i_start :i_stop ]
628-
622+
629623 # Extract current value (bits 0-8)
630624 magnitude = np .bitwise_and (data , 0xFF )
631625 sign_bit = np .bitwise_and (np .right_shift (data , 8 ), 0x01 ) # Shift right by 8 bits to get the sign bit
632-
626+
633627 # Apply sign to current values
634628 # We need to cast to int16 to handle negative values correctly
635629 # The max value of 8 bits is 255 so the casting is safe as there are non-negative values
636630 magnitude = magnitude .astype (np .int16 )
637631 current = np .where (sign_bit == 1 , - magnitude , magnitude )
638-
632+
639633 # Note: If needed, other flag bits could be extracted as follows:
640634 # compliance_flag = np.bitwise_and(np.right_shift(data, 15), 0x01).astype(bool) # Bit 16 (MSB)
641635 # charge_recovery_flag = np.bitwise_and(np.right_shift(data, 14), 0x01).astype(bool) # Bit 15
642636 # amp_settle_flag = np.bitwise_and(np.right_shift(data, 13), 0x01).astype(bool) # Bit 14
643637 # These could be returned as a structured array or dictionary if needed
644-
638+
645639 return current
646640
647641 def get_intan_timestamps (self , i_start = None , i_stop = None ):
@@ -974,8 +968,12 @@ def read_rhs(filename, file_format: str):
974968 chan_info_stim ["gain" ] = global_info ["stim_step_size" ]
975969 chan_info_stim ["offset" ] = 0.0
976970 chan_info_stim ["signal_type" ] = 11 # put it in another group
977- chan_info_stim ["dtype" ] = "uint16 "
971+ chan_info_stim ["dtype" ] = "int16 "
978972 ordered_channel_info .append (chan_info_stim )
973+
974+ # Note that the data on disk is uint16 but the data is
975+ # then decoded as int16 so the chan_info is int16
976+ memmap_dtype = "uint16"
979977 if file_format == "header-attached" :
980978 memmap_data_dtype += [(name + "_STIM" , "uint16" , BLOCK_SIZE )]
981979 else :
0 commit comments