ERIS Pipeline Reference Manual 1.8.15
eris_nix_casu_match.c
1/* $Id: casu_match.c,v 1.2 2015/08/07 13:06:54 jim Exp $
2 *
3 * ------------------------------------------------------------------------
4 * This file is a copy of casu_match.c, modified to handle the needs of
5 * ERIS/NIX. The changes are to cope with the smaller pixel size and FOV
6 * of NIX compared to HAWKI for which the CASU methods were originally
7 * developed.
8 * ------------------------------------------------------------------------
9 *
10 * This file is part of the CASU Pipeline utilities
11 * Copyright (C) 2015 European Southern Observatory
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28/*
29 * $Author: jim $
30 * $Date: 2015/08/07 13:06:54 $
31 * $Revision: 1.2 $
32 * $Name: $
33 */
34
35/* Includes */
36
37#ifdef HAVE_CONFIG_H
38#include <config.h>
39#endif
40
41#include "eris_nix_casu_match.h"
42
43#include <math.h>
44
45#include <cpl.h>
46#include <string.h>
47#include <strings.h>
48
49#include "casu_mods.h"
50#include "casu_stats.h"
51#include "catalogue/casu_utils.h"
52
53#define NX 2048
54#define NY 2048
55#define NGRIDMAX_XY 61
56#define NGRIDMAX_STD 31
57
58static cpl_table *casu_mkmstd_table(cpl_table *objtab, cpl_table *stdstab);
59
62/*---------------------------------------------------------------------------*/
111/*---------------------------------------------------------------------------*/
112
113int eris_nix_casu_matchstds(cpl_table *objtab, cpl_table *stdstab, float srad,
114 float min_errlim, cpl_table **outtab,
115 int *status) {
116 const char *fctid = "eris_casu_matchstds";
117 char *colname;
118 int nobj,nstd,ngrid,ngrid2,ibest,ig,jg,nmatch,k,*matches,jm,l,dont,null,k2;
119 float *xstd,*ystd,*xobj,*yobj,aveden,errlim,xoffbest,yoffbest,*xoffs;
120 float *yoffs,x,y,xx2,yy2,r2,xx1,yy1,r1,xoffmed,sigx,yoffmed,sigy,xoff,yoff;
121 cpl_propertylist *p;
122 cpl_table *mstds;
123 cpl_array *colnames;
124
125 /* Inherited status */
126
127 *outtab = NULL;
128 if (*status != CASU_OK)
129 return(*status);
130
131 /* Check the size of each of the tables */
132
133 nobj = (int)cpl_table_get_nrow(objtab);
134 nstd = (int)cpl_table_get_nrow(stdstab);
135 if (nobj == 0) {
136 cpl_msg_warning(fctid,"Object table has no rows");
137 mstds = casu_mkmstd_table(objtab,stdstab);
138 *outtab = cpl_table_extract_selected(mstds);
139 cpl_table_delete(mstds);
140 WARN_RETURN
141 } else if (nstd == 0) {
142 cpl_msg_warning(fctid,"Standards RA/DEC table has no rows");
143 mstds = casu_mkmstd_table(objtab,stdstab);
144 *outtab = cpl_table_extract_selected(mstds);
145 cpl_table_delete(mstds);
146 WARN_RETURN
147 }
148
149 /* First, sort the two tables by the Y coordinate */
150
151 p = cpl_propertylist_new();
152 cpl_propertylist_append_bool(p,"Y_coordinate",0);
153 if (cpl_table_sort(objtab,p) != CPL_ERROR_NONE) {
154 cpl_propertylist_delete(p);
155 FATAL_ERROR
156 }
157 cpl_propertylist_erase(p,"Y_coordinate");
158 cpl_propertylist_append_bool(p,"ypredict",0);
159 if (cpl_table_sort(stdstab,p) != CPL_ERROR_NONE) {
160 cpl_propertylist_delete(p);
161 FATAL_ERROR
162 }
163 cpl_propertylist_delete(p);
164
165 /* Get the x,y coordinates for each table */
166
167 xobj = cpl_table_get_data_float(objtab,"X_coordinate");
168 yobj = cpl_table_get_data_float(objtab,"Y_coordinate");
169 xstd = cpl_table_get_data_float(stdstab,"xpredict");
170 ystd = cpl_table_get_data_float(stdstab,"ypredict");
171 if (xstd == NULL || ystd == NULL || xobj == NULL || yobj == NULL)
172 FATAL_ERROR
173
174 /* Calculate the error limit and the number of grid points */
175
176 // This is the average density of stars in the image
177 // FIXME it is incorrect to assume NX and NY are fixed
178 aveden = (float)max(nstd,nobj)/(float)(NX*NY);
179 // An initial estimate of a sensible error limit
180 errlim = 1.0/sqrt(4.0*CPL_MATH_PI*aveden);
181
182 /* Change from casu_matchstds.
183 The second parameter of min is hardwired to 15.0 in the original.
184
185 The aim is to set a sensible starting point for the tightness of
186 the initial match.
187
188 15.0 corresponds to 1.5 arcsec for the HAWKI pixel size of 0.1
189 arcsec. For NIX the pixels are 0.013 or 0.025 arcsec so this limit
190 should be higher - to match for example the inter-jitter pointing
191 error of the telescope.
192 */
193
194 cpl_msg_info(cpl_func, "min_errlim %4.2f", min_errlim);
195 errlim = min(errlim,min_errlim);
196 // The grid size to achieve this
197 ngrid = (int)(srad/errlim);
198 ngrid = (ngrid/2)*2 + 1;
199 // Constrain the number of grid points
200 ngrid = max(5,min(NGRIDMAX_STD,ngrid));
201 ngrid2 = ngrid/2 + 1;
202
203 cpl_msg_info(fctid, "Search radius is %f pixels. Using a %d x %d grid, with error limit of %f", srad, ngrid, ngrid, errlim);
204
205 /* Now search for the best solution */
206
207 ibest = 0;
208 xoffbest = 0.0;
209 yoffbest = 0.0;
210 for (ig = -ngrid2; ig <= ngrid2; ig++) {
211 xoff = (float)ig*errlim*CPL_MATH_SQRT2;
212 for (jg = -ngrid2; jg <= ngrid2; jg++) {
213 yoff = (float)jg*errlim*CPL_MATH_SQRT2;
214 nmatch = 0;
215 for (k = 0; k < nobj; k++) {
216 x = xobj[k] + xoff;
217 y = yobj[k] + yoff;
218 if (casu_fndmatch(x,y,xstd,ystd,nstd,errlim) > -1)
219 nmatch++;
220 }
221 if (nmatch > ibest) {
222 ibest = nmatch;
223 xoffbest = xoff;
224 yoffbest = yoff;
225 }
226 }
227 }
228
229 /* Now allocate some workspace so that you can calculate a good median
230 coordinate difference */
231
232 xoffs = cpl_malloc(nstd*sizeof(*xoffs));
233 yoffs = cpl_malloc(nstd*sizeof(*yoffs));
234 matches = cpl_malloc(nstd*sizeof(*matches));
235 for (k = 0; k < nstd; k++)
236 matches[k] = -1;
237
238 /* Now get the best matches */
239
240 nmatch = 0;
241 for (k = 0; k < nstd; k++) {
242 x = xstd[k] - xoffbest;
243 y = ystd[k] - yoffbest;
244 jm = casu_fndmatch(x,y,xobj,yobj,nobj,errlim);
245 if (jm > -1) {
246 dont = 0;
247 xx2 = xobj[jm] - x;
248 yy2 = yobj[jm] - y;
249 r2 = sqrt(xx2*xx2 + yy2*yy2);
250 for (l = 0; l < nstd; l++) {
251 if (matches[l] == jm) {
252 xx1 = xobj[jm] - (xstd[l] - xoffbest);
253 yy1 = yobj[jm] - (ystd[l] - yoffbest);
254 r1 = sqrt(xx1*xx1 + yy1*yy1);
255 if (r2 < r1)
256 matches[l] = -1;
257 else
258 dont = 1;
259 break;
260 }
261 }
262 if (dont == 0)
263 matches[k] = jm;
264 }
265 }
266
267 /* Now get the coordinate difference for the best matches */
268
269 for (k = 0; k < nstd; k++) {
270 jm = matches[k];
271 if (jm != -1) {
272 xoffs[nmatch] = xobj[jm] - xstd[k];
273 yoffs[nmatch] = yobj[jm] - ystd[k];
274 nmatch++;
275 }
276 }
277 if (nmatch == 0) {
278 xoffmed = 0.0;
279 sigx = 1.0;
280 yoffmed = 0.0;
281 sigy = 1.0;
282 } else {
283 casu_medmad(xoffs,NULL,nmatch,&xoffmed,&sigx);
284 sigx *= 1.48;
285 casu_medmad(yoffs,NULL,nmatch,&yoffmed,&sigy);
286 sigy *= 1.48;
287 }
288
289 cpl_msg_info(fctid, "Median offset x=%f y=%f, sigx=%f sigy=%f", xoffmed, yoffmed, sigx, sigy);
290
291 /* Now go through one final time with a reduced error box and get
292 the final matches */
293
294 errlim = 3.0*max(sigx,sigy);
295 for (k = 0; k < nstd; k++)
296 matches[k] = -1;
297 for (k = 0; k < nstd; k++) {
298 x = xstd[k] + xoffmed;
299 y = ystd[k] + yoffmed;
300 jm = casu_fndmatch(x,y,xobj,yobj,nobj,errlim);
301 if (jm > -1) {
302 dont = 0;
303 xx2 = xobj[jm] - x;
304 yy2 = yobj[jm] - y;
305 r2 = sqrt(xx2*xx2 + yy2*yy2);
306 for (l = 0; l < nstd; l++) {
307 if (matches[l] == jm) {
308 xx1 = xobj[jm] - (xstd[l] + xoffmed);
309 yy1 = yobj[jm] - (ystd[l] + yoffmed);
310 r1 = sqrt(xx1*xx1 + yy1*yy1);
311 if (r2 < r1)
312 matches[l] = -1;
313 else
314 dont = 1;
315/* break; */
316 }
317 }
318 if (dont == 0)
319 matches[k] = jm;
320 }
321 }
322 jm = matches[1];
323
324 /* Make a copy of the standards table and add all the columns from the
325 object catalogue to it. Ignore the RA and DEC columns in the catalogue
326 as the standards table will already have these.*/
327
328 mstds = cpl_table_duplicate(stdstab);
329 colnames = cpl_table_get_column_names(objtab);
330 ig = (int)cpl_array_get_size(colnames);
331 for (k = 0; k < ig; k++) {
332 colname = (char *)cpl_array_get_string(colnames,(cpl_size)k);
333 if (strcasecmp(colname,"RA") && strcasecmp(colname,"DEC"))
334 cpl_table_new_column(mstds,colname,
335 cpl_table_get_column_type(objtab,colname));
336 }
337 cpl_table_unselect_all(mstds);
338 cpl_array_delete(colnames);
339
340 /* Now go through and find the matches. NB: the columns in objtab
341 are created by casu_imcore which only produces integer, float and
342 double columns, so we don't need to worry about any other type
343 when copying the columns to the matched standards catalogue. */
344
345 colnames = cpl_table_get_column_names(objtab);
346 ig = (int)cpl_array_get_size(colnames);
347 for (k = 0; k < nstd; k++) {
348 jm = matches[k];
349 if (jm != -1) {
350 for (k2 = 0; k2 < ig; k2++) {
351 colname = (char *)cpl_array_get_string(colnames,
352 (cpl_size)k2);
353 if (!strcasecmp(colname,"RA") || !strcasecmp(colname,"DEC"))
354 continue;
355 null = 0;
356 switch (cpl_table_get_column_type(objtab,colname)) {
357 case CPL_TYPE_INT:
358 cpl_table_set_int(mstds,colname,(cpl_size)k,
359 cpl_table_get_int(objtab,colname,
360 (cpl_size)jm,&null));
361 break;
362 case CPL_TYPE_FLOAT:
363 cpl_table_set_float(mstds,colname,(cpl_size)k,
364 cpl_table_get_float(objtab,colname,
365 (cpl_size)jm,&null));
366 break;
367 case CPL_TYPE_DOUBLE:
368 cpl_table_set_double(mstds,colname,(cpl_size)k,
369 cpl_table_get_double(objtab,colname,
370 (cpl_size)jm,&null));
371 break;
372 case CPL_TYPE_STRING:
373 cpl_table_set_string(mstds,colname,(cpl_size)k,
374 cpl_table_get_string(objtab,colname,
375 (cpl_size)jm));
376 break;
377 default:
378 cpl_msg_info(fctid, "Ignoring column %s of type %d", colname, cpl_table_get_column_type(objtab,colname));
379 break;
380 }
381 }
382 cpl_table_select_row(mstds,(cpl_size)k);
383 }
384 }
385 cpl_array_delete(colnames);
386
387 /* Now extract the selected rows into the output table */
388
389 *outtab = cpl_table_extract_selected(mstds);
390 cpl_table_delete(mstds);
391
392 /* Tidy up */
393
394 freespace(matches);
395 freespace(xoffs);
396 freespace(yoffs);
397 GOOD_STATUS
398}
399
400
401/*---------------------------------------------------------------------------*/
421/*---------------------------------------------------------------------------*/
422
423static cpl_table *casu_mkmstd_table(cpl_table *objtab, cpl_table *stdstab) {
424 cpl_table *mstds;
425 char *colname;
426 cpl_array *colnames;
427 cpl_size ncol,i;
428
429 /* Copy the input standards table */
430
431 mstds = cpl_table_duplicate(stdstab);
432
433 /* Loop throught the object table columns and copy all of them over,
434 except for the RA and DEC tables */
435
436 colnames = cpl_table_get_column_names((const cpl_table *)objtab);
437 ncol = cpl_array_get_size(colnames);
438 for (i = 0; i < ncol; i++) {
439 colname = (char *)cpl_array_get_string(colnames,i);
440 if (strcmp(colname,"RA") && strcmp(colname,"DEC"))
441 cpl_table_new_column(mstds,colname,
442 cpl_table_get_column_type(objtab,colname));
443 }
444 cpl_array_delete(colnames);
445 cpl_table_unselect_all(mstds);
446
447 /* Get out of here */
448
449 return(mstds);
450}
451