Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

unfold_freeviewing.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. function [ufresult]=unfold_freeviewing(sub_num)
  2. %%
  3. cfg = struct();
  4. cfg.subject = num2str(sub_num);
  5. cfg.filtering='causal';
  6. cfg.mainfolder = ['/net/store/nbp/projects/IntoTheWild/Daten/EEG/WLFO/VP' cfg.subject '/preprocessed/' cfg.filtering];
  7. tmp = load(fullfile(cfg.mainfolder,['4_ITW_WLFO_subj' cfg.subject '_cleaningTimes.mat']));
  8. cfg.cleantimes = tmp.rej(:,1:2);
  9. cfg.file = ['3_ITW_WLFO_subj' cfg.subject '_channelrejTriggersXensor.set'];
  10. tmp = pop_loadset('filename',['2_ITW_WLFO_subj' cfg.subject '_bandpass_resample_deblank.set'],'filepath',cfg.mainfolder,'loadmode','info');
  11. tmp=pop_chanedit(tmp, 'lookup','/net/store/nbp/projects/IntoTheWild/EEG_analysis/eeglab13_5_4b/plugins/dipfit2.3/standard_BESA/standard-10-5-cap385.elp');
  12. tmp.chanlocs(find(strcmp({tmp.chanlocs.labels},'CzAmp2'))).labels = 'Iz';
  13. cfg.urchanlocs= tmp.chanlocs;
  14. cfg.avgref=1;
  15. cfg.saccade=1;
  16. cfg.regularize=0;
  17. tmp = load(fullfile(cfg.mainfolder,['6_ITW_WLFO_subj' cfg.subject '_ICAcleancont.mat']));
  18. cfg.badcomponents = tmp.comps_to_rej;
  19. cfg.timewin=[-0.5 1];
  20. %% load EEG data
  21. EEG = pop_loadset('filename',cfg.file,'filepath',cfg.mainfolder);
  22. EEG = pop_select(EEG,'nochannel',{'AUX1','AUX2'});
  23. %% Load ICA and clean
  24. addpath('/net/store/nbp/projects/EEG/blind_spot/amica')
  25. mod = loadmodout12(fullfile(cfg.mainfolder,'amica'));
  26. EEG.icaweights = mod.W;
  27. EEG.icasphere = mod.S;
  28. EEG.icawinv = [];EEG.icaact = [];EEG.icachansind = [];
  29. EEG = eeg_checkset(EEG);
  30. EEG = pop_subcomp(EEG,cfg.badcomponents);
  31. %% interpolate missing channels
  32. % ET channels (Gaze etc.) not interpolated
  33. alldel = {'CzAmp2' 'BIP1' 'BIP2' 'BIP3' 'BIP4' 'BIP5' 'BIP6' 'BIP7' 'BIP8' 'AUX1' 'AUX2' 'AUX3' 'AUX4' 'AUX5' 'AUX6' 'AUX7' 'AUX8','TIME', 'L_GAZE_X', 'L_GAZE_Y', 'L_AREA','R_GAZE_X', 'R_GAZE_Y', 'R_AREA', 'INPUT', 'L-GAZE-X', 'L-GAZE-Y', 'L-AREA','R-GAZE-X', 'R-GAZE-Y', 'R-AREA', 'INPUT'};
  34. %remove VEOG from EEG, as there is no corresponding location
  35. EEG = pop_select(EEG, 'nochannel', {'VEOG'});
  36. if cfg.avgref
  37. %calculate avg ref
  38. EEG = pop_reref( EEG, []); %Participants’ averages were then re-referenced to a common average reference. (Rossion & Caharel, 2011)
  39. end
  40. idxs = ~ismember({cfg.urchanlocs.labels},alldel);
  41. %interpolate missing channels
  42. EEG= pop_interp(EEG,cfg.urchanlocs(idxs),'spherical');
  43. EEG = eeg_checkset(EEG);
  44. %% adjust cleaning times to be between 1 and length of data
  45. [a,b]=find(cfg.cleantimes(:,1)<1);
  46. if ~isempty(a)
  47. cfg.cleantimes(a,1)=1;
  48. end
  49. [a,b]=find(cfg.cleantimes(:,1)>length(EEG.data));
  50. if ~isempty(a)
  51. cfg.cleantimes(a,:)=[];
  52. end
  53. [a,b]=find(cfg.cleantimes(:,2)>length(EEG.data));
  54. if ~isempty(a)
  55. cfg.cleantimes(a,2)=length(EEG.data);
  56. end
  57. %% for each fixation we need to save ALL the necessary information in the EEG structure
  58. % for e = 1:length(EEG.event)
  59. for e = length(EEG.event):-1:1
  60. % if the trigger was the beginning of a trial
  61. if str2num(EEG.event(e).type) <= 171
  62. EEG.event(e).urtype = EEG.event(e).type;
  63. EEG.event(e).type = 'stimulus';
  64. continue
  65. end
  66. % 1-171 stimulus
  67. % 180 stim offset
  68. % 200 exp start
  69. % 255 exp stop
  70. % if the trigger is a fixation
  71. if ismember(EEG.event(e).type, ...
  72. {'HFtoHF' 'HFtoHH' 'HFtoNH' 'HFtoOL' 'HFtoNone' 'HFtoOS' ...
  73. 'HHtoHF' 'HHtoHH' 'HHtoNH' 'HHtoOL' 'HHtoNone' 'HHtoOS' ...
  74. 'NHtoHF' 'NHtoHH' 'NHtoNH' 'NHtoOL' 'NHtoNone' 'NHtoOS' ...
  75. 'OLtoHF' 'OLtoHH' 'OLtoNH' 'OLtoOL' 'OLtoNone' 'OLtoOS' ...
  76. 'NonetoHF' 'NonetoHH' 'NonetoNH' 'NonetoOL' 'NonetoNone' 'NonetoOS' ...
  77. 'OStoHF' 'OStoHH' 'OStoNH' 'OStoOL' 'OStoNone' 'OStoOS' ...
  78. })
  79. EEG.event(e).urtype = EEG.event(e).type;
  80. % if it is a meaningful fixation
  81. toIDX=strfind(EEG.event(e).type,'to');
  82. if toIDX>0
  83. EEG.event(e).curr=EEG.event(e).type((toIDX+2):end);
  84. EEG.event(e).prev=EEG.event(e).type(1:toIDX-1);
  85. else error(['You forgot to code a fixation type:' EEG.event(e).type])
  86. end
  87. EEG.event(e).type = 'fixation';
  88. % continue end
  89. end
  90. % recode all fix that are not Face or None to "OtherHeads"
  91. if strcmp(EEG.event(e).type, 'fixation')
  92. isFix=1;
  93. else
  94. isFix=0;
  95. end
  96. if isFix==1
  97. if ismember(EEG.event(e).curr,{'OS'})==1
  98. EEG.event(e).curr='outside';
  99. elseif ismember(EEG.event(e).curr,{'HF','None'})==0
  100. EEG.event(e).curr='OtherHeads';
  101. end
  102. if ismember(EEG.event(e).prev,{'OS'})==1
  103. EEG.event(e).prev='outside';
  104. elseif ismember(EEG.event(e).prev,{'HF','None'})==0
  105. EEG.event(e).prev='OtherHeads';
  106. end
  107. if EEG.event(e).prevprevhumanface==1
  108. EEG.event(e).prevprev='HF';
  109. elseif EEG.event(e).prevprevnone==1
  110. EEG.event(e).prevprev='None';
  111. elseif EEG.event(e).prevprevoutside==1
  112. EEG.event(e).prevprev='outside';
  113. elseif EEG.event(e).prevprevhumanhead==1
  114. EEG.event(e).prevprev='OtherHeads';
  115. elseif EEG.event(e).prevprevnonhuman==1
  116. EEG.event(e).prevprev='OtherHeads';
  117. else
  118. %first in a trial has no prevprev, therefore we define it as
  119. %'outside' as outside will not be analysed (easiest solution)
  120. EEG.event(e).prevprev='outside';
  121. end
  122. end
  123. end
  124. %% save information about saccaded and fixations
  125. if sum(strcmp({EEG.event.type},'L_saccade'))>0
  126. ixSac = find(strcmp({EEG.event.type},'L_saccade'));
  127. else
  128. ixSac = find(strcmp({EEG.event.type},'R_saccade'));
  129. end
  130. ixFix = find(strcmp({EEG.event.type},'fixation'));
  131. %find blinks
  132. ixBlk=find(cellfun(@(x)strcmp(x,'R_blink'),{EEG.event.type}));
  133. if isempty(ixBlk)
  134. ixBlk=find(cellfun(@(x)strcmp(x,'L_blink'),{EEG.event.type}));
  135. end
  136. latSac = [EEG.event(ixSac).latency];
  137. latFix = [EEG.event(ixFix).latency];
  138. latBlk = [EEG.event(ixBlk).latency];
  139. endSac = latSac+[EEG.event(ixSac).duration];
  140. endBlk = latBlk+[EEG.event(ixBlk).duration];
  141. % put the velocity for blink sac/data loss sections to NaN
  142. for ixS=1:length(ixSac)
  143. blkStarAftertSacStart=find(latBlk>=latSac(ixS),1,'first');
  144. blkStopBeforeSacStop=find(endBlk<=endSac(ixS),1,'last');
  145. if blkStarAftertSacStart== blkStopBeforeSacStop
  146. EEG.event(ixSac(ixS)).sac_vmax=NaN;
  147. end
  148. end
  149. for e = 1:length(latFix)
  150. % for each fixation, find the closest saccade before the event and copy
  151. % the saccade information
  152. % when Sac --> Fix, then lat(Fix) - lat(Sac) should be positive
  153. ix = find((latFix(e) - latSac) > 0,1,'last');
  154. for fn = {'sac_endpos_x','sac_endpos_y','sac_amplitude','sac_startpos_x','sac_startpos_y','sac_vmax'}
  155. EEG.event(ixFix(e)).(fn{1})= EEG.event(ixSac(ix)).(fn{1});
  156. end
  157. end
  158. % remove all fixation with saccade velocity of 0 (= blink saccades)
  159. for ixF=1:length(ixFix)
  160. if EEG.event(ixFix(ixF)).sac_vmax==0
  161. EEG.event(ixFix(ixF)).type='blinkFix';
  162. end
  163. end
  164. ixFix = find(strcmp({EEG.event.type},'fixation'));
  165. latFix = [EEG.event(ixFix).latency];
  166. % remove all fixations close to a blink/data loss section
  167. if ~isempty(ixBlk)
  168. ixBlk=fliplr(ixBlk);
  169. for iB=ixBlk
  170. blkStop=EEG.event(iB).latency+EEG.event(iB).duration;
  171. delFix=find(abs(latFix-blkStop)<50);
  172. if ~isempty(delFix)
  173. for dF=1:length(delFix)
  174. EEG.event(ixFix(delFix(dF))).type='blinkFix';
  175. end
  176. end
  177. end
  178. end
  179. for e = 1:length(latSac)
  180. % for each fixation, find the closest saccade before the event and copy
  181. % the saccade information
  182. % when Sac --> Fix, then lat(Fix) - lat(Sac) should be positive
  183. ix = find((latSac(e) - latFix) > 0,1,'last');
  184. if ~isempty(ix) && ~isempty( EEG.event(ixFix(ix)).prev)
  185. for fn = {'prev','curr','samebox'}
  186. EEG.event(ixSac(e)).(fn{1})= EEG.event(ixFix(ix)).(fn{1});
  187. end
  188. EEG.event(ixSac(e)).type='saccade';
  189. end
  190. end
  191. %% Remove large saccades?!
  192. fprintf('\n\n %u large saccades (>40) will be removed. \n\n\n', length(find([EEG.event.sac_amplitude]>40)))
  193. EEG.event(cellfun(@(x)(x>40),{EEG.event.sac_amplitude})) = [];
  194. %% Config
  195. EEG.unfold = [];
  196. cfgDesign = [];
  197. cfgDesign.splinespacing = 'quantile';
  198. cfgDesign.eventtypes = {
  199. {'fixation'} % fixation onset
  200. {'stimulus'}}; % stimulus onset
  201. cfgDesign.formula = {
  202. 'y~1 + cat(curr) + cat(prev)+ cat(samebox) + humanface:prevhumanface + spl(fix_avgpos_x,5) + spl(fix_avgpos_y,5) + spl(sac_amplitude,5)',
  203. 'y~1'};
  204. cfgDesign.codingschema = 'effects';
  205. cfgDesign.mashup=1;
  206. % if codingschema=effects -> reference cat is the last one
  207. % if codingschema=reference -> reference cat is the first one
  208. if strcmp(cfgDesign.codingschema, 'effects')
  209. cfgDesign.categorical = {
  210. 'curr',{'outside','HF','OtherHeads','None'};
  211. 'prev',{'outside','HF','OtherHeads','None'};
  212. 'samebox',{1,0};
  213. };
  214. elseif strcmp(cfgDesign.codingschema, 'reference')
  215. cfgDesign.categorical = {
  216. 'curr',{'None','HF','OtherHeads','outside'};
  217. 'prev',{'None','HF','OtherHeads','outside'};
  218. 'samebox',{0,1};
  219. };
  220. end
  221. cfgFit = [];
  222. cfgFit.precondition = 1;
  223. cfgFit.lsmriterations = 1500;
  224. % if subset of channels is wanted:
  225. % elecfind = @(x)find(strcmp(x,{EEG.chanlocs.labels}));
  226. % cfgFit.channel = [elecfind('PO8')];
  227. cfgFit.channel = 1:length(EEG.chanlocs);
  228. if cfg.regularize
  229. cfgFit.method='glmnet';
  230. cfgFit.glmnetalpha = 0;
  231. end
  232. EEG = uf_designmat(EEG,cfgDesign);
  233. % if we want a mashup design, exchange everything <1 with 0, in all
  234. % uninteresting parameters
  235. if cfgDesign.mashup
  236. parampos=find(ismember(EEG.unfold.colnames,{'curr_OtherHeads','prev_OtherHeads','curr_outside','prev_outside'}));
  237. for p=1:length(parampos)
  238. pos=find(EEG.unfold.X(:,parampos(p))<0);
  239. EEG.unfold.X(pos,parampos(p))=0;
  240. end
  241. sameboxpos=find(strncmp(EEG.unfold.colnames,'same',4));
  242. sbpos0=find(EEG.unfold.X(:,sameboxpos)<0);
  243. EEG.unfold.X(sbpos0,sameboxpos)=0;
  244. sameboxnpos=find(strncmp(EEG.unfold.colnames,'sameboxn',8));
  245. psbpos0=find(EEG.unfold.X(:,sameboxnpos)<0);
  246. EEG.unfold.X(psbpos0,sameboxnpos)=0;
  247. %the interaction is basically only the multiplicatiom of curr and prev
  248. interactpos=find(ismember(EEG.unfold.colnames,{'humanface:prevhumanface'}));
  249. mainpos =find(ismember(EEG.unfold.colnames,{'curr_HF','prev_HF'}));
  250. EEG.unfold.X(:,interactpos)=EEG.unfold.X(:,mainpos(1)).*EEG.unfold.X(:,mainpos(2));
  251. end
  252. %% Timeshift the design matrix
  253. for t=1:size(cfg.timewin,1)
  254. cfgTimeshift = [];
  255. cfgTimeshift.timelimits = cfg.timewin(t,:);
  256. EEG = uf_timeexpandDesignmat(EEG,cfgTimeshift);
  257. EEG = uf_continuousArtifactExclude(EEG,'winrej',cfg.cleantimes);
  258. %% Fit it iteratively
  259. EEG= uf_glmfit(EEG,cfgFit);
  260. %% Make a massive uni-variate fit without de-convolution
  261. EEGepoch = uf_epoch(EEG,'winrej',cfg.cleantimes,'timelimits',cfgTimeshift.timelimits);
  262. EEGepoch= uf_glmfit_nodc(EEGepoch);
  263. %% Save the data
  264. ufresult = uf_condense(EEGepoch);
  265. save(['/net/store/nbp/projects/IntoTheWild/Daten/EEG/WLFO/unfold_WLFO/' cfg.filtering '/ufresult_subj' num2str(sub_num) '_allElec_mashup_' num2str(cfg.timewin(t,1)*1000) '-' num2str(cfg.timewin(t,2)*1000) '_regularized' num2str(cfg.regularize) '.mat'],'ufresult')
  266. disp('successfully saved')
  267. end