From ebfa4616a83761a705113f72e3683cd1a9f5631d Mon Sep 17 00:00:00 2001 From: Dominik Bach Date: Fri, 26 Apr 2024 17:08:10 +0200 Subject: [PATCH 1/7] enable INPUT import in import_eyelink --- src/Import/eyelink/import_eyelink.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Import/eyelink/import_eyelink.m b/src/Import/eyelink/import_eyelink.m index ed81f0bf..1fc36c6c 100644 --- a/src/Import/eyelink/import_eyelink.m +++ b/src/Import/eyelink/import_eyelink.m @@ -267,7 +267,8 @@ eblink_indices = find(strncmp(messages, 'EBLINK', numel('EBLINK'))); ssacc_indices = find(strncmp(messages, 'SSACC', numel('SSACC'))); esacc_indices = find(strncmp(messages, 'ESACC', numel('ESACC'))); -msg_indices = strncmp(messages, 'MSG', numel('MSG')); +msg_indices = strncmp(messages, 'MSG', numel('MSG')) | ... + strncmp(messages, 'INPUT', numel('INPUT')); for name = {'RECCFG', 'ELCLCFG', 'GAZE_COORDS', 'THRESHOLDS', 'ELCL_', 'PUPIL_DATA_TYPE', '!MODE'} msg_indices = msg_indices & ~contains(messages, name); end @@ -433,7 +434,7 @@ extra = setdiff(beg_indices, end_indices); for idx = extra parts = split(messages{idx}); - which_eye = parts{2} + which_eye = parts{2}; beg_time = str2num(parts{3}); end_time = session_end_time; From b96dae93ddbd6d0b769835f1f08a6880869425e5 Mon Sep 17 00:00:00 2001 From: Dominik Bach Date: Sat, 27 Apr 2024 13:50:08 +0200 Subject: [PATCH 2/7] improvements to marker import --- src/Import/eyelink/import_eyelink.m | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Import/eyelink/import_eyelink.m b/src/Import/eyelink/import_eyelink.m index 1fc36c6c..59bea1ca 100644 --- a/src/Import/eyelink/import_eyelink.m +++ b/src/Import/eyelink/import_eyelink.m @@ -311,14 +311,15 @@ msgline = messages{idx}; parts = split(msgline); time = str2num(parts{2}); - if ismember(time,markers.times) - warning('ID:markers_with_same_timestamp',['PsPM found markers with same timestamps.',... - 'Only the first one will be imported.']) - elseif time <= session_end_time + % if ismember(time,markers.times) + % warning('ID:markers_with_same_timestamp',['PsPM found markers with same timestamps.',... + % 'Only the first one will be imported.']) + % keyboard + % elseif time <= session_end_time markers.markers(bsearch(timecol, time)) = true; markers.times(end + 1, 1) = time; markers.names{end + 1, 1} = cell2mat(join(parts(3:end), ' ')); - end + % end end % set data columns From 03e74b40c94d4f10df863074d05291abe9561e23 Mon Sep 17 00:00:00 2001 From: Dominik Bach Date: Mon, 29 Apr 2024 18:09:58 +0200 Subject: [PATCH 3/7] allow analog input into eyetracker to be converted to markers --- src/Import/eyelink/import_eyelink.m | 45 +++++++++------------ src/pspm_get_eyelink.m | 63 ++++++++--------------------- 2 files changed, 35 insertions(+), 73 deletions(-) diff --git a/src/Import/eyelink/import_eyelink.m b/src/Import/eyelink/import_eyelink.m index 59bea1ca..c0c4291f 100644 --- a/src/Import/eyelink/import_eyelink.m +++ b/src/Import/eyelink/import_eyelink.m @@ -22,9 +22,8 @@ % - ymin: y coordinate of top left corner of screen in pixels. % - xmax: x coordinate of bottom right corner of screen in pixels. % - ymax: y coordinate of bottom right corner of screen in pixels. -% markers: Array of the same size as data columns. An entry is 1 if there is -% marker at that time. -% markerinfos: Structure with the following fields: +% markers: Sample number of any detected marker +% markerinfo: Structure with the following fields: % - name: Cell array of marker name. % - value: Index of the marker name to an array containing % unique marker names. @@ -52,7 +51,7 @@ % write outputs % ------------- -markers_sess = create_marker_val_fields(markers_sess); +markers_sess = create_marker_val_fields(markers_sess); % create marker values from unique names for sn = 1:numel(dataraw) data{sn}.raw = dataraw{sn}; data{sn}.channels = data{sn}.raw(:, chan_info{sn}.col_idx); @@ -71,20 +70,20 @@ data{sn}.gaze_coords.ymax = chan_info{sn}.ymax; data{sn}.elcl_proc = chan_info{sn}.elcl_proc; - data{sn}.markers = markers_sess{sn}.markers; - data{sn}.markerinfos.name = markers_sess{sn}.names; - data{sn}.markerinfos.value = markers_sess{sn}.vals; + data{sn}.markers = markers_sess{sn}.times; + data{sn}.markerinfo.name = markers_sess{sn}.names; + data{sn}.markerinfo.value = markers_sess{sn}.vals; end data{end + 1} = combine_markers(markers_sess); session_end_times = calc_session_end_times(messages); for i = 1:numel(data) - 1 - [data{i}.markers, data{i}.markerinfos] = remove_markers_beyond_end(... - data{i}.markers, data{i}.markerinfos, markers_sess{i}.times, session_end_times{i}); + [data{i}.markers, data{i}.markerinfo] = remove_markers_beyond_end(... + data{i}.markers, data{i}.markerinfo, markers_sess{i}.times, session_end_times{i}); end end -function [markers_out, markerinfos_out] = remove_markers_beyond_end(markers, markerinfos, markertimes, sess_end_time) +function [markers_out, markerinfos_out] = remove_markers_beyond_end(markers, markerinfo, markertimes, sess_end_time) mask = markertimes <= sess_end_time; marker_indices = find(markers); @@ -93,7 +92,7 @@ markers_out(marker_indices(end)) = 0; end -markerinfos_out = markerinfos; +markerinfos_out = markerinfo; markerinfos_out.name = markerinfos_out.name(mask); markerinfos_out.value = markerinfos_out.value(mask); end @@ -267,6 +266,8 @@ eblink_indices = find(strncmp(messages, 'EBLINK', numel('EBLINK'))); ssacc_indices = find(strncmp(messages, 'SSACC', numel('SSACC'))); esacc_indices = find(strncmp(messages, 'ESACC', numel('ESACC'))); +% according to information from Yanfang, MSG refers to ethernet input using +% python eyelink library, and INPUT to analog (?) input msg_indices = strncmp(messages, 'MSG', numel('MSG')) | ... strncmp(messages, 'INPUT', numel('INPUT')); for name = {'RECCFG', 'ELCLCFG', 'GAZE_COORDS', 'THRESHOLDS', 'ELCL_', 'PUPIL_DATA_TYPE', '!MODE'} @@ -304,22 +305,14 @@ end % construct markers -markers.markers = false(size(dataraw, 1), 1); markers.times = []; markers.names = {}; for idx = msg_indices msgline = messages{idx}; parts = split(msgline); time = str2num(parts{2}); - % if ismember(time,markers.times) - % warning('ID:markers_with_same_timestamp',['PsPM found markers with same timestamps.',... - % 'Only the first one will be imported.']) - % keyboard - % elseif time <= session_end_time - markers.markers(bsearch(timecol, time)) = true; - markers.times(end + 1, 1) = time; - markers.names{end + 1, 1} = cell2mat(join(parts(3:end), ' ')); - % end + markers.times(end + 1, 1) = time; + markers.names{end + 1, 1} = cell2mat(join(parts(3:end), ' ')); end % set data columns @@ -463,11 +456,11 @@ for i = 1:numel(markers_sess) all_marker_names = [all_marker_names; markers_sess{i}.names]; end -all_markers_struct.times = []; -all_markers_struct.vals = []; -all_markers_struct.names = all_marker_names; +all_markers_struct.markers = []; +all_markers_struct.markerinfo.vals = []; +all_markers_struct.markerinfo.names = all_marker_names; for i = 1:numel(markers_sess) - all_markers_struct.times = [all_markers_struct.times; markers_sess{i}.times]; - all_markers_struct.vals = [all_markers_struct.vals; markers_sess{i}.vals]; + all_markers_struct.markers = [all_markers_struct.markers; markers_sess{i}.times]; + all_markers_struct.markerinfo.vals = [all_markers_struct.markerinfo.vals; markers_sess{i}.vals]; end end diff --git a/src/pspm_get_eyelink.m b/src/pspm_get_eyelink.m index ec5a880f..7ebee98d 100644 --- a/src/pspm_get_eyelink.m +++ b/src/pspm_get_eyelink.m @@ -74,12 +74,12 @@ % iterate through data and fill up channel list as long as there is no % marker channel. if there is any marker channel, the settings accordingly -% markerinfos, markers and marker type. +% markerinfo, markers and marker type. % ------------------------------------------------------------------------- % ensure sessions have the same samplerate -%separate marker_data from real data -all_markers = data{numel(data)}; +% separate marker_data from real data +all_markers = data{end}; data = data(1:numel(data)-1); sr = cell2mat(cellfun(@(d) d.sampleRate, data, 'UniformOutput', false)); eyesObs = cellfun(@(d) d.eyesObserved, data, 'UniformOutput', false); @@ -92,9 +92,9 @@ % samplerate sampleRate = data{1}.sampleRate; % markers - markers = data{1}.markers; - % markerinfos - markerinfos = data{1}.markerinfos; + markers = data{1}.markers/sampleRate; + % markerinfo + markerinfo = data{1}.markerinfo; % units units = data{1}.units; else @@ -103,10 +103,6 @@ last_time = data{1}.raw(1,1); channels = []; - markers = []; - - mi_value = []; - mi_name = {}; n_cols = size(data{1}.channels, 2); counter = 1; @@ -126,25 +122,8 @@ n_diff = round((start_time - last_time)*sr/1000); if n_diff > 0 - % channels and markers + % channels channels(counter:(counter+n_diff-1),1:n_cols) = NaN(n_diff, n_cols); - markers(counter:(counter+n_diff-1), 1) = zeros(n_diff,1); - - % markerinfos - mi_value(end + 1 : end + n_diff, 1) = zeros(n_diff,1); - mi_name( end + 1 : end + n_diff, 1) = {'0'}; - - % find if there are markers in the breaks - break_markers_idx = all_markers.times>=last_time & all_markers.times<=start_time; - tmp_times = all_markers.times(break_markers_idx); - tmp_vals = all_markers.vals(break_markers_idx); - tmp_names = all_markers.names(break_markers_idx); - - % calculate, weher to insert missing markers - tmp_marker_idx = round((tmp_times- last_time)*sr/1000)-1 + counter; - markers(tmp_marker_idx,1) = 1; - mi_value(tmp_marker_idx,1) = tmp_vals; - mi_name(tmp_marker_idx,1) = tmp_names; counter = counter + n_diff; end @@ -152,25 +131,22 @@ % channels and markers channels(counter:(counter+n_data-1),1:n_cols) = data{c}.channels; - markers(counter:(counter+n_data-1),1) = data{c}.markers; - - % markerinfos - n_markers = numel(data{c}.markerinfos.value); - mi_value(end + 1 : end + n_markers, 1) = data{c}.markerinfos.value; - mi_name( end + 1 : end + n_markers, 1) = data{c}.markerinfos.name; counter = counter + n_data; last_time = end_time; end - markerinfos.name = mi_name; - markerinfos.value = mi_value; - % units (they should be for all channels the same) units = data{1}.units; % samplerate sampleRate = sr; + + % markers + markers = all_markers.markers/sampleRate; + % markerinfo + markerinfo = all_markers.markerinfo; + end @@ -204,17 +180,10 @@ end if strcmpi(import{k}.type, 'marker') - import{k}.marker = 'continuous'; - import{k}.sr = sampleRate; + import{k}.marker = 'timestamps'; + import{k}.sr = 1; import{k}.data = markers; - % marker info is read and set (in this instance) but - % imported data cannot be read at the moment (in later instances) - import{k}.markerinfo = markerinfos; - - % by default use 'all' flank for translation from continuous to events - if ~isfield(import{k},'flank') - import{k}.flank = 'all'; - end + import{k}.markerinfo = markerinfo; else % determine channel id from channeltype - eyelink specific % thats why channel ids will be ignored! From b1c410c68dbe6f15b8f672075b8740b07b2f5a86 Mon Sep 17 00:00:00 2001 From: Dominik Bach Date: Mon, 29 Apr 2024 20:09:47 +0200 Subject: [PATCH 4/7] updates --- src/Import/eyelink/import_eyelink.m | 22 +++++++++------------- src/pspm_get_eyelink.m | 4 ++-- test/import_eyelink_test.m | 2 +- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/Import/eyelink/import_eyelink.m b/src/Import/eyelink/import_eyelink.m index c0c4291f..eed5c96b 100644 --- a/src/Import/eyelink/import_eyelink.m +++ b/src/Import/eyelink/import_eyelink.m @@ -79,19 +79,15 @@ session_end_times = calc_session_end_times(messages); for i = 1:numel(data) - 1 [data{i}.markers, data{i}.markerinfo] = remove_markers_beyond_end(... - data{i}.markers, data{i}.markerinfo, markers_sess{i}.times, session_end_times{i}); + data{i}.markers, data{i}.markerinfo, session_end_times{i}, data{1}.raw(1,1)); end +[data{end}.markers, data{end}.markerinfo] = remove_markers_beyond_end(... + data{end}.markers, data{end}.markerinfo, session_end_times{end}, data{1}.raw(1,1)); end -function [markers_out, markerinfos_out] = remove_markers_beyond_end(markers, markerinfo, markertimes, sess_end_time) -mask = markertimes <= sess_end_time; - -marker_indices = find(markers); -markers_out = markers; -if any(~mask) - markers_out(marker_indices(end)) = 0; -end - +function [markers_out, markerinfos_out] = remove_markers_beyond_end(markers, markerinfo, sess_end_time, first_sess_start) +mask = (markers <= sess_end_time) & (markers > first_sess_start); +markers_out = markers(mask); markerinfos_out = markerinfo; markerinfos_out.name = markerinfos_out.name(mask); markerinfos_out.value = markerinfos_out.value(mask); @@ -457,10 +453,10 @@ all_marker_names = [all_marker_names; markers_sess{i}.names]; end all_markers_struct.markers = []; -all_markers_struct.markerinfo.vals = []; -all_markers_struct.markerinfo.names = all_marker_names; +all_markers_struct.markerinfo.value = []; +all_markers_struct.markerinfo.name = all_marker_names; for i = 1:numel(markers_sess) all_markers_struct.markers = [all_markers_struct.markers; markers_sess{i}.times]; - all_markers_struct.markerinfo.vals = [all_markers_struct.markerinfo.vals; markers_sess{i}.vals]; + all_markers_struct.markerinfo.value = [all_markers_struct.markerinfo.value; markers_sess{i}.vals]; end end diff --git a/src/pspm_get_eyelink.m b/src/pspm_get_eyelink.m index 7ebee98d..f22ca36f 100644 --- a/src/pspm_get_eyelink.m +++ b/src/pspm_get_eyelink.m @@ -92,7 +92,7 @@ % samplerate sampleRate = data{1}.sampleRate; % markers - markers = data{1}.markers/sampleRate; + markers = (data{1}.markers-data{1}.raw(1,1))/sampleRate; % markerinfo markerinfo = data{1}.markerinfo; % units @@ -143,7 +143,7 @@ sampleRate = sr; % markers - markers = all_markers.markers/sampleRate; + markers = (all_markers.markers-data{1}.raw(1,1))/sampleRate; % markerinfo markerinfo = all_markers.markerinfo; diff --git a/test/import_eyelink_test.m b/test/import_eyelink_test.m index c1acdb6c..37519efa 100644 --- a/test/import_eyelink_test.m +++ b/test/import_eyelink_test.m @@ -62,7 +62,7 @@ function test_import_eyelink_on_file(this, filepath) sn = sn + 1; sample_idx = 1; msg_idx = 1; - msgtimes = data{sn}.raw(data{sn}.markers, 1); + msgtimes = data{sn}.raw(data{sn}.markers-data{sn}.raw(1,1)+1, 1); this.verifyEqual(data{sn}.sampleRate, to_num(parts{5})); this.verifyEqual(lower(data{sn}.eyesObserved), lower(parts{8})); eyesObserved = parts{8}; From b62a189e76684229708c61c5d9ba170d48425a1e Mon Sep 17 00:00:00 2001 From: Dominik Bach Date: Tue, 30 Apr 2024 15:00:56 +0200 Subject: [PATCH 5/7] fixes --- src/pspm_get_eyelink.m | 7 ++++--- test/import_eyelink_test.m | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pspm_get_eyelink.m b/src/pspm_get_eyelink.m index f22ca36f..98950d07 100644 --- a/src/pspm_get_eyelink.m +++ b/src/pspm_get_eyelink.m @@ -136,14 +136,15 @@ last_time = end_time; end - % units (they should be for all channels the same) + % units (they should be the same for all sessions) units = data{1}.units; % samplerate sampleRate = sr; - % markers - markers = (all_markers.markers-data{1}.raw(1,1))/sampleRate; + % markers (time stamps are in ms; convert to seconds) + markers = (all_markers.markers-data{1}.raw(1,1))/1000; + % markerinfo markerinfo = all_markers.markerinfo; diff --git a/test/import_eyelink_test.m b/test/import_eyelink_test.m index 37519efa..3edcc06f 100644 --- a/test/import_eyelink_test.m +++ b/test/import_eyelink_test.m @@ -62,7 +62,7 @@ function test_import_eyelink_on_file(this, filepath) sn = sn + 1; sample_idx = 1; msg_idx = 1; - msgtimes = data{sn}.raw(data{sn}.markers-data{sn}.raw(1,1)+1, 1); + msgtimes = data{sn}.markers; this.verifyEqual(data{sn}.sampleRate, to_num(parts{5})); this.verifyEqual(lower(data{sn}.eyesObserved), lower(parts{8})); eyesObserved = parts{8}; @@ -146,7 +146,7 @@ function test_import_eyelink_on_file(this, filepath) % check if current message and its time is correct message = parts(3:end); message = message{1}; - this.verifyEqual(message, data{sn}.markerinfos.name{msg_idx}); + this.verifyEqual(message, data{sn}.markerinfo.name{msg_idx}); this.verifyEqual(to_num(parts{2}), msgtimes(msg_idx)); msg_idx = msg_idx + 1; end From 7850352ee85c015367f5d5eee5302f665eebd168 Mon Sep 17 00:00:00 2001 From: Dominik Bach Date: Tue, 30 Apr 2024 15:22:14 +0200 Subject: [PATCH 6/7] update comments --- src/Import/eyelink/import_eyelink.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Import/eyelink/import_eyelink.m b/src/Import/eyelink/import_eyelink.m index eed5c96b..15f94186 100644 --- a/src/Import/eyelink/import_eyelink.m +++ b/src/Import/eyelink/import_eyelink.m @@ -262,8 +262,8 @@ eblink_indices = find(strncmp(messages, 'EBLINK', numel('EBLINK'))); ssacc_indices = find(strncmp(messages, 'SSACC', numel('SSACC'))); esacc_indices = find(strncmp(messages, 'ESACC', numel('ESACC'))); -% according to information from Yanfang, MSG refers to ethernet input using -% python eyelink library, and INPUT to analog (?) input +% MSG refers to ethernet input using python eyelink library, and INPUT to +% parallel/serial port input msg_indices = strncmp(messages, 'MSG', numel('MSG')) | ... strncmp(messages, 'INPUT', numel('INPUT')); for name = {'RECCFG', 'ELCLCFG', 'GAZE_COORDS', 'THRESHOLDS', 'ELCL_', 'PUPIL_DATA_TYPE', '!MODE'} From 7524bd19aa683bf344cce2c4baaf0fd41379c407 Mon Sep 17 00:00:00 2001 From: Dominik Bach Date: Thu, 2 May 2024 17:52:55 +0200 Subject: [PATCH 7/7] (re)move auxiliary functions --- .../eyelink}/set_blinks_saccades_to_nan.m | 6 +- src/backroom/blink_saccade_filtering.m | 35 ------ src/pspm_blink_saccade_filt.m | 110 ------------------ src/pspm_get_eyelink.m | 8 +- src/pspm_get_smi.m | 4 +- src/pspm_get_viewpoint.m | 4 +- test/pspm_test.m | 1 - test/pspm_test_github_actions.m | 1 - test/set_blinks_saccades_to_nan_test.m | 4 +- 9 files changed, 13 insertions(+), 160 deletions(-) rename src/{backroom => Import/eyelink}/set_blinks_saccades_to_nan.m (95%) delete mode 100644 src/backroom/blink_saccade_filtering.m delete mode 100644 src/pspm_blink_saccade_filt.m diff --git a/src/backroom/set_blinks_saccades_to_nan.m b/src/Import/eyelink/set_blinks_saccades_to_nan.m similarity index 95% rename from src/backroom/set_blinks_saccades_to_nan.m rename to src/Import/eyelink/set_blinks_saccades_to_nan.m index 3cd17998..d17d2be1 100644 --- a/src/backroom/set_blinks_saccades_to_nan.m +++ b/src/Import/eyelink/set_blinks_saccades_to_nan.m @@ -27,7 +27,11 @@ % % - all elements in right data columns (except blink/saccade) that correspond to right % blink or saccade rows are set to NaN - if nargin == 3 +%__________________________________________________________________________ +% +% (C) 2019 Eshref Yozdemir + +if nargin == 3 fn_is_left = @(x) strcmp(x(end-1:end), '_l'); end diff --git a/src/backroom/blink_saccade_filtering.m b/src/backroom/blink_saccade_filtering.m deleted file mode 100644 index 78faac4b..00000000 --- a/src/backroom/blink_saccade_filtering.m +++ /dev/null @@ -1,35 +0,0 @@ -function [out_data_mat] = blink_saccade_filtering(data_mat, column_names, mask_chans, n_samples, fn_is_left) - if nargin == 4 - fn_is_left = @(x) strcmp(x(end-1:end), '_l'); - end - - out_data_mat = expand_mask_chans(data_mat, column_names, mask_chans, n_samples); - out_data_mat = set_blinks_saccades_to_nan(out_data_mat, column_names, mask_chans); -end - -function data = expand_mask_chans(data, column_names, mask_chans, offset) - for chan = mask_chans - col_idx = find(strcmpi(column_names, chan{1})); - data(:, col_idx) = expand_mask(data(:, col_idx), offset); - end -end - -function mask = expand_mask(mask, offset) - diffmask = diff(mask); - indices_to_expand_towards_left = find(diffmask == 1) + 1; - indices_to_expand_towards_right = find(diffmask == (-1)); - - for ii = 1:numel(indices_to_expand_towards_left) - idx = indices_to_expand_towards_left(ii); - begidx = max(1, idx - offset); - endidx = max(1, idx - 1); - mask(begidx : endidx) = true; - end - ndata = numel(mask); - for ii = 1:numel(indices_to_expand_towards_right) - idx = indices_to_expand_towards_right(ii); - begidx = min(ndata, idx + 1); - endidx = min(ndata, idx + offset); - mask(begidx : endidx) = true; - end -end diff --git a/src/pspm_blink_saccade_filt.m b/src/pspm_blink_saccade_filt.m deleted file mode 100644 index ff6c487e..00000000 --- a/src/pspm_blink_saccade_filt.m +++ /dev/null @@ -1,110 +0,0 @@ -function [sts, out_channel] = pspm_blink_saccade_filt(fn, discard_factor, options) -% ● Description -% pspm_blink_saccade_filt perform blink-saccade filtering on a given file containing -% pupil data. This function extends each blink and/or saccade period towards the -% beginning and the end of the signal by an amount specified by the user. -% ● Format -% [sts, out_channel] = pspm_blink_saccade_filt(fn, discard_factor, options) -% ● Arguments -% fn: [string] Path to the PsPM file which contains -% the pupil data. -% discard_factor: [numeric] Factor used to determine the number of -% samples right before and right after a blink/saccade -% period to discard. This value is multiplied by the -% sampling rate of the recording to determine the -% number of samples to discard from one end. Therefore, -% for each blink/saccade period, 2*this_value*SR many -% samples are discarded in total, and effectively -% blink/saccade period is extended. -% This value also corresponds to the duration of -% samples to discard on one end in seconds. For example, -% when it is 0.01, we discard 10 ms worth of data on -% each end of every blink/saccade period. -% options: -% .channel: [numeric/string, optional, default:0] -% Channel ID to be preprocessed. -% By default preprocesses all the pupil and gaze channels. -% .channel_action: [string, optional, accept:'add'/'replace', default:'add'] -% Defines whether corrected data should be added or the -% corresponding preprocessed channel should be replaced. -% ● History -% Written in 2020 by Eshref Yozdemir (University of Zurich) - -global settings; -if isempty(settings), pspm_init; end -sts = -1; -if nargin == 2 - options = struct(); -end -options = pspm_options(options, 'blink_saccade_filt'); -if options.invalid; return; end -if ~isnumeric(discard_factor) - warning('ID:invalid_input', 'discard_factor must be numeric'); - return -end -[lsts, ~, data] = pspm_load_data(fn); -if lsts ~= 1 - return -end -[lsts, ~, data_user] = pspm_load_data(fn, options.channel); -if lsts ~= 1 - return -end -data_user = keep_pupil_gaze_channels(data_user); -%% build matrixes and lists -data_mat = {}; -column_names = {}; -mask_channels = {}; -for i = 1:numel(data) - channeltype = data{i}.header.chantype; - if strncmp(channeltype, 'blink', numel('blink')) || ... - strncmp(channeltype, 'saccade', numel('saccade')) - mask_channels{end + 1} = channeltype; - data_mat{end + 1} = data{i}.data; - column_names{end + 1} = channeltype; - end -end -n_mask_channels = numel(data_mat); -for i = 1:numel(data_user) - channeltype = data_user{i}.header.chantype; - should_add = options.channel ~= 0 || ... - strncmp(channeltype, 'pupil', numel('pupil')) || ... - strncmp(channeltype, 'gaze', numel('gaze')); - if should_add - data_mat{end + 1} = data_user{i}.data; - column_names{end + 1} = data_user{i}.header.chantype; - end -end -data_mat = cell2mat(data_mat); -%% perform filtering -addpath(pspm_path('backroom')); -sr = data{1}.header.sr; -samples_to_discard = round(sr * discard_factor); -out_mat = blink_saccade_filtering(data_mat, column_names, mask_channels, samples_to_discard); -rmpath(pspm_path('backroom')); -%% write back -for i = 1:numel(data_user) - data_user{i}.data = out_mat(:, n_mask_channels + i); -end -channel_str = options.channel; -if isnumeric(channel_str) - channel_str = num2str(channel_str); -end -o.msg.prefix = sprintf('Blink saccade filtering :: Input channel: %s', channel_str); -[lsts, out_id] = pspm_write_channel(fn, data_user, options.channel_action, o); -if lsts ~= 1 - return -end -out_channel = out_id.channel; -sts = 1; -return -%% keep_pupil_gaze_channels -function [out_cell] = keep_pupil_gaze_channels(in_cell) -out_cell = {}; -for i = 1:numel(in_cell) - channel = lower(in_cell{i}.header.chantype); - if contains(channel, 'pupil') || contains(channel, 'gaze') - out_cell{end + 1} = in_cell{i}; - end -end - diff --git a/src/pspm_get_eyelink.m b/src/pspm_get_eyelink.m index 98950d07..2aad77d9 100644 --- a/src/pspm_get_eyelink.m +++ b/src/pspm_get_eyelink.m @@ -50,7 +50,6 @@ % expand blink/saccade channels with offset % set data channels with blinks/saccades to NaN % ------------------------------------------------------------------------- -addpath(pspm_path('backroom')); for i = 1:numel(data)-1 if strcmpi(data{i}.eyesObserved, settings.eye.char.l) mask_chans = {'blink_l', 'saccade_l'}; @@ -61,16 +60,13 @@ else warning('ID:invalid_input', ['No valid eye marker is detected, please check input channels.']); end - expand_factor = 0; - data{i}.channels = blink_saccade_filtering(... + data{i}.channels = set_blinks_saccades_to_nan(... data{i}.channels, ... data{i}.channel_header, ... - mask_chans, ... - expand_factor * data{i}.sampleRate ... + mask_chans ... ); end -rmpath(pspm_path('backroom')); % iterate through data and fill up channel list as long as there is no % marker channel. if there is any marker channel, the settings accordingly diff --git a/src/pspm_get_smi.m b/src/pspm_get_smi.m index 26a24a35..a89815a5 100644 --- a/src/pspm_get_smi.m +++ b/src/pspm_get_smi.m @@ -119,7 +119,7 @@ if ~assert_sessions_are_one_after_another(data); return; end end [data_concat, markers, mi_values, mi_names] = concat_sessions(data); -addpath(pspm_path('backroom')); +addpath(pspm_path('Import','eyelink')); chan_struct = data{1}.channel_columns; eyes_observed = lower(data{1}.eyesObserved); if strcmpi(eyes_observed, settings.lateral.char.l) @@ -134,7 +134,7 @@ chan_struct,... mask_chans,... @(x) contains(x, 'L ')); -rmpath(pspm_path('backroom')); +rmpath(pspm_path('Import','eyelink')); sampling_rate = data{1}.sampleRate; units = data{1}.units; raw_columns = data{1}.raw_columns; diff --git a/src/pspm_get_viewpoint.m b/src/pspm_get_viewpoint.m index f9ec090d..978b5255 100644 --- a/src/pspm_get_viewpoint.m +++ b/src/pspm_get_viewpoint.m @@ -103,7 +103,7 @@ screen_size = data{1}.screenSize; viewing_dist = data{1}.viewingDistance; -addpath(pspm_path('backroom')); +addpath(pspm_path('Import', 'eyelink')); if strcmpi(eyes_observed, settings.lateral.char.l) mask_chans = {'blink_l', 'saccade_l'}; elseif strcmpi(eyes_observed, settings.lateral.char.r) @@ -112,7 +112,7 @@ mask_chans = {'blink_l', 'blink_r', 'saccade_l', 'saccade_r'}; end data_concat = set_blinks_saccades_to_nan(data_concat, chan_struct, mask_chans, @(x) strcmp(x(end-1:end), '_l')); -rmpath(pspm_path('backroom')); +rmpath(pspm_path('Import', 'eyelink')); num_import_cells = numel(import); for k = 1:num_import_cells diff --git a/test/pspm_test.m b/test/pspm_test.m index b4d0825e..463e4bff 100644 --- a/test/pspm_test.m +++ b/test/pspm_test.m @@ -22,7 +22,6 @@ function pspm_test(varargin) TestSuite.fromClass(?pspm_align_channels_test), ... TestSuite.fromClass(?pspm_bf_data_test), ... TestSuite.fromClass(?pspm_bf_test), ... - TestSuite.fromClass(?pspm_blink_saccade_filt_test), ... TestSuite.fromClass(?pspm_convert_gaze_test), ... TestSuite.fromClass(?pspm_convert_unit_test), ... TestSuite.fromClass(?pspm_dcm_test), ... diff --git a/test/pspm_test_github_actions.m b/test/pspm_test_github_actions.m index caf10ae0..f167732c 100644 --- a/test/pspm_test_github_actions.m +++ b/test/pspm_test_github_actions.m @@ -21,7 +21,6 @@ function pspm_test_github_actions(varargin) TestSuite.fromClass(?pspm_align_channels_test), ... TestSuite.fromClass(?pspm_bf_data_test), ... TestSuite.fromClass(?pspm_bf_test), ... - TestSuite.fromClass(?pspm_blink_saccade_filt_test), ... TestSuite.fromClass(?pspm_convert_gaze_test), ... TestSuite.fromClass(?pspm_convert_unit_test), ... TestSuite.fromClass(?pspm_check_python_test), ... diff --git a/test/set_blinks_saccades_to_nan_test.m b/test/set_blinks_saccades_to_nan_test.m index f8ecb246..06ce456e 100644 --- a/test/set_blinks_saccades_to_nan_test.m +++ b/test/set_blinks_saccades_to_nan_test.m @@ -11,7 +11,7 @@ end methods(TestClassSetup) function add(this) - addpath(pspm_path('backroom')); + addpath(pspm_path('Import', 'eyelink')); end end methods(Test) @@ -71,7 +71,7 @@ function test_both_eyes(this) end methods(TestClassTeardown) function remove(this) - addpath(pspm_path('backroom')); + addpath(pspm_path('Import', 'eyelink')); end end end \ No newline at end of file