Guest User

Untitled

a guest
Sep 8th, 2025
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.28 KB | None | 0 0
  1. from pyControl.utility import *
  2. from devices import *
  3.  
  4.  
  5.  
  6. board = Breakout_1_2()
  7.  
  8. # button = Digital_input(pin=board.port_1.DIO_B, rising_event='button_press',debounce=100,pull="down")
  9. wheel = Digital_output(pin = board.port_1.DIO_A)
  10. port_exp = Port_expander(port = board.port_3)
  11. speaker = Audio_board(board.port_4) # Instantiate audio board.
  12. lickometer = Lickometer(port_exp.port_1,debounce=20)
  13. motor_z = Stepper_motor(port = board.port_2)
  14. motor_y = Stepper_motor(port = board.port_5)
  15. motor_x = Stepper_motor(port = board.port_6)
  16. laser=Digital_output(pin = port_exp.port_2.DIO_B)
  17.  
  18. pump_1 = Digital_output(pin = board.BNC_2)
  19. pump_2 = Digital_output(pin = port_exp.port_3.DIO_B)
  20.  
  21. sync_output = Rsync(pin=board.BNC_1,mean_IPI=1500,event_name="pulse") #needs to be a digital input on the intan system
  22. recording_trigger = Digital_output(pin = port_exp.port_2.DIO_A) #needs to be a digital input on the intan system
  23.  
  24. trial_pulse=Digital_output(port_exp.port_3.DIO_A) #a pulse will be sent at the start of each trial to signal to an external system
  25.  
  26.  
  27. # Custom controls dialog declaration
  28. v.custom_controls_dialog = "E1_2_pumps_validation_gui" # advanced example dialog that is loaded from a .py file
  29. v.api_class = "bias_detection"
  30.  
  31.  
  32. #public variables
  33. v.number_of_trials=-1 #amount of trials to run for this task, if -1 run until manually stopped
  34. v.beep_volume = 50 #speaker volume
  35. v.start_beep_frequency = 2000 #start tone start_frequency
  36. v.water_beep_frequency = 4000 #tone for start of water window
  37. v.delay_to_start_wheel = 300 #delay from start of trial to start of wheel turn
  38. v.wheel_delay_offset = 10 #percentage of offset from original value to randomize values
  39. v.pump_duration=75*ms #pump duration for button press
  40. v.stimulus_time_window = 3000*ms #how long the stimulus stays in place
  41. v.stimulus_motor_speed = 1500
  42.  
  43. #laser variables
  44. v.laser_on=False #laser switch
  45. v.laser_delay_from_start=200 #delay from the start of the trial to start pulsing the laser
  46. v.laser_number_of_pulses=5
  47. v.laser_pulse_duration=5
  48. v.laser_inter_pulse_interval=10
  49. v.laser_pulse_counter___=0
  50.  
  51. v.inter_trial_interval=5000
  52.  
  53. # position A in whisking area
  54. v.stimulus_A_x_value=2000
  55. v.stimulus_A_y_value=4000
  56. v.stimulus_A_z_value=4800
  57.  
  58. #position B in whisking area
  59. v.stimulus_B_x_value=2000
  60. v.stimulus_B_y_value=4000
  61. v.stimulus_B_z_value=4500
  62.  
  63. v.probability_of_pos_A=0.5 #rest is for position B
  64.  
  65. #catch trial position - absolute value
  66. v.catch_trial_x_position = 0
  67. v.catch_trial_y_position = 0
  68. v.catch_trial_z_position = 0
  69.  
  70. #how many of total trials will be catch trials
  71. v.probability_of_catch_trial=0.05
  72.  
  73. #limits to move out of whisking area
  74. v.stimulus_x_outer_bounds=(800,1200)
  75. v.stimulus_y_outer_bounds=(2000,3000)
  76. v.stimulus_z_outer_bounds=(3000,3500)
  77.  
  78. ### bias and accuracy
  79. v.accuracy_history_limit=10 #the amount of trials for which we remember the response for current accuracy calculation
  80. # percentage difference between expected lick outcomes and actual
  81. # above which last trials counted as biased, triggering set of correction trials
  82. v.bias_threshold=0.1
  83. v.bias_correction_size=0.1 # when bias is detected, trigger series of bias_correction_size*accuracy_history_size trials of predetermined stimuli
  84.  
  85. #time to add to inter_trial_interval to punish mouse for wrong lick
  86. v.punishment_period=2000
  87. v.punishment_for_incorrect_timing=False
  88. v.punishment_for_incorrect_stimulus=False
  89. v.add_punishment___=False
  90.  
  91. #private variables
  92. #bias and stimulus
  93.  
  94. v.bias_correction_counter___=0
  95.  
  96.  
  97. # 0-A 1-B 2-catch trial
  98. v.correction_stimulus___ = 0 # mark which stimulus to trigger for bias correction
  99. # v.current_stimulus___ = 0
  100.  
  101. v.finished_startup___ = False
  102. v.pump_1_bool___=False #pump on/off
  103. v.pump_2_bool___=False #pump on/off
  104.  
  105. v.trial_counter___=0
  106.  
  107. v.licked_this_window___=False #flag if mouse has licked in allowed window of time for current trial
  108.  
  109. #current stimulus position
  110. v.motor_z_pos___=0
  111. v.motor_y_pos___=0
  112. v.motor_x_pos___=0
  113.  
  114. v.motors_stationary___=True
  115. v.motors_ready___=True
  116.  
  117. states = ['start_trial','main_loop','update_motors']
  118. initial_state = 'start_trial'
  119. events = ['speaker_off','start_walking','pump_1_on','pulse','move_in','move_out'
  120. ,'lick_1','lick_1_off','lick_2','lick_2_off','exit_position','laser_on','laser_off',
  121. 'start_trial_event','end_trial','end_experiment' ,'stop_trial_pulse',
  122. 'pump_2_on','pump_off']
  123.  
  124.  
  125. def return_home():
  126. if v.motor_z_pos___>0:
  127. motor_z.forward(v.stimulus_motor_speed,v.motor_z_pos___)
  128. v.motor_z_pos___=0
  129. if v.motor_y_pos___>0:
  130. motor_y.forward(v.stimulus_motor_speed,v.motor_y_pos___)
  131. v.motor_y_pos___=0
  132. if v.motor_x_pos___>0:
  133. motor_x.forward(v.stimulus_motor_speed,v.motor_x_pos___)
  134. v.motor_x_pos___=0
  135.  
  136. print("motors homed")
  137.  
  138. def move_motor_into_position(motor, position):
  139. """
  140. moves desired motor into position
  141. and updates position in variables
  142. in:
  143. motor (string) contains motor axis (x,y,z)
  144. position (int) desired position in steps
  145. out:
  146. move (int) how many steps the motor need to move
  147. """
  148. move=0
  149. if motor=='x':
  150. move = position-v.motor_x_pos___
  151. if move>0 and move<=4800:
  152. motor_x.backward(v.stimulus_motor_speed,move)
  153. v.motor_x_pos___+=move
  154. elif move>-4800 and move<0:
  155. motor_x.forward(v.stimulus_motor_speed,move*(-1))
  156. v.motor_x_pos___+=move
  157.  
  158. elif motor=='y':
  159. move = position-v.motor_y_pos___
  160. if move>0 and move<=4800:
  161. motor_y.backward(v.stimulus_motor_speed,move)
  162. v.motor_y_pos___+=move
  163. elif move>=-4800 and move<0:
  164. motor_y.forward(v.stimulus_motor_speed,move*(-1))
  165. v.motor_y_pos___+=move
  166.  
  167. elif motor=='z':
  168. move = position-v.motor_z_pos___
  169. if move>0 and move<=4800:
  170. motor_z.backward(v.stimulus_motor_speed,move)
  171. v.motor_z_pos___+=move
  172. elif move>=-4800 and move<0:
  173. motor_z.forward(v.stimulus_motor_speed,move*(-1))
  174. v.motor_z_pos___+=move
  175. return move
  176.  
  177. def get_rand_offset(): #returns a random number of time to be added to delay
  178. return randint(0,v.wheel_delay_offset)/100 + 1
  179.  
  180.  
  181. def run_start():
  182. recording_trigger.on()
  183.  
  184. print("starting recording")
  185.  
  186. def start_trial(event):
  187. if(not v.finished_startup___ and event=='start_trial_event'): #first trial will only start if worker has triggered the event manually, meaning the camera is recording
  188. if (v.trial_counter___==0):#update list lengths
  189. # 0-A 1-B 2-catch trial
  190. v.last_trials_stimuli___=[0]*v.accuracy_history_limit #list used for saving the last shown stimuli
  191. # 0-A 1-B 2-none
  192. v.last_trials_lick___=[0]*v.accuracy_history_limit #last licks of the mouse
  193.  
  194. print("trial #"+str(v.trial_counter___))
  195. print("laser on: " +str(v.laser_on))
  196. #setup the trial
  197. #turn on speaker and beep for start
  198. speaker.set_volume(v.beep_volume)
  199. speaker.sine(v.start_beep_frequency)
  200.  
  201. #randomize the duration before experiment begins
  202. rand_offset = randint(0,v.wheel_delay_offset)/100 + 1
  203. set_timer('start_walking',v.delay_to_start_wheel * rand_offset)
  204. set_timer('speaker_off',800)
  205.  
  206. # remove oldest trial data from lists
  207. v.last_trials_lick___.pop(0)
  208. v.last_trials_stimuli___.pop(0)
  209.  
  210. v.finished_startup___=True
  211.  
  212. trial_pulse.on()
  213. set_timer("stop_trial_pulse",5)
  214.  
  215. goto_state('main_loop')
  216. if v.laser_on:
  217. set_timer("laser_on",v.laser_delay_from_start)
  218. set_timer("move_in",300)
  219.  
  220. if (event=='start_walking'):
  221. wheel.on()
  222. v.finished_startup___=True
  223.  
  224. goto_state('main_loop')
  225.  
  226. def main_loop(event):
  227. """
  228. event=="move_in"
  229. pick stimulus target position, set x/y/z_position to desired value
  230. run move_motor_into_position for each axis while setting the wait time to the max
  231. event =="move_out"
  232. move stimulus out of position to the waiting spot
  233. """
  234. #move stimulus into position
  235. if event=="move_in":
  236. v.motors_stationary___=False
  237. if (v.bias_correction_counter>0): #correction series is still going
  238. v.bias_correction_counter-=1
  239. if (v.correction_stimulus___==0):
  240. x_position = v.stimulus_A_x_value
  241. y_position = v.stimulus_A_y_value
  242. z_position = v.stimulus_A_z_value
  243. v.last_trials_stimuli___.append(0)
  244. print("moving into position A")
  245. else:
  246. x_position = v.stimulus_B_x_value
  247. y_position = v.stimulus_B_y_value
  248. z_position = v.stimulus_B_z_value
  249. v.last_trials_stimuli___.append(1)
  250. print("moving into position B")
  251.  
  252. #if not correction trial
  253. else:
  254. random_value=random()
  255. if random_value<=v.probability_of_catch_trial: #pick regular trial or catch trial:
  256. z_position = v.catch_trial_z_position
  257. x_position = v.catch_trial_x_position
  258. y_position = v.catch_trial_y_position
  259. print("catch trial")
  260.  
  261. v.last_trials_stimuli___.append(2)
  262.  
  263. else:
  264. #generate value between 0 and 1 to pick a desired position
  265. #set x/y/z_position to desired value
  266. #move motor into position
  267. random_value = random() #pick random material based on preset ratio
  268. print("random value: "+str(random_value))
  269. if random_value<=v.probability_of_pos_A:
  270. x_position = v.stimulus_A_x_value
  271. y_position = v.stimulus_A_y_value
  272. z_position = v.stimulus_A_z_value
  273.  
  274. #update last stimuli list
  275.  
  276. v.last_trials_stimuli___.append(0)
  277. print("moving into position A")
  278. else:
  279. x_position = v.stimulus_B_x_value
  280. y_position = v.stimulus_B_y_value
  281. z_position = v.stimulus_B_z_value
  282. #update last stimuli list
  283.  
  284. v.last_trials_stimuli___.append(1)
  285. print("moving into position B")
  286.  
  287. moving_time = move_motor_into_position('z',z_position)
  288. #calls the function that move along axis, the function returns the ETA
  289. #takes the max of all axes ETA for the next event
  290. moving_time = max(moving_time,move_motor_into_position('y',y_position))
  291. moving_time = max(moving_time,move_motor_into_position('x',x_position))
  292. v.motors_ready___=False
  293. timed_goto_state("update_motors",moving_time*0.9/v.stimulus_motor_speed*second) #wait for motors to finish setting stimulus in position
  294.  
  295. elif event =="move_out": #move out of position to waiting spot
  296. if v.motors_stationary___ and not v.motors_ready___:
  297.  
  298. v.motors_stationary___=False
  299. #calls the function that move along axis, the function returns the ETA
  300. #takes the max of all axes ETA for the next event
  301. moving_time = move_motor_into_position('y',randint(v.stimulus_y_outer_bounds[0],v.stimulus_y_outer_bounds[1]))
  302. moving_time = max(moving_time,move_motor_into_position('x',randint(v.stimulus_x_outer_bounds[0],v.stimulus_x_outer_bounds[1])))
  303. moving_time = max(moving_time,move_motor_into_position('z',randint(v.stimulus_z_outer_bounds[0],v.stimulus_z_outer_bounds[1])))
  304. set_timer("end_trial",moving_time/v.stimulus_motor_speed*second)
  305. v.motors_ready___=True
  306. v.motors_stationary___=True
  307.  
  308.  
  309.  
  310. def update_motors(event):
  311. if event=='entry':
  312. v.motors_stationary___=True
  313. speaker.sine(v.water_beep_frequency)
  314. print("start lick window")
  315. set_timer('speaker_off',500)
  316. set_timer('exit_position',v.stimulus_time_window)
  317. elif event=='exit_position': #if picked random position that's no the correct one move back to waiting
  318. set_timer('move_out',50)
  319. goto_state('main_loop')
  320.  
  321. def all_states(event):
  322. # mouse only gets one chance to lick correctly.
  323. # meaning if he licks first the wrong spout then the right one he will get no water and punishment time will be added
  324. if event=="lick_1": #if mouse licks pump 1 disarm the incorrect position event, turn pump on and go back to main loop
  325. if v.finished_startup___ and v.motors_stationary___ and v.last_trials_stimuli___[-1]==0 and not v.licked_this_window___ and not v.motors_ready___:
  326. v.licked_this_window___=True
  327. v.correct_lick_counter___+=1
  328.  
  329. v.last_trials_lick___.append(0)
  330.  
  331. publish_event("pump_1_on")
  332. print("licked correctly for position A")
  333.  
  334. #if mouse licks for incorrect stimulus set punishment flag to True
  335. elif v.punishment_for_incorrect_stimulus and v.finished_startup___ and v.motors_stationary___ and not v.licked_this_window___ and not v.motors_ready___:
  336. v.add_punishment___=True
  337.  
  338. v.last_trials_lick___.append(0)
  339. elif v.punishment_for_incorrect_timing and not v.licked_this_window___:
  340. v.add_punishment___=True
  341.  
  342. v.last_trials_lick___.append(0)
  343.  
  344. if event=="lick_2":
  345. if v.finished_startup___ and v.motors_stationary___ and v.last_trials_stimuli___[-1]==1 and not v.licked_this_window___ and not v.motors_ready___:
  346. v.licked_this_window___=True
  347. v.correct_lick_counter___+=1
  348.  
  349. v.last_trials_lick___.append(1)
  350.  
  351. publish_event("pump_2_on")
  352. print("licked correctly for position B")
  353.  
  354. #if mouse licks for incorrect stimulus set punishment flag to True
  355. elif v.punishment_for_incorrect_stimulus and v.finished_startup___ and v.motors_stationary___ and not v.licked_this_window___ and not v.motors_ready___:
  356. v.add_punishment___=True
  357.  
  358. v.last_trials_lick___.append(1)
  359. elif v.punishment_for_incorrect_timing and not v.licked_this_window___:
  360. v.add_punishment___=True
  361.  
  362. v.last_trials_lick___.append(1)
  363.  
  364. if(event=='pump_1_on'):
  365. if(not v.pump_1_bool___):
  366. pump_1.on()
  367. v.pump_1_bool___=True
  368. set_timer('pump_off',v.pump_duration) #turn off pump after set duration
  369.  
  370.  
  371. if(event=='pump_off'):
  372. pump_1.off()
  373. v.pump_1_bool___=False
  374. pump_2.off()
  375. v.pump_2_bool___=False
  376.  
  377. if(event=='pump_2_on'):
  378. if(not v.pump_2_bool___):
  379. pump_2.on()
  380. v.pump_2_bool___=True
  381. set_timer('pump_off',v.pump_duration) #turn off pump after set duration
  382.  
  383.  
  384. if event=="laser_on":
  385. if v.laser_pulse_counter___<v.laser_number_of_pulses:
  386. v.laser_pulse_counter___+=1
  387. laser.on()
  388. set_timer("laser_off",v.laser_pulse_duration)
  389. else:
  390. v.laser_pulse_counter___=0
  391.  
  392. if event=='laser_off':
  393. laser.off()
  394. set_timer("laser_on",v.laser_inter_pulse_interval)
  395.  
  396. if (event=='speaker_off'):
  397. speaker.off()
  398.  
  399. if event == 'stop_trial_pulse':
  400. trial_pulse.off()
  401.  
  402. if (event=='end_trial'):
  403. #make sure all devices are off
  404. speaker.off()
  405. pump_1.off()
  406. pump_2.off()
  407. wheel.off()
  408. laser.off()
  409.  
  410. # print_variables()
  411. v.trial_counter___+=1
  412.  
  413. #reset flags
  414. v.finished_startup___=False
  415. v.motors_stationary___=True
  416. v.motors_ready___=True
  417. v.licked_this_window___=False
  418. n = len(v.last_trials_stimuli___)
  419. #if not licked this trial, mark it
  420. if len(v.last_trials_lick___)<n:
  421. v.last_trials_lick___.append(2)
  422.  
  423.  
  424. #bias detection
  425. #only after at least n trials
  426. if(v.trial_counter___>n):
  427. a_stimulus_counter=v.last_trials_stimuli___.count(0)
  428. b_stimulus_counter=v.last_trials_stimuli___.count(1)
  429. a_lick_counter=v.last_trials_lick___.count(0)
  430. b_lick_counter=v.last_trials_lick___.count(1)
  431. accuracy = 0
  432. for i in range(n):
  433. if v.last_trials_lick___[i]==v.last_trials_stimuli___[i]:
  434. accuracy+=1
  435.  
  436. accuracy=accuracy/n
  437. print("Current accuracy: " + str(accuracy))
  438.  
  439.  
  440. bias_flag = False
  441. #check if bias is detected and not currently being dealt with
  442. if (a_lick_counter-a_stimulus_counter)/n > v.bias_threshold and v.bias_correction_counter___==0:
  443. v.bias_correction_counter___= int(v.bias_correction_size*n)
  444. v.correction_stimulus___=1
  445. bias_flag = True
  446.  
  447. elif (b_lick_counter-b_stimulus_counter)/n > v.bias_threshold and v.bias_correction_counter___==0:
  448. v.bias_correction_counter___= int(v.bias_correction_size*n)
  449. v.correction_stimulus___=0
  450. bias_flag = True
  451.  
  452. if bias_flag:
  453. #mark last trials as biased
  454. for i in range(v.trial_counter___-n,v.trial_counter___):
  455. print("biased trial: " + str(i))
  456. elif v.bias_correction_counter___>0:
  457. print("biased trial: " + str(v.trial_counter___))
  458.  
  459. #if amount of trials is reached, end task
  460. #if amount of trials set to -1, will never stop automatically
  461. if v.trial_counter___!=v.number_of_trials:
  462. set_timer('start_trial_event',v.inter_trial_interval + v.add_punishment___*v.punishment_period)
  463. if v.add_punishment___:
  464. print("adding punishment period")
  465. v.add_punishment___=False
  466. goto_state('start_trial')
  467. else:
  468. stop_framework()
  469. if (event=='end_experiment'):
  470. stop_framework()
  471.  
  472.  
  473. def run_end():
  474. #make sure all devices are off
  475. return_home()
  476. speaker.off()
  477. pump_1.off()
  478. pump_2.off()
  479. wheel.off()
  480. recording_trigger.off()
  481. print("stopped recording")
  482. print_variables()
  483.  
Advertisement
Add Comment
Please, Sign In to add comment