FORS Pipeline Reference Manual  4.12.5
vimos_calib_impl.c
1 /* $Id: vimos_calib_impl.c,v 1.5 2013-08-19 17:05:47 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-08-19 17:05:47 $
24  * $Revision: 1.5 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <vimos_calib_impl.h>
33 
34 #include <math.h>
35 #include <cpl.h>
36 #include <moses.h>
37 #include <fors_dfs.h>
38 #include <fors_qc.h>
39 
40 #define vimos_calib_exit(message) \
41 { \
42 if (message !=NULL ) cpl_msg_error(recipe, message); \
43 cpl_free(instrume); \
44 cpl_free(pipefile); \
45 cpl_free(fiterror); \
46 cpl_free(fitlines); \
47 cpl_image_delete(bias); \
48 cpl_image_delete(master_bias); \
49 cpl_image_delete(coordinate); \
50 cpl_image_delete(checkwave); \
51 cpl_image_delete(flat); \
52 cpl_image_delete(master_flat); \
53 cpl_image_delete(norm_flat); \
54 cpl_image_delete(rainbow); \
55 cpl_image_delete(rectified); \
56 cpl_image_delete(residual); \
57 cpl_image_delete(smo_flat); \
58 cpl_image_delete(spatial); \
59 cpl_image_delete(spectra); \
60 cpl_image_delete(wavemap); \
61 cpl_image_delete(delta); \
62 cpl_mask_delete(refmask); \
63 cpl_propertylist_delete(header); \
64 cpl_propertylist_delete(save_header); \
65 cpl_propertylist_delete(qclist); \
66 cpl_table_delete(grism_table); \
67 cpl_table_delete(idscoeff); \
68 cpl_table_delete(restable); \
69 cpl_table_delete(maskslits); \
70 cpl_table_delete(overscans); \
71 cpl_table_delete(traces); \
72 cpl_table_delete(polytraces); \
73 cpl_table_delete(slits); \
74 cpl_table_delete(restab); \
75 cpl_table_delete(global); \
76 cpl_table_delete(wavelengths); \
77 cpl_vector_delete(lines); \
78 cpl_msg_indent_less(); \
79 return -1; \
80 }
81 
82 #define vimos_calib_exit_memcheck(message) \
83 { \
84 if (message !=NULL ) cpl_msg_info(recipe, message); \
85 printf("free instrume (%p)\n", instrume); \
86 cpl_free(instrume); \
87 printf("free pipefile (%p)\n", pipefile); \
88 cpl_free(pipefile); \
89 printf("free fiterror (%p)\n", fiterror); \
90 cpl_free(fiterror); \
91 printf("free fitlines (%p)\n", fitlines); \
92 cpl_free(fitlines); \
93 printf("free bias (%p)\n", bias); \
94 cpl_image_delete(bias); \
95 printf("free master_bias (%p)\n", master_bias); \
96 cpl_image_delete(master_bias); \
97 printf("free coordinate (%p)\n", coordinate); \
98 cpl_image_delete(coordinate); \
99 printf("free checkwave (%p)\n", checkwave); \
100 cpl_image_delete(checkwave); \
101 printf("free flat (%p)\n", flat); \
102 cpl_image_delete(flat); \
103 printf("free master_flat (%p)\n", master_flat); \
104 cpl_image_delete(master_flat); \
105 printf("free norm_flat (%p)\n", norm_flat); \
106 cpl_image_delete(norm_flat); \
107 printf("free rainbow (%p)\n", rainbow); \
108 cpl_image_delete(rainbow); \
109 printf("free rectified (%p)\n", rectified); \
110 cpl_image_delete(rectified); \
111 printf("free residual (%p)\n", residual); \
112 cpl_image_delete(residual); \
113 printf("free smo_flat (%p)\n", smo_flat); \
114 cpl_image_delete(smo_flat); \
115 printf("free spatial (%p)\n", spatial); \
116 cpl_image_delete(spatial); \
117 printf("free spectra (%p)\n", spectra); \
118 cpl_image_delete(spectra); \
119 printf("free wavemap (%p)\n", wavemap); \
120 cpl_image_delete(wavemap); \
121 printf("free delta (%p)\n", delta); \
122 cpl_image_delete(delta); \
123 printf("free refmask (%p)\n", refmask); \
124 cpl_mask_delete(refmask); \
125 printf("free header (%p)\n", header); \
126 cpl_propertylist_delete(header); \
127 printf("free save_header (%p)\n", save_header); \
128 cpl_propertylist_delete(save_header); \
129 printf("free qclist (%p)\n", qclist); \
130 cpl_propertylist_delete(qclist); \
131 printf("free grism_table (%p)\n", grism_table); \
132 cpl_table_delete(grism_table); \
133 printf("free idscoeff (%p)\n", idscoeff); \
134 cpl_table_delete(idscoeff); \
135 printf("free restable (%p)\n", restable); \
136 cpl_table_delete(restable); \
137 printf("free maskslits (%p)\n", maskslits); \
138 cpl_table_delete(maskslits); \
139 printf("free overscans (%p)\n", overscans); \
140 cpl_table_delete(overscans); \
141 printf("free traces (%p)\n", traces); \
142 cpl_table_delete(traces); \
143 printf("free polytraces (%p)\n", polytraces); \
144 cpl_table_delete(polytraces); \
145 printf("free slits (%p)\n", slits); \
146 cpl_table_delete(slits); \
147 printf("free restab (%p)\n", restab); \
148 cpl_table_delete(restab); \
149 printf("free global (%p)\n", global); \
150 cpl_table_delete(global); \
151 printf("free wavelengths (%p)\n", wavelengths); \
152 cpl_table_delete(wavelengths); \
153 printf("free lines (%p)\n", lines); \
154 cpl_vector_delete(lines); \
155 cpl_msg_indent_less(); \
156 return 0; \
157 }
158 
174 int vimos_calib_impl(cpl_frameset *frameset, cpl_parameterlist *parlist)
175 {
176 
177  const char *recipe = "vimos_calib";
178 
179  /*
180  * Input parameters
181  */
182 
183  double dispersion;
184  double peakdetection;
185  int wdegree;
186  int wradius;
187  double wreject;
188  int wmode;
189  const char *wcolumn;
190  int cdegree;
191  int cmode;
192  double startwavelength;
193  double endwavelength;
194  double reference;
195  int slit_ident;
196  int sdegree;
197  int ddegree;
198  int sradius;
199  int dradius;
200  int qc;
201  int check;
202 
203  /*
204  * CPL objects
205  */
206 
207  cpl_imagelist *biases = NULL;
208  cpl_image *bias = NULL;
209  cpl_image *master_bias = NULL;
210  cpl_image *multi_bias = NULL;
211  cpl_image *flat = NULL;
212  cpl_image *master_flat = NULL;
213  cpl_image *smo_flat = NULL;
214  cpl_image *norm_flat = NULL;
215  cpl_image *spectra = NULL;
216  cpl_image *wavemap = NULL;
217  cpl_image *delta = NULL;
218  cpl_image *residual = NULL;
219  cpl_image *checkwave = NULL;
220  cpl_image *rectified = NULL;
221  cpl_image *dummy = NULL;
222  cpl_image *refimage = NULL;
223  cpl_image *coordinate = NULL;
224  cpl_image *rainbow = NULL;
225  cpl_image *spatial = NULL;
226 
227  cpl_mask *refmask = NULL;
228 
229  cpl_table *grism_table = NULL;
230  cpl_table *overscans = NULL;
231  cpl_table *wavelengths = NULL;
232  cpl_table *idscoeff = NULL;
233  cpl_table *restable = NULL;
234  cpl_table *slits = NULL;
235  cpl_table *positions = NULL;
236  cpl_table *maskslits = NULL;
237  cpl_table *traces = NULL;
238  cpl_table *polytraces = NULL;
239  cpl_table *restab = NULL;
240  cpl_table *global = NULL;
241 
242  cpl_vector *lines = NULL;
243 
244  cpl_propertylist *header = NULL;
245  cpl_propertylist *save_header = NULL;
246  cpl_propertylist *qclist = NULL;
247 
248  /*
249  * Auxiliary variables
250  */
251 
252  cpl_table *idscoeff_lss = NULL;
253  char version[80];
254  const char *arc_tag;
255  const char *flat_tag;
256  const char *master_screen_flat_tag;
257  const char *master_norm_flat_tag;
258  const char *reduced_lamp_tag;
259  const char *disp_residuals_tag;
260  const char *disp_coeff_tag;
261  const char *wavelength_map_tag;
262  const char *spectra_detection_tag;
263  const char *spectral_resolution_tag;
264  const char *slit_map_tag;
265  const char *curv_traces_tag;
266  const char *curv_coeff_tag;
267  const char *spatial_map_tag;
268  const char *slit_location_tag;
269  const char *global_distortion_tag = "GLOBAL_DISTORTION_TABLE";
270  const char *disp_residuals_table_tag;
271  const char *delta_image_tag;
272  const char *key_gris_name;
273  const char *key_gris_id;
274  const char *key_filt_name;
275  const char *key_filt_id;
276  const char *key_mask_id;
277  char *keyname;
278  int quadrant;
279  int mos;
280  int treat_as_lss = 0;
281  int nslits;
282  double *xpos;
283  double mxpos;
284  double mean_rms;
285  double alltime, arctime;
286  int nflats;
287  int nbias;
288  int nlines;
289  double *line;
290  double *fiterror = NULL;
291  int *fitlines = NULL;
292  int nx, ny;
293  double gain;
294  int ccd_xsize, ccd_ysize;
295  int rotate = 1;
296  int rotate_back = -1;
297  int i;
298 
299  char *instrume = NULL;
300  char *pipefile = NULL;
301  char *grism;
302 
303 
304  snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
305 
306  cpl_msg_set_indentation(2);
307 
308 
309  /*
310  * Get configuration parameters
311  */
312 
313  cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
314  cpl_msg_indent_more();
315 
316  if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
317  vimos_calib_exit("Too many in input: GRISM_TABLE");
318 
319  grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
320 
321  dispersion = dfs_get_parameter_double(parlist,
322  "fors.vimos_calib.dispersion", grism_table);
323 
324  if (dispersion <= 0.0)
325  vimos_calib_exit("Invalid spectral dispersion value");
326 
327  peakdetection = dfs_get_parameter_double(parlist,
328  "fors.vimos_calib.peakdetection", grism_table);
329  if (peakdetection <= 0.0)
330  vimos_calib_exit("Invalid peak detection level");
331 
332  wdegree = dfs_get_parameter_int(parlist,
333  "fors.vimos_calib.wdegree", grism_table);
334 
335  if (wdegree < 1)
336  vimos_calib_exit("Invalid polynomial degree");
337 
338  if (wdegree > 5)
339  vimos_calib_exit("Max allowed polynomial degree is 5");
340 
341  wradius = dfs_get_parameter_int(parlist, "fors.vimos_calib.wradius", NULL);
342 
343  if (wradius < 0)
344  vimos_calib_exit("Invalid search radius");
345 
346  wreject = dfs_get_parameter_double(parlist,
347  "fors.vimos_calib.wreject", NULL);
348 
349  if (wreject <= 0.0)
350  vimos_calib_exit("Invalid rejection threshold");
351 
352  wmode = dfs_get_parameter_int(parlist, "fors.vimos_calib.wmode", NULL);
353 
354  if (wmode < 0 || wmode > 2)
355  vimos_calib_exit("Invalid wavelength solution interpolation mode");
356 
357  wcolumn = dfs_get_parameter_string(parlist,
358  "fors.vimos_calib.wcolumn", NULL);
359 
360  cdegree = dfs_get_parameter_int(parlist,
361  "fors.vimos_calib.cdegree", grism_table);
362 
363  if (cdegree < 1)
364  vimos_calib_exit("Invalid polynomial degree");
365 
366  if (cdegree > 5)
367  vimos_calib_exit("Max allowed polynomial degree is 5");
368 
369  cmode = dfs_get_parameter_int(parlist, "fors.vimos_calib.cmode", NULL);
370 
371  if (cmode < 0 || cmode > 2)
372  vimos_calib_exit("Invalid curvature solution interpolation mode");
373 
374  startwavelength = dfs_get_parameter_double(parlist,
375  "fors.vimos_calib.startwavelength", grism_table);
376  if (startwavelength > 1.0)
377  if (startwavelength < 3000.0 || startwavelength > 13000.0)
378  vimos_calib_exit("Invalid wavelength");
379 
380  endwavelength = dfs_get_parameter_double(parlist,
381  "fors.vimos_calib.endwavelength", grism_table);
382  if (endwavelength > 1.0) {
383  if (endwavelength < 3000.0 || endwavelength > 13000.0)
384  vimos_calib_exit("Invalid wavelength");
385  if (startwavelength < 1.0)
386  vimos_calib_exit("Invalid wavelength interval");
387  }
388 
389  if (startwavelength > 1.0)
390  if (endwavelength - startwavelength <= 0.0)
391  vimos_calib_exit("Invalid wavelength interval");
392 
393  reference = dfs_get_parameter_double(parlist,
394  "fors.vimos_calib.reference", grism_table);
395 
396  if (reference < startwavelength || reference > endwavelength)
397  vimos_calib_exit("Invalid reference wavelength");
398 
399  slit_ident = dfs_get_parameter_bool(parlist,
400  "fors.vimos_calib.slit_ident", NULL);
401 
402  sdegree = dfs_get_parameter_int(parlist, "fors.vimos_calib.sdegree", NULL);
403  ddegree = dfs_get_parameter_int(parlist, "fors.vimos_calib.ddegree", NULL);
404  sradius = dfs_get_parameter_int(parlist, "fors.vimos_calib.sradius", NULL);
405  dradius = dfs_get_parameter_int(parlist, "fors.vimos_calib.dradius", NULL);
406 
407  if (sradius < 1 || dradius < 1)
408  vimos_calib_exit("Invalid smoothing box radius");
409 
410  qc = dfs_get_parameter_bool(parlist, "fors.vimos_calib.qc", NULL);
411 
412  check = dfs_get_parameter_bool(parlist, "fors.vimos_calib.check", NULL);
413 
414  cpl_table_delete(grism_table); grism_table = NULL;
415 
416  if (cpl_error_get_code())
417  vimos_calib_exit("Failure getting the configuration parameters");
418 
419 
420  /*
421  * Check input set-of-frames
422  */
423 
424  cpl_msg_indent_less();
425  cpl_msg_info(recipe, "Check input set-of-frames:");
426  cpl_msg_indent_more();
427 
428  if (!dfs_equal_keyword(frameset, "ESO OCS CON QUAD"))
429  vimos_calib_exit("Input frames are not from the same quadrant");
430 
431  mos = cpl_frameset_count_tags(frameset, "MOS_ARC_SPECTRUM");
432 
433  if (mos == 0)
434  vimos_calib_exit("Missing input arc lamp frame");
435 
436  if (mos > 1)
437  vimos_calib_exit("Just one input arc lamp frame is allowed");
438 
439  arc_tag = "MOS_ARC_SPECTRUM";
440  flat_tag = "MOS_SCREEN_FLAT";
441  master_screen_flat_tag = "MOS_COMBINED_SCREEN_FLAT";
442  master_norm_flat_tag = "MOS_MASTER_SCREEN_FLAT";
443  reduced_lamp_tag = "MOS_ARC_SPECTRUM_EXTRACTED";
444  disp_residuals_tag = "MOS_DISP_RESIDUALS";
445  disp_coeff_tag = "MOS_DISP_COEFF";
446  wavelength_map_tag = "MOS_WAVELENGTH_MAP";
447  spectra_detection_tag = "MOS_SPECTRA_DETECTION";
448  spectral_resolution_tag = "MOS_SPECTRAL_RESOLUTION";
449  slit_map_tag = "MOS_SLIT_MAP";
450  curv_traces_tag = "MOS_CURV_TRACES";
451  curv_coeff_tag = "MOS_CURV_COEFF";
452  spatial_map_tag = "MOS_SPATIAL_MAP";
453  slit_location_tag = "MOS_SLIT_LOCATION";
454  disp_residuals_table_tag = "MOS_DISP_RESIDUALS_TABLE";
455  delta_image_tag = "MOS_DELTA_IMAGE";
456 
457  nbias = 0;
458  if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0) {
459  if (cpl_frameset_count_tags(frameset, "BIAS") == 0)
460  vimos_calib_exit("Missing required input: MASTER_BIAS or BIAS");
461  nbias = cpl_frameset_count_tags(frameset, "BIAS");
462  }
463 
464  if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
465  vimos_calib_exit("Too many in input: MASTER_BIAS");
466 
467  if (cpl_frameset_count_tags(frameset, "LINE_CATALOG") == 0)
468  vimos_calib_exit("Missing required input: LINE_CATALOG");
469 
470  if (cpl_frameset_count_tags(frameset, "LINE_CATALOG") > 1)
471  vimos_calib_exit("Too many in input: LINE_CATALOG");
472 
473  nflats = cpl_frameset_count_tags(frameset, flat_tag);
474 
475  if (nflats < 1) {
476  cpl_msg_error(recipe, "Missing required input: %s", flat_tag);
477  vimos_calib_exit(NULL);
478  }
479 
480  cpl_msg_indent_less();
481 
482  if (nflats > 1)
483  cpl_msg_info(recipe, "Load %d flat field frames and sum them...",
484  nflats);
485  else
486  cpl_msg_info(recipe, "Load flat field exposure...");
487 
488  cpl_msg_indent_more();
489 
490  header = dfs_load_header(frameset, flat_tag, 0);
491 
492  if (header == NULL)
493  vimos_calib_exit("Cannot load flat field frame header");
494 
495  alltime = cpl_propertylist_get_double(header, "EXPTIME");
496 
497  if (cpl_error_get_code() != CPL_ERROR_NONE)
498  vimos_calib_exit("Missing keyword EXPTIME in flat field frame header");
499 
500  cpl_propertylist_delete(header);
501 
502  for (i = 1; i < nflats; i++) {
503 
504  header = dfs_load_header(frameset, NULL, 0);
505 
506  if (header == NULL)
507  vimos_calib_exit("Cannot load flat field frame header");
508 
509  alltime += cpl_propertylist_get_double(header, "EXPTIME");
510 
511  if (cpl_error_get_code() != CPL_ERROR_NONE)
512  vimos_calib_exit("Missing keyword EXPTIME in flat field "
513  "frame header");
514 
515  cpl_propertylist_delete(header);
516 
517  }
518 
519  master_flat = dfs_load_image(frameset, flat_tag, CPL_TYPE_FLOAT, 0, 0);
520 
521  if (master_flat == NULL)
522  vimos_calib_exit("Cannot load flat field");
523 
524  for (i = 1; i < nflats; i++) {
525  flat = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
526  if (flat) {
527  cpl_image_add(master_flat, flat);
528  cpl_image_delete(flat); flat = NULL;
529  }
530  else
531  vimos_calib_exit("Cannot load flat field");
532  }
533 
534 
535  /*
536  * Get some info from arc lamp header
537  */
538 
539  header = dfs_load_header(frameset, arc_tag, 0);
540 
541  if (header == NULL)
542  vimos_calib_exit("Cannot load arc lamp header");
543 
544  instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
545  if (instrume == NULL)
546  vimos_calib_exit("Missing keyword INSTRUME in arc lamp header");
547  instrume = cpl_strdup(instrume);
548 
549  arctime = cpl_propertylist_get_double(header, "EXPTIME");
550 
551  quadrant = cpl_propertylist_get_int(header, "ESO OCS CON QUAD");
552 
553  switch (quadrant) {
554  case 1:
555  key_gris_name = "ESO INS GRIS1 NAME";
556  key_gris_id = "ESO INS GRIS1 ID";
557  key_filt_name = "ESO INS FILT1 NAME";
558  key_filt_id = "ESO INS FILT1 ID";
559  key_mask_id = "ESO INS MASK1 ID";
560  break;
561  case 2:
562  key_gris_name = "ESO INS GRIS2 NAME";
563  key_gris_id = "ESO INS GRIS2 ID";
564  key_filt_name = "ESO INS FILT2 NAME";
565  key_filt_id = "ESO INS FILT2 ID";
566  key_mask_id = "ESO INS MASK2 ID";
567  break;
568  case 3:
569  key_gris_name = "ESO INS GRIS3 NAME";
570  key_gris_id = "ESO INS GRIS3 ID";
571  key_filt_name = "ESO INS FILT3 NAME";
572  key_filt_id = "ESO INS FILT3 ID";
573  key_mask_id = "ESO INS MASK3 ID";
574  break;
575  case 4:
576  key_gris_name = "ESO INS GRIS4 NAME";
577  key_gris_id = "ESO INS GRIS4 ID";
578  key_filt_name = "ESO INS FILT4 NAME";
579  key_filt_id = "ESO INS FILT4 ID";
580  key_mask_id = "ESO INS MASK4 ID";
581  break;
582  }
583 
584  grism = cpl_strdup(cpl_propertylist_get_string(header, key_gris_name));
585 
586  if (cpl_error_get_code() != CPL_ERROR_NONE)
587  vimos_calib_exit("Missing keyword ESO INS GRISn NAME in arc lamp "
588  "frame header");
589 
590  cpl_msg_info(recipe, "The grism is: %s", grism);
591 
592 /*
593  if (!dfs_equal_keyword(frameset, key_gris_id))
594  vimos_calib_exit("Input frames are not from the same grism");
595 
596  if (!dfs_equal_keyword(frameset, key_filt_id))
597  vimos_calib_exit("Input frames are not from the same filter");
598 */
599 
600  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
601 
602  if (cpl_error_get_code() != CPL_ERROR_NONE)
603  vimos_calib_exit("Missing keyword ESO DET OUT1 CONAD in arc lamp "
604  "frame header");
605 
606  cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
607 
608  cpl_msg_info(recipe, "Produce mask slit position table...");
609 
610  maskslits = mos_load_slits_vimos(header);
611 
612  /*
613  * Check if all slits have the same X offset: in such case,
614  * treat the observation as a long-slit one!
615  */
616 
617  mxpos = cpl_table_get_column_median(maskslits, "ytop");
618  xpos = cpl_table_get_data_double(maskslits, "ytop");
619  nslits = cpl_table_get_nrow(maskslits);
620 
621  treat_as_lss = 1;
622  for (i = 0; i < nslits; i++) {
623  if (fabs(mxpos-xpos[i]) > 0.01) {
624  treat_as_lss = 0;
625  break;
626  }
627  }
628 
629  if (treat_as_lss) {
630  cpl_msg_warning(recipe, "All MOS slits have the same offset: %.2f\n"
631  "The long-slit data reduction strategy is applied!",
632  mxpos);
633  cpl_table_delete(maskslits); maskslits = NULL;
634  }
635 
636  if (slit_ident == 0) {
637  cpl_table_delete(maskslits); maskslits = NULL;
638  }
639 
640 
641  /* Leave the header on for the next step... */
642 
643 
644  /*
645  * Remove the master bias
646  */
647 
648  if (nbias) {
649 
650  /*
651  * Set of raw BIASes in input, need to create master bias!
652  */
653 
654  cpl_msg_info(recipe, "Generate the master from input raw biases...");
655 
656  if (nbias > 1) {
657 
658  biases = cpl_imagelist_new();
659 
660  bias = dfs_load_image(frameset, "BIAS", CPL_TYPE_FLOAT, 0, 0);
661 
662  if (bias == NULL)
663  vimos_calib_exit("Cannot load bias frame");
664 
665  cpl_imagelist_set(biases, bias, 0); bias = NULL;
666 
667  for (i = 1; i < nbias; i++) {
668  bias = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
669  if (bias) {
670  cpl_imagelist_set(biases, bias, i); bias = NULL;
671  }
672  else
673  vimos_calib_exit("Cannot load bias frame");
674  }
675 
676  master_bias = cpl_imagelist_collapse_median_create(biases);
677 
678  cpl_imagelist_delete(biases);
679  }
680  else
681  master_bias = dfs_load_image(frameset, "BIAS",
682  CPL_TYPE_FLOAT, 0, 1);
683 
684  }
685  else {
686  master_bias = dfs_load_image(frameset, "MASTER_BIAS",
687  CPL_TYPE_FLOAT, 0, 1);
688  if (master_bias == NULL)
689  vimos_calib_exit("Cannot load master bias");
690  }
691 
692  cpl_msg_info(recipe, "Remove the master bias...");
693 
694  overscans = mos_load_overscans_vimos(header, 1);
695  cpl_propertylist_delete(header); header = NULL;
696 
697  if (nbias) {
698  int xlow = cpl_table_get_int(overscans, "xlow", 0, NULL);
699  int ylow = cpl_table_get_int(overscans, "ylow", 0, NULL);
700  int xhig = cpl_table_get_int(overscans, "xhig", 0, NULL);
701  int yhig = cpl_table_get_int(overscans, "yhig", 0, NULL);
702  dummy = cpl_image_extract(master_bias, xlow+1, ylow+1, xhig, yhig);
703  cpl_image_delete(master_bias); master_bias = dummy;
704 
705  if (dfs_save_image(frameset, master_bias, "MASTER_BIAS",
706  NULL, parlist, recipe, version))
707  vimos_calib_exit(NULL);
708  }
709 
710  if (nflats > 1) {
711  multi_bias = cpl_image_multiply_scalar_create(master_bias, nflats);
712  dummy = mos_remove_bias(master_flat, multi_bias, overscans);
713  cpl_image_delete(multi_bias);
714  }
715  else {
716  dummy = mos_remove_bias(master_flat, master_bias, overscans);
717  }
718  cpl_image_delete(master_flat);
719  master_flat = dummy;
720 
721  if (master_flat == NULL)
722  vimos_calib_exit("Cannot remove bias from flat field");
723 
724  cpl_msg_indent_less();
725  cpl_msg_info(recipe, "Load arc lamp exposure...");
726  cpl_msg_indent_more();
727 
728  spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
729 
730  if (spectra == NULL)
731  vimos_calib_exit("Cannot load arc lamp exposure");
732 
733  cpl_msg_info(recipe, "Remove the master bias...");
734 
735  dummy = mos_remove_bias(spectra, master_bias, overscans);
736  cpl_table_delete(overscans); overscans = NULL;
737  cpl_image_delete(master_bias); master_bias = NULL;
738  cpl_image_delete(spectra); spectra = dummy;
739 
740  if (spectra == NULL)
741  vimos_calib_exit("Cannot remove bias from arc lamp exposure");
742 
743  cpl_msg_indent_less();
744  cpl_msg_info(recipe, "Load input line catalog...");
745  cpl_msg_indent_more();
746 
747  wavelengths = dfs_load_table(frameset, "LINE_CATALOG", 1);
748 
749  if (wavelengths == NULL)
750  vimos_calib_exit("Cannot load line catalog");
751 
752 
753  /*
754  * Cast the wavelengths into a (double precision) CPL vector
755  */
756 
757  nlines = cpl_table_get_nrow(wavelengths);
758 
759  if (nlines == 0)
760  vimos_calib_exit("Empty input line catalog");
761 
762  if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
763  cpl_msg_error(recipe, "Missing column %s in input line catalog table",
764  wcolumn);
765  vimos_calib_exit(NULL);
766  }
767 
768  line = cpl_malloc(nlines * sizeof(double));
769 
770  for (i = 0; i < nlines; i++)
771  line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
772 
773  cpl_table_delete(wavelengths); wavelengths = NULL;
774 
775  lines = cpl_vector_wrap(nlines, line);
776 
777 
778  /*
779  * Rotate frames horizontally with red to the right
780  */
781 
782  cpl_image_turn(spectra, rotate);
783  cpl_image_turn(master_flat, rotate);
784 
785  ccd_xsize = nx = cpl_image_get_size_x(spectra); // added...
786  ccd_ysize = ny = cpl_image_get_size_y(spectra);
787 
788  if (treat_as_lss) {
789 
790  /*
791  * In the case of LSS data, find first a "one slit"
792  * solution. This will be later on split into many-slits
793  * solutions. This is done for greater accuracy.
794  */
795 
796  cpl_msg_indent_less();
797  cpl_msg_info(recipe, "Perform wavelength calibration...");
798  cpl_msg_indent_more();
799 
800  wavemap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
801  if (check)
802  residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
803 
804  fiterror = cpl_calloc(ny, sizeof(double));
805  fitlines = cpl_calloc(ny, sizeof(int));
806  idscoeff = cpl_table_new(ny);
807  refmask = cpl_mask_new(nx, ny);
808 
809  if (mos_saturation_process(spectra))
810  vimos_calib_exit("Cannot process saturation");
811 
812  if (mos_subtract_background(spectra))
813  vimos_calib_exit("Cannot subtract the background");
814 
815  rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
816  peakdetection, wradius,
817  wdegree, wreject, reference,
818  &startwavelength,
819  &endwavelength, fitlines,
820  fiterror, idscoeff, wavemap,
821  residual, NULL, refmask);
822 
823  if (rectified == NULL)
824  vimos_calib_exit("Wavelength calibration failure.");
825 
826  if (!cpl_table_has_valid(idscoeff, "c0"))
827  vimos_calib_exit("Wavelength calibration failure.");
828 
829  /*
830  * This is necessary to move on to a many-slits solution
831  */
832 
833  mos_refmask_find_gaps(refmask, master_flat, 1000.);
834 
835  if (wmode) {
836  cpl_image_delete(rectified); rectified = NULL;
837  cpl_image_delete(wavemap); wavemap = NULL;
838  mos_interpolate_wavecalib(idscoeff, wavemap, wmode);
839  wavemap = mos_map_idscoeff(idscoeff, nx, reference,
840  startwavelength, endwavelength);
841  rectified = mos_wavelength_calibration(spectra, reference,
842  startwavelength,
843  endwavelength, dispersion,
844  idscoeff, 0);
845  }
846 
847  cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
848  cpl_table_set_column_unit(idscoeff, "error", "pixel");
849  cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
850 
851  for (i = 0; i < ny; i++)
852  if (!cpl_table_is_valid(idscoeff, "c0", i))
853  cpl_table_set_invalid(idscoeff, "error", i);
854 
855  slit_ident = 0;
856  idscoeff_lss = idscoeff;
857 
858  }
859  else {
860 
861  /*
862  * Here the generic MOS calibration is carried out.
863  */
864 
865  /*
866  * Detecting spectra on the CCD
867  */
868 
869  cpl_msg_indent_less();
870  cpl_msg_info(recipe, "Detecting spectra on CCD...");
871  cpl_msg_indent_more();
872 
873  ccd_xsize = nx = cpl_image_get_size_x(spectra);
874  ccd_ysize = ny = cpl_image_get_size_y(spectra);
875 
876  refmask = cpl_mask_new(nx, ny);
877 
878  if (mos_saturation_process(spectra))
879  vimos_calib_exit("Cannot process saturation");
880 
881  if (mos_subtract_background(spectra))
882  vimos_calib_exit("Cannot subtract the background");
883 
884  checkwave = mos_wavelength_calibration_raw(spectra, lines, dispersion,
885  peakdetection, wradius,
886  wdegree, wreject, reference,
887  &startwavelength,
888  &endwavelength,
889  NULL, NULL, NULL, NULL,
890  NULL, NULL, refmask);
891 
892  if (checkwave == NULL)
893  vimos_calib_exit("Wavelength calibration failure.");
894 
895  /*
896  * Save check image to disk
897  */
898 
899  header = cpl_propertylist_new();
900  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
901  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
902  cpl_propertylist_update_double(header, "CRVAL1",
903  startwavelength + dispersion/2);
904  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
905  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
906  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
907  cpl_propertylist_update_double(header, "CD1_1", dispersion);
908  cpl_propertylist_update_double(header, "CD1_2", 0.0);
909  cpl_propertylist_update_double(header, "CD2_1", 0.0);
910  cpl_propertylist_update_double(header, "CD2_2", 1.0);
911  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
912  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
913 
914  if (check) {
915  if (dfs_save_image(frameset, checkwave, spectra_detection_tag,
916  header, parlist, recipe, version))
917  vimos_calib_exit(NULL);
918  }
919 
920  cpl_image_delete(checkwave); checkwave = NULL;
921  cpl_propertylist_delete(header); header = NULL;
922 
923  }
924 
925  cpl_msg_info(recipe, "Locate slits at reference wavelength on CCD...");
926  slits = mos_locate_spectra(refmask);
927 
928  if (!slits) {
929  cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
930  vimos_calib_exit("No slits could be detected!");
931  }
932 
933  refimage = cpl_image_new_from_mask(refmask);
934  cpl_mask_delete(refmask); refmask = NULL;
935 
936  if (check) {
937  save_header = dfs_load_header(frameset, arc_tag, 0);
938  cpl_image_turn(refimage, rotate_back);
939  if (dfs_save_image(frameset, refimage, slit_map_tag, NULL,
940  parlist, recipe, version))
941  vimos_calib_exit(NULL);
942  cpl_propertylist_delete(save_header); save_header = NULL;
943  }
944 
945  cpl_image_delete(refimage); refimage = NULL;
946 
947  if (slit_ident) {
948 
949  /*
950  * Attempt slit identification: this recipe may continue even
951  * in case of failed identification (i.e., the position table is
952  * not produced, but an error is not set). In case of failure,
953  * the spectra would be still extracted, even if they would not
954  * be associated to slits on the mask.
955  *
956  * The reason for making the slit identification an user option
957  * (via the parameter slit_ident) is to offer the possibility
958  * to avoid identifications that are only apparently successful,
959  * as it would happen in the case of an incorrect slit description
960  * in the data header.
961  */
962 
963  cpl_msg_indent_less();
964  cpl_msg_info(recipe, "Attempt slit identification (optional)...");
965  cpl_msg_indent_more();
966 
967  mos_rotate_slits(maskslits, -rotate, 0, 0);
968  positions = mos_identify_slits(slits, maskslits, NULL);
969 
970  if (positions) {
971  cpl_table_delete(slits);
972  slits = positions;
973 
974  /*
975  * Eliminate slits which are _entirely_ outside the CCD
976  */
977 
978  cpl_table_and_selected_double(slits,
979  "ybottom", CPL_GREATER_THAN, ny-1);
980  cpl_table_or_selected_double(slits,
981  "ytop", CPL_LESS_THAN, 0);
982  cpl_table_erase_selected(slits);
983 
984  nslits = cpl_table_get_nrow(slits);
985 
986  if (nslits == 0)
987  vimos_calib_exit("No slits found on the CCD");
988 
989  cpl_msg_info(recipe, "%d slits are entirely or partially "
990  "contained in CCD", nslits);
991 
992  }
993  else {
994  slit_ident = 0;
995  cpl_msg_info(recipe, "Global distortion model cannot be computed");
996  if (cpl_error_get_code() != CPL_ERROR_NONE) {
997  vimos_calib_exit(NULL);
998  }
999  }
1000  }
1001 
1002 
1003  /*
1004  * Determination of spectral curvature
1005  */
1006 
1007  cpl_msg_indent_less();
1008  cpl_msg_info(recipe, "Determining spectral curvature...");
1009  cpl_msg_indent_more();
1010 
1011  cpl_msg_info(recipe, "Tracing master flat field spectra edges...");
1012  traces = mos_trace_flat(master_flat, slits, reference,
1013  startwavelength, endwavelength, dispersion);
1014 
1015  if (!traces)
1016  vimos_calib_exit("Tracing failure");
1017 
1018  cpl_msg_info(recipe, "Fitting flat field spectra edges...");
1019  polytraces = mos_poly_trace(slits, traces, cdegree);
1020 
1021  if (!polytraces)
1022  vimos_calib_exit("Trace fitting failure");
1023 
1024  if (cmode) {
1025  cpl_msg_info(recipe, "Computing global spectral curvature model...");
1026  mos_global_trace(slits, polytraces, cmode);
1027  }
1028 
1029  if (dfs_save_table(frameset, traces, curv_traces_tag, NULL, parlist,
1030  recipe, version))
1031  vimos_calib_exit(NULL);
1032 
1033  cpl_table_delete(traces); traces = NULL;
1034 
1035  coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1036  spatial = mos_spatial_calibration(spectra, slits, polytraces, reference,
1037  startwavelength, endwavelength,
1038  dispersion, 0, coordinate);
1039 
1040 // if (!slit_ident) {
1041 // cpl_image_delete(spectra); spectra = NULL;
1042 // }
1043 
1044  /*
1045  * Flat field normalisation is done directly on the master flat
1046  * field (without spatial rectification first). The spectral
1047  * curvature model may be provided in input, in future releases.
1048  */
1049 
1050  cpl_msg_indent_less();
1051  cpl_msg_info(recipe, "Perform flat field normalisation...");
1052  cpl_msg_indent_more();
1053 
1054  norm_flat = cpl_image_duplicate(master_flat);
1055 
1056  smo_flat = mos_normalise_flat(norm_flat, coordinate, slits, polytraces,
1057  reference, startwavelength, endwavelength,
1058  dispersion, dradius, ddegree);
1059 
1060  cpl_image_delete(smo_flat); smo_flat = NULL; /* It may be a product */
1061 
1062  save_header = dfs_load_header(frameset, flat_tag, 0);
1063  cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM", nflats);
1064 
1065  cpl_image_turn(norm_flat, rotate_back);
1066 
1067  if (dfs_save_image(frameset, norm_flat, master_norm_flat_tag,
1068  save_header, parlist, recipe, version))
1069  vimos_calib_exit(NULL);
1070 
1071  cpl_image_delete(norm_flat); norm_flat = NULL;
1072 
1073 
1074  /*
1075  * QC parameters for flat
1076  */
1077 
1078  if (qc) {
1079  double scale;
1080  double slit_width;
1081  double flux, flux_err;
1082  int cslit = mos_slit_closest_to_center(slits, nx, ny);
1083 
1084 
1085  /*
1086  * Refresh base property list, because previous saving
1087  * modified it - e.g., the keyword ARCFILE is missing
1088  */
1089 
1090  cpl_propertylist_delete(save_header);
1091  save_header = dfs_load_header(frameset, flat_tag, 0);
1092  cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM", nflats);
1093 
1094 
1095  /*
1096  * QC1 group header
1097  */
1098 
1099  fors_qc_start_group(save_header, "1.1", instrume);
1100 
1101  if (fors_qc_write_string("PRO.CATG", master_norm_flat_tag,
1102  "Product category", instrume))
1103  vimos_calib_exit("Cannot write product category to QC log file");
1104 
1105  if (fors_qc_keyword_to_paf(save_header, "ARCFILE", NULL,
1106  "Archive File Name", instrume))
1107  vimos_calib_exit("Missing keyword ARCFILE in flatfield frame");
1108 
1109  if (fors_qc_keyword_to_paf(save_header, "ESO TPL ID", NULL,
1110  "Template signature ID", instrume))
1111  vimos_calib_exit("Missing keyword TPL ID in flatfield frame");
1112 
1113  if (fors_qc_keyword_to_paf(save_header, "ESO OCS CON QUAD", NULL,
1114  "Quadrant", instrume))
1115  vimos_calib_exit("Missing keyword OCS CON QUAD in flatfield frame");
1116 
1117  if (fors_qc_keyword_to_paf(save_header, key_filt_name, NULL,
1118  "Filter", instrume)) {
1119  cpl_msg_error(recipe, "Missing keyword %s in flatfield frame",
1120  key_filt_name);
1121  vimos_calib_exit(NULL);
1122  }
1123 
1124  if (fors_qc_keyword_to_paf(save_header, key_gris_name, NULL,
1125  "Grism", instrume)) {
1126  cpl_msg_error(recipe, "Missing keyword %s in flatfield frame",
1127  key_gris_name);
1128  vimos_calib_exit(NULL);
1129  }
1130 
1131  if (fors_qc_keyword_to_paf(save_header, key_mask_id, NULL,
1132  "Mask", instrume)) {
1133  cpl_msg_error(recipe, "Missing keyword %s in flatfield frame",
1134  key_mask_id);
1135  vimos_calib_exit(NULL);
1136  }
1137 
1138  cpl_propertylist_update_double(save_header,
1139  "ESO PRO WLEN CEN", reference);
1140 
1141  if (fors_qc_keyword_to_paf(save_header, "ESO PRO WLEN CEN", "Angstrom",
1142  "Reference wavelength", instrume))
1143  vimos_calib_exit("Missing keyword PRO WLEN CEN in flatfield frame");
1144 
1145 /* Unused in the old VIMOS pipeline:
1146 
1147  if (fors_qc_keyword_to_paf(save_header, "ESO DPR TYPE", NULL,
1148  "DPR type", instrume))
1149  vimos_calib_exit("Missing keyword DPR TYPE in flat field frame");
1150 
1151  if (fors_qc_keyword_to_paf(save_header, key_gris_id, NULL,
1152  "Grism identifier", instrume)) {
1153  cpl_msg_error(recipe, "Missing keyword %s in arc "
1154  "lamp header", key_gris_id);
1155  vimos_calib_exit(NULL);
1156  }
1157 
1158  if (cpl_propertylist_has(save_header, key_filt_name))
1159  fors_qc_keyword_to_paf(save_header, key_filt_name, NULL,
1160  "Filter name", instrume);
1161 
1162  if (fors_qc_keyword_to_paf(save_header, "ESO DET CHIP1 ID", NULL,
1163  "Chip identifier", instrume))
1164  vimos_calib_exit("Missing keyword DET CHIP1 ID in arc "
1165  "lamp header");
1166 */
1167 
1168  pipefile = dfs_generate_filename_tfits(master_norm_flat_tag);
1169  if (fors_qc_write_string("PIPEFILE", pipefile,
1170  "Pipeline product name", instrume))
1171  vimos_calib_exit("Cannot write PIPEFILE to QC log file");
1172  cpl_free(pipefile); pipefile = NULL;
1173 
1174  scale = cpl_propertylist_get_double(save_header, "ESO TEL FOCU SCALE");
1175 
1176  if (cpl_error_get_code()) {
1177  cpl_error_reset();
1178  scale = 1.718;
1179  cpl_msg_warning(recipe, "Cannot read keyword TEL FOCU SCALE "
1180  "(defaulted to %f arcsec/mm)", scale);
1181  }
1182 
1183  /*
1184  * QC1 parameters
1185  */
1186 
1187  keyname = "QC.MOS.SLIT.WIDTH";
1188 
1189  slit_width = scale * cpl_table_get(slits, "ywidth", cslit, NULL);
1190 
1191  if (fors_qc_write_qc_double(save_header, slit_width, keyname, "arcsec",
1192  "Width of slit closest to center",
1193  instrume)) {
1194  vimos_calib_exit("Cannot write slit width to QC log file");
1195  }
1196 
1197  mos_extract_flux(master_flat, slits, 2, gain, &flux, &flux_err);
1198 
1199  flux_err /= alltime; // The master is simply the sum of all flats
1200  flux /= alltime;
1201 
1202  cpl_msg_info(recipe,
1203  "Flux at wavelength %.2f: %.2f +/- %.2f ADU/mm^2/s\n",
1204  reference, flux, flux_err);
1205 
1206  keyname = "QC.MOS.FLAT.FLUX";
1207 
1208  if (fors_qc_write_qc_double(save_header, flux, keyname, "ADU/mm^2/s",
1209  "Flux at reference wavelength",
1210  instrume)) {
1211  vimos_calib_exit("Cannot write QC.MOS.FLAT.FLUX to QC log file");
1212  }
1213 
1214  keyname = "QC.MOS.FLAT.FLUXERR";
1215 
1216  if (fors_qc_write_qc_double(save_header, flux_err, keyname,
1217  "ADU/mm^2/s",
1218  "Error on flux at reference wavelength",
1219  instrume)) {
1220  vimos_calib_exit("Cannot write QC.MOS.FLAT.FLUXERR to QC log file");
1221  }
1222 
1224 
1225  }
1226 
1227  cpl_image_turn(master_flat, rotate_back);
1228  if (dfs_save_image(frameset, master_flat, master_screen_flat_tag,
1229  save_header, parlist, recipe, version))
1230  vimos_calib_exit(NULL);
1231 
1232  cpl_image_delete(master_flat); master_flat = NULL;
1233 
1234  cpl_propertylist_delete(save_header); save_header = NULL;
1235 
1236 
1237  /*
1238  * Final wavelength calibration of spectra having their curvature
1239  * removed
1240  */
1241 
1242  cpl_msg_indent_less();
1243  cpl_msg_info(recipe, "Perform final wavelength calibration...");
1244  cpl_msg_indent_more();
1245 
1246  nx = cpl_image_get_size_x(spatial);
1247  ny = cpl_image_get_size_y(spatial);
1248 
1249  idscoeff = cpl_table_new(ny);
1250  restable = cpl_table_new(nlines);
1251  rainbow = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1252  if (check)
1253  residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
1254  fiterror = cpl_calloc(ny, sizeof(double));
1255  fitlines = cpl_calloc(ny, sizeof(int));
1256 
1257  rectified = mos_wavelength_calibration_final(spatial, slits, lines,
1258  dispersion, peakdetection,
1259  wradius, wdegree, wreject,
1260  reference, &startwavelength,
1261  &endwavelength, fitlines,
1262  fiterror, idscoeff, rainbow,
1263  residual, restable, NULL);
1264 
1265  if (rectified == NULL)
1266  vimos_calib_exit("Wavelength calibration failure.");
1267 
1268  if (dfs_save_table(frameset, restable, disp_residuals_table_tag, NULL,
1269  parlist, recipe, version))
1270  vimos_calib_exit(NULL);
1271 
1272  cpl_table_delete(restable); restable = NULL;
1273 
1274  cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
1275  cpl_table_set_column_unit(idscoeff, "error", "pixel");
1276  cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
1277 
1278  if (treat_as_lss) {
1279 
1280  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1281 
1282  int *position = cpl_table_get_data_int (slits, "position");
1283  int *length = cpl_table_get_data_int (slits, "length");
1284  double *ytop = cpl_table_get_data_double(slits, "ytop");
1285  double *ybottom = cpl_table_get_data_double(slits, "ybottom");
1286 
1287  int j, k, dr, irow;
1288 
1289  /*
1290  * Replace the LSS solutions to the poorer MOS solutions
1291  */
1292 
1293  for (j = 0, i = ny - 1; i >= 0; i--) {
1294  if (i < position[j]) {
1295  ++j;
1296  }
1297  dr = position[j] + length[j] - i - 1;
1298  irow = floor(ytop[j] - dr*(ytop[j] - ybottom[j])/length[j] + 0.5);
1299  for (k = 0; k <= wdegree; k++) {
1300  cpl_table_set_double(idscoeff, clab[k], i,
1301  cpl_table_get_double(idscoeff_lss, clab[k], irow, NULL));
1302  }
1303  cpl_table_set_double(idscoeff, "error", i,
1304  cpl_table_get_double(idscoeff_lss, "error", irow, NULL));
1305  cpl_table_set_int(idscoeff, "nlines", i,
1306  cpl_table_get_int(idscoeff_lss, "nlines", irow, NULL));
1307  }
1308 
1309  cpl_table_delete(idscoeff_lss);
1310 
1311  cpl_image_delete(rectified);
1312 
1313  rectified = mos_wavelength_calibration(spatial, reference,
1314  startwavelength, endwavelength,
1315  dispersion, idscoeff, 0);
1316  }
1317  else {
1318  for (i = 0; i < ny; i++)
1319  if (!cpl_table_is_valid(idscoeff, "c0", i))
1320  cpl_table_set_invalid(idscoeff, "error", i);
1321  }
1322 
1323  cpl_image_delete(spatial); spatial = NULL;
1324 
1325  delta = mos_map_pixel(idscoeff, reference, startwavelength,
1326  endwavelength, dispersion, 2);
1327 
1328  header = cpl_propertylist_new();
1329  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1330  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1331  cpl_propertylist_update_double(header, "CRVAL1",
1332  startwavelength + dispersion/2);
1333  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1334  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
1335  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
1336  cpl_propertylist_update_double(header, "CD1_1", dispersion);
1337  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1338  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1339  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1340  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1341  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1342 
1343  if (dfs_save_image(frameset, delta, delta_image_tag,
1344  header, parlist, recipe, version))
1345  vimos_calib_exit(NULL);
1346 
1347  cpl_image_delete(delta); delta = NULL;
1348  cpl_propertylist_delete(header); header = NULL;
1349 
1350  mean_rms = mos_distortions_rms(rectified, lines, startwavelength,
1351  dispersion, 6, 0);
1352 
1353  cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
1354 
1355  mean_rms = cpl_table_get_column_mean(idscoeff, "error");
1356 
1357  cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
1358  mean_rms, mean_rms * dispersion);
1359 
1360  restab = mos_resolution_table(rectified, startwavelength, dispersion,
1361  60000, lines);
1362 
1363  if (restab) {
1364  cpl_msg_info(recipe, "Mean spectral resolution: %.2f",
1365  cpl_table_get_column_mean(restab, "resolution"));
1366  cpl_msg_info(recipe, "Mean reference lines FWHM: %.2f +/- %.2f pixel",
1367  cpl_table_get_column_mean(restab, "fwhm") / dispersion,
1368  cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
1369 
1370  if (qc) {
1371 
1372  header = dfs_load_header(frameset, arc_tag, 0);
1373 
1374  if (header == NULL)
1375  vimos_calib_exit("Cannot reload arc lamp header");
1376 
1377  qclist = cpl_propertylist_new();
1378 
1379  fors_qc_start_group(qclist, "1.1", instrume);
1380 
1381 
1382  /*
1383  * QC1 group header
1384  */
1385 
1386  if (fors_qc_write_string("PRO.CATG", spectral_resolution_tag,
1387  "Product category", instrume))
1388  vimos_calib_exit("Cannot write product category to "
1389  "QC log file");
1390 
1391  if (fors_qc_keyword_to_paf(header, "ESO DPR TYPE", NULL,
1392  "DPR type", instrume))
1393  vimos_calib_exit("Missing keyword DPR TYPE in arc "
1394  "lamp header");
1395 
1396  if (fors_qc_keyword_to_paf(header, "ESO TPL ID", NULL,
1397  "Template", instrume))
1398  vimos_calib_exit("Missing keyword TPL ID in arc "
1399  "lamp header");
1400 
1401  if (fors_qc_keyword_to_paf(header, key_gris_name, NULL,
1402  "Grism name", instrume)) {
1403  cpl_msg_error(recipe, "Missing keyword %s in arc "
1404  "lamp header", key_gris_name);
1405  vimos_calib_exit(NULL);
1406  }
1407 
1408  if (fors_qc_keyword_to_paf(header, key_gris_id, NULL,
1409  "Grism identifier", instrume)) {
1410  cpl_msg_error(recipe, "Missing keyword %s in arc "
1411  "lamp header", key_gris_id);
1412  vimos_calib_exit(NULL);
1413  }
1414 
1415  if (cpl_propertylist_has(header, key_filt_name))
1416  fors_qc_keyword_to_paf(header, key_filt_name, NULL,
1417  "Filter name", instrume);
1418 
1419  if (fors_qc_keyword_to_paf(header, "ESO DET CHIP1 ID", NULL,
1420  "Chip identifier", instrume))
1421  vimos_calib_exit("Missing keyword DET CHIP1 ID in arc "
1422  "lamp header");
1423 
1424  if (fors_qc_keyword_to_paf(header, "ARCFILE", NULL,
1425  "Archive name of input data",
1426  instrume))
1427  vimos_calib_exit("Missing keyword ARCFILE in arc "
1428  "lamp header");
1429 
1430  cpl_propertylist_delete(header); header = NULL;
1431 
1432  pipefile = dfs_generate_filename_tfits(spectral_resolution_tag);
1433  if (fors_qc_write_string("PIPEFILE", pipefile,
1434  "Pipeline product name", instrume))
1435  vimos_calib_exit("Cannot write PIPEFILE to QC log file");
1436  cpl_free(pipefile); pipefile = NULL;
1437 
1438 
1439  /*
1440  * QC1 parameters
1441  */
1442 
1443  keyname = "QC.MOS.RESOLUTION";
1444 
1445  if (fors_qc_write_qc_double(qclist,
1446  cpl_table_get_column_mean(restab,
1447  "resolution"),
1448  keyname,
1449  "Angstrom",
1450  "Mean spectral resolution",
1451  instrume)) {
1452  vimos_calib_exit("Cannot write mean spectral resolution to QC "
1453  "log file");
1454  }
1455 
1456  keyname = "QC.MOS.RESOLUTION.RMS";
1457 
1458  if (fors_qc_write_qc_double(qclist,
1459  cpl_table_get_column_stdev(restab,
1460  "resolution"),
1461  "QC.MOS.RESOLUTION.RMS",
1462  "Angstrom",
1463  "Scatter of spectral resolution",
1464  instrume)) {
1465  vimos_calib_exit("Cannot write spectral resolution scatter "
1466  "to QC log file");
1467  }
1468 
1469  keyname = "QC.MOS.RESOLUTION.NLINES";
1470 
1471  if (fors_qc_write_qc_int(qclist, cpl_table_get_nrow(restab) -
1472  cpl_table_count_invalid(restab,
1473  "resolution"),
1474  "QC.MOS.RESOLUTION.NLINES",
1475  NULL,
1476  "Number of lines for spectral resolution "
1477  "computation",
1478  instrume)) {
1479  vimos_calib_exit("Cannot write number of lines used in "
1480  "spectral resolution computation "
1481  "to QC log file");
1482  }
1483 
1485 
1486  }
1487 
1488  if (dfs_save_table(frameset, restab, spectral_resolution_tag, qclist,
1489  parlist, recipe, version))
1490  vimos_calib_exit(NULL);
1491 
1492  cpl_propertylist_delete(qclist); qclist = NULL;
1493 
1494  }
1495  else
1496  vimos_calib_exit("Cannot compute the spectral resolution table");
1497 
1498  cpl_vector_delete(lines); lines = NULL;
1499 
1500  if (dfs_save_table(frameset, idscoeff, disp_coeff_tag, NULL,
1501  parlist, recipe, version))
1502  vimos_calib_exit(NULL);
1503 
1504  /*
1505  * Global distortion models
1506  */
1507 
1508  if (slit_ident) {
1509 
1510  cpl_msg_info(recipe, "Computing global distortions model");
1511  global = mos_global_distortion(slits, maskslits, idscoeff,
1512  polytraces, reference);
1513 
1514  if (global && 0) {
1515  cpl_table *stest;
1516  cpl_table *ctest;
1517  cpl_table *dtest;
1518  cpl_image *itest;
1519 
1520  stest = mos_build_slit_location(global, maskslits, ccd_ysize);
1521 
1522  ctest = mos_build_curv_coeff(global, maskslits, stest);
1523  if (dfs_save_table(frameset, ctest, "CURVS", NULL,
1524  parlist, recipe, version))
1525  vimos_calib_exit(NULL);
1526 
1527  itest = mos_spatial_calibration(spectra, stest, ctest,
1528  reference, startwavelength,
1529  endwavelength, dispersion,
1530  0, NULL);
1531  cpl_table_delete(ctest); ctest = NULL;
1532  cpl_image_delete(itest); itest = NULL;
1533  if (dfs_save_table(frameset, stest, "SLITS", NULL,
1534  parlist, recipe, version))
1535  vimos_calib_exit(NULL);
1536 
1537  dtest = mos_build_disp_coeff(global, stest);
1538  if (dfs_save_table(frameset, dtest, "DISPS", NULL,
1539  parlist, recipe, version))
1540  vimos_calib_exit(NULL);
1541 
1542  cpl_table_delete(dtest); dtest = NULL;
1543  cpl_table_delete(stest); stest = NULL;
1544  }
1545 
1546  if (global) {
1547  if (dfs_save_table(frameset, global, global_distortion_tag, NULL,
1548  parlist, recipe, version))
1549  vimos_calib_exit(NULL);
1550  cpl_table_delete(global); global = NULL;
1551  }
1552 
1553 // cpl_image_delete(spectra); spectra = NULL;
1554  cpl_table_delete(maskslits); maskslits = NULL;
1555  }
1556 
1557  header = dfs_load_header(frameset, arc_tag, 0);
1558  cpl_propertylist_update_double(header, "CRPIX1", 1.0);
1559  cpl_propertylist_update_double(header, "CRPIX2", 1.0);
1560  cpl_propertylist_update_double(header, "CRVAL1",
1561  startwavelength + dispersion/2);
1562  cpl_propertylist_update_double(header, "CRVAL2", 1.0);
1563  /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
1564  cpl_propertylist_update_double(header, "CDELT2", 1.0); */
1565  cpl_propertylist_update_double(header, "CD1_1", dispersion);
1566  cpl_propertylist_update_double(header, "CD1_2", 0.0);
1567  cpl_propertylist_update_double(header, "CD2_1", 0.0);
1568  cpl_propertylist_update_double(header, "CD2_2", 1.0);
1569  cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
1570  cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
1571  cpl_propertylist_update_int(header, "ESO PRO DATANCOM", 1);
1572 
1573  if (qc) {
1574 
1575  double scale;
1576  double slit_width;
1577  float lambdaHe;
1578  float lambdaNe;
1579  float lambdaAr;
1580  float lambdaRed;
1581  float lambdaYel;
1582  float lambdaBlu;
1583  double flux, flux_err, resol, resol_err;
1584  int selected;
1585  int cslit = mos_slit_closest_to_center(slits, nx, ny);
1586 
1587 
1588  /*
1589  * QC1 group header
1590  */
1591 
1592  fors_qc_start_group(header, "1.1", instrume);
1593 
1594  if (fors_qc_write_string("PRO.CATG", master_norm_flat_tag,
1595  "Product category", instrume))
1596  vimos_calib_exit("Cannot write product category to QC log file");
1597 
1598  if (fors_qc_keyword_to_paf(header, "ARCFILE", NULL,
1599  "Archive File Name", instrume))
1600  vimos_calib_exit("Missing keyword ARCFILE in arc lamp frame");
1601 
1602  if (fors_qc_keyword_to_paf(header, "ESO TPL ID", NULL,
1603  "Template signature ID", instrume))
1604  vimos_calib_exit("Missing keyword TPL ID in arc lamp frame");
1605 
1606  if (fors_qc_keyword_to_paf(header, "ESO OCS CON QUAD", NULL,
1607  "Quadrant", instrume))
1608  vimos_calib_exit("Missing keyword OCS CON QUAD in arc lamp frame");
1609 
1610  if (fors_qc_keyword_to_paf(header, key_filt_name, NULL,
1611  "Filter", instrume)) {
1612  cpl_msg_error(recipe, "Missing keyword %s in arc lamp frame",
1613  key_filt_name);
1614  vimos_calib_exit(NULL);
1615  }
1616 
1617  if (fors_qc_keyword_to_paf(header, key_gris_name, NULL,
1618  "Grism", instrume)) {
1619  cpl_msg_error(recipe, "Missing keyword %s in arc lamp frame",
1620  key_gris_name);
1621  vimos_calib_exit(NULL);
1622  }
1623 
1624  if (fors_qc_keyword_to_paf(header, key_mask_id, NULL,
1625  "Mask", instrume)) {
1626  cpl_msg_error(recipe, "Missing keyword %s in arc lamp frame",
1627  key_mask_id);
1628  vimos_calib_exit(NULL);
1629  }
1630 
1631  cpl_propertylist_update_double(header,
1632  "ESO PRO WLEN CEN", reference);
1633 
1634  if (fors_qc_keyword_to_paf(header, "ESO PRO WLEN CEN", "Angstrom",
1635  "Reference wavelength", instrume))
1636  vimos_calib_exit("Missing keyword PRO WLEN CEN in arc lamp frame");
1637 
1638  pipefile = dfs_generate_filename_tfits(master_norm_flat_tag);
1639  if (fors_qc_write_string("PIPEFILE", pipefile,
1640  "Pipeline product name", instrume))
1641  vimos_calib_exit("Cannot write PIPEFILE to QC log file");
1642  cpl_free(pipefile); pipefile = NULL;
1643 
1644  scale = cpl_propertylist_get_double(header, "ESO TEL FOCU SCALE");
1645 
1646  if (cpl_error_get_code()) {
1647  cpl_error_reset();
1648  scale = 1.718;
1649  cpl_msg_warning(recipe, "Cannot read keyword TEL FOCU SCALE "
1650  "(defaulted to %f arcsec/mm)", scale);
1651  }
1652 
1653  /*
1654  * QC1 parameters
1655  */
1656 
1657  keyname = "QC.MOS.SLIT.WIDTH";
1658 
1659  slit_width = scale * cpl_table_get(slits, "ywidth", cslit, NULL);
1660 
1661  if (fors_qc_write_qc_double(header, slit_width, keyname, "arcsec",
1662  "Width of slit closest to center",
1663  instrume)) {
1664  vimos_calib_exit("Cannot write slit width to QC log file");
1665  }
1666 
1667  if (grism[0] == 'L') {
1668  if (grism[3] == 'r') { /* LR_red */
1669  lambdaHe = 7065.19;
1670  lambdaNe = 0.0;
1671  lambdaAr = 7723.80;
1672  lambdaRed = 9122.97;
1673  lambdaYel = 7635.11;
1674  lambdaBlu = 5875.62;
1675  }
1676  if (grism[3] == 'b') { /* LR_blue */
1677  lambdaHe = 5015.68;
1678  lambdaNe = 6598.96;
1679  lambdaAr = 0.0;
1680  lambdaRed = 6598.95;
1681  lambdaYel = 5015.68;
1682  lambdaBlu = 3888.65;
1683  }
1684  }
1685 
1686  if (grism[0] == 'M') { /* MR */
1687  lambdaHe = 7065.19;
1688  lambdaNe = 7032.41;
1689  lambdaAr = 7723.80;
1690  lambdaRed = 8264.521;
1691  lambdaYel = 6678.200;
1692  lambdaBlu = 5015.675;
1693  }
1694 
1695  if (grism[0] == 'H') {
1696  if (grism[3] == 'r') { /* HR_red */
1697  lambdaHe = 7065.19;
1698  lambdaNe = 7032.41;
1699  lambdaAr = 7723.80;
1700  lambdaRed = 9122.966;
1701  lambdaYel = 7948.175;
1702  lambdaBlu = 6929.468;
1703  }
1704  if (grism[3] == 'o') { /* HR_orange */
1705  lambdaHe = 7065.19;
1706  lambdaNe = 7032.41;
1707  lambdaAr = 7723.80;
1708  lambdaRed = 7948.175;
1709  lambdaYel = 6929.468;
1710  lambdaBlu = 5875.618;
1711  }
1712  if (grism[3] == 'b') { /* HR_blue */
1713  lambdaHe = 5015.68;
1714  lambdaNe = 5944.83;
1715  lambdaAr = 0.0;
1716  lambdaRed = 6598.953;
1717  lambdaYel = 5875.618;
1718  lambdaBlu = 5015.675;
1719  }
1720  }
1721 
1722  if (lambdaHe > 1.) {
1723  mos_extract_flux_mapped(rectified, slits, lambdaHe,
1724  startwavelength, dispersion,
1725  4, gain, &flux, &flux_err);
1726 
1727  flux /= arctime;
1728  flux_err /= arctime;
1729 
1730  cpl_msg_info(recipe, "Flux of He %.2f: %.2f +/- %.2f ADU/mm^2/s",
1731  lambdaHe, flux, flux_err);
1732 
1733  keyname = "QC.MOS.HE.LAMBDA";
1734 
1735  if (fors_qc_write_qc_double(header, lambdaHe, keyname, "Angstrom",
1736  "He arc lamp line for flux determination", instrume)) {
1737  vimos_calib_exit("Cannot write He arc line to QC log file");
1738  }
1739 
1740  keyname = "QC.MOS.HE.FLUX";
1741 
1742  if (fors_qc_write_qc_double(header, flux, keyname, "ADU/mm^2/s",
1743  "Flux at chosen He wavelength", instrume)) {
1744  vimos_calib_exit("Cannot write He flux to QC log file");
1745  }
1746 
1747  keyname = "QC.MOS.HE.FLUXERR";
1748 
1749  if (fors_qc_write_qc_double(header, flux_err, keyname, "ADU/mm^2/s",
1750  "Error on flux at chosen He wavelength", instrume)) {
1751  vimos_calib_exit("Cannot write He flux error to QC log file");
1752  }
1753  }
1754  else
1755  cpl_msg_warning(recipe,
1756  "No He lines in %s spectral range: corresponding "
1757  "QC1 parameters are not computed.", grism);
1758 
1759  if (lambdaNe > 1.) {
1760  mos_extract_flux_mapped(rectified, slits, lambdaNe,
1761  startwavelength, dispersion,
1762  4, gain, &flux, &flux_err);
1763 
1764  flux /= arctime;
1765  flux_err /= arctime;
1766 
1767  cpl_msg_info(recipe, "Flux of Ne %.2f: %.2f +/- %.2f ADU/mm^2/s",
1768  lambdaNe, flux, flux_err);
1769 
1770  keyname = "QC.MOS.NE.LAMBDA";
1771 
1772  if (fors_qc_write_qc_double(header, lambdaNe, keyname, "Angstrom",
1773  "Ne arc lamp line for flux determination", instrume)) {
1774  vimos_calib_exit("Cannot write Ne arc line to QC log file");
1775  }
1776 
1777  keyname = "QC.MOS.NE.FLUX";
1778 
1779  if (fors_qc_write_qc_double(header, flux, keyname, "ADU/mm^2/s",
1780  "Flux at chosen Ne wavelength", instrume)) {
1781  vimos_calib_exit("Cannot write Ne flux to QC log file");
1782  }
1783 
1784  keyname = "QC.MOS.NE.FLUXERR";
1785 
1786  if (fors_qc_write_qc_double(header, flux_err, keyname, "ADU/mm^2/s",
1787  "Error on flux at chosen Ne wavelength", instrume)) {
1788  vimos_calib_exit("Cannot write Ne flux error to QC log file");
1789  }
1790  }
1791  else
1792  cpl_msg_warning(recipe,
1793  "No Ne lines in %s spectral range: corresponding "
1794  "QC1 parameters are not computed.", grism);
1795 
1796  if (lambdaAr > 1.) {
1797  mos_extract_flux_mapped(rectified, slits, lambdaAr,
1798  startwavelength, dispersion,
1799  4, gain, &flux, &flux_err);
1800 // mos_extract_flux(spectra, slits, 3, gain, &flux, &flux_err);
1801 
1802  flux /= arctime;
1803  flux_err /= arctime;
1804 
1805  cpl_msg_info(recipe, "Flux of Ar %.2f: %.2f +/- %.2f ADU/mm^2/s",
1806  lambdaAr, flux, flux_err);
1807 
1808  keyname = "QC.MOS.AR.LAMBDA";
1809 
1810  if (fors_qc_write_qc_double(header, lambdaAr, keyname, "Angstrom",
1811  "Ar arc lamp line for flux determination", instrume)) {
1812  vimos_calib_exit("Cannot write Ar arc line to QC log file");
1813  }
1814 
1815  keyname = "QC.MOS.AR.FLUX";
1816 
1817  if (fors_qc_write_qc_double(header, flux, keyname, "ADU/mm^2/s",
1818  "Flux at chosen Ar wavelength", instrume)) {
1819  vimos_calib_exit("Cannot write Ar flux to QC log file");
1820  }
1821 
1822  keyname = "QC.MOS.AR.FLUXERR";
1823 
1824  if (fors_qc_write_qc_double(header, flux_err, keyname, "ADU/mm^2/s",
1825  "Error on flux at chosen Ar wavelength", instrume)) {
1826  vimos_calib_exit("Cannot write Ar flux error to QC log file");
1827  }
1828  }
1829  else
1830  cpl_msg_warning(recipe,
1831  "No Ar lines in %s spectral range: corresponding "
1832  "QC1 parameters are not computed.", grism);
1833 
1834  /*
1835  * IDS coefficients
1836  */
1837 
1838  for (i = 0; i <= wdegree; i++) {
1839  char *label = cpl_sprintf("c%d", i);
1840  char *unit;
1841  char *comment;
1842  double mcoeff;
1843 
1844 
1845  mcoeff = 0.0; // Zero by definition when i == 0
1846  if (i) {
1847  if (mos_median_in_slit(idscoeff, slits,
1848  cslit, label, &mcoeff)) {
1849  cpl_free(label);
1850  break;
1851  }
1852  }
1853 
1854  keyname = cpl_sprintf("QC.MOS.WAVECAL.COEFF%d", i);
1855 
1856  switch (i) {
1857  case 0:
1858  unit = cpl_strdup("pixel");
1859  break;
1860  case 1:
1861  unit = cpl_strdup("pixel/Angstrom");
1862  break;
1863  default:
1864  unit = cpl_sprintf("pixel/Angstrom^%d", i);
1865  break;
1866  }
1867 
1868  comment = cpl_sprintf("Median coefficient %d of IDS", i);
1869 
1870  if (fors_qc_write_qc_double(header, mcoeff, keyname,
1871  unit, comment, instrume)) {
1872  vimos_calib_exit("Cannot write IDS coefficient to QC logfile");
1873  }
1874 
1875  cpl_free(keyname);
1876  cpl_free(comment);
1877  cpl_free(unit);
1878  cpl_free(label);
1879  }
1880 
1881  /*
1882  * These parameters are now useless, I set them to zero.
1883  */
1884 
1885  keyname = "QC.MOS.REFWAVE.MEAN";
1886 
1887  if (fors_qc_write_qc_double(header, 0.0, keyname, "pixel",
1888  "MEAN of CCD positions of reference wavelength", instrume)) {
1889  vimos_calib_exit("Cannot write QC.MOS.REFWAVE.MEAN to QC logfile");
1890  }
1891 
1892  keyname = "QC.MOS.REFWAVE.RMS";
1893 
1894  if (fors_qc_write_qc_double(header, 0.0, keyname, "pixel",
1895  "RMS of CCD positions of reference wavelength", instrume)) {
1896  vimos_calib_exit("Cannot write QC.MOS.REFWAVE.RMS to QC logfile");
1897  }
1898 
1899  if (restab) {
1900 
1901  /*
1902  * About spectral resolution:
1903  */
1904 
1905  keyname = "QC.MOS.RESOLUTION1.LAMBDA";
1906 
1907  if (fors_qc_write_qc_double(header, lambdaRed, keyname, "Angstrom",
1908  "Line used in spectral resolution determination", instrume)) {
1909  vimos_calib_exit("Cannot write arc line to QC log file");
1910  }
1911 
1912  keyname = "QC.MOS.RESOLUTION1";
1913 
1914  cpl_table_and_selected_double(restab, "wavelength",
1915  CPL_GREATER_THAN, lambdaRed - 1.0);
1916  selected =
1917  cpl_table_and_selected_double(restab, "wavelength",
1918  CPL_LESS_THAN, lambdaRed + 1.0);
1919 
1920  if (selected == 1) {
1921  cpl_table *one_line = cpl_table_extract_selected(restab);
1922 
1923  resol = cpl_table_get_double(one_line,
1924  "resolution", 0, NULL);
1925  resol_err = cpl_table_get_double(one_line,
1926  "resolution_rms", 0, NULL);
1927 
1928  cpl_table_delete(one_line);
1929  }
1930  else {
1931  resol = 0.0;
1932  resol_err = 0.0;
1933  }
1934 
1935  cpl_table_select_all(restab);
1936 
1937  cpl_msg_info(recipe, "Spectral resolution at %.2f: %.2f +/- %.2f",
1938  lambdaRed, resol, resol_err);
1939 
1940  if (fors_qc_write_qc_double(header, resol, keyname, NULL,
1941  "Mean spectral resolution at red end of spectrum", instrume)) {
1942  vimos_calib_exit("Cannot write spectral resolution "
1943  "to QC log file");
1944  }
1945 
1946  keyname = "QC.MOS.RESOLUTION1.RMS";
1947 
1948  if (fors_qc_write_qc_double(header, resol_err, keyname, NULL,
1949  "Error on mean spectral resolution", instrume)) {
1950  vimos_calib_exit("Cannot write error on resolution "
1951  "to QC log file");
1952  }
1953 
1954  keyname = "QC.MOS.RESOLUTION2.LAMBDA";
1955 
1956  if (fors_qc_write_qc_double(header, lambdaYel, keyname, "Angstrom",
1957  "Line used in spectral resolution determination", instrume)) {
1958  vimos_calib_exit("Cannot write arc line to QC log file");
1959  }
1960 
1961  keyname = "QC.MOS.RESOLUTION2";
1962 
1963  cpl_table_and_selected_double(restab, "wavelength",
1964  CPL_GREATER_THAN, lambdaYel - 1.0);
1965  selected =
1966  cpl_table_and_selected_double(restab, "wavelength",
1967  CPL_LESS_THAN, lambdaYel + 1.0);
1968 
1969  if (selected == 1) {
1970  cpl_table *one_line = cpl_table_extract_selected(restab);
1971 
1972  resol = cpl_table_get_double(one_line,
1973  "resolution", 0, NULL);
1974  resol_err = cpl_table_get_double(one_line,
1975  "resolution_rms", 0, NULL);
1976 
1977  cpl_table_delete(one_line);
1978  }
1979  else {
1980  resol = 0.0;
1981  resol_err = 0.0;
1982  }
1983 
1984  cpl_table_select_all(restab);
1985 
1986  cpl_msg_info(recipe, "Spectral resolution at %.2f: %.2f +/- %.2f",
1987  lambdaYel, resol, resol_err);
1988 
1989  if (fors_qc_write_qc_double(header, resol, keyname, NULL,
1990  "Mean spectral resolution at center of spectrum", instrume)) {
1991  vimos_calib_exit("Cannot write spectral resolution "
1992  "to QC log file");
1993  }
1994 
1995  keyname = "QC.MOS.RESOLUTION2.RMS";
1996 
1997  if (fors_qc_write_qc_double(header, resol_err, keyname, NULL,
1998  "Error on mean spectral resolution", instrume)) {
1999  vimos_calib_exit("Cannot write error on resolution "
2000  "to QC log file");
2001  }
2002 
2003  keyname = "QC.MOS.RESOLUTION3.LAMBDA";
2004 
2005  if (fors_qc_write_qc_double(header, lambdaBlu, keyname, "Angstrom",
2006  "Line used in spectral resolution determination", instrume)) {
2007  vimos_calib_exit("Cannot write arc line to QC log file");
2008  }
2009 
2010  keyname = "QC.MOS.RESOLUTION3";
2011 
2012  cpl_table_and_selected_double(restab, "wavelength",
2013  CPL_GREATER_THAN, lambdaBlu - 1.0);
2014  selected =
2015  cpl_table_and_selected_double(restab, "wavelength",
2016  CPL_LESS_THAN, lambdaBlu + 1.0);
2017 
2018  if (selected == 1) {
2019  cpl_table *one_line = cpl_table_extract_selected(restab);
2020 
2021  resol = cpl_table_get_double(one_line,
2022  "resolution", 0, NULL);
2023  resol_err = cpl_table_get_double(one_line,
2024  "resolution_rms", 0, NULL);
2025 
2026  cpl_table_delete(one_line);
2027  }
2028  else {
2029  resol = 0.0;
2030  resol_err = 0.0;
2031  }
2032 
2033  cpl_table_select_all(restab);
2034 
2035  cpl_msg_info(recipe, "Spectral resolution at %.2f: %.2f +/- %.2f",
2036  lambdaBlu, resol, resol_err);
2037 
2038  if (fors_qc_write_qc_double(header, resol, keyname, NULL,
2039  "Mean spectral resolution at blue end of spectrum", instrume)) {
2040  vimos_calib_exit("Cannot write spectral resolution "
2041  "to QC log file");
2042  }
2043 
2044  keyname = "QC.MOS.RESOLUTION3.RMS";
2045 
2046  if (fors_qc_write_qc_double(header, resol_err, keyname, NULL,
2047  "Error on mean spectral resolution", instrume)) {
2048  vimos_calib_exit("Cannot write error on resolution "
2049  "to QC log file");
2050  }
2051 
2052  keyname = "QC.MOS.IDS.RMS";
2053 
2054  if (fors_qc_write_qc_double(header, mean_rms, keyname, "pixel",
2055  "Mean accuracy of dispersion solution", instrume)) {
2056  vimos_calib_exit("Cannot write mean accuracy of "
2057  "dispersion solution to QC log file");
2058  }
2059  }
2060 
2062 
2063  }
2064 
2065  cpl_free(grism); grism = NULL;
2066  cpl_free(instrume); instrume = NULL;
2067  cpl_table_delete(restab); restab = NULL;
2068  cpl_table_delete(idscoeff); idscoeff = NULL;
2069  cpl_image_delete(spectra); spectra = NULL;
2070 
2071  if (dfs_save_image(frameset, rectified, reduced_lamp_tag, header,
2072  parlist, recipe, version))
2073  vimos_calib_exit(NULL);
2074 
2075  cpl_image_delete(rectified); rectified = NULL;
2076  cpl_propertylist_delete(header); header = NULL;
2077 
2078  if (check) {
2079  save_header = dfs_load_header(frameset, arc_tag, 0);
2080 
2081  cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
2082  cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
2083  /* cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
2084  cpl_propertylist_update_double(save_header, "CD1_1", 1.0);
2085  cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
2086  cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
2087  cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
2088  cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
2089  cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
2090 
2091  if (dfs_save_image(frameset, residual, disp_residuals_tag, save_header,
2092  parlist, recipe, version))
2093  vimos_calib_exit(NULL);
2094 
2095  cpl_image_delete(residual); residual = NULL;
2096  cpl_propertylist_delete(save_header); save_header = NULL;
2097  }
2098 
2099  if (!treat_as_lss) {
2100 
2101  /*
2102  * Wavemap was already produced if treat_as_lss = true
2103  */
2104 
2105  wavemap = mos_map_wavelengths(coordinate, rainbow, slits,
2106  polytraces, reference,
2107  startwavelength, endwavelength,
2108  dispersion);
2109 
2110  cpl_image_delete(rainbow); rainbow = NULL;
2111  }
2112 
2113  save_header = dfs_load_header(frameset, arc_tag, 0);
2114 
2115  cpl_image_turn(wavemap, rotate_back);
2116  if (dfs_save_image(frameset, wavemap, wavelength_map_tag, save_header,
2117  parlist, recipe, version))
2118  vimos_calib_exit(NULL);
2119 
2120  cpl_image_delete(wavemap); wavemap = NULL;
2121 
2122  cpl_image_turn(coordinate, rotate_back);
2123  if (dfs_save_image(frameset, coordinate, spatial_map_tag, save_header,
2124  parlist, recipe, version))
2125  vimos_calib_exit(NULL);
2126 
2127  cpl_image_delete(coordinate); coordinate = NULL;
2128  cpl_propertylist_delete(save_header); save_header = NULL;
2129 
2130  if (dfs_save_table(frameset, polytraces, curv_coeff_tag, NULL,
2131  parlist, recipe, version))
2132  vimos_calib_exit(NULL);
2133 
2134  cpl_table_delete(polytraces); polytraces = NULL;
2135 
2136  mos_rotate_slits(slits, rotate, ccd_ysize, ccd_xsize);
2137  if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
2138  parlist, recipe, version))
2139  vimos_calib_exit(NULL);
2140 
2141  cpl_table_delete(slits); slits = NULL;
2142 
2143  if (cpl_error_get_code()) {
2144  cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
2145  vimos_calib_exit(NULL);
2146  }
2147 
2148  return 0;
2149 }
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_table * mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
Build the IDS coefficients table from a global distortions table.
Definition: moses.c:1845
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
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_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, cpl_image *wavemap, int mode, int degree)
Interpolate LSS wavelength calibration.
Definition: moses.c:3061
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_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_error_code fors_qc_write_qc_double(cpl_propertylist *header, double value, const char *name, const char *unit, const char *comment, const char *instrument)
Write an integer value to the active QC1 PAF object and to a header.
Definition: fors_qc.c:604
cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
Rotate a slit location table.
Definition: moses.c:6074
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_error_code fors_qc_keyword_to_paf(cpl_propertylist *header, const char *name, const char *unit, const char *comment, const char *instrument)
Copy a keyword value to the currently active QC1 PAF object.
Definition: fors_qc.c:425
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_error_code mos_subtract_background(cpl_image *image)
Subtract the background.
Definition: moses.c:16079
cpl_error_code fors_qc_start_group(cpl_propertylist *header, const char *qcdic_version, const char *instrument)
Initiate a new QC1 group.
Definition: fors_qc.c:77
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
cpl_error_code fors_qc_write_string(const char *name, const char *value, const char *comment, const char *instrument)
Add string parameter to current QC1 group.
Definition: fors_qc.c:235
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 mos_extract_flux(cpl_image *image, cpl_table *slits, double xwidth, double ywidth, int dx, double gain, double *o_flux, double *o_err)
Measure flux from spectral interval on CCD.
Definition: moses.c:18694
cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces, int mode)
Recompute tracing coefficients globally.
Definition: moses.c:8083
cpl_image * mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference, double blue, double red)
Create a wavelengths map from an IDS coefficients table.
Definition: moses.c:10976
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 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 fors_qc_end_group(void)
Close current QC1 PAF file.
Definition: fors_qc.c:200
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
cpl_table * mos_global_distortion(cpl_table *slits, cpl_table *maskslits, cpl_table *ids, cpl_table *crv, double reference)
Determine all global distortions models.
Definition: moses.c:1179
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_table * mos_load_slits_vimos(cpl_propertylist *header)
Create slit location table from FITS header of VIMOS data.
Definition: moses.c:15216
cpl_table * mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits, cpl_table *slits)
Build the curvature coefficients table from a global distortions table.
Definition: moses.c:1690
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 mos_saturation_process(cpl_image *image)
Process saturation.
Definition: moses.c:16013
int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
Return slit closest to CCD center.
Definition: moses.c:18646
int vimos_calib_impl(cpl_frameset *frameset, cpl_parameterlist *parlist)
Interpret the command line options and execute the data processing.
cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits, double xwidth, double ywidth, double lambda, double startwave, double dispersion, int dx, double gain, double *o_flux, double *o_err)
Measure flux from spectral interval on remapped frame.
Definition: moses.c:18811
cpl_table * mos_load_overscans_vimos(const cpl_propertylist *header, int check_consistency)
Get the overscan positions from FITS header of VIMOS data.
Definition: moses.c:15426
int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit, char *label, double *mvalue)
Compute median from a table column section corresponding to a slit.
Definition: moses.c:18921
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