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 *******************************************************************************/
61 FGTrimLong::FGTrimLong(FGFDMExec *FDMExec,FGInitialCondition *FGIC ) {
66 A_Tolerance = Tolerance / 10;
71 alphaMin=fdmex->GetAircraft()->GetAlphaCLMin()*RADTODEG;
72 alphaMax=fdmex->GetAircraft()->GetAlphaCLMax()*RADTODEG;
73 if(alphaMax <= alphaMin) {
77 udotf=&FGTrimLong::udot_func;
78 wdotf=&FGTrimLong::wdot_func;
79 qdotf=&FGTrimLong::qdot_func;
81 udot_subits=wdot_subits=qdot_subits=0;
88 FGTrimLong::~FGTrimLong(void) {}
94 void FGTrimLong::TrimStats() {
95 cout << endl << " Trim Statistics: " << endl;
96 cout << " Total Iterations: " << total_its << endl;
98 cout << " Sub-iterations:" << endl;
99 cout << " wdot: " << wdot_subits << " average: " << wdot_subits/total_its << endl;
100 cout << " udot: " << udot_subits << " average: " << udot_subits/total_its << endl;
101 cout << " qdot: " << qdot_subits << " average: " << qdot_subits/total_its << endl;
105 void FGTrimLong::Report(void) {
106 cout << endl << " Trim Results" << endl;
107 cout << " Alpha: " << fdmex->GetTranslation()->Getalpha()*RADTODEG
108 << " wdot: " << fdmex->GetTranslation()->GetUVWdot()(3)
109 << " Tolerance " << Tolerance << endl;
111 cout << " Throttle: " << fdmex->GetFCS()->GetThrottlePos(0)
112 << " udot: " << fdmex->GetTranslation()->GetUVWdot()(1)
113 << " Tolerance " << Tolerance << endl;
115 cout << " Elevator: " << fdmex->GetFCS()->GetDePos()*RADTODEG
116 << " qdot: " << fdmex->GetRotation()->GetPQRdot()(2)
117 << " Tolerance " << A_Tolerance << endl;
120 void FGTrimLong::ReportState(void) {
121 cout << endl << " JSBSim Trim Report" << endl;
122 cout << " Weight: " << fdmex->GetAircraft()->GetWeight()
123 << " lbs. CG x,y,z: " << fdmex->GetAircraft()->GetXYZcg()
124 << " inches " << endl;
127 float flaps=fdmex->GetFCS()->GetDfPos();
134 if(fdmex->GetAircraft()->GetGearUp() == true)
135 cout << "Up" << endl;
137 cout << "Down" << endl;
139 cout << " Speed: " << fdmex->GetAuxiliary()->GetVcalibratedKTS()
140 << " KCAS Mach: " << fdmex->GetState()->GetParameter(FG_MACH)
143 cout << " Altitude: " << fdmex->GetPosition()->Geth() << " ft" << endl;
146 cout << " Pitch Angle: " << fdmex->GetRotation()->Gettht()*RADTODEG
147 << " deg Angle of Attack: " << fdmex->GetState()->GetParameter(FG_ALPHA)*RADTODEG
151 cout << " Flight Path Angle: "
152 << fdmex->GetPosition()->GetGamma()*RADTODEG
156 cout << " Normal Load Factor: " << fdmex->GetAircraft()->GetNlf() << endl;
158 cout << " Pitch Rate: " << fdmex->GetState()->GetParameter(FG_PITCHRATE)*RADTODEG
161 cout << " Roll Angle: " << fdmex->GetRotation()->Getphi()*RADTODEG
162 << " deg Roll Rate: " << fdmex->GetState()->GetParameter(FG_ROLLRATE)
166 cout << " Sideslip: " << fdmex->GetState()->GetParameter(FG_BETA) *RADTODEG
167 << " deg Yaw Rate: " << fdmex->GetState()->GetParameter(FG_YAWRATE)*RADTODEG
168 << " deg/s " << endl;
170 cout << " Elevator: " << fdmex->GetState()->GetParameter(FG_ELEVATOR_POS)*RADTODEG
171 << " deg Left Aileron: " << fdmex->GetState()->GetParameter(FG_AILERON_POS)*RADTODEG
172 << " deg Rudder: " << fdmex->GetState()->GetParameter(FG_RUDDER_POS)*RADTODEG
175 cout << " Throttle: " << fdmex->GetFCS()->GetThrottlePos(0)/100 << endl;
178 void FGTrimLong::setThrottlesPct(float tt) {
181 for(int i=0;i<fdmex->GetAircraft()->GetNumEngines();i++) {
182 tMin=fdmex->GetAircraft()->GetEngine(i)->GetThrottleMin();
183 tMax=fdmex->GetAircraft()->GetEngine(i)->GetThrottleMax();
185 //cout << "setThrottlespct: " << i << ", " << tMin << ", " << tMax << ", " << dth << endl;
186 fdmex -> GetFCS() -> SetThrottleCmd(i,tMin+dth*(tMax-tMin));
191 int FGTrimLong::checkLimits(trimfp fp, float current, float min, float max) {
194 //cout << "Min: " << min << " Max: " << max << endl;
199 //cout << "Lo: " << lo << " Hi: " << hi << endl;
212 bool FGTrimLong::solve(trimfp fp,float guess,float desired, float *result, float eps, float min, float max, int max_iterations, int *actual_its) {
214 float x1,x2,x3,f1,f2,f3,d,d0;
215 float const relax =0.9;
221 int side=checkLimits(fp,guess,min,max);
228 f1=(this->*fp)(x1)-desired;
229 f3=(this->*fp)(x3)-desired;
234 while ((fabs(d) > eps) && (i < max_iterations)) {
236 cout << "FGTrimLong::solve i,x1,x2,x3: " << i << ", " << x1 << ", " << x2 << ", " << x3 << endl;
239 x2=x1-d*d0*f1/(f3-f1);
244 f2=(this->*fp)(x2)-desired;
249 } else if(f2*f3 <= 0) {
257 if(i < max_iterations) {
268 bool FGTrimLong::findInterval(trimfp fp, float *lo, float *hi,float guess,float desired,int max_iterations) {
272 float flo,fhi,fguess;
275 fguess=(this->*fp)(guess)-desired;
282 flo=(this->*fp)(xlo)-desired;
283 fhi=(this->*fp)(xhi)-desired;
284 if(flo*fhi <=0) { //found interval with root
286 if(flo*fguess <= 0) { //narrow interval down a bit
287 xhi=xlo+step; //to pass solver interval that is as
290 else if(fhi*fguess <= 0) {
295 cout << "FGTrimLong::findInterval: i=" << i << " Lo= " << xlo << " Hi= " << xhi << " flo*fhi: " << flo*fhi << endl;
296 } while((found == 0) && (i <= max_iterations));
302 float FGTrimLong::udot_func(float x) {
305 return fdmex->GetTranslation()->GetUVWdot()(1);
308 float FGTrimLong::wdot_func(float x) {
309 fgic->SetAlphaDegIC(x);
311 return fdmex->GetTranslation()->GetUVWdot()(3);
314 float FGTrimLong::qdot_func(float x) {
315 fdmex->GetFCS()->SetPitchTrimCmd(x);
317 return fdmex->GetRotation()->GetPQRdot()(2);
320 bool FGTrimLong::DoTrim(void) {
321 int k=0,j=0,sum=0,trim_failed=0,jmax=Naxis;
323 float step,temp,min,max;
327 fgic -> SetAlphaDegIC((alphaMin+alphaMax)/2);
328 fdmex -> GetFCS() -> SetDeCmd(0);
329 fdmex -> GetFCS() -> SetPitchTrimCmd(0);
330 setThrottlesPct(0.5);
331 fdmex -> RunIC(fgic);
333 if(trimudot == false)
337 solve(wdotf,fgic->GetAlphaDegIC(),0,&wdot,Tolerance,alphaMin, alphaMax,Naxis,&its);
340 cout << "Alpha: " << fdmex->GetTranslation()->Getalpha()*RADTODEG
341 << " wdot: " << fdmex->GetTranslation()->GetUVWdot()(3)
344 solve(udotf,dth,0,&udot,Tolerance,0,1,Naxis,&its);
347 cout << "Throttle: " << fdmex->GetFCS()->GetThrottlePos(0)
348 << " udot: " << fdmex->GetTranslation()->GetUVWdot()(1)
351 solve(qdotf,fdmex->GetFCS()->GetPitchTrimCmd(),0,&qdot,A_Tolerance,-1,1,Naxis,&its);
354 cout << "Elevator: " << fdmex->GetFCS()->GetDePos()*RADTODEG
355 << " qdot: " << fdmex->GetRotation()->GetPQRdot()(2)
358 wdot=fabs(fdmex->GetTranslation()->GetUVWdot()(3));
359 qdot=fabs(fdmex->GetRotation()->GetPQRdot()(2));
360 udot=fabs(fdmex->GetTranslation()->GetUVWdot()(1));
362 //these checks need to be done after all the axes have run
367 if(qdot < A_Tolerance)
369 if(axis_count == 2) {
371 //At this point we can check the input limits of the failed axis
372 //and declare the trim failed if there is no sign change. If there
373 //is, keep going until success or max iteration count
375 //Oh, well: two out of three ain't bad
376 if(wdot > Tolerance) {
377 if(checkLimits(wdotf,fgic->GetAlphaDegIC(),alphaMin,alphaMax) == false) {
378 cout << " Sorry, wdot doesn't appear to be trimmable" << endl;
380 k=Ncycles; //force the trim to fail
386 if( udot > Tolerance ) {
387 if(checkLimits(udotf,dth,0,1) == false) {
388 cout << " Sorry, udot doesn't appear to be trimmable" << endl;
389 cout << " Resetting throttles to zero" << endl;
390 fdmex->GetFCS()->SetThrottleCmd(-1,0);
392 k=Ncycles; //force the trim to fail
398 if(qdot > A_Tolerance) {
400 if(checkLimits(qdotf,fdmex->GetFCS()->GetPitchTrimCmd(),-1,1) == false) {
401 cout << " Sorry, qdot doesn't appear to be trimmable" << endl;
403 k=Ncycles; //force the trim to fail
411 } while((axis_count < 3) && (k < Ncycles));
412 if(axis_count >= 3) {
414 cout << endl << " Trim successful" << endl;
418 cout << endl << " Trim failed" << endl;
425 //YOU WERE WARNED, BUT YOU DID IT ANYWAY.