def load_events(file_path, channels=None): """ return time stamps in seconds of each digital channel :param file_path: :param channels: name of channels :return: dictionary, {channel: {'rise':[timestamps of rising events in seconds], 'fall':[timestamps of falling events in seconds}} """ print('\n') if file_path[-7:] != '.events': raise LookupError('The input file: ' + file_path + ' is not a .events file!') with open(file_path) as f: header = oe.readHeader(f) fs = float(header['sampleRate']) events = oe.loadEvents(file_path) detected_channel_number = int(max(events['channel']) + 1) real_channels = ['ch_' + ft.int2str(c, 3) for c in range(detected_channel_number)] if channels is not None: if detected_channel_number != len(channels): warning_msg = '\nThe number of digital channels detected: ' + str(detected_channel_number) + \ ' does not match input channel number: ' + str(len(channels)) warnings.warn(warning_msg) if len(channels) <= detected_channel_number: real_channels[0:len(channels)] = channels else: real_channels = channels[0:detected_channel_number] output = {} for i, ch in enumerate(real_channels): output.update({ch : {'rise' : [], 'fall' : []} }) rise = events['timestamps'][np.logical_and(events['channel'] == i, events['eventId'] == 1)] output[ch]['rise'] = np.array(rise.astype(np.float32) / fs).astype(np.float32) fall = events['timestamps'][np.logical_and(events['channel'] == i, events['eventId'] == 0)] output[ch]['fall'] = np.array(fall.astype(np.float32) / fs).astype(np.float32) print('events loaded.\n') return output
def load_continuous_old(file_path, dtype=np.float32): """ Jun's wrapper to load .continuous data from OpenEphys data files :param file_path: :param dtype: np.float32 or np.int16 :return: header: dictionary, standard open ephys header for continuous file samples: 1D np.array """ assert dtype in (np.float32, np.int16), \ 'Invalid data type specified for loadContinous, valid types are np.float32 and np.int16' print("\nLoading continuous data from " + file_path) bytes_per_block = CONTINUOUS_TIMESTAMP_DTYPE.itemsize + CONTINUOUS_SAMPLE_PER_RECORD_DTYPE.itemsize + \ CONTINUOUS_RECORDING_NUMBER_DTYPE.itemsize + CONTINUOUS_MARKER_BYTES + \ CONTINUOUS_SAMPLE_DTYPE.itemsize * oe.SAMPLES_PER_RECORD # read in the data f = open(file_path, 'rb') file_length = os.fstat(f.fileno()).st_size print('total length of the file: ', file_length, 'bytes.') print('bytes per record block: ', bytes_per_block) block_num = (file_length - oe.NUM_HEADER_BYTES) // bytes_per_block print('total number of valid blocks: ', block_num) header = oe.readHeader(f) samples = np.empty(oe.SAMPLES_PER_RECORD * block_num, dtype) is_break = False for i in range(block_num): if i == 0: # to get the timestamp of the very first record (block) # for alignment of the digital event start_ind = np.fromfile(f, CONTINUOUS_TIMESTAMP_DTYPE, 1) start_time = float(start_ind) / float(header['sampleRate']) else: _ = np.fromfile(f, CONTINUOUS_TIMESTAMP_DTYPE, 1) N = np.fromfile(f, CONTINUOUS_SAMPLE_PER_RECORD_DTYPE, 1)[0] if N != oe.SAMPLES_PER_RECORD: print(('samples per record specified in block ' + str(i) + ' (' + str(N) + ') does not equal to expected value (' + str(oe.SAMPLES_PER_RECORD) + ')!')) samples = samples[0 : i * oe.SAMPLES_PER_RECORD] is_break = True break # raise Exception('samples per record specified in block ' + str(i) + ' (' + str(N) + \ # ') does not equal to expected value (' + str(oe.SAMPLES_PER_RECORD) + ')!') _ = (np.fromfile(f, CONTINUOUS_RECORDING_NUMBER_DTYPE, 1)) if dtype == np.float32: curr_samples = (np.fromfile(f, CONTINUOUS_SAMPLE_DTYPE, N) * float(header['bitVolts'])).astype(np.float32) elif dtype == np.int16: curr_samples = np.fromfile(f, CONTINUOUS_SAMPLE_DTYPE, N).astype(np.int16) else: raise ValueError('Error in reading data of block:' + str(i)) samples[i*oe.SAMPLES_PER_RECORD : (i+1)*oe.SAMPLES_PER_RECORD] = curr_samples record_mark = np.fromfile(f, dtype=np.dtype('<u1'), count=10) if not np.array_equal(record_mark, oe.RECORD_MARKER): print(('record marker specified in block ' + str(i) + ' (' + str(record_mark) + ') does not equal to expected array (' + str(oe.RECORD_MARKER) + ') !')) is_break = True break if is_break: samples = samples[0: oe.SAMPLES_PER_RECORD * (i -1)] header.update({'start_time': start_time}) return header, samples
def load_continuous(file_path, dtype=np.float32, start_ind=0): """ Jun's wrapper to load .continuous data from OpenEphys data files, this will try to load the whole file into memory, using np.fromfile. It can also start from any position in the file (defined by the start_ind) :param file_path: :param dtype: np.float32 or np.int16 :param start_ind: non-negative int, default 0. start index to extract data. :return: header: dictionary, standard open ephys header for continuous file samples: 1D np.array """ assert dtype in (np.float32, np.int16), \ 'Invalid data type specified for loadContinous, valid types are np.float32 and np.int16' print("\nLoading continuous data from " + file_path) bytes_per_block = CONTINUOUS_TIMESTAMP_DTYPE.itemsize + CONTINUOUS_SAMPLE_PER_RECORD_DTYPE.itemsize + \ CONTINUOUS_RECORDING_NUMBER_DTYPE.itemsize + CONTINUOUS_MARKER_BYTES + \ CONTINUOUS_SAMPLE_DTYPE.itemsize * oe.SAMPLES_PER_RECORD input_array = np.fromfile(file_path, dtype='<u1') file_length = input_array.shape[0] print('total length of the file: ', file_length, 'bytes.') valid_block_start = find_next_valid_block(input_array, bytes_per_block=bytes_per_block, start_index=start_ind) print('the beginning index of the first valid block after index: ' + str(start_ind) + ' is ' + \ str(valid_block_start)) print('bytes per record block: ', bytes_per_block) # read in the data f = open(file_path, 'rb') header = oe.readHeader(f) block_num = (file_length - valid_block_start) // bytes_per_block print('number of potential valid blocks after index', start_ind, ':', block_num) samples = np.empty(oe.SAMPLES_PER_RECORD * block_num, dtype=dtype) _ = np.fromfile(f, np.dtype('<u1'), valid_block_start - oe.NUM_HEADER_BYTES) for i in range(block_num): if i == 0: # to get the timestamp of the very first record (block) # for alignment of the digital event start_ind = np.fromfile(f, CONTINUOUS_TIMESTAMP_DTYPE, 1) start_time = float(start_ind) / float(header['sampleRate']) else: _ = np.fromfile(f, CONTINUOUS_TIMESTAMP_DTYPE, 1) N = np.fromfile(f, CONTINUOUS_SAMPLE_PER_RECORD_DTYPE, 1)[0] if N != oe.SAMPLES_PER_RECORD: print(('samples per record specified in block ' + str(i) + ' (' + str(N) + ') does not equal to expected value (' + str(oe.SAMPLES_PER_RECORD) + ')!')) samples = samples[0 : i * oe.SAMPLES_PER_RECORD] print('samples per record specified in block ' + str(i) + ' (' + str(N) + \ ') does not equal to expected value (' + str(oe.SAMPLES_PER_RECORD) + ')!') break _ = (np.fromfile(f, CONTINUOUS_RECORDING_NUMBER_DTYPE, 1)) if dtype == np.float32: curr_samples = (np.fromfile(f, CONTINUOUS_SAMPLE_DTYPE, N) * float(header['bitVolts'])).astype(np.float32) elif dtype == np.int16: curr_samples = np.fromfile(f, CONTINUOUS_SAMPLE_DTYPE, N).astype(np.int16) else: raise ValueError('Error in reading data of block:' + str(i)) samples[i*oe.SAMPLES_PER_RECORD : (i+1)*oe.SAMPLES_PER_RECORD] = curr_samples record_mark = np.fromfile(f, dtype=np.dtype('<u1'), count=10) if not np.array_equal(record_mark, oe.RECORD_MARKER): print(('record marker specified in block ' + str(i) + ' (' + str(record_mark) + ') does not equal to expected array (oe.RECORD_MARKER)!')) break header.update({'start_time': start_time}) print('continuous channel start time (for aligning digital events): ', start_time) return header, samples