1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5 Date started: 04/04/2004
6 Purpose: Store an arbitrary location on the globe
8 ------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) ------------------
9 ------- (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) ----
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 an arbitrary position in the globe with its accessors.
31 It has vector properties, so you can add multiply ....
34 ------------------------------------------------------------------------------
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
43 #include "FGLocation.h"
44 #include "input_output/FGPropertyManager.h"
48 static const char *IdSrc = "$Id: FGLocation.cpp,v 1.25 2011/10/16 00:19:56 bcoconni Exp $";
49 static const char *IdHdr = ID_LOCATION;
52 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
56 FGLocation::FGLocation(void)
60 a = b = a2 = b2 = 0.0;
65 mLon = mLat = mRadius = 0.0;
66 mGeodLat = GeodeticAltitude = initial_longitude = 0.0;
77 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 FGLocation::FGLocation(double lon, double lat, double radius)
82 a = b = a2 = b2 = 0.0;
87 mLon = mLat = mRadius = 0.0;
88 mGeodLat = GeodeticAltitude = initial_longitude = 0.0;
97 double sinLat = sin(lat);
98 double cosLat = cos(lat);
99 double sinLon = sin(lon);
100 double cosLon = cos(lon);
101 mECLoc = FGColumnVector3( radius*cosLat*cosLon,
102 radius*cosLat*sinLon,
106 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108 FGLocation::FGLocation(const FGColumnVector3& lv) : mECLoc(lv), mCacheValid(false)
110 a = b = a2 = b2 = 0.0;
115 mLon = mLat = mRadius = 0.0;
116 mGeodLat = GeodeticAltitude = initial_longitude = 0.0;
126 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
128 FGLocation::FGLocation(const FGLocation& l)
129 : mECLoc(l.mECLoc), mCacheValid(l.mCacheValid)
143 * if the cache is not valid, all of the following values are unset.
144 * They will be calculated once ComputeDerivedUnconditional is called.
145 * If unset, they may possibly contain NaN and could thus trigger floating
148 if (!mCacheValid) return;
161 initial_longitude = l.initial_longitude;
162 mGeodLat = l.mGeodLat;
163 GeodeticAltitude = l.GeodeticAltitude;
166 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168 const FGLocation& FGLocation::operator=(const FGLocation& l)
171 mCacheValid = l.mCacheValid;
183 //ag See comment in constructor above
184 if (!mCacheValid) return *this;
197 initial_longitude = l.initial_longitude;
198 mGeodLat = l.mGeodLat;
199 GeodeticAltitude = l.GeodeticAltitude;
204 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206 void FGLocation::SetLongitude(double longitude)
208 double rtmp = mECLoc.Magnitude(eX, eY);
209 // Check if we have zero radius.
210 // If so set it to 1, so that we can set a position
211 if (0.0 == mECLoc.Magnitude())
214 // Fast return if we are on the north or south pole ...
220 // Need to figure out how to set the initial_longitude here
222 mECLoc(eX) = rtmp*cos(longitude);
223 mECLoc(eY) = rtmp*sin(longitude);
226 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228 void FGLocation::SetLatitude(double latitude)
232 double r = mECLoc.Magnitude();
238 double rtmp = mECLoc.Magnitude(eX, eY);
240 double fac = r/rtmp*cos(latitude);
244 mECLoc(eX) = r*cos(latitude);
247 mECLoc(eZ) = r*sin(latitude);
250 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252 void FGLocation::SetRadius(double radius)
256 double rold = mECLoc.Magnitude();
260 mECLoc *= radius/rold;
263 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265 void FGLocation::SetPosition(double lon, double lat, double radius)
269 double sinLat = sin(lat);
270 double cosLat = cos(lat);
271 double sinLon = sin(lon);
272 double cosLon = cos(lon);
273 // initial_longitude = lon;
274 mECLoc = FGColumnVector3( radius*cosLat*cosLon,
275 radius*cosLat*sinLon,
279 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
281 void FGLocation::SetPositionGeodetic(double lon, double lat, double height)
287 GeodeticAltitude = height;
289 // initial_longitude = mLon;
291 double RN = a / sqrt(1.0 - e2*sin(mGeodLat)*sin(mGeodLat));
293 mECLoc(eX) = (RN + GeodeticAltitude)*cos(mGeodLat)*cos(mLon);
294 mECLoc(eY) = (RN + GeodeticAltitude)*cos(mGeodLat)*sin(mLon);
295 mECLoc(eZ) = ((1 - e2)*RN + GeodeticAltitude)*sin(mGeodLat);
298 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300 void FGLocation::SetEllipse(double semimajor, double semiminor)
314 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315 // Compute the ECEF to ECI transformation matrix using Stevens and Lewis "Aircraft
316 // Control and Simulation", second edition, eqn. 1.4-12, pg. 39. In Stevens and Lewis
317 // notation, this is C_i/e, a transformation from ECEF to ECI.
319 const FGMatrix33& FGLocation::GetTec2i(void)
325 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326 // This is given in Stevens and Lewis "Aircraft
327 // Control and Simulation", second edition, eqn. 1.4-12, pg. 39
328 // The notation in Stevens and Lewis is: C_e/i. This represents a transformation
329 // from ECI to ECEF - and the orientation of the ECEF frame relative to the ECI
332 const FGMatrix33& FGLocation::GetTi2ec(void)
338 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340 void FGLocation::ComputeDerivedUnconditional(void) const
342 // The radius is just the Euclidean norm of the vector.
343 mRadius = mECLoc.Magnitude();
345 // The distance of the location to the Z-axis, which is the axis
346 // through the poles.
347 double r02 = mECLoc(eX)*mECLoc(eX) + mECLoc(eY)*mECLoc(eY);
348 double rxy = sqrt(r02);
350 // Compute the sin/cos values of the longitude
351 double sinLon, cosLon;
356 sinLon = mECLoc(eY)/rxy;
357 cosLon = mECLoc(eX)/rxy;
360 // Compute the sin/cos values of the latitude
361 double sinLat, cosLat;
362 if (mRadius == 0.0) {
366 sinLat = mECLoc(eZ)/mRadius;
367 cosLat = rxy/mRadius;
370 // Compute the longitude and latitude itself
371 if ( mECLoc( eX ) == 0.0 && mECLoc( eY ) == 0.0 )
374 mLon = atan2( mECLoc( eY ), mECLoc( eX ) );
376 if ( rxy == 0.0 && mECLoc( eZ ) == 0.0 )
379 mLat = atan2( mECLoc(eZ), rxy );
381 // Compute the transform matrices from and to the earth centered frame.
382 // See Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
383 // Eqn. 1.4-13, page 40. In Stevens and Lewis notation, this is C_n/e - the
384 // orientation of the navigation (local) frame relative to the ECEF frame,
385 // and a transformation from ECEF to nav (local) frame.
387 mTec2l = FGMatrix33( -cosLon*sinLat, -sinLon*sinLat, cosLat,
388 -sinLon , cosLon , 0.0 ,
389 -cosLon*cosLat, -sinLon*cosLat, -sinLat );
391 // In Stevens and Lewis notation, this is C_e/n - the
392 // orientation of the ECEF frame relative to the nav (local) frame,
393 // and a transformation from nav (local) to ECEF frame.
395 mTl2ec = mTec2l.Transposed();
397 // Calculate the inertial to ECEF and transpose matrices
398 double cos_epa = cos(epa);
399 double sin_epa = sin(epa);
400 mTi2ec = FGMatrix33( cos_epa, sin_epa, 0.0,
401 -sin_epa, cos_epa, 0.0,
403 mTec2i = mTi2ec.Transposed();
405 // Now calculate the local (or nav, or ned) frame to inertial transform matrix,
407 mTl2i = mTec2i * mTl2ec;
408 mTi2l = mTl2i.Transposed();
410 // Calculate the geodetic latitude base on AIAA Journal of Guidance and Control paper,
411 // "Improved Method for Calculating Exact Geodetic Latitude and Altitude", and
412 // "Improved Method for Calculating Exact Geodetic Latitude and Altitude, Revisited",
415 if (a != 0.0 && b != 0.0) {
416 double c, p, q, s, t, u, v, w, z, p2, u2, r0;
417 double Ne, P, Q0, Q, signz0, sqrt_q, z_term;
418 p = fabs(mECLoc(eZ))/eps2;
429 Q0 = sqrt(P+1) + sqrt(P);
430 Q = pow(Q0, 0.66666666667);
431 t = (1.0 + Q + 1.0/Q)/6.0;
432 c = sqrt(u2 - 1 + 2.0*t);
434 signz0 = mECLoc(eZ)>=0?1.0:-1.0;
435 z_term = sqrt(t*t+v)-u*w-0.5*t-0.25;
439 z = signz0*sqrt_q*(w+sqrt(z_term));
441 Ne = a*sqrt(1+eps2*z*z/b2);
442 mGeodLat = asin((eps2+1.0)*(z/Ne));
444 GeodeticAltitude = r0*cos(mGeodLat) + mECLoc(eZ)*sin(mGeodLat) - a2/Ne;
448 // Mark the cached values as valid
452 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454 } // namespace JSBSim