UVES Pipeline Reference Manual  5.4.0
uves_wavecal_body.c
1 /* *
2  * This file is part of the ESO UVES Pipeline *
3  * Copyright (C) 2004,2005 European Southern Observatory *
4  * *
5  * This library is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA *
18  * */
19 
20 /*
21  * $Author: amodigli $
22  * $Date: 2013-08-08 13:36:47 $
23  * $Revision: 1.103 $
24  * $Name: not supported by cvs2svn $
25  *
26  */
27 
28 /*-----------------------------------------------------------------------------
29  Includes
30  -----------------------------------------------------------------------------*/
31 
32 #ifdef HAVE_CONFIG_H
33 # include <config.h>
34 #endif
35 /*----------------------------------------------------------------------------*/
39 /*----------------------------------------------------------------------------*/
40 
41 #include <uves_wavecal_body.h>
42 
43 /* Definitions */
44 #include <uves.h>
45 
46 /* Macro steps */
47 #include <uves_extract.h>
48 #include <uves_flatfield.h>
49 #include <uves_wavecal_search.h>
50 #include <uves_wavecal_firstsolution.h>
51 #include <uves_wavecal_identify.h>
52 #include <uves_rebin.h>
53 #include <uves_merge.h>
54 
55 /* Utility functions */
56 #include <uves_wavecal_utils.h>
57 #include <uves_utils.h>
58 #include <uves_utils_wrappers.h>
59 #include <uves_plot.h>
60 #include <uves_parameters.h>
61 #include <uves_dfs.h>
62 #include <uves_pfits.h>
63 #include <uves_qclog.h>
64 #include <uves_recipe.h>
65 #include <uves_error.h>
66 #include <flames_reduce_vcorrel.h>
67 
68 #include <cpl.h>
69 
70 #include <stdbool.h>
71 #include <float.h>
72 #include <string.h>
73 /*-----------------------------------------------------------------------------
74  Defines
75  -----------------------------------------------------------------------------*/
76 /* threshold values for maximum pixel saturation */
77 #define DRS_PTHRES_MAX 55000
78 #define DRS_PTHRES_MIN -20
79 #define DRS_CVEL_MIN -6.
80 #define DRS_CVEL_MAX +6.
81 #define N_FIBRES_MAX 9
82 /*-----------------------------------------------------------------------------
83  Functions prototypes
84  -----------------------------------------------------------------------------*/
85 static void uves_wavecal_qclog(const cpl_table* table,
86  int firstabs,
87  int lastabs,
88  const cpl_image *arclamp,
89  const uves_propertylist* raw_header,
90  bool flames,
91  int trace_number,
92  int fibre_mask,
93  double offset,
94  enum uves_chip chip,
95  cpl_table* qclog);
96 
97 static void uves_wavecal_qclog_intmon(cpl_table* table,
98  const cpl_table *line_intmon,
99  const uves_propertylist* raw_header,
100  bool flames,
101  int fibre,
102  enum uves_chip chip,
103  cpl_table* qclog);
104 
105 /*-----------------------------------------------------------------------------
106  Recipe standard code
107  -----------------------------------------------------------------------------*/
108 
109 const char * const uves_wavecal_desc_short = "Performs the wavelength calibration";
110 
111 const char * const uves_wavecal_desc =
112 "The recipe performs a wavelength calibration for each extraction window.\n"
113 "Conceptually, each chip contains a number of order lines, each of which\n"
114 "contains a number of fibre traces, each of which contains a number of\n"
115 "extraction windows. For UVES data, there is only one trace per order and\n"
116 "three extraction windows (sky, object, sky). For FLAMES/UVES data there\n"
117 "are multiple traces per order but only one extraction window per trace.\n"
118 "The number of traces is defined in the order table while the geometry of\n"
119 "the extraction windows is specified by recipe parameters (see below).\n"
120 "\n"
121 "Expected input for this recipe is an arc lamp frame, ARC_LAMP_xxx or\n"
122 "ECH_ARC_LAMP_xxx (where xxx=BLUE, RED), order table(s) for each chip,\n"
123 "ORDER_TABLE_xxxx (where xxxx=BLUE, REDL, REDU), 'guess' line table(s)\n"
124 "for each chip, LINE_TABLE_xxxx, a wavelength catalogue table, \n"
125 "LINE_REFER_TABLE, and optionally a wavelength table of bright lines,\n"
126 "LINE_INTMON_TABLE, used only for computing Quality Control parameters.\n"
127 "\n"
128 "The output line table(s), LINE_TABLE_xxxx, contains the columns\n"
129 "X : Horizontal position (from Gaussian fit) of detected line\n"
130 "dX : Uncertainty (one sigma) of X\n"
131 "Ynew : Vertical position of detected line\n"
132 "XWidth : Width (in pixels) of detected line from Gaussian fit\n"
133 "Peak : Intensity of detected line\n"
134 "Background : Fitted background (ADU) of detected line\n"
135 "Slope : Linear background slope (ADU/pixel) of detected line\n"
136 " from Gaussian fit\n"
137 "Intensity : Intensity of detected line scaled to unit exposure\n"
138 " time. (This column only present if a LINE_INTMON_TABLE\n"
139 " is provided.)\n"
140 "Order : Absolute order number of detected line\n"
141 "Y : Relative order number of detected line\n"
142 " (it's not a very descriptive column name)\n"
143 "WaveC : Wavelength of this line (computed using the resulting\n"
144 " dispersion relation)\n"
145 "dLambdaC : Uncertainty (one sigma) of 'WaveC'.\n"
146 "Pixel : The width in w.l.u. of a pixel (computed locally).\n"
147 "Residual : Residual (in w.l.u.) of this line\n"
148 "Residual_pix : Residual (in pixels) of this line\n"
149 "Lambda_candidate : Nearest line in catalogue\n"
150 "dLambda_cat_sq : Squared distance to nearest catalogue line\n"
151 "dLambda_nn_sq : Squared distance to nearest neighbour multiplied by ALPHA\n"
152 "Ident : The wavelength associated with this emission line,\n"
153 " or invalid if this line was not identified\n"
154 "dIdent : Uncertainty of catalogue wavelength\n"
155 "Select : 1 if the line was identified, 0 otherwise\n"
156 "NLinSol : 1 if the line was identified and accepted for the\n"
157 " polynomial fit, 0 otherwise\n"
158 "Intensity : Intensity of detected line scaled to unit exposure\n"
159 " time. (This column is present only if a LINE_INTMON_TABLE\n"
160 " is provided.)\n"
161 "\n"
162 "The 2nd table extension contains the dispersion relation (a 2d polynomial).\n"
163 "The 3rd table extension contains the map from (pixel, pixel)-space to\n"
164 " physical order numbers (used internally by the calibration recipe; \n"
165 "another 2d polynomial).\n"
166 "\n"
167 "If there is more than one extraction window, the results of each calibration\n"
168 "is stored in subsequent table extensions of the same FITS file. For \n"
169 "example, extensions 4, 5 and 6 would contain the resulting line table \n"
170 "(and its two associated polynomials) for the second extraction window. \n"
171 "The results for the calibration of the n'th extraction window is stored \n"
172 "in extensions (3*n - 2) to 3*n.\n";
175 /*-----------------------------------------------------------------------------
176  Functions code
177  -----------------------------------------------------------------------------*/
178 
179 /*----------------------------------------------------------------------------*/
187 /*----------------------------------------------------------------------------*/
188 int
189 uves_wavecal_define_parameters_body(cpl_parameterlist *parameters,
190  const char *recipe_id, double slit)
191 {
192  const char *subcontext;
193 
194  /*****************
195  * General *
196  *****************/
197 
198  if (uves_define_global_parameters(parameters) != CPL_ERROR_NONE)
199  {
200  return -1;
201  }
202 
203  /*****************
204  * Extraction *
205  *****************/
206 
207  subcontext = NULL;
208 
209  /* nwindows */
210  uves_par_new_range("nwindows",
211  CPL_TYPE_INT,
212  "Number of extraction windows per trace. "
213  "The windows will be aligned (i.e. no overlap "
214  "and no spacing between adjacent windows). "
215  "Unless an offset is specified, the middle "
216  "window(s) is centered on the trace",
217  strcmp(recipe_id, "flames_cal_wavecal") == 0 ?
218  1 : 3, /* FLAMES: 1; UVES: 3 */
219  1, INT_MAX);
220 
221  /* length */
222  uves_par_new_range("length",
223  CPL_TYPE_DOUBLE,
224  "Length (in pixels) of each extraction window. "
225  "This parameter is also equal to the seperation of "
226  "adjacent window centers, causing the extraction "
227  "windows to always be aligned. The parameter is "
228  "automatically adjusted according to the binning "
229  "of the input raw frame. If negative, the extraction "
230  "window length is determined automatically "
231  "to cover the full slit",
232  slit, -2.0, DBL_MAX);
233 
234  /* offset */
235  uves_par_new_range("offset",
236  CPL_TYPE_DOUBLE,
237  "A global offset (in pixels) of all extraction windows",
238  0.0, -25., 25.);
239 
240  /* method */
241  if (uves_propagate_parameters_step(UVES_EXTRACT_ID, parameters,
242  recipe_id, NULL) != 0)
243  {
244  return -1;
245  }
246 
247  /* Override default optimal extraction profile. Assume constant profile */
248 #if 0
249  /* this should perhaps be enabled but doesn't work properly in the moment.
250 
251  ChangeLog:
252  uves_cal_wavecal: The arc lamp spectrum is now extracted using
253  average extraction and weighting each pixel with its inverse
254  variance. This is equivalent to doing an optimal extraction under
255  the assumption of a constant spatial profile, and is implemented
256  as such. This was a necessary change in order to be robust against
257  interorder noisy pixels caused by dividing by the flat-field.
258 
259  */
260  {
261  const char *profile = "constant";
262  double kappa = -1;
263 
264  if (uves_set_parameter_default(parameters,
265  recipe_id, UVES_EXTRACT_ID ".profile",
266  CPL_TYPE_STRING, &profile)
267  != CPL_ERROR_NONE)
268  {
269  return -1;
270  }
271 
272  /* Disable cosmic ray rejection, it does not work well for
273  this particular profile and very high S/N */
274  if (uves_set_parameter_default(parameters,
275  recipe_id, UVES_EXTRACT_ID ".kappa",
276  CPL_TYPE_DOUBLE, &kappa)
277  != CPL_ERROR_NONE)
278  {
279  return -1;
280  }
281  }
282 #else
283  {
284  const char *method = "average";
285 
286  if (uves_set_parameter_default(parameters,
287  recipe_id, UVES_EXTRACT_ID ".method",
288  CPL_TYPE_STRING, &method)
289  != CPL_ERROR_NONE)
290  {
291  return -1;
292  }
293 
294  }
295 #endif
296 
297  /*****************
298  * Search *
299  *****************/
300 
301  subcontext = "search";
302 
303  /* range */
304  uves_par_new_range("range",
305  CPL_TYPE_INT,
306  "Width (pix) of search window is 2*range + 1. "
307  "This parameter is automatically adjusted "
308  "according to binning.",
309  8, 1, INT_MAX);
310 
311  /* minlines */
312  uves_par_new_range("minlines",
313  CPL_TYPE_INT,
314  "Minimum number of lines to detect. If zero, "
315  "the default value (1100 for BLUE/REDL chips; "
316  "1000 for REDU chip) is used.",
317  0, 0, INT_MAX);
318 
319  /* maxlines */
320  uves_par_new_range("maxlines",
321  CPL_TYPE_INT,
322  "Maximum number of lines to detect. If zero, "
323  "the default value (1600 for BLUE/REDL chip; "
324  "1400 for REDU chip) is used.",
325  0, 0, INT_MAX);
326 
327  /* centeringmethod */
328 /* Temporally removed as 'gravity' do not work and it does not make
329  sense a parameter with only one option
330 
331  uves_par_new_enum("centeringmethod",
332  CPL_TYPE_STRING,
333  "Line centering method",
334  "gaussian", // Default
335  1, // Number of options
336  "gaussian"); // List of options
337 
338 */
339  /* old setting allowed gravity "gaussian", "gravity"); */
340 
341  /*******************
342  * First solution *
343  *******************/
344 
345  subcontext = "first";
346 
347  /* shiftmax */
348  uves_par_new_range("shiftmax",
349  CPL_TYPE_DOUBLE,
350  "The maximum shift (pix) in either direction compared to "
351  "guess solution. This parameter is automatically "
352  "corrected for binning",
353  10.0, 0.0, DBL_MAX);
354 
355  /* shiftstep */
356  uves_par_new_range("shiftstep",
357  CPL_TYPE_DOUBLE,
358  "The step size (pix) used when searching "
359  "for the optimum shift. This parameter is "
360  "automatically corrected for binning",
361  0.1, 0.0, DBL_MAX);
362 
363  /* shifttoler */
364  uves_par_new_range("shifttoler",
365  CPL_TYPE_DOUBLE,
366  "Tolerance (pix) when matching shifted lines. "
367  "This parameter is not adjusted according to binning",
368  0.05, 0.0, DBL_MAX);
369 
370 
371  /*****************
372  * Identify *
373  *****************/
374 
375  subcontext = "identify";
376 
377  /* alpha */
378  uves_par_new_range("alpha",
379  CPL_TYPE_DOUBLE,
380  "The parameter that controls the distance to the "
381  "nearest neighbours",
382  0.1, 0.0, 1.0);
383 
384  /* maxerror */
385  uves_par_new_range("maxerror",
386  CPL_TYPE_DOUBLE,
387  "This parameter controls the graceful exit of "
388  "the identification loop. If the RMS of the "
389  "global fit exceeds this value (pix) the "
390  "iteration stops",
391  20.0, 0.0, DBL_MAX);
392 
393  /* degree */
394  uves_par_new_range("degree",
395  CPL_TYPE_INT,
396  "Degrees of the global 2d dispersion polynomial. If "
397  "a negative number is specified, the polynomial "
398  "degrees are automatically selected by starting from "
399  "(1, 1) and inreasing the degrees as long as the RMS "
400  "residual decreases significantly",
401  4, -2, INT_MAX);
402 
403 
404  /*****************
405  * Calibration *
406  *****************/
407 
408  subcontext = "calibrate";
409 
410  /* tolerance */
411  uves_par_new_value("tolerance",
412  CPL_TYPE_DOUBLE,
413  "Tolerance of fit. If positive, the tolerance "
414  "is in pixel units. If negative, abs(tolerance) "
415  "is in wavelength units. Lines with residuals "
416  "worse than the tolerance are excluded from the "
417  "final fit. Unlike in previous versions, this "
418  "parameter is not corrected for CCD binning. "
419  "This rejection based on the absolute residual in "
420  "pixel can be effectively disabled by setting the "
421  "tolerance to a very large number (e.g. 9999). In "
422  "that case outliers will be rejected using only "
423  "kappa sigma clipping.",
424  0.6);
425 /* 0.07); */
426 /* 9999.0);*/
427 
428 
429  /* kappa */
430  uves_par_new_range("kappa",
431  CPL_TYPE_DOUBLE,
432  "Lines with residuals more then kappa stdev "
433  "are rejected from the final fit",
434  4.0,0.,100.);
435 
436  /***************
437  * Rebinning *
438  ***************/
439  if (uves_propagate_parameters_step(UVES_REBIN_ID, parameters,
440  recipe_id, NULL) != 0)
441  {
442  return -1;
443  }
444 
445  return (cpl_error_get_code() != CPL_ERROR_NONE);
446 }
447 
448 /*----------------------------------------------------------------------------*/
519 /*----------------------------------------------------------------------------*/
520 static cpl_table *
521 uves_wavecal_process_window(const cpl_image *arclamp,
522  const cpl_image *arclamp_noise,
523  const uves_propertylist *rotated_header,
524  const cpl_table *ordertable,
525  const polynomial *order_locations,
526  bool flat_fielded,
527  cpl_image *weights,
528  /* const cpl_table *drs_table, Not used */
529  const cpl_table *guess,
530  const cpl_table *line_refer,
531  bool flames,
532  int tab_in_out_oshift,
533  double tab_in_out_yshift,
534  enum uves_chip chip, int bin_disp,
535  int trace, int window, int NWINDOWS,
536  /* General */
537  bool debug_mode,
538  /* Extraction */
539  double offset,
540  double slitlength,
541  const cpl_parameterlist *parameters,
542  const char *recipe_id,
543  /* Search */
544  int RANGE,
545  int MINLINES,
546  int MAXLINES,
547  centering_method CENTERING_METHOD,
548  /* First solution */
549  double SHIFT_MAX,
550  double SHIFT_STEP,
551  double SHIFT_TOLERANCE,
552  /* Identify */
553  double ALPHA,
554  double MAXERROR,
555  int DEGREE,
556  /* Calibrate */
557  double TOLERANCE,
558  double kappa,
559  cpl_frame* corvel_frm,
560  cpl_table** flames_qclog,
561  /* Result */
562  polynomial **dispersion_relation,
563  polynomial **absolute_order,
564  int *first_absolute_order,
565  int *last_absolute_order)
566 {
567  cpl_table *linetable = NULL; /* Result */
568  cpl_table *temp = NULL;
569  cpl_image *spectrum = NULL;
570  cpl_image *spectrum_noise = NULL;
571  cpl_image *debug_image = NULL;
572  polynomial *initial_dispersion = NULL;
573  int *relative_order = NULL; /* Map from physical
574  order to relative order */
575  uves_propertylist *spectrum_header = NULL;
576 
577  cpl_image *rebinned = NULL; /* Used for calculating
578  the instrument resolution */
579  cpl_image *rebinned_noise = NULL; /* Used for calculating
580  the instrument resolution */
581  uves_propertylist *rebinned_header = NULL;
582  cpl_image *merged = NULL;
583  cpl_image *merged_noise = NULL;
584  uves_propertylist *merged_header = NULL;
585  cpl_table *info_tbl = NULL;
586 
587  /* Needed for optimal extraction */
588  cpl_image *weights_opt = NULL;
589  cpl_table *cr_table = NULL;
590  cpl_image *cr_image = NULL;
591  cpl_table *order_trace = NULL;
592 
593  merge_method m_method = flat_fielded ? MERGE_OPTIMAL : MERGE_SUM;
594 
595  /* Extract the spectrum */
596  uves_free_table(&info_tbl);
597 
598 
599  check( spectrum = uves_extract((cpl_image *)arclamp,/* Const-casts are okay,
600  the image (bpm) + error bars
601  is changed
602  only in optimal extraction */
603  (cpl_image *)arclamp_noise,
604  NULL, /* Header (optimal only) */
605  ordertable,
606  order_locations,
607  slitlength, /* Slit length (pixels) */
608  offset, /* Slit center offset */
609  parameters, /* Extraction method */
610  recipe_id,
611  "",
612  true, /* Extraction partial bins? */
613  debug_mode,
614  chip,
615  &spectrum_header,
616  &spectrum_noise,
617  NULL,
618  NULL, /* Optimal: sky+noise */
619  &cr_table,
620  &cr_image,
621  NULL,
622  (weights != NULL) ? &weights : &weights_opt,
623  &info_tbl,
624  &order_trace),
625  "Error extracting spectrum");
626  uves_free_table(&info_tbl);
627 
628 
630  rotated_header,
631  "^ESO ", 0),"error copying hierarch keys");
632 
633  /* Set bad pixels to 0, so that the search algorithm doesn't
634  fail because of bad pixels (but simply does not detect anything)
635  */
636  cpl_image_fill_rejected(spectrum, 0);
637  cpl_image_accept_all(spectrum);
638  cpl_image_fill_rejected(spectrum_noise, 1);
639  cpl_image_accept_all(spectrum_noise);
640 
641  /* Save spectrum + noise */
642  if (debug_mode)
643  {
644 
645 
646  check(uves_propertylist_copy_property_regexp(spectrum_header, rotated_header,
647  "^ESO ", 0),
648  "Error copying hieararch keys");
649 
650  check( uves_save_image_local("Extracted spectrum", "spectrum",
651  spectrum, chip, trace, window, spectrum_header, true),
652  "Error saving spectrum");
653 
654  check( uves_save_image_local("Extracted spectrum noise", "spectrum_noise",
655  spectrum_noise, chip, trace, window, spectrum_header, true),
656  "Error saving spectrum");
657  }
658 
659  /* Locate lines */
660  debug_image = cpl_image_duplicate(arclamp);
661  check( linetable = uves_wavecal_search(spectrum,
662  spectrum_noise,
663  spectrum_header,
664  flat_fielded,
665  order_locations,
666  debug_image,
667  RANGE,
668  MINLINES,
669  MAXLINES,
670  CENTERING_METHOD,
671  bin_disp,trace,window,
672  flames_qclog[0]),
673  "Line search failed");
674 
675  /* Read first solution from guess line table */
676  {
677  int degree = 5; /* for the initial solution only. For the
678  final solution the degree given as recipe
679  parameter is used */
680  uves_polynomial_delete(&initial_dispersion);
681  cpl_free(relative_order);
682  check( initial_dispersion = uves_wavecal_firstsolution(linetable,
683  guess,
684  absolute_order,
685  ordertable,
686  order_locations,
687  flames,
688  offset,
689  &relative_order,
690  degree,
691  SHIFT_MAX,
692  SHIFT_STEP,
693  SHIFT_TOLERANCE,
694  MAXERROR,
695  first_absolute_order,
696  last_absolute_order),
697  "Could not get first solution");
698  }
699 
700 
701  if (flames)
702  {
703  /* !AM: To correct eventual residual shifts between Guess and Final order
704  ! (and line) table
705  compute/table {LINTAB} :YNEW =
706  :YNEW - {{ORDTAB},TAB_IN_OUT_YSHIFT} - {{OLDORDTAB},FIBREPOS({i1})}
707  compute/table {LINTAB} :Y = :Y +{{ORDTAB},TAB_IN_OUT_OSHIFT}
708  */
709 
710  cpl_table_add_scalar(linetable, "Y", tab_in_out_oshift);
711  cpl_table_add_scalar(linetable, "Ynew", - tab_in_out_yshift - offset);
712  }
713 
714  /* Calibrate */
715  check( *dispersion_relation = uves_wavecal_identify(linetable, /* Guess solution */
716  line_refer,
717  initial_dispersion,
718  DEGREE,
719  TOLERANCE, ALPHA,
720  MAXERROR,
721  kappa,trace,window,flames_qclog[0]),
722  "Could not calibrate orders");
723 
724  if (flames)
725  {
726  /* AM: To have correct split of fibres in line table:
727  > compute/table {LINTAB} :YNEW = :YNEW + {{ORDTAB},TAB_IN_OUT_YSHIFT}
728  + {{OLDORDTAB},FIBREPOS({i1})}
729  */
730 
731  cpl_table_add_scalar(linetable, "Ynew", + tab_in_out_yshift + offset);
732  }
733 
734  /* UVES: make plots (resolution + etc.) for the central window,
735  * FLAMES: all fibres
736  */
737  if (flames || (trace == 0 && window == 2)|| (window == 1 && NWINDOWS == 1))
738  {
739  /* Create x-FWHM column: FWHM = 2.3548 sigma */
740  check(( cpl_table_duplicate_column(linetable, "deltaX", linetable, "Xwidth"),
741  cpl_table_multiply_scalar (linetable, "deltaX", TWOSQRT2LN2)),
742  "Error creating FWHM column");
743 
744 
745  check_nomsg( temp = uves_extract_table_rows(
746  linetable, "NLinSol", CPL_NOT_EQUAL_TO, 0) );
747 
748  check( uves_plot_table(temp, "Order", LINETAB_RESIDUAL, "Residual of fit"),
749  "Plotting failed");
750 
751  check( uves_plot_table(temp, "X", "deltaX", "line FWHM (mean = %.2f pixels)",
752  cpl_table_get_column_mean(linetable, "deltaX")),
753  "Plotting failed");
754  /*
755  check( uves_plot_table(linetable, "Y", "deltaX",
756  "line FWHM (mean FWHM = %.2f pixels)",
757  cpl_table_get_column_mean(linetable, "deltaX")), "Plotting failed");
758  */
759 
760 
761  /* Compute resolution as as lambda / deltalambda where deltalambda
762  is the peak FWHM in wavelength space (after resampling using
763  WAVESTEP = average pixelsize)
764  */
765 
766  {
767  /* Rebin using steps of median pixelsize */
768  double wavestep;
769  double lambda_start = 0;
770  int n_traces = 1; /* We didn't do a 2d extraction;
771  there's only 1 trace
772  per order */
773  int i, nbins;
774  bool threshold_to_positive = true;
775 
776  cpl_table_new_column(linetable, "deltaLambda", CPL_TYPE_DOUBLE);
777 
778  check( rebinned_noise = uves_rebin(spectrum_noise,
779  parameters,
780  recipe_id,
781  linetable,
782  *dispersion_relation,
783  *first_absolute_order,
784  *last_absolute_order,
785  n_traces,
786  threshold_to_positive,
787  true,
788  &rebinned_header),
789  "Could not rebin noise of arc lamp spectrum");
790 
791  threshold_to_positive = false;
792  uves_free_propertylist(&rebinned_header);
793  check( rebinned = uves_rebin(spectrum,
794  parameters,
795  recipe_id,
796  linetable,
797  *dispersion_relation,
798  *first_absolute_order,
799  *last_absolute_order,
800  n_traces,
801  threshold_to_positive,
802  false,
803  &rebinned_header),
804  "Could not rebin arc lamp spectrum");
805 
806  /* Save order-by-order rebinned spectrum+noise */
807  if (debug_mode)
808  {
809  check( uves_save_image_local("Rebinned spectrum",
810  "wxb", rebinned, chip,
811  trace, window, rebinned_header, true),
812  "Error saving rebinned spectrum");
813 
814  check( uves_save_image_local("Noise of rebinned spectrum",
815  "errwxb", rebinned_noise, chip,
816  trace, window, rebinned_header, true),
817  "Error saving noise of rebinned spectrum");
818  }
819 
820  check( merged = uves_merge_orders(rebinned,
821  rebinned_noise,
822  rebinned_header,
823  m_method,
824  n_traces,
825  &merged_header,
826  0,0,chip,
827  &merged_noise),
828  "Could not merge arc lamp spectrum");
829 
830  check( uves_plot_image_rows(merged, 1, 1, 1,
831  "Wavelength (arbitrary units)",
832  "Flux", "Resampled arc lamp spectrum"),
833  "Plotting failed");
834 
835  /* Save merged arc lamp spectrum */
836  if (debug_mode)
837  {
838  check( uves_save_image_local("Rebinned, merged spectrum",
839  "merged", merged, chip,
840  trace, window, merged_header, true),
841  "Error saving merged spectrum");
842  }
843 
844  nbins = cpl_image_get_size_x(merged);
845 
846  check( wavestep = uves_pfits_get_cdelt1(merged_header),
847  "Error reading resampling step size");
848 
849  check( lambda_start = uves_pfits_get_crval1(merged_header),
850  "Could not read start wavelength of merged spectrum");
851 
852 
853  //Begin commented region
854 
855  if (flames && trace == 0 && corvel_frm != NULL)
856  {
857  //The following (flames_reduce.VCORREL) calculates
858  //a cross correlation and does some QC
859 
860 
861 //> !To support simcal Mode
862 //> if i1 .eq. 1 then
863 //> w/o "DRS_CVEL_SWITCH={DRS_CVEL_SWITCH}"
864 //> if "{DRS_CVEL_SWITCH}" .eq. "Y" then
865 //> w/o "To support simcal Mode"
866 //> define/local ord_min/i/1/1 0
867 //> define/local ord_max/i/1/1 0
868 //> define/local rsample/d/1/1 0
869 //> statistic/table {ORDTAB} :ORDER >Null
870 //> ord_min = outputr(1)
871 //> ord_max = outputr(2)
872 //> rsample = {{LINTAB},PIXEL(1)}
873 //> rsample = 2./3. * rsample
874 //> rebin/echelle {ofrm} w{ofrm} {rsample} NONL {LINTAB} {SESSOUTV}
875 //> mercut/echelle w{ofrm} mw{ofrm} {ord_min},{ord_max} NOAPPEND
876 //> !corvel stuff
877 //> define/local OLD_CVEL_MAX/d/1/1 {DRS_CVEL_MAX}
878 //> define/local OLD_CVEL_MIN/d/1/1 {DRS_CVEL_MIN}
879 //> @p flames_reduce,VCORREL x1_rbf_ cvel1 0 {ord_max} {parCorVelTab} _0 _{chip({PATHID})} 0
880 //> DRS_CVEL_MAX = DRS_CVEL_MAX + {q1}
881 //> DRS_CVEL_MIN = DRS_CVEL_MIN + {q1}
882 //>
883 //> @p flames_reduce,VCORREL x1_rbf_ cvel2 0 {ord_max} {parCorVelTab} _0 _{chip({PATHID})} 0
884 //> cvel_0 = {q1}
885 //> DRS_CVEL_MAX = OLD_CVEL_MAX
886 //> DRS_CVEL_MIN = OLD_CVEL_MIN
887 //> endif
888 //> write/keyword DRS_CVEL_SWITCH Y
889 //> endif
890 
891 
892 
893 
894 
895 
896 
897  const char* drs_base_name=NULL;
898  const char* prefid=NULL;
899  double ccf_posmax_zero_point=0;
900  double ccf_posmax_zero_point_iter0=0;
901  double cvel_max=0;
902  double cvel_sig=0;
903  if(chip == UVES_CHIP_REDL) {
904  prefid="l";
905  } else {
906  prefid="u";
907  }
908  const char* name=NULL;
909  const char* file=NULL;
910  cpl_propertylist* plist=NULL;
911  double drs_cvel_min=0;
912  double drs_cvel_max=0;
913 
914 
915  check( uves_save_image_local("Rebinned spectrum",
916  "wxb", rebinned, chip,
917  trace, window, rebinned_header, true),
918  "Error saving rebinned spectrum");
919 
920  check( uves_save_image_local("Rebinned, merged spectrum",
921  "mwxb", merged, chip,
922  trace, window, merged_header, true),
923  "Error saving merged spectrum");
924 
925 
926  check( file = uves_local_filename("wxb", chip, trace, window),
927  "Error getting filename");
928  check_nomsg(plist=cpl_propertylist_load(file,0));
929 
930  name=uves_sprintf("wfxb_%s%s%4.4d%s",prefid,"_raw",1,".fits");
931  drs_base_name=uves_sprintf("fxb_%s",prefid);
932 
933  cpl_image_save(rebinned,name, CPL_BPP_IEEE_FLOAT,plist,
934  CPL_IO_DEFAULT);
935 
936 
937  name=uves_sprintf("mwfxb_%s%s%4.4d%s",prefid,"_raw",1,".fits");
938  cpl_image_save(merged,name, CPL_BPP_IEEE_FLOAT,plist,
939  CPL_IO_DEFAULT);
940 
941  cpl_propertylist_delete(plist);
942 
943 
944  int ord_max=(*first_absolute_order-*last_absolute_order)+1;
945 
946  uves_msg("cvel max:%g %g",DRS_CVEL_MAX,DRS_CVEL_MIN);
947  drs_cvel_max =DRS_CVEL_MAX;
948  drs_cvel_min =DRS_CVEL_MIN;
949 
950  check_nomsg(flames_reduce_vcorrel(drs_base_name,
951  "cvel2",
952  prefid,
953  ord_max,
954  corvel_frm,
955  "_raw0001",
956  "_raw0001",
957  DRS_CVEL_MIN,
958  DRS_CVEL_MAX,
959  &ccf_posmax_zero_point,
960  &cvel_max,
961  &cvel_sig,
962  flames_qclog[0]));
963 
964  drs_cvel_max +=cvel_max;
965  drs_cvel_min +=cvel_max;
966  ccf_posmax_zero_point_iter0 =cvel_max;
967  uves_msg("cvel max:%g %g",drs_cvel_max,drs_cvel_min);
968 
969  check_nomsg(flames_reduce_vcorrel(drs_base_name,
970  "cvel2",
971  prefid,
972  ord_max,
973  corvel_frm,
974  "_raw0001",
975  "_raw0001",
976  drs_cvel_min,
977  drs_cvel_max,
978  &ccf_posmax_zero_point,
979  &cvel_max,
980  &cvel_sig,
981  flames_qclog[1]));
982 
983  drs_cvel_max +=cvel_max;
984  drs_cvel_min +=cvel_max;
985  ccf_posmax_zero_point =ccf_posmax_zero_point_iter0;
986  ck0_nomsg(uves_qclog_add_double(flames_qclog[1],
987  "QC CCF POSOFF",
988  ccf_posmax_zero_point,
989  "CCF pos avg from ThAr calibration",
990  "%f"));
991 
992 
993  uves_msg("cvel max:%g min: %g zp: %g",
994  drs_cvel_max,drs_cvel_min,ccf_posmax_zero_point);
995 
996 
997  }
998  //End commented region
999 
1000 
1001  /* For all identified lines fit the line width in the
1002  merged spectrum to get the resolution */
1003  for (i = 0; i < cpl_table_get_nrow(linetable); i++)
1004  {
1005  double lambda = cpl_table_get_double(
1006  linetable, LINETAB_LAMBDAC, i, NULL);
1007  double width =
1008  cpl_table_get_double(linetable, "Xwidth" , i, NULL) *
1009  fabs(cpl_table_get_double(linetable, LINETAB_PIXELSIZE, i, NULL));
1010  /* in wlu */
1011 
1012  /* Convert line wavelength and width to 'bin' units */
1013  int bin = 1 +
1014  uves_round_double((lambda - lambda_start) / wavestep);
1015  double width_bin = width / wavestep;
1016 
1017  /* Set fitting window to +-5 sigma */
1018  int first_bin = uves_max_int( 1, uves_round_double(bin - 5*width_bin));
1019  int last_bin = uves_min_int(nbins, uves_round_double(bin + 5*width_bin));
1020 
1021  double my, sigma, norm, background; /* Results of fit */
1022  double lambda_fwhm;
1023 
1024  if (cpl_table_is_valid(linetable, "Ident", i) && first_bin < last_bin)
1025  {
1026  /* Fit a gaussian to the merged arc spectrum */
1027  uves_fit_1d_image(merged,
1028 #if 1
1029  merged_noise,
1030 #else /* Unweighted fit like MIDAS which gives larger sigma */
1031  NULL,
1032 #endif
1033  NULL,
1034  true, /* Horizontal? */
1035  false, /* Fix background?*/
1036  false, /* Fit background?*/
1037  first_bin,
1038  last_bin,
1039  1, /* xlo, xhi, y */
1040  &my,
1041  &sigma,
1042  &norm,
1043  &background, NULL, /* slope */
1044  NULL,
1045  NULL,
1046  NULL, /* mse, red_chisq,
1047  covariance */
1048  uves_gauss,
1050  4);
1051 
1052  if (cpl_error_get_code() == CPL_ERROR_CONTINUE)
1053  {
1054  uves_error_reset();
1055  uves_msg_debug("Gaussian fitting failed "
1056  "at lambda = %f wlu, bins = "
1057  "%d - %d, ignoring line",
1058  lambda, first_bin, last_bin);
1059 
1060  cpl_table_set_invalid(linetable, "deltaLambda", i);
1061 
1062  }
1063  else
1064  {
1065  assure(cpl_error_get_code() == CPL_ERROR_NONE,
1066  cpl_error_get_code(), "Gaussian fitting failed");
1067 
1068  /* Convert from bins to wavelength */
1069  lambda_fwhm = TWOSQRT2LN2 * sigma * wavestep;
1070 
1071  cpl_table_set_double(linetable, "deltaLambda",
1072  i, lambda_fwhm);
1073 
1074  }
1075  }
1076  else
1077  {
1078  cpl_table_set_invalid(linetable, "deltaLambda", i);
1079  }
1080  }
1081 
1082 
1083  /* Create column 'Resol'(ution) = lambda / deltalambda */
1084  check(( cpl_table_duplicate_column(linetable, "Resol",
1085  linetable, LINETAB_LAMBDAC),
1086  cpl_table_divide_columns (linetable, "Resol",
1087  "deltaLambda")),
1088  "Error creating 'Resol' column");
1089 
1090  /* Filter out extreme outliers (due to bad gaussian fit) */
1091  {
1092 
1093  int ninvalid=0;
1094  int nrows=0;
1095  double resol_avg = 0;
1096  double resol_stdev = 0;
1097  double kappar = 10.0;
1098  nrows=cpl_table_get_nrow(linetable);
1099  ninvalid=cpl_table_count_invalid(linetable,"Resol");
1100  assure(ninvalid < nrows,CPL_ERROR_ILLEGAL_INPUT,
1101  "No valid elements in Resol column. "
1102  "You must decrease parameter rebin.wavestep");
1103  check_nomsg(resol_avg=cpl_table_get_column_median(linetable, "Resol"));
1104  check_nomsg(resol_stdev=cpl_table_get_column_stdev (linetable, "Resol"));
1105 
1106  for (i = 0; i < cpl_table_get_nrow(linetable); i++)
1107  {
1108  double r = cpl_table_get_double(linetable, "Resol", i, NULL);
1109  if (r < resol_avg - kappar*resol_stdev ||
1110  r > resol_avg + kappar*resol_stdev)
1111  {
1112  cpl_table_set_invalid(linetable, "Resol", i);
1113  cpl_table_set_invalid(linetable, "deltaLambda", i);
1114  }
1115  }
1116  }
1117 
1118  /* check( uves_plot_table(linetable, "X", "Resol",
1119  "(x, l / dl)"), "Plotting failed");
1120  check( uves_plot_table(linetable, "Y", "Resol",
1121  "(y, l / dl)"), "Plotting failed");
1122  */
1123  check( uves_plot_table(linetable, LINETAB_LAMBDAC, "Resol",
1124  "(l, l / dl)"), "Plotting failed");
1125  }
1126 
1127  /* Plot identifications */
1128  uves_free_table(&temp);
1129  check( temp = cpl_table_duplicate(linetable),
1130  "Error copying line table");
1131  check( uves_erase_invalid_table_rows(temp, "Ident"),
1132  "Error removing un-identified lines");
1133  check( uves_plot_table(temp, "X", "Ynew",
1134  "Line identifications"),
1135  "Plotting failed");
1136  uves_free_table(&temp);
1137 
1138  } /* Plots for middle (object) window */
1139 
1140  if (debug_mode)
1141  {
1142  /* Results of uves_wavecal_search are already
1143  drawn on debug_image */
1144 
1145  /* Draw guess table lines using the initial solution */
1146  if (0) check( uves_draw_lines(debug_image, initial_dispersion,
1147  order_locations, guess,
1148  "Ident", "Order", relative_order,
1149  -1, -1,
1150  false, /* true = vertical */
1151  12), "Error drawing guess solution");
1152 
1153  /* Draw catalogue lines using the initial solution */
1154  check( uves_draw_lines(debug_image, initial_dispersion, order_locations,
1155  line_refer, "Wave", NULL, relative_order,
1156  uves_min_int(*first_absolute_order, *last_absolute_order),
1157  uves_max_int(*first_absolute_order, *last_absolute_order),
1158  true, /* true = vertical */
1159  8), "Error drawing catalogue lines");
1160 
1161  /* Draw catalogue lines using the final solution */
1162  check( uves_draw_lines(debug_image, *dispersion_relation, order_locations,
1163  line_refer, "Wave", NULL, relative_order,
1164  uves_min_int(*first_absolute_order, *last_absolute_order),
1165  uves_max_int(*first_absolute_order, *last_absolute_order),
1166  true, /* true = vertical */
1167  16), "Error drawing catalogue lines");
1168 
1169  /* Draw detected lines using initial solution */
1170  if (0) check( uves_draw_lines(debug_image, initial_dispersion,
1171  order_locations, linetable,
1172  LINETAB_LAMBDAC, "Order", relative_order,
1173  -1, -1,
1174  false, /* true = vertical */
1175  -16), "Error drawing detected lines");
1176 
1177  /* Draw IDed lines */
1178  uves_free_table(&temp);
1179  check(( temp = cpl_table_duplicate(linetable),
1180  /* Delete rows with invalid 'Ident' */
1181  uves_erase_invalid_table_rows(temp, "Ident")),
1182  "Error duplicating table");
1183 
1184  check( uves_draw_lines(debug_image, *dispersion_relation, order_locations,
1185  temp, LINETAB_LAMBDAC, "Order", relative_order,
1186  -1, -1,
1187  true, /* true = vertical */
1188  0), "Error drawing detected lines");
1189 
1190 
1191  /* Explanation of drawing produced by code above:
1192  The output frame emission lines will look like this
1193 
1194 
1195  #### |1
1196  #### |2
1197  #|3#
1198  -+--
1199  #|##
1200  ####
1201  ####
1202 
1203 
1204  Legend:
1205 
1206  ##: The emmission line
1207  --: (horizontal line) The line was detected
1208  |1: (vertical line) Predicted position (final solution)
1209  |2: (vertical line) Predicted position (initial solution)
1210  |3: (vertical line) Is drawn iff the line was identified
1211 
1212 
1213 
1214  */
1215 
1216  /* Save the raw arc frame with detected emission lines marked */
1217  check( uves_save_image_local("Debug image", "rawdebug",
1218  debug_image, chip, trace, window,
1219  rotated_header, true),
1220  "Error saving spectrum");
1221  }
1222 
1223  if (flames)
1224  {
1225  int start = 0;
1226  int count = cpl_table_get_nrow(linetable);
1227 
1228  check_nomsg( cpl_table_new_column(linetable, "Fibre", CPL_TYPE_INT) );
1229 
1230  cpl_table_fill_column_window(linetable, "Fibre",
1231  start, count,
1232  trace + 1); /* Write value in range 1-9 */
1233  }
1234 
1235  cleanup:
1236 
1237  uves_free_table(&info_tbl);
1238  uves_free_table(&temp);
1239  uves_free_image(&weights_opt);
1240  uves_free_table(&cr_table);
1241  uves_free_image(&cr_image);
1242  uves_free_image(&spectrum);
1243  uves_free_image(&spectrum_noise);
1244  uves_free_image(&debug_image);
1245  uves_free_image(&rebinned);
1246  uves_free_image(&rebinned_noise);
1247  uves_free_image(&merged);
1248  uves_free_image(&merged_noise);
1249  uves_free_propertylist(&spectrum_header);
1250  uves_free_propertylist(&rebinned_header);
1251  uves_free_propertylist(&merged_header);
1252  cpl_free(relative_order);
1253  uves_polynomial_delete(&initial_dispersion);
1254  uves_free_table(&order_trace);
1255 
1256  return linetable;
1257 }
1258 
1259 
1260 
1261 /*----------------------------------------------------------------------------*/
1286 /*----------------------------------------------------------------------------*/
1287 void
1288 uves_wavecal_exe_body(cpl_frameset *frames,
1289  bool flames,
1290  const char *recipe_id,
1291  const cpl_parameterlist *parameters,
1292  const char *starttime)
1293 {
1294  /*
1295  * Variables containg the values of recipe parameters
1296  */
1297 
1298  /* General */
1299  bool debug_mode;
1300 
1301  /* Extraction */
1302  int NWINDOWS;
1303  double OFFSET;
1304  double SLITLENGTH_par; /* slitlength given by user */
1305 
1306  /* Search */
1307  int RANGE;
1308  int MAXLINES;
1309  int MINLINES;
1310  centering_method CENTERING_METHOD;
1311 
1312  /* First solution */
1313  double SHIFT_MAX;
1314  double SHIFT_STEP;
1315  double SHIFT_TOLERANCE;
1316 
1317  /* Identify */
1318  double ALPHA;
1319  double MAXERROR;
1320  int DEGREE;
1321  /* Calibrate */
1322  double TOLERANCE;
1323  double KAPPA;
1324 
1325  /* Input */
1326  cpl_image *arclamp[2] = {NULL, NULL};
1327  cpl_image *arclamp_noise = NULL;
1328  uves_propertylist *arclamp_header[2] = {NULL, NULL};
1329  uves_propertylist *rotated_header[2] = {NULL, NULL};
1330 
1331  /* Order table */
1332  cpl_table *ordertable = NULL;
1333  uves_propertylist *ordertable_header = NULL;
1334  polynomial *order_locations = NULL;
1335  cpl_table *traces = NULL;
1336 
1337  /* Bias */
1338  cpl_image *master_bias = NULL;
1339  uves_propertylist *master_bias_header = NULL;
1340 
1341  /* Flat field */
1342  cpl_image *master_flat = NULL;
1343  cpl_image *mflat_noise = NULL;
1344  uves_propertylist *master_flat_header = NULL;
1345 
1346  /* Weight map */
1347  cpl_image *weights = NULL;
1348 
1349  /* DRS guess table is not used */
1350  /*
1351  cpl_table *drs_table = NULL;
1352  uves_propertylist *drs_header = NULL;
1353  */
1354 
1355  //FLAMES-DRS specific descriptors
1356  int* fibres_mask=NULL;
1357  double* fibres_pos=NULL;
1358 
1359  /* Guess line table */
1360  cpl_table *guess = NULL;
1361  polynomial *absolute_order = NULL;
1362 
1363  /* Velocity correction table */
1364  cpl_table *corvel = NULL;
1365  cpl_frame *corvel_frm = NULL;
1366  uves_propertylist *corvel_header = NULL;
1367 
1368  /* Reference catalogue */
1369  cpl_table *line_refer = NULL;
1370  cpl_table *line_intmon = NULL;
1371 
1372  /* Output */
1373  lt_type *linetable = NULL;
1374 
1375  uves_propertylist *primary_header = NULL;
1376  uves_propertylist *table_header = NULL;
1377  /* QC for resolution + intmon + NULL */
1378  cpl_table *qclog[3] = {NULL, NULL, NULL};
1379 
1380  /* Local variables */
1381  cpl_image *absorder_image = NULL;
1382  const char *arclamp_filename = "";
1383  const char *line_refer_filename = "";
1384  const char *line_intmon_filename = "";
1385  char *product_filename = NULL;
1386  char *temp = NULL;
1387  bool blue = false;
1388  bool sim_cal = false;
1389  enum uves_chip chip;
1390  int binx = 0;
1391  int biny = 0;
1392  bool drs_cvel_sw=false;
1393  const char* PROCESS_CHIP=NULL;
1394  extract_method em;
1395 
1396  /* Read recipe parameters */
1397  {
1398  const char *centering_m = "gaussian";
1399  const char *profile = "";
1400 
1401 
1402 
1403  /* General */
1404  check( uves_get_parameter(parameters, NULL, "uves", "debug",
1405  CPL_TYPE_BOOL, &debug_mode), "Could not read parameter");
1406 
1407  check( uves_get_parameter(parameters, NULL, "uves", "process_chip", CPL_TYPE_STRING, &PROCESS_CHIP),
1408  "Could not read parameter");
1409  uves_string_toupper((char*)PROCESS_CHIP);
1410 
1411  /* Extraction */
1412  check( uves_get_parameter(parameters, NULL, recipe_id, "nwindows",
1413  CPL_TYPE_INT , &NWINDOWS ), "Could not read parameter");
1414  check( uves_get_parameter(parameters, NULL, recipe_id, "length",
1415  CPL_TYPE_DOUBLE, &SLITLENGTH_par), "Could not read parameter");
1416  check( uves_get_parameter(parameters, NULL, recipe_id, "offset",
1417  CPL_TYPE_DOUBLE, &OFFSET ), "Could not read parameter");
1418 
1419  /* Don't allow optimal extraction. This requires that
1420  additional arguments (weight image, ..) are passed to uves_extract */
1421  temp = uves_sprintf("%s.%s", recipe_id, UVES_EXTRACT_ID);
1422  check( em = uves_get_extract_method(parameters, NULL, temp),
1423  "Could not read extraction method");
1424 
1425  check( uves_get_parameter(parameters, NULL, recipe_id, UVES_EXTRACT_ID ".profile",
1426  CPL_TYPE_STRING, &profile), "Could not read parameter");
1427 
1428  assure( em == EXTRACT_LINEAR || em == EXTRACT_AVERAGE || em == EXTRACT_WEIGHTED ||
1429  (em == EXTRACT_OPTIMAL && strcmp(profile, "constant") == 0),
1430  CPL_ERROR_UNSUPPORTED_MODE,
1431  "Only linear/average/weighted/optimal(constant profile) extraction "
1432  "methods are supported by this recipe");
1433 
1434  /* Search */
1435  check( uves_get_parameter(parameters, NULL, recipe_id, "search.range",
1436  CPL_TYPE_INT , &RANGE ), "Could not read parameter");
1437  check( uves_get_parameter(parameters, NULL, recipe_id, "search.minlines",
1438  CPL_TYPE_INT , &MINLINES ), "Could not read parameter");
1439  check( uves_get_parameter(parameters, NULL, recipe_id, "search.maxlines",
1440  CPL_TYPE_INT , &MAXLINES ), "Could not read parameter");
1441 /*
1442  check( uves_get_parameter(parameters, NULL, recipe_id, "search.centeringmethod",
1443  CPL_TYPE_STRING, &centering_m ), "Could not read parameter");
1444 */
1445  if (strcmp(centering_m, "gravity" ) == 0) CENTERING_METHOD = CENTERING_GRAVITY;
1446  else if (strcmp(centering_m, "gaussian") == 0) CENTERING_METHOD = CENTERING_GAUSSIAN;
1447  else
1448  {
1449  /* Impossible */ assure(false, CPL_ERROR_ILLEGAL_INPUT,
1450  "Unrecognized parameter value '%s'", centering_m);
1451  }
1452 
1453  /* First solution */
1454  check( uves_get_parameter(parameters, NULL, recipe_id, "first.shiftmax",
1455  CPL_TYPE_DOUBLE , &SHIFT_MAX ),
1456  "Could not read parameter");
1457  check( uves_get_parameter(parameters, NULL, recipe_id, "first.shiftstep",
1458  CPL_TYPE_DOUBLE , &SHIFT_STEP ),
1459  "Could not read parameter");
1460  check( uves_get_parameter(parameters, NULL, recipe_id, "first.shifttoler",
1461  CPL_TYPE_DOUBLE , &SHIFT_TOLERANCE),
1462  "Could not read parameter");
1463 
1464  /* Identify */
1465  check( uves_get_parameter(parameters, NULL, recipe_id, "identify.alpha",
1466  CPL_TYPE_DOUBLE , &ALPHA ), "Could not read parameter");
1467  check( uves_get_parameter(parameters, NULL, recipe_id, "identify.maxerror",
1468  CPL_TYPE_DOUBLE , &MAXERROR ), "Could not read parameter");
1469  check( uves_get_parameter(parameters, NULL, recipe_id, "identify.degree",
1470  CPL_TYPE_INT , &DEGREE ), "Could not read parameter");
1471 
1472  /* Calibrate */
1473  check( uves_get_parameter(parameters, NULL, recipe_id, "calibrate.tolerance",
1474  CPL_TYPE_DOUBLE, &TOLERANCE ), "Could not read parameter");
1475  check( uves_get_parameter(parameters, NULL, recipe_id, "calibrate.kappa",
1476  CPL_TYPE_DOUBLE, &KAPPA ), "Could not read parameter");
1477 
1478  /* Additional checks */
1479  if (CENTERING_METHOD == CENTERING_GRAVITY)
1480  {
1481  uves_msg_warning("Centering method 'gravity' might lead to inaccurate "
1482  "results. Recommended is 'gaussian'");
1483  }
1484  }
1485 
1486  /* Load raw image and header, and identify input frame as red or blue */
1487  check( uves_load_arclamp(frames, flames, &arclamp_filename, arclamp, arclamp_header,
1488  rotated_header, &blue, &sim_cal), "Error loading raw frame");
1489 
1490  /* Load reference line table */
1491  check( uves_load_linerefertable(frames, &line_refer_filename, &line_refer, NULL),
1492  "Could not load line reference table");
1493  uves_msg("Using line reference table '%s'", line_refer_filename);
1494 
1495  /* Load INTensity MONitoring table if present */
1496  if (cpl_frameset_find(frames, UVES_LINE_INTMON_TABLE) != NULL)
1497  {
1498  uves_free_table(&line_intmon);
1499  check( uves_load_lineintmon(frames, &line_intmon_filename,
1500  &line_intmon),
1501  "Error loading line reference table");
1502 
1503  uves_msg("Using bright line table '%s'", line_intmon_filename);
1504  }
1505 
1506  /*
1507  * Adjust parameters according to binning
1508  */
1509  check (binx = uves_pfits_get_binx(arclamp_header[0]),
1510  "Could not read x binning factor from input header");
1511  check (biny = uves_pfits_get_biny(arclamp_header[0]),
1512  "Could not read y binning factor from input header");
1513  SLITLENGTH_par /= (1.0*binx); /* Extraction slit length */
1514  RANGE /= (1.0*biny); /* Search window */
1515  SHIFT_MAX /= (1.0*binx); /* Max shift compared to guess solution */
1516  SHIFT_STEP /= (1.0*binx);
1517 
1518  /* After the default tolerance was lowered to 0.07, do not adjust it according to binning,
1519  which would cause too many lines to be rejected
1520 
1521  TOLERANCE /= (1.0*biny);
1522  */
1523 
1524 
1525  /* Loop over one or two chips, over traces and
1526  over extraction windows */
1527  for (chip = uves_chip_get_first(blue);
1528  chip != UVES_CHIP_INVALID;
1529  chip = uves_chip_get_next(chip)) {
1530 
1531 
1532  if(strcmp(PROCESS_CHIP,"REDU") == 0) {
1533  chip = uves_chip_get_next(chip);
1534  }
1535 
1536  const char *ordertable_filename = "";
1537  const char *corvel_filename = "";
1538  const char *master_flat_filename = "";
1539  const char *master_bias_filename = "";
1540  const char *weights_filename = "";
1541  /* const char *drs_filename = ""; not used */
1542  const char *guess_filename = "";
1543  const char *chip_name = "";
1544  int ntraces;
1545  int tracerow; /* Index of table row */
1546  int raw_index = uves_chip_get_index(chip);
1547  int current_linetable_extension;
1548  int tab_in_out_oshift = -1;
1549  double tab_in_out_yshift = -1;
1550  double slitlength;
1551 
1552  uves_msg("Processing %s chip in '%s'",
1553  uves_chip_tostring_upper(chip), arclamp_filename);
1554 
1555  check_nomsg( chip_name = uves_pfits_get_chipid(arclamp_header[raw_index], chip));
1556 
1557 
1558  uves_msg_debug("binning = %dx%d", binx, biny);
1559 
1560 
1561  /* Load the order table for this chip */
1562  uves_free_table (&ordertable);
1563  uves_free_propertylist(&ordertable_header);
1564  uves_polynomial_delete(&order_locations);
1565  uves_free_table (&traces);
1566 
1567 
1568  check( uves_load_ordertable(frames,
1569  flames,
1570  chip_name,
1571  &ordertable_filename,
1572  &ordertable,
1573  &ordertable_header,
1574  NULL,
1575  &order_locations,
1576  &traces,
1577  (flames) ? &tab_in_out_oshift : NULL,
1578  (flames) ? &tab_in_out_yshift : NULL,
1579  &fibres_mask,
1580  &fibres_pos, /* fibre_pos,fibre_mask */
1581  chip,
1582  false /* load guess table? */),
1583  "Could not load order table");
1584  uves_msg("Using order table in '%s'", ordertable_filename);
1585  ntraces = cpl_table_get_nrow(traces);
1586  uves_free_double(&fibres_pos);
1587  uves_free_int(&fibres_mask);
1588 
1589  /* Load master bias if present */
1590  uves_free_image(&master_bias);
1591  uves_free_propertylist(&master_bias_header);
1592  if (cpl_frameset_find(frames, UVES_MASTER_BIAS(chip)) != NULL)
1593  {
1594  check( uves_load_mbias(frames, chip_name, &master_bias_filename, &master_bias,
1595  &master_bias_header, chip),
1596  "Error loading master bias");
1597 
1598  uves_msg_low("Using master bias in '%s'", master_bias_filename);
1599  }
1600  else
1601  {
1602  uves_msg_warning("Master bias not provided. Bias subtraction not done");
1603  }
1604 
1605 
1606  /* Load master flat if present */
1607  uves_free_image(&master_flat);
1608  uves_free_propertylist(&master_flat_header);
1609  if ((cpl_frameset_find(frames, UVES_MASTER_FLAT(chip)) != NULL ||
1610  cpl_frameset_find(frames, UVES_MASTER_DFLAT(chip)) != NULL ||
1611  cpl_frameset_find(frames, UVES_MASTER_IFLAT(chip)) != NULL ||
1612  cpl_frameset_find(frames, UVES_MASTER_TFLAT(chip)) != NULL))
1613  {
1614  check( uves_load_mflat(frames, chip_name, &master_flat_filename, &master_flat,
1615  &master_flat_header, chip, NULL),
1616  "Error loading master flat");
1617 
1618  uves_msg_low("Using master flat in '%s'", master_flat_filename);
1619  }
1620  else
1621  {
1622  uves_msg_warning("Master flat not provided. Flat-fielding not done");
1623  }
1624 
1625  /* Load weight map if present */
1626  if (em == EXTRACT_WEIGHTED) {
1627  uves_free_image(&weights);
1628  check( weights = uves_load_weights(frames, &weights_filename, chip),
1629  "Error loading weight map");
1630 
1631  uves_msg_low("Using weight map %s", weights_filename);
1632  }
1633 
1634  if (flames)
1635  /* Load CORVEL table */
1636  {
1637  if ((corvel_frm=cpl_frameset_find(frames, FLAMES_CORVEL_MASK)))
1638  {
1639  check( uves_load_corvel(frames,
1640  &corvel, &corvel_header,
1641  &corvel_filename),
1642  "Could not load velocity correction table");
1643 
1644  uves_msg("Using velocity correction table %s", corvel_filename);
1645  drs_cvel_sw=true;
1646 
1647 
1648  }
1649  else
1650  {
1651  uves_msg("No corvel table found. Switch off corvel");
1652  corvel = NULL;
1653  }
1654  }
1655 
1656  /* Allocate all line tables for this chip */
1657  uves_lt_delete(&linetable);
1658  linetable = uves_lt_new(NWINDOWS, ntraces);
1659 
1660 
1661  /* Init QC tables for this chip */
1662  uves_qclog_delete(&qclog[0]); qclog[0] = uves_qclog_init(arclamp_header[raw_index], chip);
1663  uves_qclog_delete(&qclog[1]); qclog[1] = uves_qclog_init(arclamp_header[raw_index], chip);
1664 
1665  /* Saving the rotated raw arc frame */
1666  if (debug_mode) check( uves_save_image_local("Arc lamp frame", "raw",
1667  arclamp[raw_index],
1668  chip, -1, -1, rotated_header[raw_index], true),
1669  "Error saving arc lamp frame");
1670 
1671  if (master_bias != NULL)
1672  {
1673  uves_msg("Subtracting master bias");
1674 
1675  check( uves_subtract_bias(arclamp[raw_index], master_bias),
1676  "Error during bias subtraction");
1677  }
1678  else {
1679  /* In lack of a real master bias frame, one might subtract the bias by estimating it
1680  as the median value across the chip (which should be okay for arc lamp frames)
1681  */
1682 
1683  /* Disabled. Would need to be tested. probably doesn't make any big difference anyway
1684  double bias = cpl_image_get_median(ff);
1685  uves_msg_debug("Estimated bias level is %f ADU", bias);
1686  cpl_image_subtract_scalar(ff, bias);
1687  */
1688  }
1689 
1690 
1691  /* Define arc lamp noise */
1692  uves_free_image(&arclamp_noise);
1693  check( arclamp_noise = uves_define_noise(arclamp[raw_index],
1694  arclamp_header[raw_index], 1, chip),
1695  "Could not set arc lamp noise");
1696 
1697  if (master_flat != NULL)
1698  {
1699  uves_msg("Dividing by master flat");
1700 
1701  uves_free_image(&mflat_noise);
1702  check( mflat_noise =
1703  uves_define_noise(master_flat, master_flat_header,
1704  uves_pfits_get_datancom(master_flat_header),
1705  chip),
1706  "Could not set master flat error bars");
1707 
1708  check( uves_flatfielding(arclamp[raw_index], arclamp_noise,
1709  master_flat, mflat_noise),
1710  "Error while dividing by flat field");
1711  }
1712 
1713  if (debug_mode) check( uves_save_image_local("Pre-processed arc lamp frame",
1714  "preproc",
1715  arclamp[raw_index], chip, -1, -1,
1716  rotated_header[raw_index], true),
1717  "Error saving arc lamp frame");
1718 
1719  /* Set appropriate slitlength if user did not */
1720  if (SLITLENGTH_par < 0) {
1721 
1722  double header_full_slit;
1723 
1724  check( header_full_slit =
1725  uves_pfits_get_slitlength_pixels(arclamp_header[raw_index], chip),
1726  "Could not read slit length");
1727 
1728  /* Avoid pixels at the edge of the slit
1729  * which are likely to be noisy
1730  */
1731  slitlength = uves_max_double(1.0, (header_full_slit - 2)/NWINDOWS);
1732 
1733  uves_msg("Full slit = %.2f pixels", header_full_slit);
1734  }
1735  else {
1736  slitlength = SLITLENGTH_par;
1737  }
1738 
1739 
1740  /* Loop over traces */
1741  for(tracerow = 0; tracerow < ntraces; tracerow++) {
1742  double trace_offset;
1743  int trace_number;
1744  int trace_enabled;
1745 
1746  trace_offset = cpl_table_get_double(traces, "Offset" , tracerow, NULL);
1747  trace_number = cpl_table_get_int (traces, "TraceID" , tracerow, NULL);
1748  trace_enabled = cpl_table_get_int (traces, "Tracemask", tracerow, NULL);
1749 
1750  if (ntraces > 1) {
1751  uves_msg("Processing trace %d", trace_number);
1752  }
1753 
1754  if (flames && sim_cal)
1755  {
1756  /* Only calibrate SIMCAL fibre in SIMCAL mode */
1757  trace_enabled = (trace_number == 1) ? 1 : 0;
1758  }
1759 
1760  uves_msg_low("Trace offset = %.2f pixels ; enabled = %d",
1761  trace_offset, trace_enabled);
1762 
1763  assure( flames || trace_number == 0, CPL_ERROR_ILLEGAL_INPUT,
1764  "%s: UVES trace number must be 0, it is %d",
1765  ordertable_filename, trace_number );
1766 
1767 
1768  if (trace_enabled != 0) {
1769  int window; /* window number */
1770 
1771  /* Load guess line table for this trace, any window */
1772  uves_free_table (&guess);
1773  uves_polynomial_delete(&absolute_order);
1774 
1775  check( uves_load_linetable(
1776  frames, flames, chip_name, order_locations,
1777  cpl_table_get_column_min(ordertable, "Order"),
1778  cpl_table_get_column_max(ordertable, "Order"),
1779  &guess_filename, &guess,
1780  NULL, NULL,
1781  &absolute_order, chip, trace_number, -1),
1782  "Could not load guess line table for trace number %d",
1783  trace_number);
1784  uves_msg("Using guess line table '%s'", guess_filename);
1785 
1786  if (debug_mode)
1787  {
1788  /* Create an image showing the polynomial m = f(x,y)
1789  where m is the absolute order number
1790  */
1791  int x, y;
1792 
1793  absorder_image = cpl_image_new(cpl_image_get_size_x(arclamp[raw_index]),
1794  cpl_image_get_size_y(arclamp[raw_index]),
1795  CPL_TYPE_FLOAT);
1796  assure_mem(absorder_image);
1797 
1798  for (y = 1; y <= cpl_image_get_size_y(arclamp[raw_index]); y++)
1799  {
1800  for (x = 1; x <= cpl_image_get_size_x(arclamp[raw_index]); x++)
1801  {
1802  double absorder =
1803  uves_polynomial_evaluate_2d(absolute_order, x, y);
1804 
1805  cpl_image_set(absorder_image, x, y, absorder);
1806  }
1807  }
1808 
1809  check( uves_save_image_local("Absolute order image", "absord",
1810  absorder_image, chip, trace_number,
1811  1, rotated_header[raw_index], true),
1812  "Error saving absolute order image");
1813 
1814  uves_free_image(&absorder_image);
1815  }
1816 
1817  /* Loop over sky windows */
1818  for (window = 1; window <= NWINDOWS; window ++) {
1819  /*
1820  | -trace_offs- |
1821  | | -win_offs- |
1822  | | |
1823  | | | -glb_offs- |
1824  order_loc.=0 |
1825  |
1826  window_loc.
1827  */
1828 
1829  /* Adjacent windows are separated by slitlength,
1830  and offset is zero when window_number = (NWINDOWS+1)/2,
1831  so the formula is */
1832  double window_offset =
1833  slitlength * (window - (NWINDOWS+1) / 2.0);
1834 
1835  /* Total offset (see sketch) */
1836  double offset = trace_offset + window_offset + OFFSET;
1837 
1838  /* Number of lines to detect. Use defaults if
1839  parameter values are negative */
1840 #if 0
1841  int lines_min = (MINLINES >= 1) ? MINLINES :
1842  (chip == UVES_CHIP_REDU) ? 1000 : 2000;
1843  int lines_max = (MAXLINES >= 1) ? MAXLINES :
1844  (chip == UVES_CHIP_REDU) ? 1400 : 2400;
1845 #else
1846 /* like MIDAS (fewer lines): */
1847  int lines_min = (MINLINES >= 1) ? MINLINES :
1848  (chip == UVES_CHIP_REDU) ? 1000 : 1100;
1849  int lines_max = (MAXLINES >= 1) ? MAXLINES :
1850  (chip == UVES_CHIP_REDU) ? 1400 : 1600;
1851 #endif
1852 
1853  assure( lines_min <= lines_max , CPL_ERROR_ILLEGAL_INPUT,
1854  "Minimum and maximum number of requested line "
1855  "detections don't make sense (min = %d; max = %d)",
1856  lines_min, lines_max);
1857 
1858  if (NWINDOWS > 1) {
1859  uves_msg("Processing window %d of %d", window, NWINDOWS);
1860  }
1861 
1862  passure( *(uves_lt_get_disprel(linetable, window, trace_number))
1863  == NULL, "%d %d", window, trace_number);
1864  passure( *(uves_lt_get_absord (linetable, window, trace_number))
1865  == NULL, "%d %d", window, trace_number);
1866 
1867 
1868  if (weights != NULL) {
1869  /* Object weighted extraction, use offset = 0 three times */
1870  offset = 0;
1871  }
1872 
1873  /* Set absord guess solution */
1874  *uves_lt_get_absord(linetable, window, trace_number) =
1875  uves_polynomial_duplicate(absolute_order);
1876 
1877  /* Execute macrosteps */
1878  check( *uves_lt_get_table(linetable, window, trace_number) =
1879  uves_wavecal_process_window(
1880  /* Raw */
1881  arclamp[raw_index],
1882  arclamp_noise,
1883  rotated_header[raw_index],
1884  /* Calibration */
1885  ordertable, order_locations,
1886  master_flat != NULL,
1887  weights,
1888  /* drs_table, not used */
1889  guess,
1890  line_refer,
1891  flames,
1892  tab_in_out_oshift,
1893  tab_in_out_yshift,
1894  /* Which window? */
1895  chip, biny, trace_number, window,NWINDOWS,
1896  /* Parameters */
1897  debug_mode,
1898  /* Extract */
1899  offset, slitlength, parameters, recipe_id,
1900  /* Search */
1901  RANGE, lines_min, lines_max, CENTERING_METHOD,
1902  /* First solution */
1903  SHIFT_MAX, SHIFT_STEP, SHIFT_TOLERANCE,
1904  /* Identify */
1905  ALPHA, MAXERROR, DEGREE,
1906  /* Calibrate */
1907  TOLERANCE, KAPPA,
1908  corvel_frm,qclog,
1909  /* Returned */
1910  uves_lt_get_disprel(linetable, window, trace_number),
1911  uves_lt_get_absord (linetable, window, trace_number),
1912  uves_lt_get_firstabs(linetable, window, trace_number),
1913  uves_lt_get_lastabs(linetable, window, trace_number)),
1914  "Wavelength calibration failed");
1915 
1916 
1917  //If CORVEL map is provided we perform the
1918  //corresponding analysis
1919  if(drs_cvel_sw) {
1920  /*
1921  define/local ord_min/i/1/1 0
1922  define/local ord_max/i/1/1 0
1923  define/local rsample/d/1/1 0
1924  statistic/table {ORDTAB} :ORDER >Null
1925  ord_min = outputr(1)
1926  ord_max = outputr(2)
1927  rsample = {{LINTAB},PIXEL(1)}
1928  rsample = 2./3. * rsample
1929  rebin/echelle {ofrm} w{ofrm} {rsample} NONL {LINTAB} {SESSOUTV}
1930  mercut/echelle w{ofrm} mw{ofrm} {ord_min},{ord_max} NOAPPEND
1931  !corvel stuff
1932  define/local OLD_CVEL_MAX/d/1/1 {DRS_CVEL_MAX}
1933  define/local OLD_CVEL_MIN/d/1/1 {DRS_CVEL_MIN}
1934  @p flames_reduce,VCORREL x1_rbf_ cvel1 0 {ord_max} {parCorVelTab} _0 _{chip({PATHID})} 0
1935  DRS_CVEL_MAX = DRS_CVEL_MAX + {q1}
1936  DRS_CVEL_MIN = DRS_CVEL_MIN + {q1}
1937 
1938  @p flames_reduce,VCORREL x1_rbf_ cvel2 0 {ord_max} {parCorVelTab} _0 _{chip({PATHID})} 0
1939  cvel_0 = {q1}
1940  DRS_CVEL_MAX = OLD_CVEL_MAX
1941  DRS_CVEL_MIN = OLD_CVEL_MIN
1942  */
1943 
1944 
1945  }
1946 
1947 
1948 
1949  if (flames || /* FLAMES: all fibres */
1950  (window == 2 && trace_number == 0)|| /* UVES: central window */
1951  (window == 1 && NWINDOWS == 1)) { /* UVES: special user setting */
1952 
1953  check( uves_wavecal_qclog(
1955  linetable,
1956  window,
1957  trace_number),
1958  *uves_lt_get_firstabs(linetable, window, trace_number),
1959  *uves_lt_get_lastabs(linetable, window, trace_number),
1960  arclamp[raw_index],
1961  arclamp_header[raw_index],
1962  flames,
1963  trace_number, trace_enabled, trace_offset,
1964  chip,
1965  qclog[0]),
1966  "Could not calculate resolution QC parameters");
1967 
1968  if (line_intmon != NULL) {
1969  check( uves_wavecal_qclog_intmon(
1971  linetable,
1972  window,
1973  trace_number),
1974  line_intmon,
1975  arclamp_header[raw_index],
1976  flames, trace_number,
1977  chip,
1978  qclog[1]),
1979  "Could not calculate int.mon. QC parameters");
1980  }
1981  else
1982  {
1983  /* Kill initialization and set pointer to NULL */
1984  uves_qclog_delete(&qclog[1]);
1985  }
1986  }
1987 
1988  /* Finished processing. Save later (because
1989  all QC parameters must be available
1990  when the product file is first created). */
1991 
1992  }/* for each window... */
1993 
1994  }/* if trace enabled? */
1995  else
1996  {
1997  uves_msg("Skipping trace number %d", trace_number);
1998  }
1999  }/* for each trace... */
2000 
2001  /* Finished calculating all line tables for current chip. Now save. */
2002 
2003  /* Prepare product filename
2004  (which need not be calculated for each trace and window) */
2005  cpl_free(product_filename);
2006  check( product_filename = uves_line_table_filename(chip), "Error getting filename");
2007  current_linetable_extension = 1;
2008 
2009  /* Loop over traces */
2010  for(tracerow = 0; tracerow < cpl_table_get_nrow(traces); tracerow++)
2011  {
2012  int trace_number;
2013  double trace_offset;
2014  int trace_enabled;
2015 
2016  trace_offset = cpl_table_get_double(traces, "Offset" , tracerow, NULL);
2017  trace_number = cpl_table_get_int (traces, "TraceID" , tracerow, NULL);
2018  trace_enabled = cpl_table_get_int (traces, "Tracemask" , tracerow, NULL);
2019 
2020  if (trace_enabled != 0)
2021  {
2022  int window;
2023 
2024  /* Loop over sky windows */
2025  for (window = 1; window <= NWINDOWS; window ++)
2026  {
2027  double window_offset =
2028  slitlength * (window - (NWINDOWS+1) / 2.0);
2029 
2030  double offset = trace_offset + window_offset + OFFSET;
2031 
2032  /* Table header */
2033  uves_free_propertylist(&table_header);
2034  table_header = uves_propertylist_new();
2035  check( uves_pfits_set_traceid ( table_header, trace_number),
2036  "Error writing trace ID to product header");
2037  check( uves_pfits_set_offset ( table_header, offset),
2038  "Error writing trace offset to product header");
2039  check( uves_pfits_set_windownumber( table_header, window),
2040  "Error writing window number to product header");
2041  check( uves_pfits_set_firstabsorder( table_header,
2043  linetable,
2044  window,
2045  trace_number)),
2046  "Error writing order number to product header");
2047  check( uves_pfits_set_lastabsorder( table_header,
2049  linetable,
2050  window,
2051  trace_number)),
2052  "Error writing order number to product header");
2053 
2054  /* Save line table + 2 polynomials (in 3 extensions) */
2055 
2056  if (current_linetable_extension == 1) {
2057  uves_free_propertylist(&primary_header);
2058  primary_header = uves_propertylist_new();
2059 
2060  if (flames)
2061  {
2062  char values[80];
2063  /* The MIDAS pipeline writes this QC to the
2064  header (always zero), but not as part of
2065  the QC logging */
2067  primary_header, 0.0) );
2068  //Add descriptors needed for science reduction
2069  /* FIBREMASK */
2070 
2071  uves_propertylist_append_string(primary_header,
2072  "HISTORY",
2073  "'FIBREMASK','I*4'");
2074 
2075  {
2076  int i;
2077  for (i = 0; i < N_FIBRES_MAX; i++) {
2078  snprintf(values, 80, "%1.1d ",
2079  cpl_table_get_int(traces,"Tracemask",
2080  i,NULL));
2081  uves_propertylist_append_string(primary_header,
2082  "HISTORY", values);
2083  uves_msg_debug("value=%d",
2084  cpl_table_get_int(traces,
2085  "Tracemask",
2086  i,NULL));
2087  }
2088  }
2089  uves_propertylist_append_string(primary_header,
2090  "HISTORY", " ");
2091 
2092 
2093  /* PIXEL */
2094  double pixelsize;
2095  double wavestep;
2096 
2097  check( pixelsize =
2098  cpl_table_get_column_mean(
2100  linetable,
2101  window,
2102  trace_number),
2103  LINETAB_PIXELSIZE),
2104  "Error reading mean pixelsize");
2105  uves_msg_warning("Average pixelsize = %f w.l.u.",
2106  pixelsize);
2107 
2108  wavestep = pixelsize*2.0/3;
2109 
2110  uves_propertylist_append_string(primary_header,
2111  "HISTORY",
2112  "'PIXEL','R*4'");
2113  snprintf(values,80,"%14.7g %14.7g",pixelsize,pixelsize);
2114  uves_propertylist_append_string(primary_header,
2115  "HISTORY", values);
2116  uves_propertylist_append_string(primary_header,
2117  "HISTORY", " ");
2118 
2119 
2120 
2121  }
2122 
2123  uves_msg("Creating line table '%s'", product_filename);
2124  check( uves_frameset_insert(
2125  frames,
2127  linetable,
2128  window,
2129  trace_number),
2130  CPL_FRAME_GROUP_PRODUCT,
2131  CPL_FRAME_TYPE_TABLE,
2132  CPL_FRAME_LEVEL_INTERMEDIATE,
2133  product_filename,
2134  UVES_LINE_TABLE(flames, chip),
2135  arclamp_header[raw_index],
2136  primary_header,
2137  table_header,
2138  parameters,
2139  recipe_id,
2140  PACKAGE "/" PACKAGE_VERSION,
2141  qclog, starttime, true, 0),
2142  "Could not add line table '%s' (%s) to frameset",
2143  product_filename, UVES_LINE_TABLE(flames, chip));
2144 
2145  uves_msg("Line table '%s' (%s) added to frameset",
2146  product_filename, UVES_LINE_TABLE(flames, chip));
2147  }
2148  else /* If this is not the first line table,
2149  append to the existing file */
2150  {
2151  check( uves_table_save(
2152  *uves_lt_get_table(linetable,
2153  window,
2154  trace_number),
2155  NULL, /* Primary header,
2156  ignored when mode
2157  is IO_EXTEND */
2158  table_header, /* Extension header */
2159  product_filename,/* This file
2160  already exists */
2161  CPL_IO_EXTEND), /* Append to
2162  existing file */
2163  "Error appending table to file '%s'",
2164  product_filename);
2165  }
2166  current_linetable_extension += 1;
2167 
2168  /* Save in next extension */
2169  check( uves_save_polynomial(*uves_lt_get_disprel(
2170  linetable,
2171  window,
2172  trace_number),
2173  product_filename,
2174  table_header),
2175  "Could not write polynomial to file '%s'",
2176  product_filename);
2177  current_linetable_extension += 1;
2178 
2179  /* Save in next extension */
2180  check( uves_save_polynomial(*uves_lt_get_absord(
2181  linetable,
2182  window,
2183  trace_number),
2184  product_filename,
2185  table_header),
2186  "Could not write polynomial to file '%s'",
2187  product_filename);
2188  current_linetable_extension += 1;
2189 
2190  uves_msg("Line table for trace %d, window #%d "
2191  "saved to extensions %d-%d of '%s'",
2192  trace_number, window,
2193  current_linetable_extension - 3,
2194  current_linetable_extension - 1,
2195  product_filename);
2196 
2197  } /* for each window */
2198  } /* if trace enabled */
2199  } /* for each trace */
2200 
2201  if(strcmp(PROCESS_CHIP,"REDL") == 0) {
2202  chip = uves_chip_get_next(chip);
2203  }
2204 
2205 
2206  }/* For each chip */
2207 
2208  cleanup:
2209 
2210  /* Input */
2211  uves_free_image(&arclamp[0]);
2212  uves_free_image(&arclamp[1]);
2213  uves_free_image(&arclamp_noise);
2214  uves_free_image(&absorder_image);
2215  uves_free_propertylist(&arclamp_header[0]);
2216  uves_free_propertylist(&arclamp_header[1]);
2217  uves_free_propertylist(&rotated_header[0]);
2218  uves_free_propertylist(&rotated_header[1]);
2219 
2220 
2221  uves_free_table(&ordertable);
2222  uves_free_propertylist(&ordertable_header);
2223  uves_free_table(&corvel);
2224  uves_free_propertylist(&corvel_header);
2225  uves_polynomial_delete(&order_locations);
2226  uves_polynomial_delete(&absolute_order);
2227  uves_free_table(&traces);
2228 
2229 
2230  uves_free_image(&master_bias);
2231  uves_free_propertylist(&master_bias_header);
2232  uves_free_image(&master_flat);
2233  uves_free_image(&mflat_noise);
2234  uves_free_propertylist(&master_flat_header);
2235  uves_free_image(&weights);
2236 
2237 
2238  /* DRS not used
2239  uves_free_table(&drs_table);
2240  uves_free_propertylist(&drs_header);
2241  */
2242 
2243  uves_free_table(&guess);
2244 
2245  uves_free_table(&line_refer);
2246  uves_free_table(&line_intmon);
2247 
2248 
2249  /* Output */
2250  uves_lt_delete(&linetable);
2251  uves_free_propertylist(&primary_header);
2252  uves_free_propertylist(&table_header);
2253  uves_qclog_delete(&qclog[0]);
2254  uves_qclog_delete(&qclog[1]);
2255 
2256  cpl_free(product_filename);
2257  cpl_free(temp);
2258 
2259  return;
2260 }
2261 
2262 
2275 static void uves_wavecal_qclog(const cpl_table* linetable,
2276  int firstabs,
2277  int lastabs,
2278  const cpl_image *arclamp,
2279  const uves_propertylist* raw_header,
2280  bool flames,
2281  int trace_number,
2282  int fibre_mask,
2283  double offset,
2284  enum uves_chip chip,
2285  cpl_table* qclog)
2286 {
2287 
2288  const char *qc_fib_drsno_name= uves_qclog_get_qc_name("DRSNO", flames, trace_number);
2289  const char *qc_fib_seq_name = uves_qclog_get_qc_name("SEQ", flames, trace_number);
2290  const char *qc_fib_pos_name = uves_qclog_get_qc_name("POS", flames, trace_number);
2291  const char *qc_fib_msk_name = uves_qclog_get_qc_name("MSK", flames, trace_number);
2292  const char *qc_fwhmavg_name = uves_qclog_get_qc_name("FWHMAVG", flames, trace_number);
2293  const char *qc_fwhmrms_name = uves_qclog_get_qc_name("FWHMRMS", flames, trace_number);
2294  const char *qc_fwhmmed_name = uves_qclog_get_qc_name("FWHMMED", flames, trace_number);
2295  const char *qc_resolavg_name = uves_qclog_get_qc_name("RESOLAVG", flames, trace_number);
2296  const char *qc_resolrms_name = uves_qclog_get_qc_name("RESOLRMS", flames, trace_number);
2297  const char *qc_resolmed_name = uves_qclog_get_qc_name("RESOLMED", flames, trace_number);
2298  const char *qc_wlenmin_name = uves_qclog_get_qc_name("WLENMIN", flames, trace_number);
2299  const char *qc_wlenmax_name = uves_qclog_get_qc_name("WLENMAX", flames, trace_number);
2300  const char *qc_ordmin_name = uves_qclog_get_qc_name("ORDMIN", flames, trace_number);
2301  const char *qc_ordmax_name = uves_qclog_get_qc_name("ORDMAX", flames, trace_number);
2302  const char *qc_detected_ordmin_name = uves_qclog_get_qc_name("ORDMIN DETECTED", flames, trace_number);
2303  const char *qc_detected_ordmax_name = uves_qclog_get_qc_name("ORDMAX DETECTED", flames, trace_number);
2304  const char *qc_nlintot_name = uves_qclog_get_qc_name("NLINTOT", flames, trace_number);
2305  const char *qc_nlinsel_name = uves_qclog_get_qc_name("NLINSEL", flames, trace_number);
2306  const char *qc_nlinsol_name = uves_qclog_get_qc_name("NLINSOL", flames, trace_number);
2307  const char *qc_line_werr_name = uves_qclog_get_qc_name("LINE WAVEERR", flames, trace_number);
2308  const char *qc_line_wsys_name = uves_qclog_get_qc_name("LINE SYSERR", flames, trace_number);
2309  const char *qc_nlinres1_name = uves_qclog_get_qc_name("NLINRES1", flames, trace_number);
2310  const char *qc_lineresidavg_name =
2311  uves_qclog_get_qc_name("LINE RESIDAVG", flames, trace_number);
2312  const char *qc_lineresidrms_name =
2313  uves_qclog_get_qc_name("LINE RESIDRMS", flames, trace_number);
2314  char comment[80];
2315  cpl_table *selected = NULL;
2316  double wmin=0;
2317  double wmax=0;
2318  double wcen=0;
2319  int nfinal=0;
2320 
2321  char test_id[80];
2322  sprintf(test_id,"%sResolution-Test-Results",flames ? "Fibre-" : "");
2323 
2325  "QC TEST1 ID",
2326  test_id,
2327  "Name of QC test",
2328  "%s"));
2329 
2330  check_nomsg( uves_qclog_add_common_wave(raw_header, chip, qclog) );
2331 
2332  if (flames)
2333  {
2334  /* Fibre ID */
2336  qc_fib_drsno_name,
2337  trace_number + 1,
2338  "DRS det. fibre seq. pos.",
2339  "%d"));
2340 
2341  /* Index */
2343  qc_fib_seq_name,
2344  trace_number + 1,
2345  "det. fibre seq. no.",
2346  "%d"));
2347 
2349  qc_fib_pos_name,
2350  offset,
2351  "det. fibre seq. rel. pos.",
2352  "%.4f"));
2353 
2355  qc_fib_msk_name,
2356  fibre_mask,
2357  "DRS det. fibre mask value",
2358  "%d"));
2359 
2360  {
2361 
2362  double exptime;
2363 
2364  check( exptime = uves_flames_pfits_get_dit(raw_header),
2365  "Error reading exposure time");
2366 
2368  "QC FIB ABSTRANS",
2369  cpl_image_get_flux(arclamp) / exptime,
2370  "abs. trans. countrate",
2371  "%.4f"));
2372  }
2373  {
2374  int n_hpix;
2375  int x, y;
2376 
2377  n_hpix = 0;
2378  for (y = 1; y <= cpl_image_get_size_y(arclamp); y++)
2379  for (x = 1; x <= cpl_image_get_size_x(arclamp); x++)
2380  {
2381  int pis_rejected;
2382  int value = cpl_image_get(arclamp, x, y, &pis_rejected);
2383 
2384  if (!pis_rejected &&
2385  (value < DRS_PTHRES_MIN || value > DRS_PTHRES_MAX))
2386  {
2387  n_hpix += 1;
2388  }
2389  }
2390 
2392  "QC NHOTPIX",
2393  n_hpix,
2394  "no. of hot pixels",
2395  "%d"));
2396 
2397  }
2398 
2399  {
2400  int plate_id;
2401  check( plate_id = uves_flames_pfits_get_plateid(raw_header),
2402  "Error reading plate ID");
2404  "QC PLATENO",
2405  plate_id,
2406  "Plate Id.",
2407  "%d"));
2408  }
2409 
2410  } /* if flames */
2411 
2412  /* FLAMES + UVES common QC params */
2413  selected = uves_extract_table_rows(linetable, "NLinSol", CPL_NOT_EQUAL_TO, 0);
2414  /* FWHM in pixels */
2415  sprintf(comment,"average FWHM in X of sel lines on TRACE%d WIN2 [pix]",trace_number);
2417  qc_fwhmavg_name,
2418  cpl_table_get_column_mean(selected,"Xwidth")*TWOSQRT2LN2,
2419  comment,
2420  "%.2f"));
2421 
2422  sprintf(comment,"stdev FWHM in X of sel lines on TRACE%d WIN2 [pix]",trace_number);
2424  qc_fwhmrms_name,
2425  cpl_table_get_column_stdev(selected,"Xwidth")*TWOSQRT2LN2,
2426  comment,
2427  "%.4f"));
2428 
2429  sprintf(comment,"median FWHM in X of sel lines on TRACE%d WIN2 [pix]",trace_number);
2431  qc_fwhmmed_name,
2432  cpl_table_get_column_median(selected,"Xwidth")*TWOSQRT2LN2,
2433  comment,
2434  "%.4f"));
2435 
2436  sprintf(comment,"mean R of sel lines on TRACE%d WIN2",trace_number);
2438  qc_resolavg_name,
2439  cpl_table_get_column_mean(selected,"Resol"),
2440  comment,
2441  "%.4f"));
2442 
2443  sprintf(comment,"stdev R of sel lines on TRACE%d WIN2",trace_number);
2445  qc_resolrms_name,
2446  cpl_table_get_column_stdev(selected,"Resol"),
2447  comment,
2448  "%.4f"));
2449 
2450  sprintf(comment,"median R of sel lines on TRACE%d WIN2",trace_number);
2452  qc_resolmed_name,
2453  cpl_table_get_column_median(selected,"Resol"),
2454  comment,
2455  "%.4f"));
2456 
2457  /* Convert A -> picometers */
2458  sprintf(comment,"mean line pos resid on TRACE%d WIN2 [pm]",trace_number);
2460  qc_lineresidavg_name,
2461  cpl_table_get_column_mean(selected, LINETAB_RESIDUAL)*100,
2462  comment,
2463  "%.4f"));
2464 
2465  sprintf(comment,"sigma line pos resid on TRACE%d WIN2 [pm]",trace_number);
2467  qc_lineresidrms_name,
2468  cpl_table_get_column_stdev(selected, LINETAB_RESIDUAL)*100,
2469  comment,
2470  "%.4f"));
2471 
2472  /* Convert A -> nm */
2473  wmin=cpl_table_get_column_min(linetable,LINETAB_LAMBDAC)/10.0;
2474  sprintf(comment,"minimum wavelength on TRACE%d WIN2 [nm]",trace_number);
2476  qc_wlenmin_name,
2477  wmin,
2478  comment,
2479  "%.4f"));
2480 
2481  wmax=cpl_table_get_column_max(linetable,LINETAB_LAMBDAC)/10.0;
2482  sprintf(comment,"maximum wavelength on TRACE%d WIN2 [nm]",trace_number);
2484  qc_wlenmax_name,
2485  wmax,
2486  comment,
2487  "%.4f"));
2488 
2489  sprintf(comment,"minimum order number expected on TRACE%d WIN2",trace_number);
2491  qc_ordmin_name,
2492  uves_min_int(firstabs, lastabs),
2493  comment,
2494  "%d"));
2495 
2496  sprintf(comment,"maximum order number expected on TRACE%d WIN2",trace_number);
2498  qc_ordmax_name,
2499  uves_max_int(firstabs, lastabs),
2500  comment,
2501  "%d"));
2502 
2503 
2504  sprintf(comment,"minimum order number detected on TRACE%d WIN2",trace_number);
2506  qc_detected_ordmin_name,
2507  cpl_table_get_column_min(linetable,"Order"),
2508  comment,
2509  "%d"));
2510 
2511  sprintf(comment,"maximum order number detected on TRACE%d WIN2",trace_number);
2513  qc_detected_ordmax_name,
2514  cpl_table_get_column_max(linetable,"Order"),
2515  comment,
2516  "%d"));
2517 
2518  sprintf(comment,"No. of lines found on TRACE%d WIN2",trace_number);
2520  qc_nlintot_name,
2521  cpl_table_get_nrow(linetable),
2522  comment,
2523  "%d"));
2524 
2525  sprintf(comment,"No. of lines selected on TRACE%d WIN2",trace_number);
2527  qc_nlinsel_name,
2528  cpl_table_get_nrow(linetable) -
2529  cpl_table_count_invalid(linetable, "Ident"),
2530  comment,
2531  "%d"));
2532 
2533  nfinal=cpl_table_get_nrow(selected);
2534  sprintf(comment,"Final No. of lines used on TRACE%d WIN2",trace_number);
2536  qc_nlinsol_name,
2537  nfinal,
2538  comment,
2539  "%d"));
2540 
2541  cpl_table* extracted=NULL;
2542  double rms_wlu=0;
2543  const char* rms_wlu_alpha=NULL;
2544  double lines_sqrt=0;
2545  double lines_werr=0;
2546  int next=0;
2547  wcen=0.5*(wmin+wmax);
2548  check_nomsg(next=cpl_table_and_selected_string(qclog,"key_name",
2549  CPL_EQUAL_TO,
2550  "QC LINE RESIDRMS WLU"));
2551  check_nomsg(extracted=cpl_table_extract_selected(qclog));
2552 
2553  check_nomsg(rms_wlu_alpha=cpl_table_get_string(extracted,"key_value",0));
2554  rms_wlu=atof(rms_wlu_alpha);
2555  lines_sqrt=sqrt(nfinal);
2556  lines_werr=rms_wlu/lines_sqrt;
2557  sprintf(comment,"Wavelength error on TRACE%d [Angstrom]",trace_number);
2559  qc_line_werr_name,
2560  lines_werr,
2561  comment,
2562  "%.3g"));
2563 
2564  uves_free_table(&extracted);
2565  uves_free_table(&selected);
2566  selected = cpl_table_duplicate(linetable);
2567  assure_mem( selected );
2568 
2569 
2570  sprintf(comment,"Wavelength systematic error on TRACE%d [Angstrom]",trace_number);
2572  qc_line_wsys_name,
2573  wcen*100./SPEED_OF_LIGHT,
2574  comment,
2575  "%f"));
2576 
2577 
2578  /* Remove unidentified lines and
2579  lines with residual > 1 A */
2580  check_nomsg( uves_erase_invalid_table_rows(selected, "Ident") );
2581  check_nomsg( uves_erase_table_rows(selected, LINETAB_RESIDUAL,
2582  CPL_NOT_LESS_THAN,
2583  1.0) );
2584 
2585  sprintf(comment,"No. of lines with residuals < 0.1 nm on TRACE%d",trace_number);
2587  qc_nlinres1_name,
2588  cpl_table_get_nrow(selected),
2589  comment,
2590  "%d"));
2591 
2592 
2593  cleanup:
2594 
2595  uves_free_string_const(&qc_fib_drsno_name);
2596  uves_free_string_const(&qc_fib_seq_name);
2597  uves_free_string_const(&qc_fib_pos_name);
2598  uves_free_string_const(&qc_fib_msk_name);
2599  uves_free_string_const(&qc_fwhmavg_name);
2600  uves_free_string_const(&qc_fwhmrms_name);
2601  uves_free_string_const(&qc_fwhmmed_name);
2602 
2603  uves_free_string_const(&qc_resolavg_name);
2604  uves_free_string_const(&qc_resolrms_name);
2605  uves_free_string_const(&qc_resolmed_name);
2606  uves_free_string_const(&qc_wlenmin_name);
2607  uves_free_string_const(&qc_wlenmax_name);
2608  uves_free_string_const(&qc_ordmin_name);
2609  uves_free_string_const(&qc_ordmax_name);
2610  uves_free_string_const(&qc_nlintot_name);
2611  uves_free_string_const(&qc_nlinsel_name);
2612  uves_free_string_const(&qc_line_werr_name);
2613  uves_free_string_const(&qc_line_wsys_name);
2614  uves_free_string_const(&qc_nlinsol_name);
2615  uves_free_string_const(&qc_nlinres1_name);
2616  uves_free_string_const(&qc_lineresidavg_name);
2617  uves_free_string_const(&qc_lineresidrms_name);
2618  uves_free_string_const(&qc_detected_ordmin_name);
2619  uves_free_string_const(&qc_detected_ordmax_name);
2620 
2621  uves_free_table(&selected);
2622 
2623  return;
2624 
2625 }
2626 
2635 static void uves_wavecal_qclog_intmon(cpl_table* table,
2636  const cpl_table *line_intmon,
2637  const uves_propertylist* raw_header,
2638  bool flames,
2639  int fibre,
2640  enum uves_chip chip,
2641  cpl_table* qclog)
2642 {
2643  const char *qc_intavg_name = NULL;
2644  const char *qc_nlinint_name = NULL;
2645 
2646  cpl_table *temp = NULL;
2647 
2649  "QC TEST2 ID",
2650  flames ? "Fibre-Line-Intensity-Test-Results"
2651  : "Line-Intensity-Test-Results",
2652  "Name of QC test",
2653  "%s"));
2654 
2656  chip, qclog) );
2657 
2658  {
2659  double tolerance = 0.001; /* (A)
2660  The lines in the line table
2661  and intmon table are considered
2662  identical if the difference
2663  is less than this number.
2664  */
2665 
2666  double exptime;
2667 
2668  int N_bright = cpl_table_get_nrow(line_intmon);
2669  int i;
2670 
2671  check( exptime = uves_pfits_get_exptime(raw_header),
2672  "Could not get exposure time");
2673 
2674  cpl_table_new_column(table, "Intensity", CPL_TYPE_DOUBLE);
2675  for (i = 0; i < cpl_table_get_nrow(table); i++)
2676  {
2677  int is_null;
2678  double ident = cpl_table_get_double(table, "Ident", i, &is_null);
2679 
2680  if (!is_null)
2681  {
2682  int bright_index = uves_wavecal_find_nearest(
2683  line_intmon, ident, 0, N_bright-1);
2684 
2685  double bright = cpl_table_get_double(
2686  line_intmon, "Wave", bright_index, NULL);
2687 
2688  if (fabs(bright - ident) < tolerance)
2689  {
2690  double peak = cpl_table_get_double(table, "Peak", i, NULL);
2691  double pixelsize =
2692  fabs(cpl_table_get_double(table, LINETAB_PIXELSIZE, i, NULL));
2693 
2694  double lambda_fwhm = cpl_table_get_double(table, "Xwidth", i, NULL)
2695  * TWOSQRT2LN2 * pixelsize;
2696  /* Line FWHM in wlu */
2697 
2698  double intensity = peak * lambda_fwhm / exptime;
2699  /* Same formula as in MIDAS */
2700 
2701  cpl_table_set_double(table, "Intensity", i, intensity);
2702  }
2703  else
2704  {
2705  cpl_table_set_invalid(table, "Intensity", i);
2706  }
2707  }
2708  else
2709  {
2710  cpl_table_set_invalid(table, "Intensity", i);
2711  }
2712  }
2713  }
2714 
2715  uves_free_table(&temp);
2716  temp = cpl_table_duplicate(table);
2717  uves_erase_invalid_table_rows(temp, "Intensity");
2718 
2719  {
2720  double mean;
2721  if (cpl_table_get_nrow(temp) == 0)
2722  {
2723  uves_msg_warning("No bright lines found!");
2724  mean = 0;
2725  }
2726  else
2727  {
2728  mean = cpl_table_get_column_mean(temp, "Intensity");
2729  }
2730 
2731  if (flames)
2732  {
2733  qc_intavg_name = uves_sprintf("QC FIB%d INTAVG", fibre+1); /* Count 1-9 */
2734  qc_nlinint_name = uves_sprintf("QC FIB%d NLININT", fibre+1);
2735  }
2736  else
2737  {
2738  qc_intavg_name = uves_sprintf("QC INTAVG");
2739  qc_nlinint_name = uves_sprintf("QC NLININT");
2740  }
2741 
2743  qc_intavg_name,
2744  mean,
2745  "average intensity of line list on TRACE0 WIN2",
2746  "%.4f"));
2747 
2749  qc_nlinint_name,
2750  cpl_table_get_nrow(temp),
2751  "No. of lines to measure INTAVG on TRACE0 WIN2",
2752  "%d"));
2753  }
2754 
2755  cleanup:
2756  uves_free_string_const(&qc_intavg_name);
2757  uves_free_string_const(&qc_nlinint_name);
2758  uves_free_table(&temp);
2759  return;
2760 }
2761 
2762