GeomagnetismLibrary.c
Go to the documentation of this file.
00001 
00002 #include <stdio.h>
00003 #include <string.h>
00004 #include <math.h>
00005 #include <stdlib.h>
00006 #include <ctype.h>
00007 #include <assert.h>
00008 #include "GeomagnetismHeader.h"
00009 
00010 // $Id: GeomagnetismLibrary.c 842 2012-04-20 20:59:13Z awoods $
00011 /*
00012  * ABSTRACT
00013  *
00014  * The purpose of Geomagnetism Library is primarily to support the World Magnetic Model (WMM) 2010-2015.
00015  * It however is built to be used for spherical harmonic models of the Earth's magnetic field
00016  * generally and supports models even with a large (>>12) number of degrees.  It is also used in the EMM.
00017  *
00018  * REUSE NOTES
00019  *
00020  * Geomagnetism Library is intended for reuse by any application that requires
00021  * Computation of Geomagnetic field from a spherical harmonic model.
00022  *
00023  * REFERENCES
00024  *
00025  *    Further information on Geoid can be found in the WMM Technical Documents.
00026  *
00027  *
00028  * LICENSES
00029  *
00030  *  The WMM source code is in the public domain and not licensed or under copyright.
00031  *      The information and software may be used freely by the public. As required by 17 U.S.C. 403,
00032  *      third parties producing copyrighted works consisting predominantly of the material produced by
00033  *      U.S. government agencies must provide notice with such work(s) identifying the U.S. Government material
00034  *      incorporated and stating that such material is not subject to copyright protection.
00035  *
00036  * RESTRICTIONS
00037  *
00038  *    Geomagnetism library has no restrictions.
00039  *
00040  * ENVIRONMENT
00041  *
00042  *    Geomagnetism library was tested in the following environments
00043  *
00044  *    1. Red Hat Linux  with GCC Compiler
00045  *    2. MS Windows XP with CodeGear C++ compiler
00046  *    3. Sun Solaris with GCC Compiler
00047  *
00048  *
00049  * MODIFICATIONS
00050  *
00051  *    Date                 Version
00052  *    ----                 -----------
00053  *    Jul 15, 2009         0.1
00054  *    Nov 17, 2009         0.2
00055  *    Nov 23, 2009         0.3
00056  *    Jan 28, 2010         1.0
00057 
00058  *      Contact Information
00059 
00060 
00061  *  Sponsoring Government Agency
00062  *  National Geospatial-Intelligence Agency
00063  *  PRG / CSAT, M.S. L-41
00064  *  3838 Vogel Road
00065  *  Arnold, MO 63010
00066  *  Attn: Craig Rollins
00067  *  Phone:  (314) 263-4186
00068  *  Email:  Craig.M.Rollins@Nga.Mil
00069 
00070 
00071  *  National Geophysical Data Center
00072  *  NOAA EGC/2
00073  *  325 Broadway
00074  *  Boulder, CO 80303 USA
00075  *  Attn: Susan McLean
00076  *  Phone:  (303) 497-6478
00077  *  Email:  Susan.McLean@noaa.gov
00078 
00079 
00080  *  Software and Model Support
00081  *  National Geophysical Data Center
00082  *  NOAA EGC/2
00083  *  325 Broadway"
00084  *  Boulder, CO 80303 USA
00085  *  Attn: Manoj Nair or Stefan Maus
00086  *  Phone:  (303) 497-6522 or -6522
00087  *  Email:  Manoj.C.Nair@noaa.gov or Stefan.Maus@noaa.gov
00088  *  URL: http://www.ngdc.noaa.gov/Geomagnetic/WMM/DoDWMM.shtml
00089 
00090 
00091  *  For more details on the subroutines, please consult the WMM
00092  *  Technical Documentations at
00093  *  http://www.ngdc.noaa.gov/Geomagnetic/WMM/DoDWMM.shtml
00094 
00095  *  Nov 23, 2009
00096  *  Written by Manoj C Nair and Adam Woods
00097  *  Manoj.C.Nair@Noaa.Gov
00098  *  Revision Number: $Revision: 842 $
00099  *  Last changed by: $Author: awoods $
00100  *  Last changed on: $Date: 2012-04-20 14:59:13 -0600 (Fri, 20 Apr 2012) $
00101 
00102  */
00103 
00104 
00105 
00106 
00107 
00108 
00109 /******************************************************************************
00110  ************************************Wrapper***********************************
00111  * This grouping consists of functions call groups of other functions to do a
00112  * complete calculation of some sort.  For example, the MAG_Geomag function
00113  * does everything necessary to compute the geomagnetic elements from a given
00114  * geodetic point in space and magnetic model adjusted for the appropriate
00115  * date. These functions are the external functions necessary to create a
00116  * program that uses or calculates the magnetic field.  
00117  ******************************************************************************
00118  ******************************************************************************/
00119 
00120 
00121 int MAG_Geomag(MAGtype_Ellipsoid Ellip, MAGtype_CoordSpherical CoordSpherical, MAGtype_CoordGeodetic CoordGeodetic,
00122         MAGtype_MagneticModel *TimedMagneticModel, MAGtype_GeoMagneticElements *GeoMagneticElements)
00123 /*
00124 The main subroutine that calls a sequence of WMM sub-functions to calculate the magnetic field elements for a single point.
00125 The function expects the model coefficients and point coordinates as input and returns the magnetic field elements and
00126 their rate of change. Though, this subroutine can be called successively to calculate a time series, profile or grid
00127 of magnetic field, these are better achieved by the subroutine MAG_Grid.
00128 
00129 INPUT: Ellip
00130               CoordSpherical
00131               CoordGeodetic
00132               TimedMagneticModel
00133 
00134 OUTPUT : GeoMagneticElements
00135 
00136 CALLS:          MAG_AllocateLegendreFunctionMemory(NumTerms);  ( For storing the ALF functions )
00137                      MAG_ComputeSphericalHarmonicVariables( Ellip, CoordSpherical, TimedMagneticModel->nMax, &SphVariables); (Compute Spherical Harmonic variables  )
00138                      MAG_AssociatedLegendreFunction(CoordSpherical, TimedMagneticModel->nMax, LegendreFunction);        Compute ALF
00139                      MAG_Summation(LegendreFunction, TimedMagneticModel, SphVariables, CoordSpherical, &MagneticResultsSph);  Accumulate the spherical harmonic coefficients
00140                      MAG_SecVarSummation(LegendreFunction, TimedMagneticModel, SphVariables, CoordSpherical, &MagneticResultsSphVar); Sum the Secular Variation Coefficients
00141                      MAG_RotateMagneticVector(CoordSpherical, CoordGeodetic, MagneticResultsSph, &MagneticResultsGeo); Map the computed Magnetic fields to Geodetic coordinates
00142                      MAG_CalculateGeoMagneticElements(&MagneticResultsGeo, GeoMagneticElements);   Calculate the Geomagnetic elements
00143                      MAG_CalculateSecularVariationElements(MagneticResultsGeoVar, GeoMagneticElements); Calculate the secular variation of each of the Geomagnetic elements
00144 
00145  */
00146 {
00147     MAGtype_LegendreFunction *LegendreFunction;
00148     MAGtype_SphericalHarmonicVariables *SphVariables;
00149     int NumTerms;
00150     MAGtype_MagneticResults MagneticResultsSph, MagneticResultsGeo, MagneticResultsSphVar, MagneticResultsGeoVar;
00151 
00152     NumTerms = ((TimedMagneticModel->nMax + 1) * (TimedMagneticModel->nMax + 2) / 2); 
00153     LegendreFunction = MAG_AllocateLegendreFunctionMemory(NumTerms); /* For storing the ALF functions */
00154     SphVariables = MAG_AllocateSphVarMemory(TimedMagneticModel->nMax);
00155     MAG_ComputeSphericalHarmonicVariables(Ellip, CoordSpherical, TimedMagneticModel->nMax, SphVariables); /* Compute Spherical Harmonic variables  */
00156     MAG_AssociatedLegendreFunction(CoordSpherical, TimedMagneticModel->nMax, LegendreFunction); /* Compute ALF  */
00157     MAG_Summation(LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical, &MagneticResultsSph); /* Accumulate the spherical harmonic coefficients*/
00158     MAG_SecVarSummation(LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical, &MagneticResultsSphVar); /*Sum the Secular Variation Coefficients  */
00159     MAG_RotateMagneticVector(CoordSpherical, CoordGeodetic, MagneticResultsSph, &MagneticResultsGeo); /* Map the computed Magnetic fields to Geodeitic coordinates  */
00160     MAG_RotateMagneticVector(CoordSpherical, CoordGeodetic, MagneticResultsSphVar, &MagneticResultsGeoVar); /* Map the secular variation field components to Geodetic coordinates*/
00161     MAG_CalculateGeoMagneticElements(&MagneticResultsGeo, GeoMagneticElements); /* Calculate the Geomagnetic elements, Equation 18 , WMM Technical report */
00162     MAG_CalculateSecularVariationElements(MagneticResultsGeoVar, GeoMagneticElements); /*Calculate the secular variation of each of the Geomagnetic elements*/
00163 
00164     MAG_FreeLegendreMemory(LegendreFunction);
00165     MAG_FreeSphVarMemory(SphVariables);
00166 
00167     return TRUE;
00168 } /*MAG_Geomag*/
00169 
00170 int MAG_Grid(MAGtype_CoordGeodetic minimum, MAGtype_CoordGeodetic maximum, double
00171         cord_step_size, double altitude_step_size, double time_step, MAGtype_MagneticModel *MagneticModel, MAGtype_Geoid
00172         *Geoid, MAGtype_Ellipsoid Ellip, MAGtype_Date StartDate, MAGtype_Date EndDate, int ElementOption, int PrintOption, char *OutputFile)
00173 
00174 /*This function calls WMM subroutines to generate a grid as defined by the user. The function may be used
00175 to generate a grid of magnetic field elements, time series or a profile. The selected geomagnetic element
00176 is either printed to the file GridResults.txt or to the screen depending on user option.
00177 
00178 INPUT: minimum :Data structure with the following elements (minimum limits of the grid)
00179                                 double lambda; (longitude)
00180                                 double phi; ( geodetic latitude)
00181                                 double HeightAboveEllipsoid; (height above the ellipsoid (HaE) )
00182                                 double HeightAboveGeoid;(height above the Geoid )
00183                 maximum : same as the above (maximum limist of the grid)
00184                 step_size  : double  : spatial step size, in decimal degrees
00185                 a_step_size : double  :  double altitude step size (km)
00186                 step_time : double  : time step size (decimal years)
00187                 StartDate :  data structure with the following elements used
00188                                         double DecimalYear;     ( decimal years )
00189                 EndDate :       Same as the above;
00190                 MagneticModel :  data structure with the following elements
00191                         double EditionDate;
00192                         double epoch;       Base time of Geomagnetic model epoch (yrs)
00193                         char  ModelName[20];
00194                         double *Main_Field_Coeff_G;          C - Gauss coefficients of main geomagnetic model (nT)
00195                         double *Main_Field_Coeff_H;          C - Gauss coefficients of main geomagnetic model (nT)
00196                         double *Secular_Var_Coeff_G;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
00197                         double *Secular_Var_Coeff_H;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
00198                         int nMax;  Maximum degree of spherical harmonic model
00199                         int nMaxSecVar; Maxumum degree of spherical harmonic secular model
00200                         int SecularVariationUsed; Whether or not the magnetic secular variation vector will be needed by program
00201                 Geoid :  data structure with the following elements
00202         Pointer to data structure Geoid with the following elements
00203                         int NumbGeoidCols ;   ( 360 degrees of longitude at 15 minute spacing )
00204                         int NumbGeoidRows ;   ( 180 degrees of latitude  at 15 minute spacing )
00205                         int NumbHeaderItems ;    ( min, max lat, min, max long, lat, long spacing )
00206                         int     ScaleFactor;    ( 4 grid cells per degree at 15 minute spacing  )
00207                         float *GeoidHeightBuffer;   (Pointer to the memory to store the Geoid elevation data )
00208                         int NumbGeoidElevs;    (number of points in the gridded file )
00209                         int  Geoid_Initialized ;  ( indicates successful initialization )
00210    Ellip  data  structure with the following elements
00211                         double a; semi-major axis of the ellipsoid
00212                         double b; semi-minor axis of the ellipsoid
00213                         double fla;  flattening
00214                         double epssq; first eccentricity squared
00215                         double eps;  first eccentricity
00216                         double re; mean radius of  ellipsoid
00217           ElementOption : int : Geomagnetic Elelment to print
00218           PrintOption : int : 1 Print to File, Otherwise, print to screen
00219 
00220    OUTPUT: none (prints the output to a file )
00221 
00222    CALLS : MAG_AllocateModelMemory To allocate memory for model coefficients
00223       MAG_TimelyModifyMagneticModel This modifies the Magnetic coefficients to the correct date.
00224                   MAG_ConvertGeoidToEllipsoidHeight (&CoordGeodetic, &Geoid);   Convert height above msl to height above WGS-84 ellipsoid
00225                   MAG_GeodeticToSpherical Convert from geodeitic to Spherical Equations: 7-8, WMM Technical report
00226                   MAG_ComputeSphericalHarmonicVariables Compute Spherical Harmonic variables
00227                   MAG_AssociatedLegendreFunction Compute ALF  Equations 5-6, WMM Technical report
00228                   MAG_Summation Accumulate the spherical harmonic coefficients Equations 10:12 , WMM Technical report
00229                   MAG_RotateMagneticVector Map the computed Magnetic fields to Geodeitic coordinates Equation 16 , WMM Technical report
00230                   MAG_CalculateGeoMagneticElements Calculate the geoMagnetic elements, Equation 18 , WMM Technical report
00231 
00232  */
00233 {
00234     int NumTerms;
00235     double a, b, c, d, PrintElement;
00236 
00237     MAGtype_MagneticModel *TimedMagneticModel;
00238     MAGtype_CoordSpherical CoordSpherical;
00239     MAGtype_MagneticResults MagneticResultsSph, MagneticResultsGeo, MagneticResultsSphVar, MagneticResultsGeoVar;
00240     MAGtype_SphericalHarmonicVariables *SphVariables;
00241     MAGtype_GeoMagneticElements GeoMagneticElements;
00242     MAGtype_LegendreFunction *LegendreFunction;
00243 
00244     FILE *fileout = NULL;
00245 
00246     if(PrintOption == 1)
00247     {
00248         fileout = fopen(OutputFile, "w");
00249         if(!fileout)
00250         {
00251             printf("Error opening %s to write", OutputFile);
00252             return FALSE;
00253         }
00254     }
00255 
00256 
00257 
00258     if(fabs(cord_step_size) < 1.0e-10) cord_step_size = 99999.0; //checks to make sure that the step_size is not too small
00259     if(fabs(altitude_step_size) < 1.0e-10) altitude_step_size = 99999.0;
00260     if(fabs(time_step) < 1.0e-10) time_step = 99999.0;
00261 
00262 
00263     NumTerms = ((MagneticModel->nMax + 1) * (MagneticModel->nMax + 2) / 2);
00264     TimedMagneticModel = MAG_AllocateModelMemory(NumTerms);
00265     LegendreFunction = MAG_AllocateLegendreFunctionMemory(NumTerms); /* For storing the ALF functions */
00266     SphVariables = MAG_AllocateSphVarMemory(MagneticModel->nMax);
00267     a = minimum.HeightAboveGeoid; //sets the loop initialization values
00268     b = minimum.phi;
00269     c = minimum.lambda;
00270     d = StartDate.DecimalYear;
00271 
00272 
00273 
00274     for(minimum.HeightAboveGeoid = a; minimum.HeightAboveGeoid <= maximum.HeightAboveGeoid; minimum.HeightAboveGeoid += altitude_step_size) /* Altitude loop*/
00275     {
00276 
00277         for(minimum.phi = b; minimum.phi <= maximum.phi; minimum.phi += cord_step_size) /*Latitude loop*/
00278         {
00279 
00280             for(minimum.lambda = c; minimum.lambda <= maximum.lambda; minimum.lambda += cord_step_size) /*Longitude loop*/
00281             {
00282                 if(Geoid->UseGeoid == 1)
00283                     MAG_ConvertGeoidToEllipsoidHeight(&minimum, Geoid); //This converts the height above mean sea level to height above the WGS-84 ellipsoid
00284                 else
00285                     minimum.HeightAboveEllipsoid = minimum.HeightAboveGeoid;
00286                 MAG_GeodeticToSpherical(Ellip, minimum, &CoordSpherical);
00287                 MAG_ComputeSphericalHarmonicVariables(Ellip, CoordSpherical, MagneticModel->nMax, SphVariables); /* Compute Spherical Harmonic variables  */
00288                 MAG_AssociatedLegendreFunction(CoordSpherical, MagneticModel->nMax, LegendreFunction); /* Compute ALF  Equations 5-6, WMM Technical report*/
00289 
00290                 for(StartDate.DecimalYear = d; StartDate.DecimalYear <= EndDate.DecimalYear; StartDate.DecimalYear += time_step) /*Year loop*/
00291                 {
00292 
00293                     MAG_TimelyModifyMagneticModel(StartDate, MagneticModel, TimedMagneticModel); /*This modifies the Magnetic coefficients to the correct date. */
00294                     MAG_Summation(LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical, &MagneticResultsSph); /* Accumulate the spherical harmonic coefficients Equations 10:12 , WMM Technical report*/
00295                     MAG_SecVarSummation(LegendreFunction, TimedMagneticModel, *SphVariables, CoordSpherical, &MagneticResultsSphVar); /*Sum the Secular Variation Coefficients, Equations 13:15 , WMM Technical report  */
00296                     MAG_RotateMagneticVector(CoordSpherical, minimum, MagneticResultsSph, &MagneticResultsGeo); /* Map the computed Magnetic fields to Geodeitic coordinates Equation 16 , WMM Technical report */
00297                     MAG_RotateMagneticVector(CoordSpherical, minimum, MagneticResultsSphVar, &MagneticResultsGeoVar); /* Map the secular variation field components to Geodetic coordinates, Equation 17 , WMM Technical report*/
00298                     MAG_CalculateGeoMagneticElements(&MagneticResultsGeo, &GeoMagneticElements); /* Calculate the Geomagnetic elements, Equation 18 , WMM Technical report */
00299                     MAG_CalculateSecularVariationElements(MagneticResultsGeoVar, &GeoMagneticElements); /*Calculate the secular variation of each of the Geomagnetic elements, Equation 19, WMM Technical report*/
00300 
00301                     switch(ElementOption) {
00302                         case 1:
00303                             PrintElement = GeoMagneticElements.Decl; /*1. Angle between the magnetic field vector and true north, positive east*/
00304                             break;
00305                         case 2:
00306                             PrintElement = GeoMagneticElements.Incl; /*2. Angle between the magnetic field vector and the horizontal plane, positive downward*/
00307                             break;
00308                         case 3:
00309                             PrintElement = GeoMagneticElements.F; /*3. Magnetic Field Strength*/
00310                             break;
00311                         case 4:
00312                             PrintElement = GeoMagneticElements.H; /*4. Horizontal Magnetic Field Strength*/
00313                             break;
00314                         case 5:
00315                             PrintElement = GeoMagneticElements.X; /*5. Northern component of the magnetic field vector*/
00316                             break;
00317                         case 6:
00318                             PrintElement = GeoMagneticElements.Y; /*6. Eastern component of the magnetic field vector*/
00319                             break;
00320                         case 7:
00321                             PrintElement = GeoMagneticElements.Z; /*7. Downward component of the magnetic field vector*/
00322                             break;
00323                         case 8:
00324                             PrintElement = GeoMagneticElements.GV; /*8. The Grid Variation*/
00325                             break;
00326                         case 9:
00327                             PrintElement = GeoMagneticElements.Decldot; /*9. Yearly Rate of change in declination*/
00328                             break;
00329                         case 10:
00330                             PrintElement = GeoMagneticElements.Incldot; /*10. Yearly Rate of change in inclination*/
00331                             break;
00332                         case 11:
00333                             PrintElement = GeoMagneticElements.Fdot; /*11. Yearly rate of change in Magnetic field strength*/
00334                             break;
00335                         case 12:
00336                             PrintElement = GeoMagneticElements.Hdot; /*12. Yearly rate of change in horizontal field strength*/
00337                             break;
00338                         case 13:
00339                             PrintElement = GeoMagneticElements.Xdot; /*13. Yearly rate of change in the northern component*/
00340                             break;
00341                         case 14:
00342                             PrintElement = GeoMagneticElements.Ydot; /*14. Yearly rate of change in the eastern component*/
00343                             break;
00344                         case 15:
00345                             PrintElement = GeoMagneticElements.Zdot; /*15. Yearly rate of change in the downward component*/
00346                             break;
00347                         case 16:
00348                             PrintElement = GeoMagneticElements.GVdot;
00349                             /*16. Yearly rate of chnage in grid variation*/;
00350                             break;
00351                         default:
00352                             PrintElement = GeoMagneticElements.Decl; /* 1. Angle between the magnetic field vector and true north, positive east*/
00353                     }
00354 
00355                     if(Geoid->UseGeoid == 1)
00356                     {
00357                         if(PrintOption == 1) fprintf(fileout, "%5.2lf %6.2lf %8.4lf %7.2lf %10.2lf\n", minimum.phi, minimum.lambda, minimum.HeightAboveGeoid, StartDate.DecimalYear, PrintElement);
00358                         else printf("%5.2lf %6.2lf %8.4lf %7.2lf %10.2lf\n", minimum.phi, minimum.lambda, minimum.HeightAboveGeoid, StartDate.DecimalYear, PrintElement);
00359                     } else
00360                     {
00361                         if(PrintOption == 1) fprintf(fileout, "%5.2lf %6.2lf %8.4lf %7.2lf %10.2lf\n", minimum.phi, minimum.lambda, minimum.HeightAboveEllipsoid, StartDate.DecimalYear, PrintElement);
00362                         else printf("%5.2lf %6.2lf %8.4lf %7.2lf %10.2lf\n", minimum.phi, minimum.lambda, minimum.HeightAboveEllipsoid, StartDate.DecimalYear, PrintElement);
00363                     }
00364 
00365 
00366 
00367 
00368 
00369                 } /* year loop */
00370 
00371             } /*Longitude Loop */
00372 
00373         } /* Latitude Loop */
00374 
00375     } /* Altitude Loop */
00376     if(PrintOption == 1) fclose(fileout);
00377 
00378 
00379     MAG_FreeMagneticModelMemory(TimedMagneticModel);
00380     MAG_FreeLegendreMemory(LegendreFunction);
00381     MAG_FreeSphVarMemory(SphVariables);
00382 
00383     return TRUE;
00384 } /*MAG_Grid*/
00385 
00386 int MAG_SetDefaults(MAGtype_Ellipsoid *Ellip, MAGtype_Geoid *Geoid)
00387 
00388 /*
00389         Sets default values for WMM subroutines.
00390 
00391         UPDATES : Ellip
00392                         Geoid
00393 
00394         CALLS : none
00395  */
00396 {
00397 
00398     /* Sets WGS-84 parameters */
00399     Ellip->a = 6378.137; /*semi-major axis of the ellipsoid in */
00400     Ellip->b = 6356.7523142; /*semi-minor axis of the ellipsoid in */
00401     Ellip->fla = 1 / 298.257223563; /* flattening */
00402     Ellip->eps = sqrt(1 - (Ellip->b * Ellip->b) / (Ellip->a * Ellip->a)); /*first eccentricity */
00403     Ellip->epssq = (Ellip->eps * Ellip->eps); /*first eccentricity squared */
00404     Ellip->re = 6371.2; /* Earth's radius */
00405 
00406     /* Sets EGM-96 model file parameters */
00407     Geoid->NumbGeoidCols = 1441; /* 360 degrees of longitude at 15 minute spacing */
00408     Geoid->NumbGeoidRows = 721; /* 180 degrees of latitude  at 15 minute spacing */
00409     Geoid->NumbHeaderItems = 6; /* min, max lat, min, max long, lat, long spacing*/
00410     Geoid->ScaleFactor = 4; /* 4 grid cells per degree at 15 minute spacing  */
00411     Geoid->NumbGeoidElevs = Geoid->NumbGeoidCols * Geoid->NumbGeoidRows;
00412     Geoid->Geoid_Initialized = 0; /*  Geoid will be initialized only if this is set to zero */
00413     Geoid->UseGeoid = MAG_USE_GEOID;
00414 
00415     return TRUE;
00416 } /*MAG_SetDefaults */
00417 
00418 int MAG_robustReadMagneticModel_Large(char *filename, char *filenameSV, MAGtype_MagneticModel **MagneticModel, int array_size)
00419 {
00420     char line[MAXLINELENGTH], ModelName[] = "Enhanced Magnetic Model";/*Model Name must be no longer than 31 characters*/
00421     int n, nMax = 0, nMaxSV = 0, num_terms, a, epochlength=5, i;
00422     FILE *MODELFILE;
00423     MODELFILE = fopen(filename, "r");
00424     fgets(line, MAXLINELENGTH, MODELFILE);
00425     do
00426     {
00427         if(NULL == fgets(line, MAXLINELENGTH, MODELFILE))
00428             break;
00429         a = sscanf(line, "%d", &n);
00430         if(n > nMax && (n < 99999 && a == 1 && n > 0))
00431             nMax = n;
00432     } while(n < 99999 && a == 1);
00433     fclose(MODELFILE);
00434     MODELFILE = fopen(filenameSV, "r");
00435     n = 0;
00436     fgets(line, MAXLINELENGTH, MODELFILE);
00437     do
00438     {
00439         if(NULL == fgets(line, MAXLINELENGTH, MODELFILE))
00440             break;
00441         a = sscanf(line, "%d", &n);
00442         if(n > nMaxSV && (n < 99999 && a == 1 && n > 0))
00443             nMaxSV = n;
00444     } while(n < 99999 && a == 1);
00445     fclose(MODELFILE);
00446     num_terms = CALCULATE_NUMTERMS(nMax);
00447     *MagneticModel = MAG_AllocateModelMemory(num_terms);
00448     (*MagneticModel)->nMax = nMax;
00449     (*MagneticModel)->nMaxSecVar = nMaxSV;
00450     if(nMaxSV > 0) (*MagneticModel)->SecularVariationUsed = TRUE;
00451     for(i = 0; i < num_terms; i++) {
00452         (*MagneticModel)->Main_Field_Coeff_G[i] = 0;
00453         (*MagneticModel)->Main_Field_Coeff_H[i] = 0;
00454         (*MagneticModel)->Secular_Var_Coeff_G[i] = 0;
00455         (*MagneticModel)->Secular_Var_Coeff_H[i] = 0;
00456     }
00457     MAG_readMagneticModel_Large(filename, filenameSV, *MagneticModel);
00458     (*MagneticModel)->CoefficientFileEndDate = (*MagneticModel)->epoch + epochlength;
00459     strcpy((*MagneticModel)->ModelName, ModelName);
00460     (*MagneticModel)->EditionDate = (*MagneticModel)->epoch;
00461     return 1;
00462 } /*MAG_robustReadMagneticModel_Large*/
00463 
00464 int MAG_robustReadMagModels(char *filename, MAGtype_MagneticModel *(*magneticmodels)[], int array_size)
00465 {
00466     char line[MAXLINELENGTH];
00467     int n, nMax = 0, num_terms, a;
00468     FILE *MODELFILE;
00469     MODELFILE = fopen(filename, "r");
00470     fgets(line, MAXLINELENGTH, MODELFILE);
00471     if(line[0] == '%')
00472         MAG_readMagneticModel_SHDF(filename, magneticmodels, array_size);
00473     else if(array_size == 1)
00474     {
00475 
00476         do
00477         {
00478             if(NULL == fgets(line, MAXLINELENGTH, MODELFILE))
00479                 break;
00480             a = sscanf(line, "%d", &n);
00481             if(n > nMax && (n < 99999 && a == 1 && n > 0))
00482                 nMax = n;
00483         } while(n < 99999 && a == 1);
00484         num_terms = CALCULATE_NUMTERMS(nMax);
00485         (*magneticmodels)[0] = MAG_AllocateModelMemory(num_terms);
00486         (*magneticmodels)[0]->nMax = nMax;
00487         (*magneticmodels)[0]->nMaxSecVar = nMax;
00488         MAG_readMagneticModel(filename, (*magneticmodels)[0]);
00489         (*magneticmodels)[0]->CoefficientFileEndDate = (*magneticmodels)[0]->epoch + 5;
00490 
00491     } else return 0;
00492     fclose(MODELFILE);
00493     return 1;
00494 } /*MAG_robustReadMagModels*/
00495 
00496 /*End of Wrapper Functions*/
00497 
00498 /******************************************************************************
00499  ********************************User Interface********************************
00500  * This grouping consists of functions which interact with the directly with 
00501  * the user and are generally specific to the XXX_point.c, XXX_grid.c, and    
00502  * XXX_file.c programs. They deal with input from and output to the user.
00503  ******************************************************************************/
00504 
00505 void MAG_Error(int control)
00506 
00507 /*This prints WMM errors.
00508 INPUT     control     Error look up number
00509 OUTPUT    none
00510 CALLS : none
00511 
00512  */
00513 {
00514     switch(control) {
00515         case 1:
00516             printf("\nError allocating in MAG_LegendreFunctionMemory.\n");
00517             break;
00518         case 2:
00519             printf("\nError allocating in MAG_AllocateModelMemory.\n");
00520             break;
00521         case 3:
00522             printf("\nError allocating in MAG_InitializeGeoid\n");
00523             break;
00524         case 4:
00525             printf("\nError in setting default values.\n");
00526             break;
00527         case 5:
00528             printf("\nError initializing Geoid.\n");
00529             break;
00530         case 6:
00531             printf("\nError opening WMM.COF\n.");
00532             break;
00533         case 7:
00534             printf("\nError opening WMMSV.COF\n.");
00535             break;
00536         case 8:
00537             printf("\nError reading Magnetic Model.\n");
00538             break;
00539         case 9:
00540             printf("\nError printing Command Prompt introduction.\n");
00541             break;
00542         case 10:
00543             printf("\nError converting from geodetic co-ordinates to spherical co-ordinates.\n");
00544             break;
00545         case 11:
00546             printf("\nError in time modifying the Magnetic model\n");
00547             break;
00548         case 12:
00549             printf("\nError in Geomagnetic\n");
00550             break;
00551         case 13:
00552             printf("\nError printing user data\n");\
00553                         break;
00554         case 14:
00555             printf("\nError allocating in MAG_SummationSpecial\n");
00556             break;
00557         case 15:
00558             printf("\nError allocating in MAG_SecVarSummationSpecial\n");
00559             break;
00560         case 16:
00561             printf("\nError in opening EGM9615.BIN file\n");
00562             break;
00563         case 17:
00564             printf("\nError: Latitude OR Longitude out of range in MAG_GetGeoidHeight\n");
00565             break;
00566         case 18:
00567             printf("\nError allocating in MAG_PcupHigh\n");
00568             break;
00569         case 19:
00570             printf("\nError allocating in MAG_PcupLow\n");
00571             break;
00572         case 20:
00573             printf("\nError opening coefficient file\n");
00574             break;
00575         case 21:
00576             printf("\nError: UnitDepth too large\n");
00577             break;
00578         case 22:
00579             printf("\nYour system needs Big endian version of EGM9615.BIN.  \n");
00580             printf("Please download this file from http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml.  \n");
00581             printf("Replace the existing EGM9615.BIN file with the downloaded one\n");
00582             break;
00583     }
00584 } /*MAG_Error*/
00585 
00586 char MAG_GeomagIntroduction_EMM(MAGtype_MagneticModel *MagneticModel, char* VersionDate)
00587 {
00588     char ans = 'h';
00589     printf("\n\n Welcome to the Enhanced Magnetic Model (EMM) %d C-Program\n\n", (int) MagneticModel->epoch);
00590     printf("             --- Model Release Date: 15 Dec 2009 ---\n");
00591     printf("            --- Software Release Date: %s ---\n\n", VersionDate);
00592     printf("\n This program estimates the strength and direction of ");
00593     printf("\n Earth's main Magnetic field and crustal variation for a given point/area.");
00594     while(ans != 'c' && ans != 'C')
00595     {
00596         printf("\n Enter h for help and contact information or c to continue.");
00597         printf("\n >");
00598         scanf("%c%*[^\n]", &ans);
00599         getchar();
00600 
00601         if((ans == 'h') || (ans == 'H'))
00602         {
00603             printf("\n Help information ");
00604 
00605             printf("\n The Enhanced Magnetic Model (EMM) for %d", (int) MagneticModel->epoch);
00606             printf("\n is a model of Earth's main Magnetic and crustal field.  The EMM");
00607             printf("\n is recomputed every five (5) years, in years divisible by ");
00608             printf("\n five (i.e. 2005, 2010).  See the contact information below");
00609             printf("\n to obtain more information on the EMM and associated software.");
00610             printf("\n ");
00611             printf("\n Input required is the location in geodetic latitude and");
00612             printf("\n longitude (positive for northern latitudes and eastern ");
00613             printf("\n longitudes), geodetic altitude in meters, and the date of ");
00614             printf("\n interest in years.");
00615 
00616             printf("\n\n\n The program computes the estimated Magnetic Declination");
00617             printf("\n (Decl) which is sometimes called MagneticVAR, Inclination (Incl), Total");
00618             printf("\n Intensity (F or TI), Horizontal Intensity (H or HI), Vertical");
00619             printf("\n Intensity (Z), and Grid Variation (GV). Declination and Grid");
00620             printf("\n Variation are measured in units of degrees and are considered");
00621             printf("\n positive when east or north.  Inclination is measured in units");
00622             printf("\n of degrees and is considered positive when pointing down (into");
00623             printf("\n the Earth).  The WMM is reference to the WGS-84 ellipsoid and");
00624             printf("\n is valid for 5 years after the base epoch.");
00625 
00626             printf("\n\n\n It is very important to note that a degree and  order 720 model,");
00627             printf("\n such as EMM, describes the long  wavelength spatial Magnetic ");
00628             printf("\n fluctuations due to  Earth's core.  Also included in the EMM series");
00629             printf("\n models are intermediate and short wavelength spatial fluctuations ");
00630             printf("\n that originate in Earth's mantle and crust. Not included in");
00631             printf("\n the model are temporal fluctuations of Magnetospheric and ionospheric");
00632             printf("\n origin. On the days during and immediately following Magnetic storms,");
00633             printf("\n temporal fluctuations can cause substantial deviations of the Geomagnetic");
00634             printf("\n field  from model  values.  If the required  declination accuracy  is");
00635             printf("\n more stringent than the EMM  series of models provide, the user is");
00636             printf("\n advised to request special (regional or local) surveys be performed");
00637             printf("\n and models prepared. Please make requests of this nature to the");
00638             printf("\n National Geospatial-Intelligence Agency (NGA) at the address below.");
00639 
00640             printf("\n\n\n Contact Information");
00641 
00642             printf("\n  Software and Model Support");
00643             printf("\n  National Geophysical Data Center");
00644             printf("\n  NOAA EGC/2");
00645             printf("\n  325 Broadway");
00646             printf("\n  Boulder, CO 80303 USA");
00647             printf("\n  Attn: Manoj Nair or Stefan Maus");
00648             printf("\n  Phone:  (303) 497-4642 or -6522");
00649             printf("\n  Email:  Manoj.C.Nair@noaa.gov or Stefan.Maus@noaa.gov \n");
00650         }
00651     }
00652     return ans;
00653 }
00654 
00655 char MAG_GeomagIntroduction_WMM(MAGtype_MagneticModel *MagneticModel, char *VersionDate)
00656 /*Prints the introduction to the Geomagnetic program.  It needs the Magnetic model for the epoch.
00657 
00658  * INPUT  MagneticModel         : MAGtype_MagneticModel With Model epoch        (input)
00659   OUTPUT ans   (char)  user selection
00660   CALLS : none
00661  */
00662 {
00663     char help = 'h';
00664     char ans;
00665     printf("\n\n Welcome to the World Magnetic Model (WMM) %d C-Program\n\n", (int) MagneticModel->epoch);
00666     printf("              --- Model Release Date: 15 Dec 2009 ---\n");
00667     printf("            --- Software Release Date: %s ---\n\n", VersionDate);
00668     printf("\n This program estimates the strength and direction of ");
00669     printf("\n Earth's main Magnetic field for a given point/area.");
00670     while(help != 'c' && help != 'C')
00671     {
00672         printf("\n Enter h for help and contact information or c to continue.");
00673         printf("\n >");
00674         scanf("%c%*[^\n]", &help);
00675         getchar();
00676 
00677         if((help == 'h') || (help == 'H'))
00678         {
00679             printf("\n Help information ");
00680 
00681             printf("\n The World Magnetic Model (WMM) for %d", (int) MagneticModel->epoch);
00682             printf("\n is a model of Earth's main Magnetic field.  The WMM");
00683             printf("\n is recomputed every five (5) years, in years divisible by ");
00684             printf("\n five (i.e. 2005, 2010).  See the contact information below");
00685             printf("\n to obtain more information on the WMM and associated software.");
00686             printf("\n ");
00687             printf("\n Input required is the location in geodetic latitude and");
00688             printf("\n longitude (positive for northern latitudes and eastern ");
00689             printf("\n longitudes), geodetic altitude in meters, and the date of ");
00690             printf("\n interest in years.");
00691 
00692             printf("\n\n\n The program computes the estimated Magnetic Declination");
00693             printf("\n (Decl) which is sometimes called MagneticVAR, Inclination (Incl), Total");
00694             printf("\n Intensity (F or TI), Horizontal Intensity (H or HI), Vertical");
00695             printf("\n Intensity (Z), and Grid Variation (GV). Declination and Grid");
00696             printf("\n Variation are measured in units of degrees and are considered");
00697             printf("\n positive when east or north.  Inclination is measured in units");
00698             printf("\n of degrees and is considered positive when pointing down (into");
00699             printf("\n the Earth).  The WMM is reference to the WGS-84 ellipsoid and");
00700             printf("\n is valid for 5 years after the base epoch.");
00701 
00702             printf("\n\n\n It is very important to note that a  degree and  order 12 model,");
00703             printf("\n such as WMM, describes only the long  wavelength spatial Magnetic ");
00704             printf("\n fluctuations due to  Earth's core.  Not included in the WMM series");
00705             printf("\n models are intermediate and short wavelength spatial fluctuations ");
00706             printf("\n that originate in Earth's mantle and crust. Consequently, isolated");
00707             printf("\n angular errors at various  positions on the surface (primarily over");
00708             printf("\n land, along continental margins and  over oceanic sea-mounts, ridges and");
00709             printf("\n trenches) of several degrees may be expected.  Also not included in");
00710             printf("\n the model are temporal fluctuations of Magnetospheric and ionospheric");
00711             printf("\n origin. On the days during and immediately following Magnetic storms,");
00712             printf("\n temporal fluctuations can cause substantial deviations of the Geomagnetic");
00713             printf("\n field  from model  values.  If the required  declination accuracy  is");
00714             printf("\n more stringent than the WMM  series of models provide, the user is");
00715             printf("\n advised to request special (regional or local) surveys be performed");
00716             printf("\n and models prepared. Please make requests of this nature to the");
00717             printf("\n National Geospatial-Intelligence Agency (NGA) at the address below.");
00718 
00719             printf("\n\n\n Contact Information");
00720 
00721             printf("\n  Software and Model Support");
00722             printf("\n  National Geophysical Data Center");
00723             printf("\n  NOAA EGC/2");
00724             printf("\n  325 Broadway");
00725             printf("\n  Boulder, CO 80303 USA");
00726             printf("\n  Attn: Manoj Nair or Stefan Maus");
00727             printf("\n  Phone:  (303) 497-4642 or -6522");
00728             printf("\n  Email:  Manoj.C.Nair@noaa.gov or Stefan.Maus@noaa.gov \n");
00729         }
00730     }
00731     ans = help;
00732     return ans;
00733 } /*MAG_GeomagIntroduction_WMM*/
00734 
00735 
00736 int MAG_GetUserGrid(MAGtype_CoordGeodetic *minimum, MAGtype_CoordGeodetic *maximum, double *step_size, double *a_step_size, double *step_time, MAGtype_Date
00737         *StartDate, MAGtype_Date *EndDate, int *ElementOption, int *PrintOption, char *OutputFile, MAGtype_Geoid *Geoid)
00738 
00739 /* Prompts user to enter parameters to compute a grid - for use with the MAG_grid function
00740 Note: The user entries are not validated before here. The function populates the input variables & data structures.
00741 
00742 UPDATE : minimum Pointer to data structure with the following elements
00743                 double lambda; (longitude)
00744                 double phi; ( geodetic latitude)
00745                 double HeightAboveEllipsoid; (height above the ellipsoid (HaE) )
00746                 double HeightAboveGeoid;(height above the Geoid )
00747 
00748                 maximum   -same as the above -MAG_USE_GEOID
00749                 step_size  : double pointer : spatial step size, in decimal degrees
00750                 a_step_size : double pointer :  double altitude step size (km)
00751                 step_time : double pointer : time step size (decimal years)
00752                 StartDate : pointer to data structure with the following elements updates
00753                                         double DecimalYear;     ( decimal years )
00754                 EndDate :       Same as the above
00755 CALLS : none
00756 
00757 
00758  */
00759 {
00760     FILE *fileout;
00761     char filename[] = "GridProgramDirective.txt";
00762     char buffer[20];
00763     int dummy;
00764 
00765     printf("Please Enter Minimum Latitude (in decimal degrees):\n");
00766     fgets(buffer, 20, stdin);
00767     sscanf(buffer, "%lf", &minimum->phi);
00768     strcpy(buffer, "");
00769     printf("Please Enter Maximum Latitude (in decimal degrees):\n");
00770     fgets(buffer, 20, stdin);
00771     sscanf(buffer, "%lf", &maximum->phi);
00772     strcpy(buffer, "");
00773     printf("Please Enter Minimum Longitude (in decimal degrees):\n");
00774     fgets(buffer, 20, stdin);
00775     sscanf(buffer, "%lf", &minimum->lambda);
00776     strcpy(buffer, "");
00777     printf("Please Enter Maximum Longitude (in decimal degrees):\n");
00778     fgets(buffer, 20, stdin);
00779     sscanf(buffer, "%lf", &maximum->lambda);
00780     strcpy(buffer, "");
00781     printf("Please Enter Step Size (in decimal degrees):\n");
00782     fgets(buffer, 20, stdin);
00783     sscanf(buffer, "%lf", step_size);
00784     strcpy(buffer, "");
00785     printf("Select height (default : above MSL) \n1. Above Mean Sea Level\n2. Above WGS-84 Ellipsoid \n");
00786     fgets(buffer, 20, stdin);
00787     sscanf(buffer, "%d", &dummy);
00788     if(dummy == 2) Geoid->UseGeoid = 0;
00789     else Geoid->UseGeoid = 1;
00790     strcpy(buffer, "");
00791     if(Geoid->UseGeoid == 1)
00792     {
00793         printf("Please Enter Minimum Height above MSL (in km):\n");
00794         fgets(buffer, 20, stdin);
00795         sscanf(buffer, "%lf", &minimum->HeightAboveGeoid);
00796         strcpy(buffer, "");
00797         printf("Please Enter Maximum Height above MSL (in km):\n");
00798         fgets(buffer, 20, stdin);
00799         sscanf(buffer, "%lf", &maximum->HeightAboveGeoid);
00800         strcpy(buffer, "");
00801         printf("Please Enter height step size (in km):\n");
00802         fgets(buffer, 20, stdin);
00803         sscanf(buffer, "%lf", a_step_size);
00804         strcpy(buffer, "");
00805     } else
00806     {
00807         printf("Please Enter Minimum Height above the WGS-84 Ellipsoid (in km):\n");
00808         fgets(buffer, 20, stdin);
00809         sscanf(buffer, "%lf", &minimum->HeightAboveGeoid);
00810         minimum->HeightAboveEllipsoid = minimum->HeightAboveGeoid;
00811         strcpy(buffer, "");
00812         printf("Please Enter Maximum Height above the WGS-84 Ellipsoid (in km):\n");
00813         fgets(buffer, 20, stdin);
00814         sscanf(buffer, "%lf", &maximum->HeightAboveGeoid);
00815         maximum->HeightAboveEllipsoid = minimum->HeightAboveGeoid;
00816         strcpy(buffer, "");
00817         printf("Please Enter height step size (in km):\n");
00818         fgets(buffer, 20, stdin);
00819         sscanf(buffer, "%lf", a_step_size);
00820         strcpy(buffer, "");
00821     }
00822     printf("\nPlease Enter the decimal year starting time:\n");
00823     fgets(buffer, 20, stdin);
00824     sscanf(buffer, "%lf", &StartDate->DecimalYear);
00825     strcpy(buffer, "");
00826     printf("Please Enter the decimal year ending time:\n");
00827     fgets(buffer, 20, stdin);
00828     sscanf(buffer, "%lf", &EndDate->DecimalYear);
00829     strcpy(buffer, "");
00830     printf("Please Enter the time step size:\n");
00831     fgets(buffer, 20, stdin);
00832     sscanf(buffer, "%lf", step_time);
00833     strcpy(buffer, "");
00834     printf("Enter a geomagnetic element to print. Your options are :\n");
00835     printf(" 1. Declination     9.   Ddot\n 2. Inclination      10. Idot\n 3. F         11. Fdot\n 4. H         12. Hdot\n 5. X         13. Xdot\n 6. Y         14. Ydot\n 7. Z         15. Zdot\n 8. GV                16. GVdot\n");
00836     fgets(buffer, 20, stdin);
00837     sscanf(buffer, "%d", ElementOption);
00838     strcpy(buffer, "");
00839     printf("Select output :\n");
00840     printf(" 1. Print to a file \n 2. Print to Screen\n");
00841     fgets(buffer, 20, stdin);
00842     sscanf(buffer, "%d", PrintOption);
00843     strcpy(buffer, "");
00844     fileout = fopen(filename, "a");
00845     if(*PrintOption == 1)
00846     {
00847         printf("Please enter output filename\nfor default ('GridResults.txt') press enter:\n");
00848         fgets(buffer, 20, stdin);
00849         if(strlen(buffer) <= 1)
00850         {
00851             strcpy(OutputFile, "GridResults.txt");
00852             fprintf(fileout, "\nResults printed in: GridResults.txt\n");
00853             strcpy(OutputFile, "GridResults.txt");
00854         } else
00855         {
00856             sscanf(buffer, "%s", OutputFile);
00857             fprintf(fileout, "\nResults printed in: %s\n", OutputFile);
00858         }
00859         /*strcpy(OutputFile, buffer);*/
00860         strcpy(buffer, "");
00861         /*sscanf(buffer, "%s", OutputFile);*/
00862     } else
00863         fprintf(fileout, "\nResults printed in Console\n");
00864     fprintf(fileout, "Minimum Latitude: %lf\t\tMaximum Latitude: %lf\t\tStep Size: %lf\nMinimum Longitude: %lf\t\tMaximum Longitude: %lf\t\tStep Size: %lf\n", minimum->phi, maximum->phi, *step_size, minimum->lambda, maximum->lambda, *step_size);
00865     if(Geoid->UseGeoid == 1)
00866         fprintf(fileout, "Minimum Altitude above MSL: %lf\tMaximum Altitude above MSL: %lf\tStep Size: %lf\n", minimum->HeightAboveGeoid, maximum->HeightAboveGeoid, *a_step_size);
00867     else
00868         fprintf(fileout, "Minimum Altitude above MSL: %lf\tMaximum Altitude above WGS-84 Ellipsoid: %lf\tStep Size: %lf\n", minimum->HeightAboveEllipsoid, maximum->HeightAboveEllipsoid, *a_step_size);
00869     fprintf(fileout, "Starting Date: %lf\t\tEnding Date: %lf\t\tStep Time: %lf\n\n\n", StartDate->DecimalYear, EndDate->DecimalYear, *step_time);
00870     fclose(fileout);
00871     return TRUE;
00872 }
00873 
00874 int MAG_GetUserInput(MAGtype_MagneticModel *MagneticModel, MAGtype_Geoid *Geoid, MAGtype_CoordGeodetic *CoordGeodetic, MAGtype_Date *MagneticDate)
00875 
00876 /*
00877 This prompts the user for coordinates, and accepts many entry formats.
00878 It takes the MagneticModel and Geoid as input and outputs the Geographic coordinates and Date as objects.
00879 Returns 0 when the user wants to exit and 1 if the user enters valid input data.
00880 INPUT :  MagneticModel  : Data structure with the following elements used here
00881                         double epoch;       Base time of Geomagnetic model epoch (yrs)
00882                 : Geoid Pointer to data structure MAGtype_Geoid (used for converting HeightAboveGeoid to HeightABoveEllipsoid
00883 
00884 OUTPUT: CoordGeodetic : Pointer to data structure. Following elements are updated
00885                         double lambda; (longitude)
00886                         double phi; ( geodetic latitude)
00887                         double HeightAboveEllipsoid; (height above the ellipsoid (HaE) )
00888                         double HeightAboveGeoid;(height above the Geoid )
00889 
00890                 MagneticDate : Pointer to data structure MAGtype_Date with the following elements updated
00891                         int     Year; (If user directly enters decimal year this field is not populated)
00892                         int     Month;(If user directly enters decimal year this field is not populated)
00893                         int     Day; (If user directly enters decimal year this field is not populated)
00894                         double DecimalYear;      decimal years
00895 
00896 CALLS:  MAG_DMSstringToDegree(buffer, &CoordGeodetic->lambda); (The program uses this to convert the string into a decimal longitude.)
00897                 MAG_ValidateDMSstringlong(buffer, Error_Message)
00898                 MAG_ValidateDMSstringlat(buffer, Error_Message)
00899                 MAG_Warnings
00900                 MAG_ConvertGeoidToEllipsoidHeight
00901                 MAG_DateToYear
00902 
00903  */
00904 {
00905     char Error_Message[255];
00906     char buffer[40];
00907     char tmp;
00908     int i, j, a, b, c, done = 0;
00909     strcpy(buffer, ""); /*Clear the input    */
00910     printf("\nPlease enter latitude");
00911     printf("\nNorth Latitude positive, For example:");
00912     printf("\n30, 30, 30 (D,M,S) or 30.508 (Decimal Degrees) (both are north)\n");
00913     fgets(buffer, 40, stdin);
00914     for(i = 0, done = 0, j = 0; i <= 40 && !done; i++)
00915     {
00916         if(buffer[i] == '.')
00917         {
00918             j = sscanf(buffer, "%lf", &CoordGeodetic->phi);
00919             if(j == 1)
00920                 done = 1;
00921             else
00922                 done = -1;
00923         }
00924         if(buffer[i] == ',')
00925         {
00926             if(MAG_ValidateDMSstringlat(buffer, Error_Message))
00927             {
00928                 MAG_DMSstringToDegree(buffer, &CoordGeodetic->phi);
00929                 done = 1;
00930             } else
00931                 done = -1;
00932         }
00933         if(buffer[i] == ' ')/* This detects if there is a ' ' somewhere in the string,
00934                 if there is the program tries to interpret the input as Degrees Minutes Seconds.*/
00935         {
00936             if(MAG_ValidateDMSstringlat(buffer, Error_Message))
00937             {
00938                 MAG_DMSstringToDegree(buffer, &CoordGeodetic->phi);
00939                 done = 1;
00940             } else
00941                 done = -1;
00942         }
00943         if(buffer[i] == '\0' || done == -1)
00944         {
00945             if(MAG_ValidateDMSstringlat(buffer, Error_Message) && done != -1)
00946             {
00947                 sscanf(buffer, "%lf", &CoordGeodetic->phi);
00948                 done = 1;
00949             } else
00950             {
00951                 printf("%s", Error_Message);
00952                 strcpy(buffer, "");
00953                 printf("\nError encountered, please re-enter as '(-)DDD,MM,SS' or in Decimal Degrees DD.ddd:\n");
00954                 fgets(buffer, 40, stdin);
00955                 i = -1;
00956                 done = 0;
00957             }
00958         }
00959     }
00960     strcpy(buffer, ""); /*Clear the input*/
00961     printf("\nPlease enter longitude");
00962     printf("\nEast longitude positive, West negative.  For example:");
00963     printf("\n-100.5 or -100, 30, 0 for 100.5 degrees west\n");
00964     fgets(buffer, 40, stdin);
00965     for(i = 0, done = 0, j = 0; i <= 40 && !done; i++)/*This for loop determines how the user is trying to enter their data, and makes sure that it is copied into the correct location*/
00966     {
00967         if(buffer[i] == '.') /*This detects if there is a '.' somewhere in the string, if there is the program tries to interpret the input as a double, and copies it to the longitude*/
00968         {
00969             j = sscanf(buffer, "%lf", &CoordGeodetic->lambda);
00970             if(j == 1)
00971                 done = 1; /*This control ends the loop*/
00972             else
00973                 done = -1; /*This copies an end string into the buffer so that the user is sent to the Re-enter input message*/
00974         }
00975         if(buffer[i] == ',')/*This detects if there is a ',' somewhere in the string, if there is the program tries to interpret the input as Degrees, Minutes, Seconds.*/
00976         {
00977             if(MAG_ValidateDMSstringlong(buffer, Error_Message))
00978             {
00979                 MAG_DMSstringToDegree(buffer, &CoordGeodetic->lambda); /*The program uses this to convert the string into a decimal longitude.*/
00980                 done = 1;
00981             } else
00982                 done = -1;
00983         }
00984         if(buffer[i] == ' ')/* This detects if there is a ' ' somewhere in the string, if there is the program tries to interpret the input as Degrees Minutes Seconds.*/
00985         {
00986             if(MAG_ValidateDMSstringlong(buffer, Error_Message))
00987             {
00988                 MAG_DMSstringToDegree(buffer, &CoordGeodetic->lambda);
00989                 done = 1;
00990             } else
00991                 done = -1;
00992         }
00993         if(buffer[i] == '\0' || done == -1) /*If the program reaches the end of the string before finding a "." or a "," or if its been sent by an error it does this*/
00994         {
00995             MAG_ValidateDMSstringlong(buffer, Error_Message); /*The program attempts to determine if all the characters in the string are legal, and then tries to interpret the string as a simple degree entry, like "0", or "67"*/
00996             if(MAG_ValidateDMSstringlong(buffer, Error_Message) && done != -1)
00997             {
00998                 sscanf(buffer, "%lf", &CoordGeodetic->lambda);
00999                 done = 1;
01000             } else /*The string is neither DMS, a decimal degree, or a simple degree input, or has some error*/
01001             {
01002                 printf("%s", Error_Message);
01003                 strcpy(buffer, ""); /*Clear the input*/
01004                 printf("\nError encountered, please re-enter as '(-)DDD,MM,SS' or in Decimal Degrees DD.ddd:\n"); /*Request new input*/
01005                 fgets(buffer, 40, stdin);
01006                 i = -1; /*Restart the loop, at the end of this loop i will be incremented to 0, effectively restarting the loop*/
01007                 done = 0;
01008             }
01009         }
01010     }
01011     printf("\nPlease enter height above mean sea level (in kilometers):\n[For height above WGS-84 Ellipsoid prefix E, for example (E20.1)]\n");
01012     done = 0;
01013     while(!done)
01014     {
01015         strcpy(buffer, "");
01016         fgets(buffer, 40, stdin);
01017         j = 0;
01018         if(buffer[0] == 'e' || buffer[0] == 'E') /* User entered height above WGS-84 ellipsoid, copy it to CoordGeodetic->HeightAboveEllipsoid */
01019         {
01020             j = sscanf(buffer, "%c%lf", &tmp, &CoordGeodetic->HeightAboveEllipsoid);
01021             if(j == 2)
01022                 j = 1;
01023             Geoid->UseGeoid = 0;
01024             //CoordGeodetic->HeightAboveGeoid = CoordGeodetic->HeightAboveEllipsoid;//Would probably be preferable if we had a way to convert it in this direction.
01025         } else /* User entered height above MSL, convert it to the height above WGS-84 ellipsoid */
01026         {
01027             Geoid->UseGeoid = 1;
01028             j = sscanf(buffer, "%lf", &CoordGeodetic->HeightAboveGeoid);
01029             MAG_ConvertGeoidToEllipsoidHeight(CoordGeodetic, Geoid);
01030         }
01031         if(j == 1)
01032             done = 1;
01033         else
01034             printf("\nIllegal Format, please re-enter as '(-)HHH.hhh:'\n");
01035         if(CoordGeodetic->HeightAboveEllipsoid < -10.0 && done == 1)
01036             switch(MAG_Warnings(3, CoordGeodetic->HeightAboveEllipsoid, MagneticModel)) {
01037                 case 0:
01038                     return 0;
01039                 case 1:
01040                     done = 0;
01041                     printf("Please enter height above sea level (in kilometers):\n");
01042                     break;
01043                 case 2:
01044                     break;
01045             }
01046     }
01047     strcpy(buffer, "");
01048     printf("\nPlease enter the decimal year or calendar date\n (YYYY.yyy, MM DD YYYY or MM/DD/YYYY):\n");
01049     fgets(buffer, 40, stdin);
01050     for(i = 0, done = 0; i <= 40 && !done; i++)
01051     {
01052         if(buffer[i] == '.')
01053         {
01054             j = sscanf(buffer, "%lf", &MagneticDate->DecimalYear);
01055             if(j == 1)
01056                 done = 1;
01057             else
01058                 buffer[i] = '\0';
01059         }
01060         if(buffer[i] == '/')
01061         {
01062             sscanf(buffer, "%d/%d/%d", &MagneticDate->Month, &MagneticDate->Day, &MagneticDate->Year);
01063             if(!MAG_DateToYear(MagneticDate, Error_Message))
01064             {
01065                 printf("%s", Error_Message);
01066                 printf("\nPlease re-enter Date in MM/DD/YYYY or MM DD YYYY format, or as a decimal year\n");
01067                 fgets(buffer, 40, stdin);
01068                 i = 0;
01069             } else
01070                 done = 1;
01071         }
01072         if((buffer[i] == ' ' && buffer[i + 1] != '/') || buffer[i] == '\0')
01073         {
01074             if(3 == sscanf(buffer, "%d %d %d", &a, &b, &c))
01075             {
01076                 MagneticDate->Month = a;
01077                 MagneticDate->Day = b;
01078                 MagneticDate->Year = c;
01079                 MagneticDate->DecimalYear = 99999;
01080             } else if(1 == sscanf(buffer, "%d %d %d", &a, &b, &c))
01081             {
01082                 MagneticDate->DecimalYear = a;
01083                 done = 1;
01084             }
01085             if(!(MagneticDate->DecimalYear == a))
01086             {
01087                 if(!MAG_DateToYear(MagneticDate, Error_Message))
01088                 {
01089                     printf("%s", Error_Message);
01090                     strcpy(buffer, "");
01091                     printf("\nError encountered, please re-enter Date in MM/DD/YYYY or MM DD YYYY format, or as a decimal year\n");
01092                     fgets(buffer, 40, stdin);
01093                     i = -1;
01094                 } else
01095                     done = 1;
01096             }
01097         }
01098         if(buffer[i] == '\0' && i != -1 && done != 1)
01099         {
01100             strcpy(buffer, "");
01101             printf("\nError encountered, please re-enter as MM/DD/YYYY, MM DD YYYY, or as YYYY.yyy:\n");
01102             fgets(buffer, 40, stdin);
01103             i = -1;
01104         }
01105         if(done)
01106         {
01107             if(MagneticDate->DecimalYear > MagneticModel->CoefficientFileEndDate || MagneticDate->DecimalYear < MagneticModel->epoch)
01108             {
01109                 switch(MAG_Warnings(4, MagneticDate->DecimalYear, MagneticModel)) {
01110                     case 0:
01111                         return 0;
01112                     case 1:
01113                         done = 0;
01114                         i = -1;
01115                         strcpy(buffer, "");
01116                         printf("\nPlease enter the decimal year or calendar date\n (YYYY.yyy, MM DD YYYY or MM/DD/YYYY):\n");
01117                         fgets(buffer, 40, stdin);
01118                         break;
01119                     case 2:
01120                         break;
01121                 }
01122             }
01123         }
01124     }
01125     return 1;
01126 } /*MAG_GetUserInput*/
01127 
01128 void MAG_PrintUserData(MAGtype_GeoMagneticElements GeomagElements, MAGtype_CoordGeodetic SpaceInput, MAGtype_Date TimeInput, MAGtype_MagneticModel *MagneticModel, MAGtype_Geoid *Geoid)
01129 /* This function prints the results in  Geomagnetic Elements for a point calculation. It takes the calculated
01130  *  Geomagnetic elements "GeomagElements" as input.
01131  *  As well as the coordinates, date, and Magnetic Model.
01132 INPUT :  GeomagElements : Data structure MAGtype_GeoMagneticElements with the following elements
01133                         double Decl; (Angle between the magnetic field vector and true north, positive east)
01134                         double Incl; Angle between the magnetic field vector and the horizontal plane, positive down
01135                         double F; Magnetic Field Strength
01136                         double H; Horizontal Magnetic Field Strength
01137                         double X; Northern component of the magnetic field vector
01138                         double Y; Eastern component of the magnetic field vector
01139                         double Z; Downward component of the magnetic field vector4
01140                         double Decldot; Yearly Rate of change in declination
01141                         double Incldot; Yearly Rate of change in inclination
01142                         double Fdot; Yearly rate of change in Magnetic field strength
01143                         double Hdot; Yearly rate of change in horizontal field strength
01144                         double Xdot; Yearly rate of change in the northern component
01145                         double Ydot; Yearly rate of change in the eastern component
01146                         double Zdot; Yearly rate of change in the downward component
01147                         double GVdot;Yearly rate of chnage in grid variation
01148         CoordGeodetic Pointer to the  data  structure with the following elements
01149                         double lambda; (longitude)
01150                         double phi; ( geodetic latitude)
01151                         double HeightAboveEllipsoid; (height above the ellipsoid (HaE) )
01152                         double HeightAboveGeoid;(height above the Geoid )
01153         TimeInput :  data structure MAGtype_Date with the following elements
01154                         int     Year;
01155                         int     Month;
01156                         int     Day;
01157                         double DecimalYear;      decimal years
01158         MagneticModel :  data structure with the following elements
01159                         double EditionDate;
01160                         double epoch;       Base time of Geomagnetic model epoch (yrs)
01161                         char  ModelName[20];
01162                         double *Main_Field_Coeff_G;          C - Gauss coefficients of main geomagnetic model (nT)
01163                         double *Main_Field_Coeff_H;          C - Gauss coefficients of main geomagnetic model (nT)
01164                         double *Secular_Var_Coeff_G;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01165                         double *Secular_Var_Coeff_H;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01166                         int nMax;  Maximum degree of spherical harmonic model
01167                         int nMaxSecVar; Maxumum degree of spherical harmonic secular model
01168                         int SecularVariationUsed; Whether or not the magnetic secular variation vector will be needed by program
01169         OUTPUT : none
01170  */
01171 {
01172     char DeclString[100];
01173     char InclString[100];
01174     MAG_DegreeToDMSstring(GeomagElements.Incl, 2, InclString);
01175     if(GeomagElements.H < 5000 && GeomagElements.H > 1000)
01176         MAG_Warnings(1, GeomagElements.H, MagneticModel);
01177     if(GeomagElements.H < 1000)
01178         MAG_Warnings(2, GeomagElements.H, MagneticModel);
01179     if(MagneticModel->SecularVariationUsed == TRUE)
01180     {
01181         MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
01182         printf("\n Results For \n\n");
01183         if(SpaceInput.phi < 0)
01184             printf("Latitude    %.2lfS\n", -SpaceInput.phi);
01185         else
01186             printf("Latitude    %.2lfN\n", SpaceInput.phi);
01187         if(SpaceInput.lambda < 0)
01188             printf("Longitude   %.2lfW\n", -SpaceInput.lambda);
01189         else
01190             printf("Longitude   %.2lfE\n", SpaceInput.lambda);
01191         if(Geoid->UseGeoid == 1)
01192             printf("Altitude:   %.2lf Kilometers above mean sea level\n", SpaceInput.HeightAboveGeoid);
01193         else
01194             printf("Altitude:   %.2lf Kilometers above the WGS-84 ellipsoid\n", SpaceInput.HeightAboveEllipsoid);
01195         printf("Date:           %.1lf\n", TimeInput.DecimalYear);
01196         printf("\n              Main Field\t\t\tSecular Change\n");
01197         printf("F       =       %-9.1lf nT\t\t  Fdot = %.1lf\tnT/yr\n", GeomagElements.F, GeomagElements.Fdot);
01198         printf("H       =       %-9.1lf nT\t\t  Hdot = %.1lf\tnT/yr\n", GeomagElements.H, GeomagElements.Hdot);
01199         printf("X       =       %-9.1lf nT\t\t  Xdot = %.1lf\tnT/yr\n", GeomagElements.X, GeomagElements.Xdot);
01200         printf("Y       =       %-9.1lf nT\t\t  Ydot = %.1lf\tnT/yr\n", GeomagElements.Y, GeomagElements.Ydot);
01201         printf("Z       =       %-9.1lf nT\t\t  Zdot = %.1lf\tnT/yr\n", GeomagElements.Z, GeomagElements.Zdot);
01202         if(GeomagElements.Decl < 0)
01203             printf("Decl        =%20s  (WEST)\t  Ddot = %.1lf\tMin/yr\n", DeclString, 60 * GeomagElements.Decldot);
01204         else
01205             printf("Decl        =%20s  (EAST)\t  Ddot = %.1lf\tMin/yr\n", DeclString, 60 * GeomagElements.Decldot);
01206         if(GeomagElements.Incl < 0)
01207             printf("Incl        =%20s  (UP)\t  Idot = %.1lf\tMin/yr\n", InclString, 60 * GeomagElements.Incldot);
01208         else
01209             printf("Incl        =%20s  (DOWN)\t  Idot = %.1lf\tMin/yr\n", InclString, 60 * GeomagElements.Incldot);
01210     } else
01211     {
01212         MAG_DegreeToDMSstring(GeomagElements.Decl, 2, DeclString);
01213         printf("\n Results For \n\n");
01214         if(SpaceInput.phi < 0)
01215             printf("Latitude    %.2lfS\n", -SpaceInput.phi);
01216         else
01217             printf("Latitude    %.2lfN\n", SpaceInput.phi);
01218         if(SpaceInput.lambda < 0)
01219             printf("Longitude   %.2lfW\n", -SpaceInput.lambda);
01220         else
01221             printf("Longitude   %.2lfE\n", SpaceInput.lambda);
01222         if(Geoid->UseGeoid == 1)
01223             printf("Altitude:   %.2lf Kilometers above MSL\n", SpaceInput.HeightAboveGeoid);
01224         else
01225             printf("Altitude:   %.2lf Kilometers above WGS-84 Ellipsoid\n", SpaceInput.HeightAboveEllipsoid);
01226         printf("Date:           %.1lf\n", TimeInput.DecimalYear);
01227         printf("\n      Main Field\n");
01228         printf("F       =       %-9.1lf nT\n", GeomagElements.F);
01229         printf("H       =       %-9.1lf nT\n", GeomagElements.H);
01230         printf("X       =       %-9.1lf nT\n", GeomagElements.X);
01231         printf("Y       =       %-9.1lf nT\n", GeomagElements.Y);
01232         printf("Z       =       %-9.1lf nT\n", GeomagElements.Z);
01233         if(GeomagElements.Decl < 0)
01234             printf("Decl        =%20s  (WEST)\n", DeclString);
01235         else
01236             printf("Decl        =%20s  (EAST)\n", DeclString);
01237         if(GeomagElements.Incl < 0)
01238             printf("Incl        =%20s  (UP)\n", InclString);
01239         else
01240             printf("Incl        =%20s  (DOWN)\n", InclString);
01241     }
01242 
01243     if(SpaceInput.phi <= -55 || SpaceInput.phi >= 55)
01244         /* Print Grid Variation */
01245     {
01246         MAG_DegreeToDMSstring(GeomagElements.GV, 2, InclString);
01247         printf("\n\n Grid variation =%20s\n", InclString);
01248     }
01249 
01250 }/*MAG_PrintUserData*/
01251 
01252 int MAG_ValidateDMSstringlat(char *input, char *Error)
01253 
01254 /* Validates a latitude DMS string, and returns 1 for a success and returns 0 for a failure.
01255 It copies an error message to the Error string in the event of a failure.
01256 
01257 INPUT : input (DMS string)
01258 OUTPUT : Error : Error string
01259 CALLS : none
01260  */
01261 {
01262     int degree, minute, second, j = 0, n, max_minute = 60, max_second = 60;
01263     int i;
01264     degree = -1000;
01265     minute = -1;
01266     second = -1;
01267     n = (int) strlen(input);
01268 
01269     for(i = 0; i <= n - 1; i++) //tests for legal characters
01270     {
01271         if((input[i] < '0' || input[i] > '9') && (input[i] != ',' && input[i] != ' ' && input[i] != '-' && input[i] != '\0' && input[i] != '\n'))
01272         {
01273             strcpy(Error, "\nError: Input contains an illegal character, legal characters for Degree, Minute, Second format are:\n '0-9' ',' '-' '[space]' '[Enter]'\n");
01274             return FALSE;
01275         }
01276         if(input[i] == ',')
01277             j++;
01278     }
01279     if(j == 2)
01280         j = sscanf(input, "%d, %d, %d", &degree, &minute, &second); //tests for legal formatting and range
01281     else
01282         j = sscanf(input, "%d %d %d", &degree, &minute, &second);
01283     if(j == 1)
01284     {
01285         minute = 0;
01286         second = 0;
01287         j = 3;
01288     }
01289     if(j != 3)
01290     {
01291         strcpy(Error, "\nError: Not enough numbers used for Degrees, Minutes, Seconds format\n or they were incorrectly formatted\n The legal format is DD,MM,SS or DD MM SS\n");
01292         return FALSE;
01293     }
01294     if(degree > 90 || degree < -90)
01295     {
01296         strcpy(Error, "\nError: Degree input is outside legal range for latitude\n The legal range is from -90 to 90\n");
01297         return FALSE;
01298     }
01299     if(abs(degree) == 90)
01300         max_minute = 0;
01301     if(minute > max_minute || minute < 0)
01302     {
01303         strcpy(Error, "\nError: Minute input is outside legal range\n The legal minute range is from 0 to 60\n");
01304         return FALSE;
01305     }
01306     if(minute == max_minute)
01307         max_second = 0;
01308     if(second > max_second || second < 0)
01309     {
01310         strcpy(Error, "\nError: Second input is outside legal range\n The legal second range is from 0 to 60\n");
01311         return FALSE;
01312     }
01313     return TRUE;
01314 } /*MAG_ValidateDMSstringlat*/
01315 
01316 int MAG_ValidateDMSstringlong(char *input, char *Error)
01317 
01318 /*Validates a given longitude DMS string and returns 1 for a success and returns 0 for a failure.
01319 It copies an error message to the Error string in the event of a failure.
01320 
01321 INPUT : input (DMS string)
01322 OUTPUT : Error : Error string
01323 CALLS : none
01324 
01325  */
01326 {
01327     int degree, minute, second, j = 0, max_minute = 60, max_second = 60, n;
01328     int i;
01329     degree = -1000;
01330     minute = -1;
01331     second = -1;
01332     n = (int) strlen(input);
01333 
01334     for(i = 0; i <= n - 1; i++) //tests for legal characters
01335     {
01336         if((input[i] < '0' || input[i] > '9') && (input[i] != ',' && input[i] != ' ' && input[i] != '-' && input[i] != '\0' && input[i] != '\n'))
01337         {
01338             strcpy(Error, "\nError: Input contains an illegal character, legal characters for Degree, Minute, Second format are:\n '0-9' ',' '-' '[space]' '[Enter]'\n");
01339             return FALSE;
01340         }
01341         if(input[i] == ',')
01342             j++;
01343     }
01344     if(j >= 2)
01345         j = sscanf(input, "%d, %d, %d", &degree, &minute, &second); //tests for legal formatting and range
01346     else
01347         j = sscanf(input, "%d %d %d", &degree, &minute, &second);
01348     if(j == 1)
01349     {
01350         minute = 0;
01351         second = 0;
01352         j = 3;
01353     }
01354     if(j != 3)
01355     {
01356         strcpy(Error, "\nError: Not enough numbers read for Degrees, Minutes, Seconds format\n or they were incorrectly formatted\n The legal format is DD,MM,SS or DD MM SS\n");
01357         return FALSE;
01358     }
01359     sscanf(input, "%d, %d, %d", &degree, &minute, &second); //tests for legal formatting and range
01360     if(degree > 360 || degree < -180)
01361     {
01362         strcpy(Error, "\nError: Degree input is outside legal range\n The legal range is from -180 to 360\n");
01363         return FALSE;
01364     }
01365     if(degree == 360 || degree == -180)
01366         max_minute = 0;
01367     if(minute > max_minute || minute < 0)
01368     {
01369         strcpy(Error, "\nError: Minute input is outside legal range\n The legal minute range is from 0 to 60\n");
01370         return FALSE;
01371     }
01372     if(minute == max_minute)
01373         max_second = 0;
01374     if(second > max_second || second < 0)
01375     {
01376         strcpy(Error, "\nError: Second input is outside legal range\n The legal second range is from 0 to 60\n");
01377         return FALSE;
01378     }
01379     return TRUE;
01380 } /*MAG_ValidateDMSstringlong*/
01381 
01382 int MAG_Warnings(int control, double value, MAGtype_MagneticModel *MagneticModel)
01383 
01384 /*Return value 0 means end program, Return value 1 means get new data, Return value 2 means continue.
01385   This prints a warning to the screen determined by the control integer. It also takes the value of the parameter causing the warning as a double.  This is unnecessary for some warnings.
01386   It requires the MagneticModel to determine the current epoch.
01387 
01388  INPUT control :int : (Warning number)
01389                 value  : double: Magnetic field strength
01390                 MagneticModel
01391 OUTPUT : none
01392 CALLS : none
01393 
01394  */
01395 {
01396     char ans[20];
01397     strcpy(ans, "");
01398     switch(control) {
01399         case 1://Horizontal Field strength low
01400             printf("\nWarning: The Horizontal Field strength at this location is only %lf\n", value);
01401             printf("    Compass readings have large uncertainties in areas where H\n    is smaller than 5000 nT\n");
01402             printf("Press enter to continue...\n");
01403             fgets(ans, 20, stdin);
01404             break;
01405         case 2://Horizontal Field strength very low
01406             printf("\nWarning: The Horizontal Field strength at this location is only %lf\n", value);
01407             printf("    Compass readings have VERY LARGE uncertainties in areas where\n where H is smaller than 1000 nT\n");
01408             printf("Press enter to continue...\n");
01409             fgets(ans, 20, stdin);
01410             break;
01411         case 3:/*Elevation outside the recommended range.*/
01412             printf("\nWarning: The value you have entered of %lf km for the elevation is outside of the recommended range.\n Elevations above -10.0 km are recommended for accurate results. \n", value);
01413             while(1)
01414             {
01415                 printf("\nPlease press 'C' to continue, 'G' to get new data or 'X' to exit...\n");
01416                 fgets(ans, 20, stdin);
01417                 switch(ans[0]) {
01418                     case 'X':
01419                     case 'x':
01420                         return 0;
01421                     case 'G':
01422                     case 'g':
01423                         return 1;
01424                     case 'C':
01425                     case 'c':
01426                         return 2;
01427                     default:
01428                         printf("\nInvalid input %c\n", ans[0]);
01429                         break;
01430                 }
01431             }
01432             break;
01433 
01434         case 4:/*Date outside the recommended range*/
01435             printf("\nWARNING - TIME EXTENDS BEYOND INTENDED USAGE RANGE\n CONTACT NGDC FOR PRODUCT UPDATES:\n");
01436             printf("    National Geophysical Data Center\n");
01437             printf("    NOAA EGC/2\n");
01438             printf("    325 Broadway\n");
01439             printf("    Attn: Manoj Nair or Stefan Maus\n");
01440             printf("    Phone:  (303) 497-4642 or -6522\n");
01441             printf("    Email:  Manoj.C.Nair@noaa.gov\n");
01442             printf("    or\n");
01443             printf("    Stefan.Maus@noaa.gov\n");
01444             printf("    Web: http://www.ngdc.noaa.gov/Geomagnetic/WMM/DoDWMM.shtml\n");
01445             printf("\n VALID RANGE  = %d - %d\n", (int) MagneticModel->epoch, (int) MagneticModel->CoefficientFileEndDate);
01446             printf(" TIME   = %lf\n", value);
01447             while(1)
01448             {
01449                 printf("\nPlease press 'C' to continue, 'N' to enter new data or 'X' to exit...\n");
01450                 fgets(ans, 20, stdin);
01451                 switch(ans[0]) {
01452                     case 'X':
01453                     case 'x':
01454                         return 0;
01455                     case 'N':
01456                     case 'n':
01457                         return 1;
01458                     case 'C':
01459                     case 'c':
01460                         return 2;
01461                     default:
01462                         printf("\nInvalid input %c\n", ans[0]);
01463                         break;
01464                 }
01465             }
01466             break;
01467     }
01468     return 2;
01469 } /*MAG_Warnings*/
01470 
01471 /*End of User Interface functions*/
01472 
01473 
01474 /******************************************************************************
01475  ********************************Memory and File Processing********************
01476  * This grouping consists of functions that read coefficient files into the 
01477  * memory, allocate memory, free memory or print models into coefficient files.  
01478  ******************************************************************************/
01479 
01480 
01481 MAGtype_LegendreFunction *MAG_AllocateLegendreFunctionMemory(int NumTerms)
01482 
01483 /* Allocate memory for Associated Legendre Function data types.
01484    Should be called before computing Associated Legendre Functions.
01485 
01486  INPUT: NumTerms : int : Total number of spherical harmonic coefficients in the model
01487 
01488 
01489  OUTPUT:    Pointer to data structure MAGtype_LegendreFunction with the following elements
01490                         double *Pcup;  (  pointer to store Legendre Function  )
01491                         double *dPcup; ( pointer to store  Derivative of Legendre function )
01492 
01493                         FALSE: Failed to allocate memory
01494 
01495 CALLS : none
01496 
01497  */
01498 {
01499     MAGtype_LegendreFunction *LegendreFunction;
01500 
01501     LegendreFunction = (MAGtype_LegendreFunction *) calloc(1, sizeof (MAGtype_LegendreFunction));
01502 
01503     if(!LegendreFunction)
01504     {
01505         MAG_Error(1);
01506         //printf("error allocating in MAG_AllocateLegendreFunctionMemory\n");
01507         return FALSE;
01508     }
01509     LegendreFunction->Pcup = (double *) malloc((NumTerms + 1) * sizeof ( double));
01510     if(LegendreFunction->Pcup == 0)
01511     {
01512         MAG_Error(1);
01513         //printf("error allocating in MAG_AllocateLegendreFunctionMemory\n");
01514         return FALSE;
01515     }
01516     LegendreFunction->dPcup = (double *) malloc((NumTerms + 1) * sizeof ( double));
01517     if(LegendreFunction->dPcup == 0)
01518     {
01519         MAG_Error(1);
01520         //printf("error allocating in MAG_AllocateLegendreFunctionMemory\n");
01521         return FALSE;
01522     }
01523     return LegendreFunction;
01524 } /*MAGtype_LegendreFunction*/
01525 
01526 MAGtype_MagneticModel *MAG_AllocateModelMemory(int NumTerms)
01527 
01528 /* Allocate memory for WMM Coefficients
01529  * Should be called before reading the model file *
01530 
01531   INPUT: NumTerms : int : Total number of spherical harmonic coefficients in the model
01532 
01533 
01534  OUTPUT:    Pointer to data structure MAGtype_MagneticModel with the following elements
01535                         double EditionDate;
01536                         double epoch;       Base time of Geomagnetic model epoch (yrs)
01537                         char  ModelName[20];
01538                         double *Main_Field_Coeff_G;          C - Gauss coefficients of main geomagnetic model (nT)
01539                         double *Main_Field_Coeff_H;          C - Gauss coefficients of main geomagnetic model (nT)
01540                         double *Secular_Var_Coeff_G;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01541                         double *Secular_Var_Coeff_H;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01542                         int nMax;  Maximum degree of spherical harmonic model
01543                         int nMaxSecVar; Maxumum degree of spherical harmonic secular model
01544                         int SecularVariationUsed; Whether or not the magnetic secular variation vector will be needed by program
01545 
01546                         FALSE: Failed to allocate memory
01547 CALLS : none
01548  */
01549 {
01550     MAGtype_MagneticModel *MagneticModel;
01551 
01552 
01553     MagneticModel = (MAGtype_MagneticModel *) calloc(1, sizeof (MAGtype_MagneticModel));
01554 
01555     if(MagneticModel == NULL)
01556     {
01557         MAG_Error(2);
01558         //printf("error allocating in MAG_AllocateModelMemory\n");
01559         return FALSE;
01560     }
01561 
01562     MagneticModel->Main_Field_Coeff_G = (double *) malloc((NumTerms + 1) * sizeof ( double));
01563 
01564     if(MagneticModel->Main_Field_Coeff_G == NULL)
01565     {
01566         MAG_Error(2);
01567         //printf("error allocating in MAG_AllocateModelMemory\n");
01568         return FALSE;
01569     }
01570 
01571     MagneticModel->Main_Field_Coeff_H = (double *) malloc((NumTerms + 1) * sizeof ( double));
01572 
01573     if(MagneticModel->Main_Field_Coeff_H == NULL)
01574     {
01575         MAG_Error(2);
01576         //printf("error allocating in MAG_AllocateModelMemory\n");
01577         return FALSE;
01578     }
01579     MagneticModel->Secular_Var_Coeff_G = (double *) malloc((NumTerms + 1) * sizeof ( double));
01580     if(MagneticModel->Secular_Var_Coeff_G == NULL)
01581     {
01582         MAG_Error(2);
01583         //printf("error allocating in MAG_AllocateModelMemory\n");
01584         return FALSE;
01585     }
01586     MagneticModel->Secular_Var_Coeff_H = (double *) malloc((NumTerms + 1) * sizeof ( double));
01587     if(MagneticModel->Secular_Var_Coeff_H == NULL)
01588     {
01589         MAG_Error(2);
01590         //printf("error allocating in MAG_AllocateModelMemory\n");
01591         return FALSE;
01592     }
01593     return MagneticModel;
01594 
01595 } /*MAG_AllocateModelMemory*/
01596 
01597 MAGtype_SphericalHarmonicVariables* MAG_AllocateSphVarMemory(int nMax)
01598 {
01599     MAGtype_SphericalHarmonicVariables* SphVariables;
01600     SphVariables  = (MAGtype_SphericalHarmonicVariables*) calloc(1, sizeof(MAGtype_SphericalHarmonicVariables));
01601     SphVariables->RelativeRadiusPower = (double *) malloc((nMax + 1) * sizeof ( double));
01602     SphVariables->cos_mlambda = (double *) malloc((nMax + 1) * sizeof (double));
01603     SphVariables->sin_mlambda = (double *) malloc((nMax + 1) * sizeof (double));
01604     return SphVariables;
01605 } /*MAG_AllocateSphVarMemory*/
01606 
01607 void MAG_AssignHeaderValues(MAGtype_MagneticModel *model, char values[][MAXLINELENGTH])
01608 {
01609     //    MAGtype_Date releasedate;
01610     strcpy(model->ModelName, values[MODELNAME]);
01611     /*      releasedate.Year = 0;
01612             releasedate.Day = 0;
01613             releasedate.Month = 0;
01614             releasedate.DecimalYear = 0;
01615             sscanf(values[RELEASEDATE],"%d-%d-%d",&releasedate.Year,&releasedate.Month,&releasedate.Day);
01616             if(MAG_DateToYear (&releasedate, NULL))
01617                 model->EditionDate = releasedate.DecimalYear;*/
01618     model->epoch = atof(values[MODELSTARTYEAR]);
01619     model->nMax = atoi(values[INTSTATICDEG]);
01620     model->nMaxSecVar = atoi(values[INTSECVARDEG]);
01621     model->CoefficientFileEndDate = atof(values[MODELENDYEAR]);
01622     if(model->nMaxSecVar > 0)
01623         model->SecularVariationUsed = 1;
01624     else
01625         model->SecularVariationUsed = 0;
01626 }
01627 
01628 void MAG_AssignMagneticModelCoeffs(MAGtype_MagneticModel *Assignee, MAGtype_MagneticModel *Source, int nMax, int nMaxSecVar)
01629 /* This function assigns the first nMax degrees of the Source model to the Assignee model, leaving the other coefficients
01630  untouched*/
01631 {
01632     int n, m, index;
01633     assert(nMax <= Source->nMax);
01634     assert(nMax <= Assignee->nMax);
01635     assert(nMaxSecVar <= Source->nMaxSecVar);
01636     assert(nMaxSecVar <= Assignee->nMaxSecVar);
01637     for(n = 1; n <= nMaxSecVar; n++)
01638     {
01639         for(m = 0; m <= n; m++)
01640         {
01641             index = (n * (n + 1) / 2 + m);
01642             Assignee->Main_Field_Coeff_G[index] = Source->Main_Field_Coeff_G[index];
01643             Assignee->Main_Field_Coeff_H[index] = Source->Main_Field_Coeff_H[index];
01644             Assignee->Secular_Var_Coeff_G[index] = Source->Secular_Var_Coeff_G[index];
01645             Assignee->Secular_Var_Coeff_H[index] = Source->Secular_Var_Coeff_H[index];
01646         }
01647     }
01648     for(n = nMaxSecVar + 1; n <= nMax; n++)
01649     {
01650         for(m = 0; m <= n; m++)
01651         {
01652             index = (n * (n + 1) / 2 + m);
01653             Assignee->Main_Field_Coeff_G[index] = Source->Main_Field_Coeff_G[index];
01654             Assignee->Main_Field_Coeff_H[index] = Source->Main_Field_Coeff_H[index];
01655         }
01656     }
01657     return;
01658 } /*MAG_AssignMagneticModelCoeffs*/
01659 
01660 int MAG_FreeMemory(MAGtype_MagneticModel *MagneticModel, MAGtype_MagneticModel *TimedMagneticModel, MAGtype_LegendreFunction *LegendreFunction)
01661 
01662 /* Free memory used by WMM functions. Only to be called at the end of the main function.
01663 INPUT :  MagneticModel  pointer to data structure with the following elements
01664 
01665                         double EditionDate;
01666                         double epoch;       Base time of Geomagnetic model epoch (yrs)
01667                         char  ModelName[20];
01668                         double *Main_Field_Coeff_G;          C - Gauss coefficients of main geomagnetic model (nT)
01669                         double *Main_Field_Coeff_H;          C - Gauss coefficients of main geomagnetic model (nT)
01670                         double *Secular_Var_Coeff_G;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01671                         double *Secular_Var_Coeff_H;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01672                         int nMax;  Maximum degree of spherical harmonic model
01673                         int nMaxSecVar; Maxumum degree of spherical harmonic secular model
01674                         int SecularVariationUsed; Whether or not the magnetic secular variation vector will be needed by program
01675 
01676                 TimedMagneticModel      Pointer to data structure similar to the first input.
01677                 LegendreFunction Pointer to data structure with the following elements
01678                                                 double *Pcup;  (  pointer to store Legendre Function  )
01679                                                 double *dPcup; ( pointer to store  Derivative of Lagendre function )
01680 
01681 OUTPUT  none
01682 CALLS : none
01683 
01684  */
01685 {
01686     if(MagneticModel->Main_Field_Coeff_G)
01687     {
01688         free(MagneticModel->Main_Field_Coeff_G);
01689         MagneticModel->Main_Field_Coeff_G = NULL;
01690     }
01691     if(MagneticModel->Main_Field_Coeff_H)
01692     {
01693         free(MagneticModel->Main_Field_Coeff_H);
01694         MagneticModel->Main_Field_Coeff_H = NULL;
01695     }
01696     if(MagneticModel->Secular_Var_Coeff_G)
01697     {
01698         free(MagneticModel->Secular_Var_Coeff_G);
01699         MagneticModel->Secular_Var_Coeff_G = NULL;
01700     }
01701     if(MagneticModel->Secular_Var_Coeff_H)
01702     {
01703         free(MagneticModel->Secular_Var_Coeff_H);
01704         MagneticModel->Secular_Var_Coeff_H = NULL;
01705     }
01706     if(MagneticModel)
01707     {
01708         free(MagneticModel);
01709         MagneticModel = NULL;
01710     }
01711 
01712     if(TimedMagneticModel->Main_Field_Coeff_G)
01713     {
01714         free(TimedMagneticModel->Main_Field_Coeff_G);
01715         TimedMagneticModel->Main_Field_Coeff_G = NULL;
01716     }
01717     if(TimedMagneticModel->Main_Field_Coeff_H)
01718     {
01719         free(TimedMagneticModel->Main_Field_Coeff_H);
01720         TimedMagneticModel->Main_Field_Coeff_H = NULL;
01721     }
01722     if(TimedMagneticModel->Secular_Var_Coeff_G)
01723     {
01724         free(TimedMagneticModel->Secular_Var_Coeff_G);
01725         TimedMagneticModel->Secular_Var_Coeff_G = NULL;
01726     }
01727     if(TimedMagneticModel->Secular_Var_Coeff_H)
01728     {
01729         free(TimedMagneticModel->Secular_Var_Coeff_H);
01730         TimedMagneticModel->Secular_Var_Coeff_H = NULL;
01731     }
01732 
01733     if(TimedMagneticModel)
01734     {
01735         free(TimedMagneticModel);
01736         TimedMagneticModel = NULL;
01737     }
01738 
01739     if(LegendreFunction->Pcup)
01740     {
01741         free(LegendreFunction->Pcup);
01742         LegendreFunction->Pcup = NULL;
01743     }
01744     if(LegendreFunction->dPcup)
01745     {
01746         free(LegendreFunction->dPcup);
01747         LegendreFunction->dPcup = NULL;
01748     }
01749     if(LegendreFunction)
01750     {
01751         free(LegendreFunction);
01752         LegendreFunction = NULL;
01753     }
01754 
01755     return TRUE;
01756 } /*MAG_FreeMemory */
01757 
01758 int MAG_FreeMagneticModelMemory(MAGtype_MagneticModel *MagneticModel)
01759 
01760 /* Free the magnetic model memory used by WMM functions.
01761 INPUT :  MagneticModel  pointer to data structure with the following elements
01762 
01763                         double EditionDate;
01764                         double epoch;       Base time of Geomagnetic model epoch (yrs)
01765                         char  ModelName[20];
01766                         double *Main_Field_Coeff_G;          C - Gauss coefficients of main geomagnetic model (nT)
01767                         double *Main_Field_Coeff_H;          C - Gauss coefficients of main geomagnetic model (nT)
01768                         double *Secular_Var_Coeff_G;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01769                         double *Secular_Var_Coeff_H;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01770                         int nMax;  Maximum degree of spherical harmonic model
01771                         int nMaxSecVar; Maxumum degree of spherical harmonic secular model
01772                         int SecularVariationUsed; Whether or not the magnetic secular variation vector will be needed by program
01773 
01774 OUTPUT  none
01775 CALLS : none
01776 
01777  */
01778 {
01779     if(MagneticModel->Main_Field_Coeff_G)
01780     {
01781         free(MagneticModel->Main_Field_Coeff_G);
01782         MagneticModel->Main_Field_Coeff_G = NULL;
01783     }
01784     if(MagneticModel->Main_Field_Coeff_H)
01785     {
01786         free(MagneticModel->Main_Field_Coeff_H);
01787         MagneticModel->Main_Field_Coeff_H = NULL;
01788     }
01789     if(MagneticModel->Secular_Var_Coeff_G)
01790     {
01791         free(MagneticModel->Secular_Var_Coeff_G);
01792         MagneticModel->Secular_Var_Coeff_G = NULL;
01793     }
01794     if(MagneticModel->Secular_Var_Coeff_H)
01795     {
01796         free(MagneticModel->Secular_Var_Coeff_H);
01797         MagneticModel->Secular_Var_Coeff_H = NULL;
01798     }
01799     if(MagneticModel)
01800     {
01801         free(MagneticModel);
01802         MagneticModel = NULL;
01803     }
01804 
01805     return TRUE;
01806 } /*MAG_FreeMagneticModelMemory */
01807 
01808 int MAG_FreeLegendreMemory(MAGtype_LegendreFunction *LegendreFunction)
01809 
01810 /* Free the Legendre Coefficients memory used by the WMM functions.
01811 INPUT : LegendreFunction Pointer to data structure with the following elements
01812                                                 double *Pcup;  (  pointer to store Legendre Function  )
01813                                                 double *dPcup; ( pointer to store  Derivative of Lagendre function )
01814 
01815 OUTPUT: none
01816 CALLS : none
01817 
01818  */
01819 {
01820     if(LegendreFunction->Pcup)
01821     {
01822         free(LegendreFunction->Pcup);
01823         LegendreFunction->Pcup = NULL;
01824     }
01825     if(LegendreFunction->dPcup)
01826     {
01827         free(LegendreFunction->dPcup);
01828         LegendreFunction->dPcup = NULL;
01829     }
01830     if(LegendreFunction)
01831     {
01832         free(LegendreFunction);
01833         LegendreFunction = NULL;
01834     }
01835 
01836     return TRUE;
01837 } /*MAG_FreeLegendreMemory */
01838 
01839 int MAG_FreeSphVarMemory(MAGtype_SphericalHarmonicVariables *SphVar)
01840 
01841 /* Free the Spherical Harmonic Variable memory used by the WMM functions.
01842 INPUT : LegendreFunction Pointer to data structure with the following elements
01843                                                 double *RelativeRadiusPower
01844                                                 double *cos_mlambda
01845                                                 double *sin_mlambda
01846  OUTPUT: none
01847  CALLS : none
01848  */
01849 {
01850     if(SphVar->RelativeRadiusPower)
01851     {
01852         free(SphVar->RelativeRadiusPower);
01853         SphVar->RelativeRadiusPower = NULL;
01854     }
01855     if(SphVar->cos_mlambda)
01856     {
01857         free(SphVar->cos_mlambda);
01858         SphVar->cos_mlambda = NULL;
01859     }
01860     if(SphVar->sin_mlambda)
01861     {
01862         free(SphVar->sin_mlambda);
01863         SphVar->sin_mlambda = NULL;
01864     }
01865     if(SphVar)
01866     {
01867         free(SphVar);
01868         SphVar = NULL;
01869     }
01870 
01871     return TRUE;
01872 } /*MAG_FreeSphVarMemory*/
01873 
01874 void MAG_PrintWMMFormat(char *filename, MAGtype_MagneticModel *MagneticModel)
01875 {
01876     int index, n, m;
01877     FILE *OUT;
01878     MAGtype_Date Date;
01879     char Datestring[11];
01880 
01881     Date.DecimalYear = MagneticModel->EditionDate;
01882     MAG_YearToDate(&Date);
01883     sprintf(Datestring, "%d/%d/%d", Date.Month, Date.Day, Date.Year);
01884     OUT = fopen(filename, "w");
01885     fprintf(OUT, "    %.1lf               %s              %s\n", MagneticModel->epoch, MagneticModel->ModelName, Datestring);
01886     for(n = 1; n <= MagneticModel->nMax; n++)
01887     {
01888         for(m = 0; m <= n; m++)
01889         {
01890             index = (n * (n + 1) / 2 + m);
01891             if(m != 0)
01892                 fprintf(OUT, " %2d %2d %9.1lf %9.1lf  %9.1lf %9.1lf\n", n, m, MagneticModel->Main_Field_Coeff_G[index], MagneticModel->Main_Field_Coeff_H[index], MagneticModel->Secular_Var_Coeff_G[index], MagneticModel->Secular_Var_Coeff_H[index]);
01893             else
01894                 fprintf(OUT, " %2d %2d %9.1lf %9.1lf  %9.1lf %9.1lf\n", n, m, MagneticModel->Main_Field_Coeff_G[index], 0.0, MagneticModel->Secular_Var_Coeff_G[index], 0.0);
01895         }
01896     }
01897     fclose(OUT);
01898 } /*MAG_PrintWMMFormat*/
01899 
01900 void MAG_PrintEMMFormat(char *filename, char *filenameSV, MAGtype_MagneticModel *MagneticModel)
01901 {
01902     int index, n, m;
01903     FILE *OUT;
01904     MAGtype_Date Date;
01905     char Datestring[11];
01906 
01907     Date.DecimalYear = MagneticModel->EditionDate;
01908     MAG_YearToDate(&Date);
01909     sprintf(Datestring, "%d/%d/%d", Date.Month, Date.Day, Date.Year);
01910     OUT = fopen(filename, "w");
01911     fprintf(OUT, "    %.1lf               %s              %s\n", MagneticModel->epoch, MagneticModel->ModelName, Datestring);
01912     for(n = 1; n <= MagneticModel->nMax; n++)
01913     {
01914         for(m = 0; m <= n; m++)
01915         {
01916             index = (n * (n + 1) / 2 + m);
01917             if(m != 0)
01918                 fprintf(OUT, " %2d %2d %9.4lf %9.4lf\n", n, m, MagneticModel->Main_Field_Coeff_G[index], MagneticModel->Main_Field_Coeff_H[index]);
01919             else
01920                 fprintf(OUT, " %2d %2d %9.4lf %9.4lf\n", n, m, MagneticModel->Main_Field_Coeff_G[index], 0.0);
01921         }
01922     }
01923     fclose(OUT);
01924     OUT = fopen(filenameSV, "w");
01925     for(n = 1; n <= MagneticModel->nMaxSecVar; n++)
01926     {
01927         for(m = 0; m <= n; m++)
01928         {
01929             index = (n * (n + 1) / 2 + m);
01930             if(m != 0)
01931                 fprintf(OUT, " %2d %2d %9.4lf %9.4lf\n", n, m, MagneticModel->Secular_Var_Coeff_G[index], MagneticModel->Secular_Var_Coeff_H[index]);
01932             else
01933                 fprintf(OUT, " %2d %2d %9.4lf %9.4lf\n", n, m, MagneticModel->Secular_Var_Coeff_G[index], 0.0);
01934         }
01935     }
01936     fclose(OUT);
01937     return;
01938 } /*MAG_PrintEMMFormat*/
01939 
01940 int MAG_readMagneticModel(char *filename, MAGtype_MagneticModel * MagneticModel)
01941 {
01942 
01943     /* READ WORLD Magnetic MODEL SPHERICAL HARMONIC COEFFICIENTS (WMM.cof)
01944        INPUT :  filename
01945             MagneticModel : Pointer to the data structure with the following fields required as inputs
01946                                     nMax :      Number of static coefficients
01947        UPDATES : MagneticModel : Pointer to the data structure with the following fields populated
01948                                     char  *ModelName;
01949                                     double epoch;       Base time of Geomagnetic model epoch (yrs)
01950                                     double *Main_Field_Coeff_G;          C - Gauss coefficients of main geomagnetic model (nT)
01951                                     double *Main_Field_Coeff_H;          C - Gauss coefficients of main geomagnetic model (nT)
01952                                     double *Secular_Var_Coeff_G;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01953                                     double *Secular_Var_Coeff_H;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
01954             CALLS : none
01955 
01956      */
01957 
01958     FILE *MAG_COF_File;
01959     char c_str[81], c_new[5]; /*these strings are used to read a line from coefficient file*/
01960     int i, icomp, m, n, EOF_Flag = 0, index;
01961     double epoch, gnm, hnm, dgnm, dhnm;
01962     MAG_COF_File = fopen(filename, "r");
01963     if(MAG_COF_File == NULL)
01964     {
01965         MAG_Error(20);
01966         //printf("Error in opening %s File\n",filename);
01967         return FALSE;
01968         /* should we have a standard error printing routine ?*/
01969     }
01970     MagneticModel->Main_Field_Coeff_H[0] = 0.0;
01971     MagneticModel->Main_Field_Coeff_G[0] = 0.0;
01972     MagneticModel->Secular_Var_Coeff_H[0] = 0.0;
01973     MagneticModel->Secular_Var_Coeff_G[0] = 0.0;
01974     fgets(c_str, 80, MAG_COF_File);
01975     sscanf(c_str, "%lf%s", &epoch, MagneticModel->ModelName);
01976     MagneticModel->epoch = epoch;
01977     while(EOF_Flag == 0)
01978     {
01979         fgets(c_str, 80, MAG_COF_File);
01980         /* CHECK FOR LAST LINE IN FILE */
01981         for(i = 0; i < 4 && (c_str[i] != '\0'); i++)
01982         {
01983             c_new[i] = c_str[i];
01984             c_new[i + 1] = '\0';
01985         }
01986         icomp = strcmp("9999", c_new);
01987         if(icomp == 0)
01988         {
01989             EOF_Flag = 1;
01990             break;
01991         }
01992         /* END OF FILE NOT ENCOUNTERED, GET VALUES */
01993         sscanf(c_str, "%d%d%lf%lf%lf%lf", &n, &m, &gnm, &hnm, &dgnm, &dhnm);
01994         if(m <= n)
01995         {
01996             index = (n * (n + 1) / 2 + m);
01997             MagneticModel->Main_Field_Coeff_G[index] = gnm;
01998             MagneticModel->Secular_Var_Coeff_G[index] = dgnm;
01999             MagneticModel->Main_Field_Coeff_H[index] = hnm;
02000             MagneticModel->Secular_Var_Coeff_H[index] = dhnm;
02001         }
02002     }
02003 
02004     fclose(MAG_COF_File);
02005     return TRUE;
02006 } /*MAG_readMagneticModel*/
02007 
02008 int MAG_readMagneticModel_Large(char *filename, char *filenameSV, MAGtype_MagneticModel *MagneticModel)
02009 
02010 /*  To read the high-degree model coefficients (for example, NGDC 720)
02011    INPUT :  filename   file name for static coefficients
02012                         filenameSV file name for secular variation coefficients
02013 
02014                         MagneticModel : Pointer to the data structure with the following fields required as inputs
02015                                 nMaxSecVar : Number of secular variation coefficients
02016                                 nMax :  Number of static coefficients
02017    UPDATES : MagneticModel : Pointer to the data structure with the following fields populated
02018                                 double epoch;       Base time of Geomagnetic model epoch (yrs)
02019                                 double *Main_Field_Coeff_G;          C - Gauss coefficients of main geomagnetic model (nT)
02020                                 double *Main_Field_Coeff_H;          C - Gauss coefficients of main geomagnetic model (nT)
02021                                 double *Secular_Var_Coeff_G;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
02022                                 double *Secular_Var_Coeff_H;  CD - Gauss coefficients of secular geomagnetic model (nT/yr)
02023         CALLS : none
02024 
02025  */
02026 {
02027     FILE *MAG_COF_File;
02028     FILE *MAG_COFSV_File;
02029     char c_str[81], c_str2[81]; //these strings are used to read a line from coefficient file
02030     int i, m, n, index, a, b;
02031     double epoch, gnm, hnm, dgnm, dhnm;
02032     MAG_COF_File = fopen(filename, "r");
02033     MAG_COFSV_File = fopen(filenameSV, "r");
02034     if(MAG_COF_File == NULL || MAG_COFSV_File == NULL)
02035     {
02036         MAG_Error(20);
02037         //printf("Error in opening %s File\n",filename);
02038         return FALSE;
02039     }
02040     MagneticModel->Main_Field_Coeff_H[0] = 0.0;
02041     MagneticModel->Main_Field_Coeff_G[0] = 0.0;
02042     MagneticModel->Secular_Var_Coeff_H[0] = 0.0;
02043     MagneticModel->Secular_Var_Coeff_G[0] = 0.0;
02044     fgets(c_str, 80, MAG_COF_File);
02045     sscanf(c_str, "%lf%s", &epoch, MagneticModel->ModelName);
02046     MagneticModel->epoch = epoch;
02047     a = (MagneticModel->nMaxSecVar * (MagneticModel->nMaxSecVar + 1) / 2 + MagneticModel->nMaxSecVar);
02048     b = (MagneticModel->nMax * (MagneticModel->nMax + 1) / 2 + MagneticModel->nMax);
02049     //MagneticModel->ModelName = "WMM-720: 2005";
02050     for(i = 0; i <= a; i++)
02051     {
02052         fgets(c_str, 80, MAG_COF_File);
02053         sscanf(c_str, "%d%d%lf%lf", &n, &m, &gnm, &hnm);
02054         fgets(c_str2, 80, MAG_COFSV_File);
02055         sscanf(c_str2, "%d%d%lf%lf", &n, &m, &dgnm, &dhnm);
02056         if(m <= n)
02057         {
02058             index = (n * (n + 1) / 2 + m);
02059             MagneticModel->Main_Field_Coeff_G[index] = gnm;
02060             MagneticModel->Secular_Var_Coeff_G[index] = dgnm;
02061             MagneticModel->Main_Field_Coeff_H[index] = hnm;
02062             MagneticModel->Secular_Var_Coeff_H[index] = dhnm;
02063         }
02064     }
02065     for(i = a + 1; i < b; i++)
02066     {
02067         fgets(c_str, 80, MAG_COF_File);
02068         sscanf(c_str, "%d%d%lf%lf", &n, &m, &gnm, &hnm);
02069         if(m <= n)
02070         {
02071             index = (n * (n + 1) / 2 + m);
02072             MagneticModel->Main_Field_Coeff_G[index] = gnm;
02073             //MagneticModel->Secular_Var_Coeff_G[index] = dgnm;
02074             MagneticModel->Main_Field_Coeff_H[index] = hnm;
02075             //MagneticModel->Secular_Var_Coeff_H[index] = dhnm;
02076         }
02077     }
02078     if(MAG_COF_File != NULL && MAG_COFSV_File != NULL)
02079     {
02080         fclose(MAG_COF_File);
02081         fclose(MAG_COFSV_File);
02082     }
02083 
02084     return TRUE;
02085 } /*MAG_readMagneticModel_Large*/
02086 
02087 int MAG_readMagneticModel_SHDF(char *filename, MAGtype_MagneticModel *(*magneticmodels)[], int array_size)
02088 /*
02089  * MAG_readMagneticModels - Read the Magnetic Models from an SHDF format file
02090  *
02091  * Input:
02092  *  filename - Path to the SHDF format model file to be read
02093  *  array_size - Max No of models to be read from the file
02094  *
02095  * Output:
02096  *  magneticmodels[] - Array of magnetic models read from the file
02097  *
02098  * Return value:
02099  *  Returns the number of models read from the file.
02100  *  -2 implies that internal or external static degree was not found in the file, hence memory cannot be allocated
02101  *  -1 implies some error during file processing (I/O)
02102  *  0 implies no models were read from the file
02103  *  if ReturnValue > array_size then there were too many models in model file but only <array_size> number were read .
02104  *  if ReturnValue <= array_size then the function execution was successful.
02105  */
02106 {
02107     char paramkeys[NOOFPARAMS][MAXLINELENGTH] = {
02108         "SHDF ",
02109         "ModelName: ",
02110         "Publisher: ",
02111         "ReleaseDate: ",
02112         "DataCutOff: ",
02113         "ModelStartYear: ",
02114         "ModelEndYear: ",
02115         "Epoch: ",
02116         "IntStaticDeg: ",
02117         "IntSecVarDeg: ",
02118         "ExtStaticDeg: ",
02119         "ExtSecVarDeg: ",
02120         "GeoMagRefRad: ",
02121         "Normalization: ",
02122         "SpatBasFunc: "
02123     };
02124 
02125     char paramvalues[NOOFPARAMS][MAXLINELENGTH];
02126     char *line = (char *) malloc(MAXLINELENGTH);
02127     char *ptrreset;
02128     char paramvalue[MAXLINELENGTH];
02129     int paramvaluelength = 0;
02130     int paramkeylength = 0;
02131     int i = 0, j = 0;
02132     int newrecord = 1;
02133     int header_index = -1;
02134     int numterms;
02135     int tempint;
02136     int allocationflag = 0;
02137     char coefftype; //Internal or External (I/E)
02138 
02139     // For reading coefficients
02140     int n, m;
02141     double gnm, hnm, dgnm, dhnm, cutoff;
02142     int index;
02143 
02144     //MAGtype_MagneticModel *model = NULL;
02145 
02146     FILE *stream;
02147     ptrreset = line;
02148     stream = fopen(filename, READONLYMODE);
02149     if(stream == NULL)
02150     {
02151         perror("File open error");
02152         return header_index;
02153     }
02154 
02155     // Read records from the model file and store header information.
02156     while(fgets(line, MAXLINELENGTH, stream) != NULL)
02157     {
02158         j++;
02159         if(strlen(MAG_Trim(line)) == 0)
02160             continue;
02161         if(*line == '%')
02162         {
02163             line++;
02164             if(newrecord)
02165             {
02166                 if(header_index > -1)
02167                 {
02168                     MAG_AssignHeaderValues((*magneticmodels)[header_index], paramvalues);
02169                 }
02170                 header_index++;
02171                 if(header_index >= array_size)
02172                 {
02173                     fprintf(stderr, "Header limit exceeded - too many models in model file. (%d)\n", header_index);
02174                     return array_size + 1;
02175                 }
02176                 newrecord = 0;
02177                 allocationflag = 0;
02178             }
02179             for(i = 0; i < NOOFPARAMS; i++)
02180             {
02181 
02182                 paramkeylength = strlen(paramkeys[i]);
02183                 if(!strncmp(line, paramkeys[i], paramkeylength))
02184                 {
02185                     paramvaluelength = strlen(line) - paramkeylength;
02186                     strncpy(paramvalue, line + paramkeylength, paramvaluelength);
02187                     paramvalue[paramvaluelength] = '\0';
02188                     strcpy(paramvalues[i], paramvalue);
02189                     if(!strcmp(paramkeys[i], paramkeys[INTSTATICDEG]) || !strcmp(paramkeys[i], paramkeys[EXTSTATICDEG]))
02190                     {
02191                         tempint = atoi(paramvalues[i]);
02192                         if(tempint > 0 && allocationflag == 0)
02193                         {
02194                             numterms = CALCULATE_NUMTERMS(tempint);
02195                             (*magneticmodels)[header_index] = MAG_AllocateModelMemory(numterms);
02196                             //model = (*magneticmodels)[header_index];
02197                             allocationflag = 1;
02198                         }
02199                     }
02200                     break;
02201                 }
02202             }
02203             line--;
02204         } else if(*line == '#')
02205         {
02206             // process comments
02207 
02208         } else if(sscanf(line, "%c,%d,%d", &coefftype, &n, &m) == 3)
02209         {
02210             if(m == 0)
02211             {
02212                 sscanf(line, "%c,%d,%d,%lf,,%lf,", &coefftype, &n, &m, &gnm, &dgnm);
02213                 hnm = 0;
02214                 dhnm = 0;
02215             } else
02216                 sscanf(line, "%c,%d,%d,%lf,%lf,%lf,%lf", &coefftype, &n, &m, &gnm, &hnm, &dgnm, &dhnm);
02217             newrecord = 1;
02218             if(!allocationflag)
02219             {
02220                 fprintf(stderr, "Degree not found in model. Memory cannot be allocated.\n");
02221                 return _DEGREE_NOT_FOUND;
02222             }
02223             if(m <= n)
02224             {
02225                 index = (n * (n + 1) / 2 + m);
02226                 (*magneticmodels)[header_index]->Main_Field_Coeff_G[index] = gnm;
02227                 (*magneticmodels)[header_index]->Secular_Var_Coeff_G[index] = dgnm;
02228                 (*magneticmodels)[header_index]->Main_Field_Coeff_H[index] = hnm;
02229                 (*magneticmodels)[header_index]->Secular_Var_Coeff_H[index] = dhnm;
02230             }
02231         }
02232     }
02233     if(header_index > -1)
02234         MAG_AssignHeaderValues((*magneticmodels)[header_index], paramvalues);
02235     fclose(stream);
02236 
02237     cutoff = (*magneticmodels)[array_size - 1]->CoefficientFileEndDate;
02238 
02239     for(i = 0; i < array_size; i++) (*magneticmodels)[i]->CoefficientFileEndDate = cutoff;
02240 
02241     free(ptrreset);
02242     line = NULL;
02243     ptrreset = NULL;
02244     return header_index + 1;
02245 }/*MAG_readMagneticModel_SHDF*/
02246 
02247 char *MAG_Trim(char *str)
02248 {
02249     char *end;
02250 
02251     while(isspace(*str))
02252         str++;
02253 
02254     if(*str == 0)
02255         return str;
02256 
02257     end = str + strlen(str) - 1;
02258     while(end > str && isspace(*end))
02259         end--;
02260 
02261     *(end + 1) = 0;
02262 
02263     return str;
02264 }
02265 
02266 /*End of Memory and File Processing functions*/
02267 
02268 
02269 /******************************************************************************
02270  *************Conversions, Transformations, and other Calculations**************
02271  * This grouping consists of functions that perform unit conversions, coordinate
02272  * transformations and other simple or straightforward calculations that are 
02273  * usually easily replicable with a typical scientific calculator. 
02274  ******************************************************************************/
02275 
02276 //
02277 
02278 int MAG_CalculateGeoMagneticElements(MAGtype_MagneticResults *MagneticResultsGeo, MAGtype_GeoMagneticElements *GeoMagneticElements)
02279 
02280 /* Calculate all the Geomagnetic elements from X,Y and Z components
02281 INPUT     MagneticResultsGeo   Pointer to data structure with the following elements
02282                         double Bx;    ( North )
02283                         double By;        ( East )
02284                         double Bz;    ( Down )
02285 OUTPUT    GeoMagneticElements    Pointer to data structure with the following elements
02286                         double Decl; (Angle between the magnetic field vector and true north, positive east)
02287                         double Incl; Angle between the magnetic field vector and the horizontal plane, positive down
02288                         double F; Magnetic Field Strength
02289                         double H; Horizontal Magnetic Field Strength
02290                         double X; Northern component of the magnetic field vector
02291                         double Y; Eastern component of the magnetic field vector
02292                         double Z; Downward component of the magnetic field vector
02293 CALLS : none
02294  */
02295 {
02296     GeoMagneticElements->X = MagneticResultsGeo->Bx;
02297     GeoMagneticElements->Y = MagneticResultsGeo->By;
02298     GeoMagneticElements->Z = MagneticResultsGeo->Bz;
02299 
02300     GeoMagneticElements->H = sqrt(MagneticResultsGeo->Bx * MagneticResultsGeo->Bx + MagneticResultsGeo->By * MagneticResultsGeo->By);
02301     GeoMagneticElements->F = sqrt(GeoMagneticElements->H * GeoMagneticElements->H + MagneticResultsGeo->Bz * MagneticResultsGeo->Bz);
02302     GeoMagneticElements->Decl = RAD2DEG(atan2(GeoMagneticElements->Y, GeoMagneticElements->X));
02303     GeoMagneticElements->Incl = RAD2DEG(atan2(GeoMagneticElements->Z, GeoMagneticElements->H));
02304 
02305     return TRUE;
02306 } /*MAG_CalculateGeoMagneticElements */
02307 
02308 int MAG_CalculateGridVariation(MAGtype_CoordGeodetic location, MAGtype_GeoMagneticElements *elements)
02309 
02310 /*Computes the grid variation for |latitudes| > MAG_MAX_LAT_DEGREE
02311 
02312 Grivation (or grid variation) is the angle between grid north and
02313 magnetic north. This routine calculates Grivation for the Polar Stereographic
02314 projection for polar locations (Latitude => |55| deg). Otherwise, it computes the grid
02315 variation in UTM projection system. However, the UTM projection codes may be used to compute
02316 the grid variation at any latitudes.
02317 
02318 INPUT    location    Data structure with the following elements
02319                 double lambda; (longitude)
02320                 double phi; ( geodetic latitude)
02321                 double HeightAboveEllipsoid; (height above the ellipsoid (HaE) )
02322                 double HeightAboveGeoid;(height above the Geoid )
02323 OUTPUT  elements Data  structure with the following elements updated
02324                 double GV; ( The Grid Variation )
02325 CALLS : MAG_GetTransverseMercator
02326 
02327  */
02328 {
02329     MAGtype_UTMParameters UTMParameters;
02330     if(location.phi >= MAG_PS_MAX_LAT_DEGREE)
02331     {
02332         elements->GV = elements->Decl - location.lambda;
02333         return 1;
02334     } else if(location.phi <= MAG_PS_MIN_LAT_DEGREE)
02335     {
02336         elements->GV = elements->Decl + location.lambda;
02337         return 1;
02338     } else
02339     {
02340         MAG_GetTransverseMercator(location, &UTMParameters);
02341         elements->GV = elements->Decl - UTMParameters.ConvergenceOfMeridians;
02342     }
02343     return 0;
02344 } /*MAG_CalculateGridVariation*/
02345 
02346 int MAG_CalculateSecularVariationElements(MAGtype_MagneticResults MagneticVariation, MAGtype_GeoMagneticElements *MagneticElements)
02347 /*This takes the Magnetic Variation in x, y, and z and uses it to calculate the secular variation of each of the Geomagnetic elements.
02348         INPUT     MagneticVariation   Data structure with the following elements
02349                                 double Bx;    ( North )
02350                                 double By;        ( East )
02351                                 double Bz;    ( Down )
02352         OUTPUT   MagneticElements   Pointer to the data  structure with the following elements updated
02353                         double Decldot; Yearly Rate of change in declination
02354                         double Incldot; Yearly Rate of change in inclination
02355                         double Fdot; Yearly rate of change in Magnetic field strength
02356                         double Hdot; Yearly rate of change in horizontal field strength
02357                         double Xdot; Yearly rate of change in the northern component
02358                         double Ydot; Yearly rate of change in the eastern component
02359                         double Zdot; Yearly rate of change in the downward component
02360                         double GVdot;Yearly rate of chnage in grid variation
02361         CALLS : none
02362 
02363  */
02364 {
02365     MagneticElements->Xdot = MagneticVariation.Bx;
02366     MagneticElements->Ydot = MagneticVariation.By;
02367     MagneticElements->Zdot = MagneticVariation.Bz;
02368     MagneticElements->Hdot = (MagneticElements->X * MagneticElements->Xdot + MagneticElements->Y * MagneticElements->Ydot) / MagneticElements->H; //See equation 19 in the WMM technical report
02369     MagneticElements->Fdot = (MagneticElements->X * MagneticElements->Xdot + MagneticElements->Y * MagneticElements->Ydot + MagneticElements->Z * MagneticElements->Zdot) / MagneticElements->F;
02370     MagneticElements->Decldot = 180.0 / M_PI * (MagneticElements->X * MagneticElements->Ydot - MagneticElements->Y * MagneticElements->Xdot) / (MagneticElements->H * MagneticElements->H);
02371     MagneticElements->Incldot = 180.0 / M_PI * (MagneticElements->H * MagneticElements->Zdot - MagneticElements->Z * MagneticElements->Hdot) / (MagneticElements->F * MagneticElements->F);
02372     MagneticElements->GVdot = MagneticElements->Decldot;
02373     return TRUE;
02374 } /*MAG_CalculateSecularVariationElements*/
02375 
02376 int MAG_DateToYear(MAGtype_Date *CalendarDate, char *Error)
02377 
02378 /* Converts a given calendar date into a decimal year,
02379 it also outputs an error string if there is a problem
02380 INPUT  CalendarDate  Pointer to the  data  structure with the following elements
02381                         int     Year;
02382                         int     Month;
02383                         int     Day;
02384                         double DecimalYear;      decimal years
02385 OUTPUT  CalendarDate  Pointer to the  data  structure with the following elements updated
02386                         double DecimalYear;      decimal years
02387                 Error   pointer to an error string
02388 CALLS : none
02389 
02390  */
02391 {
02392     int temp = 0; /*Total number of days */
02393     int MonthDays[13];
02394     int ExtraDay = 0;
02395     int i;
02396     if(CalendarDate->Month == 0)
02397     {
02398         CalendarDate->DecimalYear = CalendarDate->Year;
02399         return TRUE;
02400     }
02401     if((CalendarDate->Year % 4 == 0 && CalendarDate->Year % 100 != 0) || CalendarDate->Year % 400 == 0)
02402         ExtraDay = 1;
02403     MonthDays[0] = 0;
02404     MonthDays[1] = 31;
02405     MonthDays[2] = 28 + ExtraDay;
02406     MonthDays[3] = 31;
02407     MonthDays[4] = 30;
02408     MonthDays[5] = 31;
02409     MonthDays[6] = 30;
02410     MonthDays[7] = 31;
02411     MonthDays[8] = 31;
02412     MonthDays[9] = 30;
02413     MonthDays[10] = 31;
02414     MonthDays[11] = 30;
02415     MonthDays[12] = 31;
02416 
02417     /******************Validation********************************/
02418     if(CalendarDate->Month <= 0 || CalendarDate->Month > 12)
02419     {
02420         strcpy(Error, "\nError: The Month entered is invalid, valid months are '1 to 12'\n");
02421         return 0;
02422     }
02423     if(CalendarDate->Day <= 0 || CalendarDate->Day > MonthDays[CalendarDate->Month])
02424     {
02425         printf("\nThe number of days in month %d is %d\n", CalendarDate->Month, MonthDays[CalendarDate->Month]);
02426         strcpy(Error, "\nError: The day entered is invalid\n");
02427         return 0;
02428     }
02429     /****************Calculation of t***************************/
02430     for(i = 1; i <= CalendarDate->Month; i++)
02431         temp += MonthDays[i - 1];
02432     temp += CalendarDate->Day;
02433     CalendarDate->DecimalYear = CalendarDate->Year + (temp - 1) / (365.0 + ExtraDay);
02434     return TRUE;
02435 
02436 } /*MAG_DateToYear*/
02437 
02438 void MAG_DegreeToDMSstring(double DegreesOfArc, int UnitDepth, char *DMSstring)
02439 
02440 /*This converts a given decimal degree into a DMS string.
02441 INPUT  DegreesOfArc   decimal degree
02442            UnitDepth    How many iterations should be printed,
02443                         1 = Degrees
02444                         2 = Degrees, Minutes
02445                         3 = Degrees, Minutes, Seconds
02446 OUPUT  DMSstring         pointer to DMSString
02447 CALLS : none
02448  */
02449 {
02450     int DMS[3], i;
02451     double temp = DegreesOfArc;
02452     char tempstring[20] = "";
02453     char tempstring2[20] = "";
02454     strcpy(DMSstring, "");
02455     if(UnitDepth >= 3)
02456         MAG_Error(21);
02457     for(i = 0; i < UnitDepth; i++)
02458     {
02459         DMS[i] = (int) temp;
02460         switch(i) {
02461             case 0:
02462                 strcpy(tempstring2, "Deg");
02463                 break;
02464             case 1:
02465                 strcpy(tempstring2, "Min");
02466                 break;
02467             case 2:
02468                 strcpy(tempstring2, "Sec");
02469                 break;
02470         }
02471         temp = (temp - DMS[i])*60;
02472         if(i == UnitDepth - 1 && temp >= 30)
02473             DMS[i]++;
02474         else if(i == UnitDepth - 1 && temp <= -30)
02475             DMS[i]--;
02476         sprintf(tempstring, "%4d%4s", DMS[i], tempstring2);
02477         strcat(DMSstring, tempstring);
02478     }
02479 } /*MAG_DegreeToDMSstring*/
02480 
02481 void MAG_DMSstringToDegree(char *DMSstring, double *DegreesOfArc)
02482 
02483 /*This converts a given DMS string into decimal degrees.
02484 INPUT  DMSstring         pointer to DMSString
02485 OUTPUT  DegreesOfArc   decimal degree
02486 CALLS : none
02487  */
02488 {
02489     int second, minute, degree, sign = 1, j = 0;
02490     j = sscanf(DMSstring, "%d, %d, %d", &degree, &minute, &second);
02491     if(j != 3)
02492         sscanf(DMSstring, "%d %d %d", &degree, &minute, &second);
02493     if(degree < 0)
02494         sign = -1;
02495     degree = degree * sign;
02496     *DegreesOfArc = sign * (degree + minute / 60.0 + second / 3600.0);
02497 } /*MAG_DMSstringToDegree*/
02498 
02499 int MAG_GeodeticToSpherical(MAGtype_Ellipsoid Ellip, MAGtype_CoordGeodetic CoordGeodetic, MAGtype_CoordSpherical *CoordSpherical)
02500 
02501 /* Converts Geodetic coordinates to Spherical coordinates
02502 
02503   INPUT   Ellip  data  structure with the following elements
02504                         double a; semi-major axis of the ellipsoid
02505                         double b; semi-minor axis of the ellipsoid
02506                         double fla;  flattening
02507                         double epssq; first eccentricity squared
02508                         double eps;  first eccentricity
02509                         double re; mean radius of  ellipsoid
02510 
02511                 CoordGeodetic  Pointer to the  data  structure with the following elements updates
02512                         double lambda; ( longitude )
02513                         double phi; ( geodetic latitude )
02514                         double HeightAboveEllipsoid; ( height above the WGS84 ellipsoid (HaE) )
02515                         double HeightAboveGeoid; (height above the EGM96 Geoid model )
02516 
02517  OUTPUT         CoordSpherical  Pointer to the data structure with the following elements
02518                         double lambda; ( longitude)
02519                         double phig; ( geocentric latitude )
02520                         double r;         ( distance from the center of the ellipsoid)
02521 
02522 CALLS : none
02523 
02524  */
02525 {
02526     double CosLat, SinLat, rc, xp, zp; /*all local variables */
02527 
02528     /*
02529      ** Convert geodetic coordinates, (defined by the WGS-84
02530      ** reference ellipsoid), to Earth Centered Earth Fixed Cartesian
02531      ** coordinates, and then to spherical coordinates.
02532      */
02533 
02534     CosLat = cos(DEG2RAD(CoordGeodetic.phi));
02535     SinLat = sin(DEG2RAD(CoordGeodetic.phi));
02536 
02537     /* compute the local radius of curvature on the WGS-84 reference ellipsoid */
02538 
02539     rc = Ellip.a / sqrt(1.0 - Ellip.epssq * SinLat * SinLat);
02540 
02541     /* compute ECEF Cartesian coordinates of specified point (for longitude=0) */
02542 
02543     xp = (rc + CoordGeodetic.HeightAboveEllipsoid) * CosLat;
02544     zp = (rc * (1.0 - Ellip.epssq) + CoordGeodetic.HeightAboveEllipsoid) * SinLat;
02545 
02546     /* compute spherical radius and angle lambda and phi of specified point */
02547 
02548     CoordSpherical->r = sqrt(xp * xp + zp * zp);
02549     CoordSpherical->phig = RAD2DEG(asin(zp / CoordSpherical->r)); /* geocentric latitude */
02550     CoordSpherical->lambda = CoordGeodetic.lambda; /* longitude */
02551 
02552     return TRUE;
02553 }/*MAG_GeodeticToSpherical*/
02554 
02555 int MAG_GetTransverseMercator(MAGtype_CoordGeodetic CoordGeodetic, MAGtype_UTMParameters *UTMParameters)
02556 /* Gets the UTM Parameters for a given Latitude and Longitude.
02557 
02558 INPUT: CoordGeodetic : Data structure MAGtype_CoordGeodetic.
02559 OUTPUT : UTMParameters : Pointer to data structure MAGtype_UTMParameters with the following elements
02560                      double Easting;  (X) in meters
02561                      double Northing; (Y) in meters
02562                      int Zone; UTM Zone
02563                      char HemiSphere ;
02564                      double CentralMeridian; Longitude of the Central Meridian of the UTM Zone
02565                      double ConvergenceOfMeridians;  Convergence of Meridians
02566                      double PointScale;
02567  */
02568 {
02569 
02570     double Eps, Epssq;
02571     double Acoeff[8];
02572     double Lam0, K0, falseE, falseN;
02573     double K0R4, K0R4oa;
02574     double Lambda, Phi;
02575     int XYonly;
02576     double X, Y, pscale, CoM;
02577     int Zone;
02578     char Hemisphere;
02579 
02580 
02581 
02582     /*   Get the map projection  parameters */
02583 
02584     Lambda = DEG2RAD(CoordGeodetic.lambda);
02585     Phi = DEG2RAD(CoordGeodetic.phi);
02586 
02587     MAG_GetUTMParameters(Phi, Lambda, &Zone, &Hemisphere, &Lam0);
02588     K0 = 0.9996;
02589 
02590 
02591 
02592     if(Hemisphere == 'n' || Hemisphere == 'N')
02593     {
02594         falseN = 0;
02595     }
02596     if(Hemisphere == 's' || Hemisphere == 'S')
02597     {
02598         falseN = 10000000;
02599     }
02600 
02601     falseE = 500000;
02602 
02603 
02604     //WGS84 ellipsoid
02605 
02606     Eps = 0.081819190842621494335;
02607     Epssq = 0.0066943799901413169961;
02608     K0R4 = 6367449.1458234153093;
02609     K0R4oa = 0.99832429843125277950;
02610 
02611 
02612     Acoeff[0] = 8.37731820624469723600E-04;
02613     Acoeff[1] = 7.60852777357248641400E-07;
02614     Acoeff[2] = 1.19764550324249124400E-09;
02615     Acoeff[3] = 2.42917068039708917100E-12;
02616     Acoeff[4] = 5.71181837042801392800E-15;
02617     Acoeff[5] = 1.47999793137966169400E-17;
02618     Acoeff[6] = 4.10762410937071532000E-20;
02619     Acoeff[7] = 1.21078503892257704200E-22;
02620 
02621     //WGS84 ellipsoid
02622 
02623 
02624     /*   Execution of the forward T.M. algorithm  */
02625 
02626     XYonly = 0;
02627 
02628     MAG_TMfwd4(Eps, Epssq, K0R4, K0R4oa, Acoeff,
02629             Lam0, K0, falseE, falseN,
02630             XYonly,
02631             Lambda, Phi,
02632             &X, &Y, &pscale, &CoM);
02633 
02634     /*   Report results  */
02635 
02636     UTMParameters->Easting = X; /* UTM Easting (X) in meters*/
02637     UTMParameters->Northing = Y; /* UTM Northing (Y) in meters */
02638     UTMParameters->Zone = Zone; /*UTM Zone*/
02639     UTMParameters->HemiSphere = Hemisphere;
02640     UTMParameters->CentralMeridian = RAD2DEG(Lam0); /* Central Meridian of the UTM Zone */
02641     UTMParameters->ConvergenceOfMeridians = RAD2DEG(CoM); /* Convergence of meridians of the UTM Zone and location */
02642     UTMParameters->PointScale = pscale;
02643 
02644     return 0;
02645 } /*MAG_GetTransverseMercator*/
02646 
02647 int MAG_GetUTMParameters(double Latitude,
02648         double Longitude,
02649         int *Zone,
02650         char *Hemisphere,
02651         double *CentralMeridian)
02652 {
02653     /*
02654      * The function MAG_GetUTMParameters converts geodetic (latitude and
02655      * longitude) coordinates to UTM projection parameters (zone, hemisphere and central meridian)
02656      * If any errors occur, the error code(s) are returned
02657      * by the function, otherwise TRUE is returned.
02658      *
02659      *    Latitude          : Latitude in radians                 (input)
02660      *    Longitude         : Longitude in radians                (input)
02661      *    Zone              : UTM zone                            (output)
02662      *    Hemisphere        : North or South hemisphere           (output)
02663      *    CentralMeridian       : Central Meridian of the UTM Zone in radians      (output)
02664      */
02665 
02666     long Lat_Degrees;
02667     long Long_Degrees;
02668     long temp_zone;
02669     int Error_Code = 0;
02670 
02671 
02672 
02673     if((Latitude < DEG2RAD(MAG_UTM_MIN_LAT_DEGREE)) || (Latitude > DEG2RAD(MAG_UTM_MAX_LAT_DEGREE)))
02674     { /* Latitude out of range */
02675         MAG_Error(23);
02676         Error_Code = 1;
02677     }
02678     if((Longitude < -M_PI) || (Longitude > (2 * M_PI)))
02679     { /* Longitude out of range */
02680         MAG_Error(24);
02681         Error_Code = 1;
02682     }
02683     if(!Error_Code)
02684     { /* no errors */
02685         if(Longitude < 0)
02686             Longitude += (2 * M_PI) + 1.0e-10;
02687         Lat_Degrees = (long) (Latitude * 180.0 / M_PI);
02688         Long_Degrees = (long) (Longitude * 180.0 / M_PI);
02689 
02690         if(Longitude < M_PI)
02691             temp_zone = (long) (31 + ((Longitude * 180.0 / M_PI) / 6.0));
02692         else
02693             temp_zone = (long) (((Longitude * 180.0 / M_PI) / 6.0) - 29);
02694         if(temp_zone > 60)
02695             temp_zone = 1;
02696         /* UTM special cases */
02697         if((Lat_Degrees > 55) && (Lat_Degrees < 64) && (Long_Degrees > -1)
02698                 && (Long_Degrees < 3))
02699             temp_zone = 31;
02700         if((Lat_Degrees > 55) && (Lat_Degrees < 64) && (Long_Degrees > 2)
02701                 && (Long_Degrees < 12))
02702             temp_zone = 32;
02703         if((Lat_Degrees > 71) && (Long_Degrees > -1) && (Long_Degrees < 9))
02704             temp_zone = 31;
02705         if((Lat_Degrees > 71) && (Long_Degrees > 8) && (Long_Degrees < 21))
02706             temp_zone = 33;
02707         if((Lat_Degrees > 71) && (Long_Degrees > 20) && (Long_Degrees < 33))
02708             temp_zone = 35;
02709         if((Lat_Degrees > 71) && (Long_Degrees > 32) && (Long_Degrees < 42))
02710             temp_zone = 37;
02711 
02712         if(!Error_Code)
02713         {
02714             if(temp_zone >= 31)
02715                 *CentralMeridian = (6 * temp_zone - 183) * M_PI / 180.0;
02716             else
02717                 *CentralMeridian = (6 * temp_zone + 177) * M_PI / 180.0;
02718             *Zone = temp_zone;
02719             if(Latitude < 0) *Hemisphere = 'S';
02720             else *Hemisphere = 'N';
02721         }
02722     } /* END OF if (!Error_Code) */
02723     return (Error_Code);
02724 } /* MAG_GetUTMParameters */
02725 
02726 int MAG_isNaN(double d)
02727 {
02728     return d != d;
02729 }
02730 
02731 int MAG_RotateMagneticVector(MAGtype_CoordSpherical CoordSpherical, MAGtype_CoordGeodetic CoordGeodetic, MAGtype_MagneticResults MagneticResultsSph, MAGtype_MagneticResults *MagneticResultsGeo)
02732 /* Rotate the Magnetic Vectors to Geodetic Coordinates
02733 Manoj Nair, June, 2009 Manoj.C.Nair@Noaa.Gov
02734 Equation 16, WMM Technical report
02735 
02736 INPUT : CoordSpherical : Data structure MAGtype_CoordSpherical with the following elements
02737                         double lambda; ( longitude)
02738                         double phig; ( geocentric latitude )
02739                         double r;         ( distance from the center of the ellipsoid)
02740 
02741                 CoordGeodetic : Data structure MAGtype_CoordGeodetic with the following elements
02742                         double lambda; (longitude)
02743                         double phi; ( geodetic latitude)
02744                         double HeightAboveEllipsoid; (height above the ellipsoid (HaE) )
02745                         double HeightAboveGeoid;(height above the Geoid )
02746 
02747                 MagneticResultsSph : Data structure MAGtype_MagneticResults with the following elements
02748                         double Bx;      North
02749                         double By;      East
02750                         double Bz;      Down
02751 
02752 OUTPUT: MagneticResultsGeo Pointer to the data structure MAGtype_MagneticResults, with the following elements
02753                         double Bx;      North
02754                         double By;      East
02755                         double Bz;      Down
02756 
02757 CALLS : none
02758 
02759  */
02760 {
02761     double Psi;
02762     /* Difference between the spherical and Geodetic latitudes */
02763     Psi = (M_PI / 180) * (CoordSpherical.phig - CoordGeodetic.phi);
02764 
02765     /* Rotate spherical field components to the Geodetic system */
02766     MagneticResultsGeo->Bz = MagneticResultsSph.Bx * sin(Psi) + MagneticResultsSph.Bz * cos(Psi);
02767     MagneticResultsGeo->Bx = MagneticResultsSph.Bx * cos(Psi) - MagneticResultsSph.Bz * sin(Psi);
02768     MagneticResultsGeo->By = MagneticResultsSph.By;
02769     return TRUE;
02770 } /*MAG_RotateMagneticVector*/
02771 
02772 void MAG_SphericalToCartesian(MAGtype_CoordSpherical CoordSpherical, double *x, double *y, double *z)
02773 {
02774     double radphi;
02775     double radlambda;
02776     
02777     radphi = CoordSpherical.phig * (M_PI / 180);
02778     radlambda = CoordSpherical.lambda * (M_PI / 180);
02779     
02780     *x = CoordSpherical.r * cos(radphi) * cos(radlambda);
02781     *y = CoordSpherical.r * cos(radphi) * sin(radlambda);
02782     *z = CoordSpherical.r * sin(radphi);
02783     return;
02784 }
02785 
02786 void MAG_TMfwd4(double Eps, double Epssq, double K0R4, double K0R4oa,
02787         double Acoeff[], double Lam0, double K0, double falseE,
02788         double falseN, int XYonly, double Lambda, double Phi,
02789         double *X, double *Y, double *pscale, double *CoM)
02790 {
02791 
02792     /*  Transverse Mercator forward equations including point-scale and CoM
02793             =--------- =------- =--=--= ---------
02794 
02795        Algorithm developed by: C. Rollins   August 7, 2006
02796        C software written by:  K. Robins
02797 
02798 
02799             Constants fixed by choice of ellipsoid and choice of projection parameters
02800             ---------------
02801 
02802               Eps          Eccentricity (epsilon) of the ellipsoid
02803               Epssq        Eccentricity squared
02804             ( R4           Meridional isoperimetric radius   )
02805             ( K0           Central scale factor              )
02806               K0R4         K0 times R4
02807               K0R4oa       K0 times Ratio of R4 over semi-major axis
02808               Acoeff       Trig series coefficients, omega as a function of chi
02809               Lam0         Longitude of the central meridian in radians
02810               K0           Central scale factor, for example, 0.9996 for UTM
02811               falseE       False easting, for example, 500000 for UTM
02812               falseN       False northing
02813 
02814        Processing option
02815        ---------- ------
02816 
02817               XYonly       If one (1), then only X and Y will be properly
02818                                        computed.  Values returned for point-scale
02819                                        and CoM will merely be the trivial values for
02820                                        points on the central meridian
02821 
02822        Input items that identify the point to be converted
02823        ----- -----
02824 
02825               Lambda       Longitude (from Greenwich) in radians
02826               Phi          Latitude in radians
02827 
02828        Output items
02829        ------ -----
02830 
02831               X            X coordinate (Easting) in meters
02832               Y            Y coordinate (Northing) in meters
02833               pscale       point-scale (dimensionless)
02834           CoM          Convergence-of-meridians in radians
02835      */
02836 
02837     double Lam, CLam, SLam, CPhi, SPhi;
02838     double P, part1, part2, denom, CChi, SChi;
02839     double U, V;
02840     double T, Tsq, denom2;
02841     double c2u, s2u, c4u, s4u, c6u, s6u, c8u, s8u;
02842     double c2v, s2v, c4v, s4v, c6v, s6v, c8v, s8v;
02843     double Xstar, Ystar;
02844     double sig1, sig2, comroo;
02845 
02846     /*
02847        Ellipsoid to sphere
02848        --------- -- ------
02849 
02850        Convert longitude (Greenwhich) to longitude from the central meridian
02851        It is unnecessary to find the (-Pi, Pi] equivalent of the result.
02852        Compute its cosine and sine.
02853      */
02854 
02855     Lam = Lambda - Lam0;
02856     CLam = cos(Lam);
02857     SLam = sin(Lam);
02858 
02859     /*   Latitude  */
02860 
02861     CPhi = cos(Phi);
02862     SPhi = sin(Phi);
02863 
02864     /*   Convert geodetic latitude, Phi, to conformal latitude, Chi
02865          Only the cosine and sine of Chi are actually needed.        */
02866 
02867     P = exp(Eps * ATanH(Eps * SPhi));
02868     part1 = (1 + SPhi) / P;
02869     part2 = (1 - SPhi) * P;
02870     denom = 1 / (part1 + part2);
02871     CChi = 2 * CPhi * denom;
02872     SChi = (part1 - part2) * denom;
02873 
02874     /*
02875        Sphere to first plane
02876        ------ -- ----- -----
02877 
02878        Apply spherical theory of transverse Mercator to get (u,v) coordinates
02879        Note the order of the arguments in Fortran's version of ArcTan, i.e.
02880                  atan2(y, x) = ATan(y/x)
02881        The two argument form of ArcTan is needed here.
02882      */
02883 
02884     T = CChi * SLam;
02885     U = ATanH(T);
02886     V = atan2(SChi, CChi * CLam);
02887 
02888     /*
02889        Trigonometric multiple angles
02890        ------------- -------- ------
02891 
02892        Compute Cosh of even multiples of U
02893        Compute Sinh of even multiples of U
02894        Compute Cos  of even multiples of V
02895        Compute Sin  of even multiples of V
02896      */
02897 
02898     Tsq = T * T;
02899     denom2 = 1 / (1 - Tsq);
02900     c2u = (1 + Tsq) * denom2;
02901     s2u = 2 * T * denom2;
02902     c2v = (-1 + CChi * CChi * (1 + CLam * CLam)) * denom2;
02903     s2v = 2 * CLam * CChi * SChi * denom2;
02904 
02905     c4u = 1 + 2 * s2u * s2u;
02906     s4u = 2 * c2u * s2u;
02907     c4v = 1 - 2 * s2v * s2v;
02908     s4v = 2 * c2v * s2v;
02909 
02910     c6u = c4u * c2u + s4u * s2u;
02911     s6u = s4u * c2u + c4u * s2u;
02912     c6v = c4v * c2v - s4v * s2v;
02913     s6v = s4v * c2v + c4v * s2v;
02914 
02915     c8u = 1 + 2 * s4u * s4u;
02916     s8u = 2 * c4u * s4u;
02917     c8v = 1 - 2 * s4v * s4v;
02918     s8v = 2 * c4v * s4v;
02919 
02920 
02921     /*   First plane to second plane
02922          ----- ----- -- ------ -----
02923 
02924          Accumulate terms for X and Y
02925      */
02926 
02927     Xstar = Acoeff[3] * s8u * c8v;
02928     Xstar = Xstar + Acoeff[2] * s6u * c6v;
02929     Xstar = Xstar + Acoeff[1] * s4u * c4v;
02930     Xstar = Xstar + Acoeff[0] * s2u * c2v;
02931     Xstar = Xstar + U;
02932 
02933     Ystar = Acoeff[3] * c8u * s8v;
02934     Ystar = Ystar + Acoeff[2] * c6u * s6v;
02935     Ystar = Ystar + Acoeff[1] * c4u * s4v;
02936     Ystar = Ystar + Acoeff[0] * c2u * s2v;
02937     Ystar = Ystar + V;
02938 
02939     /*   Apply isoperimetric radius, scale adjustment, and offsets  */
02940 
02941     *X = K0R4 * Xstar + falseE;
02942     *Y = K0R4 * Ystar + falseN;
02943 
02944 
02945     /*  Point-scale and CoM
02946         ----- ----- --- ---  */
02947 
02948     if(XYonly == 1)
02949     {
02950         *pscale = K0;
02951         *CoM = 0;
02952     } else
02953     {
02954         sig1 = 8 * Acoeff[3] * c8u * c8v;
02955         sig1 = sig1 + 6 * Acoeff[2] * c6u * c6v;
02956         sig1 = sig1 + 4 * Acoeff[1] * c4u * c4v;
02957         sig1 = sig1 + 2 * Acoeff[0] * c2u * c2v;
02958         sig1 = sig1 + 1;
02959 
02960         sig2 = 8 * Acoeff[3] * s8u * s8v;
02961         sig2 = sig2 + 6 * Acoeff[2] * s6u * s6v;
02962         sig2 = sig2 + 4 * Acoeff[1] * s4u * s4v;
02963         sig2 = sig2 + 2 * Acoeff[0] * s2u * s2v;
02964 
02965         /*    Combined square roots  */
02966         comroo = sqrt((1 - Epssq * SPhi * SPhi) * denom2 *
02967                 (sig1 * sig1 + sig2 * sig2));
02968 
02969         *pscale = K0R4oa * 2 * denom * comroo;
02970         *CoM = atan2(SChi * SLam, CLam) + atan2(sig2, sig1);
02971     }
02972 } /*MAG_TMfwd4*/
02973 
02974 int MAG_YearToDate(MAGtype_Date *CalendarDate)
02975 
02976 /* Converts a given Decimal year into a Year, Month and Date
02977 it also outputs an error string if there is a problem
02978 INPUT  CalendarDate  Pointer to the  data  structure with the following elements
02979                     double DecimalYear;      decimal years
02980 OUTPUT  CalendarDate  Pointer to the  data  structure with the following elements updated
02981  * int Year
02982  * int Month
02983  * int Day
02984                Error    pointer to an error string
02985 CALLS : none
02986 
02987  */
02988 {
02989     int MonthDays[13], CumulativeDays = 0;
02990     int ExtraDay = 0;
02991     int i, DayOfTheYear;
02992 
02993 
02994     if(CalendarDate->DecimalYear == 0)
02995     {
02996         CalendarDate->Year = 0;
02997         CalendarDate->Month = 0;
02998         CalendarDate->Day = 0;
02999         return FALSE;
03000     }
03001 
03002     CalendarDate->Year = (int) floor(CalendarDate->DecimalYear);
03003 
03004 
03005     if((CalendarDate->Year % 4 == 0 && CalendarDate->Year % 100 != 0) || CalendarDate->Year % 400 == 0)
03006         ExtraDay = 1;
03007 
03008     DayOfTheYear = floor((CalendarDate->DecimalYear - (double) CalendarDate->Year) * (365.0 + (double) ExtraDay)+0.5) + 1;
03009     /*The above floor is used for rounding, this only works for positive integers*/
03010 
03011 
03012     MonthDays[0] = 0;
03013     MonthDays[1] = 31;
03014     MonthDays[2] = 28 + ExtraDay;
03015     MonthDays[3] = 31;
03016     MonthDays[4] = 30;
03017     MonthDays[5] = 31;
03018     MonthDays[6] = 30;
03019     MonthDays[7] = 31;
03020     MonthDays[8] = 31;
03021     MonthDays[9] = 30;
03022     MonthDays[10] = 31;
03023     MonthDays[11] = 30;
03024     MonthDays[12] = 31;
03025 
03026 
03027     for(i = 1; i <= 12; i++)
03028     {
03029         CumulativeDays = CumulativeDays + MonthDays[i];
03030 
03031         if(DayOfTheYear <= CumulativeDays)
03032         {
03033             CalendarDate->Month = i;
03034             CalendarDate->Day = MonthDays[i] - (CumulativeDays - DayOfTheYear);
03035             break;
03036         }
03037 
03038 
03039     }
03040 
03041 
03042 
03043 
03044     return TRUE;
03045 
03046 } /*MAG_YearToDate*/
03047 
03048 
03049 
03050 /******************************************************************************
03051  ********************************Spherical Harmonics***************************
03052  * This grouping consists of functions that together take gauss coefficients 
03053  * and return a magnetic vector for an input location in spherical coordinates 
03054  ******************************************************************************/
03055 
03056 int MAG_AssociatedLegendreFunction(MAGtype_CoordSpherical CoordSpherical, int nMax, MAGtype_LegendreFunction *LegendreFunction)
03057 
03058 /* Computes  all of the Schmidt-semi normalized associated Legendre
03059 functions up to degree nMax. If nMax <= 16, function MAG_PcupLow is used.
03060 Otherwise MAG_PcupHigh is called.
03061 INPUT  CoordSpherical   A data structure with the following elements
03062                                                 double lambda; ( longitude)
03063                                                 double phig; ( geocentric latitude )
03064                                                 double r;         ( distance from the center of the ellipsoid)
03065                 nMax            integer          ( Maxumum degree of spherical harmonic secular model)
03066                 LegendreFunction Pointer to data structure with the following elements
03067                                                 double *Pcup;  (  pointer to store Legendre Function  )
03068                                                 double *dPcup; ( pointer to store  Derivative of Lagendre function )
03069 
03070 OUTPUT  LegendreFunction  Calculated Legendre variables in the data structure
03071 
03072  */
03073 {
03074     double sin_phi;
03075     int FLAG = 1;
03076 
03077     sin_phi = sin(DEG2RAD(CoordSpherical.phig)); /* sin  (geocentric latitude) */
03078 
03079     if(nMax <= 16 || (1 - fabs(sin_phi)) < 1.0e-10) /* If nMax is less tha 16 or at the poles */
03080         FLAG = MAG_PcupLow(LegendreFunction->Pcup, LegendreFunction->dPcup, sin_phi, nMax);
03081     else FLAG = MAG_PcupHigh(LegendreFunction->Pcup, LegendreFunction->dPcup, sin_phi, nMax);
03082     if(FLAG == 0) /* Error while computing  Legendre variables*/
03083         return FALSE;
03084 
03085 
03086     return TRUE;
03087 } /*MAG_AssociatedLegendreFunction */
03088 
03089 int MAG_CheckGeographicPole(MAGtype_CoordGeodetic *CoordGeodetic)
03090 
03091 /* Check if the latitude is equal to -90 or 90. If it is,
03092 offset it by 1e-5 to avoid division by zero. This is not currently used in the Geomagnetic
03093 main function. This may be used to avoid calling MAG_SummationSpecial.
03094 The function updates the input data structure.
03095 
03096 INPUT   CoordGeodetic Pointer to the  data  structure with the following elements
03097                 double lambda; (longitude)
03098                 double phi; ( geodetic latitude)
03099                 double HeightAboveEllipsoid; (height above the ellipsoid (HaE) )
03100                 double HeightAboveGeoid;(height above the Geoid )
03101 OUTPUT  CoordGeodetic  Pointer to the  data  structure with the following elements updates
03102                 double phi; ( geodetic latitude)
03103 CALLS : none
03104 
03105  */
03106 {
03107     CoordGeodetic->phi = CoordGeodetic->phi < (-90.0 + MAG_GEO_POLE_TOLERANCE) ? (-90.0 + MAG_GEO_POLE_TOLERANCE) : CoordGeodetic->phi;
03108     CoordGeodetic->phi = CoordGeodetic->phi > (90.0 - MAG_GEO_POLE_TOLERANCE) ? (90.0 - MAG_GEO_POLE_TOLERANCE) : CoordGeodetic->phi;
03109     return TRUE;
03110 } /*MAG_CheckGeographicPole*/
03111 
03112 int MAG_ComputeSphericalHarmonicVariables(MAGtype_Ellipsoid Ellip, MAGtype_CoordSpherical CoordSpherical, int nMax, MAGtype_SphericalHarmonicVariables *SphVariables)
03113 
03114 /* Computes Spherical variables
03115        Variables computed are (a/r)^(n+2), cos_m(lamda) and sin_m(lambda) for spherical harmonic
03116        summations. (Equations 10-12 in the WMM Technical Report)
03117        INPUT   Ellip  data  structure with the following elements
03118                              double a; semi-major axis of the ellipsoid
03119                              double b; semi-minor axis of the ellipsoid
03120                              double fla;  flattening
03121                              double epssq; first eccentricity squared
03122                              double eps;  first eccentricity
03123                              double re; mean radius of  ellipsoid
03124                      CoordSpherical     A data structure with the following elements
03125                              double lambda; ( longitude)
03126                              double phig; ( geocentric latitude )
03127                              double r;            ( distance from the center of the ellipsoid)
03128                      nMax   integer      ( Maxumum degree of spherical harmonic secular model)\
03129 
03130      OUTPUT  SphVariables  Pointer to the   data structure with the following elements
03131              double RelativeRadiusPower[MAG_MAX_MODEL_DEGREES+1];   [earth_reference_radius_km  sph. radius ]^n
03132              double cos_mlambda[MAG_MAX_MODEL_DEGREES+1]; cp(m)  - cosine of (mspherical coord. longitude)
03133              double sin_mlambda[MAG_MAX_MODEL_DEGREES+1];  sp(m)  - sine of (mspherical coord. longitude)
03134      CALLS : none
03135  */
03136 {
03137     double cos_lambda, sin_lambda;
03138     int m, n;
03139     cos_lambda = cos(DEG2RAD(CoordSpherical.lambda));
03140     sin_lambda = sin(DEG2RAD(CoordSpherical.lambda));
03141     /* for n = 0 ... model_order, compute (Radius of Earth / Spherical radius r)^(n+2)
03142     for n  1..nMax-1 (this is much faster than calling pow MAX_N+1 times).      */
03143     SphVariables->RelativeRadiusPower[0] = (Ellip.re / CoordSpherical.r) * (Ellip.re / CoordSpherical.r);
03144     for(n = 1; n <= nMax; n++)
03145     {
03146         SphVariables->RelativeRadiusPower[n] = SphVariables->RelativeRadiusPower[n - 1] * (Ellip.re / CoordSpherical.r);
03147     }
03148 
03149     /*
03150      Compute cos(m*lambda), sin(m*lambda) for m = 0 ... nMax
03151            cos(a + b) = cos(a)*cos(b) - sin(a)*sin(b)
03152            sin(a + b) = cos(a)*sin(b) + sin(a)*cos(b)
03153      */
03154     SphVariables->cos_mlambda[0] = 1.0;
03155     SphVariables->sin_mlambda[0] = 0.0;
03156 
03157     SphVariables->cos_mlambda[1] = cos_lambda;
03158     SphVariables->sin_mlambda[1] = sin_lambda;
03159     for(m = 2; m <= nMax; m++)
03160     {
03161         SphVariables->cos_mlambda[m] = SphVariables->cos_mlambda[m - 1] * cos_lambda - SphVariables->sin_mlambda[m - 1] * sin_lambda;
03162         SphVariables->sin_mlambda[m] = SphVariables->cos_mlambda[m - 1] * sin_lambda + SphVariables->sin_mlambda[m - 1] * cos_lambda;
03163     }
03164     return TRUE;
03165 } /*MAG_ComputeSphericalHarmonicVariables*/
03166 
03167 int MAG_PcupHigh(double *Pcup, double *dPcup, double x, int nMax)
03168 
03169 /*      This function evaluates all of the Schmidt-semi normalized associated Legendre
03170         functions up to degree nMax. The functions are initially scaled by
03171         10^280 sin^m in order to minimize the effects of underflow at large m
03172         near the poles (see Holmes and Featherstone 2002, J. Geodesy, 76, 279-299).
03173         Note that this function performs the same operation as MAG_PcupLow.
03174         However this function also can be used for high degree (large nMax) models.
03175 
03176         Calling Parameters:
03177                 INPUT
03178                         nMax:    Maximum spherical harmonic degree to compute.
03179                         x:              cos(colatitude) or sin(latitude).
03180 
03181                 OUTPUT
03182                         Pcup:   A vector of all associated Legendgre polynomials evaluated at
03183                                         x up to nMax. The lenght must by greater or equal to (nMax+1)*(nMax+2)/2.
03184                   dPcup:   Derivative of Pcup(x) with respect to latitude
03185 
03186                 CALLS : none
03187         Notes:
03188 
03189 
03190 
03191   Adopted from the FORTRAN code written by Mark Wieczorek September 25, 2005.
03192 
03193   Manoj Nair, Nov, 2009 Manoj.C.Nair@Noaa.Gov
03194 
03195   Change from the previous version
03196   The prevous version computes the derivatives as
03197   dP(n,m)(x)/dx, where x = sin(latitude) (or cos(colatitude) ).
03198   However, the WMM Geomagnetic routines requires dP(n,m)(x)/dlatitude.
03199   Hence the derivatives are multiplied by sin(latitude).
03200   Removed the options for CS phase and normalizations.
03201 
03202   Note: In geomagnetism, the derivatives of ALF are usually found with
03203   respect to the colatitudes. Here the derivatives are found with respect
03204   to the latitude. The difference is a sign reversal for the derivative of
03205   the Associated Legendre Functions.
03206 
03207   The derivatives can't be computed for latitude = |90| degrees.
03208  */
03209 {
03210     double pm2, pm1, pmm, plm, rescalem, z, scalef;
03211     double *f1, *f2, *PreSqr;
03212     int k, kstart, m, n, NumTerms;
03213 
03214     NumTerms = ((nMax + 1) * (nMax + 2) / 2);
03215 
03216 
03217     if(fabs(x) == 1.0)
03218     {
03219         printf("Error in PcupHigh: derivative cannot be calculated at poles\n");
03220         return FALSE;
03221     }
03222 
03223 
03224     f1 = (double *) malloc((NumTerms + 1) * sizeof ( double));
03225     if(f1 == NULL)
03226     {
03227         MAG_Error(18);
03228         //printf("error allocating in MAG_PcupHigh\n");
03229         return FALSE;
03230     }
03231 
03232 
03233     PreSqr = (double *) malloc((NumTerms + 1) * sizeof ( double));
03234 
03235     if(PreSqr == NULL)
03236     {
03237         MAG_Error(18);
03238         //printf("error allocating in MAG_PcupHigh\n");
03239         return FALSE;
03240     }
03241 
03242     f2 = (double *) malloc((NumTerms + 1) * sizeof ( double));
03243 
03244     if(f2 == NULL)
03245     {
03246         MAG_Error(18);
03247         //printf("error allocating in MAG_PcupHigh\n");
03248         return FALSE;
03249     }
03250 
03251     scalef = 1.0e-280;
03252 
03253     for(n = 0; n <= 2 * nMax + 1; ++n)
03254     {
03255         PreSqr[n] = sqrt((double) (n));
03256     }
03257 
03258     k = 2;
03259 
03260     for(n = 2; n <= nMax; n++)
03261     {
03262         k = k + 1;
03263         f1[k] = (double) (2 * n - 1) / (double) (n);
03264         f2[k] = (double) (n - 1) / (double) (n);
03265         for(m = 1; m <= n - 2; m++)
03266         {
03267             k = k + 1;
03268             f1[k] = (double) (2 * n - 1) / PreSqr[n + m] / PreSqr[n - m];
03269             f2[k] = PreSqr[n - m - 1] * PreSqr[n + m - 1] / PreSqr[n + m] / PreSqr[n - m];
03270         }
03271         k = k + 2;
03272     }
03273 
03274     /*z = sin (geocentric latitude) */
03275     z = sqrt((1.0 - x)*(1.0 + x));
03276     pm2 = 1.0;
03277     Pcup[0] = 1.0;
03278     dPcup[0] = 0.0;
03279     if(nMax == 0)
03280         return FALSE;
03281     pm1 = x;
03282     Pcup[1] = pm1;
03283     dPcup[1] = z;
03284     k = 1;
03285 
03286     for(n = 2; n <= nMax; n++)
03287     {
03288         k = k + n;
03289         plm = f1[k] * x * pm1 - f2[k] * pm2;
03290         Pcup[k] = plm;
03291         dPcup[k] = (double) (n) * (pm1 - x * plm) / z;
03292         pm2 = pm1;
03293         pm1 = plm;
03294     }
03295 
03296     pmm = PreSqr[2] * scalef;
03297     rescalem = 1.0 / scalef;
03298     kstart = 0;
03299 
03300     for(m = 1; m <= nMax - 1; ++m)
03301     {
03302         rescalem = rescalem*z;
03303 
03304         /* Calculate Pcup(m,m)*/
03305         kstart = kstart + m + 1;
03306         pmm = pmm * PreSqr[2 * m + 1] / PreSqr[2 * m];
03307         Pcup[kstart] = pmm * rescalem / PreSqr[2 * m + 1];
03308         dPcup[kstart] = -((double) (m) * x * Pcup[kstart] / z);
03309         pm2 = pmm / PreSqr[2 * m + 1];
03310         /* Calculate Pcup(m+1,m)*/
03311         k = kstart + m + 1;
03312         pm1 = x * PreSqr[2 * m + 1] * pm2;
03313         Pcup[k] = pm1*rescalem;
03314         dPcup[k] = ((pm2 * rescalem) * PreSqr[2 * m + 1] - x * (double) (m + 1) * Pcup[k]) / z;
03315         /* Calculate Pcup(n,m)*/
03316         for(n = m + 2; n <= nMax; ++n)
03317         {
03318             k = k + n;
03319             plm = x * f1[k] * pm1 - f2[k] * pm2;
03320             Pcup[k] = plm*rescalem;
03321             dPcup[k] = (PreSqr[n + m] * PreSqr[n - m] * (pm1 * rescalem) - (double) (n) * x * Pcup[k]) / z;
03322             pm2 = pm1;
03323             pm1 = plm;
03324         }
03325     }
03326 
03327     /* Calculate Pcup(nMax,nMax)*/
03328     rescalem = rescalem*z;
03329     kstart = kstart + m + 1;
03330     pmm = pmm / PreSqr[2 * nMax];
03331     Pcup[kstart] = pmm * rescalem;
03332     dPcup[kstart] = -(double) (nMax) * x * Pcup[kstart] / z;
03333     free(f1);
03334     free(PreSqr);
03335     free(f2);
03336 
03337     return TRUE;
03338 } /* MAG_PcupHigh */
03339 
03340 int MAG_PcupLow(double *Pcup, double *dPcup, double x, int nMax)
03341 
03342 /*   This function evaluates all of the Schmidt-semi normalized associated Legendre
03343         functions up to degree nMax.
03344 
03345         Calling Parameters:
03346                 INPUT
03347                         nMax:    Maximum spherical harmonic degree to compute.
03348                         x:              cos(colatitude) or sin(latitude).
03349 
03350                 OUTPUT
03351                         Pcup:   A vector of all associated Legendgre polynomials evaluated at
03352                                         x up to nMax.
03353                    dPcup: Derivative of Pcup(x) with respect to latitude
03354 
03355         Notes: Overflow may occur if nMax > 20 , especially for high-latitudes.
03356         Use MAG_PcupHigh for large nMax.
03357 
03358    Written by Manoj Nair, June, 2009 . Manoj.C.Nair@Noaa.Gov.
03359 
03360   Note: In geomagnetism, the derivatives of ALF are usually found with
03361   respect to the colatitudes. Here the derivatives are found with respect
03362   to the latitude. The difference is a sign reversal for the derivative of
03363   the Associated Legendre Functions.
03364  */
03365 {
03366     int n, m, index, index1, index2, NumTerms;
03367     double k, z, *schmidtQuasiNorm;
03368     Pcup[0] = 1.0;
03369     dPcup[0] = 0.0;
03370     /*sin (geocentric latitude) - sin_phi */
03371     z = sqrt((1.0 - x) * (1.0 + x));
03372 
03373     NumTerms = ((nMax + 1) * (nMax + 2) / 2);
03374     schmidtQuasiNorm = (double *) malloc((NumTerms + 1) * sizeof ( double));
03375 
03376     if(schmidtQuasiNorm == NULL)
03377     {
03378         MAG_Error(19);
03379         //printf("error allocating in MAG_PcupLow\n");
03380         return FALSE;
03381     }
03382 
03383     /*   First, Compute the Gauss-normalized associated Legendre  functions*/
03384     for(n = 1; n <= nMax; n++)
03385     {
03386         for(m = 0; m <= n; m++)
03387         {
03388             index = (n * (n + 1) / 2 + m);
03389             if(n == m)
03390             {
03391                 index1 = (n - 1) * n / 2 + m - 1;
03392                 Pcup [index] = z * Pcup[index1];
03393                 dPcup[index] = z * dPcup[index1] + x * Pcup[index1];
03394             } else if(n == 1 && m == 0)
03395             {
03396                 index1 = (n - 1) * n / 2 + m;
03397                 Pcup[index] = x * Pcup[index1];
03398                 dPcup[index] = x * dPcup[index1] - z * Pcup[index1];
03399             } else if(n > 1 && n != m)
03400             {
03401                 index1 = (n - 2) * (n - 1) / 2 + m;
03402                 index2 = (n - 1) * n / 2 + m;
03403                 if(m > n - 2)
03404                 {
03405                     Pcup[index] = x * Pcup[index2];
03406                     dPcup[index] = x * dPcup[index2] - z * Pcup[index2];
03407                 } else
03408                 {
03409                     k = (double) (((n - 1) * (n - 1)) - (m * m)) / (double) ((2 * n - 1) * (2 * n - 3));
03410                     Pcup[index] = x * Pcup[index2] - k * Pcup[index1];
03411                     dPcup[index] = x * dPcup[index2] - z * Pcup[index2] - k * dPcup[index1];
03412                 }
03413             }
03414         }
03415     }
03416     /* Compute the ration between the the Schmidt quasi-normalized associated Legendre
03417      * functions and the Gauss-normalized version. */
03418 
03419     schmidtQuasiNorm[0] = 1.0;
03420     for(n = 1; n <= nMax; n++)
03421     {
03422         index = (n * (n + 1) / 2);
03423         index1 = (n - 1) * n / 2;
03424         /* for m = 0 */
03425         schmidtQuasiNorm[index] = schmidtQuasiNorm[index1] * (double) (2 * n - 1) / (double) n;
03426 
03427         for(m = 1; m <= n; m++)
03428         {
03429             index = (n * (n + 1) / 2 + m);
03430             index1 = (n * (n + 1) / 2 + m - 1);
03431             schmidtQuasiNorm[index] = schmidtQuasiNorm[index1] * sqrt((double) ((n - m + 1) * (m == 1 ? 2 : 1)) / (double) (n + m));
03432         }
03433 
03434     }
03435 
03436     /* Converts the  Gauss-normalized associated Legendre
03437               functions to the Schmidt quasi-normalized version using pre-computed
03438               relation stored in the variable schmidtQuasiNorm */
03439 
03440     for(n = 1; n <= nMax; n++)
03441     {
03442         for(m = 0; m <= n; m++)
03443         {
03444             index = (n * (n + 1) / 2 + m);
03445             Pcup[index] = Pcup[index] * schmidtQuasiNorm[index];
03446             dPcup[index] = -dPcup[index] * schmidtQuasiNorm[index];
03447             /* The sign is changed since the new WMM routines use derivative with respect to latitude
03448             insted of co-latitude */
03449         }
03450     }
03451 
03452     if(schmidtQuasiNorm)
03453         free(schmidtQuasiNorm);
03454     return TRUE;
03455 } /*MAG_PcupLow */
03456 
03457 int MAG_SecVarSummation(MAGtype_LegendreFunction *LegendreFunction, MAGtype_MagneticModel *MagneticModel, MAGtype_SphericalHarmonicVariables SphVariables, MAGtype_CoordSpherical CoordSpherical, MAGtype_MagneticResults *MagneticResults)
03458 {
03459     /*This Function sums the secular variation coefficients to get the secular variation of the Magnetic vector.
03460     INPUT :  LegendreFunction
03461                     MagneticModel
03462                     SphVariables
03463                     CoordSpherical
03464     OUTPUT : MagneticResults
03465 
03466     CALLS : MAG_SecVarSummationSpecial
03467 
03468      */
03469     int m, n, index;
03470     double cos_phi;
03471     MagneticModel->SecularVariationUsed = TRUE;
03472     MagneticResults->Bz = 0.0;
03473     MagneticResults->By = 0.0;
03474     MagneticResults->Bx = 0.0;
03475     for(n = 1; n <= MagneticModel->nMaxSecVar; n++)
03476     {
03477         for(m = 0; m <= n; m++)
03478         {
03479             index = (n * (n + 1) / 2 + m);
03480 
03481             /*              nMax        (n+2)     n     m            m           m
03482                     Bz =   -SUM (a/r)   (n+1) SUM  [g cos(m p) + h sin(m p)] P (sin(phi))
03483                                     n=1               m=0   n            n           n  */
03484             /*  Derivative with respect to radius.*/
03485             MagneticResults->Bz -= SphVariables.RelativeRadiusPower[n] *
03486                     (MagneticModel->Secular_Var_Coeff_G[index] * SphVariables.cos_mlambda[m] +
03487                     MagneticModel->Secular_Var_Coeff_H[index] * SphVariables.sin_mlambda[m])
03488                     * (double) (n + 1) * LegendreFunction-> Pcup[index];
03489 
03490             /*            1 nMax  (n+2)    n     m            m           m
03491                     By =    SUM (a/r) (m)  SUM  [g cos(m p) + h sin(m p)] dP (sin(phi))
03492                                n=1             m=0   n            n           n  */
03493             /* Derivative with respect to longitude, divided by radius. */
03494             MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
03495                     (MagneticModel->Secular_Var_Coeff_G[index] * SphVariables.sin_mlambda[m] -
03496                     MagneticModel->Secular_Var_Coeff_H[index] * SphVariables.cos_mlambda[m])
03497                     * (double) (m) * LegendreFunction-> Pcup[index];
03498             /*             nMax  (n+2) n     m            m           m
03499                     Bx = - SUM (a/r)   SUM  [g cos(m p) + h sin(m p)] dP (sin(phi))
03500                                n=1         m=0   n            n           n  */
03501             /* Derivative with respect to latitude, divided by radius. */
03502 
03503             MagneticResults->Bx -= SphVariables.RelativeRadiusPower[n] *
03504                     (MagneticModel->Secular_Var_Coeff_G[index] * SphVariables.cos_mlambda[m] +
03505                     MagneticModel->Secular_Var_Coeff_H[index] * SphVariables.sin_mlambda[m])
03506                     * LegendreFunction-> dPcup[index];
03507         }
03508     }
03509     cos_phi = cos(DEG2RAD(CoordSpherical.phig));
03510     if(fabs(cos_phi) > 1.0e-10)
03511     {
03512         MagneticResults->By = MagneticResults->By / cos_phi;
03513     } else
03514         /* Special calculation for component By at Geographic poles */
03515     {
03516         MAG_SecVarSummationSpecial(MagneticModel, SphVariables, CoordSpherical, MagneticResults);
03517     }
03518     return TRUE;
03519 } /*MAG_SecVarSummation*/
03520 
03521 int MAG_SecVarSummationSpecial(MAGtype_MagneticModel *MagneticModel, MAGtype_SphericalHarmonicVariables SphVariables, MAGtype_CoordSpherical CoordSpherical, MAGtype_MagneticResults *MagneticResults)
03522 {
03523     /*Special calculation for the secular variation summation at the poles.
03524 
03525 
03526     INPUT: MagneticModel
03527                SphVariables
03528                CoordSpherical
03529     OUTPUT: MagneticResults
03530     CALLS : none
03531 
03532 
03533      */
03534     int n, index;
03535     double k, sin_phi, *PcupS, schmidtQuasiNorm1, schmidtQuasiNorm2, schmidtQuasiNorm3;
03536 
03537     PcupS = (double *) malloc((MagneticModel->nMaxSecVar + 1) * sizeof (double));
03538 
03539     if(PcupS == NULL)
03540     {
03541         MAG_Error(15);
03542         //printf("error allocating in MAG_SummationSpecial\n");
03543         return FALSE;
03544     }
03545 
03546     PcupS[0] = 1;
03547     schmidtQuasiNorm1 = 1.0;
03548 
03549     MagneticResults->By = 0.0;
03550     sin_phi = sin(DEG2RAD(CoordSpherical.phig));
03551 
03552     for(n = 1; n <= MagneticModel->nMaxSecVar; n++)
03553     {
03554         index = (n * (n + 1) / 2 + 1);
03555         schmidtQuasiNorm2 = schmidtQuasiNorm1 * (double) (2 * n - 1) / (double) n;
03556         schmidtQuasiNorm3 = schmidtQuasiNorm2 * sqrt((double) (n * 2) / (double) (n + 1));
03557         schmidtQuasiNorm1 = schmidtQuasiNorm2;
03558         if(n == 1)
03559         {
03560             PcupS[n] = PcupS[n - 1];
03561         } else
03562         {
03563             k = (double) (((n - 1) * (n - 1)) - 1) / (double) ((2 * n - 1) * (2 * n - 3));
03564             PcupS[n] = sin_phi * PcupS[n - 1] - k * PcupS[n - 2];
03565         }
03566 
03567         /*                1 nMax  (n+2)    n     m            m           m
03568                 By =    SUM (a/r) (m)  SUM  [g cos(m p) + h sin(m p)] dP (sin(phi))
03569                            n=1             m=0   n            n           n  */
03570         /* Derivative with respect to longitude, divided by radius. */
03571 
03572         MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
03573                 (MagneticModel->Secular_Var_Coeff_G[index] * SphVariables.sin_mlambda[1] -
03574                 MagneticModel->Secular_Var_Coeff_H[index] * SphVariables.cos_mlambda[1])
03575                 * PcupS[n] * schmidtQuasiNorm3;
03576     }
03577 
03578     if(PcupS)
03579         free(PcupS);
03580     return TRUE;
03581 }/*SecVarSummationSpecial*/
03582 
03583 int MAG_Summation(MAGtype_LegendreFunction *LegendreFunction, MAGtype_MagneticModel *MagneticModel, MAGtype_SphericalHarmonicVariables SphVariables, MAGtype_CoordSpherical CoordSpherical, MAGtype_MagneticResults *MagneticResults)
03584 {
03585     /* Computes Geomagnetic Field Elements X, Y and Z in Spherical coordinate system using
03586     spherical harmonic summation.
03587 
03588 
03589     The vector Magnetic field is given by -grad V, where V is Geomagnetic scalar potential
03590     The gradient in spherical coordinates is given by:
03591 
03592                      dV ^     1 dV ^        1     dV ^
03593     grad V = -- r  +  - -- t  +  -------- -- p
03594                      dr       r dt       r sin(t) dp
03595 
03596 
03597     INPUT :  LegendreFunction
03598                     MagneticModel
03599                     SphVariables
03600                     CoordSpherical
03601     OUTPUT : MagneticResults
03602 
03603     CALLS : MAG_SummationSpecial
03604 
03605 
03606 
03607     Manoj Nair, June, 2009 Manoj.C.Nair@Noaa.Gov
03608      */
03609     int m, n, index;
03610     double cos_phi;
03611     MagneticResults->Bz = 0.0;
03612     MagneticResults->By = 0.0;
03613     MagneticResults->Bx = 0.0;
03614     for(n = 1; n <= MagneticModel->nMax; n++)
03615     {
03616         for(m = 0; m <= n; m++)
03617         {
03618             index = (n * (n + 1) / 2 + m);
03619 
03620             /*              nMax        (n+2)     n     m            m           m
03621                     Bz =   -SUM (a/r)   (n+1) SUM  [g cos(m p) + h sin(m p)] P (sin(phi))
03622                                     n=1               m=0   n            n           n  */
03623             /* Equation 12 in the WMM Technical report.  Derivative with respect to radius.*/
03624             MagneticResults->Bz -= SphVariables.RelativeRadiusPower[n] *
03625                     (MagneticModel->Main_Field_Coeff_G[index] * SphVariables.cos_mlambda[m] +
03626                     MagneticModel->Main_Field_Coeff_H[index] * SphVariables.sin_mlambda[m])
03627                     * (double) (n + 1) * LegendreFunction-> Pcup[index];
03628 
03629             /*            1 nMax  (n+2)    n     m            m           m
03630                     By =    SUM (a/r) (m)  SUM  [g cos(m p) + h sin(m p)] dP (sin(phi))
03631                                n=1             m=0   n            n           n  */
03632             /* Equation 11 in the WMM Technical report. Derivative with respect to longitude, divided by radius. */
03633             MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
03634                     (MagneticModel->Main_Field_Coeff_G[index] * SphVariables.sin_mlambda[m] -
03635                     MagneticModel->Main_Field_Coeff_H[index] * SphVariables.cos_mlambda[m])
03636                     * (double) (m) * LegendreFunction-> Pcup[index];
03637             /*             nMax  (n+2) n     m            m           m
03638                     Bx = - SUM (a/r)   SUM  [g cos(m p) + h sin(m p)] dP (sin(phi))
03639                                n=1         m=0   n            n           n  */
03640             /* Equation 10  in the WMM Technical report. Derivative with respect to latitude, divided by radius. */
03641 
03642             MagneticResults->Bx -= SphVariables.RelativeRadiusPower[n] *
03643                     (MagneticModel->Main_Field_Coeff_G[index] * SphVariables.cos_mlambda[m] +
03644                     MagneticModel->Main_Field_Coeff_H[index] * SphVariables.sin_mlambda[m])
03645                     * LegendreFunction-> dPcup[index];
03646 
03647 
03648 
03649         }
03650     }
03651 
03652     cos_phi = cos(DEG2RAD(CoordSpherical.phig));
03653     if(fabs(cos_phi) > 1.0e-10)
03654     {
03655         MagneticResults->By = MagneticResults->By / cos_phi;
03656     } else
03657         /* Special calculation for component - By - at Geographic poles.
03658          * If the user wants to avoid using this function,  please make sure that
03659          * the latitude is not exactly +/-90. An option is to make use the function
03660          * MAG_CheckGeographicPoles.
03661          */
03662     {
03663         MAG_SummationSpecial(MagneticModel, SphVariables, CoordSpherical, MagneticResults);
03664     }
03665     return TRUE;
03666 }/*MAG_Summation */
03667 
03668 int MAG_SummationSpecial(MAGtype_MagneticModel *MagneticModel, MAGtype_SphericalHarmonicVariables SphVariables, MAGtype_CoordSpherical CoordSpherical, MAGtype_MagneticResults *MagneticResults)
03669 /* Special calculation for the component By at Geographic poles.
03670 Manoj Nair, June, 2009 manoj.c.nair@noaa.gov
03671 INPUT: MagneticModel
03672            SphVariables
03673            CoordSpherical
03674 OUTPUT: MagneticResults
03675 CALLS : none
03676 See Section 1.4, "SINGULARITIES AT THE GEOGRAPHIC POLES", WMM Technical report
03677 
03678  */
03679 {
03680     int n, index;
03681     double k, sin_phi, *PcupS, schmidtQuasiNorm1, schmidtQuasiNorm2, schmidtQuasiNorm3;
03682 
03683     PcupS = (double *) malloc((MagneticModel->nMax + 1) * sizeof (double));
03684     if(PcupS == 0)
03685     {
03686         MAG_Error(14);
03687         //printf("error allocating in MAG_SummationSpecial\n");
03688         return FALSE;
03689     }
03690 
03691     PcupS[0] = 1;
03692     schmidtQuasiNorm1 = 1.0;
03693 
03694     MagneticResults->By = 0.0;
03695     sin_phi = sin(DEG2RAD(CoordSpherical.phig));
03696 
03697     for(n = 1; n <= MagneticModel->nMax; n++)
03698     {
03699 
03700         /*Compute the ration between the Gauss-normalized associated Legendre
03701   functions and the Schmidt quasi-normalized version. This is equivalent to
03702   sqrt((m==0?1:2)*(n-m)!/(n+m!))*(2n-1)!!/(n-m)!  */
03703 
03704         index = (n * (n + 1) / 2 + 1);
03705         schmidtQuasiNorm2 = schmidtQuasiNorm1 * (double) (2 * n - 1) / (double) n;
03706         schmidtQuasiNorm3 = schmidtQuasiNorm2 * sqrt((double) (n * 2) / (double) (n + 1));
03707         schmidtQuasiNorm1 = schmidtQuasiNorm2;
03708         if(n == 1)
03709         {
03710             PcupS[n] = PcupS[n - 1];
03711         } else
03712         {
03713             k = (double) (((n - 1) * (n - 1)) - 1) / (double) ((2 * n - 1) * (2 * n - 3));
03714             PcupS[n] = sin_phi * PcupS[n - 1] - k * PcupS[n - 2];
03715         }
03716 
03717         /*                1 nMax  (n+2)    n     m            m           m
03718                 By =    SUM (a/r) (m)  SUM  [g cos(m p) + h sin(m p)] dP (sin(phi))
03719                            n=1             m=0   n            n           n  */
03720         /* Equation 11 in the WMM Technical report. Derivative with respect to longitude, divided by radius. */
03721 
03722         MagneticResults->By += SphVariables.RelativeRadiusPower[n] *
03723                 (MagneticModel->Main_Field_Coeff_G[index] * SphVariables.sin_mlambda[1] -
03724                 MagneticModel->Main_Field_Coeff_H[index] * SphVariables.cos_mlambda[1])
03725                 * PcupS[n] * schmidtQuasiNorm3;
03726     }
03727 
03728     if(PcupS)
03729         free(PcupS);
03730     return TRUE;
03731 }/*MAG_SummationSpecial */
03732 
03733 int MAG_TimelyModifyMagneticModel(MAGtype_Date UserDate, MAGtype_MagneticModel *MagneticModel, MAGtype_MagneticModel *TimedMagneticModel)
03734 
03735 /* Time change the Model coefficients from the base year of the model using secular variation coefficients.
03736 Store the coefficients of the static model with their values advanced from epoch t0 to epoch t.
03737 Copy the SV coefficients.  If input "t�" is the same as "t0", then this is merely a copy operation.
03738 If the address of "TimedMagneticModel" is the same as the address of "MagneticModel", then this procedure overwrites
03739 the given item "MagneticModel".
03740 
03741 INPUT: UserDate
03742            MagneticModel
03743 OUTPUT:TimedMagneticModel
03744 CALLS : none
03745  */
03746 {
03747     int n, m, index, a, b;
03748     TimedMagneticModel->EditionDate = MagneticModel->EditionDate;
03749     TimedMagneticModel->epoch = MagneticModel->epoch;
03750     TimedMagneticModel->nMax = MagneticModel->nMax;
03751     TimedMagneticModel->nMaxSecVar = MagneticModel->nMaxSecVar;
03752     a = TimedMagneticModel->nMaxSecVar;
03753     b = (a * (a + 1) / 2 + a);
03754     strcpy(TimedMagneticModel->ModelName, MagneticModel->ModelName);
03755     for(n = 1; n <= MagneticModel->nMax; n++)
03756     {
03757         for(m = 0; m <= n; m++)
03758         {
03759             index = (n * (n + 1) / 2 + m);
03760             if(index <= b)
03761             {
03762                 TimedMagneticModel->Main_Field_Coeff_H[index] = MagneticModel->Main_Field_Coeff_H[index] + (UserDate.DecimalYear - MagneticModel->epoch) * MagneticModel->Secular_Var_Coeff_H[index];
03763                 TimedMagneticModel->Main_Field_Coeff_G[index] = MagneticModel->Main_Field_Coeff_G[index] + (UserDate.DecimalYear - MagneticModel->epoch) * MagneticModel->Secular_Var_Coeff_G[index];
03764                 TimedMagneticModel->Secular_Var_Coeff_H[index] = MagneticModel->Secular_Var_Coeff_H[index]; // We need a copy of the secular var coef to calculate secular change 
03765                 TimedMagneticModel->Secular_Var_Coeff_G[index] = MagneticModel->Secular_Var_Coeff_G[index];
03766             } else
03767             {
03768                 TimedMagneticModel->Main_Field_Coeff_H[index] = MagneticModel->Main_Field_Coeff_H[index];
03769                 TimedMagneticModel->Main_Field_Coeff_G[index] = MagneticModel->Main_Field_Coeff_G[index];
03770             }
03771         }
03772     }
03773     return TRUE;
03774 } /* MAG_TimelyModifyMagneticModel */
03775 
03776 /*End of Spherical Harmonic Functions*/
03777 
03778 
03779 /******************************************************************************
03780  *************************************Geoid************************************
03781  * This grouping consists of functions that make calculations to adjust 
03782  * ellipsoid height to height above the geoid (Height above MSL). 
03783  ******************************************************************************
03784  ******************************************************************************/
03785 
03786 
03787 int MAG_ConvertGeoidToEllipsoidHeight(MAGtype_CoordGeodetic *CoordGeodetic, MAGtype_Geoid *Geoid)
03788 
03789 /*
03790  * The function Convert_Geoid_To_Ellipsoid_Height converts the specified WGS84
03791  * Geoid height at the specified geodetic coordinates to the equivalent
03792  * ellipsoid height, using the EGM96 gravity model.
03793  *
03794  *   CoordGeodetic->phi        : Geodetic latitude in degress           (input)
03795  *    CoordGeodetic->lambda     : Geodetic longitude in degrees          (input)
03796  *    CoordGeodetic->HeightAboveEllipsoid            : Ellipsoid height, in kilometers         (output)
03797  *    CoordGeodetic->HeightAboveGeoid: Geoid height, in kilometers           (input)
03798  *
03799         CALLS : MAG_GetGeoidHeight (
03800 
03801  */
03802 {
03803     double DeltaHeight;
03804     int Error_Code;
03805 
03806     if(Geoid->UseGeoid == 1)
03807     { /* Geoid correction required */
03808         Error_Code = MAG_GetGeoidHeight(CoordGeodetic->phi, CoordGeodetic->lambda, &DeltaHeight, Geoid);
03809         CoordGeodetic->HeightAboveEllipsoid = CoordGeodetic->HeightAboveGeoid + DeltaHeight / 1000; /*  Input and output should be kilometers,
03810                         However MAG_GetGeoidHeight returns Geoid height in meters - Hence division by 1000 */
03811     } else /* Geoid correction not required, copy the MSL height to Ellipsoid height */
03812     {
03813         CoordGeodetic->HeightAboveEllipsoid = CoordGeodetic->HeightAboveGeoid;
03814         Error_Code = TRUE;
03815     }
03816     return ( Error_Code);
03817 } /* MAG_ConvertGeoidToEllipsoidHeight*/
03818 
03819 int MAG_GetGeoidHeight(double Latitude,
03820         double Longitude,
03821         double *DeltaHeight,
03822         MAGtype_Geoid *Geoid)
03823 /*
03824  * The  function MAG_GetGeoidHeight returns the height of the
03825  * EGM96 geiod above or below the WGS84 ellipsoid,
03826  * at the specified geodetic coordinates,
03827  * using a grid of height adjustments from the EGM96 gravity model.
03828  *
03829  *    Latitude            : Geodetic latitude in radians           (input)
03830  *    Longitude           : Geodetic longitude in radians          (input)
03831  *    DeltaHeight         : Height Adjustment, in meters.          (output)
03832  *    Geoid                               : MAGtype_Geoid with Geoid grid                  (input)
03833         CALLS : none
03834  */
03835 {
03836     long Index;
03837     double DeltaX, DeltaY;
03838     double ElevationSE, ElevationSW, ElevationNE, ElevationNW;
03839     double OffsetX, OffsetY;
03840     double PostX, PostY;
03841     double UpperY, LowerY;
03842     int Error_Code = 0;
03843 
03844     if(!Geoid->Geoid_Initialized)
03845     {
03846         MAG_Error(5);
03847         //printf("Geoid not initialized\n");
03848         return (FALSE);
03849     }
03850     if((Latitude < -90) || (Latitude > 90))
03851     { /* Latitude out of range */
03852         Error_Code |= 1;
03853     }
03854     if((Longitude < -180) || (Longitude > 360))
03855     { /* Longitude out of range */
03856         Error_Code |= 1;
03857     }
03858 
03859     if(!Error_Code)
03860     { /* no errors */
03861         /*  Compute X and Y Offsets into Geoid Height Array:                          */
03862 
03863         if(Longitude < 0.0)
03864         {
03865             OffsetX = (Longitude + 360.0) * Geoid->ScaleFactor;
03866         } else
03867         {
03868             OffsetX = Longitude * Geoid->ScaleFactor;
03869         }
03870         OffsetY = (90.0 - Latitude) * Geoid->ScaleFactor;
03871 
03872         /*  Find Four Nearest Geoid Height Cells for specified Latitude, Longitude;   */
03873         /*  Assumes that (0,0) of Geoid Height Array is at Northwest corner:          */
03874 
03875         PostX = floor(OffsetX);
03876         if((PostX + 1) == Geoid->NumbGeoidCols)
03877             PostX--;
03878         PostY = floor(OffsetY);
03879         if((PostY + 1) == Geoid->NumbGeoidRows)
03880             PostY--;
03881 
03882         Index = (long) (PostY * Geoid->NumbGeoidCols + PostX);
03883         ElevationNW = (double) Geoid->GeoidHeightBuffer[ Index ];
03884         ElevationNE = (double) Geoid->GeoidHeightBuffer[ Index + 1 ];
03885 
03886         Index = (long) ((PostY + 1) * Geoid->NumbGeoidCols + PostX);
03887         ElevationSW = (double) Geoid->GeoidHeightBuffer[ Index ];
03888         ElevationSE = (double) Geoid->GeoidHeightBuffer[ Index + 1 ];
03889 
03890         /*  Perform Bi-Linear Interpolation to compute Height above Ellipsoid:        */
03891 
03892         DeltaX = OffsetX - PostX;
03893         DeltaY = OffsetY - PostY;
03894 
03895         UpperY = ElevationNW + DeltaX * (ElevationNE - ElevationNW);
03896         LowerY = ElevationSW + DeltaX * (ElevationSE - ElevationSW);
03897 
03898         *DeltaHeight = UpperY + DeltaY * (LowerY - UpperY);
03899     } else
03900     {
03901         MAG_Error(17);
03902         //printf("Latitude OR Longitude out of range in MAG_GetGeoidHeight\n");
03903         return (FALSE);
03904     }
03905     return TRUE;
03906 } /*MAG_GetGeoidHeight*/
03907 
03908 int MAG_InitializeGeoid(MAGtype_Geoid *Geoid)
03909 /*
03910  * The function reads Geoid data from the file EMG9615.BIN in
03911  * the current directory and builds the Geoid grid from it.
03912  * If the Geoid file can not be found or accessed, an error is printed
03913  * and function returns false code. If the file is incomplete
03914  * or improperly formatted, an error is printed
03915  * and function returns false code.
03916 
03917  INPUT  Pointer to data structure Geoid with the following elements
03918                 int NumbGeoidCols ;   ( 360 degrees of longitude at 15 minute spacing )
03919                 int NumbGeoidRows ;   ( 180 degrees of latitude  at 15 minute spacing )
03920                 int NumbHeaderItems ;    ( min, max lat, min, max long, lat, long spacing )
03921                 int     ScaleFactor;    ( 4 grid cells per degree at 15 minute spacing  )
03922                 float *GeoidHeightBuffer;   (Pointer to the memory to store the Geoid elevation data )
03923                 int NumbGeoidElevs;    (number of points in the gridded file )
03924                 int  Geoid_Initialized ;  ( indicates successful initialization )
03925 
03926 OUPUT  Pointer to data structure Geoid with the following elements updated
03927                 int NumbGeoidCols ;   ( 360 degrees of longitude at 15 minute spacing )
03928                 int NumbGeoidRows ;   ( 180 degrees of latitude  at 15 minute spacing )
03929                 int NumbHeaderItems ;    ( min, max lat, min, max long, lat, long spacing )
03930                 int     ScaleFactor;    ( 4 grid cells per degree at 15 minute spacing  )
03931                 float *GeoidHeightBuffer;   (Pointer to the memory to store the Geoid elevation data )
03932                 int NumbGeoidElevs;    (number of points in the gridded file )
03933 CALLS : none
03934  */
03935 {
03936     int ElevationsRead, SwabType, Index;
03937     FILE *GeoidHeightFile;
03938 
03939 
03940     if(Geoid->Geoid_Initialized)
03941     {
03942         return (TRUE);
03943     } else
03944     {
03945 
03946         Geoid->GeoidHeightBuffer = (float *) malloc((Geoid->NumbGeoidElevs + 1) * sizeof (float));
03947         if(!Geoid->GeoidHeightBuffer)
03948         {
03949             MAG_Error(3);
03950             //   printf("error allocating in MAG_InitializeGeoid\n");
03951             return (FALSE);
03952         }
03953 
03954         /*  Open the File READONLY, or Return Error Condition.  EMG9615.BIN is binary
03955              dump of the ascii file WW15MGH.GRD. This file contains  EGM96 Geoid heights
03956              in 15x15 min resolution. The binary file supplied with the WMM package is
03957              Little Endian. Now check the system to determine its Endianness*/
03958 
03959 
03960 
03961 
03962         if((GeoidHeightFile = fopen("EGM9615.BIN", "rb")) == NULL)
03963         {
03964             MAG_Error(16);
03965             //printf("Error in opening EGM9615.BIN file\n");
03966             return (FALSE);
03967         }
03968 
03969         ElevationsRead = fread(Geoid->GeoidHeightBuffer, sizeof (float), Geoid->NumbGeoidElevs, GeoidHeightFile);
03970 
03971         if(ElevationsRead != Geoid->NumbGeoidElevs)
03972         {
03973 
03974             MAG_Error(3);
03975             //printf("Error in Geoid initilazation\n");
03976             return ( FALSE);
03977 
03978         }
03979         fclose(GeoidHeightFile);
03980 
03981         SwabType = MAG_swab_type(); /* Returns the Edianness of the system */
03982 
03983         /* MAG_swab_type() returns
03984               0 : Big Endian    (less common)
03985               1 : Small Endian (most common)
03986               2 : PDP (middle) Endian - not supported by WMM Software
03987          */
03988 
03989         if(SwabType == 0)
03990         { /* The system is Big Endian */
03991 
03992             for(Index = 0; Index < ElevationsRead; Index++)
03993                 /* Convert the float values from Small Endian to Big Endian */
03994                 Geoid->GeoidHeightBuffer[Index] = (float) MAG_FloatSwap(Geoid->GeoidHeightBuffer[Index]);
03995             /*  Geoid->GeoidHeightBuffer[Index] = Geoid->GeoidHeightBuffer[Index];*/
03996         }
03997 
03998 
03999 
04000         Geoid->Geoid_Initialized = 1;
04001     }
04002     return ( TRUE);
04003 } /*MAG_InitializeGeoid*/
04004 
04005 /*End of Geoid Functions*/
04006 
04007 
04008 
04009 /*Functions made obsolete by removal of Geoid binary file*/
04010 int MAG_swab_type()
04011 {
04012 
04013     /* Determine the Endianess of the system
04014 
04015         OUTPUT  0 : Big_Endian
04016                         1 : Little Endian
04017                         2 : PDP Endian
04018                         3 : Unknown type
04019 
04020      */
04021     union swabtest {
04022         unsigned long l;
04023         unsigned char c[4];
04024     } swabtest;
04025 
04026     swabtest.l = 0xaabbccdd;
04027 
04028     if((swabtest.c[0] == 0xaa) && (swabtest.c[1] == 0xbb) &&
04029             (swabtest.c[2] == 0xcc) && (swabtest.c[3] == 0xdd))
04030     {
04031         /* BIG_ENDIAN */
04032         return 0;
04033     } else if((swabtest.c[0] == 0xdd) && (swabtest.c[1] == 0xcc) &&
04034             (swabtest.c[2] == 0xbb) && (swabtest.c[3] == 0xaa))
04035     {
04036         /* LITTLE_ENDIAN */
04037         return 1;
04038     } else if((swabtest.c[0] == 0xbb) && (swabtest.c[1] == 0xaa) &&
04039             (swabtest.c[2] == 0xdd) && (swabtest.c[3] == 0xcc))
04040     {
04041         /* PDP_ENDIAN */
04042         return 2;
04043     } else
04044     {
04045         /* Unknown */
04046         return -1;
04047     }
04048 }
04049 
04050 float MAG_FloatSwap(float f)
04051 
04052 /* Swap a float  from BIG ENDIAN to LITTLE ENDIAN
04053 or vice versa */
04054 {
04055 
04056     union {
04057         float f;
04058         unsigned char b[4];
04059     } dat1, dat2;
04060 
04061     dat1.f = f;
04062     dat2.b[0] = dat1.b[3];
04063     dat2.b[1] = dat1.b[2];
04064     dat2.b[2] = dat1.b[1];
04065     dat2.b[3] = dat1.b[0];
04066     return dat2.f;
04067 }


world_magnetic_model
Author(s): National Geophysical Data Center (NGDC, Boulder CO, USA), British Geological Survey (BGS, Edinburgh, Scotland)
autogenerated on Sat Jul 26 2014 07:02:19