1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Module: FGAccelerations.cpp
6 Purpose: Calculates derivatives of rotational and translational rates, and
7 of the attitude quaternion.
10 ------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
12 This program is free software; you can redistribute it and/or modify it under
13 the terms of the GNU Lesser General Public License as published by the Free Software
14 Foundation; either version 2 of the License, or (at your option) any later
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
22 You should have received a copy of the GNU Lesser General Public License along with
23 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
24 Place - Suite 330, Boston, MA 02111-1307, USA.
26 Further information about the GNU Lesser General Public License can also be found on
27 the world wide web at http://www.gnu.org.
29 FUNCTIONAL DESCRIPTION
30 --------------------------------------------------------------------------------
31 This class encapsulates the calculation of the derivatives of the state vectors
32 UVW and PQR - the translational and rotational rates relative to the planet
33 fixed frame. The derivatives relative to the inertial frame are also calculated
34 as a side effect. Also, the derivative of the attitude quaterion is also calculated.
37 --------------------------------------------------------------------------------
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 COMMENTS, REFERENCES, and NOTES
42 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 [1] Stevens and Lewis, "Aircraft Control and Simulation", Second edition (2004)
45 [2] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
46 NASA-Ames", NASA CR-2497, January 1975
47 [3] Erin Catto, "Iterative Dynamics with Temporal Coherence", February 22, 2005
49 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
53 #include "FGAccelerations.h"
54 #include "FGFDMExec.h"
55 #include "input_output/FGPropertyManager.h"
61 static const char *IdSrc = "$Id: FGAccelerations.cpp,v 1.8 2011/08/30 20:49:04 bcoconni Exp $";
62 static const char *IdHdr = ID_ACCELERATIONS;
64 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
68 FGAccelerations::FGAccelerations(FGFDMExec* fdmex)
72 Name = "FGAccelerations";
75 vPQRidot.InitMatrix();
76 vUVWidot.InitMatrix();
77 vGravAccel.InitMatrix();
78 vBodyAccel.InitMatrix();
79 vQtrndot = FGQuaternion(0,0,0);
85 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 FGAccelerations::~FGAccelerations(void)
92 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 bool FGAccelerations::InitModel(void)
96 vPQRidot.InitMatrix();
97 vUVWidot.InitMatrix();
98 vGravAccel.InitMatrix();
99 vBodyAccel.InitMatrix();
100 vQtrndot = FGQuaternion(0,0,0);
105 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107 Purpose: Called on a schedule to calculate derivatives.
110 bool FGAccelerations::Run(bool Holding)
112 if (FGModel::Run(Holding)) return true; // Fast return if we have nothing to do ...
113 if (Holding) return false;
117 CalculatePQRdot(); // Angular rate derivative
118 CalculateUVWdot(); // Translational rate derivative
119 CalculateQuatdot(); // Angular orientation derivative
121 ResolveFrictionForces(in.DeltaT * rate); // Update rate derivatives with friction forces
129 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
130 // Compute body frame rotational accelerations based on the current body moments
132 // vPQRdot is the derivative of the absolute angular velocity of the vehicle
133 // (body rate with respect to the inertial frame), expressed in the body frame,
134 // where the derivative is taken in the body frame.
135 // J is the inertia matrix
136 // Jinv is the inverse inertia matrix
137 // vMoments is the moment vector in the body frame
138 // in.vPQRi is the total inertial angular velocity of the vehicle
139 // expressed in the body frame.
140 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
141 // Second edition (2004), eqn 1.5-16e (page 50)
143 void FGAccelerations::CalculatePQRdot(void)
145 // Compute body frame rotational accelerations based on the current body
146 // moments and the total inertial angular velocity expressed in the body
149 vPQRidot = in.Jinv * (in.Moment - in.vPQRi * (in.J * in.vPQRi));
150 vPQRdot = vPQRidot - in.vPQRi * (in.Ti2b * in.vOmegaPlanet);
153 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 // Compute the quaternion orientation derivative
156 // vQtrndot is the quaternion derivative.
157 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
158 // Second edition (2004), eqn 1.5-16b (page 50)
160 void FGAccelerations::CalculateQuatdot(void)
162 // Compute quaternion orientation derivative on current body rates
163 vQtrndot = in.qAttitudeECI.GetQDot(in.vPQRi);
166 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167 // This set of calculations results in the body and inertial frame accelerations
169 // Compute body and inertial frames accelerations based on the current body
170 // forces including centripetal and coriolis accelerations for the former.
171 // in.vOmegaPlanet is the Earth angular rate - expressed in the inertial frame -
172 // so it has to be transformed to the body frame. More completely,
173 // in.vOmegaPlanet is the rate of the ECEF frame relative to the Inertial
174 // frame (ECI), expressed in the Inertial frame.
175 // in.Force is the total force on the vehicle in the body frame.
176 // in.vPQR is the vehicle body rate relative to the ECEF frame, expressed
177 // in the body frame.
178 // in.vUVW is the vehicle velocity relative to the ECEF frame, expressed
179 // in the body frame.
180 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
181 // Second edition (2004), eqns 1.5-13 (pg 48) and 1.5-16d (page 50)
183 void FGAccelerations::CalculateUVWdot(void)
185 vBodyAccel = in.Force / in.Mass;
187 vUVWdot = vBodyAccel - (in.vPQR + 2.0 * (in.Ti2b * in.vOmegaPlanet)) * in.vUVW;
189 // Include Centripetal acceleration.
190 vUVWdot -= in.Ti2b * (in.vOmegaPlanet * (in.vOmegaPlanet * in.vInertialPosition));
192 // Include Gravitation accel
195 vGravAccel = in.Tl2b * FGColumnVector3( 0.0, 0.0, in.GAccel );
198 vGravAccel = in.Tec2b * in.J2Grav;
202 vUVWdot += vGravAccel;
203 vUVWidot = in.Tb2i * (vBodyAccel + vGravAccel);
206 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207 // Resolves the contact forces just before integrating the EOM.
208 // This routine is using Lagrange multipliers and the projected Gauss-Seidel
210 // Reference: See Erin Catto, "Iterative Dynamics with Temporal Coherence",
212 // In JSBSim there is only one rigid body (the aircraft) and there can be
213 // multiple points of contact between the aircraft and the ground. As a
214 // consequence our matrix J*M^-1*J^T is not sparse and the algorithm described
215 // in Catto's paper has been adapted accordingly.
216 // The friction forces are resolved in the body frame relative to the origin
219 void FGAccelerations::ResolveFrictionForces(double dt)
221 const double invMass = 1.0 / in.Mass;
222 const FGMatrix33& Jinv = in.Jinv;
223 FGColumnVector3 vdot, wdot;
224 vector<LagrangeMultiplier*>& multipliers = *in.MultipliersList;
225 int n = multipliers.size();
227 vFrictionForces.InitMatrix();
228 vFrictionMoments.InitMatrix();
230 // If no gears are in contact with the ground then return
233 vector<double> a(n*n); // Will contain J*M^-1*J^T
234 vector<double> rhs(n);
236 // Assemble the linear system of equations
237 for (int i=0; i < n; i++) {
238 FGColumnVector3 v1 = invMass * multipliers[i]->ForceJacobian;
239 FGColumnVector3 v2 = Jinv * multipliers[i]->MomentJacobian; // Should be J^-T but J is symmetric and so is J^-1
241 for (int j=0; j < i; j++)
242 a[i*n+j] = a[j*n+i]; // Takes advantage of the symmetry of J^T*M^-1*J
243 for (int j=i; j < n; j++)
244 a[i*n+j] = DotProduct(v1, multipliers[j]->ForceJacobian)
245 + DotProduct(v2, multipliers[j]->MomentJacobian);
248 // Assemble the RHS member
252 if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
253 vdot += (in.vUVW - in.Tec2b * in.TerrainVelocity) / dt;
257 if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
258 wdot += (in.vPQR - in.Tec2b * in.TerrainAngularVel) / dt;
260 // Prepare the linear system for the Gauss-Seidel algorithm :
261 // 1. Compute the right hand side member 'rhs'
262 // 2. Divide every line of 'a' and 'rhs' by a[i,i]. This is in order to save
263 // a division computation at each iteration of Gauss-Seidel.
264 for (int i=0; i < n; i++) {
265 double d = 1.0 / a[i*n+i];
267 rhs[i] = -(DotProduct(multipliers[i]->ForceJacobian, vdot)
268 +DotProduct(multipliers[i]->MomentJacobian, wdot))*d;
269 for (int j=0; j < n; j++)
273 // Resolve the Lagrange multipliers with the projected Gauss-Seidel method
274 for (int iter=0; iter < 50; iter++) {
277 for (int i=0; i < n; i++) {
278 double lambda0 = multipliers[i]->value;
279 double dlambda = rhs[i];
281 for (int j=0; j < n; j++)
282 dlambda -= a[i*n+j]*multipliers[j]->value;
284 multipliers[i]->value = Constrain(multipliers[i]->Min, lambda0+dlambda, multipliers[i]->Max);
285 dlambda = multipliers[i]->value - lambda0;
287 norm += fabs(dlambda);
290 if (norm < 1E-5) break;
293 // Calculate the total friction forces and moments
295 for (int i=0; i< n; i++) {
296 double lambda = multipliers[i]->value;
297 vFrictionForces += lambda * multipliers[i]->ForceJacobian;
298 vFrictionMoments += lambda * multipliers[i]->MomentJacobian;
301 FGColumnVector3 accel = invMass * vFrictionForces;
302 FGColumnVector3 omegadot = Jinv * vFrictionMoments;
306 vUVWidot += in.Tb2i * accel;
308 vPQRidot += omegadot;
310 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
312 void FGAccelerations::InitializeDerivatives(void)
314 // Make an initial run and set past values
315 CalculatePQRdot(); // Angular rate derivative
316 CalculateUVWdot(); // Translational rate derivative
317 CalculateQuatdot(); // Angular orientation derivative
318 ResolveFrictionForces(0.); // Update rate derivatives with friction forces
321 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323 void FGAccelerations::bind(void)
325 typedef double (FGAccelerations::*PMF)(int) const;
327 PropertyManager->Tie("accelerations/pdot-rad_sec2", this, eP, (PMF)&FGAccelerations::GetPQRdot);
328 PropertyManager->Tie("accelerations/qdot-rad_sec2", this, eQ, (PMF)&FGAccelerations::GetPQRdot);
329 PropertyManager->Tie("accelerations/rdot-rad_sec2", this, eR, (PMF)&FGAccelerations::GetPQRdot);
331 PropertyManager->Tie("accelerations/udot-ft_sec2", this, eU, (PMF)&FGAccelerations::GetUVWdot);
332 PropertyManager->Tie("accelerations/vdot-ft_sec2", this, eV, (PMF)&FGAccelerations::GetUVWdot);
333 PropertyManager->Tie("accelerations/wdot-ft_sec2", this, eW, (PMF)&FGAccelerations::GetUVWdot);
335 PropertyManager->Tie("simulation/gravity-model", &gravType);
337 PropertyManager->Tie("forces/fbx-total-lbs", this, eX, (PMF)&FGAccelerations::GetForces);
338 PropertyManager->Tie("forces/fby-total-lbs", this, eY, (PMF)&FGAccelerations::GetForces);
339 PropertyManager->Tie("forces/fbz-total-lbs", this, eZ, (PMF)&FGAccelerations::GetForces);
340 PropertyManager->Tie("moments/l-total-lbsft", this, eL, (PMF)&FGAccelerations::GetMoments);
341 PropertyManager->Tie("moments/m-total-lbsft", this, eM, (PMF)&FGAccelerations::GetMoments);
342 PropertyManager->Tie("moments/n-total-lbsft", this, eN, (PMF)&FGAccelerations::GetMoments);
344 PropertyManager->Tie("moments/l-gear-lbsft", this, eL, (PMF)&FGAccelerations::GetGroundMoments);
345 PropertyManager->Tie("moments/m-gear-lbsft", this, eM, (PMF)&FGAccelerations::GetGroundMoments);
346 PropertyManager->Tie("moments/n-gear-lbsft", this, eN, (PMF)&FGAccelerations::GetGroundMoments);
347 PropertyManager->Tie("forces/fbx-gear-lbs", this, eX, (PMF)&FGAccelerations::GetGroundForces);
348 PropertyManager->Tie("forces/fby-gear-lbs", this, eY, (PMF)&FGAccelerations::GetGroundForces);
349 PropertyManager->Tie("forces/fbz-gear-lbs", this, eZ, (PMF)&FGAccelerations::GetGroundForces);
352 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
353 // The bitmasked value choices are as follows:
354 // unset: In this case (the default) JSBSim would only print
355 // out the normally expected messages, essentially echoing
356 // the config files as they are read. If the environment
357 // variable is not set, debug_lvl is set to 1 internally
358 // 0: This requests JSBSim not to output any messages
360 // 1: This value explicity requests the normal JSBSim
362 // 2: This value asks for a message to be printed out when
363 // a class is instantiated
364 // 4: When this value is set, a message is displayed when a
365 // FGModel object executes its Run() method
366 // 8: When this value is set, various runtime state variables
367 // are printed out periodically
368 // 16: When set various parameters are sanity checked and
369 // a message is printed out when they go out of bounds
371 void FGAccelerations::Debug(int from)
373 if (debug_lvl <= 0) return;
375 if (debug_lvl & 1) { // Standard console startup message output
376 if (from == 0) { // Constructor
380 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
381 if (from == 0) cout << "Instantiated: FGAccelerations" << endl;
382 if (from == 1) cout << "Destroyed: FGAccelerations" << endl;
384 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
386 if (debug_lvl & 8 && from == 2) { // Runtime state variables
388 if (debug_lvl & 16) { // Sanity checking
390 if (debug_lvl & 64) {
391 if (from == 0) { // Constructor
392 cout << IdSrc << endl;
393 cout << IdHdr << endl;