]> git.mxchange.org Git - flightgear.git/blobdiff - src/FDM/JSBSim/models/FGPropagate.cpp
Merge branch 'next' into durk-atc
[flightgear.git] / src / FDM / JSBSim / models / FGPropagate.cpp
index 0660104058a17fdf2c39df4ce3d5b4042ad9a2f0..ae2174df91557d5c190be4c4f728d41bbbbc5391 100644 (file)
@@ -71,28 +71,35 @@ using namespace std;
 
 namespace JSBSim {
 
-static const char *IdSrc = "$Id: FGPropagate.cpp,v 1.73 2010/11/18 12:38:06 jberndt Exp $";
+static const char *IdSrc = "$Id: FGPropagate.cpp,v 1.85 2011/04/03 19:24:58 jberndt Exp $";
 static const char *IdHdr = ID_PROPAGATE;
 
 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 CLASS IMPLEMENTATION
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
 
-FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex)
+FGPropagate::FGPropagate(FGFDMExec* fdmex)
+  : FGModel(fdmex),
+    LocalTerrainRadius(0),
+    SeaLevelRadius(0),
+    VehicleRadius(0)
 {
   Debug(0);
   Name = "FGPropagate";
   gravType = gtWGS84;
  
-  vPQRdot.InitMatrix();
+  vPQRidot.InitMatrix();
   vQtrndot = FGQuaternion(0,0,0);
-  vUVWdot.InitMatrix();
+  vUVWidot.InitMatrix();
   vInertialVelocity.InitMatrix();
 
-  integrator_rotational_rate = eAdamsBashforth2;
-  integrator_translational_rate = eTrapezoidal;
-  integrator_rotational_position = eAdamsBashforth2;
-  integrator_translational_position = eTrapezoidal;
+  /// 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));
@@ -123,9 +130,9 @@ bool FGPropagate::InitModel(void)
   VState.vLocation.SetEllipse(FDMExec->GetInertial()->GetSemimajor(), FDMExec->GetInertial()->GetSemiminor());
   vOmegaEarth = FGColumnVector3( 0.0, 0.0, FDMExec->GetInertial()->omega() ); // Earth rotation vector
 
-  vPQRdot.InitMatrix();
+  vPQRidot.InitMatrix();
   vQtrndot = FGQuaternion(0,0,0);
-  vUVWdot.InitMatrix();
+  vUVWidot.InitMatrix();
   vInertialVelocity.InitMatrix();
 
   VState.dqPQRidot.resize(4, FGColumnVector3(0.0,0.0,0.0));
@@ -157,8 +164,8 @@ void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
 
   VState.vLocation.SetEarthPositionAngle(FDMExec->GetInertial()->GetEarthPositionAngle());
 
-  Ti2ec = GetTi2ec();         // ECI to ECEF transform
-  Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
+  Ti2ec = VState.vLocation.GetTi2ec(); // ECI to ECEF transform
+  Tec2i = Ti2ec.Transposed();          // ECEF to ECI frame transform
 
   VState.vInertialPosition = Tec2i * VState.vLocation;
 
@@ -188,23 +195,13 @@ void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
   VehicleRadius = GetRadius();
   double radInv = 1.0/VehicleRadius;
 
-  // Refer to Stevens and Lewis, 1.5-14a, pg. 49.
-  // This is the rotation rate of the "Local" frame, expressed in the local frame.
-
-  FGColumnVector3 vOmegaLocal = FGColumnVector3(
-     radInv*vVel(eEast),
-    -radInv*vVel(eNorth),
-    -radInv*vVel(eEast)*VState.vLocation.GetTanLatitude() );
-
   // Set the angular velocities of the body frame relative to the ECEF frame,
-  // expressed in the body frame. Effectively, this is:
-  //   w_b/e = w_b/l + w_l/e
+  // expressed in the body frame.
   VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
                                  FGIC->GetQRadpsIC(),
-                                 FGIC->GetRRadpsIC() ) + Tl2b*vOmegaLocal;
+                                 FGIC->GetRRadpsIC() );
 
   VState.vPQRi = VState.vPQR + Ti2b * vOmegaEarth;
