Esempio n. 1
0
class Game(object):

    def __init__(self):

        ###############
        # load config #
        ###############
        with open('config.yaml') as f:
            CONFIG = yaml.load(f)
        with open('mvc.yaml') as f:
            MVC = yaml.load(f)
        self.subj_id = CONFIG['subject-id']
        self.subj_dir = 'datasets/' + self.subj_id
        self.loc = CONFIG['location']

        #################
        # set constants #
        #################
        if self.loc == 'lab':
            self.FRAME_RATE = 120
        elif self.loc == 'scanner':
            self.FRAME_RATE = 60
        self.SCREEN_WIDTH, self.SCREEN_HEIGHT = 1024, 768
        self.BG_COLOR_REG = 70,70,70
        self.BG_COLOR_REG_2 = 110,110,110
        self.BG_COLOR_REG_3 = 40,40,40
        self.SUCCESS_COLOR = 70,170,70
        self.FAIL_COLOR = 170,70,70
        self.INDICATOR_COLOR = 40,60,40
        self.INDICATOR_COLOR_2 = 30,100,30
        self.GOOD_MSG_COLOR = 160,255,160
        self.BAD_MSG_COLOR = 255,160,160
        self.A_MSG_COLOR = 160,160,255
        self.B_MSG_COLOR = 230,230,160
        self.REG_FIXATION_COLOR = 200,200,200
        self.GOOD_FIXATION_COLOR = self.SUCCESS_COLOR
        self.BAD_FIXATION_COLOR = 240,140,140

        self.BAD_SCORE_COLOR = 200,100,100
        self.GOOD_SCORE_COLOR = 100,200,100
        self.HIGHLIGHT_SCORE_COLOR = 20,255,20
        self.NORM_SCORE_COLOR = 180,180,100

        self.SENSOR_INPUT_OFFSET = np.array([0.5*self.SCREEN_WIDTH,
                                             0.75*self.SCREEN_HEIGHT])
        self.BORDER_COLOR_DICT = {'incomplete':(50,0,0),
                                  'ready':(0,0,50),
                                  'complete':(0,50,0),
                                  'disconnected':(50,0,0),
                                  'idle':(50,50,50),
                                  'rai':(0,50,0),
                                  'rfi':(0,50,0),
                                  'loc':(0,50,0),
                                  'nfb':(0,50,0)}
        self.BG_COLOR_DICT = {'incomplete':(100,0,0),
                              'ready':(0,0,100),
                              'complete':(0,100,0),
                              'disconnected':(100,0,0),
                              'idle':(100,100,100),
                              'rai':(0,100,0),
                              'rfi':(0,100,0),
                              'loc':(0,100,0),
                              'nfb':(0,100,0)}

        # dict entry for current timer set to use
        self.PRE_REST_TIME = {'trial':0}
        self.INTRO_TIME = {'trial':0}
        self.FEEDBACK_TIME = {'trial':0}
        self.ACTIVE_TIME = {'trial':0}
        self.TRIALS = {'trial':0}
        self.TRIAL_TIME = {'trial':0}
        self.INTRO_TRIGGER_TIME = {'trial':0}
        self.ACTIVE_TRIGGER_TIME = {'trial':0}
        self.FEEDBACK_TRIGGER_TIME = {'trial':0}

        # self-paced times
        self.PRE_REST_TIME['s_p'] = 3
        self.INTRO_TIME['s_p'] = 1
        self.FEEDBACK_TIME['s_p'] = 2
        self.ACTIVE_TIME['s_p'] = -1
        self.TRIALS['s_p'] = 5
        self.TRIAL_TIME['s_p'] = (self.PRE_REST_TIME['s_p'] + self.INTRO_TIME['s_p']
                               + self.FEEDBACK_TIME['s_p'])
        self.INTRO_TRIGGER_TIME['s_p'] = self.PRE_REST_TIME['s_p'] 
        self.ACTIVE_TRIGGER_TIME['s_p'] = self.PRE_REST_TIME['s_p'] + self.INTRO_TIME['s_p']
        self.FEEDBACK_TRIGGER_TIME['s_p'] = -1

        self.TRIAL_TYPES = 9
        self.trial_type_count = 0

        # localizer times
        # 40 seconds per trial
        # 480s per run
        self.PRE_REST_TIME['loc'] = 17
        self.INTRO_TIME['loc'] = 1
        self.ACTIVE_TIME['loc'] = 20
        self.FEEDBACK_TIME['loc'] = 2
        self.TRIALS['loc'] = 12

        # neurofeedback times
        # 60 seconds per trial
        # 480s per run
        self.PRE_REST_TIME['nfb'] = 27
        self.INTRO_TIME['nfb'] = 1
        self.ACTIVE_TIME['nfb'] = 30
        self.FEEDBACK_TIME['nfb'] = 2
        self.TRIALS['nfb'] = 8

        self.NFB_INFO_TIME = 0.5
        self.NFB_INFO_HEIGHT = 28
        self.MVC_TIME = 19

        self.INTRO_TRIGGER_TIME['loc'] = self.PRE_REST_TIME['loc'] 
        self.ACTIVE_TRIGGER_TIME['loc'] = self.PRE_REST_TIME['loc'] + self.INTRO_TIME['loc']
        self.FEEDBACK_TRIGGER_TIME['loc'] = self.ACTIVE_TRIGGER_TIME['loc'] + self.ACTIVE_TIME['loc'] 

        self.INTRO_TRIGGER_TIME['nfb'] = self.PRE_REST_TIME['nfb'] 
        self.ACTIVE_TRIGGER_TIME['nfb'] = self.PRE_REST_TIME['nfb'] + self.INTRO_TIME['nfb']
        self.FEEDBACK_TRIGGER_TIME['nfb'] = self.ACTIVE_TRIGGER_TIME['nfb'] + self.ACTIVE_TIME['nfb'] 
        self.CONTROL_TRIGGER_TIME = 1000*int(0.5*self.INTRO_TRIGGER_TIME['nfb'])
        self.BASELINE_CALC_SHIFT = self.PRE_REST_TIME['nfb']-10

        self.TRIAL_TIME['loc'] = (self.PRE_REST_TIME['loc'] + self.INTRO_TIME['loc']
                               + self.ACTIVE_TIME['loc'] + self.FEEDBACK_TIME['loc'])

        self.TRIAL_TIME['nfb'] = (self.PRE_REST_TIME['nfb'] + self.INTRO_TIME['nfb']
                               + self.ACTIVE_TIME['nfb'] + self.FEEDBACK_TIME['nfb'])

        #####################
        # control variables #
        #####################
        self.TR = 1.
        self.ADD_CONTROL_TRS = 5
        self.TF_CONTROL = self.ACTIVE_TIME['nfb']+self.ADD_CONTROL_TRS
        self.N_ACTIVE_TIME = self.ACTIVE_TIME['nfb']/self.TR+1
        self.N_CONTROL_STEPS = self.TF_CONTROL/self.TR+1
        self.CONTROL_MVC_MIN = 0.1
        self.CONTROL_MVC_MAX = 0.4
        self.u_vec = np.zeros((1,self.N_CONTROL_STEPS))
        self.y_vec = np.zeros(self.N_CONTROL_STEPS)
        gc.set_system(self)

        ##########################
        # force sensor variables #
        ##########################
        self.MVC_IN_PIXEL = 900
        self.MVC_MIN = 0#0.05
        self.ANGLE_ADD_DEG = 20
        self.ANGLE_ADD = np.deg2rad(self.ANGLE_ADD_DEG)
        self.ANGLE_MULT = 1+2*self.ANGLE_ADD/np.pi
        self.set_mvc(MVC['mvc'])
        self.DAQ_LPF_CUTOFF = 8
        self.DAQ_LPF_ORDER = 3

        self.CALIBRATE_BOOL = False # False or True
        if self.CALIBRATE_BOOL:
            fu.write_cal_header(self)

        if SENSOR_ACTIVE:
            if self.CALIBRATE_BOOL:
                self.daq = Pydaq('Dev3/ai0:2', self.FRAME_RATE,
                                 lp_filt_freq=14,
                                 lp_filt_order=3,
                                 force_params='force_params_cal.txt')
            else:
                self.daq = Pydaq('Dev3/ai0:1', self.FRAME_RATE,
                                 lp_filt_freq=self.DAQ_LPF_CUTOFF,
                                 lp_filt_order=self.DAQ_LPF_ORDER,
                                 force_params='force_params_lab.txt')
            self.scanner_daq = Pydaq('Dev1/ai2:3', self.FRAME_RATE,
                                     lp_filt_freq=self.DAQ_LPF_CUTOFF,
                                     lp_filt_order=self.DAQ_LPF_ORDER,
                                     force_params='force_params_scanner.txt')

        ########################
        # networking variables #
        ########################
        IP = CONFIG['server-ip']
        PORT = CONFIG['server-port']
        self.SERVER_TARGET = 'http://{ip}:{port}/'.format(ip=IP,
                                                          port=PORT)
        self.requests_running_bool = mp.Value('i', 1)
        self.last_volume_received = mp.Value('i', 0)
        self.pool = mp.Pool()

        self.insta_mode = 'disconnected' # idle, rai, rfi, loc, nfb
        self.roi_name = 'm1_left_a'
        self.next_run_msg = ''
        self.insta_complete = OrderedDict()
        self.insta_complete['roi'] = 'incomplete'
        self.insta_complete['rai'] = 'incomplete'
        self.insta_complete['rfi'] = 'incomplete'
        self.insta_complete['warp2rfi'] = 'incomplete'
        self.insta_complete['glm'] = 'incomplete'
        self.insta_complete['loc'] = 'incomplete'

        self.raw_betas = {'active':0.,
                          'force':0.,
                          'complexity':0.,
                          'constant':0.}

        self.psc_betas = {'active':0.5,
                          'force':1.5,
                          'complexity':1.0}

        self.MAX_CONTROL = 3
        self.MIN_CONTROL = 0.5
        self.PSC_TARGET_LIST = [1.5,1.5,1.5,1.5]
        self.RUN_TYPE_LIST = CONFIG['run-type-list']

        self.NFB_TRS = int(self.TRIAL_TIME['nfb']
                           *self.TRIALS['nfb']/self.TR)
        self.bold_rois_array = []
        self.bold_rois_array.append(mp.Array('d',self.NFB_TRS))
        self.REQUEST_WAIT_TIME = 0.1
        self.requests_process = mp.Process(target = gn.requests_loop,
                                           args = (self.SERVER_TARGET,
                                                   self.REQUEST_WAIT_TIME,
                                                   self.requests_running_bool,
                                                   self.last_volume_received,
                                                   self.bold_rois_array,))

        ####################################################
        # start pygame and initialize default game objects #
        ####################################################
        pygame.init()
        pygame.mouse.set_visible(not pygame.mouse.set_visible)
        self.clock = pygame.time.Clock()
        if CONFIG['fullscreen']:
            self.screen = pygame.display.set_mode(
                            (self.SCREEN_WIDTH, self.SCREEN_HEIGHT),
                             pygame.FULLSCREEN)
        else:
            self.screen = pygame.display.set_mode(
                            (self.SCREEN_WIDTH, self.SCREEN_HEIGHT))

        ##################################
        # initialize custom game objects #
        ##################################
        self.CURSOR_RAD = 75
        self.FIXATION_WIDTH = 1.5*self.CURSOR_RAD
        self.FIXATION_HEIGHT = 8
        self.cursor = Cursor(self.screen, self.CURSOR_RAD)
        self.timers = {}
        self.init_timers()

        self.TARGET_SUCCESS_TIME = .15
        self.TARGET_MIN_TIME = .5
        self.TARGET_RAD_MIN = -25
        self.TARGET_RAD_MAX = 45

        #####################
        # target lists here #
        #####################

        # function to generate x length, all same mvc,
        # all same phase
        # and do {20s length} x {.1, .2, .4} x {0, 180}
        self.MVC_LIST = []
        self.PHASE_LIST = []
        self.MVC_LIST.append(np.array([.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1]))
        self.MVC_LIST.append(np.array([.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2,.2]))
        self.MVC_LIST.append(np.array([.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4,.4]))
        self.PHASE_LIST.append(np.array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]))
        self.PHASE_LIST.append(np.array([180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180]))

        self.TRAINING_TARGET_LOC_LIST = []

        self.loc_target_order = [0,3,1,4,2,5,1,4,0,5,3,2]

        for mvc in self.MVC_LIST:
            for phase in self.PHASE_LIST:
                self.TRAINING_TARGET_LOC_LIST.append(self.gen_target_list(mvc,phase))

        self.next_mvc_list = []
        self.next_phase_list = []
        self.next_target_loc_list = []

        # auto-generate 1, 1.5, and 2 psc from dummy data
        for psc_target in (1.,1.5,2.):
            new_targets = self.gen_control_targets(psc_target)
            self.TRAINING_TARGET_LOC_LIST.append(new_targets)

        self.SAMPLE_MVC_LIST = np.array([.4,.1,.2,.1,.2,.4])
        self.SAMPLE_PHASE_LIST = np.array([0,0,0,180,180,180])
        self.SAMPLE_TARGET_LOC_LIST = self.gen_target_list(self.SAMPLE_MVC_LIST,
                                                           self.SAMPLE_PHASE_LIST)
        self.set_targets(self.SAMPLE_TARGET_LOC_LIST)

        ######################
        # set game variables #
        ######################
        self.screen_mid = np.array([0.5*self.screen.get_width(),
                                    0.5*self.screen.get_height()])
        self.bg_color = self.BG_COLOR_REG_3
        self.fixation_color = self.REG_FIXATION_COLOR
        self.input_mode = 'mouse'
        self.input_pos = np.array([0.0,0.0])
        self.input_force = np.array([0.0,0.0])
        self.cal_force = 0.
        self.debug_bool = False
        self.nfb_info_bool = False
        self.run_mode = 'idle' # idle, mvc, trials
        self.run_type = 's_p'
        self.self_paced_bool = True
        self.controller_active_bool = False
        self.controller_calc_bool = False
        self.nfb_run_count = 0
        self.calc_score_bool = False
        self.trial_time_array = np.array(())
        self.set_timer_mode('s_p')
        self.current_score = 0
        self.score_color = self.GOOD_SCORE_COLOR
        self.score_fixation_status = 'norm'
        self.max_rec_mvc_both = 0
        self.max_rec_mvc_x = 0
        self.max_rec_mvc_y = 0
        self.MVC_DISPLAY_CEIL = 100.

    def gen_target_list(self, mvc_list, phase_list):
        mvc_list = np.ravel(mvc_list)
        phase_list = np.ravel(phase_list)
        target_list = np.zeros((2,2*len(mvc_list)))
        for idx in range(len(mvc_list)):
            if phase_list[idx] == 0:
                target_list[0][2*idx] = 0
                target_list[1][2*idx] = -self.MVC_IN_PIXEL*mvc_list[idx]
                target_list[0][2*idx+1] = 0
                target_list[1][2*idx+1] = -self.MVC_IN_PIXEL*self.MVC_MIN
            elif phase_list[idx] == 180:
                target_list[0][2*idx] = self.MVC_IN_PIXEL*mvc_list[idx]
                target_list[1][2*idx] = -self.MVC_IN_PIXEL*self.MVC_MIN
                target_list[0][2*idx+1] = -self.MVC_IN_PIXEL*mvc_list[idx]
                target_list[1][2*idx+1] = -self.MVC_IN_PIXEL*self.MVC_MIN
        return np.transpose(target_list)

    def set_mvc(self, mvc):
        self.newtons_2_pixel_both = self.MVC_IN_PIXEL/(.5*mvc['both'])
        self.newtons_2_pixel_x = self.MVC_IN_PIXEL/(mvc['x'])
        self.newtons_2_pixel_y = self.MVC_IN_PIXEL/(mvc['y'])

    def set_targets(self, target_list):
        self.targets = []
        # set targets
        for target_loc in target_list:
            target_rad = (self.TARGET_RAD_MAX - 
                          (self.TARGET_RAD_MAX - self.TARGET_RAD_MIN)
                           * np.sqrt(target_loc[0]**2 + target_loc[1]**2)
                            /float(self.MVC_IN_PIXEL))
            self.targets.append(Target(self.screen,
                                       target_loc,
                                       self.SENSOR_INPUT_OFFSET,
                                       target_rad,
                                       self.CURSOR_RAD,
                                       self.TARGET_SUCCESS_TIME,
                                       self.TARGET_MIN_TIME))
        # link targets
        self.current_target = self.targets[0]
        self.targets[0].prev_target = None
        self.targets[-1].next_target = None
        for idx in range(len(self.targets)-1):
            self.targets[idx].next_target = self.targets[idx+1]
            self.targets[idx+1].prev_target = self.targets[idx]

    def get_pos(self):
        if self.input_mode=='mouse' or not(SENSOR_ACTIVE):
            return pygame.mouse.get_pos()
        else:
            if self.loc == 'lab':
                f_in_x, f_in_y = self.daq.get_force()
            elif self.loc == 'scanner':
                f_in_x, f_in_y = self.scanner_daq.get_force()
            self.input_force = f_in_x, f_in_y
            f_mag = max(f_in_x, f_in_y)
            f_ang = np.arctan2(f_in_x, f_in_y)
            f_out_x = f_mag*np.cos(2*self.ANGLE_MULT*f_ang-self.ANGLE_ADD)
            f_out_y = f_mag*np.sin(2*self.ANGLE_MULT*f_ang-self.ANGLE_ADD)
            ang_blend = (f_ang-.25*np.pi)/(.25*np.pi)
            ang_blend_both = (max(0,(1-abs(ang_blend)))
                              *self.newtons_2_pixel_both)
            if ang_blend < 0:
                ang_blend_side = abs(ang_blend)*self.newtons_2_pixel_y
            else:
                ang_blend_side = abs(ang_blend)*self.newtons_2_pixel_x
            newtons_2_pixel = ang_blend_both + ang_blend_side
            pos_x = (self.SENSOR_INPUT_OFFSET[0]
                     +newtons_2_pixel*f_out_x)
            pos_y = (self.SENSOR_INPUT_OFFSET[1]
                     -newtons_2_pixel*f_out_y)
            return (pos_x, 
                    pos_y)

    def init_timers(self):
        self.timers['loc'] = Timer(self.TRIAL_TIME['loc'],
                                   self.TRIALS['loc'])
        self.timers['nfb'] = Timer(self.TRIAL_TIME['nfb'],
                                   self.TRIALS['nfb'])
        self.timers['s_p'] = Timer(self.TRIAL_TIME['s_p'],
                                   self.TRIALS['s_p'])
        self.timers['trial'] = self.timers['loc']
        self.timers['move'] = Timer(sys.maxint)
        self.timers['mvc'] = Timer(self.MVC_TIME)
        self.timers['nfb_info'] = Timer(self.NFB_INFO_TIME)

    def set_timer_mode(self, mode):
        self.timers['trial'] = self.timers[mode]
        self.PRE_REST_TIME['trial'] = self.PRE_REST_TIME[mode]*1000
        self.INTRO_TIME['trial'] = self.INTRO_TIME[mode]*1000
        self.FEEDBACK_TIME['trial'] = self.FEEDBACK_TIME[mode]*1000
        self.ACTIVE_TIME['trial'] = self.ACTIVE_TIME[mode]*1000
        self.TRIALS['trial'] = self.TRIALS[mode]*1000
        self.TRIAL_TIME['trial'] = self.TRIAL_TIME[mode]*1000
        self.INTRO_TRIGGER_TIME['trial'] = self.INTRO_TRIGGER_TIME[mode]*1000
        self.ACTIVE_TRIGGER_TIME['trial'] = self.ACTIVE_TRIGGER_TIME[mode]*1000
        self.FEEDBACK_TRIGGER_TIME['trial'] = self.FEEDBACK_TRIGGER_TIME[mode]*1000


    def reset_all_timers(self):
        for k,t in self.timers.iteritems():
            t.reset()


    def check_input(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    self.quit()
                elif event.key == pygame.K_0:
                    if self.loc == 'lab':
                        self.daq.set_volts_zero()
                    elif self.loc == 'scanner':
                        self.scanner_daq.set_volts_zero()
                elif event.key == pygame.K_m:
                    if self.input_mode == 'mouse':
                        self.input_mode = 'sensor'
                    elif self.input_mode == 'sensor':
                        self.input_mode = 'mouse'
                elif event.key == pygame.K_d:
                    self.debug_bool = not self.debug_bool
                elif event.key == pygame.K_s:
                    self.nfb_info_bool = not self.nfb_info_bool
                elif event.key == pygame.K_SPACE:
                    self.run_type = 's_p'
                    gr.reset_for_next_run(self)
                    self.run_mode = 'trials'
                elif event.key == pygame.K_7:
                    self.run_mode = 'mvc'
                elif event.key == pygame.K_8:
                    gn.set_mode('loc', self.SERVER_TARGET)
                    self.run_type = 'loc'
                    self.next_run_msg = 'Ready for Localizer'
                elif event.key == pygame.K_9:
                    gn.set_mode('nfb', self.SERVER_TARGET)
                    self.run_type = 'nfb'
                    self.next_run_msg = ('Ready for Run '
                        + str(game.nfb_run_count + 1) + ' of 4')
                elif event.key == pygame.K_5:
                    if ((self.run_type == 'loc'
                            or self.run_type == 'nfb')
                            and self.run_mode == 'idle'):
                        self.run_mode = 'trials'
                        self.next_run_msg = ''
                        gr.reset_for_next_run(self)
                # control of insta_server
                elif event.key == pygame.K_g:
                    gn.set_mode('idle', self.SERVER_TARGET)
                elif event.key == pygame.K_h:
                    gn.set_mode('rai', self.SERVER_TARGET)
                elif event.key == pygame.K_j:
                    gn.set_mode('rfi', self.SERVER_TARGET)
                elif event.key == pygame.K_k:
                    gn.set_mode('loc', self.SERVER_TARGET)
                elif event.key == pygame.K_l:
                    gn.set_mode('nfb', self.SERVER_TARGET)
                # testing controls
                # elif event.key == pygame.K_z:
                #     # figure out when/where to calculate this (add trigger)
                #     # need baseline calculation
                #     # and y_vec calculation
                #     psc_target = 2.
                #     # self.pool.apply_async(func = gc.calc_control,
                #     #                       args = (self,
                #     #                               psc_target,
                #     #                               self.u_vec,
                #     #                               self.y_vec),
                #     #                       callback = self.update_control_targets)
                #     u_vec = gc.calc_control(self,
                #                     psc_target,
                #                     self.u_vec,
                #                     self.y_vec)
                #     self.update_control_targets(u_vec)


    def run(self):
        fu.write_trial_s_p_header(self)
        fu.write_control_file(self)
        fu.write_all_headers_timed(self)
        self.requests_process.start()
        while True:
            time_passed = self.clock.tick_busy_loop(self.FRAME_RATE)
            self.check_input()
            self.input_pos = self.get_pos()
            self.cursor.update(self.input_pos)
            if self.current_target != None:
                self.cursor.update_warn(time_passed,
                                        not(self.current_target.success))
            else:
                self.cursor.update_warn(time_passed,
                                        False)
            self.draw_background()

            if self.run_mode == 'trials':
                gr.frame_based_updates(game, time_passed)

            if self.debug_bool:
                self.draw_debug(time_passed)
            if self.nfb_info_bool:
                self.draw_nfb_info(time_passed)
            if self.run_mode == 'mvc':
                gr.run_mvc(self, time_passed)
            else:
                self.cursor.draw()
            pygame.display.flip()

    def draw_targets(self):
        current_bool = self.current_target != None
        if current_bool:
            next_bool = self.current_target.next_target != None
        else:
            next_bool = False
        if next_bool:
            next_next_bool = self.current_target.next_target.next_target != None
        else:
            next_next_bool = False
        if next_next_bool:
            self.current_target.next_target.next_target.draw(draw_type='next_next')
        if next_bool:
            self.current_target.next_target.draw(draw_type='next')
        if current_bool:
            self.current_target.draw(draw_type='current')


    def draw_debug(self, time_passed):
        fr = 1000/float(time_passed)
        fr_msg = 'frame rate: ' + str(fr)
        x_v_msg = 'x force: ' + str(self.input_force[0])
        y_v_msg = 'y force: ' + str(self.input_force[1])
        betas_active_msg = 'active beta: ' + str(self.psc_betas['active'])
        betas_force_msg = 'force beta: ' + str(self.psc_betas['force'])
        betas_complexity_msg = 'complexity beta: ' + str(self.psc_betas['complexity'])
        lvr_msg = 'last vol: ' + str(self.last_volume_received.value)

        gg.draw_msg(self.screen, fr_msg,
                    loc='left', pos=(10,33), size=28)
        gg.draw_msg(self.screen, x_v_msg,
                    loc='left', pos=(10,66), size=28)
        gg.draw_msg(self.screen, y_v_msg,
                    loc='left', pos=(10,99), size=28)
        gg.draw_msg(self.screen, betas_active_msg,
                    loc='left', pos=(10,132), size=28)
        gg.draw_msg(self.screen, betas_force_msg,
                    loc='left', pos=(10,165), size=28)
        gg.draw_msg(self.screen, betas_complexity_msg,
                    loc='left', pos=(10,198), size=28)
        gg.draw_msg(self.screen, lvr_msg,
                    loc='left', pos=(10,231), size=28)

        if self.CALIBRATE_BOOL:
            self.cal_force = self.daq.force_transform_cal(
                                        self.daq.get_volts_cal())
            z_v_msg = 'z lbs: ' + str(self.cal_force)
            gg.draw_msg(self.screen, z_v_msg,
                        loc='left', pos=(10,231), size=28)
            fu.cal_record(self, self.f_cal)

    def draw_nfb_info(self, time_passed):
        self.timers['nfb_info'].update(time_passed)
        if self.timers['nfb_info'].time_limit_hit:
            self.pool.apply_async(func = gn.get_complete,
                                  args = (self.SERVER_TARGET,
                                          self.insta_complete),
                                  callback = self.update_complete)
            self.pool.apply_async(func = gn.get_mode,
                                  args = (self.SERVER_TARGET,),
                                  callback = self.update_mode)
            self.pool.apply_async(func = gn.get_betas,
                                  args = (self.SERVER_TARGET,
                                          self.roi_name,
                                          self.raw_betas),
                                  callback = self.update_betas)
            self.timers['nfb_info'].time_limit_hit = False
            self.timers['nfb_info'].count = 0

        # show insta_mode and insta_complete
        list_count = 0
        for k,v in self.insta_complete.iteritems():
            gg.draw_info_rect(self.screen, k + ': ' + v,
                              (255,255,255),self.BORDER_COLOR_DICT[v],
                              self.BG_COLOR_DICT[v],350,
                               self.NFB_INFO_HEIGHT,200,
                               50 + list_count*(8+self.NFB_INFO_HEIGHT))
            list_count += 1
        gg.draw_info_rect(self.screen, 'mode: ' + self.insta_mode,
                          (255,255,255),self.BORDER_COLOR_DICT[self.insta_mode],
                          self.BG_COLOR_DICT[self.insta_mode],350,
                           self.NFB_INFO_HEIGHT,200,
                           65 + list_count*(8+self.NFB_INFO_HEIGHT),
                           border_size=12)

    def update_complete(self, insta_complete):
        self.insta_complete = insta_complete

    def update_betas(self, raw_betas):
        self.raw_betas = raw_betas
        if self.raw_betas['constant'] != 0:
            for k,v in self.psc_betas.iteritems():
                self.psc_betas[k] = 100*self.raw_betas[k]/self.raw_betas['constant']
            gc.set_control_limits(self, self.psc_betas)

    def update_control_targets(self, u_vec):
        self.u_vec = self.u_vec + u_vec
        u_vec_reduced = np.ravel(self.u_vec)
        u_vec_reduced = u_vec_reduced[0:self.N_ACTIVE_TIME]
        self.next_mvc_list, self.next_phase_list = gc.betas_to_mvc_phase(self,
                                                                         self.psc_betas,
                                                                         u_vec_reduced)
        self.next_target_loc_list = self.gen_target_list(self.next_mvc_list,
                                                         self.next_phase_list)
        self.set_targets(self.next_target_loc_list)

    def calc_y_psc(self):
        trial_num = self.timers['trial'].count-1
        begin_rest_idx = trial_num*self.TRIAL_TIME['nfb'] + self.BASELINE_CALC_SHIFT
        end_rest_idx = begin_rest_idx + self.PRE_REST_TIME['nfb'] - self.BASELINE_CALC_SHIFT + self.INTRO_TIME['nfb']
        begin_move_idx = end_rest_idx
        end_move_idx = begin_move_idx + self.ACTIVE_TIME['nfb'] + 1
        rest_mean = 10e-9+np.mean(self.bold_rois_array[0][begin_rest_idx:end_rest_idx])
        y_vec_psc = (100*(self.bold_rois_array[0][begin_move_idx:end_move_idx]-rest_mean)
                     /rest_mean)
        self.y_vec[0:self.N_ACTIVE_TIME] = y_vec_psc[0:self.N_ACTIVE_TIME] 
        self.y_vec[self.N_ACTIVE_TIME:] = np.mean(y_vec_psc[self.N_ACTIVE_TIME
                                                            - self.ADD_CONTROL_TRS
                                                            :self.N_ACTIVE_TIME])

    def gen_control_targets(self, psc_target):
        u_vec = gc.calc_control(self, psc_target)
        u_vec_reduced = u_vec[0:self.N_ACTIVE_TIME]
        mvc_list, phase_list = gc.betas_to_mvc_phase(self,
                                                     self.psc_betas,
                                                     u_vec_reduced)
        return self.gen_target_list(mvc_list,
                                    phase_list)

    def update_mode(self, mode):
        self.insta_mode = mode

    def increment_target(self):
        self.current_target = self.current_target.next_target

    def draw_background(self):
        self.screen.fill(self.bg_color)
        if self.run_mode == 'idle' and self.loc == 'lab':
            instr_msg = ('Press Space for Run ' + str(self.trial_type_count+1)
                         + ' of ' + str(self.TRIAL_TYPES))
            gg.draw_msg(self.screen, instr_msg,
                        loc='center', pos=(.5*self.SCREEN_WIDTH,
                                           .5*self.SCREEN_HEIGHT), size=48)
        if self.next_run_msg != '':
            gg.draw_msg(self.screen, self.next_run_msg,
                        loc='center', pos=(.5*self.SCREEN_WIDTH,
                                           .4*self.SCREEN_HEIGHT), size=48)

    def quit(self):
        self.requests_running_bool.value = 0
        self.requests_process.join()
        sys.exit()
Esempio n. 2
0
class SequenceGame(object):

    def __init__(self):
        gi.generate_constants(self)        
        gi.generate_variables(self)        
        pygame.init()
        pygame.mouse.set_visible(not pygame.mouse.set_visible)
        self.clock = pygame.time.Clock()
        if SENSOR_ACTIVE:
            self.daq = Pydaq(self.ANALOG_IN_CHANS,
                             self.DIGITAL_OUT_CHANS,
                             self.FRAME_RATE,
                             self.LP_FILT_FREQ,
                             self.LP_FILT_ORDER,
                             self.FORCE_PARAMS)
            if self.CONFIG['sensor-type'] == 'optical':
                self.daq.set_volts_zero_init()
        if self.CONFIG['fullscreen']:
            self.screen = pygame.display.set_mode(
                            (self.SCREEN_WIDTH, self.SCREEN_HEIGHT),
                             pygame.FULLSCREEN)
        else:
            self.screen = pygame.display.set_mode(
                            (self.SCREEN_WIDTH, self.SCREEN_HEIGHT))

    def check_input(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE: self.quit()
                elif event.key == pygame.K_0:
                    self.daq.set_volts_zero()
                elif event.key == pygame.K_m:
                    if self.input_mode == 'keyboard':
                        self.input_mode = 'sensor'
                    elif self.input_mode == 'sensor':
                        self.input_mode = 'keyboard'
                elif event.key == pygame.K_n:
                    self.show_keyboard = not(self.show_keyboard)
                elif ((event.key == pygame.K_SPACE
                       or event.key == pygame.K_5)
                      and not(self.run_trials)):
                    self.run_trials = True
                    if SENSOR_ACTIVE:
                        self.daq.set_digital_out(1)
                    gr.reset_for_next_run(game)
                elif event.key == pygame.K_p:
                    self.run_trials = False
                elif event.key == pygame.K_d:
                    self.debug_bool = not(self.debug_bool)
                elif event.key == pygame.K_LEFT:
                    self.set_run('debug')
                elif event.key == pygame.K_RIGHT:
                    self.set_run('test')
                elif event.key == pygame.K_UP:
                    self.set_run('train')
                elif event.key == pygame.K_DOWN:
                    self.set_run('scan')
                if self.input_mode == 'keyboard':
                    if event.key == self.key_codes[0]: self.keydown[0] = True
                    elif event.key == self.key_codes[1]: self.keydown[1] = True
                    elif event.key == self.key_codes[2]: self.keydown[2] = True
                    elif event.key == self.key_codes[3]: self.keydown[3] = True
                    elif event.key == self.key_codes[4]: self.keydown[4] = True
            elif event.type == pygame.KEYUP:
                if self.input_mode == 'keyboard':
                    if event.key == self.key_codes[0]: self.keydown[0] = False
                    elif event.key == self.key_codes[1]: self.keydown[1] = False
                    elif event.key == self.key_codes[2]: self.keydown[2] = False
                    elif event.key == self.key_codes[3]: self.keydown[3] = False
                    elif event.key == self.key_codes[4]: self.keydown[4] = False
        if self.input_mode == 'sensor':
            self.force_array[:] = self.daq.get_force()
            self.keydown[:] = self.force_array > self.KEYPRESS_FORCE
            self.keepunder[:] = self.force_array < self.KEEP_UNDER_FORCE

    def check_key_status(self):
        if self.keydown.count(True) == 0:
            self.current_key = 'none'
            self.last_key_pressed = -1
        elif self.keydown.count(True) == 1 and self.keepunder.count(True) >= 4:
            self.current_key = self.keydown.index(True)+1

    def set_run(self, mode):
        self.run_count = 0
        self.mode = mode
        self.sequences_per_trial = self.SEQUENCES_PER_TRIAL[mode]
        self.trials_per_run = self.TRIALS_PER_RUN[mode]
        self.runs_per_experiment = self.RUNS_PER_EXPERIMENT[mode]
        self.timers['score'] = Timer(self.FEEDBACK_TIME,
                                     self.sequences_per_trial)
        if self.mode == 'scan':
            self.self_paced_bool = False 
        else:
            self.self_paced_bool = True 
        if game.mode == 'debug' or game.mode == 'train':
            game.sequence_set = game.TRAIN_SEQUENCES
        elif game.mode == 'test' or game.mode == 'scan':
            game.sequence_set = game.TEST_SEQUENCES

    def set_sequences(self):
        if self.mode == 'scan':
            self.sequence_list = []
            temp_sequence_set = game.sequence_set
            for block in range(4):
                np.random.shuffle(temp_sequence_set)
                for sequence in temp_sequence_set:
                    self.sequence_list.append(sequence)
        elif self.mode == 'test':
            self.sequence_list = self.sequence_set[:-1]
            np.random.shuffle(game.sequence_list)
        else:
            self.sequence_list = self.sequence_set
            np.random.shuffle(game.sequence_list)

    def run(self):
        while True:
            time_passed = self.clock.tick_busy_loop(self.FRAME_RATE)
            self.check_input()
            self.check_key_status()
            self.draw_background()
            if self.show_keyboard:
                gg.draw_keyboard(self)
            if self.run_trials:
                fu.frame_record(game, game.f_frame, time_passed)
                gg.draw_frame_rectangle(self)
                if not(self.timers['cue'].time_limit_hit):
                    self.timers['cue'].update(time_passed)
                    gr.run_sequence_cue(self)
                elif not(self.current_sequence_complete):
                    self.timers['move'].update(time_passed)
                    if not(self.self_paced_bool):
                        self.timers['move_limit'].update(time_passed)
                        if self.timers['move_limit'].time_limit_hit:
                            self.current_sequence_complete = True
                    gr.run_sequence_move(self)
                elif (not(self.self_paced_bool)
                          and not(self.timers['move_limit'].time_limit_hit)):
                    self.timers['move_limit'].update(time_passed)
                    gg.draw_sequence_progress(self)
                elif not(self.timers['score'].time_limit_hit):
                    self.timers['score'].update(time_passed)
                    if game.current_sequence == game.REST_SEQUENCE:
                        gr.run_sequence_score_rest(self)
                    else:
                        gr.run_sequence_score(self)
                elif not(self.timers['score'].count_limit_hit):
                    gr.reset_for_next_sequence_execution(self)
                elif self.trial_count < self.trials_per_run:
                    gr.reset_for_next_sequence_trial(self)
                else:
                    self.run_trials = False
                    if SENSOR_ACTIVE:
                        self.daq.set_digital_out(0)
            else:
                self.draw_splash()
            if self.debug_bool:
                self.draw_debug(time_passed)
            pygame.display.flip()

    def draw_background(self):
        self.screen.fill(self.BG_COLOR)

    def draw_splash(self):
        if game.run_count < game.runs_per_experiment:
            splash_msg = ('Ready for Run ' + str(game.run_count+1)
                          + ' of ' + str(game.runs_per_experiment))
        else:
            splash_msg = ('Done!')
        gg.draw_msg(self.screen, splash_msg,
                    loc='center', pos=(.5*self.SCREEN_WIDTH,
                                       .5*self.SCREEN_HEIGHT), size=50)
        gg.draw_msg(self.screen, ('mode: ' + game.mode),
                    loc='right', pos=(.975*self.SCREEN_WIDTH,
                                      .05*self.SCREEN_HEIGHT), size=24)
        if game.run_count > 0:
            points_msg = ('Points this run: ' + str(game.points_total))
            time_msg = ('Average movement time: ' + str(round(np.mean(
                            game.execution_time_array)/1000.,2)) + ' seconds')
            error_rate = np.mean(game.error_array)
            error_msg = ('Percent errors: ' + str(round(100*error_rate,1)) + '%')
            if game.run_count < game.runs_per_experiment:
                if error_rate > game.ERROR_CUTOFF:
                    advice_msg = 'For the next run, try slowing down.'
                else:
                    advice_msg = 'For the next run, try going faster.'
            else:
                advice_msg = ''
            if game.mode != 'scan':
                gg.draw_msg(self.screen, points_msg,
                            loc='center', pos=(.5*self.SCREEN_WIDTH,
                                               .6*self.SCREEN_HEIGHT), size=35)
                gg.draw_msg(self.screen, time_msg,
                            loc='center', pos=(.5*self.SCREEN_WIDTH,
                                               .7*self.SCREEN_HEIGHT), size=35)
                gg.draw_msg(self.screen, error_msg,
                            loc='center', pos=(.5*self.SCREEN_WIDTH,
                                               .8*self.SCREEN_HEIGHT), size=35)
                gg.draw_msg(self.screen, advice_msg,
                            loc='center', pos=(.5*self.SCREEN_WIDTH,
                                               .9*self.SCREEN_HEIGHT), size=35)

    def draw_debug(self, time_passed):
        fr = 1000/float(time_passed)
        fr_msg = 'frame rate: ' + str(fr)
        gg.draw_msg(self.screen, fr_msg,
                    loc='left', pos=(10,33), size=28)
        force = self.daq.get_force()
        volt_msg_1 = 'out_volt_1: ' + str(force[0])
        volt_msg_2 = 'out_volt_2: ' + str(force[1])
        gg.draw_msg(self.screen, volt_msg_1,
                    loc='left', pos=(10,66), size=28)
        gg.draw_msg(self.screen, volt_msg_2,
                    loc='left', pos=(10,99), size=28)

    def quit(self):
        sys.exit()
Esempio n. 3
0
class Game(object):

    def __init__(self):
        gi.generate_constants(self)        
        gi.generate_variables(self)        
        pygame.init()
        pygame.mouse.set_visible(not pygame.mouse.set_visible)
        self.clock = pygame.time.Clock()
        if SENSOR_ACTIVE:
            self.daq = Pydaq(self.DEVICE_NAME,
                             self.FRAME_RATE,
                             self.LP_FILT_FREQ,
                             self.LP_FILT_ORDER,
                             self.FORCE_PARAMS)
        if self.CONFIG['fullscreen']:
            self.screen = pygame.display.set_mode(
                            (self.SCREEN_WIDTH, self.SCREEN_HEIGHT),
                             pygame.FULLSCREEN)
        else:
            self.screen = pygame.display.set_mode(
                            (self.SCREEN_WIDTH, self.SCREEN_HEIGHT))


    def check_input(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE: self.quit()
                elif ((event.key == pygame.K_SPACE
                       or event.key == pygame.K_5)
                      and not(self.run_trials)):
                    self.run_trials = True
                    gr.reset_for_next_run(game)
                elif event.key == pygame.K_p:
                    self.run_trials = False
                elif event.key == pygame.K_q:
                    if not(SENSOR_ACTIVE):
                        self.force_array[0] = self.PRESS_FORCE_THRESHOLD
                elif event.key == pygame.K_w:
                    if not(SENSOR_ACTIVE):
                        self.force_array[1] = self.PRESS_FORCE_THRESHOLD
                elif event.key == pygame.K_e:
                    if not(SENSOR_ACTIVE):
                        self.force_array[2] = self.PRESS_FORCE_THRESHOLD
                elif event.key == pygame.K_r:
                    if not(SENSOR_ACTIVE):
                        self.force_array[3] = self.PRESS_FORCE_THRESHOLD
                elif event.key == pygame.K_m:
                    self.show_thermometer_keyboard_bool = not(
                        self.show_thermometer_keyboard_bool)
                elif event.key == pygame.K_LEFT:
                    self.set_run('debug')
                elif event.key == pygame.K_RIGHT:
                    self.set_run('test')
                elif event.key == pygame.K_UP:
                    self.set_run('train')
                elif event.key == pygame.K_DOWN:
                    self.set_run('scan')
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_q:
                    if not(SENSOR_ACTIVE):
                        self.force_array[0] = self.MIN_KEY_FORCE
                elif event.key == pygame.K_w:
                    if not(SENSOR_ACTIVE):
                        self.force_array[1] = self.MIN_KEY_FORCE
                elif event.key == pygame.K_e:
                    if not(SENSOR_ACTIVE):
                        self.force_array[2] = self.MIN_KEY_FORCE
                elif event.key == pygame.K_r:
                    if not(SENSOR_ACTIVE):
                        self.force_array[3] = self.MIN_KEY_FORCE
        if SENSOR_ACTIVE:
            self.check_keys()

    def check_keys(self):
        self.force_array[:] = self.daq.get_force()

    def set_run(self, mode):
        self.run_count = 0
        self.mode = mode
        self.trials_per_run = self.TRIALS_PER_RUN[mode]
        self.runs_per_experiment = self.RUNS_PER_EXPERIMENT[mode]
        gi.init_timers(self)

    def set_fingers(self):
        game.finger_list = []
        for block in range(game.trials_per_run/len(game.VALID_FINGERS_LIST)):
            np.random.shuffle(game.VALID_FINGERS_LIST)
            game.finger_list += game.VALID_FINGERS_LIST

    def draw_splash(self):
        if game.run_count < game.runs_per_experiment:
            splash_msg = ('Ready for Run ' + str(game.run_count+1)
                          + ' of ' + str(game.runs_per_experiment))
        else:
            splash_msg = ('Done!')
        gg.draw_msg(self.screen, splash_msg,
                    loc='center', pos=(.5*self.SCREEN_WIDTH,
                                       .5*self.SCREEN_HEIGHT), size=50)
        gg.draw_msg(self.screen, ('mode: ' + game.mode),
                    loc='right', pos=(.975*self.SCREEN_WIDTH,
                                      .05*self.SCREEN_HEIGHT), size=24)
        # can add scoring message here
        # if game.run_count > 0:
        #     points_msg = ('Points this run: ' + str(game.points_total))
        #     gg.draw_msg(self.screen, points_msg,
        #                 loc='center', pos=(.5*self.SCREEN_WIDTH,
        #                                    .6*self.SCREEN_HEIGHT), size=35)

    def run(self):
        while True:
            time_passed = self.clock.tick_busy_loop(self.FRAME_RATE)
            self.check_input()
            self.draw_background()
            if self.show_thermometer_keyboard_bool:
                gg.draw_keyboard(game, 'debug')
            if self.run_trials:
                if not(self.timers['rest'].time_limit_hit):
                    self.timers['rest'].update(time_passed)
                    gr.run_rest(self)
                elif not(self.timers['cue'].time_limit_hit):
                    self.timers['cue'].update(time_passed)
                    gr.run_cue(self)
                elif not(self.current_press_complete):
                    if not(self.ready_for_press):
                        gr.run_rest(self)
                        if (self.force_array[self.current_finger]
                                < self.PRESS_FORCE_KEEP_BELOW):
                            self.ready_for_press = True
                    else:
                        self.timers['press_limit'].update(time_passed)
                        gr.run_press(self)
                # elif self.mode == 'test':
                #     if not(self.timers['press_limit'].time_limit_hit):
                #         self.timers['press_limit'].update(time_passed)
                #         gr.run_rest(self)
                #     elif not(self.timers['press_limit'].count_limit_hit):
                #         gr.reset_for_next_press(self)
                #     elif self.trial_count < self.trials_per_run:
                #         gr.reset_for_next_trial(self)
                #     else:
                #         self.run_trials = False
                # elif self.mode == 'train':
                #     if not(self.timers['feedback'].time_limit_hit):
                #         self.timers['feedback'].update(time_passed)
                #         gr.run_feedback(self)
                #     elif not(self.timers['feedback'].count_limit_hit):
                #         gr.reset_for_next_press(self)
                #     elif self.trial_count < self.trials_per_run:
                #         gr.reset_for_next_trial(self)
                #     else:
                #         self.run_trials = False
                elif not(self.timers['feedback'].time_limit_hit):
                        self.timers['feedback'].update(time_passed)
                        gr.run_feedback(self)
                elif not(self.timers['feedback'].count_limit_hit):
                    gr.reset_for_next_press(self)
                elif self.trial_count < self.trials_per_run:
                    gr.reset_for_next_trial(self)
                else:
                    self.run_trials = False
            else:
                self.draw_splash()
            pygame.display.flip()

    def draw_background(self):
        self.screen.fill(self.BG_COLOR)

    def quit(self):
        sys.exit()
Esempio n. 4
0
class Trainer(object):

    def __init__(self):

        ###############
        # load config #
        ###############
        with open('trainer_config.yaml') as f:
            CONFIG = yaml.load(f)
        self.exp_type = CONFIG['experiment-type']
        self.playback_bool = CONFIG['playback-enabled']
        self.subj_id = CONFIG['subject-id']
        self.subj_dir = 'datasets/' + self.subj_id
        if self.exp_type == 'timed':
            fu.write_all_headers_timed(self)
        elif self.exp_type == 'block':
            fu.write_all_headers_block(self)

        #################
        # set constants #
        #################
        self.FRAME_RATE = 30
        self.SCREEN_WIDTH, self.SCREEN_HEIGHT = 1024, 768
        self.BG_COLOR_REG = 70,70,70
        self.BG_COLOR_REG_2 = 110,110,110
        self.BG_COLOR_REG_3 = 40,40,40
        self.SUCCESS_COLOR = 70,170,70
        self.INDICATOR_COLOR = 40,60,40
        self.INDICATOR_COLOR_2 = 30,100,30
        self.GOOD_MSG_COLOR = 160,255,160
        self.BAD_MSG_COLOR = 255,160,160
        self.A_MSG_COLOR = 160,160,255
        self.B_MSG_COLOR = 230,230,160
        self.SENSOR_INPUT_OFFSET = np.array([0.5*self.SCREEN_WIDTH,
                                             0.5*self.SCREEN_HEIGHT])
        self.NEWTONS_2_PIXEL = 200
        self.BLOCK_TIME = CONFIG['block-length']
        self.RESET_HOLD_TIME = 0.5
        self.REACH_SUCCESS_TIME = 2.
        self.NOISE_VAR_GOOD = 0.025
        if not self.playback_bool:
            self.NOISE_VAR_BAD = 10*self.NOISE_VAR_GOOD
        else:
            self.NOISE_VAR_BAD = self.NOISE_VAR_GOOD
        self.SAMPLE_PERIOD = 2.
        self.SAMPLE_FRAMES = self.SAMPLE_PERIOD*self.FRAME_RATE
        self.TRS_SHOW_UPDATE_RATE = 2
        self.TR_LIST = (0.125, .25, 0.5, 1., 2., 4.)
        self.TRS_SAMPLES_DICT = dict((tr, tr/self.SAMPLE_PERIOD)
                                     for tr in self.TR_LIST)
        self.TRS_SUCCESS_DICT = dict((tr, self.REACH_SUCCESS_TIME/tr)
                                     for tr in self.TR_LIST)
        self.BUFFER_DIST = 0.075*self.SCREEN_HEIGHT
        self.START_DIST = self.BUFFER_DIST
        self.GAME_ORIGIN = (0.5*self.SCREEN_WIDTH-0.5*self.SCREEN_HEIGHT,
                            self.SCREEN_HEIGHT)
        self.START_COORDS = np.array([self.GAME_ORIGIN[0]+self.BUFFER_DIST,
                                      self.GAME_ORIGIN[1]-self.BUFFER_DIST])
        self.TARGET_DIST = (0.3*self.SCREEN_HEIGHT,
                            0.8*self.SCREEN_HEIGHT)
        self.ERROR_CUTOFF = self.TARGET_DIST[0] - self.START_DIST
        self.MIN_ERROR_METRIC = 0.1
        self.SS_ERROR_METRIC = 0.9
        self.MIN_SUCCESS_SCORE = 0.7
        self.TARGET_RAD = (self.ERROR_CUTOFF*
                           (1-(self.MIN_SUCCESS_SCORE-self.MIN_ERROR_METRIC)
                            /(self.SS_ERROR_METRIC-self.MIN_ERROR_METRIC)))
        if SENSOR_ACTIVE:
            self.daq = Pydaq('Dev1/ai0:1', self.FRAME_RATE)
        self.VISIBLE_TRIALS = CONFIG['visible-trials']
        self.INVISIBLE_TRIALS = CONFIG['invisible-trials']
        self.NUM_TRIALS = self.VISIBLE_TRIALS
        self.TRIAL_TYPES = 8

        ####################################################
        # start pygame and initialize default game objects #
        ####################################################
        pygame.init()
        pygame.mouse.set_visible(not pygame.mouse.set_visible)
        self.clock = pygame.time.Clock()
        if CONFIG['fullscreen']:
            self.screen = pygame.display.set_mode(
                            (self.SCREEN_WIDTH, self.SCREEN_HEIGHT),
                             pygame.FULLSCREEN)
        else:
            self.screen = pygame.display.set_mode(
                            (self.SCREEN_WIDTH, self.SCREEN_HEIGHT))

        ##################################
        # initialize custom game objects #
        ##################################
        self.cursor = Cursor(self.screen)


        self.target = Target(self.screen,
                             self.FRAME_RATE,
                             self.TARGET_RAD,
                             self.ERROR_CUTOFF,
                             self.MIN_ERROR_METRIC,
                             self.SS_ERROR_METRIC,
                             self.MIN_SUCCESS_SCORE,
                             self.START_COORDS,
                             self.TARGET_DIST)

        self.therm = Thermometer(self.screen,
                                 self.MIN_ERROR_METRIC,
                                 self.MIN_SUCCESS_SCORE,
                                 self.SS_ERROR_METRIC)

        self.INDIC_RAD_MAX = self.START_DIST-self.cursor.RAD-2
        self.target.target_lims = self.TARGET_DIST
        self.timers = {}
        self.init_timers()
        self.set_tr(2.)
        self.trial_count = 0
        self.trial_type_count = 1
        self.next_dof = 1
        self.set_dof(self.next_dof)
        self.next_ir = 'impulse'
        self.next_visible = True
        self.set_trial()

        ######################
        # set game variables #
        ######################
        self.screen_mid = np.array([0.5*self.screen.get_width(),
                                    0.5*self.screen.get_height()])
        self.bg_color = self.BG_COLOR_REG
        self.bg_color_alt = self.BG_COLOR_REG_2
        self.bg_color_alt_2 = self.BG_COLOR_REG_3
        self.indicator_color = self.INDICATOR_COLOR
        self.indicator_rad = 0*self.START_DIST
        self.success_color = self.SUCCESS_COLOR
        self.input_mode = 'mouse'
        self.input_pos = np.array([0.0,0.0])
        self.training_mode = True

        #######################
        # set block variables #
        #######################
        self.NUM_BLOCK_TRIALS = CONFIG['num-block-trials'] 
        self.next_target = 'new'
        self.first_feedback = CONFIG['first-trial-feedback'] 
        self.first_noise = CONFIG['first-trial-noise']
        self.next_feedback = self.first_feedback
        self.next_noise = self.first_noise
        self.set_noise()
        self.BLOCK_TRS = self.BLOCK_TIME/self.tr
        self.block_nfb_buffer = np.zeros(self.BLOCK_TRS)
        self.block_tr_count = 0
        self.total_block_count = 0
        self.trial_block_count = 0

        ##########################
        # set playback variables #
        ##########################
        self.TIME_SHIFT = 6
        self.PLAYBACK_TRS = self.BLOCK_TRS + self.TIME_SHIFT/self.tr
        self.EXTRA_TRS = 2
        self.playback_buffer_length = (self.BLOCK_TIME
                                       + self.EXTRA_TRS)*self.FRAME_RATE
        self.move_counter = 0
        self.reset_playback_buffers()

    def reset_playback_buffers(self):
        self.playback_counter = 0
        self.playback_time_buffer = np.zeros(self.playback_buffer_length)
        self.playback_pos_buffer = np.zeros((2,self.playback_buffer_length))
        self.playback_nfb_buffer = np.zeros(self.playback_buffer_length)
        self.playback_nfb_points = np.zeros(self.PLAYBACK_TRS)


    def get_pos(self):
        if self.input_mode=='mouse' or not(SENSOR_ACTIVE):
            return pygame.mouse.get_pos()
        else:
            f_out = self.daq.get_force()
            return (self.SENSOR_INPUT_OFFSET[0]+self.NEWTONS_2_PIXEL*f_out[0], 
                    self.SENSOR_INPUT_OFFSET[1]+self.NEWTONS_2_PIXEL*f_out[1])


    def set_trial(self):
        self.set_dof(self.next_dof)
        self.target.set_fb_mode(self.next_ir)
        self.set_training_mode(self.next_visible)


    def set_noise(self):
        if self.next_noise == 'good':
            self.noise_var = self.NOISE_VAR_GOOD
        elif self.next_noise == 'bad':
            self.noise_var = self.NOISE_VAR_BAD


    def set_training_mode(self, bool_arg):
        self.training_mode = bool_arg


    def set_tr(self, tr):
        if tr == 0.125:
            self.tr = 0.125
            self.timers['tr'] = self.timers['tr_8hz']
        elif tr == 0.25:
            self.tr = 0.25
            self.timers['tr'] = self.timers['tr_4hz']
        elif tr == 0.5:
            self.tr = 0.5
            self.timers['tr'] = self.timers['tr_2hz']
        elif tr == 1:
            self.tr = 1.
            self.timers['tr'] = self.timers['tr_1hz']
        elif tr == 2:
            self.tr = 2.
            self.timers['tr'] = self.timers['tr_p5hz']
        elif tr == 4:
            self.tr = 4.
            self.timers['tr'] = self.timers['tr_p25hz']


    def init_timers(self):
        self.timers['signal'] = Timer(self.SAMPLE_PERIOD)
        self.timers['tr_8hz'] = Timer(self.TR_LIST[0])
        self.timers['tr_4hz'] = Timer(self.TR_LIST[1])
        self.timers['tr_2hz'] = Timer(self.TR_LIST[2])
        self.timers['tr_1hz'] = Timer(self.TR_LIST[3])
        self.timers['tr_p5hz'] = Timer(self.TR_LIST[4])
        self.timers['tr_p25hz'] = Timer(self.TR_LIST[5])
        self.timers['tr'] = self.timers['tr_2hz']
        self.timers['reach'] = Timer(0)
        self.timers['reach_hold'] = Timer(self.REACH_SUCCESS_TIME)
        self.timers['block'] = Timer(self.BLOCK_TIME)
        self.timers['reset_hold'] = Timer(self.RESET_HOLD_TIME)


    def reset_all_timers(self):
        for k,t in self.timers.iteritems():
            t.reset()


    def check_input(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    self.quit()
                elif event.key == pygame.K_0:
                    self.daq.set_volts_zero()
                elif event.key == pygame.K_f:
                    if self.dof == 1:
                        self.set_dof(2)
                    elif self.dof == 2:
                        self.set_dof(1)
                elif event.key == pygame.K_b:
                    self.target.set_new_target()
                elif event.key == pygame.K_v:
                    self.training_mode = not(self.training_mode)
                elif event.key == pygame.K_c:
                    self.target.set_fb_mode('hrf')
                elif event.key == pygame.K_x:
                    self.target.set_fb_mode('impulse')
                elif event.key == pygame.K_m:
                    if self.input_mode == 'mouse':
                        self.input_mode = 'sensor'
                    elif self.input_mode == 'sensor':
                        self.input_mode = 'mouse'

    def run_timed(self):
        ###########################################
        # main loop for time-to-target experiment #
        ###########################################
        while True:
            time_passed = self.clock.tick_busy_loop(self.FRAME_RATE)
            self.check_input()
            self.input_pos = self.get_pos()
            self.cursor.update(self.input_pos)
            self.target.update(self.cursor.pos)


            if not(self.timers['reset_hold'].time_limit_hit):
                gr.check_in_start(self, time_passed)
                self.target.draw_bool = True
            elif not(self.cursor.has_left):
                gr.check_if_left(self)
                self.target.draw_bool = False
            elif self.training_mode:
                self.target.draw_bool = True
            else:
                self.target.draw_bool = False

            if self.cursor.has_left:
                gr.frame_based_updates_timed(self)
                self.timers['signal'].update(time_passed)
                self.timers['tr'].update(time_passed)
                if self.timers['signal'].time_limit_hit:
                    gr.signal_based_updates(self)
                    if self.timers['tr'].time_limit_hit:
                        gr.tr_based_updates(self)

            self.draw_background()
            self.therm.draw(self.cursor.has_left,
                            self.target.error_metric)
            self.target.draw()
            if (self.trial_count == 0
                    and not self.cursor.has_left):
                self.draw_instructions_timed()
            self.cursor.draw()
            pygame.display.flip()

    def run_block(self):
        ###########################################
        # main loop for block feedback experiment #
        ###########################################
        self.target.draw_bool = False 
        if self.playback_bool:
            self.set_dof(2)
        else:
            self.set_dof(1)
        self.target.set_fb_mode('hrf')

        while True:
            time_passed = self.clock.tick_busy_loop(self.FRAME_RATE)
            self.check_input()
            self.input_pos = self.get_pos()
            self.cursor.update(self.input_pos)
            self.target.update(self.cursor.pos)


            if not(self.timers['reset_hold'].time_limit_hit):
                gr.check_in_start(self, time_passed)
                if self.next_target == 'new':
                    self.target.draw_bool = True
            elif not(self.cursor.has_left):
                gr.check_if_left(self)
                self.target.draw_bool = False
            # debugging
            # self.target.draw_bool = True

            if self.cursor.has_left:
                gr.frame_based_updates_block(self)
                self.timers['signal'].update(time_passed)
                self.timers['tr'].update(time_passed)
                self.timers['block'].update(time_passed)
                if self.timers['signal'].time_limit_hit:
                    gr.signal_based_updates(self)
                    if self.timers['tr'].time_limit_hit:
                        gr.tr_based_updates(self)
                        if self.timers['block'].time_limit_hit:
                            gr.block_based_updates(self)

            self.draw_background()
            therm_draw_bool = ((self.cursor.has_left and
                                self.next_feedback == 'continuous')
                               or (self.total_block_count > 0 
                                   and not self.cursor.has_left))
            score = self.target.error_metric
            self.therm.draw(therm_draw_bool,
                            score)
            self.target.draw()
            if not self.cursor.has_left:
                self.draw_instructions_block()
            else:
                self.therm.set_score_color('norm')
            self.cursor.draw()
            pygame.display.flip()

    def run_playback(self):
        while self.move_counter > 0:
            time_passed = self.clock.tick_busy_loop(self.FRAME_RATE)
            self.check_input()
            self.cursor.update(self.playback_pos_buffer[:,
                                                        self.playback_counter])
            self.indicator_rad = int(self.INDIC_RAD_MAX*(
                                     self.playback_time_buffer[self.playback_counter]
                                     /float(self.timers['block'].MAX_TIME)))
            self.draw_background()
            self.therm.draw(True,
                            self.playback_nfb_buffer[self.playback_counter])
            # debugging
            # self.target.draw()
            self.cursor.draw()
            pygame.display.flip()
            self.move_counter -= 1
            self.playback_counter += 1

    def set_dof(self, dof):
        self.dof = dof
        self.cursor.set_dof(dof)
        self.target.set_dof(dof)

    def draw_background(self):
        self.screen.fill(self.bg_color_alt_2)
        gr.cap_indicator_rad(self)
        self.draw_play_area()
        if self.indicator_rad > 0:
            self.draw_indicator()

    def draw_instructions_block(self):
        if self.next_target == 'new':
            top_msg = 'New target'
            top_color = self.GOOD_MSG_COLOR
        else:
            top_msg = 'Same target'
            top_color = self.BAD_MSG_COLOR

        if self.next_feedback == 'continuous':
            mid_msg = 'Continuous feedback'
            mid_color = self.A_MSG_COLOR
        else:
            mid_msg = 'Intermittent feedback'
            mid_color = self.B_MSG_COLOR

        if self.next_noise == 'good':
            btm_msg = 'Good signal'
            btm_color = self.A_MSG_COLOR
        else:
            btm_msg = 'Bad signal'
            btm_color = self.B_MSG_COLOR

        gg.draw_msg(self.screen, top_msg,
                    color=top_color,
                    center=(self.screen_mid[0],
                            self.screen_mid[1]-75))
        gg.draw_msg(self.screen, mid_msg,
                    color=mid_color,
                    center=(self.screen_mid[0],
                            self.screen_mid[1]))
        if not self.playback_bool:
            gg.draw_msg(self.screen, btm_msg,
                        color=btm_color,
                        center=(self.screen_mid[0],
                                self.screen_mid[1]+75))

    def draw_instructions_timed(self):
        if self.next_visible:
            top_msg = 'Target visible'
            top_color = self.GOOD_MSG_COLOR
        else:
            top_msg = 'Target invisible'
            top_color = self.BAD_MSG_COLOR
        if self.next_ir == 'impulse':
            btm_msg = 'Instant feedback'
            btm_color = self.GOOD_MSG_COLOR
        else:
            btm_msg = 'Delayed feedback'
            btm_color = self.BAD_MSG_COLOR
        gg.draw_msg(self.screen, top_msg,
                    color=top_color,
                    center=(self.screen_mid[0],
                            self.screen_mid[1]-50))
        gg.draw_msg(self.screen, btm_msg,
                    color=btm_color,
                    center=(self.screen_mid[0],
                            self.screen_mid[1]+50))

    def draw_play_area(self):
        if self.dof == 1:
            gg.draw_center_rect(self.screen,
                                self.SCREEN_HEIGHT, self.SCREEN_HEIGHT,
                                self.bg_color,
                                self.screen_mid[0], self.screen_mid[1])

            gg.draw_center_rect(self.screen, 
                                self.TARGET_DIST[1]-self.TARGET_DIST[0],
                                self.SCREEN_HEIGHT,
                                self.bg_color_alt,
                                (self.TARGET_DIST[0]
                                 +0.5*(self.TARGET_DIST[1]
                                       -self.TARGET_DIST[0])
                                 + self.START_COORDS[0]),
                                self.screen_mid[1])

            gg.draw_center_rect(self.screen,
                                2*(self.START_DIST-self.cursor.RAD),
                                self.SCREEN_HEIGHT,
                                self.bg_color_alt_2,
                                self.START_COORDS[0],  self.screen_mid[1])

        elif self.dof == 2:
            gg.draw_center_rect(self.screen,
                                self.SCREEN_HEIGHT, self.SCREEN_HEIGHT,
                                self.bg_color,
                                self.screen_mid[0], self.screen_mid[1])

            gg.draw_filled_aacircle(self.screen,
                                    self.TARGET_DIST[1],
                                    self.bg_color_alt,
                                    self.START_COORDS[0], self.START_COORDS[1])

            gg.draw_filled_aacircle(self.screen,
                                    self.TARGET_DIST[0],
                                    self.bg_color,
                                    self.START_COORDS[0], self.START_COORDS[1])

            gg.draw_center_rect(self.screen,
                                self.SCREEN_HEIGHT, 2*self.BUFFER_DIST,
                                self.bg_color,
                                self.screen_mid[0], self.GAME_ORIGIN[1])
            gg.draw_center_rect(self.screen,
                                2*self.BUFFER_DIST, self.SCREEN_HEIGHT,
                                self.bg_color,
                                self.GAME_ORIGIN[0], self.screen_mid[1])

            gg.draw_filled_aacircle(self.screen,
                                    self.START_DIST-self.cursor.RAD,
                                    self.bg_color_alt_2,
                                    self.START_COORDS[0], self.START_COORDS[1])

    def draw_indicator(self):
        if self.dof == 1:
            gg.draw_center_rect(self.screen,
                                2*self.indicator_rad, self.SCREEN_HEIGHT,
                                self.indicator_color,
                                self.START_COORDS[0], self.screen_mid[1])
        elif self.dof == 2:
            gg.draw_filled_aacircle(self.screen,
                                    self.indicator_rad,
                                    self.indicator_color,
                                    self.START_COORDS[0], self.START_COORDS[1])


    def quit(self):
        sys.exit()