function [bvecs,bvals] = dtiRawBuildBvecs(nVols, xform, gradsFile, bval, outBaseName, assetFlag)
%
% dtiRawBuildBvecs(nvols, xform, [gradsFile=uigetfile], [bval=0.8], ...
%                                 [outBaseName=[]], [assetFlag=false]);
%
% Creates "FSL-style" 'bvecs' and 'bvals' files given a Bammer/GE style
% 'grads' file of gradient directions. The bvecs are reoriented to image
% space given the xform (leave empty for no xform).
%
% xform should be the rotation component of the scanner-to-image xform.
% This transform should be in the NIFTI qto_ijk field, but sometimes it is
% set in sto_ijk. This transform maps scanner coordinates (x,y,z) to
% physical, image coordinates (i,j,k). In other words, it tells us how to
% rotate the scanner space to be aligned with the image space. The bvecs
% are usually specified in the scanner space coordinate frame, so we can
% use this transform to rotate them to the image frame. Note that some
% protocols might keep the directions in image (or 'logical') space, in
% which case the xform was applied at acquisition. For such protocols, you
% should not rotate the bvecs (xform=[] or xform=eye(3)).
%
% If xform is 4x4, it is assumed to be an affine xform and the rotation
% compoenent will be extracted.
%
% assetFlag is an idiosyncratic thing for the Hedehus/Bammer sequence used
% at Stanford. This sequence reorients the gradient directions specified in
% the dwepi.grads file to logical space rather than keeping the directions
% in scanner space. Thus, the bvecs do not need to be reoriented for
% oblique prescriptions as with some other DTI sequences. However, this
% sequence assumes that the 2nd column in dwepi.grads is the phase-encode
% dim. If your phase-enmcode is the usual '2', then this is fine. But, if
% you run ASSET and change the phase encode to L-R (dim 1), you need to
% swap the first and second columns of dwepi.grads. Also, there appears to
% be a flip in the phase-encode dim, so you also need to flip the sign on
% the phase-encode column.
%
% WEB RESOURCES:
%   mrvBrowseSVN('dtiRawBuildBvecs');
%   http://white.stanford.edu/newlm/index.php/DTI_Preprocessing
%
% EXAMPLE USAGE:
%   dwRaw = niftiRead('rawDti.nii.gz');
%   nVols = size(dwRaw.data,4);
%   xform = affineExtractRotation(dwRaw.qto_ijk);
%   [bvecs,bvals] = dtiRawBuildBvecs(nVols, xform, gradsFile, bval, 'rawDti')
%
%
% 2006.12.11 RFD: wrote it with BAW & AJS.
% 2007.01.29 RFD: reverted to using qto_ijk to get rotation matrix rather
% than computing it directly from the quaternion. I think it is safer to
% let the NIFTI code build the matrix for us. Also, we were occasionally
% getting imaginary values in our rotation matrix.
% 2007.03.21 RFD: changed default bval to 0.8 (consistent with our
% preference for msec/micrometers^2 units) and renamed the file to be more
% consistent with the other dti pre-processing functions.
% 2009.05.20 RFD: we now adjust bvalues to reflect gradient strength for
% bvecs with norm != 1.
%
% (C) Stanford University, VISTA
%

%% Check Inputs

if(~exist('outBaseName','var')), outBaseName = ''; end
if(nargout==0&&isempty(outBaseName))
    error('outBaseName must be specificed when nargout==0.');
end
if(~exist('gradsFile','var')||isempty(gradsFile))
  if(isunix)
    defaultDir = '/usr/local/dti/diffusion_grads/';
  else
    defaultDir = pwd;
  end
  [f,p] = uigetfile({'*.grads';'*.*'}, 'Select the GE grads file...',defaultDir);
  if(isnumeric(f)), error('User cancelled.'); end
  gradsFile = fullfile(p,f);
end

if(~exist('bval','var'))
  bval = 0.8;
  warning(sprintf('Using default b-value of %f msec/micrometers^2.',bval));
end

if(~exist('assetFlag','var')||isempty(assetFlag)), assetFlag = false; end

if(~exist('xform','var')); xform = []; end
if(isempty(xform))
  xform = eye(3);
elseif(size(xform,1)==4||size(xform,2)==4)
  xform = affineExtractRotation(xform);
end

%% Build the Bvecs/Bvals

% Read the grads file
grads = dlmread(gradsFile);

% We may need to transpose grads file
if size(grads,1)<size(grads,2)
    grads = grads';
end
if(size(grads,1)<nVols)
  % Assume that there are repeats and that the last repeat might not have
  % finished.
  grads = repmat(grads,ceil(nVols/size(grads,1)),1);
  grads = grads(1:nVols,:);
end

if(assetFlag)
    disp('Applying ASSET reorientation for Hedehus/Bammer sequence...');
    grads = grads*[0 -1 0; 1 0 0; 0 0 1];
end

% X-form the bvecs
bvecs       = xform*grads';
bvecNorm    = sqrt(sum(bvecs.^2));
nz          = bvecNorm~=0;
bvecs(:,nz) = bvecs(:,nz)./repmat(bvecNorm(nz),[3 1]);
bvals       = repmat(bval,1,size(bvecs,2));

% Zero-out non-dwi bvals
bvals(all(bvecs==0)) = 0;

% Scale the bvalues according to gradient magnitude. This assumes that the
% specified b-value is the max bvalue. Note that we only do this when all
% the bvec norms are 1 or less. If any of the bvec norms are >1, then we
% assume that the bvecs are just encoded sloppily and their norms do not
% really reflect the gradient amplitudes.
if(all(bvecNorm<=1))
    bvals = bvals.*bvecNorm.^2;
end

% Write out the bvals and bvecs
if(~isempty(outBaseName))
    dlmwrite([outBaseName '.bvecs'],bvecs,' ');
    dlmwrite([outBaseName '.bvals'],bvals,' ');
end

return;


function [dat, mask] = apply_mask(dat, mask, varargin)
% Apply a mask image (image filename or fmri_mask_image object) to an image_vector object
% stored in dat.
%
% This can be used to:
%  - Mask an image_vector or fmri_data object with a mask
%  - Obtain "pattern expression" for a weight map (entered as the
%    mask, here) in a series of images stored in dat.
%
% The mask or weight map does not have to be in the same space as the dat;
% it will be resampled to the space of the data in dat.
%
% To extract pattern expression values for each ROI within a mask use extract_roi_averages()
%
% :Optional Inputs:
%
%   **pattern_expression:**
%        calculate and return the dot product of each
%        image in dat and the values in the mask.  This is useful if comparing
%        expression values that are comprised of different datasets or differing
%        number of voxels.
%
%   **correlation:**
%        calculate the pearson correlation coefficient of each
%        image in dat and the values in the mask.
%
%   **norm_mask:**
%        normalize the mask weights by L2 norm, for patt expression
%        only.
%
%   **ignore_missing:**
%        use with pattern expression only. Ignore weights on voxels
%        with zero values in test image. If this is not entered, the function will
%        check for these values and give a warning.
%
%   **invert:**
%        Invert the mask so that out-of-mask voxels are now in (using
%        the mask as an 'exclude mask' rather than an include-mask. If pattern
%        expression is requested, the behavior is different, and it inverts the
%        sign of in-mask pattern weights.
%
%   **cosine_similarity:**
%        use with pattern expression only. scales expression by product of
%        l2 norms (norm(mask)*norm(dat))
%
% :Examples:
% ::
%
%     [dat, mask] = apply_mask(dat, mask)
%     [dat, mask] = apply_mask(dat, mask image name)
%     [dat, mask] = apply_mask(dat, mask image vector object)
%     [pattern_exp_values] = apply_mask(dat, weight map image, 'pattern_expression', 'ignore_missing')
%     [pattern_exp_values] = apply_mask(dat, weight map image, 'pattern_expression', 'ignore_missing','correlation')
%
%
% :See also:
%
% extract_roi_averages, to get individual region averages / local pattern expression
% apply_nps, which does whole-pattern and local regional expression
%
% ..
%    Notes:
%    Last modified: 10/30/11 to add support for masks that are weight maps
%    12/15/13:  Luke Chang - added correlation option for pattern-expression
%    5/10/2016: Phil Kragel - added cosine similarity
%    8/2/2018: Tor Wager - if mask is a statistic_image object, apply .sig field
%
% ..

dopatternexpression = 0; % set options
donorm = 0;
doinvert = 0;

% Now handled by canlab_pattern_similarity
% doignoremissing = 0;
% docorr = 0; %run correlation instead of dot-product for pattern expression
% docosine = 0;

if any(strcmp(varargin, 'pattern_expression'))
    dopatternexpression = 1;

    % Now handled by canlab_pattern_similarity
    %     if any(strcmp(varargin, 'ignore_missing'))
    %         doignoremissing = 1;
    %     end
    %
    %     if any(strcmp(varargin, 'cosine_similarity'))
    %         docosine = 1;
    %     end
    %
    %
    %     if any(strcmp(varargin, 'correlation')) % run correlation instead of dot-product
    %         docorr = 1;
    %     end

end

if any(strcmp(varargin, 'invert'))
    doinvert = 1;
end

if any(strcmp(varargin, 'norm_mask')) % only good for pattern expression
    donorm = 1;
end

% create mask_image object if we have a filename
if ischar(mask)
    mask = fmri_mask_image(mask);
end

isdiff = compare_space(dat, mask);

if isdiff == 1 || isdiff == 2 % diff space, not just diff voxels
    % == 3 is ok, diff non-empty voxels

    % Both work, but resample_space does not require going back to original
    % images on disk.
    %mask = resample_to_image_space(mask, dat);
    mask = resample_space(mask, dat);

    % tor added may 1 - removed voxels was not legal otherwise
    %mask.removed_voxels = mask.removed_voxels(mask.volInfo.wh_inmask);
    % resample_space is not *always* returning legal sizes for removed
    % vox? maybe this was updated to be legal

    if length(mask.removed_voxels) == mask.volInfo.nvox
        disp('Warning: resample_space returned illegal length for removed voxels. Fixing...');
        mask.removed_voxels = mask.removed_voxels(mask.volInfo.wh_inmask);
    end

end

% dat = remove_empty(dat); Tor edited 9/10/17 for computational efficiency

% nonemptydat: Logical index of voxels with valid data, in in-mask space
nonemptydat = get_nonempty_voxels(dat);

dat = replace_empty(dat);

% Check/remove NaNs. This could be done in-object...
mask.dat(isnan(mask.dat)) = 0;

% Replace if necessary
mask = replace_empty(mask);

% if mask is a statistic_image object, apply .sig field
if isa(mask, 'statistic_image')

    mask.dat(~mask.sig) = 0;  % for masking purposes

end

if doinvert

    if dopatternexpression
        mask.dat = -mask.dat;
        disp('Reversing sign of mask values.');

    else
        disp('Inverting mask: Out-of-mask areas are now in.');
        maskdat = reconstruct_image(mask);
        maskdat = double(~maskdat);
        mask = rebuild_volinfo_from_dat(mask, maskdat(:));
        mask = resample_space(mask, dat);
    end

end

% save which are in mask, but do not replace with logical, because mask may
% have weights we want to preserve
inmaskdat = logical(mask.dat);

% Remove out-of-mask voxels
% ---------------------------------------------------

% mask.dat has full list of voxels
% need to find vox in both mask and original data mask

if size(mask.volInfo.image_indx, 1) == size(dat.volInfo.image_indx, 1)
    n = size(mask.volInfo.image_indx, 1);

    if size(nonemptydat, 1) ~= n % should be all vox OR non-empty vox
        nonemptydat = zeroinsert(~dat.volInfo.image_indx, nonemptydat);
    end

    if size(inmaskdat, 1) ~= n
        inmaskdat = zeroinsert(~mask.volInfo.image_indx, inmaskdat);
    end

    inboth = inmaskdat & nonemptydat;

    % List in space of in-mask voxels in dat object.
    % Remove these from the dat object
    to_remove = ~inboth(dat.volInfo.wh_inmask);

    to_remove_mask = ~inboth(mask.volInfo.wh_inmask);

elseif size(mask.dat, 1) == size(dat.volInfo.image_indx, 1)

    % mask vox are same as total image vox
    nonemptydat = zeroinsert(~dat.volInfo.image_indx, nonemptydat);
    inboth = inmaskdat & dat.volInfo.image_indx & nonemptydat;

    % List in space of in-mask voxels in dat object.
    to_remove = ~inboth(dat.volInfo.wh_inmask);

    to_remove_mask = ~inboth(mask.volInfo.wh_inmask);

elseif size(mask.dat, 1) == size(dat.volInfo.wh_inmask, 1)
    % mask vox are same as in-mask voxels in dat
    inboth = inmaskdat & dat.volInfo.image_indx(dat.volInfo.wh_inmask) & nonemptydat;

    % List in space of in-mask voxels in .dat field.
    to_remove = ~inboth;

    to_remove_mask = ~inboth;

else
    fprintf('Sizes do not match!  Likely bug in resample_to_image_space.\n')
    fprintf('Vox in mask: %3.0f\n', size(mask.dat, 1))
    fprintf('Vox in dat - image volume: %3.0f\n', size(dat.volInfo.image_indx, 1));
    fprintf('Vox in dat - image in-mask area: %3.0f\n', size(dat.volInfo.wh_inmask, 1));
    disp('Stopping to debug');
    keyboard
end

dat = remove_empty(dat, to_remove);
mask = remove_empty(mask, to_remove_mask);

if dopatternexpression
    %mask = replace_empty(mask); % need for weights to match

    weights = double(mask.dat); % force double b/c of matlab instabilities
    dat.dat = double(dat.dat); % force double b/c of matlab instabilities

    if donorm, weights = weights ./ norm(weights); end

    % Pass similarity metric in to canlab_pattern_similarity
    similarity_output = canlab_pattern_similarity(dat.dat, weights, varargin{:});

    dat = similarity_output;

    % CHECK and weight
    % ---------------------------------------------------------

    % Pattern weights can have zeros, which may be valid values in voxels,
    % i.e., with binary masks
    % Images with values of zero or NaN are considered out-of-mask, as they
    % are not valid values. These should be excluded from both image and
    % mask when calculating similarity.
    % Thus, there is an asymmetry between pattern mask and image data
    % in considering which voxels to use.
    % Otherwise, all dot product and similarity metrics are standard.

    % We also need to calculate bad values on an image-by-image basis, not
    % relying on remove_empty to exclude voxels with ineligible values
    % across the entire set of images.

    % End check and weight ---------------------------------------------------------

end % Pattern expression

% Handle special types
% So far, atlas

if isa(dat, 'atlas')

    dat = check_properties(dat, 'compress_index');

end

end  % Main function

function nonemptydat = get_nonempty_voxels(dat)
empty_voxels = all(dat.dat' == 0 | isnan(dat.dat'), 1)';

