def read_data(filename): """ Reads an Intan Technologies RHD2000 data file generated by the evaluation board GUI. Returns data in a dictionary in raw sample units Adapted from Intan sample code... Michael Gibson 17 July 2015 Modified Adrian Foy Sep 2018 """ tic = time.time() fid = open(filename, 'rb') filesize = os.path.getsize(filename) header = read_header(fid) print('{} amplifier channels'.format(header['num_amplifier_channels'])) print('{} auxiliary input channels'.format( header['num_aux_input_channels'])) print('{} supply voltage channels'.format( header['num_supply_voltage_channels'])) print('{} board ADC channels'.format(header['num_board_adc_channels'])) print('{} board digital input channels'.format( header['num_board_dig_in_channels'])) print('{} board digital output channels'.format( header['num_board_dig_out_channels'])) print('{} temperature sensors channels'.format( header['num_temp_sensor_channels'])) # Determine how many samples the data file contains. bytes_per_block = get_bytes_per_data_block(header) # How many data blocks remain in this file? data_present = False bytes_remaining = filesize - fid.tell() if bytes_remaining > 0: data_present = True if bytes_remaining % bytes_per_block != 0: raise Exception( 'File size wrong: should have a whole number of data blocks') num_data_blocks = int(bytes_remaining / bytes_per_block) num_amplifier_samples = header[ 'num_samples_per_data_block'] * num_data_blocks num_aux_input_samples = int( (header['num_samples_per_data_block'] / 4) * num_data_blocks) num_supply_voltage_samples = 1 * num_data_blocks num_board_adc_samples = header[ 'num_samples_per_data_block'] * num_data_blocks num_board_dig_in_samples = header[ 'num_samples_per_data_block'] * num_data_blocks num_board_dig_out_samples = header[ 'num_samples_per_data_block'] * num_data_blocks record_time = num_amplifier_samples / header['sample_rate'] if data_present: print( 'File contains {:0.3f} seconds of data. Amplifiers sampled at {:0.2f} kS/s.' .format(record_time, header['sample_rate'] / 1000)) else: print('Header contains no data. Amplifiers sampled at {:0.2f} kS/s.'. format(header['sample_rate'] / 1000)) if data_present: # Pre-allocate memory for data. print('') print('Allocating memory for data...') data = {} data['num_amplifier_samples'] = num_amplifier_samples if (header['version']['major'] == 1 and header['version']['minor'] >= 2 ) or (header['version']['major'] > 1): data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.int) else: data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.uint) # NOTE: Changed from uint to uint16 data['amplifier_data'] = np.zeros( [header['num_amplifier_channels'], num_amplifier_samples], dtype=np.uint16) data['aux_input_data'] = np.zeros( [header['num_aux_input_channels'], num_aux_input_samples], dtype=np.uint) data['supply_voltage_data'] = np.zeros([ header['num_supply_voltage_channels'], num_supply_voltage_samples ], dtype=np.uint) data['temp_sensor_data'] = np.zeros( [header['num_temp_sensor_channels'], num_supply_voltage_samples], dtype=np.uint) data['board_adc_data'] = np.zeros( [header['num_board_adc_channels'], num_board_adc_samples], dtype=np.uint) # by default, this script interprets digital events (digital inputs and outputs) as booleans # if unsigned int values are preferred(0 for False, 1 for True), # replace the 'dtype=np.bool' argument with 'dtype=np.uint' as shown # the commented line below illustrates this for digital input data; # the same can be done for digital out # data['board_dig_in_data'] = np.zeros([header['num_board_dig_in_channels'], # num_board_dig_in_samples], dtype=np.uint) data['board_dig_in_data'] = np.zeros( [header['num_board_dig_in_channels'], num_board_dig_in_samples], dtype=np.bool) data['board_dig_in_raw'] = np.zeros(num_board_dig_in_samples, dtype=np.uint) data['board_dig_out_data'] = np.zeros( [header['num_board_dig_out_channels'], num_board_dig_out_samples], dtype=np.bool) data['board_dig_out_raw'] = np.zeros(num_board_dig_out_samples, dtype=np.uint) # Read sampled data from file. print('Reading data from file...') # Initialize indices used in looping indices = {} indices['amplifier'] = 0 indices['aux_input'] = 0 indices['supply_voltage'] = 0 indices['board_adc'] = 0 indices['board_dig_in'] = 0 indices['board_dig_out'] = 0 print_increment = 10 percent_done = print_increment for i in range(num_data_blocks): read_one_data_block(data, header, indices, fid) # Increment indices indices['amplifier'] += header['num_samples_per_data_block'] indices['aux_input'] += int(header['num_samples_per_data_block'] / 4) indices['supply_voltage'] += 1 indices['board_adc'] += header['num_samples_per_data_block'] indices['board_dig_in'] += header['num_samples_per_data_block'] indices['board_dig_out'] += header['num_samples_per_data_block'] fraction_done = 100 * (1.0 * i / num_data_blocks) if fraction_done >= percent_done: print('.', end='', flush=True) # print('{}% done...'.format(percent_done)) percent_done = percent_done + print_increment # Make sure we have read exactly the right amount of data. bytes_remaining = filesize - fid.tell() if bytes_remaining != 0: raise Exception('Error: End of file not reached.') # Close data file. fid.close() if (data_present): print('Parsing data...') # Extract digital input channels to separate variables. for i in range(header['num_board_dig_in_channels']): 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) # Extract digital output channels to separate variables. for i in range(header['num_board_dig_out_channels']): 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) # Scale voltage levels appropriately. # NOTE: Commented out to reduce size of file by 4x by storing # 16 bit ints in the resulting .npy # data['amplifier_data'] = np.multiply( # 0.195, (data['amplifier_data'].astype(np.int32) - 32768)) # units = microvolts data['aux_input_data'] = np.multiply( 37.4e-6, data['aux_input_data']) # units = volts data['supply_voltage_data'] = np.multiply( 74.8e-6, data['supply_voltage_data']) # units = volts if header['eval_board_mode'] == 1: data['board_adc_data'] = np.multiply( 152.59e-6, (data['board_adc_data'].astype(np.int32) - 32768)) # units = volts elif header['eval_board_mode'] == 13: data['board_adc_data'] = np.multiply( 312.5e-6, (data['board_adc_data'].astype(np.int32) - 32768)) # units = volts else: data['board_adc_data'] = np.multiply( 50.354e-6, data['board_adc_data']) # units = volts data['temp_sensor_data'] = np.multiply( 0.01, data['temp_sensor_data']) # units = deg C # Check for gaps in timestamps. num_gaps = np.sum( np.not_equal(data['t_amplifier'][1:] - data['t_amplifier'][:-1], 1)) assert num_gaps == 0 # We don't handle missing samples in all our downstream analysis # Scale time steps (units = seconds). data['t_amplifier'] = data['t_amplifier'] / header['sample_rate'] data['t_aux_input'] = data['t_amplifier'][range( 0, len(data['t_amplifier']), 4)] data['t_supply_voltage'] = data['t_amplifier'][range( 0, len(data['t_amplifier']), header['num_samples_per_data_block'])] data['t_board_adc'] = data['t_amplifier'] data['t_dig'] = data['t_amplifier'] data['t_temp_sensor'] = data['t_supply_voltage'] """ NOTE: Instead of applying the notch filter as the original below codes does we just store the original raw data. This preserves the option for downstream analysis to apply or not apply """ # If the software notch filter was selected during the recording, apply the # same notch filter to amplifier data here. # assert header['notch_filter_frequency'] == 0 # if header['notch_filter_frequency'] > 0: # print('Applying notch filter...') # print_increment = 10 # percent_done = print_increment # for i in range(header['num_amplifier_channels']): # data['amplifier_data'][i, :] = notch_filter( # data['amplifier_data'][i, :], header['sample_rate'], # header['notch_filter_frequency'], 10) # fraction_done = 100 * (i / header['num_amplifier_channels']) # if fraction_done >= percent_done: # print('{}% done...'.format(percent_done)) # percent_done += print_increment else: data = [] # Move variables to result struct. result = data_to_result(header, data, data_present) # Add back in num samples for use in the load_experiment function result['num_amplifier_samples'] = data['num_amplifier_samples'] print('num_amplifier_samples', result['num_amplifier_samples']) print('Done! Elapsed time: {0:0.1f} seconds'.format(time.time() - tic)) return result
def IntanRaw_read_data(filename): """Reads Intan Technologies RHD2000 data file generated by evaluation board GUI. Data are returned in a dictionary, for future extensibility. """ tic = time.time() fid = open(filename, 'rb') filesize = os.path.getsize(filename) header = read_header(fid) print('Found {} amplifier channel{}.'.format( header['num_amplifier_channels'], plural(header['num_amplifier_channels']))) print('Found {} auxiliary input channel{}.'.format( header['num_aux_input_channels'], plural(header['num_aux_input_channels']))) print('Found {} supply voltage channel{}.'.format( header['num_supply_voltage_channels'], plural(header['num_supply_voltage_channels']))) print('Found {} board ADC channel{}.'.format( header['num_board_adc_channels'], plural(header['num_board_adc_channels']))) print('Found {} board digital input channel{}.'.format( header['num_board_dig_in_channels'], plural(header['num_board_dig_in_channels']))) print('Found {} board digital output channel{}.'.format( header['num_board_dig_out_channels'], plural(header['num_board_dig_out_channels']))) print('Found {} temperature sensors channel{}.'.format( header['num_temp_sensor_channels'], plural(header['num_temp_sensor_channels']))) print('') # Determine how many samples the data file contains. bytes_per_block = get_bytes_per_data_block(header) # How many data blocks remain in this file? data_present = False bytes_remaining = filesize - fid.tell() if bytes_remaining > 0: data_present = True if bytes_remaining % bytes_per_block != 0: raise Exception( 'Something is wrong with file size : should have a whole number of data blocks' ) num_data_blocks = int(bytes_remaining / bytes_per_block) num_amplifier_samples = 60 * num_data_blocks num_aux_input_samples = 15 * num_data_blocks num_supply_voltage_samples = 1 * num_data_blocks num_board_adc_samples = 60 * num_data_blocks num_board_dig_in_samples = 60 * num_data_blocks num_board_dig_out_samples = 60 * num_data_blocks record_time = num_amplifier_samples / header['sample_rate'] if data_present: print( 'File contains {:0.3f} seconds of data. Amplifiers were sampled at {:0.2f} kS/s.' .format(record_time, header['sample_rate'] / 1000)) else: print( 'Header file contains no data. Amplifiers were sampled at {:0.2f} kS/s.' .format(header['sample_rate'] / 1000)) if data_present: # Pre-allocate memory for data. print('') print('Allocating memory for data...') data = {} if (header['version']['major'] == 1 and header['version']['minor'] >= 2 ) or (header['version']['major'] > 1): data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.int) else: data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.uint) data['amplifier_data'] = np.zeros( [header['num_amplifier_channels'], num_amplifier_samples], dtype=np.uint) data['aux_input_data'] = np.zeros( [header['num_aux_input_channels'], num_aux_input_samples], dtype=np.uint) data['supply_voltage_data'] = np.zeros([ header['num_supply_voltage_channels'], num_supply_voltage_samples ], dtype=np.uint) data['temp_sensor_data'] = np.zeros( [header['num_temp_sensor_channels'], num_supply_voltage_samples], dtype=np.uint) data['board_adc_data'] = np.zeros( [header['num_board_adc_channels'], num_board_adc_samples], dtype=np.uint) data['board_dig_in_data'] = np.zeros( [header['num_board_dig_in_channels'], num_board_dig_in_samples], dtype=np.uint) data['board_dig_in_raw'] = np.zeros(num_board_dig_in_samples, dtype=np.uint) data['board_dig_out_data'] = np.zeros( [header['num_board_dig_out_channels'], num_board_dig_out_samples], dtype=np.uint) data['board_dig_out_raw'] = np.zeros(num_board_dig_out_samples, dtype=np.uint) # Read sampled data from file. print('Reading data from file...') # Initialize indices used in looping indices = {} indices['amplifier'] = 0 indices['aux_input'] = 0 indices['supply_voltage'] = 0 indices['board_adc'] = 0 indices['board_dig_in'] = 0 indices['board_dig_out'] = 0 print_increment = 10 percent_done = print_increment for i in range(num_data_blocks): read_one_data_block(data, header, indices, fid) # Increment indices indices['amplifier'] += 60 indices['aux_input'] += 15 indices['supply_voltage'] += 1 indices['board_adc'] += 60 indices['board_dig_in'] += 60 indices['board_dig_out'] += 60 fraction_done = 100 * (1.0 * i / num_data_blocks) if fraction_done >= percent_done: print('{}% done...'.format(percent_done)) percent_done = percent_done + print_increment # Make sure we have read exactly the right amount of data. bytes_remaining = filesize - fid.tell() if bytes_remaining != 0: raise Exception('Error: End of file not reached.') # Close data file. fid.close() if (data_present): print('Parsing data...') # Extract digital input channels to separate variables. for i in range(header['num_board_dig_in_channels']): 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) # Extract digital output channels to separate variables. for i in range(header['num_board_dig_out_channels']): 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) # Scale voltage levels appropriately. data['amplifier_data'] = np.multiply( 0.195, (data['amplifier_data'].astype(np.int32) - 32768)) # units = microvolts data['aux_input_data'] = np.multiply( 37.4e-6, data['aux_input_data']) # units = volts data['supply_voltage_data'] = np.multiply( 74.8e-6, data['supply_voltage_data']) # units = volts if header['eval_board_mode'] == 1: data['board_adc_data'] = np.multiply( 152.59e-6, (data['board_adc_data'].astype(np.int32) - 32768)) # units = volts else: data['board_adc_data'] = np.multiply( 50.354e-6, data['board_adc_data']) # units = volts data['temp_sensor_data'] = np.multiply( 0.01, data['temp_sensor_data']) # units = deg C # Check for gaps in timestamps. num_gaps = np.sum( np.not_equal(data['t_amplifier'][1:] - data['t_amplifier'][:-1], 1)) if num_gaps == 0: print('No missing timestamps in data.') else: print( 'Warning: {0} gaps in timestamp data found. Time scale will not be uniform!' .format(num_gaps)) # Scale time steps (units = seconds). data['t_amplifier'] = data['t_amplifier'] / header['sample_rate'] data['t_aux_input'] = data['t_amplifier'][range( 0, len(data['t_amplifier']), 4)] data['t_supply_voltage'] = data['t_amplifier'][range( 0, len(data['t_amplifier']), 60)] data['t_board_adc'] = data['t_amplifier'] data['t_dig'] = data['t_amplifier'] data['t_temp_sensor'] = data['t_supply_voltage'] # If the software notch filter was selected during the recording, apply the # same notch filter to amplifier data here. if header['notch_filter_frequency'] > 0: print('Applying notch filter...') print_increment = 10 percent_done = print_increment for i in range(header['num_amplifier_channels']): data['amplifier_data'][i, :] = notch_filter( data['amplifier_data'][i, :], header['sample_rate'], header['notch_filter_frequency'], 10) fraction_done = 100 * (i / header['num_amplifier_channels']) if fraction_done >= percent_done: print('{}% done...'.format(percent_done)) percent_done += print_increment else: data = [] # Move variables to result struct. result = data_to_result(header, data, data_present) print('Done! Elapsed time: {0:0.1f} seconds'.format(time.time() - tic)) return result
def read_data(filename): """Reads Intan Technologies RHD2000 data file generated by evaluation board GUI. Data are returned in a dictionary, for future extensibility. """ tic = time.time() with open(filename, 'rb') as fid: filesize = os.path.getsize(filename) header = read_header(fid) # return header print('Found {} amplifier channel{}.'.format( header['num_amplifier_channels'], plural(header['num_amplifier_channels']))) print('Found {} board ADC channel{}.'.format( header['num_board_adc_channels'], plural(header['num_board_adc_channels']))) print('Found {} board DAC channel{}.'.format( header['num_board_dac_channels'], plural(header['num_board_dac_channels']))) print('Found {} board digital input channel{}.'.format( header['num_board_dig_in_channels'], plural(header['num_board_dig_in_channels']))) print('Found {} board digital output channel{}.'.format( header['num_board_dig_out_channels'], plural(header['num_board_dig_out_channels']))) print('') # Determine how many samples the data file contains. bytes_per_block = get_bytes_per_data_block(header) print('{} bytes per data block'.format(bytes_per_block)) # How many data blocks remain in this file? data_present = False bytes_remaining = filesize - fid.tell() if bytes_remaining > 0: data_present = True if bytes_remaining % bytes_per_block != 0: raise Exception( 'Something is wrong with file size : should have a whole number of data blocks' ) num_data_blocks = int(bytes_remaining / bytes_per_block) num_amplifier_samples = 128 * num_data_blocks num_board_adc_samples = 128 * num_data_blocks num_board_dac_samples = 128 * num_data_blocks num_board_dig_in_samples = 128 * num_data_blocks num_board_dig_out_samples = 128 * num_data_blocks record_time = num_amplifier_samples / header['sample_rate'] if data_present: print( 'File contains {:0.3f} seconds of data. Amplifiers were sampled at {:0.2f} kS/s.' .format(record_time, header['sample_rate'] / 1000)) else: print( 'Header file contains no data. Amplifiers were sampled at {:0.2f} kS/s.' .format(header['sample_rate'] / 1000)) if data_present: # Pre-allocate memory for data. print('') print('Allocating memory for data...') data = {} data['t'] = np.zeros(num_amplifier_samples, dtype=np.int) data['amplifier_data'] = np.zeros( [header['num_amplifier_channels'], num_amplifier_samples], dtype=np.uint) if header['dc_amplifier_data_saved']: data['dc_amplifier_data'] = np.zeros( [header['num_amplifier_channels'], num_amplifier_samples], dtype=np.uint) * header['dc_amplifier_data_saved'] data['stim_data_raw'] = np.zeros( [header['num_amplifier_channels'], num_amplifier_samples], dtype=np.int) data['stim_data'] = np.zeros( [header['num_amplifier_channels'], num_amplifier_samples], dtype=np.int) data['board_adc_data'] = np.zeros( [header['num_board_adc_channels'], num_board_adc_samples], dtype=np.uint) data['board_dac_data'] = np.zeros( [header['num_board_dac_channels'], num_board_dac_samples], dtype=np.uint) # by default, this script interprets digital events (digital inputs, outputs, amp settle, compliance limit, and charge recovery) as booleans # if unsigned int values are preferred (0 for False, 1 for True), replace the 'dtype=np.bool' argument with 'dtype=np.uint' as shown # the commented line below illustrates this for digital input data; the same can be done for the other digital data types #data['board_dig_in_data'] = np.zeros([header['num_board_dig_in_channels'], num_board_dig_in_samples], dtype=np.uint) data['board_dig_in_data'] = np.zeros([ header['num_board_dig_in_channels'], num_board_dig_in_samples ], dtype=np.bool) data['board_dig_in_raw'] = np.zeros(num_board_dig_in_samples, dtype=np.uint) data['board_dig_out_data'] = np.zeros([ header['num_board_dig_out_channels'], num_board_dig_out_samples ], dtype=np.bool) data['board_dig_out_raw'] = np.zeros(num_board_dig_out_samples, dtype=np.uint) # Read sampled data from file. print('Reading data from file...') # Initialize indices used in looping indices = {} indices['amplifier'] = 0 indices['aux_input'] = 0 indices['board_adc'] = 0 indices['board_dac'] = 0 indices['board_dig_in'] = 0 indices['board_dig_out'] = 0 print_increment = 10 percent_done = print_increment for i in (range(num_data_blocks)): read_one_data_block(data, header, indices, fid) # Increment all indices indices in 128 indices = {k: v + 128 for k, v in indices.items()} fraction_done = 100 * (1.0 * i / num_data_blocks) if fraction_done >= percent_done: print('{}% done...'.format(percent_done)) percent_done = percent_done + print_increment # Make sure we have read exactly the right amount of data. bytes_remaining = filesize - fid.tell() if bytes_remaining != 0: raise Exception('Error: End of file not reached.') # end of reading data file. # return data if (data_present): print('Parsing data...') # Extract digital input channels to separate variables. for i in range(header['num_board_dig_in_channels']): 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) # Extract digital output channels to separate variables. for i in range(header['num_board_dig_out_channels']): 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) # Extract stimulation data data['compliance_limit_data'] = np.bitwise_and( data['stim_data_raw'], 32768) >= 1 # get 2^15 bit, interpret as True or False data['charge_recovery_data'] = np.bitwise_and( data['stim_data_raw'], 16384) >= 1 # get 2^14 bit, interpret as True or False data['amp_settle_data'] = np.bitwise_and( data['stim_data_raw'], 8192) >= 1 # get 2^13 bit, interpret as True or False data['stim_polarity'] = 1 - ( 2 * (np.bitwise_and(data['stim_data_raw'], 256) >> 8) ) # get 2^8 bit, interpret as +1 for 0_bit or -1 for 1_bit curr_amp = np.bitwise_and( data['stim_data_raw'], 255 ) # get least-significant 8 bits corresponding to the current amplitude data['stim_data'] = curr_amp * data[ 'stim_polarity'] # multiply current amplitude by the correct sign # # Scale voltage levels appropriately. data['amplifier_data'] = np.multiply( 0.195, (data['amplifier_data'].astype(np.int32) - 32768)) # units = microvolts data['stim_data'] = np.multiply(header['stim_step_size'], data['stim_data'] / 1.0e-6) if header['dc_amplifier_data_saved']: data['dc_amplifier_data'] = np.multiply( -0.01923, (data['dc_amplifier_data'].astype(np.int32) - 512)) # units = volts data['board_adc_data'] = np.multiply( 0.0003125, (data['board_adc_data'].astype(np.int32) - 32768)) # units = volts data['board_dac_data'] = np.multiply( 0.0003125, (data['board_dac_data'].astype(np.int32) - 32768)) # units = volts # Check for gaps in timestamps. num_gaps = np.sum(np.not_equal(data['t'][1:] - data['t'][:-1], 1)) if num_gaps == 0: print('No missing timestamps in data.') else: print( 'Warning: {0} gaps in timestamp data found. Time scale will not be uniform!' .format(num_gaps)) # Scale time steps (units = seconds). data['t'] = data['t'] / header['sample_rate'] # If the software notch filter was selected during the recording, apply the # same notch filter to amplifier data here. if header['notch_filter_frequency'] > 0: print_increment = 10 percent_done = print_increment for i in range(header['num_amplifier_channels']): data['amplifier_data'][i, :] = notch_filter( data['amplifier_data'][i, :], header['sample_rate'], header['notch_filter_frequency'], 10) if fraction_done >= percent_done: print('{}% done...'.format(percent_done)) percent_done = percent_done + print_increment else: data = [] # Move variables to result struct. result = data_to_result(header, data, data_present) print('Done! Elapsed time: {0:0.1f} seconds'.format(time.time() - tic)) return result
def read_data(filename): """Reads Intan Technologies RHD2000 data file generated by evaluation board GUI. Data are returned in a dictionary, for future extensibility. """ tic = time.time() fid = open(filename, 'rb') filesize = os.path.getsize(filename) header = read_header(fid) print('Found {} amplifier channel{}.'.format(header['num_amplifier_channels'], plural(header['num_amplifier_channels']))) print('Found {} auxiliary input channel{}.'.format(header['num_aux_input_channels'], plural(header['num_aux_input_channels']))) print('Found {} supply voltage channel{}.'.format(header['num_supply_voltage_channels'], plural(header['num_supply_voltage_channels']))) print('Found {} board ADC channel{}.'.format(header['num_board_adc_channels'], plural(header['num_board_adc_channels']))) print('Found {} board digital input channel{}.'.format(header['num_board_dig_in_channels'], plural(header['num_board_dig_in_channels']))) print('Found {} board digital output channel{}.'.format(header['num_board_dig_out_channels'], plural(header['num_board_dig_out_channels']))) print('Found {} temperature sensors channel{}.'.format(header['num_temp_sensor_channels'], plural(header['num_temp_sensor_channels']))) print('') # Determine how many samples the data file contains. bytes_per_block = get_bytes_per_data_block(header) # How many data blocks remain in this file? data_present = False bytes_remaining = filesize - fid.tell() if bytes_remaining > 0: data_present = True if bytes_remaining % bytes_per_block != 0: raise Exception('Something is wrong with file size : should have a whole number of data blocks') num_data_blocks = int(bytes_remaining / bytes_per_block) num_amplifier_samples = 60 * num_data_blocks num_aux_input_samples = 15 * num_data_blocks num_supply_voltage_samples = 1 * num_data_blocks num_board_adc_samples = 60 * num_data_blocks num_board_dig_in_samples = 60 * num_data_blocks num_board_dig_out_samples = 60 * num_data_blocks record_time = num_amplifier_samples / header['sample_rate'] if data_present: print('File contains {:0.3f} seconds of data. Amplifiers were sampled at {:0.2f} kS/s.'.format(record_time, header['sample_rate'] / 1000)) else: print('Header file contains no data. Amplifiers were sampled at {:0.2f} kS/s.'.format(header['sample_rate'] / 1000)) if data_present: # Pre-allocate memory for data. print('') print('Allocating memory for data...') data = {} if (header['version']['major'] == 1 and header['version']['minor'] >= 2) or (header['version']['major'] > 1): data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.int) else: data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.uint) data['amplifier_data'] = np.zeros([header['num_amplifier_channels'], num_amplifier_samples], dtype=np.uint) data['aux_input_data'] = np.zeros([header['num_aux_input_channels'], num_aux_input_samples], dtype=np.uint) data['supply_voltage_data'] = np.zeros([header['num_supply_voltage_channels'], num_supply_voltage_samples], dtype=np.uint) data['temp_sensor_data'] = np.zeros([header['num_temp_sensor_channels'], num_supply_voltage_samples], dtype=np.uint) data['board_adc_data'] = np.zeros([header['num_board_adc_channels'], num_board_adc_samples], dtype=np.uint) data['board_dig_in_data'] = np.zeros([header['num_board_dig_in_channels'], num_board_dig_in_samples], dtype=np.uint) data['board_dig_in_raw'] = np.zeros(num_board_dig_in_samples, dtype=np.uint) data['board_dig_out_data'] = np.zeros([header['num_board_dig_out_channels'], num_board_dig_out_samples], dtype=np.uint) data['board_dig_out_raw'] = np.zeros(num_board_dig_out_samples, dtype=np.uint) # Read sampled data from file. print('Reading data from file...') # Initialize indices used in looping indices = {} indices['amplifier'] = 0 indices['aux_input'] = 0 indices['supply_voltage'] = 0 indices['board_adc'] = 0 indices['board_dig_in'] = 0 indices['board_dig_out'] = 0 print_increment = 10 percent_done = print_increment for i in range(num_data_blocks): read_one_data_block(data, header, indices, fid) # Increment indices indices['amplifier'] += 60 indices['aux_input'] += 15 indices['supply_voltage'] += 1 indices['board_adc'] += 60 indices['board_dig_in'] += 60 indices['board_dig_out'] += 60 fraction_done = 100 * (1.0 * i / num_data_blocks) if fraction_done >= percent_done: print('{}% done...'.format(percent_done)) percent_done = percent_done + print_increment # Make sure we have read exactly the right amount of data. bytes_remaining = filesize - fid.tell() if bytes_remaining != 0: raise Exception('Error: End of file not reached.') # Close data file. fid.close() if (data_present): print('Parsing data...') # Extract digital input channels to separate variables. for i in range(header['num_board_dig_in_channels']): 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) # Extract digital output channels to separate variables. for i in range(header['num_board_dig_out_channels']): 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) # Scale voltage levels appropriately. data['amplifier_data'] = np.multiply(0.195, (data['amplifier_data'].astype(np.int32) - 32768)) # units = microvolts data['aux_input_data'] = np.multiply(37.4e-6, data['aux_input_data']) # units = volts data['supply_voltage_data'] = np.multiply(74.8e-6, data['supply_voltage_data']) # units = volts if header['eval_board_mode'] == 1: data['board_adc_data'] = np.multiply(152.59e-6, (data['board_adc_data'].astype(np.int32) - 32768)) # units = volts else: data['board_adc_data'] = np.multiply(50.354e-6, data['board_adc_data']) # units = volts data['temp_sensor_data'] = np.multiply(0.01, data['temp_sensor_data']) # units = deg C # Check for gaps in timestamps. num_gaps = np.sum(np.not_equal(data['t_amplifier'][1:]-data['t_amplifier'][:-1], 1)) if num_gaps == 0: print('No missing timestamps in data.') else: print('Warning: {0} gaps in timestamp data found. Time scale will not be uniform!'.format(num_gaps)) # Scale time steps (units = seconds). data['t_amplifier'] = data['t_amplifier'] / header['sample_rate'] data['t_aux_input'] = data['t_amplifier'][range(0, len(data['t_amplifier']), 4)] data['t_supply_voltage'] = data['t_amplifier'][range(0, len(data['t_amplifier']), 60)] data['t_board_adc'] = data['t_amplifier'] data['t_dig'] = data['t_amplifier'] data['t_temp_sensor'] = data['t_supply_voltage'] # If the software notch filter was selected during the recording, apply the # same notch filter to amplifier data here. if header['notch_filter_frequency'] > 0: print('Applying notch filter...') print_increment = 10 percent_done = print_increment for i in range(header['num_amplifier_channels']): data['amplifier_data'][i,:] = notch_filter(data['amplifier_data'][i,:], header['sample_rate'], header['notch_filter_frequency'], 10) fraction_done = 100 * (i / header['num_amplifier_channels']) if fraction_done >= percent_done: print('{}% done...'.format(percent_done)) percent_done += print_increment else: data = []; # Move variables to result struct. result = data_to_result(header, data, data_present) print('Done! Elapsed time: {0:0.1f} seconds'.format(time.time() - tic)) return result
def read_data(filename, parallelFlg=True): """Reads Intan Technologies RHD2000 data file generated by evaluation board GUI. Data are returned in a dictionary, for future extensibility. """ tic = time.time() fid = open(filename, 'rb') filesize = os.path.getsize(filename) header = read_header(fid) print('Found {} amplifier channel{}.'.format( header['num_amplifier_channels'], plural(header['num_amplifier_channels']))) print('Found {} auxiliary input channel{}.'.format( header['num_aux_input_channels'], plural(header['num_aux_input_channels']))) print('Found {} board ADC channel{}.'.format( header['num_board_adc_channels'], plural(header['num_board_adc_channels']))) print('Found {} board digital input channel{}.'.format( header['num_board_dig_in_channels'], plural(header['num_board_dig_in_channels']))) print('Found {} supply voltage channel{}.'.format( header['num_supply_voltage_channels'], plural(header['num_supply_voltage_channels']))) if header["num_supply_voltage_channels"] > 0: print("Supply voltage data found but will not be imported.") print('Found {} board digital output channel{}.'.format( header['num_board_dig_out_channels'], plural(header['num_board_dig_out_channels']))) if header["num_board_dig_out_channels"] > 0: print("Digital output data found but will not be imported.") print('Found {} temperature sensors channel{}.'.format( header['num_temp_sensor_channels'], plural(header['num_temp_sensor_channels']))) if header["num_temp_sensor_channels"] > 0: print("Temperature sensor data found but will not be imported.") print('') # Determine how many samples the data file contains. bytes_per_block = get_bytes_per_data_block(header) # How many data blocks remain in this file? data_present = False bytes_remaining = filesize - fid.tell() if bytes_remaining > 0: data_present = True if bytes_remaining % bytes_per_block != 0: raise RHDError( 'Something is wrong with file size : should have a whole number of data blocks' ) num_data_blocks = int(bytes_remaining / bytes_per_block) num_amplifier_samples = 60 * num_data_blocks num_aux_input_samples = 15 * num_data_blocks num_board_adc_samples = 60 * num_data_blocks num_board_dig_in_samples = 60 * num_data_blocks # num_supply_voltage_samples = 1 * num_data_blocks # num_board_dig_out_samples = 60 * num_data_blocks record_time = num_amplifier_samples / header['sample_rate'] if data_present: print( 'File contains {:0.3f} seconds of data. Amplifiers were sampled at {:0.2f} kS/s.' .format(record_time, header['sample_rate'] / 1000)) else: print( 'Header file contains no data. Amplifiers were sampled at {:0.2f} kS/s.' .format(header['sample_rate'] / 1000)) if data_present: # Pre-allocate memory for data. print('') print('Allocating memory for data...') data = {} if (header['version']['major'] == 1 and header['version']['minor'] >= 2 ) or (header['version']['major'] > 1): data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.int) else: data['t_amplifier'] = np.zeros(num_amplifier_samples, dtype=np.uint) data['amplifier_data'] = np.zeros( [header['num_amplifier_channels'], num_amplifier_samples], dtype=np.float32) data['aux_input_data'] = np.zeros( [header['num_aux_input_channels'], num_aux_input_samples], dtype=np.float32) # data['supply_voltage_data'] = np.zeros([header['num_supply_voltage_channels'], # num_supply_voltage_samples], # dtype=np.float32) # data['temp_sensor_data'] = np.zeros([header['num_temp_sensor_channels'], # num_supply_voltage_samples], # dtype=np.float32) data['board_adc_data'] = np.zeros( [header['num_board_adc_channels'], num_board_adc_samples], dtype=np.float32) data['board_dig_in_data'] = np.zeros( [header['num_board_dig_in_channels'], num_board_dig_in_samples], dtype=np.uint) data['board_dig_in_raw'] = np.zeros(num_board_dig_in_samples, dtype=np.uint) # data['board_dig_out_data'] = np.zeros([header['num_board_dig_out_channels'], # num_board_dig_out_samples], # dtype=np.uint) # data['board_dig_out_raw'] = np.zeros(num_board_dig_out_samples, dtype=np.uint) # Read sampled data from file. print('Reading data from file...') # Initialize indices used in looping indices = {} indices['amplifier'] = 0 indices['aux_input'] = 0 indices['supply_voltage'] = 0 indices['board_adc'] = 0 indices['board_dig_in'] = 0 indices['board_dig_out'] = 0 print_increment = 10 percent_done = print_increment for i in range(num_data_blocks): read_one_data_block(data, header, indices, fid) # Increment indices indices['amplifier'] += 60 indices['aux_input'] += 15 indices['supply_voltage'] += 1 indices['board_adc'] += 60 indices['board_dig_in'] += 60 indices['board_dig_out'] += 60 fraction_done = 100 * (1.0 * i / num_data_blocks) if fraction_done >= percent_done: print('{}% done...'.format(percent_done)) percent_done = percent_done + print_increment # Make sure we have read exactly the right amount of data. bytes_remaining = filesize - fid.tell() if bytes_remaining != 0: raise RHDError('Error: End of file not reached.') # Close data file. fid.close() if (data_present): print('Parsing data...') # Extract digital input channels to separate variables. for i in range(header['num_board_dig_in_channels']): 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) # # # Extract digital output channels to separate variables. # for i in range(header['num_board_dig_out_channels']): # 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) # Check for gaps in timestamps. num_gaps = np.sum( np.not_equal(data['t_amplifier'][1:] - data['t_amplifier'][:-1], 1)) if num_gaps == 0: print('No missing timestamps in data.') else: print( 'Warning: {0} gaps in timestamp data found. Time scale will not be uniform!' .format(num_gaps)) # Scale time steps (units = seconds). data['t_amplifier'] = data['t_amplifier'] / header[ 'sample_rate'] # This is not done in place because it needs to be recasted as a float data['t_aux_input'] = data['t_amplifier'][range( 0, len(data['t_amplifier']), 4)] # data['t_supply_voltage'] = data['t_amplifier'][range(0, len(data['t_amplifier']), 60)] # We don't need 4 copies of the array #data['t_board_adc'] = data['t_amplifier'] #data['t_dig'] = data['t_amplifier'] #data['t_temp_sensor'] = data['t_supply_voltage'] # If the software notch filter was selected during the recording, apply the # same notch filter to amplifier data here. # This is slow trying multiprocessing. if header['notch_filter_frequency'] > 0: print('Applying notch filter...') if parallelFlg: # Set up the data and the arguments for parallel processing arglist = [] arrlist = [] for i in range(header['num_amplifier_channels']): arrlist.append( mp.Array('f', data['amplifier_data'][i], lock=False)) arglist.append((arrlist[i], i, header['sample_rate'], header['notch_filter_frequency'])) # Spawn the process proclist = [] for i in range(header['num_amplifier_channels']): p = mp.Process(target=notch_filter_mp, args=arglist[i]) p.start() proclist.append(p) # Wait for the process to finish and copy the data for i in range(header['num_amplifier_channels']): proclist[i].join() data['amplifier_data'][i] = arrlist[i] else: print_increment = 10 percent_done = print_increment for i in range(header['num_amplifier_channels']): data['amplifier_data'][i, :] = notch_filter( data['amplifier_data'][i, :], header['sample_rate'], header['notch_filter_frequency'], 10) fraction_done = 100 * (i / header['num_amplifier_channels']) if fraction_done >= percent_done: print('{}% done...'.format(percent_done)) percent_done += print_increment else: data = [] # Move variables to result struct. result = data_to_result(header, data, data_present) print('Done! Elapsed time: {0:0.1f} seconds'.format(time.time() - tic)) return result