Timing between laser and stimulus onset

Hi Chris,

We are about to begin our next round of recording so I am optimizing some of the protocols. I would like to try to have the laser on 50ms before stimulus onset but it is tricky because there is about a 16ms to 17ms delay between update_display() and the actual stimulus presentation on the screen. If I turn the laser on first and have it wait for 50ms before update_display(), the actual difference between laser and stimulus onset would be about 50ms + 16ms = 67ms. Another issue is that each trial may take slightly longer or shorter than 16ms to present stimulus so I would like to avoid hard-coding 16ms into the protocol.

We’ve talked about this before and I tried to follow The timing between turning laser on and stimulus on - #17 by cstawarz. However, I think the suggestion in this post only works when the laser_padding_time is shorter than 16ms? Do you have any suggestions of how to code if I want a laser_padding_time = ~50ms?

Best,
Taylor

Hi Taylor,

Yeah, this is tricky. You’re correct that my previous suggestion will only work if the padding time is less than the time between the update_display call and the predicted output time. I think it may be possible to do what you want using render actions. Let me explore that some more and get back to you.

Cheers,
Chris

Thank you, Chris!

Best,
Taylor

Hi Taylor,

I’ve attached an example that demonstrates a technique that should work for you. It uses render actions to delay the stimulus onset an appropriate amount of time for the desired laser padding. This should work for any laser padding time.

If you have any questions, please let me know.

Cheers,
Chris
laser_padding.mwel (3.1 KB)

Hi Chris,

Thank you! I will try it and get back to you.

Best,
Taylor

Hi Chris,

I spent a few days trying this technique and here are a few questions I have:

(1) When I tried to implement this technique in our protocol, the laser was on only about 3 ms before the stimulus onset, and sometimes even after the stimulus onset. Not sure if this is helpful information but it takes the laser about 2ms to actually turns on from MWork’s laser on command. I also only live_queue a few stimuli out of all stimuli because some of them were just for the experimenters to see on the eye window and were not presented on the animal’s screen.
(2) I played around and tried to move the laser on command before cuing any stimulus including the delay_stim_onset. This time, the laser was on about 35ms before the stimulus onset but it wasn’t really making sense to me.
(3) Because we have different types of stimulus in our actual protocol, I only need this delay for some stimuli but not all. So I tried to put this render actions in an if_else condition but the console gave me an error saying that, for example, "Component ‘action/assert’ is not allowed inside component 'action/if_else’ “ along with other errors. I’ve attached a .txt of the error message from the console below.

I’ve also attached the .mwel of the actual protocol that I implemented the render actions in case I implemented it incorrectly. I put the render action at the beginning of this protocol and made slight change to the delay_stim_onset so that it works for our stimulus. The state where I used the render action is in “Stimulus On - Pre-Saccade”.

Thank you for your help.

Best,
Taylor

(attachments)

error.txt (1.54 KB)
protocol_2afcLaser_50msLaserBeforeAfterStim.mwel (56.2 KB)

Hi Taylor,

Thanks for sharing your code.

Certainly, you can’t turn the laser on before the update_display and wait. The delay_stim_onset stimulus is what tells you how long to wait before turning the laser on. The right place to turn the laser on is immediately after the wait at line 634 in your code.

Also, what does the turn_laser_on macro do? Does it still use Pulse Variable to turn the laser on and off? As I explained in our previous discussion, it’s going to be hard to get the timing right that way. Instead, you should be setting the laser gate signal high and low directly in the protocol, as I demonstrated in my previous example code.

Regarding the errors: The problem is that the if_else component can only contain if and else components. Starting at lines 631 and 655, you’ve put a bunch of actions other than if and else directly inside the if_else, which isn’t allowed. If you move each set of actions in to the if or else that precedes it (which I think was your intention), then things should work.

Cheers,
Chris

Hi Chris,

Thank you for your help.

I’ve removed the pulse variable and directly setting the laser gate signal now. I also fixed the error in if_else. However, the laser still didn’t turn on 50ms before the stimulus onset. It was at most about 30ms. The weird thing is that it only turns on 20 to 30ms before photodiode onset for blank trials where there is no actual stimulus but only a photodiode signal. For regular trials with stimulus, the time difference between laser and photodiode is about 3 to 4ms. I’ve attached my new code below if that’s helpful.

Another weird thing I couldn’t understand when I was analyzing our old data was that, for the current version that we are using (the one with no intended 50ms delay and laser is on 16 to 17ms before stimulus onset which is how long update_display() takes), the time difference between laser onset and photodiode on is about 16 to 17ms for the first 70 to 130 trials or so. Then, the time difference is about 25 to 30ms for the trials afterwards. I don’t think it’s the case but it seems like a warm-up for something. I am not sure what could cause this difference? It is a very small difference and I doubt that the animal would notice but it would be helpful for me to understand for paper writing purposes.

Thank you again for your help.

Best,
Taylor

(attachments)

protocol_2afcLaser_50msLaserBeforeAfterStim.mwel (56.4 KB)

Hi Taylor,

However, the laser still didn’t turn on 50ms before the stimulus onset. It was at most about 30ms. The weird thing is that it only turns on 20 to 30ms before photodiode onset for blank trials where there is no actual stimulus but only a photodiode signal. For regular trials with stimulus, the time difference between laser and photodiode is about 3 to 4ms.

