Skip to content

Commit 3832c89

Browse files
committed
merge
2 parents cae4bac + 3208bc7 commit 3832c89

File tree

5 files changed

+101
-77
lines changed

5 files changed

+101
-77
lines changed

neo/rawio/blackrockrawio.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ class BlackrockRawIO(BaseRawIO):
9191
Any .nsX or .nev, .sif, or .ccf extensions are ignored when parsing this parameter.
9292
nsx_override: str | None, default: None
9393
File name of the .nsX files (without extension). If None, filename is used.
94-
nev_override str | None, default: None
94+
nev_override: str | None, default: None
9595
File name of the .nev file (without extension). If None, filename is used.
96-
nsx_to_load int | list | 'max' | 'all' | None, default None:
96+
nsx_to_load: int | list | 'max' | 'all' | None, default: None
9797
IDs of nsX file from which to load data, e.g., if set to 5 only data from the ns5 file are loaded.
9898
If 'all', then all nsX will be loaded. Contrary to previous version of the IO (<0.7), nsx_to_load
9999
must be set at the init before parse_header().
@@ -103,8 +103,8 @@ class BlackrockRawIO(BaseRawIO):
103103
Notes
104104
-----
105105
* Note: This routine will handle files according to specification 2.1, 2.2,
106-
and 2.3. Recording pauses that may occur in file specifications 2.2 and
107-
2.3 are automatically extracted and the data set is split into different
106+
2.3, 3.0 and 3.0-ptp. Recording pauses that may occur in file specifications
107+
2.2 and 2.3 are automatically extracted and the data set is split into different
108108
segments.
109109
110110
* The Blackrock data format consists not of a single file, but a set of

neo/rawio/intanrawio.py

Lines changed: 72 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
_signal_stream_dtype,
3535
_spike_channel_dtype,
3636
_event_channel_dtype,
37-
_common_sig_characteristics,
3837
)
3938

4039

@@ -58,11 +57,13 @@ class IntanRawIO(BaseRawIO):
5857
'one-file-per-channel' which have a header file called 'info.rhd' or 'info.rhs' and a series
5958
of binary files with the '.dat' suffix
6059
60+
* The reader can handle three file formats 'header-attached', 'one-file-per-signal' and
61+
'one-file-per-channel'.
62+
6163
* Intan files contain amplifier channels labeled 'A', 'B' 'C' or 'D'
6264
depending on the port in which they were recorded along with the following
63-
additional channels for RHD:
64-
65-
0: 'RHD2000 amplifier channel'
65+
additional streams.
66+
0: 'RHD2000' amplifier channel
6667
1: 'RHD2000 auxiliary input channel',
6768
2: 'RHD2000 supply voltage channel',
6869
3: 'USB board ADC input channel',
@@ -79,9 +80,9 @@ class IntanRawIO(BaseRawIO):
7980
10: 'DC Amplifier channel',
8081
11: 'Stim channel',
8182
82-
* Due to the structure of the digital input and output channels these can be accessed
83-
as one long vector, which must be post-processed in the case of 'header-attached' or
84-
'one-file-per-stream' formats.
83+
* For the "header-attached" and "one-file-per-signal" formats, the structure of the digital input and output channels is
84+
one long vector, which must be post-processed to extract individual digital channel information.
85+
See the intantech website for more information on performing this post-processing.
8586
8687
Examples
8788
--------
@@ -585,7 +586,6 @@ def read_rhs(filename, file_format: str):
585586

