Skip to content

Commit

Permalink
Make parsing more robust to issues with triggers
Browse files Browse the repository at this point in the history
- Ghost triggers are removed (sent 1 ms before another trigger)
- Duplicate event triggers are removed (the 1st one is kept)
- If the first trial triggers are missing, the EEG data is cropped as well (this happens when the EEG recording is triggered too late)
  • Loading branch information
smathot committed Nov 15, 2024
1 parent 9c9e4da commit 47f8e9d
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 12 deletions.
18 changes: 13 additions & 5 deletions eeg_eyetracking_parser/_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,16 +258,22 @@ def only_trial(name): return name == 'trial'
if '' in dm.t_onset_1:
valid_rows = dm[dm[dm.t_onset_1] != '']
logger.warning(
f'ignoring {len(dm) - len(valid_rows)} rows of eye-tracking data without epoch 1')
f'ignoring {len(dm) - len(valid_rows)} rows of eye-tracking data '
f'without epoch 1')
bigdm = bigdm[valid_rows]
dm = dm[valid_rows]
# Check for missing EEG triggers. If the first trigger is missing, a
# ValueError is raised. If any other trigger is missing, it is marked and
# removed also from dm and bigdm.
triggers = trial_trigger(events)[:, 2]
if triggers[0] != 128:
raise ValueError(
f'The first trial trigger is {triggers[0]}, should be 128')
n_missing_trials = triggers[0] - 128
logger.warning(
f'The first trial trigger is {triggers[0]}, should be 128. '
f'Skipping {n_missing_trials} first trials of eye-tracking data '
f'to maintain alignment.')
dm = dm[n_missing_trials:]
bigdm = bigdm[n_missing_trials:]
rows = list(range(len(dm)))
for i, (tr1, tr2) in enumerate(zip(triggers[:-1], triggers[1:])):
if tr2 - tr1 not in (1, -127):
Expand All @@ -282,7 +288,9 @@ def only_trial(name): return name == 'trial'
missing = len(dm) - len(triggers)
if missing > 0:
logger.warning(
f'final {missing} triggers missing from recording, truncating eye data with one extra trial because the last trial may be incomplete too')
f'final {missing} triggers missing from recording, truncating eye '
f'data with one extra trial because the last trial may be '
f'incomplete too')
dm = dm[:-missing - 1]
bigdm = bigdm[:-missing - 1]
last_trigger_index = np.where(events[0][:, 2] >= 128)[0][-1]
Expand Down Expand Up @@ -421,7 +429,7 @@ def _read_eeg_data(eeg_path, trigger_parser, margin):
logger.info(f'trimming eeg to 0 - {end} s')
raw.crop(0, end)
logger.info('validating events')
_validate_events(events)
events = _validate_events(events[0]), events[1]
logger.info('creating annotations from events')
raw.set_annotations(
mne.annotations_from_events(
Expand Down
29 changes: 22 additions & 7 deletions eeg_eyetracking_parser/_triggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def _validate_events(events):
for i in range(len(events) - 1):
dt = events[i + 1, 0] - events[i, 0]
if dt == 1:
logger.warning(f'ignoring ghost trigger: {events[i]}')
logger.warning(
f'ignoring ghost trigger: {events[i]} before {events[i + 1]}')
continue
valid_events.append(events[i])
events = np.array(valid_events)
Expand All @@ -81,13 +82,27 @@ def _validate_events(events):
raise ValueError('trigger codes should be values between 1 and 255')
# Check for duplicate triggers within trials
trialid = -1
triggers = []
triggers_in_trial = []
select_triggers = []
for code in codes:
# Detect trial triggers and always keep them
if code >= 128:
trialid += 1
triggers = []
if code in triggers:
raise ValueError(
triggers_in_trial = []
select_triggers.append(True)
continue
# Skip triggers that precede a trial onset
if trialid == -1:
logger.warning(f'trigger {code} precedes first trial')
select_triggers.append(False)
continue
# Skip duplicate triggers within a trial
if code in triggers_in_trial:
logger.warning(
f'duplicate trigger {code} in trial {trialid}, label {hex(255 - 128 - trialid % 128)}')
triggers.append(code)
return events
select_triggers.append(False)
else:
select_triggers.append(True)
triggers_in_trial.append(code)
events = events[select_triggers]
return events

0 comments on commit 47f8e9d

Please sign in to comment.