ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/loadEPW.c
Revision: 2.1
Committed: Wed Feb 26 20:39:28 2025 UTC (2 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
feat(gendaymtx): Added ability to read dew point from EPW input data

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2     static const char RCSid[] = "$Id$";
3     #endif
4     /*
5     * Load an EPW (or WEA) file, one data point at a time
6     *
7     * G. Ward, Feb 2025
8     */
9    
10     #include "rtio.h"
11     #include <stdlib.h>
12     #include <ctype.h>
13     #include "loadEPW.h"
14    
15     const char WDname[7][10] = {
16     "Sunday", "Monday", "Tuesday", "Wednesday",
17     "Thursday", "Friday", "Saturday"
18     };
19    
20     const char MOname[12][10] = {
21     "January", "February", "March", "April", "June", "July",
22     "August", "September", "October", "November", "December"
23     };
24    
25     #define EPWDATE_INIT {.0f,0,0,0}
26    
27     static const struct DataPeriod EPWperiodInit = {
28     "DEFAULT", 1, Sunday, {.0f, 1, 0, 0}, {24.f, 31, 11, 0}
29     };
30    
31     const EPWrecord EPWrecInit = {
32     EPWDATE_INIT, "", 99.9f, 99.9f, 999.f, 999999.f, 9999.f, 9999.f, 9999.f,
33     9999.f, 9999.f, 9999.f, 999999.f, 999999.f, 999999.f, 9999.f, 999.f, 999.f,
34     99.f, 99.f, 9999e3f, 99999.f, 999e-3f, 999e-3f, 999e-2f, 99,
35     .0f, 1.5e-3f, .0f
36     };
37    
38     #define EPWdateInit (EPWrecInit.date)
39    
40     static const short mo_da[13] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
41    
42     /* Get day-of-week from name (may be abbreviated) */
43     static int
44     get_day(const char dnm[])
45     {
46     int n = strlen(dnm);
47     int i;
48    
49     if (n < 3)
50     return(-1);
51    
52     for (i = 0; i < 7; i++)
53     if (!strncasecmp(dnm, WDname[i], n))
54     return(i);
55     return(-1);
56     }
57    
58     /* Hour difference calculator dt1 - dt2 (crude) */
59     static double
60     hour_diff(const EPWdate *dt1, const EPWdate *dt2)
61     {
62     double hrdiff = dt1->hour - dt2->hour;
63    
64     hrdiff += 24.*(mo_da[dt1->month] + dt1->day - mo_da[dt2->month] - dt2->day);
65    
66     if ((dt1->year > 0) & (dt2->year > 0))
67     hrdiff += 365.*24.*(dt1->year - dt2->year);
68    
69     return(hrdiff);
70     }
71    
72     /* Check that a date/time is legal */
73     static int
74     date_OK(const EPWdate *dt)
75     {
76     if ((0 > dt->month) | (dt->month > 11) |
77     (0 > dt->hour) | (dt->hour > 24) | (dt->day < 1))
78     return(0);
79    
80     return(dt->day <= (dt->month==1 ? 29 :
81     mo_da[dt->month+1] - mo_da[dt->month]));
82     }
83    
84     /* Parse date (less hour) */
85     static int
86     get_date(EPWdate *dt, const char date[])
87     {
88     int mo, da, yr;
89     int nf;
90    
91     *dt = EPWdateInit;
92     switch (sscanf(date, "%d/%d/%d", &mo, &da, &yr)) {
93     case 1:
94     da = mo; /* jdate */
95     if (da <= 0)
96     return(0);
97     for (mo = 0; mo_da[mo+1] < da; mo++)
98     if (mo >= 11)
99     return(0);
100     dt->day = da - mo_da[dt->month = mo];
101     return(1);
102     case 3: /* month/day/year */
103     dt->year = yr;
104     /* fall through */
105     case 2: /* month/day */
106     dt->month = mo-1;
107     dt->day = da;
108     return(date_OK(dt));
109     }
110     return(0);
111     }
112    
113     /* Check that site data is sensible */
114     static int
115     bad_location(const struct Location *lp)
116     {
117     if ((-90. > lp->latitude) | (lp->latitude > 90.)) {
118     fputs("Bad latitude in ", stderr);
119     return(1);
120     }
121     if ((-180. > lp->longitude) | (lp->longitude > 180.)) {
122     fputs("Bad longitude in ", stderr);
123     return(2);
124     }
125     if ((-12. > lp->timezone) | (lp->timezone > 12.)) {
126     fputs("Bad time zone in ", stderr);
127     return(3);
128     }
129     if ((-1000. > lp->elevation) | (lp->elevation > 9999.9)) {
130     fputs("Bad elevation in ", stderr);
131     return(4);
132     }
133     return(0);
134     }
135    
136     /* Copy word to next comma in string */
137     static char *
138     tocomma(char *dst, char *src)
139     {
140     while (*src && *src != ',')
141     *dst++ = *src++;
142     src += (*src == ',');
143     *dst = '\0';
144     return(src);
145     }
146    
147     /* Use fscanf() to load next date and time from input */
148     static int
149     scan_date(EPWheader *epw)
150     {
151     int hour;
152     float minute;
153    
154     ++epw->lino;
155     if (epw->isWEA) { /* simpler for WEA input */
156     epw->dtpos = EPWdateInit;
157     if (fscanf(epw->fp, "%hd %hd %f", &epw->dtpos.month, &epw->dtpos.day,
158     &epw->dtpos.hour) != 3)
159     goto scanerr;
160     epw->dtpos.month--;
161     } else {
162     /* EPW input line */
163     if (fscanf(epw->fp, "%hd,%hd,%hd,%d,%f,", &epw->dtpos.year,
164     &epw->dtpos.month, &epw->dtpos.day, &hour, &minute) != 5)
165     goto scanerr;
166     epw->dtpos.hour = hour - minute*(0.5/60.);
167     epw->dtpos.month--;
168     }
169     /* check date/time */
170     if (!date_OK(&epw->dtpos)) {
171     fputs("Illegal date/time in ", stderr);
172     return(0);
173     }
174     return(1);
175     scanerr:
176     if (!feof(epw->fp))
177     fputs("Unable to scan date in ", stderr);
178     return(0);
179     }
180    
181     /* Open input file and load header info. */
182     EPWheader *
183     EPWopen(const char *fname)
184     {
185     char linbuf[1024], wbuf[64];
186     char *cp;
187     int n;
188     EPWheader *hdr = (EPWheader *)calloc(1, sizeof(EPWheader));
189    
190     if (hdr == NULL)
191     goto memerr;
192     if (fname == NULL || !*fname) {
193     fname = "<stdin>";
194     hdr->fp = stdin;
195     } else if ((hdr->fp = fopen(fname, "r")) == NULL) {
196     perror(fname);
197     free(hdr);
198     return(NULL);
199     }
200     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
201     goto readerr;
202     ++hdr->lino;
203     if (!strncasecmp(linbuf, "place ", 6))
204     hdr->isWEA = 1;
205     else if (strncasecmp(linbuf, "LOCATION,", 9))
206     goto badformat;
207     if (hdr->isWEA) { /* getting WEA header */
208     cp = linbuf+6;
209     if (sscanf(cp, "%[^_]_%[^\r\n]q",
210     hdr->loc.city, hdr->loc.country) != 2)
211     goto badformat;
212     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
213     goto readerr;
214     ++hdr->lino;
215     if (sscanf(linbuf, "latitude %lf", &hdr->loc.latitude) != 1)
216     goto badformat;
217     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
218     goto readerr;
219     ++hdr->lino;
220     if (sscanf(linbuf, "longitude %lf", &hdr->loc.longitude) != 1)
221     goto badformat;
222     hdr->loc.longitude *= -1.;
223     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
224     goto readerr;
225     ++hdr->lino;
226     if (sscanf(linbuf, "time_zone %f", &hdr->loc.timezone) != 1)
227     goto badformat;
228     hdr->loc.timezone *= -1./15.;
229     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
230     goto readerr;
231     ++hdr->lino;
232     if (sscanf(linbuf, "site_elevation %f", &hdr->loc.elevation) != 1)
233     goto badformat;
234     if (bad_location(&hdr->loc))
235     goto badformat;
236     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
237     goto readerr;
238     ++hdr->lino;
239     if (sscanf(linbuf, "weather_data_file_units %hd", &hdr->isWEA) != 1)
240     goto badformat;
241     hdr->nperiods = 1;
242     hdr->period[0] = EPWperiodInit;
243     hdr->dstart = (int)ftell(hdr->fp);
244     hdr->lin0 = hdr->lino;
245     if (!scan_date(hdr)) /* get date */
246     goto badformat;
247     return(hdr);
248     } /* else EPW header */
249     cp = linbuf+9;
250     cp = tocomma(hdr->loc.city, cp);
251     if (!*cp) goto badformat;
252     cp = tocomma(hdr->loc.state, cp);
253     if (!*cp) goto badformat;
254     cp = tocomma(hdr->loc.country, cp);
255     if (!*cp) goto badformat;
256     cp = tocomma(hdr->loc.source, cp);
257     if (!*cp) goto badformat;
258     cp = tocomma(hdr->loc.wmo, cp);
259     if (sscanf(cp, "%lf,%lf,%f,%f", &hdr->loc.latitude, &hdr->loc.longitude,
260     &hdr->loc.timezone, &hdr->loc.elevation) != 4)
261     goto badformat;
262     if (bad_location(&hdr->loc))
263     goto badformat;
264     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
265     goto readerr;
266     ++hdr->lino;
267     if (strncasecmp(linbuf, "DESIGN CONDITIONS,", 18))
268     goto badformat;
269     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
270     goto readerr;
271     ++hdr->lino;
272     if (strncasecmp(linbuf, "TYPICAL/EXTREME PERIODS,", 24))
273     goto badformat;
274     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
275     goto readerr;
276     ++hdr->lino;
277     if (strncasecmp(linbuf, "GROUND TEMPERATURES,", 20))
278     goto badformat;
279     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
280     goto readerr;
281     ++hdr->lino;
282     if (strncasecmp(linbuf, "HOLIDAYS/DAYLIGHT SAVINGS,", 26))
283     goto badformat;
284     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
285     goto readerr;
286     ++hdr->lino;
287     if (strncasecmp(linbuf, "COMMENTS 1,", 11))
288     goto badformat;
289     n = strlen(linbuf+11);
290     if (n > 1) {
291     hdr->comments1 = (char *)malloc(n);
292     if (hdr->comments1 == NULL)
293     goto memerr;
294     memcpy(hdr->comments1, linbuf+11, n);
295     hdr->comments1[n] = '\0';
296     }
297     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
298     goto readerr;
299     ++hdr->lino;
300     if (strncasecmp(linbuf, "COMMENTS 2,", 11))
301     goto badformat;
302     n = strlen(linbuf+11);
303     if (n > 1) {
304     hdr->comments2 = (char *)malloc(n);
305     if (hdr->comments2 == NULL)
306     goto memerr;
307     memcpy(hdr->comments2, linbuf+11, n);
308     hdr->comments2[n] = '\0';
309     }
310     if (!fgets(linbuf, sizeof(linbuf), hdr->fp))
311     goto readerr;
312     ++hdr->lino;
313     if (strncasecmp(linbuf, "DATA PERIODS,", 13))
314     goto badformat;
315     hdr->nperiods = 1; /* XXX one for now */
316     if (atoi(linbuf+13) != 1)
317     fprintf(stderr, "%s: warning - ignoring data periods after first\n",
318     fname);
319     cp = strchr(linbuf+13, ',');
320     if (!cp++) goto badformat;
321     if ((hdr->period[0].nperhour = atoi(cp)) <= 0)
322     goto badformat;
323     cp = strchr(cp, ',');
324     if (!cp++) goto badformat;
325     cp = tocomma(hdr->period[0].name, cp);
326     if (!*cp) goto badformat;
327     cp = tocomma(wbuf, cp);
328     if ((hdr->period[0].startday = get_day(wbuf)) < 0)
329     goto badformat;
330     if (!*cp) goto badformat;
331     cp = tocomma(wbuf, cp);
332     if (!get_date(&hdr->period[0].firstdate, wbuf))
333     goto badformat;
334     if (!*cp) goto badformat;
335     cp = tocomma(wbuf, cp);
336     if (!get_date(&hdr->period[0].lastdate, wbuf))
337     goto badformat;
338     hdr->period[0].lastdate.hour = 24.f;
339     hdr->dstart = (int)ftell(hdr->fp);
340     hdr->lin0 = hdr->lino;
341     if (!scan_date(hdr)) /* and get/check date */
342     goto badformat;
343     if ((hdr->dtpos.month != hdr->period[0].firstdate.month) |
344     (hdr->dtpos.day != hdr->period[0].firstdate.day))
345     fprintf(stderr, "%s: warning - starting date does not match first record\n",
346     fname);
347     return(hdr); /* seems we're good! */
348     memerr:
349     perror("malloc");
350     if (hdr) {
351     if (hdr->fp != stdin) fclose(hdr->fp);
352     free(hdr);
353     }
354     return(NULL);
355     readerr:
356     fprintf(stderr, "%s: header read error at line %d\n", fname, hdr->lino);
357     EPWclose(hdr);
358     return(NULL);
359     badformat:
360     fprintf(stderr, "%s: header format error in %s file at line %d\n",
361     fname, hdr->isWEA ? "WEA" : "EPW", hdr->lino);
362     EPWclose(hdr);
363     return(NULL);
364     }
365    
366     /* Seek to a particular data record if we can */
367     int
368     EPWseek(EPWheader *epw, const EPWdate *dt)
369     {
370     if (!epw | !dt || !epw->fp || !date_OK(dt))
371     return(EOF);
372     /* need to back up? */
373     if (feof(epw->fp) || hour_diff(&epw->dtpos, dt) > 0.99) {
374     if (epw->dstart < 0 || fseek(epw->fp, epw->dstart, SEEK_SET) < 0) {
375     if (epw->fp == stdin)
376     fputs("Cannot seek on standard input\n", stderr);
377     else
378     fprintf(stderr, "Seek error on %s input file\n",
379     epw->isWEA ? "WEA" : "EPW");
380     return(EOF);
381     }
382     epw->lino = epw->lin0;
383     scan_date(epw); /* assume this much works! */
384     }
385     /* step through earlier records */
386     while (hour_diff(&epw->dtpos, dt) < -.99) {
387     int c;
388     while ((c = getc(epw->fp)) != EOF)
389     if (c == '\n') /* skip record data */
390     break;
391     if (scan_date(epw)) /* load next date/time */
392     continue;
393     if (feof(epw->fp))
394     fputs("Requested date/time not found in ", stderr);
395     fputs(epw->isWEA ? "WEA" : "EPW", stderr);
396     fputs(" input\n", stderr);
397     return(0);
398     }
399     return(1);
400     }
401    
402     /* Read the next data record from input */
403     int
404     EPWread(EPWheader *epw, EPWrecord *rp)
405     {
406     char linbuf[1024];
407     char *cp;
408    
409     if (!epw | !rp || feof(epw->fp))
410     return(EOF);
411    
412     *rp = EPWrecInit; /* initialize to defaults */
413     rp->date = epw->dtpos; /* date already read in */
414     if (!fgets(linbuf, sizeof(linbuf), epw->fp)) {
415     fputs("Unexpected EOF on input\n", stderr);
416     return(0);
417     }
418     switch (epw->isWEA) { /* check for WEA input */
419     case WEAnot: /* nope */
420     break;
421     case WEAradnorm: /* radiometric w/ direct normal */
422     if (sscanf(linbuf, "%f %f", &rp->dirirrad,
423     &rp->horizdiffirrad) != 2)
424     goto badformat;
425     break;
426     case WEAradhoriz: /* radiometric w/ direct horizontal */
427     if (sscanf(linbuf, "%f %f", &rp->globhorizirrad,
428     &rp->horizdiffirrad) != 2)
429     goto badformat;
430     rp->globhorizirrad += rp->horizdiffirrad;
431     break;
432     case WEAphotnorm: /* photometric w/ direct normal */
433     if (sscanf(linbuf, "%f %f", &rp->dirillum,
434     &rp->diffillum) != 2)
435     goto badformat;
436     break;
437     default:
438     fputs("Illegal WEA data type\n", stderr);
439     return(0);
440     }
441     if (epw->isWEA) {
442     if (sscanf(linbuf, "%*f %*f %f %f",
443     &rp->optdepth, &rp->skycover) == 2) {
444     rp->optdepth *= 1e-3;
445     }
446     if (!scan_date(epw) && !feof(epw->fp))
447     goto badformat;
448     return(1);
449     }
450     /* continue here if EPW file */
451     cp = tocomma(rp->uncert, linbuf);
452     if (!*cp) goto badformat;
453     if (*cp != ',') {
454     rp->dbtemp = atof(cp);
455     cp = strchr(cp, ',');
456     }
457     if (!cp++) goto badformat;
458     if (*cp != ',') {
459     rp->dptemp = atof(cp);
460     cp = strchr(cp, ',');
461     }
462     if (!cp++) goto badformat;
463     if (*cp != ',') {
464     rp->humidity = atof(cp);
465     cp = strchr(cp, ',');
466     }
467     if (!cp++) goto badformat;
468     if (*cp != ',') {
469     rp->atmospressure = atof(cp);
470     cp = strchr(cp, ',');
471     }
472     if (!cp++) goto badformat;
473     if (*cp != ',') {
474     rp->exthorizirrad = atof(cp);
475     cp = strchr(cp, ',');
476     }
477     if (!cp++) goto badformat;
478     if (*cp != ',') {
479     rp->extdirirrad = atof(cp);
480     cp = strchr(cp, ',');
481     }
482     if (!cp++) goto badformat;
483     if (*cp != ',') {
484     rp->horizirrad_ir = atof(cp);
485     cp = strchr(cp, ',');
486     }
487     if (!cp++) goto badformat;
488     if (*cp != ',') {
489     rp->globhorizirrad = atof(cp);
490     cp = strchr(cp, ',');
491     }
492     if (!cp++) goto badformat;
493     if (*cp != ',') {
494     rp->dirirrad = atof(cp);
495     cp = strchr(cp, ',');
496     }
497     if (!cp++) goto badformat;
498     if (*cp != ',') {
499     rp->horizdiffirrad = atof(cp);
500     cp = strchr(cp, ',');
501     }
502     if (!cp++) goto badformat;
503     if (*cp != ',') {
504     rp->globhorizillum = atof(cp);
505     if (rp->globhorizillum >= 999900.f)
506     rp->globhorizillum = EPWrecInit.globhorizillum;
507     cp = strchr(cp, ',');
508     }
509     if (!cp++) goto badformat;
510     if (*cp != ',') {
511     rp->diffillum = atof(cp);
512     if (rp->diffillum >= 999900.f)
513     rp->diffillum = EPWrecInit.diffillum;
514     cp = strchr(cp, ',');
515     }
516     if (!cp++) goto badformat;
517     if (*cp != ',') {
518     rp->dirillum = atof(cp);
519     if (rp->dirillum >= 999900.f)
520     rp->dirillum = EPWrecInit.dirillum;
521     cp = strchr(cp, ',');
522     }
523     if (!cp++) goto badformat;
524     if (*cp != ',') {
525     rp->zenlum = atof(cp);
526     if (rp->zenlum >= 9999.f)
527     rp->zenlum = EPWrecInit.zenlum;
528     cp = strchr(cp, ',');
529     }
530     if (!cp++) goto badformat;
531     if (*cp != ',') {
532     rp->windirection = atof(cp);
533     cp = strchr(cp, ',');
534     }
535     if (!cp++) goto badformat;
536     if (*cp != ',') {
537     rp->windspeed = atof(cp);
538     cp = strchr(cp, ',');
539     }
540     if (!cp++) goto badformat;
541     if (*cp != ',') {
542     rp->skycover = atof(cp) * 0.1;
543     cp = strchr(cp, ',');
544     }
545     if (!cp++) goto badformat;
546     if (*cp != ',') {
547     rp->opskycover = atof(cp) * 0.1;
548     cp = strchr(cp, ',');
549     }
550     if (!cp++) goto badformat;
551     if (*cp != ',') {
552     rp->visibility = atof(cp) * 1000.;
553     cp = strchr(cp, ',');
554     }
555     if (!cp++) goto badformat;
556     if (*cp != ',') {
557     rp->ceilheight = atof(cp);
558     cp = strchr(cp, ',');
559     }
560     if (!cp++) goto badformat;
561     cp = strchr(cp, ',');
562     if (!cp++) goto badformat;
563     cp = strchr(cp, ',');
564     if (!cp++) goto badformat;
565     if (*cp != ',') {
566     rp->precip = atof(cp) * 1e-3;
567     cp = strchr(cp, ',');
568     }
569     if (!cp++) goto badformat;
570     if (*cp != ',') {
571     rp->optdepth = atof(cp) * 1e-3;
572     cp = strchr(cp, ',');
573     }
574     if (!cp++) goto badformat;
575     if (*cp != ',') {
576     rp->snowdepth = atof(cp) * 1e-2;
577     cp = strchr(cp, ',');
578     }
579     if (!cp++) goto badformat;
580     if (*cp != ',') {
581     rp->nosnowdays = atoi(cp);
582     cp = strchr(cp, ',');
583     }
584     if (!cp++) goto badformat;
585     if (*cp != ',') {
586     rp->albedo = atof(cp);
587     cp = strchr(cp, ',');
588     }
589     if (!cp++) goto badformat;
590     if (*cp != ',') {
591     rp->liqpdepth = atof(cp) * 1e-3;
592     cp = strchr(cp, ',');
593     }
594     if (!cp++) goto badformat;
595     if ((*cp != ',') & (*cp != '\n'))
596     rp->liqhours = atof(cp);
597     if (scan_date(epw) || feof(epw->fp))
598     return(1); /* normal return (even if next is EOF) */
599     badformat:
600     fprintf(stderr, "%s file, format error at line %d\n",
601     epw->isWEA ? "WEA" : "EPW", epw->lino);
602     return(0);
603     }
604    
605     /* Close input and free header data */
606     void
607     EPWclose(EPWheader *epw)
608     {
609     if (!epw) return;
610     if (epw->fp != stdin) fclose(epw->fp);
611     if (epw->comments1) free(epw->comments1);
612     if (epw->comments2) free(epw->comments2);
613     free(epw);
614     return;
615     }
616    
617     #ifdef TEST_MAIN
618     int
619     main(int argc, char *argv[])
620     {
621     EPWheader *epw = EPWopen(argv[1]);
622     EPWrecord erec;
623     int rval;
624    
625     if (!epw) return(1);
626    
627     printf("Weather input file type is %d\n", epw->isWEA);
628     printf("Location is %s, %s\n", epw->loc.city, epw->loc.country);
629     printf("Time zone is %.2f hours from GMT\n", epw->loc.timezone);
630     printf("Latitude, longitude is (%.8f,%.8f) degrees (N,E)\n",
631     epw->loc.latitude, epw->loc.longitude);
632     printf("Elevation is %.0f meters\n", epw->loc.elevation);
633     printf("'%s' starts %d/%d on a %s and ends %d/%d\n", epw->period[0].name,
634     epw->period[0].firstdate.month+1, epw->period[0].firstdate.day,
635     WDname[epw->period[0].startday],
636     epw->period[0].lastdate.month+1, epw->period[0].lastdate.day);
637     while ((rval = EPWread(epw, &erec)) > 0) {
638     printf("Weather record for %d/%d/%d @ %.2f hours:\n", erec.date.month+1,
639     erec.date.day, erec.date.year, erec.date.hour);
640     if (EPWisset(&erec,dbtemp))
641     printf("\tDry bulb temp: %.1f °C\n", erec.dbtemp);
642     if (EPWisset(&erec,dptemp))
643     printf("\tDew point temp: %.1f °C\n", erec.dptemp);
644     if (EPWisset(&erec,humidity))
645     printf("\tHumidity: %.1f%%\n", erec.humidity);
646     if (EPWisset(&erec,atmospressure))
647     printf("\tAtmospheric pressure: %.0f Pa\n", erec.atmospressure);
648     if (EPWisset(&erec,exthorizirrad))
649     printf("\tExtraterrestrial horiz irrad: %.1f w/m2\n", erec.exthorizirrad);
650     if (EPWisset(&erec,horizirrad_ir))
651     printf("\tInfrared direct normal irrad: %.1f w/m2\n", erec.horizirrad_ir);
652     if (EPWisset(&erec,globhorizirrad))
653     printf("\tGlobal horiz irrad: %.1f w/m2\n", erec.globhorizirrad);
654     if (EPWisset(&erec,dirirrad))
655     printf("\tDirect normal irrad: %.1f w/m2\n", erec.dirirrad);
656     if (EPWisset(&erec,horizdiffirrad))
657     printf("\tHorizontal diffuse irrad: %.1f w/m2\n", erec.horizdiffirrad);
658     if (EPWisset(&erec,globhorizillum))
659     printf("\tGlobal horizontal illuminance: %.0f lux\n", erec.globhorizillum);
660     if (EPWisset(&erec,diffillum))
661     printf("\tDiffuse horizontal illuminance: %.0f lux\n", erec.diffillum);
662     if (EPWisset(&erec,dirillum))
663     printf("\tDirect normal illuminance: %.0f lux\n", erec.dirillum);
664     if (EPWisset(&erec,zenlum))
665     printf("\tZenith luminance: %.0f nits\n", erec.zenlum);
666     if (EPWisset(&erec,windirection))
667     printf("\tWind direction: %.1f °E of S\n", erec.windirection);
668     if (EPWisset(&erec,windspeed))
669     printf("\tWind speed: %.2f m/s\n", erec.windspeed);
670     if (EPWisset(&erec,skycover))
671     printf("\tSky cover fraction: %.3f\n", erec.skycover);
672     if (EPWisset(&erec,opskycover))
673     printf("\tOpaque sky cover fraction: %.3f\n", erec.opskycover);
674     if (EPWisset(&erec,visibility))
675     printf("\tVisibility: %.0f meters\n", erec.visibility);
676     if (EPWisset(&erec,ceilheight))
677     printf("\tCloud ceiling height: %.0f meters\n", erec.ceilheight);
678     if (EPWisset(&erec,precip))
679     printf("\tPrecipitation: %f meters\n", erec.precip);
680     if (EPWisset(&erec,optdepth))
681     printf("\tAerosol optical depth: %.4f\n", erec.optdepth);
682     if (EPWisset(&erec,snowdepth))
683     printf("\tSnow depth: %.2f meters\n", erec.snowdepth);
684     if (EPWisset(&erec,nosnowdays))
685     printf("\tDays since last snow: %d\n", erec.nosnowdays);
686     if (EPWisset(&erec,albedo))
687     printf("\tAlbedo: %.4f\n", erec.albedo);
688     if (EPWisset(&erec,liqpdepth))
689     printf("\tLiquid precipitation depth: %.6f meters\n", erec.liqpdepth);
690     if (EPWisset(&erec,liqhours))
691     printf("\tLiquid precipitation time: %.1f hours\n", erec.liqhours);
692     }
693     EPWclose(epw);
694     return(!rval);
695     }
696     #endif /* TEST_MAIN */