/toolboxes/fieldtrip/utilities/ft_checkdata.m

http://brainstream.googlecode.com/ · MATLAB · 1926 lines · 1574 code · 121 blank · 231 comment · 249 complexity · a01e1dffd614d3e255fe5b37a270a4e7 MD5 · raw file

  1. function [data] = ft_checkdata(data, varargin)
  2. % FT_CHECKDATA checks the input data of the main FieldTrip functions, e.g. whether
  3. % the type of data strucure corresponds with the required data. If neccessary
  4. % and possible, this function will adjust the data structure to the input
  5. % requirements (e.g. change dimord, average over trials, convert inside from
  6. % index into logical).
  7. %
  8. % If the input data does NOT correspond to the requirements, this function
  9. % is supposed to give a elaborate warning message and if applicable point
  10. % the user to external documentation (link to website).
  11. %
  12. % Use as
  13. % [data] = ft_checkdata(data, ...)
  14. %
  15. % Optional input arguments should be specified as key-value pairs and can include
  16. % feedback = yes, no
  17. % datatype = raw, freq, timelock, comp, spike, source, volume, dip
  18. % dimord = any combination of time, freq, chan, refchan, rpt, subj, chancmb, rpttap, pos
  19. % senstype = ctf151, ctf275, ctf151_planar, ctf275_planar, neuromag122, neuromag306, bti148, bti248, bti248_planar, magnetometer, electrode
  20. % inside = logical, index
  21. % ismeg = yes, no
  22. % hastrials = yes, no
  23. % hasunits = yes, no
  24. % hassampleinfo = yes, no, ifmakessense
  25. % hascumtapcnt = yes, no (only applies to freq data)
  26. % hasdim = yes, no
  27. % hasdof = yes, no
  28. % cmbrepresentation = sparse, full (applies to covariance and cross-spectral density)
  29. % fsample = sampling frequency to use to go from SPIKE to RAW representation
  30. %
  31. % For some options you can specify multiple values, e.g.
  32. % [data] = ft_checkdata(data, 'senstype', {'ctf151', 'ctf275'}), e.g. in megrealign
  33. % [data] = ft_checkdata(data, 'datatype', {'timelock', 'freq'}), e.g. in sourceanalysis
  34. % Copyright (C) 2007-2009, Robert Oostenveld
  35. %
  36. % This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
  37. % for the documentation and details.
  38. %
  39. % FieldTrip is free software: you can redistribute it and/or modify
  40. % it under the terms of the GNU General Public License as published by
  41. % the Free Software Foundation, either version 3 of the License, or
  42. % (at your option) any later version.
  43. %
  44. % FieldTrip is distributed in the hope that it will be useful,
  45. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  46. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  47. % GNU General Public License for more details.
  48. %
  49. % You should have received a copy of the GNU General Public License
  50. % along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
  51. %
  52. % $Id: ft_checkdata.m 5033 2011-12-14 10:46:20Z roboos $
  53. % in case of an error this function could use dbstack for more detailled
  54. % user feedback
  55. %
  56. % this function should replace/encapsulate
  57. % fixdimord
  58. % fixinside
  59. % fixprecision
  60. % fixvolume
  61. % data2raw
  62. % raw2data
  63. % grid2transform
  64. % transform2grid
  65. % fourier2crsspctrm
  66. % freq2cumtapcnt
  67. % sensortype
  68. % time2offset
  69. % offset2time
  70. % fixsens -> this is kept a separate function because it should also be
  71. % called from other modules
  72. %
  73. % other potential uses for this function:
  74. % time -> offset in freqanalysis
  75. % average over trials
  76. % csd as matrix
  77. % get the optional input arguments
  78. feedback = ft_getopt(varargin, 'feedback', 'no');
  79. dtype = ft_getopt(varargin, 'datatype'); % should not conflict with the ft_datatype function
  80. dimord = ft_getopt(varargin, 'dimord');
  81. stype = ft_getopt(varargin, 'senstype'); % senstype is a function name which should not be masked
  82. ismeg = ft_getopt(varargin, 'ismeg');
  83. inside = ft_getopt(varargin, 'inside'); % can be 'logical' or 'index'
  84. hastrials = ft_getopt(varargin, 'hastrials');
  85. hasunits = ft_getopt(varargin, 'hasunits');
  86. hassampleinfo = ft_getopt(varargin, 'hassampleinfo', 'no');
  87. hasdimord = ft_getopt(varargin, 'hasdimord', 'no');
  88. hasdim = ft_getopt(varargin, 'hasdim');
  89. hascumtapcnt = ft_getopt(varargin, 'hascumtapcnt');
  90. hasdof = ft_getopt(varargin, 'hasdof', 'no');
  91. haspow = ft_getopt(varargin, 'haspow', 'no');
  92. cmbrepresentation = ft_getopt(varargin, 'cmbrepresentation');
  93. channelcmb = ft_getopt(varargin, 'channelcmb');
  94. sourcedimord = ft_getopt(varargin, 'sourcedimord');
  95. sourcerepresentation = ft_getopt(varargin, 'sourcerepresentation');
  96. fsample = ft_getopt(varargin, 'fsample');
  97. % check whether people are using deprecated stuff
  98. depHastrialdef = ft_getopt(varargin, 'hastrialdef');
  99. if (~isempty(depHastrialdef))
  100. warning_once('ft_checkdata option ''hastrialdef'' is deprecated; use ''hassampleinfo'' instead');
  101. hassampleinfo = depHastrialdef;
  102. end
  103. if (~isempty(ft_getopt(varargin, 'hasoffset')))
  104. warning_once('ft_checkdata option ''hasoffset'' has been removed and will be ignored');
  105. end
  106. % determine the type of input data
  107. % this can be raw, freq, timelock, comp, spike, source, volume, dip
  108. israw = ft_datatype(data, 'raw');
  109. isfreq = ft_datatype(data, 'freq');
  110. istimelock = ft_datatype(data, 'timelock');
  111. iscomp = ft_datatype(data, 'comp');
  112. isspike = ft_datatype(data, 'spike');
  113. isvolume = ft_datatype(data, 'volume');
  114. issource = ft_datatype(data, 'source');
  115. isdip = ft_datatype(data, 'dip');
  116. ismvar = ft_datatype(data, 'mvar');
  117. isfreqmvar = ft_datatype(data, 'freqmvar');
  118. ischan = ft_datatype(data, 'chan');
  119. % FIXME use the istrue function on ismeg and hasxxx options
  120. if ~isequal(feedback, 'no')
  121. if israw
  122. nchan = length(data.label);
  123. ntrial = length(data.trial);
  124. fprintf('the input is raw data with %d channels and %d trials\n', nchan, ntrial);
  125. elseif isfreq
  126. nchan = length(data.label);
  127. nfreq = length(data.freq);
  128. if isfield(data, 'time'), ntime = num2str(length(data.time)); else ntime = 'no'; end
  129. fprintf('the input is freq data with %d channels, %d frequencybins and %s timebins\n', nchan, nfreq, ntime);
  130. elseif istimelock
  131. nchan = length(data.label);
  132. ntime = length(data.time);
  133. fprintf('the input is timelock data with %d channels and %d timebins\n', nchan, ntime);
  134. elseif iscomp
  135. ncomp = length(data.label);
  136. nchan = length(data.topolabel);
  137. fprintf('the input is component data with %d components and %d original channels\n', ncomp, nchan);
  138. elseif isspike
  139. nchan = length(data.label);
  140. fprintf('the input is spike data with %d channels\n',nchan);
  141. elseif isvolume
  142. fprintf('the input is volume data with dimensions [%d %d %d]\n', data.dim(1), data.dim(2), data.dim(3));
  143. elseif issource
  144. nsource = size(data.pos, 1);
  145. fprintf('the input is source data with %d positions\n', nsource);
  146. elseif isdip
  147. fprintf('the input is dipole data\n');
  148. elseif ismvar
  149. fprintf('the input is mvar data\n');
  150. elseif isfreqmvar
  151. fprintf('the input is freqmvar data\n');
  152. elseif ischan
  153. nchan = length(data.label);
  154. fprintf('the input is chan data\n');
  155. end
  156. end % give feedback
  157. if issource && isvolume
  158. % it should be either one or the other: the choice here is to
  159. % represent it as volume description since that is simpler to handle
  160. % the conversion is done by remove the grid positions
  161. data = rmfield(data, 'pos');
  162. issource = false;
  163. end
  164. % the ft_datatype_XXX functions ensures the consistency of the XXX datatype
  165. % and provides a detailled description of the dataformat and its history
  166. if israw
  167. data = ft_datatype_raw(data, 'hassampleinfo', hassampleinfo);
  168. elseif isfreq
  169. data = ft_datatype_freq(data);
  170. elseif istimelock
  171. data = ft_datatype_timelock(data);
  172. elseif iscomp
  173. data = ft_datatype_comp(data);
  174. elseif isspike
  175. data = ft_datatype_spike(data);
  176. elseif isvolume
  177. data = ft_datatype_volume(data);
  178. elseif issource
  179. data = ft_datatype_source(data);
  180. elseif isdip
  181. data = ft_datatype_dip(data);
  182. elseif ismvar || isfreqmvar
  183. data = ft_datatype_mvar(data);
  184. end
  185. if ~isempty(dtype)
  186. if ~isa(dtype, 'cell')
  187. dtype = {dtype};
  188. end
  189. okflag = 0;
  190. for i=1:length(dtype)
  191. % check that the data matches with one or more of the required ft_datatypes
  192. switch dtype{i}
  193. case 'raw'
  194. okflag = okflag + israw;
  195. case 'freq'
  196. okflag = okflag + isfreq;
  197. case 'timelock'
  198. okflag = okflag + istimelock;
  199. case 'comp'
  200. okflag = okflag + iscomp;
  201. case 'spike'
  202. okflag = okflag + isspike;
  203. case 'volume'
  204. okflag = okflag + isvolume;
  205. case 'source'
  206. okflag = okflag + issource;
  207. case 'dip'
  208. okflag = okflag + isdip;
  209. case 'mvar'
  210. okflag = okflag + ismvar;
  211. case 'freqmvar'
  212. okflag = okflag + isfreqmvar;
  213. case 'chan'
  214. okflag = okflag + ischan;
  215. end % switch dtype
  216. end % for dtype
  217. if ~okflag
  218. % try to convert the data
  219. for iCell = 1:length(dtype)
  220. if isequal(dtype(iCell), {'source'}) && isvolume
  221. data = volume2source(data);
  222. isvolume = 0;
  223. issource = 1;
  224. okflag = 1;
  225. elseif isequal(dtype(iCell), {'volume'}) && issource
  226. data = source2volume(data);
  227. isvolume = 1;
  228. issource = 0;
  229. okflag = 1;
  230. elseif isequal(dtype(iCell), {'raw'}) && issource
  231. data = data2raw(data);
  232. issource = 0;
  233. israw = 1;
  234. okflag = 1;
  235. elseif isequal(dtype(iCell), {'raw'}) && istimelock
  236. data = timelock2raw(data);
  237. istimelock = 0;
  238. israw = 1;
  239. okflag = 1;
  240. elseif isequal(dtype(iCell), {'timelock'}) && israw
  241. data = raw2timelock(data);
  242. israw = 0;
  243. istimelock = 1;
  244. okflag = 1;
  245. elseif isequal(dtype(iCell), {'raw'}) && isfreq
  246. data = freq2raw(data);
  247. isfreq = 0;
  248. israw = 1;
  249. okflag = 1;
  250. elseif isequal(dtype(iCell), {'raw'}) && iscomp
  251. data = comp2raw(data);
  252. iscomp = 0;
  253. israw = 1;
  254. okflag = 1;
  255. elseif isequal(dtype(iCell), {'timelock'}) && iscomp
  256. data = comp2raw(data);
  257. data = raw2timelock(data);
  258. iscomp = 0;
  259. istimelock = 1;
  260. okflag = 1;
  261. elseif isequal(dtype(iCell), {'timelock'}) && ischan
  262. data = chan2timelock(data);
  263. ischan = 0;
  264. istimelock = 1;
  265. okflag = 1;
  266. elseif isequal(dtype(iCell), {'freq'}) && ischan
  267. data = chan2freq(data);
  268. ischan = 0;
  269. isfreq = 1;
  270. okflag = 1;
  271. elseif isequal(dtype(iCell), {'spike'}) && israw
  272. data = raw2spike(data);
  273. israw = 0;
  274. isspike = 1;
  275. okflag = 1;
  276. elseif isequal(dtype(iCell), {'raw'}) && isspike
  277. data = spike2raw(data,fsample);
  278. isspike = 0;
  279. israw = 1;
  280. okflag = 1;
  281. end
  282. end % for iCell
  283. end % if okflag
  284. if ~okflag
  285. % construct an error message
  286. if length(dtype)>1
  287. str = sprintf('%s, ', dtype{1:(end-2)});
  288. str = sprintf('%s%s or %s', str, dtype{end-1}, dtype{end});
  289. else
  290. str = dtype{1};
  291. end
  292. str = sprintf('This function requires %s data as input.', str);
  293. error(str);
  294. end % if okflag
  295. end
  296. if ~isempty(dimord)
  297. if ~isa(dimord, 'cell')
  298. dimord = {dimord};
  299. end
  300. if isfield(data, 'dimord')
  301. okflag = any(strcmp(data.dimord, dimord));
  302. else
  303. okflag = 0;
  304. end
  305. if ~okflag
  306. % construct an error message
  307. if length(dimord)>1
  308. str = sprintf('%s, ', dimord{1:(end-2)});
  309. str = sprintf('%s%s or %s', str, dimord{end-1}, dimord{end});
  310. else
  311. str = dimord{1};
  312. end
  313. str = sprintf('This function requires data with a dimord of %s.', str);
  314. error(str);
  315. end % if okflag
  316. end
  317. if ~isempty(stype)
  318. if ~isa(stype, 'cell')
  319. stype = {stype};
  320. end
  321. if isfield(data, 'grad') || isfield(data, 'elec')
  322. if any(strcmp(ft_senstype(data), stype));
  323. okflag = 1;
  324. else
  325. okflag = 0;
  326. end
  327. else
  328. okflag = 0;
  329. end
  330. if ~okflag
  331. % construct an error message
  332. if length(stype)>1
  333. str = sprintf('%s, ', stype{1:(end-2)});
  334. str = sprintf('%s%s or %s', str, stype{end-1}, stype{end});
  335. else
  336. str = stype{1};
  337. end
  338. str = sprintf('This function requires %s data as input, but you are giving %s data.', str, ft_senstype(data));
  339. error(str);
  340. end % if okflag
  341. end
  342. if ~isempty(ismeg)
  343. if isequal(ismeg, 'yes')
  344. okflag = isfield(data, 'grad');
  345. elseif isequal(ismeg, 'no')
  346. okflag = ~isfield(data, 'grad');
  347. end
  348. if ~okflag && isequal(ismeg, 'yes')
  349. error('This function requires MEG data with a ''grad'' field');
  350. elseif ~okflag && isequal(ismeg, 'no')
  351. error('This function should not be given MEG data with a ''grad'' field');
  352. end % if okflag
  353. end
  354. if ~isempty(inside)
  355. % TODO absorb the fixinside function into this code
  356. data = fixinside(data, inside);
  357. okflag = isfield(data, 'inside');
  358. if ~okflag
  359. % construct an error message
  360. error('This function requires data with an ''inside'' field.');
  361. end % if okflag
  362. end
  363. %if isvolume
  364. % % ensure consistent dimensions of the volumetric data
  365. % % reshape each of the volumes that is found into a 3D array
  366. % param = parameterselection('all', data);
  367. % dim = data.dim;
  368. % for i=1:length(param)
  369. % tmp = getsubfield(data, param{i});
  370. % tmp = reshape(tmp, dim);
  371. % data = setsubfield(data, param{i}, tmp);
  372. % end
  373. %end
  374. if isequal(hasunits, 'yes') && ~isfield(data, 'units')
  375. % calling convert_units with only the input data adds the units without converting
  376. data = ft_convert_units(data);
  377. end
  378. if issource || isvolume,
  379. % the following section is to make a dimord-consistent representation of
  380. % volume and source data, taking trials, time and frequency into account
  381. if isequal(hasdimord, 'yes') && (~isfield(data, 'dimord') || ~strcmp(data.dimord,sourcedimord))
  382. % determine the size of the data
  383. if isfield(data, 'dimord'),
  384. dimtok = tokenize(data.dimord, '_');
  385. if ~isempty(strmatch('time', dimtok)), Ntime = length(data.time); else Ntime = 1; end
  386. if ~isempty(strmatch('freq', dimtok)), Nfreq = length(data.freq); else Nfreq = 1; end
  387. else
  388. Nfreq = 1;
  389. Ntime = 1;
  390. end
  391. %convert old style source representation into new style
  392. if isfield(data, 'avg') && isfield(data.avg, 'mom') && (isfield(data, 'freq') || isfield(data, 'frequency')) && strcmp(sourcedimord, 'rpt_pos'),
  393. %frequency domain source representation convert to single trial power
  394. Npos = size(data.pos,1);
  395. Nrpt = size(data.cumtapcnt,1);
  396. tmpmom = zeros(Npos, size(data.avg.mom{data.inside(1)},2));
  397. tmpmom(data.inside,:) = cat(1,data.avg.mom{data.inside});
  398. tmppow = zeros(Npos, Nrpt);
  399. tapcnt = [0;cumsum(data.cumtapcnt)];
  400. for k = 1:Nrpt
  401. Ntap = tapcnt(k+1)-tapcnt(k);
  402. tmppow(data.inside,k) = sum(abs(tmpmom(data.inside,(tapcnt(k)+1):tapcnt(k+1))).^2,2)./Ntap;
  403. end
  404. data.pow = tmppow';
  405. data = rmfield(data, 'avg');
  406. if strcmp(inside, 'logical'),
  407. data = fixinside(data, 'logical');
  408. data.inside = repmat(data.inside(:)',[Nrpt 1]);
  409. end
  410. elseif isfield(data, 'avg') && isfield(data.avg, 'mom') && (isfield(data, 'freq') || isfield(data, 'frequency')) && strcmp(sourcedimord, 'rpttap_pos'),
  411. %frequency domain source representation convert to single taper fourier coefficients
  412. Npos = size(data.pos,1);
  413. Nrpt = sum(data.cumtapcnt);
  414. data.fourierspctrm = complex(zeros(Nrpt, Npos), zeros(Nrpt, Npos));
  415. data.fourierspctrm(:, data.inside) = transpose(cat(1, data.avg.mom{data.inside}));
  416. data = rmfield(data, 'avg');
  417. elseif isfield(data, 'avg') && isfield(data.avg, 'mom') && isfield(data, 'time') && strcmp(sourcedimord, 'pos_time'),
  418. Npos = size(data.pos,1);
  419. Nrpt = 1;
  420. tmpmom = zeros(Npos, size(data.avg.mom{data.inside(1)},2));
  421. tmpmom(data.inside,:) = cat(1,data.avg.mom{data.inside});
  422. data.mom = tmpmom;
  423. if isfield(data.avg, 'noise'),
  424. tmpnoise = data.avg.noise(:);
  425. data.noise = tmpnoise(:,ones(1,size(tmpmom,2)));
  426. end
  427. data = rmfield(data, 'avg');
  428. Ntime = length(data.time);
  429. elseif isfield(data, 'trial') && isfield(data.trial(1), 'mom') && isfield(data, 'time') && strcmp(sourcedimord, 'rpt_pos_time'),
  430. Npos = size(data.pos,1);
  431. Nrpt = length(data.trial);
  432. Ntime = length(data.time);
  433. tmpmom = zeros(Nrpt, Npos, Ntime);
  434. for k = 1:Nrpt
  435. tmpmom(k,data.inside,:) = cat(1,data.trial(k).mom{data.inside});
  436. end
  437. data = rmfield(data, 'trial');
  438. data.mom = tmpmom;
  439. elseif isfield(data, 'trial') && isstruct(data.trial)
  440. Nrpt = length(data.trial);
  441. else
  442. Nrpt = 1;
  443. end
  444. % start with an initial specification of the dimord and dim
  445. if (~isfield(data, 'dim') || ~isfield(data, 'dimord'))
  446. if issource
  447. % at least it should have a Nx3 pos
  448. data.dim = size(data.pos, 1);
  449. data.dimord = 'pos';
  450. elseif isvolume
  451. % at least it should have a 1x3 dim
  452. data.dim = data.dim;
  453. data.dimord = 'dim1_dim2_dim3';
  454. end
  455. end
  456. % add the additional dimensions
  457. if Nfreq>1
  458. data.dimord = [data.dimord '_freq'];
  459. data.dim = [data.dim Nfreq];
  460. end
  461. if Ntime>1
  462. data.dimord = [data.dimord '_time'];
  463. data.dim = [data.dim Ntime];
  464. end
  465. if Nrpt>1 && strcmp(sourcedimord, 'rpt_pos'),
  466. data.dimord = ['rpt_' data.dimord];
  467. data.dim = [Nrpt data.dim ];
  468. elseif Nrpt>1 && strcmp(sourcedimord, 'rpttap_pos'),
  469. data.dimord = ['rpttap_' data.dimord];
  470. data.dim = [Nrpt data.dim ];
  471. end
  472. % the nested trial structure is not compatible with dimord
  473. if isfield(data, 'trial') && isstruct(data.trial)
  474. param = fieldnames(data.trial);
  475. for i=1:length(param)
  476. if isa(data.trial(1).(param{i}), 'cell')
  477. concat = cell(data.dim(1), prod(data.dim(2:end)));
  478. else
  479. concat = zeros(data.dim(1), prod(data.dim(2:end)));
  480. end
  481. for j=1:length(data.trial)
  482. tmp = data.trial(j).(param{i});
  483. concat(j,:) = tmp(:);
  484. end % for each trial
  485. data.trial = rmfield(data.trial, param{i});
  486. data.(param{i}) = reshape(concat, data.dim);
  487. end % for each param
  488. data = rmfield(data, 'trial');
  489. end
  490. end
  491. % ensure consistent dimensions of the source reconstructed data
  492. % reshape each of the source reconstructed parameters
  493. if issource && isfield(data, 'dim') && prod(data.dim)==size(data.pos,1)
  494. dim = [prod(data.dim) 1];
  495. %elseif issource && any(~cellfun('isempty',strfind(fieldnames(data), 'dimord')))
  496. % dim = [size(data.pos,1) 1]; %sparsely represented source structure new style
  497. elseif isfield(data, 'dim'),
  498. dim = [data.dim 1];
  499. elseif issource && ~isfield(data, 'dimord')
  500. dim = [size(data.pos,1) 1];
  501. elseif isfield(data, 'dimord'),
  502. %HACK
  503. dimtok = tokenize(data.dimord, '_');
  504. for i=1:length(dimtok)
  505. if strcmp(dimtok(i), 'pos')
  506. dim(1,i) = size(getsubfield(data,dimtok{i}),1);
  507. elseif strcmp(dimtok(i), 'rpt')
  508. dim(1,i) = nan;
  509. else
  510. dim(1,i) = length(getsubfield(data,dimtok{i}));
  511. end
  512. end
  513. i = find(isnan(dim));
  514. if ~isempty(i)
  515. n = fieldnames(data);
  516. for ii=1:length(n)
  517. numels(1,ii) = numel(getfield(data,n{ii}));
  518. end
  519. nrpt = numels./prod(dim(setdiff(1:length(dim),i)));
  520. nrpt = nrpt(nrpt==round(nrpt));
  521. dim(i) = max(nrpt);
  522. end
  523. if numel(dim)==1, dim(1,2) = 1; end;
  524. end
  525. % these fields should not be reshaped
  526. exclude = {'cfg' 'fwhm' 'leadfield' 'q' 'rough'};
  527. if ~strcmp(inside, 'logical')
  528. % also exclude the inside/outside from being reshaped
  529. exclude = cat(2, exclude, {'inside' 'outside'});
  530. end
  531. param = setdiff(parameterselection('all', data), exclude);
  532. for i=1:length(param)
  533. if any(param{i}=='.')
  534. % the parameter is nested in a substructure, which can have multiple elements (e.g. source.trial(1).pow, source.trial(2).pow, ...)
  535. % loop over the substructure array and reshape for every element
  536. tok = tokenize(param{i}, '.');
  537. sub1 = tok{1}; % i.e. this would be 'trial'
  538. sub2 = tok{2}; % i.e. this would be 'pow'
  539. tmp1 = getfield(data, sub1);
  540. for j=1:numel(tmp1)
  541. tmp2 = getfield(tmp1(j), sub2);
  542. tmp2 = reshape(tmp2, dim);
  543. tmp1(j) = setfield(tmp1(j), sub2, tmp2);
  544. end
  545. data = setfield(data, sub1, tmp1);
  546. else
  547. tmp = getfield(data, param{i});
  548. tmp = reshape(tmp, dim);
  549. data = setfield(data, param{i}, tmp);
  550. end
  551. end
  552. end
  553. if isequal(hastrials, 'yes')
  554. okflag = isfield(data, 'trial');
  555. if ~okflag && isfield(data, 'dimord')
  556. % instead look in the dimord for rpt or subj
  557. okflag = ~isempty(strfind(data.dimord, 'rpt')) || ...
  558. ~isempty(strfind(data.dimord, 'rpttap')) || ...
  559. ~isempty(strfind(data.dimord, 'subj'));
  560. end
  561. if ~okflag
  562. error('This function requires data with a ''trial'' field');
  563. end % if okflag
  564. end
  565. if isequal(hassampleinfo, 'yes') || isequal(hassampleinfo, 'ifmakessense')
  566. data = fixsampleinfo(data);
  567. end
  568. if isequal(hasdim, 'yes') && ~isfield(data, 'dim')
  569. data.dim = pos2dim(data.pos);
  570. elseif isequal(hasdim, 'no') && isfield(data, 'dim')
  571. data = rmfield(data, 'dim');
  572. end % if hasdim
  573. if isequal(hascumtapcnt, 'yes') && ~isfield(data, 'cumtapcnt')
  574. error('This function requires data with a ''cumtapcnt'' field');
  575. elseif isequal(hascumtapcnt, 'no') && isfield(data, 'cumtapcnt')
  576. data = rmfield(data, 'cumtapcnt');
  577. end % if hascumtapcnt
  578. if isequal(hasdof, 'yes') && ~isfield(data, 'hasdof')
  579. error('This function requires data with a ''dof'' field');
  580. elseif isequal(hasdof, 'no') && isfield(data, 'hasdof')
  581. data = rmfield(data, 'cumtapcnt');
  582. end % if hasdof
  583. if ~isempty(cmbrepresentation)
  584. if istimelock
  585. data = fixcov(data, cmbrepresentation);
  586. elseif isfreq
  587. data = fixcsd(data, cmbrepresentation, channelcmb);
  588. elseif isfreqmvar
  589. data = fixcsd(data, cmbrepresentation, channelcmb);
  590. else
  591. error('This function requires data with a covariance, coherence or cross-spectrum');
  592. end
  593. end % cmbrepresentation
  594. if issource && ~isempty(sourcerepresentation)
  595. data = fixsource(data, 'type', sourcerepresentation);
  596. end
  597. if issource && ~strcmp(haspow, 'no')
  598. data = fixsource(data, 'type', sourcerepresentation, 'haspow', haspow);
  599. end
  600. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  601. % represent the covariance matrix in a particular manner
  602. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  603. function data = fixcov(data, desired)
  604. if isfield(data, 'cov') && ~isfield(data, 'labelcmb')
  605. current = 'full';
  606. elseif isfield(data, 'cov') && isfield(data, 'labelcmb')
  607. current = 'sparse';
  608. else
  609. error('Could not determine the current representation of the covariance matrix');
  610. end
  611. if isequal(current, desired)
  612. % nothing to do
  613. elseif strcmp(current, 'full') && strcmp(desired, 'sparse')
  614. % FIXME should be implemented
  615. error('not yet implemented');
  616. elseif strcmp(current, 'sparse') && strcmp(desired, 'full')
  617. % FIXME should be implemented
  618. error('not yet implemented');
  619. end
  620. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  621. % represent the cross-spectral density matrix in a particular manner
  622. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  623. function [data] = fixcsd(data, desired, channelcmb)
  624. % FIXCSD converts univariate frequency domain data (fourierspctrm) into a bivariate
  625. % representation (crsspctrm), or changes the representation of bivariate frequency
  626. % domain data (sparse/full/sparsewithpow, sparsewithpow only works for crsspctrm or
  627. % fourierspctrm)
  628. % Copyright (C) 2010, Jan-Mathijs Schoffelen, Robert Oostenveld
  629. if isfield(data, 'crsspctrm') && isfield(data, 'powspctrm')
  630. current = 'sparsewithpow';
  631. elseif isfield(data, 'powspctrm')
  632. current = 'sparsewithpow';
  633. elseif isfield(data, 'fourierspctrm') && ~isfield(data, 'labelcmb')
  634. current = 'fourier';
  635. elseif ~isfield(data, 'labelcmb')
  636. current = 'full';
  637. elseif isfield(data, 'labelcmb')
  638. current = 'sparse';
  639. else
  640. error('Could not determine the current representation of the %s matrix', param);
  641. end
  642. % first go from univariate fourier to the required bivariate representation
  643. if strcmp(current, 'fourier') && strcmp(desired, 'fourier')
  644. % nothing to do
  645. elseif strcmp(current, 'fourier') && strcmp(desired, 'sparsewithpow')
  646. dimtok = tokenize(data.dimord, '_');
  647. if ~isempty(strmatch('rpttap', dimtok)),
  648. nrpt = size(data.cumtapcnt,1);
  649. flag = 0;
  650. else
  651. nrpt = 1;
  652. flag = 1;
  653. end
  654. if ~isempty(strmatch('freq', dimtok)), nfrq=length(data.freq); else nfrq = 1; end
  655. if ~isempty(strmatch('time', dimtok)), ntim=length(data.time); else ntim = 1; end
  656. fastflag = all(data.cumtapcnt(:)==data.cumtapcnt(1));
  657. %create auto-spectra
  658. nchan = length(data.label);
  659. if fastflag
  660. % all trials have the same amount of tapers
  661. powspctrm = zeros(nrpt,nchan,nfrq,ntim);
  662. ntap = data.cumtapcnt(1);
  663. for p = 1:ntap
  664. powspctrm = powspctrm + abs(data.fourierspctrm(p:ntap:end,:,:,:,:)).^2;
  665. end
  666. powspctrm = powspctrm./ntap;
  667. else
  668. % different amount of tapers
  669. powspctrm = zeros(nrpt,nchan,nfrq,ntim)+i.*zeros(nrpt,nchan,nfrq,ntim);
  670. sumtapcnt = [0;cumsum(data.cumtapcnt(:))];
  671. for p = 1:nrpt
  672. indx = (sumtapcnt(p)+1):sumtapcnt(p+1);
  673. tmpdat = data.fourierspctrm(indx,:,:,:);
  674. powspctrm(p,:,:,:) = (sum(tmpdat.*conj(tmpdat),1))./data.cumtapcnt(p);
  675. end
  676. end
  677. %create cross-spectra
  678. if ~isempty(channelcmb),
  679. ncmb = size(channelcmb,1);
  680. cmbindx = zeros(ncmb,2);
  681. labelcmb = cell(ncmb,2);
  682. for k = 1:ncmb
  683. ch1 = find(strcmp(data.label, channelcmb(k,1)));
  684. ch2 = find(strcmp(data.label, channelcmb(k,2)));
  685. if ~isempty(ch1) && ~isempty(ch2),
  686. cmbindx(k,:) = [ch1 ch2];
  687. labelcmb(k,:) = data.label([ch1 ch2])';
  688. end
  689. end
  690. crsspctrm = zeros(nrpt,ncmb,nfrq,ntim)+i.*zeros(nrpt,ncmb,nfrq,ntim);
  691. if fastflag
  692. for p = 1:ntap
  693. tmpdat1 = data.fourierspctrm(p:ntap:end,cmbindx(:,1),:,:,:);
  694. tmpdat2 = data.fourierspctrm(p:ntap:end,cmbindx(:,2),:,:,:);
  695. crsspctrm = crsspctrm + tmpdat1.*conj(tmpdat2);
  696. end
  697. crsspctrm = crsspctrm./ntap;
  698. else
  699. for p = 1:nrpt
  700. indx = (sumtapcnt(p)+1):sumtapcnt(p+1);
  701. tmpdat1 = data.fourierspctrm(indx,cmbindx(:,1),:,:);
  702. tmpdat2 = data.fourierspctrm(indx,cmbindx(:,2),:,:);
  703. crsspctrm(p,:,:,:) = (sum(tmpdat1.*conj(tmpdat2),1))./data.cumtapcnt(p);
  704. end
  705. end
  706. data.crsspctrm = crsspctrm;
  707. data.labelcmb = labelcmb;
  708. end
  709. data.powspctrm = powspctrm;
  710. data = rmfield(data, 'fourierspctrm');
  711. if ntim>1,
  712. data.dimord = 'chan_freq_time';
  713. else
  714. data.dimord = 'chan_freq';
  715. end
  716. if nrpt>1,
  717. data.dimord = ['rpt_',data.dimord];
  718. end
  719. if flag, siz = size(data.crsspctrm); data.crsspctrm = reshape(data.crsspctrm, siz(2:end)); end
  720. elseif strcmp(current, 'fourier') && strcmp(desired, 'sparse')
  721. if isempty(channelcmb), error('no channel combinations are specified'); end
  722. dimtok = tokenize(data.dimord, '_');
  723. if ~isempty(strmatch('rpttap', dimtok)),
  724. nrpt = size(data.cumtapcnt,1);
  725. flag = 0;
  726. else
  727. nrpt = 1;
  728. flag = 1;
  729. end
  730. if ~isempty(strmatch('freq', dimtok)), nfrq=length(data.freq); else nfrq = 1; end
  731. if ~isempty(strmatch('time', dimtok)), ntim=length(data.time); else ntim = 1; end
  732. ncmb = size(channelcmb,1);
  733. cmbindx = zeros(ncmb,2);
  734. labelcmb = cell(ncmb,2);
  735. for k = 1:ncmb
  736. ch1 = find(strcmp(data.label, channelcmb(k,1)));
  737. ch2 = find(strcmp(data.label, channelcmb(k,2)));
  738. if ~isempty(ch1) && ~isempty(ch2),
  739. cmbindx(k,:) = [ch1 ch2];
  740. labelcmb(k,:) = data.label([ch1 ch2])';
  741. end
  742. end
  743. sumtapcnt = [0;cumsum(data.cumtapcnt(:))];
  744. fastflag = all(data.cumtapcnt(:)==data.cumtapcnt(1));
  745. if fastflag && nrpt>1
  746. ntap = data.cumtapcnt(1);
  747. % compute running sum across tapers
  748. siz = [size(data.fourierspctrm) 1];
  749. for p = 1:ntap
  750. indx = p:ntap:nrpt*ntap;
  751. if p==1.
  752. tmpc = zeros(numel(indx), size(cmbindx,1), siz(3), siz(4)) + ...
  753. 1i.*zeros(numel(indx), size(cmbindx,1), siz(3), siz(4));
  754. end
  755. for k = 1:size(cmbindx,1)
  756. tmpc(:,k,:,:) = data.fourierspctrm(indx,cmbindx(k,1),:,:).* ...
  757. conj(data.fourierspctrm(indx,cmbindx(k,2),:,:));
  758. end
  759. if p==1
  760. crsspctrm = tmpc;
  761. else
  762. crsspctrm = tmpc + crsspctrm;
  763. end
  764. end
  765. crsspctrm = crsspctrm./ntap;
  766. else
  767. crsspctrm = zeros(nrpt, ncmb, nfrq, ntim);
  768. for p = 1:nrpt
  769. indx = (sumtapcnt(p)+1):sumtapcnt(p+1);
  770. tmpdat1 = data.fourierspctrm(indx,cmbindx(:,1),:,:);
  771. tmpdat2 = data.fourierspctrm(indx,cmbindx(:,2),:,:);
  772. crsspctrm(p,:,:,:) = (sum(tmpdat1.*conj(tmpdat2),1))./data.cumtapcnt(p);
  773. end
  774. end
  775. data.crsspctrm = crsspctrm;
  776. data.labelcmb = labelcmb;
  777. data = rmfield(data, 'fourierspctrm');
  778. data = rmfield(data, 'label');
  779. if ntim>1,
  780. data.dimord = 'chan_freq_time';
  781. else
  782. data.dimord = 'chan_freq';
  783. end
  784. if nrpt>1,
  785. data.dimord = ['rpt_',data.dimord];
  786. end
  787. if flag, siz = size(data.crsspctrm); data.crsspctrm = reshape(data.crsspctrm, siz(2:end)); end
  788. elseif strcmp(current, 'fourier') && strcmp(desired, 'full')
  789. % this is how it is currently and the desired functionality of prepare_freq_matrices
  790. dimtok = tokenize(data.dimord, '_');
  791. if ~isempty(strmatch('rpttap', dimtok)),
  792. nrpt = size(data.cumtapcnt, 1);
  793. flag = 0;
  794. else
  795. nrpt = 1;
  796. flag = 1;
  797. end
  798. if ~isempty(strmatch('rpttap',dimtok)), nrpt=size(data.cumtapcnt, 1); else nrpt = 1; end
  799. if ~isempty(strmatch('freq', dimtok)), nfrq=length(data.freq); else nfrq = 1; end
  800. if ~isempty(strmatch('time', dimtok)), ntim=length(data.time); else ntim = 1; end
  801. if any(data.cumtapcnt(1,:) ~= data.cumtapcnt(1,1)), error('this only works when all frequencies have the same number of tapers'); end
  802. nchan = length(data.label);
  803. crsspctrm = zeros(nrpt,nchan,nchan,nfrq,ntim);
  804. sumtapcnt = [0;cumsum(data.cumtapcnt(:,1))];
  805. for k = 1:ntim
  806. for m = 1:nfrq
  807. for p = 1:nrpt
  808. %FIXME speed this up in the case that all trials have equal number of tapers
  809. indx = (sumtapcnt(p)+1):sumtapcnt(p+1);
  810. tmpdat = transpose(data.fourierspctrm(indx,:,m,k));
  811. crsspctrm(p,:,:,m,k) = (tmpdat*tmpdat')./data.cumtapcnt(p);
  812. clear tmpdat;
  813. end
  814. end
  815. end
  816. data.crsspctrm = crsspctrm;
  817. data = rmfield(data, 'fourierspctrm');
  818. if ntim>1,
  819. data.dimord = 'chan_chan_freq_time';
  820. else
  821. data.dimord = 'chan_chan_freq';
  822. end
  823. if nrpt>1,
  824. data.dimord = ['rpt_',data.dimord];
  825. end
  826. % remove first singleton dimension
  827. if flag || nrpt==1, siz = size(data.crsspctrm); data.crsspctrm = reshape(data.crsspctrm, siz(2:end)); end
  828. elseif strcmp(current, 'fourier') && strcmp(desired, 'fullfast'),
  829. dimtok = tokenize(data.dimord, '_');
  830. nrpt = size(data.fourierspctrm, 1);
  831. nchn = numel(data.label);
  832. nfrq = numel(data.freq);
  833. if ~isempty(strmatch('time', dimtok)), ntim=numel(data.time); else ntim = 1; end
  834. data.fourierspctrm = reshape(data.fourierspctrm, [nrpt nchn nfrq*ntim]);
  835. data.fourierspctrm(~isfinite(data.fourierspctrm)) = 0;
  836. crsspctrm = complex(zeros(nchn,nchn,nfrq*ntim));
  837. for k = 1:nfrq*ntim
  838. tmp = transpose(data.fourierspctrm(:,:,k));
  839. n = sum(tmp~=0,2);
  840. crsspctrm(:,:,k) = tmp*tmp'./n(1);
  841. end
  842. data = rmfield(data, 'fourierspctrm');
  843. data.crsspctrm = reshape(crsspctrm, [nchn nchn nfrq ntim]);
  844. if isfield(data, 'time'),
  845. data.dimord = 'chan_chan_freq_time';
  846. else
  847. data.dimord = 'chan_chan_freq';
  848. end
  849. if isfield(data, 'trialinfo'), data = rmfield(data, 'trialinfo'); end;
  850. if isfield(data, 'sampleinfo'), data = rmfield(data, 'sampleinfo'); end;
  851. if isfield(data, 'cumsumcnt'), data = rmfield(data, 'cumsumcnt'); end;
  852. if isfield(data, 'cumtapcnt'), data = rmfield(data, 'cumtapcnt'); end;
  853. end % convert to the requested bivariate representation
  854. % from one bivariate representation to another
  855. if isequal(current, desired)
  856. % nothing to do
  857. elseif (strcmp(current, 'full') && strcmp(desired, 'fourier')) || ...
  858. (strcmp(current, 'sparse') && strcmp(desired, 'fourier')) || ...
  859. (strcmp(current, 'sparsewithpow') && strcmp(desired, 'fourier'))
  860. % this is not possible
  861. error('converting the cross-spectrum into a Fourier representation is not possible');
  862. elseif strcmp(current, 'full') && strcmp(desired, 'sparsewithpow')
  863. error('not yet implemented');
  864. elseif strcmp(current, 'sparse') && strcmp(desired, 'sparsewithpow')
  865. % convert back to crsspctrm/powspctrm representation: useful for plotting functions etc
  866. indx = labelcmb2indx(data.labelcmb);
  867. autoindx = indx(indx(:,1)==indx(:,2), 1);
  868. cmbindx = setdiff([1:size(indx,1)]', autoindx);
  869. if strcmp(data.dimord(1:3), 'rpt')
  870. data.powspctrm = data.crsspctrm(:, autoindx, :, :);
  871. data.crsspctrm = data.crsspctrm(:, cmbindx, :, :);
  872. else
  873. data.powspctrm = data.crsspctrm(autoindx, :, :);
  874. data.crsspctrm = data.crsspctrm(cmbindx, :, :);
  875. end
  876. data.label = data.labelcmb(autoindx,1);
  877. data.labelcmb = data.labelcmb(cmbindx, :);
  878. if isempty(cmbindx)
  879. data = rmfield(data, 'crsspctrm');
  880. data = rmfield(data, 'labelcmb');
  881. end
  882. elseif strcmp(current, 'full') && strcmp(desired, 'sparse')
  883. dimtok = tokenize(data.dimord, '_');
  884. if ~isempty(strmatch('rpt', dimtok)), nrpt=size(data.cumtapcnt,1); else nrpt = 1; end
  885. if ~isempty(strmatch('freq', dimtok)), nfrq=numel(data.freq); else nfrq = 1; end
  886. if ~isempty(strmatch('time', dimtok)), ntim=numel(data.time); else ntim = 1; end
  887. nchan = length(data.label);
  888. ncmb = nchan*nchan;
  889. labelcmb = cell(ncmb, 2);
  890. cmbindx = zeros(nchan, nchan);
  891. k = 1;
  892. for j=1:nchan
  893. for m=1:nchan
  894. labelcmb{k, 1} = data.label{m};
  895. labelcmb{k, 2} = data.label{j};
  896. cmbindx(m,j) = k;
  897. k = k+1;
  898. end
  899. end
  900. % reshape all possible fields
  901. fn = fieldnames(data);
  902. for ii=1:numel(fn)
  903. if numel(data.(fn{ii})) == nrpt*ncmb*nfrq*ntim;
  904. if nrpt>1,
  905. data.(fn{ii}) = reshape(data.(fn{ii}), nrpt, ncmb, nfrq, ntim);
  906. else
  907. data.(fn{ii}) = reshape(data.(fn{ii}), ncmb, nfrq, ntim);
  908. end
  909. end
  910. end
  911. % remove obsolete fields
  912. data = rmfield(data, 'label');
  913. try, data = rmfield(data, 'dof'); end
  914. % replace updated fields
  915. data.labelcmb = labelcmb;
  916. if ntim>1,
  917. data.dimord = 'chancmb_freq_time';
  918. else
  919. data.dimord = 'chancmb_freq';
  920. end
  921. if nrpt>1,
  922. data.dimord = ['rpt_',data.dimord];
  923. end
  924. elseif strcmp(current, 'sparsewithpow') && strcmp(desired, 'sparse')
  925. % this representation for sparse data contains autospectra
  926. % as e.g. {'A' 'A'} in labelcmb
  927. if isfield(data, 'crsspctrm'),
  928. dimtok = tokenize(data.dimord, '_');
  929. catdim = match_str(dimtok, {'chan' 'chancmb'});
  930. data.crsspctrm = cat(catdim, data.powspctrm, data.crsspctrm);
  931. data.labelcmb = [data.label(:) data.label(:); data.labelcmb];
  932. data = rmfield(data, 'powspctrm');
  933. else
  934. data.crsspctrm = data.powspctrm;
  935. data.labelcmb = [data.label(:) data.label(:)];
  936. data = rmfield(data, 'powspctrm');
  937. end
  938. data = rmfield(data, 'label');
  939. elseif strcmp(current, 'sparse') && strcmp(desired, 'full')
  940. dimtok = tokenize(data.dimord, '_');
  941. if ~isempty(strmatch('rpt', dimtok)), nrpt=size(data.cumtapcnt,1); else nrpt = 1; end
  942. if ~isempty(strmatch('freq', dimtok)), nfrq=numel(data.freq); else nfrq = 1; end
  943. if ~isempty(strmatch('time', dimtok)), ntim=numel(data.time); else ntim = 1; end
  944. if ~isfield(data, 'label')
  945. % ensure that the bivariate spectral factorization results can be
  946. % processed. FIXME this is experimental and will not work if the user
  947. % did something weird before
  948. for k = 1:numel(data.labelcmb)
  949. tmp = tokenize(data.labelcmb{k}, '[');
  950. data.labelcmb{k} = tmp{1};
  951. end
  952. data.label = unique(data.labelcmb(:));
  953. end
  954. nchan = length(data.label);
  955. ncmb = size(data.labelcmb,1);
  956. cmbindx = zeros(nchan,nchan);
  957. for k = 1:size(data.labelcmb,1)
  958. ch1 = find(strcmp(data.label, data.labelcmb(k,1)));
  959. ch2 = find(strcmp(data.label, data.labelcmb(k,2)));
  960. if ~isempty(ch1) && ~isempty(ch2),
  961. cmbindx(ch1,ch2) = k;
  962. end
  963. end
  964. complete = all(cmbindx(:)~=0);
  965. fn = fieldnames(data);
  966. for ii=1:numel(fn)
  967. if numel(data.(fn{ii})) == nrpt*ncmb*nfrq*ntim;
  968. if nrpt==1,
  969. data.(fn{ii}) = reshape(data.(fn{ii}), [nrpt ncmb nfrq ntim]);
  970. end
  971. tmpall = nan(nrpt,nchan,nchan,nfrq,ntim);
  972. for j = 1:nrpt
  973. for k = 1:ntim
  974. for m = 1:nfrq
  975. tmpdat = nan(nchan,nchan);
  976. indx = find(cmbindx);
  977. if ~complete
  978. % this realizes the missing combinations to be represented as the
  979. % conjugate of the corresponding combination across the diagonal
  980. tmpdat(indx) = reshape(data.(fn{ii})(j,cmbindx(indx),m,k),[numel(indx) 1]);
  981. tmpdat = ctranspose(tmpdat);
  982. end
  983. tmpdat(indx) = reshape(data.(fn{ii})(j,cmbindx(indx),m,k),[numel(indx) 1]);
  984. tmpall(j,:,:,m,k) = tmpdat;
  985. end % for m
  986. end % for k
  987. end % for j
  988. % replace the data in the old representation with the new representation
  989. if nrpt>1,
  990. data.(fn{ii}) = tmpall;
  991. else
  992. data.(fn{ii}) = reshape(tmpall, [nchan nchan nfrq ntim]);
  993. end
  994. end % if numel
  995. end % for ii
  996. % remove obsolete fields
  997. try, data = rmfield(data, 'powspctrm'); end
  998. try, data = rmfield(data, 'labelcmb'); end
  999. try, data = rmfield(data, 'dof'); end
  1000. if ntim>1,
  1001. data.dimord = 'chan_chan_freq_time';
  1002. else
  1003. data.dimord = 'chan_chan_freq';
  1004. end
  1005. if nrpt>1,
  1006. data.dimord = ['rpt_',data.dimord];
  1007. end
  1008. elseif strcmp(current, 'sparse') && strcmp(desired, 'fullfast')
  1009. dimtok = tokenize(data.dimord, '_');
  1010. if ~isempty(strmatch('rpt', dimtok)), nrpt=size(data.cumtapcnt,1); else nrpt = 1; end
  1011. if ~isempty(strmatch('freq', dimtok)), nfrq=numel(data.freq); else nfrq = 1; end
  1012. if ~isempty(strmatch('time', dimtok)), ntim=numel(data.time); else ntim = 1; end
  1013. if ~isfield(data, 'label')
  1014. data.label = unique(data.labelcmb(:));
  1015. end
  1016. nchan = length(data.label);
  1017. ncmb = size(data.labelcmb,1);
  1018. cmbindx = zeros(nchan,nchan);
  1019. for k = 1:size(data.labelcmb,1)
  1020. ch1 = find(strcmp(data.label, data.labelcmb(k,1)));
  1021. ch2 = find(strcmp(data.label, data.labelcmb(k,2)));
  1022. if ~isempty(ch1) && ~isempty(ch2),
  1023. cmbindx(ch1,ch2) = k;
  1024. end
  1025. end
  1026. complete = all(cmbindx(:)~=0);
  1027. fn = fieldnames(data);
  1028. for ii=1:numel(fn)
  1029. if numel(data.(fn{ii})) == nrpt*ncmb*nfrq*ntim;
  1030. if nrpt==1,
  1031. data.(fn{ii}) = reshape(data.(fn{ii}), [nrpt ncmb nfrq ntim]);
  1032. end
  1033. tmpall = nan(nchan,nchan,nfrq,ntim);
  1034. for k = 1:ntim
  1035. for m = 1:nfrq
  1036. tmpdat = nan(nchan,nchan);
  1037. indx = find(cmbindx);
  1038. if ~complete
  1039. % this realizes the missing combinations to be represented as the
  1040. % conjugate of the corresponding combination across the diagonal
  1041. tmpdat(indx) = reshape(nanmean(data.(fn{ii})(:,cmbindx(indx),m,k)),[numel(indx) 1]);
  1042. tmpdat = ctranspose(tmpdat);
  1043. end
  1044. tmpdat(indx) = reshape(nanmean(data.(fn{ii})(:,cmbindx(indx),m,k)),[numel(indx) 1]);
  1045. tmpall(:,:,m,k) = tmpdat;
  1046. end % for m
  1047. end % for k
  1048. % replace the data in the old representation with the new representation
  1049. if nrpt>1,
  1050. data.(fn{ii}) = tmpall;
  1051. else
  1052. data.(fn{ii}) = reshape(tmpall, [nchan nchan nfrq ntim]);
  1053. end
  1054. end % if numel
  1055. end % for ii
  1056. % remove obsolete fields
  1057. try, data = rmfield(data, 'powspctrm'); end
  1058. try, data = rmfield(data, 'labelcmb'); end
  1059. try, data = rmfield(data, 'dof'); end
  1060. if ntim>1,
  1061. data.dimord = 'chan_chan_freq_time';
  1062. else
  1063. data.dimord = 'chan_chan_freq';
  1064. end
  1065. elseif strcmp(current, 'sparsewithpow') && strcmp(desired, 'full')
  1066. % this is how is currently done in prepare_freq_matrices
  1067. data = ft_checkdata(data, 'cmbrepresentation', 'sparse');
  1068. data = ft_checkdata(data, 'cmbrepresentation', 'full');
  1069. end
  1070. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1071. % convert to new source representation
  1072. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1073. function [output] = fixsource(input, varargin)
  1074. % FIXSOURCE converts old style source structures into new style source structures and the
  1075. % other way around
  1076. %
  1077. % Use as:
  1078. % fixsource(input, type)
  1079. % where input is a source structure,
  1080. %
  1081. % Typically, old style source structures contain
  1082. % avg.XXX or trial.XXX fields
  1083. %
  1084. % The new style source structure contains:
  1085. % source.pos
  1086. % source.dim (optional, if the list of positions describes a 3D volume
  1087. % source.XXX the old style subfields in avg/trial
  1088. % source.XXXdimord string how to interpret the respective XXX field:
  1089. % e.g. source.leadfield = cell(1,Npos), source.leadfielddimord = '{pos}_chan_ori'
  1090. % source.mom = cell(1,Npos), source.momdimord = '{pos}_ori_rpttap'
  1091. type = ft_getopt(varargin, 'type');
  1092. haspow = ft_getopt(varargin, 'haspow');
  1093. if isempty(type), type = 'old'; end
  1094. if isempty(haspow), haspow = 'no'; end
  1095. fnames = fieldnames(input);
  1096. tmp = cell2mat(strfind(fnames, 'dimord')); %get dimord like fields
  1097. if any(tmp>1),
  1098. current = 'new';
  1099. elseif any(tmp==1),
  1100. %don't know what to do yet data is JM's own invention
  1101. current = 'old';
  1102. else
  1103. current = 'old';
  1104. end
  1105. if strcmp(current, type),
  1106. %do nothing
  1107. output = input;
  1108. %return
  1109. elseif strcmp(current, 'old') && strcmp(type, 'new'),
  1110. %go from old to new
  1111. if isfield(input, 'avg'),
  1112. stuff = getfield(input, 'avg');
  1113. output = rmfield(input, 'avg');
  1114. elseif isfield(input, 'trial'),
  1115. stuff = getfield(input, 'trial');
  1116. output = rmfield(input, 'trial');
  1117. else
  1118. %this could occur later in the pipeline, e.g. when doing group statistics using individual subject
  1119. %descriptive statistics
  1120. error('the input does not contain an avg or trial field');
  1121. end
  1122. %-------------------------------------------------
  1123. %remove and rename the specified fields if present
  1124. removefields = {'xgrid';'ygrid';'zgrid';'method'};
  1125. renamefields = {'frequency' 'freq'; 'csdlabel' 'orilabel'};
  1126. fnames = fieldnames(output);
  1127. for k = 1:numel(fnames)
  1128. ix = strmatch(fnames{k}, removefields);
  1129. if ~isempty(ix),
  1130. output = rmfield(output, fnames{k});
  1131. end
  1132. ix = strmatch(fnames{k}, renamefields(:,1), 'exact');
  1133. if ~isempty(ix),
  1134. output = setfield(output, renamefields{ix,2}, ...
  1135. getfield(output, renamefields{ix,1}));
  1136. output = rmfield(output, fnames{k});
  1137. end
  1138. end
  1139. %----------------------------------------------------------------------
  1140. %put the stuff originally in avg or trial one level up in the structure
  1141. fnames = fieldnames(stuff(1));
  1142. npos = size(input.pos,1);
  1143. nrpt = numel(stuff);
  1144. for k = 1:numel(fnames)
  1145. if nrpt>1,
  1146. %multiple trials
  1147. %(or subjects FIXME not yet implemented, nor tested)
  1148. tmp = getfield(stuff(1), fnames{k});
  1149. siz = size(tmp);
  1150. if isfield(input, 'cumtapcnt') && strcmp(fnames{k}, 'mom')
  1151. %pcc based mom is orixrpttap
  1152. %tranpose to keep manageable
  1153. for kk = 1:numel(input.inside)
  1154. indx = input.inside(kk);
  1155. tmp{indx} = permute(tmp{indx}, [2 1 3]);
  1156. end
  1157. nrpttap = sum(input.cumtapcnt);
  1158. sizvox = [size(tmp{input.inside(1)}) 1];
  1159. sizvox = [nrpttap sizvox(2:end)];
  1160. elseif strcmp(fnames{k}, 'mom'),
  1161. %this is then probably not a frequency based mom
  1162. nrpttap = numel(stuff);
  1163. sizvox = [size(tmp{input.inside(1)}) 1];
  1164. sizvox = [nrpttap sizvox];
  1165. elseif iscell(tmp)
  1166. nrpttap = numel(stuff);
  1167. sizvox = [size(tmp{input.inside(1)}) 1];
  1168. sizvox = [nrpttap sizvox];
  1169. end
  1170. if siz(1) ~= npos && siz(2) ==npos,
  1171. tmp = transpose(tmp);
  1172. end
  1173. if iscell(tmp)
  1174. %allocate memory for cell-array
  1175. tmpall = cell(npos,1);
  1176. for n = 1:numel(input.inside)
  1177. tmpall{input.inside(n)} = zeros(sizvox);
  1178. end
  1179. else
  1180. %allocate memory for matrix
  1181. tmpall = zeros([npos nrpt siz(2:end)]);
  1182. end
  1183. cnt = 0;
  1184. for m = 1:nrpt
  1185. tmp = getfield(stuff(m), fnames{k});
  1186. siz = size(tmp);
  1187. if siz(1) ~= npos && siz(2) ==npos,
  1188. tmp = transpose(tmp);
  1189. end
  1190. if ~iscell(tmp),
  1191. tmpall(:,m,:,:,:) = tmp;
  1192. else
  1193. for n = 1:numel(input.inside)
  1194. indx = input.inside(n);
  1195. tmpdat = tmp{indx};
  1196. if isfield(input, 'cumtapcnt') && strcmp(fnames{k}, 'mom'),
  1197. if n==1, siz1 = size(tmpdat,2); end
  1198. else
  1199. if n==1, siz1 = 1; end
  1200. end
  1201. tmpall{indx}(cnt+[1:siz1],:,:,:,:) = tmpdat;
  1202. if n==numel(input.inside), cnt = cnt + siz1; end
  1203. end
  1204. end
  1205. end
  1206. output = setfield(output, fnames{k}, tmpall);
  1207. newdimord = createdimord(output, fnames{k}, 1);
  1208. if ~isempty(newdimord)
  1209. output = setfield(output, [fnames{k},'dimord'], newdimord);
  1210. end
  1211. else
  1212. tmp = getfield(stuff, fnames{k});
  1213. siz = size(tmp);
  1214. if isfield(input, 'cumtapcnt') && strcmp(fnames{k}, 'mom')
  1215. %pcc based mom is orixrpttap
  1216. %tranpose to keep manageable
  1217. for kk = 1:numel(input.inside)
  1218. indx = input.inside(kk);
  1219. tmp{indx} = permute(tmp{indx}, [2 1 3]);
  1220. end
  1221. end
  1222. if siz(1) ~= npos && siz(2) ==npos,
  1223. tmp = transpose(tmp);
  1224. end
  1225. output = setfield(output, fnames{k}, tmp);
  1226. newdimord = createdimord(output, fnames{k});
  1227. if ~isempty(newdimord)
  1228. output = setfield(output, [fnames{k},'dimord'], newdimord);
  1229. end
  1230. end
  1231. end
  1232. if isfield(output, 'csdlabel')
  1233. output = setfield(output, 'orilabel', getfield(output, 'csdlabel'));
  1234. output = rmfield(output, 'csdlabel');
  1235. end
  1236. if isfield(output, 'leadfield')
  1237. % add dimord to leadfield as well. since the leadfield is not in
  1238. % the original .avg or .trial field it has not yet been taken care of
  1239. output.leadfielddimord = createdimord(output, 'leadfield');
  1240. end
  1241. if isfield(output, 'ori')
  1242. % convert cell-array ori into matrix
  1243. ori = zeros(3,npos) + nan;
  1244. try,
  1245. ori(:,output.inside) = cat(2, output.ori{output.inside});
  1246. catch
  1247. %when oris are in wrong orientation (row rather than column)
  1248. for k = 1:numel(output.inside)
  1249. ori(:,output.inside(k)) = output.ori{output.inside(k)}';
  1250. end
  1251. end
  1252. output.ori = ori;
  1253. end
  1254. current = 'new';
  1255. elseif strcmp(current, 'new') && strcmp(type, 'old')
  1256. %go from new to old
  1257. error('not implemented yet');
  1258. end
  1259. if strcmp(current, 'new') && strcmp(haspow, 'yes'),
  1260. %----------------------------------------------
  1261. %convert mom into pow if requested and possible
  1262. convert = 0;
  1263. if isfield(output, 'mom') && size(output.mom{output.inside(1)},2)==1,
  1264. convert = 1;
  1265. else
  1266. warning('conversion from mom to pow is not possible, either because there is no mom in the data, or because the dimension of mom>1. in that case call ft_sourcedescriptives first with cfg.projectmom');
  1267. end
  1268. if isfield(output, 'cumtapcnt')
  1269. convert = 1 & convert;
  1270. else
  1271. warning('conversion from mom to pow will not be done, because cumtapcnt is missing');
  1272. end
  1273. if convert,
  1274. npos = size(output.pos,1);
  1275. nrpt = size(output.cumtapcnt,1);
  1276. tmpmom = cat(2,output.mom{output.inside});
  1277. tmppow = zeros(npos, nrpt);
  1278. tapcnt = [0;cumsum(output.cumtapcnt(:))];
  1279. for k = 1:nrpt
  1280. ntap = tapcnt(k+1)-tapcnt(k);
  1281. tmppow(output.inside,k) = sum(abs(tmpmom((tapcnt(k)+1):tapcnt(k+1),:)).^2,1)./ntap;
  1282. end
  1283. output.pow = tmppow;
  1284. output.powdimord = ['pos_rpt_freq'];
  1285. end
  1286. elseif strcmp(current, 'old') && strcmp(haspow, 'yes')
  1287. warning('construction of single trial power estimates is not implemented here using old style source representation');
  1288. end
  1289. %--------------------------------------------------------
  1290. function [dimord] = createdimord(output, fname, rptflag);
  1291. if nargin==2, rptflag = 0; end
  1292. tmp = getfield(output, fname);
  1293. dimord = '';
  1294. dimnum = 1;
  1295. hasori = isfield(output, 'ori'); %if not, this is probably singleton and not relevant at the end
  1296. if iscell(tmp) && (size(output.pos,1)==size(tmp,dimnum) || size(output.pos,1)==size(tmp,2))
  1297. dimord = [dimord,'{pos}'];
  1298. dimnum = dimnum + 1;
  1299. elseif ~iscell(tmp) && size(output.pos,1)==size(tmp,dimnum)
  1300. dimord = [dimord,'pos'];
  1301. dimnum = dimnum + 1;
  1302. end
  1303. switch fname
  1304. case 'cov'
  1305. if hasori, dimord = [dimord,'_ori_ori']; end;
  1306. case 'csd'
  1307. if hasori, dimord = [dimord,'_ori_ori']; end;
  1308. case 'csdlabel'
  1309. dimord = dimord;
  1310. case 'filter'
  1311. dimord = [dimord,'_ori_chan'];
  1312. case 'leadfield'
  1313. %if hasori,
  1314. dimord = [dimord,'_chan_ori'];
  1315. %else
  1316. % dimord = [dimord,'_chan'];
  1317. %end
  1318. case 'mom'
  1319. if isfield(output, 'cumtapcnt') && sum(output.cumtapcnt)==size(tmp{output.inside(1)},1)
  1320. if hasori,
  1321. dimord = [dimord,'_rpttap_ori'];
  1322. else
  1323. dimord = [dimord,'_rpttap'];
  1324. end
  1325. elseif isfield(output, 'time')
  1326. if rptflag,
  1327. dimord = [dimord,'_rpt'];
  1328. dimnum = dimnum + 1;
  1329. end
  1330. if numel(output.time)==size(tmp{output.inside(1)},dimnum)
  1331. dimord = [dimord,'_ori_time'];
  1332. end
  1333. end
  1334. if isfield(output, 'freq') && numel(output.freq)>1,
  1335. dimord = [dimord,'_freq'];
  1336. end
  1337. case 'nai'
  1338. if isfield(output, 'freq') && numel(output.freq)==size(tmp,dimnum)
  1339. dimord = [dimord,'_freq'];
  1340. end
  1341. case 'noise'
  1342. if isfield(output, 'freq') && numel(output.freq)==size(tmp,dimnum)
  1343. dimord = [dimord,'_freq'];
  1344. end
  1345. case 'noisecsd'
  1346. if hasori, dimord = [dimord,'_ori_ori']; end
  1347. case 'ori'
  1348. dimord = '';
  1349. case 'pow'
  1350. if isfield(output, 'cumtapcnt') && size(output.cumtapcnt,1)==size(tmp,dimnum)
  1351. dimord = [dimord,'_rpt'];
  1352. dimnum = dimnum + 1;
  1353. end
  1354. if isfield(output, 'freq') && numel(output.freq)>1 && numel(output.freq)==size(tmp,dimnum)
  1355. dimord = [dimord,'_freq'];
  1356. dimnum = dimnum+1;
  1357. end
  1358. if isfield(output, 'time') && numel(output.time)>1 && numel(output.time)==size(tmp,dimnum)
  1359. dimord = [dimord,'_time'];
  1360. dimnum = dimnum+1;
  1361. end
  1362. otherwise
  1363. warning('skipping unknown fieldname %s', fname);
  1364. %error(sprintf('unknown fieldname %s', fname));
  1365. end
  1366. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1367. % convert between datatypes
  1368. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1369. function data = comp2raw(data)
  1370. % just remove the component topographies
  1371. data = rmfield(data, 'topo');
  1372. data = rmfield(data, 'topolabel');
  1373. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1374. % convert between datatypes
  1375. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1376. function data = volume2source(data)
  1377. if isfield(data, 'dimord')
  1378. % it is a modern source description
  1379. else
  1380. % it is an old-fashioned source description
  1381. xgrid = 1:data.dim(1);
  1382. ygrid = 1:data.dim(2);
  1383. zgrid = 1:data.dim(3);
  1384. [x y z] = ndgrid(xgrid, ygrid, zgrid);
  1385. data.pos = warp_apply(data.transform, [x(:) y(:) z(:)]);
  1386. end
  1387. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1388. % convert between datatypes
  1389. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1390. function data = source2volume(data)
  1391. if isfield(data, 'dimord')
  1392. % it is a modern source description
  1393. %this part depends on the assumption that the list of positions is describing a full 3D volume in
  1394. %an ordered way which allows for the extraction of a transformation matrix
  1395. %i.e. slice by slice
  1396. try,
  1397. if isfield(data, 'dim'),
  1398. data.dim = pos2dim(data.pos, data.dim);
  1399. else
  1400. data.dim = pos2dim(data);
  1401. end
  1402. catch
  1403. end
  1404. end
  1405. if isfield(data, 'dim') && length(data.dim)>=3,
  1406. % it is an old-fashioned source description, or the source describes a regular 3D volume in pos
  1407. xgrid = 1:data.dim(1);
  1408. ygrid = 1:data.dim(2);
  1409. zgrid = 1:data.dim(3);
  1410. [x y z] = ndgrid(xgrid, ygrid, zgrid);
  1411. ind = [x(:) y(:) z(:)]; % these are the positions expressed in voxel indices along each of the three axes
  1412. pos = data.pos; % these are the positions expressed in head coordinates
  1413. % represent the positions in a manner that is compatible with the homogeneous matrix multiplication,
  1414. % i.e. pos = H * ind
  1415. ind = ind'; ind(4,:) = 1;
  1416. pos = pos'; pos(4,:) = 1;
  1417. % recompute the homogeneous transformation matrix
  1418. data.transform = pos / ind;
  1419. end
  1420. % remove the unwanted fields
  1421. if isfield(data, 'pos'), data = rmfield(data, 'pos'); end
  1422. if isfield(data, 'xgrid'), data = rmfield(data, 'xgrid'); end
  1423. if isfield(data, 'ygrid'), data = rmfield(data, 'ygrid'); end
  1424. if isfield(data, 'zgrid'), data = rmfield(data, 'zgrid'); end
  1425. % make inside a volume
  1426. data = fixinside(data, 'logical');
  1427. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1428. % convert between datatypes
  1429. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1430. function data = freq2raw(freq)
  1431. if strcmp(freq.dimord, 'rpt_chan_freq_time')
  1432. dat = freq.powspctrm;
  1433. elseif strcmp(freq.dimord, 'rpttap_chan_freq_time')
  1434. warning('converting fourier representation into raw data format. this is experimental code');
  1435. dat = freq.fourierspctrm;
  1436. else
  1437. error('this only works for dimord=''rpt_chan_freq_time''');
  1438. end
  1439. nrpt = size(dat,1);
  1440. nchan = size(dat,2);
  1441. nfreq = size(dat,3);
  1442. ntime = size(dat,4);
  1443. data = [];
  1444. % create the channel labels like "MLP11@12Hz""
  1445. k = 0;
  1446. for i=1:nfreq
  1447. for j=1:nchan
  1448. k = k+1;
  1449. data.label{k} = sprintf('%s@%dHz', freq.label{j}, freq.freq(i));
  1450. end
  1451. end
  1452. % reshape and copy the data as if it were timecourses only
  1453. for i=1:nrpt
  1454. data.time{i} = freq.time;
  1455. data.trial{i} = reshape(dat(i,:,:,:), nchan*nfreq, ntime);
  1456. if any(isnan(data.trial{i}(1,:))),
  1457. tmp = data.trial{i}(1,:);
  1458. begsmp = find(isfinite(tmp),1, 'first');
  1459. endsmp = find(isfinite(tmp),1, 'last' );
  1460. data.trial{i} = data.trial{i}(:, begsmp:endsmp);
  1461. data.time{i} = data.time{i}(begsmp:endsmp);
  1462. end
  1463. end
  1464. nsmp = cellfun('size',data.time,2);
  1465. seln = find(nsmp>1,1, 'first');
  1466. data.fsample = 1/(data.time{seln}(2)-data.time{seln}(1));
  1467. if isfield(freq, 'trialinfo'), data.trialinfo = freq.trialinfo; end;
  1468. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1469. % convert between datatypes
  1470. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1471. function [data] = raw2timelock(data)
  1472. nsmp = cellfun('size',data.time,2);
  1473. data = ft_checkdata(data, 'hassampleinfo', 'yes');
  1474. ntrial = numel(data.trial);
  1475. nchan = numel(data.label);
  1476. if ntrial==1
  1477. data.time = data.time{1};
  1478. data.avg = data.trial{1};
  1479. data = rmfield(data, 'trial');
  1480. data.dimord = 'chan_time';
  1481. else
  1482. % determine the location of the trials relative to the resulting combined time axis
  1483. begtime = cellfun(@min, data.time);
  1484. endtime = cellfun(@max, data.time);
  1485. begsmp = round((begtime - min(begtime)) * data.fsample) + 1;
  1486. endsmp = round((endtime - min(begtime)) * data.fsample) + 1;
  1487. % create a combined time axis and concatenate all trials
  1488. tmptime = min(begtime):(1/data.fsample):max(endtime);
  1489. tmptrial = zeros(ntrial, nchan, length(tmptime)) + nan;
  1490. for i=1:ntrial
  1491. tmptrial(i,:,begsmp(i):endsmp(i)) = data.trial{i};
  1492. end
  1493. % update the sampleinfo
  1494. begpad = begsmp - min(begsmp);
  1495. endpad = max(endsmp) - endsmp;
  1496. if isfield(data, 'sampleinfo')
  1497. data.sampleinfo = data.sampleinfo + [-begpad(:) endpad(:)];
  1498. end
  1499. % construct the output timelocked data
  1500. % data.avg = reshape(nanmean(tmptrial, 1), nchan, length(tmptime));
  1501. % data.var = reshape(nanvar (tmptrial, [], 1), nchan, length(tmptime))
  1502. % data.dof = reshape(sum(~isnan(tmptrial), 1), nchan, length(tmptime));
  1503. data.trial = tmptrial;
  1504. data.time = tmptime;
  1505. data.dimord = 'rpt_chan_time';
  1506. data = rmfield(data, 'fsample'); % fsample in timelock data is obsolete
  1507. end
  1508. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1509. % convert between datatypes
  1510. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1511. function [data] = timelock2raw(data)
  1512. try
  1513. nsmp = cellfun('size',data.time,2);
  1514. catch
  1515. nsmp = size(data.time,2);
  1516. end
  1517. switch data.dimord
  1518. case 'chan_time'
  1519. data.trial{1} = data.avg;
  1520. data.time = {data.time};
  1521. data = rmfield(data, 'avg');
  1522. seln = find(nsmp>1,1, 'first');
  1523. data.fsample = 1/(data.time{seln}(2)-data.time{seln}(1));
  1524. case 'rpt_chan_time'
  1525. tmptrial = {};
  1526. tmptime = {};
  1527. ntrial = size(data.trial,1);
  1528. nchan = size(data.trial,2);
  1529. ntime = size(data.trial,3);
  1530. for i=1:ntrial
  1531. tmptrial{i} = reshape(data.trial(i,:,:), [nchan, ntime]);
  1532. tmptime{i} = data.time;
  1533. end
  1534. data = rmfield(data, 'trial');
  1535. data.trial = tmptrial;
  1536. data.time = tmptime;
  1537. seln = find(nsmp>1,1, 'first');
  1538. data.fsample = 1/(data.time{seln}(2)-data.time{seln}(1));
  1539. case 'subj_chan_time'
  1540. tmptrial = {};
  1541. tmptime = {};
  1542. ntrial = size(data.individual,1);
  1543. nchan = size(data.individual,2);
  1544. ntime = size(data.individual,3);
  1545. for i=1:ntrial
  1546. tmptrial{i} = reshape(data.individual(i,:,:), [nchan, ntime]);
  1547. tmptime{i} = data.time;
  1548. end
  1549. data = rmfield(data, 'individual');
  1550. data.trial = tmptrial;
  1551. data.time = tmptime;
  1552. seln = find(nsmp>1,1, 'first');
  1553. data.fsample = 1/(data.time{seln}(2)-data.time{seln}(1));
  1554. otherwise
  1555. error('unsupported dimord');
  1556. end
  1557. % remove the unwanted fields
  1558. if isfield(data, 'avg'), data = rmfield(data, 'avg'); end
  1559. if isfield(data, 'var'), data = rmfield(data, 'var'); end
  1560. if isfield(data, 'cov'), data = rmfield(data, 'cov'); end
  1561. if isfield(data, 'dimord'), data = rmfield(data, 'dimord'); end
  1562. if isfield(data, 'numsamples'), data = rmfield(data, 'numsamples'); end
  1563. if isfield(data, 'dof'), data = rmfield(data, 'dof'); end
  1564. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1565. % convert between datatypes
  1566. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1567. function [data] = chan2freq(data)
  1568. data.dimord = [data.dimord '_freq'];
  1569. data.freq = 0;
  1570. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1571. % convert between datatypes
  1572. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1573. function [data] = chan2timelock(data)
  1574. data.dimord = [data.dimord '_time'];
  1575. data.time = 0;
  1576. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1577. % convert between datatypes
  1578. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1579. function [spike] = raw2spike(data)
  1580. % Copyright (C) 2010, Martin Vinck
  1581. fprintf('converting raw data into spike data\n');
  1582. % try to do the conversion
  1583. nTrials = length(data.trial);
  1584. s = 0;
  1585. for iTrial = 1:nTrials
  1586. r = round(data.trial{iTrial})./data.trial{iTrial};
  1587. s = s + double(all((isnan(r) | r==1),2) & all(data.trial{iTrial}>=0,2));
  1588. end
  1589. spikesel = find(s==nTrials);
  1590. nUnits = length(spikesel);
  1591. if nUnits==0, error('cannot convert raw data to spike format since the raw data structure does not contain spike channels'), end
  1592. trialTimes = zeros(nTrials,2);
  1593. for iUnit = 1:nUnits
  1594. unitIndx = spikesel(iUnit);
  1595. spikeTimes = []; % we dont know how large it will be, so use concatenation inside loop
  1596. trialInds = [];
  1597. for iTrial = 1:nTrials
  1598. % read in the spike times
  1599. [spikeTimesTrial] = getspiketimes(data, iTrial, unitIndx);
  1600. nSpikes = length(spikeTimesTrial);
  1601. spikeTimes = [spikeTimes; spikeTimesTrial(:)];
  1602. trialInds = [trialInds; ones(nSpikes,1)*iTrial];
  1603. % get the begs and ends of trials
  1604. if iUnit==1, trialTimes(iTrial,:) = data.time{iTrial}([1 end]); end
  1605. end
  1606. spike.label{iUnit} = data.label{unitIndx};
  1607. spike.waveform{iUnit} = [];
  1608. spike.time{iUnit} = spikeTimes;
  1609. spike.trial{iUnit} = trialInds;
  1610. if iUnit==1, spike.trialtime = trialTimes; end
  1611. end
  1612. %%%%%%%%%% SUB FUNCTION %%%%%%%%%%
  1613. function [spikeTimes spikeIndx] = getspiketimes(data,trial,unit)
  1614. % GETSPIKETIMES extracts the spike times and spike samples from a continuous fieldtrip
  1615. % DATA structure in a selected trial for a selected unit.
  1616. %
  1617. % Inputs:
  1618. % DATA is a contiuous fieldtrip data structure
  1619. %
  1620. % TRIAL is a natural number indicating which trial is selected
  1621. % UNIT is a natural number indicating which channel is selected
  1622. %
  1623. % Outputs:
  1624. % SPIKETIMES contains the spike times, sampled at frequency data.fsample;
  1625. % SPIKEINDX contains the samples in data.trial{trial}(unit,:) at which we find the
  1626. % spikes.
  1627. spikeIndx = logical(data.trial{trial}(unit,:));
  1628. spikeCount = data.trial{trial}(unit,spikeIndx);
  1629. spikeTimes = data.time{trial}(spikeIndx);
  1630. multiSpikes = find(spikeCount>1);
  1631. % preallocate the additional times that we get from the double spikes
  1632. nMultiSpikes = sum(spikeCount(multiSpikes));
  1633. [addTimes,addSamples] = deal(zeros(nMultiSpikes,1));
  1634. binWidth = 1/data.fsample; % the width of each bin
  1635. halfBinWidth = binWidth/2;
  1636. % get the additional samples and spike times, we need only loop through the bins
  1637. n = 1;
  1638. for iBin = multiSpikes(:)' % looping over row vector
  1639. nSpikesInBin = spikeCount(iBin);
  1640. addTimes(n : n+nSpikesInBin-1) = ones(1,nSpikesInBin)*spikeTimes(iBin);
  1641. addSamples(n : n+nSpikesInBin-1) = ones(1,nSpikesInBin)*spikeIndx(iBin);
  1642. n = n + nSpikesInBin;
  1643. end
  1644. % before adding these times, first remove the old ones
  1645. spikeTimes(multiSpikes) = [];
  1646. spikeIndx(multiSpikes) = [];
  1647. spikeTimes = sort([spikeTimes(:); addTimes(:)]);
  1648. spikeIndx = sort([spikeIndx(:) ; addSamples(:)]);
  1649. function [data] = spike2raw(spike,fsample)
  1650. % SPIKE2RAW converts a point representation SPIKE
  1651. % structure to a continuous representation DATA structure.
  1652. %
  1653. % Use as
  1654. % [data] = ft_spike2raw(spike,fsample)
  1655. %
  1656. % Inputs:
  1657. % Data is a standard RAW structure.
  1658. %
  1659. % fsample is the sampling rate for the continuous representation in RAW
  1660. %
  1661. % Copyright (C) 2010, Martin Vinck
  1662. % get some sizes
  1663. nUnits = length(spike.label);
  1664. nTrials = size(spike.trialtime,1);
  1665. % preallocate
  1666. data.trial(1:nTrials) = {[]};
  1667. data.time(1:nTrials) = {[]};
  1668. for iTrial = 1:nTrials
  1669. timeAx = spike.trialtime(iTrial,1):(1/fsample):spike.trialtime(iTrial,2);
  1670. % convert to continuous
  1671. trialData = zeros(nUnits,length(timeAx));
  1672. for iUnit = 1:nUnits
  1673. % get the timestamps and only select those timestamps that are in the trial
  1674. ts = spike.time{iUnit};
  1675. hasTrial = spike.trial{iUnit}==iTrial;
  1676. ts = ts(hasTrial);
  1677. % get all the samples at once without using loops
  1678. sample = nearest_nd(timeAx,ts);
  1679. % because we have duplicates, simply get our vector by using histc trick
  1680. [N] = histc(sample,1:length(timeAx));
  1681. % store it in a matrix
  1682. trialData(iUnit,:) = N;
  1683. end
  1684. data.trial{iTrial} = trialData;
  1685. data.time{iTrial} = timeAx;
  1686. end
  1687. % create the associated labels and other aspects of data such as the header
  1688. data.label = spike.label;
  1689. if isfield(spike,'hdr'), data.hdr = spike.hdr; end
  1690. if isfield(spike,'cfg'), data.cfg = spike.cfg; end
  1691. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1692. % SUBFUNCTION
  1693. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1694. function [indx] = nearest_nd(x,y)
  1695. % NEAREST return the index of an n-d matrix to an n-d matrix.
  1696. %
  1697. % [indx] = nearest_nd(x, y)
  1698. %
  1699. % Inputs:
  1700. % X can be a n-d matrix of any size (scalar, vector, n-d matrix).
  1701. % Y can be an n-d matrix of any size (scalar, vector, n-d matrix).
  1702. %
  1703. % If Y is larger than any X, we return the last index that the maximum value of X occurred.
  1704. % Otherwise, we return the first occurence of the nearest X.
  1705. %
  1706. % If Y contains NaNs, we return a NaN for every NaN in Y.
  1707. %
  1708. % Outputs:
  1709. % INDX is a vector of size Y and contains the indices of the values in X that are
  1710. % closest to the respective value of Y. INDX is a linear index, such that x(INDX) gives
  1711. % the nearest values of X to Y. To convert INDX to subscripts, see IND2SUB.
  1712. %
  1713. % Copyright, Martin Vinck, 2009.
  1714. % store the sizes of x and y, this is used to reshape INDX later on
  1715. szY = size(y);
  1716. % vectorize both x and y
  1717. x = x(:);
  1718. y = y(:);
  1719. % from now on we can treat X and Y as vectors
  1720. nY = length(y);
  1721. nX = length(x);
  1722. hasNan = isnan(y); % indices with nans in y, indx(hasNaN)