uves_orderpos_follow.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: 2011/12/08 14:03:53 $
00023  * $Revision: 1.44 $
00024  * $Name: uves-5_0_0 $
00025  * $Log: uves_orderpos_follow.c,v $
00026  * Revision 1.44  2011/12/08 14:03:53  amodigli
00027  * Fix warnings with CPL6
00028  *
00029  * Revision 1.43  2010/09/24 09:32:04  amodigli
00030  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
00031  *
00032  * Revision 1.41  2010/05/06 14:55:29  amodigli
00033  * clearer error message
00034  *
00035  * Revision 1.40  2007/08/30 07:56:54  amodigli
00036  * fixed some doxygen warnings
00037  *
00038  * Revision 1.39  2007/08/23 08:16:40  jmlarsen
00039  * Indentation change
00040  *
00041  * Revision 1.38  2007/08/21 13:08:26  jmlarsen
00042  * Removed irplib_access module, largely deprecated by CPL-4
00043  *
00044  * Revision 1.37  2007/06/28 09:18:01  jmlarsen
00045  * Return actualy polynomial degree used
00046  *
00047  * Revision 1.36  2007/06/06 08:17:33  amodigli
00048  * replace tab with 4 spaces
00049  *
00050  * Revision 1.35  2007/05/22 14:09:56  amodigli
00051  * removed compilation warnings
00052  *
00053  * Revision 1.34  2007/05/14 15:57:15  jmlarsen
00054  * Avoid tracing orders at very edge of chip
00055  *
00056  * Revision 1.33  2007/04/12 14:02:24  jmlarsen
00057  * Made robust against input orders outside image
00058  *
00059  * Revision 1.32  2007/04/12 12:02:09  jmlarsen
00060  * Decreased verbosity
00061  *
00062  * Revision 1.31  2007/04/10 07:07:25  jmlarsen
00063  * Changed interface of polynomial_regression_2d()
00064  *
00065  * Revision 1.30  2007/03/30 07:07:28  jmlarsen
00066  * Fixed mixed code and variable definitions
00067  *
00068  * Revision 1.29  2007/03/28 14:02:21  jmlarsen
00069  * Removed unused parameter
00070  *
00071  * Revision 1.28  2007/03/28 11:39:09  jmlarsen
00072  * Killed MIDAS flag, removed dead code
00073  *
00074  * Revision 1.27  2007/03/05 10:17:03  jmlarsen
00075  * Support slope parameter in 1d fitting
00076  *
00077  * Revision 1.26  2007/02/26 11:56:39  jmlarsen
00078  * Made fitting (even) more robust against points with low sigma
00079  *
00080  * Revision 1.25  2007/01/17 13:26:18  jmlarsen
00081  * Added comment
00082  *
00083  * Revision 1.24  2007/01/15 08:46:25  jmlarsen
00084  * More robust polynomial fitting
00085  *
00086  * Revision 1.23  2006/11/23 10:04:31  jmlarsen
00087  * Minor message change
00088  *
00089  * Revision 1.22  2006/11/15 15:02:14  jmlarsen
00090  * Implemented const safe workarounds for CPL functions
00091  *
00092  * Revision 1.20  2006/11/15 14:04:08  jmlarsen
00093  * Removed non-const version of parameterlist_get_first/last/next which is already
00094  * in CPL, added const-safe wrapper, unwrapper and deallocator functions
00095  *
00096  * Revision 1.19  2006/11/13 14:23:55  jmlarsen
00097  * Removed workarounds for CPL const bugs
00098  *
00099  * Revision 1.18  2006/11/06 15:19:41  jmlarsen
00100  * Removed unused include directives
00101  *
00102  * Revision 1.17  2006/08/23 09:33:03  jmlarsen
00103  * Renamed local variables shadowing POSIX reserved names
00104  *
00105  * Revision 1.16  2006/08/17 14:40:06  jmlarsen
00106  * Added missing documentation
00107  *
00108  * Revision 1.15  2006/08/17 14:33:28  jmlarsen
00109  * Added missing opening bracket
00110  *
00111  * Revision 1.14  2006/08/17 13:56:53  jmlarsen
00112  * Reduced max line length
00113  *
00114  * Revision 1.13  2006/08/17 09:18:27  jmlarsen
00115  * Removed CPL2 code
00116  *
00117  * Revision 1.12  2006/08/10 10:52:41  jmlarsen
00118  * Removed workaround for cpl_image_get_bpm
00119  *
00120  * Revision 1.11  2006/08/08 11:27:18  amodigli
00121  * upgrade to CPL3
00122  *
00123  * Revision 1.10  2006/07/14 12:22:17  jmlarsen
00124  * Do not use uncertainties in linear fit
00125  *
00126  * Revision 1.9  2006/07/03 14:20:39  jmlarsen
00127  * Exclude bad pixels from order tracing
00128  *
00129  * Revision 1.8  2006/05/12 15:05:49  jmlarsen
00130  * Pass image bpm as extra parameter to fitting routine for efficiency reasons
00131  *
00132  * Revision 1.7  2006/04/24 09:34:26  jmlarsen
00133  * Adapted to new interface of gaussian fitting routine
00134  *
00135  * Revision 1.6  2006/04/10 12:38:43  jmlarsen
00136  * Minor layout change
00137  *
00138  * Revision 1.5  2006/04/06 08:44:16  jmlarsen
00139  * Renamed shadowing variables
00140  *
00141  * Revision 1.4  2006/03/24 14:12:18  jmlarsen
00142  * Use MIDAS default values for polynomial degree if MIDAS flag is set
00143  *
00144  * Revision 1.3  2006/03/03 13:54:11  jmlarsen
00145  * Changed syntax of check macro
00146  *
00147  * Revision 1.2  2006/02/15 13:19:15  jmlarsen
00148  * Reduced source code max. line length
00149  *
00150  * Revision 1.1  2006/02/03 07:46:30  jmlarsen
00151  * Moved recipe implementations to ./uves directory
00152  *
00153  * Revision 1.42  2006/01/25 16:15:59  jmlarsen
00154  * Changed interface of gauss.fitting routine
00155  *
00156  * Revision 1.41  2006/01/19 08:47:24  jmlarsen
00157  * Inserted missing doxygen end tag
00158  *
00159  * Revision 1.40  2006/01/12 15:41:14  jmlarsen
00160  * Moved gauss. fitting to irplib
00161  *
00162  * Revision 1.39  2005/12/19 16:17:55  jmlarsen
00163  * Replaced bool -> int
00164  *
00165  */
00166 
00167 /*----------------------------------------------------------------------------*/
00171 /*----------------------------------------------------------------------------*/
00174 #ifdef HAVE_CONFIG_H
00175 #  include <config.h>
00176 #endif
00177 
00178 #include <uves_orderpos_follow.h>
00179 
00180 #include <uves_plot.h>
00181 #include <uves_utils.h>
00182 #include <uves_utils_wrappers.h>
00183 #include <uves_error.h>
00184 #include <uves_msg.h>
00185 
00186 #include <cpl.h>
00187 #include <math.h>
00188 #include <float.h>
00189 
00190 static cpl_table * trace_order(const cpl_table *ordertable, int order,
00191                    const cpl_image *inputimage, const cpl_image *noise,
00192                    const cpl_binary *image_bad,
00193                    int TRACESTEP, 
00194                    double MAXGAP);
00195 static int            count_orders(const cpl_table *tracetable);
00196 static double         fit_order_linear(cpl_table *singletrace, int order, double KAPPA,
00197                                        double *slope);
00198 static int      get_xcenter(int nx, int ny, cpl_table *ordertab, int row);
00199 static int      get_ycenter(int nx, int ny, cpl_table *ordertab, int row);
00200 static int  get_orderlength(int nx, int ny, cpl_table *ordertab, int row);
00201 static double estimate_threshold(const cpl_image *inputimage, 
00202                  const cpl_image *nosie, 
00203                  cpl_table *ordertable, 
00204                  int row, double relative_threshold);
00205 static bool find_centroid(const cpl_image *inputimage, 
00206               const cpl_image *noise,
00207               const cpl_binary *image_bad, 
00208               double threshold, int spacing, int x, double *yguess, 
00209               double *dY);
00210 
00211 /*----------------------------------------------------------------------------*/
00251 /*----------------------------------------------------------------------------*/
00252 cpl_table *
00253 uves_locate_orders(const cpl_image *inputimage, 
00254                    const cpl_image *noise,
00255                    cpl_table *ordertable, 
00256                    int TRACESTEP, 
00257                    double MINTHRESH,
00258                    double MAXGAP,
00259                    double MAXRMS, 
00260                    int *DEFPOL1, 
00261                    int *DEFPOL2, 
00262                    double KAPPA, 
00263                    polynomial **bivariate_fit, 
00264                    int *orders_traced)
00265 {
00266     cpl_table *tracetable  = NULL;  /* The result */
00267     cpl_table *singletrace = NULL;  /* Location of one order */
00268     cpl_table *temp        = NULL;  /* Temporary  */
00269     const cpl_mask *image_badmap = NULL;
00270     const cpl_binary *image_bad  = NULL;
00271     int N;  /* Initial number of orders detected */
00272 
00273     double mse, red_chisq;
00274     int order;
00275 
00276     /* Check input */
00277     assure_nomsg( inputimage != NULL, CPL_ERROR_NULL_INPUT);
00278     assure_nomsg( noise != NULL, CPL_ERROR_NULL_INPUT);
00279     assure( cpl_image_get_size_x(inputimage) == cpl_image_get_size_x(noise) &&
00280             cpl_image_get_size_y(inputimage) == cpl_image_get_size_y(noise),
00281             CPL_ERROR_INCOMPATIBLE_INPUT, 
00282             "Image sizes are %" CPL_SIZE_FORMAT "x%" CPL_SIZE_FORMAT " and %" CPL_SIZE_FORMAT "x%" CPL_SIZE_FORMAT "",
00283             cpl_image_get_size_x(inputimage), cpl_image_get_size_x(noise),
00284             cpl_image_get_size_y(inputimage), cpl_image_get_size_y(noise));
00285 
00286     assure_nomsg( ordertable != NULL, CPL_ERROR_NULL_INPUT);
00287     assure( cpl_table_get_ncol(ordertable) == 4, 
00288                   CPL_ERROR_ILLEGAL_INPUT,
00289                   "%" CPL_SIZE_FORMAT " columns found. 4 expected",
00290                   cpl_table_get_ncol(ordertable));
00291     assure( cpl_table_has_column(ordertable, "Intersept"),
00292                   CPL_ERROR_DATA_NOT_FOUND,
00293                   "Missing column Intersept");
00294     assure( cpl_table_has_column(ordertable, "Slope"),
00295                   CPL_ERROR_DATA_NOT_FOUND,
00296                   "Missing column Slope");
00297     assure( cpl_table_has_column(ordertable, "Order"),
00298                   CPL_ERROR_DATA_NOT_FOUND,
00299                   "Missing column Order");
00300     assure( cpl_table_has_column(ordertable, "Spacing"),
00301                   CPL_ERROR_DATA_NOT_FOUND,
00302                   "Missing column Spacing");
00303     assure_nomsg( DEFPOL1 != NULL, CPL_ERROR_NULL_INPUT );
00304     assure_nomsg( DEFPOL2 != NULL, CPL_ERROR_NULL_INPUT );
00305 
00306     image_badmap = cpl_image_get_bpm_const(inputimage);
00307     image_bad    = cpl_mask_get_data_const(image_badmap);
00308 
00309     N = cpl_table_get_nrow(ordertable);
00310 
00311     *bivariate_fit = NULL;
00312     
00313     /* Initialise result table */
00314     check(( tracetable = cpl_table_new(0),
00315         cpl_table_new_column(tracetable, "Order"          , CPL_TYPE_INT),
00316         cpl_table_new_column(tracetable, "X"              , CPL_TYPE_INT),
00317         cpl_table_new_column(tracetable, "Y"              , CPL_TYPE_DOUBLE),
00318         cpl_table_new_column(tracetable, "dY"             , CPL_TYPE_DOUBLE),
00319         cpl_table_new_column(tracetable, "Residual_Square", CPL_TYPE_DOUBLE),
00320         cpl_table_new_column(tracetable, "OrderRMS"       , CPL_TYPE_DOUBLE),
00321             cpl_table_new_column(tracetable, "OrderSlope"     , CPL_TYPE_DOUBLE)),  
00322       /* The order's RMS (from linear fit) */
00323       "Could not initialize order trace table");
00324     
00325     /* Info about the order */
00326     check(( cpl_table_new_column(ordertable, "Xcenter",      CPL_TYPE_INT),
00327         cpl_table_new_column(ordertable, "Ycenter",      CPL_TYPE_INT),
00328         cpl_table_new_column(ordertable, "OrderLength",  CPL_TYPE_INT),
00329         cpl_table_new_column(ordertable, "Threshold",    CPL_TYPE_DOUBLE),
00330         cpl_table_new_column(ordertable, "MinThreshold", CPL_TYPE_DOUBLE),
00331         cpl_table_new_column(ordertable, "RMS",          CPL_TYPE_DOUBLE),
00332         cpl_table_new_column(ordertable, "TraceSlope",   CPL_TYPE_DOUBLE)),
00333         "Could not add columns to order table");
00334     
00335     *orders_traced = 0;
00336 
00337     /* Trace all orders and make a linear fit */
00338     for (order = 1; order <= N; order++)
00339     {
00340             /* Calculate parameters used for tracing */
00341         int nx = cpl_image_get_size_x(inputimage);
00342         int ny = cpl_image_get_size_y(inputimage);
00343         int points_traced = 0;
00344             int xc = get_xcenter (nx, ny, ordertable, order - 1);
00345             int yc = get_ycenter (nx, ny, ordertable, order - 1);
00346             
00347             check(( cpl_table_set_int(ordertable, "Xcenter"     , order - 1, xc),
00348                     /* Order n is at row n-1 */
00349                     cpl_table_set_int(ordertable, "Ycenter"     , order - 1, yc),
00350                     cpl_table_set_int(ordertable, "OrderLength" , order - 1, 
00351                                       get_orderlength (nx, ny, ordertable, order - 1))),
00352                   "Could not calculate order line geometry");
00353             
00354             if (!(1 <= xc && xc <= nx && 1 <= yc && yc <= ny))
00355                 {
00356                     uves_msg_warning("Order %d: Center of order (%d, %d) is outside image "
00357                                      "(intersept = %.2f, slope = %f)",
00358                                      order, xc, yc, 
00359                                      cpl_table_get_double(ordertable, "Intersept", order-1, NULL),
00360                                      cpl_table_get_double(ordertable, "Slope", order-1, NULL));
00361                 }
00362             else
00363                 {
00364                     check( cpl_table_set_double(
00365                                ordertable, "Threshold"   , order - 1, 
00366                                estimate_threshold(inputimage, noise, ordertable, order - 1, -1/*not used*/)),
00367                            "Could not calculate max. threshold");
00368                     check( cpl_table_set_double(
00369                                ordertable, "MinThreshold", order - 1,
00370                                estimate_threshold(inputimage, noise, ordertable, order - 1, MINTHRESH)),
00371                            "Could not calculate min. threshold");
00372                 }
00373         
00374         /* Trace this order */
00375         uves_free_table(&singletrace);
00376         check( singletrace = trace_order(ordertable,
00377                          order,
00378                          inputimage,
00379                          noise,
00380                          image_bad,
00381                          TRACESTEP,
00382                                              MAXGAP),
00383            "Error occured while tracing order #%d", order);
00384 
00385         check(  points_traced = cpl_table_get_nrow(singletrace), "Could not read table size");
00386 
00387         passure( cpl_table_get_ncol(singletrace) == 3, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(singletrace));
00388         passure( cpl_table_has_column(singletrace, "X"), " ");
00389         passure( cpl_table_has_column(singletrace, "Y"), " ");
00390         passure( cpl_table_has_column(singletrace, "dY"), " ");
00391         
00392         /* If no points could be located, issue a warning
00393            and continue with the next order  */
00394         if (points_traced == 0)
00395         {
00396             uves_msg_warning("Could not trace order #%d", order);
00397             check( cpl_table_set_invalid(ordertable, "RMS", order - 1),
00398                "Could not flag order %d RMS as invalid", order);
00399         }
00400         else 
00401         { /* At least one x-position of this order was traced */
00402             double rms=0;
00403                     double slope=0;
00404             
00405             /* Fit order (linear) and write RMS to trace 
00406                table and to (hough) order table */
00407             check( rms = fit_order_linear(singletrace, order, KAPPA, &slope),
00408                "Creating linear fit of order #%d failed", order);
00409 
00410             check(( cpl_table_set_double(ordertable, "RMS", order - 1, rms),
00411                 cpl_table_fill_column_window_double(singletrace, "OrderRMS", 
00412                                 0, points_traced, rms)),
00413               "Could not write RMS of order #%d to tables", order);
00414             
00415             check(( cpl_table_set_double(ordertable, "TraceSlope", order - 1, slope),
00416                 cpl_table_fill_column_window_double(singletrace, "OrderSlope", 
00417                                 0, points_traced, slope)),
00418               "Could not write slope of order #%d to tables", order);
00419             
00420 
00421             passure( cpl_table_get_ncol(singletrace) == 7, "%" CPL_SIZE_FORMAT "",
00422                  cpl_table_get_ncol(singletrace));
00423             passure( cpl_table_has_column(singletrace, "X"), " ");
00424             passure( cpl_table_has_column(singletrace, "Y"), " ");
00425             passure( cpl_table_has_column(singletrace, "dY"), " ");
00426             passure( cpl_table_has_column(singletrace, "Linear fit"), " ");
00427             passure( cpl_table_has_column(singletrace, "Residual_Square"), " ");
00428             passure( cpl_table_has_column(singletrace, "OrderRMS"), " ");
00429             passure( cpl_table_has_column(singletrace, "OrderSlope"), " ");
00430 
00431             /* Remove unnecessary column before appending */
00432             check( cpl_table_erase_column(singletrace, "Linear fit"),
00433                "Could not delete column 'Linear fit'");
00434             
00435             /* Write current order number to single order table */
00436             check(( cpl_table_new_column(singletrace, "Order", CPL_TYPE_INT),
00437                 cpl_table_fill_column_window_int(
00438                 singletrace, "Order", 
00439                 0, cpl_table_get_nrow(singletrace), order)
00440                   ),
00441               "Could not create new column 'Order'");
00442             
00443             /* The two tables now contain the same columns */
00444             passure( cpl_table_compare_structure(singletrace, tracetable) == 0, " ");
00445             
00446             /* Append to 'tracetable' */
00447             check( cpl_table_insert(tracetable, singletrace,
00448                         cpl_table_get_nrow(tracetable)), 
00449                "Could not append single order #%d to trace table", order);
00450             
00451             *orders_traced += 1;
00452         }
00453         
00454     }/*  for ... order */
00455     
00456     /* Plot initial (before rejection) order tracing */
00457     check( uves_plot_table(tracetable, "X", "Y",
00458                "Initial trace (%d orders)", *orders_traced),
00459        "Plotting failed");
00460 
00461     /* The trace table now contains these columns */
00462     passure( cpl_table_get_ncol(tracetable) == 7, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(tracetable));
00463     passure( cpl_table_has_column(tracetable, "X"), " ");
00464     passure( cpl_table_has_column(tracetable, "Order"), " ");
00465     passure( cpl_table_has_column(tracetable, "Y"), " ");
00466     passure( cpl_table_has_column(tracetable, "dY"), " ");
00467     passure( cpl_table_has_column(tracetable, "Residual_Square"), " ");
00468     passure( cpl_table_has_column(tracetable, "OrderRMS"), " ");
00469     passure( cpl_table_has_column(tracetable, "OrderSlope"), " ");
00470     
00471     assure(*orders_traced >= 1, CPL_ERROR_ILLEGAL_OUTPUT, "No orders could be traced");
00472     
00473     /* Remove badly traced orders from 'tracetable' */
00474     {
00475     double maxrms;
00476     int orders_rejected;
00477     check( maxrms = 
00478            uves_max_double(0.05, MAXRMS * cpl_table_get_column_median(ordertable, "RMS")),
00479            "Could not read median RMS");
00480     
00481     uves_msg_debug("Maximum admissible RMS is %.2f pixels", maxrms);
00482     
00483     /* Select orders with RMS > maxrms */
00484     check( orders_rejected = uves_select_table_rows(
00485            ordertable, "RMS", CPL_GREATER_THAN, maxrms),
00486            "Could not select rows in order table");
00487     
00488     /* Delete rows from trace table */
00489     if (orders_rejected > 0) 
00490         {
00491         uves_msg_warning("%d order(s) rejected because RMS "
00492                  "(from linear fit) was too large", orders_rejected);
00493         
00494         /* Delete rejected orders from 'tracetable' */
00495         check(  uves_erase_table_rows(tracetable, "OrderRMS", 
00496                           CPL_GREATER_THAN, maxrms),
00497             "Could not erase bad orders from trace table");
00498         
00499         /* Don't remove from 'ordertable' */
00500         }
00501     else
00502         {
00503         uves_msg_debug("All RMSs are less than %.2f", maxrms);
00504         }
00505 
00506 
00507         /* Reject based on line slope 
00508            (this is not the slope from a Hough transform
00509            but the slope measured after tracing the order)
00510         */
00511         check_nomsg( orders_rejected = 
00512                uves_select_table_rows(
00513                    ordertable, "TraceSlope", CPL_GREATER_THAN, 0.5) +
00514                uves_select_table_rows(
00515                    ordertable, "TraceSlope", CPL_LESS_THAN, -0.5));
00516         
00517         if (orders_rejected > 0) {
00518             uves_msg_warning("%d order(s) rejected because slope was outside [-0.5 ; 0.5]",
00519                              orders_rejected);
00520 
00521             check_nomsg( uves_erase_table_rows(tracetable, "OrderSlope",
00522                                          CPL_GREATER_THAN, 0.5));
00523             check_nomsg( uves_erase_table_rows(tracetable, "OrderSlope",
00524                                          CPL_LESS_THAN, -0.5));
00525         }
00526         else {
00527             uves_msg_debug("All line slopes are within [-0.5 ; 0.5]");
00528         }
00529     }
00530 
00531     /* Remove points with too low 'dY', 
00532      * they would have too much weight in fit.
00533      */
00534     {
00535     double dy_median = cpl_table_get_column_median(tracetable, "dY");
00536         double threshold = 0.40*dy_median;
00537         int nreject;
00538 
00539     check_nomsg( nreject = uves_erase_table_rows(tracetable, "dY", CPL_LESS_THAN, 
00540                                                      threshold) );
00541 
00542         uves_msg_debug("Rejected %d points with dY less than %f pixels (median = %f pixels)",
00543                        nreject, threshold, dy_median);
00544     }
00545 
00546     /* Auto-detect optimal pol. degree if it is negative 
00547      * (i.e. not specified)
00548      */
00549     if (*DEFPOL1 < 0 || *DEFPOL2 < 0)
00550     {
00551         int deg1, deg2;            /* Current degrees                            */
00552         int new_deg1, new_deg2;    /* New degrees                                */
00553         double red_chisq1, mse1;   /* Reduced chi^sq, mse  for  (DEG1+1, DEG2  ) */
00554         double red_chisq2, mse2;   /* Reduced chi^sq, mse  for  (DEG1  , DEG2+1) */
00555         double red_chisq3, mse3;   /* Reduced chi^sq, mse  for  (DEG1+1, DEG2+1) */
00556         bool adjust1 = (*DEFPOL1 < 0);   /* Flags indicating if DEFPOL1/DEFPOL2
00557                            should be adjusted */
00558         bool adjust2 = (*DEFPOL2 < 0);   /*   (or is held constant)               */
00559         int finished;                   /* 0 = finished, 
00560                            1 = moved to (DEG1+1, DEG2  )
00561                            2 = moved to (DEG1  , DEG2+1)
00562                            3 = moved to (DEG1+1, DEG2+1)         */
00563         int number_of_orders  = 0;      /* The number of order lines left after 
00564                            kappa-sigma clipping */
00565         int number_of_orders1 = 0;
00566         int number_of_orders2 = 0;
00567         int number_of_orders3 = 0;
00568 
00569         if (adjust1)
00570         {
00571             /* Initialize */
00572             *DEFPOL1 = 1;
00573             deg1 = 1; 
00574         }
00575         else
00576         {
00577             /* Don't modify */
00578             deg1 = *DEFPOL1;
00579         }
00580         if (adjust2)
00581         {
00582             /* Initialize */
00583             *DEFPOL2 = 1;
00584             deg2 = 1; 
00585         }
00586         else
00587         {
00588             /* Don't modify */
00589             deg2 = *DEFPOL2;
00590         }
00591 
00592         uves_free_table(&temp);
00593         temp = cpl_table_duplicate(tracetable);
00594         uves_polynomial_delete(bivariate_fit);
00595         check( *bivariate_fit = uves_polynomial_regression_2d(
00596                temp,
00597                "X", "Order", "Y", "dY",
00598                deg1,
00599                deg2,
00600                NULL, NULL, NULL,            /* No extra columns       */
00601                &mse, &red_chisq,
00602                NULL,                        /* No variance polynomial */
00603                KAPPA, -1),
00604            "Error fitting orders");
00605 
00606         check( number_of_orders = count_orders(temp),
00607            "Error counting orders");
00608         
00609         uves_msg_low("(%d, %d)-degree: RMS = %.3f pixels. "
00610              "Red.chi^2 = %.2f (%d orders) *",
00611              deg1,
00612              deg2,
00613              sqrt(mse),
00614              red_chisq,
00615              number_of_orders);
00616 
00617         /* Find best values of deg1, deg2 less than or equal to 8,8
00618            (the fitting algorithm is unstable after this point, anyway) 
00619 
00620         */
00621         do
00622         {
00623             int maxdegree = 6;
00624             finished = 0;
00625             
00626             adjust1 = adjust1 && (deg1 + 1 <= maxdegree);
00627             adjust2 = adjust2 && (deg2 + 1 <= maxdegree);
00628             
00629             /* Try (deg1+1, deg2) */
00630             if (adjust1)
00631             {
00632                 uves_free_table(&temp);
00633                 temp = cpl_table_duplicate(tracetable);
00634                 uves_polynomial_delete(bivariate_fit);
00635                 *bivariate_fit = uves_polynomial_regression_2d(
00636                 temp,
00637                 "X", "Order", "Y", "dY",
00638                 deg1 + 1,
00639                 deg2,
00640                 NULL, NULL, NULL,  /* extra columns */
00641                 &mse1, &red_chisq1,
00642                 NULL,              /* variance polynomial */
00643                 KAPPA, -1);
00644 
00645                 if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
00646                 {
00647                     uves_error_reset();
00648                     mse1 = -1;
00649                     red_chisq1 = DBL_MAX/2;
00650                 }
00651                 else
00652                 {
00653                     assure( cpl_error_get_code() == CPL_ERROR_NONE,
00654                         cpl_error_get_code(),
00655                         "Error fitting orders");
00656 
00657                     check( number_of_orders1 = count_orders(temp),
00658                    "Error counting orders");
00659                 }
00660             }
00661 
00662             /* Try (deg1, deg2+1) */
00663             if (adjust2)
00664             {
00665                 uves_free_table(&temp);
00666                 temp = cpl_table_duplicate(tracetable);
00667                 uves_polynomial_delete(bivariate_fit);
00668                 *bivariate_fit = uves_polynomial_regression_2d(
00669                 temp,
00670                 "X", "Order", "Y", "dY",
00671                 deg1,
00672                 deg2 + 1,
00673                 NULL, NULL, NULL,            /* No extra columns       */
00674                 &mse2, &red_chisq2,
00675                 NULL,                        /* No variance polynomial */
00676                 KAPPA, -1);
00677 
00678                 if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
00679                 {
00680                     uves_error_reset();
00681                     mse2 = -1;
00682                     red_chisq2 = DBL_MAX/2;
00683                 }
00684                 else
00685                 {
00686                     assure( cpl_error_get_code() == CPL_ERROR_NONE,
00687                         cpl_error_get_code(),
00688                         "Error fitting orders");
00689                     
00690                     check( number_of_orders2 = count_orders(temp),
00691                        "Error counting orders");            
00692                 }
00693             }
00694             
00695             /* Try (deg1+1, deg2+1) */
00696             if (adjust1 && adjust2)
00697             {
00698                 uves_free_table(&temp);
00699                 temp = cpl_table_duplicate(tracetable);
00700                 uves_polynomial_delete(bivariate_fit);
00701                 *bivariate_fit = uves_polynomial_regression_2d(
00702                 temp,
00703                 "X", "Order", "Y", "dY",
00704                 deg1 + 1,
00705                 deg2 + 1,
00706                 NULL, NULL, NULL,       /* extra columns       */
00707                 &mse3, &red_chisq3,
00708                 NULL,                   /* variance polynomial */
00709                 KAPPA, -1);
00710 
00711                 if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
00712                 {
00713                     uves_error_reset();
00714                     mse3 = -1;
00715                     red_chisq3 = DBL_MAX/2;
00716                 }
00717                 else
00718                 {
00719                     assure( cpl_error_get_code() == CPL_ERROR_NONE,
00720                         cpl_error_get_code(),
00721                         "Error fitting orders");
00722                     
00723                     check( number_of_orders3 = count_orders(temp),
00724                        "Error counting orders");
00725                 }
00726             }
00727             
00728             /* If fit is significantly better (say, 10% improvement
00729              * in chi^2) in either direction, (in (degree,degree)-space) 
00730              * then move in that direction.
00731              *
00732              * First try to move one step horizontal/vertical, 
00733              * otherwise try to move
00734              * diagonally (i.e. increase both degrees)
00735              *
00736              * Assign to DEFPOL1/2 only if enough orders were detected.
00737              */
00738             
00739             new_deg1 = deg1;
00740             new_deg2 = deg2;
00741             if (adjust1 && mse1 >= 0 && (red_chisq - red_chisq1)/red_chisq > 0.1 &&
00742             red_chisq1 <= red_chisq2)
00743             {
00744                 new_deg1++;
00745                 mse = mse1;
00746                 red_chisq = red_chisq1;
00747                 finished = 1;
00748 
00749                 if (number_of_orders1 >= number_of_orders)
00750                 {
00751                     *DEFPOL1 = new_deg1;
00752                     *DEFPOL2 = new_deg2;
00753                     number_of_orders = number_of_orders1;
00754                 }
00755             }
00756             else if (adjust2 && mse2 >= 0 && (red_chisq - red_chisq2)/red_chisq > 0.1 && 
00757                  red_chisq2 < red_chisq1)
00758             {
00759                 new_deg2++;
00760                 mse = mse2;
00761                 red_chisq = red_chisq2;
00762                 finished = 2;
00763 
00764                 if (number_of_orders2 >= number_of_orders)
00765                 {
00766                     *DEFPOL1 = new_deg1;
00767                     *DEFPOL2 = new_deg2;
00768                     number_of_orders = number_of_orders2;
00769                 }
00770             }
00771             else if (adjust1 && adjust2 && 
00772                  mse3 >= 0 && (red_chisq - red_chisq3)/red_chisq > 0.1)
00773             {
00774                 new_deg1++;
00775                 new_deg2++;
00776                 mse = mse3;
00777                 red_chisq = red_chisq3;
00778                 finished = 3;
00779 
00780                 if (number_of_orders3 >= number_of_orders)
00781                 {
00782                     *DEFPOL1 = new_deg1;
00783                     *DEFPOL2 = new_deg2;
00784                     number_of_orders = number_of_orders3;
00785                 }
00786             }
00787 
00788             /* Print mse, chi^2, ...
00789              * Add a star '*' at the better solution (if any).
00790              */
00791             if (adjust1)
00792             {
00793                 if (mse1 >= 0)
00794                 {
00795                     uves_msg_low("(%d, %d)-degree: RMS = %.3f pixels. "
00796                          "Red.chi^2 = %.3f (%d orders)%s",
00797                          deg1 + 1,
00798                          deg2,
00799                          sqrt(mse1),
00800                          red_chisq1,
00801                          number_of_orders1,
00802                          (finished == 1) ? " *" : "");
00803                 }
00804                 else
00805                 {
00806                     uves_msg_low("(%d, %d)-degree: Singular matrix",
00807                          deg1 + 1,
00808                          deg2);
00809                 }
00810             }
00811 
00812             if (adjust2)
00813             {
00814                 if (mse2 >= 0)
00815                 {
00816                     uves_msg_low("(%d, %d)-degree: RMS = %.3f pixels. "
00817                          "Red.chi^2 = %.3f (%d orders)%s",
00818                          deg1,
00819                          deg2 + 1,
00820                          sqrt(mse2),
00821                          red_chisq2,
00822                          number_of_orders2,
00823                          (finished == 2) ? " *" : "");
00824                 }
00825                 else
00826                 {
00827                     uves_msg_low("(%d, %d)-degree: Singular matrix",
00828                          deg1,
00829                          deg2 + 1);
00830                 }
00831             }
00832             
00833             if (adjust1 && adjust2)
00834             {
00835                 if (mse3 >= 0)
00836                 {
00837                     uves_msg_low("(%d, %d)-degree: RMS = %.3f pixels. "
00838                          "Red.chi^2 = %.3f (%d orders)%s",
00839                          deg1 + 1,
00840                          deg2 + 1,
00841                          sqrt(mse3),
00842                          red_chisq3,
00843                          number_of_orders3,
00844                          (finished == 3) ? " *" : "");
00845                 }
00846                 else
00847                 {
00848                     uves_msg_low("(%d, %d)-degree: Singular matrix",
00849                          deg1 + 1,
00850                          deg2 + 1);
00851                 }
00852             }
00853             
00854             if (finished != 0) 
00855             {
00856                 uves_msg_debug("Moved to degree (%d, %d), finished = %d, "
00857                                            "DEFPOL = %d, %d", 
00858                        new_deg1, new_deg2, finished, *DEFPOL1, *DEFPOL2);
00859             }
00860             
00861             deg1 = new_deg1;
00862             deg2 = new_deg2;
00863             
00864         } while (finished != 0);
00865         
00866         uves_msg_low("Using degree (%d, %d)", *DEFPOL1, *DEFPOL2);
00867 
00868         }/* endif auto degree */
00869 
00870     /* Make the final fit */
00871     uves_polynomial_delete(bivariate_fit);
00872     check( *bivariate_fit = uves_polynomial_regression_2d(tracetable,
00873                               "X", "Order", "Y", "dY",
00874                               *DEFPOL1,
00875                               *DEFPOL2,
00876                               "Yfit", NULL, "dYfit_Square",
00877                               &mse, &red_chisq,
00878                               NULL,  /* variance polynomial */
00879                               KAPPA, -1),
00880        "Error fitting orders");
00881 
00882     uves_msg("RMS error of (%d, %d)-degree fit is %.3f pixels. Reduced chi^2 is %.3f",
00883          *DEFPOL1,
00884          *DEFPOL2,
00885          sqrt(mse),
00886          red_chisq);
00887     
00888     /* Warn about bad fit */
00889     if (sqrt(mse) > 0.3)
00890     {
00891         uves_msg_warning("RMS of bivariate fit (%.2f pixels) "
00892                  "is larger than 0.3 pixels", sqrt(mse));
00893     }
00894     if (red_chisq < .01)
00895     {
00896         uves_msg_warning("Reduced chi^2 of fit is less than 1/100: %f", red_chisq);
00897     }
00898     if (red_chisq > 100)
00899     {
00900         uves_msg_warning("Reduced chi^2 of fit is greater than 100: %f", red_chisq);
00901     }
00902     
00903     /* Create residual column  'Residual' := 'Y' - 'Yfit' */
00904     check(( cpl_table_duplicate_column(tracetable, "Residual", tracetable, "Y"),
00905         cpl_table_subtract_columns(tracetable, "Residual", "Yfit")),
00906         "Error calculating residuals of fit");
00907 
00908     /* Show how many orders were traced */
00909     {
00910     check( *orders_traced =  count_orders(tracetable),
00911            "Error counting orders");
00912     
00913     uves_msg("%d order(s) were traced", *orders_traced);
00914     if (*orders_traced < N)
00915         {
00916         uves_msg_warning("Rejected %d order(s)", N - *orders_traced);
00917         }
00918     }
00919 
00920     /* Plot results */
00921     check( uves_plot_table(tracetable, "X", "Yfit", "%d orders detected", *orders_traced),
00922        "Plotting failed");
00923     check( uves_plot_table(tracetable, "X", "Residual", 
00924                "Residual of fit (RMS = %.3f pixels; red.chi^2 = %f)",
00925                sqrt(mse), red_chisq), "Plotting failed");
00926     check( uves_plot_table(tracetable, "Y", "Residual", 
00927                "Residual of fit (RMS = %.3f pixels; red.chi^2 = %f)",
00928                sqrt(mse), red_chisq), "Plotting failed");
00929     
00930   cleanup:
00931     uves_free_table(&temp);
00932     uves_free_table(&singletrace);
00933     if (cpl_error_get_code() != CPL_ERROR_NONE)    
00934     {
00935         uves_free_table(&tracetable);
00936     }
00937 
00938     return tracetable;
00939 }
00940 
00941 
00942 /*----------------------------------------------------------------------------*/
00950 static int
00951 count_orders(const cpl_table *tracetable)
00952 {
00953     int number = 0;
00954     int previous = -1;
00955     int row;
00956    
00957     passure( tracetable != NULL, " ");
00958     passure( cpl_table_has_column(tracetable, "Order"), " ");
00959  
00960     for (row = 0; row < cpl_table_get_nrow(tracetable); row++)
00961     {
00962     int current;
00963     current = cpl_table_get_int(tracetable, "Order", row, NULL);
00964     if (current != previous)
00965         {
00966         number++;
00967         }
00968     previous = current;
00969     }
00970     
00971   cleanup:
00972     return number;
00973 
00974 }
00975 
00976 
00977 /*----------------------------------------------------------------------------*/
00992 /*----------------------------------------------------------------------------*/
00993 
00994 static double
00995 fit_order_linear(cpl_table *singletrace, 
00996                  int order, 
00997                  double KAPPA,
00998                  double *slope)
00999 {
01000     double mse = 0;              /* mean square error of the fit             */
01001     double intersept;
01002     cpl_table *temp = NULL;      /* Don't remove rows from the input table   */
01003     polynomial *pol = NULL;      /* The 1d polynomial                        */   
01004 
01005     passure( slope != NULL, " ");
01006     passure( cpl_table_get_ncol(singletrace) == 3, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(singletrace));
01007     passure( cpl_table_has_column(singletrace, "X"), " ");
01008     passure( cpl_table_has_column(singletrace, "Y"), " ");
01009     passure( cpl_table_has_column(singletrace, "dY")," ");
01010 
01011     check( temp = cpl_table_duplicate(singletrace),
01012        "Error cloning table");
01013 
01014     if (cpl_table_get_nrow(temp) == 1)
01015     {
01016         /* Only one point: create another point at next table row (1) to 
01017            make linear fitting is possible. */
01018         check(( cpl_table_set_size(temp, 2),
01019             cpl_table_set_int   (temp, "X",  1, uves_max_int(
01020                          cpl_table_get_int   (temp, "X", 0, NULL) - 1, 1)),
01021             cpl_table_set_double(temp, "Y",  1,
01022                      cpl_table_get_double(temp, "Y", 0, NULL)),
01023             cpl_table_set_double(temp, "dY", 1,
01024                      cpl_table_get_double(temp, "dY",0, NULL))),
01025             "Could not add point");
01026     }
01027     
01028     /* Make the linear fit. When kappa-sigma clipping, rows
01029        are removed. Therefore, use a copy of the input table */
01030     check( pol = uves_polynomial_regression_1d(temp,
01031                            "X", "Y", NULL,/* Unweighted fit 
01032                                  for robustness */
01033                            1,             /* Degree         */
01034                            NULL, NULL,    /* Fit, residual  */
01035                            &mse, 
01036                            KAPPA),
01037        "Fitting of order %d failed. You may have to increase value of kappa", 
01038            order);
01039 
01040     intersept = uves_polynomial_get_coeff_1d(pol, 0);
01041     *slope = uves_polynomial_get_coeff_1d(pol, 1);
01042 
01043     uves_msg_debug("The RMS error of order #%d is %.2f pixels; "
01044                    "slope = %f; intersept = %f",
01045                    order, sqrt(mse),
01046                    *slope, intersept);
01047     
01048     /* Write results of fit to input table */
01049     {
01050     int i;
01051 
01052     check(( cpl_table_new_column(singletrace, "Linear fit", CPL_TYPE_DOUBLE),
01053         cpl_table_new_column(singletrace, "Residual_Square", CPL_TYPE_DOUBLE)),
01054         "Error adding table columns");
01055     
01056     for (i = 0; i < cpl_table_get_nrow(singletrace); i++)
01057         {
01058         int    x = cpl_table_get_int   (singletrace, "X", i, NULL);
01059         double y = cpl_table_get_double(singletrace, "Y", i, NULL);
01060 
01061         double linear_fit, residual;
01062 
01063         check (linear_fit = uves_polynomial_evaluate_1d(pol, x),
01064                "Error evaluating polynomial");
01065 
01066         residual = y - linear_fit;
01067 
01068         check(( cpl_table_set_double(singletrace, "Linear fit", i, linear_fit),
01069             cpl_table_set_double(singletrace, "Residual_Square",
01070                          i, residual*residual)),
01071               "Error updating table");
01072         }
01073     }
01074 
01075     /* Add info about the order's RMS+slope for each point */
01076     check(( cpl_table_new_column(singletrace, "OrderRMS", CPL_TYPE_DOUBLE),
01077             cpl_table_new_column(singletrace, "OrderSlope", CPL_TYPE_DOUBLE),
01078         cpl_table_fill_column_window_double(
01079         singletrace, "OrderRMS", 0, cpl_table_get_nrow(singletrace), sqrt(mse)),
01080             cpl_table_fill_column_window_double(
01081         singletrace, "OrderSlope", 0, cpl_table_get_nrow(singletrace), *slope)),
01082           "Could not create columns OrderRMS and OrderSlope");
01083 
01084     passure( cpl_table_get_ncol(singletrace) == 7, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(singletrace));
01085     passure( cpl_table_has_column(singletrace, "X"), " ");
01086     passure( cpl_table_has_column(singletrace, "Y"), " ");
01087     passure( cpl_table_has_column(singletrace, "dY")," ");
01088     passure( cpl_table_has_column(singletrace, "Linear fit"), " ");
01089     passure( cpl_table_has_column(singletrace, "Residual_Square"), " ");
01090     passure( cpl_table_has_column(singletrace, "OrderRMS"), " ");
01091     passure( cpl_table_has_column(singletrace, "OrderSlope"), " ");
01092     
01093   cleanup:
01094     uves_free_table(&temp);
01095     uves_polynomial_delete(&pol);
01096     return sqrt(mse);
01097         
01098 }
01099 
01100 /*----------------------------------------------------------------------------*/
01127 /*----------------------------------------------------------------------------*/
01128 
01129 static cpl_table *
01130 trace_order(const cpl_table *ordertable, int order,
01131         const cpl_image *inputimage, const cpl_image *noise,
01132         const cpl_binary *image_bad,
01133         int TRACESTEP, 
01134         double MAXGAP)
01135 {
01136     cpl_table *singletrace = NULL;
01137     int tracerow;                  /* pointing to the next empty row in the tracetable */
01138     int DIRECTION;
01139     double slope;
01140     double threshold;
01141     double minthreshold;
01142     int nx;
01143     int xcenter;
01144     int ycenter;
01145     int orderlength;     /* x-distance between endpoints */
01146     int order_spacing;   /* approximate distance to next order(s) */
01147     int xmax, xmin;
01148 
01149     nx = cpl_image_get_size_x(inputimage);
01150     /* Initialize result */
01151     check(( singletrace = 
01152         cpl_table_new(nx/TRACESTEP + 2),
01153         cpl_table_new_column(singletrace, "X", CPL_TYPE_INT),
01154         cpl_table_new_column(singletrace, "Y", CPL_TYPE_DOUBLE),
01155         cpl_table_new_column(singletrace, "dY",CPL_TYPE_DOUBLE),
01156         tracerow = 0),
01157         "Could not initialize tracetable");
01158     
01159     /* Trace the order */
01160     /* While less than TRACEITER of the order is traced 
01161        lower threshold, and try again
01162        But don't try more than three times    */
01163     
01164     /* Order number n is in ordertable row n-1 */
01165     check((xcenter      = cpl_table_get_int   (ordertable, "Xcenter"     , order - 1, NULL),
01166            ycenter      = cpl_table_get_int   (ordertable, "Ycenter"     , order - 1, NULL),
01167            orderlength  = cpl_table_get_int   (ordertable, "OrderLength" , order - 1, NULL),
01168            order_spacing= cpl_table_get_int   (ordertable, "Spacing"     , order - 1, NULL),
01169            threshold    = cpl_table_get_double(ordertable, "Threshold"   , order - 1, NULL),
01170            minthreshold = cpl_table_get_double(ordertable, "MinThreshold", order - 1, NULL)),
01171       "Reading order table failed");
01172     
01173     /* Trace once using the minimum threshold */
01174     threshold = minthreshold;
01175     
01176     
01177     /*  Clear the trace table */
01178     tracerow = 0;
01179         
01180     xmax = xmin = xcenter;
01181         
01182     /* Trace it to the left, trace it to the right */
01183     for (DIRECTION = -1; DIRECTION <= 1; DIRECTION += 2)  {
01184         /* Start tracing at this position */
01185         int x = xcenter;
01186         double y = (double) ycenter;
01187         double dy = 0;
01188         int gap_size = 0;    /* gap size (for jumping) in pixels */
01189             
01190         check( slope = cpl_table_get_double(
01191                    ordertable, "Slope", order - 1, NULL), 
01192                "Could not read slope from table");
01193             
01194         if (xcenter < nx/10 || xcenter > (nx*99)/100) {
01195             /* Order at very edge of chip. Give up */
01196             x = 0;
01197 
01198             /* The numbers chosen here: 10% left and 1% right
01199                are finetuned to the blaze-function of UVES */
01200         }
01201 
01202         while(1 <= x && x <= nx && gap_size < MAXGAP*nx) {
01203             bool found;
01204 
01205             check( found = find_centroid(
01206                        inputimage, noise, image_bad, threshold, 
01207                        order_spacing, x, &y, &dy),
01208                    "Could not get order line position");
01209 
01210             /* If found and if
01211                new slope when including this detection is
01212                inside [-1;1] */
01213             if (found && 
01214                 (y - ycenter)/(x - xcenter) > -1 &&
01215                 (y - ycenter)/(x - xcenter) < 1) {
01216                 
01217                 /* Update xmax, xmin */
01218                 xmax = uves_max_int(xmax, x);
01219                 xmin = uves_min_int(xmin, x);
01220                 
01221                 uves_msg_debug("(Order, x, y, dy, threshold) = "
01222                                "(%d, %d, %f, %f, %f)", 
01223                                order, x, y, dy, threshold);
01224                 
01225                 if (!(x == xcenter && DIRECTION == 1)) 
01226                     /* Update table */
01227                     /* When tracing right, don't insert the 
01228                        center point again */
01229                     
01230                     {
01231                         cpl_table_set_int   (
01232                             singletrace, "X", tracerow, x);
01233                         cpl_table_set_double(
01234                             singletrace, "Y", tracerow, y);
01235                         if (dy > 0) {
01236                             cpl_table_set_double(
01237                                 singletrace, "dY", tracerow, dy);
01238                         }
01239                         else {
01240                             cpl_table_set_invalid(
01241                                 singletrace, "dY", tracerow);
01242                         }
01243                         tracerow++;
01244                     }
01245                 
01246                 gap_size = 0;
01247                 
01248             }/* If order found */
01249             else {
01250                 gap_size += TRACESTEP;
01251             }
01252             
01253             /* Initial 'slope' will be the Hough slope */
01254             x = x + DIRECTION * TRACESTEP;
01255             y = y + slope*DIRECTION * TRACESTEP;
01256             
01257             slope = (y - ycenter)/(x - xcenter);
01258             
01259         }/*  while */
01260     
01261     }/*  for... DIRECTION */
01262         
01263     /* Now width of the trace is (xmax - xmin + 1) */
01264 
01265     uves_msg_debug("%d points were traced in order %d", tracerow, order);
01266     
01267     /* Remove the last part of the table (garbage) */
01268     check( cpl_table_set_size(singletrace, tracerow), "Could not resize tracetable");
01269     
01270     /* Set the undetermined 'dY' column values to some value that is not completely off
01271        (such as the median of all other dY). If there are no other points, set dY
01272        to 1.0 pixel which effectively excludes the point from later fits. */
01273     {
01274     double dy_median;
01275     
01276     if (cpl_table_has_valid(singletrace, "dY"))
01277         {
01278         /* Invalid column values are excluded from the computation */
01279         dy_median = cpl_table_get_column_median(singletrace, "dY");
01280         }
01281     else
01282         {
01283         dy_median = 1.0;
01284         }
01285 
01286     /* Write median value to all invalid rows */
01287     cpl_table_select_all(singletrace);
01288     cpl_table_and_selected_invalid(singletrace, "dY");
01289     {
01290             int i;
01291         for (i = 0; i < cpl_table_get_nrow(singletrace); i++)
01292         {
01293             if (cpl_table_is_selected(singletrace, i))
01294             {
01295                 cpl_table_set_double(singletrace, "dY", i, dy_median);
01296             }
01297         }
01298     }
01299     }
01300     
01301     /* Finally, sort the single order table by X */
01302     check( uves_sort_table_1(singletrace, "X", false), "Could not sort order table");    
01303     
01304   cleanup:
01305     if (cpl_error_get_code() != CPL_ERROR_NONE)
01306     {
01307         uves_free_table(&singletrace);
01308     }
01309     
01310     return singletrace;
01311 }
01312 
01313 /*----------------------------------------------------------------------------*/
01326 /*----------------------------------------------------------------------------*/
01327 static int 
01328 get_orderlength(int nx, int ny, cpl_table *ordertable, int row)
01329 {
01330     int x0 = 0, y_0, x1 = 0, y_1;
01331     double intersept, slope;
01332 
01333     check(( intersept = cpl_table_get_double(ordertable, "Intersept", row, NULL),
01334         slope     = cpl_table_get_double(ordertable, "Slope", row, NULL)),
01335         "Could not read line from ordertable");
01336     
01337     /* The left endpoint of the order line is... */
01338     x0 = 1;
01339     y_0 = uves_round_double(intersept + slope*x0);
01340     
01341     /* However, if... */
01342     if (y_0 < 1)
01343     {
01344         y_0 = 1;
01345         x0 = uves_round_double((y_0 - intersept)/slope); /* y = intersept + slope*x */
01346     }
01347     
01348     /* The right endpoint */
01349     x1 = nx;
01350     y_1 = uves_round_double(intersept + slope*nx);
01351     if (y_1 > ny)
01352     {
01353         y_1 = ny;
01354         x1 = uves_round_double((y_1 - intersept)/slope);
01355     }
01356     
01357   cleanup:
01358     return (x1 - x0);
01359 }
01360 
01361 
01362 /*----------------------------------------------------------------------------*/
01375 /*----------------------------------------------------------------------------*/
01376 static int 
01377 get_xcenter(int nx, int ny, cpl_table *ordertable, int row)
01378 {
01379     int x0, y_0, x1, y_1, xc = 0;
01380     double intersept, slope;
01381     check(( intersept = cpl_table_get_double(ordertable, "Intersept", row, NULL),
01382         slope     = cpl_table_get_double(ordertable, "Slope", row, NULL)),
01383         "Could not read line from ordertable");
01384     
01385     /* The left endpoint of the order line */
01386     x0 = 1;
01387     y_0 = uves_round_double(intersept + slope*x0);
01388         
01389     /* However, if... */
01390     if (y_0 < 1)
01391     {
01392         y_0 = 1;
01393         x0 = uves_round_double((y_0 - intersept)/slope); 
01394             /* y = intersept + slope*x */
01395     }
01396 
01397     
01398     /* The right endpoint */
01399     x1 = nx;
01400     y_1 = uves_round_double(intersept + slope*nx);
01401 
01402     /* However, if ... */
01403     if (y_1 > ny)
01404     {
01405         y_1 = ny;
01406         x1 = uves_round_double((y_1 - intersept)/slope);
01407     }
01408     
01409     xc = (x0 + x1)/2;
01410        
01411   cleanup:
01412     return xc;
01413 }
01414 
01415 /*----------------------------------------------------------------------------*/
01427 /*----------------------------------------------------------------------------*/
01428 static int 
01429 get_ycenter(int nx, int ny, cpl_table *ordertable, int row)
01430 {
01431     int xc = 0;
01432     int yc = 0;
01433     check( xc = get_xcenter(nx, ny, ordertable, row), "Could not find x-center of order");
01434     
01435     check( yc = uves_round_double(
01436            cpl_table_get_double(ordertable, "Slope"    , row, NULL)*xc +
01437            cpl_table_get_double(ordertable, "Intersept", row, NULL)
01438            ), "Could not read line from ordertable");
01439     
01440   cleanup:
01441     return yc;
01442 }
01443 
01444 /*----------------------------------------------------------------------------*/
01460 /*----------------------------------------------------------------------------*/
01461 static double
01462 estimate_threshold(const cpl_image *inputimage, const cpl_image *noise, 
01463            cpl_table *ordertable, int row, double relative_threshold)
01464 {
01465     int yupper = 0;
01466     int ylower = 0;
01467     int xc, yc;
01468     int N;
01469     int ny;
01470     double returnvalue = 0;
01471     cpl_stats *stats = NULL;
01472     
01473     passure( inputimage != NULL, " ");
01474     passure( ordertable != NULL, " ");
01475     passure( cpl_table_get_int(ordertable, "Order", row, NULL) == row+1, "%d %d", 
01476          cpl_table_get_int(ordertable, "Order", row, NULL), row);
01477 
01478     check( ny = cpl_image_get_size_y(inputimage), "Could not read input image dimension");
01479     
01480     check( N = cpl_table_get_nrow(ordertable), "Could not read size of ordertable");
01481     assure(N > 1, CPL_ERROR_ILLEGAL_INPUT, 
01482        "Cannot calculate orderspacing with less than 2 (i.e. %d) orders.", N);
01483     check( xc = cpl_table_get_int(ordertable, "Xcenter", row, NULL), 
01484        "Could not read x-center of order #%d", row+1);
01485     check( yc = cpl_table_get_int(ordertable, "Ycenter", row, NULL), 
01486        "Could not find y-center of order #%d", row+1);
01487     
01488     
01489     /* Set yupper and ylower midway between this and the adjacent orders
01490      * The y-location of the surrounding orders must be calculated at the center,
01491      * xc, of the current order
01492      */
01493     if (row < N - 1)
01494     {
01495         double ynext;
01496         check(ynext =
01497           cpl_table_get_double(ordertable, "Slope"    , row + 1, NULL)*xc +
01498           cpl_table_get_double(ordertable, "Intersept", row + 1, NULL),
01499           "Could not read line from ordertable row %d", row + 1);
01500         
01501         yupper = (int)((yc + (uves_round_double(ynext)-1))/2);  
01502         /* Midway between this and the next order */
01503     }
01504 
01505     if (row > 0)
01506     {
01507         double yprev;
01508         check( yprev =
01509            cpl_table_get_double(ordertable, "Slope"    , row - 1, NULL)*xc +
01510            cpl_table_get_double(ordertable, "Intersept", row - 1, NULL),
01511            "Could not read line from ordertable row %d", row - 1);
01512         
01513         ylower = (int)((yc + uves_round_double(yprev)-1)/2);
01514         /* Midway between this and the previous order */
01515     }
01516 
01517     /* We need to manually set yupper for the highest order
01518        and ylower for the lowest order */
01519     if (row == N-1)
01520     {
01521         yupper = yc + (yc - ylower);
01522     }
01523     if (row == 0)
01524     {
01525         ylower = yc - (yupper - yc);
01526     }
01527     yupper = uves_min_int(uves_max_int(yupper, 1), ny);
01528     ylower = uves_min_int(uves_max_int(ylower, 1), ny);
01529 
01530     /* Order lines were originally sorted with respect to intersept. This does not
01531        necessarily mean that their centers are also sorted (if the Hough algorithm
01532        detected wrong slopes (which happens if trying to detect too many lines)).
01533        So check this. */
01534     assure(yupper > ylower, CPL_ERROR_ILLEGAL_INPUT, 
01535        "Initially detected order lines intersept!");
01536 
01537     /* Find max and min pixel values between ylower and yupper, then calculate threshold */
01538     {
01539     double minval = 0;
01540     double maxval = 0;
01541     double noise_level = 0;
01542     
01543     /* Find maximum and minimum pixels along center column */
01544     check( stats = cpl_stats_new_from_image_window(
01545            inputimage,
01546            CPL_STATS_MIN | CPL_STATS_MAX | CPL_STATS_MINPOS,
01547            xc, ylower,   /* Corners of window (FITS convention) (included) */
01548            xc, yupper),
01549            "Could not get statistics on image sub-window (%d,%d)-(%d,%d)", 
01550            xc, ylower, xc, yupper);
01551     
01552     check(( minval = cpl_stats_get_min(stats),
01553         maxval = cpl_stats_get_max(stats)),
01554            "Could not get minimum and maximum pixel values");
01555 
01556     /* Get noise level at the location of 'minval' */
01557     {
01558         int xpos, ypos, pis_rejected;
01559         xpos = cpl_stats_get_min_x(stats);
01560         ypos = cpl_stats_get_min_y(stats);
01561         noise_level = cpl_image_get(noise, xpos, ypos, &pis_rejected);
01562     }
01563     
01564     /* Calculate threshold */
01565     returnvalue = uves_max_double(minval + relative_threshold * (maxval - minval),
01566                  (minval + noise_level) + noise_level);
01567     
01568     uves_msg_debug("Order: %d \tThreshold: %f \tMinimum: %f \tMaximum: %f"
01569                " \tNoise: %f \tWindow: (%d, %d)-(%d, %d)",
01570               row+1, returnvalue, minval, maxval, noise_level, xc, ylower, xc, yupper);
01571     }
01572     
01573   cleanup:
01574     uves_free_stats(&stats);
01575     return returnvalue;
01576 }
01577 
01578 /*----------------------------------------------------------------------------*/
01601 /*----------------------------------------------------------------------------*/
01602 static bool
01603 find_centroid(const cpl_image *inputimage, const cpl_image *noise, 
01604           const cpl_binary *image_bad, 
01605           double threshold, int spacing, int x, double *yguess, double *dY)
01606 {
01607     bool returnvalue = true;
01608     int nx;
01609     int ny;
01610     int y;
01611     double thisvalue = 0;
01612     int pis_rejected;
01613     int ylow = 0;
01614     int yhigh = 0;
01615     cpl_matrix *covariance = NULL;
01616 
01617     passure( inputimage != NULL, " ");
01618 
01619     nx = cpl_image_get_size_x(inputimage);
01620     ny = cpl_image_get_size_y(inputimage);
01621 
01622     passure( 1 <= x && x <= nx, "%d %d", x, nx);
01623 
01624     uves_msg_debug("Order location estimate = (%d, %f)", x, *yguess);
01625     
01626     /* Start at yguess and move to a local max */
01627 
01628     y = uves_round_double(*yguess);
01629     if (y < 1 || y > ny)
01630     {
01631         returnvalue = false;
01632     }
01633     else {
01634     bool cont;          /* continue? */
01635     
01636     do {
01637         cont = false;
01638         thisvalue  = cpl_image_get(inputimage, x, y    , &pis_rejected);
01639         /* Move up? */
01640         if (y < ny) {
01641         double uppervalue = cpl_image_get(inputimage, x, y + 1, &pis_rejected);
01642         if (!pis_rejected && uppervalue > thisvalue)
01643             {
01644             y += 1;
01645             cont = true;
01646             }
01647         }
01648     
01649         /* Move down? */
01650         if (y > 1) {
01651         double lowervalue = cpl_image_get(inputimage, x, y - 1, &pis_rejected);
01652         if (!pis_rejected && lowervalue > thisvalue)
01653             {
01654             y -= 1;
01655             cont = true;
01656             }
01657         }
01658     
01659     } while (cont);
01660     
01661     /* Now 'thisvalue' is the local maximum */
01662     
01663     uves_msg_debug("Local maximum at (%d, %d) (value = %f)\tthreshold = %f", 
01664                x, y, thisvalue, threshold);
01665     
01666     /* Return false if no value above threshold was found */
01667     if (thisvalue < threshold)
01668         {
01669         uves_msg_debug("Order not traced at (%d, %d) (value = %f)\tthreshold = %f",
01670                    x, y, thisvalue, threshold);
01671         returnvalue = false;
01672         }
01673     else
01674         { 
01675                 /* Find and use pixels that are above half max */
01676                 double minvalue;
01677         double sigmaY;   /* Width of peak */
01678 
01679                 double mse, rms, chi_sq;
01680                 double background;
01681                 double norm;
01682         
01683                 /* Threshold is half of max value at this x */
01684                 minvalue = 0.5*thisvalue;
01685             
01686         /* Move to the lowest y above 'minvalue' */
01687         while(y > 1 && cpl_image_get(inputimage, x, y - 1, &pis_rejected) >= minvalue)
01688             {
01689             y--;
01690             }
01691             
01692         assure( cpl_error_get_code() == CPL_ERROR_NONE,
01693             cpl_error_get_code(), "Could not read pixel from input image" );
01694             
01695         /* Remember this place */
01696         ylow = y;
01697             
01698         /* Move to the highest y above 'minvalue' */
01699         while(y < ny && cpl_image_get(inputimage, x, y + 1, &pis_rejected) >= minvalue)
01700             {
01701             y++;
01702             }
01703             
01704         assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(), 
01705             "Could not read pixel from input image" );
01706             
01707         /* Also remember this place */
01708         yhigh = y;
01709             
01710         /* Update the order line's location to centroid of 
01711            strip from ylow to yhigh w.r.t. minvalue*/
01712         {
01713             double sum  = 0;
01714             double sumy = 0;
01715             double sumy2= 0;
01716             for (y = ylow; y <= yhigh; y++)
01717             {
01718                 double flux;
01719                 flux = cpl_image_get(inputimage, x, y, &pis_rejected) - minvalue;
01720                 if (!pis_rejected && flux > 0)
01721                 {
01722                     sum   += flux;
01723                     sumy  += flux * (y - *yguess*0);
01724                     sumy2 += flux * (y - *yguess*0) * (y - *yguess*0);
01725                 }
01726             }
01727             if (sum > 0)
01728             {
01729                 *yguess = *yguess*0 + sumy / sum;
01730                 sigmaY = sqrt( sumy2 / sum - sumy*sumy/(sum*sum) );
01731                 
01732                 if ( sumy2 / sum - sumy*sumy/(sum*sum) < 0 || 
01733                  sigmaY < sqrt(1.0/12) )
01734                 {
01735                     /* If the sum is over one pixel, sigma will be zero 
01736                        (or less than zero because of numerical error), 
01737                        so set sigma to stddev of one pixel = 1/sqrt(12)
01738                        in that case */
01739                     sigmaY = sqrt(1.0/12);
01740                 }
01741                 
01742                 /* Uncertainty, dY, of mean value (yguess) is  sigma/sqrt(N)
01743                    where N is the total count, i.e. area under curve */
01744                 *dY = sigmaY/sqrt(sum);
01745                 
01746             }
01747             else
01748             {
01749                 /* If all pixels were bad, don't update '*yguess' */
01750                 sigmaY = 1.0;
01751                 *dY = .1;
01752                 
01753             }
01754         }
01755             
01756                 /* This is a better method. Get centroid 
01757                    position by making a Gaussian fit. */
01758                 
01759                 /* Use a wide fitting window to get a well defined background level */
01760                 ylow  = uves_max_int(1 , uves_round_double(*yguess - spacing/3));
01761                 yhigh = uves_min_int(ny, uves_round_double(*yguess + spacing/3));
01762                 
01763                 assure( yhigh - ylow >= 1, CPL_ERROR_ILLEGAL_INPUT,
01764                         "Estimated spacing too small: %d pixel(s)", spacing);
01765                 
01766                 /* Fit. Save the result in 'yguess' */
01767                 uves_fit_1d_image(inputimage, noise, 
01768                                   image_bad,
01769                                   false, false, false,
01770                                   ylow, yhigh, x,
01771                                   yguess, &sigmaY, &norm, &background, NULL,
01772                                   &mse, &chi_sq, &covariance,
01773                                   uves_gauss, uves_gauss_derivative, 4);
01774                 
01775                 /* Recover from specific fitting errors */
01776                 if (cpl_error_get_code() == CPL_ERROR_NONE)
01777                     {
01778                         /* Variance is guaranteed to be positive */
01779                         *dY = sqrt(cpl_matrix_get(covariance, 0, 0));
01780                     }
01781                 else if (cpl_error_get_code() == CPL_ERROR_CONTINUE)
01782                     {
01783                         /* Fitting failed */
01784                         uves_error_reset();
01785                         uves_msg_debug("Fitting failed at (x,y) = (%d, %e), "
01786                                        "using centroid", x, *yguess);
01787                         *dY = sigmaY / sqrt(norm);
01788                     }
01789                 else if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
01790                     {
01791                         uves_error_reset();
01792                         
01793                         /* Fitting succeeded but covariance computation failed */
01794                         uves_msg_debug("Covariance matrix computation failed");
01795                         *dY = sigmaY / sqrt(norm);
01796                     }
01797                 
01798                 assure(cpl_error_get_code() == CPL_ERROR_NONE,
01799                        cpl_error_get_code(), "Gaussian fitting failed");
01800                 
01801                 rms = sqrt(mse);
01802                 
01803                 uves_msg_debug("dy = %f   sigma/sqrt(N) = %f", *dY, sigmaY/(norm));
01804                 
01805                 /* If the peak is definitely there or definitely not there,
01806                    set the returnvalue appropriately */
01807                 if ( norm > 10 * rms)
01808                     {
01809                         returnvalue = true;
01810                     }
01811                 if ( norm < 2 * rms)
01812                     {
01813                         returnvalue = false;
01814                     }
01815                 
01816             } /* signal was above threshold at this x */
01817     
01818     }/* If yguess was inside image */
01819 
01820   cleanup:
01821     cpl_matrix_delete(covariance);
01822     return returnvalue;
01823 }

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