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) ----
10 ------- (C) 2011 Ola Røer Thorsen (ola@silentwings.no) -----------
12 This program is free software; you can redistribute it and/or modify it under
13 the terms of the GNU Lesser General Public License as published by the Free Software
14 Foundation; either version 2 of the License, or (at your option) any later
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
22 You should have received a copy of the GNU Lesser General Public License along with
23 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
24 Place - Suite 330, Boston, MA 02111-1307, USA.
26 Further information about the GNU Lesser General Public License can also be found on
27 the world wide web at http://www.gnu.org.
29 FUNCTIONAL DESCRIPTION
30 ------------------------------------------------------------------------------
31 This class encapsulates an arbitrary position in the globe with its accessors.
32 It has vector properties, so you can add multiply ....
35 ------------------------------------------------------------------------------
37 11/01/2011 ORT Encapsulated ground callback code in FGLocation and removed
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
46 #include "FGLocation.h"
47 #include "input_output/FGPropertyManager.h"
51 static const char *IdSrc = "$Id: FGLocation.cpp,v 1.26 2011/11/06 18:14:51 bcoconni Exp $";
52 static const char *IdHdr = ID_LOCATION;
56 // Set up the default ground callback object.
57 FGGroundCallback_ptr FGLocation::GroundCallback = NULL;
59 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
63 FGLocation::FGLocation(void)
67 a = b = a2 = b2 = 0.0;
72 mLon = mLat = mRadius = 0.0;
73 mGeodLat = GeodeticAltitude = initial_longitude = 0.0;
84 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 FGLocation::FGLocation(double lon, double lat, double radius)
89 a = b = a2 = b2 = 0.0;
94 mLon = mLat = mRadius = 0.0;
95 mGeodLat = GeodeticAltitude = initial_longitude = 0.0;
104 double sinLat = sin(lat);
105 double cosLat = cos(lat);
106 double sinLon = sin(lon);
107 double cosLon = cos(lon);
108 mECLoc = FGColumnVector3( radius*cosLat*cosLon,
109 radius*cosLat*sinLon,
113 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115 FGLocation::FGLocation(const FGColumnVector3& lv) : mECLoc(lv), mCacheValid(false)
117 a = b = a2 = b2 = 0.0;
122 mLon = mLat = mRadius = 0.0;
123 mGeodLat = GeodeticAltitude = initial_longitude = 0.0;
133 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135 FGLocation::FGLocation(const FGLocation& l)
136 : mECLoc(l.mECLoc), mCacheValid(l.mCacheValid)
150 * if the cache is not valid, all of the following values are unset.
151 * They will be calculated once ComputeDerivedUnconditional is called.
152 * If unset, they may possibly contain NaN and could thus trigger floating
155 if (!mCacheValid) return;
168 initial_longitude = l.initial_longitude;
169 mGeodLat = l.mGeodLat;
170 GeodeticAltitude = l.GeodeticAltitude;
173 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
175 const FGLocation& FGLocation::operator=(const FGLocation& l)
178 mCacheValid = l.mCacheValid;
190 //ag See comment in constructor above
191 if (!mCacheValid) return *this;
204 initial_longitude = l.initial_longitude;
205 mGeodLat = l.mGeodLat;
206 GeodeticAltitude = l.GeodeticAltitude;
211 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213 void FGLocation::SetLongitude(double longitude)
215 double rtmp = mECLoc.Magnitude(eX, eY);
216 // Check if we have zero radius.
217 // If so set it to 1, so that we can set a position
218 if (0.0 == mECLoc.Magnitude())
221 // Fast return if we are on the north or south pole ...
227 // Need to figure out how to set the initial_longitude here
229 mECLoc(eX) = rtmp*cos(longitude);
230 mECLoc(eY) = rtmp*sin(longitude);
233 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
235 void FGLocation::SetLatitude(double latitude)
239 double r = mECLoc.Magnitude();
245 double rtmp = mECLoc.Magnitude(eX, eY);
247 double fac = r/rtmp*cos(latitude);
251 mECLoc(eX) = r*cos(latitude);
254 mECLoc(eZ) = r*sin(latitude);
257 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259 void FGLocation::SetRadius(double radius)
263 double rold = mECLoc.Magnitude();
267 mECLoc *= radius/rold;
270 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
272 void FGLocation::SetPosition(double lon, double lat, double radius)
276 double sinLat = sin(lat);
277 double cosLat = cos(lat);
278 double sinLon = sin(lon);
279 double cosLon = cos(lon);
280 // initial_longitude = lon;
281 mECLoc = FGColumnVector3( radius*cosLat*cosLon,
282 radius*cosLat*sinLon,
286 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
288 void FGLocation::SetPositionGeodetic(double lon, double lat, double height)
294 GeodeticAltitude = height;
296 // initial_longitude = mLon;
298 double RN = a / sqrt(1.0 - e2*sin(mGeodLat)*sin(mGeodLat));
300 mECLoc(eX) = (RN + GeodeticAltitude)*cos(mGeodLat)*cos(mLon);
301 mECLoc(eY) = (RN + GeodeticAltitude)*cos(mGeodLat)*sin(mLon);
302 mECLoc(eZ) = ((1 - e2)*RN + GeodeticAltitude)*sin(mGeodLat);
305 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307 void FGLocation::SetEllipse(double semimajor, double semiminor)
321 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
322 // Compute the ECEF to ECI transformation matrix using Stevens and Lewis "Aircraft
323 // Control and Simulation", second edition, eqn. 1.4-12, pg. 39. In Stevens and Lewis
324 // notation, this is C_i/e, a transformation from ECEF to ECI.
326 const FGMatrix33& FGLocation::GetTec2i(void)
332 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
333 // This is given in Stevens and Lewis "Aircraft
334 // Control and Simulation", second edition, eqn. 1.4-12, pg. 39
335 // The notation in Stevens and Lewis is: C_e/i. This represents a transformation
336 // from ECI to ECEF - and the orientation of the ECEF frame relative to the ECI
339 const FGMatrix33& FGLocation::GetTi2ec(void)
345 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347 void FGLocation::ComputeDerivedUnconditional(void) const
349 // The radius is just the Euclidean norm of the vector.
350 mRadius = mECLoc.Magnitude();
352 // The distance of the location to the Z-axis, which is the axis
353 // through the poles.
354 double r02 = mECLoc(eX)*mECLoc(eX) + mECLoc(eY)*mECLoc(eY);
355 double rxy = sqrt(r02);
357 // Compute the sin/cos values of the longitude
358 double sinLon, cosLon;
363 sinLon = mECLoc(eY)/rxy;
364 cosLon = mECLoc(eX)/rxy;
367 // Compute the sin/cos values of the latitude
368 double sinLat, cosLat;
369 if (mRadius == 0.0) {
373 sinLat = mECLoc(eZ)/mRadius;
374 cosLat = rxy/mRadius;
377 // Compute the longitude and latitude itself
378 if ( mECLoc( eX ) == 0.0 && mECLoc( eY ) == 0.0 )
381 mLon = atan2( mECLoc( eY ), mECLoc( eX ) );
383 if ( rxy == 0.0 && mECLoc( eZ ) == 0.0 )
386 mLat = atan2( mECLoc(eZ), rxy );
388 // Compute the transform matrices from and to the earth centered frame.
389 // See Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
390 // Eqn. 1.4-13, page 40. In Stevens and Lewis notation, this is C_n/e - the
391 // orientation of the navigation (local) frame relative to the ECEF frame,
392 // and a transformation from ECEF to nav (local) frame.
394 mTec2l = FGMatrix33( -cosLon*sinLat, -sinLon*sinLat, cosLat,
395 -sinLon , cosLon , 0.0 ,
396 -cosLon*cosLat, -sinLon*cosLat, -sinLat );
398 // In Stevens and Lewis notation, this is C_e/n - the
399 // orientation of the ECEF frame relative to the nav (local) frame,
400 // and a transformation from nav (local) to ECEF frame.
402 mTl2ec = mTec2l.Transposed();
404 // Calculate the inertial to ECEF and transpose matrices
405 double cos_epa = cos(epa);
406 double sin_epa = sin(epa);
407 mTi2ec = FGMatrix33( cos_epa, sin_epa, 0.0,
408 -sin_epa, cos_epa, 0.0,
410 mTec2i = mTi2ec.Transposed();
412 // Now calculate the local (or nav, or ned) frame to inertial transform matrix,
414 mTl2i = mTec2i * mTl2ec;
415 mTi2l = mTl2i.Transposed();
417 // Calculate the geodetic latitude base on AIAA Journal of Guidance and Control paper,
418 // "Improved Method for Calculating Exact Geodetic Latitude and Altitude", and
419 // "Improved Method for Calculating Exact Geodetic Latitude and Altitude, Revisited",
422 if (a != 0.0 && b != 0.0) {
423 double c, p, q, s, t, u, v, w, z, p2, u2, r0;
424 double Ne, P, Q0, Q, signz0, sqrt_q, z_term;
425 p = fabs(mECLoc(eZ))/eps2;
436 Q0 = sqrt(P+1) + sqrt(P);
437 Q = pow(Q0, 0.66666666667);
438 t = (1.0 + Q + 1.0/Q)/6.0;
439 c = sqrt(u2 - 1 + 2.0*t);
441 signz0 = mECLoc(eZ)>=0?1.0:-1.0;
442 z_term = sqrt(t*t+v)-u*w-0.5*t-0.25;
446 z = signz0*sqrt_q*(w+sqrt(z_term));
448 Ne = a*sqrt(1+eps2*z*z/b2);
449 mGeodLat = asin((eps2+1.0)*(z/Ne));
451 GeodeticAltitude = r0*cos(mGeodLat) + mECLoc(eZ)*sin(mGeodLat) - a2/Ne;
455 // Mark the cached values as valid
459 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461 } // namespace JSBSim