Unverified Commit 37473104 authored by jayd1860's avatar jayd1860 Committed by GitHub
Browse files

Add feature to export and load stim to and from BIDS events TSV files. (#147)

* v1.55.0

-- Change to stimulus loading to load from events .tsv file if it is present. It would override SNIRF stim field and StimEditGUI. If not present then default to SNIRF stim and StimEditGUI for stim loading and editing.

* v1.55.1

-- Fix some bugs with stim export to events TSV files.
-- Add documentation to DesignNotes.ppt describing the new stim loading (from events TSV files) and exporting (to events TSV files) feature.
-- Add option to Snirf2Tsv.m to find and delete all events TSV files

* v1.56.0

-- Fix issue in Snirf.GetDataTimeSeriesMatrix() with incorrect conversion to aux matrix when aux sample rate is different than data sample rate.
-- Move events TSV loading menu option from StimEditGUI.fig to MainGUI.fig per Davis's request
-- Fix bug in moving old output derived data folder homerOutput to new BIDS style derivative/homer.

* v1.56.1

-- One more bug fix in SnirfClass.GetAuxDataMatrix() where the case of if aux has same time base as data is not being handles properly.
-- Added config option "Quiet Mode" to be able to run processing without progress bars being displayed so it doesn't constantly take control of the mouse and one could work in parallel in other windows without interference.

* v1.56.2

-- Added automatic stim loading from TSV file (if changes were made or new file created) when clicking on run. 
-- Change to SnirfFile2Tsv.m: if snirf stim field is empty then generate empty TSV file with only the default column names instead not generating any file at all as was done before.
-- Change the way we detect if stim changes were made to acquisition file.
-- Minor change to readTsv.m to automatically complete input file name without an extention with a '.tsv' extension.
-- Add DeleteTsv.m for diagnostic purposes.

* v1.57.0

-- Make config changes made through Settings-->Edit App Configuration menu take effect immediately after saving so that one does not have to restart Homer3.

-- Fix bug loading stims: Change order at init time when DataTreeClass.SetConditions() is called; before or after loading groupResult.mat. This order was changed several months ago (I believe in version 1.38.2) to be SetConditions then load groupResults. The problem with this is that it groupResults then overwrites the correct condition initialization for the whole group if groupResults happened to have unsaved condition changes that differ from SetConditions. This may be inconsistent with the correct condition set generated by SetConditions. One solution could be that TreeNode.Copy() groupResults method SHOULD NOT BE copying conditions - i.e, TreeNode.CondNames field. The other solution - the one used here - is change the order back to calling SetConditions() after copying groupResults. Although I have to try to remember why that change to caLL SetConditions first was made in the first place IN v1.38.2. It seems like a mistaken solution to address some sort of issue which the commit message does not explain.

-- Clean up: delete unused and obsolete files.

* v1.58.0

-- Add back showing pruned channels in PlotProbeGUI. Also there's a new ability to display non-HRF time course in PlotProbeGUI. If user is displaying non-HRF data then a warning pops up informing about this. You can opt out of receiving with every element selection.

-- Standardized datatype names in methods GetDataTimeSeries() and GetMeasurementList(), across different levels in DataTreeClass, to be better able to use these methods in scripts.

-- Made small change to hmrR_PreprocessIntensity_Negative per Meryem's request.

-- Fix display issue in manually pruning channels with it not always being displayed correctly with a dotted line in the probe axes. This is because of lack of clarity with currently selected wavelength with only the currently selected wavelength measurement being toggled. To make things simple when selecting a channel for pruning measurements from ALL of the wavelengths are toggled simultaneously. Also to clarify, the meaning of manual pruning is always to the raw/OD timecourse data even when it is NOT selected in the Plot Select Data

-- Fix small issue in syncSubmodules when copying peerless files in non-existent folders in the destination the folder needs to be created first (that is, copyfile won't do it for you).
parent 9cce6bff
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -10,5 +10,7 @@ don't ask again
% Export HRF Mean Output Style # all child processing elements in one file, one processing element per file
one processing element per file

% Load Stim From TSV File # Yes, No
Yes

% END
+112 −0
Original line number Diff line number Diff line
classdef AcqDataClass < matlab.mixin.Copyable
       
    properties (Access = public)
        bids
    end
    properties (Access = private)
        logger
    end
@@ -76,6 +79,24 @@ classdef AcqDataClass < matlab.mixin.Copyable
    
    methods
        
        % -------------------------------------------------------
        function obj = AcqDataClass(fileobj)
            if nargin == 0
                return
            end
            if iscell(fileobj)
                if ~isempty(fileobj)
                    fileobj = fileobj{1};
                end
            end
            if ~ischar(fileobj)
                fileobj = '';
            end
            obj.LoadBids(fileobj);           
        end


        
        % -------------------------------------------------------
        function Initialize(obj)
            global logger
@@ -83,12 +104,14 @@ classdef AcqDataClass < matlab.mixin.Copyable
        end
        
        
        
        % -------------------------------------------------------
        function err = Error(obj)
            err = obj.GetError();
        end
        
        
        
        % ---------------------------------------------------------
        function msg = GetErrorMsg(obj)
            msg = '';
@@ -106,6 +129,77 @@ classdef AcqDataClass < matlab.mixin.Copyable
        end
        
        
        
        % -------------------------------------------------------
        function err = LoadBids(obj, fileobj)
            err = obj.LoadStimOverride(fileobj);
        end
        

        
        % -------------------------------------------------------
        function status = LoadStimOverride(obj, fileobj)
            global cfg
            status = false;
            cfg = InitConfig(cfg);
            if strcmpi(cfg.GetValue('Load Stim From TSV File'), 'no')
                return
            end
            obj.bids = struct('stim',{{}});            
            if isempty(fileobj)
                return
            end
            [p,f] = fileparts(fileobj);
            if isempty(p)
                p = filesepStandard(pwd);
            end
            k = strfind(f, '_nirs');
            if isempty(k)
                k = length(f)+1;
            end
            fnameTsv = [filesepStandard(p), f(1:k-1), '_events.tsv'];
            file = mydir(fnameTsv);
            if isempty(file)
                return
            end
            obj.bids.stim = readTsv([filesepStandard(p), file(1).name],'numstr2num');
            if isempty(obj.bids.stim)
                return
            end
            s = TsvFile2Snirf(obj.bids.stim);
            obj.stim = s.stim.copy();
            status = true;
        end
        
        
        
        % -------------------------------------------------------
        function err = ReloadStim(obj, fileobj)
            err = 0;
            obj.bids = struct('stim',{{}});            
            if isempty(fileobj)
                return
            end
            [p,f] = fileparts(fileobj);
            if isempty(p)
                p = filesepStandard(pwd);
            end
            k = strfind(f, '_nirs');
            if isempty(k)
                k = length(f)+1;
            end
            fnameTsv = [filesepStandard(p), f(1:k-1), '_events.tsv'];
            file = mydir(fnameTsv);
            if isempty(file)
                return
            end
            obj.bids.stim = readTsv([filesepStandard(p), file(1).name],'numstr2num');
            s = TsvFile2Snirf(obj.bids.stim);
            obj.stim = s.stim.copy();
        end
        
        
        
        % -------------------------------------------------------
        function FreeMemory(obj, filename)
            if ~exist('filename','var')
@@ -118,6 +212,7 @@ classdef AcqDataClass < matlab.mixin.Copyable
        end
        
        
        
        % ---------------------------------------------------------
        function bbox = GetSdgBbox(obj)
            bbox = [];
@@ -150,6 +245,7 @@ classdef AcqDataClass < matlab.mixin.Copyable
        end
        
        
        
        % ----------------------------------------------------------------------------------
        function varval = GetVar(obj, varname)
            if ismethod(obj,['Get_', varname])
@@ -179,6 +275,7 @@ classdef AcqDataClass < matlab.mixin.Copyable
        end
        
        
        
        % ----------------------------------------------------------------------------------
        function t = GetTimeCombined(obj)
            % Function combines the time vectors for all data blocks into one time vectors. 
@@ -204,6 +301,7 @@ classdef AcqDataClass < matlab.mixin.Copyable
        end
        
                
                
        % ----------------------------------------------------------------------------------
        function data = GetStimData(~, ~)
            data = [];
@@ -250,6 +348,20 @@ classdef AcqDataClass < matlab.mixin.Copyable
        
        
        
        % ----------------------------------------------------------------------------------
        function fnameTsv = GetStimTsvFilename(obj)
            fnameTsv = '';
            [pname, fname] = fileparts(obj.GetFilename());
            k = strfind(fname, '_nirs');
            if isempty(k)
                k = length(fname)+1;
            end
            if isempty(fname)
                return;
            end
            fnameTsv = [filesepStandard(pname), fname(1:k-1), '_events.tsv'];
        end
                
    end
    
end
+1 −0
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ for ii = 1:length(datafiles)
    if strcmp(options, 'delete')
        fprintf('Deleting %s\n', [datafiles(ii).rootdir, '/', datafiles(ii).name]);
        delete([datafiles(ii).rootdir, '/', datafiles(ii).name]);
        delete([datafiles(ii).rootdir, '/*_events.tsv']);
    elseif strcmp(options, 'move')
        fprintf('Moving %s to %s\n', [datafiles(ii).rootdir, '/', datafiles(ii).name], [datafiles(ii).rootdir, '/', datafiles(ii).name, '.old']);
        movefile([datafiles(ii).rootdir, '/', datafiles(ii).name], [datafiles(ii).rootdir, '/', datafiles(ii).name, '.old']);
+7 −0
Original line number Diff line number Diff line
function DeleteTsv(rootdir)
if ~exist('rootdir','var')
    rootdir = pwd;
end
rootdir = filesepStandard(rootdir);
Snirf2Tsv(rootdir, 'remove');
+36 −0
Original line number Diff line number Diff line
function Snirf2Tsv(rootdir, options)
if ~exist('rootdir','var')
    rootdir = pwd;
end
if ~exist('options','var')
    options = pwd;
end
rootdir = filesepStandard(rootdir);
files = DataFilesClass(rootdir, 'snirf');
files = files.files;
for ii = 1:length(files)
    if files(ii).IsDir()
        continue
    end
    fname = filesepStandard([files(ii).rootdir, files(ii).name]);
    [pname, fname, ext1] = fileparts(fname);
    k = strfind(fname, '_nirs');
    if isempty(k)
        k = length(fname)+1;
    end
    fnameNew = [filesepStandard(pname), fname(1:k-1), '_events.tsv'];
    src = [filesepStandard(pname), fname, ext1];
    dst = fnameNew;
    if optionExists(options, 'delete') || optionExists(options, 'remove')
        if ispathvalid(dst)
            fprintf('Deleting  %s\n', dst);
            delete(dst);
        end
    else
        fprintf('Converting   %s   to   %s\n', src, dst);
        SnirfFile2Tsv(src, dst, 'removeStim');
    end
end


Loading