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