11import numpy as np
22import os , sys , struct
33from pathlib import Path
4- import matplotlib .pyplot as plt
54
65
7- def read_qstring (fid ):
6+ def _read_qstring (fid ):
87 """Read Qt style QString.
98
109 The first 32-bit unsigned number indicates the length of the string (in bytes).
@@ -37,7 +36,7 @@ def read_qstring(fid):
3736 return a
3837
3938
40- def read_header (fid ):
39+ def _read_header (fid ):
4140 """Reads the Intan File Format header from the given file."""
4241
4342 # Check 'magic number' at beginning of file to make sure this is an Intan
@@ -53,14 +52,6 @@ def read_header(fid):
5352 (version ["major" ], version ["minor" ]) = struct .unpack ("<hh" , fid .read (4 ))
5453 header ["version" ] = version
5554
56- print ("" )
57- print (
58- "Reading Intan Technologies RHS2000 Data File, Version {}.{}" .format (
59- version ["major" ], version ["minor" ]
60- )
61- )
62- print ("" )
63-
6455 # Read information of sampling rate and amplifier frequency settings.
6556 (header ["sample_rate" ],) = struct .unpack ("<f" , fid .read (4 ))
6657 (
@@ -129,16 +120,16 @@ def read_header(fid):
129120 header ["recovery_target_voltage" ],
130121 ) = struct .unpack ("fff" , fid .read (12 ))
131122
132- note1 = read_qstring (fid )
133- note2 = read_qstring (fid )
134- note3 = read_qstring (fid )
123+ note1 = _read_qstring (fid )
124+ note2 = _read_qstring (fid )
125+ note3 = _read_qstring (fid )
135126 header ["notes" ] = {"note1" : note1 , "note2" : note2 , "note3" : note3 }
136127
137128 (header ["dc_amplifier_data_saved" ], header ["eval_board_mode" ]) = struct .unpack (
138129 "<hh" , fid .read (4 )
139130 )
140131
141- header ["ref_channel_name" ] = read_qstring (fid )
132+ header ["ref_channel_name" ] = _read_qstring (fid )
142133
143134 # Create structure arrays for each type of data channel.
144135 header ["spike_triggers" ] = []
@@ -150,11 +141,11 @@ def read_header(fid):
150141
151142 # Read signal summary from data file header.
152143 (number_of_signal_groups ,) = struct .unpack ("<h" , fid .read (2 ))
153- print ("n signal groups {}" .format (number_of_signal_groups ))
144+ # print("n signal groups {}".format(number_of_signal_groups))
154145
155146 for signal_group in range (1 , number_of_signal_groups + 1 ):
156- signal_group_name = read_qstring (fid )
157- signal_group_prefix = read_qstring (fid )
147+ signal_group_name = _read_qstring (fid )
148+ signal_group_prefix = _read_qstring (fid )
158149 (
159150 signal_group_enabled ,
160151 signal_group_num_channels ,
@@ -168,8 +159,8 @@ def read_header(fid):
168159 "port_prefix" : signal_group_prefix ,
169160 "port_number" : signal_group ,
170161 }
171- new_channel ["native_channel_name" ] = read_qstring (fid )
172- new_channel ["custom_channel_name" ] = read_qstring (fid )
162+ new_channel ["native_channel_name" ] = _read_qstring (fid )
163+ new_channel ["custom_channel_name" ] = _read_qstring (fid )
173164 (
174165 new_channel ["native_order" ],
175166 new_channel ["custom_order" ],
@@ -224,22 +215,23 @@ def read_header(fid):
224215 return header
225216
226217
227- def load_rhs (folder : str , file_expr : str ):
218+ def load_rhs (folder : str , file_expr : str = "*" ):
228219 """Load rhs data
229220
230221 Example:
231222 # Read data
232223 >>> rhs_data = load_rhs("/home/inbox/organoids21/032520_US_885kHz_sham", file_expr="amp*dat")
233224
234225 # Plot data
226+ >>> import matplotlib.pyplot as plt
235227 >>> plt.plot(rhs_data["time"], rhs_data["recordings"]["amp-B-000.dat"])
236228 >>> plt.xlabel("Time (s)")
237229 >>> plt.ylabel("Reading")
238230 >>> plt.show()
239231
240232 Args:
241233 folder (str): Folder that contains info.rhs, time.dat, and *.dat files
242- file_expr (str): regex pattern of the file names to be read.
234+ file_expr (str): pattern matching of file names to be read. Defaults to "*" (read all files) .
243235
244236 Returns:
245237 rhs_data (dict): RHS data.
@@ -248,40 +240,56 @@ def load_rhs(folder: str, file_expr: str):
248240 rhs_data["timestamps"] (np.array_like): Relative timestamps in seconds.
249241 """
250242
243+ rhs_data = {}
244+
245+ # Get header
251246 header_filepath = next (Path (folder ).glob ("info.rhs" ))
252247 with open (header_filepath , "rb" ) as fid :
253- header = read_header (fid )
248+ rhs_data [ " header" ] = _read_header (fid )
254249
250+ # Get timestamps
255251 time_file = next (Path (folder ).glob ("time.dat" ))
256252
257- timestamps = (
253+ rhs_data [ " timestamps" ] = (
258254 np .memmap (time_file , dtype = np .int32 )
259- / header ["frequency_parameters" ]["amplifier_sample_rate" ]
255+ / rhs_data [ " header" ] ["frequency_parameters" ]["amplifier_sample_rate" ]
260256 )
261257
262- rhs_data = dict (header = header , timestamps = timestamps , recordings = {})
263-
258+ # Get data files
264259 file_paths = Path (folder ).glob (file_expr )
265- file_paths = [x for x in file_paths if x .as_posix () != "time.dat" ]
266260
267- for file_path in file_paths :
268- file_path = file_path .as_posix ()
269- if "amp" in file_path :
261+ exclude_list = ["time" , "info" , "Zone.Identifier" ]
262+
263+ file_paths = [
264+ file
265+ for file in file_paths
266+ if not any (string in file .as_posix () for string in exclude_list )
267+ ]
268+
269+ # Get recording data
270+ rhs_data ["recordings" ] = {}
271+
272+ for file_path in sorted (file_paths ):
273+ signal_type = file_path .stem .split ("-" )[0 ]
274+
275+ if signal_type == "amp" :
270276 signal = np .memmap (file_path , dtype = np .int16 )
271277 signal = signal * 0.195 # Convert to microvolts
272- elif "board-ANALOG-IN" in file_path or "board-ANALOG-OUT" in file_path :
278+
279+ elif signal_type == "board" :
273280 signal = np .memmap (file_path , dtype = np .uint16 )
274281 signal = (signal - 32768 ) * 0.0003125 # Convert to volts
275- elif "dc-" in file_path :
282+
283+ elif signal_type == "dc" :
276284 signal = np .memmap (file_path , dtype = np .uint16 )
277285 signal = (signal - 512 ) * 19.23 # Convert to milivolts
278- elif "board-DIGITAL-IN" in file_path or "board-DIGITAL-OUT" in file_path :
286+
287+ elif signal_type == "stim" :
279288 signal = np .memmap (file_path , dtype = np .uint16 )
280- elif "stim-" in file_path :
281- data = np .memmap (file_path , dtype = np .uint16 )
282- i = np .bitwise_and (data , 255 ) * header ["stim_step_size" ]
283- sign = (128 - np .bitwise_and (data , 255 )) / 128
284- signal = i * sign
285- rhs_data ["recordings" ][Path (file_path ).relative_to (folder ).stem ] = signal
289+ # convert the signal from 9-bit one's complement to standard encoding
290+ current = np .bitwise_and (signal , 255 ) * rhs_data ["header" ]["stim_step_size" ]
291+ sign = 1 - np .bitwise_and (signal , 256 ) // 128
292+ signal = current * sign
286293
294+ rhs_data ["recordings" ][file_path .stem ] = signal
287295 return rhs_data
0 commit comments