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

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