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 #include <stdio.h>
7 #include <math.h>
8 #include <string.h>
9 #include <assert.h>
10 
11 #include "quadfile.h"
12 #include "qfits_header.h"
13 #include "fitsioutils.h"
14 #include "starutil.h"
15 #include "ioutils.h"
16 #include "errors.h"
17 #include "an-endian.h"
18 
19 #define CHUNK_QUADS 0
20 
quads_chunk(quadfile_t * qf)21 static fitsbin_chunk_t* quads_chunk(quadfile_t* qf) {
22     return fitsbin_get_chunk(qf->fb, CHUNK_QUADS);
23 }
24 
callback_read_header(fitsbin_t * fb,fitsbin_chunk_t * chunk)25 static int callback_read_header(fitsbin_t* fb, fitsbin_chunk_t* chunk) {
26     qfits_header* primheader = fitsbin_get_primary_header(fb);
27     quadfile_t* qf = chunk->userdata;
28 
29     qf->dimquads = qfits_header_getint(primheader, "DIMQUADS", 4);
30     qf->numquads = qfits_header_getint(primheader, "NQUADS", -1);
31     qf->numstars = qfits_header_getint(primheader, "NSTARS", -1);
32     qf->index_scale_upper = qfits_header_getdouble(primheader, "SCALE_U", -1.0);
33     qf->index_scale_lower = qfits_header_getdouble(primheader, "SCALE_L", -1.0);
34     qf->indexid = qfits_header_getint(primheader, "INDEXID", 0);
35     qf->healpix = qfits_header_getint(primheader, "HEALPIX", -1);
36     qf->hpnside = qfits_header_getint(primheader, "HPNSIDE", 1);
37 
38     if ((qf->numquads == -1) || (qf->numstars == -1) ||
39         (qf->index_scale_upper == -1.0) || (qf->index_scale_lower == -1.0)) {
40         ERROR("Couldn't find NQUADS or NSTARS or SCALE_U or SCALE_L entries in FITS header");
41         return -1;
42     }
43     if (fits_check_endian(primheader)) {
44         ERROR("Quad file was written with the wrong endianness");
45         return -1;
46     }
47 
48     chunk->itemsize = qf->dimquads * sizeof(uint32_t);
49     chunk->nrows = qf->numquads;
50     return 0;
51 }
52 
new_quadfile(const char * fn,anqfits_t * fits,anbool writing)53 static quadfile_t* new_quadfile(const char* fn, anqfits_t* fits, anbool writing) {
54     quadfile_t* qf;
55     fitsbin_chunk_t chunk;
56     qf = calloc(1, sizeof(quadfile_t));
57     if (!qf) {
58         SYSERROR("Couldn't malloc a quadfile struct");
59         return NULL;
60     }
61     qf->healpix = -1;
62     qf->hpnside = 1;
63 
64     if (writing)
65         if (fn) {
66             qf->fb = fitsbin_open_for_writing(fn);
67         } else {
68             qf->fb = fitsbin_open_in_memory();
69         }
70     else {
71         if (fits)
72             qf->fb = fitsbin_open_fits(fits);
73         else
74             qf->fb = fitsbin_open(fn);
75     }
76     if (!qf->fb) {
77         ERROR("Failed to create fitsbin");
78         free(qf); //# Modified by Robert Lancaster for the StellarSolver Internal Library, to prevent leak
79         return NULL;
80     }
81 
82     fitsbin_chunk_init(&chunk);
83     chunk.tablename = "quads";
84     chunk.required = 1;
85     chunk.callback_read_header = callback_read_header;
86     chunk.userdata = qf;
87     fitsbin_add_chunk(qf->fb, &chunk);
88     fitsbin_chunk_clean(&chunk);
89 
90     return qf;
91 }
92 
quadfile_check(const quadfile_t * qf)93 int quadfile_check(const quadfile_t* qf) {
94     int q, i;
95     if (qf->dimquads < 3 || qf->dimquads > DQMAX) {
96         ERROR("Dimquads has illegal value %i", qf->dimquads);
97         return -1;
98     }
99     for (q=0; q<qf->numquads; q++) {
100         unsigned int stars[DQMAX];
101         if (quadfile_get_stars(qf, q, stars)) {
102             ERROR("Failed to get quad %i of %i", q, qf->numquads);
103             return -1;
104         }
105         for (i=0; i<qf->dimquads; i++) {
106             if (stars[i] >= qf->numstars) {
107                 ERROR("Star ID %i is out of bounds: num stars %i", stars[i], qf->numstars);
108                 return -1;
109             }
110         }
111     }
112     return 0;
113 }
114 
quadfile_dimquads(const quadfile_t * qf)115 int quadfile_dimquads(const quadfile_t* qf) {
116     return qf->dimquads;
117 }
118 
quadfile_nquads(const quadfile_t * qf)119 int quadfile_nquads(const quadfile_t* qf) {
120     return qf->numquads;
121 }
122 
quadfile_get_header(const quadfile_t * qf)123 qfits_header* quadfile_get_header(const quadfile_t* qf) {
124     return fitsbin_get_primary_header(qf->fb);
125 }
126 
my_open(const char * fn,anqfits_t * fits)127 static quadfile_t* my_open(const char* fn, anqfits_t* fits) {
128     quadfile_t* qf = NULL;
129     fitsbin_chunk_t* chunk;
130 
131     qf = new_quadfile(fn, fits, FALSE);
132     if (!qf)
133         goto bailout;
134     if (fitsbin_read(qf->fb)) {
135         ERROR("Failed to open quads file");
136         goto bailout;
137     }
138     chunk = quads_chunk(qf);
139     qf->quadarray = chunk->data;
140 
141     // close fd.
142     if (qf->fb->fid) {
143         if (fclose(qf->fb->fid)) {
144             ERROR("Failed to close quadfile FID");
145             goto bailout;
146         }
147         qf->fb->fid = NULL;
148     }
149 
150     return qf;
151 
152  bailout:
153     if (qf)
154         quadfile_close(qf);
155     return NULL;
156 }
157 
quadfile_get_filename(const quadfile_t * qf)158 char* quadfile_get_filename(const quadfile_t* qf) {
159     return fitsbin_get_filename(qf->fb);
160 }
161 
quadfile_open_fits(anqfits_t * fits)162 quadfile_t* quadfile_open_fits(anqfits_t* fits) {
163     return my_open(NULL, fits);
164 }
165 
quadfile_open(const char * fn)166 quadfile_t* quadfile_open(const char* fn) {
167     return my_open(fn, NULL);
168 }
169 
quadfile_close(quadfile_t * qf)170 int quadfile_close(quadfile_t* qf) {
171     int rtn;
172     if (!qf) return 0;
173     rtn = fitsbin_close(qf->fb);
174     free(qf);
175     return rtn;
176 }
177 
open_for_writing(const char * fn)178 static quadfile_t* open_for_writing(const char* fn) {
179     quadfile_t* qf;
180     qfits_header* hdr;
181     qf = new_quadfile(fn, NULL, TRUE);
182     if (!qf)
183         goto bailout;
184     qf->dimquads = 4;
185     // add default values to header
186     hdr = fitsbin_get_primary_header(qf->fb);
187     fits_add_endian(hdr);
188     qfits_header_add(hdr, "AN_FILE", "QUAD", "This file lists, for each quad, its stars.", NULL);
189     qfits_header_add(hdr, "DIMQUADS", "0", "", NULL);
190     qfits_header_add(hdr, "NQUADS", "0", "", NULL);
191     qfits_header_add(hdr, "NSTARS", "0", "", NULL);
192     qfits_header_add(hdr, "SCALE_U", "0.0", "", NULL);
193     qfits_header_add(hdr, "SCALE_L", "0.0", "", NULL);
194     qfits_header_add(hdr, "INDEXID", "0", "", NULL);
195     qfits_header_add(hdr, "HEALPIX", "-1", "", NULL);
196     qfits_header_add(hdr, "HPNSIDE", "1", "", NULL);
197     fits_add_long_comment(hdr, "The first extension contains the quads "
198                           "stored as %i 32-bit native-endian unsigned ints.", qf->dimquads);
199     return qf;
200 
201  bailout:
202     if (qf)
203         quadfile_close(qf);
204     return NULL;
205 }
206 
quadfile_open_for_writing(const char * fn)207 quadfile_t* quadfile_open_for_writing(const char* fn) {
208     if (!fn) {
209         ERROR("Non-NULL filename required");
210         return NULL;
211     }
212     return open_for_writing(fn);
213 }
214 
quadfile_open_in_memory()215 quadfile_t* quadfile_open_in_memory() {
216     return open_for_writing(NULL);
217 }
218 
quadfile_switch_to_reading(quadfile_t * qf)219 int quadfile_switch_to_reading(quadfile_t* qf) {
220     if (quadfile_fix_header(qf)) {
221         ERROR("Failed to fix quads header");
222         return -1;
223     }
224     if (fitsbin_switch_to_reading(qf->fb)) {
225         ERROR("Failed to switch to read mode");
226         return -1;
227     }
228     if (fitsbin_read(qf->fb)) {
229         ERROR("Failed to open quads file");
230         return -1;
231     }
232     qf->quadarray = quads_chunk(qf)->data;
233     return 0;
234 }
235 
add_to_header(qfits_header * hdr,quadfile_t * qf)236 static void add_to_header(qfits_header* hdr, quadfile_t* qf) {
237     fits_header_mod_int(hdr, "DIMQUADS", qf->dimquads, "Number of stars in a quad.");
238     fits_header_mod_int(hdr, "NQUADS", qf->numquads, "Number of quads.");
239     fits_header_mod_int(hdr, "NSTARS", qf->numstars, "Number of stars.");
240     fits_header_mod_double(hdr, "SCALE_U", qf->index_scale_upper, "Upper-bound index scale (radians).");
241     fits_header_mod_double(hdr, "SCALE_L", qf->index_scale_lower, "Lower-bound index scale (radians).");
242     fits_header_mod_int(hdr, "INDEXID", qf->indexid, "Index unique ID.");
243     fits_header_mod_int(hdr, "HEALPIX", qf->healpix, "Healpix of this index.");
244     fits_header_mod_int(hdr, "HPNSIDE", qf->hpnside, "Nside of the healpixelization");
245 }
246 
quadfile_write_header(quadfile_t * qf)247 int quadfile_write_header(quadfile_t* qf) {
248     fitsbin_t* fb = qf->fb;
249     fitsbin_chunk_t* chunk = quads_chunk(qf);
250     qfits_header* hdr;
251     chunk->itemsize = qf->dimquads * sizeof(uint32_t);
252     chunk->nrows = qf->numquads;
253 
254     hdr = fitsbin_get_primary_header(fb);
255     add_to_header(hdr, qf);
256 
257     if (fitsbin_write_primary_header(fb) ||
258         fitsbin_write_chunk_header(fb, chunk)) {
259         ERROR("Failed to write quadfile header");
260         return -1;
261     }
262     return 0;
263 }
264 
quadfile_write_header_to(quadfile_t * qf,FILE * fid)265 int quadfile_write_header_to(quadfile_t* qf, FILE* fid) {
266     fitsbin_t* fb = qf->fb;
267     fitsbin_chunk_t* chunk = quads_chunk(qf);
268     qfits_header* hdr;
269     chunk->itemsize = qf->dimquads * sizeof(uint32_t);
270     chunk->nrows = qf->numquads;
271     hdr = fitsbin_get_primary_header(fb);
272     add_to_header(hdr, qf);
273 
274     if (fitsbin_write_primary_header_to(fb, fid) ||
275         fitsbin_write_chunk_header_to(fb, chunk, fid)) {
276         ERROR("Failed to write quadfile header");
277         return -1;
278     }
279     return 0;
280 }
281 
quadfile_write_quad(quadfile_t * qf,unsigned int * stars)282 int quadfile_write_quad(quadfile_t* qf, unsigned int* stars) {
283     uint32_t* data;
284 
285 #ifndef _MSC_VER //# Modified by Robert Lancaster for the StellarSolver Internal Library
286         uint32_t ustars[qf->dimquads];
287 #else
288         uint32_t* ustars = (uint32_t*)malloc(sizeof(uint32_t)*qf->dimquads);
289 #endif
290 
291     int i;
292     fitsbin_chunk_t* chunk = quads_chunk(qf);
293 
294     if (sizeof(uint32_t) == sizeof(uint)) {
295         data = stars;
296     } else {
297         data = ustars;
298         for (i=0; i<qf->dimquads; i++)
299             ustars[i] = stars[i];
300     }
301 #ifdef _MSC_VER //# Modified by Robert Lancaster for the StellarSolver Internal Library
302         free(ustars);
303 #endif
304     if (fitsbin_write_item(qf->fb, chunk, data)) {
305         ERROR("Failed to write a quad");
306         return -1;
307     }
308     qf->numquads++;
309     return 0;
310 }
311 
quadfile_write_all_quads_to(quadfile_t * qf,FILE * fid)312 int quadfile_write_all_quads_to(quadfile_t* qf, FILE* fid) {
313     fitsbin_chunk_t* chunk = quads_chunk(qf);
314     if (fitsbin_write_items_to(chunk, qf->quadarray, quadfile_nquads(qf), fid)) {
315         ERROR("Failed to write %i quads", quadfile_nquads(qf));
316         return -1;
317     }
318     return 0;
319 }
320 
quadfile_fix_header(quadfile_t * qf)321 int quadfile_fix_header(quadfile_t* qf) {
322     qfits_header* hdr;
323     fitsbin_t* fb = qf->fb;
324     fitsbin_chunk_t* chunk = quads_chunk(qf);
325 
326     chunk->itemsize = qf->dimquads * sizeof(uint32_t);
327     chunk->nrows = qf->numquads;
328 
329     hdr = fitsbin_get_primary_header(fb);
330     add_to_header(hdr, qf);
331 
332     if (fitsbin_fix_primary_header(fb) ||
333         fitsbin_fix_chunk_header(fb, chunk)) {
334         ERROR("Failed to fix quad header");
335         return -1;
336     }
337     return 0;
338 }
339 
quadfile_get_index_scale_upper_arcsec(const quadfile_t * qf)340 double quadfile_get_index_scale_upper_arcsec(const quadfile_t* qf) {
341     return rad2arcsec(qf->index_scale_upper);
342 }
343 
quadfile_get_index_scale_lower_arcsec(const quadfile_t * qf)344 double quadfile_get_index_scale_lower_arcsec(const quadfile_t* qf) {
345     return rad2arcsec(qf->index_scale_lower);
346 }
347 
quadfile_get_stars(const quadfile_t * qf,unsigned int quadid,unsigned int * stars)348 int quadfile_get_stars(const quadfile_t* qf, unsigned int quadid, unsigned int* stars) {
349     int i;
350     if (quadid >= qf->numquads) {
351         ERROR("Requested quadid %i, but number of quads is %i",	quadid, qf->numquads);
352         assert(quadid < qf->numquads);
353         return -1;
354     }
355 
356     for (i=0; i<qf->dimquads; i++) {
357         stars[i] = qf->quadarray[quadid * qf->dimquads + i];
358     }
359     return 0;
360 }
361 
362 
363 
364