openNSx.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. function NSx = openNSx(varargin)
  2. %%Opens an NSx file for reading, returns all file information in a NSx
  3. % structure. Works with File Spec 2.1 and 2.2.
  4. % Use OUTPUT = openNSx(fname, 'read', 'report', 'electrodes', 'duration', 'mode', 'precision').
  5. %
  6. % All input arguments are optional. Input arguments can be in any order.
  7. %
  8. % fname: Name of the file to be opened. If the fname is omitted
  9. % the user will be prompted to select a file.
  10. % DEFAULT: Will open Open File UI.
  11. %
  12. % 'read': Will read the data in addition to the header information
  13. % if user passes this argument.
  14. % DEFAULT: will only read the header information.
  15. %
  16. % 'report': Will show a summary report if user passes this argument.
  17. % DEFAULT: will not show report.
  18. %
  19. % 'electrodes': User can specify which electrodes need to be read. The
  20. % number of electrodes can be greater than or equal to 1
  21. % and less than or equal to 128. The electrodes can be
  22. % selected either by specifying a range (e.g. 20:45) or by
  23. % indicating individual electrodes (e.g. 3,6,7,90) or both.
  24. % This field needs to be followed by the prefix 'e:'. See
  25. % example for more details.
  26. % DEFAULT: will read all existing channels.
  27. %
  28. % 'duration': User can specify the beginning and end of the data
  29. % segment to be read. If the start time is greater than the
  30. % length of data the program will exit with an error
  31. % message. If the end time is greater than the length of
  32. % data the end packet will be selected for end of data. The
  33. % user can specify the start and end values by comma
  34. % (e.g. [20,50]) or by a colon (e.g. [20:50]). To use this
  35. % argument the user must specify the [electrodes] or the
  36. % interval will be used for [electrodes] automatically.
  37. % This field needs to be followed by the prefix 't:'. See
  38. % example for more details.
  39. % DEFAULT: will read the entire file.
  40. %
  41. % 'mode': The user can specify the mode of duration in [duration],
  42. % such as 'sec', 'min', 'hour', or 'sample'. If 'sec' is
  43. % specified the numbers in [duration] will correspond to
  44. % the number of seconds. The same is true for 'min', 'hour'
  45. % and 'sample'.
  46. % DEFAULT: will be set to 'sample'.
  47. %
  48. % 'precision': The data storage class can be any format known by
  49. % MATLAB such as 'double', 'int16', 'int'.
  50. % This field needs to be followed by the prefix 'p:'. See
  51. % example for more details.
  52. % DEFAULT: will be set to 'double'
  53. %
  54. % OUTPUT: Contains the NSx structure.
  55. %
  56. % Example:
  57. %
  58. % openNSx('report','read','c:\data\sample.ns5', 'e:15:30', 't:3:10', 'min', 'p:int16');
  59. %
  60. % In the example above, the file c:\data\sample.ns5 will be used. A
  61. % report of the file contents will be shown. The data will be read from
  62. % electrodes 15 through 50 in the 3-10 minute time interval. The data
  63. % is saved in 'int16' type.
  64. % If any of the arguments above are omitted the default values will be used.
  65. %
  66. % Kian Torab
  67. % kian.torab@utah.edu
  68. % Department of Bioengineering
  69. % University of Utah
  70. % Version 2.1.0 - March 22, 2010
  71. NSxver = '2.1.0';
  72. disp(['openNSx version ' NSxver])
  73. %% Defining the NSx data structure and sub-branches.
  74. NSx = struct('MetaTags',[],'Data',[]);
  75. NSx.MetaTags = struct('FileTypeID',[],'ChannelCount',[],'SamplingFreq',[],'ChannelID',[],'Version',[],'ElecLabel',[],'CreateDateTime',[]);
  76. %% Validating the input arguments. Exit with error message if error occurs.
  77. for i=1:length(varargin)
  78. inputArgument = varargin{i};
  79. if strcmpi(inputArgument, 'report')
  80. Report = inputArgument;
  81. elseif strcmpi(inputArgument, 'read')
  82. ReadData = inputArgument;
  83. elseif strncmp(varargin{i}, 't:', 2)
  84. colonIndex = find(inputArgument(3:end) == ':');
  85. StartPacket = str2num(inputArgument(3:colonIndex+1));
  86. EndPacket = str2num(inputArgument(colonIndex+3:end));
  87. if min(varargin{i})<1 || max(varargin{i})>128
  88. display('The electrode number cannot be less than 1 or greater than 128.');
  89. clear variables;
  90. if nargout; NSx = []; end
  91. return;
  92. end
  93. elseif strncmp(varargin{i}, 'e:', 2)
  94. Elec = str2num(inputArgument(3:end)); %#ok<ST2NM>
  95. elseif strncmp(varargin{i}, 'p:', 2)
  96. Precision = inputArgument(3:end); % precision for storage
  97. elseif strfind(' hour min sec sample ', [' ' inputArgument ' ']) ~= 0
  98. TimeScale = inputArgument;
  99. else
  100. temp = inputArgument;
  101. if length(temp)>3 && strcmpi(temp(end-3),'.')
  102. fname = inputArgument;
  103. if exist(fname, 'file') ~= 2
  104. display('The file does not exist.');
  105. clear variables;
  106. if nargout; NSx = []; end
  107. return;
  108. end
  109. else
  110. display(['Invalid argument ''' inputArgument ''' .']);
  111. clear variables;
  112. if nargout; NSx = []; end
  113. return;
  114. end
  115. end
  116. end
  117. %% Popup the Open File UI. Also, process the file name, path, and extension
  118. % for later use, and validate the entry.
  119. if ~exist('fname', 'var')
  120. [fname, path] = uigetfile('D:\Data\*.ns*');
  121. if fname == 0
  122. clear variables;
  123. if nargout; NSx = []; end
  124. return;
  125. end
  126. fext = fname(end-3:end);
  127. else
  128. [path, fname, fext] = fileparts(fname);
  129. fname = [fname fext];
  130. end
  131. if fname==0; return; end;
  132. tic;
  133. %% Give all input arguments a default value. All input argumens are
  134. % optional.
  135. if ~exist('Report', 'var'); Report = 'noreport'; end
  136. if ~exist('ReadData', 'var'); ReadData = 'noread'; end
  137. if ~exist('StartPacket', 'var'); StartPacket = 0; end
  138. if ~exist('TimeScale', 'var'); TimeScale = 1; end
  139. if ~exist('Precision', 'var'); Precision = 'double'; end
  140. %% Reading Basic Header from file into NSx structure.
  141. % we use fullfile instead of [path '\' fname] to support nix platforms
  142. FID = fopen(fullfile(path,fname), 'r', 'ieee-le');
  143. NSx.MetaTags.FileTypeID = fread(FID, [1,8] , 'uint8=>char');
  144. % Validate the data file's File Spec.
  145. if strcmp(NSx.MetaTags.FileTypeID, 'NEURALSG') % 2.1
  146. NSx.MetaTags.Version = '2.1';
  147. NSx.MetaTags.Label = fread(FID, [1,16] , 'uint8=>char');
  148. NSx.MetaTags.SamplingFreq = 30000/fread(FID, 1 , 'uint32=>double');
  149. ChannelCount = fread(FID, 1 , 'uint32=>double');
  150. NSx.MetaTags.ChannelID = fread(FID, [ChannelCount 1], '*uint32');
  151. NSx.MetaTags.ChannelCount = ChannelCount;
  152. fHeader = ftell(FID);
  153. elseif strcmp(NSx.MetaTags.FileTypeID, 'NEURALCD') % 2.2, 2.3
  154. Major = num2str(fread(FID, 1 , 'uint8=>double'));
  155. Minor = num2str(fread(FID, 1 , 'uint8=>double'));
  156. NSx.MetaTags.Version = [Major '.' Minor];
  157. fHeader = fread(FID, 1 , 'uint32=>double');
  158. NSx.MetaTags.Label = fread(FID, [1,16] , 'uint8=>char');
  159. NSx.MetaTags.Comments = fread(FID, [1,256] , 'uint8=>char');
  160. NSx.MetaTags.SamplingFreq = 30000/fread(FID, 1 , 'uint32=>double' );
  161. NSx.MetaTags.Resolution = fread(FID, 1 , 'uint32=>double' );
  162. NSx.MetaTags.CreateDateTime = fread(FID, [1,8] , 'uint16=>double' );
  163. ChannelCount = fread(FID, 1 , 'uint32=>double' );
  164. NSx.MetaTags.ChannelCount = ChannelCount;
  165. NSx.MetaTags.ChannelID = zeros(ChannelCount, 1);
  166. NSx.MetaTags.ElecLabel = char(zeros(ChannelCount, 16)); % Electrode label
  167. % now read external header
  168. for ii = 1:ChannelCount
  169. CC = fread(FID, [1,2] , 'uint8=>char');
  170. if ~strcmp(CC, 'CC')
  171. display('Wrong extension header');
  172. fclose(FID);
  173. clear variables;
  174. return;
  175. end
  176. NSx.MetaTags.ChannelID(ii) = fread(FID, 1 , 'uint16=>double');
  177. NSx.MetaTags.ElecLabel(ii,:) = fread(FID, [1,16] , 'uint8=>char');
  178. % We do not care about the rest now
  179. dummy = num2str(fread(FID, [46,1] , 'uint8=>double')); % dummy
  180. end
  181. clear dummy;
  182. if fHeader ~= ftell(FID)
  183. display('Header file corrupted!');
  184. fHeader = ftell(FID);
  185. end
  186. fHeader = fHeader + 9; % to account for the data header
  187. else
  188. display('This version of openNSx can only read File Specs 2.1 or 2.2');
  189. display(['The selected file label is ' NSx.MetaTags.FileTypeID '.']);
  190. fclose(FID);
  191. clear variables;
  192. if nargout; NSx = []; end;
  193. return;
  194. end;
  195. % find out number of data points
  196. fseek(FID, 0, 'eof');
  197. fData = ftell(FID);
  198. fseek(FID, fHeader, 'bof');
  199. %% Adjusts StartPacket and EndPacket based on what time setting (sec, min,
  200. % hour, or packets) the user has indicated in the input argument.
  201. switch TimeScale
  202. case 'sec'
  203. StartPacket = StartPacket * NSx.MetaTags.SamplingFreq;
  204. EndPacket = EndPacket * NSx.MetaTags.SamplingFreq;
  205. case 'min'
  206. StartPacket = StartPacket * NSx.MetaTags.SamplingFreq * 60;
  207. EndPacket = EndPacket * NSx.MetaTags.SamplingFreq * 60;
  208. case 'hour'
  209. StartPacket = StartPacket * NSx.MetaTags.SamplingFreq * 3600;
  210. EndPacket = EndPacket * NSx.MetaTags.SamplingFreq * 3600;
  211. case 'sample'
  212. StartPacket = StartPacket - 1;
  213. EndPacket = EndPacket - 1;
  214. end
  215. %% Validate StartPacket and EndPacket to make sure they do not exceed the
  216. % length of packets in the file. If EndPacket is over then the last packet
  217. % will be set for EndPacket. If StartPacket is over then will exist with an
  218. % error message.
  219. NumofPackets = (fData-fHeader)/(2*ChannelCount);
  220. if exist('EndPacket', 'var') && (EndPacket > NumofPackets)
  221. display('The time interval specified is longer than the data duration.');
  222. if StartPacket > NumofPackets
  223. disp('The starting packet is greater than the total data duration.');
  224. clear variables;
  225. if nargout; NSx = []; end
  226. return;
  227. end
  228. disp('The time interval specified is longer than the data duration.');
  229. disp('Last data point will be used instead.');
  230. disp('Press enter to continue...');
  231. pause;
  232. EndPacket = NumofPackets;
  233. elseif ~exist('EndPacket', 'var')
  234. EndPacket = NumofPackets;
  235. end
  236. DataLength = EndPacket - StartPacket;
  237. clear TimeScale
  238. %% Displaying a report of basic file information and the Basic Header.
  239. if strcmp(Report, 'report')
  240. disp( '*** FILE INFO **************************');
  241. disp(['File Path = ' path]);
  242. disp(['File Name = ' fname ]);
  243. disp(['File Version = ' NSx.MetaTags.Version ]);
  244. disp(['Duration (seconds) = ' num2str(NumofPackets/NSx.MetaTags.SamplingFreq)]);
  245. disp(['Total Data Points = ' num2str(NumofPackets) ]);
  246. disp(' ');
  247. disp( '*** BASIC HEADER ***********************');
  248. disp(['File Type ID = ' NSx.MetaTags.FileTypeID ]);
  249. disp(['Label = ' NSx.MetaTags.Label ]);
  250. disp(['Sample Resolution = ' num2str(NSx.MetaTags.SamplingFreq) ]);
  251. disp(['Electrodes Read = ' num2str(NSx.MetaTags.ChannelCount) ]);
  252. end
  253. %%
  254. if ~exist('Elec', 'var');
  255. Elec = 1:ChannelCount;
  256. end
  257. Elec=Elec(Elec>=1 & Elec<=ChannelCount);
  258. ReadElec = max(Elec)-min(Elec)+1;
  259. if (ReadElec <= ChannelCount)
  260. if strcmp(ReadData, 'read')
  261. fseek(FID, StartPacket * 2 * ChannelCount + fHeader, 'bof');
  262. fseek(FID, (min(Elec)-1) * 2, 'cof');
  263. NSx.Data = fread(FID, [ReadElec DataLength-1], [num2str(ReadElec) '*int16=>' Precision], (ChannelCount-ReadElec) * 2);
  264. end
  265. end
  266. %% If user does not specify an output argument it will automatically create
  267. % a structure.
  268. outputName = ['NS' fext(4)];
  269. if (nargout == 0),
  270. assignin('caller', outputName, NSx);
  271. end
  272. if strcmp(Report, 'report')
  273. display(['The load time for ' outputName ' file was ' num2str(toc, '%0.1f') ' seconds.']);
  274. end
  275. fclose(FID);
  276. end