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

Generated on Thu Nov 15 14:32:29 2007 for UVES Pipeline Reference Manual by  doxygen 1.5.1