1 /*******************************************************************************
7 --------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) ---------
9 This program is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free Software
11 Foundation; either version 2 of the License, or (at your option) any later
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 General Public License for more
19 You should have received a copy of the GNU 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.
23 Further information about the GNU General Public License can also be found on
24 the world wide web at http://www.gnu.org.
28 --------------------------------------------------------------------------------
32 FUNCTIONAL DESCRIPTION
33 --------------------------------------------------------------------------------
35 This class takes the given set of IC's and finds the angle of attack, elevator,
36 and throttle setting required to fly steady level. This is currently for in-air
37 conditions only. It is implemented using an iterative, one-axis-at-a-time
40 // !!!!!!! BEWARE ALL YE WHO ENTER HERE !!!!!!!
43 /*******************************************************************************
45 *******************************************************************************/
47 #include "FGFDMExec.h"
48 #include "FGAtmosphere.h"
49 #include "FGInitialCondition.h"
50 #include "FGTrimLong.h"
51 #include "FGAircraft.h"
53 /*******************************************************************************
55 *******************************************************************************/
57 FGTrimLong::FGTrimLong(FGFDMExec *FDMExec,FGInitialCondition *FGIC ) {
62 A_Tolerance = Tolerance / 10;
67 alphaMin=fdmex->GetAircraft()->GetAlphaCLMin()*RADTODEG;
68 alphaMax=fdmex->GetAircraft()->GetAlphaCLMax()*RADTODEG;
69 if(alphaMax <= alphaMin) {
73 udotf=&FGTrimLong::udot_func;
74 wdotf=&FGTrimLong::wdot_func;
75 qdotf=&FGTrimLong::qdot_func;
77 udot_subits=wdot_subits=qdot_subits=0;
83 /******************************************************************************/
85 FGTrimLong::~FGTrimLong(void) {}
87 /******************************************************************************/
89 void FGTrimLong::TrimStats() {
90 cout << endl << " Trim Statistics: " << endl;
91 cout << " Total Iterations: " << total_its << endl;
93 cout << " Sub-iterations:" << endl;
94 cout << " wdot: " << wdot_subits << " average: " << wdot_subits/total_its << endl;
95 cout << " udot: " << udot_subits << " average: " << udot_subits/total_its << endl;
96 cout << " qdot: " << qdot_subits << " average: " << qdot_subits/total_its << endl;
100 /******************************************************************************/
102 void FGTrimLong::Report(void) {
103 cout << endl << " Trim Results" << endl;
104 cout << " Alpha: " << fdmex->GetTranslation()->Getalpha()*RADTODEG
105 << " wdot: " << fdmex->GetTranslation()->GetUVWdot()(3)
106 << " Tolerance " << Tolerance << endl;
108 cout << " Throttle: " << fdmex->GetFCS()->GetThrottlePos(0)
109 << " udot: " << fdmex->GetTranslation()->GetUVWdot()(1)
110 << " Tolerance " << Tolerance << endl;
112 cout << " Elevator: " << fdmex->GetFCS()->GetDePos()*RADTODEG
113 << " qdot: " << fdmex->GetRotation()->GetPQRdot()(2)
114 << " Tolerance " << A_Tolerance << endl;
117 /******************************************************************************/
119 void FGTrimLong::ReportState(void) {
120 cout << endl << " JSBSim Trim Report" << endl;
121 cout << " Weight: " << fdmex->GetAircraft()->GetWeight()
122 << " lbs. CG x,y,z: " << fdmex->GetAircraft()->GetXYZcg()
123 << " inches " << endl;
126 float flaps=fdmex->GetFCS()->GetDfPos();
133 if(fdmex->GetAircraft()->GetGearUp() == true)
134 cout << "Up" << endl;
136 cout << "Down" << endl;
138 cout << " Speed: " << fdmex->GetAuxiliary()->GetVcalibratedKTS()
139 << " KCAS Mach: " << fdmex->GetState()->GetParameter(FG_MACH)
142 cout << " Altitude: " << fdmex->GetPosition()->Geth() << " ft" << endl;
145 cout << " Pitch Angle: " << fdmex->GetRotation()->Gettht()*RADTODEG
146 << " deg Angle of Attack: " << fdmex->GetState()->GetParameter(FG_ALPHA)*RADTODEG
150 cout << " Flight Path Angle: "
151 << fdmex->GetPosition()->GetGamma()*RADTODEG
155 cout << " Normal Load Factor: " << fdmex->GetAircraft()->GetNlf() << endl;
157 cout << " Pitch Rate: " << fdmex->GetState()->GetParameter(FG_PITCHRATE)*RADTODEG
160 cout << " Roll Angle: " << fdmex->GetRotation()->Getphi()*RADTODEG
161 << " deg Roll Rate: " << fdmex->GetState()->GetParameter(FG_ROLLRATE)
165 cout << " Sideslip: " << fdmex->GetState()->GetParameter(FG_BETA) *RADTODEG
166 << " deg Yaw Rate: " << fdmex->GetState()->GetParameter(FG_YAWRATE)*RADTODEG
167 << " deg/s " << endl;
169 cout << " Elevator: " << fdmex->GetState()->GetParameter(FG_ELEVATOR_POS)*RADTODEG
170 << " deg Left Aileron: " << fdmex->GetState()->GetParameter(FG_AILERON_POS)*RADTODEG
171 << " deg Rudder: " << fdmex->GetState()->GetParameter(FG_RUDDER_POS)*RADTODEG
174 cout << " Throttle: " << fdmex->GetFCS()->GetThrottlePos(0)/100 << endl;
177 /******************************************************************************/
179 void FGTrimLong::setThrottlesPct(float tt) {
182 for(int i=0;i<fdmex->GetAircraft()->GetNumEngines();i++) {
183 tMin=fdmex->GetAircraft()->GetEngine(i)->GetThrottleMin();
184 tMax=fdmex->GetAircraft()->GetEngine(i)->GetThrottleMax();
186 //cout << "setThrottlespct: " << i << ", " << tMin << ", " << tMax << ", " << dth << endl;
187 fdmex -> GetFCS() -> SetThrottleCmd(i,tMin+dth*(tMax-tMin));
191 /******************************************************************************/
193 int FGTrimLong::checkLimits(trimfp fp, float current, float min, float max) {
196 //cout << "Min: " << min << " Max: " << max << endl;
201 //cout << "Lo: " << lo << " Hi: " << hi << endl;
214 /******************************************************************************/
216 bool FGTrimLong::solve(trimfp fp,float guess,float desired, float *result,
217 float eps, float min, float max, int max_iterations, int *actual_its) {
219 float x1,x2,x3,f1,f2,f3,d,d0;
220 float const relax =0.9;
226 int side=checkLimits(fp,guess,min,max);
233 f1=(this->*fp)(x1)-desired;
234 f3=(this->*fp)(x3)-desired;
239 while ((fabs(d) > eps) && (i < max_iterations)) {
241 cout << "FGTrimLong::solve i,x1,x2,x3: " << i << ", " << x1
242 << ", " << x2 << ", " << x3 << endl;
244 x2=x1-d*d0*f1/(f3-f1);
249 f2=(this->*fp)(x2)-desired;
254 } else if(f2*f3 <= 0) {
262 if(i < max_iterations) {
273 /******************************************************************************/
275 bool FGTrimLong::findInterval(trimfp fp, float *lo, float *hi,float guess,
276 float desired,int max_iterations) {
279 float flo,fhi,fguess;
282 fguess=(this->*fp)(guess)-desired;
289 flo=(this->*fp)(xlo)-desired;
290 fhi=(this->*fp)(xhi)-desired;
291 if(flo*fhi <=0) { //found interval with root
293 if(flo*fguess <= 0) { //narrow interval down a bit
294 xhi=xlo+step; //to pass solver interval that is as
297 else if(fhi*fguess <= 0) {
302 cout << "FGTrimLong::findInterval: i=" << i << " Lo= " << xlo
303 << " Hi= " << xhi << " flo*fhi: " << flo*fhi << endl;
304 } while((found == 0) && (i <= max_iterations));
310 /******************************************************************************/
312 float FGTrimLong::udot_func(float x) {
315 return fdmex->GetTranslation()->GetUVWdot()(1);
318 /******************************************************************************/
320 float FGTrimLong::wdot_func(float x) {
321 fgic->SetAlphaDegIC(x);
323 return fdmex->GetTranslation()->GetUVWdot()(3);
326 /******************************************************************************/
328 float FGTrimLong::qdot_func(float x) {
329 fdmex->GetFCS()->SetPitchTrimCmd(x);
331 return fdmex->GetRotation()->GetPQRdot()(2);
334 /******************************************************************************/
336 bool FGTrimLong::DoTrim(void) {
341 if(fgic->GetVtrueKtsIC() < 1) {
342 cout << "Trim failed, on-ground trimming not yet implemented." << endl;
343 cout << "Or did you *really* mean to start in-air"
344 << " with less than 1 knot airspeed?" << endl;
349 fgic -> SetAlphaDegIC((alphaMin+alphaMax)/2);
350 fdmex -> GetFCS() -> SetDeCmd(0);
351 fdmex -> GetFCS() -> SetPitchTrimCmd(0);
352 setThrottlesPct(0.5);
353 fdmex -> RunIC(fgic);
355 if(trimudot == false)
359 solve(wdotf,fgic->GetAlphaDegIC(),0,&wdot,Tolerance,alphaMin, alphaMax,Naxis,&its);
362 cout << "Alpha: " << fdmex->GetTranslation()->Getalpha()*RADTODEG
363 << " wdot: " << fdmex->GetTranslation()->GetUVWdot()(3)
366 solve(udotf,dth,0,&udot,Tolerance,0,1,Naxis,&its);
369 cout << "Throttle: " << fdmex->GetFCS()->GetThrottlePos(0)
370 << " udot: " << fdmex->GetTranslation()->GetUVWdot()(1)
373 solve(qdotf,fdmex->GetFCS()->GetPitchTrimCmd(),0,&qdot,A_Tolerance,-1,1,Naxis,&its);
376 cout << "Elevator: " << fdmex->GetFCS()->GetDePos()*RADTODEG
377 << " qdot: " << fdmex->GetRotation()->GetPQRdot()(2)
380 wdot=fabs(fdmex->GetTranslation()->GetUVWdot()(3));
381 qdot=fabs(fdmex->GetRotation()->GetPQRdot()(2));
382 udot=fabs(fdmex->GetTranslation()->GetUVWdot()(1));
384 //these checks need to be done after all the axes have run
389 if(qdot < A_Tolerance)
391 if(axis_count == 2) {
393 //At this point we can check the input limits of the failed axis
394 //and declare the trim failed if there is no sign change. If there
395 //is, keep going until success or max iteration count
397 //Oh, well: two out of three ain't bad
398 if(wdot > Tolerance) {
399 if(checkLimits(wdotf,fgic->GetAlphaDegIC(),alphaMin,alphaMax) == false) {
400 cout << " Sorry, wdot doesn't appear to be trimmable" << endl;
402 k=Ncycles; //force the trim to fail
405 if( udot > Tolerance ) {
406 if(checkLimits(udotf,dth,0,1) == false) {
407 cout << " Sorry, udot doesn't appear to be trimmable" << endl;
408 cout << " Resetting throttles to zero" << endl;
410 fdmex->GetFCS()->SetThrottleCmd(-1,0);
413 k=Ncycles; //force the trim to fail
416 if(qdot > A_Tolerance) {
418 if(checkLimits(qdotf,fdmex->GetFCS()->GetPitchTrimCmd(),-1,1) == false) {
419 cout << " Sorry, qdot doesn't appear to be trimmable" << endl;
421 k=Ncycles; //force the trim to fail
426 } while((axis_count < 3) && (k < Ncycles));
427 if(axis_count >= 3) {
429 cout << endl << " Trim successful" << endl;
433 cout << endl << " Trim failed" << endl;
439 //YOU WERE WARNED, BUT YOU DID IT ANYWAY.