1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Module: FGPropagate.cpp
6 Purpose: Integrate the EOM to determine instantaneous position
9 ------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
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
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
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.
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.
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 This class encapsulates the integration of rates and accelerations to get the
31 current position of the aircraft.
34 --------------------------------------------------------------------------------
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
43 [2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
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
52 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
57 # include <simgear/compiler.h>
58 # ifdef SG_HAVE_STD_INCLUDES
66 # if defined(sgi) && !defined(__GNUC__)
68 # if (_COMPILER_VERSION < 740)
79 #include "FGPropagate.h"
81 #include <FGFDMExec.h>
82 #include "FGAircraft.h"
83 #include "FGMassBalance.h"
84 #include "FGInertial.h"
85 #include <input_output/FGPropertyManager.h>
89 static const char *IdSrc = "$Id$";
90 static const char *IdHdr = ID_PROPAGATE;
92 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
96 FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex)
101 last2_vPQRdot.InitMatrix();
102 last_vPQRdot.InitMatrix();
103 vPQRdot.InitMatrix();
105 last2_vUVWdot.InitMatrix();
106 last_vUVWdot.InitMatrix();
107 vUVWdot.InitMatrix();
109 last2_vLocationDot.InitMatrix();
110 last_vLocationDot.InitMatrix();
111 vLocationDot.InitMatrix();
117 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119 FGPropagate::~FGPropagate(void)
125 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 bool FGPropagate::InitModel(void)
129 FGModel::InitModel();
131 SeaLevelRadius = Inertial->RefRadius(); // For initialization ONLY
132 RunwayRadius = SeaLevelRadius;
134 VState.vLocation.SetRadius( SeaLevelRadius + 4.0 );
139 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141 void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
143 SeaLevelRadius = FGIC->GetSeaLevelRadiusFtIC();
144 RunwayRadius = SeaLevelRadius;
146 // Set the position lat/lon/radius
147 VState.vLocation = FGLocation( FGIC->GetLongitudeRadIC(),
148 FGIC->GetLatitudeRadIC(),
149 FGIC->GetAltitudeFtIC() + FGIC->GetSeaLevelRadiusFtIC() );
151 // Set the Orientation from the euler angles
152 VState.vQtrn = FGQuaternion( FGIC->GetPhiRadIC(),
153 FGIC->GetThetaRadIC(),
154 FGIC->GetPsiRadIC() );
156 // Set the velocities in the instantaneus body frame
157 VState.vUVW = FGColumnVector3( FGIC->GetUBodyFpsIC(),
158 FGIC->GetVBodyFpsIC(),
159 FGIC->GetWBodyFpsIC() );
161 // Set the angular velocities in the instantaneus body frame.
162 VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
164 FGIC->GetRRadpsIC() );
166 // Compute some derived values.
167 vVel = VState.vQtrn.GetTInv()*VState.vUVW;
169 // Finally, make sure that the quaternion stays normalized.
170 VState.vQtrn.Normalize();
172 // Recompute the RunwayRadius level.
173 RecomputeRunwayRadius();
176 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
178 Purpose: Called on a schedule to perform EOM integration
179 Notes: [JB] Run in standalone mode, SeaLevelRadius will be reference radius.
180 In FGFS, SeaLevelRadius is stuffed from FGJSBSim in JSBSim.cxx each pass.
182 At the top of this Run() function, see several "shortcuts" (or, aliases) being
183 set up for use later, rather than using the longer class->function() notation.
185 Here, propagation of state is done using a simple explicit Euler scheme (see the
186 bottom of the function). This propagation is done using the current state values
187 and current derivatives. Based on these values we compute an approximation to the
188 state values for (now + dt).
192 bool FGPropagate::Run(void)
194 if (FGModel::Run()) return true; // Fast return if we have nothing to do ...
195 if (FDMExec->Holding()) return false;
197 RecomputeRunwayRadius();
199 double dt = State->Getdt()*rate; // The 'stepsize'
200 const FGColumnVector3 omega( 0.0, 0.0, Inertial->omega() ); // earth rotation
201 const FGColumnVector3& vForces = Aircraft->GetForces(); // current forces
202 const FGColumnVector3& vMoments = Aircraft->GetMoments(); // current moments
204 double mass = MassBalance->GetMass(); // mass
205 const FGMatrix33& J = MassBalance->GetJ(); // inertia matrix
206 const FGMatrix33& Jinv = MassBalance->GetJinv(); // inertia matrix inverse
207 double r = GetRadius(); // radius
208 if (r == 0.0) {cerr << "radius = 0 !" << endl; r = 1e-16;} // radius check
210 FGColumnVector3 gAccel( 0.0, 0.0, Inertial->GetGAccel(r) );
212 // The rotation matrices:
213 const FGMatrix33& Tl2b = GetTl2b(); // local to body frame
214 const FGMatrix33& Tb2l = GetTb2l(); // body to local frame
215 const FGMatrix33& Tec2l = VState.vLocation.GetTec2l(); // earth centered to local frame
216 const FGMatrix33& Tl2ec = VState.vLocation.GetTl2ec(); // local to earth centered frame
218 // Inertial angular velocity measured in the body frame.
219 const FGColumnVector3 pqri = VState.vPQR + Tl2b*(Tec2l*omega);
221 // Compute vehicle velocity wrt EC frame, expressed in Local horizontal frame.
222 vVel = Tb2l * VState.vUVW;
224 // First compute the time derivatives of the vehicle state values:
226 // Compute body frame rotational accelerations based on the current body moments
227 vPQRdot = Jinv*(vMoments - pqri*(J*pqri));
229 // Compute body frame accelerations based on the current body forces
230 vUVWdot = VState.vUVW*VState.vPQR + vForces/mass;
232 // Coriolis acceleration.
233 FGColumnVector3 ecVel = Tl2ec*vVel;
234 FGColumnVector3 ace = 2.0*omega*ecVel;
235 vUVWdot -= Tl2b*(Tec2l*ace);
237 if (!GroundReactions->GetWOW()) {
238 // Centrifugal acceleration.
239 FGColumnVector3 aeec = omega*(omega*VState.vLocation);
240 vUVWdot -= Tl2b*(Tec2l*aeec);
244 vUVWdot += Tl2b*gAccel;
246 // Compute vehicle velocity wrt EC frame, expressed in EC frame
247 vLocationDot = Tl2ec * vVel;
249 FGColumnVector3 omegaLocal( rInv*vVel(eEast),
251 -rInv*vVel(eEast)*VState.vLocation.GetTanLatitude() );
253 // Compute quaternion orientation derivative on current body rates
254 vQtrndot = VState.vQtrn.GetQDot( VState.vPQR - Tl2b*omegaLocal );
256 // Integrate to propagate the state
258 // Propagate rotational velocity
260 // VState.vPQR += dt*(1.5*vPQRdot - 0.5*last_vPQRdot); // Adams-Bashforth
261 VState.vPQR += (1/12.0)*dt*(23.0*vPQRdot - 16.0*last_vPQRdot + 5.0*last2_vPQRdot); // Adams-Bashforth 3
262 // VState.vPQR += dt*vPQRdot; // Rectangular Euler
263 // VState.vPQR += 0.5*dt*(vPQRdot + last_vPQRdot); // Trapezoidal
265 // Propagate translational velocity
267 // VState.vUVW += dt*(1.5*vUVWdot - 0.5*last_vUVWdot); // Adams Bashforth
268 VState.vUVW += (1/12.0)*dt*(23.0*vUVWdot - 16.0*last_vUVWdot + 5.0*last2_vUVWdot); // Adams-Bashforth 3
269 // VState.vUVW += dt*vUVWdot; // Rectangular Euler
270 // VState.vUVW += 0.5*dt*(vUVWdot + last_vUVWdot); // Trapezoidal
272 // Propagate angular position
274 // VState.vQtrn += dt*(1.5*vQtrndot - 0.5*last_vQtrndot); // Adams Bashforth
275 VState.vQtrn += (1/12.0)*dt*(23.0*vQtrndot - 16.0*last_vQtrndot + 5.0*last2_vQtrndot); // Adams-Bashforth 3
276 // VState.vQtrn += dt*vQtrndot; // Rectangular Euler
277 // VState.vQtrn += 0.5*dt*(vQtrndot + last_vQtrndot); // Trapezoidal
279 // Propagate translational position
281 // VState.vLocation += dt*(1.5*vLocationDot - 0.5*last_vLocationDot); // Adams Bashforth
282 VState.vLocation += (1/12.0)*dt*(23.0*vLocationDot - 16.0*last_vLocationDot + 5.0*last2_vLocationDot); // Adams-Bashforth 3
283 // VState.vLocation += dt*vLocationDot; // Rectangular Euler
284 // VState.vLocation += 0.5*dt*(vLocationDot + last_vLocationDot); // Trapezoidal
288 last2_vPQRdot = last_vPQRdot;
289 last_vPQRdot = vPQRdot;
291 last2_vUVWdot = last_vUVWdot;
292 last_vUVWdot = vUVWdot;
294 last2_vQtrndot = last_vQtrndot;
295 last_vQtrndot = vQtrndot;
297 last2_vLocationDot = last_vLocationDot;
298 last_vLocationDot = vLocationDot;
303 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
305 void FGPropagate::RecomputeRunwayRadius(void)
307 // Get the runway radius.
308 FGLocation contactloc;
310 FGGroundCallback* gcb = FDMExec->GetGroundCallback();
311 double t = State->Getsim_time();
312 gcb->GetAGLevel(t, VState.vLocation, contactloc, dv, dv);
313 RunwayRadius = contactloc.GetRadius();
316 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
318 void FGPropagate::Seth(double tt)
320 VState.vLocation.SetRadius( tt + SeaLevelRadius );
323 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
325 double FGPropagate::GetRunwayRadius(void) const
330 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332 double FGPropagate::GetDistanceAGL(void) const
334 return VState.vLocation.GetRadius() - RunwayRadius;
337 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
339 void FGPropagate::SetDistanceAGL(double tt)
341 VState.vLocation.SetRadius( tt + RunwayRadius );
344 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346 void FGPropagate::bind(void)
348 typedef double (FGPropagate::*PMF)(int) const;
349 PropertyManager->Tie("velocities/h-dot-fps", this, &FGPropagate::Gethdot);
351 PropertyManager->Tie("velocities/v-north-fps", this, eNorth, (PMF)&FGPropagate::GetVel);
352 PropertyManager->Tie("velocities/v-east-fps", this, eEast, (PMF)&FGPropagate::GetVel);
353 PropertyManager->Tie("velocities/v-down-fps", this, eDown, (PMF)&FGPropagate::GetVel);
355 PropertyManager->Tie("velocities/u-fps", this, eU, (PMF)&FGPropagate::GetUVW);
356 PropertyManager->Tie("velocities/v-fps", this, eV, (PMF)&FGPropagate::GetUVW);
357 PropertyManager->Tie("velocities/w-fps", this, eW, (PMF)&FGPropagate::GetUVW);
359 PropertyManager->Tie("velocities/p-rad_sec", this, eP, (PMF)&FGPropagate::GetPQR);
360 PropertyManager->Tie("velocities/q-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQR);
361 PropertyManager->Tie("velocities/r-rad_sec", this, eR, (PMF)&FGPropagate::GetPQR);
363 PropertyManager->Tie("accelerations/pdot-rad_sec", this, eP, (PMF)&FGPropagate::GetPQRdot);
364 PropertyManager->Tie("accelerations/qdot-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQRdot);
365 PropertyManager->Tie("accelerations/rdot-rad_sec", this, eR, (PMF)&FGPropagate::GetPQRdot);
367 PropertyManager->Tie("accelerations/udot-fps", this, eU, (PMF)&FGPropagate::GetUVWdot);
368 PropertyManager->Tie("accelerations/vdot-fps", this, eV, (PMF)&FGPropagate::GetUVWdot);
369 PropertyManager->Tie("accelerations/wdot-fps", this, eW, (PMF)&FGPropagate::GetUVWdot);
371 PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::Geth, &FGPropagate::Seth, true);
372 PropertyManager->Tie("position/lat-gc-rad", this, &FGPropagate::GetLatitude, &FGPropagate::SetLatitude);
373 PropertyManager->Tie("position/long-gc-rad", this, &FGPropagate::GetLongitude, &FGPropagate::SetLongitude);
374 PropertyManager->Tie("position/h-agl-ft", this, &FGPropagate::GetDistanceAGL, &FGPropagate::SetDistanceAGL);
375 PropertyManager->Tie("position/radius-to-vehicle-ft", this, &FGPropagate::GetRadius);
377 PropertyManager->Tie("metrics/runway-radius", this, &FGPropagate::GetRunwayRadius);
379 PropertyManager->Tie("attitude/phi-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
380 PropertyManager->Tie("attitude/theta-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
381 PropertyManager->Tie("attitude/psi-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
383 PropertyManager->Tie("attitude/roll-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
384 PropertyManager->Tie("attitude/pitch-rad", this, (int)eTht, (PMF)&FGPropagate::GetEuler);
385 PropertyManager->Tie("attitude/heading-true-rad", this, (int)ePsi, (PMF)&FGPropagate::GetEuler);
388 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390 void FGPropagate::unbind(void)
392 PropertyManager->Untie("velocities/v-north-fps");
393 PropertyManager->Untie("velocities/v-east-fps");
394 PropertyManager->Untie("velocities/v-down-fps");
395 PropertyManager->Untie("velocities/h-dot-fps");
396 PropertyManager->Untie("velocities/u-fps");
397 PropertyManager->Untie("velocities/v-fps");
398 PropertyManager->Untie("velocities/w-fps");
399 PropertyManager->Untie("velocities/p-rad_sec");
400 PropertyManager->Untie("velocities/q-rad_sec");
401 PropertyManager->Untie("velocities/r-rad_sec");
402 PropertyManager->Untie("accelerations/udot-fps");
403 PropertyManager->Untie("accelerations/vdot-fps");
404 PropertyManager->Untie("accelerations/wdot-fps");
405 PropertyManager->Untie("accelerations/pdot-rad_sec");
406 PropertyManager->Untie("accelerations/qdot-rad_sec");
407 PropertyManager->Untie("accelerations/rdot-rad_sec");
408 PropertyManager->Untie("position/h-sl-ft");
409 PropertyManager->Untie("position/lat-gc-rad");
410 PropertyManager->Untie("position/long-gc-rad");
411 PropertyManager->Untie("position/h-agl-ft");
412 PropertyManager->Untie("position/radius-to-vehicle-ft");
413 PropertyManager->Untie("metrics/runway-radius");
414 PropertyManager->Untie("attitude/phi-rad");
415 PropertyManager->Untie("attitude/theta-rad");
416 PropertyManager->Untie("attitude/psi-rad");
417 PropertyManager->Untie("attitude/roll-rad");
418 PropertyManager->Untie("attitude/pitch-rad");
419 PropertyManager->Untie("attitude/heading-true-rad");
422 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
423 // The bitmasked value choices are as follows:
424 // unset: In this case (the default) JSBSim would only print
425 // out the normally expected messages, essentially echoing
426 // the config files as they are read. If the environment
427 // variable is not set, debug_lvl is set to 1 internally
428 // 0: This requests JSBSim not to output any messages
430 // 1: This value explicity requests the normal JSBSim
432 // 2: This value asks for a message to be printed out when
433 // a class is instantiated
434 // 4: When this value is set, a message is displayed when a
435 // FGModel object executes its Run() method
436 // 8: When this value is set, various runtime state variables
437 // are printed out periodically
438 // 16: When set various parameters are sanity checked and
439 // a message is printed out when they go out of bounds
441 void FGPropagate::Debug(int from)
443 if (debug_lvl <= 0) return;
445 if (debug_lvl & 1) { // Standard console startup message output
446 if (from == 0) { // Constructor
450 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
451 if (from == 0) cout << "Instantiated: FGPropagate" << endl;
452 if (from == 1) cout << "Destroyed: FGPropagate" << endl;
454 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
456 if (debug_lvl & 8 ) { // Runtime state variables
458 if (debug_lvl & 16) { // Sanity checking
460 if (debug_lvl & 64) {
461 if (from == 0) { // Constructor
462 cout << IdSrc << endl;
463 cout << IdHdr << endl;