Skip to content

Commit b507562

Browse files
committed
refactor and comments
1 parent ea95545 commit b507562

File tree

1 file changed

+37
-39
lines changed

1 file changed

+37
-39
lines changed

neo/rawio/intanrawio.py

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)