if size(empty_voxels, 1) == dat.volInfo.n_inmask
    % .dat is full in-mask length, we have not called remove_voxels or there are none to remove
    nonemptydat = ~empty_voxels;

elseif length(dat.removed_voxels) == dat.volInfo.n_inmask
    % .dat is not in-mask length, and we have .removed_voxels defined
    nonemptydat = false(dat.volInfo.n_inmask, 1);
    nonemptydat(~dat.removed_voxels) = true;

    % additional: we could have invalid voxels that have been changed/added since
    % remove_empty was last called.
    %     dat.removed_voxels(dat.removed_voxels) =
    %     nonemptydat(empty_voxels
end

end


 function test = ir_read_op(arg1, arg2, arg3, arg4, arg5)
%function test = ir_read_op(dir, file, slice, frame, chat)
%function test = ir_read_op(file, slice, frame, chat)
%|
%| Use ASPIRE "op" command to read in a data file
%| from directory 'dir' (which is optional)
%| or optionally one slice,frame of a file
%| Jeff Fessler

slice = [];
frame = 0;
chat = 0;

if nargin < 1 || nargin > 5
	help read
	error args
elseif nargin == 1
	file = arg1;
else
	if ischar(arg2)
		file = [arg1 arg2];
		if nargin >= 3,	slice	= arg3;	end
		if nargin >= 4,	frame	= arg4;	end
		if nargin >= 5,	chat	= arg5;	end
	else
		file = arg1;
		if nargin >= 2,	slice	= arg2;	end
		if nargin >= 3,	frame	= arg3;	end
		if nargin >= 4,	chat	= arg4;	end
	end
end

if length(file) > 4
	ismat = strcmp(file((end-3):end), '.mat');
else
	ismat = 0;
end

op = sprintf('op -chat %d ', chat);

if exist(file) ~= 2
	fail(['File ' file ' not found!'])
end

tmp = '/tmp/tmp.mat';
if exist(tmp) == 2
	os_run(['/bin/rm -f ' tmp])
end

if ismat
	type = 'double';
else
%	type = '-';
	type = 'raw'; % ask op for raw file format
end

if ~isempty(slice)
	if length(slice) == 1
		choose = sprintf('%d %d %d %d 0 0', slice, slice, frame, frame);
	elseif length(slice) == 2
		choose = sprintf('%d %d %d %d 0 0', slice(1), slice(2), frame, frame);
	else
		error slice
	end
	comm = [op 'slice ' tmp ' ' file ' ' type ' ' choose];
else
	comm = [op 'convert ' tmp ' ' file ' ' type ' test'];
end

if chat
	disp(comm)
end

os_run(comm)

if ~exist(tmp, 'file')
	fail(['in read.m: Conversion error on: ' file])
end

load(tmp)
eval(['!/bin/rm -f ' tmp])


function skypos = place (tjd, object, locatn, icoord, star, observ)

% this function computes the apparent direction of a star or solar
% system body at a specified time and in a specified coordinate
% system.  based on kaplan, et al. (1989), astronomical journal 97,
% 1197-1210, with some enhancements from klioner (2003),
% astronomical journal 125, 1580-1597.

%  tjd    = tt julian date for place (in)

%  object = character string identifying object of interest (in)

%           for solar system
%           body,             specify the name using all upper-
%                             case letters ('sun', 'moon',
%                             'jupiter', etc.),
%                             - or -
%                             specify the body id number
%                             in a 4-character string of the
%                             form '=nnn', where nnn is the
%                             body id number
%           for star,         provide a blank string, the word
%                             'star', or any string beginning
%                             with '*'

%  locatn = integer code specifying location of observer (in)

%           set locatn=0 for observer at geocenter
%           set locatn=1 for observer on surface of earth
%           set locatn=2 for observer on near-earth spacecraft

%  icoord = integer code specifying coordinate system of output
%           position (in)

%           set icoord=0 for gcrs (or 'local gcrs')
%           set icoord=1 for true equator and equinox of date
%           set icoord=2 for true equator and cio of date
%           set icoord=3 for astrometric coordinates, i.e.,
%                        without light deflection or aberration

%  star   = array of catalog data for star (in)

%           (not used if solar system body requested)

%           star(1) = icrs right ascension in hours
%           star(2) = icrs declination in degrees
%           star(3) = icrs proper motion in ra in
%                     milliarcseconds/year
%           star(4) = icrs proper motion in dec in
%                     milliarcseconds/year
%           star(5) = parallax in milliarcseconds
%           star(6) = radial velocity in kilometers/second
%           further star array elements are not used here
%           but are reserved for future use

%  observ = array of data specifying location of observer (in)

%           (not used if locatn=0)

%           for locatn = 1,

%           observ(1) = geodetic longitude (wgs-84) of observer
%                       (east +) in degrees
%           observ(2) = geodetic latitude (wgs-84) of observer
%                       (north +) in degrees
%           observ(3) = height of observer above ellipsoid
%                       in meters
%           observ(4) = value of delta-t in seconds
%                       (delta-t=tt-ut1)
%           observ(5) = (not used, reserved for future use)
%           observ(6) = (not used, reserved for future use)

%           for locatn = 2,

%           observ(1) = geocentric x in kilometers
%           observ(2) = geocentric y in kilometers
%           observ(3) = geocentric z in kilometers
%           observ(4) = geocentric x-dot in kilometers/second
%           observ(5) = geocentric y-dot in kilometers/second
%           observ(6) = geocentric z-dot in kilometers/second
%           with respect to true equator and equinox of date

%  skypos = array of output data specifying object's place
%           on the sky at time tjd, with respect to the
%           specified output coordinate system (out)

%           skypos(1) = x, dimensionless      unit vector
%           skypos(2) = y, dimensionless      toward object
%           skypos(3) = z, dimensionless
%           skypos(4) = apparent, topocentric, or astrometric
%                       right ascension in hours
%           skypos(5) = apparent, topocentric, or astrometric
%                       declination in degrees
%           skypos(6) = true (geometric, euclidian) distance
%                       to solar system body in au at time tjd,
%                       or 0.0d0 for star
%           skypos(7) = radial velocity in kilometers/second

%           further skypos array elements are not used here
%           but are reserved for future use

% note 1: values of locatn and icoord for various standard kinds of place:

%   locatn = 0 and icoord = 1 apparent place
%   locatn = 1 and icoord = 1 topocentric place
%   locatn = 0 and icoord = 0 virtual place
%   locatn = 1 and icoord = 0 local place
%   locatn = 0 and icoord = 3 astrometric place
%   locatn = 1 and icoord = 3 topocentric astrometric place

% note 2: arrays star and skypos may be expanded in the future, and
% this can be allowed for in the calling code by dimensioning
% these arrays with 20 and 10 elements, respectively, even though
% elements beyond star(6) and skypos(7) are not now referred to in
% this subroutine.

% note 3: if locatn = 1 and observ(4) = 0.0d0, the value of delta-t will
% be obtained from getdt, which provides the last value of delta-t
% defined by the user via call to setdt.

% note 4: skypos(7), the radial velocity, is the predicted
% radial velocity measure (z) times the speed of light, an
% inherently spectroscopic measure. for a star, it
% includes all effects, such as gravitational red shift,
% contained in the catalog barycentric radial velocity measure,
% which is assumed given in star(6). for a solar system
% body, it applies to a fictitious emitter at the center of the
% observed object, assumed massless (no gravitational red shift),
% and does not in general apply to reflected light.

% ported from NOVAS 3.1

%%%%%%%%%%%%%%%%%%%%%%%

% t0 = tdb julian date of epoch j2000.0 (tt)

t0 = 2451545.0d0;

% get body ids

iearth = idss_novas ('earth');

isun = idss_novas ('sun');

% get c, the speed of light in au/day

c = astcon ('c(au/day)', 1.0d0);

% check on earth as an observed object

if (strcmp(object, '=3') == 1 && locatn ~= 2)

    fprintf ('\n place: will not process earth as observed object except when locatn = 2');

    return

end

% compute tdbjd, the tdb julian date corresponding to ttjd

ttjd = tjd;

tdbjd = tjd;

[x, secdif] = novas_times (tdbjd);

tdbjd = ttjd + secdif / 86400.0d0;

% get position and velocity of the earth wrt barycenter of solar system, in icrs

% fprintf('\n\nearth state vector wrt barycenter\n');

[peb, veb, ierr] = solsys (tdbjd, iearth, 0);

if (ierr ~= 0)

    fprintf ('\nplace: cannot obtain coordinates of earth at jd %16.8f', tjd);

    return

end

% get position and velocity of the sun wrt barycenter of solar system, in icrs

% fprintf('\n\nsun state vector wrt barycenter\n');

[psb, vsb, ierr] = solsys (tdbjd, isun, 0);

if (ierr ~= 0)

    fprintf ('\nplace: cannot obtain coordinates of sun at jd %16.8f', tjd);

    return

end

% get position and velocity of observer

if (locatn == 1 || locatn == 2)

    % for topocentric place, get geocentric position and velocity
    % vectors of observer

    % fprintf('\n\nobserver state vector wrt geocenter\n');

    [pog, vog] = geopos (ttjd, locatn, observ);

    loc = 1;

else

    % for geocentric place, there is nothing to do

    for j = 1:3

        pog(j) = 0.0d0;

        vog(j) = 0.0d0;

    end

    loc = 0;

end

% compute position and velocity of observer wrt barycenter of
% solar system (galilean transformation from gcrs to bcrs)

for j = 1:3

    pob(j) = peb(j) + pog(j);

    vob(j) = veb(j) + vog(j);

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% find geometric position of observed object
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if (strcmp(object, 'star') == 1 || strcmp(object, ' ') == 1 || strcmp(object(1:1), '*'))

    %%%%%%%%%%%%%%%%%%%%%%%%%
    % observed object is star
    %%%%%%%%%%%%%%%%%%%%%%%%%

    idbody = -9999;

    % get position of star updated for its space motion

    [pos1, vel1] = vectrs (star(1), star(2), star(3), star(4), star(5), star(6));

    dt = dlight (pos1, pob);

    pos2 = propmo (t0, pos1, vel1, tdbjd + dt);

    % get position of star wrt observer (corrected for parallax)

    [pos3, tlight] = geocen (pos2, pob);

    dis = 0.0d0;

else

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % observed object is solar system body
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % get id number of body

    if (strcmp(object(1:1), '=') == 1)

        idbody = object(2:length(object));

    else

        idbody = idss_novas (object);

        if (idbody == -9999)

            fprintf ('\nplace: cannot obtain coordinates of object at jd %16.8f', tjd);

            return

        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % get position of body wrt barycenter of solar system
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % fprintf('\n\nobject state vector wrt barycenter\n');

    [pos1, vel1, ierr] = solsys (tdbjd, str2num(idbody), 0);

    if (ierr ~= 0)

        fprintf ('\nplace: cannot obtain coordinates of object at jd %16.8f', tjd);

        return

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % get position of body wrt observer, and true (euclidian) distance
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % fprintf('\n\nobject state vector wrt observer\n');

    [pos2, tlight] = geocen (pos1, pob);

    dis = tlight * c;

    % get position of body wrt observer, antedated for light-time

    % fprintf('\n\nobject state vector wrt observer - littim\n');

    [pos3, tlight] = littim (tdbjd, idbody, pob, 0.0);

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% apply gravitational deflection of light and aberration
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if (icoord == 3)

    % these calculations are skipped for astrometric place

    for j = 1:3

        pos5(j) = pos3(j);

    end

else

    % variable loc determines whether earth deflection is included

    if (loc == 1)

        [x, frlimb] = limang (pos3, pog);

        if (frlimb < 0.8d0)

            loc = 0;

        end

    end

    % compute gravitational deflection and aberration

    pos4 = grvdef (tdbjd, loc, pos3, pob);

    pos5 = aberat (pos4, vob, tlight);

    % position vector is now in gcrs

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% transform, if necessary, to output coordinate system
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if (icoord == 1)

    % transform to equator and equinox of date

    pos6 = frame (pos5, 1);

    pos7 = preces (t0, pos6, tdbjd);

    pos8 = nutate (tdbjd, pos7);

elseif (icoord == 2)

    % transform to equator and cio of date

    % obtain the basis vectors, in the gcrs, of the celestial
    % intermediate system

    kcio = cioloc (tdbjd, rcio);

    [px, py, pz] = ciobas (tdbjd, rcio, kcio);

    % transform position vector to celestial intermediate system

    pos8(1) = px(1) * pos5(1) + px(2) * pos5(2) + px(3) * pos5(3);

    pos8(2) = py(1) * pos5(1) + py(2) * pos5(2) + py(3) * pos5(3);

    pos8(3) = pz(1) * pos5(1) + pz(2) * pos5(2) + pz(3) * pos5(3);

else

    % no transformation -- keep coordinates in gcrs (or icrs for astrometric place)

    for j = 1:3

        pos8(j) = pos5(j);

    end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% set up star data, if applicable
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if (idbody == -9999)

    rvs(1) = star(1);

    rvs(2) = star(2);

    rvs(3) = star(6);

    if (star(5) <= 0.0d0)

        vel1(1) = 0.0d0;

        vel1(2) = 0.0d0;

        vel1(3) = 0.0d0;

    end

else

    rvs(1) = 0.0d0;

    rvs(2) = 0.0d0;

    rvs(3) = 0.0d0;

end

% compute distances: observer-geocenter, observer-sun, object-sun

rvd(1) = sqrt((pob(1) - peb(1))^2 + (pob(2) - peb(2))^2 + (pob(3) - peb(3))^2);

rvd(2) = sqrt((pob(1) - psb(1))^2 + (pob(2) - psb(2))^2 + (pob(3) - psb(3))^2);

rvd(3) = sqrt((pos1(1) - psb(1))^2 + (pos1(2) - psb(2))^2 + (pos1(3) - psb(3))^2);

rv = radvl (pos3, vel1, vob, rvs, rvd);

% finish up

[ra, dec] = angles (pos8);

x = sqrt (pos8(1)^2 + pos8(2)^2 + pos8(3)^2);

for j = 1:3

    skypos(j) = pos8(j) / x;

end

skypos(4) = ra;

skypos(5) = dec;

skypos(6) = dis;

skypos(7) = rv;


function ons_secs = tapas_physio_preprocess_phys_timeseries(ons_secs, sqpar, doNormalize)
% Preprocess raw cardiac/respiratory time series (e.g. amplitude
% normalization, padding for acquisition duration)
%
%   ons_secs = tapas_physio_preprocess_phys_timeseries(ons_secs)
%
% IN
%   ons_secs    raw time series (c(ardiac), r(espiratory), t(ime), cpulse
%               (from file))
%
% OUT
%   ons_secs    padded and normalized time series for preprocessing
%
% EXAMPLE
%   tapas_physio_preprocess_phys_timeseries
%
%   See also

% Author: Lars Kasper
% Created: 2016-02-28
% Copyright (C) 2016 TNU, Institute for Biomedical Engineering,
%                    University of Zurich and ETH Zurich.
%
% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public
% License (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/>.

if nargin < 3
    doNormalize = 1;
end

hasCardiacData = ~isempty(ons_secs.c);
hasRespData = ~isempty(ons_secs.r);
hasDetectedCardiacPulses = ~isempty(ons_secs.cpulse);
hasAcquisitionCodes = ~isempty(ons_secs.acq_codes);

%% Normalize cardiac/respiratory time series to max 1

if doNormalize
    if hasCardiacData
        maxAbsC = max(abs(ons_secs.c));
        ons_secs.c_scaling = maxAbsC ;
        ons_secs.c = ons_secs.c/maxAbsC;
    end

    if hasRespData
        maxAbsR = max(abs(ons_secs.r));
        ons_secs.r_scaling = maxAbsR ;
        ons_secs.r = ons_secs.r/maxAbsR;
    end

end

% since resampling might have occured during read-in, dt is recalculated
ons_secs.dt = ons_secs.t(2) - ons_secs.t(1);

%% Padding of cardiac/respiratory time series, if start too late or duration too short

durationScan = sqpar.TR*(sqpar.Nscans+sqpar.Ndummies);

t = ons_secs.t;

tStartScan = 0;
if t(1) > tStartScan
    nSamples = ceil(-t(1)/ons_secs.dt);
    paddingStart = zeros(nSamples, 1);

    ons_secs.t = [(nSamples:-1:1)'*ons_secs.dt + t(1);t];

    if hasCardiacData
        ons_secs.c = [paddingStart; ons_secs.c];
    end

    if hasRespData
        ons_secs.r = [paddingStart; ons_secs.r];
    end

    if hasAcquisitionCodes
        ons_secs.acq_codes = [paddingStart; ons_secs.acq_codes];
    end
end

t = ons_secs.t;
durationPhysLog = t(end) - t(1);

if durationPhysLog < durationScan

    nSamples = ceil((durationScan - durationPhysLog)/ons_secs.dt);
    paddingEnd = zeros(nSamples, 1);

    ons_secs.t = [t; (1:nSamples)'*ons_secs.dt + t(end)];
    if hasCardiacData
        ons_secs.c = [ons_secs.c; paddingEnd];
    end

    if hasRespData
        ons_secs.r = [ons_secs.r; paddingEnd];
    end

    if hasAcquisitionCodes
        ons_secs.acq_codes = [ons_secs.acq_codes; paddingEnd];
    end
end

%% Remove onset time of log file to simplify plotting
tStartLog = t(1);
ons_secs.t_start = tStartLog;

ons_secs.t = ons_secs.t - tStartLog;

if hasDetectedCardiacPulses
    ons_secs.cpulse = ons_secs.cpulse - tStartLog;
end


function cS = rotate_outer(cS,rot)
% rotate a crystal shape by an rotation or orientation
%
% Syntax
%
%    cS = rotate_outer(cS,rot)
%    cS = ori * cS
%
% Input
%  cS - @crystalShape
%  rot - @rotation
%  ori - @orientation
%
% Output
%  cS - @crystalShape

if size(cS.V,2) > 1, error('Not yet supported'); end

% duplicate the faces
shift = length(cS.V) * repmat((0:length(rot)-1),size(cS.F,1),1);
shift = repmat(shift(:),1,size(cS.F,2));

% shift faces indices
cS.F = repmat(cS.F,length(rot),1) + shift;

cS.V = (rotation(rot) * cS.V).';

end


% SYNTAX:
% [s,tRange] = hmrR_StimRejection_Nirs(t,s,tIncAuto,tIncMan,tRange)
%
% UI NAME:
% Stim_Exclude
%
% DESCRIPTION:
% Excludes stims that fall within the time points identified as
% motion artifacts from HRF calculation.
%
%
% INPUT:
% t:        the time vector (#time points x 1)
% s:        s matrix (#time points x #conditions) containing 1 for
%           each time point and condition that has a stimulus and
%           zeros otherwise.
% tIncAuto: time points (#time points x 1) identified as motion
%           artifacts by processing stream.
% tIncMan:  time points (#time points x 1) identified as motion
%           artifacts by user.
% tRange:   an array of 2 numbers [t1 t2] specifying how many
%           seconds surrounding motion artifacts, tIncMan and tIncAuto,
%           to consider as excluded data and therefore exclude any stimuli
%           which fall within those buffers.
%           Typically values are t1=-2 and t2 equal to the stimulus
%           duration.
%
% OUTPUT:
% s:        s matrix (#time points x #conditions) containing 1 for
%           each time point and condition that has a stimulus that is
%           included in the HRF calculation, -1 for a stimulus that is
%           excluded automatically in the processing stream, -2
%           for each stimulus excluded by a manually set patch and
%           zeros otherwise.
% tRange:   same tRange array as in the input
%
% USAGE OPTIONS:
% Stim_Exclude: [s,tRange] = hmrR_StimRejection_Nirs(t,s,tIncAuto,tIncMan,tRange)
%
% PARAMETERS:
% tRange: [-5.0, 10.0]
%

function [s,tRange] = hmrR_StimRejection_Nirs(t,s,tIncAuto,tIncMan,tRange)

if isempty(tIncAuto)
    tIncAuto = cell(1,1);
end
if isempty(tIncMan)
    tIncMan = cell(1,1);
end
tIncMan = tIncMan{1};

dt = (t(end)-t(1))/length(t);
tRangeIdx = [floor(tRange(1)/dt):ceil(tRange(2)/dt)];

smax = max(s,[],2);
lstS = find(smax==1);
for iS = 1:length(lstS)
    lst = round(min(max(lstS(iS) + tRangeIdx,1),length(t)));
    if ~isempty(tIncAuto) && min(tIncAuto(lst))==0
        s(lstS(iS),:) = -2*abs(s(lstS(iS),:));
    end
    if ~isempty(tIncMan) && min(tIncMan(lst))==0
        s(lstS(iS),:) = -1*abs(s(lstS(iS),:));
    end
end


function Control = setupController(P)

if P.Sim.springDoublePendulum
    %Then set everything to zero except for the proportional gains on the
    %leg length actuators. This turns them both into springs, which makes
    %the system conserve energy. This is a handle error check.

    %The Foot One actuator operates on the absolute angle of leg one
    Control.footOne.nomPos = 0;
    Control.footOne.nomVel = 0;
    Control.footOne.Kp = 0;
    Control.footOne.Kd = 0;

    %The Foot Two actuator operates on the absolute angle of leg two
    Control.footTwo.nomPos = 0;
    Control.footTwo.nomVel = 0;
    Control.footTwo.Kp = 0;
    Control.footTwo.Kd = 0;

    %The Leg One actuator operates on the length of leg one
    Control.legOne.nomPos = 1; %(m)
    Control.legOne.nomVel = 0;
    Control.legOne.Kp = 35;
    Control.legOne.Kd = 0;

    %The Leg Two actuator operates on the length of leg two
    Control.legTwo.nomPos = 1; %(m)
    Control.legTwo.nomVel = 0;
    Control.legTwo.Kp = 35;
    Control.legTwo.Kd = 0;

    %The Hip actuator operates on the angle between the legs: th2-th1
    Control.hip.nomPos = 0;
    Control.hip.nomVel = 0;
    Control.hip.Kp = 0;
    Control.hip.Kd = 0;
else
    %Allow system to be fully controlled. This will not necessarily
    %conserve energy.

    %The Foot One actuator operates on the absolute angle of leg one
    Control.footOne.nomPos = 0;
    Control.footOne.nomVel = 0;
    Control.footOne.Kp = 0;
    Control.footOne.Kd = 0;

    %The Foot Two actuator operates on the absolute angle of leg two
    Control.footTwo.nomPos = 0;
    Control.footTwo.nomVel = 0;
    Control.footTwo.Kp = 0;
    Control.footTwo.Kd = 0;

    %The Leg One actuator operates on the length of leg one
    Control.legOne.nomPos = 1; %(m)
    Control.legOne.nomVel = 0;
    Control.legOne.Kp = 25;
    Control.legOne.Kd = 10;

    %The Leg Two actuator operates on the length of leg two
    Control.legTwo.nomPos = 1; %(m)
    Control.legTwo.nomVel = 0;
    Control.legTwo.Kp = 25;
    Control.legTwo.Kd = 10;

    %The Hip actuator operates on the angle between the legs: th2-th1
    Control.hip.nomPos = 0;
    Control.hip.nomVel = 0;
    Control.hip.Kp = 100;
    Control.hip.Kd = 80;
end

end


function stats = FieldPSP(stims,lfp,varargin)

%FieldPSP - Measure field EPSPs (waveforms, slope, amplitude) across time.
%
%  Slope is determined as the maximum slope during the time window around stimulation.
%  Amplitude is determined as the maximum amplitude during the same time window.
%
%  USAGE
%
%    stats = FieldPSP(stims,lfp,<options>)
%
%    stims          stimulation timestamps
%    lfp            local field potential samples
%    <options>      optional list of property-value pairs (see table below)
%
%    =========================================================================
%     Properties    Values
%    -------------------------------------------------------------------------
%     'direction'   direction of the field EPSP, either 'up' (recording
%                   from str. pyr., default) or 'down' (recording from str.
%                   radiatum)
%     'durations'   durations before and after synchronizing events for each
%                   trial (in s) (default = [-0.01 0.02])
%     'slope'       where max slope should be sought (default = [0.001 0.005])
%     'amplitude'   where max amplitude should be sought
%                   (default = [0.005 0.010])
%     'mode'        compute slope and amplitude running averages using a
%                   fixed time window ('time'), or a window with a fixed
%                   number of stimulations ('count', default)
%     'window'      window length (in s or in counts) for running averages
%                   (default = 100)
%     'show'        either 'on' or 'off' (default)
%    =========================================================================
%
%  OUTPUT
%
%    stats.amplitude.t         times of max amplitude (relative to stimulation)
%    stats.amplitude.v         values of max amplitudes
%    stats.slope.t             times of max slopes (relative to stimulation)
%    stats.slope.v             values of max slopes
%    stats.psp.t               times of fPSP samples (relative to stimulation)
%    stats.psp.v               fPSP waveforms, one per stimulation
%

% Copyright (C) 2010-2012 by Michaël Zugaro
%
% 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 3 of the License, or
% (at your option) any later version.

% Default values
parent = [];
durations = [-0.01 0.02];
mode = 'count';
window = 100;
direction = 'up';
stats = [];
show = 'off';
slopeWindow = [0.001 0.005];
amplitudeWindow = [0.005 0.010];

% Check number of parameters
if nargin < 2 | mod(length(varargin),2) ~= 0,
  error('Incorrect number of parameters (type ''help FieldPSP'' for details).');
end

% Check parameter sizes
if ~isdvector(stims),
	error('Parameter ''stims'' is not a vector (type ''help FieldPSP'' for details).');
end
if ~isdmatrix(lfp),
	error('Parameter ''lfp'' is not a matrix (type ''help FieldPSP'' for details).');
end

% Parse parameter list
for i = 1:2:length(varargin),
	if ~ischar(varargin{i}),
		error(['Parameter ' num2str(i+2) ' is not a property (type ''help FieldPSP'' for details).']);
	end
	switch(lower(varargin{i})),
		case 'direction',
			direction = lower(varargin{i+1});
			if ~isstring_FMAT(direction,'up','down'),
				error('Incorrect value for property ''direction'' (type ''help FieldPSP'' for details).');
			end
		case 'durations',
			durations = varargin{i+1};
			if ~isdvector(durations,'#2','<'),
				error('Incorrect value for property ''durations'' (type ''help FieldPSP'' for details).');
			end
		case 'slope',
			slopeWindow = varargin{i+1};
			if ~isdvector(slopeWindow,'#2','<'),
				error('Incorrect value for property ''slope'' (type ''help FieldPSP'' for details).');
			end
		case 'amplitude',
			amplitudeWindow = varargin{i+1};
			if ~isdvector(amplitudeWindow,'#2','<'),
				error('Incorrect value for property ''amplitude'' (type ''help FieldPSP'' for details).');
			end
		case 'mode',
			mode = lower(varargin{i+1});
			if ~isstring_FMAT(mode,'time','count'),
				error('Incorrect value for property ''mode'' (type ''help FieldPSP'' for details).');
			end
		case 'parent',
			parent = varargin{i+1};
			if ~ishandle(parent),
				error('Incorrect value for property ''parent'' (type ''help FieldPSP'' for details).');
			end
		case 'window',
			window = varargin{i+1};
			if ~isdscalar(window,'>0'),
				error('Incorrect value for property ''window'' (type ''help FieldPSP'' for details).');
			end
		case 'show',
			show = lower(varargin{i+1});
			if ~isstring_FMAT(show,'on','off'),
				error('Incorrect value for property ''show'' (type ''help FieldPSP'' for details).');
			end
		otherwise,
			error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help FieldPSP'' for details).']);
	end
end

up = strcmp(direction,'up');
show = strcmp(show,'on');

% Some analyses depend on the method (fixed time vs fixed count), but others do not
% To simplify the code, we will use two variables:
%  - stims0 uses time, whatever the method
%  - stims  uses either time or count, depending on the method
stims0 = stims;
if strcmp(mode,'count'),
	stims = (1:length(stims))';
end

% Constants
nStims = length(stims);
height = max(lfp(:,2))-min(lfp(:,2));
nSlices = 12;
start = stims(1);
stop = stims(end);
window = (stop-start)/nSlices;
slices = (0:nSlices-1)*window+start;
nStims = length(stims);
frequency = 1/median(diff(lfp(:,1)));
smooth = frequency/1250;
nStimsPlottedPerSlice = 5;
nStimsPerSlice = nStims/nSlices;
nSkip = floor(nStims/(nSlices*(nStimsPlottedPerSlice-1))); % plot one stim every n
colors = Bright(nStimsPlottedPerSlice);

% Compute slope and amplitude for each stimulation

% Resynchronize all fPSPs (use only 15ms after stims)
[sync,index] = Sync(lfp,stims0,'durations',durations);

% Preallocate large matrix to store all fPSPs
n = sum(index==1);
stats.psp.v = nan(nStims,n);
stats.psp.t = linspace(durations(1),durations(2),n);
% Compute slope and amplitude for each stimulation
if show, figure; end
for i = 1:nStims,
	% Select the appropriate data, store in fPSP matrix, smooth and differentiate
	s = sync(index==i,:);
	stats.psp.v(i,:) = interp1(s(:,1),s(:,2),stats.psp.t);
	s(:,2) = Smooth(s(:,2),smooth);
	% Compute amplitude as the first local maximum (or minimum) in the appropriate time interval
	in = InIntervals(s(:,1),amplitudeWindow);
	if up,
		extrema = find(IsExtremum(s)&in);
	else
		extrema = find(IsExtremum(s,'mode','minima')&in);
	end
	if isempty(extrema),
		amplitude(i) = nan;
		tAmplitude(i) = nan;
		slope(i) = nan;
		tSlope(i) = nan;
		continue;
	end
	extremum = extrema(1);
	tAmplitude(i) = s(extremum,1);
	amplitude(i) = s(extremum,2);
	% Compute slope as the maximum slope in the appropriate time interval
	% (set all values outside this time interval to nan to make sure the max will be in the interval)
	ds = Diff(s);
	ds(~InIntervals(ds(:,1),slopeWindow),2) = nan;
	if up,
		[slope(i),j] = nanmax(ds(:,2));
	else
		[slope(i),j] = nanmin(ds(:,2));
	end
	tSlope(i) = ds(j,1);
	% Plot slope and amplitude
	slice = floor(i/nStimsPerSlice);
	stimInSlice = floor(i-1-slice*nStimsPerSlice);
	if show && rem(stimInSlice,nSkip) == 0 && stimInSlice >= 0,
		k = ceil(nSlices*i/nStims);
		l = floor(stimInSlice/nSkip)+1;
		if l <= nStimsPlottedPerSlice,
			SquareSubplot(nSlices,k);
			xlabel(num2str(slices(k)));
			hold on;
			PlotXY(s,'color',colors(l,:),'linewidth',3);
			PlotSlope(tAmplitude(i),amplitude(i),0,0.005,'r');
			PlotSlope(tSlope(i),s(j,2),slope(i),0.005,'k');
		end
	end
end

% Make sure all subplots use the same scale
if show, AdjustAxes('y','uniform'); end

stats.amplitude.t = tAmplitude;
stats.amplitude.v = amplitude;
stats.slope.t = tSlope;
stats.slope.v = slope;

% Additional figures: mean fPSPs, slopes and amplitudes (scatterplots + running averages), CVs, ISIs and short time CCGs

if show,
	% Mean fPSPs
	figure;
	for i = 1:nSlices,
		if strcmp(mode,'count'),
			start = floor((i-1)*nStimsPerSlice)+1;
			stop = floor(i*nStimsPerSlice);
			ok = start:stop;
		else
			ok = find(InIntervals(stims,[0 window]+start+(i-1)*window));
		end
		m = mean(stats.psp.v(ok,:));
		e = sem(stats.psp.v(ok,:));
		SquareSubplot(nSlices,i);
		xlabel(num2str(slices(i)));
		hold on;
		PlotMean(stats.psp.t,m,m-e/2,m+e/2,':');
	end
	% Make sure all subplots use the same scale
	AdjustAxes('y','uniform');

	figure;
	% 'Rectify' slope and amplitude
	if ~up,
		amplitude = -amplitude;
		slope = -slope;
	end
	if strcmp(mode,'time'),
		xLabel = 'Time (s)';
	else
		xLabel = 'Count';
	end
	% Slope (scatterplot)
	subplot(2,3,1);
	hold on;
	plot(stims,slope,'.','markersize',1);
	% Slope (running average)
	[ts,ms,es] = RunningAverage(stims,slope,'window',window,'overlap',0.5*window);
	PlotMean(ts,ms,es(:,1),es(:,2),':','k');
	PlotHVLines(slices,'v','color',[0.8 0.8 0.8]);
	xlim([stims(1,1) stims(end,1)]);
	ylabel('Slope (a.u.)');
	set(gca,'xtick',[]);
	% Slope time (scatterplot)
	subplot(2,3,2);
	hold on;
	plot(stims,tSlope*1000,'.','markersize',1);
	% Slope time (running average)
	[ts,ms,es] = RunningAverage(stims,tSlope*1000,'window',window,'overlap',0.5*window);
	PlotMean(ts,ms,es(:,1),es(:,2),':','k');
	PlotHVLines(slices,'v','color',[0.8 0.8 0.8]);
	xlim([stims(1,1) stims(end,1)]);
	ylabel('Slope time (ms)');
	set(gca,'xtick',[]);

	% Amplitude (scatter)
	subplot(2,3,4);
	hold on;
	plot(stims,amplitude,'.','markersize',1);
	% Amplitude (running average)
	[ta,ma,ea] = RunningAverage(stims,amplitude,'window',window,'overlap',0.5*window);
	PlotMean(ta,ma,ea(:,1),ea(:,2),':','k');
	PlotHVLines(slices,'v','color',[0.8 0.8 0.8]);
	xlim([stims(1,1) stims(end,1)]);
	xlabel(xLabel);
	ylabel('Amplitude (a.u.)');
	% Amplitude time (scatter)
	subplot(2,3,5);
	hold on;
	plot(stims,tAmplitude*1000,'.','markersize',1);
	% Amplitude time (running average)
	[ta,ma,ea] = RunningAverage(stims,tAmplitude*1000,'window',window,'overlap',0.5*window);
	xlim([stims(1,1) stims(end,1)]);
	PlotMean(ta,ma,ea(:,1),ea(:,2),':','k');
	PlotHVLines(slices,'v','color',[0.8 0.8 0.8]);
	ylabel('Amplitude time (ms)');
	xlabel(xLabel);

	% ISIs
	subplot(2,3,3);
	ds = diff(stims0(:,1));
	x = 0:0.1:10;
	h = hist(ds,x);
	h = h/sum(h);
	bar(x,h);
	xlabel('ISI');
	xlim([0 10]);
	ylabel('Count');
	% CV and CV2
	binSize = 0.1;
	smooth = 10;
	for i = 1:20,
		cv(i) = CV(stims0,'order',i,'binSize',binSize,'smooth',smooth,'method','fixed');
	end
	cv2 = CV(stims0,'measure','cv2');
	subplot(2,3,6);
	plot(cv,'+-');
	hold on;
	plot(cv2,'r+');
	xlabel('nth ISI');
	ylabel('CV & CV2');

	% Stims autocorrelograms
	figure;
	[ccg,x,y] = ShortTimeCCG(stims0,'min',1,'mode','norm','smooth',[2 0]);
	PlotShortTimeCCG(ccg,'x',x,'y',y);
	clim([0 0.01]);
end


function up = predictor(xp,x,u)

up = interp1(x,u,xp,'spline');


function objset=readAnnotation(Name_batch,theConf)
MaxObjNum=5000;

minArea=theConf.data.minArea;
objset(MaxObjNum).folder=[];
objset(MaxObjNum).filename=[];
objset(MaxObjNum).name=[];
objset(MaxObjNum).bndbox=[];
objset(MaxObjNum).ID=[];
load([theConf.data.imgdir,Name_batch,'_obj/img/data.mat'],'samples');
root=[theConf.data.imgdir,Name_batch,'_obj/img/img/'];
files=dir(root);
for i=1:length(files)-2
    folder=[Name_batch,'_obj/img/img/'];
    filename=sprintf('%05d.jpg',i);
    xmin=samples(i).obj.bndbox.xmin;
    ymin=samples(i).obj.bndbox.ymin;
    xmax=samples(i).obj.bndbox.xmax;
    ymax=samples(i).obj.bndbox.ymax;
    bndbox.xmin=int2str(xmin);
    bndbox.xmax=int2str(xmax);
    bndbox.ymin=int2str(ymin);
    bndbox.ymax=int2str(ymax);
    if(~IsAreaValid(bndbox,minArea))
        continue;
    end
    objset(i).folder=folder;
    objset(i).filename=filename;
    objset(i).name=filename;
    objset(i).bndbox=bndbox;
    objset(i).ID=i;
end
objset=objset(1:length(files)-2);
end

function pd=IsAreaValid(bndbox,minArea)
xmin=str2double(bndbox.xmin);
xmax=str2double(bndbox.xmax);
ymin=str2double(bndbox.ymin);
ymax=str2double(bndbox.ymax);
if((xmax-xmin+1)*(ymax-ymin+1)>=minArea)
    pd=true;
else
    pd=false;
end
end


function [T,irow,icol,DS]=CDTs(TrNum);
DatabasePath='ORL\s'; % - Path of the testing database
T=[];
DS=0;

escImages=1:TrNum; % Escape Images
for td = 1:40
       Files = dir(strcat(DatabasePath,int2str(td)));
for i = 1:size(Files,1)
  x=0;
  y=1;
    for j=1:length(escImages)
        if  strcmp(Files(i).name,strcat(int2str(escImages(j)),'.pgm'))
            x=1;
        else
            x=x;
        end
    end
    if not(strcmp(Files(i).name,'.')|strcmp(Files(i).name,'..')|strcmp(Files(i).name,'Thumbs.db')|x)
        DS = DS + 1; % Number of all images in the training database
        img = imread(strcat(DatabasePath,int2str(td),'/',Files(i).name));
        [irow icol dim] = size(img);
        if dim>1
            img=rgb2gray(img);
        end
        temp = reshape(img,irow*icol,1);   % Reshaping 2D images into 1D image vectors
    T = [T temp];
    end
end
end


function varargout = process_fooof(varargin)
% PROCESS_FOOOF: Applies the "Fitting Oscillations and One Over F" (specparam) algorithm on a Welch's PSD
%
% REFERENCE: Please cite the original algorithm:
%    Donoghue T, Haller M, Peterson E, Varma P, Sebastian P, Gao R, Noto T,
%    Lara AH, Wallis JD, Knight RT, Shestyuk A, Voytek B. Parameterizing
%    neural power spectra into periodic and aperiodic components.
%    Nature Neuroscience (2020)

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

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'specparam: Fitting oscillations and 1/f';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Frequency';
    sProcess.Index       = 490;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Fooof';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'timefreq'};
    sProcess.OutputTypes = {'timefreq'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;
    % Definition of the options
    % === FOOOF TYPE
    sProcess.options.implementation.Comment = {'Matlab', 'Python 3 (3.7 recommended)', 'specparam implementation:'; 'matlab', 'python', ''};
    sProcess.options.implementation.Type    = 'radio_linelabel';
    sProcess.options.implementation.Value   = 'matlab';
    sProcess.options.implementation.Controller.matlab = 'Matlab';
    sProcess.options.implementation.Controller.python = 'Python';
    % === FREQUENCY RANGE
    sProcess.options.freqrange.Comment = 'Frequency range for analysis: ';
    sProcess.options.freqrange.Type    = 'freqrange_static';   % 'freqrange'
    sProcess.options.freqrange.Value   = {[1 40], 'Hz', 1};
    % === POWER LINE
    sProcess.options.powerline.Comment = {'None', '50 Hz', '60 Hz', 'Ignore power line frequencies:'; '-5', '50', '60', ''};
    sProcess.options.powerline.Type    = 'radio_linelabel';
    sProcess.options.powerline.Value   = '60';
    sProcess.options.powerline.Class   = 'Matlab';
    % === PEAK TYPE
    sProcess.options.peaktype.Comment = {'Gaussian', 'Cauchy*', 'Best of both* (* experimental)', 'Peak model:'; 'gaussian', 'cauchy', 'best', ''};
    sProcess.options.peaktype.Type    = 'radio_linelabel';
    sProcess.options.peaktype.Value   = 'gaussian';
    sProcess.options.peaktype.Class   = 'Matlab';
    % === PEAK WIDTH LIMITS
    sProcess.options.peakwidth.Comment = 'Peak width limits (default=[0.5-12]): ';
    sProcess.options.peakwidth.Type    = 'freqrange_static';
    sProcess.options.peakwidth.Value   = {[0.5 12], 'Hz', 1};
    % === MAX PEAKS
    sProcess.options.maxpeaks.Comment = 'Maximum number of peaks (default=3): ';
    sProcess.options.maxpeaks.Type    = 'value';
    sProcess.options.maxpeaks.Value   = {3, '', 0};
    % === MEAN PEAK HEIGHT
    sProcess.options.minpeakheight.Comment = 'Minimum peak height (default=3): ';
    sProcess.options.minpeakheight.Type    = 'value';
    sProcess.options.minpeakheight.Value   = {3, 'dB', 1};
    % === PROXIMITY THRESHOLD
    sProcess.options.proxthresh.Comment = 'Proximity threshold (default=2): ';
    sProcess.options.proxthresh.Type    = 'value';
    sProcess.options.proxthresh.Value   = {2, 'stdev of peak model', 1};
    sProcess.options.proxthresh.Class   = 'Matlab';
    % === APERIODIC MODE
    sProcess.options.apermode.Comment = {'Fixed', 'Knee', 'Aperiodic mode (default=fixed):'; 'fixed', 'knee', ''};
    sProcess.options.apermode.Type    = 'radio_linelabel';
    sProcess.options.apermode.Value   = 'fixed';
    % === GUESS WEIGHT
    sProcess.options.guessweight.Comment = {'None', 'Weak', 'Strong', 'Guess weight (default=none):'; 'none', 'weak', 'strong', ''};
    sProcess.options.guessweight.Type    = 'radio_linelabel';
    sProcess.options.guessweight.Value   = 'none';
    sProcess.options.guessweight.Class   = 'Matlab';

    % === SORT PEAKS TYPE
    sProcess.options.sorttype.Comment = {'Peak parameters', 'Frequency bands', 'Sort peaks using:'; 'param', 'band', ''};
    sProcess.options.sorttype.Type    = 'radio_linelabel';
    sProcess.options.sorttype.Value   = 'param';
    sProcess.options.sorttype.Controller.param = 'Param';
    sProcess.options.sorttype.Controller.band = 'Band';
    sProcess.options.sorttype.Group   = 'output';
    % === SORT PEAKS PARAM
    sProcess.options.sortparam.Comment = {'Frequency', 'Amplitude', 'Std dev.', 'Sort by peak...'; 'frequency', 'amplitude', 'std', ''};
    sProcess.options.sortparam.Type    = 'radio_linelabel';
    sProcess.options.sortparam.Value   = 'frequency';
    sProcess.options.sortparam.Class   = 'Param';
    sProcess.options.sortparam.Group   = 'output';
    % === SORT FREQ BANDS
    DefaultFreqBands = bst_get('DefaultFreqBands');
    sProcess.options.sortbands.Comment = '';
    sProcess.options.sortbands.Type    = 'groupbands';
    sProcess.options.sortbands.Value   = DefaultFreqBands(:,1:2);
    sProcess.options.sortbands.Class   = 'Band';
    sProcess.options.sortbands.Group   = 'output';
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFile = Run(sProcess, sInputs) %#ok<DEFNU>
    % Initialize returned list of files
    OutputFile = {};

    % Fetch user settings
    implementation = sProcess.options.implementation.Value;
    opt.freq_range          = sProcess.options.freqrange.Value{1};
    opt.peak_width_limits   = sProcess.options.peakwidth.Value{1};
    opt.max_peaks           = sProcess.options.maxpeaks.Value{1};
    opt.min_peak_height     = sProcess.options.minpeakheight.Value{1} / 10; % convert from dB to B
    opt.aperiodic_mode      = sProcess.options.apermode.Value;
    opt.peak_threshold      = 2;   % 2 std dev: parameter for interface simplification
    opt.border_threshold    = 1;   % 1 std dev: proximity to edge of spectrum, static in Python
    opt.return_spectrum     = 0;   % SPM/FT: set to 1
    % Matlab-only options
    opt.power_line          = sProcess.options.powerline.Value;
    opt.peak_type           = sProcess.options.peaktype.Value;
    opt.proximity_threshold = sProcess.options.proxthresh.Value{1};
    opt.guess_weight        = sProcess.options.guessweight.Value;
    opt.thresh_after        = true;   % Threshold after fitting always selected for Matlab (mirrors the Python FOOOF closest by removing peaks that do not satisfy a user's predetermined conditions)
    % Python-only options
    opt.verbose    = false;
    % Output options
    opt.sort_type  = sProcess.options.sorttype.Value;
    opt.sort_param = sProcess.options.sortparam.Value;
	opt.sort_bands = sProcess.options.sortbands.Value;

    % Check input frequency bounds
    if (any(opt.freq_range < 0) || opt.freq_range(1) >= opt.freq_range(2))
        bst_report('error','Invalid Frequency range');
        return
    end

    hasOptimTools = 0;
    if exist('fmincon') == 2 && strcmp(implementation,'matlab')
        hasOptimTools = 1;
        disp('Using constrained optimization, Guess Weight ignored.')
    end

    % Initialize returned list of files
    OutputFile = {};
    for iFile = 1:length(sInputs)
        bst_progress('text',['Standby: FOOOFing spectrum ' num2str(iFile) ' of ' num2str(length(sInputs))]);
        % Load input file
        PsdMat = in_bst_timefreq(sInputs(iFile).FileName);
        % Exclude 0Hz from the computation
        if (opt.freq_range(1) == 0) && (PsdMat.Freqs(1) == 0) && (length(PsdMat.Freqs) >= 2)
            opt.freq_range(1) = PsdMat.Freqs(2);
        end

        % === COMPUTE FOOOF MODEL ===
        % Switch between implementations
        switch (implementation)
            case 'matlab'   % Matlab standalone FOOOF
                [FOOOF_freqs, FOOOF_data] = FOOOF_matlab(PsdMat.TF, PsdMat.Freqs, opt, hasOptimTools);
            case 'python'
                opt.peak_type = 'gaussian';
                [FOOOF_freqs, FOOOF_data] = process_fooof_py('FOOOF_python', PsdMat.TF, PsdMat.Freqs, opt);
                % Remove unnecessary structure level, allowing easy concatenation across channels, e.g. for display.
                FOOOF_data = FOOOF_data.FOOOF;
            otherwise
                error('Invalid implentation.');
        end

        % === FOOOF ANALYSIS ===
        TFfooof = PsdMat.TF(:,1,ismember(PsdMat.Freqs,FOOOF_freqs));
        [ePeaks, eAperiodics, eStats] = FOOOF_analysis(FOOOF_data, PsdMat.RowNames, TFfooof, opt.max_peaks, opt.sort_type, opt.sort_param, opt.sort_bands);

        % === PREPARE OUTPUT STRUCTURE ===
        % Create file structure
        PsdMat.Options.FOOOF = struct(...
            'options',    opt, ...
            'freqs',      FOOOF_freqs, ...
            'data',       FOOOF_data, ...
            'peaks',      ePeaks, ...
            'aperiodics', eAperiodics, ...
            'stats',      eStats);
        % Comment: Add FOOOF
        if ~isempty(strfind(PsdMat.Comment, 'PSD:'))
            PsdMat.Comment = strrep(PsdMat.Comment, 'PSD:', 'specparam:');
        else
            PsdMat.Comment = strcat(PsdMat.Comment, ' | specparam');
        end
        % History: Computation
        PsdMat = bst_history('add', PsdMat, 'compute', 'specparam');

        % === SAVE FILE ===
        % Filename: add _fooof tag
        [fPath, fName, fExt] = bst_fileparts(file_fullpath(sInputs(iFile).FileName));
        NewFile = file_unique(bst_fullfile(fPath, [fName, '_specparam', fExt]));
        % Save file
        bst_save(NewFile, PsdMat, 'v6');
        % Add file to database structure
        db_add_data(sInputs(iFile).iStudy, NewFile, PsdMat);
        % Return new file
        OutputFile{end+1} = NewFile;
    end
end

%% ===================================================================================
%  ===== MATLAB FOOOF ================================================================
%  ===================================================================================

%% ===== MATLAB STANDALONE FOOOF =====
function [fs, fg] = FOOOF_matlab(TF, Freqs, opt, hOT)
    % Find all frequency values within user limits
    fMask = (round(Freqs.*10)./10 >= opt.freq_range(1)) & (round(Freqs.*10)./10 <= opt.freq_range(2)) & ~mod(sum(abs(round(Freqs.*10)./10-[1;2;3].*str2double(opt.power_line)) >= 2),3);
    fs = Freqs(fMask);
    spec = log10(squeeze(TF(:,1,fMask))); % extract log spectra
    nChan = size(TF,1);
    if nChan == 1, spec = spec'; end
    % Initalize FOOOF structs
    fg(nChan) = struct(...
            'aperiodic_params', [],...
            'peak_params',      [],...
            'peak_types',       '',...
            'ap_fit',           [],...
            'fooofed_spectrum', [],...
            'peak_fit',         [],...
            'error',            [],...
            'r_squared',        []);
    % Iterate across channels
    for chan = 1:nChan
        bst_progress('set', round(chan./nChan.*100));
        % Fit aperiodic
        aperiodic_pars = robust_ap_fit(fs, spec(chan,:), opt.aperiodic_mode);
        % Remove aperiodic
        flat_spec = flatten_spectrum(fs, spec(chan,:), aperiodic_pars, opt.aperiodic_mode);
        % Fit peaks
        [peak_pars, peak_function] = fit_peaks(fs, flat_spec, opt.max_peaks, opt.peak_threshold, opt.min_peak_height, ...
            opt.peak_width_limits/2, opt.proximity_threshold, opt.border_threshold, opt.peak_type, opt.guess_weight,hOT);
        if opt.thresh_after && ~hOT  % Check thresholding requirements are met for unbounded optimization
            peak_pars(peak_pars(:,2) < opt.min_peak_height,:)     = []; % remove peaks shorter than limit
            peak_pars(peak_pars(:,3) < opt.peak_width_limits(1)/2,:)  = []; % remove peaks narrower than limit
            peak_pars(peak_pars(:,3) > opt.peak_width_limits(2)/2,:)  = []; % remove peaks broader than limit
            peak_pars = drop_peak_cf(peak_pars, opt.border_threshold, opt.freq_range); % remove peaks outside frequency limits
            peak_pars(peak_pars(:,1) < 0,:) = []; % remove peaks with a centre frequency less than zero (bypass drop_peak_cf)
            peak_pars = drop_peak_overlap(peak_pars, opt.proximity_threshold); % remove smallest of two peaks fit too closely
        end
        % Refit aperiodic
        aperiodic = spec(chan,:);
        for peak = 1:size(peak_pars,1)
            aperiodic = aperiodic - peak_function(fs,peak_pars(peak,1), peak_pars(peak,2), peak_pars(peak,3));
        end
        aperiodic_pars = simple_ap_fit(fs, aperiodic, opt.aperiodic_mode);
        % Generate model fit
        ap_fit = gen_aperiodic(fs, aperiodic_pars, opt.aperiodic_mode);
        model_fit = ap_fit;
        for peak = 1:size(peak_pars,1)
            model_fit = model_fit + peak_function(fs,peak_pars(peak,1),...
                peak_pars(peak,2),peak_pars(peak,3));
        end
        % Calculate model error
        MSE = sum((spec(chan,:) - model_fit).^2)/length(model_fit);
        rsq_tmp = corrcoef(spec(chan,:),model_fit).^2;
        % Return FOOOF results
        aperiodic_pars(2) = abs(aperiodic_pars(2));
        fg(chan).aperiodic_params = aperiodic_pars;
        fg(chan).peak_params      = peak_pars;
        fg(chan).peak_types       = func2str(peak_function);
        fg(chan).ap_fit           = 10.^ap_fit;
        fg(chan).fooofed_spectrum = 10.^model_fit;
        fg(chan).peak_fit         = 10.^(model_fit-ap_fit);
        fg(chan).error            = MSE;
        fg(chan).r_squared        = rsq_tmp(2);
        if opt.return_spectrum
            fg(chan).power_spectrum = spec(chan,:);
        end
        %plot(fs', [fg(chan).ap_fit', fg(chan).peak_fit', fg(chan).fooofed_spectrum'])
    end
end

%% ===== GENERATE APERIODIC =====
function ap_vals = gen_aperiodic(freqs,aperiodic_params,aperiodic_mode)
%       Generate aperiodic values, from parameter definition.
%
%       Parameters
%       ----------
%       freqs : 1xn array
%       	Frequency vector to create aperiodic component for.
%       aperiodic_params : 1x3 array
%           Parameters that define the aperiodic component.
%       aperiodic_mode : {'fixed', 'knee'}
%           Defines absence or presence of knee in aperiodic component.
%
%       Returns
%       -------
%       ap_vals : 1d array
%           Generated aperiodic values.

    switch aperiodic_mode
        case 'fixed'  % no knee
            ap_vals = expo_nk_function(freqs,aperiodic_params);
        case 'knee'
            ap_vals = expo_function(freqs,aperiodic_params);
        case 'floor'
            ap_vals = expo_fl_function(freqs,aperiodic_params);
    end
end

%% ===== CORE MODELS =====
function ys = gaussian(freqs, mu, hgt, sigma)
%       Gaussian function to use for fitting.
%
%       Parameters
%       ----------
%       freqs : 1xn array
%           Frequency vector to create gaussian fit for.
%       mu, hgt, sigma : doubles
%           Parameters that define gaussian function (centre frequency,
%           height, and standard deviation).
%
%       Returns
%       -------
%       ys :    1xn array
%       Output values for gaussian function.

    ys = hgt*exp(-(((freqs-mu)./sigma).^2) /2);

end

function ys = cauchy(freqs, ctr, hgt, gam)
%       Cauchy function to use for fitting.
%
%       Parameters
%       ----------
%       freqs : 1xn array
%           Frequency vector to create cauchy fit for.
%       ctr, hgt, gam : doubles
%           Parameters that define cauchy function (centre frequency,
%           height, and "standard deviation" [gamma]).
%
%       Returns
%       -------
%       ys :    1xn array
%       Output values for cauchy function.

    ys = hgt./(1+((freqs-ctr)/gam).^2);

end

function ys = expo_function(freqs,params)
%       Exponential function to use for fitting 1/f, with a 'knee' (maximum at low frequencies).
%
%       Parameters
%       ----------
%       freqs : 1xn array
%           Input x-axis values.
%       params : 1x3 array (offset, knee, exp)
%           Parameters (offset, knee, exp) that define Lorentzian function:
%           y = 10^offset * (1/(knee + x^exp))
%
%       Returns
%       -------
%       ys :    1xn array
%           Output values for exponential function.

    ys = params(1) - log10(abs(params(2)) +freqs.^params(3));

end

function ys = expo_nk_function(freqs, params)
%       Exponential function to use for fitting 1/f, without a 'knee'.
%
%       Parameters
%       ----------
%       freqs : 1xn array
%           Input x-axis values.
%       params : 1x2 array (offset, exp)
%           Parameters (offset, exp) that define Lorentzian function:
%           y = 10^offset * (1/(x^exp))
%
%       Returns
%       -------
%       ys :    1xn array
%           Output values for exponential (no-knee) function.

    ys = params(1) - log10(freqs.^params(2));

end

function ys = expo_fl_function(freqs, params)

    ys = log10(f.^(params(1)) * 10^(params(2)) + params(3));

end

%% ===== FITTING ALGORITHM =====
function aperiodic_params = simple_ap_fit(freqs, power_spectrum, aperiodic_mode)
%       Fit the aperiodic component of the power spectrum.
%
%       Parameters
%       ----------
%       freqs : 1xn array
%           Frequency values for the power spectrum, in linear scale.
%       power_spectrum : 1xn array
%           Power values, in log10 scale.
%       aperiodic_mode : {'fixed','knee'}
%           Defines absence or presence of knee in aperiodic component.
%
%       Returns
%       -------
%       aperiodic_params : 1xn array
%           Parameter estimates for aperiodic fit.

%       Set guess params for lorentzian aperiodic fit, guess params set at init
    options = optimset('Display', 'off', 'TolX', 1e-4, 'TolFun', 1e-6, ...
        'MaxFunEvals', 5000, 'MaxIter', 5000);

    switch (aperiodic_mode)
        case 'fixed'  % no knee
            exp_guess = -(power_spectrum(end)-power_spectrum(1))./log10(freqs(end)./freqs(1));
            guess_vec = [power_spectrum(1), exp_guess];
            aperiodic_params = fminsearch(@error_expo_nk_function, guess_vec, options, freqs, power_spectrum);
        case 'knee'
            exp_guess = -(power_spectrum(end)-power_spectrum(1))./log10(freqs(end)./freqs(1));
            guess_vec = [power_spectrum(1),0, exp_guess];
            aperiodic_params = fminsearch(@error_expo_function, guess_vec, options, freqs, power_spectrum);
    end

end

function aperiodic_params = robust_ap_fit(freqs, power_spectrum, aperiodic_mode)
%       Fit the aperiodic component of the power spectrum robustly, ignoring outliers.
%
%       Parameters
%       ----------
%       freqs : 1xn array
%           Frequency values for the power spectrum, in linear scale.
%       power_spectrum : 1xn array
%           Power values, in log10 scale.
%       aperiodic_mode : {'fixed','knee'}
%           Defines absence or presence of knee in aperiodic component.
%
%       Returns
%       -------
%       aperiodic_params : 1xn array
%           Parameter estimates for aperiodic fit.

    % Do a quick, initial aperiodic fit
    popt = simple_ap_fit(freqs, power_spectrum, aperiodic_mode);
    initial_fit = gen_aperiodic(freqs, popt, aperiodic_mode);

    % Flatten power_spectrum based on initial aperiodic fit
    flatspec = power_spectrum - initial_fit;

    % Flatten outliers - any points that drop below 0
    flatspec(flatspec(:) < 0) = 0;

    % Use percential threshold, in terms of # of points, to extract and re-fit
    perc_thresh = bst_prctile(flatspec, 0.025);
    perc_mask = flatspec <= perc_thresh;
    freqs_ignore = freqs(perc_mask);
    spectrum_ignore = power_spectrum(perc_mask);

    % Second aperiodic fit - using results of first fit as guess parameters

    options = optimset('Display', 'off', 'TolX', 1e-4, 'TolFun', 1e-6, ...
        'MaxFunEvals', 5000, 'MaxIter', 5000);
    guess_vec = popt;

    switch (aperiodic_mode)
        case 'fixed'  % no knee
            aperiodic_params = fminsearch(@error_expo_nk_function, guess_vec, options, freqs_ignore, spectrum_ignore);
        case 'knee'
            aperiodic_params = fminsearch(@error_expo_function, guess_vec, options, freqs_ignore, spectrum_ignore);
    end
end

function spectrum_flat = flatten_spectrum(freqs, power_spectrum, robust_aperiodic_params, aperiodic_mode)
%       Flatten the power spectrum by removing the aperiodic component.
%
%       Parameters
%       ----------
%       freqs : 1xn array
%           Frequency values for the power spectrum, in linear scale.
%       power_spectrum : 1xn array
%           Power values, in log10 scale.
%       robust_aperiodic_params : 1x2 or 1x3 array (see aperiodic_mode)
%           Parameter estimates for aperiodic fit.
%       aperiodic_mode : 1 or 2
%           Defines absence or presence of knee in aperiodic component.
%
%       Returns
%       -------
%       spectrum_flat : 1xn array
%           Flattened (aperiodic removed) power spectrum.

spectrum_flat = power_spectrum - gen_aperiodic(freqs,robust_aperiodic_params,aperiodic_mode);

end

function [model_params,peak_function] = fit_peaks(freqs, flat_iter, max_n_peaks, peak_threshold, min_peak_height, gauss_std_limits, proxThresh, bordThresh, peakType, guess_weight,hOT)
%       Iteratively fit peaks to flattened spectrum.
%
%       Parameters
%       ----------
%       freqs : 1xn array
%           Frequency values for the power spectrum, in linear scale.
%       flat_iter : 1xn array
%           Flattened (aperiodic removed) power spectrum.
%       max_n_peaks : double
%           Maximum number of gaussians to fit within the spectrum.
%       peak_threshold : double
%           Threshold (in standard deviations of noise floor) to detect a peak.
%       min_peak_height : double
%           Minimum height of a peak (in log10).
%       gauss_std_limits : 1x2 double
%           Limits to gaussian (cauchy) standard deviation (gamma) when detecting a peak.
%       proxThresh : double
%           Minimum distance between two peaks, in st. dev. (gamma) of peaks.
%       peakType : {'gaussian', 'cauchy', 'both'}
%           Which types of peaks are being fitted
%       guess_weight : {'none', 'weak', 'strong'}
%           Parameter to weigh initial estimates during optimization (None, Weak, or Strong)
%       hOT : 0 or 1
%           Defines whether to use constrained optimization, fmincon, or
%           basic simplex, fminsearch.
%
%       Returns
%       -------
%       gaussian_params : mx3 array, where m = No. of peaks.
%           Parameters that define the peak fit(s). Each row is a peak, as [mean, height, st. dev. (gamma)].
    switch peakType
        case 'gaussian' % gaussian only
            peak_function = @gaussian; % Identify peaks as gaussian
            % Initialize matrix of guess parameters for gaussian fitting.
            guess_params = zeros(max_n_peaks, 3);
            % Save intact flat_spectrum
            flat_spec = flat_iter;
            % Find peak: Loop through, finding a candidate peak, and fitting with a guess gaussian.
            % Stopping procedure based on either the limit on # of peaks,
            % or the relative or absolute height thresholds.
            for guess = 1:max_n_peaks
                % Find candidate peak - the maximum point of the flattened spectrum.
                max_ind = find(flat_iter == max(flat_iter));
                max_height = flat_iter(max_ind);

                % Stop searching for peaks once max_height drops below height threshold.
                if max_height <= peak_threshold * std(flat_iter)
                    break
                end

                % Set the guess parameters for gaussian fitting - mean and height.
                guess_freq = freqs(max_ind);
                guess_height = max_height;

                % Halt fitting process if candidate peak drops below minimum height.
                if guess_height <= min_peak_height
                    break
                end

                % Data-driven first guess at standard deviation
                % Find half height index on each side of the center frequency.
                half_height = 0.5 * max_height;

                le_ind = sum(flat_iter(1:max_ind) <= half_height);
                ri_ind = length(flat_iter) - sum(flat_iter(max_ind:end) <= half_height)+1;

                % Keep bandwidth estimation from the shortest side.
                % We grab shortest to avoid estimating very large std from overalapping peaks.
                % Grab the shortest side, ignoring a side if the half max was not found.
                % Note: will fail if both le & ri ind's end up as None (probably shouldn't happen).
                short_side = min(abs([le_ind,ri_ind]-max_ind));

                % Estimate std from FWHM. Calculate FWHM, converting to Hz, get guess std from FWHM
                fwhm = short_side * 2 * (freqs(2)-freqs(1));
                guess_std = fwhm / (2 * sqrt(2 * log(2)));

                % Check that guess std isn't outside preset std limits; restrict if so.
                % Note: without this, curve_fitting fails if given guess > or < bounds.
                if guess_std < gauss_std_limits(1)
                    guess_std = gauss_std_limits(1);
                end
                if guess_std > gauss_std_limits(2)
                    guess_std = gauss_std_limits(2);
                end

                % Collect guess parameters.
                guess_params(guess,:) = [guess_freq, guess_height, guess_std];

                % Subtract best-guess gaussian.
                peak_gauss = gaussian(freqs, guess_freq, guess_height, guess_std);
                flat_iter = flat_iter - peak_gauss;

            end
            % Remove unused guesses
            guess_params(guess_params(:,1) == 0,:) = [];

            % Check peaks based on edges, and on overlap
            % Drop any that violate requirements.
            guess_params = drop_peak_cf(guess_params, bordThresh, [min(freqs) max(freqs)]);
            guess_params = drop_peak_overlap(guess_params, proxThresh);

            % If there are peak guesses, fit the peaks, and sort results.
            if ~isempty(guess_params)
                model_params = fit_peak_guess(guess_params, freqs, flat_spec, 1, guess_weight, gauss_std_limits,hOT);
            else
                model_params = zeros(1, 3);
            end

        case 'cauchy' % cauchy only
            peak_function = @cauchy; % Identify peaks as cauchy
            guess_params = zeros(max_n_peaks, 3);
            flat_spec = flat_iter;
            for guess = 1:max_n_peaks
                max_ind = find(flat_iter == max(flat_iter));
                max_height = flat_iter(max_ind);
                if max_height <= peak_threshold * std(flat_iter)
                    break
                end
                guess_freq = freqs(max_ind);
                guess_height = max_height;
                if guess_height <= min_peak_height
                    break
                end
                half_height = 0.5 * max_height;
                le_ind = sum(flat_iter(1:max_ind) <= half_height);
                ri_ind = length(flat_iter) - sum(flat_iter(max_ind:end) <= half_height);
                short_side = min(abs([le_ind,ri_ind]-max_ind));

                % Estimate gamma from FWHM. Calculate FWHM, converting to Hz, get guess gamma from FWHM
                fwhm = short_side * 2 * (freqs(2)-freqs(1));
                guess_gamma = fwhm/2;
                % Check that guess gamma isn't outside preset limits; restrict if so.
                % Note: without this, curve_fitting fails if given guess > or < bounds.
                if guess_gamma < gauss_std_limits(1)
                    guess_gamma = gauss_std_limits(1);
                end
                if guess_gamma > gauss_std_limits(2)
                    guess_gamma = gauss_std_limits(2);
                end

                % Collect guess parameters.
                guess_params(guess,:) = [guess_freq(1), guess_height, guess_gamma];

                % Subtract best-guess cauchy.
                peak_cauchy = cauchy(freqs, guess_freq(1), guess_height, guess_gamma);
                flat_iter = flat_iter - peak_cauchy;

            end
            guess_params(guess_params(:,1) == 0,:) = [];
            guess_params = drop_peak_cf(guess_params, bordThresh, [min(freqs) max(freqs)]);
            guess_params = drop_peak_overlap(guess_params, proxThresh);

            % If there are peak guesses, fit the peaks, and sort results.
            if ~isempty(guess_params)
                model_params = fit_peak_guess(guess_params, freqs, flat_spec, 2, guess_weight, gauss_std_limits,hOT);
            else
                model_params = zeros(1, 3);
            end
        case 'best' % best of both: model both fits and compare error, save best
            % Gaussian Fit
            guess_params = zeros(max_n_peaks, 3);
            flat_spec = flat_iter;
            for guess = 1:max_n_peaks
                max_ind = find(flat_iter == max(flat_iter));
                max_height = flat_iter(max_ind);
                if max_height <= peak_threshold * std(flat_iter)
                    break
                end
                guess_freq = freqs(max_ind);
                guess_height = max_height;
                if guess_height <= min_peak_height
                    break
                end
                half_height = 0.5 * max_height;
                le_ind = sum(flat_iter(1:max_ind) <= half_height);
                ri_ind = length(flat_iter) - sum(flat_iter(max_ind:end) <= half_height)+1;
                short_side = min(abs([le_ind,ri_ind]-max_ind));
                fwhm = short_side * 2 * (freqs(2)-freqs(1));
                guess_std = fwhm / (2 * sqrt(2 * log(2)));
                if guess_std < gauss_std_limits(1)
                    guess_std = gauss_std_limits(1);
                end
                if guess_std > gauss_std_limits(2)
                    guess_std = gauss_std_limits(2);
                end
                guess_params(guess,:) = [guess_freq, guess_height, guess_std];
                peak_gauss = gaussian(freqs, guess_freq, guess_height, guess_std);
                flat_iter = flat_iter - peak_gauss;
            end
            guess_params(guess_params(:,1) == 0,:) = [];
            guess_params = drop_peak_cf(guess_params, bordThresh, [min(freqs) max(freqs)]);
            guess_params = drop_peak_overlap(guess_params, proxThresh);
            if ~isempty(guess_params)
                gauss_params = fit_peak_guess(guess_params, freqs, flat_spec, 1, guess_weight, gauss_std_limits,hOT);
                flat_gauss = zeros(size(freqs));
                for peak = 1:size(gauss_params,1)
                    flat_gauss =  flat_gauss + gaussian(freqs,gauss_params(peak,1),...
                        gauss_params(peak,2),gauss_params(peak,3));
                end
                error_gauss = sum((flat_gauss-flat_spec).^2);
            else
                gauss_params = zeros(1, 3); error_gauss = 1E10;
            end

            % Cauchy Fit
            guess_params = zeros(max_n_peaks, 3);
            flat_iter = flat_spec;
            for guess = 1:max_n_peaks
                max_ind = find(flat_iter == max(flat_iter));
                max_height = flat_iter(max_ind);
                if max_height <= peak_threshold * std(flat_iter)
                    break
                end
                guess_freq = freqs(max_ind);
                guess_height = max_height;
                if guess_height <= min_peak_height
                    break
                end
                half_height = 0.5 * max_height;
                le_ind = sum(flat_iter(1:max_ind) <= half_height);
                ri_ind = length(flat_iter) - sum(flat_iter(max_ind:end) <= half_height)+1;
                short_side = min(abs([le_ind,ri_ind]-max_ind));
                fwhm = short_side * 2 * (freqs(2)-freqs(1));
                guess_gamma = fwhm/2;
                if guess_gamma < gauss_std_limits(1)
                    guess_gamma = gauss_std_limits(1);
                end
                if guess_gamma > gauss_std_limits(2)
                    guess_gamma = gauss_std_limits(2);
                end
                guess_params(guess,:) = [guess_freq(1), guess_height, guess_gamma];
                peak_cauchy = cauchy(freqs, guess_freq(1), guess_height, guess_gamma);
                flat_iter = flat_iter - peak_cauchy;
            end
            guess_params(guess_params(:,1) == 0,:) = [];
            guess_params = drop_peak_cf(guess_params, bordThresh, [min(freqs) max(freqs)]);
            guess_params = drop_peak_overlap(guess_params, proxThresh);
            if ~isempty(guess_params)
                cauchy_params = fit_peak_guess(guess_params, freqs, flat_spec, 2, guess_weight, gauss_std_limits,hOT);
                flat_cauchy = zeros(size(freqs));
                for peak = 1:size(cauchy_params,1)
                    flat_cauchy =  flat_cauchy + cauchy(freqs,cauchy_params(peak,1),...
                        cauchy_params(peak,2),cauchy_params(peak,3));
                end
                error_cauchy = sum((flat_cauchy-flat_spec).^2);
            else
                cauchy_params = zeros(1, 3); error_cauchy = 1E10;
            end
            % Save least-error model
                if min([error_gauss,error_cauchy]) == error_gauss
                    model_params = gauss_params;
                    peak_function = @gaussian;
                else
                    model_params = cauchy_params;
                    peak_function = @cauchy;
                end
    end

end

function guess = drop_peak_cf(guess, bw_std_edge, freq_range)
%       Check whether to drop peaks based on center's proximity to the edge of the spectrum.
%
%       Parameters
%       ----------
%       guess : mx3 array, where m = No. of peaks.
%           Guess parameters for peak fits.
%
%       Returns
%       -------
%       guess : qx3 where q <= m No. of peaks.
%           Guess parameters for peak fits.

    cf_params = guess(:,1)';
    bw_params = guess(:,3)' * bw_std_edge;

    % Check if peaks within drop threshold from the edge of the frequency range.

    keep_peak = abs(cf_params-freq_range(1)) > bw_params & ...
        abs(cf_params-freq_range(2)) > bw_params;

    % Drop peaks that fail the center frequency edge criterion
    guess = guess(keep_peak,:);

end

function guess = drop_peak_overlap(guess, proxThresh)
%       Checks whether to drop gaussians based on amount of overlap.
%
%       Parameters
%       ----------
%       guess : mx3 array, where m = No. of peaks.
%           Guess parameters for peak fits.
%       proxThresh: double
%           Proximity threshold (in st. dev. or gamma) between two peaks.
%
%       Returns
%       -------
%       guess : qx3 where q <= m No. of peaks.
%           Guess parameters for peak fits.
%
%       Note
%       -----
%       For any gaussians with an overlap that crosses the threshold,
%       the lowest height guess guassian is dropped.

    % Sort the peak guesses, so can check overlap of adjacent peaks
    guess = sortrows(guess);

    % Calculate standard deviation bounds for checking amount of overlap

    bounds = [guess(:,1) - guess(:,3) * proxThresh, ...
        guess(:,1), guess(:,1) + guess(:,3) * proxThresh];

    % Loop through peak bounds, comparing current bound to that of next peak
    drop_inds =  [];

    for ind = 1:size(bounds,1)-1

        b_0 = bounds(ind,:);
        b_1 = bounds(ind + 1,:);

        % Check if bound of current peak extends into next peak
        if b_0(2) > b_1(1)
            % If so, get the index of the gaussian with the lowest height (to drop)
            drop_inds = [drop_inds (ind - 1 + find(guess(ind:ind+1,2) == ...
                min(guess(ind,2),guess(ind+1,2))))];
        end
    end
    % Drop any peaks guesses that overlap too much, based on threshold.
    guess(drop_inds,:) = [];
end

function peak_params = fit_peak_guess(guess, freqs, flat_spec, peak_type, guess_weight, std_limits, hOT)
%     Fits a group of peak guesses with a fit function.
%
%     Parameters
%     ----------
%       guess : mx3 array, where m = No. of peaks.
%           Guess parameters for peak fits.
%       freqs : 1xn array
%           Frequency values for the power spectrum, in linear scale.
%       flat_iter : 1xn array
%           Flattened (aperiodic removed) power spectrum.
%       peakType : {'gaussian', 'cauchy', 'best'}
%           Which types of peaks are being fitted.
%       guess_weight : 'none', 'weak', 'strong'
%           Parameter to weigh initial estimates during optimization.
%       std_limits: 1x2 array
%           Minimum and maximum standard deviations for distribution.
%       hOT : 0 or 1
%           Defines whether to use constrained optimization, fmincon, or
%           basic simplex, fminsearch.
%
%       Returns
%       -------
%       peak_params : mx3, where m =  No. of peaks.
%           Peak parameters post-optimization.

    if hOT % Use OptimToolbox for fmincon
        options = optimset('Display', 'off', 'TolX', 1e-3, 'TolFun', 1e-5, ...
        'MaxFunEvals', 3000, 'MaxIter', 3000); % Tuned options
        lb = [guess(:,1)-guess(:,3)*2,zeros(size(guess(:,2))),ones(size(guess(:,3)))*std_limits(1)];
        ub = [guess(:,1)+guess(:,3)*2,inf(size(guess(:,2))),ones(size(guess(:,3)))*std_limits(2)];
        peak_params = fmincon(@error_model_constr,guess,[],[],[],[], ...
            lb,ub,[],options,freqs,flat_spec, peak_type);
    else % Use basic simplex approach, fminsearch, with guess_weight
        options = optimset('Display', 'off', 'TolX', 1e-4, 'TolFun', 1e-5, ...
        'MaxFunEvals', 5000, 'MaxIter', 5000);
        peak_params = fminsearch(@error_model,...
            guess, options, freqs, flat_spec, peak_type, guess, guess_weight);
    end
end

%% ===== ERROR FUNCTIONS =====
function err = error_expo_nk_function(params,xs,ys)
    ym = -log10(xs.^params(2)) + params(1);
    err = sum((ys - ym).^2);
end

function err = error_expo_function(params,xs,ys)
    ym = expo_function(xs,params);
    err = sum((ys - ym).^2);
end

function err = error_model(params, xVals, yVals, peak_type, guess, guess_weight)
    fitted_vals = 0;
    weak = 1E2;
    strong = 1E7;
    for set = 1:size(params,1)
        switch (peak_type)
            case 1 % Gaussian
                fitted_vals = fitted_vals + gaussian(xVals, params(set,1), params(set,2), params(set,3));
            case 2 % Cauchy
                fitted_vals = fitted_vals + cauchy(xVals, params(set,1), params(set,2), params(set,3));
        end
    end
    switch guess_weight
        case 'none'
            err = sum((yVals - fitted_vals).^2);
        case 'weak' % Add small weight to deviations from guess m and amp
            err = sum((yVals - fitted_vals).^2) + ...
                 weak*sum((params(:,1)-guess(:,1)).^2) + ...
                 weak*sum((params(:,2)-guess(:,2)).^2);
        case 'strong' % Add large weight to deviations from guess m and amp
            err = sum((yVals - fitted_vals).^2) + ...
                 strong*sum((params(:,1)-guess(:,1)).^2) + ...
                 strong*sum((params(:,2)-guess(:,2)).^2);
    end
end

function err = error_model_constr(params, xVals, yVals, peak_type)
    fitted_vals = 0;
    for set = 1:size(params,1)
        switch (peak_type)
            case 1 % Gaussian
                fitted_vals = fitted_vals + gaussian(xVals, params(set,1), params(set,2), params(set,3));
            case 2 % Cauchy
                fitted_vals = fitted_vals + cauchy(xVals, params(set,1), params(set,2), params(set,3));
        end
    end
    err = sum((yVals - fitted_vals).^2);
end

%% ===================================================================================
%  ===== FOOOF STATS =================================================================
%  ===================================================================================
function [ePeaks, eAper, eStats] = FOOOF_analysis(FOOOF_data, ChanNames, TF, max_peaks, sort_type, sort_param, sort_bands)
    % ===== EXTRACT PEAKS =====
    % Organize/extract peak components from FOOOF models
    nChan = numel(ChanNames);
    maxEnt = nChan * max_peaks;
    switch sort_type
        case 'param'
            % Initialize output struct
            ePeaks = struct('channel', [], 'center_frequency', [],...
                'amplitude', [], 'std_dev', []);
            % Collect data from all peaks
            i = 0;
            for chan = 1:nChan
                if ~isempty(FOOOF_data(chan).peak_params)
                    for p = 1:size(FOOOF_data(chan).peak_params,1)
                        i = i +1;
                        ePeaks(i).channel = ChanNames(chan);
                        ePeaks(i).center_frequency = FOOOF_data(chan).peak_params(p,1);
                        ePeaks(i).amplitude = FOOOF_data(chan).peak_params(p,2);
                        ePeaks(i).std_dev = FOOOF_data(chan).peak_params(p,3);
                    end
                end
            end
            % Apply specified sort
            switch sort_param
                case 'frequency'
                    [tmp,iSort] = sort([ePeaks.center_frequency]);
                    ePeaks = ePeaks(iSort);
                case 'amplitude'
                    [tmp,iSort] = sort([ePeaks.amplitude]);
                    ePeaks = ePeaks(iSort(end:-1:1));
                case 'std'
                    [tmp,iSort] = sort([ePeaks.std_dev]);
                    ePeaks = ePeaks(iSort);
            end
        case 'band'
            % Initialize output struct
            ePeaks = struct('channel', [], 'center_frequency', [],...
                'amplitude', [], 'std_dev', [], 'band', []);
            % Generate bands from input
            bands = process_tf_bands('Eval', sort_bands);
            % Collect data from all peaks
            i = 0;
            for chan = 1:nChan
                if ~isempty(FOOOF_data(chan).peak_params)
                    for p = 1:size(FOOOF_data(chan).peak_params,1)
                        i = i +1;
                        ePeaks(i).channel = ChanNames(chan);
                        ePeaks(i).center_frequency = FOOOF_data(chan).peak_params(p,1);
                        ePeaks(i).amplitude = FOOOF_data(chan).peak_params(p,2);
                        ePeaks(i).std_dev = FOOOF_data(chan).peak_params(p,3);
                        % Find name of frequency band from user definitions
                        bandRanges = cell2mat(bands(:,2));
                        iBand = find(ePeaks(i).center_frequency >= bandRanges(:,1) & ePeaks(i).center_frequency <= bandRanges(:,2));
                        if ~isempty(iBand)
                            ePeaks(i).band = bands{iBand,1};
                        else
                            ePeaks(i).band = 'None';
                        end
                    end
                end
            end
    end

    % ===== EXTRACT APERIODIC =====
    % Organize/extract aperiodic components from FOOOF models
    hasKnee = length(FOOOF_data(1).aperiodic_params) - 2;
    % Initialize output struct
    eAper = struct('channel', [], 'offset', [], 'exponent', []);
    for chan = 1:nChan
            eAper(chan).channel = ChanNames(chan);
            eAper(chan).offset = FOOOF_data(chan).aperiodic_params(1);
        if hasKnee % Legacy FOOOF alters order of parameters
            eAper(chan).exponent = FOOOF_data(chan).aperiodic_params(3);
            eAper(chan).knee_frequency = FOOOF_data(chan).aperiodic_params(2);
        else
            eAper(chan).exponent = FOOOF_data(chan).aperiodic_params(2);
        end
    end

    % ===== EXTRACT STAT =====
    % Organize/extract stats from FOOOF models
    % Initialize output struct
    eStats = struct('channel', ChanNames);
    for chan = 1:nChan
        eStats(chan).MSE = FOOOF_data(chan).error;
        eStats(chan).r_squared = FOOOF_data(chan).r_squared;
        spec = squeeze(log10(TF(chan,:,:)));
        fspec = squeeze(log10(FOOOF_data(chan).fooofed_spectrum))';
        eStats(chan).frequency_wise_error = abs(spec-fspec);
    end
end


clear

load('./results/results_clnf_wild.mat');

[clnf_error, ~,~,frontal_ids] = compute_error_small( experiment.labels,  experiment.shapes-1.0);
clnf_error_frontal = clnf_error(frontal_ids);
clnf_error_profile = clnf_error(~frontal_ids);

load('./results/results_ceclm_general.mat');

[ceclm_error,~,~,frontal_ids] = compute_error_small( experiment.labels,  experiment.shapes-1.0);
ceclm_error_frontal = ceclm_error(frontal_ids);
ceclm_error_profile = ceclm_error(~frontal_ids);
labels = experiment.labels;

load('results/CFAN_JANUS.mat');

[cfan_error,~,~,frontal_ids] = compute_error_small( labels_all,  shapes_all-1.0);
cfan_error_frontal = cfan_error(frontal_ids);
cfan_error_profile = cfan_error(~frontal_ids);

load('results/JANUS_3DDFA.mat');

[error_3ddfa,~,~,frontal_ids] = compute_error_small( labels_all,  shapes-1.0);
error_3ddfa_frontal = error_3ddfa(frontal_ids);
error_3ddfa_profile = error_3ddfa(~frontal_ids);

load('results/JANUS_pocr.mat');

[pocr_error,~,~,frontal_ids] = compute_error_small( labels,  experiments.shapes-0.5);
pocr_error_frontal = pocr_error(frontal_ids);
pocr_error_profile = pocr_error(~frontal_ids);

load('results/JANUS_chehra.mat');

[drmf_error,~,~,frontal_ids] = compute_error_small( labels,  shapes);
drmf_error_frontal = drmf_error(frontal_ids);
drmf_error_profile = drmf_error(~frontal_ids);

load('results/JANUS_sdm.mat');

[sdm_error,~,~,frontal_ids] = compute_error_small( labels,  experiments.shapes);
sdm_error_frontal = sdm_error(frontal_ids);
sdm_error_profile = sdm_error(~frontal_ids);

load('results/JANUS-CFSS.mat');
shapes = zeros(68,2, size(estimatedPose,1));

for i = 1:size(shapes, 3)
    shapes(:,1,i) = estimatedPose(i,1:68);
    shapes(:,2,i) = estimatedPose(i,69:end);
end

[cfss_error,~,~,frontal_ids] = compute_error_small( experiments.labels, shapes-1.0);
cfss_error_frontal = cfss_error(frontal_ids);
cfss_error_profile = cfss_error(~frontal_ids);

load('results/tcdcn_JANUS.mat');
shapes_c = shapes;
shapes = zeros(68,2, numel(shapes_c));

for i = 1:size(shapes, 3)
    shapes(:,1,i) = shapes_c{i}(:,1);
    shapes(:,2,i) = shapes_c{i}(:,2);
end

[tcdcn_error,~,~,frontal_ids] = compute_error_small( experiments.labels, shapes-1.0);
tcdcn_error_frontal = tcdcn_error(frontal_ids);
tcdcn_error_profile = tcdcn_error(~frontal_ids);


%the object be controlled
function [sys,x0,str,ts]=ctrl5_2_3obj(t,x,u,flag)
switch flag,
    case 0,
        [sys,x0,str,ts]=mdlInit();
    case 1,
        sys=mdlDer(t,x,u);
    case 3,
        sys=mdlOutput(t,x,u);
    otherwise,
        sys=[];
end;
function [sys,x0,str,ts]= mdlInit()
size=simsizes;
size.NumContStates=2;
size.NumDiscStates=0;
size.NumOutputs=2;
size.NumInputs=1;
size.DirFeedthrough=0;
size.NumSampleTimes=1;
sys=simsizes(size);
x0=[0];
str=[];
ts=[0,0];
function sys=mdlDer(t,x,u)
global b0;
a=1;
sys(1)=x(2);
sys(2)=a*sign(sin(10*t))+u*b0;
function sys=mdlOutput(t,x,u)
sys(1)=x(1);
sys(2)=sign(sin(2*t));