Skip to content

Commit

Permalink
Merge pull request #74 from BlackrockNeurotech/development
Browse files Browse the repository at this point in the history
Merge NEV comment timestamp fixes and NSx.Time error
  • Loading branch information
dkluger authored Oct 4, 2024
2 parents b496b58 + 41ae858 commit 5125315
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 27 deletions.
6 changes: 5 additions & 1 deletion NPMK/Versions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -536,4 +536,8 @@ NPMK Version 2.8.2.0: 5 May 2014
% 6.2.3.0: openNEV: June 13, 2024
% - Removed DataDuration and DataDurationSec from output
%
LATEST:5.5.4.0
***** NPMK Version 5.5.5.0 :June 5, 2024 *****
% 6.2.4.0: September 30, 2024
% - Fixed timestamp reporting for comments in filespec 3.0 (David Kluger)
%
LATEST:5.5.5.0
77 changes: 52 additions & 25 deletions NPMK/openNEV.m
Original file line number Diff line number Diff line change
Expand Up @@ -229,20 +229,22 @@
% spike data. (Spencer Kellis)
% 6.2.3.0: June 13, 2024
% - Removed DataDuration and DataDurationSec from output
%
% 6.2.4.0: September 30, 2024
% - Fixed timestamp reporting for comments in filespec 3.0 (David Kluger)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Check for the latest version fo NPMK
NPMKverChecker

%% Defining structures
NEV = struct('MetaTags',[], 'ElectrodesInfo', [], 'Data', []);
NEV.MetaTags.openNEVver = '6.2.3.0';
NEV.MetaTags = struct('Subject', [], 'Experimenter', [], 'DateTime', [],...
'SampleRes',[],'Comment',[],'FileTypeID',[],'Flags',[], 'openNEVver', [], ...
'DateTimeRaw', [], 'FileSpec', [], 'PacketBytes', [], 'HeaderOffset', [], ...
'PacketCount', [], 'TimeRes', [], 'Application', [], 'Filename', [], 'FilePath', []);
% 'DataDuration', [], 'DataDurationSec', [],
NEV.MetaTags.openNEVver = '6.2.4.0';
NEV.Data = struct('SerialDigitalIO', [], 'Spikes', [], 'Comments', [], 'VideoSync', [], ...
'Tracking', [], 'TrackingEvents', [], 'PatientTrigger', [], 'Reconfig', []);
NEV.Data.Spikes = struct('TimeStamp', [],'Electrode', [],...
Expand Down Expand Up @@ -300,6 +302,7 @@
strcmpi(tempst(1:2), '\\') || ...
strcmpi(tempst(end-3), '.'))
fileFullPath = varargin{i};
[path, fileName, fileExt] = fileparts(fileFullPath);
if exist(fileFullPath, 'file') ~= 2
disp('The file does not exist.');
varargout{1} = [];
Expand Down Expand Up @@ -338,11 +341,12 @@
%% Defining and validating variables
if ~exist('fileFullPath', 'var')
if exist('getFile.m', 'file') == 2
[fileName pathName] = getFile('*.nev*', 'Choose a NEV file...');
[fileName, pathName] = getFile('*.nev*', 'Choose a NEV file...');
else
[fileName pathName] = uigetfile;
[fileName, pathName] = uigetfile;
end
fileFullPath = [pathName fileName];
[path, fileName, fileExt] = fileparts(fileFullPath);
if fileFullPath==0;
clear variables;
if nargout
Expand All @@ -353,8 +357,6 @@
end
end

[~, ~, fileExt] = fileparts(fileFullPath);

%% Loading .x files for multiNSP configuration
if strcmpi(fileExt(2:4), 'nev') && length(fileExt) == 5
fileFullPath(1) = fileFullPath(end);
Expand All @@ -363,17 +365,18 @@


if ~isfield(Flags, 'Report'); Flags.Report = 'noreport'; end
if ~isfield(Flags, 'WarningStat'); Flags.WarningStat = 'warning'; end;
if ~isfield(Flags, 'WarningStat'); Flags.WarningStat = 'warning'; end
if ~isfield(Flags, 'ReadData'); Flags.ReadData = 'read'; end
if ~isfield(Flags, 'ParseData'); Flags.ParseData = 'noparse'; end
if ~isfield(Flags, 'SaveFile'); Flags.SaveFile = 'save'; end;
if ~isfield(Flags, 'NoMAT'); Flags.NoMAT = 'yesmat'; end;
if ~isfield(Flags, 'waveformUnits'); Flags.waveformUnits = 'raw'; end;
if ~isfield(Flags, 'digIOBits'); Flags.digIOBits = '16bits'; end;
if ~isfield(Flags, 'Overwrite'); Flags.Overwrite = 'nooverwrite'; end;
if ~isfield(Flags, 'MultiNSP'); Flags.MultiNSP = 'multinsp'; end;
if ~isfield(Flags, 'selChannels'); Flags.selChannels = 'all'; end;
if ~isfield(Flags, 'Direct'); Flags.Direct = 'nodirect'; end;
if ~isfield(Flags, 'SaveFile'); Flags.SaveFile = 'save'; end
if ~isfield(Flags, 'NoMAT'); Flags.NoMAT = 'yesmat'; end
if ~isfield(Flags, 'waveformUnits'); Flags.waveformUnits = 'raw'; end
if ~isfield(Flags, 'digIOBits'); Flags.digIOBits = '16bits'; end
if ~isfield(Flags, 'Overwrite'); Flags.Overwrite = 'nooverwrite'; end
if ~isfield(Flags, 'MultiNSP'); Flags.MultiNSP = 'multinsp'; end
if ~isfield(Flags, 'selChannels'); Flags.selChannels = 'all'; end
if ~isfield(Flags, 'Direct'); Flags.Direct = 'nodirect'; end
if ~isfield(Flags, 'PTP'); Flags.PTP = 'noPTP'; end

