JSBSim Flight Dynamics Model  1.2.0 (05 Nov 2023)
An Open Source Flight Dynamics and Control Software Library in C++
FGPropagate.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGPropagate.cpp
4  Author: Jon S. Berndt
5  Date started: 01/05/99
6  Purpose: Integrate the EOM to determine instantaneous position
7  Called by: FGFDMExec
8 
9  ------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
10 
11  This program is free software; you can redistribute it and/or modify it under
12  the terms of the GNU Lesser General Public License as published by the Free
13  Software Foundation; either version 2 of the License, or (at your option) any
14  later version.
15 
16  This program is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19  details.
20 
21  You should have received a copy of the GNU Lesser General Public License along
22  with this program; if not, write to the Free Software Foundation, Inc., 59
23  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 
25  Further information about the GNU Lesser General Public License can also be
26  found on the world wide web at http://www.gnu.org.
27 
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 This class encapsulates the integration of rates and accelerations to get the
31 current position of the aircraft.
32 
33 HISTORY
34 --------------------------------------------------------------------------------
35 01/05/99 JSB Created
36 
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 COMMENTS, REFERENCES, and NOTES
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 [1] Cooke, Zyda, Pratt, and McGhee, "NPSNET: Flight Simulation Dynamic Modeling
41  Using Quaternions", Presence, Vol. 1, No. 4, pp. 404-420 Naval Postgraduate
42  School, January 1994
43 [2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
44  JSC 12960, July 1977
45 [3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
46  NASA-Ames", NASA CR-2497, January 1975
47 [4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
48  Wiley & Sons, 1979 ISBN 0-471-03032-5
49 [5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
50  1982 ISBN 0-471-08936-2
51 [6] S. Buss, "Accurate and Efficient Simulation of Rigid Body Rotations",
52  Technical Report, Department of Mathematics, University of California,
53  San Diego, 1999
54 [7] Barker L.E., Bowles R.L. and Williams L.H., "Development and Application of
55  a Local Linearization Algorithm for the Integration of Quaternion Rate
56  Equations in Real-Time Flight Simulation Problems", NASA TN D-7347,
57  December 1973
58 [8] Phillips W.F, Hailey C.E and Gebert G.A, "Review of Attitude Representations
59  Used for Aircraft Kinematics", Journal Of Aircraft Vol. 38, No. 4,
60  July-August 2001
61 
62 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63 INCLUDES
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
65 
66 #include <iomanip>
67 
68 #include "FGPropagate.h"
69 #include "initialization/FGInitialCondition.h"
70 #include "FGFDMExec.h"
71 #include "simgear/io/iostreams/sgstream.hxx"
72 #include "FGInertial.h"
73 
74 using namespace std;
75 
76 namespace JSBSim {
77 
78 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 CLASS IMPLEMENTATION
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
81 
82 FGPropagate::FGPropagate(FGFDMExec* fdmex)
83  : FGModel(fdmex)
84 {
85  Debug(0);
86  Name = "FGPropagate";
87 
88  Inertial = FDMExec->GetInertial();
89 
91  // eNone = 0, eRectEuler, eTrapezoidal, eAdamsBashforth2, eAdamsBashforth3, eAdamsBashforth4};
92 
93  integrator_rotational_rate = eRectEuler;
94  integrator_translational_rate = eAdamsBashforth2;
95  integrator_rotational_position = eRectEuler;
96  integrator_translational_position = eAdamsBashforth3;
97 
98  VState.dqPQRidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
99  VState.dqUVWidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
100  VState.dqInertialVelocity.resize(5, FGColumnVector3(0.0,0.0,0.0));
101  VState.dqQtrndot.resize(5, FGQuaternion(0.0,0.0,0.0));
102 
103  epa = 0.0;
104 
105  bind();
106  Debug(0);
107 }
108 
109 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
110 
112 {
113  Debug(1);
114 }
115 
116 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117 
119 {
120  if (!FGModel::InitModel()) return false;
121 
122  // For initialization ONLY:
123  VState.vLocation.SetEllipse(in.SemiMajor, in.SemiMinor);
124  Inertial->SetAltitudeAGL(VState.vLocation, 4.0);
125 
126  VState.dqPQRidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
127  VState.dqUVWidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
128  VState.dqInertialVelocity.resize(5, FGColumnVector3(0.0,0.0,0.0));
129  VState.dqQtrndot.resize(5, FGColumnVector3(0.0,0.0,0.0));
130 
131  integrator_rotational_rate = eRectEuler;
132  integrator_translational_rate = eAdamsBashforth2;
133  integrator_rotational_position = eRectEuler;
134  integrator_translational_position = eAdamsBashforth3;
135 
136  epa = 0.0;
137 
138  return true;
139 }
140 
141 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 
143 void FGPropagate::SetInitialState(const FGInitialCondition* FGIC)
144 {
145  // Initialize the State Vector elements and the transformation matrices
146 
147  // Set the position lat/lon/radius
148  VState.vLocation = FGIC->GetPosition();
149 
150  epa = FGIC->GetEarthPositionAngleIC();
151  Ti2ec = { cos(epa), sin(epa), 0.0,
152  -sin(epa), cos(epa), 0.0,
153  0.0, 0.0, 1.0 };
154  Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
155 
156  VState.vInertialPosition = Tec2i * VState.vLocation;
157 
158  UpdateLocationMatrices();
159 
160  // Set the orientation from the euler angles (is normalized within the
161  // constructor). The Euler angles represent the orientation of the body
162  // frame relative to the local frame.
163  VState.qAttitudeLocal = FGIC->GetOrientation();
164 
165  VState.qAttitudeECI = Ti2l.GetQuaternion()*VState.qAttitudeLocal;
166  UpdateBodyMatrices();
167 
168  // Set the velocities in the instantaneus body frame
169  VState.vUVW = FGIC->GetUVWFpsIC();
170 
171  // Compute the local frame ECEF velocity
172  vVel = Tb2l * VState.vUVW;
173 
174  // Compute local terrain velocity
175  RecomputeLocalTerrainVelocity();
176 
177  // Set the angular velocities of the body frame relative to the ECEF frame,
178  // expressed in the body frame.
179  VState.vPQR = FGIC->GetPQRRadpsIC();
180 
181  VState.vPQRi = VState.vPQR + Ti2b * in.vOmegaPlanet;
182 
183  CalculateInertialVelocity(); // Translational position derivative
184  CalculateQuatdot(); // Angular orientation derivative
185 }
186 
187 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188 // Initialize the past value deques
189 
190 void FGPropagate::InitializeDerivatives()
191 {
192  VState.dqPQRidot.assign(5, in.vPQRidot);
193  VState.dqUVWidot.assign(5, in.vUVWidot);
194  VState.dqInertialVelocity.assign(5, VState.vInertialVelocity);
195  VState.dqQtrndot.assign(5, VState.vQtrndot);
196 }
197 
198 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
199 /*
200 Purpose: Called on a schedule to perform EOM integration
201 Notes: [JB] Run in standalone mode, SeaLevelRadius will be reference radius.
202  In FGFS, SeaLevelRadius is stuffed from FGJSBSim in JSBSim.cxx each pass.
203 
204 At the top of this Run() function, see several "shortcuts" (or, aliases) being
205 set up for use later, rather than using the longer class->function() notation.
206 
207 This propagation is done using the current state values
208 and current derivatives. Based on these values we compute an approximation to the
209 state values for (now + dt).
210 
211 In the code below, variables named beginning with a small "v" refer to a
212 a column vector, variables named beginning with a "T" refer to a transformation
213 matrix. ECEF refers to Earth Centered Earth Fixed. ECI refers to Earth Centered
214 Inertial.
215 
216 */
217 
218 bool FGPropagate::Run(bool Holding)
219 {
220  if (FGModel::Run(Holding)) return true; // Fast return if we have nothing to do ...
221  if (Holding) return false;
222 
223  double dt = in.DeltaT * rate; // The 'stepsize'
224 
225  // Propagate rotational / translational velocity, angular /translational position, respectively.
226 
227  if (!FDMExec->IntegrationSuspended()) {
228  Integrate(VState.qAttitudeECI, VState.vQtrndot, VState.dqQtrndot, dt, integrator_rotational_position);
229  Integrate(VState.vPQRi, in.vPQRidot, VState.dqPQRidot, dt, integrator_rotational_rate);
230  Integrate(VState.vInertialPosition, VState.vInertialVelocity, VState.dqInertialVelocity, dt, integrator_translational_position);
231  Integrate(VState.vInertialVelocity, in.vUVWidot, VState.dqUVWidot, dt, integrator_translational_rate);
232  }
233 
234  // CAUTION : the order of the operations below is very important to get
235  // transformation matrices that are consistent with the new state of the
236  // vehicle
237 
238  // 1. Update the Earth position angle (EPA)
239  epa += in.vOmegaPlanet(eZ)*dt;
240 
241  // 2. Update the Ti2ec and Tec2i transforms from the updated EPA
242  double cos_epa = cos(epa);
243  double sin_epa = sin(epa);
244  Ti2ec = { cos_epa, sin_epa, 0.0,
245  -sin_epa, cos_epa, 0.0,
246  0.0, 0.0, 1.0 };
247  Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
248 
249  // 3. Update the location from the updated Ti2ec and inertial position
250  VState.vLocation = Ti2ec*VState.vInertialPosition;
251 
252  // 4. Update the other "Location-based" transformation matrices from the
253  // updated vLocation vector.
254  UpdateLocationMatrices();
255 
256  // 5. Update the "Orientation-based" transformation matrices from the updated
257  // orientation quaternion and vLocation vector.
258  UpdateBodyMatrices();
259 
260  // Translational position derivative (velocities are integrated in the
261  // inertial frame)
262  CalculateUVW();
263 
264  // Set auxilliary state variables
265  RecomputeLocalTerrainVelocity();
266 
267  VState.vPQR = VState.vPQRi - Ti2b * in.vOmegaPlanet;
268 
269  // Angular orientation derivative
270  CalculateQuatdot();
271 
272  VState.qAttitudeLocal = Tl2b.GetQuaternion();
273 
274  // Compute vehicle velocity wrt ECEF frame, expressed in Local horizontal
275  // frame.
276  vVel = Tb2l * VState.vUVW;
277 
278  // Compute orbital parameters in the inertial frame
279  ComputeOrbitalParameters();
280 
281  Debug(2);
282  return false;
283 }
284 
285 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286 
288 {
289  if (hd) {
290  VState.vUVW.InitMatrix();
291  CalculateInertialVelocity();
292  VState.vPQR.InitMatrix();
293  VState.vPQRi = Ti2b * in.vOmegaPlanet;
294  CalculateQuatdot();
295  InitializeDerivatives();
296  }
297 }
298 
299 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300 // Compute the quaternion orientation derivative
301 //
302 // vQtrndot is the quaternion derivative.
303 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
304 // Second edition (2004), eqn 1.5-16b (page 50)
305 
306 void FGPropagate::CalculateQuatdot(void)
307 {
308  // Compute quaternion orientation derivative on current body rates
309  VState.vQtrndot = VState.qAttitudeECI.GetQDot(VState.vPQRi);
310 }
311 
312 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313  // Transform the velocity vector of the body relative to the origin (Earth
314  // center) to be expressed in the inertial frame, and add the vehicle velocity
315  // contribution due to the rotation of the planet.
316  // Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
317  // Second edition (2004), eqn 1.5-16c (page 50)
318 
319 void FGPropagate::CalculateInertialVelocity(void)
320 {
321  VState.vInertialVelocity = Tb2i * VState.vUVW + (in.vOmegaPlanet * VState.vInertialPosition);
322 }
323 
324 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
325  // Transform the velocity vector of the inertial frame to be expressed in the
326  // body frame relative to the origin (Earth center), and substract the vehicle
327  // velocity contribution due to the rotation of the planet.
328 
329 void FGPropagate::CalculateUVW(void)
330 {
331  VState.vUVW = Ti2b * (VState.vInertialVelocity - (in.vOmegaPlanet * VState.vInertialPosition));
332 }
333 
334 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 
336 void FGPropagate::Integrate( FGColumnVector3& Integrand,
337  FGColumnVector3& Val,
338  deque <FGColumnVector3>& ValDot,
339  double dt,
340  eIntegrateType integration_type)
341 {
342  ValDot.push_front(Val);
343  ValDot.pop_back();
344 
345  switch(integration_type) {
346  case eRectEuler: Integrand += dt*ValDot[0];
347  break;
348  case eTrapezoidal: Integrand += 0.5*dt*(ValDot[0] + ValDot[1]);
349  break;
350  case eAdamsBashforth2: Integrand += dt*(1.5*ValDot[0] - 0.5*ValDot[1]);
351  break;
352  case eAdamsBashforth3: Integrand += (1/12.0)*dt*(23.0*ValDot[0] - 16.0*ValDot[1] + 5.0*ValDot[2]);
353  break;
354  case eAdamsBashforth4: Integrand += (1/24.0)*dt*(55.0*ValDot[0] - 59.0*ValDot[1] + 37.0*ValDot[2] - 9.0*ValDot[3]);
355  break;
356  case eAdamsBashforth5: Integrand += dt*((1901./720.)*ValDot[0] - (1387./360.)*ValDot[1] + (109./30.)*ValDot[2] - (637./360.)*ValDot[3] + (251./720.)*ValDot[4]);
357  break;
358  case eNone: // do nothing, freeze translational rate
359  break;
360  case eBuss1:
361  case eBuss2:
362  case eLocalLinearization:
363  throw("Can only use Buss (1 & 2) or local linearization integration methods in for rotational position!");
364  default:
365  break;
366  }
367 }
368 
369 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370 
371 void FGPropagate::Integrate( FGQuaternion& Integrand,
372  FGQuaternion& Val,
373  deque <FGQuaternion>& ValDot,
374  double dt,
375  eIntegrateType integration_type)
376 {
377  ValDot.push_front(Val);
378  ValDot.pop_back();
379 
380  switch(integration_type) {
381  case eRectEuler: Integrand += dt*ValDot[0];
382  break;
383  case eTrapezoidal: Integrand += 0.5*dt*(ValDot[0] + ValDot[1]);
384  break;
385  case eAdamsBashforth2: Integrand += dt*(1.5*ValDot[0] - 0.5*ValDot[1]);
386  break;
387  case eAdamsBashforth3: Integrand += (1/12.0)*dt*(23.0*ValDot[0] - 16.0*ValDot[1] + 5.0*ValDot[2]);
388  break;
389  case eAdamsBashforth4: Integrand += (1/24.0)*dt*(55.0*ValDot[0] - 59.0*ValDot[1] + 37.0*ValDot[2] - 9.0*ValDot[3]);
390  break;
391  case eAdamsBashforth5: Integrand += dt*((1901./720.)*ValDot[0] - (1387./360.)*ValDot[1] + (109./30.)*ValDot[2] - (637./360.)*ValDot[3] + (251./720.)*ValDot[4]);
392  break;
393  case eBuss1:
394  {
395  // This is the first order method as described in Samuel R. Buss paper[6].
396  // The formula from Buss' paper is transposed below to quaternions and is
397  // actually the exact solution of the quaternion differential equation
398  // qdot = 1/2*w*q when w is constant.
399  Integrand = Integrand * QExp(0.5 * dt * VState.vPQRi);
400  }
401  return; // No need to normalize since the quaternion exponential is always normal
402  case eBuss2:
403  {
404  // This is the 'augmented second-order method' from S.R. Buss paper [6].
405  // Unlike Runge-Kutta or Adams-Bashforth, it is a one-pass second-order
406  // method (see reference [6]).
407  FGColumnVector3 wi = VState.vPQRi;
408  FGColumnVector3 wdoti = in.vPQRidot;
409  FGColumnVector3 omega = wi + 0.5*dt*wdoti + dt*dt/12.*wdoti*wi;
410  Integrand = Integrand * QExp(0.5 * dt * omega);
411  }
412  return; // No need to normalize since the quaternion exponential is always normal
413  case eLocalLinearization:
414  {
415  // This is the local linearization algorithm of Barker et al. (see ref. [7])
416  // It is also a one-pass second-order method. The code below is based on the
417  // more compact formulation issued from equation (107) of ref. [8]. The
418  // constants C1, C2, C3 and C4 have the same value than those in ref. [7] pp. 11
419  FGColumnVector3 wi = 0.5 * VState.vPQRi;
420  FGColumnVector3 wdoti = 0.5 * in.vPQRidot;
421  double omegak2 = DotProduct(VState.vPQRi, VState.vPQRi);
422  double omegak = omegak2 > 1E-6 ? sqrt(omegak2) : 1E-6;
423  double rhok = 0.5 * dt * omegak;
424  double C1 = cos(rhok);
425  double C2 = 2.0 * sin(rhok) / omegak;
426  double C3 = 4.0 * (1.0 - C1) / (omegak*omegak);
427  double C4 = 4.0 * (dt - C2) / (omegak*omegak);
428  FGColumnVector3 Omega = C2*wi + C3*wdoti + C4*wi*wdoti;
429  FGQuaternion q;
430 
431  q(1) = C1 - C4*DotProduct(wi, wdoti);
432  q(2) = Omega(eP);
433  q(3) = Omega(eQ);
434  q(4) = Omega(eR);
435 
436  Integrand = Integrand * q;
437 
438  /* Cross check with ref. [7] pp.11-12 formulas and code pp. 20
439  double pk = VState.vPQRi(eP);
440  double qk = VState.vPQRi(eQ);
441  double rk = VState.vPQRi(eR);
442  double pdotk = in.vPQRidot(eP);
443  double qdotk = in.vPQRidot(eQ);
444  double rdotk = in.vPQRidot(eR);
445  double Ap = -0.25 * (pk*pdotk + qk*qdotk + rk*rdotk);
446  double Bp = 0.25 * (pk*qdotk - qk*pdotk);
447  double Cp = 0.25 * (pdotk*rk - pk*rdotk);
448  double Dp = 0.25 * (qk*rdotk - qdotk*rk);
449  double C2p = sin(rhok) / omegak;
450  double C3p = 2.0 * (1.0 - cos(rhok)) / (omegak*omegak);
451  double H = C1 + C4 * Ap;
452  double G = -C2p*rk - C3p*rdotk + C4*Bp;
453  double J = C2p*qk + C3p*qdotk - C4*Cp;
454  double K = C2p*pk + C3p*pdotk - C4*Dp;
455 
456  cout << "q: " << q << endl;
457 
458  // Warning! In the paper of Barker et al. the quaternion components are not
459  // ordered the same way as in JSBSim (see equations (2) and (3) of ref. [7]
460  // as well as the comment just below equation (3))
461  cout << "FORTRAN: " << H << " , " << K << " , " << J << " , " << -G << endl;*/
462  }
463  break; // The quaternion q is not normal so the normalization needs to be done.
464  case eNone: // do nothing, freeze rotational rate
465  break;
466  default:
467  break;
468  }
469 
470  Integrand.Normalize();
471 }
472 
473 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474 
475 void FGPropagate::UpdateLocationMatrices(void)
476 {
477  Tl2ec = VState.vLocation.GetTl2ec(); // local to ECEF transform
478  Tec2l = Tl2ec.Transposed(); // ECEF to local frame transform
479  Ti2l = Tec2l * Ti2ec; // ECI to local frame transform
480  Tl2i = Ti2l.Transposed(); // local to ECI transform
481 }
482 
483 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
484 
485 void FGPropagate::UpdateBodyMatrices(void)
486 {
487  Ti2b = VState.qAttitudeECI.GetT(); // ECI to body frame transform
488  Tb2i = Ti2b.Transposed(); // body to ECI frame transform
489  Tl2b = Ti2b * Tl2i; // local to body frame transform
490  Tb2l = Tl2b.Transposed(); // body to local frame transform
491  Tec2b = Ti2b * Tec2i; // ECEF to body frame transform
492  Tb2ec = Tec2b.Transposed(); // body to ECEF frame tranform
493 
494  Qec2b = Tec2b.GetQuaternion();
495 }
496 
497 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498 
499 void FGPropagate::ComputeOrbitalParameters(void)
500 {
501  const FGColumnVector3 Z{0., 0., 1.};
502  FGColumnVector3 R = VState.vInertialPosition;
503  FGColumnVector3 angularMomentum = R * VState.vInertialVelocity;
504  h = angularMomentum.Magnitude();
505  Inclination = acos(angularMomentum(eZ)/h)*radtodeg;
506  FGColumnVector3 N;
507  if (abs(Inclination) > 1E-8) {
508  N = Z * angularMomentum;
509  RightAscension = atan2(N(eY), N(eX))*radtodeg;
510  N.Normalize();
511  }
512  else {
513  RightAscension = 0.0;
514  N = {1., 0., 0.};
515  }
516  R.Normalize();
517  double vr = DotProduct(R, VState.vInertialVelocity);
518  FGColumnVector3 eVector = (VState.vInertialVelocity*angularMomentum/in.GM - R);
519  Eccentricity = eVector.Magnitude();
520  if (Eccentricity > 1E-8) {
521  eVector /= Eccentricity;
522  PerigeeArgument = acos(DotProduct(N, eVector))*radtodeg;
523  if (eVector(eZ) < 0) PerigeeArgument = 360. - PerigeeArgument;
524  }
525  else
526  {
527  eVector = {1., 0., 0.};
528  PerigeeArgument = 0.0;
529  }
530 
531  TrueAnomaly = acos(Constrain(-1.0, DotProduct(eVector, R), 1.0)) * radtodeg;
532  if (vr < 0.0) TrueAnomaly = 360. - TrueAnomaly;
533  ApoapsisRadius = h*h / (in.GM * (1-Eccentricity));
534  PeriapsisRadius = h*h / (in.GM * (1+Eccentricity));
535 
536  if (Eccentricity < 1.0) {
537  double semimajor = 0.5*(ApoapsisRadius + PeriapsisRadius);
538  OrbitalPeriod = 2.*M_PI*pow(semimajor, 1.5)/sqrt(in.GM);
539  }
540  else
541  OrbitalPeriod = 0.0;
542 }
543 
544 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
545 
546 void FGPropagate::SetInertialOrientation(const FGQuaternion& Qi)
547 {
548  VState.qAttitudeECI = Qi;
549  VState.qAttitudeECI.Normalize();
550  UpdateBodyMatrices();
551  VState.qAttitudeLocal = Tl2b.GetQuaternion();
552  CalculateQuatdot();
553 }
554 
555 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
556 
557 void FGPropagate::SetInertialVelocity(const FGColumnVector3& Vi) {
558  VState.vInertialVelocity = Vi;
559  CalculateUVW();
560  vVel = Tb2l * VState.vUVW;
561 }
562 
563 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
564 
565 void FGPropagate::SetInertialRates(const FGColumnVector3& vRates) {
566  VState.vPQRi = Ti2b * vRates;
567  VState.vPQR = VState.vPQRi - Ti2b * in.vOmegaPlanet;
568  CalculateQuatdot();
569 }
570 
571 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572 
574 {
575  return VState.vLocation.GetRadius() - VState.vLocation.GetSeaLevelRadius();
576 }
577 
578 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
579 
580 void FGPropagate::SetAltitudeASL(double altASL)
581 {
582  double slr = VState.vLocation.GetSeaLevelRadius();
583  VState.vLocation.SetRadius(slr + altASL);
584  UpdateVehicleState();
585 }
586 
587 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
588 
589 void FGPropagate::RecomputeLocalTerrainVelocity()
590 {
591  FGLocation contact;
592  FGColumnVector3 normal;
593  Inertial->GetContactPoint(VState.vLocation, contact, normal,
594  LocalTerrainVelocity, LocalTerrainAngularVelocity);
595 }
596 
597 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
598 
599 double FGPropagate::GetTerrainElevation(void) const
600 {
601  FGColumnVector3 vDummy;
602  FGLocation contact;
603  contact.SetEllipse(in.SemiMajor, in.SemiMinor);
604  Inertial->GetContactPoint(VState.vLocation, contact, vDummy, vDummy, vDummy);
605  return contact.GetGeodAltitude();
606 }
607 
608 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609 
610 void FGPropagate::SetTerrainElevation(double terrainElev)
611 {
612  Inertial->SetTerrainElevation(terrainElev);
613 }
614 
615 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
616 
618 {
619  FGLocation contact;
620  FGColumnVector3 vDummy;
621  Inertial->GetContactPoint(VState.vLocation, contact, vDummy, vDummy, vDummy);
622  return contact.GetRadius();
623 }
624 
625 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
626 
627 double FGPropagate::GetDistanceAGL(void) const
628 {
629  return Inertial->GetAltitudeAGL(VState.vLocation);
630 }
631 
632 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633 
634 double FGPropagate::GetDistanceAGLKm(void) const
635 {
636  return GetDistanceAGL()*0.0003048;
637 }
638 
639 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640 
641 void FGPropagate::SetDistanceAGL(double tt)
642 {
643  Inertial->SetAltitudeAGL(VState.vLocation, tt);
644  UpdateVehicleState();
645 }
646 
647 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648 
649 void FGPropagate::SetDistanceAGLKm(double tt)
650 {
651  SetDistanceAGL(tt*3280.8399);
652 }
653 
654 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
655 
656 void FGPropagate::SetVState(const VehicleState& vstate)
657 {
658  //ToDo: Shouldn't all of these be set from the vstate vector passed in?
659  VState.vLocation = vstate.vLocation;
660  UpdateLocationMatrices();
661  SetInertialOrientation(vstate.qAttitudeECI);
662  RecomputeLocalTerrainVelocity();
663  VState.vUVW = vstate.vUVW;
664  vVel = Tb2l * VState.vUVW;
665  VState.vPQR = vstate.vPQR;
666  VState.vPQRi = VState.vPQR + Ti2b * in.vOmegaPlanet;
667  VState.vInertialPosition = vstate.vInertialPosition;
668  CalculateQuatdot();
669 }
670 
671 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 
673 void FGPropagate::UpdateVehicleState(void)
674 {
675  RecomputeLocalTerrainVelocity();
676  VState.vInertialPosition = Tec2i * VState.vLocation;
677  UpdateLocationMatrices();
678  UpdateBodyMatrices();
679  vVel = Tb2l * VState.vUVW;
680  VState.qAttitudeLocal = Tl2b.GetQuaternion();
681 }
682 
683 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
684 
685 void FGPropagate::SetLocation(const FGLocation& l)
686 {
687  VState.vLocation = l;
688  UpdateVehicleState();
689 }
690 
691 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692 
694 {
695  return VState.qAttitudeLocal.GetEuler() * radtodeg;
696 }
697 
698 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
699 
700 void FGPropagate::DumpState(void)
701 {
702  cout << endl;
703  cout << fgblue
704  << "------------------------------------------------------------------" << reset << endl;
705  cout << highint
706  << "State Report at sim time: " << FDMExec->GetSimTime() << " seconds" << reset << endl;
707  cout << " " << underon
708  << "Position" << underoff << endl;
709  cout << " ECI: " << VState.vInertialPosition.Dump(", ") << " (x,y,z, in ft)" << endl;
710  cout << " ECEF: " << VState.vLocation << " (x,y,z, in ft)" << endl;
711  cout << " Local: " << VState.vLocation.GetGeodLatitudeDeg()
712  << ", " << VState.vLocation.GetLongitudeDeg()
713  << ", " << GetAltitudeASL() << " (geodetic lat, lon, alt ASL in deg and ft)" << endl;
714 
715  cout << endl << " " << underon
716  << "Orientation" << underoff << endl;
717  cout << " ECI: " << VState.qAttitudeECI.GetEulerDeg().Dump(", ") << " (phi, theta, psi in deg)" << endl;
718  cout << " Local: " << VState.qAttitudeLocal.GetEulerDeg().Dump(", ") << " (phi, theta, psi in deg)" << endl;
719 
720  cout << endl << " " << underon
721  << "Velocity" << underoff << endl;
722  cout << " ECI: " << VState.vInertialVelocity.Dump(", ") << " (x,y,z in ft/s)" << endl;
723  cout << " ECEF: " << (Tb2ec * VState.vUVW).Dump(", ") << " (x,y,z in ft/s)" << endl;
724  cout << " Local: " << GetVel() << " (n,e,d in ft/sec)" << endl;
725  cout << " Body: " << GetUVW() << " (u,v,w in ft/sec)" << endl;
726 
727  cout << endl << " " << underon
728  << "Body Rates (relative to given frame, expressed in body frame)" << underoff << endl;
729  cout << " ECI: " << (VState.vPQRi*radtodeg).Dump(", ") << " (p,q,r in deg/s)" << endl;
730  cout << " ECEF: " << (VState.vPQR*radtodeg).Dump(", ") << " (p,q,r in deg/s)" << endl;
731 }
732 
733 //******************************************************************************
734 
735 void FGPropagate::WriteStateFile(int num)
736 {
737  sg_ofstream outfile;
738 
739  if (num == 0) return;
740 
741  SGPath path = FDMExec->GetOutputPath();
742 
743  if (path.isNull()) path = SGPath("initfile.");
744  else path.append("initfile.");
745 
746  // Append sim time to the filename since there may be more than one created during a simulation run
747  path.concat(to_string((double)FDMExec->GetSimTime())+".xml");
748 
749  switch(num) {
750  case 1:
751  outfile.open(path);
752  if (outfile.is_open()) {
753  outfile << "<?xml version=\"1.0\"?>" << endl;
754  outfile << "<initialize name=\"reset00\">" << endl;
755  outfile << " <ubody unit=\"FT/SEC\"> " << VState.vUVW(eU) << " </ubody> " << endl;
756  outfile << " <vbody unit=\"FT/SEC\"> " << VState.vUVW(eV) << " </vbody> " << endl;
757  outfile << " <wbody unit=\"FT/SEC\"> " << VState.vUVW(eW) << " </wbody> " << endl;
758  outfile << " <phi unit=\"DEG\"> " << VState.qAttitudeLocal.GetEuler(ePhi)*radtodeg << " </phi>" << endl;
759  outfile << " <theta unit=\"DEG\"> " << VState.qAttitudeLocal.GetEuler(eTht)*radtodeg << " </theta>" << endl;
760  outfile << " <psi unit=\"DEG\"> " << VState.qAttitudeLocal.GetEuler(ePsi)*radtodeg << " </psi>" << endl;
761  outfile << " <longitude unit=\"DEG\"> " << VState.vLocation.GetLongitudeDeg() << " </longitude>" << endl;
762  outfile << " <latitude unit=\"DEG\"> " << VState.vLocation.GetLatitudeDeg() << " </latitude>" << endl;
763  outfile << " <altitude unit=\"FT\"> " << GetDistanceAGL() << " </altitude>" << endl;
764  outfile << "</initialize>" << endl;
765  outfile.close();
766  } else {
767  cerr << "Could not open and/or write the state to the initial conditions file: "
768  << path << endl;
769  }
770  break;
771  case 2:
772  outfile.open(path);
773  if (outfile.is_open()) {
774  outfile << "<?xml version=\"1.0\"?>" << endl;
775  outfile << "<initialize name=\"IC File\" version=\"2.0\">" << endl;
776  outfile << "" << endl;
777  outfile << " <position frame=\"ECEF\">" << endl;
778  outfile << " <latitude unit=\"DEG\" type=\"geodetic\"> " << VState.vLocation.GetGeodLatitudeDeg() << " </latitude>" << endl;
779  outfile << " <longitude unit=\"DEG\"> " << VState.vLocation.GetLongitudeDeg() << " </longitude>" << endl;
780  outfile << " <altitudeMSL unit=\"FT\"> " << GetAltitudeASL() << " </altitudeMSL>" << endl;
781  outfile << " </position>" << endl;
782  outfile << "" << endl;
783  outfile << " <orientation unit=\"DEG\" frame=\"LOCAL\">" << endl;
784  outfile << " <yaw> " << VState.qAttitudeLocal.GetEulerDeg(eYaw) << " </yaw>" << endl;
785  outfile << " <pitch> " << VState.qAttitudeLocal.GetEulerDeg(ePitch) << " </pitch>" << endl;
786  outfile << " <roll> " << VState.qAttitudeLocal.GetEulerDeg(eRoll) << " </roll>" << endl;
787  outfile << " </orientation>" << endl;
788  outfile << "" << endl;
789  outfile << " <velocity unit=\"FT/SEC\" frame=\"LOCAL\">" << endl;
790  outfile << " <x> " << GetVel(eNorth) << " </x>" << endl;
791  outfile << " <y> " << GetVel(eEast) << " </y>" << endl;
792  outfile << " <z> " << GetVel(eDown) << " </z>" << endl;
793  outfile << " </velocity>" << endl;
794  outfile << "" << endl;
795  outfile << " <attitude_rate unit=\"DEG/SEC\" frame=\"BODY\">" << endl;
796  outfile << " <roll> " << (VState.vPQR*radtodeg)(eRoll) << " </roll>" << endl;
797  outfile << " <pitch> " << (VState.vPQR*radtodeg)(ePitch) << " </pitch>" << endl;
798  outfile << " <yaw> " << (VState.vPQR*radtodeg)(eYaw) << " </yaw>" << endl;
799  outfile << " </attitude_rate>" << endl;
800  outfile << "" << endl;
801  outfile << "</initialize>" << endl;
802  outfile.close();
803  } else {
804  cerr << "Could not open and/or write the state to the initial conditions file: "
805  << path << endl;
806  }
807  break;
808  default:
809  cerr << "When writing a state file, the supplied value must be 1 or 2 for the version number of the resulting IC file" << endl;
810  }
811 }
812 
813 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 
815 void FGPropagate::bind(void)
816 {
817  typedef double (FGPropagate::*PMF)(int) const;
818  typedef int (FGPropagate::*iPMF)(void) const;
819 
820  PropertyManager->Tie("velocities/h-dot-fps", this, &FGPropagate::Gethdot);
821 
822  PropertyManager->Tie("velocities/v-north-fps", this, eNorth, (PMF)&FGPropagate::GetVel);
823  PropertyManager->Tie("velocities/v-east-fps", this, eEast, (PMF)&FGPropagate::GetVel);
824  PropertyManager->Tie("velocities/v-down-fps", this, eDown, (PMF)&FGPropagate::GetVel);
825 
826  PropertyManager->Tie("velocities/u-fps", this, eU, (PMF)&FGPropagate::GetUVW);
827  PropertyManager->Tie("velocities/v-fps", this, eV, (PMF)&FGPropagate::GetUVW);
828  PropertyManager->Tie("velocities/w-fps", this, eW, (PMF)&FGPropagate::GetUVW);
829 
830  PropertyManager->Tie("velocities/p-rad_sec", this, eP, (PMF)&FGPropagate::GetPQR);
831  PropertyManager->Tie("velocities/q-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQR);
832  PropertyManager->Tie("velocities/r-rad_sec", this, eR, (PMF)&FGPropagate::GetPQR);
833 
834  PropertyManager->Tie("velocities/pi-rad_sec", this, eP, (PMF)&FGPropagate::GetPQRi);
835  PropertyManager->Tie("velocities/qi-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQRi);
836  PropertyManager->Tie("velocities/ri-rad_sec", this, eR, (PMF)&FGPropagate::GetPQRi);
837 
838  PropertyManager->Tie("velocities/eci-x-fps", this, eX, (PMF)&FGPropagate::GetInertialVelocity);
839  PropertyManager->Tie("velocities/eci-y-fps", this, eY, (PMF)&FGPropagate::GetInertialVelocity);
840  PropertyManager->Tie("velocities/eci-z-fps", this, eZ, (PMF)&FGPropagate::GetInertialVelocity);
841 
842  PropertyManager->Tie("velocities/eci-velocity-mag-fps", this, &FGPropagate::GetInertialVelocityMagnitude);
843  PropertyManager->Tie("velocities/ned-velocity-mag-fps", this, &FGPropagate::GetNEDVelocityMagnitude);
844 
845  PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::GetAltitudeASL, &FGPropagate::SetAltitudeASL);
846  PropertyManager->Tie("position/h-sl-meters", this, &FGPropagate::GetAltitudeASLmeters, &FGPropagate::SetAltitudeASLmeters);
847  PropertyManager->Tie("position/lat-gc-rad", this, &FGPropagate::GetLatitude, &FGPropagate::SetLatitude);
848  PropertyManager->Tie("position/long-gc-rad", this, &FGPropagate::GetLongitude, &FGPropagate::SetLongitude);
849  PropertyManager->Tie("position/lat-gc-deg", this, &FGPropagate::GetLatitudeDeg, &FGPropagate::SetLatitudeDeg);
850  PropertyManager->Tie("position/long-gc-deg", this, &FGPropagate::GetLongitudeDeg, &FGPropagate::SetLongitudeDeg);
851  PropertyManager->Tie("position/lat-geod-rad", this, &FGPropagate::GetGeodLatitudeRad);
852  PropertyManager->Tie("position/lat-geod-deg", this, &FGPropagate::GetGeodLatitudeDeg);
853  PropertyManager->Tie("position/geod-alt-ft", this, &FGPropagate::GetGeodeticAltitude);
854  PropertyManager->Tie("position/h-agl-ft", this, &FGPropagate::GetDistanceAGL, &FGPropagate::SetDistanceAGL);
855  PropertyManager->Tie("position/geod-alt-km", this, &FGPropagate::GetGeodeticAltitudeKm);
856  PropertyManager->Tie("position/h-agl-km", this, &FGPropagate::GetDistanceAGLKm, &FGPropagate::SetDistanceAGLKm);
857  PropertyManager->Tie("position/radius-to-vehicle-ft", this, &FGPropagate::GetRadius);
858  PropertyManager->Tie("position/terrain-elevation-asl-ft", this,
859  &FGPropagate::GetTerrainElevation,
860  &FGPropagate::SetTerrainElevation);
861 
862  PropertyManager->Tie("position/eci-x-ft", this, eX, (PMF)&FGPropagate::GetInertialPosition);
863  PropertyManager->Tie("position/eci-y-ft", this, eY, (PMF)&FGPropagate::GetInertialPosition);
864  PropertyManager->Tie("position/eci-z-ft", this, eZ, (PMF)&FGPropagate::GetInertialPosition);
865 
866  PropertyManager->Tie("position/ecef-x-ft", this, eX, (PMF)&FGPropagate::GetLocation);
867  PropertyManager->Tie("position/ecef-y-ft", this, eY, (PMF)&FGPropagate::GetLocation);
868  PropertyManager->Tie("position/ecef-z-ft", this, eZ, (PMF)&FGPropagate::GetLocation);
869 
870  PropertyManager->Tie("position/epa-rad", this, &FGPropagate::GetEarthPositionAngle);
871  PropertyManager->Tie("metrics/terrain-radius", this, &FGPropagate::GetLocalTerrainRadius);
872 
873  PropertyManager->Tie("attitude/phi-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
874  PropertyManager->Tie("attitude/theta-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
875  PropertyManager->Tie("attitude/psi-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
876 
877  PropertyManager->Tie("attitude/phi-deg", this, (int)ePhi, (PMF)&FGPropagate::GetEulerDeg);
878  PropertyManager->Tie("attitude/theta-deg", this, (int)eTht, (PMF)&FGPropagate::GetEulerDeg);
879  PropertyManager->Tie("attitude/psi-deg", this, (int)ePsi, (PMF)&FGPropagate::GetEulerDeg);
880 
881  PropertyManager->Tie("attitude/roll-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
882  PropertyManager->Tie("attitude/pitch-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
883  PropertyManager->Tie("attitude/heading-true-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
884 
885  PropertyManager->Tie("orbital/specific-angular-momentum-ft2_sec", &h);
886  PropertyManager->Tie("orbital/inclination-deg", &Inclination);
887  PropertyManager->Tie("orbital/right-ascension-deg", &RightAscension);
888  PropertyManager->Tie("orbital/eccentricity", &Eccentricity);
889  PropertyManager->Tie("orbital/argument-of-perigee-deg", &PerigeeArgument);
890  PropertyManager->Tie("orbital/true-anomaly-deg", &TrueAnomaly);
891  PropertyManager->Tie("orbital/apoapsis-radius-ft", &ApoapsisRadius);
892  PropertyManager->Tie("orbital/periapsis-radius-ft", &PeriapsisRadius);
893  PropertyManager->Tie("orbital/period-sec", &OrbitalPeriod);
894 
895  PropertyManager->Tie("simulation/integrator/rate/rotational", (int*)&integrator_rotational_rate);
896  PropertyManager->Tie("simulation/integrator/rate/translational", (int*)&integrator_translational_rate);
897  PropertyManager->Tie("simulation/integrator/position/rotational", (int*)&integrator_rotational_position);
898  PropertyManager->Tie("simulation/integrator/position/translational", (int*)&integrator_translational_position);
899 
900  PropertyManager->Tie("simulation/write-state-file", this, (iPMF)0, &FGPropagate::WriteStateFile);
901 }
902 
903 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
904 // The bitmasked value choices are as follows:
905 // unset: In this case (the default) JSBSim would only print
906 // out the normally expected messages, essentially echoing
907 // the config files as they are read. If the environment
908 // variable is not set, debug_lvl is set to 1 internally
909 // 0: This requests JSBSim not to output any messages
910 // whatsoever.
911 // 1: This value explicity requests the normal JSBSim
912 // startup messages
913 // 2: This value asks for a message to be printed out when
914 // a class is instantiated
915 // 4: When this value is set, a message is displayed when a
916 // FGModel object executes its Run() method
917 // 8: When this value is set, various runtime state variables
918 // are printed out periodically
919 // 16: When set various parameters are sanity checked and
920 // a message is printed out when they go out of bounds
921 
922 void FGPropagate::Debug(int from)
923 {
924  if (debug_lvl <= 0) return;
925 
926  if (debug_lvl & 1) { // Standard console startup message output
927  if (from == 0) { // Constructor
928 
929  }
930  }
931  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
932  if (from == 0) cout << "Instantiated: FGPropagate" << endl;
933  if (from == 1) cout << "Destroyed: FGPropagate" << endl;
934  }
935  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
936  }
937  if (debug_lvl & 8 && from == 2) { // Runtime state variables
938  cout << endl << fgblue << highint << left
939  << " Propagation Report (English units: ft, degrees) at simulation time " << FDMExec->GetSimTime() << " seconds"
940  << reset << endl;
941  cout << endl;
942  cout << highint << " Earth Position Angle (deg): " << setw(8) << setprecision(3) << reset
943  << GetEarthPositionAngleDeg() << endl;
944  cout << endl;
945  cout << highint << " Body velocity (ft/sec): " << setw(8) << setprecision(3) << reset << VState.vUVW << endl;
946  cout << highint << " Local velocity (ft/sec): " << setw(8) << setprecision(3) << reset << vVel << endl;
947  cout << highint << " Inertial velocity (ft/sec): " << setw(8) << setprecision(3) << reset << VState.vInertialVelocity << endl;
948  cout << highint << " Inertial Position (ft): " << setw(10) << setprecision(3) << reset << VState.vInertialPosition << endl;
949  cout << highint << " Latitude (deg): " << setw(8) << setprecision(3) << reset << VState.vLocation.GetLatitudeDeg() << endl;
950  cout << highint << " Longitude (deg): " << setw(8) << setprecision(3) << reset << VState.vLocation.GetLongitudeDeg() << endl;
951  cout << highint << " Altitude ASL (ft): " << setw(8) << setprecision(3) << reset << GetAltitudeASL() << endl;
952 // cout << highint << " Acceleration (NED, ft/sec^2): " << setw(8) << setprecision(3) << reset << Tb2l*GetUVWdot() << endl;
953  cout << endl;
954  cout << highint << " Matrix ECEF to Body (Orientation of Body with respect to ECEF): "
955  << reset << endl << Tec2b.Dump("\t", " ") << endl;
956  cout << highint << " Associated Euler angles (deg): " << setw(8)
957  << setprecision(3) << reset << (Tec2b.GetQuaternion().GetEuler()*radtodeg)
958  << endl << endl;
959 
960  cout << highint << " Matrix Body to ECEF (Orientation of ECEF with respect to Body):"
961  << reset << endl << Tb2ec.Dump("\t", " ") << endl;
962  cout << highint << " Associated Euler angles (deg): " << setw(8)
963  << setprecision(3) << reset << (Tb2ec.GetQuaternion().GetEuler()*radtodeg)
964  << endl << endl;
965 
966  cout << highint << " Matrix Local to Body (Orientation of Body with respect to Local):"
967  << reset << endl << Tl2b.Dump("\t", " ") << endl;
968  cout << highint << " Associated Euler angles (deg): " << setw(8)
969  << setprecision(3) << reset << (Tl2b.GetQuaternion().GetEuler()*radtodeg)
970  << endl << endl;
971 
972  cout << highint << " Matrix Body to Local (Orientation of Local with respect to Body):"
973  << reset << endl << Tb2l.Dump("\t", " ") << endl;
974  cout << highint << " Associated Euler angles (deg): " << setw(8)
975  << setprecision(3) << reset << (Tb2l.GetQuaternion().GetEuler()*radtodeg)
976  << endl << endl;
977 
978  cout << highint << " Matrix Local to ECEF (Orientation of ECEF with respect to Local):"
979  << reset << endl << Tl2ec.Dump("\t", " ") << endl;
980  cout << highint << " Associated Euler angles (deg): " << setw(8)
981  << setprecision(3) << reset << (Tl2ec.GetQuaternion().GetEuler()*radtodeg)
982  << endl << endl;
983 
984  cout << highint << " Matrix ECEF to Local (Orientation of Local with respect to ECEF):"
985  << reset << endl << Tec2l.Dump("\t", " ") << endl;
986  cout << highint << " Associated Euler angles (deg): " << setw(8)
987  << setprecision(3) << reset << (Tec2l.GetQuaternion().GetEuler()*radtodeg)
988  << endl << endl;
989 
990  cout << highint << " Matrix ECEF to Inertial (Orientation of Inertial with respect to ECEF):"
991  << reset << endl << Tec2i.Dump("\t", " ") << endl;
992  cout << highint << " Associated Euler angles (deg): " << setw(8)
993  << setprecision(3) << reset << (Tec2i.GetQuaternion().GetEuler()*radtodeg)
994  << endl << endl;
995 
996  cout << highint << " Matrix Inertial to ECEF (Orientation of ECEF with respect to Inertial):"
997  << reset << endl << Ti2ec.Dump("\t", " ") << endl;
998  cout << highint << " Associated Euler angles (deg): " << setw(8)
999  << setprecision(3) << reset << (Ti2ec.GetQuaternion().GetEuler()*radtodeg)
1000  << endl << endl;
1001 
1002  cout << highint << " Matrix Inertial to Body (Orientation of Body with respect to Inertial):"
1003  << reset << endl << Ti2b.Dump("\t", " ") << endl;
1004  cout << highint << " Associated Euler angles (deg): " << setw(8)
1005  << setprecision(3) << reset << (Ti2b.GetQuaternion().GetEuler()*radtodeg)
1006  << endl << endl;
1007 
1008  cout << highint << " Matrix Body to Inertial (Orientation of Inertial with respect to Body):"
1009  << reset << endl << Tb2i.Dump("\t", " ") << endl;
1010  cout << highint << " Associated Euler angles (deg): " << setw(8)
1011  << setprecision(3) << reset << (Tb2i.GetQuaternion().GetEuler()*radtodeg)
1012  << endl << endl;
1013 
1014  cout << highint << " Matrix Inertial to Local (Orientation of Local with respect to Inertial):"
1015  << reset << endl << Ti2l.Dump("\t", " ") << endl;
1016  cout << highint << " Associated Euler angles (deg): " << setw(8)
1017  << setprecision(3) << reset << (Ti2l.GetQuaternion().GetEuler()*radtodeg)
1018  << endl << endl;
1019 
1020  cout << highint << " Matrix Local to Inertial (Orientation of Inertial with respect to Local):"
1021  << reset << endl << Tl2i.Dump("\t", " ") << endl;
1022  cout << highint << " Associated Euler angles (deg): " << setw(8)
1023  << setprecision(3) << reset << (Tl2i.GetQuaternion().GetEuler()*radtodeg)
1024  << endl << endl;
1025 
1026  cout << setprecision(6); // reset the output stream
1027  }
1028  if (debug_lvl & 16) { // Sanity checking
1029  if (from == 2) { // State sanity checking
1030  if (fabs(VState.vPQR.Magnitude()) > 1000.0) {
1031  stringstream s;
1032  s << "Vehicle rotation rate is excessive (>1000 rad/sec): " << VState.vPQR.Magnitude();
1033  cerr << endl << s.str() << endl;
1034  throw BaseException(s.str());
1035  }
1036  if (fabs(VState.vUVW.Magnitude()) > 1.0e10) {
1037  stringstream s;
1038  s << "Vehicle velocity is excessive (>1e10 ft/sec): " << VState.vUVW.Magnitude();
1039  cerr << endl << s.str() << endl;
1040  throw BaseException(s.str());
1041  }
1042  if (fabs(GetDistanceAGL()) > 1e10) {
1043  stringstream s;
1044  s << "Vehicle altitude is excessive (>1e10 ft): " << GetDistanceAGL();
1045  cerr << endl << s.str() << endl;
1046  throw BaseException(s.str());
1047  }
1048  }
1049  }
1050  if (debug_lvl & 64) {
1051  if (from == 0) { // Constructor
1052  }
1053  }
1054 }
1055 }
This class implements a 3 element column vector.
double Magnitude(void) const
Length of the vector.
std::string Dump(const std::string &delimeter) const
Prints the contents of the vector.
Encapsulates the JSBSim simulation executive.
Definition: FGFDMExec.h:184
bool IntegrationSuspended(void) const
Returns the simulation suspension state.
Definition: FGFDMExec.h:557
double GetSimTime(void) const
Returns the cumulative simulation time in seconds.
Definition: FGFDMExec.h:544
std::shared_ptr< FGInertial > GetInertial(void) const
Returns the FGInertial pointer.
Definition: FGFDMExec.cpp:285
const SGPath & GetOutputPath(void)
Retrieves the path to the output files.
Definition: FGFDMExec.h:403
Initializes the simulation run.
const FGQuaternion & GetOrientation(void) const
Gets the initial orientation.
const FGLocation & GetPosition(void) const
Gets the initial position.
const FGColumnVector3 GetUVWFpsIC(void) const
Gets the initial body velocity.
double GetEarthPositionAngleIC(void) const
Gets the initial Earth position angle.
const FGColumnVector3 GetPQRRadpsIC(void) const
Gets the initial body rotation rate.
static constexpr double Constrain(double min, double value, double max)
Constrain a value between a minimum and a maximum value.
Definition: FGJSBBase.h:289
static char fgblue[6]
blue text
Definition: FGJSBBase.h:163
static char underon[5]
underlines text
Definition: FGJSBBase.h:159
static char reset[5]
resets text properties
Definition: FGJSBBase.h:157
static char underoff[6]
underline off
Definition: FGJSBBase.h:161
static char highint[5]
highlights text
Definition: FGJSBBase.h:151
FGLocation holds an arbitrary location in the Earth centered Earth fixed reference frame (ECEF).
Definition: FGLocation.h:152
void SetEllipse(double semimajor, double semiminor)
Sets the semimajor and semiminor axis lengths for this planet.
Definition: FGLocation.cpp:259
const FGMatrix33 & GetTl2ec(void) const
Transform matrix from local horizontal to earth centered frame.
Definition: FGLocation.h:296
double GetGeodLatitudeDeg(void) const
Get the GEODETIC latitude in degrees.
Definition: FGLocation.h:273
double GetRadius() const
Get the distance from the center of the earth in feet.
Definition: FGLocation.h:291
double GetSeaLevelRadius(void) const
Get the sea level radius in feet below the current location.
Definition: FGLocation.cpp:273
double GetLatitudeDeg() const
Get the GEOCENTRIC latitude in degrees.
Definition: FGLocation.h:267
void SetRadius(double radius)
Set the distance from the center of the earth.
Definition: FGLocation.cpp:214
double GetLongitudeDeg() const
Get the longitude.
Definition: FGLocation.h:240
FGMatrix33 Transposed(void) const
Transposed matrix.
Definition: FGMatrix33.h:221
std::string Dump(const std::string &delimeter) const
Prints the contents of the matrix.
Definition: FGMatrix33.cpp:66
FGQuaternion GetQuaternion(void) const
Returns the quaternion associated with this direction cosine (rotation) matrix.
Definition: FGMatrix33.cpp:106
Base class for all scheduled JSBSim models.
Definition: FGModel.h:70
virtual bool Run(bool Holding)
Runs the model; called by the Executive.
Definition: FGModel.cpp:89
const FGColumnVector3 & GetEuler(void) const
Retrieves the Euler angles that define the vehicle orientation.
Definition: FGPropagate.h:253
const FGColumnVector3 & GetUVW(void) const
Retrieves the body frame vehicle velocity vector.
Definition: FGPropagate.h:197
double GetNEDVelocityMagnitude(void) const
Retrieves the total local NED velocity in ft/sec.
Definition: FGPropagate.h:304
const FGColumnVector3 & GetVel(void) const
Retrieves the velocity vector.
Definition: FGPropagate.h:185
double GetInertialVelocityMagnitude(void) const
Retrieves the total inertial velocity in ft/sec.
Definition: FGPropagate.h:300
double Gethdot(void) const
Returns the current altitude rate.
Definition: FGPropagate.h:418
FGColumnVector3 GetEulerDeg(void) const
Retrieves the Euler angles (in degrees) that define the vehicle orientation.
double GetLocalTerrainRadius(void) const
Returns the "constant" LocalTerrainRadius.
bool Run(bool Holding)
Runs the state propagation model; called by the Executive Can pass in a value indicating if the execu...
const FGColumnVector3 & GetPQR(void) const
Retrieves the body angular rates vector, relative to the ECEF frame.
Definition: FGPropagate.h:211
double GetEarthPositionAngleDeg(void) const
Returns the Earth position angle in degrees.
Definition: FGPropagate.h:436
const FGColumnVector3 & GetInertialVelocity(void) const
Retrieves the inertial velocity vector in ft/sec.
Definition: FGPropagate.h:308
double GetAltitudeASL(void) const
Returns the current altitude above sea level.
const FGColumnVector3 & GetInertialPosition(void) const
Retrieves the inertial position vector.
Definition: FGPropagate.h:313
~FGPropagate()
Destructor.
bool InitModel(void)
Initializes the FGPropagate class after instantiation and prior to first execution.
FGPropagate(FGFDMExec *Executive)
Constructor.
Definition: FGPropagate.cpp:82
const FGColumnVector3 & GetPQRi(void) const
Retrieves the body angular rates vector, relative to the ECI (inertial) frame.
Definition: FGPropagate.h:225
double GetEarthPositionAngle(void) const
Returns the Earth position angle.
Definition: FGPropagate.h:431
void SetHoldDown(bool hd)
Sets the property forces/hold-down.
double GetAltitudeASLmeters(void) const
Returns the current altitude above sea level.
Definition: FGPropagate.h:337
Models the Quaternion representation of rotations.
Definition: FGQuaternion.h:86
const FGColumnVector3 & GetEuler(void) const
Retrieves the Euler angles.
Definition: FGQuaternion.h:199
void Normalize(void)
Normalize.
const FGMatrix33 & GetT(void) const
Transformation matrix.
Definition: FGQuaternion.h:188
double GetEulerDeg(int i) const
Retrieves the Euler angles.
Definition: FGQuaternion.h:220
FGQuaternion GetQDot(const FGColumnVector3 &PQR) const
Quaternion derivative for given angular rates.
FGColumnVector3 vUVW
The velocity vector of the vehicle with respect to the ECEF frame, expressed in the body system.
Definition: FGPropagate.h:109
FGQuaternion qAttitudeECI
The current orientation of the vehicle, that is, the orientation of the body frame relative to the in...
Definition: FGPropagate.h:127
FGColumnVector3 vPQRi
The angular velocity vector for the vehicle body frame relative to the ECI frame, expressed in the bo...
Definition: FGPropagate.h:119
FGColumnVector3 vPQR
The angular velocity vector for the vehicle relative to the ECEF frame, expressed in the body frame.
Definition: FGPropagate.h:114
FGLocation vLocation
Represents the current location of the vehicle in Earth centered Earth fixed (ECEF) frame.
Definition: FGPropagate.h:104
FGQuaternion qAttitudeLocal
The current orientation of the vehicle, that is, the orientation of the body frame relative to the lo...
Definition: FGPropagate.h:123