ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/genssky.c
Revision: 2.2
Committed: Fri Jul 19 23:38:28 2024 UTC (9 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.1: +279 -91 lines
Log Message:
feat(genssky): Major changes and improvements by Taoning Wang

File Contents

# User Rev Content
1 greg 2.2 #ifndef lint
2     static const char RCSid[] = "$Id$";
3     #endif
4     /* Main function for generating spectral sky */
5     /* Cloudy sky computed as weight average of clear and cie overcast sky */
6 greg 2.1
7 greg 2.2 #include "atmos.h"
8 greg 2.1 #include "copyright.h"
9     #include "resolu.h"
10 greg 2.2 #include "rtio.h"
11 greg 2.1 #include "view.h"
12 greg 2.2 #include <ctype.h>
13     #ifdef _WIN32
14     #include <windows.h>
15     #else
16     #include <errno.h>
17     #include <sys/stat.h>
18     #include <sys/types.h>
19     #endif
20 greg 2.1
21     char *progname;
22    
23     const double ARCTIC_LAT = 67.;
24     const double TROPIC_LAT = 23.;
25     const int SUMMER_START = 4;
26     const int SUMMER_END = 9;
27     const double GNORM = 0.777778;
28    
29     const double D65EFF = 203.; /* standard illuminant D65 */
30    
31 greg 2.2 /* Mean normalized relative daylight spectra where CCT = 6415K for overcast; */
32 greg 2.1 const double D6415[NSSAMP] = {0.63231, 1.06171, 1.00779, 1.36423, 1.34133,
33     1.27258, 1.26276, 1.26352, 1.22201, 1.13246,
34     1.0434, 1.05547, 0.98212, 0.94445, 0.9722,
35     0.82387, 0.87853, 0.82559, 0.75111, 0.78925};
36    
37 greg 2.2 /* European and North American zones */
38     struct {
39     char zname[8]; /* time zone name (all caps) */
40     float zmer; /* standard meridian */
41     } tzone[] = {{"YST", 135}, {"YDT", 120}, {"PST", 120}, {"PDT", 105},
42     {"MST", 105}, {"MDT", 90}, {"CST", 90}, {"CDT", 75},
43     {"EST", 75}, {"EDT", 60}, {"AST", 60}, {"ADT", 45},
44     {"NST", 52.5}, {"NDT", 37.5}, {"GMT", 0}, {"BST", -15},
45     {"CET", -15}, {"CEST", -30}, {"EET", -30}, {"EEST", -45},
46     {"AST", -45}, {"ADT", -60}, {"GST", -60}, {"GDT", -75},
47     {"IST", -82.5}, {"IDT", -97.5}, {"JST", -135}, {"NDT", -150},
48     {"NZST", -180}, {"NZDT", -195}, {"", 0}};
49    
50     static int make_directory(const char *path) {
51     #ifdef _WIN32
52     if (CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) {
53     return 1;
54     }
55     return 0;
56     #else
57     if (mkdir(path, 0777) == 0 || errno == EEXIST) {
58     return 1;
59     }
60     return 0;
61     #endif
62     }
63    
64     static int cvthour(char *hs, int *tsolar, double *hour) {
65     char *cp = hs;
66     int i, j;
67    
68     if ((*tsolar = *cp == '+'))
69     cp++; /* solar time? */
70     while (isdigit(*cp))
71     cp++;
72     if (*cp == ':')
73     *hour = atoi(hs) + atoi(++cp) / 60.0;
74     else {
75     *hour = atof(hs);
76     if (*cp == '.')
77     cp++;
78     }
79     while (isdigit(*cp))
80     cp++;
81     if (!*cp)
82     return (0);
83     if (*tsolar || !isalpha(*cp)) {
84     fprintf(stderr, "%s: bad time format: %s\n", progname, hs);
85     exit(1);
86     }
87     i = 0;
88     do {
89     for (j = 0; cp[j]; j++)
90     if (toupper(cp[j]) != tzone[i].zname[j])
91     break;
92     if (!cp[j] && !tzone[i].zname[j]) {
93     s_meridian = tzone[i].zmer * (PI / 180);
94     return (1);
95     }
96     } while (tzone[i++].zname[0]);
97    
98     fprintf(stderr, "%s: unknown time zone: %s\n", progname, cp);
99     fprintf(stderr, "Known time zones:\n\t%s", tzone[0].zname);
100     for (i = 1; tzone[i].zname[0]; i++)
101     fprintf(stderr, " %s", tzone[i].zname);
102     putc('\n', stderr);
103     exit(1);
104     }
105    
106     static void basename(const char *path, char *output, size_t outsize) {
107     const char *last_slash = strrchr(path, '/');
108     const char *last_backslash = strrchr(path, '\\');
109     const char *filename = path;
110     const char *last_dot;
111    
112     if (last_slash && last_backslash) {
113     filename =
114     (last_slash > last_backslash) ? last_slash + 1 : last_backslash + 1;
115     } else if (last_slash) {
116     filename = last_slash + 1;
117     } else if (last_backslash) {
118     filename = last_backslash + 1;
119     }
120    
121     last_dot = strrchr(filename, '.');
122     if (last_dot) {
123     size_t length = last_dot - filename;
124     if (length < outsize) {
125     strncpy(output, filename, length);
126     output[length] = '\0';
127     } else {
128     strncpy(output, filename, outsize - 1);
129     output[outsize - 1] = '\0';
130     }
131     }
132     }
133    
134     char *join_paths(const char *path1, const char *path2) {
135     size_t len1 = strlen(path1);
136     size_t len2 = strlen(path2);
137     int need_separator = (path1[len1 - 1] != DIRSEP);
138    
139     char *result = malloc(len1 + len2 + (need_separator ? 2 : 1));
140     if (!result)
141     return NULL;
142    
143     strcpy(result, path1);
144     if (need_separator) {
145     result[len1] = DIRSEP;
146     len1++;
147     }
148     strcpy(result + len1, path2);
149    
150     return result;
151     }
152    
153 greg 2.1 static inline double wmean2(const double a, const double b, const double x) {
154     return a * (1 - x) + b * x;
155     }
156    
157     static inline double wmean(const double a, const double x, const double b,
158     const double y) {
159     return (a * x + b * y) / (a + b);
160     }
161    
162     static double get_zenith_brightness(const double sundir[3]) {
163     double zenithbr;
164     if (sundir[2] < 0) {
165     zenithbr = 0;
166     } else {
167     zenithbr = (8.6 * sundir[2] + .123) * 1000.0 / D65EFF;
168     }
169     return zenithbr;
170     }
171    
172 greg 2.2 /* from gensky.c */
173 greg 2.1 static double get_overcast_brightness(const double dz, const double zenithbr) {
174     double groundbr = zenithbr * GNORM;
175     return wmean(pow(dz + 1.01, 10), zenithbr * (1 + 2 * dz) / 3,
176     pow(dz + 1.01, -10), groundbr);
177     }
178    
179 greg 2.2 static void write_header(const int argc, char **argv, const double cloud_cover,
180     const double grefl, const int res) {
181     printf("# ");
182     for (int i = 0; i < argc; i++) {
183     printf("%s ", argv[i]);
184     }
185     printf("\n");
186     printf("#Cloud cover: %g\n#Ground reflectance: %g\n#Sky map resolution: %d\n\n",
187     cloud_cover, grefl, res);
188     }
189    
190     static void write_rad(const double *sun_radiance, const FVECT sundir,
191     const char skyfile[PATH_MAX],
192     const char grndfile[PATH_MAX]) {
193 greg 2.1 if (sundir[2] > 0) {
194 greg 2.2 printf("void spectrum sunrad\n0\n0\n22 380 780 ");
195     /* Normalize to one */
196     double sum = 0.0;
197 greg 2.1 for (int i = 0; i < NSSAMP; ++i) {
198 greg 2.2 sum += sun_radiance[i];
199 greg 2.1 }
200 greg 2.2 double mean = sum / NSSAMP;
201     for (int i = 0; i < NSSAMP; ++i) {
202     printf("%.3f ", sun_radiance[i] / mean);
203     }
204     double intensity = mean * WVLSPAN;
205     printf("\n\nsunrad light solar\n0\n0\n3 %.1f %.1f %.1f\n\n", intensity,
206     intensity, intensity);
207     printf("solar source sun\n0\n0\n4 %f %f %f 0.533\n\n", sundir[0], sundir[1],
208     sundir[2]);
209     }
210     printf("void specpict skymap\n8 noop %s fisheye.cal fish_u fish_v -rx 90 "
211     "-mx\n0\n0\n\n",
212     skyfile);
213    
214     printf("void specpict grndmap\n8 noop %s fisheye.cal fish_u fish_v -rx -90 "
215     "-my\n0\n0\n\n",
216     grndfile);
217     printf("void mixfunc skyfunc\n4 skymap grndmap if(Dz,1,0) .\n0\n0\n");
218 greg 2.1 }
219    
220     static void write_hsr_header(FILE *fp, RESOLU *res) {
221     float wvsplit[4] = {380, 480, 588,
222 greg 2.2 780}; /* RGB wavelength limits+partitions (nm) */
223 greg 2.1 newheader("RADIANCE", fp);
224     fputncomp(NSSAMP, fp);
225     fputwlsplit(wvsplit, fp);
226     fputformat(SPECFMT, fp);
227     fputc('\n', fp);
228     fputsresolu(res, fp);
229     }
230    
231     int gen_spect_sky(DATARRAY *tau_clear, DATARRAY *scat_clear,
232     DATARRAY *scat1m_clear, DATARRAY *irrad_clear,
233     const double cloud_cover, const FVECT sundir,
234     const double grefl, const int res, const char *outname) {
235     char skyfile[PATH_MAX];
236     char grndfile[PATH_MAX];
237     if (!snprintf(skyfile, sizeof(skyfile), "%s_sky.hsr", outname)) {
238     fprintf(stderr, "Error setting sky file name\n");
239     return 0;
240     };
241     if (!snprintf(grndfile, sizeof(grndfile), "%s_ground.hsr", outname)) {
242     fprintf(stderr, "Error setting ground file name\n");
243     return 0;
244     }
245     RESOLU rs = {PIXSTANDARD, res, res};
246     FILE *skyfp = fopen(skyfile, "w");
247     FILE *grndfp = fopen(grndfile, "w");
248     write_hsr_header(grndfp, &rs);
249     write_hsr_header(skyfp, &rs);
250     VIEW skyview = {VT_ANG, {0., 0., 0.}, {0., 0., 1.}, {0., 1., 0.}, 1.,
251     180., 180., 0., 0., 0.,
252     0., {0., 0., 0.}, {0., 0., 0.}, 0., 0.};
253     VIEW grndview = {
254     VT_ANG, {0., 0., 0.}, {0., 0., -1.}, {0., 1., 0.}, 1., 180., 180., 0., 0.,
255     0., 0., {0., 0., 0.}, {0., 0., 0.}, 0., 0.};
256     setview(&skyview);
257     setview(&grndview);
258    
259     CNDX[3] = NSSAMP;
260    
261     FVECT view_point = {0, 0, ER};
262     const double radius = VLEN(view_point);
263     const double sun_ct = fdot(view_point, sundir) / radius;
264     for (unsigned int j = 0; j < res; ++j) {
265     for (unsigned int i = 0; i < res; ++i) {
266     RREAL loc[2];
267     FVECT rorg = {0};
268     FVECT rdir_sky = {0};
269     FVECT rdir_grnd = {0};
270     SCOLOR sky_radiance = {0};
271     SCOLOR ground_radiance = {0};
272     SCOLR sky_sclr = {0};
273     SCOLR ground_sclr = {0};
274    
275     pix2loc(loc, &rs, i, j);
276     viewray(rorg, rdir_sky, &skyview, loc[0], loc[1]);
277     viewray(rorg, rdir_grnd, &grndview, loc[0], loc[1]);
278    
279     const double mu_sky = fdot(view_point, rdir_sky) / radius;
280     const double nu_sky = fdot(rdir_sky, sundir);
281    
282     const double mu_grnd = fdot(view_point, rdir_grnd) / radius;
283     const double nu_grnd = fdot(rdir_grnd, sundir);
284    
285     get_sky_radiance(scat_clear, scat1m_clear, radius, mu_sky, sun_ct, nu_sky,
286     sky_radiance);
287     get_ground_radiance(tau_clear, scat_clear, scat1m_clear, irrad_clear,
288     view_point, rdir_grnd, radius, mu_grnd, sun_ct,
289     nu_grnd, grefl, sundir, ground_radiance);
290    
291     for (int k = 0; k < NSSAMP; ++k) {
292     sky_radiance[k] *= WVLSPAN;
293     ground_radiance[k] *= WVLSPAN;
294     }
295    
296     if (cloud_cover > 0) {
297     double zenithbr = get_zenith_brightness(sundir);
298     double grndbr = zenithbr * GNORM;
299     double skybr = get_overcast_brightness(rdir_sky[2], zenithbr);
300     for (int k = 0; k < NSSAMP; ++k) {
301     sky_radiance[k] =
302     wmean2(sky_radiance[k], skybr * D6415[k], cloud_cover);
303     ground_radiance[k] =
304     wmean2(ground_radiance[k], grndbr * D6415[k], cloud_cover);
305     }
306     }
307    
308     scolor2scolr(sky_sclr, sky_radiance, 20);
309     putbinary(sky_sclr, LSCOLR, 1, skyfp);
310    
311     scolor2scolr(ground_sclr, ground_radiance, 20);
312     putbinary(ground_sclr, LSCOLR, 1, grndfp);
313     }
314     }
315     fclose(skyfp);
316     fclose(grndfp);
317    
318 greg 2.2 /* Get solar radiance */
319 greg 2.1 double sun_radiance[NSSAMP] = {0};
320     get_solar_radiance(tau_clear, scat_clear, scat1m_clear, sundir, radius,
321     sun_ct, sun_radiance);
322     if (cloud_cover > 0) {
323     double zenithbr = get_zenith_brightness(sundir);
324     double skybr = get_overcast_brightness(sundir[2], zenithbr);
325     for (int i = 0; i < NSSAMP; ++i) {
326     sun_radiance[i] =
327     wmean2(sun_radiance[i], D6415[i] * skybr / WVLSPAN, cloud_cover);
328     }
329     }
330    
331 greg 2.2 write_rad(sun_radiance, sundir, skyfile, grndfile);
332 greg 2.1 return 1;
333     }
334    
335 greg 2.2 static DpPaths get_dppaths(const char *dir, const double aod, const char *mname,
336     const char *tag) {
337 greg 2.1 DpPaths paths;
338    
339 greg 2.2 snprintf(paths.tau, PATH_MAX, "%s%ctau_%s_%s_%.2f.dat", dir, DIRSEP, tag,
340     mname, aod);
341     snprintf(paths.scat, PATH_MAX, "%s%cscat_%s_%s_%.2f.dat", dir, DIRSEP, tag,
342     mname, aod);
343     snprintf(paths.scat1m, PATH_MAX, "%s%cscat1m_%s_%s_%.2f.dat", dir, DIRSEP,
344     tag, mname, aod);
345     snprintf(paths.irrad, PATH_MAX, "%s%cirrad_%s_%s_%.2f.dat", dir, DIRSEP, tag,
346     mname, aod);
347 greg 2.1
348     return paths;
349     }
350    
351 greg 2.2 static void set_rayleigh_density_profile(Atmosphere *atmos, char *tag,
352     const int is_summer,
353 greg 2.1 const double s_latitude) {
354 greg 2.2 /* Set rayleigh density profile */
355     if (fabs(s_latitude * 180.0 / PI) > ARCTIC_LAT) {
356 greg 2.1 tag[0] = 's';
357     if (is_summer) {
358     tag[1] = 's';
359     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_SS;
360     atmos->beta_r0 = BR0_SS;
361     } else {
362     tag[1] = 'w';
363     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_SW;
364     atmos->beta_r0 = BR0_SW;
365     }
366 greg 2.2 } else if (fabs(s_latitude * 180.0 / PI) > TROPIC_LAT) {
367 greg 2.1 tag[0] = 'm';
368     if (is_summer) {
369     tag[1] = 's';
370     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_MS;
371     atmos->beta_r0 = BR0_MS;
372     } else {
373     tag[1] = 'w';
374     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_MW;
375     atmos->beta_r0 = BR0_MW;
376     }
377     } else {
378     tag[0] = 't';
379     tag[1] = 'r';
380     atmos->rayleigh_density.layers[0].exp_scale = -1.0 / HR_T;
381     atmos->beta_r0 = BR0_T;
382     }
383     tag[2] = '\0';
384     }
385    
386     static Atmosphere init_atmos(const double aod, const double grefl) {
387 greg 2.2 Atmosphere atmos = {.ozone_density = {.layers =
388     {
389     {.width = 25000.0,
390     .exp_term = 0.0,
391     .exp_scale = 0.0,
392     .linear_term = 1.0 / 15000.0,
393     .constant_term = -2.0 / 3.0},
394     {.width = AH,
395     .exp_term = 0.0,
396     .exp_scale = 0.0,
397     .linear_term = -1.0 / 15000.0,
398     .constant_term = 8.0 / 3.0},
399     }},
400     .rayleigh_density = {.layers =
401     {
402     {.width = AH,
403     .exp_term = 1.0,
404     .exp_scale = -1.0 / HR_MS,
405     .linear_term = 0.0,
406     .constant_term = 0.0},
407     }},
408     .beta_r0 = BR0_MS,
409     .beta_scale = aod / AOD0_CA,
410     .beta_m = NULL,
411     .grefl = grefl};
412 greg 2.1 return atmos;
413     }
414    
415     int main(int argc, char *argv[]) {
416     progname = argv[0];
417     int month, day;
418     double hour;
419     FVECT sundir;
420     int num_threads = 1;
421     int sorder = 4;
422     int year = 0;
423     int tsolar = 0;
424 greg 2.2 int got_meridian = 0;
425 greg 2.1 double grefl = 0.2;
426     double ccover = 0.0;
427     int res = 128;
428     double aod = AOD0_CA;
429     char *outname = "out";
430     char *mie_path = getpath("mie_ca.dat", getrlibpath(), R_OK);
431 greg 2.2 char mie_name[20] = "mie_ca";
432 greg 2.1 char lstag[3];
433 greg 2.2 char *ddir = ".";
434    
435     if (!strcmp(argv[1], "-defaults")) {
436     printf("-i %d\t\t\t\t#scattering order\n", sorder);
437     printf("-g %f\t\t\t#ground reflectance\n", grefl);
438     printf("-c %f\t\t\t#cloud cover\n", ccover);
439     printf("-r %d\t\t\t\t#image resolution\n", res);
440     printf("-d %f\t\t\t#broadband aerosol optical depth\n", AOD0_CA);
441     printf("-f %s\t\t\t\t#output name (-f)\n", outname);
442     printf("-p %s\t\t\t\t#atmos data directory\n", ddir);
443     exit(1);
444     }
445 greg 2.1
446     if (argc < 4) {
447 greg 2.2 fprintf(stderr,
448     "Usage: %s month day hour -y year -a lat -o lon -m tz -d aod -r "
449     "res -n nproc -c ccover -l mie -g grefl -f outpath\n",
450 greg 2.1 argv[0]);
451     return 0;
452     }
453    
454     month = atoi(argv[1]);
455 greg 2.2 if (month < 1 || month > 12) {
456     fprintf(stderr, "bad month");
457     exit(1);
458     }
459 greg 2.1 day = atoi(argv[2]);
460 greg 2.2 if (day < 1 || day > 31) {
461     fprintf(stderr, "bad month");
462     exit(1);
463     }
464     got_meridian = cvthour(argv[3], &tsolar, &hour);
465 greg 2.1
466     if (!compute_sundir(year, month, day, hour, tsolar, sundir)) {
467     fprintf(stderr, "Cannot compute solar angle\n");
468     exit(1);
469     }
470    
471     for (int i = 4; i < argc; i++) {
472     if (argv[i][0] == '-') {
473     switch (argv[i][1]) {
474     case 'a':
475     s_latitude = atof(argv[++i]) * (PI / 180.0);
476     break;
477     case 'c':
478     ccover = atof(argv[++i]);
479     break;
480     case 'd':
481     aod = atof(argv[++i]);
482     break;
483 greg 2.2 case 'f':
484     outname = argv[++i];
485     break;
486     case 'g':
487     grefl = atof(argv[++i]);
488     break;
489 greg 2.1 case 'i':
490     sorder = atoi(argv[++i]);
491     break;
492     case 'l':
493     mie_path = argv[++i];
494 greg 2.2 basename(mie_path, mie_name, sizeof(mie_name));
495 greg 2.1 break;
496     case 'm':
497 greg 2.2 if (got_meridian) {
498     ++i;
499     break;
500     }
501 greg 2.1 s_meridian = atof(argv[++i]) * (PI / 180.0);
502     break;
503     case 'n':
504     num_threads = atoi(argv[++i]);
505     break;
506 greg 2.2 case 'o':
507     s_longitude = atof(argv[++i]) * (PI / 180.0);
508 greg 2.1 break;
509 greg 2.2 case 'p':
510     ddir = argv[++i];
511 greg 2.1 break;
512     case 'r':
513     res = atoi(argv[++i]);
514     break;
515 greg 2.2 case 'y':
516     year = atoi(argv[++i]);
517     break;
518 greg 2.1 default:
519     fprintf(stderr, "Unknown option %s\n", argv[i]);
520     exit(1);
521     }
522     }
523     }
524 greg 2.2 if (year && (year < 1950) | (year > 2050))
525     fprintf(stderr, "%s: warning - year should be in range 1950-2050\n",
526     progname);
527     if (month && !tsolar && fabs(s_meridian - s_longitude) > 45 * PI / 180)
528     fprintf(stderr,
529     "%s: warning - %.1f hours btwn. standard meridian and longitude\n",
530     progname, (s_longitude - s_meridian) * 12 / PI);
531 greg 2.1
532     Atmosphere clear_atmos = init_atmos(aod, grefl);
533    
534     int is_summer = (month >= SUMMER_START && month <= SUMMER_END);
535     if (s_latitude < 0) {
536     is_summer = !is_summer;
537     }
538     set_rayleigh_density_profile(&clear_atmos, lstag, is_summer, s_latitude);
539    
540 greg 2.2 /* Load mie density data */
541 greg 2.1 DATARRAY *mie_dp = getdata(mie_path);
542     if (mie_dp == NULL) {
543     fprintf(stderr, "Error reading mie data\n");
544     return 0;
545     }
546     clear_atmos.beta_m = mie_dp;
547    
548 greg 2.2 char gsdir[PATH_MAX];
549     size_t siz = strlen(ddir);
550     if (ISDIRSEP(ddir[siz-1]))
551     ddir[siz-1] = '\0';
552     snprintf(gsdir, PATH_MAX, "%s%catmos_data", ddir, DIRSEP);
553     printf("gsdir: %s\n", gsdir);
554     if (!make_directory(gsdir)) {
555     fprintf(stderr, "Failed creating atmos_data directory");
556     exit(1);
557     }
558     DpPaths clear_paths = get_dppaths(gsdir, aod, mie_name, lstag);
559 greg 2.1
560     if (getpath(clear_paths.tau, ".", R_OK) == NULL ||
561     getpath(clear_paths.scat, ".", R_OK) == NULL ||
562     getpath(clear_paths.scat1m, ".", R_OK) == NULL ||
563     getpath(clear_paths.irrad, ".", R_OK) == NULL) {
564 greg 2.2 printf("# Pre-computing...\n");
565 greg 2.1 if (!precompute(sorder, clear_paths, &clear_atmos, num_threads)) {
566 greg 2.2 fprintf(stderr, "Pre-compute failed\n");
567 greg 2.1 return 0;
568     }
569     }
570    
571     DATARRAY *tau_clear_dp = getdata(clear_paths.tau);
572     DATARRAY *irrad_clear_dp = getdata(clear_paths.irrad);
573     DATARRAY *scat_clear_dp = getdata(clear_paths.scat);
574     DATARRAY *scat1m_clear_dp = getdata(clear_paths.scat1m);
575    
576 greg 2.2 write_header(argc, argv, ccover, grefl, res);
577    
578 greg 2.1 if (!gen_spect_sky(tau_clear_dp, scat_clear_dp, scat1m_clear_dp,
579     irrad_clear_dp, ccover, sundir, grefl, res, outname)) {
580     fprintf(stderr, "gen_spect_sky failed\n");
581     exit(1);
582     }
583    
584     freedata(mie_dp);
585     freedata(tau_clear_dp);
586     freedata(scat_clear_dp);
587     freedata(irrad_clear_dp);
588     freedata(scat1m_clear_dp);
589    
590     return 1;
591     }