]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/models/FGPropagate.cpp
bbdee4aefa4265423b1dfe466f3c76e3a39b2109
[flightgear.git] / src / FDM / JSBSim / models / 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 (jsb@hal-pc.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 Software
13  Foundation; either version 2 of the License, or (at your option) any later
14  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 with
22  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23  Place - Suite 330, Boston, MA  02111-1307, USA.
24
25  Further information about the GNU Lesser General Public License can also be found on
26  the world wide web at http://www.gnu.org.
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
52 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53 INCLUDES
54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
55
56 #include <cmath>
57 #include <iomanip>
58
59 #include "FGPropagate.h"
60 #include <FGState.h>
61 #include <FGFDMExec.h>
62 #include "FGAircraft.h"
63 #include "FGMassBalance.h"
64 #include "FGInertial.h"
65 #include <input_output/FGPropertyManager.h>
66
67 namespace JSBSim {
68
69 static const char *IdSrc = "$Id$";
70 static const char *IdHdr = ID_PROPAGATE;
71
72 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73 CLASS IMPLEMENTATION
74 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
75
76 FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex)
77 {
78   Debug(0);
79   Name = "FGPropagate";
80
81   last2_vPQRdot.InitMatrix();
82   last_vPQRdot.InitMatrix();
83   vPQRdot.InitMatrix();
84   
85   last2_vUVWdot.InitMatrix();
86   last_vUVWdot.InitMatrix();
87   vUVWdot.InitMatrix();
88   
89   last2_vLocationDot.InitMatrix();
90   last_vLocationDot.InitMatrix();
91   vLocationDot.InitMatrix();
92
93   vOmegaLocal.InitMatrix();
94
95   integrator_rotational_rate = eAdamsBashforth2;
96   integrator_translational_rate = eAdamsBashforth2;
97   integrator_rotational_position = eTrapezoidal;
98   integrator_translational_position = eTrapezoidal;
99
100   bind();
101   Debug(0);
102 }
103
104 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105
106 FGPropagate::~FGPropagate(void)
107 {
108   Debug(1);
109 }
110
111 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112
113 bool FGPropagate::InitModel(void)
114 {
115   if (!FGModel::InitModel()) return false;
116
117   SeaLevelRadius = Inertial->GetRefRadius();          // For initialization ONLY
118   RunwayRadius   = SeaLevelRadius;
119
120   VState.vLocation.SetRadius( SeaLevelRadius + 4.0 ); // Todo Add terrain elevation?
121   VState.vLocation.SetEllipse(Inertial->GetSemimajor(), Inertial->GetSemiminor());
122   vOmega = FGColumnVector3( 0.0, 0.0, Inertial->omega() ); // Earth rotation vector
123
124   last2_vPQRdot.InitMatrix();
125   last_vPQRdot.InitMatrix();
126   vPQRdot.InitMatrix();
127   
128   last2_vUVWdot.InitMatrix();
129   last_vUVWdot.InitMatrix();
130   vUVWdot.InitMatrix();
131   
132   last2_vLocationDot.InitMatrix();
133   last_vLocationDot.InitMatrix();
134   vLocationDot.InitMatrix();
135
136   vOmegaLocal.InitMatrix();
137
138   integrator_rotational_rate = eAdamsBashforth2;
139   integrator_translational_rate = eAdamsBashforth2;
140   integrator_rotational_position = eTrapezoidal;
141   integrator_translational_position = eTrapezoidal;
142
143   return true;
144 }
145
146 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147
148 void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
149 {
150   SeaLevelRadius = FGIC->GetSeaLevelRadiusFtIC();
151   RunwayRadius = SeaLevelRadius;
152
153   // Set the position lat/lon/radius
154   VState.vLocation.SetPosition( FGIC->GetLongitudeRadIC(),
155                           FGIC->GetLatitudeRadIC(),
156                           FGIC->GetAltitudeFtIC() + FGIC->GetSeaLevelRadiusFtIC() );
157
158   VehicleRadius = GetRadius();
159   radInv = 1.0/VehicleRadius;
160
161   // Set the Orientation from the euler angles
162   VState.vQtrn = FGQuaternion( FGIC->GetPhiRadIC(),
163                         FGIC->GetThetaRadIC(),
164                         FGIC->GetPsiRadIC() );
165
166   // Set the velocities in the instantaneus body frame
167   VState.vUVW = FGColumnVector3( FGIC->GetUBodyFpsIC(),
168                           FGIC->GetVBodyFpsIC(),
169                           FGIC->GetWBodyFpsIC() );
170
171   // Set the angular velocities in the instantaneus body frame.
172   VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
173                           FGIC->GetQRadpsIC(),
174                           FGIC->GetRRadpsIC() );
175
176   // Compute the local frame ECEF velocity
177   vVel = GetTb2l()*VState.vUVW;
178
179   // Finally, make sure that the quaternion stays normalized.
180   VState.vQtrn.Normalize();
181
182   // Recompute the RunwayRadius level.
183   RecomputeRunwayRadius();
184 }
185
186 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187 /*
188 Purpose: Called on a schedule to perform EOM integration
189 Notes:   [JB] Run in standalone mode, SeaLevelRadius will be reference radius.
190          In FGFS, SeaLevelRadius is stuffed from FGJSBSim in JSBSim.cxx each pass.
191
192 At the top of this Run() function, see several "shortcuts" (or, aliases) being
193 set up for use later, rather than using the longer class->function() notation.
194
195 This propagation is done using the current state values
196 and current derivatives. Based on these values we compute an approximation to the
197 state values for (now + dt).
198
199 In the code below, variables named beginning with a small "v" refer to a 
200 a column vector, variables named beginning with a "T" refer to a transformation
201 matrix. ECEF refers to Earth Centered Earth Fixed. ECI refers to Earth Centered
202 Inertial.
203
204 */
205
206 bool FGPropagate::Run(void)
207 {
208   if (FGModel::Run()) return true;  // Fast return if we have nothing to do ...
209   if (FDMExec->Holding()) return false;
210
211   RecomputeRunwayRadius();
212
213   // Calculate current aircraft radius from center of planet
214
215   VehicleRadius = GetRadius();
216   radInv = 1.0/VehicleRadius;
217
218   // These local copies of the transformation matrices are for use this
219   // pass through Run() only.
220
221   Tl2b = GetTl2b();           // local to body frame transform
222   Tb2l = Tl2b.Transposed();   // body to local frame transform
223   Tl2ec = GetTl2ec();         // local to ECEF transform
224   Tec2l = Tl2ec.Transposed(); // ECEF to local frame transform
225   Tec2b = Tl2b * Tec2l;       // ECEF to body frame transform
226   Tb2ec = Tec2b.Transposed(); // body to ECEF frame tranform
227   Ti2ec = GetTi2ec();         // ECI to ECEF transform
228   Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
229   Ti2b  = Tec2b*Ti2ec;        // ECI to body frame transform
230   Tb2i  = Ti2b.Transposed();  // body to ECI frame transform
231
232   // Compute vehicle velocity wrt ECEF frame, expressed in Local horizontal frame.
233   vVel = Tb2l * VState.vUVW;
234
235   // Inertial angular velocity measured in the body frame.
236   vPQRi = VState.vPQR + Tec2b*vOmega;
237
238   // Calculate state derivatives
239   CalculatePQRdot();      // Angular rate derivative
240   CalculateUVWdot();      // Translational rate derivative
241   CalculateQuatdot();     // Angular orientation derivative
242   CalculateLocationdot(); // Translational position derivative
243
244   // Integrate to propagate the state
245
246   double dt = State->Getdt()*rate;  // The 'stepsize'
247
248   // Propagate rotational velocity
249
250   switch(integrator_rotational_rate) {
251   case eRectEuler:       VState.vPQR += dt*vPQRdot;
252     break;
253   case eTrapezoidal:     VState.vPQR += 0.5*dt*(vPQRdot + last_vPQRdot);
254     break;
255   case eAdamsBashforth2: VState.vPQR += dt*(1.5*vPQRdot - 0.5*last_vPQRdot);
256     break;
257   case eAdamsBashforth3: VState.vPQR += (1/12.0)*dt*(23.0*vPQRdot - 16.0*last_vPQRdot + 5.0*last2_vPQRdot);
258     break;
259   case eNone: // do nothing, freeze angular rate
260     break;
261   }
262   
263   // Propagate translational velocity
264
265   switch(integrator_translational_rate) {
266   case eRectEuler:       VState.vUVW += dt*vUVWdot;
267     break;
268   case eTrapezoidal:     VState.vUVW += 0.5*dt*(vUVWdot + last_vUVWdot);
269     break;
270   case eAdamsBashforth2: VState.vUVW += dt*(1.5*vUVWdot - 0.5*last_vUVWdot);
271     break;
272   case eAdamsBashforth3: VState.vUVW += (1/12.0)*dt*(23.0*vUVWdot - 16.0*last_vUVWdot + 5.0*last2_vUVWdot);
273     break;
274   case eNone: // do nothing, freeze translational rate
275     break;
276   }
277
278   // Propagate angular position
279
280   switch(integrator_rotational_position) {
281   case eRectEuler:       VState.vQtrn += dt*vQtrndot;
282     break;
283   case eTrapezoidal:     VState.vQtrn += 0.5*dt*(vQtrndot + last_vQtrndot);
284     break;
285   case eAdamsBashforth2: VState.vQtrn += dt*(1.5*vQtrndot - 0.5*last_vQtrndot);
286     break;
287   case eAdamsBashforth3: VState.vQtrn += (1/12.0)*dt*(23.0*vQtrndot - 16.0*last_vQtrndot + 5.0*last2_vQtrndot);
288     break;
289   case eNone: // do nothing, freeze angular position
290     break;
291   }
292
293   // Propagate translational position
294
295   switch(integrator_translational_position) {
296   case eRectEuler:       VState.vLocation += dt*vLocationDot;
297     break;
298   case eTrapezoidal:     VState.vLocation += 0.5*dt*(vLocationDot + last_vLocationDot);
299     break;
300   case eAdamsBashforth2: VState.vLocation += dt*(1.5*vLocationDot - 0.5*last_vLocationDot);
301     break;
302   case eAdamsBashforth3: VState.vLocation += (1/12.0)*dt*(23.0*vLocationDot - 16.0*last_vLocationDot + 5.0*last2_vLocationDot);
303     break;
304   case eNone: // do nothing, freeze translational position
305     break;
306   }
307   
308   // Set past values
309   
310   last2_vPQRdot = last_vPQRdot;
311   last_vPQRdot = vPQRdot;
312   
313   last2_vUVWdot = last_vUVWdot;
314   last_vUVWdot = vUVWdot;
315   
316   last2_vQtrndot = last_vQtrndot;
317   last_vQtrndot = vQtrndot;
318
319   last2_vLocationDot = last_vLocationDot;
320   last_vLocationDot = vLocationDot;
321
322   Debug(2);
323   return false;
324 }
325
326 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327 // Compute body frame rotational accelerations based on the current body moments
328 //
329 // vPQRdot is the derivative of the absolute angular velocity of the vehicle 
330 // (body rate with respect to the inertial frame), expressed in the body frame,
331 // where the derivative is taken in the body frame.
332 // J is the inertia matrix
333 // Jinv is the inverse inertia matrix
334 // vMoments is the moment vector in the body frame
335 // vPQRi is the total inertial angular velocity of the vehicle
336 // expressed in the body frame.
337 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation", 
338 //            Second edition (2004), eqn 1.5-16e (page 50)
339
340 void FGPropagate::CalculatePQRdot(void)
341 {
342   const FGColumnVector3& vMoments = Aircraft->GetMoments(); // current moments
343   const FGMatrix33& J = MassBalance->GetJ();                // inertia matrix
344   const FGMatrix33& Jinv = MassBalance->GetJinv();          // inertia matrix inverse
345
346   // Compute body frame rotational accelerations based on the current body
347   // moments and the total inertial angular velocity expressed in the body
348   // frame.
349
350   vPQRdot = Jinv*(vMoments - vPQRi*(J*vPQRi));
351 }
352
353 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
354 // Compute the quaternion orientation derivative
355 //
356 // vQtrndot is the quaternion derivative.
357 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation", 
358 //            Second edition (2004), eqn 1.5-16b (page 50)
359
360 void FGPropagate::CalculateQuatdot(void)
361 {
362   vOmegaLocal.InitMatrix( radInv*vVel(eEast),
363                          -radInv*vVel(eNorth),
364                          -radInv*vVel(eEast)*VState.vLocation.GetTanLatitude() );
365
366   // Compute quaternion orientation derivative on current body rates
367   vQtrndot = VState.vQtrn.GetQDot( VState.vPQR - Tl2b*vOmegaLocal);
368 }
369
370 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371 // This set of calculations results in the body frame accelerations being
372 // computed.
373 // Reference: See Stevens and Lewis, "Aircraft Control and Simulation", 
374 //            Second edition (2004), eqn 1.5-16d (page 50)
375
376 void FGPropagate::CalculateUVWdot(void)
377 {
378   double mass = MassBalance->GetMass();                      // mass
379   const FGColumnVector3& vForces = Aircraft->GetForces();    // current forces
380
381   const FGColumnVector3 vGravAccel( 0.0, 0.0, Inertial->GetGAccel(VehicleRadius) );
382
383   // Begin to compute body frame accelerations based on the current body forces
384   vUVWdot = vForces/mass - VState.vPQR * VState.vUVW;
385
386   // Include Coriolis acceleration.
387   vUVWdot -= 2.0 * (Ti2b *vOmega) * VState.vUVW;
388
389   // Include Centrifugal acceleration.
390   if (!GroundReactions->GetWOW()) {
391     vUVWdot -= Ti2b*(vOmega*(vOmega*(Tec2i*VState.vLocation)));
392   }
393
394   // Include Gravitation accel
395   FGColumnVector3 gravAccel = Tl2b*vGravAccel;
396   vUVWdot += gravAccel;
397 }
398
399 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400
401 void FGPropagate::CalculateLocationdot(void)
402 {
403   // Transform the vehicle velocity relative to the ECEF frame, expressed
404   // in the body frame, to be expressed in the ECEF frame.
405   vLocationDot = Tb2ec * VState.vUVW;
406
407   // Now, transform the velocity vector of the body relative to the origin (Earth
408   // center) to be expressed in the inertial frame, and add the vehicle velocity
409   // contribution due to the rotation of the planet. The above velocity is only
410   // relative to the rotating ECEF frame.
411   // Reference: See Stevens and Lewis, "Aircraft Control and Simulation", 
412   //            Second edition (2004), eqn 1.5-16c (page 50)
413
414   vInertialVelocity = Tec2i * vLocationDot + (vOmega * (Tec2i * VState.vLocation));
415 }
416
417 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418
419 void FGPropagate::RecomputeRunwayRadius(void)
420 {
421   // Get the runway radius.
422   FGLocation contactloc;
423   FGColumnVector3 dv;
424   FGGroundCallback* gcb = FDMExec->GetGroundCallback();
425   double t = State->Getsim_time();
426   gcb->GetAGLevel(t, VState.vLocation, contactloc, dv, dv);
427   RunwayRadius = contactloc.GetRadius();
428 }
429
430 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431
432 void FGPropagate::SetTerrainElevationASL(double tt)
433 {
434   FDMExec->GetGroundCallback()->SetTerrainGeoCentRadius(tt+SeaLevelRadius);
435 }
436
437 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438
439 double FGPropagate::GetTerrainElevationASL(void) const
440 {
441   return FDMExec->GetGroundCallback()->GetTerrainGeoCentRadius()-SeaLevelRadius;
442 }
443
444 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
445
446 const FGMatrix33& FGPropagate::GetTi2ec(void)
447 {
448   return VState.vLocation.GetTi2ec(Inertial->GetEarthPositionAngle());
449 }
450
451 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
452
453 const FGMatrix33& FGPropagate::GetTec2i(void)
454 {
455   return VState.vLocation.GetTec2i(Inertial->GetEarthPositionAngle());
456 }
457
458 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459
460 void FGPropagate::Seth(double tt)
461 {
462   VState.vLocation.SetRadius( tt + SeaLevelRadius );
463 }
464
465 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466
467 double FGPropagate::GetRunwayRadius(void) const
468 {
469   return RunwayRadius;
470 }
471
472 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473
474 double FGPropagate::GetDistanceAGL(void) const
475 {
476   return VState.vLocation.GetRadius() - RunwayRadius;
477 }
478
479 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480
481 void FGPropagate::SetDistanceAGL(double tt)
482 {
483   VState.vLocation.SetRadius( tt + RunwayRadius );
484 }
485
486 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487
488 void FGPropagate::bind(void)
489 {
490   typedef double (FGPropagate::*PMF)(int) const;
491 //  typedef double (FGPropagate::*dPMF)() const;
492   PropertyManager->Tie("velocities/h-dot-fps", this, &FGPropagate::Gethdot);
493
494   PropertyManager->Tie("velocities/v-north-fps", this, eNorth, (PMF)&FGPropagate::GetVel);
495   PropertyManager->Tie("velocities/v-east-fps", this, eEast, (PMF)&FGPropagate::GetVel);
496   PropertyManager->Tie("velocities/v-down-fps", this, eDown, (PMF)&FGPropagate::GetVel);
497
498   PropertyManager->Tie("velocities/u-fps", this, eU, (PMF)&FGPropagate::GetUVW);
499   PropertyManager->Tie("velocities/v-fps", this, eV, (PMF)&FGPropagate::GetUVW);
500   PropertyManager->Tie("velocities/w-fps", this, eW, (PMF)&FGPropagate::GetUVW);
501
502   PropertyManager->Tie("velocities/p-rad_sec", this, eP, (PMF)&FGPropagate::GetPQR);
503   PropertyManager->Tie("velocities/q-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQR);
504   PropertyManager->Tie("velocities/r-rad_sec", this, eR, (PMF)&FGPropagate::GetPQR);
505
506   PropertyManager->Tie("velocities/eci-velocity-mag-fps", this, &FGPropagate::GetInertialVelocityMagnitude);
507
508   PropertyManager->Tie("accelerations/pdot-rad_sec2", this, eP, (PMF)&FGPropagate::GetPQRdot);
509   PropertyManager->Tie("accelerations/qdot-rad_sec2", this, eQ, (PMF)&FGPropagate::GetPQRdot);
510   PropertyManager->Tie("accelerations/rdot-rad_sec2", this, eR, (PMF)&FGPropagate::GetPQRdot);
511
512   PropertyManager->Tie("accelerations/udot-ft_sec2", this, eU, (PMF)&FGPropagate::GetUVWdot);
513   PropertyManager->Tie("accelerations/vdot-ft_sec2", this, eV, (PMF)&FGPropagate::GetUVWdot);
514   PropertyManager->Tie("accelerations/wdot-ft_sec2", this, eW, (PMF)&FGPropagate::GetUVWdot);
515
516   PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::Geth, &FGPropagate::Seth, true);
517   PropertyManager->Tie("position/h-sl-meters", this, &FGPropagate::Gethmeters, &FGPropagate::Sethmeters, true);
518   PropertyManager->Tie("position/lat-gc-rad", this, &FGPropagate::GetLatitude, &FGPropagate::SetLatitude);
519   PropertyManager->Tie("position/long-gc-rad", this, &FGPropagate::GetLongitude, &FGPropagate::SetLongitude);
520   PropertyManager->Tie("position/lat-gc-deg", this, &FGPropagate::GetLatitudeDeg, &FGPropagate::SetLatitudeDeg);
521   PropertyManager->Tie("position/long-gc-deg", this, &FGPropagate::GetLongitudeDeg, &FGPropagate::SetLongitudeDeg);
522   PropertyManager->Tie("position/lat-geod-rad", this, &FGPropagate::GetGeodLatitudeRad);
523   PropertyManager->Tie("position/lat-geod-deg", this, &FGPropagate::GetGeodLatitudeDeg);
524   PropertyManager->Tie("position/geod-alt-ft", this, &FGPropagate::GetGeodeticAltitude);
525   PropertyManager->Tie("position/h-agl-ft", this,  &FGPropagate::GetDistanceAGL, &FGPropagate::SetDistanceAGL);
526   PropertyManager->Tie("position/radius-to-vehicle-ft", this, &FGPropagate::GetRadius);
527   PropertyManager->Tie("position/terrain-elevation-asl-ft", this,
528                           &FGPropagate::GetTerrainElevationASL,
529                           &FGPropagate::SetTerrainElevationASL, false);
530
531   PropertyManager->Tie("metrics/runway-radius", this, &FGPropagate::GetRunwayRadius);
532
533   PropertyManager->Tie("attitude/phi-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
534   PropertyManager->Tie("attitude/theta-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
535   PropertyManager->Tie("attitude/psi-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
536
537   PropertyManager->Tie("attitude/roll-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
538   PropertyManager->Tie("attitude/pitch-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
539   PropertyManager->Tie("attitude/heading-true-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
540   
541   PropertyManager->Tie("simulation/integrator/rate/rotational", &integrator_rotational_rate);
542   PropertyManager->Tie("simulation/integrator/rate/translational", &integrator_translational_rate);
543   PropertyManager->Tie("simulation/integrator/position/rotational", &integrator_rotational_position);
544   PropertyManager->Tie("simulation/integrator/position/translational", &integrator_translational_position);
545 }
546
547 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
548 //    The bitmasked value choices are as follows:
549 //    unset: In this case (the default) JSBSim would only print
550 //       out the normally expected messages, essentially echoing
551 //       the config files as they are read. If the environment
552 //       variable is not set, debug_lvl is set to 1 internally
553 //    0: This requests JSBSim not to output any messages
554 //       whatsoever.
555 //    1: This value explicity requests the normal JSBSim
556 //       startup messages
557 //    2: This value asks for a message to be printed out when
558 //       a class is instantiated
559 //    4: When this value is set, a message is displayed when a
560 //       FGModel object executes its Run() method
561 //    8: When this value is set, various runtime state variables
562 //       are printed out periodically
563 //    16: When set various parameters are sanity checked and
564 //       a message is printed out when they go out of bounds
565
566 void FGPropagate::Debug(int from)
567 {
568   if (debug_lvl <= 0) return;
569
570   if (debug_lvl & 1) { // Standard console startup message output
571     if (from == 0) { // Constructor
572
573     }
574   }
575   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
576     if (from == 0) cout << "Instantiated: FGPropagate" << endl;
577     if (from == 1) cout << "Destroyed:    FGPropagate" << endl;
578   }
579   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
580   }
581   if (debug_lvl & 8 ) { // Runtime state variables
582   }
583   if (debug_lvl & 16) { // Sanity checking
584     if (from == 2) { // State sanity checking
585       if (fabs(VState.vPQR.Magnitude()) > 1000.0) {
586         cerr << endl << "Vehicle rotation rate is excessive (>1000 rad/sec): " << VState.vPQR.Magnitude() << endl;
587         exit(-1);
588       }
589       if (fabs(VState.vUVW.Magnitude()) > 1.0e10) {
590         cerr << endl << "Vehicle velocity is excessive (>1e10 ft/sec): " << VState.vUVW.Magnitude() << endl;
591         exit(-1);
592       }
593       if (fabs(GetDistanceAGL()) > 1e10) {
594         cerr << endl << "Vehicle altitude is excessive (>1e10 ft): " << GetDistanceAGL() << endl;
595         exit(-1);
596       }
597     }
598   }
599   if (debug_lvl & 64) {
600     if (from == 0) { // Constructor
601       cout << IdSrc << endl;
602       cout << IdHdr << endl;
603     }
604   }
605 }
606 }