1 #include <cstring>
2 
3 extern "C" {
4 #include <grass/gis.h>
5 #include <grass/glocale.h>
6 }
7 
8 #include "common.h"
9 #include "geomcond.h"
10 #include "atmosmodel.h"
11 #include "aerosolmodel.h"
12 #include "aerosolconcentration.h"
13 #include "altitude.h"
14 #include "iwave.h"
15 #include "gauss.h"
16 #include "transform.h"
17 #ifdef WIN32
18 #pragma warning (disable : 4305)
19 #endif
20 
21 struct OpticalAtmosProperties
22 {
23     double rorayl, romix, roaero;
24     double ddirtr, ddiftr;
25     double ddirtt, ddiftt;
26     double ddirta, ddifta;
27     double udirtr, udiftr;
28     double udirtt, udiftt;
29     double udirta, udifta;
30     double sphalbr, sphalbt, sphalba;
31 };
32 
33 /* To compute the molecular optical depth as a function of wavelength for any
34    atmosphere defined by the pressure and temperature profiles. */
odrayl(const AtmosModel & atms,const double wl)35 double odrayl(const AtmosModel &atms, const double wl)
36 {
37     /* air refraction index edlen 1966 / metrologia,2,71-80  putting pw=0 */
38     double ak=1/wl;
39     double awl= wl*wl*wl*wl * 0.0254743;
40     double a1 = 130 - ak * ak;
41     double a2 = 38.9 - ak * ak;
42     double a3 = 2406030 / a1;
43     double a4 = 15997 / a2;
44     double an = (8342.13 + a3 + a4) * 1.0e-08 + 1;
45     double a = (24 * M_PI * M_PI * M_PI) * ((an * an - 1) * (an * an - 1))
46 	* (6 + 3 * delta) / (6 - 7 * delta) / ((an * an + 2) * (an * an + 2));
47 
48     double tray = 0;
49     for(int k = 0; k < 33; k++)
50     {
51 	double dppt = (288.15 / 1013.25) * (atms.p[k] / atms.t[k] + atms.p[k+1] / atms.t[k+1]) / 2;
52 	double sr = a * dppt / awl;
53 	tray += (double)((atms.z[k+1] - atms.z[k]) * sr);
54     }
55 
56     return tray;
57 }
58 
59 /*
60   decompose the aerosol phase function in series of Legendre polynomial used in
61   OS.f and ISO.f and compute truncation coefficient f to modify aerosol optical thickness t and single
62   scattering albedo w0 according to:
63   t' = (1-w0 f) t
64 
65   w0' =  w0 (1- f)
66   --------
67   (1-w0 f)
68 */
trunca()69 double trunca()
70 {
71     double ptemp[83];
72     double cosang[80];
73     double weight[80];
74     double rmu[83];
75     double ga[83];
76 
77     int i;
78     for(i = 0; i < 83; i++) ptemp[i] = sixs_trunc.pha[i];
79 
80     Gauss::gauss(-1,1,cosang,weight,80);
81 
82     for(i = 0; i < 40; i++)
83     {
84 	rmu[i+1] = cosang[i];
85 	ga[i+1] = weight[i];
86     }
87 
88     rmu[0] = -1;
89     ga[0] = 0;
90     rmu[41] = 0;
91     ga[41] = 0;
92 
93     for(i = 40; i < 80; i++)
94     {
95 	rmu[i+2] = cosang[i];
96 	ga[i+2] = weight[i];
97     }
98 
99     rmu[82] = 1;
100     ga[82] = 0;
101 
102     int k = 0;
103     for(i = 0; i < 83; i++)
104     {
105 	if(rmu[i] > 0.8) break;
106 	k = i - 1;
107     }
108 
109     int kk = 0;
110     for(i = 0; i < 83; i++)
111     {
112 	if(rmu[i] > 0.94) break;
113 	kk = i - 1;
114     }
115 
116 
117     double aa = (double)((log10(sixs_trunc.pha[kk]) - log10(sixs_trunc.pha[k])) /
118 		       (acos(rmu[kk]) - acos(rmu[k])));
119     double x1 = (double)(log10(sixs_trunc.pha[kk]));
120     double x2 = (double)acos(rmu[kk]);
121 
122     for(i = kk + 1; i < 83; i++)
123     {
124 	double a;
125 	if(fabs(rmu[i] - 1) <= 1e-08) a = x1 - aa * x2;
126 	else a = x1 + aa * (acos(rmu[i]) - x2);
127 	ptemp[i] = (double)pow(10,a);
128     }
129 
130 
131     for(i = 0; i < 83; i++) sixs_trunc.pha[i] = ptemp[i];
132     for(i = 0; i < 80; i++) sixs_trunc.betal[i] = 0;
133 
134     double pl[83];
135 
136 #define IPL(X) ((X)+1)
137 
138 
139     for(i = 0; i < 83; i++)
140     {
141 	double x = sixs_trunc.pha[i] * ga[i];
142 	double rm = rmu[i];
143 	pl[IPL(-1)] = 0;
144 	pl[IPL(0)] = 1;
145 
146 	for(k = 0; k <= 80; k++)
147 	{
148 	    pl[IPL(k+1)] = ((2 * k + 1) * rm * pl[IPL(k)] - k * pl[IPL(k-1)]) / (k + 1);
149 	    sixs_trunc.betal[k] += x * pl[IPL(k)];
150 	}
151     }
152 
153     for(i = 0; i <= 80; i++) sixs_trunc.betal[i] *= (2 * i + 1) * 0.5f;
154 
155     double z1 = sixs_trunc.betal[0];
156     for(i = 0; i <= 80; i++) sixs_trunc.betal[i] /= z1;
157     if(sixs_trunc.betal[80] < 0) sixs_trunc.betal[80] = 0;
158 
159     return 1 - z1;
160 }
161 
162 
163 /*
164   Decompose the atmosphere in a finite number of layers. For each layer, DISCRE
165   provides the optical thickness, the proportion of molecules and aerosols assuming an exponential
166   distribution for each constituents. Figure 1 illustrate the way molecules and aerosols are mixed in a
167   realistic atmosphere. For molecules, the scale height is 8km. For aerosols it is assumed to be 2km
168   unless otherwise specified by the user (using aircraft measurements).
169 */
170 
discre(const double ta,const double ha,const double tr,const double hr,const int it,const int ntd,const double yy,const double dd,const double ppp2,const double ppp1)171 double discre(const double ta, const double ha, const double tr, const double hr,
172 	     const int it, const int ntd, const double yy, const double dd,
173 	     const double ppp2, const double ppp1)
174 {
175     if( ha >= 7 )
176     {
177 	G_warning(_("Check aerosol measurements or plane altitude"));
178 	return 0;
179     }
180 
181     double dt;
182     if( it == 0 ) dt = 1e-17;
183     else dt = 2 * (ta + tr - yy) / (ntd - it + 1);
184 
185     double zx; /* return value */
186     double ecart = 0;
187     do {
188 	dt = dt / 2;
189 	double ti = yy + dt;
190 	double y1 = ppp2;
191 	double y2;
192 	double y3 = ppp1;
193 
194 	while(true)
195 	{
196 	    y2 = (y1 + y3) * 0.5f;
197 
198 	    double xx = -y2 / ha;
199 	    double x2;
200 	    if (xx < -18) x2 = tr * exp(-y2 / hr);
201 	    else x2 = ta * exp(xx) + tr * exp(-y2 / hr);
202 
203 	    if(fabs(ti - x2) < 0.00001) break;
204 
205 	    if(ti - x2 < 0) y3 = y2;
206 	    else y1 = y2;
207 	}
208 
209 	zx = y2;
210 	double cdelta = (double)(1. / (1 + ta * hr / tr / ha * exp((zx - ppp1) * (1. / hr - 1. / ha))));
211 	if(dd != 0) ecart = (double)fabs((dd - cdelta) / dd);
212     } while((ecart > 0.75) && (it != 0));
213     return zx;
214 
215 }
216 
217 
218 /* indexing macro for the psl variable */
219 #define PSI(X) ((X)+1)
220 /*
221   Compute the values of Legendre polynomials used in the successive order of
222   scattering method.
223 */
kernel(const int is,double (& xpl)[2* mu+1],double (& bp)[26][2* mu+1],Gauss & gauss)224 void kernel(const int is, double (&xpl)[2*mu + 1], double (&bp)[26][2*mu + 1], Gauss &gauss)
225 {
226     const double rac3 = 1.7320508075688772935274463415059;
227 #define PSI(X) ((X)+1)
228     double psl[82][2*mu + 1];
229 
230     if(is == 0)
231     {
232 	for(int j = 0; j <= mu; j++)
233 	{
234 	    psl[PSI(0)][STDI(-j)] = 1;
235 	    psl[PSI(0)][STDI(j)] = 1;
236 	    psl[PSI(1)][STDI(j)] = gauss.rm[STDI(j)];
237 	    psl[PSI(1)][STDI(-j)] = -gauss.rm[STDI(j)];
238 
239 	    double xdb = (3 * gauss.rm[STDI(j)] * gauss.rm[STDI(j)] - 1) * 0.5;
240 	    if(fabs(xdb) < 1e-30) xdb = 0;
241 	    psl[PSI(2)][STDI(-j)] = (double)xdb;
242 	    psl[PSI(2)][STDI(j)] = (double)xdb;
243 	}
244 	psl[PSI(1)][STDI(0)] = gauss.rm[STDI(0)];
245     }
246     else if(is == 1)
247     {
248 	for(int j = 0; j <= mu; j++)
249 	{
250 	    double x = 1 - gauss.rm[STDI(j)] * gauss.rm[STDI(j)];
251 	    psl[PSI(0)][STDI(j)]  = 0;
252 	    psl[PSI(0)][STDI(-j)] = 0;
253 	    psl[PSI(1)][STDI(-j)] = (double)sqrt(x * 0.5);
254 	    psl[PSI(1)][STDI(j)]  = (double)sqrt(x * 0.5);
255 	    psl[PSI(2)][STDI(j)]  = (double)(gauss.rm[STDI(j)] * psl[PSI(1)][STDI(j)] * rac3);
256 	    psl[PSI(2)][STDI(-j)] = -psl[PSI(2)][STDI(j)];
257 
258 	}
259 	psl[PSI(2)][STDI(0)] = -psl[PSI(2)][STDI(0)];
260     }
261     else
262     {
263 	double a = 1;
264 	for(int i = 1; i <= is; i++) a *= sqrt((double)(i + is) / (double)i) * 0.5;
265 /*		double b = a * sqrt((double)is / (is + 1.)) * sqrt((is - 1.) / (is + 2.));*/
266 
267 	for(int j = 0; j <= mu; j++)
268 	{
269 	    double xx = 1 - gauss.rm[STDI(j)] * gauss.rm[STDI(j)];
270 	    psl[PSI(is - 1)][STDI(j)] = 0;
271 	    double xdb = a * pow(xx, is * 0.5);
272 	    if(fabs(xdb) < 1e-30) xdb = 0;
273 	    psl[PSI(is)][STDI(-j)] = (double)xdb;
274 	    psl[PSI(is)][STDI(j)] = (double)xdb;
275 	}
276     }
277 
278     int k = 2;
279     int ip = 80;
280 
281     if(is > 2) k = is;
282     if(k != ip)
283     {
284 	int ig = -1;
285 	if( is == 1 ) ig = 1;
286 	for(int l = k; l < ip; l++)
287 	{
288 	    double a = (2 * l + 1.) / sqrt((l + is + 1.) * (l - is + 1.));
289 	    double b = sqrt(double((l + is) * (l - is))) / (2. * l + 1.);
290 
291 	    for(int j = 0; j <= mu; j++)
292 	    {
293 		double xdb = a * (gauss.rm[STDI(j)] * psl[PSI(l)][STDI(j)] - b * psl[PSI(l-1)][STDI(j)]);
294 		if (fabs(xdb) < 1e-30) xdb = 0;
295 		psl[PSI(l+1)][STDI(j)] = (double)xdb;
296 		if(j != 0) psl[PSI(l+1)][STDI(-j)] = ig * psl[PSI(l+1)][STDI(j)];
297 	    }
298 	    ig = -ig;
299 	}
300     }
301 
302     int j;
303     for(j = -mu; j <= mu; j++) xpl[STDI(j)] = psl[PSI(2)][STDI(j)];
304 
305     for(j = 0; j <= mu; j++)
306     {
307 	for(k = -mu; k <= mu; k++)
308 	{
309 	    double sbp = 0;
310 	    if(is <= 80)
311 	    {
312 		for(int l = is; l <= 80; l++)
313 		    sbp += psl[PSI(l)][STDI(j)] * psl[PSI(l)][STDI(k)] * sixs_trunc.betal[l];
314 
315 		if(fabs(sbp) < 1e-30) sbp = 0;
316 		bp[j][STDI(k)] = (double)sbp;
317 	    }
318 	}
319     }
320 
321 }
322 
323 
324 
325 #define accu 1e-20
326 #define accu2 1e-3
327 #define mum1 (mu - 1)
328 
os(const double tamoy,const double trmoy,const double pizmoy,const double tamoyp,const double trmoyp,double (& xl)[2* mu+1][np],Gauss & gauss,const Altitude & alt,const GeomCond & geom)329 void os(const double tamoy, const double trmoy, const double pizmoy,
330 	const double tamoyp, const double trmoyp,	double (&xl)[2*mu + 1][np],
331         Gauss &gauss, const Altitude &alt, const GeomCond &geom)
332 {
333     double trp = trmoy - trmoyp;
334     double tap = tamoy - tamoyp;
335     int iplane = 0;
336 
337     /* if plane observations recompute scale height for aerosol knowing:
338        the aerosol optical depth as measure from the plane 	= tamoyp
339        the rayleigh scale   height = 			= hr (8km)
340        the rayleigh optical depth  at plane level 		= trmoyp
341        the altitude of the plane 				= palt
342        the rayleigh optical depth for total atmos		= trmoy
343        the aerosol  optical depth for total atmos		= tamoy
344        if not plane observations then ha is equal to 2.0km
345        ntp local variable: if ntp=nt     no plane observation selected
346        ntp=nt-1   plane observation selected
347        it's a mixing rayleigh+aerosol */
348 
349     double ha = 2;
350     int snt = nt;
351     int ntp = snt;
352     if(alt.palt <= 900 && alt.palt > 0)
353     {
354 	if(tap > 1.e-03) ha = -alt.palt / (double)log(tap / tamoy);
355 	ntp = snt - 1;
356     }
357 
358     double xmus = -gauss.rm[STDI(0)];
359 
360     /* compute mixing rayleigh, aerosol
361        case 1: pure rayleigh
362        case 2: pure aerosol
363        case 3: mixing rayleigh-aerosol */
364 
365     double h[31];
366     memset(h, 0, sizeof(h));
367     double ch[31];
368     double ydel[31];
369 
370     double xdel[31];
371     double altc[31];
372     if( (tamoy <= accu2) && (trmoy > tamoy) )
373     {
374 	for(int j = 0; j <= ntp; j++)
375 	{
376 	    h[j] = j * trmoy / ntp;
377 	    ch[j]= (double)exp(-h[j] / xmus) / 2;
378 	    ydel[j] = 1;
379 	    xdel[j] = 0;
380 
381 	    if (j == 0) altc[j] = 300;
382 	    else altc[j] = -(double)log(h[j] / trmoy) * 8;
383 	}
384     }
385 
386 
387     if( (trmoy <= accu2) && (tamoy > trmoy) )
388     {
389 	for(int j = 0; j <= ntp; j++)
390 	{
391 	    h[j] = j * tamoy / ntp;
392 	    ch[j]= (double)exp(-h[j] / xmus) / 2;
393 	    ydel[j] = 0;
394 	    xdel[j] = pizmoy;
395 
396 	    if (j == 0) altc[j] = 300;
397 	    else altc[j] = -(double)log(h[j] / tamoy) * ha;
398 	}
399     }
400 
401     if(trmoy > accu2 && tamoy > accu2)
402     {
403 	ydel[0] = 1;
404 	xdel[0] = 0;
405 	h[0] = 0;
406 	ch[0] = 0.5;
407 	altc[0] = 300;
408 	double zx = 300;
409 	iplane = 0;
410 
411 	for(int it = 0; it <= ntp; it++)
412 	{
413 	    if(it == 0) zx = discre(tamoy, ha, trmoy, 8.0, it, ntp, 0, 0, 300, 0);
414 	    else zx = discre(tamoy, ha, trmoy, 8.0, it, ntp, h[it - 1], ydel[it - 1], 300, 0);
415 
416 	    double xx = -zx / ha;
417 	    double ca;
418 	    if( xx <= -20 ) ca = 0;
419 	    else ca = tamoy * (double)exp(xx);
420 
421 	    xx = -zx / 8;
422 	    double cr = trmoy * (double)exp(xx);
423 	    h[it] = cr + ca;
424 
425 
426 	    altc[it] = zx;
427 	    ch[it] = (double)exp(-h[it] / xmus) / 2;
428 	    cr = cr / 8;
429 	    ca = ca / ha;
430 	    double ratio = cr / (cr + ca);
431 	    xdel[it] = (1 - ratio) * pizmoy;
432 	    ydel[it] = ratio;
433 	}
434     }
435 
436     /* update plane layer if necessary */
437     if (ntp == (snt - 1))
438     {
439 	/* compute position of the plane layer */
440         double taup = tap + trp;
441         iplane = -1;
442 	for(int i = 0; i <= ntp; i++) if (taup >= h[i]) iplane = i;
443 
444 	/* update the layer from the end to the position to update if necessary */
445 	double xt1 = (double)fabs(h[iplane] - taup);
446         double xt2 = (double)fabs(h[iplane+1] - taup);
447 
448         if ((xt1 > 0.0005) && (xt2 > 0.0005))
449 	{
450 	    for(int i = snt; i >= iplane+1; i--)
451 	    {
452 		xdel[i] = xdel[i-1];
453 		ydel[i] = ydel[i-1];
454 		h[i] = h[i-1];
455 		altc[i] = altc[i-1];
456 		ch[i] = ch[i-1];
457 	    }
458 	}
459         else
460 	{
461 	    snt = ntp;
462 	    if (xt2 > xt1) iplane++;
463 
464 	}
465 
466 	h[iplane] = taup;
467 	if ( trmoy > accu2 && tamoy > accu2 )
468 	{
469 
470 	    double ca = tamoy * (double)exp(-alt.palt / ha);
471 	    double cr = trmoy * (double)exp(-alt.palt / 8);
472 	    h[iplane] = ca + cr;
473 	    cr = cr / 8;
474 	    ca = ca / ha;
475 	    double ratio = cr / (cr + ca);
476 	    xdel[iplane] = (1 - ratio) * pizmoy;
477 	    ydel[iplane] = ratio;
478 	    altc[iplane] = alt.palt;
479 	    ch[iplane] = (double)exp(-h[iplane] / xmus) / 2;
480 	}
481 
482 	if ( trmoy > accu2 && tamoy <= accu2 )
483 	{
484 	    ydel[iplane] = 1;
485 	    xdel[iplane] = 0;
486 	    altc[iplane] = alt.palt;
487 	}
488 
489 	if ( trmoy <= accu2 && tamoy > accu2 )
490 	{
491 	    ydel[iplane] = 0;
492 	    xdel[iplane] = pizmoy;
493 	    altc[iplane] = alt.palt;
494 	}
495     }
496 
497 
498     double phi = (double)geom.phirad;
499     int i, k;
500     for(i = 0; i < np; i++) for(int m = -mu; m <= mu; m++) xl[STDI(m)][i] = 0;
501 
502     /* ************ incident angle mus ******* */
503 
504     double aaaa = delta / (2 - delta);
505     double ron = (1 - aaaa) / (1 + 2 * aaaa);
506 
507     /* rayleigh phase function */
508 
509     double beta0 = 1;
510     double beta2 = 0.5f * ron;
511 
512     /* fourier decomposition */
513     double i1[31][2*mu + 1];
514     double i2[31][2*mu + 1];
515     double i3[2*mu + 1];
516     double i4[2*mu + 1];
517     double in[2*mu + 1];
518     double inm1[2*mu + 1];
519     double inm2[2*mu + 1];
520     for(i = -mu; i <= mu; i++) i4[STDI(i)] = 0;
521 
522     int iborm = 80;
523     if(fabs(xmus - 1.000000) < 1e-06) iborm = 0;
524 
525     for(int is = 0; is <= iborm; is++)
526     {
527 	/* primary scattering */
528 	int ig = 1;
529 	double roavion0 = 0;
530 	double roavion1 = 0;
531 	double roavion2 = 0;
532 	double roavion = 0;
533 
534 	int j;
535 	for(j = -mu; j <= mu; j++) i3[STDI(j)] = 0;
536 
537 	/* kernel computations */
538 	double xpl[2*mu + 1];
539 	double bp[26][2*mu + 1];
540 	memset(xpl, 0, sizeof(double)*(2*mu+1));
541 	memset(bp, 0, sizeof(double)*26*(2*mu+1));
542 
543 	kernel(is,xpl,bp,gauss);
544 
545 	if(is > 0) beta0 = 0;
546 
547 	for(j = -mu; j <= mu; j++)
548 	{
549 	    double sa1;
550 	    double sa2;
551 
552 	    if((is - 2) <= 0)
553 	    {
554 		double spl = xpl[STDI(0)];
555 		sa1 = beta0 + beta2 * xpl[STDI(j)] * spl;
556 		sa2 = bp[0][STDI(j)];
557 	    }
558 	    else
559 	    {
560 		sa2 = bp[0][STDI(j)];
561 		sa1 = 0;
562 	    }
563 	    /* primary scattering source function at every level within the layer */
564 
565 	    for(k = 0; k <= snt; k++)
566 	    {
567 		double c = ch[k];
568 		double a = ydel[k];
569 		double b = xdel[k];
570 		i2[k][STDI(j)] = (double)(c * (sa2 * b + sa1 * a));
571 	    }
572 	}
573 
574 	/* vertical integration, primary upward radiation */
575 	for(k = 1; k <= mu; k++)
576 	{
577 	    i1[snt][STDI(k)] = 0;
578 	    double zi1 = i1[snt][STDI(k)];
579 
580 	    for(i = snt - 1; i >= 0; i--)
581 	    {
582 		double f = h[i + 1] - h[i];
583 		double a = (i2[i + 1][STDI(k)] - i2[i][STDI(k)]) / f;
584 		double b = i2[i][STDI(k)] - a * h[i];
585 		double c = (double)exp(-f / gauss.rm[STDI(k)]);
586 
587 		double xx = h[i] - h[i + 1] * c;
588 		zi1 = (double)(c * zi1 + ((1 - c) * (b + a * gauss.rm[STDI(k)]) + a * xx) / 2);
589 		i1[i][STDI(k)] = zi1;
590 	    }
591 	}
592 
593 	/* vertical integration, primary downward radiation */
594 	for(k = -mu; k <= -1; k++)
595 	{
596 	    i1[0][STDI(k)] = 0;
597 	    double zi1 = i1[0][STDI(k)];
598 
599 	    for(i = 1; i <= snt; i++)
600 	    {
601 		double f = h[i] - h[i - 1];
602 		double c = (double)exp(f / gauss.rm[STDI(k)]);
603 		double a = (i2[i][STDI(k)] -i2[i - 1][STDI(k)]) / f;
604 		double b = i2[i][STDI(k)] - a * h[i];
605 		double xx = h[i] - h[i - 1] * c;
606 		zi1 = (double)(c * zi1 + ((1 - c) * (b + a * gauss.rm[STDI(k)]) + a * xx)/ 2);
607 		i1[i][STDI(k)] = zi1;
608 	    }
609 	}
610 
611 	/* inm2 is inialized with scattering computed at n-2
612 	   i3 is inialized with primary scattering */
613 	for(k = -mu; k <= mu; k++)
614 	{
615 	    if(k < 0)
616 	    {
617 		inm1[STDI(k)] = i1[snt][STDI(k)];
618 		inm2[STDI(k)] = i1[snt][STDI(k)];
619 		i3[STDI(k)] = i1[snt][STDI(k)];
620 	    }
621 	    else if(k > 0)
622 	    {
623 		inm1[STDI(k)] = i1[0][STDI(k)];
624 		inm2[STDI(k)] = i1[0][STDI(k)];
625 		i3[STDI(k)] = i1[0][STDI(k)];
626 	    }
627 	}
628 	roavion2 = i1[iplane][STDI(mu)];
629 	roavion = i1[iplane][STDI(mu)];
630 
631         do
632 	{
633 	    /* loop on successive order */
634 	    ig++;
635 
636 	    /* successive orders
637 	       multiple scattering source function at every level within the laye
638 	       if is < ou = 2 kernels are a mixing of aerosols and molecules kern
639 	       if is >2 aerosols kernels only */
640 
641 	    if(is - 2 <= 0)
642 	    {
643 		for(k = 1; k <= mu; k++)
644 		{
645 		    for(i = 0; i <= snt; i++)
646 		    {
647 			double ii1 = 0;
648 			double ii2 = 0;
649 
650 			for(j = 1; j <= mu; j++)
651 			{
652 			    double bpjk = bp[j][STDI(k)] * xdel[i] + ydel[i] * (beta0 + beta2 * xpl[STDI(j)] * xpl[STDI(k)]);
653 			    double bpjmk = bp[j][STDI(-k)] * xdel[i] + ydel[i] * (beta0 + beta2 * xpl[STDI(j)] * xpl[STDI(-k)]);
654 			    double xdb = gauss.gb[STDI(j)] * (i1[i][STDI(j)] * bpjk + i1[i][STDI(-j)] * bpjmk);
655 			    ii2 += xdb;
656 			    xdb = gauss.gb[STDI(j)] * (i1[i][STDI(j)] * bpjmk + i1[i][STDI(-j)] * bpjk);
657 			    ii1 += xdb;
658 			}
659 
660 			if (ii2 < 1e-30) ii2 = 0;
661 			if (ii1 < 1e-30) ii1 = 0;
662 			i2[i][STDI(k)] = (double)ii2;
663 			i2[i][STDI(-k)]= (double)ii1;
664 		    }
665 		}
666 	    }
667 	    else
668 	    {
669 		for(k = 1; k <= mu; k++)
670 		{
671 		    double ii1;
672 		    double ii2;
673 
674 		    for(i = 0; i <= snt; i++)
675 		    {
676 			ii1 = 0;
677 			ii2 = 0;
678 
679 			for(j = 1; j <= mu; j++)
680 			{
681 			    double bpjk = bp[j][STDI(k)] * xdel[i];
682 			    double bpjmk = bp[j][STDI(-k)] * xdel[i];
683 			    double xdb = gauss.gb[STDI(j)] * (i1[i][STDI(j)] * bpjk + i1[i][STDI(-j)] * bpjmk);
684 			    ii2 += xdb;
685 			    xdb = gauss.gb[STDI(j)] * (i1[i][STDI(j)] * bpjmk + i1[i][STDI(-j)] * bpjk);
686 			    ii1 += xdb;
687 			}
688 
689 			if (ii2 < 1e-30) ii2 = 0;
690 			if (ii1 < 1e-30) ii1 = 0;
691 			i2[i][STDI(k)]  = (double)ii2;
692 			i2[i][STDI(-k)] = (double)ii1;
693 		    }
694 		}
695 	    }
696 
697 
698 	    /* vertical integration, upward radiation */
699 	    for(k = 1; k <= mu; k++)
700 	    {
701 		i1[snt][STDI(k)] = 0;
702 		double zi1 = i1[snt][STDI(k)];
703 
704 		for(i = snt-1; i >= 0; i--)
705 		{
706 		    double f = h[i + 1] - h[i];
707 		    double a = (i2[i + 1][STDI(k)] - i2[i][STDI(k)]) / f;
708 		    double b = i2[i][STDI(k)] - a * h[i];
709 		    double c = (double)exp(-f / gauss.rm[STDI(k)]);
710 		    double xx = h[i] - h[i + 1] * c;
711 		    zi1 = (double)(c * zi1 + ((1 - c) * (b + a * gauss.rm[STDI(k)]) + a * xx) / 2);
712 		    if (fabs(zi1) <= 1e-20) zi1 = 0;
713 		    i1[i][STDI(k)] = zi1;
714 		}
715 	    }
716 
717 	    /* vertical integration, downward radiation */
718 	    for(k = -mu; k <= -1; k++)
719 	    {
720 		i1[0][STDI(k)] = 0;
721 		double zi1 = i1[0][STDI(k)];
722 
723 		for(i = 1; i <= snt; i++)
724 		{
725 		    double f = h[i] - h[i - 1];
726 		    double c = (double)exp(f / gauss.rm[STDI(k)]);
727 		    double a = (i2[i][STDI(k)] - i2[i - 1][STDI(k)]) / f;
728 		    double b = i2[i][STDI(k)] - a * h[i];
729 		    double xx = h[i] - h[i - 1] * c;
730 		    zi1 = (double)(c * zi1 + ((1 - c) * (b + a * gauss.rm[STDI(k)]) + a * xx) / 2);
731 
732 		    if (fabs(zi1) <= 1e-20) zi1 = 0;
733 		    i1[i][STDI(k)] = zi1;
734 		}
735 	    }
736 
737 	    /* in is the nieme scattering order */
738 	    for(k = -mu; k <= mu; k++)
739 	    {
740 		if(k < 0) in[STDI(k)] = i1[snt][STDI(k)];
741 		else if(k > 0) in[STDI(k)] = i1[0][STDI(k)];
742 	    }
743 	    roavion0 = i1[iplane][STDI(mu)];
744 
745 	    /*  convergence test (geometrical serie) */
746 	    if(ig > 2)
747 	    {
748 		double a1 = roavion2;
749 		double d1 = roavion1;
750 
751 		double g1 = roavion0;
752 
753 
754 		double z = 0;
755 		if(a1 >= accu && d1 >= accu && roavion >= accu)
756 		{
757 		    double y = ((g1 / d1 - d1 / a1) / ((1 - g1 / d1) * (1 - g1 / d1)) * (g1 / roavion));
758 		    y = fabs(y);
759 		    z = y > z ? y : z;
760 		}
761 
762 		for(int l = -mu; l <= mu; l++)
763 		{
764 		    if (l == 0) continue;
765 		    a1 = inm2[STDI(l)];
766 		    d1 = inm1[STDI(l)];
767 		    g1 = in[STDI(l)];
768 		    if(a1 <= accu) continue;
769 		    if(d1 <= accu) continue;
770 		    if(i3[STDI(l)] <= accu) continue;
771 
772 		    double y = ((g1 / d1 - d1 / a1) / ((1 - g1 / d1) * (1 - g1 / d1)) * (g1 / i3[STDI(l)]));
773 		    y = fabs(y);
774 		    z = y > z ? y : z;
775 		}
776 
777 		if(z < 0.0001)
778 		{
779 		    /* successful test (geometrical serie) */
780 		    double y1;
781 
782 		    for(int l = -mu; l <= mu; l++)
783 		    {
784 			y1 = 1;
785 			d1 = inm1[STDI(l)];
786 			g1 = in[STDI(l)];
787 			if(d1 <= accu) continue;
788 			y1 = 1 - g1 / d1;
789 			if(fabs(g1 - d1) <= accu) continue;
790 			g1 /= y1;
791 			i3[STDI(l)] += g1;
792 		    }
793 
794 
795 		    d1 = roavion1;
796 		    g1 = roavion0;
797 		    y1 = 1;
798 		    if(d1 >= accu)
799 		    {
800 			if(fabs(g1 - d1) >= accu)
801 			{
802 			    y1 = 1 - g1 / d1;
803 			    g1 /= y1;
804 			}
805 
806 			roavion += g1;
807 		    }
808 
809 		    break;	/* break out of the while loop */
810 		}
811 
812 		/* inm2 is the (n-2)ieme scattering order */
813 		for(k = -mu; k <= mu; k++) inm2[STDI(k)] = inm1[STDI(k)];
814 		roavion2 = roavion1;
815 	    }
816 
817 	    /* inm1 is the (n-1)ieme scattering order */
818 	    for(k = -mu; k <= mu; k++) inm1[STDI(k)] = in[STDI(k)];
819 	    roavion1 = roavion0;
820 
821 	    /* sum of the n-1 orders */
822 	    for(k = -mu; k <= mu; k++) i3[STDI(k)] += in[STDI(k)];
823 	    roavion += roavion0;
824 
825 	    /* stop if order n is less than 1% of the sum */
826 	    double z = 0;
827 	    for(k = -mu; k <= mu; k++)
828 	    {
829 		if (fabs(i3[STDI(k)]) >= accu)
830 		{
831 		    double y = fabs(in[STDI(k)] / i3[STDI(k)]);
832 		    z = z >= y ? z : y;
833 		}
834 	    }
835 	    if(z < 0.00001) break;
836 
837 	} while( ig <= 20 );	/* stop if order n is greater than 20 in any case */
838 
839 	/* sum of the fourier component s */
840 	double delta0s = 1;
841 	if(is != 0) delta0s = 2;
842 	for(k = -mu; k <= mu; k++) i4[STDI(k)] += delta0s * i3[STDI(k)];
843 
844 	/* stop of the fourier decomposition */
845 	int l;
846 	for(l = 0; l < np; l++)
847 	{
848 	    phi = gauss.rp[l];
849 
850 	    for(int m = -mum1; m <= mum1; m++)
851 	    {
852 		if(m > 0) xl[STDI(m)][l] += (double)(delta0s * i3[STDI(m)] * cos(is * (phi + M_PI)));
853 		else xl[STDI(m)][l] += (double)(delta0s * i3[STDI(m)] * cos(is * phi));
854 	    }
855 	}
856 
857 	if(is == 0)
858 	    for(k = 1; k <= mum1; k++) xl[STDI(0)][0] += gauss.rm[STDI(k)] * gauss.gb[STDI(k)] * i3[STDI(-k)];
859 
860 	xl[STDI(mu)][0] += (double)(delta0s * i3[STDI(mu)] * cos(is * (geom.phirad + M_PI)));
861 	xl[STDI(-mu)][0] += (double)(delta0s * roavion * cos(is * (geom.phirad + M_PI)));
862 
863 	double z = 0;
864 	for(l = -mu; l <= mu; l++)
865 	{
866             if(l == 0) continue;
867 	    if (fabs(i4[STDI(l)]) > accu) continue;
868 	    double x = fabs(i3[STDI(l)] / i4[STDI(l)]);
869 	    z = z > x ? z : x;
870 	}
871 
872 	if(z <= 0.001) break;
873     }
874 }
875 
876 
877 /*
878   Compute the atmospheric transmission for either a satellite or aircraft observation
879   as well as the spherical albedo of the atmosphere.
880 */
iso(const double tamoy,const double trmoy,const double pizmoy,const double tamoyp,const double trmoyp,double (& xf)[3],Gauss & gauss,const Altitude & alt)881 void iso(const double tamoy, const double trmoy, const double pizmoy,
882 	 const double tamoyp, const double trmoyp, double (&xf)[3],
883          Gauss &gauss, const Altitude &alt)
884 {
885     /* molecular ratio within the layer
886        computations are performed assuming a scale of 8km for
887        molecules and 2km for aerosols */
888 
889     /* the optical thickness above plane are recomputed to give o.t above pla */
890     double trp = trmoy - trmoyp;
891     double tap = tamoy - tamoyp;
892 
893     /* if plane observations recompute scale height for aerosol knowing:
894        the aerosol optical depth as measure from the plane 	= tamoyp
895        the rayleigh scale   height = 			= hr (8km)
896        the rayleigh optical depth  at plane level 		= trmoyp
897        the altitude of the plane 				= palt
898        the rayleigh optical depth for total atmos		= trmoy
899        the aerosol  optical depth for total atmos		= tamoy
900        if not plane observations then ha is equal to 2.0km
901        sntp local variable: if sntp=snt     no plane observation selected
902        sntp=snt-1   plane observation selected */
903 
904     /* it's a mixing rayleigh+aerosol */
905     int snt = nt;
906     int iplane = 0;
907     int ntp = snt;
908     double ha = 2.0;
909     if(alt.palt <= 900. && alt.palt > 0.0)
910     {
911 	if (tap > 1.e-03) ha = (double)(-alt.palt / log(tap / tamoy));
912         else ha = 2.;
913 	ntp = snt - 1;
914     }
915 
916     /* compute mixing rayleigh, aerosol
917        case 1: pure rayleigh
918        case 2: pure aerosol
919        case 3: mixing rayleigh-aerosol */
920 
921     double h[31];
922     double ydel[31];
923     double xdel[31];
924     double altc[31];
925 
926     if((tamoy <= accu2) && (trmoy > tamoy))
927     {
928 	for(int j = 0; j <= ntp; j++)
929 	{
930 	    h[j] = j * trmoy / ntp;
931 	    ydel[j] = 1.0;
932 	    xdel[j] = 0.0;
933 	}
934     }
935 
936     if((trmoy <= accu2) && (tamoy > trmoy))
937     {
938 	for(int j = 0; j <= ntp; j++)
939 	{
940 	    h[j] = j * tamoy / ntp;
941 	    ydel[j] = 0.0;
942 	    xdel[j] = pizmoy;
943 	}
944     }
945 
946     if(trmoy > accu2 && tamoy > accu2)
947     {
948 	ydel[0] = 1.0;
949 	xdel[0] = 0.0;
950 	h[0] = 0;
951 	altc[0] = 300;
952 	double zx = 300;
953 	iplane = 0;
954 
955 	for(int it = 0; it <= ntp; it++)
956 
957 	{
958 	    if (it == 0) zx = discre(tamoy,ha,trmoy,8.0,it,ntp,0,0,300.0,0.0);
959 	    else zx = discre(tamoy,ha,trmoy,8.0,it,ntp,h[it-1],ydel[it-1],300.0,0.0);
960 
961 	    double ca;
962 	    if ((-zx / ha) < -18) ca = 0;
963 	    else ca = (double)(tamoy * exp(-zx / ha));
964 
965 	    double cr = (double)(trmoy * exp(-zx / 8.0));
966 	    h[it] = cr + ca;
967 	    altc[it] = zx;
968 
969 	    cr = cr / 8;
970 	    ca = ca / ha;
971 	    double ratio = cr / (cr + ca);
972 	    xdel[it] = (1 - ratio) * pizmoy;
973 	    ydel[it] = ratio;
974 
975 	}
976     }
977 
978     /* update plane layer if necessary */
979     if (ntp == (snt-1))
980     {
981 	/* compute position of the plane layer */
982 	double taup = tap + trp;
983         iplane = -1;
984         for(int i = 0; i <= ntp; i++) if (taup >= h[i]) iplane = i;
985 
986 	/* update the layer from the end to the position to update if necessary */
987         double xt1 = (double)fabs(h[iplane] - taup);
988         double xt2 = (double)fabs(h[iplane + 1] - taup);
989         if ((xt1 > 0.005) && (xt2 > 0.005))
990 	{
991 	    for(int i = snt; i >= iplane + 1; i--)
992 	    {
993 		xdel[i] = xdel[i-1];
994 
995 
996 		ydel[i] = ydel[i-1];
997 		h[i] = h[i-1];
998             	altc[i] = altc[i-1];
999 	    }
1000 	}
1001         else
1002 	{
1003 	    snt = ntp;
1004 	    if (xt2 < xt1) iplane = iplane + 1;
1005 	}
1006 
1007 	h[iplane] = taup;
1008 	if ( trmoy > accu2 && tamoy > accu2)
1009 	{
1010 	    double ca = (double)(tamoy * exp(-alt.palt / ha));
1011 	    double cr = (double)(trmoy * exp(-alt.palt / 8.0));
1012 	    cr = cr / 8;
1013 	    ca = ca / ha;
1014 	    double ratio = cr / (cr + ca);
1015 	    xdel[iplane] = (1 - ratio) * pizmoy;
1016 
1017 	    ydel[iplane] = ratio;
1018 	    altc[iplane] = alt.palt;
1019 	}
1020 
1021 	if ( trmoy > accu2 && tamoy <= accu2)
1022 	{
1023 	    ydel[iplane] = 1;
1024 	    xdel[iplane] = 0;
1025 	    altc[iplane] = alt.palt;
1026 	}
1027 
1028 	if ( trmoy <= accu2 && tamoy > accu2)
1029 	{
1030 	    ydel[iplane] = 0;
1031 	    xdel[iplane] = 1 * pizmoy;
1032 	    altc[iplane] = alt.palt;
1033 	}
1034     }
1035 
1036     double aaaa = delta / (2-delta);
1037     double ron = (1 - aaaa) / (1 + 2 * aaaa);
1038 
1039     /* rayleigh phase function */
1040     double beta0 = 1;
1041     double beta2 = 0.5f * ron;
1042 
1043     /* primary scattering */
1044     int ig = 1;
1045     double tavion0 = 0;
1046     double tavion1 = 0;
1047     double tavion2 = 0;
1048     double tavion = 0;
1049 
1050     double i1[31][2*mu + 1];
1051     double i2[31][2*mu + 1];
1052     double i3[2*mu + 1];
1053     double in[2*mu + 1];
1054     double inm1[2*mu + 1];
1055     double inm2[2*mu + 1];
1056     int j;
1057     for(j = -mu; j <= mu; j++) i3[STDI(j)] = 0;
1058 
1059     /* kernel computations */
1060     double xpl[2*mu + 1];
1061     double bp[26][2*mu + 1];
1062     kernel(0, xpl, bp, gauss);
1063 
1064     for(j = -mu; j <= mu; j++)
1065 	for(int k = 0; k <= snt; k++) i2[k][STDI(j)] = 0;
1066 
1067     /* vertical integration, primary upward radiation */
1068     int k;
1069     for(k = 1; k <= mu; k++)
1070     {
1071 	i1[snt][STDI(k)] = 1.0;
1072 	for(int i = snt-1; i >= 0; i--)
1073 	    i1[i][STDI(k)] = (double)(exp(-(tamoy + trmoy - h[i]) / gauss.rm[STDI(k)]));
1074     }
1075 
1076 
1077     /* vertical integration, primary downward radiation */
1078     for(k = -mu; k <= -1; k++)
1079 	for(int i = 0; i <= snt; i++) i1[i][STDI(k)] = 0.0;
1080 
1081     /* inm2 is inialized with scattering computed at n-2
1082        i3 is inialized with primary scattering */
1083     for(k = -mu; k <= mu; k++)
1084     {
1085 	if(k == 0) continue;
1086 	if(k < 0)
1087 	{
1088 	    inm1[STDI(k)] = i1[snt][STDI(k)];
1089 	    inm2[STDI(k)] = i1[snt][STDI(k)];
1090 	    i3[STDI(k)] = i1[snt][STDI(k)];
1091 	}
1092 	else
1093 	{
1094 	    inm1[STDI(k)] = i1[0][STDI(k)];
1095 	    inm2[STDI(k)] = i1[0][STDI(k)];
1096 	    i3[STDI(k)] = i1[0][STDI(k)];
1097 	}
1098     }
1099     tavion = i1[iplane][STDI(mu)];
1100     tavion2 = i1[iplane][STDI(mu)];
1101 
1102     do {
1103 	/* loop on successive order */
1104 	ig++;
1105 
1106 	/* successive orders
1107 	   multiple scattering source function at every level within the layer */
1108 	for(k = 1; k <= mu; k++)
1109 	{
1110 	    for(int i = 0; i <= snt; i++)
1111 	    {
1112 		double ii1 = 0;
1113 		double ii2 = 0;
1114 		double x = xdel[i];
1115 		double y = ydel[i];
1116 
1117 		for(j = 1; j <= mu; j++)
1118 		{
1119 		    double bpjk = bp[j][STDI(k)] * x + y * (beta0 + beta2 * xpl[STDI(j)] * xpl[STDI(k)]);
1120 		    double bpjmk= bp[j][STDI(-k)] * x + y * (beta0 + beta2 * xpl[STDI(j)] * xpl[STDI(-k)]);
1121 		    ii2 += gauss.gb[STDI(j)] * (i1[i][STDI(j)] * bpjk + i1[i][STDI(-j)] * bpjmk);
1122 		    ii1 += gauss.gb[STDI(j)] * (i1[i][STDI(j)] * bpjmk + i1[i][STDI(-j)] * bpjk);
1123 		}
1124 
1125 		i2[i][STDI(k)] = (double)ii2;
1126 		i2[i][STDI(-k)] = (double)ii1;
1127 	    }
1128 	}
1129 
1130 	/* vertical integration, upward radiation */
1131 	for(k = 1; k <= mu; k++)
1132 	{
1133 	    i1[snt][STDI(k)] = 0.0;
1134 	    double zi1 = i1[snt][STDI(k)];
1135 
1136 	    for(int i = snt-1; i >= 0; i--)
1137 	    {
1138 		double f = h[i+1] - h[i];
1139 		double a = (i2[i+1][STDI(k)] -i2[i][STDI(k)]) / f;
1140 		double b = i2[i][STDI(k)] - a * h[i];
1141 		double c = (double)exp(-f / gauss.rm[STDI(k)]);
1142 		double xx = h[i] - h[i+1] * c;
1143 
1144 		zi1 = c * zi1 + ((1 - c) * (b + a * gauss.rm[STDI(k)]) + a * xx) / 2;
1145 		i1[i][STDI(k)] = zi1;
1146 	    }
1147 	}
1148 
1149 	/* vertical integration, downward radiation */
1150 	for(k = -mu; k <= -1; k++)
1151 	{
1152 	    i1[0][STDI(k)] = 0;
1153 	    double zi1 = i1[0][STDI(k)];
1154 
1155 	    for(int i = 1; i <= snt; i++)
1156 	    {
1157 		double f = h[i] - h[i-1];
1158 		double c = (double)exp(f / gauss.rm[STDI(k)]);
1159 		double a = (i2[i][STDI(k)] - i2[i-1][STDI(k)]) / f;
1160 		double b = i2[i][STDI(k)] - a * h[i];
1161 		double xx = h[i] - h[i-1] * c;
1162 		zi1 = c * zi1 + ((1 - c) * (b + a * gauss.rm[STDI(k)]) + a * xx) / 2;
1163 		i1[i][STDI(k)] = zi1;
1164 	    }
1165 	}
1166 
1167 	/* in is the nieme scattering order */
1168 	for(k = -mu; k <= mu; k++)
1169 	{
1170 	    if(k == 0) continue;
1171 	    if(k < 0) in[STDI(k)] = i1[snt][STDI(k)];
1172 	    else in[STDI(k)] = i1[0][STDI(k)];
1173 	}
1174 	tavion0 = i1[iplane][STDI(mu)];
1175 
1176 	/* convergence test (geometrical serie) */
1177 	if(ig > 2)
1178 	{
1179 	    double z = 0;
1180 	    double a1 = tavion2;
1181 	    double d1 = tavion1;
1182 	    double g1 = tavion0;
1183 	    if (a1 >= accu && d1 >= accu && tavion >= accu)
1184 	    {
1185 		double y = ((g1 / d1 - d1 / a1) / ((1 - g1 / d1) * (1 - g1 / d1)) * (g1 / tavion));
1186 		y = (double)fabs(y);
1187 		z = z >= y ? z : y;
1188 	    }
1189 
1190 	    for(int l = -mu; l <= mu; l++)
1191 	    {
1192 		if (l == 0) continue;
1193 		a1 = inm2[STDI(l)];
1194 		d1 = inm1[STDI(l)];
1195 		g1 = in[STDI(l)];
1196 		if(a1 == 0) continue;
1197 		if(d1 == 0) continue;
1198 		if(i3[STDI(l)] == 0) continue;
1199 
1200 		double y = ((g1 / d1 - d1 / a1) / ((1 - g1 / d1) * (1 - g1 / d1)) * (g1 / i3[STDI(l)]));
1201 		y = (double)fabs(y);
1202 		z = z >= y ? z : y;
1203 	    }
1204 
1205 	    if(z < 0.0001)
1206 	    {
1207 		/* successful test (geometrical serie) */
1208 
1209 		for(int l = -mu; l <= mu; l++)
1210 		{
1211 		    if (l == 0) continue;
1212 		    double y1 = 1;
1213 		    d1 = inm1[STDI(l)];
1214 		    g1 = in[STDI(l)];
1215 		    if(d1 == 0) continue;
1216 		    y1 = 1 - g1 / d1;
1217 		    g1 = g1 / y1;
1218 		    i3[STDI(l)] += g1;
1219 		}
1220 
1221 		d1 = tavion1;
1222 		g1 = tavion0;
1223 		if (d1 >= accu)
1224 		{
1225 		    if (fabs(g1 - d1) >= accu)
1226 		    {
1227 			double y1 = 1 - g1 / d1;
1228 			g1 = g1 / y1;
1229 		    }
1230 		    tavion = tavion + g1;
1231 
1232 		}
1233 
1234 		break; /* go to 505 */
1235 	    }
1236 
1237 	    /* inm2 is the (n-2)ieme scattering order */
1238 	    for(k = -mu; k <= mu; k++) inm2[STDI(k)] = inm1[STDI(k)];
1239 	    tavion2 = tavion1;
1240 	}
1241 
1242 	/* inm1 is the (n-1)ieme scattering order */
1243 	for(k = -mu; k <= mu; k++) inm1[STDI(k)] = in[STDI(k)];
1244 	tavion1 = tavion0;
1245 
1246 	/* sum of the n-1 orders */
1247 	for(k = -mu; k <= mu; k++) i3[STDI(k)] += in[STDI(k)];
1248 	tavion = tavion + tavion0;
1249 
1250 	/* stop if order n is less than 1% of the sum */
1251 	double z = 0;
1252 	for(k = -mu; k <= mu; k++)
1253 	{
1254 	    if(i3[STDI(k)] != 0)
1255 	    {
1256 		double y = (double)fabs(in[STDI(k)] / i3[STDI(k)]);
1257 		z = z >= y ? z : y;
1258 	    }
1259 	}
1260 	if(z < 0.00001) break;
1261 
1262 	/* stop if order n is greater than 20 in any case */
1263     } while(ig <= 20);
1264 
1265     /* dimension for os computation */
1266     xf[0] = tavion;
1267     xf[1] = 0;
1268     xf[2] = 0;
1269 
1270     xf[2] += i3[STDI(mu)];
1271     for(k = 1; k <= mu; k++) xf[1] += gauss.rm[STDI(k)] * gauss.gb[STDI(k)] * i3[STDI(-k)];
1272 
1273 }
1274 
1275 /*
1276   To compute the atmospheric reflectance for the molecular atmosphere in
1277   case of satellite observation.
1278 */
chand(const double xtau,const GeomCond & geom)1279 double chand(const double xtau, const GeomCond &geom)
1280 {
1281     /* input parameters: xphi,xmus,xmuv,xtau
1282        xphi: azimuthal difference between sun and observation (xphi=0,
1283        in backscattering) and expressed in degree (0.:360.)
1284        xmus: cosine of the sun zenith angle
1285        xmuv: cosine of the observation zenith angle
1286        xtau: molecular optical depth
1287        output parameter: xrray : molecular reflectance (0.:1.)
1288        constant : xdep: depolarization factor (0.0279) */
1289 
1290     const double xdep = 0.0279;
1291 
1292     static const double as0[10] = {
1293 	.33243832,-6.777104e-02,.16285370,1.577425e-03,-.30924818,
1294 	-1.240906e-02,-.10324388,3.241678e-02,.11493334,-3.503695e-02
1295     };
1296 
1297     static const double as1[2] = { .19666292, -5.439061e-02 };
1298     static const double as2[2] = { .14545937,-2.910845e-02 };
1299 
1300     double phios = (180 - geom.phi);
1301     double xcosf1 = 1;
1302     double xcosf2 = cos(phios * M_PI / 180);
1303     double xcosf3 = cos(2 * phios * M_PI / 180);
1304 
1305 
1306     double xfd = xdep / (2 - xdep);
1307     xfd = (1 - xfd) / (1 + 2 * xfd);
1308 
1309     double xph1 = 1 + (3 * geom.xmus * geom.xmus - 1) * (3 * geom.xmuv * geom.xmuv - 1) * xfd / 8;
1310     double xph2 = -geom.xmus * geom.xmuv * sqrt(1 - geom.xmus * geom.xmus) * sqrt(1 - geom.xmuv * geom.xmuv);
1311     xph2 *= xfd * 0.75;
1312     double xph3 = (1 - geom.xmus * geom.xmus) * (1 - geom.xmuv * geom.xmuv);
1313     xph3 *= xfd * 0.1875;
1314 
1315     double xitm = (1 - exp(-xtau * (1 / geom.xmus + 1 / geom.xmuv))) * geom.xmus / (4 * (geom.xmus + geom.xmuv));
1316     double xp1 = xph1 * xitm;
1317     double xp2 = xph2 * xitm;
1318     double xp3 = xph3 * xitm;
1319 
1320     xitm = (1 - exp(-xtau / geom.xmus)) * (1 - exp(-xtau / geom.xmuv));
1321     double cfonc1 = xph1 * xitm;
1322     double cfonc2 = xph2 * xitm;
1323     double cfonc3 = xph3 * xitm;
1324     double xlntau = log(xtau);
1325 
1326     double pl[10];
1327     pl[0] = 1;
1328     pl[1] = xlntau;
1329     pl[2] = geom.xmus + geom.xmuv;
1330     pl[3] = xlntau * pl[2];
1331     pl[4] = geom.xmus * geom.xmuv;
1332     pl[5] = xlntau * pl[4];
1333     pl[6] = geom.xmus * geom.xmus + geom.xmuv * geom.xmuv;
1334     pl[7] = xlntau * pl[6];
1335     pl[8] = geom.xmus * geom.xmus * geom.xmuv * geom.xmuv;
1336     pl[9] = xlntau * pl[8];
1337 
1338     double fs0 = 0;
1339     for(int i = 0; i < 10; i++) fs0 += pl[i] * as0[i];
1340 
1341     double fs1 = pl[0] * as1[0] + pl[1] * as1[1];
1342     double fs2 = pl[0] * as2[0] + pl[1] * as2[1];
1343     double xitot1 = xp1 + cfonc1 * fs0 * geom.xmus;
1344     double xitot2 = xp2 + cfonc2 * fs1 * geom.xmus;
1345     double xitot3 = xp3 + cfonc3 * fs2 * geom.xmus;
1346 
1347     double xrray = (double)(xitot1 * xcosf1);
1348     xrray += (double)(xitot2 * xcosf2 * 2);
1349 
1350     xrray += (double)(xitot3 * xcosf3 * 2);
1351     xrray /= (double)geom.xmus;
1352 
1353     return xrray;
1354 }
1355 
1356 /*
1357   To compute the atmospheric reflectance for the molecular and aerosol atmospheres
1358   and the mixed atmosphere. In 6S instead of an approximation as in 5S, we use the scalar Successive
1359   Order of Scattering method (subroutine OS.f). The polarization terms of aerosol or rayleigh phase
1360   are not accounted for in the computation of the aerosol reflectance and the mixed Rayleigh-aerosol
1361   reflectance. The polarization is addressed in computing the Rayleigh reflectance (Subroutine
1362   CHAND.f) by semi-empirical fitting of the vectorized Successive Orders of Scattering method
1363   (Deuz� et al, 1989).
1364 */
atmref(const double tamoy,const double trmoy,const double pizmoy,const double tamoyp,const double trmoyp,OpticalAtmosProperties & oap,Gauss & gauss,const GeomCond & geom,const AerosolModel & aero,const Altitude & alt)1365 void atmref(const double tamoy, const double trmoy, const double pizmoy,
1366 	    const double tamoyp, const double trmoyp, OpticalAtmosProperties &oap,
1367             Gauss &gauss, const GeomCond &geom, const AerosolModel &aero,
1368             const Altitude &alt)
1369 {
1370     double xlm1[2 * mu + 1][np];
1371     double xlm2[2 * mu + 1][np];
1372 
1373     /* atmospheric reflectances */
1374     oap.rorayl = 0;
1375     oap.roaero = 0;
1376 
1377     /* rayleigh reflectance 3 cases (satellite,plane,ground) */
1378     if(alt.palt < 900 && alt.palt > 0)
1379     {
1380 	gauss.rm[STDI(-mu)] = -(double)geom.xmuv;
1381 	gauss.rm[STDI(mu)] = (double)geom.xmuv;
1382 	gauss.rm[STDI(0)] = -(double)geom.xmus;
1383 
1384 	os(0, trmoy, pizmoy, 0, trmoyp, xlm1, gauss, alt, geom);
1385 
1386 	oap.rorayl = (double)(xlm1[STDI(-mu)][0] / geom.xmus);
1387     }
1388     else if(alt.palt <= 0) oap.rorayl = 0;
1389     else oap.rorayl = chand(trmoy, geom);
1390 
1391     if (aero.iaer == 0)
1392     {
1393 	oap.romix = oap.rorayl;
1394 	return;
1395     }
1396 
1397     /* rayleigh+aerosol=romix,aerosol=roaero reflectance computed
1398        using successive order of scattering method
1399        3 cases: satellite,plane,ground */
1400     if (alt.palt > 0)
1401     {
1402 	gauss.rm[STDI(-mu)] = -(double)geom.xmuv;
1403 	gauss.rm[STDI(mu)] = (double)geom.xmuv;
1404 	gauss.rm[STDI(0)] = -(double)geom.xmus;
1405 
1406 	os(tamoy, trmoy, pizmoy, tamoyp, trmoyp, xlm2, gauss, alt, geom);
1407 	oap.romix = (double)(xlm2[STDI(-mu)][0] / geom.xmus);
1408 
1409 	os(tamoy, 0, pizmoy, tamoyp, 0, xlm2, gauss, alt, geom);
1410 	oap.roaero = (double)(xlm2[STDI(-mu)][0] / geom.xmus);
1411     }
1412     else
1413     {
1414 	oap.roaero = 0;
1415 	oap.romix = 0;
1416     }
1417 }
1418 
1419 
fintexp1(const double xtau)1420 double fintexp1(const double xtau)
1421 {
1422     /* accuracy 2e-07... for 0<xtau<1 */
1423     double a[6] = { -.57721566,0.99999193,-0.24991055,0.05519968,-0.00976004,0.00107857 };
1424     double xftau = 1;
1425     double xx = a[0];
1426     for(int i = 1; i <= 5; i++)
1427     {
1428 	xftau *= xtau;
1429 	xx += a[i] * xftau;
1430     }
1431     return (double)(xx - log(xtau));
1432 }
1433 
fintexp3(const double xtau)1434 double fintexp3(const double xtau)
1435 {
1436     return (double)((exp(-xtau) * (1. - xtau) + xtau * xtau * fintexp1(xtau)) / 2.);
1437 }
1438 
1439 /* To compute the spherical albedo of the molecular layer. */
csalbr(const double xtau,double & xalb)1440 void csalbr(const double xtau, double& xalb)
1441 {
1442     xalb = (double)((3. * xtau - fintexp3(xtau) * (4. + 2. * xtau) + 2. * exp(-xtau)));
1443     xalb = (double)(xalb / (4. + 3. * xtau));
1444 }
1445 
scatra(const double taer,const double taerp,const double tray,const double trayp,const double piza,OpticalAtmosProperties & oap,Gauss & gauss,const GeomCond & geom,const Altitude & alt)1446 void scatra(const double taer, const double taerp,
1447 	    const double tray, const double trayp,
1448 	    const double piza, OpticalAtmosProperties& oap,
1449             Gauss &gauss, const GeomCond &geom, const Altitude &alt)
1450 {
1451     /* computations of the direct and diffuse transmittances
1452        for downward and upward paths , and spherical albedo */
1453     double tamol,tamolp;
1454     double xtrans[3];
1455 
1456     oap.ddirtt = 1;	oap.ddiftt = 0;
1457     oap.udirtt = 1;	oap.udiftt = 0;
1458     oap.ddirtr = 1;	oap.ddiftr = 0;
1459     oap.udirtr = 1;	oap.udiftr = 0;
1460     oap.ddirta = 1;	oap.ddifta = 0;
1461     oap.udirta = 1;	oap.udifta = 0;
1462     oap.sphalbt = 0;
1463     oap.sphalbr = 0;
1464     oap.sphalba = 0;
1465 
1466     for(int it = 1; it <= 3; it++)
1467 
1468     {
1469 	/* it=1 rayleigh only, it=2 aerosol only, it=3 rayleigh+aerosol */
1470 	if (it == 2 && taer <= 0) continue;
1471 
1472 	/* compute upward,downward diffuse transmittance for rayleigh,aerosol */
1473 	if (it == 1)
1474 	{
1475 	    if (alt.palt > 900)
1476 	    {
1477 		oap.udiftt = (double)((2. / 3. + geom.xmuv) + (2. / 3. - geom.xmuv) * exp(-tray / geom.xmuv));
1478 
1479 		oap.udiftt = (double)(oap.udiftt / ((4. / 3.) + tray) - exp(-tray / geom.xmuv));
1480 		oap.ddiftt = (double)((2. / 3. + geom.xmus) + (2. / 3. - geom.xmus) * exp(-tray / geom.xmus));
1481 		oap.ddiftt = (double)(oap.ddiftt / ((4. / 3.) + tray) - exp(-tray / geom.xmus));
1482 		oap.ddirtt = (double)exp(-tray / geom.xmus);
1483 		oap.udirtt = (double)exp(-tray / geom.xmuv);
1484 
1485 		csalbr(tray, oap.sphalbt);
1486 	    }
1487 	    else if (alt.palt > 0 && alt.palt <= 900)
1488 	    {
1489 		tamol = 0;
1490 		tamolp= 0;
1491 		gauss.rm[STDI(-mu)] = -(double)geom.xmuv;
1492 		gauss.rm[STDI(mu)] = (double)geom.xmuv;
1493 		gauss.rm[STDI(0)] = (double)geom.xmus;
1494 
1495 		iso(tamol, tray, piza, tamolp, trayp, xtrans, gauss, alt);
1496 
1497 		oap.udiftt = (double)(xtrans[0] - exp(-trayp / geom.xmuv));
1498 		oap.udirtt = (double)exp(-trayp / geom.xmuv);
1499 		gauss.rm[STDI(-mu)] = -(double)geom.xmus;
1500 		gauss.rm[STDI(mu)] = (double)geom.xmus;
1501 		gauss.rm[STDI(0)] = (double)geom.xmus;
1502 
1503 		oap.ddiftt = (double)((2. / 3. + geom.xmus) + (2. / 3. - geom.xmus) * exp(-tray / geom.xmus));
1504 		oap.ddiftt = (double)(oap.ddiftt / ((4. / 3.) + tray) - exp(-tray / geom.xmus));
1505 		oap.ddirtt = (double)exp(-tray / geom.xmus);
1506 		oap.udirtt = (double)exp(-tray / geom.xmuv);
1507 
1508 		csalbr(tray, oap.sphalbt);
1509 	    }
1510 	    else if (alt.palt <= 0.)
1511 	    {
1512 		oap.udiftt = 0;
1513 		oap.udirtt = 1;
1514 	    }
1515 
1516 	    oap.ddirtr = oap.ddirtt;
1517 	    oap.ddiftr = oap.ddiftt;
1518 	    oap.udirtr = oap.udirtt;
1519 	    oap.udiftr = oap.udiftt;
1520 	    oap.sphalbr = oap.sphalbt;
1521 	}
1522 
1523 	else if (it == 2)
1524 	{
1525 	    tamol = 0;
1526 	    tamolp= 0;
1527 	    gauss.rm[STDI(-mu)] = -(double)geom.xmuv;
1528 	    gauss.rm[STDI(mu)] = (double)geom.xmuv;
1529 	    gauss.rm[STDI(0)] = (double)geom.xmus;
1530 
1531 	    iso(taer, tamol, piza, taerp, tamolp, xtrans, gauss, alt);
1532 
1533 	    oap.udiftt = (double)(xtrans[0] - exp(-taerp / geom.xmuv));
1534 	    oap.udirtt = (double)exp(-taerp / geom.xmuv);
1535 	    gauss.rm[STDI(-mu)] = -(double)geom.xmus;
1536 	    gauss.rm[STDI(mu)] = (double)geom.xmus;
1537 	    gauss.rm[STDI(0)] = (double)geom.xmus;
1538 
1539 	    double tmp_alt = alt.palt;
1540 	    alt.palt = 999;
1541 	    iso(taer, tamol, piza, taerp, tamolp, xtrans, gauss, alt);
1542 	    alt.palt = tmp_alt;
1543 
1544 	    oap.ddirtt = (double)exp(-taer / geom.xmus);
1545 	    oap.ddiftt = (double)(xtrans[2] - exp(-taer / geom.xmus));
1546 	    oap.sphalbt= (double)(xtrans[1] * 2);
1547 
1548 	    if(alt.palt <= 0)
1549 	    {
1550 		oap.udiftt = 0;
1551 		oap.udirtt = 1;
1552 	    }
1553 
1554 	    oap.ddirta = oap.ddirtt;
1555 	    oap.ddifta = oap.ddiftt;
1556 	    oap.udirta = oap.udirtt;
1557 	    oap.udifta = oap.udiftt;
1558 	    oap.sphalba = oap.sphalbt;
1559 	}
1560 	else if(it == 3)
1561 	{
1562 	    gauss.rm[STDI(-mu)] = -(double)geom.xmuv;
1563 	    gauss.rm[STDI(mu)] = (double)geom.xmuv;
1564 	    gauss.rm[STDI(0)] = (double)geom.xmus;
1565 
1566 	    iso(taer, tray, piza, taerp, trayp, xtrans, gauss, alt);
1567 
1568 	    oap.udirtt = (double)exp(-(taerp + trayp) / geom.xmuv);
1569 	    oap.udiftt = (double)(xtrans[0] - exp(-(taerp + trayp) / geom.xmuv));
1570 	    gauss.rm[STDI(-mu)] = -(double)geom.xmus;
1571 	    gauss.rm[STDI(mu)] = (double)geom.xmus;
1572 	    gauss.rm[STDI(0)] = (double)geom.xmus;
1573 
1574 	    double tmp_alt = alt.palt;
1575 	    alt.palt = 999;
1576 	    iso(taer, tray, piza, taerp, trayp, xtrans, gauss, alt);
1577 	    alt.palt = tmp_alt;
1578 
1579 	    oap.ddiftt = (double)(xtrans[2] - exp(-(taer + tray) / geom.xmus));
1580 	    oap.ddirtt = (double)exp(-(taer + tray) / geom.xmus);
1581 
1582 	    oap.sphalbt= (double)(xtrans[1] * 2);
1583 
1584 	    if (alt.palt <= 0)
1585 	    {
1586 		oap.udiftt = 0;
1587 		oap.udirtt = 1;
1588 	    }
1589 	}
1590     }
1591 }
1592 
1593 /* To compute the optical properties of the atmosphere at the 10 discrete
1594    wavelengths. */
discom(const GeomCond & geom,const AtmosModel & atms,const AerosolModel & aero,const AerosolConcentration & aerocon,const Altitude & alt,const IWave & iwave)1595 void discom(const GeomCond &geom, const AtmosModel &atms,
1596             const AerosolModel &aero, const AerosolConcentration &aerocon,
1597             const Altitude &alt, const IWave &iwave)
1598 {
1599     OpticalAtmosProperties oap;
1600     memset(&oap, 0, sizeof(oap));
1601 
1602     Gauss gauss;
1603 
1604     gauss.init();   /* discom is the only function that uses the gauss data */
1605 
1606     memset(&sixs_trunc, 0, sizeof(sixs_trunc));  /* clear this to keep preconditions the same and output consistent */
1607 
1608 /*    computation of all scattering parameters at wavelength
1609       discrete values,so we
1610       can interpolate at any wavelength */
1611     int i;
1612     for(i = 0; i < 10; i++)
1613     {
1614 	if(!((((i < 2) && iwave.ffu.wlsup < sixs_disc.wldis[0])) || ((iwave.ffu.wlinf > sixs_disc.wldis[9]) && (i >= 8))))
1615 	    if (((i < 9) && (sixs_disc.wldis[i] < iwave.ffu.wlinf) && (sixs_disc.wldis[i+1] < iwave.ffu.wlinf)) ||
1616 		((i > 0) && (sixs_disc.wldis[i] > iwave.ffu.wlsup) && (sixs_disc.wldis[i-1] > iwave.ffu.wlsup))) continue;
1617 
1618 	double wl = sixs_disc.wldis[i];
1619 	/* computation of rayleigh optical depth at wl */
1620 	double tray = odrayl(atms, wl);
1621 	double trayp;
1622 
1623 	/* plane case discussed here above */
1624 	if (alt.idatmp == 0) trayp = 0;
1625 	else if (alt.idatmp == 4) trayp = tray;
1626 	else trayp = tray * alt.ftray;
1627 
1628 	sixs_disc.trayl[i] = tray;
1629 	sixs_disc.traypl[i] = trayp;
1630 
1631 	/* computation of aerosol optical properties at wl */
1632 
1633 	double taer = aerocon.taer55 * sixs_aer.ext[i] / sixs_aer.ext[3];
1634 	double taerp = alt.taer55p * sixs_aer.ext[i] / sixs_aer.ext[3];
1635 	double piza = sixs_aer.ome[i];
1636 
1637 	/* computation of atmospheric reflectances
1638 
1639 	rorayl is rayleigh ref
1640 	roaero is aerosol ref
1641 	call plegen to decompose aerosol phase function in Betal */
1642 
1643 	double coeff = 0;
1644 	if(aero.iaer != 0)
1645 	{
1646 	    for(int k = 0; k < 83; k++) sixs_trunc.pha[k] = sixs_sos.phasel[i][k];
1647 	    coeff = trunca();
1648 	}
1649 
1650 	double tamoy = taer * (1 - piza * coeff);
1651 	double tamoyp = taerp * (1 - piza * coeff);
1652 	double pizmoy = piza * (1 - coeff) / (1 - piza * coeff);
1653 
1654 	atmref(tamoy, tray, pizmoy, tamoyp, trayp, oap, gauss, geom, aero, alt);
1655 
1656 	/* computation of scattering transmitances (direct and diffuse)
1657 	   first time for rayleigh ,next total (rayleigh+aerosols) */
1658 
1659 	scatra(tamoy, tamoyp, tray, trayp, pizmoy, oap, gauss, geom, alt);
1660 
1661 	sixs_disc.roatm[0][i] = oap.rorayl;
1662 	sixs_disc.roatm[1][i] = oap.romix;
1663 	sixs_disc.roatm[2][i] = oap.roaero;
1664 	sixs_disc.dtdir[0][i] = oap.ddirtr;
1665 	sixs_disc.dtdif[0][i] = oap.ddiftr;
1666 	sixs_disc.dtdir[1][i] = oap.ddirtt;
1667 	sixs_disc.dtdif[1][i] = oap.ddiftt;
1668 	sixs_disc.dtdir[2][i] = oap.ddirta;
1669 	sixs_disc.dtdif[2][i] = oap.ddifta;
1670 	sixs_disc.utdir[0][i] = oap.udirtr;
1671 	sixs_disc.utdif[0][i] = oap.udiftr;
1672 	sixs_disc.utdir[1][i] = oap.udirtt;
1673 	sixs_disc.utdif[1][i] = oap.udiftt;
1674 	sixs_disc.utdir[2][i] = oap.udirta;
1675 	sixs_disc.utdif[2][i] = oap.udifta;
1676 	sixs_disc.sphal[0][i] = oap.sphalbr;
1677 	sixs_disc.sphal[1][i] = oap.sphalbt;
1678 	sixs_disc.sphal[2][i] = oap.sphalba;
1679     }
1680 
1681 }
1682 
1683 /*
1684   To compute the atmospheric properties at the equivalent wavelength (see
1685   EQUIVWL.f) needed for the calculation of the downward radiation field used
1686   in the computation of the non lambertian target contribution (main.f).
1687 */
specinterp(const double wl,double & tamoy,double & tamoyp,double & pizmoy,double & pizmoyp,const AerosolConcentration & aerocon,const Altitude & alt)1688 void specinterp(const double wl, double& tamoy, double& tamoyp, double& pizmoy, double& pizmoyp,
1689                 const AerosolConcentration &aerocon, const Altitude &alt)
1690 {
1691 
1692     int linf = 0;
1693     int i = 8;
1694     while (i >= 0 && linf == 0) {
1695 	if (wl >= sixs_disc.wldis[i] && wl <= sixs_disc.wldis[i+1]) linf = i;
1696 	i--;
1697     }
1698     if(wl > sixs_disc.wldis[9]) linf = 8;
1699 
1700     int lsup = linf + 1;
1701     double coef = (double)log(sixs_disc.wldis[lsup] / sixs_disc.wldis[linf]);
1702     double wlinf = sixs_disc.wldis[linf];
1703 
1704     double alphaa = (double)(log(sixs_aer.ext[lsup] * sixs_aer.ome[lsup] /
1705 			       (sixs_aer.ext[linf] * sixs_aer.ome[linf])) / coef);
1706     double betaa = (double)(sixs_aer.ext[linf] * sixs_aer.ome[linf] / pow(wlinf,alphaa));
1707     double tsca = (double)(aerocon.taer55 * betaa * pow(wl,alphaa) / sixs_aer.ext[3]);
1708     alphaa = (double)(log(sixs_aer.ext[lsup] / sixs_aer.ext[linf]) / coef);
1709     betaa = (double)(sixs_aer.ext[linf] / pow(wlinf,alphaa));
1710     tamoy = (double)(aerocon.taer55 * betaa * pow(wl,alphaa) / sixs_aer.ext[3]);
1711     tamoyp= (double)(alt.taer55p * betaa * pow(wl,alphaa) / sixs_aer.ext[3]);
1712     pizmoy= tsca / tamoy;
1713     pizmoyp = pizmoy;
1714 
1715     for(int k = 0; k < 83; k++)
1716     {
1717 	alphaa = (double)log(sixs_sos.phasel[lsup][k] / sixs_sos.phasel[linf][k]) / coef;
1718 	betaa = (double)(sixs_sos.phasel[linf][k] / pow(wlinf,alphaa));
1719 	sixs_trunc.pha[k] = (double)(betaa * pow(wl,alphaa));
1720     }
1721 
1722     double coeff = trunca();
1723 
1724     tamoy *= 1 - pizmoy * coeff;
1725     tamoyp *= 1 - pizmoyp * coeff;
1726     pizmoy *= (1 - coeff) / (1 - pizmoy * coeff);
1727 }
1728 
1729 /**********************************************************************c
1730 c                                                                      c
1731 c                     start of computations                            c
1732 c                                                                      c
1733 c**********************************************************************/
1734 
1735 /*
1736   To compute the environment functions F(r) which allows us to account for an
1737   inhomogeneous ground.
1738 */
enviro(const double difr,const double difa,const double r,const double palt,const double xmuv,double & fra,double & fae,double & fr)1739 void enviro (const double difr, const double difa, const double r, const double palt,
1740 	     const double xmuv, double& fra, double& fae, double& fr)
1741 {
1742     double fae0, fra0, xlnv;
1743     static const double alt[16] = {
1744 	0.5,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,
1745 	10.0,12.0,14.0,16.0,18.0,20.0,60.0
1746     };
1747 
1748     static const double cfr1[16] = {
1749 	0.730,0.710,0.656,0.606,0.560,0.516,0.473,
1750 	0.433,0.395,0.323,0.258,0.209,0.171,0.142,0.122,0.070
1751     };
1752 
1753     static const double cfr2[16] = {
1754 	2.8,1.51,0.845,0.634,0.524,0.465,0.429,
1755 	0.405,0.390,0.386,0.409,0.445,0.488,0.545,0.608,0.868
1756     };
1757 
1758     static const double cfa1[16] = {
1759 	0.239,0.396,0.588,0.626,0.612,0.505,0.454,
1760 	0.448,0.444,0.445,0.444,0.448,0.448,0.448,0.448,0.448
1761     };
1762 
1763     static const double cfa2[16] = {
1764 	1.40,1.20,1.02,0.86,0.74,0.56,0.46,0.42,
1765 	0.38,0.34,0.3,0.28,0.27,0.27,0.27,0.27
1766     };
1767 
1768     static const double cfa3[16] = {
1769 	9.17,6.26,5.48,5.16,4.74,3.65,3.24,3.15,
1770 	3.07,2.97,2.88,2.83,2.83,2.83,2.83,2.83
1771     };
1772 
1773 
1774 /*     calculation of the environmental function for
1775        rayleigh and aerosols contribution.
1776 
1777        this calculation have been done for nadir observation
1778        and are corrected of the effect of the view zenith angle. */
1779 
1780     const double a0 = 1.3347;
1781     const double b0 = 0.57757;
1782     const double a1 = -1.479;
1783     const double b1 = -1.5275;
1784 
1785     if (palt >= 60)
1786     {
1787 	fae0 = (double)(1. - 0.448 * exp( -r * 0.27) - 0.552 * exp( -r * 2.83));
1788 	fra0 = (double)(1. - 0.930 * exp( -r * 0.080) - 0.070 * exp( -r * 1.100));
1789     }
1790     else
1791     {
1792 	int i;
1793 	for(i = 0; palt >= alt[i]; i++);
1794 	double xcfr1 = 0, xcfr2 = 0, xcfa1 = 0, xcfa2 = 0, xcfa3 = 0;
1795 
1796 	if ((i > 0) && (i < 16))
1797 	{
1798 	    double zmin = alt[i - 1];
1799 	    double zmax = alt[i];
1800 	    xcfr1 = cfr1[i - 1] + (cfr1[i] - cfr1[i - 1]) * (palt - zmin) / (zmax - zmin);
1801 	    xcfr2 = cfr2[i - 1] + (cfr2[i] - cfr2[i - 1]) * (palt - zmin) / (zmax - zmin);
1802 	    xcfa1 = cfa1[i - 1] + (cfa1[i] - cfa1[i - 1]) * (palt - zmin) / (zmax - zmin);
1803 	    xcfa2 = cfa2[i - 1] + (cfa2[i] - cfa2[i - 1]) * (palt - zmin) / (zmax - zmin);
1804 	    xcfa3 = cfa3[i - 1] + (cfa3[i] - cfa3[i - 1]) * (palt - zmin) / (zmax - zmin);
1805 	}
1806 
1807 	if (i == 0)
1808 	{
1809 	    xcfr1 = cfr1[0];
1810 	    xcfr2 = cfr2[0];
1811 	    xcfa1 = cfa1[0];
1812 	    xcfa2 = cfa2[0];
1813 	    xcfa3 = cfa3[0];
1814 	}
1815 
1816 	fra0 = (double)(1. - xcfr1 * exp(-r * xcfr2) - (1. - xcfr1) * exp(-r * 0.08));
1817 	fae0 = (double)(1. - xcfa1 * exp(-r * xcfa2) - (1. - xcfa1) * exp(-r * xcfa3));
1818     }
1819 
1820     /* correction of the effect of the view zenith angle */
1821     xlnv = (double)log(xmuv);
1822     fra = (double)(fra0 * (xlnv * (1 - fra0) + 1));
1823     fae = (double)(fae0 * ((1 + a0 * xlnv + b0 * xlnv * xlnv) + fae0 * (a1 * xlnv + b1 * xlnv * xlnv) +
1824 			  fae0 * fae0 * ((-a1 - a0) * xlnv + ( - b1 - b0) * xlnv * xlnv)));
1825 
1826     if ((difa + difr) > 1e-03) fr = (fae * difa + fra * difr) / (difa + difr);
1827     else fr = 1;
1828 }
1829 
1830 
1831