uves_wavecal_firstsolution.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: jmlarsen $
00022  * $Date: 2007/08/21 13:08:26 $
00023  * $Revision: 1.21 $
00024  * $Name: uves-3_9_0 $
00025  * $Log: uves_wavecal_firstsolution.c,v $
00026  * Revision 1.21  2007/08/21 13:08:26  jmlarsen
00027  * Removed irplib_access module, largely deprecated by CPL-4
00028  *
00029  * Revision 1.20  2007/06/06 08:17:33  amodigli
00030  * replace tab with 4 spaces
00031  *
00032  * Revision 1.19  2007/05/25 07:05:21  jmlarsen
00033  * Decreased  warning verbosity
00034  *
00035  * Revision 1.18  2007/04/26 13:21:04  jmlarsen
00036  * Made more robust against inaccurate abs_order polynomial
00037  *
00038  * Revision 1.17  2007/04/10 07:11:56  jmlarsen
00039  * Changed interface of polynomial_regression_2d()
00040  *
00041  * Revision 1.16  2007/03/05 10:22:24  jmlarsen
00042  * Fixed bug in computation of max/min physical order number
00043  *
00044  * Revision 1.15  2007/01/15 08:58:20  jmlarsen
00045  * More robust polynomial fitting
00046  *
00047  * Revision 1.14  2007/01/10 12:40:12  jmlarsen
00048  * Removed unused parameter
00049  *
00050  * Revision 1.13  2006/12/07 08:29:58  jmlarsen
00051  * Compute correct Ynew column for FLAMES
00052  *
00053  * Revision 1.12  2006/11/24 16:24:32  jmlarsen
00054  * Added check of abs order polynomial
00055  *
00056  * Revision 1.11  2006/11/15 15:02:15  jmlarsen
00057  * Implemented const safe workarounds for CPL functions
00058  *
00059  * Revision 1.9  2006/11/15 14:04:08  jmlarsen
00060  * Removed non-const version of parameterlist_get_first/last/next which is 
00061  * already in CPL, added const-safe wrapper, unwrapper and deallocator functions
00062  *
00063  * Revision 1.8  2006/11/06 15:19:42  jmlarsen
00064  * Removed unused include directives
00065  *
00066  * Revision 1.7  2006/08/18 07:07:43  jmlarsen
00067  * Switched order of cpl_calloc arguments
00068  *
00069  * Revision 1.6  2006/07/14 12:43:47  jmlarsen
00070  * Documentation update
00071  *
00072  * Revision 1.5  2006/07/03 13:29:45  jmlarsen
00073  * Reduced max line length
00074  *
00075  * Revision 1.4  2006/03/03 13:54:11  jmlarsen
00076  * Changed syntax of check macro
00077  *
00078  * Revision 1.3  2006/02/15 13:19:15  jmlarsen
00079  * Reduced source code max. line length
00080  *
00081  * Revision 1.2  2006/02/08 09:25:05  jmlarsen
00082  * Fixed bug caused by == comparison of doubles
00083  *
00084  * Revision 1.1  2006/02/03 07:46:30  jmlarsen
00085  * Moved recipe implementations to ./uves directory
00086  *
00087  * Revision 1.27  2005/12/20 08:11:44  jmlarsen
00088  * Added CVS  entry
00089  *
00090  */
00091 /*----------------------------------------------------------------------------*/
00092 /*
00093  * @addtogroup uves_wavecal
00094  */
00095 /*----------------------------------------------------------------------------*/
00098 #ifdef HAVE_CONFIG_H
00099 #  include <config.h>
00100 #endif
00101 
00102 #include <uves_wavecal_firstsolution.h>
00103 
00104 #include <uves_utils.h>
00105 #include <uves_utils_wrappers.h>
00106 #include <uves_dump.h>
00107 #include <uves_error.h>
00108 #include <uves_msg.h>
00109 
00110 #include <cpl.h>
00111 
00112 #include <math.h>
00113 
00114 static int *
00115 write_physical_order(cpl_table *linetable,
00116              const polynomial *absolute_order, 
00117                      const cpl_table *ordertable,
00118              const polynomial *order_locations,
00119              int *first_abs_order, int *last_abs_order);
00120 
00121 static double
00122 calculate_shift(const cpl_table *linetable, const cpl_table *previous, 
00123         const char *column, const char *reference_column, 
00124         double range, double step, double tolerance);
00125 
00126 static double
00127 cross_correlation(double shift, 
00128           const cpl_table *t1, const cpl_table *t2,
00129           const char *column, const char* reference_column, 
00130           int minref, int maxref, double tolerance);
00131 
00132 static polynomial *apply_shift(const cpl_table *previous, 
00133                    const double shift, const int degree, double *mse);
00134 
00135 /*----------------------------------------------------------------------------*/
00182 /*----------------------------------------------------------------------------*/
00183 polynomial *
00184 uves_wavecal_firstsolution(cpl_table *linetable,
00185                const cpl_table *guess, 
00186                polynomial **absolute_order, 
00187                            const cpl_table *ordertable,
00188                            const polynomial *order_locations,
00189                bool flames,
00190                double offset,
00191                int **relative_order, 
00192                int DEGREE, double CORREL_RANGE, double CORREL_STEP,
00193                double CORREL_TOLERANCE, double MAXERROR, 
00194                int *first_abs_order, int *last_abs_order)
00195 {
00196     polynomial *initial_dispersion = NULL;
00197     polynomial *new_absorder = NULL;
00198     const char *er_msg = NULL;
00199     double shift;
00200     double mse;
00201 
00202     /* Get physical order numbering */
00203     check( *relative_order =   write_physical_order(linetable, *absolute_order, 
00204                                                     ordertable,
00205                             order_locations,
00206                             first_abs_order,
00207                             last_abs_order),
00208        "Could not calculate absolute order numbers");
00209 
00210     /* Update the 'absolute_order' map */
00211     {
00212     int row;
00213 
00214     /* Create column for Y-location (in pixels) of order */
00215     cpl_table_new_column(linetable, "Ynew", CPL_TYPE_DOUBLE);
00216     for (row = 0; row < cpl_table_get_nrow(linetable); row++)
00217         {
00218         /* For historical reasons, the column 'Y' contains the
00219            (relative) order number while 'Ynew' contains 
00220            the y-coordinate (in pixels) of the emission line. */
00221         int order = cpl_table_get_int   (linetable, "Y", row, NULL);
00222         double x  = cpl_table_get_double(linetable, "X", row, NULL);
00223         
00224         cpl_table_set_double(
00225             linetable, "Ynew", row, 
00226             uves_polynomial_evaluate_2d(order_locations, x, order));
00227         }
00228 
00229     assure_nomsg( cpl_error_get_code() == CPL_ERROR_NONE,
00230               cpl_error_get_code() );
00231 
00232     new_absorder =
00233         uves_polynomial_regression_2d(linetable, "X", "Ynew", "Order",
00234                       NULL,              /* uncertainty of order number */
00235                       DEGREE, DEGREE,
00236                       NULL, NULL, NULL,  /* New columns */
00237                       NULL, NULL,        /* mse, chi^2 */
00238                       NULL,              /* variance pol. */
00239                       -1, -1);           /* kappa */
00240 
00241     if (cpl_error_get_code() != CPL_ERROR_NONE) /* Singular matrix, or too few points */
00242         {
00243         er_msg = uves_sprintf("%s", cpl_error_get_message());
00244         
00245         uves_error_reset();
00246         uves_msg_warning("Could not make global fit of absolute order number (%s). "
00247                  "Polynomial is not updated",
00248                  er_msg);
00249         }
00250     else
00251         {
00252         uves_polynomial_delete(absolute_order);
00253         *absolute_order = uves_polynomial_duplicate(new_absorder);
00254         }
00255 
00256     /* Calculate absolute_order wrt center of orders, but add offset to Ynew column */
00257     if (flames)
00258         {
00259         cpl_table_add_scalar(linetable, "Ynew", + offset);
00260         }
00261     }
00262 
00263     /* Sort linetable by 'Order' (ascending), then 'X' (ascending) */
00264     uves_sort_table_2(linetable, "Order", "X", false, false);
00265 
00266     /* Cross correlation of guess (linetable) and linetable */
00267     /* Step size should not be less than 2*tolerance */
00268     check( shift = calculate_shift(guess, linetable, "X", "Order", 
00269                    CORREL_RANGE, CORREL_STEP, CORREL_TOLERANCE),
00270        "Could not calculate shift of position w.r.t. guess solution");
00271 
00272     /* Apply shift to guess solution
00273      * Note that it doesn't help to simply call uves_polynomial_shift()
00274      * on the guess solution
00275      * because the requested 'DEGREE' might be different from
00276      * the degree used in the guess solution
00277      */
00278     
00279     check( initial_dispersion = apply_shift(guess, shift, DEGREE, &mse),
00280        "Could not calculate initial dispersion relation");
00281     /* This fit may fail if the input guess table has too few or badly
00282        distributed points, but there is not much to do about that */
00283 
00284     /* Check quality of initial solution */
00285     if(mse > MAXERROR*MAXERROR) 
00286     {
00287         uves_msg_warning("RMS of initial fit (%f pixels) is greater "
00288                  "than tolerance (%f pixels)", sqrt(mse), MAXERROR);
00289     }
00290     
00291   cleanup:
00292     uves_free_string_const(&er_msg);
00293     uves_polynomial_delete(&new_absorder);
00294     if (cpl_error_get_code() != CPL_ERROR_NONE)
00295     {
00296         uves_polynomial_delete(&initial_dispersion);
00297     }
00298     
00299     return initial_dispersion;
00300 }
00301 
00302 /*----------------------------------------------------------------------------*/
00317 /*----------------------------------------------------------------------------*/
00318 static polynomial *
00319 apply_shift(const cpl_table *guess, double shift, int degree, double *mse)
00320 {
00321     polynomial *result = NULL;
00322     cpl_table *t = NULL;
00323     
00324     /* Copy guess table */
00325     check( t = cpl_table_duplicate(guess),
00326        "Error duplicating table");
00327     
00328     /* Create auxillary column  Ident*Order  */
00329     check(( cpl_table_duplicate_column(t, "ident_order", t, "Ident"),
00330         cpl_table_multiply_columns(t, "ident_order", "Order")),
00331       /* ident_order = Ident * Order */
00332       "Error creating auxillary column");
00333     
00334     /* Shift x values */
00335     check( cpl_table_add_scalar(t, "X", shift), "Error shifting column 'X'");
00336 
00337     /* Fit lambda*m = f(x, m) */
00338     /* Don't use uncertainties because they might not exist in guess solution */
00339     result = uves_polynomial_regression_2d(t, "X", "Order", "ident_order", NULL,
00340                        degree, degree,
00341                        NULL, NULL, NULL,
00342                        mse, NULL,
00343                        NULL, -1, -1);
00344 
00345     /* If failed, set error to SINGULAR_MATRIX */
00346     if (cpl_error_get_code() != CPL_ERROR_NONE) /* Singular matrix or too few points */
00347     {
00348         uves_error_reset();
00349 
00350         assure( false, CPL_ERROR_SINGULAR_MATRIX,
00351             "Polynomial fitting failed");
00352     }
00353 
00354   cleanup:
00355     uves_free_table(&t);
00356     return result;
00357 }
00358 
00359 /*----------------------------------------------------------------------------*/
00382 /*----------------------------------------------------------------------------*/
00383 
00384 static double
00385 calculate_shift(const cpl_table *linetable, const cpl_table *guess, const char *column,
00386         const char *reference_column, double range, double step, double tolerance)
00387 {
00388     cpl_type t;
00389     int minorder, maxorder;
00390     int N, i;
00391     double shift, max_corr, median_corr, maxpos = 0;
00392     cpl_table *temp = NULL;
00393 
00394     assure( cpl_table_has_column(linetable, column), 
00395         CPL_ERROR_ILLEGAL_INPUT, "Table has no '%s' column", column);
00396     assure( cpl_table_has_column(guess , column), 
00397         CPL_ERROR_ILLEGAL_INPUT, "Table has no '%s' column", column);
00398     assure( cpl_table_has_column(linetable, reference_column),
00399         CPL_ERROR_ILLEGAL_INPUT, "Table has no '%s' column", reference_column);
00400     assure( cpl_table_has_column(guess , reference_column), 
00401         CPL_ERROR_ILLEGAL_INPUT, "Table has no '%s' column", reference_column);
00402     assure( range > 0, CPL_ERROR_ILLEGAL_INPUT, "Range = %f", range);
00403 
00404     t = cpl_table_get_column_type(linetable, column);
00405     assure( t == CPL_TYPE_DOUBLE, CPL_ERROR_TYPE_MISMATCH,
00406         "Column '%s' has type '%s'. Double expected", column, uves_tostring_cpl_type(t));
00407 
00408     t = cpl_table_get_column_type(guess, column);
00409     assure( t == CPL_TYPE_DOUBLE, CPL_ERROR_TYPE_MISMATCH,
00410         "Column '%s' has type '%s'. Double expected", column, uves_tostring_cpl_type(t));
00411 
00412     t = cpl_table_get_column_type(linetable, reference_column);
00413     assure( t == CPL_TYPE_INT, CPL_ERROR_TYPE_MISMATCH,
00414         "Ref. column '%s' has type '%s'. Integer expected", 
00415         reference_column, uves_tostring_cpl_type(t));
00416     
00417     t = cpl_table_get_column_type(guess, reference_column);
00418     assure( t == CPL_TYPE_INT, CPL_ERROR_TYPE_MISMATCH,
00419         "Ref. column '%s' has type '%s'. Integer expected",
00420         reference_column, uves_tostring_cpl_type(t));
00421 
00422     /* Identify common orders    */
00423     check(( minorder = 
00424         uves_max_int(cpl_table_get_column_min(guess, reference_column), 
00425              cpl_table_get_column_min(linetable, reference_column)),
00426         maxorder = 
00427         uves_min_int(cpl_table_get_column_max(guess, reference_column), 
00428              cpl_table_get_column_max(linetable, reference_column))),
00429       "Error reading column '%s'", reference_column);
00430     
00431     assure(maxorder >= minorder, CPL_ERROR_ILLEGAL_INPUT, "No common orders found");
00432     
00433     uves_msg("Min/max common absolute orders = %d - %d", minorder, maxorder);
00434     
00435     /* Find maximum of cross correlation function 
00436        for shifts in [-range ; range]
00437     */
00438 
00439     /* Count number of candidates,
00440        so we can create a table of the correct size
00441        which is used to get the median of
00442        all cross-correlation values */
00443     N = 0;
00444     for (shift = -range; shift <= range; shift += step) 
00445     {
00446         N += 1;
00447     }
00448 
00449     temp = cpl_table_new(N);
00450     cpl_table_new_column(temp, "Corr", CPL_TYPE_DOUBLE);
00451 
00452     max_corr = -1;
00453     maxpos = 0;
00454     for (shift = -range, i = 0;
00455      i < N;
00456      shift += step , i++) 
00457     {
00458         double corr;
00459         check( corr = cross_correlation(shift, linetable, guess, column, 
00460                         reference_column, minorder, maxorder, tolerance),
00461            "Error calculating spectrum cross correlation for shift = %f pixel(s)", 
00462            shift);
00463         
00464         /* Update table */
00465         check( cpl_table_set_double(temp, "Corr", i, corr),
00466            "Error updating table");
00467         
00468         uves_msg_debug("Correlation(shift=%f) = %f", shift, corr);
00469         
00470         if (corr > max_corr) 
00471         {
00472             max_corr = corr;
00473             maxpos = shift;
00474         }
00475     }
00476 
00477     /* To estimate significance,
00478        compare the detected max cross-correlation 
00479        value to "no correlation" estimated as the
00480        median of all cross-corr. values */
00481 
00482     median_corr = cpl_table_get_column_median(temp, "Corr");
00483     
00484     /* Correlation value is integer ; don't divide by zero */
00485     if (median_corr < 0.5)
00486     {
00487         median_corr = 1;
00488     }
00489 
00490     uves_msg("Estimated shift compared to guess solution is %f pixels (%.2f sigma detection)",
00491          maxpos, max_corr / median_corr);
00492 
00493     /* The correlation peak is usually 
00494        ~30 or more times the background,
00495        so warn if peak value is less than, say,
00496        10 times background. */
00497     if (max_corr / median_corr < 10)
00498     {
00499         uves_msg_warning("Cross-correlation with guess solution is "
00500                  "only %f times no correlation (usually >30). "
00501                  "Make sure that the guess solution is within ~10 pixels "
00502                  "of the real dispersion relation; otherwise the following "
00503                  "wavelength calibration is likely to fail or converge "
00504                  "to a wrong solution",
00505                  max_corr / median_corr);
00506     }
00507     
00508   cleanup:
00509     uves_free_table(&temp);
00510     return maxpos;
00511 }
00512 
00513 /*----------------------------------------------------------------------------*/
00535 /*----------------------------------------------------------------------------*/
00536 static double
00537 cross_correlation(double shift,
00538           const cpl_table *t1, const cpl_table *t2,
00539           const char *column, const char* reference_column, 
00540           int minref, int maxref, double tolerance)
00541 {
00542     double result = 0;  /* The result */
00543     int i1 = 0;         /* Pointers to table rows */
00544     int i2 = 0;
00545 
00546     /* For efficiency reasons, retrieve the pointers to the columns */
00547     const double *col1 = cpl_table_get_data_double_const(t1, column);
00548     const double *col2 = cpl_table_get_data_double_const(t2, column);
00549     const int *ref1 = cpl_table_get_data_int_const(t1, reference_column);
00550     const int *ref2 = cpl_table_get_data_int_const(t2, reference_column);
00551 
00552     int N1 = cpl_table_get_nrow(t1);
00553     int N2 = cpl_table_get_nrow(t2);
00554 
00555     assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
00556         "Error reading input table");
00557     
00558     /* Search for matching rows */
00559     while (i1 < N1 && ref1[i1] <= maxref && 
00560        i2 < N2 && ref2[i2] <= maxref) {
00561     if      (i1 < minref || ref1[i1] < ref2[i2])
00562         i1++;
00563     else if (i2 < minref || ref1[i1] > ref2[i2])
00564         i2++;
00565     else {
00566         /* Reference values match */
00567         double difference = col2[i2] - (col1[i1] + shift);
00568         
00569         if      (difference > tolerance)
00570         {
00571             i1++;
00572         }
00573         else if (difference < -tolerance)
00574         {
00575             i2++;
00576         }
00577         else {
00578         /* Matching rows found: |col2-col1-shift| <= tolerance.
00579            Update result and continue search */
00580         result += 1.0;
00581         i2++;
00582         }
00583     }
00584     }
00585 
00586 
00587   cleanup:
00588     return result;
00589 }
00590 
00591 
00592 /*----------------------------------------------------------------------------*/
00611 /*----------------------------------------------------------------------------*/
00612 static int *
00613 write_physical_order(cpl_table *linetable, const polynomial *absolute_order,
00614                      const cpl_table *ordertable,
00615              const polynomial *order_locations,
00616              int *first_abs_order, int *last_abs_order)
00617 {
00618     int *relative_order = NULL; /* Result */
00619     int *physical_order = NULL;
00620     int minorder, maxorder;
00621     int maxphysical;
00622     cpl_table *temp = NULL;
00623     const polynomial *map = NULL;
00624 
00625     double *sum = NULL;   /* Auxillary variables used to calculate the average */
00626     int      *N = NULL;
00627     
00628     int i;
00629 
00630     check( cpl_table_new_column(linetable, "Order", CPL_TYPE_INT),
00631        "Error creating column");
00632 
00633     check( cpl_table_new_column(linetable, "AbsOrder", CPL_TYPE_DOUBLE),
00634        "Error creating column");
00635     
00636     check(( minorder = cpl_table_get_column_min(ordertable, "Order"),
00637         maxorder = cpl_table_get_column_max(ordertable, "Order")),
00638       "Could not read min. and max. order numbers");
00639 
00640     assure( minorder > 0, CPL_ERROR_ILLEGAL_INPUT,
00641         "Non-positive order number (%d) in linetable", minorder);
00642     
00643     physical_order = cpl_calloc(maxorder + 1, sizeof(int));
00644     assure_mem( physical_order );
00645     
00646     /* First calculate the estimation of the
00647        absolute order number at each line position */
00648     for (i = 0; i < cpl_table_get_nrow(linetable); i++) {
00649     double x, y;
00650     double absorder;
00651     int order;
00652     
00653     order = cpl_table_get_int   (linetable, "Y", i, NULL); 
00654         /* The column 'Y' contains the (relative) order number */
00655 
00656     x     = cpl_table_get_double(linetable, "X", i, NULL);
00657 
00658     y = uves_polynomial_evaluate_2d(order_locations, x, order);
00659 
00660         absorder = uves_polynomial_evaluate_2d(absolute_order, x, y);
00661 
00662         uves_msg_debug("Order #%d: Absolute order = %f at x = %f",
00663                order, absorder, x);
00664 
00665         cpl_table_set_double(linetable, "AbsOrder", i, absorder);
00666     }
00667  
00668     {
00669         int degree = 1;
00670         int coeff1, coeff2;  /* absorder = coeff1 + coeff2 * relative_order */
00671         int order;
00672         int relorder_median;
00673         int absorder_median;
00674 
00675         check_nomsg( map = 
00676                      uves_polynomial_regression_1d(linetable,
00677                                                    "Y", "AbsOrder", NULL,
00678                                                    degree, 
00679                                                    NULL, NULL, NULL, -1));
00680         
00681         relorder_median = uves_round_double(cpl_table_get_column_median(linetable, "Y"));
00682         absorder_median = uves_round_double(uves_polynomial_evaluate_1d(map, relorder_median));
00683             
00684         if (uves_polynomial_derivative_1d(map, relorder_median) > 0) {
00685             coeff2 = 1;
00686         }
00687         else {
00688             coeff2 = -1;
00689         }
00690         
00691         coeff1 = absorder_median - coeff2 * relorder_median;
00692 
00693     uves_msg_debug("Assuming relation: abs.order = %d + (%d) * rel.order",
00694                        coeff1, coeff2);
00695         
00696         maxphysical = -1;
00697         for (order = minorder; order <= maxorder; order++) {
00698             physical_order[order] = coeff1 + coeff2 * order;
00699             
00700             assure(physical_order[order] > 0, CPL_ERROR_ILLEGAL_OUTPUT,
00701                    "Estimated physical order number is non-positive (%d)", 
00702                    physical_order[order]);
00703             
00704             if (physical_order[order] > maxphysical) 
00705                 {
00706                     maxphysical = physical_order[order];
00707                 }
00708 
00709             uves_msg_debug("Mapping relative order #%d to absolute order #%d", 
00710                            order, physical_order[order]);
00711         }
00712         
00713         /* Get first and last physical orders */
00714         *first_abs_order = physical_order[minorder];
00715         *last_abs_order  = physical_order[maxorder];
00716         
00717         passure( *first_abs_order - *last_abs_order == coeff2*(minorder - maxorder),
00718                  "%d %d %d %d %d",
00719                  *first_abs_order, *last_abs_order, coeff2, minorder, maxorder);
00720         
00721     }
00722 
00723     /* Then write this rounded mean value to every row of the table */
00724     for (i = 0; i < cpl_table_get_nrow(linetable); i++) {
00725     int order;
00726     order = cpl_table_get_int (linetable, "Y", i, NULL);
00727     cpl_table_set_int(linetable, "Order", i, physical_order[order]);
00728     }
00729 
00730     /* Calculate the inverse of 'physical_order' */
00731     relative_order = cpl_calloc(maxphysical + 1, sizeof(int));
00732     for (i = 0; i <= maxorder; i++)
00733     {
00734         relative_order[physical_order[i]] = i;
00735     }
00736     
00737   cleanup:
00738     uves_free_table(&temp);
00739     uves_polynomial_delete_const(&map);
00740     cpl_free(sum);
00741     cpl_free(physical_order);
00742     cpl_free(N);
00743 
00744     return relative_order;
00745 }

Generated on Fri Apr 18 14:11:44 2008 for UVES Pipeline Reference Manual by  doxygen 1.5.1