function [rxnScores, geneScores, hpaScores, arrayScores]=scoreModel(model,hpaData,arrayData,tissue,celltype,noGeneScore,multipleGeneScoring,multipleCellScoring,hpaLevelScores)
% scoreRxns
%   Scores the reactions and genes in a model based on expression data
%   from HPA and/or gene arrays
%
%   Input:
%   model               a model structure
%   hpaData             HPA data structure from parseHPA (opt if arrayData is
%                       supplied, default [])
%   arrayData           gene expression data structure (opt if hpaData is
%                       supplied, default [])
%       genes           cell array with the unique gene names
%       tissues         cell array with the tissue names. The list may not be
%                       unique, as there can be multiple cell types per tissue
%       celltypes       cell array with the cell type names for each tissue
%       levels          GENESxTISSUES array with the expression level for
%                       each gene in each tissue/celltype. NaN should be
%                       used when no measurement was performed
%       threshold       a single value or a vector of gene expression
%                       thresholds, above which genes are considered to be
%                       "expressed". (opt, by default, the mean expression
%                       levels of each gene across all tissues in arrayData
%                       will be used as the threshold values)
%   tissue              tissue to score for. Should exist in either
%                       hpaData.tissues or arrayData.tissues
%   celltype            cell type to score for. Should exist in either
%                       hpaData.celltypes or arrayData.celltypes for this
%                       tissue (opt, default is to use the best values
%                       among all the cell types for the tissue. Use [] if
%                       you want to supply more arguments)
%   noGeneScore         score for reactions without genes (opt, default -2)
%   multipleGeneScoring determines how scores are calculated for reactions
%                       with several genes ('best' or 'average')
%                       (opt, default 'best')
%   multipleCellScoring determines how scores are calculated when several
%                       cell types are used ('best' or 'average')
%                       (opt, default 'best')
%   hpaLevelScores      structure with numerical scores for the expression
%                       level categories from HPA. The structure should have a
%                       "names" and a "scores" field (opt, see code for
%                       default scores)
%
%
%   Output:
%   rxnScores       scores for each of the reactions in model
%   geneScores      scores for each of the genes in model. Genes which are
%                   not in the dataset(s) have -Inf as scores
%   hpaScores       scores for each of the genes in model if only taking hpaData
%                   into account. Genes which are not in the dataset(s)
%                   have -Inf as scores
%   arrayScores     scores for each of the genes in model if only taking arrayData
%                   into account. Genes which are not in the dataset(s)
%                   have -Inf as scores
%
%   Usage: [rxnScores, geneScores, hpaScores, arrayScores]=scoreModel(model,...
%               hpaData,arrayData,tissue,celltype,noGeneScore,multipleGeneScoring,...
%               multipleCellScoring,hpaLevelScores)

if nargin<3
    arrayData=[];
end
if nargin<5
    celltype=[];
else
    celltype=char(celltype);
end
if nargin<6
    noGeneScore=-2;
end
if nargin<7
    multipleGeneScoring='best';
else
    multipleGeneScoring=char(multipleGeneScoring);
end
if nargin<8
    multipleCellScoring='best';
else
    multipleCellScoring=char(multipleCellScoring);
end
if nargin<9
    %The first four are for APE, the other ones for staining
    hpaLevelScores.names={'High' 'Medium' 'Low' 'None' 'Strong' 'Moderate' 'Weak' 'Negative' 'Not detected'};
    hpaLevelScores.scores=[20 15 10 -8 20 15 10 -8 -8];
end

if isempty(hpaData) && isempty(arrayData)
    EM='Must supply hpaData, arrayData or both';
    dispEM(EM);
end
if ~strcmpi(multipleGeneScoring,'best') && ~strcmpi(multipleGeneScoring,'average')
    EM='Valid options for multipleGeneScoring are "best" or "average"';
    dispEM(EM);
end
if ~strcmpi(multipleCellScoring,'best') && ~strcmpi(multipleCellScoring,'average')
    EM='Valid options for multipleCellScoring are "best" or "average"';
    dispEM(EM);
end

%Throw an error if array data for only one tissue is supplied without
%specifying threshold values
if ~isempty(arrayData)
    if numel(unique(arrayData.tissues))<2
        if ~isfield(arrayData,'threshold') || isempty(arrayData.threshold)
            EM='arrayData must contain measurements for at least two celltypes/tissues since the score is calculated based on the expression level compared to the overall average';
            dispEM(EM);
        end
    end
end

%Process arrayData.threshold if necessary
if isfield(arrayData,'threshold') && (numel(arrayData.threshold) == 1)
    % if only a single gene threshold value is provided, then just
    % duplicate this value for all genes.
    arrayData.threshold = arrayData.threshold*ones(size(arrayData.genes));
end

%This is so that the code can ignore which combination of input data that is
%used
if isempty(arrayData)
    arrayData.genes={};
    arrayData.tissues={};
    arrayData.celltypes={};
    arrayData.levels=[];
    arrayData.threshold=[];
end
if isempty(hpaData)
    hpaData.genes={};
    hpaData.tissues={};
    hpaData.celltypes={};
    hpaData.levels={};
    hpaData.types={};
    hpaData.reliabilities={};
    hpaData.gene2Level=[];
    hpaData.gene2Type=[];
    hpaData.gene2Reliability=[];
end

%Check that the tissue exists
if ~ismember(upper(tissue),upper(hpaData.tissues)) && ~ismember(upper(tissue),upper(arrayData.tissues))
    EM='The tissue name does not match';
    dispEM(EM);
end
if any(celltype)
    %Check that both data types has cell type defined if that is to be used
    if ~isfield(hpaData,'celltypes') || ~isfield(arrayData,'celltypes')
        EM='Both hpaData and arrayData must contain cell type information if cell type is to be used';
        dispEM(EM);
    end
    if ~ismember(upper(celltype),upper(hpaData.celltypes)) && ~ismember(upper(celltype),upper(arrayData.celltypes))
        EM='The cell type name does not match';
        dispEM(EM);
    end
end

%Some preprocessing of the structures to speed up a little Remove all
%tissues that are not the correct one
J=~strcmpi(hpaData.tissues,tissue);

%If cell type is supplied, then only keep that cell type
if any(celltype)
    J=J | ~strcmpi(hpaData.celltypes,celltype);
end

hpaData.tissues(J)=[];
if isfield(hpaData,'celltypes')
    hpaData.celltypes(J)=[];
end
if isfield(hpaData,'gene2Level')
    hpaData.gene2Level(:,J)=[];
end
if isfield(hpaData,'gene2Type')
    hpaData.gene2Type(:,J)=[];
end
if isfield(hpaData,'gene2Reliability')
    hpaData.gene2Reliability(:,J)=[];
end

%Remove all genes from the structures that are not in model or that aren't
%measured in the tissue
if ~isempty(hpaData.genes) %This should not be necessary, but the summation is a 0x1 matrix and the other is []
    I=~ismember(hpaData.genes,model.genes) | sum(hpaData.gene2Level,2)==0;
else
    I=[];
end
hpaData.genes(I)=[];
if isfield(hpaData,'gene2Level')
    hpaData.gene2Level(I,:)=[];
end
if isfield(hpaData,'gene2Type')
    hpaData.gene2Type(I,:)=[];
end
if isfield(hpaData,'gene2Reliability')
    hpaData.gene2Reliability(I,:)=[];
end

I=strcmpi(arrayData.tissues,tissue);
%If cell type is supplied, then only keep that cell type
if any(celltype)
    I=I & strcmpi(arrayData.celltypes,celltype);
end

%Remove all genes from the structures that are not in model or that aren't
%measured in the tissue
J=~ismember(arrayData.genes,model.genes) | myAll(isnan(arrayData.levels(:,I)),2);
arrayData.genes(J)=[];
arrayData.levels(J,:)=[];
if isfield(arrayData,'threshold')
    arrayData.threshold(J) = [];
end

%Calculate the scores for the arrayData. These scores are calculated for
%each genes from its fold change between the tissue/celltype(s) in question
%and all other celltypes. This is a lower quality data than protein
%abundance, since a gene that is equally highly expressed in all cell types
%will have a score of 0.0. These scores are therefore only used for genes
%for which there is no HPA data available. The fold changes are transformed
%as min(log(x),10) for x>1 and max(log(x),-5) for x<1 in order to have
%negative scores for lower expressed genes and to scale the scrores to have
%somewhat lower weights than the HPA scores
tempArrayLevels=arrayData.levels;
tempArrayLevels(isnan(tempArrayLevels))=0;
if isfield(arrayData,'threshold') && ~isempty(arrayData.threshold)
    % if provided, the user-supplied expression threshold value(s) will be
    % used as the "average" expression level to which each gene is
    % compared.
    average=arrayData.threshold;
else
    average=sum(tempArrayLevels,2)./sum(~isnan(arrayData.levels),2);
end
if strcmpi(multipleCellScoring,'best')
    current=max(tempArrayLevels(:,I),[],2);
else
    current=sum(tempArrayLevels(:,I),2)./sum(~isnan(arrayData.levels(:,I)),2);
end
if ~isempty(current)
    aScores=5*log(current./average);
else
    aScores=[];
end
aScores(aScores>0)=min(aScores(aScores>0),10);
aScores(aScores<0)=max(aScores(aScores<0),-5);
aScores(isnan(aScores)) = -5;  % NaNs occur when gene expression is zero across all tissues

%Map the HPA levels to scores
[I, J]=ismember(upper(hpaData.levels),upper(hpaLevelScores.names));
if ~all(I)
    EM='There are expression level categories that do not match to hpaLevelScores';
    dispEM(EM);
end
[K, L, M]=find(hpaData.gene2Level);
scores=hpaLevelScores.scores(J);
if strcmpi(multipleCellScoring,'best')
    hScores=max(sparse(K,L,scores(M),numel(hpaData.genes),numel(hpaData.tissues)),[],2);
else
    hScores=mean(sparse(K,L,scores(M),numel(hpaData.genes),numel(hpaData.tissues)),2);
end

%Get the scores for the genes, only use HPA if available
geneScores=inf(numel(model.genes),1)*-1;
hpaScores=geneScores;
arrayScores=geneScores;

[I, J]=ismember(model.genes,hpaData.genes);
hpaScores(I)=hScores(J(I));
geneScores(I)=hScores(J(I));
[I, J]=ismember(model.genes,arrayData.genes);
arrayScores(I)=aScores(J(I));
geneScores(I & myIsInf(geneScores))=aScores(J(I & myIsInf(geneScores)));

%Remove the genes that have no data from the model
I=ismember(model.genes,hpaData.genes) | ismember(model.genes,arrayData.genes);
model.genes(~I)=[];
model.rxnGeneMat(:,~I)=[];

%Map the genes to the HPA/array genes
[hpaExist, hpaMap]=ismember(model.genes,hpaData.genes);
[arrayExist, arrayMap]=ismember(model.genes,arrayData.genes);

%Set the default scores for reactions without genes
rxnScores=ones(numel(model.rxns),1)*noGeneScore;

%Loop through the reactions and calculate the scores
for i=1:numel(model.rxns)
    %Check if it has genes
    I=find(model.rxnGeneMat(i,:));
    if any(I)
        %If any of the genes exist in hpaData, then don't use arrayData
        if any(hpaExist(I))
            %At least one gene was found in HPA
            if strcmpi(multipleGeneScoring,'best')
                rxnScores(i)=max(hScores(hpaMap(I(hpaExist(I)))));
            else
                rxnScores(i)=mean(hScores(hpaMap(I(hpaExist(I)))));
            end
        else
            %Use array data
            if any(arrayExist(I))
                %At least one gene was found in the array data
                if strcmpi(multipleGeneScoring,'best')
                    rxnScores(i)=max(aScores(arrayMap(I(arrayExist(I)))));
                else
                    rxnScores(i)=mean(aScores(arrayMap(I(arrayExist(I)))));
                end
            end
        end
    end
end
end

%This is because isinf and all returns 0x1 for empty set, which gives a
%concatenation error. Do like this instead of having many if statements
function y=myIsInf(x)
y=isinf(x);
if isempty(y)
    y=[];
end
end
function y=myAll(x,dim)
y=all(x,dim);
if isempty(y)
    y=[];
end
end


function Grasping_and_twist

%% Grasping and twisting Function with EEG recording
clear; close; clc;
%% Initializing EEG recording device
bbci_acquire_bv('close');
startup_bbci;

%% RoboticArm communication channel opened
jc = JacoComm;
connect(jc);
calibrateFingers(jc);

%% Query individual object properties
jc.JointPos
%%
jc.JointVel
%%
jc.JointTorque
%%
jc.JointTemp
%%
jc.FingerPos
%%
jc.FingerVel
%%
jc.FingerTorque
%%
jc.FingerTemp
%%
jc.EndEffectorPose
%%
jc.EndEffectorWrench
%%
jc.ProtectionZone
%%
jc.EndEffectorOffset
%%
jc.DOF
%%
jc.TrajectoryInfo

%% Methods to query joint and finger values all at once
pos = getJointAndFingerPos(jc);
vel = getJointAndFingerVel(jc);

torque = getJointAndFingerTorque(jc);
temp = getJointAndFingerTemp(jc);

setPositionControlMode(jc);
goToHomePosition(jc);

current_pos=jc.EndEffectorPose;
home_pos=jc.EndEffectorPose;
previous_pos=current_pos;

setPositionControlMode(jc);
fCmd = 0*ones(3,1);
sendFingerPositionCommand(jc,fCmd);

%% Offline model training
global EEG_MAT_DIR
EEG_MAT_DIR = '';

dd = 'MotorImagery Converted Data\';
filelist= {'20191113_demo_bhkwon'};

%% Preprocessing Offline Data
Bandpass_Filter = [4 40];

% Offline Classifier Training
[cnt, mrk, mnt] = eegfile_loadMatlab([dd filelist{1}]);

ival = [0 3000]; % 3s interval

cnt_filt = proc_filtButter(cnt, 5, Bandpass_Filter);
epo = cntToEpo(cnt_filt,mrk,ival);

[fv, Out.csp_w]=  proc_multicsp(epo, 3);
fv = proc_variance(fv); fv= proc_logarithm(fv);

fv.classifier_param = {'RLDAshrink','prior', nan, 'store_prior', 1, 'store_means', 1, ...
    'store_cov', 1, 'store_invcov', 1, 'scaling', 1};

proc = {'wr_multiClass','policy','one-vs-all','coding','hamming'}; % one-vs-all all-pairs

Out.C = trainClassifier(fv, proc);
Out.out_eeg = applyClassifier(fv, 'wr_multiClass', Out.C);

%% Onling Initialization
params = struct;
state = bbci_acquire_bv('init', params);
EEG_data = [];
mnt = getElectrodePositions(state.clab);

epo.clab = state.clab;
epo.fs = state.fs;
epo.title = filelist{1};
Ans = zeros(1,3);
%% Get EEG data
py.SharedDemo_6_Vision_LBH.getPerspectiveMine(); % vision model upload

coord=py.SharedDemo_6_Vision_LBH.mainRunning(1);
coord=double(py.array.array('d',py.numpy.nditer(coord)));
coord=uint8(coord/5);
pause(1);

home_pos=jc.EndEffectorPose;
current_pos=jc.EndEffectorPose;
prev_pos=current_pos;

targetX = coord(1);
targetY = coord(2);
targetX = double(targetX);
targetY = double(targetY);
x_origin = (102 - targetX)/100
y_origin = (5+targetY)/100

desired_pos=[x_origin; -y_origin; 0.1; home_pos(4); home_pos(5); home_pos(6)];
moveToCP(jc,desired_pos);
% YOLO to RoboticArm coordinate axis
while 1
    %% Sound cue
    [A,AFs] = audioread('grasp.mp3');
    sound(A,AFs);
    pause(2);

    eog_ch = [1 ,31];
    eog_th = 18;
    time_window = 20;

    eog_test(eog_ch, eog_th, time_window);
    disp('Receiving brain signal')

    pause(2);

    %% Initializing EEG recording
    % Initialization communication channel
    bbci_acquire_bv('close');
    EEG_MAT_DIR = '';
    params=struct;
    state=bbci_acquire_bv('init',params);
    EEGData=[];
    mnt=getElectrodePositions(state.clab);
    epo.clab = state.clab;
    epo.fs = state.fs;
    epo.title = filelist{1};
    pause('on')

    [B,BFs] = audioread('censor-beep-4.wav');
    sound(B,BFs);
    pause(4);

    data = bbci_acquire_bv(state);
    EEG_data = [EEG_data; data];

    % Sampling Rate: 250Hz, get data every 3 sec
    if size(EEG_data,1) >= 750
        epo.x = EEG_data;

        % EEG filtering
        Wps= [42 49]/epo.fs*2;
        [n, Ws]= cheb2ord(Wps(1), Wps(2),3, 40);
        [filt.b, filt.a]= cheby2(n, 50, Ws);
        epo = proc_filt(epo, filt.b, filt.a);

        % Feature Extraction and Classification
        Classification_Result = MotorImagery_Online_Fn(epo, Bandpass_Filter, Out);

        disp('Signal processing');
        pause(2);
        disp('Decoding signal');
        pause(2);

        disp('Grasp');

        disp('robotic arm activation');
        pause(2)

        setPositionControlMode(jc);
        fCmd = 4000*ones(3,1);
        sendFingerPositionCommand(jc,fCmd);

        pause(1);

        desired_pos=[0.6; -0.2; 0.15; home_pos(4); home_pos(5); home_pos(6)];
        moveToCP(jc,desired_pos);

        pause(1);

    end

    [B,BFs] = audioread('drinkwater.mp3');
    sound(B,BFs);
    pause(2);

    eog_ch = [1 ,31];
    eog_th = 18;
    time_window = 20;

    eog_test(eog_ch, eog_th, time_window);
    disp('Receiving brain signal')

    bbci_acquire_bv('close');
    EEG_MAT_DIR = '';
    params=struct;
    state=bbci_acquire_bv('init',params);
    EEGData=[];
    data=[];
    mnt=getElectrodePositions(state.clab);
    epo.clab = state.clab;
    epo.fs = state.fs;
    epo.title = filelist{1};

    [B,BFs] = audioread('censor-beep-4.wav');
    sound(B,BFs);
    pause(4);

    data = bbci_acquire_bv(state);
    EEG_data = [EEG_data; data];

    if size(EEG_data,1) >= 750
        epo.x = EEG_data;

        % EEG filtering
        Wps= [42 49]/epo.fs*2;
        [n, Ws]= cheb2ord(Wps(1), Wps(2),3, 40);
        [filt.b, filt.a]= cheby2(n, 50, Ws);
        epo = proc_filt(epo, filt.b, filt.a);

        % Feature Extraction and Classification
        Classification_Result = MotorImagery_Online_Fn(epo, Bandpass_Filter, Out);

        disp('Signal processing');
        pause(2);
        disp('Decoding signal');
        pause(2);

        desired_pos=[0.6; 0.15; 0.15; home_pos(4); home_pos(5); home_pos(6)];
        moveToCP(jc,desired_pos);

        jntVelCmd = [0;0;0;0;0;0;0.2]; %7DOF
        for i=1:300
            sendJointVelocityCommand(jc,jntVelCmd);
        end

        pause(5);

        jntVelCmd = [0;0;0;0;0;0;-0.2]; %7DOF
        for i=1:260
            sendJointVelocityCommand(jc,jntVelCmd);
        end

        pause(1);

        desired_pos=[x_origin; -y_origin; 0.08; home_pos(4); home_pos(5); home_pos(6)];
        moveToCP(jc,desired_pos);

        pause(1);

        setPositionControlMode(jc);
        fCmd = 0*ones(3,1);
        sendFingerPositionCommand(jc,fCmd);

        desired_pos=[x_origin; -y_origin; 0.2; home_pos(4); home_pos(5); home_pos(6)];
        moveToCP(jc,desired_pos);

        pause(1);

        goToHomePosition(jc);

        break;
    end

    disp('Decoding Error!');
    disp('Receiving brain signal again....');

    pause(2);

    desired_pos=[x_origin; -y_origin; 0.07; home_pos(4); home_pos(5); home_pos(6)];
    moveToCP(jc,desired_pos);

    pause(1);

    setPositionControlMode(jc);
    fCmd = 0*ones(3,1);
    sendFingerPositionCommand(jc,fCmd);

    pause(1);

    desired_pos=[x_origin; -y_origin; 0.2; home_pos(4); home_pos(5); home_pos(6)];
    moveToCP(jc,desired_pos);

    goToHomePosition(jc);

    pause(1);

    break

    bbci_acquire_bv('close');
end
end


% pop_chanplot() - graphic user interface (GUI)-based function with plotting
%                options for visualizing. Only channel measures (e.g., spectra,
%                ERPs, ERSPs, ITCs) that have been computed and saved in the study EEG
%                datasets can be visualized. These can be computed using the GUI-based
%                pop_precomp().
% Usage:
%                >> STUDY = pop_chanplot(STUDY, ALLEEG);
% Inputs:
%   ALLEEG     - Top-level EEGLAB vector of loaded EEG structures for the dataset(s)
%                in the STUDY. ALLEEG for a STUDY set is typically loaded using
%                pop_loadstudy(), or in creating a new STUDY, using pop_createstudy().
%   STUDY      - EEGLAB STUDY set comprising some or all of the EEG
%   datasets in ALLEEG.
%
% Outputs:
%   STUDY      - The input STUDY set structure modified according to specified user edits,
%                if any. Plotted channel measure means (maps, ERSPs, etc.) are added to
%                the STUDY structure after they are first plotted to allow quick replotting.
%
% Graphic interface buttons:
%  "Select channel to plot" - [list box] Displays available channels to plot (format is
%                'channel name (number of channels)'). The presented channels depend s
%                on the optional input variable 'channels'. Selecting (clicking on) a
%                channel from the list will display the selected channel channels in the
%                "Select channel(s) to plot" list box. Use the plotting buttons below
%                to plot selected measures of the selected channel. Additional editing
%                options (renaming the channel, rejecting outliers, moving channels to
%                another channel) are also available. The option 'All N channel centroids'
%                at the top of the list displays all the channels in the list except the
%                'Notchannel', 'Outlier' and 'Parentchannel' channels. Selecting this option
%                will plot the channel centroids (i.e. ERP, ERSP, ...) in a single figure.
%  "Select channel(s) to plot" - [list box] Displays the ICA channels of the currently
%                selected channel (in the "Select channel to plot" list box). Each channel
%                has the format: 'subject name, channel index'. Multiple channels can be
%                selected from the list. Use the plotting buttons below to plot different
%                measures of the selected channels on different figures. Selecting the
%                "all subjects" option is  equivalent to using the channel plotting buttons.
%                Additional editing options are reassigning the selected channels to
%                another channel or moving them to the outlier channel.
%  "Plot channel properties" - [button] Displays in one figure all the mean channel measures
%                (e.g., dipole locations, scalp maps, spectra, etc.) that were calculated
%                and saved in the EEG datsets. If there is more than one condition, the ERP
%                and the spectrum will have different colors for each condition. The ERSP
%                and ITC plots will show only the first condition; clicking on the subplot
%                will open a new figure with the different conditions displayed together.
%                Uses the command line function std_propplot().
%  "Plot scalp maps"  - [button] Displays the scalp maps of channel channels.
%                If applied to a channel, scalp maps of the channel channels
%                are plotted along with the channel mean scalp map in one figure.
%                If "All # channel centroids" option is selected, all channel scalp map
%                means are plotted in the same figure. If applied to channels, displays
%                the scalp maps of the specified channel channels in separate figures.
%                Uses the command line functions std_plotmap() and std_plotchanmap().
%  "Plot ERSPs" - [button] Displays the channel channel ERSPs.
%                If applied to a channel, channel ERSPs are plotted in one figure
%                (per condition) with the channel mean ERSP. If "All # channel centroids"
%                option is selected, plots all average ERSPs of the channels in one figure
%                per condition. If applied to channels, display the ERSP images of specified
%                channel channels in separate figures, using one figure for all conditions.
%                Uses the command line functions std_plotersp() and std_plotchannelsp().
%  "Plot ITCs" - [button] Same as  "Plot ERSPs" but with ITC.
%                Uses the command line functions std_plotitc() and std_plotchanitc().
%  "Plot dipoles" - [button] Displays the dipoles of the channel channels.
%                If applied to a channel, plots the channel channel dipoles (in blue)
%                plus the average channel dipole (in red). If "All # channel centroids" option
%                is selected, all channel plots are displayed in one figure each channel in
%                a separate subplot. If applied to channels, displays the ERSP images of the
%                specified channel. For specific channels displays channels dipole (in blue)
%                plus the average channel dipole (in Red) in separate figures.
%                Uses the command line functions std_dipplot() and std_plotchandip().
%  "Plot spectra" - [button] Displays the channel channel spectra.
%                If applied to a channel, displays channel spectra plus the average channel
%                spectrum in bold. For a specific channel, displays the channel channel
%                spectra plus the average channel spectrum (in bold) in one figure per condition.
%                If the "All # channel centroids" option is selected, displays the average
%                spectrum of all channels in the same figure, with spectrum for different
%                conditions (if any) plotted in different colors.
%                If applied to channels, displays the spectrum of specified channel
%                channels in separate figures using one figure for all conditions.
%                Uses the command line functions std_plotspec() and std_plotonechanpec().
%  "Plot ERPs" - [button] Same as "Plot spectra" but for ERPs.
%                Uses the command line functions std_ploterp() and std_plotchannelp().
%  "Create new channel" - [button] Creates a new empty channel.
%                Opens a popup window in which a name for the new channel can be entered.
%                If no name is given the default name is 'Cls #', where '#' is the next
%                available channel number. For changes to take place, press the popup
%                window 'OK' button, else press the 'Cancel' button. After the empty
%                channel is created, channels can be moved into it using,
%                'Reassign selected channel(s)' (see below). Uses the command line
%                function std_createchant().
%  "Rename selected channel" - [button] Renames a channel using the selected (mnemonic) name.
%                Opens a popup window in which a new name for the selected channel can be
%                entered. For changes to take place, press the popup window 'OK' button,
%                else press the 'Cancel' button. Uses the command line function std_renamechant().
%  "Reject outlier channels" - [button] rejects outlier channels to an outlier channel.
%                Opens a popup window to specify the outlier threshold. Move outlier
%                channels that are more than x standard deviations devs from the
%                channel centroid to an outlier channel. For changes to take place,
%                press the popup window 'OK' button, else press the 'Cancel' button.
%                Uses the command line function std_rejectoutliers().
%  "Merge channels" - [button] Merges several channels into one channel.
%                Opens a popup window in which the channels to merge may be specified
%                An optional name can be given to the merged channel. If no name is given,
%                the default name is 'Cls #', where '#' is the next available channel number.
%                For changes to take place, press the popup window 'OK' button, else press
%                the 'Cancel' button. Uses the command line function std_mergechant().
%  "Remove selected outlier channel(s)" - [button] Moves selected channel(s) to the
%                outlier channel. The channels that will be moved are the ones selected
%                in the "Select channel(s) to plot" list box. Opens a popup window in which
%                a list of the selected channel(s) is presented. For changes to take place,
%                press the popup window 'OK' button, else press the 'Cancel' button.
%                Uses the command line function std_moveoutlier().
%  "Reassign selected channel(s)" - [button] Moves selected channel(s) from one channel
%                to another. The channels that will reassign are the ones selected in the
%                "Select channel(s) to plot" list box. Opens a popup window in which
%                a list of possible channels to which to move the selected channel(s) is
%                presented. For changes to take place, press the popup window 'OK' button,
%                else press the 'Cancel' button. Uses the command line function std_movecomp().
%  "Save STUDY set to disk" - [check box] Saves the STUDY set structure modified according
%                to specified user edits to the disk. If no file name is entered will
%                overwrite the current STUDY set file.
%
% See also:  pop_prechant(), pop_chant().
%
% Authors: Arnaud Delorme, Hilit Serby, Scott Makeig, SCCN/INC/UCSD, October 11, 2004

% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, October 11, 2004, arno@sccn.ucsd.edu
%
% This program 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 2 of the License, or
% (at your option) any later version.
%
% This program 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 this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

% Coding notes: Useful information on functions and global variables used.

function [STUDY, com] = pop_chanplot(varargin)

icadefs;
com = [];
if ~isstr(varargin{1})
    if nargin < 2
        error('pop_chanplot(): You must provide ALLEEG and STUDY structures');
    end
    STUDY  = varargin{1};
    STUDY.etc.erpparams.topotime  = []; % [] for channels and NaN for components
    STUDY.etc.specparams.topofreq = []; % NaN -> GUI disabled
    STUDY.etc.erspparams.topotime = [];
    STUDY.etc.erspparams.topofreq = [];

    % test path
    % ---------
    pathwarn = 'off';
    if ~strcmpi(pwd, STUDY.filepath)
        if length(STUDY.datasetinfo(1).filepath) < 1
            pathwarn = 'on';
        elseif STUDY.datasetinfo(1).filepath(1) == '.'
            pathwarn = 'on';
        end;
    end;
    if strcmpi(pathwarn, 'on')
        warndlg2(strvcat('You have changed your working path and data files are', ...
                         'no longer available; Cancel, and go back to your STUDY folder'), 'warning');
    end;

    oldhistory = STUDY.history;
    STUDY.history = '';
    ALLEEG = varargin{2};
    if ~isfield(STUDY, 'changrp')
        STUDY = std_changroup(STUDY, ALLEEG);
        disp('Warning: history not saved for group creation');
    elseif isempty(STUDY.changrp)
        STUDY = std_changroup(STUDY, ALLEEG);
        disp('Warning: history not saved for group creation');
    end;

    show_chan          = ['pop_chanplot(''showchan'',gcf);'];
    show_onechan       = ['pop_chanplot(''showchanlist'',gcf);'];
	plot_chan_maps     = ['pop_chanplot(''topoplot'',gcf); '];
    plot_onechan_maps  = ['pop_chanplot(''plotchantopo'',gcf); '];
    plot_chan_ersps    = ['pop_chanplot(''erspplot'',gcf); '];
    plot_onechan_ersps = ['pop_chanplot(''plotchanersp'',gcf); '];
    plot_chan_itcs     = ['pop_chanplot(''itcplot'',gcf); '];
    plot_onechan_itcs  = ['pop_chanplot(''plotchanitc'',gcf); '];
    plot_chan_spectra  = ['pop_chanplot(''specplot'',gcf); '];
    plot_onechan_spectra = ['pop_chanplot(''plotchanspec'',gcf); '];
    plot_chan_erp      = ['pop_chanplot(''erpplot'',gcf); '];
    plot_onechan_erp   = ['pop_chanplot(''plotchanerp'',gcf); '];
    plot_chan_dip      = ['pop_chanplot(''dipplot'',gcf); '];
    plot_onechan_dip   = ['pop_chanplot(''plotchandip'',gcf); '];
    plot_chan_sum      = ['pop_chanplot(''plotsum'',gcf); '];
    plot_onechan_sum   = ['pop_chanplot(''plotonechanum'',gcf); '];
    rename_chan        = ['pop_chanplot(''renamechan'',gcf);'];
    move_onechan       = ['pop_chanplot(''movecomp'',gcf);'];
    move_outlier       = ['pop_chanplot(''moveoutlier'',gcf);'];
    create_chan        = ['pop_chanplot(''createchan'',gcf);'];
    reject_outliers    = ['pop_chanplot(''rejectoutliers'',gcf);'];
    merge_channels     = ['pop_chanplot(''mergechannels'',gcf);'];
    erp_opt            = ['pop_chanplot(''erp_opt'',gcf);'];
    spec_opt           = ['pop_chanplot(''spec_opt'',gcf);'];
    ersp_opt           = ['pop_chanplot(''ersp_opt'',gcf);'];
    create_group       = ['pop_chanplot(''create_group'',gcf);'];
    edit_group         = ['pop_chanplot(''edit_group'',gcf);'];
    delete_group       = ['pop_chanplot(''delete_group'',gcf);'];
    saveSTUDY          = [ 'set(findobj(''parent'', gcbf, ''userdata'', ''save''), ''enable'', fastif(get(gcbo, ''value'')==1, ''on'', ''off''));' ];
    browsesave         = [ '[filename, filepath] = uiputfile2(''*.study'', ''Save STUDY with .study extension -- pop_chan()''); ' ...
                           'set(faindobj(''parent'', gcbf, ''tag'', ''studyfile''), ''string'', [filepath filename]);' ];
    sel_all_chans      = ['pop_chanplot(''sel_all_chans'',gcf);'];

    % list of channel groups
    % ----------------------
    show_options = {};
    for index = 1:length(STUDY.changrp)
        show_options{end+1} = [ 'All ' STUDY.changrp(index).name ];
    end;

    % enable buttons
    % --------------
    filename = STUDY.design(STUDY.currentdesign).cell(1).filebase;
    if exist([filename '.datspec']) , spec_enable = 'on'; else spec_enable  = 'off'; end;
    if exist([filename '.daterp'] )  , erp_enable = 'on'; else  erp_enable  = 'off'; end;
    if exist([filename '.datersp']) , ersp_enable = 'on'; else ersp_enable  = 'off'; end;
    if exist([filename '.datitc'])  ,  itc_enable = 'on'; else  itc_enable  = 'off'; end;

    if isfield(ALLEEG(1).dipfit, 'model'), dip_enable   = 'on'; else dip_enable   = 'off'; end;

    % userdata below
    % --------------
    fig_arg{1}{1} = ALLEEG;
    fig_arg{1}{2} = STUDY;
    fig_arg{1}{3} = STUDY.changrp;
    fig_arg{1}{4} = { STUDY.changrp.name };
    fig_arg{2}    = length(STUDY.changrp);

    geometry = { [4] [1] [0.7 0.3 0.3 0.1 0.9] [1 0.3 1] [1 0.3 1] [1 0.3 1] [1 0.3 1] ...
                 [1 0.3 1] [1 0.3 1] };
    str_name       = sprintf('STUDY name ''%s'' - ''%s''', STUDY.name, STUDY.design(STUDY.currentdesign).name);
    if length(str_name) > 80, str_name = [ str_name(1:80) '...''' ]; end;

    uilist   = { ...
        {'style' 'text' 'string' str_name 'FontWeight' 'Bold' 'HorizontalAlignment' 'center'} {} ...
        {'style' 'text'       'string' 'Select channel to plot' 'FontWeight' 'Bold' } ...
        {'style' 'pushbutton' 'string' 'Sel. all' 'callback' sel_all_chans } {} {} ...
        {'style' 'text'       'string' 'Select subject(s) to plot' 'FontWeight' 'Bold'} ...
        {'style' 'listbox'    'string' show_options 'value' 1 'max' 2 'tag' 'chan_list' 'Callback' show_chan } {} ...
        {'style' 'listbox'    'string' '' 'tag' 'chan_onechan' 'max' 2 'min' 1 'callback'    show_onechan } ...
        {'style' 'pushbutton' 'enable'   erp_enable 'string' 'Plot ERPs' 'Callback' plot_chan_erp} ...
        {'style' 'pushbutton' 'enable'   erp_enable 'string' 'Params' 'Callback' erp_opt }  ...
        {'style' 'pushbutton' 'enable'   erp_enable 'string' 'Plot ERP(s)' 'Callback' plot_onechan_erp} ...
        {'style' 'pushbutton' 'enable'  spec_enable 'string' 'Plot spectra' 'Callback' plot_chan_spectra} ...
        {'style' 'pushbutton' 'enable'  spec_enable 'string' 'Params' 'Callback' spec_opt }  ...
        {'style' 'pushbutton' 'enable'  spec_enable 'string' 'Plot spectra' 'Callback' plot_onechan_spectra} ...
        {'style' 'pushbutton' 'enable'  ersp_enable 'string' 'Plot ERSPs' 'Callback' plot_chan_ersps} ...
        {'vertshift' 'style' 'pushbutton' 'enable'  ersp_enable 'string' 'Params' 'Callback' ersp_opt }  ...
        {'style' 'pushbutton' 'enable'  ersp_enable 'string' 'Plot ERSP(s)' 'Callback' plot_onechan_ersps}...
        {'style' 'pushbutton' 'enable'   itc_enable 'string' 'Plot ITCs' 'Callback' plot_chan_itcs} { }  ...
        {'style' 'pushbutton' 'enable'   itc_enable 'string' 'Plot ITC(s)' 'Callback' plot_onechan_itcs}...
        {'style' 'pushbutton' 'string' 'Plot channel properties' 'Callback' plot_chan_sum} {} ...
        {'style' 'pushbutton' 'string' 'Plot channel properties (soon)' 'Callback' plot_onechan_sum 'enable' 'off'} };

   [out_param userdat] = inputgui( 'geometry' , geometry, 'uilist', uilist, ...
                                   'helpcom', 'pophelp(''pop_chanplot'')', ...
                                   'title', 'View and edit current channels -- pop_chanplot()' , 'userdata', fig_arg, ...
                                   'geomvert', [ 1 0.5 1 5 1 1 1 1 1], 'eval', show_chan );

   if ~isempty(userdat)
       ALLEEG = userdat{1}{1};
       STUDY  = userdat{1}{2};
   end

   % history
   % -------
   com = STUDY.history;
   STUDY.history =  sprintf('%s%s', oldhistory, com);

else
    hdl = varargin{2};  %figure handle
    userdat  = get(varargin{2}, 'userdat');
    ALLEEG   = userdat{1}{1};
    STUDY    = userdat{1}{2};
    cls      = userdat{1}{3};
    allchans = userdat{1}{4};

    changrp = get(findobj('parent', hdl, 'tag', 'chan_list')   , 'value');
    onechan = get(findobj('parent', hdl, 'tag', 'chan_onechan'), 'value');

    switch  varargin{1}

        case {'topoplot', 'erspplot','itcplot','specplot', 'erpplot'}
            changrpstr = allchans(changrp);
            plotting_option = varargin{1};
            plotting_option = [ plotting_option(1:end-4) 'plot' ];
            a = ['STUDY = std_' plotting_option '(STUDY,ALLEEG,''channels'','  vararg2str({changrpstr}) ');' ];
             % update Study history
            eval(a); STUDY.history =  sprintf('%s\n%s',  STUDY.history, a);
            userdat{1}{2} = STUDY;
            set(hdl, 'userdat',userdat);

        case {'plotchantopo', 'plotchanersp','plotchanitc','plotchanspec', 'plotchanerp','plotchandip'}
            changrpstr    = allchans(changrp);
            %if length(changrp) > 1
            %    subject = STUDY.subject{onechan-1};
            %else
            %    changrpstruct = STUDY.changrp(changrp);
            %    allsubjects   = unique({ STUDY.datasetinfo([ changrpstruct.setinds{:} ]).subject });
            %    subject = allsubjects{onechan-1};
            %end;
            plotting_option = varargin{1};
            plotting_option = [ plotting_option(9:end) 'plot' ];
            if onechan(1) ~= 1  % check that not all onechan in channel are requested
                 subject = STUDY.design(STUDY.currentdesign).cases.value{onechan-1};
                 a = ['STUDY = std_' plotting_option '(STUDY,ALLEEG,''channels'','  vararg2str({changrpstr}) ', ''subject'', ''' subject ''' );' ];
                 eval(a); STUDY.history =  sprintf('%s\n%s',  STUDY.history, a);
             else
                a = ['STUDY = std_' plotting_option '(STUDY,ALLEEG,''channels'','  vararg2str({changrpstr}) ', ''plotsubjects'', ''on'' );' ];
                eval(a); STUDY.history =  sprintf('%s\n%s',  STUDY.history, a);
             end;
            userdat{1}{2} = STUDY;
            set(hdl, 'userdat',userdat);

        case 'erp_opt' % save the list of selected channels
            [STUDY com] = pop_erpparams(STUDY);
            if ~isempty(com)
                STUDY.history =  sprintf('%s\n%s',  STUDY.history, com);
            end;
            userdat{1}{2} = STUDY;
            set(hdl, 'userdat',userdat); %update information (STUDY)

        case 'spec_opt' % save the list of selected channels
            [STUDY com] = pop_specparams(STUDY);
            if ~isempty(com)
                STUDY.history =  sprintf('%s\n%s',  STUDY.history, com);
            end;
            userdat{1}{2} = STUDY;
            set(hdl, 'userdat',userdat); %update information (STUDY)

        case 'ersp_opt' % save the list of selected channels
            [STUDY com] = pop_erspparams(STUDY);
            if ~isempty(com)
                STUDY.history =  sprintf('%s\n%s',  STUDY.history, com);
            end;
            userdat{1}{2} = STUDY;
            set(hdl, 'userdat',userdat); %update information (STUDY)

        case 'showchanlist' % save the list of selected channels
            if length(changrp) == 1
                STUDY.changrp(changrp).selected = onechan;
            end;
            userdat{1}{2} = STUDY;
            set(hdl, 'userdat',userdat); %update information (STUDY)

       case 'showchan'
            cind     = get(findobj('parent', hdl, 'tag', 'chan_list')   , 'value');
            changrp  = STUDY.changrp(cind);

            % Find datasets availaible
            % ------------------------
            %setind = STUDY.setind .* (changrp.chaninds > 0); % set to 0 the cell not
            %%                                       % containing any electrode
            %allchansets = unique( setind(find(setind(:))) );

            % Generate channel list
            % ---------------------
            chanid{1} = 'All subjects';
            if length(changrp) == 1
                allsubjects = unique({ STUDY.design(STUDY.currentdesign).cell([ changrp.setinds{:} ]).case });
                for l = 1:length(allsubjects)
                    chanid{end+1} = [ allsubjects{l} ' ' changrp.name ];
                end;
            else
                for l = 1:length(STUDY.design(STUDY.currentdesign).cases.value)
                    chanid{end+1} = [ STUDY.design(STUDY.currentdesign).cases.value{l} ];
                end;
            end;

            selected = 1;
            if isfield(changrp, 'selected') & length(cind) == 1
                if ~isempty(STUDY.changrp(cind).selected)
                    selected = min(STUDY.changrp(cind).selected, 1+length(chanid));
                    STUDY.changrp(cind).selected = selected;
                end;
            end;

            set(findobj('parent', hdl, 'tag', 'chan_onechan'), 'value', selected, 'String', chanid);

        case 'sel_all_chans'
            set(findobj('parent', hdl, 'tag', 'chan_list'), 'value', [1:length(STUDY.changrp)]);

            % Generate channel list
            % ---------------------
            chanid{1} = 'All subjects';
            for l = 1:length(STUDY.design(STUDY.currentdesign).cases.value)
                chanid{end+1} = [ STUDY.design(STUDY.currentdesign).cases.value{l} ' All' ];
            end;
            selected = 1;
            set(findobj('parent', hdl, 'tag', 'chan_onechan'), 'value', selected, 'String', chanid);

        case 'plotsum'
            changrpstr = allchans(changrp);
            [STUDY] = std_propplot(STUDY, ALLEEG, allchans(changrp));
            a = ['STUDY = std_propplot(STUDY, ALLEEG, ' vararg2str({ allchans(changrp) }) ' );'  ];
            STUDY.history =  sprintf('%s\n%s',  STUDY.history, a);
            userdat{1}{2} = STUDY;
            set(hdl, 'userdat',userdat);

        case 'create_group'
            channames = { STUDY.changrp(changrp).name };
            for i=1:length(channames), channames{i} = [ ' ' channames{i} ]; end;
            channamestr = strcat(channames{:});
            res = inputdlg2({ 'Name of channel group', 'Channels to group' }, 'Create channel group', 1, { '' channamestr(2:end) });
            if isempty(res), return; end;
            STUDY.changrp(end+1).name = res{1};
            allchans(end+1)         = { res{1} };
            chanlabels = parsetxt(res{2});
            if length(chanlabels) == 1
                warndlg2('Cannot create a channel group with a single channel');
                return;
            end;
            STUDY.changrp(end).channels = chanlabels;
            tmp = std_chanlookup( STUDY, ALLEEG, STUDY.changrp(end));
            STUDY.changrp(end).chaninds = tmp.chaninds;
            userdat{1}{2} = STUDY;
            userdat{1}{4} = allchans;
            set(hdl, 'userdat',userdat);

            % list of channel groups
            % ----------------------
            tmpobj  = findobj('parent', hdl, 'tag', 'chan_list');
            tmptext = get(tmpobj, 'string');
            tmptext{end+1} = [ 'All ' STUDY.changrp(end).name ];
            set(tmpobj, 'string', tmptext, 'value', length(tmptext));

        case 'edit_group'
            if length(changrp) > 1, return; end;
            if length(STUDY.changrp(changrp).channels) < 2, return; end;
            channames = STUDY.changrp(changrp).channels;
            for i=1:length(channames), channames{i} = [ ' ' channames{i} ]; end;
            channamestr = strcat(channames{:});
            res = inputdlg2({ 'Name of channel group', 'Channels to group' }, 'Create channel group', ...
                            1, { STUDY.changrp(changrp).name channamestr(2:end) });
            if isempty(res), return; end;
            STUDY.changrp(end+1).name = '';
            STUDY.changrp(changrp)    = STUDY.changrp(end);
            STUDY.changrp(end)        = [];
            STUDY.changrp(changrp).name = res{1};
            allchans(changrp)         = { res{1} };
            chanlabels = parsetxt(res{2});
            STUDY.changrp(changrp).channels = chanlabels;
            tmp = std_chanlookup( STUDY, ALLEEG, STUDY.changrp(end));
            STUDY.changrp(changrp).chaninds = tmp.chaninds;
            userdat{1}{2} = STUDY;
            userdat{1}{4} = allchans;
            set(hdl, 'userdat',userdat);

            % list of channel groups
            % ----------------------
            show_options = {};
            for index = 1:length(STUDY.changrp)
                show_options{end+1} = [ 'All ' STUDY.changrp(index).name ];
            end;
            tmpobj  = findobj('parent', hdl, 'tag', 'chan_list');
            set(tmpobj, 'string', show_options, 'value', changrp);

        case 'delete_group'
            if length(changrp) > 1, return; end;
            if length(STUDY.changrp(changrp).channels) < 2, return; end;
            STUDY.changrp(changrp)    = [];

            % list of channel groups
            % ----------------------
            show_options = {};
            for index = 1:length(STUDY.changrp)
                show_options{end+1} = [ 'All ' STUDY.changrp(index).name ];
            end;
            tmpobj  = findobj('parent', hdl, 'tag', 'chan_list');
            set(tmpobj, 'string', show_options, 'value', changrp-1);

        case 'renamechan'
            STUDY.saved = 'no';
            chan_name_list = get(findobj('parent', hdl, 'tag', 'chan_list'), 'String');
            chan_num = get(findobj('parent', hdl, 'tag', 'chan_list'), 'Value') -1;
            if chan_num == 0  % 'all subjects' option
                return;
            end
            % Don't rename 'Notchan' and 'Outliers'  channels.
            if strncmpi('Notchan',STUDY.channel(cls(chan_num)).name,8) | strncmpi('Outliers',STUDY.channel(cls(chan_num)).name,8) | ...
                    strncmpi('Parentchannel',STUDY.channel(cls(chan_num)).name,13)
                warndlg2('The Parentchannel, Outliers, and Notchan channels cannot be renamed');
                return;
			end
            old_name = STUDY.channel(cls(chan_num)).name;
            rename_param  = inputgui( { [1] [1] [1]}, ...
                { {'style' 'text' 'string' ['Rename ' old_name] 'FontWeight' 'Bold'} {'style' 'edit' 'string' '' 'tag' 'chan_rename' } {} }, ...
            '', 'Rename channel - from pop_chanplot()' );
            if ~isempty(rename_param) %if not canceled
                new_name = rename_param{1};
                STUDY = std_renamechan(STUDY, ALLEEG, cls(chan_num), new_name);
                % update Study history
                a = ['STUDY = std_renamechan(STUDY, ALLEEG, ' num2str(cls(chan_num)) ', ' STUDY.channel(cls(chan_num)).name  ');'];
                STUDY.history =  sprintf('%s\n%s',  STUDY.history, a);

                new_name = [ STUDY.channel(cls(chan_num)).name ' (' num2str(length(STUDY.channel(cls(chan_num)).onechan))  ' ICs)'];
                chan_name_list{chan_num+1} = renamechan( chan_name_list{chan_num+1}, new_name);
                set(findobj('parent', hdl, 'tag', 'chan_list'), 'String', chan_name_list);
                set(findobj('parent', hdl, 'tag', 'chan_rename'), 'String', '');
                userdat{1}{2} = STUDY;
                set(hdl, 'userdat',userdat); %update STUDY
            end
    end
end

function newname = renamechan(oldname, newname);

    tmpname = deblank(oldname(end:-1:1));
    strpos  = strfind(oldname, tmpname(end:-1:1));

    newname = [ oldname(1:strpos-1) newname ];


%% To-do
% - Delete rhs_shapefunction -> use funs
% - Clean RHSunfitted
% OK! - switch class(mesh.type) @ RHSfactory
% - filter_pde_density using rhs_unfitted (now rhs_shapefunctionfun*
% - filter_pde_levelset name change for unfitted

%% Questions
% - Filter_P1_LevelSet.getP0fromP1() ??
% - Careful: unfittedmesh needed to properly integrate!!!

%% Results
%

%% Long-term
% - Use FeFunctions in TopOpt_Problem
% - PDE belongs to Optimizer, not ShapeFunctional
% - Micro as three elasticity problems

%% Backlog
% - Move Input folder to a separate repository
% - Geometry only in Mesh

% EXTRAS
%  - Investigate: converting data to binary format to save read'n'write
%                 resources for paraview
%  - Tutorial for printing
%  - Study file ouptut size vs time (GiD/Paraview) to see which is better
%    for printing (test + graph)
%  - Check XY component of fgaussfunctions
% - Recuperar gid unfitted mesh photo GiDimagecapturer
%      density --(project)--> unfittedmesh -> innermesh/photo

% - GiDImageCapturer -> some variables should be "user variables", defined
%   once in a centralized file


function simulate_2spk_mix(data_type, wsj0root, output_dir, fs8k, min_max)
% Simulate 2-speaker mixture data for speaker extraction.
% Call:
%     simulate_2spk_mix(data_type, wsj0root, output_dir, fs8k, min_max)
%     e.g., simulate_2spk_mix('tt', '/media/clx214/data/wsj/', '/media/clx214/data/wsj0_2mix_extr_tmp/wav8k', 8000, 'max')
% Paras:
%     data_type: data set to generate, (tr|cv|tt), e.g., 'tt'
%     wsj0root: YOUR_PATH/, the folder containing converted wsj0/, e.g., '/media/clx214/data/wsj/'
%     output_dir: the folder to save simulated data for extraction, e.g., '/media/clx214/data/wsj0_2mix_extr_tmp/wav8k'
%     fs8k: sampling rate of the simulated data, e.g., 8000
%     min_max: get the mininium or maximum wav length, when simulating mixture data, e.g, 'max'
%
% The code is based on "create_wav_2speakers_extr.m" from "http://www.merl.com/demos/deep-clustering"
%
% 1. Assume that WSJ0's wv1 sphere files is converted to wav files. The folder
%    structure and file name are kept same under wsj0/, e.g.,
%    ORG_PATH/wsj0/si_tr_s/01t/01to030v.wv1 is converted to wav and
%    stored in YOUR_PATH/wsj0/si_tr_s/01t/01to030v.wv1.
%    Relevant data ('si_tr_s', 'si_dt_05' and 'si_et_05') are under YOUR_PATH/wsj0/
% 2. Put 'voicebox' toolbox in current folder. (http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html)
% 3. Set your 'YOUR_PATH' and 'OUTPUT_PATH' properly, then run this script in Matlab.
%    (The max lenght of the wav will be kept when generate the mixture. The sampling rate will be 8kHz.)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Copyright (C) 2016 Mitsubishi Electric Research Labs
%                          (Jonathan Le Roux, John R. Hershey, Zhuo Chen)
%   Apache 2.0  (http://www.apache.org/licenses/LICENSE-2.0)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Copyright 2018 Chenglin Xu, Nanyang Technological University, Singapore
%
%Licensed under the Apache License, Version 2.0 (the "License");
%you may not use this file except in compliance with the License.
%You may obtain a copy of the License at
%
%    http://www.apache.org/licenses/LICENSE-2.0
%
%Unless required by applicable law or agreed to in writing, software
%distributed under the License is distributed on an "AS IS" BASIS,
%WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%See the License for the specific language governing permissions and
%limitations under the License.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ~exist([output_dir '/' min_max '/' data_type],'dir')
    mkdir([output_dir '/' min_max '/' data_type]);
end
mkdir([output_dir  '/' min_max '/' data_type '/s1/']);
mkdir([output_dir  '/' min_max '/' data_type '/aux/']);
mkdir([output_dir  '/' min_max '/' data_type '/mix/']);

TaskFile = ['mix_2_spk_' data_type '_extr.txt'];
fid = fopen(TaskFile,'r');
C = textscan(fid,'%s %f %s %f %s');
num_files = length(C{1});

fprintf(1,'Start to generate data for %s\n', [min_max '_' data_type]);
for i = 1:num_files
    [inwav1_dir,invwav1_name,inwav1_ext] = fileparts(C{1}{i});
    [inwav2_dir,invwav2_name,inwav2_ext] = fileparts(C{3}{i});
    [inwav_aux_dir,invwav_aux_name,inwav_aux_ext] = fileparts(C{5}{i});

    inwav1_snr = C{2}(i);
    inwav2_snr = C{4}(i);
    mix_name = [invwav1_name,'_',num2str(inwav1_snr),'_',invwav2_name,'_',num2str(inwav2_snr),'_',invwav_aux_name];

    % get input wavs
    [s1, fs] = audioread([wsj0root C{1}{i}]);
    s2       = audioread([wsj0root C{3}{i}]);
    s_aux    = audioread([wsj0root C{5}{i}]);

    % resample, normalize to 8 kHz file
    s1_8k = resample(s1,fs8k,fs);
    [s1_8k,lev1] = activlev(s1_8k,fs8k,'n'); % y_norm = y /sqrt(lev);
    s2_8k = resample(s2,fs8k,fs);
    [s2_8k,lev2] = activlev(s2_8k,fs8k,'n');
    s_aux_8k = resample(s_aux,fs8k,fs);
    [s_aux_8k,lev_aux] = activlev(s_aux_8k,fs8k,'n');

    weight_1 = 10^(inwav1_snr/20);
    weight_2 = 10^(inwav2_snr/20);

    s1_8k = weight_1 * s1_8k;
    s2_8k = weight_2 * s2_8k;

    switch min_max
        case 'max'
            mix_8k_length = max(length(s1_8k),length(s2_8k));
            s1_8k = cat(1,s1_8k,zeros(mix_8k_length - length(s1_8k),1));
            s2_8k = cat(1,s2_8k,zeros(mix_8k_length - length(s2_8k),1));
        case 'min'
            mix_8k_length = min(length(s1_8k),length(s2_8k));
            s1_8k = s1_8k(1:mix_8k_length);
            s2_8k = s2_8k(1:mix_8k_length);
    end
    mix_8k = s1_8k + s2_8k;

    max_amp_8k = max(cat(1,abs(mix_8k(:)),abs(s1_8k(:)),abs(s2_8k(:)),abs(s_aux_8k(:))));
    mix_scaling_8k = 1/max_amp_8k*0.9;
    s1_8k = mix_scaling_8k * s1_8k;
    mix_8k = mix_scaling_8k * mix_8k;
    s_aux_8k = mix_scaling_8k * s_aux_8k;

    audiowrite([output_dir '/' min_max '/' data_type '/s1/' mix_name '.wav'],s1_8k,fs8k);
    audiowrite([output_dir '/' min_max '/' data_type '/aux/' mix_name '.wav'],s_aux_8k,fs8k);
    audiowrite([output_dir '/' min_max '/' data_type '/mix/' mix_name '.wav'],mix_8k,fs8k);
end
fclose(fid);
fprintf(1,'End of generating data for %s\n', [min_max '_' data_type]);
end


function [align] = mamarama(in,config)

Cst = in.Cst;
Rot = in.Rot;

Octave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
if ~Octave,
  drawscene(in.Xe,Cst',Rot,41,'cloud','Graphical Output Validation: View from top or bottom (no sRt)',config.cal.cams2use);
end

% definition of the absolute world frame (in cm)

%% for hummingbird cage
%
%cam(3).C = [168, 173, 185]';
%cam(4).C = [142, 76, 89]';
%cam(1).C = [81, 72, 165]';
%cam(2).C = [22,  15, 176]';
%cam(5).C = [53.5, 175, 78]';

% definition of the absolute world frame (in mm) (Doug's cube)

% cam(1).C = [10, 390, 600]';
% cam(2).C = [460,  380, 600]';
% cam(3).C = [430, 115, 580]';
% cam(4).C = [0, 0, 610]';
% cam(5).C = [240, 150, 610]';

% definition of the absolute world frame (in mm) (windtunnel June 2005)

%cam(1).C = [1510, 180, 500+322]';
%cam(2).C = [1140,  260, 450+322]';
%cam(3).C = [1040, 25, 480+322]';
%cam(4).C = [490, 160, 390+322]';
%cam(5).C = [900, 25, 400+322]';

% the above numbers (from June 2005) transformed to acheive better
% agreement (July 3, 2005)

%cam(1).C = [1585.0266, 157.1808, 725.3687]';
%cam(2).C = [1221.0045, 230.7819, 717.1524]';
%cam(3).C = [1123.2774, -2.1424, 731.8758]';
%cam(4).C = [580.8019, 124.5745, 705.3737]';
%cam(5).C = [978.8653, 3.3878, 663.1311]';

% cal July 5, 2005

%cam(1).C = [1710, 375, 820]';
%cam(2).C = [1110, 316, 800]';
%cam(3).C = [1110, 100, 466]';
%cam(4).C = [-380, 290, 875]';
%cam(5).C = [440, 15, 680]';

% touch-up July 6, 2005 (5c)

%cam(1).C = [1853.5325, 228.1160, 971.5365]';
%cam(2).C = [1202.0386, 199.5437, 953.7752]';
%cam(3).C = [1183.3920, 41.6797, 537.2034]';
%cam(4).C = [-406.8365, 229.6378, 1073.0449]';
%cam(5).C = [460.2840, -60.0974, 778.1218]';

% touch-up Aug 22, 2005 (touch-up from A applied to B)

%cam(1).C = cam(1).C - [130.0*0.83, 0., 110.0*0.83]';
%cam(2).C = cam(2).C - [130.0*0.83, 0., 110.0*0.83]';
%cam(3).C = cam(3).C - [130.0*0.83, 0., 110.0*0.83]';
%cam(4).C = cam(4).C - [130.0*0.83, 0., 110.0*0.83]';
%cam(5).C = cam(5).C - [130.0*0.83, 0., 110.0*0.83]';

% touch-up Jan 27, 2006 ( hacked-in estimate )

%cam(1).C = cam(1).C - [130.0*0.83+20.0, 0., 110.0*0.83]';
%cam(2).C = cam(2).C - [130.0*0.83+10.0, 0., 110.0*0.83]';
%cam(3).C = cam(3).C - [130.0*0.83+10.0, 0., 110.0*0.83]';
%cam(4).C = cam(4).C - [130.0*0.83, 0., 110.0*0.83]';
%cam(5).C = cam(5).C - [130.0*0.83, 0., 110.0*0.83]';

% new Feb 14, 2006

%cam(1).C = [1760, 190, 850]';
%cam(2).C = [1060, 260, 785]';
%cam(3).C = [1070, 70, 470 ]';
%cam(4).C = [-380, 230, 873]';
%cam(5).C = [380, -10, 670]';

% moved cam 2006 03 07 19:45

%cam(1).C = [1760, 190, 850]';
%cam(2).C = [1060, 260, 785]';
%cam(3).C = [1070, 190, 510 ]';
%cam(4).C = [-380, 230, 873]';
%cam(5).C = [380, 120, 670]';

if 1,

% 2006 03 31

%cam(1).C = [640, -105, 170]';
%cam(2).C = [1220, 172, 912]';
%cam(3).C = [475, 172, 580 ]';
%cam(4).C = [-350, 172, 932]';
%cam(5).C = [320, -105, 170]';

% 2006 04 03e

%cam(3).C = [380, 240, 530 ]';

% 2006 04 04a

%cam(4).C = [-115, 220, 800]';
end

% 2006 09 14

%cam(1).C = [685, 220, 775]';
%cam(2).C = [1220, 172, 912]';
%cam(3).C = [380, 240, 530 ]';
%cam(4).C = [-115, 220, 800]';
%cam(5).C = [180, 240, 740]';

%% 2006 10 23 (from DLT)
%cam(1).C = [ 651.7963301   169.95615265  749.51356735]';
%cam(2).C = [ 1090.40810501   190.82014771   866.8980674 ]';
%cam(3).C = [ 327.41874025  237.43022637  539.80942371]';
%cam(4).C = [-241.364468    194.27934314  767.35249515]';
%cam(5).C = [ 147.20028399  171.45774441  725.08522715]';

% 2006 12 19 riverside hummingbirds
% estimates from DLT
%cam(1).C = [976.22314128 -2289.85157108   489.78104927]';
%cam(2).C = [-17278.94752856   1794.85211148  15855.51116479]';
%cam(3).C = [ 2266.45466953  2590.2964507    -74.53159994]';
%cam(4).C = [-329.01069449  796.46308431  624.04191004]';

% hand measurements
%ft2mm = 12*25.4;
%cam(1).C = [ 1350 -1580 540]';
%cam(2).C = [ -450 770 (750+5*ft2mm) ]';
%cam(3).C = [ 1500 -1600 2250]';
%cam(4).C = [-280 820 570]';

% 2006 12 01 DLT in flydra WT
%cam(1).C = [867.37446261  198.97798549  832.51824736]';
%cam(2).C = [1194.97461953 206.718389208 898.66723201]';
%cam(3).C = [459.669418616 168.068949056 713.890827]';
%cam(4).C = [-186.0833207 207.892635698 775.298571621]';
%cam(5).C = [115.230367611 202.584140956 904.723766629]';

% 2007 10 03 DLT in flydra WT
%cam(1).C = [ 985.827871     182.49267738  1134.97921258]';
%cam(2).C = [362.27817754   189.63204145  1134.23310277]';
%cam(3).C = [721.10852967  167.31210242  932.32564817]';
%cam(4).C = [-330.47764957   168.38518854  1014.81801646]';
%cam(5).C = [1774.53765317   144.79725152   928.10954671]';

% 2007 11 16 mamarama
%cam(1).C = ([20     25  30]*25.4)';
%cam(2).C = ([26   36  33]*25.4)';
%cam(3).C = ([26  -36  31]*25.4)';
%cam(4).C = ([74   2  30]*25.4)';
%cam(5).C = ([17   -25   30]*25.4)';

% 2007 12 18 mamarama
% cam(1).C = [130   450    705]';
% cam(2).C = [0     660    665]';
% cam(3).C = [1460  640    665]';
% cam(4).C = [685   1865   710]';
% cam(5).C = [1315  480    715]';

% % 2008 03 12 mamarama
% cam(1).C = [710   1350    785]';
% cam(2).C = [795   1255    785]';
% cam(3).C = [-610   1255    790]';
% cam(4).C = [-505   1355    790]';
% cam(5).C = [110   -355     785]';
% cam(6).C = [-850   305     785]'; %mama07.2
% cam(7).C = [-810   190     785]';
% cam(8).C = [1020   305     785]'; %mama08.2
% cam(9).C = [980   190     785]';

% 2008 04 17 mamarama
cam(1).C = [645   -265    760]';    %mama01
cam(2).C = [640   1250    760]';    %mama02
cam(3).C = [-455  1260    780]';   %mama03
cam(4).C = [-450  -295    780]';   %mama04
cam(5).C = [100   495     805]';   %mama05
cam(6).C = [85    1460    780]';   %mama06_0
cam(7).C = [85    -470    780]';   %mama06_1
cam(8).C = [-745  920     775]';   %mama07_0
cam(9).C = [-685  -65     775]';    %mama07_1
cam(10).C = [890   1030    775]';  %mama08_0
cam(11).C = [935   80     775]';   %mama08_1

% of the similarity computation

[align.simT.s, align.simT.R, align.simT.t]  = estsimt([Cst'],[cam(:).C]);
[align.P, align.X] = align3d(in.Pe,in.Xe,align.simT);
% save aligned data
if 1 % SAVE_STEPHI | SAVE_PGUHA
	[align.Cst,align.Rot] = savecalpar(align.P,config);
end

if ~Octave,
  drawscene(align.X,align.Cst',align.Rot,61,'cloud','Graphical Output Validation: Aligned data',config.cal.cams2use);

  set(gca,'CameraTarget',[0,0,0]);
  set(gca,'CameraPosition',[0,0,1]);

  figure(61),
  % print -depsc graphevalaligned.eps
  eval(['print -depsc ', config.paths.data, 'topview.eps'])

  drawscene(align.X,align.Cst',align.Rot,62,'cloud','Graphical Output Validation: Aligned data',config.cal.cams2use);

  set(gca,'CameraTarget',[0,0,0.9]);
  set(gca,'CameraPosition',[2,0,0.9]);

  %figure(62),
  % print -depsc graphevalaligned.eps
  %eval(['print -depsc ', config.paths.data, 'sideview.eps'])
end

return


% ------------------------------------------------------------------------
%  Copyright (C)
%  Universitat Politecnica de Catalunya BarcelonaTech (UPC) - Spain
%  University of California Berkeley (UCB) - USA
%
%  Jordi Pont-Tuset <jordi.pont@upc.edu>
%  Pablo Arbelaez <arbelaez@berkeley.edu>
%  June 2014
% ------------------------------------------------------------------------
% This file is part of the MCG package presented in:
%    Arbelaez P, Pont-Tuset J, Barron J, Marques F, Malik J,
%    "Multiscale Combinatorial Grouping,"
%    Computer Vision and Pattern Recognition (CVPR) 2014.
% Please consider citing the paper if you use this code.
% ------------------------------------------------------------------------
%
% Create random divisions of the training set for cross-validation
%
% ------------------------------------------------------------------------

% Create random divisions of the training set
id = '3';
percentage_train = 0.5;
full_set = 'train2012';

% Get full ids
im_ids = database_ids('pascal2012',full_set);

ids_a = sort(randperm(length(im_ids),floor(percentage_train*length(im_ids))));
ids_b = setdiff(1:length(im_ids),ids_a);
im_ids_a = im_ids(ids_a);
im_ids_b = im_ids(ids_b);

% Write to file
file_a = fullfile(mcg_root,'datasets', 'pascal2012','gt_sets',[full_set '_' id 'a.txt']);
file_b = fullfile(mcg_root,'datasets', 'pascal2012','gt_sets',[full_set '_' id 'b.txt']);

dlmwrite(file_a,im_ids_a,'')
dlmwrite(file_b,im_ids_b,'')


function [headmodel, sens] = ft_prepare_vol_sens(headmodel, sens, varargin)

% FT_PREPARE_VOL_SENS does some bookkeeping to ensure that the volume
% conductor model and the sensor array are ready for subsequent forward
% leadfield computations. It takes care of some pre-computations that can
% be done efficiently prior to the leadfield calculations.
%
% Use as
%   [headmodel, sens] = ft_prepare_vol_sens(headmodel, sens, ...)
% with input arguments
%   headmodel = structure with volume conductor definition
%   sens      = structure with gradiometer or electrode definition
%
% The headmodel structure represents a volume conductor model of the head,
% its contents depend on the type of model. It is described in more detail
% in FT_DATATYPE_HEADMODEL. The sens structure represents a electrode or
% gradiometer array. It is described in more detail in FT_DATATYPE_SENS.
%
% Additional options should be specified in key-value pairs and can be
%   'channel'  = cell-array with strings (default = 'all')
%
% The detailed behavior of this function depends on whether the input
% consists of EEG or MEG and furthermoree depends on the type of volume
% conductor model:
% - in case of EEG single and concentric sphere models, the electrodes are
%   projected onto the skin surface.
% - in case of EEG boundary element models, the electrodes are projected on
%   the surface and a blilinear interpoaltion matrix from vertices to
%   electrodes is computed.
% - in case of MEG and a localspheres model, a local sphere is determined
%   for each coil in the gradiometer definition.
%  - in case of MEG with a singleshell Nolte model, the volume conduction
%    model is initialized
% In any case channel selection and reordering will be done. The channel
% order returned by this function corresponds to the order in the 'channel'
% option, or if not specified, to the order in the input sensor array.
%
% See also FT_COMPUTE_LEADFIELD, FT_READ_HEADMODEL, FT_READ_SENS

% Copyright (C) 2004-2015, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

if iscell(headmodel) && iscell(sens)
  % this represents combined EEG, ECoG and/or MEG
  for i=1:numel(headmodel)
    [headmodel{i}, sens{i}] = ft_prepare_vol_sens(headmodel{i}, sens{i}, varargin{:});
  end
  return
end

% get the optional input arguments
% fileformat = ft_getopt(varargin, 'fileformat');
channel = ft_getopt(varargin, 'channel', sens.label);   % cell-array with channel labels, default is all

% ensure that the sensor description is up-to-date (Aug 2011)
sens = ft_datatype_sens(sens);

% this is to support volumes saved in mat-files, particularly interpolated
if ischar(headmodel)
  vpath     = fileparts(headmodel);   % remember the path to the file
  headmodel = ft_read_headmodel(headmodel); % replace the filename with the content of the file
end

% ensure that the volume conduction description is up-to-date (Jul 2012)
headmodel = ft_datatype_headmodel(headmodel);

% determine whether the input contains EEG or MEG sensors
iseeg = ft_senstype(sens, 'eeg');
ismeg = ft_senstype(sens, 'meg');

% determine the skin compartment
if ~isfield(headmodel, 'skin_surface')
  if isfield(headmodel, 'bnd')
    headmodel.skin_surface   = find_outermost_boundary(headmodel.bnd);
  elseif isfield(headmodel, 'r') && length(headmodel.r)<=4
    [dum, headmodel.skin_surface] = max(headmodel.r);
  end
end

% determine the inner_skull_surface compartment
if ~isfield(headmodel, 'inner_skull_surface')
  if isfield(headmodel, 'bnd')
    headmodel.inner_skull_surface  = find_innermost_boundary(headmodel.bnd);
  elseif isfield(headmodel, 'r') && length(headmodel.r)<=4
    [dum, headmodel.inner_skull_surface] = min(headmodel.r);
  end
end

% FT_HEADMODELTYPE to an empty struct won't work further down
if isempty(headmodel)
  headmodel = [];
end

% this makes them easier to recognise
sens.type       = ft_senstype(sens);
headmodel.type  = ft_headmodeltype(headmodel);

if isfield(headmodel, 'unit') && isfield(sens, 'unit') && ~strcmp(headmodel.unit, sens.unit)
  ft_error('inconsistency in the units of the volume conductor and the sensor array');
end

if ismeg && iseeg
  % this is something that could be implemented relatively easily
  ft_error('simultaneous EEG and MEG not yet supported');

elseif ~ismeg && ~iseeg
  ft_error('the input does not look like EEG, nor like MEG');

elseif ismeg

  % always ensure that there is a linear transfer matrix for combining the coils into gradiometers
  if ~isfield(sens, 'tra');
    Nchans = length(sens.label);
    Ncoils = size(sens.coilpos,1);
    if Nchans~=Ncoils
      ft_error('inconsistent number of channels and coils');
    end
    sens.tra = eye(Nchans, Ncoils);
  end

  if ~ft_headmodeltype(headmodel, 'localspheres')
    % select the desired channels from the gradiometer array
    [selchan, selsens] = match_str(channel, sens.label);
    % only keep the desired channels, order them according to the users specification
    try, sens.chantype = sens.chantype(selsens,:); end
    try, sens.chanunit = sens.chanunit(selsens,:); end
    try, sens.chanpos  = sens.chanpos (selsens,:); end
    try, sens.chanori  = sens.chanori (selsens,:); end
    sens.label    = sens.label(selsens);
    sens.tra      = sens.tra(selsens,:);
  else
    % for the localspheres model it is done further down
  end

  % remove the coils that do not contribute to any channel output
  selcoil      = any(sens.tra~=0,1);
  sens.coilpos = sens.coilpos(selcoil,:);
  sens.coilori = sens.coilori(selcoil,:);
  sens.tra     = sens.tra(:,selcoil);

  switch ft_headmodeltype(headmodel)
    case {'infinite' 'infinite_monopole' 'infinite_currentdipole' 'infinite_magneticdipole'}
      % nothing to do

    case 'singlesphere'
      % nothing to do

    case 'concentricspheres'
      % nothing to do

    case 'neuromag'
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % if the forward model is computed using the external Neuromag toolbox,
      % we have to add a selection of the channels so that the channels
      % in the forward model correspond with those in the data.
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      [selchan, selsens] = match_str(channel, sens.label);
      headmodel.chansel = selsens;

    case 'localspheres'
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % If the volume conduction model consists of multiple spheres then we
      % have to match the channels in the gradiometer array and the volume
      % conduction model.
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

      % the initial localspheres volume conductor has a local sphere per
      % channel, whereas it should have a local sphere for each coil
      if size(headmodel.r,1)==size(sens.coilpos,1) && ~isfield(headmodel, 'label')
        % it appears that each coil already has a sphere, which suggests
        % that the volume conductor already has been prepared to match the
        % sensor array
        return
      elseif size(headmodel.r,1)==size(sens.coilpos,1) && isfield(headmodel, 'label')
        if ~isequal(headmodel.label(:), sens.label(:))
          % if only the order is different, it would be possible to reorder them
          ft_error('the coils in the volume conduction model do not correspond to the sensor array');
        else
          % the coil-specific spheres in the volume conductor should not have a label
          % because the label is already specified for the coils in the
          % sensor array
          headmodel = rmfield(headmodel, 'label');
        end
        return
      end

      % the CTF way of representing the headmodel is one-sphere-per-channel
      % whereas the FieldTrip way of doing the forward computation is one-sphere-per-coil
      Nchans   = size(sens.tra,1);
      Ncoils   = size(sens.tra,2);
      Nspheres = size(headmodel.label);

      if isfield(headmodel, 'orig')
        % these are present in a CTF *.hdm file
        singlesphere.o(1,1) = headmodel.orig.MEG_Sphere.ORIGIN_X;
        singlesphere.o(1,2) = headmodel.orig.MEG_Sphere.ORIGIN_Y;
        singlesphere.o(1,3) = headmodel.orig.MEG_Sphere.ORIGIN_Z;
        singlesphere.r      = headmodel.orig.MEG_Sphere.RADIUS;
        % ensure consistent units
        singlesphere = ft_convert_units(singlesphere, headmodel.unit);
        % determine the channels that do not have a corresponding sphere
        % and use the globally fitted single sphere for those
        missing = setdiff(sens.label, headmodel.label);
        if ~isempty(missing)
          ft_warning('using the global fitted single sphere for %d channels that do not have a local sphere', length(missing));
        end
        for i=1:length(missing)
          headmodel.label(end+1) = missing(i);
          headmodel.r(end+1,:)   = singlesphere.r;
          headmodel.o(end+1,:)   = singlesphere.o;
        end
      end

      % make a new structure that only holds the local spheres, one per coil
      localspheres = [];
      localspheres.type = headmodel.type;
      localspheres.unit = headmodel.unit;

      % for each coil in the MEG helmet, determine the corresponding channel and from that the corresponding local sphere
      for i=1:Ncoils
        coilindex = find(sens.tra(:,i)~=0); % to which channel does this coil belong
        if length(coilindex)>1
          % this indicates that there are multiple channels to which this coil contributes,
          % which happens if the sensor array represents a synthetic higher-order gradient.
          [dum, coilindex] = max(abs(sens.tra(:,i)));
        end

        coillabel = sens.label{coilindex};               % what is the label of this channel
        chanindex = find(strcmp(coillabel, headmodel.label));  % what is the index of this channel in the list of local spheres
        localspheres.r(i,:) = headmodel.r(chanindex);
        localspheres.o(i,:) = headmodel.o(chanindex,:);
      end
      headmodel = localspheres;

      % finally do the selection of channels and coils
      % order them according to the users specification
      [selchan, selsens] = match_str(channel, sens.label);

      % first only modify the linear combination of coils into channels
      try, sens.chantype = sens.chantype(selsens,:); end
      try, sens.chanunit = sens.chanunit(selsens,:); end
      try, sens.chanpos  = sens.chanpos (selsens,:); end
      try, sens.chanori  = sens.chanori (selsens,:); end
      sens.label   = sens.label(selsens);
      sens.tra     = sens.tra(selsens,:);
      % subsequently remove the coils that do not contribute to any sensor output
      selcoil      = find(sum(sens.tra,1)~=0);
      sens.coilpos = sens.coilpos(selcoil,:);
      sens.coilori = sens.coilori(selcoil,:);
      sens.tra     = sens.tra(:,selcoil);
      % make the same selection of coils in the localspheres model
      headmodel.r = headmodel.r(selcoil);
      headmodel.o = headmodel.o(selcoil,:);

    case 'singleshell'
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % if the forward model is computed using the code from Guido Nolte, we
      % have to initialize the volume model using the gradiometer coil
      % locations
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

      % compute the surface normals for each vertex point
      if ~isfield(headmodel.bnd, 'nrm')
        fprintf('computing surface normals\n');
        headmodel.bnd.nrm = normals(headmodel.bnd.pos, headmodel.bnd.tri);
      end

      % estimate center and radius
      [center, radius] = fitsphere(headmodel.bnd.pos);

      % order of spherical spherical harmonics, for 'real' realistic volume conductors order=10 is o.k
      if isfield(headmodel, 'order')
        order = headmodel.order;
      else
        order = 10;
      end

      % initialize the forward calculation (only if  coils are available)
      if size(sens.coilpos,1)>0 && ~isfield(headmodel, 'forwpar')
        s = ft_scalingfactor(headmodel.unit, 'cm');
        headmodel.forwpar = meg_ini([s*headmodel.bnd.pos headmodel.bnd.nrm], s*center', order, [s*sens.coilpos sens.coilori]);
        headmodel.forwpar.scale = s;
      end

    case  'openmeeg'
        % don't do anything, h2em or h2mm generated later in ft_prepare_leadfield

    case 'simbio'
      ft_error('MEG not yet supported with simbio');

    otherwise
      ft_error('unsupported volume conductor model for MEG');
  end

elseif iseeg

  % the electrodes are used, the channel positions are not relevant any more
  % channel positinos need to be recomputed after projecting the electrodes on the skin
  if isfield(sens, 'chanpos'); sens = rmfield(sens, 'chanpos'); end

  % select the desired channels from the electrode array
  % order them according to the users specification
  [selchan, selsens] = match_str(channel, sens.label);
  Nchans = length(sens.label);

  sens.label     = sens.label(selsens);
  try, sens.chantype  = sens.chantype(selsens); end
  try, sens.chanunit  = sens.chanunit(selsens); end

  if isfield(sens, 'tra')
    % first only modify the linear combination of electrodes into channels
    sens.tra     = sens.tra(selsens,:);
    % subsequently remove the electrodes that do not contribute to any channel output
    selelec      = any(sens.tra~=0,1);
    sens.elecpos = sens.elecpos(selelec,:);
    sens.tra     = sens.tra(:,selelec);
  else
    % the electrodes and channels are identical
    sens.elecpos = sens.elecpos(selsens,:);
  end

  switch ft_headmodeltype(headmodel)
    case {'infinite' 'infinite_monopole' 'infinite_currentdipole'}
      % nothing to do

    case {'halfspace', 'halfspace_monopole'}
      % electrodes' all-to-all distances
      numelec = size(sens.elecpos,1);
      ref_el = sens.elecpos(1,:);
      md = dist( (sens.elecpos-repmat(ref_el,[numelec 1]))' );
      % take the min distance as reference
      md = min(md(1,2:end));
      pos = sens.elecpos;
      % scan the electrodes and reposition the ones which are in the
      % wrong halfspace (projected on the plane)... if not too far away!
      for i=1:size(pos,1)
        P = pos(i,:);
        is_in_empty = acos(dot(headmodel.ori,(P-headmodel.pos)./norm(P-headmodel.pos))) < pi/2;
        if is_in_empty
          dPplane = abs(dot(headmodel.ori, headmodel.pos-P, 2));
          if dPplane>md
            ft_error('Some electrodes are too distant from the plane: consider repositioning them')
          else
            % project point on plane
            Ppr = pointproj(P,[headmodel.pos headmodel.ori]);
            pos(i,:) = Ppr;
          end
        end
      end
      sens.elecpos = pos;

    case {'slab_monopole'}
      % electrodes' all-to-all distances
      numelc  = size(sens.elecpos,1);
      ref_elc = sens.elecpos(1,:);
      md  = dist( (sens.elecpos-repmat(ref_elc,[numelc 1]))' );
      % choose min distance between electrodes
      md  = min(md(1,2:end));
      pos = sens.elecpos;
      % looks for contacts outside the strip which are not too far away
      % and projects them on the nearest plane
      for i=1:size(pos,1)
        P = pos(i,:);
        instrip1 = acos(dot(headmodel.ori1,(P-headmodel.pos1)./norm(P-headmodel.pos1))) > pi/2;
        instrip2 = acos(dot(headmodel.ori2,(P-headmodel.pos2)./norm(P-headmodel.pos2))) > pi/2;
        is_in_empty = ~(instrip1&instrip2);
        if is_in_empty
          dPplane1 = abs(dot(headmodel.ori1, headmodel.pos1-P, 2));
          dPplane2 = abs(dot(headmodel.ori2, headmodel.pos2-P, 2));
          if dPplane1>md && dPplane2>md
            ft_error('Some electrodes are too distant from the planes: consider repositioning them')
          elseif dPplane2>dPplane1
            % project point on nearest plane
            Ppr = pointproj(P,[headmodel.pos1 headmodel.ori1]);
            pos(i,:) = Ppr;
          else
            % project point on nearest plane
            Ppr = pointproj(P,[headmodel.pos2 headmodel.ori2]);
            pos(i,:) = Ppr;
          end
        end
      end
      sens.elecpos = pos;

    case {'singlesphere', 'concentricspheres'}
      % ensure that the electrodes ly on the skin surface
      radius = max(headmodel.r);
      pos    = sens.elecpos;
      if isfield(headmodel, 'o')
        % shift the the centre of the sphere to the origin
        pos(:,1) = pos(:,1) - headmodel.o(1);
        pos(:,2) = pos(:,2) - headmodel.o(2);
        pos(:,3) = pos(:,3) - headmodel.o(3);
      end
      distance = sqrt(sum(pos.^2,2)); % to the center of the sphere
      if any((abs(distance-radius)/radius)>0.005)
        ft_warning('electrodes do not lie on skin surface -> using radial projection')
      end
      pos = pos * radius ./ [distance distance distance];
      if isfield(headmodel, 'o')
        % shift the center back to the original location
        pos(:,1) = pos(:,1) + headmodel.o(1);
        pos(:,2) = pos(:,2) + headmodel.o(2);
        pos(:,3) = pos(:,3) + headmodel.o(3);
      end
      sens.elecpos = pos;

    case {'bem', 'dipoli', 'asa', 'bemcp'}
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % do postprocessing of volume and electrodes in case of BEM model
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

      % project the electrodes on the skin and determine the bilinear interpolation matrix
      if ~isfield(headmodel, 'tra') && (isfield(headmodel, 'mat') && ~isempty(headmodel.mat))
        % determine boundary corresponding with skin and inner_skull_surface
        if ~isfield(headmodel, 'skin_surface')
          headmodel.skin_surface = find_outermost_boundary(headmodel.bnd);
          fprintf('determining skin compartment (%d)\n', headmodel.skin_surface);
        end
        if ~isfield(headmodel, 'source')
          headmodel.source = find_innermost_boundary(headmodel.bnd);
          fprintf('determining source compartment (%d)\n', headmodel.source);
        end
        if size(headmodel.mat,1)~=size(headmodel.mat,2) && size(headmodel.mat,1)==length(sens.elecpos)
          fprintf('electrode transfer and system matrix were already combined\n');
        else
          fprintf('projecting electrodes on skin surface\n');
          % compute linear interpolation from triangle vertices towards electrodes
          [el, prj] = project_elec(sens.elecpos, headmodel.bnd(headmodel.skin_surface).pos, headmodel.bnd(headmodel.skin_surface).tri);
          tra       = transfer_elec(headmodel.bnd(headmodel.skin_surface).pos, headmodel.bnd(headmodel.skin_surface).tri, el);

          % replace the original electrode positions by the projected positions
          sens.elecpos = prj;

          if size(headmodel.mat,1)==size(headmodel.bnd(headmodel.skin_surface).pos,1)
            % construct the transfer from only the skin vertices towards electrodes
            interp = tra;
          else
            % construct the transfer from all vertices (also inner_skull_surface/outer_skull_surface) towards electrodes
            interp = [];
            for i=1:length(headmodel.bnd)
              if i==headmodel.skin_surface
                interp = [interp, tra];
              else
                interp = [interp, zeros(size(el,1), size(headmodel.bnd(i).pos,1))];
              end
            end
          end

          % incorporate the linear interpolation matrix and the system matrix into one matrix
          % this speeds up the subsequent repeated leadfield computations
          fprintf('combining electrode transfer and system matrix\n');

          % convert to sparse matrix to speed up the subsequent multiplication
          interp  = sparse(interp);
          headmodel.mat = interp * headmodel.mat;
          % ensure that the model potential will be average referenced
          avg = mean(headmodel.mat, 1);
          headmodel.mat = headmodel.mat - repmat(avg, size(headmodel.mat,1), 1);
        end
      end
    case  'openmeeg'
        % don't do anything, h2em or h2mm generated later in ft_prepare_leadfield

    case 'fns'
      if isfield(headmodel,'bnd')
        [el, prj] = project_elec(sens.elecpos, headmodel.bnd.pos, headmodel.bnd.tri);
        sens.tra = transfer_elec(headmodel.bnd.pos, headmodel.bnd.tri, el);
        % replace the original electrode positions by the projected positions
        sens.elecpos = prj;
      end

    case 'simbio'
      % check that the external toolbox is present
      ft_hastoolbox('simbio', 1);

      % extract the outer surface
      bnd = mesh2edge(headmodel);
      for j=1:length(sens.label)
        d = bsxfun(@minus, bnd.pos, sens.elecpos(j,:));
        [d, i] = min(sum(d.^2, 2));
        % replace the position of each electrode by the closest vertex
        sens.elecpos(j,:) = bnd.pos(i,:);
      end

      if (isfield(headmodel,'transfer') && isfield(headmodel,'elec'))
          if all(ismember(sens.label,headmodel.elec.label))
              [sensmember, senslocation] = ismember(sens.label,headmodel.elec.label);
              if (norm(sens.elecpos - headmodel.elec.elecpos(senslocation,:))<1e-8)
                  headmodel.transfer = headmodel.transfer(senslocation,:);
                  headmodel.elec = sens;
              else
                  ft_error('Electrode positions do not fit to the given transfer matrix!');
              end
          else
              ft_error('Transfer matrix does not fit the given set of electrodes!');
          end
      else
          headmodel.transfer = sb_transfer(headmodel,sens);
          headmodel.elec = sens;
      end

    case 'interpolate'
      % this is to allow moving leadfield files
      if ~exist(headmodel.filename{1}, 'file')
        for i = 1:length(headmodel.filename)
          [p, f, x] = fileparts(headmodel.filename{i});
          headmodel.filename{i} = fullfile(vpath, [f x]);
        end
      end

      matchlab = isequal(sens.label, headmodel.sens.label);
      matchpos = isequal(sens.elecpos, headmodel.sens.elecpos);
      matchtra = (~isfield(sens, 'tra') && ~isfield(headmodel.sens, 'tra')) || isequal(sens.tra, headmodel.sens.tra);

      if matchlab && matchpos && matchtra
        % the input sensor array matches precisely with the forward model
        % no further interpolation is needed
      else
        % interpolate the channels in the forward model to the desired channels
        filename = tempname;
        headmodel  = ft_headmodel_interpolate(filename, sens, headmodel);
        % update the sensor array with the one from the volume conductor
        sens = headmodel.sens;
      end % if recomputing interpolation

      % for the leadfield computations the @nifti object is used to map the image data into memory
      ft_hastoolbox('spm8up', 1);
      for i=1:length(headmodel.sens.label)
        % map each of the leadfield files into memory
        headmodel.chan{i} = nifti(headmodel.filename{i});
      end

    otherwise
      ft_error('unsupported volume conductor model for EEG');
  end

  % FIXME this needs careful thought to ensure that the average referencing which is now done here and there, and that the linear interpolation in case of BEM are all dealt with consistently
  % % always ensure that there is a linear transfer matrix for
  % % rereferencing the EEG potential
  % if ~isfield(sens, 'tra');
  %   sens.tra = eye(length(sens.label));
  % end

  % update the channel positions as the electrodes were projected to the skin surface
  [pos, ori, lab] = channelposition(sens);
  [selsens, selpos] = match_str(sens.label, lab);
  sens.chanpos = nan(length(sens.label),3);
  sens.chanpos(selsens,:) = pos(selpos,:);

end % if iseeg or ismeg

if isfield(sens, 'tra')
  if issparse(sens.tra) && size(sens.tra, 1)==1
    % this multiplication would result in a sparse leadfield, which is not what we want
    % the effect can be demonstrated as sparse(1)*rand(1,10), see also http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=1169#c7
    sens.tra = full(sens.tra);
  elseif ~issparse(sens.tra) && size(sens.tra, 1)>1
    % the multiplication of the "sensor" leadfield (electrode or coil) with the tra matrix to get the "channel" leadfield
    % is faster for most cases if the pre-multiplying weighting matrix is made sparse
    sens.tra = sparse(sens.tra);
  end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Ppr = pointproj(P,plane)
% projects a point on a plane
% plane(1:3) is a point on the plane
% plane(4:6) is the ori of the plane
Ppr  = [];
ori  = plane(4:6);
line = [P ori];
% get indices of line and plane which are parallel
par = abs(dot(plane(4:6), line(:,4:6), 2))<1e-14;
% difference between origins of plane and line
dp = plane(1:3) - line(:, 1:3);
% Divide only for non parallel vectors (DL)
t = dot(ori(~par,:), dp(~par,:), 2)./dot(ori(~par,:), line(~par,4:6), 2);
% compute coord of intersection point
Ppr(~par, :) = line(~par,1:3) + repmat(t,1,3).*line(~par,4:6);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This function serves as a replacement for the dist function in the Neural
% Networks toolbox.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [d] = dist(x)
n = size(x,2);
d = zeros(n,n);
for i=1:n
  for j=(i+1):n
    d(i,j) = sqrt(sum((x(:,i)-x(:,j)).^2));
    d(j,i) = d(i,j);
  end
end


function outFiles = out_demultiplex(DataFile, ChannelFile, OutputDir, UseSsp, ram, parallel)
% OUT_DEMULTIPLEX: Load a raw data file and creates separate electrode files.

% @=============================================================================
% 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.
% =============================================================================@
%
% Authors: Konstantinos Nasiotis, 2018-2022
%          Martin Cousineau, 2018
%          Francois Tadel, 2022

% If the output directory doesn't exist: create it
if ~exist(OutputDir, 'dir')
    mkdir(OutputDir);
end

% Load channel file
ChannelMat = in_bst_channel(ChannelFile);
numChannels = length(ChannelMat.Channel);
% Channel names: Remove any special characters
cleanNames = str_remove_spec_chars({ChannelMat.Channel.Name});

% Assemble output filenames
outFiles = cellfun(@(c)bst_fullfile(OutputDir, ['raw_elec_', c]), cleanNames, 'UniformOutput', 0);
% If all files already exist: nothing else to do in this function
isFileOk = cellfun(@(c)exist([c, '.mat'], 'file'), outFiles);
if all(isFileOk)
    % Add the .mat extension to the file names
    disp(['BST> Channels already demultiplexed in: ' OutputDir]);
    outFiles = cellfun(@(x) [x '.mat'], outFiles, 'UniformOutput', 0);
    return;
% If some files already exist: delete all intermediate existing file, before generating them again
elseif any(isFileOk)
    delete(outFiles{isFileOk});
end

% Load input data file
DataMat = in_bst_data(DataFile, 'F');
sFile = DataMat.F;
sr = sFile.prop.sfreq;

% Apply SSP/ICA when reading from data files
ImportOptions = db_template('ImportOptions');
ImportOptions.UseCtfComp = 0;
ImportOptions.UseSsp     = UseSsp;
% Special case for supported acquisition systems: Save temporary files
% using single precision instead of double to save disk space
if ismember(sFile.format, {'EEG-AXION', 'EEG-BLACKROCK', 'EEG-INTAN', 'EEG-PLEXON'})
    precision = 'single';
    nBytes = 4;
else
    precision = 'double';
    nBytes = 8;
end
ImportOptions.Precision = precision;

% Separate the file to max length based on RAM
max_samples = ram / nBytes / numChannels;
total_samples = round((sFile.prop.times(2) - sFile.prop.times(1)) .* sFile.prop.sfreq); % (Blackrock/Ripple complained). Removed +1
num_segments = ceil(total_samples / max_samples);
num_samples_per_segment = ceil(total_samples / num_segments);

% Loop on segments
for iSegment = 1:num_segments
    sampleBounds(1) = (iSegment - 1) * num_samples_per_segment + round(sFile.prop.times(1)* sFile.prop.sfreq);
    if iSegment < num_segments
        sampleBounds(2) = sampleBounds(1) + num_samples_per_segment - 1;
    else
        sampleBounds(2) = total_samples + round(sFile.prop.times(1)* sFile.prop.sfreq);
    end
    % Read recordings
    F = in_fread(sFile, ChannelMat, [], sampleBounds, [], ImportOptions);
    % Append segment to individual channel file
    if parallel
        bst_progress('start', 'Spike-sorting', 'Demultiplexing raw file...');
        parfor iChannel = 1:numChannels
            electrode_data = F(iChannel,:);
            fid = fopen([outFiles{iChannel} '.bin'], 'a');
            fwrite(fid, electrode_data, precision);
            fclose(fid);
        end
    else
        bst_progress('start', 'Spike-sorting', 'Demultiplexing raw file...', 0, num_segments * numChannels);
        for iChannel = 1:numChannels
            electrode_data = F(iChannel,:);
            fid = fopen([outFiles{iChannel} '.bin'], 'a');
            fwrite(fid, electrode_data, precision);
            fclose(fid);
            bst_progress('inc', 1);
        end
    end
end

% Convert binary files per channel to Matlab files
if parallel
    bst_progress('start', 'Spike-sorting', 'Converting demultiplexed files...');
    parfor iChannel = 1:numChannels
        convert2mat(outFiles{iChannel}, sr, precision);
    end
else
    bst_progress('start', 'Spike-sorting', 'Converting demultiplexed files...', 0, numChannels);
    for iChannel = 1:numChannels
        convert2mat(outFiles{iChannel}, sr, precision);
        bst_progress('inc', 1);
    end
end

% Add the .mat extension to the file names
outFiles = cellfun(@(x) [x '.mat'], outFiles, 'UniformOutput', 0);

end

%% ===== CONVERT BIN TO MAT =====
function convert2mat(chanFile, sr, precision)
    % Read .bin file
    fid = fopen([chanFile '.bin'], 'rb');
    data = fread(fid, precision);
    fclose(fid);
    % Save .mat file
    save([chanFile '.mat'], 'data', 'sr');
    % Delete .bin file
    delete([chanFile '.bin']);
end


% Demo for aggregate channel features object detector on Inria dataset.
%
% (1) Download data and helper routines from Caltech Peds Website
%  www.vision.caltech.edu/Image_Datasets/CaltechPedestrians/
%  (1a) Download INRIA files: set00.tar, set01.tar, and annotations.zip
%  (1b) Copy above three files to dataDir/ and untar/unzip contents
%  (1c) Download evaluation code (routines necessary for extracting images)
% (2) Set dataDir/ variable below to point to location of INRIA data.
% (3) Launch "matlabpool open" for faster training if available.
% (4) Run demo script and enjoy your newly minted fast ped detector!
%
% Note: pre-trained model files are provided (delete to re-train).
% Re-training may give slightly variable results on different machines.
%
% Piotr's Image&Video Toolbox      Version 3.22
% Copyright 2013 Piotr Dollar & Ron Appel.  [pdollar-at-caltech.edu]
% Please email me if you find bugs, or have suggestions or questions!
% Licensed under the Simplified BSD License [see external/bsd.txt]

%% extract training and testing images and ground truth
dataDir = 'D:\code\research\detectorAcf\data\Inria\';
for s=1:2
  if(s==1), set='00'; type='train'; else set='01'; type='test'; end
  if(exist([dataDir type '/posGt'],'dir')), continue; end
  seqIo([dataDir 'set' set '/V000'],'toImgs',[dataDir type '/pos']);
  seqIo([dataDir 'set' set '/V001'],'toImgs',[dataDir type '/neg']);
  V=vbb('vbbLoad',[dataDir 'annotations/set' set '/V000']);
  vbb('vbbToFiles',V,[dataDir type '/posGt']);
end

%% set up opts for training detector (see acfTrain)
opts=acfTrain(); opts.modelDs=[100 41]; opts.modelDsPad=[128 64];
opts.posGtDir=[dataDir 'train/posGt']; opts.nWeak=[32 128 512 2048];
opts.posImgDir=[dataDir 'train/pos']; opts.pJitter=struct('flip',1);
opts.negImgDir=[dataDir 'train/neg']; opts.pBoost.pTree.fracFtrs=1/16;
opts.pLoad={'squarify',{3,.41}}; opts.name='models/AcfInria';

%% train detector (see acfTrain)
detector = acfTrain( opts );

%% modify detector (see acfModify)
detector = acfModify(detector,'cascThr',-1,'cascCal',0);

%% run detector on a sample image (see acfDetect)
imgNms=bbGt('getFiles',{[dataDir 'test/pos']});
I=imread(imgNms{1}); tic, bbs=acfDetect(I,detector); toc
figure(1); im(I); bbApply('draw',bbs); pause(.1);

%% test detector and plot roc (see acfTest)
[miss,~,gt,dt]=acfTest('name',opts.name,'imgDir',[dataDir 'test/pos'],...
  'gtDir',[dataDir 'test/posGt'],'pLoad',opts.pLoad,'show',2);

%% optional timing test for detector (should be ~30 fps)
if( 0 )
  detector1=acfModify(detector,'pad',[0 0]); n=60; Is=cell(1,n);
  for i=1:n, Is{i}=imResample(imread(imgNms{i}),[480 640]); end
  tic, for i=1:n, acfDetect(Is{i},detector1); end;
  fprintf('Detector runs at %.2f fps on 640x480 images.\n',n/toc);
end

%% optionally show top false positives ('type' can be 'fp','fn','tp','dt')
if( 0 ), bbGt('cropRes',gt,dt,imgNms,'type','fn','n',50,...
    'show',3,'dims',opts.modelDs([2 1])); end