def test_rises_falls(self): # test 1D case with a long pulse and a dirac a = np.zeros(500,) a[80:120] = 1 a[200] = 1 # rising fronts self.assertTrue(all(rises(a) == np.array([80, 200]))) # falling fronts self.assertTrue(all(falls(a) == np.array([120, 201]))) # both ind, val = fronts(a) self.assertTrue(all(ind == np.array([80, 120, 200, 201]))) self.assertTrue(all(val == np.array([1, -1, 1, -1]))) # test a 2D case with 2 long pulses and a dirac a = np.zeros((2, 500)) a[0, 80:120] = 1 a[0, 200] = 1 a[1, 280:320] = 1 a[1, 400] = 1 # rising fronts self.assertTrue(np.all(rises(a) == np.array([[0, 0, 1, 1], [80, 200, 280, 400]]))) # falling fronts self.assertTrue(np.all(falls(a) == np.array([[0, 0, 1, 1], [120, 201, 320, 401]]))) # both ind, val = fronts(a) self.assertTrue(all(ind[0] == np.array([0, 0, 0, 0, 1, 1, 1, 1]))) self.assertTrue(all(ind[1] == np.array([80, 120, 200, 201, 280, 320, 400, 401]))) self.assertTrue(all(val == np.array([1, -1, 1, -1, 1, -1, 1, -1])))
def _sync_to_alf(raw_ephys_apfile, output_path=None, save=False, parts=''): """ Extracts sync.times, sync.channels and sync.polarities from binary ephys dataset :param raw_ephys_apfile: bin file containing ephys data or spike :param output_path: output directory :param save: bool write to disk only if True :param parts: string or list of strings that will be appended to the filename before extension :return: """ # handles input argument: support ibllib.io.spikeglx.Reader, str and pathlib.Path if isinstance(raw_ephys_apfile, spikeglx.Reader): sr = raw_ephys_apfile else: raw_ephys_apfile = Path(raw_ephys_apfile) sr = spikeglx.Reader(raw_ephys_apfile) # if no output, need a temp folder to swap for big files if not output_path: output_path = raw_ephys_apfile.parent file_ftcp = Path(output_path).joinpath( f'fronts_times_channel_polarity{str(uuid.uuid4())}.bin') # loop over chunks of the raw ephys file wg = dsp.WindowGenerator(sr.ns, int(SYNC_BATCH_SIZE_SECS * sr.fs), overlap=1) fid_ftcp = open(file_ftcp, 'wb') for sl in wg.slice: ss = sr.read_sync(sl) ind, fronts = dsp.fronts(ss, axis=0) # a = sr.read_sync_analog(sl) sav = np.c_[(ind[0, :] + sl.start) / sr.fs, ind[1, :], fronts.astype(np.double)] sav.tofile(fid_ftcp) # print progress wg.print_progress() # close temp file, read from it and delete fid_ftcp.close() tim_chan_pol = np.fromfile(str(file_ftcp)) tim_chan_pol = tim_chan_pol.reshape((int(tim_chan_pol.size / 3), 3)) file_ftcp.unlink() sync = { 'times': tim_chan_pol[:, 0], 'channels': tim_chan_pol[:, 1], 'polarities': tim_chan_pol[:, 2] } if save: out_files = alf.io.save_object_npy(output_path, sync, '_spikeglx_sync', parts=parts) return Bunch(sync), out_files else: return Bunch(sync)
def _sync_to_alf(raw_ephys_apfile, output_path=None, save=False): """ Extracts sync.times, sync.channels and sync.polarities from binary ephys dataset :param raw_ephys_apfile: bin file containing ephys data or spike :param out_dir: output directory :return: """ if not output_path: file_ftcp = tempfile.TemporaryFile() else: file_ftcp = Path(output_path) / 'fronts_times_channel_polarity.bin' if isinstance(raw_ephys_apfile, ibllib.io.spikeglx.Reader): sr = raw_ephys_apfile else: sr = ibllib.io.spikeglx.Reader(raw_ephys_apfile) # loop over chunks of the raw ephys file wg = dsp.WindowGenerator(sr.ns, SYNC_BATCH_SIZE_SAMPLES, overlap=1) fid_ftcp = open(file_ftcp, 'wb') for sl in wg.slice: ss = sr.read_sync(sl) ind, fronts = dsp.fronts(ss, axis=0) sav = np.c_[(ind[0, :] + sl.start) / sr.fs, ind[1, :], fronts.astype(np.double)] sav.tofile(fid_ftcp) # print progress wg.print_progress() # close temp file, read from it and delete fid_ftcp.close() tim_chan_pol = np.fromfile(str(file_ftcp)) tim_chan_pol = tim_chan_pol.reshape((int(tim_chan_pol.size / 3), 3)) file_ftcp.unlink() sync = { 'times': tim_chan_pol[:, 0], 'channels': tim_chan_pol[:, 1], 'polarities': tim_chan_pol[:, 2] } if save: alf.io.save_object_npy(output_path, sync, '_spikeglx_sync') return sync
def _rotary_encoder_positions_from_gray_code(channela, channelb): """ Extracts the rotary encoder absolute position (cm) as function of time from digital recording of the 2 channels. Rotary Encoder implements X1 encoding: http://www.ni.com/tutorial/7109/en/ rising A & B high = +1 rising A & B low = -1 falling A & B high = -1 falling A & B low = +1 :param channelA: Vector of rotary encoder digital recording channel A :type channelA: numpy array :param channelB: Vector of rotary encoder digital recording channel B :type channelB: numpy array :return: indices vector and position vector """ # detect rising and falling fronts t, fronts = dsp.fronts(channela) # apply X1 logic to get positions in ticks p = (channelb[t] * 2 - 1) * fronts # convert position in cm p = np.cumsum(p) / WHEEL_TICKS * np.pi * WHEEL_RADIUS_CM return t, p