-  VState.vPQRi_i = Tb2i * VState.vPQRi;
 
   // Make an initial run and set past values
   InitializeDerivatives();
@@ -244,11 +241,10 @@ bool FGPropagate::Run(void)
   CalculateUVWdot();           // Translational rate derivative
   ResolveFrictionForces(dt);   // Update rate derivatives with friction forces
   CalculateQuatdot();          // Angular orientation derivative
-  CalculateUVW();              // Translational position derivative (velocities are integrated in the inertial frame)
 
   // Propagate rotational / translational velocity, angular /translational position, respectively.
 
-  Integrate(VState.vPQRi_i,           vPQRidot,          VState.dqPQRidot,          dt, integrator_rotational_rate); // ECI  integration
+  Integrate(VState.vPQRi,             vPQRidot,          VState.dqPQRidot,          dt, integrator_rotational_rate);
   Integrate(VState.qAttitudeECI,      vQtrndot,          VState.dqQtrndot,          dt, integrator_rotational_position);
   Integrate(VState.vInertialPosition, VState.vInertialVelocity, VState.dqInertialVelocity, dt, integrator_translational_position);
   Integrate(VState.vInertialVelocity, vUVWidot,          VState.dqUVWidot,          dt, integrator_translational_rate);
@@ -260,8 +256,8 @@ bool FGPropagate::Run(void)
   VState.vLocation.SetEarthPositionAngle(FDMExec->GetInertial()->GetEarthPositionAngle());
 
   // 2. Update the Ti2ec and Tec2i transforms from the updated EPA
-  Ti2ec = GetTi2ec();          // ECI to ECEF transform
-  Tec2i = Ti2ec.Transposed();  // ECEF to ECI frame transform
+  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;
@@ -277,12 +273,13 @@ bool FGPropagate::Run(void)
   //    orientation quaternion and vLocation vector.
   UpdateBodyMatrices();
 
+  CalculateUVW();              // Translational position derivative (velocities are integrated in the inertial frame)
+
   // Set auxililary state variables
   RecomputeLocalTerrainRadius();
 
   VehicleRadius = GetRadius(); // Calculate current aircraft radius from center of planet
 
-  VState.vPQRi = Ti2b * VState.vPQRi_i;
   VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth;
 
   VState.qAttitudeLocal = Tl2b.GetQuaternion();
@@ -320,8 +317,8 @@ void FGPropagate::CalculatePQRdot(void)
   // moments and the total inertial angular velocity expressed in the body
   // frame.
 
-  vPQRdot = Jinv*(vMoments - VState.vPQRi*(J*VState.vPQRi));
-  vPQRidot = Tb2i * vPQRdot;
+  vPQRidot = Jinv*(vMoments - VState.vPQRi*(J*VState.vPQRi));
+  vPQRdot = vPQRidot - VState.vPQRi * (Ti2b * vOmegaEarth);
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -454,6 +451,55 @@ void FGPropagate::Integrate( FGQuaternion& Integrand,
   }
 }
 
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Evaluates the rates (translation or rotation) that the friction forces have
+// to resist to. This includes the external forces and moments as well as the
+// relative movement between the aircraft and the ground.
+// Erin Catto's paper (see ref [6]) only supports Euler integration scheme and
+// this algorithm has been adapted to handle the multistep algorithms that
+// JSBSim supports (i.e. Trapezoidal, Adams-Bashforth 2, 3 and 4). The capacity
+// to handle the multistep integration schemes adds some complexity but it
+// significantly helps stabilizing the friction forces.
+
+void FGPropagate::EvaluateRateToResistTo(FGColumnVector3& vdot,
+                                         const FGColumnVector3& Val,
+                                         const FGColumnVector3& ValDot,
+                                         const FGColumnVector3& LocalTerrainVal,
+                                         deque <FGColumnVector3>& dqValDot,
+                                         const double dt,
+                                         const eIntegrateType integration_type)
+{
+  switch(integration_type) {
+  case eAdamsBashforth4:
+    vdot = ValDot + Ti2b * (-59.*dqValDot[0]+37.*dqValDot[1]-9.*dqValDot[2])/55.;
+    if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
+      vdot += 24.*(Val - Tec2b * LocalTerrainVal) / (55.*dt);
+    break;
+  case eAdamsBashforth3:
+    vdot = ValDot + Ti2b * (-16.*dqValDot[0]+5.*dqValDot[1])/23.;
+    if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
+      vdot += 12.*(Val - Tec2b * LocalTerrainVal) / (23.*dt);
+    break;
+  case eAdamsBashforth2:
+    vdot = ValDot - Ti2b * dqValDot[0]/3.;
+    if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
+      vdot += 2.*(Val - Tec2b * LocalTerrainVal) / (3.*dt);
+    break;
+  case eTrapezoidal:
+    vdot = ValDot + Ti2b * dqValDot[0];
+    if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
+      vdot += 2.*(Val - Tec2b * LocalTerrainVal) / dt;
+    break;
+  case eRectEuler:
+    vdot = ValDot;
+    if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
+      vdot += (Val - Tec2b * LocalTerrainVal) / dt;
+    break;
+  case eNone:
+    break;
+  }
+}
+
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 // Resolves the contact forces just before integrating the EOM.
 // This routine is using Lagrange multipliers and the projected Gauss-Seidel
