1 | /*
|
---|
2 | *+
|
---|
3 | * Name:
|
---|
4 | * palRefro
|
---|
5 |
|
---|
6 | * Purpose:
|
---|
7 | * Atmospheric refraction for radio and optical/IR wavelengths
|
---|
8 |
|
---|
9 | * Language:
|
---|
10 | * Starlink ANSI C
|
---|
11 |
|
---|
12 | * Type of Module:
|
---|
13 | * Library routine
|
---|
14 |
|
---|
15 | * Invocation:
|
---|
16 | * void palRefro( double zobs, double hm, double tdk, double pmb,
|
---|
17 | * double rh, double wl, double phi, double tlr,
|
---|
18 | * double eps, double * ref ) {
|
---|
19 |
|
---|
20 | * Arguments:
|
---|
21 | * zobs = double (Given)
|
---|
22 | * Observed zenith distance of the source (radian)
|
---|
23 | * hm = double (Given)
|
---|
24 | * Height of the observer above sea level (metre)
|
---|
25 | * tdk = double (Given)
|
---|
26 | * Ambient temperature at the observer (K)
|
---|
27 | * pmb = double (Given)
|
---|
28 | * Pressure at the observer (millibar)
|
---|
29 | * rh = double (Given)
|
---|
30 | * Relative humidity at the observer (range 0-1)
|
---|
31 | * wl = double (Given)
|
---|
32 | * Effective wavelength of the source (micrometre)
|
---|
33 | * phi = double (Given)
|
---|
34 | * Latitude of the observer (radian, astronomical)
|
---|
35 | * tlr = double (Given)
|
---|
36 | * Temperature lapse rate in the troposphere (K/metre)
|
---|
37 | * eps = double (Given)
|
---|
38 | * Precision required to terminate iteration (radian)
|
---|
39 | * ref = double * (Returned)
|
---|
40 | * Refraction: in vacuao ZD minus observed ZD (radian)
|
---|
41 |
|
---|
42 | * Description:
|
---|
43 | * Calculates the atmospheric refraction for radio and optical/IR
|
---|
44 | * wavelengths.
|
---|
45 |
|
---|
46 | * Authors:
|
---|
47 | * PTW: Patrick T. Wallace
|
---|
48 | * TIMJ: Tim Jenness (JAC, Hawaii)
|
---|
49 | * {enter_new_authors_here}
|
---|
50 |
|
---|
51 | * Notes:
|
---|
52 | * - A suggested value for the TLR argument is 0.0065. The
|
---|
53 | * refraction is significantly affected by TLR, and if studies
|
---|
54 | * of the local atmosphere have been carried out a better TLR
|
---|
55 | * value may be available. The sign of the supplied TLR value
|
---|
56 | * is ignored.
|
---|
57 | *
|
---|
58 | * - A suggested value for the EPS argument is 1E-8. The result is
|
---|
59 | * usually at least two orders of magnitude more computationally
|
---|
60 | * precise than the supplied EPS value.
|
---|
61 | *
|
---|
62 | * - The routine computes the refraction for zenith distances up
|
---|
63 | * to and a little beyond 90 deg using the method of Hohenkerk
|
---|
64 | * and Sinclair (NAO Technical Notes 59 and 63, subsequently adopted
|
---|
65 | * in the Explanatory Supplement, 1992 edition - see section 3.281).
|
---|
66 | *
|
---|
67 | * - The code is a development of the optical/IR refraction subroutine
|
---|
68 | * AREF of C.Hohenkerk (HMNAO, September 1984), with extensions to
|
---|
69 | * support the radio case. Apart from merely cosmetic changes, the
|
---|
70 | * following modifications to the original HMNAO optical/IR refraction
|
---|
71 | * code have been made:
|
---|
72 | *
|
---|
73 | * . The angle arguments have been changed to radians.
|
---|
74 | *
|
---|
75 | * . Any value of ZOBS is allowed (see note 6, below).
|
---|
76 | *
|
---|
77 | * . Other argument values have been limited to safe values.
|
---|
78 | *
|
---|
79 | * . Murray's values for the gas constants have been used
|
---|
80 | * (Vectorial Astrometry, Adam Hilger, 1983).
|
---|
81 | *
|
---|
82 | * . The numerical integration phase has been rearranged for
|
---|
83 | * extra clarity.
|
---|
84 | *
|
---|
85 | * . A better model for Ps(T) has been adopted (taken from
|
---|
86 | * Gill, Atmosphere-Ocean Dynamics, Academic Press, 1982).
|
---|
87 | *
|
---|
88 | * . More accurate expressions for Pwo have been adopted
|
---|
89 | * (again from Gill 1982).
|
---|
90 | *
|
---|
91 | * . The formula for the water vapour pressure, given the
|
---|
92 | * saturation pressure and the relative humidity, is from
|
---|
93 | * Crane (1976), expression 2.5.5.
|
---|
94 |
|
---|
95 | * . Provision for radio wavelengths has been added using
|
---|
96 | * expressions devised by A.T.Sinclair, RGO (private
|
---|
97 | * communication 1989). The refractivity model currently
|
---|
98 | * used is from J.M.Rueger, "Refractive Index Formulae for
|
---|
99 | * Electronic Distance Measurement with Radio and Millimetre
|
---|
100 | * Waves", in Unisurv Report S-68 (2002), School of Surveying
|
---|
101 | * and Spatial Information Systems, University of New South
|
---|
102 | * Wales, Sydney, Australia.
|
---|
103 | *
|
---|
104 | * . The optical refractivity for dry air is from Resolution 3 of
|
---|
105 | * the International Association of Geodesy adopted at the XXIIth
|
---|
106 | * General Assembly in Birmingham, UK, 1999.
|
---|
107 | *
|
---|
108 | * . Various small changes have been made to gain speed.
|
---|
109 | *
|
---|
110 | * - The radio refraction is chosen by specifying WL > 100 micrometres.
|
---|
111 | * Because the algorithm takes no account of the ionosphere, the
|
---|
112 | * accuracy deteriorates at low frequencies, below about 30 MHz.
|
---|
113 | *
|
---|
114 | * - Before use, the value of ZOBS is expressed in the range +/- pi.
|
---|
115 | * If this ranged ZOBS is -ve, the result REF is computed from its
|
---|
116 | * absolute value before being made -ve to match. In addition, if
|
---|
117 | * it has an absolute value greater than 93 deg, a fixed REF value
|
---|
118 | * equal to the result for ZOBS = 93 deg is returned, appropriately
|
---|
119 | * signed.
|
---|
120 | *
|
---|
121 | * - As in the original Hohenkerk and Sinclair algorithm, fixed values
|
---|
122 | * of the water vapour polytrope exponent, the height of the
|
---|
123 | * tropopause, and the height at which refraction is negligible are
|
---|
124 | * used.
|
---|
125 | *
|
---|
126 | * - The radio refraction has been tested against work done by
|
---|
127 | * Iain Coulson, JACH, (private communication 1995) for the
|
---|
128 | * James Clerk Maxwell Telescope, Mauna Kea. For typical conditions,
|
---|
129 | * agreement at the 0.1 arcsec level is achieved for moderate ZD,
|
---|
130 | * worsening to perhaps 0.5-1.0 arcsec at ZD 80 deg. At hot and
|
---|
131 | * humid sea-level sites the accuracy will not be as good.
|
---|
132 | *
|
---|
133 | * - It should be noted that the relative humidity RH is formally
|
---|
134 | * defined in terms of "mixing ratio" rather than pressures or
|
---|
135 | * densities as is often stated. It is the mass of water per unit
|
---|
136 | * mass of dry air divided by that for saturated air at the same
|
---|
137 | * temperature and pressure (see Gill 1982).
|
---|
138 |
|
---|
139 | * - The algorithm is designed for observers in the troposphere. The
|
---|
140 | * supplied temperature, pressure and lapse rate are assumed to be
|
---|
141 | * for a point in the troposphere and are used to define a model
|
---|
142 | * atmosphere with the tropopause at 11km altitude and a constant
|
---|
143 | * temperature above that. However, in practice, the refraction
|
---|
144 | * values returned for stratospheric observers, at altitudes up to
|
---|
145 | * 25km, are quite usable.
|
---|
146 |
|
---|
147 | * History:
|
---|
148 | * 2012-08-24 (TIMJ):
|
---|
149 | * Initial version, direct port of SLA Fortran source.
|
---|
150 | * Adapted with permission from the Fortran SLALIB library.
|
---|
151 | * {enter_further_changes_here}
|
---|
152 |
|
---|
153 | * Copyright:
|
---|
154 | * Copyright (C) 2005 Patrick T. Wallace
|
---|
155 | * Copyright (C) 2012 Science and Technology Facilities Council.
|
---|
156 | * All Rights Reserved.
|
---|
157 |
|
---|
158 | * Licence:
|
---|
159 | * This program is free software; you can redistribute it and/or
|
---|
160 | * modify it under the terms of the GNU General Public License as
|
---|
161 | * published by the Free Software Foundation; either version 3 of
|
---|
162 | * the License, or (at your option) any later version.
|
---|
163 | *
|
---|
164 | * This program is distributed in the hope that it will be
|
---|
165 | * useful, but WITHOUT ANY WARRANTY; without even the implied
|
---|
166 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
---|
167 | * PURPOSE. See the GNU General Public License for more details.
|
---|
168 | *
|
---|
169 | * You should have received a copy of the GNU General Public License
|
---|
170 | * along with this program; if not, write to the Free Software
|
---|
171 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
---|
172 | * MA 02110-1301, USA.
|
---|
173 |
|
---|
174 | * Bugs:
|
---|
175 | * {note_any_bugs_here}
|
---|
176 | *-
|
---|
177 | */
|
---|
178 |
|
---|
179 | #include <math.h>
|
---|
180 |
|
---|
181 | #include "pal.h"
|
---|
182 | #include "pal1.h"
|
---|
183 | #include "palmac.h"
|
---|
184 |
|
---|
185 | void palRefro( double zobs, double hm, double tdk, double pmb,
|
---|
186 | double rh, double wl, double phi, double tlr,
|
---|
187 | double eps, double * ref ) {
|
---|
188 |
|
---|
189 | /*
|
---|
190 | * Fixed parameters
|
---|
191 | */
|
---|
192 |
|
---|
193 | /* 93 degrees in radians */
|
---|
194 | const double D93 = 1.623156204;
|
---|
195 | /* Universal gas constant */
|
---|
196 | const double GCR = 8314.32;
|
---|
197 | /* Molecular weight of dry air */
|
---|
198 | const double DMD = 28.9644;
|
---|
199 | /* Molecular weight of water vapour */
|
---|
200 | const double DMW = 18.0152;
|
---|
201 | /* Mean Earth radius (metre) */
|
---|
202 | const double S = 6378120.;
|
---|
203 | /* Exponent of temperature dependence of water vapour pressure */
|
---|
204 | const double DELTA = 18.36;
|
---|
205 | /* Height of tropopause (metre) */
|
---|
206 | const double HT = 11000.;
|
---|
207 | /* Upper limit for refractive effects (metre) */
|
---|
208 | const double HS = 80000.;
|
---|
209 | /* Numerical integration: maximum number of strips. */
|
---|
210 | const int ISMAX=16384l;
|
---|
211 |
|
---|
212 | /* Local variables */
|
---|
213 | int is, k, n, i, j;
|
---|
214 |
|
---|
215 | int optic, loop; /* booleans */
|
---|
216 |
|
---|
217 | double zobs1,zobs2,hmok,tdkok,pmbok,rhok,wlok,alpha,
|
---|
218 | tol,wlsq,gb,a,gamal,gamma,gamm2,delm2,
|
---|
219 | tdc,psat,pwo,w,
|
---|
220 | c1,c2,c3,c4,c5,c6,r0,tempo,dn0,rdndr0,sk0,f0,
|
---|
221 | rt,tt,dnt,rdndrt,sine,zt,ft,dnts,rdndrp,zts,fts,
|
---|
222 | rs,dns,rdndrs,zs,fs,refold,z0,zrange,fb,ff,fo,fe,
|
---|
223 | h,r,sz,rg,dr,tg,dn,rdndr,t,f,refp,reft;
|
---|
224 |
|
---|
225 | /* The refraction integrand */
|
---|
226 | #define refi(DN,RDNDR) RDNDR/(DN+RDNDR)
|
---|
227 |
|
---|
228 | /* Transform ZOBS into the normal range. */
|
---|
229 | zobs1 = palDrange(zobs);
|
---|
230 | zobs2 = DMIN(fabs(zobs1),D93);
|
---|
231 |
|
---|
232 | /* keep other arguments within safe bounds. */
|
---|
233 | hmok = DMIN(DMAX(hm,-1e3),HS);
|
---|
234 | tdkok = DMIN(DMAX(tdk,100.0),500.0);
|
---|
235 | pmbok = DMIN(DMAX(pmb,0.0),10000.0);
|
---|
236 | rhok = DMIN(DMAX(rh,0.0),1.0);
|
---|
237 | wlok = DMAX(wl,0.1);
|
---|
238 | alpha = DMIN(DMAX(fabs(tlr),0.001),0.01);
|
---|
239 |
|
---|
240 | /* tolerance for iteration. */
|
---|
241 | tol = DMIN(DMAX(fabs(eps),1e-12),0.1)/2.0;
|
---|
242 |
|
---|
243 | /* decide whether optical/ir or radio case - switch at 100 microns. */
|
---|
244 | optic = wlok < 100.0;
|
---|
245 |
|
---|
246 | /* set up model atmosphere parameters defined at the observer. */
|
---|
247 | wlsq = wlok*wlok;
|
---|
248 | gb = 9.784*(1.0-0.0026*cos(phi+phi)-0.00000028*hmok);
|
---|
249 | if (optic) {
|
---|
250 | a = (287.6155+(1.62887+0.01360/wlsq)/wlsq) * 273.15e-6/1013.25;
|
---|
251 | } else {
|
---|
252 | a = 77.6890e-6;
|
---|
253 | }
|
---|
254 | gamal = (gb*DMD)/GCR;
|
---|
255 | gamma = gamal/alpha;
|
---|
256 | gamm2 = gamma-2.0;
|
---|
257 | delm2 = DELTA-2.0;
|
---|
258 | tdc = tdkok-273.15;
|
---|
259 | psat = pow(10.0,(0.7859+0.03477*tdc)/(1.0+0.00412*tdc)) *
|
---|
260 | (1.0+pmbok*(4.5e-6+6.0e-10*tdc*tdc));
|
---|
261 | if (pmbok > 0.0) {
|
---|
262 | pwo = rhok*psat/(1.0-(1.0-rhok)*psat/pmbok);
|
---|
263 | } else {
|
---|
264 | pwo = 0.0;
|
---|
265 | }
|
---|
266 | w = pwo*(1.0-DMW/DMD)*gamma/(DELTA-gamma);
|
---|
267 | c1 = a*(pmbok+w)/tdkok;
|
---|
268 | if (optic) {
|
---|
269 | c2 = (a*w+11.2684e-6*pwo)/tdkok;
|
---|
270 | } else {
|
---|
271 | c2 = (a*w+6.3938e-6*pwo)/tdkok;
|
---|
272 | }
|
---|
273 | c3 = (gamma-1.0)*alpha*c1/tdkok;
|
---|
274 | c4 = (DELTA-1.0)*alpha*c2/tdkok;
|
---|
275 | if (optic) {
|
---|
276 | c5 = 0.0;
|
---|
277 | c6 = 0.0;
|
---|
278 | } else {
|
---|
279 | c5 = 375463e-6*pwo/tdkok;
|
---|
280 | c6 = c5*delm2*alpha/(tdkok*tdkok);
|
---|
281 | }
|
---|
282 |
|
---|
283 | /* conditions at the observer. */
|
---|
284 | r0 = S+hmok;
|
---|
285 | pal1Atmt(r0,tdkok,alpha,gamm2,delm2,c1,c2,c3,c4,c5,c6,
|
---|
286 | r0,&tempo,&dn0,&rdndr0);
|
---|
287 | sk0 = dn0*r0*sin(zobs2);
|
---|
288 | f0 = refi(dn0,rdndr0);
|
---|
289 |
|
---|
290 | /* conditions in the troposphere at the tropopause. */
|
---|
291 | rt = S+DMAX(HT,hmok);
|
---|
292 | pal1Atmt(r0,tdkok,alpha,gamm2,delm2,c1,c2,c3,c4,c5,c6,
|
---|
293 | rt,&tt,&dnt,&rdndrt);
|
---|
294 | sine = sk0/(rt*dnt);
|
---|
295 | zt = atan2(sine,sqrt(DMAX(1.0-sine*sine,0.0)));
|
---|
296 | ft = refi(dnt,rdndrt);
|
---|
297 |
|
---|
298 | /* conditions in the stratosphere at the tropopause. */
|
---|
299 | pal1Atms(rt,tt,dnt,gamal,rt,&dnts,&rdndrp);
|
---|
300 | sine = sk0/(rt*dnts);
|
---|
301 | zts = atan2(sine,sqrt(DMAX(1.0-sine*sine,0.0)));
|
---|
302 | fts = refi(dnts,rdndrp);
|
---|
303 |
|
---|
304 | /* conditions at the stratosphere limit. */
|
---|
305 | rs = S+HS;
|
---|
306 | pal1Atms(rt,tt,dnt,gamal,rs,&dns,&rdndrs);
|
---|
307 | sine = sk0/(rs*dns);
|
---|
308 | zs = atan2(sine,sqrt(DMAX(1.0-sine*sine,0.0)));
|
---|
309 | fs = refi(dns,rdndrs);
|
---|
310 |
|
---|
311 | /* variable initialization to avoid compiler warning. */
|
---|
312 | reft = 0.0;
|
---|
313 |
|
---|
314 | /* integrate the refraction integral in two parts; first in the
|
---|
315 | * troposphere (k=1), then in the stratosphere (k=2). */
|
---|
316 |
|
---|
317 | for (k=1; k<=2; k++) {
|
---|
318 |
|
---|
319 | /* initialize previous refraction to ensure at least two iterations. */
|
---|
320 | refold = 1.0;
|
---|
321 |
|
---|
322 | /* start off with 8 strips. */
|
---|
323 | is = 8;
|
---|
324 |
|
---|
325 | /* start z, z range, and start and end values. */
|
---|
326 | if (k==1) {
|
---|
327 | z0 = zobs2;
|
---|
328 | zrange = zt-z0;
|
---|
329 | fb = f0;
|
---|
330 | ff = ft;
|
---|
331 | } else {
|
---|
332 | z0 = zts;
|
---|
333 | zrange = zs-z0;
|
---|
334 | fb = fts;
|
---|
335 | ff = fs;
|
---|
336 | }
|
---|
337 |
|
---|
338 | /* sums of odd and even values. */
|
---|
339 | fo = 0.0;
|
---|
340 | fe = 0.0;
|
---|
341 |
|
---|
342 | /* first time through the loop we have to do every point. */
|
---|
343 | n = 1;
|
---|
344 |
|
---|
345 | /* start of iteration loop (terminates at specified precision). */
|
---|
346 | loop = 1;
|
---|
347 | while (loop) {
|
---|
348 |
|
---|
349 | /* strip width. */
|
---|
350 | h = zrange/((double)is);
|
---|
351 |
|
---|
352 | /* initialize distance from earth centre for quadrature pass. */
|
---|
353 | if (k == 1) {
|
---|
354 | r = r0;
|
---|
355 | } else {
|
---|
356 | r = rt;
|
---|
357 | }
|
---|
358 |
|
---|
359 | /* one pass (no need to compute evens after first time). */
|
---|
360 | for (i=1; i<is; i+=n) {
|
---|
361 |
|
---|
362 | /* sine of observed zenith distance. */
|
---|
363 | sz = sin(z0+h*(double)(i));
|
---|
364 |
|
---|
365 | /* find r (to the nearest metre, maximum four iterations). */
|
---|
366 | if (sz > 1e-20) {
|
---|
367 | w = sk0/sz;
|
---|
368 | rg = r;
|
---|
369 | dr = 1.0e6;
|
---|
370 | j = 0;
|
---|
371 | while ( fabs(dr) > 1.0 && j < 4 ) {
|
---|
372 | j++;
|
---|
373 | if (k==1) {
|
---|
374 | pal1Atmt(r0,tdkok,alpha,gamm2,delm2,
|
---|
375 | c1,c2,c3,c4,c5,c6,rg,&tg,&dn,&rdndr);
|
---|
376 | } else {
|
---|
377 | pal1Atms(rt,tt,dnt,gamal,rg,&dn,&rdndr);
|
---|
378 | }
|
---|
379 | dr = (rg*dn-w)/(dn+rdndr);
|
---|
380 | rg = rg-dr;
|
---|
381 | }
|
---|
382 | r = rg;
|
---|
383 | }
|
---|
384 |
|
---|
385 | /* find the refractive index and integrand at r. */
|
---|
386 | if (k==1) {
|
---|
387 | pal1Atmt(r0,tdkok,alpha,gamm2,delm2,
|
---|
388 | c1,c2,c3,c4,c5,c6,r,&t,&dn,&rdndr);
|
---|
389 | } else {
|
---|
390 | pal1Atms(rt,tt,dnt,gamal,r,&dn,&rdndr);
|
---|
391 | }
|
---|
392 | f = refi(dn,rdndr);
|
---|
393 |
|
---|
394 | /* accumulate odd and (first time only) even values. */
|
---|
395 | if (n==1 && i%2 == 0) {
|
---|
396 | fe += f;
|
---|
397 | } else {
|
---|
398 | fo += f;
|
---|
399 | }
|
---|
400 | }
|
---|
401 |
|
---|
402 | /* evaluate the integrand using simpson's rule. */
|
---|
403 | refp = h*(fb+4.0*fo+2.0*fe+ff)/3.0;
|
---|
404 |
|
---|
405 | /* has the required precision been achieved (or can't be)? */
|
---|
406 | if (fabs(refp-refold) > tol && is < ISMAX) {
|
---|
407 |
|
---|
408 | /* no: prepare for next iteration.*/
|
---|
409 |
|
---|
410 | /* save current value for convergence test. */
|
---|
411 | refold = refp;
|
---|
412 |
|
---|
413 | /* double the number of strips. */
|
---|
414 | is += is;
|
---|
415 |
|
---|
416 | /* sum of all current values = sum of next pass's even values. */
|
---|
417 | fe += fo;
|
---|
418 |
|
---|
419 | /* prepare for new odd values. */
|
---|
420 | fo = 0.0;
|
---|
421 |
|
---|
422 | /* skip even values next time. */
|
---|
423 | n = 2;
|
---|
424 | } else {
|
---|
425 |
|
---|
426 | /* yes: save troposphere component and terminate the loop. */
|
---|
427 | if (k==1) reft = refp;
|
---|
428 | loop = 0;
|
---|
429 | }
|
---|
430 | }
|
---|
431 | }
|
---|
432 |
|
---|
433 | /* result. */
|
---|
434 | *ref = reft+refp;
|
---|
435 | if (zobs1 < 0.0) *ref = -(*ref);
|
---|
436 |
|
---|
437 | }
|
---|