Launch an fMRI experiment: Test or Scan

Idea: Run or debug an experiment script using exactly the same code, i.e., for both testing and online data acquisition. To debug timing, you can emulate sync pulses and user responses. Limitations: pyglet only; keyboard events only.

class psychopy.hardware.emulator.ResponseEmulator(simResponses=None)

Class to allow simulation of a user’s keyboard responses during a scan.

Given a list of response tuples (time, key), the thread will simulate a user pressing a key at a specific time (relative to the start of the run).

Author: Jeremy Gray; Idea: Mike MacAskill

daemon

A boolean value indicating whether this thread is a daemon thread (True) or not (False).

This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.

The entire Python program exits when no alive non-daemon threads are left.

ident

Thread identifier of this thread or None if it has not been started.

This is a nonzero integer. See the thread.get_ident() function. Thread identifiers may be recycled when a thread exits and another thread is created. The identifier is available even after the thread has exited.

isAlive()

Return whether the thread is alive.

This method returns True just before the run() method starts until just after the run() method terminates. The module function enumerate() returns a list of all alive threads.

is_alive()

Return whether the thread is alive.

This method returns True just before the run() method starts until just after the run() method terminates. The module function enumerate() returns a list of all alive threads.

join(timeout=None)

Wait until the thread terminates.

This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception or until the optional timeout occurs.

When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call isAlive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.

When the timeout argument is not present or None, the operation will block until the thread terminates.

A thread can be join()ed many times.

join() raises a RuntimeError if an attempt is made to join the current thread as that would cause a deadlock. It is also an error to join() a thread before it has been started and attempts to do so raises the same exception.

name

A string used for identification purposes only.

It has no semantics. Multiple threads may be given the same name. The initial name is set by the constructor.

run()

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.

start()

Start the thread’s activity.

It must be called at most once per thread object. It arranges for the object’s run() method to be invoked in a separate thread of control.

This method will raise a RuntimeError if called more than once on the same thread object.

class psychopy.hardware.emulator.SyncGenerator(TR=1.0, TA=1.0, volumes=10, sync='5', skip=0, sound=False, **kwargs)

Class for a character-emitting metronome thread (emulate MR sync pulse).

Aim: Allow testing of temporal robustness of fMRI scripts by emulating a hardware sync pulse. Adds an arbitrary ‘sync’ character to the key buffer, with sub-millisecond precision (less precise if CPU is maxed). Recommend: TR=1.000 or higher and less than 100% CPU. Shorter TR –> higher CPU load.

Parameters:
  • TR – seconds between volume acquisitions
  • TA – seconds to acquire one volume
  • volumes – number of 3D volumes to obtain in a given scanning run
  • sync – character used as flag for sync timing, default=‘5’
  • skip – how many frames to silently omit initially during T1 stabilization, no sync pulse. Not needed to test script timing, but will give more accurate feel to start of run. aka “discdacqs”.
  • sound – simulate scanner noise
daemon

A boolean value indicating whether this thread is a daemon thread (True) or not (False).

This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.

The entire Python program exits when no alive non-daemon threads are left.

ident

Thread identifier of this thread or None if it has not been started.

This is a nonzero integer. See the thread.get_ident() function. Thread identifiers may be recycled when a thread exits and another thread is created. The identifier is available even after the thread has exited.

isAlive()

Return whether the thread is alive.

This method returns True just before the run() method starts until just after the run() method terminates. The module function enumerate() returns a list of all alive threads.

is_alive()

Return whether the thread is alive.

This method returns True just before the run() method starts until just after the run() method terminates. The module function enumerate() returns a list of all alive threads.

join(timeout=None)

Wait until the thread terminates.

This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception or until the optional timeout occurs.

When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call isAlive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.

When the timeout argument is not present or None, the operation will block until the thread terminates.

A thread can be join()ed many times.

join() raises a RuntimeError if an attempt is made to join the current thread as that would cause a deadlock. It is also an error to join() a thread before it has been started and attempts to do so raises the same exception.

name

A string used for identification purposes only.

It has no semantics. Multiple threads may be given the same name. The initial name is set by the constructor.

run()

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.

start()

Start the thread’s activity.

It must be called at most once per thread object. It arranges for the object’s run() method to be invoked in a separate thread of control.

This method will raise a RuntimeError if called more than once on the same thread object.

