PageRenderTime 28ms CodeModel.GetById 0ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/examples/toolboxes/fieldtrip/ft_dipolefitting.m

http://brainstream.googlecode.com/
MATLAB | 586 lines | 316 code | 46 blank | 224 comment | 88 complexity | e83b3a3f9bc428d8958c12e719bbf7f6 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_dipolefitting(cfg, data)
  2. % FT_DIPOLEFITTING perform grid search and non-linear fit with one or multiple
  3. % dipoles and try to find the location where the dipole model is best able
  4. % to explain the measured EEG or MEG topography.
  5. %
  6. % This function will initially scan the whole brain with a single dipole on
  7. % a regular coarse grid, and subsequently start at the most optimal location
  8. % with a non-linear search. Alternatively you can specify the initial
  9. % location of the dipole(s) and the non-linear search will start from there.
  10. %
  11. % Use as
  12. % [source] = ft_dipolefitting(cfg, data)
  13. %
  14. % The configuration has the following general fields
  15. % cfg.numdipoles = number, default is 1
  16. % cfg.symmetry = 'x', 'y' or 'z' symmetry for two dipoles, can be empty (default = [])
  17. % cfg.channel = Nx1 cell-array with selection of channels (default = 'all'),
  18. % see FT_CHANNELSELECTION for details
  19. % cfg.gridsearch = 'yes' or 'no', perform global search for initial
  20. % guess for the dipole parameters (default = 'yes')
  21. % cfg.nonlinear = 'yes' or 'no', perform nonlinear search for optimal
  22. % dipole parameters (default = 'yes')
  23. %
  24. % You should specify the volume conductor model with
  25. % cfg.hdmfile = string, file containing the volume conduction model
  26. % or alternatively
  27. % cfg.vol = structure with volume conduction model
  28. % If the sensor information is not contained in the data itself you should
  29. % also specify the sensor information using
  30. % cfg.gradfile = string, file containing the gradiometer definition
  31. % cfg.elecfile = string, file containing the electrode definition
  32. % or alternatively
  33. % cfg.grad = structure with gradiometer definition
  34. % cfg.elec = structure with electrode definition
  35. %
  36. % If you start with a grid search, you should specify the grid locations at
  37. % which a test dipole will be placed. The positions of the dipoles can be
  38. % specified as a regular 3-D grid that is aligned with the axes of the head
  39. % coordinate system
  40. % cfg.grid.xgrid = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
  41. % cfg.grid.ygrid = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
  42. % cfg.grid.zgrid = vector (e.g. 0:1:20) or 'auto' (default = 'auto')
  43. % cfg.grid.resolution = number (e.g. 1 cm) for automatic grid generation
  44. % Alternatively a complete grid with dipole positions and precomputed
  45. % leadfields can be specified
  46. % cfg.grid = structure, see FT_PREPARE_LEADFIELD
  47. % or the position of a few dipoles at locations of interest can be
  48. % specified, for example obtained from an anatomical or functional MRI
  49. % cfg.grid.pos = Nx3 matrix with position of each source
  50. % cfg.grid.dim = [Nx Ny Nz] vector with dimensions in case of 3-D grid (optional)
  51. % cfg.grid.inside = vector with indices of the sources inside the brain (optional)
  52. % cfg.grid.outside = vector with indices of the sources outside the brain (optional)
  53. %
  54. % If you do not start with a grid search, you have to give a starting location
  55. % for the nonlinear search
  56. % cfg.dip.pos = initial dipole position, matrix of Ndipoles x 3
  57. %
  58. % The conventional approach is to fit dipoles to event-related averages, which
  59. % within fieldtrip can be obtained from the FT_TIMELOCKANALYSIS or from
  60. % the FT_TIMELOCKGRANDAVERAGE function. This has the additional options
  61. % cfg.latency = [begin end] in seconds or 'all' (default = 'all')
  62. % cfg.model = 'moving' or 'regional'
  63. % A moving dipole model has a different position (and orientation) for each
  64. % timepoint, or for each component. A regional dipole model has the same
  65. % position for each timepoint or component, and a different orientation.
  66. %
  67. % You can also fit dipoles to the spatial topographies of an independent
  68. % component analysis, obtained from the FT_COMPONENTANALYSIS function.
  69. % This has the additional options
  70. % cfg.component = array with numbers (can be empty -> all)
  71. %
  72. % You can also fit dipoles to the spatial topographies that are present
  73. % in the data in the frequency domain, which can be obtained using the
  74. % FT_FREQANALYSIS function. This has the additional options
  75. % cfg.frequency = single number (in Hz)
  76. %
  77. % Low level details of the fitting can be specified in the cfg.dipfit structure
  78. % cfg.dipfit.display = level of display, can be 'off', 'iter', 'notify' or 'final' (default = 'iter')
  79. % cfg.dipfit.optimfun = function to use, can be 'fminsearch' or 'fminunc' (default is determined automatic)
  80. % cfg.dipfit.maxiter = maximum number of function evaluations allowed (default depends on the optimfun)
  81. %
  82. % See also FT_SOURCEANALYSIS, FT_PREPARE_LEADFIELD
  83. % TODO change the output format, more suitable would be something like:
  84. % dip.label
  85. % dip.time
  86. % dip.avg (instead of Vdata)
  87. % dip.dip.pos
  88. % dip.dip.mom
  89. % dip.dip.model, or dip.dip.avg
  90. % dip.dimord
  91. % Undocumented local options:
  92. % cfg.dipfit.constr = Source model constraints, depends on cfg.symmetry
  93. % cfg.inputfile = one can specifiy preanalysed saved data as input
  94. % cfg.outputfile = one can specify output as file to save to disk
  95. %
  96. % This function depends on FT_PREPARE_DIPOLE_GRID which has the following options:
  97. % cfg.grid.xgrid (default set in FT_PREPARE_DIPOLE_GRID: cfg.grid.xgrid = 'auto'), documented
  98. % cfg.grid.ygrid (default set in FT_PREPARE_DIPOLE_GRID: cfg.grid.ygrid = 'auto'), documented
  99. % cfg.grid.zgrid (default set in FT_PREPARE_DIPOLE_GRID: cfg.grid.zgrid = 'auto'), documented
  100. % cfg.grid.resolution, documented
  101. % cfg.grid.pos, documented
  102. % cfg.grid.dim, documented
  103. % cfg.grid.inside, documented
  104. % cfg.grid.outside, documented
  105. % cfg.symmetry, documented
  106. % cfg.mri
  107. % cfg.mriunits
  108. % cfg.smooth
  109. % cfg.sourceunits
  110. % cfg.threshold
  111. %
  112. % This function depends on FT_PREPARE_VOL_SENS which has the following options:
  113. % cfg.channel (default set in FT_DIPOLEFITTING: cfg.channel = 'all'), documented
  114. % cfg.elec, documented
  115. % cfg.elecfile, documented
  116. % cfg.grad, documented
  117. % cfg.gradfile, documented
  118. % cfg.hdmfile, documented
  119. % cfg.order
  120. % cfg.vol, documented
  121. % Copyright (C) 2004-2006, Robert Oostenveld
  122. %
  123. % This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
  124. % for the documentation and details.
  125. %
  126. % FieldTrip is free software: you can redistribute it and/or modify
  127. % it under the terms of the GNU General Public License as published by
  128. % the Free Software Foundation, either version 3 of the License, or
  129. % (at your option) any later version.
  130. %
  131. % FieldTrip is distributed in the hope that it will be useful,
  132. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  133. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  134. % GNU General Public License for more details.
  135. %
  136. % You should have received a copy of the GNU General Public License
  137. % along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
  138. %
  139. % $Id: ft_dipolefitting.m 1258 2010-06-22 08:33:48Z timeng $
  140. fieldtripdefs
  141. cfg = checkconfig(cfg, 'trackconfig', 'on');
  142. % set the defaults
  143. if ~isfield(cfg, 'channel'), cfg.channel = 'all'; end
  144. if ~isfield(cfg, 'component'), cfg.component = []; end % for comp input
  145. if ~isfield(cfg, 'frequency'), cfg.frequency = []; end % for freq input
  146. if ~isfield(cfg, 'latency'), cfg.latency = 'all'; end
  147. if ~isfield(cfg, 'feedback'), cfg.feedback = 'text'; end
  148. if ~isfield(cfg, 'gridsearch'), cfg.gridsearch = 'yes'; end
  149. if ~isfield(cfg, 'nonlinear'), cfg.nonlinear = 'yes'; end
  150. if ~isfield(cfg, 'symmetry'), cfg.symmetry = []; end
  151. if ~isfield(cfg, 'inputfile'), cfg.inputfile = []; end
  152. if ~isfield(cfg, 'outputfile'), cfg.outputfile = []; end
  153. % load optional given inputfile as data
  154. hasdata = (nargin>1);
  155. if ~isempty(cfg.inputfile)
  156. % the input data should be read from file
  157. if hasdata
  158. error('cfg.inputfile should not be used in conjunction with giving input data to this function');
  159. else
  160. data = loadvar(cfg.inputfile, 'data');
  161. end
  162. end
  163. % check if the input data is valid for this function
  164. data = checkdata(data, 'datatype', {'timelock', 'freq', 'comp'}, 'feedback', 'yes');
  165. % put the low-level options pertaining to the dipole grid (used for initial scanning) in their own field
  166. cfg = checkconfig(cfg, 'createsubcfg', {'grid'});
  167. % the default for this depends on the data type
  168. if ~isfield(cfg, 'model'),
  169. if ~isempty(cfg.component)
  170. % each component is fitted independently
  171. cfg.model = 'moving';
  172. elseif ~isempty(cfg.latency)
  173. % fit the data with a dipole at one location
  174. cfg.model = 'regional';
  175. elseif ~isempty(cfg.latency)
  176. % fit the data with a dipole at one location
  177. cfg.model = 'regional';
  178. end
  179. end
  180. if ~isfield(cfg, 'numdipoles')
  181. if isfield(cfg, 'dip')
  182. cfg.numdipoles = size(cfg.dip(1).pos,1);
  183. else
  184. cfg.numdipoles = 1;
  185. end
  186. end
  187. % set up the symmetry constraints
  188. if ~isempty(cfg.symmetry)
  189. if cfg.numdipoles~=2
  190. error('symmetry constraints are only supported for two-dipole models');
  191. elseif strcmp(cfg.symmetry, 'x')
  192. % this structure is passed onto the low-level ft_dipole_fit function
  193. cfg.dipfit.constr.reduce = [1 2 3]; % select the parameters [x1 y1 z1]
  194. cfg.dipfit.constr.expand = [1 2 3 1 2 3]; % repeat them as [x1 y1 z1 x1 y1 z1]
  195. cfg.dipfit.constr.mirror = [1 1 1 -1 1 1]; % multiply each of them with 1 or -1, resulting in [x1 y1 z1 -x1 y1 z1]
  196. elseif strcmp(cfg.symmetry, 'y')
  197. % this structure is passed onto the low-level ft_dipole_fit function
  198. cfg.dipfit.constr.reduce = [1 2 3]; % select the parameters [x1 y1 z1]
  199. cfg.dipfit.constr.expand = [1 2 3 1 2 3]; % repeat them as [x1 y1 z1 x1 y1 z1]
  200. cfg.dipfit.constr.mirror = [1 1 1 1 -1 1]; % multiply each of them with 1 or -1, resulting in [x1 y1 z1 x1 -y1 z1]
  201. elseif strcmp(cfg.symmetry, 'z')
  202. % this structure is passed onto the low-level ft_dipole_fit function
  203. cfg.dipfit.constr.reduce = [1 2 3]; % select the parameters [x1 y1 z1]
  204. cfg.dipfit.constr.expand = [1 2 3 1 2 3]; % repeat them as [x1 y1 z1 x1 y1 z1]
  205. cfg.dipfit.constr.mirror = [1 1 1 1 1 -1]; % multiply each of them with 1 or -1, resulting in [x1 y1 z1 x1 y1 -z1]
  206. else
  207. error('unrecognized symmetry constraint');
  208. end
  209. elseif ~isfield(cfg, 'dipfit') || ~isfield(cfg.dipfit, 'constr')
  210. % no symmetry constraints have been specified
  211. cfg.dipfit.constr = [];
  212. end
  213. if isfield(data, 'topolabel')
  214. % this looks like a component analysis
  215. iscomp = 1;
  216. % transform the data into a representation on which the timelocked dipole fit can perform its trick
  217. data = comp2timelock(cfg, data);
  218. else
  219. iscomp = 0;
  220. end
  221. if isfield(data, 'freq')
  222. % this looks like a frequency analysis
  223. isfreq = 1;
  224. % transform the data into a representation on which the timelocked dipole fit can perform its trick
  225. data = freq2timelock(cfg, data);
  226. else
  227. isfreq = 0;
  228. end
  229. % prepare the volume conduction model and the sensor array
  230. % this updates the configuration with the appropriate fields
  231. [vol, sens, cfg] = prepare_headmodel(cfg, data);
  232. % select the desired channels, the order should be the same as in the sensor structure
  233. [selsens, seldata] = match_str(sens.label, data.label);
  234. Vdata = data.avg(seldata, :);
  235. if iscomp
  236. % select the desired component topographies
  237. Vdata = Vdata(:, cfg.component);
  238. elseif isfreq
  239. % the desired frequency topographies have already been selected
  240. Vdata = Vdata(:, :);
  241. else
  242. % select the desired latencies
  243. if ischar(cfg.latency) && strcmp(cfg.latency, 'all')
  244. cfg.latency = data.time([1 end]);
  245. end
  246. tbeg = nearest(data.time, cfg.latency(1));
  247. tend = nearest(data.time, cfg.latency(end));
  248. cfg.latency = [data.time(tbeg) data.time(tend)];
  249. Vdata = Vdata(:, tbeg:tend);
  250. end
  251. nchans = size(Vdata,1);
  252. ntime = size(Vdata,2);
  253. Vmodel = zeros(nchans, ntime);
  254. fprintf('selected %d channels\n', nchans);
  255. fprintf('selected %d topographies\n', ntime);
  256. if nchans<cfg.numdipoles*3
  257. warning('not enough channels to perform a dipole fit');
  258. end
  259. if ntime<1
  260. error('no spatial topography selected');
  261. end
  262. % check whether EEG is average referenced
  263. if ft_senstype(sens, 'eeg')
  264. if any(rv(Vdata, avgref(Vdata))>0.001)
  265. warning('the EEG data is not average referenced, correcting this');
  266. end
  267. Vdata = avgref(Vdata);
  268. end
  269. % set to zeros if no initial dipole was specified
  270. if ~isfield(cfg, 'dip')
  271. cfg.dip.pos = zeros(cfg.numdipoles, 3);
  272. cfg.dip.mom = zeros(3*cfg.numdipoles, 1);
  273. end
  274. % set to zeros if no initial dipole position was specified
  275. if ~isfield(cfg.dip, 'pos')
  276. cfg.dip.pos = zeros(cfg.numdipoles, 3);
  277. end
  278. % set to zeros if no initial dipole moment was specified
  279. if ~isfield(cfg.dip, 'mom')
  280. cfg.dip.mom = zeros(3*cfg.numdipoles, 1);
  281. end
  282. % check the specified dipole model
  283. if numel(cfg.dip.pos)~=cfg.numdipoles*3 || numel(cfg.dip.mom)~=cfg.numdipoles*3
  284. error('inconsistent number of dipoles in configuration')
  285. end
  286. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  287. % perform the dipole scan, this is usefull for generating an initial guess
  288. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  289. if strcmp(cfg.gridsearch, 'yes')
  290. % test whether we have a valid configuration for dipole scanning
  291. if cfg.numdipoles==1
  292. % this is ok
  293. elseif cfg.numdipoles==2 && ~isempty(cfg.dipfit.constr)
  294. % this is also ok
  295. else
  296. error('dipole scanning is only possible for a single dipole or a symmetric dipole pair');
  297. end
  298. % construct the grid on which the scanning will be done
  299. [grid, cfg] = prepare_dipole_grid(cfg, vol, sens);
  300. progress('init', cfg.feedback, 'scanning grid');
  301. for i=1:length(grid.inside)
  302. progress(i/length(grid.inside), 'scanning grid location %d/%d\n', i, length(grid.inside));
  303. indx = grid.inside(i);
  304. if isfield(grid, 'leadfield')
  305. % reuse the previously computed leadfield
  306. lf = grid.leadfield{indx};
  307. else
  308. lf = ft_compute_leadfield(grid.pos(indx,:), sens, vol);
  309. end
  310. % the model is V=lf*mom+noise, therefore mom=pinv(lf)*V estimates the
  311. % dipole moment this makes the model potential U=lf*pinv(lf)*V and the
  312. % model error is norm(V-U) = norm(V-lf*pinv(lf)*V) = norm((eye-lf*pinv(lf))*V)
  313. switch cfg.model
  314. case 'regional'
  315. % sum the error over all latencies
  316. grid.error(indx,1) = sum(sum(((eye(nchans)-lf*pinv(lf))*Vdata).^2));
  317. case 'moving'
  318. % remember the error for each latency independently
  319. grid.error(indx,:) = sum(((eye(nchans)-lf*pinv(lf))*Vdata).^2);
  320. otherwise
  321. error('unsupported cfg.model');
  322. end % switch model
  323. end % looping over the grid
  324. progress('close');
  325. switch cfg.model
  326. case 'regional'
  327. % find the grid point(s) with the minimum error
  328. [err, indx] = min(grid.error(grid.inside));
  329. dip.pos = grid.pos(grid.inside(indx),:); % note that for a symmetric dipole pair this results in a vector
  330. dip.pos = reshape(dip.pos, cfg.numdipoles, 3); % convert to a Nx3 array
  331. dip.mom = zeros(cfg.numdipoles*3,1); % set the dipole moment to zero
  332. if cfg.numdipoles==1
  333. fprintf('found minimum after scanning on grid point [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3));
  334. elseif cfg.numdipoles==2
  335. fprintf('found minimum after scanning on grid point [%g %g %g; %g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3), dip.pos(4), dip.pos(5), dip.pos(6));
  336. end
  337. case 'moving'
  338. for t=1:ntime
  339. % find the grid point(s) with the minimum error
  340. [err, indx] = min(grid.error(grid.inside,t));
  341. dip(t).pos = grid.pos(grid.inside(indx),:); % note that for a symmetric dipole pair this results in a vector
  342. dip(t).pos = reshape(dip(t).pos, cfg.numdipoles, 3); % convert to a Nx3 array
  343. dip(t).mom = zeros(cfg.numdipoles*3,1); % set the dipole moment to zero
  344. if cfg.numdipoles==1
  345. fprintf('found minimum after scanning for topography %d on grid point [%g %g %g]\n', t, dip(t).pos(1), dip(t).pos(2), dip(t).pos(3));
  346. elseif cfg.numdipoles==2
  347. fprintf('found minimum after scanning for topography %d on grid point [%g %g %g; %g %g %g]\n', t, dip(t).pos(1), dip(t).pos(2), dip(t).pos(3), dip(t).pos(4), dip(t).pos(5), dip(t).pos(6));
  348. end
  349. end
  350. otherwise
  351. error('unsupported cfg.model');
  352. end % switch model
  353. end % if gridsearch
  354. if strcmp(cfg.gridsearch, 'no')
  355. % use the initial guess supplied in the configuration for the remainder
  356. switch cfg.model
  357. case 'regional'
  358. dip = struct(cfg.dip);
  359. case 'moving'
  360. for t=1:ntime
  361. dip(t) = struct(cfg.dip);
  362. end
  363. otherwise
  364. error('unsupported cfg.model');
  365. end % switch model
  366. end
  367. % multiple dipoles can be represented either as a 1x(N*3) vector or as a Nx3 matrix,
  368. % i.e. [x1 y1 z1 x2 y2 z2] or [x1 y1 z1; x2 y2 z2]
  369. switch cfg.model
  370. case 'regional'
  371. dip = fixdipole(dip);
  372. case 'moving'
  373. for t=1:ntime
  374. dip(t) = fixdipole(dip(t));
  375. end
  376. otherwise
  377. error('unsupported cfg.model');
  378. end % switch model
  379. if isfield(cfg, 'dipfit')
  380. % convert the structure with the additional low-level options into key-value pairs
  381. optarg = cfg2keyval(getfield(cfg, 'dipfit'));
  382. else
  383. % no additional low-level options were specified
  384. optarg = {};
  385. end
  386. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  387. % perform the non-linear fit
  388. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  389. if strcmp(cfg.nonlinear, 'yes')
  390. switch cfg.model
  391. case 'regional'
  392. % perform the non-linear dipole fit for all latencies together
  393. % catch errors due to non-convergence
  394. try
  395. dip = dipole_fit(dip, sens, vol, Vdata, optarg{:});
  396. success = 1;
  397. if cfg.numdipoles==1
  398. fprintf('found minimum after non-linear optimization on [%g %g %g]\n', dip.pos(1), dip.pos(2), dip.pos(3));
  399. elseif cfg.numdipoles==2
  400. fprintf('found minimum after non-linear optimization on [%g %g %g; %g %g %g]\n', dip.pos(1,1), dip.pos(1,2), dip.pos(1,3), dip.pos(2,1), dip.pos(2,2), dip.pos(2,3));
  401. end
  402. catch
  403. success = 0;
  404. disp(lasterr);
  405. end
  406. case 'moving'
  407. % perform the non-linear dipole fit for each latency independently
  408. % instead of using dip(t) = ft_dipole_fit(dip(t),...), I am using temporary variables dipin and dipout
  409. % this is to prevent errors of the type "Subscripted assignment between dissimilar structures"
  410. dipin = dip;
  411. for t=1:ntime
  412. % catch errors due to non-convergence
  413. try
  414. dipout(t) = dipole_fit(dipin(t), sens, vol, Vdata(:,t), optarg{:});
  415. success(t) = 1;
  416. if cfg.numdipoles==1
  417. fprintf('found minimum after non-linear optimization for topography %d on [%g %g %g]\n', t, dipout(t).pos(1), dipout(t).pos(2), dipout(t).pos(3));
  418. elseif cfg.numdipoles==2
  419. fprintf('found minimum after non-linear optimization for topography %d on [%g %g %g; %g %g %g]\n', t, dipout(t).pos(1,1), dipout(t).pos(1,2), dipout(t).pos(1,3), dipout(t).pos(2,1), dipout(t).pos(2,2), dipout(t).pos(2,3));
  420. end
  421. catch
  422. dipout(t).pos = dipin(t).pos;
  423. dipout(t).mom = dipin(t).mom;
  424. success(t) = 0;
  425. disp(lasterr);
  426. end
  427. end
  428. dip = dipout;
  429. clear dipin dipout
  430. otherwise
  431. error('unsupported cfg.model');
  432. end % switch model
  433. end % if nonlinear
  434. if strcmp(cfg.nonlinear, 'no')
  435. % the optimal dipole positions are either obrained from scanning or from the initial configured seed
  436. switch cfg.model
  437. case 'regional'
  438. success = 1;
  439. case 'moving'
  440. success = ones(1,ntime);
  441. otherwise
  442. error('unsupported cfg.model');
  443. end % switch model
  444. end
  445. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  446. % compute the model potential distribution and the residual variance
  447. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  448. switch cfg.model
  449. case 'regional'
  450. if success
  451. % re-compute the leadfield in order to compute the model potential and dipole moment
  452. lf = ft_compute_leadfield(dip.pos, sens, vol);
  453. % compute all details of the final dipole model
  454. dip.mom = pinv(lf)*Vdata;
  455. dip.pot = lf*dip.mom;
  456. dip.rv = rv(Vdata, dip.pot);
  457. Vmodel = dip.pot;
  458. end
  459. case 'moving'
  460. for t=1:ntime
  461. if success(t)
  462. % re-compute the leadfield in order to compute the model potential and dipole moment
  463. lf = ft_compute_leadfield(dip(t).pos, sens, vol);
  464. % compute all details of the final dipole model
  465. dip(t).mom = pinv(lf)*Vdata(:,t);
  466. dip(t).pot = lf*dip(t).mom;
  467. dip(t).rv = rv(Vdata(:,t), dip(t).pot);
  468. Vmodel(:,t) = dip(t).pot;
  469. end
  470. end
  471. otherwise
  472. error('unsupported cfg.model');
  473. end % switch model
  474. switch cfg.model
  475. case 'regional'
  476. if isfreq
  477. % the matrix with the dipole moment is encrypted and cannot be interpreted straight away
  478. % reconstruct the frequency representation of the data at the source level
  479. [dip.pow, dip.csd, dip.fourier] = timelock2freq(dip.mom);
  480. end
  481. case 'moving'
  482. if isfreq
  483. % although this is technically possible so far, it does not make any sense
  484. warning('a moving dipole model in the frequency domain is not supported');
  485. end
  486. otherwise
  487. error('unsupported cfg.model');
  488. end % switch model
  489. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  490. % collect the results
  491. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  492. source.label = cfg.channel; % these channels were used in fitting
  493. source.dip = dip;
  494. source.Vdata = Vdata; % FIXME this should be renamed (if possible w.r.t. EEGLAB)
  495. source.Vmodel = Vmodel; % FIXME this should be renamed (if possible w.r.t. EEGLAB)
  496. % assign a latency, frequeny or component axis to the output
  497. if iscomp
  498. source.component = cfg.component;
  499. % FIXME assign Vdata to an output variable, idem for the model potential
  500. elseif isfreq
  501. source.freq = cfg.frequency;
  502. source.dimord = 'chan_freq';
  503. % FIXME assign Vdata to an output variable, idem for the model potential
  504. else
  505. tbeg = nearest(data.time, cfg.latency(1));
  506. tend = nearest(data.time, cfg.latency(end));
  507. source.time = data.time(tbeg:tend);
  508. source.dimord = 'chan_time';
  509. end
  510. if isfield(data, 'grad')
  511. % copy the gradiometer array along
  512. source.grad = data.grad;
  513. end
  514. if isfield(data, 'elec')
  515. % copy the electrode array along
  516. source.elec = data.elec;
  517. end
  518. % accessing this field here is needed for the configuration tracking
  519. % by accessing it once, it will not be removed from the output cfg
  520. cfg.outputfile;
  521. % get the output cfg
  522. cfg = checkconfig(cfg, 'trackconfig', 'off', 'checksize', 'yes');
  523. % add the version details of this function call to the configuration
  524. try
  525. % get the full name of the function
  526. cfg.version.name = mfilename('fullpath');
  527. catch
  528. % required for compatibility with Matlab versions prior to release 13 (6.5)
  529. [st, i] = dbstack;
  530. cfg.version.name = st(i);
  531. end
  532. cfg.version.id = '$Id: ft_dipolefitting.m 1258 2010-06-22 08:33:48Z timeng $';
  533. % remember the configuration details of the input data
  534. try, cfg.previous = data.cfg; end
  535. % remember the exact configuration details in the output
  536. source.cfg = cfg;
  537. % the output data should be saved to a MATLAB file
  538. if ~isempty(cfg.outputfile)
  539. savevar(cfg.outputfile, 'data', source); % use the variable name "data" in the output file
  540. end