function D = spm_eeg_firstlevel(S) % Convolution modelling for M/EEG data % FORMAT D = spm_eeg_firstlevel(S) % % S - job structure generated by spm_cfg_eeg_firstlevel %__________________________________________________________________________ % Reference: Litvak V, Jha A, Flandin G, Friston K. Convolution models for % induced electromagnetic responses. Neuroimage. 2013, 64:388-98 %__________________________________________________________________________ % Copyright (C) 2013 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak % $Id: spm_eeg_firstlevel.m 6146 2014-09-02 11:27:43Z vladimir $ SVNrev = '$Rev: 6146 $'; %-Startup %-------------------------------------------------------------------------- spm('FnBanner', mfilename, SVNrev); spm('FigName','M/EEG first level'); D = spm_eeg_load(char(S.sess.D)); statdir = D.path; try delete(fullfile(statdir, 'SPM.mat')); end job{1}.spm.stats.fmri_design = []; job{1}.spm.stats.fmri_design.dir = {statdir}; job{1}.spm.stats.fmri_design.timing.units = 'secs'; job{1}.spm.stats.fmri_design.timing.fmri_t0 = 1; job{1}.spm.stats.fmri_design.volt = S.volt; job{1}.spm.stats.fmri_design.cvi = 'none'; isTF = strncmpi(D.transformtype,'TF',2); time = D.time; dt = mean(diff(time)); nt = D.nsamples; channels = D.selectchannels(S.channels); nchan = numel(channels); nf = D.nfrequencies; job{1}.spm.stats.fmri_design.timing.RT = dt; job{1}.spm.stats.fmri_design.sess = rmfield(S.sess, {'D', 'cond', 'convregress', 'multi_conv_reg', 'savereg'}); job{1}.spm.stats.fmri_design.sess.hpf = Inf; job{1}.spm.stats.fmri_design.sess.nscan = nt; nc = numel(S.sess.cond); if nc == 0 job{1}.spm.stats.fmri_design.sess.cond = rmfield(S.sess.cond, 'define'); else for j = 1:nc conditionlabels = {}; trigsample = {}; duration = {}; durationsample = {}; if isfield(S.sess.cond(j).define, 'event') S1 = []; S1.D = D; S1.reviewtrials = 0; S1.save = 0; S1.trialdef = S.sess.cond(j).define.event; S1.timewin = [0 0]; [S1.trialdef(:).conditionlabel] = deal(S.sess.cond(j).name); trl = spm_eeg_definetrial(S1); trigsample{j} = trl(:, 1); trig{j} = time(trigsample{j}); duration{j} = 0*trig{j}; durationsample{j} = 0*trig{j}; elseif isfield(S.sess.cond(j).define, 'manual') if isequal(S.timing.units, 'secs') trig{j} = S.sess.cond(j).define.manual.onset; trigsample{j} = D.indsample(trig{j}); duration{j} = S.sess.cond(j).define.manual.duration; if isempty(duration) duration{j} = 0*trig{j}; end durationsample{j} = round(duration{j}*D.fsample); else trigsample{j} = S.sess.cond(j).define.manual.onset; trig{j} = time(trigsample{j}); durationsample{j} = S.sess.cond(j).define.manual.duration; if isempty(durationsample) durationsample{j} = 0*trig{j}; end duration{j} = durationsample{j}/D.fsample; end conditionlabels{j} = repmat({S.sess.cond(j).name}, length(trig{j}), 1); else error('Unsupported option'); end job{1}.spm.stats.fmri_design.sess.cond(j).name = S.sess.cond(j).name; job{1}.spm.stats.fmri_design.sess.cond(j).onset = trig{j} - D.timeonset; job{1}.spm.stats.fmri_design.sess.cond(j).duration = duration{j}; job{1}.spm.stats.fmri_design.sess.cond(j).tmod = S.sess.cond(j).tmod; job{1}.spm.stats.fmri_design.sess.cond(j).pmod = S.sess.cond(j).pmod; job{1}.spm.stats.fmri_design.sess.cond(j).orth = S.sess.cond(j).orth; end end eventchanind = D.indchannel('event'); if ~isempty(eventchanind) && nc sess = job{1}.spm.stats.fmri_design.sess; job{1}.spm.stats.fmri_design.sess.cond = ... rmfield(sess.cond, {'tmod', 'pmod', 'orth'}); job{1}.spm.stats.fmri_design.timing.fmri_t = 1; job{1}.spm.stats.fmri_design.bases.fir.length = dt; job{1}.spm.stats.fmri_design.bases.fir.order = 1; job{1}.spm.stats.fmri_design.sess.regress = []; job{1}.spm.stats.fmri_design.sess =... rmfield(job{1}.spm.stats.fmri_design.sess, 'regress'); [dummy, job] = spm_jobman('harvest', job); spm_run_fmri_spec(job{1}.spm.stats.fmri_design); load(fullfile(statdir, 'SPM.mat')); X = SPM.xX.X*SPM.xBF.dt; X = X(:, 1:(end-1)); aX = any(X', 1); caX = cumsum(aX); if isTF Y = squeeze(mean(D(eventchanind, :, :), 2)); else Y = D(eventchanind, :); end Y = Y-min(Y); Y = Y/max(Y); [c, lags] = xcorr(aX, Y, 500, 'coeff'); [dummy, ind] = max(c); shiftcorr = lags(ind); if ~spm('CmdLine') spm_figure('GetWin','Timing check'); clf subplot(2, 3, 1:3); plot(lags, c); xlim(100*[-1 1]); ntoplot = 5; i1 = find(caX == 1); i2 = find(caX == ntoplot); i3= find((caX-caX(floor(nt/2))) == 1); i4= find((caX-caX(floor(nt/2))) == ntoplot); i5= find((caX-caX(end)) == -ntoplot); i6= find((caX-caX(end)) == 0); subplot(2, 3, 4); plot(time(i1(1):i2(1)), Y(i1(1):i2(1)), 'k'); hold on plot(time(i1(1):i2(1)), X(i1(1):i2(1), :)); subplot(2, 3, 5); plot(time(i3(1):i4(1)), Y(i3(1):i4(1)), 'k'); hold on plot(time(i3(1):i4(1)), X(i3(1):i4(1), :)); subplot(2, 3, 6); plot(time(i5(1):i6(1)), Y(i5(1):i6(1)), 'k'); hold on plot(time(i5(1):i6(1)), X(i5(1):i6(1), :)); end delete(fullfile(statdir, 'SPM.mat')); job{1}.spm.stats.fmri_design.bases = []; job{1}.spm.stats.fmri_design.sess = sess; else shiftcorr = 0; end for c = 1:nc job{1}.spm.stats.fmri_design.sess.cond(c).onset = ... job{1}.spm.stats.fmri_design.sess.cond(c).onset + 1e-3*S.timing.timewin(1) - shiftcorr*dt; end U = struct([]); for i = 1:numel(S.sess.convregress) U(i).name = S.sess.convregress(i).name; U(i).u = S.sess.convregress(i).val; end ncr = numel(U); if ~isequal(S.sess.multi_conv_reg, {''}) temp = load(S.sess.multi_conv_reg{1}); for i = 1:size(temp.R, 2) U(ncr+i).u = temp.R(:, i); if isfield(temp, 'names') U(ncr+i).name = temp.names{i}; else U(ncr+i).name = ['R' num2str(i)]; end end end ncr = numel(U); % Create dummy conditions to replace with continuous regressors later for i = 1:ncr job{1}.spm.stats.fmri_design.sess.cond(nc+i).name = U(i).name; job{1}.spm.stats.fmri_design.sess.cond(nc+i).onset = D.time(round(D.nsamples/2)); job{1}.spm.stats.fmri_design.sess.cond(nc+i).duration = 0; job{1}.spm.stats.fmri_design.sess.cond(nc+i).tmod = 0; job{1}.spm.stats.fmri_design.sess.cond(nc+i).pmod = struct([]); job{1}.spm.stats.fmri_design.sess.cond(nc+i).orth = 0; end job{1}.spm.stats.fmri_design.timing.fmri_t = S.timing.utime; bname = char(fieldnames(S.bases)); job{1}.spm.stats.fmri_design.bases = S.bases; job{1}.spm.stats.fmri_design.bases.(bname).length = 1e-3*diff(sort(S.timing.timewin)); [dummy, job] = spm_jobman('harvest', job); spm_run_fmri_spec(job{1}.spm.stats.fmri_design); clear job load(fullfile(statdir, 'SPM.mat')); X = SPM.xX.X*SPM.xBF.dt; nb = size(SPM.xBF.bf, 1); nr = size(SPM.xBF.bf, 2); T = round(1/(SPM.xBF.dt*D.fsample)); if ~isempty(U) if S.timing.timewin(1)<0 pad = round(abs(1e-3*S.timing.timewin(1)*D.fsample)); else pad = 0; end for i = 1:ncr u = U(i).u; u = u(:); if length(u)~= D.nsamples error('Convolution regressor should be the same length as the data.'); end u = [u(:); zeros(pad, 1)]; if T~=1 u = interp1(1:length(u), u, 1:1/T:length(u)); end U(i).u = u(:); U(i).dt = SPM.xBF.dt; if ~iscell(U(i).name) U(i).name = {U(i).name}; end end %-Convolve stimulus functions with basis functions %---------------------------------------------------------------------- [Xu] = spm_Volterra(U, SPM.xBF.bf); %-Resample regressors %---------------------------------------------------------------------- Xu = Xu((0:(nt + pad - 1))*T+1, :); Xu = Xu((1+pad):end, :); X(:, (nc*nr+1):((nc+ncr)*nr)) = Xu*SPM.xBF.dt; end SPM.xX.X = X./SPM.xBF.dt; %-Save SPM.mat %-------------------------------------------------------------------------- fprintf('%-40s: ','Saving SPM configuration') %-# save(fullfile(statdir, 'SPM.mat'), 'SPM', spm_get_defaults('mat.format')); fprintf('%30s\n','...SPM.mat saved') %-# if S.sess.hpf K = []; K.RT = dt; K.row = SPM.Sess.row; K.HParam = S.sess.hpf; else K = 1; end xX = spm_sp('Set', spm_filter(K, X)); % filter design if ~isfield(xX,'pX') xX.pX = spm_sp('x-',xX); end if ~isempty(SPM.Sess.U) label = {}; for i = 1:numel(SPM.Sess.U) label = [label SPM.Sess.U(i).name]; end ne = numel(label); if isTF Dout = clone(D, spm_file(D.fname, 'prefix', S.prefix), [nchan nf nb ne]); else Dout = clone(D, spm_file(D.fname, 'prefix', S.prefix), [nchan nb ne]); end Dout = fsample(Dout, 1/SPM.xBF.dt); Dout = timeonset(Dout, 1e-3*S.timing.timewin(1)); Dout = chanlabels(Dout, ':', D.chanlabels(channels)); Dout = conditions(Dout, ':', label); Dout = type(Dout, 'evoked'); spm_progress_bar('Init', nchan, 'channels done'); if nchan > 100, Ibar = floor(linspace(1, nchan, 100)); else Ibar = 1:nchan; end for c = 1:nchan W = ones(nt, 1); W(D.badsamples(channels(c), ':', 1)) = exp(-256); W = spdiags(W, 0, nt, nt); axX = spm_sp('Set', spm_filter(K, W*X)); if ~isfield(axX,'pX') axX.pX = spm_sp('x-',axX); end Y = reshape(D(channels(c), :, :, :), nf, nt); Y = spm_filter(K, W*Y'); B = axX.pX*Y; for i = 1:ne xY = SPM.xBF.bf*B((i-1)*nr+(1:nr), :); if isTF Dout(c, :, :, i) = shiftdim(xY', -1); else Dout(c, :, i) = xY'; end end if ismember(c, Ibar), spm_progress_bar('Set', c); end end spm_progress_bar('Clear'); %-Save %-------------------------------------------------------------------------- save(Dout); else Dout = []; nr = 0; ne = 0; end if ~isempty(SPM.Sess.C.C) && S.sess.savereg rlabel = {}; for i = 1:numel(SPM.Sess.C) rlabel = [rlabel SPM.Sess.C(i).name]; end ng = numel(rlabel); if ~isempty(Dout) preprefix = 'R'; else preprefix = ''; end if isTF Dr = clone(D, spm_file(D.fname, 'prefix', [S.prefix preprefix]), [nchan nf 1 ng]); else Dr = clone(D, spm_file(D.fname, 'prefix', [S.prefix preprefix]), [nchan 1 ng]); end Dr = fsample(Dr, 0); Dr = timeonset(Dr, 0); Dr = chanlabels(Dr, ':', D.chanlabels(channels)); Dr = conditions(Dr, ':', rlabel); Dr = type(Dr, 'evoked'); spm_progress_bar('Init', nchan, 'channels done'); if nchan > 100, Ibar = floor(linspace(1, nchan, 100)); else Ibar = 1:nchan; end for c = 1:nchan W = ones(nt, 1); W(D.badsamples(channels(c), ':', 1)) = exp(-256); W = spdiags(W, 0, nt, nt); axX = spm_sp('Set', spm_filter(K, W*X)); if ~isfield(axX,'pX') axX.pX = spm_sp('x-',axX); end Y = reshape(D(channels(c), :, :, :), nf, nt); Y = spm_filter(K, W*Y'); B = axX.pX*Y; for i = 1:ng xY = B(ne*nr+i, :); if isTF Dr(c, :, :, i) = xY; else Dr(c, :, i) = xY; end end if ismember(c, Ibar), spm_progress_bar('Set', c); end end spm_progress_bar('Clear'); %-Save %-------------------------------------------------------------------------- save(Dr); else Dr = []; end if ~isempty(Dout) D = Dout; else D = Dr; end