function tutorial_visual_single(bids_dir, reports_dir)
% TUTORIAL_VISUAL_SINGLE: Runs the Brainstorm/SPM group analysis pipeline (single subject, BIDS version).
%
% ONLINE TUTORIALS: https://neuroimage.usc.edu/brainstorm/Tutorials/VisualSingle
%
% INPUTS:
%    - bids_dir: Path to folder ds000117 (https://openneuro.org/datasets/ds000117)
%       |- derivatives/freesurfer/sub-XX                               : Segmentation folders generated with FreeSurfer
%       |- derivatives/meg_derivatives/sub-XX/ses-meg/meg/*.fif        : MEG+EEG recordings (processed with MaxFilter's SSS)
%       |- derivatives/meg_derivatives/sub-emptyroom/ses-meg/meg/*.fif : Empty room measurements
%    - reports_dir: If defined, exports all the reports as HTML to this folder

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Author: Francois Tadel, Elizabeth Bock, 2016-2018

%% ===== SCRIPT VARIABLES =====
% Full list of subjects to process
SubjectNames = {'sub-01', 'sub-02', 'sub-03', 'sub-04', 'sub-05', 'sub-06', 'sub-07', 'sub-08', ...
                'sub-09', 'sub-10', 'sub-11', 'sub-12', 'sub-13', 'sub-14', 'sub-15', 'sub-16'};
% Empty-room dates for each subject (so that we can match automatically recordings with empty-room)
EmptyRoomSubj = 'sub-emptyroom';
AcquisitionDates = {'09-Apr-2009', '06-May-2009', '11-May-2009', '18-May-2009', '15-May-2009', '15-May-2009', '15-May-2009', '15-May-2009', ...
                    '15-May-2009', '15-May-2009', '01-Jun-2009', '01-Jun-2009', '01-Jun-2009', '26-Nov-2009', '08-Dec-2009', '08-Dec-2009'};
% Bad channels {iSubj} = {Run01, Run02, Run03, Run04, Run05, Run06}
BadChannels{1}  = {'EEG016', 'EEG070', 'EEG050',{'EEG008','EEG050'}, [], []};
BadChannels{2}  = {{'EEG027', 'EEG030', 'EEG038'}, 'EEG010', 'EEG010', 'EEG010', 'EEG010', 'EEG010'};
BadChannels{3}  = {{'EEG008','EEG017'}, {'EEG008','EEG017'}, {'EEG008','EEG017'}, {'EEG008','EEG017'}, {'EEG008','EEG017','EEG001'}, {'EEG008','EEG017','EEG020'}};
BadChannels{4}  = {{'EEG038'}, {'EEG038','EEG001','EEG016'}, {'EEG038','EEG001','EEG016'}, {'EEG038','EEG001'}, {'EEG038','EEG001','EEG016'}, {'EEG038','EEG001','EEG016'}};
BadChannels{5}  = {'EEG001', 'EEG001', [], [], [], []};
BadChannels{6}  = {'EEG068', [], 'EEG004', [], [], []};
BadChannels{7}  = {[], [], {'EEG004','EEG008'}, {'EEG004','EEG008'},{'EEG004','EEG008','EEG043','EEG045','EEG047'}, {'EEG004','EEG008'}};
BadChannels{8}  = {[], [], [], [], [], []};
BadChannels{9}  = {[], 'EEG004', 'EEG004', [], 'EEG004', 'EEG004'};
BadChannels{10} = {[], [], [], [], [], []};
BadChannels{11} = {{'EEG010','EEG050'}, 'EEG050', 'EEG050', 'EEG050', 'EEG050', 'EEG050'};
BadChannels{12} = {{'EEG024','EEG057'}, {'EEG024','EEG057'}, {'EEG024','EEG057'}, {'EEG024','EEG057','EEG070'}, {'EEG024','EEG057'}, {'EEG024','EEG057','EEG070'}};
BadChannels{13} = {'EEG009', 'EEG009', {'EEG009','EEG057','EEG69'}, 'EEG009', {'EEG009','EEG044'}, {'EEG009','EEG044'}};
BadChannels{14} = {'EEG029', 'EEG029', 'EEG029', {'EEG004','EEG008','EEG016','EEG029'}, {'EEG004','EEG008','EEG016','EEG029'}, {'EEG004','EEG008','EEG016','EEG029'}};
BadChannels{15} = {'EEG038', 'EEG038', 'EEG038', 'EEG038', {'EEG054','EEG038'}, 'EEG038'};
BadChannels{16} = {'EEG008', 'EEG008', 'EEG008', 'EEG008', 'EEG008', 'EEG008'};
% SSP components to remove {iSubj} = {sRun01, sRun02, sRun03, sRun03, sRun04, sRun05, sRun06},   sRun0X={ECG_GRAD,ECG_MAG}
SspSelect{1}  = {{1,1},         {1,1},   {1,1},   {1,1},   {1,1},   {1,1}};
SspSelect{2}  = {{1,1},         {1,1},   {1,1},   {1,1},   {3,1},   {1,1}};
SspSelect{3}  = {{[],1},        {1,1},   {1,1},   {1,1},   {1,1},   {1,1}};
SspSelect{4}  = {{[],1},        {[],1},  {[],1},  {[],1},  {[],1},  {[],1}};
SspSelect{5}  = {{2,1},         {1,1},   {1,1},   {[],1},  {1,1},   {1,1}};
SspSelect{6}  = {{2,1},         {2,1},   {1,1},   {2,1},   {1,1},   {2,1}};
SspSelect{7}  = {{1,1},         {1,1},   {1,1},   {1,1},   {1,1},   {1,1}};
SspSelect{8}  = {{1,1},         {1,1},   {[],1},  {2,1},   {1,1},   {2,1}};
SspSelect{9}  = {{1,1},         {1,1},   {[],1},  {[],1},  {1,1},   {1,1}};
SspSelect{10} = {{1,1},         {1,1},   {1,1},   {1,1},   {1,1},   {1,1}};
SspSelect{11} = {{[],1},        {[],1},  {[],1},  {[],1},  {[],[]}, {[],[]}};
SspSelect{12} = {{[1,2],[1,2]}, {1,1},   {1,1},   {1,1},   {1,1},   {1,1}};
SspSelect{13} = {{[],[]},       {[],[]}, {[],[]}, {[],[]}, {[],[]}, {[],[]}};
SspSelect{14} = {{1,1},         {1,1},   {1,1},   {1,1},   {1,1},   {1,1}};
SspSelect{15} = {{1,1},         {1,1},   {1,1},   {1,1},   {1,1},   {1,1}};
SspSelect{16} = {{1,1},         {1,1},   {1,1},   {2,1},   {1,1},   {1,1}};

%% ===== CREATE PROTOCOL =====
% Start brainstorm without the GUI
if ~brainstorm('status')
    brainstorm nogui
end
% Output folder for reports
if (nargin < 2) || isempty(reports_dir) || ~isdir(reports_dir)
    reports_dir = [];
end
% You have to specify the folder in which the tutorial dataset is unzipped
if (nargin < 1) || isempty(bids_dir) || ~file_exist(bids_dir) || ~file_exist(bst_fullfile(bids_dir, 'derivatives')) || ~file_exist(bst_fullfile(bids_dir, 'dataset_description.json'))
    error('The first argument must be the full path to the tutorial folder.');
end
% The protocol name has to be a valid folder name (no spaces, no weird characters...)
ProtocolName = 'TutorialVisual';
% Delete existing protocol
gui_brainstorm('DeleteProtocol', ProtocolName);
% Create new protocol
gui_brainstorm('CreateProtocol', ProtocolName, 0, 0);
% Set visualization filters: 40Hz low-pass, no high-pass
panel_filter('SetFilters', 1, 40, 0, [], 0, [], 0, 0);
% Set colormap: local color scale
bst_colormaps('SetMaxMode', 'meg', 'local');
bst_colormaps('SetMaxMode', 'eeg', 'local');

%% ===== PRE-PROCESS AND IMPORT =====
for iSubj = 1:16
    % Start a new report (one report per subject)
    bst_report('Start');
    disp(sprintf('\n===== IMPORT: SUBJECT #%d =====\n', iSubj));

    % If subject already exists: delete it
    [sSubject, iSubject] = bst_get('Subject', SubjectNames{iSubj});
    if ~isempty(sSubject)
        db_delete_subjects(iSubject);
    end

    % ===== FILES TO IMPORT =====
    % Build the path of the files to import
    AnatDir    = fullfile(bids_dir, 'derivatives', 'freesurfer', SubjectNames{iSubj}, 'ses-mri', 'anat');
    DataDir    = fullfile(bids_dir, 'derivatives',  'meg_derivatives', SubjectNames{iSubj}, 'ses-meg', 'meg');
    % Check if the folder contains the required files
    if ~file_exist(AnatDir)
        error(['The folder "' AnatDir '" does not exist.']);
    end
    if ~file_exist(DataDir)
        error(['The folder "' DataDir '" does not exist.']);
    end

    % ===== ANATOMY =====
    % Process: Import anatomy folder
    bst_process('CallProcess', 'process_import_anatomy', [], [], ...
        'subjectname', SubjectNames{iSubj}, ...
        'mrifile',     {AnatDir, 'FreeSurfer'}, ...
        'nvertices',   15000);

    % ===== PROCESS EACH RUN =====
    for iRun = 1:6
        % Files to import
        FifFile = bst_fullfile(DataDir, sprintf('%s_ses-meg_task-facerecognition_run-%02d_proc-sss_meg.fif', SubjectNames{iSubj}, iRun));

        % ===== LINK CONTINUOUS FILE =====
        % Process: Create link to raw file
        sFileRaw = bst_process('CallProcess', 'process_import_data_raw', [], [], ...
            'subjectname',    SubjectNames{iSubj}, ...
            'datafile',       {FifFile, 'FIF'}, ...
            'channelreplace', 1, ...
            'channelalign',   0);
        % Set acquisition date
        panel_record('SetAcquisitionDate', sFileRaw.iStudy, AcquisitionDates{iSubj});

        % ===== PREPARE CHANNEL FILE =====
        % Process: Set channels type
        bst_process('CallProcess', 'process_channel_settype', sFileRaw, [], ...
            'sensortypes', 'EEG061, EEG064', ...
            'newtype',     'NOSIG');
        bst_process('CallProcess', 'process_channel_settype', sFileRaw, [], ...
            'sensortypes', 'EEG062', ...
            'newtype',     'EOG');
        bst_process('CallProcess', 'process_channel_settype', sFileRaw, [], ...
            'sensortypes', 'EEG063', ...
            'newtype',     'ECG');

        % Process: Remove head points
        sFileRaw = bst_process('CallProcess', 'process_headpoints_remove', sFileRaw, [], ...
            'zlimit', 0);
        % Process: Refine registration
        sFileRaw = bst_process('CallProcess', 'process_headpoints_refine', sFileRaw, []);
        % Process: Project electrodes on scalp
        sFileRaw = bst_process('CallProcess', 'process_channel_project', sFileRaw, []);

        % Process: Snapshot: Sensors/MRI registration
        bst_process('CallProcess', 'process_snapshot', sFileRaw, [], ...
            'target',   1, ...  % Sensors/MRI registration
            'modality', 1, ...  % MEG (All)
            'orient',   1, ...  % left
            'Comment',  sprintf('MEG/MRI Registration: Subject #%d, Run #%d', iSubj, iRun));
        bst_process('CallProcess', 'process_snapshot', sFileRaw, [], ...
            'target',   1, ...  % Sensors/MRI registration
            'modality', 4, ...  % EEG
            'orient',   1, ...  % left
            'Comment',  sprintf('EEG/MRI Registration: Subject #%d, Run #%d', iSubj, iRun));

        % ===== IMPORT TRIGGERS =====
        % Process: Read from channel
        bst_process('CallProcess', 'process_evt_read', sFileRaw, [], ...
            'stimchan',  'STI101', ...
            'trackmode', 2, ...  % Bit: detect the changes for each bit independently
            'zero',      0);
        % Process: Group by name
        bst_process('CallProcess', 'process_evt_groupname', sFileRaw, [], ...
            'combine', 'Unfamiliar=3,4', ...
            'dt',      0, ...
            'delete',  1);
        % Process: Rename event
        bst_process('CallProcess', 'process_evt_rename', sFileRaw, [], ...
            'src',  '3', ...
            'dest', 'Famous');
        % Process: Rename event
        bst_process('CallProcess', 'process_evt_rename', sFileRaw, [], ...
            'src',  '5', ...
            'dest', 'Scrambled');
        % Process: Add time offset
        bst_process('CallProcess', 'process_evt_timeoffset', sFileRaw, [], ...
            'info',      [], ...
            'eventname', 'Famous, Unfamiliar, Scrambled', ...
            'offset',    0.0345);
        % Process: Delete events
        bst_process('CallProcess', 'process_evt_delete', sFileRaw, [], ...
            'eventname', '1,2,6,7,8,9,10,11,12,13,14,15,16');
        % Process: Detect cHPI activity (Elekta):STI201
        bst_process('CallProcess', 'process_evt_detect_chpi', sFileRaw, [], ...
            'eventname',   'chpi_bad', ...
            'channelname', 'STI201', ...
            'method',      'off');  % Mark as bad when the HPI coils are OFF

        % ===== FREQUENCY FILTERS =====
        % Process: Notch filter: 50Hz 100Hz 150Hz 200Hz
        sFileClean = bst_process('CallProcess', 'process_notch', sFileRaw, [], ...
            'freqlist',    [50, 100, 150, 200], ...
            'sensortypes', 'MEG, EEG', ...
            'read_all',    0);
        % Process: Power spectrum density (Welch)
        sFilesPsd = bst_process('CallProcess', 'process_psd', [sFileRaw, sFileClean], [], ...
            'timewindow',  [], ...
            'win_length',  4, ...
            'win_overlap', 50, ...
            'sensortypes', 'MEG, EEG', ...
            'edit', struct(...
                 'Comment',         'Power', ...
                 'TimeBands',       [], ...
                 'Freqs',           [], ...
                 'ClusterFuncTime', 'none', ...
                 'Measure',         'power', ...
                 'Output',          'all', ...
                 'SaveKernel',      0));
        % Process: Snapshot: Frequency spectrum
        bst_process('CallProcess', 'process_snapshot', sFilesPsd, [], ...
            'target',   10, ...  % Frequency spectrum
            'Comment',  sprintf('Power spctrum: Subject #%d, Run #%d', iSubj, iRun));

        % ===== BAD CHANNELS =====
        if ~isempty(BadChannels{iSubj}{iRun})
            % Process: Set bad channels
            bst_process('CallProcess', 'process_channel_setbad', sFileClean, [], ...
                'sensortypes', BadChannels{iSubj}{iRun});
        end

        % ===== EEG REFERENCE =====
        % Process: Re-reference EEG
        bst_process('CallProcess', 'process_eegref', sFileClean, [], ...
            'eegref',      'AVERAGE', ...
            'sensortypes', 'EEG');

        % ===== DETECT ARTIFACTS ======
        % Process: Detect heartbeats
        bst_process('CallProcess', 'process_evt_detect_ecg', sFileClean, [], ...
            'channelname', 'EEG063', ...
            'timewindow',  [], ...
            'eventname',   'cardiac');
        % Different amplitude thresholds for different subjects
        if strcmpi(SubjectNames{iSubj}, 'sub-05')
            thresholdMAX = 50;
        else
            thresholdMAX = 100;
        end
        % Process: Detect: blink_BAD    -   Detects all events where the amplitude exceeds 100uV
        bst_process('CallProcess', 'process_evt_detect_threshold', sFileClean, [], ...
            'eventname',    'blink_BAD', ...
            'channelname',  'EEG062', ...
            'timewindow',   [], ...
            'thresholdMAX', thresholdMAX, ...
            'units',        3, ...  % uV (10^-6)
            'bandpass',     [0.3, 20], ...
            'isAbsolute',   1, ...
            'isDCremove',   0);

        % ===== SSP COMPUTATION =====
        % Process: SSP ECG: cardiac
        bst_process('CallProcess', 'process_ssp_ecg', sFileClean, [], ...
            'eventname',   'cardiac', ...
            'sensortypes', 'MEG GRAD', ...
            'usessp',      1, ...
            'select',      SspSelect{iSubj}{iRun}{1});
        bst_process('CallProcess', 'process_ssp_ecg', sFileClean, [], ...
            'eventname',   'cardiac', ...
            'sensortypes', 'MEG MAG', ...
            'usessp',      1, ...
            'select',      SspSelect{iSubj}{iRun}{2});
        % Process: Snapshot: SSP projectors
        bst_process('CallProcess', 'process_snapshot', sFileClean, [], ...
            'target',   2, ...
            'Comment',  sprintf('Subject #%d, Run #%d', iSubj, iRun));   % SSP projectors

        % ===== IMPORT BAD EVENTS =====
        % Get bad segments: this is typically done manually, not from a script
        BadSegments = GetBadSegments(iSubj, iRun);
        % Process: Import from file
        bst_process('CallProcess', 'process_evt_import', sFileClean, [], ...
            'evtfile', {BadSegments, 'ARRAY-TIMES'}, ...
            'evtname', 'BAD');

        % ===== IMPORT TRIALS =====
        % Process: Import MEG/EEG: Events
        sFilesEpochs = bst_process('CallProcess', 'process_import_data_event', sFileClean, [], ...
            'subjectname', SubjectNames{iSubj}, ...
            'condition',   '', ...
            'eventname',   'Famous, Scrambled, Unfamiliar', ...
            'timewindow',  [], ...
            'epochtime',   [-0.5, 1.2], ...
            'createcond',  0, ...
            'ignoreshort', 1, ...
            'usectfcomp',  1, ...
            'usessp',      1, ...
            'freq',        [], ...
            'baseline',    [-0.5, -0.0009]);

        % ===== AVERAGE: RUN =====
        % Process: Average: By trial group (folder average)
        sFilesAvg = bst_process('CallProcess', 'process_average', sFilesEpochs, [], ...
            'avgtype',    5, ...  % By trial group (folder average)
            'avg_func',   1, ...  % Arithmetic average:  mean(x)
            'weighted',   0, ...
            'keepevents', 0);
        % Process: Snapshot: Recordings time series
        bst_process('CallProcess', 'process_snapshot', sFilesAvg, [], ...
            'target',   5, ...  % Recordings time series
            'modality', 4, ...  % EEG
            'time',     0.11, ...
            'Comment',  sprintf('Subject #%d, Run #%d', iSubj, iRun));
        % Process: Snapshot: Recordings topography
        bst_process('CallProcess', 'process_snapshot', sFilesAvg, [], ...
            'target',   6, ...  % Recordings topography (one time)
            'modality', 4, ...  % EEG
            'time',     0.11, ...
            'Comment',  sprintf('Subject #%d, Run #%d', iSubj, iRun));

        % ===== COMPUTE NOISECOV: EEG =====
        % Process: Compute covariance (noise or data)
        bst_process('CallProcess', 'process_noisecov', sFilesEpochs, [], ...
            'baseline',       [-0.5, -0.0009], ...
            'sensortypes',    'EEG', ...
            'target',         1, ...  % Noise covariance     (covariance over baseline time window)
            'dcoffset',       1, ...  % Block by block, to avoid effects of slow shifts in data
            'identity',       0, ...
            'copycond',       0, ...
            'copysubj',       0, ...
            'replacefile',    1);  % Replace
    end

    % Save report
    ReportFile = bst_report('Save', []);
    if ~isempty(reports_dir) && ~isempty(ReportFile)
        bst_report('Export', ReportFile, bst_fullfile(reports_dir, ['report_' ProtocolName '_' SubjectNames{iSubj} '.html']));
    end
end

%% ===== EMPTY ROOM RECORDINGS =====
disp(sprintf('\n===== IMPORT: EMPTY-ROOM =====\n'));
% Loop on all the noise sessions
NoiseFiles = {};
for ses = {'20090409', '20090506', '20090511', '20090515', '20090518', '20090601', '20091126', '20091208'}
    NoiseFiles{end+1} = fullfile(bids_dir, 'derivatives', 'meg_derivatives', EmptyRoomSubj, ['ses-' ses{1}], 'meg', ['sub-emptyroom_ses-' ses{1} '_task-noise_proc-sss_meg.fif']);
end
% Process: Create link to raw file
sFilesNoise = bst_process('CallProcess', 'process_import_data_raw', [], [], ...
    'subjectname',    EmptyRoomSubj, ...
    'datafile',       {NoiseFiles, 'FIF'}, ...
    'channelreplace', 1, ...
    'channelalign',   0);
% Process: Notch filter: 50Hz 100Hz 150Hz 200Hz
sFileNoiseClean = bst_process('CallProcess', 'process_notch', sFilesNoise, [], ...
        'freqlist',    [50, 100, 150, 200], ...
        'sensortypes', 'MEG, EEG', ...
        'read_all',    0);
% Process: Compute noise covariance
bst_process('CallProcess', 'process_noisecov', sFileNoiseClean, [], ...
    'baseline',    [], ...
    'sensortypes', 'MEG', ...
    'target',      1, ...  % Noise covariance     (covariance over baseline time window)
    'dcoffset',    1, ...  % Block by block, to avoid effects of slow shifts in data
    'identity',    0, ...
    'copycond',    1, ...
    'copysubj',    1, ...
    'copymatch',   1, ...
    'replacefile', 2);  % Merge

%% ===== SOURCE ESTIMATION =====
% Start a new report (one report for the source estimation of all the subjects)
bst_report('Start');
% Loop on the subjects: This loop is separated from the previous one, because we should
% compute the BEM surfaces after importing all the runs, so that the registration is done
% using the high resolution head surface, instead of the smooth scalp BEM layer.
for iSubj = 1:length(SubjectNames)
    disp(sprintf('\n===== SOURCES: SUBJECT #%d =====\n', iSubj));

    % ===== BEM SURFACES =====
    % Process: Generate BEM surfaces
    bst_process('CallProcess', 'process_generate_bem', [], [], ...
        'subjectname', SubjectNames{iSubj}, ...
        'nscalp',      1082, ...
        'nouter',      642, ...
        'ninner',      642, ...
        'thickness',   4, ...
        'method',      'brainstorm');

    % ===== SELECT ALL AVERAGES =====
    % Process: Select data files in: */*
    sFilesAvg = bst_process('CallProcess', 'process_select_files_data', [], [], ...
        'subjectname',   SubjectNames{iSubj});
    % Process: Select file comments with tag: Avg
    sFilesAvg = bst_process('CallProcess', 'process_select_tag', sFilesAvg, [], ...
        'tag',    'Avg');  % Select only the files with the tag

    % ===== COMPUTE HEAD MODELS =====
    % Process: Compute head model (only for the first run of the subject)
    bst_process('CallProcess', 'process_headmodel', sFilesAvg(1), [], ...
        'sourcespace', 1, ...  % Cortex surface
        'meg',         3, ...  % Overlapping spheres
        'eeg',         3, ...  % OpenMEEG BEM
        'ecog',        1, ...  %
        'seeg',        1, ...  %
        'openmeeg',    struct(...
             'BemSelect',    [1, 1, 1], ...
             'BemCond',      [1, 0.0125, 1], ...
             'BemNames',     {{'Scalp', 'Skull', 'Brain'}}, ...
             'BemFiles',     {{}}, ...
             'isAdjoint',    0, ...
             'isAdaptative', 1, ...
             'isSplit',      0, ...
             'SplitLength',  4000));
    % Get all the runs for this subject (ie the list of the study indices)
    iStudyOther = setdiff(unique([sFilesAvg.iStudy]), sFilesAvg(1).iStudy);
    % Copy the forward model file to the other runs
    sHeadmodel = bst_get('HeadModelForStudy', sFilesAvg(1).iStudy);
    for iStudy = iStudyOther
        db_add(iStudy, sHeadmodel.FileName);
    end

    % ===== COMPUTE SOURCES: MEG =====
    % Process: Compute sources [2018]
    sAvgSrcMeg = bst_process('CallProcess', 'process_inverse_2018', sFilesAvg, [], ...
        'output',  1, ...  % Kernel only: shared
        'inverse', struct(...
             'Comment',        'MN: MEG ALL', ...
             'InverseMethod',  'minnorm', ...
             'InverseMeasure', 'amplitude', ...
             'SourceOrient',   {{'fixed'}}, ...
             'Loose',          0.2, ...
             'UseDepth',       1, ...
             'WeightExp',      0.5, ...
             'WeightLimit',    10, ...
             'NoiseMethod',    'reg', ...
             'NoiseReg',       0.1, ...
             'SnrMethod',      'fixed', ...
             'SnrRms',         1e-06, ...
             'SnrFixed',       3, ...
             'ComputeKernel',  1, ...
             'DataTypes',      {{'MEG GRAD', 'MEG MAG'}}));
    % Process: Snapshot: Sources (one time)   -   Loop only to get a correct comment for the report
    for i = 1:length(sAvgSrcMeg)
        bst_process('CallProcess', 'process_snapshot', sAvgSrcMeg(i), [], ...
            'target',         8, ...  % Sources (one time)
            'orient',         4, ...  % bottom
            'time',           0.11, ...
            'threshold',      20, ...
            'Comment',        ['MEG sources: ' sFilesAvg(i).FileName]);
    end

    % ===== COMPUTE SOURCES: EEG =====
    % Process: Compute sources [2018]
    sAvgSrcEeg = bst_process('CallProcess', 'process_inverse_2018', sFilesAvg, [], ...
        'output',  1, ...  % Kernel only: shared
        'inverse', struct(...
             'Comment',        'MN: EEG', ...
             'InverseMethod',  'minnorm', ...
             'InverseMeasure', 'amplitude', ...
             'SourceOrient',   {{'fixed'}}, ...
             'Loose',          0.2, ...
             'UseDepth',       1, ...
             'WeightExp',      0.5, ...
             'WeightLimit',    10, ...
             'NoiseMethod',    'reg', ...
             'NoiseReg',       0.1, ...
             'SnrMethod',      'fixed', ...
             'SnrRms',         1e-06, ...
             'SnrFixed',       3, ...
             'ComputeKernel',  1, ...
             'DataTypes',      {{'EEG'}}));
    % Process: Snapshot: Sources (one time)   -   Loop only to get a correct comment for the report
    for i = 1:length(sAvgSrcEeg)
        bst_process('CallProcess', 'process_snapshot', sAvgSrcEeg(i), [], ...
            'target',         8, ...  % Sources (one time)
            'orient',         4, ...  % bottom
            'time',           0.11, ...
            'threshold',      10, ...
            'Comment',        ['EEG sources: ' sFilesAvg(i).FileName]);
    end
end
% Save report
ReportFile = bst_report('Save', []);
if ~isempty(reports_dir) && ~isempty(ReportFile)
    bst_report('Export', ReportFile, bst_fullfile(reports_dir, ['report_' ProtocolName '_sources.html']));
end

%% ===== TIME-FREQUENCY =====
% Start a new report (one report for the time-frequency of all the subjects)
bst_report('Start');
% List of conditions to process separately
AllConditions = {'Famous', 'Scrambled', 'Unfamiliar'};
% Channels to display in the screen capture, by order of preference (if the first channel is bad, use the following)
SelChannel = {'EEG070','EEG060','EEG065','EEG050','EEG003'};
% Compute one separate time-frequency average for each subject/run/condition
for iSubj = 1:length(SubjectNames)
    disp(sprintf('\n===== TIME-FREQUENCY: SUBJECT #%d =====\n', iSubj));
    for iRun = 1:6
        % Process: Select data files in: Subject/Run
        sTrialsAll = bst_process('CallProcess', 'process_select_files_data', [], [], ...
            'subjectname',   SubjectNames{iSubj}, ...
            'condition',     sprintf('sub-%02d_ses-meg_task-facerecognition_run-%02d_proc-sss_meg_notch', iSubj, iRun));
        % Loop on the conditions
        for iCond = 1:length(AllConditions)
            % Comment describing this average
            strComment = [SubjectNames{iSubj}, ' / ', sprintf('run_%02d', iRun), ' / ', AllConditions{iCond}];
            disp(['BST> ' strComment]);
            % Find the first good channel in the display list
            if isempty(BadChannels{iSubj}{iRun})
                iSel = 1;
            else
                iSel = find(~ismember(SelChannel,BadChannels{iSubj}{iRun}), 1);
            end
            % Process: Select file comments with tag: Avg
            sTrialsCond = bst_process('CallProcess', 'process_select_tag', sTrialsAll, [], ...
                'tag',    [AllConditions{iCond}, '_trial'], ...
                'search', 1, ...  % Search the file names
                'select', 1);  % Select only the files with the tag
            % Process: Time-frequency (Morlet wavelets), averaged across trials
            sTimefreq = bst_process('CallProcess', 'process_timefreq', sTrialsCond, [], ...
                'sensortypes', 'MEG MAG, EEG', ...
                'edit',        struct(...
                     'Comment',         ['Avg: ' AllConditions{iCond} ', Power, 6-60Hz'], ...
                     'TimeBands',       [], ...
                     'Freqs',           [6, 6.8, 7.6, 8.6, 9.7, 11, 12.4, 14, 15.8, 17.9, 20.2, 22.8, 25.7, 29, 32.7, 37, 41.7, 47.1, 53.2, 60], ...
                     'MorletFc',        1, ...
                     'MorletFwhmTc',    3, ...
                     'ClusterFuncTime', 'none', ...
                     'Measure',         'power', ...
                     'Output',          'average', ...
                     'RemoveEvoked',    0, ...
                     'SaveKernel',      0), ...
                'normalize',   'none');  % None: Save non-standardized time-frequency maps
            % Process: Extract time: [-200ms,900ms]
            sTimefreq = bst_process('CallProcess', 'process_extract_time', sTimefreq, [], ...
                'timewindow', [-0.2, 0.9], ...
                'overwrite',  1);
            % Screen capture of one sensor
            hFigTf = view_timefreq(sTimefreq.FileName, 'SingleSensor', SelChannel{iSel});
            bst_report('Snapshot', hFigTf, strComment, 'Time-frequency', [200, 200, 400, 250]);
            close(hFigTf);
        end
    end
end
% Save report
ReportFile = bst_report('Save', []);
if ~isempty(reports_dir) && ~isempty(ReportFile)
    bst_report('Export', ReportFile, bst_fullfile(reports_dir, ['report_' ProtocolName '_timefreq.html']));
end
end

%% ===== SUPPORT FUNCTIONS =====
function BadSeg = GetBadSegments(iSubj, iRun)
     BadSegments{1} = {...
         [247.867 248.185; 598.999 598.999; 598.999 598.999; 611.999 611.999; 612.999 612.999; 613.999 613.999; 616.999 616.999; 617.999 617.999; 623.999 623.999; 715.209 715.467], ...
         [84.791 85.166], ...
         [79.183 80.167], ...
         [64.309 65.185], ...
         [90.958 91.167; 178.005 178.355; 293.282 295.919; 312.298 316.479; 353.835 357.716], ...
         [60.292 66.802; 69.975 71.210; 105.233 107.586; 108.822 109.506; 376.225 376.325]};
     BadSegments{2} = {...
         [223.806 224.199; 279.772 279.895; 453.241 455.108; 692.423 692.593], ...
         [65.298 66.194; 304.727 306.178; 399.165 400.732], ...
         [203.141 205.085; 281.579 287.883; 420.395 421.128], ...
         [387.118 388.229; 440.318 441.900; 554.825 558.744], ...
         [71.000 80.999; 82.750 87.367; 149.528 149.667; 264.747 267.995; 368.415 371.973; 376.263 378.763; 398.334 401.551; 537.410 541.645], ...
         [38.000 47.999; 47.825 50.046; 61.298 61.384; 249.653 253.379; 282.917 283.820; 286.135 287.616; 298.167 300.196; 328.254 329.511; 335.957 337.817; 478.277 480.707]};
     BadSegments{3} = {...
         [406.312 407.207; 727.055 728.714], ...
         [84.894 85.156; 152.028 152.946; 297.835 298.915; 418.272 421.845; 554.084 554.794], ...
         [73.758 74.159; 378.212 378.536; 406.065 407.099; 470.541 471.698; 488.900 491.168; 529.596 530.453], ...
         [94.874 95.152; 317.385 321.374; 325.696 327.055; 439.220 439.829; 454.473 455.175; 486.196 486.829; 518.660 522.015; 524.400 525.249; 562.417 570.325], ...
         [96.208 97.181; 98.942 99.096; 135.005 135.754; 143.990 144.599; 250.139 250.247; 300.459 300.559; 338.265 339.322; 545.913 546.067], ...
         [91.415 92.156; 284.843 286.525; 297.886 298.404; 317.046 317.163; 332.698 332.791; 358.946 359.402; 428.405 428.775; 478.374 478.690; 549.866 550.128]};
     BadSegments{4} = {...
         [22.967 22.967; 50.036 50.098; 52.058 52.058; 156.653 156.653; 171.565 173.386; 239.544 242.105; 268.162 270.175; 268.992 268.992; 316.032 316.032; 338.283 339.000; 357.959 361.909; 370.871 370.871; 381.579 383.677; 437.731 437.731; 463.482 468.505; 476.135 479.838; 486.652 488.272; 504.860 508.999], ...
         [309.493 311.707; 342.681 344.525; 354.019 357.321; 390.023 391.225; 393.926 395.855; 404.221 405.069; 432.522 435.932; 459.048 460.715; 471.763 478.529; 549.387 551.999; 591.087 594.143; 608.541 611.079; 624.847 626.615; 649.648 651.570], ...
         [57.411 58.198; 88.346 88.955; 200.761 202.335; 227.016 227.688; 257.726 258.054; 356.798 359.005; 404.260 411.003], ...
         [46.000 54.823; 61.000 70.332; 203.005 207.125; 275.875 278.121; 313.500 314.824; 337.973 338.636; 422.505 426.239], ...
         [58.000 62.479; 78.250 85.166; 89.955 91.360; 116.322 117.888; 130.013 131.987; 149.509 150.489; 174.650 175.823; 182.030 183.334; 196.758 197.384; 204.458 204.697; 205.236 208.663; 311.028 316.383; 320.700 327.181; 332.437 335.354; 344.205 346.133; 374.208 374.865; 385.519 386.214; 441.942 444.241; 453.957 456.997; 486.039 487.004; 501.238 504.185; 512.962 514.675; 553.398 556.215], ...
         [41.406 45.743; 58.681 59.144; 108.086 108.896; 140.633 143.750; 196.110 199.474; 210.778 210.971; 234.649 235.143; 258.081 259.632; 339.101 340.805; 390.277 390.609; 438.935 442.122; 528.221 534.031]};
     BadSegments{5} = {...
         [265.539 265.778; 266.334 266.495; 268.479 268.965; 367.428 367.636; 439.655 442.779; 453.497 453.853; 504.997 505.329; 519.513 519.683; 595.674 595.982; 602.000 602.463], ...
         [121.113 121.499; 124.971 126.213; 253.735 254.075; 272.232 272.464; 272.895 273.104; 346.368 346.645; 368.812 369.052; 406.382 406.605; 452.920 453.113; 454.903 455.112; 507.655 507.840; 508.766 509.013; 584.853 585.030; 594.831 595.656; 597.261 602.249], ...
         [37.251 37.497; 38.825 39.056; 40.615 41.849; 43.624 44.758; 53.333 53.641; 54.698 55.076; 57.668 59.196; 79.129 79.360; 81.475 81.714; 122.658 123.375; 284.787 285.296; 288.754 288.993; 345.790 346.022; 421.212 421.459; 481.428 482.207; 503.408 503.685; 504.272 504.449; 524.451 524.714; 526.913 531.499], ...
         [87.322 88.178; 91.085 91.325; 95.121 95.491; 114.174 114.397; 129.874 130.113; 151.220 151.544; 281.689 281.959; 532.966 533.345], ...
         [59.176 60.218; 74.854 75.317; 308.180 309.877; 380.705 381.059], ...
         [182.382 183.245; 196.220 196.736; 276.018 276.327; 292.490 294.086; 370.755 370.847; 435.644 436.624; 467.535 468.460; 522.838 525.847]};
     BadSegments{6} = {...
         [141.690 142.424; 157.070 157.417; 355.138 356.025; 423.999 423.999; 424.999 424.999; 426.999 426.999; 427.999 427.999; 486.430 488.151; 493.999 493.999; 501.511 501.619; 501.549 501.549; 501.585 501.585; 502.999 502.999; 503.999 503.999; 540.999 540.999; 541.999 541.999; 555.999 555.999; 556.999 556.999; 561.999 561.999; 563.999 563.999; 564.999 564.999; 565.999 565.999; 567.999 567.999], ...
         [64.898 65.161; 71.700 72.718; 226.185 226.740; 324.124 324.425; 329.062 329.301; 486.143 486.975], ...
         [62.300 63.148; 266.254 266.639; 409.920 410.221], ...
         [54.048 55.214; 330.893 331.255], ...
         [185.616 186.495; 331.411 331.796; 386.843 387.028; 387.999 387.999; 389.999 389.999; 434.575 434.875; 519.802 519.995], ...
         [44.720 45.167; 211.446 211.964; 368.955 369.172]};
     BadSegments{7} = {...
         [154.966 155.144; 551.639 551.855], ...
         [88.774 89.167; 107.999 107.999; 109.999 109.999; 110.999 110.999; 112.999 112.999; 113.999 113.999; 114.999 114.999; 119.999 119.999; 121.999 121.999; 124.223 125.465; 137.011 138.871], ...
         [81.627 82.136; 377.953 381.046], ...
         [241.136 242.171; 543.849 544.196; 596.639 598.553; 600.227 601.075; 603.999 603.999; 605.999 605.999; 609.999 609.999; 611.305 612.809; 614.999 614.999; 615.803 616.937; 623.694 625.877; 653.999 653.999; 655.055 655.756; 663.999 663.999], ...
         [68.852 69.481; 74.034 75.200; 78.426 78.600; 104.497 105.963; 253.951 254.034; 256.964 257.038; 257.915 258.048; 323.254 324.156; 365.880 368.131; 369.952 370.060; 371.728 372.999; 430.931 431.965; 535.521 542.999], ...
         [70.271 71.205; 94.441 96.445; 98.613 99.126; 112.318 112.749; 131.686 132.265; 148.935 150.615; 161.120 161.600; 205.325 208.214; 215.035 215.863; 217.403 218.818; 286.178 287.171; 399.075 404.853]};
     BadSegments{8} = {...
         [238.505 238.546; 256.354 257.224; 316.116 316.167; 341.519 341.558; 356.493 356.566; 380.095 380.170; 391.906 392.048; 448.457 448.562; 469.931 470.028; 530.488 530.575; 555.180 558.136; 562.152 562.245; 588.403 588.502; 625.205 625.662; 638.019 638.438; 649.982 650.008; 650.691 651.357; 651.925 652.061; 665.445 665.472; 695.923 696.015; 706.528 706.720; 729.706 732.534], ...
         [99.546 106.114; 113.245 114.199; 150.885 154.989; 277.486 278.075; 333.645 335.574; 339.358 340.646; 371.860 375.394; 497.459 499.156], ...
         [49.008 50.205; 231.446 233.406; 329.590 329.659; 355.101 356.019; 360.733 360.973; 372.955 374.891; 389.283 392.068; 453.610 455.431; 464.632 465.265; 489.209 489.996; 514.777 515.295], ...
         [178.368 179.232; 293.865 294.521; 418.252 418.314; 450.124 450.209; 480.236 480.445; 492.725 493.975; 495.170 497.624; 500.382 500.459; 504.247 504.402; 628.017 628.079; 628.827 628.905; 630.209 630.332], ...
         [100.616 101.172; 107.691 108.539; 188.814 188.875; 193.119 193.281; 207.964 208.033; 432.254 432.385; 489.834 489.911; 517.960 518.037; 518.955 519.040; 520.938 521.062; 521.945 522.037; 523.959 524.052; 525.942 526.057; 526.945 527.015; 528.958 529.059; 531.138 531.192; 531.979 532.048; 532.951 533.028; 533.962 534.024], ...
         [135.997 137.232; 383.565 383.657; 418.763 418.955]};
     BadSegments{9} = {...
         [215.107 216.187; 262.388 262.388; 287.519 287.635; 289.895 290.135; 311.999 311.999; 350.161 351.179; 526.154 527.033; 564.999 564.999; 584.935 585.059; 587.999 587.999; 601.999 601.999; 603.999 603.999; 608.999 608.999; 612.999 612.999], ...
         [47.667 47.775; 49.172 54.681; 67.195 70.805; 78.988 80.477; 138.701 138.948; 138.727 138.727; 138.728 138.728; 138.728 138.728; 138.734 138.734; 138.734 138.734; 138.881 138.881; 138.881 138.881; 138.907 138.907; 140.993 141.093; 141.037 141.037; 141.045 141.045; 155.795 155.864; 155.822 155.822; 155.849 155.849; 168.999 168.999; 206.999 206.999; 219.999 219.999; 225.999 225.999; 226.999 226.999; 228.999 228.999; 236.999 236.999; 242.463 242.463; 242.463 242.463; 242.487 242.487; 247.999 247.999; 251.999 251.999; 252.483 252.483; 253.315 254.163; 265.999 265.999; 267.999 267.999; 272.358 272.358; 272.410 272.410; 298.999 298.999; 300.999 300.999; 314.037 314.037; 314.047 314.047; 321.813 321.813; 321.813 321.813; 321.833 321.833; 329.117 329.117; 329.117 329.117; 329.156 329.156; 346.999 346.999; 347.999 347.999; 349.320 349.320; 349.329 349.329; 352.528 355.869; 364.040 364.040; 396.278 397.420; 404.865 404.865; 407.905 407.905; 407.905 407.905; 407.954 407.954; 418.454 418.454; 418.454 418.454; 418.486 418.486; 441.999 441.999; 444.999 444.999; 447.999 447.999; 453.550 453.650; 454.931 455.055; 457.999 457.999; 479.964 480.079; 481.999 481.999; 482.949 483.073; 488.948 489.064; 511.999 511.999; 520.999 520.999; 523.999 523.999; 526.954 527.069; 533.999 533.999; 537.999 537.999], ...
         [82.889 83.198; 206.143 207.424; 403.873 404.096; 406.790 407.485; 413.936 414.075; 415.912 416.112; 536.621 537.532], ...
         [182.999 182.999; 182.999 182.999; 183.999 183.999; 195.999 195.999; 208.999 208.999; 209.999 209.999; 278.601 278.955; 413.913 414.067; 415.250 417.792; 419.940 420.087; 420.999 420.999; 512.999 512.999; 514.363 516.254; 521.917 522.134; 522.999 522.999; 523.915 524.101; 533.999 533.999; 538.999 538.999; 539.892 540.062; 543.999 543.999; 548.874 549.089; 549.900 550.062; 552.755 554.075; 585.999 585.999; 587.957 588.073; 588.999 588.999; 594.999 594.999; 603.971 604.056; 604.928 605.097; 615.986 618.495; 623.999 623.999], ...
         [53.215 54.203; 164.227 168.965; 201.433 202.775; 292.973 295.889; 303.961 304.817; 309.354 311.236; 313.123 313.639; 322.547 323.017; 331.141 334.852; 356.637 356.985; 367.855 368.079; 369.995 373.459; 377.849 378.142; 406.072 407.306; 436.164 436.665; 458.033 458.905; 516.889 518.656; 517.812 518.684], ...
         [113.945 115.225; 198.570 198.863; 264.162 264.795; 383.705 385.641; 396.477 397.064; 399.706 406.457; 452.261 453.179; 486.338 487.102; 498.869 499.579; 507.968 508.925; 546.266 547.285; 558.825 560.128]};
     BadSegments{10} = {...
         [235.104 236.184; 324.272 325.028; 330.799 331.401; 541.062 541.826; 564.747 565.072], ...
         [100.581 101.229; 285.644 285.644; 285.644 285.644; 297.979 298.033; 298.999 298.999; 300.949 301.026; 301.999 301.999; 303.999 303.999; 304.999 304.999; 306.999 306.999; 307.999 307.999; 310.999 310.999; 311.999 311.999; 313.971 314.025; 314.999 314.999; 316.995 317.035; 317.999 317.999; 319.999 319.999; 320.923 321.046; 326.971 327.048; 327.999 327.999; 329.999 329.999; 330.999 330.999; 332.974 333.028; 333.999 333.999; 335.971 336.025; 336.999 336.999; 338.973 339.035; 339.999 339.999; 341.951 342.044; 343.999 343.999; 344.964 345.033; 346.999 346.999; 347.999 347.999; 452.015 453.411; 458.735 459.776; 467.974 468.089], ...
         [41.266 41.675; 53.905 55.240; 159.159 159.345; 220.255 220.394], ...
         [66.751 67.190; 201.674 201.812; 294.119 295.168; 303.188 303.528; 316.992 317.037; 317.999 317.999; 317.999 317.999; 319.999 319.999; 320.980 321.042; 322.999 322.999; 325.999 325.999; 326.955 327.048; 328.999 328.999; 333.999 333.999; 334.999 334.999; 342.999 342.999; 343.999 343.999; 351.915 352.038; 352.999 352.999; 407.979 408.056; 409.999 409.999; 411.999 411.999; 415.405 416.154; 435.999 435.999; 436.962 437.046; 468.885 469.100; 469.999 469.999; 470.999 470.999; 516.210 516.357], ...
         [105.369 106.172; 141.468 142.178; 199.008 199.818; 201.269 201.716; 352.851 353.083; 449.865 452.041; 459.508 459.802], ...
         [229.948 230.033; 230.999 230.999; 230.999 230.999; 259.588 259.990; 343.970 345.798; 361.999 361.999; 362.970 363.046; 364.999 364.999; 366.999 366.999; 367.962 368.031; 372.521 374.179; 405.999 405.999; 409.999 409.999; 411.999 411.999; 412.999 412.999; 434.999 434.999; 435.970 436.031; 516.999 516.999; 519.999 519.999]};
     BadSegments{11} = {...
         [179.045 180.225; 323.999 323.999; 323.999 323.999; 324.999 324.999; 327.978 328.025; 328.966 329.028; 330.999 330.999; 365.999 365.999; 367.999 367.999; 370.999 370.999; 371.999 371.999; 373.999 373.999; 375.965 376.042; 377.999 377.999], ...
         [52.107 53.156; 521.077 521.155], ...
         [65.331 66.156], ...
         [81.579 82.143; 108.565 108.565; 108.566 108.566; 112.176 112.176; 122.377 122.377; 122.565 122.565; 213.882 213.882; 215.305 215.305; 224.851 224.851; 224.919 224.919; 255.815 255.815; 257.893 257.893; 359.952 359.952; 361.630 361.754; 370.285 370.285; 376.853 376.853; 511.600 511.600; 513.631 513.631; 513.988 513.988; 518.215 518.215], ...
         [63.205 64.154; 170.951 171.036; 176.841 176.841; 176.842 176.842; 177.282 177.282; 223.886 223.971; 259.963 259.963; 261.547 261.547; 341.185 341.302; 368.999 368.999; 370.999 370.999; 374.999 374.999; 382.971 383.033; 383.999 383.999; 386.999 386.999; 388.958 389.035; 390.999 390.999; 391.955 392.031; 394.955 395.040; 396.999 396.999; 398.999 398.999; 400.999 400.999; 402.999 402.999; 404.963 405.033; 406.999 406.999; 429.829 429.829; 430.346 430.346; 465.184 465.184; 470.290 470.290], ...
         [50.195 51.145; 158.850 159.012]};
     BadSegments{12} = {...
         [193.435 194.175; 500.000 501.000; 08.988 509.868; 528.999 528.999; 528.999 528.999; 529.999 529.999; 531.999 531.999; 547.999 547.999], ...
         [133.484 134.185; 134.911 135.065; 553.999 553.999; 553.999 553.999; 554.999 554.999; 557.999 557.999; 558.999 558.999; 564.999 564.999; 565.977 566.046; 568.999 568.999; 569.951 570.028; 571.999 571.999; 579.959 580.028; 580.977 581.055; 583.971 584.033; 583.999 583.999; 585.999 585.999; 586.999 586.999; 588.980 589.026; 590.999 590.999; 591.958 592.044; 595.999 595.999], ...
         [46.028 47.216; 129.557 130.236; 200.999 200.999; 200.999 200.999; 201.975 202.075; 203.999 203.999; 204.967 205.053; 213.627 214.515; 218.958 219.044; 222.959 228.252; 309.999 309.999; 311.897 317.537; 311.999 311.999; 351.968 353.234; 399.999 399.999; 446.508 451.454; 487.999 487.999; 512.278 512.926], ...
         [82.535 84.062; 102.247 102.355; 134.999 134.999; 134.999 134.999; 136.999 136.999; 138.973 139.042; 147.999 147.999; 148.999 148.999; 149.999 149.999; 193.999 193.999; 194.999 194.999; 224.984 226.095; 241.954 242.077; 243.948 244.087; 280.999 280.999; 281.999 281.999; 305.000 305.451; 310.945 311.068; 328.999 328.999; 352.999 352.999; 353.999 353.999; 397.025 397.411; 415.944 418.096; 415.999 415.999; 470.999 470.999; 477.971 478.041; 483.997 484.737; 492.958 493.089; 493.999 493.999; 494.999 494.999; 522.999 522.999; 523.999 523.999; 524.955 525.039], ...
         [175.183 176.155; 246.112 246.991; 412.005 413.340; 483.915 484.061; 485.959 486.028; 497.360 498.880; 594.457 594.944; 596.641 598.006; 615.277 618.710], ...
         [66.900 72.232; 75.510 78.688; 543.495 545.999]};
     BadSegments{13} = {...
         [307.246 308.179; 627.881 628.089; 629.941 630.049], ...
         [171.506 172.301; 172.999 172.999; 430.999 430.999; 432.999 432.999; 434.999 434.999; 448.999 448.999; 479.999 479.999; 489.999 489.999; 490.999 490.999; 491.999 491.999], ...
         [92.059 93.209], ...
         [52.791 53.231; 54.999 54.999; 65.985 68.138; 91.240 91.386; 92.137 92.207; 105.346 105.493; 121.120 121.398; 146.546 146.955; 194.025 197.652; 242.985 243.571; 247.101 247.556; 269.889 270.083; 270.946 271.070; 274.408 275.866; 294.565 295.267; 319.370 319.879; 365.999 365.999; 375.913 376.044; 376.869 377.055; 377.999 377.999; 390.130 393.225; 403.965 404.035; 404.953 405.075; 419.265 419.565; 427.015 427.154; 427.879 428.118; 427.999 427.999; 428.913 429.067; 480.945 482.095; 484.285 484.571; 516.929 517.099; 517.963 518.079], ...
         [115.921 116.060; 117.999 117.999; 120.999 120.999; 126.896 127.058; 130.955 131.039; 131.957 132.043; 132.961 133.015; 134.999 134.999; 135.950 136.043; 139.999 139.999; 143.999 143.999; 144.850 145.066; 145.999 145.999; 146.999 146.999; 160.929 161.045; 161.999 161.999; 189.086 189.310; 193.939 194.063; 195.975 196.045; 199.961 200.045; 201.952 202.059; 202.939 205.084; 210.999 210.999; 211.738 213.629; 217.954 218.061; 218.987 219.049; 219.975 220.028; 258.953 259.037; 259.952 260.045; 260.963 261.032; 261.958 262.043; 262.954 263.054; 262.999 262.999; 288.999 288.999; 295.477 295.655; 298.976 299.054; 299.979 300.018; 300.975 301.005; 336.952 337.044; 337.977 338.032; 338.988 339.027; 338.999 338.999; 339.991 340.045; 340.975 341.029; 341.978 342.016; 343.976 344.054; 361.961 362.045; 365.942 366.050; 370.999 370.999; 384.961 385.045; 409.977 410.047; 415.984 416.030; 426.944 427.059; 430.999 430.999; 436.999 436.999; 448.999 448.999; 449.500 452.131; 481.962 482.055; 482.973 483.027; 484.975 485.060; 503.999 503.999; 504.999 504.999; 507.967 508.028; 508.955 509.032; 510.999 510.999; 512.999 512.999; 518.865 520.292; 522.958 523.043; 523.999 523.999; 524.948 525.034; 525.959 526.044; 526.946 527.055; 527.942 528.027; 528.961 529.053; 529.945 530.022; 535.999 535.999; 536.957 537.058; 538.967 539.036; 539.963 540.040; 541.976 542.045; 543.999 543.999; 551.956 552.034; 552.990 553.028; 553.977 554.024; 555.968 556.022; 556.204 557.454; 557.963 558.032; 559.969 560.023; 563.973 564.035; 563.999 563.999], ...
         [87.308 87.639; 107.039 108.189; 427.943 428.035; 437.965 438.027; 439.973 440.026; 491.275 492.571]};
     BadSegments{14} = {...
         [365.982 367.194; 368.999 368.999; 368.999 368.999; 369.999 369.999; 371.999 371.999; 373.999 373.999; 375.942 376.050; 376.999 376.999; 378.999 378.999; 380.999 380.999; 382.999 382.999; 383.999 383.999; 386.999 386.999; 387.999 387.999; 418.999 418.999; 444.999 444.999; 690.178 693.218], ...
         [101.796 102.183; 218.999 218.999; 219.999 219.999; 221.999 221.999; 222.999 222.999; 227.999 227.999; 229.999 229.999; 230.999 230.999; 232.999 232.999; 233.999 233.999; 235.999 235.999; 237.999 237.999; 238.985 239.031; 240.999 240.999; 242.999 242.999; 243.999 243.999; 246.999 246.999; 247.999 247.999; 253.999 253.999; 258.999 258.999; 259.999 259.999; 260.227 261.123; 264.999 264.999; 265.999 265.999; 268.999 268.999; 269.999 269.999; 274.999 274.999; 276.999 276.999; 277.999 277.999; 280.999 280.999; 282.966 283.021; 284.999 284.999; 285.999 285.999; 360.999 360.999; 362.973 363.026; 364.999 364.999; 365.999 365.999; 367.999 367.999; 368.985 369.024; 370.999 370.999; 371.999 371.999; 378.999 378.999; 379.999 379.999; 381.999 381.999; 383.999 383.999; 385.999 385.999; 387.999 387.999; 388.999 388.999; 398.999 398.999; 415.999 415.999; 417.999 417.999; 418.999 418.999; 421.999 421.999; 422.999 422.999], ...
         [89.285 90.187], ...
         [85.991 87.179; 88.999 88.999; 89.999 89.999; 92.966 93.027; 93.999 93.999; 107.999 107.999; 110.999 110.999; 111.971 112.025; 118.999 118.999; 121.999 121.999; 122.999 122.999; 166.999 166.999; 168.957 169.035; 173.999 173.999; 174.999 174.999; 175.957 176.035; 198.999 198.999; 199.999 199.999; 202.999 202.999; 203.999 203.999; 205.999 205.999; 207.999 207.999; 208.999 208.999; 210.974 211.044; 211.999 211.999; 212.999 212.999; 232.984 233.023; 234.999 234.999; 235.999 235.999; 236.999 236.999; 239.958 240.044; 240.999 240.999], ...
         [71.132 72.227], ...
         [98.134 98.827; 110.497 110.814; 114.000 117.272; 279.999 279.999; 279.999 279.999; 280.999 280.999; 282.999 282.999; 283.999 283.999; 284.999 284.999; 286.999 286.999; 287.999 287.999; 288.999 288.999; 289.999 289.999; 291.966 292.044; 292.999 292.999; 346.999 346.999; 437.608 437.646; 518.988 519.104; 522.969 523.015; 534.999 534.999; 536.967 537.029; 562.725 563.426]};
     BadSegments{15} = {...
         [66.755 67.172; 257.988 258.042; 258.714 259.385; 260.958 261.044; 265.931 266.062; 267.968 268.022; 268.978 269.033; 270.992 271.031; 272.964 273.017; 273.968 274.030; 275.975 276.059; 276.970 277.046; 288.976 289.038; 289.984 290.015; 295.994 296.025; 296.982 297.013; 300.966 301.152; 364.730 364.800], ...
         [46.531 46.555; 65.156 66.148; 87.535 87.642; 183.896 183.934; 294.342 294.375; 330.004 330.528; 333.785 333.815; 345.177 345.203; 399.544 399.573; 480.964 480.980], ...
         [53.557 54.175; 148.851 149.136], ...
         [43.681 44.214; 361.255 361.587; 409.462 411.021], ...
         [90.868 91.130; 410.935 411.044], ...
         [64.786 65.141; 132.385 132.786; 177.735 178.252; 278.747 278.902; 313.959 314.028; 314.970 315.031]};
     BadSegments{16} = {...
         [38.958 44.335; 305.280 312.826; 324.778 326.067; 393.183 398.367; 403.410 422.854], ...
         [49.726 50.251; 58.675 65.465; 263.918 264.088; 321.795 322.195; 522.881 523.081], ...
         [61.245 62.325; 65.628 65.905; 315.686 317.275; 335.184 336.449; 387.870 388.117; 389.892 390.062], ...
         [56.020 57.255; 60.711 61.096; 64.353 64.924; 73.202 73.757; 76.134 76.844; 95.625 96.859; 171.436 172.625; 178.359 178.715; 212.828 213.415; 352.425 352.617; 367.755 369.097; 488.400 489.426], ...
         [71.665 72.513; 76.347 77.181; 127.134 128.862], ...
         [46.190 47.209; 53.461 55.752; 194.317 194.510; 213.101 213.240; 216.356 216.442; 225.962 226.286; 245.163 245.464; 252.041 253.422; 254.648 254.856; 292.966 294.340; 340.928 341.067; 346.298 346.452]};
    BadSeg = BadSegments{iSubj}{iRun}';
end


function d = commandlines(d, commandname, varargin)

%tstoolbox/@description/addcommandlines
%   adds new commandline to list of commands that have been applied to
%   that signal
%   example 1
%          addcommandlines(s, 's = spec2(s', 512, 'Hanning' )) will add 's
%          = spec2(s, 512, 'Hanning');' to the list of applied commands
%   example 2
%          len = 512; text = 'Hanning'; addcommandlines(s, 's = spec2(s',
%          512, 'Hanning' )) will add 's = spec2(s, 512, 'Hanning');' to
%          the list of applied commands
%
% Copyright 1997-2001 DPI Goettingen, License http://www.physik3.gwdg.de/tstool/gpl.txt

