1 /*
2 # This file is part of the Astrometry.net suite.
3 # Licensed under a 3-clause BSD style license - see LICENSE
4 */
5
6 /**
7 * Accepts an augmented xylist that describes a field or set of fields to solve.
8 * Reads a config file to find local indices, and merges information about the
9 * indices with the job description to create an input file for 'onefield'. Runs
10 * and merges the results.
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 #include <sys/time.h>
20 #include <time.h>
21 #include <libgen.h>
22 #include <getopt.h>
23 #include <dirent.h>
24 #include <assert.h>
25 #include <glob.h>
26
27 // Some systems (Solaris) don't have these glob symbols. Don't really need.
28 #ifndef GLOB_BRACE
29 #define GLOB_BRACE 0
30 #endif
31 #ifndef GLOB_TILDE
32 #define GLOB_TILDE 0
33 #endif
34
35 #include "os-features.h"
36 #include "tic.h"
37 #include "fileutils.h"
38 #include "ioutils.h"
39 #include "bl.h"
40 #include "an-bool.h"
41 #include "solver.h"
42 #include "math.h"
43 #include "fitsioutils.h"
44 #include "solverutils.h"
45 #include "onefield.h"
46 #include "log.h"
47 #include "errors.h"
48 #include "engine.h"
49 #include "an-opts.h"
50 #include "gslutils.h"
51
52 #include "datalog.h"
53
54 static an_option_t myopts[] = {
55 {'h', "help", no_argument, NULL, "print this help"},
56 {'v', "verbose", no_argument, NULL, "+verbose"},
57 {'c', "config", required_argument, "file",
58 "Use this config file (default: \"astrometry.cfg\" in the directory ../etc/ relative to the directory containing the \"astrometry-engine\" executable); 'none' for no config file"},
59 {'d', "base-dir", required_argument, "dir",
60 "set base directory of all output filenames."},
61 {'C', "cancel", required_argument, "file",
62 "quit solving if this file appears" },
63 {'s', "solved", required_argument, "file",
64 "write to this file when a field is solved"},
65 {'E', "to-stderr", no_argument, NULL,
66 "send log message to stderr"},
67 {'f', "inputs-from", required_argument, "file",
68 "read input filenames from the given file, \"-\" for stdin"},
69 {'i', "index", required_argument, "file(s)",
70 "use the given index files (in addition to any specified in the config file); put in quotes to use wildcards, eg: \" -i 'index-*.fits' \""},
71 {'p', "in-parallel", no_argument, NULL,
72 "run the index files in parallel"},
73 {'D', "data-log file", required_argument, "file",
74 "log data to the given filename"},
75 {'j', "job-id", required_argument, "jobid",
76 "IGNORED; purely to allow process to contain the job id!"},
77 };
78
print_help(const char * progname,bl * opts)79 static void print_help(const char* progname, bl* opts) {
80 printf("Usage: %s [options] <augmented xylist (axy) file(s)>\n", progname);
81 opts_print_help(opts, stdout, NULL, NULL);
82 }
83
84 FILE* datalogfid = NULL;
close_datalogfid()85 static void close_datalogfid() {
86 if (datalogfid) {
87 data_log_end();
88 if (fclose(datalogfid)) {
89 SYSERROR("Failed to close data log file");
90 }
91 }
92 }
93
main(int argc,char ** args)94 int main(int argc, char** args) {
95 char* default_configfn = "astrometry.cfg";
96 char* default_config_path = "../etc";
97
98 int c;
99 char* configfn = NULL;
100 int i;
101 engine_t* engine;
102 char* mydir = NULL;
103 char* basedir = NULL;
104 char* me;
105 anbool help = FALSE;
106 sl* strings = sl_new(4);
107 char* cancelfn = NULL;
108 char* solvedfn = NULL;
109 int loglvl = LOG_MSG;
110 anbool tostderr = FALSE;
111 char* infn = NULL;
112 FILE* fin = NULL;
113 anbool fromstdin = FALSE;
114
115 bl* opts = opts_from_array(myopts, sizeof(myopts)/sizeof(an_option_t), NULL);
116 sl* inds = sl_new(4);
117
118 char* datalog = NULL;
119
120 engine = engine_new();
121
122 while (1) {
123 c = opts_getopt(opts, argc, args);
124 if (c == -1)
125 break;
126 switch (c) {
127 case 'j':
128 break;
129 case 'D':
130 datalog = optarg;
131 break;
132 case 'p':
133 engine->inparallel = TRUE;
134 break;
135 case 'i':
136 sl_append(inds, optarg);
137 break;
138 case 'd':
139 basedir = optarg;
140 break;
141 case 'f':
142 infn = optarg;
143 fromstdin = streq(infn, "-");
144 break;
145 case 'E':
146 tostderr = TRUE;
147 break;
148 case 'h':
149 help = TRUE;
150 break;
151 case 'v':
152 loglvl++;
153 break;
154 case 's':
155 solvedfn = optarg;
156 case 'C':
157 cancelfn = optarg;
158 break;
159 case 'c':
160 configfn = strdup(optarg);
161 break;
162 case '?':
163 break;
164 default:
165 printf("Unknown flag %c\n", c);
166 exit( -1);
167 }
168 }
169
170 if (optind == argc && !infn) {
171 // Need extra args: filename
172 printf("You must specify at least one input file!\n\n");
173 help = TRUE;
174 }
175 if (help) {
176 print_help(args[0], opts);
177 exit(0);
178 }
179 bl_free(opts);
180
181 gslutils_use_error_system();
182
183 log_init(loglvl);
184 if (tostderr)
185 log_to(stderr);
186
187 if (datalog) {
188 datalogfid = fopen(datalog, "wb");
189 if (!datalogfid) {
190 SYSERROR("Failed to open data log file \"%s\" for writing", datalog);
191 return -1;
192 }
193 atexit(close_datalogfid);
194 data_log_init(100);
195 data_log_enable_all();
196 data_log_to(datalogfid);
197 data_log_start();
198 }
199
200 if (infn) {
201 logverb("Reading input filenames from %s\n", (fromstdin ? "stdin" : infn));
202 if (!fromstdin) {
203 fin = fopen(infn, "rb");
204 if (!fin) {
205 ERROR("Failed to open file %s for reading input filenames", infn);
206 exit(-1);
207 }
208 } else
209 fin = stdin;
210 }
211
212 // directory containing the 'engine' executable:
213 me = find_executable(args[0], NULL);
214 if (!me)
215 me = strdup(args[0]);
216 mydir = sl_append(strings, dirname(me));
217 free(me);
218
219 // Read config file
220 if (!configfn) {
221 int i;
222 sl* trycf = sl_new(4);
223 sl_appendf(trycf, "%s/%s/%s", mydir, default_config_path, default_configfn);
224 // if I'm in /usr/bin, look for config file in /etc
225 if (streq(mydir, "/usr/bin")) {
226 sl_appendf(trycf, "/etc/%s", default_configfn);
227 }
228 sl_appendf(trycf, "%s/%s", mydir, default_configfn);
229 sl_appendf(trycf, "./%s", default_configfn);
230 sl_appendf(trycf, "./%s/%s", default_config_path, default_configfn);
231 for (i=0; i<sl_size(trycf); i++) {
232 char* cf = sl_get(trycf, i);
233 if (file_exists(cf)) {
234 configfn = strdup(cf);
235 logverb("Using config file \"%s\"\n", cf);
236 break;
237 } else {
238 logverb("Config file \"%s\" doesn't exist.\n", cf);
239 }
240 }
241 if (!configfn) {
242 char* cflist = sl_join(trycf, "\n ");
243 logerr("Couldn't find config file: tried:\n %s\n", cflist);
244 free(cflist);
245 }
246 sl_free2(trycf);
247 }
248
249 if (!streq(configfn, "none")) {
250 if (engine_parse_config_file(engine, configfn)) {
251 logerr("Failed to parse (or encountered an error while interpreting) config file \"%s\"\n", configfn);
252 exit( -1);
253 }
254 }
255
256 if (sl_size(inds)) {
257 // Expand globs.
258 for (i=0; i<sl_size(inds); i++) {
259 char* s = sl_get(inds, i);
260 glob_t myglob;
261 int flags = GLOB_TILDE | GLOB_BRACE;
262 if (glob(s, flags, NULL, &myglob)) {
263 SYSERROR("Failed to expand wildcards in index-file path \"%s\"", s);
264 exit(-1);
265 }
266 for (c=0; c<myglob.gl_pathc; c++) {
267 if (engine_add_index(engine, myglob.gl_pathv[c])) {
268 ERROR("Failed to add index \"%s\"", myglob.gl_pathv[c]);
269 exit(-1);
270 }
271 }
272 globfree(&myglob);
273 }
274 }
275
276 if (!pl_size(engine->indexes)) {
277 logerr("\n\n"
278 "---------------------------------------------------------------------\n"
279 "You must list at least one index in the config file (%s)\n\n"
280 "See http://astrometry.net/use.html about how to get some index files.\n"
281 "---------------------------------------------------------------------\n"
282 "\n", configfn);
283 exit(-1);
284 }
285
286 if (engine->minwidth <= 0.0 || engine->maxwidth <= 0.0) {
287 logerr("\"minwidth\" and \"maxwidth\" in the config file %s must be positive!\n", configfn);
288 exit(-1);
289 }
290
291 free(configfn);
292
293 if (!il_size(engine->default_depths)) {
294 parse_depth_string(engine->default_depths,
295 "10 20 30 40 50 60 70 80 90 100 "
296 "110 120 130 140 150 160 170 180 190 200");
297 }
298
299 engine->cancelfn = cancelfn;
300 engine->solvedfn = solvedfn;
301
302 i = optind;
303 while (1) {
304 char* jobfn;
305 job_t* job;
306 struct timeval tv1, tv2;
307
308 if (infn) {
309 // Read name of next input file to be read.
310 logverb("\nWaiting for next input filename...\n");
311 jobfn = read_string_terminated(fin, "\n\r\0", 3, FALSE);
312 if (strlen(jobfn) == 0)
313 break;
314 } else {
315 if (i == argc)
316 break;
317 jobfn = args[i];
318 i++;
319 }
320 gettimeofday(&tv1, NULL);
321 logmsg("Reading file \"%s\"...\n", jobfn);
322 job = engine_read_job_file(engine, jobfn);
323 if (!job) {
324 ERROR("Failed to read job file \"%s\"", jobfn);
325 exit(-1);
326 }
327
328 if (basedir) {
329 logverb("Setting job's output base directory to %s\n", basedir);
330 job_set_output_base_dir(job, basedir);
331 }
332
333 if (engine_run_job(engine, job))
334 logerr("Failed to run_job()\n");
335
336 job_free(job);
337 gettimeofday(&tv2, NULL);
338 logverb("Spent %g seconds on this field.\n", millis_between(&tv1, &tv2)/1000.0);
339 }
340
341 engine_free(engine);
342 sl_free2(strings);
343 sl_free2(inds);
344
345 if (fin && !fromstdin)
346 fclose(fin);
347
348 return 0;
349 }
350