586587
# 0: RHS2000 amplifier channel.
587588
for chan_info in channels_by_type[0]:
588-
name = chan_info["native_channel_name"]
589589
chan_info["sampling_rate"] = sr
590590
chan_info["units"] = "uV"
591591
chan_info["gain"] = 0.195
@@ -599,6 +599,7 @@ def read_rhs(filename, file_format: str):
599599
chan_info["dtype"] = "int16"
600600
ordered_channel_info.append(chan_info)
601601
if file_format == "header-attached":
602+
name = chan_info["native_channel_name"]
602603
data_dtype += [(name, "uint16", BLOCK_SIZE)]
603604
else:
604605
data_dtype[0] = "int16"
@@ -607,8 +608,8 @@ def read_rhs(filename, file_format: str):
607608
# if we have dc amp we need to grab the correct number of channels
608609
channel_number_dict[10] = channel_number_dict[0]
609610
for chan_info in channels_by_type[0]:
610-
name = chan_info["native_channel_name"]
611611
chan_info_dc = dict(chan_info)
612+
name = chan_info["native_channel_name"]
612613
chan_info_dc["native_channel_name"] = name + "_DC"
613614
chan_info_dc["sampling_rate"] = sr
614615
chan_info_dc["units"] = "mV"
@@ -627,8 +628,8 @@ def read_rhs(filename, file_format: str):
627628
if file_format != "one-file-per-channel":
628629
channel_number_dict[11] = channel_number_dict[0] # should be one stim / amplifier channel
629630
for chan_info in channels_by_type[0]:
630-
name = chan_info["native_channel_name"]
631631
chan_info_stim = dict(chan_info)
632+
name = chan_info["native_channel_name"]
632633
chan_info_stim["native_channel_name"] = name + "_STIM"
633634
chan_info_stim["sampling_rate"] = sr
634635
# stim channel are complicated because they are coded
@@ -650,43 +651,51 @@ def read_rhs(filename, file_format: str):
650651

651652
# 3: Analog input channel.
652653
# 4: Analog output channel.
653-
for sig_type in [
654-
3,
655-
4,
656-
]:
654+
for sig_type in [3, 4]:
657655
for chan_info in channels_by_type[sig_type]:
658-
name = chan_info["native_channel_name"]
659656
chan_info["sampling_rate"] = sr
660657
chan_info["units"] = "V"
661658
chan_info["gain"] = 0.0003125
662659
chan_info["offset"] = -32768 * 0.0003125
663660
chan_info["dtype"] = "uint16"
664661
ordered_channel_info.append(chan_info)
665662
if file_format == "header-attached":
663+
name = chan_info["native_channel_name"]
666664
data_dtype += [(name, "uint16", BLOCK_SIZE)]
667665
else:
668666
data_dtype[sig_type] = "uint16"
669667

670668
# 5: Digital input channel.
671669
# 6: Digital output channel.
672670
for sig_type in [5, 6]:
673-
if len(channels_by_type[sig_type]) > 0:
674-
name = {5: "DIGITAL-IN", 6: "DIGITAL-OUT"}[sig_type]
675-
chan_info = channels_by_type[sig_type][0]
676-
# So currently until we have get_digitalsignal_chunk we need to do a tiny hack to
677-
# make this memory map work correctly. So since our digital data is not organized
678-
# by channel like analog/ADC are we have to overwrite the native name to create
679-
# a single permanent name that we can find with channel id
680-
chan_info["native_channel_name"] = name # overwite to allow memmap to work
681-
chan_info["sampling_rate"] = sr
682-
chan_info["units"] = "TTL" # arbitrary units TTL for logic
683-
chan_info["gain"] = 1.0
684-
chan_info["offset"] = 0.0
685-
chan_info["dtype"] = "uint16"
686-
ordered_channel_info.append(chan_info)
687-
if file_format == "header-attached":
688-
data_dtype += [(name, "uint16", BLOCK_SIZE)]
689-
else:
671+
if file_format in ["header-attached", "one-file-per-signal"]:
672+
if len(channels_by_type[sig_type]) > 0:
673+
name = {5: "DIGITAL-IN", 6: "DIGITAL-OUT"}[sig_type]
674+
chan_info = channels_by_type[sig_type][0]
675+
# So currently until we have get_digitalsignal_chunk we need to do a tiny hack to
676+
# make this memory map work correctly. So since our digital data is not organized
677+
# by channel like analog/ADC are we have to overwrite the native name to create
678+
# a single permanent name that we can find with channel id
679+
chan_info["native_channel_name"] = name
680+
chan_info["sampling_rate"] = sr
681+
chan_info["units"] = "TTL" # arbitrary units TTL for logic
682+
chan_info["gain"] = 1.0
683+
chan_info["offset"] = 0.0
684+
chan_info["dtype"] = "uint16"
685+
ordered_channel_info.append(chan_info)
686+
if file_format == "header-attached":
687+
data_dtype += [(name, "uint16", BLOCK_SIZE)]
688+
else:
689+
data_dtype[sig_type] = "uint16"
690+
# This case behaves as a binary with 0 and 1 coded as uint16
691+
elif file_format == "one-file-per-channel":
692+
for chan_info in channels_by_type[sig_type]:
693+
chan_info["sampling_rate"] = sr
694+
chan_info["units"] = "TTL"
695+
chan_info["gain"] = 1.0
696+
chan_info["offset"] = 0.0
697+
chan_info["dtype"] = "uint16"
698+
ordered_channels_info.append(chan_info)
690699
data_dtype[sig_type] = "uint16"
691700

