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

Generated on Tue Jun 19 14:39:20 2007 for UVES Pipeline Reference Manual by  doxygen 1.4.6