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
28 #include "wmweather+.h"
35 #include "sunzenith.h"
39 /* Important variables */
41 static time_t avn_time
=0;
42 static char *avn_file
=NULL
;
43 static char *avn_newfile
=NULL
;
44 static char *avn_req
[2]={ NULL
, NULL
};
45 static struct forecast forecasts
[AVN_MAX
];
48 /********* init functions ************/
49 static int parse_avn(char *file
);
51 static void reset_avn(void){
54 for(i
=0; i
<AVN_MAX
; i
++) reset_forecast(&forecasts
[i
]);
60 struct subst_val subs
[]={
61 { 's', STRING
, &avn_station
},
65 strncpy(bigbuf
, avn_station
, BIGBUF_LEN
-14);
66 bigbuf
[BIGBUF_LEN
-14]='\0';
67 for(e
=bigbuf
; *e
!='\0'; e
++);
68 strcpy(e
, ".avn.txt");
69 avn_file
=get_pid_filename(bigbuf
);
70 strcpy(e
, ".new-avn.txt");
71 avn_newfile
=get_pid_filename(bigbuf
);
73 if((avn_req
[0]=subst(avn_uri
, subs
))==NULL
) die("init_avn");
74 if(avn_post
!=NULL
&& (avn_req
[1]=subst(avn_post
, subs
))==NULL
) die("init_avn");
77 /* Remove stale file */
81 for(i
=0; i
<AVN_MAX
; i
++) add_forecast(&forecasts
[i
], "AVN", avn_station
);
85 /********* download functions ************/
87 static void avn_callback(char *filename
, void *v
){
90 if(stat(avn_newfile
, &statbuf
)>=0){
91 if(S_ISREG(statbuf
.st_mode
) && statbuf
.st_size
!=0
92 && diff(avn_newfile
, avn_file
) && parse_avn(avn_newfile
)){
93 avn_time
=find_next_time(avn_newfile
, "MOS GUIDANCE", 720);
94 rename(avn_newfile
, avn_file
);
97 if(!parse_avn(avn_file
)) reset_avn();
102 void avn_cleanup(void){
103 if(avn_file
==NULL
) return;
108 void update_avn(int force
){
111 if(avn_file
==NULL
) return;
114 if(!force
&& avn_time
>t
) return;
116 avn_time
=find_next_time(avn_file
, "MOS GUIDANCE", 15);
117 download_file(avn_newfile
, avn_req
[0], avn_req
[1], force
?DOWNLOAD_KILL_OTHER_REQUESTS
:0, avn_callback
, NULL
);
121 /********* parse functions ************/
123 #define NEXT(s) free(s); \
124 len=getLine(&s, fp); \
125 if(strstr(s, "</PRE>")!=NULL) len=0;
127 #define DIE() return (free(s), fclose(fp), 0)
133 for(n=0, c=s+4; c<s+len && n<AVN_MAX; n++, c+=3){ \
140 #define ASSIGN(field) \
141 for(n=0; n<AVN_MAX; n++) forecasts[n].field=atoi(split[n]);
143 #define ASSIGN2(field, inval) \
144 for(n=0; n<AVN_MAX; n++){ \
146 if(i!=inval) forecasts[n].field=i; \
149 static int parse_avn(char *file
){
154 int h
, i
=0, j
, k
, m
, n
, x
, y
, z
;
156 char split
[AVN_MAX
][4];
159 if((fp
=fopen(file
, "r"))==NULL
) return 0;
161 /* Look for something like an AVN coded forecast */
165 if((c
=strstr(s
, "MOS GUIDANCE"))!=NULL
) break;
168 if(c
==NULL
) return (fclose(fp
), 0);
170 if(c
==NULL
|| !isdigit(*(c
-1)) || !isdigit(*(c
+1))) DIE();
173 if(c
==NULL
|| !isdigit(*(c
-1)) || !isdigit(*(c
+1))) DIE();
178 if(strncmp(s
, "DT ", 3)) DIE();
185 for(mon
=1; mon
<=12; mon
++){
186 if(!strncmp(c
+1, monthnames
[mon
], 3) && isspace(*(c
+4))) break;
187 if(!strncmp(c
+1, monthnames2
[mon
], 4) && isspace(*(c
+5))){
200 if(strncmp(s
, "HR ", 3)) DIE();
204 for(n
=0; n
<AVN_MAX
; n
++){
208 fix_date(&mon
, &x
, &y
, NULL
);
213 h
=utc2local(i
*100, &m
, &j
, &z
, &k
)/100;
214 forecasts
[n
].month
=m
;
219 if(latitude
!=999 && calcSolarZenith(latitude
, longitude
, y
, mon
, x
, i
*60)>90)
220 forecasts
[n
].moon
=calc_moon(m
, j
, z
, h
*100);
228 if(!strcmp(ID
, "X/N")) j
=1;
229 else if(!strcmp(ID
, "N/X")) j
=2;
232 for(n
=0; n
<AVN_MAX
; n
++){
233 if(!isdigit(split
[n
][2])) continue;
236 for(m
=0; m
<AVN_MAX
; m
++){
238 ((forecasts
[m
].day
==k
-1 && forecasts
[m
].hour
>=19)
239 || (forecasts
[m
].day
==k
&& forecasts
[m
].hour
<19)))
242 ((forecasts
[m
].day
==k
-1 && forecasts
[m
].hour
>=8)
243 || (forecasts
[m
].day
==k
&& forecasts
[m
].hour
<8)))
250 if(!strcmp(ID
, "TMP")){
254 if(!strcmp(ID
, "DPT")){
258 if(!strcmp(ID
, "WDR")){
259 for(n
=0; n
<AVN_MAX
; n
++){
261 if(i
==99) forecasts
[n
].winddir
=0;
262 else forecasts
[n
].winddir
=((int)((i
+1.125)/2.25))%16+1;
266 if(!strcmp(ID
, "WSP")){
267 ASSIGN2(windspeed
, 99);
270 if(!strcmp(ID
, "P06")){
271 for(m
=0; m
<AVN_MAX
; m
++){
272 if(!isdigit(split
[m
][2])) continue;
275 forecasts
[m
].pcp_total
=i
;
276 /* AVN_MAX-2 because the last 2
277 * are already 6-hour intervals */
278 if(m
>0 && m
<AVN_MAX
-2) forecasts
[m
-1].pcp_total
=i
;
283 if(!strcmp(ID
, "T06")){
284 for(m
=1; m
<AVN_MAX
; m
+=2){
285 if(!isdigit(split
[m
][2])) continue;
286 i
=atoi(split
[m
]); if(i
==999) i
=0;
287 j
=atoi(split
[m
+1]+1); if(j
==99) j
=0;
289 forecasts
[m
].tstorm
=forecasts
[m
+1].tstorm
=i
;
290 forecasts
[m
].svtstorm
=forecasts
[m
+1].svtstorm
=j
;
294 if(!strcmp(ID
, "Q06")){
295 for(m
=0; m
<AVN_MAX
; m
++){
296 if(!isdigit(split
[m
][2])) continue;
299 forecasts
[m
].precipamt
=i
;
300 /* AVN_MAX-2 because the last 2
301 * are already 6-hour intervals */
302 if(m
>0 && m
<AVN_MAX
-2) forecasts
[m
-1].precipamt
=i
;
307 if(!strcmp(ID
, "SNW")){
308 for(m
=0; m
<AVN_MAX
; m
++){
309 if(!isdigit(split
[m
][2])) continue;
315 if(forecasts
[n
].day
<k
&& forecasts
[n
].hour
<j
) break;
316 forecasts
[n
].snowamt
=i
;
322 if(!strcmp(ID
, "CLD")){
323 for(m
=0; m
<AVN_MAX
; m
++){
324 if(split
[m
][1]=='C') forecasts
[m
].sky
=0;
325 if(split
[m
][1]=='F') forecasts
[m
].sky
=1;
326 if(split
[m
][1]=='S') forecasts
[m
].sky
=2;
327 if(split
[m
][1]=='B') forecasts
[m
].sky
=3;
328 if(split
[m
][1]=='O') forecasts
[m
].sky
=4;
332 if(!strcmp(ID
, "VIS")){
336 if(!strcmp(ID
, "OBV")){
337 for(m
=0; m
<AVN_MAX
; m
++){
338 if(split
[m
][2]=='N') forecasts
[m
].obs
=0;
339 if(split
[m
][2]=='R' || split
[m
][2]=='G') forecasts
[m
].obs
=1;
340 if(split
[m
][2]=='Z') forecasts
[m
].obs
=2;
341 if(split
[m
][2]=='L') forecasts
[m
].obs
=3;
345 if(!strcmp(ID
, "POZ")){
349 if(!strcmp(ID
, "POS")){
357 for(m
=0; m
<AVN_MAX
; m
++){
358 forecasts
[m
].rh
=rh_F(forecasts
[m
].temp
, forecasts
[m
].dewpt
);
359 forecasts
[m
].heatindex
=heatindex_F(forecasts
[m
].temp
, forecasts
[m
].rh
);
360 forecasts
[m
].windchill
=windchill_F(forecasts
[m
].temp
, forecasts
[m
].windspeed
);
361 forecasts
[m
].rain
=93-forecasts
[m
].frz
-forecasts
[m
].snow
;
362 forecasts
[m
].rain
=forecasts
[m
].rain
*forecasts
[m
].pcp_total
/93;
363 forecasts
[m
].snow
=forecasts
[m
].snow
*forecasts
[m
].pcp_total
/93;
364 forecasts
[m
].frz
=forecasts
[m
].frz
*forecasts
[m
].pcp_total
/93;