@@ -21,6 +21,10 @@ class TestIntanRawIO(
2121 "intan/intan_fpc_rhs_test_240329_091637/info.rhs" , # Format one-file-per-channel
2222 "intan/intan_fps_rhs_test_240329_091536/info.rhs" , # Format one-file-per-signal
2323 "intan/rhd_fpc_multistim_240514_082044/info.rhd" , # Multiple digital channels one-file-per-channel rhd
24+ "intan/rhs_stim_data_single_file_format/intanTestFile.rhs" , # header-attached rhs data with stimulus current
25+ "intan/test_fcs_dc_250327_154333/info.rhs" , # this is an example of only having dc amp rather than amp files
26+ "intan/test_fpc_stim_250327_151617/info.rhs" , # wrong files names Heberto will fix naimgin in the future
27+
2428 ]
2529
2630 def test_annotations (self ):
@@ -67,7 +71,7 @@ def test_annotations(self):
6771 )
6872 np .testing .assert_array_equal (signal_array_annotations ["board_stream_num" ][:10 ], [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ])
6973
70- def test_correct_reading_one_file_per_channel (self ):
74+ def test_correct_reading_one_file_per_channel_amplifiers (self ):
7175 "Issue: https://github.com/NeuralEnsemble/python-neo/issues/1599"
7276 # Test reading of one-file-per-channel format file. The channels should match the raw files
7377 file_path = Path (self .get_local_path ("intan/intan_fpc_test_231117_052630/info.rhd" ))
@@ -81,11 +85,134 @@ def test_correct_reading_one_file_per_channel(self):
8185 amplifier_file_paths = [path for path in folder_path .iterdir () if "amp" in path .name ]
8286 channel_names = [path .name [4 :- 4 ] for path in amplifier_file_paths ]
8387
88+ amplifier_stream_index = 0
8489 for channel_name , amplifier_file_path in zip (channel_names , amplifier_file_paths ):
8590 data_raw = np .fromfile (amplifier_file_path , dtype = np .int16 ).squeeze ()
86- data_from_neo = intan_reader .get_analogsignal_chunk (channel_ids = [channel_name ], stream_index = 0 ).squeeze ()
91+ data_from_neo = intan_reader .get_analogsignal_chunk (channel_ids = [channel_name ], stream_index = amplifier_stream_index ).squeeze ()
8792 np .testing .assert_allclose (data_raw , data_from_neo )
8893
94+ def test_correct_reading_one_file_per_channel_rhs_stim (self ):
95+ "Zach request for testing that the channel order is correct"
96+ # Test reading of one-file-per-channel format file. The channels should match the raw files
97+ file_path = Path (self .get_local_path ("intan/test_fpc_stim_250327_151617/info.rhs" ))
98+ intan_reader = IntanRawIO (filename = file_path )
99+ intan_reader .parse_header ()
100+
101+ # This should be the folder where the files of all the channels are stored
102+ folder_path = file_path .parent
103+
104+ # The paths for the stim channels are stim-A-000.dat, stim-A-001.dat, stim-A-002.dat,
105+ # Whereas the ids are A-001_STIM, A-002_STIM, A-003_STIM, etc
106+ stim_file_paths = [path for path in folder_path .iterdir () if "stim" in path .name ]
107+ channel_ids = [f"{ p .stem [5 :]} _STIM" for p in stim_file_paths ]
108+
109+ stim_stream_index = 2
110+ for channel_id , amplifier_file_path in zip (channel_ids , stim_file_paths ):
111+ data_raw = np .fromfile (amplifier_file_path , dtype = np .uint16 )
112+ decoded_data = intan_reader ._decode_current_from_stim_data (data_raw , 0 , data_raw .shape [0 ])
113+ data_from_neo = intan_reader .get_analogsignal_chunk (channel_ids = [channel_id ], stream_index = stim_stream_index ).squeeze ()
114+ np .testing .assert_allclose (decoded_data , data_from_neo )
115+
116+
117+ def test_correct_decoding_of_stimulus_current (self ):
118+ # See https://github.com/NeuralEnsemble/python-neo/pull/1660 for discussion
119+ # See https://gin.g-node.org/NeuralEnsemble/ephy_testing_data/src/master/intan/README.md#rhs_stim_data_single_file_format
120+ # For a description of the data
121+
122+ file_path = Path (self .get_local_path ("intan/rhs_stim_data_single_file_format/intanTestFile.rhs" ))
123+ intan_reader = IntanRawIO (filename = file_path )
124+ intan_reader .parse_header ()
125+
126+ signal_streams = intan_reader .header ['signal_streams' ]
127+ stream_ids = signal_streams ['id' ].tolist ()
128+ stream_index = stream_ids .index ('11' )
129+ sampling_rate = intan_reader .get_signal_sampling_rate (stream_index = stream_index )
130+ sig_chunk = intan_reader .get_analogsignal_chunk (stream_index = stream_index , channel_ids = ["D-016_STIM" ])
131+ final_stim = intan_reader .rescale_signal_raw_to_float (sig_chunk , stream_index = stream_index , channel_ids = ["D-016_STIM" ])
132+
133+ # This contains only the first pulse and I got this by visual inspection
134+ data_to_test = final_stim [200 :250 ]
135+
136+ positive_pulse_size = np .max (data_to_test ).item ()
137+ negative_pulse_size = np .min (data_to_test ).item ()
138+
139+ expected_value = 60 * 1e-6 # 60 microamperes
140+
141+ # Assert is close float
142+ assert np .isclose (positive_pulse_size , expected_value )
143+ assert np .isclose (negative_pulse_size , - expected_value )
144+
145+ # Check that negative pulse is leading
146+ argmin = np .argmin (data_to_test )
147+ argmax = np .argmax (data_to_test )
148+ assert argmin < argmax
149+
150+ # Check that the negative pulse is 200 us long
151+ negative_pulse_frames = np .where (data_to_test > 0 )[0 ]
152+ number_of_negative_frames = negative_pulse_frames .size
153+ duration_of_negative_pulse = number_of_negative_frames / sampling_rate
154+
155+ expected_duration = 200 * 1e-6 # 400 microseconds / 2
156+ assert np .isclose (duration_of_negative_pulse , expected_duration )
157+
158+ # Check that the positive pulse is 200 us long
159+ positive_pulse_frames = np .where (data_to_test > 0 )[0 ]
160+ number_of_positive_frames = positive_pulse_frames .size
161+ duration_of_positive_pulse = number_of_positive_frames / sampling_rate
162+ expected_duration = 200 * 1e-6 # 400 microseconds / 2
163+
164+ assert np .isclose (duration_of_positive_pulse , expected_duration )
165+
166+
167+ def test_correct_decoding_of_stimulus_current (self ):
168+ # See https://github.com/NeuralEnsemble/python-neo/pull/1660 for discussion
169+ # See https://gin.g-node.org/NeuralEnsemble/ephy_testing_data/src/master/intan/README.md#rhs_stim_data_single_file_format
170+ # For a description of the data
171+
172+ file_path = Path (self .get_local_path ("intan/rhs_stim_data_single_file_format/intanTestFile.rhs" ))
173+ intan_reader = IntanRawIO (filename = file_path )
174+ intan_reader .parse_header ()
175+
176+ signal_streams = intan_reader .header ['signal_streams' ]
177+ stream_ids = signal_streams ['id' ].tolist ()
178+ stream_index = stream_ids .index ('11' )
179+ sampling_rate = intan_reader .get_signal_sampling_rate (stream_index = stream_index )
180+ sig_chunk = intan_reader .get_analogsignal_chunk (stream_index = stream_index , channel_ids = ["D-016_STIM" ])
181+ final_stim = intan_reader .rescale_signal_raw_to_float (sig_chunk , stream_index = stream_index , channel_ids = ["D-016_STIM" ])
182+
183+ # This contains only the first pulse and I got this by visual inspection
184+ data_to_test = final_stim [200 :250 ]
185+
186+ positive_pulse_size = np .max (data_to_test ).item ()
187+ negative_pulse_size = np .min (data_to_test ).item ()
188+
189+ expected_value = 60 * 1e-6 # 60 microamperes
190+
191+ # Assert is close float
192+ assert np .isclose (positive_pulse_size , expected_value )
193+ assert np .isclose (negative_pulse_size , - expected_value )
194+
195+ # Check that negative pulse is leading
196+ argmin = np .argmin (data_to_test )
197+ argmax = np .argmax (data_to_test )
198+ assert argmin < argmax
199+
200+ # Check that the negative pulse is 200 us long
201+ negative_pulse_frames = np .where (data_to_test > 0 )[0 ]
202+ number_of_negative_frames = negative_pulse_frames .size
203+ duration_of_negative_pulse = number_of_negative_frames / sampling_rate
204+
205+ expected_duration = 200 * 1e-6 # 400 microseconds / 2
206+ assert np .isclose (duration_of_negative_pulse , expected_duration )
207+
208+ # Check that the positive pulse is 200 us long
209+ positive_pulse_frames = np .where (data_to_test > 0 )[0 ]
210+ number_of_positive_frames = positive_pulse_frames .size
211+ duration_of_positive_pulse = number_of_positive_frames / sampling_rate
212+ expected_duration = 200 * 1e-6 # 400 microseconds / 2
213+
214+ assert np .isclose (duration_of_positive_pulse , expected_duration )
215+
89216
90217if __name__ == "__main__" :
91218 unittest .main ()
0 commit comments