JSBSim Flight Dynamics Model  1.0 (02 March 2017)
An Open Source Flight Dynamics and Control Software Library in C++
FGTrimAnalysis.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Header: FGTrimAnalysis.cpp
4  Author: Agostino De Marco
5  Date started: Dec/14/2006
6 
7  ------------- Copyright (C) 2006 Agostino De Marco (agodemar@unina.it) -------
8 
9  This program is free software; you can redistribute it and/or modify it under
10  the terms of the GNU Lesser General Public License as published by the Free Software
11  Foundation; either version 2 of the License, or (at your option) any later
12  version.
13 
14  This program is distributed in the hope that it will be useful, but WITHOUT
15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17  details.
18 
19  You should have received a copy of the GNU Lesser General Public License along with
20  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21  Place - Suite 330, Boston, MA 02111-1307, USA.
22 
23  Further information about the GNU Lesser General Public License can also be found on
24  the world wide web at http://www.gnu.org.
25 
26  HISTORY
27 --------------------------------------------------------------------------------
28 12/14/06 ADM Created
29 
30 FUNCTIONAL DESCRIPTION
31 --------------------------------------------------------------------------------
32 
33 This class takes the given set of IC's and analyzes the possible trim states
34 of the aircraft, i.e. finds the aircraft state required to
35 maintain a specified flight condition. This flight condition can be
36 steady-level, a steady turn, a pull-up or pushover.
37 It is implemented using an iterative, direct search of a cost function minimum.
38 
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 SENTRY
41 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
42 
43 // !!!!!!! BEWARE ALL YE WHO ENTER HERE !!!!!!!
44 
45 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46 INCLUDES
47 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
48 
49 #include <stdlib.h>
50 
51 #include "FGFDMExec.h"
52 #include "models/FGAtmosphere.h"
53 #include "initialization/FGInitialCondition.h"
54 #include "FGTrimAnalysis.h"
55 #include "models/FGAircraft.h"
56 #include "models/FGMassBalance.h"
57 #include "models/FGGroundReactions.h"
58 #include "models/FGInertial.h"
59 #include "models/FGAerodynamics.h"
60 #include "math/FGColumnVector3.h"
61 
62 #include "input_output/FGPropertyManager.h"
63 #include "input_output/FGXMLParse.h"
64 
65 #include "models/propulsion/FGPiston.h"
66 #include "models/propulsion/FGPropeller.h"
67 #include "models/propulsion/FGTurbine.h"
68 
69 #include "math/FGTable.h"
70 
71 #if defined(sgi) && !defined(__GNUC__)
72 # include <math.h>
73 #else
74 # include <cmath>
75 #endif
76 #include <sstream>
77 #include <map>
78 
79 #include "math/direct_search/SMDSearch.h"
80 #include "math/direct_search/NMSearch.h"
81 #include "math/direct_search/vec.h"
82 
83 #if _MSC_VER
84 #pragma warning (disable : 4786 4788)
85 #endif
86 
87 namespace JSBSim {
88 
89 IDENT(IdSrc,"$Id: FGTrimAnalysis.cpp,v 1.16 2014/01/13 10:46:00 ehofman Exp $");
90 IDENT(IdHdr,ID_FGTRIMANALYSIS);
91 
92 
105  //friend void find_CostFunctionFull(long vars, Vector<double> &v, double & f,
106  void find_CostFunctionFull(long vars, Vector<double> &v, double & f,
107  bool & success, void* t_ptr)
108  {
109  (*(Objective*)t_ptr).CostFunctionFull(vars, v, f);
110  success = true;
111  }
124  //friend void find_CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f,
125  void find_CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f,
126  bool & success, void* t_ptr)
127  {
128  (*(Objective*)t_ptr).CostFunctionFullWingsLevel(vars, v, f);
129  success = true;
130  }
143  //friend void find_CostFunctionLongitudinal(long vars, Vector<double> &v, double & f,
144  void find_CostFunctionLongitudinal(long vars, Vector<double> &v, double & f,
145  bool & success, void* t_ptr)
146  {
147  (*(Objective*)t_ptr).CostFunctionLongitudinal(vars, v, f);
148  success = true;
149  }
162  //friend void find_CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f,
163  void find_CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f,
164  bool & success, void* t_ptr)
165  {
166  (*(Objective*)t_ptr).CostFunctionFullCoordinatedTurn(vars, v, f);
167  success = true;
168  }
181  //fryyiend void find_CostFunctionFullTurn(long vars, Vector<double> &v, double & f,
182  void find_CostFunctionFullTurn(long vars, Vector<double> &v, double & f,
183  bool & success, void* t_ptr)
184  {
185  (*(Objective*)t_ptr).CostFunctionFullTurn(vars, v, f);
186  success = true;
187  }
200  //friend void find_CostFunctionPullUp(long vars, Vector<double> &v, double & f,
201  void find_CostFunctionPullUp(long vars, Vector<double> &v, double & f,
202  bool & success, void* t_ptr)
203  {
204  (*(Objective*)t_ptr).CostFunctionPullUp(vars, v, f);
205  success = true;
206  }
207 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208 //public:
209 
210 Objective::Objective(FGFDMExec* fdmex, FGTrimAnalysis* ta, double x) : _x(x), TrimAnalysis(ta), FDMExec(fdmex)
211 {
212  // populate the map:type-of-trim/function-pointer
213  mpCostFunctions[taFull ] = find_CostFunctionFull;
214  mpCostFunctions[taFullWingsLevel ] = find_CostFunctionFullWingsLevel;
215  mpCostFunctions[taLongitudinal ] = find_CostFunctionLongitudinal;
216  mpCostFunctions[taTurn ] = find_CostFunctionFullCoordinatedTurn;
217  mpCostFunctions[taPullup ] = find_CostFunctionPullUp;
218 }
219 
220 void Objective::CostFunctionFull(long vars, Vector<double> &v, double & f)
221 {
222  if (vars != 7) {
223  cerr << "\nError: (Cost function for taFull mode) Dimension must be 7 !!\n";
224  exit(1);
225  }
226  if (TrimAnalysis->GetMode()!=taFull){
227  cerr << "\nError: must be taFull mode !!\n";
228  exit(1);
229  }
230 
231  f = myCostFunctionFull(v);
232  return;
233 }
234 
235 void Objective::CostFunctionFullWingsLevel(long vars, Vector<double> &v, double & f)
236 {
237  if (vars != 6) {
238  cerr << "\nError: (Cost function for taFullWingsLevel mode) Dimension must be 6 !!\n";
239  exit(1);
240  }
241  if (TrimAnalysis->GetMode()!=taFullWingsLevel){
242  cerr << "\nError: must be taFull mode !!\n";
243  exit(1);
244  }
245 
246  f = myCostFunctionFullWingsLevel(v);
247  return;
248 }
249 
250 void Objective::CostFunctionLongitudinal(long vars, Vector<double> &v, double & f)
251 {
252  if (vars != 3) {
253  cerr << "\nError: (Cost function for taLongitudinal mode) Dimension must be 3 !!\n";
254  exit(1);
255  }
256  if (TrimAnalysis->GetMode()!=taLongitudinal){
257  cerr << "\nError: trim mode must be taLongitudinal mode !!\n";
258  exit(1);
259  }
260 
261  f = myCostFunctionLongitudinal(v);
262  return;
263 }
264 
265 void Objective::CostFunctionFullCoordinatedTurn(long vars, Vector<double> &v, double & f)
266 {
267  if (vars != 5) {
268  cerr << "\nError: (Cost function for taTurn mode) Dimension must be 5 !!\n";
269  exit(1);
270  }
271  if (TrimAnalysis->GetMode()!=taTurn){
272  cerr << "\nError: trim mode must be taTurn mode !!\n";
273  exit(1);
274  }
275 
276  f = myCostFunctionFullCoordinatedTurn(v);
277  return;
278 }
279 
280 void Objective::CostFunctionFullTurn(long vars, Vector<double> &v, double & f)
281 {
282  if (vars != 6) {
283  cerr << "\nError: (Cost function for taTurn mode) Dimension must be 6 !!\n";
284  exit(1);
285  }
286  if (TrimAnalysis->GetMode()!=taTurnFull){
287  cerr << "\nError: trim mode must be taTurnFull ("<< (int)taTurnFull << ") mode !!\n";
288  exit(1);
289  }
290 
291  f = myCostFunctionFullTurn(v);
292  return;
293 }
294 
295 void Objective::CostFunctionPullUp(long vars, Vector<double> &v, double & f)
296 {
297  if (vars != 5) {
298  cerr << "\nError: (Cost function for taPullup mode) Dimension must be 5 !!\n";
299  exit(1);
300  }
301  if (TrimAnalysis->GetMode()!=taPullup){
302  cerr << "\nError: trim mode must be taPullup mode !!\n";
303  exit(1);
304  }
305 
306  f = myCostFunctionPullUp(v);
307  return;
308 }
309 
310 void Objective::Set_x_val(double new_x)
311 {
312  _x = new_x;
313 }
314 
315 //private:
316 // see the end of the file
317 
318 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319 
320 FGTrimAnalysis::FGTrimAnalysis(FGFDMExec *FDMExec,TrimAnalysisMode tt)
321 {
322  SetDebug(2);
323 
324  N=0;
325 
326  trim_failed = true;
327 
328  max_iterations=2500;
329  stop_criterion="Stop-On-Delta";
330 
331  Debug=0;DebugLevel=0;
332 
333  fdmex=FDMExec;
334 
335  fgic=fdmex->GetIC();
336  total_its=0;
337  trimudot=true;
338  gamma_fallback=true;
339  ctrl_count=0;
340  mode=tt;
341  _targetNlf=1.0;
342  _targetNlf=fgic->GetTargetNlfIC();
343 
344  _vtIC = fgic->GetVtrueFpsIC();
345  _hIC = fgic->GetAltitudeFtIC();
346  _gamma = fgic->GetFlightPathAngleRadIC();
347  _rocIC = _vtIC*cos(_gamma);
348  _vdownIC = _rocIC;
349 
350  // state variables
351  _u = fgic->GetUBodyFpsIC();
352  _v = fgic->GetVBodyFpsIC();
353  _w = fgic->GetWBodyFpsIC();
354  _p = fgic->GetPRadpsIC();
355  _q = fgic->GetQRadpsIC();
356  _r = fgic->GetRRadpsIC();
357  _alpha = fgic->GetAlphaRadIC();
358  _beta = fgic->GetBetaRadIC();
359  _theta = fgic->GetThetaRadIC();
360  _phi = fgic->GetPhiRadIC();
361  _psiIC = fgic->GetPsiRadIC();
362  _psi = _psiIC;
363  _psigtIC = _psi;
364 
365  _phiW = _psiW = 0.0;
366 
367  _vgIC = _vtIC*cos(_gamma);
368  _vnorthIC = _vgIC * cos(_psigtIC);
369  _veastIC = _vgIC * sin(_psigtIC);
370  wnorthIC = _weastIC = _wdownIC = 0.;
371 
372  _udot=_vdot=_wdot=_pdot=_qdot=_rdot=0.;
373  _psidot=_thetadot=0.;
374  _psiWdot = _phiWdot = _gammadot = 0.;
375 
376  C1 = C2 = C3 = 1.0;
377  _cbeta = cos(_beta);
378  _sbeta = sin(_beta);
379  _sphi = sin(_phi);
380 
381  SetMode(tt); // creates vTrimAnalysisControls
382  fdmex->SetTrimMode( (int)tt );
383 
384  trim_id = "default-trim";
385 
386  // direct search stuff
387  search_type = "Nelder-Mead";
388  sigma_nm = 0.5; alpha_nm = 1.0; beta_nm = 0.5; gamma_nm = 2.0;
389  initial_step = 0.01;
390  tolerance = 1.0E-10; // 0.0000000001
391  cost_function_value = 9999.0;
392 
393  rf_name = "";
394  if (rf.is_open()) rf.close();
395 
396  Auxiliary = fdmex->GetAuxiliary();
397  Aerodynamics = fdmex->GetAerodynamics();
398  Propulsion = fdmex->GetPropulsion();
399  FCS = fdmex->GetFCS();
400 }
401 
402 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403 
405 
406  ClearControls();
407 
408  vAlphaDeg.clear();
409  vCL.clear(); vCD.clear(); vCm.clear();
410  vThrottleCmd.clear(); vElevatorCmd.clear();
411  vVn.clear(); vTn.clear();
412 
413  if (rf.is_open()) rf.close();
414 
415  fdmex->SetTrimStatus(false);
416  fdmex->SetTrimMode( 99 );
417 }
418 
419 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420 
421 void FGTrimAnalysis::SetState(double u0, double v0, double w0, double p0, double q0, double r0,
422  double alpha0, double beta0, double phi0, double theta0, double psi0, double gamma0)
423 {
424  _u=u0; _v=v0; _w=w0;
425  _p=p0; _q=q0; _r=r0;
426  _alpha=alpha0; _beta=beta0; _gamma=gamma0;
427  _theta=theta0; _phi=phi0; _psi=psi0;
428 }
429 
430 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431 
432 void FGTrimAnalysis::SetEulerAngles(double phi, double theta, double psi)
433 {
434  // feed into private variables
435  _phi = phi; _cphi = cos(_phi); _sphi = sin(_phi);
436  _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta);
437  _psi = psi; _cpsi = cos(_psi); _spsi = sin(_psi);
438 
439 }
440 
441 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442 
443 void FGTrimAnalysis::SetDottedValues(double udot, double vdot, double wdot, double pdot, double qdot, double rdot)
444 {
445  _udot=udot; _vdot=vdot; _wdot=wdot; _pdot=pdot; _qdot=qdot; _rdot=rdot;
446 }
447 
448 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449 
450 bool FGTrimAnalysis::Load(string fname, bool useStoredPath)
451 {
452  string name="", type="";
453  string trimDef;
454  Element *element=0, *trimCfg=0, *search_element=0, *output_element=0;
455 
456  string sep = "/";
457 # ifdef macintosh
458  sep = ";";
459 # endif
460 
461  if( useStoredPath ) {
462  trimDef = fdmex->GetFullAircraftPath() + sep + fname + ".xml";
463  } else {
464  trimDef = fname;
465  }
466 
467  document = this->LoadXMLDocument(trimDef);
468 
469  trimCfg = document->FindElement("trim_config");
470  if (!trimCfg) {
471  cerr << "File: " << trimDef << " does not contain a trim configuration tag" << endl;
472  return false;
473  }
474 
475  name = trimCfg->GetAttributeValue("name");
476  trim_id = name;
477 
478  // First, find "search" element that specifies the type of cost function minimum search
479 
480  search_element = trimCfg->FindElement("search");
481  if (!search_element) {
482  cerr << "Using the Nelder-Mead search algorithm (default)." << endl;
483  } else {
484  type = search_element->GetAttributeValue("type");
485  if (type.size() > 0) search_type = type; // if search type is not set, default is already Nelder-Mead
486  if (search_type == "Nelder-Mead") {
487  // Read settings from search
488  // Note: all of these have defaults set above
489  if ( search_element->FindElement("sigma_nm") )
490  sigma_nm = search_element->FindElementValueAsNumber("sigma_nm");
491  if ( search_element->FindElement("alpha_nm") )
492  alpha_nm = search_element->FindElementValueAsNumber("alpha_nm");
493  if ( search_element->FindElement("beta_nm") )
494  beta_nm = search_element->FindElementValueAsNumber("beta_nm");
495  if ( search_element->FindElement("gamma_nm") )
496  gamma_nm = search_element->FindElementValueAsNumber("gamma_nm");
497  }
498  // ToDo: manage all the other possible choices here
499  // if (search_type == "Sequential-Multiple-Nelder-Mead") { }
500  // if (search_type == "Multicompass") { }
501  // etc ...
502  if ( search_element->FindElement("tolerance") ) {
503  tolerance = search_element->FindElement("tolerance")->GetAttributeValueAsNumber("value");
504  }
505  if ( search_element->FindElement("max_iterations") ) {
506  max_iterations = (unsigned int)search_element->FindElement("max_iterations")->GetAttributeValueAsNumber("value");
507  }
508  if ( search_element->FindElement("stop_criterion") ) {
509  stop_criterion = search_element->FindElement("stop_criterion")->GetAttributeValue("type");
510  }
511  }
512 
513  // Initialize trim controls based on what is in the trim config file. This
514  // includes initial trim values (or defaults from the IC file) and step
515  // size values.
516 
517  element = trimCfg->FindElement("phi");
518  InitializeTrimControl(fgic->GetPhiRadIC(), element, "RAD", JSBSim::taPhi);
519  if ( ( fabs(_phi) < 89.5*(FGJSBBase::degtorad ) ) && ( mode == taTurn ))
520  _targetNlf = 1./cos(_phi);
521 
522  element = trimCfg->FindElement("theta");
523  InitializeTrimControl(fgic->GetThetaRadIC(), element, "RAD", JSBSim::taTheta);
524 
525  element = trimCfg->FindElement("psi");
526  InitializeTrimControl(fgic->GetPsiRadIC(), element, "RAD", JSBSim::taHeading);
527 
528  element = trimCfg->FindElement("gamma");
529  _gamma = fgic->GetFlightPathAngleRadIC();
530  if (element)
531  if (element->GetNumDataLines() > 0) _gamma = element->GetDataAsNumber();
532 
533  element = trimCfg->FindElement("nlf");
534  if (element) {
535  if (element->GetNumDataLines() > 0) _targetNlf = element->GetDataAsNumber();
536  CalculatePhiWFromTargetNlfTurn(_targetNlf);
537  }
538 
539  element = trimCfg->FindElement("throttle_cmd");
540  if (element) InitializeTrimControl(0, element, "", JSBSim::taThrottle);
541 
542  element = trimCfg->FindElement("elevator_cmd");
543  if (element) InitializeTrimControl(0, element, "", JSBSim::taElevator);
544 
545  element = trimCfg->FindElement("rudder_cmd");
546  if (element) InitializeTrimControl(0, element, "", JSBSim::taRudder);
547 
548  element = trimCfg->FindElement("aileron_cmd");
549  if (element) InitializeTrimControl(0, element, "", JSBSim::taAileron);
550 
551  output_element = trimCfg->FindElement("output_file");
552  if (output_element) {
553  rf_name = output_element->GetAttributeValue("name");
554  if (rf_name.empty()) {
555  cerr << "name must be specified in output_file \"name\" attribute."<< endl;
556  } else {
557  if ( !SetResultsFile(rf_name) )
558  cerr << "Unable to use output file "<< rf_name << endl;
559  }
560  }
561  return true;
562 }
563 
564 
565 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566 
567 bool FGTrimAnalysis::InitializeTrimControl(double default_value, Element* el,
568  string unit, TaControl type)
569 {
570  Element *step_size_element=0, *trim_config=0;
571  double iv = 0.0;
572  double step = 0.0; // default step value
573  bool set_override = false;
574 
575  iv = default_value;
576 
577  if (el != 0) {
578  string name = el->GetName();
579  trim_config = el->GetParent();
580  set_override = el->GetNumDataLines() != 0;
581  if (set_override) {
582  if (unit.empty()) iv = trim_config->FindElementValueAsNumber(name);
583  else iv = trim_config->FindElementValueAsNumberConvertTo(name, unit);
584  }
585  if (el->GetAttributeValueAsNumber("step_size") != HUGE_VAL)
586  step = el->GetAttributeValueAsNumber("step_size");
587  }
588 
589  for (unsigned int i=0; i<vTrimAnalysisControls.size(); i++) {
590  if (vTrimAnalysisControls[i]->GetControlType() == type) {
591  vTrimAnalysisControls[i]->SetControlInitialValue(iv);
592  vTrimAnalysisControls[i]->SetControlStep(step);
593  break;
594  }
595  }
596 
597  return set_override;
598 }
599 
600 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
601 
603  int run_sum=0;
604  cout << endl << " Trim Statistics: " << endl;
605  cout << " Total Iterations: " << total_its << endl;
606 }
607 
608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609 
611 
612  cout << "---------------------------------------------------------------------\n";
613 
614  cout << "Trim report: " << endl;
615  cout << "\tTrim algorithm terminated with the following values:" << endl;
616  cout << "\tu, v, w (ft/s): " << _u <<", "<< _v <<", "<< _w << endl
617  << "\tp, q, r (rad/s): " << _p <<", "<< _q <<", "<< _r << endl
618  << "\talpha, beta (deg): " << _alpha*57.3 <<", "<< _beta*57.3 << endl
619  << "\tphi, theta, psi (deg): " << _phi*57.3 <<", "<< _theta*57.3 << ", " << _psi*57.3 << endl
620  << "\tCost function value : " << cost_function_value << endl
621  << "\tCycles executed : " << total_its << endl << endl;
622 
623  cout << "\tTrim variables adjusted:" << endl;
624  for (unsigned int i=0; i<vTrimAnalysisControls.size();i++){
625  cout << "\t\t" << vTrimAnalysisControls[i]->GetControlName() <<": ";
626  cout << vTrimAnalysisControls[i]->GetControl() << endl;
627  }
628  //...
629 
630  cout << endl;
631 
632  cout << "\t** Initial -> Final Conditions **" << endl;
633  cout << "\tAlpha IC: " << fgic->GetAlphaDegIC() << " Degrees" << endl;
634  cout << "\t Final: " << Auxiliary->Getalpha()*57.3 << " Degrees" << endl;
635  cout << "\tBeta IC: " << fgic->GetBetaDegIC() << " Degrees" << endl;
636  cout << "\t Final: " << Auxiliary->Getbeta()*57.3 << " Degrees" << endl;
637  cout << "\tGamma IC: " << fgic->GetFlightPathAngleDegIC() << " Degrees" << endl;
638  cout << "\t Final: " << Auxiliary->GetGamma()*57.3 << " Degrees" << endl;
639  cout << "\tPhi IC : " << fgic->GetPhiDegIC() << " Degrees" << endl;
640  cout << "\t Final: " << fdmex->GetPropagate()->GetEuler(1)*57.3 << " Degrees" << endl;
641  cout << "\tTheta IC: " << fgic->GetThetaDegIC() << " Degrees" << endl;
642  cout << "\t Final: " << fdmex->GetPropagate()->GetEuler(2)*57.3 << " Degrees" << endl;
643  cout << "\tPsi IC : " << fgic->GetPsiDegIC() << " Degrees" << endl;
644  cout << "\t Final: " << fdmex->GetPropagate()->GetEuler(3)*57.3 << " Degrees" << endl;
645  cout << endl;
646  cout << "--------------------------------------------------------------------- \n\n";
647 
648  fdmex->EnableOutput();
649 }
650 
651 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
652 
654 
656 
657  mode=taCustom;
658  vector<FGTrimAnalysisControl*>::iterator iControls;
659  iControls = vTrimAnalysisControls.begin();
660  while (iControls != vTrimAnalysisControls.end()) {
661  tac=*iControls;
662  delete tac;
663 
664  iControls++;
665  }
666 
667  vTrimAnalysisControls.clear();
668 
669 }
670 
671 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 
673 bool FGTrimAnalysis::AddControl( TaControl control ) {
674 
676  bool result=true;
677 
678  mode = taCustom;
679  vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin();
680  while (iControls != vTrimAnalysisControls.end()) {
681  tac=*iControls;
682  if( tac->GetControlType() == control )
683  result=false;
684  iControls++;
685  }
686  if(result) {
687  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,control));
688  }
689  return result;
690 }
691 
692 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
693 
694 bool FGTrimAnalysis::RemoveControl( TaControl control ) {
695 
697  bool result=false;
698 
699  mode = taCustom;
700  vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin();
701  while (iControls != vTrimAnalysisControls.end()) {
702  tac=*iControls;
703  if( tac->GetControlType() == control ) {
704  delete tac;
705  vTrimAnalysisControls.erase(iControls);
706  result=true;
707  continue;
708  }
709  iControls++;
710  }
711 
712  return result;
713 }
714 
715 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716 
717 bool FGTrimAnalysis::EditState( TaControl new_control, double new_initvalue, double new_step, double new_min, double new_max ) {
719  bool result=false;
720 
721  mode = taCustom;
722  vector <FGTrimAnalysisControl*>::iterator iControls = vTrimAnalysisControls.begin();
723  while (iControls != vTrimAnalysisControls.end()) {
724  tac=*iControls;
725  if( tac->GetControlType() == new_control ) {
726  vTrimAnalysisControls.insert(iControls,1,new FGTrimAnalysisControl(fdmex,fgic,new_control));
727  delete tac;
728  vTrimAnalysisControls.erase(iControls+1);
729  result=true;
730  break;
731  }
732  iControls++;
733  }
734  return result;
735 }
736 
737 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
738 
739 void FGTrimAnalysis::setupPullup() {
740  double g,q,cgamma;
741  g=fdmex->GetInertial()->gravity();
742  cgamma=cos(fgic->GetFlightPathAngleRadIC());
743  q=g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC();
744  cout << _targetNlf << ", " << q << endl;
745  fgic->SetQRadpsIC(q);
746  updateRates();
747 
748 }
749 
750 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751 
753  if ( ( mode == taTurn ) || ( mode == taTurnFull ) ) {
754  // target Nlf is given
755  // set _phiW according to given Nlf
756  _targetNlf = nlf;
757  _phiW = atan2( sqrt(_targetNlf*_targetNlf-cos(_gamma)*cos(_gamma)), cos(_gamma) );
758  }
759 }
760 
761 //ToDo: check formulas for nonzero gamma
762 void FGTrimAnalysis::setupTurn(void){
763 
764  if ( mode == taTurn ) {
765  // target Nlf is given
766  // set _phiW according to given Nlf
767  _phiW = atan2( sqrt(_targetNlf*_targetNlf-cos(_gamma)*cos(_gamma)), cos(_gamma) );
768  C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
769  sin(_phiW) *cos(_theta)*cos(_psi) ;
770  C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
771  cos(_phiW)*sin(_gamma)*sin(_theta) ;
772  C3 = sin(_phiW) *sin(_theta)+
773  cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ;
774  _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
775  /( sqrt(C1*C1 + C2*C2 + C3*C3) );
776  _sbeta = sqrt(1.-_cbeta*_cbeta );
777  _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
778  _phi = asin(_sphi);
779 
780  // theta_underlined (see paper on Trim) for coordinated turn
781  _theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) );
782 
783  double g,V;
784  V = sqrt( _u*_u +_v*_v + _w*_w );
785  g=fdmex->GetInertial()->gravity();
786  _psiWdot = (g/V)*tan(_phiW);
787  updateRates();
788  }
789  if ( mode == taTurnFull ) {
790  // target Nlf is given
791  C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
792  sin(_phiW) *cos(_theta)*cos(_psi) ;
793  C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
794  cos(_phiW)*sin(_gamma)*sin(_theta) ;
795  C3 = sin(_phiW) *sin(_theta)+
796  cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ;
797  _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
798  /( sqrt(C1*C1 + C2*C2 + C3*C3) );
799  _sbeta = sqrt(1.-_cbeta*_cbeta );
800  _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
801  _phi = asin(_sphi);
802 
803  double g,V;
804  V = sqrt( _u*_u +_v*_v + _w*_w );
805  g=fdmex->GetInertial()->gravity();
806  _psiWdot = (g/V)*tan(_phiW);
807  updateRates();
808  }
809 }
810 
811 void FGTrimAnalysis::setupTurn(double phiW){
812  if ( mode == taTurn ) {
813  _phiW = phiW;
814  // recalculate target Nlf
815  _targetNlf = sqrt( cos(_gamma)*cos(_gamma)*tan(_phiW)*tan(_phiW) + cos(_gamma)*cos(_gamma) );
816 
817  C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
818  sin(_phiW) *cos(_theta)*cos(_psi) ;
819  C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
820  cos(_phiW)*sin(_gamma)*sin(_theta) ;
821  C3 = sin(_phiW) *sin(_theta)+
822  cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ;
823  _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
824  /( sqrt(C1*C1 + C2*C2 + C3*C3) );
825  _sbeta = sqrt(1.-_cbeta*_cbeta );
826  _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
827  _phi = asin(_sphi);
828 
829  _cphi = cos(_phi);
830 
831  // theta_underlined (see paper on Trim) for coordinated turn
832  //_theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) );
833 
834  double g,V;
835  V = sqrt( _u*_u +_v*_v + _w*_w );
836  g=fdmex->GetInertial()->gravity();
837  _psiWdot = (g/V)*tan(_phiW);
838  updateRates();
839  }
840  if ( mode == taTurnFull ) {
841  _phiW = phiW;
842  // recalculate target Nlf
843  _targetNlf = sqrt( cos(_gamma)*cos(_gamma)*tan(_phiW)*tan(_phiW) + cos(_gamma)*cos(_gamma) );
844 
845  C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
846  sin(_phiW) *cos(_theta)*cos(_psi) ;
847  C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
848  cos(_phiW)*sin(_gamma)*sin(_theta) ;
849  C3 = sin(_phiW) *sin(_theta)+
850  cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ;
851  _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
852  /( sqrt(C1*C1 + C2*C2 + C3*C3) );
853  _sbeta = sqrt(1.-_cbeta*_cbeta );
854  _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
855  _phi = asin(_sphi);
856 
857  _cphi = cos(_phi);
858 
859  // theta_underlined (see paper on Trim) for coordinated turn
860  //_theta = atan2( sin(_psi)*cos(_gamma)+cos(_psi)*sin(_gamma), cos(_gamma) );
861 
862  double g,V;
863  V = sqrt( _u*_u +_v*_v + _w*_w );
864  g=fdmex->GetInertial()->gravity();
865  _psiWdot = (g/V)*tan(_phiW);
866  updateRates();
867  }
868 
869 }
870 
871 //ToDo: check formulas for nonzero gamma
872 void FGTrimAnalysis::setupTurnPhi(double psi, double theta){
873  if ( mode == taTurn ) {
874  _psi = psi; _cpsi = cos(_psi); _spsi = sin(_psi);
875  _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta);
876 
877  C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
878  sin(_phiW) *cos(_theta)*cos(_psi) ;
879  C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
880  cos(_phiW)*sin(_gamma)*sin(_theta) ;
881  C3 = sin(_phiW) *sin(_theta)+
882  cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ;
883  _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
884  /( sqrt(C1*C1 + C2*C2 + C3*C3) );
885  _sbeta = sqrt(1.-_cbeta*_cbeta );
886 
887  _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
888  _phi = asin(_sphi);
889  _cphi = cos(_phi);
890  }
891  if ( mode == taTurnFull ) {
892  _psi = psi; _cpsi = cos(_psi); _spsi = sin(_psi);
893  _theta = theta; _ctheta = cos(_theta); _stheta = sin(_theta);
894 
895  C1 = cos(_phiW)*sin(_gamma)*cos(_theta)*sin(_psi)+
896  sin(_phiW) *cos(_theta)*cos(_psi) ;
897  C2 = cos(_phiW)*cos(_gamma)*cos(_theta)*cos(_psi)+
898  cos(_phiW)*sin(_gamma)*sin(_theta) ;
899  C3 = sin(_phiW) *sin(_theta)+
900  cos(_phiW)*cos(_gamma)*cos(_theta)*sin(_psi) ;
901  _cbeta = ( C1*sin(_phiW)*cos(_gamma)+C2*cos(_phiW) + C3*sin(_phiW)*sin(_gamma) )
902  /( sqrt(C1*C1 + C2*C2 + C3*C3) );
903  _sbeta = sqrt(1.-_cbeta*_cbeta );
904 
905  _sphi = ( _cbeta*sin(_phiW) * cos(_gamma) - _sbeta*sin(_gamma) )/cos(_theta);
906  _phi = asin(_sphi);
907  _cphi = cos(_phi);
908 
909  }
910 }
911 
912 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
913 
914 FGColumnVector3 FGTrimAnalysis::UpdateRatesTurn(double psi, double theta, double phi, double phiW){
915  _psi = psi;
916  setupTurn(phiW); // calls updateRates: updates _p,_q,_r
917  return FGColumnVector3(_p,_q,_r);
918 }
919 
921  double g,cgamma;
922  g=fdmex->GetInertial()->gravity();
923  cgamma=cos(fgic->GetFlightPathAngleRadIC());
924  _p = 0;
925  _q = g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC();
926  _r = 0.;
927 
928  fgic->SetQRadpsIC(_q);
929  return FGColumnVector3(_p,_q,_r);
930 }
931 
932 void FGTrimAnalysis::updateRates(void){
933  if ( ( mode == taTurn ) || ( mode == taTurnFull ) ){
934  // this is the result of a Matlab symbolic expression
935  double cth2 = cos(_theta)*cos(_theta),
936  sth2 = sin(_theta)*sin(_theta),
937  scth = sin(_theta)*cos(_theta),
938  cph2 = cos(_phiW)*cos(_phiW),
939  sph2 = sin(_phiW)*sin(_phiW),
940  scph = sin(_phiW)*cos(_phiW),
941  cga2 = cos(_gamma)*cos(_gamma),
942  scga = sin(_gamma)*cos(_gamma),
943  cps2 = cos(_psi)*cos(_psi),
944  scps = sin(_psi)*cos(_psi);
945  _calpha =
946  sqrt(
947  1-cth2
948  +cph2*cth2
949  -2*scph*scth*cos(_gamma)*sin(_psi)+cph2*cga2*cth2
950  +cph2*cga2*cth2*(1-cps2)
951  +2*cph2*scga*scth*cos(_psi)
952  -cga2*cph2
953  -2*cph2*cth2*cps2
954  +2*scph*sin(_gamma)*cth2*scps
955  +cps2*cth2 ),
956  _salpha = sqrt( 1. - _calpha*_calpha );
957 
958  _p = -_psiWdot*
959  ( sin(_gamma)*_calpha*_cbeta
960  +cos(_gamma)*sin(_phiW)*_calpha*_sbeta
961  +cos(_gamma)*cos(_phiW)*_salpha
962  );
963 
964  _q = -_psiWdot*
965  ( sin(_gamma)*_sbeta
966  -cos(_gamma)*sin(_phiW)*_cbeta
967  );
968 
969  _r = -_psiWdot*
970  ( sin(_gamma)*_salpha*_cbeta
971  +cos(_gamma)*sin(_phiW)*_salpha*_sbeta
972  -cos(_gamma)*cos(_phiW)*_calpha
973  );
974 
975  } else if( mode == taPullup && fabs(_targetNlf-1) > 0.01) {
976 
977  double g,q,cgamma;
978  g=fdmex->GetInertial()->gravity();
979  cgamma=cos(fgic->GetFlightPathAngleRadIC());
980  q=g*(_targetNlf-cgamma)/fgic->GetVtrueFpsIC();
981  _q=q;
982  fgic->SetQRadpsIC(q);
983  }
984 }
985 
986 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
987 
988 void FGTrimAnalysis::setDebug(void) {
989  Debug=0;
990  return;
991 }
992 
993 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994 
995 void FGTrimAnalysis::SetMode(TrimAnalysisMode tt) {
996 
997  ClearControls();
998 
999  cout << "---------------------------------------------------------------------" << endl;
1000  cout << "Trim analysis performed: ";
1001  mode=tt;
1002  switch(tt) {
1003  case taLongitudinal:
1004  if (debug_lvl > 0)
1005  cout << " Longitudinal Trim" << endl;
1006  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
1007  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
1008  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
1009  break;
1010  case taFull:
1011  if (debug_lvl > 0)
1012  cout << " Full Trim" << endl;
1013  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
1014  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
1015  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); // TODO: taRollTrim
1016  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); // TODO: taYawTrim
1017  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
1018  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
1019  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
1020  break;
1021  case taFullWingsLevel:
1022  if (debug_lvl > 0)
1023  cout << " Full Trim, Wings-Level" << endl;
1024  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
1025  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
1026  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); // TODO: taRollTrim
1027  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); // TODO: taYawTrim
1028  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
1029  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
1030  break;
1031  case taTurn:
1032  // ToDo: set target NLF here !!!
1033  // ToDo: assign psiDot here !!
1034  if (debug_lvl > 0)
1035  cout << " Full Trim, Coordinated turn" << endl;
1036  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
1037  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
1038  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); // TODO: taRollTrim
1039  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); // TODO: taYawTrim
1040  //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
1041  //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
1042  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
1043  break;
1044  case taTurnFull:
1045  if (debug_lvl > 0)
1046  cout << " Non-coordinated Turn Trim" << endl;
1047  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
1048  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
1049  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron )); // TODO: taRollTrim
1050  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder )); // TODO: taYawTrim
1051  //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
1052  // calculate this from target nlf
1053  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
1054  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
1055  break;
1056  case taPullup:
1057  // ToDo: set target NLF here !!!
1058  // ToDo: assign qDot here !!
1059  if (debug_lvl > 0)
1060  cout << " Full Trim, Pullup" << endl;
1061  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taThrottle ));
1062  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taElevator )); // TODO: taPitchTrim
1063  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAileron ));
1064  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taRudder ));
1065  //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
1066  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
1067  //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taHeading ));
1068  break;
1069  case taGround:
1070  if (debug_lvl > 0)
1071  cout << " Ground Trim" << endl;
1072  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taAltAGL ));
1073  vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taTheta ));
1074  //vTrimAnalysisControls.push_back(new FGTrimAnalysisControl(fdmex,fgic,taPhi ));
1075  break;
1076  case taCustom:
1077  // agodemar...
1078  // ...agodemar
1079  case taNone:
1080  break;
1081  }
1082 
1083  current_ctrl=0;
1084 }
1085 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1086 
1088 {
1089  if ( rf.is_open() ) return false;
1090 
1091  rf_name = name;
1092  rf.open(rf_name.c_str(), ios::out);
1093  if ( !rf.is_open() ) {
1094  cerr << "Unable to open " << rf_name << endl;
1095  return false;
1096  }
1097  //rf << "# ... complete this " << endl;
1098  //rf << "# iteration, CostFunc, size, dT, dE, dA, dR, Psi (rad), Theta (rad), Phi (rad)\n";
1099  return true;
1100 }
1101 
1102 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103 
1104 bool FGTrimAnalysis::ensureRunning()
1105 {
1106  bool success = false;
1107 
1108  if ( Propulsion == 0L ) return false;
1109 
1110  for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
1111  {
1112  if (!Propulsion->GetEngine(i)->GetRunning() ) {
1113  Propulsion->GetEngine(i)->SetStarter( true );
1114  if ( Propulsion->GetEngine(i)->GetType() == JSBSim::FGEngine::etPiston )
1115  {
1116  FGPiston * Piston = (FGPiston*)Propulsion->GetEngine(i);
1117  Piston->SetMagnetos(3);
1118  }
1119  else if ( Propulsion->GetEngine(i)->GetType() == FGEngine::etTurbine )
1120  {
1121  FGTurbine * Turbine = (FGTurbine*)Propulsion->GetEngine(i);
1122  Turbine->SetCutoff(false);
1123  Turbine->SetStarter(true);
1124  Turbine->Calculate();
1125  }
1126  Propulsion->GetEngine(i)->SetRunning( true );
1127  Propulsion->Run();
1128  } else {
1129  success = true; // at least one engine is found in a running state
1130  Propulsion->SetActiveEngine(i);
1131  }
1132  }
1133  return success;
1134 }
1135 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1136 
1137 bool FGTrimAnalysis::ensureRunning(unsigned int i)
1138 {
1139  bool success = false;
1140 
1141  if ( Propulsion == 0L ) return false;
1142 
1143  if ( i < Propulsion->GetNumEngines() )
1144  {
1145  if (!Propulsion->GetEngine(i)->GetRunning() ) {
1146  Propulsion->GetEngine(i)->SetStarter( true );
1147  if ( Propulsion->GetEngine(i)->GetType() == JSBSim::FGEngine::etPiston )
1148  {
1149  FGPiston * Piston = (FGPiston*)Propulsion->GetEngine(i);
1150  Piston->SetMagnetos(3);
1151  }
1152  else if ( Propulsion->GetEngine(i)->GetType() == FGEngine::etTurbine )
1153  {
1154  FGTurbine * Turbine = (FGTurbine*)Propulsion->GetEngine(i);
1155  Turbine->SetCutoff(false);
1156  Turbine->SetStarter(true);
1157  Turbine->Calculate();
1158  }
1159  Propulsion->GetEngine(i)->SetRunning( true );
1160  Propulsion->Run();
1161 
1162  } else {
1163  success = true; // at least one engine is found in a running state
1164  Propulsion->SetActiveEngine(i);
1165  }
1166  }
1167  else success=false; // i not in range
1168 
1169  return success;
1170 }
1171 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1172 
1173 bool FGTrimAnalysis::runForAWhile(int nruns)
1174 {
1175  bool result = fdmex->Run(); // MAKE AN INITIAL RUN
1176 
1177  int counter = 0;
1178 
1179  // *** CYCLIC EXECUTION LOOP, AND MESSAGE READING *** //
1180  while ( counter < nruns ) { // (result && (counter < nruns))
1181  counter++;
1182  result = fdmex->Run();
1183  }
1184  return result;
1185 }
1186 
1187 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1188 bool FGTrimAnalysis::populateVecAlphaDeg(double vmin, double vmax, int n)
1189 {
1190  if ( !vAlphaDeg.empty() ) return false;
1191  for(int i=0; i<n; i++)
1192  vAlphaDeg.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) );
1193  return true;
1194 }
1195 
1196 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1197 bool FGTrimAnalysis::populateVecThrottleCmd(double vmin, double vmax, int n)
1198 {
1199  if ( !vThrottleCmd.empty() ) return false;
1200  for(int i=0; i<n; i++)
1201  vThrottleCmd.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) );
1202  return true;
1203 }
1204 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1205 bool FGTrimAnalysis::populateVecElevatorCmd(double vmin, double vmax, int n)
1206 {
1207  if ( !vElevatorCmd.empty() ) return false;
1208  for(int i=0; i<n; i++)
1209  vElevatorCmd.push_back( vmin + double(i)*(vmax - vmin)/double(n-1) );
1210  return true;
1211 }
1212 
1213 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1214 bool FGTrimAnalysis::calculateAerodynamics(
1215  double dE_cmd,
1216  double Vt,
1217  double alpha_deg,
1218  double altitude,
1219  double rho,
1220  double S, double mac, double bw,
1221  double& CL, double& CD, double& Cm)
1222 {
1223  double qBar = 0.5 * rho * Vt*Vt;
1224  double qBar_S = qBar * S;
1225 
1226  Auxiliary->SetVt( Vt );
1227  Auxiliary->Setalpha( alpha_deg*degtorad ); // ! note these are DEC converted to RAD!!
1228 
1229  Auxiliary->Setbeta( 0.0 );
1230 
1231  // ensure zero rates
1232  Auxiliary->Setadot( 0.0 );
1233  Auxiliary->Setbdot( 0.0 );
1234  Auxiliary->SetAeroPQR( FGColumnVector3(0.,0.,0.) ) ; // note assumes that trim_mode is triggered
1235  // note: do not Auxiliary->Run(), otherwise dotted values
1236  // _and_ aerodynamic angles are recalculated !!!
1237 
1238  // ensure the desired dE before aerodynamics
1239  FCS->SetDeCmd( dE_cmd );
1240  FCS->Run();
1241 
1242  Aerodynamics->Run();
1243 
1244  // get the Aerodynamics internat data
1245  vector <FGFunction*> * Coeff = Aerodynamics->GetAeroFunctions();
1246 
1247  if ( Coeff->empty() ) return false;
1248 
1249  CL = 0.;
1250  unsigned int axis_ctr = 2; // means LIFT, 0=DRAG, 1=SIDE, 3,4,5=ROLL,PITCH,YAW
1251  for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++)
1252  CL += Coeff[axis_ctr][ctr]->GetValue();
1253  CL /= qBar_S;
1254 
1255  CD = 0.;
1256  axis_ctr = 0; // means DRAG
1257  for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++)
1258  CD += Coeff[axis_ctr][ctr]->GetValue();
1259  CD /= qBar_S;
1260 
1261  Cm=0.;
1262  axis_ctr = 4; // means PITCH 0=DRAG, 1=SIDE, 2=LIFT, 3=ROLL, 4=PITCH, 5=YAW
1263  for (unsigned int ctr=0; ctr < Coeff[axis_ctr].size(); ctr++)
1264  Cm += Coeff[axis_ctr][ctr]->GetValue();
1265 
1266  Cm /= ( qBar_S*mac );
1267 
1268  return true;
1269 
1270 }
1271 
1272 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1273 
1275 
1276  // retrieve initial conditions
1277  fdmex->RunIC();
1278 
1279  cout << endl << "Numerical trim algorithm: constrained optimization of a cost function" << endl;
1280 
1281  Objective* obj_ptr = new Objective(this->fdmex, this, 999.0);
1282 
1283  fdmex->SetTrimStatus( true );
1284 
1285  //###################################
1286  // run for a while
1287  //###################################
1288 
1289  double tMin,tMax;
1290  double throttle = 1.0;
1291  for (unsigned i=0;i<Propulsion->GetNumEngines();i++)
1292  {
1293  tMin=Propulsion->GetEngine(i)->GetThrottleMin();
1294  tMax=Propulsion->GetEngine(i)->GetThrottleMax();
1295  FCS->SetThrottleCmd(i,tMin + throttle *(tMax-tMin));
1296  if (Propulsion->GetEngine(i)->GetType()==FGEngine::etPiston)
1297  {
1298  FCS->SetMixtureCmd(i,0.87);
1299  }
1300  if (Propulsion->GetEngine(i)->GetType()==FGEngine::etTurbine)
1301  {
1302  ((FGTurbine*)Propulsion->GetEngine(i))->SetCutoff(false);
1303  ((FGTurbine*)Propulsion->GetEngine(i))->SetPhase(FGTurbine::tpRun); // tpStart
1304  }
1305 
1306  FCS->Run(); // apply throttle change
1307  }
1308  Propulsion->GetSteadyState(); // GetSteadyState processes all engines
1309 
1310  //--------------------------------------------------------
1311 
1312  for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
1313  {
1314  int engineStartCount = 0;
1315  bool engine_started = false;
1316 
1317  int n_attempts = 100;
1318  if (Propulsion->GetEngine(i)->GetType()==FGEngine::etTurbine)
1319  n_attempts = 5000; // a turbine engine, e.g. 737's cfm56, takes a little longer to get running
1320 
1321  while ( !engine_started && (engineStartCount < n_attempts) )
1322  {
1323  engine_started = ensureRunning(i);
1324  fdmex->Run();
1325  engineStartCount++;
1326  }
1327 
1328  }
1329 
1330  //--------------------------------------------------------
1331 
1332  Propulsion->GetSteadyState();
1333 
1334  fdmex->SetDebugLevel(0); // 4
1335 
1336  //#########################################################################
1337 
1338  trim_failed=false;
1339 
1340  for(int i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++){
1341  fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(false);
1342  }
1343 
1344  fdmex->DisableOutput();
1345 
1346  fgic->SetPRadpsIC(0.0);
1347  fgic->SetQRadpsIC(0.0);
1348  fgic->SetRRadpsIC(0.0);
1349 
1350  if(mode == taPullup ) {
1351  setupPullup(); // also calls updateRates()
1352  } else if (mode == taTurn) {
1353  setupTurn(); // also calls updateRates()
1354  } else if (mode == taTurnFull) {
1355  setupTurn(); // also calls updateRates()
1356  } else {
1357  fgic->SetPRadpsIC(0.0);
1358  fgic->SetQRadpsIC(0.0);
1359  fgic->SetRRadpsIC(0.0);
1360  _p = _q = _r = 0.;
1361  }
1362 
1363  // ** DO HERE THE TRIM ** //
1364 
1365  //---------------------------------------------------------------------------
1366  // REMINDER:
1367  // n. of control variables for full trim (taFull): 7
1368  // ordering: the four commands first, then the three Euler angles,
1369  // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
1370  // [4] phi, [5] theta, [6] psi (alias taHeading)
1371  //-----------------------------------------------------------------------------------------------------------------
1372 
1373  // re-run ICs
1374  fdmex->RunIC();
1375 
1376  // write trim results on file, rf=results file
1377  if ( rf.is_open() )
1378  rf <<
1379  "# 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"
1380  << endl;
1381 
1382  long n = vTrimAnalysisControls.size(); // number of variables (dimension of the search)
1383 
1384  /* we'll initialize an n-entry Vector whose value is startVal,
1385  * and use it as our starting point.
1386  */
1387 
1388  // a Vec, needed in the search constructor
1389  Vector<double>* minVec = new Vector<double>(n, 0.0);
1390 
1391  // a Vec needed to store the minimum point later
1392  Vector<double>* Sminimum;
1393  new_Vector_Init(Sminimum,*minVec);
1394 
1395  /*
1396  * now construct the search object:
1397  * NMS = Nelder-Mead Search algorithm;
1398  * if n parameters have to be adjusted, then user has to provide
1399  * n+1 initial combinations where the cost function is going to be sampled.
1400  * The class has the capability of automatically initialize those points,
1401  * but here I prefer giving them explicitly so that the initial steps in
1402  * all parameter "directions" are well defined.
1403  */
1404 
1405  NMSearch NMS(
1406  n, *minVec, /* minVec, */
1407  sigma_nm, alpha_nm, beta_nm, gamma_nm,
1408  initial_step, tolerance,
1409  //find_CostFunctionFull, // gets the proper member function from the
1410  // object of class Objective (see main_obj.cc from DirectSearch examples)
1411  0L, // gets the proper member function from the
1412  (void *)obj_ptr
1413  );
1414 
1415  if ( GetMode()==taLongitudinal ) {
1416  NMS.SetFcnName(find_CostFunctionLongitudinal);
1417  }
1418  if ( GetMode()==taFull ) {
1419  NMS.SetFcnName(find_CostFunctionFull);
1420  }
1421  if ( GetMode()==taFullWingsLevel ) {
1422  NMS.SetFcnName(find_CostFunctionFullWingsLevel);
1423  }
1424  if ( GetMode()==taTurn ) {
1425  NMS.SetFcnName(find_CostFunctionFullCoordinatedTurn);
1426  }
1427  if ( GetMode()==taTurnFull ) {
1428  NMS.SetFcnName(find_CostFunctionFullTurn);
1429  }
1430  if ( GetMode()==taPullup ) {
1431  NMS.SetFcnName(find_CostFunctionPullUp);
1432  }
1433 
1434  //-----------------------------------------
1435  // initialize simplex (n+1 conditions)
1436  //-----------------------------------------
1437  // InitGeneralSimplex(plex) where...
1438  // Matrix<double> *plex = NULL;
1439  // new_Matrix(plex, n+1,n); // create one more row, see Dyn_alloc.h
1440  // ...
1441 
1442  //------------------------------------------------------------------------------
1443  // In this stringstream we I put the n+1 sequences, each of n values (n-ples)
1444  // The stream is then used to initialize the simplex by a call to the method
1445  // ReadInFile
1446  //------------------------------------------------------------------------------
1447  stringstream ss (stringstream::in | stringstream::out);
1448 
1449  // first feed the trial minimizer, zeroth point of the simplex
1450  for (unsigned int k=0; k<vTrimAnalysisControls.size();k++)
1451  ss << vTrimAnalysisControls[k]->GetControlInitialValue() << " ";
1452 
1453  // then the rest of n-ples
1454  for (unsigned int k=0; k<vTrimAnalysisControls.size();k++)
1455  {
1456  for(vector<FGTrimAnalysisControl*>::iterator vi=vTrimAnalysisControls.begin();
1457  vi!=vTrimAnalysisControls.end();vi++) {
1458  if ( (*vi)->GetControlType()==vTrimAnalysisControls[k]->GetControlType() )
1459  ss << (*vi)->GetControlInitialValue() + (*vi)->GetControlStep() << " ";
1460  else
1461  ss << (*vi)->GetControlInitialValue() << " ";
1462  }// this complets the k-th n-ple
1463  }// this completes the (N+1) n-ples
1464 
1465  NMS.ReadInFile(ss); // assign the initial combinations of parameters
1466 
1467  /*
1468  If Stop_on_std is set to false in an NMSearch, the standard-deviation test is used until that test is met.
1469  Then (and in subsequent iterations of the same search) the more expensive ``delta'' test will be used.
1470  We realize that there will be times when the standard deviation test will actually take more function calls than
1471  the ``delta'' test, but this is not typically the case, and we make this compromise for the sake of efficiency.
1472  */
1473 
1474  NMS.SetMaxCalls(max_iterations);
1475  //NMS.SetMaxCallsExact(20000);
1476  if ( stop_criterion=="Stop-On-Std" )
1477  NMS.Set_Stop_on_std();
1478  if ( stop_criterion=="Stop-On-Delta" )
1479  NMS.Set_Stop_on_delta();
1480 
1481  double SMinVal;
1482  long Scalls;
1483 
1484 // NMS.PrintDesign();
1485 
1486  fdmex->SetDebugLevel(0);
1487 
1488  /* start searching */
1489  NMS.BeginSearch(); // stops when the simplex shrinks to a point
1490 
1491  // WATCH this !!
1492  delete obj_ptr;
1493 
1494  /* recover information about the search */
1495  NMS.GetMinPoint(*Sminimum);
1496  NMS.GetMinVal(SMinVal);
1497  Scalls = NMS.GetFunctionCalls();
1498 
1499  // Apply the set of controls found by the minimization procedure
1500 
1501  if ( ( mode == taFull ) ||
1502  ( mode == taTurnFull ) )
1503  {
1504  FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
1505  FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons
1506  FCS->SetDrCmd( (*Sminimum)[3] ); // rudder
1507  double tMin,tMax;
1508  for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
1509  {
1510  tMin=Propulsion->GetEngine(i)->GetThrottleMin();
1511  tMax=Propulsion->GetEngine(i)->GetThrottleMax();
1512  FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
1513  FCS->Run(); // apply throttle change
1514  }
1515  Propulsion->GetSteadyState(); // GetSteadyState processes all engines
1516  FCS->Run(); // apply throttle, yoke & pedal changes
1517 
1518  FGQuaternion quat( (*Sminimum)[4], (*Sminimum)[5], (*Sminimum)[6] ); // phi, theta, psi
1519  quat.Normalize();
1520 
1521  fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
1522 
1523  FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
1524  vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
1525  fdmex->GetPropagate()->SetVState(vstate);
1526  Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
1527  Auxiliary->Setbeta ( _beta );
1528 
1529  // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
1530  /* */
1531  //fdmex->RunIC();
1532  fdmex->Run();
1533 
1534  } // end taFull
1535 
1536  if ( mode == taLongitudinal )
1537  {
1538  FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
1539  double tMin,tMax;
1540  for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
1541  {
1542  tMin=Propulsion->GetEngine(i)->GetThrottleMin();
1543  tMax=Propulsion->GetEngine(i)->GetThrottleMax();
1544  FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
1545  FCS->Run(); // apply throttle change
1546  }
1547  Propulsion->GetSteadyState(); // GetSteadyState processes all engines
1548  FCS->Run(); // apply throttle, yoke & pedal changes
1549 
1550  FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi
1551  quat.Normalize();
1552 
1553  // enforce the current state to IC object
1554 
1555  fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
1556 
1557  // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
1558  // which means that we populate IC private variables with our set of values
1559  //fdmex->RunIC();
1560 
1561  FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
1562  vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
1563  fdmex->GetPropagate()->SetVState(vstate);
1564  Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
1565  Auxiliary->Setbeta ( _beta );
1566 
1567  fdmex->Run();
1568 
1569  } // end taLongitudinal
1570 
1571  if ( mode == taFullWingsLevel)
1572  {
1573  FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
1574  FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons
1575  FCS->SetDrCmd( (*Sminimum)[3] ); // rudder
1576 
1577  double tMin,tMax;
1578  for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
1579  {
1580  tMin=Propulsion->GetEngine(i)->GetThrottleMin();
1581  tMax=Propulsion->GetEngine(i)->GetThrottleMax();
1582  FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
1583  FCS->Run(); // apply throttle change
1584  }
1585  Propulsion->GetSteadyState(); // GetSteadyState processes all engines
1586  FCS->Run(); // apply throttle, yoke & pedal changes
1587 
1588  FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi
1589  quat.Normalize();
1590 
1591  //...
1592  // enforce the current state to IC object
1593 
1594  fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
1595 
1596  // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
1597  // which means that we populate IC private variables with our set of values
1598  //fdmex->RunIC();
1599 
1600  FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
1601  vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
1602  fdmex->GetPropagate()->SetVState(vstate);
1603  Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
1604  Auxiliary->Setbeta ( _beta );
1605 
1606  fdmex->Run();
1607  }// end taFullWingsLevel
1608 
1609  if ( mode == taTurn )
1610  {
1611  FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
1612  FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons
1613  FCS->SetDrCmd( (*Sminimum)[3] ); // rudder
1614 
1615  double tMin,tMax;
1616  for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
1617  {
1618  tMin=Propulsion->GetEngine(i)->GetThrottleMin();
1619  tMax=Propulsion->GetEngine(i)->GetThrottleMax();
1620  FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
1621  FCS->Run(); // apply throttle change
1622  }
1623  Propulsion->GetSteadyState(); // GetSteadyState processes all engines
1624  FCS->Run(); // apply throttle, yoke & pedal changes
1625 
1626  FGQuaternion quat( fgic->GetPhiRadIC(), (*Sminimum)[2], fgic->GetPsiRadIC() ); // phi, theta, psi
1627  quat.Normalize();
1628 
1629  //...
1630  // enforce the current state to IC object
1631 
1632  fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
1633 
1634  // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
1635  // which means that we populate IC private variables with our set of values
1636  //fdmex->RunIC();
1637 
1638  FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
1639  vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
1640  fdmex->GetPropagate()->SetVState(vstate);
1641  Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
1642  Auxiliary->Setbeta ( 0.0 );
1643  Auxiliary->SetGamma( fgic->GetFlightPathAngleRadIC() );
1644 
1645  fdmex->Run();
1646  }// end ta turn
1647 
1648  if ( mode == taPullup )
1649  {
1650  FCS->SetDeCmd( (*Sminimum)[1] ); // elevator
1651  FCS->SetDaCmd( (*Sminimum)[2] ); // ailerons
1652  FCS->SetDrCmd( (*Sminimum)[3] ); // rudder
1653 
1654  double tMin,tMax;
1655  for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
1656  {
1657  tMin=Propulsion->GetEngine(i)->GetThrottleMin();
1658  tMax=Propulsion->GetEngine(i)->GetThrottleMax();
1659  FCS->SetThrottleCmd(i,tMin + (*Sminimum)[0] *(tMax-tMin));
1660  FCS->Run(); // apply throttle change
1661  }
1662  Propulsion->GetSteadyState(); // GetSteadyState processes all engines
1663  FCS->Run(); // apply throttle, yoke & pedal changes
1664 
1665  FGQuaternion quat( 0, (*Sminimum)[2], fgic->GetPRadpsIC() ); // phi, theta, psi
1666  quat.Normalize();
1667 
1668  //...
1669  // enforce the current state to IC object
1670 
1671  fgic->ResetIC(_u, _v, _w, _p, _q, _r, _alpha, _beta, _phi, _theta, _psi, _gamma);
1672 
1673  // NOTE: _do not_ fdmex->RunIC() here ! We just reset the state
1674  // which means that we populate IC private variables with our set of values
1675  //fdmex->RunIC();
1676 
1677  FGPropagate::VehicleState vstate = fdmex->GetPropagate()->GetVState();
1678  vstate.vQtrn = FGQuaternion(_phi,_theta,_psi);
1679  fdmex->GetPropagate()->SetVState(vstate);
1680  Auxiliary->Setalpha( _alpha ); // need to get Auxiliary updated
1681  Auxiliary->Setbeta ( 0.0 );
1682  Auxiliary->SetGamma( fgic->GetFlightPathAngleRadIC() );
1683 
1684  fdmex->Run();
1685  }//end taPullup
1686 
1687  delete minVec;
1688  delete Sminimum;
1689 
1690  //-----------------------------------------------------------------------------------------------------------------
1691 
1692  total_its = NMS.GetFunctionCalls();
1693 
1694  if( !trim_failed ) {
1695  if (debug_lvl > 0) {
1696  cout << endl << " Trim successful. (Cost function value: " << cost_function_value << ")" << endl;
1697  }
1698  } else {
1699  if (debug_lvl > 0)
1700  cout << endl << " Trim failed" << endl;
1701  }
1702 
1703 
1704  for (unsigned int i=0; i<(unsigned int)fdmex->GetGroundReactions()->GetNumGearUnits();i++){
1705  fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(true);
1706  }
1707 
1708  return !trim_failed;
1709 }
1710 
1711 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1712 
1713 double Objective::myCostFunctionFull(Vector<double> & x) // x variations come from the search algorithm
1714 {
1715 
1716  //----------------------------------------------------------------------------
1717  // NOTE:
1718  // when the program flow arrives here, it is assumed that a successful
1719  // instantiation of a TrimAnalysis object took place with a mode set
1720  // to taFull, i.e. 7 independent variables to be adjusted
1721  //----------------------------------------------------------------------------
1722 
1723  //----------------------------------------------------------------------------
1724  // NOTE:
1725  // rely on the fact that IC quantities have been fed at least once into
1726  // their correspondent data structure in TrimAnalysis->fdmex tree
1727  //----------------------------------------------------------------------------
1728 
1729  double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
1730 
1731  double theta, phi, psi;
1732 
1733  double alpha = 0.0, beta = 0.0, gamma = 0.0;
1734 
1735  FGQuaternion Quat;
1736  FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
1737 
1739  VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
1740  VState.vUVW = FGColumnVector3(0.0,0.0,0.0);
1741  VState.vPQR = FGColumnVector3(0.0,0.0,0.0);
1742  VState.vQtrn = FGQuaternion (0.0,0.0,0.0);
1743 
1744  //----------------------------------------------------------------------------
1745  // Minimization control vector, i.e. independent variables
1746  //----------------------------------------------------------------------------
1747 
1748  // CHECK the trim mode
1749 
1750  //-----------------------------------------------------------------------------------------------------------------
1751  // REMINDER:
1752  // n. of control variables for full trim (taFull): 7
1753  // ordering: the four commands first, then the three Euler angles,
1754  // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
1755  // [4] phi, [5] theta, [6] psi (alias taHeading)
1756  //-----------------------------------------------------------------------------------------------------------------
1757 
1758  delta_cmd_T = x[0]; // throttle, cmd
1759  delta_cmd_E = x[1]; // elevator, cmd
1760  delta_cmd_A = x[2]; // aileron, cmd
1761  delta_cmd_R = x[3]; // rudder, cmd
1762 
1763  phi = x[4]; // rad
1764  theta = x[5];
1765  psi = x[6];
1766 
1767  TrimAnalysis->SetEulerAngles(phi, theta, psi);
1768 
1769  //----------------------------------------------------------------------------
1770  // parameter bound check
1771  // ToDo: manage bounds via the FGTrimAnalysisControl class
1772  //----------------------------------------------------------------------------
1773  bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) )
1774  || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) )
1775  || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) )
1776  || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) )
1777  || ( (psi < 0.0 ) || (psi > 2.0*M_PI) )
1778  || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) )
1779  || ( (phi < - M_PI) || (phi > M_PI) )
1780  ;
1781 
1782  if ( penalty ) {
1783  //-------------------------------------------------
1784  // just return an "infinite" value
1785  return HUGE_VAL;
1786  //-------------------------------------------------
1787  } else {calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
1788  phi, theta, psi,
1789  TrimAnalysis->GetMode(),
1790  alpha, beta, gamma,
1791  VState,
1792  vUVWdot, vPQRdot );
1793  }
1794 
1795  double u , v , w ,
1796  p , q , r ,
1797  uDot, vDot, wDot,
1798  pDot, qDot, rDot;
1799  u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3);
1800  p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3);
1801  uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3);
1802  pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3);
1803 
1804  double
1805  f = 1.000*uDot*uDot
1806  + vDot*vDot
1807  + 1.000*wDot*wDot
1808  + 0.010*pDot*pDot
1809  + 0.010*qDot*qDot
1810  + 0.010*rDot*rDot;
1811 
1812  static int count = 0;
1813  count++;
1814 
1815  if ( f < TrimAnalysis->GetCostFunctionValue() )
1816  {
1817  // feed into the vector of TrimAnalysis Controls the current values
1818  vector<FGTrimAnalysisControl*>::iterator vi;
1819  for(vi=TrimAnalysis->GetControls()->begin();
1820  vi!=TrimAnalysis->GetControls()->end();vi++)
1821  {
1822  if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T);
1823  if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim
1824  if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim
1825  if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim
1826  if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi);
1827  if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta);
1828  if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi);
1829  }
1830 
1831  if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
1832 
1833  // write on file, if open
1834 
1835  // "# iteration, costf,
1836  // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
1837  // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
1838  // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
1839  // alphaDot (rad/s), betaDot (rad/s), Thrust"
1840 
1841  ofstream* rfp = TrimAnalysis->GetResultsFile();
1842  if (rfp)
1843  (*rfp)
1844  << count <<", "<< f <<", " // 1, 2
1845  << delta_cmd_T <<", "<< delta_cmd_E <<", "<< delta_cmd_A <<", "<< delta_cmd_R <<", "
1846  // 3, 4, 5, 6
1847  << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9
1848  << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12
1849  << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15
1850  << u <<", "<< v <<", "<< w << ", " // 16, 17, 18
1851  << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
1852  // 19, 20, 21
1853  << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22
1854  << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23
1855  << FDMExec->GetAuxiliary()->Getadot() << ", " // 24
1856  << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25
1857 
1858  << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; //26
1859 
1860  // this really goes into the integration process
1861  FDMExec->GetPropagate()->SetVState( VState );
1862 
1863  // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
1864  TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
1865 
1866  TrimAnalysis->SetCostFunctionValue(f);
1867  }
1868 
1869  return f;
1870 }
1871 
1872 
1873 double Objective::myCostFunctionFullWingsLevel(Vector<double> & x) // x variations come from the search algorithm
1874 {
1875 
1876  //----------------------------------------------------------------------------
1877  // NOTE:
1878  // when the program flow arrives here, it is assumed that a successful
1879  // instantiation of a TrimAnalysis object took place with a mode set
1880  // to taFullWingsLevel, i.e. 6 independent variables to be adjusted
1881  // ___phi is set to zero because we want wings leveled___
1882  //----------------------------------------------------------------------------
1883 
1884  //----------------------------------------------------------------------------
1885  // NOTE:
1886  // rely on the fact that IC quantities have been fed at least once into
1887  // their correspondent data structure in TrimAnalysis->fdmex tree
1888  //----------------------------------------------------------------------------
1889 
1890  double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
1891 
1892  double theta, phi, psi;
1893 
1894  double alpha = 0.0, beta = 0.0, gamma = 0.0;
1895 
1896  FGQuaternion Quat;
1897  FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
1898 
1900  VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
1901  VState.vUVW = FGColumnVector3(0.0,0.0,0.0);
1902  VState.vPQR = FGColumnVector3(0.0,0.0,0.0);
1903  VState.vQtrn = FGQuaternion (0.0,0.0,0.0);
1904 
1905  //----------------------------------------------------------------------------
1906  // Minimization control vector, i.e. independent variables
1907  //----------------------------------------------------------------------------
1908 
1909  // CHECK the trim mode
1910 
1911  //-----------------------------------------------------------------------------------------------------------------
1912  // REMINDER:
1913  // n. of control variables for full trim (taFullWingsLevel): 6
1914  // ordering: the four commands first, then the three Euler angles,
1915  // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
1916  // [4] theta, [5] psi (alias taHeading)
1917  //-----------------------------------------------------------------------------------------------------------------
1918 
1919  delta_cmd_T = x[0]; // throttle, cmd
1920  delta_cmd_E = x[1]; // elevator, cmd
1921  delta_cmd_A = x[2]; // aileron, cmd
1922  delta_cmd_R = x[3]; // rudder, cmd
1923 
1924  phi = 0.0 ; // rad, this enforces the wings-level condition
1925  theta = x[4];
1926  psi = x[5];
1927 
1928  TrimAnalysis->SetEulerAngles(phi, theta, psi);
1929 
1930  //----------------------------------------------------------------------------
1931  // parameter bound check
1932  // ToDo: manage bounds via the FGTrimAnalysisControl class
1933  //----------------------------------------------------------------------------
1934  bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) )
1935  || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) )
1936  || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) )
1937  || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) )
1938  || ( (psi < 0.0 ) || (psi > 2.0*M_PI) )
1939  || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) )
1940  // || ( (phi < - M_PI) || (phi > M_PI) ) ...always zero here
1941  ;
1942 
1943  if ( penalty ) {
1944  //-------------------------------------------------
1945  // just return an "infinite" value
1946  return HUGE_VAL;
1947  //-------------------------------------------------
1948  } else {
1949  calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
1950  phi, theta, psi,
1951  TrimAnalysis->GetMode(),
1952  alpha, beta, gamma,
1953  VState,
1954  vUVWdot, vPQRdot );
1955  }
1956 
1957  double u , v , w ,
1958  p , q , r ,
1959  uDot, vDot, wDot,
1960  pDot, qDot, rDot;
1961  u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3);
1962  p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3);
1963  uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3);
1964  pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3);
1965 
1966  double
1967  f = 1.000*uDot*uDot
1968  + vDot*vDot
1969  + 1.000*wDot*wDot
1970  + 0.010*pDot*pDot
1971  + 0.010*qDot*qDot
1972  + 0.010*rDot*rDot;
1973 
1974  static int count = 0;
1975  count++;
1976 
1977  if ( f < TrimAnalysis->GetCostFunctionValue() )
1978  {
1979  // feed into the vector of TrimAnalysis Controls the current values
1980  vector<FGTrimAnalysisControl*>::iterator vi;
1981  for(vi=TrimAnalysis->GetControls()->begin();
1982  vi!=TrimAnalysis->GetControls()->end();vi++)
1983  {
1984  if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T);
1985  if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim
1986  if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim
1987  if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim
1988  if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi);
1989  if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta);
1990  if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi);
1991  }
1992 
1993  if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
1994 
1995  // write on file, if open
1996 
1997  // "# iteration, costf,
1998  // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
1999  // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
2000  // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
2001  // alphaDot (rad/s), betaDot (rad/s), Thrust"
2002 
2003  ofstream* rfp = TrimAnalysis->GetResultsFile();
2004  if (rfp)
2005  (*rfp)
2006  << count <<", "<< f <<", " // 1, 2
2007  << delta_cmd_T <<", "<< delta_cmd_E <<", "<< delta_cmd_A <<", "<< delta_cmd_R <<", "
2008  // 3, 4, 5, 6
2009  << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9
2010  << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12
2011  << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15
2012  << u <<", "<< v <<", "<< w << ", " // 16, 17, 18
2013  << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
2014  // 19, 20, 21
2015  << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22
2016  << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23
2017  << FDMExec->GetAuxiliary()->Getadot() << ", " // 24
2018  << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25
2019 
2020  << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 26
2021 
2022  // this influences really into the further integration process
2023  FDMExec->GetPropagate()->SetVState( VState );
2024 
2025  // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
2026  TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
2027 
2028  TrimAnalysis->SetCostFunctionValue(f);
2029  }
2030 
2031  return f;
2032 }
2033 
2034 double Objective::myCostFunctionLongitudinal(Vector<double> & x)
2035 {
2036  //----------------------------------------------------------------------------
2037  // NOTE:
2038  // when the program flow arrives here, it is assumed that a successful
2039  // instantiation of a TrimAnalysis object took place with a mode set
2040  // to taLongitudinal, i.e. 3 independent variables to be adjusted
2041  //----------------------------------------------------------------------------
2042 
2043  //----------------------------------------------------------------------------
2044  // NOTE:
2045  // rely on the fact that IC quantities have been fed at least once into
2046  // their correspondent data structure in TrimAnalysis->fdmex tree
2047  //
2048  // A perfect longitudinal trim has to be possible!
2049  // (no asymmetric effect should be present)
2050  //----------------------------------------------------------------------------
2051 
2052  double delta_cmd_T, delta_cmd_E, delta_cmd_A = 0.0, delta_cmd_R = 0.0;
2053 
2054  double phi, theta, psi;
2055 
2056  double alpha = 0.0, beta = 0.0, gamma = 0.0;
2057 
2058  FGQuaternion Quat;
2059  FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
2060 
2062  VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
2063  VState.vUVW = FGColumnVector3(0.0,0.0,0.0);
2064  VState.vPQR = FGColumnVector3(0.0,0.0,0.0);
2065  VState.vQtrn = FGQuaternion (0.0,0.0,0.0);
2066 
2067  //----------------------------------------------------------------------------
2068  // Minimization control vector, i.e. independent variables
2069  //----------------------------------------------------------------------------
2070 
2071  // CHECK the trim mode
2072 
2073  //-----------------------------------------------------------------------------------------------------------------
2074  // REMINDER:
2075  // n. of control variables for longitudinal trim (taLongitudinal): 3
2076  // ordering: the two commands first, then the Euler angle theta,
2077  // [0] throttle cmd, [1] elevator cmd, [2] theta
2078  //-----------------------------------------------------------------------------------------------------------------
2079 
2080  delta_cmd_T = x[0]; // throttle, cmd
2081  delta_cmd_E = x[1]; // elevator, cmd
2082  theta = x[2]; // pitch attitude
2083 
2084  double psiIC = FDMExec->GetIC()->GetPsiRadIC();
2085 
2086  psi = psiIC;
2087  phi = 0.0;
2088 
2089  TrimAnalysis->SetEulerAngles(phi, theta, psi);
2090 
2091  //----------------------------------------------------------------------------
2092  // parameter bound check
2093  // ToDo: manage bounds via the FGTrimAnalysisControl class
2094  //----------------------------------------------------------------------------
2095  bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) )
2096  || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) )
2097  || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) )
2098  ;
2099 
2100  if ( penalty ) {
2101  //-------------------------------------------------
2102  // just return an "infinite" value
2103  return HUGE_VAL;
2104  //-------------------------------------------------
2105  } else {
2106  calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
2107  phi, theta, psi,
2108  TrimAnalysis->GetMode(),
2109  alpha, beta, gamma,
2110  VState,
2111  vUVWdot, vPQRdot );
2112  }
2113 
2114  double u , v , w ,
2115  p , q , r ,
2116  uDot, vDot, wDot,
2117  pDot, qDot, rDot;
2118  u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3);
2119  p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3);
2120  uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3);
2121  pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3);
2122 
2123  double
2124  f = 1.000*uDot*uDot
2125  + 1.000*wDot*wDot
2126  + //1.0
2127  0.010
2128  *qDot*qDot;
2129 
2130  static int count = 0;
2131  count++;
2132 
2133  if ( f < TrimAnalysis->GetCostFunctionValue() )
2134  {
2135  // feed into the vector of TrimAnalysis Controls the current values
2136  vector<FGTrimAnalysisControl*>::iterator vi;
2137  for(vi=TrimAnalysis->GetControls()->begin();
2138  vi!=TrimAnalysis->GetControls()->end();vi++)
2139  {
2140  if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T);
2141  if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim
2142  if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(0); // TODO: taRollTrim
2143  if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(0); // TODO: taYawTrim
2144  if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(0);
2145  if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta);
2146  if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl( FDMExec->GetIC()->GetPsiRadIC());
2147  }
2148 
2149  if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
2150 
2151  // write on file, if open
2152 
2153  // "# iteration, costf,
2154  // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
2155  // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
2156  // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
2157  // alphaDot (rad/s), betaDot (rad/s), Thrust"
2158 
2159  ofstream* rfp = TrimAnalysis->GetResultsFile();
2160  if (rfp)
2161  (*rfp)
2162  << count <<", "<< f <<", " // 1, 2
2163  << delta_cmd_T <<", " // 3
2164  << delta_cmd_E <<", " <<0.0 <<", "<< 0.0 <<", " // 4, 5, 6
2165  << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9
2166  << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12
2167  << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15
2168  << u <<", "<< v <<", "<< w << ", " // 16, 17, 18
2169  << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
2170  // 19, 20, 21
2171  << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22
2172  << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23
2173  << FDMExec->GetAuxiliary()->Getadot() << ", " // 24
2174  << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25
2175 
2176  << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 26
2177 
2178  FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary
2179 
2180  // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
2181  TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
2182 
2183  TrimAnalysis->SetCostFunctionValue(f); // update the current minimum
2184  }
2185 
2186  return f;
2187 } // end of myCostFunctionLongitudinal
2188 
2189 double Objective::myCostFunctionFullCoordinatedTurn(Vector<double> & x)
2190 {
2191  //----------------------------------------------------------------------------
2192  // NOTE:
2193  // when the program flow arrives here, it is assumed that a successful
2194  // instantiation of a TrimAnalysis object took place with a mode set
2195  // to taTurn, i.e. 4 independent variables to be adjusted
2196  //----------------------------------------------------------------------------
2197 
2198  //----------------------------------------------------------------------------
2199  // NOTE:
2200  // rely on the fact that IC quantities have been fed at least once into
2201  // their correspondent data structure in TrimAnalysis->fdmex tree
2202  //----------------------------------------------------------------------------
2203 
2204  double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
2205 
2206  double phi, theta, psi;
2207 
2208  double alpha = 0.0, beta = 0.0;
2209  double gamma = 0.0;
2210 
2211  // for coordinated turns with nonzero rate of climb
2212  gamma = FDMExec->GetIC()->GetFlightPathAngleRadIC();
2213 
2214  FGQuaternion Quat;
2215  FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
2216 
2218  VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
2219  VState.vUVW = FGColumnVector3(0.0,0.0,0.0);
2220  VState.vPQR = FGColumnVector3(0.0,0.0,0.0);
2221  VState.vQtrn = FGQuaternion (0.0,0.0,0.0);
2222 
2223  //----------------------------------------------------------------------------
2224  // Minimization control vector, i.e. independent variables
2225  //----------------------------------------------------------------------------
2226 
2227  // CHECK the trim mode
2228 
2229  //-----------------------------------------------------------------------------------------------------------------
2230  // REMINDER:
2231  // n. of control variables for longitudinal trim (taTurn): 5
2232  // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
2233  // [4] psi
2234  //-----------------------------------------------------------------------------------------------------------------
2235 
2236  delta_cmd_T = x[0]; // throttle, cmd
2237  delta_cmd_E = x[1]; // elevator, cmd
2238  delta_cmd_A = x[2]; // aileron, cmd
2239  delta_cmd_R = x[3]; // rudder, cmd
2240 
2241  psi = x[4];
2242 
2243  // theta_underlined (see paper on Trim) for coordinated turn
2244  theta = atan2( sin(psi)*cos(gamma)+cos(psi)*sin(gamma), cos(gamma) );
2245 
2246  TrimAnalysis->setupTurnPhi(psi,theta); // calculates also phi
2247 
2248  double psiIC = FDMExec->GetIC()->GetPsiRadIC();
2249  double phiIC = FDMExec->GetIC()->GetPhiRadIC();
2250 
2251  phi = TrimAnalysis->GetPhiRad(); // this one is calculated by setupTurnPhi() above
2252 
2253  TrimAnalysis->SetEulerAngles(phi, theta, psi); // recalculates sin and cosines
2254 
2255  //----------------------------------------------------------------------------
2256  // parameter bound check
2257  // ToDo: manage bounds via the FGTrimAnalysisControl class
2258  //----------------------------------------------------------------------------
2259  bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) )
2260  || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) )
2261  || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) )
2262  || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) )
2263  || ( (psi < 0.0 ) || (psi > 2.0*M_PI) )
2264  || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) ) // may go out of range
2265  ;
2266 
2267  if ( penalty ) {
2268  //-------------------------------------------------
2269  // just return an "infinite" value
2270  return HUGE_VAL;
2271  //-------------------------------------------------
2272  } else {
2273  calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
2274  phi, theta, psi,
2275  TrimAnalysis->GetMode(),
2276  alpha, beta, gamma,
2277  VState,
2278  vUVWdot, vPQRdot );
2279  }
2280 
2281  double u , v , w ,
2282  p , q , r ,
2283  uDot, vDot, wDot,
2284  pDot, qDot, rDot;
2285  u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3);
2286  p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3);
2287  uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3);
2288  pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3);
2289 
2290  double
2291  f = 1.000*uDot*uDot
2292  + vDot*vDot
2293  + 1.000*wDot*wDot
2294  + 0.010*pDot*pDot
2295  + 0.010*qDot*qDot
2296  + 0.010*rDot*rDot;
2297 
2298  static int count = 0;
2299  count++;
2300 
2301  if ( f < TrimAnalysis->GetCostFunctionValue() )
2302  {
2303  // feed into the vector of TrimAnalysis Controls the current values
2304  vector<FGTrimAnalysisControl*>::iterator vi;
2305  for(vi=TrimAnalysis->GetControls()->begin();
2306  vi!=TrimAnalysis->GetControls()->end();vi++)
2307  {
2308  if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T);
2309  if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim
2310  if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim
2311  if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim
2312  if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi);
2313  if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta);
2314  if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi);
2315  }
2316 
2317  if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
2318 
2319  // write on file, if open
2320 
2321  // "# iteration, costf,
2322  // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
2323  // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
2324  // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
2325  // alphaDot (rad/s), betaDot (rad/s), Thrust"
2326 
2327  ofstream* rfp = TrimAnalysis->GetResultsFile();
2328  if (rfp)
2329  (*rfp)
2330  << count <<", "<< f <<", " // 1, 2
2331  << delta_cmd_T <<", " // 3
2332  << delta_cmd_E <<", " << delta_cmd_A <<", "<< delta_cmd_R <<", " // 4, 5, 6
2333  << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9
2334  << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12
2335  << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15
2336  << u <<", "<< v <<", "<< w << ", " // 16, 17, 18
2337  << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
2338  // 19, 20, 21
2339  << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22
2340  << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23
2341  << FDMExec->GetAuxiliary()->Getadot() << ", " // 24
2342  << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25
2343 
2344  << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 26
2345 
2346  FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary
2347 
2348  // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
2349  TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
2350 
2351  TrimAnalysis->SetCostFunctionValue(f); // update the current minimum
2352  }
2353 
2354  return f;
2355 } // end of myCostFunctionFullCoordinatedTurn
2356 
2357 double Objective::myCostFunctionFullTurn(Vector<double> & x)
2358 {
2359  //----------------------------------------------------------------------------
2360  // NOTE:
2361  // when the program flow arrives here, it is assumed that a successful
2362  // instantiation of a TrimAnalysis object took place with a mode set
2363  // to taTurnFull, i.e. 5 independent variables to be adjusted
2364  //----------------------------------------------------------------------------
2365 
2366  //----------------------------------------------------------------------------
2367  // NOTE:
2368  // rely on the fact that IC quantities have been fed at least once into
2369  // their correspondent data structure in TrimAnalysis->fdmex tree
2370  //----------------------------------------------------------------------------
2371 
2372  double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
2373 
2374  double phi, theta, psi;
2375 
2376  double alpha = 0.0, beta = 0.0;
2377  double gamma = 0.0;
2378 
2379  // for coordinated turns with nonzero rate of climb
2380  gamma = FDMExec->GetIC()->GetFlightPathAngleRadIC();
2381 
2382  FGQuaternion Quat;
2383  FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
2384 
2386  VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
2387  VState.vUVW = FGColumnVector3(0.0,0.0,0.0);
2388  VState.vPQR = FGColumnVector3(0.0,0.0,0.0);
2389  VState.vQtrn = FGQuaternion (0.0,0.0,0.0);
2390 
2391  //----------------------------------------------------------------------------
2392  // Minimization control vector, i.e. independent variables
2393  //----------------------------------------------------------------------------
2394 
2395  // CHECK the trim mode
2396 
2397  //-----------------------------------------------------------------------------------------------------------------
2398  // REMINDER:
2399  // n. of control variables for longitudinal trim (taTurn): 5
2400  // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
2401  // [4] psi, [5] theta
2402  //-----------------------------------------------------------------------------------------------------------------
2403 
2404  delta_cmd_T = x[0]; // throttle, cmd
2405  delta_cmd_E = x[1]; // elevator, cmd
2406  delta_cmd_A = x[2]; // aileron, cmd
2407  delta_cmd_R = x[3]; // rudder, cmd
2408 
2409  psi = x[4];
2410  theta = x[5];
2411 
2412  TrimAnalysis->setupTurn();
2413 
2414  double psiIC = FDMExec->GetIC()->GetPsiRadIC();
2415  double phiIC = FDMExec->GetIC()->GetPhiRadIC();
2416 
2417  phi = TrimAnalysis->GetPhiRad(); // this one is calculated by sutupTurnPhi() above
2418 
2419  TrimAnalysis->SetEulerAngles(phi, theta, psi); // recalculates sin and cosines
2420 
2421  //----------------------------------------------------------------------------
2422  // parameter bound check
2423  // ToDo: manage bounds via the FGTrimAnalysisControl class
2424  //----------------------------------------------------------------------------
2425  bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) )
2426  || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) )
2427  || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) )
2428  || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) )
2429  || ( (psi < 0.0 ) || (psi > 2.0*M_PI) )
2430  || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) ) // may go out of range
2431  ;
2432 
2433  if ( penalty ) {
2434  //-------------------------------------------------
2435  // just return an "infinite" value
2436  return HUGE_VAL;
2437  //-------------------------------------------------
2438  } else {
2439  calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
2440  phi, theta, psi,
2441  TrimAnalysis->GetMode(),
2442  alpha, beta, gamma,
2443  VState,
2444  vUVWdot, vPQRdot );
2445  }
2446 
2447  double u , v , w ,
2448  p , q , r ,
2449  uDot, vDot, wDot,
2450  pDot, qDot, rDot;
2451  u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3);
2452  p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3);
2453  uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3);
2454  pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3);
2455 
2456  double
2457  f = 1.000*uDot*uDot
2458  + vDot*vDot
2459  + 1.000*wDot*wDot
2460  + 0.010*pDot*pDot
2461  + 0.010*qDot*qDot
2462  + 0.010*rDot*rDot;
2463 
2464  static int count = 0;
2465  count++;
2466 
2467  if ( f < TrimAnalysis->GetCostFunctionValue() )
2468  {
2469  // feed into the vector of TrimAnalysis Controls the current values
2470  vector<FGTrimAnalysisControl*>::iterator vi;
2471  for(vi=TrimAnalysis->GetControls()->begin();
2472  vi!=TrimAnalysis->GetControls()->end();vi++)
2473  {
2474  if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T);
2475  if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim
2476  if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim
2477  if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim
2478  if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi);
2479  if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta);
2480  if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi);
2481  }
2482 
2483  if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
2484 
2485  // write on file, if open
2486 
2487  // "# iteration, costf,
2488  // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
2489  // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
2490  // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
2491  // alphaDot (rad/s), betaDot (rad/s), Thrust"
2492 
2493  ofstream* rfp = TrimAnalysis->GetResultsFile();
2494  if (rfp)
2495  (*rfp)
2496  << count <<", "<< f <<", " // 1, 2
2497  << delta_cmd_T <<", " // 3
2498  << delta_cmd_E <<", " << delta_cmd_A <<", "<< delta_cmd_R <<", " // 4, 5, 6
2499  << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9
2500  << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12
2501  << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15
2502  << u <<", "<< v <<", "<< w << ", " // 16, 17, 18
2503  << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
2504  // 19, 20, 21
2505  << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22
2506  << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23
2507  << FDMExec->GetAuxiliary()->Getadot() << ", " // 24
2508  << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25
2509 
2510  << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; //
2511 
2512  FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary
2513 
2514  // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
2515  TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
2516 
2517  TrimAnalysis->SetCostFunctionValue(f); // update the current minimum
2518  }
2519 
2520  return f;
2521 } // end of myCostFunctionFullCoordinatedTurn
2522 
2523 
2524 double Objective::myCostFunctionPullUp(Vector<double> & x)
2525 {
2526  //----------------------------------------------------------------------------
2527  // NOTE:
2528  // when the program flow arrives here, it is assumed that a successful
2529  // instantiation of a TrimAnalysis object took place with a mode set
2530  // to taTurn, i.e. 6 independent variables to be adjusted
2531  //----------------------------------------------------------------------------
2532 
2533  //----------------------------------------------------------------------------
2534  // NOTE:
2535  // rely on the fact that IC quantities have been fed at least once into
2536  // their correspondent data structure in TrimAnalysis->fdmex tree
2537  //----------------------------------------------------------------------------
2538 
2539  double delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R;
2540 
2541  double phi, theta, psi;
2542 
2543  double alpha = 0.0, beta = 0.0, gamma = 0.0;
2544 
2545  FGQuaternion Quat;
2546  FGColumnVector3 vPQRdot(0.0,0.0,0.0), vUVWdot(0.0,0.0,0.0);
2547 
2549  VState.vLocation = FGColumnVector3(0.0,0.0,0.0);
2550  VState.vUVW = FGColumnVector3(0.0,0.0,0.0);
2551  VState.vPQR = FGColumnVector3(0.0,0.0,0.0);
2552  VState.vQtrn = FGQuaternion (0.0,0.0,0.0);
2553 
2554  //----------------------------------------------------------------------------
2555  // Minimization control vector, i.e. independent variables
2556  //----------------------------------------------------------------------------
2557 
2558  // CHECK the trim mode
2559 
2560  //-----------------------------------------------------------------------------------------------------------------
2561  // REMINDER:
2562  // n. of control variables for longitudinal trim (taPullup): 5
2563  // [0] throttle cmd, [1] elevator cmd, 2) aileron cmd, [3] rudder cmd
2564  // [4] theta
2565  //-----------------------------------------------------------------------------------------------------------------
2566 
2567  delta_cmd_T = x[0]; // throttle, cmd
2568  delta_cmd_E = x[1]; // elevator, cmd
2569  delta_cmd_A = x[2]; // aileron, cmd
2570  delta_cmd_R = x[3]; // rudder, cmd
2571 
2572  theta = x[4];
2573 
2574  double psiIC = FDMExec->GetIC()->GetPsiRadIC();
2575 
2576  phi = 0.0; // assume a wing level pull-up
2577  psi = psiIC;
2578 
2579  TrimAnalysis->SetEulerAngles(phi, theta, psi);
2580 
2581  //----------------------------------------------------------------------------
2582  // parameter bound check
2583  // ToDo: manage bounds via the FGTrimAnalysisControl class
2584  //----------------------------------------------------------------------------
2585  bool penalty = ( (delta_cmd_T < 0) || (delta_cmd_T > 1) )
2586  || ( (delta_cmd_E < -1) || (delta_cmd_E > 1) )
2587  || ( (delta_cmd_A < -1) || (delta_cmd_A > 1) )
2588  || ( (delta_cmd_R < -1) || (delta_cmd_R > 1) )
2589  || ( (theta < -0.5*M_PI) || (theta > 0.5*M_PI) );
2590 
2591  if ( penalty ) {
2592  //-------------------------------------------------
2593  // just return an "infinite" value
2594  return HUGE_VAL;
2595  //-------------------------------------------------
2596  } else {
2597  calculateDottedStates(delta_cmd_T, delta_cmd_E, delta_cmd_A, delta_cmd_R,
2598  phi, theta, psi,
2599  TrimAnalysis->GetMode(),
2600  alpha, beta, gamma,
2601  VState,
2602  vUVWdot, vPQRdot );
2603  }
2604 
2605  double u , v , w ,
2606  p , q , r ,
2607  uDot, vDot, wDot,
2608  pDot, qDot, rDot;
2609  u = VState.vUVW (1); v = VState.vUVW (2); w = VState.vUVW (3);
2610  p = VState.vPQR (1); q = VState.vPQR (2); r = VState.vPQR (3);
2611  uDot = vUVWdot(1); vDot = vUVWdot(2); wDot = vUVWdot(3);
2612  pDot = vPQRdot(1); qDot = vPQRdot(2); rDot = vPQRdot(3);
2613 
2614  double
2615  f = 1.000*uDot*uDot
2616  + vDot*vDot
2617  + 1.000*wDot*wDot
2618  + 0.010*pDot*pDot
2619  + 0.010*qDot*qDot
2620  + 0.010*rDot*rDot;
2621 
2622  static int count = 0;
2623  count++;
2624 
2625  if ( f < TrimAnalysis->GetCostFunctionValue() )
2626  {
2627  // feed into the vector of TrimAnalysis Controls the current values
2628  vector<FGTrimAnalysisControl*>::iterator vi;
2629  for(vi=TrimAnalysis->GetControls()->begin();
2630  vi!=TrimAnalysis->GetControls()->end();vi++)
2631  {
2632  if ( (*vi)->GetControlType()==JSBSim::taThrottle ) (*vi)->SetControl(delta_cmd_T);
2633  if ( (*vi)->GetControlType()==JSBSim::taElevator ) (*vi)->SetControl(delta_cmd_E); // TODO: taPitchTrim
2634  if ( (*vi)->GetControlType()==JSBSim::taAileron ) (*vi)->SetControl(delta_cmd_A); // TODO: taRollTrim
2635  if ( (*vi)->GetControlType()==JSBSim::taRudder ) (*vi)->SetControl(delta_cmd_R); // TODO: taYawTrim
2636  if ( (*vi)->GetControlType()==JSBSim::taPhi ) (*vi)->SetControl(phi);
2637  if ( (*vi)->GetControlType()==JSBSim::taTheta ) (*vi)->SetControl(theta);
2638  if ( (*vi)->GetControlType()==JSBSim::taHeading ) (*vi)->SetControl(psi);
2639  }
2640 
2641  if ( f<=TrimAnalysis->GetTolerance() ) TrimAnalysis->SetTrimSuccessfull(); // SetTrimFailed(false);
2642 
2643  // write on file, if open
2644 
2645  // "# iteration, costf,
2646  // dT, dE, dA, dR, dPsi, dTheta, dPhi, Psi (rad), Theta (rad), Phi (rad),
2647  // uDot (fps2), vDot (fps2), wDot (fps2), pDot (rad/s2), qDot (rad/s2), rDot (rad/s2),
2648  // u (fps), v (fps), w (fps), p (rad/s), q (rad/s), r (rad/s), alpha (rad), beta (rad),
2649  // alphaDot (rad/s), betaDot (rad/s), Thrust"
2650 
2651  ofstream* rfp = TrimAnalysis->GetResultsFile();
2652  if (rfp)
2653  (*rfp)
2654  << count <<", "<< f <<", " // 1, 2
2655  << delta_cmd_T <<", " // 3
2656  << delta_cmd_E <<", " <<delta_cmd_A <<", "<< delta_cmd_R <<", " // 4, 5, 6
2657  << phi <<", "<< theta <<", "<< psi <<", " // 7, 8, 9
2658  << uDot <<", "<< vDot <<", "<< wDot <<", " // 10, 11, 12
2659  << pDot <<", "<< qDot <<", "<< rDot <<", " // 13, 14, 15
2660  << u <<", "<< v <<", "<< w << ", " // 16, 17, 18
2661  << VState.vPQR(1) <<", "<< VState.vPQR(2) <<", "<< VState.vPQR(3) << ", "
2662  // 19, 20, 21
2663  << FDMExec->GetAuxiliary()->Getalpha() << ", " // 22
2664  << FDMExec->GetAuxiliary()->Getbeta() << ", " // 23
2665  << FDMExec->GetAuxiliary()->Getadot() << ", " // 24
2666  << FDMExec->GetAuxiliary()->Getbdot() << ", " // 25
2667 
2668  << FDMExec->GetPropulsion()->GetEngine(0)->GetThrust() << "\n"; // 26
2669 
2670  FDMExec->GetPropagate()->SetVState( VState ); // ?? is this really necessary
2671 
2672  // update in TrimAnalysis some private variables, i.e. the state _u, _v, _w, ...
2673  TrimAnalysis->SetState(u, v, w, p, q, r, alpha, beta, phi, theta, psi, gamma);
2674 
2675  TrimAnalysis->SetCostFunctionValue(f); // update the current minimum
2676  }
2677 
2678  return f;
2679 } // end of myCostFunctionPullUp
2680 
2681 
2682 void Objective::calculateDottedStates(double delta_cmd_T, double delta_cmd_E, double delta_cmd_A, double delta_cmd_R,
2683  double phi, double theta, double psi,
2684  TrimAnalysisMode trimMode,
2685  double& alpha, double& beta, double& gamma,
2686  FGPropagate::VehicleState& VState,
2687  FGColumnVector3& vUVWdot, FGColumnVector3& vPQRdot )
2688 {
2689  double stheta,sphi,spsi;
2690  double ctheta,cphi,cpsi;
2691  double phiW = 0.;
2692  FGPropulsion* Propulsion = FDMExec->GetPropulsion();
2693  FGFCS* FCS = FDMExec->GetFCS();
2694  FGAuxiliary* Auxiliary = FDMExec->GetAuxiliary();
2695 
2696  if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) )//... Coordinated turn: p,q,r not zero, beta=0, gamma=0
2697  {
2698  // setupTurn has already made its job
2699  phiW = TrimAnalysis->GetPhiWRad();
2700  }
2701 
2702  cphi = cos(phi); sphi = sin(phi); // phi, rad
2703  ctheta = cos(theta); stheta = sin(theta); // theta, rad
2704  cpsi = cos(psi); spsi = sin(psi); // psi, rad
2705 
2706  TrimAnalysis->SetEulerAngles(phi, theta, psi);
2707 
2708  //-------------------------------------------------
2709  // apply controls
2710  //-------------------------------------------------
2711 
2712  // make sure the engines are running
2713  for (unsigned int i=0; i<Propulsion->GetNumEngines(); i++) {
2714  Propulsion->GetEngine(i)->SetRunning(true);
2715  }
2716 
2717  double tMin,tMax;
2718  for(unsigned i=0;i<Propulsion->GetNumEngines();i++)
2719  {
2720  tMin=Propulsion->GetEngine(i)->GetThrottleMin();
2721  tMax=Propulsion->GetEngine(i)->GetThrottleMax();
2722  FCS->SetThrottleCmd(i,tMin + delta_cmd_T *(tMax-tMin));
2723  FCS->Run(); // apply throttle change
2724  }
2725  Propulsion->GetSteadyState(); // GetSteadyState processes all engines
2726 
2727  // apply commands
2728  // ToDo: apply aerosurface deflections,
2729  // to override control system authority
2730 
2731  FCS->SetDeCmd( delta_cmd_E ); // elevator
2732  FCS->SetDaCmd( delta_cmd_A ); // ailerons
2733  FCS->SetDrCmd( delta_cmd_R ); // rudder
2734 
2735  FCS->Run(); // apply yoke & pedal changes
2736 
2737  //................................................
2738  // set also euler angles
2739  //................................................
2740 
2741  // new euler angles -> new quaternion Quat
2742 
2743  FGQuaternion Quat1( phi, theta, psi ); // values coming fro search algorithm
2744 
2745  VState.vQtrn = Quat1;
2746  VState.vQtrn.Normalize();
2747 
2748  //------------------------------------------
2749  // reconstruct NED velocity components from
2750  // initial conditions
2751  //------------------------------------------
2752 
2753  // initial altitude (never changed)
2754  double hIC = FDMExec->GetIC()->GetAltitudeFtIC();
2755 
2756  // re-apply the desired altitude;
2757  // FGAtmosphere wants the altitude from FGPropagate
2758  // (currently there's no Seth method in FGAtmosphere)
2759  FDMExec->GetPropagate()->Seth( hIC );
2760  FDMExec->GetAtmosphere()->Run();
2761 
2762  // initial speed (never changed)
2763  double vtIC = FDMExec->GetIC()->GetVtrueFpsIC();
2764 
2765  // initial flight-path angle (never changed)
2766  double gammaIC = FDMExec->GetIC()->GetFlightPathAngleRadIC();
2767 
2768  // initial climb-rate (never changed)
2769  // ???
2770  double rocIC = FDMExec->GetIC()->GetClimbRateFpsIC();
2771  // ???
2772  gammaIC = TrimAnalysis->GetGamma(); // from file of from IC
2773  rocIC = vtIC * tan(gammaIC);
2774  gamma = gammaIC; // <--------------------------------------------- this goes out to the caller
2775 
2776  double vdownIC = - rocIC;
2777 
2778  if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) ) //... turn: p,q,r, gamma not zero
2779  {
2780  gamma = TrimAnalysis->GetGammaRad(); //0.0;
2781  vdownIC = TrimAnalysis->GetVtFps() * tan(gamma); //0.0;
2782  }
2783  Auxiliary->SetGamma(gamma);
2784 
2785  // euler angles from the IC
2786  double psiIC = FDMExec->GetIC()->GetPsiRadIC(); // this is the desired ground track direction
2787 
2788  // assume that the desired ground-track heading coincides with the
2789  // aircraft initial heading given in IC
2790  double psigtIC = psiIC;
2791 
2792  // NED velocity components
2793  //double vgIC = FDMExec->GetIC()->GetVgroundFpsIC();
2794  double vgIC = vtIC * cos(gammaIC);
2795 
2796  double vnorthIC = vgIC * cos(psigtIC);
2797  double veastIC = vgIC * sin(psigtIC);
2798 
2799  // Wind components in NED frame
2800  double wnorthIC = FDMExec->GetIC()->GetWindNFpsIC();
2801  double weastIC = FDMExec->GetIC()->GetWindEFpsIC();
2802  double wdownIC = FDMExec->GetIC()->GetWindDFpsIC();
2803 
2804  // Velocity components in body-frame (from NED)
2805  double u, v, w;
2806 
2807  u=vnorthIC*ctheta*cpsi +
2808  veastIC*ctheta*spsi -
2809  vdownIC*stheta;
2810  v=vnorthIC*( sphi*stheta*cpsi - cphi*spsi ) +
2811  veastIC*( sphi*stheta*spsi + cphi*cpsi ) +
2812  vdownIC*sphi*ctheta;
2813  w=vnorthIC*( cphi*stheta*cpsi + sphi*spsi ) +
2814  veastIC*( cphi*stheta*spsi - sphi*cpsi ) +
2815  vdownIC*cphi*ctheta;
2816 
2817  // Wind domponents in body-frame (from NED)
2818  double uw, vw, ww;
2819 
2820  uw=wnorthIC*ctheta*cpsi +
2821  weastIC*ctheta*spsi -
2822  wdownIC*stheta;
2823  vw=wnorthIC*( sphi*stheta*cpsi - cphi*spsi ) +
2824  weastIC*( sphi*stheta*spsi + cphi*cpsi ) +
2825  wdownIC*sphi*ctheta;
2826  ww=wnorthIC*(cphi*stheta*cpsi + sphi*spsi) +
2827  weastIC*(cphi*stheta*spsi - sphi*cpsi) +
2828  wdownIC*cphi*ctheta;
2829 
2830  /**********************************************************************************************
2831  P R O P A G A T I O N start . . .
2832  **********************************************************************************************/
2833 
2834  // now that we have the velocities we can imitate the chain
2835  // in FDMexec->Run()... Propagate::Run()
2836 
2837  //++++++++++++++++++++++++++++++++++++++++++++
2838  // recalculate some important auxiliary data
2839  // imitate here Auxiliary::Run()
2840  //++++++++++++++++++++++++++++++++++++++++++++
2841 
2842  double ua, va, wa;
2843  double adot, bdot;
2844 
2845  Auxiliary->SetVt(vtIC); // re-apply the desired velocity
2846 
2847  if ((trimMode==taTurn)||(trimMode==taPullup))
2848  {
2849  v=0.0; // no sideslip
2850  }
2851 
2852  ua = u + uw; va = v + vw; wa = w + ww;
2853 
2854  // from FGAuxiliary::Run()
2855  if ( vtIC > 0.05) {
2856  if ( wa != 0.0 ) alpha = ua*ua > 0.0 ? atan2(wa, ua) : 0.0; // this goes out to the caller
2857  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
2858  double mUW = (ua*ua + wa*wa);
2859  double signU=1;
2860  if (ua != 0.0) signU = ua/fabs(ua);
2861  adot = bdot = 0; // enforce zero aerodyn. angle rates
2862  } else {
2863  alpha = beta = adot = bdot = 0; // this goes out to the caller
2864  }
2865 
2866  //------------------------------------------------------------------------------------
2867  // APPLY constraints to (p,q,r)
2868  //------------------------------------------------------------------------------------
2869 
2870  // constraint applied to p,q,r
2871  double p, q, r;
2872 
2873  // for NON TURNING straight FLIGHT, these will remain zero
2874  p = q = r = 0.0;
2875 
2876  if ( ( trimMode == taTurn ) || ( trimMode == taTurnFull ) ) //... Coordinated turn: p,q,r not zero
2877  {
2878  double g=FDMExec->GetInertial()->gravity();
2879  //double cgamma=cos(FDMExec->GetIC()->GetFlightPathAngleRadIC());
2880 
2881  // assume that the given phiIC is the desired phi_Wind
2882  // phiW ... see above at the beginning
2883  double turnRate = g*tan(phiW) / FDMExec->GetIC()->GetVtrueFpsIC(); //FDMExec->GetIC()->GetUBodyFpsIC();
2884  double pW = 0.0;
2885  double qW = turnRate*sin(phiW);
2886  double rW = turnRate*cos(phiW);
2887 
2888  FGColumnVector3 pqr = TrimAnalysis->UpdateRatesTurn(psi, theta, phi, phiW);
2889 
2890  // finally calculate the body frame angular rates
2891  p = pqr(1);
2892  q = pqr(2);
2893  r = pqr(3);
2894 
2895  Auxiliary->SetGamma(0.);
2896 
2897  }
2898  if ( trimMode == taPullup ) //... then p,q,r not zero
2899  {
2900  double g=FDMExec->GetInertial()->gravity();
2901  double cgamma=cos(FDMExec->GetIC()->GetFlightPathAngleRadIC());
2902  //double targetNlf=FDMExec->GetIC()->GetTargetNlfIC();
2903  double targetNlf = TrimAnalysis->GetTargetNlf();
2904 
2905  FGColumnVector3 pqr = TrimAnalysis->UpdateRatesPullup();
2906  q = pqr(2);
2907 
2908  // assume levelled wings
2909  }
2910  //if ( trimMode == taRoll ) ... then p,q,r not zero
2911  // may assign theta rate (=q if wings are leveled)
2912 
2913  double qbar = 0.5*FDMExec->GetAtmosphere()->GetDensity()*vtIC*vtIC;
2914  double qbarUW = 0.5*FDMExec->GetAtmosphere()->GetDensity()*(ua*ua + wa*wa);
2915  double qbarUV = 0.5*FDMExec->GetAtmosphere()->GetDensity()*(ua*ua + va*va);
2916  double Mach = vtIC / FDMExec->GetAtmosphere()->GetSoundSpeed();
2917 
2918  double MachU = ua / FDMExec->GetAtmosphere()->GetSoundSpeed();
2919  double MachV = va / FDMExec->GetAtmosphere()->GetSoundSpeed();
2920  double MachW = wa / FDMExec->GetAtmosphere()->GetSoundSpeed();
2921 
2922  //++++++++++++++++++++++++++++++++++++++++++++
2923  // now feed the above values into Auxiliary data structure
2924  //++++++++++++++++++++++++++++++++++++++++++++
2925 
2926  Auxiliary->Setalpha( alpha );
2927  Auxiliary->Setbeta ( beta );
2928 
2929  if ((trimMode==taTurn)||(trimMode==taPullup)) Auxiliary->Setbeta( 0.0 );
2930 
2931  // ensure zero rates
2932  Auxiliary->Setadot( 0.0 );
2933  Auxiliary->Setbdot( 0.0 );
2934 
2935  // ToDo: take into account here the wind and other desired trim conditions
2936  Auxiliary->SetAeroPQR( FGColumnVector3(p,q,r)) ;
2937 
2938  // note assumes that trim_mode is triggered
2939  // assumes that p=q=r=0, or set by the appropriate taTrimMode context
2940 
2941  if ((trimMode==taTurn)||(trimMode==taPullup))
2942  {
2943  v=0.0; // no sideslip
2944  }
2945 
2946  JSBSim::FGColumnVector3 vUVWAero( ua,va,wa );
2947  Auxiliary->SetAeroUVW( vUVWAero );
2948 
2949  Auxiliary->Setqbar ( qbar );
2950  Auxiliary->SetqbarUV( qbarUV );
2951  Auxiliary->SetqbarUW( qbarUW );
2952 
2953  Auxiliary->SetVt ( vtIC );
2954 
2955  Auxiliary->SetMach ( Mach );
2956  Auxiliary->SetGamma ( gammaIC );
2957 
2958  // note: do not Auxiliary->Run(), otherwise dotted values
2959  // _and_ aerodynamic angles are recalculated !!!
2960 
2961  //++++++++++++++++++++++++++++++++++++++++++++
2962  // Recalculate aerodynamics by taking into
2963  // account the above changes
2964  //++++++++++++++++++++++++++++++++++++++++++++
2965  FDMExec->GetAerodynamics()->Run();
2966 
2967  //++++++++++++++++++++++++++++++++++++++++++++
2968  // Recalculate propulsion forces & moments
2969  //++++++++++++++++++++++++++++++++++++++++++++
2970  //Propulsion->Run();
2971  Propulsion->GetSteadyState();
2972 
2973  //++++++++++++++++++++++++++++++++++++++++++++
2974  // Recalculate GroundReaction forces & moments
2975  //++++++++++++++++++++++++++++++++++++++++++++
2976  FDMExec->GetGroundReactions()->Run();
2977 
2978  //++++++++++++++++++++++++++++++++++++++++++++
2979  // Recalculate TOTAL Forces & Moments
2980  //++++++++++++++++++++++++++++++++++++++++++++
2981  FDMExec->GetAircraft()->Run();
2982 
2983  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2984  // now we have the forces & moments
2985  // we need some Propagate-like statements ... i.e. replicate what Propagate::Run() does
2986  // to finally get the "dotted" state
2987  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2988 
2989  VState.vLocation = JSBSim::FGLocation( FDMExec->GetPropagate()->GetLongitude(), // location
2990  FDMExec->GetPropagate()->GetLatitude(),
2991  hIC + FDMExec->GetIC()->GetSeaLevelRadiusFtIC() );
2992 
2993  // _do not_ feed into Propagate at this level!!
2994  //FDMExec->GetPropagate()->SetLocation( VState.vLocation );
2995  // but pass to the caller the calculated state and dotted state
2996 
2997  VState.vUVW(1) = u; // u,v,w
2998  VState.vUVW(2) = v;
2999  VState.vUVW(3) = w;
3000 
3001  VState.vPQR(1) = p; // p,q,r
3002  VState.vPQR(2) = q;
3003  VState.vPQR(3) = r;
3004 
3005  //VState.vQtrn = Quat; // already fed above
3006 
3007  const FGColumnVector3 omega( 0.0, 0.0, FDMExec->GetInertial()->omega() ); // earth rotation
3008  const FGColumnVector3& vForces = FDMExec->GetAircraft()->GetForces(); // current forces
3009  const FGColumnVector3& vMoments = FDMExec->GetAircraft()->GetMoments(); // current moments
3010 
3011  double mass = FDMExec->GetMassBalance()->GetMass(); // mass
3012  const FGMatrix33& J = FDMExec->GetMassBalance()->GetJ(); // inertia matrix
3013  const FGMatrix33& Jinv = FDMExec->GetMassBalance()->GetJinv(); // inertia matrix inverse
3014 
3015  double rd = FDMExec->GetPropagate()->GetRadius(); // radius
3016  if (rd == 0.0) {cerr << "radius = 0 !" << endl; rd = 1e-16;} // radius check
3017 
3018  double rdInv = 1.0/rd;
3019  FGColumnVector3 gAccel( 0.0, 0.0, FDMExec->GetInertial()->GetGAccel(rd) );
3020 
3021  // The rotation matrices:
3022  const FGMatrix33& Tl2b = VState.vQtrn.GetT(); // local to body frame
3023  const FGMatrix33& Tb2l = VState.vQtrn.GetTInv(); // body to local frame
3024  const FGMatrix33& Tec2l = VState.vLocation.GetTec2l(); // earth centered to local frame
3025  const FGMatrix33& Tl2ec = VState.vLocation.GetTl2ec(); // local to earth centered frame
3026 
3027  // Inertial angular velocity measured in the body frame.
3028  //... const FGColumnVector3 pqri = VState.vPQR + Tl2b*(Tec2l*omega);
3029 
3030  // NOTE: the trim is valid in flat-earth hypothesis,
3031  // do not take into account the motion relative to the e.c.,
3032  // consider only the motion wrt local frame
3033 
3034  const FGColumnVector3 pqri = VState.vPQR;
3035 
3036  const FGColumnVector3 vVel = Tb2l * VState.vUVW;
3037 
3038  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3039  // Finally compute the time derivatives of the vehicle state values:
3040  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3041 
3042  // Compute body frame rotational accelerations based on the current body moments
3043  vPQRdot = Jinv*(vMoments - pqri*(J*pqri)); // <------------------------ this goes out to the caller
3044 
3045  // Compute body frame accelerations based on the current body forces
3046  vUVWdot = VState.vUVW*VState.vPQR + vForces/mass; // <----------------- this goes out to the caller
3047 
3048  // Coriolis acceleration.
3049  FGColumnVector3 ecVel = Tl2ec*vVel;
3050  FGColumnVector3 ace = 2.0*omega*ecVel;
3051  //... vUVWdot -= Tl2b*(Tec2l*ace);
3052 
3053  if (!FDMExec->GetGroundReactions()->GetWOW()) {
3054  // Centrifugal acceleration.
3055  FGColumnVector3 aeec = omega*(omega*VState.vLocation);
3056  }
3057 
3058  // Gravitation accel
3059  vUVWdot += Tl2b*gAccel;
3060 
3061  // Compute vehicle velocity wrt EC frame, expressed in EC frame
3062  FGColumnVector3 vLocationDot = Tl2ec * vVel;
3063 
3064  FGColumnVector3 omegaLocal( rdInv*vVel(2), // East
3065  -rdInv*vVel(1), // North
3066  -rdInv*vVel(2)*VState.vLocation.GetTanLatitude() );
3067 
3068  // Compute quaternion orientation derivative on current body rates
3069  //... FGQuaternion vQtrndot = VState.vQtrn.GetQDot( VState.vPQR - Tl2b*omegaLocal );
3070  FGQuaternion vQtrndot = VState.vQtrn.GetQDot( VState.vPQR );
3071 
3072  /**********************************************************************************************
3073  end of.... P R O P A G A T I O N
3074  **********************************************************************************************/
3075 
3076 }
3077 
3078 bool FGTrimAnalysis::getSteadyState(int nrepeat )
3079 {
3080  double currentThrust = 0, lastThrust=-1;
3081  int steady_count=0;
3082  bool steady=false;
3083 
3084  while ( !steady && steady_count <= nrepeat )
3085  {
3086  steady_count++;
3087  steady = Propulsion->GetSteadyState();
3088  }
3089  return steady;
3090 }
3091 
3092 //................................................................................
3093 } // JSBSim namespace
Element * GetParent(void)
Returns a pointer to the parent of an element.
Definition: FGXMLElement.h:231
double GetPhiRad()
Gets Euler angle phi.
double GetGamma()
Return the current flight path angle in TrimAnalysis object.
double GetPhiWRad()
Gets Euler angle phiW (wind axes)
~FGTrimAnalysis(void)
Destructor.
bool RemoveControl(TaControl control)
Remove a specific control from the current configuration.
FGInitialCondition * GetIC(void)
Returns a pointer to the FGInitialCondition object.
Definition: FGFDMExec.h:385
Models the Quaternion representation of rotations.
Definition: FGQuaternion.h:92
TrimAnalysisMode GetMode() const
double GetVtrueFpsIC(void) const
Gets the initial true velocity.
FGInertial * GetInertial(void)
Returns the FGInertial pointer.
Definition: FGFDMExec.h:359
bool DoTrim(void)
Execute the trim.
double GetFlightPathAngleRadIC(void) const
Gets the initial flight path angle.
bool SetResultsFile(string name)
Set the file where trim analysis results are written, open and get ready.
void SetMode(TrimAnalysisMode tam)
Clear all controls and set a predefined trim mode (Note: controls are intended here as those variable...
friend void find_CostFunctionFullWingsLevel(long vars, Vector< double > &v, double &f, bool &success, void *t_ptr)
Wrapping function for the effective Wings Level Trim cost function, to be called by optimization meth...
ofstream * GetResultsFile() const
Get the pointer to the file where trim analysis results are written,.
void Calculate(void)
Calculates the thrust of the engine, and other engine functions.
Definition: FGTurbine.cpp:115
FGColumnVector3 UpdateRatesTurn(double psi, double theta, double phi, double phiW)
Updates angular rates for turn trim according to turning trim constraints.
void SetEulerAngles(double phi0, double theta0, double psi0)
Sets Euler angles.
bool AddControl(TaControl control)
Add a control to the current configuration.
double GetPhiRadIC(void) const
Gets the initial roll angle.
void SetCostFunctionValue(double value)
Set the value of the cost function.
FGAuxiliary * GetAuxiliary(void)
Returns the FGAuxiliary pointer.
Definition: FGFDMExec.h:371
FGLocation holds an arbitrary location in the Earth centered Earth fixed reference frame (ECEF)...
Definition: FGLocation.h:160
This class models a turbine engine.
Definition: FGTurbine.h:162
FGLocation vLocation
Represents the current location of the vehicle in Earth centered Earth fixed (ECEF) frame...
Definition: FGPropagate.h:111
FGEngine * GetEngine(unsigned int index) const
Retrieves an engine object pointer from the list of engines.
Definition: FGPropulsion.h:140
Element * FindElement(const std::string &el="")
Searches for a specified element.
bool EditState(TaControl new_control, double new_initvalue, double new_step, double new_min, double new_max)
Change the control settings previously configured.
friend void find_CostFunctionLongitudinal(long vars, Vector< double > &v, double &f, bool &success, void *t_ptr)
Wrapping function for the effective Longitudinal Trim cost function, to be called by optimization met...
unsigned int GetNumDataLines(void)
Returns the number of lines of data stored.
Definition: FGXMLElement.h:195
void Report(void)
Print the results of the trim.
void SetState(double u0, double v0, double w0, double p0, double q0, double r0, double alpha0, double beta0, double phi0, double theta0, double psi0, double gamma0)
Sets state variables.
void ClearControls(void)
Clear all controls from the current configuration.
void SetThrottleCmd(int engine, double cmd)
Sets the throttle command for the specified engine.
Definition: FGFCS.cpp:323
void SetDaCmd(double cmd)
Sets the aileron command.
Definition: FGFCS.h:379
bool Run(bool Holding)
Runs the Flight Controls model; called by the Executive Can pass in a value indicating if the executi...
Definition: FGFCS.cpp:161
The current vehicle state vector structure contains the translational and angular position...
Definition: FGPropagate.h:107
void SetDottedValues(double udot, double vdot, double wdot, double pdot, double qdot, double rdot)
Sets Dotted values.
void CostFunctionFullTurn(long vars, Vector< double > &v, double &f)
Steady Turn Trim cost function.
double GetWindNFpsIC(void) const
Gets the initial wind velocity in local frame.
TaControl GetControlType(void)
Return the control type.
friend void find_CostFunctionPullUp(long vars, Vector< double > &v, double &f, bool &success, void *t_ptr)
Wrapping function for the effective Pullup Trim cost function, to be called by optimization method...
Objective(FGFDMExec *fdmex, FGTrimAnalysis *ta, double x)
Constructor.
bool Load(string fname, bool useStoredPath=true)
Loads the trim configuration from file.
FGTrimAnalysis(FGFDMExec *FDMExec, TrimAnalysisMode tam=taFull)
Initializes the trimming class.
void CostFunctionFullCoordinatedTurn(long vars, Vector< double > &v, double &f)
Steady Turn Trim cost function, NON-coordinated.
FGColumnVector3 vPQR
The angular velocity vector for the vehicle relative to the ECEF frame, expressed in the body frame...
Definition: FGPropagate.h:121
double GetVtFps()
Gets true speed [fps] from IC.
double GetTargetNlfIC(void) const
Gets the target normal load factor set from IC.
Propulsion management class.
Definition: FGPropulsion.h:106
double GetTanLatitude() const
Get the cosine of Latitude.
Definition: FGLocation.h:302
double GetDataAsNumber(void)
Converts the element data to a number.
FGAircraft * GetAircraft(void)
Returns the FGAircraft pointer.
Definition: FGFDMExec.h:367
double GetAttributeValueAsNumber(const std::string &key)
Retrieves an attribute value as a double precision real number.
virtual double GetSoundSpeed(void) const
Returns the speed of sound in ft/sec.
Definition: FGAtmosphere.h:191
const std::string & GetName(void) const
Retrieves the element name.
Definition: FGXMLElement.h:186
FGGroundReactions * GetGroundReactions(void)
Returns the FGGroundReactions pointer.
Definition: FGFDMExec.h:361
double GetWindEFpsIC(void) const
Gets the initial wind velocity in local frame.
double GetPsiRadIC(void) const
Gets the initial heading angle.
FGColumnVector3 UpdateRatesPullup(void)
Updates angular rates for pull-up trim.
bool Run(bool Holding)
Runs the Ground Reactions model; called by the Executive Can pass in a value indicating if the execut...
double GetTargetNlf(void)
Gets target normal load factor in steady turn.
void SetDrCmd(double cmd)
Sets the rudder command.
Definition: FGFCS.h:387
void SetDeCmd(double cmd)
Sets the elevator command.
Definition: FGFCS.h:383
bool Run(bool Holding)
Runs the atmosphere forces model; called by the Executive.
void CostFunctionLongitudinal(long vars, Vector< double > &v, double &f)
Longitudinal Trim cost function.
void CostFunctionFullWingsLevel(long vars, Vector< double > &v, double &f)
Wings Level Trim cost function.
This class implements a 3 element column vector.
void TrimStats()
Iteration statistics.
virtual double GetDensity(void) const
Returns the density in slugs/ft^3.
Definition: FGAtmosphere.h:175
void CostFunctionPullUp(long vars, Vector< double > &v, double &f)
Pullup Trim cost function.
FGFCS * GetFCS(void)
Returns the FGFCS pointer.
Definition: FGFDMExec.h:351
Encapsulates the Flight Control System (FCS) functionality.
Definition: FGFCS.h:193
double GetWindDFpsIC(void) const
Gets the initial wind velocity in local frame.
bool GetSteadyState(void)
Loops the engines until thrust output steady (used for trimming)
const FGMatrix33 & GetTec2l(void) const
Transform matrix from the earth centered to local horizontal frame.
Definition: FGLocation.h:414
double GetGammaRad()
Gets flight path angle.
friend void find_CostFunctionFull(long vars, Vector< double > &v, double &f, bool &success, void *t_ptr)
Wrapping function for the effective Full Trim cost function, to be called by optimization method...
Encapsulates various uncategorized scheduled functions.
Definition: FGAuxiliary.h:109
Models a Supercharged Piston engine.
Definition: FGPiston.h:221
Handles matrix math operations.
Definition: FGMatrix33.h:92
void CostFunctionFull(long vars, Vector< double > &v, double &f)
Full Trim cost function.
FGAerodynamics * GetAerodynamics(void)
Returns the FGAerodynamics pointer.
Definition: FGFDMExec.h:357
void Normalize(void)
Normalize.
FGPropulsion * GetPropulsion(void)
Returns the FGPropulsion pointer.
Definition: FGFDMExec.h:353
Models an aircraft control variables for purposes of trimming.
const FGMatrix33 & GetTl2ec(void) const
Transform matrix from local horizontal to earth centered frame.
Definition: FGLocation.h:409
void CalculatePhiWFromTargetNlfTurn(double nlf)
Calculate the wind axis bank angle from a given Nlf (sets also the target Nlf)
Encapsulates the JSBSim simulation executive.
Definition: FGFDMExec.h:189
bool Run(bool Holding)
Runs the Aircraft model; called by the Executive Can pass in a value indicating if the executive is d...
Definition: FGAircraft.cpp:109
FGColumnVector3 vUVW
The velocity vector of the vehicle with respect to the ECEF frame, expressed in the body system...
Definition: FGPropagate.h:116
double GetClimbRateFpsIC(void) const
Gets the initial climb rate.
bool Run(bool Holding)
Runs the Aerodynamics model; called by the Executive Can pass in a value indicating if the executive ...
FGPropagate * GetPropagate(void)
Returns the FGPropagate pointer.
Definition: FGFDMExec.h:369
FGMassBalance * GetMassBalance(void)
Returns the FGAircraft pointer.
Definition: FGFDMExec.h:355
friend void find_CostFunctionFullCoordinatedTurn(long vars, Vector< double > &v, double &f, bool &success, void *t_ptr)
Wrapping function for the effective Steady Turn Trim cost function, to be called by optimization meth...
unsigned int GetNumEngines(void) const
Retrieves the number of engines defined for the aircraft.
Definition: FGPropulsion.h:134
friend void find_CostFunctionFullTurn(long vars, Vector< double > &v, double &f, bool &success, void *t_ptr)
Wrapping function for the effective Steady Turn Trim cost function, to be called by optimization meth...