Skip to content

Commit eb4d611

Browse files
authored
Merge pull request #75 from JaerongA/intan
refactor: 🎨 streamline the code
2 parents 1e1abae + 283aac3 commit eb4d611

1 file changed

Lines changed: 48 additions & 40 deletions

File tree

element_interface/intan_loader.py

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import numpy as np
22
import os, sys, struct
33
from 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

Comments
 (0)