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 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