function TripPend_GaussNewton_fit_v3
% This function uses a Gauss-Newton Method to fit the physical parameters
% of the triple models to measured longitudinal and pitch data. Each 
% Gauss-Newton iteration uses a linesearch algorithm to optimize the step
% size for the search direction for that iteration. This function must be
% put in the same directory where the model is compiled, i.e. where the
% ssmake... function lives. It uses a built in version of ssmake... and
% calls the same functions.
%
% What you must input as the user:
% 1) Model parameter file: e.g. 'hstsopt_metal', in the 'Fitting routine
% parameters and files' section
% 2) The top mass transfer fuction svn data path. This also is in the 
% 'Fitting routine parameters and files' section.
% 3) The measured longitudinal and pitch mode frequencies in the 'Measured 
% Data' section. These mode frequencies should be determined from the 
% transfer function data for consistency.
% 4) The indices of the desired parameters to float in the fit. These are
% listed in the 'param_index' vector in the 'Parameter Initializations'
% section.
%
% The data used to make the fit is just the measured frequencies of the 6
% longitudinal and pitch modes. Future versions of the code can include in
% principle any kind of data.
%
% Along with the measured data, the code also asks for the estimated
% standard deviation of the measurements, e.g. the error bars for the
% resonance frequencies, etc. This data is important because it is used to
% calculate the minimum error bars on the adjusted model parameters. The
% parameter error bars are calculated using the Fisher Information Matrix,
% which by definition predicts only lower limits on these error bars, so
% the actual error bars could be larger. The estimated standard deviation
% here is just the frequency resolution of the measured data, assuming
% linear frequency spacing.
%
% The output model is a state space variable called pen_mod. The adjusted
% parameters are collected in a vector called param. The list of which
% element of the vector param is which model param is in the 
% 'Parameter Initializations' section of the code below. All the relevant
% output data is saved to a filename of your choice in a folder called 'Fit
% Files'.
%
% A summary of the adjusted model parameters is outputted to the command
% line. A series of before and after plots of model transfer functions
% against the measured transfer functions are plotted. A final plot with
% the evolution of the model error is plotted as well.
%
% The code interfaces with the model through the function
% 'ssmake3MBf_modelfitting.m'. The starting values
% for all the parameters come from the '...opt...' file. The success of
% this fitting code depends on the parameters in this file being as close
% as possible to the as-built values. You also have to provide the string
% of the name of the parameter file in the 'Fitting routine parameters and
% files' section. E.g. 'hstsopt_metal'.
% 
% Algorithm stopping parameters
% Search.iter: sets the maximum number of Gauss-Newton iterations
% Search.gtol: sets the minimum error gradient magnitude
% Search.iter_LS_max: maximum number of line search iterations
% Search.c1 and Search.c2: Wolfe condition parameters for linesearch
% Search.alpha_max0: maximum step size for linesearch algorithm
%
% Choosing the parameters to fit is done with the vector 'param_index'.
% A list of the number for each parameter is given in the code.
% Edit the values of param_index in the 'Parameter Initialization' section
% to choose the desired parameters. In this version, the parameters
% available to adjust are the d's, rotational inertias, wire lengths, 
% wire longitudinal spacings, wire radii, masses, and vertical spring stiffnesses:
% pend.d0;       %1      param(1)
% pend.d1;       %2      param(2)
% pend.d2;       %3      param(3)
% pend.d3;       %4      param(4)
% pend.d4;       %5      param(5)        
% pend.I1x;      %6      param(6)
% pend.I2x;      %7      param(7)
% pend.I3x;      %8      param(8)
% pend.I1y;      %9      param(9)
% pend.I2y;      %10     param(10)
% pend.I3y;      %11     param(11)        
% pend.I1z;      %12     param(12)
% pend.I2z;      %13     param(13)
% pend.I3z;      %14     param(14)               
% pend.l1;       %15     param(15)
% pend.l2;       %16     param(16)
% pend.l3;       %17     param(17)   
% pend.si;       %18     param(18)    
% pend.sl;       %19     param(19)
% pend.r1;       %20     param(20)
% pend.r2;       %21     param(21)
% pend.r3;       %22     param(22)
% pend.m1;       %23     param(23)
% pend.m2;       %24     param(24)
% pend.m3;       %25     param(25)
% pend.kc1;      %26     param(26)
% pend.kc2;      %27     param(27)
%
% Brett Shapiro
% Version 1
% Original version.
% 27 September 2012
%
% Brett Shapiro
% Version 2
% Fixed typos in the comments above. Fixed errors in the parameter change
% report printed to the command window. The +- percent errors on the fit
% parameters were calculated incorrectly.
%
% Brett Shapiro
% Version 3
% General cleanup of code. Added more mode frequencies. Now includes all of
% them except the vertical and roll bounce modes.
% 26 September 2013

clc
close all

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Fitting routine parameters and files
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Path where transfer function data is stored
svnDir.sus = '/ligo/svncommon/SusSVN/sus/trunk/';
toolsDir = [svnDir.sus 'Common/MatlabTools/'];
modelDir = [svnDir.sus 'Common/MatlabTools/TripleModel_Production/'];
addpath(toolsDir,modelDir)

% M1toM1data_filename = 'HSTS/L1/MC2/SAGM1/Results/2013-06-17_1055564583_L1SUSMC2_M1_damp_OFF_MATTF';
% M1toM1data_filename = 'HSTS/L1/PRM/SAGM1/Results/2013-06-04_1054434416_L1SUSPRM_M1_damp_OFF_MATTF';
M1toM1data_filename = 'HSTS/H1/MC2/SAGM1/Results/2013-05-25_1053525085_H1SUSMC2_M1_damp_OFF_MATTF';
save_filename = 'L1MC2_L_GuassNewtonFit_25Sept2013'; % save the results of this run to this filename

