Commit a3e3afb5 authored by Jay Dubb's avatar Jay Dubb
Browse files

v1.29.3, v1.29.2, v1.29.1

-- Fix not being able to load Snirf data because of missing OR invalid stim or AuxClass. Fix some error handling bugs when optional fields from acquisition layer are missing, we inconsistently reported to user that data wasn't loaded correctly but other times we'd load it as if there's no error. Distinguish between reporting error when fields are invalid vs reporting a warning when fields are missing. Errors raise negative errors and prevent file data from being accessed displaying message boix instead whereas missing optional fields produce positive errors and allow data to be access and generate only a log message on the cosole and log file.

-- Move acquisition layer error messages from RunClass to the acquisition layer (AcqDataClass, SnirfClass, NirsClass). The previous code would not work when loading faulty .nirs files. Create methods for the acquisition classes to access the correct error message matching the error.

-- Fix error when displaying data from .nirs files by removing incomplete code in MainGUI to handle non-Snirf data.

-- Simplify DataTreeClass.AutoSetDataStorageScheme to default to 'files' since in AtlasViewer it doesn't matter what the save scheme was that Homer3 used. Defaulting to 'files' means that whether the save scheme was 'memory' or 'files' AV is sure to find the output as opposed to a 'memory' setting if the output is not found in memory it'll give up looking. This change was propagated from AV.

-- Fix missing error checking in hmrR_BlockAvg_Nirs which didn't handle when stim data is not passed.

-- Bring back logging of matlab version at startup which somehow got lost in a previous commit.

-- Add DebugLevel class to be able to set various debug levels to - for instance - simulate bad data for testing. Add debuglevel fields to AuxClass and StimClass to be able to generate bad data at when saving a snirf file (when converting from other formats)

-- Add AppSettings configuration in unit tests UnitTestsAll_Nirs and UnitTestsAll_Snirf when running standalone .
parent 014edcaa
Loading
Loading
Loading
Loading
+21 −7
Original line number Diff line number Diff line
@@ -3,6 +3,9 @@ classdef AcqDataClass < matlab.mixin.Copyable
    properties (Access = private)
        logger
    end
    properties (Access = public)
        errmsgs
    end
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % These methods must be implemented in any derived class
@@ -81,14 +84,25 @@ classdef AcqDataClass < matlab.mixin.Copyable
        
        
        % -------------------------------------------------------
        function b = Error(obj)
            if obj.GetError()<0
                b = true;
            elseif obj.GetError()==0
                b = false;
            else
                b = true;
        function err = Error(obj)
            err = obj.GetError();
        end
        
        
        % ---------------------------------------------------------
        function msg = GetErrorMsg(obj)
            msg = '';
            if isempty(obj)
                msg = 'AcqDataClass object is empty';
                return;
            end
            if isempty(obj.errmsgs)
                return;
            end
            if ~obj.GetError()
                return;
            end
            msg = obj.errmsgs{abs(obj.GetError())};
        end
        
        
+140 −72
Original line number Diff line number Diff line
@@ -34,15 +34,15 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
            % 
            %           NirsClass with properties:
            % 
            %                      SD: [11 struct]
            %                       t: [126981 double]
            %                       s: [126982 double]
            %                       d: [1269818 double]
            %                     aux: [126981 double]
            %                      SD: [1x1 struct]
            %                       t: [12698x1 double]
            %                       s: [12698x2 double]
            %                       d: [12698x18 double]
            %                     aux: [12698x1 double]
            %               CondNames: {'1'  '2'}
            %                filename: './s1/neuro_run01.nirs'
            %              fileformat: 'mat'
            %         supportedFomats: [11 struct]
            %         supportedFomats: [1x1 struct]
            %                     err: 0
            %
            
