JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
FGTrim.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Header: FGTrim.cpp
4  Author: Tony Peden
5  Date started: 9/8/99
6 
7  --------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) ---------
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
11  Software Foundation; either version 2 of the License, or (at your option) any
12  later 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
20  with this program; if not, write to the Free Software Foundation, Inc., 59
21  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 
23  Further information about the GNU Lesser General Public License can also be
24  found on the world wide web at http://www.gnu.org.
25 
26  HISTORY
27 --------------------------------------------------------------------------------
28 9/8/99 TP Created
29 
30 FUNCTIONAL DESCRIPTION
31 --------------------------------------------------------------------------------
32 
33 This class takes the given set of IC's and finds the angle of attack, elevator,
34 and throttle setting required to fly steady level. This is currently for in-air
35 conditions only. It is implemented using an iterative, one-axis-at-a-time
36 scheme. */
37 
38 // !!!!!!! BEWARE ALL YE WHO ENTER HERE !!!!!!!
39 
40 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 INCLUDES
42 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
43 
44 #include <iomanip>
45 #include "FGTrim.h"
46 #include "models/FGInertial.h"
47 #include "models/FGAccelerations.h"
48 #include "models/FGMassBalance.h"
49 #include "models/FGFCS.h"
50 
51 #if _MSC_VER
52 #pragma warning (disable : 4786 4788)
53 #endif
54 
55 using namespace std;
56 
57 namespace JSBSim {
58 
59 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60 
61 FGTrim::FGTrim(FGFDMExec *FDMExec,TrimMode tt)
62  : fgic(FDMExec)
63 {
64 
65  Nsub=0;
66  max_iterations=60;
67  max_sub_iterations=100;
68  Tolerance=1E-3;
69  A_Tolerance = Tolerance / 10;
70 
71  Debug=0;DebugLevel=0;
72  fdmex=FDMExec;
73  fgic = *fdmex->GetIC();
74  total_its=0;
75  gamma_fallback=false;
76  mode=tt;
77  xlo=xhi=alo=ahi=0.0;
78  targetNlf=fgic.GetTargetNlfIC();
79  debug_axis=tAll;
80  SetMode(tt);
81  if (debug_lvl & 2) cout << "Instantiated: FGTrim" << endl;
82 }
83 
84 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85 
86 FGTrim::~FGTrim(void) {
87  if (debug_lvl & 2) cout << "Destroyed: FGTrim" << endl;
88 }
89 
90 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 
93  int run_sum=0;
94  cout << endl << " Trim Statistics: " << endl;
95  cout << " Total Iterations: " << total_its << endl;
96  if( total_its > 0) {
97  cout << " Sub-iterations:" << endl;
98  for (unsigned int current_axis=0; current_axis<TrimAxes.size(); current_axis++) {
99  run_sum += TrimAxes[current_axis].GetRunCount();
100  cout << " " << setw(5) << TrimAxes[current_axis].GetStateName().c_str()
101  << ": " << setprecision(3) << sub_iterations[current_axis]
102  << " average: " << setprecision(5) << sub_iterations[current_axis]/double(total_its)
103  << " successful: " << setprecision(3) << successful[current_axis]
104  << " stability: " << setprecision(5) << TrimAxes[current_axis].GetAvgStability()
105  << endl;
106  }
107  cout << " Run Count: " << run_sum << endl;
108  }
109 }
110 
111 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112 
113 void FGTrim::Report(void) {
114  cout << " Trim Results: " << endl;
115  for(unsigned int current_axis=0; current_axis<TrimAxes.size(); current_axis++)
116  TrimAxes[current_axis].AxisReport();
117 
118 }
119 
120 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 
123  mode=tCustom;
124  TrimAxes.clear();
125  //cout << "TrimAxes.size(): " << TrimAxes.size() << endl;
126 }
127 
128 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129 
130 bool FGTrim::AddState( State state, Control control ) {
131  mode = tCustom;
132  vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
133  for (; iAxes != TrimAxes.end(); ++iAxes) {
134  if (iAxes->GetStateType() == state)
135  return false;
136  }
137 
138  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,state,control));
139  sub_iterations.resize(TrimAxes.size());
140  successful.resize(TrimAxes.size());
141  solution.resize(TrimAxes.size());
142 
143  return true;
144 }
145 
146 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147 
148 bool FGTrim::RemoveState( State state ) {
149  bool result=false;
150 
151  mode = tCustom;
152  vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
153  while (iAxes != TrimAxes.end()) {
154  if( iAxes->GetStateType() == state ) {
155  iAxes = TrimAxes.erase(iAxes);
156  result=true;
157  continue;
158  }
159  ++iAxes;
160  }
161  if(result) {
162  sub_iterations.resize(TrimAxes.size());
163  successful.resize(TrimAxes.size());
164  solution.resize(TrimAxes.size());
165  }
166  return result;
167 }
168 
169 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
170 
171 bool FGTrim::EditState( State state, Control new_control ){
172  mode = tCustom;
173  vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
174  while (iAxes != TrimAxes.end()) {
175  if( iAxes->GetStateType() == state ) {
176  *iAxes = FGTrimAxis(fdmex,&fgic,state,new_control);
177  return true;
178  }
179  ++iAxes;
180  }
181  return false;
182 }
183 
184 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
185 
186 bool FGTrim::DoTrim(void) {
187  bool trim_failed=false;
188  unsigned int N = 0;
189  unsigned int axis_count = 0;
190  FGFCS *FCS = fdmex->GetFCS();
191  vector<double> throttle0 = FCS->GetThrottleCmd();
192  double elevator0 = FCS->GetDeCmd();
193  double aileron0 = FCS->GetDaCmd();
194  double rudder0 = FCS->GetDrCmd();
195  double PitchTrim0 = FCS->GetPitchTrimCmd();
196 
197  for(int i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++)
198  fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(false);
199 
200  fdmex->SetTrimStatus(true);
201  fdmex->SuspendIntegration();
202 
203  fgic.SetPRadpsIC(0.0);
204  fgic.SetQRadpsIC(0.0);
205  fgic.SetRRadpsIC(0.0);
206 
207  if (mode == tGround) {
208  fdmex->Initialize(&fgic);
209  fdmex->Run();
210  trimOnGround();
211  double theta = fgic.GetThetaRadIC();
212  double phi = fgic.GetPhiRadIC();
213  // Take opportunity of the first approx. found by trimOnGround() to
214  // refine the control limits.
215  TrimAxes[0].SetControlLimits(0., fgic.GetAltitudeAGLFtIC());
216  TrimAxes[1].SetControlLimits(theta - 5.0 * degtorad, theta + 5.0 * degtorad);
217  TrimAxes[2].SetControlLimits(phi - 30.0 * degtorad, phi + 30.0 * degtorad);
218  }
219 
220  //clear the sub iterations counts & zero out the controls
221  for(unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
222  //cout << current_axis << " " << TrimAxes[current_axis]->GetStateName()
223  //<< " " << TrimAxes[current_axis]->GetControlName()<< endl;
224  xlo=TrimAxes[current_axis].GetControlMin();
225  xhi=TrimAxes[current_axis].GetControlMax();
226  TrimAxes[current_axis].SetControl((xlo+xhi)/2);
227  TrimAxes[current_axis].Run();
228  //TrimAxes[current_axis].AxisReport();
229  sub_iterations[current_axis]=0;
230  successful[current_axis]=0;
231  solution[current_axis]=false;
232  }
233 
234  if(mode == tPullup ) {
235  cout << "Setting pitch rate and nlf... " << endl;
236  setupPullup();
237  cout << "pitch rate done ... " << endl;
238  TrimAxes[0].SetStateTarget(targetNlf);
239  cout << "nlf done" << endl;
240  } else if (mode == tTurn) {
241  setupTurn();
242  //TrimAxes[0].SetStateTarget(targetNlf);
243  }
244 
245  do {
246  axis_count=0;
247  for(unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
248  setDebug(TrimAxes[current_axis]);
249  updateRates();
250  Nsub=0;
251  if(!solution[current_axis]) {
252  if(checkLimits(TrimAxes[current_axis])) {
253  solution[current_axis]=true;
254  solve(TrimAxes[current_axis]);
255  }
256  } else if(findInterval(TrimAxes[current_axis])) {
257  solve(TrimAxes[current_axis]);
258  } else {
259  solution[current_axis]=false;
260  }
261  sub_iterations[current_axis]+=Nsub;
262  }
263  for(unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
264  //these checks need to be done after all the axes have run
265  if(Debug > 0) TrimAxes[current_axis].AxisReport();
266  if(TrimAxes[current_axis].InTolerance()) {
267  axis_count++;
268  successful[current_axis]++;
269  }
270  }
271 
272  if((axis_count == TrimAxes.size()-1) && (TrimAxes.size() > 1)) {
273  //cout << TrimAxes.size()-1 << " out of " << TrimAxes.size() << "!" << endl;
274  //At this point we can check the input limits of the failed axis
275  //and declare the trim failed if there is no sign change. If there
276  //is, keep going until success or max iteration count
277 
278  //Oh, well: two out of three ain't bad
279  for(unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
280  //these checks need to be done after all the axes have run
281  if(!TrimAxes[current_axis].InTolerance()) {
282  if(!checkLimits(TrimAxes[current_axis])) {
283  // special case this for now -- if other cases arise proper
284  // support can be added to FGTrimAxis
285  if( (gamma_fallback) &&
286  (TrimAxes[current_axis].GetStateType() == tUdot) &&
287  (TrimAxes[current_axis].GetControlType() == tThrottle)) {
288  cout << " Can't trim udot with throttle, trying flight"
289  << " path angle. (" << N << ")" << endl;
290  if(TrimAxes[current_axis].GetState() > 0)
291  TrimAxes[current_axis].SetControlToMin();
292  else
293  TrimAxes[current_axis].SetControlToMax();
294  TrimAxes[current_axis].Run();
295  TrimAxes[current_axis]=FGTrimAxis(fdmex,&fgic,tUdot,tGamma);
296  } else {
297  cout << " Sorry, " << TrimAxes[current_axis].GetStateName()
298  << " doesn't appear to be trimmable" << endl;
299  //total_its=k;
300  trim_failed=true; //force the trim to fail
301  } //gamma_fallback
302  }
303  } //solution check
304  } //for loop
305  } //all-but-one check
306  N++;
307  if(N > max_iterations)
308  trim_failed=true;
309  } while((axis_count < TrimAxes.size()) && (!trim_failed));
310 
311  if((!trim_failed) && (axis_count >= TrimAxes.size())) {
312  total_its=N;
313  if (debug_lvl > 0)
314  cout << endl << " Trim successful" << endl;
315  } else { // The trim has failed
316  total_its=N;
317 
318  // Restore the aircraft parameters to their initial values
319  fgic = *fdmex->GetIC();
320  FCS->SetDeCmd(elevator0);
321  FCS->SetDaCmd(aileron0);
322  FCS->SetDrCmd(rudder0);
323  FCS->SetPitchTrimCmd(PitchTrim0);
324  for (unsigned int i=0; i < throttle0.size(); i++)
325  FCS->SetThrottleCmd(i, throttle0[i]);
326 
327  fdmex->Initialize(&fgic);
328  fdmex->Run();
329 
330  // If WOW is true we must make sure there are no gears into the ground.
331  if (fdmex->GetGroundReactions()->GetWOW())
332  trimOnGround();
333 
334  if (debug_lvl > 0)
335  cout << endl << " Trim failed" << endl;
336  }
337 
338  fdmex->GetPropagate()->InitializeDerivatives();
339  fdmex->ResumeIntegration();
340  fdmex->SetTrimStatus(false);
341 
342  for(int i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++)
343  fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(true);
344 
345  return !trim_failed;
346 }
347 
348 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349 // Trim the aircraft on the ground. The algorithm is looking for a stable
350 // position of the aicraft. Assuming the aircaft is a rigid body and the ground
351 // a plane: we need to find the translations and rotations of the aircraft that
352 // will move 3 non-colinear points in contact with the ground.
353 // The algorithm proceeds in three stages (one for each point):
354 // 1. Look for the contact point closer to or deeper into the ground. Move the
355 // aircraft along the vertical direction so that only this contact point
356 // remains in contact with the ground.
357 // 2. The forces applied on the aircraft (most likely the gravity) will generate
358 // a moment on the aircraft around the point in contact. The rotation axis is
359 // therefore the moment axis. The 2nd stage thus consists in determining the
360 // minimum rotation angle around the first point in contact that will place a
361 // second contact point on the ground.
362 // 3. At this stage, 2 points are in contact with the ground: the rotation axis
363 // is therefore the vector generated by the 2 points. Like stage #2, the
364 // rotation direction will be driven by the moment around the axis formed by
365 // the 2 points in contact. The rotation angle is obtained similarly to stage
366 // #2: it is the minimum angle that will place a third contact point on the
367 // ground.
368 // The calculations below do not account for the compression of the landing
369 // gears meaning that the position found is close to the real position but not
370 // strictly equal to it.
371 
372 void FGTrim::trimOnGround(void)
373 {
374  FGGroundReactions* GroundReactions = fdmex->GetGroundReactions();
375  FGPropagate* Propagate = fdmex->GetPropagate();
376  FGMassBalance* MassBalance = fdmex->GetMassBalance();
377  FGAccelerations* Accelerations = fdmex->GetAccelerations();
378  vector<ContactPoints> contacts;
379  FGLocation CGLocation = Propagate->GetLocation();
380  FGMatrix33 Tec2b = Propagate->GetTec2b();
381  FGMatrix33 Tb2l = Propagate->GetTb2l();
382  double hmin = 1E+10;
383  int contactRef = -1;
384 
385  // Build the list of the aircraft contact points and take opportunity of the
386  // loop to find which one is closer to (or deeper into) the ground.
387  for (int i = 0; i < GroundReactions->GetNumGearUnits(); ++i) {
388  ContactPoints c;
389  FGLGear* gear = GroundReactions->GetGearUnit(i);
390 
391  // Skip the retracted landing gears
392  if (!gear->GetGearUnitDown())
393  continue;
394 
395  c.location = gear->GetBodyLocation();
396  FGLocation gearLoc = CGLocation.LocalToLocation(Tb2l * c.location);
397 
398  FGColumnVector3 normal, vDummy;
399  FGLocation lDummy;
400  double height = fdmex->GetInertial()->GetContactPoint(gearLoc, lDummy,
401  normal, vDummy,
402  vDummy);
403 
404  if (gear->IsBogey() && !GroundReactions->GetSolid())
405  continue;
406 
407  c.normal = Tec2b * normal;
408  contacts.push_back(c);
409 
410  if (height < hmin) {
411  hmin = height;
412  contactRef = contacts.size() - 1;
413  }
414  }
415 
416  if (contacts.size() < 3)
417  return;
418 
419  // Remove the contact point that is closest to the ground from the list:
420  // the rotation axis will be going thru this point so we need to remove it
421  // to avoid divisions by zero that could result from the computation of
422  // the rotations.
423  FGColumnVector3 contact0 = contacts[contactRef].location;
424  contacts.erase(contacts.begin() + contactRef);
425 
426  // Update the initial conditions: this should remove the forces generated
427  // by overcompressed landing gears
428  fgic.SetAltitudeASLFtIC(fgic.GetAltitudeASLFtIC() - hmin);
429  fdmex->Initialize(&fgic);
430  fdmex->Run();
431 
432  // Compute the rotation axis: it is obtained from the direction of the
433  // moment measured at the contact point 'contact0'
434  FGColumnVector3 force = MassBalance->GetMass() * Accelerations->GetUVWdot();
435  FGColumnVector3 moment = MassBalance->GetJ() * Accelerations->GetPQRdot()
436  + force * contact0;
437  FGColumnVector3 rotationAxis = moment.Normalize();
438 
439  // Compute the rotation parameters: angle and the first point to come into
440  // contact with the ground when the rotation is applied.
441  RotationParameters rParam = calcRotation(contacts, rotationAxis, contact0);
442  FGQuaternion q0(rParam.angleMin, rotationAxis);
443 
444  // Apply the computed rotation to all the contact points
445  FGMatrix33 rot = q0.GetTInv();
446  vector<ContactPoints>::iterator iter;
447  for (iter = contacts.begin(); iter != contacts.end(); ++iter)
448  iter->location = contact0 + rot * (iter->location - contact0);
449 
450  // Remove the second point to come in contact with the ground from the list.
451  // The reason is the same than above: avoid divisions by zero when the next
452  // rotation will be computed.
453  FGColumnVector3 contact1 = rParam.contactRef->location;
454  contacts.erase(rParam.contactRef);
455 
456  // Compute the rotation axis: now there are 2 points in contact with the
457  // ground so the only option for the aircraft is to rotate around the axis
458  // generated by these 2 points.
459  rotationAxis = contact1 - contact0;
460  // Make sure that the rotation orientation is consistent with the moment.
461  if (DotProduct(rotationAxis, moment) < 0.0)
462  rotationAxis = contact0 - contact1;
463 
464  rotationAxis.Normalize();
465 
466  // Compute the rotation parameters
467  rParam = calcRotation(contacts, rotationAxis, contact0);
468  FGQuaternion q1(rParam.angleMin, rotationAxis);
469 
470  // Update the aircraft orientation
471  FGColumnVector3 euler = (fgic.GetOrientation() * q0 * q1).GetEuler();
472 
473  fgic.SetPhiRadIC(euler(1));
474  fgic.SetThetaRadIC(euler(2));
475  fgic.SetPsiRadIC(euler(3));
476 }
477 
478 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479 // Given a set of points and a rotation axis, this routine computes for each
480 // point the rotation angle that would drive the point in contact with the
481 // plane. It returns the minimum angle as well as the point with which this
482 // angle has been obtained.
483 // The rotation axis is defined by a vector 'u' and a point 'M0' on the axis.
484 // Since we are in the body frame, the position if 'M0' is measured from the CG
485 // hence the name 'GM0'.
486 
487 FGTrim::RotationParameters FGTrim::calcRotation(vector<ContactPoints>& contacts,
488  const FGColumnVector3& u,
489  const FGColumnVector3& GM0)
490 {
491  RotationParameters rParam;
492  vector<ContactPoints>::iterator iter;
493 
494  rParam.angleMin = 3.0 * M_PI;
495 
496  for (iter = contacts.begin(); iter != contacts.end(); ++iter) {
497  // Below the processed contact point is named 'M'
498  // Construct an orthonormal basis (u, v, t). The ground normal is obtained
499  // from iter->normal.
500  FGColumnVector3 t = u * iter->normal;
501  double length = t.Magnitude();
502  t /= length; // Normalize the tangent
503  FGColumnVector3 v = t * u;
504  FGColumnVector3 MM0 = GM0 - iter->location;
505  // d0 is the distance from the circle center 'C' to the reference point 'M0'
506  double d0 = DotProduct(MM0, u);
507  // Compute the square of the circle radius i.e. the square of the distance
508  // between 'C' and 'M'.
509  double sqrRadius = DotProduct(MM0, MM0) - d0 * d0;
510  // Compute the distance from the circle center 'C' to the line made by the
511  // intersection between the ground and the plane that contains the circle.
512  double DistPlane = d0 * DotProduct(u, iter->normal) / length;
513  // The coordinate of the point of intersection 'P' between the circle and
514  // the ground is (0, DistPlane, alpha) in the basis (u, v, t)
515  double mag = sqrRadius - DistPlane * DistPlane;
516  if (mag < 0) {
517  cout << "FGTrim::calcRotation DistPlane^2 larger than sqrRadius" << endl;
518  }
519  double alpha = sqrt(max(mag, 0.0));
520  FGColumnVector3 CP = alpha * t + DistPlane * v;
521  // The transformation is now constructed: we can extract the angle using the
522  // classical formulas (cosine is obtained from the dot product and sine from
523  // the cross product).
524  double cosine = -DotProduct(MM0, CP) / sqrRadius;
525  double sine = DotProduct(MM0 * u, CP) / sqrRadius;
526  double angle = atan2(sine, cosine);
527  if (angle < 0.0) angle += 2.0 * M_PI;
528  if (angle < rParam.angleMin) {
529  rParam.angleMin = angle;
530  rParam.contactRef = iter;
531  }
532  }
533 
534  return rParam;
535 }
536 
537 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
538 
539 bool FGTrim::solve(FGTrimAxis& axis) {
540 
541  double x1,x2,x3,f1,f2,f3,d,d0;
542  const double relax =0.9;
543  double eps=axis.GetSolverEps();
544 
545  x1=x2=x3=0;
546  d=1;
547  bool success=false;
548  //initializations
549  if( solutionDomain != 0) {
550  /* if(ahi > alo) { */
551  x1=xlo;f1=alo;
552  x3=xhi;f3=ahi;
553  /* } else {
554  x1=xhi;f1=ahi;
555  x3=xlo;f3=alo;
556  } */
557  d0=fabs(x3-x1);
558  //iterations
559  //max_sub_iterations=axis.GetIterationLimit();
560  while ( (axis.InTolerance() == false )
561  && (fabs(d) > eps) && (Nsub < max_sub_iterations)) {
562  Nsub++;
563  d=(x3-x1)/d0;
564  x2=x1-d*d0*f1/(f3-f1);
565  axis.SetControl(x2);
566  axis.Run();
567  f2=axis.GetState();
568  if(Debug > 1) {
569  cout << "FGTrim::solve Nsub,x1,x2,x3: " << Nsub << ", " << x1
570  << ", " << x2 << ", " << x3 << endl;
571  cout << " " << f1 << ", " << f2 << ", " << f3 << endl;
572  }
573  if(f1*f2 <= 0.0) {
574  x3=x2;
575  f3=f2;
576  f1=relax*f1;
577  //cout << "Solution is between x1 and x2" << endl;
578  }
579  else if(f2*f3 <= 0.0) {
580  x1=x2;
581  f1=f2;
582  f3=relax*f3;
583  //cout << "Solution is between x2 and x3" << endl;
584 
585  }
586  //cout << i << endl;
587 
588 
589  }//end while
590  if(Nsub < max_sub_iterations) success=true;
591  }
592  return success;
593 }
594 
595 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
596 /*
597  produces an interval (xlo..xhi) on one side or the other of the current
598  control value in which a solution exists. This domain is, hopefully,
599  smaller than xmin..0 or 0..xmax and the solver will require fewer iterations
600  to find the solution. This is, hopefully, more efficient than having the
601  solver start from scratch every time. Maybe it isn't though...
602  This tries to take advantage of the idea that the changes from iteration to
603  iteration will be small after the first one or two top-level iterations.
604 
605  assumes that changing the control will a produce significant change in the
606  accel i.e. checkLimits() has already been called.
607 
608  if a solution is found above the current control, the function returns true
609  and xlo is set to the current control, xhi to the interval max it found, and
610  solutionDomain is set to 1.
611  if the solution lies below the current control, then the function returns
612  true and xlo is set to the interval min it found and xmax to the current
613  control. if no solution is found, then the function returns false.
614 
615 
616  in all cases, alo=accel(xlo) and ahi=accel(xhi) after the function exits.
617  no assumptions about the state of the sim after this function has run
618  can be made.
619 */
620 bool FGTrim::findInterval(FGTrimAxis& axis) {
621  bool found=false;
622  double step;
623  double current_control=axis.GetControl();
624  double current_accel=axis.GetState();;
625  double xmin=axis.GetControlMin();
626  double xmax=axis.GetControlMax();
627  double lastxlo,lastxhi,lastalo,lastahi;
628 
629  step=0.025*fabs(xmax);
630  xlo=xhi=current_control;
631  alo=ahi=current_accel;
632  lastxlo=xlo;lastxhi=xhi;
633  lastalo=alo;lastahi=ahi;
634  do {
635 
636  Nsub++;
637  step*=2;
638  xlo-=step;
639  if(xlo < xmin) xlo=xmin;
640  xhi+=step;
641  if(xhi > xmax) xhi=xmax;
642  axis.SetControl(xlo);
643  axis.Run();
644  alo=axis.GetState();
645  axis.SetControl(xhi);
646  axis.Run();
647  ahi=axis.GetState();
648  if(fabs(ahi-alo) <= axis.GetTolerance()) continue;
649  if(alo*ahi <=0) { //found interval with root
650  found=true;
651  if(alo*current_accel <= 0) { //narrow interval down a bit
652  solutionDomain=-1;
653  xhi=lastxlo;
654  ahi=lastalo;
655  //xhi=current_control;
656  //ahi=current_accel;
657  } else {
658  solutionDomain=1;
659  xlo=lastxhi;
660  alo=lastahi;
661  //xlo=current_control;
662  //alo=current_accel;
663  }
664  }
665  lastxlo=xlo;lastxhi=xhi;
666  lastalo=alo;lastahi=ahi;
667  if( !found && xlo==xmin && xhi==xmax ) continue;
668  if(Debug > 1)
669  cout << "FGTrim::findInterval: Nsub=" << Nsub << " Lo= " << xlo
670  << " Hi= " << xhi << " alo*ahi: " << alo*ahi << endl;
671  } while(!found && (Nsub <= max_sub_iterations) );
672  return found;
673 }
674 
675 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
676 //checks to see which side of the current control value the solution is on
677 //and sets solutionDomain accordingly:
678 // 1 if solution is between the current and max
679 // -1 if solution is between the min and current
680 // 0 if there is no solution
681 //
682 //if changing the control produces no significant change in the accel then
683 //solutionDomain is set to zero and the function returns false
684 //if a solution is found, then xlo and xhi are set so that they bracket
685 //the solution, alo is set to accel(xlo), and ahi is set to accel(xhi)
686 //if there is no change or no solution then xlo=xmin, alo=accel(xmin) and
687 //xhi=xmax and ahi=accel(xmax)
688 //in all cases the sim is left such that the control=xmax and accel=ahi
689 
690 bool FGTrim::checkLimits(FGTrimAxis& axis)
691 {
692  bool solutionExists;
693  double current_control=axis.GetControl();
694  double current_accel=axis.GetState();
695  xlo=axis.GetControlMin();
696  xhi=axis.GetControlMax();
697 
698  axis.SetControl(xlo);
699  axis.Run();
700  alo=axis.GetState();
701  axis.SetControl(xhi);
702  axis.Run();
703  ahi=axis.GetState();
704  if(Debug > 1)
705  cout << "checkLimits() xlo,xhi,alo,ahi: " << xlo << ", " << xhi << ", "
706  << alo << ", " << ahi << endl;
707  solutionDomain=0;
708  solutionExists=false;
709  if(fabs(ahi-alo) > axis.GetTolerance()) {
710  if(alo*current_accel <= 0) {
711  solutionExists=true;
712  solutionDomain=-1;
713  xhi=current_control;
714  ahi=current_accel;
715  } else if(current_accel*ahi < 0){
716  solutionExists=true;
717  solutionDomain=1;
718  xlo=current_control;
719  alo=current_accel;
720  }
721  }
722  axis.SetControl(current_control);
723  axis.Run();
724  return solutionExists;
725 }
726 
727 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728 
729 void FGTrim::setupPullup() {
730  double g,q,cgamma;
731  g=fdmex->GetInertial()->GetGravity().Magnitude();
732  cgamma=cos(fgic.GetFlightPathAngleRadIC());
733  cout << "setPitchRateInPullup(): " << g << ", " << cgamma << ", "
734  << fgic.GetVtrueFpsIC() << endl;
735  q=g*(targetNlf-cgamma)/fgic.GetVtrueFpsIC();
736  cout << targetNlf << ", " << q << endl;
737  fgic.SetQRadpsIC(q);
738  cout << "setPitchRateInPullup() complete" << endl;
739 
740 }
741 
742 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
743 
744 void FGTrim::setupTurn(void){
745  double g,phi;
746  phi = fgic.GetPhiRadIC();
747  if( fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
748  targetNlf = 1 / cos(phi);
749  g = fdmex->GetInertial()->GetGravity().Magnitude();
750  psidot = g*tan(phi) / fgic.GetUBodyFpsIC();
751  cout << targetNlf << ", " << psidot << endl;
752  }
753 
754 }
755 
756 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
757 
758 void FGTrim::updateRates(void){
759  if( mode == tTurn ) {
760  double phi = fgic.GetPhiRadIC();
761  double g = fdmex->GetInertial()->GetGravity().Magnitude();
762  double p,q,r,theta;
763  if(fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
764  theta=fgic.GetThetaRadIC();
765  phi=fgic.GetPhiRadIC();
766  psidot = g*tan(phi) / fgic.GetUBodyFpsIC();
767  p=-psidot*sin(theta);
768  q=psidot*cos(theta)*sin(phi);
769  r=psidot*cos(theta)*cos(phi);
770  } else {
771  p=q=r=0;
772  }
773  fgic.SetPRadpsIC(p);
774  fgic.SetQRadpsIC(q);
775  fgic.SetRRadpsIC(r);
776  } else if( mode == tPullup && fabs(targetNlf-1) > 0.01) {
777  double g,q,cgamma;
778  g=fdmex->GetInertial()->GetGravity().Magnitude();
779  cgamma=cos(fgic.GetFlightPathAngleRadIC());
780  q=g*(targetNlf-cgamma)/fgic.GetVtrueFpsIC();
781  fgic.SetQRadpsIC(q);
782  }
783 }
784 
785 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
786 
787 void FGTrim::setDebug(FGTrimAxis& axis) {
788  if(debug_axis == tAll ||
789  axis.GetStateType() == debug_axis ) {
790  Debug=DebugLevel;
791  return;
792  } else {
793  Debug=0;
794  return;
795  }
796 }
797 
798 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799 
800 void FGTrim::SetMode(TrimMode tt) {
801  ClearStates();
802  mode=tt;
803  switch(tt) {
804  case tFull:
805  if (debug_lvl > 0)
806  cout << " Full Trim" << endl;
807  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tWdot,tAlpha));
808  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
809  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
810  //TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tHmgt,tBeta ));
811  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tVdot,tPhi ));
812  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
813  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
814  break;
815  case tLongitudinal:
816  if (debug_lvl > 0)
817  cout << " Longitudinal Trim" << endl;
818  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tWdot,tAlpha ));
819  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
820  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
821  break;
822  case tGround:
823  if (debug_lvl > 0)
824  cout << " Ground Trim" << endl;
825  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tWdot,tAltAGL ));
826  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tTheta ));
827  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tPdot,tPhi ));
828  break;
829  case tPullup:
830  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tNlf,tAlpha ));
831  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
832  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
833  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tHmgt,tBeta ));
834  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tVdot,tPhi ));
835  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
836  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
837  break;
838  case tTurn:
839  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tWdot,tAlpha ));
840  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
841  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
842  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tVdot,tBeta ));
843  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
844  TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
845  break;
846  case tCustom:
847  case tNone:
848  break;
849  }
850  //cout << "TrimAxes.size(): " << TrimAxes.size() << endl;
851  sub_iterations.resize(TrimAxes.size());
852  successful.resize(TrimAxes.size());
853  solution.resize(TrimAxes.size());
854 }
855 //YOU WERE WARNED, BUT YOU DID IT ANYWAY.
856 }
JSBSim::FGFDMExec
Encapsulates the JSBSim simulation executive.
Definition: FGFDMExec.h:185
JSBSim::FGTrim::RemoveState
bool RemoveState(State state)
Remove a specific state-control pair from the current configuration.
Definition: FGTrim.cpp:148
JSBSim::FGPropagate::GetTb2l
const FGMatrix33 & GetTb2l(void) const
Retrieves the body-to-local transformation matrix.
Definition: FGPropagate.h:471
JSBSim::FGAccelerations::GetUVWdot
const FGColumnVector3 & GetUVWdot(void) const
Retrieves the body axis acceleration.
Definition: FGAccelerations.h:130
JSBSim::FGLocation::LocalToLocation
FGLocation LocalToLocation(const FGColumnVector3 &lvec) const
Conversion from Local frame coordinates to a location in the earth centered and fixed frame.
Definition: FGLocation.h:326
JSBSim::FGTrim::EditState
bool EditState(State state, Control new_control)
Change the control used to zero a state previously configured.
Definition: FGTrim.cpp:171
JSBSim::FGSurface::GetSolid
bool GetSolid(void)
Gets the surface is a solid flag value.
Definition: FGSurface.h:112
JSBSim::FGLGear::SetReport
void SetReport(bool flag)
Set the console touchdown reporting feature.
Definition: FGLGear.h:268
JSBSim::FGFDMExec::SuspendIntegration
void SuspendIntegration(void)
Suspends the simulation and sets the delta T to zero.
Definition: FGFDMExec.h:548
JSBSim::FGTrim::ClearStates
void ClearStates(void)
Clear all state-control pairs from the current configuration.
Definition: FGTrim.cpp:122
JSBSim::FGColumnVector3
This class implements a 3 element column vector.
Definition: FGColumnVector3.h:63
JSBSim::FGLocation
FGLocation holds an arbitrary location in the Earth centered Earth fixed reference frame (ECEF).
Definition: FGLocation.h:151
JSBSim::FGInitialCondition::GetOrientation
const FGQuaternion & GetOrientation(void) const
Gets the initial orientation.
Definition: FGInitialCondition.h:648
JSBSim::FGFDMExec::GetIC
FGInitialCondition * GetIC(void)
Returns a pointer to the FGInitialCondition object.
Definition: FGFDMExec.h:387
JSBSim::FGFCS::GetPitchTrimCmd
double GetPitchTrimCmd(void) const
Gets the pitch trim command.
Definition: FGFCS.h:264
JSBSim::FGFDMExec::GetInertial
FGInertial * GetInertial(void)
Returns the FGInertial pointer.
Definition: FGFDMExec.h:367
JSBSim::FGFCS::GetDeCmd
double GetDeCmd(void) const
Gets the elevator command.
Definition: FGFCS.h:216
JSBSim::FGMassBalance
Models weight, balance and moment of inertia information.
Definition: FGMassBalance.h:122
JSBSim::FGFCS::SetDeCmd
void SetDeCmd(double cmd)
Sets the elevator command.
Definition: FGFCS.h:377
JSBSim::FGFCS::GetThrottleCmd
double GetThrottleCmd(int engine) const
Gets the throttle command.
Definition: FGFCS.cpp:351
JSBSim::FGFCS::SetThrottleCmd
void SetThrottleCmd(int engine, double cmd)
Sets the throttle command for the specified engine.
Definition: FGFCS.cpp:315
JSBSim::FGFDMExec::GetGroundReactions
FGGroundReactions * GetGroundReactions(void)
Returns the FGGroundReactions pointer.
Definition: FGFDMExec.h:369
JSBSim::FGLGear::GetBodyLocation
FGColumnVector3 GetBodyLocation(void) const
Gets the location of the gear in Body axes.
Definition: FGLGear.h:241
JSBSim::FGInitialCondition::SetPRadpsIC
void SetPRadpsIC(double P)
Sets the initial body axis roll rate.
Definition: FGInitialCondition.h:428
JSBSim::FGFCS::GetDaCmd
double GetDaCmd(void) const
Gets the aileron command.
Definition: FGFCS.h:212
JSBSim::FGLGear
Landing gear model.
Definition: FGLGear.h:190
JSBSim::FGTrim::DoTrim
bool DoTrim(void)
Execute the trim.
Definition: FGTrim.cpp:186
JSBSim::FGTrim::SetMode
void SetMode(TrimMode tm)
Clear all state-control pairs and set a predefined trim mode.
Definition: FGTrim.cpp:800
JSBSim::FGInitialCondition::SetAltitudeASLFtIC
void SetAltitudeASLFtIC(double altitudeASL)
Sets the altitude above sea level initial condition in feet.
Definition: FGInitialCondition.cpp:748
JSBSim::FGMatrix33
Handles matrix math operations.
Definition: FGMatrix33.h:69
JSBSim::FGFDMExec::ResumeIntegration
void ResumeIntegration(void)
Resumes the simulation by resetting delta T to the correct value.
Definition: FGFDMExec.h:551
JSBSim::FGInitialCondition::SetPsiRadIC
void SetPsiRadIC(double psi)
Sets the initial heading angle.
Definition: FGInitialCondition.h:595
JSBSim::FGFCS::SetDaCmd
void SetDaCmd(double cmd)
Sets the aileron command.
Definition: FGFCS.h:373
JSBSim::FGColumnVector3::Magnitude
double Magnitude(void) const
Length of the vector.
Definition: FGColumnVector3.cpp:109
JSBSim::FGInitialCondition::GetThetaRadIC
double GetThetaRadIC(void) const
Gets the initial pitch angle.
Definition: FGInitialCondition.h:656
JSBSim::FGFDMExec::GetPropagate
FGPropagate * GetPropagate(void)
Returns the FGPropagate pointer.
Definition: FGFDMExec.h:377
JSBSim::FGInitialCondition::SetThetaRadIC
void SetThetaRadIC(double theta)
Sets the initial pitch angle.
Definition: FGInitialCondition.h:591
JSBSim::FGInitialCondition::GetAltitudeASLFtIC
double GetAltitudeASLFtIC(void) const
Gets the initial altitude above sea level.
Definition: FGInitialCondition.cpp:649
JSBSim::FGFDMExec::GetMassBalance
FGMassBalance * GetMassBalance(void)
Returns the FGAircraft pointer.
Definition: FGFDMExec.h:363
JSBSim::FGInitialCondition::GetFlightPathAngleRadIC
double GetFlightPathAngleRadIC(void) const
Gets the initial flight path angle.
Definition: FGInitialCondition.h:618
JSBSim::FGPropagate
Models the EOM and integration/propagation of state.
Definition: FGPropagate.h:93
JSBSim::FGFCS::SetPitchTrimCmd
void SetPitchTrimCmd(double cmd)
Sets the pitch trim command.
Definition: FGFCS.h:401
JSBSim::FGInitialCondition::GetVtrueFpsIC
double GetVtrueFpsIC(void) const
Gets the initial true velocity.
Definition: FGInitialCondition.h:474
JSBSim::FGPropagate::GetTec2b
const FGMatrix33 & GetTec2b(void) const
Retrieves the ECEF-to-body transformation matrix.
Definition: FGPropagate.h:475
JSBSim::FGGroundReactions
Manages ground reactions modeling.
Definition: FGGroundReactions.h:79
JSBSim::FGFDMExec::GetFCS
FGFCS * GetFCS(void)
Returns the FGFCS pointer.
Definition: FGFDMExec.h:359
JSBSim::FGInitialCondition::GetAltitudeAGLFtIC
double GetAltitudeAGLFtIC(void) const
Gets the initial altitude above ground level.
Definition: FGInitialCondition.cpp:656
JSBSim::FGTrim::TrimStats
void TrimStats()
Iteration statistics.
Definition: FGTrim.cpp:92
JSBSim::FGAccelerations
Handles the calculation of accelerations.
Definition: FGAccelerations.h:95
JSBSim::FGFCS::GetDrCmd
double GetDrCmd(void) const
Gets the rudder command.
Definition: FGFCS.h:220
JSBSim::FGFCS
Encapsulates the Flight Control System (FCS) functionality.
Definition: FGFCS.h:187
JSBSim::FGColumnVector3::Normalize
FGColumnVector3 & Normalize(void)
Normalize.
Definition: FGColumnVector3.cpp:116
JSBSim::FGGroundReactions::GetGearUnit
FGLGear * GetGearUnit(int gear) const
Gets a gear instance.
Definition: FGGroundReactions.h:110
JSBSim::FGInitialCondition::SetPhiRadIC
void SetPhiRadIC(double phi)
Sets the initial roll angle.
Definition: FGInitialCondition.h:587
JSBSim::FGInitialCondition::GetPhiRadIC
double GetPhiRadIC(void) const
Gets the initial roll angle.
Definition: FGInitialCondition.h:652
JSBSim::FGInitialCondition::GetUBodyFpsIC
double GetUBodyFpsIC(void) const
Gets the initial body axis X velocity.
Definition: FGInitialCondition.h:534
JSBSim::FGTrim::Report
void Report(void)
Print the results of the trim.
Definition: FGTrim.cpp:113
JSBSim::FGInitialCondition::SetQRadpsIC
void SetQRadpsIC(double Q)
Sets the initial body axis pitch rate.
Definition: FGInitialCondition.h:432
JSBSim::FGGroundReactions::GetNumGearUnits
int GetNumGearUnits(void) const
Gets the number of gears.
Definition: FGGroundReactions.h:105
JSBSim::FGTrimAxis
Definition: FGTrimAxis.h:87
JSBSim::FGFCS::SetDrCmd
void SetDrCmd(double cmd)
Sets the rudder command.
Definition: FGFCS.h:381
JSBSim::FGMassBalance::GetJ
const FGMatrix33 & GetJ(void) const
Returns the inertia matrix expressed in the body frame.
Definition: FGMassBalance.h:193
JSBSim::FGInertial::GetContactPoint
double GetContactPoint(const FGLocation &location, FGLocation &contact, FGColumnVector3 &normal, FGColumnVector3 &velocity, FGColumnVector3 &ang_velocity) const
Get terrain contact point information below the current location.
Definition: FGInertial.h:103
JSBSim::FGTrim::AddState
bool AddState(State state, Control control)
Add a state-control pair to the current configuration.
Definition: FGTrim.cpp:130
JSBSim::FGAccelerations::GetPQRdot
const FGColumnVector3 & GetPQRdot(void) const
Retrieves the body axis angular acceleration vector.
Definition: FGAccelerations.h:161
JSBSim::FGFDMExec::Initialize
void Initialize(FGInitialCondition *FGIC)
Initializes the simulation with initial conditions.
Definition: FGFDMExec.cpp:599
JSBSim::FGFDMExec::GetAccelerations
FGAccelerations * GetAccelerations(void)
Returns the FGAccelerations pointer.
Definition: FGFDMExec.h:355
JSBSim::FGFDMExec::Run
bool Run(void)
This function executes each scheduled model in succession.
Definition: FGFDMExec.cpp:325
JSBSim::FGInitialCondition::SetRRadpsIC
void SetRRadpsIC(double R)
Sets the initial body axis yaw rate.
Definition: FGInitialCondition.h:436
JSBSim::FGInitialCondition::GetTargetNlfIC
double GetTargetNlfIC(void) const
Gets the target normal load factor set from IC.
Definition: FGInitialCondition.h:668