openNSx.m 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. function varargout = openNSx(varargin)
  2. % openNSx
  3. %
  4. % Opens and reads an NSx file then returns all file information in a NSx
  5. % structure. Works with File Spec 2.1, 2.2, 2.3, and 3.0.
  6. % Use OUTPUT = openNSx(fname, 'read', 'report', 'e:xx:xx', 'c:xx:xx', 't:xx:xx', 'mode', 'precision', 'skipfactor', 'nozeropad').
  7. %
  8. % All input arguments are optional. Input arguments can be in any order.
  9. %
  10. % fname: Name of the file to be opened. If the fname is omitted
  11. % the user will be prompted to select a file.
  12. % DEFAULT: Will open Open File UI.
  13. %
  14. % 'read': Will read the data in addition to the header information
  15. % if user passes this argument.
  16. % DEFAULT: will read the entire file.
  17. %
  18. % 'report': Will show a summary report if user passes this argument.
  19. % DEFAULT: will not show report.
  20. %
  21. % 'e:XX:YY': User can specify which electrodes need to be read. The
  22. % number of electrodes can be greater than or equal to 1
  23. % and less than or equal to 256. The electrodes can be
  24. % selected either by specifying a range (e.g. 20:45) or by
  25. % indicating individual electrodes (e.g. 3,6,7,90) or both.
  26. % Note that, when individual channels are to be read, all
  27. % channels in between will also be read. The prorgam will
  28. % then remove the unwanted channels. This may result in a
  29. % large memory footprint. If memory issues arrise please
  30. % consider placing openNSx in a for loop and reading
  31. % individual channels.
  32. % This field needs to be preceded by the prefix 'e:'. See
  33. % example for more details. If this option is selected the
  34. % user will be promped for a CMP mapfile (see: KTUEAMapFile)
  35. % provided by Blackrock Microsystems. This feature required
  36. % KTUEAMapFile to be present in path.
  37. % DEFAULT: will read all existing electrodes.
  38. %
  39. % 'c:XX:YY': User can specify which channels need to be read. The
  40. % number of channels can be greater than or equal to 1
  41. % and less than or equal to 272. The channels can be
  42. % selected either by specifying a range (e.g. 20:45) or by
  43. % indicating individual channels (e.g. 3,6,7,90) or both.
  44. % Note that, when individual channels are to be read, all
  45. % channels in between will also be read. The prorgam will
  46. % then remove the unwanted channels. This may result in a
  47. % large memory footprint. If memory issues arrise please
  48. % consider placing openNSx in a for loop and reading
  49. % individual channels.
  50. % This field needs to be preceded by the prefix 'c:'. See
  51. % example for more details.
  52. % DEFAULT: will read all existing analog channels.
  53. %
  54. % 't:XX:YY': User can specify the beginning and end of the data
  55. % segment to be read. If the start time is greater than the
  56. % length of data the program will exit with an errorNS
  57. % message. If the end time is greater than the length of
  58. % data the end packet will be selected for end of data. The
  59. % user can specify the start and end values by comma
  60. % (e.g. [20,50]) or by a colon (e.g. [20:50]). To use this
  61. % argument the user must specify the [electrodes] or the
  62. % interval will be used for [electrodes] automatically.
  63. % This field needs to be preceded by the prefix 't:'.
  64. % Note that if 'mode' is 'sample' the start duration cannot
  65. % be less than 1. The duration is inclusive.
  66. % See example for more details.
  67. % DEFAULT: will read the entire file.
  68. %
  69. % 'mode': The user can specify the mode of duration in [duration],
  70. % such as 'sec', 'min', 'hour', or 'sample'. If 'sec' is
  71. % specified the numbers in [duration] will correspond to
  72. % the number of seconds. The same is true for 'min', 'hour'
  73. % and 'sample'.
  74. % DEFAULT: reads 'sample'.
  75. %
  76. % 'uV': Will read the recording waveforms in unit of uV instead of
  77. % raw values. Note that this conversion may lead to loss of
  78. % information (e.g. 15/4 = 4) since the data type will
  79. % stay in int16. It's recommended to read raw spike
  80. % waveforms and then perform the conversion at a later
  81. % time.
  82. % DEFAULT: will read recording information in raw.
  83. %
  84. % 'precision': This will specify the precision for NSx file. If set to
  85. % 'double' the NSx data will be read as 'double' and if set
  86. % to 'short', the NSx data will be read as 'int16' data
  87. % type. While reading the file as 'short' may have a much
  88. % smaller memory footprint and a faster read time, some
  89. % post data analysis such as multiplying the signal by a
  90. % factor that will make the data larger than (-32,768 to
  91. % 32,767 -- refer to MATLAB documentation for more
  92. % information) may result in unexpected behavior.
  93. % Always use caution when using short. If you are not sure
  94. % of what to use then do not specify this option.
  95. % DEFAULT: will read data in 'int16'.
  96. %
  97. % 'skipfactor': This option will allow the user to read a decimated
  98. % version of the data. The skipfactor will determine how
  99. % many samples to skip. For example, if skipfactor is 2
  100. % then every other sample is read. If skipfactor is 5 then
  101. % every fifth sample is read. This is useful to briefly
  102. % looking at the data in a large datafile when reading the
  103. % entire dataset would overflow the memory.
  104. % DEFAULT: is set to 1, so every sample will be read.
  105. %
  106. % 'ver': If this argument is passed to the function it will return
  107. % the version number of the function without reading any
  108. % data files.
  109. %
  110. % 'nozeropad': It will not zeropad the data to compensate foro the non-
  111. % zero start time.
  112. % DEFAULT: zeropads the loaded data.
  113. %
  114. % OUTPUT: Contains the NSx structure.
  115. %
  116. % Example 1:
  117. % openNSx('report','read','c:\data\sample.ns5', 'e:15:30', 't:3:10','min', 'p:short', 's:5');
  118. %
  119. % or equivalently
  120. % openNSx('report','read','c:\data\sample.ns5', 'electrodes', 15:30, 'duration', 3:10, 'min', 'precision', 'short', 'skipfactor', 5);
  121. %
  122. % In the example above, the file c:\data\sample.ns5 will be used. A
  123. % report of the file contents will be shown. The data will be read from
  124. % electrodes 15 through 50 in the 3-10 minute time interval. A decimated
  125. % version of the datafile will be read, where only every 5th sample is read.
  126. % If any of the arguments above are omitted the default values will be used.
  127. %
  128. % Example 2:
  129. % openNSx('read','c:15:30');
  130. %
  131. % In the example above, the file user will be prompted for the file. The
  132. % file will be read using 'int16' precision as default. All time points
  133. % of Only channels 15 through 30 will be read. If any of the arguments
  134. % above are omitted the default values will be used.
  135. %
  136. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  137. % Kian Torab
  138. % support@blackrockmicro.com
  139. % Blackrock Microsystems
  140. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  141. % Version History
  142. %
  143. % 5.1.8.2:
  144. % - Fixed the way DayOfWeek is read in MetaTags.
  145. %
  146. % 5.1.9.0:
  147. % - Fixed a bug where with skipFactor being read correctly as a num.
  148. %
  149. % 5.1.10.0:
  150. % - Updated feature to save data headers for a paused file. It is a
  151. % dependent feature for seperatePausedNSx.
  152. %
  153. % 5.1.11.0:
  154. % - Fixed an issue where 1 sample would not be read when using the
  155. % t:xx:xx argument and 'sample'.
  156. % - Fixed an error when 'duration' was used to load specific data length.
  157. %
  158. % 5.1.12.0:
  159. % - Better error handling if a file is not provided and an output
  160. % variable was requested by the calling function.
  161. %
  162. % 5.2.0.0: June 12, 2014
  163. % - It removes the extra ElectrodesInfo entried for channels not
  164. % read if 'c:XX:XX' or 'e:XX:XX' are used.
  165. % - It reports variable ChannelCount under MetaTags correctly.
  166. % - It automatically compensate for any NSx file with non-0 beginnings
  167. % and adds 0s for to the begining of the file to properly align the
  168. % timestamps.
  169. %
  170. % 5.2.1.0: June 12, 2014
  171. % - Fixed a small bug where extra 0s were tacked on to the beginning of
  172. % paused file segments.
  173. % - Updated the version.
  174. %
  175. % 5.2.2.0: June 13, 2014
  176. % - Fixed bug for when 'noread' was used on a paused file.
  177. %
  178. % 6.0.1.0: December 2, 2014
  179. % - Fixed a bug related to file format 2.1 not being read correctly.
  180. % - Corrected the way Filename, FileExt, and FilePath was being
  181. % processed.
  182. % - File dialogue now only shows NSx files on non Windows-based
  183. % computers.
  184. % - Added 512 synchronized reading capability.
  185. % - Now on non-Windows computers only NSx files are shown in the file
  186. % dialogue.
  187. % - Fixed the date in NSx.MetaTags.DateTime.
  188. %
  189. % 6.1.0.0: March, 15 2015
  190. % - Added the ability to read from networked drives in Windows.
  191. % - Fixed the DateTime variable in MetaTags.
  192. % - Fixed the date in NSx.MetaTags.DateTime (again).
  193. % - Fixed a bug related to starting and stopping packets when a specific
  194. % time is passed to the function.
  195. % - Fixed a bug where 512+ ch rules were being applied to smaller channel
  196. % count configuration.
  197. %
  198. % 6.1.1.0: June 15, 2015
  199. % - Bug fixes related to timestamps when the recording didn't start at
  200. % proctime 0.
  201. %
  202. % 6.2.0.0: October 1, 2015
  203. % - Fixed a bug related to reading the correct length of time when a skip
  204. % factor was used.
  205. % - Bug fixes related to information that separatePausedNSx depends on.
  206. % - Added 'uV' as an option to read the data in the unit of uV.
  207. %
  208. % 6.2.1.0: April 16, 2016
  209. % - Fixed a bug related to converting the unit to uV in case of having
  210. % multiple data segments (paused file).
  211. %
  212. % 6.2.2.0: July 6, 2016
  213. % - Fixed another bug related to converting the unit to uV.
  214. %
  215. % 6.3.0.0: August 3, 2016
  216. % - Added support for loading a segment of paused files.
  217. %
  218. % 6.3.1.0: August 31, 2016
  219. % - Fixed a bug when reading a non-o start across a paused segment.
  220. %
  221. % 6.4.0.0: December 1, 2016
  222. % - Fixed a serious bug related to loading paused files.
  223. % - Fixed a bug where an empty data segment resulted in a cell structure.
  224. %
  225. % 6.4.1.0: June 15, 2017
  226. % - It is no longer necessary to provide the full path for loading a
  227. % file.
  228. %
  229. % 6.4.2.0: September 1, 2017
  230. % - Fixed a bug related to reading data from sample that is not 1 and
  231. % timestamp that used to get reset to 0.
  232. %
  233. % 6.4.3.0: September 13, 2017
  234. % - Removed a redundant block of code that was accidentally placed in the
  235. % script twice.
  236. % - Checks to see if there's a newer version of NPMK is available.
  237. %
  238. % 6.4.3.1: January 24, 2020
  239. % - Changed file opening access from r+ to r.
  240. %
  241. % 7.0.0.0: January 27, 2020
  242. % - Added support for 64-bit timestamps in NEV and NSx.
  243. %
  244. % 7.1.0.0: April 14, 2020
  245. % - Added option to load the data without zero padding to compensate for
  246. % a non-zero start time. (David Kluger)
  247. % - Bug fixes and documentation updates (David Kluger)
  248. %
  249. % 7.1.1.0: June 11, 2020
  250. % - Fixed a bug related to fread and MATLAB 2020a.
  251. %
  252. % 7.3.0.0: September 11, 2020
  253. % - Fixed a bug related to fread and MATLAB 2020a.
  254. % - Gives a warning about FileSpec 3.0 and gives the user options for how
  255. % to proceed.
  256. % - Added a warning about the data unit and that by default it in the
  257. % unit of 250 nV or 1/4 µV.
  258. % - If the units are in "raw", ths correct information is now written to
  259. % the electrodes header: 250 nV (raw).
  260. %
  261. % 7.3.1.0: October 2, 2020
  262. % - If the units are in µV (openNSx('uv'), ths correct information is now
  263. % written to the electrodes header: 1000 nV (raw).
  264. %
  265. % 7.3.2.0: October 23, 2020
  266. % - Fixed a typo.
  267. %
  268. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  269. %% Defining the NSx data structure and sub-branches.
  270. NSx = struct('MetaTags',[],'Data',[], 'RawData', []);
  271. NSx.MetaTags = struct('FileTypeID',[],'SamplingLabel',[],'ChannelCount',[],'SamplingFreq',[], 'TimeRes', [], ...
  272. 'ChannelID',[],'DateTime',[],'DateTimeRaw',[], 'Comment', [], 'FileSpec', [], ...
  273. 'Timestamp', [], 'DataPoints', [], 'DataDurationSec', [], 'openNSxver', [], 'Filename', [], 'FilePath', [], ...
  274. 'FileExt', []);
  275. NSx.MetaTags.openNSxver = '7.2.0.0';
  276. %% Check for the latest version fo NPMK
  277. NPMKverChecker
  278. % Defining constants
  279. ExtHeaderLength = 66;
  280. elecReading = 0;
  281. NSx.RawData.PausedFile = 0;
  282. syncShift = 0;
  283. %% Validating the input arguments. Exit with error message if error occurs.
  284. next = '';
  285. for i=1:length(varargin)
  286. inputArgument = varargin{i};
  287. if strcmpi(inputArgument, 'ver')
  288. varargout{1} = NSx.MetaTags.openNSxver;
  289. return;
  290. elseif strcmpi(inputArgument, 'channels')
  291. next = 'channels';
  292. elseif strcmpi(inputArgument, 'skipfactor')
  293. next = 'skipfactor';
  294. elseif strcmpi(inputArgument, 'electrodes')
  295. next = 'electrodes';
  296. elseif strcmpi(inputArgument, 'duration')
  297. next = 'duration';
  298. elseif strcmpi(inputArgument, 'precision')
  299. next = 'precision';
  300. elseif strcmpi(inputArgument, 'report')
  301. Report = inputArgument;
  302. elseif strcmpi(inputArgument, 'noread')
  303. ReadData = inputArgument;
  304. elseif strcmpi(inputArgument, 'nomultinsp')
  305. multinsp = 'no';
  306. elseif strcmpi(inputArgument, 'nozeropad')
  307. zeropad = 'no';
  308. elseif strcmpi(inputArgument, 'uV')
  309. waveformUnits = 'uV';
  310. elseif strcmpi(inputArgument, 'read')
  311. ReadData = inputArgument;
  312. elseif (strncmp(inputArgument, 't:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'duration')
  313. if strncmp(inputArgument, 't:', 2)
  314. inputArgument(1:2) = [];
  315. inputArgument = str2num(inputArgument);
  316. end
  317. modifiedTime = 1;
  318. StartPacket = inputArgument(1);
  319. EndPacket = inputArgument(end);
  320. next = '';
  321. elseif (strncmp(inputArgument, 'e:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'electrodes')
  322. if exist('KTUEAMapFile', 'file') == 2
  323. Mapfile = KTUEAMapFile;
  324. Elec = str2num(inputArgument(3:end)); %#ok<ST2NM>
  325. if min(Elec)<1 || max(Elec)>128
  326. disp('The electrode number cannot be less than 1 or greater than 128.');
  327. if nargout; varargout{1} = -1; end
  328. return;
  329. end
  330. for chanIDX = 1:length(Elec)
  331. userRequestedChannels(chanIDX) = Mapfile.Electrode2Channel(Elec(chanIDX));
  332. end
  333. elecReading = 1;
  334. else
  335. disp('To read data by ''electrodes'' the function KTUEAMapFile needs to be in path.');
  336. clear variables;
  337. if nargout; varargout{1} = -1; end
  338. return;
  339. end
  340. next = '';
  341. elseif (strncmp(inputArgument, 's:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'skipFactor')
  342. if strncmp(inputArgument, 's:', 2)
  343. skipFactor = str2num(inputArgument(3:end)); %#ok<ST2NM>
  344. else
  345. if ischar(inputArgument)
  346. skipFactor = str2num(inputArgument);
  347. else
  348. skipFactor = inputArgument;
  349. end
  350. end
  351. next = '';
  352. elseif (strncmp(inputArgument, 'c:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'channels')
  353. if strncmp(inputArgument, 'c:', 2)
  354. userRequestedChanRow = str2num(inputArgument(3:end)); %#ok<ST2NM>
  355. else
  356. userRequestedChanRow = inputArgument;
  357. end
  358. next = '';
  359. elseif (strncmp(varargin{i}, 'p:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'precision')
  360. if strncmp(varargin{i}, 'p:', 2)
  361. precisionTypeRaw = varargin{i}(3:end);
  362. else
  363. precisionTypeRaw = varargin{i};
  364. end
  365. switch precisionTypeRaw
  366. case 'int16'
  367. precisionType = '*int16=>int16';
  368. precisionData = 'int16';
  369. case 'short'
  370. precisionType = '*short=>short';
  371. precisionData = 'int16';
  372. case 'double'
  373. precisionType = '*int16';
  374. precisionData = 'double';
  375. otherwise
  376. disp('Read type is not valid. Refer to ''help'' for more information.');
  377. if nargout; varargout{1} = -1; end
  378. return;
  379. end
  380. clear precisionTypeRaw;
  381. next = '';
  382. elseif strfind(' hour min sec sample ', [' ' inputArgument ' ']) ~= 0
  383. TimeScale = inputArgument;
  384. else
  385. temp = inputArgument;
  386. if length(temp)>3 && ...
  387. (strcmpi(temp(3),'\') || ...
  388. strcmpi(temp(1),'/') || ...
  389. strcmpi(temp(2),'/') || ...
  390. strcmpi(temp(1:2), '\\') || ...
  391. strcmpi(temp(end-3), '.'))
  392. fname = inputArgument;
  393. if exist(fname, 'file') ~= 2
  394. disp('The file does not exist.');
  395. if nargout;
  396. varargout{1} = -1;
  397. end
  398. return;
  399. end
  400. else
  401. disp(['Invalid argument ''' inputArgument ''' .']);
  402. if nargout; varargout{1} = -1; end
  403. return;
  404. end
  405. end
  406. end
  407. clear next;
  408. %% Popup the Open File UI. Also, process the file name, path, and extension
  409. % for later use, and validate the entry.
  410. if ~exist('fname', 'var')
  411. [fname, path] = getFile('*.ns1;*.ns2;*.ns3;*.ns4;*.ns5;*.ns6;*.ns6m', 'Choose an NSx file...');
  412. if fname == 0
  413. disp('No file was selected.');
  414. if nargout; varargout{1} = -1; end
  415. return;
  416. end
  417. [~, ~, fext] = fileparts(fname);
  418. else
  419. if isempty(fileparts(fname))
  420. fname = which(fname);
  421. end
  422. [path,fname, fext] = fileparts(fname);
  423. fname = [fname fext];
  424. path = [path '/'];
  425. end
  426. if fname==0
  427. if nargout; varargout{1} = -1; end
  428. return;
  429. end
  430. %% Loading .x files for multiNSP configuration
  431. if strcmpi(fext(2:4), 'ns6') && length(fext) == 5
  432. path(1) = fname(end);
  433. fname(end) = [];
  434. end
  435. tic;
  436. %% Give all input arguments a default value. All input argumens are
  437. % optional.
  438. if ~exist('Report', 'var'); Report = 'noreport'; end
  439. if ~exist('ReadData', 'var'); ReadData = 'read'; end
  440. if ~exist('StartPacket', 'var'); StartPacket = 1; end
  441. if ~exist('TimeScale', 'var'); TimeScale = 'sample'; end
  442. if ~exist('precisionType', 'var'); precisionType = '*short=>short';...
  443. precisionData='double'; end
  444. if ~exist('skipFactor', 'var'); skipFactor = 1; end
  445. if ~exist('modifiedTime', 'var'); modifiedTime = 0; end
  446. if ~exist('multinsp', 'var'); multinsp = 'yes'; end
  447. if ~exist('waveformUnits', 'var'); waveformUnits = 'raw'; end
  448. if ~exist('zeropad', 'var'); zeropad = 'yes'; end
  449. % Check to see if 512 setup and calculate offset
  450. if strcmpi(multinsp, 'yes')
  451. fiveTwelveFlag = regexp(fname, '-i[0123]-');
  452. if ~isempty(fiveTwelveFlag)
  453. syncShift = multiNSPSync(fullfile(path, fname));
  454. else
  455. multinsp = 'no';
  456. end
  457. end
  458. if strcmpi(ReadData, 'noread')
  459. % disp('NOTE: Reading the header information only. To read the data use with parameter ''read'': openNSx(''read'')');
  460. end
  461. if strcmp(Report, 'report')
  462. disp(['openNSx ' NSx.MetaTags.openNSxver]);
  463. end
  464. %% Reading Basic Header from file into NSx structure.
  465. FID = fopen([path fname], 'r', 'ieee-le');
  466. fileFullPath = fullfile(path, fname);
  467. [NSx.MetaTags.FilePath, NSx.MetaTags.Filename, NSx.MetaTags.FileExt] = fileparts(fileFullPath);
  468. NSx.MetaTags.FileTypeID = fread(FID, [1,8] , 'uint8=>char');
  469. if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALSG')
  470. NSx.MetaTags.FileSpec = '2.1';
  471. NSx.MetaTags.SamplingLabel = fread(FID, [1,16] , 'uint8=>char');
  472. NSx.MetaTags.TimeRes = 30000;
  473. NSx.MetaTags.SamplingFreq = NSx.MetaTags.TimeRes / fread(FID, 1 , 'uint32=>double');
  474. ChannelCount = double(fread(FID, 1 , 'uint32=>double'));
  475. NSx.MetaTags.ChannelCount = ChannelCount;
  476. NSx.MetaTags.ChannelID = fread(FID, [ChannelCount 1], '*uint32');
  477. try
  478. t = dir(fileFullPath);
  479. NSx.MetaTags.DateTime = t.date;
  480. end
  481. elseif or(strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD'), strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP'))
  482. BasicHeader = fread(FID, 306, '*uint8');
  483. NSx.MetaTags.FileSpec = [num2str(double(BasicHeader(1))) '.' num2str(double(BasicHeader(2)))];
  484. HeaderBytes = double(typecast(BasicHeader(3:6), 'uint32'));
  485. NSx.MetaTags.SamplingLabel = char(BasicHeader(7:22))';
  486. NSx.MetaTags.Comment = char(BasicHeader(23:278))';
  487. NSx.MetaTags.TimeRes = double(typecast(BasicHeader(283:286), 'uint32'));
  488. NSx.MetaTags.SamplingFreq = NSx.MetaTags.TimeRes / double(typecast(BasicHeader(279:282), 'uint32'));
  489. t = double(typecast(BasicHeader(287:302), 'uint16'));
  490. ChannelCount = double(typecast(BasicHeader(303:306), 'uint32'));
  491. NSx.MetaTags.ChannelCount = ChannelCount;
  492. readSize = double(ChannelCount * ExtHeaderLength);
  493. ExtendedHeader = fread(FID, readSize, '*uint8');
  494. if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD')
  495. timeStampBytes = 4;
  496. elseif strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP')
  497. timeStampBytes = 8;
  498. end
  499. %% Removing extra garbage characters from the Comment field.
  500. NSx.MetaTags.Comment(find(NSx.MetaTags.Comment==0,1):end) = 0;
  501. %% Populating extended header information
  502. for headerIDX = 1:ChannelCount
  503. offset = double((headerIDX-1)*ExtHeaderLength);
  504. NSx.ElectrodesInfo(headerIDX).Type = char(ExtendedHeader((1:2)+offset))';
  505. if (~strcmpi(NSx.ElectrodesInfo(headerIDX).Type, 'CC'))
  506. disp('extended header not supported');
  507. fclose(FID);
  508. if nargout; varargout{1} = -1; end
  509. return;
  510. end
  511. NSx.ElectrodesInfo(headerIDX).ElectrodeID = typecast(ExtendedHeader((3:4)+offset), 'uint16');
  512. NSx.ElectrodesInfo(headerIDX).Label = char(ExtendedHeader((5:20)+offset))';
  513. NSx.ElectrodesInfo(headerIDX).ConnectorBank = char(ExtendedHeader(21+offset) + ('A' - 1));
  514. NSx.ElectrodesInfo(headerIDX).ConnectorPin = ExtendedHeader(22+offset);
  515. NSx.ElectrodesInfo(headerIDX).MinDigiValue = typecast(ExtendedHeader((23:24)+offset), 'int16');
  516. NSx.ElectrodesInfo(headerIDX).MaxDigiValue = typecast(ExtendedHeader((25:26)+offset), 'int16');
  517. NSx.ElectrodesInfo(headerIDX).MinAnalogValue = typecast(ExtendedHeader((27:28)+offset), 'int16');
  518. NSx.ElectrodesInfo(headerIDX).MaxAnalogValue = typecast(ExtendedHeader((29:30)+offset), 'int16');
  519. if strcmpi(waveformUnits, 'uV')
  520. NSx.ElectrodesInfo(headerIDX).AnalogUnits = '1000 nV (raw) ';
  521. else
  522. conversion = int16(double(NSx.ElectrodesInfo(headerIDX).MaxAnalogValue) / double(NSx.ElectrodesInfo(headerIDX).MaxDigiValue)*1000);
  523. NSx.ElectrodesInfo(headerIDX).AnalogUnits = [num2str(conversion), ' nV (raw) '];
  524. end
  525. NSx.ElectrodesInfo(headerIDX).HighFreqCorner = typecast(ExtendedHeader((47:50)+offset), 'uint32');
  526. NSx.ElectrodesInfo(headerIDX).HighFreqOrder = typecast(ExtendedHeader((51:54)+offset), 'uint32');
  527. NSx.ElectrodesInfo(headerIDX).HighFilterType = typecast(ExtendedHeader((55:56)+offset), 'uint16');
  528. NSx.ElectrodesInfo(headerIDX).LowFreqCorner = typecast(ExtendedHeader((57:60)+offset), 'uint32');
  529. NSx.ElectrodesInfo(headerIDX).LowFreqOrder = typecast(ExtendedHeader((61:64)+offset), 'uint32');
  530. NSx.ElectrodesInfo(headerIDX).LowFilterType = typecast(ExtendedHeader((65:66)+offset), 'uint16');
  531. end
  532. clear ExtendedHeader;
  533. %% Parsing and validating FileSpec and DateTime variables
  534. NSx.MetaTags.DateTimeRaw = t.';
  535. NSx.MetaTags.DateTime = datestr(datenum(t(1), t(2), t(4), t(5), t(6), t(7)));
  536. clear t;
  537. else
  538. disp('This version of openNSx can only read File Specs 2.1, 2.2, 2.3, and 3.0.');
  539. disp(['The selected file spec is ' NSx.MetaTags.FileSpec '.']);
  540. fclose(FID);
  541. clear variables;
  542. if nargout; varargout{1} = -1; end
  543. return;
  544. end
  545. % Determining the length of file and storing the value of fEOF
  546. f.EOexH = double(ftell(FID));
  547. fseek(FID, 0, 'eof');
  548. f.EOF = double(ftell(FID));
  549. % Read Raw Header for saveNSx
  550. fseek(FID, 0, 'bof');
  551. NSx.RawData.Headers = fread(FID, f.EOexH, '*uint8');
  552. % if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD')
  553. NSx.RawData.DataHeader = fread(FID, timeStampBytes+5, '*uint8');
  554. % end
  555. fseek(FID, f.EOexH, 'bof');
  556. %% Reading all data headers and calculating all the file pointers for data
  557. % and headers
  558. if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALSG')
  559. % Determining DataPoints
  560. f.BOData = f.EOexH;
  561. f.EOData = f.EOF;
  562. NSx.MetaTags.DataPoints = (f.EOF-f.EOexH)/(ChannelCount*2);
  563. elseif or(strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD'), strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP'))
  564. segmentCount = 0;
  565. while double(ftell(FID)) < f.EOF
  566. if (fread(FID, 1, 'uint8') ~= 1)
  567. % Fixing another bug in Central 6.01.00.00 TOC where DataPoints is
  568. % not written back into the Data Header
  569. %% BIG NEEDS TO BE FIXED
  570. NSx.MetaTags.DataPoints = double(f.EOF - f.BOData)/(ChannelCount*2);
  571. break;
  572. end
  573. segmentCount = segmentCount + 1;
  574. if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD')
  575. startTimeStamp = fread(FID, 1, 'uint32');
  576. elseif strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP')
  577. startTimeStamp = fread(FID, 1, 'uint64');
  578. end
  579. if strcmpi(multinsp, 'yes')
  580. startTimeStamp = startTimeStamp + syncShift;
  581. fseek(FID, -timeStampBytes, 'cof');
  582. fwrite(FID, startTimeStamp, '*uint32');
  583. end
  584. NSx.MetaTags.Timestamp(segmentCount) = startTimeStamp;
  585. NSx.MetaTags.DataPoints(segmentCount) = fread(FID, 1, 'uint32');
  586. f.BOData(segmentCount) = double(ftell(FID));
  587. fseek(FID, NSx.MetaTags.DataPoints(segmentCount) * ChannelCount * 2, 'cof');
  588. f.EOData(segmentCount) = double(ftell(FID));
  589. % Fixing the bug in 6.01.00.00 TOC where DataPoints is not
  590. % updated and is left as 0
  591. % NSx.MetaTags.DataPoints(segmentCount) = (f.EOData(segmentCount)-f.BOData(segmentCount))/(ChannelCount*2);
  592. end
  593. end
  594. % Determining if the file has a pause in it
  595. if length(NSx.MetaTags.DataPoints) > 1
  596. NSx.RawData.PausedFile = 1;
  597. % if modifiedTime == 1
  598. % disp('This data file contains pauses.');
  599. % disp('openNSx cannot read files with pauses using the ''t:XX'' parameter.');
  600. % fclose(FID); clear variables; if nargout; varargout{1} = -1; end; return;
  601. % end
  602. end
  603. %% Added by NH - Feb 19, 2014
  604. % Create incrementing loop to skip from dataheader to dataheader and
  605. % collect the dataheader data in individual cells
  606. headerCount = 0;
  607. if NSx.RawData.PausedFile == 1
  608. while double(ftell(FID)) < f.EOF
  609. headerCount = headerCount + 1;
  610. fseek(FID, f.EOexH, 'bof');
  611. DataHeader{headerCount} = fread(FID, 9, '*uint8');
  612. DataPoints(headerCount) = typecast(DataHeader{headerCount}(6:9), 'uint32');
  613. f.BOData(headerCount) = double(ftell(FID));
  614. fseek(FID, DataPoints(headerCount) * ChannelCount * 2, 'cof');
  615. f.EOData(headerCount) = double(ftell(FID));
  616. end
  617. % Create an array that will contain all of the dataheader data
  618. % collected in the cells above
  619. FinalDataHeader = [];
  620. %Fill the above mentioned pre-created array
  621. for i = 1:headerCount
  622. FinalDataHeader = cat(1,FinalDataHeader,DataHeader(i));
  623. end
  624. % Convert to correct type for interpreting in separatingPausedNSx
  625. FinalDataHeader = cell2mat(FinalDataHeader);
  626. NSx.RawData.DataHeader = FinalDataHeader;
  627. fseek(FID, f.EOexH, 'bof');
  628. end
  629. %% Copying ChannelID to MetaTags for filespec 2.2, 2.3, and 3.0 for compatibility with filespec 2.1
  630. if or(strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD'), strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP'))
  631. NSx.MetaTags.ChannelID = [NSx.ElectrodesInfo.ElectrodeID]';
  632. end
  633. %% Determining the number of channels to read and validating the input
  634. if ~elecReading
  635. if ~exist('userRequestedChanRow', 'var')
  636. userRequestedChannels = NSx.MetaTags.ChannelID;
  637. else
  638. if any(userRequestedChanRow > ChannelCount)
  639. disp(['Channel file only contains ' num2str(ChannelCount) ' channels.']);
  640. fclose(FID); clear variables; if nargout; varargout{1} = -1; end; return;
  641. else
  642. userRequestedChannels = NSx.MetaTags.ChannelID(userRequestedChanRow);
  643. NSx.MetaTags.ChannelCount = length(userRequestedChannels);
  644. end
  645. end
  646. else
  647. NSx.MetaTags.ChannelCount = length(userRequestedChannels);
  648. end
  649. for idx = 1:length(userRequestedChannels)
  650. if ~any(ismember(NSx.MetaTags.ChannelID, userRequestedChannels(idx)))
  651. disp(['Electrode ' num2str(Mapfile.Channel2Electrode(userRequestedChannels(idx))) ' does not exist in this file.']);
  652. fclose(FID);
  653. clear variables;
  654. if nargout; varargout{1} = -1; end
  655. return;
  656. end
  657. userRequestedChanRow(idx) = find(NSx.MetaTags.ChannelID == userRequestedChannels(idx),1);
  658. end
  659. %% Removing extra ElectrodesInfo for channels not read
  660. if or(strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD'), strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP'))
  661. for headerIDX = length(NSx.ElectrodesInfo):-1:1
  662. if ~ismember(headerIDX, userRequestedChanRow)
  663. NSx.ElectrodesInfo(headerIDX) = [];
  664. end
  665. end
  666. end
  667. %% Adjusts StartPacket and EndPacket based on what time setting (sec, min,
  668. % hour, or packets) the user has indicated in the input argument.
  669. if ~exist('EndPacket', 'var')
  670. EndPacket = sum(NSx.MetaTags.DataPoints);
  671. end
  672. switch TimeScale
  673. case 'sec'
  674. StartPacket = double(StartPacket) * NSx.MetaTags.SamplingFreq + 1;
  675. EndPacket = EndPacket * NSx.MetaTags.SamplingFreq;
  676. case 'min'
  677. StartPacket = StartPacket * NSx.MetaTags.SamplingFreq * 60 + 1;
  678. EndPacket = EndPacket * NSx.MetaTags.SamplingFreq * 60;
  679. case 'hour'
  680. StartPacket = StartPacket * NSx.MetaTags.SamplingFreq * 3600 + 1;
  681. EndPacket = EndPacket * NSx.MetaTags.SamplingFreq * 3600;
  682. end
  683. %% Validate StartPacket and EndPacket to make sure they do not exceed the
  684. % length of packets in the file. If EndPacket is over then the last packet
  685. % will be set for EndPacket. If StartPacket is over then will exist with an
  686. % error message.
  687. if StartPacket >= EndPacket
  688. disp('The starting packet is greater than the end packet.');
  689. disp('The file was not read.');
  690. fclose(FID);
  691. if nargout; varargout{1} = -1; end
  692. return;
  693. end
  694. if StartPacket <= 0
  695. disp('The starting packet must be greater or equal to 1.');
  696. disp('The starting packet was changed to 1.');
  697. StartPacket = 1;
  698. end
  699. if EndPacket > sum(NSx.MetaTags.DataPoints)
  700. if StartPacket >= NSx.MetaTags.DataPoints
  701. disp('The starting packet is greater than the total data duration.');
  702. disp('The file was not read.');
  703. fclose(FID);
  704. if nargout; varargout{1} = -1; end
  705. return;
  706. end
  707. disp('The time interval specified is longer than the data duration.');
  708. disp('Last data point will be used instead.');
  709. disp('Press enter to continue...');
  710. pause;
  711. EndPacket = sum(NSx.MetaTags.DataPoints) - 1;
  712. end
  713. % Adjusting the endPacket for the skipFactor to reduce the length of
  714. % the data read.
  715. % DEBUG: This is not needed since the same length of data is to be
  716. % read.
  717. EndPacket = EndPacket / skipFactor;
  718. % Finding which data segment the StartPacket is falling in-between
  719. segmentCounters = [];
  720. startTimeStampShift = 0;
  721. if NSx.RawData.PausedFile
  722. dataPointOfInterest = StartPacket;
  723. for idx = 1:length(NSx.MetaTags.DataPoints)
  724. if dataPointOfInterest <= sum(NSx.MetaTags.DataPoints(1:idx)) %sum(NSx.MetaTags.DataPoints(1:idx)) < dataPointOfInterest && ...
  725. if isempty(segmentCounters)
  726. if idx == 1
  727. segmentStartPacket(idx) = dataPointOfInterest;
  728. else
  729. segmentStartPacket(idx) = dataPointOfInterest - (sum(NSx.MetaTags.DataPoints(1:idx-1)));
  730. end
  731. startTimeStampShift = segmentStartPacket(idx) - 1;
  732. if EndPacket <= sum(NSx.MetaTags.DataPoints(1:idx))
  733. segmentDataPoints(idx) = EndPacket - sum(NSx.MetaTags.DataPoints(1:idx-1)) - segmentStartPacket(idx) + 1;
  734. segmentCounters = [idx, idx];
  735. break;
  736. end
  737. segmentDataPoints(idx) = sum(NSx.MetaTags.DataPoints(1:idx)) - dataPointOfInterest + 1;
  738. dataPointOfInterest = EndPacket;
  739. else
  740. segmentStartPacket(idx) = 1;
  741. if idx == 1
  742. segmentDataPoints(idx) = dataPointOfInterest;
  743. else
  744. segmentDataPoints(idx) = dataPointOfInterest - sum(NSx.MetaTags.DataPoints(1:idx-1));
  745. segmentCounters(length(segmentCounters)+1) = idx;
  746. end
  747. break;
  748. end
  749. segmentCounters(length(segmentCounters)+1) = idx;
  750. else
  751. if isempty(segmentCounters)
  752. segmentStartPacket(idx) = NSx.MetaTags.DataPoints(idx);
  753. segmentDataPoints(idx) = 0;
  754. elseif length(segmentCounters) == 1
  755. segmentStartPacket(idx) = 1;
  756. segmentDataPoints(idx) = NSx.MetaTags.DataPoints(idx);
  757. else
  758. segmentStartPacket(idx) = 1;
  759. segmentDataPoints(idx) = 0;
  760. end
  761. end
  762. end
  763. end
  764. DataLength = EndPacket - StartPacket + 1;
  765. % from now StartPacket and EndPacket are in terms of Samples and are zero-based
  766. clear TimeScale
  767. %% Reading the data if flag 'read' is used
  768. if strcmp(ReadData, 'read')
  769. % Determine what channels to read
  770. numChansToRead = double(length(min(userRequestedChanRow):max(userRequestedChanRow)));
  771. if NSx.RawData.PausedFile
  772. for dataIDX = segmentCounters(1):segmentCounters(2) %1:length(NSx.MetaTags.DataPoints)
  773. fseek(FID, f.BOData(dataIDX), 'bof');
  774. % Skip the file to the beginning of the time requsted, if not 0
  775. fseek(FID, (segmentStartPacket(dataIDX) - 1) * 2 * ChannelCount, 'cof');
  776. % Skip the file to the first channel to read
  777. fseek(FID, (find(NSx.MetaTags.ChannelID == min(userRequestedChannels))-1) * 2, 'cof');
  778. % Read data
  779. NSx.Data{size(NSx.Data,2)+1} = fread(FID, [numChansToRead segmentDataPoints(dataIDX)], [num2str(numChansToRead) precisionType], double((ChannelCount-numChansToRead)*2 + ChannelCount*(skipFactor-1)*2));
  780. end
  781. NSx.MetaTags.DataPoints = segmentDataPoints(segmentCounters(1):segmentCounters(2));
  782. NSx.MetaTags.Timestamp = NSx.MetaTags.Timestamp(segmentCounters(1):segmentCounters(2));
  783. NSx.MetaTags.Timestamp(1) = NSx.MetaTags.Timestamp(1) + startTimeStampShift;
  784. else
  785. fseek(FID, f.BOData(1), 'bof');
  786. % Skip the file to the beginning of the time requsted, if not 0
  787. fseek(FID, (StartPacket - 1) * 2 * ChannelCount, 'cof');
  788. % Skip the file to the first channel to read
  789. fseek(FID, (find(NSx.MetaTags.ChannelID == min(userRequestedChannels))-1) * 2, 'cof');
  790. % Read data
  791. NSx.Data = fread(FID, [numChansToRead DataLength], [num2str(numChansToRead) precisionType], double((ChannelCount-numChansToRead)*2 + ChannelCount*(skipFactor-1)*2));
  792. end
  793. end
  794. %% Fixing a bug in 6.03 TOC where an extra 0-length packet is introduced
  795. if NSx.RawData.PausedFile && strcmp(ReadData, 'read')
  796. if isempty(NSx.Data{1})
  797. NSx.Data = cell2mat(NSx.Data(2));
  798. end
  799. end
  800. % Fixing a bug in 6.03 where data packets with 0 lengh may be added
  801. if any(NSx.MetaTags.DataPoints == 0) && strcmp(ReadData, 'read')
  802. segmentsThatAreZero = find(NSx.MetaTags.DataPoints == 0);
  803. NSx.MetaTags.DataPoints(segmentsThatAreZero) = [];
  804. NSx.MetaTags.Timestamp(segmentsThatAreZero) = [];
  805. NSx.Data(segmentsThatAreZero) = [];
  806. end
  807. %% Removing extra channels that were read, but weren't supposed to be read
  808. channelThatWereRead = min(userRequestedChanRow):max(userRequestedChanRow);
  809. if ~isempty(setdiff(channelThatWereRead,userRequestedChanRow))
  810. deleteChannels = setdiff(channelThatWereRead, userRequestedChanRow) - min(userRequestedChanRow) + 1;
  811. if NSx.RawData.PausedFile
  812. for segIDX = 1:size(NSx.Data,2)
  813. NSx.Data{segIDX}(deleteChannels,:) = [];
  814. end
  815. else
  816. NSx.Data(deleteChannels,:) = [];
  817. end
  818. end
  819. %% Remove the cell if there is only one recorded segment present
  820. if all(size(NSx.Data) == [1,1])
  821. NSx.Data = NSx.Data{1};
  822. end
  823. %% Adjusting the ChannelID variable to match the read electrodes
  824. channelIDToDelete = setdiff(1:ChannelCount, userRequestedChanRow);
  825. NSx.MetaTags.ChannelID(channelIDToDelete) = [];
  826. %% Adjusting the file for a non-0 timestamp start
  827. if strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP') && strcmpi(zeropad, 'yes')
  828. NPMKSettings = settingsManager;
  829. if NSx.MetaTags.Timestamp(1) > 30000 && NPMKSettings.ShowZeroPadWarning == 1
  830. disp(' ');
  831. disp('You have chosen to zeropad the NSx file that contains a large timestamp gap.');
  832. disp('For more information please refer to our <a href = "https://support.blackrockmicro.com/portal/en/kb/articles/nozeropad-in-opennsx">knowledge base article</a> on this subject.');
  833. disp('https://support.blackrockmicro.com/portal/en/kb/articles/nozeropad-in-opennsx');
  834. response = input('This could take a while. Do you wish to continue? ', 's');
  835. if strcmpi(response, 'n')
  836. return;
  837. end
  838. response = input('Do you want NPMK to continue to ask you about this every time? ', 's');
  839. if strcmpi(response, 'n')
  840. NPMKSettings.ShowZeroPadWarning = 0;
  841. settingsManager(NPMKSettings);
  842. end
  843. end
  844. end
  845. if ~NSx.RawData.PausedFile && StartPacket == 1 && strcmpi(zeropad, 'yes')
  846. if length(NSx.MetaTags.Timestamp) > 1
  847. cellIDX = 1; % only do this for the first cell segment and not modify the subsequent segments
  848. if strcmpi(ReadData, 'read')
  849. NSx.Data{cellIDX} = [zeros(NSx.MetaTags.ChannelCount, floor(NSx.MetaTags.Timestamp(cellIDX) / skipFactor), precisionData) NSx.Data{cellIDX}];
  850. end
  851. NSx.MetaTags.DataPoints(cellIDX) = NSx.MetaTags.DataPoints(cellIDX) + NSx.MetaTags.Timestamp(cellIDX);
  852. NSx.MetaTags.DataDurationSec(cellIDX) = NSx.MetaTags.DataPoints(cellIDX) / NSx.MetaTags.SamplingFreq;
  853. NSx.MetaTags.Timestamp(cellIDX) = 0;
  854. elseif strcmpi(ReadData, 'read')
  855. NSx.Data = [zeros(NSx.MetaTags.ChannelCount, floor(NSx.MetaTags.Timestamp / skipFactor), precisionData) NSx.Data];
  856. NSx.MetaTags.DataPoints = size(NSx.Data,2);
  857. NSx.MetaTags.DataDurationSec = NSx.MetaTags.DataPoints / NSx.MetaTags.SamplingFreq;
  858. NSx.MetaTags.Timestamp = 0;
  859. end
  860. if strcmpi(multinsp, 'yes')
  861. NSx.Data = [zeros(NSx.MetaTags.ChannelCount, syncShift, precisionData) NSx.Data];
  862. NSx.MetaTags.DataPoints = size(NSx.Data,2);
  863. NSx.MetaTags.DataDurationSec = NSx.MetaTags.DataPoints / NSx.MetaTags.SamplingFreq;
  864. end
  865. end
  866. %% Adjusting for the data's unit.
  867. if strcmpi(waveformUnits, 'uV')
  868. if iscell(NSx.Data) % Contribution by Michele Cox @ Vanderbilt
  869. NSx.Data = cellfun(@(x) bsxfun(@rdivide, double(x), 1./(double([NSx.ElectrodesInfo.MaxAnalogValue])./double([NSx.ElectrodesInfo.MaxDigiValue]))'),NSx.Data ,'UniformOutput',false);
  870. else
  871. NSx.Data = bsxfun(@rdivide, double(NSx.Data), 1./(double([NSx.ElectrodesInfo.MaxAnalogValue])./double([NSx.ElectrodesInfo.MaxDigiValue]))');
  872. end % End of contribution
  873. else
  874. NPMKSettings = settingsManager;
  875. if NPMKSettings.ShowuVWarning == 1
  876. disp(' ');
  877. disp('The data is in unit of 1/4 µV. This means that 100 in the NSx file equals to 25 µV. All values must be divided by 4.');
  878. disp('To read the data in unit of µV, use openNSx(''uv''). For more information type: help openNSx');
  879. response = input('Do you want NPMK to continue to ask you about this every time? ', 's');
  880. if strcmpi(response, 'n')
  881. NPMKSettings.ShowuVWarning = 0;
  882. settingsManager(NPMKSettings);
  883. end
  884. end
  885. end
  886. %% Converting the data points in sample to seconds
  887. NSx.MetaTags.DataPointsSec = double(NSx.MetaTags.DataPoints)/NSx.MetaTags.SamplingFreq;
  888. %% Calculating the DataPoints in seconds and adding it to MetaData
  889. NSx.MetaTags.DataDurationSec = double(NSx.MetaTags.DataPoints)/NSx.MetaTags.SamplingFreq;
  890. %% Displaying a report of basic file information and the Basic Header.
  891. if strcmp(Report, 'report')
  892. disp( '*** FILE INFO **************************');
  893. disp(['File Path = ' NSx.MetaTags.FilePath]);
  894. disp(['File Name = ' NSx.MetaTags.Filename]);
  895. disp(['File Extension = ' NSx.MetaTags.FileExt]);
  896. disp(['File Version = ' NSx.MetaTags.FileSpec]);
  897. disp(['Duration (seconds) = ' num2str(NSx.MetaTags.DataDurationSec)]);
  898. disp(['Total Data Points = ' num2str(NSx.MetaTags.DataPoints)]);
  899. disp(' ');
  900. disp( '*** BASIC HEADER ***********************');
  901. disp(['File Type ID = ' NSx.MetaTags.FileTypeID]);
  902. disp(['Sample Frequency = ' num2str(double(NSx.MetaTags.SamplingFreq))]);
  903. disp(['Electrodes Read = ' num2str(double(NSx.MetaTags.ChannelCount))]);
  904. disp(['Data Point Read = ' num2str(size(NSx.Data,2))]);
  905. end
  906. %% If user does not specify an output argument it will automatically create
  907. % a structure.
  908. outputName = ['NS' fext(4)];
  909. if (nargout == 0)
  910. assignin('caller', outputName, NSx);
  911. else
  912. varargout{1} = NSx;
  913. end
  914. if strcmp(Report, 'report')
  915. disp(['The load time for ' outputName ' file was ' num2str(toc, '%0.1f') ' seconds.']);
  916. end
  917. fclose(FID);
  918. end