-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathf_sep_trial_groups.m
More file actions
277 lines (214 loc) · 9.11 KB
/
f_sep_trial_groups.m
File metadata and controls
277 lines (214 loc) · 9.11 KB
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
function [EEG_Odor, EEG_Sham, set_sequence] = f_sep_trial_groups(...
EEG, set_sequence, noiseTrialFile, def_variable)
% |===USER INPUT===|
trialEdges = [-15 15]; %Default [-15 15];
% Default [-15 15]. What are the edges (s) around the event codes which the
% trials should be extracted of. The datasets will be sliced into trials
% centered trialEdges seconds around the "triggerOI" defined later.
% |=END USER INPUT=|
% recordings --> trigger1 on, trigger1 off, trigger2 on, trigger2 off, ...
% set_sequence = cell string of sequence of odor stimulation and
% olfactometer control: {'ON_OFF', 'OFF_ON'}
% "on_off" = [ongoing stimulation type 1, post-stimulation type 1] and
% "off_on" = [pre-stimulation type 1, ongoing stimulation type 1], where
% "pre-stimulation type 1" is actually post-stimulation type 2!
% On and Off therefore refers to the current state of the stimulation
% ("switched on" or "switched off").
global str_base % This is the file name
% At this stage, the rejecteddata field (containing time series of rejected
% channels) is probably not of any need any more and increases the file
% size unnecessarily.
if isfield(EEG, 'rejecteddata')
EEG = rmfield(EEG, 'rejecteddata');
end
%% Retain only trials with selected midtrial trigger type of valid length
if strcmp(set_sequence, 'ON_OFF')
triggerOI = 'DIN2';
triggerEND = 'DIN1';
elseif strcmp(set_sequence, 'OFF_ON')
triggerOI = 'DIN1';
triggerEND = 'DIN2';
end
idx_triggerOI = find(strcmp({EEG.event.label}, triggerOI));
cidx_all = {EEG.event.mffkey_cidx};
cidx_all(cellfun('isempty',cidx_all)) = [];
cidx_all = cellfun(@str2double,cidx_all);
cidx_unique = sort(unique(cidx_all));
mark4rejection = zeros(1, numel(cidx_unique));
cidx_unique_ori = cidx_unique;
for cidx = numel(cidx_unique):-1:1
will_be_rejected = 0;
idx = find(strcmp({EEG.event.mffkey_cidx}, ...
num2str(cidx_unique(cidx))));
% where in the event structure are we
% For each event, check whether it occurs exactly twice (start/end)
if sum(cidx_all == cidx_unique(cidx)) ~= 2
will_be_rejected = 1;
warning(['Deleting stimulation because it doesnt have a s', ...
'tart and end.'])
% ...whether first is a start and second an end trigger
elseif ~strcmp(EEG.event(idx(1)).label, 'DIN1') || ...
~strcmp(EEG.event(idx(end)).label, 'DIN2')
will_be_rejected = 1;
warning(['Deleting stimulation because it doesnt have the r', ...
'ight start and end.'])
% ...whether the On period is about 15 s long
elseif EEG.event(idx(end)).latency - EEG.event(idx(1)).latency ...
< 15 * EEG.srate || ...
EEG.event(idx(end)).latency - EEG.event(idx(1)).latency ...
> 15.1 * EEG.srate
will_be_rejected = 1;
warning('Deleting stimulation because its too short or too long.')
end
% Check whether condition is too close to subsequent one
remove_subseq = 0;
if cidx_unique(cidx) == cidx_unique_ori(end) && ...
mod(cidx_unique(cidx), 2) ~= 0
will_be_rejected = 1; % Since no Sham condition following
warning('Deleting Odor because not followed by Sham')
elseif idx(end) < length(EEG.event) - 2
if cidx_unique(cidx) == cidx_unique(end)
break
end
idx_subseq = find(strcmp({EEG.event.mffkey_cidx}, ...
num2str(cidx_unique(cidx+1))));
if mod(cidx_unique(cidx), 2) ~= 0 && ( ...
EEG.event(idx_subseq(1)).latency - ...
EEG.event(idx(end)).latency < 15 * EEG.srate || ...
EEG.event(idx_subseq(1)).latency - ...
EEG.event(idx(end)).latency > 15.1 * EEG.srate )
% We care here whether Odor condition is of adequate length to
% the next Sham condition
remove_subseq = 1;
will_be_rejected = 1;
elseif mod(cidx_unique(cidx), 2) == 0 && ( ...
EEG.event(idx_subseq(1)).latency - ...
EEG.event(idx(end)).latency < 15 * EEG.srate )
% We don't care whether the next trial is too far away since
% it's Sham here and therefore the end of the stimulation cycle
remove_subseq = 1;
will_be_rejected = 1;
end
end
if remove_subseq == 1
cidx_unique(cidx+1)
% cidx_unique(cidx+1) = [];
mark4rejection(cidx+1) = 1;
end
if will_be_rejected == 1
cidx_unique(cidx)
% cidx_unique(cidx) = [];
mark4rejection(cidx) = 1;
end
end
cidx_unique(logical(mark4rejection)) = [];
% It can occur that because of sleep scoring an Odor condition is not
% listed in the EEG.event structure as first condition since it has been
% rejected by extractsws, but instead the structure is starting by Sham. We
% correct this here.
if mod(cidx_unique(1), 2) == 0 % Sham condition
cidx_unique(1) = [];
end
%% =-=-=-=-=-=-=-=-=-=- Slice datasets into trials -=-=-=-=-=-=-=-=-=-=-=-=
% Now all EEG.event are valid, all odd ones are odor, all even ones are
% sham
cidx_odor = cidx_unique(mod(cidx_unique,2) ~= 0);
cidx_sham = cidx_unique(mod(cidx_unique,2) == 0);
[~,Odor_Epochs] = intersect(str2double({EEG.event.mffkey_cidx}), cidx_odor);
[~,Sham_Epochs] = intersect(str2double({EEG.event.mffkey_cidx}), cidx_sham);
idx_trigger_odor = intersect(idx_triggerOI, Odor_Epochs);
idx_trigger_sham = intersect(idx_triggerOI, Sham_Epochs);
% % End check-up
% if numel(idx_trigger_odor) ~= numel(idx_trigger_sham) || ...
% mod(cidx_unique(end), 2) ~= 0
% error('Still not matching correctly!')
% end
EEG_Sham = EEG;
EEG_Odor = EEG;
[EEG_Sham, EEG_Sham.lst_changes{end+1,1}] = pop_epoch( EEG_Sham, ...
{EEG_Sham.event(idx_trigger_sham).type}, ...
trialEdges, ...
'newname', str_base, ...
'epochinfo', 'yes');
[EEG_Odor, EEG_Odor.lst_changes{end+1,1}] = pop_epoch( EEG_Odor, ...
{EEG_Odor.event(idx_trigger_odor).type}, ...
trialEdges, ...
'newname', str_base, ...
'epochinfo', 'yes');
%% =-=-=-=-=-=-=-=-=-=- Balance sequence of trials -=-=-=-=-=-=-=-=-=-=-=-=
% Check for overlapping conditions
cidx_sham = {EEG_Sham.event.mffkey_cidx};
cidx_sham = cellfun(@str2double,cidx_sham);
cidx_odor = {EEG_Odor.event.mffkey_cidx};
cidx_odor = cellfun(@str2double,cidx_odor);
% Check Odor conditions
idx_remove = [];
idx_incomp = find(mod(cidx_odor, 2) == 0);
idx_multip = [];
for i_num = 1:numel(cidx_odor)
s_found = find(cidx_odor == cidx_odor(i_num));
if numel(s_found) > 1
idx_multip = [idx_multip, i_num];
end
end
idx_check = [idx_incomp, idx_multip];
for i = 1:numel(idx_check)
if EEG_Odor.event(idx_check(i)+1).latency - ...
EEG_Odor.event(idx_check(i)).latency < 15 * EEG.srate
idx_remove = [idx_remove, idx_check(i), idx_check(i) + 1];
end
if EEG_Odor.event(idx_check(i)).latency - ...
EEG_Odor.event(idx_check(i)-1).latency < 15 * EEG.srate
idx_remove = [idx_remove, idx_check(i), idx_check(i) - 1];
end
end
idx_remove_odor = unique(idx_remove);
cidx_odor(idx_remove_odor) = [];
% Check Sham conditions
idx_remove = [];
idx_incomp = find(mod(cidx_sham, 2) ~= 0);
idx_multip = [];
for i_num = 1:numel(cidx_sham)
s_found = find(cidx_sham == cidx_sham(i_num));
if numel(s_found) > 1
idx_multip = [idx_multip, i_num];
end
end
idx_check = [idx_incomp, idx_multip];
for i = 1:numel(idx_check)
if EEG_Sham.event(idx_check(i)+1).latency - ...
EEG_Sham.event(idx_check(i)).latency < 15 * EEG.srate
idx_remove = [idx_remove, idx_check(i), idx_check(i) + 1];
end
if EEG_Sham.event(idx_check(i)).latency - ...
EEG_Sham.event(idx_check(i)-1).latency < 15 * EEG.srate
idx_remove = [idx_remove, idx_check(i), idx_check(i) - 1];
end
end
idx_remove_sham = unique(idx_remove);
cidx_sham(idx_remove_sham) = [];
idx_retain_odor = find(ismember(cidx_odor+1, cidx_sham));
idx_retain_sham = find(ismember(cidx_sham-1, cidx_odor));
[EEG_Odor, EEG_Odor.lst_changes{end+1,1}] = pop_select( EEG_Odor, ...
'trial', idx_retain_odor );
[EEG_Sham, EEG_Sham.lst_changes{end+1,1}] = pop_select( EEG_Sham, ...
'trial', idx_retain_sham );
cidx_sham = {EEG_Sham.event.mffkey_cidx};
cidx_sham = cellfun(@str2double,cidx_sham);
cidx_odor = {EEG_Odor.event.mffkey_cidx};
cidx_odor = cellfun(@str2double,cidx_odor);
% End checkup
if any(cidx_sham-1 ~= cidx_odor) || ...
numel(cidx_sham) ~= numel(cidx_odor) || ...
numel(cidx_sham) ~= numel(unique(cidx_sham)) || ...
numel(cidx_odor) ~= numel(unique(cidx_odor)) || ...
any(mod(cidx_sham, 2) ~= 0) || ...
any(mod(cidx_odor, 2) == 0)
% Check whether Sham and Odor datasets have:
% 1. Compatible (Sham trials directly downstream of Odor trials)
% 2. Same amount of trials
% 3. No duplicated triggers
% 4. Appropriate trigger values (Odd ones for Odor, Even ones for Sham)
error('End result is not compatible!')
end
end