2010-09-21 21:29:35 +02:00
/**
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* @ file WorldMagModel . c
2015-12-13 12:59:25 +01:00
* @ author The LibrePilot Project , http : //www.librepilot.org Copyright (C) 2015.
* The OpenPilot Team , http : //www.openpilot.org Copyright (C) 2010.
2010-09-21 21:29:35 +02:00
* @ 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
*
2015-12-13 12:59:25 +01:00
* The hard coded coefficients should be valid until 2020.
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
*/
# include "openpilot.h"
# include <stdio.h>
# include <string.h>
# include <math.h>
# include <stdlib.h>
# include <stdint.h>
# include "WorldMagModel.h"
# include "WMMInternal.h"
2014-06-11 20:11:48 +02:00
# define MALLOC(x) pios_malloc(x)
2013-05-19 16:37:30 +02:00
# 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-05-19 16:37:30 +02:00
static const float CoeffFile [ 91 ] [ 6 ] = {
{ 0.0f , 0.0f , 0.0f , 0.0f , 0.0f , 0.0f } ,
2015-12-13 12:59:25 +01:00
{ 1.0f , 0.0f , - 29438.5f , 0.0f , 10.7f , 0.0f } ,
{ 1.0f , 1.0f , - 1501.1f , 4796.2f , 17.9f , - 26.8f } ,
{ 2.0f , 0.0f , - 2445.3f , 0.0f , - 8.6f , 0.0f } ,
{ 2.0f , 1.0f , 3012.5f , - 2845.6f , - 3.3f , - 27.1f } ,
{ 2.0f , 2.0f , 1676.6f , - 642.0f , 2.4f , - 13.3f } ,
{ 3.0f , 0.0f , 1351.1f , 0.0f , 3.1f , 0.0f } ,
{ 3.0f , 1.0f , - 2352.3f , - 115.3f , - 6.2f , 8.4f } ,
{ 3.0f , 2.0f , 1225.6f , 245.0f , - 0.4f , - 0.4f } ,
{ 3.0f , 3.0f , 581.9f , - 538.3f , - 10.4f , 2.3f } ,
{ 4.0f , 0.0f , 907.2f , 0.0f , - 0.4f , 0.0f } ,
{ 4.0f , 1.0f , 813.7f , 283.4f , 0.8f , - 0.6f } ,
{ 4.0f , 2.0f , 120.3f , - 188.6f , - 9.2f , 5.3f } ,
{ 4.0f , 3.0f , - 335.0f , 180.9f , 4.0f , 3.0f } ,
{ 4.0f , 4.0f , 70.3f , - 329.5f , - 4.2f , - 5.3f } ,
{ 5.0f , 0.0f , - 232.6f , 0.0f , - 0.2f , 0.0f } ,
{ 5.0f , 1.0f , 360.1f , 47.4f , 0.1f , 0.4f } ,
{ 5.0f , 2.0f , 192.4f , 196.9f , - 1.4f , 1.6f } ,
{ 5.0f , 3.0f , - 141.0f , - 119.4f , 0.0f , - 1.1f } ,
{ 5.0f , 4.0f , - 157.4f , 16.1f , 1.3f , 3.3f } ,
{ 5.0f , 5.0f , 4.3f , 100.1f , 3.8f , 0.1f } ,
{ 6.0f , 0.0f , 69.5f , 0.0f , - 0.5f , 0.0f } ,
{ 6.0f , 1.0f , 67.4f , - 20.7f , - 0.2f , 0.0f } ,
{ 6.0f , 2.0f , 72.8f , 33.2f , - 0.6f , - 2.2f } ,
{ 6.0f , 3.0f , - 129.8f , 58.8f , 2.4f , - 0.7f } ,
{ 6.0f , 4.0f , - 29.0f , - 66.5f , - 1.1f , 0.1f } ,
{ 6.0f , 5.0f , 13.2f , 7.3f , 0.3f , 1.0f } ,
{ 6.0f , 6.0f , - 70.9f , 62.5f , 1.5f , 1.3f } ,
{ 7.0f , 0.0f , 81.6f , 0.0f , 0.2f , 0.0f } ,
{ 7.0f , 1.0f , - 76.1f , - 54.1f , - 0.2f , 0.7f } ,
{ 7.0f , 2.0f , - 6.8f , - 19.4f , - 0.4f , 0.5f } ,
{ 7.0f , 3.0f , 51.9f , 5.6f , 1.3f , - 0.2f } ,
{ 7.0f , 4.0f , 15.0f , 24.4f , 0.2f , - 0.1f } ,
{ 7.0f , 5.0f , 9.3f , 3.3f , - 0.4f , - 0.7f } ,
{ 7.0f , 6.0f , - 2.8f , - 27.5f , - 0.9f , 0.1f } ,
{ 7.0f , 7.0f , 6.7f , - 2.3f , 0.3f , 0.1f } ,
{ 8.0f , 0.0f , 24.0f , 0.0f , 0.0f , 0.0f } ,
{ 8.0f , 1.0f , 8.6f , 10.2f , 0.1f , - 0.3f } ,
{ 8.0f , 2.0f , - 16.9f , - 18.1f , - 0.5f , 0.3f } ,
{ 8.0f , 3.0f , - 3.2f , 13.2f , 0.5f , 0.3f } ,
{ 8.0f , 4.0f , - 20.6f , - 14.6f , - 0.2f , 0.6f } ,
{ 8.0f , 5.0f , 13.3f , 16.2f , 0.4f , - 0.1f } ,
{ 8.0f , 6.0f , 11.7f , 5.7f , 0.2f , - 0.2f } ,
{ 8.0f , 7.0f , - 16.0f , - 9.1f , - 0.4f , 0.3f } ,
{ 8.0f , 8.0f , - 2.0f , 2.2f , 0.3f , 0.0f } ,
2013-05-19 16:37:30 +02:00
{ 9.0f , 0.0f , 5.4f , 0.0f , 0.0f , 0.0f } ,
2015-12-13 12:59:25 +01:00
{ 9.0f , 1.0f , 8.8f , - 21.6f , - 0.1f , - 0.2f } ,
{ 9.0f , 2.0f , 3.1f , 10.8f , - 0.1f , - 0.1f } ,
{ 9.0f , 3.0f , - 3.1f , 11.7f , 0.4f , - 0.2f } ,
{ 9.0f , 4.0f , 0.6f , - 6.8f , - 0.5f , 0.1f } ,
{ 9.0f , 5.0f , - 13.3f , - 6.9f , - 0.2f , 0.1f } ,
{ 9.0f , 6.0f , - 0.1f , 7.8f , 0.1f , 0.0f } ,
{ 9.0f , 7.0f , 8.7f , 1.0f , 0.0f , - 0.2f } ,
{ 9.0f , 8.0f , - 9.1f , - 3.9f , - 0.2f , 0.4f } ,
{ 9.0f , 9.0f , - 10.5f , 8.5f , - 0.1f , 0.3f } ,
{ 10.0f , 0.0f , - 1.9f , 0.0f , 0.0f , 0.0f } ,
{ 10.0f , 1.0f , - 6.5f , 3.3f , 0.0f , 0.1f } ,
{ 10.0f , 2.0f , 0.2f , - 0.3f , - 0.1f , - 0.1f } ,
{ 10.0f , 3.0f , 0.6f , 4.6f , 0.3f , 0.0f } ,
{ 10.0f , 4.0f , - 0.6f , 4.4f , - 0.1f , 0.0f } ,
{ 10.0f , 5.0f , 1.7f , - 7.9f , - 0.1f , - 0.2f } ,
{ 10.0f , 6.0f , - 0.7f , - 0.6f , - 0.1f , 0.1f } ,
{ 10.0f , 7.0f , 2.1f , - 4.1f , 0.0f , - 0.1f } ,
{ 10.0f , 8.0f , 2.3f , - 2.8f , - 0.2f , - 0.2f } ,
{ 10.0f , 9.0f , - 1.8f , - 1.1f , - 0.1f , 0.1f } ,
{ 10.0f , 10.0f , - 3.6f , - 8.7f , - 0.2f , - 0.1f } ,
{ 11.0f , 0.0f , 3.1f , 0.0f , 0.0f , 0.0f } ,
{ 11.0f , 1.0f , - 1.5f , - 0.1f , 0.0f , 0.0f } ,
{ 11.0f , 2.0f , - 2.3f , 2.1f , - 0.1f , 0.1f } ,
{ 11.0f , 3.0f , 2.1f , - 0.7f , 0.1f , 0.0f } ,
{ 11.0f , 4.0f , - 0.9f , - 1.1f , 0.0f , 0.1f } ,
{ 11.0f , 5.0f , 0.6f , 0.7f , 0.0f , 0.0f } ,
{ 11.0f , 6.0f , - 0.7f , - 0.2f , 0.0f , 0.0f } ,
{ 11.0f , 7.0f , 0.2f , - 2.1f , 0.0f , 0.1f } ,
{ 11.0f , 8.0f , 1.7f , - 1.5f , 0.0f , 0.0f } ,
{ 11.0f , 9.0f , - 0.2f , - 2.5f , 0.0f , - 0.1f } ,
{ 11.0f , 10.0f , 0.4f , - 2.0f , - 0.1f , 0.0f } ,
{ 11.0f , 11.0f , 3.5f , - 2.3f , - 0.1f , - 0.1f } ,
{ 12.0f , 0.0f , - 2.0f , 0.0f , 0.1f , 0.0f } ,
{ 12.0f , 1.0f , - 0.3f , - 1.0f , 0.0f , 0.0f } ,
{ 12.0f , 2.0f , 0.4f , 0.5f , 0.0f , 0.0f } ,
{ 12.0f , 3.0f , 1.3f , 1.8f , 0.1f , - 0.1f } ,
{ 12.0f , 4.0f , - 0.9f , - 2.2f , - 0.1f , 0.0f } ,
{ 12.0f , 5.0f , 0.9f , 0.3f , 0.0f , 0.0f } ,
{ 12.0f , 6.0f , 0.1f , 0.7f , 0.1f , 0.0f } ,
{ 12.0f , 7.0f , 0.5f , - 0.1f , 0.0f , 0.0f } ,
{ 12.0f , 8.0f , - 0.4f , 0.3f , 0.0f , 0.0f } ,
{ 12.0f , 9.0f , - 0.4f , 0.2f , 0.0f , 0.0f } ,
2013-05-19 16:37:30 +02:00
{ 12.0f , 10.0f , 0.2f , - 0.9f , 0.0f , 0.0f } ,
2015-12-13 12:59:25 +01:00
{ 12.0f , 11.0f , - 0.9f , - 0.2f , 0.0f , 0.0f } ,
{ 12.0f , 12.0f , 0.0f , 0.7f , 0.0f , 0.0f }
2010-12-24 22:57:12 +01:00
} ;
2013-05-19 16:37:30 +02: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 ( )
2013-05-19 16:37:30 +02:00
// Sets default values for WMM subroutines.
// UPDATES : Ellip and MagneticModel
{
if ( ! Ellip ) {
return - 1 ; // invalid pointer
}
if ( ! MagneticModel ) {
return - 2 ; // invalid pointer
}
// Sets WGS-84 parameters
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
Ellip - > eps = sqrt ( 1 - ( Ellip - > b * Ellip - > b ) / ( Ellip - > a * Ellip - > a ) ) ; // first eccentricity
Ellip - > epssq = ( Ellip - > eps * Ellip - > eps ) ; // first eccentricity squared
Ellip - > re = 6371.2f ; // Earth's radius in km
// Sets Magnetic Model parameters
MagneticModel - > nMax = WMM_MAX_MODEL_DEGREES ;
MagneticModel - > nMaxSecVar = WMM_MAX_SECULAR_VARIATION_MODEL_DEGREES ;
MagneticModel - > SecularVariationUsed = 0 ;
2015-12-13 12:59:25 +01:00
// Really, Really needs to be read from a file - out of date in 2020 at latest
2013-05-19 16:37:30 +02:00
MagneticModel - > EditionDate = 0.0f ; /* OP change. Originally 5.7863328170559505e-307, truncates to 0.0f */
2015-12-13 12:59:25 +01:00
MagneticModel - > epoch = 2015.0f ;
sprintf ( MagneticModel - > ModelName , " WMM-2015 " ) ;
2013-05-19 16:37:30 +02: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
2013-05-19 16:37:30 +02:00
int returned = 0 ; // default to OK
2011-01-11 12:11:23 +01:00
// ***********
// range check supplied params
2013-05-19 16:37:30 +02:00
if ( Lat < - 90.0f ) {
return - 1 ; // error
}
if ( Lat > 90.0f ) {
return - 2 ; // error
}
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
2013-05-19 16:37:30 +02:00
// Ellip = NULL;
// MagneticModel = NULL;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
// MagneticModel = NULL;
// CoordGeodetic = NULL;
// GeoMagneticElements = NULL;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
Ellip = ( WMMtype_Ellipsoid * ) MALLOC ( sizeof ( WMMtype_Ellipsoid ) ) ;
MagneticModel = ( WMMtype_MagneticModel * ) MALLOC ( sizeof ( WMMtype_MagneticModel ) ) ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
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 ) ) ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( ! Ellip | | ! MagneticModel | | ! CoordSpherical | | ! CoordGeodetic | | ! GeoMagneticElements ) {
returned = - 5 ; // error
}
2011-01-11 12:11:23 +01:00
// ***********
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) {
if ( WMM_Initialize ( ) < 0 ) {
returned = - 6 ; // error
}
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) {
2011-01-11 12:11:23 +01:00
CoordGeodetic - > lambda = Lon ;
2013-05-19 16:37:30 +02:00
CoordGeodetic - > phi = Lat ;
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
2013-05-19 16:37:30 +02:00
if ( WMM_GeodeticToSpherical ( CoordGeodetic , CoordSpherical ) < 0 ) {
returned = - 7 ; // error
}
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) {
if ( WMM_DateToYear ( Month , Day , Year ) < 0 ) {
returned = - 8 ; // error
}
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) {
2011-01-11 12:11:23 +01:00
// Compute the geoMagnetic field elements and their time change
2013-05-19 16:37:30 +02:00
if ( WMM_Geomag ( CoordSpherical , CoordGeodetic , GeoMagneticElements ) < 0 ) {
returned = - 9 ; // error
} else { // set the returned values
2011-01-11 12:11:23 +01:00
B [ 0 ] = GeoMagneticElements - > X ;
B [ 1 ] = GeoMagneticElements - > Y ;
B [ 2 ] = GeoMagneticElements - > Z ;
}
}
2013-05-19 16:37:30 +02:00
// ***********
// free allocated memory
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( GeoMagneticElements ) {
2011-01-11 12:11:23 +01:00
FREE ( GeoMagneticElements ) ;
2013-05-19 16:37:30 +02:00
}
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( CoordGeodetic ) {
2011-01-11 12:11:23 +01:00
FREE ( CoordGeodetic ) ;
2013-05-19 16:37:30 +02:00
}
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( CoordSpherical ) {
2011-01-11 12:11:23 +01:00
FREE ( CoordSpherical ) ;
2013-05-19 16:37:30 +02:00
}
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( MagneticModel ) {
FREE ( MagneticModel ) ;
MagneticModel = NULL ;
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
if ( Ellip ) {
2011-01-11 12:11:23 +01:00
FREE ( Ellip ) ;
Ellip = NULL ;
}
2013-05-19 16:37:30 +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
}
2013-05-19 16:37:30 +02:00
int WMM_Geomag ( WMMtype_CoordSpherical * CoordSpherical , WMMtype_CoordGeodetic * CoordGeodetic , WMMtype_GeoMagneticElements * GeoMagneticElements )
/*
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
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
int returned = 0 ; // default to OK
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
WMMtype_MagneticResults MagneticResultsSph ;
WMMtype_MagneticResults MagneticResultsGeo ;
WMMtype_MagneticResults MagneticResultsSphVar ;
WMMtype_MagneticResults MagneticResultsGeoVar ;
2011-01-10 21:30:47 +01:00
2011-01-11 12:11:23 +01:00
// ********
// allocate required memory
2011-01-10 21:30:47 +01:00
2013-05-19 16:37:30 +02:00
WMMtype_LegendreFunction * LegendreFunction = ( WMMtype_LegendreFunction * ) MALLOC ( sizeof ( WMMtype_LegendreFunction ) ) ;
WMMtype_SphericalHarmonicVariables * SphVariables = ( WMMtype_SphericalHarmonicVariables * ) MALLOC ( sizeof ( WMMtype_SphericalHarmonicVariables ) ) ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( ! LegendreFunction | | ! SphVariables ) {
returned = - 1 ; // memory allocation error
}
2011-01-11 12:11:23 +01:00
// ********
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) { // Compute Spherical Harmonic variables
if ( WMM_ComputeSphericalHarmonicVariables ( CoordSpherical , MagneticModel - > nMax , SphVariables ) < 0 ) {
returned = - 2 ; // error
}
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) { // Compute ALF
if ( WMM_AssociatedLegendreFunction ( CoordSpherical , MagneticModel - > nMax , LegendreFunction ) < 0 ) {
returned = - 3 ; // error
}
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) { // Accumulate the spherical harmonic coefficients
if ( WMM_Summation ( LegendreFunction , SphVariables , CoordSpherical , & MagneticResultsSph ) < 0 ) {
returned = - 4 ; // error
}
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) { // Sum the Secular Variation Coefficients
if ( WMM_SecVarSummation ( LegendreFunction , SphVariables , CoordSpherical , & MagneticResultsSphVar ) < 0 ) {
returned = - 5 ; // error
}
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) { // Map the computed Magnetic fields to Geodeitic coordinates
if ( WMM_RotateMagneticVector ( CoordSpherical , CoordGeodetic , & MagneticResultsSph , & MagneticResultsGeo ) < 0 ) {
returned = - 6 ; // error
}
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) { // Map the secular variation field components to Geodetic coordinates
if ( WMM_RotateMagneticVector ( CoordSpherical , CoordGeodetic , & MagneticResultsSphVar , & MagneticResultsGeoVar ) < 0 ) {
returned = - 7 ; // error
}
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) { // Calculate the Geomagnetic elements, Equation 18 , WMM Technical report
if ( WMM_CalculateGeoMagneticElements ( & MagneticResultsGeo , GeoMagneticElements ) < 0 ) {
returned = - 8 ; // error
}
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
if ( returned > = 0 ) { // Calculate the secular variation of each of the Geomagnetic elements
if ( WMM_CalculateSecularVariation ( & MagneticResultsGeoVar , GeoMagneticElements ) < 0 ) {
returned = - 9 ; // error
}
2011-01-11 12:11:23 +01:00
}
// ********
// free allocated memory
2013-05-19 16:37:30 +02:00
if ( SphVariables ) {
2011-01-11 12:11:23 +01:00
FREE ( SphVariables ) ;
2013-05-19 16:37:30 +02:00
}
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( LegendreFunction ) {
2011-01-11 12:11:23 +01:00
FREE ( LegendreFunction ) ;
2013-05-19 16:37:30 +02:00
}
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_ComputeSphericalHarmonicVariables ( WMMtype_CoordSpherical * CoordSpherical , uint16_t nMax , WMMtype_SphericalHarmonicVariables * SphVariables )
2013-05-19 16:37:30 +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
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
float cos_lambda , sin_lambda ;
uint16_t m , n ;
cos_lambda = cosf ( DEG2RAD ( CoordSpherical - > lambda ) ) ;
sin_lambda = sinf ( DEG2RAD ( CoordSpherical - > lambda ) ) ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +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 ) . */
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
SphVariables - > RelativeRadiusPower [ 0 ] = ( Ellip - > re / CoordSpherical - > r ) * ( Ellip - > re / CoordSpherical - > r ) ;
for ( n = 1 ; n < = nMax ; n + + ) {
SphVariables - > RelativeRadiusPower [ n ] = SphVariables - > RelativeRadiusPower [ n - 1 ] * ( Ellip - > re / CoordSpherical - > r ) ;
}
/*
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 )
*/
SphVariables - > cos_mlambda [ 0 ] = 1.0f ;
SphVariables - > sin_mlambda [ 0 ] = 0.0f ;
SphVariables - > cos_mlambda [ 1 ] = cos_lambda ;
SphVariables - > sin_mlambda [ 1 ] = sin_lambda ;
for ( m = 2 ; m < = nMax ; m + + ) {
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
2013-05-19 16:37:30 +02:00
return 0 ; // OK
}
int WMM_AssociatedLegendreFunction ( WMMtype_CoordSpherical * CoordSpherical , uint16_t nMax , WMMtype_LegendreFunction * LegendreFunction )
/* 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
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
float sin_phi = sinf ( DEG2RAD ( CoordSpherical - > phig ) ) ; /* sinf (geocentric latitude) */
if ( nMax < = 16 | | ( 1 - fabsf ( sin_phi ) ) < 1.0e-10 f ) { /* If nMax is less tha 16 or at the poles */
if ( WMM_PcupLow ( LegendreFunction - > Pcup , LegendreFunction - > dPcup , sin_phi , nMax ) < 0 ) {
return - 1 ; // error
}
} else {
if ( WMM_PcupHigh ( LegendreFunction - > Pcup , LegendreFunction - > dPcup , sin_phi , nMax ) < 0 ) {
return - 2 ; // error
}
}
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
int WMM_Summation ( WMMtype_LegendreFunction * LegendreFunction ,
WMMtype_SphericalHarmonicVariables * SphVariables ,
WMMtype_CoordSpherical * CoordSpherical , WMMtype_MagneticResults * MagneticResults )
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
/* Computes Geomagnetic Field Elements X, Y and Z in Spherical coordinate system using
spherical harmonic summation .
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
The vector Magnetic field is given by - grad V , where V is Geomagnetic scalar potential
The gradient in spherical coordinates is given by :
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
dV ^ 1 dV ^ 1 dV ^
grad V = - - r + - - - t + - - - - - - - - - - p
dr r dt r sinf ( t ) dp
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
INPUT : LegendreFunction
MagneticModel
SphVariables
CoordSpherical
OUTPUT : MagneticResults
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
CALLS : WMM_SummationSpecial
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
Manoj Nair , June , 2009 Manoj . C . Nair @ Noaa . Gov
*/
2011-01-11 12:11:23 +01:00
uint16_t m , n , index ;
2013-05-19 16:37:30 +02:00
float cos_phi ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
MagneticResults - > Bz = 0.0f ;
MagneticResults - > By = 0.0f ;
MagneticResults - > Bx = 0.0f ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
for ( n = 1 ; n < = MagneticModel - > nMax ; n + + ) {
for ( m = 0 ; m < = n ; m + + ) {
index = ( n * ( n + 1 ) / 2 + m ) ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
/* nMax (n+2) n m m m
Bz = - SUM ( a / r ) ( n + 1 ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] P ( sinf ( phi ) )
n = 1 m = 0 n n n */
2010-09-21 21:29:35 +02:00
/* Equation 12 in the WMM Technical report. Derivative with respect to radius.*/
2013-05-19 16:37:30 +02:00
MagneticResults - > Bz - =
SphVariables - > RelativeRadiusPower [ n ] *
( WMM_get_main_field_coeff_g ( index ) *
SphVariables - > cos_mlambda [ m ] + WMM_get_main_field_coeff_h ( index ) * SphVariables - > sin_mlambda [ m ] )
* ( float ) ( n + 1 ) * LegendreFunction - > Pcup [ index ] ;
2010-09-21 21:29:35 +02:00
/* 1 nMax (n+2) n m m m
2013-05-19 16:37:30 +02:00
By = SUM ( a / r ) ( m ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
n = 1 m = 0 n n n */
2010-09-21 21:29:35 +02:00
/* Equation 11 in the WMM Technical report. Derivative with respect to longitude, divided by radius. */
2013-05-19 16:37:30 +02:00
MagneticResults - > By + =
SphVariables - > RelativeRadiusPower [ n ] *
( WMM_get_main_field_coeff_g ( index ) *
SphVariables - > sin_mlambda [ m ] - WMM_get_main_field_coeff_h ( index ) * SphVariables - > cos_mlambda [ m ] )
* ( float ) ( m ) * LegendreFunction - > Pcup [ index ] ;
2010-09-21 21:29:35 +02:00
/* nMax (n+2) n m m m
2013-05-19 16:37:30 +02:00
Bx = - SUM ( a / r ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
n = 1 m = 0 n n n */
2010-09-21 21:29:35 +02:00
/* Equation 10 in the WMM Technical report. Derivative with respect to latitude, divided by radius. */
2013-05-19 16:37:30 +02:00
MagneticResults - > Bx - =
SphVariables - > RelativeRadiusPower [ n ] *
( WMM_get_main_field_coeff_g ( index ) *
SphVariables - > cos_mlambda [ m ] + WMM_get_main_field_coeff_h ( index ) * SphVariables - > sin_mlambda [ m ] )
* LegendreFunction - > dPcup [ index ] ;
}
}
cos_phi = cosf ( DEG2RAD ( CoordSpherical - > phig ) ) ;
if ( fabsf ( cos_phi ) > 1.0e-10 f ) {
MagneticResults - > By = MagneticResults - > By / cos_phi ;
} else {
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 .
*/
2013-05-19 16:37:30 +02:00
if ( WMM_SummationSpecial ( SphVariables , CoordSpherical , MagneticResults ) < 0 ) {
return - 1 ; // error
}
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
int WMM_SecVarSummation ( WMMtype_LegendreFunction * LegendreFunction ,
WMMtype_SphericalHarmonicVariables *
SphVariables , WMMtype_CoordSpherical * CoordSpherical , WMMtype_MagneticResults * MagneticResults )
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +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
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
CALLS : WMM_SecVarSummationSpecial
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
*/
2011-01-11 12:11:23 +01:00
uint16_t m , n , index ;
2013-05-19 16:37:30 +02:00
float cos_phi ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
MagneticModel - > SecularVariationUsed = TRUE ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
MagneticResults - > Bz = 0.0f ;
MagneticResults - > By = 0.0f ;
MagneticResults - > Bx = 0.0f ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
for ( n = 1 ; n < = MagneticModel - > nMaxSecVar ; n + + ) {
for ( m = 0 ; m < = n ; m + + ) {
index = ( n * ( n + 1 ) / 2 + m ) ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
/* nMax (n+2) n m m m
Bz = - SUM ( a / r ) ( n + 1 ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] P ( sinf ( phi ) )
n = 1 m = 0 n n n */
2010-09-21 21:29:35 +02:00
/* Derivative with respect to radius.*/
2013-05-19 16:37:30 +02:00
MagneticResults - > Bz - =
SphVariables - > RelativeRadiusPower [ n ] *
( WMM_get_secular_var_coeff_g ( index ) *
SphVariables - > cos_mlambda [ m ] + WMM_get_secular_var_coeff_h ( index ) * SphVariables - > sin_mlambda [ m ] )
* ( float ) ( n + 1 ) * LegendreFunction - > Pcup [ index ] ;
2010-09-21 21:29:35 +02:00
/* 1 nMax (n+2) n m m m
2013-05-19 16:37:30 +02:00
By = SUM ( a / r ) ( m ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
n = 1 m = 0 n n n */
2010-09-21 21:29:35 +02:00
/* Derivative with respect to longitude, divided by radius. */
2013-05-19 16:37:30 +02:00
MagneticResults - > By + =
SphVariables - > RelativeRadiusPower [ n ] *
( WMM_get_secular_var_coeff_g ( index ) *
SphVariables - > sin_mlambda [ m ] - WMM_get_secular_var_coeff_h ( index ) * SphVariables - > cos_mlambda [ m ] )
* ( float ) ( m ) * LegendreFunction - > Pcup [ index ] ;
2010-09-21 21:29:35 +02:00
/* nMax (n+2) n m m m
2013-05-19 16:37:30 +02:00
Bx = - SUM ( a / r ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
n = 1 m = 0 n n n */
2010-09-21 21:29:35 +02:00
/* Derivative with respect to latitude, divided by radius. */
2013-05-19 16:37:30 +02:00
MagneticResults - > Bx - =
SphVariables - > RelativeRadiusPower [ n ] *
( WMM_get_secular_var_coeff_g ( index ) *
SphVariables - > cos_mlambda [ m ] + WMM_get_secular_var_coeff_h ( index ) * SphVariables - > sin_mlambda [ m ] )
* LegendreFunction - > dPcup [ index ] ;
}
}
cos_phi = cosf ( DEG2RAD ( CoordSpherical - > phig ) ) ;
if ( fabsf ( cos_phi ) > 1.0e-10 f ) {
MagneticResults - > By = MagneticResults - > By / cos_phi ;
} else {
/* Special calculation for component By at Geographic poles */
if ( WMM_SecVarSummationSpecial ( SphVariables , CoordSpherical , MagneticResults ) < 0 ) {
return - 1 ; // error
}
}
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
2013-05-19 16:37:30 +02:00
int WMM_RotateMagneticVector ( WMMtype_CoordSpherical * CoordSpherical ,
WMMtype_CoordGeodetic * CoordGeodetic ,
WMMtype_MagneticResults * MagneticResultsSph , WMMtype_MagneticResults * MagneticResultsGeo )
/* 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
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
/* Difference between the spherical and Geodetic latitudes */
float Psi = DEG2RAD ( CoordSpherical - > phig - CoordGeodetic - > phi ) ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
/* Rotate spherical field components to the Geodeitic system */
MagneticResultsGeo - > Bz = MagneticResultsSph - > Bx * sinf ( Psi ) + MagneticResultsSph - > Bz * cosf ( Psi ) ;
MagneticResultsGeo - > Bx = MagneticResultsSph - > Bx * cosf ( Psi ) - MagneticResultsSph - > Bz * sinf ( Psi ) ;
MagneticResultsGeo - > By = MagneticResultsSph - > By ;
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
return 0 ;
}
2013-05-19 16:37:30 +02:00
int WMM_CalculateGeoMagneticElements ( WMMtype_MagneticResults * MagneticResultsGeo , WMMtype_GeoMagneticElements * GeoMagneticElements )
/* 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
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
GeoMagneticElements - > X = MagneticResultsGeo - > Bx ;
GeoMagneticElements - > Y = MagneticResultsGeo - > By ;
GeoMagneticElements - > Z = MagneticResultsGeo - > Bz ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +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
2013-05-19 16:37:30 +02:00
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02: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.
2013-05-19 16:37:30 +02:00
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
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
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 +
MagneticElements - > Y * MagneticElements - > Ydot + MagneticElements - > Z * MagneticElements - > Zdot ) / MagneticElements - > F ;
MagneticElements - > Decldot =
180.0f / M_PI_F * ( MagneticElements - > X * MagneticElements - > Ydot -
MagneticElements - > Y * MagneticElements - > Xdot ) / ( MagneticElements - > H * MagneticElements - > H ) ;
MagneticElements - > Incldot =
180.0f / M_PI_F * ( MagneticElements - > H * MagneticElements - > Zdot -
MagneticElements - > Z * MagneticElements - > Hdot ) / ( MagneticElements - > F * MagneticElements - > F ) ;
MagneticElements - > GVdot = MagneticElements - > Decldot ;
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
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
2013-05-19 16:37:30 +02:00
functions up to degree nMax . The functions are initially scaled by
10 ^ 280 sinf ^ m in order to minimize the effects of underflow at large m
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 .
x : cosf ( colatitude ) or sinf ( latitude ) .
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
dP ( n , m ) ( x ) / dx , where x = sinf ( latitude ) ( or cosf ( colatitude ) ) .
However , the WMM Geomagnetic routines requires dP ( n , m ) ( x ) / dlatitude .
Hence the derivatives are multiplied by sinf ( latitude ) .
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 .
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
uint16_t k , kstart , m , n ;
float pm2 , pm1 , pmm , plm , rescalem , z , scalef ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02: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
2013-05-19 16:37:30 +02:00
if ( ! PreSqr | | ! f2 | | ! f1 ) { // memory allocation error
if ( PreSqr ) {
FREE ( PreSqr ) ;
}
if ( f2 ) {
FREE ( f2 ) ;
}
if ( f1 ) {
FREE ( f1 ) ;
}
2011-01-11 12:11:23 +01:00
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-19 16:37:30 +02:00
if ( fabsf ( x ) - 1.0f < 1e-9 f ) {
FREE ( PreSqr ) ;
FREE ( f2 ) ;
FREE ( f1 ) ;
// printf("Error in PcupHigh: derivative cannot be calculated at poles\n");
return - 2 ;
}
/* 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 ;
for ( n = 0 ; n < = 2 * nMax + 1 ; + + n ) {
PreSqr [ n ] = sqrtf ( ( float ) ( n ) ) ;
}
k = 2 ;
for ( n = 2 ; n < = nMax ; n + + ) {
k = k + 1 ;
f1 [ k ] = ( float ) ( 2 * n - 1 ) / ( float ) ( n ) ;
f2 [ k ] = ( float ) ( n - 1 ) / ( float ) ( n ) ;
for ( m = 1 ; m < = n - 2 ; m + + ) {
k = k + 1 ;
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 ] ;
}
k = k + 2 ;
}
/*z = sinf (geocentric latitude) */
z = sqrtf ( ( 1.0f - x ) * ( 1.0f + x ) ) ;
pm2 = 1.0f ;
Pcup [ 0 ] = 1.0f ;
dPcup [ 0 ] = 0.0f ;
if ( nMax = = 0 ) {
2011-01-11 12:11:23 +01:00
FREE ( PreSqr ) ;
FREE ( f2 ) ;
FREE ( f1 ) ;
return - 3 ;
}
2013-05-19 16:37:30 +02:00
pm1 = x ;
Pcup [ 1 ] = pm1 ;
dPcup [ 1 ] = z ;
k = 1 ;
for ( n = 2 ; n < = nMax ; n + + ) {
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 ;
rescalem = 1.0f / scalef ;
kstart = 0 ;
for ( m = 1 ; m < = nMax - 1 ; + + m ) {
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 ;
dPcup [ k ] = ( ( pm2 * rescalem ) * PreSqr [ 2 * m + 1 ] - x * ( float ) ( m + 1 ) * Pcup [ k ] ) / z ;
/* Calculate Pcup(n,m) */
for ( n = m + 2 ; n < = nMax ; + + n ) {
k = k + n ;
plm = x * f1 [ k ] * pm1 - f2 [ k ] * pm2 ;
Pcup [ k ] = plm * rescalem ;
dPcup [ k ] = ( PreSqr [ n + m ] * PreSqr [ n - m ] * ( pm1 * rescalem ) - ( float ) ( n ) * x * Pcup [ k ] ) / z ;
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 ;
// *********
// free allocated memory
FREE ( PreSqr ) ;
2011-01-10 21:30:47 +01:00
FREE ( f2 ) ;
2013-05-19 16:37:30 +02:00
FREE ( f1 ) ;
2011-01-10 21:30:47 +01:00
2011-01-11 12:11:23 +01:00
// *********
2013-05-19 16:37:30 +02:00
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
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
2013-05-19 16:37:30 +02:00
functions up to degree nMax .
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
Calling Parameters :
INPUT
nMax : Maximum spherical harmonic degree to compute .
x : cosf ( colatitude ) or sinf ( latitude ) .
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +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
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
Notes : Overflow may occur if nMax > 20 , especially for high - latitudes .
Use WMM_PcupHigh for large nMax .
2010-09-21 21:29:35 +02:00
Writted by Manoj Nair , June , 2009 . Manoj . C . Nair @ Noaa . Gov .
2013-05-19 16:37:30 +02:00
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 .
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
uint16_t n , m , index , index1 , index2 ;
float k , z ;
float * schmidtQuasiNorm = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUP ) ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( ! schmidtQuasiNorm ) { // memory allocation error
2011-01-11 12:11:23 +01:00
return - 1 ;
}
2011-01-10 21:30:47 +01:00
2013-05-19 16:37:30 +02:00
Pcup [ 0 ] = 1.0f ;
dPcup [ 0 ] = 0.0f ;
/*sinf (geocentric latitude) - sin_phi */
z = sqrtf ( ( 1.0f - x ) * ( 1.0f + x ) ) ;
/* First, Compute the Gauss-normalized associated Legendre functions */
for ( n = 1 ; n < = nMax ; n + + ) {
for ( m = 0 ; m < = n ; m + + ) {
index = ( n * ( n + 1 ) / 2 + m ) ;
if ( n = = m ) {
index1 = ( n - 1 ) * n / 2 + m - 1 ;
Pcup [ index ] = z * Pcup [ index1 ] ;
dPcup [ index ] = z * dPcup [ index1 ] + x * Pcup [ index1 ] ;
} else if ( n = = 1 & & m = = 0 ) {
index1 = ( n - 1 ) * n / 2 + m ;
Pcup [ index ] = x * Pcup [ index1 ] ;
dPcup [ index ] = x * dPcup [ index1 ] - z * Pcup [ index1 ] ;
} else if ( n > 1 & & n ! = m ) {
index1 = ( n - 2 ) * ( n - 1 ) / 2 + m ;
index2 = ( n - 1 ) * n / 2 + m ;
if ( m > n - 2 ) {
Pcup [ index ] = x * Pcup [ index2 ] ;
dPcup [ index ] = x * dPcup [ index2 ] - z * Pcup [ index2 ] ;
} else {
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
2013-05-19 16:37:30 +02:00
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 ) ! */
schmidtQuasiNorm [ 0 ] = 1.0f ;
for ( n = 1 ; n < = nMax ; n + + ) {
index = ( n * ( n + 1 ) / 2 ) ;
index1 = ( n - 1 ) * n / 2 ;
/* for m = 0 */
schmidtQuasiNorm [ index ] = schmidtQuasiNorm [ index1 ] * ( float ) ( 2 * n - 1 ) / ( float ) n ;
for ( m = 1 ; m < = n ; m + + ) {
index = ( n * ( n + 1 ) / 2 + m ) ;
index1 = ( n * ( n + 1 ) / 2 + m - 1 ) ;
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
2013-05-19 16:37:30 +02:00
functions to the Schmidt quasi - normalized version using pre - computed
relation stored in the variable schmidtQuasiNorm */
for ( n = 1 ; n < = nMax ; n + + ) {
for ( m = 0 ; m < = n ; m + + ) {
index = ( n * ( n + 1 ) / 2 + m ) ;
Pcup [ index ] = Pcup [ index ] * schmidtQuasiNorm [ index ] ;
dPcup [ index ] = - dPcup [ index ] * schmidtQuasiNorm [ index ] ;
/* The sign is changed since the new WMM routines use derivative with respect to latitude
insted of co - latitude */
}
}
FREE ( schmidtQuasiNorm ) ;
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_SummationSpecial ( WMMtype_SphericalHarmonicVariables *
2013-05-19 16:37:30 +02:00
SphVariables , WMMtype_CoordSpherical * CoordSpherical , WMMtype_MagneticResults * MagneticResults )
/* 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
*/
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
uint16_t n , index ;
float k , sin_phi ;
float schmidtQuasiNorm1 ;
float schmidtQuasiNorm2 ;
float schmidtQuasiNorm3 ;
float * PcupS = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUPS ) ;
if ( ! PcupS ) {
return - 1 ; // memory allocation error
}
PcupS [ 0 ] = 1 ;
schmidtQuasiNorm1 = 1.0f ;
MagneticResults - > By = 0.0f ;
sin_phi = sinf ( DEG2RAD ( CoordSpherical - > phig ) ) ;
for ( n = 1 ; n < = MagneticModel - > nMax ; n + + ) {
/*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 ) ;
schmidtQuasiNorm2 = schmidtQuasiNorm1 * ( float ) ( 2 * n - 1 ) / ( float ) n ;
schmidtQuasiNorm3 = schmidtQuasiNorm2 * sqrtf ( ( float ) ( n * 2 ) / ( float ) ( n + 1 ) ) ;
schmidtQuasiNorm1 = schmidtQuasiNorm2 ;
if ( n = = 1 ) {
PcupS [ n ] = PcupS [ n - 1 ] ;
} else {
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-05-19 16:37:30 +02:00
By = SUM ( a / r ) ( m ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
n = 1 m = 0 n n n */
2010-09-21 21:29:35 +02:00
/* Equation 11 in the WMM Technical report. Derivative with respect to longitude, divided by radius. */
2013-05-19 16:37:30 +02:00
MagneticResults - > By + =
SphVariables - > RelativeRadiusPower [ n ] *
( WMM_get_main_field_coeff_g ( index ) *
SphVariables - > sin_mlambda [ 1 ] - WMM_get_main_field_coeff_h ( index ) * SphVariables - > cos_mlambda [ 1 ] )
* PcupS [ n ] * schmidtQuasiNorm3 ;
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
FREE ( PcupS ) ;
2011-01-10 21:30:47 +01:00
2013-05-19 16:37:30 +02:00
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2011-01-11 12:11:23 +01:00
int WMM_SecVarSummationSpecial ( WMMtype_SphericalHarmonicVariables *
2013-05-19 16:37:30 +02:00
SphVariables , WMMtype_CoordSpherical * CoordSpherical , WMMtype_MagneticResults * MagneticResults )
2010-09-21 21:29:35 +02:00
{
2013-05-19 16:37:30 +02:00
/*Special calculation for the secular variation summation at the poles.
INPUT : MagneticModel
SphVariables
CoordSpherical
OUTPUT : MagneticResults
CALLS : none
*/
uint16_t n , index ;
float k , sin_phi ;
float schmidtQuasiNorm1 ;
float schmidtQuasiNorm2 ;
float schmidtQuasiNorm3 ;
float * PcupS = ( float * ) MALLOC ( sizeof ( float ) * NUMPCUPS ) ;
if ( ! PcupS ) {
return - 1 ; // memory allocation error
}
PcupS [ 0 ] = 1 ;
schmidtQuasiNorm1 = 1.0f ;
MagneticResults - > By = 0.0f ;
sin_phi = sinf ( DEG2RAD ( CoordSpherical - > phig ) ) ;
for ( n = 1 ; n < = MagneticModel - > nMaxSecVar ; n + + ) {
index = ( n * ( n + 1 ) / 2 + 1 ) ;
schmidtQuasiNorm2 = schmidtQuasiNorm1 * ( float ) ( 2 * n - 1 ) / ( float ) n ;
schmidtQuasiNorm3 = schmidtQuasiNorm2 * sqrtf ( ( float ) ( n * 2 ) / ( float ) ( n + 1 ) ) ;
schmidtQuasiNorm1 = schmidtQuasiNorm2 ;
if ( n = = 1 ) {
PcupS [ n ] = PcupS [ n - 1 ] ;
} else {
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-05-19 16:37:30 +02:00
By = SUM ( a / r ) ( m ) SUM [ g cosf ( m p ) + h sinf ( m p ) ] dP ( sinf ( phi ) )
n = 1 m = 0 n n n */
2010-09-21 21:29:35 +02:00
/* Derivative with respect to longitude, divided by radius. */
2013-05-19 16:37:30 +02:00
MagneticResults - > By + =
SphVariables - > RelativeRadiusPower [ n ] *
( WMM_get_secular_var_coeff_g ( index ) *
SphVariables - > sin_mlambda [ 1 ] - WMM_get_secular_var_coeff_h ( index ) * SphVariables - > cos_mlambda [ 1 ] )
* PcupS [ n ] * schmidtQuasiNorm3 ;
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
FREE ( PcupS ) ;
2011-01-10 21:30:47 +01:00
2013-05-19 16:37:30 +02:00
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2010-12-24 22:57:12 +01:00
/**
* @ brief Comput the MainFieldCoeffH accounting for the date
*/
2013-05-19 16:37:30 +02:00
float WMM_get_main_field_coeff_g ( uint16_t index )
{
if ( index > = NUMTERMS ) {
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 ] ;
2013-05-19 16:37:30 +02:00
a = MagneticModel - > nMaxSecVar ;
b = ( a * ( a + 1 ) / 2 + a ) ;
for ( n = 1 ; n < = MagneticModel - > nMax ; n + + ) {
for ( m = 0 ; m < = n ; m + + ) {
sum_index = ( n * ( n + 1 ) / 2 + m ) ;
/* Hacky for now, will solve for which conditions need summing analytically */
if ( sum_index ! = index ) {
continue ;
}
if ( index < = b ) {
coeff + = ( decimal_date - MagneticModel - > epoch ) * WMM_get_secular_var_coeff_g ( sum_index ) ;
}
}
}
return coeff ;
2010-12-24 22:57:12 +01:00
}
2013-05-19 16:37:30 +02:00
float WMM_get_main_field_coeff_h ( uint16_t index )
{
if ( index > = NUMTERMS ) {
return 0 ;
}
2011-01-10 21:30:47 +01:00
uint16_t n , m , sum_index , a , b ;
2013-05-19 16:37:30 +02:00
float coeff = CoeffFile [ index ] [ 3 ] ;
a = MagneticModel - > nMaxSecVar ;
b = ( a * ( a + 1 ) / 2 + a ) ;
for ( n = 1 ; n < = MagneticModel - > nMax ; n + + ) {
for ( m = 0 ; m < = n ; m + + ) {
sum_index = ( n * ( n + 1 ) / 2 + m ) ;
/* Hacky for now, will solve for which conditions need summing analytically */
if ( sum_index ! = index ) {
continue ;
}
if ( index < = b ) {
coeff + = ( decimal_date - MagneticModel - > epoch ) * WMM_get_secular_var_coeff_h ( sum_index ) ;
}
}
}
return coeff ;
2010-12-24 22:57:12 +01:00
}
2013-05-19 16:37:30 +02:00
float WMM_get_secular_var_coeff_g ( uint16_t index )
{
if ( index > = NUMTERMS ) {
return 0 ;
}
return CoeffFile [ index ] [ 4 ] ;
2010-12-24 22:57:12 +01:00
}
2013-05-19 16:37:30 +02:00
float WMM_get_secular_var_coeff_h ( uint16_t index )
{
if ( index > = NUMTERMS ) {
return 0 ;
}
return CoeffFile [ index ] [ 5 ] ;
2010-12-24 22:57:12 +01:00
}
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
{
2013-05-19 16:37:30 +02:00
uint16_t temp = 0 ; // Total number of days
uint16_t MonthDays [ 13 ] = { 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 } ;
uint16_t ExtraDay = 0 ;
uint16_t i ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
if ( ( year % 4 = = 0 & & year % 100 ! = 0 ) | | ( year % 400 = = 0 ) ) {
ExtraDay = 1 ;
}
MonthDays [ 2 ] + = ExtraDay ;
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
/******************Validation********************************/
2011-01-11 12:11:23 +01:00
2013-05-19 16:37:30 +02:00
if ( month < = 0 | | month > 12 ) {
return - 1 ; // error
}
if ( day < = 0 | | day > MonthDays [ month ] ) {
return - 2 ; // error
}
/****************Calculation of t***************************/
for ( i = 1 ; i < = month ; i + + ) {
temp + = MonthDays [ i - 1 ] ;
}
temp + = day ;
2011-01-10 21:30:47 +01:00
2013-05-19 16:37:30 +02:00
decimal_date = year + ( temp - 1 ) / ( 365.0f + ExtraDay ) ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02: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.
{
2013-05-19 16:37:30 +02:00
float CosLat , SinLat , rc , xp , zp ; // all local variables
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
CosLat = cosf ( DEG2RAD ( CoordGeodetic - > phi ) ) ;
SinLat = sinf ( DEG2RAD ( CoordGeodetic - > phi ) ) ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
// compute the local radius of curvature on the WGS-84 reference ellipsoid
rc = Ellip - > a / sqrtf ( 1.0f - Ellip - > epssq * SinLat * SinLat ) ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
// compute ECEF Cartesian coordinates of specified point (for longitude=0)
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
xp = ( rc + CoordGeodetic - > HeightAboveEllipsoid ) * CosLat ;
zp = ( rc * ( 1.0f - Ellip - > epssq ) + CoordGeodetic - > HeightAboveEllipsoid ) * SinLat ;
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
// compute spherical radius and angle lambda and phi of specified point
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
CoordSpherical - > r = sqrtf ( xp * xp + zp * zp ) ;
CoordSpherical - > phig = RAD2DEG ( asinf ( zp / CoordSpherical - > r ) ) ; // geocentric latitude
CoordSpherical - > lambda = CoordGeodetic - > lambda ; // longitude
2010-09-21 21:29:35 +02:00
2013-05-19 16:37:30 +02:00
return 0 ; // OK
2011-01-11 12:11:23 +01:00
}