def make_circle(self, angle_deg=360): ls = LineSegs() angle_radians = np.deg2rad(angle_deg) # assume visual angle is approximately the same for x and y, # which probably is not true, maybe need to change #radius = 1 * Positions().visual_angle()[0] res = [1024, 768] # Screen size screen = [1337, 991] v_dist = 1219 # number of visual angles want circle radius to be # (so twice this is the x and y of the square) angle = 0.25 # visual angle returns degrees per pixel, so invert since # we want pixel per degree deg_per_pixel = visual_angle(screen, res, v_dist) x_radius = angle * 1 / deg_per_pixel[0] y_radius = angle * 1 / deg_per_pixel[0] for i in range(50): a = angle_radians * i / 49 y = y_radius * np.sin(a) #print y x = x_radius * np.cos(a) #print x ls.drawTo(x, self.depth, y) #node = ls.create() node = self.base.render.attachNewNode(ls.create(True)) return NodePath(node)
def test_visual_angle(self): # visual angle returns deg_per_pix, # check with projector parameters # screen_size = [1337, 991] # resolution = [1024, 768] screen_size = [1467, 902] resolution = [1280, 800] view_dist = 1219 deg_per_pix = visual_angle(screen_size, resolution, view_dist) print deg_per_pix print 1/deg_per_pix[0] print 1/deg_per_pix[1] # used Fine's tutorial in Matlab to come up with the # numbers for testing #self.assertAlmostEqual(deg_per_pix[0], 0.061369, 6) #self.assertAlmostEqual(deg_per_pix[1], 0.06065, 6) self.assertAlmostEqual(deg_per_pix[0], 0.0539, 4) self.assertAlmostEqual(deg_per_pix[1], 0.0530, 4)
def test_visual_angle(self): # visual angle returns deg_per_pix, # check with projector parameters # screen_size = [1337, 991] # resolution = [1024, 768] screen_size = [1467, 902] resolution = [1280, 800] view_dist = 1219 deg_per_pix = visual_angle(screen_size, resolution, view_dist) print deg_per_pix print 1 / deg_per_pix[0] print 1 / deg_per_pix[1] # used Fine's tutorial in Matlab to come up with the # numbers for testing #self.assertAlmostEqual(deg_per_pix[0], 0.061369, 6) #self.assertAlmostEqual(deg_per_pix[1], 0.06065, 6) self.assertAlmostEqual(deg_per_pix[0], 0.0539, 4) self.assertAlmostEqual(deg_per_pix[1], 0.0530, 4)
def test_visual_angle_positions(self): # make sure furthest out positions are plotted are at the correct visual angle # for this test, just test whatever is in the config file, and make sure it # is following whatever is there, regardless of if that is really correct (more # likely it is me sitting a foot or two away from the laptop, but not going to change # the view_dist and screen size since it doesn't really matter... max_x = self.config['MAX_DEGREES_X'] max_y = self.config['MAX_DEGREES_Y'] deg_per_pixel = visual_angle(self.config['SCREEN'], self.config['WIN_RES'], self.config['VIEW_DIST']) # # Key 9 should get you the max visual degrees for both x and y. Of course, it will # really be farther than the max visual angle, since we are maximizing both x and y, # but as long as the cardinal directions are the right visual angle, we understand the # corners are really further out, and will take this under consideration pos = Positions(self.config) pos_9 = pos.get_key_position(self.depth, key=9) #print pos_9 self.assertAlmostEqual(pos_9[0], max_x / deg_per_pixel[0], 4) self.assertAlmostEqual(pos_9[2], max_y / deg_per_pixel[1], 4)
def change_square_size(self): pos = None if self.mode == 0: self.config['MAX_DEGREES_X'] = 20 self.config['MAX_DEGREES_Y'] = 20 self.make_circle() pos = Positions(self.config).get_position(self.depth) self.mode = 1 res = [1024, 768] # Screen size screen = [1337, 991] v_dist = 1219 b = 0 scale = 1 size_list = [] for i, j in enumerate(pos): #b += 0.04 # covers all of the values if using 25 points #b += 0.08 b += 0.03 scale += 0.5 #print b #print i #print j square = self.make_square(scale) square.setPos(Point3(j)) #print square.getPos() #print square.getTightBounds() sq_min, sq_max = square.getTightBounds() size = sq_max - sq_min #print size[0], size[2] deg_per_pixel = visual_angle(screen, res, v_dist) #print deg_per_pixel print scale print 'size in degrees, x', size[0] * deg_per_pixel[0] print 'size in degrees, y', size[2] * deg_per_pixel[1] size_list.append(size[0] * deg_per_pixel[0]) print size_list import pickle pickle.dump(size_list, open('size_list', 'wb'))
def setup_windows(self): # get properties for setting up researchers window props = WindowProperties() # props.setForeground(True) props.setCursorHidden(True) try: self.base.win.requestProperties(props) # print props except AttributeError: print 'Cannot open second window. To open just one window, ' \ 'change the resolution in the config file to Test ' \ 'or change the resolution to None for default Panda window' # go ahead and give the traceback and exit raise # Need to get this better. keypress only works with one window. # plus looks ugly. window2 = self.base.openWindow() window2.setClearColor((115 / 255, 115 / 255, 115 / 255, 1)) # resolution of window for actual calibration resolution = self.config['WIN_RES'] eye_res = self.config['EYE_RES'] # if resolution given, set the appropriate resolution # otherwise assume want small windows if resolution is not None: # properties for second window props.setOrigin(-int(eye_res[0]), 0) # props.setOrigin(0, 0) # resolution for second window, one for plotting eye data # props.setSize(1024, 768) props.setSize(int(eye_res[0]), int(eye_res[1])) else: resolution = [ 800, 600 ] # if no resolution given, assume normal panda window props.setOrigin( 600, 200) # make it so windows aren't on top of each other # x and y are pretty damn close, so just use x # degree per pixel is important only for determining where to plot squares and # determining tolerance, but no effect on actual eye position plotting, uses projector # resolution, screen size, etc self.deg_per_pixel = visual_angle(self.config['SCREEN'], resolution, self.config['VIEW_DIST'])[0] # print 'deg_per_pixel', self.deg_per_pixel # set the properties for eye data window props.set_foreground(False) window2.requestProperties(props) # print 'main', window1.getReqeustedProperties() # print 'researcher', window2.getRequestedProperties() # resolution for main window, subjects monitor self.set_resolution(resolution) # orthographic lens means 2d, then we can set size to resolution # so coordinate system is in pixels lens = OrthographicLens() lens.setFilmSize(int(resolution[0]), int(resolution[1])) # lens.setFilmSize(800, 600) # this allows us to layer, as long as we use between -100 # and 100 for z. (eye position on top of squares) lens.setNearFar(-100, 100) camera = self.base.camList[0] camera.node().setLens(lens) camera.reparentTo(self.base.render) camera2 = self.base.camList[1] camera2.node().setLens(lens) camera2.reparentTo(self.base.render) # set bit mask for eye positions camera.node().setCameraMask(BitMask32.bit(1)) camera2.node().setCameraMask(BitMask32.bit(0))
def __init__(self, mode=None, config_file=None): DirectObject.__init__(self) # Python assumes all input from sys are string, but not # input variables # mode sets whether starts at manual or auto, default is manual, auto is 0 # Start in auto, if, and only if the input number was a zero, if mode == '0' or mode == 0: self.manual = False else: self.manual = True # print('manual', self.manual) if not config_file: config_file = 'config.py' self.pydaq = pydaq # get configurations from config file self.config = {} execfile(config_file, self.config) print 'Subject is', self.config['SUBJECT'] self.config.setdefault('file_name', config_file) print 'Calibration file is', config_file # if subject is test, doing unit tests if self.config['SUBJECT'] == 'test': # doing tests so use fake eye data and testing configuration. # for testing, always leave gain at one, so eye_data and eye_data_to_plot are the same gain = [1, 1] # print 'test' self.testing = True self.use_daq_reward = False else: gain = self.config['GAIN'] self.testing = False self.use_daq_reward = self.config.setdefault('REWARD', True) # default is not fake data, and don't send signal data_type = 'IScan' if self.config.setdefault('FAKE_DATA', False) or not self.pydaq: print('using fake data') data_type = 'Fake Data' self.config.setdefault('SEND_DATA', False) # assume 0.2 seconds for pump delay, if not set self.config.setdefault('PUMP_DELAY', 0.2) # start Panda3d self.base = ShowBase() # default is no subroutines, will fill in later, if using self.sub_index = None self.call_subroutine = [] # This will be the photo object, if we are showing photos self.photos = None # initialize text before setting up second window. # text will be overridden there. # text only happens on second window self.text_nodes = [None] * 5 self.text_dict = dict(Gain=0, Offset=1, Tolerance=3, Manual=4) self.text_dict[data_type] = 2 eye_position = '[0, 0]' data_type = '' # always start with Manual and eye in the center # stuff that changes with plotting offset = [0, 0] # tolerance in degrees, will need to be changed to pixels to be useful, # but since tolerance can change (in degrees), makes sense to do this on the fly self.plot_variables = [ gain, offset, eye_position, self.config['TOLERANCE'], data_type ] # print base.pipe.getDisplayWidth() # print base.pipe.getDisplayHeight() # if window is offscreen (for testing), does not have WindowProperties, # so can't open second window. # if an actual resolution in config file, change to that resolution, # otherwise keep going... if self.config['WIN_RES'] != 'Test': # only set up windows if not testing self.setup_windows() eye_res = self.config['EYE_RES'] position = [ (0 - eye_res[0] / 4, 0, eye_res[1] / 2 - eye_res[1] / 16), (0, 0, eye_res[1] / 2 - eye_res[1] / 16), (0 + eye_res[0] / 4, 0, eye_res[1] / 2 - eye_res[1] / 16), (0, 0, eye_res[1] / 2 - eye_res[1] * 2 / 16), (-eye_res[0] / 2 + eye_res[0] * 1 / 16, 0, eye_res[1] / 2 - eye_res[1] * 1 / 16) ] for k, v in self.text_dict.items(): self.text_nodes[v] = (self.setup_text(k, self.plot_variables[v], position[v])) self.text_dict['Auto'] = 4 else: # resolution in file equal to test, so use the projector screen # value for determining pixels size. In this case, accuracy is not # important, because never actually calibrating with this setup. resolution = [1280, 800] self.deg_per_pixel = visual_angle(self.config['SCREEN'], resolution, self.config['VIEW_DIST'])[0] # print('gain', self.gain) # print 'window loaded' # empty list for plotting eye nodes self.eye_nodes = [] self.current_eye_data = None # initialize file variables self.eye_file_name = '' self.time_file_name = '' self.eye_data_file = None self.time_data_file = None # starts out not fixated, and not checking for fixation (will # check for fixation when stimulus comes on, if we are doing an auto task) self.fixated = False self.base.setBackgroundColor(115 / 255, 115 / 255, 115 / 255) self.base.disableMouse() # create dummy variable for helper objects self.logging = None self.eye_data = None self.sequences = None # initialize list for eye window self.eye_window = [] # set up daq for reward, if on windows and not testing # testing auto mode depends on being able to control eye position self.reward_task = None self.num_beeps = self.config[ 'NUM_BEEPS'] # number of rewards each time self.num_reward = 0 # count number of rewards # Keyboard stuff: # initiate self.key_dict = {"switch": 0, "task_flag": False}