File Coverage

palsrc/palRefro.c
Criterion Covered Total %
statement 83 83 100.0
branch 61 88 69.3
condition n/a
subroutine n/a
pod n/a
total 144 171 84.2


line stmt bran cond sub pod time code
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
180              
181             #include "pal.h"
182             #include "pal1.h"
183             #include "palmac.h"
184              
185 25           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 25           zobs1 = palDrange(zobs);
230 25 50         zobs2 = DMIN(fabs(zobs1),D93);
231              
232             /* keep other arguments within safe bounds. */
233 25 50         hmok = DMIN(DMAX(hm,-1e3),HS);
    50          
    50          
234 25 50         tdkok = DMIN(DMAX(tdk,100.0),500.0);
    50          
    50          
235 25 50         pmbok = DMIN(DMAX(pmb,0.0),10000.0);
    50          
    50          
236 25 50         rhok = DMIN(DMAX(rh,0.0),1.0);
    50          
    50          
237 25 50         wlok = DMAX(wl,0.1);
238 25 50         alpha = DMIN(DMAX(fabs(tlr),0.001),0.01);
    50          
    50          
239              
240             /* tolerance for iteration. */
241 25 100         tol = DMIN(DMAX(fabs(eps),1e-12),0.1)/2.0;
    50          
    100          
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 25           wlsq = wlok*wlok;
248 25           gb = 9.784*(1.0-0.0026*cos(phi+phi)-0.00000028*hmok);
249 25 100         if (optic) {
250 20           a = (287.6155+(1.62887+0.01360/wlsq)/wlsq) * 273.15e-6/1013.25;
251             } else {
252             a = 77.6890e-6;
253             }
254 25           gamal = (gb*DMD)/GCR;
255 25           gamma = gamal/alpha;
256 25           gamm2 = gamma-2.0;
257             delm2 = DELTA-2.0;
258 25           tdc = tdkok-273.15;
259 50           psat = pow(10.0,(0.7859+0.03477*tdc)/(1.0+0.00412*tdc)) *
260 25           (1.0+pmbok*(4.5e-6+6.0e-10*tdc*tdc));
261 25 50         if (pmbok > 0.0) {
262 25           pwo = rhok*psat/(1.0-(1.0-rhok)*psat/pmbok);
263             } else {
264             pwo = 0.0;
265             }
266 25           w = pwo*(1.0-DMW/DMD)*gamma/(DELTA-gamma);
267 25           c1 = a*(pmbok+w)/tdkok;
268 25 100         if (optic) {
269 20           c2 = (a*w+11.2684e-6*pwo)/tdkok;
270             } else {
271 5           c2 = (a*w+6.3938e-6*pwo)/tdkok;
272             }
273 25           c3 = (gamma-1.0)*alpha*c1/tdkok;
274 25           c4 = (DELTA-1.0)*alpha*c2/tdkok;
275 25 100         if (optic) {
276             c5 = 0.0;
277             c6 = 0.0;
278             } else {
279 5           c5 = 375463e-6*pwo/tdkok;
280 5           c6 = c5*delm2*alpha/(tdkok*tdkok);
281             }
282              
283             /* conditions at the observer. */
284 25           r0 = S+hmok;
285 25           pal1Atmt(r0,tdkok,alpha,gamm2,delm2,c1,c2,c3,c4,c5,c6,
286             r0,&tempo,&dn0,&rdndr0);
287 25           sk0 = dn0*r0*sin(zobs2);
288 25           f0 = refi(dn0,rdndr0);
289              
290             /* conditions in the troposphere at the tropopause. */
291 25 50         rt = S+DMAX(HT,hmok);
292 25           pal1Atmt(r0,tdkok,alpha,gamm2,delm2,c1,c2,c3,c4,c5,c6,
293             rt,&tt,&dnt,&rdndrt);
294 25           sine = sk0/(rt*dnt);
295 25 50         zt = atan2(sine,sqrt(DMAX(1.0-sine*sine,0.0)));
296 25           ft = refi(dnt,rdndrt);
297              
298             /* conditions in the stratosphere at the tropopause. */
299 25           pal1Atms(rt,tt,dnt,gamal,rt,&dnts,&rdndrp);
300 25           sine = sk0/(rt*dnts);
301 25 50         zts = atan2(sine,sqrt(DMAX(1.0-sine*sine,0.0)));
302 25           fts = refi(dnts,rdndrp);
303              
304             /* conditions at the stratosphere limit. */
305             rs = S+HS;
306 25           pal1Atms(rt,tt,dnt,gamal,rs,&dns,&rdndrs);
307 25           sine = sk0/(rs*dns);
308 25 50         zs = atan2(sine,sqrt(DMAX(1.0-sine*sine,0.0)));
309 25           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 75 100         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 50 100         if (k==1) {
327             z0 = zobs2;
328 25           zrange = zt-z0;
329             fb = f0;
330             ff = ft;
331             } else {
332             z0 = zts;
333 25           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 289 100         while (loop) {
348              
349             /* strip width. */
350 239           h = zrange/((double)is);
351              
352             /* initialize distance from earth centre for quadrature pass. */
353 239 100         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 14461 100         for (i=1; i
361              
362             /* sine of observed zenith distance. */
363 14222           sz = sin(z0+h*(double)(i));
364              
365             /* find r (to the nearest metre, maximum four iterations). */
366 14222 50         if (sz > 1e-20) {
367 14222           w = sk0/sz;
368             rg = r;
369             dr = 1.0e6;
370             j = 0;
371 44274 100         while ( fabs(dr) > 1.0 && j < 4 ) {
    50          
372 30052           j++;
373 30052 100         if (k==1) {
374 3662           pal1Atmt(r0,tdkok,alpha,gamm2,delm2,
375             c1,c2,c3,c4,c5,c6,rg,&tg,&dn,&rdndr);
376             } else {
377 26390           pal1Atms(rt,tt,dnt,gamal,rg,&dn,&rdndr);
378             }
379 30052           dr = (rg*dn-w)/(dn+rdndr);
380 30052           rg = rg-dr;
381             }
382             r = rg;
383             }
384              
385             /* find the refractive index and integrand at r. */
386 14222 100         if (k==1) {
387 1575           pal1Atmt(r0,tdkok,alpha,gamm2,delm2,
388             c1,c2,c3,c4,c5,c6,r,&t,&dn,&rdndr);
389             } else {
390 12647           pal1Atms(rt,tt,dnt,gamal,r,&dn,&rdndr);
391             }
392 14222           f = refi(dn,rdndr);
393              
394             /* accumulate odd and (first time only) even values. */
395 14222 100         if (n==1 && i%2 == 0) {
    100          
396 150           fe += f;
397             } else {
398 14072           fo += f;
399             }
400             }
401              
402             /* evaluate the integrand using simpson's rule. */
403 239           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 239 100         if (fabs(refp-refold) > tol && is < ISMAX) {
    50          
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 189           is += is;
415              
416             /* sum of all current values = sum of next pass's even values. */
417 189           fe += fo;
418              
419             /* prepare for new odd values. */
420             fo = 0.0;
421              
422             /* skip even values next time. */
423 189           n = 2;
424             } else {
425              
426             /* yes: save troposphere component and terminate the loop. */
427 239 100         if (k==1) reft = refp;
428             loop = 0;
429             }
430             }
431             }
432              
433             /* result. */
434 25           *ref = reft+refp;
435 25 50         if (zobs1 < 0.0) *ref = -(*ref);
436              
437 25           }