[open] Reading and sending triggers from loop item list
This is a follow up question to this earlier post on sending triggers to an eeg recording device.
I try to send out triggers that are read out from a loop item. I added two columns in the loop variable list (trig1 and trig2) and filled them with numbers to be used as triggers.
I used these variables within the python inline script that sends out the triggers. Here as an example trig1:
global io
trigger = '[trig1]'
port = 0x378
try:
io.DlPortWritePortUchar(port, trigger)
except:
print 'Failed to send trigger!'
But this produced weird results.
The triggers from the list were:
trig1 trig2
122 7
223 3
134 6
144 3
133 6
244 8
221 3
...and this was the outcome in the eeg recodring software:
84 148
180 244
20 180
116 244
116 244
116 20
84 212
What am I doing wrong here? How can I get the right triggers sent out?
Furthermore, is it possible to send out a certain (static) trigger for a correct response and another one for an incorrect reponse?
Something like:
global io
if response == '[correct_response]':
trigger = 99
else:
trigger = 88
port = 0x378
try:
io.DlPortWritePortUchar(port, trigger)
except:
print 'Failed to send trigger!'
Thanks in advance for your help,
Best wishes,
Johannes
Comments
Hi Johannes!
If you are using the following notation within an inline_script...
...I suggest you try the following:
The '[varname]' notation can be used within OpenSesame's items, but not in an inline_script, where Python-code is expected (for more information, see here).
On your second question: this is possible and your code is almost spot on. OpenSesame's keyboard_response and mouse_response produce a variable 'correct' that is either 1 on a correct response or 0 on an incorrect response. If you use one of those two (or the joystick plugin), you can use the following:
If you use something else, but keep track of the variables 'response' and 'correct_response', you can use this:
Please let me know if it works!
Edwin
Hello Edwin,
Thanks for your quick reply. The code you posted worked perfectly. As regards the second question (dynamic answer triggers) only the second bit of code worked for me (although I currently use keyboard response for testing).
However, I have some new problems with a) the triggers actually sent and b) the timing.
a) In my test experiment (which I rebuilt alike in Presentation) I send the following triggers (in the following order):
When I look at the trigger file of the EEG recording software, I see the following for the experiment run with Presentation (the last column are the triggers):
OpenSesame, however, creates this:
I wonder what all these other triggers are. Are they presented automatically by OpenSesame? Is there a way to get rid of them? If not this would be bad, as sometimes these "unexpected" triggers are just the same as the ones I use to trigger my events, so there would be no easy way to filter them out post hoc via a script.
b) I have a totally different timing (of triggers) in OpenSesame and Presentation, although I think I use the same timing instructions in both (but here I may be wrong as I don't have that much experience with OpenSesame).
So here is what I want:
In OpenSesame I set up this (using Psychopy back-end, I set the Inline scripts after visual events as Lotje suggested but before the sound (as I want to know where the sound started)):
Now I used the EEG-trigger files of Presentation and OpenSesame to compare the intended timing with the one that actually is in the EEG recording (I calculated the differences between the trigger times in the trigger files of the EEG recording software):
For Presentation (not that good either):
For OpenSesame:
So while the standard deviations are much better than in Presentation the timing is weird (but stable).
What's going on here? Could you help me out?
Cheers,
Johannes
Add:
As I'm looking at my own post. The OpenSesame timing makes sense somehow. The trigger is sent out after the visual events, so for the timing fix. cross-visual stim. that is 650ms and for visual stim.-auditory stim. that is 500ms. So the triggers should be sent before the visual stimuli?
Hi Johannes,
We will respond to your questions shortly.
Best,
Lotje
ITA
Did you like my answer? Feel free to
Hi Johannes,
With regard to your first question, OpenSesame does not generate triggers automatically. This should therefore be caused by something in one of your inline scripts. It's difficult for me to tell where in your code the additional triggers are sent exactly. So, if you would like to, you could upload your experiment (for example on pastebin.com) or send it by mail.
With regard to your second question, what you mentioned in your edit seems to be exactly what is going on. Instead of sending a trigger just after fixation and stimulus presentation, respectively,
the trigger is sent after fixation presentation + its duration (i.e. after 500 ms), and after stimulus presentation + its duration (i.e. after 650 ms).
This is probably because you set the duration of your fixation sketchpad and stimulus sketchpad to 500 and 650 respectively, right? So OpenSesame first presents the sketchpad for the given duration and only then sends the trigger.
Sending the triggers before the visual stimuli is not a good idea, as mentioned previously.
However, you can easily make the time stamps correspond to the stimulus onsets (instead of their offsets) in the following way:
1) Set the durations of the to-be-time-stamped sketchpads to 0 ms.
2) Send the trigger right after those
3) And then, to keep your sketchpads presented for the intended period of time, there are two possible solutions:
I think your analysis of the mean and SDs of the differences between the different triggers is an excellent idea. So perhaps you could do this again after applying the above suggestions and see whether you get the intended results).
Finally, I want to point out (perhaps obviously) that the SDs you report for Presentation are way too high for carrying out trigger-locked analyses; the SD of the duration should not be much more than 1 ms.
Good luck!
Lotje
Did you like my answer? Feel free to
Hi Lotje,
Thanks for your reply and your instructions, I have a clearer understanding on how and when to send the triggers with OpenSesame now. I'll try that out soon and post the results.
You are right concerning the Presentation SDs. I took a second look on it and have found out, that there were some rather extreme outliers within the trigger data, which have distorted the means and sds. So when I exclude these outliers I get the following results for Presentation:
Anyway, the stimulus events themselves and the trigger are in sync. So the SDs are not for differences between stimulus and trigger but between two pairs of stimulus and trigger. So these SDs would of course not be good for e.g. reaction time studies but for designs where you just lay onsets over each other and average afterwards this will not be a concern as long as the triggers are in sync with their stimulus or am I wrong?
As regards the strange additional triggers in OpenSesame I will send you an email with the code I ran. I really need to get them out.
Thanks for your help and patience,
Cheers,
Johannes
Hi Johannes,
Thank you for sending us your experiment. Your code looks very good and it took
us a while to find out what could be causing the unintended triggers.
But I think we found it. The function DlPortWritePortUchar(), which is not a native Python module, expects the parameter
'trigger' to be an unsigned byte (called a 'ubyte' or 'uchar'). This is a specific data type that is used by low-level programming languages such as C.
And possibly this is where it goes wrong, for some very technical reasons.
Our suggestion is therefore to try whether your problem is solved when you force
the parameter 'trigger'
to be a ubyte by adding two lines in all your inline scripts for sending the triggers
in the following way:
We cannot test this direclty because we don't have the necessary equipment right now. So we would be
very glad to hear whether this solution worked for you, such that we can edit the
documentation if necessary.
If you still encounter the same problem after trying the above, please let us know and we'll
have a further look at how it could be solved.
Have a good weekend!
Lotje
Did you like my answer? Feel free to
Hi Lotje,
Thanks for your reply and suggestions.
I tried that out, but unfortunately that makes no difference, I still have these additional triggers. What I may have to add, these triggers do not appear in the window with the live recording (only the intended triggers appear) but as soon as I look inside the recordings later on, these additional triggers appear. Do you have any suggestions on what else I could try out?
Just some additional questions about triggers and sound stimuli:
You mentioned, that it is not a good idea (because of the monitor refresh time jitter) to send a trigger out before a stimulus event. Is this true as well for auditory stimuli?
In my test experiment I need to trigger the onset of the sound. Therefore I send out a trigger right before the sound is played.
Or would it be a better solution to send it after the stimulus as well? The problem then is, all the sounds presented have a slightly different length, so I cannot just use the self.sleep(x) solution as for the visual stimuli without the risk that some sounds are not fully played, or am I wrong here?
It is most important for me that the sound onset and the corresponding trigger are in sync. Do you know a way to test this?
I'm sorry to bother you with so many questions but I really need to understand these things to make use of OpenSesame.
Cheers,
Johannes
Hi Johannes,
It's no problem at all to ask a lot of questions! On the contrary; that's what the forum is for!
Our discussions could be informative for others and at the same time your information is very helpful for the documentation on OpenSesame (such as your explanation on how to install DLPortIO on Windows 7).
It's too bad our suggestion did not solve your problem. As a further test I recommend to
execute a very brief experiment which sends triggers with the values 0 to 255 in two
different runs: Once in the old way, and once by first converting the trigger values to ubytes.
I uploaded a mini experiment with which you can easily do this (it only takes a few seconds to run):
[gist:3503485]
(Right click on the button 'raw' and 'save as'.)
If you could send us the recordings afterwards we can find out whether converting the values
to ubytes made any difference at all.
With regard to the auditory stimuli I think your solution is very good: First send the trigger and
then play the sound (and set the duration in the sampler item to [sound]).
In contrast to visual output, sound output is continuous, and therefore does not suffer from a refresh-rate-like issue.
This is why in general it doesn't matter whether such stimuli are presented before or after the trigger.
Yet, like you said, the duration of your sounds is variable and therefore the order of your items is
optimal as it is now!
On the downside, the timing for auditory playback is much less accurate than for visual output. The only way to test to what extent your triggers and sound onsets are in sync is by doing some kind of benchmark experiment, like the one reported here but you'll need special equipment for this. (Without doing external measurements you will not be able to determine the real delay.)
As far as the software is concerned, the results of the above benchmark experiment will also hold for OpenSesame because the same libraries are used to handle audio playback.
Unfortunately, the delay will largely depend on the set up of your hardware, and, most notably, the sound card. So its difficult to say what the delay will be in your case.
The benchmark experiment was done by using a reasonably good external sound card (Soundblaster Audigy), so the timing will probably not be as good when you just use a built-in sound card.
Finally, I just want to emphasise two things. Firstly, both the refresh-rate jitter and the delay between code and actual sound are not specific for OpenSesame. Experiment builders can try to minise those issues
as far as the software is concerned (e.g. by allowing for different back-ends to use), but they cannot solve the limitations of the hardware.
And secondly, the above-mentioned issues in time-stamping sound stimuli are just meant as 'things to keep in mind'. Of course it's very well possible to carry out
EEG experiments with sound stimuli; the timing is just less accurate than with visual stimuli.
Best wishes,
Lotje
Did you like my answer? Feel free to
Hi Johannes,
Thanks for your email and explanation!
First off, the failure of test script cannot be related to the fact that only trigger changes are recognised because this is exactly what the test script does: looping through a range of triggers (from 0 to 255) such that every trigger differs from the previous one (see also the comments in the script).
As you have probably noticed, the trigger-sending piece of code uses 'try..except' statements.
In general this is convenient because it enables you to run your experiment on computers that don't have DLPortIO installed too. Yet, in the current situation handing such exceptions has a disadvantage too: Any errors in this particular part of the script will not cause a crash and therefore no error messages (which are always informative).
Therefore we adapted the previous test script by removing the exceptions. We also added a self.sleep() statement after the trigger signal, because after looking at the literature you attached we realised that the triggers were possibly sent too rapidly, that is, faster than the sampling rate. (Note that the latter only holds for the test script, not for your script.)
So perhaps you could try to run the test script once more, and send us either the output (if the script turns out to work), or the error message(s) you get in the debug window (if the script causes a crash).
You could do this by creating a new experiment with a single inline script and just insert the following code in the run phase of the inline script:
[gist:3597692]
Hopefully this will help you further!
Lotje
Did you like my answer? Feel free to
Hi Lotje,
Great that script worked. And it ran without any errors.
However, I have additional triggers in both unsigned bytes and normal.
I'll send you an email with the whole trigger file, so you can take a look.
Well that's strange, isn't it. Do you have an idea what to do now?
Best wishes,
Johannes
Hi Johannes,
Lotje told me about your mysterious extra triggers, and sent me the results of the test script. I'm pretty sure we figured out the core of the issue, but it's one of the strangest problems I have ever encountered.
Let's first take a look at a snippet of your trigger file:
The correct order is 1-2, so the 3 shouldn't be there. Now let us consider the binary representation of 1, 2, and 3:
The point to note here is that 3 contains all the bits that are enabled in 1 and/or 2. More formally, you could say that 3 = 1 OR 2. Try it in a Python terminal:
This is not a coincidence. As far as I can tell, all extraneous triggers can be explained this way. For example the mysterious 127, in between 111 and 112:
Let's look at the binary representation:
Again, 127 = 111 OR 112.
So something like the following might be going on: When a trigger changes, first all the bits that should be switched on are switched on, and only a tiny bit later the bits that should be switched off are switched off. The result is that just before a new trigger is registered, there is very brief 'ghost' trigger that is the bitwise OR of the old and the new trigger. Does this make sense?
The question is whether this issue is related to OpenSesame. To a certain extent it apparently is, because you report that the same thing doesn't happen with Presentation. But I strongly suspect that DLPortIO (the DLL used for sending triggers) does not, in fact, send those ghost triggers, but that they are a quirk of the EEG apparatus. Why the problem sometimes occurs, and sometimes not, I couldn't say.
So what can you do?
Cheers,
Sebastiaan
Check out SigmundAI.eu for our OpenSesame AI assistant!
Hi Sebastian,
Great you found out the reason of these ghost triggers. It seems to me that this even explains when the triggers do not appear:
So switching from 217 to 218 would involve switching the final 1 to 0. This does not happen (at least fast enough) with the result 219. Switching from 216 to 217 does not involve any switching down to 0. That is why there appears no ghost between them.
So the problem is that activated bits are not deactivated (fast enough). I checked this with two different computers (with totally different hardware, one is a Win7 laptop with a parallel express card the other a winxp sp3 desktop with a pci parallel card) running OpenSesame and in both setups the ghost triggers occur, so it does not seem to be a hardware problem related to the parallel card.
Ok, filtering the ghost triggers out post hoc would be an option but I also contacted the manufacturers of the amplifier and recording software and sent them a test recording.
Well, in reply to this they asked me some questions I cannot answer right now as I just do not know, so I repeat them here:
What is the trigger length sent out by OpenSesame (can this be determined?)?
With a sampling rate of the recording set to 500Hz the trigger must be at least 2ms or 5ms (the official support and the documentation of the software differ here) long.
Are the triggers high activ or low active? (I think this means is an "on" bit set by 5V or 0V?)
Are the "pulses"/triggers then completely closed after sending, as it seems that the ports just remain open after trigger activation?
Is there a way to determine this or can this be manipulated from within OpenSesame?
Thanks a lot for your help.
Cheers,
Johannes
Indeed! The bitwise OR of 216 and 217 is 217, so no additional trigger appears:
Each trigger is sent for 100ms, so that should be plenty. (To my understanding, the trigger event itself does not have a length. The length of a trigger is simply the interval between to subsequent triggers.)
I don't know, to be honest. This is not explicitly set, and I do not see any mention of this in DLPortIO. My guess is therefore that this is a property of the operating system, in which case DLPortIO just uses the system default (whatever that may be).
I'm not sure what they mean here? As far as I understand it, the parallel port always carries some value. Sending a trigger simply changes that value, and that's pretty much all there is to it. So (as far as I know) the parallel port cannot be closed, like you can close, say, an internet connection. That being said, my understanding is incomplete and I might be wrong.
But with the necessary equipment, the vendor guys can easily check all of this themselves, of course. As you know, OpenSesame does not have any custom code to handle the parallel port. It just uses DLPortIO.dll, which is in turn a pretty simple (and widely used) library.
Good luck!
Check out SigmundAI.eu for our OpenSesame AI assistant!
Hi Lotje, hi Sebastian!
I'm happy to report that I found a solution!
The trick is to send a second trigger that just sends a 0 before the next trigger is sent out. Further, it is crucial that behind both triggers there has to be a self.sleep() phase of at least 2ms (no upper border) if you record with a sample rate of 500 Hz or at least 1ms if you record with 1000 Hz.
A trigger value of 0 is not interpreted at all by BrainVisionRecorder (so simply nothing appears) but it resets all the activated bits to zero/0V/low. With this no ghost triggers appear any more!
Here is the code I ran:
[gist:3716574]
I have also tried out inpout.dll (another free parallel driver) and this works as well. This driver is also available as inpout64.dll for Windows 7, but I had no time to try that out right now (I currently test on an XP machine). I'll check the timing an report if there are differences to dlportio.dll. The code is nearly identical, you just replace:
with
and the triggers are sent with
instead of
However, the same issue appears with inpout32.dll. One has to send a zero trigger here as well.
Additional questions arise now:
If I have to send an additional zero trigger after each 'normal' trigger, I can't control the timing of the preceding visual stimulus (sketchpad) just by e.g. self.sleep(500), or can I do this?
The same thing vice versa with sound stimuli: I have an additional 2ms delay if I send the stimulus trigger right before the sound, or am I wrong here?
How could I solve this?
Best wishes,
Johannes
Right, that's a clever solution! The binary or of 0 and X is always X, so there are no ghost triggers when switching to and from 0. If the EEG apparatus disregards 0-value triggers, you can thus use a 0-value trigger as a reset.
Thanks, that's useful to know.
You could send the extra trigger halfway the delay period, like so:
This should work, I believe. Of course you can use different delays, as long as they add up to 500. (As an aside, it is a good idea to choose a delay that is just a few milliseconds short of the delay that you actually want to have. The reason is that the display presentation will block until the next display refresh, which essentially 'rounds up' the delay interval. Does that make sense?)
Right, so you could send the 0-value trigger after the sound. If I understand correctly it doesn't matter if the 0-value trigger is sent right after the regular trigger, or only some time later. As long as there is a 0-value trigger somewhere in between each pair of regular triggers you should be good.
Thanks for being so patient and persistent! I'm sure this discussion will be helpful to a lot of people!
Cheers!
Check out SigmundAI.eu for our OpenSesame AI assistant!
please could any one help me in linking between Emotive EEG and opensesame via serial port , i supposed to make VEP