eegdash 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of eegdash might be problematic. Click here for more details.
- eegdash/SignalStore/__init__.py +0 -0
- eegdash/SignalStore/signalstore/__init__.py +3 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/abstract_read_adapter.py +13 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/domain_modeling/schema_read_adapter.py +16 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/domain_modeling/vocabulary_read_adapter.py +19 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/handmade_records/excel_study_organizer_read_adapter.py +114 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/axona/axona_read_adapter.py +912 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/ReadIntanSpikeFile.py +140 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/intan_read_adapter.py +29 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhd_format/intanutil/__init__.py +0 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhd_format/intanutil/data_to_result.py +62 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhd_format/intanutil/get_bytes_per_data_block.py +36 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhd_format/intanutil/notch_filter.py +50 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhd_format/intanutil/qstring.py +41 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhd_format/intanutil/read_header.py +135 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhd_format/intanutil/read_one_data_block.py +45 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhd_format/load_intan_rhd_format.py +204 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhs_format/intanutil/__init__.py +0 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhs_format/intanutil/data_to_result.py +60 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhs_format/intanutil/get_bytes_per_data_block.py +37 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhs_format/intanutil/notch_filter.py +50 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhs_format/intanutil/qstring.py +41 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhs_format/intanutil/read_header.py +153 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhs_format/intanutil/read_one_data_block.py +47 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/intan/load_intan_rhs_format/load_intan_rhs_format.py +213 -0
- eegdash/SignalStore/signalstore/adapters/read_adapters/recording_acquisitions/neurodata_without_borders/neurodata_without_borders_read_adapter.py +14 -0
- eegdash/SignalStore/signalstore/operations/__init__.py +4 -0
- eegdash/SignalStore/signalstore/operations/handler_executor.py +22 -0
- eegdash/SignalStore/signalstore/operations/handler_factory.py +41 -0
- eegdash/SignalStore/signalstore/operations/handlers/base_handler.py +44 -0
- eegdash/SignalStore/signalstore/operations/handlers/domain/property_model_handlers.py +79 -0
- eegdash/SignalStore/signalstore/operations/handlers/domain/schema_handlers.py +3 -0
- eegdash/SignalStore/signalstore/operations/helpers/abstract_helper.py +17 -0
- eegdash/SignalStore/signalstore/operations/helpers/neuroscikit_extractor.py +33 -0
- eegdash/SignalStore/signalstore/operations/helpers/neuroscikit_rawio.py +165 -0
- eegdash/SignalStore/signalstore/operations/helpers/spikeinterface_helper.py +100 -0
- eegdash/SignalStore/signalstore/operations/helpers/wrappers/neo_wrappers.py +21 -0
- eegdash/SignalStore/signalstore/operations/helpers/wrappers/nwb_wrappers.py +27 -0
- eegdash/SignalStore/signalstore/store/__init__.py +8 -0
- eegdash/SignalStore/signalstore/store/data_access_objects.py +1181 -0
- eegdash/SignalStore/signalstore/store/datafile_adapters.py +131 -0
- eegdash/SignalStore/signalstore/store/repositories.py +928 -0
- eegdash/SignalStore/signalstore/store/store_errors.py +68 -0
- eegdash/SignalStore/signalstore/store/unit_of_work.py +97 -0
- eegdash/SignalStore/signalstore/store/unit_of_work_provider.py +67 -0
- eegdash/SignalStore/signalstore/utilities/data_adapters/spike_interface_adapters/si_recording.py +1 -0
- eegdash/SignalStore/signalstore/utilities/data_adapters/spike_interface_adapters/si_sorter.py +1 -0
- eegdash/SignalStore/signalstore/utilities/testing/data_mocks.py +513 -0
- eegdash/SignalStore/signalstore/utilities/tools/dataarrays.py +49 -0
- eegdash/SignalStore/signalstore/utilities/tools/mongo_records.py +25 -0
- eegdash/SignalStore/signalstore/utilities/tools/operation_response.py +78 -0
- eegdash/SignalStore/signalstore/utilities/tools/purge_orchestration_response.py +21 -0
- eegdash/SignalStore/signalstore/utilities/tools/quantities.py +15 -0
- eegdash/SignalStore/signalstore/utilities/tools/strings.py +38 -0
- eegdash/SignalStore/signalstore/utilities/tools/time.py +17 -0
- eegdash/SignalStore/tests/conftest.py +799 -0
- eegdash/SignalStore/tests/data/valid_data/data_arrays/make_fake_data.py +59 -0
- eegdash/SignalStore/tests/unit/store/conftest.py +0 -0
- eegdash/SignalStore/tests/unit/store/test_data_access_objects.py +1235 -0
- eegdash/SignalStore/tests/unit/store/test_repositories.py +1309 -0
- eegdash/SignalStore/tests/unit/store/test_unit_of_work.py +7 -0
- eegdash/SignalStore/tests/unit/test_ci_cd.py +8 -0
- eegdash/__init__.py +1 -0
- eegdash/aws_ingest.py +29 -0
- eegdash/data_utils.py +213 -0
- eegdash/main.py +17 -0
- eegdash/signalstore_data_utils.py +280 -0
- eegdash-0.0.1.dist-info/LICENSE +20 -0
- eegdash-0.0.1.dist-info/METADATA +72 -0
- eegdash-0.0.1.dist-info/RECORD +72 -0
- eegdash-0.0.1.dist-info/WHEEL +5 -0
- eegdash-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#! /bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Michael Gibson 17 July 2015
|
|
4
|
+
# Modified Adrian Foy January 2023
|
|
5
|
+
|
|
6
|
+
import sys, struct, math, os, time
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from intanutil.read_header import read_header
|
|
10
|
+
from intanutil.get_bytes_per_data_block import get_bytes_per_data_block
|
|
11
|
+
from intanutil.read_one_data_block import read_one_data_block
|
|
12
|
+
from intanutil.notch_filter import notch_filter
|
|
13
|
+
from intanutil.data_to_result import data_to_result
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def read_data(filename):
|
|
17
|
+
"""Reads Intan Technologies RHD2000 data file generated by evaluation board GUI.
|
|
18
|
+
|
|
19
|
+
Data are returned in a dictionary, for future extensibility.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
tic = time.time()
|
|
23
|
+
fid = open(filename, 'rb')
|
|
24
|
+
filesize = os.path.getsize(filename)
|
|
25
|
+
|
|
26
|
+
header = read_header(fid)
|
|
27
|
+
|
|
28
|
+
print('Found {} amplifier channel{}.'.format(header['num_amplifier_channels'], plural(header['num_amplifier_channels'])))
|
|
29
|
+
print('Found {} auxiliary input channel{}.'.format(header['num_aux_input_channels'], plural(header['num_aux_input_channels'])))
|
|
30
|
+
print('Found {} supply voltage channel{}.'.format(header['num_supply_voltage_channels'], plural(header['num_supply_voltage_channels'])))
|
|
31
|
+
print('Found {} board ADC channel{}.'.format(header['num_board_adc_channels'], plural(header['num_board_adc_channels'])))
|
|
32
|
+
print('Found {} board digital input channel{}.'.format(header['num_board_dig_in_channels'], plural(header['num_board_dig_in_channels'])))
|
|
33
|
+
print('Found {} board digital output channel{}.'.format(header['num_board_dig_out_channels'], plural(header['num_board_dig_out_channels'])))
|
|
34
|
+
print('Found {} temperature sensors channel{}.'.format(header['num_temp_sensor_channels'], plural(header['num_temp_sensor_channels'])))
|
|
35
|
+
print('')
|
|
36
|
+
|
|
37
|
+
# Determine how many samples the data file contains.
|
|
38
|
+
bytes_per_block = get_bytes_per_data_block(header)
|
|
39
|
+
|
|
40
|
+
# How many data blocks remain in this file?
|
|
41
|
+
data_present = False
|
|
42
|
+
bytes_remaining = filesize - fid.tell()
|
|
43
|
+
if bytes_remaining > 0:
|
|
44
|
+
data_present = True
|
|
45
|
+
|
|
46
|
+
if bytes_remaining % bytes_per_block != 0:
|
|
47
|
+
raise Exception('Something is wrong with file size : should have a whole number of data blocks')
|
|
48
|
+
|
|
49
|
+
num_data_blocks = int(bytes_remaining / bytes_per_block)
|
|
50
|
+
|
|
51
|
+
num_amplifier_samples = header['num_samples_per_data_block'] * num_data_blocks
|
|
52
|
+
num_aux_input_samples = int((header['num_samples_per_data_block'] / 4) * num_data_blocks)
|
|
53
|
+
num_supply_voltage_samples = 1 * num_data_blocks
|
|
54
|
+
num_board_adc_samples = header['num_samples_per_data_block'] * num_data_blocks
|
|
55
|
+
num_board_dig_in_samples = header['num_samples_per_data_block'] * num_data_blocks
|
|
56
|
+
num_board_dig_out_samples = header['num_samples_per_data_block'] * num_data_blocks
|
|
57
|
+
|
|
58
|
+
record_time = num_amplifier_samples / header['sample_rate']
|
|
59
|
+
|
|
60
|
+
if data_present:
|
|
61
|
+
print('File contains {:0.3f} seconds of data. Amplifiers were sampled at {:0.2f} kS/s.'.format(record_time, header['sample_rate'] / 1000))
|
|
62
|
+
else:
|
|
63
|
+
print('Header file contains no data. Amplifiers were sampled at {:0.2f} kS/s.'.format(header['sample_rate'] / 1000))
|
|
64
|
+
|
|
65
|
+
if data_present:
|
|
66
|
+
# Pre-allocate memory for data.
|
|
67
|
+
print('')
|
|
68
|
+
print('Allocating memory for data...')
|
|
69
|
+
|
|
70
|
+
data = {}
|
|
71
|
+
if (header['version']['major'] == 1 and header['version']['minor'] >= 2) or (header['version']['major'] > 1):
|
|
72
|
+
data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.int_)
|
|
73
|
+
else:
|
|
74
|
+
data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.uint)
|
|
75
|
+
|
|
76
|
+
data['amplifier_data'] = np.zeros([header['num_amplifier_channels'], num_amplifier_samples], dtype=np.uint)
|
|
77
|
+
data['aux_input_data'] = np.zeros([header['num_aux_input_channels'], num_aux_input_samples], dtype=np.uint)
|
|
78
|
+
data['supply_voltage_data'] = np.zeros([header['num_supply_voltage_channels'], num_supply_voltage_samples], dtype=np.uint)
|
|
79
|
+
data['temp_sensor_data'] = np.zeros([header['num_temp_sensor_channels'], num_supply_voltage_samples], dtype=np.uint)
|
|
80
|
+
data['board_adc_data'] = np.zeros([header['num_board_adc_channels'], num_board_adc_samples], dtype=np.uint)
|
|
81
|
+
|
|
82
|
+
# by default, this script interprets digital events (digital inputs and outputs) as booleans
|
|
83
|
+
# if unsigned int values are preferred(0 for False, 1 for True), replace the 'dtype=np.bool_' argument with 'dtype=np.uint' as shown
|
|
84
|
+
# the commented line below illustrates this for digital input data; the same can be done for digital out
|
|
85
|
+
|
|
86
|
+
#data['board_dig_in_data'] = np.zeros([header['num_board_dig_in_channels'], num_board_dig_in_samples], dtype=np.uint)
|
|
87
|
+
data['board_dig_in_data'] = np.zeros([header['num_board_dig_in_channels'], num_board_dig_in_samples], dtype=np.bool_)
|
|
88
|
+
data['board_dig_in_raw'] = np.zeros(num_board_dig_in_samples, dtype=np.uint)
|
|
89
|
+
|
|
90
|
+
data['board_dig_out_data'] = np.zeros([header['num_board_dig_out_channels'], num_board_dig_out_samples], dtype=np.bool_)
|
|
91
|
+
data['board_dig_out_raw'] = np.zeros(num_board_dig_out_samples, dtype=np.uint)
|
|
92
|
+
|
|
93
|
+
# Read sampled data from file.
|
|
94
|
+
print('Reading data from file...')
|
|
95
|
+
|
|
96
|
+
# Initialize indices used in looping
|
|
97
|
+
indices = {}
|
|
98
|
+
indices['amplifier'] = 0
|
|
99
|
+
indices['aux_input'] = 0
|
|
100
|
+
indices['supply_voltage'] = 0
|
|
101
|
+
indices['board_adc'] = 0
|
|
102
|
+
indices['board_dig_in'] = 0
|
|
103
|
+
indices['board_dig_out'] = 0
|
|
104
|
+
|
|
105
|
+
print_increment = 10
|
|
106
|
+
percent_done = print_increment
|
|
107
|
+
for i in range(num_data_blocks):
|
|
108
|
+
read_one_data_block(data, header, indices, fid)
|
|
109
|
+
|
|
110
|
+
# Increment indices
|
|
111
|
+
indices['amplifier'] += header['num_samples_per_data_block']
|
|
112
|
+
indices['aux_input'] += int(header['num_samples_per_data_block'] / 4)
|
|
113
|
+
indices['supply_voltage'] += 1
|
|
114
|
+
indices['board_adc'] += header['num_samples_per_data_block']
|
|
115
|
+
indices['board_dig_in'] += header['num_samples_per_data_block']
|
|
116
|
+
indices['board_dig_out'] += header['num_samples_per_data_block']
|
|
117
|
+
|
|
118
|
+
fraction_done = 100 * (1.0 * i / num_data_blocks)
|
|
119
|
+
if fraction_done >= percent_done:
|
|
120
|
+
print('{}% done...'.format(percent_done))
|
|
121
|
+
percent_done = percent_done + print_increment
|
|
122
|
+
|
|
123
|
+
# Make sure we have read exactly the right amount of data.
|
|
124
|
+
bytes_remaining = filesize - fid.tell()
|
|
125
|
+
if bytes_remaining != 0: raise Exception('Error: End of file not reached.')
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# Close data file.
|
|
130
|
+
fid.close()
|
|
131
|
+
|
|
132
|
+
if (data_present):
|
|
133
|
+
print('Parsing data...')
|
|
134
|
+
|
|
135
|
+
# Extract digital input channels to separate variables.
|
|
136
|
+
for i in range(header['num_board_dig_in_channels']):
|
|
137
|
+
data['board_dig_in_data'][i, :] = np.not_equal(np.bitwise_and(data['board_dig_in_raw'], (1 << header['board_dig_in_channels'][i]['native_order'])), 0)
|
|
138
|
+
|
|
139
|
+
# Extract digital output channels to separate variables.
|
|
140
|
+
for i in range(header['num_board_dig_out_channels']):
|
|
141
|
+
data['board_dig_out_data'][i, :] = np.not_equal(np.bitwise_and(data['board_dig_out_raw'], (1 << header['board_dig_out_channels'][i]['native_order'])), 0)
|
|
142
|
+
|
|
143
|
+
# Scale voltage levels appropriately.
|
|
144
|
+
data['amplifier_data'] = np.multiply(0.195, (data['amplifier_data'].astype(np.int32) - 32768)) # units = microvolts
|
|
145
|
+
data['aux_input_data'] = np.multiply(37.4e-6, data['aux_input_data']) # units = volts
|
|
146
|
+
data['supply_voltage_data'] = np.multiply(74.8e-6, data['supply_voltage_data']) # units = volts
|
|
147
|
+
if header['eval_board_mode'] == 1:
|
|
148
|
+
data['board_adc_data'] = np.multiply(152.59e-6, (data['board_adc_data'].astype(np.int32) - 32768)) # units = volts
|
|
149
|
+
elif header['eval_board_mode'] == 13:
|
|
150
|
+
data['board_adc_data'] = np.multiply(312.5e-6, (data['board_adc_data'].astype(np.int32) - 32768)) # units = volts
|
|
151
|
+
else:
|
|
152
|
+
data['board_adc_data'] = np.multiply(50.354e-6, data['board_adc_data']) # units = volts
|
|
153
|
+
data['temp_sensor_data'] = np.multiply(0.01, data['temp_sensor_data']) # units = deg C
|
|
154
|
+
|
|
155
|
+
# Check for gaps in timestamps.
|
|
156
|
+
num_gaps = np.sum(np.not_equal(data['t_amplifier'][1:]-data['t_amplifier'][:-1], 1))
|
|
157
|
+
if num_gaps == 0:
|
|
158
|
+
print('No missing timestamps in data.')
|
|
159
|
+
else:
|
|
160
|
+
print('Warning: {0} gaps in timestamp data found. Time scale will not be uniform!'.format(num_gaps))
|
|
161
|
+
|
|
162
|
+
# Scale time steps (units = seconds).
|
|
163
|
+
data['t_amplifier'] = data['t_amplifier'] / header['sample_rate']
|
|
164
|
+
data['t_aux_input'] = data['t_amplifier'][range(0, len(data['t_amplifier']), 4)]
|
|
165
|
+
data['t_supply_voltage'] = data['t_amplifier'][range(0, len(data['t_amplifier']), header['num_samples_per_data_block'])]
|
|
166
|
+
data['t_board_adc'] = data['t_amplifier']
|
|
167
|
+
data['t_dig'] = data['t_amplifier']
|
|
168
|
+
data['t_temp_sensor'] = data['t_supply_voltage']
|
|
169
|
+
|
|
170
|
+
# If the software notch filter was selected during the recording, apply the
|
|
171
|
+
# same notch filter to amplifier data here.
|
|
172
|
+
if header['notch_filter_frequency'] > 0 and header['version']['major'] < 3:
|
|
173
|
+
print('Applying notch filter...')
|
|
174
|
+
|
|
175
|
+
print_increment = 10
|
|
176
|
+
percent_done = print_increment
|
|
177
|
+
for i in range(header['num_amplifier_channels']):
|
|
178
|
+
data['amplifier_data'][i,:] = notch_filter(data['amplifier_data'][i,:], header['sample_rate'], header['notch_filter_frequency'], 10)
|
|
179
|
+
|
|
180
|
+
fraction_done = 100 * (i / header['num_amplifier_channels'])
|
|
181
|
+
if fraction_done >= percent_done:
|
|
182
|
+
print('{}% done...'.format(percent_done))
|
|
183
|
+
percent_done += print_increment
|
|
184
|
+
else:
|
|
185
|
+
data = [];
|
|
186
|
+
|
|
187
|
+
# Move variables to result struct.
|
|
188
|
+
result = data_to_result(header, data, data_present)
|
|
189
|
+
|
|
190
|
+
print('Done! Elapsed time: {0:0.1f} seconds'.format(time.time() - tic))
|
|
191
|
+
return result
|
|
192
|
+
|
|
193
|
+
def plural(n):
|
|
194
|
+
"""Utility function to optionally pluralize words based on the value of n.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
if n == 1:
|
|
198
|
+
return ''
|
|
199
|
+
else:
|
|
200
|
+
return 's'
|
|
201
|
+
|
|
202
|
+
if __name__ == '__main__':
|
|
203
|
+
a=read_data(sys.argv[1])
|
|
204
|
+
#print(a)
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#! /bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Michael Gibson 27 April 2015
|
|
4
|
+
# Modified Zeke Arneodo Dec 2017
|
|
5
|
+
# Modified Adrian Foy Sep 2018
|
|
6
|
+
|
|
7
|
+
def data_to_result(header, data, data_present):
|
|
8
|
+
"""Moves the header and data (if present) into a common object."""
|
|
9
|
+
|
|
10
|
+
result = {}
|
|
11
|
+
result['t'] = data['t']
|
|
12
|
+
|
|
13
|
+
stim_parameters = {}
|
|
14
|
+
stim_parameters['stim_step_size'] = header['stim_step_size']
|
|
15
|
+
stim_parameters['charge_recovery_current_limit'] = header['recovery_current_limit']
|
|
16
|
+
stim_parameters['charge_recovery_target_voltage'] = header['recovery_target_voltage']
|
|
17
|
+
stim_parameters['amp_settle_mode'] = header['amp_settle_mode']
|
|
18
|
+
stim_parameters['charge_recovery_mode'] = header['charge_recovery_mode']
|
|
19
|
+
result['stim_parameters'] = stim_parameters
|
|
20
|
+
|
|
21
|
+
result['stim_data'] = data['stim_data']
|
|
22
|
+
result['spike_triggers'] = header['spike_triggers']
|
|
23
|
+
result['notes'] = header['notes']
|
|
24
|
+
result['frequency_parameters'] = header['frequency_parameters']
|
|
25
|
+
|
|
26
|
+
if header['dc_amplifier_data_saved']:
|
|
27
|
+
result['dc_amplifier_data'] = data['dc_amplifier_data']
|
|
28
|
+
|
|
29
|
+
if header['num_amplifier_channels'] > 0:
|
|
30
|
+
if data_present:
|
|
31
|
+
result['compliance_limit_data'] = data['compliance_limit_data']
|
|
32
|
+
result['charge_recovery_data'] = data['charge_recovery_data']
|
|
33
|
+
result['amp_settle_data'] = data['amp_settle_data']
|
|
34
|
+
|
|
35
|
+
if header['num_board_dig_out_channels'] > 0:
|
|
36
|
+
result['board_dig_out_channels'] = header['board_dig_out_channels']
|
|
37
|
+
if data_present:
|
|
38
|
+
result['board_dig_out_data'] = data['board_dig_out_data']
|
|
39
|
+
|
|
40
|
+
if header['num_board_dig_in_channels'] > 0:
|
|
41
|
+
result['board_dig_in_channels'] = header['board_dig_in_channels']
|
|
42
|
+
if data_present:
|
|
43
|
+
result['board_dig_in_data'] = data['board_dig_in_data']
|
|
44
|
+
|
|
45
|
+
if header['num_board_dac_channels'] > 0:
|
|
46
|
+
result['board_dac_channels'] = header['board_dac_channels']
|
|
47
|
+
if data_present:
|
|
48
|
+
result['board_dac_data'] = data['board_dac_data']
|
|
49
|
+
|
|
50
|
+
if header['num_board_adc_channels'] > 0:
|
|
51
|
+
result['board_adc_channels'] = header['board_adc_channels']
|
|
52
|
+
if data_present:
|
|
53
|
+
result['board_adc_data'] = data['board_adc_data']
|
|
54
|
+
|
|
55
|
+
if header['num_amplifier_channels'] > 0:
|
|
56
|
+
result['amplifier_channels'] = header['amplifier_channels']
|
|
57
|
+
if data_present:
|
|
58
|
+
result['amplifier_data'] = data['amplifier_data']
|
|
59
|
+
|
|
60
|
+
return result
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#! /bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Michael Gibson 23 April 2015
|
|
4
|
+
# Modified Zeke Arneodo Dec 2017
|
|
5
|
+
# Modified Adrian Foy Sep 2018
|
|
6
|
+
|
|
7
|
+
def get_bytes_per_data_block(header):
|
|
8
|
+
"""Calculates the number of bytes in each 128-sample datablock."""
|
|
9
|
+
N = 128 # n of amplifier samples
|
|
10
|
+
# Each data block contains N amplifier samples.
|
|
11
|
+
bytes_per_block = N * 4 # timestamp data
|
|
12
|
+
|
|
13
|
+
bytes_per_block += N * 2 * header['num_amplifier_channels']
|
|
14
|
+
|
|
15
|
+
# DC amplifier voltage (absent if flag was off)
|
|
16
|
+
# bytes_per_block += N * 2 * header['dc_amplifier_data_saved']
|
|
17
|
+
if header['dc_amplifier_data_saved'] > 0:
|
|
18
|
+
bytes_per_block += N * 2 * header['num_amplifier_channels']
|
|
19
|
+
|
|
20
|
+
# Stimulation data, one per enabled amplifier channels
|
|
21
|
+
bytes_per_block += N * 2 * header['num_amplifier_channels']
|
|
22
|
+
|
|
23
|
+
# Board analog inputs are sampled at same rate as amplifiers
|
|
24
|
+
bytes_per_block += N * 2 * header['num_board_adc_channels']
|
|
25
|
+
|
|
26
|
+
# Board analog outputs are sampled at same rate as amplifiers
|
|
27
|
+
bytes_per_block += N * 2 * header['num_board_dac_channels']
|
|
28
|
+
|
|
29
|
+
# Board digital inputs are sampled at same rate as amplifiers
|
|
30
|
+
if header['num_board_dig_in_channels'] > 0:
|
|
31
|
+
bytes_per_block += N * 2
|
|
32
|
+
|
|
33
|
+
# Board digital outputs are sampled at same rate as amplifiers
|
|
34
|
+
if header['num_board_dig_out_channels'] > 0:
|
|
35
|
+
bytes_per_block += N * 2
|
|
36
|
+
|
|
37
|
+
return bytes_per_block
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#! /bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Michael Gibson 27 April 2015
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
def notch_filter(input, fSample, fNotch, Bandwidth):
|
|
9
|
+
"""Implements a notch filter (e.g., for 50 or 60 Hz) on vector 'input'.
|
|
10
|
+
|
|
11
|
+
fSample = sample rate of data (input Hz or Samples/sec)
|
|
12
|
+
fNotch = filter notch frequency (input Hz)
|
|
13
|
+
Bandwidth = notch 3-dB bandwidth (input Hz). A bandwidth of 10 Hz is
|
|
14
|
+
recommended for 50 or 60 Hz notch filters; narrower bandwidths lead to
|
|
15
|
+
poor time-domain properties with an extended ringing response to
|
|
16
|
+
transient disturbances.
|
|
17
|
+
|
|
18
|
+
Example: If neural data was sampled at 30 kSamples/sec
|
|
19
|
+
and you wish to implement a 60 Hz notch filter:
|
|
20
|
+
|
|
21
|
+
out = notch_filter(input, 30000, 60, 10);
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
tstep = 1.0/fSample
|
|
25
|
+
Fc = fNotch*tstep
|
|
26
|
+
|
|
27
|
+
L = len(input)
|
|
28
|
+
|
|
29
|
+
# Calculate IIR filter parameters
|
|
30
|
+
d = math.exp(-2.0*math.pi*(Bandwidth/2.0)*tstep)
|
|
31
|
+
b = (1.0 + d*d) * math.cos(2.0*math.pi*Fc)
|
|
32
|
+
a0 = 1.0
|
|
33
|
+
a1 = -b
|
|
34
|
+
a2 = d*d
|
|
35
|
+
a = (1.0 + d*d)/2.0
|
|
36
|
+
b0 = 1.0
|
|
37
|
+
b1 = -2.0 * math.cos(2.0*math.pi*Fc)
|
|
38
|
+
b2 = 1.0
|
|
39
|
+
|
|
40
|
+
out = np.zeros(len(input))
|
|
41
|
+
out[0] = input[0]
|
|
42
|
+
out[1] = input[1]
|
|
43
|
+
# (If filtering a continuous data stream, change out[0:1] to the
|
|
44
|
+
# previous final two values of out.)
|
|
45
|
+
|
|
46
|
+
# Run filter
|
|
47
|
+
for i in range(2,L):
|
|
48
|
+
out[i] = (a*b2*input[i-2] + a*b1*input[i-1] + a*b0*input[i] - a2*out[i-2] - a1*out[i-1])/a0
|
|
49
|
+
|
|
50
|
+
return out
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#! /bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Michael Gibson 23 April 2015
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import sys, struct, os
|
|
7
|
+
|
|
8
|
+
def read_qstring(fid):
|
|
9
|
+
"""Read Qt style QString.
|
|
10
|
+
|
|
11
|
+
The first 32-bit unsigned number indicates the length of the string (in bytes).
|
|
12
|
+
If this number equals 0xFFFFFFFF, the string is null.
|
|
13
|
+
|
|
14
|
+
Strings are stored as unicode.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
length, = struct.unpack('<I', fid.read(4))
|
|
18
|
+
if length == int('ffffffff', 16): return ""
|
|
19
|
+
|
|
20
|
+
if length > (os.fstat(fid.fileno()).st_size - fid.tell() + 1) :
|
|
21
|
+
print(length)
|
|
22
|
+
raise Exception('Length too long.')
|
|
23
|
+
|
|
24
|
+
# convert length from bytes to 16-bit Unicode words
|
|
25
|
+
length = int(length / 2)
|
|
26
|
+
|
|
27
|
+
data = []
|
|
28
|
+
for i in range(0, length):
|
|
29
|
+
c, = struct.unpack('<H', fid.read(2))
|
|
30
|
+
data.append(c)
|
|
31
|
+
|
|
32
|
+
if sys.version_info >= (3,0):
|
|
33
|
+
a = ''.join([chr(c) for c in data])
|
|
34
|
+
else:
|
|
35
|
+
a = ''.join([unichr(c) for c in data])
|
|
36
|
+
|
|
37
|
+
return a
|
|
38
|
+
|
|
39
|
+
if __name__ == '__main__':
|
|
40
|
+
a=read_qstring(open(sys.argv[1], 'rb'))
|
|
41
|
+
print(a)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#! /bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Michael Gibson 23 April 2015
|
|
4
|
+
# Modified Zeke Arneodo Dec 2017
|
|
5
|
+
# Modified Adrian Foy Sep 2018
|
|
6
|
+
|
|
7
|
+
import sys, struct
|
|
8
|
+
from intanutil.qstring import read_qstring
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def read_header(fid):
|
|
12
|
+
"""Reads the Intan File Format header from the given file."""
|
|
13
|
+
|
|
14
|
+
# Check 'magic number' at beginning of file to make sure this is an Intan
|
|
15
|
+
# Technologies RHD2000 data file.
|
|
16
|
+
magic_number, = struct.unpack('<I', fid.read(4))
|
|
17
|
+
|
|
18
|
+
if magic_number != int('0xD69127AC', 16): raise Exception('Unrecognized file type.')
|
|
19
|
+
|
|
20
|
+
header = {}
|
|
21
|
+
# Read version number.
|
|
22
|
+
version = {}
|
|
23
|
+
(version['major'], version['minor']) = struct.unpack('<hh', fid.read(4))
|
|
24
|
+
header['version'] = version
|
|
25
|
+
|
|
26
|
+
print('')
|
|
27
|
+
print('Reading Intan Technologies RHS2000 Data File, Version {}.{}'.format(version['major'], version['minor']))
|
|
28
|
+
print('')
|
|
29
|
+
|
|
30
|
+
# Read information of sampling rate and amplifier frequency settings.
|
|
31
|
+
header['sample_rate'], = struct.unpack('<f', fid.read(4))
|
|
32
|
+
(header['dsp_enabled'],
|
|
33
|
+
header['actual_dsp_cutoff_frequency'],
|
|
34
|
+
header['actual_lower_bandwidth'],
|
|
35
|
+
header['actual_lower_settle_bandwidth'],
|
|
36
|
+
header['actual_upper_bandwidth'],
|
|
37
|
+
header['desired_dsp_cutoff_frequency'],
|
|
38
|
+
header['desired_lower_bandwidth'],
|
|
39
|
+
header['desired_lower_settle_bandwidth'],
|
|
40
|
+
header['desired_upper_bandwidth']) = struct.unpack('<hffffffff', fid.read(34))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# This tells us if a software 50/60 Hz notch filter was enabled during
|
|
44
|
+
# the data acquisition.
|
|
45
|
+
notch_filter_mode, = struct.unpack('<h', fid.read(2))
|
|
46
|
+
header['notch_filter_frequency'] = 0
|
|
47
|
+
if notch_filter_mode == 1:
|
|
48
|
+
header['notch_filter_frequency'] = 50
|
|
49
|
+
elif notch_filter_mode == 2:
|
|
50
|
+
header['notch_filter_frequency'] = 60
|
|
51
|
+
|
|
52
|
+
(header['desired_impedance_test_frequency'], header['actual_impedance_test_frequency']) = struct.unpack('<ff', fid.read(8))
|
|
53
|
+
(header['amp_settle_mode'], header['charge_recovery_mode']) = struct.unpack('<hh', fid.read(4))
|
|
54
|
+
|
|
55
|
+
frequency_parameters = {}
|
|
56
|
+
frequency_parameters['amplifier_sample_rate'] = header['sample_rate']
|
|
57
|
+
frequency_parameters['board_adc_sample_rate'] = header['sample_rate']
|
|
58
|
+
frequency_parameters['board_dig_in_sample_rate'] = header['sample_rate']
|
|
59
|
+
frequency_parameters['desired_dsp_cutoff_frequency'] = header['desired_dsp_cutoff_frequency']
|
|
60
|
+
frequency_parameters['actual_dsp_cutoff_frequency'] = header['actual_dsp_cutoff_frequency']
|
|
61
|
+
frequency_parameters['dsp_enabled'] = header['dsp_enabled']
|
|
62
|
+
frequency_parameters['desired_lower_bandwidth'] = header['desired_lower_bandwidth']
|
|
63
|
+
frequency_parameters['desired_lower_settle_bandwidth'] = header['desired_lower_settle_bandwidth']
|
|
64
|
+
frequency_parameters['actual_lower_bandwidth'] = header['actual_lower_bandwidth']
|
|
65
|
+
frequency_parameters['actual_lower_settle_bandwidth'] = header['actual_lower_settle_bandwidth']
|
|
66
|
+
frequency_parameters['desired_upper_bandwidth'] = header['desired_upper_bandwidth']
|
|
67
|
+
frequency_parameters['actual_upper_bandwidth'] = header['actual_upper_bandwidth']
|
|
68
|
+
frequency_parameters['notch_filter_frequency'] = header['notch_filter_frequency']
|
|
69
|
+
frequency_parameters['desired_impedance_test_frequency'] = header['desired_impedance_test_frequency']
|
|
70
|
+
frequency_parameters['actual_impedance_test_frequency'] = header['actual_impedance_test_frequency']
|
|
71
|
+
|
|
72
|
+
header['frequency_parameters'] = frequency_parameters
|
|
73
|
+
|
|
74
|
+
(header['stim_step_size'],
|
|
75
|
+
header['recovery_current_limit'],
|
|
76
|
+
header['recovery_target_voltage']) = struct.unpack('fff', fid.read(12))
|
|
77
|
+
|
|
78
|
+
note1 = read_qstring(fid)
|
|
79
|
+
note2 = read_qstring(fid)
|
|
80
|
+
note3 = read_qstring(fid)
|
|
81
|
+
header['notes'] = { 'note1' : note1, 'note2' : note2, 'note3' : note3}
|
|
82
|
+
|
|
83
|
+
(header['dc_amplifier_data_saved'],
|
|
84
|
+
header['eval_board_mode']) = struct.unpack('<hh', fid.read(4))
|
|
85
|
+
|
|
86
|
+
header['ref_channel_name'] = read_qstring(fid)
|
|
87
|
+
|
|
88
|
+
# Create structure arrays for each type of data channel.
|
|
89
|
+
header['spike_triggers'] = []
|
|
90
|
+
header['amplifier_channels'] = []
|
|
91
|
+
header['board_adc_channels'] = []
|
|
92
|
+
header['board_dac_channels'] = []
|
|
93
|
+
header['board_dig_in_channels'] = []
|
|
94
|
+
header['board_dig_out_channels'] = []
|
|
95
|
+
|
|
96
|
+
# Read signal summary from data file header.
|
|
97
|
+
number_of_signal_groups, = struct.unpack('<h', fid.read(2))
|
|
98
|
+
print('n signal groups {}'.format(number_of_signal_groups))
|
|
99
|
+
|
|
100
|
+
for signal_group in range(1, number_of_signal_groups + 1):
|
|
101
|
+
signal_group_name = read_qstring(fid)
|
|
102
|
+
signal_group_prefix = read_qstring(fid)
|
|
103
|
+
(signal_group_enabled, signal_group_num_channels, signal_group_num_amp_channels) = struct.unpack('<hhh', fid.read(6))
|
|
104
|
+
|
|
105
|
+
if (signal_group_num_channels > 0) and (signal_group_enabled > 0):
|
|
106
|
+
for signal_channel in range(0, signal_group_num_channels):
|
|
107
|
+
new_channel = {'port_name' : signal_group_name, 'port_prefix' : signal_group_prefix, 'port_number' : signal_group}
|
|
108
|
+
new_channel['native_channel_name'] = read_qstring(fid)
|
|
109
|
+
new_channel['custom_channel_name'] = read_qstring(fid)
|
|
110
|
+
(new_channel['native_order'], new_channel['custom_order'],
|
|
111
|
+
signal_type, channel_enabled, new_channel['chip_channel'],
|
|
112
|
+
command_stream, new_channel['board_stream']) = struct.unpack('<hhhhhhh', fid.read(14)) # ignore command_stream
|
|
113
|
+
new_trigger_channel = {}
|
|
114
|
+
(new_trigger_channel['voltage_trigger_mode'],
|
|
115
|
+
new_trigger_channel['voltage_threshold'],
|
|
116
|
+
new_trigger_channel['digital_trigger_channel'],
|
|
117
|
+
new_trigger_channel['digital_edge_polarity']) = struct.unpack('<hhhh', fid.read(8))
|
|
118
|
+
(new_channel['channel_impedance_magnitude'],
|
|
119
|
+
new_channel['channel_impedance_phase']) = struct.unpack('<ff', fid.read(8))
|
|
120
|
+
|
|
121
|
+
if channel_enabled:
|
|
122
|
+
if signal_type == 0:
|
|
123
|
+
header['amplifier_channels'].append(new_channel)
|
|
124
|
+
header['spike_triggers'].append(new_trigger_channel)
|
|
125
|
+
elif signal_type == 1:
|
|
126
|
+
raise Exception('Wrong signal type for the rhs format')
|
|
127
|
+
#header['aux_input_channels'].append(new_channel)
|
|
128
|
+
elif signal_type == 2:
|
|
129
|
+
raise Exception('Wrong signal type for the rhs format')
|
|
130
|
+
#header['supply_voltage_channels'].append(new_channel)
|
|
131
|
+
elif signal_type == 3:
|
|
132
|
+
header['board_adc_channels'].append(new_channel)
|
|
133
|
+
elif signal_type == 4:
|
|
134
|
+
header['board_dac_channels'].append(new_channel)
|
|
135
|
+
elif signal_type == 5:
|
|
136
|
+
header['board_dig_in_channels'].append(new_channel)
|
|
137
|
+
elif signal_type == 6:
|
|
138
|
+
header['board_dig_out_channels'].append(new_channel)
|
|
139
|
+
else:
|
|
140
|
+
raise Exception('Unknown channel type.')
|
|
141
|
+
|
|
142
|
+
# Summarize contents of data file.
|
|
143
|
+
header['num_amplifier_channels'] = len(header['amplifier_channels'])
|
|
144
|
+
header['num_board_adc_channels'] = len(header['board_adc_channels'])
|
|
145
|
+
header['num_board_dac_channels'] = len(header['board_dac_channels'])
|
|
146
|
+
header['num_board_dig_in_channels'] = len(header['board_dig_in_channels'])
|
|
147
|
+
header['num_board_dig_out_channels'] = len(header['board_dig_out_channels'])
|
|
148
|
+
|
|
149
|
+
return header
|
|
150
|
+
|
|
151
|
+
if __name__ == '__main__':
|
|
152
|
+
h=read_header(open(sys.argv[1], 'rb'))
|
|
153
|
+
print(h)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#! /bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Michael Gibson 23 April 2015
|
|
4
|
+
# Modified Zeke Arneodo Dec 2017
|
|
5
|
+
# Modified Adrian Foy Sep 2018
|
|
6
|
+
|
|
7
|
+
import struct
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def read_one_data_block(data, header, indices, fid):
|
|
12
|
+
"""Reads one 128-sample data block from fid into data, at the location indicated by indices."""
|
|
13
|
+
|
|
14
|
+
data['t'][indices['amplifier']:
|
|
15
|
+
(indices['amplifier']+128)] = np.array(struct.unpack('<' + 'i' * 128, fid.read(128*4)))
|
|
16
|
+
|
|
17
|
+
if header['num_amplifier_channels'] > 0:
|
|
18
|
+
tmp = np.fromfile(fid, dtype='uint16', count=128 * header['num_amplifier_channels'])
|
|
19
|
+
data['amplifier_data'][range(header['num_amplifier_channels']),
|
|
20
|
+
indices['amplifier']:(indices['amplifier']+128)] = tmp.reshape(header['num_amplifier_channels'], 128)
|
|
21
|
+
|
|
22
|
+
# check if dc amplifier voltage was saved
|
|
23
|
+
if header['dc_amplifier_data_saved']:
|
|
24
|
+
tmp = np.fromfile(fid, dtype='uint16', count=128 * header['num_amplifier_channels'])
|
|
25
|
+
data['dc_amplifier_data'][range(header['num_amplifier_channels']),
|
|
26
|
+
indices['amplifier']:(indices['amplifier'] + 128)] = tmp.reshape(header['num_amplifier_channels'], 128)
|
|
27
|
+
|
|
28
|
+
# get the stimulation data
|
|
29
|
+
tmp = np.fromfile(fid, dtype='uint16', count=128 * header['num_amplifier_channels'])
|
|
30
|
+
data['stim_data_raw'][range(header['num_amplifier_channels']),
|
|
31
|
+
indices['amplifier']:(indices['amplifier'] + 128)] = tmp.reshape(header['num_amplifier_channels'], 128)
|
|
32
|
+
|
|
33
|
+
if header['num_board_adc_channels'] > 0:
|
|
34
|
+
tmp = np.fromfile(fid, dtype='uint16', count=128 * header['num_board_adc_channels'])
|
|
35
|
+
data['board_adc_data'][range(header['num_board_adc_channels']),
|
|
36
|
+
indices['board_adc']:(indices['board_adc'] + 128)] = tmp.reshape(header['num_board_adc_channels'], 128)
|
|
37
|
+
|
|
38
|
+
if header['num_board_dac_channels'] > 0:
|
|
39
|
+
tmp = np.fromfile(fid, dtype='uint16', count=128 * header['num_board_dac_channels'])
|
|
40
|
+
data['board_dac_data'][range(header['num_board_dac_channels']),
|
|
41
|
+
indices['board_dac']:(indices['board_dac'] + 128)] = tmp.reshape(header['num_board_dac_channels'], 128)
|
|
42
|
+
|
|
43
|
+
if header['num_board_dig_in_channels'] > 0:
|
|
44
|
+
data['board_dig_in_raw'][indices['board_dig_in']:(indices['board_dig_in'] + 128)] = np.array(struct.unpack('<' + 'H' * 128, fid.read(256)))
|
|
45
|
+
|
|
46
|
+
if header['num_board_dig_out_channels'] > 0:
|
|
47
|
+
data['board_dig_out_raw'][indices['board_dig_out']:(indices['board_dig_out'] + 128)] = np.array(struct.unpack('<' + 'H' * 128, fid.read(256)))
|