@@ -472,58 +518,52 @@ void FGPropagate::ResolveFrictionForces(double dt)
   const double invMass = 1.0 / FDMExec->GetMassBalance()->GetMass();
   const FGMatrix33& Jinv = FDMExec->GetMassBalance()->GetJinv();
   vector <FGColumnVector3> JacF, JacM;
+  vector<double> lambda, lambdaMin, lambdaMax;
   FGColumnVector3 vdot, wdot;
   FGColumnVector3 Fc, Mc;
-  int n = 0, i;
+  int n = 0;
 
   // Compiles data from the ground reactions to build up the jacobian matrix
   for (MultiplierIterator it=MultiplierIterator(FDMExec->GetGroundReactions()); *it; ++it, n++) {
     JacF.push_back((*it)->ForceJacobian);
     JacM.push_back((*it)->MomentJacobian);
+    lambda.push_back((*it)->value);
+    lambdaMax.push_back((*it)->Max);
+    lambdaMin.push_back((*it)->Min);
   }
 
   // If no gears are in contact with the ground then return
   if (!n) return;
 
   vector<double> a(n*n); // Will contain J*M^-1*J^T
-  vector<double> eta(n);
-  vector<double> lambda(n);
-  vector<double> lambdaMin(n);
-  vector<double> lambdaMax(n);
-
-  // Initializes the Lagrange multipliers
-  i = 0;
-  for (MultiplierIterator it=MultiplierIterator(FDMExec->GetGroundReactions()); *it; ++it, i++) {
-    lambda[i] = (*it)->value;
-    lambdaMax[i] = (*it)->Max;
-    lambdaMin[i] = (*it)->Min;
-  }
-
-  vdot = vUVWdot;
-  wdot = vPQRdot;
-
-  if (dt > 0.) {
-    // Instruct the algorithm to zero out the relative movement between the
-    // aircraft and the ground.
-    vdot += (VState.vUVW - Tec2b * LocalTerrainVelocity) / dt;
-    wdot += (VState.vPQR - Tec2b * LocalTerrainAngularVelocity) / dt;
-  }
+  vector<double> rhs(n);
 
   // Assemble the linear system of equations
