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 
10 #include "qfits_header.h"
11 #include "fitsioutils.h"
12 #include "starutil.h"
13 #include "ioutils.h"
14 #include "qidxfile.h"
15 #include "errors.h"
16 
17 #define CHUNK_QIDX 0
18 
callback_read_header(fitsbin_t * fb,fitsbin_chunk_t * chunk)19 static int callback_read_header(fitsbin_t* fb, fitsbin_chunk_t* chunk) {
20     qfits_header* primheader = fitsbin_get_primary_header(fb);
21     qidxfile* qf = chunk->userdata;
22 
23     if (fits_check_endian(primheader)) {
24         ERROR("qidx file was written with wrong endianness");
25         return -1;
26     }
27     qf->numstars = qfits_header_getint(primheader, "NSTARS", -1);
28     qf->numquads = qfits_header_getint(primheader, "NQUADS", -1);
29     qf->dimquads = qfits_header_getint(primheader, "DIMQUADS", 4);
30     if ((qf->numstars == -1) || (qf->numquads == -1)) {
31         ERROR("Couldn't find NSTARS or NQUADS entries in FITS header");
32         return -1;
33     }
34 
35     chunk->nrows = qf->numstars * 2 + qf->numquads * qf->dimquads;
36     return 0;
37 }
38 
new_qidxfile(const char * fn,anbool writing)39 static qidxfile* new_qidxfile(const char* fn, anbool writing) {
40     qidxfile* qf;
41     fitsbin_chunk_t chunk;
42 
43     qf = calloc(1, sizeof(qidxfile));
44     if (!qf) {
45         SYSERROR("Couldn't malloc a qidxfile struct");
46         return NULL;
47     }
48 
49     // default
50     qf->dimquads = 4;
51 
52     if (writing)
53         qf->fb = fitsbin_open_for_writing(fn);
54     else
55         qf->fb = fitsbin_open(fn);
56     if (!qf->fb) {
57         ERROR("Failed to create fitsbin");
58         free(qf); //# Modified by Robert Lancaster for the StellarSolver Internal Library, to prevent leak
59         return NULL;
60     }
61 
62     fitsbin_chunk_init(&chunk);
63     chunk.tablename = "qidx";
64     chunk.required = 1;
65     chunk.callback_read_header = callback_read_header;
66     chunk.userdata = qf;
67     chunk.itemsize = sizeof(uint32_t);
68     fitsbin_add_chunk(qf->fb, &chunk);
69     fitsbin_chunk_clean(&chunk);
70 
71     return qf;
72 }
73 
qidxfile_open(const char * fn)74 qidxfile* qidxfile_open(const char* fn) {
75     qidxfile* qf = NULL;
76 
77     qf = new_qidxfile(fn, FALSE);
78     if (!qf)
79         goto bailout;
80 
81     if (fitsbin_read(qf->fb)) {
82         ERROR("Failed to find qidx table.\n");
83         goto bailout;
84     }
85 
86     qf->index = fitsbin_get_chunk(qf->fb, CHUNK_QIDX)->data;
87     qf->heap  = qf->index + 2 * qf->numstars;
88     return qf;
89 
90  bailout:
91     if (qf)
92         qidxfile_close(qf);
93     return NULL;
94 }
95 
qidxfile_close(qidxfile * qf)96 int qidxfile_close(qidxfile* qf) {
97     int rtn;
98     if (!qf) return 0;
99     if (fitsbin_get_fid(qf->fb))
100         fits_pad_file(fitsbin_get_fid(qf->fb));
101     rtn = fitsbin_close(qf->fb);
102     free(qf);
103     return rtn;
104 }
105 
106 
107 
qidxfile_open_for_writing(const char * fn,int nstars,int nquads)108 qidxfile* qidxfile_open_for_writing(const char* fn, int nstars, int nquads) {
109     qidxfile* qf;
110     qfits_header* hdr;
111     qf = new_qidxfile(fn, TRUE);
112     if (!qf)
113         goto bailout;
114     qf->numstars = nstars;
115     qf->numquads = nquads;
116 
117     hdr = fitsbin_get_primary_header(qf->fb);
118     fits_add_endian(hdr);
119     fits_header_add_int(hdr, "NSTARS", qf->numstars, "Number of stars used.");
120     fits_header_add_int(hdr, "NQUADS", qf->numquads, "Number of quads used.");
121     qfits_header_add(hdr, "AN_FILE", "QIDX", "This is a quad index file.", NULL);
122     qfits_header_add(hdr, "COMMENT", "The data table of this file has two parts:", NULL, NULL);
123     qfits_header_add(hdr, "COMMENT", " -the index", NULL, NULL);
124     qfits_header_add(hdr, "COMMENT", " -the heap", NULL, NULL);
125     fits_add_long_comment(hdr, "The index contains two uint32 values for each star: the offset and "
126                           "length, in the heap, of the list of quads to which it belongs.  "
127                           "The offset and length are in units of uint32s, not bytes.  "
128                           "Offset 0 is the first uint32 in the heap.  "
129                           "The heap is ordered and tightly packed.  "
130                           "The heap is a flat list of quad indices (uint32s).");
131     return qf;
132 
133  bailout:
134     if (qf)
135         qidxfile_close(qf);
136     return NULL;
137 }
138 
qidxfile_write_header(qidxfile * qf)139 int qidxfile_write_header(qidxfile* qf) {
140     fitsbin_t* fb = qf->fb;
141     fitsbin_chunk_t* chunk;
142     chunk = fitsbin_get_chunk(fb, CHUNK_QIDX);
143     chunk->nrows = 2 * qf->numstars + qf->dimquads * qf->numquads;
144     if (fitsbin_write_primary_header(fb) ||
145         fitsbin_write_chunk_header(fb, chunk)) {
146         ERROR("Failed to write qidxfile header");
147         return -1;
148     }
149     qf->cursor_index = 0;
150     qf->cursor_heap  = 0;
151     return 0;
152 }
153 
qidxfile_write_star(qidxfile * qf,int * quads,int nquads)154 int qidxfile_write_star(qidxfile* qf, int* quads, int nquads) {
155     fitsbin_t* fb = qf->fb;
156     FILE* fid;
157     uint32_t nq;
158     int i;
159     fitsbin_chunk_t* chunk;
160 
161     fid = fitsbin_get_fid(fb);
162     chunk = fitsbin_get_chunk(fb, CHUNK_QIDX);
163 
164     // Write the offset & size:
165     if (fseeko(fid, fitsbin_get_data_start(fb, chunk) + qf->cursor_index * 2 * sizeof(uint32_t), SEEK_SET)) {
166         ERROR("qidxfile_write_star: failed to fseek");
167         return -1;
168     }
169     nq = nquads;
170     if (fitsbin_write_item(fb, chunk, &qf->cursor_heap) ||
171         fitsbin_write_item(fb, chunk, &nq)) {
172         ERROR("qidxfile_write_star: failed to write a qidx offset/size");
173         return -1;
174     }
175     // Write the quads.
176     if (fseeko(fid, fitsbin_get_data_start(fb, chunk) + qf->numstars * 2 * sizeof(uint32_t) +
177                qf->cursor_heap * sizeof(uint32_t), SEEK_SET)) {
178         SYSERROR("qidxfile_write_star: failed to fseek");
179         return -1;
180     }
181 
182     for (i=0; i<nquads; i++) {
183         // (in case uint != uint32_t)
184         uint32_t q = quads[i];
185         if (fitsbin_write_item(fb, chunk, &q)) {
186             ERROR("qidxfile_write_star: failed to write quads");
187             return -1;
188         }
189     }
190 
191     qf->cursor_index++;
192     qf->cursor_heap += nquads;
193     return 0;
194 }
195 
qidxfile_get_quads(const qidxfile * qf,int starid,uint32_t ** quads,int * nquads)196 int qidxfile_get_quads(const qidxfile* qf, int starid, uint32_t** quads, int* nquads) {
197     int heapindex = qf->index[2*starid];
198     *nquads = qf->index[2*starid + 1];
199     *quads = qf->heap + heapindex;
200     return 0;
201 }
202 
qidxfile_get_header(const qidxfile * qf)203 qfits_header* qidxfile_get_header(const qidxfile* qf) {
204     return fitsbin_get_primary_header(qf->fb);
205 }
206