JSBSim Flight Dynamics Model 1.0 (23 February 2013)
An Open Source Flight Dynamics and Control Software Library in C++

FGTrimAnalysis.cpp

00001 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00002 
00003  Header:       FGTrimAnalysis.cpp
00004  Author:       Agostino De Marco
00005  Date started: Dec/14/2006
00006 
00007  ------------- Copyright (C) 2006  Agostino De Marco (agodemar@unina.it) -------
00008 
00009  This program is free software; you can redistribute it and/or modify it under
00010  the terms of the GNU Lesser General Public License as published by the Free Software
00011  Foundation; either version 2 of the License, or (at your option) any later
00012  version.
00013 
00014  This program is distributed in the hope that it will be useful, but WITHOUT
00015  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00016  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
00017  details.
00018 
00019  You should have received a copy of the GNU Lesser General Public License along with
00020  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
00021  Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00023  Further information about the GNU Lesser General Public License can also be found on
00024  the world wide web at http://www.gnu.org.
00025 
00026  HISTORY
00027 --------------------------------------------------------------------------------
00028 12/14/06   ADM   Created
00029 
00030 FUNCTIONAL DESCRIPTION
00031 --------------------------------------------------------------------------------
00032 
00033 This class takes the given set of IC's and analyzes the possible trim states
00034 of the aircraft, i.e. finds the aircraft state required to
00035 maintain a specified flight condition.  This flight condition can be
00036 steady-level, a steady turn, a pull-up or pushover.
00037 It is implemented using an iterative, direct search of a cost function minimum.
00038 
00039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00040 SENTRY
00041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
00042 
00043 //  !!!!!!! BEWARE ALL YE WHO ENTER HERE !!!!!!!
00044 
00045 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00046 INCLUDES
00047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
00048 
00049 #include <stdlib.h>
00050 
00051 #include "FGFDMExec.h"
00052 #include "models/FGAtmosphere.h"
00053 #include "initialization/FGInitialCondition.h"
00054 #include "FGTrimAnalysis.h"
00055 #include "models/FGAircraft.h"
00056 #include "models/FGMassBalance.h"
00057 #include "models/FGGroundReactions.h"
00058 #include "models/FGInertial.h"
00059 #include "models/FGAerodynamics.h"
00060 #include "math/FGColumnVector3.h"
00061 
00062 #include "input_output/FGPropertyManager.h"
00063 #include "input_output/FGXMLParse.h"
00064 
00065 #include "models/propulsion/FGPiston.h"
00066 #include "models/propulsion/FGPropeller.h"
00067 #include "models/propulsion/FGTurbine.h"
00068 
00069 #include "math/FGTable.h"
00070 
00071 #if defined(sgi) && !defined(__GNUC__)
00072 # include <math.h>
00073 #else
00074 # include <cmath>
00075 #endif
00076 #include <sstream>
00077 #include <map>
00078 
00079 #include "math/direct_search/SMDSearch.h"
00080 #include "math/direct_search/NMSearch.h"
00081 #include "math/direct_search/vec.h"
00082 
00083 #if _MSC_VER
00084 #pragma warning (disable : 4786 4788)
00085 #endif
00086 
00087 namespace JSBSim {
00088 
00089 static const char *IdSrc = "$Id: FGTrimAnalysis.cpp,v 1.14 2011/03/23 11:58:29 jberndt Exp $";
00090 static const char *IdHdr = ID_FGTRIMANALYSIS;
00091 
00092 
00105     //friend void find_CostFunctionFull(long vars, Vector<double> &v, double & f,
00106     void find_CostFunctionFull(long vars, Vector<double> &v, double & f,
00107                     bool & success, void* t_ptr)
00108     {
00109         (*(Objective*)t_ptr).CostFunctionFull(vars, v, f);
00110         success = true;
00111     }
00124     //friend void find_CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f,
00125     void find_CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f,
00126                     bool & success, void* t_ptr)
00127     {
00128         (*(Objective*)t_ptr).CostFunctionFullWingsLevel(vars, v, f);
00129         success = true;
00130     }
00143     //friend void find_CostFunctionLongitudinal(long vars, Vector<double> &v, double & f,
00144     void find_CostFunctionLongitudinal(long vars, Vector<double> &v, double & f,
00145                     bool & success, void* t_ptr)
00146     {
00147         (*(Objective*)t_ptr).CostFunctionLongitudinal(vars, v, f);
00148         success = true;
00149     }
00162     //friend void find_CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f,
00163     void find_CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f,
00164                     bool & success, void* t_ptr)
00165     {
00166         (*(Objective*)t_ptr).CostFunctionFullCoordinatedTurn(vars, v, f);
00167         success = true;
00168     }
00181     //fryyiend void find_CostFunctionFullTurn(long vars, Vector<double> &v, double & f,
00182     void find_CostFunctionFullTurn(long vars, Vector<double> &v, double & f,
00183                     bool & success, void* t_ptr)
00184     {
00185         (*(Objective*)t_ptr).CostFunctionFullTurn(vars, v, f);
00186         success = true;
00187     }
00200     //friend void find_CostFunctionPullUp(long vars, Vector<double> &v, double & f,
00201     void find_CostFunctionPullUp(long vars, Vector<double> &v, double & f,
00202                     bool & success, void* t_ptr)
00203     {
00204         (*(Objective*)t_ptr).CostFunctionPullUp(vars, v, f);
00205         success = true;
00206     }
00207 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00208 //public:
00209 
00210 Objective::Objective(FGFDMExec* fdmex, FGTrimAnalysis* ta, double x) : _x(x), TrimAnalysis(ta), FDMExec(fdmex)
00211 {
00212     // populate the map:type-of-trim/function-pointer
00213     mpCostFunctions[taFull           ] = find_CostFunctionFull;
00214     mpCostFunctions[taFullWingsLevel ] = find_CostFunctionFullWingsLevel;
00215     mpCostFunctions[taLongitudinal   ] = find_CostFunctionLongitudinal;
00216     mpCostFunctions[taTurn           ] = find_CostFunctionFullCoordinatedTurn;
00217     mpCostFunctions[taPullup         ] = find_CostFunctionPullUp;
00218 }
00219 
00220 void Objective::CostFunctionFull(long vars, Vector<double> &v, double & f)
00221 {
00222   if (vars != 7) {
00223       cerr << "\nError: (Cost function for taFull mode) Dimension must be 7 !!\n";
00224       exit(1);
00225   }
00226   if (TrimAnalysis->GetMode()!=taFull){
00227       cerr << "\nError: must be taFull mode !!\n";
00228       exit(1);
00229   }
00230 
00231   f = myCostFunctionFull(v);
00232   return;
00233 }
00234 
00235 void Objective::CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f)
00236 {
00237   if (vars != 6) {
00238       cerr << "\nError: (Cost function for taFullWingsLevel mode) Dimension must be 6 !!\n";
00239       exit(1);
00240   }
00241   if (TrimAnalysis->GetMode()!=taFullWingsLevel){
00242       cerr << "\nError: must be taFull mode !!\n";
00243       exit(1);
00244   }
00245 
00246   f = myCostFunctionFullWingsLevel(v);
00247   return;
00248 }
00249 
00250 void Objective::CostFunctionLongitudinal(long vars, Vector<double> &v, double & f)
00251 {
00252   if (vars != 3) {
00253       cerr << "\nError: (Cost function for taLongitudinal mode) Dimension must be 3 !!\n";
00254       exit(1);
00255   }
00256   if (TrimAnalysis->GetMode()!=taLongitudinal){
00257       cerr << "\nError: trim mode must be taLongitudinal mode !!\n";
00258       exit(1);
00259   }
00260 
00261   f = myCostFunctionLongitudinal(v);
00262   return;
00263 }
00264 
00265 void Objective::CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f)
00266 {
00267   if (vars != 5) {
00268       cerr << "\nError: (Cost function for taTurn mode) Dimension must be 5 !!\n";
00269       exit(1);
00270   }
00271   if (TrimAnalysis->GetMode()!=taTurn){
00272       cerr << "\nError: trim mode must be taTurn mode !!\n";
00273       exit(1);
00274   }
00275 
00276   f = myCostFunctionFullCoordinatedTurn(v);
00277   return;
00278 }
00279 
00280 void Objective::CostFunctionFullTurn(long vars, Vector<double> &v, double & f)
00281 {
00282   if (vars != 6) {
00283       cerr << "\nError: (Cost function for taTurn mode) Dimension must be 6 !!\n";
00284       exit(1);
00285   }
00286   if (TrimAnalysis->GetMode()!=taTurnFull){
00287       cerr << "\nError: trim mode must be taTurnFull ("<< (int)taTurnFull << ") mode !!\n";
00288       exit(1);
00289   }
00290 
00291   f = myCostFunctionFullTurn(v);
00292   return;
00293 }
00294 
00295 void Objective::CostFunctionPullUp(long vars, Vector<double> &v, double & f)
00296 {
00297   if (vars != 5) {
00298       cerr << "\nError: (Cost function for taPullup mode) Dimension must be 5 !!\n";
00299       exit(1);
00300   }
00301   if (TrimAnalysis->GetMode()!=taPullup){
00302       cerr << "\nError: trim mode must be taPullup mode !!\n";
00303       exit(1);
00304   }
00305 
00306   f = myCostFunctionPullUp(v);
00307   return;
00308 }
00309 
00310 void Objective::Set_x_val(double new_x)
00311 {
00312     _x = new_x;
00313 }
00314 
00315 //private:
00316 // see the end of the file
00317 
00318 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00319 
00320 FGTrimAnalysis::FGTrimAnalysis(FGFDMExec *FDMExec,TrimAnalysisMode tt)
00321 {
00322   SetDebug(2);
00323 
00324   N=0;
00325 
00326   trim_failed = true;
00327 
00328   max_iterations=2500;
00329   stop_criterion="Stop-On-Delta";
00330 
00331   Debug=0;DebugLevel=0;
00332 
00333   fdmex=FDMExec;
00334 
00335   fgic=fdmex->GetIC();
00336   total_its=0;
00337   trimudot=true;
00338   gamma_fallback=true;
00339   ctrl_count=0;
00340   mode=tt;
00341   _targetNlf=1.0;
00342   _targetNlf=fgic->GetTargetNlfIC();
00343 
00344   _vtIC  = fgic->GetVtrueFpsIC();
00345   _hIC   = fgic->GetAltitudeFtIC();
00346   _gamma = fgic->GetFlightPathAngleRadIC();
00347   _rocIC = _vtIC*cos(_gamma);
00348   _vdownIC = _rocIC;
00349 
00350   // state variables
00351   _u       = fgic->GetUBodyFpsIC();
00352   _v       = fgic->GetVBodyFpsIC();
00353   _w       = fgic->GetWBodyFpsIC();
00354   _p       = fgic->GetPRadpsIC();
00355   _q       = fgic->GetQRadpsIC();
00356   _r       = fgic->GetRRadpsIC();
00357   _alpha   = fgic->GetAlphaRadIC();
00358   _beta    = fgic->GetBetaRadIC();
00359   _theta   = fgic->GetThetaRadIC();
00360   _phi     = fgic->GetPhiRadIC();
00361   _psiIC   = fgic->GetPsiRadIC();
00362   _psi     = _psiIC;
00363   _psigtIC = _psi;
00364 
00365   _phiW = _psiW = 0.0;
00366 
00367   _vgIC = _vtIC*cos(_gamma);
00368   _vnorthIC = _vgIC * cos(_psigtIC);
00369   _veastIC  = _vgIC * sin(_psigtIC);
00370   wnorthIC = _weastIC = _wdownIC = 0.;
00371 
00372   _udot=_vdot=_wdot=_pdot=_qdot=_rdot=0.;
00373   _psidot=_thetadot=0.;
00374   _psiWdot = _phiWdot = _gammadot = 0.;
00375 
00376   C1 = C2 = C3 = 1.0;
00377   _cbeta = cos(_beta);
00378   _sbeta = sin(_beta);
00379   _sphi = sin(_phi);
00380 
00381   SetMode(tt); // creates vTrimAnalysisControls
00382   fdmex->SetTrimMode( (int)tt );
00383 
00384   trim_id = "default-trim";
00385 
00386   // direct search stuff
00387   search_type = "Nelder-Mead";
00388   sigma_nm = 0.5; alpha_nm = 1.0; beta_nm = 0.5; gamma_nm = 2.0;
00389   initial_step = 0.01;
00390   tolerance = 1.0E-10; // 0.0000000001
00391   cost_function_value = 9999.0;
00392 
00393   rf_name = "";
00394   if (rf.is_open()) rf.close();
00395 
00396   Auxiliary    = fdmex->GetAuxiliary();
00397   Aerodynamics = fdmex->GetAerodynamics();
00398   Propulsion   = fdmex->GetPropulsion();
00399   FCS          = fdmex->GetFCS();
00400 }
00401 
00402 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00403 
00404 FGTrimAnalysis::~FGTrimAnalysis(void) {
00405 
00406   ClearControls();
00407 
00408   vAlphaDeg.clear();
00409   vCL.clear(); vCD.clear(); vCm.clear();
00410   vThrottleCmd.clear(); vElevatorCmd.clear();
00411   vVn.clear(); vTn.clear();
00412 
00413   if (rf.is_open()) rf.close();
00414 
00415   fdmex->SetTrimStatus(false);
00416   fdmex->SetTrimMode( 99 );
00417 }
00418 
00419 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00420 
00421 void FGTrimAnalysis::SetState(double u0, double v0, double w0, double p0, double q0, double r0,
00422                               double alpha0, double beta0, double phi0, double theta0, double psi0, double gamma0)
00423 {
00424   _u=u0; _v=v0; _w=w0;
00425   _p=p0; _q=q0; _r=r0;
00426   _alpha=alpha0; _beta=beta0; _gamma=gamma0;
00427   _theta=theta0; _phi=phi0; _psi=psi0;
00428 }
00429 
00430 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00431 
00432 void FGTrimAnalysis::SetEulerAngles(double phi, double theta, double psi)
00433 {
00434     // feed into private variables
00435     _phi = phi; _cphi = cos(_phi); _sphi = sin(_phi);
00436     _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta);
00437     _psi = psi; _cpsi = cos(_psi); _spsi = sin(_psi);
00438 
00439 }
00440 
00441 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00442 
00443 void FGTrimAnalysis::SetDottedValues(double udot, double vdot, double wdot, double pdot, double qdot, double rdot)
00444 {
00445     _udot=udot; _vdot=vdot; _wdot=wdot; _pdot=pdot; _qdot=qdot; _rdot=rdot;
00446 }
00447 
00448 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00449 
00450 bool FGTrimAnalysis::Load(string fname, bool useStoredPath)
00451 {
00452     string name="", type="";
00453     string trimDef;
00454     Element *element=0, *trimCfg=0, *search_element=0, *output_element=0;
00455 
00456     string sep = "/";
00457 # ifdef macintosh
00458     sep = ";";
00459 # endif
00460 
00461     if( useStoredPath ) {
00462         trimDef = fdmex->GetFullAircraftPath() + sep + fname + ".xml";
00463     } else {
00464         trimDef = fname;
00465     }
00466 
00467     document = this->LoadXMLDocument(trimDef);
00468 
00469     trimCfg = document->FindElement("trim_config");
00470     if (!trimCfg) {
00471         cerr << "File: " << trimDef << " does not contain a trim configuration tag" << endl;
00472         return false;
00473     }
00474 
00475     name = trimCfg->GetAttributeValue("name");
00476     trim_id = name;
00477 
00478     // First, find "search" element that specifies the type of cost function minimum search
00479 
00480     search_element = trimCfg->FindElement("search");
00481     if (!search_element) {
00482         cerr << "Using the Nelder-Mead search algorithm (default)." << endl;
00483     } else {
00484         type = search_element->GetAttributeValue("type");
00485         if (type.size() > 0) search_type = type; // if search type is not set, default is already Nelder-Mead
00486         if (search_type == "Nelder-Mead") {
00487             // Read settings from search
00488             // Note: all of these have defaults set above
00489             if ( search_element->FindElement("sigma_nm") )
00490                 sigma_nm = search_element->FindElementValueAsNumber("sigma_nm");
00491             if ( search_element->FindElement("alpha_nm") )
00492                 alpha_nm = search_element->FindElementValueAsNumber("alpha_nm");
00493             if ( search_element->FindElement("beta_nm") )
00494                 beta_nm = search_element->FindElementValueAsNumber("beta_nm");
00495             if ( search_element->FindElement("gamma_nm") )
00496                 gamma_nm = search_element->FindElementValueAsNumber("gamma_nm");
00497         }
00498         // ToDo: manage all the other possible choices here
00499         // if (search_type == "Sequential-Multiple-Nelder-Mead") { }
00500         // if (search_type == "Multicompass") { }
00501         // etc ...
00502         if ( search_element->FindElement("tolerance") ) {
00503             tolerance = search_element->FindElement("tolerance")->GetAttributeValueAsNumber("value");
00504         }
00505         if ( search_element->FindElement("max_iterations") ) {
00506             max_iterations = (unsigned int)search_element->FindElement("max_iterations")->GetAttributeValueAsNumber("value");
00507         }
00508         if ( search_element->FindElement("stop_criterion") ) {
00509             stop_criterion = search_element->FindElement("stop_criterion")->GetAttributeValue("type");
00510         }
00511     }
00512 
00513     // Initialize trim controls based on what is in the trim config file. This
00514     // includes initial trim values (or defaults from the IC file) and step
00515     // size values.
00516 
00517     element = trimCfg->FindElement("phi");
00518     InitializeTrimControl(fgic->GetPhiRadIC(), element, "RAD", JSBSim::taPhi);
00519     if ( ( fabs(_phi) < 89.5*(FGJSBBase::degtorad ) ) && ( mode == taTurn ))
00520                 _targetNlf = 1./cos(_phi);
00521 
00522     element = trimCfg->FindElement("theta");
00523     InitializeTrimControl(fgic->GetThetaRadIC(), element, "RAD", JSBSim::taTheta);
00524     
00525     element = trimCfg->FindElement("psi");
00526     InitializeTrimControl(fgic->GetPsiRadIC(), element, "RAD", JSBSim::taHeading);
00527 
00528     element = trimCfg->FindElement("gamma");
00529     _gamma = fgic->GetFlightPathAngleRadIC();
00530     if (element)
00531       if (element->GetNumDataLines() > 0) _gamma = element->GetDataAsNumber();
00532 
00533     element = trimCfg->FindElement("nlf");
00534     if (element) {
00535       if (element->GetNumDataLines() > 0) _targetNlf = element->GetDataAsNumber();
00536       CalculatePhiWFromTargetNlfTurn(_targetNlf);
00537     }
00538 
00539     element = trimCfg->FindElement("throttle_cmd");
00540     if (element) InitializeTrimControl(0, element, "", JSBSim::taThrottle);
00541 
00542     element = trimCfg->FindElement("elevator_cmd");
00543     if (element) InitializeTrimControl(0, element, "", JSBSim::taElevator);
00544 
00545     element = trimCfg->FindElement("rudder_cmd");
00546     if (element) InitializeTrimControl(0, element, "", JSBSim::taRudder);
00547 
00548     element = trimCfg->FindElement("aileron_cmd");
00549     if (element) InitializeTrimControl(0, element, "", JSBSim::taAileron);
00550 
00551     output_element = trimCfg->FindElement("output_file");
00552     if (output_element) {
00553         rf_name = output_element->GetAttributeValue("name");
00554         if (rf_name.empty()) {
00555             cerr << "name must be specified in output_file \"name\" attribute."<< endl;
00556         } else {
00557             if ( !SetResultsFile(rf_name) )
00558                 cerr << "Unable to use output file "<< rf_name << endl;
00559         }
00560     }
00561     return true;
00562 }
00563 
00564 
00565 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00566 
00567 bool FGTrimAnalysis::InitializeTrimControl(double default_value, Element* el,
00568                                            string unit, TaControl type)
00569 {
00570   Element *step_size_element=0, *trim_config=0;
00571   double iv = 0.0;
00572   double step = 0.0; // default step value
00573   bool set_override = false;
00574 
00575   iv = default_value;
00576 
00577   if (el != 0) {
00578     string name = el->GetName();
00579     trim_config = el->GetParent();
00580     set_override = el->GetNumDataLines() != 0;
00581     if (set_override) {
00582       if (unit.empty()) iv = trim_config->FindElementValueAsNumber(name);
00583       else              iv = trim_config->FindElementValueAsNumberConvertTo(name, unit);
00584     }
00585     if (el->GetAttributeValueAsNumber("step_size") != HUGE_VAL)
00586       step = el->GetAttributeValueAsNumber("step_size");
00587   }
00588 
00589   for (unsigned int i=0; i<vTrimAnalysisControls.size(); i++) {
00590     if (vTrimAnalysisControls[i]->GetControlType() == type) {
00591       vTrimAnalysisControls[i]->SetControlInitialValue(iv);
00592       vTrimAnalysisControls[i]->SetControlStep(step);
00593       break;
00594     }
00595   }
00596 
00597   return set_override;
00598 }
00599 
00600 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00601 
00602 void FGTrimAnalysis::TrimStats() {
00603   int run_sum=0;
00604   cout << endl << "  Trim Statistics: " << endl;
00605   cout << "    Total Iterations: " << total_its << endl;
00606 }
00607 
00608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00609 
00610 void FGTrimAnalysis::Report(void) {
00611 
00612     cout << "---------------------------------------------------------------------\n";
00613 
00614     cout << "Trim report: " << endl;
00615       cout << "\tTrim algorithm terminated with the following values:" << endl;
00616       cout << "\tu, v, w        (ft/s): " << _u <<", "<< _v <<", "<< _w << endl
00617            << "\tp, q, r       (rad/s): " << _p <<", "<< _q <<", "<< _r << endl
00618            << "\talpha, beta     (deg): " << _alpha*57.3 <<", "<< _beta*57.3 << endl
00619            << "\tphi, theta, psi (deg): " << _phi*57.3 <<", "<< _theta*57.3 << ", " << _psi*57.3 << endl
00620            << "\tCost function value  : " << cost_function_value << endl
00621            << "\tCycles executed      : " << total_its << endl << endl;
00622 
00623       cout << "\tTrim variables adjusted:" << endl;
00624       for (unsigned int i=0; i<vTrimAnalysisControls.size();i++){
00625           cout << "\t\t" << vTrimAnalysisControls[i]->GetControlName() <<": ";
00626           cout << vTrimAnalysisControls[i]->GetControl() << endl;
00627       }
00628       //...
00629 
00630       cout << endl;
00631 
00632       cout << "\t** Initial -> Final Conditions **" << endl;
00633       cout << "\tAlpha IC: " << fgic->GetAlphaDegIC() << " Degrees" << endl;
00634       cout << "\t   Final: " << Auxiliary->Getalpha()*57.3 << " Degrees" << endl;
00635       cout << "\tBeta  IC: " << fgic->GetBetaDegIC() << " Degrees" << endl;
00636       cout << "\t   Final: " << Auxiliary->Getbeta()*57.3 << " Degrees" << endl;
00637       cout << "\tGamma IC: " << fgic->GetFlightPathAngleDegIC() << " Degrees" << endl;
00638       cout << "\t   Final: " << Auxiliary->GetGamma()*57.3 << " Degrees" << endl;
00639       cout << "\tPhi IC  : " << fgic->GetPhiDegIC() << " Degrees" << endl;
00640       cout << "\t   Final: " << fdmex->GetPropagate()->GetEuler(1)*57.3 << " Degrees" << endl;
00641       cout << "\tTheta IC: " << fgic->GetThetaDegIC() << " Degrees" << endl;
00642       cout << "\t   Final: " << fdmex->GetPropagate()->GetEuler(2)*57.3 << " Degrees" << endl;
00643       cout << "\tPsi IC  : " << fgic->GetPsiDegIC() << " Degrees" << endl;
00644       cout << "\t   Final: " << fdmex->GetPropagate()->GetEuler(3)*57.3 << " Degrees" << endl;
00645       cout << endl;
00646       cout << "--------------------------------------------------------------------- \n\n";
00647 
00648       fdmex->EnableOutput();
00649 }
00650 
00651 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00652 
00653 void FGTrimAnalysis::ClearControls(void) {
00654 
00655     FGTrimAnalysisControl* tac;
00656 
00657     mode=taCustom;
00658     vector<FGTrimAnalysisControl*>::iterator iControls;
00659     iControls = vTrimAnalysisControls.begin();
00660     while (iControls != vTrimAnalysisControls.end()) {
00661       tac=*iControls;
00662       delete tac;
00663 
00664       iControls++;
00665     }
00666 
00667     vTrimAnalysisControls.clear();
00668 
00669 }
00670 
00671 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00672 
00673 bool FGTrimAnalysis::AddControl( TaControl control ) {
00674 
00675   FGTrimAnalysisControl* tac;
00676   bool result=true;
00677 
00678   mode = taCustom;
00679   vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin();
00680   while (iControls != vTrimAnalysisControls.end()) {
00681       tac=*iControls;
00682       if( tac->GetControlType() == control )
00683         result=false;
00684       iControls++;
00685   }
00686   if(result) {
00687     vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,control));
00688   }
00689   return result;
00690 }
00691 
00692 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00693 
00694 bool FGTrimAnalysis::RemoveControl( TaControl control ) {
00695 
00696   FGTrimAnalysisControl* tac;
00697   bool result=false;
00698 
00699   mode = taCustom;
00700   vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin();
00701   while (iControls != vTrimAnalysisControls.end()) {
00702       tac=*iControls;
00703       if( tac->GetControlType() == control ) {
00704         delete tac;
00705         vTrimAnalysisControls.erase(iControls);
00706         result=true;
00707         continue;
00708       }
00709       iControls++;
00710   }
00711 
00712   return result;
00713 }
00714 
00715 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00716 
00717 bool FGTrimAnalysis::EditState( TaControl new_control, double new_initvalue, double new_step, double new_min, double new_max ) {
00718   FGTrimAnalysisControl* tac;
00719   bool result=false;
00720 
00721   mode = taCustom;
00722   vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin();
00723   while (iControls != vTrimAnalysisControls.end()) {
00724       tac=*iControls;
00725       if( tac->GetControlType() == new_control ) {
00726         vTrimAnalysisControls.insert(iControls,1,new FGTrimAnalysisControl(fdmex,fgic,new_control));
00727         delete tac;
00728         vTrimAnalysisControls.erase(iControls+1);
00729         result=true;
00730         break;
00731       }
00732       iControls++;
00733   }
00734   return result;
00735 }
00736 
00737 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00738 
00739 void FGTrimAnalysis::setupPullup() {
00740   double g,q,cgamma;
00741   g=fdmex->GetInertial()->gravity();
00742   cgamma=cos(fgic->GetFlightPathAngleRadIC());
00743   q=g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC();
00744   cout << _targetNlf << ", " << q << endl;
00745   fgic->SetQRadpsIC(q);
00746   updateRates();
00747 
00748 }
00749 
00750 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00751 
00752 void FGTrimAnalysis::CalculatePhiWFromTargetNlfTurn(double nlf){
00753   if ( ( mode == taTurn ) || ( mode == taTurnFull ) ) {
00754      // target Nlf is given
00755      // set _phiW according to given Nlf
00756      _targetNlf = nlf;
00757      _phiW = atan2( sqrt(_targetNlf*_targetNlf-cos(_gamma)*cos(_gamma)), cos(_gamma) );
00758   }
00759 }
00760 
00761 //ToDo: check formulas for nonzero gamma
00762 void FGTrimAnalysis::setupTurn(void){
00763 
00764   if ( mode == taTurn ) {
00765      // target Nlf is given
00766      // set _phiW according to given Nlf
00767      _phiW = atan2( sqrt(_targetNlf*_targetNlf-cos(_gamma)*cos(_gamma)), cos(_gamma) );
00768      C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
00769                    sin(_phiW)            *cos(_theta)*cos(_psi)    ;
00770      C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
00771                   cos(_phiW)*sin(_gamma)*sin(_theta)              ;
00772      C3 = sin(_phiW)            *sin(_theta)+
00773                   cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi)    ;
00774      _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
00775                     /( sqrt(C1*C1 + C2*C2 + C3*C3) );
00776      _sbeta = sqrt(1.-_cbeta*_cbeta );
00777      _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
00778      _phi = asin(_sphi);
00779 
00780      // theta_underlined (see paper on Trim) for coordinated turn
00781      _theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) );
00782 
00783       double g,V;
00784       V = sqrt( _u*_u +_v*_v + _w*_w );
00785       g=fdmex->GetInertial()->gravity();
00786       _psiWdot = (g/V)*tan(_phiW);
00787       updateRates();
00788   }
00789   if ( mode == taTurnFull ) {
00790      // target Nlf is given
00791      C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
00792                    sin(_phiW)            *cos(_theta)*cos(_psi)    ;
00793      C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
00794                   cos(_phiW)*sin(_gamma)*sin(_theta)              ;
00795      C3 = sin(_phiW)            *sin(_theta)+
00796                   cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi)    ;
00797      _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
00798                     /( sqrt(C1*C1 + C2*C2 + C3*C3) );
00799      _sbeta = sqrt(1.-_cbeta*_cbeta );
00800      _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
00801      _phi = asin(_sphi);
00802 
00803       double g,V;
00804       V = sqrt( _u*_u +_v*_v + _w*_w );
00805       g=fdmex->GetInertial()->gravity();
00806       _psiWdot = (g/V)*tan(_phiW);
00807       updateRates();
00808   }
00809 }
00810 
00811 void FGTrimAnalysis::setupTurn(double phiW){
00812   if ( mode == taTurn ) {
00813      _phiW = phiW;
00814      // recalculate target Nlf
00815      _targetNlf =  sqrt( cos(_gamma)*cos(_gamma)*tan(_phiW)*tan(_phiW) + cos(_gamma)*cos(_gamma) );
00816 
00817      C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
00818                    sin(_phiW)            *cos(_theta)*cos(_psi)    ;
00819      C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
00820                   cos(_phiW)*sin(_gamma)*sin(_theta)              ;
00821      C3 = sin(_phiW)            *sin(_theta)+
00822                   cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi)    ;
00823      _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
00824                     /( sqrt(C1*C1 + C2*C2 + C3*C3) );
00825      _sbeta = sqrt(1.-_cbeta*_cbeta );
00826      _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
00827      _phi = asin(_sphi);
00828 
00829      _cphi = cos(_phi);
00830 
00831      // theta_underlined (see paper on Trim) for coordinated turn
00832      //_theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) );
00833 
00834       double g,V;
00835       V = sqrt( _u*_u +_v*_v + _w*_w );
00836       g=fdmex->GetInertial()->gravity();
00837       _psiWdot = (g/V)*tan(_phiW);
00838       updateRates();
00839   }
00840   if ( mode == taTurnFull ) {
00841      _phiW = phiW;
00842      // recalculate target Nlf
00843      _targetNlf =  sqrt( cos(_gamma)*cos(_gamma)*tan(_phiW)*tan(_phiW) + cos(_gamma)*cos(_gamma) );
00844 
00845      C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
00846                    sin(_phiW)            *cos(_theta)*cos(_psi)    ;
00847      C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
00848                   cos(_phiW)*sin(_gamma)*sin(_theta)              ;
00849      C3 = sin(_phiW)            *sin(_theta)+
00850                   cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi)    ;
00851      _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
00852                     /( sqrt(C1*C1 + C2*C2 + C3*C3) );
00853      _sbeta = sqrt(1.-_cbeta*_cbeta );
00854      _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
00855      _phi = asin(_sphi);
00856 
00857      _cphi = cos(_phi);
00858 
00859      // theta_underlined (see paper on Trim) for coordinated turn
00860      //_theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) );
00861 
00862       double g,V;
00863       V = sqrt( _u*_u +_v*_v + _w*_w );
00864       g=fdmex->GetInertial()->gravity();
00865       _psiWdot = (g/V)*tan(_phiW);
00866       updateRates();
00867   }
00868 
00869 }
00870 
00871 //ToDo: check formulas for nonzero gamma
00872 void FGTrimAnalysis::setupTurnPhi(double psi, double theta){
00873   if ( mode == taTurn ) {
00874      _psi   = psi;   _cpsi   = cos(_psi);   _spsi   = sin(_psi);
00875      _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta);
00876 
00877      C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
00878                    sin(_phiW)            *cos(_theta)*cos(_psi)    ;
00879      C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
00880                   cos(_phiW)*sin(_gamma)*sin(_theta)              ;
00881      C3 = sin(_phiW)            *sin(_theta)+
00882                   cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi)    ;
00883      _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
00884                     /( sqrt(C1*C1 + C2*C2 + C3*C3) );
00885      _sbeta = sqrt(1.-_cbeta*_cbeta );
00886 
00887      _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
00888      _phi = asin(_sphi);
00889      _cphi = cos(_phi);
00890   }
00891   if ( mode == taTurnFull ) {
00892      _psi   = psi;   _cpsi   = cos(_psi);   _spsi   = sin(_psi);
00893      _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta);
00894 
00895      C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
00896                    sin(_phiW)            *cos(_theta)*cos(_psi)    ;
00897      C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
00898                   cos(_phiW)*sin(_gamma)*sin(_theta)              ;
00899      C3 = sin(_phiW)            *sin(_theta)+
00900                   cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi)    ;
00901      _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
00902                     /( sqrt(C1*C1 + C2*C2 + C3*C3) );
00903      _sbeta = sqrt(1.-_cbeta*_cbeta );
00904 
00905      _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
00906      _phi = asin(_sphi);
00907      _cphi = cos(_phi);
00908 
00909   }
00910 }
00911 
00912 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00913 
00914 FGColumnVector3 FGTrimAnalysis::UpdateRatesTurn(double psi, double theta, double phi, double phiW){
00915       _psi = psi;
00916       setupTurn(phiW); // calls updateRates: updates _p,_q,_r
00917       return FGColumnVector3(_p,_q,_r);
00918 }
00919 
00920 FGColumnVector3 FGTrimAnalysis::UpdateRatesPullup(void){
00921       double g,cgamma;
00922       g=fdmex->GetInertial()->gravity();
00923       cgamma=cos(fgic->GetFlightPathAngleRadIC());
00924       _p = 0;
00925       _q = g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC();
00926       _r = 0.;
00927 
00928       fgic->SetQRadpsIC(_q);
00929       return FGColumnVector3(_p,_q,_r);
00930 }
00931 
00932 void FGTrimAnalysis::updateRates(void){
00933   if ( ( mode == taTurn ) || ( mode == taTurnFull ) ){
00934       // this is the result of a Matlab symbolic expression
00935       double cth2 = cos(_theta)*cos(_theta),
00936               sth2 = sin(_theta)*sin(_theta),
00937               scth = sin(_theta)*cos(_theta),
00938               cph2 = cos(_phiW)*cos(_phiW),
00939               sph2 = sin(_phiW)*sin(_phiW),
00940               scph = sin(_phiW)*cos(_phiW),
00941               cga2 = cos(_gamma)*cos(_gamma),
00942               scga = sin(_gamma)*cos(_gamma),
00943               cps2 = cos(_psi)*cos(_psi),
00944               scps = sin(_psi)*cos(_psi);
00945      _calpha =
00946         sqrt(
00947             1-cth2
00948             +cph2*cth2
00949             -2*scph*scth*cos(_gamma)*sin(_psi)+cph2*cga2*cth2
00950             +cph2*cga2*cth2*(1-cps2)
00951             +2*cph2*scga*scth*cos(_psi)
00952             -cga2*cph2
00953             -2*cph2*cth2*cps2
00954             +2*scph*sin(_gamma)*cth2*scps
00955             +cps2*cth2 ),
00956             _salpha = sqrt( 1. - _calpha*_calpha );
00957 
00958      _p = -_psiWdot*
00959           ( sin(_gamma)*_calpha*_cbeta
00960            +cos(_gamma)*sin(_phiW)*_calpha*_sbeta
00961            +cos(_gamma)*cos(_phiW)*_salpha
00962            );
00963 
00964      _q = -_psiWdot*
00965           ( sin(_gamma)*_sbeta
00966            -cos(_gamma)*sin(_phiW)*_cbeta
00967            );
00968 
00969      _r = -_psiWdot*
00970           ( sin(_gamma)*_salpha*_cbeta
00971            +cos(_gamma)*sin(_phiW)*_salpha*_sbeta
00972            -cos(_gamma)*cos(_phiW)*_calpha
00973            );
00974 
00975   } else if( mode == taPullup && fabs(_targetNlf-1) > 0.01) {
00976 
00977       double g,q,cgamma;
00978       g=fdmex->GetInertial()->gravity();
00979       cgamma=cos(fgic->GetFlightPathAngleRadIC());
00980       q=g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC();
00981       _q=q;
00982       fgic->SetQRadpsIC(q);
00983   }
00984 }
00985 
00986 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00987 
00988 void FGTrimAnalysis::setDebug(void) {
00989     Debug=0;
00990     return;
00991 }
00992 
00993 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00994 
00995 void FGTrimAnalysis::SetMode(TrimAnalysisMode tt) {
00996 
00997     ClearControls();
00998 
00999     cout << "---------------------------------------------------------------------" << endl;
01000     cout << "Trim analysis performed: ";
01001     mode=tt;
01002     switch(tt) {
01003       case taLongitudinal:
01004         if (debug_lvl > 0)
01005           cout << "  Longitudinal Trim" << endl;
01006         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
01007         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
01008         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
01009         break;
01010       case taFull:
01011         if (debug_lvl > 0)
01012           cout << "  Full Trim" << endl;
01013         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
01014         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
01015         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron ));  // TODO: taRollTrim
01016         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder ));   // TODO: taYawTrim
01017         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
01018         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
01019         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
01020         break;
01021       case taFullWingsLevel:
01022         if (debug_lvl > 0)
01023           cout << "  Full Trim, Wings-Level" << endl;
01024         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
01025         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
01026         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron ));  // TODO: taRollTrim
01027         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder ));   // TODO: taYawTrim
01028         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
01029         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
01030         break;
01031       case taTurn:
01032           // ToDo: set target NLF here !!!
01033           // ToDo: assign psiDot here !!
01034         if (debug_lvl > 0)
01035           cout << "  Full Trim, Coordinated turn" << endl;
01036         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
01037         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
01038         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron ));  // TODO: taRollTrim
01039         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder ));   // TODO: taYawTrim
01040         //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
01041         //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
01042         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
01043         break;
01044       case taTurnFull:
01045         if (debug_lvl > 0)
01046           cout << "  Non-coordinated Turn Trim" << endl;
01047         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
01048         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
01049         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron ));  // TODO: taRollTrim
01050         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder ));   // TODO: taYawTrim
01051         //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
01052         // calculate this from target nlf
01053         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
01054         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
01055         break;
01056       case taPullup:
01057           // ToDo: set target NLF here !!!
01058           // ToDo: assign qDot here !!
01059         if (debug_lvl > 0)
01060           cout << "  Full Trim, Pullup" << endl;
01061         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
01062         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
01063         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron ));
01064         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder ));
01065         //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
01066         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
01067         //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
01068         break;
01069       case taGround:
01070         if (debug_lvl > 0)
01071           cout << "  Ground Trim" << endl;
01072         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAltAGL ));
01073         vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
01074         //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
01075         break;
01076       case taCustom:
01077         // agodemar...
01078         // ...agodemar
01079       case taNone:
01080         break;
01081     }
01082 
01083     current_ctrl=0;
01084 }
01085 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01086 
01087 bool FGTrimAnalysis::SetResultsFile(string name)
01088 {
01089     if ( rf.is_open() ) return false;
01090 
01091     rf_name = name;
01092     rf.open(rf_name.c_str(), ios::out);
01093     if ( !rf.is_open() ) {
01094        cerr << "Unable to open " << rf_name << endl;
01095        return false;
01096     }
01097     //rf << "# ... complete this " << endl;
01098     //rf << "# iteration, CostFunc, size, dT, dE, dA, dR, Psi (rad), Theta (rad), Phi (rad)\n";
01099     return true;
01100 }
01101 
01102 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01103 
01104 bool FGTrimAnalysis::ensureRunning()
01105 {
01106     bool success = false;
01107 
01108     if ( Propulsion == 0L ) return false;
01109 
01110     for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
01111     {
01112       if (!Propulsion->GetEngine(i)->GetRunning() ) {
01113           Propulsion->GetEngine(i)->SetStarter( true );
01114           if ( Propulsion->GetEngine(i)->GetType() == JSBSim::FGEngine::etPiston )
01115           {
01116               FGPiston * Piston = (FGPiston*)Propulsion->GetEngine(i);
01117               Piston->SetMagnetos(3);
01118           }
01119           else if ( Propulsion->GetEngine(i)->GetType() == FGEngine::etTurbine )
01120           {
01121               FGTurbine * Turbine = (FGTurbine*)Propulsion->GetEngine(i);
01122               Turbine->SetCutoff(false);
01123               Turbine->SetStarter(true);
01124               Turbine->Calculate();
01125           }
01126           Propulsion->GetEngine(i)->SetRunning( true );
01127           Propulsion->Run();
01128       } else {
01129           success = true; // at least one engine is found in a running state
01130           Propulsion->SetActiveEngine(i);
01131       }
01132     }
01133     return success;
01134 }
01135 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01136 
01137 bool FGTrimAnalysis::ensureRunning(unsigned int i)
01138 {
01139     bool success = false;
01140 
01141     if ( Propulsion == 0L ) return false;
01142 
01143     if ( i < Propulsion->GetNumEngines() )
01144     {
01145       if (!Propulsion->GetEngine(i)->GetRunning() ) {
01146           Propulsion->GetEngine(i)->SetStarter( true );
01147           if ( Propulsion->GetEngine(i)->GetType() == JSBSim::FGEngine::etPiston )
01148           {
01149               FGPiston * Piston = (FGPiston*)Propulsion->GetEngine(i);
01150               Piston->SetMagnetos(3);
01151           }
01152           else if ( Propulsion->GetEngine(i)->GetType() == FGEngine::etTurbine )
01153           {
01154               FGTurbine * Turbine = (FGTurbine*)Propulsion->GetEngine(i);
01155               Turbine->SetCutoff(false);
01156               Turbine->SetStarter(true);
01157               Turbine->Calculate();
01158           }
01159           Propulsion->GetEngine(i)->SetRunning( true );
01160           Propulsion->Run();
01161 
01162       } else {
01163           success = true; // at least one engine is found in a running state
01164           Propulsion->SetActiveEngine(i);
01165       }
01166     }
01167     else success=false; // i not in range
01168 
01169     return success;
01170 }
01171 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01172 
01173 bool FGTrimAnalysis::runForAWhile(int nruns)
01174 {
01175   bool result = fdmex->Run();  // MAKE AN INITIAL RUN
01176 
01177   int counter = 0;
01178 
01179   // *** CYCLIC EXECUTION LOOP, AND MESSAGE READING *** //
01180   while ( counter < nruns ) { // (result && (counter < nruns))
01181     counter++;
01182     result = fdmex->Run();
01183   }
01184   return result;
01185 }
01186 
01187 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01188 bool FGTrimAnalysis::populateVecAlphaDeg(double vmin, double vmax, int n)
01189 {
01190     if ( !vAlphaDeg.empty() ) return false;
01191     for(int i=0; i<n; i++)
01192           vAlphaDeg.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) );
01193     return true;
01194 }
01195 
01196 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01197 bool FGTrimAnalysis::populateVecThrottleCmd(double vmin, double vmax, int n)
01198 {
01199     if ( !vThrottleCmd.empty() ) return false;
01200     for(int i=0; i<n; i++)
01201           vThrottleCmd.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) );
01202     return true;
01203 }
01204 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01205 bool FGTrimAnalysis::populateVecElevatorCmd(double vmin, double vmax, int n)
01206 {
01207     if ( !vElevatorCmd.empty() ) return false;
01208     for(int i=0; i<n; i++)
01209           vElevatorCmd.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) );
01210     return true;
01211 }
01212 
01213 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01214 bool FGTrimAnalysis::calculateAerodynamics(
01215                            double dE_cmd,
01216                            double Vt,
01217                            double alpha_deg,
01218                            double altitude,
01219                            double rho,
01220                            double S, double mac, double bw,
01221                            double& CL, double& CD, double& Cm)
01222 {
01223     double qBar   = 0.5 * rho * Vt*Vt;
01224     double qBar_S = qBar * S;
01225 
01226     Auxiliary->SetVt( Vt );
01227     Auxiliary->Setalpha( alpha_deg*degtorad ); // ! note these are DEC converted to RAD!!
01228 
01229     Auxiliary->Setbeta( 0.0 );
01230 
01231     // ensure zero rates
01232     Auxiliary->Setadot( 0.0 );
01233     Auxiliary->Setbdot( 0.0 );
01234     Auxiliary->SetAeroPQR( FGColumnVector3(0.,0.,0.) ) ; // note assumes that trim_mode is triggered
01235     // note: do not Auxiliary->Run(), otherwise dotted values
01236     //       _and_ aerodynamic angles are recalculated !!!
01237 
01238     // ensure the desired dE before aerodynamics
01239     FCS->SetDeCmd( dE_cmd );
01240     FCS->Run();
01241 
01242     Aerodynamics->Run();
01243 
01244     // get the Aerodynamics internat data
01245     vector <FGFunction*> * Coeff = Aerodynamics->GetAeroFunctions();
01246 
01247     if ( Coeff->empty() ) return false;
01248 
01249     CL = 0.;
01250     unsigned int axis_ctr = 2; // means LIFT, 0=DRAG, 1=SIDE, 3,4,5=ROLL,PITCH,YAW
01251     for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++)
01252       CL += Coeff[axis_ctr][ctr]->GetValue();
01253     CL /= qBar_S;
01254 
01255     CD = 0.;
01256     axis_ctr = 0; // means DRAG
01257     for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++)
01258       CD += Coeff[axis_ctr][ctr]->GetValue();
01259     CD /= qBar_S;
01260 
01261     Cm=0.;
01262     axis_ctr = 4; // means PITCH 0=DRAG, 1=SIDE, 2=LIFT, 3=ROLL, 4=PITCH, 5=YAW
01263     for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++)
01264       Cm += Coeff[axis_ctr][ctr]->GetValue();
01265 
01266     Cm /= ( qBar_S*mac );
01267 
01268     return true;
01269 
01270 }
01271 
01272 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01273 
01274 bool FGTrimAnalysis::DoTrim(void) {
01275 
01276     // retrieve initial conditions
01277     fdmex->RunIC();
01278 
01279     cout << endl << "Numerical trim algorithm: constrained optimization of a cost function" << endl;
01280 
01281     Objective* obj_ptr = new Objective(this->fdmex, this, 999.0);
01282 
01283     fdmex->SetTrimStatus( true );
01284 
01285     //###################################
01286     // run for a while
01287     //###################################
01288 
01289     double tMin,tMax;
01290     double throttle = 1.0;
01291     for (unsigned i=0;i<Propulsion->GetNumEngines();i++)
01292     {
01293         tMin=Propulsion->GetEngine(i)->GetThrottleMin();
01294         tMax=Propulsion->GetEngine(i)->GetThrottleMax();
01295         FCS->SetThrottleCmd(i,tMin + throttle *(tMax-tMin));
01296         if (Propulsion->GetEngine(i)->GetType()==FGEngine::etPiston)
01297         {
01298             FCS->SetMixtureCmd(i,0.87);
01299         }
01300         if (Propulsion->GetEngine(i)->GetType()==FGEngine::etTurbine)
01301         {
01302             ((FGTurbine*)Propulsion->GetEngine(i))->SetCutoff(false);
01303             ((FGTurbine*)Propulsion->GetEngine(i))->SetPhase(FGTurbine::tpRun); // tpStart
01304         }
01305 
01306         FCS->Run(); // apply throttle change
01307     }
01308     Propulsion->GetSteadyState(); // GetSteadyState processes all engines
01309 
01310     //--------------------------------------------------------
01311 
01312     for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
01313     {
01314       int engineStartCount = 0;
01315       bool engine_started = false;
01316 
01317       int n_attempts = 100;
01318       if (Propulsion->GetEngine(i)->GetType()==FGEngine::etTurbine)
01319           n_attempts = 5000; // a turbine engine, e.g. 737's cfm56, takes a little longer to get running
01320 
01321       while ( !engine_started && (engineStartCount < n_attempts) )
01322       {
01323           engine_started = ensureRunning(i);
01324           fdmex->Run();
01325           engineStartCount++;
01326       }
01327 
01328     }
01329 
01330     //--------------------------------------------------------
01331 
01332     Propulsion->GetSteadyState();
01333 
01334     fdmex->SetDebugLevel(0); // 4
01335 
01336     //#########################################################################
01337 
01338     trim_failed=false;
01339 
01340     for(int i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++){
01341         fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(false);
01342     }
01343 
01344     fdmex->DisableOutput();
01345 
01346     fgic->SetPRadpsIC(0.0);
01347     fgic->SetQRadpsIC(0.0);
01348     fgic->SetRRadpsIC(0.0);
01349 
01350     if(mode == taPullup ) {
01351         setupPullup(); // also calls updateRates()
01352     } else if (mode == taTurn) {
01353         setupTurn(); // also calls updateRates()
01354     } else if (mode == taTurnFull) {
01355         setupTurn(); // also calls updateRates()
01356     } else {
01357         fgic->SetPRadpsIC(0.0);
01358         fgic->SetQRadpsIC(0.0);
01359         fgic->SetRRadpsIC(0.0);
01360         _p = _q = _r = 0.;
01361     }
01362 
01363   // ** DO HERE THE TRIM ** //
01364 
01365   //---------------------------------------------------------------------------
01366   // REMINDER:
01367   // n. of control variables for full trim (taFull): 7
01368   // ordering: the four commands first, then the three Euler angles,
01369   // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
01370   // [4] phi, [5] theta, [6] psi (alias taHeading)
01371   //-----------------------------------------------------------------------------------------------------------------
01372 
01373   // re-run ICs
01374   fdmex->RunIC();
01375 
01376   // write trim results on file, rf=results file
01377   if ( rf.is_open() )
01378     rf <<
01379       "# iteration, costf, dT, dE, dA, dR, Phi (rad), Theta (rad), Psi (rad), uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2), u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad), alphaDot (rad/s), betaDot (rad/s), Thrust"
01380       << endl;
01381 
01382   long n = vTrimAnalysisControls.size();  // number of variables (dimension of the search)
01383 
01384   /* we'll initialize an n-entry Vector whose value is startVal,
01385    * and use it as our starting point.
01386    */
01387 
01388   // a Vec, needed in the search constructor
01389   Vector<double>* minVec = new Vector<double>(n, 0.0);
01390 
01391   // a Vec needed to store the minimum point later
01392   Vector<double>* Sminimum;
01393   new_Vector_Init(Sminimum,*minVec);
01394 
01395   /*
01396    * now construct the search object:
01397    * NMS = Nelder-Mead Search algorithm;
01398    *       if n parameters have to be adjusted, then user has to provide
01399    *       n+1 initial combinations where the cost function is going to be sampled.
01400    *       The class has the capability of automatically initialize those points,
01401    *       but here I prefer giving them explicitly so that the initial steps in
01402    *       all parameter "directions" are well defined.
01403    */
01404 
01405   NMSearch NMS(
01406           n, *minVec, /* minVec, */
01407           sigma_nm, alpha_nm, beta_nm, gamma_nm,
01408           initial_step, tolerance,
01409           //find_CostFunctionFull, // gets the proper member function from the
01410                                    // object of class Objective (see main_obj.cc from DirectSearch examples)
01411           0L, // gets the proper member function from the
01412           (void *)obj_ptr
01413           );
01414 
01415   if ( GetMode()==taLongitudinal ) {
01416       NMS.SetFcnName(find_CostFunctionLongitudinal);
01417   }
01418   if ( GetMode()==taFull ) {
01419       NMS.SetFcnName(find_CostFunctionFull);
01420   }
01421   if ( GetMode()==taFullWingsLevel ) {
01422       NMS.SetFcnName(find_CostFunctionFullWingsLevel);
01423   }
01424   if ( GetMode()==taTurn ) {
01425       NMS.SetFcnName(find_CostFunctionFullCoordinatedTurn);
01426   }
01427   if ( GetMode()==taTurnFull ) {
01428       NMS.SetFcnName(find_CostFunctionFullTurn);
01429   }
01430   if ( GetMode()==taPullup ) {
01431       NMS.SetFcnName(find_CostFunctionPullUp);
01432   }
01433 
01434   //-----------------------------------------
01435   // initialize simplex (n+1 conditions)
01436   //-----------------------------------------
01437   // InitGeneralSimplex(plex) where...
01438   //    Matrix<double> *plex = NULL;
01439   //    new_Matrix(plex, n+1,n); // create one more row, see Dyn_alloc.h
01440   // ...
01441 
01442   //------------------------------------------------------------------------------
01443   // In this stringstream we I put the n+1 sequences, each of n values (n-ples)
01444   // The stream is then used to initialize the simplex by a call to the method
01445   // ReadInFile
01446   //------------------------------------------------------------------------------
01447   stringstream ss (stringstream::in | stringstream::out);
01448 
01449   // first feed the trial minimizer, zeroth point of the simplex
01450   for (unsigned int k=0; k<vTrimAnalysisControls.size();k++)
01451       ss << vTrimAnalysisControls[k]->GetControlInitialValue() << " ";
01452 
01453   // then the rest of n-ples
01454   for (unsigned int k=0; k<vTrimAnalysisControls.size();k++)
01455   {
01456     for(vector<FGTrimAnalysisControl*>::iterator vi=vTrimAnalysisControls.begin();
01457         vi!=vTrimAnalysisControls.end();vi++) {
01458             if ( (*vi)->GetControlType()==vTrimAnalysisControls[k]->GetControlType() )
01459                 ss << (*vi)->GetControlInitialValue() + (*vi)->GetControlStep() << " ";
01460             else
01461                 ss << (*vi)->GetControlInitialValue() << " ";
01462     }// this complets the k-th n-ple
01463   }// this completes the (N+1) n-ples
01464 
01465   NMS.ReadInFile(ss); // assign the initial combinations of parameters
01466 
01467   /*
01468   If Stop_on_std is set to false in an NMSearch, the standard-deviation test is used until that test is met.
01469   Then (and in subsequent iterations of the same search) the more expensive ``delta'' test will be used.
01470   We realize that there will be times when the standard deviation test will actually take more function calls than
01471   the ``delta'' test, but this is not typically the case, and we make this compromise for the sake of efficiency.
01472   */
01473 
01474   NMS.SetMaxCalls(max_iterations);
01475   //NMS.SetMaxCallsExact(20000);
01476   if ( stop_criterion=="Stop-On-Std" )
01477     NMS.Set_Stop_on_std();
01478   if ( stop_criterion=="Stop-On-Delta" )
01479     NMS.Set_Stop_on_delta();
01480 
01481   double SMinVal;
01482   long Scalls;
01483 
01484 //  NMS.PrintDesign();
01485 
01486   fdmex->SetDebugLevel(0);
01487 
01488   /* start searching */
01489   NMS.BeginSearch();    // stops when the simplex shrinks to a point
01490 
01491   // WATCH this !!
01492   delete obj_ptr;
01493 
01494   /* recover information about the search */
01495   NMS.GetMinPoint(*Sminimum);
01496   NMS.GetMinVal(SMinVal);
01497   Scalls = NMS.GetFunctionCalls();
01498 
01499   // Apply the set of controls found by the minimization procedure
01500 
01501   if ( ( mode == taFull ) ||
01502        ( mode == taTurnFull ) )
01503   {
01504       FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
01505       FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons
01506       FCS->SetDrCmd( (*Sminimum)[3] ); // rudder
01507       double tMin,tMax;
01508       for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
01509       {
01510           tMin=Propulsion->GetEngine(i)->GetThrottleMin();
01511           tMax=Propulsion->GetEngine(i)->GetThrottleMax();
01512           FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
01513           FCS->Run(); // apply throttle change
01514       }
01515       Propulsion->GetSteadyState(); // GetSteadyState processes all engines
01516       FCS->Run(); // apply throttle, yoke & pedal changes
01517 
01518       FGQuaternion quat( (*Sminimum)[4], (*Sminimum)[5], (*Sminimum)[6] ); // phi, theta, psi
01519       quat.Normalize();
01520 
01521       fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
01522 
01523       FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
01524       vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
01525       fdmex->GetPropagate()->SetVState(vstate);
01526       Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
01527       Auxiliary->Setbeta ( _beta  );
01528 
01529       // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
01530       /*      */
01531       //fdmex->RunIC();
01532       fdmex->Run();
01533 
01534   }  // end taFull
01535 
01536   if ( mode == taLongitudinal )
01537   {
01538       FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
01539       double tMin,tMax;
01540       for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
01541       {
01542           tMin=Propulsion->GetEngine(i)->GetThrottleMin();
01543           tMax=Propulsion->GetEngine(i)->GetThrottleMax();
01544           FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
01545           FCS->Run(); // apply throttle change
01546       }
01547       Propulsion->GetSteadyState(); // GetSteadyState processes all engines
01548       FCS->Run(); // apply throttle, yoke & pedal changes
01549 
01550       FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi
01551       quat.Normalize();
01552 
01553       // enforce the current state to IC object
01554 
01555       fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
01556 
01557       // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
01558       //       which means that we populate IC private variables with our set of values
01559       //fdmex->RunIC();
01560 
01561       FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
01562       vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
01563       fdmex->GetPropagate()->SetVState(vstate);
01564       Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
01565       Auxiliary->Setbeta ( _beta  );
01566 
01567       fdmex->Run();
01568 
01569   }  // end taLongitudinal
01570 
01571   if ( mode == taFullWingsLevel)
01572   {
01573       FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
01574       FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons
01575       FCS->SetDrCmd( (*Sminimum)[3] ); // rudder
01576 
01577       double tMin,tMax;
01578       for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
01579       {
01580           tMin=Propulsion->GetEngine(i)->GetThrottleMin();
01581           tMax=Propulsion->GetEngine(i)->GetThrottleMax();
01582           FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
01583           FCS->Run(); // apply throttle change
01584       }
01585       Propulsion->GetSteadyState(); // GetSteadyState processes all engines
01586       FCS->Run(); // apply throttle, yoke & pedal changes
01587 
01588       FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi
01589       quat.Normalize();
01590 
01591       //...
01592       // enforce the current state to IC object
01593 
01594       fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
01595 
01596       // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
01597       //       which means that we populate IC private variables with our set of values
01598       //fdmex->RunIC();
01599 
01600       FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
01601       vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
01602       fdmex->GetPropagate()->SetVState(vstate);
01603       Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
01604       Auxiliary->Setbeta ( _beta  );
01605 
01606       fdmex->Run();
01607   }// end taFullWingsLevel
01608 
01609   if ( mode == taTurn )
01610   {
01611       FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
01612       FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons
01613       FCS->SetDrCmd( (*Sminimum)[3] ); // rudder
01614 
01615       double tMin,tMax;
01616       for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
01617       {
01618           tMin=Propulsion->GetEngine(i)->GetThrottleMin();
01619           tMax=Propulsion->GetEngine(i)->GetThrottleMax();
01620           FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
01621           FCS->Run(); // apply throttle change
01622       }
01623       Propulsion->GetSteadyState(); // GetSteadyState processes all engines
01624       FCS->Run(); // apply throttle, yoke & pedal changes
01625 
01626       FGQuaternion quat( fgic->GetPhiRadIC(), (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi
01627       quat.Normalize();
01628 
01629       //...
01630       // enforce the current state to IC object
01631 
01632       fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
01633 
01634       // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
01635       //       which means that we populate IC private variables with our set of values
01636       //fdmex->RunIC();
01637 
01638       FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
01639       vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
01640       fdmex->GetPropagate()->SetVState(vstate);
01641       Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
01642       Auxiliary->Setbeta ( 0.0  );
01643       Auxiliary->SetGamma( fgic->GetFlightPathAngleRadIC() );
01644 
01645       fdmex->Run();
01646   }// end ta turn
01647 
01648   if ( mode == taPullup )
01649   {
01650       FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
01651       FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons
01652       FCS->SetDrCmd( (*Sminimum)[3] ); // rudder
01653 
01654       double tMin,tMax;
01655       for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
01656       {
01657           tMin=Propulsion->GetEngine(i)->GetThrottleMin();
01658           tMax=Propulsion->GetEngine(i)->GetThrottleMax();
01659           FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
01660           FCS->Run(); // apply throttle change
01661       }
01662       Propulsion->GetSteadyState(); // GetSteadyState processes all engines
01663       FCS->Run(); // apply throttle, yoke & pedal changes
01664 
01665       FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPRadpsIC() ); // phi, theta, psi
01666       quat.Normalize();
01667 
01668       //...
01669       // enforce the current state to IC object
01670 
01671       fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
01672 
01673       // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
01674       //       which means that we populate IC private variables with our set of values
01675       //fdmex->RunIC();
01676 
01677       FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
01678       vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
01679       fdmex->GetPropagate()->SetVState(vstate);
01680       Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
01681       Auxiliary->Setbeta ( 0.0  );
01682       Auxiliary->SetGamma( fgic->GetFlightPathAngleRadIC() );
01683 
01684       fdmex->Run();
01685   }//end taPullup
01686 
01687   delete minVec;
01688   delete Sminimum;
01689 
01690   //-----------------------------------------------------------------------------------------------------------------
01691 
01692   total_its = NMS.GetFunctionCalls();
01693 
01694   if( !trim_failed ) {
01695     if (debug_lvl > 0) {
01696       cout << endl << "  Trim successful. (Cost function value: " << cost_function_value << ")" << endl;
01697     }
01698   } else {
01699     if (debug_lvl > 0)
01700         cout << endl << "  Trim failed" << endl;
01701   }
01702 
01703 
01704   for (unsigned int i=0; i<(unsigned int)fdmex->GetGroundReactions()->GetNumGearUnits();i++){
01705     fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(true);
01706   }
01707 
01708   return !trim_failed;
01709 }
01710 
01711 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01712 
01713 double Objective::myCostFunctionFull(Vector<double> & x) // x variations come from the search algorithm
01714 {
01715 
01716     //----------------------------------------------------------------------------
01717     // NOTE:
01718     // when the program flow arrives here, it is assumed that a successful
01719     // instantiation of a TrimAnalysis object took place with a mode set
01720     // to taFull, i.e. 7 independent variables to be adjusted
01721     //----------------------------------------------------------------------------
01722 
01723     //----------------------------------------------------------------------------
01724     // NOTE:
01725     // rely on the fact that IC quantities have been fed at least once into
01726     // their correspondent data structure in TrimAnalysis->fdmex tree
01727     //----------------------------------------------------------------------------
01728 
01729     double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
01730 
01731     double theta, phi, psi;
01732 
01733     double alpha = 0.0, beta = 0.0, gamma = 0.0;
01734 
01735     FGQuaternion Quat;
01736     FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
01737 
01738     FGPropagate::VehicleState VState;
01739     VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
01740     VState.vUVW      = FGColumnVector3(0.0,0.0,0.0);
01741     VState.vPQR      = FGColumnVector3(0.0,0.0,0.0);
01742     VState.vQtrn     = FGQuaternion   (0.0,0.0,0.0);
01743 
01744     //----------------------------------------------------------------------------
01745     // Minimization control vector, i.e. independent variables
01746     //----------------------------------------------------------------------------
01747 
01748     // CHECK the trim mode
01749 
01750     //-----------------------------------------------------------------------------------------------------------------
01751     // REMINDER:
01752     // n. of control variables for full trim (taFull): 7
01753     // ordering: the four commands first, then the three Euler angles,
01754     // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
01755     // [4] phi, [5] theta, [6] psi (alias taHeading)
01756     //-----------------------------------------------------------------------------------------------------------------
01757 
01758     delta_cmd_T = x[0]; // throttle, cmd
01759     delta_cmd_E = x[1]; // elevator, cmd
01760     delta_cmd_A = x[2]; // aileron,  cmd
01761     delta_cmd_R = x[3]; // rudder,   cmd
01762 
01763     phi   = x[4]; // rad
01764     theta = x[5];
01765     psi   = x[6];
01766 
01767     TrimAnalysis->SetEulerAngles(phi, theta, psi);
01768 
01769     //----------------------------------------------------------------------------
01770     // parameter bound check
01771     // ToDo: manage bounds via the FGTrimAnalysisControl class
01772     //----------------------------------------------------------------------------
01773     bool penalty =  ( (delta_cmd_T <   0) || (delta_cmd_T >  1) )
01774                  || ( (delta_cmd_E <  -1) || (delta_cmd_E >  1) )
01775                  || ( (delta_cmd_A <  -1) || (delta_cmd_A >  1) )
01776                  || ( (delta_cmd_R <  -1) || (delta_cmd_R >  1) )
01777                  || ( (psi   <    0.0     ) || (psi   >  2.0*M_PI) )
01778                  || ( (theta <   -0.5*M_PI) || (theta >  0.5*M_PI) )
01779                  || ( (phi   <   -    M_PI) || (phi   >      M_PI) )
01780                  ;
01781 
01782     if ( penalty ) {
01783         //-------------------------------------------------
01784         // just return an "infinite" value
01785         return HUGE_VAL;
01786         //-------------------------------------------------
01787     } else {calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
01788                               phi, theta, psi,
01789                               TrimAnalysis->GetMode(),
01790                               alpha, beta, gamma,
01791                               VState,
01792                               vUVWdot, vPQRdot );
01793     }
01794 
01795     double u   , v   , w   ,
01796            p   , q   , r   ,
01797            uDot, vDot, wDot,
01798            pDot, qDot, rDot;
01799     u     = VState.vUVW   (1);  v     = VState.vUVW   (2);  w     = VState.vUVW   (3);
01800     p     = VState.vPQR   (1);  q     = VState.vPQR   (2);  r     = VState.vPQR   (3);
01801     uDot  =        vUVWdot(1);  vDot  =        vUVWdot(2);  wDot  =        vUVWdot(3);
01802     pDot  =        vPQRdot(1);  qDot  =        vPQRdot(2);  rDot  =        vPQRdot(3);
01803 
01804     double
01805     f = 1.000*uDot*uDot
01806       +       vDot*vDot
01807       + 1.000*wDot*wDot
01808       + 0.010*pDot*pDot
01809       + 0.010*qDot*qDot
01810       + 0.010*rDot*rDot;
01811 
01812     static int count = 0;
01813     count++;
01814 
01815     if ( f < TrimAnalysis->GetCostFunctionValue() )
01816     {
01817         // feed into the vector of TrimAnalysis Controls the current values
01818         vector<FGTrimAnalysisControl*>::iterator vi;
01819         for(vi=TrimAnalysis->GetControls()->begin();
01820             vi!=TrimAnalysis->GetControls()->end();vi++)
01821         {
01822             if ( (*vi)->GetControlType()==JSBSim::taThrottle  ) (*vi)->SetControl(delta_cmd_T);
01823             if ( (*vi)->GetControlType()==JSBSim::taElevator  ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim
01824             if ( (*vi)->GetControlType()==JSBSim::taAileron   ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim
01825             if ( (*vi)->GetControlType()==JSBSim::taRudder    ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim
01826             if ( (*vi)->GetControlType()==JSBSim::taPhi       ) (*vi)->SetControl(phi);
01827             if ( (*vi)->GetControlType()==JSBSim::taTheta     ) (*vi)->SetControl(theta);
01828             if ( (*vi)->GetControlType()==JSBSim::taHeading   ) (*vi)->SetControl(psi);
01829         }
01830 
01831         if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
01832 
01833         // write on file, if open
01834 
01835         // "# iteration, costf,
01836         //    dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
01837         //    uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
01838         //    u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
01839         //    alphaDot (rad/s), betaDot (rad/s), Thrust"
01840 
01841         ofstream* rfp = TrimAnalysis->GetResultsFile();
01842         if (rfp)
01843             (*rfp)
01844             << count <<", "<< f <<", "                              // 1, 2
01845             << delta_cmd_T <<", "<< delta_cmd_E <<", "<< delta_cmd_A <<", "<< delta_cmd_R <<", "
01846                                                                     // 3, 4, 5, 6
01847             << phi <<", "<< theta <<", "<< psi <<", "               // 7, 8, 9
01848             << uDot <<", "<< vDot <<", "<< wDot <<", "              // 10, 11, 12
01849             << pDot <<", "<< qDot <<", "<< rDot <<", "              // 13, 14, 15
01850             << u <<", "<< v <<", "<< w << ", "                      // 16, 17, 18
01851             << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
01852                                                                     // 19, 20, 21
01853             << FDMExec->GetAuxiliary()->Getalpha() << ", "          // 22
01854             << FDMExec->GetAuxiliary()->Getbeta() << ", "           // 23
01855             << FDMExec->GetAuxiliary()->Getadot() << ", "           // 24
01856             << FDMExec->GetAuxiliary()->Getbdot() << ", "           // 25
01857 
01858             << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; //26
01859 
01860         // this really goes into the integration process
01861         FDMExec->GetPropagate()->SetVState( VState );
01862 
01863         // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
01864         TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
01865 
01866         TrimAnalysis->SetCostFunctionValue(f);
01867     }
01868  
01869     return f;
01870 }
01871 
01872 
01873 double Objective::myCostFunctionFullWingsLevel(Vector<double> & x) // x variations come from the search algorithm
01874 {
01875 
01876     //----------------------------------------------------------------------------
01877     // NOTE:
01878     // when the program flow arrives here, it is assumed that a successful
01879     // instantiation of a TrimAnalysis object took place with a mode set
01880     // to taFullWingsLevel, i.e. 6 independent variables to be adjusted
01881     // ___phi is set to zero because we want wings leveled___
01882     //----------------------------------------------------------------------------
01883 
01884     //----------------------------------------------------------------------------
01885     // NOTE:
01886     // rely on the fact that IC quantities have been fed at least once into
01887     // their correspondent data structure in TrimAnalysis->fdmex tree
01888     //----------------------------------------------------------------------------
01889 
01890     double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
01891 
01892     double theta, phi, psi;
01893 
01894     double alpha = 0.0, beta = 0.0, gamma = 0.0;
01895 
01896     FGQuaternion Quat;
01897     FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
01898 
01899     FGPropagate::VehicleState VState;
01900     VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
01901     VState.vUVW      = FGColumnVector3(0.0,0.0,0.0);
01902     VState.vPQR      = FGColumnVector3(0.0,0.0,0.0);
01903     VState.vQtrn     = FGQuaternion   (0.0,0.0,0.0);
01904 
01905     //----------------------------------------------------------------------------
01906     // Minimization control vector, i.e. independent variables
01907     //----------------------------------------------------------------------------
01908 
01909     // CHECK the trim mode
01910 
01911     //-----------------------------------------------------------------------------------------------------------------
01912     // REMINDER:
01913     // n. of control variables for full trim (taFullWingsLevel): 6
01914     // ordering: the four commands first, then the three Euler angles,
01915     // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
01916     // [4] theta, [5] psi (alias taHeading)
01917     //-----------------------------------------------------------------------------------------------------------------
01918 
01919     delta_cmd_T = x[0]; // throttle, cmd
01920     delta_cmd_E = x[1]; // elevator, cmd
01921     delta_cmd_A = x[2]; // aileron,  cmd
01922     delta_cmd_R = x[3]; // rudder,   cmd
01923 
01924     phi   = 0.0 ; // rad, this enforces the wings-level condition
01925     theta = x[4];
01926     psi   = x[5];
01927 
01928     TrimAnalysis->SetEulerAngles(phi, theta, psi);
01929 
01930     //----------------------------------------------------------------------------
01931     // parameter bound check
01932     // ToDo: manage bounds via the FGTrimAnalysisControl class
01933     //----------------------------------------------------------------------------
01934     bool penalty =  ( (delta_cmd_T <   0) || (delta_cmd_T >  1) )
01935                  || ( (delta_cmd_E <  -1) || (delta_cmd_E >  1) )
01936                  || ( (delta_cmd_A <  -1) || (delta_cmd_A >  1) )
01937                  || ( (delta_cmd_R <  -1) || (delta_cmd_R >  1) )
01938                  || ( (psi   <    0.0     ) || (psi   >  2.0*M_PI) )
01939                  || ( (theta <   -0.5*M_PI) || (theta >  0.5*M_PI) )
01940                  // || ( (phi   <   -    M_PI) || (phi   >      M_PI) ) ...always zero here
01941                  ;
01942 
01943     if ( penalty ) {
01944         //-------------------------------------------------
01945         // just return an "infinite" value
01946         return HUGE_VAL;
01947         //-------------------------------------------------
01948     } else {
01949         calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
01950                               phi, theta, psi,
01951                               TrimAnalysis->GetMode(),
01952                               alpha, beta, gamma,
01953                               VState,
01954                               vUVWdot, vPQRdot );
01955     }
01956 
01957     double u   , v   , w   ,
01958            p   , q   , r   ,
01959            uDot, vDot, wDot,
01960            pDot, qDot, rDot;
01961     u     = VState.vUVW   (1);  v     = VState.vUVW   (2);  w     = VState.vUVW   (3);
01962     p     = VState.vPQR   (1);  q     = VState.vPQR   (2);  r     = VState.vPQR   (3);
01963     uDot  =        vUVWdot(1);  vDot  =        vUVWdot(2);  wDot  =        vUVWdot(3);
01964     pDot  =        vPQRdot(1);  qDot  =        vPQRdot(2);  rDot  =        vPQRdot(3);
01965 
01966     double
01967     f = 1.000*uDot*uDot
01968       +       vDot*vDot
01969       + 1.000*wDot*wDot
01970       + 0.010*pDot*pDot
01971       + 0.010*qDot*qDot
01972       + 0.010*rDot*rDot;
01973 
01974     static int count = 0;
01975     count++;
01976 
01977     if ( f < TrimAnalysis->GetCostFunctionValue() )
01978     {
01979         // feed into the vector of TrimAnalysis Controls the current values
01980         vector<FGTrimAnalysisControl*>::iterator vi;
01981         for(vi=TrimAnalysis->GetControls()->begin();
01982             vi!=TrimAnalysis->GetControls()->end();vi++)
01983         {
01984             if ( (*vi)->GetControlType()==JSBSim::taThrottle  ) (*vi)->SetControl(delta_cmd_T);
01985             if ( (*vi)->GetControlType()==JSBSim::taElevator  ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim
01986             if ( (*vi)->GetControlType()==JSBSim::taAileron   ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim
01987             if ( (*vi)->GetControlType()==JSBSim::taRudder    ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim
01988             if ( (*vi)->GetControlType()==JSBSim::taPhi       ) (*vi)->SetControl(phi);
01989             if ( (*vi)->GetControlType()==JSBSim::taTheta     ) (*vi)->SetControl(theta);
01990             if ( (*vi)->GetControlType()==JSBSim::taHeading   ) (*vi)->SetControl(psi);
01991         }
01992 
01993         if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
01994 
01995         // write on file, if open
01996 
01997         // "# iteration, costf,
01998         //    dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
01999         //    uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
02000         //    u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
02001         //    alphaDot (rad/s), betaDot (rad/s), Thrust"
02002 
02003         ofstream* rfp = TrimAnalysis->GetResultsFile();
02004         if (rfp)
02005             (*rfp)
02006             << count <<", "<< f <<", "                              // 1, 2
02007             << delta_cmd_T <<", "<< delta_cmd_E <<", "<< delta_cmd_A <<", "<< delta_cmd_R <<", "
02008                                                                     // 3, 4, 5, 6
02009             << phi <<", "<< theta <<", "<< psi <<", "               // 7, 8, 9
02010             << uDot <<", "<< vDot <<", "<< wDot <<", "              // 10, 11, 12
02011             << pDot <<", "<< qDot <<", "<< rDot <<", "              // 13, 14, 15
02012             << u <<", "<< v <<", "<< w << ", "                      // 16, 17, 18
02013             << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
02014                                                                     // 19, 20, 21
02015             << FDMExec->GetAuxiliary()->Getalpha() << ", "          // 22
02016             << FDMExec->GetAuxiliary()->Getbeta() << ", "           // 23
02017             << FDMExec->GetAuxiliary()->Getadot() << ", "           // 24
02018             << FDMExec->GetAuxiliary()->Getbdot() << ", "           // 25
02019 
02020             << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n";  // 26
02021 
02022         // this influences really into the further integration process
02023         FDMExec->GetPropagate()->SetVState( VState );
02024 
02025         // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
02026         TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
02027 
02028         TrimAnalysis->SetCostFunctionValue(f);
02029     }
02030 
02031     return f;
02032 }
02033 
02034 double Objective::myCostFunctionLongitudinal(Vector<double> & x)
02035 {
02036     //----------------------------------------------------------------------------
02037     // NOTE:
02038     // when the program flow arrives here, it is assumed that a successful
02039     // instantiation of a TrimAnalysis object took place with a mode set
02040     // to taLongitudinal, i.e. 3 independent variables to be adjusted
02041     //----------------------------------------------------------------------------
02042 
02043     //----------------------------------------------------------------------------
02044     // NOTE:
02045     // rely on the fact that IC quantities have been fed at least once into
02046     // their correspondent data structure in TrimAnalysis->fdmex tree
02047     //
02048     // A perfect longitudinal trim has to be possible!
02049     // (no asymmetric effect should be present)
02050     //----------------------------------------------------------------------------
02051 
02052     double delta_cmd_T, delta_cmd_E, delta_cmd_A = 0.0, delta_cmd_R = 0.0;
02053 
02054     double phi, theta, psi;
02055 
02056     double alpha = 0.0, beta = 0.0, gamma = 0.0;
02057 
02058     FGQuaternion Quat;
02059     FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
02060 
02061     FGPropagate::VehicleState VState;
02062     VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
02063     VState.vUVW      = FGColumnVector3(0.0,0.0,0.0);
02064     VState.vPQR      = FGColumnVector3(0.0,0.0,0.0);
02065     VState.vQtrn     = FGQuaternion   (0.0,0.0,0.0);
02066 
02067     //----------------------------------------------------------------------------
02068     // Minimization control vector, i.e. independent variables
02069     //----------------------------------------------------------------------------
02070 
02071     // CHECK the trim mode
02072 
02073     //-----------------------------------------------------------------------------------------------------------------
02074     // REMINDER:
02075     // n. of control variables for longitudinal trim (taLongitudinal): 3
02076     // ordering: the two commands first, then the Euler angle theta,
02077     // [0] throttle cmd, [1] elevator cmd, [2] theta
02078     //-----------------------------------------------------------------------------------------------------------------
02079 
02080     delta_cmd_T = x[0]; // throttle, cmd
02081     delta_cmd_E = x[1]; // elevator, cmd
02082     theta       = x[2]; // pitch attitude
02083 
02084     double psiIC   = FDMExec->GetIC()->GetPsiRadIC();
02085 
02086     psi   = psiIC;
02087     phi   = 0.0;
02088 
02089     TrimAnalysis->SetEulerAngles(phi, theta, psi);
02090 
02091     //----------------------------------------------------------------------------
02092     // parameter bound check
02093     // ToDo: manage bounds via the FGTrimAnalysisControl class
02094     //----------------------------------------------------------------------------
02095     bool penalty =  ( (delta_cmd_T <   0) || (delta_cmd_T >  1) )
02096                  || ( (delta_cmd_E <  -1) || (delta_cmd_E >  1) )
02097                  || ( (theta <   -0.5*M_PI) || (theta >  0.5*M_PI) )
02098                  ;
02099 
02100     if ( penalty ) {
02101         //-------------------------------------------------
02102         // just return an "infinite" value
02103         return HUGE_VAL;
02104         //-------------------------------------------------
02105     } else {
02106         calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
02107                               phi, theta, psi,
02108                               TrimAnalysis->GetMode(),
02109                               alpha, beta, gamma,
02110                               VState,
02111                               vUVWdot, vPQRdot );
02112     }
02113 
02114     double u   , v   , w   ,
02115            p   , q   , r   ,
02116            uDot, vDot, wDot,
02117            pDot, qDot, rDot;
02118     u     = VState.vUVW   (1);  v     = VState.vUVW   (2);  w     = VState.vUVW   (3);
02119     p     = VState.vPQR   (1);  q     = VState.vPQR   (2);  r     = VState.vPQR   (3);
02120     uDot  =        vUVWdot(1);  vDot  =        vUVWdot(2);  wDot  =        vUVWdot(3);
02121     pDot  =        vPQRdot(1);  qDot  =        vPQRdot(2);  rDot  =        vPQRdot(3);
02122 
02123     double
02124     f = 1.000*uDot*uDot
02125       + 1.000*wDot*wDot
02126       + //1.0
02127         0.010
02128         *qDot*qDot;
02129 
02130     static int count = 0;
02131     count++;
02132 
02133     if ( f < TrimAnalysis->GetCostFunctionValue() )
02134     {
02135         // feed into the vector of TrimAnalysis Controls the current values
02136         vector<FGTrimAnalysisControl*>::iterator vi;
02137         for(vi=TrimAnalysis->GetControls()->begin();
02138             vi!=TrimAnalysis->GetControls()->end();vi++)
02139         {
02140             if ( (*vi)->GetControlType()==JSBSim::taThrottle  ) (*vi)->SetControl(delta_cmd_T);
02141             if ( (*vi)->GetControlType()==JSBSim::taElevator  ) (*vi)->SetControl(delta_cmd_E);    // TODO: taPitchTrim
02142             if ( (*vi)->GetControlType()==JSBSim::taAileron   ) (*vi)->SetControl(0);              // TODO: taRollTrim
02143             if ( (*vi)->GetControlType()==JSBSim::taRudder    ) (*vi)->SetControl(0);              // TODO: taYawTrim
02144             if ( (*vi)->GetControlType()==JSBSim::taPhi       ) (*vi)->SetControl(0);
02145             if ( (*vi)->GetControlType()==JSBSim::taTheta     ) (*vi)->SetControl(theta);
02146             if ( (*vi)->GetControlType()==JSBSim::taHeading   ) (*vi)->SetControl( FDMExec->GetIC()->GetPsiRadIC());
02147         }
02148 
02149         if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
02150 
02151         // write on file, if open
02152 
02153         // "# iteration, costf,
02154         //    dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
02155         //    uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
02156         //    u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
02157         //    alphaDot (rad/s), betaDot (rad/s), Thrust"
02158 
02159         ofstream* rfp = TrimAnalysis->GetResultsFile();
02160         if (rfp)
02161             (*rfp)
02162             << count <<", "<< f <<", "                              // 1, 2
02163             << delta_cmd_T <<", "                                   // 3
02164             << delta_cmd_E <<", " <<0.0 <<", "<< 0.0 <<", "         // 4, 5, 6
02165             << phi <<", "<< theta <<", "<< psi <<", "               // 7, 8, 9
02166             << uDot <<", "<< vDot <<", "<< wDot <<", "              // 10, 11, 12
02167             << pDot <<", "<< qDot <<", "<< rDot <<", "              // 13, 14, 15
02168             << u <<", "<< v <<", "<< w << ", "                      // 16, 17, 18
02169             << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
02170                                                                     // 19, 20, 21
02171             << FDMExec->GetAuxiliary()->Getalpha() << ", "          // 22
02172             << FDMExec->GetAuxiliary()->Getbeta() << ", "           // 23
02173             << FDMExec->GetAuxiliary()->Getadot() << ", "           // 24
02174             << FDMExec->GetAuxiliary()->Getbdot() << ", "           // 25
02175 
02176             << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n";  // 26
02177 
02178         FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary
02179 
02180         // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
02181         TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
02182 
02183         TrimAnalysis->SetCostFunctionValue(f); // update the current minimum
02184     }
02185 
02186     return f;
02187 } // end of myCostFunctionLongitudinal
02188 
02189 double Objective::myCostFunctionFullCoordinatedTurn(Vector<double> & x)
02190 {
02191     //----------------------------------------------------------------------------
02192     // NOTE:
02193     // when the program flow arrives here, it is assumed that a successful
02194     // instantiation of a TrimAnalysis object took place with a mode set
02195     // to taTurn, i.e. 4 independent variables to be adjusted
02196     //----------------------------------------------------------------------------
02197 
02198     //----------------------------------------------------------------------------
02199     // NOTE:
02200     // rely on the fact that IC quantities have been fed at least once into
02201     // their correspondent data structure in TrimAnalysis->fdmex tree
02202     //----------------------------------------------------------------------------
02203 
02204     double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
02205 
02206     double phi, theta, psi;
02207 
02208     double alpha = 0.0, beta = 0.0;
02209     double gamma = 0.0;
02210 
02211     // for coordinated turns with nonzero rate of climb
02212     gamma = FDMExec->GetIC()->GetFlightPathAngleRadIC();
02213 
02214     FGQuaternion Quat;
02215     FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
02216 
02217     FGPropagate::VehicleState VState;
02218     VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
02219     VState.vUVW      = FGColumnVector3(0.0,0.0,0.0);
02220     VState.vPQR      = FGColumnVector3(0.0,0.0,0.0);
02221     VState.vQtrn     = FGQuaternion   (0.0,0.0,0.0);
02222 
02223     //----------------------------------------------------------------------------
02224     // Minimization control vector, i.e. independent variables
02225     //----------------------------------------------------------------------------
02226 
02227     // CHECK the trim mode
02228 
02229     //-----------------------------------------------------------------------------------------------------------------
02230     // REMINDER:
02231     // n. of control variables for longitudinal trim (taTurn): 5
02232     // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
02233     // [4] psi
02234     //-----------------------------------------------------------------------------------------------------------------
02235 
02236     delta_cmd_T = x[0]; // throttle, cmd
02237     delta_cmd_E = x[1]; // elevator, cmd
02238     delta_cmd_A = x[2]; // aileron,  cmd
02239     delta_cmd_R = x[3]; // rudder,   cmd
02240 
02241     psi         = x[4];
02242 
02243     // theta_underlined (see paper on Trim) for coordinated turn
02244     theta = atan2( sin(psi)*cos(gamma)+cos(psi)*sin(gamma), cos(gamma) );
02245 
02246     TrimAnalysis->setupTurnPhi(psi,theta); // calculates also phi
02247 
02248     double psiIC   = FDMExec->GetIC()->GetPsiRadIC();
02249     double phiIC   = FDMExec->GetIC()->GetPhiRadIC();
02250 
02251     phi   = TrimAnalysis->GetPhiRad(); // this one is calculated by setupTurnPhi() above
02252 
02253     TrimAnalysis->SetEulerAngles(phi, theta, psi); // recalculates sin and cosines
02254 
02255     //----------------------------------------------------------------------------
02256     // parameter bound check
02257     // ToDo: manage bounds via the FGTrimAnalysisControl class
02258     //----------------------------------------------------------------------------
02259     bool penalty =  ( (delta_cmd_T <   0) || (delta_cmd_T >  1) )
02260                  || ( (delta_cmd_E <  -1) || (delta_cmd_E >  1) )
02261                  || ( (delta_cmd_A <  -1) || (delta_cmd_A >  1) )
02262                  || ( (delta_cmd_R <  -1) || (delta_cmd_R >  1) )
02263                  || ( (psi   <    0.0     ) || (psi   >  2.0*M_PI) )
02264                  || ( (theta <   -0.5*M_PI) || (theta >  0.5*M_PI) ) // may go out of range
02265                  ;
02266 
02267     if ( penalty ) {
02268         //-------------------------------------------------
02269         // just return an "infinite" value
02270         return HUGE_VAL;
02271         //-------------------------------------------------
02272     } else {
02273         calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
02274                               phi, theta, psi,
02275                               TrimAnalysis->GetMode(),
02276                               alpha, beta, gamma,
02277                               VState,
02278                               vUVWdot, vPQRdot );
02279     }
02280 
02281     double u   , v   , w   ,
02282            p   , q   , r   ,
02283            uDot, vDot, wDot,
02284            pDot, qDot, rDot;
02285     u     = VState.vUVW   (1);  v     = VState.vUVW   (2);  w     = VState.vUVW   (3);
02286     p     = VState.vPQR   (1);  q     = VState.vPQR   (2);  r     = VState.vPQR   (3);
02287     uDot  =        vUVWdot(1);  vDot  =        vUVWdot(2);  wDot  =        vUVWdot(3);
02288     pDot  =        vPQRdot(1);  qDot  =        vPQRdot(2);  rDot  =        vPQRdot(3);
02289 
02290     double
02291     f = 1.000*uDot*uDot
02292       +       vDot*vDot
02293       + 1.000*wDot*wDot
02294       + 0.010*pDot*pDot
02295       + 0.010*qDot*qDot
02296       + 0.010*rDot*rDot;
02297 
02298     static int count = 0;
02299     count++;
02300 
02301     if ( f < TrimAnalysis->GetCostFunctionValue() )
02302     {
02303         // feed into the vector of TrimAnalysis Controls the current values
02304         vector<FGTrimAnalysisControl*>::iterator vi;
02305         for(vi=TrimAnalysis->GetControls()->begin();
02306             vi!=TrimAnalysis->GetControls()->end();vi++)
02307         {
02308             if ( (*vi)->GetControlType()==JSBSim::taThrottle  ) (*vi)->SetControl(delta_cmd_T);
02309             if ( (*vi)->GetControlType()==JSBSim::taElevator  ) (*vi)->SetControl(delta_cmd_E);    // TODO: taPitchTrim
02310             if ( (*vi)->GetControlType()==JSBSim::taAileron   ) (*vi)->SetControl(delta_cmd_A);              // TODO: taRollTrim
02311             if ( (*vi)->GetControlType()==JSBSim::taRudder    ) (*vi)->SetControl(delta_cmd_R);              // TODO: taYawTrim
02312             if ( (*vi)->GetControlType()==JSBSim::taPhi       ) (*vi)->SetControl(phi);
02313             if ( (*vi)->GetControlType()==JSBSim::taTheta     ) (*vi)->SetControl(theta);
02314             if ( (*vi)->GetControlType()==JSBSim::taHeading   ) (*vi)->SetControl(psi);
02315         }
02316 
02317         if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
02318 
02319         // write on file, if open
02320 
02321         // "# iteration, costf,
02322         //    dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
02323         //    uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
02324         //    u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
02325         //    alphaDot (rad/s), betaDot (rad/s), Thrust"
02326 
02327         ofstream* rfp = TrimAnalysis->GetResultsFile();
02328         if (rfp)
02329             (*rfp)
02330             << count <<", "<< f <<", "                              // 1, 2
02331             << delta_cmd_T <<", "                                   // 3
02332             << delta_cmd_E <<", " << delta_cmd_A <<", "<< delta_cmd_R <<", " // 4, 5, 6
02333             << phi <<", "<< theta <<", "<< psi <<", "               // 7, 8, 9
02334             << uDot <<", "<< vDot <<", "<< wDot <<", "              // 10, 11, 12
02335             << pDot <<", "<< qDot <<", "<< rDot <<", "              // 13, 14, 15
02336             << u <<", "<< v <<", "<< w << ", "                      // 16, 17, 18
02337             << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
02338                                                                     // 19, 20, 21
02339             << FDMExec->GetAuxiliary()->Getalpha() << ", "          // 22
02340             << FDMExec->GetAuxiliary()->Getbeta() << ", "           // 23
02341             << FDMExec->GetAuxiliary()->Getadot() << ", "           // 24
02342             << FDMExec->GetAuxiliary()->Getbdot() << ", "           // 25
02343 
02344             << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n";  // 26
02345 
02346         FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary
02347 
02348         // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
02349         TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
02350 
02351         TrimAnalysis->SetCostFunctionValue(f); // update the current minimum
02352     }
02353 
02354     return f;
02355 } // end of myCostFunctionFullCoordinatedTurn
02356 
02357 double Objective::myCostFunctionFullTurn(Vector<double> & x)
02358 {
02359     //----------------------------------------------------------------------------
02360     // NOTE:
02361     // when the program flow arrives here, it is assumed that a successful
02362     // instantiation of a TrimAnalysis object took place with a mode set
02363     // to taTurnFull, i.e. 5 independent variables to be adjusted
02364     //----------------------------------------------------------------------------
02365 
02366     //----------------------------------------------------------------------------
02367     // NOTE:
02368     // rely on the fact that IC quantities have been fed at least once into
02369     // their correspondent data structure in TrimAnalysis->fdmex tree
02370     //----------------------------------------------------------------------------
02371 
02372     double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
02373 
02374     double phi, theta, psi;
02375 
02376     double alpha = 0.0, beta = 0.0;
02377     double gamma = 0.0;
02378 
02379     // for coordinated turns with nonzero rate of climb
02380     gamma = FDMExec->GetIC()->GetFlightPathAngleRadIC();
02381 
02382     FGQuaternion Quat;
02383     FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
02384 
02385     FGPropagate::VehicleState VState;
02386     VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
02387     VState.vUVW      = FGColumnVector3(0.0,0.0,0.0);
02388     VState.vPQR      = FGColumnVector3(0.0,0.0,0.0);
02389     VState.vQtrn     = FGQuaternion   (0.0,0.0,0.0);
02390 
02391     //----------------------------------------------------------------------------
02392     // Minimization control vector, i.e. independent variables
02393     //----------------------------------------------------------------------------
02394 
02395     // CHECK the trim mode
02396 
02397     //-----------------------------------------------------------------------------------------------------------------
02398     // REMINDER:
02399     // n. of control variables for longitudinal trim (taTurn): 5
02400     // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
02401     // [4] psi, [5] theta
02402     //-----------------------------------------------------------------------------------------------------------------
02403 
02404     delta_cmd_T = x[0]; // throttle, cmd
02405     delta_cmd_E = x[1]; // elevator, cmd
02406     delta_cmd_A = x[2]; // aileron,  cmd
02407     delta_cmd_R = x[3]; // rudder,   cmd
02408 
02409     psi         = x[4];
02410     theta       = x[5];
02411 
02412     TrimAnalysis->setupTurn();
02413 
02414     double psiIC   = FDMExec->GetIC()->GetPsiRadIC();
02415     double phiIC   = FDMExec->GetIC()->GetPhiRadIC();
02416 
02417     phi   = TrimAnalysis->GetPhiRad(); // this one is calculated by sutupTurnPhi() above
02418 
02419     TrimAnalysis->SetEulerAngles(phi, theta, psi); // recalculates sin and cosines
02420 
02421     //----------------------------------------------------------------------------
02422     // parameter bound check
02423     // ToDo: manage bounds via the FGTrimAnalysisControl class
02424     //----------------------------------------------------------------------------
02425     bool penalty =  ( (delta_cmd_T <   0) || (delta_cmd_T >  1) )
02426                  || ( (delta_cmd_E <  -1) || (delta_cmd_E >  1) )
02427                  || ( (delta_cmd_A <  -1) || (delta_cmd_A >  1) )
02428                  || ( (delta_cmd_R <  -1) || (delta_cmd_R >  1) )
02429                  || ( (psi   <    0.0     ) || (psi   >  2.0*M_PI) )
02430                  || ( (theta <   -0.5*M_PI) || (theta >  0.5*M_PI) ) // may go out of range
02431                  ;
02432 
02433     if ( penalty ) {
02434         //-------------------------------------------------
02435         // just return an "infinite" value
02436         return HUGE_VAL;
02437         //-------------------------------------------------
02438     } else {
02439         calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
02440                               phi, theta, psi,
02441                               TrimAnalysis->GetMode(),
02442                               alpha, beta, gamma,
02443                               VState,
02444                               vUVWdot, vPQRdot );
02445     }
02446 
02447     double u   , v   , w   ,
02448            p   , q   , r   ,
02449            uDot, vDot, wDot,
02450            pDot, qDot, rDot;
02451     u     = VState.vUVW   (1);  v     = VState.vUVW   (2);  w     = VState.vUVW   (3);
02452     p     = VState.vPQR   (1);  q     = VState.vPQR   (2);  r     = VState.vPQR   (3);
02453     uDot  =        vUVWdot(1);  vDot  =        vUVWdot(2);  wDot  =        vUVWdot(3);
02454     pDot  =        vPQRdot(1);  qDot  =        vPQRdot(2);  rDot  =        vPQRdot(3);
02455 
02456     double
02457     f = 1.000*uDot*uDot
02458       +       vDot*vDot
02459       + 1.000*wDot*wDot
02460       + 0.010*pDot*pDot
02461       + 0.010*qDot*qDot
02462       + 0.010*rDot*rDot;
02463 
02464     static int count = 0;
02465     count++;
02466 
02467     if ( f < TrimAnalysis->GetCostFunctionValue() )
02468     {
02469         // feed into the vector of TrimAnalysis Controls the current values
02470         vector<FGTrimAnalysisControl*>::iterator vi;
02471         for(vi=TrimAnalysis->GetControls()->begin();
02472             vi!=TrimAnalysis->GetControls()->end();vi++)
02473         {
02474             if ( (*vi)->GetControlType()==JSBSim::taThrottle  ) (*vi)->SetControl(delta_cmd_T);
02475             if ( (*vi)->GetControlType()==JSBSim::taElevator  ) (*vi)->SetControl(delta_cmd_E);    // TODO: taPitchTrim
02476             if ( (*vi)->GetControlType()==JSBSim::taAileron   ) (*vi)->SetControl(delta_cmd_A);              // TODO: taRollTrim
02477             if ( (*vi)->GetControlType()==JSBSim::taRudder    ) (*vi)->SetControl(delta_cmd_R);              // TODO: taYawTrim
02478             if ( (*vi)->GetControlType()==JSBSim::taPhi       ) (*vi)->SetControl(phi);
02479             if ( (*vi)->GetControlType()==JSBSim::taTheta     ) (*vi)->SetControl(theta);
02480             if ( (*vi)->GetControlType()==JSBSim::taHeading   ) (*vi)->SetControl(psi);
02481         }
02482 
02483         if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
02484 
02485         // write on file, if open
02486 
02487         // "# iteration, costf,
02488         //    dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
02489         //    uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
02490         //    u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
02491         //    alphaDot (rad/s), betaDot (rad/s), Thrust"
02492 
02493         ofstream* rfp = TrimAnalysis->GetResultsFile();
02494         if (rfp)
02495             (*rfp)
02496             << count <<", "<< f <<", "                              // 1, 2
02497             << delta_cmd_T <<", "                                   // 3
02498             << delta_cmd_E <<", " << delta_cmd_A <<", "<< delta_cmd_R <<", " // 4, 5, 6
02499             << phi <<", "<< theta <<", "<< psi <<", "               // 7, 8, 9
02500             << uDot <<", "<< vDot <<", "<< wDot <<", "              // 10, 11, 12
02501             << pDot <<", "<< qDot <<", "<< rDot <<", "              // 13, 14, 15
02502             << u <<", "<< v <<", "<< w << ", "                      // 16, 17, 18
02503             << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
02504                                                                     // 19, 20, 21
02505             << FDMExec->GetAuxiliary()->Getalpha() << ", "          // 22
02506             << FDMExec->GetAuxiliary()->Getbeta() << ", "           // 23
02507             << FDMExec->GetAuxiliary()->Getadot() << ", "           // 24
02508             << FDMExec->GetAuxiliary()->Getbdot() << ", "           // 25
02509 
02510             << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n";  // 
02511 
02512         FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary
02513 
02514         // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
02515         TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
02516 
02517         TrimAnalysis->SetCostFunctionValue(f); // update the current minimum
02518     }
02519 
02520     return f;
02521 } // end of myCostFunctionFullCoordinatedTurn
02522 
02523 
02524 double Objective::myCostFunctionPullUp(Vector<double> & x)
02525 {
02526     //----------------------------------------------------------------------------
02527     // NOTE:
02528     // when the program flow arrives here, it is assumed that a successful
02529     // instantiation of a TrimAnalysis object took place with a mode set
02530     // to taTurn, i.e. 6 independent variables to be adjusted
02531     //----------------------------------------------------------------------------
02532 
02533     //----------------------------------------------------------------------------
02534     // NOTE:
02535     // rely on the fact that IC quantities have been fed at least once into
02536     // their correspondent data structure in TrimAnalysis->fdmex tree
02537     //----------------------------------------------------------------------------
02538 
02539     double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
02540 
02541     double phi, theta, psi;
02542 
02543     double alpha = 0.0, beta = 0.0, gamma = 0.0;
02544 
02545     FGQuaternion Quat;
02546     FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
02547 
02548     FGPropagate::VehicleState VState;
02549     VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
02550     VState.vUVW      = FGColumnVector3(0.0,0.0,0.0);
02551     VState.vPQR      = FGColumnVector3(0.0,0.0,0.0);
02552     VState.vQtrn     = FGQuaternion   (0.0,0.0,0.0);
02553 
02554     //----------------------------------------------------------------------------
02555     // Minimization control vector, i.e. independent variables
02556     //----------------------------------------------------------------------------
02557 
02558     // CHECK the trim mode
02559 
02560     //-----------------------------------------------------------------------------------------------------------------
02561     // REMINDER:
02562     // n. of control variables for longitudinal trim (taPullup): 5
02563     // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
02564     // [4] theta
02565     //-----------------------------------------------------------------------------------------------------------------
02566 
02567     delta_cmd_T = x[0]; // throttle, cmd
02568     delta_cmd_E = x[1]; // elevator, cmd
02569     delta_cmd_A = x[2]; // aileron,  cmd
02570     delta_cmd_R = x[3]; // rudder,   cmd
02571 
02572     theta       = x[4];
02573 
02574     double psiIC   = FDMExec->GetIC()->GetPsiRadIC();
02575 
02576     phi   = 0.0; // assume a wing level pull-up
02577     psi   = psiIC;
02578 
02579     TrimAnalysis->SetEulerAngles(phi, theta, psi);
02580 
02581     //----------------------------------------------------------------------------
02582     // parameter bound check
02583     // ToDo: manage bounds via the FGTrimAnalysisControl class
02584     //----------------------------------------------------------------------------
02585     bool penalty =  ( (delta_cmd_T <   0) || (delta_cmd_T >  1) )
02586                  || ( (delta_cmd_E <  -1) || (delta_cmd_E >  1) )
02587                  || ( (delta_cmd_A <  -1) || (delta_cmd_A >  1) )
02588                  || ( (delta_cmd_R <  -1) || (delta_cmd_R >  1) )
02589                  || ( (theta <   -0.5*M_PI) || (theta >  0.5*M_PI) );
02590 
02591     if ( penalty ) {
02592         //-------------------------------------------------
02593         // just return an "infinite" value
02594         return HUGE_VAL;
02595         //-------------------------------------------------
02596     } else {
02597         calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
02598                               phi, theta, psi,
02599                               TrimAnalysis->GetMode(),
02600                               alpha, beta, gamma,
02601                               VState,
02602                               vUVWdot, vPQRdot );
02603     }
02604 
02605     double u   , v   , w   ,
02606            p   , q   , r   ,
02607            uDot, vDot, wDot,
02608            pDot, qDot, rDot;
02609     u     = VState.vUVW   (1);  v     = VState.vUVW   (2);  w     = VState.vUVW   (3);
02610     p     = VState.vPQR   (1);  q     = VState.vPQR   (2);  r     = VState.vPQR   (3);
02611     uDot  =        vUVWdot(1);  vDot  =        vUVWdot(2);  wDot  =        vUVWdot(3);
02612     pDot  =        vPQRdot(1);  qDot  =        vPQRdot(2);  rDot  =        vPQRdot(3);
02613 
02614     double
02615     f = 1.000*uDot*uDot
02616       +       vDot*vDot
02617       + 1.000*wDot*wDot
02618       + 0.010*pDot*pDot
02619       + 0.010*qDot*qDot
02620       + 0.010*rDot*rDot;
02621 
02622     static int count = 0;
02623     count++;
02624 
02625     if ( f < TrimAnalysis->GetCostFunctionValue() )
02626     {
02627         // feed into the vector of TrimAnalysis Controls the current values
02628         vector<FGTrimAnalysisControl*>::iterator vi;
02629         for(vi=TrimAnalysis->GetControls()->begin();
02630             vi!=TrimAnalysis->GetControls()->end();vi++)
02631         {
02632             if ( (*vi)->GetControlType()==JSBSim::taThrottle  ) (*vi)->SetControl(delta_cmd_T);
02633             if ( (*vi)->GetControlType()==JSBSim::taElevator  ) (*vi)->SetControl(delta_cmd_E);    // TODO: taPitchTrim
02634             if ( (*vi)->GetControlType()==JSBSim::taAileron   ) (*vi)->SetControl(delta_cmd_A);    // TODO: taRollTrim
02635             if ( (*vi)->GetControlType()==JSBSim::taRudder    ) (*vi)->SetControl(delta_cmd_R);    // TODO: taYawTrim
02636             if ( (*vi)->GetControlType()==JSBSim::taPhi       ) (*vi)->SetControl(phi);
02637             if ( (*vi)->GetControlType()==JSBSim::taTheta     ) (*vi)->SetControl(theta);
02638             if ( (*vi)->GetControlType()==JSBSim::taHeading   ) (*vi)->SetControl(psi);
02639         }
02640 
02641         if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
02642 
02643         // write on file, if open
02644 
02645         // "# iteration, costf,
02646         //    dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
02647         //    uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
02648         //    u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
02649         //    alphaDot (rad/s), betaDot (rad/s), Thrust"
02650 
02651         ofstream* rfp = TrimAnalysis->GetResultsFile();
02652         if (rfp)
02653             (*rfp)
02654             << count <<", "<< f <<", "                              // 1, 2
02655             << delta_cmd_T <<", "                                   // 3
02656             << delta_cmd_E <<", " <<delta_cmd_A <<", "<< delta_cmd_R <<", "         // 4, 5, 6
02657             << phi <<", "<< theta <<", "<< psi <<", "               // 7, 8, 9
02658             << uDot <<", "<< vDot <<", "<< wDot <<", "              // 10, 11, 12
02659             << pDot <<", "<< qDot <<", "<< rDot <<", "              // 13, 14, 15
02660             << u <<", "<< v <<", "<< w << ", "                      // 16, 17, 18
02661             << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
02662                                                                     // 19, 20, 21
02663             << FDMExec->GetAuxiliary()->Getalpha() << ", "          // 22
02664             << FDMExec->GetAuxiliary()->Getbeta() << ", "           // 23
02665             << FDMExec->GetAuxiliary()->Getadot() << ", "           // 24
02666             << FDMExec->GetAuxiliary()->Getbdot() << ", "           // 25
02667 
02668             << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n";  // 26
02669 
02670         FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary
02671 
02672         // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
02673         TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
02674 
02675         TrimAnalysis->SetCostFunctionValue(f); // update the current minimum
02676     }
02677 
02678     return f;
02679 } // end of myCostFunctionPullUp
02680 
02681 
02682 void Objective::calculateDottedStates(double delta_cmd_T, double delta_cmd_E, double delta_cmd_A, double delta_cmd_R,
02683                                       double phi, double theta, double psi,
02684                                       TrimAnalysisMode trimMode,
02685                                       double& alpha, double& beta, double& gamma,
02686                                       FGPropagate::VehicleState& VState,
02687                                       FGColumnVector3& vUVWdot, FGColumnVector3& vPQRdot )
02688 {
02689     double stheta,sphi,spsi;
02690     double ctheta,cphi,cpsi;
02691     double phiW = 0.;
02692     FGPropulsion* Propulsion = FDMExec->GetPropulsion();
02693     FGFCS* FCS = FDMExec->GetFCS();
02694     FGAuxiliary* Auxiliary = FDMExec->GetAuxiliary();
02695 
02696     if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) )//... Coordinated turn: p,q,r not zero, beta=0, gamma=0
02697     {
02698         // setupTurn has already made its job
02699         phiW = TrimAnalysis->GetPhiWRad();
02700     }
02701 
02702     cphi   = cos(phi);   sphi   = sin(phi);   // phi, rad
02703     ctheta = cos(theta); stheta = sin(theta); // theta, rad
02704     cpsi   = cos(psi);   spsi   = sin(psi);   // psi, rad
02705 
02706     TrimAnalysis->SetEulerAngles(phi, theta, psi);
02707 
02708     //-------------------------------------------------
02709     // apply controls
02710     //-------------------------------------------------
02711 
02712     // make sure the engines are running
02713     for (unsigned int i=0; i<Propulsion->GetNumEngines(); i++) {
02714        Propulsion->GetEngine(i)->SetRunning(true);
02715     }
02716 
02717     double tMin,tMax;
02718     for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
02719     {
02720       tMin=Propulsion->GetEngine(i)->GetThrottleMin();
02721       tMax=Propulsion->GetEngine(i)->GetThrottleMax();
02722       FCS->SetThrottleCmd(i,tMin + delta_cmd_T *(tMax-tMin));
02723       FCS->Run(); // apply throttle change
02724     }
02725     Propulsion->GetSteadyState(); // GetSteadyState processes all engines
02726 
02727     // apply commands
02728     // ToDo: apply aerosurface deflections,
02729     // to override control system authority
02730 
02731     FCS->SetDeCmd( delta_cmd_E ); // elevator
02732     FCS->SetDaCmd( delta_cmd_A ); // ailerons
02733     FCS->SetDrCmd( delta_cmd_R ); // rudder
02734 
02735     FCS->Run(); // apply yoke & pedal changes
02736 
02737     //................................................
02738     // set also euler angles
02739     //................................................
02740 
02741     // new euler angles -> new quaternion Quat
02742 
02743     FGQuaternion Quat1( phi, theta, psi ); // values coming fro search algorithm
02744 
02745     VState.vQtrn = Quat1;
02746     VState.vQtrn.Normalize();
02747 
02748     //------------------------------------------
02749     // reconstruct NED velocity components from
02750     // initial conditions
02751     //------------------------------------------
02752 
02753     // initial altitude (never changed)
02754     double hIC = FDMExec->GetIC()->GetAltitudeFtIC();
02755 
02756     // re-apply the desired altitude;
02757     // FGAtmosphere wants the altitude from FGPropagate
02758     // (currently there's no Seth method in FGAtmosphere)
02759     FDMExec->GetPropagate()->Seth( hIC );
02760     FDMExec->GetAtmosphere()->Run();
02761 
02762     // initial speed (never changed)
02763     double vtIC = FDMExec->GetIC()->GetVtrueFpsIC();
02764 
02765     // initial flight-path angle (never changed)
02766     double gammaIC = FDMExec->GetIC()->GetFlightPathAngleRadIC();
02767 
02768     // initial climb-rate (never changed)
02769     // ???
02770     double rocIC = FDMExec->GetIC()->GetClimbRateFpsIC();
02771     // ???
02772     gammaIC = TrimAnalysis->GetGamma(); // from file of from IC
02773     rocIC = vtIC * tan(gammaIC);
02774     gamma = gammaIC; // <--------------------------------------------- this goes out to the caller
02775 
02776     double vdownIC = - rocIC;
02777 
02778     if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) ) //... turn: p,q,r, gamma not zero
02779     {
02780         gamma   = TrimAnalysis->GetGammaRad(); //0.0;
02781         vdownIC = TrimAnalysis->GetVtFps() * tan(gamma); //0.0;
02782     }
02783     Auxiliary->SetGamma(gamma);
02784 
02785     // euler angles from the IC
02786     double psiIC   = FDMExec->GetIC()->GetPsiRadIC(); // this is the desired ground track direction
02787 
02788     // assume that the desired ground-track heading coincides with the
02789     // aircraft initial heading given in IC
02790     double psigtIC = psiIC;
02791 
02792     // NED velocity components
02793     //double vgIC = FDMExec->GetIC()->GetVgroundFpsIC();
02794     double vgIC = vtIC * cos(gammaIC);
02795 
02796     double vnorthIC = vgIC * cos(psigtIC);
02797     double veastIC  = vgIC * sin(psigtIC);
02798 
02799     // Wind components in NED frame
02800     double wnorthIC = FDMExec->GetIC()->GetWindNFpsIC();
02801     double weastIC  = FDMExec->GetIC()->GetWindEFpsIC();
02802     double wdownIC  = FDMExec->GetIC()->GetWindDFpsIC();
02803 
02804     // Velocity components in body-frame (from NED)
02805     double u, v, w;
02806 
02807     u=vnorthIC*ctheta*cpsi +
02808        veastIC*ctheta*spsi -
02809        vdownIC*stheta;
02810     v=vnorthIC*( sphi*stheta*cpsi - cphi*spsi ) +
02811        veastIC*( sphi*stheta*spsi + cphi*cpsi ) +
02812        vdownIC*sphi*ctheta;
02813     w=vnorthIC*( cphi*stheta*cpsi + sphi*spsi ) +
02814        veastIC*( cphi*stheta*spsi - sphi*cpsi ) +
02815        vdownIC*cphi*ctheta;
02816 
02817     // Wind domponents in body-frame (from NED)
02818     double uw, vw, ww;
02819 
02820     uw=wnorthIC*ctheta*cpsi +
02821         weastIC*ctheta*spsi -
02822         wdownIC*stheta;
02823     vw=wnorthIC*( sphi*stheta*cpsi - cphi*spsi ) +
02824         weastIC*( sphi*stheta*spsi + cphi*cpsi ) +
02825         wdownIC*sphi*ctheta;
02826     ww=wnorthIC*(cphi*stheta*cpsi + sphi*spsi) +
02827         weastIC*(cphi*stheta*spsi - sphi*cpsi) +
02828         wdownIC*cphi*ctheta;
02829 
02830     /**********************************************************************************************
02831                                                                  P R O P A G A T I O N  start . . .
02832      **********************************************************************************************/
02833 
02834     // now that we have the velocities we can imitate the chain
02835     // in FDMexec->Run()... Propagate::Run()
02836 
02837     //++++++++++++++++++++++++++++++++++++++++++++
02838     // recalculate some important auxiliary data
02839     // imitate here Auxiliary::Run()
02840     //++++++++++++++++++++++++++++++++++++++++++++
02841 
02842     double ua, va, wa;
02843     double adot, bdot;
02844 
02845     Auxiliary->SetVt(vtIC); // re-apply the desired velocity
02846 
02847     if ((trimMode==taTurn)||(trimMode==taPullup))
02848     {
02849         v=0.0; // no sideslip
02850     }
02851 
02852     ua = u + uw; va = v + vw; wa = w + ww;
02853 
02854     // from FGAuxiliary::Run()
02855     if ( vtIC > 0.05) {
02856       if ( wa != 0.0 ) alpha = ua*ua > 0.0 ? atan2(wa, ua) : 0.0;                    // this goes out to the caller
02857       if ( va != 0.0 ) beta = ua*ua+wa*wa > 0.0 ? atan2(va,sqrt(ua*ua+wa*wa)) : 0.0; // this goes out to the caller
02858       double mUW = (ua*ua + wa*wa);
02859       double signU=1;
02860       if (ua != 0.0) signU = ua/fabs(ua);
02861       adot = bdot = 0; // enforce zero aerodyn. angle rates
02862     } else {
02863       alpha = beta = adot = bdot = 0;                                                // this goes out to the caller
02864     }
02865 
02866     //------------------------------------------------------------------------------------
02867     // APPLY constraints to (p,q,r)
02868     //------------------------------------------------------------------------------------
02869 
02870     // constraint applied to p,q,r
02871     double p, q, r;
02872 
02873     // for NON TURNING straight FLIGHT, these will remain zero
02874     p = q = r = 0.0;
02875 
02876     if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) ) //... Coordinated turn: p,q,r not zero
02877     {
02878       double g=FDMExec->GetInertial()->gravity();
02879       //double cgamma=cos(FDMExec->GetIC()->GetFlightPathAngleRadIC());
02880 
02881       // assume that the given phiIC is the desired phi_Wind
02882       // phiW  ... see above at the beginning
02883       double turnRate = g*tan(phiW) / FDMExec->GetIC()->GetVtrueFpsIC(); //FDMExec->GetIC()->GetUBodyFpsIC();
02884       double pW = 0.0;
02885       double qW = turnRate*sin(phiW);
02886       double rW = turnRate*cos(phiW);
02887 
02888       FGColumnVector3 pqr = TrimAnalysis->UpdateRatesTurn(psi, theta, phi, phiW);
02889 
02890       // finally calculate the body frame angular rates
02891       p = pqr(1);
02892       q = pqr(2);
02893       r = pqr(3);
02894 
02895       Auxiliary->SetGamma(0.);
02896 
02897     }
02898     if ( trimMode == taPullup ) //... then p,q,r not zero
02899     {
02900         double g=FDMExec->GetInertial()->gravity();
02901         double cgamma=cos(FDMExec->GetIC()->GetFlightPathAngleRadIC());
02902         //double targetNlf=FDMExec->GetIC()->GetTargetNlfIC();
02903         double targetNlf = TrimAnalysis->GetTargetNlf();
02904 
02905         FGColumnVector3 pqr = TrimAnalysis->UpdateRatesPullup();
02906         q = pqr(2);
02907 
02908         // assume levelled wings
02909     }
02910     //if ( trimMode == taRoll ) ... then p,q,r not zero
02911     //    may assign theta rate (=q if wings are leveled)
02912 
02913     double qbar   = 0.5*FDMExec->GetAtmosphere()->GetDensity()*vtIC*vtIC;
02914     double qbarUW = 0.5*FDMExec->GetAtmosphere()->GetDensity()*(ua*ua + wa*wa);
02915     double qbarUV = 0.5*FDMExec->GetAtmosphere()->GetDensity()*(ua*ua + va*va);
02916     double Mach = vtIC / FDMExec->GetAtmosphere()->GetSoundSpeed();
02917 
02918     double MachU = ua / FDMExec->GetAtmosphere()->GetSoundSpeed();
02919     double MachV = va / FDMExec->GetAtmosphere()->GetSoundSpeed();
02920     double MachW = wa / FDMExec->GetAtmosphere()->GetSoundSpeed();
02921 
02922     //++++++++++++++++++++++++++++++++++++++++++++
02923     // now feed the above values into Auxiliary data structure
02924     //++++++++++++++++++++++++++++++++++++++++++++
02925 
02926     Auxiliary->Setalpha( alpha );
02927     Auxiliary->Setbeta ( beta  );
02928 
02929     if ((trimMode==taTurn)||(trimMode==taPullup)) Auxiliary->Setbeta( 0.0 );
02930 
02931     // ensure zero rates
02932     Auxiliary->Setadot( 0.0 );
02933     Auxiliary->Setbdot( 0.0 );
02934 
02935     // ToDo: take into account here the wind and other desired trim conditions
02936     Auxiliary->SetAeroPQR( FGColumnVector3(p,q,r)) ;
02937 
02938     // note assumes that trim_mode is triggered
02939     // assumes that p=q=r=0, or set by the appropriate taTrimMode context
02940 
02941     if ((trimMode==taTurn)||(trimMode==taPullup))
02942     {
02943         v=0.0; // no sideslip
02944     }
02945 
02946     JSBSim::FGColumnVector3 vUVWAero( ua,va,wa );
02947     Auxiliary->SetAeroUVW( vUVWAero );
02948 
02949     Auxiliary->Setqbar  ( qbar    );
02950     Auxiliary->SetqbarUV( qbarUV  );
02951     Auxiliary->SetqbarUW( qbarUW  );
02952 
02953     Auxiliary->SetVt    ( vtIC    );
02954 
02955     Auxiliary->SetMach  ( Mach    );
02956     Auxiliary->SetGamma ( gammaIC );
02957 
02958     // note: do not Auxiliary->Run(), otherwise dotted values
02959     //       _and_ aerodynamic angles are recalculated !!!
02960 
02961     //++++++++++++++++++++++++++++++++++++++++++++
02962     // Recalculate aerodynamics by taking into
02963     // account the above changes
02964     //++++++++++++++++++++++++++++++++++++++++++++
02965     FDMExec->GetAerodynamics()->Run();
02966 
02967     //++++++++++++++++++++++++++++++++++++++++++++
02968     // Recalculate propulsion forces & moments
02969     //++++++++++++++++++++++++++++++++++++++++++++
02970     //Propulsion->Run();
02971     Propulsion->GetSteadyState();
02972 
02973     //++++++++++++++++++++++++++++++++++++++++++++
02974     // Recalculate GroundReaction forces & moments
02975     //++++++++++++++++++++++++++++++++++++++++++++
02976     FDMExec->GetGroundReactions()->Run();
02977 
02978     //++++++++++++++++++++++++++++++++++++++++++++
02979     // Recalculate TOTAL Forces & Moments
02980     //++++++++++++++++++++++++++++++++++++++++++++
02981     FDMExec->GetAircraft()->Run();
02982 
02983     //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
02984     // now we have the forces & moments
02985     // we need some Propagate-like statements ... i.e. replicate what Propagate::Run() does
02986     // to finally get the "dotted" state
02987     //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
02988 
02989     VState.vLocation = JSBSim::FGLocation( FDMExec->GetPropagate()->GetLongitude(),            // location
02990                                            FDMExec->GetPropagate()->GetLatitude(),
02991                                            hIC + FDMExec->GetIC()->GetSeaLevelRadiusFtIC() );
02992 
02993     // _do not_ feed into Propagate at this level!!
02994     //FDMExec->GetPropagate()->SetLocation( VState.vLocation );
02995     // but pass to the caller the calculated state and dotted state
02996 
02997     VState.vUVW(1) = u; // u,v,w
02998     VState.vUVW(2) = v;
02999     VState.vUVW(3) = w;
03000 
03001     VState.vPQR(1) = p; // p,q,r
03002     VState.vPQR(2) = q;
03003     VState.vPQR(3) = r;
03004 
03005     //VState.vQtrn     = Quat;    // already fed above
03006 
03007     const FGColumnVector3 omega( 0.0, 0.0, FDMExec->GetInertial()->omega() ); // earth rotation
03008     const FGColumnVector3& vForces = FDMExec->GetAircraft()->GetForces();     // current forces
03009     const FGColumnVector3& vMoments = FDMExec->GetAircraft()->GetMoments();   // current moments
03010 
03011     double mass = FDMExec->GetMassBalance()->GetMass();                       // mass
03012     const FGMatrix33& J = FDMExec->GetMassBalance()->GetJ();                  // inertia matrix
03013     const FGMatrix33& Jinv = FDMExec->GetMassBalance()->GetJinv();            // inertia matrix inverse
03014 
03015     double rd = FDMExec->GetPropagate()->GetRadius();                         // radius
03016     if (rd == 0.0) {cerr << "radius = 0 !" << endl; rd = 1e-16;}              // radius check
03017 
03018     double rdInv = 1.0/rd;
03019     FGColumnVector3 gAccel( 0.0, 0.0, FDMExec->GetInertial()->GetGAccel(rd) );
03020 
03021     // The rotation matrices:
03022     const FGMatrix33& Tl2b  = VState.vQtrn.GetT();          // local to body frame
03023     const FGMatrix33& Tb2l  = VState.vQtrn.GetTInv();       // body to local frame
03024     const FGMatrix33& Tec2l = VState.vLocation.GetTec2l();  // earth centered to local frame
03025     const FGMatrix33& Tl2ec = VState.vLocation.GetTl2ec();  // local to earth centered frame
03026 
03027     // Inertial angular velocity measured in the body frame.
03028     //... const FGColumnVector3 pqri = VState.vPQR + Tl2b*(Tec2l*omega);
03029 
03030     // NOTE:   the trim is valid in flat-earth hypothesis,
03031     //         do not take into account the motion relative to the e.c.,
03032     //         consider only the motion wrt local frame
03033 
03034     const FGColumnVector3 pqri = VState.vPQR;
03035 
03036     const FGColumnVector3 vVel = Tb2l * VState.vUVW;
03037 
03038     //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
03039     // Finally compute the time derivatives of the vehicle state values:
03040     //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
03041 
03042     // Compute body frame rotational accelerations based on the current body moments
03043     vPQRdot = Jinv*(vMoments - pqri*(J*pqri)); // <------------------------ this goes out to the caller
03044 
03045     // Compute body frame accelerations based on the current body forces
03046     vUVWdot = VState.vUVW*VState.vPQR + vForces/mass; // <----------------- this goes out to the caller
03047 
03048     // Coriolis acceleration.
03049     FGColumnVector3 ecVel = Tl2ec*vVel;
03050     FGColumnVector3 ace = 2.0*omega*ecVel;
03051     //... vUVWdot -= Tl2b*(Tec2l*ace);
03052 
03053     if (!FDMExec->GetGroundReactions()->GetWOW()) {
03054         // Centrifugal acceleration.
03055         FGColumnVector3 aeec = omega*(omega*VState.vLocation);
03056     }
03057 
03058     // Gravitation accel
03059     vUVWdot += Tl2b*gAccel;
03060 
03061     // Compute vehicle velocity wrt EC frame, expressed in EC frame
03062     FGColumnVector3 vLocationDot = Tl2ec * vVel;
03063 
03064     FGColumnVector3 omegaLocal( rdInv*vVel(2), // East
03065                                -rdInv*vVel(1), // North
03066                                -rdInv*vVel(2)*VState.vLocation.GetTanLatitude() );
03067 
03068     // Compute quaternion orientation derivative on current body rates
03069     //... FGQuaternion vQtrndot = VState.vQtrn.GetQDot( VState.vPQR - Tl2b*omegaLocal );
03070     FGQuaternion vQtrndot = VState.vQtrn.GetQDot( VState.vPQR );
03071 
03072     /**********************************************************************************************
03073                                                                end of....     P R O P A G A T I O N
03074      **********************************************************************************************/
03075 
03076 }
03077 
03078 bool FGTrimAnalysis::getSteadyState(int nrepeat )
03079 {
03080   double currentThrust = 0, lastThrust=-1;
03081   int steady_count=0;
03082   bool steady=false;
03083 
03084   while ( !steady && steady_count <= nrepeat )
03085   {
03086       steady_count++;
03087       steady = Propulsion->GetSteadyState();
03088   }
03089   return steady;
03090 }
03091 
03092 //................................................................................
03093 } // JSBSim namespace