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

encapsulate trial params #286

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
44 changes: 21 additions & 23 deletions eegnb/experiments/Experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from abc import abstractmethod
from typing import Callable
from psychopy import prefs
#change the pref libraty to PTB and set the latency mode to high precision
from eegnb.experiments.utils import TrialParams
#change the pref library to PTB and set the latency mode to high precision
prefs.hardware['audioLib'] = 'PTB'
prefs.hardware['audioLatencyMode'] = 3

Expand All @@ -27,15 +28,15 @@

class BaseExperiment:

def __init__(self, exp_name, duration, eeg, save_fn, n_trials: int, iti: float, soa: float, jitter: float,
use_vr=False, use_fullscr = True):
def __init__(self, exp_name, duration, eeg, save_fn, trial_params: TrialParams, use_vr=False, use_fullscr = True):
""" Initializer for the Base Experiment Class

Args:
n_trials (int): Number of trials/stimulus
iti (float): Inter-trial interval
soa (float): Stimulus on arrival
jitter (float): Random delay between stimulus
exp_name (str): Name of the experiment
duration (float): Total duration of the experiment
eeg: EEG object
save_fn: Function to save data
trial_params (TrialParams): Trial parameters
use_vr (bool): Use VR for displaying stimulus
"""

Expand All @@ -45,17 +46,14 @@ def __init__(self, exp_name, duration, eeg, save_fn, n_trials: int, iti: float,
self.duration = duration
self.eeg = eeg
self.save_fn = save_fn
self.n_trials = n_trials
self.iti = iti
self.soa = soa
self.jitter = jitter
self.trial_params = trial_params
self.use_vr = use_vr
self.use_fullscr = use_fullscr
self.window_size = [1600,800]
self.window_size = [1600,800]

@abstractmethod
def load_stimulus(self):
"""
"""
Method that loads the stimulus for the specific experiment, overwritten by the specific experiment
Returns the stimulus object in the form of [{stim1},{stim2},...]
Throws error if not overwritten in the specific experiment
Expand All @@ -79,27 +77,27 @@ def setup(self, instructions=True):
# Initializing the record duration and the marker names
self.record_duration = np.float32(self.duration)
self.markernames = [1, 2]

# Setting up the trial and parameter list
self.parameter = np.random.binomial(1, 0.5, self.n_trials)
self.trials = DataFrame(dict(parameter=self.parameter, timestamp=np.zeros(self.n_trials)))
self.parameter = np.random.binomial(1, 0.5, self.trial_params.n_trials)
self.trials = DataFrame(dict(parameter=self.parameter, timestamp=np.zeros(self.trial_params.n_trials)))

# Setting up Graphics
# Setting up Graphics
self.window = (
visual.Rift(monoscopic=True, headLocked=True) if self.use_vr
else visual.Window(self.window_size, monitor="testMonitor", units="deg", fullscr=self.use_fullscr))

# Loading the stimulus from the specific experiment, throws an error if not overwritten in the specific experiment
self.stim = self.load_stimulus()

# Show Instruction Screen if not skipped by the user
if instructions:
self.show_instructions()

# Checking for EEG to setup the EEG stream
if self.eeg:
# If no save_fn passed, generate a new unnamed save file
if self.save_fn is None:
if self.save_fn is None:
# Generating a random int for the filename
random_id = random.randint(1000,10000)
# Generating save function
Expand All @@ -109,9 +107,9 @@ def setup(self, instructions=True):
print(
f"No path for a save file was passed to the experiment. Saving data to {self.save_fn}"
)

def show_instructions(self):
"""
"""
Method that shows the instructions for the specific Experiment
In the usual case it is not overwritten, the instruction text can be overwritten by the specific experiment
No parameters accepted, can be skipped through passing a False while running the Experiment
Expand Down Expand Up @@ -151,7 +149,7 @@ def run(self, instructions=True):
""" Do the present operation for a bunch of experiments """

def iti_with_jitter():
return self.iti + np.random.rand() * self.jitter
return self.trial_params.iti + np.random.rand() * self.trial_params.jitter

# Setup the experiment, alternatively could get rid of this line, something to think about
self.setup(instructions)
Expand All @@ -178,7 +176,7 @@ def iti_with_jitter():
if current_trial_end < current_experiment_seconds:
current_trial += 1
current_trial_begin = current_experiment_seconds + iti_with_jitter()
current_trial_end = current_trial_begin + self.soa
current_trial_end = current_trial_begin + self.trial_params.soa

# Do not present stimulus after trial has ended(stimulus on arrival interval).
elif current_trial_begin < current_experiment_seconds:
Expand Down
31 changes: 31 additions & 0 deletions eegnb/experiments/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from dataclasses import dataclass

@dataclass
class TrialParams:
"""
Encapsulates parameters for defining trials in EEG experiments.

This dataclass provides a structured way to define and manage trial-specific
parameters, particularly timing information, for EEG studies. It's designed
to be flexible enough for use in various experimental paradigms, including
but not limited to event-related designs.

Attributes:
iti (float): Inter-Trial Interval, in seconds. The base time between
the end of one trial and the beginning of the next.
jitter (float): Maximum random time, in seconds, added to the ITI.
Used to prevent anticipatory responses and increase
design efficiency in event-related paradigms.
soa (float): Stimulus On Arrival, in seconds. The duration the
stimulus is shown for until the trial ends.
n_trials (int): The total number of trials in the experiment.

Example:
# Create parameters 1s ITI, up to 0.2s jitter, and 0.5s SOA for 100 trials.
params = TrialParams(iti=1.0, jitter=0.2, soa=0.5, n_trials=100)
"""

iti: float
jitter: float
soa: float
n_trials: int
Loading