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 <string.h>
7 #include <unistd.h>
8 #include <assert.h>
9 
10 #include "codefile.h"
11 #include "starutil.h"
12 #include "ioutils.h"
13 #include "fitsioutils.h"
14 #include "errors.h"
15 #include "quad-utils.h"
16 
quad_write(codefile_t * codes,quadfile_t * quads,unsigned int * quad,startree_t * starkd,int dimquads,int dimcodes)17 void quad_write(codefile_t* codes, quadfile_t* quads,
18                 unsigned int* quad, startree_t* starkd,
19                 int dimquads, int dimcodes) {
20     double code[DCMAX];
21     quad_compute_code(quad, dimquads, starkd, code);
22     quad_enforce_invariants(quad, code, dimquads, dimcodes);
23     codefile_write_code(codes, code);
24     quadfile_write_quad(quads, quad);
25 }
26 
quad_write_const(codefile_t * codes,quadfile_t * quads,const unsigned int * quad,startree_t * starkd,int dimquads,int dimcodes)27 void quad_write_const(codefile_t* codes, quadfile_t* quads,
28                       const unsigned int* quad, startree_t* starkd,
29                       int dimquads, int dimcodes) {
30     int k;
31     unsigned int quadcopy[DQMAX];
32     for (k=0; k<dimquads; k++)
33         quadcopy[k] = quad[k];
34     quad_write(codes, quads, quadcopy, starkd, dimquads, dimcodes);
35 }
36 
codefile_compute_field_code(const double * xy,double * code,int dimquads)37 void codefile_compute_field_code(const double* xy, double* code, int dimquads) {
38     double Ax, Ay;
39     double Bx, By;
40     double ABx, ABy;
41     double scale, invscale;
42     double costheta, sintheta;
43     int i;
44 
45     Ax = xy[2*0 + 0];
46     Ay = xy[2*0 + 1];
47     Bx = xy[2*1 + 0];
48     By = xy[2*1 + 1];
49     ABx = Bx - Ax;
50     ABy = By - Ay;
51     scale = (ABx * ABx) + (ABy * ABy);
52     invscale = 1.0 / scale;
53     costheta = (ABy + ABx) * invscale;
54     sintheta = (ABy - ABx) * invscale;
55 
56     // starting with star C...
57     for (i=2; i<dimquads; i++) {
58         double Cx, Cy;
59         double x, y;
60         Cx = xy[2*i + 0];
61         Cy = xy[2*i + 1];
62         Cx -= Ax;
63         Cy -= Ay;
64         x =  Cx * costheta + Cy * sintheta;
65         y = -Cx * sintheta + Cy * costheta;
66         code[2*(i-2)+0] = x;
67         code[2*(i-2)+1] = y;
68     }
69 }
70 
codefile_compute_star_code(const double * starxyz,double * code,int dimquads)71 void codefile_compute_star_code(const double* starxyz, double* code, int dimquads) {
72     quad_compute_star_code(starxyz, code, dimquads);
73 }
74 
75 
76 #define CHUNK_CODES 0
77 
codes_chunk(codefile_t * cf)78 static fitsbin_chunk_t* codes_chunk(codefile_t* cf) {
79     return fitsbin_get_chunk(cf->fb, CHUNK_CODES);
80 }
81 
callback_read_header(fitsbin_t * fb,fitsbin_chunk_t * chunk)82 static int callback_read_header(fitsbin_t* fb, fitsbin_chunk_t* chunk) {
83     qfits_header* primheader = fitsbin_get_primary_header(fb);
84     codefile_t* cf = chunk->userdata;
85 
86     cf->dimcodes = qfits_header_getint(primheader, "DIMCODES", 4);
87     cf->numcodes = qfits_header_getint(primheader, "NCODES", -1);
88     cf->numstars = qfits_header_getint(primheader, "NSTARS", -1);
89     cf->index_scale_upper = qfits_header_getdouble(primheader, "SCALE_U", -1.0);
90     cf->index_scale_lower = qfits_header_getdouble(primheader, "SCALE_L", -1.0);
91     cf->indexid = qfits_header_getint(primheader, "INDEXID", 0);
92     cf->healpix = qfits_header_getint(primheader, "HEALPIX", -1);
93     cf->hpnside = qfits_header_getint(primheader, "HPNSIDE", 1);
94 
95     if ((cf->numcodes == -1) || (cf->numstars == -1) ||
96         (cf->index_scale_upper == -1.0) || (cf->index_scale_lower == -1.0)) {
97         ERROR("Couldn't find NCODES or NSTARS or SCALE_U or SCALE_L entries in FITS header");
98         return -1;
99     }
100     if (fits_check_endian(primheader)) {
101         ERROR("File was written with the wrong endianness");
102         return -1;
103     }
104     chunk->itemsize = cf->dimcodes * sizeof(double);
105     chunk->nrows = cf->numcodes;
106     return 0;
107 }
108 
new_codefile(const char * fn,anbool writing,anbool inmem)109 static codefile_t* new_codefile(const char* fn, anbool writing, anbool inmem) {
110     fitsbin_chunk_t chunk;
111     codefile_t* cf = calloc(1, sizeof(codefile_t));
112     if (!cf) {
113         SYSERROR("Couldn't calloc a codefile struct");
114         return NULL;
115     }
116     cf->healpix = -1;
117     cf->hpnside = 1;
118 
119     if (inmem) {
120         cf->fb = fitsbin_open_in_memory();
121     } else {
122         if (writing)
123             cf->fb = fitsbin_open_for_writing(fn);
124         else
125             cf->fb = fitsbin_open(fn);
126     }
127     if (!cf->fb) {
128         ERROR("Failed to create fitsbin");
129         return NULL;
130     }
131 
132     fitsbin_chunk_init(&chunk);
133     chunk.tablename = "codes";
134     chunk.required = 1;
135     chunk.callback_read_header = callback_read_header;
136     chunk.userdata = cf;
137     fitsbin_add_chunk(cf->fb, &chunk);
138     fitsbin_chunk_clean(&chunk);
139 
140     return cf;
141 }
142 
codefile_get_code(const codefile_t * cf,int codeid,double * code)143 void codefile_get_code(const codefile_t* cf, int codeid, double* code) {
144     int i;
145     if (codeid >= cf->numcodes) {
146         ERROR("Requested codeid %i, but number of codes is %i", codeid, cf->numcodes);
147         assert(codeid < cf->numcodes);
148         // just carry on - we'll probably segfault.
149     }
150     for (i=0; i<cf->dimcodes; i++)
151         code[i] = cf->codearray[codeid * cf->dimcodes + i];
152 }
153 
codefile_close(codefile_t * cf)154 int codefile_close(codefile_t* cf) {
155     int rtn;
156     if (!cf) return 0;
157     rtn = fitsbin_close(cf->fb);
158     free(cf);
159     return rtn;
160 }
161 
codefile_open(const char * fn)162 codefile_t* codefile_open(const char* fn) {
163     codefile_t* cf = NULL;
164 
165     cf = new_codefile(fn, FALSE, FALSE);
166     if (!cf)
167         goto bailout;
168     if (fitsbin_read(cf->fb)) {
169         ERROR("Failed to open codes file");
170         goto bailout;
171     }
172     cf->codearray = codes_chunk(cf)->data;
173     return cf;
174 
175  bailout:
176     if (cf)
177         codefile_close(cf);
178     return NULL;
179 }
180 
open_for_writing(const char * fn)181 static codefile_t* open_for_writing(const char* fn) {
182     codefile_t* cf;
183     qfits_header* hdr;
184     if (fn)
185         cf = new_codefile(fn, TRUE, FALSE);
186     else
187         cf = new_codefile(fn, TRUE, TRUE);
188     if (!cf)
189         goto bailout;
190     // default
191     cf->dimcodes = 4;
192 
193     // add default values to header
194     hdr = codefile_get_header(cf);
195     fits_add_endian(hdr);
196     qfits_header_add(hdr, "AN_FILE", "CODE", "This file lists the code for each quad.", NULL);
197     qfits_header_add(hdr, "NCODES", "0", "", NULL);
198     qfits_header_add(hdr, "NSTARS", "0", "", NULL);
199     fits_header_add_int(hdr, "DIMCODES", cf->dimcodes, "");
200     qfits_header_add(hdr, "SCALE_U", "0.0", "", NULL);
201     qfits_header_add(hdr, "SCALE_L", "0.0", "", NULL);
202     qfits_header_add(hdr, "INDEXID", "0", "", NULL);
203     qfits_header_add(hdr, "HEALPIX", "-1", "", NULL);
204     qfits_header_add(hdr, "HPNSIDE", "1", "", NULL);
205     fits_add_long_comment(hdr, "The first extension contains the codes "
206                           "stored as %i native-endian doubles.  "
207                           "(the quad location in %i-D code space)", cf->dimcodes, cf->dimcodes);
208     return cf;
209  bailout:
210     if (cf)
211         codefile_close(cf);
212     return NULL;
213 }
214 
codefile_open_for_writing(const char * fn)215 codefile_t* codefile_open_for_writing(const char* fn) {
216     if (!fn) {
217         ERROR("Non-NULL filename required");
218         return NULL;
219     }
220     return open_for_writing(fn);
221 }
222 
codefile_open_in_memory()223 codefile_t* codefile_open_in_memory() {
224     return open_for_writing(NULL);
225 }
226 
codefile_switch_to_reading(codefile_t * cf)227 int codefile_switch_to_reading(codefile_t* cf) {
228     if (codefile_fix_header(cf)) {
229         ERROR("Failed to fix codes header");
230         return -1;
231     }
232     if (fitsbin_switch_to_reading(cf->fb)) {
233         ERROR("Failed to switch to read mode");
234         return -1;
235     }
236     if (fitsbin_read(cf->fb)) {
237         ERROR("Failed to open codes file");
238         return -1;
239     }
240     cf->codearray = codes_chunk(cf)->data;
241     return 0;
242 }
243 
codefile_write_header(codefile_t * cf)244 int codefile_write_header(codefile_t* cf) {
245     fitsbin_t* fb = cf->fb;
246     fitsbin_chunk_t* chunk = codes_chunk(cf);
247     chunk->itemsize = cf->dimcodes * sizeof(double);
248     chunk->nrows = cf->numcodes;
249 
250     if (fitsbin_write_primary_header(fb) ||
251         fitsbin_write_chunk_header(fb, chunk)) {
252         ERROR("Failed to write codefile header");
253         return -1;
254     }
255     return 0;
256 }
257 
codefile_fix_header(codefile_t * cf)258 int codefile_fix_header(codefile_t* cf) {
259     qfits_header* hdr;
260     fitsbin_t* fb = cf->fb;
261     fitsbin_chunk_t* chunk = codes_chunk(cf);
262     chunk->itemsize = cf->dimcodes * sizeof(double);
263     chunk->nrows = cf->numcodes;
264 
265     hdr = codefile_get_header(cf);
266 
267     // fill in the real values...
268     fits_header_mod_int(hdr, "DIMCODES", cf->dimcodes, "Number of values in a code.");
269     fits_header_mod_int(hdr, "NCODES", cf->numcodes, "Number of codes.");
270     fits_header_mod_int(hdr, "NSTARS", cf->numstars, "Number of stars.");
271     fits_header_mod_double(hdr, "SCALE_U", cf->index_scale_upper, "Upper-bound index scale (radians).");
272     fits_header_mod_double(hdr, "SCALE_L", cf->index_scale_lower, "Lower-bound index scale (radians).");
273     fits_header_mod_int(hdr, "INDEXID", cf->indexid, "Index unique ID.");
274     fits_header_mod_int(hdr, "HEALPIX", cf->healpix, "Healpix of this index.");
275     fits_header_mod_int(hdr, "HPNSIDE", cf->hpnside, "Nside of the healpixelization");
276 
277     if (fitsbin_fix_primary_header(fb) ||
278         fitsbin_fix_chunk_header(fb, chunk)) {
279         ERROR("Failed to fix code header");
280         return -1;
281     }
282     return 0;
283 }
284 
codefile_write_code(codefile_t * cf,double * code)285 int codefile_write_code(codefile_t* cf, double* code) {
286     fitsbin_chunk_t* chunk = codes_chunk(cf);
287     if (fitsbin_write_item(cf->fb, chunk, code)) {
288         ERROR("Failed to write code");
289         return -1;
290     }
291     cf->numcodes++;
292     return 0;
293 }
294 
codefile_get_header(const codefile_t * cf)295 qfits_header* codefile_get_header(const codefile_t* cf) {
296     return fitsbin_get_primary_header(cf->fb);
297 }
298 
codefile_dimcodes(const codefile_t * cf)299 int codefile_dimcodes(const codefile_t* cf) {
300     return cf->dimcodes;
301 }
302