@@ -89,6 +89,19 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
            obj.d         = [];
            obj.aux       = [];
            obj.CondNames = {};
            
            
            % Initialize non-.nirs variables
            obj.errmsgs = {
                'MATLAB could not load the file.'
                '''d'' is invalid.'
                '''t'' is invalid.'
                '''SD'' is invalid.'
                '''aux'' is invalid.'
                '''s'' is invalid.'
                'error unknown.'
                };
            
        end
        
        
@@ -101,9 +114,10 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
        
        
        % ---------------------------------------------------------
        function err = LoadMat(obj, fname, params)
        function err = LoadMat(obj, fname, ~)
            err = 0;

            try 
                % Arg 1
                if ~exist('fname','var') || ~exist(fname,'file')
                    fname = '';
@@ -122,63 +136,99 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
                
                % Don't reload if not empty
                if ~obj.IsEmpty()
                    err = obj.GetError();     % preserve error state if exiting early
                    return;
                end
                
                warning('off', 'MATLAB:load:variableNotFound');
                fdata = load(fname,'-mat', 'SD','t','d','s','aux','CondNames');
                
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                % NOTE: Optional fields have positive error codes if they are
                % missing, but negative error codes if they're not missing but
                % invalid
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                
                
                % Mandatory fields
                err = obj.LoadRawData(fdata, err);
                err = obj.LoadTime(fdata, err);
                err = obj.LoadProbeMeasList(fdata, err);
                                
                % Optional fields
                err = obj.LoadAux(fdata, err);
                err = obj.LoadStims(fdata, err);
                
            catch
                
                
                
            end            
            warning('on', 'MATLAB:load:variableNotFound');
            
        end
        
        
        
        % ---------------------------------------------------------
        function err = LoadRawData(obj, fdata, err)
            if isproperty(fdata,'d')
                obj.d = fdata.d;
                if isempty(obj.d)
                if isempty(obj.d) && err == 0
                    err = -2;
                end
            else
            elseif err == 0
                err = -2;
            end
        end
        
        
        % ---------------------------------------------------------
        function err = LoadTime(obj, fdata, err)
            if isproperty(fdata,'t')
                obj.t = fdata.t;
                if ~isempty(obj.t)
                    obj.errmargin = min(diff(obj.t))/10;
                else
                elseif err == 0
                    err = -3;
                end
            else
            elseif err == 0
                err = -3;
            end
            if length(fdata.t) ~= size(fdata.d,1) && err == 0
                err = -3;
            end            
        end
        
                
        % ---------------------------------------------------------
        function err = LoadProbeMeasList(obj, fdata, err)
            if isproperty(fdata,'SD')
                obj.SetSD(fdata.SD);
                if isempty(obj.SD)
                if isempty(obj.SD) && err == 0
                    err = -4;
                end
            else
            elseif err == 0
                err = -4;
            end
        end
        
        
            % Optional fields
        % ---------------------------------------------------------
        function err = LoadAux(obj, fdata, err)
            if isproperty(fdata,'aux')
                obj.aux = fdata.aux;
            else
                obj.aux = [];
            end            
            if obj.LoadStims(fname, fdata)<0 
                err = -4;
            elseif err==0
                err = 5;
            end
        end
       
        
                
        % ---------------------------------------------------------
        function err = LoadStims(obj, fname, fdata)
            err = 0;
            
            if ~exist('fdata','var') || isempty(fdata)
                % Arg 1
                if ~exist('fname','var') || ~exist(fname,'file')
                    fname = '';
                end
        function err = LoadStims(obj, fdata, err)                        
            if ischar(fdata)
                fname = fdata;
                
                % Do some error checking
                if ~isempty(fname)
@@ -190,24 +240,32 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
                    err = -1;
                    return;
                end                              
                
                % Don't reload if not empty
                if ~obj.IsEmpty()
                    return;
                end
                
                warning('off', 'MATLAB:load:variableNotFound');
                fdata = load(fname,'-mat', 's','CondNames');
                fdata = load(fname,'-mat', 's','CondNames','t');
            end
        
            if ~isproperty(fdata,'s')
                if err==0 
                    err = 6; 
                end
                return;
            end
            
            if isproperty(fdata,'s')
            obj.s = fdata.s;
            else
                obj.s = [];
            if size(fdata.s,1) ~= size(fdata.t)
                if err==0 
                    err = -6; 
                end
                return;
            end
            if isproperty(fdata,'CondNames')
                obj.CondNames = fdata.CondNames;
                if size(fdata.s,1) ~= length(fdata.CondNames)
                    if err==0
                        err = -6;
                    end
                    return;
                end
            else
                obj.InitCondNames();
            end
@@ -261,7 +319,7 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
            % If we're working off the snirf file instead of loading everything into memory
            % then we have to load stim here from file before accessing it.
            if strcmpi(obj.GetDataStorageScheme(), 'files')
                obj.LoadStims(obj.GetFilename());
                obj.LoadStims(obj.GetFilename(), 0);
            end
            
            % Generate new instance of NirsClass
@@ -534,6 +592,16 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
            ich={ich};
        end

        
        % ---------------------------------------------------------
        function t = GetAuxiliaryTime(obj)
            t = [];
            if isempty(obj.aux)
                return;
            end
            t = obj.t;
        end
        
    end
    
    
+80 −10
Original line number Diff line number Diff line
@@ -7,6 +7,11 @@ classdef AuxClass < FileLoadSaveClass
        timeOffset
    end

    % Properties not part of the SNIRF spec. These parameters aren't loaded or saved to files
    properties (Access = private)
        debuglevel
    end    

    methods
        
        % -------------------------------------------------------
@@ -14,6 +19,8 @@ classdef AuxClass < FileLoadSaveClass
            % Set class properties not part of the SNIRF format
            obj.SetFileFormat('hdf5');

            obj.debuglevel = DebugLevel('none');
            
            obj.timeOffset = 0;
            if nargin==1
                if isa(varargin{1}, 'AuxClass')
@@ -31,13 +38,11 @@ classdef AuxClass < FileLoadSaveClass
                obj.dataTimeSeries = [];
                obj.time = [];
            end                        
            
        end
        
        
        % -------------------------------------------------------
        function err = LoadHdf5(obj, fileobj, location)
            err = 0;
            
            % Arg 1
            if ~exist('fileobj','var') || (ischar(fileobj) && ~exist(fileobj,'file'))
@@ -51,7 +56,7 @@ classdef AuxClass < FileLoadSaveClass
                location = ['/',location];
            end
            
            % Error checking            
            % Error checking for file existence
            if ~isempty(fileobj) && ischar(fileobj)
                obj.SetFilename(fileobj);
            elseif isempty(fileobj)
@@ -66,8 +71,10 @@ classdef AuxClass < FileLoadSaveClass
            try               
                % Open group
                [gid, fid] = HDF5_GroupOpen(fileobj, location);
                if gid<0
                    err = -1;
                
                % Absence of optional aux field raises error > 0
                if gid.double < 0
                    err = 1;
                    return;
                end
                
@@ -76,10 +83,20 @@ classdef AuxClass < FileLoadSaveClass
                obj.time            = HDF5_DatasetLoad(gid, 'time');
                obj.timeOffset      = HDF5_DatasetLoad(gid, 'timeOffset');

                err = obj.ErrorCheck();
                
                % Close group
                HDF5_GroupClose(fileobj, gid, fid);
                
            catch
                err = -2;
                
                if gid.double > 0
                    % If optional aux field exists BUT is in some way invalid it raises error < 0
                    err = -6;
                else
                    err = 1;
                end
                
            end
        end

@@ -103,6 +120,10 @@ classdef AuxClass < FileLoadSaveClass
                H5F.close(fid);
            end     
            
            if obj.debuglevel.Get() == obj.debuglevel.SimulateBadData()
                obj.SimulateBadData();
            end
            
            hdf5write_safe(fileobj, [location, '/name'], obj.name);
            hdf5write_safe(fileobj, [location, '/dataTimeSeries'], obj.dataTimeSeries);
            hdf5write_safe(fileobj, [location, '/time'], obj.time);
@@ -182,6 +203,55 @@ classdef AuxClass < FileLoadSaveClass
        end
        
        
        % ----------------------------------------------------------------------------------
        function b = IsEmpty(obj)
            b = true;
            if isempty(obj)
                return
            end
            if isempty(obj.name)
                return
            end
            if isempty(obj.dataTimeSeries)
                return
            end
            if isempty(obj.time)
                return
            end
            if length(obj.dataTimeSeries) ~= length(obj.time)
                return
            end
            b = false;
        end
        
        
        % ----------------------------------------------------------------------------------
        function err = ErrorCheck(obj)
            err = 0;
            if isempty(obj.name)
                err = -1;
                return
            end
            if isempty(obj.dataTimeSeries)
                err = -2;
                return
            end
            if isempty(obj.time)
                err = -3;
                return
            end
            if length(obj.dataTimeSeries) ~= length(obj.time)
                err = -4;
                return
            end
        end
        
        
        % ----------------------------------------------------------------------------------
        function SimulateBadData(obj)
            obj.dataTimeSeries(end,:) = [];
        end
        
    end
    
end
+91 −45
Original line number Diff line number Diff line
@@ -143,10 +143,16 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                    end
                    dotnirs = varargin{1};
                    obj.GenSimulatedTimeBases(dotnirs, tfactors);
                    
                    % Required fields
                    for ii=1:length(tfactors)
                        obj.data(ii) = DataClass(obj.nirs_tb(ii).d, obj.nirs_tb(ii).t(:), obj.nirs_tb(ii).SD.MeasList);
                    end
                    obj.probe      = ProbeClass(dotnirs.SD);
                    
                    
                    % Optional fields
                    if isfield(dotnirs,'s')
                        for ii=1:size(dotnirs.s,2)
                            if isfield(dotnirs, 'CondNames')
                                obj.stim(ii) = StimClass(dotnirs.s(:,ii), dotnirs.t(:), dotnirs.CondNames{ii});
@@ -154,14 +160,18 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                                obj.stim(ii) = StimClass(dotnirs.s(:,ii), dotnirs.t(:), num2str(ii));
                            end
                        end
                    obj.probe      = ProbeClass(dotnirs.SD);
                    end
                    if isfield(dotnirs,'aux')
                        for ii=1:size(dotnirs.aux,2)
                            obj.aux(ii) = AuxClass(dotnirs.aux(:,ii), dotnirs.t(:), sprintf('aux%d',ii));
                        end
                    end
                    
                    % Add metadatatags
                    % Add required field metadatatags that has no .nirs
                    % equivalent 
                    obj.metaDataTags   = MetaDataTagsClass();

                    
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                % obj = SnirfClass(data, stim);
                % obj = SnirfClass(data, stim, probe);
@@ -244,7 +254,17 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
            obj.probe          = ProbeClass().empty();
            obj.aux            = AuxClass().empty();
            
            % Initialize non-SNIRF variables
            obj.stim0          = StimClass().empty();            
            obj.errmsgs = {
                'MATLAB could not load the file.'
                '''formatVersion'' is invalid.'
                '''metaDataTags'' is invalid.'
                '''data'' is invalid.'
                '''stim'' is invalid and could not be loaded'
                '''probe'' is invalid.'
                '''aux'' is invalid and could not be loaded'
                };
        end
        
               
@@ -397,12 +417,10 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                if ii > length(obj.stim)
                    obj.stim(ii) = StimClass;
                end
                if obj.stim(ii).LoadHdf5(fileobj, [obj.location, '/stim', num2str(ii)]) < 0
                err = obj.stim(ii).LoadHdf5(fileobj, [obj.location, '/stim', num2str(ii)]);
                if err ~= 0
                    obj.stim(ii).delete();
                    obj.stim(ii) = [];
                    if ii==1
                        err = 1;  % Absence of optional field raises error > 0
                    end
                    break;
                end
                ii=ii+1;
@@ -415,7 +433,7 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                if ii > length(obj.stim0)
                    obj.stim0(ii) = StimClass;
                end
                if obj.stim0(ii).LoadHdf5(fileobj, [obj.location, '/stim0', num2str(ii)]) < 0
                if obj.stim0(ii).LoadHdf5(fileobj, [obj.location, '/stim0', num2str(ii)]) ~= 0
                    obj.stim0(ii).delete();
                    obj.stim0(ii) = [];
                    break;
@@ -443,12 +461,10 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                if ii > length(obj.aux)
                    obj.aux(ii) = AuxClass;
                end
                if obj.aux(ii).LoadHdf5(fileobj, [obj.location, '/aux', num2str(ii)]) < 0
                err = obj.aux(ii).LoadHdf5(fileobj, [obj.location, '/aux', num2str(ii)]);
                if err ~= 0
                    obj.aux(ii).delete();
                    obj.aux(ii) = [];
                    if ii==1
                        err = 1;  % Error code for no optional field is > 0
                    end
                    break;
                end
                ii=ii+1;
@@ -479,6 +495,7 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
            
            % Don't reload if not empty
            if ~obj.IsEmpty()
                err = obj.GetError();     % preserve error state if exiting early
                return;
            end
            
@@ -491,38 +508,53 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                [obj.gid, obj.fid] = HDF5_GroupOpen(fileobj, '/');
                
                
                if obj.SetLocation() < 0 & err == 0
                if obj.SetLocation() < 0 && err == 0
                    err = -1;
                end
                
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                % NOTE: Optional fields have positive error codes if they are
                % missing, but negative error codes if they're not missing but 
                % invalid
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                
                %%%% Load formatVersion
                if obj.LoadFormatVersion() < 0 & err == 0
                if obj.LoadFormatVersion() < 0 && err == 0
                    err = -2;
                end

                %%%% Load metaDataTags
                if obj.LoadMetaDataTags(obj.fid) < 0 & err == 0
                    err = -3;
                if obj.LoadMetaDataTags(obj.fid) < 0 && err == 0
                    % Here a positive return value means that invalid data meta tags 
                    % should NOT be a show stopper if we can help it, if the reste of the data 
                    % is valid. So just let user know they're invalid with a warning.
                    err = 3;
                end

                %%%% Load data
                if obj.LoadData(obj.fid) < 0 & err == 0
                if obj.LoadData(obj.fid) < 0 && err == 0
                    err = -4;
                end

                %%%% Load stim
                if obj.LoadStim(obj.fid) < 0 & err == 0
                    err = -5;
                if obj.LoadStim(obj.fid) < 0 && err == 0
                    % Optional field: even if invalid we still want to be
                    % able to work with the rest of the data. Only log
                    % warning
                    err = 5;
                end

                %%%% Load probe
                if obj.LoadProbe(obj.fid) < 0 & err == 0
                if obj.LoadProbe(obj.fid) < 0 && err == 0
                    err = -6;
                end

                %%%% Load aux. This is an optional field
                if obj.LoadAux(obj.fid) < 0 & err == 0
                    err = -7;
                if obj.LoadAux(obj.fid) < 0 && err == 0
                    % Optional field: even if invalid we still want to be
                    % able to work with the rest of the data. Only log
                    % warning
                    err = 7;
                end
                
                % Close group
@@ -795,6 +827,7 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
            end
            B = true;
        end
        
    end
    
    
@@ -936,6 +969,19 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
        end
        
        
        % ---------------------------------------------------------
        function t = GetAuxiliaryTime(obj)
            t = [];
            if isempty(obj.aux)
                return;
            end
            if obj.aux(1).IsEmpty()
                return;
            end
            t = obj.aux(1).GetTime();
        end
        
        
        % ---------------------------------------------------------
        function ml = GetMeasList(obj, iBlk)
            ml = [];
+57 −11

File changed.

Preview size limit exceeded, changes collapsed.

Loading