ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/gen/loadEPW.c
Revision: 2.3
Committed: Tue Mar 4 17:45:41 2025 UTC (8 weeks, 3 days ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.2: +2 -2 lines
Log Message:
fix(gendaymtx): Somehow missed "May" in month list!

File Contents

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