1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4 Author: Jon S. Berndt, Mathias Froehlich
5 Date started: 04/04/2004
7 ------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) ------------------
8 ------- (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) ----
9 ------- (C) 2011 Ola Røer Thorsen (ola@silentwings.no) -----------
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.
29 -------------------------------------------------------------------------------
30 04/04/2004 MF Created from code previously in the old positions class.
31 11/01/2011 ORT Encapsulated ground callback code in FGLocation and removed
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
41 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
45 #include "FGJSBBase.h"
46 #include "input_output/FGPropertyManager.h"
47 #include "FGColumnVector3.h"
48 #include "FGMatrix33.h"
49 #include "input_output/FGGroundCallback.h"
51 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
55 #define ID_LOCATION "$Id: FGLocation.h,v 1.29 2011/11/06 18:14:51 bcoconni Exp $"
57 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
59 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
63 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
67 /** FGLocation holds an arbitrary location in the Earth centered Earth fixed
68 reference frame (ECEF). This coordinate frame has its center in the middle
69 of the earth. The X-axis points from the center of the Earth towards a
70 location with zero latitude and longitude on the Earth surface. The Y-axis
71 points from the center of the Earth towards a location with zero latitude
72 and 90 deg East longitude on the Earth surface. The Z-axis points from the
73 Earth center to the geographic north pole.
75 This class provides access functions to set and get the location as either
76 the simple X, Y and Z values in ft or longitude/latitude and the radial
77 distance of the location from the Earth center.
79 It is common to associate a parent frame with a location. This frame is
80 usually called the local horizontal frame or simply the local frame. It is
81 also called the NED frame (North, East, Down), as well as the Navigation
82 frame. This frame has its X/Y plane parallel to the surface of the Earth
83 (with the assumption of a spherical Earth). The X-axis points towards north,
84 the Y-axis points east and the Z-axis points to the center of the Earth.
86 Since the local frame is determined by the location (and NOT by the
87 orientation of the vehicle IN any frame), this class also provides the
88 rotation matrices required to transform from the Earth centered (ECEF) frame
89 to the local horizontal frame and back. This class also "owns" the
90 transformations that go from the inertial frame (Earth-centered Inertial, or
91 ECI) to and from the ECEF frame, as well as to and from the local frame.
92 Again, this is because the ECI, ECEF, and local frames do not involve the
93 actual orientation of the vehicle - only the location on the Earth surface,
94 and the angular difference between the ECI and ECEF frames. There are
95 conversion functions for conversion of position vectors given in the one
96 frame to positions in the other frame.
98 The Earth centered reference frame is NOT an inertial frame since it rotates
101 The coordinates in the Earth centered frame are the master values. All other
102 values are computed from these master values and are cached as long as the
103 location is changed by access through a non-const member function. Values
104 are cached to improve performance. It is best practice to work with a
105 natural set of master values. Other parameters that are derived from these
106 master values are calculated only when needed, and IF they are needed and
107 calculated, then they are cached (stored and remembered) so they do not need
108 to be re-calculated until the master values they are derived from are
109 themselves changed (and become stale).
111 Accuracy and round off
115 -that we model a vehicle near the Earth
116 -that the Earth surface radius is about 2*10^7, ft
117 -that we use double values for the representation of the location
119 we have an accuracy of about
121 1e-16*2e7ft/1 = 2e-9 ft
123 left. This should be sufficient for our needs. Note that this is the same
124 relative accuracy we would have when we compute directly with
125 lon/lat/radius. For the radius value this is clear. For the lon/lat pair
126 this is easy to see. Take for example KSFO located at about 37.61 deg north
127 122.35 deg west, which corresponds to 0.65642 rad north and 2.13541 rad
128 west. Both values are of magnitude of about 1. But 1 ft corresponds to about
129 1/(2e7*2*pi) = 7.9577e-09 rad. So the left accuracy with this representation
130 is also about 1*1e-16/7.9577e-09 = 1.2566e-08 which is of the same magnitude
131 as the representation chosen here.
133 The advantage of this representation is that it is a linear space without
134 singularities. The singularities are the north and south pole and most
135 notably the non-steady jump at -pi to pi. It is harder to track this jump
136 correctly especially when we need to work with error norms and derivatives
137 of the equations of motion within the time-stepping code. Also, the rate of
138 change is of the same magnitude for all components in this representation
139 which is an advantage for numerical stability in implicit time-stepping.
141 Note: The latitude is a GEOCENTRIC value. FlightGear converts latitude to a
142 geodetic value and uses that. In order to get best matching relative to a
143 map, geocentric latitude must be converted to geodetic.
145 @see Stevens and Lewis, "Aircraft Control and Simulation", Second edition
146 @see W. C. Durham "Aircraft Dynamics & Control", section 2.2
148 @author Mathias Froehlich
149 @version $Id: FGLocation.h,v 1.29 2011/11/06 18:14:51 bcoconni Exp $
152 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
156 class FGLocation : public FGJSBBase
159 /** Default constructor. */
162 /** Constructor to set the longitude, latitude and the distance
163 from the center of the earth.
165 @param lat GEOCENTRIC latitude
166 @param radius distance from center of earth to vehicle in feet*/
167 FGLocation(double lon, double lat, double radius);
169 /** Column constructor. */
170 FGLocation(const FGColumnVector3& lv);
172 /** Copy constructor. */
173 FGLocation(const FGLocation& l);
175 /** Set the longitude.
176 @param longitude Longitude in rad to set.
177 Sets the longitude of the location represented with this class
178 instance to the value of the given argument. The value is meant
179 to be in rad. The latitude and the radius value are preserved
180 with this call with the exception of radius being equal to
181 zero. If the radius is previously set to zero it is changed to be
182 equal to 1.0 past this call. Longitude is positive east and negative west. */
183 void SetLongitude(double longitude);
185 /** Set the latitude.
186 @param latitude Latitude in rad to set.
187 Sets the latitude of the location represented with this class
188 instance to the value of the given argument. The value is meant
189 to be in rad. The longitude and the radius value are preserved
190 with this call with the exception of radius being equal to
191 zero. If the radius is previously set to zero it is changed to be
192 equal to 1.0 past this call.
193 Latitude is positive north and negative south.
194 The arguments should be within the bounds of -pi/2 <= lat <= pi/2.
195 The behavior of this function with arguments outside this range is
196 left as an exercise to the gentle reader ... */
197 void SetLatitude(double latitude);
199 /** Set the distance from the center of the earth.
200 @param radius Radius in ft to set.
201 Sets the radius of the location represented with this class
202 instance to the value of the given argument. The value is meant
203 to be in ft. The latitude and longitude values are preserved
204 with this call with the exception of radius being equal to
205 zero. If the radius is previously set to zero, latitude and
206 longitude is set equal to zero past this call.
207 The argument should be positive.
208 The behavior of this function called with a negative argument is
209 left as an exercise to the gentle reader ... */
210 void SetRadius(double radius);
212 /** Sets the longitude, latitude and the distance from the center of the earth.
213 @param lon longitude in radians
214 @param lat GEOCENTRIC latitude in radians
215 @param radius distance from center of earth to vehicle in feet*/
216 void SetPosition(double lon, double lat, double radius);
218 /** Sets the longitude, latitude and the distance above the reference ellipsoid.
219 @param lon longitude in radians
220 @param lat GEODETIC latitude in radians
221 @param height distance above the reference ellipsoid to vehicle in feet*/
222 void SetPositionGeodetic(double lon, double lat, double height);
224 /** Sets the semimajor and semiminor axis lengths for this planet.
225 The eccentricity and flattening are calculated from the semimajor
226 and semiminor axis lengths */
227 void SetEllipse(double semimajor, double semiminor);
229 /** Sets the Earth position angle.
230 This is the relative orientation of the ECEF frame with respect to the
232 @param EPA Earth fixed frame (ECEF) rotation offset about the axis with
233 respect to the Inertial (ECI) frame in radians. */
234 void SetEarthPositionAngle(double EPA) {epa = EPA; mCacheValid = false;}
236 /** Increments the Earth position angle.
237 This is the relative orientation of the ECEF frame with respect to the
239 @param delta delta to the Earth fixed frame (ECEF) rotation offset about the axis with
240 respect to the Inertial (ECI) frame in radians. */
241 void IncrementEarthPositionAngle(double delta) {epa += delta; mCacheValid = false;}
243 /** Get the longitude.
244 @return the longitude in rad of the location represented with this
245 class instance. The returned values are in the range between
246 -pi <= lon <= pi. Longitude is positive east and negative west. */
247 double GetLongitude() const { ComputeDerived(); return mLon; }
249 /** Get the longitude.
250 @return the longitude in deg of the location represented with this
251 class instance. The returned values are in the range between
252 -180 <= lon <= 180. Longitude is positive east and negative west. */
253 double GetLongitudeDeg() const { ComputeDerived(); return radtodeg*mLon; }
255 /** Get the sine of Longitude. */
256 double GetSinLongitude() const { ComputeDerived(); return -mTec2l(2,1); }
258 /** Get the cosine of Longitude. */
259 double GetCosLongitude() const { ComputeDerived(); return mTec2l(2,2); }
261 /** Get the latitude.
262 @return the latitude in rad of the location represented with this
263 class instance. The returned values are in the range between
264 -pi/2 <= lon <= pi/2. Latitude is positive north and negative south. */
265 double GetLatitude() const { ComputeDerived(); return mLat; }
267 /** Get the geodetic latitude.
268 @return the geodetic latitude in rad of the location represented with this
269 class instance. The returned values are in the range between
270 -pi/2 <= lon <= pi/2. Latitude is positive north and negative south. */
271 double GetGeodLatitudeRad(void) const { ComputeDerived(); return mGeodLat; }
273 /** Get the latitude.
274 @return the latitude in deg of the location represented with this
275 class instance. The returned value is in the range between
276 -90 <= lon <= 90. Latitude is positive north and negative south. */
277 double GetLatitudeDeg() const { ComputeDerived(); return radtodeg*mLat; }
279 /** Get the geodetic latitude in degrees.
280 @return the geodetic latitude in degrees of the location represented by
281 this class instance. The returned value is in the range between
282 -90 <= lon <= 90. Latitude is positive north and negative south. */
283 double GetGeodLatitudeDeg(void) const { ComputeDerived(); return radtodeg*mGeodLat; }
285 /** Gets the geodetic altitude in feet. */
286 double GetGeodAltitude(void) const {ComputeDerived(); return GeodeticAltitude;}
288 /** Get the sine of Latitude. */
289 double GetSinLatitude() const { ComputeDerived(); return -mTec2l(3,3); }
291 /** Get the cosine of Latitude. */
292 double GetCosLatitude() const { ComputeDerived(); return mTec2l(1,3); }
294 /** Get the cosine of Latitude. */
295 double GetTanLatitude() const {
297 double cLat = mTec2l(1,3);
301 return -mTec2l(3,3)/cLat;
304 double GetEPA() const {return epa;}
306 /** Get the distance from the center of the earth.
307 @return the distance of the location represented with this class
308 instance to the center of the earth in ft. The radius value is
310 //double GetRadius() const { return mECLoc.Magnitude(); } // may not work with FlightGear
311 double GetRadius() const { ComputeDerived(); return mRadius; }
313 /// @name Functions that need the ground callback to be set
315 /** Set the altitude above sea level.
316 @param altitudeASL altitude above Sea Level in feet. */
317 void SetAltitudeASL(double altitudeASL)
318 { SetRadius(GroundCallback->GetSeaLevelRadius(*this) + altitudeASL); }
320 /** Set the altitude above ground level.
321 @param altitudeAGL altitude above Ground Level in feet. */
322 void SetAltitudeAGL(double altitudeAGL, double time)
323 { SetRadius(GroundCallback->GetTerrainGeoCentRadius(time, *this) + altitudeAGL); }
325 /** Get the local sea level radius
326 @return the sea level radius at the location in feet. */
327 double GetSeaLevelRadius(void) const
328 { ComputeDerived(); return GroundCallback->GetSeaLevelRadius(*this); }
330 /** Get the local terrain radius
331 @return the terrain level radius at the location in feet. */
332 double GetTerrainRadius(double time) const
333 { ComputeDerived(); return GroundCallback->GetTerrainGeoCentRadius(time, *this); }
335 /** Get the altitude above sea level.
336 @return the altitude ASL in feet. */
337 double GetAltitudeASL() const
338 { ComputeDerived(); return GroundCallback->GetAltitude(*this); }
340 /** Get the altitude above ground level.
341 @return the altitude AGL in feet. */
342 double GetAltitudeAGL(double time) const {
344 FGColumnVector3 n,v,w;
345 return GetContactPoint(time,c,n,v,w);
348 /** Get terrain contact point information below the current location.
349 @param time Simulation time
350 @param contact Contact point location
351 @param normal Terrain normal vector in contact point (ECEF frame)
352 @param v Terrain linear velocity in contact point (ECEF frame)
353 @param w Terrain angular velocity in contact point (ECEF frame)
354 @return Location altitude above contact point (AGL) in feet. */
355 double GetContactPoint(double time,
356 FGLocation& contact, FGColumnVector3& normal,
357 FGColumnVector3& v, FGColumnVector3& w) const
358 { ComputeDerived(); return GroundCallback->GetAGLevel(time, *this, contact, normal, v, w); }
360 /** Sets the ground callback pointer. For optimal memory management, a shared
361 pointer is used internally that maintains a reference counter. The calling
362 application must therefore use FGGroundCallback_ptr 'smart pointers' to
363 manage their copy of the ground callback.
364 @param gc A pointer to a ground callback object
365 @see FGGroundCallback
367 static void SetGroundCallback(FGGroundCallback* gc) { GroundCallback = gc; }
369 /** Get a pointer to the ground callback currently used. It is recommanded
370 to store the returned pointer in a 'smart pointer' FGGroundCallback_ptr.
371 @return A pointer to the current ground callback object.
372 @see FGGroundCallback
374 static FGGroundCallback* GetGroundCallback(void) { return GroundCallback; }
377 /** Transform matrix from local horizontal to earth centered frame.
378 Returns a const reference to the rotation matrix of the transform from
379 the local horizontal frame to the earth centered frame. */
380 const FGMatrix33& GetTl2ec(void) const { ComputeDerived(); return mTl2ec; }
382 /** Transform matrix from the earth centered to local horizontal frame.
383 Returns a const reference to the rotation matrix of the transform from
384 the earth centered frame to the local horizontal frame. */
385 const FGMatrix33& GetTec2l(void) const { ComputeDerived(); return mTec2l; }
387 /** Transform matrix from inertial to earth centered frame.
388 Returns a const reference to the rotation matrix of the transform from
389 the inertial frame to the earth centered frame (ECI to ECEF). */
390 const FGMatrix33& GetTi2ec(void);
392 /** Transform matrix from the earth centered to inertial frame.
393 Returns a const reference to the rotation matrix of the transform from
394 the earth centered frame to the inertial frame (ECEF to ECI). */
395 const FGMatrix33& GetTec2i(void);
397 const FGMatrix33& GetTi2l(void) const {ComputeDerived(); return mTi2l;}
399 const FGMatrix33& GetTl2i(void) const {ComputeDerived(); return mTl2i;}
401 /** Conversion from Local frame coordinates to a location in the
402 earth centered and fixed frame.
403 @param lvec Vector in the local horizontal coordinate frame
404 @return The location in the earth centered and fixed frame */
405 FGLocation LocalToLocation(const FGColumnVector3& lvec) const {
406 ComputeDerived(); return mTl2ec*lvec + mECLoc;
409 /** Conversion from a location in the earth centered and fixed frame
410 to local horizontal frame coordinates.
411 @param ecvec Vector in the earth centered and fixed frame
412 @return The vector in the local horizontal coordinate frame */
413 FGColumnVector3 LocationToLocal(const FGColumnVector3& ecvec) const {
414 ComputeDerived(); return mTec2l*(ecvec - mECLoc);
417 // For time-stepping, locations have vector properties...
419 /** Read access the entries of the vector.
420 @param idx the component index.
421 Return the value of the matrix entry at the given index.
422 Indices are counted starting with 1.
423 Note that the index given in the argument is unchecked. */
424 double operator()(unsigned int idx) const { return mECLoc.Entry(idx); }
426 /** Write access the entries of the vector.
427 @param idx the component index.
428 @return a reference to the vector entry at the given index.
429 Indices are counted starting with 1.
430 Note that the index given in the argument is unchecked. */
431 double& operator()(unsigned int idx) { mCacheValid = false; return mECLoc.Entry(idx); }
433 /** Read access the entries of the vector.
434 @param idx the component index.
435 @return the value of the matrix entry at the given index.
436 Indices are counted starting with 1.
437 This function is just a shortcut for the <tt>double
438 operator()(unsigned int idx) const</tt> function. It is
439 used internally to access the elements in a more convenient way.
440 Note that the index given in the argument is unchecked. */
441 double Entry(unsigned int idx) const { return mECLoc.Entry(idx); }
443 /** Write access the entries of the vector.
444 @param idx the component index.
445 @return a reference to the vector entry at the given index.
446 Indices are counted starting with 1.
447 This function is just a shortcut for the double&
448 operator()(unsigned int idx) function. It is
449 used internally to access the elements in a more convenient way.
450 Note that the index given in the argument is unchecked. */
451 double& Entry(unsigned int idx) {
452 mCacheValid = false; return mECLoc.Entry(idx);
455 /** Sets this location via the supplied vector.
456 The location can be set by an Earth-centered, Earth-fixed (ECEF) frame
457 position vector. The cache is marked as invalid, so any future requests
458 for selected important data will cause the parameters to be calculated.
459 @param v the ECEF column vector in feet.
460 @return a reference to the FGLocation object. */
461 const FGLocation& operator=(const FGColumnVector3& v)
471 /** Sets this location via the supplied location object.
472 @param v A location object reference.
473 @return a reference to the FGLocation object. */
474 const FGLocation& operator=(const FGLocation& l);
476 /** This operator returns true if the ECEF location vectors for the two
477 location objects are equal. */
478 bool operator==(const FGLocation& l) const {
479 return mECLoc == l.mECLoc;
482 /** This operator returns true if the ECEF location vectors for the two
483 location objects are not equal. */
484 bool operator!=(const FGLocation& l) const { return ! operator==(l); }
486 /** This operator adds the ECEF position vectors.
487 The supplied vector (right side) is added to the ECEF position vector
488 on the left side of the equality, and a pointer to this object is
490 const FGLocation& operator+=(const FGLocation &l) {
496 const FGLocation& operator-=(const FGLocation &l) {
502 const FGLocation& operator*=(double scalar) {
508 const FGLocation& operator/=(double scalar) {
509 return operator*=(1.0/scalar);
512 FGLocation operator+(const FGLocation& l) const {
513 return FGLocation(mECLoc + l.mECLoc);
516 FGLocation operator-(const FGLocation& l) const {
517 return FGLocation(mECLoc - l.mECLoc);
520 FGLocation operator*(double scalar) const {
521 return FGLocation(scalar*mECLoc);
524 /** Cast to a simple 3d vector */
525 operator const FGColumnVector3&() const {
530 /** Computation of derived values.
531 This function re-computes the derived values like lat/lon and
532 transformation matrices. It does this unconditionally. */
533 void ComputeDerivedUnconditional(void) const;
535 /** Computation of derived values.
536 This function checks if the derived values like lat/lon and
537 transformation matrices are already computed. If so, it
538 returns. If they need to be computed this is done here. */
539 void ComputeDerived(void) const {
541 ComputeDerivedUnconditional();
544 /** The coordinates in the earth centered frame. This is the master copy.
545 The coordinate frame has its center in the middle of the earth.
546 Its x-axis points from the center of the earth towards a
547 location with zero latitude and longitude on the earths
548 surface. The y-axis points from the center of the earth towards a
549 location with zero latitude and 90deg longitude on the earths
550 surface. The z-axis points from the earths center to the
551 geographic north pole.
552 @see W. C. Durham "Aircraft Dynamics & Control", section 2.2 */
553 FGColumnVector3 mECLoc;
555 /** The cached lon/lat/radius values. */
558 mutable double mRadius;
559 mutable double mGeodLat;
560 mutable double GeodeticAltitude;
562 double initial_longitude;
564 /** The cached rotation matrices from and to the associated frames. */
565 mutable FGMatrix33 mTl2ec;
566 mutable FGMatrix33 mTec2l;
567 mutable FGMatrix33 mTi2ec;
568 mutable FGMatrix33 mTec2i;
569 mutable FGMatrix33 mTi2l;
570 mutable FGMatrix33 mTl2i;
574 /* Terms for geodetic latitude calculation. Values are from WGS84 model */
575 double a; // Earth semimajor axis in feet (6,378,137.0 meters)
576 double b; // Earth semiminor axis in feet (6,356,752.3142 meters)
579 double e; // Earth eccentricity
580 double e2; // Earth eccentricity squared
582 double f; // Flattening
584 /** A data validity flag.
585 This class implements caching of the derived values like the
586 orthogonal rotation matrices or the lon/lat/radius values. For caching we
587 carry a flag which signals if the values are valid or not.
588 The C++ keyword "mutable" tells the compiler that the data member is
589 allowed to change during a const member function. */
590 mutable bool mCacheValid;
592 /** The ground callback object pointer */
593 static FGGroundCallback_ptr GroundCallback;
596 /** Scalar multiplication.
598 @param scalar scalar value to multiply with.
599 @param l Vector to multiply.
601 Multiply the Vector with a scalar value. */
602 inline FGLocation operator*(double scalar, const FGLocation& l)
604 return l.operator*(scalar);
607 } // namespace JSBSim
609 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%