8#include "GeomagnetismHeader.h"
145 MagneticResultsSphVar, MagneticResultsGeoVar;
148 ((TimedMagneticModel->nMax + 1) * (TimedMagneticModel->nMax + 2) / 2);
149 LegendreFunction = MAG_AllocateLegendreFunctionMemory(
151 SphVariables = MAG_AllocateSphVarMemory(TimedMagneticModel->nMax);
152 MAG_ComputeSphericalHarmonicVariables(
153 Ellip, CoordSpherical, TimedMagneticModel->nMax,
155 MAG_AssociatedLegendreFunction(CoordSpherical, TimedMagneticModel->nMax,
158 LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical,
159 &MagneticResultsSph);
161 LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical,
162 &MagneticResultsSphVar);
163 MAG_RotateMagneticVector(
164 CoordSpherical, CoordGeodetic, MagneticResultsSph,
165 &MagneticResultsGeo);
167 MAG_RotateMagneticVector(
168 CoordSpherical, CoordGeodetic, MagneticResultsSphVar,
169 &MagneticResultsGeoVar);
171 MAG_CalculateGeoMagneticElements(
173 GeoMagneticElements);
175 MAG_CalculateSecularVariationElements(
176 MagneticResultsGeoVar,
177 GeoMagneticElements);
180 MAG_FreeLegendreMemory(LegendreFunction);
181 MAG_FreeSphVarMemory(SphVariables);
198 double phiDelta = 0.01, hDelta = -1, x[2], y[2], z[2],
206 MAG_GeodeticToSpherical(Ellip, CoordGeodetic, &AdjCoordSpherical);
207 MAG_Geomag(Ellip, AdjCoordSpherical, CoordGeodetic, TimedMagneticModel,
208 &GeomagneticElements);
209 AdjCoordGeodetic = MAG_CoordGeodeticAssign(CoordGeodetic);
213 AdjCoordGeodetic.phi = CoordGeodetic.phi + phiDelta;
214 MAG_GeodeticToSpherical(Ellip, AdjCoordGeodetic, &AdjCoordSpherical);
215 MAG_Geomag(Ellip, AdjCoordSpherical, AdjCoordGeodetic, TimedMagneticModel,
216 &AdjGeoMagneticElements[0]);
217 MAG_SphericalToCartesian(AdjCoordSpherical, &x[0], &y[0], &z[0]);
218 AdjCoordGeodetic.phi = CoordGeodetic.phi - phiDelta;
219 MAG_GeodeticToSpherical(Ellip, AdjCoordGeodetic, &AdjCoordSpherical);
220 MAG_Geomag(Ellip, AdjCoordSpherical, AdjCoordGeodetic, TimedMagneticModel,
221 &AdjGeoMagneticElements[1]);
222 MAG_SphericalToCartesian(AdjCoordSpherical, &x[1], &y[1], &z[1]);
225 sqrt((x[0] - x[1]) * (x[0] - x[1]) + (y[0] - y[1]) * (y[0] - y[1]) +
226 (z[0] - z[1]) * (z[0] - z[1]));
227 Gradient->GradPhi = MAG_GeoMagneticElementsSubtract(
228 AdjGeoMagneticElements[0], AdjGeoMagneticElements[1]);
230 MAG_GeoMagneticElementsScale(Gradient->GradPhi, 1 / distance);
231 AdjCoordGeodetic = MAG_CoordGeodeticAssign(CoordGeodetic);
242 MAG_GeodeticToSpherical(Ellip, CoordGeodetic, &AdjCoordSpherical);
243 MAG_GradY(Ellip, AdjCoordSpherical, CoordGeodetic, TimedMagneticModel,
244 GeomagneticElements, &(Gradient->GradLambda));
247 AdjCoordGeodetic.HeightAboveEllipsoid =
248 CoordGeodetic.HeightAboveEllipsoid + hDelta;
249 AdjCoordGeodetic.HeightAboveGeoid = CoordGeodetic.HeightAboveGeoid + hDelta;
250 MAG_GeodeticToSpherical(Ellip, AdjCoordGeodetic, &AdjCoordSpherical);
251 MAG_Geomag(Ellip, AdjCoordSpherical, AdjCoordGeodetic, TimedMagneticModel,
252 &AdjGeoMagneticElements[0]);
253 MAG_SphericalToCartesian(AdjCoordSpherical, &x[0], &y[0], &z[0]);
254 AdjCoordGeodetic.HeightAboveEllipsoid =
255 CoordGeodetic.HeightAboveEllipsoid - hDelta;
256 AdjCoordGeodetic.HeightAboveGeoid = CoordGeodetic.HeightAboveGeoid - hDelta;
257 MAG_GeodeticToSpherical(Ellip, AdjCoordGeodetic, &AdjCoordSpherical);
258 MAG_Geomag(Ellip, AdjCoordSpherical, AdjCoordGeodetic, TimedMagneticModel,
259 &AdjGeoMagneticElements[1]);
260 MAG_SphericalToCartesian(AdjCoordSpherical, &x[1], &y[1], &z[1]);
263 sqrt((x[0] - x[1]) * (x[0] - x[1]) + (y[0] - y[1]) * (y[0] - y[1]) +
264 (z[0] - z[1]) * (z[0] - z[1]));
265 Gradient->GradZ = MAG_GeoMagneticElementsSubtract(AdjGeoMagneticElements[0],
266 AdjGeoMagneticElements[1]);
267 Gradient->GradZ = MAG_GeoMagneticElementsScale(Gradient->GradZ, 1 / distance);
268 AdjCoordGeodetic = MAG_CoordGeodeticAssign(CoordGeodetic);
284 Ellip->b = 6356.7523142;
285 Ellip->fla = 1 / 298.257223563;
286 Ellip->eps = sqrt(1 - (Ellip->b * Ellip->b) /
287 (Ellip->a * Ellip->a));
288 Ellip->epssq = (Ellip->eps * Ellip->eps);
292 Geoid->NumbGeoidCols =
294 Geoid->NumbGeoidRows =
296 Geoid->NumbHeaderItems =
298 Geoid->ScaleFactor = 4;
299 Geoid->NumbGeoidElevs = Geoid->NumbGeoidCols * Geoid->NumbGeoidRows;
300 Geoid->Geoid_Initialized =
302 Geoid->UseGeoid = MAG_USE_GEOID;
307int MAG_robustReadMagneticModel_Large(
char *filename,
char *filenameSV,
309 char line[MAXLINELENGTH],
310 ModelName[] =
"Enhanced Magnetic Model";
312 int n, nMax = 0, nMaxSV = 0, num_terms, a, epochlength = 5, i;
314 MODELFILE = fopen(filename,
"r");
315 if (MODELFILE == 0) {
318 if (NULL == fgets(line, MAXLINELENGTH, MODELFILE)) {
322 if (NULL == fgets(line, MAXLINELENGTH, MODELFILE))
break;
323 a = sscanf(line,
"%d", &n);
324 if (n > nMax && (n < 99999 && a == 1 && n > 0)) nMax = n;
325 }
while (n < 99999 && a == 1);
327 MODELFILE = fopen(filenameSV,
"r");
328 if (MODELFILE == 0) {
332 if (NULL == fgets(line, MAXLINELENGTH, MODELFILE))
return 0;
334 if (NULL == fgets(line, MAXLINELENGTH, MODELFILE))
break;
335 a = sscanf(line,
"%d", &n);
336 if (n > nMaxSV && (n < 99999 && a == 1 && n > 0)) nMaxSV = n;
337 }
while (n < 99999 && a == 1);
339 num_terms = CALCULATE_NUMTERMS(nMax);
340 *MagneticModel = MAG_AllocateModelMemory(num_terms);
341 (*MagneticModel)->nMax = nMax;
342 (*MagneticModel)->nMaxSecVar = nMaxSV;
343 if (nMaxSV > 0) (*MagneticModel)->SecularVariationUsed = TRUE;
344 for (i = 0; i < num_terms; i++) {
345 (*MagneticModel)->Main_Field_Coeff_G[i] = 0;
346 (*MagneticModel)->Main_Field_Coeff_H[i] = 0;
347 (*MagneticModel)->Secular_Var_Coeff_G[i] = 0;
348 (*MagneticModel)->Secular_Var_Coeff_H[i] = 0;
350 MAG_readMagneticModel_Large(filename, filenameSV, *MagneticModel);
351 (*MagneticModel)->CoefficientFileEndDate =
352 (*MagneticModel)->epoch + epochlength;
353 strcpy((*MagneticModel)->ModelName, ModelName);
354 (*MagneticModel)->EditionDate = (*MagneticModel)->epoch;
358int MAG_robustReadMagModels(
char *filename,
361 char line[MAXLINELENGTH];
362 int n, nMax = 0, num_terms, a;
364 MODELFILE = fopen(filename,
"r");
365 if (MODELFILE == 0) {
368 if (NULL == fgets(line, MAXLINELENGTH, MODELFILE)) {
371 if (line[0] ==
'%') {
372 MAG_readMagneticModel_SHDF(filename, magneticmodels);
373 }
else if (array_size == 1) {
375 if (NULL == fgets(line, MAXLINELENGTH, MODELFILE))
break;
376 a = sscanf(line,
"%d", &n);
377 if (n > nMax && (n < 99999 && a == 1 && n > 0)) nMax = n;
378 }
while (n < 99999 && a == 1);
379 num_terms = CALCULATE_NUMTERMS(nMax);
380 (*magneticmodels)[0] = MAG_AllocateModelMemory(num_terms);
381 (*magneticmodels)[0]->nMax = nMax;
382 (*magneticmodels)[0]->nMaxSecVar = nMax;
383 MAG_readMagneticModel(filename, (*magneticmodels)[0]);
384 (*magneticmodels)[0]->CoefficientFileEndDate =
385 (*magneticmodels)[0]->epoch + 5;
402void MAG_Error(
int control)
413 printf(
"\nError allocating in MAG_LegendreFunctionMemory.\n");
416 printf(
"\nError allocating in MAG_AllocateModelMemory.\n");
419 printf(
"\nError allocating in MAG_InitializeGeoid\n");
422 printf(
"\nError in setting default values.\n");
425 printf(
"\nError initializing Geoid.\n");
428 printf(
"\nError opening WMM.COF\n.");
431 printf(
"\nError opening WMMSV.COF\n.");
434 printf(
"\nError reading Magnetic Model.\n");
437 printf(
"\nError printing Command Prompt introduction.\n");
441 "\nError converting from geodetic co-ordinates to spherical "
445 printf(
"\nError in time modifying the Magnetic model\n");
448 printf(
"\nError in Geomagnetic\n");
451 printf(
"\nError printing user data\n");
454 printf(
"\nError allocating in MAG_SummationSpecial\n");
457 printf(
"\nError allocating in MAG_SecVarSummationSpecial\n");
460 printf(
"\nError in opening EGM9615.BIN file\n");
464 "\nError: Latitude OR Longitude out of range in "
465 "MAG_GetGeoidHeight\n");
468 printf(
"\nError allocating in MAG_PcupHigh\n");
471 printf(
"\nError allocating in MAG_PcupLow\n");
474 printf(
"\nError opening coefficient file\n");
477 printf(
"\nError: UnitDepth too large\n");
480 printf(
"\nYour system needs Big endian version of EGM9615.BIN. \n");
482 "Please download this file from "
483 "http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml. \n");
484 printf(
"Replace the existing EGM9615.BIN file with the downloaded one\n");
491 double *a_step_size,
double *step_time,
493 int *ElementOption,
int *PrintOption,
char *OutputFile,
517 char filename[] =
"GridProgramDirective.txt";
521 printf(
"Please Enter Minimum Latitude (in decimal degrees):\n");
522 if (NULL == fgets(buffer, 20, stdin)) {
524 printf(
"Unrecognized input default %lf used\n", minimum->phi);
526 sscanf(buffer,
"%lf", &minimum->phi);
529 printf(
"Please Enter Maximum Latitude (in decimal degrees):\n");
530 if (NULL == fgets(buffer, 20, stdin)) {
532 printf(
"Unrecognized input default %lf used\n", maximum->phi);
534 sscanf(buffer,
"%lf", &maximum->phi);
537 printf(
"Please Enter Minimum Longitude (in decimal degrees):\n");
538 if (NULL == fgets(buffer, 20, stdin)) {
540 printf(
"Unrecognized input default %lf used\n", minimum->lambda);
542 sscanf(buffer,
"%lf", &minimum->lambda);
545 printf(
"Please Enter Maximum Longitude (in decimal degrees):\n");
546 if (NULL == fgets(buffer, 20, stdin)) {
548 printf(
"Unrecognized input default %lf used\n", maximum->lambda);
550 sscanf(buffer,
"%lf", &maximum->lambda);
553 printf(
"Please Enter Step Size (in decimal degrees):\n");
554 if (NULL == fgets(buffer, 20, stdin)) {
556 fmax(maximum->phi - minimum->phi, maximum->lambda - minimum->lambda);
557 printf(
"Unrecognized input default %lf used\n", *step_size);
559 sscanf(buffer,
"%lf", step_size);
563 "Select height (default : above MSL) \n1. Above Mean Sea Level\n2. Above "
564 "WGS-84 Ellipsoid \n");
565 if (NULL == fgets(buffer, 20, stdin)) {
567 printf(
"Unrecognized option, height above MSL used.");
569 sscanf(buffer,
"%d", &dummy);
576 if (Geoid->UseGeoid == 1) {
577 printf(
"Please Enter Minimum Height above MSL (in km):\n");
578 if (NULL == fgets(buffer, 20, stdin)) {
579 minimum->HeightAboveGeoid = 0;
580 printf(
"Unrecognized input default %lf used\n",
581 minimum->HeightAboveGeoid);
583 sscanf(buffer,
"%lf", &minimum->HeightAboveGeoid);
586 printf(
"Please Enter Maximum Height above MSL (in km):\n");
587 if (NULL == fgets(buffer, 20, stdin)) {
588 maximum->HeightAboveGeoid = 0;
589 printf(
"Unrecognized input default %lf used\n",
590 maximum->HeightAboveGeoid);
592 sscanf(buffer,
"%lf", &maximum->HeightAboveGeoid);
597 printf(
"Please Enter Minimum Height above the WGS-84 Ellipsoid (in km):\n");
598 if (NULL == fgets(buffer, 20, stdin)) {
599 minimum->HeightAboveGeoid = 0;
603 sscanf(buffer,
"%lf", &minimum->HeightAboveGeoid);
605 minimum->HeightAboveEllipsoid = minimum->HeightAboveGeoid;
607 printf(
"Please Enter Maximum Height above the WGS-84 Ellipsoid (in km):\n");
608 if (NULL == fgets(buffer, 20, stdin)) {
609 maximum->HeightAboveGeoid = 0;
613 sscanf(buffer,
"%lf", &maximum->HeightAboveGeoid);
615 maximum->HeightAboveEllipsoid = maximum->HeightAboveGeoid;
618 printf(
"Please Enter height step size (in km):\n");
619 if (NULL == fgets(buffer, 20, stdin)) {
620 *a_step_size = maximum->HeightAboveGeoid - minimum->HeightAboveGeoid;
621 printf(
"Unrecognized input default %lf used\n", *a_step_size);
623 sscanf(buffer,
"%lf", a_step_size);
626 printf(
"\nPlease Enter the decimal year starting time:\n");
627 while (NULL == fgets(buffer, 20, stdin)) {
628 printf(
"\nUnrecognized input, please re-enter a decimal year\n");
630 sscanf(buffer,
"%lf", &StartDate->DecimalYear);
632 printf(
"Please Enter the decimal year ending time:\n");
633 while (NULL == fgets(buffer, 20, stdin)) {
634 printf(
"\nUnrecognized input, please re-enter a decimal year\n");
636 sscanf(buffer,
"%lf", &EndDate->DecimalYear);
638 printf(
"Please Enter the time step size:\n");
639 if (NULL == fgets(buffer, 20, stdin)) {
640 *step_time = EndDate->DecimalYear - StartDate->DecimalYear;
641 printf(
"Unrecognized input, default of %lf used\n", *step_time);
643 sscanf(buffer,
"%lf", step_time);
646 printf(
"Enter a geomagnetic element to print. Your options are:\n");
648 " 1. Declination 9. Ddot\n 2. Inclination 10. Idot\n 3. F 11. "
649 "Fdot\n 4. H 12. Hdot\n 5. X 13. Xdot\n 6. Y 14. Ydot\n 7. Z "
650 " 15. Zdot\n 8. GV 16. GVdot\nFor gradients enter: 17\n");
651 if (NULL == fgets(buffer, 20, stdin)) {
653 printf(
"Unrecognized input, default of %d used\n", *ElementOption);
655 sscanf(buffer,
"%d", ElementOption);
657 if (*ElementOption == 17) {
658 printf(
"Enter a gradient element to print. Your options are:\n");
659 printf(
" 1. dX/dphi \t2. dY/dphi \t3. dZ/dphi\n");
660 printf(
" 4. dX/dlambda \t5. dY/dlambda \t6. dZ/dlambda\n");
661 printf(
" 7. dX/dz \t8. dY/dz \t9. dZ/dz\n");
663 if (NULL == fgets(buffer, 20, stdin)) {
665 printf(
"Unrecognized input, default of %d used\n", *ElementOption);
667 sscanf(buffer,
"%d", ElementOption);
670 *ElementOption += 16;
672 printf(
"Select output :\n");
673 printf(
" 1. Print to a file \n 2. Print to Screen\n");
674 if (NULL == fgets(buffer, 20, stdin)) {
676 printf(
"Unrecognized input, default of printing to screen\n");
678 sscanf(buffer,
"%d", PrintOption);
681 fileout = fopen(filename,
"a");
682 if (*PrintOption == 1) {
684 "Please enter output filename\nfor default ('GridResults.txt') press "
686 if (NULL == fgets(buffer, 20, stdin) || strlen(buffer) <= 1) {
687 strcpy(OutputFile,
"GridResults.txt");
688 fprintf(fileout,
"\nResults printed in: GridResults.txt\n");
689 strcpy(OutputFile,
"GridResults.txt");
691 sscanf(buffer,
"%s", OutputFile);
692 fprintf(fileout,
"\nResults printed in: %s\n", OutputFile);
698 fprintf(fileout,
"\nResults printed in Console\n");
701 "Minimum Latitude: %f\t\tMaximum Latitude: %f\t\tStep Size: %f\nMinimum "
702 "Longitude: %f\t\tMaximum Longitude: %f\t\tStep Size: %f\n",
703 minimum->phi, maximum->phi, *step_size, minimum->lambda, maximum->lambda,
705 if (Geoid->UseGeoid == 1)
707 "Minimum Altitude above MSL: %f\tMaximum Altitude above MSL: "
708 "%f\tStep Size: %f\n",
709 minimum->HeightAboveGeoid, maximum->HeightAboveGeoid, *a_step_size);
712 "Minimum Altitude above WGS-84 Ellipsoid: %f\tMaximum Altitude "
713 "above WGS-84 Ellipsoid: %f\tStep Size: %f\n",
714 minimum->HeightAboveEllipsoid, maximum->HeightAboveEllipsoid,
717 "Starting Date: %f\t\tEnding Date: %f\t\tStep Time: %f\n\n\n",
718 StartDate->DecimalYear, EndDate->DecimalYear, *step_time);
758 char Error_Message[255];
760 int i, j, a, b, c, done = 0;
761 double lat_bound[2] = {LAT_BOUND_MIN, LAT_BOUND_MAX};
762 double lon_bound[2] = {LON_BOUND_MIN, LON_BOUND_MAX};
763 int alt_bound[2] = {ALT_BOUND_MIN, NO_ALT_MAX};
764 char *Qstring = malloc(
sizeof(
char) * 1028);
767 "\nPlease enter latitude\nNorth latitude positive, For example:\n30, "
768 "30, 30 (D,M,S) or 30.508 (Decimal Degrees) (both are north)\n");
769 MAG_GetDeg(Qstring, &CoordGeodetic->phi, lat_bound);
772 "\nPlease enter longitude\nEast longitude positive, West negative. "
773 "For example:\n-100.5 or -100, 30, 0 for 100.5 degrees west\n");
774 MAG_GetDeg(Qstring, &CoordGeodetic->lambda, lon_bound);
777 "\nPlease enter height above mean sea level (in kilometers):\n[For "
778 "height above WGS-84 ellipsoid prefix E, for example (E20.1)]\n");
779 if (MAG_GetAltitude(Qstring, Geoid, CoordGeodetic, alt_bound, FALSE) ==
784 "\nPlease enter the decimal year or calendar date\n (YYYY.yyy, MM DD "
785 "YYYY or MM/DD/YYYY):\n");
786 while (NULL == fgets(buffer, 40, stdin)) {
788 "\nPlease enter the decimal year or calendar date\n (YYYY.yyy, MM DD "
789 "YYYY or MM/DD/YYYY):\n");
791 for (i = 0, done = 0; i <= 40 && !done; i++) {
792 if (buffer[i] ==
'.') {
793 j = sscanf(buffer,
"%lf", &MagneticDate->DecimalYear);
799 if (buffer[i] ==
'/') {
800 sscanf(buffer,
"%d/%d/%d", &MagneticDate->Month, &MagneticDate->Day,
801 &MagneticDate->Year);
802 if (!MAG_DateToYear(MagneticDate, Error_Message)) {
803 printf(
"%s", Error_Message);
805 "\nPlease re-enter Date in MM/DD/YYYY or MM DD YYYY format, or as "
807 while (NULL == fgets(buffer, 40, stdin)) {
809 "\nPlease re-enter Date in MM/DD/YYYY or MM DD YYYY format, or "
810 "as a decimal year\n");
816 if ((buffer[i] ==
' ' && buffer[i + 1] !=
'/') || buffer[i] ==
'\0') {
817 if (3 == sscanf(buffer,
"%d %d %d", &a, &b, &c)) {
818 MagneticDate->Month = a;
819 MagneticDate->Day = b;
820 MagneticDate->Year = c;
821 MagneticDate->DecimalYear = 99999;
822 }
else if (1 == sscanf(buffer,
"%d %d %d", &a, &b, &c)) {
823 MagneticDate->DecimalYear = a;
826 if (!(MagneticDate->DecimalYear == a)) {
827 if (!MAG_DateToYear(MagneticDate, Error_Message)) {
828 printf(
"%s", Error_Message);
831 "\nError encountered, please re-enter Date in MM/DD/YYYY or MM "
832 "DD YYYY format, or as a decimal year\n");
833 while (NULL == fgets(buffer, 40, stdin)) {
835 "\nError encountered, please re-enter Date in MM/DD/YYYY or MM "
836 "DD YYYY format, or as a decimal year\n");
843 if (buffer[i] ==
'\0' && i != -1 && done != 1) {
846 "\nError encountered, please re-enter as MM/DD/YYYY, MM DD YYYY, or "
848 while (NULL == fgets(buffer, 40, stdin)) {
850 "\nError encountered, please re-enter as MM/DD/YYYY, MM DD YYYY, "
851 "or as YYYY.yyy:\n");
856 if (MagneticDate->DecimalYear > MagneticModel->CoefficientFileEndDate ||
857 MagneticDate->DecimalYear < MagneticModel->epoch) {
858 switch (MAG_Warnings(4, MagneticDate->DecimalYear, MagneticModel)) {
866 "\nPlease enter the decimal year or calendar date\n (YYYY.yyy, "
867 "MM DD YYYY or MM/DD/YYYY):\n");
868 while (NULL == fgets(buffer, 40, stdin)) {
870 "\nPlease enter the decimal year or calendar date\n "
871 "(YYYY.yyy, MM DD YYYY or MM/DD/YYYY):\n");
887 printf(
"\nGradient\n");
888 printf(
"\n Northward Eastward Downward\n");
889 printf(
"X: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
890 Gradient.GradPhi.X, Gradient.GradLambda.X, Gradient.GradZ.X);
891 printf(
"Y: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
892 Gradient.GradPhi.Y, Gradient.GradLambda.Y, Gradient.GradZ.Y);
893 printf(
"Z: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
894 Gradient.GradPhi.Z, Gradient.GradLambda.Z, Gradient.GradZ.Z);
895 printf(
"H: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
896 Gradient.GradPhi.H, Gradient.GradLambda.H, Gradient.GradZ.H);
897 printf(
"F: %7.1f nT/km %9.1f nT/km %9.1f nT/km \n",
898 Gradient.GradPhi.F, Gradient.GradLambda.F, Gradient.GradZ.F);
899 printf(
"Declination: %7.2f min/km %8.2f min/km %8.2f min/km \n",
900 Gradient.GradPhi.Decl * 60, Gradient.GradLambda.Decl * 60,
901 Gradient.GradZ.Decl * 60);
902 printf(
"Inclination: %7.2f min/km %8.2f min/km %8.2f min/km \n",
903 Gradient.GradPhi.Incl * 60, Gradient.GradLambda.Incl * 60,
904 Gradient.GradZ.Incl * 60);
946 char DeclString[100];
947 char InclString[100];
948 MAG_DegreeToDMSstring(GeomagElements.Incl, 2, InclString);
949 if (GeomagElements.H < 6000 && GeomagElements.H > 2000)
950 MAG_Warnings(1, GeomagElements.H, MagneticModel);
951 if (GeomagElements.H < 2000) MAG_Warnings(2, GeomagElements.H, MagneticModel);
952 if (MagneticModel->SecularVariationUsed == TRUE) {
953 MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
954 printf(
"\n Results For \n\n");
955 if (SpaceInput.phi < 0)
956 printf(
"Latitude %.2fS\n", -SpaceInput.phi);
958 printf(
"Latitude %.2fN\n", SpaceInput.phi);
959 if (SpaceInput.lambda < 0)
960 printf(
"Longitude %.2fW\n", -SpaceInput.lambda);
962 printf(
"Longitude %.2fE\n", SpaceInput.lambda);
963 if (Geoid->UseGeoid == 1)
964 printf(
"Altitude: %.2f Kilometers above mean sea level\n",
965 SpaceInput.HeightAboveGeoid);
967 printf(
"Altitude: %.2f Kilometers above the WGS-84 ellipsoid\n",
968 SpaceInput.HeightAboveEllipsoid);
969 printf(
"Date: %.1f\n", TimeInput.DecimalYear);
970 printf(
"\n Main Field\t\t\tSecular Change\n");
971 printf(
"F = %-9.1f nT\t\t Fdot = %.1f\tnT/yr\n", GeomagElements.F,
972 GeomagElements.Fdot);
973 printf(
"H = %-9.1f nT\t\t Hdot = %.1f\tnT/yr\n", GeomagElements.H,
974 GeomagElements.Hdot);
975 printf(
"X = %-9.1f nT\t\t Xdot = %.1f\tnT/yr\n", GeomagElements.X,
976 GeomagElements.Xdot);
977 printf(
"Y = %-9.1f nT\t\t Ydot = %.1f\tnT/yr\n", GeomagElements.Y,
978 GeomagElements.Ydot);
979 printf(
"Z = %-9.1f nT\t\t Zdot = %.1f\tnT/yr\n", GeomagElements.Z,
980 GeomagElements.Zdot);
981 if (GeomagElements.Decl < 0)
982 printf(
"Decl =%20s (WEST)\t Ddot = %.1f\tMin/yr\n", DeclString,
983 60 * GeomagElements.Decldot);
985 printf(
"Decl =%20s (EAST)\t Ddot = %.1f\tMin/yr\n", DeclString,
986 60 * GeomagElements.Decldot);
987 if (GeomagElements.Incl < 0)
988 printf(
"Incl =%20s (UP)\t Idot = %.1f\tMin/yr\n", InclString,
989 60 * GeomagElements.Incldot);
991 printf(
"Incl =%20s (DOWN)\t Idot = %.1f\tMin/yr\n", InclString,
992 60 * GeomagElements.Incldot);
994 MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
995 printf(
"\n Results For \n\n");
996 if (SpaceInput.phi < 0)
997 printf(
"Latitude %.2fS\n", -SpaceInput.phi);
999 printf(
"Latitude %.2fN\n", SpaceInput.phi);
1000 if (SpaceInput.lambda < 0)
1001 printf(
"Longitude %.2fW\n", -SpaceInput.lambda);
1003 printf(
"Longitude %.2fE\n", SpaceInput.lambda);
1004 if (Geoid->UseGeoid == 1)
1005 printf(
"Altitude: %.2f Kilometers above MSL\n",
1006 SpaceInput.HeightAboveGeoid);
1008 printf(
"Altitude: %.2f Kilometers above WGS-84 Ellipsoid\n",
1009 SpaceInput.HeightAboveEllipsoid);
1010 printf(
"Date: %.1f\n", TimeInput.DecimalYear);
1011 printf(
"\n Main Field\n");
1012 printf(
"F = %-9.1f nT\n", GeomagElements.F);
1013 printf(
"H = %-9.1f nT\n", GeomagElements.H);
1014 printf(
"X = %-9.1f nT\n", GeomagElements.X);
1015 printf(
"Y = %-9.1f nT\n", GeomagElements.Y);
1016 printf(
"Z = %-9.1f nT\n", GeomagElements.Z);
1017 if (GeomagElements.Decl < 0)
1018 printf(
"Decl =%20s (WEST)\n", DeclString);
1020 printf(
"Decl =%20s (EAST)\n", DeclString);
1021 if (GeomagElements.Incl < 0)
1022 printf(
"Incl =%20s (UP)\n", InclString);
1024 printf(
"Incl =%20s (DOWN)\n", InclString);
1027 if (SpaceInput.phi <= -55 || SpaceInput.phi >= 55)
1030 MAG_DegreeToDMSstring(GeomagElements.GV, 2, InclString);
1031 printf(
"\n\n Grid variation =%20s\n", InclString);
1036int MAG_ValidateDMSstring(
char *input,
int min,
int max,
char *Error)
1047 int degree, minute, second, j = 0, n, max_minute = 60, max_second = 60;
1052 n = (int)strlen(input);
1054 for (i = 0; i <= n - 1; i++)
1056 if ((input[i] <
'0' || input[i] >
'9') &&
1057 (input[i] !=
',' && input[i] !=
' ' && input[i] !=
'-' &&
1058 input[i] !=
'\0' && input[i] !=
'\n')) {
1060 "\nError: Input contains an illegal character, legal characters "
1061 "for Degree, Minute, Second format are:\n '0-9' ',' '-' '[space]' "
1065 if (input[i] ==
',') j++;
1068 j = sscanf(input,
"%d, %d, %d", °ree, &minute,
1071 j = sscanf(input,
"%d %d %d", °ree, &minute, &second);
1079 "\nError: Not enough numbers used for Degrees, Minutes, Seconds "
1080 "format\n or they were incorrectly formatted\n The legal format is "
1081 "DD,MM,SS or DD MM SS\n");
1084 if (degree > max || degree < min) {
1086 "\nError: Degree input is outside legal range\n The legal range is "
1091 if (degree == max || degree == min) max_minute = 0;
1092 if (minute > max_minute || minute < 0) {
1094 "\nError: Minute input is outside legal range\n The legal minute "
1095 "range is from 0 to 60\n");
1098 if (minute == max_minute) max_second = 0;
1099 if (second > max_second || second < 0) {
1101 "\nError: Second input is outside legal range\n The legal second "
1102 "range is from 0 to 60\n");
1108int MAG_Warnings(
int control,
double value,
1132 "\nCaution: location is approaching the blackout zone around the "
1133 "magnetic pole as\n");
1134 printf(
" defined by the WMM military specification \n");
1137 "(https://www.ngdc.noaa.gov/geomag/WMM/data/MIL-PRF-89500B.pdf). "
1139 printf(
" accuracy may be degraded in this region.\n");
1140 printf(
"Press enter to continue...\n");
1141 }
while (NULL == fgets(ans, 20, stdin));
1146 "\nWarning: location is in the blackout zone around the magnetic "
1147 "pole as defined\n");
1148 printf(
" by the WMM military specification \n");
1151 "(https://www.ngdc.noaa.gov/geomag/WMM/data/MIL-PRF-89500B.pdf). "
1153 printf(
" accuracy is highly degraded in this region.\n");
1154 }
while (NULL == fgets(ans, 20, stdin));
1158 "\nWarning: The value you have entered of %.1f km for the elevation "
1159 "is outside of the recommended range.\n Elevations above -10.0 km "
1160 "are recommended for accurate results. \n",
1164 "\nPlease press 'C' to continue, 'G' to get new data or 'X' to "
1166 while (NULL == fgets(ans, 20, stdin)) {
1167 printf(
"\nInvalid input\n");
1180 printf(
"\nInvalid input %c\n", ans[0]);
1188 "\nWARNING - TIME EXTENDS BEYOND INTENDED USAGE RANGE\n CONTACT NCEI "
1189 "FOR PRODUCT UPDATES:\n");
1190 printf(
" National Centers for Environmental Information\n");
1191 printf(
" NOAA E/NE42\n");
1192 printf(
" 325 Broadway\n");
1193 printf(
"\n Boulder, CO 80305 USA");
1194 printf(
" Attn: Manoj Nair or Arnaud Chulliat\n");
1195 printf(
" Phone: (303) 497-4642 or -6522\n");
1196 printf(
" Email: geomag.models@noaa.gov\n");
1197 printf(
" Web: http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml\n");
1198 printf(
"\n VALID RANGE = %d - %d\n", (
int)MagneticModel->epoch,
1199 (
int)MagneticModel->CoefficientFileEndDate);
1200 printf(
" TIME = %f\n", value);
1203 "\nPlease press 'C' to continue, 'N' to enter new data or 'X' to "
1205 while (NULL == fgets(ans, 20, stdin)) {
1206 printf(
"\nInvalid input\n");
1219 printf(
"\nInvalid input %c\n", ans[0]);
1226 "\nError: The value you have entered of %f km for the elevation is "
1227 "outside of the recommended range.\n Elevations above -10.0 km are "
1228 "recommended for accurate results. \n",
1232 "\nPlease press 'C' to continue, 'G' to get new data or 'X' to "
1234 while (NULL == fgets(ans, 20, stdin)) {
1235 printf(
"\nInvalid input\n");
1248 printf(
"\nInvalid input %c\n", ans[0]);
1290 if (!LegendreFunction) {
1294 LegendreFunction->Pcup = (
double *)malloc((NumTerms + 1) *
sizeof(double));
1295 if (LegendreFunction->Pcup == 0) {
1299 LegendreFunction->dPcup = (
double *)malloc((NumTerms + 1) *
sizeof(double));
1300 if (LegendreFunction->dPcup == 0) {
1304 return LegendreFunction;
1337 if (MagneticModel == NULL) {
1342 MagneticModel->Main_Field_Coeff_G =
1343 (
double *)malloc((NumTerms + 1) *
sizeof(double));
1345 if (MagneticModel->Main_Field_Coeff_G == NULL) {
1350 MagneticModel->Main_Field_Coeff_H =
1351 (
double *)malloc((NumTerms + 1) *
sizeof(double));
1353 if (MagneticModel->Main_Field_Coeff_H == NULL) {
1357 MagneticModel->Secular_Var_Coeff_G =
1358 (
double *)malloc((NumTerms + 1) *
sizeof(double));
1359 if (MagneticModel->Secular_Var_Coeff_G == NULL) {
1363 MagneticModel->Secular_Var_Coeff_H =
1364 (
double *)malloc((NumTerms + 1) *
sizeof(double));
1365 if (MagneticModel->Secular_Var_Coeff_H == NULL) {
1369 MagneticModel->CoefficientFileEndDate = 0;
1370 MagneticModel->EditionDate = 0;
1371 strcpy(MagneticModel->ModelName,
"");
1372 MagneticModel->SecularVariationUsed = 0;
1373 MagneticModel->epoch = 0;
1374 MagneticModel->nMax = 0;
1375 MagneticModel->nMaxSecVar = 0;
1377 for (i = 0; i < NumTerms; i++) {
1378 MagneticModel->Main_Field_Coeff_G[i] = 0;
1379 MagneticModel->Main_Field_Coeff_H[i] = 0;
1380 MagneticModel->Secular_Var_Coeff_G[i] = 0;
1381 MagneticModel->Secular_Var_Coeff_H[i] = 0;
1384 return MagneticModel;
1392 SphVariables->RelativeRadiusPower =
1393 (
double *)malloc((nMax + 1) *
sizeof(double));
1394 SphVariables->cos_mlambda = (
double *)malloc((nMax + 1) *
sizeof(double));
1395 SphVariables->sin_mlambda = (
double *)malloc((nMax + 1) *
sizeof(double));
1396 return SphVariables;
1400 char values[][MAXLINELENGTH]) {
1402 strcpy(model->ModelName, values[MODELNAME]);
1410 model->epoch = atof(values[MODELSTARTYEAR]);
1411 model->nMax = atoi(values[INTSTATICDEG]);
1412 model->nMaxSecVar = atoi(values[INTSECVARDEG]);
1413 model->CoefficientFileEndDate = atof(values[MODELENDYEAR]);
1414 if (model->nMaxSecVar > 0)
1415 model->SecularVariationUsed = 1;
1417 model->SecularVariationUsed = 0;
1427 assert(nMax <= Source->nMax);
1428 assert(nMax <= Assignee->nMax);
1429 assert(nMaxSecVar <= Source->nMaxSecVar);
1430 assert(nMaxSecVar <= Assignee->nMaxSecVar);
1431 for (n = 1; n <= nMaxSecVar; n++) {
1432 for (m = 0; m <= n; m++) {
1433 index = (n * (n + 1) / 2 + m);
1434 Assignee->Main_Field_Coeff_G[index] = Source->Main_Field_Coeff_G[index];
1435 Assignee->Main_Field_Coeff_H[index] = Source->Main_Field_Coeff_H[index];
1436 Assignee->Secular_Var_Coeff_G[index] = Source->Secular_Var_Coeff_G[index];
1437 Assignee->Secular_Var_Coeff_H[index] = Source->Secular_Var_Coeff_H[index];
1440 for (n = nMaxSecVar + 1; n <= nMax; n++) {
1441 for (m = 0; m <= n; m++) {
1442 index = (n * (n + 1) / 2 + m);
1443 Assignee->Main_Field_Coeff_G[index] = Source->Main_Field_Coeff_G[index];
1444 Assignee->Main_Field_Coeff_H[index] = Source->Main_Field_Coeff_H[index];
1479 if (MagneticModel->Main_Field_Coeff_G) {
1480 free(MagneticModel->Main_Field_Coeff_G);
1481 MagneticModel->Main_Field_Coeff_G = NULL;
1483 if (MagneticModel->Main_Field_Coeff_H) {
1484 free(MagneticModel->Main_Field_Coeff_H);
1485 MagneticModel->Main_Field_Coeff_H = NULL;
1487 if (MagneticModel->Secular_Var_Coeff_G) {
1488 free(MagneticModel->Secular_Var_Coeff_G);
1489 MagneticModel->Secular_Var_Coeff_G = NULL;
1491 if (MagneticModel->Secular_Var_Coeff_H) {
1492 free(MagneticModel->Secular_Var_Coeff_H);
1493 MagneticModel->Secular_Var_Coeff_H = NULL;
1495 if (MagneticModel) {
1496 free(MagneticModel);
1497 MagneticModel = NULL;
1500 if (TimedMagneticModel->Main_Field_Coeff_G) {
1501 free(TimedMagneticModel->Main_Field_Coeff_G);
1502 TimedMagneticModel->Main_Field_Coeff_G = NULL;
1504 if (TimedMagneticModel->Main_Field_Coeff_H) {
1505 free(TimedMagneticModel->Main_Field_Coeff_H);
1506 TimedMagneticModel->Main_Field_Coeff_H = NULL;
1508 if (TimedMagneticModel->Secular_Var_Coeff_G) {
1509 free(TimedMagneticModel->Secular_Var_Coeff_G);
1510 TimedMagneticModel->Secular_Var_Coeff_G = NULL;
1512 if (TimedMagneticModel->Secular_Var_Coeff_H) {
1513 free(TimedMagneticModel->Secular_Var_Coeff_H);
1514 TimedMagneticModel->Secular_Var_Coeff_H = NULL;
1517 if (TimedMagneticModel) {
1518 free(TimedMagneticModel);
1519 TimedMagneticModel = NULL;
1522 if (LegendreFunction->Pcup) {
1523 free(LegendreFunction->Pcup);
1524 LegendreFunction->Pcup = NULL;
1526 if (LegendreFunction->dPcup) {
1527 free(LegendreFunction->dPcup);
1528 LegendreFunction->dPcup = NULL;
1530 if (LegendreFunction) {
1531 free(LegendreFunction);
1532 LegendreFunction = NULL;
1559 if (MagneticModel->Main_Field_Coeff_G) {
1560 free(MagneticModel->Main_Field_Coeff_G);
1561 MagneticModel->Main_Field_Coeff_G = NULL;
1563 if (MagneticModel->Main_Field_Coeff_H) {
1564 free(MagneticModel->Main_Field_Coeff_H);
1565 MagneticModel->Main_Field_Coeff_H = NULL;
1567 if (MagneticModel->Secular_Var_Coeff_G) {
1568 free(MagneticModel->Secular_Var_Coeff_G);
1569 MagneticModel->Secular_Var_Coeff_G = NULL;
1571 if (MagneticModel->Secular_Var_Coeff_H) {
1572 free(MagneticModel->Secular_Var_Coeff_H);
1573 MagneticModel->Secular_Var_Coeff_H = NULL;
1575 if (MagneticModel) {
1576 free(MagneticModel);
1577 MagneticModel = NULL;
1596 if (LegendreFunction->Pcup) {
1597 free(LegendreFunction->Pcup);
1598 LegendreFunction->Pcup = NULL;
1600 if (LegendreFunction->dPcup) {
1601 free(LegendreFunction->dPcup);
1602 LegendreFunction->dPcup = NULL;
1604 if (LegendreFunction) {
1605 free(LegendreFunction);
1606 LegendreFunction = NULL;
1623 if (SphVar->RelativeRadiusPower) {
1624 free(SphVar->RelativeRadiusPower);
1625 SphVar->RelativeRadiusPower = NULL;
1627 if (SphVar->cos_mlambda) {
1628 free(SphVar->cos_mlambda);
1629 SphVar->cos_mlambda = NULL;
1631 if (SphVar->sin_mlambda) {
1632 free(SphVar->sin_mlambda);
1633 SphVar->sin_mlambda = NULL;
1648 char Datestring[11];
1650 Date.DecimalYear = MagneticModel->EditionDate;
1651 MAG_YearToDate(&Date);
1652 sprintf(Datestring,
"%d/%d/%d", Date.Month, Date.Day, Date.Year);
1653 OUT = fopen(filename,
"w");
1654 fprintf(OUT,
" %.1f %s %s\n",
1655 MagneticModel->epoch, MagneticModel->ModelName, Datestring);
1656 for (n = 1; n <= MagneticModel->nMax; n++) {
1657 for (m = 0; m <= n; m++) {
1658 index = (n * (n + 1) / 2 + m);
1660 fprintf(OUT,
" %2d %2d %9.4f %9.4f %9.4f %9.4f\n", n, m,
1661 MagneticModel->Main_Field_Coeff_G[index],
1662 MagneticModel->Main_Field_Coeff_H[index],
1663 MagneticModel->Secular_Var_Coeff_G[index],
1664 MagneticModel->Secular_Var_Coeff_H[index]);
1666 fprintf(OUT,
" %2d %2d %9.4f %9.4f %9.4f %9.4f\n", n, m,
1667 MagneticModel->Main_Field_Coeff_G[index], 0.0,
1668 MagneticModel->Secular_Var_Coeff_G[index], 0.0);
1677void MAG_PrintEMMFormat(
char *filename,
char *filenameSV,
1682 char Datestring[11];
1684 Date.DecimalYear = MagneticModel->EditionDate;
1685 MAG_YearToDate(&Date);
1686 sprintf(Datestring,
"%d/%d/%d", Date.Month, Date.Day, Date.Year);
1687 OUT = fopen(filename,
"w");
1688 fprintf(OUT,
" %.1f %s %s\n",
1689 MagneticModel->epoch, MagneticModel->ModelName, Datestring);
1690 for (n = 1; n <= MagneticModel->nMax; n++) {
1691 for (m = 0; m <= n; m++) {
1692 index = (n * (n + 1) / 2 + m);
1694 fprintf(OUT,
" %2d %2d %9.4f %9.4f\n", n, m,
1695 MagneticModel->Main_Field_Coeff_G[index],
1696 MagneticModel->Main_Field_Coeff_H[index]);
1698 fprintf(OUT,
" %2d %2d %9.4f %9.4f\n", n, m,
1699 MagneticModel->Main_Field_Coeff_G[index], 0.0);
1703 OUT = fopen(filenameSV,
"w");
1704 for (n = 1; n <= MagneticModel->nMaxSecVar; n++) {
1705 for (m = 0; m <= n; m++) {
1706 index = (n * (n + 1) / 2 + m);
1708 fprintf(OUT,
" %2d %2d %9.4f %9.4f\n", n, m,
1709 MagneticModel->Secular_Var_Coeff_G[index],
1710 MagneticModel->Secular_Var_Coeff_H[index]);
1712 fprintf(OUT,
" %2d %2d %9.4f %9.4f\n", n, m,
1713 MagneticModel->Secular_Var_Coeff_G[index], 0.0);
1723void MAG_PrintSHDFFormat(
char *filename,
1726 int i, n, m, index, epochRange;
1728 SHDF_file = fopen(filename,
"w");
1730 for (i = 0; i < epochs; i++) {
1732 epochRange = (*MagneticModel)[i + 1]->epoch - (*MagneticModel)[i]->epoch;
1734 epochRange = (*MagneticModel)[i]->epoch - (*MagneticModel)[i - 1]->epoch;
1736 "%%SHDF 16695 Definitive Geomagnetic Reference Field Model "
1737 "Coefficient File\n");
1738 fprintf(SHDF_file,
"%%ModelName: %s\n", (*MagneticModel)[i]->ModelName);
1740 "%%Publisher: International Association of Geomagnetism and "
1741 "Aeronomy (IAGA), Working Group V-Mod\n");
1742 fprintf(SHDF_file,
"%%ReleaseDate: Some Number\n");
1743 fprintf(SHDF_file,
"%%DataCutOFF: Some Other Number\n");
1744 fprintf(SHDF_file,
"%%ModelStartYear: %d\n",
1745 (
int)(*MagneticModel)[i]->epoch);
1746 fprintf(SHDF_file,
"%%ModelEndYear: %d\n",
1747 (
int)(*MagneticModel)[i]->epoch + epochRange);
1748 fprintf(SHDF_file,
"%%Epoch: %.0f\n", (*MagneticModel)[i]->epoch);
1749 fprintf(SHDF_file,
"%%IntStaticDeg: %d\n", (*MagneticModel)[i]->nMax);
1750 fprintf(SHDF_file,
"%%IntSecVarDeg: %d\n", (*MagneticModel)[i]->nMaxSecVar);
1751 fprintf(SHDF_file,
"%%ExtStaticDeg: 0\n");
1752 fprintf(SHDF_file,
"%%ExtSecVarDeg: 0\n");
1753 fprintf(SHDF_file,
"%%Normalization: Schmidt semi-normailized\n");
1754 fprintf(SHDF_file,
"%%SpatBasFunc: spherical harmonics\n");
1755 fprintf(SHDF_file,
"# To synthesize the field for a given date:\n");
1757 "# Use the sub-model of the epoch corresponding to each date\n");
1759 "#\n#\n#\n#\n# I/E, n, m, Gnm, Hnm, SV-Gnm, SV-Hnm\n#\n");
1762 for (n = 1; n <= (*MagneticModel)[i]->nMax; n++) {
1763 for (m = 0; m <= n; m++) {
1764 index = (n * (n + 1)) / 2 + m;
1765 if (i < epochs - 1) {
1767 fprintf(SHDF_file,
"I,%d,%d,%f,%f,%f,%f\n", n, m,
1768 (*MagneticModel)[i]->Main_Field_Coeff_G[index],
1769 (*MagneticModel)[i]->Main_Field_Coeff_H[index],
1770 (*MagneticModel)[i]->Secular_Var_Coeff_G[index],
1771 (*MagneticModel)[i]->Secular_Var_Coeff_H[index]);
1773 fprintf(SHDF_file,
"I,%d,%d,%f,,%f,\n", n, m,
1774 (*MagneticModel)[i]->Main_Field_Coeff_G[index],
1775 (*MagneticModel)[i]->Secular_Var_Coeff_G[index]);
1778 fprintf(SHDF_file,
"I,%d,%d,%f,%f,%f,%f\n", n, m,
1779 (*MagneticModel)[i]->Main_Field_Coeff_G[index],
1780 (*MagneticModel)[i]->Main_Field_Coeff_H[index],
1781 (*MagneticModel)[i]->Secular_Var_Coeff_G[index],
1782 (*MagneticModel)[i]->Secular_Var_Coeff_H[index]);
1784 fprintf(SHDF_file,
"I,%d,%d,%f,,%f,\n", n, m,
1785 (*MagneticModel)[i]->Main_Field_Coeff_G[index],
1786 (*MagneticModel)[i]->Secular_Var_Coeff_G[index]);
1795int MAG_readMagneticModel(
char *filename,
1815 int i, icomp, m, n, EOF_Flag = 0, index;
1816 double epoch, gnm, hnm, dgnm, dhnm;
1817 MAG_COF_File = fopen(filename,
"r");
1819 if (MAG_COF_File == NULL) {
1824 MagneticModel->Main_Field_Coeff_H[0] = 0.0;
1825 MagneticModel->Main_Field_Coeff_G[0] = 0.0;
1826 MagneticModel->Secular_Var_Coeff_H[0] = 0.0;
1827 MagneticModel->Secular_Var_Coeff_G[0] = 0.0;
1828 fgets(c_str, 80, MAG_COF_File);
1829 sscanf(c_str,
"%lf%s", &epoch, MagneticModel->ModelName);
1830 MagneticModel->epoch = epoch;
1831 while (EOF_Flag == 0) {
1832 if (NULL == fgets(c_str, 80, MAG_COF_File)) {
1836 for (i = 0; i < 4 && (c_str[i] !=
'\0'); i++) {
1837 c_new[i] = c_str[i];
1838 c_new[i + 1] =
'\0';
1840 icomp = strcmp(
"9999", c_new);
1846 sscanf(c_str,
"%d%d%lf%lf%lf%lf", &n, &m, &gnm, &hnm, &dgnm, &dhnm);
1848 index = (n * (n + 1) / 2 + m);
1849 MagneticModel->Main_Field_Coeff_G[index] = gnm;
1850 MagneticModel->Secular_Var_Coeff_G[index] = dgnm;
1851 MagneticModel->Main_Field_Coeff_H[index] = hnm;
1852 MagneticModel->Secular_Var_Coeff_H[index] = dhnm;
1856 fclose(MAG_COF_File);
1860int MAG_readMagneticModel_Large(
char *filename,
char *filenameSV,
1882 FILE *MAG_COFSV_File;
1883 char c_str[81], c_str2[81];
1885 int i, m, n, index, a, b;
1886 double epoch, gnm, hnm, dgnm, dhnm;
1887 MAG_COF_File = fopen(filename,
"r");
1888 MAG_COFSV_File = fopen(filenameSV,
"r");
1889 if (MAG_COF_File == NULL || MAG_COFSV_File == NULL) {
1893 MagneticModel->Main_Field_Coeff_H[0] = 0.0;
1894 MagneticModel->Main_Field_Coeff_G[0] = 0.0;
1895 MagneticModel->Secular_Var_Coeff_H[0] = 0.0;
1896 MagneticModel->Secular_Var_Coeff_G[0] = 0.0;
1897 if (NULL == fgets(c_str, 80, MAG_COF_File)) {
1898 fclose(MAG_COF_File);
1899 fclose(MAG_COFSV_File);
1902 sscanf(c_str,
"%lf%s", &epoch, MagneticModel->ModelName);
1903 MagneticModel->epoch = epoch;
1904 a = CALCULATE_NUMTERMS(MagneticModel->nMaxSecVar);
1905 b = CALCULATE_NUMTERMS(MagneticModel->nMax);
1906 for (i = 0; i < a; i++) {
1907 if (NULL == fgets(c_str, 80, MAG_COF_File)) {
1908 fclose(MAG_COF_File);
1909 fclose(MAG_COFSV_File);
1912 sscanf(c_str,
"%d%d%lf%lf", &n, &m, &gnm, &hnm);
1913 if (NULL == fgets(c_str2, 80, MAG_COFSV_File)) {
1914 fclose(MAG_COF_File);
1915 fclose(MAG_COFSV_File);
1918 sscanf(c_str2,
"%d%d%lf%lf", &n, &m, &dgnm, &dhnm);
1920 index = (n * (n + 1) / 2 + m);
1921 MagneticModel->Main_Field_Coeff_G[index] = gnm;
1922 MagneticModel->Secular_Var_Coeff_G[index] = dgnm;
1923 MagneticModel->Main_Field_Coeff_H[index] = hnm;
1924 MagneticModel->Secular_Var_Coeff_H[index] = dhnm;
1927 for (i = a; i < b; i++) {
1928 if (NULL == fgets(c_str, 80, MAG_COF_File)) {
1929 fclose(MAG_COF_File);
1930 fclose(MAG_COFSV_File);
1933 sscanf(c_str,
"%d%d%lf%lf", &n, &m, &gnm, &hnm);
1935 index = (n * (n + 1) / 2 + m);
1936 MagneticModel->Main_Field_Coeff_G[index] = gnm;
1937 MagneticModel->Main_Field_Coeff_H[index] = hnm;
1940 if (MAG_COF_File != NULL && MAG_COFSV_File != NULL) {
1941 fclose(MAG_COF_File);
1942 fclose(MAG_COFSV_File);
1948int MAG_readMagneticModel_SHDF(
char *filename,
1970 char paramkeys[NOOFPARAMS][MAXLINELENGTH] = {
1971 "SHDF ",
"ModelName: ",
"Publisher: ",
"ReleaseDate: ",
1972 "DataCutOff: ",
"ModelStartYear: ",
"ModelEndYear: ",
"Epoch: ",
1973 "IntStaticDeg: ",
"IntSecVarDeg: ",
"ExtStaticDeg: ",
"ExtSecVarDeg: ",
1974 "GeoMagRefRad: ",
"Normalization: ",
"SpatBasFunc: "};
1976 char paramvalues[NOOFPARAMS][MAXLINELENGTH];
1977 char *line = (
char *)malloc(MAXLINELENGTH);
1979 char paramvalue[MAXLINELENGTH];
1980 int paramvaluelength = 0;
1981 int paramkeylength = 0;
1984 int header_index = -1;
1987 int allocationflag = 0;
1993 double gnm, hnm, dgnm, dhnm, cutoff;
1998 stream = fopen(filename, READONLYMODE);
1999 if (stream == NULL) {
2000 perror(
"File open error");
2001 return header_index;
2005 while (fgets(line, MAXLINELENGTH, stream) != NULL) {
2007 if (strlen(MAG_Trim(line)) == 0)
continue;
2011 if (header_index > -1) {
2012 MAG_AssignHeaderValues((*magneticmodels)[header_index], paramvalues);
2015 if (header_index >= array_size) {
2018 "Header limit exceeded - too many models in model file. (%d)\n",
2020 return array_size + 1;
2025 for (i = 0; i < NOOFPARAMS; i++) {
2026 paramkeylength = strlen(paramkeys[i]);
2027 if (!strncmp(line, paramkeys[i], paramkeylength)) {
2028 paramvaluelength = strlen(line) - paramkeylength;
2029 strncpy(paramvalue, line + paramkeylength, paramvaluelength);
2030 paramvalue[paramvaluelength] =
'\0';
2031 strcpy(paramvalues[i], paramvalue);
2032 if (!strcmp(paramkeys[i], paramkeys[INTSTATICDEG]) ||
2033 !strcmp(paramkeys[i], paramkeys[EXTSTATICDEG])) {
2034 tempint = atoi(paramvalues[i]);
2035 if (tempint > 0 && allocationflag == 0) {
2036 numterms = CALCULATE_NUMTERMS(tempint);
2037 (*magneticmodels)[header_index] =
2038 MAG_AllocateModelMemory(numterms);
2047 }
else if (*line ==
'#') {
2050 }
else if (sscanf(line,
"%c,%d,%d", &coefftype, &n, &m) == 3) {
2052 sscanf(line,
"%c,%d,%d,%lf,,%lf,", &coefftype, &n, &m, &gnm, &dgnm);
2056 sscanf(line,
"%c,%d,%d,%lf,%lf,%lf,%lf", &coefftype, &n, &m, &gnm, &hnm,
2059 if (!allocationflag) {
2061 "Degree not found in model. Memory cannot be allocated.\n");
2062 return _DEGREE_NOT_FOUND;
2065 index = (n * (n + 1) / 2 + m);
2066 (*magneticmodels)[header_index]->Main_Field_Coeff_G[index] = gnm;
2067 (*magneticmodels)[header_index]->Secular_Var_Coeff_G[index] = dgnm;
2068 (*magneticmodels)[header_index]->Main_Field_Coeff_H[index] = hnm;
2069 (*magneticmodels)[header_index]->Secular_Var_Coeff_H[index] = dhnm;
2073 if (header_index > -1)
2074 MAG_AssignHeaderValues((*magneticmodels)[header_index], paramvalues);
2077 cutoff = (*magneticmodels)[array_size - 1]->CoefficientFileEndDate;
2079 for (i = 0; i < array_size; i++)
2080 (*magneticmodels)[i]->CoefficientFileEndDate = cutoff;
2085 return header_index + 1;
2088char *MAG_Trim(
char *str) {
2091 while (isspace(*str)) str++;
2093 if (*str == 0)
return str;
2095 end = str + strlen(str) - 1;
2096 while (end > str && isspace(*end)) end--;
2112void MAG_BaseErrors(
double DeclCoef,
double DeclBaseline,
double InclOffset,
2113 double FOffset,
double Multiplier,
double H,
2114 double *DeclErr,
double *InclErr,
double *FErr) {
2115 double declHorizontalAdjustmentSq;
2116 declHorizontalAdjustmentSq = (DeclCoef / H) * (DeclCoef / H);
2117 *DeclErr = sqrt(declHorizontalAdjustmentSq + DeclBaseline * DeclBaseline) *
2119 *InclErr = InclOffset * Multiplier;
2120 *FErr = FOffset * Multiplier;
2123int MAG_CalculateGeoMagneticElements(
2139 GeoMagneticElements->X = MagneticResultsGeo->Bx;
2140 GeoMagneticElements->Y = MagneticResultsGeo->By;
2141 GeoMagneticElements->Z = MagneticResultsGeo->Bz;
2143 GeoMagneticElements->H =
2144 sqrt(MagneticResultsGeo->Bx * MagneticResultsGeo->Bx +
2145 MagneticResultsGeo->By * MagneticResultsGeo->By);
2146 GeoMagneticElements->F =
2147 sqrt(GeoMagneticElements->H * GeoMagneticElements->H +
2148 MagneticResultsGeo->Bz * MagneticResultsGeo->Bz);
2149 GeoMagneticElements->Decl =
2150 RAD2DEG(atan2(GeoMagneticElements->Y, GeoMagneticElements->X));
2151 GeoMagneticElements->Incl =
2152 RAD2DEG(atan2(GeoMagneticElements->Z, GeoMagneticElements->H));
2180 if (location.phi >= MAG_PS_MAX_LAT_DEGREE) {
2181 elements->GV = elements->Decl - location.lambda;
2183 }
else if (location.phi <= MAG_PS_MIN_LAT_DEGREE) {
2184 elements->GV = elements->Decl + location.lambda;
2187 MAG_GetTransverseMercator(location, &UTMParameters);
2188 elements->GV = elements->Decl - UTMParameters.ConvergenceOfMeridians;
2196 GradElements->X = GradResults.Bx;
2197 GradElements->Y = GradResults.By;
2198 GradElements->Z = GradResults.Bz;
2200 GradElements->H = (GradElements->X * MagneticElements.X +
2201 GradElements->Y * MagneticElements.Y) /
2203 GradElements->F = (GradElements->X * MagneticElements.X +
2204 GradElements->Y * MagneticElements.Y +
2205 GradElements->Z * MagneticElements.Z) /
2207 GradElements->Decl = 180.0 / M_PI *
2208 (MagneticElements.X * GradElements->Y -
2209 MagneticElements.Y * GradElements->X) /
2210 (MagneticElements.H * MagneticElements.H);
2211 GradElements->Incl = 180.0 / M_PI *
2212 (MagneticElements.H * GradElements->Z -
2213 MagneticElements.Z * GradElements->H) /
2214 (MagneticElements.F * MagneticElements.F);
2215 GradElements->GV = GradElements->Decl;
2218int MAG_CalculateSecularVariationElements(
2236 MagneticElements->Xdot = MagneticVariation.Bx;
2237 MagneticElements->Ydot = MagneticVariation.By;
2238 MagneticElements->Zdot = MagneticVariation.Bz;
2239 MagneticElements->Hdot =
2240 (MagneticElements->X * MagneticElements->Xdot +
2241 MagneticElements->Y * MagneticElements->Ydot) /
2242 MagneticElements->H;
2243 MagneticElements->Fdot = (MagneticElements->X * MagneticElements->Xdot +
2244 MagneticElements->Y * MagneticElements->Ydot +
2245 MagneticElements->Z * MagneticElements->Zdot) /
2246 MagneticElements->F;
2247 MagneticElements->Decldot = 180.0 / M_PI *
2248 (MagneticElements->X * MagneticElements->Ydot -
2249 MagneticElements->Y * MagneticElements->Xdot) /
2250 (MagneticElements->H * MagneticElements->H);
2251 MagneticElements->Incldot = 180.0 / M_PI *
2252 (MagneticElements->H * MagneticElements->Zdot -
2253 MagneticElements->Z * MagneticElements->Hdot) /
2254 (MagneticElements->F * MagneticElements->F);
2255 MagneticElements->GVdot = MagneticElements->Decldot;
2270 double modified_b, r, e, f, p, q, d, v, g, t, zlong, rlat;
2278 modified_b = -Ellip.b;
2280 modified_b = Ellip.b;
2285 r = sqrt(x * x + y * y);
2286 e = (modified_b * z - (Ellip.a * Ellip.a - modified_b * modified_b)) /
2288 f = (modified_b * z + (Ellip.a * Ellip.a - modified_b * modified_b)) /
2294 p = (4.0 / 3.0) * (e * f + 1.0);
2295 q = 2.0 * (e * e - f * f);
2296 d = p * p * p + q * q;
2299 v = pow((sqrt(d) - q), (1.0 / 3.0)) - pow((sqrt(d) + q), (1.0 / 3.0));
2301 v = 2.0 * sqrt(-p) * cos(acos(q / (p * sqrt(-p))) / 3.0);
2307 if (v * v < fabs(p)) {
2308 v = -(v * v * v + 2.0 * q) / (3.0 * p);
2310 g = (sqrt(e * e + v) + e) / 2.0;
2311 t = sqrt(g * g + (f - v * g) / (2.0 * g - e)) - g;
2313 rlat = atan((Ellip.a * (1.0 - t * t)) / (2.0 * modified_b * t));
2314 CoordGeodetic->phi = RAD2DEG(rlat);
2319 CoordGeodetic->HeightAboveEllipsoid =
2320 (r - Ellip.a * t) * cos(rlat) + (z - modified_b) * sin(rlat);
2324 zlong = atan2(y, x);
2325 if (zlong < 0.0) zlong = zlong + 2 * M_PI;
2327 CoordGeodetic->lambda = RAD2DEG(zlong);
2328 while (CoordGeodetic->lambda > 180) {
2329 CoordGeodetic->lambda -= 360;
2336 Assignee.phi = CoordGeodetic.phi;
2337 Assignee.lambda = CoordGeodetic.lambda;
2338 Assignee.HeightAboveEllipsoid = CoordGeodetic.HeightAboveEllipsoid;
2339 Assignee.HeightAboveGeoid = CoordGeodetic.HeightAboveGeoid;
2340 Assignee.UseGeoid = CoordGeodetic.UseGeoid;
2344int MAG_DateToYear(
MAGtype_Date *CalendarDate,
char *Error)
2363 if (CalendarDate->Month == 0) {
2364 CalendarDate->DecimalYear = CalendarDate->Year;
2367 if ((CalendarDate->Year % 4 == 0 && CalendarDate->Year % 100 != 0) ||
2368 CalendarDate->Year % 400 == 0)
2372 MonthDays[2] = 28 + ExtraDay;
2385 if (CalendarDate->Month <= 0 || CalendarDate->Month > 12) {
2388 "\nError: The Month entered is invalid, valid months are '1 to 12'\n");
2391 if (CalendarDate->Day <= 0 ||
2392 CalendarDate->Day > MonthDays[CalendarDate->Month]) {
2393 printf(
"\nThe number of days in month %d is %d\n", CalendarDate->Month,
2394 MonthDays[CalendarDate->Month]);
2395 strcpy(Error,
"\nError: The day entered is invalid\n");
2399 for (i = 1; i <= CalendarDate->Month; i++) temp += MonthDays[i - 1];
2400 temp += CalendarDate->Day;
2401 CalendarDate->DecimalYear =
2402 CalendarDate->Year + (temp - 1) / (365.0 + ExtraDay);
2407void MAG_DegreeToDMSstring(
double DegreesOfArc,
int UnitDepth,
char *DMSstring)
2420 double temp = DegreesOfArc;
2421 char tempstring[36] =
"";
2422 char tempstring2[32] =
"";
2423 strcpy(DMSstring,
"");
2424 if (UnitDepth > 3) MAG_Error(21);
2425 for (i = 0; i < UnitDepth; i++) {
2429 strcpy(tempstring2,
"Deg");
2432 strcpy(tempstring2,
"Min");
2435 strcpy(tempstring2,
"Sec");
2438 temp = (temp - DMS[i]) * 60;
2439 if (i == UnitDepth - 1 && temp >= 30)
2441 else if (i == UnitDepth - 1 && temp <= -30)
2443 sprintf(tempstring,
"%4d%4s", DMS[i], tempstring2);
2444 strcat(DMSstring, tempstring);
2448void MAG_DMSstringToDegree(
char *DMSstring,
double *DegreesOfArc)
2456 int second, minute, degree, sign = 1, j = 0;
2457 j = sscanf(DMSstring,
"%d, %d, %d", °ree, &minute, &second);
2458 if (j != 3) sscanf(DMSstring,
"%d %d %d", °ree, &minute, &second);
2459 if (degree < 0) sign = -1;
2460 degree = degree * sign;
2461 *DegreesOfArc = sign * (degree + minute / 60.0 + second / 3600.0);
2467 double cos2D, cos2I, sin2D, sin2I, EDSq, EISq, eD, eI;
2468 cos2D = cos(DEG2RAD(B.Decl)) * cos(DEG2RAD(B.Decl));
2469 cos2I = cos(DEG2RAD(B.Incl)) * cos(DEG2RAD(B.Incl));
2470 sin2D = sin(DEG2RAD(B.Decl)) * sin(DEG2RAD(B.Decl));
2471 sin2I = sin(DEG2RAD(B.Incl)) * sin(DEG2RAD(B.Incl));
2472 eD = DEG2RAD(Errors->Decl);
2473 eI = DEG2RAD(Errors->Incl);
2477 sqrt(cos2D * cos2I * Errors->F * Errors->F +
2478 B.F * B.F * sin2D * cos2I * EDSq + B.F * B.F * cos2D * sin2I * EISq);
2480 sqrt(sin2D * cos2I * Errors->F * Errors->F +
2481 B.F * B.F * cos2D * cos2I * EDSq + B.F * B.F * sin2D * sin2I * EISq);
2482 Errors->Z = sqrt(sin2I * Errors->F * Errors->F + B.F * B.F * cos2I * EISq);
2483 Errors->H = sqrt(cos2I * Errors->F * Errors->F + B.F * B.F * sin2I * EISq);
2513 double CosLat, SinLat, rc, xp, zp;
2521 CosLat = cos(DEG2RAD(CoordGeodetic.phi));
2522 SinLat = sin(DEG2RAD(CoordGeodetic.phi));
2526 rc = Ellip.a / sqrt(1.0 - Ellip.epssq * SinLat * SinLat);
2530 xp = (rc + CoordGeodetic.HeightAboveEllipsoid) * CosLat;
2531 zp = (rc * (1.0 - Ellip.epssq) + CoordGeodetic.HeightAboveEllipsoid) * SinLat;
2535 CoordSpherical->r = sqrt(xp * xp + zp * zp);
2536 CoordSpherical->phig =
2537 RAD2DEG(asin(zp / CoordSpherical->r));
2538 CoordSpherical->lambda = CoordGeodetic.lambda;
2546 Assignee.X = Elements.X;
2547 Assignee.Y = Elements.Y;
2548 Assignee.Z = Elements.Z;
2549 Assignee.H = Elements.H;
2550 Assignee.F = Elements.F;
2551 Assignee.Decl = Elements.Decl;
2552 Assignee.Incl = Elements.Incl;
2553 Assignee.GV = Elements.GV;
2554 Assignee.Xdot = Elements.Xdot;
2555 Assignee.Ydot = Elements.Ydot;
2556 Assignee.Zdot = Elements.Zdot;
2557 Assignee.Hdot = Elements.Hdot;
2558 Assignee.Fdot = Elements.Fdot;
2559 Assignee.Decldot = Elements.Decldot;
2560 Assignee.Incldot = Elements.Incldot;
2561 Assignee.GVdot = Elements.GVdot;
2570 product.X = Elements.X * factor;
2571 product.Y = Elements.Y * factor;
2572 product.Z = Elements.Z * factor;
2573 product.H = Elements.H * factor;
2574 product.F = Elements.F * factor;
2575 product.Incl = Elements.Incl * factor;
2576 product.Decl = Elements.Decl * factor;
2577 product.GV = Elements.GV * factor;
2578 product.Xdot = Elements.Xdot * factor;
2579 product.Ydot = Elements.Ydot * factor;
2580 product.Zdot = Elements.Zdot * factor;
2581 product.Hdot = Elements.Hdot * factor;
2582 product.Fdot = Elements.Fdot * factor;
2583 product.Incldot = Elements.Incldot * factor;
2584 product.Decldot = Elements.Decldot * factor;
2585 product.GVdot = Elements.GVdot * factor;
2596 difference.X = minuend.X - subtrahend.X;
2597 difference.Y = minuend.Y - subtrahend.Y;
2598 difference.Z = minuend.Z - subtrahend.Z;
2600 difference.H = minuend.H - subtrahend.H;
2601 difference.F = minuend.F - subtrahend.F;
2602 difference.Decl = minuend.Decl - subtrahend.Decl;
2603 difference.Incl = minuend.Incl - subtrahend.Incl;
2605 difference.Xdot = minuend.Xdot - subtrahend.Xdot;
2606 difference.Ydot = minuend.Ydot - subtrahend.Ydot;
2607 difference.Zdot = minuend.Zdot - subtrahend.Zdot;
2609 difference.Hdot = minuend.Hdot - subtrahend.Hdot;
2610 difference.Fdot = minuend.Fdot - subtrahend.Fdot;
2611 difference.Decldot = minuend.Decldot - subtrahend.Decldot;
2612 difference.Incldot = minuend.Incldot - subtrahend.Incldot;
2614 difference.GV = minuend.GV - subtrahend.GV;
2615 difference.GVdot = minuend.GVdot - subtrahend.GVdot;
2634 double Lam0, K0, falseE, falseN;
2635 double K0R4, K0R4oa;
2638 double X, Y, pscale, CoM;
2644 Lambda = DEG2RAD(CoordGeodetic.lambda);
2645 Phi = DEG2RAD(CoordGeodetic.phi);
2647 MAG_GetUTMParameters(Phi, Lambda, &Zone, &Hemisphere, &Lam0);
2650 if (Hemisphere ==
'n' || Hemisphere ==
'N') {
2653 if (Hemisphere ==
's' || Hemisphere ==
'S') {
2661 Eps = 0.081819190842621494335;
2662 Epssq = 0.0066943799901413169961;
2663 K0R4 = 6367449.1458234153093 * K0;
2664 K0R4oa = K0R4 / 6378137;
2666 Acoeff[0] = 8.37731820624469723600E-04;
2667 Acoeff[1] = 7.60852777357248641400E-07;
2668 Acoeff[2] = 1.19764550324249124400E-09;
2669 Acoeff[3] = 2.42917068039708917100E-12;
2670 Acoeff[4] = 5.71181837042801392800E-15;
2671 Acoeff[5] = 1.47999793137966169400E-17;
2672 Acoeff[6] = 4.10762410937071532000E-20;
2673 Acoeff[7] = 1.21078503892257704200E-22;
2681 MAG_TMfwd4(Eps, Epssq, K0R4, K0R4oa, Acoeff, Lam0, K0, falseE, falseN, XYonly,
2682 Lambda, Phi, &X, &Y, &pscale, &CoM);
2686 UTMParameters->Easting = X;
2687 UTMParameters->Northing = Y;
2688 UTMParameters->Zone = Zone;
2689 UTMParameters->HemiSphere = Hemisphere;
2690 UTMParameters->CentralMeridian =
2692 UTMParameters->ConvergenceOfMeridians =
2694 UTMParameters->PointScale = pscale;
2699int MAG_GetUTMParameters(
double Latitude,
double Longitude,
int *Zone,
2700 char *Hemisphere,
double *CentralMeridian) {
2719 if ((Latitude < DEG2RAD(MAG_UTM_MIN_LAT_DEGREE)) ||
2721 DEG2RAD(MAG_UTM_MAX_LAT_DEGREE))) {
2725 if ((Longitude < -M_PI) ||
2726 (Longitude > (2 * M_PI))) {
2731 if (Longitude < 0) Longitude += (2 * M_PI) + 1.0e-10;
2732 Lat_Degrees = (long)(Latitude * 180.0 / M_PI);
2733 Long_Degrees = (long)(Longitude * 180.0 / M_PI);
2735 if (Longitude < M_PI)
2736 temp_zone = (long)(31 + ((Longitude * 180.0 / M_PI) / 6.0));
2738 temp_zone = (long)(((Longitude * 180.0 / M_PI) / 6.0) - 29);
2739 if (temp_zone > 60) temp_zone = 1;
2741 if ((Lat_Degrees > 55) && (Lat_Degrees < 64) && (Long_Degrees > -1) &&
2744 if ((Lat_Degrees > 55) && (Lat_Degrees < 64) && (Long_Degrees > 2) &&
2745 (Long_Degrees < 12))
2747 if ((Lat_Degrees > 71) && (Long_Degrees > -1) && (Long_Degrees < 9))
2749 if ((Lat_Degrees > 71) && (Long_Degrees > 8) && (Long_Degrees < 21))
2751 if ((Lat_Degrees > 71) && (Long_Degrees > 20) && (Long_Degrees < 33))
2753 if ((Lat_Degrees > 71) && (Long_Degrees > 32) && (Long_Degrees < 42))
2757 if (temp_zone >= 31)
2758 *CentralMeridian = (6 * temp_zone - 183) * M_PI / 180.0;
2760 *CentralMeridian = (6 * temp_zone + 177) * M_PI / 180.0;
2768 return (Error_Code);
2771int MAG_isNaN(
double d) {
return d != d; }
2805 Psi = (M_PI / 180) * (CoordSpherical.phig - CoordGeodetic.phi);
2808 MagneticResultsGeo->Bz =
2809 MagneticResultsSph.Bx * sin(Psi) + MagneticResultsSph.Bz * cos(Psi);
2810 MagneticResultsGeo->Bx =
2811 MagneticResultsSph.Bx * cos(Psi) - MagneticResultsSph.Bz * sin(Psi);
2812 MagneticResultsGeo->By = MagneticResultsSph.By;
2817 double *y,
double *z) {
2821 radphi = CoordSpherical.phig * (M_PI / 180);
2822 radlambda = CoordSpherical.lambda * (M_PI / 180);
2824 *x = CoordSpherical.r * cos(radphi) * cos(radlambda);
2825 *y = CoordSpherical.r * cos(radphi) * sin(radlambda);
2826 *z = CoordSpherical.r * sin(radphi);
2838 MAG_SphericalToCartesian(CoordSpherical, &x, &y, &z);
2839 MAG_CartesianToGeodetic(Ellip, x, y, z, CoordGeodetic);
2842void MAG_TMfwd4(
double Eps,
double Epssq,
double K0R4,
double K0R4oa,
2843 double Acoeff[],
double Lam0,
double K0,
double falseE,
2844 double falseN,
int XYonly,
double Lambda,
double Phi,
double *X,
2845 double *Y,
double *pscale,
double *CoM) {
2892 double Lam, CLam, SLam, CPhi, SPhi;
2893 double P, part1, part2, denom, CChi, SChi;
2895 double T, Tsq, denom2;
2896 double c2u, s2u, c4u, s4u, c6u, s6u, c8u, s8u;
2897 double c2v, s2v, c4v, s4v, c6v, s6v, c8v, s8v;
2898 double Xstar, Ystar;
2899 double sig1, sig2, comroo;
2910 Lam = Lambda - Lam0;
2922 P = exp(Eps * ATanH(Eps * SPhi));
2923 part1 = (1 + SPhi) / P;
2924 part2 = (1 - SPhi) * P;
2925 denom = 1 / (part1 + part2);
2926 CChi = 2 * CPhi * denom;
2927 SChi = (part1 - part2) * denom;
2941 V = atan2(SChi, CChi * CLam);
2954 denom2 = 1 / (1 - Tsq);
2955 c2u = (1 + Tsq) * denom2;
2956 s2u = 2 * T * denom2;
2957 c2v = (-1 + CChi * CChi * (1 + CLam * CLam)) * denom2;
2958 s2v = 2 * CLam * CChi * SChi * denom2;
2960 c4u = 1 + 2 * s2u * s2u;
2961 s4u = 2 * c2u * s2u;
2962 c4v = 1 - 2 * s2v * s2v;
2963 s4v = 2 * c2v * s2v;
2965 c6u = c4u * c2u + s4u * s2u;
2966 s6u = s4u * c2u + c4u * s2u;
2967 c6v = c4v * c2v - s4v * s2v;
2968 s6v = s4v * c2v + c4v * s2v;
2970 c8u = 1 + 2 * s4u * s4u;
2971 s8u = 2 * c4u * s4u;
2972 c8v = 1 - 2 * s4v * s4v;
2973 s8v = 2 * c4v * s4v;
2981 Xstar = Acoeff[3] * s8u * c8v;
2982 Xstar = Xstar + Acoeff[2] * s6u * c6v;
2983 Xstar = Xstar + Acoeff[1] * s4u * c4v;
2984 Xstar = Xstar + Acoeff[0] * s2u * c2v;
2987 Ystar = Acoeff[3] * c8u * s8v;
2988 Ystar = Ystar + Acoeff[2] * c6u * s6v;
2989 Ystar = Ystar + Acoeff[1] * c4u * s4v;
2990 Ystar = Ystar + Acoeff[0] * c2u * s2v;
2995 *X = K0R4 * Xstar + falseE;
2996 *Y = K0R4 * Ystar + falseN;
3005 sig1 = 8 * Acoeff[3] * c8u * c8v;
3006 sig1 = sig1 + 6 * Acoeff[2] * c6u * c6v;
3007 sig1 = sig1 + 4 * Acoeff[1] * c4u * c4v;
3008 sig1 = sig1 + 2 * Acoeff[0] * c2u * c2v;
3011 sig2 = 8 * Acoeff[3] * s8u * s8v;
3012 sig2 = sig2 + 6 * Acoeff[2] * s6u * s6v;
3013 sig2 = sig2 + 4 * Acoeff[1] * s4u * s4v;
3014 sig2 = sig2 + 2 * Acoeff[0] * s2u * s2v;
3018 sqrt((1 - Epssq * SPhi * SPhi) * denom2 * (sig1 * sig1 + sig2 * sig2));
3020 *pscale = K0R4oa * 2 * denom * comroo;
3021 *CoM = atan2(SChi * SLam, CLam) + atan2(sig2, sig1);
3042 int MonthDays[13], CumulativeDays = 0;
3044 int i, DayOfTheYear;
3046 if (CalendarDate->DecimalYear == 0) {
3047 CalendarDate->Year = 0;
3048 CalendarDate->Month = 0;
3049 CalendarDate->Day = 0;
3053 CalendarDate->Year = (int)floor(CalendarDate->DecimalYear);
3055 if ((CalendarDate->Year % 4 == 0 && CalendarDate->Year % 100 != 0) ||
3056 CalendarDate->Year % 400 == 0)
3060 floor((CalendarDate->DecimalYear - (
double)CalendarDate->Year) *
3061 (365.0 + (
double)ExtraDay) +
3069 MonthDays[2] = 28 + ExtraDay;
3081 for (i = 1; i <= 12; i++) {
3082 CumulativeDays = CumulativeDays + MonthDays[i];
3084 if (DayOfTheYear <= CumulativeDays) {
3085 CalendarDate->Month = i;
3086 CalendarDate->Day = MonthDays[i] - (CumulativeDays - DayOfTheYear);
3126 sin_phi = sin(DEG2RAD(CoordSpherical.phig));
3128 if (nMax <= 16 || (1 - fabs(sin_phi)) <
3130 FLAG = MAG_PcupLow(LegendreFunction->Pcup, LegendreFunction->dPcup, sin_phi,
3133 FLAG = MAG_PcupHigh(LegendreFunction->Pcup, LegendreFunction->dPcup,
3157 CoordGeodetic->phi = CoordGeodetic->phi < (-90.0 + MAG_GEO_POLE_TOLERANCE)
3158 ? (-90.0 + MAG_GEO_POLE_TOLERANCE)
3159 : CoordGeodetic->phi;
3160 CoordGeodetic->phi = CoordGeodetic->phi > (90.0 - MAG_GEO_POLE_TOLERANCE)
3161 ? (90.0 - MAG_GEO_POLE_TOLERANCE)
3162 : CoordGeodetic->phi;
3166int MAG_ComputeSphericalHarmonicVariables(
3195 double cos_lambda, sin_lambda;
3197 cos_lambda = cos(DEG2RAD(CoordSpherical.lambda));
3198 sin_lambda = sin(DEG2RAD(CoordSpherical.lambda));
3202 SphVariables->RelativeRadiusPower[0] =
3203 (Ellip.re / CoordSpherical.r) * (Ellip.re / CoordSpherical.r);
3204 for (n = 1; n <= nMax; n++) {
3205 SphVariables->RelativeRadiusPower[n] =
3206 SphVariables->RelativeRadiusPower[n - 1] *
3207 (Ellip.re / CoordSpherical.r);
3215 SphVariables->cos_mlambda[0] = 1.0;
3216 SphVariables->sin_mlambda[0] = 0.0;
3218 SphVariables->cos_mlambda[1] = cos_lambda;
3219 SphVariables->sin_mlambda[1] = sin_lambda;
3220 for (m = 2; m <= nMax; m++) {
3221 SphVariables->cos_mlambda[m] =
3222 SphVariables->cos_mlambda[m - 1] * cos_lambda -
3223 SphVariables->sin_mlambda[m - 1] * sin_lambda;
3224 SphVariables->sin_mlambda[m] =
3225 SphVariables->cos_mlambda[m - 1] * sin_lambda +
3226 SphVariables->sin_mlambda[m - 1] * cos_lambda;
3242 ((TimedMagneticModel->nMax + 1) * (TimedMagneticModel->nMax + 2) / 2);
3243 LegendreFunction = MAG_AllocateLegendreFunctionMemory(
3245 SphVariables = MAG_AllocateSphVarMemory(TimedMagneticModel->nMax);
3246 MAG_ComputeSphericalHarmonicVariables(
3247 Ellip, CoordSpherical, TimedMagneticModel->nMax,
3249 MAG_AssociatedLegendreFunction(CoordSpherical, TimedMagneticModel->nMax,
3252 LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical,
3254 MAG_RotateMagneticVector(
3255 CoordSpherical, CoordGeodetic, GradYResultsSph,
3258 MAG_CalculateGradientElements(
3259 GradYResultsGeo, GeoMagneticElements,
3263 MAG_FreeLegendreMemory(LegendreFunction);
3264 MAG_FreeSphVarMemory(SphVariables);
3277 for (n = 1; n <= MagneticModel->nMax; n++) {
3278 for (m = 0; m <= n; m++) {
3279 index = (n * (n + 1) / 2 + m);
3281 GradY->Bz -= SphVariables.RelativeRadiusPower[n] *
3282 (-1 * MagneticModel->Main_Field_Coeff_G[index] *
3283 SphVariables.sin_mlambda[m] +
3284 MagneticModel->Main_Field_Coeff_H[index] *
3285 SphVariables.cos_mlambda[m]) *
3286 (
double)(n + 1) * (
double)(m)*LegendreFunction->Pcup[index] *
3287 (1 / CoordSpherical.r);
3288 GradY->By += SphVariables.RelativeRadiusPower[n] *
3289 (MagneticModel->Main_Field_Coeff_G[index] *
3290 SphVariables.cos_mlambda[m] +
3291 MagneticModel->Main_Field_Coeff_H[index] *
3292 SphVariables.sin_mlambda[m]) *
3293 (
double)(m * m) * LegendreFunction->Pcup[index] *
3294 (1 / CoordSpherical.r);
3295 GradY->Bx -= SphVariables.RelativeRadiusPower[n] *
3296 (-1 * MagneticModel->Main_Field_Coeff_G[index] *
3297 SphVariables.sin_mlambda[m] +
3298 MagneticModel->Main_Field_Coeff_H[index] *
3299 SphVariables.cos_mlambda[m]) *
3300 (
double)(m)*LegendreFunction->dPcup[index] *
3301 (1 / CoordSpherical.r);
3305 cos_phi = cos(DEG2RAD(CoordSpherical.phig));
3306 if (fabs(cos_phi) > 1.0e-10) {
3307 GradY->By = GradY->By / (cos_phi * cos_phi);
3308 GradY->Bx = GradY->Bx / (cos_phi);
3309 GradY->Bz = GradY->Bz / (cos_phi);
3322int MAG_PcupHigh(
double *Pcup,
double *dPcup,
double x,
int nMax)
3366 double pm2, pm1, pmm, plm, rescalem, z, scalef;
3367 double *f1, *f2, *PreSqr;
3368 int k, kstart, m, n, NumTerms;
3370 NumTerms = ((nMax + 1) * (nMax + 2) / 2);
3372 if (fabs(x) == 1.0) {
3373 printf(
"Error in PcupHigh: derivative cannot be calculated at poles\n");
3377 f1 = (
double *)malloc((NumTerms + 1) *
sizeof(double));
3383 PreSqr = (
double *)malloc((NumTerms + 1) *
sizeof(double));
3385 if (PreSqr == NULL) {
3390 f2 = (
double *)malloc((NumTerms + 1) *
sizeof(double));
3399 for (n = 0; n <= 2 * nMax + 1; ++n) {
3400 PreSqr[n] = sqrt((
double)(n));
3405 for (n = 2; n <= nMax; n++) {
3407 f1[k] = (double)(2 * n - 1) / (double)(n);
3408 f2[k] = (double)(n - 1) / (double)(n);
3409 for (m = 1; m <= n - 2; m++) {
3411 f1[k] = (double)(2 * n - 1) / PreSqr[n + m] / PreSqr[n - m];
3413 PreSqr[n - m - 1] * PreSqr[n + m - 1] / PreSqr[n + m] / PreSqr[n - m];
3419 z = sqrt((1.0 - x) * (1.0 + x));
3423 if (nMax == 0)
return FALSE;
3429 for (n = 2; n <= nMax; n++) {
3431 plm = f1[k] * x * pm1 - f2[k] * pm2;
3433 dPcup[k] = (double)(n) * (pm1 - x * plm) / z;
3438 pmm = PreSqr[2] * scalef;
3439 rescalem = 1.0 / scalef;
3442 for (m = 1; m <= nMax - 1; ++m) {
3443 rescalem = rescalem * z;
3446 kstart = kstart + m + 1;
3447 pmm = pmm * PreSqr[2 * m + 1] / PreSqr[2 * m];
3448 Pcup[kstart] = pmm * rescalem / PreSqr[2 * m + 1];
3449 dPcup[kstart] = -((double)(m)*x * Pcup[kstart] / z);
3450 pm2 = pmm / PreSqr[2 * m + 1];
3453 pm1 = x * PreSqr[2 * m + 1] * pm2;
3454 Pcup[k] = pm1 * rescalem;
3456 ((pm2 * rescalem) * PreSqr[2 * m + 1] - x * (
double)(m + 1) * Pcup[k]) /
3459 for (n = m + 2; n <= nMax; ++n) {
3461 plm = x * f1[k] * pm1 - f2[k] * pm2;
3462 Pcup[k] = plm * rescalem;
3463 dPcup[k] = (PreSqr[n + m] * PreSqr[n - m] * (pm1 * rescalem) -
3464 (
double)(n)*x * Pcup[k]) /
3472 rescalem = rescalem * z;
3473 kstart = kstart + m + 1;
3474 pmm = pmm / PreSqr[2 * nMax];
3475 Pcup[kstart] = pmm * rescalem;
3476 dPcup[kstart] = -(double)(nMax)*x * Pcup[kstart] / z;
3484int MAG_PcupLow(
double *Pcup,
double *dPcup,
double x,
int nMax)
3510 int n, m, index, index1, index2, NumTerms;
3511 double k, z, *schmidtQuasiNorm;
3515 z = sqrt((1.0 - x) * (1.0 + x));
3517 NumTerms = ((nMax + 1) * (nMax + 2) / 2);
3518 schmidtQuasiNorm = (
double *)malloc((NumTerms + 1) *
sizeof(double));
3520 if (schmidtQuasiNorm == NULL) {
3526 for (n = 1; n <= nMax; n++) {
3527 for (m = 0; m <= n; m++) {
3528 index = (n * (n + 1) / 2 + m);
3530 index1 = (n - 1) * n / 2 + m - 1;
3531 Pcup[index] = z * Pcup[index1];
3532 dPcup[index] = z * dPcup[index1] + x * Pcup[index1];
3533 }
else if (n == 1 && m == 0) {
3534 index1 = (n - 1) * n / 2 + m;
3535 Pcup[index] = x * Pcup[index1];
3536 dPcup[index] = x * dPcup[index1] - z * Pcup[index1];
3537 }
else if (n > 1 && n != m) {
3538 index1 = (n - 2) * (n - 1) / 2 + m;
3539 index2 = (n - 1) * n / 2 + m;
3541 Pcup[index] = x * Pcup[index2];
3542 dPcup[index] = x * dPcup[index2] - z * Pcup[index2];
3544 k = (double)(((n - 1) * (n - 1)) - (m * m)) /
3545 (double)((2 * n - 1) * (2 * n - 3));
3546 Pcup[index] = x * Pcup[index2] - k * Pcup[index1];
3548 x * dPcup[index2] - z * Pcup[index2] - k * dPcup[index1];
3556 schmidtQuasiNorm[0] = 1.0;
3557 for (n = 1; n <= nMax; n++) {
3558 index = (n * (n + 1) / 2);
3559 index1 = (n - 1) * n / 2;
3561 schmidtQuasiNorm[index] =
3562 schmidtQuasiNorm[index1] * (double)(2 * n - 1) / (double)n;
3564 for (m = 1; m <= n; m++) {
3565 index = (n * (n + 1) / 2 + m);
3566 index1 = (n * (n + 1) / 2 + m - 1);
3567 schmidtQuasiNorm[index] =
3568 schmidtQuasiNorm[index1] *
3569 sqrt((
double)((n - m + 1) * (m == 1 ? 2 : 1)) / (double)(n + m));
3577 for (n = 1; n <= nMax; n++) {
3578 for (m = 0; m <= n; m++) {
3579 index = (n * (n + 1) / 2 + m);
3580 Pcup[index] = Pcup[index] * schmidtQuasiNorm[index];
3581 dPcup[index] = -dPcup[index] * schmidtQuasiNorm[index];
3587 if (schmidtQuasiNorm) free(schmidtQuasiNorm);
3607 MagneticModel->SecularVariationUsed = TRUE;
3608 MagneticResults->Bz = 0.0;
3609 MagneticResults->By = 0.0;
3610 MagneticResults->Bx = 0.0;
3611 for (n = 1; n <= MagneticModel->nMaxSecVar; n++) {
3612 for (m = 0; m <= n; m++) {
3613 index = (n * (n + 1) / 2 + m);
3619 MagneticResults->Bz -= SphVariables.RelativeRadiusPower[n] *
3620 (MagneticModel->Secular_Var_Coeff_G[index] *
3621 SphVariables.cos_mlambda[m] +
3622 MagneticModel->Secular_Var_Coeff_H[index] *
3623 SphVariables.sin_mlambda[m]) *
3624 (
double)(n + 1) * LegendreFunction->Pcup[index];
3630 MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
3631 (MagneticModel->Secular_Var_Coeff_G[index] *
3632 SphVariables.sin_mlambda[m] -
3633 MagneticModel->Secular_Var_Coeff_H[index] *
3634 SphVariables.cos_mlambda[m]) *
3635 (
double)(m)*LegendreFunction->Pcup[index];
3641 MagneticResults->Bx -= SphVariables.RelativeRadiusPower[n] *
3642 (MagneticModel->Secular_Var_Coeff_G[index] *
3643 SphVariables.cos_mlambda[m] +
3644 MagneticModel->Secular_Var_Coeff_H[index] *
3645 SphVariables.sin_mlambda[m]) *
3646 LegendreFunction->dPcup[index];
3649 cos_phi = cos(DEG2RAD(CoordSpherical.phig));
3650 if (fabs(cos_phi) > 1.0e-10) {
3651 MagneticResults->By = MagneticResults->By / cos_phi;
3655 MAG_SecVarSummationSpecial(MagneticModel, SphVariables, CoordSpherical,
3677 double k, sin_phi, *PcupS, schmidtQuasiNorm1, schmidtQuasiNorm2,
3680 PcupS = (
double *)malloc((MagneticModel->nMaxSecVar + 1) *
sizeof(double));
3682 if (PcupS == NULL) {
3688 schmidtQuasiNorm1 = 1.0;
3690 MagneticResults->By = 0.0;
3691 sin_phi = sin(DEG2RAD(CoordSpherical.phig));
3693 for (n = 1; n <= MagneticModel->nMaxSecVar; n++) {
3694 index = (n * (n + 1) / 2 + 1);
3695 schmidtQuasiNorm2 = schmidtQuasiNorm1 * (double)(2 * n - 1) / (double)n;
3697 schmidtQuasiNorm2 * sqrt((
double)(n * 2) / (
double)(n + 1));
3698 schmidtQuasiNorm1 = schmidtQuasiNorm2;
3700 PcupS[n] = PcupS[n - 1];
3702 k = (double)(((n - 1) * (n - 1)) - 1) /
3703 (
double)((2 * n - 1) * (2 * n - 3));
3704 PcupS[n] = sin_phi * PcupS[n - 1] - k * PcupS[n - 2];
3712 MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
3713 (MagneticModel->Secular_Var_Coeff_G[index] *
3714 SphVariables.sin_mlambda[1] -
3715 MagneticModel->Secular_Var_Coeff_H[index] *
3716 SphVariables.cos_mlambda[1]) *
3717 PcupS[n] * schmidtQuasiNorm3;
3720 if (PcupS) free(PcupS);
3755 MagneticResults->Bz = 0.0;
3756 MagneticResults->By = 0.0;
3757 MagneticResults->Bx = 0.0;
3758 for (n = 1; n <= MagneticModel->nMax; n++) {
3759 for (m = 0; m <= n; m++) {
3760 index = (n * (n + 1) / 2 + m);
3767 MagneticResults->Bz -= SphVariables.RelativeRadiusPower[n] *
3768 (MagneticModel->Main_Field_Coeff_G[index] *
3769 SphVariables.cos_mlambda[m] +
3770 MagneticModel->Main_Field_Coeff_H[index] *
3771 SphVariables.sin_mlambda[m]) *
3772 (
double)(n + 1) * LegendreFunction->Pcup[index];
3779 MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
3780 (MagneticModel->Main_Field_Coeff_G[index] *
3781 SphVariables.sin_mlambda[m] -
3782 MagneticModel->Main_Field_Coeff_H[index] *
3783 SphVariables.cos_mlambda[m]) *
3784 (
double)(m)*LegendreFunction->Pcup[index];
3791 MagneticResults->Bx -= SphVariables.RelativeRadiusPower[n] *
3792 (MagneticModel->Main_Field_Coeff_G[index] *
3793 SphVariables.cos_mlambda[m] +
3794 MagneticModel->Main_Field_Coeff_H[index] *
3795 SphVariables.sin_mlambda[m]) *
3796 LegendreFunction->dPcup[index];
3800 cos_phi = cos(DEG2RAD(CoordSpherical.phig));
3801 if (fabs(cos_phi) > 1.0e-10) {
3802 MagneticResults->By = MagneticResults->By / cos_phi;
3810 MAG_SummationSpecial(MagneticModel, SphVariables, CoordSpherical,
3832 double k, sin_phi, *PcupS, schmidtQuasiNorm1, schmidtQuasiNorm2,
3835 PcupS = (
double *)malloc((MagneticModel->nMax + 1) *
sizeof(double));
3842 schmidtQuasiNorm1 = 1.0;
3844 MagneticResults->By = 0.0;
3845 sin_phi = sin(DEG2RAD(CoordSpherical.phig));
3847 for (n = 1; n <= MagneticModel->nMax; n++) {
3852 index = (n * (n + 1) / 2 + 1);
3853 schmidtQuasiNorm2 = schmidtQuasiNorm1 * (double)(2 * n - 1) / (double)n;
3855 schmidtQuasiNorm2 * sqrt((
double)(n * 2) / (
double)(n + 1));
3856 schmidtQuasiNorm1 = schmidtQuasiNorm2;
3858 PcupS[n] = PcupS[n - 1];
3860 k = (double)(((n - 1) * (n - 1)) - 1) /
3861 (
double)((2 * n - 1) * (2 * n - 3));
3862 PcupS[n] = sin_phi * PcupS[n - 1] - k * PcupS[n - 2];
3871 MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
3872 (MagneticModel->Main_Field_Coeff_G[index] *
3873 SphVariables.sin_mlambda[1] -
3874 MagneticModel->Main_Field_Coeff_H[index] *
3875 SphVariables.cos_mlambda[1]) *
3876 PcupS[n] * schmidtQuasiNorm3;
3879 if (PcupS) free(PcupS);
3883int MAG_TimelyModifyMagneticModel(
MAGtype_Date UserDate,
3900 int n, m, index, a, b;
3901 TimedMagneticModel->EditionDate = MagneticModel->EditionDate;
3902 TimedMagneticModel->epoch = MagneticModel->epoch;
3903 TimedMagneticModel->nMax = MagneticModel->nMax;
3904 TimedMagneticModel->nMaxSecVar = MagneticModel->nMaxSecVar;
3905 a = TimedMagneticModel->nMaxSecVar;
3906 b = (a * (a + 1) / 2 + a);
3907 strcpy(TimedMagneticModel->ModelName, MagneticModel->ModelName);
3908 for (n = 1; n <= MagneticModel->nMax; n++) {
3909 for (m = 0; m <= n; m++) {
3910 index = (n * (n + 1) / 2 + m);
3912 TimedMagneticModel->Main_Field_Coeff_H[index] =
3913 MagneticModel->Main_Field_Coeff_H[index] +
3914 (UserDate.DecimalYear - MagneticModel->epoch) *
3915 MagneticModel->Secular_Var_Coeff_H[index];
3916 TimedMagneticModel->Main_Field_Coeff_G[index] =
3917 MagneticModel->Main_Field_Coeff_G[index] +
3918 (UserDate.DecimalYear - MagneticModel->epoch) *
3919 MagneticModel->Secular_Var_Coeff_G[index];
3920 TimedMagneticModel->Secular_Var_Coeff_H[index] =
3922 ->Secular_Var_Coeff_H[index];
3925 TimedMagneticModel->Secular_Var_Coeff_G[index] =
3926 MagneticModel->Secular_Var_Coeff_G[index];
3928 TimedMagneticModel->Main_Field_Coeff_H[index] =
3929 MagneticModel->Main_Field_Coeff_H[index];
3930 TimedMagneticModel->Main_Field_Coeff_G[index] =
3931 MagneticModel->Main_Field_Coeff_G[index];
3969 if (Geoid->UseGeoid == 1) {
3971 MAG_EquivalentLatLon(CoordGeodetic->phi, CoordGeodetic->lambda, &lat, &lon);
3972 Error_Code = MAG_GetGeoidHeight(lat, lon, &DeltaHeight, Geoid);
3973 CoordGeodetic->HeightAboveEllipsoid =
3974 CoordGeodetic->HeightAboveGeoid +
3981 CoordGeodetic->HeightAboveEllipsoid = CoordGeodetic->HeightAboveGeoid;
3984 return (Error_Code);
3987int MAG_GetGeoidHeight(
double Latitude,
double Longitude,
double *DeltaHeight,
4003 double DeltaX, DeltaY;
4004 double ElevationSE, ElevationSW, ElevationNE, ElevationNW;
4005 double OffsetX, OffsetY;
4006 double PostX, PostY;
4007 double UpperY, LowerY;
4010 if (!Geoid->Geoid_Initialized) {
4014 if ((Latitude < -90) || (Latitude > 90)) {
4017 if ((Longitude < -180) || (Longitude > 360)) {
4024 if (Longitude < 0.0) {
4025 OffsetX = (Longitude + 360.0) * Geoid->ScaleFactor;
4027 OffsetX = Longitude * Geoid->ScaleFactor;
4029 OffsetY = (90.0 - Latitude) * Geoid->ScaleFactor;
4035 PostX = floor(OffsetX);
4036 if ((PostX + 1) == Geoid->NumbGeoidCols) PostX--;
4037 PostY = floor(OffsetY);
4038 if ((PostY + 1) == Geoid->NumbGeoidRows) PostY--;
4040 Index = (long)(PostY * Geoid->NumbGeoidCols + PostX);
4041 ElevationNW = (double)Geoid->GeoidHeightBuffer[Index];
4042 ElevationNE = (double)Geoid->GeoidHeightBuffer[Index + 1];
4044 Index = (long)((PostY + 1) * Geoid->NumbGeoidCols + PostX);
4045 ElevationSW = (double)Geoid->GeoidHeightBuffer[Index];
4046 ElevationSE = (double)Geoid->GeoidHeightBuffer[Index + 1];
4050 DeltaX = OffsetX - PostX;
4051 DeltaY = OffsetY - PostY;
4053 UpperY = ElevationNW + DeltaX * (ElevationNE - ElevationNW);
4054 LowerY = ElevationSW + DeltaX * (ElevationSE - ElevationSW);
4056 *DeltaHeight = UpperY + DeltaY * (LowerY - UpperY);
4064void MAG_EquivalentLatLon(
double lat,
double lon,
double *repairedLat,
4065 double *repairedLon)
4073 if (colat < 0) colat = -colat;
4074 while (colat > 360) {
4079 *repairedLon = *repairedLon + 180;
4081 *repairedLat = 90 - colat;
4082 if (*repairedLon > 360) *repairedLon -= 360;
4083 if (*repairedLon < -180) *repairedLon += 360;
4091 double decl_variable, decl_constant;
4092 Uncertainty->F = WMM_UNCERTAINTY_F;
4093 Uncertainty->H = WMM_UNCERTAINTY_H;
4094 Uncertainty->X = WMM_UNCERTAINTY_X;
4095 Uncertainty->Z = WMM_UNCERTAINTY_Z;
4096 Uncertainty->Incl = WMM_UNCERTAINTY_I;
4097 Uncertainty->Y = WMM_UNCERTAINTY_Y;
4098 decl_variable = (WMM_UNCERTAINTY_D_COEF / H);
4099 decl_constant = (WMM_UNCERTAINTY_D_OFFSET);
4101 sqrt(decl_constant * decl_constant + decl_variable * decl_variable);
4102 if (Uncertainty->Decl > 180) {
4103 Uncertainty->Decl = 180;
4107void MAG_PrintUserDataWithUncertainty(
4112 char DeclString[100];
4113 char InclString[100];
4114 MAG_DegreeToDMSstring(GeomagElements.Incl, 2, InclString);
4115 if (GeomagElements.H < 6000 && GeomagElements.H > 2000)
4116 MAG_Warnings(1, GeomagElements.H, MagneticModel);
4117 if (GeomagElements.H < 2000) MAG_Warnings(2, GeomagElements.H, MagneticModel);
4118 if (MagneticModel->SecularVariationUsed == TRUE) {
4119 MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
4120 printf(
"\n Results For \n\n");
4121 if (SpaceInput.phi < 0)
4122 printf(
"Latitude %.2fS\n", -SpaceInput.phi);
4124 printf(
"Latitude %.2fN\n", SpaceInput.phi);
4125 if (SpaceInput.lambda < 0)
4126 printf(
"Longitude %.2fW\n", -SpaceInput.lambda);
4128 printf(
"Longitude %.2fE\n", SpaceInput.lambda);
4129 if (Geoid->UseGeoid == 1)
4130 printf(
"Altitude: %.2f Kilometers above mean sea level\n",
4131 SpaceInput.HeightAboveGeoid);
4133 printf(
"Altitude: %.2f Kilometers above the WGS-84 ellipsoid\n",
4134 SpaceInput.HeightAboveEllipsoid);
4135 printf(
"Date: %.1f\n", TimeInput.DecimalYear);
4136 printf(
"\n Main Field\t\t\tSecular Change\n");
4137 printf(
"F = %9.1f +/- %5.1f nT\t\t Fdot = %5.1f\tnT/yr\n",
4138 GeomagElements.F, Errors.F, GeomagElements.Fdot);
4139 printf(
"H = %9.1f +/- %5.1f nT\t\t Hdot = %5.1f\tnT/yr\n",
4140 GeomagElements.H, Errors.H, GeomagElements.Hdot);
4141 printf(
"X = %9.1f +/- %5.1f nT\t\t Xdot = %5.1f\tnT/yr\n",
4142 GeomagElements.X, Errors.X, GeomagElements.Xdot);
4143 printf(
"Y = %9.1f +/- %5.1f nT\t\t Ydot = %5.1f\tnT/yr\n",
4144 GeomagElements.Y, Errors.Y, GeomagElements.Ydot);
4145 printf(
"Z = %9.1f +/- %5.1f nT\t\t Zdot = %5.1f\tnT/yr\n",
4146 GeomagElements.Z, Errors.Z, GeomagElements.Zdot);
4147 if (GeomagElements.Decl < 0)
4148 printf(
"Decl =%20s (WEST) +/-%3.0f Min Ddot = %.1f\tMin/yr\n",
4149 DeclString, 60 * Errors.Decl, 60 * GeomagElements.Decldot);
4151 printf(
"Decl =%20s (EAST) +/-%3.0f Min Ddot = %.1f\tMin/yr\n",
4152 DeclString, 60 * Errors.Decl, 60 * GeomagElements.Decldot);
4153 if (GeomagElements.Incl < 0)
4154 printf(
"Incl =%20s (UP) +/-%3.0f Min Idot = %.1f\tMin/yr\n",
4155 InclString, 60 * Errors.Incl, 60 * GeomagElements.Incldot);
4157 printf(
"Incl =%20s (DOWN) +/-%3.0f Min Idot = %.1f\tMin/yr\n",
4158 InclString, 60 * Errors.Incl, 60 * GeomagElements.Incldot);
4160 MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
4161 printf(
"\n Results For \n\n");
4162 if (SpaceInput.phi < 0)
4163 printf(
"Latitude %.2fS\n", -SpaceInput.phi);
4165 printf(
"Latitude %.2fN\n", SpaceInput.phi);
4166 if (SpaceInput.lambda < 0)
4167 printf(
"Longitude %.2fW\n", -SpaceInput.lambda);
4169 printf(
"Longitude %.2fE\n", SpaceInput.lambda);
4170 if (Geoid->UseGeoid == 1)
4171 printf(
"Altitude: %.2f Kilometers above MSL\n",
4172 SpaceInput.HeightAboveGeoid);
4174 printf(
"Altitude: %.2f Kilometers above WGS-84 Ellipsoid\n",
4175 SpaceInput.HeightAboveEllipsoid);
4176 printf(
"Date: %.1f\n", TimeInput.DecimalYear);
4177 printf(
"\n Main Field\n");
4178 printf(
"F = %-9.1f +/-%5.1f nT\n", GeomagElements.F, Errors.F);
4179 printf(
"H = %-9.1f +/-%5.1f nT\n", GeomagElements.H, Errors.H);
4180 printf(
"X = %-9.1f +/-%5.1f nT\n", GeomagElements.X, Errors.X);
4181 printf(
"Y = %-9.1f +/-%5.1f nT\n", GeomagElements.Y, Errors.Y);
4182 printf(
"Z = %-9.1f +/-%5.1f nT\n", GeomagElements.Z, Errors.Z);
4183 if (GeomagElements.Decl < 0)
4184 printf(
"Decl =%20s (WEST)+/-%4f\n", DeclString, 60 * Errors.Decl);
4186 printf(
"Decl =%20s (EAST)+/-%4f\n", DeclString, 60 * Errors.Decl);
4187 if (GeomagElements.Incl < 0)
4188 printf(
"Incl =%20s (UP)+/-%4f\n", InclString, 60 * Errors.Incl);
4190 printf(
"Incl =%20s (DOWN)+/-%4f\n", InclString, 60 * Errors.Incl);
4193 if (SpaceInput.phi <= -55 || SpaceInput.phi >= 55)
4196 MAG_DegreeToDMSstring(GeomagElements.GV, 2, InclString);
4197 printf(
"\n\n Grid variation =%20s\n", InclString);
4203void MAG_GetDeg(
char *Query_String,
double *latitude,
double bounds[2]) {
4205 char buffer[64], Error_Message[255];
4208 printf(
"%s", Query_String);
4209 while (NULL == fgets(buffer, 64, stdin)) {
4210 printf(
"%s", Query_String);
4212 for (i = 0, done = 0, j = 0; i <= 64 && !done; i++) {
4213 if (buffer[i] ==
'.') {
4214 j = sscanf(buffer,
"%lf", latitude);
4220 if (buffer[i] ==
',') {
4221 if (MAG_ValidateDMSstring(buffer, bounds[0], bounds[1], Error_Message)) {
4222 MAG_DMSstringToDegree(buffer, latitude);
4227 if (buffer[i] ==
' ')
4231 if (MAG_ValidateDMSstring(buffer, bounds[0], bounds[1], Error_Message)) {
4232 MAG_DMSstringToDegree(buffer, latitude);
4237 if (buffer[i] ==
'\0' || done == -1) {
4238 if (MAG_ValidateDMSstring(buffer, bounds[0], bounds[1], Error_Message) &&
4240 sscanf(buffer,
"%lf", latitude);
4243 printf(
"%s", Error_Message);
4246 "\nError encountered, please re-enter as '(-)DDD,MM,SS' or in "
4247 "Decimal Degrees DD.ddd:\n");
4248 while (NULL == fgets(buffer, 40, stdin)) {
4250 "\nError encountered, please re-enter as '(-)DDD,MM,SS' or in "
4251 "Decimal Degrees DD.ddd:\n");
4262int MAG_GetAltitude(
char *Query_String,
MAGtype_Geoid *Geoid,
4264 int AltitudeSetting) {
4265 int done, j, UpBoundOn;
4270 if (bounds[1] != NO_ALT_MAX) {
4275 printf(
"%s", Query_String);
4279 while (NULL == fgets(buffer, 40, stdin)) {
4280 printf(
"%s", Query_String);
4283 if ((AltitudeSetting != MSLON) &&
4284 (buffer[0] ==
'e' || buffer[0] ==
'E' ||
4289 if (buffer[0] ==
'e' || buffer[0] ==
'E') {
4290 j = sscanf(buffer,
"%c%lf", &tmp, &coords->HeightAboveEllipsoid);
4292 j = sscanf(buffer,
"%lf", &coords->HeightAboveEllipsoid);
4295 Geoid->UseGeoid = 0;
4296 coords->HeightAboveGeoid = coords->HeightAboveEllipsoid;
4297 value = coords->HeightAboveEllipsoid;
4301 Geoid->UseGeoid = 1;
4302 j = sscanf(buffer,
"%lf", &coords->HeightAboveGeoid);
4303 MAG_ConvertGeoidToEllipsoidHeight(coords, Geoid);
4304 value = coords->HeightAboveGeoid;
4309 printf(
"\nIllegal Format, please re-enter as '(-)HHH.hhh:'\n");
4310 if ((value < bounds[0] || (value > bounds[1] && UpBoundOn)) && done == 1) {
4314 "\nWarning: The value you have entered of %f km for the elevation "
4315 "is outside of the required range.\n",
4317 printf(
" An elevation between %d km and %d km is needed. \n", bounds[0],
4319 if (AltitudeSetting == WGS84ON) {
4321 "Please enter height above WGS-84 Ellipsoid (in kilometers):\n");
4322 }
else if (AltitudeSetting == MSLON) {
4323 printf(
"Please enter height above mean sea level (in kilometers):\n");
4326 "Please enter height in kilometers (prepend E for height above "
4327 "WGS-84 Ellipsoid):");
4330 switch (MAG_Warnings(3, value, NULL)) {
4332 return USER_GAVE_UP;
4335 printf(
"Please enter height above sea level (in kilometers):\n");