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

Generated on 3 Mar 2013 for UVES Pipeline Reference Manual by  doxygen 1.6.1