Sorry, I didn’t take the photodiode stimulus into account previously. Since you’re using it to detect stimulus onset (as you should), you need to make sure it appears at the same time as the actual stimulus. This means that you’ll need to control its alpha in delay_stim_onset, too. If you’re not doing that, that’s probably the issue.

Another weird thing I couldn’t understand when I was analyzing our old data was that, for the current version that we are using (the one with no intended 50ms delay and laser is on 16 to 17ms before stimulus onset which is how long update_display() takes), the time difference between laser onset and photodiode on is about 16 to 17ms for the first 70 to 130 trials or so. Then, the time difference is about 25 to 30ms for the trials afterwards. I don’t think it’s the case but it seems like a warm-up for something. I am not sure what could cause this difference?

There are no guarantees about the amount of time that elapses between update_display completing and the display actually updating. The display update thread can be rendering anywhere from 1 to 3 frames in advance, so you could conceivably see a time difference of up to 50ms. Also, you don’t know where in the display update period update_display will complete. If it completes in the middle of the update period, then the time difference could be 1.5 times the refresh period (25ms, which is in the range you observed).

This is why, to get consistent timing between laser onset and stimulus onset, you need to schedule the laser relative to the predicted display update time, as you’re doing now.

Cheers,
Chris

Hi Chris,

I tried controlling the alpha of the photodiode and it worked! Thanks a lot! But there’s still a small issue.

The time difference between the laser onset and the photodiode on started being about 50 to 55 ms or so, but after about 20 trials, the difference became 60ms. Is this also expected? Even though we did try to control the laser based on the predicted display update time?

Best,
Taylor

Hi Taylor,

I tried controlling the alpha of the photodiode and it worked!

Great!

The time difference between the laser onset and the photodiode on started being about 50 to 55 ms or so, but after about 20 trials, the difference became 60ms. Is this also expected?

I don’t know what to think of that. I guess it’s possible that the operating system’s prediction of the display update time is becoming less accurate, but I have no idea why it would. Does this happen consistently, over multiple runs of the experiment?

Chris

Hi Chris,

I ran the protocol a few times today and noticed two things.

One is that the latency between laser and photodiode onset stabilized at around 60 to 62 ms. However, this wasn’t the case for all trials. Most of the trials had a latency of about 60ms but the others had a latency of about 13ms. Another thing that I noticed was that the laser stopped responding and was stuck after I ran the protocol for a few times. I think this cold be caused by the first issue which created a miscommunication between MWork and the laser for the later runs. Do you have any idea how to fix this?

Best,
Taylor

Hi Taylor,

One is that the latency between laser and photodiode onset stabilized at around 60 to 62 ms.

Interesting. It would be nice to know if, as I suggested previously, this is due to a discrepancy between the predicted display update time and the photodiode onset time.

Would it be possible for you to do some runs with target_laser_padding_time set to zero? In that case, you would be sending your e_stimulus_on signal to the Blackrock very close to the same time that the OS thinks the display will be updating. If you then compare the e_stimulus_on time to the photodiode onset time, we could see if all or part of the unexpected, additional 10ms latency is due to prediction error.

Also, is your photodiode in the upper-left corner of the display? If not, then that could be a source of additional latency, since most displays refresh themselves left to right, top to bottom.

However, this wasn’t the case for all trials. Most of the trials had a latency of about 60ms but the others had a latency of about 13ms. Another thing that I noticed was that the laser stopped responding and was stuck after I ran the protocol for a few times. I think this could be caused by the first issue which created a miscommunication between MWork and the laser for the later runs.

Yeah, I think you’re right that these are related. Let me look through your code and see if I can find an error.

Cheers,
Chris

Hi Taylor,

I don’t see any logic errors in your experiment.

Did MWorks report any skipped refreshes during your tests? Skipped refreshes would cause delay_stim_onset to wait longer than intended, as it determines how many refreshes have elapsed just by counting how many times it’s been invoked. We could (and probably should) make it more robust by comparing the result of next_frame_time() to the value of extrapolated_predicted_display_update_time on each iteration.

Another thought: It would be useful to compare extrapolated_predicted_display_update_time to actual_predicted_display_update_time at the end of each trial that uses delay_stim_onset, in order to see if the accurary of the extrapolated time is worse than we expect.

Chris

Hi Chris,

I’ve attached a table of the three timings. There does seem to be a big difference between extrapolated_predicted_display_update_time and actual_predicted_display_update_time, but not so much between next_frame_time() and extrapolated_predicted_display_update_time? Please also let me know if you need more samples. I can record for longer. Thank you!

(Attachment timing.numbers is missing)

Hi Taylor,

I’m away on vacation this week, but I’ll take a look at this when I return next week.

Thanks,
Chris

Hi Chris,

No rush at all. Enjoy your vacation!

Best,
Taylor

Hi Taylor,

I’ve attached a table of the three timings.

I didn’t actually receive the table, just a message saying “Attachment timing.numbers is missing”. Can you try sharing it again – maybe directly on the MWorks support web site, in case it’s being rejected by an email server somewhere?

Thanks,
Chris