-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathpspm_get_events.m
155 lines (134 loc) · 5.48 KB
/
pspm_get_events.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
function [sts, import] = pspm_get_events(import)
% ● Description
% pspm_get_events processes events for different event channel types
% ● Format
% [sts, data] = pspm_get_events(import)
% ● Arguments
% import: import job structure with mandatory fields
% .data: mandatory
% .marker: mandatory, accepts 'timestamps' and 'continuous'.
% .sr: timestamps: timeunits in seconds, continuous: sample rate in
% 1/seconds)
% .flank: optional for continuous channels; default: both; accepts
% 'ascending', 'descending', 'both', 'all'.
% ● Output
% import: returns event timestamps in seconds in import.data
% ● History
% Introduced in PsPM 3.0
% Written in 2013-2015 by Dominik R Bach & Tobias Moser (University of Zurich)
%% Initialise
global settings
if isempty(settings)
pspm_init;
end
sts = -1;
% get data
% -------------------------------------------------------------------------
if ~isfield(import, 'marker')
warning('ID:nonexistent_field', 'The field ''marker'' is missing'); return;
elseif strcmpi(import.marker, 'continuous')
% determine relevant data points
% filtering noise does not yet work! -> any kind of noise will be
% identified as maxima/minima
% copy from findLocalMaxima / findpeaks / signal processing toolbox
% with a small change not to loose data with diff()
data = import.data;
% ensure the incoming data is in the format we need it to be
% to process it accordingly (must be vertical)
dim = size(data);
if dim(1) == 1
data = data';
end
possible_values = unique(data);
min_values_indices = data == min(possible_values);
max_values_indices = data == max(possible_values);
data_orig = data;
data = (data + min(possible_values)) / (max(possible_values) - min(possible_values));
data(min_values_indices) = 0;
data(max_values_indices) = 1;
% add more data in order to prevent deleting values with diff
data = [0; 0; 0; data; 0; 0; NaN;];
% store information about finite and infinite in vector
% used to reduce temp vector to relevant data
finite = ~isnan(data);
% initialize temp array and transpose to have a vertical vector
temp = (1:numel(data)).';
% just pick inequal neighbor values of which at least one has a valid
% value
iNeq = [1; 1+ find((data(1:end-1) ~= data(2:end)) ...
& ((finite(1:end-1) | finite(2:end))))];
temp = temp(iNeq);
% we want to check for a difference within a trend
% not for a difference between values -> usage of sign()
% diff the whole dataset not to loose relevant data
s = sign(diff(data));
d = diff(s);
% where are the sign changes of the corresponding differences
% lo2hi should be minima
% hi2lo should be maxima
lo2hi = temp(1+find(d(temp(2:end-1)-2) > 0))-3;
hi2lo = temp(1+find(d(temp(2:end-1)-2) < 0))-4;
if isempty(lo2hi) && isempty(hi2lo)
fprintf('\n');
warning('No markers, or problem with TTL channel.');
import.data = [];
elseif isfield(import,'flank') && strcmpi(import.flank, 'all')
allMrk = find(import.data);
import.data = allMrk./import.sr;
mPos = allMrk+3;
elseif isfield(import, 'flank') && strcmpi(import.flank, 'ascending')
import.data = lo2hi./import.sr;
mPos = lo2hi+3;
elseif isfield(import, 'flank') && strcmpi(import.flank, 'descending')
import.data = (hi2lo+1)./import.sr;
mPos = hi2lo+3;
elseif numel(lo2hi) == numel(hi2lo)
% only use mean if amount of minima corresponds to amount of maxima
% otherwise output a warning
import.data = mean([lo2hi, hi2lo], 2)./import.sr;
mPos = mean([lo2hi, hi2lo],2);
mPos = mPos+3;
else
fprintf('\n');
warning('Different number of hi2lo and lo2hi transitions in marker channel - please choose ascending or descending flank.');
import.data = [];
return;
end;
% check if markerinfo should be set and if there are any data points
if ~isfield(import, 'markerinfo') && ~isempty(import.data)
% determine baseline
v = unique(data_orig(~isnan(data_orig)));
for i=1:numel(v)
v(i,2) = numel(find(data_orig == v(i,1)));
end
% ascending sorting: most frequent value is at the end of this
% vector
v = sortrows(v, 2);
baseline = v(end, 1);
% we are interested in the delta -> remove `baseline offset`
values = data_orig(round(mPos) - 3) - baseline;
import.markerinfo.value = values;
% prepare values to convert them into strings
values = num2cell(values);
import.markerinfo.name = cellfun(@num2str, values, 'UniformOutput', false);
% add one second of tolerance because tails are added at the
% beginning. and maybe sometimes values might not be exactly the
% same
elseif isfield(import, 'markerinfo') && ...
(numel(data) - numel(import.markerinfo.value))/import.sr < 1
% also translate marker info if necessary. this code was written
% with and for import_eyelink function. there flank = 'all'
% has to be set to use import.data as index for the marker values.
n_minfo = struct('value', {import.markerinfo.value(round(import.data*import.sr))}, ...
'name', {import.markerinfo.name(round(import.data*import.sr))});
import.markerinfo = n_minfo;
end
elseif strcmpi(import.marker, 'timestamp') || strcmpi(import.marker, 'timestamps')
import.data = import.data(:) .* import.sr;
else
warning('ID:invalid_field_content', 'The value of ''marker'' must either be ''continuous'' or ''timestamps'''); return;
end;
% set status
% -------------------------------------------------------------------------
sts = 1;
return