% model parameter file name
modparamfilename = 'hstsopt_metal_withspringfits';
% model compilation code filename
modcompcodename = 'ssmake3MBf_modelfitting';
% Model compile string
modcompstring = ['mbtrip = ',modcompcodename,'(fitparam,''',modparamfilename,''');'];
   
% stopping conditions: The fit will run until either Search.iter or Search.gtol is satisfied first.
Search.iter = 50; % Maximum number of iterations in this run.
Search.g_tol = 0.001; % min tolerance on error gradient to keep running the routine. The routine stops when the magnitude of the gradient drops below this value

% line search parameters for Gauss-Newton algorithm
Search.c1 = 0;%0.0001;
Search.c2 = 1;%0.9; % Search.c1 and Searcg.c2 are the Wolfe condition constants
Search.iter_LS_max = 100; % Limit on number of iterations of the line search
Search.alpha_max0 = 50; % initial step length

% Model indices
[~,in,out,~] = generate_Triple_Model_Production(0.1:0.001:10,'hstsopt_metal'); % the inputs here are just dummies, only want the model input and output indices

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Measured Data
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    % Measured mode frequencies 
        % measured long-pitch
    meas.modes.free.LP.modefreqs =  2*pi*sort([0.67;...
                                               1.52;...
                                               2.76;...
                                               1.02;...
                                               3.10;...
                                               3.63]); 
        % measured vert modes
    meas.modes.free.V.modefreqs  =  2*pi*sort([0.89;...
                                               2.70]);                                      
        % measured top to top tf vert zero                                   
%     meas.modes.toplocked.V.modefreqs = 2*pi*1.40; 
        % measured yaw modes
    meas.modes.free.Y.modefreqs  =  2*pi*sort([1.1;...
                                               2.08;
                                               3.46]);  
         % measured trans-roll
    meas.modes.free.TR.modefreqs =  2*pi*sort([0.67;...
                                               1.52;...
                                               2.93;...
                                               1.51;...
                                               2.27]); 
                                           
        % measured long mode when cavity is locked with bot and mid using high gain at mid
   meas.modes.cavmidbot.LP.modefreqs = 2*pi*2.21094;
                                           
%     meas.modes.all.modefreqs     =  [meas.modes.free.LP.modefreqs;meas.modes.free.V.modefreqs;meas.modes.toplocked.V.modefreqs;meas.modes.free.Y.modefreqs;meas.modes.free.TR.modefreqs];           % all measured modes
    meas.modes.all.modefreqs     =  [meas.modes.free.LP.modefreqs;meas.modes.free.V.modefreqs;meas.modes.free.Y.modefreqs;meas.modes.free.TR.modefreqs;meas.modes.cavmidbot.LP.modefreqs];           % all measured modes
%     meas.modes.all.modefreqs     =  [meas.modes.cavmidbot.LP.modefreqs];           % all measured modes
    

    % The measured gain of the triple top mass electronics
    egain = 1;%34.1086; 
    assignin('base','egain',egain) 
    
    % measured Transfer Function data
    M1toM1TFdata = load([svnDir.sus,M1toM1data_filename]);
    
    % M1 longitudinal to M1 longitudinal high frequency magnitude to help M1 mass estimation
    meas.mag.M1toM1.L2L = [];
%     low_freq = 7;
%     high_freq = 9;
%     low_freq_ind = indfind(M1toM1TFdata.meas.freq,low_freq);
%     high_freq_ind = indfind(M1toM1TFdata.meas.freq,high_freq);
%     meas.mag.M1toM1.L2L = sum(abs(M1toM1TFdata.meas.eul2eul(M1toM1TFdata.meas.out.osem.L-6,M1toM1TFdata.meas.in.L).f(low_freq_ind:high_freq_ind)))/length(M1toM1TFdata.meas.freq(low_freq_ind:high_freq_ind));
    
    % frequency resolution to predict errors
    meas.modes.free.freq_resolution = 2*pi*(M1toM1TFdata.meas.freq(3)-M1toM1TFdata.meas.freq(2)); % Assuming linear frequency spacing. Used to estimate the mode frequency measurement error, which then predicts the fit error.
    meas.modes.toplocked.freq_resolution = 2*pi*(M1toM1TFdata.meas.freq(3)-M1toM1TFdata.meas.freq(2)); % Assuming linear frequency spacing. Used to estimate the mode frequency measurement error, which then predicts the fit error.
    
    % magnitude percent error
    meas.mag.err = [];
%     meas.mag.err = 0.25;
                   
    % variance of measurement errors (order must match order in err_vec_calc)
    meas.modes.all.err = meas.modes.free.freq_resolution./meas.modes.all.modefreqs;
    meas.mag.all.err = meas.mag.err;
    meas.err = [meas.modes.all.err;meas.mag.all.err];
    Cmeas_var = meas.err*meas.err';


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Parameter Initializations %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%% Edit this vector using the list above to choose the desired parameter space for this fit
param_index = [2 6 7 9:11 23:25]; % selecting the parameters specific to the top mass
% param_index = 23; % selecting the parameters specific to the top mass

        % Loading Original Model Parameters 
        eval(['pend = ',modparamfilename,';'])
       
       % Defining all parameters of interest. The numbers are entered into
       % parameter_index to choose the parameter space for the fit.
        f_d0 = pend.d0;       %1      param(1)
        f_d1 = pend.d1;       %2      param(2)
        f_d2 = pend.d2;       %3      param(3)
        f_d3 = pend.d3;       %4      param(4)
        f_d4 = pend.d4;       %5      param(5)        
        f_I1x = pend.I1x;     %6      param(6)
        f_I2x = pend.I2x;     %7      param(7)
        f_I3x = pend.I3x;     %8      param(8)
        f_I1y = pend.I1y;     %9      param(9)
        f_I2y = pend.I2y;     %10     param(10)
        f_I3y = pend.I3y;     %11     param(11)        
        f_I1z = pend.I1z;     %12     param(12)
        f_I2z = pend.I2z;     %13     param(13)
        f_I3z = pend.I3z;     %14     param(14)               
        f_l1 = pend.l1;       %15     param(15)
        f_l2 = pend.l2;       %16     param(16)
        f_l3 = pend.l3;       %17     param(17)   
        f_si = pend.si;       %18     param(18)    
        f_sl = pend.sl;       %19     param(19)
        f_r1 = pend.r1;       %20     param(20)
        f_r2 = pend.r2;       %21     param(21)
        f_r3 = pend.r3;       %22     param(22)
        f_m1 = pend.m1;       %23     param(23)
        f_m2 = pend.m2;       %24     param(24)
        f_m3 = pend.m3;       %25     param(25)
        f_kc1 = pend.kc1;     %26     param(26)
        f_kc2 = pend.kc2;     %27     param(27)
    
              
        param = [f_d0;f_d1;f_d2;f_d3;f_d4;f_I1x;f_I2x;f_I3x;f_I1y;f_I2y;f_I3y;f_I1z;f_I2z;f_I3z;f_l1;f_l2;f_l3;f_si;f_sl;f_r1;f_r2;f_r3;f_m1;f_m2;f_m3;f_kc1;f_kc2]; % parameters vector used by fitting code        
        fitparam0 = param;  % fitparam is the parameter vector used by ssmake3MBf_modelfitting to recompile the model. fitparam0 is the before-the-fit version of this vector.
        mbtrip = genmod(fitparam0,modcompstring);
        err_vec1 = err_vec_calc(mbtrip,meas); %the error of the original model
        err1 = err_vec1'*err_vec1;

param_index_length = length(param_index);
param_length = length(param);
d_param = (1e-9)*[ones(1,param_length-3) 0.1 0.1 0.1]; %step size used to calculate the error gradient vector. Must be small enough to represent the gradient at a given point, but large enough yield a junk gradient. The lower limit is d_param/param > eps =(approx) 1e-15.
grad_param = zeros(param_length,1); %error gradient vector
grad_param_k1 = grad_param;  % linesearch error gradient
param_check = param; % used during linesearch to check if the error decreased

% There should always be more data points than variable model parameters. 
% This is a necessary condition to finding a unique fit.
if param_index_length > length(err_vec1)
    warning(['There are more parameters than data points, the solution will not be unique! # of parameters = ',num2str(param_index_length),', # of data points = ',num2str(length(err_vec1)),'.']) %#ok<WNTAG>        
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Loop Initializations %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
err = zeros(1,Search.iter); % error vector
err(1) = err1; % initial error
disp(' '),disp(['1000*err(1) = ',num2str(1000*err1)]) % display initial error
ii = 1; % iterations of loop
pass = 0; % loop flag

% finding the intial error gradient
    for jj = 1:param_index_length
    
        param_test = param(param_index(jj)) + d_param(param_index(jj));
        % Testing single parameter change
        fitparam = param_conv(param,param_test,param_index(jj)); % assignin('base','fitparam',fitparam)
        pen_mod_test = genmod(fitparam,modcompstring);
        err_vec_test = err_vec_calc(pen_mod_test,meas);
        err_test = err_vec_test'*err_vec_test;
        grad_param(param_index(jj)) = (err_test - err(1))/d_param(param_index(jj));
        
    end
    gradmag = sqrt(grad_param'*grad_param);
    disp(['     |gradient| = ',num2str(gradmag)]),drawnow

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Gauss-Newton Algortithm Loop %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
while ii < Search.iter && gradmag > Search.g_tol && pass < 2;
    ii = ii+1;
    
     % calculating the Jacobian matrix for this point in the parameter space
        fitparam_vec = param_conv(param,0,0); % assignin('base','fitparam',fitparam)
        pen_mod_vec = genmod(fitparam_vec,modcompstring);
        err_vec = err_vec_calc(pen_mod_vec,meas);
        Jacobian = zeros(length(err_vec),param_index_length);
    for jj = 1:param_index_length    
        param_test = param(param_index(jj)) + d_param(param_index(jj));
        % Testing single parameter change
        fitparam_vec_test = param_conv(param,param_test,param_index(jj)); % assignin('base','fitparam',fitparam)
        pen_mod_vec_test = genmod(fitparam_vec_test,modcompstring);
        err_vec_test = err_vec_calc(pen_mod_vec_test,meas);
        Jacobian(:,jj) = (err_vec_test - err_vec)/d_param(param_index(jj));                
    end    
    sval = svd(Jacobian);
    if max(sval)/min(sval) > 1e5 % 1e5 is an arbitrary user defined value
        % This means that the parameter space has lost strict convexity due
        % to insuffient data points that are coupled to the chosen model
        % parameters. A nonunique solution is possible. Examing the singular 
        % value decomposition of the Jacobian matrix will quantify how
        % important the measurements are to the model parameters.
        warning(['Jacobian poorly scaled, max/min singular value ratio at ',num2str(max(sval)/min(sval)),'. Good ratios are close to 1.']) %#ok<WNTAG>        
    end

    % Gauss-Newton algorithm
        % Step 1: find search direction
            dk = -2*pinv(Jacobian)*err_vec;
        % Step 2: line search
            alpha_min = 0; % lower limit of alpha bracket
            alpha_max = Search.alpha_max0; % upper limit of alpha bracket
            alpha = (alpha_min+alpha_max)/2;
            iter_LS = 0; % Count of the number of iterations of the line search
            pass = 0; % Termination flag, pass == 1 means Wolfe conditions satisfied
            while pass == 0
                % Limit the number of iterations in this loop.
                iter_LS = iter_LS + 1;
                if iter_LS > Search.iter_LS_max
                    disp(['alpha_min = ',num2str(alpha_min)])
                    disp(['alpha_max = ',num2str(alpha_max)])
                    disp(['alpha = ',num2str(alpha)])
                    disp(['err_k1 = ',num2str(err_k1)])
                    disp(['sqrt(grad_param''*grad_param) = ',num2str(sqrt(grad_param'*grad_param))])
%                     error(['Maximum iterations of ',num2str(Search.iter_LS_max),' reached.'])
                    pass = 2;
                    warning(['Maximum line search iterations of ',num2str(Search.iter_LS_max),' reached.']) %#ok<WNTAG>
                    break
                end
    
                % Bisection iteration
                alpha = (alpha_min+alpha_max)/2; % testing the middle point in the bracket
               
                param_check(param_index) = param(param_index) + alpha*dk;             % potential new position                
                                          
                % function and slope values at new point: [fk1 gk1] = f(xk1);    
                fitparam = param_conv(param_check,0,0);
                pen_mod = genmod(fitparam,modcompstring);
                err_veck1 = err_vec_calc(pen_mod,meas); 
                err_k1 = err_veck1'*err_veck1;
                
                % handling cases where error goes complex do to instabilites in model from overly large steps                
                infinite_loop_flag = 0;
                while abs(imag(err_k1)) > 0
                    alpha = alpha/2;
                    param_check(param_index) = param(param_index) + alpha*dk;             % potential new position           
                    fitparam = param_conv(param_check,0,0);
                    pen_mod = genmod(fitparam,modcompstring);
                    err_veck1 = err_vec_calc(pen_mod,meas);
                    err_k1 = err_veck1'*err_veck1;
                    infinite_loop_flag = infinite_loop_flag + 1;
                    if infinite_loop_flag > 100 % stopping run and spitting out useful information
                        disp(['err_k1 = ',num2str(err_k1)])
                        disp(['alpha_min = ',num2str(alpha_min)])
                        disp(['alpha_max = ',num2str(alpha_max)])
                        disp(['alpha = ',num2str(alpha)])
                        disp(['iter_LS = ',num2str(iter_LS)])
                        disp(['fitparam = ',num2str(fitparam)])
                        disp(['param = ',num2str(param')])
                        disp(['param_check = ',num2str(param_check')])
                        disp(['dk = ',num2str(dk')])
                        assignin('base','pen_mod',pen_mod)
                        error('Infinite loop during complex error check')
                    end
                end
                
                for jj = 1:param_index_length    
                    param_test = param_check(param_index(jj)) + d_param(param_index(jj));
                    % Testing single parameter change
                    fitparam = param_conv(param_check,param_test,param_index(jj));
                    pen_mod_test = genmod(fitparam,modcompstring);
                    err_vec_test = err_vec_calc(pen_mod_test,meas);
                    err_test = err_vec_test'*err_vec_test;
                    grad_param_k1(param_index(jj)) = (err_test - err_k1)/d_param(param_index(jj));        
                end    
                
                if err_k1 <= err(ii-1) + Search.c1*alpha*dk'*grad_param(param_index) && err_k1 <= err(ii-1)  % Armijo rule: makes sure function is reduced
                    curve_left = dk'*grad_param_k1(param_index);
                    curve_right = Search.c2*dk'*grad_param(param_index);
                    if abs(curve_left) <= abs(curve_right) % Curvature condition: ensures significant reduction in slope
                        pass = 1;                          % end loop, Wolfe conditions have been met
                    else
                        if curve_left > curve_right        % Curvature failure: the left side of the inequality is greater, so we stepped too far, so set a new upper limit with this point.
                            alpha_max = alpha;
                        else                               % Curvature failure: the right side of the inequality is greater, so did not step far enough, so set a new lower limit with this point.
                            alpha_min = alpha;
                        end
                    end
                else
                    alpha_max = alpha;           % Armijo failure: sets alpha as the new upperlimit of the bracket
                end
            end
            
    
      % Step 3: Replace old data with new
        param(param_index) = param_check(param_index);
        err(ii) = err_k1;
        grad_param = grad_param_k1;  
        gradmag = sqrt(grad_param'*grad_param);
    
      % Display Progress
        disp(['1000*err(',num2str(ii),') = ',num2str(1000*err(ii))]), drawnow
        disp(['     |gradient| = ',num2str(gradmag),', alpha = ',num2str(alpha),', iter_LS = ',num2str(iter_LS)]), drawnow  
        
      % Display reason for terminating optimiztion
      if ii == Search.iter
          disp(' '),disp(['Maximum number of ',num2str(Search.iter),' iterations reached'])
      end
      if gradmag <= Search.g_tol
          disp(' '),disp(['Gradient magnitude reached desired tolerance of ',num2str(Search.g_tol)])
      end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Calculating final values and predicting the fit error %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% calculating the Jacobian matrix for the terminal point in the paramter space
fitparam = param_conv(param,0,0); % assignin('base','fitparam',fitparam)
pen_mod = genmod(fitparam,modcompstring);
err_vec = err_vec_calc(pen_mod,meas);
Jacobian = zeros(length(err_vec),param_index_length);
for jj = 1:param_index_length    
    param_test = param(param_index(jj)) + d_param(param_index(jj));
    % Testing single parameter change
    fitparam_vec_test = param_conv(param,param_test,param_index(jj)); % assignin('base','fitparam',fitparam)
    pen_mod_vec_test = genmod(fitparam_vec_test,modcompstring);
    err_vec_test = err_vec_calc(pen_mod_vec_test,meas);
    Jacobian(:,jj) = (err_vec_test - err_vec)/d_param(param_index(jj));                
end    
    
% Estimated fit errors. These come from the fisher information matrix.
% The fisher information matrix gives only a LOWER LIMIT on the fit error!!!! 
% It is not clear to me (Brett) how much below the true error this tends to be.
pinvJ = pinv(Jacobian);
param_err_raw = sqrt(diag(pinvJ*Cmeas_var*pinvJ'));
param_err = ones(param_length,1); count = 0;
for jj = 1:param_length % padding the unfit parameters with zeros
    
    if length(find(param_index-jj)) < param_index_length
        count = count + 1;
        param_err(jj) = param_err_raw(count);
    else
        param_err(jj) = 0;
    end
    
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Results %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% saving useful values
save(['Fit_Files/',save_filename],'pend','d_param','err','fitparam0','fitparam','param','param_index','param_index_length','pen_mod','Search','param_length','grad_param','Jacobian','pinvJ','Cmeas_var','param_err','meas','M1toM1TFdata')
assignin('base','pend',pend)                                % Original model structure
assignin('base','d_param',d_param)                          % finite difference parameter increment
assignin('base','err',err)                                  % err values at each Gauss-Newton iteration
assignin('base','fitparam0',fitparam0)                      % original model parameter list used by the model compilation code
assignin('base','fitparam',fitparam)                        % new model parameter list used by the model compilation code
assignin('base','param',param)                              % new model parameter list used by the fitting routine
assignin('base','param_index',param_index)                  % index of the chosen parameters for this fit
assignin('base','param_index_length',param_index_length)    % number of parameters chosen for this fit
assignin('base','pen_mod',pen_mod)                          % new model from this fit
assignin('base','Search',Search)                            % Gauss-Newton search parameters
assignin('base','param_length',param_length)                % length of the full parameter vector (param_index choses which of these to fit)
assignin('base','grad_param',grad_param)                    % error gradient with respect to the fitting model parameters
assignin('base','Jacobian',Jacobian)                        % Jacobian matrix linking changes in model parameters to changes in the error residuals
assignin('base','pinvJ',pinvJ)                              % Moore-Penrose pseudoinverse of the Jacobian matrix
assignin('base','Cmeas_var',Cmeas_var)                      % Variance of measurements
assignin('base','param_err',param_err)                      % RMS of parameter error after running the fit
assignin('base','meas',meas)                                % measured data used in this fit
assignin('base','M1toM1TFdata',M1toM1TFdata)                % M1 to M1 measured transfer function data


% if ii < Search.iter && pass < 2% if Search.gtol triggers end before max iteration limit
%     disp(['sqrt(grad_param''*grad_param) = ',num2str(sqrt(grad_param'*grad_param)),', Search.gtol = ',num2str(Search.gtol)])
% end

% Plotting fit against measurements
model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.L,in.m1.drive.L)
model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.T,in.m1.drive.T)
model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.V,in.m1.drive.V)
model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.Y,in.m1.drive.Y)
model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.P,in.m1.drive.P)
model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.R,in.m1.drive.R)
% model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.P,in.m1.drive.L)
model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.R,in.m1.drive.T)
% model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.L,in.m1.drive.P)
% model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.R,in.m1.drive.V)
% model_comparison(mbtrip,pen_mod,M1toM1TFdata,out.m1.disp.V,in.m1.drive.R)

% Plotting Error Evolution
figure,maxfig
semilogy(1:ii,1000*err(1:ii),'Linewidth',3),grid on
set(gca,'FontSize',25)
title(['1000*Err(1) = ',num2str(1000*err(1)),', 1000*Err(',num2str(ii),') = ',num2str(1000*err(ii)),'.'])
ylabel('1000 \times Error Magnitude'),xlabel('Iteration Number')

% Displaying the change in parameters from the model parameter file '...opt...'
disp(' '),disp(' ')
disp('Change in parameters from original model with the estimated minimum errors')
disp('---------------------------------------')
disp(['d0: ',num2str(1000*(param(1)-f_d0)),' +- ',num2str(1000*param_err(1)),' mm'])
disp(['d1: ',num2str(1000*(param(2)-f_d1)),' +- ',num2str(1000*param_err(2)),' mm'])
disp(['d2: ',num2str(1000*(param(3)-f_d2)),' +- ',num2str(1000*param_err(3)),' mm'])
disp(['d3: ',num2str(1000*(param(4)-f_d3)),' +- ',num2str(1000*param_err(4)),' mm'])
disp(['d4: ',num2str(1000*(param(5)-f_d4)),' +- ',num2str(1000*param_err(5)),' mm'])
disp(['I1x: ',num2str(100*(param(6)/f_I1x - 1)),' +- ',num2str(100*param_err(6)/f_I1x),' %'])
disp(['I2x: ',num2str(100*(param(7)/f_I2x - 1)),' +- ',num2str(100*param_err(7)/f_I2x),' %'])
disp(['I3x: ',num2str(100*(param(8)/f_I3x - 1)),' +- ',num2str(100*param_err(8)/f_I3x),' %'])
disp(['I1y: ',num2str(100*(param(9)/f_I1y - 1)),' +- ',num2str(100*param_err(9)/f_I1y),' %'])
disp(['I2y: ',num2str(100*(param(10)/f_I2y - 1)),' +- ',num2str(100*param_err(10)/f_I2y),' %'])
disp(['I3y: ',num2str(100*(param(11)/f_I3y - 1)),' +- ',num2str(100*param_err(11)/f_I3y),' %'])
disp(['I1z: ',num2str(100*(param(12)/f_I1z - 1)),' +- ',num2str(100*param_err(12)/f_I1z),' %'])
disp(['I2z: ',num2str(100*(param(13)/f_I2z - 1)),' +- ',num2str(100*param_err(13)/f_I2z),' %'])
disp(['I3z: ',num2str(100*(param(14)/f_I3z - 1)),' +- ',num2str(100*param_err(14)/f_I3z),' %'])
disp(['l1: ',num2str(1000*(param(15)-f_l1)),' +- ',num2str(1000*param_err(15)),' mm'])
disp(['l2: ',num2str(1000*(param(16)-f_l2)),' +- ',num2str(1000*param_err(16)),' mm'])
disp(['l3: ',num2str(1000*(param(17)-f_l3)),' +- ',num2str(1000*param_err(17)),' mm'])
disp(['si: ',num2str(1000*(param(18)-f_si)),' +- ',num2str(1000*param_err(18)),' mm'])
disp(['sl: ',num2str(1000*(param(19)-f_sl)),' +- ',num2str(1000*param_err(19)),' mm'])
disp(['r1: ',num2str(1000*(param(20)-f_r1)),' +- ',num2str(1000*param_err(20)),' mm'])
disp(['r2: ',num2str(1000*(param(21)-f_r2)),' +- ',num2str(1000*param_err(21)),' mm'])
disp(['r3: ',num2str(1000*(param(22)-f_r3)),' +- ',num2str(1000*param_err(22)),' mm'])
disp(['m1: ',num2str(1000*(param(23)-f_m1)),' +- ',num2str(1000*param_err(23)),' g'])
disp(['m2: ',num2str(1000*(param(24)-f_m2)),' +- ',num2str(1000*param_err(24)),' g'])
disp(['m3: ',num2str(1000*(param(25)-f_m3)),' +- ',num2str(1000*param_err(25)),' g'])
disp(['kc1: ',num2str(100*(param(26)/f_kc1 - 1)),' +- ',num2str(100*param_err(26)/f_kc1),' %'])
disp(['kc2: ',num2str(100*(param(27)/f_kc2 - 1)),' +- ',num2str(100*param_err(27)/f_kc2),' %'])
disp('---------------------------------------'),disp(' ')


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Functions %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function fitparam = param_conv(param,param_test,jj)
% Converts fitting parameter vector to a vector useful for the model compiling
% code

% testing a parameter for gradient calculation
if jj > 0
    param(jj) = param_test;
end

% converting param to fitparam for
fitparam = param;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function pen_mod = genmod(fitparam,modcompstring) %#ok<INUSL>
% compiling model with values in fitparam
% modfit = 1; %#ok<NASGU> % tells the model to compile with these parameters
natural_damp = 0.01;
eval(modcompstring)
pen_mod = mbtrip;
pen_mod.a(19:36,19:36) = -natural_damp*eye(18);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function model_comparison(mbtrip,pen_mod,meas_data,model_index_out,model_index_in)
% comparing model to top mass transfer functions to view results

f = meas_data.meas.freq;
w = 2*pi*f;
xlim = [0.1 10]; % x axis limits

in_names  = {'Gnd L','Gnd T','Gnd V','Gnd Y','Gnd P','Gnd R',...
             'TOP L','TOP T','TOP V','TOP Y','TOP P','TOP R',...
             'MID L','MID T','MID V','MID Y','MID P','MID R',...
             'BOT L','BOT T','BOT V','BOT Y','BOT P','BOT R'};
        
out_names = {'TOP L','TOP T','TOP V','TOP Y','TOP P','TOP R',...
             'MID L','MID T','MID V','MID Y','MID P','MID R',...
             'BOT L','BOT T','BOT V','BOT Y','BOT P','BOT R'};
         
in_units  = {'m','m','m','rad','rad','rad',...
             'N','N','N','Nm','Nm','Nm',...
             'N','N','N','Nm','Nm','Nm',...
             'N','N','N','Nm','Nm','Nm'};
        
out_units = {'m','m','m','rad','rad','rad',...
             'm','m','m','rad','rad','rad',...
             'm','m','m','rad','rad','rad'};         
         
model2meas_in_index = [zeros(6,1);1;2;3;6;5;4];
model2meas_out_index = [1;2;3;6;5;4];

[mod_mag,mod_ph] = bode(mbtrip(model_index_out,model_index_in),w);
mod_mag = abs(squeeze(mod_mag));
mod_ph = squeeze(mod_ph);
[mod_mag_fit,mod_ph_fit] = bode(pen_mod(model_index_out,model_index_in),w);
mod_mag_fit = abs(squeeze(mod_mag_fit));
mod_ph_fit = squeeze(mod_ph_fit);
meas_mag = abs(meas_data.meas.eul2eul(model2meas_out_index(model_index_out),model2meas_in_index(model_index_in)).f); % /((8.7431e-07)*1e6)
meas_ph = angle(meas_data.meas.eul2eul(model_index_out,model_index_in-6).f)*180/pi;

figure,maxfig
subplot(211)
loglog(f,meas_mag,'k',f,mod_mag,'b',f,mod_mag_fit,'r','LineWidth',3),grid on
set(gca,'XLim',xlim,'FontSize',25,'YTick',logspace(-10,10,11))%,'YLim',ylims)
title(['Triple Model Fit TF Results: ',in_names{model_index_in},' to ',out_names{model_index_out}])
ylabel([out_names{model_index_out},' Response (',out_units{model_index_out},'/',in_units{model_index_in},')'])
subplot(212)
semilogx(f,meas_ph,'k',f,mod_ph,'b',f,mod_ph_fit,'r','LineWidth',3),grid on
set(gca,'XLim',xlim,'FontSize',25,'YLim',[-185 5],'YTick',linspace(-180,0,5))
xlabel('Hz'),ylabel('Phase (degrees)')
legend('Measurement','Original Model','Fit Model',3)

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


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function err = err_vec_calc(mbtrip,meas) 
% This function calculates the percent error residuals between the model
% and the measurements. The residuals are output as a column vector. This
% function must be modified to account for any changes in which type of input data
% is used for the fit.

phi = zeros(22,1); % model predicted values
err = zeros(17,1); % percent error residuals between model predicted values and measured values
% phi = zeros(4,1); % model predicted values
% err = zeros(1,1); % percent error residuals between model predicted values and measured values

% Calculating the Long-Pitch model error
lpmodel = model_cut_triple(mbtrip,'lp');
phi(1:6) = sqrt(sort(eig(-lpmodel.a(7:12,1:6)))); % model modes for free suspension
err(1:6) = (meas.modes.free.LP.modefreqs-phi(1:6))./meas.modes.free.LP.modefreqs;

% Calculating the Vertical model error
vmodel = model_cut_triple(mbtrip,'v');
phi(7:9) = sqrt(sort(eig(-vmodel.a(4:6,1:3)))); % model modes for free suspension
err(7:8) = (meas.modes.free.V.modefreqs-phi(7:8))./meas.modes.free.V.modefreqs;

% % Calculating the top to top TF vertical zero
% v23model = model_cut_triple(mbtrip,'v23');
% phi(10:11) = sqrt(sort(eig(-v23model.a(3:4,1:2)))); % model modes for free suspension
% err(9) = (meas.modes.toplocked.V.modefreqs-phi(10))./meas.modes.toplocked.V.modefreqs;

% Calculating the Yaw model error
ymodel = model_cut_triple(mbtrip,'y');
phi(10:12) = sqrt(sort(eig(-ymodel.a(4:6,1:3)))); % model modes for free suspension
err(9:11) = (meas.modes.free.Y.modefreqs-phi(10:12))./meas.modes.free.Y.modefreqs;

% Calculating the Trans-Roll model error
trmodel = model_cut_triple(mbtrip,'tr');
phi(13:18) = sqrt(sort(eig(-trmodel.a(7:12,1:6)))); % model modes for free suspension
err(12:16) = (meas.modes.free.TR.modefreqs-phi(13:17))./meas.modes.free.TR.modefreqs;

% Calculating the top mass long mode when the cavity is locked at the mid
% stage with high gain
l1p123 = model_cut_triple(mbtrip,'l1p123');
phi(19:22) = sqrt(sort(eig(-l1p123.a(5:8,1:4)))); % model modes for free suspension
err(17) = (meas.modes.cavmidbot.LP.modefreqs-phi(20))./meas.modes.cavmidbot.LP.modefreqs;
% l1p123 = model_cut_triple(mbtrip,'l1p123');
% phi(1:4) = sqrt(sort(eig(-l1p123.a(5:8,1:4)))); % model modes for free suspension
% err(1) = (meas.modes.cavmidbot.LP.modefreqs-phi(2))./meas.modes.cavmidbot.LP.modefreqs;

% % M1 to M1 high freq mag
% freqlength = 100;
% freq = 2*pi*linspace(7,9,freqlength);
% phi(23) = sum(abs(squeeze(freqresp(mbtrip(1,7),freq))))/freqlength;
% err(18) = (meas.mag.M1toM1.L2L-phi(23))/meas.mag.M1toM1.L2L;


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [mbtriple,pend]=ssmake3MBf_modelfitting(fitparam,varargin) %#ok<DEFNU>

% ssmake3MBf - the full-matrix version of the triple 'make' file to end (hopefully) all triple make files.
%
% Generates 4 systems of state-space matrices representing a triple pendulum.
%
% This file loads file triple.m which should define structure pend with fields for
%  all required parameters.
%
% Depending on the selected options, this file loads one of symbexport3.m,
%  symbexport3db.m, symbexport3lat.m, or symbexport3dblat.m. These contain
%  mechanically generated Matlab code for state-space matrix elements
%  exported from the corresponding Mathematica model by Mark Barton.
%
% If pend.db is defined and non-zero, top mass has dual blades at both levels 
%  as proposed for the AdvLIGO beamsplitter, otherwise, quad blades at top
%  mass a la GEO.
%
% If pend.kx1 and pend.kx2 are defined as per-side stiffnesses, blades can flex
%  laterally, otherwise just vertically.
%
% If pend.ribbon is defined and non-zero, bottom stage is taken to have
%  ribbons of thickness pend.t3 and width pend.W3.
%
% If pend.stage2 is defined and non-zero, d0-d4 are interpreted as raw
%  values, i.e., as actual wire breakoff vertical positions, and fudges are
%  applied to include the effect of wire flexure. The fudges used give essentially
%  perfect agreement with the output of Stage 2 of the equivalent
%  Mathematica model if pend.ribbon is not set, and very good agreement
%  otherwise. If pend.stage2 is undefined or 0, d0-d4 are taken as effective values, 
%  and flexure correction is assumed to be inapplicable (as for infinitely 
%  flexible wire) or to have been applied manually.
%
% Ultimately based on the mathematical model of Calum Iain Torrie et al. 1998
% with contributions from K.A. Strain 6/99.

% 8/16/06: initial version based on ssmake3pv2nMB2.m for GEO-style triple with 
%  four blades, with lots of improvements cut'n'pasted from ssm4pv2eMB4.m.
%  Transverse/roll SS object renamed from rtn to trn to avoid naming
%  conflict with Real-Time Workshop. All B matrix signs hand-checked.

% 3/4/07: corrected formula for flex1/2/3 (was '4' instead of 'nw1'/'nw2'/'nw3')

% 6/21/2011: renamed ssmake3MBf.m, reworked to use full matrices from symbexport3full.m etc.

% 9/27/2012 Brett Shapiro: Added section between lines 57 and 83 to interface with the
% model fitting file TripPend_GaussNewton_fit_...m. Added the suffix
% '_modelfitting' to the name of this file to distinguish it from the
% non-model fitting version. Added the input, 'fitparam' to the function,
% which is the vector of values modified from the 'pend' structure.
% Commented out some disp comments that output to command line.
% TripPend_GaussNewton_fit calls this function many times and will flood
% the command line like crazy if they are left in.

% load in the pendulum parameters
% global pend
if isempty(varargin)
    triplep;
else
    pend = eval([varargin{1} ';']);
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% for use with model fitting code TripPend_GuassNewton_...m %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    pend.d0  =  fitparam(1);
    pend.d1  =  fitparam(2);
    pend.d2  =  fitparam(3);
    pend.d3  =  fitparam(4);
    pend.d4  =  fitparam(5);
    pend.I1x =  fitparam(6);
    pend.I2x =  fitparam(7);
    pend.I3x =  fitparam(8); 
    pend.I1y =  fitparam(9);
    pend.I2y =  fitparam(10);
    pend.I3y =  fitparam(11);       
    pend.I1z =  fitparam(12);
    pend.I2z =  fitparam(13);
    pend.I3z =  fitparam(14);    
    pend.l1  =  fitparam(15);
    pend.l2  =  fitparam(16);
    pend.l3  =  fitparam(17); 
    pend.si  =  fitparam(18);
    pend.sl  =  fitparam(19);
    pend.r1  =  fitparam(20);
    pend.r2  =  fitparam(21);
    pend.r3  =  fitparam(22);
    pend.m1  =  fitparam(23);
    pend.m2  =  fitparam(24);
    pend.m3  =  fitparam(25);
    pend.kc1 =  fitparam(26);
    pend.kc2 =  fitparam(27);
    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% for use with model fitting code TripPend_GuassNewton_...m %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% Set any undefined flags to default values
if not(isfield(pend,'stage2'))
    pend.stage2=0; % define, default to false (wire flexure correction neglected or implicit)
end
if not(isfield(pend,'ribbon') )
    pend.ribbon=0; % define, default to false (round fibres)
end
if not(isfield(pend,'db') )
    pend.db=0; % define, default to false (GEO-style quad blades, not AdvLIGO-style double)
end
if not(isfield(pend,'g') )
    pend.g=9.81; 
end

% general redefintion of variables to simplify notation
% and cope with different pendulum designs

g = pend.g;
Y1   = pend.Y1;
Y2   = pend.Y2;
Y3   = pend.Y3;

% Allow for blade stiffnesses to be specified directly rather than via ufcs.
if isfield(pend, 'kc1')
    kc1 = pend.kc1;
    pend.ufc1 = sqrt(2*kc1/pend.m1)/2/pi;
else
    kc1	=  1/2 * (2*pi*pend.ufc1)^2*pend.m1;
    pend.kc1 = kc1;
end
if isfield(pend, 'kc2')
    kc2 = pend.kc2;
    pend.ufc2 = sqrt(2*kc2/pend.m2)/2/pi;
else
    kc2	=  1/2 * (2*pi*pend.ufc2)^2*pend.m2;
    pend.kc2 = kc2;
end

l1  =pend.l1;
l2  =pend.l2;
l3  =pend.l3;

% wire stiffnesses (per side, as opposed to per wire for the Mathematica)
kw1	=pend.Y1*pi*pend.r1^2/l1*pend.nw1/2;	
kw2	=pend.Y2*pi*pend.r2^2/l2*pend.nw2/2;	
if not(isfield(pend,'kw3')) % vertical stiffness of fibers
    if pend.ribbon
        kw3	= pend.Y3*pend.W3*pend.t3/l3*pend.nw3/2;
    else
        kw3	= pend.Y3*pi*pend.r3^2/l3*pend.nw3/2;	
    end
else
    kw3 = pend.kw3;
end

%********************************************************************

% allows choice of 2 wires to set separation to zero

s0=pend.su; %#ok<NASGU>
s1=pend.su; %#ok<NASGU>
s2=pend.si; %#ok<NASGU>
s3=pend.si; %#ok<NASGU>
s4=pend.sl; %#ok<NASGU>
s5=pend.sl; %#ok<NASGU>

d0=pend.d0;
d1=pend.d1;
d2=pend.d2;
d3=pend.d3;
d4=pend.d4;

%********************************************************************

m1 = pend.m1;
m2 = pend.m2;
m3 = pend.m3;

I1x = pend.I1x; %#ok<NASGU>
I2x = pend.I2x; %#ok<NASGU>
I3x = pend.I3x; %#ok<NASGU>
I1y = pend.I1y; %#ok<NASGU>
I2y = pend.I2y; %#ok<NASGU>
I3y = pend.I3y; %#ok<NASGU>
I1z = pend.I1z; %#ok<NASGU>
I2z = pend.I2z; %#ok<NASGU>
I3z = pend.I3z; %#ok<NASGU>

% for backward compatibility, default off-axis MOI terms to zero
if isfield(pend, 'I1xy')
else
    pend.I1xy = 0;
end
if isfield(pend, 'I1yz')
else
    pend.I1yz = 0;
end
if isfield(pend, 'I1zx')
else
    pend.I1zx = 0;
end
if isfield(pend, 'I2xy')
else
    pend.I2xy = 0;
end
if isfield(pend, 'I2yz')
else
    pend.I2yz = 0;
end
if isfield(pend, 'I2zx')
else
    pend.I2zx = 0;
end
if isfield(pend, 'I3xy')
else
    pend.I3xy = 0;
end
if isfield(pend, 'I3yz')
else
    pend.I3yz = 0;
end
if isfield(pend, 'I3zx')
else
    pend.I3zx = 0;
end

I1xy = pend.I1xy; %#ok<NASGU>
I1yz = pend.I1yz; %#ok<NASGU>
I1zx = pend.I1zx; %#ok<NASGU>
I2xy = pend.I2xy; %#ok<NASGU>
I2yz = pend.I2yz; %#ok<NASGU>
I2zx = pend.I2zx; %#ok<NASGU>
I3xy = pend.I3xy; %#ok<NASGU>
I3yz = pend.I3yz; %#ok<NASGU>
I3zx = pend.I3zx; %#ok<NASGU>
m13	= m1+m2+m3;
m23	= m2+m3;

n0  = pend.n0;
n1  = pend.n1;
n2  = pend.n2;
n3  = pend.n3;
n4  = pend.n4;
n5  = pend.n5;

%***********************************************************************************

% cosine and sine of the angle the wire makes with the vertical (z)

si1=(n1-n0)/l1;			%sin(omega1)
si2=(n3-n2)/l2;			%sin(omega2)
si3=(n5-n4)/l3;			%sin(omega3)	
	
c1=(l1^2-(n1-n0)^2)^0.5/l1;	%cos(omega1)
c2=(l2^2-(n3-n2)^2)^0.5/l2;	%cos(omega2)
c3=(l3^2-(n5-n4)^2)^0.5/l3;	%cos(omega3)

k1 = kw1; %#ok<NASGU>
k2 = kw2; %#ok<NASGU>
k3 = kw3; %#ok<NASGU>
su=pend.su; %#ok<NASGU>
si=pend.si; %#ok<NASGU>
sl=pend.sl; %#ok<NASGU>

% Total Mass Loads
pend.m13 = m13;
pend.m23 = m23;

% Vertical distances between centres of mass 
pend.tl1 = sqrt(pend.l1^2 - (pend.n0-pend.n1)^2) + pend.d0;
pend.tl2 = sqrt(pend.l2^2 - (pend.n2-pend.n3)^2) + pend.d1 + pend.d2;
pend.tl3 = sqrt(pend.l3^2 - (pend.n4-pend.n5)^2) + pend.d3 + pend.d4;

% Distance to the centre of mass from suspension point 
pend.l_suspoint_to_centreofoptic  =  pend.tl1+pend.tl2+pend.tl3;

% Distance to the bottom of the test mass from suspension point 
pend.l_suspoint_to_bottomofoptic  =  pend.tl1+pend.tl2+pend.tl3+pend.tr;

% Stiffness of fibres/ribbons
% pi = 4*atan(1);
M11 = (1/4)*pi*pend.r1^4;
M21 = (1/4)*pi*pend.r2^4;
if pend.ribbon
    M31 = (1/4)*pend.W3*pend.t3^3/12;
    M32 = (1/4)*pend.W3^3*pend.t3/12;
else
    M31 = (1/4)*pi*pend.r3^4;
    M32 = (1/4)*pi*pend.r3^4;
end
pend.flex1 = sqrt(pend.nw1*M11*Y1/m13/g)*c1^(3/2);
pend.flex2 = sqrt(pend.nw2*M21*Y2/m23/g)*c2^(3/2);
pend.flex3 = sqrt(pend.nw3*M31*Y3/m3/g)*c3^(3/2); % for long/pitch, yaw and vertical
pend.flex3tr = sqrt(pend.nw3*M32*Y3/m3/g)*c3^(3/2); % for trans/roll, with M32 instead of M31

flex1 = pend.flex1;
flex2 = pend.flex2;
flex3 = pend.flex3; % for long/pitch, yaw and vertical; see below for trans/roll

if pend.stage2
    % apply fudges to approximate Mathematica Stage2 results
    d0 = d0 + flex1; %#ok<NASGU>
    d1 = d1 + flex2; %#ok<NASGU>
    d2 = d2 + flex2; %#ok<NASGU>
    d3 = d3 + flex3; %#ok<NASGU>
    d4 = d4 + flex3; %#ok<NASGU>
    l1 = l1 - 2*flex1/c1; %#ok<NASGU>
    l2 = l2 - 2*flex2/c2; %#ok<NASGU>
    l3 = l3 - 2*flex3/c3; %#ok<NASGU>
    n0 = n0 + si1*flex1/c1; %#ok<NASGU>
    n1 = n1 - si1*flex1/c1; %#ok<NASGU>
    n2 = n2 + si2*flex2/c2; %#ok<NASGU>
    n3 = n3 - si2*flex2/c2; %#ok<NASGU>
    n4 = n4 + si3*flex3/c3; %#ok<NASGU>
    n5 = n5 - si3*flex3/c3; %#ok<NASGU>
%     disp('Using Stage 2 fudges');
else
    disp('Not using Stage 2 fudges');
end

bd = pend.bd;

latelements=0; 
if isfield(pend,'kx1') 
    if isfield(pend,'kx2')
        % lateral compliances have been defined - use appropriate matrix elements
        disp('Using matrix elements with blade lateral compliance');
        latelements = 1;
        kx1 = pend.kx1; %#ok<NASGU>
        kx2 = pend.kx2; %#ok<NASGU>
        if pend.db
            symbexport3dblatfull
        disp('Using matrix elements with double blades at top mass');
        else
         disp('Using matrix elements with quad blades at top mass');
           symbexport3latfull
        end
    else

    end   
end

if not(latelements)
%     disp('Using matrix elements with no blade lateral compliance');
    if pend.db
        disp('Using matrix elements with double blades at top mass');
        symbexport3dbfull
    else
%         disp('Using matrix elements with quad blades at top mass');
        symbexport3full
    end
end

mbtripleA = [...
    zeros(18) eye(18)
    (-km)\(xm-cqxm'/qm*cqxm) -bd*eye(18)
];

bm = (-km)\(cxsm-cqxm'/qm*cqsm); % ground displacement inputs: x,y,z,yaw,pitch,roll
bm2 = km\eye(18); % coordinate force inputs: pitchn,xn,pitch1,x1,...

mbtripleB = [...
    zeros(18,24)
    bm bm2
];

mbtripleC = [...
    eye(18) zeros(18,18)
];
 
 mbtripleD = [...
    zeros(18,24)
 ]; %#ok<NBRAK>

mbtriple = ss(mbtripleA,mbtripleB,mbtripleC,mbtripleD);