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.23 2010/09/22 11:34:09 jberndt 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)
142 * if the cache is not valid, all of the following values are unset.
143 * They will be calculated once ComputeDerivedUnconditional is called.
144 * If unset, they may possibly contain NaN and could thus trigger floating
147 if (!mCacheValid) return;
156 initial_longitude = l.initial_longitude;
157 mGeodLat = l.mGeodLat;
158 GeodeticAltitude = l.GeodeticAltitude;
161 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163 const FGLocation& FGLocation::operator=(const FGLocation& l)
166 mCacheValid = l.mCacheValid;
177 //ag See comment in constructor above
178 if (!mCacheValid) return *this;
187 initial_longitude = l.initial_longitude;
188 mGeodLat = l.mGeodLat;
189 GeodeticAltitude = l.GeodeticAltitude;
194 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196 void FGLocation::SetLongitude(double longitude)
198 double rtmp = mECLoc.Magnitude(eX, eY);
199 // Check if we have zero radius.
200 // If so set it to 1, so that we can set a position
201 if (0.0 == mECLoc.Magnitude())
204 // Fast return if we are on the north or south pole ...
210 // Need to figure out how to set the initial_longitude here
212 mECLoc(eX) = rtmp*cos(longitude);
213 mECLoc(eY) = rtmp*sin(longitude);
216 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
218 void FGLocation::SetLatitude(double latitude)
222 double r = mECLoc.Magnitude();
228 double rtmp = mECLoc.Magnitude(eX, eY);
230 double fac = r/rtmp*cos(latitude);
234 mECLoc(eX) = r*cos(latitude);
237 mECLoc(eZ) = r*sin(latitude);
240 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
242 void FGLocation::SetRadius(double radius)
246 double rold = mECLoc.Magnitude();
250 mECLoc *= radius/rold;
253 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255 void FGLocation::SetPosition(double lon, double lat, double radius)
259 double sinLat = sin(lat);
260 double cosLat = cos(lat);
261 double sinLon = sin(lon);
262 double cosLon = cos(lon);
263 // initial_longitude = lon;
264 mECLoc = FGColumnVector3( radius*cosLat*cosLon,
265 radius*cosLat*sinLon,
269 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
271 void FGLocation::SetPositionGeodetic(double lon, double lat, double height)
277 GeodeticAltitude = height;
279 // initial_longitude = mLon;
281 double RN = a / sqrt(1.0 - e2*sin(mGeodLat)*sin(mGeodLat));
283 mECLoc(eX) = (RN + GeodeticAltitude)*cos(mGeodLat)*cos(mLon);
284 mECLoc(eY) = (RN + GeodeticAltitude)*cos(mGeodLat)*sin(mLon);
285 mECLoc(eZ) = ((1 - e2)*RN + GeodeticAltitude)*sin(mGeodLat);
288 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
290 void FGLocation::SetEllipse(double semimajor, double semiminor)
304 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
305 // Compute the ECEF to ECI transformation matrix using Stevens and Lewis "Aircraft
306 // Control and Simulation", second edition, eqn. 1.4-12, pg. 39. In Stevens and Lewis
307 // notation, this is C_i/e, a transformation from ECEF to ECI.
309 const FGMatrix33& FGLocation::GetTec2i(void)
315 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316 // This is given in Stevens and Lewis "Aircraft
317 // Control and Simulation", second edition, eqn. 1.4-12, pg. 39
318 // The notation in Stevens and Lewis is: C_e/i. This represents a transformation
319 // from ECI to ECEF - and the orientation of the ECEF frame relative to the ECI
322 const FGMatrix33& FGLocation::GetTi2ec(void)
328 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330 void FGLocation::ComputeDerivedUnconditional(void) const
332 // The radius is just the Euclidean norm of the vector.
333 mRadius = mECLoc.Magnitude();
335 // The distance of the location to the Z-axis, which is the axis
336 // through the poles.
337 double r02 = mECLoc(eX)*mECLoc(eX) + mECLoc(eY)*mECLoc(eY);
338 double rxy = sqrt(r02);
340 // Compute the sin/cos values of the longitude
341 double sinLon, cosLon;
346 sinLon = mECLoc(eY)/rxy;
347 cosLon = mECLoc(eX)/rxy;
350 // Compute the sin/cos values of the latitude
351 double sinLat, cosLat;
352 if (mRadius == 0.0) {
356 sinLat = mECLoc(eZ)/mRadius;
357 cosLat = rxy/mRadius;
360 // Compute the longitude and latitude itself
361 if ( mECLoc( eX ) == 0.0 && mECLoc( eY ) == 0.0 )
364 mLon = atan2( mECLoc( eY ), mECLoc( eX ) );
366 if ( rxy == 0.0 && mECLoc( eZ ) == 0.0 )
369 mLat = atan2( mECLoc(eZ), rxy );
371 // Compute the transform matrices from and to the earth centered frame.
372 // See Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
373 // Eqn. 1.4-13, page 40. In Stevens and Lewis notation, this is C_n/e - the
374 // orientation of the navigation (local) frame relative to the ECEF frame,
375 // and a transformation from ECEF to nav (local) frame.
377 mTec2l = FGMatrix33( -cosLon*sinLat, -sinLon*sinLat, cosLat,
378 -sinLon , cosLon , 0.0 ,
379 -cosLon*cosLat, -sinLon*cosLat, -sinLat );
381 // In Stevens and Lewis notation, this is C_e/n - the
382 // orientation of the ECEF frame relative to the nav (local) frame,
383 // and a transformation from nav (local) to ECEF frame.
385 mTl2ec = mTec2l.Transposed();
387 // Calculate the inertial to ECEF and transpose matrices
388 double cos_epa = cos(epa);
389 double sin_epa = sin(epa);
390 mTi2ec = FGMatrix33( cos_epa, sin_epa, 0.0,
391 -sin_epa, cos_epa, 0.0,
393 mTec2i = mTi2ec.Transposed();
395 // Now calculate the local (or nav, or ned) frame to inertial transform matrix,
397 mTl2i = mTec2i * mTl2ec;
398 mTi2l = mTl2i.Transposed();
400 // Calculate the geodetic latitude base on AIAA Journal of Guidance and Control paper,
401 // "Improved Method for Calculating Exact Geodetic Latitude and Altitude", and
402 // "Improved Method for Calculating Exact Geodetic Latitude and Altitude, Revisited",
405 if (a != 0.0 && b != 0.0) {
406 double c, p, q, s, t, u, v, w, z, p2, u2, r0;
407 double Ne, P, Q0, Q, signz0, sqrt_q, z_term;
408 p = fabs(mECLoc(eZ))/eps2;
419 Q0 = sqrt(P+1) + sqrt(P);
420 Q = pow(Q0, 0.66666666667);
421 t = (1.0 + Q + 1.0/Q)/6.0;
422 c = sqrt(u2 - 1 + 2.0*t);
424 signz0 = mECLoc(eZ)>=0?1.0:-1.0;
425 z_term = sqrt(t*t+v)-u*w-0.5*t-0.25;
429 z = signz0*sqrt_q*(w+sqrt(z_term));
431 Ne = a*sqrt(1+eps2*z*z/b2);
432 mGeodLat = asin((eps2+1.0)*(z/Ne));
434 GeodeticAltitude = r0*cos(mGeodLat) + mECLoc(eZ)*sin(mGeodLat) - a2/Ne;
438 // Mark the cached values as valid
442 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444 } // namespace JSBSim