Mercurial > ~darius > hgwebdir.cgi > wh1080
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 } |