def sync_pixel_clock(mwk_path, oe_path, oe_channels=[0, 1]): # 1. read in ephys binary data and timestamps #for open ephys format data, use: #oe_events = open_ephys.loadEvents(oe_path) # unpack the channels #channels = oe_events['channel'] #directions = oe_events['eventId'] # 0: 1 -> 0, 1: 0 -> 1 #times = oe_events['timestamps'] #event_types = oe_events['eventType'] # for KWIK format, use: # 2. send those files to extract relevant info and concatenate: #all_times,all_channels,all_directions = concatenate(raw_files) #[relevant,channels,directions,times],experiment_length = concatenateKWIKfiles.concatenate(oe_path) # make path to the Ch_0 and Ch_1 files: if oe_path[-1] == '/': ttls_path = oe_path + 'Ch_' else: ttls_path = oe_path + '/Ch_' [times,channels,directions] = titanspikes_ttl_extract.get_TTL_info(ttls_path) # times are in seconds.microseconds ephys_fs = 1 experiment_length=[] print 'Experiment length = ', experiment_length # duplicate every element of times and directions rel_times_0 = times[channels==0] rel_times_1 = times[channels==1] rel_directions_0 = directions[channels==0] rel_directions_1 = directions[channels==1] plot_times_0 = np.array(list(itertools.chain(*zip(rel_times_0,rel_times_0[1:])))) plot_times_1 = np.array(list(itertools.chain(*zip(rel_times_1,rel_times_1[1:])))) plot_directions_0 = np.array(list(itertools.chain(*zip(rel_directions_0,rel_directions_0[:-1])))) plot_directions_1 = np.array(list(itertools.chain(*zip(rel_directions_1,rel_directions_1[:-1])))) # plot the up/down transitions as recorded on OE: # fig1 = plt.figure() # ax1 = plt.subplot(2, 1, 1) # plt.plot(plot_times_0,plot_directions_0) # plt.ylabel('OE Ch 0') # ax2 = plt.subplot(2, 1, 2,sharex=ax1) # plt.plot(plot_times_1,plot_directions_1) # plt.ylabel('OE Ch 1') #plt.show() #print 'some oe ttl times: ', times(0) oe_codes, latencies = pixelclock.events_to_codes(np.vstack((times, channels, directions)).T, len(oe_channels), 0.01,swap_12_codes = 1,swap_03_codes=0) # the pixel clock should change once per frame, or at ~16ms max. This is 16ms * 30samples/ms = 480 samples. If a code is shorter than that, it's probably a fluke. # if oe_code times are in 636... format, use 10e4 as min code length #if in seconds.microseconds, min code time = 0.01 print 'Number of ephys codes = ', len(oe_codes) # !! assuming there's just one mworks file, take the first element in the list mwk_path: mwk_path = os.path.abspath(mwk_path[0]) mwk = mw.MWKFile(mwk_path) mwk.open() # Start by getting the pixel clock / bit code data stimulus_announces = mwk.get_events(codes=['#announceStimulus']) # bit_codes is a list of (time, code) tuples mw_codes = [(e.time, e.value['bit_code']) for e in stimulus_announces if isiterable(e.value) and 'bit_code' in e.value] print 'Number of mworks codes = ', len(mw_codes) ## for mw_codes and oe_codes - if one code persists for too long a time (>thresh), get rid of it (keep only the fast-changing codes that come from the grating stimulus): #oe_codes = lowpass_codetimes(oe_codes,fs=ephys_fs,thresh_samples = 0.02) #0.2 #mw_codes = lowpass_codetimes(mw_codes,fs=1e6,thresh_samples = 0.02) print 'Number of oe codes after lowpass = '******'Number of mworks codes after lowpass = '******'win max is ',int(len(oe_codes)/win_size) for win in range(0,int(len(oe_codes)/win_size),25): #range(int(round(len(oe_codes)/win_size))) print 'win = ', win if win*win_size+win_size < len(oe_codes): tmp_match = pixelclock.match_codes( [evt[0] for evt in oe_codes[win*win_size:(win+1)*win_size]], # oe times [evt[1] for evt in oe_codes[win*win_size:(win+1)*win_size]], # oe codes [evt[0] for evt in mw_codes], # mw times [evt[1] for evt in mw_codes], # mw codes minMatch = 20, maxErr = 0) print 'temp matches = ', tmp_match matches.extend(tmp_match) else: print '!!win = ', win tmp_match = pixelclock.match_codes( [evt[0] for evt in oe_codes[win*win_size:-1]], # oe times [evt[1] for evt in oe_codes[win*win_size:-1]], # oe codes [evt[0] for evt in mw_codes], # mw times [evt[1] for evt in mw_codes], # mw codes minMatch = 9, maxErr = 0) matches.extend(tmp_match) print 'matches = ', matches print 'type = ', type(matches) #matches = pixelclock.match_codes( # [evt[0] for evt in oe_codes], # oe times # [evt[1] for evt in oe_codes], # oe codes # [evt[0] for evt in mw_codes], # mw times # [evt[1] for evt in mw_codes], # mw codes # minMatch = 5, # maxErr = 0) # from 0 to 1 == from 23 to 27 matches, but in the wrong places. #print "OE Code sequence:" #print [evt[1] for evt in oe_codes] #print "MW Code sequence:" #print [evt[1] for evt in mw_codes] #print "MATCHES:" #print matches mw_times = [item[0] for item in mw_codes] #[e.time for e in stimulus_announces if isiterable(e.value)] oe_times = [item[0] for item in oe_codes] # condition the data to plot square pulses: tmp_mw_codes = [evt[1] for evt in mw_codes] tmp_mw_codetimes = [evt[0] for evt in mw_codes] plot_mw_codes = np.array(list(itertools.chain(*zip(tmp_mw_codes,tmp_mw_codes[:-1])))) plot_mw_codetimes = np.array(list(itertools.chain(*zip(tmp_mw_codetimes,tmp_mw_codetimes[1:])))) tmp_oe_codes = [evt[1] for evt in oe_codes] tmp_oe_codetimes = [evt[0] for evt in oe_codes] plot_oe_codes = np.array(list(itertools.chain(*zip(tmp_oe_codes,tmp_oe_codes[:-1])))) plot_oe_codetimes = np.array(list(itertools.chain(*zip(tmp_oe_codetimes,tmp_oe_codetimes[1:])))) # Bokeh: save_folder = './pixel_clock/' if not os.path.exists(save_folder): os.makedirs(save_folder) ####### FIGURE 1 ########### colors = [] #col = np.matlib.repmat(rgb,10,1) for i in range(len(matches)): r = np.random.randint(255) g = np.random.randint(255) b = np.random.randint(255) colors.append(RGB(r,g,b)) match_idx = [idx for idx,match in enumerate(matches)] #TOOLS = [HoverTool(),'box_zoom','reset','box_select'] TOOLS="pan,wheel_zoom,box_zoom,reset,hover,previewsave" s1 = figure(width=1000, plot_height=500, title='MWorks and OpenEhys Pixel Clock Codes') # ,tools = TOOLS s1.line(plot_mw_codetimes/1e6,plot_mw_codes) mw_match_circles = [mat[1]/1e6 for mat in matches] mw_match_circles_samples = [mat[1] for mat in matches] s1.circle(mw_match_circles,match_idx,color=colors,size=20) #s1.circle(mw_match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes' #tap = s1.select(dict(type=TapTool)) s2 = figure(width=1000, plot_height=500, title=None) #,tools = TOOLS s2.line(plot_oe_codetimes/ephys_fs,plot_oe_codes) oe_match_circles = [mat[0]/ephys_fs for mat in matches] oe_match_circles_samples = [mat[0] for mat in matches] s2.circle(oe_match_circles,match_idx,color=colors,size=20) # ,tags = match_idx #s2.circle(oe_match_circles,np.ones(len(matches)),color=colors,size=20) # ,tags = match_idx s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes' p = gridplot([[s1], [s2]]) output_file(save_folder + "pc_codes_match.html") # show the results show(p) #plt.savefig('pc_code_matches.pdf') #plt.show() #m,c = fit_line(oe_match_circles_samples,mw_match_circles_samples) m,c = fit_ridge(oe_match_circles_samples,mw_match_circles_samples) # tb object lets you go back and forth between oe and mw timezones tb = timebase.TimeBase(matches,tmp_oe_codetimes,tmp_mw_codetimes) ## to test quality of match, plot OE codes in MW time print 'len plot_oe_codetimes = ', len(plot_oe_codetimes) #### want: take MW time (e.g. stim time) and get oe time: mw2oe_time = [] for mw_time in plot_mw_codetimes: oe_tmp = mw_to_oe_time(mw_time,m,c) ### take MW time and convert to OE time #oe_tmp = tb.mw_to_oe_time(mw_time) mw2oe_time.append(oe_tmp) mw2oe_time = np.squeeze(np.array(mw2oe_time)) oe2mw_time = [] for oe_time in plot_oe_codetimes: mw_tmp = oe_to_mw_time(oe_time,m,c) ### take OE and convert to MW time! #mw_tmp = tb.oe_to_mw_time(oe_time) oe2mw_time.append(mw_tmp) oe2mw_time = np.squeeze(np.array(oe2mw_time)) print 'oe2mw_time.shape ==== ', oe2mw_time.shape print 'plot_oe_codes.shape === ', plot_oe_codes.shape ####### FIGURE 2 ########### ####### PLOT the codes on the same time axis: e.g. everything on MW. tmp_oeMWconv_codetimes = [tb.audio_to_mworks(evt[0]/ephys_fs)* 1e6 for evt in oe_codes] plot_oeMWconv_codetimes = np.array(list(itertools.chain(*zip(tmp_oeMWconv_codetimes,tmp_oeMWconv_codetimes[1:])))) s1 = figure(width=1000, plot_height=500, title='OE Codes Plotted in MW Time') s1.line(plot_mw_codetimes/1e6,plot_mw_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes in MW Time' s2 = figure(width=1000, plot_height=500, title=None,x_range=s1.x_range,y_range=s1.y_range) s2.line(oe2mw_time/1e6,plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes in MW Time' p = gridplot([[s1], [s2]]) output_file(save_folder + "oe_codes_in_MWtime.html") # show the results show(p) ####### FIGURE 3 ########### s1 = figure(width=1000, plot_height=500, title='MW Codes Plotted in OE Time') s1.line(mw2oe_time/ephys_fs,plot_mw_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes in OE Time' s2 = figure(width=1000, plot_height=500, title=None,x_range=s1.x_range,y_range=s1.y_range) s2.line(plot_oe_codetimes/ephys_fs,plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes in OE Time' p = gridplot([[s1], [s2]]) output_file(save_folder + "mw_codes_in_OEtime.html") # show the results show(p) ####### FIGURE 4 ########### ####### PLOT the LINE fit: pp = figure(width=1000, plot_height=500, title='Line Fit for MW and OE Time') #pp.line(oe2mw_time/1e6,plot_oe_codes) pp.line(plot_oe_codetimes,m*plot_oe_codetimes+c,color='red') #pp.circle(plot_oe_codetimes[0:len(matches)],plot_mw_codetimes[0:len(matches)]) pp.circle(oe_match_circles_samples,mw_match_circles_samples) pp.xaxis.axis_label = 'oe codetimes' pp.yaxis.axis_label = 'mw codetimes' output_file(save_folder + "pc_line_fit.html") show(pp) print "number of MW events:" print len(mw_times) print "number of OE events:" print len(oe_times) print "number of matches: " + str(len(matches)) return matches,mwk,m,c,experiment_length
def sync_pixel_clock(vid_path, oe_path, head_data, oe_channels=[0, 1]): # make path to the Ch_0 and Ch_1 files: if oe_path[-1] == '/': ttls_path = oe_path + 'Ch_' else: ttls_path = oe_path + '/Ch_' [times, channels, directions ] = titanspikes_ttl_extract.get_TTL_info(ttls_path, oe_channels[0], oe_channels[1]) # times are in seconds.microseconds ephys_fs = 1 experiment_length = [] print 'Experiment length = ', experiment_length # duplicate every element of times and directions # rel_times_0 = times[channels==0] # rel_times_1 = times[channels==1] # rel_directions_0 = directions[channels==0] # rel_directions_1 = directions[channels==1] # plot_times_0 = np.array(list(itertools.chain(*zip(rel_times_0,rel_times_0[1:])))) # plot_times_1 = np.array(list(itertools.chain(*zip(rel_times_1,rel_times_1[1:])))) # plot_directions_0 = np.array(list(itertools.chain(*zip(rel_directions_0,rel_directions_0[:-1])))) # plot_directions_1 = np.array(list(itertools.chain(*zip(rel_directions_1,rel_directions_1[:-1])))) #oe_codes, latencies = pixelclock.events_to_codes(np.vstack((times, channels, directions)).T, len(oe_channels), 0.01,swap_12_codes =1,swap_03_codes=0) # the pixel clock should change once per frame, or at ~16ms max. This is 16ms * 30samples/ms = 480 samples. If a code is shorter than that, it's probably a fluke. # if oe_code times are in 636... format, use 10e4 as min code length #if in seconds.microseconds, min code time = 0.01 #oe_codes = titanspikes_ttl_extract.read_raw_ttl(os.getcwd() + '/TTLIns') #print 'Number of ephys codes = ', len(oe_codes) ############################ get Video's LED data (pre-extracted): video_data = np.load(vid_path) vid_channels = video_data['channels'] vid_directions = video_data['directions'] vid_times = video_data['times'] #head_data.time = head_data.time / 1e3 ## convert from ms to sec ch1_diffs = np.diff(head_data.bit1) ch2_diffs = np.diff(head_data.bit2) ch1_nonzero_diffs = np.nonzero(ch1_diffs)[ 0] ## [0] b/c where returns a stupid tuple ch2_nonzero_diffs = np.nonzero(ch2_diffs)[0] ch1_directions = ch1_diffs[ch1_nonzero_diffs] ch1_times = head_data.iloc[ch1_nonzero_diffs].converted_times.values ch1_channels = np.zeros(ch1_directions.shape[0]) ch2_directions = ch2_diffs[ch2_nonzero_diffs] ch2_times = head_data.iloc[ch2_nonzero_diffs].converted_times.values ch2_channels = np.ones(ch2_directions.shape[0]) ard_channels = np.hstack([ch1_channels, ch2_channels]) ard_directions = np.hstack([ch1_directions, ch2_directions]) ard_times = np.hstack([ch1_times, ch2_times]) print 'ch1_times = ', ch1_times[0:10] print 'ch2_times = ', ch2_times[0:10] ard_codes, ard_latencies = pixelclock.events_to_codes(np.vstack( (ard_times, ard_channels, ard_directions)).T, 2, 0.01, swap_12_codes=1, swap_03_codes=1) vid_codes, vid_latencies = pixelclock.events_to_codes(np.vstack( (vid_times, vid_channels, vid_directions)).T, 2, 0.01, swap_12_codes=0, swap_03_codes=0) ### hack: instead of changing downstream variable names, just rename ard codes "oe_codes": #oe_codes = None #oe_codes = ard_codes ##### alternative method: # codes = [] # times = [] # for idx in range(head_data.shape[0]): # tmp_sum = head_data.bit1.values[idx] + head_data.bit2.values[idx] # #print tmp_sum # if tmp_sum == 1: # if head_data.bit1.values[idx] == 1: # tmp_code = 2 # elif head_data.bit2.values[idx] == 1: # tmp_code = 1 # elif tmp_sum == 0: # tmp_code = 0 # else: # tmp_code = 3 # codes.append(tmp_code) # times.append(head_data.time[idx]) # # (0,0) = 0. code = 0 # # (0,1) = 1 code = 1 # # (1,0) = 1 code = 2 # # (1,1) = 2 code = 3 # codes = np.array(codes) # times = np.array(times) # uni_codes = codes[np.where(np.diff(codes))[0]] # uni_times = times[np.where(np.diff(codes))[0]] # ard_codes = zip(uni_times,uni_codes) print 'Number of Arduino codes = ', len(ard_codes) print 'Number of Video codes = ', len(vid_codes) #### special skipping first few codes (which are bad,mkay) to get better matches- 8/3/16 for grat17: #vid_codes = vid_codes[3:] #10096 #oe_codes = oe_codes[2:] ard_codes = ard_codes[1:] vid_codes = lowpass_codetimes(vid_codes, 30, 1) print 'some arduino codes = ', ard_codes[0:10] # 3. get pixel clock matches def run_match(codes1, codes2): print '############################### PREPARING TO MATCH CODES #######################################' matches = [] win_size = 400 print 'win max is ', int(len(codes1) / win_size) for win in range(0, int(len(codes1) / win_size), 5): #range(int(round(len(oe_codes)/win_size))) #for idx,win in enumerate(range(int(len(oe_codes)/win_size))): print 'win = ', win if win * win_size + win_size < len(codes1): ## don't go thru all arduino codes, but start at the previous time. (i.e. move forward!) if len(matches) > 1: ard_idx = np.where( [thing[0] == matches[-1][1] for thing in codes2])[0][0] print 'ard idx and last match time = ', ard_idx, matches[ -1][1] else: ard_idx = 0 tmp_match = pixelclock.match_codes( [ evt[0] for evt in codes1[win * win_size:(win + 1) * win_size] ], # oe times [ evt[1] for evt in codes1[win * win_size:(win + 1) * win_size] ], # oe codes [evt[0] for evt in codes2[ard_idx:]], # mw times [evt[1] for evt in codes2[ard_idx:]], # mw codes minMatch=15, maxErr=0) print 'temp matches = ', tmp_match matches.extend(tmp_match) else: print '!!win = ', win tmp_match = pixelclock.match_codes( [evt[0] for evt in codes1[win * win_size:-1]], # oe times [evt[1] for evt in codes1[win * win_size:-1]], # oe codes [evt[0] for evt in codes2], # mw times [evt[1] for evt in codes2], # mw codes minMatch=9, maxErr=0) matches.extend(tmp_match) return matches print 'len(vid_codes), len(ard_codes) = ', len(vid_codes), len(ard_codes) matches = run_match(vid_codes, ard_codes) print 'matches = ', matches print 'type = ', type(matches) #ard_times = [item[0] for item in ard_codes] #[e.time for e in stimulus_announces if isiterable(e.value)] #oe_times = [item[0] for item in oe_codes] # condition the data to plot square pulses: tmp_ard_codes = [evt[1] for evt in ard_codes] tmp_mw_codetimes = [evt[0] for evt in ard_codes] plot_ard_codes = np.array( list(itertools.chain(*zip(tmp_ard_codes, tmp_ard_codes[:-1])))) plot_mw_codetimes = np.array( list(itertools.chain(*zip(tmp_mw_codetimes, tmp_mw_codetimes[1:])))) tmp_oe_codes = [evt[1] for evt in vid_codes] tmp_oe_codetimes = [evt[0] for evt in vid_codes] plot_oe_codes = np.array( list(itertools.chain(*zip(tmp_oe_codes, tmp_oe_codes[:-1])))) plot_oe_codetimes = np.array( list(itertools.chain(*zip(tmp_oe_codetimes, tmp_oe_codetimes[1:])))) # Bokeh: ############################################################### FIGURE 1 ##################################################### colors = [] #col = np.matlib.repmat(rgb,10,1) for i in range(len(matches)): r = np.random.randint(255) g = np.random.randint(255) b = np.random.randint(255) colors.append(RGB(r, g, b)) match_idx = [idx for idx, match in enumerate(matches)] #TOOLS = [HoverTool(),'box_zoom','reset','box_select'] TOOLS = "pan,wheel_zoom,box_zoom,reset,hover,previewsave" s1 = figure( width=1000, plot_height=500, title='MWorks and OpenEhys Pixel Clock Codes') # ,tools = TOOLS s1.line(plot_mw_codetimes, plot_ard_codes) mw_match_circles = [mat[1] for mat in matches] mw_match_circles_samples = [mat[1] for mat in matches] s1.circle(mw_match_circles, match_idx, color=colors, size=20) #s1.circle(mw_match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'Arduino Codes' #tap = s1.select(dict(type=TapTool)) s2 = figure(width=1000, plot_height=500, title=None) #,tools = TOOLS s2.line(plot_oe_codetimes / ephys_fs, plot_oe_codes) oe_match_circles = [mat[0] / ephys_fs for mat in matches] oe_match_circles_samples = [mat[0] for mat in matches] s2.circle(oe_match_circles, match_idx, color=colors, size=20) # ,tags = match_idx #s2.circle(oe_match_circles,np.ones(len(matches)),color=colors,size=20) # ,tags = match_idx s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'Video Codes' p = gridplot([[s1], [s2]]) output_file("pc_codes_match.html") # show the results show(p) #plt.savefig('pc_code_matches.pdf') #plt.show() #m,c = fit_line(tmp_oe_codetimes,tmp_mw_codetimes) #oe,mw m, c = fit_line(oe_match_circles_samples, mw_match_circles_samples) # tb object lets you go back and forth between oe and mw timezones tb = timebase.TimeBase(matches, tmp_oe_codetimes, tmp_mw_codetimes) ## to test quality of match, plot OE codes in MW time print 'len plot_oe_codetimes = ', len(plot_oe_codetimes) #### want: take MW time (e.g. stim time) and get oe time: mw2oe_time = [] for mw_time in plot_mw_codetimes: oe_tmp = mw_to_oe_time(mw_time, m, c) ### take MW time and convert to OE time #oe_tmp = tb.mw_to_oe_time(mw_time) mw2oe_time.append(oe_tmp) mw2oe_time = np.array(mw2oe_time) oe2mw_time = [] for oe_time in plot_oe_codetimes: mw_tmp = oe_to_mw_time(oe_time, m, c) ### take OE and convert to MW time! #mw_tmp = tb.oe_to_mw_time(oe_time) oe2mw_time.append(mw_tmp) oe2mw_time = np.array(oe2mw_time) ####### FIGURE 2 ########### ####### PLOT the codes on the same time axis: e.g. everything on MW. tmp_oeMWconv_codetimes = [ tb.audio_to_mworks(evt[0] / ephys_fs) for evt in vid_codes ] plot_oeMWconv_codetimes = np.array( list( itertools.chain( *zip(tmp_oeMWconv_codetimes, tmp_oeMWconv_codetimes[1:])))) s1 = figure(width=1000, plot_height=500, title='Video Codes Plotted in Arduino Time') s1.line(plot_mw_codetimes, plot_ard_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'Arduino Codes in Arduino Time' s2 = figure(width=1000, plot_height=500, title=None, x_range=s1.x_range, y_range=s1.y_range) s2.line(oe2mw_time, plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'Video Codes in Arduino Time' p = gridplot([[s1], [s2]]) output_file("vid_codes_in_Arduino_time.html") # show the results show(p) ####### FIGURE 3 ########### s1 = figure(width=1000, plot_height=500, title='Arduino Codes Plotted in Video Time') s1.line(mw2oe_time / ephys_fs, plot_ard_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'Arduino Codes in Video Time' s2 = figure(width=1000, plot_height=500, title=None, x_range=s1.x_range, y_range=s1.y_range) s2.line(plot_oe_codetimes / ephys_fs, plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'Video Codes in Video Time' p = gridplot([[s1], [s2]]) output_file("ard_codes_in_Video_time.html") # show the results show(p) ####### FIGURE 4 ########### ####### PLOT the LINE fit: pp = figure(width=1000, plot_height=500, title='Line Fit for MW and OE Time') #pp.line(oe2mw_time/1e6,plot_oe_codes) pp.line(plot_oe_codetimes, m * plot_oe_codetimes + c, color='red') #pp.circle(plot_oe_codetimes[0:len(matches)],plot_mw_codetimes[0:len(matches)]) pp.circle(oe_match_circles_samples, mw_match_circles_samples) pp.xaxis.axis_label = 'video codetimes' pp.yaxis.axis_label = 'arduino codetimes' output_file("pc_line_fit.html") show(pp) #print "number of Arduino events:" #print len(ard_times) #print "number of OE events:" #print len(oe_times) print "number of matches: " + str(len(matches)) return matches, m, c, experiment_length
def sync_pixel_clock(ard_path, oe_path, oe_channels=[0, 1], skip_head=0, skip_ephys=0, win_size=20, minMatch=15, max_codes=-1): # make path to the Ch_0 and Ch_1 files: if oe_path[-1] == '/': ttls_path = oe_path + 'Ch_' else: ttls_path = oe_path + '/Ch_' [times, channels, directions ] = titanspikes_ttl_extract.get_TTL_info(ttls_path, oe_channels[0], oe_channels[1]) # times are in seconds.microseconds ephys_fs = 1 experiment_length = [] print('Experiment length = ', experiment_length) oe_codes, latencies = pixelclock.events_to_codes(np.vstack( (times, channels, directions)).T, len(oe_channels), 0.01, swap_12_codes=1, swap_03_codes=0) # the pixel clock should change once per frame, or at ~16ms max. This is 16ms * 30samples/ms = 480 samples. If a code is shorter than that, it's probably a fluke. # if oe_code times are in 636... format, use 10e4 as min code length #if in seconds.microseconds, min code time = 0.01 #oe_codes = titanspikes_ttl_extract.read_raw_ttl(os.getcwd() + '/636598533041104644/TTLIns',swap_12_codes =1,limit=1e7) print('Number of ephys codes = ', len(oe_codes)) ############################ get Arduino data: names = ['time', 'bit1', 'bit2', 'ox', 'oy', 'oz', 'ax', 'ay', 'az'] head_data = pd.read_csv(ard_path, names=names) print(head_data[0:15]) head_data.time = head_data.time / 1e3 ## convert from ms to sec ch1_diffs = np.diff(head_data.bit1) ch2_diffs = np.diff(head_data.bit2) ch1_nonzero_diffs = np.nonzero(ch1_diffs)[ 0] ## [0] b/c where returns a stupid tuple ch2_nonzero_diffs = np.nonzero(ch2_diffs)[0] ch1_directions = ch1_diffs[ch1_nonzero_diffs] ch1_times = head_data.iloc[ch1_nonzero_diffs].time.values ch1_channels = np.zeros(ch1_directions.shape[0]) ch2_directions = ch2_diffs[ch2_nonzero_diffs] ch2_times = head_data.iloc[ch2_nonzero_diffs].time.values ch2_channels = np.ones(ch2_directions.shape[0]) ard_channels = np.hstack([ch1_channels, ch2_channels]) ard_directions = np.hstack([ch1_directions, ch2_directions]) ard_times = np.hstack([ch1_times, ch2_times]) #print '$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$' #print np.where(np.isnan(ard_channels)), np.where(np.isnan(ard_directions)), np.where(np.isnan(ard_times)) ard_codes, ard_latencies = pixelclock.events_to_codes(np.vstack( (ard_times, ard_channels, ard_directions)).T, 2, 0.01, swap_12_codes=1, swap_03_codes=1) # !! assuming there's just one mworks file, take the first element in the list mwk_path: ##### alternative method: # codes = [] # times = [] # for idx in range(head_data.shape[0]): # tmp_sum = head_data.bit1.values[idx] + head_data.bit2.values[idx] # #print tmp_sum # if tmp_sum == 1: # if head_data.bit1.values[idx] == 1: # tmp_code = 2 # elif head_data.bit2.values[idx] == 1: # tmp_code = 1 # elif tmp_sum == 0: # tmp_code = 0 # else: # tmp_code = 3 # codes.append(tmp_code) # times.append(head_data.time[idx]) # # (0,0) = 0. code = 0 # # (0,1) = 1 code = 1 # # (1,0) = 1 code = 2 # # (1,1) = 2 code = 3 # codes = np.array(codes) # times = np.array(times) # uni_codes = codes[np.where(np.diff(codes))[0]] # uni_times = times[np.where(np.diff(codes))[0]] # ard_codes = zip(uni_times,uni_codes) print('Number of Arduino codes = ', len(ard_codes)) #### special skipping first few codes (which are bad,mkay) to get better matches- 8/3/16 for grat17: ard_codes = ard_codes[skip_head: max_codes] #10096 ,skip_head=2,skip_ephys=0 oe_codes = oe_codes[skip_ephys:max_codes] # 3. get pixel clock matches print( '############################### PREPARING TO MATCH CODES #######################################' ) matches = [] #win_size = 20 ### WIN SIZE is now a kwarg print('win max is ', int(len(oe_codes) / win_size)) for win in range(0, int(len(oe_codes) / win_size), 10): #range(int(round(len(oe_codes)/win_size))) #for idx,win in enumerate(range(int(len(oe_codes)/win_size))): print('win = ', win) if win * win_size + win_size < len(oe_codes): ## don't go thru all arduino codes, but start at the previous time. (i.e. move forward!) if len(matches) > 1: ard_idx = np.where( [thing[0] == matches[-1][1] for thing in ard_codes])[0][0] print('ard idx and last match time = ', ard_idx, matches[-1][1]) else: ard_idx = 0 tmp_match = pixelclock.match_codes( [ evt[0] for evt in oe_codes[win * win_size:(win + 1) * win_size] ], # oe times [ evt[1] for evt in oe_codes[win * win_size:(win + 1) * win_size] ], # oe codes #[evt[0] for evt in oe_codes[win_size*idx: win_size*(idx+1)]], # oe times #[evt[1] for evt in oe_codes[win_size*idx: win_size*(idx+1)]], # oe codes [evt[0] for evt in ard_codes[ard_idx:]], # mw times [evt[1] for evt in ard_codes[ard_idx:]], # mw codes minMatch=minMatch, ###15 maxErr=0) print('temp matches = ', tmp_match) matches.extend(tmp_match) else: #print '!!win = ', win tmp_match = pixelclock.match_codes( [evt[0] for evt in oe_codes[win * win_size:-1]], # oe times [evt[1] for evt in oe_codes[win * win_size:-1]], # oe codes [evt[0] for evt in ard_codes], # mw times [evt[1] for evt in ard_codes], # mw codes minMatch=9, maxErr=0) matches.extend(tmp_match) print('matches = ', matches) #print 'type = ', type(matches) ard_times = [ item[0] for item in ard_codes ] #[e.time for e in stimulus_announces if isiterable(e.value)] oe_times = [item[0] for item in oe_codes] # condition the data to plot square pulses: tmp_ard_codes = [evt[1] for evt in ard_codes] tmp_mw_codetimes = [evt[0] for evt in ard_codes] plot_ard_codes = np.array( list(itertools.chain(*zip(tmp_ard_codes, tmp_ard_codes[:-1])))) plot_mw_codetimes = np.array( list(itertools.chain(*zip(tmp_mw_codetimes, tmp_mw_codetimes[1:])))) tmp_oe_codes = [evt[1] for evt in oe_codes] tmp_oe_codetimes = [evt[0] for evt in oe_codes] plot_oe_codes = np.array( list(itertools.chain(*zip(tmp_oe_codes, tmp_oe_codes[:-1])))) plot_oe_codetimes = np.array( list(itertools.chain(*zip(tmp_oe_codetimes, tmp_oe_codetimes[1:])))) # Bokeh: save_folder = './pixel_clock_arduino/' # os.path.dirname(ard_path) + if not os.path.exists(save_folder): os.makedirs(save_folder) ####### FIGURE 1 ########### colors = [] #col = np.matlib.repmat(rgb,10,1) for i in range(len(matches)): r = np.random.randint(255) g = np.random.randint(255) b = np.random.randint(255) colors.append(RGB(r, g, b)) match_idx = [idx for idx, match in enumerate(matches)] #TOOLS = [HoverTool(),'box_zoom','reset','box_select'] TOOLS = "pan,wheel_zoom,box_zoom,reset,hover,previewsave" s1 = figure( width=1000, plot_height=500, title='MWorks and OpenEhys Pixel Clock Codes') # ,tools = TOOLS s1.line(plot_mw_codetimes, plot_ard_codes) mw_match_circles = [mat[1] for mat in matches] mw_match_circles_samples = [mat[1] for mat in matches] s1.circle(mw_match_circles, match_idx, color=colors, size=20) #s1.circle(mw_match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes' #tap = s1.select(dict(type=TapTool)) s2 = figure(width=1000, plot_height=500, title=None) #,tools = TOOLS s2.line(plot_oe_codetimes / ephys_fs, plot_oe_codes) oe_match_circles = [mat[0] / ephys_fs for mat in matches] oe_match_circles_samples = [mat[0] for mat in matches] s2.circle(oe_match_circles, match_idx, color=colors, size=20) # ,tags = match_idx #s2.circle(oe_match_circles,np.ones(len(matches)),color=colors,size=20) # ,tags = match_idx s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes' p = gridplot([[s1], [s2]]) output_file(save_folder + "pc_codes_match.html") # show the results show(p) #plt.savefig('pc_code_matches.pdf') #plt.show() #m,c = fit_line(tmp_oe_codetimes,tmp_mw_codetimes) #oe,mw m, c = fit_line(oe_match_circles_samples, mw_match_circles_samples) # tb object lets you go back and forth between oe and mw timezones tb = timebase.TimeBase(matches, tmp_oe_codetimes, tmp_mw_codetimes) ## to test quality of match, plot OE codes in MW time print('len plot_oe_codetimes = ', len(plot_oe_codetimes)) #### want: take MW time (e.g. stim time) and get oe time: mw2oe_time = [] for mw_time in plot_mw_codetimes: oe_tmp = mw_to_oe_time(mw_time, m, c) ### take MW time and convert to OE time #oe_tmp = tb.mw_to_oe_time(mw_time) mw2oe_time.append(oe_tmp) mw2oe_time = np.array(mw2oe_time) oe2mw_time = [] for oe_time in plot_oe_codetimes: mw_tmp = oe_to_mw_time(oe_time, m, c) ### take OE and convert to MW time! #mw_tmp = tb.oe_to_mw_time(oe_time) oe2mw_time.append(mw_tmp) oe2mw_time = np.array(oe2mw_time) ####### FIGURE 2 ########### ####### PLOT the codes on the same time axis: e.g. everything on MW. tmp_oeMWconv_codetimes = [ tb.audio_to_mworks(evt[0] / ephys_fs) for evt in oe_codes ] plot_oeMWconv_codetimes = np.array( list( itertools.chain( *zip(tmp_oeMWconv_codetimes, tmp_oeMWconv_codetimes[1:])))) s1 = figure(width=1000, plot_height=500, title='OE Codes Plotted in MW Time') s1.line(plot_mw_codetimes, plot_ard_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes in MW Time' s2 = figure(width=1000, plot_height=500, title=None, x_range=s1.x_range, y_range=s1.y_range) s2.line(oe2mw_time, plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes in MW Time' p = gridplot([[s1], [s2]]) output_file(save_folder + "oe_codes_in_MWtime.html") # show the results show(p) ####### FIGURE 3 ########### s1 = figure(width=1000, plot_height=500, title='MW Codes Plotted in OE Time') s1.line(mw2oe_time / ephys_fs, plot_ard_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes in OE Time' s2 = figure(width=1000, plot_height=500, title=None, x_range=s1.x_range, y_range=s1.y_range) s2.line(plot_oe_codetimes / ephys_fs, plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes in OE Time' p = gridplot([[s1], [s2]]) output_file(save_folder + "ard_codes_in_OEtime.html") # show the results show(p) ####### FIGURE 4 ########### ####### PLOT the LINE fit: pp = figure(width=1000, plot_height=500, title='Line Fit for MW and OE Time') #pp.line(oe2mw_time/1e6,plot_oe_codes) pp.line(plot_oe_codetimes, m * plot_oe_codetimes + c, color='red') #pp.circle(plot_oe_codetimes[0:len(matches)],plot_mw_codetimes[0:len(matches)]) pp.circle(oe_match_circles_samples, mw_match_circles_samples) pp.xaxis.axis_label = 'oe codetimes' pp.yaxis.axis_label = 'ard codetimes' output_file(save_folder + "pc_line_fit.html") show(pp) print("number of Arduino events:") print(len(ard_times)) print("number of OE events:") print(len(oe_times)) print("number of matches: " + str(len(matches))) return matches, m, c, experiment_length, head_data
ax1 = plt.subplot(2, 1, 1) plt.plot(plot_mw_codetimes,plot_mw_codes) plt.scatter([mat[1] for mat in matches],np.ones(len(matches)),s=200, c=range(len(matches))) #plt.plot([mat[1] for mat in matches],numpy.matlib.repmat(1.5,len(matches),1), '-ro') plt.ylabel('MW Codes') ax2 = plt.subplot(2, 1, 2) plt.plot(plot_oe_codetimes,plot_oe_codes) plt.scatter([mat[0] for mat in matches],np.ones(len(matches)),s=200, c=range(len(matches))) #plt.plot([mat[0] for mat in matches],numpy.matlib.repmat(1.5,len(matches),1), '-ro') plt.ylabel('OE Codes') plt.show() tb = timebase.TimeBase(matches) oe_code_conv2mw_time = [] for oe in plot_oe_codetimes: oe_code_conv2mw_time.append(tb.audio_to_mworks(oe)) # plot the 4 code values over time: f,(ax1,ax2) = plt.subplots(2,1,sharex=True) #ax1 = plt.subplot(2, 1, 1) ax1.set_title('Plotting OE codes in MW time') ax1.plot(plot_mw_codetimes,plot_mw_codes) #ax1.scatter([mat[1] for mat in matches],np.ones(len(matches)),s=200, c=range(len(matches))) #plt.plot([mat[1] for mat in matches],numpy.matlib.repmat(1.5,len(matches),1), '-ro') plt.ylabel('MW Codes') #ax2 = plt.subplot(2, 1, 2)
def sync_pixel_clock(mwk_path, oe_path, oe_channels=[0, 1]): # 1. read in ephys binary data and timestamps #for open ephys format data, use: #oe_events = open_ephys.loadEvents(oe_path) # unpack the channels #channels = oe_events['channel'] #directions = oe_events['eventId'] # 0: 1 -> 0, 1: 0 -> 1 #times = oe_events['timestamps'] #event_types = oe_events['eventType'] # for KWIK format, use: # 2. send those files to extract relevant info and concatenate: #all_times,all_channels,all_directions = concatenate(raw_files) [relevant,channels,directions,times],experiment_length = concatenateKWIKfiles.concatenate(oe_path) print 'Experiment length = ', experiment_length # duplicate every element of times and directions rel_times_0 = times[channels==0] rel_times_1 = times[channels==1] rel_directions_0 = directions[channels==0] rel_directions_1 = directions[channels==1] plot_times_0 = np.array(list(itertools.chain(*zip(rel_times_0,rel_times_0[1:])))) plot_times_1 = np.array(list(itertools.chain(*zip(rel_times_1,rel_times_1[1:])))) plot_directions_0 = np.array(list(itertools.chain(*zip(rel_directions_0,rel_directions_0[:-1])))) plot_directions_1 = np.array(list(itertools.chain(*zip(rel_directions_1,rel_directions_1[:-1])))) # plot the up/down transitions as recorded on OE: # fig1 = plt.figure() # ax1 = plt.subplot(2, 1, 1) # plt.plot(plot_times_0,plot_directions_0) # plt.ylabel('OE Ch 0') # ax2 = plt.subplot(2, 1, 2,sharex=ax1) # plt.plot(plot_times_1,plot_directions_1) # plt.ylabel('OE Ch 1') #plt.show() oe_codes, latencies = pixelclock.events_to_codes(np.vstack((times, channels, directions)).T, len(oe_channels), 200) #oe_justthecodes = [evt[1] for evt in oe_codes] #print "oe_justthecodes:" #print oe_justthecodes #ind1 = [i for i,x in enumerate(oe_justthecodes) if x==1]; #ind2 = [i for i,x in enumerate(oe_justthecodes) if x==2]; #for i in ind1: oe_justthecodes[i]=2; #for j in ind2: oe_justthecodes[j]=1; #for x in oe_codes[x][1]: #print "x = " + x #for x in oe_codes: # print oe_codes[x][1] #print "OpenEphys Codes (timestamp, code, which_channel_triggered)" # 2, Read in mworks events #mwk_path = '/Volumes/GG Data Raid/Ephys/grat10/2016-02-02_16-22-34/session_1622/grat10_ephys_160202_1622.mwk' #mwk_path = '/Volumes/GG Data Raid/Ephys/grat10/2016-02-02_16-22-34/session_1811/grat10_ephys_160202_1811.mwk' #mwk_path = '/Users/Guitchounts/Dropbox (coxlab)/Scripts/Repositories/continuous-ephys/sample_data/digintest_160112_3/digintest_160112_3.mwk' # !! assuming there's just one mworks file, take the first element in the list mwk_path: mwk_path = os.path.abspath(mwk_path[0]) mwk = mw.MWKFile(mwk_path) mwk.open() # Start by getting the pixel clock / bit code data stimulus_announces = mwk.get_events(codes=['#announceStimulus']) # bit_codes is a list of (time, code) tuples mw_codes = [(e.time, e.value['bit_code']) for e in stimulus_announces if isiterable(e.value) and 'bit_code' in e.value] ## for mw_codes and oe_codes - if one code persists for too long a time (>thresh), get rid of it (keep only the fast-changing codes that come from the grating stimulus): oe_codes = lowpass_codetimes(oe_codes,fs=30e3,thresh_samples = 0.2) #0.2 mw_codes = lowpass_codetimes(mw_codes,fs=1e6,thresh_samples = 1) #oe_codes = del_duplicate_codes(oe_codes) #mw_codes = del_duplicate_codes(mw_codes) #### special skipping first few codes (which are bad,mkay) to get better matches- 8/3/16 for grat17: #mw_codes = mw_codes[1:] #oe_codes = oe_codes[1:] # 3. get pixel clock matches matches = [] win_size = 40 print 'win max is ',int(len(oe_codes)/win_size) for win in range(0,int(len(oe_codes)/win_size),50): #range(int(round(len(oe_codes)/win_size))) print 'win = ', win if win*win_size+win_size < len(oe_codes): tmp_match = pixelclock.match_codes( [evt[0] for evt in oe_codes[win*win_size:(win+1)*win_size]], # oe times [evt[1] for evt in oe_codes[win*win_size:(win+1)*win_size]], # oe codes [evt[0] for evt in mw_codes], # mw times [evt[1] for evt in mw_codes], # mw codes minMatch = 20, maxErr = 0) print 'temp matches = ', tmp_match matches.extend(tmp_match) else: print '!!win = ', win tmp_match = pixelclock.match_codes( [evt[0] for evt in oe_codes[win*win_size:-1]], # oe times [evt[1] for evt in oe_codes[win*win_size:-1]], # oe codes [evt[0] for evt in mw_codes], # mw times [evt[1] for evt in mw_codes], # mw codes minMatch = 9, maxErr = 0) matches.extend(tmp_match) print 'matches = ', matches print 'type = ', type(matches) #matches = pixelclock.match_codes( # [evt[0] for evt in oe_codes], # oe times # [evt[1] for evt in oe_codes], # oe codes # [evt[0] for evt in mw_codes], # mw times # [evt[1] for evt in mw_codes], # mw codes # minMatch = 5, # maxErr = 0) # from 0 to 1 == from 23 to 27 matches, but in the wrong places. #print "OE Code sequence:" #print [evt[1] for evt in oe_codes] #print "MW Code sequence:" #print [evt[1] for evt in mw_codes] #print "MATCHES:" #print matches mw_times = [item[0] for item in mw_codes] #[e.time for e in stimulus_announces if isiterable(e.value)] oe_times = [item[0] for item in oe_codes] # condition the data to plot square pulses: tmp_mw_codes = [evt[1] for evt in mw_codes] tmp_mw_codetimes = [evt[0] for evt in mw_codes] plot_mw_codes = np.array(list(itertools.chain(*zip(tmp_mw_codes,tmp_mw_codes[:-1])))) plot_mw_codetimes = np.array(list(itertools.chain(*zip(tmp_mw_codetimes,tmp_mw_codetimes[1:])))) tmp_oe_codes = [evt[1] for evt in oe_codes] tmp_oe_codetimes = [evt[0] for evt in oe_codes] plot_oe_codes = np.array(list(itertools.chain(*zip(tmp_oe_codes,tmp_oe_codes[:-1])))) plot_oe_codetimes = np.array(list(itertools.chain(*zip(tmp_oe_codetimes,tmp_oe_codetimes[1:])))) # Bokeh: ####### FIGURE 1 ########### colors = [] #col = np.matlib.repmat(rgb,10,1) for i in range(len(matches)): r = np.random.randint(255) g = np.random.randint(255) b = np.random.randint(255) colors.append(RGB(r,g,b)) match_idx = [idx for idx,match in enumerate(matches)] #TOOLS = [HoverTool(),'box_zoom','reset','box_select'] TOOLS="pan,wheel_zoom,box_zoom,reset,hover,previewsave" s1 = figure(width=1000, plot_height=500, title='MWorks and OpenEhys Pixel Clock Codes') # ,tools = TOOLS s1.line(plot_mw_codetimes/1e6,plot_mw_codes) mw_match_circles = [mat[1]/1e6 for mat in matches] mw_match_circles_samples = [mat[1] for mat in matches] s1.circle(mw_match_circles,match_idx,color=colors,size=20) #s1.circle(mw_match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes' #tap = s1.select(dict(type=TapTool)) s2 = figure(width=1000, plot_height=500, title=None) #,tools = TOOLS s2.line(plot_oe_codetimes/30e3,plot_oe_codes) oe_match_circles = [mat[0]/30e3 for mat in matches] oe_match_circles_samples = [mat[0] for mat in matches] s2.circle(oe_match_circles,match_idx,color=colors,size=20) # ,tags = match_idx #s2.circle(oe_match_circles,np.ones(len(matches)),color=colors,size=20) # ,tags = match_idx s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes' p = gridplot([[s1], [s2]]) output_file("pc_codes_match.html") # show the results show(p) #plt.savefig('pc_code_matches.pdf') #plt.show() #m,c = fit_line(tmp_oe_codetimes,tmp_mw_codetimes) #oe,mw m,c = fit_line(oe_match_circles_samples,mw_match_circles_samples) # tb object lets you go back and forth between oe and mw timezones tb = timebase.TimeBase(matches,tmp_oe_codetimes,tmp_mw_codetimes) ## to test quality of match, plot OE codes in MW time print 'len plot_oe_codetimes = ', len(plot_oe_codetimes) #### want: take MW time (e.g. stim time) and get oe time: mw2oe_time = [] for mw_time in plot_mw_codetimes: oe_tmp = mw_to_oe_time(mw_time,m,c) ### take MW time and convert to OE time #oe_tmp = tb.mw_to_oe_time(mw_time) mw2oe_time.append(oe_tmp) mw2oe_time = np.array(mw2oe_time) oe2mw_time = [] for oe_time in plot_oe_codetimes: mw_tmp = oe_to_mw_time(oe_time,m,c) ### take OE and convert to MW time! #mw_tmp = tb.oe_to_mw_time(oe_time) oe2mw_time.append(mw_tmp) oe2mw_time = np.array(oe2mw_time) ####### FIGURE 2 ########### ####### PLOT the codes on the same time axis: e.g. everything on MW. tmp_oeMWconv_codetimes = [tb.audio_to_mworks(evt[0]/30e3)* 1e6 for evt in oe_codes] plot_oeMWconv_codetimes = np.array(list(itertools.chain(*zip(tmp_oeMWconv_codetimes,tmp_oeMWconv_codetimes[1:])))) s1 = figure(width=1000, plot_height=500, title='OE Codes Plotted in MW Time') s1.line(plot_mw_codetimes/1e6,plot_mw_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes in MW Time' s2 = figure(width=1000, plot_height=500, title=None,x_range=s1.x_range,y_range=s1.y_range) s2.line(oe2mw_time/1e6,plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes in MW Time' p = gridplot([[s1], [s2]]) output_file("oe_codes_in_MWtime.html") # show the results show(p) ####### FIGURE 3 ########### s1 = figure(width=1000, plot_height=500, title='MW Codes Plotted in OE Time') s1.line(mw2oe_time/30e3,plot_mw_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes in OE Time' s2 = figure(width=1000, plot_height=500, title=None,x_range=s1.x_range,y_range=s1.y_range) s2.line(plot_oe_codetimes/30e3,plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes in OE Time' p = gridplot([[s1], [s2]]) output_file("mw_codes_in_OEtime.html") # show the results show(p) ####### FIGURE 4 ########### ####### PLOT the LINE fit: pp = figure(width=1000, plot_height=500, title='Line Fit for MW and OE Time') #pp.line(oe2mw_time/1e6,plot_oe_codes) pp.line(plot_oe_codetimes,m*plot_oe_codetimes+c,color='red') #pp.circle(plot_oe_codetimes[0:len(matches)],plot_mw_codetimes[0:len(matches)]) pp.circle(oe_match_circles_samples,mw_match_circles_samples) pp.xaxis.axis_label = 'oe codetimes' pp.yaxis.axis_label = 'mw codetimes' output_file("pc_line_fit.html") show(pp) print "number of MW events:" print len(mw_times) print "number of OE events:" print len(oe_times) print "number of matches: " + str(len(matches)) return matches,mwk,m,c,experiment_length
def sync_pixel_clock(mwk_path, oe_path, oe_channels=[0, 1]): # 1. read in ephys binary data and timestamps ###[times,channels,directions] = titanspikes_ttl_extract.get_TTL_info('./636152664381217973/TTLChanges/Ch_') # times are in seconds.microseconds ephys_fs = 1 experiment_length=[] print('Experiment length = ', experiment_length) #oe_codes, latencies = pixelclock.events_to_codes(np.vstack((times, channels, directions)).T, len(oe_channels), 0.01) # the pixel clock should change once per frame, or at ~16ms max. This is 16ms * 30samples/ms = 480 samples. If a code is shorter than that, it's probably a fluke. # if oe_code times are in 636... format, use 10e4 as min code length #if in seconds.microseconds, min code time = 0.01 oe_codes = titanspikes_ttl_extract.read_raw_ttl(oe_path) # './636151800793559606/TTLIns' # oe_codes[0,:] = times # oe_codes[1,:] = codes print('Number of ephys codes = ', len(oe_codes)) # !! assuming there's just one mworks file, take the first element in the list mwk_path: mwk_path = os.path.abspath(mwk_path[0]) mwk = mw.MWKFile(mwk_path) mwk.open() # Start by getting the pixel clock / bit code data stimulus_announces = mwk.get_events(codes=['#announceStimulus']) # bit_codes is a list of (time, code) tuples mw_codes = [(e.time, e.value['bit_code']) for e in stimulus_announces if isiterable(e.value) and 'bit_code' in e.value] print('Number of mworks codes = ', len(mw_codes)) ## for mw_codes and oe_codes - if one code persists for too long a time (>thresh), get rid of it (keep only the fast-changing codes that come from the grating stimulus): oe_codes = lowpass_codetimes(oe_codes,fs=1,thresh_samples = 0.01) #0.2 mw_codes = highpass_codetimes(mw_codes,fs=1e6,thresh_samples = 1) print('Number of oe codes after lowpass = '******'Number of mworks codes after lowpass = '******'win max is ',int(len(oe_codes)/win_size)) for win in range(0,int(len(oe_codes)/win_size),50): #range(int(round(len(oe_codes)/win_size))) print('win = ', win) if win*win_size+win_size < len(oe_codes): tmp_match = pixelclock.match_codes( [evt[0] for evt in oe_codes[win*win_size:(win+1)*win_size]], # oe times [evt[1] for evt in oe_codes[win*win_size:(win+1)*win_size]], # oe codes [evt[0] for evt in mw_codes], # mw times [evt[1] for evt in mw_codes], # mw codes minMatch = 20, maxErr = 0) print('temp matches = ', tmp_match) matches.extend(tmp_match) else: #print '!!win = ', win tmp_match = pixelclock.match_codes( [evt[0] for evt in oe_codes[win*win_size:-1]], # oe times [evt[1] for evt in oe_codes[win*win_size:-1]], # oe codes [evt[0] for evt in mw_codes], # mw times [evt[1] for evt in mw_codes], # mw codes minMatch = 9, maxErr = 0) matches.extend(tmp_match) print('matches = ', matches) #print 'type = ', type(matches) mw_times = [item[0] for item in mw_codes] #[e.time for e in stimulus_announces if isiterable(e.value)] oe_times = [item[0] for item in oe_codes] # condition the data to plot square pulses: tmp_mw_codes = [evt[1] for evt in mw_codes] tmp_mw_codetimes = [evt[0] for evt in mw_codes] plot_mw_codes = np.array(list(itertools.chain(*zip(tmp_mw_codes,tmp_mw_codes[:-1])))) plot_mw_codetimes = np.array(list(itertools.chain(*zip(tmp_mw_codetimes,tmp_mw_codetimes[1:])))) tmp_oe_codes = [evt[1] for evt in oe_codes] tmp_oe_codetimes = [evt[0] for evt in oe_codes] plot_oe_codes = np.array(list(itertools.chain(*zip(tmp_oe_codes,tmp_oe_codes[:-1])))) plot_oe_codetimes = np.array(list(itertools.chain(*zip(tmp_oe_codetimes,tmp_oe_codetimes[1:])))) # Bokeh: ## make save directory: save_folder = './pixelclock/' if not os.path.exists(save_folder): os.makedirs(save_folder) ####### FIGURE 1 ########### colors = [] #col = np.matlib.repmat(rgb,10,1) for i in range(len(matches)): r = np.random.randint(255) g = np.random.randint(255) b = np.random.randint(255) colors.append(RGB(r,g,b)) match_idx = [idx for idx,match in enumerate(matches)] #TOOLS = [HoverTool(),'box_zoom','reset','box_select'] TOOLS="pan,wheel_zoom,box_zoom,reset,hover,previewsave" s1 = figure(width=1000, plot_height=500, title='MWorks and OpenEhys Pixel Clock Codes') # ,tools = TOOLS s1.line(plot_mw_codetimes/1e6,plot_mw_codes) mw_match_circles = [mat[1]/1e6 for mat in matches] mw_match_circles_samples = [mat[1] for mat in matches] s1.circle(mw_match_circles,match_idx,color=colors,size=20) #s1.circle(mw_match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes' #tap = s1.select(dict(type=TapTool)) s2 = figure(width=1000, plot_height=500, title=None) #,tools = TOOLS s2.line(plot_oe_codetimes/ephys_fs,plot_oe_codes) oe_match_circles = [mat[0]/ephys_fs for mat in matches] oe_match_circles_samples = [mat[0] for mat in matches] s2.circle(oe_match_circles,match_idx,color=colors,size=20) # ,tags = match_idx #s2.circle(oe_match_circles,np.ones(len(matches)),color=colors,size=20) # ,tags = match_idx s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes' p = gridplot([[s1], [s2]]) output_file(save_folder + "pc_codes_match.html") # show the results show(p) #plt.savefig('pc_code_matches.pdf') #plt.show() #m,c = fit_line(tmp_oe_codetimes,tmp_mw_codetimes) #oe,mw m,c = fit_line(oe_match_circles_samples,mw_match_circles_samples) # tb object lets you go back and forth between oe and mw timezones tb = timebase.TimeBase(matches,tmp_oe_codetimes,tmp_mw_codetimes) ## to test quality of match, plot OE codes in MW time print('len plot_oe_codetimes = ', len(plot_oe_codetimes)) #### want: take MW time (e.g. stim time) and get oe time: mw2oe_time = [] for mw_time in plot_mw_codetimes: oe_tmp = mw_to_oe_time(mw_time,m,c) ### take MW time and convert to OE time #oe_tmp = tb.mw_to_oe_time(mw_time) mw2oe_time.append(oe_tmp) mw2oe_time = np.array(mw2oe_time) oe2mw_time = [] for oe_time in plot_oe_codetimes: mw_tmp = oe_to_mw_time(oe_time,m,c) ### take OE and convert to MW time! #mw_tmp = tb.oe_to_mw_time(oe_time) oe2mw_time.append(mw_tmp) oe2mw_time = np.array(oe2mw_time) ####### FIGURE 2 ########### ####### PLOT the codes on the same time axis: e.g. everything on MW. tmp_oeMWconv_codetimes = [tb.audio_to_mworks(evt[0]/ephys_fs)* 1e6 for evt in oe_codes] plot_oeMWconv_codetimes = np.array(list(itertools.chain(*zip(tmp_oeMWconv_codetimes,tmp_oeMWconv_codetimes[1:])))) s1 = figure(width=1000, plot_height=500, title='OE Codes Plotted in MW Time') s1.line(plot_mw_codetimes/1e6,plot_mw_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes in MW Time' s2 = figure(width=1000, plot_height=500, title=None,x_range=s1.x_range,y_range=s1.y_range) s2.line(oe2mw_time/1e6,plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes in MW Time' p = gridplot([[s1], [s2]]) output_file(save_folder + "oe_codes_in_MWtime.html") # show the results show(p) ####### FIGURE 3 ########### s1 = figure(width=1000, plot_height=500, title='MW Codes Plotted in OE Time') s1.line(mw2oe_time/ephys_fs,plot_mw_codes) #match_circles = [mat[1] for mat in matches] #s1.circle(match_circles,np.ones(len(matches)),color=colors,size=20) s1.yaxis.axis_label = 'MW Codes in OE Time' s2 = figure(width=1000, plot_height=500, title=None,x_range=s1.x_range,y_range=s1.y_range) s2.line(plot_oe_codetimes/ephys_fs,plot_oe_codes) s2.xaxis.axis_label = 'Time (sec)' s2.yaxis.axis_label = 'OE Codes in OE Time' p = gridplot([[s1], [s2]]) output_file(save_folder + "mw_codes_in_OEtime.html") # show the results show(p) ####### FIGURE 4 ########### ####### PLOT the LINE fit: pp = figure(width=1000, plot_height=500, title='Line Fit for MW and OE Time') #pp.line(oe2mw_time/1e6,plot_oe_codes) pp.line(plot_oe_codetimes,m*plot_oe_codetimes+c,color='red') #pp.circle(plot_oe_codetimes[0:len(matches)],plot_mw_codetimes[0:len(matches)]) pp.circle(oe_match_circles_samples,mw_match_circles_samples) pp.xaxis.axis_label = 'oe codetimes' pp.yaxis.axis_label = 'mw codetimes' output_file(save_folder + "pc_line_fit.html") show(pp) print("number of MW events:") print(len(mw_times)) print("number of OE events:") print(len(oe_times)) print("number of matches: " + str(len(matches))) linefit = dict(m=[m],c=[c]) linefit_pd = pd.DataFrame.from_dict(linefit) linefit_pd.to_csv('linefit.csv') return matches,mwk,m,c,experiment_length