UVES Pipeline Reference Manual  5.4.0
uves_cd_align_impl.c
1 /* *
2  * This file is part of the ESO UVES Pipeline *
3  * Copyright (C) 2004,2005 European Southern Observatory *
4  * *
5  * This library is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA *
18  */
19 
20 /*
21  * $Author: amodigli $
22  * $Date: 2013-08-08 13:36:46 $
23  * $Revision: 1.19 $
24  * $Name: not supported by cvs2svn $
25  * $Log: not supported by cvs2svn $
26  * Revision 1.18 2013/07/01 15:36:08 amodigli
27  * Rename DEBUG to debug_mode to remove compiler error on some platforms (that name is reserved to special compiler options)
28  *
29  * Revision 1.17 2012/03/02 16:23:45 amodigli
30  * fixed compiler warnings related to CPL6 upgrade
31  *
32  * Revision 1.16 2011/12/08 13:59:05 amodigli
33  * Fox warnings with CPL6
34  *
35  * Revision 1.15 2010/09/24 09:32:02 amodigli
36  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
37  *
38  * Revision 1.13 2008/02/15 12:43:49 amodigli
39  * allow lower/upper chip for parameter process_chip
40  *
41  * Revision 1.12 2007/10/05 16:01:44 amodigli
42  * using proces_chip parameter to process or not a given RED chip
43  *
44  * Revision 1.11 2007/06/22 09:28:24 jmlarsen
45  * Changed interface of uves_save_image
46  *
47  * Revision 1.10 2007/06/11 13:28:26 jmlarsen
48  * Changed recipe contact address to cpl at eso.org
49  *
50  * Revision 1.9 2007/06/08 13:06:16 jmlarsen
51  * Send bug reports to Andrea
52  *
53  * Revision 1.8 2007/06/06 08:17:33 amodigli
54  * replace tab with 4 spaces
55  *
56  * Revision 1.7 2007/05/22 11:29:53 jmlarsen
57  * Changed text
58  *
59  * Revision 1.6 2007/05/14 08:09:48 amodigli
60  * updated input frames and tag description in recipe man page
61  *
62  * Revision 1.5 2007/04/24 12:50:29 jmlarsen
63  * Replaced cpl_propertylist -> uves_propertylist which is much faster
64  *
65  * Revision 1.4 2007/03/05 10:14:56 jmlarsen
66  * Support slope parameter in 1d fitting
67  *
68  * Revision 1.3 2007/02/16 10:36:15 jmlarsen
69  * Renamed variable y0->y_0
70  *
71  * Revision 1.2 2007/02/09 13:36:32 jmlarsen
72  * Use defines for recipe id
73  *
74  * Revision 1.1 2007/02/08 11:38:37 jmlarsen
75  * Added cd_align recipe
76  *
77  * Revision 1.31 2007/01/10 12:37:39 jmlarsen
78  * Removed obsolete comments
79  *
80  */
81 
82 #ifdef HAVE_CONFIG_H
83 # include <config.h>
84 #endif
85 
86 /*----------------------------------------------------------------------------*/
93 /*----------------------------------------------------------------------------*/
94 
95 /*-----------------------------------------------------------------------------
96  Includes
97  -----------------------------------------------------------------------------*/
98 #include <uves_cd_align_impl.h>
99 
100 #include <uves.h>
101 #include <uves_plot.h>
102 #include <uves_parameters.h>
103 #include <uves_dfs.h>
104 #include <uves_pfits.h>
105 #include <uves_qclog.h>
106 #include <uves_recipe.h>
107 #include <uves_utils_cpl.h>
108 #include <uves_utils_wrappers.h>
109 #include <uves_error.h>
110 #include <uves_msg.h>
111 
112 #include <cpl.h>
113 
114 /*-----------------------------------------------------------------------------
115  Functions prototypes
116  -----------------------------------------------------------------------------*/
117 
118 static int
119 uves_cal_cd_align_define_parameters(cpl_parameterlist *parameters);
120 
121 /*-----------------------------------------------------------------------------
122  Recipe standard code
123  -----------------------------------------------------------------------------*/
124 #define cpl_plugin_get_info uves_cal_cd_align_get_info
125 UVES_RECIPE_DEFINE(
126  UVES_CD_ALIGN_ID, UVES_CD_ALIGN_DOM, uves_cal_cd_align_define_parameters,
127  "Jonas M. Larsen", "cpl@eso.org",
128  "Measures the reproducability of the cross disperser positioning",
129  "Given two input frames (CD_ALIGN_xxx where xxx = BLUE or RED) which contain only\n"
130  "one echelle order, this recipe measures the shift in the cross-dispersion \n"
131  "direction of that order. For RED input frames, only the lower chip is processed.\n"
132  "\n"
133  "The recipe produces a CD_ALIGN_TABLE_xxxx (with xxxx = BLUE or REDL) with columns\n"
134  "X: Column number\n"
135  "YCENi: Centroid from Gaussian fit (for i = 1,2)\n"
136  "SIGMAi: Stdev from Gaussian fit\n"
137  "BACKi: Constant background from Gaussian fit\n"
138  "NORMi: Normalization constant from Gaussian fit\n"
139  "YDIFF: Difference YCEN2 - YCEN1 of centroid positions\n"
140  "\n"
141  "and the QC-parameters ESO.QC.YDIFF(AVG|MED|RMS), which are the average,\n"
142  "median and root-mean-square of the y-shift, respectively.\n");
143 
144 /*-----------------------------------------------------------------------------
145  Functions code
146  -----------------------------------------------------------------------------*/
148 static int
149 uves_cal_cd_align_define_parameters(cpl_parameterlist *parameters)
150 {
151  const char *subcontext = NULL;
152  const char *recipe_id = make_str(UVES_CD_ALIGN_ID);
153 
154  /*****************
155  * General *
156  *****************/
157  if (uves_define_global_parameters(parameters) != CPL_ERROR_NONE)
158  {
159  return -1;
160  }
161 
162  /* stepsize */
163  uves_par_new_range("steps",
164  CPL_TYPE_INT,
165  "Step size in pixels",
166  100, 1, INT_MAX);
167 
168  /* xborder */
169  uves_par_new_range("xborder",
170  CPL_TYPE_INT,
171  "Exclude a border region of this size (pixels)",
172  200, 0, INT_MAX);
173 
174  /* window */
175  uves_par_new_range("window",
176  CPL_TYPE_INT,
177  "The half window height used for Gaussian fitting",
178  50, 1, INT_MAX);
179 
180  return (cpl_error_get_code() != CPL_ERROR_NONE);
181 }
182 
183 /*----------------------------------------------------------------------------*/
200 /*----------------------------------------------------------------------------*/
201 cpl_table *
202 uves_cd_align_process(const cpl_image *im1,
203  const cpl_image *im2,
204  const uves_propertylist *rotated_header1,
205  const uves_propertylist *rotated_header2,
206  int steps,
207  int xborder,
208  int window,
209  bool debug_mode,
210  enum uves_chip chip)
211 {
212  cpl_table *result = NULL;
213  int row = 0; /* number of table rows used */
214  const cpl_image *images[2];
215  cpl_image *rows = NULL;
216  cpl_size max_row[2]; /* image row with max flux */
217  int nx, ny, x;
218  cpl_size num_fits, fit_succeeded;
219 
220  images[0] = im1;
221  images[1] = im2;
222  nx = cpl_image_get_size_x(images[0]);
223  ny = cpl_image_get_size_y(images[0]);
224 
225  if (debug_mode) check( uves_save_image_local("CD alignment frame", "cd_align1",
226  images[0], chip, -1, -1,
227  rotated_header1, true),
228  "Error saving 1st CD aligment frame");
229 
230  if (debug_mode) check( uves_save_image_local("CD alignment frame", "cd_align2",
231  images[1], chip, -1, -1,
232  rotated_header2, true),
233  "Error saving 2nd CD aligment frame");
234 
235  assure( cpl_image_get_size_x(images[0]) == cpl_image_get_size_x(images[1]) &&
236  cpl_image_get_size_y(images[0]) == cpl_image_get_size_y(images[1]),
237  CPL_ERROR_INCOMPATIBLE_INPUT,
238  "Images sizes: %" CPL_SIZE_FORMAT "x%" CPL_SIZE_FORMAT " and %" CPL_SIZE_FORMAT "x%" CPL_SIZE_FORMAT "",
239  cpl_image_get_size_x(images[0]),
240  cpl_image_get_size_y(images[0]),
241  cpl_image_get_size_x(images[1]),
242  cpl_image_get_size_y(images[1]) );
243 
244 
245  result = cpl_table_new(nx); row = 0;
246  cpl_table_new_column(result, "X" , CPL_TYPE_INT);
247  cpl_table_new_column(result, "YCEN1", CPL_TYPE_DOUBLE);
248  cpl_table_new_column(result, "YCEN2", CPL_TYPE_DOUBLE);
249  cpl_table_new_column(result, "SIGMA1", CPL_TYPE_DOUBLE);
250  cpl_table_new_column(result, "SIGMA2", CPL_TYPE_DOUBLE);
251  cpl_table_new_column(result, "BACK1", CPL_TYPE_DOUBLE);
252  cpl_table_new_column(result, "BACK2", CPL_TYPE_DOUBLE);
253  cpl_table_new_column(result, "NORM1", CPL_TYPE_DOUBLE);
254  cpl_table_new_column(result, "NORM2", CPL_TYPE_DOUBLE);
255  assure_mem( result );
256 
257  /* Find row of max accumulated flux (i.e. position of the order) */
258  {
259  int im;
260  for (im = 0; im < 2; im++)
261  {
262  int direction = 1; /* To get image of single column */
263  cpl_size max_col;
264 
265  uves_free_image(&rows);
266  rows = cpl_image_collapse_create(images[im], direction);
267 
268  cpl_image_get_maxpos(rows, &max_col, &(max_row[im]));
269  uves_msg("Row of max flux (%" CPL_SIZE_FORMAT ". image) = %" CPL_SIZE_FORMAT "", (cpl_size)im+1, max_row[im]);
270 
271  assure( max_col == 1, CPL_ERROR_ILLEGAL_OUTPUT,
272  "Something went wrong, max_col in collapsed image is = %" CPL_SIZE_FORMAT "", max_col);
273  }
274  }
275 
276 
277  num_fits = 0; /* Number of measure points */
278  fit_succeeded = 0; /* Number of successful Gauss fits */
279  for (x = 1 + xborder; x <= nx - xborder; x += steps)
280  {
281  int im;
282  for (im = 0; im < 2; im++)
283  {
284  bool horizontal = false;
285  bool fix_background = false;
286  bool fit_background = false;
287  int number_of_parameters = 4;
288  double y_0, sigma, norm, background;
289  int ylow = uves_max_int(1, uves_min_int(ny, max_row[im] - window));
290  int yhigh = uves_max_int(1, uves_min_int(ny, max_row[im] + window));
291 
292  uves_fit_1d_image(images[im],
293  NULL, NULL, /* errors, bpm */
294  horizontal, fix_background, fit_background,
295  ylow, yhigh, x,
296  &y_0, &sigma, &norm, &background, NULL, /* slope */
297  NULL, NULL, /* mse, red_chisq */
298  NULL, /* Covariance */
300  number_of_parameters);
301 
302  num_fits += 1;
303  if (cpl_error_get_code() == CPL_ERROR_CONTINUE)
304  {
306 
307  uves_msg_warning("Fitting window (%" CPL_SIZE_FORMAT ", %" CPL_SIZE_FORMAT ") - (%" CPL_SIZE_FORMAT ", %" CPL_SIZE_FORMAT ") failed",
308  (cpl_size)x, (cpl_size)ylow,
309  (cpl_size)x, (cpl_size)yhigh);
310  }
311  else
312  {
313  fit_succeeded += 1;
314 
315  assure( cpl_error_get_code() == CPL_ERROR_NONE,
316  cpl_error_get_code(),
317  "Gaussian fitting failed");
318 
319  cpl_table_set_int (result, "X" , row, x);
320  cpl_table_set_double(result, (im == 0) ? "YCEN1" : "YCEN2", row, y_0);
321  cpl_table_set_double(result, (im == 0) ? "SIGMA1": "SIGMA2", row, sigma);
322  cpl_table_set_double(result, (im == 0) ? "BACK1" : "BACK2", row, norm);
323  cpl_table_set_double(result, (im == 0) ? "NORM1" : "NORM2", row, background);
324  }
325  }
326  row++;
327  }
328 
329  cpl_table_set_size(result, row);
330 
331  uves_msg_low("Was able to fit %" CPL_SIZE_FORMAT " of %" CPL_SIZE_FORMAT " columns", fit_succeeded, num_fits);
332 
333  check(( cpl_table_duplicate_column(result, "YDIFF", result, "YCEN2"),
334  cpl_table_subtract_columns(result, "YDIFF", "YCEN1")),
335  "Error calculating residuals of fit");
336 
337  {
338  cpl_size num_valid = cpl_table_get_nrow(result) - cpl_table_count_invalid(result, "YDIFF");
339 
340  assure( num_valid >= 1, CPL_ERROR_ILLEGAL_OUTPUT,
341  "Only %" CPL_SIZE_FORMAT " valid YDIFF value(s), 1 or more needed",
342  num_valid);
343  }
344 
345 
346  cleanup:
347  uves_free_image(&rows);
348  return result;
349 }
350 
351 /*----------------------------------------------------------------------------*/
360 /*----------------------------------------------------------------------------*/
361 
362 static cpl_table*
363 cd_align_qclog(const cpl_table *cdalign,
364  const uves_propertylist *raw_header,
365  enum uves_chip chip)
366 {
367  cpl_table *qclog = NULL;
368  double mean, sigma, median;
369 
370  check( qclog = uves_qclog_init(raw_header, chip),
371  "Error during QC initialization");
372 
373  mean = cpl_table_get_column_mean (cdalign, "YDIFF");
374  sigma = cpl_table_get_column_stdev (cdalign, "YDIFF");
375  median = cpl_table_get_column_median(cdalign, "YDIFF");
376 
377  uves_qclog_add_string(qclog,
378  "QC TEST1 ID",
379  "Test-of-CD-Alignment",
380  "Name of QC test",
381  "%s");
382 
383  uves_qclog_add_double(qclog,
384  "QC YDIFFAVG",
385  mean,
386  "Average Y difference",
387  "%8.4f");
388 
389  uves_qclog_add_double(qclog,
390  "QC YDIFFMED",
391  median,
392  "Median Y difference",
393  "%8.4f");
394 
395  uves_qclog_add_double(qclog,
396  "QC YDIFFRMS",
397  sigma,
398  "RMS Y difference",
399  "%8.4f");
400 
401 
402  uves_msg("Average shift = %.4f +- %.4f pixels",
403  mean, sigma);
404 
405 
406  cleanup:
407  return qclog;
408 }
409 
410 /*----------------------------------------------------------------------------*/
418 /*----------------------------------------------------------------------------*/
419 static double
420 avg_flux(const cpl_image *im)
421 {
422  double result = 0;
423  cpl_image *median_filt = NULL;
424  bool extrapolate_border = true;
425 
426  /* Report total flux after bias subtraction.
427  Bias is estimated as the median value
428 
429  Note that: total flux - nx*ny*median =
430  nx*ny(mean - median)
431 
432  so just report (mean - median)
433  */
434 
435  /* First apply a small window (3x3) median filter to
436  get a bit robust avg, but without destroying the echelle order signal
437  */
438 
439  median_filt = cpl_image_duplicate(im);
440  assure_mem( median_filt );
441 
442  uves_filter_image_median(&median_filt, 1, 1,
443  extrapolate_border);
444 
445  result =
446  cpl_image_get_mean (median_filt) -
447  cpl_image_get_median(median_filt);
448 
449  cleanup:
450  uves_free_image(&median_filt);
451  return result;
452 }
453 
454 /*----------------------------------------------------------------------------*/
462 /*----------------------------------------------------------------------------*/
463 static void
464 uves_cal_cd_align_exe(cpl_frameset *frames, const cpl_parameterlist *parameters,
465  const char *starttime)
466 {
467  /* Input */
468  cpl_image *raw_images[2][2] = {{NULL, NULL}, {NULL, NULL}};
469 
470  /* This 2x2 array of images contain:
471 
472  raw_images[0][0]: First frame, REDL or BLUE chip
473  raw_images[0][1]: First frame, REDU or NULL
474  raw_images[1][0]: Second frame, REDL or BLUE chip
475  raw_images[1][1]: Second frame, REDU or NULL
476 
477  etc. for the following arrays
478  */
479 
480  uves_propertylist *raw_headers[2][2] = {{NULL, NULL}, {NULL, NULL}};
481  uves_propertylist *rotated_headers[2][2] = {{NULL, NULL}, {NULL, NULL}};
482 
483  cpl_table* qclog[2] = {NULL, NULL};
484 
485  /* Output */
486  uves_propertylist *product_header = NULL;
487  cpl_table *cd_align = NULL;
488 
489  /* Parameters */
490  int steps, xborder, window;
491  bool debug_mode;
492 
493  /* Local variables */
494  const char *product_filename = NULL;
495  bool blue;
496  enum uves_chip chip;
497  const char *raw_filename[2];
498  int raw_index;
499 
500  const char* PROCESS_CHIP=NULL;
501 
502  check( uves_get_parameter(parameters, NULL, "uves", "debug",
503  CPL_TYPE_BOOL, &debug_mode), "Could not read parameter");
504 
505  check( uves_get_parameter(parameters, NULL, "uves", "process_chip", CPL_TYPE_STRING, &PROCESS_CHIP),
506  "Could not read parameter");
507  uves_string_toupper((char*)PROCESS_CHIP);
508 
509  check( uves_get_parameter(parameters, NULL, make_str(UVES_CD_ALIGN_ID), "steps",
510  CPL_TYPE_INT , &steps), "Could not read parameter");
511  check( uves_get_parameter(parameters, NULL, make_str(UVES_CD_ALIGN_ID), "xborder",
512  CPL_TYPE_INT , &xborder), "Could not read parameter");
513  check( uves_get_parameter(parameters, NULL, make_str(UVES_CD_ALIGN_ID), "window",
514  CPL_TYPE_INT , &window), "Could not read parameter");
515 
516 
517  check( uves_load_cd_align(frames,
518  &raw_filename[0],
519  &raw_filename[1],
520  raw_images[0],
521  raw_images[1],
522  raw_headers[0],
523  raw_headers[1],
524  rotated_headers[0],
525  rotated_headers[1],
526  &blue),
527  "Error loading raw frame");
528 
529  uves_msg("Using %s", raw_filename[0]);
530  uves_msg("Using %s", raw_filename[1]);
531 
532 
533  if (blue)
534  {
535  chip = UVES_CHIP_BLUE;
536  }
537  else
538  {
539  if (debug_mode)
540  {
541  int raw_index_l = uves_chip_get_index(UVES_CHIP_REDL);
542  int raw_index_u = uves_chip_get_index(UVES_CHIP_REDU);
543 
544  uves_msg("1. REDL average flux per pixel = %f ADU", avg_flux(raw_images[0][raw_index_l]));
545  uves_msg("2. REDL average flux per pixel = %f ADU", avg_flux(raw_images[1][raw_index_l]));
546 
547  uves_msg("1. REDU average flux per pixel = %f ADU", avg_flux(raw_images[0][raw_index_u]));
548  uves_msg("2. REDU average flux per pixel = %f ADU", avg_flux(raw_images[1][raw_index_u]));
549  }
550 
551  chip = UVES_CHIP_REDL; /* Process only lower red chip */
552  }
553 
554  raw_index = uves_chip_get_index(chip);
555 
556  uves_msg("Processing %s chip",
558 
559  check( cd_align = uves_cd_align_process(raw_images[0][raw_index],
560  raw_images[1][raw_index],
561  rotated_headers[0][raw_index],
562  rotated_headers[1][raw_index],
563  steps,
564  xborder,
565  window,
566  debug_mode,
567  chip),
568  "Error during processing");
569 
570  check( qclog[0] = cd_align_qclog(cd_align,
571  raw_headers[0][raw_index], /* of first frame */
572  chip),
573  "Could not compute QC");
574 
575  product_header = uves_propertylist_new();
576  product_filename = uves_cd_align_filename(chip);
577  check( uves_frameset_insert(frames,
578  cd_align,
579  CPL_FRAME_GROUP_PRODUCT,
580  CPL_FRAME_TYPE_TABLE,
581  CPL_FRAME_LEVEL_FINAL,
582  product_filename,
583  UVES_CD_ALIGN_TABLE(blue),
584  raw_headers[0][raw_index],
585  product_header,
586  NULL, /* table header */
587  parameters,
588  make_str(UVES_CD_ALIGN_ID),
589  PACKAGE "/" PACKAGE_VERSION,
590  qclog, /* No QC */
591  starttime, true,
592  0),
593  "Could not add CD align table %s to frameset", product_filename);
594 
595  uves_msg("CD align table %s (%s) added to frameset",
596  product_filename, UVES_CD_ALIGN_TABLE(blue));
597 
598  cleanup:
599  uves_free_image(&raw_images[0][0]);
600  uves_free_image(&raw_images[0][1]);
601  uves_free_image(&raw_images[1][0]);
602  uves_free_image(&raw_images[1][1]);
603  uves_free_propertylist(&raw_headers[0][0]);
604  uves_free_propertylist(&raw_headers[0][1]);
605  uves_free_propertylist(&raw_headers[1][0]);
606  uves_free_propertylist(&raw_headers[1][1]);
607  uves_free_propertylist(&rotated_headers[0][0]);
608  uves_free_propertylist(&rotated_headers[0][1]);
609  uves_free_propertylist(&rotated_headers[1][0]);
610  uves_free_propertylist(&rotated_headers[1][1]);
611 
612  uves_free_table(&qclog[0]);
613  uves_free_string_const(&product_filename);
614  uves_free_table(&cd_align);
615  uves_free_propertylist(&product_header);
616 
617  return;
618 }
619 
620