comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:9dab44dcb331
1 /*
2 * Create text files with a table comparing local temperature and barometric
3 * pressurs to remote observations.
4 *
5 * Format:
6 *
7 * Date Difference # legible date
8 *
9 * Greg Lehey, 17 December 2009
10 *
11 * FIXME. This needs rethinking. In particular, startup parameters are a mess.
12 * Read on, brave programmer.
13 *
14 * $Id: local-compare.c,v 1.5 2010/02/07 03:38:26 grog Exp $
15 */
16
17 #include "wh1080.h"
18 #include <syslog.h>
19
20 #define STAMPSIZE 32 /* size of time stamp to print out */
21
22 char *stations [] = {"94852", /* Ballarat airport */
23 "94863", /* Sheoaks */
24 "94866"}; /* Melbourne airport */
25
26 const int station_count = sizeof (stations) / sizeof (char *);
27
28 /* MySQL stuff */
29 MYSQL *mysql;
30 MYSQL_RES *mysql_result_BoM;
31 MYSQL_RES *mysql_result_local;
32 MYSQL_ROW mysql_row_BoM;
33 MYSQL_ROW mysql_row_local;
34 char mysql_querytext [1024]; /* build up queries here */
35
36 /* Command line options */
37 int debug; /* -d */
38 int verbose; /* -v */
39
40 /*
41 * Temperatures for calculation: two pairs of BoM time and temperature,
42 * and one time and temperature between them for local.
43 */
44 time_t interval_starttime;
45 float interval_starttemp;
46 time_t interval_endtime;
47 float interval_endtemp;
48 time_t local_time;
49 float local_temp;
50 float interval_temp; /* interpolated BoM temperature */
51 /* barometric pressure stuff */
52 float interval_startpressure;
53 float interval_endpressure;
54 float interval_pressure; /* interpolated BoM pressure */
55 float local_pressure;
56
57 /* XXX get time zone in here */
58 const int my2k = 946731600; /* difference between UNIX and GNU epochs */
59 char *startdate_text; /* start date, YYYY-MM-DD */
60 char *enddate_text; /* and end date (default: 24 hours later) */
61 /* Same times in struct tm format */
62 struct tm startdate;
63 struct tm enddate;
64 time_t endtime_t;
65
66 void usage (char *myname)
67 {
68 fprintf (stderr,
69 "Usage: %s YYYY-MM-DD YYYY-MM-DD [-d] [-v] [station-id] [user] [passwd] [host] [db]\n",
70 myname);
71 exit (1);
72 }
73
74 /*
75 * Make files with comparative data for temperatures.
76 *
77 * The remote observations are relatively infrequent, while the local
78 * observations are frequent. We interpolate the "remote" values linearly to
79 * estimate the temperature at the time of a local observation, but it doesn't
80 * make sense to do that for all local observations. Instead, we just take the
81 * one immediately after the remote observation, then skip to the next remote
82 * observation.
83 */
84 void mktempcompare (char *stationid)
85 {
86 char filename [PATH_MAX];
87 FILE *myfile; /* output file */
88 int finishing = 0; /* set to exit loop */
89
90 /* Create a file name for this comparison */
91 strcpy (filename, config.comparefile);
92 strcat (filename, "-");
93 strcat (filename, stationid);
94 myfile = fopen (filename, "w");
95 if (! myfile)
96 {
97 syslog (LOG_ERR | LOG_DAEMON,
98 "Can't open %s: %s (%d)\n",
99 filename,
100 strerror (errno),
101 errno );
102 exit (1);
103 }
104 chmod (filename, 0666); /* let anybody access it */
105 sprintf (mysql_querytext,
106 "SELECT unix_timestamp(timestamp(date, time)), outside_temp\n"
107 "FROM remote_observations\n"
108 "WHERE station_id = '%s'\n"
109 " AND date >= '%s'\n"
110 " AND date <= '%s'\n"
111 "ORDER BY date, time;\n",
112 stationid,
113 startdate_text,
114 enddate_text );
115 if (domyquery (mysql_querytext, &mysql_result_BoM))
116 exit (1); /* failed and reported */
117 if (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM)) /* got something */
118 {
119 interval_starttime = atol (mysql_row_BoM [0]);
120 interval_starttemp = atof (mysql_row_BoM [1]);
121 /* Next row for first interval */
122 if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* got something ?*/
123 exit (1); /* no */
124 interval_endtime = atol (mysql_row_BoM [0]);
125 interval_endtemp = atof (mysql_row_BoM [1]);
126
127 /*
128 * Now we have an initial interval. Get the first local record after start
129 * of interval.
130 */
131 sprintf (mysql_querytext,
132 "SELECT unix_timestamp(timestamp(date, time)), outside_temp\n"
133 "FROM %s\n"
134 "WHERE station_id = '%s'\n"
135 " AND date >= '%s'\n"
136 " AND date <= '%s'\n"
137 "ORDER BY date, time;\n",
138 config.db_table,
139 config.station_id,
140 startdate_text,
141 enddate_text );
142 if (domyquery (mysql_querytext, &mysql_result_local))
143 exit (1); /* failed and reported */
144 while (mysql_row_local = mysql_fetch_row (mysql_result_local)) /* got something */
145 {
146 local_time = atol (mysql_row_local [0]);
147 local_temp = atof (mysql_row_local [1]);
148 if (local_time > interval_starttime) /* we're in business */
149 {
150 while (local_time > interval_endtime) /* beyond current BoM readings */
151 {
152 interval_starttime = interval_endtime;
153 interval_starttemp = interval_endtemp;
154 /* Next row for first interval */
155 if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* finished? */
156 {
157 fclose (myfile);
158 return;
159 }
160 interval_endtime = atol (mysql_row_BoM [0]);
161 interval_endtemp = atof (mysql_row_BoM [1]);
162 }
163
164 /*
165 * Now our local reading is between start and end times. Interpolate
166 * to get BoM temperature at this time.
167 */
168 interval_temp = interval_starttemp
169 + (interval_endtemp - interval_starttemp)
170 * (local_time - interval_starttime) / (interval_endtime - interval_starttime);
171 fprintf (myfile,
172 "%d %d %2.1f %2.1f %2.1f # %s",
173 local_time,
174 local_time - my2k,
175 local_temp,
176 interval_temp,
177 interval_temp - local_temp,
178 ctime (&local_time) );
179
180 if (finishing)
181 {
182 fclose (myfile);
183 return;
184 }
185 /* Next remote row */
186 interval_starttime = interval_endtime;
187 interval_starttemp = interval_endtemp;
188 if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* finished? */
189 finishing = 1;
190 else
191 {
192 interval_endtime = atol (mysql_row_BoM [0]);
193 interval_endtemp = atof (mysql_row_BoM [1]);
194 }
195 }
196 }
197 }
198 }
199
200 /*
201 * Make files with comparative data for barometric pressures.
202 */
203 void mkpresscompare (char *stationid)
204 {
205 char filename [PATH_MAX];
206 FILE *myfile; /* output file */
207 int finishing = 0; /* set to exit loop */
208
209 /* Create a file name for this comparison */
210 strcpy (filename, config.comparefile);
211 strcat (filename, "-pressure-");
212 strcat (filename, stationid);
213 myfile = fopen (filename, "w");
214 if (! myfile)
215 {
216 syslog (LOG_ERR | LOG_DAEMON,
217 "Can't open %s: %s (%d)\n",
218 filename,
219 strerror (errno),
220 errno );
221 exit (1);
222 }
223 chmod (filename, 0666); /* let anybody access it */
224 sprintf (mysql_querytext,
225 "SELECT unix_timestamp(timestamp(date, time)), pressure_msl\n"
226 "FROM remote_observations\n"
227 "WHERE station_id = '%s'\n"
228 " AND date >= '%s'\n"
229 " AND date <= '%s'\n"
230 " AND pressure_msl IS NOT NULL\n"
231 "ORDER BY date, time;\n",
232 stationid,
233 startdate_text,
234 enddate_text );
235 if (domyquery (mysql_querytext, &mysql_result_BoM))
236 exit (1); /* failed and reported */
237 if (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM)) /* got something */
238 {
239 interval_starttime = atol (mysql_row_BoM [0]);
240 interval_startpressure = atof (mysql_row_BoM [1]);
241
242 /*
243 * Next row for first interval.
244 *
245 * The BoM doesn't issue this information very frequently, so we'll accept a
246 * single reading if we have to. In this case, we just pretend that the
247 * pressure is constant.
248 */
249 if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* got something ?*/
250 {
251 finishing = 1; /* no, just do one more result */
252 interval_endtime = interval_starttime + 100;
253 interval_endpressure = interval_startpressure;
254 }
255 else
256 {
257 interval_endtime = atol (mysql_row_BoM [0]);
258 interval_endpressure = atof (mysql_row_BoM [1]);
259 }
260
261 /*
262 * Now we have an initial interval. Get the first local record after start
263 * of interval.
264 */
265 sprintf (mysql_querytext,
266 "SELECT unix_timestamp(timestamp(date, time)), pressure_msl\n"
267 "FROM %s\n"
268 "WHERE station_id = '%s'\n"
269 " AND date >= '%s'\n"
270 " AND date <= '%s'\n"
271 " AND pressure_msl IS NOT NULL\n"
272 "ORDER BY date, time;\n",
273 config.db_table,
274 config.station_id,
275 startdate_text,
276 enddate_text );
277 if (domyquery (mysql_querytext, &mysql_result_local))
278 exit (1); /* failed and reported */
279 while (mysql_row_local = mysql_fetch_row (mysql_result_local)) /* got something */
280 {
281 local_time = atol (mysql_row_local [0]);
282 local_pressure = atof (mysql_row_local [1]);
283 if (local_time > interval_starttime) /* we're in business */
284 {
285 /*
286 * Unlike with temperatures, we only look at the first pressure reading
287 * newer than the BoM reading. We have no way of knowing if the BoM has
288 * similar changes to what we have locally.
289 */
290 while (local_time > interval_endtime) /* beyond current BoM readings */
291 {
292 interval_starttime = interval_endtime;
293 interval_startpressure = interval_endpressure;
294 /* Next row for first interval */
295 if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* finished? */
296 {
297 finishing = 1;
298 interval_endtime += 1000; /* to avoid division by 0 */
299 }
300 else
301 {
302 interval_endtime = atol (mysql_row_BoM [0]);
303 interval_endpressure = atof (mysql_row_BoM [1]);
304 }
305 }
306
307 /*
308 * Now our local reading is between start and end times. Interpolate
309 * to get BoM pressure at this time.
310 */
311 interval_pressure = interval_startpressure
312 + (interval_endpressure - interval_startpressure)
313 * (local_time - interval_starttime) / (interval_endtime - interval_starttime);
314 fprintf (myfile,
315 "%d %d %2.1f %2.1f %2.1f # %s",
316 local_time,
317 local_time - my2k,
318 local_pressure,
319 interval_pressure,
320 interval_pressure - local_pressure,
321 ctime (&local_time) );
322 local_time = interval_endtime + 1; /* force the next record */
323
324 if (finishing)
325 {
326 fclose (myfile);
327 return;
328 }
329 /* Next remote row */
330 interval_starttime = interval_endtime;
331 interval_starttemp = interval_endtemp;
332 if (! (mysql_row_BoM = mysql_fetch_row (mysql_result_BoM))) /* finished? */
333 finishing = 1;
334 else
335 {
336 interval_endtime = atol (mysql_row_BoM [0]);
337 interval_endtemp = atof (mysql_row_BoM [1]);
338 }
339 }
340 }
341 }
342 fclose (myfile);
343 }
344
345 int main (int argc, char *argv [])
346 {
347 int station;
348 /*
349 * This is tacky. We want to use the standard parameter sequence in
350 * read_config (), but that doesn't include dates. So we require the sequence
351 * in usage (). And that means that we need to find a way to specify the end
352 * day. FIXME.
353 */
354
355 if (argc < 3) /* we need at least start date */
356 usage (argv [0]);
357 startdate_text = argv [1]; /* start and */
358 enddate_text = argv [1]; /* end date */
359 read_config (argc - 2, &argv [2]); /* this implies that we know argv [0] won't be used */
360
361 mysql = mysql_init (mysql);
362 if (! mysql)
363 {
364 syslog (LOG_ERR | LOG_DAEMON, "Can't initialize MySQL: insufficient memory\n");
365 exit (1);
366 }
367 if (! mysql_real_connect (mysql, /* DB state */
368 config.db_host, /* database host */
369 config.db_user,
370 config.db_passwd,
371 config.db,
372 0,
373 NULL,
374 0 ))
375 {
376 syslog (LOG_ERR | LOG_DAEMON,
377 "Can't connect to databaase: %s (%d)\n",
378 mysql_error (mysql),
379 mysql_errno (mysql) );
380 exit (1);
381 }
382
383 /* Generate the files */
384 for (station = 0; station < station_count; station++)
385 {
386 mktempcompare (stations [station]);
387 mkpresscompare (stations [station]);
388 }
389 exit (0);
390 }