123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978 |
- function varargout = openNSx(varargin)
- % openNSx
- %
- % Opens and reads an NSx file then returns all file information in a NSx
- % structure. Works with File Spec 2.1, 2.2, 2.3, and 3.0.
- % Use OUTPUT = openNSx(fname, 'read', 'report', 'e:xx:xx', 'c:xx:xx', 't:xx:xx', 'mode', 'precision', 'skipfactor', 'nozeropad').
- %
- % All input arguments are optional. Input arguments can be in any order.
- %
- % fname: Name of the file to be opened. If the fname is omitted
- % the user will be prompted to select a file.
- % DEFAULT: Will open Open File UI.
- %
- % 'read': Will read the data in addition to the header information
- % if user passes this argument.
- % DEFAULT: will read the entire file.
- %
- % 'report': Will show a summary report if user passes this argument.
- % DEFAULT: will not show report.
- %
- % 'e:XX:YY': User can specify which electrodes need to be read. The
- % number of electrodes can be greater than or equal to 1
- % and less than or equal to 256. The electrodes can be
- % selected either by specifying a range (e.g. 20:45) or by
- % indicating individual electrodes (e.g. 3,6,7,90) or both.
- % Note that, when individual channels are to be read, all
- % channels in between will also be read. The prorgam will
- % then remove the unwanted channels. This may result in a
- % large memory footprint. If memory issues arrise please
- % consider placing openNSx in a for loop and reading
- % individual channels.
- % This field needs to be preceded by the prefix 'e:'. See
- % example for more details. If this option is selected the
- % user will be promped for a CMP mapfile (see: KTUEAMapFile)
- % provided by Blackrock Microsystems. This feature required
- % KTUEAMapFile to be present in path.
- % DEFAULT: will read all existing electrodes.
- %
- % 'c:XX:YY': User can specify which channels need to be read. The
- % number of channels can be greater than or equal to 1
- % and less than or equal to 272. The channels can be
- % selected either by specifying a range (e.g. 20:45) or by
- % indicating individual channels (e.g. 3,6,7,90) or both.
- % Note that, when individual channels are to be read, all
- % channels in between will also be read. The prorgam will
- % then remove the unwanted channels. This may result in a
- % large memory footprint. If memory issues arrise please
- % consider placing openNSx in a for loop and reading
- % individual channels.
- % This field needs to be preceded by the prefix 'c:'. See
- % example for more details.
- % DEFAULT: will read all existing analog channels.
- %
- % 't:XX:YY': User can specify the beginning and end of the data
- % segment to be read. If the start time is greater than the
- % length of data the program will exit with an errorNS
- % message. If the end time is greater than the length of
- % data the end packet will be selected for end of data. The
- % user can specify the start and end values by comma
- % (e.g. [20,50]) or by a colon (e.g. [20:50]). To use this
- % argument the user must specify the [electrodes] or the
- % interval will be used for [electrodes] automatically.
- % This field needs to be preceded by the prefix 't:'.
- % Note that if 'mode' is 'sample' the start duration cannot
- % be less than 1. The duration is inclusive.
- % See example for more details.
- % DEFAULT: will read the entire file.
- %
- % 'mode': The user can specify the mode of duration in [duration],
- % such as 'sec', 'min', 'hour', or 'sample'. If 'sec' is
- % specified the numbers in [duration] will correspond to
- % the number of seconds. The same is true for 'min', 'hour'
- % and 'sample'.
- % DEFAULT: reads 'sample'.
- %
- % 'uV': Will read the recording waveforms in unit of uV instead of
- % raw values. Note that this conversion may lead to loss of
- % information (e.g. 15/4 = 4) since the data type will
- % stay in int16. It's recommended to read raw spike
- % waveforms and then perform the conversion at a later
- % time.
- % DEFAULT: will read recording information in raw.
- %
- % 'precision': This will specify the precision for NSx file. If set to
- % 'double' the NSx data will be read as 'double' and if set
- % to 'short', the NSx data will be read as 'int16' data
- % type. While reading the file as 'short' may have a much
- % smaller memory footprint and a faster read time, some
- % post data analysis such as multiplying the signal by a
- % factor that will make the data larger than (-32,768 to
- % 32,767 -- refer to MATLAB documentation for more
- % information) may result in unexpected behavior.
- % Always use caution when using short. If you are not sure
- % of what to use then do not specify this option.
- % DEFAULT: will read data in 'int16'.
- %
- % 'skipfactor': This option will allow the user to read a decimated
- % version of the data. The skipfactor will determine how
- % many samples to skip. For example, if skipfactor is 2
- % then every other sample is read. If skipfactor is 5 then
- % every fifth sample is read. This is useful to briefly
- % looking at the data in a large datafile when reading the
- % entire dataset would overflow the memory.
- % DEFAULT: is set to 1, so every sample will be read.
- %
- % 'ver': If this argument is passed to the function it will return
- % the version number of the function without reading any
- % data files.
- %
- % 'nozeropad': It will not zeropad the data to compensate foro the non-
- % zero start time.
- % DEFAULT: zeropads the loaded data.
- %
- % OUTPUT: Contains the NSx structure.
- %
- % Example 1:
- % openNSx('report','read','c:\data\sample.ns5', 'e:15:30', 't:3:10','min', 'p:short', 's:5');
- %
- % or equivalently
- % openNSx('report','read','c:\data\sample.ns5', 'electrodes', 15:30, 'duration', 3:10, 'min', 'precision', 'short', 'skipfactor', 5);
- %
- % In the example above, the file c:\data\sample.ns5 will be used. A
- % report of the file contents will be shown. The data will be read from
- % electrodes 15 through 50 in the 3-10 minute time interval. A decimated
- % version of the datafile will be read, where only every 5th sample is read.
- % If any of the arguments above are omitted the default values will be used.
- %
- % Example 2:
- % openNSx('read','c:15:30');
- %
- % In the example above, the file user will be prompted for the file. The
- % file will be read using 'int16' precision as default. All time points
- % of Only channels 15 through 30 will be read. If any of the arguments
- % above are omitted the default values will be used.
- %
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- % Kian Torab
- % support@blackrockmicro.com
- % Blackrock Microsystems
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- % Version History
- %
- % 5.1.8.2:
- % - Fixed the way DayOfWeek is read in MetaTags.
- %
- % 5.1.9.0:
- % - Fixed a bug where with skipFactor being read correctly as a num.
- %
- % 5.1.10.0:
- % - Updated feature to save data headers for a paused file. It is a
- % dependent feature for seperatePausedNSx.
- %
- % 5.1.11.0:
- % - Fixed an issue where 1 sample would not be read when using the
- % t:xx:xx argument and 'sample'.
- % - Fixed an error when 'duration' was used to load specific data length.
- %
- % 5.1.12.0:
- % - Better error handling if a file is not provided and an output
- % variable was requested by the calling function.
- %
- % 5.2.0.0: June 12, 2014
- % - It removes the extra ElectrodesInfo entried for channels not
- % read if 'c:XX:XX' or 'e:XX:XX' are used.
- % - It reports variable ChannelCount under MetaTags correctly.
- % - It automatically compensate for any NSx file with non-0 beginnings
- % and adds 0s for to the begining of the file to properly align the
- % timestamps.
- %
- % 5.2.1.0: June 12, 2014
- % - Fixed a small bug where extra 0s were tacked on to the beginning of
- % paused file segments.
- % - Updated the version.
- %
- % 5.2.2.0: June 13, 2014
- % - Fixed bug for when 'noread' was used on a paused file.
- %
- % 6.0.1.0: December 2, 2014
- % - Fixed a bug related to file format 2.1 not being read correctly.
- % - Corrected the way Filename, FileExt, and FilePath was being
- % processed.
- % - File dialogue now only shows NSx files on non Windows-based
- % computers.
- % - Added 512 synchronized reading capability.
- % - Now on non-Windows computers only NSx files are shown in the file
- % dialogue.
- % - Fixed the date in NSx.MetaTags.DateTime.
- %
- % 6.1.0.0: March, 15 2015
- % - Added the ability to read from networked drives in Windows.
- % - Fixed the DateTime variable in MetaTags.
- % - Fixed the date in NSx.MetaTags.DateTime (again).
- % - Fixed a bug related to starting and stopping packets when a specific
- % time is passed to the function.
- % - Fixed a bug where 512+ ch rules were being applied to smaller channel
- % count configuration.
- %
- % 6.1.1.0: June 15, 2015
- % - Bug fixes related to timestamps when the recording didn't start at
- % proctime 0.
- %
- % 6.2.0.0: October 1, 2015
- % - Fixed a bug related to reading the correct length of time when a skip
- % factor was used.
- % - Bug fixes related to information that separatePausedNSx depends on.
- % - Added 'uV' as an option to read the data in the unit of uV.
- %
- % 6.2.1.0: April 16, 2016
- % - Fixed a bug related to converting the unit to uV in case of having
- % multiple data segments (paused file).
- %
- % 6.2.2.0: July 6, 2016
- % - Fixed another bug related to converting the unit to uV.
- %
- % 6.3.0.0: August 3, 2016
- % - Added support for loading a segment of paused files.
- %
- % 6.3.1.0: August 31, 2016
- % - Fixed a bug when reading a non-o start across a paused segment.
- %
- % 6.4.0.0: December 1, 2016
- % - Fixed a serious bug related to loading paused files.
- % - Fixed a bug where an empty data segment resulted in a cell structure.
- %
- % 6.4.1.0: June 15, 2017
- % - It is no longer necessary to provide the full path for loading a
- % file.
- %
- % 6.4.2.0: September 1, 2017
- % - Fixed a bug related to reading data from sample that is not 1 and
- % timestamp that used to get reset to 0.
- %
- % 6.4.3.0: September 13, 2017
- % - Removed a redundant block of code that was accidentally placed in the
- % script twice.
- % - Checks to see if there's a newer version of NPMK is available.
- %
- % 6.4.3.1: January 24, 2020
- % - Changed file opening access from r+ to r.
- %
- % 7.0.0.0: January 27, 2020
- % - Added support for 64-bit timestamps in NEV and NSx.
- %
- % 7.1.0.0: April 14, 2020
- % - Added option to load the data without zero padding to compensate for
- % a non-zero start time. (David Kluger)
- % - Bug fixes and documentation updates (David Kluger)
- %
- % 7.1.1.0: June 11, 2020
- % - Fixed a bug related to fread and MATLAB 2020a.
- %
- % 7.3.0.0: September 11, 2020
- % - Fixed a bug related to fread and MATLAB 2020a.
- % - Gives a warning about FileSpec 3.0 and gives the user options for how
- % to proceed.
- % - Added a warning about the data unit and that by default it in the
- % unit of 250 nV or 1/4 µV.
- % - If the units are in "raw", ths correct information is now written to
- % the electrodes header: 250 nV (raw).
- %
- % 7.3.1.0: October 2, 2020
- % - If the units are in µV (openNSx('uv'), ths correct information is now
- % written to the electrodes header: 1000 nV (raw).
- %
- % 7.3.2.0: October 23, 2020
- % - Fixed a typo.
- %
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- %% Defining the NSx data structure and sub-branches.
- NSx = struct('MetaTags',[],'Data',[], 'RawData', []);
- NSx.MetaTags = struct('FileTypeID',[],'SamplingLabel',[],'ChannelCount',[],'SamplingFreq',[], 'TimeRes', [], ...
- 'ChannelID',[],'DateTime',[],'DateTimeRaw',[], 'Comment', [], 'FileSpec', [], ...
- 'Timestamp', [], 'DataPoints', [], 'DataDurationSec', [], 'openNSxver', [], 'Filename', [], 'FilePath', [], ...
- 'FileExt', []);
-
- NSx.MetaTags.openNSxver = '7.2.0.0';
-
- %% Check for the latest version fo NPMK
- NPMKverChecker
- % Defining constants
- ExtHeaderLength = 66;
- elecReading = 0;
- NSx.RawData.PausedFile = 0;
- syncShift = 0;
- %% Validating the input arguments. Exit with error message if error occurs.
- next = '';
- for i=1:length(varargin)
- inputArgument = varargin{i};
- if strcmpi(inputArgument, 'ver')
- varargout{1} = NSx.MetaTags.openNSxver;
- return;
- elseif strcmpi(inputArgument, 'channels')
- next = 'channels';
- elseif strcmpi(inputArgument, 'skipfactor')
- next = 'skipfactor';
- elseif strcmpi(inputArgument, 'electrodes')
- next = 'electrodes';
- elseif strcmpi(inputArgument, 'duration')
- next = 'duration';
- elseif strcmpi(inputArgument, 'precision')
- next = 'precision';
- elseif strcmpi(inputArgument, 'report')
- Report = inputArgument;
- elseif strcmpi(inputArgument, 'noread')
- ReadData = inputArgument;
- elseif strcmpi(inputArgument, 'nomultinsp')
- multinsp = 'no';
- elseif strcmpi(inputArgument, 'nozeropad')
- zeropad = 'no';
- elseif strcmpi(inputArgument, 'uV')
- waveformUnits = 'uV';
- elseif strcmpi(inputArgument, 'read')
- ReadData = inputArgument;
- elseif (strncmp(inputArgument, 't:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'duration')
- if strncmp(inputArgument, 't:', 2)
- inputArgument(1:2) = [];
- inputArgument = str2num(inputArgument);
- end
- modifiedTime = 1;
- StartPacket = inputArgument(1);
- EndPacket = inputArgument(end);
- next = '';
- elseif (strncmp(inputArgument, 'e:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'electrodes')
- if exist('KTUEAMapFile', 'file') == 2
- Mapfile = KTUEAMapFile;
- Elec = str2num(inputArgument(3:end)); %#ok<ST2NM>
- if min(Elec)<1 || max(Elec)>128
- disp('The electrode number cannot be less than 1 or greater than 128.');
- if nargout; varargout{1} = -1; end
- return;
- end
- for chanIDX = 1:length(Elec)
- userRequestedChannels(chanIDX) = Mapfile.Electrode2Channel(Elec(chanIDX));
- end
- elecReading = 1;
- else
- disp('To read data by ''electrodes'' the function KTUEAMapFile needs to be in path.');
- clear variables;
- if nargout; varargout{1} = -1; end
- return;
- end
- next = '';
- elseif (strncmp(inputArgument, 's:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'skipFactor')
- if strncmp(inputArgument, 's:', 2)
- skipFactor = str2num(inputArgument(3:end)); %#ok<ST2NM>
- else
- if ischar(inputArgument)
- skipFactor = str2num(inputArgument);
- else
- skipFactor = inputArgument;
- end
- end
- next = '';
- elseif (strncmp(inputArgument, 'c:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'channels')
- if strncmp(inputArgument, 'c:', 2)
- userRequestedChanRow = str2num(inputArgument(3:end)); %#ok<ST2NM>
- else
- userRequestedChanRow = inputArgument;
- end
- next = '';
- elseif (strncmp(varargin{i}, 'p:', 2) && inputArgument(3) ~= '\' && inputArgument(3) ~= '/') || strcmpi(next, 'precision')
- if strncmp(varargin{i}, 'p:', 2)
- precisionTypeRaw = varargin{i}(3:end);
- else
- precisionTypeRaw = varargin{i};
- end
- switch precisionTypeRaw
- case 'int16'
- precisionType = '*int16=>int16';
- precisionData = 'int16';
- case 'short'
- precisionType = '*short=>short';
- precisionData = 'int16';
- case 'double'
- precisionType = '*int16';
- precisionData = 'double';
- otherwise
- disp('Read type is not valid. Refer to ''help'' for more information.');
- if nargout; varargout{1} = -1; end
- return;
- end
- clear precisionTypeRaw;
- next = '';
- elseif strfind(' hour min sec sample ', [' ' inputArgument ' ']) ~= 0
- TimeScale = inputArgument;
- else
- temp = inputArgument;
- if length(temp)>3 && ...
- (strcmpi(temp(3),'\') || ...
- strcmpi(temp(1),'/') || ...
- strcmpi(temp(2),'/') || ...
- strcmpi(temp(1:2), '\\') || ...
- strcmpi(temp(end-3), '.'))
- fname = inputArgument;
- if exist(fname, 'file') ~= 2
- disp('The file does not exist.');
- if nargout;
- varargout{1} = -1;
- end
- return;
- end
- else
- disp(['Invalid argument ''' inputArgument ''' .']);
- if nargout; varargout{1} = -1; end
- return;
- end
- end
- end
- clear next;
- %% Popup the Open File UI. Also, process the file name, path, and extension
- % for later use, and validate the entry.
- if ~exist('fname', 'var')
- [fname, path] = getFile('*.ns1;*.ns2;*.ns3;*.ns4;*.ns5;*.ns6;*.ns6m', 'Choose an NSx file...');
- if fname == 0
- disp('No file was selected.');
- if nargout; varargout{1} = -1; end
- return;
- end
- [~, ~, fext] = fileparts(fname);
- else
- if isempty(fileparts(fname))
- fname = which(fname);
- end
- [path,fname, fext] = fileparts(fname);
- fname = [fname fext];
- path = [path '/'];
- end
- if fname==0
- if nargout; varargout{1} = -1; end
- return;
- end
- %% Loading .x files for multiNSP configuration
- if strcmpi(fext(2:4), 'ns6') && length(fext) == 5
- path(1) = fname(end);
- fname(end) = [];
- end
- tic;
- %% Give all input arguments a default value. All input argumens are
- % optional.
- if ~exist('Report', 'var'); Report = 'noreport'; end
- if ~exist('ReadData', 'var'); ReadData = 'read'; end
- if ~exist('StartPacket', 'var'); StartPacket = 1; end
- if ~exist('TimeScale', 'var'); TimeScale = 'sample'; end
- if ~exist('precisionType', 'var'); precisionType = '*short=>short';...
- precisionData='double'; end
- if ~exist('skipFactor', 'var'); skipFactor = 1; end
- if ~exist('modifiedTime', 'var'); modifiedTime = 0; end
- if ~exist('multinsp', 'var'); multinsp = 'yes'; end
- if ~exist('waveformUnits', 'var'); waveformUnits = 'raw'; end
- if ~exist('zeropad', 'var'); zeropad = 'yes'; end
- % Check to see if 512 setup and calculate offset
- if strcmpi(multinsp, 'yes')
- fiveTwelveFlag = regexp(fname, '-i[0123]-');
- if ~isempty(fiveTwelveFlag)
- syncShift = multiNSPSync(fullfile(path, fname));
- else
- multinsp = 'no';
- end
- end
- if strcmpi(ReadData, 'noread')
- % disp('NOTE: Reading the header information only. To read the data use with parameter ''read'': openNSx(''read'')');
- end
- if strcmp(Report, 'report')
- disp(['openNSx ' NSx.MetaTags.openNSxver]);
- end
- %% Reading Basic Header from file into NSx structure.
- FID = fopen([path fname], 'r', 'ieee-le');
- fileFullPath = fullfile(path, fname);
- [NSx.MetaTags.FilePath, NSx.MetaTags.Filename, NSx.MetaTags.FileExt] = fileparts(fileFullPath);
- NSx.MetaTags.FileTypeID = fread(FID, [1,8] , 'uint8=>char');
- if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALSG')
- NSx.MetaTags.FileSpec = '2.1';
- NSx.MetaTags.SamplingLabel = fread(FID, [1,16] , 'uint8=>char');
- NSx.MetaTags.TimeRes = 30000;
- NSx.MetaTags.SamplingFreq = NSx.MetaTags.TimeRes / fread(FID, 1 , 'uint32=>double');
- ChannelCount = double(fread(FID, 1 , 'uint32=>double'));
- NSx.MetaTags.ChannelCount = ChannelCount;
- NSx.MetaTags.ChannelID = fread(FID, [ChannelCount 1], '*uint32');
- try
- t = dir(fileFullPath);
- NSx.MetaTags.DateTime = t.date;
- end
- elseif or(strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD'), strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP'))
- BasicHeader = fread(FID, 306, '*uint8');
- NSx.MetaTags.FileSpec = [num2str(double(BasicHeader(1))) '.' num2str(double(BasicHeader(2)))];
- HeaderBytes = double(typecast(BasicHeader(3:6), 'uint32'));
- NSx.MetaTags.SamplingLabel = char(BasicHeader(7:22))';
- NSx.MetaTags.Comment = char(BasicHeader(23:278))';
- NSx.MetaTags.TimeRes = double(typecast(BasicHeader(283:286), 'uint32'));
- NSx.MetaTags.SamplingFreq = NSx.MetaTags.TimeRes / double(typecast(BasicHeader(279:282), 'uint32'));
- t = double(typecast(BasicHeader(287:302), 'uint16'));
- ChannelCount = double(typecast(BasicHeader(303:306), 'uint32'));
- NSx.MetaTags.ChannelCount = ChannelCount;
- readSize = double(ChannelCount * ExtHeaderLength);
- ExtendedHeader = fread(FID, readSize, '*uint8');
- if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD')
- timeStampBytes = 4;
- elseif strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP')
- timeStampBytes = 8;
- end
-
- %% Removing extra garbage characters from the Comment field.
- NSx.MetaTags.Comment(find(NSx.MetaTags.Comment==0,1):end) = 0;
-
- %% Populating extended header information
- for headerIDX = 1:ChannelCount
- offset = double((headerIDX-1)*ExtHeaderLength);
- NSx.ElectrodesInfo(headerIDX).Type = char(ExtendedHeader((1:2)+offset))';
- if (~strcmpi(NSx.ElectrodesInfo(headerIDX).Type, 'CC'))
- disp('extended header not supported');
- fclose(FID);
- if nargout; varargout{1} = -1; end
- return;
- end
- NSx.ElectrodesInfo(headerIDX).ElectrodeID = typecast(ExtendedHeader((3:4)+offset), 'uint16');
- NSx.ElectrodesInfo(headerIDX).Label = char(ExtendedHeader((5:20)+offset))';
- NSx.ElectrodesInfo(headerIDX).ConnectorBank = char(ExtendedHeader(21+offset) + ('A' - 1));
- NSx.ElectrodesInfo(headerIDX).ConnectorPin = ExtendedHeader(22+offset);
- NSx.ElectrodesInfo(headerIDX).MinDigiValue = typecast(ExtendedHeader((23:24)+offset), 'int16');
- NSx.ElectrodesInfo(headerIDX).MaxDigiValue = typecast(ExtendedHeader((25:26)+offset), 'int16');
- NSx.ElectrodesInfo(headerIDX).MinAnalogValue = typecast(ExtendedHeader((27:28)+offset), 'int16');
- NSx.ElectrodesInfo(headerIDX).MaxAnalogValue = typecast(ExtendedHeader((29:30)+offset), 'int16');
- if strcmpi(waveformUnits, 'uV')
- NSx.ElectrodesInfo(headerIDX).AnalogUnits = '1000 nV (raw) ';
- else
- conversion = int16(double(NSx.ElectrodesInfo(headerIDX).MaxAnalogValue) / double(NSx.ElectrodesInfo(headerIDX).MaxDigiValue)*1000);
- NSx.ElectrodesInfo(headerIDX).AnalogUnits = [num2str(conversion), ' nV (raw) '];
- end
- NSx.ElectrodesInfo(headerIDX).HighFreqCorner = typecast(ExtendedHeader((47:50)+offset), 'uint32');
- NSx.ElectrodesInfo(headerIDX).HighFreqOrder = typecast(ExtendedHeader((51:54)+offset), 'uint32');
- NSx.ElectrodesInfo(headerIDX).HighFilterType = typecast(ExtendedHeader((55:56)+offset), 'uint16');
- NSx.ElectrodesInfo(headerIDX).LowFreqCorner = typecast(ExtendedHeader((57:60)+offset), 'uint32');
- NSx.ElectrodesInfo(headerIDX).LowFreqOrder = typecast(ExtendedHeader((61:64)+offset), 'uint32');
- NSx.ElectrodesInfo(headerIDX).LowFilterType = typecast(ExtendedHeader((65:66)+offset), 'uint16');
- end
- clear ExtendedHeader;
- %% Parsing and validating FileSpec and DateTime variables
- NSx.MetaTags.DateTimeRaw = t.';
- NSx.MetaTags.DateTime = datestr(datenum(t(1), t(2), t(4), t(5), t(6), t(7)));
- clear t;
- else
- disp('This version of openNSx can only read File Specs 2.1, 2.2, 2.3, and 3.0.');
- disp(['The selected file spec is ' NSx.MetaTags.FileSpec '.']);
- fclose(FID);
- clear variables;
- if nargout; varargout{1} = -1; end
- return;
- end
- % Determining the length of file and storing the value of fEOF
- f.EOexH = double(ftell(FID));
- fseek(FID, 0, 'eof');
- f.EOF = double(ftell(FID));
- % Read Raw Header for saveNSx
- fseek(FID, 0, 'bof');
- NSx.RawData.Headers = fread(FID, f.EOexH, '*uint8');
- % if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD')
- NSx.RawData.DataHeader = fread(FID, timeStampBytes+5, '*uint8');
- % end
- fseek(FID, f.EOexH, 'bof');
- %% Reading all data headers and calculating all the file pointers for data
- % and headers
- if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALSG')
- % Determining DataPoints
- f.BOData = f.EOexH;
- f.EOData = f.EOF;
- NSx.MetaTags.DataPoints = (f.EOF-f.EOexH)/(ChannelCount*2);
- elseif or(strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD'), strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP'))
- segmentCount = 0;
- while double(ftell(FID)) < f.EOF
- if (fread(FID, 1, 'uint8') ~= 1)
- % Fixing another bug in Central 6.01.00.00 TOC where DataPoints is
- % not written back into the Data Header
- %% BIG NEEDS TO BE FIXED
- NSx.MetaTags.DataPoints = double(f.EOF - f.BOData)/(ChannelCount*2);
- break;
- end
- segmentCount = segmentCount + 1;
- if strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD')
- startTimeStamp = fread(FID, 1, 'uint32');
- elseif strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP')
- startTimeStamp = fread(FID, 1, 'uint64');
- end
- if strcmpi(multinsp, 'yes')
- startTimeStamp = startTimeStamp + syncShift;
- fseek(FID, -timeStampBytes, 'cof');
- fwrite(FID, startTimeStamp, '*uint32');
- end
- NSx.MetaTags.Timestamp(segmentCount) = startTimeStamp;
- NSx.MetaTags.DataPoints(segmentCount) = fread(FID, 1, 'uint32');
- f.BOData(segmentCount) = double(ftell(FID));
- fseek(FID, NSx.MetaTags.DataPoints(segmentCount) * ChannelCount * 2, 'cof');
- f.EOData(segmentCount) = double(ftell(FID));
- % Fixing the bug in 6.01.00.00 TOC where DataPoints is not
- % updated and is left as 0
- % NSx.MetaTags.DataPoints(segmentCount) = (f.EOData(segmentCount)-f.BOData(segmentCount))/(ChannelCount*2);
- end
- end
- % Determining if the file has a pause in it
- if length(NSx.MetaTags.DataPoints) > 1
- NSx.RawData.PausedFile = 1;
- % if modifiedTime == 1
- % disp('This data file contains pauses.');
- % disp('openNSx cannot read files with pauses using the ''t:XX'' parameter.');
- % fclose(FID); clear variables; if nargout; varargout{1} = -1; end; return;
- % end
- end
- %% Added by NH - Feb 19, 2014
- % Create incrementing loop to skip from dataheader to dataheader and
- % collect the dataheader data in individual cells
- headerCount = 0;
- if NSx.RawData.PausedFile == 1
- while double(ftell(FID)) < f.EOF
- headerCount = headerCount + 1;
- fseek(FID, f.EOexH, 'bof');
- DataHeader{headerCount} = fread(FID, 9, '*uint8');
- DataPoints(headerCount) = typecast(DataHeader{headerCount}(6:9), 'uint32');
- f.BOData(headerCount) = double(ftell(FID));
- fseek(FID, DataPoints(headerCount) * ChannelCount * 2, 'cof');
- f.EOData(headerCount) = double(ftell(FID));
- end
- % Create an array that will contain all of the dataheader data
- % collected in the cells above
- FinalDataHeader = [];
- %Fill the above mentioned pre-created array
- for i = 1:headerCount
- FinalDataHeader = cat(1,FinalDataHeader,DataHeader(i));
- end
- % Convert to correct type for interpreting in separatingPausedNSx
- FinalDataHeader = cell2mat(FinalDataHeader);
- NSx.RawData.DataHeader = FinalDataHeader;
- fseek(FID, f.EOexH, 'bof');
- end
- %% Copying ChannelID to MetaTags for filespec 2.2, 2.3, and 3.0 for compatibility with filespec 2.1
- if or(strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD'), strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP'))
- NSx.MetaTags.ChannelID = [NSx.ElectrodesInfo.ElectrodeID]';
- end
- %% Determining the number of channels to read and validating the input
- if ~elecReading
- if ~exist('userRequestedChanRow', 'var')
- userRequestedChannels = NSx.MetaTags.ChannelID;
- else
- if any(userRequestedChanRow > ChannelCount)
- disp(['Channel file only contains ' num2str(ChannelCount) ' channels.']);
- fclose(FID); clear variables; if nargout; varargout{1} = -1; end; return;
- else
- userRequestedChannels = NSx.MetaTags.ChannelID(userRequestedChanRow);
- NSx.MetaTags.ChannelCount = length(userRequestedChannels);
- end
- end
- else
- NSx.MetaTags.ChannelCount = length(userRequestedChannels);
- end
- for idx = 1:length(userRequestedChannels)
- if ~any(ismember(NSx.MetaTags.ChannelID, userRequestedChannels(idx)))
- disp(['Electrode ' num2str(Mapfile.Channel2Electrode(userRequestedChannels(idx))) ' does not exist in this file.']);
- fclose(FID);
- clear variables;
- if nargout; varargout{1} = -1; end
- return;
- end
- userRequestedChanRow(idx) = find(NSx.MetaTags.ChannelID == userRequestedChannels(idx),1);
- end
- %% Removing extra ElectrodesInfo for channels not read
- if or(strcmpi(NSx.MetaTags.FileTypeID, 'NEURALCD'), strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP'))
- for headerIDX = length(NSx.ElectrodesInfo):-1:1
- if ~ismember(headerIDX, userRequestedChanRow)
- NSx.ElectrodesInfo(headerIDX) = [];
- end
- end
- end
- %% Adjusts StartPacket and EndPacket based on what time setting (sec, min,
- % hour, or packets) the user has indicated in the input argument.
- if ~exist('EndPacket', 'var')
- EndPacket = sum(NSx.MetaTags.DataPoints);
- end
- switch TimeScale
- case 'sec'
- StartPacket = double(StartPacket) * NSx.MetaTags.SamplingFreq + 1;
- EndPacket = EndPacket * NSx.MetaTags.SamplingFreq;
- case 'min'
- StartPacket = StartPacket * NSx.MetaTags.SamplingFreq * 60 + 1;
- EndPacket = EndPacket * NSx.MetaTags.SamplingFreq * 60;
- case 'hour'
- StartPacket = StartPacket * NSx.MetaTags.SamplingFreq * 3600 + 1;
- EndPacket = EndPacket * NSx.MetaTags.SamplingFreq * 3600;
- end
- %% Validate StartPacket and EndPacket to make sure they do not exceed the
- % length of packets in the file. If EndPacket is over then the last packet
- % will be set for EndPacket. If StartPacket is over then will exist with an
- % error message.
- if StartPacket >= EndPacket
- disp('The starting packet is greater than the end packet.');
- disp('The file was not read.');
- fclose(FID);
- if nargout; varargout{1} = -1; end
- return;
- end
- if StartPacket <= 0
- disp('The starting packet must be greater or equal to 1.');
- disp('The starting packet was changed to 1.');
- StartPacket = 1;
- end
- if EndPacket > sum(NSx.MetaTags.DataPoints)
- if StartPacket >= NSx.MetaTags.DataPoints
- disp('The starting packet is greater than the total data duration.');
- disp('The file was not read.');
- fclose(FID);
- if nargout; varargout{1} = -1; end
- return;
- end
- disp('The time interval specified is longer than the data duration.');
- disp('Last data point will be used instead.');
- disp('Press enter to continue...');
- pause;
- EndPacket = sum(NSx.MetaTags.DataPoints) - 1;
- end
- % Adjusting the endPacket for the skipFactor to reduce the length of
- % the data read.
- % DEBUG: This is not needed since the same length of data is to be
- % read.
- EndPacket = EndPacket / skipFactor;
- % Finding which data segment the StartPacket is falling in-between
- segmentCounters = [];
- startTimeStampShift = 0;
- if NSx.RawData.PausedFile
- dataPointOfInterest = StartPacket;
- for idx = 1:length(NSx.MetaTags.DataPoints)
- if dataPointOfInterest <= sum(NSx.MetaTags.DataPoints(1:idx)) %sum(NSx.MetaTags.DataPoints(1:idx)) < dataPointOfInterest && ...
- if isempty(segmentCounters)
- if idx == 1
- segmentStartPacket(idx) = dataPointOfInterest;
- else
- segmentStartPacket(idx) = dataPointOfInterest - (sum(NSx.MetaTags.DataPoints(1:idx-1)));
- end
- startTimeStampShift = segmentStartPacket(idx) - 1;
- if EndPacket <= sum(NSx.MetaTags.DataPoints(1:idx))
- segmentDataPoints(idx) = EndPacket - sum(NSx.MetaTags.DataPoints(1:idx-1)) - segmentStartPacket(idx) + 1;
- segmentCounters = [idx, idx];
- break;
- end
- segmentDataPoints(idx) = sum(NSx.MetaTags.DataPoints(1:idx)) - dataPointOfInterest + 1;
- dataPointOfInterest = EndPacket;
- else
- segmentStartPacket(idx) = 1;
- if idx == 1
- segmentDataPoints(idx) = dataPointOfInterest;
- else
- segmentDataPoints(idx) = dataPointOfInterest - sum(NSx.MetaTags.DataPoints(1:idx-1));
- segmentCounters(length(segmentCounters)+1) = idx;
- end
- break;
- end
- segmentCounters(length(segmentCounters)+1) = idx;
- else
- if isempty(segmentCounters)
- segmentStartPacket(idx) = NSx.MetaTags.DataPoints(idx);
- segmentDataPoints(idx) = 0;
- elseif length(segmentCounters) == 1
- segmentStartPacket(idx) = 1;
- segmentDataPoints(idx) = NSx.MetaTags.DataPoints(idx);
- else
- segmentStartPacket(idx) = 1;
- segmentDataPoints(idx) = 0;
- end
- end
- end
- end
- DataLength = EndPacket - StartPacket + 1;
-
- % from now StartPacket and EndPacket are in terms of Samples and are zero-based
- clear TimeScale
- %% Reading the data if flag 'read' is used
- if strcmp(ReadData, 'read')
- % Determine what channels to read
- numChansToRead = double(length(min(userRequestedChanRow):max(userRequestedChanRow)));
- if NSx.RawData.PausedFile
- for dataIDX = segmentCounters(1):segmentCounters(2) %1:length(NSx.MetaTags.DataPoints)
- fseek(FID, f.BOData(dataIDX), 'bof');
- % Skip the file to the beginning of the time requsted, if not 0
- fseek(FID, (segmentStartPacket(dataIDX) - 1) * 2 * ChannelCount, 'cof');
- % Skip the file to the first channel to read
- fseek(FID, (find(NSx.MetaTags.ChannelID == min(userRequestedChannels))-1) * 2, 'cof');
- % Read data
- NSx.Data{size(NSx.Data,2)+1} = fread(FID, [numChansToRead segmentDataPoints(dataIDX)], [num2str(numChansToRead) precisionType], double((ChannelCount-numChansToRead)*2 + ChannelCount*(skipFactor-1)*2));
- end
- NSx.MetaTags.DataPoints = segmentDataPoints(segmentCounters(1):segmentCounters(2));
- NSx.MetaTags.Timestamp = NSx.MetaTags.Timestamp(segmentCounters(1):segmentCounters(2));
- NSx.MetaTags.Timestamp(1) = NSx.MetaTags.Timestamp(1) + startTimeStampShift;
- else
- fseek(FID, f.BOData(1), 'bof');
- % Skip the file to the beginning of the time requsted, if not 0
- fseek(FID, (StartPacket - 1) * 2 * ChannelCount, 'cof');
- % Skip the file to the first channel to read
- fseek(FID, (find(NSx.MetaTags.ChannelID == min(userRequestedChannels))-1) * 2, 'cof');
- % Read data
- NSx.Data = fread(FID, [numChansToRead DataLength], [num2str(numChansToRead) precisionType], double((ChannelCount-numChansToRead)*2 + ChannelCount*(skipFactor-1)*2));
- end
- end
- %% Fixing a bug in 6.03 TOC where an extra 0-length packet is introduced
- if NSx.RawData.PausedFile && strcmp(ReadData, 'read')
- if isempty(NSx.Data{1})
- NSx.Data = cell2mat(NSx.Data(2));
- end
- end
- % Fixing a bug in 6.03 where data packets with 0 lengh may be added
- if any(NSx.MetaTags.DataPoints == 0) && strcmp(ReadData, 'read')
- segmentsThatAreZero = find(NSx.MetaTags.DataPoints == 0);
- NSx.MetaTags.DataPoints(segmentsThatAreZero) = [];
- NSx.MetaTags.Timestamp(segmentsThatAreZero) = [];
- NSx.Data(segmentsThatAreZero) = [];
- end
- %% Removing extra channels that were read, but weren't supposed to be read
- channelThatWereRead = min(userRequestedChanRow):max(userRequestedChanRow);
- if ~isempty(setdiff(channelThatWereRead,userRequestedChanRow))
- deleteChannels = setdiff(channelThatWereRead, userRequestedChanRow) - min(userRequestedChanRow) + 1;
- if NSx.RawData.PausedFile
- for segIDX = 1:size(NSx.Data,2)
- NSx.Data{segIDX}(deleteChannels,:) = [];
- end
- else
- NSx.Data(deleteChannels,:) = [];
- end
- end
- %% Remove the cell if there is only one recorded segment present
- if all(size(NSx.Data) == [1,1])
- NSx.Data = NSx.Data{1};
- end
- %% Adjusting the ChannelID variable to match the read electrodes
- channelIDToDelete = setdiff(1:ChannelCount, userRequestedChanRow);
- NSx.MetaTags.ChannelID(channelIDToDelete) = [];
- %% Adjusting the file for a non-0 timestamp start
- if strcmpi(NSx.MetaTags.FileTypeID, 'BRSMPGRP') && strcmpi(zeropad, 'yes')
- NPMKSettings = settingsManager;
- if NSx.MetaTags.Timestamp(1) > 30000 && NPMKSettings.ShowZeroPadWarning == 1
- disp(' ');
- disp('You have chosen to zeropad the NSx file that contains a large timestamp gap.');
- 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.');
- disp('https://support.blackrockmicro.com/portal/en/kb/articles/nozeropad-in-opennsx');
- response = input('This could take a while. Do you wish to continue? ', 's');
- if strcmpi(response, 'n')
- return;
- end
- response = input('Do you want NPMK to continue to ask you about this every time? ', 's');
- if strcmpi(response, 'n')
- NPMKSettings.ShowZeroPadWarning = 0;
- settingsManager(NPMKSettings);
- end
- end
- end
- if ~NSx.RawData.PausedFile && StartPacket == 1 && strcmpi(zeropad, 'yes')
- if length(NSx.MetaTags.Timestamp) > 1
- cellIDX = 1; % only do this for the first cell segment and not modify the subsequent segments
- if strcmpi(ReadData, 'read')
- NSx.Data{cellIDX} = [zeros(NSx.MetaTags.ChannelCount, floor(NSx.MetaTags.Timestamp(cellIDX) / skipFactor), precisionData) NSx.Data{cellIDX}];
- end
- NSx.MetaTags.DataPoints(cellIDX) = NSx.MetaTags.DataPoints(cellIDX) + NSx.MetaTags.Timestamp(cellIDX);
- NSx.MetaTags.DataDurationSec(cellIDX) = NSx.MetaTags.DataPoints(cellIDX) / NSx.MetaTags.SamplingFreq;
- NSx.MetaTags.Timestamp(cellIDX) = 0;
- elseif strcmpi(ReadData, 'read')
- NSx.Data = [zeros(NSx.MetaTags.ChannelCount, floor(NSx.MetaTags.Timestamp / skipFactor), precisionData) NSx.Data];
- NSx.MetaTags.DataPoints = size(NSx.Data,2);
- NSx.MetaTags.DataDurationSec = NSx.MetaTags.DataPoints / NSx.MetaTags.SamplingFreq;
- NSx.MetaTags.Timestamp = 0;
- end
- if strcmpi(multinsp, 'yes')
- NSx.Data = [zeros(NSx.MetaTags.ChannelCount, syncShift, precisionData) NSx.Data];
- NSx.MetaTags.DataPoints = size(NSx.Data,2);
- NSx.MetaTags.DataDurationSec = NSx.MetaTags.DataPoints / NSx.MetaTags.SamplingFreq;
- end
- end
- %% Adjusting for the data's unit.
- if strcmpi(waveformUnits, 'uV')
- if iscell(NSx.Data) % Contribution by Michele Cox @ Vanderbilt
- NSx.Data = cellfun(@(x) bsxfun(@rdivide, double(x), 1./(double([NSx.ElectrodesInfo.MaxAnalogValue])./double([NSx.ElectrodesInfo.MaxDigiValue]))'),NSx.Data ,'UniformOutput',false);
- else
- NSx.Data = bsxfun(@rdivide, double(NSx.Data), 1./(double([NSx.ElectrodesInfo.MaxAnalogValue])./double([NSx.ElectrodesInfo.MaxDigiValue]))');
- end % End of contribution
- else
- NPMKSettings = settingsManager;
- if NPMKSettings.ShowuVWarning == 1
- disp(' ');
- 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.');
- disp('To read the data in unit of µV, use openNSx(''uv''). For more information type: help openNSx');
- response = input('Do you want NPMK to continue to ask you about this every time? ', 's');
- if strcmpi(response, 'n')
- NPMKSettings.ShowuVWarning = 0;
- settingsManager(NPMKSettings);
- end
- end
- end
- %% Converting the data points in sample to seconds
- NSx.MetaTags.DataPointsSec = double(NSx.MetaTags.DataPoints)/NSx.MetaTags.SamplingFreq;
- %% Calculating the DataPoints in seconds and adding it to MetaData
- NSx.MetaTags.DataDurationSec = double(NSx.MetaTags.DataPoints)/NSx.MetaTags.SamplingFreq;
- %% Displaying a report of basic file information and the Basic Header.
- if strcmp(Report, 'report')
- disp( '*** FILE INFO **************************');
- disp(['File Path = ' NSx.MetaTags.FilePath]);
- disp(['File Name = ' NSx.MetaTags.Filename]);
- disp(['File Extension = ' NSx.MetaTags.FileExt]);
- disp(['File Version = ' NSx.MetaTags.FileSpec]);
- disp(['Duration (seconds) = ' num2str(NSx.MetaTags.DataDurationSec)]);
- disp(['Total Data Points = ' num2str(NSx.MetaTags.DataPoints)]);
- disp(' ');
- disp( '*** BASIC HEADER ***********************');
- disp(['File Type ID = ' NSx.MetaTags.FileTypeID]);
- disp(['Sample Frequency = ' num2str(double(NSx.MetaTags.SamplingFreq))]);
- disp(['Electrodes Read = ' num2str(double(NSx.MetaTags.ChannelCount))]);
- disp(['Data Point Read = ' num2str(size(NSx.Data,2))]);
- end
- %% If user does not specify an output argument it will automatically create
- % a structure.
- outputName = ['NS' fext(4)];
- if (nargout == 0)
- assignin('caller', outputName, NSx);
- else
- varargout{1} = NSx;
- end
- if strcmp(Report, 'report')
- disp(['The load time for ' outputName ' file was ' num2str(toc, '%0.1f') ' seconds.']);
- end
- fclose(FID);
- end
|