5 /* Copyright (C) 2006 Martin Budaj
7 * based on GPL-licensed code by
8 * Copyright (C) 2000 Edward A Williams <Ed_Williams@compuserve.com>
10 * --------------------------------------------------------------------
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * --------------------------------------------------------------------
30 #include "thgeomagdata.h"
32 #define max(a,b) (((a) > (b)) ? (a) : (b))
39 #define nmax thgeomag_maxdeg
40 #define nmaxl thgeomag_maxdeg
42 #define pi 3.14159265358979
44 #define b 6356.7523142
47 double thgeomag(double lat
, double lon
, double h
, double dat
) {
51 static double P
[nmax
+1][nmax
+1];
52 static double DP
[nmax
+1][nmax
+1];
53 static double gnm
[nmax
+1][nmax
+1];
54 static double hnm
[nmax
+1][nmax
+1];
55 static double sm
[nmax
+1];
56 static double cm
[nmax
+1];
58 static double root
[nmax
+1];
59 static double roots
[nmax
+1][nmax
+1][2];
62 double yearfrac
,sr
,r
,theta
,c
,s
,psi
,fn
,fn_0
,B_r
,B_theta
,B_phi
,X
,Y
; /* Z */
63 double sinpsi
, cospsi
, inv_s
;
65 static int been_here
= 0;
67 double sinlat
= sin(lat
);
68 double coslat
= cos(lat
);
72 /* convert to geocentric */
73 sr
= sqrt(a
*a
*coslat
*coslat
+ b
*b
*sinlat
*sinlat
);
74 /* sr is effective radius */
75 theta
= atan2(coslat
* (h
*sr
+ a
*a
), sinlat
* (h
*sr
+ b
*b
));
77 /* theta is geocentric co-latitude */
79 r
= h
*h
+ 2.0*h
* sr
+
80 (a
*a
*a
*a
- ( a
*a
*a
*a
- b
*b
*b
*b
) * sinlat
*sinlat
) /
81 (a
*a
- (a
*a
- b
*b
) * sinlat
*sinlat
);
85 /* r is geocentric radial distance */
88 /* protect against zero divide at geographic poles */
89 inv_s
= 1.0 / (s
+ (s
== 0.)*1.0e-8);
92 for ( n
= 0; n
<= nmax
; n
++ ) {
93 for ( m
= 0; m
<= n
; m
++ ) {
99 /* diagonal elements */
107 /* these values will not change for subsequent function calls */
109 for ( n
= 2; n
<= nmax
; n
++ ) {
110 root
[n
] = sqrt((2.0*n
-1) / (2.0*n
));
113 for ( m
= 0; m
<= nmax
; m
++ ) {
115 for ( n
= max(m
+ 1, 2); n
<= nmax
; n
++ ) {
116 roots
[m
][n
][0] = sqrt((n
-1)*(n
-1) - mm
);
117 roots
[m
][n
][1] = 1.0 / sqrt( n
*n
- mm
);
123 for ( n
=2; n
<= nmax
; n
++ ) {
124 /* double root = sqrt((2.0*n-1) / (2.0*n)); */
125 P
[n
][n
] = P
[n
-1][n
-1] * s
* root
[n
];
126 DP
[n
][n
] = (DP
[n
-1][n
-1] * s
+ P
[n
-1][n
-1] * c
) * root
[n
];
130 for ( m
= 0; m
<= nmax
; m
++ ) {
131 /* double mm = m*m; */
132 for ( n
= max(m
+ 1, 2); n
<= nmax
; n
++ ) {
133 /* double root1 = sqrt((n-1)*(n-1) - mm); */
134 /* double root2 = 1.0 / sqrt( n*n - mm); */
135 P
[n
][m
] = (P
[n
-1][m
] * c
* (2.0*n
-1) -
136 P
[n
-2][m
] * roots
[m
][n
][0]) * roots
[m
][n
][1];
137 DP
[n
][m
] = ((DP
[n
-1][m
] * c
- P
[n
-1][m
] * s
) *
138 (2.0*n
-1) - DP
[n
-2][m
] * roots
[m
][n
][0]) * roots
[m
][n
][1];
142 /* compute gnm, hnm at dat */
144 int mindex
= (int)((dat
- thgeomag_minyear
) / thgeomag_step
);
145 if (mindex
< 0) mindex
= 0;
146 if (mindex
> thgeomag_maxmindex
) mindex
= thgeomag_maxmindex
;
147 yearfrac
= dat
- thgeomag_step
*mindex
- thgeomag_minyear
;
149 for (n
=1;n
<=nmaxl
;n
++) {
150 for (m
= 0;m
<=nmaxl
;m
++) {
151 if (mindex
== thgeomag_maxmindex
) {
152 gnm
[n
][m
] = thgeomag_GNM
[mindex
][n
][m
] + yearfrac
* thgeomag_GNMD
[n
][m
];
153 hnm
[n
][m
] = thgeomag_HNM
[mindex
][n
][m
] + yearfrac
* thgeomag_HNMD
[n
][m
];
155 gnm
[n
][m
] = thgeomag_GNM
[mindex
][n
][m
] + yearfrac
/ thgeomag_step
* (thgeomag_GNM
[mindex
+1][n
][m
] - thgeomag_GNM
[mindex
][n
][m
]);
156 hnm
[n
][m
] = thgeomag_HNM
[mindex
][n
][m
] + yearfrac
/ thgeomag_step
* (thgeomag_HNM
[mindex
+1][n
][m
] - thgeomag_HNM
[mindex
][n
][m
]);
161 /* compute sm (sin(m lon) and cm (cos(m lon)) */
162 for (m
= 0;m
<=nmaxl
;m
++) {
163 sm
[m
] = sin(m
* lon
);
164 cm
[m
] = cos(m
* lon
);
167 /* compute B fields */
174 for ( n
= 1; n
<= nmaxl
; n
++ ) {
178 for ( m
= 0; m
<= n
; m
++ ) {
179 double tmp
= (gnm
[n
][m
] * cm
[m
] + hnm
[n
][m
] * sm
[m
]);
180 c1_n
+= tmp
* P
[n
][m
];
181 c2_n
+= tmp
* DP
[n
][m
];
182 c3_n
+= m
* (gnm
[n
][m
] * sm
[m
] - hnm
[n
][m
] * cm
[m
]) * P
[n
][m
];
184 /* fn=pow(r_0/r,n+2.0); */
186 B_r
+= (n
+ 1) * c1_n
* fn
;
187 B_theta
-= c2_n
* fn
;
188 B_phi
+= c3_n
* fn
* inv_s
;
193 /* Find geodetic field components: */
194 psi
= theta
- (pi
/ 2.0 - lat
);
197 X
= -B_theta
* cospsi
- B_r
* sinpsi
;
199 /* Z = B_theta * sinpsi - B_r * cospsi; */
206 field[5]=Z;*/ /* output fields */
207 /* find variation in radians */
208 /* return zero variation at magnetic pole X=Y=0. */
215 return (X
!= 0. || Y
!= 0.) ? atan2(Y
, X
) : (double) 0.;