JSBSim Flight Dynamics Model  1.0 (02 March 2017)
An Open Source Flight Dynamics and Control Software Library in C++
FGLocation.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGLocation.cpp
4  Author: Jon S. Berndt
5  Date started: 04/04/2004
6  Purpose: Store an arbitrary location on the globe
7 
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) -----------
11 
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
15  version.
16 
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
20  details.
21 
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.
25 
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.
28 
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 ....
33 
34 HISTORY
35 ------------------------------------------------------------------------------
36 04/04/2004 MF Created
37 11/01/2011 ORT Encapsulated ground callback code in FGLocation and removed
38  it from FGFDMExec.
39 
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 INCLUDES
42 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
43 
44 #include <cmath>
45 
46 #include "FGLocation.h"
47 
48 namespace JSBSim {
49 
50 IDENT(IdSrc,"$Id: FGLocation.cpp,v 1.34 2015/09/20 20:53:13 bcoconni Exp $");
51 IDENT(IdHdr,ID_LOCATION);
52 
53 // Set up the default ground callback object.
54 FGGroundCallback_ptr FGLocation::GroundCallback = NULL;
55 
56 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57 CLASS IMPLEMENTATION
58 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
59 
61  : mECLoc(1.0, 0.0, 0.0), mCacheValid(false)
62 {
63  e2 = c = 0.0;
64  a = ec = ec2 = 1.0;
65  epa = 0.0;
66 
67  mLon = mLat = mRadius = 0.0;
68  mGeodLat = GeodeticAltitude = 0.0;
69 
70  mTl2ec.InitMatrix();
71  mTec2l.InitMatrix();
72  mTi2ec.InitMatrix();
73  mTec2i.InitMatrix();
74  mTi2l.InitMatrix();
75  mTl2i.InitMatrix();
76 }
77 
78 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 
80 FGLocation::FGLocation(double lon, double lat, double radius)
81  : mCacheValid(false)
82 {
83  e2 = c = 0.0;
84  a = ec = ec2 = 1.0;
85  epa = 0.0;
86 
87  mLon = mLat = mRadius = 0.0;
88  mGeodLat = GeodeticAltitude = 0.0;
89 
90  mTl2ec.InitMatrix();
91  mTec2l.InitMatrix();
92  mTi2ec.InitMatrix();
93  mTec2i.InitMatrix();
94  mTi2l.InitMatrix();
95  mTl2i.InitMatrix();
96 
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,
103  radius*sinLat );
104 }
105 
106 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107 
109  : mECLoc(lv), mCacheValid(false)
110 {
111  e2 = c = 0.0;
112  a = ec = ec2 = 1.0;
113  epa = 0.0;
114 
115  mLon = mLat = mRadius = 0.0;
116  mGeodLat = GeodeticAltitude = 0.0;
117 
118  mTl2ec.InitMatrix();
119  mTec2l.InitMatrix();
120  mTi2ec.InitMatrix();
121  mTec2i.InitMatrix();
122  mTi2l.InitMatrix();
123  mTl2i.InitMatrix();
124 }
125 
126 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 
129  : mECLoc(l.mECLoc), mCacheValid(l.mCacheValid)
130 {
131  a = l.a;
132  e2 = l.e2;
133  c = l.c;
134  ec = l.ec;
135  ec2 = l.ec2;
136  epa = l.epa;
137 
138  /*ag
139  * if the cache is not valid, all of the following values are unset.
140  * They will be calculated once ComputeDerivedUnconditional is called.
141  * If unset, they may possibly contain NaN and could thus trigger floating
142  * point exceptions.
143  */
144  if (!mCacheValid) return;
145 
146  mLon = l.mLon;
147  mLat = l.mLat;
148  mRadius = l.mRadius;
149 
150  mTl2ec = l.mTl2ec;
151  mTec2l = l.mTec2l;
152  mTi2ec = l.mTi2ec;
153  mTec2i = l.mTec2i;
154  mTi2l = l.mTi2l;
155  mTl2i = l.mTl2i;
156 
157  mGeodLat = l.mGeodLat;
158  GeodeticAltitude = l.GeodeticAltitude;
159 }
160 
161 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
162 
164 {
165  mECLoc = l.mECLoc;
166  mCacheValid = l.mCacheValid;
167 
168  a = l.a;
169  e2 = l.e2;
170  c = l.c;
171  ec = l.ec;
172  ec2 = l.ec2;
173  epa = l.epa;
174 
175  //ag See comment in constructor above
176  if (!mCacheValid) return *this;
177 
178  mLon = l.mLon;
179  mLat = l.mLat;
180  mRadius = l.mRadius;
181 
182  mTl2ec = l.mTl2ec;
183  mTec2l = l.mTec2l;
184  mTi2ec = l.mTi2ec;
185  mTec2i = l.mTec2i;
186  mTi2l = l.mTi2l;
187  mTl2i = l.mTl2i;
188 
189  mGeodLat = l.mGeodLat;
190  GeodeticAltitude = l.GeodeticAltitude;
191 
192  return *this;
193 }
194 
195 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196 
197 void FGLocation::SetLongitude(double longitude)
198 {
199  double rtmp = mECLoc.Magnitude(eX, eY);
200  // Check if we have zero radius.
201  // If so set it to 1, so that we can set a position
202  if (0.0 == mECLoc.Magnitude())
203  rtmp = 1.0;
204 
205  // Fast return if we are on the north or south pole ...
206  if (rtmp == 0.0)
207  return;
208 
209  mCacheValid = false;
210 
211  mECLoc(eX) = rtmp*cos(longitude);
212  mECLoc(eY) = rtmp*sin(longitude);
213 }
214 
215 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
216 
217 void FGLocation::SetLatitude(double latitude)
218 {
219  mCacheValid = false;
220 
221  double r = mECLoc.Magnitude();
222  if (r == 0.0) {
223  mECLoc(eX) = 1.0;
224  r = 1.0;
225  }
226 
227  double rtmp = mECLoc.Magnitude(eX, eY);
228  if (rtmp != 0.0) {
229  double fac = r/rtmp*cos(latitude);
230  mECLoc(eX) *= fac;
231  mECLoc(eY) *= fac;
232  } else {
233  mECLoc(eX) = r*cos(latitude);
234  mECLoc(eY) = 0.0;
235  }
236  mECLoc(eZ) = r*sin(latitude);
237 }
238 
239 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
240 
241 void FGLocation::SetRadius(double radius)
242 {
243  mCacheValid = false;
244 
245  double rold = mECLoc.Magnitude();
246  if (rold == 0.0)
247  mECLoc(eX) = radius;
248  else
249  mECLoc *= radius/rold;
250 }
251 
252 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253 
254 void FGLocation::SetPosition(double lon, double lat, double radius)
255 {
256  mCacheValid = false;
257 
258  double sinLat = sin(lat);
259  double cosLat = cos(lat);
260  double sinLon = sin(lon);
261  double cosLon = cos(lon);
262 
263  mECLoc = FGColumnVector3( radius*cosLat*cosLon,
264  radius*cosLat*sinLon,
265  radius*sinLat );
266 }
267 
268 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269 
270 void FGLocation::SetPositionGeodetic(double lon, double lat, double height)
271 {
272  mCacheValid = false;
273 
274  double slat = sin(lat);
275  double clat = cos(lat);
276  double RN = a / sqrt(1.0 - e2*slat*slat);
277 
278  mECLoc(eX) = (RN + height)*clat*cos(lon);
279  mECLoc(eY) = (RN + height)*clat*sin(lon);
280  mECLoc(eZ) = ((1 - e2)*RN + height)*slat;
281 }
282 
283 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
284 
285 void FGLocation::SetEllipse(double semimajor, double semiminor)
286 {
287  mCacheValid = false;
288 
289  a = semimajor;
290  ec = semiminor/a;
291  ec2 = ec * ec;
292  e2 = 1.0 - ec2;
293  c = a * e2;
294 }
295 
296 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
297 
298 void FGLocation::ComputeDerivedUnconditional(void) const
299 {
300  // The radius is just the Euclidean norm of the vector.
301  mRadius = mECLoc.Magnitude();
302 
303  // The distance of the location to the Z-axis, which is the axis
304  // through the poles.
305  double r02 = mECLoc(eX)*mECLoc(eX) + mECLoc(eY)*mECLoc(eY);
306  double rxy = sqrt(r02);
307 
308  // Compute the sin/cos values of the longitude
309  double sinLon, cosLon;
310  if (rxy == 0.0) {
311  sinLon = 0.0;
312  cosLon = 1.0;
313  } else {
314  sinLon = mECLoc(eY)/rxy;
315  cosLon = mECLoc(eX)/rxy;
316  }
317 
318  // Compute the sin/cos values of the latitude
319  double sinLat, cosLat;
320  if (mRadius == 0.0) {
321  sinLat = 0.0;
322  cosLat = 1.0;
323  } else {
324  sinLat = mECLoc(eZ)/mRadius;
325  cosLat = rxy/mRadius;
326  }
327 
328  // Compute the longitude and latitude itself
329  if ( mECLoc( eX ) == 0.0 && mECLoc( eY ) == 0.0 )
330  mLon = 0.0;
331  else
332  mLon = atan2( mECLoc( eY ), mECLoc( eX ) );
333 
334  if ( rxy == 0.0 && mECLoc( eZ ) == 0.0 )
335  mLat = 0.0;
336  else
337  mLat = atan2( mECLoc(eZ), rxy );
338 
339  // Compute the transform matrices from and to the earth centered frame.
340  // See Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
341  // Eqn. 1.4-13, page 40. In Stevens and Lewis notation, this is C_n/e - the
342  // orientation of the navigation (local) frame relative to the ECEF frame,
343  // and a transformation from ECEF to nav (local) frame.
344 
345  mTec2l = FGMatrix33( -cosLon*sinLat, -sinLon*sinLat, cosLat,
346  -sinLon , cosLon , 0.0 ,
347  -cosLon*cosLat, -sinLon*cosLat, -sinLat );
348 
349  // In Stevens and Lewis notation, this is C_e/n - the
350  // orientation of the ECEF frame relative to the nav (local) frame,
351  // and a transformation from nav (local) to ECEF frame.
352 
353  mTl2ec = mTec2l.Transposed();
354 
355  // Calculate the inertial to ECEF and transpose matrices
356  double cos_epa = cos(epa);
357  double sin_epa = sin(epa);
358  mTi2ec = FGMatrix33( cos_epa, sin_epa, 0.0,
359  -sin_epa, cos_epa, 0.0,
360  0.0, 0.0, 1.0 );
361  mTec2i = mTi2ec.Transposed();
362 
363  // Now calculate the local (or nav, or ned) frame to inertial transform matrix,
364  // and the inverse.
365  mTl2i = mTec2i * mTl2ec;
366  mTi2l = mTl2i.Transposed();
367 
368  // Calculate the geodetic latitude based on "Transformation from Cartesian
369  // to geodetic coordinates accelerated by Halley's method", Fukushima T. (2006)
370  // Journal of Geodesy, Vol. 79, pp. 689-693
371  // Unlike I. Sofair's method which uses a closed form solution, Fukushima's
372  // method is an iterative method whose convergence is so fast that only one
373  // iteration suffices. In addition, Fukushima's method has a much better
374  // numerical stability over Sofair's method at the North and South poles and
375  // it also gives the correct result for a spherical Earth.
376 
377  double s0 = fabs(mECLoc(eZ));
378  double zc = ec * s0;
379  double c0 = ec * rxy;
380  double c02 = c0 * c0;
381  double s02 = s0 * s0;
382  double a02 = c02 + s02;
383  double a0 = sqrt(a02);
384  double a03 = a02 * a0;
385  double s1 = zc*a03 + c*s02*s0;
386  double c1 = rxy*a03 - c*c02*c0;
387  double cs0c0 = c*c0*s0;
388  double b0 = 1.5*cs0c0*((rxy*s0-zc*c0)*a0-cs0c0);
389  s1 = s1*a03-b0*s0;
390  double cc = ec*(c1*a03-b0*c0);
391  mGeodLat = sign(mECLoc(eZ))*atan(s1 / cc);
392  double s12 = s1 * s1;
393  double cc2 = cc * cc;
394  GeodeticAltitude = (rxy*cc + s0*s1 - a*sqrt(ec2*s12 + cc2)) / sqrt(s12 + cc2);
395 
396  // Mark the cached values as valid
397  mCacheValid = true;
398 }
399 
400 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
401 // The calculations, below, implement the Haversine formulas to calculate
402 // heading and distance to a set of lat/long coordinates from the current
403 // position.
404 //
405 // The basic equations are (lat1, long1 are source positions; lat2
406 // long2 are target positions):
407 //
408 // R = earth’s radius
409 // Δlat = lat2 − lat1
410 // Δlong = long2 − long1
411 //
412 // For the waypoint distance calculation:
413 //
414 // a = sin²(Δlat/2) + cos(lat1)∙cos(lat2)∙sin²(Δlong/2)
415 // c = 2∙atan2(√a, √(1−a))
416 // d = R∙c
417 
418 double FGLocation::GetDistanceTo(double target_longitude,
419  double target_latitude) const
420 {
421  double delta_lat_rad = target_latitude - GetLatitude();
422  double delta_lon_rad = target_longitude - GetLongitude();
423 
424  double distance_a = pow(sin(0.5*delta_lat_rad), 2.0)
425  + (GetCosLatitude() * cos(target_latitude)
426  * (pow(sin(0.5*delta_lon_rad), 2.0)));
427 
428  return 2.0 * GetRadius() * atan2(sqrt(distance_a), sqrt(1.0 - distance_a));
429 }
430 
431 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
432 // The calculations, below, implement the Haversine formulas to calculate
433 // heading and distance to a set of lat/long coordinates from the current
434 // position.
435 //
436 // The basic equations are (lat1, long1 are source positions; lat2
437 // long2 are target positions):
438 //
439 // R = earth’s radius
440 // Δlat = lat2 − lat1
441 // Δlong = long2 − long1
442 //
443 // For the heading angle calculation:
444 //
445 // θ = atan2(sin(Δlong)∙cos(lat2), cos(lat1)∙sin(lat2) − sin(lat1)∙cos(lat2)∙cos(Δlong))
446 
447 double FGLocation::GetHeadingTo(double target_longitude,
448  double target_latitude) const
449 {
450  double delta_lon_rad = target_longitude - GetLongitude();
451 
452  double Y = sin(delta_lon_rad) * cos(target_latitude);
453  double X = GetCosLatitude() * sin(target_latitude)
454  - GetSinLatitude() * cos(target_latitude) * cos(delta_lon_rad);
455 
456  double heading_to_waypoint_rad = atan2(Y, X);
457  if (heading_to_waypoint_rad < 0) heading_to_waypoint_rad += 2.0*M_PI;
458 
459  return heading_to_waypoint_rad;
460 }
461 
462 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 
464 } // namespace JSBSim
void SetPosition(double lon, double lat, double radius)
Sets the longitude, latitude and the distance from the center of the earth.
Definition: FGLocation.cpp:254
void SetLongitude(double longitude)
Set the longitude.
Definition: FGLocation.cpp:197
void SetLatitude(double latitude)
Set the latitude.
Definition: FGLocation.cpp:217
void SetRadius(double radius)
Set the distance from the center of the earth.
Definition: FGLocation.cpp:241
double GetSinLatitude() const
Get the sine of Latitude.
Definition: FGLocation.h:296
double GetDistanceTo(double target_longitude, double target_latitude) const
Get the geodetic distance between the current location and a given location.
Definition: FGLocation.cpp:418
double GetLatitude() const
Get the latitude.
Definition: FGLocation.h:272
FGLocation holds an arbitrary location in the Earth centered Earth fixed reference frame (ECEF)...
Definition: FGLocation.h:160
void SetPositionGeodetic(double lon, double lat, double height)
Sets the longitude, latitude and the distance above the reference ellipsoid.
Definition: FGLocation.cpp:270
double GetRadius() const
Get the distance from the center of the earth.
Definition: FGLocation.h:323
void SetEllipse(double semimajor, double semiminor)
Sets the semimajor and semiminor axis lengths for this planet.
Definition: FGLocation.cpp:285
const FGLocation & operator=(const FGColumnVector3 &v)
Sets this location via the supplied vector.
Definition: FGLocation.h:525
double GetLongitude() const
Get the longitude.
Definition: FGLocation.h:254
double GetHeadingTo(double target_longitude, double target_latitude) const
Get the heading that should be followed from the current location to a given location along the short...
Definition: FGLocation.cpp:447
FGLocation(void)
Default constructor.
Definition: FGLocation.cpp:60
This class implements a 3 element column vector.
double GetCosLatitude() const
Get the cosine of Latitude.
Definition: FGLocation.h:299
Handles matrix math operations.
Definition: FGMatrix33.h:92
FGMatrix33 Transposed(void) const
Transposed matrix.
Definition: FGMatrix33.h:243
double Magnitude(void) const
Length of the vector.
void InitMatrix(void)
Initialize the matrix.
Definition: FGMatrix33.cpp:257