FlightGear next
FGLocation.cpp
Go to the documentation of this file.
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
14 Software Foundation; either version 2 of the License, or (at your option) any
15 later 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
23 with this program; if not, write to the Free Software Foundation, Inc., 59
24 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26 Further information about the GNU Lesser General Public License can also be
27 found on the world wide web at http://www.gnu.org.
28
29FUNCTIONAL DESCRIPTION
30------------------------------------------------------------------------------
31This class encapsulates an arbitrary position in the globe with its accessors.
32It has vector properties, so you can add multiply ....
33
34HISTORY
35------------------------------------------------------------------------------
3604/04/2004 MF Created
3711/01/2011 ORT Encapsulated ground callback code in FGLocation and removed
38 it from FGFDMExec.
39
40%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41INCLUDES
42%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
43
44#include <cmath>
45
46#include "FGLocation.h"
47
48namespace JSBSim {
49
50/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
51CLASS IMPLEMENTATION
52%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
53
55 : mECLoc(1.0, 0.0, 0.0), mCacheValid(false)
56{
57 e2 = c = 0.0;
58 a = ec = ec2 = 1.0;
59
60 mLon = mLat = mRadius = 0.0;
61 mGeodLat = GeodeticAltitude = 0.0;
62
63 mTl2ec.InitMatrix();
64 mTec2l.InitMatrix();
65}
66
67//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68
69FGLocation::FGLocation(double lon, double lat, double radius)
70 : mCacheValid(false)
71{
72 e2 = c = 0.0;
73 a = ec = ec2 = 1.0;
74
75 mLon = mLat = mRadius = 0.0;
76 mGeodLat = GeodeticAltitude = 0.0;
77
78 mTl2ec.InitMatrix();
79 mTec2l.InitMatrix();
80
81 double sinLat = sin(lat);
82 double cosLat = cos(lat);
83 double sinLon = sin(lon);
84 double cosLon = cos(lon);
85 mECLoc = { radius*cosLat*cosLon,
86 radius*cosLat*sinLon,
87 radius*sinLat };
88}
89
90//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91
93 : mECLoc(lv), mCacheValid(false)
94{
95 e2 = c = 0.0;
96 a = ec = ec2 = 1.0;
97
98 mLon = mLat = mRadius = 0.0;
99 mGeodLat = GeodeticAltitude = 0.0;
100
101 mTl2ec.InitMatrix();
102 mTec2l.InitMatrix();
103}
104
105//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106
108 : mECLoc(l.mECLoc), mCacheValid(l.mCacheValid)
109{
110 a = l.a;
111 e2 = l.e2;
112 c = l.c;
113 ec = l.ec;
114 ec2 = l.ec2;
115 mEllipseSet = l.mEllipseSet;
116
117 /*ag
118 * if the cache is not valid, all of the following values are unset.
119 * They will be calculated once ComputeDerivedUnconditional is called.
120 * If unset, they may possibly contain NaN and could thus trigger floating
121 * point exceptions.
122 */
123 if (!mCacheValid) return;
124
125 mLon = l.mLon;
126 mLat = l.mLat;
127 mRadius = l.mRadius;
128
129 mTl2ec = l.mTl2ec;
130 mTec2l = l.mTec2l;
131
132 mGeodLat = l.mGeodLat;
133 GeodeticAltitude = l.GeodeticAltitude;
134}
135
136//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
137
139{
140 mECLoc = l.mECLoc;
141 mCacheValid = l.mCacheValid;
142 mEllipseSet = l.mEllipseSet;
143
144 a = l.a;
145 e2 = l.e2;
146 c = l.c;
147 ec = l.ec;
148 ec2 = l.ec2;
149
150 //ag See comment in constructor above
151 if (!mCacheValid) return *this;
152
153 mLon = l.mLon;
154 mLat = l.mLat;
155 mRadius = l.mRadius;
156
157 mTl2ec = l.mTl2ec;
158 mTec2l = l.mTec2l;
159
160 mGeodLat = l.mGeodLat;
161 GeodeticAltitude = l.GeodeticAltitude;
162
163 return *this;
164}
165
166//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167
169{
170 double rtmp = mECLoc.Magnitude(eX, eY);
171 // Check if we have zero radius.
172 // If so set it to 1, so that we can set a position
173 if (0.0 == mECLoc.Magnitude())
174 rtmp = 1.0;
175
176 // Fast return if we are on the north or south pole ...
177 if (rtmp == 0.0)
178 return;
179
180 mCacheValid = false;
181
182 mECLoc(eX) = rtmp*cos(longitude);
183 mECLoc(eY) = rtmp*sin(longitude);
184}
185
186//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187
189{
190 mCacheValid = false;
191
192 double r = mECLoc.Magnitude();
193 if (r == 0.0) {
194 mECLoc(eX) = 1.0;
195 r = 1.0;
196 }
197
198 double rtmp = mECLoc.Magnitude(eX, eY);
199 if (rtmp != 0.0) {
200 double fac = r/rtmp*cos(latitude);
201 mECLoc(eX) *= fac;
202 mECLoc(eY) *= fac;
203 } else {
204 mECLoc(eX) = r*cos(latitude);
205 mECLoc(eY) = 0.0;
206 }
207 mECLoc(eZ) = r*sin(latitude);
208}
209
210//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
211
212void FGLocation::SetRadius(double radius)
213{
214 mCacheValid = false;
215
216 double rold = mECLoc.Magnitude();
217 if (rold == 0.0)
218 mECLoc(eX) = radius;
219 else
220 mECLoc *= radius/rold;
221}
222
223//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224
225void FGLocation::SetPosition(double lon, double lat, double radius)
226{
227 mCacheValid = false;
228
229 double sinLat = sin(lat);
230 double cosLat = cos(lat);
231 double sinLon = sin(lon);
232 double cosLon = cos(lon);
233
234 mECLoc = { radius*cosLat*cosLon,
235 radius*cosLat*sinLon,
236 radius*sinLat };
237}
238
239//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
240
241void FGLocation::SetPositionGeodetic(double lon, double lat, double height)
242{
243 assert(mEllipseSet);
244 mCacheValid = false;
245
246 double slat = sin(lat);
247 double clat = cos(lat);
248 double RN = a / sqrt(1.0 - e2*slat*slat);
249
250 mECLoc(eX) = (RN + height)*clat*cos(lon);
251 mECLoc(eY) = (RN + height)*clat*sin(lon);
252 mECLoc(eZ) = ((1 - e2)*RN + height)*slat;
253}
254
255//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
256
257void FGLocation::SetEllipse(double semimajor, double semiminor)
258{
259 mCacheValid = false;
260 mEllipseSet = true;
261
262 a = semimajor;
263 ec = semiminor/a;
264 ec2 = ec * ec;
265 e2 = 1.0 - ec2;
266 c = a * e2;
267}
268
269//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270
272{
273 assert(mEllipseSet);
274 ComputeDerived();
275 double cosLat = cos(mLat);
276 return a*ec/sqrt(1.0-e2*cosLat*cosLat);
277}
278
279//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280
281void FGLocation::ComputeDerivedUnconditional(void) const
282{
283 // The radius is just the Euclidean norm of the vector.
284 mRadius = mECLoc.Magnitude();
285
286 // The distance of the location to the Z-axis, which is the axis
287 // through the poles.
288 double rxy = mECLoc.Magnitude(eX, eY);
289
290 // Compute the longitude and its sin/cos values.
291 double sinLon, cosLon;
292 if (rxy == 0.0) {
293 sinLon = 0.0;
294 cosLon = 1.0;
295 mLon = 0.0;
296 } else {
297 sinLon = mECLoc(eY)/rxy;
298 cosLon = mECLoc(eX)/rxy;
299 mLon = atan2(mECLoc(eY), mECLoc(eX));
300 }
301
302 // Compute the geocentric & geodetic latitudes.
303 double sinLat, cosLat;
304 if (mRadius == 0.0) {
305 mLat = 0.0;
306 sinLat = 0.0;
307 cosLat = 1.0;
308 if (mEllipseSet) {
309 mGeodLat = 0.0;
310 GeodeticAltitude = -a;
311 }
312 }
313 else {
314 mLat = atan2( mECLoc(eZ), rxy );
315
316 // Calculate the geodetic latitude based on "Transformation from Cartesian to
317 // geodetic coordinates accelerated by Halley's method", Fukushima T. (2006)
318 // Journal of Geodesy, Vol. 79, pp. 689-693
319 // Unlike I. Sofair's method which uses a closed form solution, Fukushima's
320 // method is an iterative method whose convergence is so fast that only one
321 // iteration suffices. In addition, Fukushima's method has a much better
322 // numerical stability over Sofair's method at the North and South poles and
323 // it also gives the correct result for a spherical Earth.
324 if (mEllipseSet) {
325 double s0 = fabs(mECLoc(eZ));
326 double zc = ec * s0;
327 double c0 = ec * rxy;
328 double c02 = c0 * c0;
329 double s02 = s0 * s0;
330 double a02 = c02 + s02;
331 double a0 = sqrt(a02);
332 double a03 = a02 * a0;
333 double s1 = zc*a03 + c*s02*s0;
334 double c1 = rxy*a03 - c*c02*c0;
335 double cs0c0 = c*c0*s0;
336 double b0 = 1.5*cs0c0*((rxy*s0-zc*c0)*a0-cs0c0);
337 s1 = s1*a03-b0*s0;
338 double cc = ec*(c1*a03-b0*c0);
339 mGeodLat = sign(mECLoc(eZ))*atan(s1 / cc);
340 double s12 = s1 * s1;
341 double cc2 = cc * cc;
342 double norm = sqrt(s12 + cc2);
343 cosLat = cc / norm;
344 sinLat = sign(mECLoc(eZ)) * s1 / norm;
345 GeodeticAltitude = (rxy*cc + s0*s1 - a*sqrt(ec2*s12 + cc2)) / norm;
346 }
347 else {
348 sinLat = mECLoc(eZ)/mRadius;
349 cosLat = rxy/mRadius;
350 }
351 }
352
353 // Compute the transform matrices from and to the earth centered frame.
354 // See Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
355 // Eqn. 1.4-13, page 40. In Stevens and Lewis notation, this is C_n/e - the
356 // orientation of the navigation (local) frame relative to the ECEF frame,
357 // and a transformation from ECEF to nav (local) frame.
358
359 mTec2l = { -cosLon*sinLat, -sinLon*sinLat, cosLat,
360 -sinLon , cosLon , 0.0 ,
361 -cosLon*cosLat, -sinLon*cosLat, -sinLat };
362
363 // In Stevens and Lewis notation, this is C_e/n - the
364 // orientation of the ECEF frame relative to the nav (local) frame,
365 // and a transformation from nav (local) to ECEF frame.
366
367 mTl2ec = mTec2l.Transposed();
368
369 // Mark the cached values as valid
370 mCacheValid = true;
371}
372
373//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374// The calculations, below, implement the Haversine formulas to calculate
375// heading and distance to a set of lat/long coordinates from the current
376// position.
377//
378// The basic equations are (lat1, long1 are source positions; lat2
379// long2 are target positions):
380//
381// R = earth’s radius
382// Δlat = lat2 − lat1
383// Δlong = long2 − long1
384//
385// For the waypoint distance calculation:
386//
387// a = sin²(Δlat/2) + cos(lat1)∙cos(lat2)∙sin²(Δlong/2)
388// c = 2∙atan2(√a, √(1−a))
389// d = R∙c
390
391double FGLocation::GetDistanceTo(double target_longitude,
392 double target_latitude) const
393{
394 ComputeDerived();
395 double delta_lat_rad = target_latitude - mLat;
396 double delta_lon_rad = target_longitude - mLon;
397
398 double distance_a = pow(sin(0.5*delta_lat_rad), 2.0)
399 + (cos(mLat) * cos(target_latitude)
400 * (pow(sin(0.5*delta_lon_rad), 2.0)));
401
402 return 2.0 * GetRadius() * atan2(sqrt(distance_a), sqrt(1.0 - distance_a));
403}
404
405//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406// The calculations, below, implement the Haversine formulas to calculate
407// heading and distance to a set of lat/long coordinates from the current
408// position.
409//
410// The basic equations are (lat1, long1 are source positions; lat2
411// long2 are target positions):
412//
413// R = earth’s radius
414// Δlat = lat2 − lat1
415// Δlong = long2 − long1
416//
417// For the heading angle calculation:
418//
419// θ = atan2(sin(Δlong)∙cos(lat2), cos(lat1)∙sin(lat2) − sin(lat1)∙cos(lat2)∙cos(Δlong))
420
421double FGLocation::GetHeadingTo(double target_longitude,
422 double target_latitude) const
423{
424 ComputeDerived();
425 double delta_lon_rad = target_longitude - mLon;
426
427 double Y = sin(delta_lon_rad) * cos(target_latitude);
428 double X = cos(mLat) * sin(target_latitude)
429 - sin(mLat) * cos(target_latitude) * cos(delta_lon_rad);
430
431 double heading_to_waypoint_rad = atan2(Y, X);
432 if (heading_to_waypoint_rad < 0) heading_to_waypoint_rad += 2.0*M_PI;
433
434 return heading_to_waypoint_rad;
435}
436
437//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438
439} // namespace JSBSim
double latitude
Definition ADA.cxx:53
double longitude
Definition ADA.cxx:54
#define M_PI
Definition FGJSBBase.h:50
This class implements a 3 element column vector.
double Magnitude(void) const
Length of the vector.
static constexpr double sign(double num)
Definition FGJSBBase.h:337
void SetLongitude(double longitude)
Set the longitude.
const FGLocation & operator=(const FGColumnVector3 &v)
Sets this location via the supplied vector.
Definition FGLocation.h:384
FGLocation(void)
Default constructor.
double GetRadius() const
Get the distance from the center of the earth in feet.
Definition FGLocation.h:291
void SetPositionGeodetic(double lon, double lat, double height)
Sets the longitude, latitude and the distance above the reference spheroid.
double GetDistanceTo(double target_longitude, double target_latitude) const
Get the geodetic distance between the current location and a given location.
void SetRadius(double radius)
Set the distance from the center of the earth.
void SetPosition(double lon, double lat, double radius)
Sets the longitude, latitude and the distance from the center of the earth.
double GetSeaLevelRadius(void) const
Get the sea level radius in feet below the current location.
void SetLatitude(double latitude)
Set the GEOCENTRIC latitude.
void SetEllipse(double semimajor, double semiminor)
Sets the semimajor and semiminor axis lengths for this planet.
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...
#define Y