![]() |
JSBSim Flight Dynamics Model 1.0 (23 February 2013)
An Open Source Flight Dynamics and Control Software Library in C++
|
00001 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00002 00003 Header: FGTrimAxis.cpp 00004 Author: Tony Peden 00005 Date started: 7/3/00 00006 00007 --------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) --------- 00008 00009 This program is free software; you can redistribute it and/or modify it under 00010 the terms of the GNU Lesser General Public License as published by the Free Software 00011 Foundation; either version 2 of the License, or (at your option) any later 00012 version. 00013 00014 This program is distributed in the hope that it will be useful, but WITHOUT 00015 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00016 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 00017 details. 00018 00019 You should have received a copy of the GNU Lesser General Public License along with 00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple 00021 Place - Suite 330, Boston, MA 02111-1307, USA. 00022 00023 Further information about the GNU Lesser General Public License can also be found on 00024 the world wide web at http://www.gnu.org. 00025 00026 00027 HISTORY 00028 -------------------------------------------------------------------------------- 00029 7/3/00 TP Created 00030 00031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00032 INCLUDES 00033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 00034 00035 #ifdef _MSC_VER 00036 # pragma warning (disable : 4786) 00037 #endif 00038 00039 #include <string> 00040 #include <cstdlib> 00041 #include <iomanip> 00042 #include "FGFDMExec.h" 00043 #include "models/FGAtmosphere.h" 00044 #include "FGInitialCondition.h" 00045 #include "FGTrimAxis.h" 00046 #include "models/FGPropulsion.h" 00047 #include "models/FGAerodynamics.h" 00048 #include "models/FGFCS.h" 00049 #include "models/propulsion/FGEngine.h" 00050 #include "models/FGAuxiliary.h" 00051 #include "models/FGGroundReactions.h" 00052 #include "models/FGPropagate.h" 00053 #include "models/FGAccelerations.h" 00054 00055 using namespace std; 00056 00057 namespace JSBSim { 00058 00059 static const char *IdSrc = "$Id: FGTrimAxis.cpp,v 1.14 2012/09/05 21:49:19 bcoconni Exp $"; 00060 static const char *IdHdr = ID_TRIMAXIS; 00061 00062 /*****************************************************************************/ 00063 00064 FGTrimAxis::FGTrimAxis(FGFDMExec* fdex, FGInitialCondition* ic, State st, 00065 Control ctrl) { 00066 00067 fdmex=fdex; 00068 fgic=ic; 00069 state=st; 00070 control=ctrl; 00071 max_iterations=10; 00072 control_value=0; 00073 its_to_stable_value=0; 00074 total_iterations=0; 00075 total_stability_iterations=0; 00076 state_convert=1.0; 00077 control_convert=1.0; 00078 state_value=0; 00079 state_target=0; 00080 switch(state) { 00081 case tUdot: tolerance = DEFAULT_TOLERANCE; break; 00082 case tVdot: tolerance = DEFAULT_TOLERANCE; break; 00083 case tWdot: tolerance = DEFAULT_TOLERANCE; break; 00084 case tQdot: tolerance = DEFAULT_TOLERANCE / 10; break; 00085 case tPdot: tolerance = DEFAULT_TOLERANCE / 10; break; 00086 case tRdot: tolerance = DEFAULT_TOLERANCE / 10; break; 00087 case tHmgt: tolerance = 0.01; break; 00088 case tNlf: state_target=1.0; tolerance = 1E-5; break; 00089 case tAll: break; 00090 } 00091 00092 solver_eps=tolerance; 00093 switch(control) { 00094 case tThrottle: 00095 control_min=0; 00096 control_max=1; 00097 control_value=0.5; 00098 break; 00099 case tBeta: 00100 control_min=-30*degtorad; 00101 control_max=30*degtorad; 00102 control_convert=radtodeg; 00103 break; 00104 case tAlpha: 00105 control_min=fdmex->GetAerodynamics()->GetAlphaCLMin(); 00106 control_max=fdmex->GetAerodynamics()->GetAlphaCLMax(); 00107 if(control_max <= control_min) { 00108 control_max=20*degtorad; 00109 control_min=-5*degtorad; 00110 } 00111 control_value= (control_min+control_max)/2; 00112 control_convert=radtodeg; 00113 solver_eps=tolerance/100; 00114 break; 00115 case tPitchTrim: 00116 case tElevator: 00117 case tRollTrim: 00118 case tAileron: 00119 case tYawTrim: 00120 case tRudder: 00121 control_min=-1; 00122 control_max=1; 00123 state_convert=radtodeg; 00124 solver_eps=tolerance/100; 00125 break; 00126 case tAltAGL: 00127 control_min=0; 00128 control_max=30; 00129 control_value=fdmex->GetPropagate()->GetDistanceAGL(); 00130 solver_eps=tolerance/100; 00131 break; 00132 case tTheta: 00133 control_min=fdmex->GetPropagate()->GetEuler(eTht) - 5*degtorad; 00134 control_max=fdmex->GetPropagate()->GetEuler(eTht) + 5*degtorad; 00135 state_convert=radtodeg; 00136 break; 00137 case tPhi: 00138 control_min=fdmex->GetPropagate()->GetEuler(ePhi) - 30*degtorad; 00139 control_max=fdmex->GetPropagate()->GetEuler(ePhi) + 30*degtorad; 00140 state_convert=radtodeg; 00141 control_convert=radtodeg; 00142 break; 00143 case tGamma: 00144 solver_eps=tolerance/100; 00145 control_min=-80*degtorad; 00146 control_max=80*degtorad; 00147 control_convert=radtodeg; 00148 break; 00149 case tHeading: 00150 control_min=fdmex->GetPropagate()->GetEuler(ePsi) - 30*degtorad; 00151 control_max=fdmex->GetPropagate()->GetEuler(ePsi) + 30*degtorad; 00152 state_convert=radtodeg; 00153 break; 00154 } 00155 00156 00157 Debug(0); 00158 } 00159 00160 /*****************************************************************************/ 00161 00162 FGTrimAxis::~FGTrimAxis(void) 00163 { 00164 Debug(1); 00165 } 00166 00167 /*****************************************************************************/ 00168 00169 void FGTrimAxis::getState(void) { 00170 switch(state) { 00171 case tUdot: state_value=fdmex->GetAccelerations()->GetUVWdot(1)-state_target; break; 00172 case tVdot: state_value=fdmex->GetAccelerations()->GetUVWdot(2)-state_target; break; 00173 case tWdot: state_value=fdmex->GetAccelerations()->GetUVWdot(3)-state_target; break; 00174 case tQdot: state_value=fdmex->GetAccelerations()->GetPQRdot(2)-state_target;break; 00175 case tPdot: state_value=fdmex->GetAccelerations()->GetPQRdot(1)-state_target; break; 00176 case tRdot: state_value=fdmex->GetAccelerations()->GetPQRdot(3)-state_target; break; 00177 case tHmgt: state_value=computeHmgt()-state_target; break; 00178 case tNlf: state_value=fdmex->GetAuxiliary()->GetNlf()-state_target; break; 00179 case tAll: break; 00180 } 00181 } 00182 00183 /*****************************************************************************/ 00184 00185 //States are not settable 00186 00187 void FGTrimAxis::getControl(void) { 00188 switch(control) { 00189 case tThrottle: control_value=fdmex->GetFCS()->GetThrottleCmd(0); break; 00190 case tBeta: control_value=fdmex->GetAuxiliary()->Getbeta(); break; 00191 case tAlpha: control_value=fdmex->GetAuxiliary()->Getalpha(); break; 00192 case tPitchTrim: control_value=fdmex->GetFCS() -> GetPitchTrimCmd(); break; 00193 case tElevator: control_value=fdmex->GetFCS() -> GetDeCmd(); break; 00194 case tRollTrim: 00195 case tAileron: control_value=fdmex->GetFCS() -> GetDaCmd(); break; 00196 case tYawTrim: 00197 case tRudder: control_value=fdmex->GetFCS() -> GetDrCmd(); break; 00198 case tAltAGL: control_value=fdmex->GetPropagate()->GetDistanceAGL();break; 00199 case tTheta: control_value=fdmex->GetPropagate()->GetEuler(eTht); break; 00200 case tPhi: control_value=fdmex->GetPropagate()->GetEuler(ePhi); break; 00201 case tGamma: control_value=fdmex->GetAuxiliary()->GetGamma();break; 00202 case tHeading: control_value=fdmex->GetPropagate()->GetEuler(ePsi); break; 00203 } 00204 } 00205 00206 /*****************************************************************************/ 00207 00208 double FGTrimAxis::computeHmgt(void) { 00209 double diff; 00210 00211 diff = fdmex->GetPropagate()->GetEuler(ePsi) - 00212 fdmex->GetAuxiliary()->GetGroundTrack(); 00213 00214 if( diff < -M_PI ) { 00215 return (diff + 2*M_PI); 00216 } else if( diff > M_PI ) { 00217 return (diff - 2*M_PI); 00218 } else { 00219 return diff; 00220 } 00221 00222 } 00223 00224 /*****************************************************************************/ 00225 00226 00227 void FGTrimAxis::setControl(void) { 00228 switch(control) { 00229 case tThrottle: setThrottlesPct(); break; 00230 case tBeta: fgic->SetBetaRadIC(control_value); break; 00231 case tAlpha: fgic->SetAlphaRadIC(control_value); break; 00232 case tPitchTrim: fdmex->GetFCS()->SetPitchTrimCmd(control_value); break; 00233 case tElevator: fdmex->GetFCS()->SetDeCmd(control_value); break; 00234 case tRollTrim: 00235 case tAileron: fdmex->GetFCS()->SetDaCmd(control_value); break; 00236 case tYawTrim: 00237 case tRudder: fdmex->GetFCS()->SetDrCmd(control_value); break; 00238 case tAltAGL: fgic->SetAltitudeAGLFtIC(control_value); break; 00239 case tTheta: fgic->SetThetaRadIC(control_value); break; 00240 case tPhi: fgic->SetPhiRadIC(control_value); break; 00241 case tGamma: fgic->SetFlightPathAngleRadIC(control_value); break; 00242 case tHeading: fgic->SetPsiRadIC(control_value); break; 00243 } 00244 } 00245 00246 00247 00248 00249 00250 /*****************************************************************************/ 00251 00252 // the aircraft center of rotation is no longer the cg once the gear 00253 // contact the ground so the altitude needs to be changed when pitch 00254 // and roll angle are adjusted. Instead of attempting to calculate the 00255 // new center of rotation, pick a gear unit as a reference and use its 00256 // location vector to calculate the new height change. i.e. new altitude = 00257 // earth z component of that vector (which is in body axes ) 00258 void FGTrimAxis::SetThetaOnGround(double ff) { 00259 int center,i,ref; 00260 00261 // favor an off-center unit so that the same one can be used for both 00262 // pitch and roll. An on-center unit is used (for pitch)if that's all 00263 // that's in contact with the ground. 00264 i=0; ref=-1; center=-1; 00265 while( (ref < 0) && (i < fdmex->GetGroundReactions()->GetNumGearUnits()) ) { 00266 if(fdmex->GetGroundReactions()->GetGearUnit(i)->GetWOW()) { 00267 if(fabs(fdmex->GetGroundReactions()->GetGearUnit(i)->GetBodyLocation(2)) > 0.01) 00268 ref=i; 00269 else 00270 center=i; 00271 } 00272 i++; 00273 } 00274 if((ref < 0) && (center >= 0)) { 00275 ref=center; 00276 } 00277 cout << "SetThetaOnGround ref gear: " << ref << endl; 00278 if(ref >= 0) { 00279 double sp = fdmex->GetPropagate()->GetSinEuler(ePhi); 00280 double cp = fdmex->GetPropagate()->GetCosEuler(ePhi); 00281 double lx = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(1); 00282 double ly = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(2); 00283 double lz = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(3); 00284 double hagl = -1*lx*sin(ff) + 00285 ly*sp*cos(ff) + 00286 lz*cp*cos(ff); 00287 00288 fgic->SetAltitudeAGLFtIC(hagl); 00289 cout << "SetThetaOnGround new alt: " << hagl << endl; 00290 } 00291 fgic->SetThetaRadIC(ff); 00292 cout << "SetThetaOnGround new theta: " << ff << endl; 00293 } 00294 00295 /*****************************************************************************/ 00296 00297 bool FGTrimAxis::initTheta(void) { 00298 int i,N; 00299 int iForward = 0; 00300 int iAft = 1; 00301 double zAft,zForward,zDiff,theta; 00302 double xAft,xForward,xDiff; 00303 bool level; 00304 double saveAlt; 00305 00306 saveAlt=fgic->GetAltitudeAGLFtIC(); 00307 fgic->SetAltitudeAGLFtIC(100); 00308 00309 00310 N=fdmex->GetGroundReactions()->GetNumGearUnits(); 00311 00312 //find the first wheel unit forward of the cg 00313 //the list is short so a simple linear search is fine 00314 for( i=0; i<N; i++ ) { 00315 if(fdmex->GetGroundReactions()->GetGearUnit(i)->GetBodyLocation(1) > 0 ) { 00316 iForward=i; 00317 break; 00318 } 00319 } 00320 //now find the first wheel unit aft of the cg 00321 for( i=0; i<N; i++ ) { 00322 if(fdmex->GetGroundReactions()->GetGearUnit(i)->GetBodyLocation(1) < 0 ) { 00323 iAft=i; 00324 break; 00325 } 00326 } 00327 00328 // now adjust theta till the wheels are the same distance from the ground 00329 xAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetBodyLocation(1); 00330 xForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetBodyLocation(1); 00331 xDiff = xForward - xAft; 00332 zAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetLocalGear(3); 00333 zForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetLocalGear(3); 00334 zDiff = zForward - zAft; 00335 level=false; 00336 theta=fgic->GetThetaDegIC(); 00337 while(!level && (i < 100)) { 00338 theta+=radtodeg*atan(zDiff/xDiff); 00339 fgic->SetThetaDegIC(theta); 00340 fdmex->Initialize(fgic); 00341 fdmex->Run(); 00342 zAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetLocalGear(3); 00343 zForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetLocalGear(3); 00344 zDiff = zForward - zAft; 00345 //cout << endl << theta << " " << zDiff << endl; 00346 //cout << "0: " << fdmex->GetGroundReactions()->GetGearUnit(0)->GetLocalGear() << endl; 00347 //cout << "1: " << fdmex->GetGroundReactions()->GetGearUnit(1)->GetLocalGear() << endl; 00348 if(fabs(zDiff ) < 0.1) 00349 level=true; 00350 i++; 00351 } 00352 //cout << i << endl; 00353 if (debug_lvl > 0) { 00354 cout << " Initial Theta: " << fdmex->GetPropagate()->GetEuler(eTht)*radtodeg << endl; 00355 cout << " Used gear unit " << iAft << " as aft and " << iForward << " as forward" << endl; 00356 } 00357 control_min=(theta+5)*degtorad; 00358 control_max=(theta-5)*degtorad; 00359 fgic->SetAltitudeAGLFtIC(saveAlt); 00360 if(i < 100) 00361 return true; 00362 else 00363 return false; 00364 } 00365 00366 /*****************************************************************************/ 00367 00368 void FGTrimAxis::SetPhiOnGround(double ff) { 00369 int i,ref; 00370 00371 i=0; ref=-1; 00372 //must have an off-center unit here 00373 while ( (ref < 0) && (i < fdmex->GetGroundReactions()->GetNumGearUnits()) ) { 00374 if ( (fdmex->GetGroundReactions()->GetGearUnit(i)->GetWOW()) && 00375 (fabs(fdmex->GetGroundReactions()->GetGearUnit(i)->GetBodyLocation(2)) > 0.01)) 00376 ref=i; 00377 i++; 00378 } 00379 if (ref >= 0) { 00380 double st = fdmex->GetPropagate()->GetSinEuler(eTht); 00381 double ct = fdmex->GetPropagate()->GetCosEuler(eTht); 00382 double lx = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(1); 00383 double ly = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(2); 00384 double lz = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(3); 00385 double hagl = -1*lx*st + 00386 ly*sin(ff)*ct + 00387 lz*cos(ff)*ct; 00388 00389 fgic->SetAltitudeAGLFtIC(hagl); 00390 } 00391 fgic->SetPhiRadIC(ff); 00392 00393 } 00394 00395 /*****************************************************************************/ 00396 00397 void FGTrimAxis::Run(void) { 00398 00399 double last_state_value; 00400 int i; 00401 setControl(); 00402 //cout << "FGTrimAxis::Run: " << control_value << endl; 00403 i=0; 00404 bool stable=false; 00405 while(!stable) { 00406 i++; 00407 last_state_value=state_value; 00408 fdmex->Initialize(fgic); 00409 fdmex->Run(); 00410 getState(); 00411 if(i > 1) { 00412 if((fabs(last_state_value - state_value) < tolerance) || (i >= 100) ) 00413 stable=true; 00414 } 00415 } 00416 00417 its_to_stable_value=i; 00418 total_stability_iterations+=its_to_stable_value; 00419 total_iterations++; 00420 } 00421 00422 /*****************************************************************************/ 00423 00424 void FGTrimAxis::setThrottlesPct(void) { 00425 double tMin,tMax; 00426 for(unsigned i=0;i<fdmex->GetPropulsion()->GetNumEngines();i++) { 00427 tMin=fdmex->GetPropulsion()->GetEngine(i)->GetThrottleMin(); 00428 tMax=fdmex->GetPropulsion()->GetEngine(i)->GetThrottleMax(); 00429 00430 // Both the main throttle setting in FGFCS and the copy of the position 00431 // in the Propulsion::Inputs structure need to be set at this time. 00432 fdmex->GetFCS()->SetThrottleCmd(i,tMin+control_value*(tMax-tMin)); 00433 fdmex->GetPropulsion()->in.ThrottlePos[i] = tMin +control_value*(tMax - tMin); 00434 00435 fdmex->Initialize(fgic); 00436 fdmex->Run(); //apply throttle change 00437 fdmex->GetPropulsion()->GetSteadyState(); 00438 } 00439 } 00440 00441 /*****************************************************************************/ 00442 00443 void FGTrimAxis::AxisReport(void) { 00444 // Save original cout format characteristics 00445 std::ios_base::fmtflags originalFormat = cout.flags(); 00446 std::streamsize originalPrecision = cout.precision(); 00447 std::streamsize originalWidth = cout.width(); 00448 cout << " " << setw(20) << GetControlName() << ": "; 00449 cout << setw(6) << setprecision(2) << GetControl()*control_convert << ' '; 00450 cout << setw(5) << GetStateName() << ": "; 00451 cout << setw(9) << setprecision(2) << scientific << GetState()+state_target; 00452 cout << " Tolerance: " << setw(3) << setprecision(0) << scientific << GetTolerance(); 00453 00454 if( fabs(GetState()+state_target) < fabs(GetTolerance()) ) 00455 cout << " Passed" << endl; 00456 else 00457 cout << " Failed" << endl; 00458 // Restore original cout format characteristics 00459 cout.flags(originalFormat); 00460 cout.precision(originalPrecision); 00461 cout.width(originalWidth); 00462 } 00463 00464 /*****************************************************************************/ 00465 00466 double FGTrimAxis::GetAvgStability( void ) { 00467 if(total_iterations > 0) { 00468 return double(total_stability_iterations)/double(total_iterations); 00469 } 00470 return 0; 00471 } 00472 00473 /*****************************************************************************/ 00474 // The bitmasked value choices are as follows: 00475 // unset: In this case (the default) JSBSim would only print 00476 // out the normally expected messages, essentially echoing 00477 // the config files as they are read. If the environment 00478 // variable is not set, debug_lvl is set to 1 internally 00479 // 0: This requests JSBSim not to output any messages 00480 // whatsoever. 00481 // 1: This value explicity requests the normal JSBSim 00482 // startup messages 00483 // 2: This value asks for a message to be printed out when 00484 // a class is instantiated 00485 // 4: When this value is set, a message is displayed when a 00486 // FGModel object executes its Run() method 00487 // 8: When this value is set, various runtime state variables 00488 // are printed out periodically 00489 // 16: When set various parameters are sanity checked and 00490 // a message is printed out when they go out of bounds 00491 00492 void FGTrimAxis::Debug(int from) 00493 { 00494 00495 if (debug_lvl <= 0) return; 00496 if (debug_lvl & 1 ) { // Standard console startup message output 00497 if (from == 0) { // Constructor 00498 00499 } 00500 } 00501 if (debug_lvl & 2 ) { // Instantiation/Destruction notification 00502 if (from == 0) cout << "Instantiated: FGTrimAxis" << endl; 00503 if (from == 1) cout << "Destroyed: FGTrimAxis" << endl; 00504 } 00505 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects 00506 } 00507 if (debug_lvl & 8 ) { // Runtime state variables 00508 } 00509 if (debug_lvl & 16) { // Sanity checking 00510 } 00511 if (debug_lvl & 64) { 00512 if (from == 0) { // Constructor 00513 cout << IdSrc << endl; 00514 cout << IdHdr << endl; 00515 } 00516 } 00517 } 00518 }