def updateparams(self, i): """Updates stimulus parameters, given sweep table index i""" if i == None: # do a blank sweep self.tsp.on = False # turn off the movie, leave all other parameters unchanged self.postval = C.MAXPOSTABLEINT # posted to DT port to indicate a blank sweep self.nvsyncs = sec2intvsync(self.blanksweeps.sec) # this many vsyncs for this sweep self.npostvsyncs = 0 # this many post-sweep vsyncs for this sweep, blank sweeps have no post-sweep delay else: # not a blank sweep self.tsp.on = True # ensure texture stimulus is on self.postval = i # sweep table index will be posted to DT port self.nvsyncs = sec2intvsync(self.st.sweepSec[i]) # this many vsyncs for this sweep self.npostvsyncs = sec2intvsync(self.st.postsweepSec[i]) # this many post-sweep vsyncs for this sweep # Update texture frame = self.frames[self.st.framei[i]] # get the frame for this sweep #frame = self[self.st.framei[i]] # get the frame for this sweep if self.st.invert[i]: frame = 255 - frame # give the frame inverted polarity self.to.put_sub_image(frame, data_format=gl.GL_LUMINANCE, data_type=gl.GL_UNSIGNED_BYTE) # Update texturestimulus self.tsp.angle = self.static.orioff + self.st.ori[i] self.tsp.position = self.xorig+deg2pix(self.st.xposDeg[i]), self.yorig+deg2pix(self.st.yposDeg[i]) # Update background parameters self.bgp.color = self.st.bgbrightness[i], self.st.bgbrightness[i], self.st.bgbrightness[i], 1.0 # Update fixationspot self.fsp.on = bool(self.st.fixationspotDeg[i]) self.fsp.size = deg2pix(self.st.fixationspotDeg[i]), deg2pix(self.st.fixationspotDeg[i])
def __init__(self, *args, **kwargs): super(ForagingSweeps, self).__init__(*args, **kwargs) self.width = deg2pix(self.static.widthDeg) # do this here so it doesn't have to be done repeatedly in self.updateparams() self.height = deg2pix(self.static.heightDeg) self.terrain = self.static.terrain #square initialization self.brightness = self.terrain.color #set up encoder self.static.encoder.start() self.lastDx = 0 if self.static.encoder.getVin() < 1: print 'Encoder not connected or powered on.' #set up reward self.framescorrect = 0 self.static.reward.start() #set up data logs self.laps = [] self.rewards = [] self.posx = [] self.dx = [] self.terrainlog = []
def updateparams(self, i): """Updates stimulus parameters, given sweep table index i""" if i == None: # do a blank sweep self.tp.on = False # turn off the target, leave all other parameters unchanged self.postval = C.MAXPOSTABLEINT # posted to DT port to indicate a blank sweep self.nvsyncs = sec2intvsync(self.blanksweeps.sec) # this many vsyncs for this sweep self.npostvsyncs = 0 # this many post-sweep vsyncs for this sweep, blank sweeps have no post-sweep delay else: # not a blank sweep self.tp.on = True # ensure stimulus is on self.postval = i # sweep table index will be posted to DT port self.nvsyncs = sec2intvsync(self.st.sweepSec[i]) # this many vsyncs for this sweep self.npostvsyncs = sec2intvsync(self.st.postsweepSec[i]) # this many post-sweep vsyncs for this sweep # Generate position as a f'n of vsynci for this sweep, even if speedDegSec is 0 distance = degSec2pixVsync(self.st.speedDegSec[i]) * self.nvsyncs # total distance to travel on this sweep direction = self.static.orioff + self.st.ori[i] + 90 # direction to travel on this sweep, always |_ to current ori xdistance = distance * math.cos(direction / 180 * math.pi) ydistance = distance * math.sin(direction / 180 * math.pi) xstep = xdistance / self.nvsyncs # pix to travel per vsync ystep = ydistance / self.nvsyncs x0 = self.xorig - xdistance / 2 y0 = self.yorig - ydistance / 2 self.x = x0 + xstep * np.arange(self.nvsyncs) + deg2pix(self.st.xposDeg[i]) # deg2pix returns 0 if deg is None self.y = y0 + ystep * np.arange(self.nvsyncs) + deg2pix(self.st.yposDeg[i]) # Update non-positional target parameters self.tp.orientation = self.static.orioff + self.st.ori[i] self.tp.size = deg2pix(self.st.widthDeg[i]), deg2pix(self.st.heightDeg[i]) self.tp.color = self.st.rbrightness[i], self.st.gbrightness[i], self.st.bbrightness[i], 1.0 self.tp.anti_aliasing = self.st.antialiase[i] # Update background parameters self.bgp.color = self.st.bgbrightness[i], self.st.bgbrightness[i], self.st.bgbrightness[i], 1.0
def updateparams(self, i): """Updates stimulus parameters, given sweep table index i""" if i == None: # do a blank sweep self.tp.on = False # turn off the target, leave all other parameters unchanged self.postval = C.MAXPOSTABLEINT # posted to DT port to indicate a blank sweep self.nvsyncs = sec2intvsync(self.blanksweeps.sec) # this many vsyncs for this sweep self.npostvsyncs = 0 # this many post-sweep vsyncs for this sweep, blank sweeps have no post-sweep delay else: # not a blank sweep self.tp.on = True # ensure stimulus is on self.postval = i # sweep table index will be posted to DT port self.nvsyncs = sec2intvsync(self.st.sweepSec[i]) # this many vsyncs for this sweep self.npostvsyncs = sec2intvsync(self.st.postsweepSec[i]) # this many post-sweep vsyncs for this sweep # Update target position ori = self.static.orioff + self.st.ori[i] theta = ori / 180 * pi dxi = self.st.xi[i] - self.xi0 # destination index - origin index dyi = self.st.yi[i] - self.yi0 sintheta = math.sin(theta) costheta = math.cos(theta) dx = dxi*self.barWidth*costheta - dyi*self.barHeight*sintheta # see SparseNoise.png for the trigonometry dy = dxi*self.barWidth*sintheta + dyi*self.barHeight*costheta x = self.xorig + deg2pix(self.st.xposDeg[i]) + dx y = self.yorig + deg2pix(self.st.yposDeg[i]) + dy self.tp.position = (x, y) # Update non-positional target parameters self.tp.orientation = ori self.tp.color = self.st.brightness[i], self.st.brightness[i], self.st.brightness[i], 1.0 self.tp.anti_aliasing = self.st.antialiase[i] # Update background parameters self.bgp.color = self.st.bgbrightness[i], self.st.bgbrightness[i], self.st.bgbrightness[i], 1.0
def build(self): """Builds the SweepTable and the Header for this Experiment""" super(SparseNoise, self).build() self.header.NVS.data[C.STT_STS] = 11 # enter NVS stimulus code for sparse noise (actually, 'reverse correlation') self.barWidth = deg2pix(self.static.widthDeg / self.static.ncellswide) # in pix self.barHeight = deg2pix(self.static.heightDeg / self.static.ncellshigh) # in pix self.xi0 = (self.static.ncellswide - 1) / 2 # center of grid, in units of 0-based cell index self.yi0 = (self.static.ncellshigh - 1) / 2
def loadManbar(self, n): """Load Manbar n setting in dimstim config file and assign it to the current manual bar""" mbn = "Manbar" + str(n) self.x = intround( deg2pix(dc.get(mbn, "xorigDeg")) + I.SCREENWIDTH / 2 ) # int pix, since pygame.mouse pos works with ints self.y = intround(deg2pix(dc.get(mbn, "yorigDeg")) + I.SCREENHEIGHT / 2) self.widthDeg = dc.get(mbn, "widthDeg") self.heightDeg = dc.get(mbn, "heightDeg") self.ori = dc.get(mbn, "orioff") self.fp.position = self.x, self.y
def updatestimuli(self): """Update stimuli""" # Update target params width = deg2pix(self.widthDeg) # convenience height = deg2pix(self.heightDeg) self.tp.position = self.x, self.y self.tp.size = width, height # convert to pix self.tp.orientation = self.ori self.tp.color = (self.brightness, self.brightness, self.brightness, 1.0) self.bgp.color = (self.bgbrightness, self.bgbrightness, self.bgbrightness, 1.0) self.cp.position = self.x, self.y # update center spot position self.wlp.position = I.SCREENWIDTH/2 - self.terrain.windowwidth, self.y #DW self.wrp.position = I.SCREENWIDTH/2 + self.terrain.windowwidth, self.y #DW # Update grating parms if grating is turned on if self.gp.on: self.gp.position = I.SCREENWIDTH/2,I.SCREENHEIGHT/2 self.gp.orientation = self.gratingori sfreq = cycDeg2cycPix(self.sfreqCycDeg) try: self.phase except AttributeError: # phase hasn't been init'd yet """phaseoffset is req'd to make phase0 the initial phase at the centre of the grating, instead of at the edge of the grating as VE does. Take the distance from the centre to the edge along the axis of the sinusoid (which in this case is the height), multiply by spatial freq to get numcycles between centre and edge, multiply by 360 deg per cycle to get req'd phaseoffset. THE EXTRA 180 DEG IS NECESSARY FOR SOME REASON, DON'T REALLY UNDERSTAND WHY, BUT IT WORKS!!!""" phaseoffset = self.gratingHeight / 2 * sfreq * 360 + 180 self.phase = -self.phase0 - phaseoffset phasestep = cycSec2cycVsync(self.tfreqCycSec * self.nscreens) * 360 # delta cycles per vsync, in degrees of sinusoid, adjust for buffer flips on multiple screens self.phase = self.phase - phasestep # update phase self.gp.spatial_freq = sfreq self.gp.phase_at_t0 = self.phase self.gp.contrast = self.contrast self.bgp.color = (self.bgbrightness, self.bgbrightness, self.bgbrightness, 1.0) self.cp.position = I.SCREENWIDTH/2, I.SCREENHEIGHT/2 # update center spot position # Update text params self.mbtp.text = 'x, y = (%5.1f, %5.1f) deg | size = (%.1f, %.1f) deg | ori = %5.1f deg' \ % ( pix2deg(self.x - I.SCREENWIDTH / 2), pix2deg(self.y - I.SCREENHEIGHT / 2), self.widthDeg, self.heightDeg, self.ori) self.dtp.text = "%i Rewards | %.1f Rewards/minute " \ % (self.reward.rewardcount, self.reward.rewardcount/(self.sc.secondselapsed()+0.001)*60) #reward counter self.ttp.text = "Session time : %s" % str(self.sc.elapsed())[:10] #shows session time if self.brightenText == 'Manbar0': self.mbtp.color = (1.0, 1.0, 0.0, 1.0) # set to yellow elif self.brightenText == 'Manbar1': self.mbtp.color = (1.0, 0.0, 0.0, 1.0) # set to red elif self.brightenText == 'Eye': self.stp.color = (1.0, 0.0, 0.0, 1.0) # set to red else: self.mbtp.color = (0.0, 1.0, 0.0, 1.0) # set it back to green
def build(self): """Builds the SweepTable and the Header for this Experiment""" # Build the sweep table self.sweeptable = Core.SweepTable(experiment=self) self.st = self.sweeptable.data # synonym, used a lot by Experiment subclasses # Do time and space conversions of applicable static and dynamic parameters - or maybe do this in init - is this really necessary, can't it be done on the fly, or would that be too slow? If too slow, write it inline in C and use scipy.weave? # Is there a better place to store these, rather than polluting self namespace? self.xorig = deg2pix(self.static.xorigDeg) + I.SCREENWIDTH / 2 # do this once, since it's static, save time in main loop self.yorig = deg2pix(self.static.yorigDeg) + I.SCREENHEIGHT / 2 self.y = self.static.ypos self.x = self.xorig self.offscreen = self.off_screen_distance(self.static.terrain.orientation) self.encDeg = self.static.encoder.getDegrees() # Calculate Experiment duration self.sec = self.calcduration() info('Expected experiment duration: %s' % isotime(self.sec, 6), tolog=False)
def build(self): """Builds the SweepTable and the Header for this Experiment""" self.sweeptable = Core.SweepTable(experiment=self) self.st = self.sweeptable.data # synonym, used a lot by Experiment subclasses self.barWidth = deg2pix(self.static.widthDeg / self.static.ncellswide) # in pix self.barHeight = deg2pix(self.static.heightDeg / self.static.ncellshigh) # in pix self.xi0 = (self.static.ncellswide - 1) / 2 # center of grid, in units of 0-based cell index self.yi0 = (self.static.ncellshigh - 1) / 2 self.sec = self.calcduration() self.xorig = deg2pix(self.static.xorigDeg) + I.SCREENWIDTH / 2 # do this once, since it's static, save time in main loop self.yorig = deg2pix(self.static.yorigDeg) + I.SCREENHEIGHT / 2 self.y = self.static.ypos self.x = self.xorig
def createstimuli(self): """Creates the VisionEgg stimuli objects for this Experiment subclass""" super(Grating, self).createstimuli() # Create instances of the Mask2D class, one for each diameter if self.static.mask: print 'Generating masks', self.nmasksamples = 512 # number of samples in mask, must be power of 2, quality/performance tradeoff self.masks = {} # init a dictionary samplesperpix = self.nmasksamples / deg2pix(min(self.static.widthDeg, self.static.heightDeg)) for diameterDeg in toiter(self.dynamic.diameterDeg): radius = deg2pix(diameterDeg / 2) # in pix radiusSamples = samplesperpix * radius # in mask samples self.masks[diameterDeg] = Mask2D(function=self.static.mask, radius_parameter=radiusSamples, # sigma for gaussian, radius for circle, in units of mask samples num_samples=(self.nmasksamples, self.nmasksamples)) # size of mask texture data (# of texels) print '.', print else: self.masks = None self.nsinsamples = 2048 # number of samples of sine f'n, must be power of 2, quality/performance tradeoff self.grating = SinGrating2D(position=(self.xorig, self.yorig), # init to orig, anchor='center', size=(deg2pix(self.static.heightDeg), deg2pix(self.static.widthDeg)), # VE defines grating ori as direction of motion of grating, but we want it to be the orientation of the grating elements, so add 90 deg (this also makes grating ori def'n correspond to bar ori def'n). This means that width and height have to be swapped ignore_time=True, # don't use this class' own time f'n #mask=self.masks.values()[0], # init to a random mask in maskobjects num_samples=self.nsinsamples, max_alpha=1.0, # opaque on=False) # keep it off until first sweep starts self.gp = self.grating.parameters if self.static.syncsq: self.sync = Target2D(anchor='center', anti_aliasing=False, color=(0.0, 0.0, 0.0, 1.0), position = self.static.syncsqloc, on = True, size = (100,100)) self.sp = self.sync.parameters if self.static.syncsq: self.stimuli = (self.background, self.grating, self.sync) # last entry will be topmost layer in viewport else: self.stimuli = (self.background, self.grating) # last entry will be topmost layer in viewport
def build(self): """Builds the SweepTable and the Header for this Experiment""" # Build the sweep table self.sweeptable = Core.SweepTable(experiment=self) self.st = self.sweeptable.data # synonym, used a lot by Experiment subclasses # Do time and space conversions of applicable static and dynamic parameters - or maybe do this in init - is this really necessary, can't it be done on the fly, or would that be too slow? If too slow, write it inline in C and use scipy.weave? # Is there a better place to store these, rather than polluting self namespace? self.xorig = deg2pix(self.static.xorigDeg) + I.SCREENWIDTH / 2 # do this once, since it's static, save time in main loop self.yorig = deg2pix(self.static.yorigDeg) + I.SCREENHEIGHT / 2 # Calculate Experiment duration self.sec = self.calcduration() info('Expected experiment duration: %s' % isotime(self.sec, 6), tolog=False) # Build the Surf file header, the NVS header, and the text header self.header = Core.Header(experiment=self) info('TextHeader.data:', toscreen=False) printf2log(str(self.header.text)) # print text header data to log
def createstimuli(self): """Creates the VisionEgg stimuli objects for this Experiment subclass""" super(Movie, self).createstimuli() # Create an instance of the Mask2D class if self.static.mask: self.nmasksamples = 512 # number of samples in mask, must be power of 2, quality/performance tradeoff samplesperpix = self.nmasksamples / deg2pix(min(self.static.widthDeg, self.static.heightDeg)) radius = deg2pix(self.static.diameterDeg / 2) # in pix radiusSamples = samplesperpix * radius # in mask samples self.mask2d = Mask2D(function=self.static.mask, radius_parameter=radiusSamples, # sigma for gaussian, radius for circle, in units of mask samples num_samples=(self.nmasksamples, self.nmasksamples)) # size of mask texture data (# of texels) else: self.mask2d = None self.texture = Texture(self.frames[self.st.framei[0]]) # init texture to frame of first sweep in sweep table self.texturestimulus = TextureStimulus(texture=self.texture, position=(self.xorig, self.yorig), # init to orig anchor='center', # texture is scaled to this size: size=(deg2pix(self.static.widthDeg), deg2pix(self.static.heightDeg)), mask=self.mask2d, max_alpha=1.0, mipmaps_enabled=False, # ? texture_min_filter=gl.GL_NEAREST, # ? texture_mag_filter=gl.GL_NEAREST, # ? on=False) # leave it off for now self.fixationspot = ve.Core.FixationSpot(position=(self.xorig, self.yorig), anchor='center', color=(255, 0, 0, 0), size=(1, 1), on=False) # leave it off for now self.stimuli = (self.background, self.texturestimulus, self.fixationspot) # last entry will be topmost layer in viewport self.tsp = self.texturestimulus.parameters # synonym self.to = self.tsp.texture.get_texture_object() self.fsp = self.fixationspot.parameters
def updatestimuli(self): """Update stimuli""" # Update target params width = deg2pix(self.widthDeg) # convenience height = deg2pix(self.heightDeg) self.tp.position = self.x, self.y self.tp.size = width, height # convert to pix self.tp.orientation = self.ori self.tp.color = (self.brightness, self.brightness, self.brightness, 1.0) self.bgp.color = (self.bgbrightness, self.bgbrightness, self.bgbrightness, 1.0) self.tipp.position = ( self.x + width / 2 * math.cos(math.pi / 180 * self.ori), self.y + width / 2 * math.sin(math.pi / 180 * self.ori), ) self.tipp.orientation = self.ori self.cp.position = self.x, self.y # update center spot position # Update text params self.mbtp.text = "x, y = (%5.1f, %5.1f) deg | size = (%.1f, %.1f) deg | ori = %5.1f deg" % ( pix2deg(self.x - I.SCREENWIDTH / 2), pix2deg(self.y - I.SCREENHEIGHT / 2), self.widthDeg, self.heightDeg, self.ori, ) self.stp.text = "Eye open: %s | " % C.EYESTATES[self.eyei] + self.screenstring if self.brightenText == "Manbar0": self.mbtp.color = (1.0, 1.0, 0.0, 1.0) # set to yellow elif self.brightenText == "Manbar1": self.mbtp.color = (1.0, 0.0, 0.0, 1.0) # set to red elif self.brightenText == "Eye": self.stp.color = (1.0, 0.0, 0.0, 1.0) # set to red else: self.mbtp.color = (0.0, 1.0, 0.0, 1.0) # set it back to green self.stp.color = (0.0, 1.0, 1.0, 1.0) # set it back to cyan if self.squarelock: self.sltp.on = True else: self.sltp.on = False
def updateparams(self, i): """Updates stimulus parameters, given sweep table index i""" if i == None: # do a blank sweep self.gp.on = False # turn off the grating, leave all other parameters unchanged self.postval = C.MAXPOSTABLEINT # posted to DT port to indicate a blank sweep self.nvsyncs = sec2intvsync(self.blanksweeps.sec) # this many vsyncs for this sweep self.npostvsyncs = 0 # this many post-sweep vsyncs for this sweep, blank sweeps have no post-sweep delay else: # not a blank sweep self.gp.on = True # ensure grating is on self.postval = i # sweep table index will be posted to DT port self.nvsyncs = sec2intvsync(self.st.sweepSec[i]) # this many vsyncs for this sweep self.npostvsyncs = sec2intvsync(self.st.postsweepSec[i]) # this many post-sweep vsyncs for this sweep """Generate phase as a f'n of vsynci for this sweep sine grating eq'n used by VE: luminance(x) = 0.5*contrast*sin(2*pi*sfreqCycDeg*x + phaseRad) + ml ...where x is the position in deg along the axis of the sinusoid, and phaseRad = phaseDeg/180*pi. Motion in time is achieved by changing phaseDeg over time. phaseDeg inits to phase0""" sfreq = cycDeg2cycPix(self.st.sfreqCycDeg[i]) # convert it just once, reuse it for this sweep """phaseoffset is req'd to make phase0 the initial phase at the centre of the grating, instead of at the edge of the grating as VE does. Take the distance from the centre to the edge along the axis of the sinusoid (which in this case is the height), multiply by spatial freq to get numcycles between centre and edge, multiply by 360 deg per cycle to get req'd phaseoffset. THE EXTRA 180 DEG IS NECESSARY FOR SOME REASON, DON'T REALLY UNDERSTAND WHY, BUT IT WORKS!!!""" phaseoffset = self.height / 2 * sfreq * 360 + 180 phasestep = cycSec2cycVsync(self.st.tfreqCycSec[i]) * 360 # delta cycles per vsync, in degrees of sinusoid self.phase = -self.st.phase0[i] - phaseoffset - phasestep * np.arange(self.nvsyncs) # array of phases for this sweep. -ve makes the grating move in +ve direction along sinusoidal axis, see sin eq'n above # Update grating stimulus self.gp.position = self.xorig+deg2pix(self.st.xposDeg[i]), self.yorig+deg2pix(self.st.yposDeg[i]) self.gp.orientation = self.static.orioff + self.st.ori[i] + 90 # VE defines grating ori as direction of motion of grating, but we want it to be the orientation of the grating elements, so add 90 deg (this also makes grating ori def'n correspond to bar ori def'n). This means that width and height have to be swapped (done at creation of grating) if self.masks: self.gp.mask = self.masks[self.st.diameterDeg[i]] self.gp.spatial_freq = sfreq self.gp.pedestal = self.st.ml[i] if self.st.contrastreverse[i]: contraststep = cycSec2cycVsync(self.st.cfreqCycSec[i])*self.st.contrast[i]*2 self.contrast =self.st.contrast[i]*np.sin(contraststep * np.arange(self.nvsyncs)) else: self.gp.contrast = self.st.contrast[i] # Update background parameters self.bgp.color = self.st.bgbrightness[i], self.st.bgbrightness[i], self.st.bgbrightness[i], 1.0
def off_screen_distance(self, orientation = 0): '''Gets off screen distance using formula to compensate for orientation of object ''' x = deg2pix(self.static.terrain.objectwidthDeg) # converts width of object to pixels from degrees dist = orientation/45*(np.sqrt(2*(x)**2)-x) + x #pythagorean theorem return dist/2 #float divide by two because measurement is from center of object
def __init__(self, *args, **kwargs): super(Grating, self).__init__(*args, **kwargs) self.width = deg2pix(self.static.widthDeg) # do this here so it doesn't have to be done repeatedly in self.updateparams() self.height = deg2pix(self.static.heightDeg)