if strcmpi(Flags.Report, 'report')
disp(['openNEV ' NEV.MetaTags.openNEVver]);
Expand Down Expand Up @@ -447,10 +450,19 @@
clear BasicHeader;

if or(strcmpi(NEV.MetaTags.FileTypeID, 'NEURALEV'), strcmpi(NEV.MetaTags.FileTypeID, 'BREVENTS'))
if exist([fileFullPath(1:end-8) '.sif'], 'file') == 2
METATAGS = textread([fileFullPath(1:end-8) '.sif'], '%s');
prefixes = {'Hub1-', 'Hub2-','NSP-'};
if NEV.MetaTags.TimeRes == 1e9
Flags.PTP = 'PTP';
end
fileNameBase = fileName(1:end-4);
sifName = [path '\' erase(fileNameBase,prefixes) '.sif'];
if exist(sifName, 'file') == 2
METATAGS = textread(sifName, '%s');
NEV.MetaTags.Subject = METATAGS{3}(5:end-5);
NEV.MetaTags.Experimenter = [METATAGS{5}(8:end-8) ' ' METATAGS{6}(7:end-7)];
else
warning(['No .sif file found corresponding to ' fileFullPath...
'. Subject and Experimenter data skipped in MetaTags']);
end
end
if ~any(strcmpi(NEV.MetaTags.FileSpec, {'2.1', '2.2', '2.3', '3.0'}))
Expand Down Expand Up @@ -714,9 +726,17 @@
tempCharSet = tRawData(timeStampBytes+3, commentIndices);
NEV.Data.Comments.CharSet = tempCharSet(orderOfTS); clear tempCharSet;
colorFlag = tRawData(timeStampBytes+4, commentIndices);
NEV.Data.Comments.TimeStampStarted = tRawData(timeStampBytes+5:timeStampBytes+8, commentIndices);
tempTimeStampStarted = typecast(NEV.Data.Comments.TimeStampStarted(:), 'uint32').';
NEV.Data.Comments.TimeStampStarted = tempTimeStampStarted(orderOfTS); clear tempTimeStampStarted;
tempPayload = tRawData(timeStampBytes+5:timeStampBytes+8, commentIndices);
diffTimeStampStarted = typecast(tempPayload(:), 'uint32').';
if strcmp(Flags.PTP, 'PTP')
diffFactor = 1000; % ns -> µs conversion
else
diffFactor = 1; % sample count
end
tempTimeStampStarted = NEV.Data.Comments.TimeStamp-uint64(diffTimeStampStarted)*diffFactor;
NEV.Data.Comments.TimeStampStarted = tempTimeStampStarted(orderOfTS); clear diffTimeStampStarted tempTimeStampStarted;
NEV.Data.Comments.Color = dec2hex(typecast(tempPayload(:),'uint32')); clear tempPayload

tempText = char(tRawData(timeStampBytes+9:Trackers.countPacketBytes, commentIndices).');
NEV.Data.Comments.Text = tempText(orderOfTS,:); clear tempText;

Expand All @@ -742,12 +762,19 @@
NEV.Data.Comments.Text(neuroMotiveEvents,:) = [];
colorFlag(neuroMotiveEvents) = [];

% Figuring out the text color of the comments that had color
NEV.Data.Comments.Color = dec2hex(NEV.Data.Comments.TimeStampStarted);
NEV.Data.Comments.Color(colorFlag == 1,:) = repmat('0', size(NEV.Data.Comments.Color(colorFlag == 1,:)));
NEV.Data.Comments.TimeStampStarted(colorFlag == 0) = NEV.Data.Comments.TimeStamp(colorFlag == 0);
% remove duplicated comment packets for color and time stamp
% start in filespec 3.0
if strcmp(NEV.MetaTags.FileSpec, '3.0')
NEV.Data.Comments.TimeStampStarted = NEV.Data.Comments.TimeStampStarted(colorFlag == 1);
NEV.Data.Comments.TimeStampStartedSec = NEV.Data.Comments.TimeStampStartedSec(colorFlag == 1);
NEV.Data.Comments.TimeStamp = NEV.Data.Comments.TimeStamp(colorFlag == 1);
NEV.Data.Comments.TimeStampSec = NEV.Data.Comments.TimeStampSec(colorFlag == 1);
NEV.Data.Comments.CharSet = NEV.Data.Comments.CharSet(colorFlag == 1);
NEV.Data.Comments.Text = NEV.Data.Comments.Text(colorFlag == 1,:);
NEV.Data.Comments.Color = NEV.Data.Comments.Color(colorFlag == 0,:);
end

clear commentIndices;
clear commentIndices colorFlag;
end
if ~isempty(videoSyncPacketIDIndices)
NEV.Data.VideoSync.TimeStamp = Timestamp(videoSyncPacketIDIndices);
Expand Down
4 changes: 3 additions & 1 deletion NPMK/openNSx.m
Original file line number Diff line number Diff line change
Expand Up @@ -1444,7 +1444,9 @@
% reduce to array if only one cell
if flagReadData && iscell(NSx.Data) && length(NSx.Data)==1
NSx.Data = NSx.Data{1};
NSx.Time = NSx.Time{1};
if isfield(NSx,'Time')
NSx.Time = NSx.Time{1};
end
end

% Display a report of basic file information and the Basic Header.
Expand Down

0 comments on commit 5125315

Please sign in to comment.