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

Meg desktop : solved a bug with the sending of STOP triggers #11

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/sessions/ses-mariomeg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import sys, os
import numpy as np
import random
import hashlib
import pandas


worlds = 8
levels = 3
exclude_list = [(2,2),(7,2)]
all_levels = [(world, level)
for world in range(1,worlds+1)
for level in range(1,levels+1)
if (world,level) not in exclude_list]
n_repetitions = 50 # very high number, will never reach that point


def get_tasks(parsed):

from ..tasks import videogame, task_base
from .game_questionnaires import flow_ratings, other_ratings
import json
import retro
# point to a copy of the whole gym-retro with custom states and scenarii
retro.data.Integrations.add_custom_path(
os.path.join(os.getcwd(), "data", "videogames", "mario")
)

bids_sub = "sub-%s" % parsed.subject

design_path = os.path.join(
'data',
'videogames',
'mario',
"designs",
f"{bids_sub}_design.tsv",
)
design = pandas.read_csv(design_path, sep='\t')
scenario = "scenario"

savestate_path = os.path.abspath(os.path.join(parsed.output, "sourcedata", bids_sub, f"{bids_sub}_phase-stable_task-mario_savestate.json"))

# check for a "savestate"
if os.path.exists(savestate_path):
with open(savestate_path) as f:
savestate = json.load(f)
else:
savestate = {"index": 0}

for run in range(10):

next_levels = [f"Level{world}-{level}" for idx,(world,level) in design[savestate['index']:savestate['index']+20].iterrows()]
if len(next_levels) == 0:
print('Stable phase completed, no more levels to play')
return []

task = videogame.VideoGameMultiLevel(
game_name='SuperMarioBros-Nes',
state_names=next_levels,
scenarii=[scenario]*len(next_levels),
repeat_scenario=True,
max_duration=10 * 60, # if when level completed or dead we exceed that time in secs, stop the task
name=f"task-mario_run-{run+1:02d}",
instruction="playing Super Mario Bros {state_name} \n\n Let's-a go!",
post_run_ratings = [(k, q, 7) for k, q in enumerate(other_ratings+flow_ratings)],
use_eyetracking=True,
scaling=.5,
)

yield task

#only increment if the task was not interrupted, if interrupted, it needs to be rescan
if task._task_completed:
savestate['index'] += task._nlevels
with open(savestate_path, 'w') as f:
json.dump(savestate, f)

yield task_base.Pause(
text="You can take a short break.\n Press A when ready to continue",
wait_key='a',
)
9 changes: 6 additions & 3 deletions src/shared/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def listen_shortcuts():
return False


