FACT++  1.0
palRefro.c
Go to the documentation of this file.
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 }
double palDrange(double angle)
Definition: palDrange.c:68
void palRefro(double zobs, double hm, double tdk, double pmb, double rh, double wl, double phi, double tlr, double eps, double *ref)
Definition: palRefro.c:185
int i
Definition: db_dim_client.c:21
void pal1Atmt(double r0, double t0, double alpha, double gamm2, double delm2, double c1, double c2, double c3, double c4, double c5, double c6, double r, double *t, double *dn, double *rdndr)
Definition: pal1Atmt.c:103
#define DMIN(A, B)
Definition: palmac.h:129
double w
Definition: palObs.c:168
double h
Definition: palObs.c:170
#define DMAX(A, B)
Definition: palmac.h:126
void pal1Atms(double rt, double tt, double dnt, double gamal, double r, double *dn, double *rdndr)
Definition: pal1Atms.c:83
TT t
Definition: test_client.c:26
#define refi(DN, RDNDR)