Airtrack.ino 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424
  1. /**
  2. * Copyright (C) 2019-2020 Ronny Bergmann
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #import <Arduino.h>
  19. #include <Wire.h> // Need by sensor.h
  20. #include <SPI.h>
  21. #include <Pixy.h>
  22. #include <Servo.h>
  23. #include "definitions.h"
  24. #include "leds.h"
  25. #include "pins.h"
  26. #include "stats.h"
  27. #include "sensor.h"
  28. #include "actuator.h"
  29. #include "reporter.h"
  30. const bool AUTOMATED_REWARD = true;
  31. const bool SINGLE_REWARD = true;
  32. const bool FEEDBACK_AUTOMATED_REWARD = false;
  33. const float threshold = 75;
  34. const int pumpActivatorPulse = 29;
  35. const bool training = true;
  36. const int time_until_training = 8000;
  37. int time_counter=0;
  38. int opto_activation_trial = -1;
  39. typedef struct Maus{
  40. float posx[10];
  41. float posy[10];
  42. float pos_new[2];
  43. float pos_old[2];
  44. float difference[2];
  45. int angle[10];
  46. float mean_angle;
  47. float velocity;
  48. int index;
  49. bool optogenetics;
  50. bool below_threshold;
  51. }Maus;
  52. struct Maus maus;
  53. GlobalState global_state;
  54. Sensor sensor = Sensor(Pins.Sensor);
  55. Actuator actuator = Actuator(Pins.ActuatorPush, Pins.ActuatorPull,
  56. global_state.actuator_max_pwm_distance,
  57. &global_state);
  58. Pixy pixy;
  59. Servo myservo1;
  60. Servo myservoleft;
  61. Servo myservoright;
  62. // the setup routine runs once when you press reset:
  63. void setup()
  64. {
  65. Serial.begin(115200);
  66. Serial.println("Initializing");
  67. Serial.println("Setting up sensor");
  68. sensor.setup();
  69. Serial.println("Setting up LEDs");
  70. setupLeds();
  71. Serial.println("Setting up Pins");
  72. setupPins();
  73. Serial.println("Setting up Lanes");
  74. setupLanes();
  75. Serial.println("Setting up Pixy");
  76. pixy.init();
  77. Serial.println("Pixy is set up");
  78. pinMode(25, INPUT_PULLUP);
  79. pinMode(31,INPUT_PULLUP);
  80. pinMode(35, INPUT_PULLUP);
  81. pinMode(3, OUTPUT);
  82. pinMode(4, OUTPUT);
  83. pinMode(5, OUTPUT);
  84. pinMode(6, OUTPUT);
  85. pinMode(9, OUTPUT);
  86. pinMode(44, OUTPUT);
  87. pinMode(13, OUTPUT);
  88. pinMode(23, INPUT);
  89. pinMode(pumpActivatorPulse, OUTPUT);
  90. //GPIO Outputs for trigger
  91. pinMode(A8, OUTPUT);
  92. pinMode(A9, OUTPUT);
  93. pinMode(A10, OUTPUT);
  94. pinMode(A11, OUTPUT);
  95. pinMode(A12, OUTPUT);
  96. pinMode(A13, OUTPUT);
  97. pinMode(A14, OUTPUT);
  98. pinMode(A15, OUTPUT);
  99. myservo1.attach(2);
  100. myservoright.attach(6);
  101. myservoleft.attach(7);
  102. myservoright.attach(44);
  103. //pinMode(29, OUTPUT);
  104. for (int i = 0; i < global_state.MOTOR_DURATION_ENTERIES_SIZE; i++)
  105. {
  106. global_state.motor_duration_entries[i].activated = false;
  107. }
  108. global_state.is_automated_reward = AUTOMATED_REWARD;
  109. global_state.trial_number = 0;
  110. global_state.was_inside_lane = false;
  111. // Initially report non existing lane
  112. global_state.last_reported_lane = global_state.NUM_OF_LANES + 1;
  113. // Assign any random initial value
  114. global_state.last_reported_light_status = 250;
  115. global_state.actuator_duration_activated = false;
  116. Serial.println("Setting up actuator");
  117. actuator.setup();
  118. Serial.println("Actuator is set up");
  119. // We are seeding the random so it would give us reproducible results
  120. // randomSeed(0);// call randomSeed(analogRead(A3)) for random order on each run
  121. randomSeed(analogRead(A3));
  122. global_state.was_inside_lane = false;
  123. // Assign last lane id to soeme random value so that it'd be possible to
  124. // choose lane 0 as the first lane
  125. global_state.reward_lane_id = -1;
  126. global_state.reward_direction = -1;
  127. createShuffledChoice();
  128. makeNewRewardLane();
  129. }
  130. // the loop routine runs over and over again forever:
  131. void loop ()
  132. {
  133. //Serial.println(global_state.reward_direction);
  134. //#######################################################################################Begin
  135. if(digitalRead(25)==LOW) //Enter Setup
  136. {
  137. Serial.println("Setup entered");
  138. digitalWrite(A9,HIGH);
  139. delay(300);
  140. while(digitalRead(25)==HIGH)
  141. {
  142. digitalWrite(3, HIGH);
  143. if(digitalRead(35)==LOW)
  144. {
  145. actuator.enablePush();
  146. }
  147. else if(digitalRead(31)==LOW)
  148. {
  149. actuator.enablePull();
  150. }
  151. else
  152. {
  153. actuator.enableStill();
  154. }
  155. }
  156. actuator.setMaxDistance(actuator.getMotorDist());
  157. actuator.setup();
  158. digitalWrite(3, LOW);
  159. delay(1000);
  160. }
  161. digitalWrite(A9,LOW);
  162. if(digitalRead(23)==HIGH){
  163. //turnOnMotor(Pins.SolenoidLeft, global_state.SOLENOID_DURATION);
  164. Serial.println("Pump pulse");
  165. digitalWrite(pumpActivatorPulse, HIGH);
  166. delayMicroseconds(100);
  167. digitalWrite(pumpActivatorPulse,LOW);
  168. }
  169. //#######################################################################################End
  170. SubjectLocation subject_location = getSubjectLocation();
  171. if (subject_location.block_detected)
  172. {
  173. global_state.last_subject_location = subject_location;
  174. }
  175. else
  176. {
  177. subject_location = global_state.last_subject_location;
  178. }
  179. bool is_within_reward_lane_angle = isWithinRewardLaneAngle(subject_location);
  180. bool is_inside_lane = isInsideLane(subject_location);
  181. move_training_servos(subject_location.angle, is_within_reward_lane_angle);
  182. bool motor_pushed = false;
  183. if(time_counter<time_until_training+10 && !is_within_reward_lane_angle){
  184. time_counter++;
  185. }
  186. int factor = 100;
  187. if(time_counter > time_until_training && training && global_state.Servo1_pos < 160*factor && !is_within_reward_lane_angle){
  188. global_state.Servo1_pos += 1;
  189. myservo1.write(global_state.Servo1_pos/factor);
  190. }
  191. SensorTouched touched_sensor = sensor.readInput();
  192. if ( !touched_sensor.change_happened )
  193. {
  194. Stats.REPORT_SENSORS_UNTOUCHED();
  195. }
  196. // Serial.print("We are within reward lane");
  197. if (is_inside_lane)
  198. {
  199. if (!global_state.was_inside_lane)
  200. {
  201. writeStats(Stats.ENTERED_LANE(global_state.current_lane));
  202. }
  203. global_state.was_inside_lane = true;
  204. // Serial.print( " and inside lane ");
  205. if (is_within_reward_lane_angle)
  206. {
  207. if (shouldTriggerMotor(subject_location))
  208. {
  209. // Serial.print( " and we should fire motor ");
  210. //Serial.println("");
  211. actuator.setState(Actuator::PUSH);
  212. if (global_state.last_reported_actuator_status != Actuator::PUSH)
  213. {
  214. writeStats(Stats.MOTOR_PUSHED());
  215. global_state.last_reported_actuator_status = Actuator::PUSH;
  216. global_state.sensor_was_touched = false;
  217. }
  218. motor_pushed = true;
  219. if (global_state.actuator_at_max_push)
  220. {
  221. if (!global_state.reported_motor_max_distance)
  222. {
  223. writeStats(Stats.MOTOR_MAX_RANGE());
  224. global_state.reported_motor_max_distance = true;
  225. }
  226. // Allow a bit of buffer time for sensor vibration to
  227. // rest before reading
  228. long int time_now = millis();
  229. if (global_state.max_push_current_duration <=
  230. time_now - global_state.MAX_PUSH_WAIT)
  231. {
  232. if (!global_state.reported_motor_max_wait)
  233. {
  234. Serial.print("max_push_current_duration: ");
  235. Serial.print(global_state.max_push_current_duration);
  236. Serial.print(" - MAX_PUSH_WAIT: ");
  237. Serial.print(global_state.MAX_PUSH_WAIT);
  238. Serial.print("- time_now: ");
  239. Serial.println(time_now);
  240. writeStats(Stats.MOTOR_WAIT_DONE());
  241. global_state.reported_motor_max_wait = true;
  242. }
  243. // Call isCorrectSensor() anyway so it'd call
  244. // writeStats() on the touched sensor
  245. bool is_correct_sensor = isCorrectSensor(touched_sensor);
  246. if (touched_sensor.change_happened &&
  247. !global_state.sensor_was_touched)
  248. {
  249. global_state.sensor_was_touched = true;
  250. if (is_correct_sensor)
  251. global_state.miss_or_wrong_touch_count = 0;
  252. else
  253. global_state.miss_or_wrong_touch_count += 1;
  254. }
  255. if (global_state.is_automated_reward ||
  256. touched_sensor.change_happened)
  257. {
  258. checkGiveReward(is_correct_sensor,
  259. global_state.is_automated_reward);
  260. }
  261. }
  262. }
  263. }
  264. }
  265. else
  266. {
  267. // Count that it's a bad trial
  268. // if (touched_sensor.change_happened)
  269. // {
  270. // checkGiveReward(false);
  271. // }
  272. }
  273. //Serial.println("");
  274. }
  275. else // Outside a lane
  276. {
  277. if (global_state.was_inside_lane)
  278. {
  279. writeStats(Stats.EXITED_LANE(global_state.current_lane));
  280. // Turn off peizo if it was on
  281. if (global_state.peizo_motor_entry != NULL)
  282. {
  283. do
  284. {
  285. digitalWrite(global_state.peizo_motor_entry->motor_id, LOW);
  286. }
  287. while (digitalRead(global_state.peizo_motor_entry->motor_id));
  288. global_state.peizo_motor_entry->activated = false;
  289. global_state.peizo_motor_entry = NULL;
  290. }
  291. }
  292. global_state.was_inside_lane = false;
  293. global_state.reported_motor_max_distance = false;
  294. }
  295. if (subject_location.block_detected && !motor_pushed)
  296. {
  297. //digitalWrite(29,LOW); //########################################################################################
  298. actuator.setState(Actuator::PULL);
  299. if (global_state.last_reported_actuator_status != Actuator::PULL)
  300. {
  301. writeStats(Stats.MOTOR_PULLED());
  302. global_state.last_reported_actuator_status = Actuator::PULL;
  303. }
  304. if (global_state.actuator_at_min_pull)
  305. {
  306. if (!global_state.reported_motor_min_distance)
  307. {
  308. writeStats(Stats.MOTOR_MIN_RANGE());
  309. global_state.reported_motor_min_distance = true;
  310. // TODO: Do it cleanly
  311. turnOnMotor(13, 20);
  312. //digitalWrite(45, HIGH);
  313. }
  314. }
  315. else
  316. {
  317. global_state.reported_motor_min_distance = false;
  318. }
  319. }
  320. global_state.was_inside_lane = is_inside_lane;
  321. turnOffMotor();
  322. actuator.motorLoop();
  323. if (global_state.delayed_report)
  324. {
  325. long int time_now = millis();
  326. if (time_now >= global_state.delayed_report)
  327. {
  328. // TODO: Do it cleanly
  329. turnOnMotor(13, 20);
  330. global_state.delayed_report = 0;
  331. }
  332. }
  333. }
  334. bool isInsideLane(SubjectLocation subject_location)
  335. {
  336. int flexible_range = 10;
  337. if (global_state.was_inside_lane)
  338. {
  339. if (subject_location.y >= Distances.y_threshold_max &&
  340. subject_location.y - Distances.y_threshold_max <= flexible_range)
  341. {
  342. subject_location.y = Distances.y_threshold_max - 1;
  343. }
  344. }
  345. else
  346. {
  347. if (subject_location.y <= Distances.y_threshold_max &&
  348. Distances.y_threshold_max - subject_location.y <= flexible_range)
  349. {
  350. subject_location.y = Distances.y_threshold_max + 1;
  351. }
  352. }
  353. return Distances.x_threshold_min < subject_location.x &&
  354. subject_location.x < Distances.x_threshold_max &&
  355. Distances.y_threshold_min < subject_location.y &&
  356. subject_location.y < Distances.y_threshold_max;
  357. }
  358. bool shouldTriggerMotor(SubjectLocation subject_location)
  359. {
  360. bool is_within_distance = Distances.y_threshold_min < subject_location.y &&
  361. subject_location.y < Distances.y_motor_threshold;
  362. if (!is_within_distance)
  363. {
  364. // Serial.println("Not withing distance");
  365. global_state.actuator_duration_activated = false;
  366. if (global_state.reward_given && !global_state.chose_new_lane)
  367. {
  368. makeNewRewardLane();
  369. global_state.chose_new_lane = true;
  370. }
  371. return false;
  372. }
  373. else
  374. {
  375. long int time_now = millis();
  376. if (!global_state.actuator_duration_activated)
  377. {
  378. global_state.chose_new_lane = false;
  379. // Only start counting when we are at max pwm
  380. setActuatorTimeout(global_state.MAX_PUSH_TIMEOUT);
  381. return true;
  382. }
  383. // We are still counting, retunr true until we time out
  384. else if (global_state.max_push_current_duration +
  385. global_state.motor_timeout_duration >= time_now)
  386. {
  387. return true;
  388. }
  389. else
  390. {
  391. if (isWithinRewardLaneAngle(subject_location) &&
  392. !global_state.chose_new_lane)
  393. {
  394. if (!global_state.reward_given ||
  395. (AUTOMATED_REWARD && !global_state.sensor_was_touched))
  396. {
  397. writeStats(Stats.MISS_DECISION());
  398. global_state.miss_or_wrong_touch_count += 1;
  399. }
  400. makeNewRewardLane();
  401. global_state.chose_new_lane = true;
  402. }
  403. return false;
  404. }
  405. }
  406. }
  407. SubjectLocation getSubjectLocation()
  408. {
  409. // Serial.println("Gettin blocks");
  410. uint16_t num_of_blocks = pixy.getBlocks();
  411. //Serial.print("Pixy array size: ");
  412. //Serial.println(num_of_blocks);
  413. SubjectLocation location = SubjectLocation();
  414. location.block_detected = num_of_blocks == 1;
  415. if (location.block_detected)
  416. {
  417. location.angle = pixy.blocks[0].angle;
  418. location.x = pixy.blocks[0].x;
  419. location.y = pixy.blocks[0].y;
  420. if (location.angle > 180)
  421. location.angle -= 360;
  422. maus = calculateVelocity(maus, location.x, location.y, location.angle);
  423. //Serial.println(location.angle);
  424. Stats.REPORT_PIXY_POSITION(location);
  425. const bool ENABLE_POSITION_PRINT = false;
  426. if (ENABLE_POSITION_PRINT)
  427. {
  428. Serial.print("subject_location: x: ");
  429. Serial.print(location.x);
  430. Serial.print(" y: ");
  431. Serial.print(location.y);
  432. Serial.print(" angle: ");
  433. Serial.print(location.angle);
  434. Serial.print(" Mouse velocity: ");
  435. Serial.print(maus.velocity);
  436. Serial.print(" Num. of blocks: ");
  437. Serial.println(num_of_blocks);
  438. }
  439. }
  440. return location;
  441. }
  442. bool isCorrectSensor(SensorTouched touched_sensor)
  443. {
  444. Lane lane = global_state.lanes[global_state.current_lane];
  445. if (touched_sensor.left_sensor == true)
  446. {
  447. // Bug here: switch name
  448. writeStats(Stats.LEFT_SENSOR_TOUCHED());
  449. digitalWrite(Leds.SensorLeft, HIGH);
  450. checkForceSensorMode(true);
  451. if (lane.reward_sensor == sensor.LEFT_ANALOUGE_PIN)
  452. {
  453. writeStats(Stats.CORRECT_SENSOR_TOUCHED());
  454. return true;
  455. }
  456. else
  457. {
  458. writeStats(Stats.WRONG_SENSOR_TOUCHED());
  459. return false;
  460. }
  461. }
  462. else
  463. {
  464. digitalWrite(Leds.SensorLeft, LOW);
  465. }
  466. if (touched_sensor.right_sensor == true)
  467. {
  468. // Bug here: switch name
  469. writeStats(Stats.RIGHT_SENSOR_TOUCHED());
  470. digitalWrite(Leds.SensorRight, HIGH);
  471. checkForceSensorMode(false);
  472. if (lane.reward_sensor == sensor.RIGHT_ANALOUGE_PIN)
  473. {
  474. writeStats(Stats.CORRECT_SENSOR_TOUCHED());
  475. return true;
  476. }
  477. else
  478. {
  479. writeStats(Stats.WRONG_SENSOR_TOUCHED());
  480. return false;
  481. }
  482. }
  483. else
  484. {
  485. digitalWrite(Leds.SensorRight, LOW);
  486. }
  487. return false;
  488. }
  489. void checkForceSensorMode(bool is_left_sensor_touched)
  490. {
  491. if (SINGLE_REWARD)
  492. return;
  493. if (global_state.actuator_at_max_push && !global_state.sensor_was_touched)
  494. {
  495. if (is_left_sensor_touched == global_state.last_sensor_touched_left)
  496. {
  497. global_state.same_sensor_touch_count++;
  498. }
  499. else
  500. {
  501. global_state.last_sensor_touched_left = is_left_sensor_touched;
  502. global_state.same_sensor_touch_count = 1;
  503. }
  504. unsigned int touch_count = global_state.same_sensor_touch_count;
  505. if (global_state.in_force_sensor_mode)
  506. {
  507. if (is_left_sensor_touched == global_state.is_force_sensor_left)
  508. {
  509. if (touch_count == global_state.FORCE_OTHER_SENSOR)
  510. {
  511. global_state.in_force_sensor_mode = false;
  512. // Should we reset the counter or not?
  513. global_state.same_sensor_touch_count = 1;
  514. writeStats(Stats.FORCE_SENSOR_OFF());
  515. }
  516. }
  517. if (FEEDBACK_AUTOMATED_REWARD)
  518. {
  519. if (is_left_sensor_touched == global_state.is_force_sensor_left)
  520. {
  521. if (global_state.is_automated_reward)
  522. writeStats(Stats.FEEDBACK_AUTOMATED_OFF());
  523. global_state.is_automated_reward = false;
  524. }
  525. else if (global_state.miss_or_wrong_touch_count >=
  526. global_state.FEEDBACK_AUTOMATED_REWARD_THRESHOLD)
  527. {
  528. if (!global_state.is_automated_reward)
  529. writeStats(Stats.FEEDBACK_AUTOMATED_ON());
  530. global_state.is_automated_reward = true;
  531. }
  532. }
  533. }
  534. else if (touch_count == global_state.SAME_SENSOR_MAX_THRESHOLD)
  535. {
  536. global_state.in_force_sensor_mode = true;
  537. global_state.is_force_sensor_left = !is_left_sensor_touched;
  538. writeStats(Stats.FORCE_SENSOR_ON(!is_left_sensor_touched));
  539. }
  540. }
  541. }
  542. void checkGiveReward(bool is_correct_sensor, bool is_automated_reward)
  543. {
  544. if (global_state.reward_given)
  545. return;
  546. if (is_correct_sensor || is_automated_reward)
  547. {
  548. // Report if reward was given due to correct sensor was touched or
  549. // due to wrong sensore touched but automated reward is enabled
  550. writeStats(Stats.REWARD_GIVEN(is_correct_sensor == false));
  551. // TODO: Clean hacky way
  552. global_state.delayed_report = millis() + 2000;
  553. Lane lane = global_state.lanes[global_state.reward_lane_id];
  554. if (lane.reward_sensor == sensor.RIGHT_ANALOUGE_PIN)
  555. {
  556. writeStats(Stats.SOLENOID_RIGHT_ON());
  557. turnOnMotor(Pins.SolenoidRight, global_state.SOLENOID_DURATION);
  558. }
  559. else if (lane.reward_sensor == sensor.LEFT_ANALOUGE_PIN)
  560. {
  561. writeStats(Stats.SOLENOID_LEFT_ON());
  562. Serial.write("Reward given");
  563. digitalWrite(pumpActivatorPulse, HIGH);
  564. delayMicroseconds(global_state.SOLENOID_DURATION);
  565. digitalWrite(pumpActivatorPulse, LOW);
  566. //turnOnMotor(Pins.SolenoidLeft, global_state.SOLENOID_DURATION);
  567. }
  568. else
  569. {
  570. Serial.println("Want to give reward - Unknown Solenoid");
  571. }
  572. setActuatorTimeout(global_state.ALLOWED_REWARD_TIMEOUT);
  573. }
  574. else
  575. {
  576. //Serial.println("No reward");
  577. writeStats(Stats.REWARD_NOT_GIVEN());
  578. setActuatorTimeout(global_state.NO_REWARD_TIMEOUT);
  579. global_state.peizo_motor_entry = turnOnMotor(Pins.PeizoTone,
  580. global_state.PEIZO_TIMEOUT);
  581. }
  582. global_state.reward_given = true;
  583. }
  584. void makeNewRewardLane()
  585. {
  586. // Assign to a non existing lane initially
  587. LANE_ID new_lane_id = global_state.NUM_OF_LANES + 1;
  588. if (global_state.in_force_sensor_mode)
  589. {
  590. bool found_lane = false;
  591. do
  592. {
  593. LANE_ID potential_lane_id = random(global_state.NUM_OF_LANES);
  594. if (potential_lane_id == global_state.reward_lane_id)
  595. continue;
  596. Lane lane = global_state.lanes[potential_lane_id];
  597. bool is_left = lane.reward_sensor == sensor.LEFT_ANALOUGE_PIN;
  598. if (is_left != global_state.is_force_sensor_left)
  599. continue;
  600. new_lane_id = potential_lane_id;
  601. found_lane = true;
  602. }
  603. while (!found_lane);
  604. }
  605. else
  606. {
  607. LANE_ID* list_ptr = global_state.lane_shuffle_list;
  608. size_t index = global_state.shuffle_list_index;
  609. new_lane_id = list_ptr[index];
  610. // This might happen if the last lane was due to a forced sensor mode
  611. if (new_lane_id == global_state.reward_lane_id)
  612. {
  613. // IF there is enough space then swap
  614. if (index < global_state.GUARANTEED_RANDOM_BOUND - 1)
  615. {
  616. LANE_ID temp = list_ptr[index];
  617. list_ptr[index] = list_ptr[index + 1];
  618. list_ptr[index + 1] = temp;
  619. }
  620. else
  621. {
  622. createShuffledChoice();
  623. new_lane_id = list_ptr[0];
  624. }
  625. }
  626. global_state.shuffle_list_index++;
  627. }
  628. //#####################################################easy trial
  629. global_state.reward_direction = random(0,2);
  630. if(global_state.reward_direction == 0){
  631. if(global_state.current_lane == 3){
  632. new_lane_id = 0;
  633. }
  634. else
  635. new_lane_id = global_state.current_lane + 1;
  636. }
  637. if(global_state.reward_direction == 1){
  638. if(global_state.current_lane == 0){
  639. new_lane_id = 3;
  640. }
  641. else
  642. new_lane_id = global_state.current_lane - 1;
  643. }
  644. //######################################################
  645. global_state.reward_lane_id = new_lane_id;
  646. Serial.print("New reward lane is: ");
  647. Serial.println(global_state.reward_lane_id);
  648. Serial.print("New reward rotation direction is: ");
  649. Serial.println(global_state.reward_direction);
  650. global_state.reward_given = false;
  651. // We must assign first thenew lane before calling createShuffledChoice()
  652. if (global_state.shuffle_list_index == global_state.GUARANTEED_RANDOM_BOUND)
  653. {
  654. createShuffledChoice();
  655. }
  656. writeStats(Stats.NEW_LANE(new_lane_id));
  657. writeStats(Stats.NEW_TRIAL(global_state.trial_number));
  658. //#########################################################################################################################
  659. //Video end trigger
  660. digitalWrite(A8, HIGH);
  661. digitalWrite(A9, HIGH);
  662. digitalWrite(A10, HIGH);
  663. delay(100);
  664. digitalWrite(A8, LOW);
  665. digitalWrite(A9, LOW);
  666. digitalWrite(A10, LOW);
  667. global_state.trial_number += 1;
  668. digitalWrite(A9, HIGH);
  669. delay(20);
  670. digitalWrite(A9,LOW);
  671. printRewardLane();
  672. //pylonPD execution Trigger
  673. delay(100);
  674. digitalWrite(A11, HIGH);
  675. delay(20);
  676. digitalWrite(A11, LOW);
  677. delay(500);
  678. //Video start trigger
  679. digitalWrite(A12, HIGH);
  680. digitalWrite(A15, HIGH);
  681. delay(100);
  682. digitalWrite(A12, LOW);
  683. digitalWrite(A15, LOW);
  684. delay(50);
  685. //Video Mark trigger (removed, because new camera has issues with channel 4 and ZR-View only supports 3 channels due to reasons)
  686. digitalWrite(A15, HIGH);
  687. //digitalWrite(44, HIGH); // Video alignment trigger
  688. delay(50);
  689. digitalWrite(A15, LOW);
  690. //digitalWrite(44, LOW);
  691. time_counter = 0;
  692. global_state.Servo1_pos = 200;
  693. //##########################################################################################################################
  694. }
  695. void failedTrial(){
  696. LANE_ID new_lane_id = global_state.NUM_OF_LANES + 1;
  697. //##################################################### same as easy trial but without assigning a new reward direction
  698. if(global_state.reward_direction == 0){
  699. if(global_state.current_lane == 3){
  700. new_lane_id = 0;
  701. }
  702. else
  703. new_lane_id = global_state.current_lane + 1;
  704. }
  705. if(global_state.reward_direction == 1){
  706. if(global_state.current_lane == 0){
  707. new_lane_id = 3;
  708. }
  709. else
  710. new_lane_id = global_state.current_lane - 1;
  711. }
  712. //######################################################
  713. global_state.reward_lane_id = new_lane_id;
  714. Serial.print("New reward lane is: ");
  715. Serial.println(global_state.reward_lane_id);
  716. Serial.print("New reward rotation direction is: ");
  717. Serial.println(global_state.reward_direction);
  718. global_state.reward_given = false;
  719. }
  720. void createShuffledChoice()
  721. {
  722. if (global_state.GUARANTEED_RANDOM_BOUND % global_state.NUM_OF_LANES != 0)
  723. {
  724. Serial.println("Num of lanes is not divisble by random bound");
  725. }
  726. // Create a list containing the lanes values (repeated in order). This list
  727. // will be shuffled in the next step.
  728. int j_max = global_state.GUARANTEED_RANDOM_BOUND/global_state.NUM_OF_LANES;
  729. for (int i = 0; i < global_state.NUM_OF_LANES; i++)
  730. {
  731. for (int j = 0; j < j_max; j++)
  732. {
  733. size_t index = j + (i*j_max);
  734. global_state.lane_shuffle_list[index] = i;
  735. }
  736. }
  737. int last_lane_value = global_state.reward_lane_id;
  738. // Serial.print("Starting with last lane value: ");
  739. // Serial.println(last_lane_value);
  740. for (int i = 0; i < global_state.GUARANTEED_RANDOM_BOUND; i++)
  741. {
  742. int r_index;
  743. int new_value = global_state.lane_shuffle_list[i];
  744. int count = 0;
  745. bool print = true;
  746. do
  747. {
  748. r_index = random(i, global_state.GUARANTEED_RANDOM_BOUND);
  749. new_value = global_state.lane_shuffle_list[r_index];
  750. // Don't get stuck if it's the last element. Roll backwards
  751. if (count > global_state.GUARANTEED_RANDOM_BOUND - 1)
  752. {
  753. int decrement = global_state.GUARANTEED_RANDOM_BOUND /
  754. global_state.NUM_OF_LANES;
  755. if (decrement < i)
  756. i -= decrement;
  757. else
  758. i = 0;
  759. if (i)
  760. last_lane_value = global_state.lane_shuffle_list[i - 1];
  761. else
  762. last_lane_value = global_state.reward_lane_id;
  763. // Serial.print("Stepped in error handling - ");
  764. // Serial.print("Resetting i to: ");
  765. // Serial.print(i);
  766. // Serial.print(" and last lane value to: ");
  767. // Serial.println(last_lane_value);
  768. count = 0;
  769. }
  770. count++;
  771. }
  772. while (last_lane_value == new_value);
  773. int old_value = global_state.lane_shuffle_list[i];
  774. global_state.lane_shuffle_list[i] = new_value;
  775. global_state.lane_shuffle_list[r_index] = old_value;
  776. last_lane_value = new_value;
  777. }
  778. global_state.shuffle_list_index = 0;
  779. //printShuffleList();
  780. }
  781. void printShuffleList()
  782. {
  783. LANE_ID lane_counter[global_state.NUM_OF_LANES];
  784. for (int i = 0; i < global_state.NUM_OF_LANES; i++)
  785. lane_counter[i] = 0;
  786. Serial.print("[");
  787. for (int i = 0; i < global_state.GUARANTEED_RANDOM_BOUND; i++)
  788. {
  789. Serial.print(global_state.lane_shuffle_list[i] + 1);
  790. if (i !=global_state.GUARANTEED_RANDOM_BOUND - 1)
  791. Serial.print(", ");
  792. lane_counter[global_state.lane_shuffle_list[i]]++;
  793. }
  794. Serial.println("]");
  795. for (int i = 0; i < global_state.NUM_OF_LANES; i++)
  796. {
  797. Serial.print("Count for lane ");
  798. Serial.print(i + 1);
  799. Serial.print(":");
  800. Serial.println(lane_counter[i]);
  801. }
  802. }
  803. void setActuatorTimeout(long int actuator_time_out)
  804. {
  805. if (global_state.actuator_at_max_push)
  806. {
  807. long int time_now = millis();
  808. global_state.motor_timeout_duration = actuator_time_out;
  809. global_state.max_push_current_duration = time_now;
  810. global_state.actuator_duration_activated = true;
  811. }
  812. }
  813. void setupPins()
  814. {
  815. // Solenoid
  816. pinMode(Pins.SolenoidLeft, OUTPUT);
  817. pinMode(Pins.SolenoidRight, OUTPUT);
  818. digitalWrite(Pins.SolenoidLeft, LOW);
  819. digitalWrite(Pins.SolenoidRight, LOW);
  820. // Actuator
  821. pinMode(Pins.ActuatorPush, OUTPUT);
  822. pinMode(Pins.ActuatorPull, OUTPUT);
  823. digitalWrite(Pins.ActuatorPush, LOW);
  824. digitalWrite(Pins.ActuatorPush, LOW);
  825. pinMode(Pins.LaneLight, OUTPUT);
  826. digitalWrite(Pins.LaneLight, LOW);
  827. pinMode(Pins.PeizoTone, OUTPUT);
  828. digitalWrite(Pins.PeizoTone, LOW);
  829. // Initially set all used PWM to OUTPUT and to value Unkown or off (== 0)
  830. #ifdef PWM_ENABLED
  831. for (int i = 2; i <= 13; i++)
  832. {
  833. pinMode(i, OUTPUT);
  834. analogWrite(i, 0);
  835. }
  836. for (int i = 44; i <= 46; i++)
  837. {
  838. pinMode(i, OUTPUT);
  839. analogWrite(i, 0);
  840. }
  841. // temp change for TTL
  842. pinMode(31, OUTPUT);
  843. digitalWrite(31, LOW);
  844. pinMode(32, OUTPUT);
  845. digitalWrite(32, LOW);
  846. #endif
  847. }
  848. void setupLeds()
  849. {
  850. //pinMode(Leds.Solenoid, OUTPUT);
  851. //digitalWrite(Leds.Solenoid, LOW);
  852. // pinMode(Leds.ActuatorPush, OUTPUT);
  853. // digitalWrite(Leds.ActuatorPush, HIGH);
  854. // pinMode(Leds.ActuatorPull, OUTPUT);
  855. // digitalWrite(Leds.ActuatorPull, HIGH);
  856. // pinMode(Leds.Unused, OUTPUT);
  857. // digitalWrite(Leds.Unused, LOW);
  858. pinMode(Leds.SensorLeft, OUTPUT);
  859. digitalWrite(Leds.SensorLeft, LOW);
  860. pinMode(Leds.SensorRight, OUTPUT);
  861. digitalWrite(Leds.SensorRight, LOW);
  862. }
  863. void setupLanes()
  864. {
  865. Lane lane;
  866. lane.lane_id = 0;
  867. if (SINGLE_REWARD)
  868. lane.reward_sensor = sensor.LEFT_ANALOUGE_PIN;
  869. else
  870. lane.reward_sensor = sensor.RIGHT_ANALOUGE_PIN;
  871. lane.region_start_angle = 45;
  872. lane.region_end_angle = 135;
  873. global_state.lanes[lane.lane_id] = lane;
  874. lane.lane_id = 1;
  875. if (SINGLE_REWARD)
  876. lane.reward_sensor = sensor.LEFT_ANALOUGE_PIN;
  877. else
  878. lane.reward_sensor = sensor.LEFT_ANALOUGE_PIN;
  879. lane.region_start_angle = 136;
  880. lane.region_end_angle = -136;
  881. global_state.lanes[lane.lane_id] = lane;
  882. lane.lane_id = 2;
  883. if (SINGLE_REWARD)
  884. lane.reward_sensor = sensor.LEFT_ANALOUGE_PIN;
  885. else
  886. lane.reward_sensor = sensor.LEFT_ANALOUGE_PIN;
  887. lane.region_start_angle = -135;
  888. lane.region_end_angle = -46;
  889. global_state.lanes[lane.lane_id] = lane;
  890. lane.lane_id = 3;
  891. if (SINGLE_REWARD)
  892. lane.reward_sensor = sensor.LEFT_ANALOUGE_PIN;
  893. else
  894. lane.reward_sensor = sensor.RIGHT_ANALOUGE_PIN;
  895. lane.region_start_angle = -45;
  896. lane.region_end_angle = 44;
  897. global_state.lanes[lane.lane_id] = lane;
  898. }
  899. bool isWithinRewardLaneAngle(SubjectLocation subject_location)
  900. {
  901. if (!subject_location.block_detected)
  902. {
  903. //Serial.println("Current location: No block detected");
  904. return false;
  905. }
  906. for (int j = 0; j < global_state.NUM_OF_LANES; j++)
  907. {
  908. // Sometimes the angle jumps forward and backwards between the current
  909. // lane and the next lane. So we need to give a bit of extra buffer to
  910. // the current until we are sure it's in the next lane. To do so, start
  911. // looping from the current lane;
  912. int i = (global_state.current_lane + j) % global_state.NUM_OF_LANES;
  913. int flexible_range = 0;
  914. if (i == global_state.current_lane)
  915. flexible_range = 10;
  916. Lane lane = global_state.lanes[i];
  917. int start = lane.region_start_angle - flexible_range;
  918. int end = lane.region_end_angle + flexible_range;
  919. int angle = subject_location.angle;
  920. if (end < start)
  921. end += 360;
  922. if (angle < start)
  923. angle += 360;
  924. if (start <= angle && angle <= end)
  925. {
  926. global_state.current_lane = i;
  927. //Serial.println(global_state.reward_direction);
  928. //Serial.print(" ");
  929. //Serial.println(angle);
  930. if (global_state.last_reported_lane != i)
  931. {
  932. switch(i){ //Here it is decided if the animal went right(0) or left(1)
  933. case 0 :
  934. if(global_state.last_reported_lane == 1)
  935. global_state.rotation = 1;
  936. else if(global_state.last_reported_lane == 3)
  937. global_state.rotation = 0;
  938. else
  939. global_state.rotation = -1;
  940. break;
  941. case 1 :
  942. if(global_state.last_reported_lane == 2)
  943. global_state.rotation = 1;
  944. else if(global_state.last_reported_lane == 0)
  945. global_state.rotation = 0;
  946. else
  947. global_state.rotation = -1;
  948. break;
  949. case 2 :
  950. if(global_state.last_reported_lane == 3)
  951. global_state.rotation = 1;
  952. else if(global_state.last_reported_lane == 1)
  953. global_state.rotation = 0;
  954. else
  955. global_state.rotation = -1;
  956. break;
  957. case 3 :
  958. if(global_state.last_reported_lane == 0)
  959. global_state.rotation = 1;
  960. else if(global_state.last_reported_lane == 2)
  961. global_state.rotation = 0;
  962. else
  963. global_state.rotation = -1;
  964. break;
  965. }
  966. if(global_state.rotation != global_state.reward_direction){
  967. failedTrial();
  968. Serial.println("Trial has been failed");
  969. }
  970. writeStats(Stats.ENTERED_LANE_RANGE(i));
  971. global_state.last_reported_lane = i;
  972. }
  973. //Serial.print(lane.lane_id); //Debug stuff
  974. //Serial.print(global_state.rotation);
  975. //Serial.println(global_state.was_inside_lane);
  976. if (lane.lane_id == global_state.reward_lane_id && global_state.rotation == global_state.reward_direction)
  977. {
  978. digitalWrite(Pins.LaneLight, LOW);
  979. digitalWrite(Pins.LaneLight2, LOW);
  980. myservo1.write(10);
  981. if (global_state.last_reported_light_status != LOW)
  982. {
  983. writeStats(Stats.LIGHT_OFF());
  984. global_state.last_reported_light_status = LOW;
  985. }
  986. return true;
  987. }
  988. else if(global_state.reward_direction == 0){
  989. digitalWrite(Pins.LaneLight, LOW);
  990. digitalWrite(Pins.LaneLight2, HIGH);
  991. return false;
  992. }
  993. else if(global_state.reward_direction == 1){
  994. digitalWrite(Pins.LaneLight2, LOW);
  995. digitalWrite(Pins.LaneLight, HIGH);
  996. return false;
  997. }
  998. else
  999. {
  1000. if (global_state.last_reported_light_status != HIGH)
  1001. {
  1002. writeStats(Stats.LIGHT_ON());
  1003. global_state.last_reported_light_status = HIGH;
  1004. }
  1005. return false;
  1006. }
  1007. }
  1008. }
  1009. Serial.print("Unexpected code redirection. Subject location angle: ");
  1010. Serial.println(subject_location.angle);
  1011. return false;
  1012. }
  1013. void move_training_servos(int angle, bool is_within_reward_lane_angle){
  1014. if(global_state.reward_direction == 0){
  1015. if(inRange(angle, -120,-90) || inRange(angle, -30,0) || inRange(angle, 60,80) || inRange(angle, 160,180)){
  1016. myservoleft.write(170);
  1017. if(is_within_reward_lane_angle){
  1018. myservoright.write(20);
  1019. }
  1020. }
  1021. else{
  1022. myservoleft.write(20);
  1023. myservoright.write(170);
  1024. }
  1025. }
  1026. else{
  1027. if(inRange(angle, -120,-90) || inRange(angle, -30,0) || inRange(angle, 60,80) || inRange(angle, 160,180)){
  1028. myservoright.write(20);
  1029. if(is_within_reward_lane_angle){
  1030. myservoleft.write(170);
  1031. }
  1032. }
  1033. else{
  1034. myservoleft.write(20);
  1035. myservoright.write(170);
  1036. }
  1037. }
  1038. }
  1039. bool inRange(int val, int minimum, int maximum)
  1040. {
  1041. return ((minimum <= val) && (val <= maximum));
  1042. }
  1043. void printRewardLane()
  1044. {
  1045. // Already printed by the stats
  1046. //Lane reward_lane = global_state.lanes[global_state.reward_lane_id];
  1047. //Serial.print("Next reward lane id: ");
  1048. //Serial.println(reward_lane.lane_id);
  1049. }
  1050. MotorDurationEntry* turnOnMotor(PIN_TYPE motor_id, long int activation_period)
  1051. {
  1052. long int time_now = millis();
  1053. for(int i = 0; i < global_state.MOTOR_DURATION_ENTERIES_SIZE; i++)
  1054. {
  1055. MotorDurationEntry* motor_entry = &global_state.motor_duration_entries[i];
  1056. if (!motor_entry->activated)
  1057. {
  1058. do
  1059. {
  1060. pinMode(motor_id, OUTPUT);
  1061. digitalWrite(motor_id, HIGH);
  1062. // if (motor_id == 31 || motor_id == 32)
  1063. // pinMode(motor_id, INPUT);
  1064. }
  1065. while (!digitalRead(motor_id));
  1066. pinMode(motor_id, OUTPUT);
  1067. motor_entry->activated = true;
  1068. motor_entry->motor_id = motor_id;
  1069. motor_entry->activation_time = time_now;
  1070. motor_entry->timeout_period = activation_period;
  1071. Serial.print("Setting high on pin: ");
  1072. Serial.println(motor_id);
  1073. return motor_entry;
  1074. }
  1075. }
  1076. Serial.print("Didn't find a non-empty motor slot to turn on motor pin: ");
  1077. Serial.println(motor_id);
  1078. return NULL;
  1079. }
  1080. void turnOffMotor()
  1081. {
  1082. long int time_now = millis();
  1083. for(int i = 0; i < global_state.MOTOR_DURATION_ENTERIES_SIZE; i++)
  1084. {
  1085. MotorDurationEntry* motor_entry = &global_state.motor_duration_entries[i];
  1086. if (motor_entry->activated)
  1087. {
  1088. if (motor_entry->activation_time + motor_entry->timeout_period < time_now)
  1089. {
  1090. do
  1091. {
  1092. Serial.print("Turning off pin: ");
  1093. Serial.println(motor_entry->motor_id);
  1094. pinMode(motor_entry->motor_id, OUTPUT);
  1095. digitalWrite(motor_entry->motor_id, LOW);
  1096. // if (motor_entry->motor_id == 31 || motor_entry->motor_id == 32)
  1097. // pinMode(motor_entry->motor_id, INPUT);
  1098. }
  1099. while (digitalRead(motor_entry->motor_id));
  1100. pinMode(motor_entry->motor_id, OUTPUT);
  1101. motor_entry->activated = false;
  1102. Serial.print("Turned off pin: ");
  1103. Serial.println(motor_entry->motor_id);
  1104. if (motor_entry->motor_id == Pins.SolenoidRight)
  1105. writeStats(Stats.SOLENOID_RIGHT_OFF());
  1106. else if (motor_entry->motor_id == Pins.SolenoidLeft)
  1107. writeStats(Stats.SOLENOID_LEFT_OFF());
  1108. }
  1109. }
  1110. }
  1111. }
  1112. void writeStats(StatsMessage stat)
  1113. {
  1114. long int time_now = millis();
  1115. Serial.print("s:");
  1116. Serial.print(time_now);
  1117. Serial.print("\\id:");
  1118. Serial.print(stat.event_id);
  1119. Serial.print("\\msg:");
  1120. Serial.print(stat.msg);
  1121. if (stat.parameter != -1)
  1122. {
  1123. Serial.print("\\parameter:");
  1124. Serial.print(stat.parameter);
  1125. }
  1126. Serial.println("");
  1127. Serial.flush();
  1128. }
  1129. Maus calculateVelocity(Maus maus, int newX, int newY, int angle){
  1130. maus.posx[maus.index] = newX;
  1131. maus.posy[maus.index] = newY;
  1132. //maus.angle[maus.index] = sin(angle)*1000;
  1133. //Serial.println((sin((angle/57.296))+1)*120);
  1134. float mean[2] = {0.0,0.0};
  1135. //float mean_angle = 0;
  1136. for(int i = 0; i<10 ; i++){
  1137. mean[0] += maus.posx[i];
  1138. mean[1] += maus.posy[i];
  1139. //mean_angle += maus.angle[i];
  1140. //Serial.print(maus.angle[i]);
  1141. //Serial.print(", ");
  1142. }
  1143. //Serial.println();
  1144. mean[0] /= 10;
  1145. mean[1] /= 10;
  1146. //maus.mean_angle = asin(mean_angle/10);
  1147. //Serial.println(maus.mean_angle);
  1148. float diff = sqrt(sq(mean[0] - maus.pos_old[0]) + sq(mean[1] - maus.pos_old[1]) );
  1149. maus.difference[0] = mean[0]-maus.pos_old[0];
  1150. maus.difference[1] = mean[1]-maus.pos_old[1];
  1151. if(diff<0.2){
  1152. diff=0;
  1153. }
  1154. //Serial.println(maus.difference[1]);
  1155. // Optogenetic pulse randomly every 7 occurences
  1156. if(maus.difference[1]>2 && newY > threshold - 5 && newY < threshold + 5){
  1157. int random_number = random(100);
  1158. Serial.println(random_number);
  1159. if(random_number > 70 && opto_activation_trial != global_state.trial_number){
  1160. digitalWrite(A13, HIGH);
  1161. Serial.println("Opto-activation)");
  1162. opto_activation_trial = global_state.trial_number;
  1163. }
  1164. }
  1165. else{
  1166. digitalWrite(A13, LOW);
  1167. }
  1168. /*
  1169. * if(maus.optogenetics == false &&
  1170. if(mean[1] < threshold){
  1171. maus.below_threshold = true;
  1172. }
  1173. else{
  1174. maus.below_threshold = false;
  1175. }
  1176. */
  1177. analogWrite(13, (sin((angle/57.296))+1)*120);
  1178. //Serial.println(maus.mean_angle);
  1179. //Serial.println(diff);
  1180. //Serial.println(maus.difference[0]);
  1181. //Serial.println((maus.difference[1]+14)*9);
  1182. //analogWrite(9, (maus.difference[1]+14)*9); //uncomment for platform velocity
  1183. analogWrite(9, mean[1]);
  1184. /*
  1185. Serial.print(" = sqrt(");
  1186. Serial.print(mean[0]);
  1187. Serial.print(" - ");
  1188. Serial.print(maus.pos_old[0]);
  1189. Serial.print(")^2 + (");
  1190. Serial.print(mean[1]);
  1191. Serial.print(" - ");
  1192. Serial.print(maus.pos_old[1]);
  1193. Serial.print(")^2");
  1194. Serial.print(")");
  1195. Serial.println();
  1196. */
  1197. maus.velocity = diff;
  1198. maus.pos_old[0] = mean[0];
  1199. maus.pos_old[1] = mean[1];
  1200. if(maus.index<9){
  1201. maus.index++;
  1202. }
  1203. else{
  1204. maus.index = 0;
  1205. }
  1206. return maus;
  1207. }