Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Calibration Interrupt #359

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

maddycapp27
Copy link
Contributor

I'd attempted this before and it was surprisingly messy so guidance/debate is welcome. @larsoner @drammock @rkmaddox

The goal is to be able to re-calibrate during an experiment when a given key is pressed.

Things to consider:

  1. ec has the button press info but el needs to be called -- so which should the function live in? I'd previously gotten it to "work" with the function living in ec and importing el to ec after both were initialized but it was pretty gross.
  2. Which functions (if any) should automatically check for re-calibration? We'd want to be able to re-calibrate before a trial if the subject isn't able to get fixation (and then probably abort the rest of the trial after calibration) and also between trials (so we could re-calibrate after a break).
  3. Any key press functions in input controllers like _retrieve_keyboard_events or _correct_presses will need to recognize the calibration key.

@maddycapp27
Copy link
Contributor Author

@larsoner It looks like circle build is working

@larsoner
Copy link
Member

Which functions (if any) should automatically check for re-calibration? We'd want to be able to re-calibrate before a trial if the subject isn't able to get fixation (and then probably abort the rest of the trial after calibration) and also between trials (so we could re-calibrate after a break).

I don't think any should, I think you should make a function to do this.

It should probably be a method of EyelinkController, because it already contains a reference to its parent ec, so it can call the proper ec polling function.

Any key press functions in input controllers like _retrieve_keyboard_events or _correct_presses will need to recognize the calibration key.

This sounds too messy, as it requires burdening generic press-event code with paradigm-specific checks. A cleaner design is probably to know that there is some interval during which a recalibration can be requested, and press the button then.

@maddycapp27
Copy link
Contributor Author

@rkmaddox and I were talking about how this might work in an experiment. Perhaps writing ec.call_on_every_wait() function might allow us to check for recalibration automatically without having to include several instances of el.check_calibration() in experiment code.

@larsoner
Copy link
Member

Sounds reasonable to me

@codecov-io
Copy link

codecov-io commented Jul 30, 2018

Codecov Report

Merging #359 into master will decrease coverage by 0.09%.
The diff coverage is 74.28%.

@@            Coverage Diff            @@
##           master     #359     +/-   ##
=========================================
- Coverage   89.07%   88.98%   -0.1%     
=========================================
  Files          44       44             
  Lines        5682     5717     +35     
  Branches      917      926      +9     
=========================================
+ Hits         5061     5087     +26     
- Misses        426      430      +4     
- Partials      195      200      +5

@maddycapp27
Copy link
Contributor Author

Have not tested this with an actual eyelink connected yet and have not added tests.

I've started with what I think the minimum scope of ec.on_every_wait() should be -- it's only included in wait_secs in _utils. To my knowledge this gets called for ec.wait_secs() and at the beginning of functions that have a min_wait parameter. The next thing is whether we want wait functions to be called by ec.screen_prompt(), ec.wait_one_press(), etc. The only other place I can think to include it would be during el.wait_for_fix() but doing so would call for aborting a trial and probably should be done on an experiment by experiment basis.

@rkmaddox
Copy link
Contributor

My feeling is that putting the functions into the base waiting function (which I think is wait_secs ?) is the way to do it. That way every function that waits at all, including the ones you listed, calls the on_every_wait functions at some interval.

@rkmaddox
Copy link
Contributor

Ahh, I see what you're saying. Functions like wait_one_press don't use wait_secs, so they'll also require modification. That's a tough one. I think those should also do the on_every_wait functions? Or maybe have a flag that defaults to True? Is there ever a reason we wouldn't want them to run?

@maddycapp27
Copy link
Contributor Author

maddycapp27 commented Jul 30, 2018

Yes, wait_secs is called during those functions in _init_wait_click or _init_wait_press for the duration of min_wait but then the remainder of the waiting is controlled by a while loop that depends on whether there has been a click/a live key pressed and the max_wait.

Upshot, it should check at the beginning of those functions (but actually doesn't in my test script).

Update: calibration checking doesn't work because wait_secs isn't given an instance of ec as an argument in _init_wait_press or _init_wait_click but this can be changed?

@maddycapp27
Copy link
Contributor Author

Another question is whether the recalibration should show the calibration prompt or not. Since the default behavior of el.calibrate() is to show the prompt, that is what I have for the moment.

@larsoner @drammock Any concerns so far?

@maddycapp27
Copy link
Contributor Author

Tested with real eyelink and seems to behave


Notes
-----
See `flip_and_play` for order of operations. Can be called multiple
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not look correct / relevant


Notes
-----
The data will be saved to the ExperimentController ``output_dir``.
If this was `None`, data will be saved to the current working dir.
"""
@verbose_dec
def __init__(self, ec, link='default', fs=1000, verbose=None):
def __init__(self, ec, link='default', fs=1000, verbose=None,
calibration_key=['c']):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use mutable default arguments. Tuple is immutable so ('c',) is better here

@@ -46,6 +47,9 @@ def test_eyelink_methods():
# run much of the calibration code, but don't *actually* do it
el._fake_calibration = True
el.calibrate(beep=False, prompt=False)
fake_button_press(ec, 'c')
el.check_recalibrate(prompt=False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you assert that this is True ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test gets hung up on the prompt screen. Could put in a fake_button_press if you want to test the prompt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand. It currently gets stuck, or will get stuck with my suggested addition of an assert in this line preceding the el...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read too quickly, you are correct

Copy link
Member

@drammock drammock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to understand what this does just by reading the docstrings and the code, so I have several docstring suggestions. I haven't tested the code itself.

Parameters
----------
function : function | None
The function to call. If ``None``, all the "on every flip"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this say all the "on every wait"?

@@ -137,14 +137,17 @@ class EyelinkController(object):
Sample rate to use. Must be one of [250, 500, 1000, 2000].
verbose : bool, str, int, or None
If not None, override default verbose level (see expyfun.verbose).
calbration_keys : list
Keys that will trigger recalibration when check_recalibration.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you expand this docstring line a bit? The reference to check_calibration needs context (i.e., it's not a parameter of this constructor, so explain what it is / where it lives)... is it referring to the new check_recalibrate method? (if so, the method name is wrong here)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, you say it should be list but you initiate it with a tuple. Will it work with any iterable? if so, say iterable instead of list

def check_recalibrate(self, keys=None, prompt=True):
"""Compare key buffer to recalibration keys and calibrate if matched.

This function always uses the keyboard, so is part of abstraction.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand ths note.

Parameters
----------
keys : list or string
keys to check if prompt recalibration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keys to check against the (set of) key(s) that trigger calibration.

keys : list or string
keys to check if prompt recalibration
prompt : bool
Whether to show the calibration prompt or not
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether to show the calibration prompt screen before starting the calibration procedure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants