function [sift, SIFTparam] = LMdenseSift(D, HOMEIMAGES, SIFTparam, HOMESIFT)
%
% Computes dense SIFT features.
% The SIFT grid will be defined by the parameters:
%    SIFTparam.grid_spacing = 1; % distance between grid centers
%    SIFTparam.patch_size = 16;  % size of patch from which to compute SIFT
%    descriptor (it has to be a factor of 4)
%
% Run demoSIFT.m to see an example of how it works.
%
% The SIFT descriptor at each location has 128 dimensions.
%
% This function can be called as:
%
% [sift, param] = LMdenseSift(D(n), HOMEIMAGES, param);
% [sift, param] = LMdenseSift(filename, HOMEIMAGES, param);
% [sift, param] = LMdenseSift(filename, HOMEIMAGES, param, HOMESIFT);
% LMdenseSift(D, HOMEIMAGES, param, HOMESIFT);
%
% 'sift' corresponds to the features of the last image. So, call it passing
% just one image. But you can precompute the SIFT features for a set of
% images: When calling LMdenseSift with a fourth argument it will store the sift descriptors in a
% new folder structure mirroring the folder structure of the images. Then,
% when called again, if the sift files already exist, it will just read
% them without recomputing them.
%
% Antonio Torralba, 2008

if nargin==4
    precomputed = 1;
    % get list of folders and create non-existing ones
    %listoffolders = {D(:).annotation.folder};
else
    precomputed = 0;
    HOMESIFT = '';
end

if nargin<3
    % Default parameters
    SIFTparam.grid_spacing = 1; % distance between grid centers
    SIFTparam.patch_size = 16; % size of patch from which to compute SIFT descriptor (it has to be a factor of 4)
end
SIFTparam.w = SIFTparam.patch_size/2; % boundary
Nfeatures = 128;

if isstruct(D)
    % [gist, param] = LMdenseSift(D, HOMEIMAGES, param);
    Nscenes = length(D);
    typeD = 1;
end

if iscell(D)
    % [gist, param] = LMdenseSift(filename, HOMEIMAGES, param);
    Nscenes = length(D);
    typeD = 2;
end

if isnumeric(D)
    % [gist, param] = LMdenseSift(img, HOMEIMAGES, param);
    Nscenes = size(D,4);
    typeD = 3;
end

if Nscenes >1
    fig = figure;
end

% Loop: Compute SIFT features for all scenes
sift = zeros([Nscenes Nfeatures], 'single');
for n = 1:Nscenes
    g = [];
    todo = 1;

    % if SIFT has already been computed, just read the file
    if precomputed==1
        filesift = fullfile(HOMESIFT, D(n).annotation.folder, [D(n).annotation.filename(1:end-4) '.mat']);
        if exist(filesift, 'file')
            load(filesift, 'sift', 'SIFTparam');
            todo = 0;
        end
    end

    % otherwise compute SIFT
    if todo==1
        disp([n Nscenes])

        % load image
        try
            switch typeD
                case 1
                    img = LMimread(D, n, HOMEIMAGES);
                case 2
                    img = imread(fullfile(HOMEIMAGES, D{n}));
                case 3
                    img = D(:,:,:,n);
            end
        catch
            disp(D(n).annotation.folder)
            disp(D(n).annotation.filename)
            rethrow(lasterror)
        end

        % get SIFT descriptors
        [sift, SIFTparam.grid_x, SIFTparam.grid_y] = dense_sift(img, SIFTparam);

        if isfield(SIFTparam, 'edges')
            % 'dont-compute': default if field not present
            % 'siftrepeat'
            w = SIFTparam.w-1;
            switch lower(SIFTparam.edges)
                case 'siftrepeat'
                    sift = [repmat(sift(1,:,:),[w 1 1]); sift; repmat(sift(end,:,:),[w 1 1])];
                    sift = [repmat(sift(:,1,:),[1 w 1]), sift, repmat(sift(:,end,:),[1 w 1])];
                otherwise
                    error('Unknown edges method')
            end
        end

        % save SIFT if a HOMESIFT file is provided
        if precomputed
            mkdir(fullfile(HOMESIFT, D(n).annotation.folder))
            save (filesift, 'sift', 'SIFTparam')
        end

        if Nscenes >1
            figure(fig);
            subplot(121)
            imshow(uint8(img))
            subplot(122)
            showColorSIFT(sift)
        end
    end

    drawnow
end

function [sift_arr, grid_x, grid_y] = dense_sift(I, SIFTparam)
% Original script by Svetlana Lazebnick
% Antonio Torralba: modified using convolutions to speed up the
% computations.

grid_spacing = SIFTparam.grid_spacing;
patch_size = SIFTparam.patch_size;

I = double(I);
I = mean(I,3);
I = I /max(I(:));

% parameters
num_angles = 8;
num_bins = 4;
num_samples = num_bins * num_bins;
alpha = 9; %% parameter for attenuation of angles (must be odd)

if nargin < 5
    sigma_edge = 1;
end

angle_step = 2 * pi / num_angles;
angles = 0:angle_step:2*pi;
angles(num_angles+1) = []; % bin centers

[hgt wid] = size(I);

[G_X,G_Y]=gen_dgauss(sigma_edge);

% add boundary:
I = [I(2:-1:1,:,:); I; I(end:-1:end-1,:,:)];
I = [I(:,2:-1:1,:) I I(:,end:-1:end-1,:)];

I = I-mean(I(:));
I_X = filter2(G_X, I, 'same'); % vertical edges
I_Y = filter2(G_Y, I, 'same'); % horizontal edges

I_X = I_X(3:end-2,3:end-2,:);
I_Y = I_Y(3:end-2,3:end-2,:);

I_mag = sqrt(I_X.^2 + I_Y.^2); % gradient magnitude
I_theta = atan2(I_Y,I_X);
I_theta(find(isnan(I_theta))) = 0; % necessary????

% grid
grid_x = patch_size/2:grid_spacing:wid-patch_size/2+1;
grid_y = patch_size/2:grid_spacing:hgt-patch_size/2+1;

% make orientation images
I_orientation = zeros([hgt, wid, num_angles], 'single');

% for each histogram angle
cosI = cos(I_theta);
sinI = sin(I_theta);
for a=1:num_angles
    % compute each orientation channel
    tmp = (cosI*cos(angles(a))+sinI*sin(angles(a))).^alpha;
    tmp = tmp .* (tmp > 0);

    % weight by magnitude
    I_orientation(:,:,a) = tmp .* I_mag;
end

% Convolution formulation:
weight_kernel = zeros(patch_size,patch_size);
r = patch_size/2;
cx = r - 0.5;
sample_res = patch_size/num_bins;
weight_x = abs((1:patch_size) - cx)/sample_res;
weight_x = (1 - weight_x) .* (weight_x <= 1);

for a = 1:num_angles
    %I_orientation(:,:,a) = conv2(I_orientation(:,:,a), weight_kernel, 'same');
    I_orientation(:,:,a) = conv2(weight_x, weight_x', I_orientation(:,:,a), 'same');
end

% Sample SIFT bins at valid locations (without boundary artifacts)
% find coordinates of sample points (bin centers)
[sample_x, sample_y] = meshgrid(linspace(1,patch_size+1,num_bins+1));
sample_x = sample_x(1:num_bins,1:num_bins); sample_x = sample_x(:)-patch_size/2;
sample_y = sample_y(1:num_bins,1:num_bins); sample_y = sample_y(:)-patch_size/2;

sift_arr = zeros([length(grid_y) length(grid_x) num_angles*num_bins*num_bins], 'single');
b = 0;
for n = 1:num_bins*num_bins
    sift_arr(:,:,b+1:b+num_angles) = I_orientation(grid_y+sample_y(n), grid_x+sample_x(n), :);
    b = b+num_angles;
end
clear I_orientation

% Outputs:
[grid_x,grid_y] = meshgrid(grid_x, grid_y);
[nrows, ncols, cols] = size(sift_arr);

% normalize SIFT descriptors

%sift_arr = reshape(sift_arr, [nrows*ncols num_angles*num_bins*num_bins]);
%sift_arr = normalize_sift(sift_arr);
%sift_arr = reshape(sift_arr, [nrows ncols num_angles*num_bins*num_bins]);

ct = .1;
sift_arr = sift_arr + ct;
tmp = sqrt(sum(sift_arr.^2, 3));
sift_arr = sift_arr ./ repmat(tmp, [1 1 size(sift_arr,3)]);

function [GX,GY]=gen_dgauss(sigma)

% laplacian of size sigma
%f_wid = 4 * floor(sigma);
%G = normpdf(-f_wid:f_wid,0,sigma);
%G = G' * G;
G = gen_gauss(sigma);
[GX,GY] = gradient(G);

GX = GX * 2 ./ sum(sum(abs(GX)));
GY = GY * 2 ./ sum(sum(abs(GY)));

function G=gen_gauss(sigma)

if all(size(sigma)==[1, 1])
    % isotropic gaussian
	f_wid = 4 * ceil(sigma) + 1;
    G = fspecial('gaussian', f_wid, sigma);
%	G = normpdf(-f_wid:f_wid,0,sigma);
%	G = G' * G;
else
    % anisotropic gaussian
    f_wid_x = 2 * ceil(sigma(1)) + 1;
    f_wid_y = 2 * ceil(sigma(2)) + 1;
    G_x = normpdf(-f_wid_x:f_wid_x,0,sigma(1));
    G_y = normpdf(-f_wid_y:f_wid_y,0,sigma(2));
    G = G_y' * G_x;
end


function view = motionCompNestaresWithin1st(view, tgtScans, baseScan, baseFrame, smoothing);
%
% view = motionCompNestaresFull(view, <tgtScans=all>, <baseScan=1>, , <baseFrame=1>, <temporalSmoothing = 1 frame>);
%
% Call both between and within scan motion compensation, back-to-back, on
% an inplane view. Right now just a simple wrapper.
%
% Note that these are the older, rigid-body only motion compensation tools
% based around the code originally developed by Nestares and Heeger.
%
%
% DY 09/2008 based on motionCompNestaresFull
if notDefined('view'), view = getSelectedInplane; end
if notDefined('tgtScans'), tgtScans = 1:numScans(view); end
if notDefined('baseScan'), baseScan = 1; end
if notDefined('baseFrame'), baseFrame = 1; end
if notDefined('smoothing'), smoothing = 1; end

if ~isequal(view.viewType, 'Inplane')
    myErrorDlg('Can only run motion compensation on Inplane data.');
end

% first do within scans motion compensation:
newDataType = 'MotionComp';
view = motionCompSelScan(view, newDataType, tgtScans, baseFrame, smoothing);

% then run between scans motion compensation:
view = selectDataType(view, newDataType);
newnewDataType=['MotionComp_RefScan' num2str(baseScan)];
view = betweenScanMotComp(view, newnewDataType, baseScan, tgtScans);

disp('Finished all motion compensation. Final results in MotionComp_RefScan# data type.')

return


function [data_train, labels_train, data_devel, labels_devel, raw_devel, PC, means_norm, stds_norm, devel_ids, devel_success] = ...
    Prepare_HOG_AU_data_dynamic(train_users, devel_users, au_train, rest_aus, UNBC_dir, features_dir)

%%
addpath(genpath('../data extraction/'));

% First extracting the labels
[ labels_train, valid_ids_train, filenames ] = extract_UNBC_labels(UNBC_dir, train_users, au_train);

[ labels_other, ~, ~ ] = extract_UNBC_labels(UNBC_dir, train_users, rest_aus);
labels_other = cat(1, labels_other{:});

% Reading in the HOG data (of only relevant frames)
[train_appearance_data, valid_ids_train_hog, vid_ids_train_string] = Read_HOG_files_dynamic(train_users, features_dir);

[train_geom_data] = Read_geom_files_dynamic(train_users,  features_dir);

% Subsample the data to make training quicker
labels_train = cat(1, labels_train{:});
valid_ids_train = logical(cat(1, valid_ids_train{:}));

if(numel(train_users) > 0)
    reduced_inds = false(size(labels_train,1),1);
    reduced_inds(labels_train > 0) = true;

    % make sure the same number of positive and negative samples is taken
    pos_count = sum(labels_train > 0);
    neg_count = sum(labels_train == 0);

    num_other = floor(pos_count / (size(labels_other, 2)));

    inds_all = 1:size(labels_train,1);

    for i=1:size(labels_other, 2)+1

        if(i > size(labels_other, 2))
            % fill the rest with a proportion of neutral
            inds_other = inds_all(sum(labels_other,2)==0 & ~labels_train );
                num_other_i = min(numel(inds_other), pos_count - sum(labels_train(reduced_inds,:)==0));
        else
                % take a proportion of each other AU
            inds_other = inds_all(labels_other(:, i) & ~labels_train );
            num_other_i = min(numel(inds_other), num_other);
        end
        inds_other_to_keep = inds_other(round(linspace(1, numel(inds_other), num_other_i)));
        reduced_inds(inds_other_to_keep) = true;

    end

    % Remove invalid ids based on CLM failing or AU not being labelled
    reduced_inds(~valid_ids_train) = false;
    reduced_inds(~valid_ids_train_hog) = false;

    labels_other = labels_other(reduced_inds, :);
    labels_train = labels_train(reduced_inds,:);
    train_appearance_data = train_appearance_data(reduced_inds,:);
    train_geom_data = train_geom_data(reduced_inds,:);
    vid_ids_train_string = vid_ids_train_string(reduced_inds,:);
end
%% Extract devel data

% First extracting the labels
[ labels_devel, valid_ids_devel, vid_ids_devel ] = extract_UNBC_labels(UNBC_dir, devel_users, au_train);

% Reading in the HOG data (of only relevant frames)
[devel_appearance_data, valid_ids_devel_hog, vid_ids_devel_string] = Read_HOG_files_dynamic(devel_users, features_dir);
devel_success = valid_ids_devel_hog;
devel_ids = vid_ids_devel_string;

[devel_geom_data] = Read_geom_files_dynamic(devel_users, features_dir);

labels_devel = cat(1, labels_devel{:});

% Peforming zone specific masking
if(au_train < 8 || au_train == 43 || au_train == 45) % upper face AUs ignore bottom face
    % normalise the data
    pca_file = '../../pca_generation/generic_face_upper.mat';
    load(pca_file);
elseif(au_train > 9) % lower face AUs ignore upper face and the sides
    % normalise the data
    pca_file = '../../pca_generation/generic_face_lower.mat';
    load(pca_file);
elseif(au_train == 9) % Central face model
    % normalise the data
    pca_file = '../../pca_generation/generic_face_rigid.mat';
    load(pca_file);
end

% Grab all data for validation as want good params for all the data
raw_devel = cat(2, devel_appearance_data, devel_geom_data);

devel_appearance_data = bsxfun(@times, bsxfun(@plus, devel_appearance_data, -means_norm), 1./stds_norm);

data_devel = devel_appearance_data * PC;

data_devel = cat(2, data_devel, devel_geom_data);

if(numel(train_users) > 0)
    train_appearance_data = bsxfun(@times, bsxfun(@plus, train_appearance_data, -means_norm), 1./stds_norm);

    data_train = train_appearance_data * PC;
    data_train = cat(2, data_train, train_geom_data);
else
    data_train = [];
end

geom_size = max(size(train_geom_data, 2), size(devel_geom_data, 2));

PC_n = zeros(size(PC)+geom_size);
PC_n(1:size(PC,1), 1:size(PC,2)) = PC;
PC_n(size(PC,1)+1:end, size(PC,2)+1:end) = eye(geom_size);
PC = PC_n;

means_norm = cat(2, means_norm, zeros(1, geom_size));
stds_norm = cat(2, stds_norm, ones(1, geom_size));

end


function my_net_backward(net_info, work_info_batch, data_info, net_run_config)

    gpu_mode=net_run_config.use_gpu;

    if gpu_mode
      if ~net_info.ref.net_on_gpu
           my_move_net(net_info, 'gpu') ;
      end
      data_info.ref.output_info_layers{end}=...
          move_output_info_gpu(data_info.ref.output_info_layers{end});
    else
      assert(~net_info.ref.net_on_gpu);
      data_info.ref.output_info_layers{end}=...
          move_output_info_cpu(data_info.ref.output_info_layers{end});
    end

    do_backward(net_info, work_info_batch, data_info, net_run_config);

    if gpu_mode
        if ~net_info.ref.net_stay_on_gpu
            my_move_net(net_info, 'cpu') ;
        end
    end

end

function do_backward(net_info, work_info_batch, data_info, net_run_config)

one_optimizer_param=work_info_batch.ref.gen_optimizer_param_fn(work_info_batch, net_info);
net_info.ref.current_lr=one_optimizer_param.learning_rate;

bp_start_layer=net_info.ref.bp_start_layer;
layer_num = numel(net_info.ref.layers) ;
assert(bp_start_layer<=layer_num);

gpu_mode=net_run_config.use_gpu;

if gpu_mode && net_run_config.sync
    wait(gpuDevice) ;
end

if gpu_mode
    if ~net_info.ref.data_stay_on_gpu
        data_info.ref.output_info_layers{end}=move_output_info_gpu(...
            data_info.ref.output_info_layers{end});
    end
end

for layer_idx=layer_num:-1:bp_start_layer

    input_info=data_info.ref.output_info_layers{layer_idx};
    output_info=data_info.ref.output_info_layers{layer_idx+1};

    if gpu_mode && ~net_info.ref.data_stay_on_gpu
        input_info=move_output_info_gpu(input_info);
    end

    l = net_info.ref.layers{layer_idx} ;
    is_simple_layer= ~strcmp(l.type, 'my_custom');

    if is_simple_layer
        assert(~input_info.is_group_data);
        assert(~output_info.is_group_data);
        switch l.type
          case 'conv'

            one_dzdw=cell(2, 1);
            [input_info.dzdx, one_dzdw{1}, one_dzdw{2}] = ...
                vl_nnconv(input_info.x, l.filters, l.biases, ...
                          output_info.dzdx, ...
                          'pad', l.pad, 'stride', l.stride) ;
            input_info.dzdw=one_dzdw;

          case 'pool'
            input_info.dzdx = vl_nnpool(input_info.x, l.pool, output_info.dzdx, ...
              'pad', l.pad, 'stride', l.stride, 'method', l.method) ;
          case 'normalize'
            input_info.dzdx = vl_nnnormalize(input_info.x, l.param, output_info.dzdx) ;
          case 'softmax'
            input_info.dzdx = vl_nnsoftmax(input_info.x, output_info.dzdx) ;
          case 'relu'
            input_info.dzdx = vl_nnrelu(input_info.x, output_info.dzdx) ;
          case 'noffset'
            input_info.dzdx = vl_nnnoffset(input_info.x, l.param, output_info.dzdx) ;
          case 'dropout'
              input_info.dzdx = vl_nndropout(input_info.x, output_info.dzdx, 'mask', output_info.aux) ;
        end
    else
        if ~check_valid_net_output(output_info)
            break;
        end
        input_info = l.backward_fn(input_info, l, work_info_batch, output_info);
    end

    if gpu_mode && net_run_config.sync
      wait(gpuDevice) ;
    end

    input_info.bp_finished=true;
    data_info.ref.output_info_layers{layer_idx}=input_info;
    do_bp_update_one_layer(net_info, work_info_batch, input_info, layer_idx, one_optimizer_param);

    data_info.ref.output_info_layers{layer_idx+1}=[];

    if gpu_mode && net_run_config.sync
      wait(gpuDevice) ;
    end
end

end

function do_bp_update_one_layer(net_info, work_info_batch, input_info, layer_idx, one_optimizer_param)

    bp_start_layer=net_info.ref.bp_start_layer;
    if layer_idx<bp_start_layer
        return;
    end

      ly=net_info.ref.layers{layer_idx} ;

      if strcmp(ly.type, 'conv')

          lr=one_optimizer_param.learning_rate;
          momentum_param=one_optimizer_param.momentum;
          weightDecay_param=one_optimizer_param.weightDecay;

          ly.filtersMomentum = momentum_param * ly.filtersMomentum ...
              - weightDecay_param * ly.filtersWeightDecay ...
                  * lr * ly.filtersLearningRate * ly.filters ...
              - lr * ly.filtersLearningRate * input_info.dzdw{1} ;

          ly.biasesMomentum = momentum_param * ly.biasesMomentum ...
              - weightDecay_param * ly.biasesWeightDecay ...
                  * lr * ly.biasesLearningRate * ly.biases ...
              - lr * ly.biasesLearningRate * input_info.dzdw{2} ;

          ly.filters = ly.filters + ly.filtersMomentum ;
          ly.biases = ly.biases + ly.biasesMomentum ;
          net_info.ref.layers{layer_idx} = ly ;
      end

      if strcmp(ly.type, 'my_custom')
          layer_update_fn=ly.layer_update_fn;
          if ~isempty(layer_update_fn)

              update_info=[];
              update_info.input_info=input_info;
              update_info.work_info_batch=work_info_batch;

              ly=layer_update_fn('bp_update', ly, net_info, update_info);
              net_info.ref.layers{layer_idx} = ly ;
          end
      end

end


%{
 * Copyright (C) 2020-2030, The Regents of The University of Michigan.
 * All rights reserved.
 * This software was developed in the Biped Lab (https://www.biped.solutions/)
 * under the direction of Jessy Grizzle, grizzle@umich.edu. This software may
 * be available under alternative licensing terms; contact the address above.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the Regents of The University of Michigan.
 *
 * AUTHOR: Bruce JK Huang (bjhuang[at]umich.edu)
 * WEBSITE: https://www.brucerobot.com/
%}

function cost = checkCost(point, cons1, cons2)
    if point >= cons1 && point <= cons2
        cost = 0;
    else
        dis = min(abs(point-cons2), abs(point-cons1));
        cost = getCost(dis);
    end
end


function validJunctionMets = findMetabolicJunctions(model, nRxnsJnc)
% Finds metabolic branchpoints with different numbers of branches
%
% USAGE:
%
%    validJunctionMets = findMetabolicJunctions(model, nRxnsJnc)
%
% INPUTS:
%    model:                COBRA model structure
%    nRxnJnc:              Number of reactions to be considered a junction
%
% OUTPUT:
%    validJunctionMets:    List of junction metabolites
%
% .. Author: - Markus Herrgard 12/14/06

if (isfield(model,'c'))
    selRxnsC = (model.c == 0);
else
    selRxnsC = true(length(model.rxns),1);
end

%rxnGeneMat is a required field for this function, so if it does not exist,
%build it.
if ~isfield(model,'rxnGeneMat')
    model = buildRxnGeneMat(model);
end

[baseMetNames,compSymbols,uniqueMetNames,uniqueCompSymbols] = parseMetNames(model.mets);
uniqueMetNames = uniqueMetNames';
for i = 1:length(uniqueMetNames)
    sel = ismember(baseMetNames,uniqueMetNames{i});
    nRxnsMetUni(i) = sum(any(model.S(sel,selRxnsC) ~= 0,1));
end
nRxnsMetUni = full(nRxnsMetUni');
junctionMets = uniqueMetNames(nRxnsMetUni >= nRxnsJnc);

validJunctionMets = {};
for i = 1:length(junctionMets)
    sel = ismember(baseMetNames,junctionMets{i});
    if (length(unique(compSymbols(sel))) == 1)
        selRxns = any(model.S(sel,:) ~= 0,1) & selRxnsC';
        thisRxns = model.rxns(selRxns);
        geneMap = model.rxnGeneMat(findRxnIDs(model,thisRxns),:);
        selNonZero = any(geneMap,2);
        if (sum(selNonZero) == nRxnsJnc & ...
            size(unique(geneMap(selNonZero,:),'rows'),1) == nRxnsJnc)
            validJunctionMets{end+1} = junctionMets{i};
            if (verbFlag)
                fprintf('*** %s ***\n',junctionMets{i});
                for j = 1:length(thisRxns)
                    %fprintf('%s\t',thisRxns{j});
                    geneInd = find(model.rxnGeneMat(findRxnIDs(model,thisRxns{j}),:));
                    if (~isempty(geneInd))
                        thisGenes = model.genes(geneInd);
                        for k = 1:length(thisGenes)
                            fprintf('%s ',thisGenes{k});
                        end
                    end
                    fprintf('\n');
                end
            end
        end
    end
end

validJunctionMets = validJunctionMets';
for i = 1:length(validJunctionMets)
    validJunctionMets{i} = [validJunctionMets{i} '(c)'];
end


classdef GeometricVectorZConstraint < AbstractGeometricVectorConstraint
    %GeometricVectorMagConstraint Summary of this class goes here
    %   Detailed explanation goes here

    properties

    end

    methods
        function obj = GeometricVectorZConstraint(vector, event, lb, ub)
            obj.vector = vector;
            obj.event = event;
            obj.lb = lb;
            obj.ub = ub;

            obj.type = 'VectorZ';

            obj.id = rand();
        end

        function type = getConstraintType(obj)
            type = 'Geometric Vector Z Component';
        end
    end

    methods(Static)
        function constraint = getDefaultConstraint(~, ~)
            constraint = GeometricVectorZConstraint(AbstractGeometricVector.empty(1,0), LaunchVehicleEvent.empty(1,0), 0, 0);
        end
    end
end


%%%%%%%%%%%%%%%%%%%%%%%%% UPDATE PROCEDURES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% UPDATE THE GLOBAL STATE VECTORS FROM ENTITY.VIRTUAL
function [METAObjUpdate]	= OMAS_updateGlobalStates(SIM,objectID,globalVelocity_k,quaternion_k,idleStatus_k)
% This function reallocates the global properties of each entity to the
% META.OBJECT series to allow faster reference and increased independance
% of the main cycle from the object cycles.

% CONFIRM OBJECT VARIABLES
assert(isa(objectID,'uint16')   == 1,'The objectID must be a uint16 type');
assert(islogical(idleStatus_k)  == 1,'The idle agent status must be reported as a logical');
assert(size(globalVelocity_k,1) == 3 && isnumeric(globalVelocity_k),'Object velocity update must be given as a column vector [3x1].');
assert(size(quaternion_k,1)     == 4 && isnumeric(quaternion_k),'Object quaternion update must be given as a column vector [4x1].');
assert(any(isnan(quaternion_k)) == 0,sprintf('Quaternion for objectID %d is invalid; q = [%f %f %f %f]',int8(objectID),quaternion_k(1),quaternion_k(2),quaternion_k(3),quaternion_k(4)));

% IDENTIFY THE ASSOCIATED META OBJECT
logicalIndices = SIM.globalIDvector == objectID;                           % The logical position of the ID
index = inf;
for i = 1:numel(logicalIndices)
   if logicalIndices(i)
      index = i;
      break
   end
end

% EXTRACT THE META STRUCTURE TO BE UPDATED
METAObjUpdate = SIM.OBJECTS(1,index);                                      % Get the current META object associated with 'entity'

% //////////////////////// UPDATE META.OBJECT /////////////////////////////
% UPDATE THE META ROTATION DEFINING FIXED LOCAL >> ROTATED IN THE GLOBAL
METAObjUpdate.R = OMAS_geometry.quaternionToRotationMatrix(quaternion_k);
% UPDATE THE GLOBAL POSITION
globalPosition_k = METAObjUpdate.globalState(1:3) + globalVelocity_k*SIM.TIME.dt; % Calculate the new global position
% REBUILD GLOBAL STATES (ENTITY & META)
METAObjUpdate.globalState = [globalPosition_k;globalVelocity_k;quaternion_k];
% DETERMINE IF ENTITY HAS INDICATED THAT TASK IS COMPLETE
METAObjUpdate.idleStatus = idleStatus_k;
end


function [data_train, labels_train, data_test, labels_test, raw_test, PC, means_norm, stds_norm, vid_ids_test, success_test] = ...
    Prepare_HOG_AU_data_generic_dynamic(train_users, test_users, au_train, rest_aus, root, features_dir)

%% This should be a separate function?

input_train_label_files = cell(numel(train_users),1);
input_test_label_files = cell(numel(test_users),1);

% This is for loading the labels
for i=1:numel(train_users)
    input_train_label_files{i} = [root, '/ActionUnit_Labels/', train_users{i}, '/', train_users{i}];
end

% This is for loading the labels
for i=1:numel(test_users)
    input_test_label_files{i} = [root, '/ActionUnit_Labels/', test_users{i}, '/', test_users{i}];
end

% First extracting the labels
[train_geom_data] = Read_geom_files_dynamic(train_users, features_dir);
[test_geom_data] = Read_geom_files_dynamic(test_users, features_dir);

% Reading in the HOG data
[train_data, tracked_inds_hog, vid_ids_train] = Read_HOG_files_dynamic(train_users, features_dir);
[test_data, success_test, vid_ids_test] = Read_HOG_files_dynamic(test_users, features_dir);

train_data = cat(2, train_data, train_geom_data);
raw_test = cat(2, test_data, test_geom_data);

% Extracting the labels
labels_train = extract_au_labels(input_train_label_files, au_train);
labels_test = extract_au_labels(input_test_label_files, au_train);

labels_other = zeros(size(labels_train,1), numel(rest_aus));

% This is used to pick up activity of other AUs for a more 'interesting'
% data split and not only neutral expressions for negative samples
if(numel(input_train_label_files) > 0)
    for i=1:numel(rest_aus)
        labels_other(:,i) = extract_au_labels(input_train_label_files, rest_aus(i));
    end

    % can now extract the needed training labels (do not rebalance validation
    % data)

    % make sure the same number of positive and negative samples is taken
    reduced_inds = false(size(labels_train,1),1);
    reduced_inds(labels_train > 0) = true;

    % make sure the same number of positive and negative samples is taken
    pos_count = sum(labels_train > 0);
    neg_count = sum(labels_train == 0);

    % pos_count = pos_count * 8;

    num_other = floor(pos_count / (size(labels_other, 2)));

    inds_all = 1:size(labels_train,1);

    for i=1:size(labels_other, 2)+1

        if(i > size(labels_other, 2))
            % fill the rest with a proportion of neutral
            inds_other = inds_all(sum(labels_other,2)==0 & ~labels_train);
            num_other_i = min(numel(inds_other), pos_count - sum(labels_train(reduced_inds,:)==0));
        else
            % take a proportion of each other AU
            inds_other = inds_all(labels_other(:, i) & ~labels_train);
            num_other_i = min(numel(inds_other), num_other);
        end
        inds_other_to_keep = inds_other(round(linspace(1, numel(inds_other), num_other_i)));
        reduced_inds(inds_other_to_keep) = true;

    end

    % Remove invalid ids based on CLM failing or AU not being labelled
    reduced_inds(~tracked_inds_hog) = false;

    labels_train = labels_train(reduced_inds);
    train_data = train_data(reduced_inds,:);

end

geom_size = max(size(train_geom_data,2), size(test_geom_data,2));

% Peforming zone specific masking
if(au_train < 8 || au_train == 43 || au_train == 45) % upper face AUs ignore bottom face
    % normalise the data
    pca_file = '../../pca_generation/generic_face_upper.mat';
    load(pca_file);
elseif(au_train > 9) % lower face AUs ignore upper face and the sides
    % normalise the data
    pca_file = '../../pca_generation/generic_face_lower.mat';
    load(pca_file);
elseif(au_train == 9) % Central face model
    % normalise the data
    pca_file = '../../pca_generation/generic_face_rigid.mat';
    load(pca_file);
end

PC_n = zeros(size(PC)+geom_size);
PC_n(1:size(PC,1), 1:size(PC,2)) = PC;
PC_n(size(PC,1)+1:end, size(PC,2)+1:end) = eye(geom_size);
PC = PC_n;

means_norm = cat(2, means_norm, zeros(1, geom_size));
stds_norm = cat(2, stds_norm, ones(1, geom_size));

data_test = bsxfun(@times, bsxfun(@plus, raw_test, -means_norm), 1./stds_norm);
data_test = data_test * PC;

if(numel(train_data > 0))
    data_train = bsxfun(@times, bsxfun(@plus, train_data, -means_norm), 1./stds_norm);
    data_train = data_train * PC;
else
   data_train = [];
end

end


%% MKL Numerical Jacobian Install for OPTI Toolbox
% Supplied binaries are built from MKL 10.3 Release 11

%   Copyright (C) 2011 Jonathan Currie (I2C2)

% This file will help you compile the Intel Math Kernel Library (MKL)
% djacobi function for use with MATLAB. NOTE you must NOT link the threaded
% MKL libraries as the MATLAB callback function is not thread safe!

% My build platform:
% - Windows 7 SP1 x64
% - Visual Studio 2010
% - Intel Math Kernel Library 10.3

% To recompile you will need to get / do the following:

% 1) Get and Install Intel MKL
% http://software.intel.com/en-us/articles/intel-mkl/

% 2) MKL JAC MEX Interface
% The MKL JAC MEX Interface is a simple MEX interface I wrote to use this
% function and is supplied in the Utilities\NumDiff folder.

% 6) Compile the MEX File
% The code below will automatically include all required libraries and
% directories to build the MKL JAC MEX file. Once you have completed all
% the above steps, simply run this file to compile! You MUST BE in the
% base directory of OPTI!

clear mklJac

% Modify below function if it cannot find Intel MKL on your system.
mkl_link = opti_FindMKL('seq'); %NOTE sequential only build!

fprintf('\n------------------------------------------------\n');
fprintf('MKL JAC MEX FILE INSTALL\n\n');

%Get MKL Includes & MKL Libraries (for DJACOBI)
post = mkl_link;

%CD to Source Directory
cdir = cd;
cd 'Utilities/Differentiation/Numerical/Source';

%Compile & Move
pre = 'mex -v -largeArrayDims mklJac.c';
try
    eval([pre post])
    movefile(['mklJac.' mexext],'../','f')
    fprintf('Done!\n');
catch ME
    cd(cdir);
    error('opti:mkljac','Error Compiling MKL JAC!\n%s',ME.message);
end
cd(cdir);
fprintf('------------------------------------------------\n');


function normalise = spm_cfg_normalise
% SPM Configuration file for toolbox 'Old Normalise'
%__________________________________________________________________________
% Copyright (C) 2005-2012 Wellcome Trust Centre for Neuroimaging

% $Id: spm_cfg_normalise.m 7155 2017-08-17 10:55:05Z john $

if ~isdeployed, addpath(fullfile(spm('dir'),'toolbox','OldNorm')); end

%--------------------------------------------------------------------------
% source Source Image
%--------------------------------------------------------------------------
source         = cfg_files;
source.tag     = 'source';
source.name    = 'Source Image';
source.help    = {'The image that is warped to match the template(s).  The result is a set of warps, which can be applied to this image, or any other image that is in register with it.'};
source.filter  = 'image';
source.ufilter = '.*';
source.num     = [1 1];

%--------------------------------------------------------------------------
% wtsrc Source Weighting Image
%--------------------------------------------------------------------------
wtsrc         = cfg_files;
wtsrc.tag     = 'wtsrc';
wtsrc.name    = 'Source Weighting Image';
wtsrc.val     = {''};
wtsrc.help    = {'Optional weighting images (consisting of pixel values between the range of zero to one) to be used for registering abnormal or lesioned brains.  These images should match the dimensions of the image from which the parameters are estimated, and should contain zeros corresponding to regions of abnormal tissue.'};
wtsrc.filter  = 'image';
wtsrc.ufilter = '.*';
wtsrc.num     = [0 1];

%--------------------------------------------------------------------------
% subj Subject
%--------------------------------------------------------------------------
subj         = cfg_branch;
subj.tag     = 'subj';
subj.name    = 'Subject';
subj.val     = {source wtsrc };
subj.help    = {'Data for this subject.  The same parameters are used within subject.'};

%--------------------------------------------------------------------------
% esubjs Data
%--------------------------------------------------------------------------
esubjs         = cfg_repeat;
esubjs.tag     = 'esubjs';
esubjs.name    = 'Data';
esubjs.help    = {'List of subjects. Images of each subject should be warped differently.'};
esubjs.values  = {subj };
esubjs.num     = [1 Inf];

%--------------------------------------------------------------------------
% template Template Image
%--------------------------------------------------------------------------
template         = cfg_files;
template.tag     = 'template';
template.name    = 'Template Image';
template.help    = {'Specify a template image to match the source image with. The contrast in the template must be similar to that of the source image in order to achieve a good registration.  It is also possible to select more than one template, in which case the registration algorithm will try to find the best linear combination of these images in order to best model the intensities in the source image.'};
template.filter  = 'image';
template.ufilter = '.*';
template.dir     = fileparts(mfilename('fullpath'));
template.num     = [1 Inf];

%--------------------------------------------------------------------------
% weight Template Weighting Image
%--------------------------------------------------------------------------
weight         = cfg_files;
weight.tag     = 'weight';
weight.name    = 'Template Weighting Image';
weight.val     = {''};
weight.help    = {
                  'Applies a weighting mask to the template(s) during the parameter estimation.  With the default brain mask, weights in and around the brain have values of one whereas those clearly outside the brain are zero.  This is an attempt to base the normalisation purely upon the shape of the brain, rather than the shape of the head (since low frequency basis functions can not really cope with variations in skull thickness).'
                  ''
                  'The option is now available for a user specified weighting image. This should have the same dimensions and mat file as the template images, with values in the range of zero to one.'
}';
weight.filter  = 'image';
weight.ufilter = '.*';
weight.num     = [0 1];

%--------------------------------------------------------------------------
% smosrc Source Image Smoothing
%--------------------------------------------------------------------------
smosrc         = cfg_entry;
smosrc.tag     = 'smosrc';
smosrc.name    = 'Source Image Smoothing';
smosrc.help    = {'Smoothing to apply to a copy of the source image. The template and source images should have approximately the same smoothness. Remember that the templates supplied with SPM have been smoothed by 8mm, and that smoothnesses combine by Pythagoras'' rule.'};
smosrc.strtype = 'e';
smosrc.num     = [1 1];
smosrc.def     = @(val)spm_get_defaults('old.normalise.estimate.smosrc', val{:});

%--------------------------------------------------------------------------
% smoref Template Image Smoothing
%--------------------------------------------------------------------------
smoref         = cfg_entry;
smoref.tag     = 'smoref';
smoref.name    = 'Template Image Smoothing';
smoref.help    = {'Smoothing to apply to a copy of the template image. The template and source images should have approximately the same smoothness. Remember that the templates supplied with SPM have been smoothed by 8mm, and that smoothnesses combine by Pythagoras'' rule.'};
smoref.strtype = 'e';
smoref.num     = [1 1];
smoref.def     = @(val)spm_get_defaults('old.normalise.estimate.smoref', val{:});

%--------------------------------------------------------------------------
% regtype Affine Regularisation
%--------------------------------------------------------------------------
regtype         = cfg_menu;
regtype.tag     = 'regtype';
regtype.name    = 'Affine Regularisation';
regtype.help    = {'Affine registration into a standard space can be made more robust by regularisation (penalising excessive stretching or shrinking).  The best solutions can be obtained by knowing the approximate amount of stretching that is needed (e.g. ICBM templates are slightly bigger than typical brains, so greater zooms are likely to be needed). If registering to an image in ICBM/MNI space, then choose the first option.  If registering to a template that is close in size, then select the second option.  If you do not want to regularise, then choose the third.'};
regtype.labels  = {
                  'ICBM space template'
                  'Average sized template'
                  'No regularisation'
}';
regtype.values  = {
                  'mni'
                  'subj'
                  'none'
}';
regtype.def     = @(val)spm_get_defaults('old.normalise.estimate.regtype', val{:});

%--------------------------------------------------------------------------
% cutoff Nonlinear Frequency Cutoff
%--------------------------------------------------------------------------
cutoff         = cfg_entry;
cutoff.tag     = 'cutoff';
cutoff.name    = 'Nonlinear Frequency Cutoff';
cutoff.help    = {'Cutoff of DCT bases.  Only DCT bases of periods longer than the cutoff are used to describe the warps. The number used will depend on the cutoff and the field of view of the template image(s).'};
cutoff.strtype = 'e';
cutoff.num     = [1 1];
cutoff.def     = @(val)spm_get_defaults('old.normalise.estimate.cutoff', val{:});

%--------------------------------------------------------------------------
% nits Nonlinear Iterations
%--------------------------------------------------------------------------
nits         = cfg_entry;
nits.tag     = 'nits';
nits.name    = 'Nonlinear Iterations';
nits.help    = {'Number of iterations of nonlinear warping performed.'};
nits.strtype = 'w';
nits.num     = [1 1];
nits.def     = @(val)spm_get_defaults('old.normalise.estimate.nits', val{:});

%--------------------------------------------------------------------------
% reg Nonlinear Regularisation
%--------------------------------------------------------------------------
reg         = cfg_entry;
reg.tag     = 'reg';
reg.name    = 'Nonlinear Regularisation';
reg.help    = {'The amount of regularisation for the nonlinear part of the spatial normalisation. Pick a value around one.  However, if your normalised images appear distorted, then it may be an idea to increase the amount of regularisation (by an order of magnitude) - or even just use an affine normalisation. The regularisation influences the smoothness of the deformation fields.'};
reg.strtype = 'e';
reg.num     = [1 1];
reg.def     = @(val)spm_get_defaults('old.normalise.estimate.reg', val{:});

%--------------------------------------------------------------------------
% eoptions Estimation Options
%--------------------------------------------------------------------------
eoptions      = cfg_branch;
eoptions.tag  = 'eoptions';
eoptions.name = 'Estimation Options';
eoptions.val  = {template weight smosrc smoref regtype cutoff nits reg };
eoptions.help = {'Various settings for estimating warps.'};

%--------------------------------------------------------------------------
% est Old Normalise: Estimate
%--------------------------------------------------------------------------
est         = cfg_exbranch;
est.tag     = 'est';
est.name    = 'Old Normalise: Estimate';
est.val     = {esubjs eoptions };
est.help    = {'Computes the warp that best registers a source image (or series of source images) to match a template, saving it to a file imagename''_sn.mat''.'};
est.prog    = @spm_run_normalise;
est.vout    = @vout_estimate;

%--------------------------------------------------------------------------
% matname Parameter File
%--------------------------------------------------------------------------
matname         = cfg_files;
matname.tag     = 'matname';
matname.name    = 'Parameter File';
matname.help    = {'Select the ''_sn.mat'' file containing the spatial normalisation parameters for that subject.'};
matname.filter  = 'mat';
matname.ufilter = '.*_sn\.mat$';
matname.num     = [1 1];

%--------------------------------------------------------------------------
% resample Images to Write
%--------------------------------------------------------------------------
resample         = cfg_files;
resample.tag     = 'resample';
resample.name    = 'Images to Write';
resample.help    = {'These are the images for warping according to the estimated parameters. They can be any images that are in register with the "source" image used to generate the parameters.'};
resample.filter  = 'image';
resample.ufilter = '.*';
resample.num     = [1 Inf];

%--------------------------------------------------------------------------
% subj Subject
%--------------------------------------------------------------------------
subj         = cfg_branch;
subj.tag     = 'subj';
subj.name    = 'Subject';
subj.val     = {matname resample };
subj.help    = {'Data for this subject.  The same parameters are used within subject.'};

%--------------------------------------------------------------------------
% wsubjs Data
%--------------------------------------------------------------------------
wsubjs         = cfg_repeat;
wsubjs.tag     = 'wsubjs';
wsubjs.name    = 'Data';
wsubjs.help    = {'List of subjects. Images of each subject should be warped differently.'};
wsubjs.values  = {subj };
wsubjs.num     = [1 Inf];

%--------------------------------------------------------------------------
% preserve Preserve
%--------------------------------------------------------------------------
preserve         = cfg_menu;
preserve.tag     = 'preserve';
preserve.name    = 'Preserve';
preserve.help    = {
                    'Preserve Concentrations: Spatially normalised images are not "modulated". The warped images preserve the intensities of the original images.'
                    ''
                    'Preserve Total: Spatially normalised images are "modulated" in order to preserve the total amount of signal in the images. Areas that are expanded during warping are correspondingly reduced in intensity.'
}';
preserve.labels = {
                   'Preserve Concentrations'
                   'Preserve Amount'
}';
preserve.values = {0 1};
preserve.def     = @(val)spm_get_defaults('old.normalise.write.preserve', val{:});

%--------------------------------------------------------------------------
% bb Bounding box
%--------------------------------------------------------------------------
bb         = cfg_entry;
bb.tag     = 'bb';
bb.name    = 'Bounding box';
bb.help    = {'The bounding box (in mm) of the volume which is to be written (relative to the anterior commissure).'};
bb.strtype = 'e';
bb.num     = [2 3];
bb.def     = @(val)spm_get_defaults('old.normalise.write.bb', val{:});

%--------------------------------------------------------------------------
% vox Voxel sizes
%--------------------------------------------------------------------------
vox         = cfg_entry;
vox.tag     = 'vox';
vox.name    = 'Voxel sizes';
vox.help    = {'The voxel sizes (x, y & z, in mm) of the written normalised images.'};
vox.strtype = 'e';
vox.num     = [1 3];
vox.def     = @(val)spm_get_defaults('old.normalise.write.vox', val{:});

%--------------------------------------------------------------------------
% interp Interpolation
%--------------------------------------------------------------------------
interp         = cfg_menu;
interp.tag     = 'interp';
interp.name    = 'Interpolation';
interp.help    = {
                  ['The method by which the images are sampled when ' ...
                  'being written in a different space. ' ...
                  '(Note that Inf or NaN values are treated as zero, ' ...
                  'rather than as missing data)']
                  '    Nearest Neighbour:'
                  '      - Fastest, but not normally recommended.'
                  '    Trilinear Interpolation:'
                  '      - OK for PET, realigned fMRI, or segmentations'
                  '    B-spline Interpolation:'
                  ['      - Better quality (but slower) interpolation' ...
                  '/* \cite{thevenaz00a}*/, especially with higher ' ...
                  'degree splines. Can produce values outside the ' ...
                  'original range (e.g. small negative values from an ' ...
                  'originally all positive image).']
}';
interp.labels = {
                 'Nearest neighbour'
                 'Trilinear'
                 '2nd Degree B-spline'
                 '3rd Degree B-Spline '
                 '4th Degree B-Spline '
                 '5th Degree B-Spline'
                 '6th Degree B-Spline'
                 '7th Degree B-Spline'
}';
interp.values = {0 1 2 3 4 5 6 7};
interp.def     = @(val)spm_get_defaults('old.normalise.write.interp', val{:});

%--------------------------------------------------------------------------
% wrap Wrapping
%--------------------------------------------------------------------------
wrap         = cfg_menu;
wrap.tag     = 'wrap';
wrap.name    = 'Wrapping';
wrap.help    = {
                'These are typically:'
                '    No wrapping: for PET or images that have already been spatially transformed. '
                '    Wrap in  Y: for (un-resliced) MRI where phase encoding is in the Y direction (voxel space).'
}';
wrap.labels  = {
               'No wrap'
               'Wrap X'
               'Wrap Y'
               'Wrap X & Y'
               'Wrap Z'
               'Wrap X & Z'
               'Wrap Y & Z'
               'Wrap X, Y & Z'
}';
wrap.values  = {[0 0 0] [1 0 0] [0 1 0] [1 1 0] [0 0 1] [1 0 1] [0 1 1]...
               [1 1 1]};
wrap.def     = @(val)spm_get_defaults('old.normalise.write.wrap', val{:});

%--------------------------------------------------------------------------
% prefix Filename Prefix
%--------------------------------------------------------------------------
prefix         = cfg_entry;
prefix.tag     = 'prefix';
prefix.name    = 'Filename Prefix';
prefix.help    = {'Specify the string to be prepended to the filenames of the normalised image file(s). Default prefix is ''w''.'};
prefix.strtype = 's';
prefix.num     = [1 Inf];
prefix.def     = @(val)spm_get_defaults('old.normalise.write.prefix', val{:});

%--------------------------------------------------------------------------
% roptions Writing Options
%--------------------------------------------------------------------------
roptions      = cfg_branch;
roptions.tag  = 'roptions';
roptions.name = 'Writing Options';
roptions.val  = {preserve bb vox interp wrap prefix };
roptions.help = {'Various options for writing normalised images.'};

%--------------------------------------------------------------------------
% write Old Normalise: Write
%--------------------------------------------------------------------------
write         = cfg_exbranch;
write.tag     = 'write';
write.name    = 'Old Normalise: Write';
write.val     = {wsubjs roptions };
write.help    = {'Allows previously estimated warps (stored in imagename''_sn.mat'' files) to be applied to series of images.'};
write.prog    = @spm_run_normalise;
write.vout    = @vout_write;

%--------------------------------------------------------------------------
% source Source Image
%--------------------------------------------------------------------------
source         = cfg_files;
source.tag     = 'source';
source.name    = 'Source Image';
source.help    = {'The image that is warped to match the template(s).  The result is a set of warps, which can be applied to this image, or any other image that is in register with it.'};
source.filter  = 'image';
source.ufilter = '.*';
source.num     = [1 1];

%--------------------------------------------------------------------------
% wtsrc Source Weighting Image
%--------------------------------------------------------------------------
wtsrc         = cfg_files;
wtsrc.tag     = 'wtsrc';
wtsrc.name    = 'Source Weighting Image';
wtsrc.val     = {''};
wtsrc.help    = {'Optional weighting images (consisting of pixel values between the range of zero to one) to be used for registering abnormal or lesioned brains.  These images should match the dimensions of the image from which the parameters are estimated, and should contain zeros corresponding to regions of abnormal tissue.'};
wtsrc.filter  = 'image';
wtsrc.ufilter = '.*';
wtsrc.num     = [0 1];

%--------------------------------------------------------------------------
% resample Images to Write
%--------------------------------------------------------------------------
resample         = cfg_files;
resample.tag     = 'resample';
resample.name    = 'Images to Write';
resample.help    = {'These are the images for warping according to the estimated parameters. They can be any images that are in register with the "source" image used to generate the parameters.'};
resample.filter  = 'image';
resample.ufilter = '.*';
resample.num     = [1 Inf];

%--------------------------------------------------------------------------
% subj Subject
%--------------------------------------------------------------------------
subj         = cfg_branch;
subj.tag     = 'subj';
subj.name    = 'Subject';
subj.val     = {source wtsrc resample };
subj.help    = {'Data for this subject.  The same parameters are used within subject.'};

%--------------------------------------------------------------------------
% ewsubjs Data
%--------------------------------------------------------------------------
ewsubjs         = cfg_repeat;
ewsubjs.tag     = 'ewsubjs';
ewsubjs.name    = 'Data';
ewsubjs.help    = {'List of subjects. Images of each subject should be warped differently.'};
ewsubjs.values  = {subj };
ewsubjs.num     = [1 Inf];

%--------------------------------------------------------------------------
% estwrite Old Normalise: Estimate & Write
%--------------------------------------------------------------------------
estwrite      = cfg_exbranch;
estwrite.tag  = 'estwrite';
estwrite.name = 'Old Normalise: Estimate & Write';
estwrite.val  = {ewsubjs eoptions roptions };
estwrite.help = {'Computes the warp that best registers a source image (or series of source images) to match a template, saving it to the file imagename''_sn.mat''. This option also allows the contents of the imagename''_sn.mat'' files to be applied to a series of images.'};
estwrite.prog = @spm_run_normalise;
estwrite.vout = @vout_estwrite;

%--------------------------------------------------------------------------
% oldnorm Old Normalise
%--------------------------------------------------------------------------
normalise         = cfg_choice;
normalise.tag     = 'oldnorm';
normalise.name    = 'Old Normalise';
normalise.help    = {
                     'This very ancient module /* \cite{ashburner97b,ashburner99a} */ spatially (stereotactically) normalises MRI, PET or SPECT images into a standard space defined by some ideal model or template image[s].  The template images supplied with SPM conform to the space defined by the ICBM, NIH P-20 project, and approximate that of the the space described in the atlas of Talairach and Tournoux (1988). The transformation can also be applied to any other image that has been coregistered with these scans. A few researchers may wish to continue using this strategy, but (when good quality anatomical MRI scans are available) the DARTEL approach is now generally recommended instead.'
                     ''
                     'Generally, the algorithms work by minimising the sum of squares difference between the image which is to be normalised, and a linear combination of one or more template images.  For the least squares registration to produce an unbiased estimate of the spatial transformation, the image contrast in the templates (or linear combination of templates) should be similar to that of the image from which the spatial normalisation is derived.  The registration simply searches for an optimum solution.  If the starting estimates are not good, then the optimum it finds may not find the global optimum.'
                     ''
                     'The first step of the normalisation is to determine the optimum 12-parameter affine transformation.  Initially, the registration is performed by matching the whole of the head (including the scalp) to the template.  Following this, the registration proceeded by only matching the brains together, by appropriate weighting of the template voxels.  This is a completely automated procedure (that does not require ``scalp editing'') that discounts the confounding effects of skull and scalp differences.   A Bayesian framework is used, such that the registration searches for the solution that maximises the a posteriori probability of it being correct /* \cite{ashburner97b} */.  i.e., it maximises the product of the likelihood function (derived from the residual squared difference) and the prior function (which is based on the probability of obtaining a particular set of zooms and shears).'
                     ''
                     'The affine registration is followed by estimating nonlinear deformations, whereby the deformations are defined by a linear combination of three dimensional discrete cosine transform (DCT) basis functions /* \cite{ashburner99a} */.  The default options result in each of the deformation fields being described by 1176parameters, where these represent the coefficients of the deformations in three orthogonal directions.  The matching involved simultaneously minimising the membrane energies of the deformation fields and the residual squared difference between the images and template(s).'
                     ''
                     'The primarily use is for stereotactic normalisation to facilitate inter-subject averaging and precise characterisation of functional anatomy /* \cite{ashburner97bir} */.  It is not necessary to spatially normalise the data (this is only a pre-requisite  for  inter-subject averaging or reporting in the Talairach space).  If you wish to circumnavigate this step  (e.g. if you have single slice data or do not have an appropriate high resolution MRI scan) simply specify where you think the  anterior commissure  is  with  the  ORIGIN in the header of the first scan (using the ''Display'' facility) and proceed directly  to ''Smoothing''or ''Statistics''.'
                     ''
                     'All normalised images are written to the same subdirectory as the original images, prefixed with a ''w''.  The details of the transformations are displayed in the results window, and the parameters are saved in the "*_sn.mat" file.'
}';
normalise.values   = {est write estwrite};

%==========================================================================
function dep = vout_estimate(job)
for k=1:numel(job.subj)
    dep(k)            = cfg_dep;
    dep(k).sname      = sprintf('Norm Params File (Subj %d)',k);
    dep(k).src_output = substruct('()',{k},'.','params');
    dep(k).tgt_spec   = cfg_findspec({{'filter','mat','strtype','e'}});
end

%==========================================================================
function dep = vout_write(job)
for k=1:numel(job.subj)
    dep(k)            = cfg_dep;
    dep(k).sname      = sprintf('Normalised Images (Subj %d)',k);
    dep(k).src_output = substruct('()',{k},'.','files');
    dep(k).tgt_spec   = cfg_findspec({{'filter','image','strtype','e'}});
end

%==========================================================================
function dep = vout_estwrite(job)
depe = vout_estimate(job);
depw = vout_write(job);
dep = [depe depw];


% params
totalNum = 6159264;
bValue = 1000;
lamda = 0;
qFile = '/home/yinhuan/Data/mapModel/yq/weightVector.txt';
visFilesDir = '/home/yinhuan/Data/mapModel/yq/visMatrix/';
maxQ = 179;
minQ = 1;

splitLength_0 = 50;
saveResultsDir_0 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/0/';
saveReIndexFile_0 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/index/0.txt';
saveNewQFile_0 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/weight/0.txt';

splitLength_1 = 100;
saveResultsDir_1 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/1/';
saveReIndexFile_1 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/index/1.txt';
saveNewQFile_1 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/weight/1.txt';

splitLength_2 = 200;
saveResultsDir_2 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/2/';
saveReIndexFile_2 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/index/2.txt';
saveNewQFile_2 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/weight/2.txt';

splitLength_3 = 400;
saveResultsDir_3 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/3/';
saveReIndexFile_3 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/index/3.txt';
saveNewQFile_3 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/weight/3.txt';

splitLength_4 = 800;
saveResultsDir_4 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/4/';
saveReIndexFile_4 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/index/4.txt';
saveNewQFile_4 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/weight/4.txt';

splitLength_5 = 1600;
saveResultsDir_5 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/5/';
saveReIndexFile_5 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/index/5.txt';
saveNewQFile_5 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/weight/5.txt';

splitLength_6 = 3200;
saveResultsDir_6 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/6/';
saveReIndexFile_6 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/index/6.txt';
saveNewQFile_6 = '/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/weight/6.txt';

%% iteration of optimization

visCells_origin = fromVisDirToCells(visFilesDir);

[ epsilon_soft_0, time_sum_0  ] = Uniform_loopCompress_Cells( lamda, qFile, visCells_origin, splitLength_0, totalNum, bValue, saveResultsDir_0 );
min_cost_0 = get_min_cost(minQ, maxQ, saveResultsDir_0, qFile, lamda, epsilon_soft_0);
[totalNum_0, visCells_0] = deleteZeroPoints_Cells( qFile, visCells_origin, saveResultsDir_0, saveReIndexFile_0, saveNewQFile_0 );

[ epsilon_soft_1, time_sum_1  ] = Uniform_loopCompress_Cells( lamda, saveNewQFile_0, visCells_0, splitLength_1, totalNum_0, bValue, saveResultsDir_1 );
min_cost_1 = get_min_cost(minQ, maxQ, saveResultsDir_1, saveNewQFile_0, lamda, epsilon_soft_1);
[totalNum_1, visCells_1] = deleteZeroPoints_Cells( saveNewQFile_0, visCells_0, saveResultsDir_1, saveReIndexFile_1, saveNewQFile_1 );

[ epsilon_soft_2, time_sum_2  ] = Uniform_loopCompress_Cells( lamda, saveNewQFile_1, visCells_1, splitLength_2, totalNum_1, bValue, saveResultsDir_2 );
min_cost_2 = get_min_cost(minQ, maxQ, saveResultsDir_2, saveNewQFile_1, lamda, epsilon_soft_2);
[totalNum_2, visCells_2] = deleteZeroPoints_Cells( saveNewQFile_1, visCells_1, saveResultsDir_2, saveReIndexFile_2, saveNewQFile_2 );

[ epsilon_soft_3, time_sum_3  ] = Uniform_loopCompress_Cells( lamda, saveNewQFile_2, visCells_2, splitLength_3, totalNum_2, bValue, saveResultsDir_3 );
min_cost_3 = get_min_cost(minQ, maxQ, saveResultsDir_3, saveNewQFile_2, lamda, epsilon_soft_3);
[totalNum_3, visCells_3] = deleteZeroPoints_Cells( saveNewQFile_2, visCells_2, saveResultsDir_3, saveReIndexFile_3, saveNewQFile_3 );

[ epsilon_soft_4, time_sum_4  ] = Uniform_loopCompress_Cells( lamda, saveNewQFile_3, visCells_3, splitLength_4, totalNum_3, bValue, saveResultsDir_4 );
min_cost_4 = get_min_cost(minQ, maxQ, saveResultsDir_4, saveNewQFile_3, lamda, epsilon_soft_4);
[totalNum_4, visCells_4] = deleteZeroPoints_Cells( saveNewQFile_3, visCells_3, saveResultsDir_4, saveReIndexFile_4, saveNewQFile_4 );

[ epsilon_soft_5, time_sum_5  ] = Uniform_loopCompress_Cells( lamda, saveNewQFile_4, visCells_4, splitLength_5, totalNum_4, bValue, saveResultsDir_5 );
min_cost_5 = get_min_cost(minQ, maxQ, saveResultsDir_5, saveNewQFile_4, lamda, epsilon_soft_5);
[totalNum_5, visCells_5] = deleteZeroPoints_Cells( saveNewQFile_4, visCells_4, saveResultsDir_5, saveReIndexFile_5, saveNewQFile_5 );

[ epsilon_soft_6, time_sum_6  ] = Uniform_loopCompress_Cells( lamda, saveNewQFile_5, visCells_5, splitLength_6, totalNum_5, bValue, saveResultsDir_6 );
min_cost_6 = get_min_cost(minQ, maxQ, saveResultsDir_6, saveNewQFile_5, lamda, epsilon_soft_6);
[totalNum_6, visCells_6] = deleteZeroPoints_Cells( saveNewQFile_5, visCells_5, saveResultsDir_6, saveReIndexFile_6, saveNewQFile_6 );

% save the results

compressIndex_5 = anal_reindex_last(saveResultsDir_6, saveReIndexFile_5);
compressIndex_4 = anal_reindex_middle(compressIndex_5, saveReIndexFile_4);
compressIndex_3 = anal_reindex_middle(compressIndex_4, saveReIndexFile_3);
compressIndex_2 = anal_reindex_middle(compressIndex_3, saveReIndexFile_2);
compressIndex_1 = anal_reindex_middle(compressIndex_2, saveReIndexFile_1);
compressIndex_0 = anal_reindex_middle(compressIndex_1, saveReIndexFile_0);

dlmwrite('/home/yinhuan/Data/mapModel/yq/iter_b_1000_0/compressIndex_final.txt', compressIndex_0, 'precision', '%d');


function stage_time_struct = timesStruct(N, nt)
stage_time_struct = ocl.types.Structure();
stage_time_struct.addRepeated({'states', 'integrator', 'controls'}, ...
  {ocl.types.Matrix([1,1]), ocl.types.Matrix([nt,1]), ocl.types.Matrix([1,1])}, N);
stage_time_struct.add('states', ocl.types.Matrix([1,1]));


function [F1,Se,PPV,Nb] = run_sqi(refqrs,testqrs,thres,margin,windowlen,fs)
% sqi = run_sqi(refqrs,testqrs,thres,margin,windowlen,fs)
% compare two sets of annotation with one as the reference (refqrs) and one
% as the test (testqrs)
%
% inputs
%     refqrs:       reference qrs annotation (in sec)
%     testqrs:      test qrs annotations (in sec)
%     thres:        threshold (in sec,default 0.05s)
%     margin:       margin time not include in comparison (in sec,default 2s)
%     windowlen:    length of the comparison window (in sec,default 60s)
%     fs:           sampling frequency
%
% output
%     sqi: match proportion according to some criteria you can change
%     depending on what you are looking for (can be Se, PPV or F1 measure).
%     See at the end of the function.
%
% When using this work, then please cite [1] and [2]:
%     [1] Behar Joachim, Oster Julien, Qiao Li, Clifford Gari D. Signal Quality
%     During Arrhythmia and its Application to False Alarm Reduction.
%     IEEE Transactions on Biomedical Engineering. 60(6). 1660-6. 2013.
%
%     [2] Li, Qiao, Roger G. Mark, and Gari D. Clifford. "Robust heart rate estimation
%     from multiple asynchronous noisy sources using signal quality indices and
%     a Kalman filter." Physiological measurement 29.1 (2008): 15.
%
% PCinCC2014, version 1.0, June 2014
% Released under the GNU General Public License
%
% Copyright (C) 2014  Joachim Behar
% Oxford university, Intelligent Patient Monitoring Group - Oxford 2014
% joachim.behar@gmail.com
%
% Updates:
% 07-02-2014
% JB- testes with Octave -> running OK
%
% 02-10-2014
% Bug fix: Dealing with annotations close to the border of the search
% window. Lines 70 - 100
% David Springer
%
% 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.

% == managing inputs
if nargin<2; error('bsqi: wrong number of input arguments \n'); end;
if nargin<3; thres=0.05; end;
if nargin<4; margin=2; end;
if nargin<5; windowlen=60; end;
if nargin<6; fs=1000; end;

if size(refqrs,1)>size(refqrs,2); refqrs=refqrs';end
if size(testqrs,1)>size(testqrs,2); testqrs=testqrs';end

start = margin*fs;
stop = (windowlen-margin)*fs;
refqrs = refqrs.*fs; % convert into samples from time
testqrs = testqrs.*fs; % convert into samples from time

try
    refqrs  = refqrs(refqrs>start & refqrs<stop)'; % reference annotations
    testqrs = testqrs(testqrs>start & testqrs<stop)'; % test annotations

    if ~isempty(refqrs)

        NB_REF  = length(refqrs);
        NB_TEST = length(testqrs);

        % == removing borders problems
        indbord = find(refqrs<thres*fs | refqrs>(windowlen-thres)*fs); % reference QRS at the border
        if ~isempty(indbord)
            [IndMatchBord,DistQRSbord] = dsearchn(testqrs,refqrs(indbord));
            Indeces_below_threshold = DistQRSbord<thres*fs; %Added line to find indices of annotation of interest (refqrs) below threshold (02-10-14)
            IndMatchBord = IndMatchBord(Indeces_below_threshold);
            NB_QRS_BORD = length(indbord); % QRS at the border of the window (0,1,2)
            NB_MATCHING = length(IndMatchBord); % nb of corresponding mathing QRS (0,1,2)
            if isempty(IndMatchBord);
                refqrs(indbord) = [];
            elseif NB_MATCHING<NB_QRS_BORD
                refqrs(indbord(~Indeces_below_threshold)) = []; %Removing other indices not below threshold (02-10-14)
            end
        end

        indbord = find(testqrs<thres*fs | testqrs>(windowlen-thres)*fs); % reference QRS at the border
        if ~isempty(indbord)
            [IndMatchBord,DistQRSbord] = dsearchn(refqrs,testqrs(indbord));
            Indeces_below_threshold = DistQRSbord<thres*fs; %Added line to find indices of annotation of interest (testqrs) below threshold (02-10-14)
            IndMatchBord = IndMatchBord(Indeces_below_threshold);
            NB_QRS_BORD = length(indbord); % QRS at the border of the window (0,1,2)
            NB_MATCHING = length(IndMatchBord); % nb of corresponding mathing QRS (0,1,2)
            if isempty(IndMatchBord);
                testqrs(indbord) = [];
            elseif NB_MATCHING<NB_QRS_BORD
                testqrs(indbord(~Indeces_below_threshold)) = []; %Removing other indices not below threshold (02-10-14)
            end
        end

        % == core function
        [IndMatch,Dist] = dsearchn(refqrs,testqrs);         % closest ref for each point in test qrs
        IndMatchInWindow = IndMatch(Dist<thres*fs);         % keep only the ones within a certain window
        NB_MATCH_UNIQUE = length(unique(IndMatchInWindow)); % how many unique matching
        TP = NB_MATCH_UNIQUE;                               % number of identified ref QRS
        FN = NB_REF-TP;                                     % number of missed ref QRS
        FP = NB_TEST-TP;                                    % how many extra detection?
        Se  = TP/(TP+FN);
        PPV = TP/(FP+TP);
        F1 = 2*Se*PPV/(Se+PPV);                             % accuracy measure

        Nb.TP = TP;
        Nb.FN = FN;
        Nb.FP = FP;
    else
        F1=[];Se=[];PPV=[];Nb=[];
    end
catch
    F1=[];Se=[];PPV=[];Nb=[];
end

end


function  [ons_secs, VOLLOCS, LOCS, verbose] = tapas_physio_create_scan_timing(...
    log_files, scan_timing, ons_secs, verbose)
% Extracts slice and volume scan onsets (triggers) from different vendor formats
%
%   [ons_secs, VOLLOCS, LOCS, verbose] = tapas_physio_create_scan_timing(...
%            log_files, scan_timing, ons_secs, verbose);
%
%
% IN
%   NOTE: The detailed description of all input structures can be found as
%   comments in tapas_physio_new
%
%   log_files    - file names (physiology and scan timing) and sampling rates
%
%   ons_secs     -  structure for time-dependent variables, i.e. onsets,
%                   specified in seconds, in particular
%                   .t          - time vector of phys time course
%
%   scan_timing         -  Parameters for sequence timing & synchronization
%   scan_tming.sqpar    -  slice and volume acquisition starts, TR,
%                          number of scans etc.
%   scan_timing.sync    -  synchronization options
%                          (e.g. from gradients, trigger, tics phys
%                           logfile to scan acquisition)
%
%       sqpar           - sequence timing parameters, used for computation
%                         of scan events from 'nominal' timing
%           .Nslices        - number of slices per volume in fMRI scan
%           .TR             - repetition time in seconds
%           .Ndummies       - number of dummy volumes
%           .Nscans         - number of full volumes saved (volumes in nifti file,
%                             usually rows in your design matrix)
%           .Nprep          - number of non-dummy, volume like preparation pulses
%                             before 1st dummy scan. If set, logfile is read from beginning,
%                             otherwise volumes are counted from last detected volume in the logfile
%           .time_slice_to_slice - time between the acquisition of 2 subsequent
%                             slices; typically TR/Nslices or
%                             minTR/Nslices, if minimal temporal slice
%                             spacing was chosen
%
%   verbose                 - defines output level (which graphics to plot
%                             and whether to save them)
%
% OUT
%   ons_secs    -  structure for time-dependent variables, i.e. onsets,
%                  specified in seconds, updated fields
%                   .spulse     - scan slice trigger events
%                   .svolpulse  - scan volume trigger events
%                   .spulse_per_vol
%                               - cell(nVolumes,1) of slice triggers per
%                                 volume
%                   .acq_codes  - acquisition codes (e.g. triggers) within
%                                 phys log files (e.g. Philips, Biopac)
%
%   VOLLOCS     - index locations in time vector (of physiological recordings),
%                             when volume scan events started
%   LOCS        - locations in time vector, when slice or volume scan
%                             events started
%
%   See also tapas_physio_new tapas_physio_main_create_regresssors

% Author: Lars Kasper
% Created: 2013-08-23
% Copyright (C) 2016 Institute for Biomedical Engineering, ETH/Uni Zurich.
%
% This file is part of the PhysIO toolbox, which is released under the terms of the GNU General Public
% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version). For further details, see the file
% COPYING or <http://www.gnu.org/licenses/>.

sqpar   = scan_timing.sqpar;

% TODO: introduce auto that takes time stamps from default locations
% for different vendors
switch lower(scan_timing.sync.method)
    case 'nominal'
        [VOLLOCS, LOCS] = ...
            tapas_physio_create_scan_timing_nominal(ons_secs.t + ...
                    ons_secs.t_start, sqpar, log_files.align_scan);
    case {'gradient', 'gradient_log'}
        [VOLLOCS, LOCS, verbose] = ...
            tapas_physio_create_scan_timing_from_gradients_philips( ...
            log_files, scan_timing, verbose);
    case {'gradient_auto', 'gradient_log_auto'}
        [VOLLOCS, LOCS, verbose] = ...
            tapas_physio_create_scan_timing_from_gradients_auto_philips( ...
            log_files, scan_timing, verbose);
    case 'scan_timing_log'
        switch lower(log_files.vendor)
            case 'siemens'
                % for alignScan = 'last', in case logfile lasts longer than end of last scan
                % assuming t = 0 is already start of first scan
                durationPhyslogAfterEndOfLastScan = ons_secs.t(end) + ...
                    ons_secs.t_start - sqpar.Nscans*sqpar.TR;
                [VOLLOCS, LOCS] = ...
                    tapas_physio_create_scan_timing_nominal(ons_secs.t + ...
                    ons_secs.t_start, sqpar, log_files.align_scan, ...
                    durationPhyslogAfterEndOfLastScan);
            case 'siemens_tics'
                [VOLLOCS, LOCS, verbose] = ...
                    tapas_physio_create_scan_timing_from_tics_siemens( ...
                    ons_secs.t, ons_secs.t_start, log_files, verbose);
            case {'biopac_mat', 'biopac_txt', 'bids'}
                [VOLLOCS, LOCS, verbose] = ...
                    tapas_physio_create_scan_timing_from_acq_codes( ...
                    ons_secs.t + ons_secs.t_start, ons_secs.acq_codes, ...
                    sqpar, log_files.align_scan, verbose);
        end
    otherwise
        verbose = tapas_physio_log(...
            sprintf('unknown scan_timing.sync.method: %s', ...
            scan_timing.sync.method), verbose, 2);
end

% remove arbitrary offset in time vector now, since all timings have now
% been aligned to ons_secs.t
% ons_secs.t = ons_secs.t - ons_secs.t(1);

[ons_secs.svolpulse, ons_secs.spulse, ons_secs.spulse_per_vol, verbose] = ...
    tapas_physio_get_onsets_from_locs(...
    ons_secs.t, VOLLOCS, LOCS, sqpar, verbose);


function cellInfo = Extended_States_Bus(varargin)
% EXTENDED_STATES_BUS returns a cell array containing bus object information
%
% Optional Input: 'false' will suppress a call to Simulink.Bus.cellToObject
%                 when the MATLAB file is executed.
% The order of bus element attributes is as follows:
%   ElementName, Dimensions, DataType, SampleTime, Complexity, SamplingMode, DimensionsMode, Min, Max, DocUnits, Description

suppressObject = false;
if nargin == 1 && islogical(varargin{1}) && varargin{1} == false
    suppressObject = true;
elseif nargin > 1
    error('Invalid input argument(s) encountered');
end

cellInfo = { ...
  { ...
    'AirSpeed_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'diff_pressure', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('Pa'), ''}; ...
{'temperature', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('degC'), ''}; ...
    } ...
  } ...
  { ...
    'Auto_Cmd_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'p_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), sprintf('rate x command in body frame')}; ...
{'q_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), sprintf('rate y command in body frame')}; ...
{'r_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), sprintf('rate z command in body frame')}; ...
{'phi_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), sprintf('roll command')}; ...
{'theta_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), sprintf('pitch command')}; ...
{'psi_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'psi_rate_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), sprintf('yaw rate command')}; ...
{'x_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'y_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'z_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'lat_cmd', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'lon_cmd', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'alt_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'u_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), sprintf('velocity x command in control frame')}; ...
{'v_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), sprintf('velocity y command in control frame')}; ...
{'w_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), sprintf('velocity z command in control frame')}; ...
{'ax_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'ay_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'az_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'throttle_cmd', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('throttle command')}; ...
{'frame', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Coordinate Frame:\n0:FRAME_GLOBAL_NED\n1:FRAME_LOCAL_FRD\n2:FRAME_BODY_FRD')}; ...
{'reserved', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'cmd_mask', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Type mask for auto command:\n  1: p_cmd valid\n  2: q_cmd valid\n  3: r_cmd valid\n  4: phi_cmd valid\n  5: theta_cmd valid\n  6: psi__cmd_valid\n  7: psi_rate_cmd_valid\n  8: x_cmd valid\n  9: y_cmd valid\n10: z_cmd valid\n11: lat_cmd valid\n12: lon_cmd valid\n13: alt_cmd valid\n14: u_cmd valid\n15: v_cmd valid\n16: w_cmd valid\n17: ax_cmd valid\n18: ay_cmd valid\n19: ax_cmd valid\n20: throttle_cmd valid')}; ...
    } ...
  } ...
  { ...
    'Barometer_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'pressure', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('Pa'), ''}; ...
{'temperature', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('deg'), ''}; ...
    } ...
  } ...
  { ...
    'Commander_In_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'sp_waypoint', 3, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'cur_waypoint', 3, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'offboard_psi_0', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('The psi value when offboard mode entered, \nwhich is used for FRAME_LOCAL_FRD')}; ...
    } ...
  } ...
  { ...
    'Control_Out_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'actuator_cmd', 16, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
    } ...
  } ...
  { ...
    'Extended_States_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'temprature', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('deg'), ''}; ...
{'prop_vel', 8, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'quat', 4, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'M_BO', [3 3], 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'M_OB', [3 3], 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'Va', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), sprintf('Airspeed in body frame')}; ...
    } ...
  } ...
  { ...
    'FMS_Out_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), sprintf('fms output timestamp')}; ...
{'p_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), sprintf('roll rate command in body frame')}; ...
{'q_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), sprintf('pitch rate command in body frame')}; ...
{'r_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), sprintf('yaw rate command in body frame')}; ...
{'phi_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), sprintf('roll command in body frame')}; ...
{'theta_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), sprintf('pitch command in body frame')}; ...
{'psi_rate_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), sprintf('yaw rate command in body frame')}; ...
{'u_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), sprintf('velocity x command in control frame')}; ...
{'v_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), sprintf('velocity y command in control frame')}; ...
{'w_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), sprintf('velocity z command in control frame')}; ...
{'ax_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s^2'), sprintf('acceleration x command in control frame')}; ...
{'ay_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s^2'), sprintf('acceleration y command in control frame')}; ...
{'az_cmd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s^2'), sprintf('acceleration z command in control frame')}; ...
{'actuator_cmd', 16, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('actuator command')}; ...
{'throttle_cmd', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('throttle command')}; ...
{'cmd_mask', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Type mask for offboard mode:\n  1: p_cmd valid\n  2: q_cmd valid\n  3: r_cmd valid\n  4: phi_cmd valid\n  5: theta_cmd valid\n  6: psi_rate_cmd_valid\n  7: u_cmd valid\n  8: v_cmd valid\n  9: w_cmd valid\n10: ax_cmd valid\n11: ay_cmd valid\n12: ax_cmd valid\n13: throttle_cmd valid')}; ...
{'status', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('enum VehicleStatus\n\nvehicle status:\n0: None\n1: Disarm\n2: Standby\n3: Arm')}; ...
{'state', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('enum VehicleState\n\nvehicle state:\n0: None\n1: Disarm\n2: Standby\n3: Offboard\n4: Mission\n5: InvalidAutoMode\n6: Hold\n7: Acro\n8: Stabilize\n9: Altitude\n10: Position\n11: InvalidAssistMode\n12: Manual\n13: InvalidManualMode\n14: InvalidArmMode\n15: Land\n16: Return\n17: Takeoff')}; ...
{'ctrl_mode', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('enum ControlMode\n\ncontrol mode:\n0: None\n1: Manual\n2: Acro\n3: Stabilize\n4: ALTCTL\n5: POSCTL')}; ...
{'mode', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('enum PilotMode\n\npilot mode:\n0: None\n1: Manual\n2: Acro\n3: Stabilize\n4: Altitude\n5: Position\n6: Mission\n7: Offboard')}; ...
{'reset', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('reset the controller')}; ...
{'wp_consume', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('consumed waypoints')}; ...
{'wp_current', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('current waypoint')}; ...
{'reserved', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('enum of PilotMode')}; ...
    } ...
  } ...
  { ...
    'GCS_Cmd_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'mode', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'cmd_1', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Operation channel 1')}; ...
{'cmd_2', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Operation channel 2')}; ...
    } ...
  } ...
  { ...
    'GPS_uBlox_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'iTOW', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'year', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'month', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'day', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'hour', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'min', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'sec', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'valid', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'tAcc', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'nano', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'fixType', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'flags', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'reserved1', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'numSV', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'lon', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'lat', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'height', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'hMSL', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'hAcc', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'vAcc', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'velN', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'velE', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'velD', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'gSpeed', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'heading', 1, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'sAcc', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'headingAcc', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'pDOP', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'reserved2', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
    } ...
  } ...
  { ...
    'IMU_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'gyr_x', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'gyr_y', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'gyr_z', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'acc_x', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
{'acc_y', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
{'acc_z', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
    } ...
  } ...
  { ...
    'INS_Out_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'phi', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'theta', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'psi', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'quat', 4, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('unified'), ''}; ...
{'p', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'q', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'r', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'ax', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
{'ay', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
{'az', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
{'vn', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'ve', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'vd', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'airspeed', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'lat', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('degree'), ''}; ...
{'lon', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('degree'), ''}; ...
{'alt', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'lat_0', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('degree'), ''}; ...
{'lon_0', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('degree'), ''}; ...
{'alt_0', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'x_R', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'y_R', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'h_R', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'h_AGL', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'flag', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'status', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
    } ...
  } ...
  { ...
    'MAG_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'mag_x', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('gauss'), ''}; ...
{'mag_y', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('gauss'), ''}; ...
{'mag_z', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('gauss'), ''}; ...
    } ...
  } ...
  { ...
    'Mission_Data_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'valid_items', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'reserved', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'seq', 8, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Start from 0')}; ...
{'command', 8, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'frame', 8, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'current', 8, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'autocontinue', 8, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'mission_type', 8, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'param1', 8, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'param2', 8, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'param3', 8, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'param4', 8, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'x', 8, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'y', 8, 'int32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'z', 8, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
    } ...
  } ...
  { ...
    'Optical_Flow_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'vx', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'vy', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'quality', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'reserved1', 1, 'uint8', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'reserved2', 1, 'uint16', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
    } ...
  } ...
  { ...
    'Pilot_Cmd_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'stick_yaw', 1, 'single', -1, 'real', 'Sample', 'Fixed', -1, 1, '', sprintf('Stick value of yaw channel')}; ...
{'stick_throttle', 1, 'single', -1, 'real', 'Sample', 'Fixed', -1, 1, '', sprintf('Stick value of throttle channel')}; ...
{'stick_roll', 1, 'single', -1, 'real', 'Sample', 'Fixed', -1, 1, '', sprintf('Stick value of roll chanel')}; ...
{'stick_pitch', 1, 'single', -1, 'real', 'Sample', 'Fixed', -1, 1, '', sprintf('Stick value of pitch channel')}; ...
{'mode', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'cmd_1', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Operation channel 1')}; ...
{'cmd_2', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Operation channel 2')}; ...
    } ...
  } ...
  { ...
    'Plant_States_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'phi', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'theta', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'psi', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'rot_x_B', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'rot_y_B', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'rot_z_B', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad/s'), ''}; ...
{'acc_x_O', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
{'acc_y_O', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
{'acc_z_O', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s2'), ''}; ...
{'vel_x_O', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'vel_y_O', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'vel_z_O', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m/s'), ''}; ...
{'x_R', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'y_R', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'h_R', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'lat', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'lon', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'alt', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
{'lat_0', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'lon_0', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('rad'), ''}; ...
{'alt_0', 1, 'double', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
    } ...
  } ...
  { ...
    'Rangefinder_Bus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('ms'), ''}; ...
{'distance', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], sprintf('m'), ''}; ...
    } ...
  } ...
  { ...
    'StatesBus', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'V_body', [3 1], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'Omega_body', [3 1], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'Euler', [3 1], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'Accel_body', [3 1], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'dOmega_body', [3 1], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'V_ned', [3 1], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'X_ned', [3 1], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
{'LLA', [1 3], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Latitude, Longitude, Altitude')}; ...
{'DCM_be', [3 3], 'double', -1, 'real', 'Sample', 'Fixed', [], [], '', ''}; ...
    } ...
  } ...
  { ...
    'mavlink_fmt_pilot_cmd_t', ...
    '', ...
    '', ...
    'Auto', ...
    '-1', {...
{'timestamp', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('timestamp in milliseconds')}; ...
{'ls_lr', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Left stick left/right')}; ...
{'ls_ud', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Left stick up/down')}; ...
{'rs_lr', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Right stick left/right')}; ...
{'rs_ud', 1, 'single', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Right stick up/down')}; ...
{'mode', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Control Mode')}; ...
{'command_1', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Pilot command 1')}; ...
{'command_2', 1, 'uint32', -1, 'real', 'Sample', 'Fixed', [], [], '', sprintf('Pilot command 2')}; ...
    } ...
  } ...
}';

if ~suppressObject
    % Create bus objects in the MATLAB base workspace
    Simulink.Bus.cellToObject(cellInfo)
end