From d63b50b4ac95c16487c58d73881c5f86dbd579d4 Mon Sep 17 00:00:00 2001 From: crasgaitis Date: Thu, 27 Oct 2022 17:30:43 -0700 Subject: [PATCH 01/10] cursor initial pos at bottom --- built_in_tasks/target_tracking_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/built_in_tasks/target_tracking_task.py b/built_in_tasks/target_tracking_task.py index 52b348b4..5def2b07 100644 --- a/built_in_tasks/target_tracking_task.py +++ b/built_in_tasks/target_tracking_task.py @@ -315,7 +315,7 @@ class ScreenTargetTracking(TargetTracking, Window): cursor_radius = traits.Float(.5, desc='Radius of cursor in cm') cursor_color = traits.OptionsList("pink", *target_colors, desc='Color of cursor endpoint', bmi3d_input_options=list(target_colors.keys())) cursor_bounds = traits.Tuple((-10., 10., 0., 0., -10., 10.), desc='(x min, x max, y min, y max, z min, z max)') - starting_pos = traits.Tuple((5., 0., 5.), desc='Where to initialize the cursor') + starting_pos = traits.Tuple((5., -8., 5.), desc='Where to initialize the cursor') fps = traits.Float(60, desc="Rate at which the FSM is called in Hz") # originally set by class Experiment def __init__(self, *args, **kwargs): From ed261508fa32b79ab6ba2974610ec5e8d416419c Mon Sep 17 00:00:00 2001 From: crasgaitis Date: Tue, 1 Nov 2022 12:16:32 -0700 Subject: [PATCH 02/10] test cursor starting pos --- built_in_tasks/target_tracking_task.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/built_in_tasks/target_tracking_task.py b/built_in_tasks/target_tracking_task.py index 5def2b07..206a34f2 100644 --- a/built_in_tasks/target_tracking_task.py +++ b/built_in_tasks/target_tracking_task.py @@ -315,7 +315,7 @@ class ScreenTargetTracking(TargetTracking, Window): cursor_radius = traits.Float(.5, desc='Radius of cursor in cm') cursor_color = traits.OptionsList("pink", *target_colors, desc='Color of cursor endpoint', bmi3d_input_options=list(target_colors.keys())) cursor_bounds = traits.Tuple((-10., 10., 0., 0., -10., 10.), desc='(x min, x max, y min, y max, z min, z max)') - starting_pos = traits.Tuple((5., -8., 5.), desc='Where to initialize the cursor') + starting_pos = traits.Tuple((5., 5., -8.), desc='Where to initialize the cursor') fps = traits.Float(60, desc="Rate at which the FSM is called in Hz") # originally set by class Experiment def __init__(self, *args, **kwargs): @@ -551,6 +551,7 @@ def _while_tracking_in(self): # Check if the trial is over and there are no more target frames to display if self.frame_index+self.lookahead >= np.shape(self.targs)[0]: self.trial_timed_out = True + def _start_tracking_out(self): super()._start_tracking_out() @@ -577,6 +578,7 @@ def _while_tracking_out(self): # Check if the trial is over and there are no more target frames to display if self.frame_index+self.lookahead >= np.shape(self.targs)[0]: + cursor_pos = [5., 5., -8.] self.trial_timed_out = True def _start_timeout_penalty(self): From 17b4c0c0a0c066db351f09503c25fc29541eadfc Mon Sep 17 00:00:00 2001 From: leo Date: Tue, 1 Nov 2022 12:42:37 -0700 Subject: [PATCH 03/10] add booted server --- db/runserver.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/db/runserver.sh b/db/runserver.sh index f866c1f0..41664df1 100755 --- a/db/runserver.sh +++ b/db/runserver.sh @@ -11,6 +11,11 @@ elif [ "$HOST" = "pagaiisland-surface" ]; then export DISPLAY=localhost:0 export LIBGL_ALWAYS_INDIRECT=0 echo "success" +elif [ "$HOST" = "booted-server" ]; then + export DISPLAY=':1' + Xvnc :1 -securityTypes None -geometry 1920x1080 -nocursor & + eval "$(conda shell.bash hook)" + conda activate bmi3d fi # Find the BMI3D directory @@ -88,8 +93,6 @@ git --git-dir=$BMI3D/.git --work-tree=$BMI3D status >> $BMI3D/log/runserver_log # Activate the relevant environment if test -f "$BMI3D/env/bin/activate"; then source $BMI3D/env/bin/activate -else - echo "No environment found." fi trap "exit" INT TERM ERR @@ -111,3 +114,5 @@ if [ "$HOST" = "pagaiisland2" ]; then fi wait + +sleep 10 \ No newline at end of file From e64afeeaa950d8490b1bd347042b1df267158d09 Mon Sep 17 00:00:00 2001 From: leo Date: Wed, 23 Nov 2022 14:33:44 -0800 Subject: [PATCH 04/10] add option to reset cursor postion --- built_in_tasks/target_tracking_task.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/built_in_tasks/target_tracking_task.py b/built_in_tasks/target_tracking_task.py index 206a34f2..a8797898 100644 --- a/built_in_tasks/target_tracking_task.py +++ b/built_in_tasks/target_tracking_task.py @@ -316,6 +316,7 @@ class ScreenTargetTracking(TargetTracking, Window): cursor_color = traits.OptionsList("pink", *target_colors, desc='Color of cursor endpoint', bmi3d_input_options=list(target_colors.keys())) cursor_bounds = traits.Tuple((-10., 10., 0., 0., -10., 10.), desc='(x min, x max, y min, y max, z min, z max)') starting_pos = traits.Tuple((5., 5., -8.), desc='Where to initialize the cursor') + reset = traits.Bool(False, desc='reset the cursor position to start position on every trial') fps = traits.Float(60, desc="Rate at which the FSM is called in Hz") # originally set by class Experiment def __init__(self, *args, **kwargs): @@ -468,6 +469,10 @@ def _start_wait(self): self.target.hide() self.trajectory.hide() + if self.reset: + self.plant.set_endpoint_pos(self.starting_pos) + self.hdf.sendMsg("reset") + def _while_wait(self): super()._while_wait() # Add disturbance From 94da49045978aa29d2001c85cd25b2414f799aeb Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 6 Jan 2023 14:51:44 -0800 Subject: [PATCH 05/10] fix broken reports --- db/tracker/models.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/db/tracker/models.py b/db/tracker/models.py index 9df6c546..ac62044b 100644 --- a/db/tracker/models.py +++ b/db/tracker/models.py @@ -1075,7 +1075,13 @@ def desc(self): params = json_param.Parameters(self.params) if self.report is not None and len(self.report) > 0: - report_data = json.loads(self.report) + try: + report_data = json.loads(self.report) + except: + print(self.id) + print("THIS TE IS BROKEN <--") + report_data = None + else: report_data = None try: From 591ae3add8d23dd29b1f129a12486fc79beb4d9b Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 4 Aug 2023 15:20:36 -0700 Subject: [PATCH 06/10] remove reset --- built_in_tasks/target_tracking_task.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/built_in_tasks/target_tracking_task.py b/built_in_tasks/target_tracking_task.py index 6eab5432..3876d0b4 100644 --- a/built_in_tasks/target_tracking_task.py +++ b/built_in_tasks/target_tracking_task.py @@ -527,10 +527,6 @@ def _start_wait(self): self.target.hide() self.trajectory.hide() - if self.reset: - self.plant.set_endpoint_pos(self.starting_pos) - self.hdf.sendMsg("reset") - def _while_wait(self): super()._while_wait() # # Add disturbance From 249aa349f94d5b9651ffaf9ae2daaabb4d03b42a Mon Sep 17 00:00:00 2001 From: resatomo Date: Fri, 4 Aug 2023 15:50:31 -0700 Subject: [PATCH 07/10] to do to set up 2 little tablets --- db/html/static/resources/js/report.js | 2 +- db/runserver.sh | 9 +++++---- db/tracker/websocket.py | 6 +++++- riglib/tablet_reward.py | 8 ++++++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/db/html/static/resources/js/report.js b/db/html/static/resources/js/report.js index 3344091e..427ef3c4 100644 --- a/db/html/static/resources/js/report.js +++ b/db/html/static/resources/js/report.js @@ -43,7 +43,7 @@ Report.prototype.activate = function() { //var on_windows = window.navigator.userAgent.indexOf("Windows") > 0; if (!this.ws) { // && !on_windows) { some websocket issues on windows.. // Create a new JS WebSocket object - this.ws = new WebSocket("ws://"+hostname.split(":")[0]+":8001/connect"); + this.ws = new WebSocket("ws://"+hostname.split(":")[0]+":8001/connect"); // hostname.split(":")[1] + 1 (end of hostname should be either 8000 or 8002) this.ws.onmessage = function(evt) { debug(evt.data); diff --git a/db/runserver.sh b/db/runserver.sh index 41664df1..5c8a4959 100755 --- a/db/runserver.sh +++ b/db/runserver.sh @@ -11,11 +11,12 @@ elif [ "$HOST" = "pagaiisland-surface" ]; then export DISPLAY=localhost:0 export LIBGL_ALWAYS_INDIRECT=0 echo "success" -elif [ "$HOST" = "booted-server" ]; then +elif [ "$HOST" = "booted-server" ]; then # this is Churro computer + # set DISPLAY var based on BMI3D_PORT var export DISPLAY=':1' - Xvnc :1 -securityTypes None -geometry 1920x1080 -nocursor & + Xvnc :1 -securityTypes None -geometry 1920x1080 -nocursor & # replace :1 with DISPLAY eval "$(conda shell.bash hook)" - conda activate bmi3d + conda activate bmi3d # may not work? remove? fi # Find the BMI3D directory @@ -100,7 +101,7 @@ trap "kill 0" EXIT # Start python processes cd $BMI3D -python manage.py runserver 0.0.0.0:8000 --noreload & +python manage.py runserver 0.0.0.0:8000 --noreload & # if BMI3D_PORT exists, use it instead if [ "$HOST" = "pagaiisland2" ]; then celery -A db.tracker worker -l INFO & fi diff --git a/db/tracker/websocket.py b/db/tracker/websocket.py index ab3c91cc..9c7d3f5b 100644 --- a/db/tracker/websocket.py +++ b/db/tracker/websocket.py @@ -70,8 +70,12 @@ def run(self): import asyncio asyncio.set_event_loop(asyncio.new_event_loop()) + + if os.environ.get('BMI3D_PORT') is not None: # set BMI3D_PORT to 8000 or 8002 in two separate shortcut scripts, one per tablet + application.listen(int(os.environ.get('BMI3D_PORT')) + 1) + else: + application.listen(8001) - application.listen(8001) self.ioloop = tornado.ioloop.IOLoop.current() self.ioloop.add_handler(self._pipe, self._send, self.ioloop.READ) self.ioloop.add_handler(self._outp, self._stdout, self.ioloop.READ) diff --git a/riglib/tablet_reward.py b/riglib/tablet_reward.py index df2d0d5a..65ef641a 100644 --- a/riglib/tablet_reward.py +++ b/riglib/tablet_reward.py @@ -40,9 +40,13 @@ def open(): import requests class RemoteReward(): - def __init__(self): + def __init__(self, tablet_name): + + if tablet_name == 'booted-1': + self.hostName = "192.168.0.200" + elif tablet_name == 'booted-2': + # set self.hostName according to little tablet 2 address on router, maybe need to ask Leo/Drew to set? - self.hostName = "192.168.0.200" self.serverPort = 8080 def trigger(self): From b06607f40e9fff15fad37039be28313a9704bff3 Mon Sep 17 00:00:00 2001 From: resatomo Date: Fri, 4 Aug 2023 15:53:26 -0700 Subject: [PATCH 08/10] Merge branch 'tablet-target-training' of github.com:aolabNeuro/brain-python-interface into tablet-target-training --- .gitignore | 1 + built_in_tasks/bmimultitasks.py | 1 + built_in_tasks/manualcontrolmultitasks.py | 8 ++-- built_in_tasks/target_capture_task.py | 42 +++++++++++++++++++- built_in_tasks/target_tracking_task.py | 34 +++------------- db/db_settings.py | 48 ++++++++++++++++++----- features/__init__.py | 3 +- riglib/gpio.py | 46 ++++++++++++++++++---- tests/test_tasks.py | 15 ++++++- 9 files changed, 143 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index b235a928..54e9bbbb 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,4 @@ riglib/plexon/Plexfile_utilities.egg-info/* *.dot riglib/dio/nidaq/pcidio.py db/settings.py +db/secrets.json diff --git a/built_in_tasks/bmimultitasks.py b/built_in_tasks/bmimultitasks.py index ac431ec3..9b09d5cb 100644 --- a/built_in_tasks/bmimultitasks.py +++ b/built_in_tasks/bmimultitasks.py @@ -200,6 +200,7 @@ class BMIControlMultiMixin(BMILoop, LinearlyDecreasingAssist): reset = traits.Int(0, desc='reset the decoder state to the starting configuration') cursor_color = traits.OptionsList("orange", *target_colors, desc='Color of cursor endpoint', bmi3d_input_options=list(target_colors.keys())) + static_states = ['reward'] ordered_traits = ['session_length', 'assist_level', 'assist_level_time', 'reward_time','timeout_time','timeout_penalty_time'] exclude_parent_traits = ['marker_count', 'marker_num', 'goal_cache_block'] diff --git a/built_in_tasks/manualcontrolmultitasks.py b/built_in_tasks/manualcontrolmultitasks.py index 579c3e69..ae69a1da 100644 --- a/built_in_tasks/manualcontrolmultitasks.py +++ b/built_in_tasks/manualcontrolmultitasks.py @@ -14,10 +14,10 @@ rotations = dict( - yzx = np.array( - [[0, 1, 0, 0], - [0, 0, 1, 0], - [1, 0, 0, 0], + yzx = np.array( # names come from rows (optitrack), but screen coords come from columns: + [[0, 1, 0, 0], # x goes into second column (y-coordinate, coming out of screen) + [0, 0, 1, 0], # y goes into third column (z-coordinate, up) + [1, 0, 0, 0], # z goes into first column (x-coordinate, right) [0, 0, 0, 1]] ), zyx = np.array( diff --git a/built_in_tasks/target_capture_task.py b/built_in_tasks/target_capture_task.py index a564e371..7d5f33f8 100644 --- a/built_in_tasks/target_capture_task.py +++ b/built_in_tasks/target_capture_task.py @@ -268,7 +268,7 @@ class ScreenTargetCapture(TargetCapture, Window): limit2d = traits.Bool(True, desc="Limit cursor movement to 2D") sequence_generators = [ - 'out_2D', 'centerout_2D', 'centeroutback_2D', 'rand_target_chain_2D', 'rand_target_chain_3D', + 'out_2D', 'centerout_2D', 'centeroutback_2D', 'rand_target_chain_2D', 'rand_target_chain_3D', 'corners_2D', ] hidden_traits = ['cursor_color', 'target_color', 'cursor_bounds', 'cursor_radius', 'plant_hide_rate', 'starting_pos'] @@ -634,6 +634,46 @@ def rand_target_chain_3D(ntrials=100, chain_length=1, boundaries=(-12,12,-10,10, yield idx+np.arange(chain_length), pts idx += chain_length + @staticmethod + def corners_2D(nblocks=5, chain_length=1, corners=(-8,8,-8,8)): + ''' + Generates a sequence of 2D (x and z) targets at the given 4 corners + + Parameters + ---------- + nblocks : 3-tuple + Number of blocks + chain_length : int + The number of targets in each chain before a reward is given + corners : 4-tuple + Location of the edges of the screen (-x, x, -y, y) + + Returns + ------- + [nblocks*4 x 1] array of tuples containing trial indices and [1 x 3] target coordinates + + ''' + ntargets = 4 + corners = np.array([ + [corners[0], 0, corners[2]], + [corners[0], 0, corners[3]], + [corners[1], 0, corners[2]], + [corners[1], 0, corners[3]] + ]) + target_order = [] + rng = np.random.default_rng() + for _ in range(nblocks): + order = np.arange(ntargets) + 1 # target indices, starting from 1 + rng.shuffle(order) + target_order = np.concatenate((target_order, order), axis=0) + + # Spit out trials in groups of chain_length + ntrials = nblocks*4//chain_length + for t in range(ntrials): + idx = target_order[int(t*chain_length):int(t*chain_length+chain_length)] + pos = [corners[int(i-1),:] for i in idx] + yield idx, pos + class ScreenReachAngle(ScreenTargetCapture): ''' A modified task that requires the cursor to move in the right direction towards the target, diff --git a/built_in_tasks/target_tracking_task.py b/built_in_tasks/target_tracking_task.py index b9e3353c..e65b152b 100644 --- a/built_in_tasks/target_tracking_task.py +++ b/built_in_tasks/target_tracking_task.py @@ -341,6 +341,7 @@ class ScreenTargetTracking(TargetTracking, Window): cursor_color = traits.OptionsList("pink", *target_colors, desc='Color of cursor endpoint', bmi3d_input_options=list(target_colors.keys())) cursor_bounds = traits.Tuple((-10., 10., 0., 0., -10., 10.), desc='(x min, x max, y min, y max, z min, z max)') starting_pos = traits.Tuple((5., 5., -8.), desc='Where to initialize the cursor') + reset = traits.Bool(False, desc='reset the cursor position to start position on every trial') fps = traits.Float(60, desc="Rate at which the FSM is called in Hz") # originally set by class Experiment def __init__(self, *args, **kwargs): @@ -437,15 +438,6 @@ def run(self): self.plant.stop() ##### HELPER AND UPDATE FUNCTIONS #### - # def tracking_in_reward(self): - # self.reward.on() - # print('REWARD ON') - # start_manual_reward = time.time() - # while (time.time() - start_manual_reward) <= self.manual_reward_time: - # continue - # self.reward.off() - # print('REWARD OFF') - def update_plant_visibility(self): ''' Update plant visibility''' if self.plant_visible != self.plant_vis_prev: @@ -459,10 +451,6 @@ def update_frame(self): self.trajectory.show() self.frame_index +=1 - def cleanup(self, database, saveid, **kwargs): - self.reward.off() - super().cleanup(database, saveid, **kwargs) - #### TEST FUNCTIONS #### def _test_enter_target(self, time_in_state): ''' @@ -517,6 +505,10 @@ def _start_wait(self): self.target.hide() self.trajectory.hide() + if self.reset: + self.plant.set_endpoint_pos(self.starting_pos) + self.hdf.sendMsg("reset") + def _while_wait(self): super()._while_wait() # # Add disturbance @@ -629,9 +621,6 @@ def _start_tracking_in(self): self.limit1d = self.original_limit1d # Cue successful tracking self.target.cue_trial_end_success() - self.trigger_reward = False - self.reward.off() - self.start_time = 0 def _while_tracking_in(self): super()._while_tracking_in() @@ -654,25 +643,12 @@ def _while_tracking_in(self): self.trial_timed_out = True - # Give reward for tracking in - curr_time = self.frame_index/self.fps - if curr_time % self.tracking_reward_interval==0 and self.trigger_reward==False: - self.trigger_reward = True - self.start_time = curr_time - self.reward.on() - print('REWARD ON') - if curr_time - self.start_time > self.tracking_reward_time and self.trigger_reward==True: - self.trigger_reward = False - self.reward.off() - print('REWARD OFF') - def _start_tracking_out(self): super()._start_tracking_out() # print('STOP TRACKING') self.sync_event('CURSOR_LEAVE_TARGET') # Reset target color self.target.reset() - self.reward.off() def _while_tracking_out(self): super()._while_tracking_out() diff --git a/db/db_settings.py b/db/db_settings.py index ecb9597e..c7a50403 100644 --- a/db/db_settings.py +++ b/db/db_settings.py @@ -7,6 +7,9 @@ import glob import re import socket +import json +from django.core.exceptions import ImproperlyConfigured + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) HOSTNAME = socket.gethostname() @@ -37,19 +40,45 @@ def get_sqlite3_databases(): return dbs -DATABASES = get_sqlite3_databases() -#DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'db.sql', } } -if HOSTNAME in ['pagaiisland2', 'moor', 'crab-eating', 'ecube']: - DATABASES['default'] = { +# DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'db.sql', } } +DATABASES = {} + +def get_secret(setting): + """Get secret setting or fail with ImproperlyConfigured""" + try: + with open(os.path.join(BASE_DIR, 'secrets.json')) as secrets_file: + secrets = json.load(secrets_file) + return secrets[setting] + except KeyError: + raise ImproperlyConfigured("Set the {} setting".format(setting)) + +def get_mysql_database(dbname): + return { 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'rig1', - 'HOST': 'moor.ece.uw.edu', - 'PORT': '3306', - 'USER': 'aolab', - 'PASSWORD': 'Securepassword1@#' # Very secure wow + 'NAME': dbname, + 'HOST': get_secret("DB_HOST"), + 'PORT': get_secret("DB_PORT"), + 'USER': get_secret("DB_USER"), + 'PASSWORD': get_secret("DB_PASSWORD") } +if HOSTNAME in ['pagaiisland2']: + DATABASES['default'] = get_mysql_database('rig1') + CELERY_BROKER_URL = f'amqp://{get_secret("AMQP_USER")}:{get_secret("AMQP_PASSWORD")}@{get_secret("AMQP_HOST")}:{get_secret("AMQP_PORT")}//' +elif HOSTNAME in ['siberut-bmi']: + DATABASES['default'] = get_mysql_database('rig2') + CELERY_BROKER_URL = f'amqp://{get_secret("AMQP_USER")}:{get_secret("AMQP_PASSWORD")}@{get_secret("AMQP_HOST")}:{get_secret("AMQP_PORT")}//' +elif HOSTNAME in ['booted-server']: + DATABASES['default'] = get_mysql_database('tablet') +elif HOSTNAME in ['moor', 'crab-eating', 'ecube']: + DATABASES['rig1'] = get_mysql_database('rig1') + DATABASES['rig2'] = get_mysql_database('rig2') + DATABASES['tablet'] = get_mysql_database('tablet') + DATABASES['default'] = DATABASES['rig1'] +else: + DATABASES = get_sqlite3_databases() + # Django settings for db project. DEBUG = True TEMPLATE_DEBUG = DEBUG @@ -191,5 +220,4 @@ def get_sqlite3_databases(): APPEND_SLASH=False ALLOWED_HOSTS = ['*'] #['127.0.0.1', 'localhost', "testserver"] -CELERY_BROKER_URL = 'amqp://bmi3d:verysecurel0l@moor.ece.uw.edu:5672//' diff --git a/features/__init__.py b/features/__init__.py index 449517a5..23217e8d 100644 --- a/features/__init__.py +++ b/features/__init__.py @@ -8,7 +8,7 @@ from riglib.stereo_opengl.window import WindowWithExperimenterDisplay, Window2D from .generator_features import Autostart, AdaptiveGenerator, IgnoreCorrectness, PoissonWait from .peripheral_device_features import Button, Joystick, DualJoystick, Joystick_plus_TouchSensor, KeyboardControl, MouseControl -from .reward_features import RewardSystem, TTLReward, JuiceLogging, PelletReward, JackpotRewards, ProgressBar +from .reward_features import RewardSystem, TTLReward, JuiceLogging, PelletReward, JackpotRewards, ProgressBar, TrackingRewards from .eyetracker_features import EyeData, CalibratedEyeData, SimulatedEyeData, FixationStart from .phasespace_features import MotionData, MotionSimulate, MotionAutoAlign from .optitrack_features import Optitrack @@ -67,6 +67,7 @@ screen_sync=ScreenSync, cursor_sync=CursorAnalogOut, progress_bar=ProgressBar, + tracking_rewards=TrackingRewards ) # >>> features.built_in_features['autostart'].__module__ diff --git a/riglib/gpio.py b/riglib/gpio.py index 29ec7df4..82d141da 100644 --- a/riglib/gpio.py +++ b/riglib/gpio.py @@ -8,6 +8,7 @@ import time import pyfirmata from serial.serialutil import SerialException +import serial def convert_masked_data_to_pins(mask, data, bits=64): ''' Helper to take a mask and some data and turn it into a list of pins and values''' @@ -59,6 +60,29 @@ def write_many(self, mask, data): def read(self, pin): return self.value[pin,-1] +class CustomBoard(pyfirmata.Board): + + default_layout = pyfirmata.boards.BOARDS['arduino'] + wait_time = 1 + + def __init__(self, port, layout=None, baudrate=112500, name=None, timeout=None): + self.sp = serial.Serial(port, baudrate, timeout=timeout) + self.pass_time(self.wait_time) + self.name = name + self._layout = layout + if not self.name: + self.name = port + + if layout: + self.setup_layout(layout) + else: + layout = self.default_layout + self.setup_layout(layout) + + # Iterate over the first messages to get firmware data + while self.bytes_available(): + self.iterate() + class ArduinoGPIO(GPIO): ''' Pin-addressable arduino serial interface''' def __init__(self, port=None, baudrate=57600, timeout=10): @@ -70,7 +94,7 @@ def __init__(self, port=None, baudrate=57600, timeout=10): port = p.device if port is None: raise Exception('No serial device found') - self.board = pyfirmata.Arduino(port, baudrate=baudrate, timeout=timeout) + self.board = CustomBoard(port, baudrate=baudrate, timeout=timeout) self.lock = Lock() def write(self, pin, value): @@ -92,6 +116,18 @@ def close(self): ''' Call this method before destroying the object''' self.board.exit() +class TeensyBoard(CustomBoard): + + default_layout = { + 'digital': tuple(x for x in range(63)), + 'pwm': tuple(x for x in range(63)), + 'analog': tuple(x for x in range(43, 69)), + 'disabled': (0,1), + } + wait_time = 0.5 # Teensy is much faster than arduino with hardware Serial port, so we + # don't have to wait so long. + + class TeensyGPIO(ArduinoGPIO): def __init__(self, port=None, baudrate=112500, timeout=10): @@ -103,13 +139,7 @@ def __init__(self, port=None, baudrate=112500, timeout=10): port = p.device if port is None: raise Exception('No serial device found') - teensy = { - 'digital': tuple(x for x in range(63)), - 'pwm': tuple(x for x in range(63)), - 'analog': tuple(x for x in range(43, 69)), - 'disabled': (0,1), - } - self.board = pyfirmata.Board(port, baudrate=baudrate, timeout=timeout, layout=teensy) + self.board = TeensyBoard(port, baudrate=baudrate, timeout=timeout) self.lock = Lock() def analog_write(self, pin, value): diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 6749089e..3549554b 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -30,14 +30,14 @@ def init_exp(base_class, feats, seq=None, **kwargs): class TestManualControlTasks(unittest.TestCase): - # @unittest.skip("") + @unittest.skip("") def test_exp(self): seq = ManualControl.centerout_2D() exp = init_exp(ManualControl, [MouseControl, Window2D], seq) exp.rotation = 'xzy' exp.run() - # @unittest.skip("") + @unittest.skip("") def test_tracking(self): print("Running tracking task test") seq = TrackingTask.tracking_target_debug(nblocks=1, ntrials=6, time_length=5, seed=40, sample_rate=60, ramp=1) # sample_rate needs to match fps in ScreenTargetTracking @@ -82,6 +82,17 @@ def test_dual_laser_wave(self): seq = LaserConditions.dual_laser_square_wave(duty_cycle_1=0.025, duty_cycle_2=0.025, phase_delay_2=0.1) print(seq[0]) + def test_corners(self): + seq = ScreenTargetCapture.corners_2D(chain_length=3) + seq = list(seq) + + idx = np.array([s[0][0] for s in seq]) + loc = np.array([s[1][0] for s in seq]) + print("corners---------------") + print(idx) + print(loc) + print("---------------corners") + class TestYouTube(unittest.TestCase): @unittest.skip("") From 17cacede27634ff98ef58006c825865161cc2ff5 Mon Sep 17 00:00:00 2001 From: resatomo Date: Fri, 4 Aug 2023 15:53:54 -0700 Subject: [PATCH 09/10] more to do --- features/reward_features.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/features/reward_features.py b/features/reward_features.py index cf560d58..dcedcc2b 100644 --- a/features/reward_features.py +++ b/features/reward_features.py @@ -62,11 +62,13 @@ class PelletReward(RewardSystem): ''' ''' + + # tablet_name = traits.Float(1, desc='Number of successful trials before solenoid is opened') # dropdown menu with booted-1 and booted-2 as options def __init__(self, *args, **kwargs): from riglib.tablet_reward import RemoteReward super(RewardSystem, self).__init__(*args, **kwargs) - self.reward = RemoteReward() + self.reward = RemoteReward(self.tablet_name) self.reportstats['Reward #'] = 0 def _start_reward(self): @@ -253,6 +255,38 @@ def _while_reward(self): self.bar.show() +class TrackingRewards(traits.HasTraits): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def cleanup(self, database, saveid, **kwargs): + self.reward.off() + super().cleanup(database, saveid, **kwargs) + + def _start_tracking_in(self): + super()._start_tracking_in() + self.trigger_reward = False + self.reward.off() + self.start_time = 0 + + def _while_tracking_in(self): + super()._while_tracking_in() + # Give reward for tracking in + curr_time = self.frame_index/self.fps + if curr_time % self.tracking_reward_interval==0 and self.trigger_reward==False: + self.trigger_reward = True + self.start_time = curr_time + self.reward.on() + # print('REWARD ON') + if curr_time - self.start_time > self.tracking_reward_time and self.trigger_reward==True: + self.trigger_reward = False + self.reward.off() + # print('REWARD OFF') + + def _start_tracking_out(self): + super()._start_tracking_out() + self.reward.off() + """"" BELOW THIS IS ALL THE OLD CODE ASSOCIATED WITH REWARD FEATURES""" From 118dbfaba96c239d31964b4ebbf8707b6834c6de Mon Sep 17 00:00:00 2001 From: resatomo Date: Fri, 4 Aug 2023 16:04:12 -0700 Subject: [PATCH 10/10] to do for multiple rewards per trial --- features/reward_features.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/reward_features.py b/features/reward_features.py index dcedcc2b..128ddc23 100644 --- a/features/reward_features.py +++ b/features/reward_features.py @@ -62,7 +62,7 @@ class PelletReward(RewardSystem): ''' ''' - + # rewards_per_trial = traits.Int(1, desc='Number of reward pellets per successful trial') # tablet_name = traits.Float(1, desc='Number of successful trials before solenoid is opened') # dropdown menu with booted-1 and booted-2 as options def __init__(self, *args, **kwargs):