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

FGAccelerations.cpp

00001 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00002 
00003  Module:       FGAccelerations.cpp
00004  Author:       Jon S. Berndt
00005  Date started: 07/12/11
00006  Purpose:      Calculates derivatives of rotational and translational rates, and
00007                of the attitude quaternion.
00008  Called by:    FGFDMExec
00009 
00010  ------------- Copyright (C) 2011  Jon S. Berndt (jon@jsbsim.org) -------------
00011 
00012  This program is free software; you can redistribute it and/or modify it under
00013  the terms of the GNU Lesser General Public License as published by the Free Software
00014  Foundation; either version 2 of the License, or (at your option) any later
00015  version.
00016 
00017  This program is distributed in the hope that it will be useful, but WITHOUT
00018  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00019  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
00020  details.
00021 
00022  You should have received a copy of the GNU Lesser General Public License along with
00023  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
00024  Place - Suite 330, Boston, MA  02111-1307, USA.
00025 
00026  Further information about the GNU Lesser General Public License can also be found on
00027  the world wide web at http://www.gnu.org.
00028 
00029 FUNCTIONAL DESCRIPTION
00030 --------------------------------------------------------------------------------
00031 This class encapsulates the calculation of the derivatives of the state vectors
00032 UVW and PQR - the translational and rotational rates relative to the planet
00033 fixed frame. The derivatives relative to the inertial frame are also calculated
00034 as a side effect. Also, the derivative of the attitude quaterion is also calculated.
00035 
00036 HISTORY
00037 --------------------------------------------------------------------------------
00038 07/12/11   JSB   Created
00039 
00040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00041 COMMENTS, REFERENCES,  and NOTES
00042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00043 [1] Stevens and Lewis, "Aircraft Control and Simulation", Second edition (2004)
00044     Wiley
00045 [2] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
00046     NASA-Ames", NASA CR-2497, January 1975
00047 [3] Erin Catto, "Iterative Dynamics with Temporal Coherence", February 22, 2005
00048 [4] Mark Harris and Robert Lyle, "Spacecraft Gravitational Torques",
00049     NASA SP-8024, May 1969
00050 
00051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00052 INCLUDES
00053 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
00054 
00055 #include "FGAccelerations.h"
00056 #include "FGFDMExec.h"
00057 #include "input_output/FGPropertyManager.h"
00058 
00059 using namespace std;
00060 
00061 namespace JSBSim {
00062 
00063 static const char *IdSrc = "$Id: FGAccelerations.cpp,v 1.14 2012/09/15 17:00:56 bcoconni Exp $";
00064 static const char *IdHdr = ID_ACCELERATIONS;
00065 
00066 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00067 CLASS IMPLEMENTATION
00068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
00069 
00070 FGAccelerations::FGAccelerations(FGFDMExec* fdmex)
00071   : FGModel(fdmex)
00072 {
00073   Debug(0);
00074   Name = "FGAccelerations";
00075   gravType = gtWGS84;
00076   gravTorque = false;
00077   HoldDown = 0;
00078 
00079   vPQRidot.InitMatrix();
00080   vUVWidot.InitMatrix();
00081   vGravAccel.InitMatrix();
00082   vBodyAccel.InitMatrix();
00083   vQtrndot = FGQuaternion(0,0,0);
00084 
00085   bind();
00086   Debug(0);
00087 }
00088 
00089 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00090 
00091 FGAccelerations::~FGAccelerations(void)
00092 {
00093   Debug(1);
00094 }
00095 
00096 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00097 
00098 bool FGAccelerations::InitModel(void)
00099 {
00100   vPQRidot.InitMatrix();
00101   vUVWidot.InitMatrix();
00102   vGravAccel.InitMatrix();
00103   vBodyAccel.InitMatrix();
00104   vQtrndot = FGQuaternion(0,0,0);
00105 
00106   return true;
00107 }
00108 
00109 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00110 /*
00111 Purpose: Called on a schedule to calculate derivatives.
00112 */
00113 
00114 bool FGAccelerations::Run(bool Holding)
00115 {
00116   if (FGModel::Run(Holding)) return true;  // Fast return if we have nothing to do ...
00117   if (Holding) return false;
00118 
00119   CalculatePQRdot();   // Angular rate derivative
00120   CalculateUVWdot();   // Translational rate derivative
00121   CalculateQuatdot();  // Angular orientation derivative
00122 
00123   ResolveFrictionForces(in.DeltaT * rate);  // Update rate derivatives with friction forces
00124 
00125   Debug(2);
00126   return false;
00127 }
00128 
00129 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00130 // Compute body frame rotational accelerations based on the current body moments
00131 //
00132 // vPQRdot is the derivative of the absolute angular velocity of the vehicle
00133 // (body rate with respect to the ECEF frame), expressed in the body frame,
00134 // where the derivative is taken in the body frame.
00135 // J is the inertia matrix
00136 // Jinv is the inverse inertia matrix
00137 // vMoments is the moment vector in the body frame
00138 // in.vPQRi is the total inertial angular velocity of the vehicle
00139 // expressed in the body frame.
00140 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
00141 //            Second edition (2004), eqn 1.5-16e (page 50)
00142 
00143 void FGAccelerations::CalculatePQRdot(void)
00144 {
00145   if (gravTorque) {
00146     // Compute the gravitational torque
00147     // Reference: See Harris and Lyle "Spacecraft Gravitational Torques",
00148     //            NASA SP-8024 (1969) eqn (2) (page 7)
00149     FGColumnVector3 R = in.Ti2b * in.vInertialPosition;
00150     double invRadius = 1.0 / R.Magnitude();
00151     R *= invRadius;
00152     in.Moment += (3.0 * in.GAccel * invRadius) * (R * (in.J * R));
00153   }
00154 
00155   // Compute body frame rotational accelerations based on the current body
00156   // moments and the total inertial angular velocity expressed in the body
00157   // frame.
00158   if (HoldDown) {
00159     // The rotational acceleration in ECI is calculated so that the rotational
00160     // acceleration is zero in the body frame.
00161     vPQRdot.InitMatrix();
00162     vPQRidot = in.vPQRi * (in.Ti2b * in.vOmegaPlanet);
00163   }
00164   else {
00165     vPQRidot = in.Jinv * (in.Moment - in.vPQRi * (in.J * in.vPQRi));
00166     vPQRdot = vPQRidot - in.vPQRi * (in.Ti2b * in.vOmegaPlanet);
00167   }
00168 }
00169 
00170 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00171 // Compute the quaternion orientation derivative
00172 //
00173 // vQtrndot is the quaternion derivative.
00174 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
00175 //            Second edition (2004), eqn 1.5-16b (page 50)
00176 
00177 void FGAccelerations::CalculateQuatdot(void)
00178 {
00179   // Compute quaternion orientation derivative on current body rates
00180   vQtrndot = in.qAttitudeECI.GetQDot(in.vPQRi);
00181 }
00182 
00183 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00184 // This set of calculations results in the body and inertial frame accelerations
00185 // being computed.
00186 // Compute body and inertial frames accelerations based on the current body
00187 // forces including centripetal and Coriolis accelerations for the former.
00188 // in.vOmegaPlanet is the Earth angular rate - expressed in the inertial frame -
00189 //   so it has to be transformed to the body frame. More completely,
00190 //   in.vOmegaPlanet is the rate of the ECEF frame relative to the Inertial
00191 //   frame (ECI), expressed in the Inertial frame.
00192 // in.Force is the total force on the vehicle in the body frame.
00193 // in.vPQR is the vehicle body rate relative to the ECEF frame, expressed
00194 //   in the body frame.
00195 // in.vUVW is the vehicle velocity relative to the ECEF frame, expressed
00196 //   in the body frame.
00197 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
00198 //            Second edition (2004), eqns 1.5-13 (pg 48) and 1.5-16d (page 50)
00199 
00200 void FGAccelerations::CalculateUVWdot(void)
00201 {
00202   if (HoldDown)
00203     vBodyAccel.InitMatrix();
00204   else
00205     vBodyAccel = in.Force / in.Mass;
00206 
00207   vUVWdot = vBodyAccel - (in.vPQR + 2.0 * (in.Ti2b * in.vOmegaPlanet)) * in.vUVW;
00208 
00209   // Include Centripetal acceleration.
00210   vUVWdot -= in.Ti2b * (in.vOmegaPlanet * (in.vOmegaPlanet * in.vInertialPosition));
00211 
00212   // Include Gravitation accel
00213   switch (gravType) {
00214     case gtStandard:
00215       {
00216         double radius = in.vInertialPosition.Magnitude();
00217         vGravAccel = -(in.GAccel / radius) * in.vInertialPosition;
00218       }
00219       break;
00220     case gtWGS84:
00221       vGravAccel = in.Tec2i * in.J2Grav;
00222       break;
00223   }
00224 
00225   if (HoldDown) {
00226     // The acceleration in ECI is calculated so that the acceleration is zero
00227     // in the body frame.
00228     vUVWidot = -1.0 * (in.Tb2i * vUVWdot);
00229     vUVWdot.InitMatrix();
00230   }
00231   else {
00232     vUVWdot += in.Ti2b * vGravAccel;
00233     vUVWidot = in.Tb2i * vBodyAccel + vGravAccel;
00234   }
00235 }
00236 
00237 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00238 // Resolves the contact forces just before integrating the EOM.
00239 // This routine is using Lagrange multipliers and the projected Gauss-Seidel
00240 // (PGS) method.
00241 // Reference: See Erin Catto, "Iterative Dynamics with Temporal Coherence",
00242 //            February 22, 2005
00243 // In JSBSim there is only one rigid body (the aircraft) and there can be
00244 // multiple points of contact between the aircraft and the ground. As a
00245 // consequence our matrix Jac*M^-1*Jac^T is not sparse and the algorithm
00246 // described in Catto's paper has been adapted accordingly.
00247 // The friction forces are resolved in the body frame relative to the origin
00248 // (Earth center).
00249 
00250 void FGAccelerations::ResolveFrictionForces(double dt)
00251 {
00252   const double invMass = 1.0 / in.Mass;
00253   const FGMatrix33& Jinv = in.Jinv;
00254   FGColumnVector3 vdot, wdot;
00255   vector<LagrangeMultiplier*>& multipliers = *in.MultipliersList;
00256   int n = multipliers.size();
00257 
00258   vFrictionForces.InitMatrix();
00259   vFrictionMoments.InitMatrix();
00260 
00261   // If no gears are in contact with the ground then return
00262   if (!n) return;
00263 
00264   vector<double> a(n*n); // Will contain Jac*M^-1*Jac^T
00265   vector<double> rhs(n);
00266 
00267   // Assemble the linear system of equations
00268   for (int i=0; i < n; i++) {
00269     FGColumnVector3 v1 = invMass * multipliers[i]->ForceJacobian;
00270     FGColumnVector3 v2 = Jinv * multipliers[i]->MomentJacobian; // Should be J^-T but J is symmetric and so is J^-1
00271 
00272     for (int j=0; j < i; j++)
00273       a[i*n+j] = a[j*n+i]; // Takes advantage of the symmetry of Jac^T*M^-1*Jac
00274     for (int j=i; j < n; j++)
00275       a[i*n+j] = DotProduct(v1, multipliers[j]->ForceJacobian)
00276                + DotProduct(v2, multipliers[j]->MomentJacobian);
00277   }
00278 
00279   // Assemble the RHS member
00280 
00281   // Translation
00282   vdot = vUVWdot;
00283   if (dt > 0.) // Zeroes out the relative movement between the aircraft and the ground
00284     vdot += (in.vUVW - in.Tec2b * in.TerrainVelocity) / dt;
00285 
00286   // Rotation
00287   wdot = vPQRdot;
00288   if (dt > 0.) // Zeroes out the relative movement between the aircraft and the ground
00289     wdot += (in.vPQR - in.Tec2b * in.TerrainAngularVel) / dt;
00290 
00291   // Prepare the linear system for the Gauss-Seidel algorithm :
00292   // 1. Compute the right hand side member 'rhs'
00293   // 2. Divide every line of 'a' and 'rhs' by a[i,i]. This is in order to save
00294   //    a division computation at each iteration of Gauss-Seidel.
00295   for (int i=0; i < n; i++) {
00296     double d = 1.0 / a[i*n+i];
00297 
00298     rhs[i] = -(DotProduct(multipliers[i]->ForceJacobian, vdot)
00299               +DotProduct(multipliers[i]->MomentJacobian, wdot))*d;
00300     for (int j=0; j < n; j++)
00301       a[i*n+j] *= d;
00302   }
00303 
00304   // Resolve the Lagrange multipliers with the projected Gauss-Seidel method
00305   for (int iter=0; iter < 50; iter++) {
00306     double norm = 0.;
00307 
00308     for (int i=0; i < n; i++) {
00309       double lambda0 = multipliers[i]->value;
00310       double dlambda = rhs[i];
00311 
00312       for (int j=0; j < n; j++)
00313         dlambda -= a[i*n+j]*multipliers[j]->value;
00314 
00315       multipliers[i]->value = Constrain(multipliers[i]->Min, lambda0+dlambda, multipliers[i]->Max);
00316       dlambda = multipliers[i]->value - lambda0;
00317 
00318       norm += fabs(dlambda);
00319     }
00320 
00321     if (norm < 1E-5) break;
00322   }
00323 
00324   // Calculate the total friction forces and moments
00325 
00326   for (int i=0; i< n; i++) {
00327     double lambda = multipliers[i]->value;
00328     vFrictionForces += lambda * multipliers[i]->ForceJacobian;
00329     vFrictionMoments += lambda * multipliers[i]->MomentJacobian;
00330   }
00331 
00332   FGColumnVector3 accel = invMass * vFrictionForces;
00333   FGColumnVector3 omegadot = Jinv * vFrictionMoments;
00334 
00335   vBodyAccel += accel;
00336   vUVWdot += accel;
00337   vUVWidot += in.Tb2i * accel;
00338   vPQRdot += omegadot;
00339   vPQRidot += omegadot;
00340 }
00341 
00342 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00343 
00344 void FGAccelerations::InitializeDerivatives(void)
00345 {
00346   // Make an initial run and set past values
00347   CalculatePQRdot();           // Angular rate derivative
00348   CalculateUVWdot();           // Translational rate derivative
00349   CalculateQuatdot();          // Angular orientation derivative
00350   ResolveFrictionForces(0.);   // Update rate derivatives with friction forces
00351 }
00352 
00353 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00354 
00355 void FGAccelerations::bind(void)
00356 {
00357   typedef double (FGAccelerations::*PMF)(int) const;
00358 
00359   PropertyManager->Tie("accelerations/pdot-rad_sec2", this, eP, (PMF)&FGAccelerations::GetPQRdot);
00360   PropertyManager->Tie("accelerations/qdot-rad_sec2", this, eQ, (PMF)&FGAccelerations::GetPQRdot);
00361   PropertyManager->Tie("accelerations/rdot-rad_sec2", this, eR, (PMF)&FGAccelerations::GetPQRdot);
00362 
00363   PropertyManager->Tie("accelerations/udot-ft_sec2", this, eU, (PMF)&FGAccelerations::GetUVWdot);
00364   PropertyManager->Tie("accelerations/vdot-ft_sec2", this, eV, (PMF)&FGAccelerations::GetUVWdot);
00365   PropertyManager->Tie("accelerations/wdot-ft_sec2", this, eW, (PMF)&FGAccelerations::GetUVWdot);
00366 
00367   PropertyManager->Tie("simulation/gravity-model", &gravType);
00368   PropertyManager->Tie("simulation/gravitational-torque", &gravTorque);
00369 
00370   PropertyManager->Tie("forces/fbx-total-lbs", this, eX, (PMF)&FGAccelerations::GetForces);
00371   PropertyManager->Tie("forces/fby-total-lbs", this, eY, (PMF)&FGAccelerations::GetForces);
00372   PropertyManager->Tie("forces/fbz-total-lbs", this, eZ, (PMF)&FGAccelerations::GetForces);
00373   PropertyManager->Tie("moments/l-total-lbsft", this, eL, (PMF)&FGAccelerations::GetMoments);
00374   PropertyManager->Tie("moments/m-total-lbsft", this, eM, (PMF)&FGAccelerations::GetMoments);
00375   PropertyManager->Tie("moments/n-total-lbsft", this, eN, (PMF)&FGAccelerations::GetMoments);
00376 
00377   PropertyManager->Tie("moments/l-gear-lbsft", this, eL, (PMF)&FGAccelerations::GetGroundMoments);
00378   PropertyManager->Tie("moments/m-gear-lbsft", this, eM, (PMF)&FGAccelerations::GetGroundMoments);
00379   PropertyManager->Tie("moments/n-gear-lbsft", this, eN, (PMF)&FGAccelerations::GetGroundMoments);
00380   PropertyManager->Tie("forces/fbx-gear-lbs", this, eX, (PMF)&FGAccelerations::GetGroundForces);
00381   PropertyManager->Tie("forces/fby-gear-lbs", this, eY, (PMF)&FGAccelerations::GetGroundForces);
00382   PropertyManager->Tie("forces/fbz-gear-lbs", this, eZ, (PMF)&FGAccelerations::GetGroundForces);
00383 
00384   PropertyManager->Tie("forces/hold-down", this, &FGAccelerations::GetHoldDown, &FGAccelerations::SetHoldDown);
00385 }
00386 
00387 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00388 //    The bitmasked value choices are as follows:
00389 //    unset: In this case (the default) JSBSim would only print
00390 //       out the normally expected messages, essentially echoing
00391 //       the config files as they are read. If the environment
00392 //       variable is not set, debug_lvl is set to 1 internally
00393 //    0: This requests JSBSim not to output any messages
00394 //       whatsoever.
00395 //    1: This value explicity requests the normal JSBSim
00396 //       startup messages
00397 //    2: This value asks for a message to be printed out when
00398 //       a class is instantiated
00399 //    4: When this value is set, a message is displayed when a
00400 //       FGModel object executes its Run() method
00401 //    8: When this value is set, various runtime state variables
00402 //       are printed out periodically
00403 //    16: When set various parameters are sanity checked and
00404 //       a message is printed out when they go out of bounds
00405 
00406 void FGAccelerations::Debug(int from)
00407 {
00408   if (debug_lvl <= 0) return;
00409 
00410   if (debug_lvl & 1) { // Standard console startup message output
00411     if (from == 0) { // Constructor
00412 
00413     }
00414   }
00415   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
00416     if (from == 0) cout << "Instantiated: FGAccelerations" << endl;
00417     if (from == 1) cout << "Destroyed:    FGAccelerations" << endl;
00418   }
00419   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
00420   }
00421   if (debug_lvl & 8 && from == 2) { // Runtime state variables
00422   }
00423   if (debug_lvl & 16) { // Sanity checking
00424   }
00425   if (debug_lvl & 64) {
00426     if (from == 0) { // Constructor
00427       cout << IdSrc << endl;
00428       cout << IdHdr << endl;
00429     }
00430   }
00431 }
00432 }