def test_refresh_rate(self): if self.win.winType=='pygame': pytest.skip("getMsPerFrame seems to crash the testing of pygame") #make sure that we're successfully syncing to the frame rate msPFavg, msPFstd, msPFmed = visual.getMsPerFrame(self.win,nFrames=60, showVisual=True) assert (1000/150.0 < msPFavg < 1000/40.0), \ "Your frame period is %.1fms which suggests you aren't syncing to the frame" %msPFavg
def launch_window(params, test_refresh=True, test_tol=.5): """Open up a presentation window and measure the refresh rate.""" # Get the monitor parameters m = WindowInfo(params) stated_refresh_hz = getattr(m, "refresh_hz", None) # Initialize the Psychopy window object win = visual.Window(**m.window_kwargs) # Record the refresh rate we are currently achieving if test_refresh or stated_refresh_hz is None: win.setRecordFrameIntervals(True) logging.console.setLevel(logging.CRITICAL) flip_time, _, _ = visual.getMsPerFrame(win) observed_refresh_hz = 1000 / flip_time # Possibly test the refresh rate against what we expect if test_refresh and stated_refresh_hz is not None: refresh_error = np.abs(stated_refresh_hz - observed_refresh_hz) if refresh_error > test_tol: msg = ("Observed refresh rate differs from expected by {:.3f} Hz" .format(refresh_error)) raise RuntimeError(msg) # Set the refresh rate to use in the experiment if stated_refresh_hz is None: msg = "Monitor configuration does not have refresh rate information" warnings.warn(msg) win.refresh_hz = observed_refresh_hz else: win.refresh_hz = stated_refresh_hz return win
def testRefreshRate(self): if self.win.winType == "pygame": raise nose.plugins.skip.SkipTest("getMsPerFrame seems to crash the testing of pygame") # make sure that we're successfully syncing to the frame rate msPFavg, msPFstd, msPFmed = visual.getMsPerFrame(self.win, nFrames=60, showVisual=True) nose.tools.ok_( 1000 / 150.0 < msPFavg < 1000 / 40.0, "Your frame period is %.1fms which suggests you aren't syncing to the frame" % msPFavg, )
def test_refresh_rate(self): if self.win.winType == "pygame": pytest.skip("getMsPerFrame seems to crash the testing of pygame") # make sure that we're successfully syncing to the frame rate msPFavg, msPFstd, msPFmed = visual.getMsPerFrame(self.win, nFrames=60, showVisual=True) utils.skip_under_travis() # skip late so we smoke test the code assert 1000 / 150.0 < msPFavg < 1000 / 40.0, ( "Your frame period is %.1fms which suggests you aren't syncing to the frame" % msPFavg )
def _setWindowInfo(self, win, verbose=False, refreshTest='grating', usingTempWin=True): """find and store info about the window: refresh rate, configuration info """ if refreshTest in ['grating', True]: msPFavg, msPFstd, msPFmd6 = visual.getMsPerFrame(win, nFrames=120, showVisual=bool(refreshTest == 'grating')) self['windowRefreshTimeAvg_ms'] = msPFavg self['windowRefreshTimeMedian_ms'] = msPFmd6 self['windowRefreshTimeSD_ms'] = msPFstd if usingTempWin: return # These 'configuration lists' control what attributes are reported. # All desired attributes/properties need a legal internal name, e.g., win.winType. # If an attr is callable, its gets called with no arguments, e.g., win.monitor.getWidth() winAttrList = ['winType', '_isFullScr', 'units', 'monitor', 'pos', 'screen', 'rgb', 'size'] winAttrListVerbose = ['allowGUI', 'useNativeGamma', 'recordFrameIntervals', 'waitBlanking', '_haveShaders', '_refreshThreshold'] if verbose: winAttrList += winAttrListVerbose monAttrList = ['name', 'getDistance', 'getWidth', 'currentCalibName'] monAttrListVerbose = ['_gammaInterpolator', '_gammaInterpolator2'] if verbose: monAttrList += monAttrListVerbose if 'monitor' in winAttrList: # replace 'monitor' with all desired monitor.<attribute> i = winAttrList.index('monitor') # retain list-position info, put monitor stuff there del winAttrList[i] for monAttr in monAttrList: winAttrList.insert(i, 'monitor.' + monAttr) i += 1 for winAttr in winAttrList: try: attrValue = eval('win.' + winAttr) except AttributeError: logging.warning('AttributeError in RuntimeInfo._setWindowInfo(): Window instance has no attribute', winAttr) continue if hasattr(attrValue, '__call__'): try: a = attrValue() attrValue = a except: print 'Warning: could not get a value from win.' + winAttr + '() (expects arguments?)' continue while winAttr[0] == '_': winAttr = winAttr[1:] winAttr = winAttr[0].capitalize() + winAttr[1:] winAttr = winAttr.replace('Monitor._', 'Monitor.') if winAttr in ['Pos', 'Size']: winAttr += '_pix' if winAttr in ['Monitor.getWidth', 'Monitor.getDistance']: winAttr += '_cm' if winAttr in ['RefreshThreshold']: winAttr += '_sec' self['window' + winAttr] = attrValue
def _setWindowInfo(self, win, verbose=False, refreshTest='grating', usingTempWin=True): """find and store info about the window: refresh rate, configuration info """ if refreshTest in ['grating', True]: msPFavg, msPFstd, msPFmd6 = visual.getMsPerFrame(win, nFrames=120, showVisual=bool(refreshTest == 'grating')) self['windowRefreshTimeAvg_ms'] = msPFavg self['windowRefreshTimeMedian_ms'] = msPFmd6 self['windowRefreshTimeSD_ms'] = msPFstd if usingTempWin: return # These 'configuration lists' control what attributes are reported. # All desired attributes/properties need a legal internal name, e.g., win.winType. # If an attr is callable, its gets called with no arguments, e.g., win.monitor.getWidth() winAttrList = ['winType', '_isFullScr', 'units', 'monitor', 'pos', 'screen', 'rgb', 'size'] winAttrListVerbose = ['allowGUI', 'useNativeGamma', 'recordFrameIntervals', 'waitBlanking', '_haveShaders', '_refreshThreshold'] if verbose: winAttrList += winAttrListVerbose monAttrList = ['name', 'getDistance', 'getWidth', 'currentCalibName'] monAttrListVerbose = ['getGammaGrid','getLinearizeMethod','_gammaInterpolator', '_gammaInterpolator2'] if verbose: monAttrList += monAttrListVerbose if 'monitor' in winAttrList: # replace 'monitor' with all desired monitor.<attribute> i = winAttrList.index('monitor') # retain list-position info, put monitor stuff there del winAttrList[i] for monAttr in monAttrList: winAttrList.insert(i, 'monitor.' + monAttr) i += 1 for winAttr in winAttrList: try: attrValue = eval('win.' + winAttr) except AttributeError: logging.warning('AttributeError in RuntimeInfo._setWindowInfo(): Window instance has no attribute', winAttr) continue if hasattr(attrValue, '__call__'): try: a = attrValue() attrValue = a except: print 'Warning: could not get a value from win.' + winAttr + '() (expects arguments?)' continue while winAttr[0] == '_': winAttr = winAttr[1:] winAttr = winAttr[0].capitalize() + winAttr[1:] winAttr = winAttr.replace('Monitor._', 'Monitor.') if winAttr in ['Pos', 'Size']: winAttr += '_pix' if winAttr in ['Monitor.getWidth', 'Monitor.getDistance']: winAttr += '_cm' if winAttr in ['RefreshThreshold']: winAttr += '_sec' self['window' + winAttr] = attrValue
def testRefreshRate(self): if self.win.winType == 'pygame': raise nose.plugins.skip.SkipTest( "getMsPerFrame seems to crash the testing of pygame") #make sure that we're successfully syncing to the frame rate msPFavg, msPFstd, msPFmed = visual.getMsPerFrame(self.win, nFrames=60, showVisual=True) nose.tools.ok_( 1000 / 150.0 < msPFavg < 1000 / 40.0, "Your frame period is %.1fms which suggests you aren't syncing to the frame" % msPFavg)
def runDiagnostics(self, win, verbose=False): """Return list of (key, val, msg) tuple, set self.warnings All tuple elements will be of <type str>. msg can depend on val; msg starts with 'Warning:' to indicate a concern. Plain text is returned, expected to be used in html <table>. Hyperlinks can be embedded as <a href="..."> """ report = [] # add item tuples in display order # get lots of info and do quick-to-render visual (want no frames drop): # for me, grating draw times are: mean 0.53 ms, SD 0.77 ms items = info.RunTimeInfo(win=win, refreshTest='grating', verbose=True, userProcsDetailed=True) totalRAM, freeRAM = items['systemMemTotalRAM'], items['systemMemFreeRAM'] if freeRAM == 'unknown': if totalRAM != 'unknown': totalRAM = "%.1fG" % (totalRAM / 1024.) msg = 'could not assess available physical RAM; total %s' % totalRAM report.append(('available memory', 'unknown', msg)) else: msg = 'physical RAM available for configuration test (of %.1fG total)' % (totalRAM / 1024.) if freeRAM < 300: # in M msg = 'Warning: low available physical RAM for configuration test (of %.1fG total)' % (totalRAM / 1024.) report.append(('available memory', str(freeRAM)+'M', msg)) # ----- PSYCHOPY: ----- report.append(('PsychoPy', '', '')) report.append(('psychopy', __version__, 'avoid upgrading during an experiment')) report.append(('locale', items['systemLocale'], 'can be set in <a href="http://www.psychopy.org/general/prefs.html#application-settings">Preferences -> App</a>')) msg = '' if items['pythonVersion'] < '2.5' or items['pythonVersion'] >= '3': msg = 'Warning: python 2.5, 2.6, or 2.7 required; 2.5 is iffy' if 'EPD' in items['pythonFullVersion']: msg += ' Enthought Python Distribution' elif 'PsychoPy2.app' in items['pythonExecutable']: msg += ' (PsychoPy StandAlone)' bits, linkage = platform.architecture() if not bits.startswith('32'): msg = 'Warning: 32-bit python required; ' + msg report.append(('python version', items['pythonVersion'] + ' (%s)' % bits, msg)) if verbose: msg = '' if items['pythonWxVersion'] < '2.8.10': msg = 'Warning: wx 2.8.10 or higher required' report.append(('wx', items['pythonWxVersion'], '')) report.append(('pyglet', items['pythonPygletVersion'][:32], '')) report.append(('rush', str(items['psychopyHaveExtRush']), 'for high-priority threads')) # ----- VISUAL: ----- report.append(('Visual', '', '')) # openGL settings: msg = '' if items['openGLVersion'] < '2.': msg = 'Warning: <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=OpenGL+2.0">OpenGL 2.0 or higher is ideal</a>.' report.append(('openGL version', items['openGLVersion'], msg)) report.append(('openGL vendor', items['openGLVendor'], '')) report.append(('screen size', ' x '.join(map(str, items['windowSize_pix'])), '')) #report.append(('wait blanking', str(items['windowWaitBlanking']), '')) msg = '' if not items['windowHaveShaders']: msg = 'Warning: <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=shader">Rendering of complex stimuli will be slow</a>.' report.append(('have shaders', str(items['windowHaveShaders']), msg)) msg = 'during the drifting <a href="http://www.psychopy.org/api/visual/gratingstim.html">GratingStim</a>' if items['windowRefreshTimeMedian_ms'] < 3.3333333: msg = """Warning: too fast? visual sync'ing with the monitor seems unlikely at 300+ Hz""" report.append(('visual sync (refresh)', "%.2f ms/frame" % items['windowRefreshTimeMedian_ms'], msg)) msg = 'SD < 0.5 ms is ideal (want low variability)' if items['windowRefreshTimeSD_ms'] > .5: msg = 'Warning: the refresh rate has high frame-to-frame variability (SD > 0.5 ms)' report.append(('refresh stability (SD)', "%.2f ms" % items['windowRefreshTimeSD_ms'], msg)) # draw 100 dots as a minimally demanding visual test: # first get baseline frame-rate (safe as possible, no drawing): avg, sd, median = visual.getMsPerFrame(win) dots100 = visual.DotStim(win, nDots=100, speed=0.005, dotLife=12, dir=90, coherence=0.2, dotSize=8, fieldShape='circle') win.setRecordFrameIntervals(True) win.frameIntervals = [] win.flip() for i in xrange(180): dots100.draw() win.flip() msg = 'during <a href="http://www.psychopy.org/api/visual/dotstim.html">DotStim</a> with 100 random dots' intervalsMS = np.array(win.frameIntervals) * 1000 nTotal = len(intervalsMS) nDropped = sum(intervalsMS > (1.5 * median)) if nDropped: msg = 'Warning: could not keep up during <a href="http://www.psychopy.org/api/visual/dotstim.html">DotStim</a> with 100 random dots.' report.append(('no dropped frames', '%i / %i' % (nDropped, nTotal), msg)) win.setRecordFrameIntervals(False) try: from pyglet.media import avbin ver = avbin.get_version() if ver < 5 or ver >= 6: msg = 'Warning: version 5 recommended (for movies); Visit <a href="http://code.google.com/p/avbin">download page</a> [google.com]' else: msg = 'for movies' report.append(('pyglet avbin', str(ver), msg)) except: # not sure what error to catch, WindowsError not found report.append(('pyglet avbin', 'import error', 'Warning: could not import avbin; playing movies will not work')) if verbose: report.append(('openGL max vertices', str(items['openGLmaxVerticesInVertexArray']), '')) keyList = ['GL_ARB_multitexture', 'GL_EXT_framebuffer_object', 'GL_ARB_fragment_program', 'GL_ARB_shader_objects', 'GL_ARB_vertex_shader', 'GL_ARB_texture_non_power_of_two', 'GL_ARB_texture_float', 'GL_STEREO'] for key in keyList: val = items['openGLext.'+key] # boolean if not val: val = '<strong>' + str(val) + '</strong>' report.append((key, str(val), '')) # ----- AUDIO: ----- report.append(('Audio', '', '')) msg = '' if not 'systemPyoVersion' in items: msg = 'Warning: pyo is needed for sound and microphone.' items['systemPyoVersion'] = '(missing)' elif items['systemPyoVersion'] < '0.6.2': msg = 'pyo 0.6.2 compiled with --no-messages will suppress start-up messages' report.append(('pyo', items['systemPyoVersion'], msg)) # sound latencies from portaudio; requires pyo svn r1024 try: sndInputDevices = items['systemPyo.InputDevices'] if len(sndInputDevices.keys()): key = sndInputDevices.keys()[0] mic = sndInputDevices[key] if mic['name'].endswith('icroph'): mic['name'] += 'one' # portaudio (?) seems to clip to 16 chars msg = '"%s"' % mic['name'] if mic['latency'] > 0.003: msg = 'Warning: "%s" latency > 3ms' % mic['name'] report.append(('microphone latency', "%.4f s" % mic['latency'], msg)) else: report.append(('microphone', '(not detected)','')) sndOutputDevices = items['systemPyo.OutputDevices'] if len(sndOutputDevices.keys()): key = sndOutputDevices.keys()[0] spkr = sndOutputDevices[key] msg = '"%s"' % spkr['name'] if spkr['latency'] > 0.003: msg = 'Warning: "%s" latency > 3ms' % spkr['name'] report.append(('speakers latency', "%.4f s" % spkr['latency'], msg)) else: report.append(('speakers', '(not detected)','')) except KeyError: pass s2t = '<a href="http://www.psychopy.org/api/microphone.html?highlight=Speech2Text">speech-to-text</a>' msg = 'audio codec for %s' % s2t if not 'systemFlacVersion' in items: msg = 'Warning: flac is needed for using %s features. <a href="http://flac.sourceforge.net/download.html">Download</a> [sourceforge.net].' % s2t items['systemFlacVersion'] = '(missing)' if verbose: report.append(('flac', items['systemFlacVersion'].lstrip('flac '), msg)) # TO-DO: add microphone + playback as sound test # ----- NUMERIC: ----- report.append(('Numeric', '', '')) report.append(('numpy', items['pythonNumpyVersion'], 'vector-based (fast) calculations')) report.append(('scipy', items['pythonScipyVersion'], 'scientific / numerical')) report.append(('matplotlib', items['pythonMatplotlibVersion'], 'plotting; fast contains(), overlaps()')) # ----- SYSTEM: ----- report.append(('System', '', '')) report.append(('platform', items['systemPlatform'], '')) msg = 'for online help, usage statistics, software updates, and google-speech' if items['systemHaveInternetAccess'] is not True: items['systemHaveInternetAccess'] = 'False' msg = 'Warning: could not connect (no proxy attempted)' # TO-DO: dlg to query whether to try to auto-detect (can take a while), or allow manual entry of proxy str, save into prefs val = str(items['systemHaveInternetAccess']) report.append(('internet access', val, msg)) report.append(('auto proxy', str(self.prefs.connections['autoProxy']), 'try to auto-detect a proxy if needed; see <a href="http://www.psychopy.org/general/prefs.html#connection-settings">Preferences -> Connections</a>')) if not self.prefs.connections['proxy'].strip(): prx = '  --' else: prx = str(self.prefs.connections['proxy']) report.append(('proxy setting', prx, 'current manual proxy setting from <a href="http://www.psychopy.org/general/prefs.html#connection-settings">Preferences -> Connections</a>')) msg = '' items['systemUserProcFlagged'].sort() self.badBgProc = [p for p,pid in items['systemUserProcFlagged']] val = ("%s ..." % self.badBgProc[0]) if len(self.badBgProc) else 'No bad background processes found.' msg = 'Warning: Some <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=background+processes">background processes</a> can adversely affect timing' report.append(('background processes', val, msg)) if verbose and 'systemSec.OpenSSLVersion' in items: report.append(('OpenSSL', items['systemSec.OpenSSLVersion'].lstrip('OpenSSL '), 'for <a href="http://www.psychopy.org/api/encryption.html">encryption</a>')) report.append(('CPU speed test', "%.3f s" % items['systemTimeNumpySD1000000_sec'], 'numpy.std() of a million data points')) # TO-DO: more speed benchmarks # - load large image file from disk # - transfer image to GPU # ----- IMPORTS (relevant for developers & non-StandAlone): ----- if verbose: # always False for a real first-run report.append(('Packages', '', '')) packages = ['PIL', 'openpyxl', 'lxml', 'setuptools', 'pytest', 'sphinx', 'psignifit', 'pyserial', 'pp', 'pynetstation', 'ioLabs', 'labjack' ] if sys.platform == 'win32': packages.append('pywin32') packages.append('winioport') for pkg in packages: try: if pkg == 'PIL': exec('import PIL.Image') ver = PIL.Image.VERSION #elif pkg == 'lxml': # elif pkg == 'pp': exec('import pp; ver = pp.version') elif pkg == 'pynetstation': exec('from psychopy.hardware import egi') ver = 'import ok' elif pkg == 'pyserial': exec('import serial') ver = serial.VERSION else: exec('import ' + pkg) try: ver = eval(pkg+'.__version__') except: ver = 'import ok' report.append((pkg, ver, '')) except (ImportError, AttributeError): report.append((pkg, '  --', 'could not import %s' % pkg)) self.warnings = list(set([key for key, val, msg in report if msg.startswith('Warning')])) return report
def testRefreshRate(self): #make sure that we're successfully syncing to the frame rate msPFavg, msPFstd, msPFmed = visual.getMsPerFrame(self.win,nFrames=60, showVisual=True) nose.tools.ok_(1000/150.0 < msPFavg < 1000/40.0, "Your frame period is %.1fms which suggests you aren't syncing to the frame" %msPFavg)
def launch_window(self, test_refresh=True, test_tol=.5): """Load window info""" #taken from Mwaskom cregg try: mod = __import__("monitors") except ImportError: sys.exit("Could not import monitors.py in this directory.") try: minfo = getattr(mod, self.monitor_name) except IndexError: sys.exit("Monitor not found in monitors.py") fullscreen = self.full_screen size = minfo["size"] if fullscreen else (800, 600) monitor = Monitor(name=minfo["name"], width=minfo["width"], distance=minfo["distance"]) monitor.setSizePix(minfo["size"]) info = dict(units=self.monitor_units, fullscr=fullscreen, color=self.window_color, size=size, monitor=monitor) if "framerate" in minfo: self.framerate = minfo["framerate"] self.name = self.monitor_name self.__dict__.update(info) self.window_kwargs = info """Open up a presentation window and measure the refresh rate.""" stated_refresh_hz = self.framerate # Initialize the Psychopy window object win = visual.Window(**self.window_kwargs) # Record the refresh rate we are currently achieving if self.test_refresh or stated_refresh_hz is None: win.setRecordFrameIntervals(True) logging.console.setLevel(logging.CRITICAL) flip_time, _, _ = visual.getMsPerFrame(win) observed_refresh_hz = 1000 / flip_time print('observed_refresh_hz', observed_refresh_hz) # Possibly test the refresh rate against what we expect if self.test_refresh and stated_refresh_hz is not None: refresh_error = np.abs(stated_refresh_hz - observed_refresh_hz) print('refresh_error', refresh_error) if refresh_error > test_tol: msg = ( "Observed refresh rate differs from expected by {:.3f} Hz". format(refresh_error)) raise RuntimeError(msg) # Set the refresh rate to use in the experiment if stated_refresh_hz is None: msg = "Monitor configuration does not have refresh rate information" warnings.warn(msg) win.framerate = observed_refresh_hz else: win.framerate = stated_refresh_hz return win
def __init__(self, exp_info, file_name): """ Initialize experiment - read XML file, setup window, connect to netstation and tobii exp_info - experiment information file_name - name of XML file containing experiment definition """ self.exp_info = exp_info self.name = None self.type = None self.num_blocks = 0 self.blocks = {} self.block_order = [] # Window to use wintype = 'pyglet' # use pyglet if possible, it's faster at event handling # Add 14cm to distance - this is distance from eyetracker to monitor mon = monitors.Monitor(exp_info['monitor'], distance=float(exp_info['monitor distance'])) self.win = Window( [1280, 1024], monitor=mon, screen=SCREEN, units="deg", fullscr=True, #fullscr=False, color=[-1, -1, -1], winType=wintype) self.win.setMouseVisible(False) event.clearEvents() # Measure frame rate self.mean_ms_per_frame, std_ms_per_frame, median_ms_per_frame = visual.getMsPerFrame(self.win, nFrames=60, showVisual=True) self.debug_sq=None if exp_info['monitor']=='tobii': self.debug_sq=psychopy.visual.Rect(self.win, width=30, height=30, units='pix') self.debug_sq.setFillColor((1,1,1)) self.debug_sq.setPos((630,-500)) # Compute distractor duration in frames based on frame rate distractor_duration_frames = int(2000.0/self.mean_ms_per_frame) # Initialize set of distractors self.distractor_set = DistractorSet(os.path.join(DATA_DIR, 'images', 'distractors', 'space'), os.path.join(DATA_DIR, 'sounds', 'distractors'), os.path.join(DATA_DIR, 'movies', 'distractors'), os.path.join(DATA_DIR, 'images', 'distractors', 'star-cartoon.jpg'), distractor_duration_frames, self.win) # Connect to nestation self.ns = None if exp_info['eeg']: # connect to netstation self.ns = egi.Netstation() ms_localtime = egi.ms_localtime self.eye_tracker = None mouse_visible = False if exp_info['eyetracking source'] == 'tobii': # Initialize eyetracker self.eye_tracker = TobiiController(self.win) self.eye_tracker.waitForFindEyeTracker() self.eye_tracker.activate(EYETRACKER_NAME) elif exp_info['eyetracking source'] == 'mouse': mouse_visible = True # Initialize mouse self.mouse = event.Mouse(visible=mouse_visible, win=self.win) self.gaze_debug=None if self.exp_info['debug mode']: self.gaze_debug=psychopy.visual.Circle(self.win, radius=1, fillColor=(1.0,-1.0,-1.0)) self.read_xml(file_name) # Initialize netstation and eyetracker self.initialize()
def runDiagnostics(self, win, verbose=False): """Return list of (key, val, msg, warn) tuple, set self.warnings All tuple elements will be of <type str>. msg can depend on val; warn==True indicates a concern. Plain text is returned, expected to be used in html <table>. Hyperlinks can be embedded as <a href="..."> """ report = [] # add item tuples in display order # get lots of info and do quick-to-render visual (want no frames drop): # for me, grating draw times are: mean 0.53 ms, SD 0.77 ms items = info.RunTimeInfo(win=win, refreshTest='grating', verbose=True, userProcsDetailed=True) totalRAM, freeRAM = items['systemMemTotalRAM'], items['systemMemFreeRAM'] warn = False if freeRAM == 'unknown': if totalRAM != 'unknown': totalRAM = "%.1fG" % (totalRAM / 1024.) msg = _translate('could not assess available physical RAM; total %s') % totalRAM report.append(('available memory', 'unknown', msg, warn)) else: msg = _translate('physical RAM available for configuration test (of %.1fG total)') % (totalRAM / 1024.) if freeRAM < 300: # in M msg = _translate('Warning: low available physical RAM for configuration test (of %.1fG total)') % (totalRAM / 1024.) warn = True report.append(('available memory', unicode(freeRAM)+'M', msg, warn)) # ----- PSYCHOPY: ----- warn = False report.append(('PsychoPy', '', '', False)) # not localized report.append(('psychopy', __version__, _translate('avoid upgrading during an experiment'), False)) report.append(('locale', items['systemLocale'], _translate('can be set in <a href="http://www.psychopy.org/general/prefs.html#application-settings-app">Preferences -> App</a>'), False)) msg = '' if items['pythonVersion'] < '2.5' or items['pythonVersion'] >= '3': msg = _translate('Warning: python 2.6 or 2.7 required; 2.5 is not supported but might work') warn = True if 'EPD' in items['pythonFullVersion']: msg += ' Enthought Python Distribution' elif 'PsychoPy2.app' in items['pythonExecutable']: msg += ' (PsychoPy StandAlone)' bits, linkage = platform.architecture() #if not bits.startswith('32'): # msg = 'Warning: 32-bit python required; ' + msg report.append(('python version', items['pythonVersion'] + ' (%s)' % bits, msg, warn)) warn = False if verbose: msg = '' if items['pythonWxVersion'] < '2.8.10': msg = _translate('Warning: wx 2.8.10 or higher required') warn = True report.append(('wx', items['pythonWxVersion'], '', warn)) report.append(('pyglet', items['pythonPygletVersion'][:32], '', False)) report.append(('rush', str(items['psychopyHaveExtRush']), _translate('for high-priority threads'), False)) # ----- VISUAL: ----- report.append(('Visual', '', '', False)) warn = False # openGL settings: msg = '' if items['openGLVersion'] < '2.': msg = _translate('Warning: <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=OpenGL+2.0">OpenGL 2.0 or higher is ideal</a>.') warn = True report.append(('openGL version', items['openGLVersion'], msg, warn)) report.append(('openGL vendor', items['openGLVendor'], '', False)) report.append(('screen size', ' x '.join(map(str, items['windowSize_pix'])), '', False)) #report.append(('wait blanking', str(items['windowWaitBlanking']), '', False)) warn = False msg = '' if not items['windowHaveShaders']: msg = _translate('Warning: <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=shader">Rendering of complex stimuli will be slow</a>.') warn = True report.append(('have shaders', str(items['windowHaveShaders']), msg, warn)) warn = False msg = _translate('during the drifting <a href="http://www.psychopy.org/api/visual/gratingstim.html">GratingStim</a>') if items['windowRefreshTimeMedian_ms'] < 3.3333333: msg = _translate("""Warning: too fast? visual sync'ing with the monitor seems unlikely at 300+ Hz""") warn = True report.append(('visual sync (refresh)', "%.2f ms/frame" % items['windowRefreshTimeMedian_ms'], msg, warn)) msg = _translate('SD < 0.5 ms is ideal (want low variability)') warn = False if items['windowRefreshTimeSD_ms'] > .5: msg = _translate('Warning: the refresh rate has high frame-to-frame variability (SD > 0.5 ms)') warn = True report.append(('refresh stability (SD)', "%.2f ms" % items['windowRefreshTimeSD_ms'], msg, warn)) # draw 100 dots as a minimally demanding visual test: # first get baseline frame-rate (safe as possible, no drawing): avg, sd, median = visual.getMsPerFrame(win) dots100 = visual.DotStim(win, nDots=100, speed=0.005, dotLife=12, dir=90, coherence=0.2, dotSize=8, fieldShape='circle', autoLog=False) win.recordFrameIntervals = True win.frameIntervals = [] win.flip() for i in xrange(180): dots100.draw() win.flip() msg = _translate('during <a href="http://www.psychopy.org/api/visual/dotstim.html">DotStim</a> with 100 random dots') warn = False intervalsMS = np.array(win.frameIntervals) * 1000 nTotal = len(intervalsMS) nDropped = sum(intervalsMS > (1.5 * median)) if nDropped: msg = _translate('Warning: could not keep up during <a href="http://www.psychopy.org/api/visual/dotstim.html">DotStim</a> with 100 random dots.') warn = True report.append(('no dropped frames', '%i / %i' % (nDropped, nTotal), msg, warn)) win.recordFrameIntervals = False msg = _translate('for movies') warn = False try: from pyglet.media import avbin except: # not sure what error to catch, WindowsError not found report.append(('pyglet avbin', 'import error', _translate('Warning: could not import avbin; playing movies will not work'), True)) else: ver = avbin.get_version() if sys.platform.startswith('linux'): if not (7 <= ver < 8): msg = _translate('Warning: version 7 recommended on linux (for movies)') warn = True elif not (5 <= ver < 6): msg = _translate('Warning: version 5 recommended (for movies); Visit <a href="http://code.google.com/p/avbin">download page</a> [google.com]') warn = True report.append(('pyglet avbin', unicode(ver), msg, warn)) if verbose: report.append(('openGL max vertices', str(items['openGLmaxVerticesInVertexArray']), '', False)) keyList = ['GL_ARB_multitexture', 'GL_EXT_framebuffer_object', 'GL_ARB_fragment_program', 'GL_ARB_shader_objects', 'GL_ARB_vertex_shader', 'GL_ARB_texture_non_power_of_two', 'GL_ARB_texture_float', 'GL_STEREO'] for key in keyList: val = items['openGLext.'+key] # boolean if not val: val = '<strong>' + str(val) + '</strong>' report.append((key, str(val), '', False)) # ----- AUDIO: ----- report.append(('Audio', '', '', False)) msg = '' warn = False if not 'systemPyoVersion' in items: msg = _translate('Warning: pyo is needed for sound and microphone.') warn = True items['systemPyoVersion'] = _translate('(missing)') #elif items['systemPyoVersion'] < '0.6.2': # msg = 'pyo 0.6.2 compiled with --no-messages will suppress start-up messages' report.append(('pyo', items['systemPyoVersion'], msg, warn)) # sound latencies from portaudio; requires pyo svn r1024 try: sndInputDevices = items['systemPyo.InputDevices'] warn = False if len(sndInputDevices.keys()): key = sndInputDevices.keys()[0] mic = sndInputDevices[key] if mic['name'].endswith('icroph'): mic['name'] += 'one' # portaudio (?) seems to clip to 16 chars msg = '"%s"' % mic['name'] if mic['latency'] > 0.01: msg = _translate('Warning: "%s" latency > 10ms') % mic['name'] warn = True report.append(('microphone latency', "%.4f s" % mic['latency'], msg, warn)) else: report.append(('microphone', _translate('(not detected)'),'', False)) sndOutputDevices = items['systemPyo.OutputDevices'] if len(sndOutputDevices.keys()): warn = False key = sndOutputDevices.keys()[0] spkr = sndOutputDevices[key] msg = '"%s"' % spkr['name'] if spkr['latency'] > 0.01: msg = _translate('Warning: "%s" latency > 10ms') % spkr['name'] warn = True report.append(('speakers latency', "%.4f s" % spkr['latency'], msg, warn)) else: report.append(('speakers', _translate('(not detected)'),'', False)) except KeyError: pass s2t = '<a href="http://www.psychopy.org/api/microphone.html?highlight=Speech2Text">speech-to-text</a>' msg = _translate('audio codec for %s and sound file compression') % s2t warn = False if not 'systemFlacVersion' in items: msg = _translate('Warning: flac is needed for using %s and sound compression.') % s2t +\ ' <a href="http://flac.sourceforge.net/download.html">' +\ _translate('Download</a> [sourceforge.net].') warn = True items['systemFlacVersion'] = _translate('(missing)') if verbose: report.append(('flac', items['systemFlacVersion'].lstrip('flac '), msg, warn)) # TO-DO: add microphone + playback as sound test # ----- NUMERIC: ----- report.append(('Numeric', '', '', False)) report.append(('numpy', items['pythonNumpyVersion'], _translate('vector-based (fast) calculations'), False)) report.append(('scipy', items['pythonScipyVersion'], _translate('scientific / numerical'), False)) report.append(('matplotlib', items['pythonMatplotlibVersion'], _translate('plotting; fast contains(), overlaps()'), False)) # ----- SYSTEM: ----- report.append(('System', '', '', False)) report.append(('platform', items['systemPlatform'], '', False)) msg = _translate('for online help, usage statistics, software updates, and google-speech') warn = False if items['systemHaveInternetAccess'] is not True: items['systemHaveInternetAccess'] = 'False' msg = _translate('Warning: could not connect (no proxy attempted)') warn = True # TO-DO: dlg to query whether to try to auto-detect (can take a while), or allow manual entry of proxy str, save into prefs val = str(items['systemHaveInternetAccess']) report.append(('internet access', val, msg, warn)) report.append(('auto proxy', str(self.prefs.connections['autoProxy']), _translate('try to auto-detect a proxy if needed; see <a href="http://www.psychopy.org/general/prefs.html#connection-settings-connections">Preferences -> Connections</a>'), False)) if not self.prefs.connections['proxy'].strip(): prx = ' --' else: prx = unicode(self.prefs.connections['proxy']) report.append(('proxy setting', prx, _translate('current manual proxy setting from <a href="http://www.psychopy.org/general/prefs.html#connection-settings-connections">Preferences -> Connections</a>'), False)) msg = '' warn = False # assure that we have a list if items['systemUserProcFlagged'] is None: items['systemUserProcFlagged'] = [] items['systemUserProcFlagged'].sort() self.badBgProc = [p for p,pid in items['systemUserProcFlagged']] if len(self.badBgProc): val = ("%s ..." % self.badBgProc[0]) msg = _translate('Warning: Some <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=background+processes">background processes</a> can adversely affect timing') warn = True else: val = _translate('No bad background processes active.') report.append(('background processes', val, msg, warn)) if verbose and 'systemSec.OpenSSLVersion' in items: report.append(('OpenSSL', items['systemSec.OpenSSLVersion'].lstrip('OpenSSL '), 'for <a href="http://www.psychopy.org/api/encryption.html">encryption</a>', False)) report.append(('CPU speed test', "%.3f s" % items['systemTimeNumpySD1000000_sec'], _translate('numpy.std() of 1,000,000 data points'), False)) # TO-DO: more speed benchmarks # - load large image file from disk # - transfer image to GPU # ----- IMPORTS (relevant for developers & non-StandAlone): ----- if verbose: # always False for a real first-run report.append((_translate('Python packages'), '', '', False)) packages = ['PIL', 'openpyxl', 'lxml', 'setuptools', 'pytest', 'sphinx', 'psignifit', 'pyserial', 'pp', 'pynetstation', 'ioLabs', 'labjack' ] if sys.platform == 'win32': packages.append('pywin32') packages.append('winioport') for pkg in packages: try: if pkg == 'PIL': exec('import PIL.Image') ver = PIL.Image.VERSION #elif pkg == 'lxml': # elif pkg == 'pp': exec('import pp; ver = pp.version') elif pkg == 'pynetstation': exec('from psychopy.hardware import egi') ver = 'import ok' elif pkg == 'pyserial': exec('import serial') ver = serial.VERSION elif pkg == 'pywin32': exec('import win32api') ver = 'import ok' else: exec('import ' + pkg) try: ver = eval(pkg+'.__version__') except: ver = 'import ok' report.append((pkg, ver, '', False)) except (ImportError, AttributeError): report.append((pkg, ' --', _translate('could not import package %s') % pkg, False)) # rewrite to avoid assumption of locale en_US: self.warnings = list(set([key for key, val, msg, warn in report if warn])) return report
def runDiagnostics(self, win, verbose=False): """Return list of (key, val, msg, warn) tuple, set self.warnings All tuple elements will be of <type str>. msg can depend on val; warn==True indicates a concern. Plain text is returned, expected to be used in html <table>. Hyperlinks can be embedded as <a href="..."> """ report = [] # add item tuples in display order # get lots of info and do quick-to-render visual (want 0 frames drop): # for me, grating draw times are: mean 0.53 ms, SD 0.77 ms items = info.RunTimeInfo(win=win, refreshTest='grating', verbose=True, userProcsDetailed=True) totalRAM = items['systemMemTotalRAM'] freeRAM = items['systemMemFreeRAM'] warn = False if freeRAM == 'unknown': if totalRAM != 'unknown': totalRAM = "%.1fG" % (totalRAM / 1024.0) txt = _translate( 'could not assess available physical RAM; total %s') msg = txt % totalRAM report.append(('available memory', 'unknown', msg, warn)) else: txt = _translate('physical RAM available for configuration test ' '(of %.1fG total)') msg = txt % (totalRAM / 1024.) if freeRAM < 300: # in M txt = _translate('Warning: low available physical RAM for ' 'configuration test (of %.1fG total)') msg = txt % (totalRAM / 1024.) warn = True report.append(('available memory', str(freeRAM) + 'M', msg, warn)) # ----- PSYCHOPY: ----- warn = False report.append(('PsychoPy', '', '', False)) # not localized report.append( ('psychopy', __version__, _translate('avoid upgrading during an experiment'), False)) msg = _translate( 'can be set in <a href="http://www.psychopy.org/general/' 'prefs.html#application-settings-app">Preferences -> App</a>') report.append(('locale', items['systemLocale'], msg, False)) msg = '' v = parse_version thisV = v(items['pythonVersion']) if (thisV < v('2.7') or (v('3.0') <= thisV < v('3.6'))): msg = _translate("Warning: python 2.7 or 3.6 are recommended; " "2.6 and 3.5 might work. Others probably won't.") warn = True if 'EPD' in items['pythonFullVersion']: msg += ' Enthought Python Distribution' elif 'PsychoPy3.app' in items['pythonExecutable']: msg += ' (PsychoPy StandAlone)' bits, linkage = platform.architecture() # if not bits.startswith('32'): # msg = 'Warning: 32-bit python required; ' + msg report.append( ('python version', items['pythonVersion'] + ' (%s)' % bits, msg, warn)) warn = False if verbose: msg = '' if items['pythonWxVersion'] < '2.8.10': msg = _translate('Warning: wx 2.8.10 or higher required') warn = True report.append(('wx', items['pythonWxVersion'], '', warn)) report.append( ('pyglet', items['pythonPygletVersion'][:32], '', False)) report.append(('rush', str(items['psychopyHaveExtRush']), _translate('for high-priority threads'), False)) # ----- VISUAL: ----- report.append(('Visual', '', '', False)) warn = False # openGL settings: msg = '' if items['openGLVersion'] < '2.': msg = _translate( 'Warning: <a href="http://www.psychopy.org/general/timing' '/reducingFrameDrops.html?highlight=OpenGL+2.0">OpenGL ' '2.0 or higher is ideal</a>.') warn = True report.append(('openGL version', items['openGLVersion'], msg, warn)) report.append(('openGL vendor', items['openGLVendor'], '', False)) report.append( ('screen size', ' x '.join(map(str, items['windowSize_pix'])), '', False)) # report.append(('wait blanking', str(items['windowWaitBlanking']), '', # False)) warn = False msg = '' if not items['windowHaveShaders']: msg = _translate( 'Warning: <a href="http://www.psychopy.org/general/timing' '/reducingFrameDrops.html?highlight=shader">Rendering of' ' complex stimuli will be slow</a>.') warn = True report.append( ('have shaders', str(items['windowHaveShaders']), msg, warn)) warn = False msg = _translate( 'during the drifting <a href="http://www.psychopy.org/api/' 'visual/gratingstim.html">GratingStim</a>') if items['windowRefreshTimeMedian_ms'] < 3.3333333: msg = _translate( "Warning: too fast? visual sync'ing with the monitor" " seems unlikely at 300+ Hz") warn = True report.append( ('visual sync (refresh)', "%.2f ms/frame" % items['windowRefreshTimeMedian_ms'], msg, warn)) msg = _translate('SD < 0.5 ms is ideal (want low variability)') warn = False if items['windowRefreshTimeSD_ms'] > .5: msg = _translate( 'Warning: the refresh rate has high frame-to-frame ' 'variability (SD > 0.5 ms)') warn = True report.append(('refresh stability (SD)', "%.2f ms" % items['windowRefreshTimeSD_ms'], msg, warn)) # draw 100 dots as a minimally demanding visual test: # first get baseline frame-rate (safe as possible, no drawing): avg, sd, median = visual.getMsPerFrame(win) dots100 = visual.DotStim(win, nDots=100, speed=0.005, dotLife=12, dir=90, coherence=0.2, dotSize=8, fieldShape='circle', autoLog=False) win.recordFrameIntervals = True win.frameIntervals = [] win.flip() for i in range(180): dots100.draw() win.flip() msg = _translate('during <a href="http://www.psychopy.org/api/visual/' 'dotstim.html">DotStim</a> with 100 random dots') warn = False intervalsMS = np.array(win.frameIntervals) * 1000 nTotal = len(intervalsMS) nDropped = sum(intervalsMS > (1.5 * median)) if nDropped: msg = _translate( 'Warning: could not keep up during <a href="http://' 'www.psychopy.org/api/visual/dotstim.html">DotStim</a>' ' with 100 random dots.') warn = True report.append( ('no dropped frames', '%i / %i' % (nDropped, nTotal), msg, warn)) win.recordFrameIntervals = False if verbose: report.append( ('openGL max vertices', str(items['openGLmaxVerticesInVertexArray']), '', False)) keyList = ('GL_ARB_multitexture', 'GL_EXT_framebuffer_object', 'GL_ARB_fragment_program', 'GL_ARB_shader_objects', 'GL_ARB_vertex_shader', 'GL_ARB_texture_float', 'GL_ARB_texture_non_power_of_two', 'GL_STEREO') for key in keyList: val = items['openGLext.' + key] # boolean if not val: val = '<strong>' + str(val) + '</strong>' report.append((key, str(val), '', False)) # ----- AUDIO: ----- report.append(('Audio', '', '', False)) msg = '' warn = False if not 'systemPyoVersion' in items: msg = _translate( 'Warning: pyo is needed for sound and microphone.') warn = True items['systemPyoVersion'] = _translate('(missing)') # elif items['systemPyoVersion'] < '0.6.2': # msg = 'pyo 0.6.2 compiled with --no-messages will # suppress start-up messages' report.append(('pyo', items['systemPyoVersion'], msg, warn)) # TO-DO: add microphone + playback as sound test # ----- NUMERIC: ----- report.append(('Numeric', '', '', False)) report.append(('numpy', items['pythonNumpyVersion'], _translate('vector-based (fast) calculations'), False)) report.append(('scipy', items['pythonScipyVersion'], _translate('scientific / numerical'), False)) report.append( ('matplotlib', items['pythonMatplotlibVersion'], _translate('plotting; fast contains(), overlaps()'), False)) # ----- SYSTEM: ----- report.append(('System', '', '', False)) report.append(('platform', items['systemPlatform'], '', False)) msg = _translate('for online help, usage statistics, software ' 'updates, and google-speech') warn = False if items['systemHaveInternetAccess'] is not True: items['systemHaveInternetAccess'] = 'False' msg = _translate('Warning: could not connect (no proxy attempted)') warn = True # TO-DO: dlg to query whether to try to auto-detect (can take a # while), or allow manual entry of proxy str, save into prefs val = str(items['systemHaveInternetAccess']) report.append(('internet access', val, msg, warn)) report.append(('auto proxy', str(self.prefs.connections['autoProxy']), _translate('try to auto-detect a proxy if needed; see' ' <a href="http://www.psychopy.org/general' '/prefs.html#connection-settings-connection' 's">Preferences -> Connections</a>'), False)) if not self.prefs.connections['proxy'].strip(): prx = ' --' else: prx = str(self.prefs.connections['proxy']) report.append(('proxy setting', prx, _translate('current manual proxy setting from <a ' 'href="http://www.psychopy.org/general/' 'prefs.html#connection-settings-connections' '">Preferences -> Connections</a>'), False)) txt = 'CPU speed test' report.append( (txt, "%.3f s" % items['systemTimeNumpySD1000000_sec'], _translate('numpy.std() of 1,000,000 data points'), False)) # TO-DO: more speed benchmarks # - load large image file from disk # - transfer image to GPU # ----- IMPORTS (relevant for developers & non-StandAlone): ----- if verbose: # always False for a real first-run report.append((_translate('Python packages'), '', '', False)) packages = [ 'PIL', 'openpyxl', 'lxml', 'setuptools', 'pytest', 'sphinx', 'psignifit', 'pyserial', 'pp', 'pynetstation', 'labjack' ] if sys.platform == 'win32': packages.append('pywin32') packages.append('winioport') if constants.PY3: pkgError = ModuleNotFoundError else: pkgError = ImportError for pkg in packages: try: if pkg == 'PIL': import PIL ver = PIL.__version__ # elif pkg == 'lxml': # elif pkg == 'pynetstation': from psychopy.hardware import egi ver = 'import ok' elif pkg == 'pyserial': import serial ver = serial.VERSION elif pkg == 'pywin32': import win32api ver = 'import ok' else: exec('import ' + pkg) try: ver = eval(pkg + '.__version__') except Exception: ver = 'imported but no version info' report.append((pkg, ver, '', False)) except (pkgError, AttributeError): msg = _translate('could not import package %s') report.append((pkg, ' --', msg % pkg, False)) # rewrite to avoid assumption of locale en_US: self.warnings = list( set([key for key, val, msg, warn in report if warn])) return report
def runDiagnostics(self, win, verbose=False): """Return list of (key, val, msg, warn) tuple, set self.warnings All tuple elements will be of <type str>. msg can depend on val; warn==True indicates a concern. Plain text is returned, expected to be used in html <table>. Hyperlinks can be embedded as <a href="..."> """ report = [] # add item tuples in display order # get lots of info and do quick-to-render visual (want 0 frames drop): # for me, grating draw times are: mean 0.53 ms, SD 0.77 ms items = info.RunTimeInfo(win=win, refreshTest='grating', verbose=True, userProcsDetailed=True) totalRAM = items['systemMemTotalRAM'] freeRAM = items['systemMemFreeRAM'] warn = False if freeRAM == 'unknown': if totalRAM != 'unknown': totalRAM = "%.1fG" % (totalRAM/1024.0) txt = _translate( 'could not assess available physical RAM; total %s') msg = txt % totalRAM report.append(('available memory', 'unknown', msg, warn)) else: txt = _translate( 'physical RAM available for configuration test ' '(of %.1fG total)') msg = txt % (totalRAM/1024.) if freeRAM < 300: # in M txt = _translate( 'Warning: low available physical RAM for ' 'configuration test (of %.1fG total)') msg = txt % (totalRAM/1024.) warn = True report.append(('available memory', str(freeRAM) + 'M', msg, warn)) # ----- PSYCHOPY: ----- warn = False report.append(('PsychoPy', '', '', False)) # not localized report.append(('psychopy', __version__, _translate('avoid upgrading during an experiment'), False)) msg = _translate( 'can be set in <a href="http://www.psychopy.org/general/' 'prefs.html#application-settings-app">Preferences -> App</a>') report.append(('locale', items['systemLocale'], msg, False)) msg = '' v = parse_version thisV = v(items['pythonVersion']) if (thisV < v('2.7') or (v('3.0') <= thisV < v('3.6')) ): msg = _translate("Warning: python 2.7 or 3.6 are recommended; " "2.6 and 3.5 might work. Others probably won't.") warn = True if 'EPD' in items['pythonFullVersion']: msg += ' Enthought Python Distribution' elif 'PsychoPy3.app' in items['pythonExecutable']: msg += ' (PsychoPy StandAlone)' bits, linkage = platform.architecture() # if not bits.startswith('32'): # msg = 'Warning: 32-bit python required; ' + msg report.append( ('python version', items['pythonVersion'] + ' (%s)' % bits, msg, warn)) warn = False if verbose: msg = '' if items['pythonWxVersion'] < '2.8.10': msg = _translate('Warning: wx 2.8.10 or higher required') warn = True report.append(('wx', items['pythonWxVersion'], '', warn)) report.append( ('pyglet', items['pythonPygletVersion'][:32], '', False)) report.append(('rush', str(items['psychopyHaveExtRush']), _translate('for high-priority threads'), False)) # ----- VISUAL: ----- report.append(('Visual', '', '', False)) warn = False # openGL settings: msg = '' if items['openGLVersion'] < '2.': msg = _translate( 'Warning: <a href="http://www.psychopy.org/general/timing' '/reducingFrameDrops.html?highlight=OpenGL+2.0">OpenGL ' '2.0 or higher is ideal</a>.') warn = True report.append(('openGL version', items['openGLVersion'], msg, warn)) report.append(('openGL vendor', items['openGLVendor'], '', False)) report.append(('screen size', ' x '.join( map(str, items['windowSize_pix'])), '', False)) # report.append(('wait blanking', str(items['windowWaitBlanking']), '', # False)) warn = False msg = '' if not items['windowHaveShaders']: msg = _translate( 'Warning: <a href="http://www.psychopy.org/general/timing' '/reducingFrameDrops.html?highlight=shader">Rendering of' ' complex stimuli will be slow</a>.') warn = True report.append(('have shaders', str( items['windowHaveShaders']), msg, warn)) warn = False msg = _translate( 'during the drifting <a href="http://www.psychopy.org/api/' 'visual/gratingstim.html">GratingStim</a>') if items['windowRefreshTimeMedian_ms'] < 3.3333333: msg = _translate( "Warning: too fast? visual sync'ing with the monitor" " seems unlikely at 300+ Hz") warn = True report.append(('visual sync (refresh)', "%.2f ms/frame" % items['windowRefreshTimeMedian_ms'], msg, warn)) msg = _translate('SD < 0.5 ms is ideal (want low variability)') warn = False if items['windowRefreshTimeSD_ms'] > .5: msg = _translate( 'Warning: the refresh rate has high frame-to-frame ' 'variability (SD > 0.5 ms)') warn = True report.append(('refresh stability (SD)', "%.2f ms" % items['windowRefreshTimeSD_ms'], msg, warn)) # draw 100 dots as a minimally demanding visual test: # first get baseline frame-rate (safe as possible, no drawing): avg, sd, median = visual.getMsPerFrame(win) dots100 = visual.DotStim( win, nDots=100, speed=0.005, dotLife=12, dir=90, coherence=0.2, dotSize=8, fieldShape='circle', autoLog=False) win.recordFrameIntervals = True win.frameIntervals = [] win.flip() for i in range(180): dots100.draw() win.flip() msg = _translate( 'during <a href="http://www.psychopy.org/api/visual/' 'dotstim.html">DotStim</a> with 100 random dots') warn = False intervalsMS = np.array(win.frameIntervals) * 1000 nTotal = len(intervalsMS) nDropped = sum(intervalsMS > (1.5 * median)) if nDropped: msg = _translate( 'Warning: could not keep up during <a href="http://' 'www.psychopy.org/api/visual/dotstim.html">DotStim</a>' ' with 100 random dots.') warn = True report.append(('no dropped frames', '%i / %i' % (nDropped, nTotal), msg, warn)) win.recordFrameIntervals = False if verbose: report.append(('openGL max vertices', str(items['openGLmaxVerticesInVertexArray']), '', False)) keyList = ('GL_ARB_multitexture', 'GL_EXT_framebuffer_object', 'GL_ARB_fragment_program', 'GL_ARB_shader_objects', 'GL_ARB_vertex_shader', 'GL_ARB_texture_float', 'GL_ARB_texture_non_power_of_two', 'GL_STEREO') for key in keyList: val = items['openGLext.' + key] # boolean if not val: val = '<strong>' + str(val) + '</strong>' report.append((key, str(val), '', False)) # ----- AUDIO: ----- report.append(('Audio', '', '', False)) msg = '' warn = False if not 'systemPyoVersion' in items: msg = _translate( 'Warning: pyo is needed for sound and microphone.') warn = True items['systemPyoVersion'] = _translate('(missing)') # elif items['systemPyoVersion'] < '0.6.2': # msg = 'pyo 0.6.2 compiled with --no-messages will # suppress start-up messages' report.append(('pyo', items['systemPyoVersion'], msg, warn)) # TO-DO: add microphone + playback as sound test # ----- NUMERIC: ----- report.append(('Numeric', '', '', False)) report.append(('numpy', items['pythonNumpyVersion'], _translate('vector-based (fast) calculations'), False)) report.append(('scipy', items['pythonScipyVersion'], _translate('scientific / numerical'), False)) report.append(('matplotlib', items['pythonMatplotlibVersion'], _translate('plotting; fast contains(), overlaps()'), False)) # ----- SYSTEM: ----- report.append(('System', '', '', False)) report.append(('platform', items['systemPlatform'], '', False)) msg = _translate('for online help, usage statistics, software ' 'updates, and google-speech') warn = False if items['systemHaveInternetAccess'] is not True: items['systemHaveInternetAccess'] = 'False' msg = _translate('Warning: could not connect (no proxy attempted)') warn = True # TO-DO: dlg to query whether to try to auto-detect (can take a # while), or allow manual entry of proxy str, save into prefs val = str(items['systemHaveInternetAccess']) report.append(('internet access', val, msg, warn)) report.append(('auto proxy', str(self.prefs.connections['autoProxy']), _translate('try to auto-detect a proxy if needed; see' ' <a href="http://www.psychopy.org/general' '/prefs.html#connection-settings-connection' 's">Preferences -> Connections</a>'), False)) if not self.prefs.connections['proxy'].strip(): prx = ' --' else: prx = str(self.prefs.connections['proxy']) report.append(('proxy setting', prx, _translate('current manual proxy setting from <a ' 'href="http://www.psychopy.org/general/' 'prefs.html#connection-settings-connections' '">Preferences -> Connections</a>'), False)) txt = 'CPU speed test' report.append((txt, "%.3f s" % items['systemTimeNumpySD1000000_sec'], _translate('numpy.std() of 1,000,000 data points'), False)) # TO-DO: more speed benchmarks # - load large image file from disk # - transfer image to GPU # ----- IMPORTS (relevant for developers & non-StandAlone): ----- if verbose: # always False for a real first-run report.append((_translate('Python packages'), '', '', False)) packages = ['PIL', 'openpyxl', 'lxml', 'setuptools', 'pytest', 'sphinx', 'psignifit', 'pyserial', 'pp', 'pynetstation', 'labjack'] if sys.platform == 'win32': packages.append('pywin32') packages.append('winioport') if constants.PY3: pkgError = ModuleNotFoundError else: pkgError = ImportError for pkg in packages: try: if pkg == 'PIL': import PIL ver = PIL.__version__ # elif pkg == 'lxml': # elif pkg == 'pynetstation': from psychopy.hardware import egi ver = 'import ok' elif pkg == 'pyserial': import serial ver = serial.VERSION elif pkg == 'pywin32': import win32api ver = 'import ok' else: exec('import ' + pkg) try: ver = eval(pkg + '.__version__') except Exception: ver = 'imported but no version info' report.append((pkg, ver, '', False)) except (pkgError, AttributeError): msg = _translate('could not import package %s') report.append((pkg, ' --', msg % pkg, False)) # rewrite to avoid assumption of locale en_US: self.warnings = list( set([key for key, val, msg, warn in report if warn])) return report
def runDiagnostics(self, win, verbose=False): """Return list of (key, val, msg, warn) tuple, set self.warnings All tuple elements will be of <type str>. msg can depend on val; warn==True indicates a concern. Plain text is returned, expected to be used in html <table>. Hyperlinks can be embedded as <a href="..."> """ report = [] # add item tuples in display order # get lots of info and do quick-to-render visual (want no frames drop): # for me, grating draw times are: mean 0.53 ms, SD 0.77 ms items = info.RunTimeInfo(win=win, refreshTest="grating", verbose=True, userProcsDetailed=True) totalRAM, freeRAM = items["systemMemTotalRAM"], items["systemMemFreeRAM"] warn = False if freeRAM == "unknown": if totalRAM != "unknown": totalRAM = "%.1fG" % (totalRAM / 1024.0) msg = _translate("could not assess available physical RAM; total %s") % totalRAM report.append(("available memory", "unknown", msg, warn)) else: msg = _translate("physical RAM available for configuration test (of %.1fG total)") % (totalRAM / 1024.0) if freeRAM < 300: # in M msg = _translate("Warning: low available physical RAM for configuration test (of %.1fG total)") % ( totalRAM / 1024.0 ) warn = True report.append(("available memory", unicode(freeRAM) + "M", msg, warn)) # ----- PSYCHOPY: ----- warn = False report.append(("PsychoPy", "", "", False)) # not localized report.append(("psychopy", __version__, _translate("avoid upgrading during an experiment"), False)) report.append( ( "locale", items["systemLocale"], _translate( 'can be set in <a href="http://www.psychopy.org/general/prefs.html#application-settings">Preferences -> App</a>' ), False, ) ) msg = "" if items["pythonVersion"] < "2.5" or items["pythonVersion"] >= "3": msg = _translate("Warning: python 2.6 or 2.7 required; 2.5 is not supported but might work") warn = True if "EPD" in items["pythonFullVersion"]: msg += " Enthought Python Distribution" elif "PsychoPy2.app" in items["pythonExecutable"]: msg += " (PsychoPy StandAlone)" bits, linkage = platform.architecture() # if not bits.startswith('32'): # msg = 'Warning: 32-bit python required; ' + msg report.append(("python version", items["pythonVersion"] + " (%s)" % bits, msg, warn)) warn = False if verbose: msg = "" if items["pythonWxVersion"] < "2.8.10": msg = _translate("Warning: wx 2.8.10 or higher required") warn = True report.append(("wx", items["pythonWxVersion"], "", warn)) report.append(("pyglet", items["pythonPygletVersion"][:32], "", False)) report.append(("rush", str(items["psychopyHaveExtRush"]), _translate("for high-priority threads"), False)) # ----- VISUAL: ----- report.append(("Visual", "", "", False)) warn = False # openGL settings: msg = "" if items["openGLVersion"] < "2.": msg = _translate( 'Warning: <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=OpenGL+2.0">OpenGL 2.0 or higher is ideal</a>.' ) warn = True report.append(("openGL version", items["openGLVersion"], msg, warn)) report.append(("openGL vendor", items["openGLVendor"], "", False)) report.append(("screen size", " x ".join(map(str, items["windowSize_pix"])), "", False)) # report.append(('wait blanking', str(items['windowWaitBlanking']), '', False)) warn = False msg = "" if not items["windowHaveShaders"]: msg = _translate( 'Warning: <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=shader">Rendering of complex stimuli will be slow</a>.' ) warn = True report.append(("have shaders", str(items["windowHaveShaders"]), msg, warn)) warn = False msg = _translate( 'during the drifting <a href="http://www.psychopy.org/api/visual/gratingstim.html">GratingStim</a>' ) if items["windowRefreshTimeMedian_ms"] < 3.3333333: msg = _translate("""Warning: too fast? visual sync'ing with the monitor seems unlikely at 300+ Hz""") warn = True report.append(("visual sync (refresh)", "%.2f ms/frame" % items["windowRefreshTimeMedian_ms"], msg, warn)) msg = _translate("SD < 0.5 ms is ideal (want low variability)") warn = False if items["windowRefreshTimeSD_ms"] > 0.5: msg = _translate("Warning: the refresh rate has high frame-to-frame variability (SD > 0.5 ms)") warn = True report.append(("refresh stability (SD)", "%.2f ms" % items["windowRefreshTimeSD_ms"], msg, warn)) # draw 100 dots as a minimally demanding visual test: # first get baseline frame-rate (safe as possible, no drawing): avg, sd, median = visual.getMsPerFrame(win) dots100 = visual.DotStim( win, nDots=100, speed=0.005, dotLife=12, dir=90, coherence=0.2, dotSize=8, fieldShape="circle", autoLog=False, ) win.recordFrameIntervals = True win.frameIntervals = [] win.flip() for i in xrange(180): dots100.draw() win.flip() msg = _translate( 'during <a href="http://www.psychopy.org/api/visual/dotstim.html">DotStim</a> with 100 random dots' ) warn = False intervalsMS = np.array(win.frameIntervals) * 1000 nTotal = len(intervalsMS) nDropped = sum(intervalsMS > (1.5 * median)) if nDropped: msg = _translate( 'Warning: could not keep up during <a href="http://www.psychopy.org/api/visual/dotstim.html">DotStim</a> with 100 random dots.' ) warn = True report.append(("no dropped frames", "%i / %i" % (nDropped, nTotal), msg, warn)) win.recordFrameIntervals = False msg = _translate("for movies") warn = False try: from pyglet.media import avbin except: # not sure what error to catch, WindowsError not found report.append( ( "pyglet avbin", "import error", _translate("Warning: could not import avbin; playing movies will not work"), True, ) ) else: ver = avbin.get_version() if sys.platform.startswith("linux"): if not (7 <= ver < 8): msg = _translate("Warning: version 7 recommended on linux (for movies)") warn = True elif not (5 <= ver < 6): msg = _translate( 'Warning: version 5 recommended (for movies); Visit <a href="http://code.google.com/p/avbin">download page</a> [google.com]' ) warn = True report.append(("pyglet avbin", unicode(ver), msg, warn)) if verbose: report.append(("openGL max vertices", str(items["openGLmaxVerticesInVertexArray"]), "", False)) keyList = [ "GL_ARB_multitexture", "GL_EXT_framebuffer_object", "GL_ARB_fragment_program", "GL_ARB_shader_objects", "GL_ARB_vertex_shader", "GL_ARB_texture_non_power_of_two", "GL_ARB_texture_float", "GL_STEREO", ] for key in keyList: val = items["openGLext." + key] # boolean if not val: val = "<strong>" + str(val) + "</strong>" report.append((key, str(val), "", False)) # ----- AUDIO: ----- report.append(("Audio", "", "", False)) msg = "" warn = False if not "systemPyoVersion" in items: msg = _translate("Warning: pyo is needed for sound and microphone.") warn = True items["systemPyoVersion"] = _translate("(missing)") # elif items['systemPyoVersion'] < '0.6.2': # msg = 'pyo 0.6.2 compiled with --no-messages will suppress start-up messages' report.append(("pyo", items["systemPyoVersion"], msg, warn)) # sound latencies from portaudio; requires pyo svn r1024 try: sndInputDevices = items["systemPyo.InputDevices"] warn = False if len(sndInputDevices.keys()): key = sndInputDevices.keys()[0] mic = sndInputDevices[key] if mic["name"].endswith("icroph"): mic["name"] += "one" # portaudio (?) seems to clip to 16 chars msg = '"%s"' % mic["name"] if mic["latency"] > 0.01: msg = _translate('Warning: "%s" latency > 10ms') % mic["name"] warn = True report.append(("microphone latency", "%.4f s" % mic["latency"], msg, warn)) else: report.append(("microphone", _translate("(not detected)"), "", False)) sndOutputDevices = items["systemPyo.OutputDevices"] if len(sndOutputDevices.keys()): warn = False key = sndOutputDevices.keys()[0] spkr = sndOutputDevices[key] msg = '"%s"' % spkr["name"] if spkr["latency"] > 0.01: msg = _translate('Warning: "%s" latency > 10ms') % spkr["name"] warn = True report.append(("speakers latency", "%.4f s" % spkr["latency"], msg, warn)) else: report.append(("speakers", _translate("(not detected)"), "", False)) except KeyError: pass s2t = '<a href="http://www.psychopy.org/api/microphone.html?highlight=Speech2Text">speech-to-text</a>' msg = _translate("audio codec for %s and sound file compression") % s2t warn = False if not "systemFlacVersion" in items: msg = ( _translate("Warning: flac is needed for using %s and sound compression.") % s2t + ' <a href="http://flac.sourceforge.net/download.html">' + _translate("Download</a> [sourceforge.net].") ) warn = True items["systemFlacVersion"] = _translate("(missing)") if verbose: report.append(("flac", items["systemFlacVersion"].lstrip("flac "), msg, warn)) # TO-DO: add microphone + playback as sound test # ----- NUMERIC: ----- report.append(("Numeric", "", "", False)) report.append(("numpy", items["pythonNumpyVersion"], _translate("vector-based (fast) calculations"), False)) report.append(("scipy", items["pythonScipyVersion"], _translate("scientific / numerical"), False)) report.append( ("matplotlib", items["pythonMatplotlibVersion"], _translate("plotting; fast contains(), overlaps()"), False) ) # ----- SYSTEM: ----- report.append(("System", "", "", False)) report.append(("platform", items["systemPlatform"], "", False)) msg = _translate("for online help, usage statistics, software updates, and google-speech") warn = False if items["systemHaveInternetAccess"] is not True: items["systemHaveInternetAccess"] = "False" msg = _translate("Warning: could not connect (no proxy attempted)") warn = True # TO-DO: dlg to query whether to try to auto-detect (can take a while), or allow manual entry of proxy str, save into prefs val = str(items["systemHaveInternetAccess"]) report.append(("internet access", val, msg, warn)) report.append( ( "auto proxy", str(self.prefs.connections["autoProxy"]), _translate( 'try to auto-detect a proxy if needed; see <a href="http://www.psychopy.org/general/prefs.html#connection-settings">Preferences -> Connections</a>' ), False, ) ) if not self.prefs.connections["proxy"].strip(): prx = "  --" else: prx = unicode(self.prefs.connections["proxy"]) report.append( ( "proxy setting", prx, _translate( 'current manual proxy setting from <a href="http://www.psychopy.org/general/prefs.html#connection-settings">Preferences -> Connections</a>' ), False, ) ) msg = "" warn = False # assure that we have a list if items["systemUserProcFlagged"] is None: items["systemUserProcFlagged"] = [] items["systemUserProcFlagged"].sort() self.badBgProc = [p for p, pid in items["systemUserProcFlagged"]] if len(self.badBgProc): val = "%s ..." % self.badBgProc[0] msg = _translate( 'Warning: Some <a href="http://www.psychopy.org/general/timing/reducingFrameDrops.html?highlight=background+processes">background processes</a> can adversely affect timing' ) warn = True else: val = _translate("No bad background processes active.") report.append(("background processes", val, msg, warn)) if verbose and "systemSec.OpenSSLVersion" in items: report.append( ( "OpenSSL", items["systemSec.OpenSSLVersion"].lstrip("OpenSSL "), 'for <a href="http://www.psychopy.org/api/encryption.html">encryption</a>', False, ) ) report.append( ( "CPU speed test", "%.3f s" % items["systemTimeNumpySD1000000_sec"], _translate("numpy.std() of 1,000,000 data points"), False, ) ) # TO-DO: more speed benchmarks # - load large image file from disk # - transfer image to GPU # ----- IMPORTS (relevant for developers & non-StandAlone): ----- if verbose: # always False for a real first-run report.append((_translate("Python packages"), "", "", False)) packages = [ "PIL", "openpyxl", "lxml", "setuptools", "pytest", "sphinx", "psignifit", "pyserial", "pp", "pynetstation", "ioLabs", "labjack", ] if sys.platform == "win32": packages.append("pywin32") packages.append("winioport") for pkg in packages: try: if pkg == "PIL": exec ("import PIL.Image") ver = PIL.Image.VERSION # elif pkg == 'lxml': # elif pkg == "pp": exec ("import pp; ver = pp.version") elif pkg == "pynetstation": exec ("from psychopy.hardware import egi") ver = "import ok" elif pkg == "pyserial": exec ("import serial") ver = serial.VERSION elif pkg == "pywin32": exec ("import win32api") ver = "import ok" else: exec ("import " + pkg) try: ver = eval(pkg + ".__version__") except: ver = "import ok" report.append((pkg, ver, "", False)) except (ImportError, AttributeError): report.append((pkg, "  --", _translate("could not import package %s") % pkg, False)) # rewrite to avoid assumption of locale en_US: self.warnings = list(set([key for key, val, msg, warn in report if warn])) return report
def __init__(self, fixation_duration, max_dot_duration, min_iti_duration, break_duration): ''' RDMD task hemifield = hemifield to show dot stim in (right or left) fixation_duration = duration of fixation (ms) max_dot_duration = max duration of dot stimuli (ms) min_iti_duration = min inter-trial-interval (ms) break_duration = duration of break (ms) ''' #create window and stimuli # Window to use self.wintype='pyglet' # use pyglet if possible, it's faster at event handling self.win = visual.Window( [1280,1024], monitor=MONITOR, screen=SCREEN, units="deg", fullscr=True, color=[-1,-1,-1], winType=self.wintype) self.win.setMouseVisible(False) event.clearEvents() # Measure frame rate self.mean_ms_per_frame, std_ms_per_frame, median_ms_per_frame=visual.getMsPerFrame(self.win, nFrames=60, showVisual=True) # Compute number of frames for fixation self.fixation_duration=fixation_duration self.fixation_frames=int(fixation_duration/self.mean_ms_per_frame) # Compute max number of frames for dot stimuli self.max_dot_duration=max_dot_duration self.max_dot_frames=int(max_dot_duration/self.mean_ms_per_frame) # Compute minimum inter-trial-interval frames (will be adjusted based on time of other stimuli) self.min_iti_duration=min_iti_duration self.min_iti_frames=int(min_iti_duration/self.mean_ms_per_frame) self.break_duration=break_duration self.break_duration_frames=int(break_duration/self.mean_ms_per_frame) # fixation stimulus self.fixation = visual.PatchStim( self.win, units='deg', tex=None, mask='circle', sf=0, size=0.5, name='fixation', autoLog=False, color=[1,1,1] ) # dot stimulus self.dots = visual.DotStim( win=self.win, name='dots', nDots=200, # number of dots dotSize=2, # dot size in degrees speed=0.4, # 60Hz refresh - 16.7ms/frame, 4deg/s=.0668deg/frame dir=0.0, # 0=right, 180=left coherence=12.5, # percentage of dots moving in the same direction fieldPos=[0.0, 0.0], # centered on the screen fieldSize=10.0, # field is 10 deg wide fieldShape='circle', # circle shaped field signalDots='different', # are the signal and noise dots 'different' or 'same' popns (see Scase et al) noiseDots='direction', # do the noise dots follow random- 'walk', 'direction', or 'position' dotLife=3, # number of frames for each dot to be drawn color=[1.0,1.0,1.0], # white dots colorSpace='rgb', opacity=1, # fully opaque depth=-1.0 ) self.training_message = visual.TextStim(self.win, wrapWidth=30, pos=[0,3]) # Clock for computing response time self.rt_clock = core.Clock()