3 /* Copyright (C) 2002 Brad Jorsch <anomie@users.sourceforge.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 # if TIME_WITH_SYS_TIME
24 # include <sys/time.h>
28 # include <sys/time.h>
44 #include "wmweather+.h"
50 #include "sunzenith.h"
54 /* Important variables */
55 static time_t metar_time
=0;
57 static char *metar_newfile
=NULL
;
58 static char *metar_file
=NULL
;
59 static char *metar_req
[2]={ NULL
, NULL
};
61 struct current_weather current
;
63 /* Regular Expressions */
64 static pcre
*station_time
;
69 static pcre
*pressure
;
73 static int parse_metar(char *file
);
77 static void reset_current(struct current_weather
*c
){
78 c
->last_update
=time(NULL
);
99 #define compile(var, re) \
100 var=pcre_compile(re, 0, (const char **)&e, &i, NULL); \
101 if(var==NULL) die("init_metar PCRE error: %s at %i", e, i); \
102 pcre_fullinfo(var, NULL, PCRE_INFO_CAPTURECOUNT, &i); \
103 if(i>ovecsize) ovecsize=i;
105 void init_metar(void){
108 struct subst_val subs
[]={
109 { 's', STRING
, &metar_station
},
113 snprintf(bigbuf
, BIGBUF_LEN
, "%s.metar.txt", metar_station
);
114 metar_file
=get_pid_filename(bigbuf
);
115 snprintf(bigbuf
, BIGBUF_LEN
, "%s.new-metar.txt", metar_station
);
116 metar_newfile
=get_pid_filename(bigbuf
);
118 if((metar_req
[0]=subst(metar_uri
, subs
))==NULL
) die("init_metar");
119 if(metar_post
!=NULL
&& (metar_req
[1]=subst(metar_post
, subs
))==NULL
) die("init_metar");
125 strncpy(bigbuf
, metar_station
, BIGBUF_LEN
-25);
126 bigbuf
[BIGBUF_LEN
-25]='\0';
127 strcat(bigbuf
, " ((?:\\d\\d)?)(\\d\\d\\d\\d)Z( .* )");
128 compile(station_time
, bigbuf
);
129 compile(wind
, " (VRB|\\d\\d\\d)(\\d\\d\\d?)(?:G\\d\\d\\d?)?(KT|MPS|KMH)((?: \\d\\d\\dV\\d\\d\\d)?) ");
130 compile(weather
, " ((?:-|\\+|VC)?)((?:MI|PR|BC|DR|BL|SH|TS|FZ)?)((?:DZ|RA|SN|SG|IC|PE|PL|GR|GS|UP){0,3})((?:BR|FG|FU|VA|DU|SA|HZ|PY)?)((?:PO|SQ|FC|SS|DS)?)\\b");
131 compile(vis
[0], " (\\d+)SM ");
132 compile(vis
[1], " (\\d+)/(\\d+)SM ");
133 compile(vis
[2], " (\\d+) (\\d+)/(\\d+)SM ");
134 compile(vis
[3], " (\\d{4})[NS]?[EW]? ");
135 compile(temp
, " (M?\\d\\d\\d?)/((?:M?\\d\\d\\d?)?) ");
136 compile(pressure
, " ([AQ])(\\d\\d\\d\\d) ");
138 ovecsize
=(ovecsize
+1)*3;
140 /* Remove stale file */
142 unlink(metar_newfile
);
143 reset_current(¤t
);
144 current
.last_update
= 0; // This was not a real "update", just an init
148 static void metar_callback(char *filename
, void *v
){
151 if(stat(metar_newfile
, &statbuf
)>=0){
152 if(S_ISREG(statbuf
.st_mode
) && statbuf
.st_size
!=0
153 && parse_metar(metar_newfile
)){
154 rename(metar_newfile
, metar_file
);
156 unlink(metar_newfile
);
157 if(!parse_metar(metar_file
)) reset_current(¤t
);
161 update_warnings(v
!=NULL
);
164 void metar_cleanup(void){
165 unlink(metar_newfile
);
169 void update_metar(int force
){
173 if(!force
&& metar_time
>t
) return;
176 download_file(metar_newfile
, metar_req
[0], metar_req
[1], force
?DOWNLOAD_KILL_OTHER_REQUESTS
:0, metar_callback
, force
?"":NULL
);
180 #define get_substr(n, c) \
181 if(pcre_get_substring(s, ovector, ovalue, n, (const char **)&c)<0){ pcre_free_substring(s); return 0; }
183 static int parse_metar(char *file
){
186 int ovector
[ovecsize
];
192 reset_current(¤t
);
193 if((fp
=fopen(file
, "r"))==NULL
) return 0;
194 len
=fread(bigbuf
, sizeof(char), BIGBUF_LEN
-2, fp
);
197 for(i
=0; i
<len
; i
++){
198 if(isspace(bigbuf
[i
])) bigbuf
[i
]=' ';
200 c
=strstr(bigbuf
, " RMK");
201 if(c
!=NULL
) *(c
+1)='\0';
202 c
=strstr(bigbuf
, " TEMPO");
203 s
=strstr(bigbuf
, " BECMG");
204 if(c
!=NULL
) *(c
+1)='\0';
205 if(s
!=NULL
) *(s
+1)='\0';
206 /* XXX: parse trend forecast data? */
209 if(bigbuf
[len
-1]!=' '){
214 /* Look for something like a METAR coded report */
215 ovalue
=pcre_exec(station_time
, NULL
, bigbuf
, len
, 0, 0, ovector
, ovecsize
);
216 if(ovalue
<=0) return 0;
217 if(pcre_get_substring(bigbuf
, ovector
, ovalue
, 1, (const char **)&c
)<0) return 0;
218 if(c
[0]!='\0') current
.date
=atoi(c
);
219 pcre_free_substring(c
);
220 if(pcre_get_substring(bigbuf
, ovector
, ovalue
, 2, (const char **)&c
)<0) return 0;
221 current
.time
=atoi(c
);
222 pcre_free_substring(c
);
224 /* Chop off extraneous stuff */
225 if(pcre_get_substring(bigbuf
, ovector
, ovalue
, 3, (const char **)&s
)<0) return 0;
227 /* windspeed, winddir */
228 ovalue
=pcre_exec(wind
, NULL
, s
, len
, 0, 0, ovector
, ovecsize
);
234 pcre_free_substring(c
);
236 if(c
[0]=='V') current
.winddir
=0;
237 else current
.winddir
=((int)((atoi(c
)+11.25)/22.5))%16+1;
239 pcre_free_substring(c
);
241 current
.windspeed
=atoi(c
);
242 pcre_free_substring(c
);
244 if(c
[0]=='M'){ /* MPS */
245 current
.windspeed
=mps2knots(current
.windspeed
);
246 } else if(c
[0]=='K' && c
[1]=='M'){ /* KMH */
247 current
.windspeed
=kph2knots(current
.windspeed
);
253 c
=strstr(s
, " M1/4SM ");
258 ovalue
=pcre_exec(vis
[2], NULL
, s
, len
, 0, 0, ovector
, ovecsize
);
262 pcre_free_substring(c
);
265 pcre_free_substring(c
);
267 f
=atoi(c
)+(float)i
/j
;
268 pcre_free_substring(c
);
271 ovalue
=pcre_exec(vis
[1], NULL
, s
, len
, 0, 0, ovector
, ovecsize
);
275 pcre_free_substring(c
);
278 pcre_free_substring(c
);
281 ovalue
=pcre_exec(vis
[0], NULL
, s
, len
, 0, 0, ovector
, ovecsize
);
285 pcre_free_substring(c
);
288 c
=strstr(s
, " CAVOK ");
294 ovalue
=pcre_exec(vis
[3], NULL
, s
, len
, 0, 0, ovector
, ovecsize
);
298 pcre_free_substring(c
);
302 if(f
<=6) current
.vis
=6;
303 if(f
<=5) current
.vis
=5;
304 if(f
<3) current
.vis
=4;
305 if(f
<1) current
.vis
=3;
306 if(f
<=.5) current
.vis
=2;
307 if(f
<=.25) current
.vis
=1;
310 ovalue
=pcre_exec(temp
, NULL
, s
, len
, 0, 0, ovector
, ovecsize
);
313 if(c
[0]=='M') c
[0]='-';
314 current
.temp
=atoi(c
);
315 pcre_free_substring(c
);
318 if(c
[0]=='M') c
[0]='-';
319 current
.rh
=rh_C(current
.temp
, atoi(c
));
321 pcre_free_substring(c
);
325 ovalue
=pcre_exec(pressure
, NULL
, s
, len
, 0, 0, ovector
, ovecsize
);
329 pcre_free_substring(c
);
332 current
.pressure
=hPa2inHg(i
);
334 current
.pressure
=i
/100.0;
336 pcre_free_substring(c
);
340 if(strstr(s
, " SKC")!=NULL
|| strstr(s
, " CLR")!=NULL
) current
.sky
=0;
341 if(strstr(s
, " FEW")!=NULL
) current
.sky
=1;
342 if(strstr(s
, " SCT")!=NULL
) current
.sky
=2;
343 if(strstr(s
, " BKN")!=NULL
) current
.sky
=3;
344 if(strstr(s
, " OVC")!=NULL
|| strstr(s
, " VV")!=NULL
) current
.sky
=4;
346 /* obs, frz, snow, rain, tstorm */
347 /* There can be multiple weather chunks, so we while loop */
349 while((ovalue
=pcre_exec(weather
, NULL
, s
, len
, j
, 0, ovector
, ovecsize
))>0){{
350 char *in
, *de
, *pp
, *ob
, *ot
;
355 pcre_free_substring(c
);
365 #define IN(haystack, needle) ((needle[0]=='\0')?0:strstr(haystack, needle))
366 if(current
.obs
<1 && strcmp(de
, "FZ") && IN("BR|FG", ob
))
368 if(current
.obs
<2 && IN("FU|VA|DU|SA|HZ|PY", ob
))
370 if(current
.obs
<3 && IN("PO|SS|DS", ot
))
372 if(current
.obs
<3 && IN("DR|BL", de
)
373 && (strstr(pp
, "SN") || IN("DU|SA|PY", ob
)))
375 if(!strcmp(ot
, "FC")){
383 if(in
[0]=='-' || in
[0]=='V') i
=33;
385 if(!strcmp(de
, "SH")) i
=33;
387 && ((!strcmp(de
, "FZ") && (strstr(pp
, "DZ") || strstr(pp
, "RA")))
388 || strstr(pp
, "IC") || strstr(pp
, "PE") || strstr(pp
, "PL")
389 || strstr(pp
, "GR") || strstr(pp
, "GS")))
391 if(current
.snow
<i
&& strcmp(de
, "BL")
392 && (strstr(pp
, "SN") || strstr(pp
, "SG")))
394 if(current
.rain
<i
&& (strstr(pp
, "UP")
396 && (strstr(pp
, "DZ") || strstr(pp
, "RA")))))
398 if(current
.tstorm
<i
&& !strcmp(de
, "TS"))
401 pcre_free_substring(in
);
402 pcre_free_substring(de
);
403 pcre_free_substring(pp
);
404 pcre_free_substring(ob
);
405 pcre_free_substring(ot
);
407 if(current
.obs
==99) current
.obs
=0;
409 pcre_free_substring(s
); /* Done parsing! Just a few final calculations... */
411 current
.heatindex
=heatindex_C(current
.temp
, current
.rh
);
412 current
.windchill
=windchill_C(current
.temp
, current
.windspeed
);
414 /* Figure out the proper month... */
416 int mon
, day
, year
, time2
; /* holds UTC */
417 int y
; /* with current.*, holds local time */
419 struct tm
*tm
=gmtime(&t
);
420 current
.month
=tm
->tm_mon
+1;
421 if(tm
->tm_mday
<current
.date
) current
.month
--;
422 if(current
.month
<1){ current
.month
+=12; tm
->tm_year
--; }
427 current
.time
=utc2local((int)current
.time
, ¤t
.month
, ¤t
.date
, &y
, NULL
);
429 if(latitude
!=999 && calcSolarZenith(latitude
, longitude
, year
, mon
, day
, hm2min(time2
))>90)
430 current
.moon
=calc_moon(current
.month
, current
.date
, y
, current
.time
);