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

Generated on 8 Mar 2011 for UVES Pipeline Reference Manual by  doxygen 1.6.1