1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Module: FGPropagate.cpp
6 Purpose: Integrate the EOM to determine instantaneous position
9 ------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
11 This program is free software; you can redistribute it and/or modify it under
12 the terms of the GNU Lesser General Public License as published by the Free Software
13 Foundation; either version 2 of the License, or (at your option) any later
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
21 You should have received a copy of the GNU Lesser General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 Place - Suite 330, Boston, MA 02111-1307, USA.
25 Further information about the GNU Lesser General Public License can also be found on
26 the world wide web at http://www.gnu.org.
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 This class encapsulates the integration of rates and accelerations to get the
31 current position of the aircraft.
34 --------------------------------------------------------------------------------
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 COMMENTS, REFERENCES, and NOTES
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 [1] Cooke, Zyda, Pratt, and McGhee, "NPSNET: Flight Simulation Dynamic Modeling
41 Using Quaternions", Presence, Vol. 1, No. 4, pp. 404-420 Naval Postgraduate
43 [2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
45 [3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
46 NASA-Ames", NASA CR-2497, January 1975
47 [4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
48 Wiley & Sons, 1979 ISBN 0-471-03032-5
49 [5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
50 1982 ISBN 0-471-08936-2
51 [6] Erin Catto, "Iterative Dynamics with Temporal Coherence", February 22, 2005
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
62 #include "FGPropagate.h"
63 #include "FGGroundReactions.h"
64 #include "FGFDMExec.h"
65 #include "FGAircraft.h"
66 #include "FGMassBalance.h"
67 #include "FGInertial.h"
68 #include "input_output/FGPropertyManager.h"
74 static const char *IdSrc = "$Id: FGPropagate.cpp,v 1.65 2010/09/18 22:48:12 jberndt Exp $";
75 static const char *IdHdr = ID_PROPAGATE;
77 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
81 FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex)
85 gravType = gtStandard;
88 vQtrndot = FGQuaternion(0,0,0);
90 vInertialVelocity.InitMatrix();
92 integrator_rotational_rate = eAdamsBashforth2;
93 integrator_translational_rate = eTrapezoidal;
94 integrator_rotational_position = eAdamsBashforth2;
95 integrator_translational_position = eTrapezoidal;
97 VState.dqPQRdot.resize(4, FGColumnVector3(0.0,0.0,0.0));
98 VState.dqUVWidot.resize(4, FGColumnVector3(0.0,0.0,0.0));
99 VState.dqInertialVelocity.resize(4, FGColumnVector3(0.0,0.0,0.0));
100 VState.dqQtrndot.resize(4, FGQuaternion(0.0,0.0,0.0));
106 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108 FGPropagate::~FGPropagate(void)
113 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115 bool FGPropagate::InitModel(void)
117 if (!FGModel::InitModel()) return false;
119 // For initialization ONLY:
120 SeaLevelRadius = LocalTerrainRadius = Inertial->GetRefRadius();
122 VState.vLocation.SetRadius( LocalTerrainRadius + 4.0 );
123 VState.vLocation.SetEllipse(Inertial->GetSemimajor(), Inertial->GetSemiminor());
124 vOmegaEarth = FGColumnVector3( 0.0, 0.0, Inertial->omega() ); // Earth rotation vector
126 vPQRdot.InitMatrix();
127 vQtrndot = FGQuaternion(0,0,0);
128 vUVWdot.InitMatrix();
129 vInertialVelocity.InitMatrix();
131 VState.dqPQRdot.resize(4, FGColumnVector3(0.0,0.0,0.0));
132 VState.dqUVWidot.resize(4, FGColumnVector3(0.0,0.0,0.0));
133 VState.dqInertialVelocity.resize(4, FGColumnVector3(0.0,0.0,0.0));
134 VState.dqQtrndot.resize(4, FGColumnVector3(0.0,0.0,0.0));
136 integrator_rotational_rate = eAdamsBashforth2;
137 integrator_translational_rate = eTrapezoidal;
138 integrator_rotational_position = eAdamsBashforth2;
139 integrator_translational_position = eTrapezoidal;
144 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
148 SetSeaLevelRadius(FGIC->GetSeaLevelRadiusFtIC());
149 SetTerrainElevation(FGIC->GetTerrainElevationFtIC());
151 // Initialize the State Vector elements and the transformation matrices
153 // Set the position lat/lon/radius
154 VState.vLocation.SetPosition( FGIC->GetLongitudeRadIC(),
155 FGIC->GetLatitudeRadIC(),
156 FGIC->GetAltitudeASLFtIC() + FGIC->GetSeaLevelRadiusFtIC() );
158 VState.vLocation.SetEarthPositionAngle(Inertial->GetEarthPositionAngle());
160 Ti2ec = GetTi2ec(); // ECI to ECEF transform
161 Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
163 VState.vInertialPosition = Tec2i * VState.vLocation;
165 UpdateLocationMatrices();
167 // Set the orientation from the euler angles (is normalized within the
168 // constructor). The Euler angles represent the orientation of the body
169 // frame relative to the local frame.
170 VState.qAttitudeLocal = FGQuaternion( FGIC->GetPhiRadIC(),
171 FGIC->GetThetaRadIC(),
172 FGIC->GetPsiRadIC() );
174 VState.qAttitudeECI = Ti2l.GetQuaternion()*VState.qAttitudeLocal;
175 UpdateBodyMatrices();
177 // Set the velocities in the instantaneus body frame
178 VState.vUVW = FGColumnVector3( FGIC->GetUBodyFpsIC(),
179 FGIC->GetVBodyFpsIC(),
180 FGIC->GetWBodyFpsIC() );
182 // Compute the local frame ECEF velocity
183 vVel = Tb2l * VState.vUVW;
185 // Recompute the LocalTerrainRadius.
186 RecomputeLocalTerrainRadius();
188 VehicleRadius = GetRadius();
189 double radInv = 1.0/VehicleRadius;
191 // Refer to Stevens and Lewis, 1.5-14a, pg. 49.
192 // This is the rotation rate of the "Local" frame, expressed in the local frame.
194 FGColumnVector3 vOmegaLocal = FGColumnVector3(
196 -radInv*vVel(eNorth),
197 -radInv*vVel(eEast)*VState.vLocation.GetTanLatitude() );
199 // Set the angular velocities of the body frame relative to the ECEF frame,
200 // expressed in the body frame. Effectively, this is:
201 // w_b/e = w_b/l + w_l/e
202 VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
204 FGIC->GetRRadpsIC() ) + Tl2b*vOmegaLocal;
206 VState.vPQRi = VState.vPQR + Ti2b * vOmegaEarth;
208 // Make an initial run and set past values
209 InitializeDerivatives();
212 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
214 Purpose: Called on a schedule to perform EOM integration
215 Notes: [JB] Run in standalone mode, SeaLevelRadius will be reference radius.
216 In FGFS, SeaLevelRadius is stuffed from FGJSBSim in JSBSim.cxx each pass.
218 At the top of this Run() function, see several "shortcuts" (or, aliases) being
219 set up for use later, rather than using the longer class->function() notation.
221 This propagation is done using the current state values
222 and current derivatives. Based on these values we compute an approximation to the
223 state values for (now + dt).
225 In the code below, variables named beginning with a small "v" refer to a
226 a column vector, variables named beginning with a "T" refer to a transformation
227 matrix. ECEF refers to Earth Centered Earth Fixed. ECI refers to Earth Centered
232 bool FGPropagate::Run(void)
234 if (FGModel::Run()) return true; // Fast return if we have nothing to do ...
235 if (FDMExec->Holding()) return false;
237 double dt = FDMExec->GetDeltaT()*rate; // The 'stepsize'
241 // Calculate state derivatives
242 CalculatePQRdot(); // Angular rate derivative
243 CalculateUVWdot(); // Translational rate derivative
244 ResolveFrictionForces(dt); // Update rate derivatives with friction forces
245 CalculateQuatdot(); // Angular orientation derivative
246 CalculateUVW(); // Translational position derivative (velocities are integrated in the inertial frame)
248 // Propagate rotational / translational velocity, angular /translational position, respectively.
249 Integrate(VState.vPQRi, vPQRdot, VState.dqPQRdot, dt, integrator_rotational_rate);
250 Integrate(VState.vInertialVelocity, vUVWidot, VState.dqUVWidot, dt, integrator_translational_rate);
251 Integrate(VState.qAttitudeECI, vQtrndot, VState.dqQtrndot, dt, integrator_rotational_position);
252 Integrate(VState.vInertialPosition, VState.vInertialVelocity, VState.dqInertialVelocity, dt, integrator_translational_position);
254 // CAUTION : the order of the operations below is very important to get transformation
255 // matrices that are consistent with the new state of the vehicle
257 // 1. Update the Earth position angle (EPA)
258 VState.vLocation.SetEarthPositionAngle(Inertial->GetEarthPositionAngle());
260 // 2. Update the Ti2ec and Tec2i transforms from the updated EPA
261 Ti2ec = GetTi2ec(); // ECI to ECEF transform
262 Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
264 // 3. Update the location from the updated Ti2ec and inertial position
265 VState.vLocation = Ti2ec*VState.vInertialPosition;
267 // 4. Update the other "Location-based" transformation matrices from the updated
269 UpdateLocationMatrices();
271 // 5. Normalize the ECI Attitude quaternion
272 VState.qAttitudeECI.Normalize();
274 // 6. Update the "Orientation-based" transformation matrices from the updated
275 // orientation quaternion and vLocation vector.
276 UpdateBodyMatrices();
278 // Set auxililary state variables
279 RecomputeLocalTerrainRadius();
281 VehicleRadius = GetRadius(); // Calculate current aircraft radius from center of planet
283 VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth;
285 VState.qAttitudeLocal = Tl2b.GetQuaternion();
287 // Compute vehicle velocity wrt ECEF frame, expressed in Local horizontal frame.
288 vVel = Tb2l * VState.vUVW;
296 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
297 // Compute body frame rotational accelerations based on the current body moments
299 // vPQRdot is the derivative of the absolute angular velocity of the vehicle
300 // (body rate with respect to the inertial frame), expressed in the body frame,
301 // where the derivative is taken in the body frame.
302 // J is the inertia matrix
303 // Jinv is the inverse inertia matrix
304 // vMoments is the moment vector in the body frame
305 // VState.vPQRi is the total inertial angular velocity of the vehicle
306 // expressed in the body frame.
307 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
308 // Second edition (2004), eqn 1.5-16e (page 50)
310 void FGPropagate::CalculatePQRdot(void)
312 const FGColumnVector3& vMoments = Aircraft->GetMoments(); // current moments
313 const FGMatrix33& J = MassBalance->GetJ(); // inertia matrix
314 const FGMatrix33& Jinv = MassBalance->GetJinv(); // inertia matrix inverse
316 // Compute body frame rotational accelerations based on the current body
317 // moments and the total inertial angular velocity expressed in the body
320 vPQRdot = Jinv*(vMoments - VState.vPQRi*(J*VState.vPQRi));
323 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324 // Compute the quaternion orientation derivative
326 // vQtrndot is the quaternion derivative.
327 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
328 // Second edition (2004), eqn 1.5-16b (page 50)
330 void FGPropagate::CalculateQuatdot(void)
332 // Compute quaternion orientation derivative on current body rates
333 vQtrndot = VState.qAttitudeECI.GetQDot( VState.vPQRi);
336 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
337 // This set of calculations results in the body and inertial frame accelerations
339 // Compute body and inertial frames accelerations based on the current body
340 // forces including centripetal and coriolis accelerations for the former.
341 // vOmegaEarth is the Earth angular rate - expressed in the inertial frame -
342 // so it has to be transformed to the body frame. More completely,
343 // vOmegaEarth is the rate of the ECEF frame relative to the Inertial
344 // frame (ECI), expressed in the Inertial frame.
345 // vForces is the total force on the vehicle in the body frame.
346 // VState.vPQR is the vehicle body rate relative to the ECEF frame, expressed
347 // in the body frame.
348 // VState.vUVW is the vehicle velocity relative to the ECEF frame, expressed
349 // in the body frame.
350 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
351 // Second edition (2004), eqns 1.5-13 (pg 48) and 1.5-16d (page 50)
353 void FGPropagate::CalculateUVWdot(void)
355 double mass = MassBalance->GetMass(); // mass
356 const FGColumnVector3& vForces = Aircraft->GetForces(); // current forces
358 vUVWdot = vForces/mass - (VState.vPQR + 2.0*(Ti2b *vOmegaEarth)) * VState.vUVW;
360 // Include Centripetal acceleration.
361 vUVWdot -= Ti2b * (vOmegaEarth*(vOmegaEarth*VState.vInertialPosition));
363 // Include Gravitation accel
366 vGravAccel = Tl2b * FGColumnVector3( 0.0, 0.0, Inertial->GetGAccel(VehicleRadius) );
369 vGravAccel = Tec2b * Inertial->GetGravityJ2(VState.vLocation);
373 vUVWdot += vGravAccel;
374 vUVWidot = Tb2i * (vForces/mass + vGravAccel);
377 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
378 // Transform the velocity vector of the body relative to the origin (Earth
379 // center) to be expressed in the inertial frame, and add the vehicle velocity
380 // contribution due to the rotation of the planet.
381 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
382 // Second edition (2004), eqn 1.5-16c (page 50)
384 void FGPropagate::CalculateInertialVelocity(void)
386 VState.vInertialVelocity = Tb2i * VState.vUVW + (vOmegaEarth * VState.vInertialPosition);
389 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390 // Transform the velocity vector of the inertial frame to be expressed in the
391 // body frame relative to the origin (Earth center), and substract the vehicle
392 // velocity contribution due to the rotation of the planet.
394 void FGPropagate::CalculateUVW(void)
396 VState.vUVW = Ti2b * (VState.vInertialVelocity - (vOmegaEarth * VState.vInertialPosition));
399 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
401 void FGPropagate::Integrate( FGColumnVector3& Integrand,
402 FGColumnVector3& Val,
403 deque <FGColumnVector3>& ValDot,
405 eIntegrateType integration_type)
407 ValDot.push_front(Val);
410 switch(integration_type) {
411 case eRectEuler: Integrand += dt*ValDot[0];
413 case eTrapezoidal: Integrand += 0.5*dt*(ValDot[0] + ValDot[1]);
415 case eAdamsBashforth2: Integrand += dt*(1.5*ValDot[0] - 0.5*ValDot[1]);
417 case eAdamsBashforth3: Integrand += (1/12.0)*dt*(23.0*ValDot[0] - 16.0*ValDot[1] + 5.0*ValDot[2]);
419 case eAdamsBashforth4: Integrand += (1/24.0)*dt*(55.0*ValDot[0] - 59.0*ValDot[1] + 37.0*ValDot[2] - 9.0*ValDot[3]);
421 case eNone: // do nothing, freeze translational rate
426 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428 void FGPropagate::Integrate( FGQuaternion& Integrand,
430 deque <FGQuaternion>& ValDot,
432 eIntegrateType integration_type)
434 ValDot.push_front(Val);
437 switch(integration_type) {
438 case eRectEuler: Integrand += dt*ValDot[0];
440 case eTrapezoidal: Integrand += 0.5*dt*(ValDot[0] + ValDot[1]);
442 case eAdamsBashforth2: Integrand += dt*(1.5*ValDot[0] - 0.5*ValDot[1]);
444 case eAdamsBashforth3: Integrand += (1/12.0)*dt*(23.0*ValDot[0] - 16.0*ValDot[1] + 5.0*ValDot[2]);
446 case eAdamsBashforth4: Integrand += (1/24.0)*dt*(55.0*ValDot[0] - 59.0*ValDot[1] + 37.0*ValDot[2] - 9.0*ValDot[3]);
448 case eNone: // do nothing, freeze rotational rate
453 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454 // Resolves the contact forces just before integrating the EOM.
455 // This routine is using Lagrange multipliers and the projected Gauss-Seidel
457 // Reference: See Erin Catto, "Iterative Dynamics with Temporal Coherence",
459 // In JSBSim there is only one rigid body (the aircraft) and there can be
460 // multiple points of contact between the aircraft and the ground. As a
461 // consequence our matrix J*M^-1*J^T is not sparse and the algorithm described
462 // in Catto's paper has been adapted accordingly.
463 // The friction forces are resolved in the body frame relative to the origin
466 void FGPropagate::ResolveFrictionForces(double dt)
468 const double invMass = 1.0 / MassBalance->GetMass();
469 const FGMatrix33& Jinv = MassBalance->GetJinv();
470 vector <FGColumnVector3> JacF, JacM;
471 FGColumnVector3 vdot, wdot;
472 FGColumnVector3 Fc, Mc;
475 // Compiles data from the ground reactions to build up the jacobian matrix
476 for (MultiplierIterator it=MultiplierIterator(GroundReactions); *it; ++it, n++) {
477 JacF.push_back((*it)->ForceJacobian);
478 JacM.push_back((*it)->MomentJacobian);
481 // If no gears are in contact with the ground then return
484 vector<double> a(n*n); // Will contain J*M^-1*J^T
485 vector<double> eta(n);
486 vector<double> lambda(n);
487 vector<double> lambdaMin(n);
488 vector<double> lambdaMax(n);
490 // Initializes the Lagrange multipliers
492 for (MultiplierIterator it=MultiplierIterator(GroundReactions); *it; ++it, i++) {
493 lambda[i] = (*it)->value;
494 lambdaMax[i] = (*it)->Max;
495 lambdaMin[i] = (*it)->Min;
502 // Instruct the algorithm to zero out the relative movement between the
503 // aircraft and the ground.
504 vdot += (VState.vUVW - Tec2b * LocalTerrainVelocity) / dt;
505 wdot += VState.vPQR / dt;
508 // Assemble the linear system of equations
509 for (i=0; i < n; i++) {
510 for (int j=0; j < i; j++)
511 a[i*n+j] = a[j*n+i]; // Takes advantage of the symmetry of J^T*M^-1*J
512 for (int j=i; j < n; j++)
513 a[i*n+j] = DotProduct(JacF[i],invMass*JacF[j])+DotProduct(JacM[i],Jinv*JacM[j]);
516 // Prepare the linear system for the Gauss-Seidel algorithm :
517 // divide every line of 'a' and eta by a[i,i]. This is in order to save
518 // a division computation at each iteration of Gauss-Seidel.
519 for (i=0; i < n; i++) {
520 double d = 1.0 / a[i*n+i];
522 eta[i] = -(DotProduct(JacF[i],vdot)+DotProduct(JacM[i],wdot))*d;
523 for (int j=0; j < n; j++)
527 // Resolve the Lagrange multipliers with the projected Gauss-Seidel method
528 for (int iter=0; iter < 50; iter++) {
531 for (i=0; i < n; i++) {
532 double lambda0 = lambda[i];
533 double dlambda = eta[i];
535 for (int j=0; j < n; j++)
536 dlambda -= a[i*n+j]*lambda[j];
538 lambda[i] = Constrain(lambdaMin[i], lambda0+dlambda, lambdaMax[i]);
539 dlambda = lambda[i] - lambda0;
541 norm += fabs(dlambda);
544 if (norm < 1E-5) break;
547 // Calculate the total friction forces and moments
552 for (i=0; i< n; i++) {
553 Fc += lambda[i]*JacF[i];
554 Mc += lambda[i]*JacM[i];
557 vUVWdot += invMass * Fc;
558 vUVWidot += invMass * Tb2i * Fc;
559 vPQRdot += Jinv * Mc;
561 // Save the value of the Lagrange multipliers to accelerate the convergence
562 // of the Gauss-Seidel algorithm at next iteration.
564 for (MultiplierIterator it=MultiplierIterator(GroundReactions); *it; ++it)
565 (*it)->value = lambda[i++];
567 GroundReactions->UpdateForcesAndMoments();
570 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572 void FGPropagate::UpdateLocationMatrices(void)
574 Tl2ec = GetTl2ec(); // local to ECEF transform
575 Tec2l = Tl2ec.Transposed(); // ECEF to local frame transform
577 Tl2i = Ti2l.Transposed();
580 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
582 void FGPropagate::UpdateBodyMatrices(void)
584 Ti2b = GetTi2b(); // ECI to body frame transform
585 Tb2i = Ti2b.Transposed(); // body to ECI frame transform
586 Tl2b = Ti2b*Tl2i; // local to body frame transform
587 Tb2l = Tl2b.Transposed(); // body to local frame transform
588 Tec2b = Tl2b * Tec2l; // ECEF to body frame transform
589 Tb2ec = Tec2b.Transposed(); // body to ECEF frame tranform
592 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
594 void FGPropagate::SetInertialOrientation(FGQuaternion Qi) {
595 VState.qAttitudeECI = Qi;
596 VState.qAttitudeECI.Normalize();
597 UpdateBodyMatrices();
598 VState.qAttitudeLocal = Tl2b.GetQuaternion();
601 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
603 void FGPropagate::SetInertialVelocity(FGColumnVector3 Vi) {
604 VState.vInertialVelocity = Vi;
606 vVel = GetTb2l() * VState.vUVW;
609 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611 void FGPropagate::SetInertialRates(FGColumnVector3 vRates) {
612 VState.vPQRi = Ti2b * vRates;
613 VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth;
616 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618 void FGPropagate::InitializeDerivatives(void)
620 // Make an initial run and set past values
621 CalculatePQRdot(); // Angular rate derivative
622 CalculateUVWdot(); // Translational rate derivative
623 ResolveFrictionForces(0.); // Update rate derivatives with friction forces
624 CalculateQuatdot(); // Angular orientation derivative
625 CalculateInertialVelocity(); // Translational position derivative
627 // Initialize past values deques
628 VState.dqPQRdot.clear();
629 VState.dqUVWidot.clear();
630 VState.dqInertialVelocity.clear();
631 VState.dqQtrndot.clear();
632 for (int i=0; i<4; i++) {
633 VState.dqPQRdot.push_front(vPQRdot);
634 VState.dqUVWidot.push_front(vUVWdot);
635 VState.dqInertialVelocity.push_front(VState.vInertialVelocity);
636 VState.dqQtrndot.push_front(vQtrndot);
640 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
642 void FGPropagate::RecomputeLocalTerrainRadius(void)
644 FGLocation contactloc;
646 double t = FDMExec->GetSimTime();
648 // Get the LocalTerrain radius.
649 FDMExec->GetGroundCallback()->GetAGLevel(t, VState.vLocation, contactloc, dv,
650 LocalTerrainVelocity);
651 LocalTerrainRadius = contactloc.GetRadius();
654 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
656 void FGPropagate::SetTerrainElevation(double terrainElev)
658 LocalTerrainRadius = terrainElev + SeaLevelRadius;
659 FDMExec->GetGroundCallback()->SetTerrainGeoCentRadius(LocalTerrainRadius);
662 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
664 double FGPropagate::GetTerrainElevation(void) const
666 return FDMExec->GetGroundCallback()->GetTerrainGeoCentRadius()-SeaLevelRadius;
669 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 //Todo: when should this be called - when should the new EPA be used to
671 // calculate the transformation matrix, so that the matrix is not a step
672 // ahead of the sim and the associated calculations?
673 const FGMatrix33& FGPropagate::GetTi2ec(void)
675 return VState.vLocation.GetTi2ec();
678 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
680 const FGMatrix33& FGPropagate::GetTec2i(void)
682 return VState.vLocation.GetTec2i();
685 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687 void FGPropagate::SetAltitudeASL(double altASL)
689 VState.vLocation.SetRadius( altASL + SeaLevelRadius );
692 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
694 double FGPropagate::GetLocalTerrainRadius(void) const
696 return LocalTerrainRadius;
699 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
701 double FGPropagate::GetDistanceAGL(void) const
703 return VState.vLocation.GetRadius() - LocalTerrainRadius;
706 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
708 void FGPropagate::SetDistanceAGL(double tt)
710 VState.vLocation.SetRadius( tt + LocalTerrainRadius );
713 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
715 void FGPropagate::bind(void)
717 typedef double (FGPropagate::*PMF)(int) const;
719 PropertyManager->Tie("velocities/h-dot-fps", this, &FGPropagate::Gethdot);
721 PropertyManager->Tie("velocities/v-north-fps", this, eNorth, (PMF)&FGPropagate::GetVel);
722 PropertyManager->Tie("velocities/v-east-fps", this, eEast, (PMF)&FGPropagate::GetVel);
723 PropertyManager->Tie("velocities/v-down-fps", this, eDown, (PMF)&FGPropagate::GetVel);
725 PropertyManager->Tie("velocities/u-fps", this, eU, (PMF)&FGPropagate::GetUVW);
726 PropertyManager->Tie("velocities/v-fps", this, eV, (PMF)&FGPropagate::GetUVW);
727 PropertyManager->Tie("velocities/w-fps", this, eW, (PMF)&FGPropagate::GetUVW);
729 PropertyManager->Tie("velocities/p-rad_sec", this, eP, (PMF)&FGPropagate::GetPQR);
730 PropertyManager->Tie("velocities/q-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQR);
731 PropertyManager->Tie("velocities/r-rad_sec", this, eR, (PMF)&FGPropagate::GetPQR);
733 PropertyManager->Tie("velocities/pi-rad_sec", this, eP, (PMF)&FGPropagate::GetPQRi);
734 PropertyManager->Tie("velocities/qi-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQRi);
735 PropertyManager->Tie("velocities/ri-rad_sec", this, eR, (PMF)&FGPropagate::GetPQRi);
737 PropertyManager->Tie("velocities/eci-velocity-mag-fps", this, &FGPropagate::GetInertialVelocityMagnitude);
739 PropertyManager->Tie("accelerations/pdot-rad_sec2", this, eP, (PMF)&FGPropagate::GetPQRdot);
740 PropertyManager->Tie("accelerations/qdot-rad_sec2", this, eQ, (PMF)&FGPropagate::GetPQRdot);
741 PropertyManager->Tie("accelerations/rdot-rad_sec2", this, eR, (PMF)&FGPropagate::GetPQRdot);
743 PropertyManager->Tie("accelerations/udot-ft_sec2", this, eU, (PMF)&FGPropagate::GetUVWdot);
744 PropertyManager->Tie("accelerations/vdot-ft_sec2", this, eV, (PMF)&FGPropagate::GetUVWdot);
745 PropertyManager->Tie("accelerations/wdot-ft_sec2", this, eW, (PMF)&FGPropagate::GetUVWdot);
747 PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::GetAltitudeASL, &FGPropagate::SetAltitudeASL, true);
748 PropertyManager->Tie("position/h-sl-meters", this, &FGPropagate::GetAltitudeASLmeters, &FGPropagate::SetAltitudeASLmeters, true);
749 PropertyManager->Tie("position/lat-gc-rad", this, &FGPropagate::GetLatitude, &FGPropagate::SetLatitude);
750 PropertyManager->Tie("position/long-gc-rad", this, &FGPropagate::GetLongitude, &FGPropagate::SetLongitude);
751 PropertyManager->Tie("position/lat-gc-deg", this, &FGPropagate::GetLatitudeDeg, &FGPropagate::SetLatitudeDeg);
752 PropertyManager->Tie("position/long-gc-deg", this, &FGPropagate::GetLongitudeDeg, &FGPropagate::SetLongitudeDeg);
753 PropertyManager->Tie("position/lat-geod-rad", this, &FGPropagate::GetGeodLatitudeRad);
754 PropertyManager->Tie("position/lat-geod-deg", this, &FGPropagate::GetGeodLatitudeDeg);
755 PropertyManager->Tie("position/geod-alt-ft", this, &FGPropagate::GetGeodeticAltitude);
756 PropertyManager->Tie("position/h-agl-ft", this, &FGPropagate::GetDistanceAGL, &FGPropagate::SetDistanceAGL);
757 PropertyManager->Tie("position/radius-to-vehicle-ft", this, &FGPropagate::GetRadius);
758 PropertyManager->Tie("position/terrain-elevation-asl-ft", this,
759 &FGPropagate::GetTerrainElevation,
760 &FGPropagate::SetTerrainElevation, false);
762 PropertyManager->Tie("metrics/terrain-radius", this, &FGPropagate::GetLocalTerrainRadius);
764 PropertyManager->Tie("attitude/phi-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
765 PropertyManager->Tie("attitude/theta-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
766 PropertyManager->Tie("attitude/psi-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
768 PropertyManager->Tie("attitude/roll-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
769 PropertyManager->Tie("attitude/pitch-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
770 PropertyManager->Tie("attitude/heading-true-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
772 PropertyManager->Tie("simulation/integrator/rate/rotational", (int*)&integrator_rotational_rate);
773 PropertyManager->Tie("simulation/integrator/rate/translational", (int*)&integrator_translational_rate);
774 PropertyManager->Tie("simulation/integrator/position/rotational", (int*)&integrator_rotational_position);
775 PropertyManager->Tie("simulation/integrator/position/translational", (int*)&integrator_translational_position);
776 PropertyManager->Tie("simulation/gravity-model", &gravType);
779 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780 // The bitmasked value choices are as follows:
781 // unset: In this case (the default) JSBSim would only print
782 // out the normally expected messages, essentially echoing
783 // the config files as they are read. If the environment
784 // variable is not set, debug_lvl is set to 1 internally
785 // 0: This requests JSBSim not to output any messages
787 // 1: This value explicity requests the normal JSBSim
789 // 2: This value asks for a message to be printed out when
790 // a class is instantiated
791 // 4: When this value is set, a message is displayed when a
792 // FGModel object executes its Run() method
793 // 8: When this value is set, various runtime state variables
794 // are printed out periodically
795 // 16: When set various parameters are sanity checked and
796 // a message is printed out when they go out of bounds
798 void FGPropagate::Debug(int from)
800 if (debug_lvl <= 0) return;
802 if (debug_lvl & 1) { // Standard console startup message output
803 if (from == 0) { // Constructor
807 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
808 if (from == 0) cout << "Instantiated: FGPropagate" << endl;
809 if (from == 1) cout << "Destroyed: FGPropagate" << endl;
811 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
813 if (debug_lvl & 8 && from == 2) { // Runtime state variables
814 cout << endl << fgblue << highint << left
815 << " Propagation Report (English units: ft, degrees) at simulation time " << FDMExec->GetSimTime() << " seconds"
818 cout << highint << " Earth Position Angle (deg): " << setw(8) << setprecision(3) << reset
819 << Inertial->GetEarthPositionAngleDeg() << endl;
821 cout << highint << " Body velocity (ft/sec): " << setw(8) << setprecision(3) << reset << VState.vUVW << endl;
822 cout << highint << " Local velocity (ft/sec): " << setw(8) << setprecision(3) << reset << vVel << endl;
823 cout << highint << " Inertial velocity (ft/sec): " << setw(8) << setprecision(3) << reset << VState.vInertialVelocity << endl;
824 cout << highint << " Inertial Position (ft): " << setw(10) << setprecision(3) << reset << VState.vInertialPosition << endl;
825 cout << highint << " Latitude (deg): " << setw(8) << setprecision(3) << reset << VState.vLocation.GetLatitudeDeg() << endl;
826 cout << highint << " Longitude (deg): " << setw(8) << setprecision(3) << reset << VState.vLocation.GetLongitudeDeg() << endl;
827 cout << highint << " Altitude ASL (ft): " << setw(8) << setprecision(3) << reset << GetAltitudeASL() << endl;
828 cout << highint << " Acceleration (NED, ft/sec^2): " << setw(8) << setprecision(3) << reset << Tb2l*GetUVWdot() << endl;
830 cout << highint << " Matrix ECEF to Body (Orientation of Body with respect to ECEF): "
831 << reset << endl << Tec2b.Dump("\t", " ") << endl;
832 cout << highint << " Associated Euler angles (deg): " << setw(8)
833 << setprecision(3) << reset << (Tec2b.GetQuaternion().GetEuler()*radtodeg)
836 cout << highint << " Matrix Body to ECEF (Orientation of ECEF with respect to Body):"
837 << reset << endl << Tb2ec.Dump("\t", " ") << endl;
838 cout << highint << " Associated Euler angles (deg): " << setw(8)
839 << setprecision(3) << reset << (Tb2ec.GetQuaternion().GetEuler()*radtodeg)
842 cout << highint << " Matrix Local to Body (Orientation of Body with respect to Local):"
843 << reset << endl << Tl2b.Dump("\t", " ") << endl;
844 cout << highint << " Associated Euler angles (deg): " << setw(8)
845 << setprecision(3) << reset << (Tl2b.GetQuaternion().GetEuler()*radtodeg)
848 cout << highint << " Matrix Body to Local (Orientation of Local with respect to Body):"
849 << reset << endl << Tb2l.Dump("\t", " ") << endl;
850 cout << highint << " Associated Euler angles (deg): " << setw(8)
851 << setprecision(3) << reset << (Tb2l.GetQuaternion().GetEuler()*radtodeg)
854 cout << highint << " Matrix Local to ECEF (Orientation of ECEF with respect to Local):"
855 << reset << endl << Tl2ec.Dump("\t", " ") << endl;
856 cout << highint << " Associated Euler angles (deg): " << setw(8)
857 << setprecision(3) << reset << (Tl2ec.GetQuaternion().GetEuler()*radtodeg)
860 cout << highint << " Matrix ECEF to Local (Orientation of Local with respect to ECEF):"
861 << reset << endl << Tec2l.Dump("\t", " ") << endl;
862 cout << highint << " Associated Euler angles (deg): " << setw(8)
863 << setprecision(3) << reset << (Tec2l.GetQuaternion().GetEuler()*radtodeg)
866 cout << highint << " Matrix ECEF to Inertial (Orientation of Inertial with respect to ECEF):"
867 << reset << endl << Tec2i.Dump("\t", " ") << endl;
868 cout << highint << " Associated Euler angles (deg): " << setw(8)
869 << setprecision(3) << reset << (Tec2i.GetQuaternion().GetEuler()*radtodeg)
872 cout << highint << " Matrix Inertial to ECEF (Orientation of ECEF with respect to Inertial):"
873 << reset << endl << Ti2ec.Dump("\t", " ") << endl;
874 cout << highint << " Associated Euler angles (deg): " << setw(8)
875 << setprecision(3) << reset << (Ti2ec.GetQuaternion().GetEuler()*radtodeg)
878 cout << highint << " Matrix Inertial to Body (Orientation of Body with respect to Inertial):"
879 << reset << endl << Ti2b.Dump("\t", " ") << endl;
880 cout << highint << " Associated Euler angles (deg): " << setw(8)
881 << setprecision(3) << reset << (Ti2b.GetQuaternion().GetEuler()*radtodeg)
884 cout << highint << " Matrix Body to Inertial (Orientation of Inertial with respect to Body):"
885 << reset << endl << Tb2i.Dump("\t", " ") << endl;
886 cout << highint << " Associated Euler angles (deg): " << setw(8)
887 << setprecision(3) << reset << (Tb2i.GetQuaternion().GetEuler()*radtodeg)
890 cout << highint << " Matrix Inertial to Local (Orientation of Local with respect to Inertial):"
891 << reset << endl << Ti2l.Dump("\t", " ") << endl;
892 cout << highint << " Associated Euler angles (deg): " << setw(8)
893 << setprecision(3) << reset << (Ti2l.GetQuaternion().GetEuler()*radtodeg)
896 cout << highint << " Matrix Local to Inertial (Orientation of Inertial with respect to Local):"
897 << reset << endl << Tl2i.Dump("\t", " ") << endl;
898 cout << highint << " Associated Euler angles (deg): " << setw(8)
899 << setprecision(3) << reset << (Tl2i.GetQuaternion().GetEuler()*radtodeg)
902 cout << setprecision(6); // reset the output stream
904 if (debug_lvl & 16) { // Sanity checking
905 if (from == 2) { // State sanity checking
906 if (fabs(VState.vPQR.Magnitude()) > 1000.0) {
907 cerr << endl << "Vehicle rotation rate is excessive (>1000 rad/sec): " << VState.vPQR.Magnitude() << endl;
910 if (fabs(VState.vUVW.Magnitude()) > 1.0e10) {
911 cerr << endl << "Vehicle velocity is excessive (>1e10 ft/sec): " << VState.vUVW.Magnitude() << endl;
914 if (fabs(GetDistanceAGL()) > 1e10) {
915 cerr << endl << "Vehicle altitude is excessive (>1e10 ft): " << GetDistanceAGL() << endl;
920 if (debug_lvl & 64) {
921 if (from == 0) { // Constructor
922 cout << IdSrc << endl;
923 cout << IdHdr << endl;