UVES Pipeline Reference Manual  5.4.6
uves_wavecal_identify.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: 2012-05-02 06:11:40 $
23  * $Revision: 1.38 $
24  * $Name: not supported by cvs2svn $
25  * $Log: not supported by cvs2svn $
26  * Revision 1.37 2012/03/02 16:40:40 amodigli
27  * fixed warning related to upgrade to CPL6
28  *
29  * Revision 1.36 2011/12/08 14:00:02 amodigli
30  * Fox warnings with CPL6
31  *
32  * Revision 1.35 2011/04/14 11:25:40 amodigli
33  * fixed typo QC key in comments
34  *
35  * Revision 1.34 2011/04/11 09:07:41 amodigli
36  * implemented QC comments corrections from DFO
37  *
38  * Revision 1.33 2011/04/11 07:53:12 amodigli
39  * uniformed QC param key name
40  *
41  * Revision 1.32 2011/03/23 12:27:31 amodigli
42  * changed QC key as user likes
43  *
44  * Revision 1.31 2011/03/23 10:08:47 amodigli
45  * added QC to better characterize wave accuracy
46  *
47  * Revision 1.30 2010/09/24 09:32:09 amodigli
48  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
49  *
50  * Revision 1.28 2007/07/23 14:57:30 jmlarsen
51  * Make workaround work
52  *
53  * Revision 1.27 2007/07/23 12:40:37 jmlarsen
54  * Update to CPL4
55  *
56  * Revision 1.26 2007/06/06 08:17:33 amodigli
57  * replace tab with 4 spaces
58  *
59  * Revision 1.25 2007/05/22 11:46:15 jmlarsen
60  * Removed 1d wavecal mode which was not supported
61  *
62  * Revision 1.24 2007/05/16 16:33:42 amodigli
63  * fixed leak
64  *
65  * Revision 1.23 2007/05/10 08:32:48 jmlarsen
66  * Minor output message change
67  *
68  * Revision 1.22 2007/05/07 14:26:44 jmlarsen
69  * Added QC.NLINSOL parameter
70  *
71  * Revision 1.21 2007/05/07 07:13:59 jmlarsen
72  * Made resolution computation robust against negative dl/dx
73  *
74  * Revision 1.20 2007/04/27 07:22:57 jmlarsen
75  * Implemented possibility to use automatic polynomial degree
76  *
77  * Revision 1.19 2007/04/13 07:34:54 jmlarsen
78  * Removed dead code
79  *
80  * Revision 1.18 2007/04/10 07:12:09 jmlarsen
81  * Changed interface of polynomial_regression_2d()
82  *
83  * Revision 1.17 2007/03/15 12:36:44 jmlarsen
84  * Added experimental ppm code
85  *
86  * Revision 1.16 2007/03/05 10:24:14 jmlarsen
87  * Do kappa-sigma rejection only in second loop
88  *
89  * Revision 1.15 2007/02/22 15:37:35 jmlarsen
90  * Use kappa-sigma clipping when fitting dispersion
91  *
92  * Revision 1.14 2007/01/15 08:58:51 jmlarsen
93  * Added text output
94  *
95  * Revision 1.13 2006/11/06 15:19:42 jmlarsen
96  * Removed unused include directives
97  *
98  * Revision 1.12 2006/10/12 11:36:48 jmlarsen
99  * Reduced max line length
100  *
101  * Revision 1.11 2006/10/10 11:20:11 jmlarsen
102  * Renamed line table columns to match MIDAS
103  *
104  * Revision 1.10 2006/08/17 14:11:25 jmlarsen
105  * Use assure_mem macro to check for memory allocation failure
106  *
107  * Revision 1.9 2006/08/17 13:56:53 jmlarsen
108  * Reduced max line length
109  *
110  * Revision 1.8 2006/08/11 14:36:37 jmlarsen
111  * Added profiling info
112  *
113  * Revision 1.7 2006/08/07 11:35:08 jmlarsen
114  * Removed hardcoded constant
115  *
116  * Revision 1.6 2006/07/14 12:52:57 jmlarsen
117  * Exported/renamed function find_nearest
118  *
119  * Revision 1.5 2006/07/14 12:44:26 jmlarsen
120  * Use less significant digits
121  *
122  * Revision 1.4 2006/04/24 09:33:48 jmlarsen
123  * Shortened max line length
124  *
125  * Revision 1.3 2006/03/03 13:54:11 jmlarsen
126  * Changed syntax of check macro
127  *
128  * Revision 1.2 2006/02/15 13:19:15 jmlarsen
129  * Reduced source code max. line length
130  *
131  * Revision 1.1 2006/02/03 07:46:30 jmlarsen
132  * Moved recipe implementations to ./uves directory
133  *
134  * Revision 1.31 2005/12/20 08:11:44 jmlarsen
135  * Added CVS entry
136  *
137  */
138 
139 /*----------------------------------------------------------------------------*/
143 /*----------------------------------------------------------------------------*/
146 #ifdef HAVE_CONFIG_H
147 # include <config.h>
148 #endif
149 
150 #include <uves_wavecal_identify.h>
151 
152 #include <uves_wavecal_utils.h>
153 #include <uves_utils.h>
154 #include <uves_utils_wrappers.h>
155 #include <uves_error.h>
156 #include <uves_msg.h>
157 #include <cpl_ppm.h> /* missing from cpl.h */
158 #include <uves_qclog.h>
159 #include <cpl.h>
160 
161 #include <math.h>
162 #include <float.h>
163 
164 #define USE_PPM 0
165 
166 static cpl_error_code verify_calibration(const cpl_table *selected,
167  const cpl_table *linetable,
168  double TOLERANCE,
169  double red_chisq,cpl_table* qclog);
170 static cpl_error_code compute_lambda(cpl_table *linetable,
171  const polynomial *dispersion_relation,
172  const polynomial *dispersion_variance,
173  bool verbose);
174 
175 static int identify_lines(cpl_table *linetable,
176  const cpl_table *line_refer,
177  double ALPHA);
178 
179 static polynomial *calibrate_global(const cpl_table *linetable,
180  cpl_table **selected,
181  int degree, bool verbose,
182  bool reject,
183  double TOLERANCE,
184  double kappa,
185  double *red_chisq,
186  polynomial **dispersion_variance,
187  double *pixelsize,
188  double *rms_wlu,
189  double *rms_pixels);
190 
191 /*----------------------------------------------------------------------------*/
231 /*----------------------------------------------------------------------------*/
232 
233 polynomial *
234 uves_wavecal_identify(cpl_table *linetable,
235  const cpl_table *line_refer,
236  const polynomial *guess_dispersion,
237  int DEGREE, double TOLERANCE,
238  double ALPHA, double MAXERROR,
239  double kappa,
240  const int trace,const int window,cpl_table* qclog)
241 {
242  polynomial *dispersion_relation = NULL; /* Result */
243  polynomial *dispersion_variance = NULL; /* Variance of result,
244  written to line table */
245  int current_id; /* Current and previous number of line identifications */
246  int previous_id;
247  int idloop; /* Number of iterations of grand loop */
248  int n; /* Number of iterations in ID loop */
249  double pixelsize; /* Average conversion factor between pixels and wlu */
250  double red_chisq; /* Reduced chi^2 of fit */
251  cpl_table *selected = NULL; /* Lines used in final fit */
252  char qc_key[40];
253 
254  passure( linetable != NULL, " ");
255  passure( line_refer != NULL, " ");
256  passure( guess_dispersion != NULL, " ");
257 
258  assure( 0 < ALPHA && ALPHA <= 1, CPL_ERROR_ILLEGAL_INPUT,
259  "Illegal alpha = %e", ALPHA);
260 
261  /* Calculate LambdaC from the initial dispersion relation */
262  {
263  cpl_table_new_column(linetable, LINETAB_LAMBDAC , CPL_TYPE_DOUBLE);
264  cpl_table_new_column(linetable, "dLambdaC" , CPL_TYPE_DOUBLE);
265  cpl_table_new_column(linetable, LINETAB_PIXELSIZE , CPL_TYPE_DOUBLE);
266  cpl_table_new_column(linetable, LINETAB_RESIDUAL , CPL_TYPE_DOUBLE);
267  cpl_table_new_column(linetable, "Residual_pix" , CPL_TYPE_DOUBLE);
268  cpl_table_new_column(linetable, "Lambda_candidate" , CPL_TYPE_DOUBLE);
269  cpl_table_new_column(linetable, "dLambda_candidate", CPL_TYPE_DOUBLE);
270  cpl_table_new_column(linetable, "dLambda_cat_sq" , CPL_TYPE_DOUBLE);
271  cpl_table_new_column(linetable, "dLambda_nn_sq" , CPL_TYPE_DOUBLE);
272 
273  /* Create columns 'Ident' and 'dIdent' (uncertainty) and fill with
274  invalid (no identification made) */
275  cpl_table_new_column(linetable, "Ident", CPL_TYPE_DOUBLE);
276  cpl_table_new_column(linetable, "dIdent",CPL_TYPE_DOUBLE);
277  cpl_table_set_column_invalid(linetable, "Ident", 0, cpl_table_get_nrow(linetable));
278  cpl_table_set_column_invalid(linetable, "dIdent",0, cpl_table_get_nrow(linetable));
279 
280  cpl_table_set_column_unit(linetable,LINETAB_LAMBDAC,"Angstrom" );
281  cpl_table_set_column_unit(linetable,"dLambdaC","Angstrom" );
282  cpl_table_set_column_unit(linetable,LINETAB_PIXELSIZE,"Angstrom" );
283  cpl_table_set_column_unit(linetable,LINETAB_RESIDUAL,"Angstrom" );
284  cpl_table_set_column_unit(linetable,"Residual_pix","Angstrom" );
285  cpl_table_set_column_unit(linetable,"Lambda_candidate","Angstrom" );
286  cpl_table_set_column_unit(linetable,"dLambda_candidate","Angstrom" );
287  cpl_table_set_column_unit(linetable,"dLambda_cat_sq","Angstrom" );
288  cpl_table_set_column_unit(linetable,"dLambda_nn_sq","Angstrom" );
289  cpl_table_set_column_unit(linetable,"Ident","Angstrom" );
290  cpl_table_set_column_unit(linetable,"dIdent","Angstrom" );
291  /* Residuals are not calculated because 'Ident' is invalid */
292  check( compute_lambda(linetable, guess_dispersion, NULL, false),
293  "Error applying dispersion relation");
294  }
295 
296 
297 #if USE_PPM
298  for (idloop = 2; idloop <= 2; idloop += 1)
299 #else
300  for (idloop = 1; idloop <= 2; idloop += 1)
301 #endif
302  {
303 
304  current_id = 0;
305  n = 0;
306  /* Iterate until no more identifications can be made */
307  do {
308  double rms_wlu;
309  double rms_pixels;
310  bool reject = (idloop == 2);
311 #if USE_PPM
312  int nident_ppm;
313 #endif
314 
315  previous_id = current_id;
316  n++;
317 
318  /* Identify lines */
319  check( current_id = identify_lines(linetable, line_refer, ALPHA),
320  "Error identifying lines");
321 
322 
323 #if USE_PPM
324  /* Try PPM */
325  check( nident_ppm = uves_wavecal_identify_lines_ppm(linetable, line_refer),
326  "Error during point pattern matching");
327 
328  cpl_table_erase_column(linetable, "Ident");
329  cpl_table_duplicate_column(linetable, "Ident", linetable, "Ident_ppm");
330  current_id = nident_ppm;
331 
332  /* FIXME: This only works if 'dIdent' is constant.
333  We should propagate error bars during ppm matching */
334  cpl_table_fill_column_window(linetable, "dIdent",
335  0, cpl_table_get_nrow(linetable),
336  cpl_table_get_column_mean(linetable, "dIdent"));
337 #endif
338 
339  /* Calibrate with
340  * 1st loop: tolerance=infinity (i.e. all identified lines are considered good).
341  * 2nd loop: use specified tolerance (ignore outliers)
342  */
343  uves_polynomial_delete(&dispersion_relation);
344  uves_polynomial_delete(&dispersion_variance);
345 
346  check( dispersion_relation = calibrate_global(
347  linetable, NULL,
348  DEGREE, false,
349  reject,
350  TOLERANCE,
351  kappa,
352  &red_chisq,
353  &dispersion_variance,
354  &pixelsize,
355  &rms_wlu,
356  &rms_pixels),
357  "Could not perform global calibration");
358 
359  uves_msg_debug("Average pixelsize = %f wlu", pixelsize);
360  if (idloop == 1)
361  {
362  uves_msg("%d identifications made. RMS = %.5f wlu = %.3f "
363  "pixels (no rejection)",
364  current_id, rms_wlu, rms_pixels);
365 
366 
367 
368 
369  }
370  else
371  {
372  uves_msg("%d identifications made. RMS = %.5f wlu = %.3f "
373  "pixels (%f %s rejection, kappa = %.1f)",
374  current_id, rms_wlu, rms_pixels,
375  fabs(TOLERANCE), (TOLERANCE > 0) ? "pixels" : "wlu",
376  kappa);
377  }
378 
379  sprintf(qc_key,"QC TRACE%d WIN%d NLINID%d",trace,window,idloop);
380  ck0_nomsg(uves_qclog_add_int(qclog,qc_key,current_id,
381  "ThAr lamp identified lines",
382  "%d"));
383 
384 #if USE_PPM
385  uves_msg("%d identifications from point pattern matching",
386  nident_ppm);
387 #endif
388 
389  assure( rms_pixels < MAXERROR, CPL_ERROR_CONTINUE,
390  "Wavelength calibration did not converge. "
391  "After %d iterations the RMS was %f pixels. "
392  "Try to improve on the initial solution", n, rms_pixels);
393 
394 
395  /* Apply calibration result */
396  check( compute_lambda(linetable, dispersion_relation, dispersion_variance,
397  false),
398  "Error applying dispersion relation");
399 
400 
401  }
402  while (current_id > previous_id) ;
403 
404  sprintf(qc_key,"QC TRACE%d WIN%d NLINID NITERS",trace,window);
405  ck0_nomsg(uves_qclog_add_int(qclog,qc_key,idloop+1,
406  "Number of iterations",
407  "%d"));
408 
409 
410 
411  if (idloop == 1)
412  {
413  /*
414  * Remove all identifications and repeat
415  */
416 
417  uves_msg("Identification loop converged. Resetting identifications");
418  cpl_table_set_column_invalid(linetable, "Ident", 0,
419  cpl_table_get_nrow(linetable));
420  }
421  }
422 
423  /* Calibrate again with a global polynomial, but this time don't
424  use lines with residuals worse than TOLERANCE */
425  uves_polynomial_delete(&dispersion_relation);
426  uves_polynomial_delete(&dispersion_variance);
427  uves_free_table(&selected);
428 
429  check( dispersion_relation = calibrate_global(linetable,
430  &selected,
431  DEGREE, true,
432  true, /* do rejection? */
433  TOLERANCE,
434  kappa,
435  &red_chisq,
436  &dispersion_variance,
437  NULL, NULL, NULL),
438  "Could not perform global calibration");
439 
440  /* Update the computed wavelengths */
441  check( compute_lambda(linetable, dispersion_relation, dispersion_variance,
442  true),
443  "Error applying dispersion relation");
444 
445  /* Add columns 'Select' and 'NLinSol' to linetable.
446  The columns defines which lines were identified,
447  and which lines were used in the final fit */
448  {
449  int i, j;
450 
451  /* Tables are sorted by Order, X */
452 
453  cpl_table_new_column(linetable, "NLinSol", CPL_TYPE_INT);
454  cpl_table_new_column(linetable, "Select", CPL_TYPE_INT);
455 
456  cpl_table_fill_column_window_int(linetable, "NLinSol",
457  0, cpl_table_get_nrow(linetable),
458  0);
459  cpl_table_fill_column_window_int(linetable, "Select",
460  0, cpl_table_get_nrow(linetable),
461  0);
462 
463  j = 0;
464  for (i = 0; i < cpl_table_get_nrow(selected); i++) {
465  int order = cpl_table_get_int(selected, "Order", i, NULL);
466  double x = cpl_table_get_double(selected, "X", i, NULL);
467  int order2;
468  double x2;
469 
470  /* Find this line in the original linetable */
471  passure( j < cpl_table_get_nrow(linetable), "%d %" CPL_SIZE_FORMAT "",
472  j, cpl_table_get_nrow(linetable));
473  do {
474  order2 = cpl_table_get_int(linetable, "Order", j, NULL);
475  x2 = cpl_table_get_double(linetable, "X", j, NULL);
476  if (cpl_table_is_valid(linetable, "Ident", j))
477  {
478  cpl_table_set_int(linetable, "Select", j, 1);
479  }
480  j++;
481 
482  } while (order2 < order || x2 < x - 0.1);
483 
484  passure( order2 == order && fabs(x2 - x) < 0.1,
485  "%d %d %g %g", order2, order, x2, x);
486 
487  cpl_table_set_int(linetable, "NLinSol", j-1, 1);
488  }
489  }
490 
491  /* Display results */
492  check( verify_calibration(selected, linetable, TOLERANCE, red_chisq,qclog),
493  "Error verifying calibration");
494 
495  cleanup:
496  uves_free_table(&selected);
497  uves_polynomial_delete(&dispersion_variance);
498  return dispersion_relation;
499 }
500 
501 /*----------------------------------------------------------------------------*/
515 /*----------------------------------------------------------------------------*/
516 static cpl_error_code
517 verify_calibration(const cpl_table *selected,
518  const cpl_table *linetable, double TOLERANCE,
519  double red_chisq, cpl_table* qclog)
520 {
521  cpl_table *brightest = NULL;
522  double median_intensity;
523  int ninvalid; /* Number of unidentified lines among the brightest half */
524  double ratio;
525  double rms_wlu;
526  double rms_pixels;
527  double rms_speed;
528  char qc_key[40];
529 
530  {
531  double mean;
532  double stdev;
533 
534  check(( mean = cpl_table_get_column_mean (selected, LINETAB_RESIDUAL),
535  stdev= cpl_table_get_column_stdev(selected, LINETAB_RESIDUAL),
536  rms_wlu = sqrt(mean*mean + stdev*stdev),
537 
538  mean = cpl_table_get_column_mean (selected, "Residual_pix"),
539  stdev= cpl_table_get_column_stdev(selected, "Residual_pix"),
540  rms_pixels = sqrt(mean*mean + stdev*stdev)),
541  "Error reading RMS of fit");
542  }
543  rms_speed=rms_wlu * SPEED_OF_LIGHT/
544  cpl_table_get_column_mean(selected,LINETAB_LAMBDAC);
545  uves_msg("%" CPL_SIZE_FORMAT " lines accepted", cpl_table_get_nrow(selected));
546  uves_msg("Average RMS of calibration (tolerance = %.3f %s) = %.5f wlu = %.4f pixels ~ %.1f m/s",
547  fabs(TOLERANCE),
548  (TOLERANCE > 0) ? "pixels" : "wlu",
549  rms_wlu, rms_pixels, rms_speed);
550 
551  sprintf(qc_key,"QC LINE RESIDRMS WLU");
552  ck0_nomsg(uves_qclog_add_double(qclog,qc_key,rms_wlu,
553  "Line ID RMS TRACE0 WIN2 [Angstrom]",
554  "%f"));
555  sprintf(qc_key,"QC LINE RESIDRMS PIX");
556  ck0_nomsg(uves_qclog_add_double(qclog,qc_key,rms_pixels,
557  "Line ID RMS TRACE0 WIN2 [pix]",
558  "%f"));
559  sprintf(qc_key,"QC LINE RESIDRMS SPEED");
560  ck0_nomsg(uves_qclog_add_double(qclog,qc_key,rms_speed,
561  "Line ID RMS TRACE0 WIN2 [m/s]",
562  "%f"));
563 
564 
565  uves_msg("Reduced chi^2 of calibration = %f", red_chisq);
566  sprintf(qc_key,"QC LINE IDCHI2");
567  ck0_nomsg(uves_qclog_add_double(qclog,qc_key,red_chisq,
568  "Reduced chi^2 of line ID TRACE0 WIN2",
569  "%f"));
570 
571  if (red_chisq < .01)
572  {
573  uves_msg_warning("Reduced chi^2 of fit is less than 1/100: %f",
574  red_chisq);
575  }
576  if (red_chisq > 100)
577  {
578  uves_msg_warning("Reduced chi^2 of fit is greater than 100: %f",
579  red_chisq);
580  }
581 
582  check(( median_intensity = cpl_table_get_column_median(linetable, "Peak"),
583  brightest = uves_extract_table_rows(linetable, "Peak",
584  CPL_GREATER_THAN,
585  median_intensity),
586  ninvalid = cpl_table_count_invalid(brightest, "Ident")),
587  "Error counting identifications");
588 
589  ratio = 1 - ((double) ninvalid)/cpl_table_get_nrow(brightest);
590  uves_msg("Percentage of identifications among the half brighter lines : %.2f %%",
591  100*ratio);
592 
593  sprintf(qc_key,"QC LINE HALFBRIG");
594  ck0_nomsg(uves_qclog_add_double(qclog,qc_key,100*ratio,
595  "Half brighter lines frac TRACE0 WIN2",
596  "%f"));
597 
598  cleanup:
599  uves_free_table(&brightest);
600 
601  return cpl_error_get_code();
602 }
603 
604 /*----------------------------------------------------------------------------*/
618 /*----------------------------------------------------------------------------*/
619 static cpl_error_code
620 compute_lambda(cpl_table *linetable,
621  const polynomial *dispersion_relation,
622  const polynomial *dispersion_variance,
623  bool verbose)
624 {
625  int i;
626  bool printed_warning = false;
627 
628  /* Check input */
629  passure(linetable != NULL, " ");
630  passure(dispersion_relation != NULL, " ");
631  /* 'dispersion_variance' may be NULL */
632 
633  passure( uves_polynomial_get_dimension(dispersion_relation) == 2, "%d",
634  uves_polynomial_get_dimension(dispersion_relation));
635 
636  /* Input columns */
637  passure(cpl_table_has_column(linetable, "X") , " ");
638  passure(cpl_table_has_column(linetable, "Order") , " ");
639  passure(cpl_table_has_column(linetable, "Ident") , " ");
640  /* Output columns */
641  passure(cpl_table_has_column(linetable, LINETAB_LAMBDAC) , " ");
642  /* The column 'dLambdaC' is set to invalid if 'dispersion_variance' is NULL */
643  passure(cpl_table_has_column(linetable, "dLambdaC") , " ");
644  passure(cpl_table_has_column(linetable, "dIdent") , " ");
645  passure(cpl_table_has_column(linetable, LINETAB_RESIDUAL), " ");
646  passure(cpl_table_has_column(linetable, "Residual_pix"), " ");
647  passure(cpl_table_has_column(linetable, LINETAB_PIXELSIZE) , " ");
648 
649  /* The linetable is sorted w.r.t. order.
650  Move to the first order above minorder */
651  for(i = 0; i < cpl_table_get_nrow(linetable); i++)
652  {
653  int order;
654  double x, dfdx;
655  double lambdac, dlambdac, pixelsize;
656  order = cpl_table_get_int(linetable, "Order", i, NULL);
657 
658  x = cpl_table_get_double(linetable, "X", i, NULL);
659 
660  /* Evaluate the dispersion relation
661  m.lambda = f(x,m) (2d global fit) */
662 
663  lambdac =
664  uves_polynomial_evaluate_2d(dispersion_relation, x, order) / order;
665 
666  /* Pixelsize = dl/dx = (df/dx)/m (for fixed m) */
667  dfdx = uves_polynomial_derivative_2d(dispersion_relation, x, order, 1);
668  if (dfdx < 0) {
669  if (!printed_warning && verbose) {
670  uves_msg_warning("Inferred dispersion (dlambda/dx) is negative at"
671  "(x, order) = (%f, %d)", x, order);
672  printed_warning = true; /* To avoid repeating the same warning */
673  }
674  else {
675  uves_msg_debug("Inferred dispersion (dlambda/dx) is negative at "
676  "(x, order) = (%f, %d)", x, order);
677  }
678  }
679  pixelsize = dfdx / order;
680 
681  check(( cpl_table_set_double(linetable, LINETAB_LAMBDAC , i, lambdac),
682  cpl_table_set_double(linetable, LINETAB_PIXELSIZE, i, pixelsize)),
683  "Error writing table");
684 
685  if (dispersion_variance != NULL)
686  {
687  /* d( lambda (x, order) ) =
688  d( lambda*m(x, order) ) / m */
689  dlambdac =
690  sqrt(uves_polynomial_evaluate_2d(dispersion_variance, x, order))
691  / order;
692 
693  cpl_table_set_double(linetable, "dLambdaC" , i, dlambdac);
694  }
695  else
696  {
697  /* Only the ratio of a line's "dLambdaC" to other
698  lines' are used, so set "dLambdaC" to a constant value
699  when the actual uncertainty is not known
700  */
701  cpl_table_set_double(linetable, "dLambdaC" , i, 1.0);
702  }
703 
704  /* If line is identified, calculate residual */
705  if (cpl_table_is_valid(linetable, "Ident", i))
706  {
707  double ident = cpl_table_get_double(linetable, "Ident", i, NULL);
708  cpl_table_set_double(linetable, LINETAB_RESIDUAL, i,
709  ident - lambdac);
710  cpl_table_set_double(linetable, "Residual_pix", i,
711  (ident - lambdac)/pixelsize);
712  }
713  else
714  {
715  cpl_table_set_invalid(linetable, LINETAB_RESIDUAL, i);
716  cpl_table_set_invalid(linetable, "Residual_pix", i);
717  }
718  }
719 
720  /* Sort by 'Order' (ascending), then 'X' (ascending) */
721  check( uves_sort_table_2(linetable, "Order", "X", false, false),
722  "Error sorting table");
723 
724  cleanup:
725  return cpl_error_get_code();
726 }
727 
728 
729 /*----------------------------------------------------------------------------*/
766 /*----------------------------------------------------------------------------*/
767 
768 static int
769 identify_lines(cpl_table *linetable, const cpl_table *line_refer, double ALPHA)
770 {
771  int number_identifications = 0; /* Result */
772  int linetable_size;
773  int linerefer_size;
774  int row;
775  int *histogram = NULL;
776  const double minlog = -5.0; /* Histogram (it's sort of ugly
777  to hardcode these numbers, but
778  as long as it works, ...) */
779  const double maxlog = 15.0;
780  const int nbins = 400;
781  double error = 0; /* Dimensionless factor
782  that controls IDs */
783  double average_dlambda_com = 0; /* Average of uncertainty of
784  predicted wavelenghts */
785 
786  /* Check input */
787  passure( linetable != NULL, " ");
788  /* Line table input columns */
789  passure( cpl_table_has_column(linetable, LINETAB_LAMBDAC ), " "); /* Predicted
790  wavelength */
791  passure( cpl_table_has_column(linetable, "dLambdaC" ), " "); /* Predicted wavelength
792  uncertainty */
793  passure( cpl_table_has_column(linetable, "X" ), " "); /* Line position, used
794  only for messaging */
795  passure( cpl_table_has_column(linetable, "Order" ), " "); /* Absolute order number
796  of line */
797  passure( cpl_table_has_column(linetable, "Xwidth" ), " "); /* Line width (sigma) */
798  passure( cpl_table_has_column(linetable, LINETAB_PIXELSIZE), " "); /* Pixelsize */
799 
800  /* Line table output columns */
801  passure( cpl_table_has_column(linetable, "Ident" ), " "); /* Identified catalogue
802  wavelength */
803  passure( cpl_table_has_column(linetable, "dIdent" ), " "); /* Uncertainty of IDed
804  catalogue wavelength */
805 
806  /* Catalogue */
807  passure( line_refer != NULL, " ");
808  passure( cpl_table_has_column(line_refer, "Wave" ), " "); /* Catalogue wavelength */
809  passure( cpl_table_has_column(line_refer, "dWave"), " "); /* Uncertainty of
810  catalogue wavelength */
811 
812  linetable_size = cpl_table_get_nrow(linetable);
813  linerefer_size = cpl_table_get_nrow(line_refer);
814  assure(linerefer_size >= 1, CPL_ERROR_ILLEGAL_INPUT, "Empty line reference table");
815 
816  /* Parameter */
817  passure( 0 < ALPHA && ALPHA <= 1, "%e", ALPHA);
818 
819  /* Get average uncertainty of predicted wavelength */
820  average_dlambda_com = cpl_table_get_column_median(linetable, "dLambdaC");
821 
822  /* Initialize histogram to zero */
823  histogram = cpl_calloc(nbins, sizeof(int));
824  assure_mem( histogram );
825 
826 
827  /* First: Find distance to closest catalogue match,
828  distance to nearest neighbour,
829  and calculate histogram (to get average of distances to nearest neighbour) */
830  for (row = 0; row < linetable_size; row++) {
831  double lambda_com; /* Computed (predicted) wavelength */
832  double line_width; /* Line width (sigma) in wlu */
833  double line_fwhm; /* Line FWHM in wlu */
834  int order; /* (Absolute) order of detected wavelength */
835  double lambda_cat; /* Catalogue wavelength */
836  double lambda_cat_sigma; /* Catalogue wavelength uncertainty */
837  double distance_cat_sq; /* Distance to catalogue wavelength (squared) */
838  double nn_distance_sq; /* Distance to nearest neighbour (squared) */
839  int row_cat; /* Row number of best matching catalogue wavelength */
840 
841  /* Read line table */
842  lambda_com = cpl_table_get_double(linetable, LINETAB_LAMBDAC , row, NULL);
843  order = cpl_table_get_int (linetable, "Order" , row, NULL);
844 
845 
846  line_width =
847  cpl_table_get_double(linetable, "Xwidth" , row, NULL) *
848  fabs(cpl_table_get_double(linetable, LINETAB_PIXELSIZE , row, NULL));
849  /* Convert pixel->wlu */
850 
851  line_fwhm = TWOSQRT2LN2 * line_width;
852 
853  /* Find closest match in catalogue */
854  row_cat = uves_wavecal_find_nearest(
855  line_refer, lambda_com, 0, linerefer_size - 1);
856  lambda_cat = cpl_table_get_double(line_refer, "Wave", row_cat, NULL);
857  lambda_cat_sigma = cpl_table_get_double(line_refer, "dWave",row_cat, NULL);
858 
859  /* Distance to closest match */
860  distance_cat_sq = (lambda_com - lambda_cat)*(lambda_com - lambda_cat);
861 
862  /* Determine the distance to the next neighbour
863  * There are (max) 4 candiates: 2 neigbours in spectrum (i.e. line table)
864  * and 2 neigbours in line catalogue
865  */
866  {
867  double lambda_com_prev, lambda_com_next;
868  int order_prev, order_next;
869  double lambda_cat_prev, lambda_cat_next;
870 
871  nn_distance_sq = DBL_MAX;
872 
873  /* Read previous and next rows of line table */
874  if (row >= 1)
875  {
876  order_prev = cpl_table_get_int (
877  linetable, "Order" , row - 1, NULL);
878  lambda_com_prev = cpl_table_get_double(
879  linetable, LINETAB_LAMBDAC, row - 1, NULL);
880 
881  if (order == order_prev)
882  {
883  nn_distance_sq = uves_min_double(nn_distance_sq,
884  (lambda_com_prev - lambda_com)*
885  (lambda_com_prev - lambda_com)
886  );
887  }
888  }
889 
890  if (row <= linetable_size - 2)
891  {
892  order_next = cpl_table_get_int (linetable, "Order",
893  row + 1, NULL);
894  lambda_com_next = cpl_table_get_double(linetable, LINETAB_LAMBDAC,
895  row + 1, NULL);
896 
897  if (order == order_next)
898  {
899  nn_distance_sq = uves_min_double(nn_distance_sq,
900  (lambda_com_next - lambda_com)*
901  (lambda_com_next - lambda_com)
902  );
903  }
904  }
905 
906  /* Read previous and next rows of catalogue */
907  if (row_cat >= 1)
908  {
909  lambda_cat_prev = cpl_table_get_double(
910  line_refer, "Wave", row_cat - 1, NULL);
911 
912  nn_distance_sq = uves_min_double(
913  nn_distance_sq,
914  (lambda_cat_prev - lambda_cat)*
915  (lambda_cat_prev - lambda_cat)
916  );
917  }
918  if (row_cat <= linerefer_size - 2)
919  {
920  lambda_cat_next = cpl_table_get_double(
921  line_refer, "Wave", row_cat + 1, NULL);
922 
923  nn_distance_sq = uves_min_double(
924  nn_distance_sq,
925  (lambda_cat_next - lambda_cat)*
926  (lambda_cat_next - lambda_cat)
927  );
928  }
929 
930  /* Update distance to nearest neighbour with a
931  safety margin (determined by parameter ALPHA < 1) */
932  if (nn_distance_sq < DBL_MAX)
933  {
934  nn_distance_sq *= ALPHA*ALPHA;
935  }
936 
937  }/* Find next neighbour */
938 
939  /* Update line table */
940  cpl_table_set_double(linetable, "Lambda_candidate", row, lambda_cat);
941  cpl_table_set_double(linetable, "dLambda_candidate",row, lambda_cat_sigma);
942  cpl_table_set_double(linetable, "dLambda_cat_sq", row, distance_cat_sq);
943  cpl_table_set_double(linetable, "dLambda_nn_sq", row, nn_distance_sq);
944 
945  /* Update histogram with the interval
946  [distance_cat_sq ; nn_distance_sq] (in units of line_fwhm) */
947  {
948  int ilow = uves_round_double((0.5*log(distance_cat_sq/(line_fwhm*line_fwhm))
949  - minlog)/(maxlog - minlog) * nbins);
950  int ihigh = uves_round_double((0.5*log(nn_distance_sq /(line_fwhm*line_fwhm))
951  - minlog)/(maxlog - minlog) * nbins);
952  int i;
953 
954  for (i = uves_max_int(ilow, 0); i < uves_min_int(ihigh, nbins); i++)
955  {
956  histogram[i] += 1;
957  }
958  }
959  }/* ... finding neighbours */
960 
961  /* Determine error as peak of histogram */
962  {
963  int i;
964  int maxfreq = -1;
965  for (i = 0; i < nbins; i++)
966  {
967  uves_msg_debug("histogram[%d] = %d", i, histogram[i]);
968  if (histogram[i] > maxfreq)
969  {
970  maxfreq = histogram[i];
971  error = exp( i / ((double)nbins) * (maxlog - minlog) + minlog ) ;
972  /* == the dimensionless factor to be multiplied by Xwidth */
973  }
974  }
975  uves_msg_debug("Dimensionless error factor is %f", error);
976  }
977 
978  /* Sketch of situation:
979 
980  lambda_com Nearest neighbour
981 
982  | |
983  | | |
984  | | |
985  | | |
986  |
987 
988  lambda_cat
989 
990 
991  The 'average' (as inferred from the histogram)
992  midpoint between 'lambda_cat' and 'nearest neighbour'
993  is at 'error' * 'line_fwhm' .
994  */
995 
996  /* Make the identification if
997 
998  1) the catalogue candidate is within two sigma:
999  | lambda_cat - lambda_com | < 2 * dlambda_com
1000 
1001  and
1002 
1003  2) after multiplying the distance to the nearest neighbour by ALPHA < 1,
1004  the nearest neighbour is farther away than the catalogue wavelength
1005  distance_nn > distance_cat
1006  and farther away than the tolerance
1007  distance_nn > line_fwhm * error
1008 
1009  */
1010  for (row = 0; row < linetable_size; row++)
1011  {
1012  double distance_cat_sq; /* Distance to catalogue wavelength (squared) */
1013  double nn_distance_sq; /* Distance to nearest neighbour (squared) */
1014  double tolerance_sq;
1015  double dlambda_com;
1016  double line_width; /* Line width (1 sigma) */
1017  double line_fwhm;
1018  double lambda_cat;
1019  double lambda_cat_sigma; /* Uncertainty of lambda_cat */
1020 
1021  lambda_cat = cpl_table_get_double(linetable, "Lambda_candidate", row, NULL);
1022  lambda_cat_sigma = cpl_table_get_double(linetable, "dLambda_candidate", row, NULL);
1023 
1024 
1025  /* Sigma less than 1 pixel is usually not
1026  justified by the data (which obviously
1027  has a resolution of only 1 pixel). Such
1028  an underenstimation of the uncertainty
1029  leads to wrong identifications.
1030  Therefore use a width of at least 1 pixel */
1031  line_width =
1032  uves_max_double(1, cpl_table_get_double(linetable, "Xwidth" , row, NULL)) *
1033  fabs(cpl_table_get_double(linetable, LINETAB_PIXELSIZE , row, NULL));
1034  /* convert to wlu */
1035 
1036  line_fwhm = TWOSQRT2LN2 * line_width;
1037 
1038  /* As the uncertainty of the computed wavelength is used
1039  * line_fwhm (in w.l.u.)
1040  * To take into account the fact that lines near the edge of
1041  * the chip have larger error of the computed wavelength,
1042  * this is also scaled according to the accuracy of the dispersion
1043  * relation, i.e. multiplied by dl/<dl>,
1044  * where <dl> is an average, say the median, of uncertainties of
1045  * all predicted wavelengths.
1046  */
1047 
1048  dlambda_com = line_fwhm
1049  * cpl_table_get_double(linetable, "dLambdaC" , row, NULL)
1050  / average_dlambda_com;
1051 
1052  tolerance_sq = line_fwhm*line_fwhm * error*error;
1053 
1054  distance_cat_sq = cpl_table_get_double(linetable, "dLambda_cat_sq", row, NULL);
1055  nn_distance_sq = cpl_table_get_double(linetable, "dLambda_nn_sq" , row, NULL);
1056 
1057 #if WANT_BIG_LOGFILE
1058  uves_msg_debug("(order,x) = (%d,%f) lcom = %f+-%f lcat = %f "
1059  "dist_cat = %f (%f pixels) tolerance = %.3f error = %f "
1060  "nn = %f (%f pixels)",
1061  cpl_table_get_int (linetable, "Order" , row, NULL),
1062  cpl_table_get_double(linetable, "X" , row, NULL),
1063  cpl_table_get_double(linetable, LINETAB_LAMBDAC, row, NULL),
1064  dlambda_com,
1065  lambda_cat,
1066  sqrt(distance_cat_sq),
1067  sqrt(distance_cat_sq)
1068  /cpl_table_get_double(linetable, LINETAB_PIXELSIZE, row, NULL),
1069  sqrt(tolerance_sq),
1070  error,
1071  sqrt(nn_distance_sq),
1072  sqrt(nn_distance_sq)
1073  /cpl_table_get_double(linetable, LINETAB_PIXELSIZE, row, NULL));
1074 #endif
1075 
1076  /* Make the ID? */
1077  if (distance_cat_sq < (dlambda_com)*(dlambda_com)
1078  && tolerance_sq < nn_distance_sq
1079  && distance_cat_sq < nn_distance_sq)
1080  {
1081  number_identifications++;
1082  cpl_table_set_double(linetable, "Ident", row, lambda_cat);
1083  cpl_table_set_double(linetable, "dIdent",row, lambda_cat_sigma);
1084 #if WANT_BIG_LOGFILE
1085  uves_msg_debug("ID made");
1086 #endif
1087  }
1088  else
1089  {
1090  if (cpl_table_is_valid(linetable, "Ident", row)) {
1091  number_identifications++;
1092  /* Also count lines that were already identified */
1093  uves_msg_debug("Line at (%d,%f) does not match ID criterion anymore",
1094  cpl_table_get_int (linetable, "Order", row, NULL),
1095  cpl_table_get_double(linetable, "X", row, NULL)
1096  );
1097  }
1098  }
1099  }
1100 
1101  cleanup:
1102  cpl_free(histogram);
1103  return number_identifications;
1104 }
1105 
1106 /*----------------------------------------------------------------------------*/
1132 /*----------------------------------------------------------------------------*/
1133 static polynomial *
1134 calibrate_global(const cpl_table *linetable,
1135  cpl_table **selected,
1136  int degree, bool verbose,
1137  bool reject,
1138  double TOLERANCE,
1139  double kappa,
1140  double *red_chisq, polynomial **dispersion_variance,
1141  double *pixelsize,
1142  double *rms_wlu,
1143  double *rms_pixels)
1144 {
1145  polynomial *dispersion_relation = NULL; /* Result */
1146  cpl_table *identified = NULL;
1147  int valid_ids =
1148  cpl_table_get_nrow(linetable) -
1149  cpl_table_count_invalid(linetable, "Ident");
1150  int rejected;
1151 
1152  passure( (pixelsize == NULL) == (rms_wlu == NULL) &&
1153  (pixelsize == NULL) == (rms_pixels == NULL), " ");
1154 
1155  assure( degree < 0 ||
1156  valid_ids >= (degree + 1)*(degree + 1), CPL_ERROR_ILLEGAL_INPUT,
1157  "There are not enough identifications to create a %d.-degree global fit. "
1158  "%d needed. %d found", degree, (degree + 1)*(degree + 1), valid_ids);
1159 
1160  identified = cpl_table_duplicate(linetable);
1161  assure_mem(identified);
1162 
1163  /* Delete rows with invalid 'Ident' and large residuals */
1164  if (reject)
1165  {
1166  check_nomsg( rejected = uves_delete_bad_lines(identified, TOLERANCE, kappa) );
1167  uves_msg_debug("%d lines rejected %f %f", rejected, TOLERANCE, kappa);
1168  }
1169  else
1170  {
1171  check( uves_erase_invalid_table_rows(identified, "Ident"),
1172  "Error erasing un-identified lines");
1173  }
1174 
1175 
1176  /* Create column 'Aux' = 'Order' * 'Ident' */
1177  check(( cpl_table_duplicate_column(identified, "Aux", identified, "Ident"),
1178  cpl_table_multiply_columns(identified, "Aux", "Order"),
1179 
1180  /* Create column 'dAux' = 'Order' * 'dIdent' */
1181  cpl_table_duplicate_column(identified, "dAux", identified, "dIdent"),
1182  cpl_table_multiply_columns(identified, "dAux", "Order")),
1183  "Error setting up temporary table");
1184 
1185  /* Fit */
1186 
1187  if (degree >= 0) {
1188  check( dispersion_relation =
1189  uves_polynomial_regression_2d(identified,
1190  "X", "Order", "Aux",
1191  "dAux", /* Use "dAux" for weighting,
1192  to be able to compute an uncertainty
1193  of WAVEC.
1194 
1195  It would probably make more sense
1196  to use the uncertainty of 'dX' for
1197  weighting. */
1198  degree, degree,
1199  NULL, NULL, NULL, /* Don't add extra columns */
1200  NULL, /* mse */
1201  red_chisq,
1202  dispersion_variance,
1203  reject ? kappa : -1, -1),
1204  "Error fitting polynomial. Possible cause: too few (%d) "
1205  "line identifications", valid_ids);
1206  }
1207  else {
1208  int max_degree = 8;
1209  double min_rms = -1; /* disabled */
1210  double min_reject = -1; /* disabled */
1211  check( dispersion_relation =
1213  "X", "Order", "Aux",
1214  "dAux",
1215  NULL, NULL, NULL,
1216  NULL,
1217  red_chisq,
1218  dispersion_variance,
1219  reject ? kappa : -1,
1220  max_degree, max_degree,
1221  min_rms, min_reject,
1222  verbose,
1223  NULL, NULL, 0, NULL),
1224  "Error fitting polynomial. Possible cause: too few (%d) "
1225  "line identifications", valid_ids);
1226  }
1227 
1228  if (pixelsize != NULL)
1229  {
1230  /* Compute parameters if requested */
1231 
1232  check( compute_lambda(identified, dispersion_relation, NULL,
1233  false),
1234  "Error applying dispersion relation");
1235 
1236  *pixelsize = cpl_table_get_column_median(identified, LINETAB_PIXELSIZE);
1237  *rms_wlu = cpl_table_get_column_stdev (identified, LINETAB_RESIDUAL);
1238  *rms_pixels= cpl_table_get_column_stdev (identified, "Residual_pix");
1239  }
1240 
1241  if (selected != NULL) {
1242  *selected = cpl_table_duplicate(identified);
1243  }
1244 
1245  cleanup:
1246  uves_free_table(&identified);
1247  if (cpl_error_get_code() != CPL_ERROR_NONE)
1248  {
1249  uves_polynomial_delete(&dispersion_relation);
1250  }
1251 
1252  return dispersion_relation;
1253 }
1254 
1255 
1256 
1257 /*----------------------------------------------------------------------------*/
1264 /*----------------------------------------------------------------------------*/
1265 
1266 int
1267 uves_wavecal_identify_lines_ppm(cpl_table *linetable, const cpl_table *line_refer)
1268 {
1269  int result = 0;
1270  int minorder, maxorder;
1271  int order;
1272  cpl_table *lt_order = NULL;
1273  cpl_table *refer_order = NULL;
1274  cpl_vector *peaks = NULL;
1275  cpl_vector *lines = NULL;
1276  cpl_bivector *ids = NULL;
1277 
1278  assure( cpl_table_has_column(linetable, LINETAB_LAMBDAC), CPL_ERROR_DATA_NOT_FOUND,
1279  "Missing column %s", LINETAB_LAMBDAC);
1280 
1281  assure( cpl_table_has_column(linetable, LINETAB_PIXELSIZE), CPL_ERROR_DATA_NOT_FOUND,
1282  "Missing column %s", LINETAB_PIXELSIZE);
1283 
1284  assure( cpl_table_has_column(linetable, "Order"), CPL_ERROR_DATA_NOT_FOUND,
1285  "Missing column %s", "Order");
1286 
1287  minorder = uves_round_double( cpl_table_get_column_min(linetable, "Order"));
1288  maxorder = uves_round_double( cpl_table_get_column_max(linetable, "Order"));
1289 
1290  /* Reset identifications */
1291  if (cpl_table_has_column(linetable, "Ident_ppm"))
1292  {
1293  cpl_table_erase_column(linetable, "Ident_ppm");
1294  }
1295 
1296  cpl_table_new_column(linetable, "Ident_ppm", CPL_TYPE_DOUBLE);
1297 
1298  for (order = minorder; order <= maxorder; order++)
1299  {
1300  const double tolerance = 0.05; /* relative tolerance on interval ratios */
1301  double min_lambda, max_lambda;
1302  double min_disp, max_disp;
1303 
1304  /* Extract current order */
1305 
1306  uves_free_table(&lt_order);
1307  lt_order = uves_extract_table_rows(linetable, "Order",
1308  CPL_EQUAL_TO, order); /* Uses integer comparison */
1309 
1310  check_nomsg((min_lambda = cpl_table_get_column_min(lt_order, LINETAB_LAMBDAC),
1311  max_lambda = cpl_table_get_column_max(lt_order, LINETAB_LAMBDAC),
1312  min_disp = cpl_table_get_column_min(lt_order, LINETAB_PIXELSIZE)*0.99,
1313  max_disp = cpl_table_get_column_max(lt_order, LINETAB_PIXELSIZE)*1.01));
1314 
1315  uves_free_table(&refer_order);
1316  refer_order = uves_extract_table_rows(line_refer, "Wave", CPL_GREATER_THAN,
1317  min_lambda);
1318  uves_extract_table_rows_local(refer_order, "Wave", CPL_LESS_THAN,
1319  max_lambda);
1320 
1321  /* Convert to vectors */
1322  {
1323  int i;
1324  uves_free_vector(&peaks);
1325  peaks = cpl_vector_new(cpl_table_get_nrow(lt_order));
1326  for (i = 0; i < cpl_vector_get_size(peaks); i++)
1327  {
1328  cpl_vector_set(peaks, i, cpl_table_get_double(lt_order, "X", i, NULL));
1329  }
1330 
1331  uves_free_vector(&lines);
1332  lines = cpl_vector_new(cpl_table_get_nrow(refer_order));
1333  for (i = 0; i < cpl_vector_get_size(lines); i++)
1334  {
1335  cpl_vector_set(lines, i, cpl_table_get_double(refer_order, "Wave", i, NULL));
1336  }
1337  }
1338 
1339  /* Not sure if this is necessary for the PPM algorithm */
1340  cpl_vector_sort(peaks, 1);
1341  cpl_vector_sort(lines, 1);
1342 
1343  uves_msg_debug("Call ppm with %" CPL_SIZE_FORMAT " peaks, %" CPL_SIZE_FORMAT " lines, dispersion range = %f - %f A/pixel",
1344  cpl_vector_get_size(peaks),
1345  cpl_vector_get_size(lines),
1346  min_disp, max_disp);
1347 
1348  uves_free_bivector(&ids);
1349 
1350  ids = cpl_ppm_match_positions(peaks, lines,
1351  min_disp, max_disp,
1352  tolerance,
1353  NULL, NULL);
1354 
1355 
1356  if (ids == NULL)
1357  {
1358  uves_msg_warning("Order %d: Point pattern matching failed", order);
1359  if (cpl_error_get_code() != CPL_ERROR_NONE)
1360  {
1361  uves_msg_debug("%s at %s", cpl_error_get_message(),
1362  cpl_error_get_where());
1363  uves_error_reset();
1364  }
1365  }
1366  else
1367  {
1368  int i, j;
1369 
1370  uves_msg_debug("%" CPL_SIZE_FORMAT " identifications from point pattern matching (order %d)",
1371  cpl_bivector_get_size(ids), order);
1372 
1373  result += cpl_bivector_get_size(ids);
1374 
1375  for (i = 0; i < cpl_table_get_nrow(linetable); i++) {
1376 
1377  if (cpl_table_get_int(linetable, "Order", i, NULL) == order)
1378  for (j = 0; j < cpl_bivector_get_size(ids); j++)
1379  {
1380  if (fabs(cpl_table_get_double(linetable, "X", i, NULL) -
1381  cpl_bivector_get_x_data(ids)[j]) < 0.001)
1382  cpl_table_set_double(linetable, "Ident_ppm", i,
1383  cpl_bivector_get_y_data(ids)[j]);
1384  }
1385  }
1386  }
1387  }
1388 
1389  cleanup:
1390  uves_free_table(&lt_order);
1391  uves_free_table(&refer_order);
1392  uves_free_vector(&peaks);
1393  uves_free_vector(&lines);
1394  uves_free_bivector(&ids);
1395 
1396  return result;
1397 }
int uves_polynomial_get_dimension(const polynomial *p)
Get the dimension of a polynomial.
void uves_polynomial_delete(polynomial **p)
Delete a polynomial.
#define uves_msg_warning(...)
Print an warning message.
Definition: uves_msg.h:87
int uves_wavecal_identify_lines_ppm(cpl_table *linetable, const cpl_table *line_refer)
Identify lines using point pattern matching.
#define check_nomsg(CMD)
Definition: uves_error.h:204
#define passure(BOOL,...)
Definition: uves_error.h:207
int uves_qclog_add_double(cpl_table *table, const char *key_name, const double value, const char *key_help, const char *format)
Add double key to QC-LOG table.
Definition: uves_qclog.c:641
static polynomial * calibrate_global(const cpl_table *linetable, cpl_table **selected, int degree, bool verbose, bool reject, double TOLERANCE, double kappa, double *red_chisq, polynomial **dispersion_variance, double *pixelsize, double *rms_wlu, double *rms_pixels)
Create a fit of all orders.
int uves_qclog_add_int(cpl_table *table, const char *key_name, const int value, const char *key_help, const char *format)
Add integer key to QC-LOG table.
Definition: uves_qclog.c:521
double uves_polynomial_derivative_2d(const polynomial *p, double x1, double x2, int varno)
Evaluate the partial derivative of a 2d polynomial.
#define uves_msg(...)
Print a message on 'info' or 'debug' level.
Definition: uves_msg.h:119
polynomial * uves_polynomial_regression_2d(cpl_table *t, const char *X1, const char *X2, const char *Y, const char *sigmaY, int degree1, int degree2, const char *polynomial_fit, const char *residual_square, const char *variance_fit, double *mse, double *red_chisq, polynomial **variance, double kappa, double min_reject)
Fit a 2d polynomial to three table columns.
Definition: uves_utils.c:2869
polynomial * uves_wavecal_identify(cpl_table *linetable, const cpl_table *line_refer, const polynomial *guess_dispersion, int DEGREE, double TOLERANCE, double ALPHA, double MAXERROR, double kappa, const int trace, const int window, cpl_table *qclog)
Obtain final dispersion relation.
polynomial * uves_polynomial_regression_2d_autodegree(cpl_table *t, const char *X1, const char *X2, const char *Y, const char *sigmaY, const char *polynomial_fit, const char *residual_square, const char *variance_fit, double *mean_squared_error, double *red_chisq, polynomial **variance, double kappa, int maxdeg1, int maxdeg2, double min_rms, double min_reject, bool verbose, const double *min_val, const double *max_val, int npos, double positions[][2])
Fit a 2d polynomial to three table columns.
Definition: uves_utils.c:3305
int uves_delete_bad_lines(cpl_table *table, double TOLERANCE, double kappa)
Delete bad lines from line table.
#define assure_mem(PTR)
Definition: uves_error.h:181
double uves_polynomial_evaluate_2d(const polynomial *p, double x1, double x2)
Evaluate a 2d polynomial.
static cpl_error_code verify_calibration(const cpl_table *selected, const cpl_table *linetable, double TOLERANCE, double red_chisq, cpl_table *qclog)
Report quality of calibration.
#define uves_error_reset()
Definition: uves_error.h:215
#define uves_msg_debug(...)
Print a debug message.
Definition: uves_msg.h:97
static int identify_lines(cpl_table *linetable, const cpl_table *line_refer, double ALPHA)
Identify lines by comparing to catalogue wavelengths.
static cpl_error_code compute_lambda(cpl_table *linetable, const polynomial *dispersion_relation, const polynomial *dispersion_variance, bool verbose)
Apply dispersion relation to line table.
int uves_wavecal_find_nearest(const cpl_table *line_refer, double lambda, int lo, int hi)
Find best matching catalogue wavelength.
#define check(CMD,...)
Definition: uves_error.h:198