]> git.mxchange.org Git - flightgear.git/blobdiff - src/FDM/JSBSim/models/FGPropagate.cpp
Better fix for a compilation problem with MSVC 2012
[flightgear.git] / src / FDM / JSBSim / models / FGPropagate.cpp
index db2ff3f92589bf7c7424dd4325edf80eb57fa07e..3c77bca282af8e5df2c38680b08efdd9924d064a 100644 (file)
@@ -6,7 +6,7 @@
  Purpose:      Integrate the EOM to determine instantaneous position
  Called by:    FGFDMExec
 
- ------------- Copyright (C) 1999  Jon S. Berndt (jsb@hal-pc.org) -------------
+ ------------- Copyright (C) 1999  Jon S. Berndt (jon@jsbsim.org) -------------
 
  This program is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the Free Software
@@ -48,55 +48,63 @@ COMMENTS, REFERENCES,  and NOTES
     Wiley & Sons, 1979 ISBN 0-471-03032-5
 [5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
     1982 ISBN 0-471-08936-2
+[6] S. Buss, "Accurate and Efficient Simulation of Rigid Body Rotations",
+    Technical Report, Department of Mathematics, University of California,
+    San Diego, 1999
+[7] Barker L.E., Bowles R.L. and Williams L.H., "Development and Application of
+    a Local Linearization Algorithm for the Integration of Quaternion Rate
+    Equations in Real-Time Flight Simulation Problems", NASA TN D-7347,
+    December 1973
+[8] Phillips W.F, Hailey C.E and Gebert G.A, "Review of Attitude Representations
+    Used for Aircraft Kinematics", Journal Of Aircraft Vol. 38, No. 4,
+    July-August 2001
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 INCLUDES
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
 
-#ifdef FGFS
-#  include <simgear/compiler.h>
-#  ifdef SG_HAVE_STD_INCLUDES
-#    include <cmath>
-#    include <iomanip>
-#  else
-#    include <math.h>
-#    include <iomanip.h>
-#  endif
-#else
-#  if defined(sgi) && !defined(__GNUC__)
-#    include <math.h>
-#    if (_COMPILER_VERSION < 740)
-#      include <iomanip.h>
-#    else
-#      include <iomanip>
-#    endif
-#  else
-#    include <cmath>
-#    include <iomanip>
-#  endif
-#endif
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <iomanip>
 
 #include "FGPropagate.h"
-#include <FGState.h>
-#include <FGFDMExec.h>
-#include "FGAircraft.h"
-#include "FGMassBalance.h"
-#include "FGInertial.h"
-#include <input_output/FGPropertyManager.h>
+#include "FGGroundReactions.h"
+#include "FGFDMExec.h"
+#include "input_output/FGPropertyManager.h"
+
+using namespace std;
 
 namespace JSBSim {
 
-static const char *IdSrc = "$Id$";
+static const char *IdSrc = "$Id: FGPropagate.cpp,v 1.105 2012/03/26 21:26:11 bcoconni Exp $";
 static const char *IdHdr = ID_PROPAGATE;
 
 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 CLASS IMPLEMENTATION
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
 
-FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex)
+FGPropagate::FGPropagate(FGFDMExec* fdmex)
+  : FGModel(fdmex),
+    VehicleRadius(0)
 {
+  Debug(0);
   Name = "FGPropagate";
-//  vQtrndot.zero();
+
+  vInertialVelocity.InitMatrix();
+
+  /// These define the indices use to select the various integrators.
+  // eNone = 0, eRectEuler, eTrapezoidal, eAdamsBashforth2, eAdamsBashforth3, eAdamsBashforth4};
+
+  integrator_rotational_rate = eRectEuler;
+  integrator_translational_rate = eAdamsBashforth2;
+  integrator_rotational_position = eRectEuler;
+  integrator_translational_position = eAdamsBashforth3;
+
+  VState.dqPQRidot.resize(4, FGColumnVector3(0.0,0.0,0.0));
+  VState.dqUVWidot.resize(4, FGColumnVector3(0.0,0.0,0.0));
+  VState.dqInertialVelocity.resize(4, FGColumnVector3(0.0,0.0,0.0));
+  VState.dqQtrndot.resize(4, FGQuaternion(0.0,0.0,0.0));
 
   bind();
   Debug(0);
@@ -106,7 +114,6 @@ FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex)
 
 FGPropagate::~FGPropagate(void)
 {
-  unbind();
   Debug(1);
 }
 
@@ -114,12 +121,21 @@ FGPropagate::~FGPropagate(void)
 
 bool FGPropagate::InitModel(void)
 {
-  FGModel::InitModel();
+  // For initialization ONLY:
+  VState.vLocation.SetEllipse(in.SemiMajor, in.SemiMinor);
+  VState.vLocation.SetAltitudeAGL(4.0, FDMExec->GetSimTime());
+
+  vInertialVelocity.InitMatrix();
 
-  SeaLevelRadius = Inertial->RefRadius();          // For initialization ONLY
-  RunwayRadius   = SeaLevelRadius;
+  VState.dqPQRidot.resize(4, FGColumnVector3(0.0,0.0,0.0));
+  VState.dqUVWidot.resize(4, FGColumnVector3(0.0,0.0,0.0));
+  VState.dqInertialVelocity.resize(4, FGColumnVector3(0.0,0.0,0.0));
+  VState.dqQtrndot.resize(4, FGColumnVector3(0.0,0.0,0.0));
 
-  VState.vLocation.SetRadius( SeaLevelRadius + 4.0 );
+  integrator_rotational_rate = eRectEuler;
+  integrator_translational_rate = eAdamsBashforth2;
+  integrator_rotational_position = eRectEuler;
+  integrator_translational_position = eAdamsBashforth3;
 
   return true;
 }
@@ -128,37 +144,56 @@ bool FGPropagate::InitModel(void)
 
 void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
 {
-  SeaLevelRadius = FGIC->GetSeaLevelRadiusFtIC();
-  RunwayRadius = SeaLevelRadius;
+  // Initialize the State Vector elements and the transformation matrices
 
   // Set the position lat/lon/radius
-  VState.vLocation = FGLocation( FGIC->GetLongitudeRadIC(),
-                          FGIC->GetLatitudeRadIC(),
-                          FGIC->GetAltitudeFtIC() + FGIC->GetSeaLevelRadiusFtIC() );
+  VState.vLocation = FGIC->GetPosition();
 
-  // Set the Orientation from the euler angles
-  VState.vQtrn = FGQuaternion( FGIC->GetPhiRadIC(),
-                        FGIC->GetThetaRadIC(),
-                        FGIC->GetPsiRadIC() );
+  Ti2ec = VState.vLocation.GetTi2ec(); // ECI to ECEF transform
+  Tec2i = Ti2ec.Transposed();          // ECEF to ECI frame transform
+
+  VState.vInertialPosition = Tec2i * VState.vLocation;
+
+  UpdateLocationMatrices();
+
+  // Set the orientation from the euler angles (is normalized within the
+  // constructor). The Euler angles represent the orientation of the body
+  // frame relative to the local frame.
+  VState.qAttitudeLocal = FGIC->GetOrientation();
+
+  VState.qAttitudeECI = Ti2l.GetQuaternion()*VState.qAttitudeLocal;
+  UpdateBodyMatrices();
 
   // Set the velocities in the instantaneus body frame
-  VState.vUVW = FGColumnVector3( FGIC->GetUBodyFpsIC(),
-                          FGIC->GetVBodyFpsIC(),
-                          FGIC->GetWBodyFpsIC() );
+  VState.vUVW = FGIC->GetUVWFpsIC();
+
+  // Compute the local frame ECEF velocity
+  vVel = Tb2l * VState.vUVW;
 
-  // Set the angular velocities in the instantaneus body frame.
-  VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
-                          FGIC->GetQRadpsIC(),
-                          FGIC->GetRRadpsIC() );
+  // Compute local terrain velocity
+  RecomputeLocalTerrainVelocity();
+  VehicleRadius = GetRadius();
 
-  // Compute some derived values.
-  vVel = VState.vQtrn.GetTInv()*VState.vUVW;
+  // Set the angular velocities of the body frame relative to the ECEF frame,
+  // expressed in the body frame.
+  VState.vPQR = FGIC->GetPQRRadpsIC();
 
-  // Finally, make sure that the quaternion stays normalized.
-  VState.vQtrn.Normalize();
+  VState.vPQRi = VState.vPQR + Ti2b * in.vOmegaPlanet;
 
-  // Recompute the RunwayRadius level.
-  RecomputeRunwayRadius();
+  CalculateInertialVelocity(); // Translational position derivative
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Initialize the past value deques
+
+void FGPropagate::InitializeDerivatives()
+{
+  for (int i=0; i<4; i++) {
+    VState.dqPQRidot[i] = in.vPQRidot;
+    VState.dqUVWidot[i] = in.vUVWidot;
+    VState.dqInertialVelocity[i] = VState.vInertialVelocity;
+    VState.dqQtrndot[i] = in.vQtrndot;
+  }
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -170,163 +205,392 @@ Notes:   [JB] Run in standalone mode, SeaLevelRadius will be reference radius.
 At the top of this Run() function, see several "shortcuts" (or, aliases) being
 set up for use later, rather than using the longer class->function() notation.
 
-Here, propagation of state is done using a simple explicit Euler scheme (see the
-bottom of the function). This propagation is done using the current state values
+This propagation is done using the current state values
 and current derivatives. Based on these values we compute an approximation to the
 state values for (now + dt).
 
+In the code below, variables named beginning with a small "v" refer to a
+a column vector, variables named beginning with a "T" refer to a transformation
+matrix. ECEF refers to Earth Centered Earth Fixed. ECI refers to Earth Centered
+Inertial.
+
 */
 
-bool FGPropagate::Run(void)
+bool FGPropagate::Run(bool Holding)
 {
-  if (FGModel::Run()) return true;  // Fast return if we have nothing to do ...
-  if (FDMExec->Holding()) return false;
-
-  RecomputeRunwayRadius();
-
-  double dt = State->Getdt()*rate;  // The 'stepsize'
-  const FGColumnVector3 omega( 0.0, 0.0, Inertial->omega() ); // earth rotation
-  const FGColumnVector3& vForces = Aircraft->GetForces();     // current forces
-  const FGColumnVector3& vMoments = Aircraft->GetMoments();   // current moments
-
-  double mass = MassBalance->GetMass();             // mass
-  const FGMatrix33& J = MassBalance->GetJ();        // inertia matrix
-  const FGMatrix33& Jinv = MassBalance->GetJinv();  // inertia matrix inverse
-  double r = GetRadius();                           // radius
-  if (r == 0.0) {cerr << "radius = 0 !" << endl; r = 1e-16;} // radius check
-  double rInv = 1.0/r;
-  FGColumnVector3 gAccel( 0.0, 0.0, Inertial->GetGAccel(r) );
-
-  // The rotation matrices:
-  const FGMatrix33& Tl2b = GetTl2b();  // local to body frame
-  const FGMatrix33& Tb2l = GetTb2l();  // body to local frame
-  const FGMatrix33& Tec2l = VState.vLocation.GetTec2l();  // earth centered to local frame
-  const FGMatrix33& Tl2ec = VState.vLocation.GetTl2ec();  // local to earth centered frame
-
-  // Inertial angular velocity measured in the body frame.
-  const FGColumnVector3 pqri = VState.vPQR + Tl2b*(Tec2l*omega);
-
-  // Compute vehicle velocity wrt EC frame, expressed in Local horizontal frame.
+  if (FGModel::Run(Holding)) return true;  // Fast return if we have nothing to do ...
+  if (Holding) return false;
+
+  double dt = in.DeltaT * rate;  // The 'stepsize'
+
+  // Propagate rotational / translational velocity, angular /translational position, respectively.
+
+  Integrate(VState.qAttitudeECI,      in.vQtrndot,          VState.dqQtrndot,          dt, integrator_rotational_position);
+  Integrate(VState.vPQRi,             in.vPQRidot,          VState.dqPQRidot,          dt, integrator_rotational_rate);
+  Integrate(VState.vInertialPosition, VState.vInertialVelocity, VState.dqInertialVelocity, dt, integrator_translational_position);
+  Integrate(VState.vInertialVelocity, in.vUVWidot,          VState.dqUVWidot,          dt, integrator_translational_rate);
+
+  // CAUTION : the order of the operations below is very important to get transformation
+  // matrices that are consistent with the new state of the vehicle
+
+  // 1. Update the Earth position angle (EPA)
+  VState.vLocation.IncrementEarthPositionAngle(in.vOmegaPlanet(eZ)*(in.DeltaT*rate));
+
+  // 2. Update the Ti2ec and Tec2i transforms from the updated EPA
+  Ti2ec = VState.vLocation.GetTi2ec(); // ECI to ECEF transform
+  Tec2i = Ti2ec.Transposed();          // ECEF to ECI frame transform
+
+  // 3. Update the location from the updated Ti2ec and inertial position
+  VState.vLocation = Ti2ec*VState.vInertialPosition;
+
+  // 4. Update the other "Location-based" transformation matrices from the updated
+  //    vLocation vector.
+  UpdateLocationMatrices();
+
+  // 5. Update the "Orientation-based" transformation matrices from the updated
+  //    orientation quaternion and vLocation vector.
+  UpdateBodyMatrices();
+
+  // Translational position derivative (velocities are integrated in the inertial frame)
+  CalculateUVW();
+
+  // Set auxilliary state variables
+  RecomputeLocalTerrainVelocity();
+  VehicleRadius = GetRadius(); // Calculate current aircraft radius from center of planet
+
+  VState.vPQR = VState.vPQRi - Ti2b * in.vOmegaPlanet;
+
+  VState.qAttitudeLocal = Tl2b.GetQuaternion();
+
+  // Compute vehicle velocity wrt ECEF frame, expressed in Local horizontal frame.
   vVel = Tb2l * VState.vUVW;
 
-  // First compute the time derivatives of the vehicle state values:
+  Debug(2);
+  return false;
+}
 
-  // Compute body frame rotational accelerations based on the current body moments
-  vPQRdot = Jinv*(vMoments - pqri*(J*pqri));
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+  // Transform the velocity vector of the body relative to the origin (Earth
+  // center) to be expressed in the inertial frame, and add the vehicle velocity
+  // contribution due to the rotation of the planet.
+  // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
+  //            Second edition (2004), eqn 1.5-16c (page 50)
 
-  // Compute body frame accelerations based on the current body forces
-  vUVWdot = VState.vUVW*VState.vPQR + vForces/mass;
+void FGPropagate::CalculateInertialVelocity(void)
+{
+  VState.vInertialVelocity = Tb2i * VState.vUVW + (in.vOmegaPlanet * VState.vInertialPosition);
+}
 
-  // Coriolis acceleration.
-  FGColumnVector3 ecVel = Tl2ec*vVel;
-  FGColumnVector3 ace = 2.0*omega*ecVel;
-  vUVWdot -= Tl2b*(Tec2l*ace);
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+  // Transform the velocity vector of the inertial frame to be expressed in the
+  // body frame relative to the origin (Earth center), and substract the vehicle
+  // velocity contribution due to the rotation of the planet.
 
-  if (!GroundReactions->GetWOW()) {
-    // Centrifugal acceleration.
-    FGColumnVector3 aeec = omega*(omega*VState.vLocation);
-    vUVWdot -= Tl2b*(Tec2l*aeec);
-  }
+void FGPropagate::CalculateUVW(void)
+{
+  VState.vUVW = Ti2b * (VState.vInertialVelocity - (in.vOmegaPlanet * VState.vInertialPosition));
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-  // Gravitation accel
-  vUVWdot += Tl2b*gAccel;
+void FGPropagate::Integrate( FGColumnVector3& Integrand,
+                             FGColumnVector3& Val,
+                             deque <FGColumnVector3>& ValDot,
+                             double dt,
+                             eIntegrateType integration_type)
+{
+  ValDot.push_front(Val);
+  ValDot.pop_back();
+
+  switch(integration_type) {
+  case eRectEuler:       Integrand += dt*ValDot[0];
+    break;
+  case eTrapezoidal:     Integrand += 0.5*dt*(ValDot[0] + ValDot[1]);
+    break;
+  case eAdamsBashforth2: Integrand += dt*(1.5*ValDot[0] - 0.5*ValDot[1]);
+    break;
+  case eAdamsBashforth3: Integrand += (1/12.0)*dt*(23.0*ValDot[0] - 16.0*ValDot[1] + 5.0*ValDot[2]);
+    break;
+  case eAdamsBashforth4: Integrand += (1/24.0)*dt*(55.0*ValDot[0] - 59.0*ValDot[1] + 37.0*ValDot[2] - 9.0*ValDot[3]);
+    break;
+  case eNone: // do nothing, freeze translational rate
+    break;
+  default:
+    break;
+  }
+}
 
-  // Compute vehicle velocity wrt EC frame, expressed in EC frame
-  vLocationDot = Tl2ec * vVel;
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-  FGColumnVector3 omegaLocal( rInv*vVel(eEast),
-                              -rInv*vVel(eNorth),
-                              -rInv*vVel(eEast)*VState.vLocation.GetTanLatitude() );
+void FGPropagate::Integrate( FGQuaternion& Integrand,
+                             FGQuaternion& Val,
+                             deque <FGQuaternion>& ValDot,
+                             double dt,
+                             eIntegrateType integration_type)
+{
+  ValDot.push_front(Val);
+  ValDot.pop_back();
+
+  switch(integration_type) {
+  case eRectEuler:       Integrand += dt*ValDot[0];
+    break;
+  case eTrapezoidal:     Integrand += 0.5*dt*(ValDot[0] + ValDot[1]);
+    break;
+  case eAdamsBashforth2: Integrand += dt*(1.5*ValDot[0] - 0.5*ValDot[1]);
+    break;
+  case eAdamsBashforth3: Integrand += (1/12.0)*dt*(23.0*ValDot[0] - 16.0*ValDot[1] + 5.0*ValDot[2]);
+    break;
+  case eAdamsBashforth4: Integrand += (1/24.0)*dt*(55.0*ValDot[0] - 59.0*ValDot[1] + 37.0*ValDot[2] - 9.0*ValDot[3]);
+    break;
+  case eBuss1:
+    {
+      // This is the first order method as described in Samuel R. Buss paper[6].
+      // The formula from Buss' paper is transposed below to quaternions and is
+      // actually the exact solution of the quaternion differential equation
+      // qdot = 1/2*w*q when w is constant.
+      Integrand = Integrand * QExp(0.5 * dt * VState.vPQRi);
+    }
+    return; // No need to normalize since the quaternion exponential is always normal
+  case eBuss2:
+    {
+      // This is the 'augmented second-order method' from S.R. Buss paper [6].
+      // Unlike Runge-Kutta or Adams-Bashforth, it is a one-pass second-order
+      // method (see reference [6]).
+      FGColumnVector3 wi = VState.vPQRi;
+      FGColumnVector3 wdoti = in.vPQRidot;
+      FGColumnVector3 omega = wi + 0.5*dt*wdoti + dt*dt/12.*wdoti*wi;
+      Integrand = Integrand * QExp(0.5 * dt * omega);
+    }
+    return; // No need to normalize since the quaternion exponential is always normal
+  case eLocalLinearization:
+    {
+      // This is the local linearization algorithm of Barker et al. (see ref. [7])
+      // It is also a one-pass second-order method. The code below is based on the
+      // more compact formulation issued from equation (107) of ref. [8]. The
+      // constants C1, C2, C3 and C4 have the same value than those in ref. [7] pp. 11
+      FGColumnVector3 wi = 0.5 * VState.vPQRi;
+      FGColumnVector3 wdoti = 0.5 * in.vPQRidot;
+      double omegak2 = DotProduct(VState.vPQRi, VState.vPQRi);
+      double omegak = omegak2 > 1E-6 ? sqrt(omegak2) : 1E-6;
+      double rhok = 0.5 * dt * omegak;
+      double C1 = cos(rhok);
+      double C2 = 2.0 * sin(rhok) / omegak;
+      double C3 = 4.0 * (1.0 - C1) / (omegak*omegak);
+      double C4 = 4.0 * (dt - C2) / (omegak*omegak);
+      FGColumnVector3 Omega = C2*wi + C3*wdoti + C4*wi*wdoti;
+      FGQuaternion q;
+
+      q(1) = C1 - C4*DotProduct(wi, wdoti);
+      q(2) = Omega(eP);
+      q(3) = Omega(eQ);
+      q(4) = Omega(eR);
+
+      Integrand = Integrand * q;
+
+      /* Cross check with ref. [7] pp.11-12 formulas and code pp. 20
+      double pk = VState.vPQRi(eP);
+      double qk = VState.vPQRi(eQ);
+      double rk = VState.vPQRi(eR);
+      double pdotk = in.vPQRidot(eP);
+      double qdotk = in.vPQRidot(eQ);
+      double rdotk = in.vPQRidot(eR);
+      double Ap = -0.25 * (pk*pdotk + qk*qdotk + rk*rdotk);
+      double Bp = 0.25 * (pk*qdotk - qk*pdotk);
+      double Cp = 0.25 * (pdotk*rk - pk*rdotk);
+      double Dp = 0.25 * (qk*rdotk - qdotk*rk);
+      double C2p = sin(rhok) / omegak;
+      double C3p = 2.0 * (1.0 - cos(rhok)) / (omegak*omegak);
+      double H = C1 + C4 * Ap;
+      double G = -C2p*rk - C3p*rdotk + C4*Bp;
+      double J = C2p*qk + C3p*qdotk - C4*Cp;
+      double K = C2p*pk + C3p*pdotk - C4*Dp;
+
+      cout << "q:       " << q << endl;
+
+      // Warning! In the paper of Barker et al. the quaternion components are not
+      // ordered the same way as in JSBSim (see equations (2) and (3) of ref. [7]
+      // as well as the comment just below equation (3))
+      cout << "FORTRAN: " << H << " , " << K << " , " << J << " , " << -G << endl;*/
+    }
+    break; // The quaternion q is not normal so the normalization needs to be done.
+  case eNone: // do nothing, freeze rotational rate
+    break;
+  default:
+    break;
+  }
 
-  // Compute quaternion orientation derivative on current body rates
-  vQtrndot = VState.vQtrn.GetQDot( VState.vPQR - Tl2b*omegaLocal );
+  Integrand.Normalize();
+}
 
-  // Integrate to propagate the state
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-  // Propagate rotational velocity
+void FGPropagate::UpdateLocationMatrices(void)
+{
+  Tl2ec = VState.vLocation.GetTl2ec(); // local to ECEF transform
+  Tec2l = Tl2ec.Transposed();          // ECEF to local frame transform
+  Ti2l  = VState.vLocation.GetTi2l();  // ECI to local frame transform
+  Tl2i  = Ti2l.Transposed();           // local to ECI transform
+}
 
-  // VState.vPQR += dt*(1.5*vPQRdot - 0.5*last_vPQRdot); // Adams-Bashforth
-  VState.vPQR += (1/12.0)*dt*(23.0*vPQRdot - 16.0*last_vPQRdot + 5.0*last2_vPQRdot); // Adams-Bashforth 3
-  // VState.vPQR += dt*vPQRdot;                          // Rectangular Euler
-  // VState.vPQR += 0.5*dt*(vPQRdot + last_vPQRdot);     // Trapezoidal
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-  // Propagate translational velocity
+void FGPropagate::UpdateBodyMatrices(void)
+{
+  Ti2b  = VState.qAttitudeECI.GetT(); // ECI to body frame transform
+  Tb2i  = Ti2b.Transposed();          // body to ECI frame transform
+  Tl2b  = Ti2b * Tl2i;                // local to body frame transform
+  Tb2l  = Tl2b.Transposed();          // body to local frame transform
+  Tec2b = Ti2b * Tec2i;               // ECEF to body frame transform
+  Tb2ec = Tec2b.Transposed();         // body to ECEF frame tranform
+}
 
-  // VState.vUVW += dt*(1.5*vUVWdot - 0.5*last_vUVWdot); // Adams Bashforth
-  VState.vUVW += (1/12.0)*dt*(23.0*vUVWdot - 16.0*last_vUVWdot + 5.0*last2_vUVWdot); // Adams-Bashforth 3
-  // VState.vUVW += dt*vUVWdot;                         // Rectangular Euler
-  // VState.vUVW += 0.5*dt*(vUVWdot + last_vUVWdot);    // Trapezoidal
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-  // Propagate angular position
+void FGPropagate::SetInertialOrientation(const FGQuaternion& Qi) {
+  VState.qAttitudeECI = Qi;
+  VState.qAttitudeECI.Normalize();
+  UpdateBodyMatrices();
+  VState.qAttitudeLocal = Tl2b.GetQuaternion();
+}
 
-  // VState.vQtrn += dt*(1.5*vQtrndot - 0.5*last_vQtrndot); // Adams Bashforth
-  VState.vQtrn += (1/12.0)*dt*(23.0*vQtrndot - 16.0*last_vQtrndot + 5.0*last2_vQtrndot); // Adams-Bashforth 3
-  // VState.vQtrn += dt*vQtrndot;                           // Rectangular Euler
-  // VState.vQtrn += 0.5*dt*(vQtrndot + last_vQtrndot);     // Trapezoidal
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-  // Propagate translational position
+void FGPropagate::SetInertialVelocity(const FGColumnVector3& Vi) {
+  VState.vInertialVelocity = Vi;
+  CalculateUVW();
+  vVel = Tb2l * VState.vUVW;
+}
 
-  // VState.vLocation += dt*(1.5*vLocationDot - 0.5*last_vLocationDot); // Adams Bashforth
-  VState.vLocation += (1/12.0)*dt*(23.0*vLocationDot - 16.0*last_vLocationDot + 5.0*last2_vLocationDot); // Adams-Bashforth 3
-  // VState.vLocation += dt*vLocationDot;                               // Rectangular Euler
-  // VState.vLocation += 0.5*dt*(vLocationDot + last_vLocationDot);     // Trapezoidal
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-  // Set past values
-  
-  last2_vPQRdot = last_vPQRdot;
-  last_vPQRdot = vPQRdot;
-  
-  last2_vUVWdot = last_vUVWdot;
-  last_vUVWdot = vUVWdot;
-  
-  last2_vQtrndot = last_vQtrndot;
-  last_vQtrndot = vQtrndot;
+void FGPropagate::SetInertialRates(const FGColumnVector3& vRates) {
+  VState.vPQRi = Ti2b * vRates;
+  VState.vPQR = VState.vPQRi - Ti2b * in.vOmegaPlanet;
+}
 
-  last2_vLocationDot = last_vLocationDot;
-  last_vLocationDot = vLocationDot;
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-  return false;
+void FGPropagate::RecomputeLocalTerrainVelocity()
+{
+  FGLocation contact;
+  FGColumnVector3 normal;
+  VState.vLocation.GetContactPoint(FDMExec->GetSimTime(), contact, normal,
+                                   LocalTerrainVelocity, LocalTerrainAngularVelocity);
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-void FGPropagate::RecomputeRunwayRadius(void)
+void FGPropagate::SetTerrainElevation(double terrainElev)
 {
-  // Get the runway radius.
-  FGLocation contactloc;
-  FGColumnVector3 dv;
-  FGGroundCallback* gcb = FDMExec->GetGroundCallback();
-  double t = State->Getsim_time();
-  gcb->GetAGLevel(t, VState.vLocation, contactloc, dv, dv);
-  RunwayRadius = contactloc.GetRadius();
+  double radius = terrainElev + VState.vLocation.GetSeaLevelRadius();
+  FDMExec->GetGroundCallback()->SetTerrainGeoCentRadius(radius);
 }
 
+
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-void FGPropagate::Seth(double tt)
+void FGPropagate::SetSeaLevelRadius(double tt)
 {
-  VState.vLocation.SetRadius( tt + SeaLevelRadius );
+  FDMExec->GetGroundCallback()->SetSeaLevelRadius(tt);
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-double FGPropagate::GetRunwayRadius(void) const
+double FGPropagate::GetLocalTerrainRadius(void) const
 {
-  return RunwayRadius;
+  return VState.vLocation.GetTerrainRadius(FDMExec->GetSimTime());
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 double FGPropagate::GetDistanceAGL(void) const
 {
-  return VState.vLocation.GetRadius() - RunwayRadius;
+  return VState.vLocation.GetAltitudeAGL(FDMExec->GetSimTime());
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 void FGPropagate::SetDistanceAGL(double tt)
 {
-  VState.vLocation.SetRadius( tt + RunwayRadius );
+  VState.vLocation.SetAltitudeAGL(tt, FDMExec->GetSimTime());
+  UpdateVehicleState();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropagate::SetVState(const VehicleState& vstate)
+{
+  //ToDo: Shouldn't all of these be set from the vstate vector passed in?
+  VState.vLocation = vstate.vLocation;
+  Ti2ec = VState.vLocation.GetTi2ec(); // useless ?
+  Tec2i = Ti2ec.Transposed();
+  UpdateLocationMatrices();
+  SetInertialOrientation(vstate.qAttitudeECI);
+  RecomputeLocalTerrainVelocity();
+  VehicleRadius = GetRadius();
+  VState.vUVW = vstate.vUVW;
+  vVel = Tb2l * VState.vUVW;
+  VState.vPQR = vstate.vPQR;
+  VState.vPQRi = VState.vPQR + Ti2b * in.vOmegaPlanet;
+  VState.vInertialPosition = vstate.vInertialPosition;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropagate::UpdateVehicleState(void)
+{
+  RecomputeLocalTerrainVelocity();
+  VehicleRadius = GetRadius();
+  VState.vInertialPosition = Tec2i * VState.vLocation;
+  UpdateLocationMatrices();
+  UpdateBodyMatrices();
+  vVel = Tb2l * VState.vUVW;
+  VState.qAttitudeLocal = Tl2b.GetQuaternion();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropagate::SetLocation(const FGLocation& l)
+{
+  VState.vLocation = l;
+  Ti2ec = VState.vLocation.GetTi2ec(); // useless ?
+  Tec2i = Ti2ec.Transposed();
+  UpdateVehicleState();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropagate::DumpState(void)
+{
+  cout << endl;
+  cout << fgblue
+       << "------------------------------------------------------------------" << reset << endl;
+  cout << highint
+       << "State Report at sim time: " << FDMExec->GetSimTime() << " seconds" << reset << endl;
+  cout << "  " << underon
+       <<   "Position" << underoff << endl;
+  cout << "    ECI:   " << VState.vInertialPosition.Dump(", ") << " (x,y,z, in ft)" << endl;
+  cout << "    ECEF:  " << VState.vLocation << " (x,y,z, in ft)"  << endl;
+  cout << "    Local: " << VState.vLocation.GetLatitudeDeg()
+                        << ", " << VState.vLocation.GetLongitudeDeg()
+                        << ", " << GetAltitudeASL() << " (lat, lon, alt in deg and ft)" << endl;
+
+  cout << endl << "  " << underon
+       <<   "Orientation" << underoff << endl;
+  cout << "    ECI:   " << VState.qAttitudeECI.GetEulerDeg().Dump(", ") << " (phi, theta, psi in deg)" << endl;
+  cout << "    Local: " << VState.qAttitudeLocal.GetEulerDeg().Dump(", ") << " (phi, theta, psi in deg)" << endl;
+
+  cout << endl << "  " << underon
+       <<   "Velocity" << underoff << endl;
+  cout << "    ECI:   " << VState.vInertialVelocity.Dump(", ") << " (x,y,z in ft/s)" << endl;
+  cout << "    ECEF:  " << (Tb2ec * VState.vUVW).Dump(", ")  << " (x,y,z in ft/s)"  << endl;
+  cout << "    Local: " << GetVel() << " (n,e,d in ft/sec)" << endl;
+  cout << "    Body:  " << GetUVW() << " (u,v,w in ft/sec)" << endl;
+
+  cout << endl << "  " << underon
+       <<   "Body Rates (relative to given frame, expressed in body frame)" << underoff << endl;
+  cout << "    ECI:   " << (VState.vPQRi*radtodeg).Dump(", ") << " (p,q,r in deg/s)" << endl;
+  cout << "    ECEF:  " << (VState.vPQR*radtodeg).Dump(", ") << " (p,q,r in deg/s)" << endl;
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -334,6 +598,7 @@ void FGPropagate::SetDistanceAGL(double tt)
 void FGPropagate::bind(void)
 {
   typedef double (FGPropagate::*PMF)(int) const;
+
   PropertyManager->Tie("velocities/h-dot-fps", this, &FGPropagate::Gethdot);
 
   PropertyManager->Tie("velocities/v-north-fps", this, eNorth, (PMF)&FGPropagate::GetVel);
@@ -348,21 +613,29 @@ void FGPropagate::bind(void)
   PropertyManager->Tie("velocities/q-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQR);
   PropertyManager->Tie("velocities/r-rad_sec", this, eR, (PMF)&FGPropagate::GetPQR);
 
-  PropertyManager->Tie("accelerations/pdot-rad_sec", this, eP, (PMF)&FGPropagate::GetPQRdot);
-  PropertyManager->Tie("accelerations/qdot-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQRdot);
-  PropertyManager->Tie("accelerations/rdot-rad_sec", this, eR, (PMF)&FGPropagate::GetPQRdot);
-
-  PropertyManager->Tie("accelerations/udot-fps", this, eU, (PMF)&FGPropagate::GetUVWdot);
-  PropertyManager->Tie("accelerations/vdot-fps", this, eV, (PMF)&FGPropagate::GetUVWdot);
-  PropertyManager->Tie("accelerations/wdot-fps", this, eW, (PMF)&FGPropagate::GetUVWdot);
-
-  PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::Geth, &FGPropagate::Seth, true);
-  PropertyManager->Tie("position/lat-gc-rad", this, &FGPropagate::GetLatitude, &FGPropagate::SetLatitude);
-  PropertyManager->Tie("position/long-gc-rad", this, &FGPropagate::GetLongitude, &FGPropagate::SetLongitude);
+  PropertyManager->Tie("velocities/pi-rad_sec", this, eP, (PMF)&FGPropagate::GetPQRi);
+  PropertyManager->Tie("velocities/qi-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQRi);
+  PropertyManager->Tie("velocities/ri-rad_sec", this, eR, (PMF)&FGPropagate::GetPQRi);
+
+  PropertyManager->Tie("velocities/eci-velocity-mag-fps", this, &FGPropagate::GetInertialVelocityMagnitude);
+
+  PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::GetAltitudeASL, &FGPropagate::SetAltitudeASL, true);
+  PropertyManager->Tie("position/h-sl-meters", this, &FGPropagate::GetAltitudeASLmeters, &FGPropagate::SetAltitudeASLmeters, true);
+  PropertyManager->Tie("position/lat-gc-rad", this, &FGPropagate::GetLatitude, &FGPropagate::SetLatitude, false);
+  PropertyManager->Tie("position/long-gc-rad", this, &FGPropagate::GetLongitude, &FGPropagate::SetLongitude, false);
+  PropertyManager->Tie("position/lat-gc-deg", this, &FGPropagate::GetLatitudeDeg, &FGPropagate::SetLatitudeDeg, false);
+  PropertyManager->Tie("position/long-gc-deg", this, &FGPropagate::GetLongitudeDeg, &FGPropagate::SetLongitudeDeg, false);
+  PropertyManager->Tie("position/lat-geod-rad", this, &FGPropagate::GetGeodLatitudeRad);
+  PropertyManager->Tie("position/lat-geod-deg", this, &FGPropagate::GetGeodLatitudeDeg);
+  PropertyManager->Tie("position/geod-alt-ft", this, &FGPropagate::GetGeodeticAltitude);
   PropertyManager->Tie("position/h-agl-ft", this,  &FGPropagate::GetDistanceAGL, &FGPropagate::SetDistanceAGL);
   PropertyManager->Tie("position/radius-to-vehicle-ft", this, &FGPropagate::GetRadius);
+  PropertyManager->Tie("position/terrain-elevation-asl-ft", this,
+                          &FGPropagate::GetTerrainElevation,
+                          &FGPropagate::SetTerrainElevation, false);
 
-  PropertyManager->Tie("metrics/runway-radius", this, &FGPropagate::GetRunwayRadius);
+  PropertyManager->Tie("position/epa-rad", this, &FGPropagate::GetEarthPositionAngle);
+  PropertyManager->Tie("metrics/terrain-radius", this, &FGPropagate::GetLocalTerrainRadius);
 
   PropertyManager->Tie("attitude/phi-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
   PropertyManager->Tie("attitude/theta-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
@@ -371,40 +644,11 @@ void FGPropagate::bind(void)
   PropertyManager->Tie("attitude/roll-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
   PropertyManager->Tie("attitude/pitch-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
   PropertyManager->Tie("attitude/heading-true-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
-}
-
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-void FGPropagate::unbind(void)
-{
-  PropertyManager->Untie("velocities/v-north-fps");
-  PropertyManager->Untie("velocities/v-east-fps");
-  PropertyManager->Untie("velocities/v-down-fps");
-  PropertyManager->Untie("velocities/h-dot-fps");
-  PropertyManager->Untie("velocities/u-fps");
-  PropertyManager->Untie("velocities/v-fps");
-  PropertyManager->Untie("velocities/w-fps");
-  PropertyManager->Untie("velocities/p-rad_sec");
-  PropertyManager->Untie("velocities/q-rad_sec");
-  PropertyManager->Untie("velocities/r-rad_sec");
-  PropertyManager->Untie("accelerations/udot-fps");
-  PropertyManager->Untie("accelerations/vdot-fps");
-  PropertyManager->Untie("accelerations/wdot-fps");
-  PropertyManager->Untie("accelerations/pdot-rad_sec");
-  PropertyManager->Untie("accelerations/qdot-rad_sec");
-  PropertyManager->Untie("accelerations/rdot-rad_sec");
-  PropertyManager->Untie("position/h-sl-ft");
-  PropertyManager->Untie("position/lat-gc-rad");
-  PropertyManager->Untie("position/long-gc-rad");
-  PropertyManager->Untie("position/h-agl-ft");
-  PropertyManager->Untie("position/radius-to-vehicle-ft");
-  PropertyManager->Untie("metrics/runway-radius");
-  PropertyManager->Untie("attitude/phi-rad");
-  PropertyManager->Untie("attitude/theta-rad");
-  PropertyManager->Untie("attitude/psi-rad");
-  PropertyManager->Untie("attitude/roll-rad");
-  PropertyManager->Untie("attitude/pitch-rad");
-  PropertyManager->Untie("attitude/heading-true-rad");
+  PropertyManager->Tie("simulation/integrator/rate/rotational", (int*)&integrator_rotational_rate);
+  PropertyManager->Tie("simulation/integrator/rate/translational", (int*)&integrator_translational_rate);
+  PropertyManager->Tie("simulation/integrator/position/rotational", (int*)&integrator_rotational_position);
+  PropertyManager->Tie("simulation/integrator/position/translational", (int*)&integrator_translational_position);
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -441,9 +685,112 @@ void FGPropagate::Debug(int from)
   }
   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
   }
-  if (debug_lvl & 8 ) { // Runtime state variables
+  if (debug_lvl & 8 && from == 2) { // Runtime state variables
+    cout << endl << fgblue << highint << left
+         << "  Propagation Report (English units: ft, degrees) at simulation time " << FDMExec->GetSimTime() << " seconds"
+         << reset << endl;
+    cout << endl;
+    cout << highint << "  Earth Position Angle (deg): " << setw(8) << setprecision(3) << reset
+         << GetEarthPositionAngleDeg() << endl;
+    cout << endl;
+    cout << highint << "  Body velocity (ft/sec): " << setw(8) << setprecision(3) << reset << VState.vUVW << endl;
+    cout << highint << "  Local velocity (ft/sec): " << setw(8) << setprecision(3) << reset << vVel << endl;
+    cout << highint << "  Inertial velocity (ft/sec): " << setw(8) << setprecision(3) << reset << VState.vInertialVelocity << endl;
+    cout << highint << "  Inertial Position (ft): " << setw(10) << setprecision(3) << reset << VState.vInertialPosition << endl;
+    cout << highint << "  Latitude (deg): " << setw(8) << setprecision(3) << reset << VState.vLocation.GetLatitudeDeg() << endl;
+    cout << highint << "  Longitude (deg): " << setw(8) << setprecision(3) << reset << VState.vLocation.GetLongitudeDeg() << endl;
+    cout << highint << "  Altitude ASL (ft): " << setw(8) << setprecision(3) << reset << GetAltitudeASL() << endl;
+//    cout << highint << "  Acceleration (NED, ft/sec^2): " << setw(8) << setprecision(3) << reset << Tb2l*GetUVWdot() << endl;
+    cout << endl;
+    cout << highint << "  Matrix ECEF to Body (Orientation of Body with respect to ECEF): "
+                    << reset << endl << Tec2b.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tec2b.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Body to ECEF (Orientation of ECEF with respect to Body):"
+                    << reset << endl << Tb2ec.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tb2ec.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Local to Body (Orientation of Body with respect to Local):"
+                    << reset << endl << Tl2b.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tl2b.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Body to Local (Orientation of Local with respect to Body):"
+                    << reset << endl << Tb2l.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tb2l.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Local to ECEF (Orientation of ECEF with respect to Local):"
+                    << reset << endl << Tl2ec.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tl2ec.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix ECEF to Local (Orientation of Local with respect to ECEF):"
+                    << reset << endl << Tec2l.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tec2l.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix ECEF to Inertial (Orientation of Inertial with respect to ECEF):"
+                    << reset << endl << Tec2i.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tec2i.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Inertial to ECEF (Orientation of ECEF with respect to Inertial):"
+                    << reset << endl << Ti2ec.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Ti2ec.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Inertial to Body (Orientation of Body with respect to Inertial):"
+                    << reset << endl << Ti2b.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Ti2b.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Body to Inertial (Orientation of Inertial with respect to Body):"
+                    << reset << endl << Tb2i.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tb2i.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Inertial to Local (Orientation of Local with respect to Inertial):"
+                    << reset << endl << Ti2l.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Ti2l.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << highint << "  Matrix Local to Inertial (Orientation of Inertial with respect to Local):"
+                    << reset << endl << Tl2i.Dump("\t", "    ") << endl;
+    cout << highint << "    Associated Euler angles (deg): " << setw(8)
+                    << setprecision(3) << reset << (Tl2i.GetQuaternion().GetEuler()*radtodeg)
+                    << endl << endl;
+
+    cout << setprecision(6); // reset the output stream
   }
   if (debug_lvl & 16) { // Sanity checking
+    if (from == 2) { // State sanity checking
+      if (fabs(VState.vPQR.Magnitude()) > 1000.0) {
+        cerr << endl << "Vehicle rotation rate is excessive (>1000 rad/sec): " << VState.vPQR.Magnitude() << endl;
+        exit(-1);
+      }
+      if (fabs(VState.vUVW.Magnitude()) > 1.0e10) {
+        cerr << endl << "Vehicle velocity is excessive (>1e10 ft/sec): " << VState.vUVW.Magnitude() << endl;
+        exit(-1);
+      }
+      if (fabs(GetDistanceAGL()) > 1e10) {
+        cerr << endl << "Vehicle altitude is excessive (>1e10 ft): " << GetDistanceAGL() << endl;
+        exit(-1);
+      }
+    }
   }
   if (debug_lvl & 64) {
     if (from == 0) { // Constructor