Adding fMRI null events

Hey everyone!

Thanks to some amazing help from this forum, I have been able to randomize the order of presentation of my stimuli following sequentially a list from an excel sheet. I’ll just quickly put here the link to the post if it helps: Pseudorandomization for fMRI - #5 by jonathan.kominsky

So, I have an Excel sheet with the order of presentation of my conditions: happy, sad, ambiguous, and control. Each trial lasts 5.6s comprising two routines: presentation of stimuli, and rating.

However, I need to add null events (fixation cross) of different lengths depending on what is written on the Excel sheet (see picture) - it is the output of Optseq2!

So when it is sad, control, happy, or ambiguous, my code goes find the right images and set them in the routine presentation of stimuli, but when it is NE, I need to display a fixation cross for the time on the left (1.4s, 7s, 5.6s… NB: which is a multiple of the MRI TR).

  1. How can I make conditional timing for fixation cross? Is it possible?
  2. How can I do something like: if condition == happy, show the routine presentation of stimuli + rating, and if condition == NE, don’t show the routines, and only show a fixation cross for x seconds (Reminder: the excel sheet is a sequential order)!

Thank you in advance!

Best wishes,

Alexane

First, you might find this thread relevant in general though not to these specific questions: Confusion about how to implement non-slip timing for trials with known end points

Yes. You can have a code component with something like this:

Before routine

countdownTimer = core.Clock()
countdownTimer.addTime(thisOrder_loop['time_presentation'])

Each frame

if countdownTimer.getTime() <= 0:
    continueRoutine = False

That code is fairly modular, you can kind of fit it in or around your existing code without too much difficulty.

There are a few different ways to do it.

One is to have something which sets the image autoDraw property of the images to False, which means it shouldn’t appear. You can put this in your Before Routine code but you may need to add it to the Each Frame code as well. If the fixation cross normally has an offset time, you could set the autoDraw property to True in the Each Frame code (or just call its draw() function directly in that code), which should override the duration time-out.

Another approach is to have an actual branching path where the fixation-only trial is a separate trial altogether. However, that might mess with your timing because there isn’t really a way to do it without creating an additional trial in the loop that will take up at least one frame. So I think for an fMRI experiment specifically it’s probably best to just have the one trial type and control what is or isn’t drawn in.

Thank you for your very quick answer!

I am not sure I understand your point…

At some point, I actually thought about this: I set the opacity of all the elements in a trial to 0 when it was NE, and then to 1 when it was happy, sad, ambiguous, or control, and it kind of works (although there are lots of mistakes), but the problem with that is that it means that all NE trials have also to be 5.6s (the length of other trials). But I need them to have a conditional timing depending on what is written in the other column!

If I understand correctly, what you suggested is that I put a conditional timing on the fixation cross at the beginning of the trial, and that would overwrite all the images etc and the other routine in my loop called rating? Because sometimes the fixation cross has to be there for 2s and then goes to another trial, but sometimes 7s, and the trial is always 5.6s, so how can the fixation cross stay 7s for instance and overwrite everything else? (I don’t understand the draw function maybe!)

Best,

Alexane

Right. Basically, you can create layers of conditional timers. Looking closely I see that the length of the NE trial can be longer than the 5.6 seconds of every other trial type, so you probably want two different timers, one for the fixation cross and one for the trial as a whole, and independent code that controls whether the other stimuli appear.

Before routine:

fixationCountdown = core.Clock()
trialCountdown = core.Clock()
if thisOrder_loop['condition'] != 'NE':
    fixationCountdown.addTime(2.0)
    # if 2s is how long the fixation should appear in a regular trial
else:
    fixationCountdown.addTime(20)
    # arbitrary but should be longer than longest NE trial, which will end 
    # before the fixation disappears

trialCountdown.addTime(thisOrder_loop['time_presentation'])
# since you have this column even for the regular trials, you can just use it unconditionally

Each frame:

if fixationCountdown.getTime() <= 0:
    fixation.autoDraw = False # hides the fixation
    # you could also add code that shows the other stimuli here or just do it
    # w/the builder

if trialCountdown.getTime() <= 0:
    continueRoutine = False # ends the trial

if thisOrder_loop['condition'] == 'NE':
    # some code that hides everything but the fixation cross

It looks good, I will try to implement that.

How can I change your code so that it is not second but frames? I am only working with frames!

Even easier. You just create a counter and decrement it in your each frame code. I’m assuming you’re working with a 60Hz display here (60 frames per second), but there are also ways to get the framerate from within PsychoPy if necessary (look at the ‘visual.Window’ documentation in the reference on the PsychoPy website).

fixationCountdown = 0
trialCountdown = 0
fps = 60 # I'm just assuming this for now
if thisOrder_loop['condition'] != 'NE':
    fixationCountdown = 2*fps
    # if 2s is how long the fixation should appear in a regular trial
else:
    fixationCountdown = 20*fps
    # arbitrary but should be longer than longest NE trial, which will end 
    # before the fixation disappears

trialCountdown = thisOrder_loop['time_presentation']*fps
# since you have this column even for the regular trials, you can just use it unconditionally

Each frame:

fixationCountdown -= 1
trialCountdown -= 1
# decrements each of the counters by 1.

if fixationCountdown <= 0:
    fixation.autoDraw = False # hides the fixation
    # you could also add code that shows the other stimuli here or just do it
    # w/the builder

if trialCountdown <= 0:
    continueRoutine = False # ends the trial

if thisOrder_loop['condition'] == 'NE':
    # some code that hides everything but the fixation cross