MUSE Pipeline Reference Manual  1.0.2
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-2014 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, first into separate images for each lamp. If --lampwise is not given or if --resample is given, these lamp-separate exposures are summed to create a single combined master arc. 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. If --lampwise is switched on, 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 . 1 Bad pixel table"
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  "\n ARC_RED final Reduced master ARC image"
103  "\n ARC_RESAMPLED final Resampled arc images (if --resampled=true)"
104  "\n WAVE_MAP final Wavelength map of the input images";
105 
106 /*----------------------------------------------------------------------------*/
114 /*----------------------------------------------------------------------------*/
115 static cpl_recipeconfig *
116 muse_wavecal_new_recipeconfig(void)
117 {
118  cpl_recipeconfig *recipeconfig = cpl_recipeconfig_new();
119  const char *tag;
120 
121  tag = "ARC";
122  cpl_recipeconfig_set_tag(recipeconfig, tag, 1, -1);
123  cpl_recipeconfig_set_input(recipeconfig, tag, "MASTER_BIAS", 1, 1);
124  cpl_recipeconfig_set_input(recipeconfig, tag, "MASTER_DARK", -1, 1);
125  cpl_recipeconfig_set_input(recipeconfig, tag, "MASTER_FLAT", -1, 1);
126  cpl_recipeconfig_set_input(recipeconfig, tag, "TRACE_TABLE", 1, 1);
127  cpl_recipeconfig_set_input(recipeconfig, tag, "LINE_CATALOG", 1, 1);
128  cpl_recipeconfig_set_input(recipeconfig, tag, "BADPIX_TABLE", -1, 1);
129  cpl_recipeconfig_set_output(recipeconfig, tag, "WAVECAL_TABLE");
130  cpl_recipeconfig_set_output(recipeconfig, tag, "WAVECAL_RESIDUALS");
131  cpl_recipeconfig_set_output(recipeconfig, tag, "ARC_RED_LAMP");
132  cpl_recipeconfig_set_output(recipeconfig, tag, "ARC_RED");
133  cpl_recipeconfig_set_output(recipeconfig, tag, "ARC_RESAMPLED");
134  cpl_recipeconfig_set_output(recipeconfig, tag, "WAVE_MAP");
135 
136  return recipeconfig;
137 } /* muse_wavecal_new_recipeconfig() */
138 
139 /*----------------------------------------------------------------------------*/
149 /*----------------------------------------------------------------------------*/
150 static cpl_error_code
151 muse_wavecal_prepare_header(const char *aFrametag, cpl_propertylist *aHeader)
152 {
153  cpl_ensure_code(aFrametag, CPL_ERROR_NULL_INPUT);
154  cpl_ensure_code(aHeader, CPL_ERROR_NULL_INPUT);
155  if (!strcmp(aFrametag, "WAVECAL_TABLE")) {
156  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES NDET",
157  CPL_TYPE_INT,
158  "Number of detected arc lines in slice j");
159  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES NID",
160  CPL_TYPE_INT,
161  "Number of identified arc lines in slice j");
162  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES PEAK MEAN",
163  CPL_TYPE_FLOAT,
164  "[count] Mean peak count level above background of detected arc lines in slice j");
165  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES PEAK STDEV",
166  CPL_TYPE_FLOAT,
167  "[count] Standard deviation of peak count level above background of detected arc lines in slice j");
168  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES PEAK MIN",
169  CPL_TYPE_FLOAT,
170  "[count] Peak count level above background of the faintest line in slice j");
171  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES PEAK MAX",
172  CPL_TYPE_FLOAT,
173  "[count] Peak count level above background of the brightest line in slice j");
174  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LAMP[0-9] LINES PEAK MEAN",
175  CPL_TYPE_FLOAT,
176  "[count] Mean peak count level of lines of lamp l above background of detected arc lines in slice j. Not produced with --lampwise=FALSE!");
177  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LAMP[0-9] LINES PEAK STDEV",
178  CPL_TYPE_FLOAT,
179  "[count] Standard deviation of peak count level of lines of lamp l above background of detected arc lines in slice j. Not produced with --lampwise=FALSE!");
180  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LAMP[0-9] LINES PEAK MAX",
181  CPL_TYPE_FLOAT,
182  "[count] Peak count level above background of the brightest line of lamp l in slice j. Not produced with --lampwise=FALSE!");
183  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES FWHM MEAN",
184  CPL_TYPE_FLOAT,
185  "Mean FWHM of detected arc lines in slice j");
186  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES FWHM STDEV",
187  CPL_TYPE_FLOAT,
188  "Standard deviation of FWHM of detected arc lines in slice j");
189  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES FWHM MIN",
190  CPL_TYPE_FLOAT,
191  "Minimum FWHM of detected arc lines in slice j");
192  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ LINES FWHM MAX",
193  CPL_TYPE_FLOAT,
194  "Maximum FWHM of detected arc lines in slice j");
195  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ RESOL",
196  CPL_TYPE_FLOAT,
197  "Mean spectral resolution R determined in slice j");
198  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ FIT NLINES",
199  CPL_TYPE_INT,
200  "Number of arc lines used in calibration solution fit in slice j");
201  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ FIT RMS",
202  CPL_TYPE_FLOAT,
203  "[Angstrom] RMS of the wavelength calibration fit in slice j");
204  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ DWLEN BOTTOM",
205  CPL_TYPE_FLOAT,
206  "[Angstrom] Difference in wavelength computed for the bottom left and bottom right corners of the slice on the CCD");
207  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ DWLEN TOP",
208  CPL_TYPE_FLOAT,
209  "[Angstrom] Difference in wavelength computed for the top left and top right corners of the slice on the CCD");
210  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ WLPOS",
211  CPL_TYPE_FLOAT,
212  "[pix] Position of wavelength given in WLEN in slice j");
213  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL SLICE[0-9]+ WLEN",
214  CPL_TYPE_FLOAT,
215  "[Angstrom] Wavelength associated to WLPOS in slice j");
216  } else if (!strcmp(aFrametag, "WAVECAL_RESIDUALS")) {
217  } else if (!strcmp(aFrametag, "ARC_RED_LAMP")) {
218  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL INPUT[0-9]+ NSATURATED",
219  CPL_TYPE_INT,
220  "Number of saturated pixels in raw arc i in input list");
221  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL NSATURATED",
222  CPL_TYPE_INT,
223  "Number of saturated pixels in output data");
224  } else if (!strcmp(aFrametag, "ARC_RED")) {
225  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL LAMP[0-9]+ INPUT[0-9]+ NSATURATED",
226  CPL_TYPE_INT,
227  "Number of saturated pixels in raw arc i of lamp l in input list");
228  muse_processing_prepare_property(aHeader, "ESO QC WAVECAL NSATURATED",
229  CPL_TYPE_INT,
230  "Number of saturated pixels in output data");
231  } else if (!strcmp(aFrametag, "ARC_RESAMPLED")) {
232  } else if (!strcmp(aFrametag, "WAVE_MAP")) {
233  } else {
234  cpl_msg_warning(__func__, "Frame tag %s is not defined", aFrametag);
235  return CPL_ERROR_ILLEGAL_INPUT;
236  }
237  return CPL_ERROR_NONE;
238 } /* muse_wavecal_prepare_header() */
239 
240 /*----------------------------------------------------------------------------*/
249 /*----------------------------------------------------------------------------*/
250 static cpl_frame_level
251 muse_wavecal_get_frame_level(const char *aFrametag)
252 {
253  if (!aFrametag) {
254  return CPL_FRAME_LEVEL_NONE;
255  }
256  if (!strcmp(aFrametag, "WAVECAL_TABLE")) {
257  return CPL_FRAME_LEVEL_FINAL;
258  }
259  if (!strcmp(aFrametag, "WAVECAL_RESIDUALS")) {
260  return CPL_FRAME_LEVEL_FINAL;
261  }
262  if (!strcmp(aFrametag, "ARC_RED_LAMP")) {
263  return CPL_FRAME_LEVEL_FINAL;
264  }
265  if (!strcmp(aFrametag, "ARC_RED")) {
266  return CPL_FRAME_LEVEL_FINAL;
267  }
268  if (!strcmp(aFrametag, "ARC_RESAMPLED")) {
269  return CPL_FRAME_LEVEL_FINAL;
270  }
271  if (!strcmp(aFrametag, "WAVE_MAP")) {
272  return CPL_FRAME_LEVEL_FINAL;
273  }
274  return CPL_FRAME_LEVEL_NONE;
275 } /* muse_wavecal_get_frame_level() */
276 
277 /*----------------------------------------------------------------------------*/
286 /*----------------------------------------------------------------------------*/
287 static muse_frame_mode
288 muse_wavecal_get_frame_mode(const char *aFrametag)
289 {
290  if (!aFrametag) {
291  return MUSE_FRAME_MODE_ALL;
292  }
293  if (!strcmp(aFrametag, "WAVECAL_TABLE")) {
294  return MUSE_FRAME_MODE_MASTER;
295  }
296  if (!strcmp(aFrametag, "WAVECAL_RESIDUALS")) {
297  return MUSE_FRAME_MODE_MASTER;
298  }
299  if (!strcmp(aFrametag, "ARC_RED_LAMP")) {
300  return MUSE_FRAME_MODE_SUBSET;
301  }
302  if (!strcmp(aFrametag, "ARC_RED")) {
303  return MUSE_FRAME_MODE_MASTER;
304  }
305  if (!strcmp(aFrametag, "ARC_RESAMPLED")) {
306  return MUSE_FRAME_MODE_MASTER;
307  }
308  if (!strcmp(aFrametag, "WAVE_MAP")) {
309  return MUSE_FRAME_MODE_MASTER;
310  }
311  return MUSE_FRAME_MODE_ALL;
312 } /* muse_wavecal_get_frame_mode() */
313 
314 /*----------------------------------------------------------------------------*/
324 /*----------------------------------------------------------------------------*/
325 static int
326 muse_wavecal_create(cpl_plugin *aPlugin)
327 {
328  /* Check that the plugin is part of a valid recipe */
329  cpl_recipe *recipe;
330  if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
331  recipe = (cpl_recipe *)aPlugin;
332  } else {
333  return -1;
334  }
335 
336  /* register the extended processing information (new FITS header creation, *
337  * getting of the frame level for a certain tag) */
339  muse_wavecal_new_recipeconfig(),
340  muse_wavecal_prepare_header,
341  muse_wavecal_get_frame_level,
342  muse_wavecal_get_frame_mode);
343 
344  /* XXX initialize timing in messages *
345  * since at least esorex is too stupid to turn it on, we have to do it */
347  cpl_msg_set_time_on();
348  }
349 
350  /* Create the parameter list in the cpl_recipe object */
351  recipe->parameters = cpl_parameterlist_new();
352  /* Fill the parameters list */
353  cpl_parameter *p;
354 
355  /* --nifu: IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in parallel. */
356  p = cpl_parameter_new_range("muse.muse_wavecal.nifu",
357  CPL_TYPE_INT,
358  "IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in parallel.",
359  "muse.muse_wavecal",
360  (int)0,
361  (int)-1,
362  (int)24);
363  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "nifu");
364  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "nifu");
365 
366  cpl_parameterlist_append(recipe->parameters, p);
367 
368  /* --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. */
369  p = cpl_parameter_new_value("muse.muse_wavecal.overscan",
370  CPL_TYPE_STRING,
371  "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.",
372  "muse.muse_wavecal",
373  (const char *)"vpoly");
374  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "overscan");
375  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "overscan");
376 
377  cpl_parameterlist_append(recipe->parameters, p);
378 
379  /* --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"). */
380  p = cpl_parameter_new_value("muse.muse_wavecal.ovscreject",
381  CPL_TYPE_STRING,
382  "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\").",
383  "muse.muse_wavecal",
384  (const char *)"dcr");
385  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "ovscreject");
386  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ovscreject");
387 
388  cpl_parameterlist_append(recipe->parameters, p);
389 
390  /* --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". */
391  p = cpl_parameter_new_value("muse.muse_wavecal.ovscsigma",
392  CPL_TYPE_DOUBLE,
393  "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\".",
394  "muse.muse_wavecal",
395  (double)30.);
396  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "ovscsigma");
397  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ovscsigma");
398 
399  cpl_parameterlist_append(recipe->parameters, p);
400 
401  /* --ovscignore: The number of pixels of the overscan adjacent to the data region of the CCD that are ignored when computing statistics or fits. */
402  p = cpl_parameter_new_value("muse.muse_wavecal.ovscignore",
403  CPL_TYPE_INT,
404  "The number of pixels of the overscan adjacent to the data region of the CCD that are ignored when computing statistics or fits.",
405  "muse.muse_wavecal",
406  (int)3);
407  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "ovscignore");
408  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ovscignore");
409 
410  cpl_parameterlist_append(recipe->parameters, p);
411 
412  /* --combine: Type of lampwise image combination to use. */
413  p = cpl_parameter_new_enum("muse.muse_wavecal.combine",
414  CPL_TYPE_STRING,
415  "Type of lampwise image combination to use.",
416  "muse.muse_wavecal",
417  (const char *)"sigclip",
418  4,
419  (const char *)"average",
420  (const char *)"median",
421  (const char *)"minmax",
422  (const char *)"sigclip");
423  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "combine");
424  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "combine");
425 
426  cpl_parameterlist_append(recipe->parameters, p);
427 
428  /* --lampwise: Identify and measure the arc emission lines on images separately for each lamp setup. */
429  p = cpl_parameter_new_value("muse.muse_wavecal.lampwise",
430  CPL_TYPE_BOOL,
431  "Identify and measure the arc emission lines on images separately for each lamp setup.",
432  "muse.muse_wavecal",
433  (int)TRUE);
434  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lampwise");
435  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lampwise");
436 
437  cpl_parameterlist_append(recipe->parameters, p);
438 
439  /* --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 */
440  p = cpl_parameter_new_value("muse.muse_wavecal.sigma",
441  CPL_TYPE_DOUBLE,
442  "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",
443  "muse.muse_wavecal",
444  (double)1.0);
445  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "sigma");
446  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sigma");
447 
448  cpl_parameterlist_append(recipe->parameters, p);
449 
450  /* --dres: The allowed range of resolutions for pattern matching (of detected arc lines to line list) in fractions relative to the expected value */
451  p = cpl_parameter_new_value("muse.muse_wavecal.dres",
452  CPL_TYPE_DOUBLE,
453  "The allowed range of resolutions for pattern matching (of detected arc lines to line list) in fractions relative to the expected value",
454  "muse.muse_wavecal",
455  (double)0.05);
456  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "dres");
457  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dres");
458 
459  cpl_parameterlist_append(recipe->parameters, p);
460 
461  /* --tolerance: Tolerance for pattern matching (of detected arc lines to line list) */
462  p = cpl_parameter_new_value("muse.muse_wavecal.tolerance",
463  CPL_TYPE_DOUBLE,
464  "Tolerance for pattern matching (of detected arc lines to line list)",
465  "muse.muse_wavecal",
466  (double)0.1);
467  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "tolerance");
468  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "tolerance");
469 
470  cpl_parameterlist_append(recipe->parameters, p);
471 
472  /* --xorder: Order of the polynomial for the horizontal curvature within each slice */
473  p = cpl_parameter_new_value("muse.muse_wavecal.xorder",
474  CPL_TYPE_INT,
475  "Order of the polynomial for the horizontal curvature within each slice",
476  "muse.muse_wavecal",
477  (int)2);
478  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "xorder");
479  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "xorder");
480 
481  cpl_parameterlist_append(recipe->parameters, p);
482 
483  /* --yorder: Order of the polynomial used to fit the dispersion relation */
484  p = cpl_parameter_new_value("muse.muse_wavecal.yorder",
485  CPL_TYPE_INT,
486  "Order of the polynomial used to fit the dispersion relation",
487  "muse.muse_wavecal",
488  (int)6);
489  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "yorder");
490  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "yorder");
491 
492  cpl_parameterlist_append(recipe->parameters, p);
493 
494  /* --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). */
495  p = cpl_parameter_new_value("muse.muse_wavecal.linesigma",
496  CPL_TYPE_DOUBLE,
497  "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).",
498  "muse.muse_wavecal",
499  (double)-1.0);
500  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "linesigma");
501  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "linesigma");
502 
503  cpl_parameterlist_append(recipe->parameters, p);
504 
505  /* --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. */
506  p = cpl_parameter_new_value("muse.muse_wavecal.residuals",
507  CPL_TYPE_BOOL,
508  "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.",
509  "muse.muse_wavecal",
510  (int)FALSE);
511  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "residuals");
512  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "residuals");
513 
514  cpl_parameterlist_append(recipe->parameters, p);
515 
516  /* --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). */
517  p = cpl_parameter_new_value("muse.muse_wavecal.fitsigma",
518  CPL_TYPE_DOUBLE,
519  "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).",
520  "muse.muse_wavecal",
521  (double)-1.0);
522  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "fitsigma");
523  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "fitsigma");
524 
525  cpl_parameterlist_append(recipe->parameters, p);
526 
527  /* --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. */
528  p = cpl_parameter_new_enum("muse.muse_wavecal.fitweighting",
529  CPL_TYPE_STRING,
530  "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.",
531  "muse.muse_wavecal",
532  (const char *)"cerrscatter",
533  4,
534  (const char *)"uniform",
535  (const char *)"cerr",
536  (const char *)"scatter",
537  (const char *)"cerrscatter");
538  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "fitweighting");
539  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "fitweighting");
540 
541  cpl_parameterlist_append(recipe->parameters, p);
542 
543  /* --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! */
544  p = cpl_parameter_new_value("muse.muse_wavecal.resample",
545  CPL_TYPE_BOOL,
546  "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!",
547  "muse.muse_wavecal",
548  (int)FALSE);
549  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "resample");
550  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "resample");
551 
552  cpl_parameterlist_append(recipe->parameters, p);
553 
554  /* --wavemap: Create a wavelength map of the input images */
555  p = cpl_parameter_new_value("muse.muse_wavecal.wavemap",
556  CPL_TYPE_BOOL,
557  "Create a wavelength map of the input images",
558  "muse.muse_wavecal",
559  (int)FALSE);
560  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "wavemap");
561  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wavemap");
562 
563  cpl_parameterlist_append(recipe->parameters, p);
564 
565  /* --merge: Merge output products from different IFUs into a common file. */
566  p = cpl_parameter_new_value("muse.muse_wavecal.merge",
567  CPL_TYPE_BOOL,
568  "Merge output products from different IFUs into a common file.",
569  "muse.muse_wavecal",
570  (int)FALSE);
571  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "merge");
572  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "merge");
573 
574  cpl_parameterlist_append(recipe->parameters, p);
575 
576  return 0;
577 } /* muse_wavecal_create() */
578 
579 /*----------------------------------------------------------------------------*/
590 /*----------------------------------------------------------------------------*/
591 static int
592 muse_wavecal_params_fill(muse_wavecal_params_t *aParams, cpl_parameterlist *aParameters)
593 {
594  cpl_ensure_code(aParams, CPL_ERROR_NULL_INPUT);
595  cpl_ensure_code(aParameters, CPL_ERROR_NULL_INPUT);
596  cpl_parameter *p;
597 
598  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.nifu");
599  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
600  aParams->nifu = cpl_parameter_get_int(p);
601 
602  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.overscan");
603  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
604  aParams->overscan = cpl_parameter_get_string(p);
605 
606  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.ovscreject");
607  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
608  aParams->ovscreject = cpl_parameter_get_string(p);
609 
610  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.ovscsigma");
611  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
612  aParams->ovscsigma = cpl_parameter_get_double(p);
613 
614  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.ovscignore");
615  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
616  aParams->ovscignore = cpl_parameter_get_int(p);
617 
618  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.combine");
619  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
620  aParams->combine_s = cpl_parameter_get_string(p);
621  aParams->combine =
622  (!strcasecmp(aParams->combine_s, "average")) ? MUSE_WAVECAL_PARAM_COMBINE_AVERAGE :
623  (!strcasecmp(aParams->combine_s, "median")) ? MUSE_WAVECAL_PARAM_COMBINE_MEDIAN :
624  (!strcasecmp(aParams->combine_s, "minmax")) ? MUSE_WAVECAL_PARAM_COMBINE_MINMAX :
625  (!strcasecmp(aParams->combine_s, "sigclip")) ? MUSE_WAVECAL_PARAM_COMBINE_SIGCLIP :
626  MUSE_WAVECAL_PARAM_COMBINE_INVALID_VALUE;
627  cpl_ensure_code(aParams->combine != MUSE_WAVECAL_PARAM_COMBINE_INVALID_VALUE,
628  CPL_ERROR_ILLEGAL_INPUT);
629 
630  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.lampwise");
631  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
632  aParams->lampwise = cpl_parameter_get_bool(p);
633 
634  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.sigma");
635  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
636  aParams->sigma = cpl_parameter_get_double(p);
637 
638  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.dres");
639  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
640  aParams->dres = cpl_parameter_get_double(p);
641 
642  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.tolerance");
643  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
644  aParams->tolerance = cpl_parameter_get_double(p);
645 
646  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.xorder");
647  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
648  aParams->xorder = cpl_parameter_get_int(p);
649 
650  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.yorder");
651  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
652  aParams->yorder = cpl_parameter_get_int(p);
653 
654  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.linesigma");
655  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
656  aParams->linesigma = cpl_parameter_get_double(p);
657 
658  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.residuals");
659  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
660  aParams->residuals = cpl_parameter_get_bool(p);
661 
662  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.fitsigma");
663  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
664  aParams->fitsigma = cpl_parameter_get_double(p);
665 
666  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.fitweighting");
667  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
668  aParams->fitweighting_s = cpl_parameter_get_string(p);
669  aParams->fitweighting =
670  (!strcasecmp(aParams->fitweighting_s, "uniform")) ? MUSE_WAVECAL_PARAM_FITWEIGHTING_UNIFORM :
671  (!strcasecmp(aParams->fitweighting_s, "cerr")) ? MUSE_WAVECAL_PARAM_FITWEIGHTING_CERR :
672  (!strcasecmp(aParams->fitweighting_s, "scatter")) ? MUSE_WAVECAL_PARAM_FITWEIGHTING_SCATTER :
673  (!strcasecmp(aParams->fitweighting_s, "cerrscatter")) ? MUSE_WAVECAL_PARAM_FITWEIGHTING_CERRSCATTER :
674  MUSE_WAVECAL_PARAM_FITWEIGHTING_INVALID_VALUE;
675  cpl_ensure_code(aParams->fitweighting != MUSE_WAVECAL_PARAM_FITWEIGHTING_INVALID_VALUE,
676  CPL_ERROR_ILLEGAL_INPUT);
677 
678  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.resample");
679  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
680  aParams->resample = cpl_parameter_get_bool(p);
681 
682  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.wavemap");
683  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
684  aParams->wavemap = cpl_parameter_get_bool(p);
685 
686  p = cpl_parameterlist_find(aParameters, "muse.muse_wavecal.merge");
687  cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
688  aParams->merge = cpl_parameter_get_bool(p);
689 
690  return 0;
691 } /* muse_wavecal_params_fill() */
692 
693 /*----------------------------------------------------------------------------*/
700 /*----------------------------------------------------------------------------*/
701 static int
702 muse_wavecal_exec(cpl_plugin *aPlugin)
703 {
704  if (cpl_plugin_get_type(aPlugin) != CPL_PLUGIN_TYPE_RECIPE) {
705  return -1;
706  }
707  cpl_recipe *recipe = (cpl_recipe *)aPlugin;
708  cpl_msg_set_threadid_on();
709 
710  cpl_frameset *usedframes = cpl_frameset_new(),
711  *outframes = cpl_frameset_new();
712  muse_wavecal_params_t params;
713  muse_wavecal_params_fill(&params, recipe->parameters);
714 
715  cpl_errorstate prestate = cpl_errorstate_get();
716 
717  if (params.nifu < -1 || params.nifu > kMuseNumIFUs) {
718  cpl_msg_error(__func__, "Please specify a valid IFU number (between 1 and "
719  "%d), 0 (to process all IFUs consecutively), or -1 (to "
720  "process all IFUs in parallel) using --nifu.", kMuseNumIFUs);
721  return -1;
722  } /* if invalid params.nifu */
723 
724  cpl_boolean donotmerge = CPL_FALSE; /* depending on nifu we may not merge */
725  int rc = 0;
726  if (params.nifu > 0) {
727  muse_processing *proc = muse_processing_new("muse_wavecal",
728  recipe);
729  rc = muse_wavecal_compute(proc, &params);
730  cpl_frameset_join(usedframes, proc->usedframes);
731  cpl_frameset_join(outframes, proc->outframes);
733  donotmerge = CPL_TRUE; /* after processing one IFU, merging cannot work */
734  } else if (params.nifu < 0) { /* parallel processing */
735  int *rcs = cpl_calloc(kMuseNumIFUs, sizeof(int));
736  int nifu;
737  #pragma omp parallel for default(none) \
738  shared(outframes, params, rcs, recipe, usedframes)
739  for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
740  muse_processing *proc = muse_processing_new("muse_wavecal",
741  recipe);
742  muse_wavecal_params_t *pars = cpl_malloc(sizeof(muse_wavecal_params_t));
743  memcpy(pars, &params, sizeof(muse_wavecal_params_t));
744  pars->nifu = nifu;
745  int *rci = rcs + (nifu - 1);
746  *rci = muse_wavecal_compute(proc, pars);
747  if (rci && cpl_error_get_code() == MUSE_ERROR_CHIP_NOT_LIVE) {
748  *rci = 0;
749  }
750  cpl_free(pars);
751  #pragma omp critical(muse_processing_used_frames)
752  cpl_frameset_join(usedframes, proc->usedframes);
753  #pragma omp critical(muse_processing_output_frames)
754  cpl_frameset_join(outframes, proc->outframes);
756  } /* for nifu */
757  /* non-parallel loop to propagate the "worst" return code; *
758  * since we only ever return -1, any non-zero code is propagated */
759  for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
760  if (rcs[nifu-1] != 0) {
761  rc = rcs[nifu-1];
762  } /* if */
763  } /* for nifu */
764  cpl_free(rcs);
765  } else { /* serial processing */
766  for (params.nifu = 1; params.nifu <= kMuseNumIFUs && !rc; params.nifu++) {
767  muse_processing *proc = muse_processing_new("muse_wavecal",
768  recipe);
769  rc = muse_wavecal_compute(proc, &params);
770  if (rc && cpl_error_get_code() == MUSE_ERROR_CHIP_NOT_LIVE) {
771  rc = 0;
772  }
773  cpl_frameset_join(usedframes, proc->usedframes);
774  cpl_frameset_join(outframes, proc->outframes);
776  } /* for nifu */
777  } /* else */
778  UNUSED_ARGUMENT(donotmerge); /* maybe this is not going to be used below */
779 
780  if (!cpl_errorstate_is_equal(prestate)) {
781  /* dump all errors from this recipe in chronological order */
782  cpl_errorstate_dump(prestate, CPL_FALSE, muse_cplerrorstate_dump_some);
783  /* reset message level to not get the same errors displayed again by esorex */
784  cpl_msg_set_level(CPL_MSG_INFO);
785  }
786  /* clean up duplicates in framesets of used and output frames */
789 
790  /* merge output products from the up to 24 IFUs */
791  if (params.merge && !donotmerge) {
792  muse_utils_frameset_merge_frames(outframes);
793  }
794 
795  /* to get esorex to see our classification (frame groups etc.), *
796  * replace the original frameset with the list of used frames *
797  * before appending product output frames */
798  /* keep the same pointer, so just erase all frames, not delete the frameset */
799  muse_cplframeset_erase_all(recipe->frames);
800  cpl_frameset_join(recipe->frames, usedframes);
801  cpl_frameset_join(recipe->frames, outframes);
802  cpl_frameset_delete(usedframes);
803  cpl_frameset_delete(outframes);
804  return rc;
805 } /* muse_wavecal_exec() */
806 
807 /*----------------------------------------------------------------------------*/
814 /*----------------------------------------------------------------------------*/
815 static int
816 muse_wavecal_destroy(cpl_plugin *aPlugin)
817 {
818  /* Get the recipe from the plugin */
819  cpl_recipe *recipe;
820  if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
821  recipe = (cpl_recipe *)aPlugin;
822  } else {
823  return -1;
824  }
825 
826  /* Clean up */
827  cpl_parameterlist_delete(recipe->parameters);
829  return 0;
830 } /* muse_wavecal_destroy() */
831 
832 /*----------------------------------------------------------------------------*/
842 /*----------------------------------------------------------------------------*/
843 int
844 cpl_plugin_get_info(cpl_pluginlist *aList)
845 {
846  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
847  cpl_plugin *plugin = &recipe->interface;
848 
849  char *helptext;
851  helptext = cpl_sprintf("%s%s", muse_wavecal_help,
852  muse_wavecal_help_esorex);
853  } else {
854  helptext = cpl_sprintf("%s", muse_wavecal_help);
855  }
856 
857  /* Initialize the CPL plugin stuff for this module */
858  cpl_plugin_init(plugin, CPL_PLUGIN_API, MUSE_BINARY_VERSION,
859  CPL_PLUGIN_TYPE_RECIPE,
860  "muse_wavecal",
861  "Detect arc emission lines and determine the wavelength solution for each slice.",
862  helptext,
863  "Peter Weilbacher",
864  "usd-help@eso.org",
866  muse_wavecal_create,
867  muse_wavecal_exec,
868  muse_wavecal_destroy);
869  cpl_pluginlist_append(aList, plugin);
870  cpl_free(helptext);
871 
872  return 0;
873 } /* cpl_plugin_get_info() */
874 
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 region of the CCD that are ignored when com...
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:84
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)
const char * fitweighting_s
Type of weighting to use in the final polynomial wavelength solution fit, using centroiding error est...
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.