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 General Public License as published by the Free Software
+ the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
- You should have received a copy of the GNU General Public License along with
+ You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
- Further information about the GNU General Public License can also be found on
+ Further information about the GNU Lesser General Public License can also be found on
the world wide web at http://www.gnu.org.
FUNCTIONAL DESCRIPTION
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 "FGGroundReactions.h"
+#include "FGFDMExec.h"
#include "FGAircraft.h"
#include "FGMassBalance.h"
#include "FGInertial.h"
-#include <input_output/FGPropertyManager.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.55 2010/07/09 04:11:45 jberndt Exp $";
static const char *IdHdr = ID_PROPAGATE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex)
{
+ Debug(0);
Name = "FGPropagate";
+ gravType = gtWGS84;
+
+ vPQRdot.InitMatrix();
+ vQtrndot = FGQuaternion(0,0,0);
+ vUVWdot.InitMatrix();
+ vInertialVelocity.InitMatrix();
+
+ integrator_rotational_rate = eAdamsBashforth2;
+ integrator_translational_rate = eTrapezoidal;
+ integrator_rotational_position = eAdamsBashforth2;
+ integrator_translational_position = eTrapezoidal;
+
+ VState.dqPQRdot.resize(4, FGColumnVector3(0.0,0.0,0.0));
+ VState.dqUVWdot.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);
FGPropagate::~FGPropagate(void)
{
- unbind();
Debug(1);
}
bool FGPropagate::InitModel(void)
{
- FGModel::InitModel();
+ if (!FGModel::InitModel()) return false;
+
+ // For initialization ONLY:
+ SeaLevelRadius = LocalTerrainRadius = Inertial->GetRefRadius();
+
+ VState.vLocation.SetRadius( LocalTerrainRadius + 4.0 );
+ VState.vLocation.SetEllipse(Inertial->GetSemimajor(), Inertial->GetSemiminor());
+ vOmegaEarth = FGColumnVector3( 0.0, 0.0, Inertial->omega() ); // Earth rotation vector
- SeaLevelRadius = Inertial->RefRadius(); // For initialization ONLY
- RunwayRadius = SeaLevelRadius;
+ vPQRdot.InitMatrix();
+ vQtrndot = FGQuaternion(0,0,0);
+ vUVWdot.InitMatrix();
+ vInertialVelocity.InitMatrix();
- VState.vLocation.SetRadius( SeaLevelRadius + 4.0 );
+ VState.dqPQRdot.resize(4, FGColumnVector3(0.0,0.0,0.0));
+ VState.dqUVWdot.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));
+
+ integrator_rotational_rate = eAdamsBashforth2;
+ integrator_translational_rate = eTrapezoidal;
+ integrator_rotational_position = eAdamsBashforth2;
+ integrator_translational_position = eTrapezoidal;
return true;
}
void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
{
- SeaLevelRadius = FGIC->GetSeaLevelRadiusFtIC();
- RunwayRadius = SeaLevelRadius;
+ SetSeaLevelRadius(FGIC->GetSeaLevelRadiusFtIC());
+ SetTerrainElevation(FGIC->GetTerrainElevationFtIC());
+
+ VehicleRadius = GetRadius();
+ radInv = 1.0/VehicleRadius;
+
+ // 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.SetPosition( FGIC->GetLongitudeRadIC(),
+ FGIC->GetLatitudeRadIC(),
+ FGIC->GetAltitudeASLFtIC() + FGIC->GetSeaLevelRadiusFtIC() );
+
+ VState.vLocation.SetEarthPositionAngle(Inertial->GetEarthPositionAngle());
+
+ Ti2ec = GetTi2ec(); // ECI to ECEF transform
+ Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
+
+ Tl2ec = GetTl2ec(); // local to ECEF transform
+ Tec2l = Tl2ec.Transposed(); // ECEF to local frame transform
+
+ Ti2l = GetTi2l();
+ Tl2i = Ti2l.Transposed();
+
+ // 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 = FGQuaternion( FGIC->GetPhiRadIC(),
+ FGIC->GetThetaRadIC(),
+ FGIC->GetPsiRadIC() );
+
+ VState.qAttitudeECI = Ti2l.GetQuaternion()*VState.qAttitudeLocal;
+
+ Ti2b = GetTi2b(); // ECI to body frame transform
+ Tb2i = Ti2b.Transposed(); // body to ECI frame transform
- // Set the Orientation from the euler angles
- VState.vQtrn = FGQuaternion( FGIC->GetPhiRadIC(),
- FGIC->GetThetaRadIC(),
- FGIC->GetPsiRadIC() );
+ Tl2b = VState.qAttitudeLocal; // 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
// Set the velocities in the instantaneus body frame
VState.vUVW = FGColumnVector3( FGIC->GetUBodyFpsIC(),
- FGIC->GetVBodyFpsIC(),
- FGIC->GetWBodyFpsIC() );
+ FGIC->GetVBodyFpsIC(),
+ FGIC->GetWBodyFpsIC() );
- // Set the angular velocities in the instantaneus body frame.
- VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
- FGIC->GetQRadpsIC(),
- FGIC->GetRRadpsIC() );
+ VState.vInertialPosition = Tec2i * VState.vLocation;
+
+ // Compute the local frame ECEF velocity
+ vVel = Tb2l * VState.vUVW;
+
+ // Refer to Stevens and Lewis, 1.5-14a, pg. 49.
+ // This is the rotation rate of the "Local" frame, expressed in the local frame.
- // Compute some derived values.
- vVel = VState.vQtrn.GetTInv()*VState.vUVW;
+ FGColumnVector3 vOmegaLocal = FGColumnVector3(
+ radInv*vVel(eEast),
+ -radInv*vVel(eNorth),
+ -radInv*vVel(eEast)*VState.vLocation.GetTanLatitude() );
- // Finally, make sure that the quaternion stays normalized.
- VState.vQtrn.Normalize();
+ // 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
+ VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
+ FGIC->GetQRadpsIC(),
+ FGIC->GetRRadpsIC() ) + Tl2b*vOmegaLocal;
+
+ VState.vPQRi = VState.vPQR + Ti2b * vOmegaEarth;
+
+ // Make an initial run and set past values
+ CalculatePQRdot(); // Angular rate derivative
+ CalculateUVWdot(); // Translational rate derivative
+ CalculateQuatdot(); // Angular orientation derivative
+ CalculateInertialVelocity(); // Translational position derivative
+
+ // Initialize past values deques
+ VState.dqPQRdot.clear();
+ VState.dqUVWdot.clear();
+ VState.dqInertialVelocity.clear();
+ VState.dqQtrndot.clear();
+ for (int i=0; i<4; i++) {
+ VState.dqPQRdot.push_front(vPQRdot);
+ VState.dqUVWdot.push_front(vUVWdot);
+ VState.dqInertialVelocity.push_front(VState.vInertialVelocity);
+ VState.dqQtrndot.push_front(vQtrndot);
+ }
- // Recompute the RunwayRadius level.
- RecomputeRunwayRadius();
+ // Recompute the LocalTerrainRadius.
+ RecomputeLocalTerrainRadius();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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)
{
+static int ctr;
+
if (FGModel::Run()) return true; // Fast return if we have nothing to do ...
if (FDMExec->Holding()) return false;
- RecomputeRunwayRadius();
+ double dt = FDMExec->GetDeltaT()*rate; // The 'stepsize'
+
+ RunPreFunctions();
+
+ // Calculate state derivatives
+ CalculatePQRdot(); // Angular rate derivative
+ CalculateUVWdot(); // Translational rate derivative
+ CalculateQuatdot(); // Angular orientation derivative
+ CalculateInertialVelocity(); // Translational position derivative
+
+ // Propagate rotational / translational velocity, angular /translational position, respectively.
+ Integrate(VState.vPQRi, vPQRdot, VState.dqPQRdot, dt, integrator_rotational_rate);
+ Integrate(VState.vUVW, vUVWdot, VState.dqUVWdot, dt, integrator_translational_rate);
+ Integrate(VState.qAttitudeECI, vQtrndot, VState.dqQtrndot, dt, integrator_rotational_position);
+ Integrate(VState.vInertialPosition, VState.vInertialVelocity, VState.dqInertialVelocity, dt, integrator_translational_position);
- 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
+ VState.qAttitudeECI.Normalize(); // Normalize the ECI Attitude quaternion
- 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) );
+ VState.vLocation.SetEarthPositionAngle(Inertial->GetEarthPositionAngle()); // Update the Earth position angle (EPA)
- // 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
+ // Update the "Location-based" transformation matrices from the vLocation vector.
- // Inertial angular velocity measured in the body frame.
- const FGColumnVector3 pqri = VState.vPQR + Tl2b*(Tec2l*omega);
+ Ti2ec = GetTi2ec(); // ECI to ECEF transform
+ Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
+ Tl2ec = GetTl2ec(); // local to ECEF transform
+ Tec2l = Tl2ec.Transposed(); // ECEF to local frame transform
+ Ti2l = GetTi2l();
+ Tl2i = Ti2l.Transposed();
- // Compute vehicle velocity wrt EC frame, expressed in Local horizontal frame.
+ // Update the "Orientation-based" transformation matrices from the orientation quaternion
+
+ 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
+
+ // Set auxililary state variables
+ VState.vLocation = Ti2ec*VState.vInertialPosition;
+ RecomputeLocalTerrainRadius();
+
+ // Calculate current aircraft radius from center of planet
+ VehicleRadius = VState.vInertialPosition.Magnitude();
+ radInv = 1.0/VehicleRadius;
+
+ VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth;
+
+ 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:
+ RunPostFunctions();
- // Compute body frame rotational accelerations based on the current body moments
- vPQRdot = Jinv*(vMoments - pqri*(J*pqri));
+ Debug(2);
+ return false;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Compute body frame rotational accelerations based on the current body moments
+//
+// vPQRdot is the derivative of the absolute angular velocity of the vehicle
+// (body rate with respect to the inertial frame), expressed in the body frame,
+// where the derivative is taken in the body frame.
+// J is the inertia matrix
+// Jinv is the inverse inertia matrix
+// vMoments is the moment vector in the body frame
+// VState.vPQRi is the total inertial angular velocity of the vehicle
+// expressed in the body frame.
+// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
+// Second edition (2004), eqn 1.5-16e (page 50)
+
+void FGPropagate::CalculatePQRdot(void)
+{
+ const FGColumnVector3& vMoments = Aircraft->GetMoments(); // current moments
+ const FGMatrix33& J = MassBalance->GetJ(); // inertia matrix
+ const FGMatrix33& Jinv = MassBalance->GetJinv(); // inertia matrix inverse
- // Compute body frame accelerations based on the current body forces
- vUVWdot = VState.vUVW*VState.vPQR + vForces/mass;
+ // Compute body frame rotational accelerations based on the current body
+ // moments and the total inertial angular velocity expressed in the body
+ // frame.
- // Coriolis acceleration.
- FGColumnVector3 ecVel = Tl2ec*vVel;
- FGColumnVector3 ace = 2.0*omega*ecVel;
- vUVWdot -= Tl2b*(Tec2l*ace);
+ vPQRdot = Jinv*(vMoments - VState.vPQRi*(J*VState.vPQRi));
+}
- if (!GroundReactions->GetWOW()) {
- // Centrifugal acceleration.
- FGColumnVector3 aeec = omega*(omega*VState.vLocation);
- vUVWdot -= Tl2b*(Tec2l*aeec);
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// Compute the quaternion orientation derivative
+//
+// vQtrndot is the quaternion derivative.
+// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
+// Second edition (2004), eqn 1.5-16b (page 50)
+
+void FGPropagate::CalculateQuatdot(void)
+{
+ // Compute quaternion orientation derivative on current body rates
+ vQtrndot = VState.qAttitudeECI.GetQDot( VState.vPQRi);
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+// This set of calculations results in the body frame accelerations being
+// computed.
+// Compute body frame accelerations based on the current body forces.
+// Include centripetal and coriolis accelerations.
+// vOmegaEarth is the Earth angular rate - expressed in the inertial frame -
+// so it has to be transformed to the body frame. More completely,
+// vOmegaEarth is the rate of the ECEF frame relative to the Inertial
+// frame (ECI), expressed in the Inertial frame.
+// vForces is the total force on the vehicle in the body frame.
+// VState.vPQR is the vehicle body rate relative to the ECEF frame, expressed
+// in the body frame.
+// VState.vUVW is the vehicle velocity relative to the ECEF frame, expressed
+// in the body frame.
+// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
+// Second edition (2004), eqns 1.5-13 (pg 48) and 1.5-16d (page 50)
+
+void FGPropagate::CalculateUVWdot(void)
+{
+ double mass = MassBalance->GetMass(); // mass
+ const FGColumnVector3& vForces = Aircraft->GetForces(); // current forces
+
+ vUVWdot = vForces/mass - (VState.vPQR + 2.0*(Ti2b *vOmegaEarth)) * VState.vUVW;
+
+ // Include Centripetal acceleration.
+ if (!GroundReactions->GetWOW() && Aircraft->GetHoldDown() == 0) {
+ vUVWdot -= Ti2b * (vOmegaEarth*(vOmegaEarth*VState.vInertialPosition));
}
- // Gravitation accel
- vUVWdot += Tl2b*gAccel;
+ // Include Gravitation accel
+ switch (gravType) {
+ case gtStandard:
+ vGravAccel = Tl2b * FGColumnVector3( 0.0, 0.0, Inertial->GetGAccel(VehicleRadius) );
+ break;
+ case gtWGS84:
+ vGravAccel = Tec2b * Inertial->GetGravityJ2(VState.vLocation);
+ break;
+ }
- // Compute vehicle velocity wrt EC frame, expressed in EC frame
- FGColumnVector3 vLocationDot = Tl2ec * vVel;
+ vUVWdot += vGravAccel;
+}
- FGColumnVector3 omegaLocal( rInv*vVel(eEast),
- -rInv*vVel(eNorth),
- -rInv*vVel(eEast)*VState.vLocation.GetTanLatitude() );
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ // 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 quaternion orientation derivative on current body rates
- FGQuaternion vQtrndot = VState.vQtrn.GetQDot( VState.vPQR - Tl2b*omegaLocal );
+void FGPropagate::CalculateInertialVelocity(void)
+{
+ VState.vInertialVelocity = Tb2i * VState.vUVW + (vOmegaEarth * VState.vInertialPosition);
+}
- // Propagate velocities
- VState.vPQR += dt*vPQRdot;
- VState.vUVW += dt*vUVWdot;
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
- // Propagate positions
- VState.vQtrn += dt*vQtrndot;
- VState.vLocation += dt*vLocationDot;
+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;
+ }
+}
- return false;
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+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 eNone: // do nothing, freeze translational rate
+ break;
+ }
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-void FGPropagate::RecomputeRunwayRadius(void)
+void FGPropagate::SetInertialOrientation(FGQuaternion Qi) {
+ VState.qAttitudeECI = Qi;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropagate::SetInertialVelocity(FGColumnVector3 Vi) {
+ VState.vInertialVelocity = Vi;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropagate::RecomputeLocalTerrainRadius(void)
{
- // 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 t = FDMExec->GetSimTime();
+
+ // Get the LocalTerrain radius.
+// FDMExec->GetGroundCallback()->GetAGLevel(t, VState.vLocation, contactloc, dv, dv);
+// LocalTerrainRadius = contactloc.GetRadius();
+ LocalTerrainRadius = FDMExec->GetGroundCallback()->GetTerrainGeoCentRadius();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-void FGPropagate::Seth(double tt)
+void FGPropagate::SetTerrainElevation(double terrainElev)
{
- VState.vLocation.SetRadius( tt + SeaLevelRadius );
+ LocalTerrainRadius = terrainElev + SeaLevelRadius;
+ FDMExec->GetGroundCallback()->SetTerrainGeoCentRadius(LocalTerrainRadius);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-double FGPropagate::GetRunwayRadius(void) const
+double FGPropagate::GetTerrainElevation(void) const
+{
+ return FDMExec->GetGroundCallback()->GetTerrainGeoCentRadius()-SeaLevelRadius;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+//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 RunwayRadius;
+ return VState.vLocation.GetTi2ec();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+const FGMatrix33& FGPropagate::GetTec2i(void)
+{
+ return VState.vLocation.GetTec2i();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropagate::SetAltitudeASL(double altASL)
+{
+ VState.vLocation.SetRadius( altASL + SeaLevelRadius );
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+double FGPropagate::GetLocalTerrainRadius(void) const
+{
+ return LocalTerrainRadius;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGPropagate::GetDistanceAGL(void) const
{
- return VState.vLocation.GetRadius() - RunwayRadius;
+ return VState.vLocation.GetRadius() - LocalTerrainRadius;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::SetDistanceAGL(double tt)
{
- VState.vLocation.SetRadius( tt + RunwayRadius );
+ VState.vLocation.SetRadius( tt + LocalTerrainRadius );
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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);
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("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("accelerations/pdot-rad_sec2", this, eP, (PMF)&FGPropagate::GetPQRdot);
+ PropertyManager->Tie("accelerations/qdot-rad_sec2", this, eQ, (PMF)&FGPropagate::GetPQRdot);
+ PropertyManager->Tie("accelerations/rdot-rad_sec2", 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("accelerations/udot-ft_sec2", this, eU, (PMF)&FGPropagate::GetUVWdot);
+ PropertyManager->Tie("accelerations/vdot-ft_sec2", this, eV, (PMF)&FGPropagate::GetUVWdot);
+ PropertyManager->Tie("accelerations/wdot-ft_sec2", this, eW, (PMF)&FGPropagate::GetUVWdot);
- PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::Geth, &FGPropagate::Seth, true);
+ 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);
PropertyManager->Tie("position/long-gc-rad", this, &FGPropagate::GetLongitude, &FGPropagate::SetLongitude);
+ PropertyManager->Tie("position/lat-gc-deg", this, &FGPropagate::GetLatitudeDeg, &FGPropagate::SetLatitudeDeg);
+ PropertyManager->Tie("position/long-gc-deg", this, &FGPropagate::GetLongitudeDeg, &FGPropagate::SetLongitudeDeg);
+ 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("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);
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);
+ PropertyManager->Tie("simulation/gravity-model", &gravType);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
}
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
+ << Inertial->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