How to use coroutines with inline scripts ?
Hi all,
I am trying to use coroutines with an inline script. I read the documentation (https://osdoc.cogsci.nl/3.2/manual/structure/coroutines/) but I really don't get how to do it.
What I need to do is the following :
I am running my experiment in MRI, and I need to get the triggers sent by the MRI to write them in a file, and change what is shown to the participant accordingly.
While I wait for these triggers, my participant is able to answer using a response box. While trying the experiment in real conditions, it happened that the participant responded close to a trigger, so some triggers were missed.
For these reasons, I really need to have a script running in the background, waiting for keys. What I tried so far is the following :
I have a function my_coroutine() that I define at the beginning of my experiment, in the prepare tab. I want to get keys in this function and set global variables accordingly.
key_pressed = None trigger_pressed = None key_pressed_time = 0 def my_coroutine(): global key_pressed global trigger_pressed global key_pressed_time yield while True: key, end_time = phase_1_keyboard.get_key(timeout=None) # If I got a trigger if key in trigger_keys: trigger_pressed = key # If I got a response else: key_pressed = key key_pressed_time = end_time
This generator is then called in a coroutines object in which I put the other script I want to run along with my coroutine.
In my inline script Display_target_1 that is triggered at each trial, I show a canvas, check if a trigger or a response key was pressed and do stuff accordingly.
canvas_trial.show() while count_trigger < 3: if trigger_pressed: trigger_pressed = None list_to_write = [str(x) for x in (1, block+1, self.get("run_task1")+1, trial+1, count_trigger+1, end_time, exp.get("Condition"))] trigger_files[0].write("\t".join(list_to_write) + "\n") count_trigger += 1 if key_pressed: key = key_pressed key_pressed = None if not response_given: var.Response_task1 = key var.Response_time_task1 = key_pressed_time - var.Begin_canvas_show canvas_trial['lure'].visible = False canvas_trial['target'].visible = False response_given = True
But when I launch my experiment, it seems I can't get past the initialize yield of my coroutine. Do you know what I am missing ?
Thanks for your help.
Jessica
Comments
Hi @Jessica Bourgin ,
The reason that
my_coroutine()
blocks is that there's noyield
statement inside thewhile
loop, which is necessary for the function to suspend.However, this specific issue aside, I'm not sure that you're going about this in the right way. What exactly do you want to do? How do the key press (which I imagine is the trigger from the fMRI machine and not the participant's response?), the button box, and the canvas relate to each other?
— Sebastiaan
Check out SigmundAI.eu for our OpenSesame AI assistant!
Hi @sebastiaan,
Thanks a lot for your quick answer. I forgot I tried this with a yield in the while loop of the coroutine as well, but the result was the same. I assumed I had something to do in my Display_target_1 as well but I could not figure out what.
In particular, I tried to print "start" before the initialization yield, and "yield" after, and only "start" was printed. Therefore I assumed I had something to do in my Display_target_1 while loop.
This aside, it's very probable that I am looking in the wrong direction. I assumed that using threads was what I needed but I couldn't manage to make it work. The problem I have to solve is the following:
So the first issue I have is that a response and a trigger can occur at the "same" time, and so one of them can be missed.
Finally, when I receive a trigger or a response, I need to react to them adequately and quickly:
For triggers:
For responses :
So I thought that a way to do that could be to have a loop running in the background, listening to keyboard inputs and storing them in a cache.
Then my trial loop could read that cache progressively and perform the appropriate actions (end trial, store response).
So i am trying to find a way to do that, but maybe there is another option I did not consider?
Thanks for your help.
Best regards,
Jessica
Hi Jessica,
So I thought that a way to do that could be to have a loop running in the background, listening to keyboard inputs and storing them in a cache.
That is indeed a bit complicated, because you essentially want to run your entire experiment as a co-routine. However, if the schedule is contingent on the fmri triggers, you can probably get away with separate co-routines per phase.
So the first issue I have is that a response and a trigger can occur at the "same" time, and so one of them can be missed.
For sakes of polling both fmri triggers and responses in parallel and treat them differently I suggest a while loop, as demonstrated in the attached example. Essentially, you are stuck in a loop for a certain amount of time, or until something predefined happens (e.g. a specific button press). Due to the loop, you can then dynamically adjust the presentation or other experimental factors.
I would like to write them in a file for further data analysis
I would not do that yet. Better store them in a list or so, and write them to file later. e.g. after a run. You can pickle it, or use pandas to write it to a csv file
I count them to know at which state of the trial I am
That is also quite easy. Essentially add a variable every time you detect a t response. Also here, you need to initialize the variable before you enter the main loop otherwise it will be reset every time a new loop starts. For other phases of the experiment you might need to do something similar in order to deal with the parallel polling of triggers and doing other experimental things.
Anyway, I hope this helps you already a bit.
good luck,
Eduard
Hi @eduard,
Thanks a lot for your response.
The problem I have is that when I receive a response really close to a trigger, then a trigger (or a response) can be missed. It is not very probable (it seems that they need to be close by less than 10 ms) but it indeed happened during a pilot experiment. I precise that I tried it while removing the file writing process, so to lighten the instructions weight.
So what I need is a way to detect if two keys are pressed exactly at the same time or at a very close time. Please tell me if this is not possible, I will then look for another way around (i.e., not listening to triggers during trials).
I don't know if coroutines can help me with that, but in any case I don't know how to make them work with an inline_script in a coroutines item. I attach a basic example:
I made a function my_coroutine, which I then use as a generator in the coroutines item. And when I run the experiment, I don't go past the initialisation yield of the coroutine, and can't figure out why.
Thanks for your help.
Best,
Jessica
Hi Jessica,
yeah, I see what you mean. Personally, I believe the chances for two events happening at the same time is so low, that I would not be too worried that this would happen (consistently) during the experiment. Anyway, I understand that it would be really really annoying if it happened, so it makes sense to try to minimize the chances of it happening. With respect to the co-routines, I can't help you much, as I have not much experience with them. That being said, I don't know whether they can be a key to your solution or not. If you insist on trying it with them, try to tag Sebastiaan (with an @ handle), he will be able to give you better directions with co-routines than I can.
A couple more thoughts about no co-routine-solutions:
I hope this helps a bit.
Best wishes,
Eduard