psychopy.hardware.emulator.launchScan(win, settings, globalClock=None, simResponses=None, mode=None, esc_key='escape', instr='select Scan or Test, press enter', wait_msg='waiting for scanner...', wait_timeout=300, log=True)

Accepts up to four fMRI scan parameters (TR, volumes, sync-key, skip), and launches an experiment in one of two modes: Scan, or Test.

Usage:

See Coder Demo -> experiment control -> fMRI_launchScan.py.

In brief: 1) from psychopy.hardware.emulator import launchScan; 2) Define your args; and 3) add ‘vol = launchScan(args)’ at the top of your experiment script.

launchScan() waits for the first sync pulse and then returns, allowing your experiment script to proceed. The key feature is that, in test mode, it first starts an autonomous thread that emulates sync pulses (i.e., emulated by your CPU rather than generated by an MRI machine). The thread places a character in the key buffer, exactly like a keyboard event does. launchScan will wait for the first such sync pulse (i.e., character in the key buffer). launchScan returns the number of sync pulses detected so far (i.e., 1), so that a script can account for them explicitly.

If a globalClock is given (highly recommended), it is reset to 0.0 when the first sync pulse is detected. If a mode was not specified when calling launchScan, the operator is prompted to select Scan or Test.

If scan mode is selected, the script will wait until the first scan pulse is detected. Typically this would be coming from the scanner, but note that it could also be a person manually pressing that key.

If test mode is selected, launchScan() starts a separate thread to emit sync pulses / key presses. Note that this thread is effectively nothing more than a key-pressing metronome, emitting a key at the start of every TR, doing so with high temporal precision.

If your MR hardware interface does not deliver a key character as a sync flag, you can still use launchScan() to test script timing. You have to code your experiment to trigger on either a sync character (to test timing) or your usual sync flag (for actual scanning).

Parameters:

win: a Window object (required)

settings : a dict containing up to 5 parameters

(2 required: TR, volumes)

TR :

seconds per whole-brain volume (minimum value = 0.1s)

volumes :

number of whole-brain (3D) volumes to obtain in a given scanning run.

sync :

(optional) key for sync timing, default = ‘5’.

skip :

(optional) how many volumes to silently omit initially (during T1 stabilization, no sync pulse). default = 0.

sound :

(optional) whether to play a sound when simulating scanner sync pulses

globalClock :

optional but highly recommended Clock to be used during the scan; if one is given, it is reset to 0.000 when the first sync pulse is received.

simResponses :

optional list of tuples [(time, key), (time, key), …]. time values are seconds after the first scan pulse is received.

esc_key :

key to be used for user-interrupt during launch. default = ‘escape’

mode :

if mode is ‘Test’ or ‘Scan’, launchScan() will start in that mode.

instr :

instructions to be displayed to the scan operator during mode selection.

wait_msg :

message to be displayed to the subject while waiting for the scan to start (i.e., after operator indicates start but before the first scan pulse is received).

wait_timeout :

time in seconds that launchScan will wait before assuming something went wrong and exiting. Defaults to 300sec (5 min). Raises a RuntimeError if no sync pulse is received in the allowable time.

class psychopy.hardware.emulator.ResponseEmulator(simResponses=None)

Class to allow simulation of a user’s keyboard responses during a scan.

Given a list of response tuples (time, key), the thread will simulate a user pressing a key at a specific time (relative to the start of the run).

Author: Jeremy Gray; Idea: Mike MacAskill

run()

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.

class psychopy.hardware.emulator.SyncGenerator(TR=1.0, TA=1.0, volumes=10, sync='5', skip=0, sound=False, **kwargs)

Class for a character-emitting metronome thread (emulate MR sync pulse).

Aim: Allow testing of temporal robustness of fMRI scripts by emulating a hardware sync pulse. Adds an arbitrary ‘sync’ character to the key buffer, with sub-millisecond precision (less precise if CPU is maxed). Recommend: TR=1.000 or higher and less than 100% CPU. Shorter TR –> higher CPU load.

Parameters:
  • TR – seconds between volume acquisitions
  • TA – seconds to acquire one volume
  • volumes – number of 3D volumes to obtain in a given scanning run
  • sync – character used as flag for sync timing, default=‘5’
  • skip – how many frames to silently omit initially during T1 stabilization, no sync pulse. Not needed to test script timing, but will give more accurate feel to start of run. aka “discdacqs”.
  • sound – simulate scanner noise
run()

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.


Back to top