692701
# per discussion with Intan developers before version 3 of their software the 'notch_filter_mode'
@@ -866,7 +875,6 @@ def read_rhd(filename, file_format: str):
866875

867876
# 0: RHD2000 amplifier channel
868877
for chan_info in channels_by_type[0]:
869-
name = chan_info["native_channel_name"]
870878
chan_info["sampling_rate"] = sr
871879
chan_info["units"] = "uV"
872880
chan_info["gain"] = 0.195
@@ -879,34 +887,35 @@ def read_rhd(filename, file_format: str):
879887
ordered_channel_info.append(chan_info)
880888

881889
if file_format == "header-attached":
890+
name = chan_info["native_channel_name"]
882891
data_dtype += [(name, "uint16", BLOCK_SIZE)]
883892
else:
884893
data_dtype[0] = "int16"
885894

886895
# 1: RHD2000 auxiliary input channel
887896
for chan_info in channels_by_type[1]:
888-
name = chan_info["native_channel_name"]
889897
chan_info["sampling_rate"] = sr / 4.0
890898
chan_info["units"] = "V"
891899
chan_info["gain"] = 0.0000374
892900
chan_info["offset"] = 0.0
893901
chan_info["dtype"] = "uint16"
894902
ordered_channel_info.append(chan_info)
895903
if file_format == "header-attached":
904+
name = chan_info["native_channel_name"]
896905
data_dtype += [(name, "uint16", BLOCK_SIZE // 4)]
897906
else:
898907
data_dtype[1] = "uint16"
899908

900909
# 2: RHD2000 supply voltage channel
901910
for chan_info in channels_by_type[2]:
902-
name = chan_info["native_channel_name"]
903911
chan_info["sampling_rate"] = sr / BLOCK_SIZE
904912
chan_info["units"] = "V"
905913
chan_info["gain"] = 0.0000748
906914
chan_info["offset"] = 0.0
907915
chan_info["dtype"] = "uint16"
908916
ordered_channel_info.append(chan_info)
909917
if file_format == "header-attached":
918+
name = chan_info["native_channel_name"]
910919
data_dtype += [(name, "uint16")]
911920
else:
912921
data_dtype[2] = "uint16"
@@ -925,7 +934,6 @@ def read_rhd(filename, file_format: str):
925934

926935
# 3: USB board ADC input channel
927936
for chan_info in channels_by_type[3]:
928-
name = chan_info["native_channel_name"]
929937
chan_info["sampling_rate"] = sr
930938
chan_info["units"] = "V"
931939
if global_info["eval_board_mode"] == 0:
@@ -940,32 +948,41 @@ def read_rhd(filename, file_format: str):
940948
chan_info["dtype"] = "uint16"
941949
ordered_channel_info.append(chan_info)
942950
if file_format == "header-attached":
951+
name = chan_info["native_channel_name"]
943952
data_dtype += [(name, "uint16", BLOCK_SIZE)]
944953
else:
945954
data_dtype[3] = "uint16"
946955

947956
# 4: USB board digital input channel
948957
# 5: USB board digital output channel
949958
for sig_type in [4, 5]:
950-
# Now these are included so that user can obtain the
951-
# dig signals and process them at the same time
952-
if len(channels_by_type[sig_type]) > 0:
953-
name = {4: "DIGITAL-IN", 5: "DIGITAL-OUT"}[sig_type]
954-
chan_info = channels_by_type[sig_type][0]
955-
# So currently until we have get_digitalsignal_chunk we need to do a tiny hack to
956-
# make this memory map work correctly. So since our digital data is not organized
957-
# by channel like analog/ADC are we have to overwrite the native name to create
958-
# a single permanent name that we can find with channel id
959-
chan_info["native_channel_name"] = name # overwite to allow memmap to work
960-
chan_info["sampling_rate"] = sr
961-
chan_info["units"] = "TTL" # arbitrary units TTL for logic
962-
chan_info["gain"] = 1.0
963-
chan_info["offset"] = 0.0
964-
chan_info["dtype"] = "uint16"
965-
ordered_channel_info.append(chan_info)
966-
if file_format == "header-attached":
967-
data_dtype += [(name, "uint16", BLOCK_SIZE)]
968-
else:
959+
if file_format in ["header-attached", "one-file-per-signal"]:
960+
if len(channels_by_type[sig_type]) > 0:
961+
name = {4: "DIGITAL-IN", 5: "DIGITAL-OUT"}[sig_type]
962+
chan_info = channels_by_type[sig_type][0]
963+
# So currently until we have get_digitalsignal_chunk we need to do a tiny hack to
964+
# make this memory map work correctly. So since our digital data is not organized
965+
# by channel like analog/ADC are we have to overwrite the native name to create
966+
# a single permanent name that we can find with channel id
967+
chan_info["native_channel_name"] = name
968+
chan_info["sampling_rate"] = sr
969+
chan_info["units"] = "TTL" # arbitrary units TTL for logic
970+
chan_info["gain"] = 1.0
971+
chan_info["offset"] = 0.0
972+
chan_info["dtype"] = "uint16"
973+
ordered_channel_info.append(chan_info)
974+
if file_format == "header-attached":
975+
data_dtype += [(name, "uint16", BLOCK_SIZE)]
976+
else:
977+
data_dtype[sig_type] = "uint16"
978+
elif file_format == "one-file-per-channel":
979+
for chan_info in channels_by_type[sig_type]:
980+
chan_info["sampling_rate"] = sr
981+
chan_info["units"] = "TTL"
982+
chan_info["gain"] = 1.0
983+
chan_info["offset"] = 0.0
984+
chan_info["dtype"] = "uint16"
985+
ordered_channels_info.append(chan_info)
969986
data_dtype[sig_type] = "uint16"
970987

971988
# per discussion with Intan developers before version 3 of their software the 'notch_filter_mode'

neo/rawio/spikeglxrawio.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -566,10 +566,12 @@ def extract_stream_info(meta_file, meta):
566566
info["digital_channels"] = []
567567
info["analog_channels"] = [channel for channel in info["channel_names"] if not channel.startswith("XD")]
568568
# Digital/event channels are encoded within the digital word, so that will need more handling
569-
for item in meta["niXDChans1"].split(","):
570-
if ":" in item:
571-
start, end = map(int, item.split(":"))
572-
info["digital_channels"].extend([f"XD{i}" for i in range(start, end + 1)])
573-
else:
574-
info["digital_channels"].append(f"XD{int(item)}")
569+
if meta.get("niXDChans1", "") != "":
570+
nixd_chans1_items = meta["niXDChans1"].split(",")
571+
for item in nixd_chans1_items:
572+
if ":" in item:
573+
start, end = map(int, item.split(":"))
574+
info["digital_channels"].extend([f"XD{i}" for i in range(start, end + 1)])
575+
else:
576+
info["digital_channels"].append(f"XD{int(item)}")
575577
return info

neo/test/iotest/test_intanio.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ class TestIntanIO(
1515
ioclass = IntanIO
1616
entities_to_download = ["intan"]
1717
entities_to_test = [
18-
"intan/intan_rhs_test_1.rhs",
19-
"intan/intan_rhd_test_1.rhd",
20-
"intan/intan_fpc_test_231117_052630/info.rhd",
21-
"intan/intan_fps_test_231117_052500/info.rhd",
22-
"intan/intan_fpc_rhs_test_240329_091637/info.rhs",
23-
"intan/intan_fps_rhs_test_240329_091536/info.rhs",
18+
"intan/intan_rhs_test_1.rhs", # Format header-attached
19+
"intan/intan_rhd_test_1.rhd", # Format header-attached
20+
"intan/rhs_fpc_multistim_240514_082243/rhs_fpc_multistim_240514_082243.rhs", # Format header-attached newer version
21+
"intan/intan_fpc_test_231117_052630/info.rhd", # Format one-file-per-channel
22+
"intan/intan_fps_test_231117_052500/info.rhd", # Format one file per signal
23+
"intan/intan_fpc_rhs_test_240329_091637/info.rhs", # Format one-file-per-channel
24+
"intan/intan_fps_rhs_test_240329_091536/info.rhs", # Format one-file-per-signal
25+
"intan/rhd_fpc_multistim_240514_082044/info.rhd", # Multiple digital channels one-file-per-channel rhd
2426
]
2527

2628

neo/test/rawiotest/test_intanrawio.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ class TestIntanRawIO(
1212
rawioclass = IntanRawIO
1313
entities_to_download = ["intan"]
1414
entities_to_test = [
15-
"intan/intan_rhs_test_1.rhs",
16-
"intan/intan_rhd_test_1.rhd",
17-
"intan/intan_fpc_test_231117_052630/info.rhd",
18-
"intan/intan_fps_test_231117_052500/info.rhd",
19-
"intan/intan_fpc_rhs_test_240329_091637/info.rhs",
20-
"intan/intan_fps_rhs_test_240329_091536/info.rhs",
15+
"intan/intan_rhs_test_1.rhs", # Format header-attached
16+
"intan/intan_rhd_test_1.rhd", # Format header-attached
17+
"intan/rhs_fpc_multistim_240514_082243/rhs_fpc_multistim_240514_082243.rhs", # Format header-attached newer version
18+
"intan/intan_fpc_test_231117_052630/info.rhd", # Format one-file-per-channel
19+
"intan/intan_fps_test_231117_052500/info.rhd", # Format one file per signal
20+
"intan/intan_fpc_rhs_test_240329_091637/info.rhs", # Format one-file-per-channel
21+
"intan/intan_fps_rhs_test_240329_091536/info.rhs", # Format one-file-per-signal
22+
"intan/rhd_fpc_multistim_240514_082044/info.rhd", # Multiple digital channels one-file-per-channel rhd
2123
]
2224

2325

@@ -47,5 +49,6 @@ def test_annotations(self):
4749
np.testing.assert_array_equal(signal_array_annotations["native_order"][:10], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
4850
np.testing.assert_array_equal(signal_array_annotations["spike_scope_digital_edge_polarity"][:10], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
4951
np.testing.assert_array_equal(signal_array_annotations["board_stream_num"][:10], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
52+
5053
if __name__ == "__main__":
5154
unittest.main()

0 commit comments

Comments
 (0)