openNEV.m 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. function NEV = openNEV(varargin)
  2. %%
  3. % Opens an .nev file for reading, returns all file information in a NEV
  4. % structure. Works with File Spec 2.1 & 2.2.
  5. % This version reads only waveforms of a specified list of neuron IDs.
  6. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  7. % Use OUTPUT = openNEV(fname, 'read', 'wave_elec', 'wave_unit', 'report', 'noparse', 'nowarning', 'precision').
  8. %
  9. % All input arguments are optional. Input arguments can be in any order.
  10. %
  11. % fname: Name of the file to be opened. If the fname is omitted
  12. % the user will be prompted to select a file using an open
  13. % file user interface.
  14. % DEFAULT: Will open Open File UI.
  15. %
  16. % 'read': Will read the waveform data if user passes this argument.
  17. % The field .Data.Spikes.WaveformIndices contains the index
  18. % of each waveform i in .Data.Spikes.Waveform(i,:) into
  19. % arrays such as, e.g., .Data.Spikes.Electrode (useful when
  20. % selecting only sets of electrodes/units using 'wave_unit'
  21. % and 'wave_elec').
  22. % DEFAULT: will not read data.
  23. %
  24. % 'wave_unit': User can specify which units are used to read spike
  25. % waveforms (option "read"). The units can be selected
  26. % either by specifying a range (e.g. 1:3) or by indicating
  27. % individual units (e.g. 1,2,4) or both. This field needs
  28. % to be followed by the prefix 'u:'. See example for more
  29. % details.
  30. % DEFAULT: will read all existing units.
  31. %
  32. % 'wave_elec': User can specify which electrodes are used to read spike
  33. % waveforms (option "read"). The number of electrodes can
  34. % be greater than or equal to 1 and less than or equal to
  35. % 128. The electrodes can be selected either by specifying
  36. % a range (e.g. 20:45) or by indicating individual
  37. % electrodes (e.g. 3,6,7,90) or both. This field needs to
  38. % be followed by the prefix 'e:'. See example for more
  39. % details.
  40. % DEFAULT: will read from all existing channels.
  41. %
  42. % 'report': Will show a summary report if user passes this argument.
  43. % DEFAULT: will not show report.
  44. %
  45. % 'noparse': The code will not parse the experimental parameters.
  46. % See below for format.
  47. % DEFAULT: will parse the parameters.
  48. %
  49. % 'nowarning': The code will not give a warning if there is an error in
  50. % parsing or several other actions.
  51. % DEFAULT: will give warning message.
  52. %
  53. % 'nosave': The code will not save a copy of the NEV structure as a
  54. % MAT file. By default the code will save a copy in the
  55. % same folder as the NEV file for easy future access. If
  56. % "wave_elec" (e:) or "wave_unit" (u:) is specified, the
  57. % mat file is never saved!
  58. % DEFAULT: will save the MAT file, unless e: or u: is
  59. % specified.
  60. %
  61. % 'nomat': Will not look for a MAT file. This option will force
  62. % openNEV to open a NEV file instead of any available MAT
  63. % files. If "wave_elec" (e:) or "wave_unit" (u:) is
  64. % specified, the mat file is never loaded!
  65. % DEFAULT: will load the MAT file if available, unless e:
  66. % or u: is specified.
  67. %
  68. % 'compact': If specified the spike data is stored in 'int16' type
  69. % instead of default double precision.
  70. %
  71. % OUTPUT: Contains the NEV structure.
  72. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  73. % USAGE EXAMPLE:
  74. %
  75. % openNEV('report','read','c:\data\sample.nev','u:1','e:80');
  76. %
  77. % In the example above, the file c:\data\sample.nev will be used. A
  78. % report of the file contents will be shown. The digital data will be
  79. % parsed. The data needs to be in the proper format (refer below). The
  80. % waveforms of unit 1 on electrode 80 is also stored in the resulting
  81. % structure.
  82. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  83. % DIGITAL PARAMETERS/MARKERS FORMAT:
  84. %
  85. % The experimental parameters need to be in the following format for the
  86. % code to properly parse them:
  87. %
  88. % *Label:Parameter1=value1;Parameter2=value2;Parameter3=value3;#
  89. %
  90. % EXAMPLES:
  91. % *ExpParameter:Intensity=1.02;Duration=400;Trials=1;PageSegment=14;#
  92. % *Stimulation:StimCount=5;Duration=10;#
  93. %
  94. % The above line is an "ExpParameter". The parameters are, "Intensity,
  95. % Duration, Trials, and PageSement." The values of those parameters are,
  96. % respectively, "1.02, 400, 1, and 14." The second example is a
  97. % "Stimulation". The name of the parameters are "StimCount" and
  98. % "Duration" and the values are "5" and "10" respectively.
  99. %
  100. % It can also read single value markers that follow the following format.
  101. %
  102. % *MarkerName=Value;#
  103. %
  104. % EXAMPLE:
  105. % *WaitSeconds=10;# OR
  106. % *JuiceStatus=ON;#
  107. %
  108. % The above line is a "Marker". The marker value is 10 in the first
  109. % and it's ON in the second example.
  110. %
  111. % The label, parameter name, and values are flexible and can be anything.
  112. % The only required formatting is that the user needs to have a label
  113. % followed by a colon ':', followed by a field name 'MarkerVal', followed
  114. % by an equal sign '=', followed by the parameter value '10', and end
  115. % with a semi-colon ';'.
  116. %
  117. % NOTE:
  118. % Every parameter requires a pound-sign '#' at the very end.
  119. % Every parameter requires a star sign '*' at the very beginning. If you
  120. % use my LabVIEW SendtoCerebus VI then there is no need for a '*' in the
  121. % beginning.
  122. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  123. % Kian Torab
  124. % kian.torab@utah.edu
  125. % Department of Bioengineering
  126. % University of Utah
  127. % Version 3.5.0 - March 22, 2010
  128. %
  129. % 2010-06-28: - Added code for selecting only specific waveforms using the
  130. % directives "e:" and "u:". A further field
  131. % .Data.Spikes.WaveformIndices was added to save the packet
  132. % indices of those waveforms that make up
  133. % .Data.Spikes.Waveform. A few clear statements have been
  134. % removed/moved to enable this functionality -- this should
  135. % not cause siginificant memory problems (however, current
  136. % optimizations with "clear" are very quick-and-dirty
  137. % anyhow, so there should be room for improvement/cleanup).
  138. % - Changed default behavior to NOT read the matlab files or
  139. % write them if either e: or u: is supplied for obvious
  140. % readons.
  141. % Author: Michael Denker
  142. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  143. NEVver = '3.5.0'; % optimize for memory, supporting general usage
  144. disp(['openNEV version ' NEVver])
  145. %% Validating existance of parseCommand
  146. if exist('parseCommand.m', 'file') ~= 2
  147. disp('This version of openNEV requires function parseCommand.m to be placed in path.');
  148. return;
  149. end
  150. %% Defining structures
  151. NEV = struct('MetaTags',[],'IOLabels',[], 'ElectrodesInfo', [], 'Data',[]);
  152. NEV.MetaTags = struct('Subject', [], 'Experimenter', [], 'DateTime',[],...
  153. 'SampleRes',[],'Comment',[],'FileTypeID',[],'Flags',[]);
  154. NEV.Data = struct('SerialDigitalIO', [], 'Spikes', []);
  155. NEV.Data.Spikes = struct('Timestamps',[],'Electrode',[],...
  156. 'Unit',[],'Waveform',[]);
  157. NEV.Data.SerialDigitalIO = struct('InputType',[],'TimeStamp',[],...
  158. 'TimeStampSec',[],'Type',[],'Value',[]);
  159. Electrodes=[];
  160. Units=[];
  161. %% Validating input arguments
  162. for i=1:length(varargin)
  163. if strncmp(varargin{i}, 'e:', 2)
  164. Electrodes = str2num(varargin{i}(3:end)); %#ok<ST2NM>
  165. elseif strncmp(varargin{i}, 'u:', 2)
  166. Units = str2num(varargin{i}(3:end)); %#ok<ST2NM>
  167. else
  168. switch varargin{i}
  169. case 'report'
  170. Report = varargin{i};
  171. case 'read'
  172. ReadData = varargin{i};
  173. case 'nosave'
  174. SaveFile = varargin{i};
  175. case 'nomat'
  176. NoMAT = varargin{i};
  177. case 'nowarning'
  178. WarningStat = varargin{i};
  179. case 'noparse'
  180. ParseData = 'noparse';
  181. case 'compact'
  182. Compact = 'compact';
  183. otherwise
  184. temp = varargin{i};
  185. if length(temp)>3 && strcmpi(temp(end-3),'.')
  186. fname = varargin{i};
  187. if exist(fname, 'file') ~= 2
  188. disp('The file does not exist.');
  189. clear variables;
  190. if nargout; NEV = []; end;
  191. return;
  192. end
  193. else
  194. if ~isnumeric(varargin{i})
  195. disp(['Invalid argument ''' varargin{i} ''' .']);
  196. else
  197. disp(['Invalid argument ''' num2str(varargin{i}) ''' .']);
  198. end
  199. clear variables;
  200. return;
  201. end
  202. end
  203. end
  204. end
  205. %% Defining and validating variables
  206. if ~exist('fname', 'var')
  207. [fname, fpath] = uigetfile('D:\Data\*.nev');
  208. else
  209. [fpath,fname, fext] = fileparts(fname);
  210. fname = [fname fext];
  211. fpath = [fpath '/'];
  212. end
  213. if ~exist('Report', 'var'); Report = 'noreport'; end
  214. if ~exist('WarningStat', 'var'); WarningStat = 'warning'; end;
  215. if ~exist('ReadData', 'var'); ReadData = 'noread'; end
  216. if ~exist('ParseData', 'var'); ParseData = 'parse'; end
  217. if ~exist('SaveFile', 'var'); SaveFile = 'save'; end;
  218. if ~exist('NoMAT', 'var'); NoMAT = 'yesmat'; end;
  219. if ~exist('Compact', 'var'); Compact = 'non-compact'; end;
  220. if fname==0; clear variables; if nargout; NEV = []; end; disp('No file was selected.'); return; end;
  221. tic;
  222. matPath = [fpath fname(1:end-4) '.mat'];
  223. %if only part of the waveforms are read, do not load or save the MAT file.
  224. if (~isempty(Units) || ~isempty(Electrodes)) && (strcmp(NoMAT,'yesmat') || strcmp(SaveFile,'save') )
  225. NoMAT='nomat';
  226. SaveFile='nosave';
  227. if strcmp(WarningStat,'warning')
  228. disp('WARNING: Overriding and enabling nomat/nosave options, as electrodes (e:) and/or units (u:) was specified.');
  229. end
  230. end
  231. %% Check for a MAT file and load that instead of NEV
  232. if exist(matPath, 'file') == 2 && strcmpi(NoMAT, 'yesmat')
  233. disp('MAT file corresponding to selected NEV file already exists. Loading MAT instead...');
  234. load(matPath);
  235. if isempty(NEV.Data.Spikes.Waveform) && strcmpi(ReadData, 'read')
  236. disp('The MAT file does not contain waveforms. Loading NEV instead...');
  237. else
  238. if ~nargout
  239. assignin('base', 'NEV', NEV);
  240. clear variables;
  241. end
  242. return;
  243. end
  244. end
  245. %% Reading BasicHeader information from file
  246. FID = fopen([fpath fname], 'r', 'ieee-le');
  247. BasicHeader = fread(FID, 336, '*uint8');
  248. NEV.MetaTags.FileTypeID = char(BasicHeader(1:8)');
  249. fileSpec = num2str((BasicHeader(9:10)'));
  250. NEV.MetaTags.Flags = dec2bin(typecast(BasicHeader(11:12), 'uint16'),16);
  251. fExtendedHeader = double(typecast(BasicHeader(13:16), 'uint32'));
  252. PacketBytes = double(typecast(BasicHeader(17:20), 'uint32'));
  253. TimeRes = double(typecast(BasicHeader(21:24), 'uint32'));
  254. NEV.MetaTags.SampleRes = typecast(BasicHeader(25:28), 'uint32');
  255. t = typecast(BasicHeader(29:44), 'uint16');
  256. NEV.MetaTags.Comment = char(BasicHeader(77:332)');
  257. NEV.MetaTags.Filename = fname;
  258. ExtHeaderCount = typecast(BasicHeader(333:336), 'uint32');
  259. if strcmpi(NEV.MetaTags.FileTypeID, 'NEURALEV')
  260. disp(['Current NEV Filespec: ' fileSpec]);
  261. if exist([fpath fname(1:end-8) '.sif'], 'file') == 2
  262. METATAGS = textread([fpath fname(1:end-8) '.sif'], '%s');
  263. NEV.MetaTasg.Subject = METATAGS{3}(5:end-5);
  264. NEV.MetaTags.Experimenter = [METATAGS{5}(8:end-8) ' ' METATAGS{6}(7:end-7)];
  265. end
  266. elseif strcmpi(fileSpec, '2 1')
  267. disp('Current NEV Filespec: 2.1');
  268. else
  269. disp('Unknown filespec. Cannot open file.')
  270. clear variables;
  271. return;
  272. end
  273. %% Parsing and validating FileSpec and DateTime variables
  274. NEV.MetaTags.DateTime = [num2str(t(2)) '/' num2str(t(4)) '/' num2str(t(1))...
  275. ' ' datestr(double(t(3)), 'dddd') ' ' num2str(t(5)) ':' ...
  276. num2str(t(6)) ':' num2str(t(7)) '.' num2str(t(8))] ;
  277. %% Removing extra garbage characters from the Comment field.
  278. NEV.MetaTags.Comment(find(NEV.MetaTags.Comment==0,1):end) = 0;
  279. %% Recording after BasicHeader file position
  280. fBasicHeader = ftell(FID); %#ok<NASGU>
  281. %% Reading ExtendedHeader information
  282. for ii=1:ExtHeaderCount
  283. ExtendedHeader = fread(FID, 32, '*uint8');
  284. PacketID = char(ExtendedHeader(1:8)');
  285. switch PacketID
  286. case 'ARRAYNME'
  287. NEV.ArrayInfo.ElectrodeName = char(ExtendedHeader(9:end));
  288. case 'ECOMMENT'
  289. NEV.ArrayInfo.ArrayComment = char(ExtendedHeader(9:end));
  290. case 'CCOMMENT'
  291. NEV.ArrayInfo.ArrayCommentCont = char(ExtendedHeader(9:end));
  292. case 'MAPFILE'
  293. NEV.ArrayInfo.MapFile = char(ExtendedHeader(9:end));
  294. case 'NEUEVWAV'
  295. PacketID = typecast(ExtendedHeader(9:10), 'uint16');
  296. NEV.ElectrodesInfo{PacketID, 1}.ElectrodeID = PacketID;
  297. NEV.ElectrodesInfo{PacketID, 1}.ConnectorBank = char(ExtendedHeader(11)+64);
  298. NEV.ElectrodesInfo{PacketID, 1}.ConnectorPin = ExtendedHeader(12);
  299. NEV.ElectrodesInfo{PacketID, 1}.DigitalFactor = typecast(ExtendedHeader(13:14),'uint16');
  300. NEV.ElectrodesInfo{PacketID, 1}.EnergyThreshold = typecast(ExtendedHeader(15:16),'uint16');
  301. NEV.ElectrodesInfo{PacketID, 1}.HighThreshold = typecast(ExtendedHeader(17:18),'int16');
  302. NEV.ElectrodesInfo{PacketID, 1}.LowThreshold = typecast(ExtendedHeader(19:20),'int16');
  303. NEV.ElectrodesInfo{PacketID, 1}.Units = ExtendedHeader(21);
  304. NEV.ElectrodesInfo{PacketID, 1}.WaveformBytes = ExtendedHeader(22);
  305. case 'NEUEVLBL'
  306. PacketID = typecast(ExtendedHeader(9:10), 'uint16');
  307. NEV.ElectrodesInfo{PacketID, 1}.ElectrodeLabel = char(ExtendedHeader(11:26));
  308. case 'NEUEVFLT'
  309. PacketID = typecast(ExtendedHeader(9:10), 'uint16');
  310. NEV.ElectrodesInfo{PacketID, 1}.HighFreqCorner = typecast(ExtendedHeader(11:14),'uint32');
  311. NEV.ElectrodesInfo{PacketID, 1}.HighFreqOrder = typecast(ExtendedHeader(15:18),'uint32');
  312. NEV.ElectrodesInfo{PacketID, 1}.HighFilterType = typecast(ExtendedHeader(19:20),'uint16');
  313. NEV.ElectrodesInfo{PacketID, 1}.LowFreqCorner = typecast(ExtendedHeader(21:24),'uint32');
  314. NEV.ElectrodesInfo{PacketID, 1}.LowFreqOrder = typecast(ExtendedHeader(25:28),'uint32');
  315. NEV.ElectrodesInfo{PacketID, 1}.LowFilterType = typecast(ExtendedHeader(29:30),'uint16');
  316. case 'DIGLABEL'
  317. Label = char(ExtendedHeader(9:24));
  318. Mode = ExtendedHeader(25);
  319. NEV.IOLabels{Mode+1, 1} = Label;
  320. case 'NSASEXEV' %% Not implemented in the Cerebus firmware.
  321. %% Needs to be updated once implemented into the
  322. %% firmware by Blackrock Microsystems.
  323. NEV.NSAS.Freq = typecast(ExtendedHeader(9:10),'uint16');
  324. NEV.NSAS.DigInputConf = char(ExtendedHeader(11));
  325. NEV.NSAS.AnalCh1Conf = char(ExtendedHeader(12));
  326. NEV.NSAS.AnalCh1Detect = typecast(ExtendedHeader(13:14),'uint16');
  327. NEV.NSAS.AnalCh2Conf = char(ExtendedHeader(15));
  328. NEV.NSAS.AnalCh2Detect = typecast(ExtendedHeader(16:17),'uint16');
  329. NEV.NSAS.AnalCh3Conf = char(ExtendedHeader(18));
  330. NEV.NSAS.AnalCh3Detect = typecast(ExtendedHeader(19:20),'uint16');
  331. NEV.NSAS.AnalCh4Conf = char(ExtendedHeader(21));
  332. NEV.NSAS.AnalCh4Detect = typecast(ExtendedHeader(22:23),'uint16');
  333. NEV.NSAS.AnalCh5Conf = char(ExtendedHeader(24));
  334. NEV.NSAS.AnalCh5Detect = typecast(ExtendedHeader(25:26),'uint16');
  335. otherwise
  336. display(['PacketID ' PacketID ' is unknown.']);
  337. end
  338. end
  339. %% Recording after ExtendedHeader file position and calculating Data Length
  340. % and number of data packets
  341. fseek(FID, 0, 'eof');
  342. fData = ftell(FID);
  343. DataPacketCount = (fData - fExtendedHeader)/PacketBytes;
  344. DataLen = PacketBytes - 8; %#ok<NASGU>
  345. UnparsedDigitalDataFlag = 0;
  346. %% Reading data packets if 'read' is passed as an argument
  347. fseek(FID, fExtendedHeader, 'bof');
  348. Timestamps = fread(FID, DataPacketCount, '*uint32', PacketBytes-4);
  349. fseek(FID, fExtendedHeader+4, 'bof');
  350. PacketIDs = fread(FID, DataPacketCount, '*uint16', PacketBytes-2);
  351. fseek(FID, fExtendedHeader+6, 'bof');
  352. tempClassOrReason = fread(FID, DataPacketCount, '*uchar', PacketBytes-1);
  353. fseek(FID, fExtendedHeader+8, 'bof');
  354. tempDigiVals = fread(FID, DataPacketCount, '*uint16', PacketBytes-2);
  355. %% Populate the NEV structure with spike timestamps, electrode numbers
  356. % and unit numbers
  357. nonNeuralIndices = find(PacketIDs == 0);
  358. neuralIndices = find(PacketIDs ~= 0);
  359. nonNeuTimestamps = Timestamps(nonNeuralIndices);
  360. NEV.Data.Spikes.Timestamps = Timestamps(neuralIndices);
  361. clear Timestamps; % save memory
  362. NEV.Data.Spikes.Electrode = PacketIDs(neuralIndices);
  363. NEV.Data.Spikes.Unit = tempClassOrReason(neuralIndices);
  364. %% Parse read digital data. Please refer to help to learn about the proper
  365. % formatting if the data.
  366. if strcmp(ParseData, 'parse')
  367. try
  368. InsertionReason = tempClassOrReason(find(PacketIDs == 0));
  369. DigiValues = char(tempDigiVals(nonNeuralIndices)');
  370. % % This section needs to be uncommented out for Justin
  371. % if int16(DigiValues(1)) > 128
  372. % DigiValues = char(DigiValues-128);
  373. % end
  374. AsteriskIndices = find(DigiValues == '*');
  375. DataBegTimestamps = nonNeuTimestamps(AsteriskIndices);
  376. Inputs = {'Digital'; 'AnCh1'; 'AnCh2'; 'AnCh3'; 'AnCh4'; 'AnCh5'; 'PerSamp'; 'Serial'};
  377. if ~isempty(DigiValues)
  378. if ~int8(DigiValues(2))
  379. DigiValues(find(DigiValues == DigiValues(2))) = [];
  380. end
  381. if strcmp(ParseData, 'parse') && ~isempty(DigiValues)
  382. splitDigiValues = regexp(DigiValues(2:end), '*', 'split')';
  383. for idx = 1:length(splitDigiValues)
  384. try
  385. if isempty(find(splitDigiValues{idx} == ':', 1))
  386. splitDigiValues{idx}(find(splitDigiValues{idx} == '#')) = [];
  387. NEV.Data.SerialDigitalIO(idx).Value = splitDigiValues{idx};
  388. NEV.Data.SerialDigitalIO(idx).Type = 'Marker';
  389. else
  390. [tempParsedCommand error] = parseCommand(splitDigiValues{idx});
  391. if ~error
  392. pcFields = fields(tempParsedCommand);
  393. for fidx = 1:length(pcFields)
  394. NEV.Data.SerialDigitalIO(idx).(pcFields{fidx}) = tempParsedCommand.(pcFields{fidx});
  395. end
  396. else
  397. NEV.Data.SerialDigitalIO(idx).Value = splitDigiValues{idx};
  398. NEV.Data.SerialDigitalIO(idx).Type = 'UnparsedData';
  399. UnparsedDigitalDataFlag = 1;
  400. end
  401. end
  402. catch
  403. disp(['Error parsing: ' splitDigiValues{idx}]);
  404. disp('Please refer to the help for more information on how to properly format the digital data for parsing.');
  405. end
  406. end
  407. % Populate the NEV structure with timestamps and inputtypes for the
  408. % digital data
  409. if ~isempty(DataBegTimestamps)
  410. c = num2cell(DataBegTimestamps); [NEV.Data.SerialDigitalIO(1:length(NEV.Data.SerialDigitalIO)).TimeStamp] = deal(c{1:end});
  411. c = num2cell(DataBegTimestamps/NEV.MetaTags.SampleRes); [NEV.Data.SerialDigitalIO.TimeStampSec] = deal(c{1:end});
  412. c = {Inputs{InsertionReason(AsteriskIndices)}}; [NEV.Data.SerialDigitalIO.InputType] = deal(c{1:end});
  413. end
  414. elseif ~isempty(DigiValues)
  415. NEV.Data.SerialDigitalIO.TimeStamp = nonNeuTimestamps;
  416. NEV.Data.SerialDigitalIO.UnparsedData = DigiValues;
  417. end
  418. else
  419. disp('No digital data to read.');
  420. end
  421. catch
  422. disp('An error occured during reading digital data. This is due to a problem with formatting digital data.');
  423. disp('Refer to help ''help openNEV'' for more information on how to properly format the digital data.');
  424. end
  425. else
  426. %clear tempClassOrReason;
  427. %clear PacketIDs;
  428. NEV.Data.SerialDigitalIO.UnparsedData = tempDigiVals(nonNeuralIndices);
  429. clear tempDigiVals;
  430. NEV.Data.SerialDigitalIO.TimeStamp = nonNeuTimestamps;
  431. NEV.Data.SerialDigitalIO.TimeStampSec = double(nonNeuTimestamps) / double(NEV.MetaTags.SampleRes);
  432. clear nonNeuTimestamps;
  433. end
  434. %% Reads the waveforms if 'read' is passed to the function
  435. if strcmp(ReadData, 'read')
  436. if (strcmp(Compact, 'compact'))
  437. SpikeWaveformType='int16';
  438. else
  439. SpikeWaveformType='double';
  440. end
  441. %read all waveforms or only a specific subset?
  442. if isempty(Units) && isempty(Electrodes)
  443. fseek(FID, fExtendedHeader + 8, 'bof'); % go to where spikes are located
  444. NEV.Data.Spikes.Waveform = fread(FID,...
  445. [(PacketBytes-8)/2 DataPacketCount], ...
  446. [num2str((PacketBytes-8)/2) '*int16=>' SpikeWaveformType],...
  447. 8)';
  448. %remove non neural indices -- these have no waveform
  449. NEV.Data.Spikes.Waveform(nonNeuralIndices, :) = [];
  450. %retain a list of all packet indices
  451. NEV.Data.Spikes.WaveformIndices=neuralIndices;
  452. else
  453. %find all data packets that correspond to this electrode and/or
  454. %unit
  455. waveform_inds=find((isempty(Units) | ismember(tempClassOrReason,Units)) & ((isempty(Electrodes) & PacketIDs~=0) | ismember(PacketIDs,Electrodes)));
  456. %pre-allocate waveform array
  457. NEV.Data.Spikes.Waveform=zeros(length(waveform_inds),(PacketBytes-8)/2,SpikeWaveformType);
  458. for k=1:length(waveform_inds)
  459. fseek(FID, fExtendedHeader + 8 + (waveform_inds(k)-1)*PacketBytes, 'bof'); % go to where the packet ist located
  460. NEV.Data.Spikes.Waveform(k,:) = fread(FID,...
  461. [(PacketBytes-8)/2 1], ...
  462. [num2str((PacketBytes-8)/2) '*int16=>' SpikeWaveformType])';
  463. end
  464. %retain a list of all packet indices -- this time respective the
  465. %neural data packets only
  466. NEV.Data.Spikes.WaveformIndices=find((isempty(Units) | ismember(tempClassOrReason(neuralIndices),Units)) & (isempty(Electrodes) | ismember(PacketIDs(neuralIndices),Electrodes)));
  467. end
  468. end
  469. % save memory
  470. clear nonNeuralIndices;
  471. clear neuralIndices;
  472. %% Calculating the length of the data
  473. fseek(FID, -PacketBytes, 'eof');
  474. DataDuration = fread(FID, 1, 'uint32=>double');
  475. %% Show a report if 'report' is passed as an argument
  476. if strcmp(Report, 'report')
  477. disp( '*** FILE INFO **************************');
  478. disp(['File Path = ' fpath(1:end-1)]);
  479. disp(['File Name = ' fname]);
  480. disp(['Filespec = ' fileSpec]);
  481. disp(['Data Duration (min) = ' num2str(round(DataDuration/NEV.MetaTags.SampleRes/60))]);
  482. disp(['Packet Counts = ' num2str(DataPacketCount)]);
  483. disp(' ');
  484. disp( '*** BASIC HEADER ***********************');
  485. disp(['File Type ID = ' NEV.MetaTags.FileTypeID]);
  486. disp(['Sample Resolution = ' num2str(NEV.MetaTags.SampleRes)]);
  487. disp(['Date and Time = ' NEV.MetaTags.DateTime]);
  488. disp(['Comment = ' NEV.MetaTags.Comment(1:64) ]);
  489. disp([' ' NEV.MetaTags.Comment(65:128) ]);
  490. disp([' ' NEV.MetaTags.Comment(129:192)]);
  491. disp([' ' NEV.MetaTags.Comment(193:256)]);
  492. end
  493. %% Display how fast the function was executed.
  494. if strcmp(Report, 'report')
  495. disp(['The load time was for NEV file was ' num2str(toc, '%0.1f') ' seconds.']);
  496. end
  497. %% Closing and clearing memory
  498. if strcmp(ParseData, 'parse')
  499. if UnparsedDigitalDataFlag && strcmp(WarningStat, 'warning')
  500. fprintf(2, 'WARNING: The NEV file contains unparsed digital data.\n'); % where the file is opened?
  501. pause;
  502. end
  503. end
  504. %% Saving the NEV structure as a MAT file for easy access
  505. if strcmp(SaveFile, 'save')
  506. if exist(matPath, 'file') == 2
  507. disp(['File ' matPath ' already exists.']);
  508. overWrite = input('Would you like to overwrite (Y/N)? ', 's');
  509. if strcmpi(overWrite, 'y')
  510. disp('Saving MAT file. This may take a few seconds...');
  511. save(matPath, 'NEV');
  512. else
  513. disp('File was not overwritten.');
  514. end
  515. else
  516. disp('Saving MAT file. This may take a few seconds...');
  517. save(matPath, 'NEV');
  518. end
  519. end
  520. %% Closing and clearing memory
  521. if ~nargout
  522. assignin('base', 'NEV', NEV);
  523. fclose(FID);
  524. clear variables;
  525. else
  526. fclose(FID);
  527. end
  528. end