![]() |
JSBSim Flight Dynamics Model 1.0 (23 February 2013)
An Open Source Flight Dynamics and Control Software Library in C++
|
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 }