Ejemplo n.º 1
0
 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)
Ejemplo n.º 2
0
 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)
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
 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)
Ejemplo n.º 5
0
 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)
Ejemplo n.º 6
0
 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'))
Ejemplo n.º 7
0
    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))
Ejemplo n.º 8
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}