Mercurial > ~darius > hgwebdir.cgi > wh1080
diff local-compare.c @ 0:9dab44dcb331
Initial commit of Greg's code from http://www.lemis.com/grog/tmp/wh1080.tar.gz
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Tue, 09 Feb 2010 13:44:25 +1030 |
parents | |
children | 9da35e705144 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/local-compare.c Tue Feb 09 13:44:25 2010 +1030 @@ -0,0 +1,390 @@ +/* + * Create text files with a table comparing local temperature and barometric + * pressurs to remote observations. + * + * Format: + * + * Date Difference # legible date + * + * Greg Lehey, 17 December 2009 + * + * FIXME. This needs rethinking. In particular, startup parameters are a mess. + * Read on, brave programmer. + * + * $Id: local-compare.c,v 1.5 2010/02/07 03:38:26 grog Exp $ + */ + +#include "wh1080.h" +#include <syslog.h> + +#define STAMPSIZE 32 /* size of time stamp to print out */ + +char *stations [] = {"94852", /* Ballarat airport */ + "94863", /* Sheoaks */ + "94866"}; /* Melbourne airport */ + +const int station_count = sizeof (stations) / sizeof (char *); + +/* MySQL stuff */ +MYSQL *mysql; +MYSQL_RES *mysql_result_BoM; +MYSQL_RES *mysql_result_local; +MYSQL_ROW mysql_row_BoM; +MYSQL_ROW mysql_row_local; +char mysql_querytext [1024]; /* build up queries here */ + +/* Command line options */ +int debug; /* -d */ +int verbose; /* -v */ + +/* + * Temperatures for calculation: two pairs of BoM time and temperature, + * and one time and temperature between them for local. + */ +time_t interval_starttime; +float interval_starttemp; +time_t interval_endtime; +float interval_endtemp; +time_t local_time; +float local_temp; +float interval_temp; /* interpolated BoM temperature */ +/* barometric pressure stuff */ +float interval_startpressure; +float interval_endpressure; +float interval_pressure; /* interpolated BoM pressure */ +float local_pressure; + +/* XXX get time zone in here */ +const int my2k = 946731600; /* difference between UNIX and GNU epochs */ +char *startdate_text; /* start date, YYYY-MM-DD */ +char *enddate_text; /* and end date (default: 24 hours later) */ +/* Same times in struct tm format */ +struct tm startdate; +struct tm enddate; +time_t endtime_t; + +void usage (char *myname) +{ + fprintf (stderr, + "Usage: %s YYYY-MM-DD YYYY-MM-DD [-d] [-v] [station-id] [user] [passwd] [host] [db]\n", + myname); + exit (1); +} + +/* + * Make files with comparative data for temperatures. + * + * The remote observations are relatively infrequent, while the local + * observations are frequent. We interpolate the "remote" values linearly to + * estimate the temperature at the time of a local observation, but it doesn't + * make sense to do that for all local observations. Instead, we just take the + * one immediately after the remote observation, then skip to the next remote + * observation. + */ +void mktempcompare (char *stationid) +{ + char filename [PATH_MAX]; + FILE *myfile; /* output file */ + int finishing = 0; /* set to exit loop */ + + /* Create a file name for this comparison */ + strcpy (filename, config.comparefile); + strcat (filename, "-"); + strcat (filename, stationid); + myfile = fopen (filename, "w"); + if (! myfile) + { + syslog (LOG_ERR | LOG_DAEMON, + "Can't open %s: %s (%d)\n", + filename, + strerror (errno), + errno ); + exit (1); + } + chmod (filename, 0666); /* let anybody access it */ + sprintf (mysql_querytext, + "SELECT unix_timestamp(timestamp(date, time)), outside_temp\n" + "FROM remote_observations\n" + "WHERE station_id = '%s'\n" + " AND date >= '%s'\n" + " AND date <= '%s'\n" + "ORDER BY date, time;\n", + stationid, + startdate_text, + enddate_text ); + if (domyquery (mysql_querytext, &mysql_result_BoM)) + exit (1); /* failed and reported */ + if (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM)) /* got something */ + { + interval_starttime = atol (mysql_row_BoM [0]); + interval_starttemp = atof (mysql_row_BoM [1]); + /* Next row for first interval */ + if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* got something ?*/ + exit (1); /* no */ + interval_endtime = atol (mysql_row_BoM [0]); + interval_endtemp = atof (mysql_row_BoM [1]); + + /* + * Now we have an initial interval. Get the first local record after start + * of interval. + */ + sprintf (mysql_querytext, + "SELECT unix_timestamp(timestamp(date, time)), outside_temp\n" + "FROM %s\n" + "WHERE station_id = '%s'\n" + " AND date >= '%s'\n" + " AND date <= '%s'\n" + "ORDER BY date, time;\n", + config.db_table, + config.station_id, + startdate_text, + enddate_text ); + if (domyquery (mysql_querytext, &mysql_result_local)) + exit (1); /* failed and reported */ + while (mysql_row_local = mysql_fetch_row (mysql_result_local)) /* got something */ + { + local_time = atol (mysql_row_local [0]); + local_temp = atof (mysql_row_local [1]); + if (local_time > interval_starttime) /* we're in business */ + { + while (local_time > interval_endtime) /* beyond current BoM readings */ + { + interval_starttime = interval_endtime; + interval_starttemp = interval_endtemp; + /* Next row for first interval */ + if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* finished? */ + { + fclose (myfile); + return; + } + interval_endtime = atol (mysql_row_BoM [0]); + interval_endtemp = atof (mysql_row_BoM [1]); + } + + /* + * Now our local reading is between start and end times. Interpolate + * to get BoM temperature at this time. + */ + interval_temp = interval_starttemp + + (interval_endtemp - interval_starttemp) + * (local_time - interval_starttime) / (interval_endtime - interval_starttime); + fprintf (myfile, + "%d %d %2.1f %2.1f %2.1f # %s", + local_time, + local_time - my2k, + local_temp, + interval_temp, + interval_temp - local_temp, + ctime (&local_time) ); + + if (finishing) + { + fclose (myfile); + return; + } + /* Next remote row */ + interval_starttime = interval_endtime; + interval_starttemp = interval_endtemp; + if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* finished? */ + finishing = 1; + else + { + interval_endtime = atol (mysql_row_BoM [0]); + interval_endtemp = atof (mysql_row_BoM [1]); + } + } + } + } +} + +/* + * Make files with comparative data for barometric pressures. + */ +void mkpresscompare (char *stationid) +{ + char filename [PATH_MAX]; + FILE *myfile; /* output file */ + int finishing = 0; /* set to exit loop */ + + /* Create a file name for this comparison */ + strcpy (filename, config.comparefile); + strcat (filename, "-pressure-"); + strcat (filename, stationid); + myfile = fopen (filename, "w"); + if (! myfile) + { + syslog (LOG_ERR | LOG_DAEMON, + "Can't open %s: %s (%d)\n", + filename, + strerror (errno), + errno ); + exit (1); + } + chmod (filename, 0666); /* let anybody access it */ + sprintf (mysql_querytext, + "SELECT unix_timestamp(timestamp(date, time)), pressure_msl\n" + "FROM remote_observations\n" + "WHERE station_id = '%s'\n" + " AND date >= '%s'\n" + " AND date <= '%s'\n" + " AND pressure_msl IS NOT NULL\n" + "ORDER BY date, time;\n", + stationid, + startdate_text, + enddate_text ); + if (domyquery (mysql_querytext, &mysql_result_BoM)) + exit (1); /* failed and reported */ + if (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM)) /* got something */ + { + interval_starttime = atol (mysql_row_BoM [0]); + interval_startpressure = atof (mysql_row_BoM [1]); + + /* + * Next row for first interval. + * + * The BoM doesn't issue this information very frequently, so we'll accept a + * single reading if we have to. In this case, we just pretend that the + * pressure is constant. + */ + if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* got something ?*/ + { + finishing = 1; /* no, just do one more result */ + interval_endtime = interval_starttime + 100; + interval_endpressure = interval_startpressure; + } + else + { + interval_endtime = atol (mysql_row_BoM [0]); + interval_endpressure = atof (mysql_row_BoM [1]); + } + + /* + * Now we have an initial interval. Get the first local record after start + * of interval. + */ + sprintf (mysql_querytext, + "SELECT unix_timestamp(timestamp(date, time)), pressure_msl\n" + "FROM %s\n" + "WHERE station_id = '%s'\n" + " AND date >= '%s'\n" + " AND date <= '%s'\n" + " AND pressure_msl IS NOT NULL\n" + "ORDER BY date, time;\n", + config.db_table, + config.station_id, + startdate_text, + enddate_text ); + if (domyquery (mysql_querytext, &mysql_result_local)) + exit (1); /* failed and reported */ + while (mysql_row_local = mysql_fetch_row (mysql_result_local)) /* got something */ + { + local_time = atol (mysql_row_local [0]); + local_pressure = atof (mysql_row_local [1]); + if (local_time > interval_starttime) /* we're in business */ + { + /* + * Unlike with temperatures, we only look at the first pressure reading + * newer than the BoM reading. We have no way of knowing if the BoM has + * similar changes to what we have locally. + */ + while (local_time > interval_endtime) /* beyond current BoM readings */ + { + interval_starttime = interval_endtime; + interval_startpressure = interval_endpressure; + /* Next row for first interval */ + if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* finished? */ + { + finishing = 1; + interval_endtime += 1000; /* to avoid division by 0 */ + } + else + { + interval_endtime = atol (mysql_row_BoM [0]); + interval_endpressure = atof (mysql_row_BoM [1]); + } + } + + /* + * Now our local reading is between start and end times. Interpolate + * to get BoM pressure at this time. + */ + interval_pressure = interval_startpressure + + (interval_endpressure - interval_startpressure) + * (local_time - interval_starttime) / (interval_endtime - interval_starttime); + fprintf (myfile, + "%d %d %2.1f %2.1f %2.1f # %s", + local_time, + local_time - my2k, + local_pressure, + interval_pressure, + interval_pressure - local_pressure, + ctime (&local_time) ); + local_time = interval_endtime + 1; /* force the next record */ + + if (finishing) + { + fclose (myfile); + return; + } + /* Next remote row */ + interval_starttime = interval_endtime; + interval_starttemp = interval_endtemp; + if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* finished? */ + finishing = 1; + else + { + interval_endtime = atol (mysql_row_BoM [0]); + interval_endtemp = atof (mysql_row_BoM [1]); + } + } + } + } + fclose (myfile); +} + +int main (int argc, char *argv []) +{ + int station; + /* + * This is tacky. We want to use the standard parameter sequence in + * read_config (), but that doesn't include dates. So we require the sequence + * in usage (). And that means that we need to find a way to specify the end + * day. FIXME. + */ + + if (argc < 3) /* we need at least start date */ + usage (argv [0]); + startdate_text = argv [1]; /* start and */ + enddate_text = argv [1]; /* end date */ + read_config (argc - 2, &argv [2]); /* this implies that we know argv [0] won't be used */ + + mysql = mysql_init (mysql); + if (! mysql) + { + syslog (LOG_ERR | LOG_DAEMON, "Can't initialize MySQL: insufficient memory\n"); + exit (1); + } + if (! mysql_real_connect (mysql, /* DB state */ + config.db_host, /* database host */ + config.db_user, + config.db_passwd, + config.db, + 0, + NULL, + 0 )) + { + syslog (LOG_ERR | LOG_DAEMON, + "Can't connect to databaase: %s (%d)\n", + mysql_error (mysql), + mysql_errno (mysql) ); + exit (1); + } + + /* Generate the files */ + for (station = 0; station < station_count; station++) + { + mktempcompare (stations [station]); + mkpresscompare (stations [station]); + } + exit (0); +}