commandline = commandname;

for i = 1:nargin-2
	commandline = [commandline ', ' tostring(varargin{i})];
end

commandline = [commandline ');'];
d.commandlines = TSTL_append(d.commandlines, commandline);

function out = tostring(in)
switch class(in)
	case 'char'
		out = ['''' in ''''];
	case 'double'
		out = num2str(in);
	otherwise
		error('Wrong type of argument(s) given');
end


%{
    Copyright (C) Cambridge Electronic Design Limited 2014
    Author: James Thompson
    Web: www.ced.co.uk email: james@ced.co.uk, softhelp@ced.co.uk

    This file is part of CEDS64ML, a MATLAB interface to the SON64 library.

    CEDS64ML is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CEDS64ML is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CEDS64ML.  If not, see <http://www.gnu.org/licenses/>.
%}

function [ iOk, sCommentOut ] = CEDS64ChanComment( fhand, iChan, sCommentIn )
%CEDS64CHANCOMMENT Gets and sets the comment for a channel
%   [ iOk, sCommentOut ] = CEDS64ChanComment( fhand, iChan {, sCommentIn} )
%   fhand - An integer handle to an open file
%   iChan - A channel number
%   sCommentIn - (Optional) A string containing the new comment
%   Output
%   iOk - 0 if the comment was set correctly otherwise a negative error code
%   sCommentOut - A string containing the old comment

if (nargin == 2 || nargin == 3) % always get the comment
    %step 1 find out how big the comment is
    dummystring = blanks(1);
    [iSize] = calllib('ceds64int', 'S64GetChanComment', fhand, iChan, dummystring, -1);
    %step 2 create a string buffer of the correct size
    stringptr = blanks(iSize+1);
    [iOk, sCommentOut] = calllib('ceds64int', 'S64GetChanComment', fhand, iChan, stringptr, 0);
else
    iOk = -22;
end

% has there been an error?
if iOk < 0
    return;
end

% if not set the comment if we're given one
if (nargin == 3)
    iOk = calllib('ceds64int', 'S64SetChanComment', fhand, iChan, sCommentIn);
end
end


function v = asgn(a1, a2) %#ok
% Author Bruno Luong <brunoluong@yahoo.com>
% Last update: 05/Apil/2009
v = a2;
end


function [status,results] = mrtrix_cmd(cmd_str, bkgrnd, verbose)
% function [status,results] = mrtrix_cmd(cmd_str, [bkgrnd=true], [verbose=true])
%
% Send a command to an mrtrix function and get back status and results
%
% INPUTS:
%   cmd_str - A string containing the mrtrix command to run
%   bkgrnd  - [true/false] Whether to execute the command in the background (only possible on unix)
%   verbose - Whether to display stdout to the command window (when it's done).
%
% OUTPUTS:
%   status  - Whether the operation succeeded (1) or not (0)
%   results - The results of the operation in stdout.
%
% Notes:
%  When bkgrnd is set to true, the command will be executed in another
%  terminal.
%
%
% Franco Pestilli, Ariel Rokem & Bod Dougherty Stanford Univesity

if notDefined('bkgrnd'), bkgrnd = false;end
if notDefined('verbose'),verbose = true;end

% Need to bypass the matlab libraries at the top of the path, by screwing
% with it:
orig_ld_path = mrtrix_set_ld_path;

% This opens another xterm, runs it there and gives you back control of the
% matlab session:
if bkgrnd
    if ~isunix
        bkgrnd = false;
        warning('Cannot run in background on MS Windows. Defaulting to run mrtrix command in the Matlab session');
    else
        cmd_str = ['xterm -e ' cmd_str ' &'];
    end
end
fprintf('\n[%s] Running the following command: \n%s\n',mfilename,cmd_str);

% Run the command and get back status and results:
[status,results] = system(cmd_str);

% If there was a failure, throw a warning:
if (status ~=0)
    warning('[%s] There was a failure running the command. \nCommand: %s\nError %s\n',mfilename,results);
end

% If running the command within the matlab session and verbose, display
% results, when it returns:
if (~bkgrnd && verbose), disp(results); end

% Reset the ld_path:
setenv('LD_LIBRARY_PATH', orig_ld_path);

end


function L = add_mex_source(L, directory, relName, matchPlatform, excludePlatform, extras)
% function L = add_mex_source(L, directory, relName, matchPlatform, excludePlatform, extras)
%
% Input + output argument L is a structure array of directory names, source file names,
% and extra arguments required for the compilation of MEX files. This function will
% create a new element of this structure and append it to L.
%
% Further inputs:
%   directory
%      target directory of the mex-file
%   relName
%      source file relative to 'directory'
%   matchPlatform
%      list of platforms this MEX file should only be compiled for.
%      use an empty matrix [] to compile for all platforms
%   excludePlatform
%      list of platforms this MEX file should NOT be compiled for.
%   extras
%      extra arguments to the MEX command, e.g. additional source files

% (C) 2010 S. Klanke

% Check if this file only needs compilation on certain platforms (including this one)
if nargin>3 && ~isempty(matchPlatform)
   ok = false;
   for k=1:numel(matchPlatform)
       if strcmp(matchPlatform{k}, computer)
		  ok = true;
		  break;
	   end
	end
	if ~ok
	   return
	end
end

% Check if this file cannot be compiled on certain platforms (including this one)
if nargin>4 && ~isempty(excludePlatform)
   ok = true;
   for k=1:numel(excludePlatform)
      if strcmp(excludePlatform{k}, computer)
         return;
      end
   end
end

L(end+1).dir   = directory;
L(end).relName = relName;
if nargin>5
  L(end).extras = extras;
end


function [major,minor,patchnum,extra]=iso2meshver
%
% [major,minor,patchnum,extra]=iso2meshver
%      or
% v=iso2meshver
%
% get the version number of iso2mesh toolbox
%
% author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
%
% output:
%    if you ask for a single output:
%      v: a string denotes the current version number; the string is
%       typically in the following format: "major.minor.patch-extra"
%       where major/minor/patch are typically integers, and extra can
%       be an arbitrary string and is optional
%    if you ask for 4 outputs:
%     [major,minor,patchnum,extra] are each field of the version string
%
% -- this function is part of iso2mesh toolbox (http://iso2mesh.sf.net)
%

major=1;
minor=5;
patchnum=0;
extra='$Rev:: 403 $';
extra=regexprep(extra,'[\s$:]', '');

iso2meshvstr=sprintf('%d.%d.%d',major,minor,patchnum);
if(~isempty(extra))
   iso2meshvstr=[iso2meshvstr '-' extra];
end

if(nargout==0)
   fprintf(1,'iso2mesh toolbox version: %s\n',iso2meshvstr);
   clear major;
elseif(nargout==1)
   major=iso2meshvstr;
elseif(nargout~=4)
   error('you need to return either 1 or 4 output variables');
end


function success = verifyCobraFunctionError(functionCall, varargin)
% Tests whether the provided call throws an error and if the error message
% fits to the message provided
%
% USAGE:
%
%    success = verifyCobraFunctionError(functionCall, message)
%
% INPUTS:
%    functionCall:       The name of a function.
%
% OPTIONAL INPUTS:
%
%    varargin:           Parameter Value pairs for specific options.
%                         * outputArgCount -     The number of output arguments requested, as this can influence the functionality (Default: 0).
%                         * testMessage -        The message that should be thrown in this error. If no message is supplied, any thrown error will be accepted
%                         * inputs -             If the function requires Any inputs, provide them in a cell array.
%
% OUTPUTS:
%    success:            Whether an error was thrown, and the type and
%                        message match (if provided)
% EXAMPLES:
%    %Test whether pow throws an error when using chars
%    verifyCobraFunctionError('pow','inputs',{'abc','dce'});
%    %Test, whether initCobraToolbox throws an error
%    verifyCobraFunctionError('initCobraToolbox');
%    %Test whether targetFunctions error message is 'blubb'
%    verifyCobraFunctionError('targetFunctions','message','blubb');
%    %Test whether fluxvariability throws an error when 5 outputs are defined
%    verifyCobraFunctionError('fluxVariability','inputs',{model,0},'outputArgCount',10);

testMessage = false;
parser = inputParser();
parser.addRequired('functionCall',@ischar)
parser.addParamValue('inputs',{},@iscell)
parser.addParamValue('testMessage','',@ischar)
parser.addParamValue('outputArgCount',0,@(x) isnumeric(x) && mod(x,1) == 0);

parser.parse(functionCall,varargin{:});

outputArgcount = parser.Results.outputArgCount;
testMessage = ~any(ismember('testMessage',parser.UsingDefaults));
message = parser.Results.testMessage;
inputs = parser.Results.inputs;

functionCall = str2func(functionCall);
success = true;
try
    if outputArgcount > 0
        outArgs = cell(outputArgcount,1);
        [outArgs{:}] = functionCall(inputs{:});
    else
        functionCall(inputs{:});
    end
catch ME
    if testMessage
        success = strcmp(ME.message,message);
    end
    %Now, we checked the message if necessary, and we are obvously in the
    %catch block, so an error was thrown, i.e. this is successful.
    return
end
%If we reach this point, the function did not throw an error. i.e. the
%verification is unsuccessful.
success = false;


function input = prepare_data_for_input(root, list_pos, box_pos, pts_pos, list_neg, box_neg, ignore_neg, param)
    input = cell(length(param.gpu_id), 1);
    input(1:length(param.pos_gpu)) = prepare_pos_data_for_input( root, list_pos, box_pos, pts_pos, param );
    input(length(param.pos_gpu)+1:end) = prepare_neg_data_for_input( root, list_neg, box_neg, ignore_neg, param );
end


function [ meta, symmetry ] = meta2sicd_tiffrs2( tiff_filename )
%META2SICD_TIFFRS2 Compile SICD metadata for RS2 data package
%
% Written by: Wade Schwartzkopf, NGA/IDT
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

[ meta, symmetry ] = meta2sicd_tiffrs( tiff_filename, 'RS2' );

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function [structOut, errors] = checkStructureDefaults(params, defaults)
% Check params input parameter values against defaults put in structOut
errors = cell(0);
fNames = fieldnames(defaults);
structOut = defaults;
for k = 1:length(fNames)
    try
       nextValue = getStructureParameters(params, fNames{k}, ...
                                          defaults.(fNames{k}).value);
       validateattributes(nextValue, defaults.(fNames{k}).classes, ...
                         defaults.(fNames{k}).attributes);
       structOut.(fNames{k}).value = nextValue;
    catch mex
        errors{end + 1} = [fNames{k} ' invalid: ' mex.message]; %#ok<AGROW>
    end
end


% test101 Test querying strings from a table.

% OtMatlab: Matlab interface to the Omnitrans transport planning software

% Copyright (c) 2008, VORtech Computing
% Copyright (c) 2010, Omnitrans International

% All rights reserved.

% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:

%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%     * Neither the name of VORtech Computing and Omnitrans International nor the names
%       of its contributors may be used to endorse or promote products derived
%       from this software without specific prior written permission.

% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.

% 2006-04-13 Created (Johan Meijdam, VORtech)

function test101()
   OtStart();
   hquery = OtQueryNew();
   OtQuerySetSQL(hquery, ['SELECT name AS Hnames FROM "' testdir() 'name.DB" AS nametable WHERE name LIKE "H%"']);
   if OtQueryOpen(hquery) ~= 1
      error('OtQueryOpen failed')
   end
   disp(OtQueryFields(hquery));
   OtQueryFirst(hquery);
   while ~OtQueryEof(hquery)
      disp(OtQueryGetAll(hquery));
      OtQueryNext(hquery);
   end
   OtQueryClose(hquery);
   OtQueryFree(hquery);
   OtStop();
end