Millisecond Forums

How to match stimulus presentation time

https://forums.millisecond.com/Topic22658.aspx

By MartE - 10/10/2017

Hi Dave,
I have a question regarding how to optimally match stimulus presentation time based on former presentation time.
In the task, a target stimulus is presented (screen 1). Next, participants see two stimuli (screen 2). One is the former target. Participants have to decide on which side the target was. After pressing a button, the stimuli disappear. Afterwards a new stimulus should be presented for as long as the participant saw either the target or the distractor in sum (screen 3). This means, the stimulus on the third screen (neutral) should be presented either the time screen 1 and screen 2 where presented OR the time only screen 2 was presented

My problem now is that I am not sure how to get the proper time. As the time the stimuli are displayed depends on the monitor refresh rate, which property takes this into account?
The latency measures the time from stimulus onset to response (or timeout if no response), but does not take into account, that after a response within a frame the picture can still be presented for a couple of milliseconds to finish the frame.
The timeout seems  to take also other processes into account (trial preparation etc.). And the timeout also seems not to take into account that a frame is not finished when timeout occurs.

Do I have to specify the numframes to be exact? But than I would have to calculate the number of frames based on each monitor (if there are differences). Or do I have to add a blank filler stimulus to get the stimulusonset for that and use this?

Code example:
<trial present>
            / ontrialbegin = [values.target=list.target.nextvalue; values.distractor=list.distractor.nextvalue; values.neutral=list.neutral.nextvalue]
            / ontrialbegin = [values.target_hpos=50%]
            / stimulustimes = [0= target, present_text]
            / timeout = 1000
            / beginresponsetime = 0
</trial>
<trial comp_T>
            / ontrialbegin = [values.target_hpos=list.2pos.nextvalue;
                                                            values.distractor_hpos=list.2pos.nextvalue; ]      
            / inputdevice = keyboard
            / validresponse = (30, 38)
            / responsemode = free
            / stimulustimes = [0= target, distractor]
            / beginresponsetime = 0
            / iscorrectresponse = [values.target_hpos >50% && trial.comp_T.response == 38]
            / iscorrectresponse = [values.target_hpos <50% && trial.comp_T.response == 30]
            / ontrialend = [if ((values.target_hpos <50% && trial.comp_T.response == 38)||(values.target_hpos >50% && trial.comp_T.response == 30)) {values.incorrect = TRUE}]
            / ontrialend = [values.duration = trial.comp_T.latency + trial.present.timeout]
</trial>
<trial comp_D>
            / ontrialbegin = [values.target_hpos=list.2pos.nextvalue;
                                                            values.distractor_hpos=list.2pos.nextvalue; ]      
            / inputdevice = keyboard
            / validresponse = (30, 38)
            / responsemode = free
            / stimulustimes = [0= target, distractor]
            / responsetime = 0
            / iscorrectresponse = [values.target_hpos >50% && trial.comp_D.response == 38]
            / iscorrectresponse = [values.target_hpos <50% && trial.comp_D.response == 30]
            / ontrialend = [if ((values.target_hpos <50% && trial.comp_D.response == 38)||(values.target_hpos >50% && trial.comp_D.response == 30)) {values.incorrect = TRUE}]
            / ontrialend = [values.duration = trial.comp_D.latency]
</trial>
<trial neutral>
            / ontrialbegin = [if( values.cond == "target_comp" || values.cond == "choice_comp_T") {values.duration = trial.comp_t.latency + trial.present.latency}]
            / ontrialbegin = [if( values.cond == "distractor_comp" || values.cond == "choice_comp_D") {values.duration = trial.comp_d.latency}]
            / ontrialbegin = [values.neutral_hpos=50%]
            / stimulustimes = [0= neutral, clearing]
            / timeout = values.duration
</trial>

Thanks!
By Dave - 10/10/2017

MartE - Tuesday, October 10, 2017
Hi Dave,
I have a question regarding how to optimally match stimulus presentation time based on former presentation time.
In the task, a target stimulus is presented (screen 1). Next, participants see two stimuli (screen 2). One is the former target. Participants have to decide on which side the target was. After pressing a button, the stimuli disappear. Afterwards a new stimulus should be presented for as long as the participant saw either the target or the distractor in sum (screen 3). This means, the stimulus on the third screen (neutral) should be presented either the time screen 1 and screen 2 where presented OR the time only screen 2 was presented

