9#include "GeomagnetismHeader.h"
86double MAG_dtstr_to_dyear(
char *edit_date) {
91 int months[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
96 if (sscanf(edit_date,
"%d/%d/%d", &month, &day, &year) != 3) {
98 "Failed to parse the date string. Please use the format mm/dd/yyyy\n");
102 if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
106 double total_year_days = 365.0 + extra_day;
107 months[2] = months[2] + extra_day;
109 for (
int i = 0; i < month; i++) {
110 total_days += months[i];
114 dyear = (double)year + (
double)(total_days - 1) / total_year_days;
172 MagneticResultsSphVar, MagneticResultsGeoVar;
175 ((TimedMagneticModel->nMax + 1) * (TimedMagneticModel->nMax + 2) / 2);
176 LegendreFunction = MAG_AllocateLegendreFunctionMemory(
178 SphVariables = MAG_AllocateSphVarMemory(TimedMagneticModel->nMax);
179 MAG_ComputeSphericalHarmonicVariables(
180 Ellip, CoordSpherical, TimedMagneticModel->nMax,
182 MAG_AssociatedLegendreFunction(CoordSpherical, TimedMagneticModel->nMax,
185 LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical,
186 &MagneticResultsSph);
188 LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical,
189 &MagneticResultsSphVar);
190 MAG_RotateMagneticVector(
191 CoordSpherical, CoordGeodetic, MagneticResultsSph,
192 &MagneticResultsGeo);
194 MAG_RotateMagneticVector(
195 CoordSpherical, CoordGeodetic, MagneticResultsSphVar,
196 &MagneticResultsGeoVar);
198 MAG_CalculateGeoMagneticElements(
200 GeoMagneticElements);
202 MAG_CalculateSecularVariationElements(
203 MagneticResultsGeoVar,
204 GeoMagneticElements);
207 MAG_FreeLegendreMemory(LegendreFunction);
208 MAG_FreeSphVarMemory(SphVariables);
225 double phiDelta = 0.01, hDelta = -1, x[2], y[2], z[2],
233 MAG_GeodeticToSpherical(Ellip, CoordGeodetic, &AdjCoordSpherical);
234 MAG_Geomag(Ellip, AdjCoordSpherical, CoordGeodetic, TimedMagneticModel,
235 &GeomagneticElements);
236 AdjCoordGeodetic = MAG_CoordGeodeticAssign(CoordGeodetic);
240 AdjCoordGeodetic.phi = CoordGeodetic.phi + phiDelta;
241 MAG_GeodeticToSpherical(Ellip, AdjCoordGeodetic, &AdjCoordSpherical);
242 MAG_Geomag(Ellip, AdjCoordSpherical, AdjCoordGeodetic, TimedMagneticModel,
243 &AdjGeoMagneticElements[0]);
244 MAG_SphericalToCartesian(AdjCoordSpherical, &x[0], &y[0], &z[0]);
245 AdjCoordGeodetic.phi = CoordGeodetic.phi - phiDelta;
246 MAG_GeodeticToSpherical(Ellip, AdjCoordGeodetic, &AdjCoordSpherical);
247 MAG_Geomag(Ellip, AdjCoordSpherical, AdjCoordGeodetic, TimedMagneticModel,
248 &AdjGeoMagneticElements[1]);
249 MAG_SphericalToCartesian(AdjCoordSpherical, &x[1], &y[1], &z[1]);
252 sqrt((x[0] - x[1]) * (x[0] - x[1]) + (y[0] - y[1]) * (y[0] - y[1]) +
253 (z[0] - z[1]) * (z[0] - z[1]));
254 Gradient->GradPhi = MAG_GeoMagneticElementsSubtract(
255 AdjGeoMagneticElements[0], AdjGeoMagneticElements[1]);
257 MAG_GeoMagneticElementsScale(Gradient->GradPhi, 1 / distance);
258 AdjCoordGeodetic = MAG_CoordGeodeticAssign(CoordGeodetic);
269 MAG_GeodeticToSpherical(Ellip, CoordGeodetic, &AdjCoordSpherical);
270 MAG_GradY(Ellip, AdjCoordSpherical, CoordGeodetic, TimedMagneticModel,
271 GeomagneticElements, &(Gradient->GradLambda));
274 AdjCoordGeodetic.HeightAboveEllipsoid =
275 CoordGeodetic.HeightAboveEllipsoid + hDelta;
276 AdjCoordGeodetic.HeightAboveGeoid = CoordGeodetic.HeightAboveGeoid + hDelta;
277 MAG_GeodeticToSpherical(Ellip, AdjCoordGeodetic, &AdjCoordSpherical);
278 MAG_Geomag(Ellip, AdjCoordSpherical, AdjCoordGeodetic, TimedMagneticModel,
279 &AdjGeoMagneticElements[0]);
280 MAG_SphericalToCartesian(AdjCoordSpherical, &x[0], &y[0], &z[0]);
281 AdjCoordGeodetic.HeightAboveEllipsoid =
282 CoordGeodetic.HeightAboveEllipsoid - hDelta;
283 AdjCoordGeodetic.HeightAboveGeoid = CoordGeodetic.HeightAboveGeoid - hDelta;
284 MAG_GeodeticToSpherical(Ellip, AdjCoordGeodetic, &AdjCoordSpherical);
285 MAG_Geomag(Ellip, AdjCoordSpherical, AdjCoordGeodetic, TimedMagneticModel,
286 &AdjGeoMagneticElements[1]);
287 MAG_SphericalToCartesian(AdjCoordSpherical, &x[1], &y[1], &z[1]);
290 sqrt((x[0] - x[1]) * (x[0] - x[1]) + (y[0] - y[1]) * (y[0] - y[1]) +
291 (z[0] - z[1]) * (z[0] - z[1]));
292 Gradient->GradZ = MAG_GeoMagneticElementsSubtract(AdjGeoMagneticElements[0],
293 AdjGeoMagneticElements[1]);
294 Gradient->GradZ = MAG_GeoMagneticElementsScale(Gradient->GradZ, 1 / distance);
295 AdjCoordGeodetic = MAG_CoordGeodeticAssign(CoordGeodetic);
311 Ellip->b = 6356.7523142;
312 Ellip->fla = 1 / 298.257223563;
313 Ellip->eps = sqrt(1 - (Ellip->b * Ellip->b) /
314 (Ellip->a * Ellip->a));
315 Ellip->epssq = (Ellip->eps * Ellip->eps);
319 Geoid->NumbGeoidCols =
321 Geoid->NumbGeoidRows =
323 Geoid->NumbHeaderItems =
325 Geoid->ScaleFactor = 4;
326 Geoid->NumbGeoidElevs = Geoid->NumbGeoidCols * Geoid->NumbGeoidRows;
327 Geoid->Geoid_Initialized =
329 Geoid->UseGeoid = MAG_USE_GEOID;
334int MAG_robustReadMagModels(
char *filename,
337 char *line = malloc(
sizeof(
char) * MAXLINELENGTH);
338 int n, nMax = 0, num_terms, a;
340 MODELFILE = fopen(filename,
"r");
341 if (MODELFILE == 0) {
344 if (NULL == fgets(line, MAXLINELENGTH, MODELFILE)) {
348 if (array_size == 1) {
350 if (NULL == fgets(line, MAXLINELENGTH, MODELFILE))
break;
351 a = sscanf(line,
"%d", &n);
352 if (n > nMax && (n < 99999 && a == 1 && n > 0)) nMax = n;
353 }
while (n < 99999 && a == 1);
354 num_terms = CALCULATE_NUMTERMS(nMax);
355 (*magneticmodels)[0] = MAG_AllocateModelMemory(num_terms);
356 (*magneticmodels)[0]->nMax = nMax;
357 (*magneticmodels)[0]->nMaxSecVar = nMax;
358 MAG_readMagneticModel(filename, (*magneticmodels)[0]);
359 (*magneticmodels)[0]->CoefficientFileEndDate =
360 (*magneticmodels)[0]->epoch + 5;
382void MAG_Error(
int control)
393 printf(
"\nError allocating in MAG_LegendreFunctionMemory.\n");
396 printf(
"\nError allocating in MAG_AllocateModelMemory.\n");
399 printf(
"\nError allocating in MAG_InitializeGeoid\n");
402 printf(
"\nError in setting default values.\n");
405 printf(
"\nError initializing Geoid.\n");
408 printf(
"\nError opening wmmhr.cof\n.");
411 printf(
"\nError opening WMMSV.COF\n.");
414 printf(
"\nError reading Magnetic Model.\n");
417 printf(
"\nError printing Command Prompt introduction.\n");
421 "\nError converting from geodetic co-ordinates to spherical "
425 printf(
"\nError in time modifying the Magnetic model\n");
428 printf(
"\nError in Geomagnetic\n");
431 printf(
"\nError printing user data\n");
434 printf(
"\nError allocating in MAG_SummationSpecial\n");
437 printf(
"\nError allocating in MAG_SecVarSummationSpecial\n");
440 printf(
"\nError in opening EGM9615.BIN file\n");
444 "\nError: Latitude OR Longitude out of range in "
445 "MAG_GetGeoidHeight\n");
448 printf(
"\nError allocating in MAG_PcupHigh\n");
451 printf(
"\nError allocating in MAG_PcupLow\n");
454 printf(
"\nError opening coefficient file\n");
457 printf(
"\nError: UnitDepth too large\n");
460 printf(
"\nYour system needs Big endian version of EGM9615.BIN. \n");
462 "Please download this file from "
463 "http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml. \n");
464 printf(
"Replace the existing EGM9615.BIN file with the downloaded one\n");
470 printf(
"\nGradient\n");
471 printf(
"\n Northward Eastward Downward\n");
472 printf(
"X: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
473 Gradient.GradPhi.X, Gradient.GradLambda.X, Gradient.GradZ.X);
474 printf(
"Y: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
475 Gradient.GradPhi.Y, Gradient.GradLambda.Y, Gradient.GradZ.Y);
476 printf(
"Z: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
477 Gradient.GradPhi.Z, Gradient.GradLambda.Z, Gradient.GradZ.Z);
478 printf(
"H: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
479 Gradient.GradPhi.H, Gradient.GradLambda.H, Gradient.GradZ.H);
480 printf(
"F: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
481 Gradient.GradPhi.F, Gradient.GradLambda.F, Gradient.GradZ.F);
482 printf(
"Declination: %7.2f min/km %8.2f min/km %8.2f min/km \n",
483 Gradient.GradPhi.Decl * 60, Gradient.GradLambda.Decl * 60,
484 Gradient.GradZ.Decl * 60);
485 printf(
"Inclination: %7.2f min/km %8.2f min/km %8.2f min/km \n",
486 Gradient.GradPhi.Incl * 60, Gradient.GradLambda.Incl * 60,
487 Gradient.GradZ.Incl * 60);
529 char DeclString[100];
530 char InclString[100];
531 MAG_DegreeToDMSstring(GeomagElements.Incl, 2, InclString);
532 if (GeomagElements.H < 6000 && GeomagElements.H > 2000)
533 MAG_Warnings(1, GeomagElements.H, MagneticModel);
534 if (GeomagElements.H < 2000) MAG_Warnings(2, GeomagElements.H, MagneticModel);
535 if (MagneticModel->SecularVariationUsed == TRUE) {
536 MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
537 printf(
"\n Results For \n\n");
538 if (SpaceInput.phi < 0)
539 printf(
"Latitude %.2fS\n", -SpaceInput.phi);
541 printf(
"Latitude %.2fN\n", SpaceInput.phi);
542 if (SpaceInput.lambda < 0)
543 printf(
"Longitude %.2fW\n", -SpaceInput.lambda);
545 printf(
"Longitude %.2fE\n", SpaceInput.lambda);
546 if (Geoid->UseGeoid == 1)
547 printf(
"Altitude: %.2f Kilometers above mean sea level\n",
548 SpaceInput.HeightAboveGeoid);
550 printf(
"Altitude: %.2f Kilometers above the WGS-84 ellipsoid\n",
551 SpaceInput.HeightAboveEllipsoid);
552 printf(
"Date: %.1f\n", TimeInput.DecimalYear);
553 printf(
"\n Main Field\t\t\tSecular Change\n");
554 printf(
"F = %-9.1f nT\t\t Fdot = %.1f\tnT/yr\n", GeomagElements.F,
555 GeomagElements.Fdot);
556 printf(
"H = %-9.1f nT\t\t Hdot = %.1f\tnT/yr\n", GeomagElements.H,
557 GeomagElements.Hdot);
558 printf(
"X = %-9.1f nT\t\t Xdot = %.1f\tnT/yr\n", GeomagElements.X,
559 GeomagElements.Xdot);
560 printf(
"Y = %-9.1f nT\t\t Ydot = %.1f\tnT/yr\n", GeomagElements.Y,
561 GeomagElements.Ydot);
562 printf(
"Z = %-9.1f nT\t\t Zdot = %.1f\tnT/yr\n", GeomagElements.Z,
563 GeomagElements.Zdot);
564 if (GeomagElements.Decl < 0)
565 printf(
"Decl =%20s (WEST)\t Ddot = %.1f\tMin/yr\n", DeclString,
566 60 * GeomagElements.Decldot);
568 printf(
"Decl =%20s (EAST)\t Ddot = %.1f\tMin/yr\n", DeclString,
569 60 * GeomagElements.Decldot);
570 if (GeomagElements.Incl < 0)
571 printf(
"Incl =%20s (UP)\t Idot = %.1f\tMin/yr\n", InclString,
572 60 * GeomagElements.Incldot);
574 printf(
"Incl =%20s (DOWN)\t Idot = %.1f\tMin/yr\n", InclString,
575 60 * GeomagElements.Incldot);
577 MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
578 printf(
"\n Results For \n\n");
579 if (SpaceInput.phi < 0)
580 printf(
"Latitude %.2fS\n", -SpaceInput.phi);
582 printf(
"Latitude %.2fN\n", SpaceInput.phi);
583 if (SpaceInput.lambda < 0)
584 printf(
"Longitude %.2fW\n", -SpaceInput.lambda);
586 printf(
"Longitude %.2fE\n", SpaceInput.lambda);
587 if (Geoid->UseGeoid == 1)
588 printf(
"Altitude: %.2f Kilometers above MSL\n",
589 SpaceInput.HeightAboveGeoid);
591 printf(
"Altitude: %.2f Kilometers above WGS-84 Ellipsoid\n",
592 SpaceInput.HeightAboveEllipsoid);
593 printf(
"Date: %.1f\n", TimeInput.DecimalYear);
594 printf(
"\n Main Field\n");
595 printf(
"F = %-9.1f nT\n", GeomagElements.F);
596 printf(
"H = %-9.1f nT\n", GeomagElements.H);
597 printf(
"X = %-9.1f nT\n", GeomagElements.X);
598 printf(
"Y = %-9.1f nT\n", GeomagElements.Y);
599 printf(
"Z = %-9.1f nT\n", GeomagElements.Z);
600 if (GeomagElements.Decl < 0)
601 printf(
"Decl =%20s (WEST)\n", DeclString);
603 printf(
"Decl =%20s (EAST)\n", DeclString);
604 if (GeomagElements.Incl < 0)
605 printf(
"Incl =%20s (UP)\n", InclString);
607 printf(
"Incl =%20s (DOWN)\n", InclString);
610 if (SpaceInput.phi <= -55 || SpaceInput.phi >= 55)
613 MAG_DegreeToDMSstring(GeomagElements.GV, 2, InclString);
614 printf(
"\n\n Grid variation =%20s\n", InclString);
619int MAG_Warnings(
int control,
double value,
637 MAG_strlcpy_equivalent(ans,
"",
sizeof(ans));
643 "\nCaution: location is approaching the blackout zone around the "
644 "magnetic pole as\n");
645 printf(
" defined by the WMM military specification \n");
648 "(https://www.ngdc.noaa.gov/geomag/WMM/data/MIL-PRF-89500B.pdf). "
650 printf(
" accuracy may be degraded in this region.\n");
651 printf(
"Press enter to continue...\n");
652 }
while (NULL == fgets(ans,
sizeof(ans), stdin));
657 "\nWarning: location is in the blackout zone around the magnetic "
658 "pole as defined\n");
659 printf(
" by the WMM military specification \n");
662 "(https://www.ngdc.noaa.gov/geomag/WMM/data/MIL-PRF-89500B.pdf). "
664 printf(
" accuracy is highly degraded in this region.\n");
665 }
while (NULL == fgets(ans,
sizeof(ans), stdin));
669 "\nWarning: The value you have entered of %.1f km for the elevation "
670 "is outside of the recommended range.\n Elevations above -10.0 km "
671 "are recommended for accurate results. \n",
675 "\nPlease press 'C' to continue, 'G' to get new data or 'X' to "
677 while (NULL == fgets(ans,
sizeof(ans), stdin)) {
678 printf(
"\nInvalid input\n");
691 printf(
"\nInvalid input %c\n", ans[0]);
699 "\nWARNING - TIME EXTENDS BEYOND INTENDED USAGE RANGE\n CONTACT NCEI "
700 "FOR PRODUCT UPDATES:\n");
701 printf(
" National Centers for Environmental Information\n");
702 printf(
" NOAA E/NE42\n");
703 printf(
" 325 Broadway\n");
704 printf(
"\n Boulder, CO 80305 USA");
705 printf(
" Attn: Manoj Nair or Arnaud Chulliat\n");
706 printf(
" Phone: (303) 497-4642 or -6522\n");
707 printf(
" Email: geomag.models@noaa.gov\n");
708 printf(
" Web: http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml\n");
709 printf(
"\n VALID RANGE = %d - %d\n", (
int)MagneticModel->min_year,
710 (
int)MagneticModel->CoefficientFileEndDate);
711 printf(
" TIME = %f\n", value);
714 "\nPlease press 'C' to continue, 'N' to enter new data or 'X' to "
716 while (NULL == fgets(ans,
sizeof(ans), stdin)) {
717 printf(
"\nInvalid input\n");
730 printf(
"\nInvalid input %c\n", ans[0]);
737 "\nError: The value you have entered of %f km for the elevation is "
738 "outside of the recommended range.\n Elevations above -10.0 km are "
739 "recommended for accurate results. \n",
743 "\nPlease press 'C' to continue, 'G' to get new data or 'X' to "
745 while (NULL == fgets(ans,
sizeof(ans), stdin)) {
746 printf(
"\nInvalid input\n");
759 printf(
"\nInvalid input %c\n", ans[0]);
801 if (!LegendreFunction) {
805 LegendreFunction->Pcup = (
double *)malloc((NumTerms + 1) *
sizeof(double));
806 if (LegendreFunction->Pcup == 0) {
810 LegendreFunction->dPcup = (
double *)malloc((NumTerms + 1) *
sizeof(double));
811 if (LegendreFunction->dPcup == 0) {
815 return LegendreFunction;
848 if (MagneticModel == NULL) {
853 MagneticModel->Main_Field_Coeff_G =
854 (
double *)malloc((NumTerms + 1) *
sizeof(double));
856 if (MagneticModel->Main_Field_Coeff_G == NULL) {
861 MagneticModel->Main_Field_Coeff_H =
862 (
double *)malloc((NumTerms + 1) *
sizeof(double));
864 if (MagneticModel->Main_Field_Coeff_H == NULL) {
868 MagneticModel->Secular_Var_Coeff_G =
869 (
double *)malloc((NumTerms + 1) *
sizeof(double));
870 if (MagneticModel->Secular_Var_Coeff_G == NULL) {
874 MagneticModel->Secular_Var_Coeff_H =
875 (
double *)malloc((NumTerms + 1) *
sizeof(double));
876 if (MagneticModel->Secular_Var_Coeff_H == NULL) {
880 MagneticModel->CoefficientFileEndDate = 0;
881 MagneticModel->EditionDate = 0;
882 MAG_strlcpy_equivalent(MagneticModel->ModelName,
"",
883 sizeof(MagneticModel->ModelName));
884 MagneticModel->SecularVariationUsed = 0;
885 MagneticModel->epoch = 0;
886 MagneticModel->nMax = 0;
887 MagneticModel->nMaxSecVar = 0;
889 for (i = 0; i < NumTerms; i++) {
890 MagneticModel->Main_Field_Coeff_G[i] = 0;
891 MagneticModel->Main_Field_Coeff_H[i] = 0;
892 MagneticModel->Secular_Var_Coeff_G[i] = 0;
893 MagneticModel->Secular_Var_Coeff_H[i] = 0;
896 return MagneticModel;
904 SphVariables->RelativeRadiusPower =
905 (
double *)malloc((nMax + 1) *
sizeof(double));
906 SphVariables->cos_mlambda = (
double *)malloc((nMax + 1) *
sizeof(double));
907 SphVariables->sin_mlambda = (
double *)malloc((nMax + 1) *
sizeof(double));
912 char values[][MAXLINELENGTH]) {
914 MAG_strlcpy_equivalent(model->ModelName, values[MODELNAME],
915 sizeof(model->ModelName));
923 model->epoch = atof(values[MODELSTARTYEAR]);
924 model->nMax = atoi(values[INTSTATICDEG]);
925 model->nMaxSecVar = atoi(values[INTSECVARDEG]);
926 model->CoefficientFileEndDate = atof(values[MODELENDYEAR]);
927 if (model->nMaxSecVar > 0)
928 model->SecularVariationUsed = 1;
930 model->SecularVariationUsed = 0;
940 assert(nMax <= Source->nMax);
941 assert(nMax <= Assignee->nMax);
942 assert(nMaxSecVar <= Source->nMaxSecVar);
943 assert(nMaxSecVar <= Assignee->nMaxSecVar);
944 for (n = 1; n <= nMaxSecVar; n++) {
945 for (m = 0; m <= n; m++) {
946 index = (n * (n + 1) / 2 + m);
947 Assignee->Main_Field_Coeff_G[index] = Source->Main_Field_Coeff_G[index];
948 Assignee->Main_Field_Coeff_H[index] = Source->Main_Field_Coeff_H[index];
949 Assignee->Secular_Var_Coeff_G[index] = Source->Secular_Var_Coeff_G[index];
950 Assignee->Secular_Var_Coeff_H[index] = Source->Secular_Var_Coeff_H[index];
953 for (n = nMaxSecVar + 1; n <= nMax; n++) {
954 for (m = 0; m <= n; m++) {
955 index = (n * (n + 1) / 2 + m);
956 Assignee->Main_Field_Coeff_G[index] = Source->Main_Field_Coeff_G[index];
957 Assignee->Main_Field_Coeff_H[index] = Source->Main_Field_Coeff_H[index];
992 if (MagneticModel->Main_Field_Coeff_G) {
993 free(MagneticModel->Main_Field_Coeff_G);
994 MagneticModel->Main_Field_Coeff_G = NULL;
996 if (MagneticModel->Main_Field_Coeff_H) {
997 free(MagneticModel->Main_Field_Coeff_H);
998 MagneticModel->Main_Field_Coeff_H = NULL;
1000 if (MagneticModel->Secular_Var_Coeff_G) {
1001 free(MagneticModel->Secular_Var_Coeff_G);
1002 MagneticModel->Secular_Var_Coeff_G = NULL;
1004 if (MagneticModel->Secular_Var_Coeff_H) {
1005 free(MagneticModel->Secular_Var_Coeff_H);
1006 MagneticModel->Secular_Var_Coeff_H = NULL;
1008 if (MagneticModel) {
1009 free(MagneticModel);
1010 MagneticModel = NULL;
1013 if (TimedMagneticModel->Main_Field_Coeff_G) {
1014 free(TimedMagneticModel->Main_Field_Coeff_G);
1015 TimedMagneticModel->Main_Field_Coeff_G = NULL;
1017 if (TimedMagneticModel->Main_Field_Coeff_H) {
1018 free(TimedMagneticModel->Main_Field_Coeff_H);
1019 TimedMagneticModel->Main_Field_Coeff_H = NULL;
1021 if (TimedMagneticModel->Secular_Var_Coeff_G) {
1022 free(TimedMagneticModel->Secular_Var_Coeff_G);
1023 TimedMagneticModel->Secular_Var_Coeff_G = NULL;
1025 if (TimedMagneticModel->Secular_Var_Coeff_H) {
1026 free(TimedMagneticModel->Secular_Var_Coeff_H);
1027 TimedMagneticModel->Secular_Var_Coeff_H = NULL;
1030 if (TimedMagneticModel) {
1031 free(TimedMagneticModel);
1032 TimedMagneticModel = NULL;
1035 if (LegendreFunction->Pcup) {
1036 free(LegendreFunction->Pcup);
1037 LegendreFunction->Pcup = NULL;
1039 if (LegendreFunction->dPcup) {
1040 free(LegendreFunction->dPcup);
1041 LegendreFunction->dPcup = NULL;
1043 if (LegendreFunction) {
1044 free(LegendreFunction);
1045 LegendreFunction = NULL;
1072 if (MagneticModel->Main_Field_Coeff_G) {
1073 free(MagneticModel->Main_Field_Coeff_G);
1074 MagneticModel->Main_Field_Coeff_G = NULL;
1076 if (MagneticModel->Main_Field_Coeff_H) {
1077 free(MagneticModel->Main_Field_Coeff_H);
1078 MagneticModel->Main_Field_Coeff_H = NULL;
1080 if (MagneticModel->Secular_Var_Coeff_G) {
1081 free(MagneticModel->Secular_Var_Coeff_G);
1082 MagneticModel->Secular_Var_Coeff_G = NULL;
1084 if (MagneticModel->Secular_Var_Coeff_H) {
1085 free(MagneticModel->Secular_Var_Coeff_H);
1086 MagneticModel->Secular_Var_Coeff_H = NULL;
1088 if (MagneticModel) {
1089 free(MagneticModel);
1090 MagneticModel = NULL;
1109 if (LegendreFunction->Pcup) {
1110 free(LegendreFunction->Pcup);
1111 LegendreFunction->Pcup = NULL;
1113 if (LegendreFunction->dPcup) {
1114 free(LegendreFunction->dPcup);
1115 LegendreFunction->dPcup = NULL;
1117 if (LegendreFunction) {
1118 free(LegendreFunction);
1119 LegendreFunction = NULL;
1136 if (SphVar->RelativeRadiusPower) {
1137 free(SphVar->RelativeRadiusPower);
1138 SphVar->RelativeRadiusPower = NULL;
1140 if (SphVar->cos_mlambda) {
1141 free(SphVar->cos_mlambda);
1142 SphVar->cos_mlambda = NULL;
1144 if (SphVar->sin_mlambda) {
1145 free(SphVar->sin_mlambda);
1146 SphVar->sin_mlambda = NULL;
1160 char Datestring[11];
1162 Date.DecimalYear = MagneticModel->EditionDate;
1163 MAG_YearToDate(&Date);
1164 sprintf(Datestring,
"%d/%d/%d", Date.Month, Date.Day, Date.Year);
1165 OUT = fopen(filename,
"w");
1166 fprintf(OUT,
" %.1f %s %s\n",
1167 MagneticModel->epoch, MagneticModel->ModelName, Datestring);
1168 for (n = 1; n <= MagneticModel->nMax; n++) {
1169 for (m = 0; m <= n; m++) {
1170 index = (n * (n + 1) / 2 + m);
1172 fprintf(OUT,
" %2d %2d %9.4f %9.4f %9.4f %9.4f\n", n, m,
1173 MagneticModel->Main_Field_Coeff_G[index],
1174 MagneticModel->Main_Field_Coeff_H[index],
1175 MagneticModel->Secular_Var_Coeff_G[index],
1176 MagneticModel->Secular_Var_Coeff_H[index]);
1178 fprintf(OUT,
" %2d %2d %9.4f %9.4f %9.4f %9.4f\n", n, m,
1179 MagneticModel->Main_Field_Coeff_G[index], 0.0,
1180 MagneticModel->Secular_Var_Coeff_G[index], 0.0);
1186void MAG_PrintEMMFormat(
char *filename,
char *filenameSV,
1191 char Datestring[11];
1193 Date.DecimalYear = MagneticModel->EditionDate;
1194 MAG_YearToDate(&Date);
1195 sprintf(Datestring,
"%d/%d/%d", Date.Month, Date.Day, Date.Year);
1196 OUT = fopen(filename,
"w");
1197 fprintf(OUT,
" %.1f %s %s\n",
1198 MagneticModel->epoch, MagneticModel->ModelName, Datestring);
1199 for (n = 1; n <= MagneticModel->nMax; n++) {
1200 for (m = 0; m <= n; m++) {
1201 index = (n * (n + 1) / 2 + m);
1203 fprintf(OUT,
" %2d %2d %9.4f %9.4f\n", n, m,
1204 MagneticModel->Main_Field_Coeff_G[index],
1205 MagneticModel->Main_Field_Coeff_H[index]);
1207 fprintf(OUT,
" %2d %2d %9.4f %9.4f\n", n, m,
1208 MagneticModel->Main_Field_Coeff_G[index], 0.0);
1212 OUT = fopen(filenameSV,
"w");
1213 for (n = 1; n <= MagneticModel->nMaxSecVar; n++) {
1214 for (m = 0; m <= n; m++) {
1215 index = (n * (n + 1) / 2 + m);
1217 fprintf(OUT,
" %2d %2d %9.4f %9.4f\n", n, m,
1218 MagneticModel->Secular_Var_Coeff_G[index],
1219 MagneticModel->Secular_Var_Coeff_H[index]);
1221 fprintf(OUT,
" %2d %2d %9.4f %9.4f\n", n, m,
1222 MagneticModel->Secular_Var_Coeff_G[index], 0.0);
1229void MAG_PrintSHDFFormat(
char *filename,
1232 int i, n, m, index, epochRange;
1234 SHDF_file = fopen(filename,
"w");
1236 for (i = 0; i < epochs; i++) {
1238 epochRange = (*MagneticModel)[i + 1]->epoch - (*MagneticModel)[i]->epoch;
1240 epochRange = (*MagneticModel)[i]->epoch - (*MagneticModel)[i - 1]->epoch;
1242 "%%SHDF 16695 Definitive Geomagnetic Reference Field Model "
1243 "Coefficient File\n");
1244 fprintf(SHDF_file,
"%%ModelName: %s\n", (*MagneticModel)[i]->ModelName);
1246 "%%Publisher: International Association of Geomagnetism and "
1247 "Aeronomy (IAGA), Working Group V-Mod\n");
1248 fprintf(SHDF_file,
"%%ReleaseDate: Some Number\n");
1249 fprintf(SHDF_file,
"%%DataCutOFF: Some Other Number\n");
1250 fprintf(SHDF_file,
"%%ModelStartYear: %d\n",
1251 (
int)(*MagneticModel)[i]->epoch);
1252 fprintf(SHDF_file,
"%%ModelEndYear: %d\n",
1253 (
int)(*MagneticModel)[i]->epoch + epochRange);
1254 fprintf(SHDF_file,
"%%Epoch: %.0f\n", (*MagneticModel)[i]->epoch);
1255 fprintf(SHDF_file,
"%%IntStaticDeg: %d\n", (*MagneticModel)[i]->nMax);
1256 fprintf(SHDF_file,
"%%IntSecVarDeg: %d\n", (*MagneticModel)[i]->nMaxSecVar);
1257 fprintf(SHDF_file,
"%%ExtStaticDeg: 0\n");
1258 fprintf(SHDF_file,
"%%ExtSecVarDeg: 0\n");
1259 fprintf(SHDF_file,
"%%Normalization: Schmidt semi-normailized\n");
1260 fprintf(SHDF_file,
"%%SpatBasFunc: spherical harmonics\n");
1261 fprintf(SHDF_file,
"# To synthesize the field for a given date:\n");
1263 "# Use the sub-model of the epoch corresponding to each date\n");
1265 "#\n#\n#\n#\n# I/E, n, m, Gnm, Hnm, SV-Gnm, SV-Hnm\n#\n");
1268 for (n = 1; n <= (*MagneticModel)[i]->nMax; n++) {
1269 for (m = 0; m <= n; m++) {
1270 index = (n * (n + 1)) / 2 + m;
1271 if (i < epochs - 1) {
1273 fprintf(SHDF_file,
"I,%d,%d,%f,%f,%f,%f\n", n, m,
1274 (*MagneticModel)[i]->Main_Field_Coeff_G[index],
1275 (*MagneticModel)[i]->Main_Field_Coeff_H[index],
1276 (*MagneticModel)[i]->Secular_Var_Coeff_G[index],
1277 (*MagneticModel)[i]->Secular_Var_Coeff_H[index]);
1279 fprintf(SHDF_file,
"I,%d,%d,%f,,%f,\n", n, m,
1280 (*MagneticModel)[i]->Main_Field_Coeff_G[index],
1281 (*MagneticModel)[i]->Secular_Var_Coeff_G[index]);
1284 fprintf(SHDF_file,
"I,%d,%d,%f,%f,%f,%f\n", n, m,
1285 (*MagneticModel)[i]->Main_Field_Coeff_G[index],
1286 (*MagneticModel)[i]->Main_Field_Coeff_H[index],
1287 (*MagneticModel)[i]->Secular_Var_Coeff_G[index],
1288 (*MagneticModel)[i]->Secular_Var_Coeff_H[index]);
1290 fprintf(SHDF_file,
"I,%d,%d,%f,,%f,\n", n, m,
1291 (*MagneticModel)[i]->Main_Field_Coeff_G[index],
1292 (*MagneticModel)[i]->Secular_Var_Coeff_G[index]);
1299int MAG_readMagneticModel(
char *filename,
1319 int i, icomp, m, n, EOF_Flag = 0, index;
1320 double epoch, gnm, hnm, dgnm, dhnm;
1321 MAG_COF_File = fopen(filename,
"r");
1323 char *edit_date = (
char *)malloc(
sizeof(
char) * date_size);
1324 if (MAG_COF_File == NULL) {
1329 MagneticModel->Main_Field_Coeff_H[0] = 0.0;
1330 MagneticModel->Main_Field_Coeff_G[0] = 0.0;
1331 MagneticModel->Secular_Var_Coeff_H[0] = 0.0;
1332 MagneticModel->Secular_Var_Coeff_G[0] = 0.0;
1334 char header_fmt[
sizeof(c_str)];
1335 snprintf(header_fmt,
sizeof(header_fmt),
"%%lf %%%lus %%%ds",
1336 (
unsigned long)
sizeof(MagneticModel->ModelName), date_size);
1338 fgets(c_str,
sizeof(c_str), MAG_COF_File);
1339 sscanf(c_str, header_fmt, &epoch, MagneticModel->ModelName, edit_date);
1341 MagneticModel->min_year = MAG_dtstr_to_dyear(edit_date);
1342 if (MagneticModel->min_year == -1) {
1343 MagneticModel->min_year = epoch;
1346 MagneticModel->epoch = epoch;
1347 while (EOF_Flag == 0) {
1348 if (NULL == fgets(c_str,
sizeof(c_str), MAG_COF_File)) {
1352 for (i = 0; i < 4 && (c_str[i] !=
'\0'); i++) {
1353 c_new[i] = c_str[i];
1354 c_new[i + 1] =
'\0';
1356 icomp = strcmp(
"9999", c_new);
1362 sscanf(c_str,
"%d%d%lf%lf%lf%lf", &n, &m, &gnm, &hnm, &dgnm, &dhnm);
1364 index = (n * (n + 1) / 2 + m);
1365 MagneticModel->Main_Field_Coeff_G[index] = gnm;
1366 MagneticModel->Secular_Var_Coeff_G[index] = dgnm;
1367 MagneticModel->Main_Field_Coeff_H[index] = hnm;
1368 MagneticModel->Secular_Var_Coeff_H[index] = dhnm;
1373 fclose(MAG_COF_File);
1377int MAG_readMagneticModel_SHDF(
char *filename,
1400 char paramkeys[NOOFPARAMS][MAXLINELENGTH] = {
1401 "SHDF ",
"ModelName: ",
"Publisher: ",
"ReleaseDate: ",
1402 "DataCutOff: ",
"ModelStartYear: ",
"ModelEndYear: ",
"Epoch: ",
1403 "IntStaticDeg: ",
"IntSecVarDeg: ",
"ExtStaticDeg: ",
"ExtSecVarDeg: ",
1404 "GeoMagRefRad: ",
"Normalization: ",
"SpatBasFunc: "};
1406 char paramvalues[NOOFPARAMS][MAXLINELENGTH];
1407 char *line = (
char *)malloc(MAXLINELENGTH);
1409 char paramvalue[MAXLINELENGTH];
1410 int paramvaluelength = 0;
1411 int paramkeylength = 0;
1414 int header_index = -1;
1417 int allocationflag = 0;
1422 double gnm, hnm, dgnm, dhnm, cutoff;
1427 stream = fopen(filename, READONLYMODE);
1428 if (stream == NULL) {
1429 perror(
"File open error");
1430 return header_index;
1434 while (fgets(line, MAXLINELENGTH, stream) != NULL) {
1436 if (strlen(MAG_Trim(line)) == 0)
continue;
1440 if (header_index > -1) {
1441 MAG_AssignHeaderValues((*magneticmodels)[header_index], paramvalues);
1444 if (header_index >= array_size) {
1447 "Header limit exceeded - too many models in model file. (%d)\n",
1449 return array_size + 1;
1454 for (i = 0; i < NOOFPARAMS; i++) {
1455 paramkeylength = strlen(paramkeys[i]);
1456 if (!strncmp(line, paramkeys[i], paramkeylength)) {
1457 paramvaluelength = strlen(line) - paramkeylength;
1458 memset(paramvalues,
'\0', paramvaluelength);
1459 MAG_strlcpy_equivalent(paramvalue, line + paramkeylength,
1461 paramvalue[paramvaluelength] =
'\0';
1462 MAG_strlcpy_equivalent(paramvalues[i], paramvalue, 1);
1463 if (!strcmp(paramkeys[i], paramkeys[INTSTATICDEG]) ||
1464 !strcmp(paramkeys[i], paramkeys[EXTSTATICDEG])) {
1465 tempint = atoi(paramvalues[i]);
1466 if (tempint > 0 && allocationflag == 0) {
1467 numterms = CALCULATE_NUMTERMS(tempint);
1468 (*magneticmodels)[header_index] =
1469 MAG_AllocateModelMemory(numterms);
1478 }
else if (*line ==
'#') {
1481 }
else if (sscanf(line,
"%c,%d,%d", &coefftype, &n, &m) == 3) {
1483 sscanf(line,
"%c,%d,%d,%lf,,%lf,", &coefftype, &n, &m, &gnm, &dgnm);
1487 sscanf(line,
"%c,%d,%d,%lf,%lf,%lf,%lf", &coefftype, &n, &m, &gnm, &hnm,
1490 if (!allocationflag) {
1492 "Degree not found in model. Memory cannot be allocated.\n");
1493 return _DEGREE_NOT_FOUND;
1496 index = (n * (n + 1) / 2 + m);
1497 (*magneticmodels)[header_index]->Main_Field_Coeff_G[index] = gnm;
1498 (*magneticmodels)[header_index]->Secular_Var_Coeff_G[index] = dgnm;
1499 (*magneticmodels)[header_index]->Main_Field_Coeff_H[index] = hnm;
1500 (*magneticmodels)[header_index]->Secular_Var_Coeff_H[index] = dhnm;
1504 if (header_index > -1)
1505 MAG_AssignHeaderValues((*magneticmodels)[header_index], paramvalues);
1508 cutoff = (*magneticmodels)[array_size - 1]->CoefficientFileEndDate;
1510 for (i = 0; i < array_size; i++)
1511 (*magneticmodels)[i]->CoefficientFileEndDate = cutoff;
1516 return header_index + 1;
1519char *MAG_Trim(
char *str) {
1522 while (isspace(*str)) str++;
1524 if (*str == 0)
return str;
1526 end = str + strlen(str) - 1;
1527 while (end > str && isspace(*end)) end--;
1543void MAG_BaseErrors(
double DeclCoef,
double DeclBaseline,
double InclOffset,
1544 double FOffset,
double Multiplier,
double H,
1545 double *DeclErr,
double *InclErr,
double *FErr) {
1546 double declHorizontalAdjustmentSq;
1547 declHorizontalAdjustmentSq = (DeclCoef / H) * (DeclCoef / H);
1548 *DeclErr = sqrt(declHorizontalAdjustmentSq + DeclBaseline * DeclBaseline) *
1550 *InclErr = InclOffset * Multiplier;
1551 *FErr = FOffset * Multiplier;
1554int MAG_CalculateGeoMagneticElements(
1570 GeoMagneticElements->X = MagneticResultsGeo->Bx;
1571 GeoMagneticElements->Y = MagneticResultsGeo->By;
1572 GeoMagneticElements->Z = MagneticResultsGeo->Bz;
1574 GeoMagneticElements->H =
1575 sqrt(MagneticResultsGeo->Bx * MagneticResultsGeo->Bx +
1576 MagneticResultsGeo->By * MagneticResultsGeo->By);
1577 GeoMagneticElements->F =
1578 sqrt(GeoMagneticElements->H * GeoMagneticElements->H +
1579 MagneticResultsGeo->Bz * MagneticResultsGeo->Bz);
1580 GeoMagneticElements->Decl =
1581 RAD2DEG(atan2(GeoMagneticElements->Y, GeoMagneticElements->X));
1582 GeoMagneticElements->Incl =
1583 RAD2DEG(atan2(GeoMagneticElements->Z, GeoMagneticElements->H));
1611 if (location.phi >= MAG_PS_MAX_LAT_DEGREE) {
1612 elements->GV = elements->Decl - location.lambda;
1614 }
else if (location.phi <= MAG_PS_MIN_LAT_DEGREE) {
1615 elements->GV = elements->Decl + location.lambda;
1618 MAG_GetTransverseMercator(location, &UTMParameters);
1619 elements->GV = elements->Decl - UTMParameters.ConvergenceOfMeridians;
1627 GradElements->X = GradResults.Bx;
1628 GradElements->Y = GradResults.By;
1629 GradElements->Z = GradResults.Bz;
1631 GradElements->H = (GradElements->X * MagneticElements.X +
1632 GradElements->Y * MagneticElements.Y) /
1634 GradElements->F = (GradElements->X * MagneticElements.X +
1635 GradElements->Y * MagneticElements.Y +
1636 GradElements->Z * MagneticElements.Z) /
1638 GradElements->Decl = 180.0 / M_PI *
1639 (MagneticElements.X * GradElements->Y -
1640 MagneticElements.Y * GradElements->X) /
1641 (MagneticElements.H * MagneticElements.H);
1642 GradElements->Incl = 180.0 / M_PI *
1643 (MagneticElements.H * GradElements->Z -
1644 MagneticElements.Z * GradElements->H) /
1645 (MagneticElements.F * MagneticElements.F);
1646 GradElements->GV = GradElements->Decl;
1649int MAG_CalculateSecularVariationElements(
1667 MagneticElements->Xdot = MagneticVariation.Bx;
1668 MagneticElements->Ydot = MagneticVariation.By;
1669 MagneticElements->Zdot = MagneticVariation.Bz;
1670 MagneticElements->Hdot =
1671 (MagneticElements->X * MagneticElements->Xdot +
1672 MagneticElements->Y * MagneticElements->Ydot) /
1673 MagneticElements->H;
1674 MagneticElements->Fdot = (MagneticElements->X * MagneticElements->Xdot +
1675 MagneticElements->Y * MagneticElements->Ydot +
1676 MagneticElements->Z * MagneticElements->Zdot) /
1677 MagneticElements->F;
1678 MagneticElements->Decldot = 180.0 / M_PI *
1679 (MagneticElements->X * MagneticElements->Ydot -
1680 MagneticElements->Y * MagneticElements->Xdot) /
1681 (MagneticElements->H * MagneticElements->H);
1682 MagneticElements->Incldot = 180.0 / M_PI *
1683 (MagneticElements->H * MagneticElements->Zdot -
1684 MagneticElements->Z * MagneticElements->Hdot) /
1685 (MagneticElements->F * MagneticElements->F);
1686 MagneticElements->GVdot = MagneticElements->Decldot;
1701 double modified_b, r, e, f, p, q, d, v, g, t, zlong, rlat;
1709 modified_b = -Ellip.b;
1711 modified_b = Ellip.b;
1716 r = sqrt(x * x + y * y);
1717 e = (modified_b * z - (Ellip.a * Ellip.a - modified_b * modified_b)) /
1719 f = (modified_b * z + (Ellip.a * Ellip.a - modified_b * modified_b)) /
1725 p = (4.0 / 3.0) * (e * f + 1.0);
1726 q = 2.0 * (e * e - f * f);
1727 d = p * p * p + q * q;
1730 v = pow((sqrt(d) - q), (1.0 / 3.0)) - pow((sqrt(d) + q), (1.0 / 3.0));
1732 v = 2.0 * sqrt(-p) * cos(acos(q / (p * sqrt(-p))) / 3.0);
1738 if (v * v < fabs(p)) {
1739 v = -(v * v * v + 2.0 * q) / (3.0 * p);
1741 g = (sqrt(e * e + v) + e) / 2.0;
1742 t = sqrt(g * g + (f - v * g) / (2.0 * g - e)) - g;
1744 rlat = atan((Ellip.a * (1.0 - t * t)) / (2.0 * modified_b * t));
1745 CoordGeodetic->phi = RAD2DEG(rlat);
1750 CoordGeodetic->HeightAboveEllipsoid =
1751 (r - Ellip.a * t) * cos(rlat) + (z - modified_b) * sin(rlat);
1755 zlong = atan2(y, x);
1756 if (zlong < 0.0) zlong = zlong + 2 * M_PI;
1758 CoordGeodetic->lambda = RAD2DEG(zlong);
1759 while (CoordGeodetic->lambda > 180) {
1760 CoordGeodetic->lambda -= 360;
1767 Assignee.phi = CoordGeodetic.phi;
1768 Assignee.lambda = CoordGeodetic.lambda;
1769 Assignee.HeightAboveEllipsoid = CoordGeodetic.HeightAboveEllipsoid;
1770 Assignee.HeightAboveGeoid = CoordGeodetic.HeightAboveGeoid;
1771 Assignee.UseGeoid = CoordGeodetic.UseGeoid;
1775int MAG_DateToYear(
MAGtype_Date *CalendarDate,
char *Error)
1794 int Error_size = 255;
1795 if (CalendarDate->Month == 0) {
1796 CalendarDate->DecimalYear = CalendarDate->Year;
1799 if ((CalendarDate->Year % 4 == 0 && CalendarDate->Year % 100 != 0) ||
1800 CalendarDate->Year % 400 == 0)
1804 MonthDays[2] = 28 + ExtraDay;
1817 if (CalendarDate->Month <= 0 || CalendarDate->Month > 12) {
1821 MAG_strlcpy_equivalent(
1823 "\nError: The Month entered is invalid, valid months are '1 to 12'\n",
1827 if (CalendarDate->Day <= 0 ||
1828 CalendarDate->Day > MonthDays[CalendarDate->Month]) {
1829 printf(
"\nThe number of days in month %d is %d\n", CalendarDate->Month,
1830 MonthDays[CalendarDate->Month]);
1831 MAG_strlcpy_equivalent(Error,
"\nError: The day entered is invalid\n",
1836 for (i = 1; i <= CalendarDate->Month; i++) temp += MonthDays[i - 1];
1837 temp += CalendarDate->Day;
1838 CalendarDate->DecimalYear =
1839 CalendarDate->Year + (temp - 1) / (365.0 + ExtraDay);
1844void MAG_DegreeToDMSstring(
double DegreesOfArc,
int UnitDepth,
char *DMSstring)
1857 double temp = DegreesOfArc;
1860 char *tempstring = malloc(
sizeof(
char) * tmp_size);
1861 char *tempstring2 = malloc(
sizeof(
char) * tmp2_size);
1862 int DMSstring_size = 100;
1864 memset(tempstring,
'\0', tmp_size);
1865 memset(tempstring2,
'\0', tmp2_size);
1871 if (UnitDepth > 3) MAG_Error(21);
1872 for (i = 0; i < UnitDepth; i++) {
1876 MAG_strlcpy_equivalent(tempstring2,
"Deg", tmp2_size);
1879 MAG_strlcpy_equivalent(tempstring2,
"Min", tmp2_size);
1882 MAG_strlcpy_equivalent(tempstring2,
"Sec", tmp2_size);
1885 temp = (temp - DMS[i]) * 60;
1886 if (i == UnitDepth - 1 && temp >= 30)
1888 else if (i == UnitDepth - 1 && temp <= -30)
1890 snprintf(tempstring, tmp_size,
"%4d%4s", DMS[i], tempstring2);
1891 strcat(DMSstring, tempstring);
1899void MAG_DMSstringToDegree(
char *DMSstring,
double *DegreesOfArc)
1907 int second, minute, degree, sign = 1, j = 0;
1908 j = sscanf(DMSstring,
"%d, %d, %d", °ree, &minute, &second);
1909 if (j != 3) sscanf(DMSstring,
"%d %d %d", °ree, &minute, &second);
1910 if (degree < 0) sign = -1;
1911 degree = degree * sign;
1912 *DegreesOfArc = sign * (degree + minute / 60.0 + second / 3600.0);
1918 double cos2D, cos2I, sin2D, sin2I, EDSq, EISq, eD, eI;
1919 cos2D = cos(DEG2RAD(B.Decl)) * cos(DEG2RAD(B.Decl));
1920 cos2I = cos(DEG2RAD(B.Incl)) * cos(DEG2RAD(B.Incl));
1921 sin2D = sin(DEG2RAD(B.Decl)) * sin(DEG2RAD(B.Decl));
1922 sin2I = sin(DEG2RAD(B.Incl)) * sin(DEG2RAD(B.Incl));
1923 eD = DEG2RAD(Errors->Decl);
1924 eI = DEG2RAD(Errors->Incl);
1928 sqrt(cos2D * cos2I * Errors->F * Errors->F +
1929 B.F * B.F * sin2D * cos2I * EDSq + B.F * B.F * cos2D * sin2I * EISq);
1931 sqrt(sin2D * cos2I * Errors->F * Errors->F +
1932 B.F * B.F * cos2D * cos2I * EDSq + B.F * B.F * sin2D * sin2I * EISq);
1933 Errors->Z = sqrt(sin2I * Errors->F * Errors->F + B.F * B.F * cos2I * EISq);
1934 Errors->H = sqrt(cos2I * Errors->F * Errors->F + B.F * B.F * sin2I * EISq);
1964 double CosLat, SinLat, rc, xp, zp;
1972 CosLat = cos(DEG2RAD(CoordGeodetic.phi));
1973 SinLat = sin(DEG2RAD(CoordGeodetic.phi));
1977 rc = Ellip.a / sqrt(1.0 - Ellip.epssq * SinLat * SinLat);
1981 xp = (rc + CoordGeodetic.HeightAboveEllipsoid) * CosLat;
1982 zp = (rc * (1.0 - Ellip.epssq) + CoordGeodetic.HeightAboveEllipsoid) * SinLat;
1986 CoordSpherical->r = sqrt(xp * xp + zp * zp);
1987 CoordSpherical->phig =
1988 RAD2DEG(asin(zp / CoordSpherical->r));
1989 CoordSpherical->lambda = CoordGeodetic.lambda;
1997 Assignee.X = Elements.X;
1998 Assignee.Y = Elements.Y;
1999 Assignee.Z = Elements.Z;
2000 Assignee.H = Elements.H;
2001 Assignee.F = Elements.F;
2002 Assignee.Decl = Elements.Decl;
2003 Assignee.Incl = Elements.Incl;
2004 Assignee.GV = Elements.GV;
2005 Assignee.Xdot = Elements.Xdot;
2006 Assignee.Ydot = Elements.Ydot;
2007 Assignee.Zdot = Elements.Zdot;
2008 Assignee.Hdot = Elements.Hdot;
2009 Assignee.Fdot = Elements.Fdot;
2010 Assignee.Decldot = Elements.Decldot;
2011 Assignee.Incldot = Elements.Incldot;
2012 Assignee.GVdot = Elements.GVdot;
2021 product.X = Elements.X * factor;
2022 product.Y = Elements.Y * factor;
2023 product.Z = Elements.Z * factor;
2024 product.H = Elements.H * factor;
2025 product.F = Elements.F * factor;
2026 product.Incl = Elements.Incl * factor;
2027 product.Decl = Elements.Decl * factor;
2028 product.GV = Elements.GV * factor;
2029 product.Xdot = Elements.Xdot * factor;
2030 product.Ydot = Elements.Ydot * factor;
2031 product.Zdot = Elements.Zdot * factor;
2032 product.Hdot = Elements.Hdot * factor;
2033 product.Fdot = Elements.Fdot * factor;
2034 product.Incldot = Elements.Incldot * factor;
2035 product.Decldot = Elements.Decldot * factor;
2036 product.GVdot = Elements.GVdot * factor;
2047 difference.X = minuend.X - subtrahend.X;
2048 difference.Y = minuend.Y - subtrahend.Y;
2049 difference.Z = minuend.Z - subtrahend.Z;
2051 difference.H = minuend.H - subtrahend.H;
2052 difference.F = minuend.F - subtrahend.F;
2053 difference.Decl = minuend.Decl - subtrahend.Decl;
2054 difference.Incl = minuend.Incl - subtrahend.Incl;
2056 difference.Xdot = minuend.Xdot - subtrahend.Xdot;
2057 difference.Ydot = minuend.Ydot - subtrahend.Ydot;
2058 difference.Zdot = minuend.Zdot - subtrahend.Zdot;
2060 difference.Hdot = minuend.Hdot - subtrahend.Hdot;
2061 difference.Fdot = minuend.Fdot - subtrahend.Fdot;
2062 difference.Decldot = minuend.Decldot - subtrahend.Decldot;
2063 difference.Incldot = minuend.Incldot - subtrahend.Incldot;
2065 difference.GV = minuend.GV - subtrahend.GV;
2066 difference.GVdot = minuend.GVdot - subtrahend.GVdot;
2085 double Lam0, K0, falseE, falseN;
2086 double K0R4, K0R4oa;
2089 double X, Y, pscale, CoM;
2095 Lambda = DEG2RAD(CoordGeodetic.lambda);
2096 Phi = DEG2RAD(CoordGeodetic.phi);
2098 MAG_GetUTMParameters(Phi, Lambda, &Zone, &Hemisphere, &Lam0);
2101 if (Hemisphere ==
'n' || Hemisphere ==
'N') {
2104 if (Hemisphere ==
's' || Hemisphere ==
'S') {
2112 Eps = 0.081819190842621494335;
2113 Epssq = 0.0066943799901413169961;
2114 K0R4 = 6367449.1458234153093 * K0;
2115 K0R4oa = K0R4 / 6378137;
2117 Acoeff[0] = 8.37731820624469723600E-04;
2118 Acoeff[1] = 7.60852777357248641400E-07;
2119 Acoeff[2] = 1.19764550324249124400E-09;
2120 Acoeff[3] = 2.42917068039708917100E-12;
2121 Acoeff[4] = 5.71181837042801392800E-15;
2122 Acoeff[5] = 1.47999793137966169400E-17;
2123 Acoeff[6] = 4.10762410937071532000E-20;
2124 Acoeff[7] = 1.21078503892257704200E-22;
2132 MAG_TMfwd4(Eps, Epssq, K0R4, K0R4oa, Acoeff, Lam0, K0, falseE, falseN, XYonly,
2133 Lambda, Phi, &X, &Y, &pscale, &CoM);
2137 UTMParameters->Easting = X;
2138 UTMParameters->Northing = Y;
2139 UTMParameters->Zone = Zone;
2140 UTMParameters->HemiSphere = Hemisphere;
2141 UTMParameters->CentralMeridian =
2143 UTMParameters->ConvergenceOfMeridians =
2145 UTMParameters->PointScale = pscale;
2150int MAG_GetUTMParameters(
double Latitude,
double Longitude,
int *Zone,
2151 char *Hemisphere,
double *CentralMeridian) {
2170 if ((Latitude < DEG2RAD(MAG_UTM_MIN_LAT_DEGREE)) ||
2172 DEG2RAD(MAG_UTM_MAX_LAT_DEGREE))) {
2176 if ((Longitude < -M_PI) ||
2177 (Longitude > (2 * M_PI))) {
2182 if (Longitude < 0) Longitude += (2 * M_PI) + 1.0e-10;
2183 Lat_Degrees = (long)(Latitude * 180.0 / M_PI);
2184 Long_Degrees = (long)(Longitude * 180.0 / M_PI);
2186 if (Longitude < M_PI)
2187 temp_zone = (long)(31 + ((Longitude * 180.0 / M_PI) / 6.0));
2189 temp_zone = (long)(((Longitude * 180.0 / M_PI) / 6.0) - 29);
2190 if (temp_zone > 60) temp_zone = 1;
2192 if ((Lat_Degrees > 55) && (Lat_Degrees < 64) && (Long_Degrees > -1) &&
2195 if ((Lat_Degrees > 55) && (Lat_Degrees < 64) && (Long_Degrees > 2) &&
2196 (Long_Degrees < 12))
2198 if ((Lat_Degrees > 71) && (Long_Degrees > -1) && (Long_Degrees < 9))
2200 if ((Lat_Degrees > 71) && (Long_Degrees > 8) && (Long_Degrees < 21))
2202 if ((Lat_Degrees > 71) && (Long_Degrees > 20) && (Long_Degrees < 33))
2204 if ((Lat_Degrees > 71) && (Long_Degrees > 32) && (Long_Degrees < 42))
2208 if (temp_zone >= 31)
2209 *CentralMeridian = (6 * temp_zone - 183) * M_PI / 180.0;
2211 *CentralMeridian = (6 * temp_zone + 177) * M_PI / 180.0;
2219 return (Error_Code);
2222int MAG_isNaN(
double d) {
return d != d; }
2256 Psi = (M_PI / 180) * (CoordSpherical.phig - CoordGeodetic.phi);
2259 MagneticResultsGeo->Bz =
2260 MagneticResultsSph.Bx * sin(Psi) + MagneticResultsSph.Bz * cos(Psi);
2261 MagneticResultsGeo->Bx =
2262 MagneticResultsSph.Bx * cos(Psi) - MagneticResultsSph.Bz * sin(Psi);
2263 MagneticResultsGeo->By = MagneticResultsSph.By;
2268 double *y,
double *z) {
2272 radphi = CoordSpherical.phig * (M_PI / 180);
2273 radlambda = CoordSpherical.lambda * (M_PI / 180);
2275 *x = CoordSpherical.r * cos(radphi) * cos(radlambda);
2276 *y = CoordSpherical.r * cos(radphi) * sin(radlambda);
2277 *z = CoordSpherical.r * sin(radphi);
2289 MAG_SphericalToCartesian(CoordSpherical, &x, &y, &z);
2290 MAG_CartesianToGeodetic(Ellip, x, y, z, CoordGeodetic);
2293void MAG_TMfwd4(
double Eps,
double Epssq,
double K0R4,
double K0R4oa,
2294 double Acoeff[],
double Lam0,
double K0,
double falseE,
2295 double falseN,
int XYonly,
double Lambda,
double Phi,
double *X,
2296 double *Y,
double *pscale,
double *CoM) {
2343 double Lam, CLam, SLam, CPhi, SPhi;
2344 double P, part1, part2, denom, CChi, SChi;
2346 double T, Tsq, denom2;
2347 double c2u, s2u, c4u, s4u, c6u, s6u, c8u, s8u;
2348 double c2v, s2v, c4v, s4v, c6v, s6v, c8v, s8v;
2349 double Xstar, Ystar;
2350 double sig1, sig2, comroo;
2361 Lam = Lambda - Lam0;
2373 P = exp(Eps * ATanH(Eps * SPhi));
2374 part1 = (1 + SPhi) / P;
2375 part2 = (1 - SPhi) * P;
2376 denom = 1 / (part1 + part2);
2377 CChi = 2 * CPhi * denom;
2378 SChi = (part1 - part2) * denom;
2392 V = atan2(SChi, CChi * CLam);
2405 denom2 = 1 / (1 - Tsq);
2406 c2u = (1 + Tsq) * denom2;
2407 s2u = 2 * T * denom2;
2408 c2v = (-1 + CChi * CChi * (1 + CLam * CLam)) * denom2;
2409 s2v = 2 * CLam * CChi * SChi * denom2;
2411 c4u = 1 + 2 * s2u * s2u;
2412 s4u = 2 * c2u * s2u;
2413 c4v = 1 - 2 * s2v * s2v;
2414 s4v = 2 * c2v * s2v;
2416 c6u = c4u * c2u + s4u * s2u;
2417 s6u = s4u * c2u + c4u * s2u;
2418 c6v = c4v * c2v - s4v * s2v;
2419 s6v = s4v * c2v + c4v * s2v;
2421 c8u = 1 + 2 * s4u * s4u;
2422 s8u = 2 * c4u * s4u;
2423 c8v = 1 - 2 * s4v * s4v;
2424 s8v = 2 * c4v * s4v;
2432 Xstar = Acoeff[3] * s8u * c8v;
2433 Xstar = Xstar + Acoeff[2] * s6u * c6v;
2434 Xstar = Xstar + Acoeff[1] * s4u * c4v;
2435 Xstar = Xstar + Acoeff[0] * s2u * c2v;
2438 Ystar = Acoeff[3] * c8u * s8v;
2439 Ystar = Ystar + Acoeff[2] * c6u * s6v;
2440 Ystar = Ystar + Acoeff[1] * c4u * s4v;
2441 Ystar = Ystar + Acoeff[0] * c2u * s2v;
2446 *X = K0R4 * Xstar + falseE;
2447 *Y = K0R4 * Ystar + falseN;
2456 sig1 = 8 * Acoeff[3] * c8u * c8v;
2457 sig1 = sig1 + 6 * Acoeff[2] * c6u * c6v;
2458 sig1 = sig1 + 4 * Acoeff[1] * c4u * c4v;
2459 sig1 = sig1 + 2 * Acoeff[0] * c2u * c2v;
2462 sig2 = 8 * Acoeff[3] * s8u * s8v;
2463 sig2 = sig2 + 6 * Acoeff[2] * s6u * s6v;
2464 sig2 = sig2 + 4 * Acoeff[1] * s4u * s4v;
2465 sig2 = sig2 + 2 * Acoeff[0] * s2u * s2v;
2469 sqrt((1 - Epssq * SPhi * SPhi) * denom2 * (sig1 * sig1 + sig2 * sig2));
2471 *pscale = K0R4oa * 2 * denom * comroo;
2472 *CoM = atan2(SChi * SLam, CLam) + atan2(sig2, sig1);
2492 int MonthDays[13], CumulativeDays = 0;
2494 int i, DayOfTheYear;
2496 if (CalendarDate->DecimalYear == 0) {
2497 CalendarDate->Year = 0;
2498 CalendarDate->Month = 0;
2499 CalendarDate->Day = 0;
2503 CalendarDate->Year = (int)floor(CalendarDate->DecimalYear);
2505 if ((CalendarDate->Year % 4 == 0 && CalendarDate->Year % 100 != 0) ||
2506 CalendarDate->Year % 400 == 0)
2510 floor((CalendarDate->DecimalYear - (
double)CalendarDate->Year) *
2511 (365.0 + (
double)ExtraDay) +
2519 MonthDays[2] = 28 + ExtraDay;
2531 for (i = 1; i <= 12; i++) {
2532 CumulativeDays = CumulativeDays + MonthDays[i];
2534 if (DayOfTheYear <= CumulativeDays) {
2535 CalendarDate->Month = i;
2536 CalendarDate->Day = MonthDays[i] - (CumulativeDays - DayOfTheYear);
2574 sin_phi = sin(DEG2RAD(CoordSpherical.phig));
2576 if (nMax <= 16 || (1 - fabs(sin_phi)) <
2578 FLAG = MAG_PcupLow(LegendreFunction->Pcup, LegendreFunction->dPcup, sin_phi,
2581 FLAG = MAG_PcupHigh(LegendreFunction->Pcup, LegendreFunction->dPcup,
2605 CoordGeodetic->phi = CoordGeodetic->phi < (-90.0 + MAG_GEO_POLE_TOLERANCE)
2606 ? (-90.0 + MAG_GEO_POLE_TOLERANCE)
2607 : CoordGeodetic->phi;
2608 CoordGeodetic->phi = CoordGeodetic->phi > (90.0 - MAG_GEO_POLE_TOLERANCE)
2609 ? (90.0 - MAG_GEO_POLE_TOLERANCE)
2610 : CoordGeodetic->phi;
2614int MAG_ComputeSphericalHarmonicVariables(
2643 double cos_lambda, sin_lambda;
2645 cos_lambda = cos(DEG2RAD(CoordSpherical.lambda));
2646 sin_lambda = sin(DEG2RAD(CoordSpherical.lambda));
2650 SphVariables->RelativeRadiusPower[0] =
2651 (Ellip.re / CoordSpherical.r) * (Ellip.re / CoordSpherical.r);
2652 for (n = 1; n <= nMax; n++) {
2653 SphVariables->RelativeRadiusPower[n] =
2654 SphVariables->RelativeRadiusPower[n - 1] *
2655 (Ellip.re / CoordSpherical.r);
2664 SphVariables->cos_mlambda[0] =
2666 SphVariables->sin_mlambda[0] = 0.0;
2667 if (nMax + 1 >= 2) {
2668 SphVariables->cos_mlambda[1] = cos_lambda;
2669 SphVariables->sin_mlambda[1] = sin_lambda;
2672 if (nMax + 1 >= 3) {
2673 for (m = 2; m <= nMax; m++) {
2674 SphVariables->cos_mlambda[m] =
2675 SphVariables->cos_mlambda[m - 1] * cos_lambda -
2676 SphVariables->sin_mlambda[m - 1] * sin_lambda;
2677 SphVariables->sin_mlambda[m] =
2678 SphVariables->cos_mlambda[m - 1] * sin_lambda +
2679 SphVariables->sin_mlambda[m - 1] * cos_lambda;
2696 ((TimedMagneticModel->nMax + 1) * (TimedMagneticModel->nMax + 2) / 2);
2697 LegendreFunction = MAG_AllocateLegendreFunctionMemory(
2699 SphVariables = MAG_AllocateSphVarMemory(TimedMagneticModel->nMax);
2700 MAG_ComputeSphericalHarmonicVariables(
2701 Ellip, CoordSpherical, TimedMagneticModel->nMax,
2703 MAG_AssociatedLegendreFunction(CoordSpherical, TimedMagneticModel->nMax,
2706 LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical,
2708 MAG_RotateMagneticVector(
2709 CoordSpherical, CoordGeodetic, GradYResultsSph,
2712 MAG_CalculateGradientElements(
2713 GradYResultsGeo, GeoMagneticElements,
2717 MAG_FreeLegendreMemory(LegendreFunction);
2718 MAG_FreeSphVarMemory(SphVariables);
2731 for (n = 1; n <= MagneticModel->nMax; n++) {
2732 for (m = 0; m <= n; m++) {
2733 index = (n * (n + 1) / 2 + m);
2735 GradY->Bz -= SphVariables.RelativeRadiusPower[n] *
2736 (-1 * MagneticModel->Main_Field_Coeff_G[index] *
2737 SphVariables.sin_mlambda[m] +
2738 MagneticModel->Main_Field_Coeff_H[index] *
2739 SphVariables.cos_mlambda[m]) *
2740 (
double)(n + 1) * (
double)(m)*LegendreFunction->Pcup[index] *
2741 (1 / CoordSpherical.r);
2742 GradY->By += SphVariables.RelativeRadiusPower[n] *
2743 (MagneticModel->Main_Field_Coeff_G[index] *
2744 SphVariables.cos_mlambda[m] +
2745 MagneticModel->Main_Field_Coeff_H[index] *
2746 SphVariables.sin_mlambda[m]) *
2747 (
double)(m * m) * LegendreFunction->Pcup[index] *
2748 (1 / CoordSpherical.r);
2749 GradY->Bx -= SphVariables.RelativeRadiusPower[n] *
2750 (-1 * MagneticModel->Main_Field_Coeff_G[index] *
2751 SphVariables.sin_mlambda[m] +
2752 MagneticModel->Main_Field_Coeff_H[index] *
2753 SphVariables.cos_mlambda[m]) *
2754 (
double)(m)*LegendreFunction->dPcup[index] *
2755 (1 / CoordSpherical.r);
2759 cos_phi = cos(DEG2RAD(CoordSpherical.phig));
2760 if (fabs(cos_phi) > 1.0e-10) {
2761 GradY->By = GradY->By / (cos_phi * cos_phi);
2762 GradY->Bx = GradY->Bx / (cos_phi);
2763 GradY->Bz = GradY->Bz / (cos_phi);
2776int MAG_PcupHigh(
double *Pcup,
double *dPcup,
double x,
int nMax)
2820 double pm2, pm1, pmm, plm, rescalem, z, scalef;
2821 double *f1, *f2, *PreSqr;
2822 int k, kstart, m, n, NumTerms;
2824 NumTerms = ((nMax + 1) * (nMax + 2) / 2);
2826 z = sqrt((1.0 - x) * (1.0 + x));
2832 if (fabs(x) == 1.0) {
2833 printf(
"Error in PcupHigh: derivative cannot be calculated at poles\n");
2837 f1 = (
double *)malloc((NumTerms + 1) *
sizeof(double));
2843 PreSqr = (
double *)malloc((NumTerms + 1) *
sizeof(double));
2845 if (PreSqr == NULL) {
2850 f2 = (
double *)malloc((NumTerms + 1) *
sizeof(double));
2859 for (n = 0; n <= 2 * nMax + 1; ++n) {
2860 PreSqr[n] = sqrt((
double)(n));
2865 for (n = 2; n <= nMax; n++) {
2867 f1[k] = (double)(2 * n - 1) / (double)(n);
2868 f2[k] = (double)(n - 1) / (double)(n);
2869 for (m = 1; m <= n - 2; m++) {
2871 f1[k] = (double)(2 * n - 1) / PreSqr[n + m] / PreSqr[n - m];
2873 PreSqr[n - m - 1] * PreSqr[n + m - 1] / PreSqr[n + m] / PreSqr[n - m];
2883 if (nMax == 0)
return FALSE;
2889 for (n = 2; n <= nMax; n++) {
2891 plm = f1[k] * x * pm1 - f2[k] * pm2;
2893 dPcup[k] = (double)(n) * (pm1 - x * plm) / z;
2897#pragma GCC diagnostic push
2898#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
2900 pmm = PreSqr[2] * scalef;
2901 rescalem = 1.0 / scalef;
2904 for (m = 1; m <= nMax - 1; ++m) {
2905 rescalem = rescalem * z;
2908 kstart = kstart + m + 1;
2909 pmm = pmm * PreSqr[2 * m + 1] / PreSqr[2 * m];
2910 Pcup[kstart] = pmm * rescalem / PreSqr[2 * m + 1];
2911 dPcup[kstart] = -((double)(m)*x * Pcup[kstart] / z);
2912 pm2 = pmm / PreSqr[2 * m + 1];
2915 pm1 = x * PreSqr[2 * m + 1] * pm2;
2916 Pcup[k] = pm1 * rescalem;
2918 ((pm2 * rescalem) * PreSqr[2 * m + 1] - x * (
double)(m + 1) * Pcup[k]) /
2921 for (n = m + 2; n <= nMax; ++n) {
2923 plm = x * f1[k] * pm1 - f2[k] * pm2;
2924 Pcup[k] = plm * rescalem;
2925 dPcup[k] = (PreSqr[n + m] * PreSqr[n - m] * (pm1 * rescalem) -
2926 (
double)(n)*x * Pcup[k]) /
2931#pragma GCC diagnostic pop
2935 rescalem = rescalem * z;
2936 kstart = kstart + m + 1;
2937 pmm = pmm / PreSqr[2 * nMax];
2938 Pcup[kstart] = pmm * rescalem;
2939 dPcup[kstart] = -(double)(nMax)*x * Pcup[kstart] / z;
2947int MAG_PcupLow(
double *Pcup,
double *dPcup,
double x,
int nMax)
2973 int n, m, index, index1, index2, NumTerms;
2974 double k, z, *schmidtQuasiNorm;
2978 z = sqrt((1.0 - x) * (1.0 + x));
2980 NumTerms = ((nMax + 1) * (nMax + 2) / 2);
2981 schmidtQuasiNorm = (
double *)malloc((NumTerms + 1) *
sizeof(double));
2983 if (schmidtQuasiNorm == NULL) {
2989 for (n = 1; n <= nMax; n++) {
2990 for (m = 0; m <= n; m++) {
2991 index = (n * (n + 1) / 2 + m);
2993 index1 = (n - 1) * n / 2 + m - 1;
2994 Pcup[index] = z * Pcup[index1];
2995 dPcup[index] = z * dPcup[index1] + x * Pcup[index1];
2996 }
else if (n == 1 && m == 0) {
2997 index1 = (n - 1) * n / 2 + m;
2998 Pcup[index] = x * Pcup[index1];
2999 dPcup[index] = x * dPcup[index1] - z * Pcup[index1];
3000 }
else if (n > 1 && n != m) {
3001 index1 = (n - 2) * (n - 1) / 2 + m;
3002 index2 = (n - 1) * n / 2 + m;
3004 Pcup[index] = x * Pcup[index2];
3005 dPcup[index] = x * dPcup[index2] - z * Pcup[index2];
3007 k = (double)(((n - 1) * (n - 1)) - (m * m)) /
3008 (double)((2 * n - 1) * (2 * n - 3));
3009 Pcup[index] = x * Pcup[index2] - k * Pcup[index1];
3011 x * dPcup[index2] - z * Pcup[index2] - k * dPcup[index1];
3019 schmidtQuasiNorm[0] = 1.0;
3020 for (n = 1; n <= nMax; n++) {
3021 index = (n * (n + 1) / 2);
3022 index1 = (n - 1) * n / 2;
3024 schmidtQuasiNorm[index] =
3025 schmidtQuasiNorm[index1] * (double)(2 * n - 1) / (double)n;
3027 for (m = 1; m <= n; m++) {
3028 index = (n * (n + 1) / 2 + m);
3029 index1 = (n * (n + 1) / 2 + m - 1);
3030 schmidtQuasiNorm[index] =
3031 schmidtQuasiNorm[index1] *
3032 sqrt((
double)((n - m + 1) * (m == 1 ? 2 : 1)) / (double)(n + m));
3040 for (n = 1; n <= nMax; n++) {
3041 for (m = 0; m <= n; m++) {
3042 index = (n * (n + 1) / 2 + m);
3043 Pcup[index] = Pcup[index] * schmidtQuasiNorm[index];
3044 dPcup[index] = -dPcup[index] * schmidtQuasiNorm[index];
3050 if (schmidtQuasiNorm) free(schmidtQuasiNorm);
3070 MagneticModel->SecularVariationUsed = TRUE;
3071 MagneticResults->Bz = 0.0;
3072 MagneticResults->By = 0.0;
3073 MagneticResults->Bx = 0.0;
3074 for (n = 1; n <= MagneticModel->nMaxSecVar; n++) {
3075 for (m = 0; m <= n; m++) {
3076 index = (n * (n + 1) / 2 + m);
3082 MagneticResults->Bz -= SphVariables.RelativeRadiusPower[n] *
3083 (MagneticModel->Secular_Var_Coeff_G[index] *
3084 SphVariables.cos_mlambda[m] +
3085 MagneticModel->Secular_Var_Coeff_H[index] *
3086 SphVariables.sin_mlambda[m]) *
3087 (
double)(n + 1) * LegendreFunction->Pcup[index];
3093 MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
3094 (MagneticModel->Secular_Var_Coeff_G[index] *
3095 SphVariables.sin_mlambda[m] -
3096 MagneticModel->Secular_Var_Coeff_H[index] *
3097 SphVariables.cos_mlambda[m]) *
3098 (
double)(m)*LegendreFunction->Pcup[index];
3104 MagneticResults->Bx -= SphVariables.RelativeRadiusPower[n] *
3105 (MagneticModel->Secular_Var_Coeff_G[index] *
3106 SphVariables.cos_mlambda[m] +
3107 MagneticModel->Secular_Var_Coeff_H[index] *
3108 SphVariables.sin_mlambda[m]) *
3109 LegendreFunction->dPcup[index];
3112 cos_phi = cos(DEG2RAD(CoordSpherical.phig));
3113 if (fabs(cos_phi) > 1.0e-10) {
3114 MagneticResults->By = MagneticResults->By / cos_phi;
3118 MAG_SecVarSummationSpecial(MagneticModel, SphVariables, CoordSpherical,
3140 double k, sin_phi, *PcupS, schmidtQuasiNorm1, schmidtQuasiNorm2,
3143 PcupS = (
double *)malloc((MagneticModel->nMaxSecVar + 1) *
sizeof(double));
3145 if (PcupS == NULL) {
3151 schmidtQuasiNorm1 = 1.0;
3153 MagneticResults->By = 0.0;
3154 sin_phi = sin(DEG2RAD(CoordSpherical.phig));
3156 for (n = 1; n <= MagneticModel->nMaxSecVar; n++) {
3157 index = (n * (n + 1) / 2 + 1);
3158 schmidtQuasiNorm2 = schmidtQuasiNorm1 * (double)(2 * n - 1) / (double)n;
3160 schmidtQuasiNorm2 * sqrt((
double)(n * 2) / (
double)(n + 1));
3161 schmidtQuasiNorm1 = schmidtQuasiNorm2;
3163 PcupS[n] = PcupS[n - 1];
3165 k = (double)(((n - 1) * (n - 1)) - 1) /
3166 (
double)((2 * n - 1) * (2 * n - 3));
3167 PcupS[n] = sin_phi * PcupS[n - 1] - k * PcupS[n - 2];
3175 MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
3176 (MagneticModel->Secular_Var_Coeff_G[index] *
3177 SphVariables.sin_mlambda[1] -
3178 MagneticModel->Secular_Var_Coeff_H[index] *
3179 SphVariables.cos_mlambda[1]) *
3180 PcupS[n] * schmidtQuasiNorm3;
3183 if (PcupS) free(PcupS);
3218 MagneticResults->Bz = 0.0;
3219 MagneticResults->By = 0.0;
3220 MagneticResults->Bx = 0.0;
3221 for (n = 1; n <= MagneticModel->nMax; n++) {
3222 for (m = 0; m <= n; m++) {
3223 index = (n * (n + 1) / 2 + m);
3230 MagneticResults->Bz -= SphVariables.RelativeRadiusPower[n] *
3231 (MagneticModel->Main_Field_Coeff_G[index] *
3232 SphVariables.cos_mlambda[m] +
3233 MagneticModel->Main_Field_Coeff_H[index] *
3234 SphVariables.sin_mlambda[m]) *
3235 (
double)(n + 1) * LegendreFunction->Pcup[index];
3242 MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
3243 (MagneticModel->Main_Field_Coeff_G[index] *
3244 SphVariables.sin_mlambda[m] -
3245 MagneticModel->Main_Field_Coeff_H[index] *
3246 SphVariables.cos_mlambda[m]) *
3247 (
double)(m)*LegendreFunction->Pcup[index];
3254 MagneticResults->Bx -= SphVariables.RelativeRadiusPower[n] *
3255 (MagneticModel->Main_Field_Coeff_G[index] *
3256 SphVariables.cos_mlambda[m] +
3257 MagneticModel->Main_Field_Coeff_H[index] *
3258 SphVariables.sin_mlambda[m]) *
3259 LegendreFunction->dPcup[index];
3263 cos_phi = cos(DEG2RAD(CoordSpherical.phig));
3264 if (fabs(cos_phi) > 1.0e-10) {
3265 MagneticResults->By = MagneticResults->By / cos_phi;
3273 MAG_SummationSpecial(MagneticModel, SphVariables, CoordSpherical,
3295 double k, sin_phi, *PcupS, schmidtQuasiNorm1, schmidtQuasiNorm2,
3298 PcupS = (
double *)malloc((MagneticModel->nMax + 1) *
sizeof(double));
3305 schmidtQuasiNorm1 = 1.0;
3307 MagneticResults->By = 0.0;
3308 sin_phi = sin(DEG2RAD(CoordSpherical.phig));
3310 for (n = 1; n <= MagneticModel->nMax; n++) {
3315 index = (n * (n + 1) / 2 + 1);
3316 schmidtQuasiNorm2 = schmidtQuasiNorm1 * (double)(2 * n - 1) / (double)n;
3318 schmidtQuasiNorm2 * sqrt((
double)(n * 2) / (
double)(n + 1));
3319 schmidtQuasiNorm1 = schmidtQuasiNorm2;
3321 PcupS[n] = PcupS[n - 1];
3323 k = (double)(((n - 1) * (n - 1)) - 1) /
3324 (
double)((2 * n - 1) * (2 * n - 3));
3325 PcupS[n] = sin_phi * PcupS[n - 1] - k * PcupS[n - 2];
3334 MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
3335 (MagneticModel->Main_Field_Coeff_G[index] *
3336 SphVariables.sin_mlambda[1] -
3337 MagneticModel->Main_Field_Coeff_H[index] *
3338 SphVariables.cos_mlambda[1]) *
3339 PcupS[n] * schmidtQuasiNorm3;
3342 if (PcupS) free(PcupS);
3346int MAG_TimelyModifyMagneticModel(
MAGtype_Date UserDate,
3363 int n, m, index, a, b;
3364 TimedMagneticModel->EditionDate = MagneticModel->EditionDate;
3365 TimedMagneticModel->epoch = MagneticModel->epoch;
3366 TimedMagneticModel->nMax = MagneticModel->nMax;
3367 TimedMagneticModel->nMaxSecVar = MagneticModel->nMaxSecVar;
3368 a = TimedMagneticModel->nMaxSecVar;
3369 b = (a * (a + 1) / 2 + a);
3370 MAG_strlcpy_equivalent(TimedMagneticModel->ModelName,
3371 MagneticModel->ModelName,
3372 sizeof(TimedMagneticModel->ModelName));
3373 for (n = 1; n <= MagneticModel->nMax; n++) {
3374 for (m = 0; m <= n; m++) {
3375 index = (n * (n + 1) / 2 + m);
3377 TimedMagneticModel->Main_Field_Coeff_H[index] =
3378 MagneticModel->Main_Field_Coeff_H[index] +
3379 (UserDate.DecimalYear - MagneticModel->epoch) *
3380 MagneticModel->Secular_Var_Coeff_H[index];
3381 TimedMagneticModel->Main_Field_Coeff_G[index] =
3382 MagneticModel->Main_Field_Coeff_G[index] +
3383 (UserDate.DecimalYear - MagneticModel->epoch) *
3384 MagneticModel->Secular_Var_Coeff_G[index];
3385 TimedMagneticModel->Secular_Var_Coeff_H[index] =
3387 ->Secular_Var_Coeff_H[index];
3390 TimedMagneticModel->Secular_Var_Coeff_G[index] =
3391 MagneticModel->Secular_Var_Coeff_G[index];
3393 TimedMagneticModel->Main_Field_Coeff_H[index] =
3394 MagneticModel->Main_Field_Coeff_H[index];
3395 TimedMagneticModel->Main_Field_Coeff_G[index] =
3396 MagneticModel->Main_Field_Coeff_G[index];
3434 if (Geoid->UseGeoid == 1) {
3436 MAG_EquivalentLatLon(CoordGeodetic->phi, CoordGeodetic->lambda, &lat, &lon);
3437 Error_Code = MAG_GetGeoidHeight(lat, lon, &DeltaHeight, Geoid);
3438 CoordGeodetic->HeightAboveEllipsoid =
3439 CoordGeodetic->HeightAboveGeoid + DeltaHeight / 1000;
3445 CoordGeodetic->HeightAboveEllipsoid = CoordGeodetic->HeightAboveGeoid;
3448 return (Error_Code);
3451int MAG_GetGeoidHeight(
double Latitude,
double Longitude,
double *DeltaHeight,
3467 double DeltaX, DeltaY;
3468 double ElevationSE, ElevationSW, ElevationNE, ElevationNW;
3469 double OffsetX, OffsetY;
3470 double PostX, PostY;
3471 double UpperY, LowerY;
3474 if (!Geoid->Geoid_Initialized) {
3478 if ((Latitude < -90) || (Latitude > 90)) {
3481 if ((Longitude < -180) || (Longitude > 360)) {
3488 if (Longitude < 0.0) {
3489 OffsetX = (Longitude + 360.0) * Geoid->ScaleFactor;
3491 OffsetX = Longitude * Geoid->ScaleFactor;
3493 OffsetY = (90.0 - Latitude) * Geoid->ScaleFactor;
3499 PostX = floor(OffsetX);
3500 if ((PostX + 1) == Geoid->NumbGeoidCols) PostX--;
3501 PostY = floor(OffsetY);
3502 if ((PostY + 1) == Geoid->NumbGeoidRows) PostY--;
3504 Index = (long)(PostY * Geoid->NumbGeoidCols + PostX);
3505 ElevationNW = (double)Geoid->GeoidHeightBuffer[Index];
3506 ElevationNE = (double)Geoid->GeoidHeightBuffer[Index + 1];
3508 Index = (long)((PostY + 1) * Geoid->NumbGeoidCols + PostX);
3509 ElevationSW = (double)Geoid->GeoidHeightBuffer[Index];
3510 ElevationSE = (double)Geoid->GeoidHeightBuffer[Index + 1];
3514 DeltaX = OffsetX - PostX;
3515 DeltaY = OffsetY - PostY;
3517 UpperY = ElevationNW + DeltaX * (ElevationNE - ElevationNW);
3518 LowerY = ElevationSW + DeltaX * (ElevationSE - ElevationSW);
3520 *DeltaHeight = UpperY + DeltaY * (LowerY - UpperY);
3528void MAG_EquivalentLatLon(
double lat,
double lon,
double *repairedLat,
3529 double *repairedLon)
3537 if (colat < 0) colat = -colat;
3538 while (colat > 360) {
3543 *repairedLon = *repairedLon + 180;
3545 *repairedLat = 90 - colat;
3546 if (*repairedLon > 360) *repairedLon -= 360;
3547 if (*repairedLon < -180) *repairedLon += 360;
3555 double decl_variable, decl_constant;
3556 Uncertainty->F = WMM_UNCERTAINTY_F;
3557 Uncertainty->H = WMM_UNCERTAINTY_H;
3558 Uncertainty->X = WMM_UNCERTAINTY_X;
3559 Uncertainty->Z = WMM_UNCERTAINTY_Z;
3560 Uncertainty->Incl = WMM_UNCERTAINTY_I;
3561 Uncertainty->Y = WMM_UNCERTAINTY_Y;
3562 decl_variable = (WMM_UNCERTAINTY_D_COEF / H);
3563 decl_constant = (WMM_UNCERTAINTY_D_OFFSET);
3565 sqrt(decl_constant * decl_constant + decl_variable * decl_variable);
3566 if (Uncertainty->Decl > 180) {
3567 Uncertainty->Decl = 180;
3572 double decl_variable, decl_constant;
3573 Uncertainty->F = WMMHR_UNCERTAINTY_F;
3574 Uncertainty->H = WMMHR_UNCERTAINTY_H;
3575 Uncertainty->X = WMMHR_UNCERTAINTY_X;
3576 Uncertainty->Z = WMMHR_UNCERTAINTY_Z;
3577 Uncertainty->Incl = WMMHR_UNCERTAINTY_I;
3578 Uncertainty->Y = WMMHR_UNCERTAINTY_Y;
3579 decl_variable = (WMMHR_UNCERTAINTY_D_COEF / H);
3580 decl_constant = (WMMHR_UNCERTAINTY_D_OFFSET);
3582 sqrt(decl_constant * decl_constant + decl_variable * decl_variable);
3583 if (Uncertainty->Decl > 180) {
3584 Uncertainty->Decl = 180;
3588void MAG_PrintUserDataWithUncertainty(
3594 char *DeclString = malloc(
sizeof(
char) * dms_size);
3595 char *InclString = malloc(
sizeof(
char) * dms_size);
3596 char *GVString = malloc(
sizeof(
char) * dms_size);
3597 memset(DeclString,
'\0', dms_size);
3598 memset(InclString,
'\0', dms_size);
3599 memset(GVString,
'\0', dms_size);
3600 MAG_DegreeToDMSstring(GeomagElements.Incl, 2, InclString);
3601 if (GeomagElements.H < 6000 && GeomagElements.H > 2000)
3602 MAG_Warnings(1, GeomagElements.H, MagneticModel);
3603 if (GeomagElements.H < 2000) MAG_Warnings(2, GeomagElements.H, MagneticModel);
3604 if (MagneticModel->SecularVariationUsed == TRUE) {
3605 MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
3606 printf(
"\n Results For \n\n");
3607 if (SpaceInput.phi < 0)
3608 printf(
"Latitude %.2fS\n", -SpaceInput.phi);
3610 printf(
"Latitude %.2fN\n", SpaceInput.phi);
3611 if (SpaceInput.lambda < 0)
3612 printf(
"Longitude %.2fW\n", -SpaceInput.lambda);
3614 printf(
"Longitude %.2fE\n", SpaceInput.lambda);
3615 if (Geoid->UseGeoid == 1)
3616 printf(
"Altitude: %.2f Kilometers above mean sea level\n",
3617 SpaceInput.HeightAboveGeoid);
3619 printf(
"Altitude: %.2f Kilometers above the WGS-84 ellipsoid\n",
3620 SpaceInput.HeightAboveEllipsoid);
3621 printf(
"Date: %.1f\n", TimeInput.DecimalYear);
3622 printf(
"\n Main Field\t\t\tSecular Change\n");
3623 printf(
"F = %9.1f +/- %5.1f nT\t\t Fdot = %5.1f\tnT/yr\n",
3624 GeomagElements.F, Errors.F, GeomagElements.Fdot);
3625 printf(
"H = %9.1f +/- %5.1f nT\t\t Hdot = %5.1f\tnT/yr\n",
3626 GeomagElements.H, Errors.H, GeomagElements.Hdot);
3627 printf(
"X = %9.1f +/- %5.1f nT\t\t Xdot = %5.1f\tnT/yr\n",
3628 GeomagElements.X, Errors.X, GeomagElements.Xdot);
3629 printf(
"Y = %9.1f +/- %5.1f nT\t\t Ydot = %5.1f\tnT/yr\n",
3630 GeomagElements.Y, Errors.Y, GeomagElements.Ydot);
3631 printf(
"Z = %9.1f +/- %5.1f nT\t\t Zdot = %5.1f\tnT/yr\n",
3632 GeomagElements.Z, Errors.Z, GeomagElements.Zdot);
3633 if (GeomagElements.Decl < 0)
3634 printf(
"Decl =%20s (WEST) +/-%3.0f Min Ddot = %.1f\tMin/yr\n",
3635 DeclString, 60 * Errors.Decl, 60 * GeomagElements.Decldot);
3637 printf(
"Decl =%20s (EAST) +/-%3.0f Min Ddot = %.1f\tMin/yr\n",
3638 DeclString, 60 * Errors.Decl, 60 * GeomagElements.Decldot);
3639 if (GeomagElements.Incl < 0)
3640 printf(
"Incl =%20s (UP) +/-%3.0f Min Idot = %.1f\tMin/yr\n",
3641 InclString, 60 * Errors.Incl, 60 * GeomagElements.Incldot);
3643 printf(
"Incl =%20s (DOWN) +/-%3.0f Min Idot = %.1f\tMin/yr\n",
3644 InclString, 60 * Errors.Incl, 60 * GeomagElements.Incldot);
3646 MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
3647 printf(
"\n Results For \n\n");
3648 if (SpaceInput.phi < 0)
3649 printf(
"Latitude %.2fS\n", -SpaceInput.phi);
3651 printf(
"Latitude %.2fN\n", SpaceInput.phi);
3652 if (SpaceInput.lambda < 0)
3653 printf(
"Longitude %.2fW\n", -SpaceInput.lambda);
3655 printf(
"Longitude %.2fE\n", SpaceInput.lambda);
3656 if (Geoid->UseGeoid == 1)
3657 printf(
"Altitude: %.2f Kilometers above MSL\n",
3658 SpaceInput.HeightAboveGeoid);
3660 printf(
"Altitude: %.2f Kilometers above WGS-84 Ellipsoid\n",
3661 SpaceInput.HeightAboveEllipsoid);
3662 printf(
"Date: %.1f\n", TimeInput.DecimalYear);
3663 printf(
"\n Main Field\n");
3664 printf(
"F = %-9.1f +/-%5.1f nT\n", GeomagElements.F, Errors.F);
3665 printf(
"H = %-9.1f +/-%5.1f nT\n", GeomagElements.H, Errors.H);
3666 printf(
"X = %-9.1f +/-%5.1f nT\n", GeomagElements.X, Errors.X);
3667 printf(
"Y = %-9.1f +/-%5.1f nT\n", GeomagElements.Y, Errors.Y);
3668 printf(
"Z = %-9.1f +/-%5.1f nT\n", GeomagElements.Z, Errors.Z);
3669 if (GeomagElements.Decl < 0)
3670 printf(
"Decl =%20s (WEST)+/-%4f\n", DeclString, 60 * Errors.Decl);
3672 printf(
"Decl =%20s (EAST)+/-%4f\n", DeclString, 60 * Errors.Decl);
3673 if (GeomagElements.Incl < 0)
3674 printf(
"Incl =%20s (UP)+/-%4f\n", InclString, 60 * Errors.Incl);
3676 printf(
"Incl =%20s (DOWN)+/-%4f\n", InclString, 60 * Errors.Incl);
3679 MAG_DegreeToDMSstring(GeomagElements.GV, 2, GVString);
3683 if (SpaceInput.phi < -55) {
3684 printf(
"\n\n Grid variation (SOUTH) =%20s\n", GVString);
3685 }
else if (SpaceInput.phi > 55) {
3686 printf(
"\n\n Grid variation (NORTH) =%20s \n", GVString);
3693size_t MAG_strlcpy_equivalent(
char *dst,
char *src,
size_t dstlen) {
3697 srclen = strlen(src);
3702 int copy_size = (srclen >= dstlen) ? dstlen - 1 : srclen;
3703 memcpy(dst, src, copy_size);
3704 dst[dstlen - 1] =
'\0';