FORS Pipeline Reference Manual  5.0.9
fors_pmos_calib.c
1 /* $Id: fors_pmos_calib.c,v 1.42 2013-10-09 15:59:38 cgarcia Exp $
2  *
3  * This file is part of the FORS Data Reduction Pipeline
4  * Copyright (C) 2002-2010 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * $Author: cgarcia $
23  * $Date: 2013-10-09 15:59:38 $
24  * $Revision: 1.42 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <math.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <cpl.h>
36 #include <moses.h>
37 #include <fors_stack.h>
38 #include <fors_dfs.h>
39 #include <fors_header.h>
40 
41 #define OFFSET 50
42 #define TOLERANCE 10
43 
44 static int fors_pmos_calib_create(cpl_plugin *);
45 static int fors_pmos_calib_exec(cpl_plugin *);
46 static int fors_pmos_calib_destroy(cpl_plugin *);
47 static int fors_pmos_calib(cpl_parameterlist *, cpl_frameset *);
48 
49 static char fors_pmos_calib_description[] =
50 "This recipe is used to identify reference lines on PMOS arc lamp\n"
51 "exposures, and trace the spectral edges on the corresponding flat field\n"
52 "exposures. This information is used to determine the spectral extraction\n"
53 "mask to be applied in the scientific data reduction, performed with the\n"
54 "recipe fors_science.\n"
55 "This recipe accepts both FORS1 and FORS2 frames. The input arc lamps and\n"
56 "flat field exposures are assumed to be obtained quasi-simultaneously, so\n"
57 "that they would be described by exactly the same instrument distortions.\n"
58 "A line catalog must be specified, containing the wavelengths of the\n"
59 "reference arc lamp lines used for the wavelength calibration. A grism\n"
60 "table (typically depending on the instrument mode, and in particular on\n"
61 "the grism used) may also be specified: this table contains a default\n"
62 "recipe parameter setting to control the way spectra are extracted for\n"
63 "a specific instrument mode, as it is used for automatic run of the\n"
64 "pipeline on Paranal and in Garching. If this table is specified, it\n"
65 "will modify the default recipe parameter setting, with the exception of\n"
66 "those parameters which have been explicitly modifyed on the command line.\n"
67 "If a grism table is not specified, the input recipe parameters values\n"
68 "will always be read from the command line, or from an esorex configuration\n"
69 "file if present, or from their generic default values (that are rarely\n"
70 "meaningful). Finally a master bias frame must be input to this recipe.\n"
71 "The products SPECTRA_DETECTION_PMOS, SLIT_MAP_PMOS, and DISP_RESIDUALS_PMOS,\n"
72 "are just created if the --check parameter is set to true.\n"
73 "The MASTER_DISTORTION_TABLE is marked as required, but it is not so if all\n"
74 "slits have different offsets, and in the case of FORS1 observations made\n"
75 "with the old TK2048EB4-1 1604 chip read in windowed mode (2048x400)\n\n"
76 "Input files:\n\n"
77 " DO category: Type: Explanation: Required:\n"
78 " SCREEN_FLAT_PMOS Raw Flat field exposures Y\n"
79 " LAMP_PMOS Raw Arc lamp exposure Y\n"
80 " MASTER_BIAS or BIAS Calib Bias frame Y\n"
81 " MASTER_LINECAT Calib Line catalog Y\n"
82 " GRISM_TABLE Calib Grism table .\n"
83 " MASTER_DISTORTION_TABLE Calib Master distortions table Y\n\n"
84 "Output files:\n\n"
85 " DO category: Data type: Explanation:\n"
86 " MASTER_SCREEN_FLAT_PMOS FITS image Combined (sum) flat field\n"
87 " MASTER_NORM_FLAT_PMOS FITS image Normalised flat field\n"
88 " MAPPED_SCREEN_FLAT_PMOS FITS image Wavelength calibrated flat field\n"
89 " MAPPED_NORM_FLAT_PMOS FITS image Wavelength calibrated normalised flat\n"
90 " REDUCED_LAMP_PMOS FITS image Wavelength calibrated arc spectrum\n"
91 " DISP_COEFF_PMOS FITS table Inverse dispersion coefficients\n"
92 " DISP_RESIDUALS_PMOS FITS image Residuals in wavelength calibration\n"
93 " DISP_RESIDUALS_TABLE_PMOS FITS table Residuals in wavelength calibration\n"
94 " DELTA_IMAGE_PMOS FITS image Offset vs linear wavelength calib\n"
95 " WAVELENGTH_MAP_PMOS FITS image Wavelength for each pixel on CCD\n"
96 " SPECTRA_DETECTION_PMOS FITS image Check for preliminary detection\n"
97 " SLIT_MAP_PMOS FITS image Map of central wavelength on CCD\n"
98 " CURV_TRACES_PMOS FITS table Spectral curvature traces\n"
99 " CURV_COEFF_PMOS FITS table Spectral curvature coefficients\n"
100 " SPATIAL_MAP_PMOS FITS image Spatial position along slit on CCD\n"
101 " SPECTRAL_RESOLUTION_PMOS FITS table Resolution at reference arc lines\n"
102 " SLIT_LOCATION_PMOS FITS table Slits on product frames and CCD\n\n";
103 
104 #define fors_pmos_calib_exit(message) \
105 { \
106 if ((const char *)message != NULL) cpl_msg_error(recipe, message); \
107 cpl_free(instrume); \
108 cpl_free(fiterror); \
109 cpl_free(fitlines); \
110 cpl_image_delete(bias); \
111 cpl_image_delete(master_bias); \
112 cpl_image_delete(coordinate); \
113 cpl_image_delete(checkwave); \
114 cpl_image_delete(flat); \
115 cpl_image_delete(master_flat); \
116 cpl_image_delete(added_flat); \
117 cpl_image_delete(norm_flat); \
118 cpl_image_delete(mapped_flat); \
119 cpl_image_delete(mapped_nflat); \
120 cpl_image_delete(rainbow); \
121 cpl_image_delete(rectified); \
122 cpl_image_delete(residual); \
123 cpl_image_delete(smo_flat); \
124 cpl_image_delete(spatial); \
125 cpl_image_delete(spectra); \
126 cpl_image_delete(wavemap); \
127 cpl_image_delete(delta); \
128 cpl_image_delete(rect_flat); \
129 cpl_image_delete(rect_nflat); \
130 cpl_mask_delete(refmask); \
131 cpl_propertylist_delete(header); \
132 cpl_propertylist_delete(save_header); \
133 cpl_propertylist_delete(qclist); \
134 cpl_table_delete(grism_table); \
135 cpl_table_delete(idscoeff); \
136 cpl_table_delete(idscoeff_all); \
137 cpl_table_delete(restable); \
138 cpl_table_delete(maskslits); \
139 cpl_table_delete(overscans); \
140 cpl_table_delete(traces); \
141 cpl_table_delete(polytraces); \
142 cpl_table_delete(slits); \
143 cpl_table_delete(restab); \
144 cpl_table_delete(global); \
145 cpl_table_delete(wavelengths); \
146 cpl_vector_delete(lines); \
147 cpl_msg_indent_less(); \
148 return -1; \
149 }
150 
151 #define fors_pmos_calib_exit_memcheck(message) \
152 { \
153 if ((const char *)message != NULL) cpl_msg_info(recipe, message); \
154 printf("free instrume (%p)\n", instrume); \
155 cpl_free(instrume); \
156 printf("free pipefile (%p)\n", pipefile); \
157 cpl_free(pipefile); \
158 printf("free fiterror (%p)\n", fiterror); \
159 cpl_free(fiterror); \
160 printf("free fitlines (%p)\n", fitlines); \
161 cpl_free(fitlines); \
162 printf("free bias (%p)\n", bias); \
163 cpl_image_delete(bias); \
164 printf("free master_bias (%p)\n", master_bias); \
165 cpl_image_delete(master_bias); \
166 printf("free coordinate (%p)\n", coordinate); \
167 cpl_image_delete(coordinate); \
168 printf("free checkwave (%p)\n", checkwave); \
169 cpl_image_delete(checkwave); \
170 printf("free flat (%p)\n", flat); \
171 cpl_image_delete(flat); \
172 printf("free master_flat (%p)\n", master_flat); \
173 cpl_image_delete(master_flat); \
174 printf("free norm_flat (%p)\n", norm_flat); \
175 cpl_image_delete(norm_flat); \
176 printf("free mapped_flat (%p)\n", mapped_flat); \
177 cpl_image_delete(mapped_flat); \
178 printf("free mapped_nflat (%p)\n", mapped_nflat); \
179 cpl_image_delete(mapped_nflat); \
180 printf("free rainbow (%p)\n", rainbow); \
181 cpl_image_delete(rainbow); \
182 printf("free rectified (%p)\n", rectified); \
183 cpl_image_delete(rectified); \
184 printf("free residual (%p)\n", residual); \
185 cpl_image_delete(residual); \
186 printf("free smo_flat (%p)\n", smo_flat); \
187 cpl_image_delete(smo_flat); \
188 printf("free spatial (%p)\n", spatial); \
189 cpl_image_delete(spatial); \
190 printf("free spectra (%p)\n", spectra); \
191 cpl_image_delete(spectra); \
192 printf("free wavemap (%p)\n", wavemap); \
193 cpl_image_delete(wavemap); \
194 printf("free delta (%p)\n", delta); \
195 cpl_image_delete(delta); \
196 printf("free rect_flat (%p)\n", rect_flat); \
197 cpl_image_delete(rect_flat); \
198 printf("free rect_nflat (%p)\n", rect_nflat); \
199 cpl_image_delete(rect_nflat); \
200 printf("free refmask (%p)\n", refmask); \
201 cpl_mask_delete(refmask); \
202 printf("free header (%p)\n", header); \
203 cpl_propertylist_delete(header); \
204 printf("free save_header (%p)\n", save_header); \
205 cpl_propertylist_delete(save_header); \
206 printf("free qclist (%p)\n", qclist); \
207 cpl_propertylist_delete(qclist); \
208 printf("free grism_table (%p)\n", grism_table); \
209 cpl_table_delete(grism_table); \
210 printf("free idscoeff (%p)\n", idscoeff); \
211 cpl_table_delete(idscoeff); \
212 printf("free idscoeff_all (%p)\n", idscoeff_all); \
213 cpl_table_delete(idscoeff_all); \
214 printf("free restable (%p)\n", restable); \
215 cpl_table_delete(restable); \
216 printf("free maskslits (%p)\n", maskslits); \
217 cpl_table_delete(maskslits); \
218 printf("free overscans (%p)\n", overscans); \
219 cpl_table_delete(overscans); \
220 printf("free traces (%p)\n", traces); \
221 cpl_table_delete(traces); \
222 printf("free polytraces (%p)\n", polytraces); \
223 cpl_table_delete(polytraces); \
224 printf("free slits (%p)\n", slits); \
225 cpl_table_delete(slits); \
226 printf("free restab (%p)\n", restab); \
227 cpl_table_delete(restab); \
228 printf("free global (%p)\n", global); \
229 cpl_table_delete(global); \
230 printf("free wavelengths (%p)\n", wavelengths); \
231 cpl_table_delete(wavelengths); \
232 printf("free lines (%p)\n", lines); \
233 cpl_vector_delete(lines); \
234 cpl_msg_indent_less(); \
235 return 0; \
236 }
237 
238 
250 int cpl_plugin_get_info(cpl_pluginlist *list)
251 {
252  cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
253  cpl_plugin *plugin = &recipe->interface;
254 
255  cpl_plugin_init(plugin,
256  CPL_PLUGIN_API,
257  FORS_BINARY_VERSION,
258  CPL_PLUGIN_TYPE_RECIPE,
259  "fors_pmos_calib",
260  "Determination of the extraction mask",
261  fors_pmos_calib_description,
262  "Carlo Izzo",
263  PACKAGE_BUGREPORT,
264  "This file is currently part of the FORS Instrument Pipeline\n"
265  "Copyright (C) 2002-2010 European Southern Observatory\n\n"
266  "This program is free software; you can redistribute it and/or modify\n"
267  "it under the terms of the GNU General Public License as published by\n"
268  "the Free Software Foundation; either version 2 of the License, or\n"
269  "(at your option) any later version.\n\n"
270  "This program is distributed in the hope that it will be useful,\n"
271  "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
272  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
273  "GNU General Public License for more details.\n\n"
274  "You should have received a copy of the GNU General Public License\n"
275  "along with this program; if not, write to the Free Software Foundation,\n"
276  "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n",
277  fors_pmos_calib_create,
278  fors_pmos_calib_exec,
279  fors_pmos_calib_destroy);
280 
281  cpl_pluginlist_append(list, plugin);
282 
283  return 0;
284 }
285 
286 
297 static int fors_pmos_calib_create(cpl_plugin *plugin)
298 {
299  cpl_recipe *recipe;
300  cpl_parameter *p;
301 
302 
303  /*
304  * Check that the plugin is part of a valid recipe
305  */
306 
307  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
308  recipe = (cpl_recipe *)plugin;
309  else
310  return -1;
311 
312  /*
313  * Create the parameters list in the cpl_recipe object
314  */
315 
316  recipe->parameters = cpl_parameterlist_new();
317 
318 
319  /*
320  * Dispersion
321  */
322 
323  p = cpl_parameter_new_value("fors.fors_pmos_calib.dispersion",
324  CPL_TYPE_DOUBLE,
325  "Expected spectral dispersion (Angstrom/pixel)",
326  "fors.fors_pmos_calib",
327  0.0);
328  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
329  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
330  cpl_parameterlist_append(recipe->parameters, p);
331 
332  /*
333  * Peak detection level
334  */
335 
336  p = cpl_parameter_new_value("fors.fors_pmos_calib.peakdetection",
337  CPL_TYPE_DOUBLE,
338  "Initial peak detection threshold (ADU)",
339  "fors.fors_pmos_calib",
340  0.0);
341  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "peakdetection");
342  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
343  cpl_parameterlist_append(recipe->parameters, p);
344 
345  /*
346  * Degree of wavelength calibration polynomial
347  */
348 
349  p = cpl_parameter_new_value("fors.fors_pmos_calib.wdegree",
350  CPL_TYPE_INT,
351  "Degree of wavelength calibration polynomial",
352  "fors.fors_pmos_calib",
353  0);
354  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wdegree");
355  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
356  cpl_parameterlist_append(recipe->parameters, p);
357 
358  /*
359  * Reference lines search radius
360  */
361 
362  p = cpl_parameter_new_value("fors.fors_pmos_calib.wradius",
363  CPL_TYPE_INT,
364  "Search radius if iterating pattern-matching "
365  "with first-guess method",
366  "fors.fors_pmos_calib",
367  4);
368  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wradius");
369  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
370  cpl_parameterlist_append(recipe->parameters, p);
371 
372  /*
373  * Rejection threshold in dispersion relation polynomial fitting
374  */
375 
376  p = cpl_parameter_new_value("fors.fors_pmos_calib.wreject",
377  CPL_TYPE_DOUBLE,
378  "Rejection threshold in dispersion "
379  "relation fit (pixel)",
380  "fors.fors_pmos_calib",
381  0.7);
382  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wreject");
383  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
384  cpl_parameterlist_append(recipe->parameters, p);
385 
386  /*
387  * Line catalog table column containing the reference wavelengths
388  */
389 
390  p = cpl_parameter_new_value("fors.fors_pmos_calib.wcolumn",
391  CPL_TYPE_STRING,
392  "Name of line catalog table column "
393  "with wavelengths",
394  "fors.fors_pmos_calib",
395  "WLEN");
396  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
397  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
398  cpl_parameterlist_append(recipe->parameters, p);
399 
400  /*
401  * Degree of spectral curvature polynomial
402  */
403 
404  p = cpl_parameter_new_value("fors.fors_pmos_calib.cdegree",
405  CPL_TYPE_INT,
406  "Degree of spectral curvature polynomial",
407  "fors.fors_pmos_calib",
408  0);
409  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cdegree");
410  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
411  cpl_parameterlist_append(recipe->parameters, p);
412 
413  /*
414  * Curvature solution interpolation
415  */
416 
417  p = cpl_parameter_new_value("fors.fors_pmos_calib.cmode",
418  CPL_TYPE_INT,
419  "Interpolation mode of curvature solution "
420  "(0 = no "
421  "interpolation, 1 = fill gaps, 2 = global "
422  "model)",
423  "fors.fors_pmos_calib",
424  1);
425  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cmode");
426  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
427  cpl_parameterlist_append(recipe->parameters, p);
428 
429  /*
430  * Start wavelength for spectral extraction
431  */
432 
433  p = cpl_parameter_new_value("fors.fors_pmos_calib.startwavelength",
434  CPL_TYPE_DOUBLE,
435  "Start wavelength in spectral extraction",
436  "fors.fors_pmos_calib",
437  0.0);
438  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
439  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
440  cpl_parameterlist_append(recipe->parameters, p);
441 
442  /*
443  * End wavelength for spectral extraction
444  */
445 
446  p = cpl_parameter_new_value("fors.fors_pmos_calib.endwavelength",
447  CPL_TYPE_DOUBLE,
448  "End wavelength in spectral extraction",
449  "fors.fors_pmos_calib",
450  0.0);
451  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
452  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
453  cpl_parameterlist_append(recipe->parameters, p);
454 
455  /*
456  * Flat field frames stack parameters
457  */
458 
459  fors_stack_define_parameters(recipe->parameters, "fors.fors_pmos_calib",
460  "average");
461 
462  /*
463  * Degree of flat field fitting polynomial along dispersion direction
464  */
465 
466  p = cpl_parameter_new_value("fors.fors_pmos_calib.ddegree",
467  CPL_TYPE_INT,
468  "Degree of flat field fitting polynomial "
469  "along dispersion direction",
470  "fors.fors_pmos_calib",
471  -1);
472  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ddegree");
473  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
474  cpl_parameterlist_append(recipe->parameters, p);
475 
476  /*
477  * Smooth box radius for flat field along dispersion direction
478  */
479 
480  p = cpl_parameter_new_value("fors.fors_pmos_calib.dradius",
481  CPL_TYPE_INT,
482  "Smooth box radius for flat field along "
483  "dispersion direction",
484  "fors.fors_pmos_calib",
485  10);
486  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dradius");
487  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
488  cpl_parameterlist_append(recipe->parameters, p);
489 
490  /*
491  * Computation of QC1 parameters
492  */
493 
494  p = cpl_parameter_new_value("fors.fors_pmos_calib.qc",
495  CPL_TYPE_BOOL,
496  "Compute QC1 parameters",
497  "fors.fors_pmos_calib",
498  TRUE);
499  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "qc");
500  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
501  cpl_parameterlist_append(recipe->parameters, p);
502 
503  /*
504  * Create check products
505  */
506 
507  p = cpl_parameter_new_value("fors.fors_pmos_calib.check",
508  CPL_TYPE_BOOL,
509  "Create intermediate products",
510  "fors.fors_pmos_calib",
511  FALSE);
512  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "check");
513  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
514  cpl_parameterlist_append(recipe->parameters, p);
515 
516  return 0;
517 }
518 
519 
528 static int fors_pmos_calib_exec(cpl_plugin *plugin)
529 {
530  cpl_recipe *recipe;
531 
532  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
533  recipe = (cpl_recipe *)plugin;
534  else
535  return -1;
536 
537  return fors_pmos_calib(recipe->parameters, recipe->frames);
538 }
539 
540 
549 static int fors_pmos_calib_destroy(cpl_plugin *plugin)
550 {
551  cpl_recipe *recipe;
552 
553  if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
554  recipe = (cpl_recipe *)plugin;
555  else
556  return -1;
557 
558  cpl_parameterlist_delete(recipe->parameters);
559 
560  return 0;
561 }
562 
563 
573 static int fors_pmos_calib(cpl_parameterlist *parlist, cpl_frameset *frameset)
574 {
575 
576  const char *recipe = "fors_pmos_calib";
577 
578 
579  /*
580  * Input parameters
581  */
582 
583  double dispersion;
584  double peakdetection;
585  int wdegree;
586  int wradius;
587  double wreject;
588  const char *wcolumn;
589  int cdegree;
590  int cmode;
591  double startwavelength;
592  double endwavelength;
593  int ddegree;
594  int dradius;
595  int qc;
596  int check;
597  const char *stack_method;
598  int min_reject;
599  int max_reject;
600  double klow;
601  double khigh;
602  int kiter;
603 
604 
605  /*
606  * CPL objects
607  */
608 
609  cpl_imagelist *biases = NULL;
610  cpl_image *bias = NULL;
611  cpl_image *master_bias = NULL;
612  cpl_image *multi_bias = NULL;
613  cpl_image *flat = NULL;
614  cpl_image *master_flat = NULL;
615  cpl_image *added_flat = NULL;
616  cpl_image *trace_flat = NULL;
617  cpl_image *smo_flat = NULL;
618  cpl_image *norm_flat = NULL;
619  cpl_image *spectra = NULL;
620  cpl_image *wavemap = NULL;
621  cpl_image *delta = NULL;
622  cpl_image *residual = NULL;
623  cpl_image *checkwave = NULL;
624  cpl_image *rectified = NULL;
625  cpl_image *dummy = NULL;
626  cpl_image *add_dummy = NULL;
627  cpl_image *refimage = NULL;
628  cpl_image *coordinate = NULL;
629  cpl_image *rainbow = NULL;
630  cpl_image *spatial = NULL;
631  cpl_image *rect_flat = NULL;
632  cpl_image *rect_nflat = NULL;
633  cpl_image *mapped_flat = NULL;
634  cpl_image *mapped_nflat = NULL;
635 
636  cpl_mask *refmask = NULL;
637 
638  cpl_table *grism_table = NULL;
639  cpl_table *overscans = NULL;
640  cpl_table *wavelengths = NULL;
641  cpl_table *idscoeff = NULL;
642  cpl_table *idscoeff_all = NULL;
643  cpl_table *restable = NULL;
644  cpl_table *slits = NULL;
645  cpl_table *positions = NULL;
646  cpl_table *maskslits = NULL;
647  cpl_table *traces = NULL;
648  cpl_table *polytraces = NULL;
649  cpl_table *restab = NULL;
650  cpl_table *global = NULL;
651 
652  cpl_vector *lines = NULL;
653 
654  cpl_propertylist *header_dist = NULL;
655  cpl_propertylist *header = NULL;
656  cpl_propertylist *save_header = NULL;
657  cpl_propertylist *qclist = NULL;
658 
659  /*
660  * Auxiliary variables
661  */
662 
663  char version[80];
664  const char *arc_tag;
665  const char *flat_tag;
666  const char *master_screen_flat_tag;
667  const char *master_norm_flat_tag;
668  const char *reduced_lamp_tag;
669  const char *disp_residuals_tag;
670  const char *disp_coeff_tag;
671  const char *wavelength_map_tag;
672  const char *spectra_detection_tag;
673  const char *spectral_resolution_tag;
674  const char *slit_map_tag;
675  const char *curv_traces_tag;
676  const char *curv_coeff_tag;
677  const char *spatial_map_tag;
678  const char *slit_location_tag;
679  const char *master_distortion_tag = "MASTER_DISTORTION_TABLE";
680  const char *disp_residuals_table_tag;
681  const char *delta_image_tag;
682  const char *mapped_screen_flat_tag;
683  const char *mapped_norm_flat_tag;
684  const char *keyname;
685  int pmos;
686  int same_offset = 0;
687  int nslits;
688  float *data;
689  double *xpos;
690  double mxpos;
691  double mean_rms;
692  double mean_rms_err;
693  double alltime;
694  int nflats;
695  int nbias;
696  int nlines;
697  int rebin, rebin_dist;
698  double *line;
699  double *fiterror = NULL;
700  int *fitlines = NULL;
701  int nx, ny;
702  double reference;
703  double gain;
704  int ccd_ysize;
705  int i, j;
706 
707  char *instrume = NULL;
708 
709  /*
710  * Variables just related to bagoo
711  */
712 
713  int bagoo = 0;
714  int doit = 0;
715  double blevel = 0.0;
716  double ron = 0.0;
717 
718  snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
719 
720  cpl_msg_set_indentation(2);
721 
722  fors_dfs_set_groups(frameset);
723 
724  /*
725  * Get configuration parameters
726  */
727 
728  cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
729  cpl_msg_indent_more();
730 
731  if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
732  fors_pmos_calib_exit("Too many in input: GRISM_TABLE");
733 
734  grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
735 
736  dispersion = dfs_get_parameter_double(parlist,
737  "fors.fors_pmos_calib.dispersion", grism_table);
738 
739  if (dispersion <= 0.0)
740  fors_pmos_calib_exit("Invalid spectral dispersion value");
741 
742  peakdetection = dfs_get_parameter_double(parlist,
743  "fors.fors_pmos_calib.peakdetection", grism_table);
744  if (peakdetection <= 0.0)
745  fors_pmos_calib_exit("Invalid peak detection level");
746 
747  wdegree = dfs_get_parameter_int(parlist,
748  "fors.fors_pmos_calib.wdegree", grism_table);
749 
750  if (wdegree < 1)
751  fors_pmos_calib_exit("Invalid polynomial degree");
752 
753  if (wdegree > 5)
754  fors_pmos_calib_exit("Max allowed polynomial degree is 5");
755 
756  wradius = dfs_get_parameter_int(parlist,
757  "fors.fors_pmos_calib.wradius", NULL);
758 
759  if (wradius < 0)
760  fors_pmos_calib_exit("Invalid search radius");
761 
762  wreject = dfs_get_parameter_double(parlist,
763  "fors.fors_pmos_calib.wreject", NULL);
764 
765  if (wreject <= 0.0)
766  fors_pmos_calib_exit("Invalid rejection threshold");
767 
768  wcolumn = dfs_get_parameter_string(parlist,
769  "fors.fors_pmos_calib.wcolumn", NULL);
770 
771  cdegree = dfs_get_parameter_int(parlist,
772  "fors.fors_pmos_calib.cdegree", grism_table);
773 
774  if (cdegree < 1)
775  fors_pmos_calib_exit("Invalid polynomial degree");
776 
777  if (cdegree > 5)
778  fors_pmos_calib_exit("Max allowed polynomial degree is 5");
779 
780  cmode = dfs_get_parameter_int(parlist, "fors.fors_pmos_calib.cmode", NULL);
781 
782  if (cmode < 0 || cmode > 2)
783  fors_pmos_calib_exit("Invalid curvature solution interpolation mode");
784 
785  startwavelength = dfs_get_parameter_double(parlist,
786  "fors.fors_pmos_calib.startwavelength", grism_table);
787  if (startwavelength > 1.0)
788  if (startwavelength < 3000.0 || startwavelength > 13000.0)
789  fors_pmos_calib_exit("Invalid wavelength");
790 
791  endwavelength = dfs_get_parameter_double(parlist,
792  "fors.fors_pmos_calib.endwavelength", grism_table);
793  if (endwavelength > 1.0) {
794  if (endwavelength < 3000.0 || endwavelength > 13000.0)
795  fors_pmos_calib_exit("Invalid wavelength");
796  if (startwavelength < 1.0)
797  fors_pmos_calib_exit("Invalid wavelength interval");
798  }
799 
800  if (startwavelength > 1.0)
801  if (endwavelength - startwavelength <= 0.0)
802  fors_pmos_calib_exit("Invalid wavelength interval");
803 
804  stack_method = dfs_get_parameter_string(parlist,
805  "fors.fors_pmos_calib.stack_method",
806  NULL);
807 
808  if (strcmp(stack_method, "minmax") == 0) {
809  min_reject = dfs_get_parameter_int(parlist,
810  "fors.fors_pmos_calib.minrejection", NULL);
811  if (min_reject < 0)
812  fors_pmos_calib_exit("Invalid number of lower rejections");
813 
814  max_reject = dfs_get_parameter_int(parlist,
815  "fors.fors_pmos_calib.maxrejection", NULL);
816  if (max_reject < 0)
817  fors_pmos_calib_exit("Invalid number of upper rejections");
818  }
819 
820  if (strcmp(stack_method, "ksigma") == 0) {
821  klow = dfs_get_parameter_double(parlist,
822  "fors.fors_pmos_calib.klow", NULL);
823  if (klow < 0.1)
824  fors_pmos_calib_exit("Invalid lower K-sigma");
825 
826  khigh = dfs_get_parameter_double(parlist,
827  "fors.fors_pmos_calib.khigh", NULL);
828  if (khigh < 0.1)
829  fors_pmos_calib_exit("Invalid lower K-sigma");
830 
831  kiter = dfs_get_parameter_int(parlist,
832  "fors.fors_pmos_calib.kiter", NULL);
833  if (kiter < 1)
834  fors_pmos_calib_exit("Invalid number of iterations");
835  }
836 
837  ddegree = dfs_get_parameter_int(parlist,
838  "fors.fors_pmos_calib.ddegree", NULL);
839  dradius = dfs_get_parameter_int(parlist,
840  "fors.fors_pmos_calib.dradius", NULL);
841 
842  if (dradius < 1)
843  fors_pmos_calib_exit("Invalid smoothing box radius");
844 
845  qc = dfs_get_parameter_bool(parlist, "fors.fors_pmos_calib.qc", NULL);
846 
847  check = dfs_get_parameter_bool(parlist, "fors.fors_pmos_calib.check", NULL);
848 
849  cpl_table_delete(grism_table); grism_table = NULL;
850 
851  if (cpl_error_get_code())
852  fors_pmos_calib_exit("Failure getting the configuration parameters");
853 
854 
855  /*
856  * Check input set-of-frames
857  */
858 
859  cpl_msg_indent_less();
860  cpl_msg_info(recipe, "Check input set-of-frames:");
861  cpl_msg_indent_more();
862 
863  {
864  cpl_frameset *subframeset = cpl_frameset_duplicate(frameset);
865  cpl_frameset_erase(subframeset, "BIAS");
866  cpl_frameset_erase(subframeset, "MASTER_BIAS");
867 
868  if (!dfs_equal_keyword(subframeset, "ESO INS GRIS1 ID"))
869  cpl_msg_warning(cpl_func,"Input frames are not from the same grism");
870 
871  if (!dfs_equal_keyword(subframeset, "ESO INS FILT1 ID"))
872  cpl_msg_warning(cpl_func,"Input frames are not from the same filter");
873 
874  if (!dfs_equal_keyword(subframeset, "ESO DET CHIP1 ID"))
875  cpl_msg_warning(cpl_func,"Input frames are not from the same chip");
876 
877  cpl_frameset_delete(subframeset);
878  }
879 
880  pmos = cpl_frameset_count_tags(frameset, "LAMP_PMOS");
881 
882  if (pmos == 0)
883  fors_pmos_calib_exit("Missing input arc lamp frame");
884 
885  if (pmos) {
886  cpl_msg_info(recipe, "PMOS data found");
887  arc_tag = "LAMP_PMOS";
888  flat_tag = "SCREEN_FLAT_PMOS";
889  master_screen_flat_tag = "MASTER_SCREEN_FLAT_PMOS";
890  master_norm_flat_tag = "MASTER_NORM_FLAT_PMOS";
891  reduced_lamp_tag = "REDUCED_LAMP_PMOS";
892  disp_residuals_tag = "DISP_RESIDUALS_PMOS";
893  disp_coeff_tag = "DISP_COEFF_PMOS";
894  wavelength_map_tag = "WAVELENGTH_MAP_PMOS";
895  spectra_detection_tag = "SPECTRA_DETECTION_PMOS";
896  spectral_resolution_tag = "SPECTRAL_RESOLUTION_PMOS";
897  slit_map_tag = "SLIT_MAP_PMOS";
898  curv_traces_tag = "CURV_TRACES_PMOS";
899  curv_coeff_tag = "CURV_COEFF_PMOS";
900  spatial_map_tag = "SPATIAL_MAP_PMOS";
901  slit_location_tag = "SLIT_LOCATION_PMOS";
902  disp_residuals_table_tag = "DISP_RESIDUALS_TABLE_PMOS";
903  delta_image_tag = "DELTA_IMAGE_PMOS";
904  mapped_screen_flat_tag = "MAPPED_SCREEN_FLAT_PMOS";
905  mapped_norm_flat_tag = "MAPPED_NORM_FLAT_PMOS";
906  }
907 
908  nbias = 0;
909  if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0) {
910  if (cpl_frameset_count_tags(frameset, "BIAS") == 0)
911  fors_pmos_calib_exit("Missing required input: MASTER_BIAS or BIAS");
912  nbias = cpl_frameset_count_tags(frameset, "BIAS");
913  }
914 
915  if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
916  fors_pmos_calib_exit("Too many in input: MASTER_BIAS");
917 
918  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
919  fors_pmos_calib_exit("Missing required input: MASTER_LINECAT");
920 
921  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
922  fors_pmos_calib_exit("Too many in input: MASTER_LINECAT");
923 
924  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
925  fors_pmos_calib_exit("Missing required input: MASTER_LINECAT");
926 
927  if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
928  fors_pmos_calib_exit("Too many in input: MASTER_LINECAT");
929 
930 /*
931  if (cpl_frameset_count_tags(frameset, master_distortion_tag) == 0)
932  fors_pmos_calib_exit("Missing required input: MASTER_DISTORTION_TABLE");
933 */
934 
935  if (cpl_frameset_count_tags(frameset, master_distortion_tag) > 1)
936  fors_pmos_calib_exit("Too many in input: MASTER_DISTORTION_TABLE");
937 
938  nflats = cpl_frameset_count_tags(frameset, flat_tag);
939 
940  if (nflats < 1) {
941  cpl_msg_error(recipe, "Missing required input: %s", flat_tag);
942  fors_pmos_calib_exit(NULL);
943  }
944 
945  cpl_msg_indent_less();
946 
947  if (nflats > 1)
948  cpl_msg_info(recipe, "Load %d flat field frames and stack them "
949  "with method \"%s\"", nflats, stack_method);
950  else
951  cpl_msg_info(recipe, "Load flat field exposure...");
952 
953  cpl_msg_indent_more();
954 
955  header = dfs_load_header(frameset, flat_tag, 0);
956 
957  if (header == NULL)
958  fors_pmos_calib_exit("Cannot load flat field frame header");
959 
960  alltime = cpl_propertylist_get_double(header, "EXPTIME");
961 
962  if (cpl_error_get_code() != CPL_ERROR_NONE)
963  fors_pmos_calib_exit("Missing keyword EXPTIME in flat field "
964  "frame header");
965 
966  cpl_propertylist_delete(header);
967 
968  for (i = 1; i < nflats; i++) {
969 
970  header = dfs_load_header(frameset, NULL, 0);
971 
972  if (header == NULL)
973  fors_pmos_calib_exit("Cannot load flat field frame header");
974 
975  alltime += cpl_propertylist_get_double(header, "EXPTIME");
976 
977  if (cpl_error_get_code() != CPL_ERROR_NONE)
978  fors_pmos_calib_exit("Missing keyword EXPTIME in flat field "
979  "frame header");
980 
981  cpl_propertylist_delete(header);
982 
983  }
984 
985  if (bagoo) {
986  char *montecarlo = getenv("MONTECARLO");
987 
988  if (montecarlo)
989  doit = atoi(montecarlo);
990 
991  if (doit) {
992  master_bias = dfs_load_image(frameset, "MASTER_BIAS",
993  CPL_TYPE_FLOAT, 0, 1);
994  if (master_bias == NULL)
995  fors_pmos_calib_exit("Cannot load master bias");
996 
997  blevel = cpl_image_get_mean(master_bias);
998 
999  cpl_image_delete(master_bias);
1000  }
1001  }
1002 
1003  master_flat = dfs_load_image(frameset, flat_tag, CPL_TYPE_FLOAT, 0, 0);
1004 
1005  if (master_flat == NULL)
1006  fors_pmos_calib_exit("Cannot load flat field");
1007 
1008  if (doit) {
1009  header = dfs_load_header(frameset, flat_tag, 0);
1010 
1011  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
1012 
1013  if (cpl_error_get_code() != CPL_ERROR_NONE)
1014  fors_pmos_calib_exit("Missing keyword ESO DET OUT1 CONAD "
1015  "in flat field frame header");
1016 
1017  ron = cpl_propertylist_get_double(header, "ESO DET OUT1 RON");
1018 
1019  if (cpl_error_get_code() != CPL_ERROR_NONE)
1020  fors_pmos_calib_exit("Missing keyword ESO DET OUT1 RON "
1021  "in flat field frame header");
1022 
1023  cpl_propertylist_delete(header);
1024 
1025  ron /= gain; // RON converted from electrons to ADU
1026 
1027  mos_randomise_image(master_flat, ron, gain, blevel);
1028  }
1029 
1030  ny = cpl_image_get_size_y(master_flat);
1031 
1032  if (nflats > 1) {
1033  if (strcmp(stack_method, "average") == 0) {
1034  for (i = 1; i < nflats; i++) {
1035  flat = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
1036  if (flat) {
1037  if (doit) {
1038  mos_randomise_image(flat, ron, gain, blevel);
1039  }
1040  cpl_image_add(master_flat, flat);
1041  cpl_image_delete(flat); flat = NULL;
1042  }
1043  else
1044  fors_pmos_calib_exit("Cannot load flat field");
1045  }
1046 
1047  /***
1048  if (nflats > 1)
1049  cpl_image_divide_scalar(master_flat, nflats);
1050  ***/
1051 
1052  }
1053  else {
1054  cpl_imagelist *flatlist = NULL;
1055  double rflux, flux;
1056 
1057  added_flat = cpl_image_duplicate(master_flat);
1058 
1059  flatlist = cpl_imagelist_new();
1060  cpl_imagelist_set(flatlist, master_flat,
1061  cpl_imagelist_get_size(flatlist));
1062 
1063  /*
1064  * Stacking with rejection requires normalization
1065  * at the same flux. We normalise according to mean
1066  * flux. This is equivalent to determining the
1067  * flux ratio for each image as the average of the
1068  * flux ratio of all pixels weighted on the actual
1069  * flux of each pixel.
1070  */
1071 
1072  rflux = cpl_image_get_mean(master_flat);
1073 
1074  for (i = 1; i < nflats; i++) {
1075  flat = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
1076  if (flat) {
1077  if (doit) {
1078  mos_randomise_image(flat, ron, gain, blevel);
1079  }
1080  cpl_image_add(added_flat, flat);
1081  flux = cpl_image_get_mean(flat);
1082  cpl_image_multiply_scalar(flat, rflux / flux);
1083  cpl_imagelist_set(flatlist, flat,
1084  cpl_imagelist_get_size(flatlist));
1085  }
1086  else {
1087  fors_pmos_calib_exit("Cannot load flat field");
1088  }
1089  }
1090 
1091  if (strcmp(stack_method, "median") == 0) {
1092  master_flat = cpl_imagelist_collapse_median_create(flatlist);
1093  }
1094 
1095  if (strcmp(stack_method, "minmax") == 0) {
1096  master_flat = cpl_imagelist_collapse_minmax_create(flatlist,
1097  min_reject,
1098  max_reject);
1099  }
1100 
1101  if (strcmp(stack_method, "ksigma") == 0) {
1102  master_flat = mos_ksigma_stack(flatlist,
1103  klow, khigh, kiter, NULL);
1104  }
1105  }
1106  }
1107 
1108 
1109  /*
1110  * Get the reference wavelength and the rebin factor along the
1111  * dispersion direction from the arc lamp exposure
1112  */
1113 
1114  header = dfs_load_header(frameset, arc_tag, 0);
1115 
1116  if (header == NULL)
1117  fors_pmos_calib_exit("Cannot load arc lamp header");
1118 
1119  instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
1120  if (instrume == NULL)
1121  fors_pmos_calib_exit("Missing keyword INSTRUME in arc lamp header");
1122 
1123  instrume = cpl_strdup(instrume);
1124 
1125  if (instrume[4] == '1')
1126  snprintf(version, 80, "%s/%s", "fors1", VERSION);
1127  if (instrume[4] == '2')
1128  snprintf(version, 80, "%s/%s", "fors2", VERSION);
1129 
1130  reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
1131 
1132  if (cpl_error_get_code() != CPL_ERROR_NONE)
1133  fors_pmos_calib_exit("Missing keyword ESO INS GRIS1 WLEN in arc lamp "
1134  "frame header");
1135 
1136  if (reference < 3000.0) /* Perhaps in nanometers... */
1137  reference *= 10;
1138 
1139  if (reference < 3000.0 || reference > 13000.0) {
1140  cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
1141  "keyword ESO INS GRIS1 WLEN in arc lamp frame header",
1142  reference);
1143  fors_pmos_calib_exit(NULL);
1144  }
1145 
1146  cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
1147 
1148  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
1149 
1150  if (cpl_error_get_code() != CPL_ERROR_NONE)
1151  fors_pmos_calib_exit("Missing keyword ESO DET WIN1 BINX in arc lamp "
1152  "frame header");
1153 
1154  if (rebin != 1) {
1155  dispersion *= rebin;
1156  cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
1157  "working dispersion used is %f A/pixel", rebin,
1158  dispersion);
1159  }
1160 
1161  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
1162 
1163  if (cpl_error_get_code() != CPL_ERROR_NONE)
1164  fors_pmos_calib_exit("Missing keyword ESO DET OUT1 CONAD in arc lamp "
1165  "frame header");
1166 
1167  cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
1168 
1169  if (pmos) {
1170  int nslits_out_det;
1171  cpl_msg_info(recipe, "Produce mask slit position table...");
1172 
1173  maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
1174 
1175  /*
1176  * Check if all slits have the same X offset: in such case,
1177  * treat the observation as a long-slit one!
1178  */
1179 
1180  mxpos = cpl_table_get_column_median(maskslits, "xtop");
1181  xpos = cpl_table_get_data_double(maskslits, "xtop");
1182  nslits = cpl_table_get_nrow(maskslits);
1183 
1184  same_offset = 1;
1185  for (i = 0; i < nslits; i++) {
1186  if (fabs(mxpos-xpos[i]) > 0.01) {
1187  same_offset = 0;
1188  break;
1189  }
1190  }
1191  //If not all the slits are illuminated, then we cannot say that
1192  //all have the same offsets.
1193  if(nslits_out_det != 0)
1194  same_offset = 0;
1195 
1196  if (same_offset) {
1197  cpl_msg_info(recipe, "All slits have same offset: %.2f", mxpos);
1198  }
1199  else {
1200  cpl_msg_info(recipe, "All slits have different offsets");
1201  }
1202 
1203  if (ny != 400 && ny != 500) {
1204  if (cpl_frameset_count_tags(frameset,
1205  master_distortion_tag) == 0)
1206  fors_pmos_calib_exit(
1207  "Missing required input: MASTER_DISTORTION_TABLE");
1208 
1209  header_dist = dfs_load_header(frameset,
1210  master_distortion_tag, 0);
1211  rebin_dist = cpl_propertylist_get_int(header_dist,
1212  "ESO DET WIN1 BINX");
1213  cpl_propertylist_delete(header_dist);
1214  }
1215  }
1216 
1217  /* Leave the header on for the next step... */
1218 
1219 
1220  /*
1221  * Remove the master bias
1222  */
1223 
1224  if (nbias) {
1225 
1226  /*
1227  * Set of raw BIASes in input, need to create master bias!
1228  */
1229 
1230  cpl_msg_info(recipe, "Generate the master from input raw biases...");
1231 
1232  if (nbias > 1) {
1233 
1234  biases = cpl_imagelist_new();
1235 
1236  bias = dfs_load_image(frameset, "BIAS", CPL_TYPE_FLOAT, 0, 0);
1237 
1238  if (bias == NULL)
1239  fors_pmos_calib_exit("Cannot load bias frame");
1240 
1241  cpl_imagelist_set(biases, bias, 0);
1242 
1243  for (i = 1; i < nbias; i++) {
1244  bias = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
1245  if (bias)
1246  cpl_imagelist_set(biases, bias, i);
1247  else
1248  fors_pmos_calib_exit("Cannot load bias frame");
1249  }
1250 
1251  master_bias = cpl_imagelist_collapse_median_create(biases);
1252 
1253  cpl_imagelist_delete(biases);
1254  }
1255  else {
1256  master_bias = dfs_load_image(frameset, "BIAS",
1257  CPL_TYPE_FLOAT, 0, 1);
1258  if (master_bias == NULL)
1259  fors_pmos_calib_exit("Cannot load bias");
1260  }
1261 
1262  }
1263  else {
1264  master_bias = dfs_load_image(frameset, "MASTER_BIAS",
1265  CPL_TYPE_FLOAT, 0, 1);
1266  if (master_bias == NULL)
1267  fors_pmos_calib_exit("Cannot load master bias");
1268  }
1269 
1270  cpl_msg_info(recipe, "Remove the master bias...");
1271 
1272  overscans = mos_load_overscans_fors(header);
1273  cpl_propertylist_delete(header); header = NULL;
1274 
1275  if (nbias) {
1276  int xlow = cpl_table_get_int(overscans, "xlow", 0, NULL);
1277  int ylow = cpl_table_get_int(overscans, "ylow", 0, NULL);
1278  int xhig = cpl_table_get_int(overscans, "xhig", 0, NULL);
1279  int yhig = cpl_table_get_int(overscans, "yhig", 0, NULL);
1280  dummy = cpl_image_extract(master_bias, xlow+1, ylow+1, xhig, yhig);
1281  cpl_image_delete(master_bias); master_bias = dummy;
1282 
1283  if (dfs_save_image(frameset, master_bias, "MASTER_BIAS",
1284  NULL, parlist, recipe, version))
1285  fors_pmos_calib_exit(NULL);
1286  }
1287 
1288  if (nflats > 1) {
1289  multi_bias = cpl_image_multiply_scalar_create(master_bias, nflats);
1290  dummy = mos_remove_bias(master_flat, multi_bias, overscans);
1291  if (added_flat)
1292  add_dummy = mos_remove_bias(added_flat, multi_bias, overscans);
1293  cpl_image_delete(multi_bias);
1294  }
1295  else {
1296  dummy = mos_remove_bias(master_flat, master_bias, overscans);
1297  }
1298  cpl_image_delete(master_flat);
1299  master_flat = dummy;
1300 
1301  if (master_flat == NULL)
1302  fors_pmos_calib_exit("Cannot remove bias from flat field");
1303 
1304  if (added_flat) {
1305  cpl_image_delete(added_flat);
1306  added_flat = add_dummy;
1307 
1308  if (added_flat == NULL)
1309  fors_pmos_calib_exit("Cannot remove bias from added flat field");
1310 
1311  trace_flat = added_flat;
1312  }
1313  else
1314  trace_flat = master_flat;
1315 
1316  wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1);
1317 
1318  if (wavelengths == NULL)
1319  fors_pmos_calib_exit("Cannot load line catalog");
1320 
1321  /*
1322  * Cast the wavelengths into a (double precision) CPL vector
1323  */
1324 
1325  nlines = cpl_table_get_nrow(wavelengths);
1326 
1327  if (nlines == 0)
1328  fors_pmos_calib_exit("Empty input line catalog");
1329 
1330  if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
1331  cpl_msg_error(recipe, "Missing column %s in input line catalog table",
1332  wcolumn);
1333  fors_pmos_calib_exit(NULL);
1334  }
1335 
1336  line = cpl_malloc(nlines * sizeof(double));
1337 
1338  for (i = 0; i < nlines; i++)
1339  line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
1340 
1341  lines = cpl_vector_wrap(nlines, line);
1342 
1343  for (j = 0; j < pmos; j++) {
1344  int k;
1345 
1346  cpl_msg_indent_less();
1347  cpl_msg_info(recipe, "Processing arc lamp nb %d out of %d ...",
1348  j + 1, pmos);
1349  cpl_msg_indent_more();
1350 
1351  cpl_msg_info(recipe, "Load arc lamp exposure...");
1352  cpl_msg_indent_more();
1353 
1354  spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
1355 
1356  /*
1357  * FIXME: Horrible workaround to avoid the problem because of the
1358  * multiple encapsulation of cpl_frameset_find() in different
1359  * loading functions
1360  */
1361  for (k = 0; k < j; k ++) {
1362  cpl_image_delete(spectra);
1363  spectra = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
1364  }
1365 
1366  if (spectra == NULL)
1367  fors_pmos_calib_exit("Cannot load arc lamp exposure");
1368 
1369  if (doit) {
1370  mos_randomise_image(spectra, ron, gain, blevel);
1371  }
1372 
1373  cpl_msg_info(recipe, "Remove the master bias...");
1374 
1375  dummy = mos_remove_bias(spectra, master_bias, overscans);
1376  cpl_image_delete(spectra); spectra = dummy;
1377 
1378  if (spectra == NULL)
1379  fors_pmos_calib_exit("Cannot remove bias from arc lamp exposure");
1380 
1381  cpl_msg_indent_less();
1382  cpl_msg_info(recipe, "Load input line catalog...");
1383  cpl_msg_indent_more();
1384 
1385  /*
1386  * Here the PMOS calibration is carried out.
1387  */
1388 
1389  if (mos_saturation_process(spectra))
1390  fors_pmos_calib_exit("Cannot process saturation");
1391 
1392  if (mos_subtract_background(spectra))
1393  fors_pmos_calib_exit("Cannot subtract the background");
1394 
1395  if (!j) {
1396  /*
1397  * Detecting spectra on the CCD
1398  */
1399 
1400  cpl_msg_indent_less();
1401  cpl_msg_info(recipe, "Detecting spectra on CCD...");
1402  cpl_msg_indent_more();
1403 
1404  nx = cpl_image_get_size_x(spectra);
1405  ccd_ysize = ny = cpl_image_get_size_y(spectra);
1406 
1407  refmask = cpl_mask_new(nx, ny);
1408 
1409  checkwave =
1410  mos_wavelength_calibration_raw(spectra, lines, dispersion,
1411  peakdetection, wradius,
1412  wdegree, wreject, reference,
1413  &startwavelength, &endwavelength,
1414  NULL, NULL, NULL, NULL, NULL,
1415  NULL, refmask, NULL);
1416 
1417  if (checkwave == NULL)
1418  fors_pmos_calib_exit("Wavelength calibration failure.");
1419 
1420  /*
1421  * Save check image to disk
1422  */
1423 
1424  header = cpl_propertylist_new();
1425  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1426  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1427  cpl_propertylist_update_double(header, "CRVAL1",
1428  startwavelength + dispersion/2);
1429  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1430  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
1431  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
1432  cpl_propertylist_update_double(header, "CD1_1", dispersion);
1433  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1434  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1435  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1436  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1437  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1438 
1439  if (check) {
1440  if (!j) {
1441  if(dfs_save_image_null(frameset, parlist,
1442  spectra_detection_tag,
1443  recipe, version)) {
1444  fors_pmos_calib_exit(NULL);
1445  }
1446  }
1447 
1448  if (dfs_save_image_ext(checkwave,
1449  spectra_detection_tag, header)) {
1450  fors_pmos_calib_exit(NULL);
1451  }
1452  }
1453 
1454  cpl_image_delete(checkwave); checkwave = NULL;
1455  cpl_propertylist_delete(header); header = NULL;
1456 
1457  if (cpl_mask_is_empty(refmask))
1458  fors_pmos_calib_exit("Wavelength calibration failure.");
1459 
1460  if (mos_refmask_find_gaps(refmask, trace_flat, -1.0))
1461  fors_pmos_calib_exit("The gaps could not be found");
1462 
1463  cpl_msg_info(recipe,
1464  "Locate slits at reference wavelength on CCD...");
1465  slits = mos_locate_spectra(refmask);
1466 
1467  if (!slits) {
1468  cpl_msg_error(cpl_error_get_where(), "%s",
1469  cpl_error_get_message());
1470  fors_pmos_calib_exit("No slits could be detected!");
1471  }
1472 
1473  if (same_offset) {
1474  if (ny != 400 && ny != 500) {
1475  float rescale = (float) rebin_dist / rebin;
1476  if (mos_check_slits(slits, rescale)) {
1477  fors_pmos_calib_exit("Some slits are missing. "
1478  "Cannot recover!");
1479  }
1480  }
1481  }
1482 
1483  refimage = cpl_image_new_from_mask(refmask);
1484  cpl_mask_delete(refmask); refmask = NULL;
1485 
1486  if (check) {
1487  if (!j) {
1488  if(dfs_save_image_null(frameset, parlist,
1489  slit_map_tag,
1490  recipe, version)) {
1491  fors_pmos_calib_exit(NULL);
1492  }
1493  }
1494 
1495  save_header = dfs_load_header(frameset, arc_tag, 0);
1496 
1497  for (k = 0; k < j; k ++) {
1498  cpl_propertylist_delete(save_header);
1499  save_header = dfs_load_header(frameset, NULL, 0);
1500  }
1501 
1502  if (dfs_save_image_ext(refimage, slit_map_tag, save_header)) {
1503  fors_pmos_calib_exit(NULL);
1504  }
1505  cpl_propertylist_delete(save_header); save_header = NULL;
1506  }
1507 
1508  cpl_image_delete(refimage); refimage = NULL;
1509 
1510 // if (same_offset == 0) {
1511 
1512  same_offset = 1; // Added, see next line comment.
1513  if (0) { // This part is eliminated: a successful
1514  // pattern matching would identify just
1515  // one of the two beams!!! It needs to be FIXED.
1516 
1517  /*
1518  * Attempt slit identification: this recipe may continue even
1519  * in case of failed identification (i.e., the position table
1520  * is not produced, but an error is not set). In case of
1521  * failure, the spectra would be still extracted, even if they
1522  * would not be associated to slits on the mask.
1523  *
1524  * The reason for making the slit identification an user option
1525  * (via the parameter slit_ident) is to offer the possibility
1526  * to avoid identifications that are only apparently successful
1527  * as it would happen in the case of an incorrect slit
1528  * description in the data header.
1529  */
1530 
1531  cpl_msg_indent_less();
1532  cpl_msg_info(recipe,
1533  "Attempt slit identification (optional)...");
1534  cpl_msg_indent_more();
1535 
1536  positions = mos_identify_slits(slits, maskslits, NULL);
1537 
1538  if (positions) {
1539  cpl_table_delete(slits);
1540  slits = positions;
1541 
1542  /*
1543  * Eliminate slits which are not _entirely_ inside the CCD
1544  */
1545 
1546  cpl_table_and_selected_double(slits,
1547  "ytop", CPL_GREATER_THAN, ny);
1548  cpl_table_or_selected_double(slits,
1549  "ybottom", CPL_LESS_THAN, 0);
1550  cpl_table_erase_selected(slits);
1551 
1552  nslits = cpl_table_get_nrow(slits);
1553 
1554  if (nslits == 0)
1555  fors_pmos_calib_exit("No slits found on the CCD");
1556 
1557  cpl_msg_info(recipe,
1558  "%d slits are entirely contained in CCD",
1559  nslits);
1560  }
1561  else {
1562  same_offset = 1; /* FIXLANDER slit_ident = 0; */
1563  cpl_msg_info(recipe,
1564  "Global distortion model cannot be computed");
1565  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1566  fors_pmos_calib_exit(NULL);
1567  }
1568  }
1569  }
1570 
1571 
1572  if (ny == 400 || ny == 500) {
1573 
1574  /*
1575  * For the FORS1 special case (old chip 2048x400 readout)
1576  * keep the central slits only
1577  */
1578 
1579  nslits = cpl_table_get_nrow(slits);
1580 
1581  if (nslits > 4) {
1582  cpl_table_unselect_all(slits);
1583  for (k = 0; k < cpl_table_get_nrow(slits); k++) {
1584  double jump = cpl_table_get(slits, "ytop", k, NULL)
1585  - cpl_table_get(slits, "ybottom", k, NULL);
1586  if (jump < 50.) {
1587  cpl_table_select_row(slits, k);
1588  }
1589  }
1590  cpl_table_erase_selected(slits);
1591  nslits = cpl_table_get_nrow(slits);
1592  }
1593 
1594  if (nslits == 0)
1595  fors_pmos_calib_exit("No slits found on the CCD");
1596 
1597  if (nslits == 4) {
1598  cpl_table_unselect_all(slits);
1599  cpl_table_select_row(slits, 0);
1600  cpl_table_select_row(slits, cpl_table_get_nrow(slits)-1);
1601  cpl_table_erase_selected(slits);
1602  }
1603 
1604  cpl_msg_info(recipe,
1605  "%d slits are entirely contained in CCD", nslits);
1606  }
1607  else {
1608  cpl_table_unselect_all(slits);
1609  for (k = 0; k < cpl_table_get_nrow(slits); k++) {
1610  double jump = cpl_table_get(slits, "ytop", k, NULL)
1611  - cpl_table_get(slits, "ybottom", k, NULL);
1612  if (jump < 10.) {
1613  cpl_table_select_row(slits, k);
1614  }
1615  }
1616  cpl_table_erase_selected(slits);
1617  nslits = cpl_table_get_nrow(slits);
1618  }
1619 
1620 
1621  /*
1622  * Determination of spectral curvature
1623  */
1624 
1625  cpl_msg_indent_less();
1626  cpl_msg_info(recipe, "Determining spectral curvature...");
1627  cpl_msg_indent_more();
1628 
1629  cpl_msg_info(recipe, "Tracing master flat field spectra edges...");
1630  traces = mos_trace_flat(trace_flat, slits, reference,
1631  startwavelength, endwavelength, dispersion);
1632 
1633  if (!traces)
1634  fors_pmos_calib_exit("Tracing failure");
1635 
1636  cpl_image_delete(added_flat); added_flat = NULL;
1637 
1638  cpl_msg_info(recipe, "Fitting flat field spectra edges...");
1639  polytraces = mos_poly_trace(slits, traces, cdegree);
1640 
1641  if (!polytraces)
1642  fors_pmos_calib_exit("Trace fitting failure");
1643 
1644  if (cmode) {
1645  cpl_msg_info(recipe,
1646  "Computing global spectral curvature model...");
1647  mos_global_trace(slits, polytraces, cmode);
1648  }
1649 
1650  if (!j) {
1651  if(dfs_save_image_null(frameset, parlist, curv_traces_tag,
1652  recipe, version)) {
1653  fors_pmos_calib_exit(NULL);
1654  }
1655  }
1656 
1657  if (dfs_save_table_ext(traces, curv_traces_tag, NULL)) {
1658  fors_pmos_calib_exit(NULL);
1659  }
1660 
1661  cpl_table_delete(traces); traces = NULL;
1662 
1663  coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1664 
1665  }
1666 //
1667  spatial = mos_spatial_calibration(spectra, slits, polytraces,
1668  reference,
1669  startwavelength, endwavelength,
1670  dispersion, 0, j ? NULL: coordinate);
1671 
1672  if (!j) {
1673 //
1674  if (same_offset) { /* FIXLANDER It was !slit_ident */
1675  cpl_image_delete(spectra); spectra = NULL;
1676  }
1677 
1678  /*
1679  * Flat field normalisation is done directly on the master flat
1680  * field (without spatial rectification first). The spectral
1681  * curvature model may be provided in input, in future releases.
1682  */
1683 
1684  cpl_msg_indent_less();
1685  cpl_msg_info(recipe, "Perform flat field normalisation...");
1686  cpl_msg_indent_more();
1687 
1688  norm_flat = cpl_image_duplicate(master_flat);
1689 
1690  smo_flat = mos_normalise_flat(norm_flat, coordinate, slits,
1691  polytraces, reference,
1692  startwavelength, endwavelength,
1693  dispersion, dradius, ddegree);
1694 
1695  /* This may be a product */
1696  cpl_image_delete(smo_flat); smo_flat = NULL;
1697 
1698 
1699  save_header = dfs_load_header(frameset, flat_tag, 0);
1700  cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM",
1701  nflats);
1702 
1703  rect_flat = mos_spatial_calibration(master_flat, slits, polytraces,
1704  reference, startwavelength,
1705  endwavelength, dispersion, 0,
1706  NULL);
1707  rect_nflat = mos_spatial_calibration(norm_flat, slits, polytraces,
1708  reference, startwavelength,
1709  endwavelength, dispersion, 0,
1710  NULL);
1711 
1712 
1713  if (dfs_save_image(frameset, master_flat, master_screen_flat_tag,
1714  save_header, parlist, recipe, version))
1715  fors_pmos_calib_exit(NULL);
1716 
1717 
1718  if (dfs_save_image(frameset, norm_flat, master_norm_flat_tag,
1719  save_header, parlist, recipe, version))
1720  fors_pmos_calib_exit(NULL);
1721 
1722  cpl_image_delete(norm_flat); norm_flat = NULL;
1723  cpl_propertylist_delete(save_header); save_header = NULL;
1724 
1725  }
1726 
1727 
1728  /*
1729  * Final wavelength calibration of spectra having their curvature
1730  * removed
1731  */
1732 
1733  cpl_msg_indent_less();
1734  cpl_msg_info(recipe, "Perform final wavelength calibration...");
1735  cpl_msg_indent_more();
1736 
1737  nx = cpl_image_get_size_x(spatial);
1738  ny = cpl_image_get_size_y(spatial);
1739 
1740  idscoeff = cpl_table_new(ny);
1741  restable = cpl_table_new(nlines);
1742  rainbow = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1743  if (check)
1744  residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1745  fiterror = cpl_calloc(ny, sizeof(double));
1746  fitlines = cpl_calloc(ny, sizeof(int));
1747 
1748  rectified = mos_wavelength_calibration_final(spatial, slits, lines,
1749  dispersion, peakdetection,
1750  wradius, wdegree, wreject,
1751  reference,
1752  &startwavelength,
1753  &endwavelength, fitlines,
1754  fiterror, idscoeff,
1755  rainbow,
1756  residual, restable, NULL);
1757 
1758  if (rectified == NULL)
1759  fors_pmos_calib_exit("Wavelength calibration failure.");
1760 
1761  if (!j) {
1762  if(dfs_save_image_null(frameset, parlist, disp_residuals_table_tag,
1763  recipe, version)) {
1764  fors_pmos_calib_exit(NULL);
1765  }
1766  }
1767 
1768  header = dfs_load_header(frameset, arc_tag, 0);
1769 
1770  for (k = 0; k < j; k ++) {
1771  cpl_propertylist_delete(header);
1772  header = dfs_load_header(frameset, NULL, 0);
1773  }
1774 
1775  if (dfs_save_table_ext(restable, disp_residuals_table_tag, header)) {
1776  fors_pmos_calib_exit(NULL);
1777  }
1778 
1779  cpl_propertylist_delete(header);
1780 
1781  cpl_table_delete(restable); restable = NULL;
1782 
1783  cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
1784  cpl_table_set_column_unit(idscoeff, "error", "pixel");
1785  cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
1786 
1787  for (i = 0; i < ny; i++)
1788  if (!cpl_table_is_valid(idscoeff, "c0", i))
1789  cpl_table_set_invalid(idscoeff, "error", i);
1790 
1791  delta = mos_map_pixel(idscoeff, reference, startwavelength,
1792  endwavelength, dispersion, 2);
1793 
1794  header = dfs_load_header(frameset, arc_tag, 0);
1795 
1796  for (k = 0; k < j; k ++) {
1797  cpl_propertylist_delete(header);
1798  header = dfs_load_header(frameset, NULL, 0);
1799  }
1800 
1801  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1802  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1803  cpl_propertylist_update_double(header, "CRVAL1",
1804  startwavelength + dispersion/2);
1805  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1806  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
1807  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
1808  cpl_propertylist_update_double(header, "CD1_1", dispersion);
1809  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1810  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1811  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1812  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1813  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1814 
1815  if (!j) {
1816  if(dfs_save_image_null(frameset, parlist, delta_image_tag,
1817  recipe, version)) {
1818  fors_pmos_calib_exit(NULL);
1819  }
1820  }
1821 
1822  if (dfs_save_image_ext(delta, delta_image_tag, header)) {
1823  fors_pmos_calib_exit(NULL);
1824  }
1825 
1826  cpl_image_delete(delta); delta = NULL;
1827  cpl_propertylist_delete(header); header = NULL;
1828 
1829  mean_rms = mos_distortions_rms(rectified, lines, startwavelength,
1830  dispersion, 6, 0);
1831 
1832  cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
1833 
1834  mean_rms = cpl_table_get_column_mean(idscoeff, "error");
1835  mean_rms_err = cpl_table_get_column_stdev(idscoeff, "error");
1836 
1837  cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
1838  mean_rms, mean_rms * dispersion);
1839 
1840  restab = mos_resolution_table(rectified, startwavelength, dispersion,
1841  60000, lines);
1842 
1843  if (restab) {
1844  cpl_msg_info(recipe, "Mean spectral resolution: %.2f",
1845  cpl_table_get_column_mean(restab, "resolution"));
1846  cpl_msg_info(recipe,
1847  "Mean reference lines FWHM: %.2f +/- %.2f pixel",
1848  cpl_table_get_column_mean(restab, "fwhm") / dispersion,
1849  cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
1850 
1851  cpl_table_duplicate_column(restab, "dlambda",
1852  restab, "fwhm");
1853  cpl_table_multiply_scalar(restab, "dlambda", dispersion);
1854  cpl_table_duplicate_column(restab, "dlambda_rms",
1855  restab, "fwhm_rms");
1856  cpl_table_multiply_scalar(restab, "dlambda_rms", dispersion);
1857 
1858  if (qc) {
1859 
1860  qclist = cpl_propertylist_new();
1861 
1862  /*
1863  * QC1 parameters
1864  */
1865  keyname = "QC.DID";
1866 
1867  if (fors_header_write_string(qclist,
1868  keyname,
1869  "2.0",
1870  "QC1 dictionary")) {
1871  fors_pmos_calib_exit("Cannot write dictionary version "
1872  "to QC log file");
1873  }
1874 
1875 
1876  keyname = "QC.PMOS.RESOLUTION";
1877 
1878  if (fors_header_write_double(qclist,
1879  cpl_table_get_column_mean(restab,
1880  "resolution"),
1881  keyname,
1882  "Angstrom",
1883  "Mean spectral resolution")) {
1884  fors_pmos_calib_exit("Cannot write mean spectral "
1885  "resolution to QC log file");
1886  }
1887 
1888  keyname = "QC.PMOS.RESOLUTION.RMS";
1889 
1890  if (fors_header_write_double(qclist,
1891  cpl_table_get_column_stdev(restab,
1892  "resolution"),
1893  keyname,
1894  "Angstrom",
1895  "Scatter of spectral resolution")) {
1896  fors_pmos_calib_exit("Cannot write spectral resolution "
1897  "scatter to QC log file");
1898  }
1899 
1900  keyname = "QC.PMOS.RESOLUTION.NWAVE";
1901 
1902  if (fors_header_write_int(qclist, cpl_table_get_nrow(restab) -
1903  cpl_table_count_invalid(restab,
1904  "resolution"),
1905  keyname,
1906  NULL,
1907  "Number of examined wavelengths "
1908  "for resolution computation")) {
1909  fors_pmos_calib_exit("Cannot write number of lines used "
1910  "in spectral resolution computation "
1911  "to QC log file");
1912  }
1913 
1914  keyname = "QC.PMOS.RESOLUTION.MEANRMS";
1915 
1916  if (fors_header_write_double(qclist,
1917  cpl_table_get_column_mean(restab,
1918  "resolution_rms"),
1919  keyname, NULL,
1920  "Mean error on spectral "
1921  "resolution computation")) {
1922  fors_pmos_calib_exit("Cannot write mean error in "
1923  "spectral resolution computation "
1924  "to QC log file");
1925  }
1926 
1927  keyname = "QC.PMOS.RESOLUTION.NLINES";
1928 
1929  if (fors_header_write_int(qclist,
1930  cpl_table_get_column_mean(restab,
1931  "nlines") *
1932  cpl_table_get_nrow(restab),
1933  keyname, NULL,
1934  "Number of lines for spectral "
1935  "resolution computation")) {
1936  fors_pmos_calib_exit("Cannot write number of examined "
1937  "wavelengths in spectral resolution "
1938  "computation to QC log file");
1939  }
1940 
1941  }
1942 
1943  if (!j) {
1944  if(dfs_save_image_null(frameset, parlist,
1945  spectral_resolution_tag,
1946  recipe, version)) {
1947  fors_pmos_calib_exit(NULL);
1948  }
1949  }
1950 
1951  header = dfs_load_header(frameset, arc_tag, 0);
1952 
1953  for (k = 0; k < j; k ++) {
1954  cpl_propertylist_delete(header);
1955  header = dfs_load_header(frameset, NULL, 0);
1956  }
1957 
1958  cpl_propertylist_append(header, qclist);
1959 
1960  if (dfs_save_table_ext(restab, spectral_resolution_tag, header)) {
1961  fors_pmos_calib_exit(NULL);
1962  }
1963 
1964  cpl_table_delete(restab); restab = NULL;
1965  cpl_propertylist_delete(qclist); qclist = NULL;
1966  cpl_propertylist_delete(header); header = NULL;
1967 
1968  }
1969  else
1970  fors_pmos_calib_exit("Cannot compute the spectral "
1971  "resolution table");
1972 
1973  if (!j) {
1974  if(dfs_save_image_null(frameset, parlist, disp_coeff_tag,
1975  recipe, version)) {
1976  fors_pmos_calib_exit(NULL);
1977  }
1978  }
1979 
1980  header = dfs_load_header(frameset, arc_tag, 0);
1981 
1982  for (k = 0; k < j; k ++) {
1983  cpl_propertylist_delete(header);
1984  header = dfs_load_header(frameset, NULL, 0);
1985  }
1986 
1987  if (dfs_save_table_ext(idscoeff, disp_coeff_tag, header)) {
1988  fors_pmos_calib_exit(NULL);
1989  }
1990 
1991  cpl_propertylist_delete(header);
1992 
1993  if (!j) {
1994  mapped_flat = mos_wavelength_calibration(rect_flat, reference,
1995  startwavelength,
1996  endwavelength,
1997  dispersion, idscoeff, 0);
1998 
1999  mapped_nflat = mos_wavelength_calibration(rect_nflat, reference,
2000  startwavelength,
2001  endwavelength,
2002  dispersion, idscoeff, 0);
2003 
2004  cpl_image_delete(rect_flat); rect_flat = NULL;
2005  cpl_image_delete(rect_nflat); rect_nflat = NULL;
2006  }
2007 
2008  /* Global removed */
2009 
2010  cpl_table_delete(idscoeff); idscoeff = NULL;
2011 
2012  header = dfs_load_header(frameset, arc_tag, 0);
2013 
2014  for (k = 0; k < j; k ++) {
2015  cpl_propertylist_delete(header);
2016  header = dfs_load_header(frameset, NULL, 0);
2017  }
2018 
2019  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
2020  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
2021  cpl_propertylist_update_double(header, "CRVAL1",
2022  startwavelength + dispersion/2);
2023  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
2024  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
2025  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
2026  cpl_propertylist_update_double(header, "CD1_1", dispersion);
2027  cpl_propertylist_update_double(header, "CD1_2", 0.0);
2028  cpl_propertylist_update_double(header, "CD2_1", 0.0);
2029  cpl_propertylist_update_double(header, "CD2_2", 1.0);
2030  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
2031  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
2032  cpl_propertylist_update_int(header, "ESO PRO DATANCOM", 1);
2033 
2034  if (!j) {
2035  if(dfs_save_image_null(frameset, parlist, reduced_lamp_tag,
2036  recipe, version)) {
2037  fors_pmos_calib_exit(NULL);
2038  }
2039  }
2040 
2041  if (dfs_save_image_ext(rectified, reduced_lamp_tag, header)) {
2042  fors_pmos_calib_exit(NULL);
2043  }
2044 
2045  cpl_image_delete(rectified); rectified = NULL;
2046 
2047  cpl_propertylist_update_int(header, "ESO PRO DATANCOM", nflats);
2048 
2049  if (!j) {
2050  if (dfs_save_image(frameset, mapped_flat, mapped_screen_flat_tag,
2051  header, parlist, recipe, version))
2052  fors_pmos_calib_exit(NULL);
2053  cpl_image_delete(mapped_flat); mapped_flat = NULL;
2054 
2055  if (dfs_save_image(frameset, mapped_nflat, mapped_norm_flat_tag,
2056  header, parlist, recipe, version))
2057  fors_pmos_calib_exit(NULL);
2058  cpl_image_delete(mapped_nflat); mapped_nflat = NULL;
2059  }
2060 
2061  cpl_propertylist_delete(header); header = NULL;
2062 
2063  if (check) {
2064  save_header = dfs_load_header(frameset, arc_tag, 0);
2065  for (k = 0; k < j; k ++) {
2066  cpl_propertylist_delete(save_header);
2067  save_header = dfs_load_header(frameset, NULL, 0);
2068  }
2069 
2070  cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
2071  cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
2072  /* cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
2073  cpl_propertylist_update_double(save_header, "CD1_1", 1.0);
2074  cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
2075  cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
2076  cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
2077  cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
2078  cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
2079 
2080  if (!j) {
2081  if(dfs_save_image_null(frameset, parlist, disp_residuals_tag,
2082  recipe, version)) {
2083  fors_pmos_calib_exit(NULL);
2084  }
2085  }
2086 
2087  if (dfs_save_image_ext(residual, disp_residuals_tag, save_header)) {
2088  fors_pmos_calib_exit(NULL);
2089  }
2090 
2091  cpl_image_delete(residual); residual = NULL;
2092  cpl_propertylist_delete(save_header); save_header = NULL;
2093  }
2094 
2095  wavemap = mos_map_wavelengths(coordinate, rainbow, slits, polytraces,
2096  reference, startwavelength, endwavelength,
2097  dispersion);
2098 
2099  cpl_image_delete(rainbow); rainbow = NULL;
2100 
2101  save_header = dfs_load_header(frameset, arc_tag, 0);
2102 
2103  for (k = 0; k < j; k ++) {
2104  cpl_propertylist_delete(save_header);
2105  save_header = dfs_load_header(frameset, NULL, 0);
2106  }
2107 
2108  if (qc) {
2109 
2110  /*
2111  * QC1 parameters
2112  */
2113  if (fors_header_write_string(save_header,
2114  "QC.DID",
2115  "2.0",
2116  "QC1 dictionary")) {
2117  fors_pmos_calib_exit("Cannot write dictionary version "
2118  "to QC log file");
2119  }
2120 
2121  if (fors_header_write_double(save_header,
2122  mean_rms,
2123  "QC.WAVE.ACCURACY",
2124  "pixel",
2125  "Mean accuracy of wavecalib model")) {
2126  fors_pmos_calib_exit("Cannot write mean wavelength calibration "
2127  "accuracy to QC log file");
2128  }
2129 
2130 
2131  if (fors_header_write_double(save_header,
2132  mean_rms_err,
2133  "QC.WAVE.ACCURACY.ERROR",
2134  "pixel",
2135  "Error on accuracy of wavecalib model")) {
2136  fors_pmos_calib_exit("Cannot write error on wavelength "
2137  "calibration accuracy to QC log file");
2138  }
2139 
2140  if (same_offset && fabs(mxpos) < 0.05) {
2141  /* Only if same offset is 0.0 */
2142 
2143  data = cpl_image_get_data(wavemap);
2144 
2145  if (fors_header_write_double(save_header,
2146  data[nx/2 + ccd_ysize*nx/2],
2147  "QC.PMOS.CENTRAL.WAVELENGTH",
2148  "Angstrom",
2149  "Wavelength at CCD center")) {
2150  fors_pmos_calib_exit("Cannot write central wavelength "
2151  "to QC log file");
2152  }
2153  }
2154 
2155  }
2156 
2157  if (!j) {
2158  if(dfs_save_image_null(frameset, parlist, wavelength_map_tag,
2159  recipe, version)) {
2160  fors_pmos_calib_exit(NULL);
2161  }
2162  }
2163 
2164  if (dfs_save_image_ext(wavemap, wavelength_map_tag, save_header)) {
2165  fors_pmos_calib_exit(NULL);
2166  }
2167 
2168  cpl_image_delete(wavemap); wavemap = NULL;
2169 
2170  cpl_propertylist_erase_regexp(save_header, "^ESO QC ", 0);
2171 
2172  cpl_propertylist_delete(save_header); save_header = NULL;
2173 
2174  cpl_msg_indent_less();
2175 
2176  }
2177 
2178  if (dfs_save_image(frameset, coordinate, spatial_map_tag, save_header,
2179  parlist, recipe, version))
2180  fors_pmos_calib_exit(NULL);
2181 
2182  cpl_image_delete(coordinate); coordinate = NULL;
2183  cpl_propertylist_delete(save_header); save_header = NULL;
2184 
2185  header = NULL;
2186 
2187  if (qc) {
2188 
2189  double maxpos, maxneg, maxcurve, maxslope;
2190 
2191  header = dfs_load_header(frameset, arc_tag, 0);
2192 
2193  /*
2194  * QC1 parameters
2195  */
2196  if (fors_header_write_string(header,
2197  "QC.DID",
2198  "2.0",
2199  "QC1 dictionary")) {
2200  fors_pmos_calib_exit("Cannot write dictionary version "
2201  "to QC log file");
2202  }
2203 
2204  maxpos = fabs(cpl_table_get_column_max(polytraces, "c2"));
2205  maxneg = fabs(cpl_table_get_column_min(polytraces, "c2"));
2206  maxcurve = maxpos > maxneg ? maxpos : maxneg;
2207  if (fors_header_write_double(header,
2208  maxcurve,
2209  "QC.TRACE.MAX.CURVATURE",
2210  "Y pixel / X pixel ^2",
2211  "Max observed curvature "
2212  "in spectral tracing")) {
2213  fors_pmos_calib_exit("Cannot write max observed curvature in "
2214  "spectral tracing to QC log file");
2215  }
2216 
2217  maxpos = fabs(cpl_table_get_column_max(polytraces, "c1"));
2218  maxneg = fabs(cpl_table_get_column_min(polytraces, "c1"));
2219  maxslope = maxpos > maxneg ? maxpos : maxneg;
2220 
2221  if (fors_header_write_double(header,
2222  maxslope,
2223  "QC.TRACE.MAX.SLOPE",
2224  "Y pixel / X pixel",
2225  "Max observed slope in spectral tracing")) {
2226  fors_pmos_calib_exit("Cannot write max observed slope in spectral "
2227  "tracing to QC log file");
2228  }
2229  }
2230 
2231  if (dfs_save_table(frameset, polytraces, curv_coeff_tag, header,
2232  parlist, recipe, version)) {
2233  fors_pmos_calib_exit(NULL);
2234  }
2235 
2236  cpl_propertylist_delete(header); header = NULL;
2237  cpl_table_delete(polytraces); polytraces = NULL;
2238 
2239  /* FIXLANDER It was slit_ident == 0 and
2240  it was in a different place above in the code */
2241 
2242  if (same_offset) {
2243  cpl_table *globaltbl;
2244  cpl_table *slitpos;
2245  double *l_ytop;
2246  int *l_id;
2247  int npairs;
2248  double *ytop = cpl_table_get_data_double(slits, "ytop");
2249  double *ybot = cpl_table_get_data_double(slits, "ybottom");
2250  int k;
2251 // int *p_id;
2252 
2253  /* Just in case it has been modified */
2254  nslits = cpl_table_get_nrow(slits);
2255 
2256  cpl_table_new_column(slits, "pair_id", CPL_TYPE_INT);
2257 // p_id = cpl_table_get_data_int(slits, "pair_id");
2258 
2259  if (ccd_ysize == 400 || ccd_ysize == 500) {
2260 
2261  /*
2262  * Special case with old FORS1 chip
2263  */
2264 
2265  l_ytop = cpl_malloc(sizeof(double));
2266  l_ytop[0] = 255.0;
2267  l_id = cpl_malloc(sizeof(double));
2268  l_id[0] = 10;
2269  npairs = 1;
2270  }
2271  else {
2272  globaltbl = dfs_load_table(frameset, master_distortion_tag, 1);
2273  slitpos = mos_build_slit_location(globaltbl, maskslits, ccd_ysize);
2274  l_ytop = cpl_table_get_data_double(slitpos, "ytop");
2275  l_id = cpl_table_get_data_int(slitpos, "slit_id");
2276  npairs = cpl_table_get_nrow(slitpos);
2277  if (rebin_dist != rebin) {
2278  float rescale = (float)rebin_dist / rebin;
2279  for (i = 0; i < npairs; i++) {
2280  l_ytop[i] *= rescale;
2281  }
2282  }
2283  }
2284 
2285  for (k = 0; k < npairs; k++) {
2286  int h;
2287 
2288  for (h = 0; h < nslits; h++) {
2289 
2290  if (l_ytop[k] < ytop[h] && l_ytop[k] > ybot[h]) {
2291  if (h + 1 < nslits) {
2292  cpl_table_set_int(slits, "pair_id", h, l_id[k]);
2293  cpl_table_set_int(slits, "pair_id", h + 1, l_id[k]);
2294  }
2295  }
2296  }
2297  }
2298 
2299 /* %%% */
2300 
2301  cpl_table_fill_invalid_int(slits, "pair_id", -1);
2302 
2303  if (ccd_ysize == 400 || ccd_ysize == 500) {
2304  cpl_free(l_ytop);
2305  cpl_free(l_id);
2306  }
2307  else {
2308  cpl_table_delete(slitpos); slitpos = NULL;
2309  cpl_table_delete(globaltbl); globaltbl = NULL;
2310 
2311  cpl_table_delete(maskslits); maskslits = NULL;
2312  }
2313  }
2314 
2315  if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
2316  parlist, recipe, version)) {
2317  fors_pmos_calib_exit(NULL);
2318  }
2319 
2320  cpl_table_delete(slits); slits = NULL;
2321 
2322  cpl_image_delete(spatial); spatial = NULL;
2323 
2324  cpl_free(instrume); instrume = NULL;
2325 
2326  cpl_table_delete(overscans); overscans = NULL;
2327  cpl_image_delete(master_bias); master_bias = NULL;
2328  cpl_image_delete(master_flat); master_flat = NULL;
2329 
2330  cpl_table_delete(wavelengths); wavelengths = NULL;
2331  cpl_vector_delete(lines); lines = NULL;
2332 
2333  if (cpl_error_get_code()) {
2334  cpl_msg_error(cpl_error_get_where(), "%s", cpl_error_get_message());
2335  fors_pmos_calib_exit(NULL);
2336  }
2337 
2338  return 0;
2339 }
cpl_image * mos_spatial_calibration(cpl_image *spectra, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion, int flux, cpl_image *calibration)
Spatial remapping of CCD spectra eliminating the spectral curvature.
Definition: moses.c:8539
cpl_image * mos_map_pixel(cpl_table *idscoeff, double reference, double blue, double red, double dispersion, int trend)
Create a pixel map from an IDS coefficients table.
Definition: moses.c:11184
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.
Definition: fors_bias.c:62
cpl_image * dfs_load_image(cpl_frameset *frameset, const char *category, cpl_type type, int ext, int calib)
Loading image data of given category.
Definition: fors_dfs.c:860
const char * dfs_get_parameter_string(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe string parameter value.
Definition: fors_dfs.c:602
cpl_image * mos_wavelength_calibration_raw(const cpl_image *image, cpl_vector *lines, double dispersion, float level, int sradius, int order, double reject, double refwave, double *wavestart, double *waveend, int *nlines, double *error, cpl_table *idscoeff, cpl_image *calibration, cpl_image *residuals, cpl_table *restable, cpl_mask *refmask, cpl_table *detected_lines)
Derive wavelength calibration from a raw arc lamp or sky exposure.
Definition: moses.c:5492
double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, double wavestart, double dispersion, int radius, int highres)
Estimate the spectral distortion modeling goodness.
Definition: moses.c:11047
cpl_propertylist * dfs_load_header(cpl_frameset *frameset, const char *category, int ext)
Loading header associated to data of given category.
Definition: fors_dfs.c:964
cpl_error_code dfs_save_image_null(cpl_frameset *frameset, cpl_parameterlist *parlist, const char *tag, const char *recipename, const char *version)
Save a product with an empty primary extension.
Definition: fors_dfs.c:1893
cpl_error_code dfs_save_image_ext(cpl_image *image, const char *tag, cpl_propertylist *extheader)
Save an image in a extension.
Definition: fors_dfs.c:1850
cpl_image * mos_normalise_flat(cpl_image *flat, cpl_image *spatial, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion, int sradius, int polyorder)
Normalise a flat field exposure.
Definition: moses.c:2333
cpl_image * mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Remapping of spatially rectified wavelengths to original CCD pixels.
Definition: moses.c:11380
cpl_image * mos_wavelength_calibration(cpl_image *image, double refwave, double firstLambda, double lastLambda, double dispersion, cpl_table *idscoeff, int flux)
Remap at constant wavelength step an image of rectified scientific spectra.
Definition: moses.c:9697
cpl_table * mos_identify_slits(cpl_table *slits, cpl_table *maskslits, cpl_table *global)
Identify slits listed in a slit location table.
Definition: moses.c:6459
cpl_table * mos_load_slits_fors_mos(cpl_propertylist *header, int *nslits_out_det)
Create slit location table from FITS header of FORS1/2 MOS data.
Definition: moses.c:15098
cpl_error_code fors_header_write_double(cpl_propertylist *header, double value, const char *name, const char *unit, const char *comment)
Write an integer value to the active QC1 PAF object and to a header.
Definition: fors_header.c:131
cpl_error_code mos_subtract_background(cpl_image *image)
Subtract the background.
Definition: moses.c:16378
cpl_image * mos_remove_bias(cpl_image *image, cpl_image *bias, cpl_table *overscans)
Subtract the bias from a CCD exposure.
Definition: moses.c:3694
cpl_table * mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
Fit spectral traces.
Definition: moses.c:8197
void fors_dfs_set_groups(cpl_frameset *set)
Set the group as RAW or CALIB in a frameset.
Definition: fors_dfs.c:257
int dfs_get_parameter_bool(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe boolean parameter value.
Definition: fors_dfs.c:701
int dfs_equal_keyword(cpl_frameset *frameset, const char *keyword)
Saving table data of given category.
Definition: fors_dfs.c:1683
cpl_error_code dfs_save_table_ext(cpl_table *table, const char *tag, cpl_propertylist *extheader)
Save a table in a extension (different from the first one)
Definition: fors_dfs.c:1811
cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces, int mode)
Recompute tracing coefficients globally.
Definition: moses.c:8358
void fors_stack_define_parameters(cpl_parameterlist *parameters, const char *context, const char *default_method)
Define recipe parameters.
Definition: fors_stack.c:55
int dfs_save_image(cpl_frameset *frameset, const cpl_image *image, const char *category, cpl_propertylist *header, const cpl_parameterlist *parlist, const char *recipename, const char *version)
Saving image data of given category.
Definition: fors_dfs.c:1451
cpl_table * dfs_load_table(cpl_frameset *frameset, const char *category, int ext)
Loading table data of given category.
Definition: fors_dfs.c:916
cpl_table * mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference, double blue, double red, double dispersion)
Trace flat field spectra.
Definition: moses.c:7763
cpl_image * mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits, cpl_vector *lines, double dispersion, float level, int sradius, int order, double reject, double refwave, double *wavestart, double *waveend, int *nlines, double *error, cpl_table *idscoeff, cpl_image *calibration, cpl_image *residuals, cpl_table *restable, cpl_table *detected_lines)
Derive wavelength calibration from a rectified arc lamp or sky exposure.
Definition: moses.c:8954
int dfs_get_parameter_int(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe integer parameter value.
Definition: fors_dfs.c:407
int mos_check_slits(cpl_table *slits, float rescale)
Check that all slit have been detected, insert them if not.
Definition: moses.c:17060
int dfs_save_table(cpl_frameset *frameset, const cpl_table *table, const char *category, cpl_propertylist *header, const cpl_parameterlist *parlist, const char *recipename, const char *version)
Saving table data of given category.
Definition: fors_dfs.c:1575
cpl_error_code mos_refmask_find_gaps(cpl_mask *refmask, cpl_image *master_flat, double level)
Reconstruct the gaps required for slit location.
Definition: moses.c:16218
Definition: list.c:74
cpl_image * mos_ksigma_stack(cpl_imagelist *imlist, double klow, double khigh, int kiter, cpl_image **good)
Stack images using k-sigma clipping.
Definition: moses.c:17980
cpl_table * mos_resolution_table(cpl_image *image, double startwave, double dispersion, int saturation, cpl_vector *lines)
Compute mean spectral resolution at a given arc lamp line.
Definition: moses.c:14698
cpl_error_code mos_randomise_image(cpl_image *image, double ron, double gain, double bias)
Randomise image.
Definition: moses.c:16174
cpl_table * mos_locate_spectra(cpl_mask *mask)
Find the location of detected spectra on the CCD.
Definition: moses.c:6193
double dfs_get_parameter_double(cpl_parameterlist *parlist, const char *name, const cpl_table *defaults)
Reading a recipe double parameter value.
Definition: fors_dfs.c:504
cpl_error_code fors_header_write_string(cpl_propertylist *header, const char *name, const char *value, const char *comment)
Write a string value to the active QC1 PAF object and to a header.
Definition: fors_header.c:78
cpl_error_code mos_saturation_process(cpl_image *image)
Process saturation.
Definition: moses.c:16310
cpl_table * mos_build_slit_location(cpl_table *global, cpl_table *maskslits, int ysize)
Build the slit location table from a global distortions table.
Definition: moses.c:1586