My problem now is that I am not sure how to get the proper time. As the time the stimuli are displayed depends on the monitor refresh rate, which property takes this into account?
The latency measures the time from stimulus onset to response (or timeout if no response), but does not take into account, that after a response within a frame the picture can still be presented for a couple of milliseconds to finish the frame.
The timeout seems  to take also other processes into account (trial preparation etc.). And the timeout also seems not to take into account that a frame is not finished when timeout occurs.

Do I have to specify the numframes to be exact? But than I would have to calculate the number of frames based on each monitor (if there are differences). Or do I have to add a blank filler stimulus to get the stimulusonset for that and use this?

Code example:
<trial present>
            / ontrialbegin = [values.target=list.target.nextvalue; values.distractor=list.distractor.nextvalue; values.neutral=list.neutral.nextvalue]
            / ontrialbegin = [values.target_hpos=50%]
            / stimulustimes = [0= target, present_text]
            / timeout = 1000
            / beginresponsetime = 0
</trial>
<trial comp_T>
            / ontrialbegin = [values.target_hpos=list.2pos.nextvalue;
                                                            values.distractor_hpos=list.2pos.nextvalue; ]      
            / inputdevice = keyboard
            / validresponse = (30, 38)
            / responsemode = free
            / stimulustimes = [0= target, distractor]
            / beginresponsetime = 0
            / iscorrectresponse = [values.target_hpos >50% && trial.comp_T.response == 38]
            / iscorrectresponse = [values.target_hpos <50% && trial.comp_T.response == 30]
            / ontrialend = [if ((values.target_hpos <50% && trial.comp_T.response == 38)||(values.target_hpos >50% && trial.comp_T.response == 30)) {values.incorrect = TRUE}]
            / ontrialend = [values.duration = trial.comp_T.latency + trial.present.timeout]
</trial>
<trial comp_D>
            / ontrialbegin = [values.target_hpos=list.2pos.nextvalue;
                                                            values.distractor_hpos=list.2pos.nextvalue; ]      
            / inputdevice = keyboard
            / validresponse = (30, 38)
            / responsemode = free
            / stimulustimes = [0= target, distractor]
            / responsetime = 0
            / iscorrectresponse = [values.target_hpos >50% && trial.comp_D.response == 38]
            / iscorrectresponse = [values.target_hpos <50% && trial.comp_D.response == 30]
            / ontrialend = [if ((values.target_hpos <50% && trial.comp_D.response == 38)||(values.target_hpos >50% && trial.comp_D.response == 30)) {values.incorrect = TRUE}]
            / ontrialend = [values.duration = trial.comp_D.latency]
</trial>
<trial neutral>
            / ontrialbegin = [if( values.cond == "target_comp" || values.cond == "choice_comp_T") {values.duration = trial.comp_t.latency + trial.present.latency}]
            / ontrialbegin = [if( values.cond == "distractor_comp" || values.cond == "choice_comp_D") {values.duration = trial.comp_d.latency}]
            / ontrialbegin = [values.neutral_hpos=50%]
            / stimulustimes = [0= neutral, clearing]
            / timeout = values.duration
</trial>

Thanks!

I think the clearest measure you can get of a trial's actual duration is to (1) store script.elapsedtime in a variable /ontrialbegin in trial "A" (the 1st trial in the chain), (2) store script.elapsedtime in a variable /ontrialbegin in trial "B" (the 2nd trial in the chain), (3) calculating the difference B-A, and using the result in your further duration calculations. Does that make sense?
By MartE - 10/10/2017

Dave - Tuesday, October 10, 2017

I think the clearest measure you can get of a trial's actual duration is to (1) store script.elapsedtime in a variable /ontrialbegin in trial "A" (the 1st trial in the chain), (2) store script.elapsedtime in a variable /ontrialbegin in trial "B" (the 2nd trial in the chain), (3) calculating the difference B-A, and using the result in your further duration calculations. Does that make sense?

Technically I already thought about something like that, but did now how to built it in Inquisit. So, yes this makes sense now. But would it also work to store elapsedtime at /ontrialbegin in trial A, store a second variable of elapsedtime at /ontrialend of trial A and calculate the difference to be more precise (and more flexible with filler trials in between)?
And as follow up question: Is there a way to save/read this information of real trialduration to/from the standard output file?


By Dave - 10/10/2017

MartE - Tuesday, October 10, 2017
Dave - Tuesday, October 10, 2017

I think the clearest measure you can get of a trial's actual duration is to (1) store script.elapsedtime in a variable /ontrialbegin in trial "A" (the 1st trial in the chain), (2) store script.elapsedtime in a variable /ontrialbegin in trial "B" (the 2nd trial in the chain), (3) calculating the difference B-A, and using the result in your further duration calculations. Does that make sense?

