geographiclib 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +12 -0
- data/LICENSE +24 -0
- data/ext/geographiclib/Accumulator.cpp +23 -0
- data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
- data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
- data/ext/geographiclib/CassiniSoldner.cpp +89 -0
- data/ext/geographiclib/CircularEngine.cpp +96 -0
- data/ext/geographiclib/DMS.cpp +381 -0
- data/ext/geographiclib/Ellipsoid.cpp +125 -0
- data/ext/geographiclib/EllipticFunction.cpp +512 -0
- data/ext/geographiclib/GARS.cpp +122 -0
- data/ext/geographiclib/GeoCoords.cpp +175 -0
- data/ext/geographiclib/Geocentric.cpp +172 -0
- data/ext/geographiclib/Geodesic.cpp +1908 -0
- data/ext/geographiclib/GeodesicExact.cpp +927 -0
- data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
- data/ext/geographiclib/GeodesicLine.cpp +321 -0
- data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
- data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
- data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
- data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
- data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
- data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
- data/ext/geographiclib/GeographicLib/Config.h +12 -0
- data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
- data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
- data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
- data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
- data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
- data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
- data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
- data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
- data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
- data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
- data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
- data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
- data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
- data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
- data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
- data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
- data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
- data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
- data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
- data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
- data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
- data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
- data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
- data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
- data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
- data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
- data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
- data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
- data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
- data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
- data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
- data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
- data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
- data/ext/geographiclib/Geohash.cpp +102 -0
- data/ext/geographiclib/Geoid.cpp +509 -0
- data/ext/geographiclib/Georef.cpp +135 -0
- data/ext/geographiclib/Gnomonic.cpp +85 -0
- data/ext/geographiclib/GravityCircle.cpp +129 -0
- data/ext/geographiclib/GravityModel.cpp +360 -0
- data/ext/geographiclib/LambertConformalConic.cpp +456 -0
- data/ext/geographiclib/LocalCartesian.cpp +62 -0
- data/ext/geographiclib/MGRS.cpp +461 -0
- data/ext/geographiclib/MagneticCircle.cpp +52 -0
- data/ext/geographiclib/MagneticModel.cpp +269 -0
- data/ext/geographiclib/Math.cpp +63 -0
- data/ext/geographiclib/NormalGravity.cpp +262 -0
- data/ext/geographiclib/OSGB.cpp +167 -0
- data/ext/geographiclib/PolarStereographic.cpp +108 -0
- data/ext/geographiclib/PolygonArea.cpp +204 -0
- data/ext/geographiclib/Rhumb.cpp +383 -0
- data/ext/geographiclib/SphericalEngine.cpp +477 -0
- data/ext/geographiclib/TransverseMercator.cpp +603 -0
- data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
- data/ext/geographiclib/UTMUPS.cpp +296 -0
- data/ext/geographiclib/Utility.cpp +61 -0
- data/ext/geographiclib/extconf.rb +3 -0
- data/ext/geographiclib/geographiclib.cpp +62 -0
- data/lib/geographiclib.rb +20 -0
- metadata +140 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
/**
|
2
|
+
* \file MagneticCircle.cpp
|
3
|
+
* \brief Implementation for GeographicLib::MagneticCircle class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2011-2015) <charles@karney.com> and licensed
|
6
|
+
* under the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/MagneticCircle.hpp>
|
11
|
+
#include <fstream>
|
12
|
+
#include <sstream>
|
13
|
+
#include <GeographicLib/Geocentric.hpp>
|
14
|
+
|
15
|
+
namespace GeographicLib {
|
16
|
+
|
17
|
+
using namespace std;
|
18
|
+
|
19
|
+
void MagneticCircle::Field(real lon, bool diffp,
|
20
|
+
real& Bx, real& By, real& Bz,
|
21
|
+
real& Bxt, real& Byt, real& Bzt) const {
|
22
|
+
real slam, clam;
|
23
|
+
Math::sincosd(lon, slam, clam);
|
24
|
+
real M[Geocentric::dim2_];
|
25
|
+
Geocentric::Rotation(_sphi, _cphi, slam, clam, M);
|
26
|
+
real BX0, BY0, BZ0, BX1, BY1, BZ1; // Components in geocentric basis
|
27
|
+
real BXc = 0, BYc = 0, BZc = 0;
|
28
|
+
_circ0(slam, clam, BX0, BY0, BZ0);
|
29
|
+
_circ1(slam, clam, BX1, BY1, BZ1);
|
30
|
+
if (_constterm)
|
31
|
+
_circ2(slam, clam, BXc, BYc, BZc);
|
32
|
+
if (_interpolate) {
|
33
|
+
BX1 = (BX1 - BX0) / _dt0;
|
34
|
+
BY1 = (BY1 - BY0) / _dt0;
|
35
|
+
BZ1 = (BZ1 - BZ0) / _dt0;
|
36
|
+
}
|
37
|
+
BX0 += _t1 * BX1 + BXc;
|
38
|
+
BY0 += _t1 * BY1 + BYc;
|
39
|
+
BZ0 += _t1 * BZ1 + BZc;
|
40
|
+
if (diffp) {
|
41
|
+
Geocentric::Unrotate(M, BX1, BY1, BZ1, Bxt, Byt, Bzt);
|
42
|
+
Bxt *= - _a;
|
43
|
+
Byt *= - _a;
|
44
|
+
Bzt *= - _a;
|
45
|
+
}
|
46
|
+
Geocentric::Unrotate(M, BX0, BY0, BZ0, Bx, By, Bz);
|
47
|
+
Bx *= - _a;
|
48
|
+
By *= - _a;
|
49
|
+
Bz *= - _a;
|
50
|
+
}
|
51
|
+
|
52
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,269 @@
|
|
1
|
+
/**
|
2
|
+
* \file MagneticModel.cpp
|
3
|
+
* \brief Implementation for GeographicLib::MagneticModel class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2011-2015) <charles@karney.com> and licensed
|
6
|
+
* under the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/MagneticModel.hpp>
|
11
|
+
#include <fstream>
|
12
|
+
#include <GeographicLib/SphericalEngine.hpp>
|
13
|
+
#include <GeographicLib/MagneticCircle.hpp>
|
14
|
+
#include <GeographicLib/Utility.hpp>
|
15
|
+
|
16
|
+
#if !defined(GEOGRAPHICLIB_DATA)
|
17
|
+
# if defined(_WIN32)
|
18
|
+
# define GEOGRAPHICLIB_DATA "C:/ProgramData/GeographicLib"
|
19
|
+
# else
|
20
|
+
# define GEOGRAPHICLIB_DATA "/usr/local/share/GeographicLib"
|
21
|
+
# endif
|
22
|
+
#endif
|
23
|
+
|
24
|
+
#if !defined(GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME)
|
25
|
+
# define GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME "wmm2015"
|
26
|
+
#endif
|
27
|
+
|
28
|
+
#if defined(_MSC_VER)
|
29
|
+
// Squelch warnings about unsafe use of getenv
|
30
|
+
# pragma warning (disable: 4996)
|
31
|
+
#endif
|
32
|
+
|
33
|
+
namespace GeographicLib {
|
34
|
+
|
35
|
+
using namespace std;
|
36
|
+
|
37
|
+
MagneticModel::MagneticModel(const std::string& name,const std::string& path,
|
38
|
+
const Geocentric& earth)
|
39
|
+
: _name(name)
|
40
|
+
, _dir(path)
|
41
|
+
, _description("NONE")
|
42
|
+
, _date("UNKNOWN")
|
43
|
+
, _t0(Math::NaN())
|
44
|
+
, _dt0(1)
|
45
|
+
, _tmin(Math::NaN())
|
46
|
+
, _tmax(Math::NaN())
|
47
|
+
, _a(Math::NaN())
|
48
|
+
, _hmin(Math::NaN())
|
49
|
+
, _hmax(Math::NaN())
|
50
|
+
, _Nmodels(1)
|
51
|
+
, _Nconstants(0)
|
52
|
+
, _norm(SphericalHarmonic::SCHMIDT)
|
53
|
+
, _earth(earth)
|
54
|
+
{
|
55
|
+
if (_dir.empty())
|
56
|
+
_dir = DefaultMagneticPath();
|
57
|
+
ReadMetadata(_name);
|
58
|
+
_G.resize(_Nmodels + 1 + _Nconstants);
|
59
|
+
_H.resize(_Nmodels + 1 + _Nconstants);
|
60
|
+
{
|
61
|
+
string coeff = _filename + ".cof";
|
62
|
+
ifstream coeffstr(coeff.c_str(), ios::binary);
|
63
|
+
if (!coeffstr.good())
|
64
|
+
throw GeographicErr("Error opening " + coeff);
|
65
|
+
char id[idlength_ + 1];
|
66
|
+
coeffstr.read(id, idlength_);
|
67
|
+
if (!coeffstr.good())
|
68
|
+
throw GeographicErr("No header in " + coeff);
|
69
|
+
id[idlength_] = '\0';
|
70
|
+
if (_id != string(id))
|
71
|
+
throw GeographicErr("ID mismatch: " + _id + " vs " + id);
|
72
|
+
for (int i = 0; i < _Nmodels + 1 + _Nconstants; ++i) {
|
73
|
+
int N, M;
|
74
|
+
SphericalEngine::coeff::readcoeffs(coeffstr, N, M, _G[i], _H[i]);
|
75
|
+
if (!(M < 0 || _G[i][0] == 0))
|
76
|
+
throw GeographicErr("A degree 0 term is not permitted");
|
77
|
+
_harm.push_back(SphericalHarmonic(_G[i], _H[i], N, N, M, _a, _norm));
|
78
|
+
}
|
79
|
+
int pos = int(coeffstr.tellg());
|
80
|
+
coeffstr.seekg(0, ios::end);
|
81
|
+
if (pos != coeffstr.tellg())
|
82
|
+
throw GeographicErr("Extra data in " + coeff);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
void MagneticModel::ReadMetadata(const std::string& name) {
|
87
|
+
const char* spaces = " \t\n\v\f\r";
|
88
|
+
_filename = _dir + "/" + name + ".wmm";
|
89
|
+
ifstream metastr(_filename.c_str());
|
90
|
+
if (!metastr.good())
|
91
|
+
throw GeographicErr("Cannot open " + _filename);
|
92
|
+
string line;
|
93
|
+
getline(metastr, line);
|
94
|
+
if (!(line.size() >= 6 && line.substr(0,5) == "WMMF-"))
|
95
|
+
throw GeographicErr(_filename + " does not contain WMMF-n signature");
|
96
|
+
string::size_type n = line.find_first_of(spaces, 5);
|
97
|
+
if (n != string::npos)
|
98
|
+
n -= 5;
|
99
|
+
string version = line.substr(5, n);
|
100
|
+
if (!(version == "1" || version == "2"))
|
101
|
+
throw GeographicErr("Unknown version in " + _filename + ": " + version);
|
102
|
+
string key, val;
|
103
|
+
while (getline(metastr, line)) {
|
104
|
+
if (!Utility::ParseLine(line, key, val))
|
105
|
+
continue;
|
106
|
+
// Process key words
|
107
|
+
if (key == "Name")
|
108
|
+
_name = val;
|
109
|
+
else if (key == "Description")
|
110
|
+
_description = val;
|
111
|
+
else if (key == "ReleaseDate")
|
112
|
+
_date = val;
|
113
|
+
else if (key == "Radius")
|
114
|
+
_a = Utility::num<real>(val);
|
115
|
+
else if (key == "Type") {
|
116
|
+
if (!(val == "Linear" || val == "linear"))
|
117
|
+
throw GeographicErr("Only linear models are supported");
|
118
|
+
} else if (key == "Epoch")
|
119
|
+
_t0 = Utility::num<real>(val);
|
120
|
+
else if (key == "DeltaEpoch")
|
121
|
+
_dt0 = Utility::num<real>(val);
|
122
|
+
else if (key == "NumModels")
|
123
|
+
_Nmodels = Utility::num<int>(val);
|
124
|
+
else if (key == "NumConstants")
|
125
|
+
_Nconstants = Utility::num<int>(val);
|
126
|
+
else if (key == "MinTime")
|
127
|
+
_tmin = Utility::num<real>(val);
|
128
|
+
else if (key == "MaxTime")
|
129
|
+
_tmax = Utility::num<real>(val);
|
130
|
+
else if (key == "MinHeight")
|
131
|
+
_hmin = Utility::num<real>(val);
|
132
|
+
else if (key == "MaxHeight")
|
133
|
+
_hmax = Utility::num<real>(val);
|
134
|
+
else if (key == "Normalization") {
|
135
|
+
if (val == "FULL" || val == "Full" || val == "full")
|
136
|
+
_norm = SphericalHarmonic::FULL;
|
137
|
+
else if (val == "SCHMIDT" || val == "Schmidt" || val == "schmidt")
|
138
|
+
_norm = SphericalHarmonic::SCHMIDT;
|
139
|
+
else
|
140
|
+
throw GeographicErr("Unknown normalization " + val);
|
141
|
+
} else if (key == "ByteOrder") {
|
142
|
+
if (val == "Big" || val == "big")
|
143
|
+
throw GeographicErr("Only little-endian ordering is supported");
|
144
|
+
else if (!(val == "Little" || val == "little"))
|
145
|
+
throw GeographicErr("Unknown byte ordering " + val);
|
146
|
+
} else if (key == "ID")
|
147
|
+
_id = val;
|
148
|
+
// else unrecognized keywords are skipped
|
149
|
+
}
|
150
|
+
// Check values
|
151
|
+
if (!(Math::isfinite(_a) && _a > 0))
|
152
|
+
throw GeographicErr("Reference radius must be positive");
|
153
|
+
if (!(_t0 > 0))
|
154
|
+
throw GeographicErr("Epoch time not defined");
|
155
|
+
if (_tmin >= _tmax)
|
156
|
+
throw GeographicErr("Min time exceeds max time");
|
157
|
+
if (_hmin >= _hmax)
|
158
|
+
throw GeographicErr("Min height exceeds max height");
|
159
|
+
if (int(_id.size()) != idlength_)
|
160
|
+
throw GeographicErr("Invalid ID");
|
161
|
+
if (_Nmodels < 1)
|
162
|
+
throw GeographicErr("NumModels must be positive");
|
163
|
+
if (!(_Nconstants == 0 || _Nconstants == 1))
|
164
|
+
throw GeographicErr("NumConstants must be 0 or 1");
|
165
|
+
if (!(_dt0 > 0)) {
|
166
|
+
if (_Nmodels > 1)
|
167
|
+
throw GeographicErr("DeltaEpoch must be positive");
|
168
|
+
else
|
169
|
+
_dt0 = 1;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
void MagneticModel::Field(real t, real lat, real lon, real h, bool diffp,
|
174
|
+
real& Bx, real& By, real& Bz,
|
175
|
+
real& Bxt, real& Byt, real& Bzt) const {
|
176
|
+
t -= _t0;
|
177
|
+
int n = max(min(int(floor(t / _dt0)), _Nmodels - 1), 0);
|
178
|
+
bool interpolate = n + 1 < _Nmodels;
|
179
|
+
t -= n * _dt0;
|
180
|
+
real X, Y, Z;
|
181
|
+
real M[Geocentric::dim2_];
|
182
|
+
_earth.IntForward(lat, lon, h, X, Y, Z, M);
|
183
|
+
// Components in geocentric basis
|
184
|
+
// initial values to suppress warning
|
185
|
+
real BX0 = 0, BY0 = 0, BZ0 = 0, BX1 = 0, BY1 = 0, BZ1 = 0;
|
186
|
+
real BXc = 0, BYc = 0, BZc = 0;
|
187
|
+
_harm[n](X, Y, Z, BX0, BY0, BZ0);
|
188
|
+
_harm[n + 1](X, Y, Z, BX1, BY1, BZ1);
|
189
|
+
if (_Nconstants)
|
190
|
+
_harm[_Nmodels + 1](X, Y, Z, BXc, BYc, BZc);
|
191
|
+
if (interpolate) {
|
192
|
+
// Convert to a time derivative
|
193
|
+
BX1 = (BX1 - BX0) / _dt0;
|
194
|
+
BY1 = (BY1 - BY0) / _dt0;
|
195
|
+
BZ1 = (BZ1 - BZ0) / _dt0;
|
196
|
+
}
|
197
|
+
BX0 += t * BX1 + BXc;
|
198
|
+
BY0 += t * BY1 + BYc;
|
199
|
+
BZ0 += t * BZ1 + BZc;
|
200
|
+
if (diffp) {
|
201
|
+
Geocentric::Unrotate(M, BX1, BY1, BZ1, Bxt, Byt, Bzt);
|
202
|
+
Bxt *= - _a;
|
203
|
+
Byt *= - _a;
|
204
|
+
Bzt *= - _a;
|
205
|
+
}
|
206
|
+
Geocentric::Unrotate(M, BX0, BY0, BZ0, Bx, By, Bz);
|
207
|
+
Bx *= - _a;
|
208
|
+
By *= - _a;
|
209
|
+
Bz *= - _a;
|
210
|
+
}
|
211
|
+
|
212
|
+
MagneticCircle MagneticModel::Circle(real t, real lat, real h) const {
|
213
|
+
real t1 = t - _t0;
|
214
|
+
int n = max(min(int(floor(t1 / _dt0)), _Nmodels - 1), 0);
|
215
|
+
bool interpolate = n + 1 < _Nmodels;
|
216
|
+
t1 -= n * _dt0;
|
217
|
+
real X, Y, Z, M[Geocentric::dim2_];
|
218
|
+
_earth.IntForward(lat, 0, h, X, Y, Z, M);
|
219
|
+
// Y = 0, cphi = M[7], sphi = M[8];
|
220
|
+
|
221
|
+
return (_Nconstants == 0 ?
|
222
|
+
MagneticCircle(_a, _earth._f, lat, h, t,
|
223
|
+
M[7], M[8], t1, _dt0, interpolate,
|
224
|
+
_harm[n].Circle(X, Z, true),
|
225
|
+
_harm[n + 1].Circle(X, Z, true)) :
|
226
|
+
MagneticCircle(_a, _earth._f, lat, h, t,
|
227
|
+
M[7], M[8], t1, _dt0, interpolate,
|
228
|
+
_harm[n].Circle(X, Z, true),
|
229
|
+
_harm[n + 1].Circle(X, Z, true),
|
230
|
+
_harm[_Nmodels + 1].Circle(X, Z, true)));
|
231
|
+
}
|
232
|
+
|
233
|
+
void MagneticModel::FieldComponents(real Bx, real By, real Bz,
|
234
|
+
real Bxt, real Byt, real Bzt,
|
235
|
+
real& H, real& F, real& D, real& I,
|
236
|
+
real& Ht, real& Ft,
|
237
|
+
real& Dt, real& It) {
|
238
|
+
H = Math::hypot(Bx, By);
|
239
|
+
Ht = H ? (Bx * Bxt + By * Byt) / H : Math::hypot(Bxt, Byt);
|
240
|
+
D = H ? Math::atan2d(Bx, By) : Math::atan2d(Bxt, Byt);
|
241
|
+
Dt = (H ? (By * Bxt - Bx * Byt) / Math::sq(H) : 0) / Math::degree();
|
242
|
+
F = Math::hypot(H, Bz);
|
243
|
+
Ft = F ? (H * Ht + Bz * Bzt) / F : Math::hypot(Ht, Bzt);
|
244
|
+
I = F ? Math::atan2d(-Bz, H) : Math::atan2d(-Bzt, Ht);
|
245
|
+
It = (F ? (Bz * Ht - H * Bzt) / Math::sq(F) : 0) / Math::degree();
|
246
|
+
}
|
247
|
+
|
248
|
+
std::string MagneticModel::DefaultMagneticPath() {
|
249
|
+
string path;
|
250
|
+
char* magneticpath = getenv("GEOGRAPHICLIB_MAGNETIC_PATH");
|
251
|
+
if (magneticpath)
|
252
|
+
path = string(magneticpath);
|
253
|
+
if (!path.empty())
|
254
|
+
return path;
|
255
|
+
char* datapath = getenv("GEOGRAPHICLIB_DATA");
|
256
|
+
if (datapath)
|
257
|
+
path = string(datapath);
|
258
|
+
return (!path.empty() ? path : string(GEOGRAPHICLIB_DATA)) + "/magnetic";
|
259
|
+
}
|
260
|
+
|
261
|
+
std::string MagneticModel::DefaultMagneticName() {
|
262
|
+
string name;
|
263
|
+
char* magneticname = getenv("GEOGRAPHICLIB_MAGNETIC_NAME");
|
264
|
+
if (magneticname)
|
265
|
+
name = string(magneticname);
|
266
|
+
return !name.empty() ? name : string(GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME);
|
267
|
+
}
|
268
|
+
|
269
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,63 @@
|
|
1
|
+
/**
|
2
|
+
* \file Math.cpp
|
3
|
+
* \brief Implementation for GeographicLib::Math class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2015) <charles@karney.com> and licensed
|
6
|
+
* under the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/Math.hpp>
|
11
|
+
|
12
|
+
#if defined(_MSC_VER)
|
13
|
+
// Squelch warnings about constant conditional expressions
|
14
|
+
# pragma warning (disable: 4127)
|
15
|
+
#endif
|
16
|
+
|
17
|
+
namespace GeographicLib {
|
18
|
+
|
19
|
+
using namespace std;
|
20
|
+
|
21
|
+
template<typename T> T Math::eatanhe(T x, T es) {
|
22
|
+
return es > T(0) ? es * atanh(es * x) : -es * atan(es * x);
|
23
|
+
}
|
24
|
+
|
25
|
+
template<typename T> T Math::taupf(T tau, T es) {
|
26
|
+
T tau1 = hypot(T(1), tau),
|
27
|
+
sig = sinh( eatanhe(tau / tau1, es ) );
|
28
|
+
return hypot(T(1), sig) * tau - sig * tau1;
|
29
|
+
}
|
30
|
+
|
31
|
+
template<typename T> T Math::tauf(T taup, T es) {
|
32
|
+
static const int numit = 5;
|
33
|
+
static const T tol = sqrt(numeric_limits<T>::epsilon()) / T(10);
|
34
|
+
T e2m = T(1) - sq(es),
|
35
|
+
// To lowest order in e^2, taup = (1 - e^2) * tau = _e2m * tau; so use
|
36
|
+
// tau = taup/_e2m as a starting guess. (This starting guess is the
|
37
|
+
// geocentric latitude which, to first order in the flattening, is equal
|
38
|
+
// to the conformal latitude.) Only 1 iteration is needed for |lat| <
|
39
|
+
// 3.35 deg, otherwise 2 iterations are needed. If, instead, tau = taup
|
40
|
+
// is used the mean number of iterations increases to 1.99 (2 iterations
|
41
|
+
// are needed except near tau = 0).
|
42
|
+
tau = taup/e2m,
|
43
|
+
stol = tol * max(T(1), abs(taup));
|
44
|
+
// min iterations = 1, max iterations = 2; mean = 1.94
|
45
|
+
for (int i = 0; i < numit || GEOGRAPHICLIB_PANIC; ++i) {
|
46
|
+
T taupa = taupf(tau, es),
|
47
|
+
dtau = (taup - taupa) * (1 + e2m * sq(tau)) /
|
48
|
+
( e2m * hypot(T(1), tau) * hypot(T(1), taupa) );
|
49
|
+
tau += dtau;
|
50
|
+
if (!(abs(dtau) >= stol))
|
51
|
+
break;
|
52
|
+
}
|
53
|
+
return tau;
|
54
|
+
}
|
55
|
+
|
56
|
+
/// \cond SKIP
|
57
|
+
// Instantiate
|
58
|
+
template Math::real Math::eatanhe<Math::real>(Math::real, Math::real);
|
59
|
+
template Math::real Math::taupf<Math::real>(Math::real, Math::real);
|
60
|
+
template Math::real Math::tauf<Math::real>(Math::real, Math::real);
|
61
|
+
/// \endcond
|
62
|
+
|
63
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,262 @@
|
|
1
|
+
/**
|
2
|
+
* \file NormalGravity.cpp
|
3
|
+
* \brief Implementation for GeographicLib::NormalGravity class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2011-2015) <charles@karney.com> and licensed
|
6
|
+
* under the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/NormalGravity.hpp>
|
11
|
+
|
12
|
+
#if defined(_MSC_VER)
|
13
|
+
// Squelch warnings about constant conditional expressions
|
14
|
+
# pragma warning (disable: 4127)
|
15
|
+
#endif
|
16
|
+
|
17
|
+
namespace GeographicLib {
|
18
|
+
|
19
|
+
using namespace std;
|
20
|
+
|
21
|
+
NormalGravity::NormalGravity(real a, real GM, real omega, real f, real J2)
|
22
|
+
: _a(a)
|
23
|
+
, _GM(GM)
|
24
|
+
, _omega(omega)
|
25
|
+
, _f(f)
|
26
|
+
, _J2(J2)
|
27
|
+
, _omega2(Math::sq(_omega))
|
28
|
+
, _aomega2(Math::sq(_omega * _a))
|
29
|
+
{
|
30
|
+
if (!(Math::isfinite(_a) && _a > 0))
|
31
|
+
throw GeographicErr("Major radius is not positive");
|
32
|
+
if (!(Math::isfinite(_GM) && _GM > 0))
|
33
|
+
throw GeographicErr("Gravitational constant is not positive");
|
34
|
+
if (!(_omega == 0 && _f == 0 && _J2 == 0)) {
|
35
|
+
bool flatp = _f > 0 && Math::isfinite(_f);
|
36
|
+
if (_J2 > 0 && Math::isfinite(_J2) && flatp)
|
37
|
+
throw GeographicErr("Cannot specify both f and J2");
|
38
|
+
if (!(_J2 > 0 && Math::isfinite(_J2)) && !flatp)
|
39
|
+
throw GeographicErr("Must specify one of f and J2");
|
40
|
+
if (!(Math::isfinite(_omega) && _omega != 0))
|
41
|
+
throw GeographicErr("Angular velocity is not non-zero");
|
42
|
+
if (flatp)
|
43
|
+
_J2 = FlatteningToJ2(_a, _GM, _omega, _f);
|
44
|
+
else
|
45
|
+
_f = J2ToFlattening(_a, _GM, _omega, _J2);
|
46
|
+
} // else we have a sphere: omega = f = J2 = 0
|
47
|
+
_e2 = _f * (2 - _f);
|
48
|
+
_ep2 = _e2 / (1 - _e2);
|
49
|
+
_q0 = qf(_ep2);
|
50
|
+
_earth = Geocentric(_a, _f);
|
51
|
+
_b = _a * (1 - _f);
|
52
|
+
_E = _a * sqrt(_e2); // H+M, Eq 2-54
|
53
|
+
// H+M, Eq 2-61
|
54
|
+
_U0 = _GM / (_E ? _E / atan(sqrt(_ep2)) : _b) + _aomega2 / 3;
|
55
|
+
// The approximate ratio of the centrifugal acceleration (at the equator)
|
56
|
+
// to gravity.
|
57
|
+
_m = _aomega2 * _b / _GM; // H+M, Eq 2-70
|
58
|
+
real
|
59
|
+
Q = _m * (_q0 ? sqrt(_ep2) * qpf(_ep2) / (3 * _q0) : 1),
|
60
|
+
G = (1 - _m - Q / 2);
|
61
|
+
_gammae = _GM / (_a * _b) * G; // H+M, Eq 2-73
|
62
|
+
_gammap = _GM / (_a * _a) * (1 + Q); // H+M, Eq 2-74
|
63
|
+
// k = b * gammap / (a * gammae) - 1
|
64
|
+
_k = (_m + 3 * Q / 2 - _e2 * (1 + Q)) / G;
|
65
|
+
// f* = (gammap - gammae) / gammae
|
66
|
+
_fstar = (_m + 3 * Q / 2 - _f * (1 + Q)) / G;
|
67
|
+
}
|
68
|
+
|
69
|
+
const NormalGravity& NormalGravity::WGS84() {
|
70
|
+
static const NormalGravity wgs84(Constants::WGS84_a(),
|
71
|
+
Constants::WGS84_GM(),
|
72
|
+
Constants::WGS84_omega(),
|
73
|
+
Constants::WGS84_f(), 0);
|
74
|
+
return wgs84;
|
75
|
+
}
|
76
|
+
|
77
|
+
const NormalGravity& NormalGravity::GRS80() {
|
78
|
+
static const NormalGravity grs80(Constants::GRS80_a(),
|
79
|
+
Constants::GRS80_GM(),
|
80
|
+
Constants::GRS80_omega(),
|
81
|
+
0, Constants::GRS80_J2());
|
82
|
+
return grs80;
|
83
|
+
}
|
84
|
+
|
85
|
+
// (atan(y)-(y-y^3/3+y^5/5))/y^7 (y = sqrt(x)) = -1/7+x/9-x^2/11+x^3/13...
|
86
|
+
Math::real NormalGravity::atan7(real x) {
|
87
|
+
if (abs(x) >= real(0.5)) {
|
88
|
+
real y = sqrt(abs(x)), x2 = Math::sq(x);
|
89
|
+
return ((x > 0 ? atan(y) : Math::atanh(y))- y * (1 - x / 3 + x2 / 5)) /
|
90
|
+
(x * x2 * y);
|
91
|
+
} else {
|
92
|
+
real xn = -1, q = 0;
|
93
|
+
for (int n = 7; ; n += 2) {
|
94
|
+
real qn = q + xn / n;
|
95
|
+
if (qn == q)
|
96
|
+
break;
|
97
|
+
q = qn;
|
98
|
+
xn *= -x;
|
99
|
+
}
|
100
|
+
return q;
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
// (atan(y)-(y-y^3/3))/y^5 (y = sqrt(x)) = 1/5-x/7+x^2/9-x^3/11...
|
105
|
+
Math::real NormalGravity::atan5(real x)
|
106
|
+
{ return 1/real(5) + x * atan7(x); }
|
107
|
+
|
108
|
+
Math::real NormalGravity::qf(real ep2) {
|
109
|
+
// Compute
|
110
|
+
//
|
111
|
+
// ((1 + 3/e'^2) * atan(e') - 3/e')/2
|
112
|
+
//
|
113
|
+
// See H+M, Eq 2-57, with E/u = e'. This suffers from two levels of
|
114
|
+
// cancelation. The e'^-1 and e'^1 terms drop out, so that the leading
|
115
|
+
// term is O(e'^3). Substitute atan(e') = e' - e'^3/3 + e'^5*atan5(e'^2)
|
116
|
+
return sqrt(ep2) * ep2 * (3 * (3 + ep2) * atan5(ep2) - 1) / 6;
|
117
|
+
}
|
118
|
+
|
119
|
+
Math::real NormalGravity::dq(real ep2) {
|
120
|
+
// Compute d qf(ep2) / d ep2 and substitute
|
121
|
+
// atan(e') = e' - e'^3/3 + e'^5/5 + e'^7*atan7(e'^2)
|
122
|
+
return sqrt(ep2) * (5 - 3 * (1 + ep2) * (1 + 5 * ep2 * atan7(ep2))) /
|
123
|
+
(10 * (1 + ep2));
|
124
|
+
}
|
125
|
+
|
126
|
+
Math::real NormalGravity::qpf(real ep2) {
|
127
|
+
// Compute
|
128
|
+
//
|
129
|
+
// 3*(1 + 1/e'^2) * (1 - atan(e')/e') - 1
|
130
|
+
//
|
131
|
+
// See H+M, Eq 2-67, with E/u = e'. This suffers from two levels of
|
132
|
+
// cancelation. The e'^-2 and e'^0 terms drop out, so that the leading
|
133
|
+
// term is O(e'^2).
|
134
|
+
return ep2 * (1 - 3 * (1 + ep2) * atan5(ep2));
|
135
|
+
}
|
136
|
+
|
137
|
+
Math::real NormalGravity::Jn(int n) const {
|
138
|
+
// Note Jn(0) = -1; Jn(2) = _J2; Jn(odd) = 0
|
139
|
+
if (n & 1 || n < 0)
|
140
|
+
return 0;
|
141
|
+
n /= 2;
|
142
|
+
real e2n = 1; // Perhaps this should just be e2n = pow(-_e2, n);
|
143
|
+
for (int j = n; j--;)
|
144
|
+
e2n *= -_e2;
|
145
|
+
return // H+M, Eq 2-92
|
146
|
+
-3 * e2n * ((1 - n) + 5 * n * _J2 / _e2) / ((2 * n + 1) * (2 * n + 3));
|
147
|
+
}
|
148
|
+
|
149
|
+
Math::real NormalGravity::SurfaceGravity(real lat) const {
|
150
|
+
real sphi = Math::sind(Math::LatFix(lat));
|
151
|
+
// H+M, Eq 2-78
|
152
|
+
return _gammae * (1 + _k * Math::sq(sphi)) / sqrt(1 - _e2 * Math::sq(sphi));
|
153
|
+
}
|
154
|
+
|
155
|
+
Math::real NormalGravity::V0(real X, real Y, real Z,
|
156
|
+
real& GammaX, real& GammaY, real& GammaZ)
|
157
|
+
const {
|
158
|
+
// See H+M, Sec 6-2
|
159
|
+
real
|
160
|
+
p = Math::hypot(X, Y),
|
161
|
+
clam = p ? X/p : 1,
|
162
|
+
slam = p ? Y/p : 0,
|
163
|
+
r = Math::hypot(p, Z),
|
164
|
+
Q = Math::sq(r) - Math::sq(_E),
|
165
|
+
t2 = Math::sq(2 * _E * Z),
|
166
|
+
disc = sqrt(Math::sq(Q) + t2),
|
167
|
+
// This is H+M, Eq 6-8a, but generalized to deal with Q negative
|
168
|
+
// accurately.
|
169
|
+
u = sqrt((Q >= 0 ? (Q + disc) : t2 / (disc - Q)) / 2),
|
170
|
+
uE = Math::hypot(u, _E),
|
171
|
+
// H+M, Eq 6-8b
|
172
|
+
sbet = Z * uE,
|
173
|
+
cbet = p * u,
|
174
|
+
s = Math::hypot(cbet, sbet);
|
175
|
+
cbet = s ? cbet/s : 0;
|
176
|
+
sbet = s ? sbet/s : 1;
|
177
|
+
real
|
178
|
+
invw = uE / Math::hypot(u, _E * sbet), // H+M, Eq 2-63
|
179
|
+
ep = _E/u,
|
180
|
+
ep2 = Math::sq(ep),
|
181
|
+
q = _q0 ? qf(ep2) / _q0 : pow(_a / u, 3),
|
182
|
+
qp = qpf(ep2) / _q0,
|
183
|
+
// H+M, Eqs 2-62 + 6-9, but omitting last (rotational) term .
|
184
|
+
Vres = (_GM / (_E ? _E / atan(_E / u) : u)
|
185
|
+
+ _aomega2 * q * (Math::sq(sbet) - 1/real(3)) / 2),
|
186
|
+
// H+M, Eq 6-10
|
187
|
+
gamu = - invw * (_GM
|
188
|
+
+ (_aomega2 * (_q0 ? _E * qp : 3 * q * u)
|
189
|
+
* (Math::sq(sbet) - 1/real(3)) / 2)) / Math::sq(uE),
|
190
|
+
gamb = _aomega2 * q * sbet * cbet * invw / uE,
|
191
|
+
t = u * invw / uE;
|
192
|
+
// H+M, Eq 6-12
|
193
|
+
GammaX = t * cbet * gamu - invw * sbet * gamb;
|
194
|
+
GammaY = GammaX * slam;
|
195
|
+
GammaX *= clam;
|
196
|
+
GammaZ = invw * sbet * gamu + t * cbet * gamb;
|
197
|
+
return Vres;
|
198
|
+
}
|
199
|
+
|
200
|
+
Math::real NormalGravity::Phi(real X, real Y, real& fX, real& fY)
|
201
|
+
const {
|
202
|
+
fX = _omega2 * X;
|
203
|
+
fY = _omega2 * Y;
|
204
|
+
// N.B. fZ = 0;
|
205
|
+
return _omega2 * (Math::sq(X) + Math::sq(Y)) / 2;
|
206
|
+
}
|
207
|
+
|
208
|
+
Math::real NormalGravity::U(real X, real Y, real Z,
|
209
|
+
real& gammaX, real& gammaY, real& gammaZ)
|
210
|
+
const {
|
211
|
+
real fX, fY;
|
212
|
+
real Ures = V0(X, Y, Z, gammaX, gammaY, gammaZ) + Phi(X, Y, fX, fY);
|
213
|
+
gammaX += fX;
|
214
|
+
gammaY += fY;
|
215
|
+
return Ures;
|
216
|
+
}
|
217
|
+
|
218
|
+
Math::real NormalGravity::Gravity(real lat, real h,
|
219
|
+
real& gammay, real& gammaz)
|
220
|
+
const {
|
221
|
+
real X, Y, Z;
|
222
|
+
real M[Geocentric::dim2_];
|
223
|
+
_earth.IntForward(lat, 0, h, X, Y, Z, M);
|
224
|
+
real gammaX, gammaY, gammaZ,
|
225
|
+
Ures = U(X, Y, Z, gammaX, gammaY, gammaZ);
|
226
|
+
// gammax = M[0] * gammaX + M[3] * gammaY + M[6] * gammaZ;
|
227
|
+
gammay = M[1] * gammaX + M[4] * gammaY + M[7] * gammaZ;
|
228
|
+
gammaz = M[2] * gammaX + M[5] * gammaY + M[8] * gammaZ;
|
229
|
+
return Ures;
|
230
|
+
}
|
231
|
+
|
232
|
+
Math::real NormalGravity::J2ToFlattening(real a, real GM,
|
233
|
+
real omega, real J2) {
|
234
|
+
real
|
235
|
+
K = 2 * Math::sq(a * omega) * a / (15 * GM),
|
236
|
+
e2 = 3 * J2; // See Moritz (1980), p 398.
|
237
|
+
// Solve using Newton's method
|
238
|
+
for (int j = 0; j < maxit_ || GEOGRAPHICLIB_PANIC; ++j) {
|
239
|
+
real e2a = e2,
|
240
|
+
ep2 = e2 / (1 - e2),
|
241
|
+
q0 = qf(ep2),
|
242
|
+
dq0 = dq(ep2) / Math::sq(1 - e2),
|
243
|
+
h = e2 * (1 - sqrt(e2) * K / q0) - 3 * J2,
|
244
|
+
dh = 1 - sqrt(e2) * K * (3 * q0 - 2 * e2 * dq0) / (2 * Math::sq(q0)),
|
245
|
+
de2 = - h / dh;
|
246
|
+
e2 = e2a + de2;
|
247
|
+
if (e2 == e2a)
|
248
|
+
break;
|
249
|
+
}
|
250
|
+
return e2 / (1 + sqrt(1 - e2));
|
251
|
+
}
|
252
|
+
|
253
|
+
Math::real NormalGravity::FlatteningToJ2(real a, real GM,
|
254
|
+
real omega, real f) {
|
255
|
+
real
|
256
|
+
K = 2 * Math::sq(a * omega) * a / (15 * GM),
|
257
|
+
e2 = f * (2 - f),
|
258
|
+
q0 = qf(e2 / (1 - e2));
|
259
|
+
return e2 * (1 - K * sqrt(e2) / q0) / 3; // H+M, Eq 2-90
|
260
|
+
}
|
261
|
+
|
262
|
+
} // namespace GeographicLib
|