GeographicLib: MagneticModel.cpp Source File

GeographicLib 2.5

Loading...
Searching...
No Matches
MagneticModel.cpp
Go to the documentation of this file.
1/**
2 * \file MagneticModel.cpp
3 * \brief Implementation for GeographicLib::MagneticModel class
4 *
5 * Copyright (c) Charles Karney (2011-2021) <karney@alum.mit.edu> and licensed
6 * under the MIT/X11 License. For more information, see
7 * https://geographiclib.sourceforge.io/
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 "wmm2025"
26#endif
27
28#if defined(_MSC_VER)
29// Squelch warnings about unsafe use of getenv
30# pragma warning (disable: 4996)
31#endif
32
33namespace GeographicLib {
34
35 using namespace std;
36
37 MagneticModel::MagneticModel(const std::string& name, const std::string& path,
38 const Geocentric& earth, int Nmax, int Mmax)
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 , _nNmodels(1)
51 , _nNconstants(0)
52 , _nmx(-1)
53 , _mmx(-1)
54 , _norm(SphericalHarmonic::SCHMIDT)
55 , _earth(earth)
56 {
57 if (_dir.empty())
58 _dir = DefaultMagneticPath();
59 bool truncate = Nmax >= 0 || Mmax >= 0;
60 if (truncate) {
61 if (Nmax >= 0 && Mmax < 0) Mmax = Nmax;
62 if (Nmax < 0) Nmax = numeric_limits<int>::max();
63 if (Mmax < 0) Mmax = numeric_limits<int>::max();
64 }
65 ReadMetadata(_name);
66 _gG.resize(_nNmodels + 1 + _nNconstants);
67 _hH.resize(_nNmodels + 1 + _nNconstants);
68 {
69 string coeff = _filename + ".cof";
70 ifstream coeffstr(coeff.c_str(), ios::binary);
71 if (!coeffstr.good())
72 throw GeographicErr("Error opening " + coeff);
73 char id[idlength_ + 1];
74 coeffstr.read(id, idlength_);
75 if (!coeffstr.good())
76 throw GeographicErr("No header in " + coeff);
77 id[idlength_] = '\0';
78 if (_id != string(id))
79 throw GeographicErr("ID mismatch: " + _id + " vs " + id);
80 for (int i = 0; i < _nNmodels + 1 + _nNconstants; ++i) {
81 int N, M;
82 if (truncate) { N = Nmax; M = Mmax; }
83 SphericalEngine::coeff::readcoeffs(coeffstr, N, M, _gG[i], _hH[i],
84 truncate);
85 if (!(M < 0 || _gG[i][0] == 0))
86 throw GeographicErr("A degree 0 term is not permitted");
87 _harm.push_back(SphericalHarmonic(_gG[i], _hH[i], N, N, M, _a, _norm));
88 _nmx = max(_nmx, _harm.back().Coefficients().nmx());
89 _mmx = max(_mmx, _harm.back().Coefficients().mmx());
90 }
91 int pos = int(coeffstr.tellg());
92 coeffstr.seekg(0, ios::end);
93 if (pos != coeffstr.tellg())
94 throw GeographicErr("Extra data in " + coeff);
95 }
96 }
97
98 void MagneticModel::ReadMetadata(const string& name) {
99 const char* spaces = " \t\n\v\f\r";
100 _filename = _dir + "/" + name + ".wmm";
101 ifstream metastr(_filename.c_str());
102 if (!metastr.good())
103 throw GeographicErr("Cannot open " + _filename);
104 string line;
105 getline(metastr, line);
106 if (!(line.size() >= 6 && line.substr(0,5) == "WMMF-"))
107 throw GeographicErr(_filename + " does not contain WMMF-n signature");
108 string::size_type n = line.find_first_of(spaces, 5);
109 if (n != string::npos)
110 n -= 5;
111 string version(line, 5, n);
112 // version 2 added treatment of NumConstants. The MagneticModel class now
113 // accepts NumConstants for version = 1 or 2. (The version logic was
114 // necessary to allow old versions of GeographicLib, which didn't
115 // understand NumConstants, to complain when it encountered a version 2
116 // file.)
117 if (!(version == "1" || version == "2"))
118 throw GeographicErr("Unknown version in " + _filename + ": " + version);
119 string key, val;
120 while (getline(metastr, line)) {
121 if (!Utility::ParseLine(line, key, val))
122 continue;
123 // Process key words
124 if (key == "Name")
125 _name = val;
126 else if (key == "Description")
127 _description = val;
128 else if (key == "ReleaseDate")
129 _date = val;
130 else if (key == "Radius")
131 _a = Utility::val<real>(val);
132 else if (key == "Type") {
133 if (!(val == "Linear" || val == "linear"))
134 throw GeographicErr("Only linear models are supported");
135 } else if (key == "Epoch")
136 _t0 = Utility::val<real>(val);
137 else if (key == "DeltaEpoch")
138 _dt0 = Utility::val<real>(val);
139 else if (key == "NumModels")
140 _nNmodels = Utility::val<int>(val);
141 else if (key == "NumConstants")
142 _nNconstants = Utility::val<int>(val);
143 else if (key == "MinTime")
144 _tmin = Utility::val<real>(val);
145 else if (key == "MaxTime")
146 _tmax = Utility::val<real>(val);
147 else if (key == "MinHeight")
148 _hmin = Utility::val<real>(val);
149 else if (key == "MaxHeight")
150 _hmax = Utility::val<real>(val);
151 else if (key == "Normalization") {
152 if (val == "FULL" || val == "Full" || val == "full")
153 _norm = SphericalHarmonic::FULL;
154 else if (val == "SCHMIDT" || val == "Schmidt" || val == "schmidt")
155 _norm = SphericalHarmonic::SCHMIDT;
156 else
157 throw GeographicErr("Unknown normalization " + val);
158 } else if (key == "ByteOrder") {
159 if (val == "Big" || val == "big")
160 throw GeographicErr("Only little-endian ordering is supported");
161 else if (!(val == "Little" || val == "little"))
162 throw GeographicErr("Unknown byte ordering " + val);
163 } else if (key == "ID")
164 _id = val;
165 // else unrecognized keywords are skipped
166 }
167 // Check values
168 if (!(isfinite(_a) && _a > 0))
169 throw GeographicErr("Reference radius must be positive");
170 if (!(_t0 > 0))
171 throw GeographicErr("Epoch time not defined");
172 if (_tmin >= _tmax)
173 throw GeographicErr("Min time exceeds max time");
174 if (_hmin >= _hmax)
175 throw GeographicErr("Min height exceeds max height");
176 if (int(_id.size()) != idlength_)
177 throw GeographicErr("Invalid ID");
178 if (_nNmodels < 1)
179 throw GeographicErr("NumModels must be positive");
180 if (!(_nNconstants == 0 || _nNconstants == 1))
181 throw GeographicErr("NumConstants must be 0 or 1");
182 if (!(_dt0 > 0)) {
183 if (_nNmodels > 1)
184 throw GeographicErr("DeltaEpoch must be positive");
185 else
186 _dt0 = 1;
187 }
188 }
189
190 void MagneticModel::FieldGeocentric(real t, real X, real Y, real Z,
191 real& BX, real& BY, real& BZ,
192 real& BXt, real& BYt, real& BZt) const {
193 t -= _t0;
194 int n = max(min(int(floor(t / _dt0)), _nNmodels - 1), 0);
195 bool interpolate = n + 1 < _nNmodels;
196 t -= n * _dt0;
197 // Components in geocentric basis
198 // initial values to suppress warning
199 real BXc = 0, BYc = 0, BZc = 0;
200 _harm[n](X, Y, Z, BX, BY, BZ);
201 _harm[n + 1](X, Y, Z, BXt, BYt, BZt);
202 if (_nNconstants)
203 _harm[_nNmodels + 1](X, Y, Z, BXc, BYc, BZc);
204 if (interpolate) {
205 // Convert to a time derivative
206 BXt = (BXt - BX) / _dt0;
207 BYt = (BYt - BY) / _dt0;
208 BZt = (BZt - BZ) / _dt0;
209 }
210 BX += t * BXt + BXc;
211 BY += t * BYt + BYc;
212 BZ += t * BZt + BZc;
213
214 BXt = BXt * - _a;
215 BYt = BYt * - _a;
216 BZt = BZt * - _a;
217
218 BX *= - _a;
219 BY *= - _a;
220 BZ *= - _a;
221 }
222
223 void MagneticModel::Field(real t, real lat, real lon, real h, bool diffp,
224 real& Bx, real& By, real& Bz,
225 real& Bxt, real& Byt, real& Bzt) const {
226 real X, Y, Z;
227 real M[Geocentric::dim2_];
228 _earth.IntForward(lat, lon, h, X, Y, Z, M);
229 // Components in geocentric basis
230 // initial values to suppress warning
231 real BX = 0, BY = 0, BZ = 0, BXt = 0, BYt = 0, BZt = 0;
232 FieldGeocentric(t, X, Y, Z, BX, BY, BZ, BXt, BYt, BZt);
233 if (diffp)
234 Geocentric::Unrotate(M, BXt, BYt, BZt, Bxt, Byt, Bzt);
235 Geocentric::Unrotate(M, BX, BY, BZ, Bx, By, Bz);
236 }
237
238 MagneticCircle MagneticModel::Circle(real t, real lat, real h) const {
239 real t1 = t - _t0;
240 int n = max(min(int(floor(t1 / _dt0)), _nNmodels - 1), 0);
241 bool interpolate = n + 1 < _nNmodels;
242 t1 -= n * _dt0;
243 real X, Y, Z, M[Geocentric::dim2_];
244 _earth.IntForward(lat, 0, h, X, Y, Z, M);
245 // Y = 0, cphi = M[7], sphi = M[8];
246
247 return (_nNconstants == 0 ?
248 MagneticCircle(_a, _earth._f, lat, h, t,
249 M[7], M[8], t1, _dt0, interpolate,
250 _harm[n].Circle(X, Z, true),
251 _harm[n + 1].Circle(X, Z, true)) :
252 MagneticCircle(_a, _earth._f, lat, h, t,
253 M[7], M[8], t1, _dt0, interpolate,
254 _harm[n].Circle(X, Z, true),
255 _harm[n + 1].Circle(X, Z, true),
256 _harm[_nNmodels + 1].Circle(X, Z, true)));
257 }
258
259 void MagneticModel::FieldComponents(real Bx, real By, real Bz,
260 real Bxt, real Byt, real Bzt,
261 real& H, real& F, real& D, real& I,
262 real& Ht, real& Ft,
263 real& Dt, real& It) {
264 H = hypot(Bx, By);
265 Ht = H != 0 ? (Bx * Bxt + By * Byt) / H : hypot(Bxt, Byt);
266 D = H != 0 ? Math::atan2d(Bx, By) : Math::atan2d(Bxt, Byt);
267 Dt = (H != 0 ? (By * Bxt - Bx * Byt) / Math::sq(H) : 0) / Math::degree();
268 F = hypot(H, Bz);
269 Ft = F != 0 ? (H * Ht + Bz * Bzt) / F : hypot(Ht, Bzt);
270 I = F != 0 ? Math::atan2d(-Bz, H) : Math::atan2d(-Bzt, Ht);
271 It = (F != 0 ? (Bz * Ht - H * Bzt) / Math::sq(F) : 0) / Math::degree();
272 }
273
274 string MagneticModel::DefaultMagneticPath() {
275 string path;
276 char* magneticpath = getenv("GEOGRAPHICLIB_MAGNETIC_PATH");
277 if (magneticpath)
278 path = string(magneticpath);
279 if (!path.empty())
280 return path;
281 char* datapath = getenv("GEOGRAPHICLIB_DATA");
282 if (datapath)
283 path = string(datapath);
284 return (!path.empty() ? path : string(GEOGRAPHICLIB_DATA)) + "/magnetic";
285 }
286
287 string MagneticModel::DefaultMagneticName() {
288 string name;
289 char* magneticname = getenv("GEOGRAPHICLIB_MAGNETIC_NAME");
290 if (magneticname)
291 name = string(magneticname);
292 return !name.empty() ? name : string(GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME);
293 }
294
295} // namespace GeographicLib
real
GeographicLib::Math::real real
Definition GeodSolve.cpp:28
GEOGRAPHICLIB_DATA
#define GEOGRAPHICLIB_DATA
Definition Geoid.cpp:19
MagneticCircle.hpp
Header for GeographicLib::MagneticCircle class.
GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME
#define GEOGRAPHICLIB_MAGNETIC_DEFAULT_NAME
Definition MagneticModel.cpp:25
MagneticModel.hpp
Header for GeographicLib::MagneticModel class.
SphericalEngine.hpp
Header for GeographicLib::SphericalEngine class.
Utility.hpp
Header for GeographicLib::Utility class.
GeographicLib::Geocentric
Geocentric coordinates
Definition Geocentric.hpp:67
GeographicLib::GeographicErr
Exception handling for GeographicLib.
Definition Constants.hpp:316
GeographicLib::MagneticCircle
Geomagnetic field on a circle of latitude.
Definition MagneticCircle.hpp:37
GeographicLib::MagneticModel::FieldGeocentric
void FieldGeocentric(real t, real X, real Y, real Z, real &BX, real &BY, real &BZ, real &BXt, real &BYt, real &BZt) const
Definition MagneticModel.cpp:190
GeographicLib::MagneticModel::Circle
MagneticCircle Circle(real t, real lat, real h) const
Definition MagneticModel.cpp:238
GeographicLib::MagneticModel::DefaultMagneticPath
static std::string DefaultMagneticPath()
Definition MagneticModel.cpp:274
GeographicLib::MagneticModel::DefaultMagneticName
static std::string DefaultMagneticName()
Definition MagneticModel.cpp:287
GeographicLib::MagneticModel::FieldComponents
static void FieldComponents(real Bx, real By, real Bz, real &H, real &F, real &D, real &I)
Definition MagneticModel.hpp:263
GeographicLib::Math
Mathematical functions needed by GeographicLib.
Definition Math.hpp:77
GeographicLib::Math::degree
static T degree()
Definition Math.hpp:209
GeographicLib::Math::atan2d
static T atan2d(T y, T x)
Definition Math.cpp:199
GeographicLib::Math::sq
static T sq(T x)
Definition Math.hpp:221
GeographicLib::SphericalEngine::coeff::readcoeffs
static void readcoeffs(std::istream &stream, int &N, int &M, std::vector< real > &C, std::vector< real > &S, bool truncate=false)
Definition SphericalEngine.cpp:384
GeographicLib::SphericalHarmonic
Spherical harmonic series.
Definition SphericalHarmonic.hpp:71
GeographicLib::SphericalHarmonic::SCHMIDT
@ SCHMIDT
Definition SphericalHarmonic.hpp:120
GeographicLib::SphericalHarmonic::FULL
@ FULL
Definition SphericalHarmonic.hpp:98
GeographicLib::Utility::ParseLine
static bool ParseLine(const std::string &line, std::string &key, std::string &value, char equals='\0', char comment='#')
Definition Utility.cpp:170
GeographicLib::Utility::val
static T val(const std::string &s)
Definition Utility.hpp:225
GeographicLib
Namespace for GeographicLib.
Definition Accumulator.cpp:12
std
Definition NearestNeighbor.hpp:806