MUSE Pipeline Reference Manual  2.1.1
muse_wavecal_z.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2005-2015 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 /* This file was automatically generated */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 /*----------------------------------------------------------------------------*
29  * Includes *
30  *----------------------------------------------------------------------------*/
31 #include <string.h> /* strcmp(), strstr() */
32 #include <strings.h> /* strcasecmp() */
33 #include <cpl.h>
34 
35 #include "muse_wavecal_z.h" /* in turn includes muse.h */
36 
37 /*----------------------------------------------------------------------------*/
76 /*----------------------------------------------------------------------------*/
79 /*----------------------------------------------------------------------------*
80  * Static variables *
81  *----------------------------------------------------------------------------*/
82 static const char *muse_wavecal_help =
83  "This recipe detects arc emission lines and fits a wavelength solution to each slice of the instrument. The wavelength calibration table contains polynomials defining the wavelength solution of the slices on the CCD. Processing trims the raw data and records the overscan statistics, subtracts the bias (taking account of the overscan, if --overscan is not \"none\") and converts them from adu to count. Optionally, the dark can be subtracted and the data can be divided by the flat-field, but this is not recommended. The data is then combined using input parameters, into separate images for each lamp. To compute the wavelength solution, arc lines are detected at the center of each slice (using threshold detection on a S/N image) and subsequently assigned wavelengths, using pattern matching to identify lines from the input line catalog. Each line is then traced to the edges of the slice, using Gaussian centering in each CCD column. The Gaussians not only yield center, but also centering error, and line properties (e.g. FWHM). Deviant fits are detected using polynomial fits to each arc line (using the xorder parameter) and rejected. These analysis and measuring steps are carried out separately on images exposed by the different arc lamps, reducing the amount of blending, that can otherwise influence line identification and Gaussian centering. The final two-dimensional fit uses all positions (of all lamps), their wavelengths, and the given polynomial orders to compute the final wavelength solution for each slice, iteratively rejecting outliers. This final fit can be either unweighted (fitweighting=\"uniform\", for fastest processing) or weighted (other values of fitweighting, for higher accuracy).";
84 
85 static const char *muse_wavecal_help_esorex =
86  "\n\nInput frames for raw frame tag \"ARC\":\n"
87  "\n Frame tag Type Req #Fr Description"
88  "\n -------------------- ---- --- --- ------------"
89  "\n ARC raw Y Raw arc lamp exposures"
90  "\n MASTER_BIAS calib Y 1 Master bias"
91  "\n MASTER_DARK calib . 1 Master dark"
92  "\n MASTER_FLAT calib . 1 Master flat"
93  "\n TRACE_TABLE calib Y 1 Trace table"
94  "\n LINE_CATALOG calib Y 1 Arc line catalog"
95  "\n BADPIX_TABLE calib . Known bad pixels"
96  "\n\nProduct frames for raw frame tag \"ARC\":\n"
97  "\n Frame tag Level Description"
98  "\n -------------------- -------- ------------"
99  "\n WAVECAL_TABLE final Wavelength calibration table"
100  "\n WAVECAL_RESIDUALS final Fit residuals of all arc lines (if --residuals=true)"
101  "\n ARC_RED_LAMP final Reduced ARC image, per lamp";
102 
103 /*----------------------------------------------------------------------------*/
111 /*----------------------------------------------------------------------------*/
112 static cpl_recipeconfig *
113 muse_wavecal_new_recipeconfig(void)
114 {
115  cpl_recipeconfig *recipeconfig = cpl_recipeconfig_new();
116 
117  cpl_recipeconfig_set_tag(recipeconfig, "ARC", 1, -1);
118  cpl_recipeconfig_set_input(recipeconfig, "ARC", "MASTER_BIAS", 1, 1);
119  cpl_recipeconfig_set_input(recipeconfig, "ARC", "MASTER_DARK", -1, 1);
120  cpl_recipeconfig_set_input(recipeconfig, "ARC", "MASTER_FLAT", -1, 1);
121  cpl_recipeconfig_set_input(recipeconfig, "ARC", "TRACE_TABLE", 1, 1);
122  cpl_recipeconfig_set_input(recipeconfig, "ARC", "LINE_CATALOG", 1, 1);
123  cpl_recipeconfig_set_input(recipeconfig, "ARC", "BADPIX_TABLE", -1, -1);
124  cpl_recipeconfig_set_output(recipeconfig, "ARC", "WAVECAL_TABLE");
125  cpl_recipeconfig_set_output(recipeconfig, "ARC", "WAVECAL_RESIDUALS");
126  cpl_recipeconfig_set_output(recipeconfig, "ARC", "ARC_RED_LAMP");
127 
128  return recipeconfig;
129 } /* muse_wavecal_new_recipeconfig() */
130 
131 /*----------------------------------------------------------------------------*/
141 /*----------------------------------------------------------------------------*/
142 static cpl_error_code
143 muse_wavecal_prepare_header(const char *aFrametag, cpl_propertylist *aHeader)
144 {
145  cpl_ensure_code(aFrametag, CPL_ERROR_NULL_INPUT);
146  cpl_ensure_code(aHeader, CPL_ERROR_NULL_INPUT);
147  if (!strcmp(aFrametag, "WAVECAL_TABLE")) {
148  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES NDET",
149  CPL_TYPE_INT,
150  "Number of detected arc lines in slice j");
151  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES NID",
152  CPL_TYPE_INT,
153  "Number of identified arc lines in slice j");
154  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES PEAK MEAN",
155  CPL_TYPE_FLOAT,
156  "[count] Mean peak count level above background of detected arc lines in slice j");
157  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES PEAK STDEV",
158  CPL_TYPE_FLOAT,
159  "[count] Standard deviation of peak count level above background of detected arc lines in slice j");
160  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES PEAK MIN",
161  CPL_TYPE_FLOAT,
162  "[count] Peak count level above background of the faintest line in slice j");
163  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES PEAK MAX",
164  CPL_TYPE_FLOAT,
165  "[count] Peak count level above background of the brightest line in slice j");
166  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LAMP[0-9] LINES PEAK MEAN",
167  CPL_TYPE_FLOAT,
168  "[count] Mean peak count level of lines of lamp l above background of detected arc lines in slice j.");
169  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LAMP[0-9] LINES PEAK STDEV",
170  CPL_TYPE_FLOAT,
171  "[count] Standard deviation of peak count level of lines of lamp l above background of detected arc lines in slice j.");
172  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LAMP[0-9] LINES PEAK MAX",
173  CPL_TYPE_FLOAT,
174  "[count] Peak count level above background of the brightest line of lamp l in slice j.");
175  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES FWHM MEAN",
176  CPL_TYPE_FLOAT,
177  "Mean FWHM of detected arc lines in slice j");
178  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES FWHM STDEV",
179  CPL_TYPE_FLOAT,
180  "Standard deviation of FWHM of detected arc lines in slice j");
181  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES FWHM MIN",
182  CPL_TYPE_FLOAT,
183  "Minimum FWHM of detected arc lines in slice j");
184  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES FWHM MAX",
185  CPL_TYPE_FLOAT,
186  "Maximum FWHM of detected arc lines in slice j");
187  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ RESOL",
188  CPL_TYPE_FLOAT,
189  "Mean spectral resolution R determined in slice j");
190  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ FIT NLINES",
191  CPL_TYPE_INT,
192  "Number of arc lines used in calibration solution fit in slice j");
193  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ FIT RMS",
194  CPL_TYPE_FLOAT,
195  "[Angstrom] RMS of the wavelength calibration fit in slice j");
196  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ DWLEN BOTTOM",
197  CPL_TYPE_FLOAT,
198  "[Angstrom] Difference in wavelength computed for the bottom left and bottom right corners of the slice on the CCD");
199  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ DWLEN TOP",
200  CPL_TYPE_FLOAT,
201  "[Angstrom] Difference in wavelength computed for the top left and top right corners of the slice on the CCD");
202  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ WLPOS",
203  CPL_TYPE_FLOAT,
204  "[pix] Position of wavelength given in WLEN in slice j");
205  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ WLEN",
206  CPL_TYPE_FLOAT,
207  "[Angstrom] Wavelength associated to WLPOS in slice j");
208  } else if (!strcmp(aFrametag, "WAVECAL_RESIDUALS")) {
209  } else if (!strcmp(aFrametag, "ARC_RED_LAMP")) {
210  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL INPUT[0-9]+ NSATURATED",
211  CPL_TYPE_INT,
212  "Number of saturated pixels in raw arc i in input list");
213  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL NSATURATED",
214  CPL_TYPE_INT,
215  "Number of saturated pixels in output data");
216  } else {
217  cpl_msg_warning(__func__, "Frame tag %s is not defined", aFrametag);
218  return CPL_ERROR_ILLEGAL_INPUT;
219  }
220  return CPL_ERROR_NONE;
221 } /* muse_wavecal_prepare_header() */
222 
223 /*----------------------------------------------------------------------------*/
232 /*----------------------------------------------------------------------------*/
233 static cpl_frame_level
234 muse_wavecal_get_frame_level(const char *aFrametag)
235 {
236  if (!aFrametag) {
237  return CPL_FRAME_LEVEL_NONE;
238  }
239  if (!strcmp(aFrametag, "WAVECAL_TABLE")) {
240  return CPL_FRAME_LEVEL_FINAL;
241  }
242  if (!strcmp(aFrametag, "WAVECAL_RESIDUALS")) {
243  return CPL_FRAME_LEVEL_FINAL;
244  }
245  if (!strcmp(aFrametag, "ARC_RED_LAMP")) {
246  return CPL_FRAME_LEVEL_FINAL;
247  }
248  return CPL_FRAME_LEVEL_NONE;
249 } /* muse_wavecal_get_frame_level() */
250 
251 /*----------------------------------------------------------------------------*/
260 /*----------------------------------------------------------------------------*/
261 static muse_frame_mode
262 muse_wavecal_get_frame_mode(const char *aFrametag)
263 {
264  if (!aFrametag) {
265  return MUSE_FRAME_MODE_ALL;
266  }
267  if (!strcmp(aFrametag, "WAVECAL_TABLE")) {
268  return MUSE_FRAME_MODE_MASTER;
269  }
270  if (!strcmp(aFrametag, "WAVECAL_RESIDUALS")) {
271  return MUSE_FRAME_MODE_MASTER;
272  }
273  if (!strcmp(aFrametag, "ARC_RED_LAMP")) {
274  return MUSE_FRAME_MODE_SUBSET;
275  }
276  return MUSE_FRAME_MODE_ALL;
277 } /* muse_wavecal_get_frame_mode() */
278 
279 /*----------------------------------------------------------------------------*/
289 /*----------------------------------------------------------------------------*/
290 static int
291 muse_wavecal_create(cpl_plugin *aPlugin)
292 {
293  /* Check that the plugin is part of a valid recipe */
294  cpl_recipe *recipe;
295  if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
296  recipe = (cpl_recipe *)aPlugin;
297  } else {
298  return -1;
299  }
300 
301  /* register the extended processing information (new FITS header creation, *
302  * getting of the frame level for a certain tag) */
304  muse_wavecal_new_recipeconfig(),
305  muse_wavecal_prepare_header,
306  muse_wavecal_get_frame_level,
307  muse_wavecal_get_frame_mode);
308 
309  /* XXX initialize timing in messages *
310  * since at least esorex is too stupid to turn it on, we have to do it */
312  cpl_msg_set_time_on();
313  }
314 
315  /* Create the parameter list in the cpl_recipe object */
316  recipe->parameters = cpl_parameterlist_new();
317  /* Fill the parameters list */
318  cpl_parameter *p;
319 
320  /* --nifu: IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in parallel. */
321  p = cpl_parameter_new_range("muse.muse_wavecal.nifu",
322  CPL_TYPE_INT,
323  "IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in parallel.",
324  "muse.muse_wavecal",
325  (int)0,
326  (int)-1,
327  (int)24);
328  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "nifu");
329  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "nifu");
330 
331  cpl_parameterlist_append(recipe->parameters, p);
332 
333  /* --overscan: If this is "none", stop when detecting discrepant overscan levels (see ovscsigma), for "offset" it assumes that the mean overscan level represents the real offset in the bias levels of the exposures involved, and adjusts the data accordingly; for "vpoly", a polynomial is fit to the vertical overscan and subtracted from the whole quadrant. */
334  p = cpl_parameter_new_value("muse.muse_wavecal.overscan",
335  CPL_TYPE_STRING,
336  "If this is \"none\", stop when detecting discrepant overscan levels (see ovscsigma), for \"offset\" it assumes that the mean overscan level represents the real offset in the bias levels of the exposures involved, and adjusts the data accordingly; for \"vpoly\", a polynomial is fit to the vertical overscan and subtracted from the whole quadrant.",
337  "muse.muse_wavecal",
338  (const char *)"vpoly");
339  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "overscan");
340  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "overscan");
341 
342  cpl_parameterlist_append(recipe->parameters, p);
343 
344  /* --ovscreject: This influences how values are rejected when computing overscan statistics. Either no rejection at all ("none"), rejection using the DCR algorithm ("dcr"), or rejection using an iterative constant fit ("fit"). */
345  p = cpl_parameter_new_value("muse.muse_wavecal.ovscreject",
346  CPL_TYPE_STRING,
347  "This influences how values are rejected when computing overscan statistics. Either no rejection at all (\"none\"), rejection using the DCR algorithm (\"dcr\"), or rejection using an iterative constant fit (\"fit\").",
348  "muse.muse_wavecal",
349  (const char *)"dcr");
350  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "ovscreject");
351  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ovscreject");
352 
353  cpl_parameterlist_append(recipe->parameters, p);
354 
355  /* --ovscsigma: If the deviation of mean overscan levels between a raw input image and the reference image is higher than |ovscsigma x stdev|, stop the processing. If overscan="vpoly", this is used as sigma rejection level for the iterative polynomial fit (the level comparison is then done afterwards with |100 x stdev| to guard against incompatible settings). Has no effect for overscan="offset". */
356  p = cpl_parameter_new_value("muse.muse_wavecal.ovscsigma",
357  CPL_TYPE_DOUBLE,
358  "If the deviation of mean overscan levels between a raw input image and the reference image is higher than |ovscsigma x stdev|, stop the processing. If overscan=\"vpoly\", this is used as sigma rejection level for the iterative polynomial fit (the level comparison is then done afterwards with |100 x stdev| to guard against incompatible settings). Has no effect for overscan=\"offset\".",
359  "muse.muse_wavecal",
360  (double)30.);
361  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "ovscsigma");
362  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ovscsigma");
363 
364  cpl_parameterlist_append(recipe->parameters, p);
365 
366  /* --ovscignore: The number of pixels of the overscan adjacent to the data section of the CCD that are ignored when computing statistics or fits. */
367  p = cpl_parameter_new_value("muse.muse_wavecal.ovscignore",
368  CPL_TYPE_INT,
369  "The number of pixels of the overscan adjacent to the data section of the CCD that are ignored when computing statistics or fits.",
370  "muse.muse_wavecal",
371  (int)3);
372  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "ovscignore");
373  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ovscignore");
374 
375  cpl_parameterlist_append(recipe->parameters, p);
376 
377  /* --combine: Type of lampwise image combination to use. */
378  p = cpl_parameter_new_enum("muse.muse_wavecal.combine",
379  CPL_TYPE_STRING,
380  "Type of lampwise image combination to use.",
381  "muse.muse_wavecal",
382  (const char *)"sigclip",
383  4,
384  (const char *)"average",
385  (const char *)"median",
386  (const char *)"minmax",
387  (const char *)"sigclip");
388  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "combine");
389  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "combine");
390 
391  cpl_parameterlist_append(recipe->parameters, p);
392 
393  /* --lampwise: Identify and measure the arc emission lines on images separately for each lamp setup. */
394  p = cpl_parameter_new_value("muse.muse_wavecal.lampwise",
395  CPL_TYPE_BOOL,
396  "Identify and measure the arc emission lines on images separately for each lamp setup.",
397  "muse.muse_wavecal",
398  (int)TRUE);
399  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lampwise");
400  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lampwise");
401  if (!getenv("MUSE_EXPERT_USER")) {
402  cpl_parameter_disable(p, CPL_PARAMETER_MODE_CLI);
403  }
404 
405  cpl_parameterlist_append(recipe->parameters, p);
406 
407  /* --sigma: Sigma level used to detect arc emission lines above the median background level in the S/N image of the central column of each slice */
408  p = cpl_parameter_new_value("muse.muse_wavecal.sigma",
409  CPL_TYPE_DOUBLE,
410  "Sigma level used to detect arc emission lines above the median background level in the S/N image of the central column of each slice",
411  "muse.muse_wavecal",
412  (double)1.0);
413  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "sigma");
414  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sigma");
415 
416  cpl_parameterlist_append(recipe->parameters, p);
417 
418  /* --dres: The allowed range of resolutions for pattern matching (of detected arc lines to line list) in fractions relative to the expected value */
419  p = cpl_parameter_new_value("muse.muse_wavecal.dres",
420  CPL_TYPE_DOUBLE,
421  "The allowed range of resolutions for pattern matching (of detected arc lines to line list) in fractions relative to the expected value",
422  "muse.muse_wavecal",
423  (double)0.05);
424  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "dres");
425  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dres");
426 
427  cpl_parameterlist_append(recipe->parameters, p);
428 
429  /* --tolerance: Tolerance for pattern matching (of detected arc lines to line list) */
430  p = cpl_parameter_new_value("muse.muse_wavecal.tolerance",
431  CPL_TYPE_DOUBLE,
432  "Tolerance for pattern matching (of detected arc lines to line list)",
433  "muse.muse_wavecal",
434  (double)0.1);
435  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "tolerance");
436  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "tolerance");
437 
438  cpl_parameterlist_append(recipe->parameters, p);
439 
440  /* --xorder: Order of the polynomial for the horizontal curvature within each slice */
441  p = cpl_parameter_new_value("muse.muse_wavecal.xorder",
442  CPL_TYPE_INT,
443  "Order of the polynomial for the horizontal curvature within each slice",
444  "muse.muse_wavecal",
445  (int)2);
446  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "xorder");
447  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "xorder");
448 
449  cpl_parameterlist_append(recipe->parameters, p);
450 
451  /* --yorder: Order of the polynomial used to fit the dispersion relation */
452  p = cpl_parameter_new_value("muse.muse_wavecal.yorder",
453  CPL_TYPE_INT,
454  "Order of the polynomial used to fit the dispersion relation",
455  "muse.muse_wavecal",
456  (int)6);
457  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "yorder");
458  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "yorder");
459 
460  cpl_parameterlist_append(recipe->parameters, p);
461 
462  /* --linesigma: Sigma level for iterative rejection of deviant fits for each arc line within each slice, a negative value means to use the default (2.5). */
463  p = cpl_parameter_new_value("muse.muse_wavecal.linesigma",
464  CPL_TYPE_DOUBLE,
465  "Sigma level for iterative rejection of deviant fits for each arc line within each slice, a negative value means to use the default (2.5).",
466  "muse.muse_wavecal",
467  (double)-1.0);
468  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "linesigma");
469  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "linesigma");
470 
471  cpl_parameterlist_append(recipe->parameters, p);
472 
473  /* --residuals: Create a table containing residuals of the fits to the data of all arc lines. This is useful to assess the quality of the wavelength solution in detail. */
474  p = cpl_parameter_new_value("muse.muse_wavecal.residuals",
475  CPL_TYPE_BOOL,
476  "Create a table containing residuals of the fits to the data of all arc lines. This is useful to assess the quality of the wavelength solution in detail.",
477  "muse.muse_wavecal",
478  (int)FALSE);
479  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "residuals");
480  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "residuals");
481 
482  cpl_parameterlist_append(recipe->parameters, p);
483 
484  /* --fitsigma: Sigma level for iterative rejection of deviant datapoints during the final polynomial wavelength solution within each slice, a negative value means to use the default (3.0). */
485  p = cpl_parameter_new_value("muse.muse_wavecal.fitsigma",
486  CPL_TYPE_DOUBLE,
487  "Sigma level for iterative rejection of deviant datapoints during the final polynomial wavelength solution within each slice, a negative value means to use the default (3.0).",
488  "muse.muse_wavecal",
489  (double)-1.0);
490  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "fitsigma");
491  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "fitsigma");
492 
493  cpl_parameterlist_append(recipe->parameters, p);
494 
495  /* --fitweighting: Type of weighting to use in the final polynomial wavelength solution fit, using centroiding error estimate and/or scatter of each single line as estimates of its accuracy. */
496  p = cpl_parameter_new_enum("muse.muse_wavecal.fitweighting",
497  CPL_TYPE_STRING,
498  "Type of weighting to use in the final polynomial wavelength solution fit, using centroiding error estimate and/or scatter of each single line as estimates of its accuracy.",
499  "muse.muse_wavecal",
500  (const char *)"cerrscatter",
501  4,
502  (const char *)"uniform",
503  (const char *)"cerr",
504  (const char *)"scatter",
505  (const char *)"cerrscatter");
506  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "fitweighting");
507  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "fitweighting");
508 
509  cpl_parameterlist_append(recipe->parameters, p);
510 
511  /* --resample: Resample the input arc images onto 2D images for a visual check using tracing and wavelength calibration solutions. Note that the image produced will show small wiggles even when the calibration was successful! */
512  p = cpl_parameter_new_value("muse.muse_wavecal.resample",
513  CPL_TYPE_BOOL,
514  "Resample the input arc images onto 2D images for a visual check using tracing and wavelength calibration solutions. Note that the image produced will show small wiggles even when the calibration was successful!",
515  "muse.muse_wavecal",
516  (int)FALSE);
517  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "resample");
518  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "resample");
519  if (!getenv("MUSE_EXPERT_USER")) {
520  cpl_parameter_disable(p, CPL_PARAMETER_MODE_CLI);
521  }
522 
523  cpl_parameterlist_append(recipe->parameters, p);
524 
525  /* --wavemap: Create a wavelength map of the input images */
526  p = cpl_parameter_new_value("muse.muse_wavecal.wavemap",
527  CPL_TYPE_BOOL,
528  "Create a wavelength map of the input images",
529  "muse.muse_wavecal",
530  (int)FALSE);
531  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "wavemap");
532  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wavemap");
533  if (!getenv("MUSE_EXPERT_USER")) {
534  cpl_parameter_disable(p, CPL_PARAMETER_MODE_CLI);
535  }
536 
537  cpl_parameterlist_append(recipe->parameters, p);
538 
539  /* --merge: Merge output products from different IFUs into a common file. */
540  p = cpl_parameter_new_value("muse.muse_wavecal.merge",
541  CPL_TYPE_BOOL,
542  "Merge output products from different IFUs into a common file.",
543  "muse.muse_wavecal",
544  (int)FALSE);
545  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "merge");
546  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "merge");
547 
548  cpl_parameterlist_append(recipe->parameters, p);
549 
550  return 0;
551 } /* muse_wavecal_create() */
552 
553 /*----------------------------------------------------------------------------*/
564 /*----------------------------------------------------------------------------*/
565 static int
566 muse_wavecal_params_fill(muse_wavecal_params_t *aParams, cpl_parameterlist *aParameters)
567 {
568  cpl_ensure_code(aParams, CPL_ERROR_NULL_INPUT);
569  cpl_ensure_code(aParameters, CPL_ERROR_NULL_INPUT);
570  cpl_parameter *p;
571 
572  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.nifu");
573  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
574  aParams->nifu = cpl_parameter_get_int(p);
575 
576  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.overscan");
577  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
578  aParams->overscan = cpl_parameter_get_string(p);
579 
580  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.ovscreject");
581  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
582  aParams->ovscreject = cpl_parameter_get_string(p);
583 
584  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.ovscsigma");
585  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
586  aParams->ovscsigma = cpl_parameter_get_double(p);
587 
588  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.ovscignore");
589  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
590  aParams->ovscignore = cpl_parameter_get_int(p);
591 
592  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.combine");
593  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
594  aParams->combine_s = cpl_parameter_get_string(p);
595  aParams->combine =
596  (!strcasecmp(aParams->combine_s, "average")) ? MUSE_WAVECAL_PARAM_COMBINE_AVERAGE :
597  (!strcasecmp(aParams->combine_s, "median")) ? MUSE_WAVECAL_PARAM_COMBINE_MEDIAN :
598  (!strcasecmp(aParams->combine_s, "minmax")) ? MUSE_WAVECAL_PARAM_COMBINE_MINMAX :
599  (!strcasecmp(aParams->combine_s, "sigclip")) ? MUSE_WAVECAL_PARAM_COMBINE_SIGCLIP :
600  MUSE_WAVECAL_PARAM_COMBINE_INVALID_VALUE;
601  cpl_ensure_code(aParams->combine != MUSE_WAVECAL_PARAM_COMBINE_INVALID_VALUE,
602  CPL_ERROR_ILLEGAL_INPUT);
603 
604  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.lampwise");
605  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
606  aParams->lampwise = cpl_parameter_get_bool(p);
607 
608  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.sigma");
609  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
610  aParams->sigma = cpl_parameter_get_double(p);
611 
612  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.dres");
613  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
614  aParams->dres = cpl_parameter_get_double(p);
615 
616  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.tolerance");
617  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
618  aParams->tolerance = cpl_parameter_get_double(p);
619 
620  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.xorder");
621  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
622  aParams->xorder = cpl_parameter_get_int(p);
623 
624  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.yorder");
625  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
626  aParams->yorder = cpl_parameter_get_int(p);
627 
628  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.linesigma");
629  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
630  aParams->linesigma = cpl_parameter_get_double(p);
631 
632  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.residuals");
633  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
634  aParams->residuals = cpl_parameter_get_bool(p);
635 
636  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.fitsigma");
637  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
638  aParams->fitsigma = cpl_parameter_get_double(p);
639 
640  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.fitweighting");
641  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
642  aParams->fitweighting_s = cpl_parameter_get_string(p);
643  aParams->fitweighting =
644  (!strcasecmp(aParams->fitweighting_s, "uniform")) ? MUSE_WAVECAL_PARAM_FITWEIGHTING_UNIFORM :
645  (!strcasecmp(aParams->fitweighting_s, "cerr")) ? MUSE_WAVECAL_PARAM_FITWEIGHTING_CERR :
646  (!strcasecmp(aParams->fitweighting_s, "scatter")) ? MUSE_WAVECAL_PARAM_FITWEIGHTING_SCATTER :
647  (!strcasecmp(aParams->fitweighting_s, "cerrscatter")) ? MUSE_WAVECAL_PARAM_FITWEIGHTING_CERRSCATTER :
648  MUSE_WAVECAL_PARAM_FITWEIGHTING_INVALID_VALUE;
649  cpl_ensure_code(aParams->fitweighting != MUSE_WAVECAL_PARAM_FITWEIGHTING_INVALID_VALUE,
650  CPL_ERROR_ILLEGAL_INPUT);
651 
652  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.resample");
653  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
654  aParams->resample = cpl_parameter_get_bool(p);
655 
656  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.wavemap");
657  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
658  aParams->wavemap = cpl_parameter_get_bool(p);
659 
660  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.merge");
661  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
662  aParams->merge = cpl_parameter_get_bool(p);
663 
664  return 0;
665 } /* muse_wavecal_params_fill() */
666 
667 /*----------------------------------------------------------------------------*/
674 /*----------------------------------------------------------------------------*/
675 static int
676 muse_wavecal_exec(cpl_plugin *aPlugin)
677 {
678  if (cpl_plugin_get_type(aPlugin) != CPL_PLUGIN_TYPE_RECIPE) {
679  return -1;
680  }
682  cpl_recipe *recipe = (cpl_recipe *)aPlugin;
683  cpl_msg_set_threadid_on();
684 
685  cpl_frameset *usedframes = cpl_frameset_new(),
686  *outframes = cpl_frameset_new();
687  muse_wavecal_params_t params;
688  muse_wavecal_params_fill(&params, recipe->parameters);
689 
690  cpl_errorstate prestate = cpl_errorstate_get();
691 
692  if (params.nifu < -1 || params.nifu > kMuseNumIFUs) {
693  cpl_msg_error(__func__, "Please specify a valid IFU number (between 1 and "
694  "%d), 0 (to process all IFUs consecutively), or -1 (to "
695  "process all IFUs in parallel) using --nifu.", kMuseNumIFUs);
696  return -1;
697  } /* if invalid params.nifu */
698 
699  cpl_boolean donotmerge = CPL_FALSE; /* depending on nifu we may not merge */
700  int rc = 0;
701  if (params.nifu > 0) {
702  muse_processing *proc = muse_processing_new("muse_wavecal",
703  recipe);
704  rc = muse_wavecal_compute(proc, &params);
705  cpl_frameset_join(usedframes, proc->usedframes);
706  cpl_frameset_join(outframes, proc->outframes);
708  donotmerge = CPL_TRUE; /* after processing one IFU, merging cannot work */
709  } else if (params.nifu < 0) { /* parallel processing */
710  int *rcs = cpl_calloc(kMuseNumIFUs, sizeof(int));
711  int nifu;
712  #pragma omp parallel for default(none) \
713  shared(outframes, params, rcs, recipe, usedframes)
714  for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
715  muse_processing *proc = muse_processing_new("muse_wavecal",
716  recipe);
717  muse_wavecal_params_t *pars = cpl_malloc(sizeof(muse_wavecal_params_t));
718  memcpy(pars, &params, sizeof(muse_wavecal_params_t));
719  pars->nifu = nifu;
720  int *rci = rcs + (nifu - 1);
721  *rci = muse_wavecal_compute(proc, pars);
722  if (rci && (int)cpl_error_get_code() == MUSE_ERROR_CHIP_NOT_LIVE) {
723  *rci = 0;
724  }
725  cpl_free(pars);
726  #pragma omp critical(muse_processing_used_frames)
727  cpl_frameset_join(usedframes, proc->usedframes);
728  #pragma omp critical(muse_processing_output_frames)
729  cpl_frameset_join(outframes, proc->outframes);
731  } /* for nifu */
732  /* non-parallel loop to propagate the "worst" return code; *
733  * since we only ever return -1, any non-zero code is propagated */
734  for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
735  if (rcs[nifu-1] != 0) {
736  rc = rcs[nifu-1];
737  } /* if */
738  } /* for nifu */
739  cpl_free(rcs);
740  } else { /* serial processing */
741  for (params.nifu = 1; params.nifu <= kMuseNumIFUs && !rc; params.nifu++) {
742  muse_processing *proc = muse_processing_new("muse_wavecal",
743  recipe);
744  rc = muse_wavecal_compute(proc, &params);
745  if (rc && (int)cpl_error_get_code() == MUSE_ERROR_CHIP_NOT_LIVE) {
746  rc = 0;
747  }
748  cpl_frameset_join(usedframes, proc->usedframes);
749  cpl_frameset_join(outframes, proc->outframes);
751  } /* for nifu */
752  } /* else */
753  UNUSED_ARGUMENT(donotmerge); /* maybe this is not going to be used below */
754 
755  if (!cpl_errorstate_is_equal(prestate)) {
756  /* dump all errors from this recipe in chronological order */
757  cpl_errorstate_dump(prestate, CPL_FALSE, muse_cplerrorstate_dump_some);
758  /* reset message level to not get the same errors displayed again by esorex */
759  cpl_msg_set_level(CPL_MSG_INFO);
760  }
761  /* clean up duplicates in framesets of used and output frames */
764 
765  /* merge output products from the up to 24 IFUs */
766  if (params.merge && !donotmerge) {
767  muse_utils_frameset_merge_frames(outframes, CPL_TRUE);
768  }
769 
770  /* to get esorex to see our classification (frame groups etc.), *
771  * replace the original frameset with the list of used frames *
772  * before appending product output frames */
773  /* keep the same pointer, so just erase all frames, not delete the frameset */
774  muse_cplframeset_erase_all(recipe->frames);
775  cpl_frameset_join(recipe->frames, usedframes);
776  cpl_frameset_join(recipe->frames, outframes);
777  cpl_frameset_delete(usedframes);
778  cpl_frameset_delete(outframes);
779  return rc;
780 } /* muse_wavecal_exec() */
781 
782 /*----------------------------------------------------------------------------*/
789 /*----------------------------------------------------------------------------*/
790 static int
791 muse_wavecal_destroy(cpl_plugin *aPlugin)
792 {
793  /* Get the recipe from the plugin */
794  cpl_recipe *recipe;
795  if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
796  recipe = (cpl_recipe *)aPlugin;
797  } else {
798  return -1;
799  }
800 
801  /* Clean up */
802  cpl_parameterlist_delete(recipe->parameters);
804  return 0;
805 } /* muse_wavecal_destroy() */
806 
807 /*----------------------------------------------------------------------------*/
817 /*----------------------------------------------------------------------------*/
818 int
819 cpl_plugin_get_info(cpl_pluginlist *aList)
820 {
821  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
822  cpl_plugin *plugin = &recipe->interface;
823 
824  char *helptext;
826  helptext = cpl_sprintf("%s%s", muse_wavecal_help,
827  muse_wavecal_help_esorex);
828  } else {
829  helptext = cpl_sprintf("%s", muse_wavecal_help);
830  }
831 
832  /* Initialize the CPL plugin stuff for this module */
833  cpl_plugin_init(plugin, CPL_PLUGIN_API, MUSE_BINARY_VERSION,
834  CPL_PLUGIN_TYPE_RECIPE,
835  "muse_wavecal",
836  "Detect arc emission lines and determine the wavelength solution for each slice.",
837  helptext,
838  "Peter Weilbacher",
839  "usd-help@eso.org",
841  muse_wavecal_create,
842  muse_wavecal_exec,
843  muse_wavecal_destroy);
844  cpl_pluginlist_append(aList, plugin);
845  cpl_free(helptext);
846 
847  return 0;
848 } /* cpl_plugin_get_info() */
849 
void muse_processing_delete(muse_processing *aProcessing)
Free the muse_processing structure.
double fitsigma
Sigma level for iterative rejection of deviant datapoints during the final polynomial wavelength solu...
double linesigma
Sigma level for iterative rejection of deviant fits for each arc line within each slice...
const char * ovscreject
This influences how values are rejected when computing overscan statistics. Either no rejection at al...
double ovscsigma
If the deviation of mean overscan levels between a raw input image and the reference image is higher ...
muse_cplframework_type muse_cplframework(void)
Return the CPL framework the recipe is run under.
int ovscignore
The number of pixels of the overscan adjacent to the data section of the CCD that are ignored when co...
double dres
The allowed range of resolutions for pattern matching (of detected arc lines to line list) in fractio...
cpl_frameset * usedframes
muse_processing * muse_processing_new(const char *aName, cpl_recipe *aRecipe)
Create a new processing structure.
const char * muse_get_license(void)
Get the pipeline copyright and license.
Definition: muse_utils.c:83
muse_frame_mode
int nifu
IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in ...
cpl_frameset * outframes
const char * overscan
If this is "none", stop when detecting discrepant overscan levels (see ovscsigma), for "offset" it assumes that the mean overscan level represents the real offset in the bias levels of the exposures involved, and adjusts the data accordingly; for "vpoly", a polynomial is fit to the vertical overscan and subtracted from the whole quadrant.
void muse_cplerrorstate_dump_some(unsigned aCurrent, unsigned aFirst, unsigned aLast)
Dump some CPL errors.
int wavemap
Create a wavelength map of the input images.
int lampwise
Identify and measure the arc emission lines on images separately for each lamp setup.
int residuals
Create a table containing residuals of the fits to the data of all arc lines. This is useful to asses...
void muse_processinginfo_delete(cpl_recipe *)
Clear all information from the processing info and from the recipe config.
int resample
Resample the input arc images onto 2D images for a visual check using tracing and wavelength calibrat...
int yorder
Order of the polynomial used to fit the dispersion relation.
cpl_error_code muse_cplframeset_erase_duplicate(cpl_frameset *aFrames)
Erase all duplicate frames from a frameset.
cpl_error_code muse_cplframeset_erase_all(cpl_frameset *aFrames)
Erase all frames in a frameset.
int fitweighting
Type of weighting to use in the final polynomial wavelength solution fit, using centroiding error est...
const char * combine_s
Type of lampwise image combination to use. (as string)
void muse_processinginfo_register(cpl_recipe *, cpl_recipeconfig *, muse_processing_prepare_header_func *, muse_processing_get_frame_level_func *, muse_processing_get_frame_mode_func *)
Register extended functionalities for MUSE recipes.
int combine
Type of lampwise image combination to use.
Structure to hold the parameters of the muse_wavecal recipe.
double tolerance
Tolerance for pattern matching (of detected arc lines to line list)
void muse_processing_recipeinfo(cpl_plugin *)
Output main pipeline configuration, inputs, and parameters.
const char * fitweighting_s
Type of weighting to use in the final polynomial wavelength solution fit, using centroiding error est...
cpl_error_code muse_utils_frameset_merge_frames(cpl_frameset *aFrames, cpl_boolean aDelete)
Merge IFU-specific files from a frameset to create multi-IFU outputs.
Definition: muse_utils.c:589
double sigma
Sigma level used to detect arc emission lines above the median background level in the S/N image of t...
int merge
Merge output products from different IFUs into a common file.
int xorder
Order of the polynomial for the horizontal curvature within each slice.
cpl_error_code muse_processing_prepare_property(cpl_propertylist *, const char *, cpl_type, const char *)
Prepare and check the specified property.