2010-09-21 21:29:35 +02:00
/**
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* @ file WorldMagModel . c
* @ author The OpenPilot Team , http : //www.openpilot.org Copyright (C) 2010.
* @ brief Source file for the World Magnetic Model
* This is a port of code available from the US NOAA .
2011-01-11 16:45:16 +01:00
*
2010-09-21 21:29:35 +02:00
* The hard coded coefficients should be valid until 2015.
2011-01-11 16:45:16 +01:00
*
* Updated coeffs from . .
* http : //www.ngdc.noaa.gov/geomag/WMM/wmm_ddownload.shtml
*
* NASA C source code . .
* http : //www.ngdc.noaa.gov/geomag/WMM/wmm_wdownload.shtml
*
2010-09-21 21:29:35 +02:00
* Major changes include :
* - No geoid model ( altitude must be geodetic WGS - 84 )
* - Floating point calculation ( not double precision )
* - Hard coded coefficients for model
* - Elimination of user interface
* - Elimination of dynamic memory allocation
*
* @ see The GNU Public License ( GPL ) Version 3
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
// I don't want this dependency, but currently using pvPortMalloc
# include "openpilot.h"
# include <stdio.h>
# include <string.h>
# include <math.h>
# include <stdlib.h>
# include <stdint.h>
# include "WorldMagModel.h"
# include "WMMInternal.h"
2010-12-24 22:57:15 +01:00
# define MALLOC(x) pvPortMalloc(x)
# define FREE(x) vPortFree(x)
//#define MALLOC(x) malloc(x)
//#define FREE(x) free(x)
2010-12-24 22:57:12 +01:00
2013-04-18 21:03:25 +02:00
// http://reviews.openpilot.org/cru/OPReview-436#c6476 :
// first column not used but it will be optimized out by compiler
2013-04-30 13:06:42 +02:00
static const float CoeffFile [ 91 ] [ 6 ] = { { 0.0f , 0.0f , 0.0f , 0.0f , 0.0f , 0.0f } ,
{ 1.0f , 0.0f , - 29496.6f , 0.0f , 11.6f , 0.0f } ,
{ 1.0f , 1.0f , - 1586.3f , 4944.4f , 16.5f , - 25.9f } ,
{ 2.0f , 0.0f , - 2396.6f , 0.0f , - 12.1f , 0.0f } ,
{ 2.0f , 1.0f , 3026.1f , - 2707.7f , - 4.4f , - 22.5f } ,
{ 2.0f , 2.0f , 1668.6f , - 576.1f , 1.9f , - 11.8f } ,
{ 3.0f , 0.0f , 1340.1f , 0.0f , 0.4f , 0.0f } ,
{ 3.0f , 1.0f , - 2326.2f , - 160.2f , - 4.1f , 7.3f } ,
{ 3.0f , 2.0f , 1231.9f , 251.9f , - 2.9f , - 3.9f } ,
{ 3.0f , 3.0f , 634.0f , - 536.6f , - 7.7f , - 2.6f } ,
{ 4.0f , 0.0f , 912.6f , 0.0f , - 1.8f , 0.0f } ,
{ 4.0f , 1.0f , 808.9f , 286.4f , 2.3f , 1.1f } ,
{ 4.0f , 2.0f , 166.7f , - 211.2f , - 8.7f , 2.7f } ,
{ 4.0f , 3.0f , - 357.1f , 164.3f , 4.6f , 3.9f } ,
{ 4.0f , 4.0f , 89.4f , - 309.1f , - 2.1f , - 0.8f } ,
{ 5.0f , 0.0f , - 230.9f , 0.0f , - 1.0f , 0.0f } ,
{ 5.0f , 1.0f , 357.2f , 44.6f , 0.6f , 0.4f } ,
{ 5.0f , 2.0f , 200.3f , 188.9f , - 1.8f , 1.8f } ,
{ 5.0f , 3.0f , - 141.1f , - 118.2f , - 1.0f , 1.2f } ,
{ 5.0f , 4.0f , - 163.0f , 0.0f , 0.9f , 4.0f } ,
{ 5.0f , 5.0f , - 7.8f , 100.9f , 1.0f , - 0.6f } ,
{ 6.0f , 0.0f , 72.8f , 0.0f , - 0.2f , 0.0f } ,
{ 6.0f , 1.0f , 68.6f , - 20.8f , - 0.2f , - 0.2f } ,
{ 6.0f , 2.0f , 76.0f , 44.1f , - 0.1f , - 2.1f } ,
{ 6.0f , 3.0f , - 141.4f , 61.5f , 2.0f , - 0.4f } ,
{ 6.0f , 4.0f , - 22.8f , - 66.3f , - 1.7f , - 0.6f } ,
{ 6.0f , 5.0f , 13.2f , 3.1f , - 0.3f , 0.5f } ,
{ 6.0f , 6.0f , - 77.9f , 55.0f , 1.7f , 0.9f } ,
{ 7.0f , 0.0f , 80.5f , 0.0f , 0.1f , 0.0f } ,
{ 7.0f , 1.0f , - 75.1f , - 57.9f , - 0.1f , 0.7f } ,
{ 7.0f , 2.0f , - 4.7f , - 21.1f , - 0.6f , 0.3f } ,
{ 7.0f , 3.0f , 45.3f , 6.5f , 1.3f , - 0.1f } ,
{ 7.0f , 4.0f , 13.9f , 24.9f , 0.4f , - 0.1f } ,
{ 7.0f , 5.0f , 10.4f , 7.0f , 0.3f , - 0.8f } ,
{ 7.0f , 6.0f , 1.7f , - 27.7f , - 0.7f , - 0.3f } ,
{ 7.0f , 7.0f , 4.9f , - 3.3f , 0.6f , 0.3f } ,
{ 8.0f , 0.0f , 24.4f , 0.0f , - 0.1f , 0.0f } ,
{ 8.0f , 1.0f , 8.1f , 11.0f , 0.1f , - 0.1f } ,
{ 8.0f , 2.0f , - 14.5f , - 20.0f , - 0.6f , 0.2f } ,
{ 8.0f , 3.0f , - 5.6f , 11.9f , 0.2f , 0.4f } ,
{ 8.0f , 4.0f , - 19.3f , - 17.4f , - 0.2f , 0.4f } ,
{ 8.0f , 5.0f , 11.5f , 16.7f , 0.3f , 0.1f } ,
{ 8.0f , 6.0f , 10.9f , 7.0f , 0.3f , - 0.1f } ,
{ 8.0f , 7.0f , - 14.1f , - 10.8f , - 0.6f , 0.4f } ,
{ 8.0f , 8.0f , - 3.7f , 1.7f , 0.2f , 0.3f } ,
{ 9.0f , 0.0f , 5.4f , 0.0f , 0.0f , 0.0f } ,
{ 9.0f , 1.0f , 9.4f , - 20.5f , - 0.1f , 0.0f } ,
{ 9.0f , 2.0f , 3.4f , 11.5f , 0.0f , - 0.2f } ,
{ 9.0f , 3.0f , - 5.2f , 12.8f , 0.3f , 0.0f } ,
{ 9.0f , 4.0f , 3.1f , - 7.2f , - 0.4f , - 0.1f } ,
{ 9.0f , 5.0f , - 12.4f , - 7.4f , - 0.3f , 0.1f } ,
{ 9.0f , 6.0f , - 0.7f , 8.0f , 0.1f , 0.0f } ,
{ 9.0f , 7.0f , 8.4f , 2.1f , - 0.1f , - 0.2f } ,
{ 9.0f , 8.0f , - 8.5f , - 6.1f , - 0.4f , 0.3f } ,
{ 9.0f , 9.0f , - 10.1f , 7.0f , - 0.2f , 0.2f } ,
{ 10.0f , 0.0f , - 2.0f , 0.0f , 0.0f , 0.0f } ,
{ 10.0f , 1.0f , - 6.3f , 2.8f , 0.0f , 0.1f } ,
{ 10.0f , 2.0f , 0.9f , - 0.1f , - 0.1f , - 0.1f } ,
{ 10.0f , 3.0f , - 1.1f , 4.7f , 0.2f , 0.0f } ,
{ 10.0f , 4.0f , - 0.2f , 4.4f , 0.0f , - 0.1f } ,
{ 10.0f , 5.0f , 2.5f , - 7.2f , - 0.1f , - 0.1f } ,
{ 10.0f , 6.0f , - 0.3f , - 1.0f , - 0.2f , 0.0f } ,
{ 10.0f , 7.0f , 2.2f , - 3.9f , 0.0f , - 0.1f } ,
{ 10.0f , 8.0f , 3.1f , - 2.0f , - 0.1f , - 0.2f } ,
{ 10.0f , 9.0f , - 1.0f , - 2.0f , - 0.2f , 0.0f } ,
{ 10.0f , 10.0f , - 2.8f , - 8.3f , - 0.2f , - 0.1f } ,
{ 11.0f , 0.0f , 3.0f , 0.0f , 0.0f , 0.0f } ,
{ 11.0f , 1.0f , - 1.5f , 0.2f , 0.0f , 0.0f } ,
{ 11.0f , 2.0f , - 2.1f , 1.7f , 0.0f , 0.1f } ,
{ 11.0f , 3.0f , 1.7f , - 0.6f , 0.1f , 0.0f } ,
{ 11.0f , 4.0f , - 0.5f , - 1.8f , 0.0f , 0.1f } ,
{ 11.0f , 5.0f , 0.5f , 0.9f , 0.0f , 0.0f } ,
{ 11.0f , 6.0f , - 0.8f , - 0.4f , 0.0f , 0.1f } ,
{ 11.0f , 7.0f , 0.4f , - 2.5f , 0.0f , 0.0f } ,
{ 11.0f , 8.0f , 1.8f , - 1.3f , 0.0f , - 0.1f } ,
{ 11.0f , 9.0f , 0.1f , - 2.1f , 0.0f , - 0.1f } ,
{ 11.0f , 10.0f , 0.7f , - 1.9f , - 0.1f , 0.0f } ,
{ 11.0f , 11.0f , 3.8f , - 1.8f , 0.0f , - 0.1f } ,
{ 12.0f , 0.0f , - 2.2f , 0.0f , 0.0f , 0.0f } ,
{ 12.0f , 1.0f , - 0.2f , - 0.9f , 0.0f , 0.0f } ,
{ 12.0f , 2.0f , 0.3f , 0.3f , 0.1f , 0.0f } ,
{ 12.0f , 3.0f , 1.0f , 2.1f , 0.1f , 0.0f } ,
{ 12.0f , 4.0f , - 0.6f , - 2.5f , - 0.1f , 0.0f } ,
{ 12.0f , 5.0f , 0.9f , 0.5f , 0.0f , 0.0f } ,
{ 12.0f , 6.0f , - 0.1f , 0.6f , 0.0f , 0.1f } ,
{ 12.0f , 7.0f , 0.5f , 0.0f , 0.0f , 0.0f } ,
{ 12.0f , 8.0f , - 0.4f , 0.1f , 0.0f , 0.0f } ,
{ 12.0f , 9.0f , - 0.4f , 0.3f , 0.0f , 0.0f } ,
{ 12.0f , 10.0f , 0.2f , - 0.9f , 0.0f , 0.0f } ,
{ 12.0f , 11.0f , - 0.8f , - 0.2f , - 0.1f , 0.0f } ,
{ 12.0f , 12.0f , 0.0f , 0.9f , 0.1f , 0.0f }
2010-12-24 22:57:12 +01:00
} ;
2011-01-11 12:11:23 +01:00
static WMMtype_Ellipsoid * Ellip = NULL ;
static WMMtype_MagneticModel * MagneticModel = NULL ;
static float decimal_date ;
2010-09-21 21:29:35 +02:00
/**************************************************************************************
* Example use - very simple - only two exposed functions
*
* WMM_Initialize ( ) ; // Set default values and constants
*
* WMM_GetMagVector ( float Lat , float Lon , float Alt , uint16_t Month , uint16_t Day , uint16_t Year , float B [ 3 ] ) ;
* e . g . Iceland in may of 2012 = WMM_GetMagVector ( 65.0 , - 20.0 , 0.0 , 5 , 5 , 2012 , B ) ;
* Alt is above the WGS - 84 Ellipsoid
* B is the NED ( XYZ ) magnetic vector in nTesla
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int WMM_Initialize ( )
// Sets default values for WMM subroutines.
// UPDATES : Ellip and MagneticModel
2010-12-24 22:57:12 +01:00
{
2011-01-11 12:11:23 +01:00
if ( ! Ellip ) return - 1 ; // invalid pointer
if ( ! MagneticModel ) return - 2 ; // invalid pointer
2010-12-24 22:57:12 +01:00
2010-09-21 21:29:35 +02:00
// Sets WGS-84 parameters
2013-04-30 13:06:42 +02:00
Ellip - > a = 6378.137f ; // semi-major axis of the ellipsoid in km
Ellip - > b = 6356.7523142f ; // semi-minor axis of the ellipsoid in km
Ellip - > fla = 1.0f / 298.257223563f ; // flattening
2010-09-21 21:29:35 +02:00
Ellip - > eps = sqrt ( 1 - ( Ellip - > b * Ellip - > b ) / ( Ellip - > a * Ellip - > a ) ) ; // first eccentricity
Ellip - > epssq = ( Ellip - > eps * Ellip - > eps ) ; // first eccentricity squared
2013-04-30 13:06:42 +02:00
Ellip - > re = 6371.2f ; // Earth's radius in km
2010-09-21 21:29:35 +02:00
// Sets Magnetic Model parameters
MagneticModel - > nMax = WMM_MAX_MODEL_DEGREES ;
2010-10-02 04:17:22 +02:00
MagneticModel - > nMaxSecVar = WMM_MAX_SECULAR_VARIATION_MODEL_DEGREES ;
2010-09-21 21:29:35 +02:00
MagneticModel - > SecularVariationUsed = 0 ;
// Really, Really needs to be read from a file - out of date in 2015 at latest
2013-04-30 13:06:42 +02:00
MagneticModel - > EditionDate = 0.0f ; /* OP change. Originally 5.7863328170559505e-307, truncates to 0.0f */
MagneticModel - > epoch = 2010.0f ;
2010-09-21 21:29:35 +02:00
sprintf ( MagneticModel - > ModelName , " WMM-2010 " ) ;
2011-01-11 12:11:23 +01:00
return 0 ; // OK
2010-09-21 21:29:35 +02:00
}
2011-01-11 12:11:23 +01:00
int WMM_GetMagVector ( float Lat , float Lon , float AltEllipsoid , uint16_t Month , uint16_t Day , uint16_t Year , float B [ 3 ] )
2013-04-30 13:06:42 +02:00
{
2011-01-11 12:11:23 +01:00
// return '0' if all appears to be OK
// return < 0 if error
int returned = 0 ; // default to OK
// ***********
// range check supplied params
2013-04-18 21:03:25 +02:00
if ( Lat < - 90.0f ) return - 1 ; // error
if ( Lat > 90.0f ) return - 2 ; // error
2011-01-11 12:11:23 +01:00
2013-04-18 21:03:25 +02:00
if ( Lon < - 180.0f ) return - 3 ; // error
if ( Lon > 180.0f ) return - 4 ; // error
2011-01-11 12:11:23 +01:00
// ***********
// allocated required memory
// Ellip = NULL;
// MagneticModel = NULL;
// MagneticModel = NULL;
// CoordGeodetic = NULL;
// GeoMagneticElements = NULL;
Ellip = ( WMMtype_Ellipsoid * ) MALLOC ( sizeof ( WMMtype_Ellipsoid ) ) ;
MagneticModel = ( WMMtype_MagneticModel * ) MALLOC ( sizeof ( WMMtype_MagneticModel ) ) ;
WMMtype_CoordSpherical * CoordSpherical = ( WMMtype_CoordSpherical * ) MALLOC ( sizeof ( WMMtype_CoordSpherical ) ) ;
WMMtype_CoordGeodetic * CoordGeodetic = ( WMMtype_CoordGeodetic * ) MALLOC ( sizeof ( WMMtype_CoordGeodetic ) ) ;
WMMtype_GeoMagneticElements * GeoMagneticElements = ( WMMtype_GeoMagneticElements * ) MALLOC ( sizeof ( WMMtype_GeoMagneticElements ) ) ;
if ( ! Ellip | | ! MagneticModel | | ! CoordSpherical | | ! CoordGeodetic | | ! GeoMagneticElements )
returned = - 5 ; // error
// ***********
if ( returned > = 0 )
{
if ( WMM_Initialize ( ) < 0 )
returned = - 6 ; // error
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
if ( returned > = 0 )
{
CoordGeodetic - > lambda = Lon ;
CoordGeodetic - > phi = Lat ;
2013-04-06 16:16:23 +02:00
CoordGeodetic - > HeightAboveEllipsoid = AltEllipsoid / 1000.0f ; // convert to km
2010-09-21 21:29:35 +02:00
2013-04-30 13:06:42 +02:00
// Convert from geodetic to Spherical Equations: 17-18, WMM Technical report
2011-01-11 12:11:23 +01:00
if ( WMM_GeodeticToSpherical ( CoordGeodetic , CoordSpherical ) < 0 )
returned = - 7 ; // error
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
if ( returned > = 0 )
{
if ( WMM_DateToYear ( Month , Day , Year ) < 0 )
returned = - 8 ; // error
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
if ( returned > = 0 )
{
// Compute the geoMagnetic field elements and their time change
if ( WMM_Geomag ( CoordSpherical , CoordGeodetic , GeoMagneticElements ) < 0 )
returned = - 9 ; // error
else
{ // set the returned values
B [ 0 ] = GeoMagneticElements - > X ;
B [ 1 ] = GeoMagneticElements - > Y ;
B [ 2 ] = GeoMagneticElements - > Z ;
}
}
// ***********
// free allocated memory
if ( GeoMagneticElements )
FREE ( GeoMagneticElements ) ;
if ( CoordGeodetic )
FREE ( CoordGeodetic ) ;
if ( CoordSpherical )
FREE ( CoordSpherical ) ;
if ( MagneticModel )
{
FREE ( MagneticModel ) ;
MagneticModel = NULL ;
}
if ( Ellip )
{
FREE ( Ellip ) ;
Ellip = NULL ;
}
2013-04-06 16:16:23 +02:00
B [ 0 ] = GeoMagneticElements - > X * 1e-2 f ;
B [ 1 ] = GeoMagneticElements - > Y * 1e-2 f ;
B [ 2 ] = GeoMagneticElements - > Z * 1e-2 f ;
2011-01-11 12:11:23 +01:00
return returned ;
2010-09-21 21:29:35 +02:00
}
2011-01-11 12:11:23 +01:00
int WMM_Geomag ( WMMtype_CoordSpherical * CoordSpherical , WMMtype_CoordGeodetic * CoordGeodetic , WMMtype_GeoMagneticElements * GeoMagneticElements )
2010-09-21 21:29:35 +02:00
/*
The main subroutine that calls a sequence of WMM sub - functions to calculate the magnetic field elements for a single point .
The function expects the model coefficients and point coordinates as input and returns the magnetic field elements and
their rate of change . Though , this subroutine can be called successively to calculate a time series , profile or grid
of magnetic field , these are better achieved by the subroutine WMM_Grid .
INPUT : Ellip
CoordSpherical
CoordGeodetic
TimedMagneticModel
OUTPUT : GeoMagneticElements
CALLS : WMM_ComputeSphericalHarmonicVariables ( Ellip , CoordSpherical , TimedMagneticModel - > nMax , & SphVariables ) ; ( Compute Spherical Harmonic variables )
WMM_AssociatedLegendreFunction ( CoordSpherical , TimedMagneticModel - > nMax , LegendreFunction ) ; Compute ALF
WMM_Summation ( LegendreFunction , TimedMagneticModel , SphVariables , CoordSpherical , & MagneticResultsSph ) ; Accumulate the spherical harmonic coefficients
WMM_SecVarSummation ( LegendreFunction , TimedMagneticModel , SphVariables , CoordSpherical , & MagneticResultsSphVar ) ; Sum the Secular Variation Coefficients
WMM_RotateMagneticVector ( CoordSpherical , CoordGeodetic , MagneticResultsSph , & MagneticResultsGeo ) ; Map the computed Magnetic fields to Geodeitic coordinates
WMM_RotateMagneticVector ( CoordSpherical , CoordGeodetic , MagneticResultsSphVar , & MagneticResultsGeoVar ) ; Map the secular variation field components to Geodetic coordinates
WMM_CalculateGeoMagneticElements ( & MagneticResultsGeo , GeoMagneticElements ) ; Calculate the Geomagnetic elements
WMM_CalculateSecularVariation ( MagneticResultsGeoVar , GeoMagneticElements ) ; Calculate the secular variation of each of the Geomagnetic elements
*/
{
2011-01-11 12:11:23 +01:00
int returned = 0 ; // default to OK
2011-01-10 21:30:47 +01:00
WMMtype_MagneticResults MagneticResultsSph ;
WMMtype_MagneticResults MagneticResultsGeo ;
WMMtype_MagneticResults MagneticResultsSphVar ;
WMMtype_MagneticResults MagneticResultsGeoVar ;
2011-01-11 12:11:23 +01:00
// ********
// allocate required memory
2011-01-10 21:30:47 +01:00
2011-01-11 12:11:23 +01:00
WMMtype_LegendreFunction * LegendreFunction = ( WMMtype_LegendreFunction * ) MALLOC ( sizeof ( WMMtype_LegendreFunction ) ) ;
WMMtype_SphericalHarmonicVariables * SphVariables = ( WMMtype_SphericalHarmonicVariables * ) MALLOC ( sizeof ( WMMtype_SphericalHarmonicVariables ) ) ;
2011-01-10 21:30:47 +01:00
2011-01-11 12:11:23 +01:00
if ( ! LegendreFunction | | ! SphVariables )
returned = - 1 ; // memory allocation error
// ********
if ( returned > = 0 )
{ // Compute Spherical Harmonic variables
if ( WMM_ComputeSphericalHarmonicVariables ( CoordSpherical , MagneticModel - > nMax , SphVariables ) < 0 )
returned = - 2 ; // error
}
if ( returned > = 0 )
{ // Compute ALF
if ( WMM_AssociatedLegendreFunction ( CoordSpherical , MagneticModel - > nMax , LegendreFunction ) < 0 )
returned = - 3 ; // error
}
if ( returned > = 0 )
{ // Accumulate the spherical harmonic coefficients
if ( WMM_Summation ( LegendreFunction , SphVariables , CoordSpherical , & MagneticResultsSph ) < 0 )
returned = - 4 ; // error
}
if ( returned > = 0 )
{ // Sum the Secular Variation Coefficients
if ( WMM_SecVarSummation ( LegendreFunction , SphVariables , CoordSpherical , & MagneticResultsSphVar ) < 0 )
returned = - 5 ; // error
}
if ( returned > = 0 )
{ // Map the computed Magnetic fields to Geodeitic coordinates
if ( WMM_RotateMagneticVector ( CoordSpherical , CoordGeodetic , & MagneticResultsSph , & MagneticResultsGeo ) < 0 )
returned = - 6 ; // error
}
if ( returned > = 0 )
{ // Map the secular variation field components to Geodetic coordinates
if ( WMM_RotateMagneticVector ( CoordSpherical , CoordGeodetic , & MagneticResultsSphVar , & MagneticResultsGeoVar ) < 0 )
returned = - 7 ; // error
}
if ( returned > = 0 )
{ // Calculate the Geomagnetic elements, Equation 18 , WMM Technical report
if ( WMM_CalculateGeoMagneticElements ( & MagneticResultsGeo , GeoMagneticElements ) < 0 )
returned = - 8 ; // error
}
if ( returned > = 0 )
{ // Calculate the secular variation of each of the Geomagnetic elements
if ( WMM_CalculateSecularVariation ( & MagneticResultsGeoVar , GeoMagneticElements ) < 0 )
returned = - 9 ; // error
}
// ********
// free allocated memory
if ( SphVariables )
FREE ( SphVariables ) ;
if ( LegendreFunction )
FREE ( LegendreFunction ) ;
// ********
return returned ;
2010-09-21 21:29:35 +02:00
}
2011-01-11 12:11:23 +01:00
int WMM_ComputeSphericalHarmonicVariables ( WMMtype_CoordSpherical * CoordSpherical , uint16_t nMax , WMMtype_SphericalHarmonicVariables * SphVariables )
2010-09-21 21:29:35 +02:00
/* Computes Spherical variables
Variables computed are ( a / r ) ^ ( n + 2 ) , cos_m ( lamda ) and sin_m ( lambda ) for spherical harmonic
summations . ( Equations 10 - 12 in the WMM Technical Report )
INPUT Ellip data structure with the following elements
float a ; semi - major axis of the ellipsoid
float b ; semi - minor axis of the ellipsoid
float fla ; flattening
float epssq ; first eccentricity squared
float eps ; first eccentricity
float re ; mean radius of ellipsoid
CoordSpherical A data structure with the following elements
float lambda ; ( longitude )
float phig ; ( geocentric latitude )
float r ; ( distance from the center of the ellipsoid )
nMax integer ( Maxumum degree of spherical harmonic secular model ) \
OUTPUT SphVariables Pointer to the data structure with the following elements
float RelativeRadiusPower [ WMM_MAX_MODEL_DEGREES + 1 ] ; [ earth_reference_radius_km sph . radius ] ^ n
float cos_mlambda [ WMM_MAX_MODEL_DEGREES + 1 ] ; cp ( m ) - cosine of ( mspherical coord . longitude )
float sin_mlambda [ WMM_MAX_MODEL_DEGREES + 1 ] ; sp ( m ) - sine of ( mspherical coord . longitude )
CALLS : none
*/
{
float cos_lambda , sin_lambda ;
uint16_t m , n ;
2011-01-11 12:11:23 +01:00
2013-04-06 16:16:23 +02:00
cos_lambda = cosf ( DEG2RAD ( CoordSpherical - > lambda ) ) ;
sin_lambda = sinf ( DEG2RAD ( CoordSpherical - > lambda ) ) ;
2011-01-11 12:11:23 +01:00
2010-09-21 21:29:35 +02:00
/* for n = 0 ... model_order, compute (Radius of Earth / Spherica radius r)^(n+2)
for n 1. . nMax - 1 ( this is much faster than calling pow MAX_N + 1 times ) . */
2011-01-11 12:11:23 +01:00
2010-10-02 04:17:22 +02:00
SphVariables - > RelativeRadiusPower [ 0 ] = ( Ellip - > re / CoordSpherical - > r ) * ( Ellip - > re / CoordSpherical - > r ) ;
2011-01-10 21:30:47 +01:00
for ( n = 1 ; n < = nMax ; n + + )
2010-10-02 04:17:22 +02:00
SphVariables - > RelativeRadiusPower [ n ] = SphVariables - > RelativeRadiusPower [ n - 1 ] * ( Ellip - > re / CoordSpherical - > r ) ;
2010-09-21 21:29:35 +02:00
/*
2013-04-06 16:16:23 +02:00
Compute cosf ( m * lambda ) , sinf ( m * lambda ) for m = 0 . . . nMax
cosf ( a + b ) = cosf ( a ) * cosf ( b ) - sinf ( a ) * sinf ( b )
sinf ( a + b ) = cosf ( a ) * sinf ( b ) + sinf ( a ) * cosf ( b )
2010-09-21 21:29:35 +02:00
*/
2013-04-06 16:16:23 +02:00
SphVariables - > cos_mlambda [ 0 ] = 1.0f ;
SphVariables - > sin_mlambda [ 0 ] = 0.0f ;
2010-09-21 21:29:35 +02:00
SphVariables - > cos_mlambda [ 1 ] = cos_lambda ;
SphVariables - > sin_mlambda [ 1 ] = sin_lambda ;
2011-01-10 21:30:47 +01:00
for ( m = 2 ; m < = nMax ; m + + )
{
2010-10-02 04:17:22 +02:00
SphVariables - > cos_mlambda [ m ] = SphVariables - > cos_mlambda [ m - 1 ] * cos_lambda - SphVariables - > sin_mlambda [ m - 1 ] * sin_lambda ;
SphVariables - > sin_mlambda [ m ] = SphVariables - > cos_mlambda [ m - 1 ] * sin_lambda + SphVariables - > sin_mlambda [ m - 1 ] * cos_lambda ;
2010-09-21 21:29:35 +02:00
}
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
int WMM_AssociatedLegendreFunction ( WMMtype_CoordSpherical * CoordSpherical , uint16_t nMax , WMMtype_LegendreFunction * LegendreFunction )
2010-09-21 21:29:35 +02:00
/* Computes all of the Schmidt-semi normalized associated Legendre
functions up to degree nMax . If nMax < = 16 , function WMM_PcupLow is used .
Otherwise WMM_PcupHigh is called .
INPUT CoordSpherical A data structure with the following elements
float lambda ; ( longitude )
float phig ; ( geocentric latitude )
float r ; ( distance from the center of the ellipsoid )
nMax integer ( Maxumum degree of spherical harmonic secular model )
LegendreFunction Pointer to data structure with the following elements
float * Pcup ; ( pointer to store Legendre Function )
float * dPcup ; ( pointer to store Derivative of Lagendre function )
OUTPUT LegendreFunction Calculated Legendre variables in the data structure
*/
{
2013-04-06 16:16:23 +02:00
float sin_phi = sinf ( DEG2RAD ( CoordSpherical - > phig ) ) ; /* sinf (geocentric latitude) */
2010-09-21 21:29:35 +02:00
2013-04-06 16:16:23 +02:00
if ( nMax < = 16 | | ( 1 - fabsf ( sin_phi ) ) < 1.0e-10 f ) /* If nMax is less tha 16 or at the poles */
2011-01-11 12:11:23 +01:00
{
if ( WMM_PcupLow ( LegendreFunction - > Pcup , LegendreFunction - > dPcup , sin_phi , nMax ) < 0 )
return - 1 ; // error
}
2010-09-21 21:29:35 +02:00
else
2011-01-11 12:11:23 +01:00
{
if ( WMM_PcupHigh ( LegendreFunction - > Pcup , LegendreFunction - > dPcup , sin_phi , nMax ) < 0 )
return - 2 ; // error
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_Summation ( WMMtype_LegendreFunction * LegendreFunction ,
2010-09-21 21:29:35 +02:00
WMMtype_SphericalHarmonicVariables * SphVariables ,
2010-10-02 04:17:22 +02:00
WMMtype_CoordSpherical * CoordSpherical , WMMtype_MagneticResults * MagneticResults )
2010-09-21 21:29:35 +02:00
{
/* Computes Geomagnetic Field Elements X, Y and Z in Spherical coordinate system using
spherical harmonic summation .
The vector Magnetic field is given by - grad V , where V is Geomagnetic scalar potential
The gradient in spherical coordinates is given by :
dV ^ 1 dV ^ 1 dV ^
grad V = - - r + - - - t + - - - - - - - - - - p
2013-04-06 16:16:23 +02:00
dr r dt r sinf ( t ) dp
2010-09-21 21:29:35 +02:00
INPUT : LegendreFunction
MagneticModel
SphVariables
CoordSpherical
OUTPUT : MagneticResults
CALLS : WMM_SummationSpecial
Manoj Nair , June , 2009 Manoj . C . Nair @ Noaa . Gov
*/
2011-01-11 12:11:23 +01:00
uint16_t m , n , index ;
2010-09-21 21:29:35 +02:00
float cos_phi ;
2011-01-11 12:11:23 +01:00
2013-04-30 13:06:42 +02:00
MagneticResults - > Bz = 0.0f ;
MagneticResults - > By = 0.0f ;
MagneticResults - > Bx = 0.0f ;
2011-01-11 12:11:23 +01:00
for ( n = 1 ; n < = MagneticModel - > nMax ; n + + )
{
for ( m = 0 ; m < = n ; m + + )
{
2010-09-21 21:29:35 +02:00
index = ( n * ( n + 1 ) / 2 + m ) ;
/* nMax (n+2) n m m m
2013-04-06 16:16:23 +02:00
Bz = - SUM ( a / r ) ( n + 1 ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] P ( sinf ( phi ) )
2010-09-21 21:29:35 +02:00
n = 1 m = 0 n n n */
/* Equation 12 in the WMM Technical report. Derivative with respect to radius.*/
MagneticResults - > Bz - =
SphVariables - > RelativeRadiusPower [ n ] *
2010-12-24 22:57:12 +01:00
( WMM_get_main_field_coeff_g ( index ) *
SphVariables - > cos_mlambda [ m ] + WMM_get_main_field_coeff_h ( index ) * SphVariables - > sin_mlambda [ m ] )
2010-10-02 04:17:22 +02:00
* ( float ) ( n + 1 ) * LegendreFunction - > Pcup [ index ] ;
2010-09-21 21:29:35 +02:00
/* 1 nMax (n+2) n m m m
2013-04-06 16:16:23 +02:00
By = SUM ( a / r ) ( m ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
2010-09-21 21:29:35 +02:00
n = 1 m = 0 n n n */
/* Equation 11 in the WMM Technical report. Derivative with respect to longitude, divided by radius. */
MagneticResults - > By + =
SphVariables - > RelativeRadiusPower [ n ] *
2010-12-24 22:57:12 +01:00
( WMM_get_main_field_coeff_g ( index ) *
SphVariables - > sin_mlambda [ m ] - WMM_get_main_field_coeff_h ( index ) * SphVariables - > cos_mlambda [ m ] )
2010-09-21 21:29:35 +02:00
* ( float ) ( m ) * LegendreFunction - > Pcup [ index ] ;
/* nMax (n+2) n m m m
2013-04-06 16:16:23 +02:00
Bx = - SUM ( a / r ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
2010-09-21 21:29:35 +02:00
n = 1 m = 0 n n n */
/* Equation 10 in the WMM Technical report. Derivative with respect to latitude, divided by radius. */
MagneticResults - > Bx - =
SphVariables - > RelativeRadiusPower [ n ] *
2010-12-24 22:57:12 +01:00
( WMM_get_main_field_coeff_g ( index ) *
SphVariables - > cos_mlambda [ m ] + WMM_get_main_field_coeff_h ( index ) * SphVariables - > sin_mlambda [ m ] )
2010-09-21 21:29:35 +02:00
* LegendreFunction - > dPcup [ index ] ;
}
}
2013-04-06 16:16:23 +02:00
cos_phi = cosf ( DEG2RAD ( CoordSpherical - > phig ) ) ;
if ( fabsf ( cos_phi ) > 1.0e-10 f )
2011-01-11 12:11:23 +01:00
{
2010-09-21 21:29:35 +02:00
MagneticResults - > By = MagneticResults - > By / cos_phi ;
2011-01-11 12:11:23 +01:00
}
else
2010-09-21 21:29:35 +02:00
{
2011-01-11 12:11:23 +01:00
/* Special calculation for component - By - at Geographic poles.
* If the user wants to avoid using this function , please make sure that
* the latitude is not exactly + / - 90. An option is to make use the function
* WMM_CheckGeographicPoles .
*/
if ( WMM_SummationSpecial ( SphVariables , CoordSpherical , MagneticResults ) < 0 )
return - 1 ; // error
2010-09-21 21:29:35 +02:00
}
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
int WMM_SecVarSummation ( WMMtype_LegendreFunction * LegendreFunction ,
2010-09-21 21:29:35 +02:00
WMMtype_SphericalHarmonicVariables *
2010-10-02 04:17:22 +02:00
SphVariables , WMMtype_CoordSpherical * CoordSpherical , WMMtype_MagneticResults * MagneticResults )
2010-09-21 21:29:35 +02:00
{
/*This Function sums the secular variation coefficients to get the secular variation of the Magnetic vector.
INPUT : LegendreFunction
MagneticModel
SphVariables
CoordSpherical
OUTPUT : MagneticResults
CALLS : WMM_SecVarSummationSpecial
*/
2011-01-11 12:11:23 +01:00
uint16_t m , n , index ;
2010-09-21 21:29:35 +02:00
float cos_phi ;
2011-01-11 12:11:23 +01:00
2010-09-21 21:29:35 +02:00
MagneticModel - > SecularVariationUsed = TRUE ;
2011-01-11 12:11:23 +01:00
2013-04-30 13:06:42 +02:00
MagneticResults - > Bz = 0.0f ;
MagneticResults - > By = 0.0f ;
MagneticResults - > Bx = 0.0f ;
2011-01-11 12:11:23 +01:00
2011-01-10 21:30:47 +01:00
for ( n = 1 ; n < = MagneticModel - > nMaxSecVar ; n + + )
{
for ( m = 0 ; m < = n ; m + + )
{
2010-09-21 21:29:35 +02:00
index = ( n * ( n + 1 ) / 2 + m ) ;
/* nMax (n+2) n m m m
2013-04-06 16:16:23 +02:00
Bz = - SUM ( a / r ) ( n + 1 ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] P ( sinf ( phi ) )
2010-09-21 21:29:35 +02:00
n = 1 m = 0 n n n */
/* Derivative with respect to radius.*/
MagneticResults - > Bz - =
SphVariables - > RelativeRadiusPower [ n ] *
2010-12-24 22:57:12 +01:00
( WMM_get_secular_var_coeff_g ( index ) *
SphVariables - > cos_mlambda [ m ] + WMM_get_secular_var_coeff_h ( index ) * SphVariables - > sin_mlambda [ m ] )
2010-10-02 04:17:22 +02:00
* ( float ) ( n + 1 ) * LegendreFunction - > Pcup [ index ] ;
2010-09-21 21:29:35 +02:00
/* 1 nMax (n+2) n m m m
2013-04-06 16:16:23 +02:00
By = SUM ( a / r ) ( m ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
2010-09-21 21:29:35 +02:00
n = 1 m = 0 n n n */
/* Derivative with respect to longitude, divided by radius. */
MagneticResults - > By + =
SphVariables - > RelativeRadiusPower [ n ] *
2010-12-24 22:57:12 +01:00
( WMM_get_secular_var_coeff_g ( index ) *
SphVariables - > sin_mlambda [ m ] - WMM_get_secular_var_coeff_h ( index ) * SphVariables - > cos_mlambda [ m ] )
2010-09-21 21:29:35 +02:00
* ( float ) ( m ) * LegendreFunction - > Pcup [ index ] ;
/* nMax (n+2) n m m m
2013-04-06 16:16:23 +02:00
Bx = - SUM ( a / r ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
2010-09-21 21:29:35 +02:00
n = 1 m = 0 n n n */
/* Derivative with respect to latitude, divided by radius. */
MagneticResults - > Bx - =
SphVariables - > RelativeRadiusPower [ n ] *
2010-12-24 22:57:12 +01:00
( WMM_get_secular_var_coeff_g ( index ) *
SphVariables - > cos_mlambda [ m ] + WMM_get_secular_var_coeff_h ( index ) * SphVariables - > sin_mlambda [ m ] )
2010-09-21 21:29:35 +02:00
* LegendreFunction - > dPcup [ index ] ;
}
}
2013-04-06 16:16:23 +02:00
cos_phi = cosf ( DEG2RAD ( CoordSpherical - > phig ) ) ;
if ( fabsf ( cos_phi ) > 1.0e-10 f )
2011-01-10 21:30:47 +01:00
{
2010-09-21 21:29:35 +02:00
MagneticResults - > By = MagneticResults - > By / cos_phi ;
2011-01-10 21:30:47 +01:00
}
else
2010-09-21 21:29:35 +02:00
/* Special calculation for component By at Geographic poles */
{
2011-01-11 12:11:23 +01:00
if ( WMM_SecVarSummationSpecial ( SphVariables , CoordSpherical , MagneticResults ) < 0 )
return - 1 ; // error
2010-09-21 21:29:35 +02:00
}
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
int WMM_RotateMagneticVector ( WMMtype_CoordSpherical * CoordSpherical ,
2010-09-21 21:29:35 +02:00
WMMtype_CoordGeodetic * CoordGeodetic ,
2010-10-02 04:17:22 +02:00
WMMtype_MagneticResults * MagneticResultsSph , WMMtype_MagneticResults * MagneticResultsGeo )
2010-09-21 21:29:35 +02:00
/* Rotate the Magnetic Vectors to Geodetic Coordinates
Manoj Nair , June , 2009 Manoj . C . Nair @ Noaa . Gov
Equation 16 , WMM Technical report
INPUT : CoordSpherical : Data structure WMMtype_CoordSpherical with the following elements
float lambda ; ( longitude )
float phig ; ( geocentric latitude )
float r ; ( distance from the center of the ellipsoid )
CoordGeodetic : Data structure WMMtype_CoordGeodetic with the following elements
float lambda ; ( longitude )
float phi ; ( geodetic latitude )
float HeightAboveEllipsoid ; ( height above the ellipsoid ( HaE ) )
float HeightAboveGeoid ; ( height above the Geoid )
MagneticResultsSph : Data structure WMMtype_MagneticResults with the following elements
float Bx ; North
float By ; East
float Bz ; Down
OUTPUT : MagneticResultsGeo Pointer to the data structure WMMtype_MagneticResults , with the following elements
float Bx ; North
float By ; East
float Bz ; Down
CALLS : none
*/
{
/* Difference between the spherical and Geodetic latitudes */
2013-04-28 03:15:28 +02:00
float Psi = DEG2RAD ( CoordSpherical - > phig - CoordGeodetic - > phi ) ;
2010-09-21 21:29:35 +02:00
/* Rotate spherical field components to the Geodeitic system */
2013-04-06 16:16:23 +02:00
MagneticResultsGeo - > Bz = MagneticResultsSph - > Bx * sinf ( Psi ) + MagneticResultsSph - > Bz * cosf ( Psi ) ;
MagneticResultsGeo - > Bx = MagneticResultsSph - > Bx * cosf ( Psi ) - MagneticResultsSph - > Bz * sinf ( Psi ) ;
2010-09-21 21:29:35 +02:00
MagneticResultsGeo - > By = MagneticResultsSph - > By ;
2011-01-11 12:11:23 +01:00
return 0 ;
}
int WMM_CalculateGeoMagneticElements ( WMMtype_MagneticResults * MagneticResultsGeo , WMMtype_GeoMagneticElements * GeoMagneticElements )
2010-09-21 21:29:35 +02:00
/* Calculate all the Geomagnetic elements from X,Y and Z components
INPUT MagneticResultsGeo Pointer to data structure with the following elements
float Bx ; ( North )
float By ; ( East )
float Bz ; ( Down )
OUTPUT GeoMagneticElements Pointer to data structure with the following elements
float Decl ; ( Angle between the magnetic field vector and true north , positive east )
float Incl ; Angle between the magnetic field vector and the horizontal plane , positive down
float F ; Magnetic Field Strength
float H ; Horizontal Magnetic Field Strength
float X ; Northern component of the magnetic field vector
float Y ; Eastern component of the magnetic field vector
float Z ; Downward component of the magnetic field vector
CALLS : none
*/
{
GeoMagneticElements - > X = MagneticResultsGeo - > Bx ;
GeoMagneticElements - > Y = MagneticResultsGeo - > By ;
GeoMagneticElements - > Z = MagneticResultsGeo - > Bz ;
2013-04-06 16:16:23 +02:00
GeoMagneticElements - > H = sqrtf ( MagneticResultsGeo - > Bx * MagneticResultsGeo - > Bx + MagneticResultsGeo - > By * MagneticResultsGeo - > By ) ;
GeoMagneticElements - > F = sqrtf ( GeoMagneticElements - > H * GeoMagneticElements - > H + MagneticResultsGeo - > Bz * MagneticResultsGeo - > Bz ) ;
GeoMagneticElements - > Decl = RAD2DEG ( atan2f ( GeoMagneticElements - > Y , GeoMagneticElements - > X ) ) ;
GeoMagneticElements - > Incl = RAD2DEG ( atan2f ( GeoMagneticElements - > Z , GeoMagneticElements - > H ) ) ;
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_CalculateSecularVariation ( WMMtype_MagneticResults * MagneticVariation , WMMtype_GeoMagneticElements * MagneticElements )
2010-09-21 21:29:35 +02:00
/*This takes the Magnetic Variation in x, y, and z and uses it to calculate the secular variation of each of the Geomagnetic elements.
INPUT MagneticVariation Data structure with the following elements
float Bx ; ( North )
float By ; ( East )
float Bz ; ( Down )
OUTPUT MagneticElements Pointer to the data structure with the following elements updated
float Decldot ; Yearly Rate of change in declination
float Incldot ; Yearly Rate of change in inclination
float Fdot ; Yearly rate of change in Magnetic field strength
float Hdot ; Yearly rate of change in horizontal field strength
float Xdot ; Yearly rate of change in the northern component
float Ydot ; Yearly rate of change in the eastern component
float Zdot ; Yearly rate of change in the downward component
float GVdot ; Yearly rate of chnage in grid variation
CALLS : none
*/
{
MagneticElements - > Xdot = MagneticVariation - > Bx ;
MagneticElements - > Ydot = MagneticVariation - > By ;
MagneticElements - > Zdot = MagneticVariation - > Bz ;
MagneticElements - > Hdot = ( MagneticElements - > X * MagneticElements - > Xdot + MagneticElements - > Y * MagneticElements - > Ydot ) / MagneticElements - > H ; //See equation 19 in the WMM technical report
MagneticElements - > Fdot =
( MagneticElements - > X * MagneticElements - > Xdot +
2010-10-02 04:17:22 +02:00
MagneticElements - > Y * MagneticElements - > Ydot + MagneticElements - > Z * MagneticElements - > Zdot ) / MagneticElements - > F ;
2010-09-21 21:29:35 +02:00
MagneticElements - > Decldot =
2013-04-06 16:16:23 +02:00
180.0f / M_PI_F * ( MagneticElements - > X * MagneticElements - > Ydot -
2010-10-02 04:17:22 +02:00
MagneticElements - > Y * MagneticElements - > Xdot ) / ( MagneticElements - > H * MagneticElements - > H ) ;
2010-09-21 21:29:35 +02:00
MagneticElements - > Incldot =
2013-04-06 16:16:23 +02:00
180.0f / M_PI_F * ( MagneticElements - > H * MagneticElements - > Zdot -
2010-10-02 04:17:22 +02:00
MagneticElements - > Z * MagneticElements - > Hdot ) / ( MagneticElements - > F * MagneticElements - > F ) ;
2010-09-21 21:29:35 +02:00
MagneticElements - > GVdot = MagneticElements - > Decldot ;
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
int WMM_PcupHigh ( float * Pcup , float * dPcup , float x , uint16_t nMax )
2010-09-21 21:29:35 +02:00
/* This function evaluates all of the Schmidt-semi normalized associated Legendre
functions up to degree nMax . The functions are initially scaled by
2013-04-06 16:16:23 +02:00
10 ^ 280 sinf ^ m in order to minimize the effects of underflow at large m
2010-09-21 21:29:35 +02:00
near the poles ( see Holmes and Featherstone 2002 , J . Geodesy , 76 , 279 - 299 ) .
Note that this function performs the same operation as WMM_PcupLow .
However this function also can be used for high degree ( large nMax ) models .
Calling Parameters :
INPUT
nMax : Maximum spherical harmonic degree to compute .
2013-04-06 16:16:23 +02:00
x : cosf ( colatitude ) or sinf ( latitude ) .
2010-09-21 21:29:35 +02:00
OUTPUT
Pcup : A vector of all associated Legendgre polynomials evaluated at
x up to nMax . The lenght must by greater or equal to ( nMax + 1 ) * ( nMax + 2 ) / 2.
dPcup : Derivative of Pcup ( x ) with respect to latitude
CALLS : none
Notes :
Adopted from the FORTRAN code written by Mark Wieczorek September 25 , 2005.
Manoj Nair , Nov , 2009 Manoj . C . Nair @ Noaa . Gov
Change from the previous version
The prevous version computes the derivatives as
2013-04-06 16:16:23 +02:00
dP ( n , m ) ( x ) / dx , where x = sinf ( latitude ) ( or cosf ( colatitude ) ) .
2010-09-21 21:29:35 +02:00
However , the WMM Geomagnetic routines requires dP ( n , m ) ( x ) / dlatitude .
2013-04-06 16:16:23 +02:00
Hence the derivatives are multiplied by sinf ( latitude ) .
2010-09-21 21:29:35 +02:00
Removed the options for CS phase and normalizations .
Note : In geomagnetism , the derivatives of ALF are usually found with
respect to the colatitudes . Here the derivatives are found with respect
to the latitude . The difference is a sign reversal for the derivative of
the Associated Legendre Functions .
The derivates can ' t be computed for latitude = | 90 | degrees .
*/
{
2011-01-10 21:30:47 +01:00
uint16_t k , kstart , m , n ;
2011-01-11 12:11:23 +01:00
float pm2 , pm1 , pmm , plm , rescalem , z , scalef ;
2011-01-10 21:30:47 +01:00
float * f1 = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUP ) ;
float * f2 = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUP ) ;
float * PreSqr = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUP ) ;
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
if ( ! PreSqr | | ! f2 | | ! f1 )
{ // memory allocation error
if ( PreSqr ) FREE ( PreSqr ) ;
if ( f2 ) FREE ( f2 ) ;
if ( f1 ) FREE ( f1 ) ;
return - 1 ;
}
2013-04-30 13:06:42 +02:00
/*
* Note : OP code change to avoid floating point equality test .
* Was : if ( fabs ( x ) = = 1.0 )
*/
2013-05-04 11:54:01 +02:00
if ( fabsf ( x ) - 1.0f < 1e-9 f )
2011-01-10 21:30:47 +01:00
{
2011-01-11 12:11:23 +01:00
FREE ( PreSqr ) ;
FREE ( f2 ) ;
FREE ( f1 ) ;
2010-09-21 21:29:35 +02:00
// printf("Error in PcupHigh: derivative cannot be calculated at poles\n");
2011-01-11 12:11:23 +01:00
return - 2 ;
2010-09-21 21:29:35 +02:00
}
2013-04-30 13:06:42 +02:00
/* OP Change: 1.0e-280 is too small to store in a float - the compiler truncates
* it to 0.0f , which is bad as the code below divides by scalef . */
scalef = 1.0e-20 f ;
2010-09-21 21:29:35 +02:00
2011-01-10 21:30:47 +01:00
for ( n = 0 ; n < = 2 * nMax + 1 ; + + n )
2013-04-06 16:16:23 +02:00
PreSqr [ n ] = sqrtf ( ( float ) ( n ) ) ;
2010-09-21 21:29:35 +02:00
k = 2 ;
2011-01-10 21:30:47 +01:00
for ( n = 2 ; n < = nMax ; n + + )
{
2010-09-21 21:29:35 +02:00
k = k + 1 ;
f1 [ k ] = ( float ) ( 2 * n - 1 ) / ( float ) ( n ) ;
f2 [ k ] = ( float ) ( n - 1 ) / ( float ) ( n ) ;
2011-01-10 21:30:47 +01:00
for ( m = 1 ; m < = n - 2 ; m + + )
{
2010-09-21 21:29:35 +02:00
k = k + 1 ;
2010-10-02 04:17:22 +02:00
f1 [ k ] = ( float ) ( 2 * n - 1 ) / PreSqr [ n + m ] / PreSqr [ n - m ] ;
f2 [ k ] = PreSqr [ n - m - 1 ] * PreSqr [ n + m - 1 ] / PreSqr [ n + m ] / PreSqr [ n - m ] ;
2010-09-21 21:29:35 +02:00
}
k = k + 2 ;
}
2013-04-06 16:16:23 +02:00
/*z = sinf (geocentric latitude) */
z = sqrtf ( ( 1.0f - x ) * ( 1.0f + x ) ) ;
2013-04-30 13:06:42 +02:00
pm2 = 1.0f ;
2013-04-06 16:16:23 +02:00
Pcup [ 0 ] = 1.0f ;
2013-04-30 13:06:42 +02:00
dPcup [ 0 ] = 0.0f ;
2010-09-21 21:29:35 +02:00
if ( nMax = = 0 )
2011-01-11 12:11:23 +01:00
{
FREE ( PreSqr ) ;
FREE ( f2 ) ;
FREE ( f1 ) ;
return - 3 ;
}
2010-09-21 21:29:35 +02:00
pm1 = x ;
Pcup [ 1 ] = pm1 ;
dPcup [ 1 ] = z ;
k = 1 ;
2011-01-10 21:30:47 +01:00
for ( n = 2 ; n < = nMax ; n + + )
{
2010-09-21 21:29:35 +02:00
k = k + n ;
plm = f1 [ k ] * x * pm1 - f2 [ k ] * pm2 ;
Pcup [ k ] = plm ;
dPcup [ k ] = ( float ) ( n ) * ( pm1 - x * plm ) / z ;
pm2 = pm1 ;
pm1 = plm ;
}
pmm = PreSqr [ 2 ] * scalef ;
2013-04-30 13:06:42 +02:00
rescalem = 1.0f / scalef ;
2010-09-21 21:29:35 +02:00
kstart = 0 ;
2011-01-10 21:30:47 +01:00
for ( m = 1 ; m < = nMax - 1 ; + + m )
{
2010-09-21 21:29:35 +02:00
rescalem = rescalem * z ;
/* Calculate Pcup(m,m) */
kstart = kstart + m + 1 ;
pmm = pmm * PreSqr [ 2 * m + 1 ] / PreSqr [ 2 * m ] ;
Pcup [ kstart ] = pmm * rescalem / PreSqr [ 2 * m + 1 ] ;
dPcup [ kstart ] = - ( ( float ) ( m ) * x * Pcup [ kstart ] / z ) ;
pm2 = pmm / PreSqr [ 2 * m + 1 ] ;
/* Calculate Pcup(m+1,m) */
k = kstart + m + 1 ;
pm1 = x * PreSqr [ 2 * m + 1 ] * pm2 ;
Pcup [ k ] = pm1 * rescalem ;
2010-10-02 04:17:22 +02:00
dPcup [ k ] = ( ( pm2 * rescalem ) * PreSqr [ 2 * m + 1 ] - x * ( float ) ( m + 1 ) * Pcup [ k ] ) / z ;
2010-09-21 21:29:35 +02:00
/* Calculate Pcup(n,m) */
2011-01-10 21:30:47 +01:00
for ( n = m + 2 ; n < = nMax ; + + n )
{
2010-09-21 21:29:35 +02:00
k = k + n ;
plm = x * f1 [ k ] * pm1 - f2 [ k ] * pm2 ;
Pcup [ k ] = plm * rescalem ;
2010-10-02 04:17:22 +02:00
dPcup [ k ] = ( PreSqr [ n + m ] * PreSqr [ n - m ] * ( pm1 * rescalem ) - ( float ) ( n ) * x * Pcup [ k ] ) / z ;
2010-09-21 21:29:35 +02:00
pm2 = pm1 ;
pm1 = plm ;
}
}
/* Calculate Pcup(nMax,nMax) */
rescalem = rescalem * z ;
kstart = kstart + m + 1 ;
pmm = pmm / PreSqr [ 2 * nMax ] ;
Pcup [ kstart ] = pmm * rescalem ;
dPcup [ kstart ] = - ( float ) ( nMax ) * x * Pcup [ kstart ] / z ;
2011-01-11 12:11:23 +01:00
// *********
// free allocated memory
2011-01-10 21:30:47 +01:00
FREE ( PreSqr ) ;
FREE ( f2 ) ;
FREE ( f1 ) ;
2011-01-11 12:11:23 +01:00
// *********
return 0 ; // OK
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_PcupLow ( float * Pcup , float * dPcup , float x , uint16_t nMax )
2010-09-21 21:29:35 +02:00
/* This function evaluates all of the Schmidt-semi normalized associated Legendre
functions up to degree nMax .
Calling Parameters :
INPUT
nMax : Maximum spherical harmonic degree to compute .
2013-04-06 16:16:23 +02:00
x : cosf ( colatitude ) or sinf ( latitude ) .
2010-09-21 21:29:35 +02:00
OUTPUT
Pcup : A vector of all associated Legendgre polynomials evaluated at
x up to nMax .
dPcup : Derivative of Pcup ( x ) with respect to latitude
Notes : Overflow may occur if nMax > 20 , especially for high - latitudes .
Use WMM_PcupHigh for large nMax .
Writted by Manoj Nair , June , 2009 . Manoj . C . Nair @ Noaa . Gov .
Note : In geomagnetism , the derivatives of ALF are usually found with
respect to the colatitudes . Here the derivatives are found with respect
to the latitude . The difference is a sign reversal for the derivative of
the Associated Legendre Functions .
*/
{
2011-01-10 21:30:47 +01:00
uint16_t n , m , index , index1 , index2 ;
float k , z ;
2011-01-11 12:11:23 +01:00
2011-01-10 21:30:47 +01:00
float * schmidtQuasiNorm = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUP ) ;
2011-01-11 12:11:23 +01:00
if ( ! schmidtQuasiNorm )
{ // memory allocation error
return - 1 ;
}
2011-01-10 21:30:47 +01:00
2013-04-30 13:06:42 +02:00
Pcup [ 0 ] = 1.0f ;
dPcup [ 0 ] = 0.0f ;
2011-01-10 21:30:47 +01:00
2013-04-06 16:16:23 +02:00
/*sinf (geocentric latitude) - sin_phi */
z = sqrtf ( ( 1.0f - x ) * ( 1.0f + x ) ) ;
2010-09-21 21:29:35 +02:00
/* First, Compute the Gauss-normalized associated Legendre functions */
2011-01-11 12:11:23 +01:00
for ( n = 1 ; n < = nMax ; n + + )
{
for ( m = 0 ; m < = n ; m + + )
{
2010-09-21 21:29:35 +02:00
index = ( n * ( n + 1 ) / 2 + m ) ;
2011-01-11 12:11:23 +01:00
if ( n = = m )
{
2010-09-21 21:29:35 +02:00
index1 = ( n - 1 ) * n / 2 + m - 1 ;
Pcup [ index ] = z * Pcup [ index1 ] ;
2010-10-02 04:17:22 +02:00
dPcup [ index ] = z * dPcup [ index1 ] + x * Pcup [ index1 ] ;
2011-01-11 12:11:23 +01:00
}
else
if ( n = = 1 & & m = = 0 )
{
2010-09-21 21:29:35 +02:00
index1 = ( n - 1 ) * n / 2 + m ;
Pcup [ index ] = x * Pcup [ index1 ] ;
2010-10-02 04:17:22 +02:00
dPcup [ index ] = x * dPcup [ index1 ] - z * Pcup [ index1 ] ;
2011-01-11 12:11:23 +01:00
}
else
if ( n > 1 & & n ! = m )
{
2010-09-21 21:29:35 +02:00
index1 = ( n - 2 ) * ( n - 1 ) / 2 + m ;
index2 = ( n - 1 ) * n / 2 + m ;
2011-01-11 12:11:23 +01:00
if ( m > n - 2 )
{
2010-09-21 21:29:35 +02:00
Pcup [ index ] = x * Pcup [ index2 ] ;
2010-10-02 04:17:22 +02:00
dPcup [ index ] = x * dPcup [ index2 ] - z * Pcup [ index2 ] ;
2011-01-11 12:11:23 +01:00
}
else
{
2010-10-02 04:17:22 +02:00
k = ( float ) ( ( ( n - 1 ) * ( n - 1 ) ) - ( m * m ) ) / ( float ) ( ( 2 * n - 1 )
* ( 2 * n - 3 ) ) ;
Pcup [ index ] = x * Pcup [ index2 ] - k * Pcup [ index1 ] ;
dPcup [ index ] = x * dPcup [ index2 ] - z * Pcup [ index2 ] - k * dPcup [ index1 ] ;
2010-09-21 21:29:35 +02:00
}
}
}
}
/*Compute the ration between the Gauss-normalized associated Legendre
functions and the Schmidt quasi - normalized version . This is equivalent to
sqrt ( ( m = = 0 ? 1 : 2 ) * ( n - m ) ! / ( n + m ! ) ) * ( 2 n - 1 ) ! ! / ( n - m ) ! */
2013-04-30 13:06:42 +02:00
schmidtQuasiNorm [ 0 ] = 1.0f ;
2011-01-11 12:11:23 +01:00
for ( n = 1 ; n < = nMax ; n + + )
{
2010-09-21 21:29:35 +02:00
index = ( n * ( n + 1 ) / 2 ) ;
index1 = ( n - 1 ) * n / 2 ;
/* for m = 0 */
2010-10-02 04:17:22 +02:00
schmidtQuasiNorm [ index ] = schmidtQuasiNorm [ index1 ] * ( float ) ( 2 * n - 1 ) / ( float ) n ;
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
for ( m = 1 ; m < = n ; m + + )
{
2010-09-21 21:29:35 +02:00
index = ( n * ( n + 1 ) / 2 + m ) ;
index1 = ( n * ( n + 1 ) / 2 + m - 1 ) ;
2013-04-06 16:16:23 +02:00
schmidtQuasiNorm [ index ] = schmidtQuasiNorm [ index1 ] * sqrtf ( ( float ) ( ( n - m + 1 ) * ( m = = 1 ? 2 : 1 ) ) / ( float ) ( n + m ) ) ;
2010-09-21 21:29:35 +02:00
}
}
/* Converts the Gauss-normalized associated Legendre
functions to the Schmidt quasi - normalized version using pre - computed
relation stored in the variable schmidtQuasiNorm */
2011-01-11 12:11:23 +01:00
for ( n = 1 ; n < = nMax ; n + + )
{
for ( m = 0 ; m < = n ; m + + )
{
2010-09-21 21:29:35 +02:00
index = ( n * ( n + 1 ) / 2 + m ) ;
2010-10-02 04:17:22 +02:00
Pcup [ index ] = Pcup [ index ] * schmidtQuasiNorm [ index ] ;
dPcup [ index ] = - dPcup [ index ] * schmidtQuasiNorm [ index ] ;
2010-09-21 21:29:35 +02:00
/* The sign is changed since the new WMM routines use derivative with respect to latitude
insted of co - latitude */
}
}
2011-01-10 21:30:47 +01:00
FREE ( schmidtQuasiNorm ) ;
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_SummationSpecial ( WMMtype_SphericalHarmonicVariables *
2010-10-02 04:17:22 +02:00
SphVariables , WMMtype_CoordSpherical * CoordSpherical , WMMtype_MagneticResults * MagneticResults )
2010-09-21 21:29:35 +02:00
/* Special calculation for the component By at Geographic poles.
Manoj Nair , June , 2009 manoj . c . nair @ noaa . gov
INPUT : MagneticModel
SphVariables
CoordSpherical
OUTPUT : MagneticResults
CALLS : none
See Section 1.4 , " SINGULARITIES AT THE GEOGRAPHIC POLES " , WMM Technical report
*/
{
2011-01-10 21:30:47 +01:00
uint16_t n , index ;
float k , sin_phi ;
float schmidtQuasiNorm1 ;
float schmidtQuasiNorm2 ;
float schmidtQuasiNorm3 ;
2011-01-11 12:11:23 +01:00
2011-01-10 21:30:47 +01:00
float * PcupS = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUPS ) ;
2011-01-11 12:11:23 +01:00
if ( ! PcupS )
return - 1 ; // memory allocation error
2010-09-21 21:29:35 +02:00
PcupS [ 0 ] = 1 ;
2013-04-30 13:06:42 +02:00
schmidtQuasiNorm1 = 1.0f ;
2010-09-21 21:29:35 +02:00
2013-04-30 13:06:42 +02:00
MagneticResults - > By = 0.0f ;
2013-04-06 16:16:23 +02:00
sin_phi = sinf ( DEG2RAD ( CoordSpherical - > phig ) ) ;
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
for ( n = 1 ; n < = MagneticModel - > nMax ; n + + )
{
2010-09-21 21:29:35 +02:00
/*Compute the ration between the Gauss-normalized associated Legendre
functions and the Schmidt quasi - normalized version . This is equivalent to
sqrt ( ( m = = 0 ? 1 : 2 ) * ( n - m ) ! / ( n + m ! ) ) * ( 2 n - 1 ) ! ! / ( n - m ) ! */
index = ( n * ( n + 1 ) / 2 + 1 ) ;
2010-10-02 04:17:22 +02:00
schmidtQuasiNorm2 = schmidtQuasiNorm1 * ( float ) ( 2 * n - 1 ) / ( float ) n ;
2013-04-06 16:16:23 +02:00
schmidtQuasiNorm3 = schmidtQuasiNorm2 * sqrtf ( ( float ) ( n * 2 ) / ( float ) ( n + 1 ) ) ;
2010-09-21 21:29:35 +02:00
schmidtQuasiNorm1 = schmidtQuasiNorm2 ;
2011-01-11 12:11:23 +01:00
if ( n = = 1 )
{
2010-09-21 21:29:35 +02:00
PcupS [ n ] = PcupS [ n - 1 ] ;
2011-01-11 12:11:23 +01:00
}
else
{
2010-10-02 04:17:22 +02:00
k = ( float ) ( ( ( n - 1 ) * ( n - 1 ) ) - 1 ) / ( float ) ( ( 2 * n - 1 ) * ( 2 * n - 3 ) ) ;
PcupS [ n ] = sin_phi * PcupS [ n - 1 ] - k * PcupS [ n - 2 ] ;
2010-09-21 21:29:35 +02:00
}
/* 1 nMax (n+2) n m m m
2013-04-06 16:16:23 +02:00
By = SUM ( a / r ) ( m ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
2010-09-21 21:29:35 +02:00
n = 1 m = 0 n n n */
/* Equation 11 in the WMM Technical report. Derivative with respect to longitude, divided by radius. */
MagneticResults - > By + =
SphVariables - > RelativeRadiusPower [ n ] *
2010-12-24 22:57:12 +01:00
( WMM_get_main_field_coeff_g ( index ) *
SphVariables - > sin_mlambda [ 1 ] - WMM_get_main_field_coeff_h ( index ) * SphVariables - > cos_mlambda [ 1 ] )
2010-09-21 21:29:35 +02:00
* PcupS [ n ] * schmidtQuasiNorm3 ;
}
2011-01-10 21:30:47 +01:00
FREE ( PcupS ) ;
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_SecVarSummationSpecial ( WMMtype_SphericalHarmonicVariables *
2010-10-02 04:17:22 +02:00
SphVariables , WMMtype_CoordSpherical * CoordSpherical , WMMtype_MagneticResults * MagneticResults )
2010-09-21 21:29:35 +02:00
{
/*Special calculation for the secular variation summation at the poles.
INPUT : MagneticModel
SphVariables
CoordSpherical
OUTPUT : MagneticResults
CALLS : none
*/
2011-01-10 21:30:47 +01:00
uint16_t n , index ;
float k , sin_phi ;
float schmidtQuasiNorm1 ;
float schmidtQuasiNorm2 ;
float schmidtQuasiNorm3 ;
2011-01-11 12:11:23 +01:00
2011-01-10 21:30:47 +01:00
float * PcupS = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUPS ) ;
2011-01-11 12:11:23 +01:00
if ( ! PcupS )
return - 1 ; // memory allocation error
2010-09-21 21:29:35 +02:00
PcupS [ 0 ] = 1 ;
2013-04-30 13:06:42 +02:00
schmidtQuasiNorm1 = 1.0f ;
2010-09-21 21:29:35 +02:00
2013-04-30 13:06:42 +02:00
MagneticResults - > By = 0.0f ;
2013-04-06 16:16:23 +02:00
sin_phi = sinf ( DEG2RAD ( CoordSpherical - > phig ) ) ;
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
for ( n = 1 ; n < = MagneticModel - > nMaxSecVar ; n + + )
{
2010-09-21 21:29:35 +02:00
index = ( n * ( n + 1 ) / 2 + 1 ) ;
2010-10-02 04:17:22 +02:00
schmidtQuasiNorm2 = schmidtQuasiNorm1 * ( float ) ( 2 * n - 1 ) / ( float ) n ;
2013-04-06 16:16:23 +02:00
schmidtQuasiNorm3 = schmidtQuasiNorm2 * sqrtf ( ( float ) ( n * 2 ) / ( float ) ( n + 1 ) ) ;
2010-09-21 21:29:35 +02:00
schmidtQuasiNorm1 = schmidtQuasiNorm2 ;
2011-01-11 12:11:23 +01:00
if ( n = = 1 )
{
2010-09-21 21:29:35 +02:00
PcupS [ n ] = PcupS [ n - 1 ] ;
2011-01-11 12:11:23 +01:00
}
else
{
2010-10-02 04:17:22 +02:00
k = ( float ) ( ( ( n - 1 ) * ( n - 1 ) ) - 1 ) / ( float ) ( ( 2 * n - 1 ) * ( 2 * n - 3 ) ) ;
PcupS [ n ] = sin_phi * PcupS [ n - 1 ] - k * PcupS [ n - 2 ] ;
2010-09-21 21:29:35 +02:00
}
/* 1 nMax (n+2) n m m m
2013-04-06 16:16:23 +02:00
By = SUM ( a / r ) ( m ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
2010-09-21 21:29:35 +02:00
n = 1 m = 0 n n n */
/* Derivative with respect to longitude, divided by radius. */
MagneticResults - > By + =
SphVariables - > RelativeRadiusPower [ n ] *
2010-12-24 22:57:12 +01:00
( WMM_get_secular_var_coeff_g ( index ) *
SphVariables - > sin_mlambda [ 1 ] - WMM_get_secular_var_coeff_h ( index ) * SphVariables - > cos_mlambda [ 1 ] )
2010-09-21 21:29:35 +02:00
* PcupS [ n ] * schmidtQuasiNorm3 ;
}
2011-01-10 21:30:47 +01:00
FREE ( PcupS ) ;
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
2010-09-21 21:29:35 +02:00
2010-12-24 22:57:12 +01:00
/**
* @ brief Comput the MainFieldCoeffH accounting for the date
*/
float WMM_get_main_field_coeff_g ( uint16_t index )
{
2011-01-10 21:30:47 +01:00
if ( index > = NUMTERMS )
2010-12-24 22:57:12 +01:00
return 0 ;
2010-09-21 21:29:35 +02:00
2011-01-10 21:30:47 +01:00
uint16_t n , m , sum_index , a , b ;
float coeff = CoeffFile [ index ] [ 2 ] ;
2010-12-24 22:57:12 +01:00
2010-09-21 21:29:35 +02:00
a = MagneticModel - > nMaxSecVar ;
b = ( a * ( a + 1 ) / 2 + a ) ;
2011-01-10 21:30:47 +01:00
for ( n = 1 ; n < = MagneticModel - > nMax ; n + + )
{
for ( m = 0 ; m < = n ; m + + )
{
2010-12-24 22:57:12 +01:00
sum_index = ( n * ( n + 1 ) / 2 + m ) ;
/* Hacky for now, will solve for which conditions need summing analytically */
2011-01-10 21:30:47 +01:00
if ( sum_index ! = index )
2010-12-24 22:57:12 +01:00
continue ;
if ( index < = b )
coeff + = ( decimal_date - MagneticModel - > epoch ) * WMM_get_secular_var_coeff_g ( sum_index ) ;
2010-09-21 21:29:35 +02:00
}
}
2010-12-24 22:57:12 +01:00
return coeff ;
}
float WMM_get_main_field_coeff_h ( uint16_t index )
{
2011-01-10 21:30:47 +01:00
if ( index > = NUMTERMS )
2010-12-24 22:57:12 +01:00
return 0 ;
2011-01-10 21:30:47 +01:00
uint16_t n , m , sum_index , a , b ;
2010-12-24 22:57:12 +01:00
float coeff = CoeffFile [ index ] [ 3 ] ;
a = MagneticModel - > nMaxSecVar ;
b = ( a * ( a + 1 ) / 2 + a ) ;
2011-01-10 21:30:47 +01:00
for ( n = 1 ; n < = MagneticModel - > nMax ; n + + )
{
for ( m = 0 ; m < = n ; m + + )
{
2010-12-24 22:57:12 +01:00
sum_index = ( n * ( n + 1 ) / 2 + m ) ;
/* Hacky for now, will solve for which conditions need summing analytically */
2011-01-10 21:30:47 +01:00
if ( sum_index ! = index )
2010-12-24 22:57:12 +01:00
continue ;
if ( index < = b )
coeff + = ( decimal_date - MagneticModel - > epoch ) * WMM_get_secular_var_coeff_h ( sum_index ) ;
}
}
return coeff ;
}
float WMM_get_secular_var_coeff_g ( uint16_t index )
{
2011-01-10 21:30:47 +01:00
if ( index > = NUMTERMS )
2010-12-24 22:57:12 +01:00
return 0 ;
return CoeffFile [ index ] [ 4 ] ;
}
float WMM_get_secular_var_coeff_h ( uint16_t index )
{
2011-01-10 21:30:47 +01:00
if ( index > = NUMTERMS )
2010-12-24 22:57:12 +01:00
return 0 ;
return CoeffFile [ index ] [ 5 ] ;
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_DateToYear ( uint16_t month , uint16_t day , uint16_t year )
2010-09-21 21:29:35 +02:00
// Converts a given calendar date into a decimal year
{
uint16_t temp = 0 ; // Total number of days
2010-10-02 04:17:22 +02:00
uint16_t MonthDays [ 13 ] = { 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 } ;
2010-09-21 21:29:35 +02:00
uint16_t ExtraDay = 0 ;
uint16_t i ;
2011-01-10 21:30:47 +01:00
if ( ( year % 4 = = 0 & & year % 100 ! = 0 ) | | ( year % 400 = = 0 ) )
2010-09-21 21:29:35 +02:00
ExtraDay = 1 ;
MonthDays [ 2 ] + = ExtraDay ;
/******************Validation********************************/
2011-01-11 12:11:23 +01:00
2011-01-10 21:30:47 +01:00
if ( month < = 0 | | month > 12 )
2011-01-11 12:11:23 +01:00
return - 1 ; // error
2011-01-10 21:30:47 +01:00
if ( day < = 0 | | day > MonthDays [ month ] )
2011-01-11 12:11:23 +01:00
return - 2 ; // error
2011-01-10 21:30:47 +01:00
2010-09-21 21:29:35 +02:00
/****************Calculation of t***************************/
2010-12-24 22:57:15 +01:00
for ( i = 1 ; i < = month ; i + + )
2010-09-21 21:29:35 +02:00
temp + = MonthDays [ i - 1 ] ;
2010-12-24 22:57:15 +01:00
temp + = day ;
2013-04-06 16:16:23 +02:00
decimal_date = year + ( temp - 1 ) / ( 365.0f + ExtraDay ) ;
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_GeodeticToSpherical ( WMMtype_CoordGeodetic * CoordGeodetic , WMMtype_CoordSpherical * CoordSpherical )
2010-09-21 21:29:35 +02:00
// Converts Geodetic coordinates to Spherical coordinates
// Convert geodetic coordinates, (defined by the WGS-84
// reference ellipsoid), to Earth Centered Earth Fixed Cartesian
// coordinates, and then to spherical coordinates.
{
float CosLat , SinLat , rc , xp , zp ; // all local variables
2013-04-06 16:16:23 +02:00
CosLat = cosf ( DEG2RAD ( CoordGeodetic - > phi ) ) ;
SinLat = sinf ( DEG2RAD ( CoordGeodetic - > phi ) ) ;
2010-09-21 21:29:35 +02:00
// compute the local radius of curvature on the WGS-84 reference ellipsoid
2013-04-06 16:16:23 +02:00
rc = Ellip - > a / sqrtf ( 1.0f - Ellip - > epssq * SinLat * SinLat ) ;
2010-09-21 21:29:35 +02:00
// compute ECEF Cartesian coordinates of specified point (for longitude=0)
xp = ( rc + CoordGeodetic - > HeightAboveEllipsoid ) * CosLat ;
2013-04-06 16:16:23 +02:00
zp = ( rc * ( 1.0f - Ellip - > epssq ) + CoordGeodetic - > HeightAboveEllipsoid ) * SinLat ;
2010-09-21 21:29:35 +02:00
// compute spherical radius and angle lambda and phi of specified point
2013-04-06 16:16:23 +02:00
CoordSpherical - > r = sqrtf ( xp * xp + zp * zp ) ;
CoordSpherical - > phig = RAD2DEG ( asinf ( zp / CoordSpherical - > r ) ) ; // geocentric latitude
2010-09-21 21:29:35 +02:00
CoordSpherical - > lambda = CoordGeodetic - > lambda ; // longitude
2011-01-11 12:11:23 +01:00
return 0 ; // OK
}