Technically I already thought about something like that, but did now how to built it in Inquisit. So, yes this makes sense now. But would it also work to store elapsedtime at /ontrialbegin in trial A, store a second variable of elapsedtime at /ontrialend of trial A and calculate the difference to be more precise (and more flexible with filler trials in between)?
And as follow up question: Is there a way to save/read this information of real trialduration to/from the standard output file?



> But would it also work to store elapsedtime at /ontrialbegin in trial A, store a second variable of elapsedtime at /ontrialend of trial A.

You can do that as well, although I'd argue that the other option would be (slightly) more precise.

Whatever option you go for, though, you can log that information to the data file by adding the respective values to the <data> element's /columns attribute.


<block myblock>
/ trials = [1-5 = sequence(a,b)]
</block>

<trial a>
/ ontrialbegin = [values.start_a = script.elapsedtime]
/ stimulusframes = [1=mytext]
/ timeout = 1000
</trial>

<trial b>
/ ontrialbegin = [values.start_b = script.elapsedtime;
    values.a_duration = values.start_b - values.start_a;]
/ stimulusframes = [1=mytext]
/ validresponse = (57)
</trial>

<values>
/ start_a = 0
/ start_b = 0
/ a_duration = 0
</values>

<text mytext>
/ items = ("<%script.currenttrial%>")
/ erase = false
</text>

<data>
/ columns = [date time subject group blocknum blockcode trialnum trialcode stimulusitem response latency correct values.a_duration]
/ separatefiles = true
</data>

By MartE - 10/10/2017

Thanks for your help Dave!
Just out of curiosity and to understand Inquisit better: Why is it more precise to store the information at the beginning of the two trials? Should it not be more precise to store data as directly as possible before and after the stimulus presentation within a trial? Between the end of a trial and the start of a new trial can also other processes happen and therefore time difference might be higher.
By MartE - 10/10/2017

I just tried out a little bit with the elapsed time solution. It seems the differences are relatively high. When I compare the duration provided by the latency and the duration i calculated with the elapsed time, there are differences of easily 100 to 200 ms. How precise is the ontrialbegin/end attribute when it comes to the time a stimulus is presented? Are there processes performed inbetween which need additional time? I question if this really is a more precise way to calculate the delay than based on the latency. I am not shure here how Inquisit works. But if latency ends a trial with an inaccuracy of up to one frame (when timeout or response is set nearly at a frame start), then the inaccuracy would be up to 2 frames (about 34 ms). In this case, the inaccuracy would be far less then based on the elapsed time.
By Dave - 10/11/2017

MartE - Wednesday, October 11, 2017
I just tried out a little bit with the elapsed time solution. It seems the differences are relatively high. When I compare the duration provided by the latency and the duration i calculated with the elapsed time, there are differences of easily 100 to 200 ms. How precise is the ontrialbegin/end attribute when it comes to the time a stimulus is presented? Are there processes performed inbetween which need additional time? I question if this really is a more precise way to calculate the delay than based on the latency. I am not shure here how Inquisit works. But if latency ends a trial with an inaccuracy of up to one frame (when timeout or response is set nearly at a frame start), then the inaccuracy would be up to 2 frames (about 34 ms). In this case, the inaccuracy would be far less then based on the elapsed time.

/ontrialbegin is executed when the trial object starts executing. That is, _before_ the trial presents any stimuli / begins its stimulus presentation sequence. After any /ontrialbegin logic is done, the trial does some more preparing of stimuli and then starts displaying them starting with the 1st display frame it can catch. In other words: The time you log /ontrialbegin is indicative of the start of the execution of the <trial>, not indicative of any stimulus onset _within_ that trial object.

Latency is measured relative to the stimulus presentation sequence, however.

So, in sum, any discrepancy you see between latency and the time calculations done /ontrialbegin and /ontrialend is due to stimulus preparation and waiting to catch the start of a display frame for the stimulus presentation sequence.

Does that clarify?
By MartE - 10/11/2017

Yes, this makes it clear how the attributes work. So basically, this means, the dependent presentation of trial 3 is most precise (closest) to the presentation duration of the previous trials when I use the latency instead of anything else. However, still dealing with a small uncertainty. Am I right?

By Dave - 10/11/2017

MartE - Wednesday, October 11, 2017
Yes, this makes it clear how the attributes work. So basically, this means, the dependent presentation of trial 3 is most precise (closest) to the presentation duration of the previous trials when I use the latency instead of anything else. However, still dealing with a small uncertainty. Am I right?


Yes, that's right (assuming I understand your design / its desired properties correctly).
By MartE - 10/11/2017


Great! Thanks for your help!