-  for (i=0; i < n; i++) {
+  for (int i=0; i < n; i++) {
     for (int j=0; j < i; j++)
       a[i*n+j] = a[j*n+i]; // Takes advantage of the symmetry of J^T*M^-1*J
     for (int j=i; j < n; j++)
       a[i*n+j] = DotProduct(JacF[i],invMass*JacF[j])+DotProduct(JacM[i],Jinv*JacM[j]);
   }
 
+  // Assemble the RHS member
+
+  // Translation
+  EvaluateRateToResistTo(vdot, VState.vUVW, vUVWdot, LocalTerrainVelocity,
+                         VState.dqUVWidot, dt, integrator_translational_rate);
+
+  // Rotation
+  EvaluateRateToResistTo(wdot, VState.vPQR, vPQRdot, LocalTerrainAngularVelocity,
+                         VState.dqPQRidot, dt, integrator_rotational_rate);
+
   // Prepare the linear system for the Gauss-Seidel algorithm :
-  // divide every line of 'a' and eta by a[i,i]. This is in order to save
-  // a division computation at each iteration of Gauss-Seidel.
-  for (i=0; i < n; i++) {
+  // 1. Compute the right hand side member 'rhs'
+  // 2. Divide every line of 'a' and 'rhs' by a[i,i]. This is in order to save
+  //    a division computation at each iteration of Gauss-Seidel.
+  for (int i=0; i < n; i++) {
     double d = 1.0 / a[i*n+i];
 
-    eta[i] = -(DotProduct(JacF[i],vdot)+DotProduct(JacM[i],wdot))*d;
+    rhs[i] = -(DotProduct(JacF[i],vdot)+DotProduct(JacM[i],wdot))*d;
     for (int j=0; j < n; j++)
       a[i*n+j] *= d;
   }
@@ -532,9 +572,9 @@ void FGPropagate::ResolveFrictionForces(double dt)
   for (int iter=0; iter < 50; iter++) {
     double norm = 0.;
 
-    for (i=0; i < n; i++) {
+    for (int i=0; i < n; i++) {
       double lambda0 = lambda[i];
-      double dlambda = eta[i];
+      double dlambda = rhs[i];
       
       for (int j=0; j < n; j++)
         dlambda -= a[i*n+j]*lambda[j];
@@ -553,7 +593,7 @@ void FGPropagate::ResolveFrictionForces(double dt)
   Fc.InitMatrix();
   Mc.InitMatrix();
 
-  for (i=0; i< n; i++) {
+  for (int i=0; i< n; i++) {
     Fc += lambda[i]*JacF[i];
     Mc += lambda[i]*JacM[i];
   }
@@ -561,11 +601,11 @@ void FGPropagate::ResolveFrictionForces(double dt)
   vUVWdot += invMass * Fc;
   vUVWidot += invMass * Tb2i * Fc;
   vPQRdot += Jinv * Mc;
-  vPQRidot += Tb2i* Jinv * Mc;
+  vPQRidot += Jinv * Mc;
 
   // Save the value of the Lagrange multipliers to accelerate the convergence
   // of the Gauss-Seidel algorithm at next iteration.
-  i = 0;
+  int i = 0;
   for (MultiplierIterator it=MultiplierIterator(FDMExec->GetGroundReactions()); *it; ++it)
     (*it)->value = lambda[i++];
 
@@ -576,22 +616,22 @@ void FGPropagate::ResolveFrictionForces(double dt)
 
 void FGPropagate::UpdateLocationMatrices(void)
 {
-  Tl2ec = GetTl2ec();          // local to ECEF transform
-  Tec2l = Tl2ec.Transposed();  // ECEF to local frame transform
-  Ti2l  = GetTi2l();
-  Tl2i  = Ti2l.Transposed();
+  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
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 void FGPropagate::UpdateBodyMatrices(void)
 {
-  Ti2b  = GetTi2b();           // 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 = Tl2b * Tec2l;        // ECEF to body frame transform
-  Tb2ec = Tec2b.Transposed();  // body to ECEF frame tranform
+  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 = Tl2b * Tec2l;               // ECEF to body frame transform
+  Tb2ec = Tec2b.Transposed();         // body to ECEF frame tranform
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -608,14 +648,13 @@ void FGPropagate::SetInertialOrientation(FGQuaternion Qi) {
 void FGPropagate::SetInertialVelocity(FGColumnVector3 Vi) {
   VState.vInertialVelocity = Vi;
   CalculateUVW();
-  vVel = GetTb2l() * VState.vUVW;
+  vVel = Tb2l * VState.vUVW;
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 void FGPropagate::SetInertialRates(FGColumnVector3 vRates) {
-  VState.vPQRi_i = vRates;
-  VState.vPQRi = Ti2b * VState.vPQRi_i;
+  VState.vPQRi = Ti2b * vRates;
   VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth;
 }
 
@@ -637,7 +676,7 @@ void FGPropagate::InitializeDerivatives(void)
   VState.dqQtrndot.clear();
   for (int i=0; i<4; i++) {
     VState.dqPQRidot.push_front(vPQRidot);
-    VState.dqUVWidot.push_front(vUVWdot);
+    VState.dqUVWidot.push_front(vUVWidot);
     VState.dqInertialVelocity.push_front(VState.vInertialVelocity);
     VState.dqQtrndot.push_front(vQtrndot);
   }
@@ -673,47 +712,55 @@ double FGPropagate::GetTerrainElevation(void) const
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-//Todo: when should this be called - when should the new EPA be used to
-// calculate the transformation matrix, so that the matrix is not a step
-// ahead of the sim and the associated calculations?
-const FGMatrix33& FGPropagate::GetTi2ec(void)
-{
-  return VState.vLocation.GetTi2ec();
-}
 
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-const FGMatrix33& FGPropagate::GetTec2i(void)
+double FGPropagate::GetDistanceAGL(void) const
 {
-  return VState.vLocation.GetTec2i();
+  return VState.vLocation.GetRadius() - LocalTerrainRadius;
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-void FGPropagate::SetAltitudeASL(double altASL)
+void FGPropagate::SetVState(const VehicleState& vstate)
 {
-  VState.vLocation.SetRadius( altASL + SeaLevelRadius );
-}
-
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+  VState.vLocation = vstate.vLocation;
+  VState.vLocation.SetEarthPositionAngle(FDMExec->GetInertial()->GetEarthPositionAngle());
+  Ti2ec = VState.vLocation.GetTi2ec(); // useless ?
+  Tec2i = Ti2ec.Transposed();
+  UpdateLocationMatrices();
+  SetInertialOrientation(vstate.qAttitudeECI);
+  RecomputeLocalTerrainRadius();
+  VehicleRadius = GetRadius();
+  VState.vUVW = vstate.vUVW;
+  vVel = Tb2l * VState.vUVW;
+  VState.vPQR = vstate.vPQR;
+  VState.vPQRi = VState.vPQR + Ti2b * vOmegaEarth;
+  VState.vInertialPosition = vstate.vInertialPosition;
 
-double FGPropagate::GetLocalTerrainRadius(void) const
-{
-  return LocalTerrainRadius;
+  InitializeDerivatives();
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-double FGPropagate::GetDistanceAGL(void) const
+void FGPropagate::UpdateVehicleState(void)
 {
-  return VState.vLocation.GetRadius() - LocalTerrainRadius;
+  RecomputeLocalTerrainRadius();
+  VehicleRadius = GetRadius();
+  VState.vInertialPosition = Tec2i * VState.vLocation;
+  UpdateLocationMatrices();
+  UpdateBodyMatrices();
+  vVel = Tb2l * VState.vUVW;
+  VState.qAttitudeLocal = Tl2b.GetQuaternion();
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-void FGPropagate::SetDistanceAGL(double tt)
+void FGPropagate::SetLocation(const FGLocation& l)
 {
-  VState.vLocation.SetRadius( tt + LocalTerrainRadius );
+  VState.vLocation = l;
+  VState.vLocation.SetEarthPositionAngle(FDMExec->GetInertial()->GetEarthPositionAngle());
+  Ti2ec = VState.vLocation.GetTi2ec(); // useless ?
+  Tec2i = Ti2ec.Transposed();
+  UpdateVehicleState();
 }
 
 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -741,7 +788,7 @@ void FGPropagate::DumpState(void)
   cout << endl << "  " << underon
        <<   "Velocity" << underoff << endl;
   cout << "    ECI:   " << VState.vInertialVelocity.Dump(", ") << " (x,y,z in ft/s)" << endl;
-  cout << "    ECEF:  " << (GetTb2ec() * VState.vUVW).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;