FlightGear next
FGTrim.cpp
Go to the documentation of this file.
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--------------------------------------------------------------------------------
289/8/99 TP Created
29
30FUNCTIONAL DESCRIPTION
31--------------------------------------------------------------------------------
32
33This class takes the given set of IC's and finds the angle of attack, elevator,
34and throttle setting required to fly steady level. This is currently for in-air
35conditions only. It is implemented using an iterative, one-axis-at-a-time
36scheme. */
37
38// !!!!!!! BEWARE ALL YE WHO ENTER HERE !!!!!!!
39
40/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41INCLUDES
42%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
43
44#include <iomanip>
45#include "FGTrim.h"
46#include "models/FGInertial.h"
49#include "models/FGFCS.h"
50
51#if _MSC_VER
52#pragma warning (disable : 4786 4788)
53#endif
54
55using namespace std;
56
57namespace JSBSim {
58
59//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60
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
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
113void 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
130bool 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
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
171bool 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
186bool 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
372void FGTrim::trimOnGround(void)
373{
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
487FGTrim::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
539bool 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*/
620bool 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
690bool 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
729void 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
744void 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
758void 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
787void 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
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}
#define p(x)
#define M_PI
Definition FGJSBBase.h:50
JSBSim::FGFDMExec * FDMExec
Definition JSBSim.cpp:88
#define i(x)
const FGColumnVector3 & GetUVWdot(void) const
Retrieves the body axis acceleration.
const FGColumnVector3 & GetPQRdot(void) const
Retrieves the body axis angular acceleration vector.
This class implements a 3 element column vector.
FGColumnVector3 & Normalize(void)
Normalize.
double GetDeCmd(void) const
Gets the elevator command.
Definition FGFCS.h:216
void SetPitchTrimCmd(double cmd)
Sets the pitch trim command.
Definition FGFCS.h:401
void SetDeCmd(double cmd)
Sets the elevator command.
Definition FGFCS.h:377
double GetDrCmd(void) const
Gets the rudder command.
Definition FGFCS.h:220
void SetDaCmd(double cmd)
Sets the aileron command.
Definition FGFCS.h:373
double GetThrottleCmd(int engine) const
Gets the throttle command.
Definition FGFCS.cpp:351
void SetThrottleCmd(int engine, double cmd)
Sets the throttle command for the specified engine.
Definition FGFCS.cpp:315
void SetDrCmd(double cmd)
Sets the rudder command.
Definition FGFCS.h:381
double GetDaCmd(void) const
Gets the aileron command.
Definition FGFCS.h:212
double GetPitchTrimCmd(void) const
Gets the pitch trim command.
Definition FGFCS.h:264
FGPropagate * GetPropagate(void)
Returns the FGPropagate pointer.
Definition FGFDMExec.h:377
FGAccelerations * GetAccelerations(void)
Returns the FGAccelerations pointer.
Definition FGFDMExec.h:355
FGMassBalance * GetMassBalance(void)
Returns the FGAircraft pointer.
Definition FGFDMExec.h:363
FGGroundReactions * GetGroundReactions(void)
Returns the FGGroundReactions pointer.
Definition FGFDMExec.h:369
FGInertial * GetInertial(void)
Returns the FGInertial pointer.
Definition FGFDMExec.h:367
Manages ground reactions modeling.
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
static constexpr double degtorad
Definition FGJSBBase.h:349
static short debug_lvl
Definition FGJSBBase.h:190
Landing gear model.
Definition FGLGear.h:191
bool GetGearUnitDown(void) const
Definition FGLGear.h:284
bool IsBogey(void) const
Definition FGLGear.h:312
FGColumnVector3 GetBodyLocation(void) const
Gets the location of the gear in Body axes.
Definition FGLGear.h:241
FGLocation holds an arbitrary location in the Earth centered Earth fixed reference frame (ECEF).
Definition FGLocation.h:152
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
Models weight, balance and moment of inertia information.
double GetMass(void) const
const FGMatrix33 & GetJ(void) const
Returns the inertia matrix expressed in the body frame.
Handles matrix math operations.
Definition FGMatrix33.h:70
Models the EOM and integration/propagation of state.
Definition FGPropagate.h:93
const FGLocation & GetLocation(void) const
const FGMatrix33 & GetTec2b(void) const
Retrieves the ECEF-to-body transformation matrix.
const FGMatrix33 & GetTb2l(void) const
Retrieves the body-to-local transformation matrix.
void SetMode(TrimMode tm)
Clear all state-control pairs and set a predefined trim mode.
Definition FGTrim.cpp:800
bool DoTrim(void)
Execute the trim.
Definition FGTrim.cpp:186
void Report(void)
Print the results of the trim.
Definition FGTrim.cpp:113
void TrimStats()
Iteration statistics.
Definition FGTrim.cpp:92
void ClearStates(void)
Clear all state-control pairs from the current configuration.
Definition FGTrim.cpp:122
~FGTrim(void)
Definition FGTrim.cpp:86
bool EditState(State state, Control new_control)
Change the control used to zero a state previously configured.
Definition FGTrim.cpp:171
bool AddState(State state, Control control)
Add a state-control pair to the current configuration.
Definition FGTrim.cpp:130
bool RemoveState(State state)
Remove a specific state-control pair from the current configuration.
Definition FGTrim.cpp:148
FGTrim(FGFDMExec *FDMExec, TrimMode tm=tGround)
Initializes the trimming class.
Definition FGTrim.cpp:61
#define N
@ tAltAGL
Definition FGTrimAxis.h:84
@ tPitchTrim
Definition FGTrimAxis.h:85
@ tRudder
Definition FGTrimAxis.h:84
@ tThrottle
Definition FGTrimAxis.h:84
@ tAileron
Definition FGTrimAxis.h:84
TrimMode
Definition FGTrim.h:65
@ tCustom
Definition FGTrim.h:66
@ tFull
Definition FGTrim.h:65
@ tTurn
Definition FGTrim.h:66
@ tLongitudinal
Definition FGTrim.h:65
@ tPullup
Definition FGTrim.h:65
@ tNone
Definition FGTrim.h:66
@ tGround
Definition FGTrim.h:65
double DotProduct(const FGColumnVector3 &v1, const FGColumnVector3 &v2)
Dot product of two vectors Compute and return the euclidean dot (or scalar) product of two vectors v1...
State
Models an aircraft axis for purposes of trimming.
Definition FGTrimAxis.h:83
const double g(9.80665)