JSBSim Flight Dynamics Model 1.0 (23 February 2013)
An Open Source Flight Dynamics and Control Software Library in C++

FGTrimAxis.cpp

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 }