46 #define _USE_MATH_DEFINES
53 #include "physical_constants.h"
71 double AltEllipsoid = LLA[2]/1000.0;
76 if (Lat < -90)
return -1;
77 if (Lat > 90)
return -2;
79 if (Lon < -180)
return -3;
80 if (Lon > 180)
return -4;
90 CoordGeodetic.
lambda = Lon;
95 GeodeticToSpherical(&CoordGeodetic, &CoordSpherical);
97 if (DateToYear(Month, Day, Year) < 0)
101 if (Geomag(&CoordSpherical, &CoordGeodetic, &GeoMagneticElements) < 0)
105 Be[0] = GeoMagneticElements.
X * 1
e-2;
106 Be[1] = GeoMagneticElements.
Y * 1
e-2;
107 Be[2] = GeoMagneticElements.
Z * 1
e-2;
114 void WorldMagModel::Initialize()
121 Ellip.
fla = WGS84_FLATTENING;
122 Ellip.
eps = WGS84_EPS;
123 Ellip.
epssq = WGS84_EPS2;
124 Ellip.
re = WGS84_RADIUS_EARTH_KM;
127 MagneticModel.
nMax = WMM_MAX_MODEL_DEGREES;
128 MagneticModel.
nMaxSecVar = WMM_MAX_SECULAR_VARIATION_MODEL_DEGREES;
132 MagneticModel.
EditionDate = MAGNETIC_MODEL_EDITION_DATE;
133 MagneticModel.
epoch = MAGNETIC_MODEL_EPOCH;
134 sprintf(MagneticModel.
ModelName, MAGNETIC_MODEL_NAME);
161 ComputeSphericalHarmonicVariables(CoordSpherical, MagneticModel.
nMax, &SphVariables);
164 if (AssociatedLegendreFunction(CoordSpherical, MagneticModel.
nMax, &LegendreFunction) < 0)
168 Summation(&LegendreFunction, &SphVariables, CoordSpherical, &MagneticResultsSph);
171 SecVarSummation(&LegendreFunction, &SphVariables, CoordSpherical, &MagneticResultsSphVar);
174 RotateMagneticVector(CoordSpherical, CoordGeodetic, &MagneticResultsSph, &MagneticResultsGeo);
177 RotateMagneticVector(CoordSpherical, CoordGeodetic, &MagneticResultsSphVar, &MagneticResultsGeoVar);
180 CalculateGeoMagneticElements(&MagneticResultsGeo, GeoMagneticElements);
183 CalculateSecularVariation(&MagneticResultsGeoVar, GeoMagneticElements);
218 for (
int n = 1;
n <= nMax;
n++)
231 for (
int m = 2;
m <= nMax;
m++)
255 double sin_phi = sin(CoordSpherical->
phig *
DEG2RAD);
257 if (nMax <= 16 || (1 - fabs(sin_phi)) < 1.0
e-10)
258 PcupLow(LegendreFunction->
Pcup, LegendreFunction->
dPcup, sin_phi, nMax);
261 if (PcupHigh(LegendreFunction->
Pcup, LegendreFunction->
dPcup, sin_phi, nMax) < 0)
291 MagneticResults->
Bz = 0.0;
292 MagneticResults->
By = 0.0;
293 MagneticResults->
Bx = 0.0;
295 for (
int n = 1;
n <= MagneticModel.
nMax;
n++)
297 for (
int m = 0;
m <=
n;
m++)
299 int index = (
n * (
n + 1) / 2 +
m);
305 MagneticResults->
Bz -=
307 (get_main_field_coeff_g(index) *
309 * (
double)(
n + 1) * LegendreFunction->
Pcup[index];
315 MagneticResults->
By +=
317 (get_main_field_coeff_g(index) *
319 * (
double)(
m) * LegendreFunction->
Pcup[index];
325 MagneticResults->
Bx -=
327 (get_main_field_coeff_g(index) *
329 * LegendreFunction->
dPcup[index];
334 double cos_phi = cos(CoordSpherical->
phig *
DEG2RAD);
335 if (fabs(cos_phi) > 1.0
e-10)
337 MagneticResults->
By = MagneticResults->
By / cos_phi;
346 SummationSpecial(SphVariables, CoordSpherical, MagneticResults);
365 MagneticResults->
Bz = 0.0;
366 MagneticResults->
By = 0.0;
367 MagneticResults->
Bx = 0.0;
371 for (
int m = 0;
m <=
n;
m++)
373 int index = (
n * (
n + 1) / 2 +
m);
379 MagneticResults->
Bz -=
381 (get_secular_var_coeff_g(index) *
383 * (
double)(
n + 1) * LegendreFunction->
Pcup[index];
389 MagneticResults->
By +=
391 (get_secular_var_coeff_g(index) *
393 * (
double)(
m) * LegendreFunction->
Pcup[index];
399 MagneticResults->
Bx -=
401 (get_secular_var_coeff_g(index) *
403 * LegendreFunction->
dPcup[index];
407 double cos_phi = cos(CoordSpherical->
phig *
DEG2RAD);
408 if (fabs(cos_phi) > 1.0
e-10)
410 MagneticResults->
By = MagneticResults->
By / cos_phi;
414 SecVarSummationSpecial(SphVariables, CoordSpherical, MagneticResults);
450 double Psi = (CoordSpherical->
phig - CoordGeodetic->
phi) *
DEG2RAD;
453 MagneticResultsGeo->
Bz = MagneticResultsSph->
Bx * sin(Psi) + MagneticResultsSph->
Bz * cos(Psi);
454 MagneticResultsGeo->
Bx = MagneticResultsSph->
Bx * cos(Psi) - MagneticResultsSph->
Bz * sin(Psi);
455 MagneticResultsGeo->
By = MagneticResultsSph->
By;
475 GeoMagneticElements->
X = MagneticResultsGeo->
Bx;
476 GeoMagneticElements->
Y = MagneticResultsGeo->
By;
477 GeoMagneticElements->
Z = MagneticResultsGeo->
Bz;
479 GeoMagneticElements->
H = sqrt(MagneticResultsGeo->
Bx * MagneticResultsGeo->
Bx + MagneticResultsGeo->
By * MagneticResultsGeo->
By);
480 GeoMagneticElements->
F = sqrt(GeoMagneticElements->
H * GeoMagneticElements->
H + MagneticResultsGeo->
Bz * MagneticResultsGeo->
Bz);
481 GeoMagneticElements->
Decl = atan2(GeoMagneticElements->
Y, GeoMagneticElements->
X) *
RAD2DEG;
482 GeoMagneticElements->
Incl = atan2(GeoMagneticElements->
Z, GeoMagneticElements->
H) *
RAD2DEG;
503 MagneticElements->
Xdot = MagneticVariation->
Bx;
504 MagneticElements->
Ydot = MagneticVariation->
By;
505 MagneticElements->
Zdot = MagneticVariation->
Bz;
506 MagneticElements->
Hdot = (MagneticElements->
X * MagneticElements->
Xdot + MagneticElements->
Y * MagneticElements->
Ydot) / MagneticElements->
H;
507 MagneticElements->
Fdot =
508 (MagneticElements->
X * MagneticElements->
Xdot +
509 MagneticElements->
Y * MagneticElements->
Ydot + MagneticElements->
Z * MagneticElements->
Zdot) / MagneticElements->
F;
511 180.0 / M_PI * (MagneticElements->
X * MagneticElements->
Ydot -
512 MagneticElements->
Y * MagneticElements->
Xdot) / (MagneticElements->
H * MagneticElements->
H);
514 180.0 / M_PI * (MagneticElements->
H * MagneticElements->
Zdot -
515 MagneticElements->
Z * MagneticElements->
Hdot) / (MagneticElements->
F * MagneticElements->
F);
519 int WorldMagModel::PcupHigh(
double *Pcup,
double *dPcup,
double x,
int nMax)
557 double f1[WMM_NUMPCUP];
558 double f2[WMM_NUMPCUP];
559 double PreSqr[WMM_NUMPCUP];
568 double scalef = 1.0e-280;
570 for (
int n = 0;
n <= 2 * nMax + 1; ++
n)
571 PreSqr[
n] = sqrt((
double)(
n));
575 for (
int n = 2;
n <= nMax;
n++)
578 f1[k] = (double)(2 *
n - 1) /
n;
579 f2[k] = (double)(
n - 1) /
n;
580 for (
int m = 1; m <=
n - 2; m++)
583 f1[k] = (double)(2 *
n - 1) / PreSqr[
n +
m] / PreSqr[
n -
m];
584 f2[k] = PreSqr[
n - m - 1] * PreSqr[
n + m - 1] / PreSqr[
n +
m] / PreSqr[
n -
m];
590 double z = sqrt((1.0 - x) * (1.0 + x));
601 for (
int n = 2;
n <= nMax;
n++)
604 double plm = f1[k] * x * pm1 - f2[k] * pm2;
606 dPcup[k] = (double)(n) * (pm1 - x * plm) / z;
611 double pmm = PreSqr[2] * scalef;
612 double rescalem = 1.0 / scalef;
615 for (m = 1; m <= nMax - 1; ++
m)
617 rescalem = rescalem *
z;
620 kstart = kstart + m + 1;
621 pmm = pmm * PreSqr[2 * m + 1] / PreSqr[2 *
m];
622 Pcup[kstart] = pmm * rescalem / PreSqr[2 * m + 1];
623 dPcup[kstart] = -((double)(m) * x * Pcup[kstart] /
z);
624 pm2 = pmm / PreSqr[2 * m + 1];
627 pm1 = x * PreSqr[2 * m + 1] * pm2;
628 Pcup[k] = pm1 * rescalem;
629 dPcup[k] = ((pm2 * rescalem) * PreSqr[2 * m + 1] - x * (
double)(m + 1) * Pcup[k]) /
z;
631 for (
int n = m + 2; n <= nMax; ++
n)
634 double plm = x * f1[k] * pm1 - f2[k] * pm2;
635 Pcup[k] = plm * rescalem;
636 dPcup[k] = (PreSqr[n +
m] * PreSqr[n -
m] * (pm1 * rescalem) - (
double)(
n) * x * Pcup[k]) /
z;
643 rescalem = rescalem *
z;
644 kstart = kstart + m + 1;
645 pmm = pmm / PreSqr[2 * nMax];
646 Pcup[kstart] = pmm * rescalem;
647 dPcup[kstart] = -(double)(nMax) * x * Pcup[kstart] /
z;
654 void WorldMagModel::PcupLow(
double *Pcup,
double *dPcup,
double x,
int nMax)
679 double schmidtQuasiNorm[WMM_NUMPCUP];
685 double z = sqrt((1.0 - x) * (1.0 + x));
688 for (
int n = 1; n <= nMax; n++)
690 for (
int m = 0; m <=
n; m++)
692 int index = (n * (n + 1) / 2 + m);
695 int index1 = (n - 1) * n / 2 + m - 1;
696 Pcup[index] = z * Pcup[index1];
697 dPcup[index] = z * dPcup[index1] + x * Pcup[index1];
700 if (n == 1 && m == 0)
702 int index1 = (n - 1) * n / 2 + m;
703 Pcup[index] = x * Pcup[index1];
704 dPcup[index] = x * dPcup[index1] - z * Pcup[index1];
709 int index1 = (n - 2) * (n - 1) / 2 +
m;
710 int index2 = (n - 1) * n / 2 + m;
713 Pcup[index] = x * Pcup[index2];
714 dPcup[index] = x * dPcup[index2] - z * Pcup[index2];
718 double k = (double)(((n - 1) * (n - 1)) - (m *
m)) / (double)((2 * n - 1) * (2 * n - 3));
719 Pcup[index] = x * Pcup[index2] - k * Pcup[index1];
720 dPcup[index] = x * dPcup[index2] - z * Pcup[index2] - k * dPcup[index1];
730 schmidtQuasiNorm[0] = 1.0;
731 for (
int n = 1; n <= nMax; n++)
733 int index = (n * (n + 1) / 2);
734 int index1 = (n - 1) * n / 2;
736 schmidtQuasiNorm[index] = schmidtQuasiNorm[index1] * (double)(2 * n - 1) / (double)n;
738 for (
int m = 1; m <=
n; m++)
740 index = (n * (n + 1) / 2 + m);
741 index1 = (n * (n + 1) / 2 + m - 1);
742 schmidtQuasiNorm[index] = schmidtQuasiNorm[index1] * sqrt((
double)((n - m + 1) * (m == 1 ? 2 : 1)) / (
double)(n + m));
750 for (
int n = 1; n <= nMax; n++)
752 for (
int m = 0; m <=
n; m++)
754 int index = (n * (n + 1) / 2 + m);
755 Pcup[index] = Pcup[index] * schmidtQuasiNorm[index];
756 dPcup[index] = -dPcup[index] * schmidtQuasiNorm[index];
774 double PcupS[WMM_NUMPCUPS];
777 double schmidtQuasiNorm1 = 1.0;
779 MagneticResults->
By = 0.0;
780 double sin_phi = sin(CoordSpherical->
phig *
DEG2RAD);
782 for (
int n = 1; n <= MagneticModel.
nMax; n++)
788 int index = (n * (n + 1) / 2 + 1);
789 double schmidtQuasiNorm2 = schmidtQuasiNorm1 * (double)(2 * n - 1) / (double)n;
790 double schmidtQuasiNorm3 = schmidtQuasiNorm2 * sqrt((
double)(n * 2) / (
double)(n + 1));
791 schmidtQuasiNorm1 = schmidtQuasiNorm2;
794 PcupS[
n] = PcupS[n - 1];
798 double k = (double)(((n - 1) * (n - 1)) - 1) / (
double)((2 * n - 1) * (2 * n - 3));
799 PcupS[
n] = sin_phi * PcupS[n - 1] - k * PcupS[n - 2];
807 MagneticResults->
By +=
809 (get_main_field_coeff_g(index) *
811 * PcupS[n] * schmidtQuasiNorm3;
825 double PcupS[WMM_NUMPCUPS];
828 double schmidtQuasiNorm1 = 1.0;
830 MagneticResults->
By = 0.0;
831 double sin_phi = sin(CoordSpherical->
phig *
DEG2RAD);
833 for (
int n = 1; n <= MagneticModel.
nMaxSecVar; n++)
835 int index = (n * (n + 1) / 2 + 1);
836 double schmidtQuasiNorm2 = schmidtQuasiNorm1 * (double)(2 * n - 1) / (double)n;
837 double schmidtQuasiNorm3 = schmidtQuasiNorm2 * sqrt((
double)(n * 2) / (
double)(n + 1));
838 schmidtQuasiNorm1 = schmidtQuasiNorm2;
841 PcupS[
n] = PcupS[n - 1];
845 double k = (double)(((n - 1) * (n - 1)) - 1) / (
double)((2 * n - 1) * (2 * n - 3));
846 PcupS[
n] = sin_phi * PcupS[n - 1] - k * PcupS[n - 2];
854 MagneticResults->
By +=
856 (get_secular_var_coeff_g(index) *
858 * PcupS[n] * schmidtQuasiNorm3;
863 double WorldMagModel::get_main_field_coeff_g(
int index)
865 if (index >= WMM_NUMTERMS)
871 int b = (a * (a + 1) / 2 + a);
872 for (
int n = 1; n <= MagneticModel.
nMax; n++)
874 for (
int m = 0; m <=
n; m++)
876 int sum_index = (n * (n + 1) / 2 + m);
879 if (sum_index != index)
883 coeff += (decimal_date - MagneticModel.
epoch) * get_secular_var_coeff_g(sum_index);
890 double WorldMagModel::get_main_field_coeff_h(
int index)
892 if (index >= WMM_NUMTERMS)
898 int b = (a * (a + 1) / 2 + a);
899 for (
int n = 1; n <= MagneticModel.
nMax; n++)
901 for (
int m = 0; m <=
n; m++)
903 int sum_index = (n * (n + 1) / 2 + m);
906 if (sum_index != index)
910 coeff += (decimal_date - MagneticModel.
epoch) * get_secular_var_coeff_h(sum_index);
917 double WorldMagModel::get_secular_var_coeff_g(
int index)
919 if (index >= WMM_NUMTERMS)
925 double WorldMagModel::get_secular_var_coeff_h(
int index)
927 if (index >= WMM_NUMTERMS)
933 int WorldMagModel::DateToYear(
int month,
int day,
int year)
938 int MonthDays[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
941 if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
943 MonthDays[2] += ExtraDay;
947 if (month <= 0 || month > 12)
950 if (day <= 0 || day > MonthDays[month])
954 for (
int i = 1;
i <= month;
i++)
955 temp += MonthDays[
i - 1];
958 decimal_date = year + (temp - 1) / (365.0 + ExtraDay);
970 double CosLat = cos(CoordGeodetic->
phi *
DEG2RAD);
971 double SinLat = sin(CoordGeodetic->
phi *
DEG2RAD);
974 double rc = Ellip.
a / sqrt(1.0 - Ellip.
epssq * SinLat * SinLat);
981 CoordSpherical->
r = sqrt(xp * xp + zp * zp);
982 CoordSpherical->
phig = asin(zp / CoordSpherical->
r) *
RAD2DEG;
int GetMagVector(double LLA[3], int Month, int Day, int Year, double Be[3])
double cos_mlambda[WMM_MAX_MODEL_DEGREES+1]
double dPcup[WMM_NUMPCUP]
double HeightAboveEllipsoid
double RelativeRadiusPower[WMM_MAX_MODEL_DEGREES+1]
const double CoeffFile[91][6]
double sin_mlambda[WMM_MAX_MODEL_DEGREES+1]