uves_wavecal_identify.c

00001 /*                                                                              *
00002  *   This file is part of the ESO UVES Pipeline                                 *
00003  *   Copyright (C) 2004,2005 European Southern Observatory                      *
00004  *                                                                              *
00005  *   This library is free software; you can redistribute it and/or modify       *
00006  *   it under the terms of the GNU General Public License as published by       *
00007  *   the Free Software Foundation; either version 2 of the License, or          *
00008  *   (at your option) any later version.                                        *
00009  *                                                                              *
00010  *   This program is distributed in the hope that it will be useful,            *
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of             *
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
00013  *   GNU General Public License for more details.                               *
00014  *                                                                              *
00015  *   You should have received a copy of the GNU General Public License          *
00016  *   along with this program; if not, write to the Free Software                *
00017  *   Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA       *
00018  *                                                                              */
00019 
00020 /*
00021  * $Author: amodigli $
00022  * $Date: 2012/03/02 16:40:40 $
00023  * $Revision: 1.37 $
00024  * $Name: uves-5_0_0 $
00025  * $Log: uves_wavecal_identify.c,v $
00026  * Revision 1.37  2012/03/02 16:40:40  amodigli
00027  * fixed warning related to upgrade to CPL6
00028  *
00029  * Revision 1.36  2011/12/08 14:00:02  amodigli
00030  * Fox warnings with CPL6
00031  *
00032  * Revision 1.35  2011/04/14 11:25:40  amodigli
00033  * fixed typo QC key  in comments
00034  *
00035  * Revision 1.34  2011/04/11 09:07:41  amodigli
00036  * implemented QC comments corrections from DFO
00037  *
00038  * Revision 1.33  2011/04/11 07:53:12  amodigli
00039  * uniformed QC param key name
00040  *
00041  * Revision 1.32  2011/03/23 12:27:31  amodigli
00042  * changed QC key as user likes
00043  *
00044  * Revision 1.31  2011/03/23 10:08:47  amodigli
00045  * added QC to better characterize wave accuracy
00046  *
00047  * Revision 1.30  2010/09/24 09:32:09  amodigli
00048  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
00049  *
00050  * Revision 1.28  2007/07/23 14:57:30  jmlarsen
00051  * Make workaround work
00052  *
00053  * Revision 1.27  2007/07/23 12:40:37  jmlarsen
00054  * Update to CPL4
00055  *
00056  * Revision 1.26  2007/06/06 08:17:33  amodigli
00057  * replace tab with 4 spaces
00058  *
00059  * Revision 1.25  2007/05/22 11:46:15  jmlarsen
00060  * Removed 1d wavecal mode which was not supported
00061  *
00062  * Revision 1.24  2007/05/16 16:33:42  amodigli
00063  * fixed leak
00064  *
00065  * Revision 1.23  2007/05/10 08:32:48  jmlarsen
00066  * Minor output message change
00067  *
00068  * Revision 1.22  2007/05/07 14:26:44  jmlarsen
00069  * Added QC.NLINSOL parameter
00070  *
00071  * Revision 1.21  2007/05/07 07:13:59  jmlarsen
00072  * Made resolution computation robust against negative dl/dx
00073  *
00074  * Revision 1.20  2007/04/27 07:22:57  jmlarsen
00075  * Implemented possibility to use automatic polynomial degree
00076  *
00077  * Revision 1.19  2007/04/13 07:34:54  jmlarsen
00078  * Removed dead code
00079  *
00080  * Revision 1.18  2007/04/10 07:12:09  jmlarsen
00081  * Changed interface of polynomial_regression_2d()
00082  *
00083  * Revision 1.17  2007/03/15 12:36:44  jmlarsen
00084  * Added experimental ppm code
00085  *
00086  * Revision 1.16  2007/03/05 10:24:14  jmlarsen
00087  * Do kappa-sigma rejection only in second loop
00088  *
00089  * Revision 1.15  2007/02/22 15:37:35  jmlarsen
00090  * Use kappa-sigma clipping when fitting dispersion
00091  *
00092  * Revision 1.14  2007/01/15 08:58:51  jmlarsen
00093  * Added text output
00094  *
00095  * Revision 1.13  2006/11/06 15:19:42  jmlarsen
00096  * Removed unused include directives
00097  *
00098  * Revision 1.12  2006/10/12 11:36:48  jmlarsen
00099  * Reduced max line length
00100  *
00101  * Revision 1.11  2006/10/10 11:20:11  jmlarsen
00102  * Renamed line table columns to match MIDAS
00103  *
00104  * Revision 1.10  2006/08/17 14:11:25  jmlarsen
00105  * Use assure_mem macro to check for memory allocation failure
00106  *
00107  * Revision 1.9  2006/08/17 13:56:53  jmlarsen
00108  * Reduced max line length
00109  *
00110  * Revision 1.8  2006/08/11 14:36:37  jmlarsen
00111  * Added profiling info
00112  *
00113  * Revision 1.7  2006/08/07 11:35:08  jmlarsen
00114  * Removed hardcoded constant
00115  *
00116  * Revision 1.6  2006/07/14 12:52:57  jmlarsen
00117  * Exported/renamed function find_nearest
00118  *
00119  * Revision 1.5  2006/07/14 12:44:26  jmlarsen
00120  * Use less significant digits
00121  *
00122  * Revision 1.4  2006/04/24 09:33:48  jmlarsen
00123  * Shortened max line length
00124  *
00125  * Revision 1.3  2006/03/03 13:54:11  jmlarsen
00126  * Changed syntax of check macro
00127  *
00128  * Revision 1.2  2006/02/15 13:19:15  jmlarsen
00129  * Reduced source code max. line length
00130  *
00131  * Revision 1.1  2006/02/03 07:46:30  jmlarsen
00132  * Moved recipe implementations to ./uves directory
00133  *
00134  * Revision 1.31  2005/12/20 08:11:44  jmlarsen
00135  * Added CVS  entry
00136  *
00137  */
00138 
00139 /*----------------------------------------------------------------------------*/
00143 /*----------------------------------------------------------------------------*/
00146 #ifdef HAVE_CONFIG_H
00147 #  include <config.h>
00148 #endif
00149 
00150 #include <uves_wavecal_identify.h>
00151 
00152 #include <uves_wavecal_utils.h>
00153 #include <uves_utils.h>
00154 #include <uves_utils_wrappers.h>
00155 #include <uves_error.h>
00156 #include <uves_msg.h>
00157 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 0, 0)
00158 #include <cpl_ppm.h> /* missing from cpl.h */
00159 #else
00160 #include <irplib_ppm.h>
00161 #endif
00162 #include <uves_qclog.h>
00163 #include <cpl.h>
00164 
00165 #include <math.h>
00166 #include <float.h>
00167 
00168 #define USE_PPM 0
00169 
00170 static cpl_error_code verify_calibration(const cpl_table *selected,
00171                                          const cpl_table *linetable, 
00172                      double TOLERANCE,
00173                                          double red_chisq,cpl_table* qclog);
00174 static cpl_error_code compute_lambda(cpl_table *linetable, 
00175                      const polynomial *dispersion_relation, 
00176                      const polynomial *dispersion_variance,
00177                                      bool verbose);
00178 
00179 static int identify_lines(cpl_table *linetable, 
00180                           const cpl_table *line_refer, 
00181                           double ALPHA);
00182 
00183 static polynomial *calibrate_global(const cpl_table *linetable,
00184                                     cpl_table **selected,
00185                     int degree, bool verbose,
00186                                     bool reject,
00187                     double TOLERANCE, 
00188                                     double kappa,
00189                     double *red_chisq, 
00190                     polynomial **dispersion_variance,
00191                     double *pixelsize,
00192                     double *rms_wlu,
00193                     double *rms_pixels);
00194 
00195 /*----------------------------------------------------------------------------*/
00235 /*----------------------------------------------------------------------------*/
00236 
00237 polynomial *
00238 uves_wavecal_identify(cpl_table *linetable, 
00239               const cpl_table *line_refer, 
00240               const polynomial *guess_dispersion, 
00241               int DEGREE, double TOLERANCE, 
00242               double ALPHA, double MAXERROR,
00243                       double kappa,
00244                       const int trace,const int window,cpl_table* qclog)
00245 {
00246     polynomial *dispersion_relation = NULL; /* Result */
00247     polynomial *dispersion_variance = NULL; /* Variance of result, 
00248                            written to line table */
00249     int current_id; /* Current and previous number of line identifications */
00250     int previous_id;
00251     int idloop;             /* Number of iterations of grand loop */
00252     int n;                  /* Number of iterations in ID loop */
00253     double pixelsize;       /* Average conversion factor between pixels and wlu */
00254     double red_chisq;       /* Reduced chi^2 of fit         */
00255     cpl_table *selected = NULL;  /* Lines used in final fit */
00256     char qc_key[40];
00257 
00258     passure( linetable        != NULL, " ");
00259     passure( line_refer       != NULL, " ");
00260     passure( guess_dispersion != NULL, " ");
00261 
00262     assure( 0 < ALPHA && ALPHA <= 1, CPL_ERROR_ILLEGAL_INPUT, 
00263         "Illegal alpha = %e", ALPHA);
00264 
00265     /* Calculate LambdaC from the initial dispersion relation */
00266     {
00267     cpl_table_new_column(linetable, LINETAB_LAMBDAC    , CPL_TYPE_DOUBLE);
00268     cpl_table_new_column(linetable, "dLambdaC"         , CPL_TYPE_DOUBLE);
00269     cpl_table_new_column(linetable, LINETAB_PIXELSIZE  , CPL_TYPE_DOUBLE);
00270     cpl_table_new_column(linetable, LINETAB_RESIDUAL   , CPL_TYPE_DOUBLE);
00271     cpl_table_new_column(linetable, "Residual_pix"     , CPL_TYPE_DOUBLE);
00272     cpl_table_new_column(linetable, "Lambda_candidate" , CPL_TYPE_DOUBLE);
00273     cpl_table_new_column(linetable, "dLambda_candidate", CPL_TYPE_DOUBLE);
00274     cpl_table_new_column(linetable, "dLambda_cat_sq"   , CPL_TYPE_DOUBLE);
00275     cpl_table_new_column(linetable, "dLambda_nn_sq"    , CPL_TYPE_DOUBLE);
00276 
00277     /* Create columns 'Ident' and 'dIdent' (uncertainty) and fill with
00278        invalid (no identification made) */
00279     cpl_table_new_column(linetable, "Ident", CPL_TYPE_DOUBLE);
00280     cpl_table_new_column(linetable, "dIdent",CPL_TYPE_DOUBLE);
00281     cpl_table_set_column_invalid(linetable, "Ident", 0, cpl_table_get_nrow(linetable));
00282     cpl_table_set_column_invalid(linetable, "dIdent",0, cpl_table_get_nrow(linetable));
00283     
00284     /* Residuals are not calculated because 'Ident' is invalid */
00285     check( compute_lambda(linetable, guess_dispersion, NULL, false), 
00286            "Error applying dispersion relation");
00287     }
00288 
00289 
00290 #if USE_PPM
00291     for (idloop = 2; idloop <= 2; idloop += 1)
00292 #else
00293     for (idloop = 1; idloop <= 2; idloop += 1)
00294 #endif
00295     {
00296 
00297         current_id = 0;
00298         n = 0;
00299         /* Iterate until no more identifications can be made */
00300         do {
00301         double rms_wlu;
00302         double rms_pixels;
00303                 bool reject = (idloop == 2);
00304 #if USE_PPM
00305                 int nident_ppm;
00306 #endif
00307         
00308         previous_id = current_id;
00309         n++;
00310         
00311         /* Identify lines */
00312         check( current_id = identify_lines(linetable, line_refer, ALPHA), 
00313                "Error identifying lines");
00314 
00315 
00316 #if USE_PPM
00317                 /* Try PPM */
00318                 check( nident_ppm = uves_wavecal_identify_lines_ppm(linetable, line_refer),
00319                        "Error during point pattern matching");
00320 
00321                 cpl_table_erase_column(linetable, "Ident");
00322                 cpl_table_duplicate_column(linetable, "Ident", linetable, "Ident_ppm");
00323                 current_id = nident_ppm;
00324 
00325                 /* FIXME: This only works if 'dIdent' is constant.
00326                    We should propagate error bars during ppm matching */
00327                 cpl_table_fill_column_window(linetable, "dIdent",
00328                                              0, cpl_table_get_nrow(linetable),
00329                                              cpl_table_get_column_mean(linetable, "dIdent"));
00330 #endif
00331 
00332         /* Calibrate with 
00333          * 1st loop: tolerance=infinity (i.e. all identified lines are considered good). 
00334          * 2nd loop: use specified tolerance (ignore outliers)
00335          */
00336         uves_polynomial_delete(&dispersion_relation);
00337         uves_polynomial_delete(&dispersion_variance);
00338 
00339         check( dispersion_relation = calibrate_global(
00340                linetable, NULL,
00341                            DEGREE, false,
00342                            reject,
00343                TOLERANCE,
00344                kappa,
00345                &red_chisq,
00346                &dispersion_variance,
00347                &pixelsize,
00348                &rms_wlu,
00349                &rms_pixels),
00350                "Could not perform global calibration");
00351 
00352         uves_msg_debug("Average pixelsize = %f wlu", pixelsize);
00353                 if (idloop == 1)
00354                     {
00355                         uves_msg("%d identifications made. RMS = %.5f wlu = %.3f "
00356                                  "pixels (no rejection)", 
00357                                  current_id, rms_wlu, rms_pixels);
00358 
00359 
00360 
00361 
00362                     }
00363                 else
00364                     {
00365                         uves_msg("%d identifications made. RMS = %.5f wlu = %.3f "
00366                                  "pixels (%f %s rejection, kappa = %.1f)", 
00367                                  current_id, rms_wlu, rms_pixels,
00368                                  fabs(TOLERANCE), (TOLERANCE > 0) ? "pixels" : "wlu",
00369                                  kappa);
00370                     }
00371 
00372                 sprintf(qc_key,"QC TRACE%d WIN%d NLINID%d",trace,window,idloop);
00373                 ck0_nomsg(uves_qclog_add_int(qclog,qc_key,current_id,
00374                                              "ThAr lamp identified lines",
00375                                              "%d"));
00376 
00377 #if USE_PPM
00378                 uves_msg("%d identifications from point pattern matching",
00379                          nident_ppm);
00380 #endif
00381         
00382         assure( rms_pixels < MAXERROR, CPL_ERROR_CONTINUE,
00383             "Wavelength calibration did not converge. "
00384             "After %d iterations the RMS was %f pixels. "
00385             "Try to improve on the initial solution", n, rms_pixels);
00386         
00387 
00388         /* Apply calibration result */
00389         check( compute_lambda(linetable, dispersion_relation, dispersion_variance,
00390                                       false),
00391                "Error applying dispersion relation");
00392 
00393 
00394         }
00395         while (current_id > previous_id) ;
00396 
00397         sprintf(qc_key,"QC TRACE%d WIN%d NLINID NITERS",trace,window);
00398         ck0_nomsg(uves_qclog_add_int(qclog,qc_key,idloop+1,
00399                                      "Number of iterations",
00400                                      "%d"));
00401 
00402 
00403 
00404         if (idloop == 1)
00405         {
00406             /* 
00407              * Remove all identifications and repeat
00408              */
00409             
00410             uves_msg("Identification loop converged. Resetting identifications");
00411             cpl_table_set_column_invalid(linetable, "Ident", 0, 
00412                          cpl_table_get_nrow(linetable));
00413         }
00414     }
00415 
00416     /* Calibrate again with a global polynomial, but this time don't
00417        use lines with residuals worse than TOLERANCE */
00418     uves_polynomial_delete(&dispersion_relation);
00419     uves_polynomial_delete(&dispersion_variance);
00420     uves_free_table(&selected);
00421     
00422     check( dispersion_relation = calibrate_global(linetable,
00423                                                   &selected,
00424                                                   DEGREE, true,
00425                                                   true,  /* do rejection? */
00426                                                   TOLERANCE,
00427                                                   kappa,
00428                                                   &red_chisq,
00429                                                   &dispersion_variance,
00430                                                   NULL, NULL, NULL),
00431            "Could not perform global calibration");
00432     
00433     /* Update the computed wavelengths */
00434     check( compute_lambda(linetable, dispersion_relation, dispersion_variance,
00435                           true), 
00436            "Error applying dispersion relation");
00437     
00438     /* Add columns 'Select' and 'NLinSol' to linetable.
00439        The columns defines which lines were identified,
00440        and which lines were used in the final fit */
00441     {
00442         int i, j;
00443 
00444         /* Tables are sorted by Order, X */
00445 
00446         cpl_table_new_column(linetable, "NLinSol", CPL_TYPE_INT);
00447         cpl_table_new_column(linetable, "Select", CPL_TYPE_INT);
00448 
00449         cpl_table_fill_column_window_int(linetable, "NLinSol", 
00450                                          0, cpl_table_get_nrow(linetable),
00451                                          0);
00452         cpl_table_fill_column_window_int(linetable, "Select", 
00453                                          0, cpl_table_get_nrow(linetable),
00454                                          0);
00455 
00456         j = 0;
00457         for (i = 0; i < cpl_table_get_nrow(selected); i++) {
00458             int order = cpl_table_get_int(selected, "Order", i, NULL);
00459             double  x = cpl_table_get_double(selected, "X", i, NULL);
00460             int order2;
00461             double x2;
00462 
00463             /* Find this line in the original linetable */
00464             passure( j < cpl_table_get_nrow(linetable), "%d %" CPL_SIZE_FORMAT "",
00465                      j, cpl_table_get_nrow(linetable));
00466             do {
00467                 order2 = cpl_table_get_int(linetable, "Order", j, NULL);
00468                 x2     = cpl_table_get_double(linetable, "X", j, NULL);
00469                 if (cpl_table_is_valid(linetable, "Ident", j))
00470                     {
00471                         cpl_table_set_int(linetable, "Select", j, 1);
00472                     }
00473                 j++;
00474 
00475             } while (order2 < order || x2 < x - 0.1);
00476             
00477             passure( order2 == order && fabs(x2 - x) < 0.1,
00478                      "%d %d %g %g", order2, order, x2, x);
00479             
00480             cpl_table_set_int(linetable, "NLinSol", j-1, 1);
00481         }
00482     }
00483 
00484     /* Display results */
00485     check( verify_calibration(selected, linetable, TOLERANCE, red_chisq,qclog), 
00486        "Error verifying calibration");
00487     
00488   cleanup:
00489     uves_free_table(&selected);
00490     uves_polynomial_delete(&dispersion_variance);
00491     return dispersion_relation;
00492 }
00493 
00494 /*----------------------------------------------------------------------------*/
00508 /*----------------------------------------------------------------------------*/
00509 static cpl_error_code
00510 verify_calibration(const cpl_table *selected,
00511                    const cpl_table *linetable, double TOLERANCE,
00512                    double red_chisq, cpl_table* qclog)
00513 {
00514     cpl_table *brightest  = NULL;
00515     double median_intensity;
00516     int ninvalid;    /* Number of unidentified lines among the brightest half */
00517     double ratio;
00518     double rms_wlu;
00519     double rms_pixels;
00520     double rms_speed;
00521     char qc_key[40];
00522     
00523     {
00524     double mean;
00525     double stdev;
00526     
00527     check(( mean = cpl_table_get_column_mean (selected, LINETAB_RESIDUAL),
00528         stdev= cpl_table_get_column_stdev(selected, LINETAB_RESIDUAL),
00529         rms_wlu = sqrt(mean*mean + stdev*stdev),
00530         
00531         mean = cpl_table_get_column_mean (selected, "Residual_pix"),
00532         stdev= cpl_table_get_column_stdev(selected, "Residual_pix"),
00533         rms_pixels = sqrt(mean*mean + stdev*stdev)),
00534           "Error reading RMS of fit");
00535     }
00536     rms_speed=rms_wlu * SPEED_OF_LIGHT/
00537        cpl_table_get_column_mean(selected,LINETAB_LAMBDAC);
00538     uves_msg("%" CPL_SIZE_FORMAT " lines accepted", cpl_table_get_nrow(selected));
00539     uves_msg("Average RMS of calibration (tolerance = %.3f %s) = %.5f wlu = %.4f pixels ~ %.1f m/s",
00540          fabs(TOLERANCE),
00541          (TOLERANCE > 0) ? "pixels" : "wlu",
00542          rms_wlu, rms_pixels, rms_speed);
00543     
00544    sprintf(qc_key,"QC LINE RESIDRMS WLU");
00545    ck0_nomsg(uves_qclog_add_double(qclog,qc_key,rms_wlu,
00546                                    "Line ID RMS TRACE0 WIN2 [Ang]",
00547                                    "%f"));
00548    sprintf(qc_key,"QC LINE RESIDRMS PIX");
00549    ck0_nomsg(uves_qclog_add_double(qclog,qc_key,rms_pixels,
00550                                    "Line ID RMS TRACE0 WIN2 [pix]",
00551                                    "%f"));
00552    sprintf(qc_key,"QC LINE RESIDRMS SPEED");
00553    ck0_nomsg(uves_qclog_add_double(qclog,qc_key,rms_speed,
00554                                    "Line ID RMS TRACE0 WIN2 [m/s]",
00555                                    "%f"));
00556 
00557 
00558     uves_msg("Reduced chi^2 of calibration = %f", red_chisq);
00559     sprintf(qc_key,"QC LINE IDCHI2");
00560     ck0_nomsg(uves_qclog_add_double(qclog,qc_key,red_chisq,
00561                                    "Reduced chi^2 of line ID TRACE0 WIN2",
00562                                    "%f"));
00563 
00564     if (red_chisq < .01)
00565     {
00566         uves_msg_warning("Reduced chi^2 of fit is less than 1/100: %f", 
00567                  red_chisq);
00568     }
00569     if (red_chisq > 100)
00570     {
00571         uves_msg_warning("Reduced chi^2 of fit is greater than 100: %f", 
00572                  red_chisq);
00573     }
00574     
00575     check(( median_intensity = cpl_table_get_column_median(linetable, "Peak"),
00576         brightest = uves_extract_table_rows(linetable, "Peak", 
00577                         CPL_GREATER_THAN, 
00578                         median_intensity),
00579         ninvalid = cpl_table_count_invalid(brightest, "Ident")),
00580       "Error counting identifications");
00581 
00582     ratio = 1 - ((double) ninvalid)/cpl_table_get_nrow(brightest);
00583     uves_msg("Percentage of identifications among the half brighter lines : %.2f %%",
00584          100*ratio);
00585 
00586     sprintf(qc_key,"QC LINE HALFBRIG");
00587     ck0_nomsg(uves_qclog_add_double(qclog,qc_key,100*ratio,
00588                                    "Half brighter lines frac TRACE0 WIN2",
00589                                    "%f"));
00590 
00591   cleanup:
00592     uves_free_table(&brightest);
00593 
00594     return cpl_error_get_code();
00595 }
00596 
00597 /*----------------------------------------------------------------------------*/
00611 /*----------------------------------------------------------------------------*/
00612 static cpl_error_code
00613 compute_lambda(cpl_table *linetable, 
00614            const polynomial *dispersion_relation, 
00615            const polynomial *dispersion_variance,
00616                bool verbose)
00617 {
00618     int i;
00619     bool printed_warning = false;
00620     
00621     /* Check input */
00622     passure(linetable           != NULL, " ");
00623     passure(dispersion_relation != NULL, " ");
00624     /* 'dispersion_variance' may be NULL */
00625     
00626     passure( uves_polynomial_get_dimension(dispersion_relation) == 2, "%d", 
00627          uves_polynomial_get_dimension(dispersion_relation));
00628     
00629     /* Input columns */
00630     passure(cpl_table_has_column(linetable, "X")           , " ");
00631     passure(cpl_table_has_column(linetable, "Order")       , " ");
00632     passure(cpl_table_has_column(linetable, "Ident")       , " ");
00633     /* Output columns */
00634     passure(cpl_table_has_column(linetable, LINETAB_LAMBDAC)     , " ");
00635     /* The column 'dLambdaC' is set to invalid if 'dispersion_variance' is NULL */
00636     passure(cpl_table_has_column(linetable, "dLambdaC")    , " ");  
00637     passure(cpl_table_has_column(linetable, "dIdent")      , " ");
00638     passure(cpl_table_has_column(linetable, LINETAB_RESIDUAL), " ");
00639     passure(cpl_table_has_column(linetable, "Residual_pix"), " ");
00640     passure(cpl_table_has_column(linetable, LINETAB_PIXELSIZE)   , " ");
00641     
00642     /* The linetable is sorted w.r.t. order. 
00643        Move to the first order above minorder */
00644     for(i = 0; i < cpl_table_get_nrow(linetable); i++)
00645     {
00646         int order;
00647         double x, dfdx;
00648         double lambdac, dlambdac, pixelsize;
00649         order = cpl_table_get_int(linetable, "Order", i, NULL);
00650         
00651         x     = cpl_table_get_double(linetable, "X", i, NULL);
00652         
00653         /* Evaluate the dispersion relation
00654            m.lambda = f(x,m)  (2d global fit)  */
00655         
00656         lambdac =
00657         uves_polynomial_evaluate_2d(dispersion_relation, x, order) / order;
00658         
00659         /* Pixelsize = dl/dx = (df/dx)/m  (for fixed m) */
00660             dfdx = uves_polynomial_derivative_2d(dispersion_relation, x, order, 1);
00661             if (dfdx < 0) {
00662                 if (!printed_warning && verbose) {
00663                     uves_msg_warning("Inferred dispersion (dlambda/dx) is negative at"
00664                                      "(x, order) = (%f, %d)", x, order);
00665                     printed_warning = true;  /* To avoid repeating the same warning */
00666                 }
00667                 else {
00668                     uves_msg_debug("Inferred dispersion (dlambda/dx) is negative at "
00669                                    "(x, order) = (%f, %d)", x, order);
00670                 }
00671             }
00672             pixelsize = dfdx / order;
00673         
00674         check(( cpl_table_set_double(linetable, LINETAB_LAMBDAC , i, lambdac),
00675             cpl_table_set_double(linetable, LINETAB_PIXELSIZE, i, pixelsize)),
00676             "Error writing table");
00677         
00678         if (dispersion_variance != NULL)
00679         {
00680             /* d( lambda  (x, order) ) = 
00681                d( lambda*m(x, order) ) / m    */
00682             dlambdac = 
00683             sqrt(uves_polynomial_evaluate_2d(dispersion_variance, x, order))
00684             / order;
00685             
00686             cpl_table_set_double(linetable, "dLambdaC" , i, dlambdac);
00687         }
00688         else
00689         {
00690             /* Only the ratio of a line's "dLambdaC" to other
00691                lines' are used, so set "dLambdaC" to a constant value
00692                when the actual uncertainty is not known
00693             */
00694             cpl_table_set_double(linetable, "dLambdaC" , i, 1.0);
00695         }
00696         
00697         /* If line is identified, calculate residual */
00698         if (cpl_table_is_valid(linetable, "Ident", i)) 
00699         {
00700             double ident = cpl_table_get_double(linetable, "Ident", i, NULL);
00701             cpl_table_set_double(linetable, LINETAB_RESIDUAL, i,
00702                      ident - lambdac);
00703             cpl_table_set_double(linetable, "Residual_pix", i, 
00704                      (ident - lambdac)/pixelsize);
00705         }
00706         else
00707         {
00708             cpl_table_set_invalid(linetable, LINETAB_RESIDUAL, i);
00709             cpl_table_set_invalid(linetable, "Residual_pix", i);
00710         }
00711     }
00712     
00713     /* Sort by 'Order' (ascending), then 'X' (ascending) */
00714     check( uves_sort_table_2(linetable, "Order", "X", false, false), 
00715        "Error sorting table");
00716     
00717   cleanup:
00718     return cpl_error_get_code();
00719 }
00720 
00721 
00722 /*----------------------------------------------------------------------------*/
00759 /*----------------------------------------------------------------------------*/
00760 
00761 static int
00762 identify_lines(cpl_table *linetable, const cpl_table *line_refer, double ALPHA)
00763 {
00764     int number_identifications = 0;      /* Result */
00765     int linetable_size;
00766     int linerefer_size;
00767     int row;
00768     int *histogram = NULL;
00769     const double minlog  = -5.0;         /* Histogram (it's sort of ugly
00770                         to hardcode these numbers, but
00771                         as long as it works, ...) */
00772     const double maxlog  = 15.0;
00773     const int nbins       = 400;
00774     double error = 0;                    /* Dimensionless factor
00775                         that controls IDs */
00776     double average_dlambda_com = 0;      /* Average of uncertainty of 
00777                         predicted wavelenghts */
00778 
00779     /* Check input */
00780     passure( linetable  != NULL, " ");
00781     /* Line table input columns */
00782     passure( cpl_table_has_column(linetable, LINETAB_LAMBDAC  ), " "); /* Predicted
00783                                       wavelength  */
00784     passure( cpl_table_has_column(linetable, "dLambdaC" ), " "); /* Predicted wavelength 
00785                                     uncertainty  */
00786     passure( cpl_table_has_column(linetable, "X"        ), " "); /* Line position, used
00787                                     only for messaging */
00788     passure( cpl_table_has_column(linetable, "Order"    ), " "); /* Absolute order number 
00789                                     of line */
00790     passure( cpl_table_has_column(linetable, "Xwidth"   ), " "); /* Line width (sigma) */
00791     passure( cpl_table_has_column(linetable, LINETAB_PIXELSIZE), " "); /* Pixelsize */
00792 
00793     /* Line table output columns */
00794     passure( cpl_table_has_column(linetable, "Ident"    ), " "); /* Identified catalogue 
00795                                     wavelength */
00796     passure( cpl_table_has_column(linetable, "dIdent"   ), " "); /* Uncertainty of IDed
00797                                     catalogue wavelength */
00798 
00799     /* Catalogue */
00800     passure( line_refer != NULL, " ");
00801     passure( cpl_table_has_column(line_refer, "Wave" ), " ");    /* Catalogue wavelength */
00802     passure( cpl_table_has_column(line_refer, "dWave"), " ");    /* Uncertainty of
00803                                     catalogue wavelength */
00804     
00805     linetable_size = cpl_table_get_nrow(linetable);
00806     linerefer_size = cpl_table_get_nrow(line_refer);
00807     assure(linerefer_size >= 1, CPL_ERROR_ILLEGAL_INPUT, "Empty line reference table");
00808     
00809     /* Parameter */
00810     passure( 0 < ALPHA && ALPHA <= 1, "%e", ALPHA);
00811 
00812     /* Get average uncertainty of predicted wavelength */
00813     average_dlambda_com = cpl_table_get_column_median(linetable, "dLambdaC");
00814 
00815     /* Initialize histogram to zero */
00816     histogram = cpl_calloc(nbins, sizeof(int));
00817     assure_mem( histogram );
00818     
00819 
00820     /* First: Find distance to closest catalogue match, 
00821        distance to nearest neighbour, 
00822        and calculate histogram (to get average of distances to nearest neighbour) */
00823     for (row = 0; row < linetable_size; row++) {
00824     double lambda_com;                 /* Computed (predicted) wavelength */
00825     double line_width;                 /* Line width (sigma) in wlu       */
00826     double line_fwhm;                  /* Line FWHM in wlu                */
00827     int order;                         /* (Absolute) order of detected wavelength */
00828     double lambda_cat;                 /* Catalogue wavelength */
00829     double lambda_cat_sigma;           /* Catalogue wavelength uncertainty */
00830     double distance_cat_sq;            /* Distance to catalogue wavelength (squared) */
00831     double nn_distance_sq;             /* Distance to nearest neighbour (squared) */
00832     int row_cat;                       /* Row number of best matching catalogue wavelength */
00833     
00834     /* Read line table */
00835     lambda_com  = cpl_table_get_double(linetable, LINETAB_LAMBDAC   , row, NULL);
00836     order       = cpl_table_get_int   (linetable, "Order"     , row, NULL);
00837 
00838     
00839     line_width = 
00840         cpl_table_get_double(linetable, "Xwidth"    , row, NULL) *
00841         fabs(cpl_table_get_double(linetable, LINETAB_PIXELSIZE , row, NULL)); 
00842     /* Convert pixel->wlu */
00843 
00844     line_fwhm = TWOSQRT2LN2 * line_width;
00845     
00846     /* Find closest match in catalogue */
00847     row_cat          = uves_wavecal_find_nearest(
00848         line_refer, lambda_com, 0, linerefer_size - 1);
00849     lambda_cat       = cpl_table_get_double(line_refer, "Wave", row_cat, NULL);
00850     lambda_cat_sigma = cpl_table_get_double(line_refer, "dWave",row_cat, NULL);
00851 
00852     /* Distance to closest match */
00853     distance_cat_sq = (lambda_com - lambda_cat)*(lambda_com - lambda_cat);
00854     
00855         /* Determine the distance to the next neighbour
00856      * There are (max) 4 candiates: 2 neigbours in spectrum (i.e. line table)
00857      *                          and 2 neigbours in line catalogue
00858      */
00859     {
00860         double lambda_com_prev, lambda_com_next;
00861         int order_prev, order_next;
00862         double lambda_cat_prev, lambda_cat_next;
00863 
00864         nn_distance_sq = DBL_MAX;
00865 
00866         /* Read previous and next rows of line table */
00867         if (row >= 1) 
00868         {
00869             order_prev      = cpl_table_get_int   (
00870             linetable, "Order"  , row - 1, NULL);
00871             lambda_com_prev = cpl_table_get_double(
00872             linetable, LINETAB_LAMBDAC, row - 1, NULL);
00873             
00874             if (order == order_prev) 
00875             {
00876                 nn_distance_sq = uves_min_double(nn_distance_sq,
00877                                  (lambda_com_prev - lambda_com)*
00878                                  (lambda_com_prev - lambda_com)
00879                 );
00880             }
00881         }
00882 
00883         if (row <= linetable_size - 2) 
00884         {
00885             order_next      = cpl_table_get_int   (linetable, "Order", 
00886                                row + 1, NULL);
00887             lambda_com_next = cpl_table_get_double(linetable, LINETAB_LAMBDAC,
00888                                row + 1, NULL);
00889             
00890             if (order == order_next) 
00891             {
00892                 nn_distance_sq = uves_min_double(nn_distance_sq,
00893                                  (lambda_com_next - lambda_com)*
00894                                  (lambda_com_next - lambda_com)
00895                 );
00896             }
00897         }
00898         
00899         /* Read previous and next rows of catalogue */
00900         if (row_cat >= 1)
00901         {
00902             lambda_cat_prev = cpl_table_get_double(
00903             line_refer, "Wave", row_cat - 1, NULL);
00904 
00905             nn_distance_sq = uves_min_double(
00906             nn_distance_sq,
00907             (lambda_cat_prev - lambda_cat)*
00908             (lambda_cat_prev - lambda_cat)
00909             );
00910         }
00911         if (row_cat <= linerefer_size - 2) 
00912         {
00913             lambda_cat_next = cpl_table_get_double(
00914             line_refer, "Wave", row_cat + 1, NULL);
00915 
00916             nn_distance_sq = uves_min_double(
00917             nn_distance_sq,
00918             (lambda_cat_next - lambda_cat)*
00919             (lambda_cat_next - lambda_cat)
00920             );
00921         }
00922 
00923         /* Update distance to nearest neighbour with a 
00924            safety margin (determined by parameter ALPHA < 1) */
00925         if (nn_distance_sq < DBL_MAX)
00926         {
00927             nn_distance_sq *= ALPHA*ALPHA;
00928         }
00929         
00930     }/* Find next neighbour */
00931     
00932     /* Update line table */
00933     cpl_table_set_double(linetable, "Lambda_candidate", row, lambda_cat);
00934     cpl_table_set_double(linetable, "dLambda_candidate",row, lambda_cat_sigma);
00935     cpl_table_set_double(linetable, "dLambda_cat_sq", row, distance_cat_sq);
00936     cpl_table_set_double(linetable, "dLambda_nn_sq", row, nn_distance_sq);
00937 
00938     /* Update histogram with the interval
00939        [distance_cat_sq ; nn_distance_sq]  (in units of line_fwhm) */
00940     {
00941         int ilow  = uves_round_double((0.5*log(distance_cat_sq/(line_fwhm*line_fwhm))
00942                        - minlog)/(maxlog - minlog) * nbins);
00943         int ihigh = uves_round_double((0.5*log(nn_distance_sq /(line_fwhm*line_fwhm))
00944                        - minlog)/(maxlog - minlog) * nbins);
00945         int i;
00946         
00947         for (i = uves_max_int(ilow, 0); i < uves_min_int(ihigh, nbins); i++) 
00948         {
00949             histogram[i] += 1;
00950         }
00951     }
00952     }/* ... finding neighbours */
00953     
00954     /* Determine error as peak of histogram */
00955     {
00956     int i;
00957     int maxfreq = -1;
00958     for (i = 0; i < nbins; i++) 
00959         {
00960         uves_msg_debug("histogram[%d] = %d", i, histogram[i]);
00961         if (histogram[i] > maxfreq) 
00962             {
00963             maxfreq = histogram[i];
00964             error   = exp( i / ((double)nbins) * (maxlog - minlog) + minlog ) ;
00965             /* == the dimensionless factor to be multiplied by Xwidth */
00966             }
00967         }
00968     uves_msg_debug("Dimensionless error factor is %f", error);
00969     }
00970     
00971     /* Sketch of situation:
00972        
00973   lambda_com                  Nearest neighbour
00974 
00975       |                            |
00976       |    |                       |
00977       |    |                       |
00978       |    |                       |
00979            |
00980 
00981     lambda_cat
00982 
00983 
00984      The 'average' (as inferred from the histogram)
00985      midpoint between 'lambda_cat' and 'nearest neighbour'
00986      is at   'error' * 'line_fwhm' .
00987     */
00988     
00989     /* Make the identification if
00990        
00991     1) the catalogue candidate is within two sigma:
00992          | lambda_cat - lambda_com | < 2 * dlambda_com
00993 
00994     and
00995 
00996     2) after multiplying the distance to the nearest neighbour by ALPHA < 1,
00997     the nearest neighbour is farther away than the catalogue wavelength 
00998          distance_nn  >  distance_cat
00999     and farther away than the tolerance
01000          distance_nn  >  line_fwhm * error
01001      
01002     */
01003     for (row = 0; row < linetable_size; row++)
01004     {
01005         double distance_cat_sq;              /* Distance to catalogue wavelength (squared) */
01006         double nn_distance_sq;               /* Distance to nearest neighbour (squared) */
01007         double tolerance_sq;
01008         double dlambda_com;
01009         double line_width;                   /* Line width (1 sigma) */
01010         double line_fwhm;
01011         double lambda_cat;
01012         double lambda_cat_sigma;             /* Uncertainty of lambda_cat */
01013         
01014         lambda_cat       = cpl_table_get_double(linetable,  "Lambda_candidate", row, NULL);
01015         lambda_cat_sigma = cpl_table_get_double(linetable, "dLambda_candidate", row, NULL);
01016         
01017         
01018         /* Sigma less than 1 pixel is usually not
01019            justified by the data (which obviously 
01020            has a resolution of only 1 pixel). Such
01021            an underenstimation of the uncertainty
01022            leads to wrong identifications.
01023            Therefore use a width of at least 1 pixel */
01024         line_width =
01025         uves_max_double(1, cpl_table_get_double(linetable, "Xwidth"    , row, NULL)) *
01026         fabs(cpl_table_get_double(linetable, LINETAB_PIXELSIZE , row, NULL));
01027         /* convert to wlu */
01028         
01029         line_fwhm = TWOSQRT2LN2 * line_width;
01030 
01031         /* As the uncertainty of the computed wavelength is used
01032          *  line_fwhm (in w.l.u.)
01033          * To take into account the fact that lines near the edge of
01034          * the chip have larger error of the computed wavelength,
01035          * this is also scaled according to the accuracy of the dispersion
01036          * relation, i.e. multiplied by  dl/<dl>,
01037          * where <dl> is an average, say the median, of uncertainties of
01038          * all predicted wavelengths.
01039          */
01040         
01041         dlambda_com = line_fwhm 
01042         * cpl_table_get_double(linetable, "dLambdaC"  , row, NULL)
01043         / average_dlambda_com;
01044         
01045         tolerance_sq = line_fwhm*line_fwhm * error*error;
01046         
01047         distance_cat_sq = cpl_table_get_double(linetable, "dLambda_cat_sq", row, NULL);
01048         nn_distance_sq  = cpl_table_get_double(linetable, "dLambda_nn_sq" , row, NULL);
01049         
01050 #if WANT_BIG_LOGFILE
01051         uves_msg_debug("(order,x) = (%d,%f) lcom = %f+-%f lcat = %f "
01052                "dist_cat = %f (%f pixels) tolerance = %.3f error = %f "
01053                "nn = %f (%f pixels)", 
01054                cpl_table_get_int   (linetable, "Order"  , row, NULL),
01055                cpl_table_get_double(linetable, "X"      , row, NULL),
01056                cpl_table_get_double(linetable, LINETAB_LAMBDAC, row, NULL),
01057                dlambda_com,
01058                lambda_cat,
01059                sqrt(distance_cat_sq),
01060                sqrt(distance_cat_sq)
01061                /cpl_table_get_double(linetable, LINETAB_PIXELSIZE, row, NULL),
01062                sqrt(tolerance_sq),
01063                error,
01064                sqrt(nn_distance_sq),
01065                sqrt(nn_distance_sq)
01066                /cpl_table_get_double(linetable, LINETAB_PIXELSIZE, row, NULL));
01067 #endif
01068         
01069         /* Make the ID? */
01070         if (distance_cat_sq < (dlambda_com)*(dlambda_com)
01071         && tolerance_sq < nn_distance_sq
01072         && distance_cat_sq < nn_distance_sq)
01073         {
01074             number_identifications++;
01075             cpl_table_set_double(linetable, "Ident", row, lambda_cat);
01076             cpl_table_set_double(linetable, "dIdent",row, lambda_cat_sigma);
01077 #if WANT_BIG_LOGFILE
01078             uves_msg_debug("ID made");
01079 #endif
01080         }
01081         else 
01082         {
01083             if (cpl_table_is_valid(linetable, "Ident", row)) {
01084             number_identifications++;                      
01085             /* Also count lines that were already identified */
01086             uves_msg_debug("Line at (%d,%f) does not match ID criterion anymore",
01087                        cpl_table_get_int   (linetable, "Order", row, NULL),
01088                        cpl_table_get_double(linetable, "X", row, NULL)
01089             );
01090         }
01091         }
01092     }
01093 
01094   cleanup:
01095     cpl_free(histogram);
01096     return number_identifications;
01097 }
01098 
01099 /*----------------------------------------------------------------------------*/
01125 /*----------------------------------------------------------------------------*/
01126 static polynomial *
01127 calibrate_global(const cpl_table *linetable,
01128                  cpl_table **selected,
01129          int degree, bool verbose,
01130                  bool reject,
01131          double TOLERANCE,
01132                  double kappa,
01133          double *red_chisq, polynomial **dispersion_variance,
01134          double *pixelsize,
01135          double *rms_wlu,
01136          double *rms_pixels)
01137 {
01138     polynomial *dispersion_relation = NULL; /* Result */
01139     cpl_table *identified = NULL;
01140     int valid_ids = 
01141     cpl_table_get_nrow(linetable) - 
01142     cpl_table_count_invalid(linetable, "Ident");
01143     int rejected;
01144     
01145     passure( (pixelsize == NULL) == (rms_wlu    == NULL) &&
01146          (pixelsize == NULL) == (rms_pixels == NULL), " ");
01147 
01148     assure( degree < 0 ||
01149             valid_ids >= (degree + 1)*(degree + 1), CPL_ERROR_ILLEGAL_INPUT,
01150         "There are not enough identifications to create a %d.-degree global fit. "
01151         "%d needed. %d found", degree, (degree + 1)*(degree + 1), valid_ids);
01152     
01153     identified = cpl_table_duplicate(linetable);
01154     assure_mem(identified);
01155 
01156     /* Delete rows with invalid 'Ident' and large residuals */
01157     if (reject)
01158         {
01159             check_nomsg( rejected = uves_delete_bad_lines(identified, TOLERANCE, kappa) );
01160             uves_msg_debug("%d lines rejected %f %f", rejected, TOLERANCE, kappa);
01161         }
01162     else
01163         {
01164             check( uves_erase_invalid_table_rows(identified, "Ident"),
01165                    "Error erasing un-identified lines");
01166         }
01167 
01168     
01169     /* Create column 'Aux' = 'Order' * 'Ident' */
01170     check((  cpl_table_duplicate_column(identified, "Aux", identified, "Ident"),
01171              cpl_table_multiply_columns(identified, "Aux", "Order"),
01172              
01173              /* Create column 'dAux' = 'Order' * 'dIdent' */
01174              cpl_table_duplicate_column(identified, "dAux", identified, "dIdent"),
01175              cpl_table_multiply_columns(identified, "dAux", "Order")),
01176           "Error setting up temporary table");
01177 
01178     /* Fit */
01179     
01180     if (degree >= 0) {
01181         check( dispersion_relation =
01182                uves_polynomial_regression_2d(identified, 
01183                                              "X", "Order", "Aux", 
01184                                              "dAux", /* Use "dAux" for weighting,
01185                                                         to be able to compute an uncertainty
01186                                                         of WAVEC.
01187                                                         
01188                                                         It would probably make more sense
01189                                                         to use the uncertainty of 'dX' for
01190                                                         weighting. */
01191                                              degree, degree,
01192                                              NULL, NULL, NULL,     /* Don't add extra columns */
01193                                              NULL,                 /* mse */
01194                                              red_chisq,
01195                                              dispersion_variance, 
01196                                              reject ? kappa : -1, -1),
01197                "Error fitting polynomial. Possible cause: too few (%d) "
01198                "line identifications", valid_ids);
01199     } 
01200     else {
01201         int max_degree = 8;
01202         double min_rms = -1; /* disabled */
01203         double min_reject = -1; /* disabled */
01204         check( dispersion_relation =
01205                uves_polynomial_regression_2d_autodegree(identified,
01206                                                         "X", "Order", "Aux", 
01207                                                         "dAux", 
01208                                                         NULL, NULL, NULL,  
01209                                                         NULL, 
01210                                                         red_chisq,
01211                                                         dispersion_variance,
01212                                                         reject ? kappa : -1,
01213                                                         max_degree, max_degree, 
01214                                                         min_rms, min_reject,
01215                                                         verbose,
01216                                                         NULL, NULL, 0, NULL),
01217                "Error fitting polynomial. Possible cause: too few (%d) "
01218                "line identifications", valid_ids);
01219     }
01220 
01221     if (pixelsize != NULL)
01222     {
01223         /* Compute parameters if requested */
01224 
01225         check( compute_lambda(identified, dispersion_relation, NULL,
01226                                   false),
01227            "Error applying dispersion relation");
01228         
01229         *pixelsize = cpl_table_get_column_median(identified, LINETAB_PIXELSIZE);
01230         *rms_wlu   = cpl_table_get_column_stdev (identified, LINETAB_RESIDUAL);
01231         *rms_pixels= cpl_table_get_column_stdev (identified, "Residual_pix");
01232     }
01233 
01234     if (selected != NULL) {
01235         *selected = cpl_table_duplicate(identified);
01236     }
01237 
01238   cleanup:
01239     uves_free_table(&identified);
01240     if (cpl_error_get_code() != CPL_ERROR_NONE)
01241     {
01242         uves_polynomial_delete(&dispersion_relation);
01243     }
01244     
01245     return dispersion_relation;
01246 }
01247 
01248 
01249 
01250 /*----------------------------------------------------------------------------*/
01257 /*----------------------------------------------------------------------------*/
01258 
01259 int
01260 uves_wavecal_identify_lines_ppm(cpl_table *linetable, const cpl_table *line_refer)
01261 {
01262     int result = 0;
01263     int minorder, maxorder;
01264     int order;
01265     cpl_table *lt_order = NULL;
01266     cpl_table *refer_order = NULL;
01267     cpl_vector *peaks = NULL;
01268     cpl_vector *lines = NULL;
01269     cpl_bivector *ids = NULL;
01270 
01271     assure( cpl_table_has_column(linetable, LINETAB_LAMBDAC), CPL_ERROR_DATA_NOT_FOUND,
01272             "Missing column %s", LINETAB_LAMBDAC);
01273 
01274     assure( cpl_table_has_column(linetable, LINETAB_PIXELSIZE), CPL_ERROR_DATA_NOT_FOUND,
01275             "Missing column %s", LINETAB_PIXELSIZE);
01276 
01277     assure( cpl_table_has_column(linetable, "Order"), CPL_ERROR_DATA_NOT_FOUND,
01278             "Missing column %s", "Order");
01279 
01280     minorder = uves_round_double( cpl_table_get_column_min(linetable, "Order"));
01281     maxorder = uves_round_double( cpl_table_get_column_max(linetable, "Order"));
01282 
01283     /* Reset identifications */
01284     if (cpl_table_has_column(linetable, "Ident_ppm"))
01285         {
01286             cpl_table_erase_column(linetable, "Ident_ppm");
01287         }
01288 
01289     cpl_table_new_column(linetable, "Ident_ppm", CPL_TYPE_DOUBLE);
01290     
01291     for (order = minorder; order <= maxorder; order++)
01292         {
01293             const double tolerance = 0.05; /* relative tolerance on interval ratios */
01294             double min_lambda, max_lambda;
01295             double min_disp, max_disp;
01296 
01297             /* Extract current order */
01298            
01299             uves_free_table(&lt_order);
01300             lt_order = uves_extract_table_rows(linetable, "Order",
01301                                                CPL_EQUAL_TO, order); /* Uses integer comparison */
01302 
01303             check_nomsg((min_lambda = cpl_table_get_column_min(lt_order, LINETAB_LAMBDAC),
01304                          max_lambda = cpl_table_get_column_max(lt_order, LINETAB_LAMBDAC),
01305                          min_disp   = cpl_table_get_column_min(lt_order, LINETAB_PIXELSIZE)*0.99,
01306                          max_disp   = cpl_table_get_column_max(lt_order, LINETAB_PIXELSIZE)*1.01));
01307                         
01308             uves_free_table(&refer_order);
01309             refer_order = uves_extract_table_rows(line_refer, "Wave", CPL_GREATER_THAN,
01310                                                   min_lambda);
01311             uves_extract_table_rows_local(refer_order, "Wave", CPL_LESS_THAN,
01312                                           max_lambda);
01313 
01314             /* Convert to vectors */
01315             {
01316                 int i;
01317                 uves_free_vector(&peaks);
01318                 peaks = cpl_vector_new(cpl_table_get_nrow(lt_order));
01319                 for (i = 0; i < cpl_vector_get_size(peaks); i++)
01320                     {
01321                         cpl_vector_set(peaks, i, cpl_table_get_double(lt_order, "X", i, NULL));
01322                     }
01323                 
01324                 uves_free_vector(&lines);
01325                 lines = cpl_vector_new(cpl_table_get_nrow(refer_order));
01326                 for (i = 0; i < cpl_vector_get_size(lines); i++)
01327                     {
01328                         cpl_vector_set(lines, i, cpl_table_get_double(refer_order, "Wave", i, NULL));
01329                     }
01330             }
01331             
01332             /* Not sure if this is necessary for the PPM algorithm */
01333             cpl_vector_sort(peaks, 1);
01334             cpl_vector_sort(lines, 1);
01335 
01336             uves_msg_debug("Call ppm with %" CPL_SIZE_FORMAT " peaks, %" CPL_SIZE_FORMAT " lines, dispersion range = %f - %f A/pixel",
01337                            cpl_vector_get_size(peaks), 
01338                            cpl_vector_get_size(lines),
01339                            min_disp, max_disp);
01340 
01341             uves_free_bivector(&ids);
01342 
01343 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 0, 0)
01344             ids = cpl_ppm_match_positions(peaks, lines,
01345                                           min_disp, max_disp,
01346                                           tolerance, 
01347                                           NULL, NULL);
01348 #else
01349             ids = irplib_ppm_match_positions(peaks, lines,
01350                                              min_disp, max_disp,
01351                                              tolerance);
01352 #endif
01353 
01354 
01355 
01356             if (ids == NULL)
01357                 {
01358                     uves_msg_warning("Order %d: Point pattern matching failed", order);
01359                     if (cpl_error_get_code() != CPL_ERROR_NONE)
01360                         {
01361                             uves_msg_debug("%s at %s", cpl_error_get_message(),
01362                                            cpl_error_get_where());
01363                             uves_error_reset();
01364                         }
01365                 }
01366             else
01367                 {
01368                     int i, j;
01369 
01370                     uves_msg_debug("%" CPL_SIZE_FORMAT " identifications from point pattern matching (order %d)",
01371                                    cpl_bivector_get_size(ids), order);
01372 
01373                     result += cpl_bivector_get_size(ids);
01374 
01375                     for (i = 0; i < cpl_table_get_nrow(linetable); i++) {
01376 
01377                         if (cpl_table_get_int(linetable, "Order", i, NULL) == order)
01378                             for (j = 0; j < cpl_bivector_get_size(ids); j++)
01379                                 {
01380                                     if (fabs(cpl_table_get_double(linetable, "X", i, NULL) -
01381                                              cpl_bivector_get_x_data(ids)[j]) < 0.001)
01382                                         cpl_table_set_double(linetable, "Ident_ppm", i,
01383                                                              cpl_bivector_get_y_data(ids)[j]);
01384                                 }
01385                     }
01386                 }
01387         }
01388     
01389   cleanup:
01390     uves_free_table(&lt_order);
01391     uves_free_table(&refer_order);
01392     uves_free_vector(&peaks);
01393     uves_free_vector(&lines);
01394     uves_free_bivector(&ids);
01395 
01396     return result;
01397 }

Generated on 9 Mar 2012 for UVES Pipeline Reference Manual by  doxygen 1.6.1