]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/math/FGLocation.h
Merge commit 'refs/merge-requests/14' of git://gitorious.org/fg/flightgear into next
[flightgear.git] / src / FDM / JSBSim / math / FGLocation.h
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3  Header:       FGLocation.h
4  Author:       Jon S. Berndt, Mathias Froehlich
5  Date started: 04/04/2004
6
7  ------- Copyright (C) 1999  Jon S. Berndt (jon@jsbsim.org) ------------------
8  -------           (C) 2004  Mathias Froehlich (Mathias.Froehlich@web.de) ----
9
10  This program is free software; you can redistribute it and/or modify it under
11  the terms of the GNU Lesser General Public License as published by the Free Software
12  Foundation; either version 2 of the License, or (at your option) any later
13  version.
14
15  This program is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
18  details.
19
20  You should have received a copy of the GNU Lesser General Public License along with
21  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22  Place - Suite 330, Boston, MA  02111-1307, USA.
23
24  Further information about the GNU Lesser General Public License can also be found on
25  the world wide web at http://www.gnu.org.
26
27 HISTORY
28 -------------------------------------------------------------------------------
29 04/04/2004   MF   Created from code previously in the old positions class.
30
31 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32 SENTRY
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
34
35 #ifndef FGLOCATION_H
36 #define FGLOCATION_H
37
38 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39 INCLUDES
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
41
42 #include "FGJSBBase.h"
43 #include "input_output/FGPropertyManager.h"
44 #include "FGColumnVector3.h"
45 #include "FGMatrix33.h"
46
47 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48 DEFINITIONS
49 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
50
51 #define ID_LOCATION "$Id: FGLocation.h,v 1.25 2010/09/18 22:47:24 jberndt Exp $"
52
53 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54 FORWARD DECLARATIONS
55 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
56
57 namespace JSBSim {
58
59 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60 CLASS DOCUMENTATION
61 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
62
63 /** FGLocation holds an arbitrary location in the Earth centered Earth fixed
64     reference frame (ECEF). This coordinate frame has its center in the middle
65     of the earth. The X-axis points from the center of the Earth towards a
66     location with zero latitude and longitude on the Earth surface. The Y-axis
67     points from the center of the Earth towards a location with zero latitude
68     and 90 deg East longitude on the Earth surface. The Z-axis points from the
69     Earth center to the geographic north pole.
70
71     This class provides access functions to set and get the location as either
72     the simple X, Y and Z values in ft or longitude/latitude and the radial
73     distance of the location from the Earth center.
74
75     It is common to associate a parent frame with a location. This frame is
76     usually called the local horizontal frame or simply the local frame. It is
77     also called the NED frame (North, East, Down), as well as the Navigation
78     frame. This frame has its X/Y plane parallel to the surface of the Earth
79     (with the assumption of a spherical Earth). The X-axis points towards north,
80     the Y-axis points east and the Z-axis points to the center of the Earth.
81
82     Since the local frame is determined by the location (and NOT by the
83     orientation of the  vehicle IN any frame), this class also provides the
84     rotation matrices required to transform from the Earth centered (ECEF) frame
85     to the local horizontal frame and back. This class also "owns" the
86     transformations that go from the inertial frame (Earth-centered Inertial, or
87     ECI) to and from the ECEF frame, as well as to and from the local frame.
88     Again, this is because the ECI, ECEF, and local frames do not involve the
89     actual orientation of the vehicle - only the location on the Earth surface,
90     and the angular difference between the ECI and ECEF frames. There are
91     conversion functions for conversion of position vectors given in the one
92     frame to positions in the other frame.
93
94     The Earth centered reference frame is NOT an inertial frame since it rotates
95     with the Earth.
96
97     The coordinates in the Earth centered frame are the master values. All other
98     values are computed from these master values and are cached as long as the
99     location is changed by access through a non-const member function. Values
100     are cached to improve performance. It is best practice to work with a
101     natural set of master values. Other parameters that are derived from these
102     master values are calculated only when needed, and IF they are needed and
103     calculated, then they are cached (stored and remembered) so they do not need
104     to be re-calculated until the master values they are derived from are
105     themselves changed (and become stale).
106
107     Accuracy and round off
108
109     Given,
110
111     -that we model a vehicle near the Earth
112     -that the Earth surface radius is about 2*10^7, ft
113     -that we use double values for the representation of the location
114     
115     we have an accuracy of about
116     
117     1e-16*2e7ft/1 = 2e-9 ft
118     
119     left. This should be sufficient for our needs. Note that this is the same
120     relative accuracy we would have when we compute directly with
121     lon/lat/radius. For the radius value this is clear. For the lon/lat pair
122     this is easy to see. Take for example KSFO located at about 37.61 deg north
123     122.35 deg west, which corresponds to 0.65642 rad north and 2.13541 rad
124     west. Both values are of magnitude of about 1. But 1 ft corresponds to about
125     1/(2e7*2*pi) = 7.9577e-09 rad. So the left accuracy with this representation
126     is also about 1*1e-16/7.9577e-09 = 1.2566e-08 which is of the same magnitude
127     as the representation chosen here.
128
129     The advantage of this representation is that it is a linear space without
130     singularities. The singularities are the north and south pole and most
131     notably the non-steady jump at -pi to pi. It is harder to track this jump
132     correctly especially when we need to work with error norms and derivatives
133     of the equations of motion within the time-stepping code. Also, the rate of
134     change is of the same magnitude for all components in this representation
135     which is an advantage for numerical stability in implicit time-stepping.
136
137     Note: The latitude is a GEOCENTRIC value. FlightGear converts latitude to a
138     geodetic value and uses that. In order to get best matching relative to a
139     map, geocentric latitude must be converted to geodetic.
140
141     @see Stevens and Lewis, "Aircraft Control and Simulation", Second edition
142     @see W. C. Durham "Aircraft Dynamics & Control", section 2.2
143
144     @author Mathias Froehlich
145     @version $Id: FGLocation.h,v 1.25 2010/09/18 22:47:24 jberndt Exp $
146   */
147
148 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149 CLASS DECLARATION
150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
151
152 class FGLocation : virtual FGJSBBase
153 {
154 public:
155   /** Default constructor. */
156   FGLocation(void);
157
158   /** Constructor to set the longitude, latitude and the distance
159       from the center of the earth.
160       @param lon longitude
161       @param lat GEOCENTRIC latitude
162       @param radius distance from center of earth to vehicle in feet*/
163   FGLocation(double lon, double lat, double radius);
164
165   /** Column constructor. */
166   FGLocation(const FGColumnVector3& lv) : mECLoc(lv), mCacheValid(false)
167   {
168     a = 0.0;
169     b = 0.0;
170     a2 = 0.0;
171     b2 = 0.0;
172     e2 = 1.0;
173     e = 1.0;
174     eps2 = -1.0;
175     f = 1.0;
176   }
177
178   /** Copy constructor. */
179   FGLocation(const FGLocation& l)
180     : mECLoc(l.mECLoc), mCacheValid(l.mCacheValid)
181   {
182 //    if (!mCacheValid) return; // This doesn't seem right.
183
184     mLon = l.mLon;
185     mLat = l.mLat;
186     mRadius = l.mRadius;
187
188     mTl2ec = l.mTl2ec;
189     mTec2l = l.mTec2l;
190
191     a = l.a;
192     b = l.b;
193     a2 = l.a2;
194     b2 = l.b2;
195     e2 = l.e2;
196     e = l.e;
197     eps2 = l.eps2;
198     f = l.f;
199
200     initial_longitude = l.initial_longitude;
201   }
202
203   /** Set the longitude.
204       @param longitude Longitude in rad to set.
205       Sets the longitude of the location represented with this class
206       instance to the value of the given argument. The value is meant
207       to be in rad. The latitude and the radius value are preserved
208       with this call with the exception of radius being equal to
209       zero. If the radius is previously set to zero it is changed to be
210       equal to 1.0 past this call. Longitude is positive east and negative west. */
211   void SetLongitude(double longitude);
212
213   /** Set the latitude.
214       @param latitude Latitude in rad to set.
215       Sets the latitude of the location represented with this class
216       instance to the value of the given argument. The value is meant
217       to be in rad. The longitude and the radius value are preserved
218       with this call with the exception of radius being equal to
219       zero. If the radius is previously set to zero it is changed to be
220       equal to 1.0 past this call.
221       Latitude is positive north and negative south.
222       The arguments should be within the bounds of -pi/2 <= lat <= pi/2.
223       The behavior of this function with arguments outside this range is
224       left as an exercise to the gentle reader ... */
225   void SetLatitude(double latitude);
226
227   /** Set the distance from the center of the earth.
228       @param radius Radius in ft to set.
229       Sets the radius of the location represented with this class
230       instance to the value of the given argument. The value is meant
231       to be in ft. The latitude and longitude values are preserved
232       with this call with the exception of radius being equal to
233       zero. If the radius is previously set to zero, latitude and
234       longitude is set equal to zero past this call.
235       The argument should be positive.
236       The behavior of this function called with a negative argument is
237       left as an exercise to the gentle reader ... */
238   void SetRadius(double radius);
239
240   /** Sets the longitude, latitude and the distance from the center of the earth.
241       @param lon longitude in radians
242       @param lat GEOCENTRIC latitude in radians
243       @param radius distance from center of earth to vehicle in feet*/
244   void SetPosition(double lon, double lat, double radius);
245
246   /** Sets the longitude, latitude and the distance above the reference ellipsoid.
247       @param lon longitude in radians
248       @param lat GEODETIC latitude in radians
249       @param height distance above the reference ellipsoid to vehicle in feet*/
250   void SetPositionGeodetic(double lon, double lat, double height);
251
252   /** Sets the semimajor and semiminor axis lengths for this planet.
253       The eccentricity and flattening are calculated from the semimajor
254       and semiminor axis lengths */
255   void SetEllipse(double semimajor, double semiminor);
256
257   /** Sets the Earth position angle.
258       This is the relative orientation of the ECEF frame with respect to the
259       Inertial frame.
260       @param EPA Earth fixed frame (ECEF) rotation offset about the axis with
261                  respect to the Inertial (ECI) frame in radians. */
262   void SetEarthPositionAngle(double EPA) {epa = EPA; mCacheValid = false;}
263
264   /** Get the longitude.
265       @return the longitude in rad of the location represented with this
266       class instance. The returned values are in the range between
267       -pi <= lon <= pi. Longitude is positive east and negative west. */
268   double GetLongitude() const { ComputeDerived(); return mLon; }
269
270   /** Get the longitude.
271       @return the longitude in deg of the location represented with this
272       class instance. The returned values are in the range between
273       -180 <= lon <= 180.  Longitude is positive east and negative west. */
274   double GetLongitudeDeg() const { ComputeDerived(); return radtodeg*mLon; }
275
276   /** Get the sine of Longitude. */
277   double GetSinLongitude() const { ComputeDerived(); return -mTec2l(2,1); }
278
279   /** Get the cosine of Longitude. */
280   double GetCosLongitude() const { ComputeDerived(); return mTec2l(2,2); }
281
282   /** Get the latitude.
283       @return the latitude in rad of the location represented with this
284       class instance. The returned values are in the range between
285       -pi/2 <= lon <= pi/2. Latitude is positive north and negative south. */
286   double GetLatitude() const { ComputeDerived(); return mLat; }
287
288   /** Get the geodetic latitude.
289       @return the geodetic latitude in rad of the location represented with this
290       class instance. The returned values are in the range between
291       -pi/2 <= lon <= pi/2. Latitude is positive north and negative south. */
292   double GetGeodLatitudeRad(void) const { ComputeDerived(); return mGeodLat; }
293
294   /** Get the latitude.
295       @return the latitude in deg of the location represented with this
296       class instance. The returned value is in the range between
297       -90 <= lon <= 90. Latitude is positive north and negative south. */
298   double GetLatitudeDeg() const { ComputeDerived(); return radtodeg*mLat; }
299
300   /** Get the geodetic latitude in degrees.
301       @return the geodetic latitude in degrees of the location represented by
302       this class instance. The returned value is in the range between
303       -90 <= lon <= 90. Latitude is positive north and negative south. */
304   double GetGeodLatitudeDeg(void) const { ComputeDerived(); return radtodeg*mGeodLat; }
305
306   /** Gets the geodetic altitude in feet. */
307   double GetGeodAltitude(void) const {ComputeDerived(); return GeodeticAltitude;}
308
309   /** Get the sine of Latitude. */
310   double GetSinLatitude() const { ComputeDerived(); return -mTec2l(3,3); }
311
312   /** Get the cosine of Latitude. */
313   double GetCosLatitude() const { ComputeDerived(); return mTec2l(1,3); }
314
315   /** Get the cosine of Latitude. */
316   double GetTanLatitude() const {
317     ComputeDerived();
318     double cLat = mTec2l(1,3);
319     if (cLat == 0.0)
320       return 0.0;
321     else
322       return -mTec2l(3,3)/cLat;
323   }
324
325   /** Get the distance from the center of the earth.
326       @return the distance of the location represented with this class
327       instance to the center of the earth in ft. The radius value is
328       always positive. */
329   //double GetRadius() const { return mECLoc.Magnitude(); } // may not work with FlightGear
330   double GetRadius() const { ComputeDerived(); return mRadius; }
331
332   /** Transform matrix from local horizontal to earth centered frame.
333       Returns a const reference to the rotation matrix of the transform from
334       the local horizontal frame to the earth centered frame. */
335   const FGMatrix33& GetTl2ec(void) const { ComputeDerived(); return mTl2ec; }
336
337   /** Transform matrix from the earth centered to local horizontal frame.
338       Returns a const reference to the rotation matrix of the transform from
339       the earth centered frame to the local horizontal frame. */
340   const FGMatrix33& GetTec2l(void) const { ComputeDerived(); return mTec2l; }
341
342   /** Transform matrix from inertial to earth centered frame.
343       Returns a const reference to the rotation matrix of the transform from
344       the inertial frame to the earth centered frame (ECI to ECEF). */
345   const FGMatrix33& GetTi2ec(void);
346
347   /** Transform matrix from the earth centered to inertial frame.
348       Returns a const reference to the rotation matrix of the transform from
349       the earth centered frame to the inertial frame (ECEF to ECI). */
350   const FGMatrix33& GetTec2i(void);
351
352   const FGMatrix33& GetTi2l(void) const {ComputeDerived(); return mTi2l;}
353
354   const FGMatrix33& GetTl2i(void) const {ComputeDerived(); return mTl2i;}
355
356   /** Conversion from Local frame coordinates to a location in the
357       earth centered and fixed frame.
358       @param lvec Vector in the local horizontal coordinate frame
359       @return The location in the earth centered and fixed frame */
360   FGLocation LocalToLocation(const FGColumnVector3& lvec) const {
361     ComputeDerived(); return mTl2ec*lvec + mECLoc;
362   }
363
364   /** Conversion from a location in the earth centered and fixed frame
365       to local horizontal frame coordinates.
366       @param ecvec Vector in the earth centered and fixed frame
367       @return The vector in the local horizontal coordinate frame */
368   FGColumnVector3 LocationToLocal(const FGColumnVector3& ecvec) const {
369     ComputeDerived(); return mTec2l*(ecvec - mECLoc);
370   }
371
372   // For time-stepping, locations have vector properties...
373
374   /** Read access the entries of the vector.
375       @param idx the component index.
376       Return the value of the matrix entry at the given index.
377       Indices are counted starting with 1.
378       Note that the index given in the argument is unchecked. */
379   double operator()(unsigned int idx) const { return mECLoc.Entry(idx); }
380
381   /** Write access the entries of the vector.
382       @param idx the component index.
383       @return a reference to the vector entry at the given index.
384       Indices are counted starting with 1.
385       Note that the index given in the argument is unchecked. */
386   double& operator()(unsigned int idx) { mCacheValid = false; return mECLoc.Entry(idx); }
387
388   /** Read access the entries of the vector.
389       @param idx the component index.
390       @return the value of the matrix entry at the given index.
391       Indices are counted starting with 1.
392       This function is just a shortcut for the <tt>double
393       operator()(unsigned int idx) const</tt> function. It is
394       used internally to access the elements in a more convenient way.
395       Note that the index given in the argument is unchecked. */
396   double Entry(unsigned int idx) const { return mECLoc.Entry(idx); }
397
398   /** Write access the entries of the vector.
399       @param idx the component index.
400       @return a reference to the vector entry at the given index.
401       Indices are counted starting with 1.
402       This function is just a shortcut for the double&
403       operator()(unsigned int idx) function. It is
404       used internally to access the elements in a more convenient way.
405       Note that the index given in the argument is unchecked. */
406   double& Entry(unsigned int idx) {
407     mCacheValid = false; return mECLoc.Entry(idx);
408   }
409
410   /** Sets this location via the supplied vector.
411       The location can be set by an Earth-centered, Earth-fixed (ECEF) frame
412       position vector. The cache is marked as invalid, so any future requests
413       for selected important data will cause the parameters to be calculated.
414       @param v the ECEF column vector in feet. 
415       @return a reference to the FGLocation object. */
416   const FGLocation& operator=(const FGColumnVector3& v)
417   {
418     mECLoc(eX) = v(eX);
419     mECLoc(eY) = v(eY);
420     mECLoc(eZ) = v(eZ);
421     mCacheValid = false;
422     ComputeDerived();
423     return *this;
424   }
425
426   /** Sets this location via the supplied location object.
427       @param v A location object reference. 
428       @return a reference to the FGLocation object. */
429   const FGLocation& operator=(const FGLocation& l)
430   {
431     mECLoc = l.mECLoc;
432     mCacheValid = l.mCacheValid;
433
434 //    if (!mCacheValid) return *this; // Why is this here for an assignment operator?
435
436     mLon = l.mLon;
437     mLat = l.mLat;
438     mRadius = l.mRadius;
439
440     mTl2ec = l.mTl2ec;
441     mTec2l = l.mTec2l;
442
443     a = l.a;
444     b = l.b;
445     a2 = l.a2;
446     b2 = l.b2;
447     e2 = l.e2;
448     e = l.e;
449     eps2 = l.eps2;
450     f = l.f;
451
452     initial_longitude = l.initial_longitude;
453     mGeodLat = l.mGeodLat;
454     GeodeticAltitude = l.GeodeticAltitude;
455
456     return *this;
457   }
458
459   /** This operator returns true if the ECEF location vectors for the two
460       location objects are equal. */
461   bool operator==(const FGLocation& l) const {
462     return mECLoc == l.mECLoc;
463   }
464
465   /** This operator returns true if the ECEF location vectors for the two
466       location objects are not equal. */
467   bool operator!=(const FGLocation& l) const { return ! operator==(l); }
468
469   /** This operator adds the ECEF position vectors.
470       The supplied vector (right side) is added to the ECEF position vector
471       on the left side of the equality, and a pointer to this object is
472       returned. */
473   const FGLocation& operator+=(const FGLocation &l) {
474     mCacheValid = false;
475     mECLoc += l.mECLoc;
476     return *this;
477   }
478
479   const FGLocation& operator-=(const FGLocation &l) {
480     mCacheValid = false;
481     mECLoc -= l.mECLoc;
482     return *this;
483   }
484
485   const FGLocation& operator*=(double scalar) {
486     mCacheValid = false;
487     mECLoc *= scalar;
488     return *this;
489   }
490
491   const FGLocation& operator/=(double scalar) {
492     return operator*=(1.0/scalar);
493   }
494
495   FGLocation operator+(const FGLocation& l) const {
496     return FGLocation(mECLoc + l.mECLoc);
497   }
498
499   FGLocation operator-(const FGLocation& l) const {
500     return FGLocation(mECLoc - l.mECLoc);
501   }
502
503   FGLocation operator*(double scalar) const {
504     return FGLocation(scalar*mECLoc);
505   }
506
507   /** Cast to a simple 3d vector */
508   operator const FGColumnVector3&() const {
509     return mECLoc;
510   }
511
512 private:
513   /** Computation of derived values.
514       This function re-computes the derived values like lat/lon and
515       transformation matrices. It does this unconditionally. */
516   void ComputeDerivedUnconditional(void) const;
517
518   /** Computation of derived values.
519       This function checks if the derived values like lat/lon and
520       transformation matrices are already computed. If so, it
521       returns. If they need to be computed this is done here. */
522   void ComputeDerived(void) const {
523     if (!mCacheValid)
524       ComputeDerivedUnconditional();
525   }
526
527   /** The coordinates in the earth centered frame. This is the master copy.
528       The coordinate frame has its center in the middle of the earth.
529       Its x-axis points from the center of the earth towards a
530       location with zero latitude and longitude on the earths
531       surface. The y-axis points from the center of the earth towards a
532       location with zero latitude and 90deg longitude on the earths
533       surface. The z-axis points from the earths center to the
534       geographic north pole.
535       @see W. C. Durham "Aircraft Dynamics & Control", section 2.2 */
536   FGColumnVector3 mECLoc;
537
538   /** The cached lon/lat/radius values. */
539   mutable double mLon;
540   mutable double mLat;
541   mutable double mRadius;
542   mutable double mGeodLat;
543   mutable double GeodeticAltitude;
544   
545   double initial_longitude;
546
547   /** The cached rotation matrices from and to the associated frames. */
548   mutable FGMatrix33 mTl2ec;
549   mutable FGMatrix33 mTec2l;
550   mutable FGMatrix33 mTi2ec;
551   mutable FGMatrix33 mTec2i;
552   mutable FGMatrix33 mTi2l;
553   mutable FGMatrix33 mTl2i;
554
555   double epa;
556
557   /* Terms for geodetic latitude calculation. Values are from WGS84 model */
558   double a;    // Earth semimajor axis in feet (6,378,137.0 meters)
559   double b;    // Earth semiminor axis in feet (6,356,752.3142 meters)
560   double a2;
561   double b2;
562   double e;    // Earth eccentricity
563   double e2;   // Earth eccentricity squared
564   double eps2; //
565   double f;    // Flattening
566
567   /** A data validity flag.
568       This class implements caching of the derived values like the
569       orthogonal rotation matrices or the lon/lat/radius values. For caching we
570       carry a flag which signals if the values are valid or not.
571       The C++ keyword "mutable" tells the compiler that the data member is
572       allowed to change during a const member function. */
573   mutable bool mCacheValid;
574 };
575
576 /** Scalar multiplication.
577
578     @param scalar scalar value to multiply with.
579     @param l Vector to multiply.
580
581     Multiply the Vector with a scalar value. */
582 inline FGLocation operator*(double scalar, const FGLocation& l)
583 {
584   return l.operator*(scalar);
585 }
586
587 } // namespace JSBSim
588
589 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590 #endif