def run_task_loop(loop, eyetracker=None, gaze_drawer=None, record_movie=False):
def run_task_loop(task, loop, eyetracker=None, gaze_drawer=None, record_movie=False):
for frameN, _ in enumerate(loop):
if gaze_drawer:
gaze = eyetracker.get_gaze()
Expand All @@ -49,6 +49,7 @@ def run_task(

# show instruction
shortcut_evt = run_task_loop(
task,
task.instructions(exp_win, ctl_win),
eyetracker,
gaze_drawer,
Expand All @@ -66,10 +67,11 @@ def run_task(
eyetracker.start_recording(task.name)
# send start trigger/marker to MEG + Biopac (or anything else on parallel port)
if task.use_meg and not shortcut_evt:
meg.send_signal(meg.MEG_settings["TASK_START_CODE"])
exp_win.callOnFlip(meg.send_signal, meg.MEG_settings["TASK_START_CODE"])

if not shortcut_evt:
shortcut_evt = run_task_loop(
task,
task.run(exp_win, ctl_win),
eyetracker,
gaze_drawer,
Expand All @@ -78,12 +80,13 @@ def run_task(

# send stop trigger/marker to MEG + Biopac (or anything else on parallel port)
if task.use_meg and not shortcut_evt:
meg.send_signal(meg.MEG_settings["TASK_STOP_CODE"])
exp_win.callOnFlip(meg.send_signal, meg.MEG_settings["TASK_STOP_CODE"])

if eyetracker:
eyetracker.stop_recording()

run_task_loop(
task,
task.stop(exp_win, ctl_win),
eyetracker,
gaze_drawer,
Expand Down
6 changes: 3 additions & 3 deletions src/shared/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

EYETRACKING_ROI = (60, 30, 660, 450)

EXP_SCREEN_XRANDR_NAME = "eDP-1"
EXP_SCREEN_XRANDR_NAME = "DVI-D-0"

EXP_MONITOR = Monitor(
name='__blank__',
Expand All @@ -23,7 +23,7 @@
)

EXP_WINDOW = dict(
size=(1280, 1024),
size=(1920, 1080),
screen=1,
fullscr=True,
gammaErrorPolicy="warn",
Expand All @@ -49,4 +49,4 @@
WRAP_WIDTH = 2

# port for meg setup
PARALLEL_PORT_ADDRESS = "/dev/parport0"
PARALLEL_PORT_ADDRESS = "/dev/parport1"
17 changes: 14 additions & 3 deletions src/shared/meg.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@
from . import config
import time

MEG_MARKERS_ON_FLIP = True

MEG_MARKER_DURATION = .001

MEG_settings = {
"TASK_START_CODE": int("00000010", 2),
"TASK_START_STOP": int("00000100", 2),
"TASK_STOP_CODE": int("00000100", 2),
"TASK_FLIP": int("00000101", 2),
}

port = None

def send_signal(data):
port = parallel.ParallelPort(address=config.PARALLEL_PORT_ADDRESS)
global port
if not port:
port = parallel.ParallelPort(address=config.PARALLEL_PORT_ADDRESS)
start=time.monotonic()
port.setData(data)
time.sleep(0.001)
# hog cpu for accurate timing
while time.monotonic() < start + MEG_MARKER_DURATION:
continue
port.setData(0) # reset
2 changes: 2 additions & 0 deletions src/tasks/task_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ def run(self, exp_win, ctl_win):
for clearBuffer in self._run(exp_win, ctl_win):
# yield first to allow external draw before flip
yield
if meg.MEG_MARKERS_ON_FLIP and self.use_meg:
exp_win.callOnFlip(meg.send_signal, meg.MEG_settings["TASK_FLIP"])
self._flip_all_windows(exp_win, ctl_win, clearBuffer)
# increment the progress bar depending on task flip rate
if self.progress_bar:
Expand Down
6 changes: 4 additions & 2 deletions src/tasks/videogame.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def __init__(
state_name=None,
scenario=None,
repeat_scenario=True,
scaling=1,
inttype=retro.data.Integrations.CUSTOM_ONLY,
*args,
**kwargs
Expand All @@ -85,6 +86,7 @@ def __init__(
self.scenario = scenario
self.repeat_scenario = repeat_scenario
self.inttype = inttype
self._scaling = scaling

def _setup(self, exp_win):

Expand All @@ -102,8 +104,8 @@ def _setup(self, exp_win):
exp_win.size[0] / self._first_frame.shape[1],
exp_win.size[1] / self._first_frame.shape[0],
)
width = int(min_ratio * self._first_frame.shape[1])
height = int(min_ratio * self._first_frame.shape[0])
width = int(min_ratio * self._first_frame.shape[1] * self._scaling)
height = int(min_ratio * self._first_frame.shape[0] * self._scaling)

self.game_vis_stim = visual.ImageStim(
exp_win,
Expand Down
5 changes: 3 additions & 2 deletions start_eyetracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ def start_eyetracker(args):
"args": {
"fullscreen": True,
"marker_scale": 1.0,
"sample_duration": 60,
"sample_duration": 360,
"monitor_name": "eDP-1 [1]",
"fixed_screen": True,
"selected_gazer_class_name": "3D"
},
}
)
Expand All @@ -37,7 +38,7 @@ def start_eyetracker(args):
"subject": "start_plugin",
"name": "Annotation_Capture",
"args": {
"annotation_definitions": [['Trigger','T']],
"annotation_definitions": [['Trigger','T'], ['Trigger5','5'], ['Trigger%','%']]
},
}
)
Expand Down
14 changes: 14 additions & 0 deletions utils/check_mario_stable_progress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import glob, pandas
for sub in [1,2,3, 6]:
print(f"sub-{sub:02}" + '#'*50)
ev_fnames = glob.glob(f"sub-{sub:02d}/ses-*/*task-mario_*_events.tsv")
evs = [pandas.read_csv(ev_fname,delimiter='\t') for ev_fname in ev_fnames]
evs_complete = [ev for ev in evs if 'questionnaire-answer' in ev.trial_type.values]
evs_stable = [ev for ev in evs_complete if len(ev.level.unique()) > 1 ]
if len(evs_stable) == 0:
print("not reached stable phase yet?")
continue
quest_onset = [ev[ev.trial_type=='questionnaire-value-change'].onset.values for ev in evs_stable]
levels = pandas.concat([ev[ev.trial_type=='gym-retro_game'].level for ev in evs_stable])
print("stable phase duration (min)", sum([qo[0] for qo in quest_onset if len(qo)])/60.)
#print(levels.value_counts().sort_index())