base timing of a trial on total time taken in previous trials--mismatch error?

273 views
Skip to first unread message

Janet Trammell

unread,
Jun 30, 2011, 2:18:23 PM6/30/11
to E-Prime
I am programming an experiment where the total time a person can take
is 10 minutes (600000 ms) (to do 12 trials), with a maximum of 2
minutes per trial (the trial terminates and starts the next trial
after 2 minutes). I have set the program to terminate the list if the
total time is over 10 minutes, however there is a problem--say a
person has taken 9:45 do to 7 trials, so they have 15 seconds left.
BUT...on trial 8, the duration is still set to max 2 min--so a person
can theoretically have a total of 11:45, not 10:00 before it
terminates. So I need to have the program know that if more than 8
minutes (480000) has been used, the duration of the trial should be
600000-however much time has been used so far.

I have tried to fix this by putting an attribute in the list object
called "trial" which is just the number of the trial, and another
attribute called "trialtime", which will be the duration of the slide
object (where the stimulus/trial is presented). in the slide object
properties, I have set timelimit to be the attribute "trialtime".
Lastly, after each trial, the attribute "timertrial" is calculated,
which essentially calculates the length of time taken for that trial
and adds it to the previous time taken, so that this attribute is
constantly updated to reflect the total time taken. All of this seems
to work fine, and running through the program shows that "timertrial"
is indeed calculating correctly and is recording in the edata file.
the problem occurs, however, when I put all this together, and try to
change the duration of the next trial based on the "timertrial"
attribute. Here is my code:

Using the inline statement below, I have set the first trial duration
is 120 seconds, and every other trial after that the duration is
calculated based on total time taken so far

If c.GetAttrib ("trial") = 1 then c.SetAttrib "trialtime", 120000
If c.GetAttrib ("trial") > 1 then
If c.getAttrib ("timertrial") >= 480000 then c.setAttrib "trialtime",
600000 - c.getAttrib ("timertrial")
If c.getAttrib ("timertrial") < 480000 then c.SetAttrib "trialtime",
120000
End If

The problem is, the program cannot find the attribute "timertrial"--it
calculates fine at the end of each trial, but when I start a new trial
(within the same list), it does not have a value yet for timertrial,
since that is calculated at the END of the trial. How do I get it to
pull the value of "timertrial" from the previous trial and use it as
an attribute in the NEXT trial? I am sure this is simple, but I can't
seem to get anywhere.

Also, Note: I have tried using this code instead:
If c.GetAttrib ("Trial") > 1 then
if LetterSetList.GetPrevAttrib ("timertrial") >= 480000 then
c.setAttrib "trialtime", 600000 - LetterSetList.GetPrevAttrib
("timertrial")
if LetterSetList.GetPrevAttrib ("timertrial") < 480000 then
c.setAttrib "trialtime", 120000
End If

And I keep getting type mismatch errors, which should be easy to
resolve, but this one keep stumping me. Again, I'm sure this is
something simple, but for some reason I can't get it.

David McFarlane

unread,
Jun 30, 2011, 5:26:58 PM6/30/11
to e-p...@googlegroups.com
This got addressed on the PST Forum
(http://www.pstnet.com/forum/Topic5419-7-1.aspx ), but you did well
to post here as well -- generally I respond only here on the E-Prime
Group, and now I can post a few more details and provide code with
proper indentation.

First, as you might find explained in Chapter 4 of the User's Guide
that came with E-Prime, and have instead discovered the hard way,
attributes propogate only downward through logging levels, they do
not persist as you pop back up to higher levels, and as a result they
do not persist as you pop up a level and then back down to the same
level, as happens at the end of a Procedure run by a List. IOW,
despite some appearance, "attributes" are *not* "variables", and they
will not fulfill the role of variables. Variables and attributes
each have their own roles.

So, as explained on the Forum, in short you need to use a global
variable. As explained in Chapter 4 of the User's Guide, you do that
in the User tab of the Script view. Define a variable such as
TRemaining, e.g.,

Dim TRemaining as Long

Then use that global variable to keep track of the value from trial
to trial, as in ... well, when I tried to write out the code it
didn't seem quite right, and I have to run off and catch a bus
now. Maybe you or someone else can now sort this out based on the
clues given so far.

-- David McFarlane, Professional Faultfinder

David McFarlane

unread,
Jul 1, 2011, 4:11:20 PM7/1/11
to e-p...@googlegroups.com
The original poster figured out a solution using
an array, as posted on the PST Forum
(http://www.pstnet.com/forum/Topic5419-7-1.aspx
). Since I already figured out an alternative
solution without using an array, I am posting it
here just to show that I could do it.

First, I will restate the specifications:

(1) A block of trials will last for either some
specified time (e.g., 10 min) or for 12 trials, whichever comes first.

(2) The stimulus on each trial will last for
either some specified time (2 min), or for the
remainder of the block duration, or until the
subject responds, whichever comes
first. Unpacking this, in effect it means that
all trials except the final one last for the
usual trial duration or until a response
(whichever comes first), and the final trial
lasts for the remainder of the block duration or
until a response (whichever comes first).

I already know some functions for working
directly with the clock times of events, so for
this problem I find it handier to think in terms
of the clock times for when the blocks and trials
will end, instead of the remaining block
duration. So here is my solution (following good
programming practices, such as using constants in place of "magic numbers").

I would first define a global variable on the
User tab of the Script window. This will hold
the clock time of when the block must end. (I
like to use a "g_" prefix to remind me that it's
a global and not a local variable):

Dim g_BlockTargetOffsetTime as Long

Then I would set up a structure with some InLines something like the following:

BlockInitCode
TrialList
TrialProc
TrialInitCode
TrialStimSlide

TrialStimSlide is the stimulus, with Duration set to [TrialDur].

BlockInitCode is an InLine object that simply
stores the target offset time of the block for
use later (it assumes that the first event in
TrialProc is our stimulus, and uses the much
overlooked GetNextTargetOnsetTime method, see
that topic in the E-Basic Help facility):

Const BlockDur as Long = 10 * 60 * 1000
g_BlockTargetOffsetTime = GetNextTargetOnsetTime + BlockDur

And TrialInitCode is another InLine object that
sets the TrialDur attribute according to specifications (1) and (2) above:

Const TrialDur as Long = 2 * 60 * 1000
If ((GetNextTargetOnsetTime + TrialDur) <=
g_BlockTargetOffsetTime) Then ' all but the final trial
c.SetAttrib "TrialDur", TrialDur
Else ' final trial
c.SetAttrib "TrialDur", _
(g_BlockTargetOffsetTime - GetNextTargetOnsetTime)
End If

And if you don't mind using one of the less
common coding conveniences of E-Basic/VBA, then you could shorten that to

Const TrialDur as Long = 2 * 60 * 1000
c.SetAttrib "TrialDur", _
Iif( ((GetNextTargetOnsetTime +
TrialDur) <= g_BlockTargetOffsetTime ), _
TrialDur, (g_BlockTargetOffsetTime - GetNextTargetOnsetTime) )


Pros to this solution:
- It does not use any array, nor require the
attending complications of computing array indexes.

- It does not require any "If
c.GetAttrib("Trial") > 1" test on each trial --
seems wasteful to run such a test on each trial
when we know ahead of time that it will succeed
on all trials except the first. Usually we can find a better way.

- It works directly in terms of the target offset
time for the block, which gets computed only
once. It is thus more accurate with time
(without care, accumulated computations of block
time remaining might not take into account delays
in actual stimulus duration, etc.).


Criticisms of this solution:
- Some users would find it more natural to work
in terms of the remaining block duration instead
of the target block offset time (but, without
much trouble, this solution could be adapted to
compute and use remaining block duration instead).

- It uses the GetNextTargetOnsetTime method -- a
very powerful concept in E-Prime, but kept well
hidden in the documentation (I stumbled upon it
myself only while searching the Forum for other things).

- Instead of having all the code compactly
gathered into one InLine (plus a bit of global
User code), it is spread across two InLines (plus
a bit of global User code). This may make it
slightly harder for some to follow (I think
Michiel Spapé has made well-put comments on this
sort of tradeoff either on the Group or in his E-Primer).

- You already have a perfectly good working
solution that makes sense to you, so I'm a little late to the game, aren't I?

-- David McFarlane, Professional Faultfinder

Janet Trammell

unread,
Jul 1, 2011, 5:00:24 PM7/1/11
to E-Prime
I do now have a perfectly good working solution that makes sense, but
the solution you just posted looks a whole lot "cleaner". I think
this will be really useful if I ever need to do this again, and it
looks like it can be adapted to similar situations. Many thanks for
your help!

Janet
Reply all
Reply to author
Forward
0 new messages