PageRenderTime 64ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/examples/toolboxes/fieldtrip/external/ctf/writeCTFds.m

http://brainstream.googlecode.com/
MATLAB | 1616 lines | 1126 code | 163 blank | 327 comment | 175 complexity | b75e119c345beec5a724138aae97b89d 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

Large files files are truncated, but you can click here to view the full file

  1. function ds=writeCTFds(datasetname,ds,data,unit);
  2. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  3. % %
  4. % This program creates datasets that can be analyzed by CTF software. %
  5. % %
  6. % Datasets created by this program MUST NOT BE USED FOR CLINICAL APPLICATIONS. %
  7. % %
  8. % Please do not redistribute it without permission from VSM MedTech Ltd. %
  9. % %
  10. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  11. % Author : Harold Wilson
  12. % Version 1.3 5 October 2007 Spelling errors in some variables corrected.
  13. % Version 1.2 24 April 2007 Modified to write both MEG and fMEG .hc files.
  14. % writeCTFds.m Version 1.1 Prepares a CTF-format data set. MATLAB to CTF conversion.
  15. % Adds record of filter changes to hist and newds files.
  16. % Adds no-clinical-use messages.
  17. % Creates multiple meg4 files when the total size of the data array
  18. % >536870910 elements.
  19. % Operation :
  20. % 1. User reads a data set using readCTFds and getTrial2.
  21. % size(data)= [SAMPLES, CHANNELS, TRIALS].
  22. % 2. After working on data in MATLAB, user adjusts ds structure to reflect changes.
  23. % (writeCTFds will adjust the number of channels, channel names and trial structure.)
  24. % 3. This program then creates a new CTF data set.
  25. % datasetname must include the complete path description.
  26. % If a data set with name datasetname already exists, writeCTFds will issue an error message.
  27. % The new directory contains files for each field of structure ds. If the field is
  28. % missing no file is created. If the field is empty, an empty file is created.
  29. % Files default.* are not created by writeCTFds.
  30. % 4. The following fields of structure ds.res4 are modified based on the size of array data :
  31. % no_samples
  32. % no_channels
  33. % no_trials.
  34. % If size(ds.res4.chanNames,1)<no_channels, additional channel names are created
  35. % as required. The additional channels are called MXT%%%. It is assumed that
  36. % there will be <1000 new channels.
  37. % Inputs : datasetname : Output dataset including path. Extension '.ds' is optional.
  38. % ds : Structure produced by readCTFds.
  39. % data : MEG data array. size(data)=[no_samples no_channels no_trials]
  40. % Array data may be single or double.
  41. % unit : Determines the unit of the SQUID and EEG signals:
  42. % If unit is missing or unit==[], the unit is set to 'fT'.
  43. % 'ft' or 'fT' : Convert to fT (MEG), uV (EEG)
  44. % 't' or 'T' : Convert to T (MEG), V (EEG)
  45. % 'phi0' : Convert to phi0 (MEG), uV (EEG)
  46. % 'int': Read plain integers from *.meg4-file
  47. % Outputs : - ds : The ds structure of the output data set.
  48. % - datasetout : the name of the output data set.
  49. % - A data set. The .hz and .hz2 subdirectories are not included.
  50. % Function calls
  51. % Included in this listing:
  52. % - check_senres: Does simple checks on the fields of the senres table.
  53. % - writeHc: Creates the new .hc file
  54. % - checkMrk: Checks structure ds.mrk to see if it is valid marker set.
  55. % - writeEEG: Creates the new .EEG file.
  56. % - writeBadSegments: Creates the new bad.segments file.
  57. % - writeClassFile: Creates the new ClassFile.cls file.
  58. % - writeVirtualChannels: Creates the new VirtualChannels file.
  59. % - updateDescriptors: Adds non-clinical-use and creation software messages to
  60. % infods, res4, newds and hist fields of ds.
  61. % - updateHLC: Adds head-coil movement information to infods.
  62. % - updateDateTime : Resets dattime fields of res4 and infods.
  63. % - updateBandwidth: Resets bandwidth of newds and infods. Adds res4 filter
  64. % description to ds.hist.
  65. % - getArrayField : Extracts one field of a structure array to make it easier to
  66. % manipulate.
  67. % - writeMarkerFile: Creates the new MarkerFile.mrk file.
  68. % - writeCPersist: Creates the new .acq and .infods files.
  69. % Other calls:
  70. % - writeRes4: Writes the new .res4 file.
  71. % Output files are opened with 'w' permission and 'ieee-be' machine format in order
  72. % to be compatible with the Linux acquisition and analysis software. Do not open files
  73. % with 'wt' permission because this will add an extra char(13) byte at the end of each
  74. % line of text.
  75. persistent printWarning bandwidthMessage
  76. delim=filesep;
  77. if nargin==0 & nargout==0 % Print a version number
  78. fprintf(['\twriteCTFds: Version 1.3 5 October 2007 ',...
  79. 'Creates v4.1 and v4.2 CTF data sets.\n',...
  80. '\tCall: ds=writeCTFds(datasetname,ds,data,unit);\n',...
  81. '\t\tdatasetname = Name of the new dataset including the path.\n',...
  82. '\t\tds = Structure describing the new dataset (ds.hc must be v1.2 format).\n',...
  83. '\t\tdata = data that will be written to the new dataset .meg4 file.\n',...
  84. '\t\tunit = physical units of the data.\n\n']);
  85. return
  86. end
  87. % Allowed 8-byte headers for res4 and meg4 files.
  88. res4_headers=strvcat(['MEG41RS',char(0)],['MEG42RS',char(0)]);
  89. meg4_headers=strvcat(['MEG41CP',char(0)],['MEG42CP',char(0)]);
  90. maxMEG4Size=2^31; % Maximum MEG$ file in bytes. (Limit set by Linux software)
  91. MAX_COILS=8; % Parameter that determines the size of the ds.res.senres structure.
  92. lenSensorName=32; % Channel names must be 32 characters
  93. printDefaultBandwidthMessage=0; % Print a message about default bandwidth?
  94. default_flp=0.25; % Default is flp=0.25*ds.res4.sample_rate
  95. clinicalUseMessage='NOT FOR CLINICAL USE';
  96. creatorSoftware='writeCTFds'; % Added to .infods file
  97. meg4ChunkSize=2^20; % Write new .meg4 file in chunks of 4*meg4ChunkSize bytes.
  98. DATASET_HZ_UNKNOWN=round(2^31-1); % Peculiar requirement of DataEditor as of 23 Oct. 2006
  99. if ~exist('clinicalUseMessage');
  100. clinicalUseMessage=char([]);
  101. end
  102. % Check inputs
  103. if nargin<3
  104. fprintf(['\nwriteCTFds: Must supply inputs datasetname,ds,data. ',...
  105. 'Only %d input arguments are present.\n\n'],nargin);
  106. ds=-1; % Force an error in the calling program.
  107. return
  108. end
  109. % Check input argument unit. Convert unit to lower case.
  110. if exist('unit')~=1
  111. unit='ft'; % default
  112. elseif isempty(unit)
  113. unit='ft'; % default
  114. elseif ischar(unit)
  115. unit=lower(unit);
  116. if ~strcmp(unit,'int') & ~strcmp(unit,'ft') & ~strcmp(unit,'t') & ~strcmp(unit,'phi0')
  117. fprintf(['\nwriteCTFds : unit=%s Not a valid option. Must be ',...
  118. '''fT'', ''T'', ''phi0'' or ''int''\n\n'],unit);
  119. ds=-1; % Force an error in the calling program.
  120. return
  121. end
  122. end
  123. % Check argument type
  124. if ~isstruct(ds) | ~isnumeric(data) | ~ischar(unit) | ~ischar(datasetname)
  125. fprintf('\nwriteCTFds: Some of the inputs are the wrong type.\n');
  126. whos datasetname ds data unit;
  127. ds=-1;
  128. return
  129. elseif ~isfield(ds,'res4') | ~isfield(ds,'meg4')
  130. fprintf('\nwriteCTFds: Fields res4 and meg4 must be present in structure ds.\n\n');
  131. ds % List the fields of structure ds.
  132. ds=-1; % Force an error in the calling program.
  133. return
  134. end
  135. % Refuse to write a data set with balanced reference gradiometers.
  136. balancedGref=0;
  137. for k=1:ds.res4.no_channels
  138. balancedGref=(ds.res4.senres(k).sensorTypeIndex==1 & ds.res4.senres(k).grad_order_no~=0);
  139. end
  140. if balancedGref
  141. fprintf('\nwriteCTFds: ds.res4.senres indicates balanced reference gradiometers.\n\n');
  142. ds=-1; % Force an error in the calling program.
  143. return
  144. end
  145. clear k balancedGref;
  146. % Separate datasetname into a path and the baseName
  147. datasetname=deblank(datasetname);
  148. ksep=max([0 findstr(datasetname,delim)]);
  149. baseName=datasetname((ksep+1):length(datasetname));
  150. path=datasetname(1:ksep); % String path is terminated by the file delimiter (or path=[]).
  151. % Remove the last .ds from baseName.
  152. kdot=max(findstr(baseName,'.ds'));
  153. if kdot==(length(baseName)-2)
  154. baseName=baseName(1:(max(kdot)-1));
  155. else
  156. datasetname=[datasetname,'.ds'];
  157. end
  158. clear ksep kdot;
  159. % Save the name already in structure ds, and change to the new datset name.
  160. if isfield(ds,'baseName')
  161. olddatasetname=[ds.baseName,'.ds'];
  162. if isfield(ds.path)
  163. olddatasetname=[ds.path,olddatasetname];
  164. end
  165. else
  166. olddatasetname=' None ';
  167. end
  168. ds.path=path;
  169. ds.baseName=baseName;
  170. % Does the dataset already exist?
  171. if exist(datasetname)==7
  172. fprintf('\nwriteCTFds: Dataset %s already exists. Use a different name.\n\n',...
  173. datasetname);
  174. ds=-1; % Force an error in the calling program.
  175. return
  176. end
  177. if size(ds.res4.chanNames,2)~=lenSensorName
  178. fprintf(['\nwriteCTFds : size(ds.res4.chanNames)=[%d %d] ? Must ',...
  179. 'have %d-character channel names.\n\n'],size(ds.res4.chanNames),lenSensorName);
  180. ds=-1;
  181. return
  182. end
  183. % Check that the channel names have a sensor-file identification extensions.
  184. % If it is missing, print a warning message.
  185. % Sensor type indices : SQUIDs (0:7), ADCs (10), DACs(14), Clock (17), HLC (13,28,29)
  186. % See Document CTF MEG File Formats (PN 900-0088), RES4 File Format/
  187. for index=[0:7 10 13 14 17 28 29]
  188. for k=find([ds.res4.senres.sensorTypeIndex]==index);
  189. if isempty(strfind(ds.res4.chanNames(k,:),'-'))
  190. fprintf(['writeCTFds: Channel %3d %s No sensor-file identification.',...
  191. ' (''-xxxx'' appended to channel name).\n',...
  192. '\t\tSome CTF software may not work with these channel names.\n'],...
  193. k,deblank(ds.res4.chanNames(k,:)));
  194. break;
  195. end
  196. end
  197. if isempty(strfind(ds.res4.chanNames(k,:),'-'));break;end
  198. end
  199. clear index k chanName;
  200. % Update the data description in the ds.res4 structure.
  201. [nSample, nChan, trials]=size(data);
  202. % Update ds.res4 fields to match the size of array data.
  203. ds.res4.no_trials=trials;
  204. ds.res4.no_channels=nChan;
  205. ds.res4.no_samples=nSample;
  206. ds.res4.epoch_time=nSample*trials/ds.res4.sample_rate;
  207. % Check if channels have been added or removed from array data.
  208. [no_chanNames len_chanNames]=size(ds.res4.chanNames);
  209. if no_chanNames<nChan
  210. % Assume that new channel are at the end of the data set. Add a fake extension.
  211. for kx=1:(nChan-no_chanNames)
  212. ds.res4.chanNames=...
  213. strvcat(ds.res4.chanNames,['MXT' num2str(kx,'%3.3d') '-0001' char(0)]);
  214. end
  215. fprintf('\tAdded %d SQUID channels to the end of ds.res4.chanNames table.\n',...
  216. nChan-no_chanNames);
  217. elseif no_chanNames>nChan
  218. fprintf(['\nlength(chanNames)=%d, but only %d channels of data. ',...
  219. 'writeCTFds cannot tell which channel names to remove.\n\n'],no_chanNames,nChan);
  220. ds=-1;
  221. return
  222. end
  223. clear no_chanNames len_chanNames;
  224. % The senres table may have been changed, especially if channels are added or removed.
  225. % Check structure senres, print error messages, and possibly fix the errors.
  226. [ds.res4.senres,status]=check_senres(ds.res4.senres,ds.res4.no_channels);
  227. if status<0;ds=-1;return;end
  228. clear status;
  229. % Check that ds.res4.numcoef is the size of structure array ds.res4.scrr.
  230. if isfield(ds.res4,'scrr')
  231. if ~isequal(size(ds.res4.scrr,2),ds.res4.numcoef)
  232. fprintf('Error in ds.res4: ds.res4.numcoef=%d, but size(ds.res4.scrr)=[',...
  233. ds.res4.numcoef);
  234. fprintf(' %d',size(ds.res4.scrr));fprintf('] ?\n');
  235. return
  236. end
  237. elseif ds.res4.numcoef~=0
  238. fprintf(['Error in ds.res4: ds.res4.numcoef=%d,',...
  239. ' but scrr is not a field of ds.res4\n'],ds.res4.numcoef);
  240. return
  241. end
  242. % Before converting data to integers, save HLC channels for motion analysis in function
  243. % make_new_infods. Pass HLCdata to function make_new_infods
  244. if isempty(strmatch('HLC',ds.res4.chanNames))
  245. HLCdata=[];
  246. else
  247. % Make a list of head-coil channels
  248. coil=0;
  249. HLClist=[];
  250. while ~isempty(strmatch(['HLC00',int2str(coil+1)],ds.res4.chanNames))
  251. coil=coil+1;
  252. for k=1:3
  253. HLClist=[HLClist strmatch(['HLC00',int2str(coil),int2str(k)],ds.res4.chanNames)];
  254. end
  255. end
  256. HLCdata=reshape(double(data(:,HLClist,:)),ds.res4.no_samples,3,coil,ds.res4.no_trials);
  257. clear coil k HLClist;
  258. end
  259. % Convert data to integers because CTF data sets are stored as raw numbers and
  260. % not as physical qunatities. The res4 file contains the calibrations for
  261. % converting back to physical units. Array data may be single precision, so
  262. % convert to double before doing any adjustments to the data.
  263. % Already checked that unit is valid.
  264. if strcmp(unit,'int')
  265. data=reshape(data,nSample,nChan*trials);
  266. for k=1:nChan*trials
  267. if strcmp(class(data),'single')
  268. data(:,k)=single(round(double(data(:,k))));
  269. else
  270. data(:,k)=round(double(data(:,k)));
  271. end
  272. end
  273. data=reshape(data,nSample,nChan,trials);
  274. clear k;
  275. else
  276. for chan=1:nChan % Convert EEGs from uV to V, SQUIDs from fT to T
  277. SQUIDtype=any(ds.res4.senres(chan).sensorTypeIndex==[0:7]);
  278. EEGtype=any(ds.res4.senres(chan).sensorTypeIndex==[8 9]);
  279. if EEGtype & (strcmp(unit,'ft') | strtcmp(unit,'phi0'))
  280. alphaG=1e-6;
  281. elseif SQUIDtype & strcmp(unit,'ft')
  282. alphaG=1e-15;
  283. elseif SQUIDtype & strcmp(unit,'phi0')
  284. alphaG=1./(ds.res4.senres(chan).properGain*ds.res4.senres(chan).ioGain);
  285. else
  286. alphaG=1;
  287. end
  288. % Convert from physical units to integers using the gains in the senres table.
  289. for kt=1:trials
  290. buff=round(double(data(:,chan,kt))*(alphaG*...
  291. (ds.res4.senres(chan).properGain*ds.res4.senres(chan).qGain*ds.res4.senres(chan).ioGain)));
  292. if strcmp(class(data),'single')
  293. data(:,chan,kt)=single(buff);
  294. else
  295. data(:,chan,kt)=buff;
  296. end
  297. end
  298. end
  299. clear chan alphaG SQUIDtype EEGtype buff kt;
  300. end
  301. % Create the output dataset
  302. [status,msg]=mkdir(path,[baseName '.ds']);
  303. if status==0 | ~isempty(msg)
  304. fprintf('\nwriteCTFds: Failed to create directory.\n');
  305. fprintf(' [status,msg]=mkdir(%s)\n',datasetname);
  306. fprintf(' returns status=%d, msg=%s',status,msg);fprintf('\n\n');
  307. ds=-1;
  308. return
  309. end
  310. clear msg status;
  311. % Write the data file (meg4 file). Check ds.meg4.header and make sure that
  312. % the output .meg4 file has an acceptable header.
  313. headerMessage=1;
  314. if ~isfield(ds.meg4,'header'); % If ds.meg4.header is missing, add it.
  315. nChar=length(deblank(meg4_headers(1,:)));
  316. ds.meg4.header=[meg4_headers(1,1:min(7,nChar)) char(zeros(1,7-nChar))];
  317. elseif isempty(strmatch(ds.meg4.header(1:7),meg4_headers(:,1:7),'exact'))
  318. ds.meg4.header=meg4_headers(1,1:7);
  319. else
  320. headerMessage=0;
  321. end
  322. if headerMessage;
  323. fprintf('writeCTFds: Set ds.meg4.header=%s\n',ds.meg4.header);
  324. end
  325. clear headerMessage;
  326. if isempty(printWarning)
  327. fprintf(['\nwriteCTFds: The data you are writing have been processed by software not\n',...
  328. '\tmanufactured by VSM MedTech Ltd. and that has not received marketing clearance\n',...
  329. '\tfor clinical applications. These data should not be later employed for clinical\n',...
  330. '\tand/or diagnostic purposes.\n\n']);
  331. printWarning=1;
  332. end
  333. % Write the meg4 file(s). If there are more than maxMEG4Size-8 bytes, then additional meg4
  334. % files will be created.
  335. % Convert data to a 1-D array
  336. ndata=prod(size(data));
  337. data=reshape(data,ndata,1);
  338. ptsPerTrial=nSample*nChan;
  339. maxPtsPerFile=ptsPerTrial*floor((maxMEG4Size-8)/(4*ptsPerTrial));
  340. pt=0; % Last point written to the output file(s).
  341. while pt<ndata
  342. endPt=pt+min(ndata-pt,maxPtsPerFile);
  343. if pt==0
  344. meg4Ext='.meg4';
  345. else
  346. meg4Ext=['.',int2str(floor(pt/maxPtsPerFile)),'_meg4'];
  347. end
  348. fidMeg4=fopen([path,baseName,'.ds\',baseName,meg4Ext],'w','ieee-be');
  349. fwrite(fidMeg4,[ds.meg4.header(1:7),char(0)],'uint8');
  350. while pt<endPt
  351. pt1=min(pt+meg4ChunkSize,endPt); % Convert to double in case data is
  352. fwrite(fidMeg4,double(data((pt+1):pt1)),'int32'); % is single and write in short
  353. pt=pt1; % pieces.
  354. end
  355. fclose(fidMeg4);
  356. end
  357. % Update the .meg4 part of structure ds.
  358. ds.meg4.fileSize=4*ndata+8*(1+floor(ndata/maxPtsPerFile));
  359. clear data pt pt1 ndata fidMeg4 ptsPerTrial maxPtsPerFile meg4Ext;
  360. % Add dataset names to .hist
  361. if ~isfield(ds,'hist');ds.hist=char([]);end
  362. ds.hist=[ds.hist char(10) char(10) datestr(now) ' :' char(10) ...
  363. ' Read into MATLAB as data set ' olddatasetname char(10) ...
  364. ' Rewritten by writeCTFds as data set ' datasetname char(10)];
  365. % If infods doesn't exist or is empty create it.
  366. if ~isfield(ds,'infods');ds.infods=[];end
  367. if isempty(ds.infods)
  368. ds.infods=make_dummy_infods(isfield(ds,'hc'),~isempty(HLCdata),ds.res4.sample_rate);
  369. end
  370. % Update text fields of res4,infods, newds and hist.
  371. ds=updateDescriptors(ds,clinicalUseMessage,creatorSoftware);
  372. % Add HLC data to infods
  373. ds.infods=updateHLC(ds.infods,HLCdata);
  374. % Analyze structure array ds.res4.filters to make text info for .hist file and
  375. % bandwidth parameters for .newds file.
  376. fhp=0; % High-pass cutoff assuming no filters
  377. flp=default_flp*ds.res4.sample_rate; % Assumed lowpass cutoff. SHOULD THIS BE CHANGED?
  378. if isempty(bandwidthMessage) & printDefaultBandwidthMessage
  379. fprintf('writeCTFds: Lowpass filter set to flp=%0.2f*sample_rate\n',default_flp);
  380. bandwidthMessage=1;
  381. end
  382. ds=updateBandwidth(ds,fhp,flp);
  383. % Update date/time fields of infods and res4
  384. ds=updateDateTime(ds);
  385. % Create the .res4 file in the output dataset.
  386. ds.res4=writeRes4([path,baseName,'.ds\',baseName,'.res4'],ds.res4,MAX_COILS);
  387. if ds.res4.numcoef<0
  388. fprintf('\nwriteCTFds: writeRes4 returned ds.res4.numcoef=%d (<0??)\n\n',...
  389. ds.res4.numcoef);
  390. % Kill the output dataset.
  391. rmdir([path,baseName,'.ds'],'s');
  392. ds=-1;
  393. return
  394. end
  395. % Create .hist file
  396. histfile=[path,baseName,'.ds\',baseName,'.hist'];
  397. fid=fopen(histfile,'w');
  398. fwrite(fid,ds.hist,'char');
  399. fclose(fid);
  400. % New .newds file
  401. if isfield(ds,'newds')
  402. fid=fopen([path,baseName,'.ds\',baseName,'.newds'],'w');
  403. fwrite(fid,ds.newds,'char');
  404. fclose(fid);
  405. end
  406. % New infods file.
  407. if isfield(ds,'infods')
  408. writeCPersist([path,baseName,'.ds',delim,baseName,'.infods'],ds.infods);
  409. end
  410. % new hc file
  411. if isfield(ds,'hc')
  412. ds.hc=writeHc([path,baseName,'.ds',delim,baseName,'.hc'],ds.hc,HLCdata(:,:,1));
  413. end
  414. clear HLCdata;
  415. % MarkerFile.mrk
  416. if checkMrk(ds)
  417. writeMarkerFile([path,baseName,'.ds',delim,'MarkerFile.mrk'],ds.mrk);
  418. end
  419. % .EEG
  420. if isfield(ds,'EEG')
  421. writeEEG([path,baseName,'.ds',delim,baseName,'.EEG'],ds.EEG);
  422. end
  423. % .acq
  424. if isfield(ds,'acq');
  425. % Check that ds.acq has the correct fields
  426. if isfield(ds.acq,'name') & isfield(ds.acq,'type') & isfield(ds.acq,'data')
  427. acqFilename=[path,baseName,'.ds',delim,baseName,'.acq'];
  428. writeCPersist(acqFilename,ds.acq);
  429. end
  430. end
  431. % bad.segments file
  432. if isfield(ds,'badSegments')
  433. writeBadSegments([path,baseName,'.ds',delim,'bad.segments'],ds.badSegments,...
  434. ds.res4.no_trials,ds.res4.no_samples/ds.res4.sample_rate);
  435. end
  436. % BadChannels
  437. if isfield(ds,'BadChannels');
  438. if ischar(ds.BadChannels) & ~isempty(ds.BadChannels)
  439. fid=fopen([path,baseName,'.ds',delim,'BadChannels'],'w','ieee-be');
  440. for k=1:size(ds.BadChannels,1)
  441. fprintf(fid,'%s\n',deblank(ds.BadChannels(k,:)));
  442. end
  443. fclose(fid);
  444. end
  445. end
  446. % ClassFile
  447. if check_cls(ds)
  448. writeClassFile([path,baseName,'.ds',delim,'ClassFile.cls'],ds.TrialClass);
  449. end
  450. % VirtualChannels
  451. if isfield(ds,'Virtual')
  452. writeVirtualChannels([path,baseName,'.ds',delim,'VirtualChannels'],ds.Virtual);
  453. end
  454. % processing.cfg
  455. if isfield(ds,'processing');
  456. if ischar(ds.processing)
  457. fid=fopen([datasetname,delim,'processing.cfg'],'w','ieee-be');
  458. fwrite(fid,ds.processing,'char');
  459. fclose(fid);
  460. end
  461. end
  462. % Update the data set path and name
  463. ds.path=path;
  464. ds.baseName=baseName;
  465. return
  466. % *************** End of function writeCTFds ********************************************
  467. % **************************************************************************************
  468. % **************************************************************************************
  469. % *************** Function check_senres **************************
  470. function [senres,status]=check_senres(senres,numChan);
  471. % A user may have augmented the senres table, so check that all the fields have the
  472. % correct size. This will cause errors in programs that try to compute
  473. % sensor response using the geometry in the senres table.
  474. % Does "sanity" checks on the senres table. If there are obviously incorrect entries, it
  475. % tries to fix them, and prints a message. If the senres table does not specify coil
  476. % positions, orientations or areas, set them to zero, but give them the correct array
  477. % size.
  478. newChannelType=4; % Create the fake sensors as MEG magnetometers. This way offsets can be removed.
  479. status=-1;
  480. % Does the senres table have the correct no. of channels?
  481. no_senres=length(senres);
  482. if no_senres<numChan
  483. % Add channels. Assume that they are gradiometers.
  484. ioGain=1;
  485. qGain=2^20;
  486. gain=0.3;
  487. properGain=1e15/(qGain*ioGain*gain); % sensor gain in phi0/fT
  488. for kx=(no_senres+1):numChan
  489. senres(kx)=struct(...
  490. 'sensorTypeIndex',newChannelType,'originalRunNum',0,'coilShape',0,...
  491. 'properGain',properGain,'qGain',qGain,'ioGain',ioGain,...
  492. 'ioOffset',0,'numCoils',1,...
  493. 'grad_order_no',0,...
  494. 'gain',gain,...
  495. 'pos0',zeros(3,1),'ori0',zeros(3,1),...
  496. 'area',0.1,'numturns',1,...
  497. 'pos',[0 0 21]','ori',zeros(3,1));
  498. end
  499. fprintf(['\tAdded %d SQUID channels to senres table. Nominal gain of each = ',...
  500. '%8.4f fT/step\n'],numChan-no_senres,gain);
  501. clear kx gain qGain ioGain properGain;
  502. elseif no_senres>numChan
  503. % Channels have been removed from the data set, but can't tell which elements of the
  504. % senres table to remove.
  505. fprintf(['length(senres)=%d, but only %d channels of data. writeCTFds can''t',...
  506. ' tell which channels to remove from senres table.\n'],no_senres,numChan);
  507. return
  508. end
  509. no_senres=length(senres);
  510. % Previous version of check_senres ensures that several fields had size [1 1].
  511. % Seems unnecessary, so removed it.
  512. for k=1:no_senres
  513. % Check the fields that define pickup loops. Force the field defining the pickup
  514. % loops to have the correct size. It is not clear why the EEG channels and
  515. % the type=13,28,29 HLC channels need numCoils=1, and area>0.
  516. if any(senres(k).sensorTypeIndex==[0:7]) % SQUID channels
  517. correct_numCoils=rem(senres(k).sensorTypeIndex,4)+1;
  518. elseif any(senres(k).sensorTypeIndex==[13 28 29]) % HLC channels
  519. correct_numCoils=1;
  520. elseif any(senres(k).sensorTypeIndex==[8 9]) % EEG channels
  521. correct_numCoils=1;
  522. else
  523. correct_numCoils=0;
  524. end
  525. if senres(k).numCoils~=correct_numCoils & any(senres(k).sensorTypeIndex==[0:7])
  526. fprintf('writeCTFds_test: senres(%d).sensorTypeIndex=%d but numCoils=%d??\n',...
  527. k,senres(k).sensorTypeIndex,senres(k).numCoils);
  528. fprintf(' Set numCoils=%d\n',correct_numCoils);
  529. senres(k).numCoils=correct_numCoils;
  530. end
  531. numCoils=senres(k).numCoils;
  532. if size(senres(k).pos0)~=[3 numCoils];pos0=zeros(3,numCoils);end
  533. if size(senres(k).ori0)~=[3 numCoils];ori0=zeros(3,numCoils);end
  534. if size(senres(k).pos)~=[3 numCoils];pos=zeros(3,numCoils);end
  535. if size(senres(k).ori)~=[3 numCoils];ori=zeros(3,numCoils);end
  536. if size(senres(k).numturns)~=[1 numCoils];numturns=zeros(1,numCoils);end
  537. if size(senres(k).area)~=[1 numCoils];area=zeros(3,numCoils);end
  538. end
  539. status=1;
  540. return
  541. % ************* End of function check_senres*********************************************
  542. % **************************************************************************************
  543. % **************************************************************************************
  544. % ************* function make_dummy_infods*********************************************
  545. function Tag=make_dummy_infods(exist_hc,exist_HLC,sample_rate);
  546. % If the user does not supply ds.infos, this code makes a dummy version. It has all of
  547. % the tags that Acq created in file Richard_SEF_20060606_04.infods.
  548. % ***********************************************************************************
  549. % ***********************************************************************************
  550. DATASET_HZ_UNKNOWN=round(2^31-1); % Peculiar requirement of DataEditor as of 23 Oct. 2006
  551. fprintf('writeCTDds (make_dummy_infods): Creating a dummy infods file with all the tags.\n');
  552. Tag(1)=struct('name','WS1_','type',0,'data',[]);
  553. Tag(length(Tag)+1)=struct('name','_PATIENT_INFO','type',2,'data',[]);
  554. Tag(length(Tag)+1)=struct('name','WS1_','type',0,'data',[]);
  555. Tag(length(Tag)+1)=struct('name','_PATIENT_UID','type',10,...
  556. 'data','2.16.124.113000.000000.00000000000000.000000000.00000000.0011');
  557. Tag(length(Tag)+1)=struct('name','_PATIENT_NAME_FIRST','type',10,'data','');
  558. Tag(length(Tag)+1)=struct('name','_PATIENT_NAME_MIDDLE','type',10,'data','');
  559. Tag(length(Tag)+1)=struct('name','_PATIENT_NAME_LAST','type',10,'data','');
  560. Tag(length(Tag)+1)=struct('name','_PATIENT_ID','type',10,'data','x');
  561. Tag(length(Tag)+1)=struct('name','_PATIENT_BIRTHDATE','type',10,'data','19500101000000');
  562. Tag(length(Tag)+1)=struct('name','_PATIENT_SEX','type',5,'data',2);
  563. Tag(length(Tag)+1)=struct('name','_PATIENT_PACS_NAME','type',10,'data','');
  564. Tag(length(Tag)+1)=struct('name','_PATIENT_PACS_UID','type',10,'data','');
  565. Tag(length(Tag)+1)=struct('name','_PATIENT_INSTITUTE','type',10,'data','');
  566. Tag(length(Tag)+1)=struct('name','EndOfParameters','type',-1,'data',[]);
  567. Tag(length(Tag)+1)=struct('name','_PROCEDURE_INFO','type',2,'data',[]);
  568. Tag(length(Tag)+1)=struct('name','WS1_','type',0,'data',[]);
  569. Tag(length(Tag)+1)=struct('name','_PROCEDURE_VERSION','type',5,'data',1);
  570. Tag(length(Tag)+1)=struct('name','_PROCEDURE_UID','type',10,...
  571. 'data','2.16.124.113000.000000.00000000000000.000000000.00000000.0041');
  572. Tag(length(Tag)+1)=struct('name','_PROCEDURE_ACCESSIONNUMBER','type',10,'data','0');
  573. Tag(length(Tag)+1)=struct('name','_PROCEDURE_TITLE','type',10,'data','0');
  574. Tag(length(Tag)+1)=struct('name','_PROCEDURE_SITE','type',10,'data','');
  575. Tag(length(Tag)+1)=struct('name','_PROCEDURE_STATUS','type',5,'data',1);
  576. Tag(length(Tag)+1)=struct('name','_PROCEDURE_TYPE','type',5,'data',2); % Research type
  577. Tag(length(Tag)+1)=struct('name','_PROCEDURE_STARTEDDATETIME','type',10,...
  578. 'data','20060606164306');
  579. Tag(length(Tag)+1)=struct('name','_PROCEDURE_CLOSEDDATETIME','type',10,...
  580. 'data','19000100000000');
  581. Tag(length(Tag)+1)=struct('name','_PROCEDURE_COMMENTS','type',10,'data','');
  582. Tag(length(Tag)+1)=struct('name','_PROCEDURE_LOCATION','type',10,'data','');
  583. Tag(length(Tag)+1)=struct('name','_PROCEDURE_ISINDB','type',5,'data',0);
  584. Tag(length(Tag)+1)=struct('name','EndOfParameters','type',-1,'data',[]);
  585. Tag(length(Tag)+1)=struct('name','_DATASET_INFO','type',2,'data',[]);
  586. Tag(length(Tag)+1)=struct('name','WS1_','type',0,'data',[]);
  587. Tag(length(Tag)+1)=struct('name','_DATASET_VERSION','type',5,'data',2);
  588. Tag(length(Tag)+1)=struct('name','_DATASET_UID','type',10,...
  589. 'data','2.16.124.113000.000000.00000000000000.000000000.00000000.0042');
  590. Tag(length(Tag)+1)=struct('name','_DATASET_PATIENTUID','type',10,...
  591. 'data','2.16.124.113000.000000.00000000000000.000000000.00000000.0011');
  592. Tag(length(Tag)+1)=struct('name','_DATASET_PROCEDUREUID','type',10,...
  593. 'data','2.16.124.113000.000000.00000000000000.000000000.00000000.0041');
  594. Tag(length(Tag)+1)=struct('name','_DATASET_STATUS','type',10,'data','');
  595. Tag(length(Tag)+1)=struct('name','_DATASET_RPFILE','type',10,'data','default.rp');
  596. Tag(length(Tag)+1)=struct('name','_DATASET_PROCSTEPTITLE','type',10,'data','');
  597. Tag(length(Tag)+1)=struct('name','_DATASET_PROCSTEPPROTOCOL','type',10,'data','');
  598. Tag(length(Tag)+1)=struct('name','_DATASET_PROCSTEPDESCRIPTION','type',10,...
  599. 'data','');
  600. Tag(length(Tag)+1)=struct('name','_DATASET_COLLECTIONDATETIME','type',10,...
  601. 'data','Unknown');
  602. Tag(length(Tag)+1)=struct('name','_DATASET_COLLECTIONSOFTWARE','type',10,...
  603. 'data','Acq ');
  604. Tag(length(Tag)+1)=struct('name','_DATASET_CREATORDATETIME','type',10,...
  605. 'data',sprintf('%d',floor(clock)));
  606. Tag(length(Tag)+1)=struct('name','_DATASET_CREATORSOFTWARE','type',10,...
  607. 'data','Acq ');
  608. Tag(length(Tag)+1)=struct('name','_DATASET_KEYWORDS','type',10,'data','');
  609. Tag(length(Tag)+1)=struct('name','_DATASET_COMMENTS','type',10,...
  610. 'data','Dummy infods.');
  611. Tag(length(Tag)+1)=struct('name','_DATASET_OPERATORNAME','type',10,'data','');
  612. Tag(length(Tag)+1)=struct('name','_DATASET_LASTMODIFIEDDATETIME','type',10,...
  613. 'data',sprintf('%d',floor(clock)));
  614. if exist_hc
  615. nominalPositions=0; % Measured
  616. else
  617. nominalPositions=1; % Nominal
  618. end
  619. Tag(length(Tag)+1)=struct('name','_DATASET_NOMINALHCPOSITIONS','type',5,...
  620. 'data',nominalPositions);
  621. Tag(length(Tag)+1)=struct('name','_DATASET_COEFSFILENAME','type',10,...
  622. 'data','ds.res4.scrr');
  623. Tag(length(Tag)+1)=struct('name','_DATASET_SENSORSFILENAME','type',10,...
  624. 'data','ds.res4.senres');
  625. %Tag(length(Tag)+1)=struct('name','_DATASET_COEFSFILENAME','type',10,...
  626. % 'data','/opt/ctf-5.1/hardware/M015/M015_1609.coef');
  627. %Tag(length(Tag)+1)=struct('name','_DATASET_SENSORSFILENAME','type',10,...
  628. % 'data','/opt/ctf-5.1/hardware/M015/M015_1609.sens');
  629. Tag(length(Tag)+1)=struct('name','_DATASET_SYSTEM','type',10,'data','DSQ-2010');
  630. Tag(length(Tag)+1)=struct('name','_DATASET_SYSTEMTYPE','type',10,'data','Untitled');
  631. Tag(length(Tag)+1)=struct('name','_DATASET_LOWERBANDWIDTH','type',4,'data',0);
  632. Tag(length(Tag)+1)=struct('name','_DATASET_UPPERBANDWIDTH','type',4,'data',...
  633. round(0.25*sample_rate));
  634. Tag(length(Tag)+1)=struct('name','_DATASET_ISINDB','type',5,'data',0);
  635. if exist_HLC
  636. HZ_MODE=5;
  637. elseif exist_hc
  638. HZ_MODE=1;
  639. else
  640. HZ_MODE=DATASET_HZ_UNKNOWN;
  641. end
  642. Tag(length(Tag)+1)=struct('name','_DATASET_HZ_MODE','type',5,'data',HZ_MODE);
  643. Tag(length(Tag)+1)=struct('name','_DATASET_MOTIONTOLERANCE','type',4,'data',0.005);
  644. Tag(length(Tag)+1)=struct('name','_DATASET_MAXHEADMOTION','type',4,'data',0.005);
  645. Tag(length(Tag)+1)=struct('name','_DATASET_MAXHEADMOTIONTRIAL','type',7,'data',0);
  646. Tag(length(Tag)+1)=struct('name','_DATASET_MAXHEADMOTIONCOIL','type',10,'data','1');
  647. Tag(length(Tag)+1)=struct('name','EndOfParameters','type',-1,'data',[]);
  648. Tag(length(Tag)+1)=struct('name','EndOfParameters','type',-1,'data',[]);
  649. return
  650. % ************* End of function make_dummy_infods*********************************************
  651. % **************************************************************************************
  652. % **************************************************************************************
  653. % ************* Function writeHc*********************************************
  654. function hc=writeHc(hcFileName,hc,HLCdata);
  655. % Modified for v1.2 ds.hc structures. ds.hc.names has the exact, complete names of the
  656. % head coils. The coordinates relative to the subject may be ds.hc.head(MEG) OR
  657. % ds.hc.abdomen (fMEG).
  658. % Creates a .hc file in a CTF dataset
  659. % Inputs: hcFileName : Complete name of .hc file including path, basename and .hc ext.
  660. % hc : structure with nasion, left, right head coil positions in dewar and
  661. % CTF head coordinates.
  662. % HLCdata : head coil locations at the start of 1st trial. unit=m
  663. % coordinates=dewar. Used only if structure hc is empty and it is MEG
  664. % data (not fMEG data).
  665. % Output : hc : hc structure. hc=struct([]) on failure. hc is set to the coil positions
  666. % in array HLCdata if hc=[] on entry.
  667. % Check inputs
  668. if exist('hc')~=1
  669. hc=struct([]);
  670. elseif ~isstruct(hc)
  671. hc=struct([]);
  672. end
  673. hcfields=fieldnames(hc);
  674. if exist('HLCdata')~=1
  675. HLCdata=[];
  676. elseif ~isnumeric(HLCdata) | size(HLCdata,1)~=3 | size(HLCdata,2)<3
  677. HLCdata=[];
  678. end
  679. % Check hc Filename
  680. if exist('hcFileName')~=1;hcFileName=char([]);end
  681. if ~ischar(hcFileName) | isempty(hcFileName)
  682. fprintf('writeCTFds (writeHc): Called writeHc with bad file name.\n');
  683. hcFileName
  684. return
  685. end
  686. % Both hc and HLCdata bad?
  687. if length(hcfields)~=4 & isempty(HLCdata)
  688. fprintf('writeCTFds (writeHc): Called writeHc with bad hc and bad HLCdata.\n');
  689. hc=struct([])
  690. return
  691. elseif length(hcfields)~=4
  692. rstandard=8/sqrt(2)*[1 1 0;-1 1 0;1 -1 0]';
  693. rstandard(3,:)=-27;
  694. rdewar=100*HLCdata(:,1:3);
  695. % Convert from dewar coordinates to CTF head coordinates.
  696. originCTF=0.5*(hc.dewar(:,2)+hc.dewar(:,3));
  697. % Unit vectors for the CTF coordinates
  698. uCTF(:,1)=hc.dewar(:,1)-originCTF;
  699. uCTF(:,3)=cross(uxCTF,hc.dewar(:,2)-hc.dewar(:,3));
  700. uCTF(:,2)=cross(uCTF(:,3),uCTF(:,1));
  701. uCTF=uCTF./(ones(3,1)*sqrt(sum(uCTF.^2,1)));
  702. rCTF=uCTF'*(rdewar-originCTF*ones(1,3))
  703. hc=struct('names',strvcat('nasion','left ear','right ear'),...
  704. 'standard',rstandard,'dewar',rdewar,'head',rCTF);
  705. clear originCTF uCTF rstandard rdewar rCTF;
  706. end
  707. % Character strings for generating the .hc text file
  708. % Should never have both 'head' and 'abdomen' fields.
  709. labelword=strvcat('standard','measured','measured');
  710. printField=[strmatch('standard',hcfields) strmatch('dewar',hcfields) ...
  711. strmatch('head',hcfields) strmatch('abdomen',hcfields)];
  712. if ~strmatch('names',hcfields) | length(printField)~=3
  713. fprintf(['writeCTFds (writeHc): Structure hc does not have all of the required fields.\n',...
  714. ' No .hc file will appear in the output dataset.\n']);
  715. hc;
  716. hc=struct([]);
  717. return
  718. end
  719. relative=strvcat('dewar','dewar',hcfields{printField(3)});
  720. coilcoord=strvcat('standard','dewar',hcfields{printField(3)});
  721. comp='xyz';
  722. coilname=hc.names;
  723. fid=fopen(hcFileName,'w','ieee-be');
  724. for k=1:size(coilcoord,1)
  725. rcoil=getfield(hc,coilcoord(k,:));
  726. for coil=1:size(hc.names,1)
  727. clName=deblank(hc.names(coil,:));
  728. fwrite(fid,[labelword(k,:) ' ' clName ' coil position relative to ',...
  729. deblank(relative(k,:)) ' (cm):' char(10)],'char');
  730. for m=1:3
  731. fwrite(fid,[char(9) comp(m) ' = ' num2str(rcoil(m,coil),'%7.5f') char(10)],'char');
  732. end
  733. end
  734. end
  735. fclose(fid);
  736. status=0;
  737. return
  738. % ************* End of function writeHc*********************************************
  739. % **************************************************************************************
  740. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  741. %%%%%%%%%%Function checkMrk %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  742. function MarkerFileOK=checkMrk(ds);
  743. % Examines structure ds to see if a sensible MarkerFile can be created from ds.mrk.
  744. % Output: MarkerFileOK=1 : ds.mrk looks OK
  745. % MarkerFileOK=0 : ds.mrk cannot be a set of valid markers for these data.
  746. MarkerFileOK=isfield(ds,'mrk');
  747. if MarkerFileOK
  748. MarkerFileOK=~isempty(ds.mrk);
  749. end
  750. if MarkerFileOK
  751. % Are the markers appropriate?
  752. minMarkerTrial=[];
  753. minMarkerTime=[];
  754. maxMarkerTrial=[];
  755. maxMarkerTime=[];
  756. for k=1:length(ds.mrk)
  757. maxMarkerTrial=max([maxMarkerTrial max(ds.mrk(k).trial)]);
  758. maxMarkerTime=max([maxMarkerTime max(ds.mrk(k).time)]);
  759. minMarkerTrial=min([minMarkerTrial min(ds.mrk(k).trial)]);
  760. minMarkerTime=min([minMarkerTime min(ds.mrk(k).time)]);
  761. end
  762. if isempty(maxMarkerTrial) | isempty(maxMarkerTime)
  763. MarkerFileOK=0; % Do not create MarkerFile.mrk if all of the marker classes are empty.
  764. else
  765. MarkerFileOK=(maxMarkerTrial<=ds.res4.no_trials & minMarkerTrial>=1 & ...
  766. maxMarkerTime<=(ds.res4.no_samples/ds.res4.sample_rate) & ...
  767. minMarkerTime>=(-ds.res4.preTrigPts/ds.res4.sample_rate));
  768. if ~MarkerFileOK
  769. fprintf(['\nwriteCTFds (checkMrk): ds.mrk cannot possibly be a set of markers ',...
  770. 'for array(data).\n']);
  771. fprintf([' minMarkerTrial=%d (must be >=1) ',...
  772. 'maxMarkerTrial=%d (must be <=%d)\n'],...
  773. minMarkerTrial,maxMarkerTrial,ds.res4.no_trials);
  774. fprintf([' minMarkerTime=%7.4f (must be >=%7.4f) ',...
  775. 'maxMarkerTrial=%7.4f (must be <=%7.4f )\n'],...
  776. minMarkerTime,-ds.res4.preTrigPts/ds.res4.sample_rate,...
  777. maxMarkerTime,ds.res4.no_samples/ds.res4.sample_rate);
  778. fprintf(' MarkerFile.mrk will not be created.\n\n');
  779. end
  780. end
  781. end
  782. return
  783. %%%%%%%%%%%%%% end of checkMrk %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  784. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  785. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  786. %%%%%%%%% Function writeEEG %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  787. function writeEEG(eegFileName,EEG);
  788. % Reads the EEG file of a dtaset and stores the infoemation in strucure array EEG.
  789. % EEG(k).chanName = channel name in the dataset (EEGmmm where mmm=channel number)
  790. % EEG(k).name = channel name given by the user (e.g. Fp4)
  791. % EEG(k).pos = electrode position in cm in CTF head coordinates
  792. % Check inputs
  793. if exist('eegFileName')~=1;eegFileName=char([]);end
  794. if ~ischar(eegFileName) | isempty(eegFileName)
  795. fprintf('writeCTFds (writeEEG): Called writeEEG with bad file name.\n');
  796. eegFileName
  797. EEG=struct([]);
  798. end
  799. if exist('EEG')~=1
  800. EEG=struct([]);
  801. elseif ~isstruct(EEG)
  802. EEG=struct([]);
  803. end
  804. if isempty(EEG);return;end
  805. fid=fopen(eegFileName,'w','ieee-be');
  806. if fid<0
  807. fprintf('writeCTFds (writeEEG): Could not open file %s\n',eegFileName);
  808. return
  809. end
  810. nEEG=length(EEG);
  811. for k=1:nEEG
  812. fprintf(fid,'%d\t%s\t%7.5f\t%7.5f\t%7.5f\n',EEG(k).chanNum,EEG(k).name,EEG(k).pos);
  813. end
  814. fclose(fid);
  815. return
  816. %%%%%%%%% End of writeEEG %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  817. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  818. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  819. %%%%%%%%% Function writeBadSegments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  820. function writeBadSegments(badSegmentsFile,badSegments,nTrial,tTrial);
  821. % Creates a bad.segements file in a CTF data set from the information in structure
  822. % badSegments which is created by read_badSegments, or by the user.
  823. % If structure badSegments is empty, then the file is not created.
  824. % badSegments structure:
  825. % badSegments.trial = List of trial numbers
  826. % badSegments.StartTime = List of bad segment start times (relative to trial).
  827. % badSegments.EndTime = List of bad segment end times.
  828. % Check badSegmentsFile
  829. if exist('badSegmentsFile')~=1;badSegmentsFile=char([]);end
  830. if isempty(badSegmentsFile) | ~ischar(badSegmentsFile)
  831. fprintf('writeCTFds(writeBadSegments): Bad file name.\n');
  832. badSegmentsFile
  833. return
  834. end
  835. % Check that structure badSegments is defined correctly
  836. if exist('badSegments')~=1 | exist('nTrial')~=1
  837. return
  838. elseif ~isstruct(badSegments) | isempty(badSegments)
  839. return
  840. elseif ~isfield(badSegments,'trial') | ~isfield(badSegments,'StartTime') | ...
  841. ~isfield(badSegments,'EndTime')
  842. return
  843. elseif isempty(badSegments.trial) | isempty(badSegments.StartTime) | ...
  844. isempty(badSegments.EndTime)
  845. return
  846. elseif ~isequal(size(badSegments.trial),size(badSegments.StartTime),...
  847. size(badSegments.EndTime))
  848. fprintf(['\nwriteCTFds (writeBadSegments): ',...
  849. 'The fields of structure badSegments do not all have the same size.\n']);
  850. return
  851. elseif any(badSegments.trial>nTrial) | any(badSegments.trial<1) | ...
  852. any(badSegments.EndTime>tTrial)
  853. fprintf(['\nwriteCTFds (writeBadSegments): ds.badSegments cannot possibly describe ',...
  854. 'bad segments for these data.\n',...
  855. '\tmin(badSegments.trial)=%d max(badSegments.trial)=%d ',...
  856. 'max(badSegments.EndTime)=%0.4f s\n\t\tDataset: nTrial=%d tTrial=%0.4f s\n'],...
  857. min(badSegments.trial),max(badSegments.trial),max(badSegments.EndTime),...
  858. nTrial,tTrial);
  859. fprintf('\t\tbad.segments file will not be created.\n\n');
  860. return
  861. end
  862. % Convert all fields to simple vectors
  863. nSeg=prod(size(badSegments.trial));
  864. trial=reshape(badSegments.trial,1,nSeg);
  865. StartTime=reshape(badSegments.StartTime,1,nSeg);
  866. EndTime=reshape(badSegments.EndTime,1,nSeg);
  867. fid=fopen(badSegmentsFile,'w','ieee-be');
  868. if fid<0
  869. fprintf('writeCTFds (writeBadSegments): Could not open file %s\n',badSegmentsFile);
  870. return
  871. end
  872. % Extra tabs are inserted to reproduce the format of bad.segments files produced by
  873. % DataEditor (5.3.0-experimental-linux-20060918).
  874. fprintf(fid,'%0.6g\t\t%0.6g\t\t%0.6g\t\t\n',[trial;StartTime;EndTime]);
  875. fclose(fid);
  876. return
  877. %%%%%%%%% End of writeBadSegments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  878. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  879. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  880. %%%%%%%%%%Function check_cls %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  881. function ClassFileOK=check_cls(ds);
  882. % Examines structure ds to see if a sensible ClassFile can be created from ds.TrialClass.
  883. ClassFileOK=isfield(ds,'TrialClass');
  884. if ClassFileOK
  885. ClassFileOK=~isempty(ds.TrialClass);
  886. end
  887. if ClassFileOK
  888. % Are the class trials appropriate?
  889. minClassTrial=[];
  890. maxClassTrial=[];
  891. for k=1:length(ds.TrialClass)
  892. maxClassTrial=max([maxClassTrial max(ds.TrialClass(k).trial)]);
  893. minClassTrial=min([minClassTrial min(ds.TrialClass(k).trial)]);
  894. end
  895. % Create ClassFile.cls even when the trail classes are empty.
  896. if ~isempty(maxClassTrial)
  897. ClassFileOK=(maxClassTrial<=ds.res4.no_trials & minClassTrial>=1);
  898. if ~ClassFileOK
  899. fprintf(['\nwriteCTFds (check_cls): ds.TrialClass cannot possibly be a set of ',...
  900. 'trial classes for array(data).\n minClassTrial=%d (must be >=1) ',...
  901. 'maxClassTrial=%d (must be <=%d)\n'],...
  902. minClassTrial,maxClassTrial,ds.res4.no_trials);
  903. fprintf(' ClassFile.cls will not be created.\n');
  904. end
  905. end
  906. end
  907. return
  908. %%%%%%%%%%%%%% end of check_cls %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  909. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  910. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  911. %%%%%%%%%% Function writeClassFile %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  912. function writeClassFile(ClassFile,TrialClass);
  913. % Write the ClassFile of a CTF data set.
  914. % The CLassFile allows a user to store a list of trial classifications in a data set.
  915. % The ClassFile format is defined in document CTF MEG File Formats, PN900-0088.
  916. % This format is rigid.
  917. % Inputs :
  918. % ClassFile : marker file including the full path and extension .mrk.
  919. % TrialClass : Structure creted by read_ClassFile.
  920. % Output : ClassFile.cls.
  921. % Check input TrialClass.
  922. if exist('TrialClass')~=1;TrialClass=[];end
  923. if isempty(TrialClass) | ~isstruct(TrialClass)
  924. fprintf('writeCTFds (writeClassFile): TrialClass is empty or is not a structure.\n');
  925. TrialClass
  926. return
  927. end
  928. % Check ClassFile
  929. if exist('ClassFile')~=1;ClassFile=char([]);end
  930. if isempty(ClassFile) | ~ischar(ClassFile)
  931. fprintf('writeCTFds (writeClassFile): Bad file name.\n');
  932. ClassFile
  933. end
  934. fid=fopen(ClassFile,'w','ieee-be');
  935. if fid<0
  936. fprintf('writeCTFds (writeClassFile): Could not open file %s\n',ClassFile);
  937. return
  938. end
  939. nClass=length(TrialClass);
  940. % Generate datasetname from ClassFIle.
  941. ksep=max([0 strfind(ClassFile,filesep)]);
  942. datasetname=ClassFile(1:ksep-1);
  943. if isempty(datasetname);datasetname=cd;end
  944. fprintf(fid,'PATH OF DATASET:\n%s\n\n\n',datasetname);
  945. fprintf(fid,'NUMBER OF CLASSES:\n%d\n\n\n',nClass);
  946. for k=1:nClass
  947. if k==1 % Add sign character to make output match the output of Acq.
  948. sgn=char([]); % There should be no real significance to this.
  949. else % Why does DataEditor places the + sign only on ClassID 2,3,...?
  950. sgn='+';
  951. end
  952. No_of_Trials=prod(size(TrialClass(k).trial));
  953. fprintf(fid,'CLASSGROUPID:\n%s%d\nNAME:\n%s\nCOMMENT:\n%s\n',...
  954. sgn,TrialClass(k).ClassGroupId,TrialClass(k).Name,TrialClass(k).Comment);
  955. fprintf(fid,'COLOR:\n%s\nEDITABLE:\n%s\nCLASSID:\n%s%d\nNUMBER OF TRIALS:\n%d\n',...
  956. TrialClass(k).Color,TrialClass(k).Editable,sgn,TrialClass(k).ClassId,No_of_Trials);
  957. fprintf(fid,'LIST OF TRIALS:\nTRIAL NUMBER\n');
  958. % Subtract one so trial numbering starts at 0 in ClassFile.cls
  959. fprintf(fid,'%20d\n',reshape(TrialClass(k).trial,1,No_of_Trials)-1);
  960. fprintf(fid,'\n\n');
  961. end
  962. fclose(fid);
  963. return
  964. %%%%%%%%%%%%%% end of writeClassFile %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  965. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  966. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  967. %%%%%%%%%% Function writeVirtualChannels %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  968. function writeVirtualChannels(VirtualChannelsFile,Virtual);
  969. % Writes a VirtualChannels file using the information in structure Virtual.
  970. % S\tructure Virtual is prepared by read_VirtualChannels
  971. % Check VirtualChannelsFile
  972. if exist('VirtualChannelsFile')~=1;VirtualChannelsFile=char([]);end
  973. if isempty(VirtualChannelsFile) | ~ischar(VirtualChannelsFile)
  974. fprintf('write_VirtualChannelsFile: Bad file name.\n');
  975. VirtualChannelsFile
  976. return
  977. end
  978. % Check structure array Virtual
  979. if exist('Virtual')~=1
  980. fprintf('writeVirtualChannels: Must specify structure Virtual.\n');
  981. return
  982. elseif isempty(Virtual) | ~isstruct(Virtual)
  983. return
  984. elseif ~isfield(Virtual,'Name') | ~isfield(Virtual,'wt');
  985. return
  986. elseif isempty(Virtual(1).Name)
  987. return
  988. end
  989. fid=fopen(VirtualChannelsFile,'w','ieee-be');
  990. if fid<0
  991. fprintf('writeCTFds (writeVirtualChannels): Could not open file %s\n',VirtualChannelsFile);
  992. return
  993. end
  994. fprintf(fid,'//Virtual channel configuration\n\n');
  995. for k=1:length(Virtual)
  996. fprintf(fid,'VirtualChannel\n{\n\tName:\t%s\n\tUnit:\t%s\n',...
  997. Virtual(k).Name,Virtual(k).Unit);
  998. for q=1:size(Virtual(k).wt)
  999. % Floating format chosen to match VirtualChanels file creatd by
  1000. % DataEditor (5.3.0-experimental-linux-20060918).
  1001. fprintf(fid,'\tRef:\t%s,%0.6g\n',deblank(Virtual(k).chan(q,:)),Virtual(k).wt(q));
  1002. end
  1003. fprintf(fid,'}\n\n');
  1004. end
  1005. fclose(fid);
  1006. return
  1007. %%%%%%%%%%%%%% end of writeVirtualChannels %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1008. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1009. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1010. %%%%%%%%%%%%%% Function updateDescriptors %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1011. function ds=updateDescriptors(ds,comment,creatorSoftware,HLCcntrl);
  1012. % Makes sure that certain tags are in structure infods, and makes sure that information
  1013. % is transfered to infods and newds from res4. Updates several fields of res4 with
  1014. % clinical use message (=comment) and name of creator program.
  1015. % Inputs: ds : ds structure produced by readCTFds.
  1016. % comment : Character string that is added to infods tags listed in addCommentTag
  1017. % and res4 fields listed in addCommentField
  1018. % creatorSoftware : Character string indicating that the data set was
  1019. % created by writeCTFds. Added to infods tags listed in addCreatorTag and
  1020. % appName field of res4.
  1021. % HLCcntrl: If ds.infods is missing or empty, HLCcntrl determines the
  1022. % _DATA_HZ_MODE tage of infods. if noit present or empty, HLCcntrl=0.
  1023. % Creates a dummy infods structure if necessary using function make_dummy_infods.
  1024. % Changes infods and newds to match information in
  1025. % ds.res4.nf_run_title
  1026. % ds.res4.collect_descriptor
  1027. % ds.res4.nf_subject_id
  1028. % ds.res4.nf_operator
  1029. % Adds comment (clinical use message) and creator software name to infods, newds and hist files.
  1030. if ~isfield(ds,'infods');ds.infods=[];end
  1031. % infods tags that will display the comment.
  1032. addCommentTag=strvcat('_PATIENT_ID','_PATIENT_INSTITUTE','_PROCEDURE_COMMENTS',...
  1033. '_DATASET_COMMENTS','_DATASET_STATUS','_DATASET_PROCSTEPTITLE');
  1034. % res4 text fields that will display the comment
  1035. addCommentField=strvcat('appName','dataOrigin','dataDescription',...
  1036. 'nf_run_title','nf_subject_id','run_description');
  1037. % res4 text string lengths (from document "CTF MEG File Formats", PN900-0088)
  1038. addCommentLength=[256 256 256 256 32 -1]'; % -1 indicates variable length
  1039. % infods tags that will display the creator software.
  1040. addCreatorTag=strvcat('_PROCEDURE_COMMENTS','_DATASET_STATUS',...
  1041. '_DATASET_COLLECTIONSOFTWARE','_DATASET_CREATORSOFTWARE');
  1042. % res4 text fields that will display the creator software.
  1043. addCreatorField=strvcat('appName','run_description');
  1044. addCreatorLength=[256 -1]';
  1045. % List of infods tags that will be set to ds.res4.fields (not necessarily fields listed in
  1046. % addCommentField or addCreatorField
  1047. addRes4Tag=strvcat('_PATIENT_ID','_DATASET_PROCSTEPTITLE',...
  1048. '_DATASET_PROCSTEPDESCRIPTION','_DATASET_OPERATORNAME');
  1049. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1050. % Add the Tags to infods. In most cases they will already be there, but just to be sure.
  1051. addTag=strvcat(addRes4Tag,addCommentTag,addCreatorTag);
  1052. tagClass=strvcat('_PATIENT','_PROCEDURE','_DATASET');
  1053. % List of all the tag names as a character array
  1054. tagName=getArrayField(ds.infods,'name')';
  1055. if length(ds.infods)==1;tagName=tagName';end
  1056. tagType=getArrayField(ds.infods,'type');
  1057. tagPtr=1; % If a class is missing, inject the class starting at tagPtr+1.
  1058. for k=1:size(tagClass,1)
  1059. addIndex=strmatch(deblank(tagClass(k,:)),addTag)';
  1060. % Are there any tags to be added in this class?
  1061. if isempty(addIndex);continue;end
  1062. % List of infods tags in the tagClass, but excluding the CPerist type (which marks the
  1063. % start of a class.
  1064. infodsIndex=strmatch(deblank(tagClass(k,:)),tagName);
  1065. if isempty(infodsIndex) % Create a new class of tags.
  1066. if strcmp(deblank(tagName(tagPtr+1,:)),'EndOfParameters') & k>1;
  1067. tagPtr=tagPtr+1;
  1068. end
  1069. nTag=length(ds.infods);
  1070. tagName((tagPtr+4):(nTag+3),:)=tagName((tagPtr+1):nTag,:);

Large files files are truncated, but you can click here to view the full file