374 lines
11 KiB
C
374 lines
11 KiB
C
#include "config.h"
|
|
|
|
/* Copyright (C) 2002 Brad Jorsch <anomie@users.sourceforge.net>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "wmweather+.h"
|
|
#include "forecast.h"
|
|
#include "getLine.h"
|
|
#include "convert.h"
|
|
#include "download.h"
|
|
#include "diff.h"
|
|
#include "die.h"
|
|
#include "sunzenith.h"
|
|
#include "moon.h"
|
|
#include "subst.h"
|
|
|
|
/* Important variables */
|
|
#define AVN_MAX 21
|
|
static time_t avn_time=0;
|
|
static char *avn_file=NULL;
|
|
static char *avn_newfile=NULL;
|
|
static char *avn_req[2]={ NULL, NULL };
|
|
static struct forecast forecasts[AVN_MAX];
|
|
|
|
|
|
/********* init functions ************/
|
|
static int parse_avn(char *file);
|
|
|
|
static void reset_avn(void){
|
|
int i;
|
|
|
|
for(i=0; i<AVN_MAX; i++) reset_forecast(&forecasts[i]);
|
|
}
|
|
|
|
void init_avn(void){
|
|
char *e;
|
|
int i;
|
|
struct subst_val subs[]={
|
|
{ 's', STRING, &avn_station },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
strncpy(bigbuf, avn_station, BIGBUF_LEN-14);
|
|
bigbuf[BIGBUF_LEN-14]='\0';
|
|
for(e=bigbuf; *e!='\0'; e++);
|
|
strcpy(e, ".avn.txt");
|
|
avn_file=get_pid_filename(bigbuf);
|
|
strcpy(e, ".new-avn.txt");
|
|
avn_newfile=get_pid_filename(bigbuf);
|
|
|
|
if((avn_req[0]=subst(avn_uri, subs))==NULL) die("init_avn");
|
|
if(avn_post!=NULL && (avn_req[1]=subst(avn_post, subs))==NULL) die("init_avn");
|
|
avn_time=0;
|
|
|
|
/* Remove stale file */
|
|
unlink(avn_file);
|
|
unlink(avn_newfile);
|
|
reset_avn();
|
|
for(i=0; i<AVN_MAX; i++) add_forecast(&forecasts[i], "AVN", avn_station);
|
|
}
|
|
|
|
|
|
/********* download functions ************/
|
|
|
|
static void avn_callback(char *filename, void *v){
|
|
struct stat statbuf;
|
|
|
|
if(stat(avn_newfile, &statbuf)>=0){
|
|
if(S_ISREG(statbuf.st_mode) && statbuf.st_size!=0
|
|
&& diff(avn_newfile, avn_file) && parse_avn(avn_newfile)){
|
|
avn_time=find_next_time(avn_newfile, "MOS GUIDANCE", 720);
|
|
rename(avn_newfile, avn_file);
|
|
} else {
|
|
unlink(avn_newfile);
|
|
if(!parse_avn(avn_file)) reset_avn();
|
|
}
|
|
}
|
|
}
|
|
|
|
void avn_cleanup(void){
|
|
if(avn_file==NULL) return;
|
|
unlink(avn_newfile);
|
|
unlink(avn_file);
|
|
}
|
|
|
|
void update_avn(int force){
|
|
time_t t;
|
|
|
|
if(avn_file==NULL) return;
|
|
|
|
t=time(NULL)/60;
|
|
if(!force && avn_time>t) return;
|
|
|
|
avn_time=find_next_time(avn_file, "MOS GUIDANCE", 15);
|
|
download_file(avn_newfile, avn_req[0], avn_req[1], force?DOWNLOAD_KILL_OTHER_REQUESTS:0, avn_callback, NULL);
|
|
}
|
|
|
|
|
|
/********* parse functions ************/
|
|
|
|
#define NEXT(s) free(s); \
|
|
len=getLine(&s, fp); \
|
|
if(strstr(s, "</PRE>")!=NULL) len=0;
|
|
|
|
#define DIE() return (free(s), fclose(fp), 0)
|
|
#define SPLIT(s) { \
|
|
ID[0]=s[0]; \
|
|
ID[1]=s[1]; \
|
|
ID[2]=s[2]; \
|
|
ID[3]='\0'; \
|
|
for(n=0, c=s+4; c<s+len && n<AVN_MAX; n++, c+=3){ \
|
|
split[n][0]=c[0]; \
|
|
split[n][1]=c[1]; \
|
|
split[n][2]=c[2]; \
|
|
split[n][3]='\0'; \
|
|
} \
|
|
}
|
|
#define ASSIGN(field) \
|
|
for(n=0; n<AVN_MAX; n++) forecasts[n].field=atoi(split[n]);
|
|
|
|
#define ASSIGN2(field, inval) \
|
|
for(n=0; n<AVN_MAX; n++){ \
|
|
i=atoi(split[n]); \
|
|
if(i!=inval) forecasts[n].field=i; \
|
|
}
|
|
|
|
static int parse_avn(char *file){
|
|
FILE *fp;
|
|
char *s, *c;
|
|
int len;
|
|
int mon, day;
|
|
int h, i=0, j, k, m, n, x, y, z;
|
|
char ID[4];
|
|
char split[AVN_MAX][4];
|
|
|
|
reset_avn();
|
|
if((fp=fopen(file, "r"))==NULL) return 0;
|
|
|
|
/* Look for something like an AVN coded forecast */
|
|
c=NULL;
|
|
while(!feof(fp)){
|
|
len=getLine(&s, fp);
|
|
if((c=strstr(s, "MOS GUIDANCE"))!=NULL) break;
|
|
free(s);
|
|
}
|
|
if(c==NULL) return (fclose(fp), 0);
|
|
c=strchr(c, '/');
|
|
if(c==NULL || !isdigit(*(c-1)) || !isdigit(*(c+1))) DIE();
|
|
m=atoi(c-2);
|
|
c=strchr(c+1, '/');
|
|
if(c==NULL || !isdigit(*(c-1)) || !isdigit(*(c+1))) DIE();
|
|
y=atoi(c+1)-1900;
|
|
|
|
NEXT(s);
|
|
if(len<10) DIE();
|
|
if(strncmp(s, "DT ", 3)) DIE();
|
|
mon=13;
|
|
c=s;
|
|
i=4;
|
|
while(mon>12){
|
|
c=strchr(c+1, '/');
|
|
if(c==NULL) DIE();
|
|
for(mon=1; mon<=12; mon++){
|
|
if(!strncmp(c+1, monthnames[mon], 3) && isspace(*(c+4))) break;
|
|
if(!strncmp(c+1, monthnames2[mon], 4) && isspace(*(c+5))){
|
|
i=5;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
day=atoi(c+i);
|
|
if(day<1) DIE();
|
|
if(c>s+4) day--;
|
|
if(mon<m) y++;
|
|
|
|
NEXT(s);
|
|
if(len<10) DIE();
|
|
if(strncmp(s, "HR ", 3)) DIE();
|
|
x=day;
|
|
m=mon;
|
|
SPLIT(s);
|
|
for(n=0; n<AVN_MAX; n++){
|
|
i=atoi(split[n]);
|
|
if(i==0){
|
|
x++;
|
|
fix_date(&mon, &x, &y, NULL);
|
|
}
|
|
m=mon;
|
|
j=x;
|
|
z=y;
|
|
h=utc2local(i*100, &m, &j, &z, &k)/100;
|
|
forecasts[n].month=m;
|
|
forecasts[n].day=j;
|
|
forecasts[n].year=z;
|
|
forecasts[n].hour=h;
|
|
forecasts[n].wday=k;
|
|
if(latitude!=999 && calcSolarZenith(latitude, longitude, y, mon, x, i*60)>90)
|
|
forecasts[n].moon=calc_moon(m, j, z, h*100);
|
|
}
|
|
|
|
while(1){
|
|
NEXT(s);
|
|
if(len<=10) break;
|
|
SPLIT(s);
|
|
|
|
if(!strcmp(ID, "X/N")) j=1;
|
|
else if(!strcmp(ID, "N/X")) j=2;
|
|
else j=0;
|
|
if(j!=0){
|
|
for(n=0; n<AVN_MAX; n++){
|
|
if(!isdigit(split[n][2])) continue;
|
|
i=atoi(split[n]);
|
|
k=day+(j>>1);
|
|
for(m=0; m<AVN_MAX; m++){
|
|
if((j&1)==1 &&
|
|
((forecasts[m].day==k-1 && forecasts[m].hour>=19)
|
|
|| (forecasts[m].day==k && forecasts[m].hour<19)))
|
|
forecasts[m].high=i;
|
|
if((j&1)==0 &&
|
|
((forecasts[m].day==k-1 && forecasts[m].hour>=8)
|
|
|| (forecasts[m].day==k && forecasts[m].hour<8)))
|
|
forecasts[m].low=i;
|
|
}
|
|
j++;
|
|
}
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "TMP")){
|
|
ASSIGN(temp);
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "DPT")){
|
|
ASSIGN(dewpt);
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "WDR")){
|
|
for(n=0; n<AVN_MAX; n++){
|
|
i=atoi(split[n]);
|
|
if(i==99) forecasts[n].winddir=0;
|
|
else forecasts[n].winddir=((int)((i+1.125)/2.25))%16+1;
|
|
}
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "WSP")){
|
|
ASSIGN2(windspeed, 99);
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "P06")){
|
|
for(m=0; m<AVN_MAX; m++){
|
|
if(!isdigit(split[m][2])) continue;
|
|
i=atoi(split[m]);
|
|
if(i!=999){
|
|
forecasts[m].pcp_total=i;
|
|
/* AVN_MAX-2 because the last 2
|
|
* are already 6-hour intervals */
|
|
if(m>0 && m<AVN_MAX-2) forecasts[m-1].pcp_total=i;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "T06")){
|
|
for(m=1; m<AVN_MAX; m+=2){
|
|
if(!isdigit(split[m][2])) continue;
|
|
i=atoi(split[m]); if(i==999) i=0;
|
|
j=atoi(split[m+1]+1); if(j==99) j=0;
|
|
j=i*j/100;
|
|
forecasts[m].tstorm=forecasts[m+1].tstorm=i;
|
|
forecasts[m].svtstorm=forecasts[m+1].svtstorm=j;
|
|
}
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "Q06")){
|
|
for(m=0; m<AVN_MAX; m++){
|
|
if(!isdigit(split[m][2])) continue;
|
|
i=atoi(split[m]);
|
|
if(i!=999){
|
|
forecasts[m].precipamt=i;
|
|
/* AVN_MAX-2 because the last 2
|
|
* are already 6-hour intervals */
|
|
if(m>0 && m<AVN_MAX-2) forecasts[m-1].precipamt=i;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "SNW")){
|
|
for(m=0; m<AVN_MAX; m++){
|
|
if(!isdigit(split[m][2])) continue;
|
|
i=atoi(split[m]);
|
|
if(i!=9){
|
|
j=forecasts[m].hour;
|
|
k=forecasts[m].day;
|
|
for(n=m; n>=0; n--){
|
|
if(forecasts[n].day<k && forecasts[n].hour<j) break;
|
|
forecasts[n].snowamt=i;
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "CLD")){
|
|
for(m=0; m<AVN_MAX; m++){
|
|
if(split[m][1]=='C') forecasts[m].sky=0;
|
|
if(split[m][1]=='F') forecasts[m].sky=1;
|
|
if(split[m][1]=='S') forecasts[m].sky=2;
|
|
if(split[m][1]=='B') forecasts[m].sky=3;
|
|
if(split[m][1]=='O') forecasts[m].sky=4;
|
|
}
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "VIS")){
|
|
ASSIGN2(vis, 9);
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "OBV")){
|
|
for(m=0; m<AVN_MAX; m++){
|
|
if(split[m][2]=='N') forecasts[m].obs=0;
|
|
if(split[m][2]=='R' || split[m][2]=='G') forecasts[m].obs=1;
|
|
if(split[m][2]=='Z') forecasts[m].obs=2;
|
|
if(split[m][2]=='L') forecasts[m].obs=3;
|
|
}
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "POZ")){
|
|
ASSIGN2(frz, 999);
|
|
continue;
|
|
}
|
|
if(!strcmp(ID, "POS")){
|
|
ASSIGN2(snow, 999);
|
|
continue;
|
|
}
|
|
}
|
|
free(s);
|
|
fclose(fp);
|
|
|
|
for(m=0; m<AVN_MAX; m++){
|
|
forecasts[m].rh=rh_F(forecasts[m].temp, forecasts[m].dewpt);
|
|
forecasts[m].heatindex=heatindex_F(forecasts[m].temp, forecasts[m].rh);
|
|
forecasts[m].windchill=windchill_F(forecasts[m].temp, forecasts[m].windspeed);
|
|
forecasts[m].rain=93-forecasts[m].frz-forecasts[m].snow;
|
|
forecasts[m].rain=forecasts[m].rain*forecasts[m].pcp_total/93;
|
|
forecasts[m].snow=forecasts[m].snow*forecasts[m].pcp_total/93;
|
|
forecasts[m].frz=forecasts[m].frz*forecasts[m].pcp_total/93;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#undef NEXT
|
|
#undef DIE
|
|
#undef SPLIT
|
|
#undef ASSIGN
|
|
#undef ASSIGN2
|