PageRenderTime 32ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/trunk/examples/toolboxes/fieldtrip/ft_sourceanalysis.m

http://brainstream.googlecode.com/
MATLAB | 1036 lines | 681 code | 66 blank | 289 comment | 155 complexity | 3f91b42bfb24d0e0396d7ccaad63d443 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, LGPL-2.0, GPL-3.0, GPL-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
  1. function [source] = ft_sourceanalysis(cfg, data, baseline);
  2. % FT_SOURCEANALYSIS performs beamformer dipole analysis on EEG or MEG data
  3. % after preprocessing and a timelocked or frequency analysis
  4. %
  5. % Use as either
  6. % [source] = ft_sourceanalysis(cfg, freq)
  7. % [source] = ft_sourceanalysis(cfg, timelock)
  8. %
  9. % where the data in freq or timelock should be organised in a structure
  10. % as obtained from the FT_FREQANALYSIS or FT_TIMELOCKANALYSIS function. The
  11. % configuration "cfg" is a structure containing information about
  12. % source positions and other options.
  13. %
  14. % The different source reconstruction algorithms that are implemented
  15. % are
  16. % cfg.method = 'lcmv' linear constrained minimum variance beamformer
  17. % 'sam' synthetic aperture magnetometry
  18. % 'dics' dynamic imaging of coherent sources
  19. % 'pcc' partial cannonical correlation/coherence
  20. % 'mne' minimum norm estimation
  21. % 'loreta' minimum norm estimation with smoothness constraint
  22. % 'rv' scan residual variance with single dipole
  23. % 'music' multiple signal classification
  24. % 'mvl' multivariate Laplace source localization
  25. % The DICS and PCC methods are for frequency domain data, all other methods
  26. % are for time domain data.
  27. %
  28. % The positions of the sources can be specified as a regular 3-D
  29. % grid that is aligned with the axes of the head coordinate system
  30. % cfg.grid.xgrid = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
  31. % cfg.grid.ygrid = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
  32. % cfg.grid.zgrid = vector (e.g. 0:1:20) or 'auto' (default = 'auto')
  33. % cfg.grid.resolution = number (e.g. 1 cm) for automatic grid generation
  34. % Alternatively the position of a few sources at locations of interest can
  35. % be specified, for example obtained from an anatomical or functional MRI
  36. % cfg.grid.pos = Nx3 matrix with position of each source
  37. % cfg.grid.dim = [Nx Ny Nz] vector with dimensions in case of 3-D grid (optional)
  38. % cfg.grid.inside = vector with indices of the sources inside the brain (optional)
  39. % cfg.grid.outside = vector with indices of the sources outside the brain (optional)
  40. % You can also use the FT_PREPARE_LEADFIELD function to create a grid with
  41. % dipole positions and with precomputed leadfields.
  42. %
  43. % The following strategies are supported to obtain statistics for the source parameters using
  44. % multiple trials in the data, either directly or through a resampling-based approach
  45. % cfg.singletrial = 'no' or 'yes' construct filter from average, apply to single trials
  46. % cfg.rawtrial = 'no' or 'yes' construct filter from single trials, apply to single trials
  47. % cfg.jackknife = 'no' or 'yes' jackknife resampling of trials
  48. % cfg.pseudovalue = 'no' or 'yes' pseudovalue resampling of trials
  49. % cfg.bootstrap = 'no' or 'yes' bootstrap resampling of trials
  50. % cfg.numbootstrap = number of bootstrap replications (e.g. number of original trials)
  51. % If none of these options is specified, the average over the trials will
  52. % be computed prior to computing the source reconstruction.
  53. %
  54. % To obtain statistics over the source parameters between two conditions, you
  55. % can also use a resampling procedure that reshuffles the trials over both
  56. % conditions. In that case, you should call the function with two datasets
  57. % containing single trial data like
  58. % [source] = ft_sourceanalysis(cfg, freqA, freqB)
  59. % [source] = ft_sourceanalysis(cfg, timelockA, timelockB)
  60. % and you should specify
  61. % cfg.randomization = 'no' or 'yes'
  62. % cfg.permutation = 'no' or 'yes'
  63. % cfg.numrandomization = number, e.g. 500
  64. % cfg.numpermutation = number, e.g. 500 or 'all'
  65. %
  66. % You should specify the volume conductor model with
  67. % cfg.hdmfile = string, file containing the volume conduction model
  68. % or alternatively
  69. % cfg.vol = structure with volume conduction model
  70. %
  71. % If the sensor information is not contained in the data itself you should
  72. % also specify the sensor information using
  73. % cfg.gradfile = string, file containing the gradiometer definition
  74. % cfg.elecfile = string, file containing the electrode definition
  75. % or alternatively
  76. % cfg.grad = structure with gradiometer definition
  77. % cfg.elec = structure with electrode definition
  78. %
  79. % If you have not specified a grid with pre-computed leadfields,
  80. % the leadfield for each grid location will be computed on the fly.
  81. % In that case you can modify the leadfields by reducing the rank
  82. % (i.e. remove the weakest orientation), or by normalizing each
  83. % column.
  84. % cfg.reducerank = 'no', or number (default = 3 for EEG, 2 for MEG)
  85. % cfg.normalize = 'no' or 'yes' (default = 'no')
  86. %
  87. % Other configuration options are
  88. % cfg.channel = Nx1 cell-array with selection of channels (default = 'all'),
  89. % see FT_CHANNELSELECTION for details
  90. % cfg.frequency = single number (in Hz)
  91. % cfg.latency = single number in seconds, for time-frequency analysis
  92. % cfg.lambda = number or empty for automatic default
  93. % cfg.refchan = reference channel label (for coherence)
  94. % cfg.refdip = reference dipole location (for coherence)
  95. % cfg.supchan = suppressed channel label(s)
  96. % cfg.supdip = suppressed dipole location(s)
  97. % cfg.keeptrials = 'no' or 'yes'
  98. % cfg.keepleadfield = 'no' or 'yes'
  99. % cfg.projectnoise = 'no' or 'yes'
  100. % cfg.keepfilter = 'no' or 'yes'
  101. % cfg.keepcsd = 'no' or 'yes'
  102. % cfg.keepmom = 'no' or 'yes'
  103. % cfg.feedback = 'no', 'text', 'textbar', 'gui' (default = 'text')
  104. %
  105. % See also FT_SOURCEDESCRIPTIVES, FT_SOURCESTATISTICS, FT_PREPARE_LEADFIELD
  106. % Undocumented local options:
  107. % cfg.numcomponents
  108. % cfg.refchannel
  109. % cfg.trialweight = 'equal' or 'proportional'
  110. % cfg.powmethod = 'lambda1' or 'trace'
  111. % cfg.inputfile = one can specifiy preanalysed saved data as input
  112. % cfg.outputfile = one can specify output as file to save to disk
  113. %
  114. % This function depends on FT_PREPARE_DIPOLE_GRID which has the following options:
  115. % cfg.grid.xgrid (default set in FT_PREPARE_DIPOLE_GRID: cfg.grid.xgrid = 'auto'), documented
  116. % cfg.grid.ygrid (default set in FT_PREPARE_DIPOLE_GRID: cfg.grid.ygrid = 'auto'), documented
  117. % cfg.grid.zgrid (default set in FT_PREPARE_DIPOLE_GRID: cfg.grid.zgrid = 'auto'), documented
  118. % cfg.grid.resolution, documented
  119. % cfg.grid.pos, documented
  120. % cfg.grid.dim, documented
  121. % cfg.grid.inside, documented
  122. % cfg.grid.outside, documented
  123. % cfg.mri
  124. % cfg.mriunits
  125. % cfg.smooth
  126. % cfg.sourceunits
  127. % cfg.threshold
  128. % cfg.symmetry
  129. %
  130. % This function depends on FT_PREPARE_FREQ_MATRICES which has the following options:
  131. % cfg.channel (default set in FT_SOURCEANALYSIS: cfg.channel = 'all'), documented
  132. % cfg.dicsfix
  133. % cfg.frequency, documented
  134. % cfg.latency, documented
  135. % cfg.refchan, documented
  136. %
  137. % This function depends on FT_PREPARE_RESAMPLED_DATA which has the following options:
  138. % cfg.jackknife (default set in FT_SOURCEANALYSIS: cfg.jackknife = 'no'), documented
  139. % cfg.numbootstrap, documented
  140. % cfg.numcondition (set in FT_SOURCEANALYSIS: cfg.numcondition = 2)
  141. % cfg.numpermutation (default set in FT_SOURCEANALYSIS: cfg.numpermutation = 100), documented
  142. % cfg.numrandomization (default set in FT_SOURCEANALYSIS: cfg.numrandomization = 100), documented
  143. % cfg.permutation (default set in FT_SOURCEANALYSIS: cfg.permutation = 'no'), documented
  144. % cfg.pseudovalue (default set in FT_SOURCEANALYSIS: cfg.pseudovalue = 'no'), documented
  145. % cfg.randomization (default set in FT_SOURCEANALYSIS: cfg.randomization = 'no'), documented
  146. %
  147. % This function depends on FT_PREPARE_VOL_SENS which has the following options:
  148. % cfg.channel, (default set in FT_SOURCEANALYSIS: cfg.channel = 'all'), documented
  149. % cfg.elec, documented
  150. % cfg.elecfile, documented
  151. % cfg.grad, documented
  152. % cfg.gradfile, documented
  153. % cfg.hdmfile, documented
  154. % cfg.order
  155. % cfg.vol, documented
  156. %
  157. % This function depends on FT_PREPARE_LEADFIELD which has the following options:
  158. % cfg.feedback, (default set in FT_SOURCEANALYSIS: cfg.feedback = 'text'), documented
  159. % cfg.grid, documented
  160. % cfg.lbex
  161. % cfg.normalize (default set in FT_SOURCEANALYSIS), documented
  162. % cfg.previous
  163. % cfg.reducerank (default set in FT_SOURCEANALYSIS), documented
  164. % cfg.sel50p
  165. % cfg.version
  166. % Copyright (c) 2003-2008, Robert Oostenveld, F.C. Donders Centre
  167. %
  168. % This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
  169. % for the documentation and details.
  170. %
  171. % FieldTrip is free software: you can redistribute it and/or modify
  172. % it under the terms of the GNU General Public License as published by
  173. % the Free Software Foundation, either version 3 of the License, or
  174. % (at your option) any later version.
  175. %
  176. % FieldTrip is distributed in the hope that it will be useful,
  177. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  178. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  179. % GNU General Public License for more details.
  180. %
  181. % You should have received a copy of the GNU General Public License
  182. % along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
  183. %
  184. % $Id: ft_sourceanalysis.m 1258 2010-06-22 08:33:48Z timeng $
  185. fieldtripdefs
  186. % set a timer to determine how long the sourceanalysis takes in total
  187. tic;
  188. cfg = checkconfig(cfg, 'trackconfig', 'on');
  189. % set defaults for optional cfg.inputfile, cfg.outputfile
  190. if ~isfield(cfg, 'inputfile'), cfg.inputfile = []; end
  191. if ~isfield(cfg, 'outputfile'), cfg.outputfile = []; end
  192. % load optional given inputfile as data
  193. hasdata = (nargin>1);
  194. if ~isempty(cfg.inputfile)
  195. % the input data should be read from file
  196. if hasdata
  197. error('cfg.inputfile should not be used in conjunction with giving input data to this function');
  198. else
  199. data = loadvar(cfg.inputfile, 'data');
  200. end
  201. end
  202. % check if the input data is valid for this function
  203. data = checkdata(data, 'datatype', {'timelock', 'freq', 'comp'}, 'feedback', 'yes');
  204. if nargin>2
  205. baseline = checkdata(baseline, 'datatype', {'timelock', 'freq', 'comp'}, 'feedback', 'yes');
  206. end
  207. % check if the input cfg is valid for this function
  208. cfg = checkconfig(cfg, 'renamed', {'jacknife', 'jackknife'});
  209. cfg = checkconfig(cfg, 'renamed', {'refchannel', 'refchan'});
  210. cfg = checkconfig(cfg, 'renamedval', {'method', 'power', 'dics'});
  211. cfg = checkconfig(cfg, 'renamedval', {'method', 'coh_refchan', 'dics'});
  212. cfg = checkconfig(cfg, 'renamedval', {'method', 'coh_refdip', 'dics'});
  213. cfg = checkconfig(cfg, 'renamedval', {'method', 'dics_cohrefchan', 'dics'});
  214. cfg = checkconfig(cfg, 'renamedval', {'method', 'dics_cohrefdip', 'dics'});
  215. cfg = checkconfig(cfg, 'forbidden', {'parallel'});
  216. % determine the type of input data
  217. if isfield(data, 'freq')
  218. isfreq = 1;
  219. iscomp = 0;
  220. istimelock = 0;
  221. elseif isfield(data, 'topo')
  222. isfreq = 0;
  223. iscomp = 1;
  224. istimelock = 0;
  225. elseif isfield(data, 'time')
  226. iscomp = 0;
  227. isfreq = 0;
  228. istimelock = 1;
  229. else
  230. error('input data is not recognized');
  231. end
  232. % set the defaults
  233. if ~isfield(cfg, 'method') && istimelock, cfg.method = 'lcmv'; end
  234. if ~isfield(cfg, 'method') && isfreq, cfg.method = 'dics'; end
  235. if ~isfield(cfg, 'keeptrials') cfg.keeptrials = 'no'; end
  236. if ~isfield(cfg, 'keepfilter') cfg.keepfilter = 'no'; end
  237. if ~isfield(cfg, 'keepleadfield') cfg.keepleadfield = 'no'; end
  238. if ~isfield(cfg, 'keepcsd') cfg.keepcsd = 'no'; end
  239. if ~isfield(cfg, 'keepmom') cfg.keepmom = 'yes'; end
  240. if ~isfield(cfg, 'projectnoise') cfg.projectnoise = 'no'; end
  241. if ~isfield(cfg, 'trialweight') cfg.trialweight = 'equal'; end
  242. if ~isfield(cfg, 'jackknife'), cfg.jackknife = 'no'; end
  243. if ~isfield(cfg, 'pseudovalue'), cfg.pseudovalue = 'no'; end
  244. if ~isfield(cfg, 'bootstrap'), cfg.bootstrap = 'no'; end
  245. if ~isfield(cfg, 'singletrial'), cfg.singletrial = 'no'; end
  246. if ~isfield(cfg, 'rawtrial'), cfg.rawtrial = 'no'; end
  247. if ~isfield(cfg, 'randomization'), cfg.randomization = 'no'; end
  248. if ~isfield(cfg, 'numrandomization'), cfg.numrandomization = 100; end
  249. if ~isfield(cfg, 'permutation'), cfg.permutation = 'no'; end
  250. if ~isfield(cfg, 'numpermutation'), cfg.numpermutation = 100; end
  251. if ~isfield(cfg, 'wakewulf'), cfg.wakewulf = 'yes'; end
  252. if ~isfield(cfg, 'killwulf'), cfg.killwulf = 'yes'; end
  253. if ~isfield(cfg, 'feedback'), cfg.feedback = 'text'; end
  254. if ~isfield(cfg, 'supdip'), cfg.supdip = []; end
  255. if ~isfield(cfg, 'lambda'), cfg.lambda = []; end
  256. if ~isfield(cfg, 'powmethod'), cfg.powmethod = []; end
  257. if ~isfield(cfg, 'channel'), cfg.channel = 'all'; end
  258. if ~isfield(cfg, 'normalize'), cfg.normalize = 'no'; end
  259. if ~isfield(cfg, 'prewhiten'), cfg.prewhiten = 'no'; end
  260. % if ~isfield(cfg, 'reducerank'), cfg.reducerank = 'no'; end % the default for this depends on EEG/MEG and is set below
  261. % put the low-level options pertaining to the source reconstruction method in their own field
  262. % put the low-level options pertaining to the dipole grid in their own field
  263. cfg = checkconfig(cfg, 'createsubcfg', {cfg.method, 'grid'});
  264. convertfreq = 0;
  265. convertcomp = 0;
  266. if ~istimelock && (strcmp(cfg.method, 'mne') || strcmp(cfg.method, 'loreta') || strcmp(cfg.method, 'rv') || strcmp(cfg.method, 'music'))
  267. % these timelock methods are also supported for frequency or component data
  268. if isfreq
  269. [data, cfg] = freq2timelock(cfg, data);
  270. convertfreq = 1; % flag indicating that the data was converted
  271. elseif iscomp
  272. [data, cfg] = comp2timelock(cfg, data);
  273. convertcomp = 1; % flag indicating that the data was converted
  274. end
  275. istimelock = 1; % from now on the data can be treated as timelocked
  276. isfreq = 0;
  277. iscomp = 0;
  278. end
  279. % select only those channels that are present in the data
  280. cfg.channel = ft_channelselection(cfg.channel, data.label);
  281. if nargin>2 && (strcmp(cfg.randomization, 'no') && strcmp(cfg.permutation, 'no') && strcmp(cfg.prewhiten, 'no'))
  282. error('input of two conditions only makes sense if you want to randomize or permute, or if you want to prewhiten');
  283. elseif nargin<3 && (strcmp(cfg.randomization, 'yes') || strcmp(cfg.permutation, 'yes'))
  284. error('randomization or permutation requires that you give two conditions as input');
  285. end
  286. if isfield(cfg, 'latency') && istimelock
  287. error('specification of cfg.latency is only required for time-frequency data');
  288. end
  289. if sum([strcmp(cfg.jackknife, 'yes'), strcmp(cfg.bootstrap, 'yes'), strcmp(cfg.pseudovalue, 'yes'), strcmp(cfg.singletrial, 'yes'), strcmp(cfg.rawtrial, 'yes'), strcmp(cfg.randomization, 'yes'), strcmp(cfg.permutation, 'yes')])>1
  290. error('jackknife, bootstrap, pseudovalue, singletrial, rawtrial, randomization and permutation are mutually exclusive');
  291. end
  292. if isfreq
  293. if ~strcmp(data.dimord, 'chan_freq') && ...
  294. ~strcmp(data.dimord, 'chan_freq_time') && ...
  295. ~strcmp(data.dimord, 'rpt_chan_freq') && ...
  296. ~strcmp(data.dimord, 'rpt_chan_freq_time') && ...
  297. ~strcmp(data.dimord, 'rpttap_chan_freq') && ...
  298. ~strcmp(data.dimord, 'rpttap_chan_freq_time')
  299. error('dimord of input frequency data is not recognized');
  300. end
  301. end
  302. % collect and preprocess the electrodes/gradiometer and head model
  303. [vol, sens, cfg] = prepare_headmodel(cfg, data);
  304. % It might be that the number of channels in the data, the number of
  305. % channels in the electrode/gradiometer definition and the number of
  306. % channels in the multisphere volume conduction model are different.
  307. % Hence a subset of the data channels will be used.
  308. Nchans = length(cfg.channel);
  309. % set the default for reducing the rank of the leadfields, this is an
  310. % option to the specific method and will be passed on to the low-level
  311. % function
  312. if ~isfield(cfg.(cfg.method), 'reducerank')
  313. if ft_senstype(sens, 'meg')
  314. cfg.(cfg.method).reducerank = 2;
  315. else
  316. cfg.(cfg.method).reducerank = 3;
  317. end
  318. end
  319. if strcmp(cfg.keepleadfield, 'yes') && (~isfield(cfg, 'grid') || ~isfield(cfg.grid, 'leadfield'))
  320. % precompute the leadfields upon the users request
  321. fprintf('precomputing leadfields\n');
  322. [grid, cfg] = ft_prepare_leadfield(cfg, data);
  323. elseif (strcmp(cfg.permutation, 'yes') || ...
  324. strcmp(cfg.randomization, 'yes') || ...
  325. strcmp(cfg.bootstrap, 'yes') || ...
  326. strcmp(cfg.jackknife, 'yes') || ...
  327. strcmp(cfg.pseudovalue, 'yes') || ...
  328. strcmp(cfg.singletrial, 'yes') || ...
  329. strcmp(cfg.rawtrial, 'yes')) && (~isfield(cfg, 'grid') || ~isfield(cfg.grid, 'leadfield'))
  330. % also precompute the leadfields if multiple trials have to be processed
  331. fprintf('precomputing leadfields for efficient handling of multiple trials\n');
  332. [grid, cfg] = ft_prepare_leadfield(cfg, data);
  333. else
  334. % only prepare the grid positions, the leadfield will be computed on the fly if not present
  335. [grid, cfg] = prepare_dipole_grid(cfg, vol, sens);
  336. end
  337. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  338. % do frequency domain source reconstruction
  339. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  340. if isfreq && any(strcmp(cfg.method, {'dics', 'pcc'}))
  341. if strcmp(cfg.method, 'pcc')
  342. % HACK: requires some extra defaults
  343. if ~isfield(cfg, 'refdip'), cfg.refdip = []; end
  344. if ~isfield(cfg, 'supdip'), cfg.supdip = []; end
  345. if ~isfield(cfg, 'refchan'), cfg.refchan = []; end
  346. if ~isfield(cfg, 'supchan'), cfg.supchan = []; end
  347. cfg.refchan = ft_channelselection(cfg.refchan, data.label);
  348. cfg.supchan = ft_channelselection(cfg.supchan, data.label);
  349. % HACK: use some experimental code
  350. if nargin>2 && strcmp(cfg.prewhiten, 'no'),
  351. error('not supported')
  352. end
  353. tmpcfg = cfg;
  354. tmpcfg.refchan = ''; % prepare_freq_matrices should not know explicitly about the refchan
  355. tmpcfg.channel = cfg.channel(:)';
  356. if isfield(cfg, 'refchan')
  357. % add the refchan implicitely
  358. tmpcfg.channel = [tmpcfg.channel cfg.refchan(:)'];
  359. end
  360. if isfield(cfg, 'supchan')
  361. % add the supchan implicitely
  362. tmpcfg.channel = [tmpcfg.channel cfg.supchan(:)'];
  363. end
  364. % select the data in the channels and the frequency of interest
  365. [Cf, Cr, Pr, Ntrials, tmpcfg] = prepare_freq_matrices(tmpcfg, data);
  366. if strcmp(cfg.prewhiten, 'yes'),
  367. [Cfb, Crb, Prb, Ntrialsb, tmpcfgb] = prepare_freq_matrices(tmpcfg, baseline);
  368. Cf = prewhitening_filter2(squeeze(mean(Cf,1)), squeeze(mean(Cfb,1)));
  369. Ntrials = 1;
  370. end
  371. if isfield(cfg, 'refchan') && ~isempty(cfg.refchan)
  372. [dum, refchanindx] = match_str(cfg.refchan, tmpcfg.channel);
  373. else
  374. refchanindx = [];
  375. end
  376. if isfield(cfg, 'supchan') && ~isempty(cfg.supchan)
  377. [dum, supchanindx] = match_str(cfg.supchan,tmpcfg.channel);
  378. else
  379. supchanindx = [];
  380. end
  381. Nchans = length(tmpcfg.channel); % update the number of channels
  382. % if the input data has a complete fourier spectrum, project it through the filters
  383. % FIXME it was incorrect , since the
  384. % ' leads to a conjugate transposition check this in beamformer_pcc
  385. if isfield(data, 'fourierspctrm')
  386. [dum, datchanindx] = match_str(tmpcfg.channel, data.label);
  387. fbin = nearest(data.freq, cfg.frequency);
  388. if strcmp(data.dimord, 'chan_freq')
  389. avg = data.fourierspctrm(datchanindx, fbin);
  390. elseif strcmp(data.dimord, 'rpt_chan_freq') || strcmp(data.dimord, 'rpttap_chan_freq'),
  391. %avg = data.fourierspctrm(:, datchanindx, fbin)';
  392. avg = transpose(data.fourierspctrm(:, datchanindx, fbin));
  393. elseif strcmp(data.dimord, 'chan_freq_time')
  394. tbin = nearest(data.time, cfg.latency);
  395. avg = data.fourierspctrm(datchanindx, fbin, tbin);
  396. elseif strcmp(data.dimord, 'rpt_chan_freq_time') || strcmp(data.dimord, 'rpttap_chan_freq_time'),
  397. tbin = nearest(data.time, cfg.latency);
  398. %avg = data.fourierspctrm(:, datchanindx, fbin, tbin)';
  399. avg = transpose(data.fourierspctrm(:, datchanindx, fbin, tbin));
  400. end
  401. else
  402. avg = [];
  403. end
  404. else
  405. % HACK: use the default code
  406. % convert the input data, so that Cf, Cr and Pr contain either the average over all trials (if Ntrials==1)
  407. % or the individual cross-spectral-densities/powers of each individual trial (if Ntrials>1)
  408. [Cf, Cr, Pr, Ntrials, cfg] = prepare_freq_matrices(cfg, data);
  409. end
  410. if strcmp(cfg.method, 'dics')
  411. % assign a descriptive name to each of the dics sub-methods, the default is power only
  412. if strcmp(cfg.method, 'dics') && isfield(cfg, 'refdip') && ~isempty(cfg.refdip);
  413. submethod = 'dics_refdip';
  414. elseif strcmp(cfg.method, 'dics') && isfield(cfg, 'refchan') && ~isempty(cfg.refchan);
  415. submethod = 'dics_refchan';
  416. else
  417. submethod = 'dics_power';
  418. end
  419. end
  420. % fill these with NaNs, so that I dont have to treat them separately
  421. if isempty(Cr), Cr = nan*zeros(Ntrials, Nchans, 1); end
  422. if isempty(Pr), Pr = nan*zeros(Ntrials, 1, 1); end
  423. if nargin>2
  424. % repeat the conversion for the baseline condition
  425. [bCf, bCr, bPr, Nbaseline, cfg] = prepare_freq_matrices(cfg, baseline);
  426. % fill these with NaNs, so that I dont have to treat them separately
  427. if isempty(bCr), bCr = nan*zeros(Nbaseline, Nchans, 1); end
  428. if isempty(bPr), bPr = nan*zeros(Nbaseline, 1, 1); end
  429. % rename the active condition for convenience
  430. aCf = Cf;
  431. aCr = Cr;
  432. aPr = Pr;
  433. % this is required for averaging 2 conditions using prepare_resampled_data
  434. cfg2 = [];
  435. cfg2.numcondition = 2;
  436. % this is required for randomizing/permuting 2 conditions using prepare_resampled_data
  437. cfg.numcondition = 2;
  438. end
  439. % prepare the resampling of the trials, or average the data if multiple trials are present and no resampling is neccessary
  440. if (Ntrials<=1) && (strcmp(cfg.jackknife, 'yes') || strcmp(cfg.bootstrap, 'yes') || strcmp(cfg.pseudovalue, 'yes') || strcmp(cfg.singletrial, 'yes') || strcmp(cfg.rawtrial, 'yes') || strcmp(cfg.randomization, 'yes') || strcmp(cfg.permutation, 'yes'))
  441. error('multiple trials required in the data\n');
  442. elseif strcmp(cfg.permutation, 'yes')
  443. % compute the cross-spectral density matrix without resampling
  444. [dum, avg_aCf, avg_aCr, avg_aPr, avg_bCf, avg_bCr, avg_bPr] = prepare_resampled_data(cfg2 , aCf, aCr, aPr, bCf, bCr, bPr);
  445. % compute the cross-spectral density matrix with random permutation
  446. [dum, rnd_aCf, rnd_aCr, rnd_aPr, rnd_bCf, rnd_bCr, rnd_bPr] = prepare_resampled_data(cfg, aCf, aCr, aPr, bCf, bCr, bPr);
  447. % concatenate the different resamplings
  448. Cf = cat(1, reshape(avg_aCf, [1 Nchans Nchans]), reshape(avg_bCf, [1 Nchans Nchans]), rnd_aCf, rnd_bCf);
  449. Cr = cat(1, reshape(avg_aCr, [1 Nchans 1 ]), reshape(avg_bCr, [1 Nchans 1 ]), rnd_aCr, rnd_bCr);
  450. Pr = cat(1, reshape(avg_aPr, [1 1 1 ]), reshape(avg_bPr, [1 1 1 ]), rnd_aPr, rnd_bPr);
  451. % clear temporary working copies
  452. clear avg_aCf avg_aCr avg_aPr avg_bCf avg_bCr avg_bPr
  453. clear rnd_aCf rnd_aCr rnd_aPr rnd_bCf rnd_bCr rnd_bPr
  454. % the order of the resamplings should be [avgA avgB rndA rndB rndA rndB rndA rndB ....]
  455. Nrepetitions = 2*cfg.numpermutation + 2;
  456. order = [1 2 3:2:Nrepetitions 4:2:Nrepetitions];
  457. Cf = Cf(order,:,:);
  458. Cr = Cr(order,:,:);
  459. Pr = Pr(order,:,:);
  460. elseif strcmp(cfg.randomization, 'yes')
  461. % compute the cross-spectral density matrix without resampling
  462. [dum, avg_aCf, avg_aCr, avg_aPr, avg_bCf, avg_bCr, avg_bPr] = prepare_resampled_data(cfg2 , aCf, aCr, aPr, bCf, bCr, bPr);
  463. % compute the cross-spectral density matrix with random resampling
  464. [dum, rnd_aCf, rnd_aCr, rnd_aPr, rnd_bCf, rnd_bCr, rnd_bPr] = prepare_resampled_data(cfg, aCf, aCr, aPr, bCf, bCr, bPr);
  465. % concatenate the different resamplings
  466. Cf = cat(1, reshape(avg_aCf, [1 Nchans Nchans]), reshape(avg_bCf, [1 Nchans Nchans]), rnd_aCf, rnd_bCf);
  467. Cr = cat(1, reshape(avg_aCr, [1 Nchans 1 ]), reshape(avg_bCr, [1 Nchans 1 ]), rnd_aCr, rnd_bCr);
  468. Pr = cat(1, reshape(avg_aPr, [1 1 1 ]), reshape(avg_bPr, [1 1 1 ]), rnd_aPr, rnd_bPr);
  469. % clear temporary working copies
  470. clear avg_aCf avg_aCr avg_aPr avg_bCf avg_bCr avg_bPr
  471. clear rnd_aCf rnd_aCr rnd_aPr rnd_bCf rnd_bCr rnd_bPr
  472. % the order of the resamplings should be [avgA avgB rndA rndB rndA rndB rndA rndB ....]
  473. Nrepetitions = 2*cfg.numrandomization + 2;
  474. order = [1 2 3:2:Nrepetitions 4:2:Nrepetitions];
  475. Cf = Cf(order,:,:);
  476. Cr = Cr(order,:,:);
  477. Pr = Pr(order,:,:);
  478. elseif strcmp(cfg.jackknife, 'yes')
  479. % compute the cross-spectral density matrix with jackknife resampling
  480. [cfg, Cf, Cr, Pr] = prepare_resampled_data(cfg, Cf, Cr, Pr);
  481. Nrepetitions = Ntrials;
  482. elseif strcmp(cfg.bootstrap, 'yes')
  483. % compute the cross-spectral density matrix with bootstrap resampling
  484. [cfg, Cf, Cr, Pr] = prepare_resampled_data(cfg, Cf, Cr, Pr);
  485. Nrepetitions = cfg.numbootstrap;
  486. elseif strcmp(cfg.pseudovalue, 'yes')
  487. % compute the cross-spectral density matrix with pseudovalue resampling
  488. [cfg, Cf, Cr, Pr] = prepare_resampled_data(cfg, Cf, Cr, Pr);
  489. Nrepetitions = Ntrials+1;
  490. elseif strcmp(cfg.singletrial, 'yes')
  491. % The idea is that beamformer uses the average covariance to construct the
  492. % filter and applies it to the single trial covariance/csd. The problem
  493. % is that beamformer will use the averaged covariance/csd to estimate the
  494. % power and not the single trial covariance/csd
  495. error('this option contains a bug, and is therefore not supported at the moment');
  496. Cf = Cf; % FIXME, should be averaged and repeated for each trial
  497. Cr = Cr; % FIXME, should be averaged and repeated for each trial
  498. Pr = Pr; % FIXME, should be averaged and repeated for each trial
  499. Nrepetitions = Ntrials;
  500. elseif strcmp(cfg.rawtrial, 'yes')
  501. % keep all the individual trials, do not average them
  502. Cf = Cf;
  503. Cr = Cr;
  504. Pr = Pr;
  505. Nrepetitions = Ntrials;
  506. elseif Ntrials>1
  507. % compute the average from the individual trials
  508. Cf = reshape(sum(Cf, 1) / Ntrials, [Nchans Nchans]);
  509. Cr = reshape(sum(Cr, 1) / Ntrials, [Nchans 1]);
  510. Pr = reshape(sum(Pr, 1) / Ntrials, [1 1]);
  511. Nrepetitions = 1;
  512. elseif Ntrials==1
  513. % no rearrangement of trials is neccesary, the data already represents the average
  514. Cf = Cf;
  515. Cr = Cr;
  516. Pr = Pr;
  517. Nrepetitions = 1;
  518. end
  519. % reshape so that it also looks like one trial (out of many)
  520. if Nrepetitions==1
  521. Cf = reshape(Cf , [1 Nchans Nchans]);
  522. Cr = reshape(Cr , [1 Nchans 1]);
  523. Pr = reshape(Pr , [1 1 1]);
  524. end
  525. % get the relevant low level options from the cfg and convert into key-value pairs
  526. optarg = cfg2keyval(getfield(cfg, cfg.method));
  527. for i=1:Nrepetitions
  528. fprintf('scanning repetition %d\n', i);
  529. if strcmp(cfg.method, 'dics') && strcmp(submethod, 'dics_power')
  530. dip(i) = beamformer_dics(grid, sens, vol, [], squeeze(Cf(i,:,:)), optarg{:});
  531. elseif strcmp(cfg.method, 'dics') && strcmp(submethod, 'dics_refchan')
  532. dip(i) = beamformer_dics(grid, sens, vol, [], squeeze(Cf(i,:,:)), optarg{:}, 'Cr', Cr(i,:), 'Pr', Pr(i));
  533. elseif strcmp(cfg.method, 'dics') && strcmp(submethod, 'dics_refdip')
  534. dip(i) = beamformer_dics(grid, sens, vol, [], squeeze(Cf(i,:,:)), optarg{:}, 'refdip', cfg.refdip);
  535. elseif strcmp(cfg.method, 'pcc')
  536. dip(i) = beamformer_pcc(grid, sens, vol, avg, squeeze(Cf(i,:,:)), optarg{:}, 'refdip', cfg.refdip, 'refchan', refchanindx, 'supdip', cfg.supdip, 'supchan', supchanindx);
  537. else
  538. error(sprintf('method ''%s'' is unsupported for source reconstruction in the frequency domain', cfg.method));
  539. end
  540. end
  541. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  542. % do time domain source reconstruction
  543. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  544. elseif istimelock && any(strcmp(cfg.method, {'lcmv', 'sam', 'mne', 'loreta', 'rv', 'music', 'pcc', 'mvl'}))
  545. % determine the size of the data
  546. Nsamples = size(data.avg,2);
  547. Nchans = length(data.label);
  548. if isfield(data, 'cov') && length(size(data.cov))==3
  549. Ntrials = size(data.cov,1);
  550. elseif isfield(data, 'trial') && length(size(data.trial))==3
  551. Ntrials = size(data.trial,1);
  552. else
  553. Ntrials = 1;
  554. end
  555. if isfield(data, 'cov')
  556. % use the estimated data covariance matrix
  557. hascovariance = 1;
  558. else
  559. % add a identity covariance matrix, this simplifies the handling of the different source reconstruction methods
  560. % since the covariance is only used by some reconstruction methods and might not allways be present in the data
  561. if Ntrials==1
  562. data.cov = eye(Nchans);
  563. else
  564. data.cov = zeros(Ntrials,Nchans,Nchans);
  565. for i=1:Ntrials
  566. data.cov(i,:,:) = eye(Nchans);
  567. end
  568. end
  569. hascovariance = 0;
  570. end
  571. if strcmp(cfg.method, 'pcc')
  572. % HACK: requires some extra defaults
  573. if ~isfield(cfg, 'refdip'), cfg.refdip = []; end
  574. if ~isfield(cfg, 'supdip'), cfg.supdip = []; end
  575. % HACK: experimental code
  576. if nargin>2
  577. error('not supported')
  578. end
  579. tmpcfg = [];
  580. tmpcfg.channel = cfg.channel(:)';
  581. if isfield(cfg, 'refchan')
  582. tmpcfg.channel = [tmpcfg.channel cfg.refchan(:)'];
  583. end
  584. if isfield(cfg, 'supchan')
  585. tmpcfg.channel = [tmpcfg.channel cfg.supchan(:)'];
  586. end
  587. % select the data in the channels of interest
  588. [dum, datchanindx] = match_str(tmpcfg.channel, data.label);
  589. if Ntrials==1
  590. data.avg = data.avg(datchanindx,:);
  591. data.cov = data.cov(datchanindx,datchanindx);
  592. else
  593. data.avg = data.avg(datchanindx,:);
  594. data.cov = data.cov(:,datchanindx,datchanindx);
  595. data.trial = data.trial(:,datchanindx,:);
  596. end
  597. data.label = data.label(datchanindx);
  598. if isfield(cfg, 'refchan') && ~isempty(cfg.refchan)
  599. [dum, refchanindx] = match_str(cfg.refchan, data.label);
  600. else
  601. refchanindx = [];
  602. end
  603. if isfield(cfg, 'supchan') && ~isempty(cfg.supchan)
  604. [dum, supchanindx] = match_str(cfg.supchan, data.label);
  605. else
  606. supchanindx = [];
  607. end
  608. Nchans = length(tmpcfg.channel); % update the number of channels
  609. else
  610. % HACK: use the default code
  611. % select the channels of interest
  612. [dum, datchanindx] = match_str(cfg.channel, data.label);
  613. if strcmp(data.dimord, 'chan_time')
  614. % It is in principle possible to have timelockanalysis with
  615. % keeptrial=yes and only a single trial in the raw data.
  616. % In that case the covariance should be represented as Nchan*Nchan
  617. data.avg = data.avg(datchanindx,:);
  618. data.cov = reshape(data.cov, length(datchanindx), length(datchanindx));
  619. data.cov = data.cov(datchanindx,datchanindx);
  620. else
  621. data.avg = data.avg(datchanindx,:);
  622. data.cov = data.cov(:,datchanindx,datchanindx);
  623. data.trial = data.trial(:,datchanindx,:);
  624. end
  625. data.label = data.label(datchanindx);
  626. Nchans = length(data.label);
  627. end
  628. if nargin>2
  629. % baseline and active are only available together for resampling purposes,
  630. % hence I assume here that there are multiple trials in both
  631. baseline.avg = baseline.avg(datchanindx,:);
  632. baseline.cov = baseline.cov(:,datchanindx,datchanindx);
  633. baseline.trial = baseline.trial(:,datchanindx,:);
  634. % this is required for averaging 2 conditions using prepare_resampled_data
  635. cfg2 = [];
  636. cfg2.numcondition = 2;
  637. end
  638. % prepare the resampling of the trials, or average the data if multiple trials are present and no resampling is neccessary
  639. if (strcmp(cfg.jackknife, 'yes') || strcmp(cfg.bootstrap, 'yes') || strcmp(cfg.pseudovalue, 'yes') || strcmp(cfg.singletrial, 'yes') || strcmp(cfg.rawtrial, 'yes') || strcmp(cfg.randomization, 'yes')) && ~strcmp(data.dimord, 'rpt_chan_time')
  640. error('multiple trials required in the data\n');
  641. elseif strcmp(cfg.permutation, 'yes')
  642. % compute the average and covariance without resampling
  643. [dum, avgA, covA, avgB, covB] = prepare_resampled_data(cfg2 , data.trial, data.cov, baseline.trial, baseline.cov);
  644. % compute the average and covariance with random permutation
  645. [cfg, avRA, coRA, avRB, coRB] = prepare_resampled_data(cfg, data.trial, data.cov, baseline.trial, baseline.cov);
  646. % concatenate the different resamplings
  647. avg = cat(1, reshape(avgA, [1 Nchans Nsamples]), reshape(avgB, [1 Nchans Nsamples]), avRA, avRB);
  648. Cy = cat(1, reshape(covA, [1 Nchans Nchans ]), reshape(covB, [1 Nchans Nchans ]), coRA, coRB);
  649. % clear temporary working copies
  650. clear avgA avgB covA covB
  651. clear avRA avRB coRA coRB
  652. % the order of the resamplings should be [avgA avgB randA randB randA randB randA randB ....]
  653. Nrepetitions = 2*cfg.numpermutation + 2;
  654. order = [1 2 3:2:Nrepetitions 4:2:Nrepetitions];
  655. avg = avg(order,:,:);
  656. Cy = Cy (order,:,:);
  657. elseif strcmp(cfg.randomization, 'yes')
  658. % compute the average and covariance without resampling
  659. [dum, avgA, covA, avgB, covB] = prepare_resampled_data(cfg2 , data.trial, data.cov, baseline.trial, baseline.cov);
  660. % compute the average and covariance with random resampling
  661. [cfg, avRA, coRA, avRB, coRB] = prepare_resampled_data(cfg, data.trial, data.cov, baseline.trial, baseline.cov);
  662. % concatenate the different resamplings
  663. avg = cat(1, reshape(avgA, [1 Nchans Nsamples]), reshape(avgB, [1 Nchans Nsamples]), avRA, avRB);
  664. Cy = cat(1, reshape(covA, [1 Nchans Nchans ]), reshape(covB, [1 Nchans Nchans ]), coRA, coRB);
  665. % clear temporary working copies
  666. clear avgA avgB covA covB
  667. clear avRA avRB coRA coRB
  668. % the order of the resamplings should be [avgA avgB randA randB randA randB randA randB ....]
  669. Nrepetitions = 2*cfg.numrandomization + 2;
  670. order = [1 2 3:2:Nrepetitions 4:2:Nrepetitions];
  671. avg = avg(order,:,:);
  672. Cy = Cy (order,:,:);
  673. elseif strcmp(cfg.jackknife, 'yes')
  674. % compute the jackknife repetitions for the average and covariance
  675. [cfg, avg, Cy] = prepare_resampled_data(cfg, data.trial, data.cov);
  676. Nrepetitions = Ntrials;
  677. elseif strcmp(cfg.bootstrap, 'yes')
  678. % compute the bootstrap repetitions for the average and covariance
  679. [cfg, avg, Cy] = prepare_resampled_data(cfg, data.trial, data.cov);
  680. Nrepetitions = cfg.numbootstrap;
  681. elseif strcmp(cfg.pseudovalue, 'yes')
  682. % compute the pseudovalue repetitions for the average and covariance
  683. [cfg, avg, Cy] = prepare_resampled_data(cfg, data.trial, data.cov);
  684. Nrepetitions = Ntrials+1;
  685. elseif strcmp(cfg.singletrial, 'yes')
  686. % The idea is that beamformer uses the average covariance to construct the
  687. % filter and applies it to the single trial covariance/csd. The problem
  688. % is that beamformer will use the averaged covariance/csd to estimate the
  689. % power and not the single trial covariance/csd
  690. error('this option contains a bug, and is therefore not supported at the moment');
  691. % average the single-trial covariance matrices
  692. Cy = mean(data.cov,1);
  693. % copy the average covariance matrix for every individual trial
  694. Cy = repmat(Cy, [Ntrials 1 1]);
  695. % keep the single-trial ERFs, rename them to avg for convenience
  696. avg = data.trial;
  697. Nrepetitions = Ntrials;
  698. elseif strcmp(cfg.rawtrial, 'yes')
  699. % do not do any resampling, keep the single-trial covariances
  700. Cy = data.cov;
  701. % do not do any resampling, keep the single-trial ERFs (rename them to avg for convenience)
  702. avg = data.trial;
  703. Nrepetitions = Ntrials;
  704. elseif Ntrials>1
  705. % average the single-trial covariance matrices
  706. Cy = reshape(mean(data.cov,1), [Nchans Nchans]);
  707. % select the average ERF
  708. avg = data.avg;
  709. Nrepetitions = 1;
  710. elseif Ntrials==1
  711. % select the average covariance matrix
  712. Cy = data.cov;
  713. % select the average ERF
  714. avg = data.avg;
  715. Nrepetitions = 1;
  716. end
  717. % reshape so that it also looks like one trial (out of many)
  718. if Nrepetitions==1
  719. Cy = reshape(Cy , [1 Nchans Nchans]);
  720. avg = reshape(avg, [1 Nchans Nsamples]);
  721. end
  722. % get the relevant low level options from the cfg and convert into key-value pairs
  723. optarg = cfg2keyval(getfield(cfg, cfg.method));
  724. if strcmp(cfg.method, 'lcmv') && ~isfield(grid, 'filter'),
  725. for i=1:Nrepetitions
  726. fprintf('scanning repetition %d\n', i);
  727. dip(i) = beamformer_lcmv(grid, sens, vol, squeeze(avg(i,:,:)), squeeze(Cy(i,:,:)), optarg{:});
  728. end
  729. elseif strcmp(cfg.method, 'lcmv')
  730. %don't loop over repetitions (slow), but reshape the input data to obtain single trial timecourses efficiently
  731. %in the presence of filters pre-computed on the average (or whatever)
  732. siz = size(avg);
  733. tmpdat = reshape(permute(avg,[2 3 1]),[siz(2) siz(3)*siz(1)]);
  734. tmpdip = beamformer_lcmv(grid, sens, vol, tmpdat, squeeze(mean(Cy,1)), optarg{:});
  735. tmpmom = tmpdip.mom{tmpdip.inside(1)};
  736. sizmom = size(tmpmom);
  737. for i=1:length(tmpdip.inside)
  738. indx = tmpdip.inside(i);
  739. tmpdip.mom{indx} = permute(reshape(tmpdip.mom{indx}, [sizmom(1) siz(3) siz(1)]), [3 1 2]);
  740. end
  741. try, tmpdip = rmfield(tmpdip, 'pow'); end
  742. try, tmpdip = rmfield(tmpdip, 'cov'); end
  743. try, tmpdip = rmfield(tmpdip, 'noise'); end
  744. for i=1:Nrepetitions
  745. dip(i).pos = tmpdip.pos;
  746. dip(i).inside = tmpdip.inside;
  747. dip(i).outside = tmpdip.outside;
  748. dip(i).mom = cell(1,size(tmpdip.pos,1));
  749. for ii=1:length(tmpdip.inside)
  750. indx = tmpdip.inside(ii);
  751. dip(i).mom{indx} = reshape(tmpdip.mom{indx}(i,:,:),[sizmom(1) siz(3)]);
  752. end
  753. end
  754. elseif strcmp(cfg.method, 'sam')
  755. for i=1:Nrepetitions
  756. fprintf('scanning repetition %d\n', i);
  757. dip(i) = beamformer_sam(grid, sens, vol, squeeze(avg(i,:,:)), squeeze(Cy(i,:,:)), optarg{:});
  758. end
  759. elseif strcmp(cfg.method, 'pcc')
  760. for i=1:Nrepetitions
  761. fprintf('scanning repetition %d\n', i);
  762. dip(i) = beamformer_pcc(grid, sens, vol, squeeze(avg(i,:,:)), squeeze(Cy(i,:,:)), optarg{:}, 'refdip', cfg.refdip, 'refchan', refchanindx, 'supchan', supchanindx);
  763. end
  764. elseif strcmp(cfg.method, 'mne')
  765. for i=1:Nrepetitions
  766. fprintf('estimating current density distribution for repetition %d\n', i);
  767. dip(i) = minimumnormestimate(grid, sens, vol, squeeze(avg(i,:,:)), optarg{:});
  768. end
  769. elseif strcmp(cfg.method, 'loreta')
  770. for i=1:Nrepetitions
  771. fprintf('estimating LORETA current density distribution for repetition %d\n', i);
  772. dip(i) = loreta( grid, sens, vol, squeeze(avg(i,:,:)), optarg{:});
  773. end
  774. elseif strcmp(cfg.method, 'rv')
  775. for i=1:Nrepetitions
  776. fprintf('estimating residual variance at each grid point for repetition %d\n', i);
  777. dip(i) = residualvariance( grid, sens, vol, squeeze(avg(i,:,:)), optarg{:});
  778. end
  779. elseif strcmp(cfg.method, 'music')
  780. for i=1:Nrepetitions
  781. fprintf('computing multiple signal classification for repetition %d\n', i);
  782. if hascovariance
  783. dip(i) = music(grid, sens, vol, squeeze(avg(i,:,:)), 'cov', squeeze(Cy(i,:,:)), optarg{:});
  784. else
  785. dip(i) = music(grid, sens, vol, squeeze(avg(i,:,:)), optarg{:});
  786. end
  787. end
  788. elseif strcmp(cfg.method, 'mvl')
  789. for i=1:Nrepetitions
  790. fprintf('estimating current density distribution for repetition %d\n', i);
  791. fns = fieldnames(cfg);
  792. optarg = cell(1,length(fns));
  793. n=1;
  794. for c=1:length(fns)
  795. optarg{n} = fns{c};
  796. optarg{n+1} = cfg.(fns{c});
  797. n=n+2;
  798. end
  799. dip(i) = mvlestimate(grid, sens, vol, squeeze(avg(i,:,:)), optarg{:});
  800. end
  801. else
  802. error(sprintf('method ''%s'' is unsupported for source reconstruction in the time domain', cfg.method));
  803. end
  804. end % if freq or timelock data
  805. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  806. % clean up and collect the results
  807. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  808. if isfield(grid, 'xgrid')
  809. % the dipoles are placed on a regular grid that is aligned with the carthesian axes
  810. % copy the description of the grid axes
  811. source.xgrid = grid.xgrid;
  812. source.ygrid = grid.ygrid;
  813. source.zgrid = grid.zgrid;
  814. source.dim = [length(source.xgrid) length(source.ygrid) length(source.zgrid)];
  815. else
  816. source.dim = [size(grid.pos,1) 1];
  817. end
  818. source.vol = vol;
  819. if exist('grad', 'var')
  820. source.grad = grad;
  821. elseif exist('elec', 'var')
  822. source.elec = elec;
  823. end
  824. if istimelock
  825. % add the time axis to the output
  826. source.time = data.time;
  827. elseif iscomp
  828. % FIXME, add the component numbers to the output
  829. elseif isfreq
  830. % add the frequency axis to the output
  831. cfg.frequency = data.freq(nearest(data.freq, cfg.frequency));
  832. source.frequency = cfg.frequency;
  833. if isfield(data, 'time') && isfield(cfg, 'latency')
  834. cfg.latency = data.time(nearest(data.time, cfg.latency));
  835. source.latency = cfg.latency;
  836. end
  837. if isfield(data, 'cumtapcnt'),
  838. source.cumtapcnt = data.cumtapcnt;
  839. end
  840. end
  841. if exist('dip', 'var')
  842. % do some cleaning up, keep the dipole positions etc. in the global structure and not in each trial
  843. source.pos = dip(1).pos;
  844. source.inside = dip(1).inside;
  845. source.outside = dip(1).outside;
  846. dip = rmfield(dip, 'pos');
  847. dip = rmfield(dip, 'inside');
  848. dip = rmfield(dip, 'outside');
  849. if isfield(dip(1), 'leadfield')
  850. source.leadfield = dip(1).leadfield;
  851. dip = rmfield(dip, 'leadfield');
  852. end
  853. elseif exist('grid', 'var')
  854. % no scanning has been done, probably only the leadfield has been computed
  855. try, source.pos = grid.pos; end
  856. try, source.inside = grid.inside; end
  857. try, source.outside = grid.outside; end
  858. try, source.leadfield = grid.leadfield; end
  859. end
  860. if strcmp(cfg.keepleadfield, 'yes') && ~isfield(source, 'leadfield')
  861. % add the precomputed leadfields to the output source
  862. source.leadfield = grid.leadfield;
  863. elseif strcmp(cfg.keepleadfield, 'no') && isfield(source, 'leadfield')
  864. % remove the precomputed leadfields from the output source
  865. source = rmfield(source, 'leadfield');
  866. end
  867. % remove the precomputed leadfields from the cfg regardless of what keepleadfield is saying
  868. % it should not be kept in cfg, since there it takes up too much space
  869. if isfield(cfg, 'grid') && isfield(cfg.grid, 'leadfield')
  870. cfg.grid = rmfield(cfg.grid, 'leadfield');
  871. end
  872. if convertfreq
  873. % FIXME, convert the source reconstruction back to a frequency representation
  874. elseif convertcomp
  875. % FIXME, convert the source reconstruction back to a component representation
  876. end
  877. if strcmp(cfg.jackknife, 'yes')
  878. source.method = 'jackknife';
  879. source.trial = dip;
  880. source.df = Ntrials;
  881. elseif strcmp(cfg.bootstrap, 'yes')
  882. source.method = 'bootstrap';
  883. source.trial = dip;
  884. source.df = Ntrials;
  885. elseif strcmp(cfg.pseudovalue, 'yes')
  886. source.method = 'pseudovalue';
  887. source.trial = dip;
  888. elseif strcmp(cfg.singletrial, 'yes')
  889. source.method = 'singletrial';
  890. source.trial = dip;
  891. source.df = Ntrials; % is this correct?
  892. elseif strcmp(cfg.rawtrial, 'yes')
  893. source.method = 'rawtrial';
  894. source.trial = dip;
  895. source.df = Ntrials; % is this correct?
  896. elseif strcmp(cfg.randomization, 'yes')
  897. % assign the randomized resamplings to the output, keeping the special meaning of trial 1 and 2 in mind
  898. source.method = 'randomization';
  899. source.avgA = dip(1);
  900. source.avgB = dip(2);
  901. source.trialA = dip(1+2*(1:cfg.numrandomization));
  902. source.trialB = dip(2+2*(1:cfg.numrandomization));
  903. elseif strcmp(cfg.permutation, 'yes')
  904. % assign the randomized resamplings to the output, keeping the special meaning of trial 1 and 2 in mind
  905. source.method = 'permutation';
  906. source.avgA = dip(1);
  907. source.avgB = dip(2);
  908. source.trialA = dip(1+2*(1:cfg.numpermutation));
  909. source.trialB = dip(2+2*(1:cfg.numpermutation));
  910. elseif exist('dip', 'var')
  911. % it looks like beamformer analysis was done on an average input, keep the average source reconstruction
  912. source.method = 'average';
  913. source.avg = dip;
  914. else
  915. % apparently no computations were performed
  916. end
  917. if (strcmp(cfg.jackknife, 'yes') || strcmp(cfg.bootstrap, 'yes') || strcmp(cfg.pseudovalue, 'yes') || strcmp(cfg.singletrial, 'yes') || strcmp(cfg.rawtrial, 'yes')) && strcmp(cfg.keeptrials, 'yes')
  918. % keep the source reconstruction for each repeated or resampled trial
  919. source.trial = dip;
  920. end
  921. % accessing this field here is needed for the configuration tracking
  922. % by accessing it once, it will not be removed from the output cfg
  923. cfg.outputfile;
  924. % get the output cfg
  925. cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes');
  926. % add version information to the configuration
  927. try
  928. % get the full name of the function
  929. cfg.version.name = mfilename('fullpath');
  930. catch
  931. % required for compatibility with Matlab versions prior to release 13 (6.5)
  932. [st, i] = dbstack;
  933. cfg.version.name = st(i);
  934. end
  935. cfg.version.id = '$Id: ft_sourceanalysis.m 1258 2010-06-22 08:33:48Z timeng $';
  936. % remember the configuration details of the input data
  937. if nargin==2
  938. try, cfg.previous = data.cfg; end
  939. elseif nargin==3
  940. cfg.previous = [];
  941. try, cfg.previous{1} = data.cfg; end
  942. try, cfg.previous{2} = baseline.cfg; end
  943. end
  944. % remember the exact configuration details in the output
  945. source.cfg = cfg;
  946. % the output data should be saved to a MATLAB file
  947. if ~isempty(cfg.outputfile)
  948. savevar(cfg.outputfile, 'data', source); % use the variable name "data" in the output file
  949. end
  950. fprintf('total time in sourceanalysis %.1f seconds\n', toc);