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