uves_reduce.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: 2008/03/03 16:35:55 $
00023  * $Revision: 1.95 $
00024  * $Name: uves-4_2_2 $
00025  * $Log: uves_reduce.c,v $
00026  * Revision 1.95  2008/03/03 16:35:55  amodigli
00027  * removed clean_traps now defined elsewhere
00028  *
00029  * Revision 1.94  2008/02/21 07:51:05  amodigli
00030  * added method noappend
00031  *
00032  * Revision 1.93  2008/02/15 11:51:36  amodigli
00033  * added parameter clean_traps
00034  *
00035  * Revision 1.92  2007/12/03 16:30:46  amodigli
00036  * fixed typo in uves_propertylist_copy_property_regexp usage
00037  *
00038  * Revision 1.91  2007/12/03 08:01:34  amodigli
00039  * added HIERARCH keys to 'debug' product
00040  *
00041  * Revision 1.90  2007/11/14 09:32:47  amodigli
00042  * fixed typo reading header info to get cdelt1
00043  *
00044  * Revision 1.89  2007/06/22 09:28:59  jmlarsen
00045  * Changed interface of uves_save_image
00046  *
00047  * Revision 1.88  2007/06/11 07:46:25  amodigli
00048  * removed lower thresholding to 0 in sky subtraction
00049  *
00050  * Revision 1.87  2007/06/06 08:17:33  amodigli
00051  * replace tab with 4 spaces
00052  *
00053  * Revision 1.86  2007/05/25 11:50:32  jmlarsen
00054  * Re-added ORDER_TRACE_TABLE
00055  *
00056  * Revision 1.85  2007/05/22 11:43:10  jmlarsen
00057  * Removed MIDAS flag for good
00058  *
00059  * Revision 1.84  2007/05/09 06:10:07  jmlarsen
00060  * Fixed recently introduced bug in 2d extraction
00061  *
00062  * Revision 1.83  2007/05/08 11:29:18  jmlarsen
00063  * Disable line tilt correction for 2d extraction
00064  *
00065  * Revision 1.82  2007/05/07 10:21:34  jmlarsen
00066  * Factored out duplication of functionality also in wavecal
00067  *
00068  * Revision 1.81  2007/05/03 15:26:12  jmlarsen
00069  * Implemented line tilt correction
00070  *
00071  * Revision 1.80  2007/05/02 16:48:02  jmlarsen
00072  * Allow setting slit offset in optimal extraction
00073  *
00074  * Revision 1.79  2007/05/02 13:49:03  jmlarsen
00075  * Added debug flag to uves_extract()
00076  *
00077  * Revision 1.78  2007/04/24 12:50:29  jmlarsen
00078  * Replaced cpl_propertylist -> uves_propertylist which is much faster
00079  *
00080  * Revision 1.77  2007/03/28 11:39:33  jmlarsen
00081  * Removed MIDAS flag from uves_define_noise
00082  *
00083  * Revision 1.76  2007/03/28 11:11:39  jmlarsen
00084  * Bugfix in computation of WCALIB_SCIENCE_ product which should not be flat-fielded
00085  *
00086  * Revision 1.75  2007/03/05 10:18:24  jmlarsen
00087  * Write 2d extraion slit length
00088  *
00089  * Revision 1.74  2007/02/08 11:47:09  jmlarsen
00090  * Fixed problem with infinities by not using CPL_PIXEL_MAXVAL
00091  *
00092  * Revision 1.73  2007/01/29 12:11:54  jmlarsen
00093  * Calculate extraction slit length
00094  *
00095  * Revision 1.72  2006/11/16 14:12:21  jmlarsen
00096  * Changed undefined trace number from 0 to -1, to support zero as an actual trace number
00097  *
00098  * Revision 1.71  2006/11/15 15:02:15  jmlarsen
00099  * Implemented const safe workarounds for CPL functions
00100  *
00101  * Revision 1.69  2006/11/15 14:04:08  jmlarsen
00102  * Removed non-const version of parameterlist_get_first/last/next which is already 
00103  * in CPL, added const-safe wrapper, unwrapper and deallocator functions
00104  *
00105  * Revision 1.68  2006/11/08 08:32:46  jmlarsen
00106  * Changed message
00107  *
00108  * Revision 1.67  2006/11/06 15:19:41  jmlarsen
00109  * Removed unused include directives
00110  *
00111  * Revision 1.66  2006/10/12 10:48:27  jmlarsen
00112  * Reduced max line length
00113  *
00114  * Revision 1.65  2006/10/04 10:57:59  jmlarsen
00115  * Support the case where sky-windows have only bad pixels
00116  *
00117  * Revision 1.64  2006/10/02 08:43:46  jmlarsen
00118  * Fixed bug hidden with CPL2 but exposed with CPL3 
00119  * (occurring when a sky window has only bad pixels)
00120  *
00121  * Revision 1.63  2006/09/27 13:17:09  jmlarsen
00122  * Added parameter to enable/disable sky subtraction
00123  *
00124  * Revision 1.62  2006/09/20 12:53:57  jmlarsen
00125  * Replaced stringcat functions with uves_sprintf()
00126  *
00127  * Revision 1.61  2006/09/19 07:15:35  jmlarsen
00128  * Added chip to argument list of uves_extract()
00129  *
00130  * Revision 1.60  2006/08/17 14:40:06  jmlarsen
00131  * Added missing documentation
00132  *
00133  * Revision 1.59  2006/08/17 14:11:25  jmlarsen
00134  * Use assure_mem macro to check for memory allocation failure
00135  *
00136  * Revision 1.58  2006/08/17 13:56:53  jmlarsen
00137  * Reduced max line length
00138  *
00139  * Revision 1.57  2006/08/11 11:27:57  jmlarsen
00140  * Added variable initialization
00141  *
00142  * Revision 1.56  2006/08/07 11:35:35  jmlarsen
00143  * Disabled parameter environment variable mode
00144  *
00145  * Revision 1.55  2006/07/14 12:31:03  jmlarsen
00146  * Reduced max line length
00147  *
00148  * Revision 1.54  2006/07/03 13:17:52  jmlarsen
00149  * Added message
00150  *
00151  * Revision 1.53  2006/06/19 09:31:08  jmlarsen
00152  * Bugfix: The sky spectrum (intermediate product saved to disk) 
00153  *   contained the extracted object. Now it is the extracted sky
00154  *
00155  * Revision 1.52  2006/06/13 12:01:43  jmlarsen
00156  * Disallow slitlength less than 1 pixel
00157  *
00158  * Revision 1.51  2006/06/08 11:01:50  amodigli
00159  * fixed some warnings
00160  *
00161  * Revision 1.50  2006/05/16 12:13:07  amodigli
00162  * added QC log
00163  *
00164  * Revision 1.49  2006/05/12 15:09:31  jmlarsen
00165  * Moved definition of noise image to before back.sub.
00166  *
00167  * Revision 1.48  2006/04/24 09:24:35  jmlarsen
00168  * Removed unused profile variable
00169  *
00170  * Revision 1.47  2006/04/10 12:38:43  jmlarsen
00171  * Minor layout change
00172  *
00173  * Revision 1.46  2006/04/06 08:48:35  jmlarsen
00174  * Define flat-field noise only when needed
00175  *
00176  * Revision 1.45  2006/03/24 14:46:39  jmlarsen
00177  * Doc. bugfix
00178  *
00179  * Revision 1.44  2006/03/24 13:57:47  jmlarsen
00180  * Changed meaning of VARIANCE_SCIENCE to match MIDAS
00181  *
00182  * Revision 1.43  2006/03/06 09:22:43  jmlarsen
00183  * Added support for reading MIDAS line tables with MIDAS tags
00184  *
00185  * Revision 1.42  2006/03/03 13:54:11  jmlarsen
00186  * Changed syntax of check macro
00187  *
00188  * Revision 1.41  2006/02/28 09:15:23  jmlarsen
00189  * Minor update
00190  *
00191  * Revision 1.40  2006/02/21 14:26:08  jmlarsen
00192  * Implemented correction of ripples caused by different 
00193  *   object/flat-field blaze functions
00194  *
00195  * Revision 1.39  2006/02/15 13:19:15  jmlarsen
00196  * Reduced source code max. line length
00197  *
00198  * Revision 1.38  2006/01/31 08:24:29  jmlarsen
00199  * Wrapper for cpl_image_get_bpm
00200  *
00201  * Revision 1.37  2005/12/20 16:10:32  jmlarsen
00202  * Added some documentation
00203  *
00204  * Revision 1.36  2005/12/19 16:17:56  jmlarsen
00205  * Replaced bool -> int
00206  *
00207  */
00208 
00209 #ifdef HAVE_CONFIG_H
00210 #  include <config.h>
00211 #endif
00212 
00213 /*----------------------------------------------------------------------------*/
00219 /*----------------------------------------------------------------------------*/
00223 /*-----------------------------------------------------------------------------
00224                                 Includes
00225  -----------------------------------------------------------------------------*/
00226 
00227 #include "uves_reduce.h"
00228 
00229 #include <uves.h>
00230 #include <uves_extract.h>
00231 #include <uves_backsub.h>
00232 #include <uves_parameters.h>
00233 #include <uves_flatfield.h>
00234 #include <uves_rebin.h>
00235 #include <uves_merge.h>
00236 #include <uves_utils_cpl.h>
00237 #include <uves_utils_wrappers.h>
00238 #include <uves_pfits.h>
00239 #include <uves_dfs.h>
00240 #include <uves_dump.h>
00241 #include <uves_plot.h>
00242 #include <uves_error.h>
00243 
00244 #include <cpl.h>
00245 
00246 #include <float.h>
00247 
00248 /*-----------------------------------------------------------------------------
00249                             Functions prototypes
00250   -----------------------------------------------------------------------------*/
00251 
00252 static cpl_error_code
00253 extract_ff_rebin_merge(cpl_image *back_subbed, 
00254                        cpl_image *backsubbed_noise,
00255                const uves_propertylist *backsubbed_header,
00256                const cpl_image *master_flat, 
00257                        cpl_image *mflat_noise,
00258                const cpl_table *ordertable, 
00259                        const polynomial *order_locations,
00260                const cpl_table *linetable, 
00261                        const uves_propertylist *linetable_header[3],
00262                        const polynomial *dispersion_relation[3],
00263                double slit_length, 
00264                        double slit_offset, 
00265                        int window,
00266                enum uves_chip chip,
00267                bool blaze_correct,
00268                        bool tilt_corr,
00269                        bool DEBUG,
00270                const cpl_parameterlist *parameters, 
00271                        const char *context,
00272                flatfielding_method ff_method,
00273                extract_method ee_method,
00274                merge_method m_method,
00275                /* Output */
00276                cpl_image **x, 
00277                        uves_propertylist **x_header,
00278                cpl_image **fx,
00279                cpl_table **cosmic_mask,
00280                cpl_image **flatfielded_variance,
00281                uves_propertylist **flatfielded_variance_header,
00282                cpl_image **resampled_spectrum,
00283                cpl_image **resampled_mf,
00284                cpl_image **merged_sky,
00285                cpl_image **rebinned_spectrum, 
00286                        cpl_image **rebinned_noise, 
00287                        uves_propertylist **rebinned_header,
00288                cpl_image **merged_spectrum, 
00289                        cpl_image **merged_noise, 
00290                        uves_propertylist **merged_header,
00291                        cpl_table** info_tbl,
00292                        cpl_table **order_trace);
00293 
00294 static cpl_image *
00295 subtract_sky(cpl_image *rebinned_obj, 
00296              cpl_image *rebinned_obj_noise, 
00297              uves_propertylist *rebinned_obj_header,
00298          const cpl_image *rebinned_sky1, 
00299              const cpl_image *rebinned_sky1_noise, 
00300              const uves_propertylist *rebinned_sky1_header,
00301          const cpl_image *rebinned_sky2, 
00302              const cpl_image *rebinned_sky2_noise, 
00303              const uves_propertylist *rebinned_sky2_header,
00304          cpl_image **merged_obj, 
00305              cpl_image **merged_obj_noise, 
00306              uves_propertylist *merged_obj_header,
00307          const cpl_image *merged_sky1, 
00308              const cpl_image *merged_sky1_noise, 
00309              const uves_propertylist *merged_sky1_header,
00310          const cpl_image *merged_sky2, 
00311              const cpl_image *merged_sky2_noise, 
00312              const uves_propertylist *merged_sky2_header,
00313          double obj_slit, 
00314              double sky1_slit, 
00315              double sky2_slit);
00316 
00317 
00318 static cpl_image *
00319 subtract_sky_row(cpl_image *obj, 
00320                  cpl_image *obj_noise, 
00321                  double obj_start, 
00322                  double obj_end, 
00323                  double obj_slit,
00324          const cpl_image *sky1, 
00325                  const cpl_image *sky1_noise, 
00326                  double sky1_start, 
00327                  double sky1_end, 
00328                  double sky1_slit,
00329          const cpl_image *sky2, 
00330                  const cpl_image *sky2_noise, 
00331                  double sky2_start, 
00332                  double sky2_end, 
00333                  double sky2_slit,
00334          int row, 
00335                  double wavestep, 
00336                  double *common_start, 
00337                  double *common_end);
00338 
00339 static double get_offset(const cpl_image *back_subbed, 
00340              const cpl_table *ordertable, 
00341                          const polynomial *order_locations,
00342              double search_range, 
00343                          int nsamples, 
00344                          double *doffset);
00345 
00346 static cpl_image *
00347 uves_get_blaze_ratio(const cpl_image *spectrum,
00348              const cpl_image *spectrum_noise);
00349 
00350 /*-----------------------------------------------------------------------------
00351                               Implementation
00352   -----------------------------------------------------------------------------*/
00353 
00354 
00355 
00356 /*----------------------------------------------------------------------------*/
00363 /*----------------------------------------------------------------------------*/
00364 
00365 cpl_parameterlist *
00366 uves_reduce_define_parameters(void)
00367 {
00368     const char *name = "";
00369     char *full_name = NULL;
00370     cpl_parameterlist *parameters = NULL;
00371     cpl_parameter *p = NULL;
00372 
00373     parameters = cpl_parameterlist_new();
00374     
00375     /**************
00376      *  Backsub   *
00377      **************/
00378     if (cpl_error_get_code() == CPL_ERROR_NONE)
00379     {
00380         uves_propagate_parameters_step(UVES_BACKSUB_ID, parameters, 
00381                        UVES_REDUCE_ID, NULL);
00382     }
00383     
00384     
00385     /*****************
00386      *  Extraction   *
00387      *****************/
00388     if (cpl_error_get_code() == CPL_ERROR_NONE)
00389     {
00390         uves_propagate_parameters_step(UVES_EXTRACT_ID, parameters, 
00391                        UVES_REDUCE_ID, NULL);
00392     }
00393 
00394 
00395     /******************
00396      *  Slit geometry *
00397      ******************/
00398     if (cpl_error_get_code() == CPL_ERROR_NONE)
00399     {
00400         name = "slitlength";
00401         full_name = uves_sprintf("%s.%s", UVES_REDUCE_ID, name);
00402         
00403         uves_parameter_new_range(p, full_name,
00404                     CPL_TYPE_DOUBLE,
00405                     "Extraction slit length (in pixels). "
00406                     "If negative, the value "
00407                     "inferred from the raw frame header is used",
00408                     UVES_REDUCE_ID,
00409                     -1.0,
00410                     -2.0, DBL_MAX);
00411         
00412         cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00413         cpl_parameterlist_append(parameters, p);
00414         cpl_free(full_name);
00415     }
00416 
00417     if (cpl_error_get_code() == CPL_ERROR_NONE)
00418     {
00419         name = "skysub";
00420         full_name = uves_sprintf("%s.%s", UVES_REDUCE_ID, name);
00421         
00422         uves_parameter_new_value(p, full_name,
00423                      CPL_TYPE_BOOL,
00424                      "Do sky-subtraction (only applicable to linear "
00425                      "and average extractions)?",
00426                      UVES_REDUCE_ID,
00427                      true);
00428         
00429         cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00430         cpl_parameterlist_append(parameters, p);
00431         cpl_free(full_name);
00432     }
00433 
00434     if (cpl_error_get_code() == CPL_ERROR_NONE)
00435     {
00436         name = "objoffset";
00437         full_name = uves_sprintf("%s.%s", UVES_REDUCE_ID, name);
00438         
00439         uves_parameter_new_value(p, full_name,
00440                      CPL_TYPE_DOUBLE,
00441                      "Offset (in pixels) of extraction slit "
00442                                      "with respect to center of order. "
00443                                      "This parameter applies to linear/average/"
00444                                      "optimal extraction. "
00445                                      "For linear/average extraction, if the related "
00446                                      "parameter objslit is negative, the offset is "
00447                      "automatically determined by measuring the "
00448                      "actual object position. ",
00449                      UVES_REDUCE_ID,
00450                      0.0);
00451         
00452         cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00453         cpl_parameterlist_append(parameters, p);
00454         cpl_free(full_name);
00455     }
00456 
00457     if (cpl_error_get_code() == CPL_ERROR_NONE)
00458         {
00459             name = "objslit";
00460             full_name = uves_sprintf("%s.%s", UVES_REDUCE_ID, name);
00461             
00462             uves_parameter_new_range(p, full_name,
00463                                      CPL_TYPE_DOUBLE,
00464                                      "Object window size (in pixels). This must "
00465                                      "be less than the total slit length. If "
00466                                      "negative, the default value (half of full "
00467                                      "slit length) is used. The upper and lower "
00468                                      "sky windows are defined as the part of the "
00469                                      "full slit (if any) outside the object "
00470                                      "window. The center of the object window "
00471                                      "is determined by the offset parameter. "
00472                                      "This parameter does not apply to optimal "
00473                                      "extraction.",
00474                                      UVES_REDUCE_ID,
00475                                      -1.0,
00476                                      -2.0, DBL_MAX);
00477         
00478         cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00479         cpl_parameterlist_append(parameters, p);
00480         cpl_free(full_name);
00481     }
00482 
00483     if (cpl_error_get_code() == CPL_ERROR_NONE)
00484     {
00485         name = "tiltcorr";
00486         full_name = uves_sprintf("%s.%s", UVES_REDUCE_ID, name);
00487         
00488         uves_parameter_new_value(p, full_name,
00489                      CPL_TYPE_BOOL,
00490                      "If enabled (recommended), the provided "
00491                                      "dispersion solutions "
00492                                      "obtained at different slit positions are "
00493                                      "interpolated linearly at the actually "
00494                                      "measured position of the object/sky. "
00495                                      "Line tilt correction is currently not supported "
00496                                      "for 2d extraction, in which case the "
00497                                      "dispersion solution obtained at the middle of "
00498                                      "the slit is always used.",
00499                      UVES_REDUCE_ID,
00500                      true);
00501         
00502         cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00503         cpl_parameterlist_append(parameters, p);
00504         cpl_free(full_name);
00505     }
00506 
00507 
00508 
00509     /*****************
00510      *  Flatfielding *
00511      *****************/
00512 
00513     if (cpl_error_get_code() == CPL_ERROR_NONE)
00514     {
00515         name = "ffmethod";
00516         full_name = uves_sprintf("%s.%s", UVES_REDUCE_ID, name);
00517         
00518         uves_parameter_new_enum(p, full_name,
00519                     CPL_TYPE_STRING,
00520                     "Flat-fielding method. If set to 'pixel', "
00521                     "flat-fielding is done in pixel-pixel space "
00522                     "(before extraction); if set to 'extract', "
00523                     "flat-fielding is performed in pixel-order "
00524                     "space (i.e. after extraction). If set to "
00525                     "'no', no flat-field correction is done",
00526                     UVES_REDUCE_ID,
00527                     "extract",    /* 'Pixel' method is usually preferred,
00528                              but do like UVES/MIDAS */
00529                     3, 
00530                     "pixel", "extract", "no");
00531         
00532         cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00533         cpl_parameterlist_append(parameters, p);
00534         cpl_free(full_name);
00535     }
00536 
00537     /*****************
00538      *  Blaze corr.  *
00539      *****************/
00540 
00541     if (cpl_error_get_code() == CPL_ERROR_NONE)
00542     {
00543         name = "blazecorr";
00544         full_name = uves_sprintf("%s.%s", UVES_REDUCE_ID, name);
00545         
00546         uves_parameter_new_value(p, full_name,
00547                     CPL_TYPE_BOOL,
00548                     "(highly experimental, recommended=false) "
00549                     "Apply a correction for the different shapes "
00550                     "of flat-field and science blaze functions? "
00551                     "For this to be possible, flat-fielding method "
00552                     "must be different from 'no'.",
00553                     UVES_REDUCE_ID,
00554                     false);
00555         
00556         cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00557         cpl_parameterlist_append(parameters, p);
00558         cpl_free(full_name);
00559     }
00560 
00561     /*****************
00562      *  Rebinning    *
00563      *****************/
00564     if (cpl_error_get_code() == CPL_ERROR_NONE)
00565     {
00566         uves_propagate_parameters_step(UVES_REBIN_ID, parameters, 
00567                        UVES_REDUCE_ID, NULL);
00568     }
00569     
00570 
00571     /*****************
00572      *   Merging     *
00573      *****************/
00574     if (cpl_error_get_code() == CPL_ERROR_NONE)
00575     {
00576         name = "merge";
00577         full_name = uves_sprintf("%s.%s", UVES_REDUCE_ID, name);
00578         
00579         uves_parameter_new_enum(p, full_name,
00580                        CPL_TYPE_STRING,
00581                        "Order merging method. If 'optimal', the "
00582                        "flux in the overlapping region is set "
00583                        "to the (optimally computed, using the "
00584                        "uncertainties) average of single order "
00585                        "spectra. If 'sum', the flux in the "
00586                        "overlapping region is computed as the "
00587                        "sum of the single order spectra. If 'noappend' "
00588                        "the spectrum is simply rebinned but not merged."
00589                        "If flat-fielding is done, method 'optimal' "
00590                        "is recommended, otherwise 'sum'.",
00591                        UVES_REDUCE_ID,
00592                        "optimal",
00593                        3, 
00594                 "optimal", "sum", "noappend");
00595         
00596         cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00597         cpl_parameterlist_append(parameters, p);
00598         cpl_free(full_name);
00599     }
00600 
00601     if (cpl_error_get_code() != CPL_ERROR_NONE)
00602     {
00603         cpl_msg_error(__func__, "Creation of reduction parameters failed: '%s'", 
00604               cpl_error_get_where());
00605         cpl_parameterlist_delete(parameters);
00606         return NULL;
00607     }
00608 
00609     
00610     return parameters;
00611 }
00612 
00613 /*----------------------------------------------------------------------------*/
00703 /*----------------------------------------------------------------------------*/
00704 
00705 cpl_error_code uves_reduce(const cpl_image *raw_image, 
00706                            const uves_propertylist *raw_header, 
00707                            const uves_propertylist *rotated_header,
00708                const cpl_image *master_bias,
00709                const cpl_image *master_dark, 
00710                            const uves_propertylist *mdark_header, 
00711                const cpl_image *master_flat, 
00712                            const uves_propertylist *mflat_header,
00713                const cpl_table *ordertable, 
00714                            const polynomial *order_locations,
00715                const cpl_table *linetable[3], 
00716                            const uves_propertylist *linetable_header[3], 
00717                            const polynomial *dispersion_relation[3],
00718                enum uves_chip chip,
00719                /* General */
00720                bool   DEBUG,
00721                /* Backsub */
00722                /* Flat fielding */
00723                /* Extraction */
00724                /* Rebinning  */
00725                const cpl_parameterlist *parameters, 
00726                            const char *context,
00727                /* Output */
00728                cpl_image **x, uves_propertylist **x_header,
00729                cpl_image **fx,
00730                cpl_table **cosmic_mask,
00731                cpl_image **background,
00732                cpl_image **flatfielded_variance,
00733                uves_propertylist **flatfielded_variance_header,
00734                cpl_image **resampled_spectrum,
00735                cpl_image **resampled_mf,
00736                cpl_image **merged_sky,
00737                /* Before sky-subtraction */
00738                cpl_image **rebinned_spectrum, 
00739                            cpl_image **rebinned_noise, 
00740                            uves_propertylist **rebinned_header,
00741                cpl_image **merged_spectrum  , 
00742                            cpl_image **merged_noise, 
00743                            uves_propertylist **merged_header,
00744                /* After sky-subtraction */
00745                cpl_image **reduced_rebinned_spectrum, 
00746                            cpl_image **reduced_rebinned_noise,
00747                cpl_image **reduced_spectrum         , 
00748                            cpl_image **reduced_noise, 
00749                            cpl_table **info_tbl,
00750                double *extraction_slit,
00751                            cpl_table **order_trace)
00752 {
00753     /* Recipe parameters */
00754     flatfielding_method ff_method;
00755     merge_method m_method;
00756     extract_method ex_method;
00757     bool blaze_corr;
00758     bool sky_sub;
00759     bool tilt_corr;
00760     double full_slit;
00761     double obj_slit;
00762     double obj_offset;
00763 
00764     cpl_image *back_subbed         = NULL;         /* Image before extraction */
00765     cpl_image *backsubbed_noise    = NULL;
00766 
00767     cpl_image *mflat_noise         = NULL;         /* Master flat noise */
00768 
00769     cpl_image *simple_extracted    = NULL;         /* Needed only for blaze-correction */
00770     cpl_image *simple_extracted_mf = NULL; 
00771 
00772     cpl_image *sky_lo                = NULL;       /* Merged sky spectra */
00773     cpl_image *sky_lo_noise          = NULL;
00774     cpl_image *sky_hi                = NULL;
00775     cpl_image *sky_hi_noise          = NULL;
00776     uves_propertylist *sky_lo_header  = NULL;
00777     uves_propertylist *sky_hi_header  = NULL;
00778 
00779     cpl_image *sky_lo_rebinned       = NULL;       /* Rebinned sky spectra */
00780     cpl_image *sky_lo_rebinned_noise = NULL;
00781     cpl_image *sky_hi_rebinned       = NULL;
00782     cpl_image *sky_hi_rebinned_noise = NULL;
00783     uves_propertylist *sky_lo_rebinned_header = NULL;
00784     uves_propertylist *sky_hi_rebinned_header = NULL;
00785 
00786     char *subcontext                 = NULL;
00787     double header_full_slit;                       /* Slit length in pixels
00788                               from FITS header */
00789     char *ex_method_string           = NULL;
00790     
00791     /* Check, initialize input */
00792     passure( background                 != NULL, " "); *background                = NULL;
00793     /* resampled_spectrum, resampled_mf may be NULL */
00794     passure( rebinned_spectrum          != NULL, " "); *rebinned_spectrum         = NULL;
00795     passure( rebinned_noise             != NULL, " "); *rebinned_noise            = NULL;
00796     passure( rebinned_header            != NULL, " "); *rebinned_header           = NULL;
00797     passure( merged_spectrum            != NULL, " "); *merged_spectrum           = NULL;
00798     passure( merged_sky                 != NULL, " "); *merged_sky                = NULL;
00799     passure( merged_noise               != NULL, " "); *merged_noise              = NULL;
00800     passure( merged_header              != NULL, " "); *merged_header             = NULL;
00801     passure( reduced_rebinned_spectrum  != NULL, " "); *reduced_rebinned_spectrum = NULL;
00802     passure( reduced_rebinned_noise     != NULL, " "); *reduced_rebinned_noise    = NULL;
00803     passure( reduced_spectrum           != NULL, " "); *reduced_spectrum          = NULL;
00804     passure( reduced_noise              != NULL, " "); *reduced_noise             = NULL;
00805 
00806     passure( (flatfielded_variance == NULL) == (flatfielded_variance_header == NULL),
00807          "%d %d", flatfielded_variance == NULL, flatfielded_variance_header == NULL);
00808 
00809     assure_nomsg( extraction_slit != NULL, CPL_ERROR_NULL_INPUT );
00810 
00811     /* Get flat-fielding/extract method (recipe parameters) 
00812        These parameters determine the overall reduction strategy. */
00813     {
00814     check( ff_method = uves_get_flatfield_method(parameters, context, UVES_REDUCE_ID),
00815            "Could not read flat-fielding method");
00816     
00817     assure( ff_method == FF_NO || master_flat != NULL, CPL_ERROR_NULL_INPUT,
00818         "Flat-fielding requested, but no flat field provided");
00819     
00820     /* Read extract method from  <context>.<uves_reduce>.<extract>.method  */
00821     check( ex_method = uves_get_extract_method(parameters, context,
00822                            UVES_REDUCE_ID "." UVES_EXTRACT_ID),
00823            "Could not get extraction method");
00824            
00825     assure( ex_method != EXTRACT_WEIGHTED, CPL_ERROR_ILLEGAL_INPUT, 
00826         "Weighted extraction is used only internally, "
00827         "as a part of optimal extraction");
00828 
00829     check( m_method = uves_get_merge_method(parameters, context, UVES_REDUCE_ID),
00830            "Could not get merging method");
00831 
00832     check( uves_get_parameter(parameters, context, UVES_REDUCE_ID,
00833                   "blazecorr", CPL_TYPE_BOOL, &blaze_corr),
00834            "Could not read parameter");
00835     
00836     check( uves_get_parameter(parameters, context, UVES_REDUCE_ID,
00837                   "skysub", CPL_TYPE_BOOL, &sky_sub),
00838            "Could not read parameter");
00839     
00840     check( uves_get_parameter(parameters, context, UVES_REDUCE_ID,
00841                   "tiltcorr", CPL_TYPE_BOOL, &tilt_corr),
00842            "Could not read parameter");
00843     
00844     assure( !blaze_corr || ff_method != FF_NO, CPL_ERROR_INCOMPATIBLE_INPUT,
00845         "Sorry, cannot apply blaze function "
00846         "correction when no flatfielding is done");
00847 
00848     if (blaze_corr && ex_method == EXTRACT_2D)
00849         {
00850         uves_msg_warning("There will be no blaze function correction "
00851                  "for 2d extraction");
00852         }
00853 
00854     if (ff_method == FF_NO && m_method == MERGE_OPTIMAL)
00855         {
00856         uves_msg_warning("No flat-fielding done, but merge method = optimal. "
00857                  "Is this really what you want?");
00858         }
00859     if (ff_method != FF_NO && m_method == MERGE_SUM)
00860         {
00861         uves_msg_warning("Flat-fielding will be done, but merge method = sum. "
00862                  "Is this really what you want?");
00863         }
00864 
00865     check( uves_get_parameter(parameters, context, UVES_REDUCE_ID, "slitlength", 
00866                   CPL_TYPE_DOUBLE, &full_slit), "Could not read parameter");
00867 
00868         check( uves_get_parameter(parameters, context, UVES_REDUCE_ID, "objoffset",
00869                                   CPL_TYPE_DOUBLE, &obj_offset), 
00870                "Could not read parameter");
00871         check( uves_get_parameter(parameters, context, UVES_REDUCE_ID, "objslit",
00872                                   CPL_TYPE_DOUBLE, &obj_slit), 
00873                "Could not read parameter");
00874     }
00875 
00876     /* Append '.uves_reduce' to context */
00877     subcontext = uves_sprintf("%s.%s", context, UVES_REDUCE_ID);
00878 
00879     /* Subtract bias */
00880     check( back_subbed = cpl_image_duplicate(raw_image),
00881        "Error copying raw image");
00882 
00883     if (master_bias != NULL)
00884     {
00885         uves_msg("Subtracting master bias");
00886         check( uves_subtract_bias(back_subbed, master_bias),
00887            "Error subtracting master bias");
00888     }
00889     else
00890     {
00891         uves_msg("Skipping bias subtraction");
00892     }
00893     
00894     /* Subtract dark if available */
00895     if (master_dark != NULL)
00896     {
00897         uves_msg("Subtracting master dark");
00898         check( uves_subtract_dark(back_subbed, raw_header,
00899                       master_dark, mdark_header),
00900            "Error subtracting master dark");
00901     }
00902     else
00903     {
00904         uves_msg("Skipping dark subtraction");
00905     }
00906     
00907     if (DEBUG) check( uves_save_image_local("Bias/dark subtracted raw image", "pre",
00908                         back_subbed, chip, -1, -1, rotated_header, true), 
00909               "Error saving image");
00910 
00911     uves_msg("Creating noise image");
00912 
00913     /* Define/initialize input image noise (r.o.n. and photonic) */
00914     check( backsubbed_noise = uves_define_noise(back_subbed, raw_header, 
00915                         1, chip),
00916        "Could not calculate noise image");    
00917 
00918     /* Save noise image */
00919     if (DEBUG) check( uves_save_image_local("Background subtracted raw image noise",
00920                         "errb", backsubbed_noise,
00921                         chip, -1, -1, rotated_header, true),
00922               "Error saving image"); 
00923 
00924     /* Subtract background  */
00925     uves_msg("Subtracting inter-order background");
00926     
00927     check( uves_backsub_spline(back_subbed, raw_header,
00928                    ordertable, order_locations,
00929                    parameters, subcontext,
00930                    chip,
00931                    false,           /* Use flat-field parameters? */
00932                    background),
00933        "Error subtracting background");
00934 
00935     /* Save bias, dark, background subtracted frame */
00936     if (DEBUG) check( uves_save_image_local("Background subtracted raw image", "b",
00937                         back_subbed, chip, -1, -1, rotated_header, true),
00938               "Error saving image");
00939 
00940     /* 
00941      * Initialize flat-field noise (if necessary)
00942      */
00943     if (ff_method == FF_NO)
00944     {
00945         uves_msg("Skipping flat-field correction");
00946     }
00947 
00948     if (ff_method != FF_NO || resampled_mf != NULL)
00949     {
00950         int mflat_datancom;
00951 
00952         /* Save master flat image */
00953         if (DEBUG)
00954         {
00955             check( uves_save_image_local("Master flat image", "mf", 
00956                          master_flat,
00957                          chip, -1, -1, rotated_header, true), 
00958                "Error saving master flat image");
00959         }
00960         
00961         
00962         /* Define master flat noise */
00963         check( mflat_datancom  = uves_pfits_get_datancom(mflat_header),
00964            "Error reading number of raw flat field frames "
00965            "used for master flat image");
00966 
00967         uves_msg("Creating master flat noise image");
00968         
00969         check( mflat_noise = uves_define_noise(master_flat, mflat_header, 
00970                            mflat_datancom, chip),
00971            "Could not define master flat noise");
00972 
00973 
00974         /* Save master flat noise image */
00975         if (DEBUG)
00976         {
00977             check( uves_save_image_local("Master flat noise", "errmf", mflat_noise,
00978                          chip, -1, -1, rotated_header, true), 
00979                "Error saving master flat image");
00980         }
00981     }
00982 
00983     /*
00984      * Get full slit length 
00985      */
00986     check( header_full_slit = uves_pfits_get_slitlength_pixels(raw_header, chip),
00987        "Could not read slit length");
00988     
00989     /* If user didn't specify slit length, use header value */
00990     if (full_slit < 0)
00991     {
00992         /* Avoid pixels at the edge of the slit
00993          *  which are likely to be noisy
00994          */
00995         full_slit = uves_max_double(1.0, header_full_slit - 2);
00996     }
00997     else
00998     {
00999         /* Warn if user specified value is larger than header value */
01000         if (full_slit > header_full_slit)
01001         {
01002             uves_msg_warning("Specified full slit length (%e pixels) "
01003                      "is larger than input header slit "
01004                      "length (%e pixels)",
01005                      full_slit, header_full_slit);
01006         }
01007     }
01008     
01009     uves_msg("Slit length = %.2f pixels", full_slit);
01010     *extraction_slit = full_slit;
01011 
01012     if (ff_method == FF_PIXEL)
01013     {
01014         uves_msg("Dividing by normalized master flat-field (method = pixel)");
01015         
01016         check( uves_flatfielding(back_subbed, backsubbed_noise,
01017                      master_flat, mflat_noise),
01018            "Could not perform flat-fielding");
01019         
01020             /* Save flat-fielded image + noise */
01021         if (DEBUG) 
01022         {
01023             check( uves_save_image_local("Flat-fielded image", "fb", 
01024                          back_subbed, chip, -1, -1, 
01025                          rotated_header, true),
01026                "Error saving flat-fielded image");
01027             
01028             check( uves_save_image_local("Flat-fielded image noise", "errfb", 
01029                          backsubbed_noise, chip, -1, -1,
01030                          rotated_header, true),
01031                "Error saving noise of flat-fielded image");
01032         }
01033     }
01034     
01035     /* Extract the object window (+ sky windows depending on method) */
01036     switch(ex_method)
01037     {
01038     case EXTRACT_OPTIMAL:
01039     {
01040         int window_number = 2;
01041 
01042         check( extract_ff_rebin_merge(back_subbed, 
01043                                           backsubbed_noise,
01044                       raw_header,
01045                       master_flat, 
01046                                           mflat_noise,
01047                       ordertable, 
01048                                           order_locations,
01049                       linetable[window_number-1],
01050                                           linetable_header,
01051                                           dispersion_relation,
01052                       full_slit, 
01053                                           obj_offset,
01054                       window_number,
01055                       chip,
01056                       blaze_corr,
01057                                           tilt_corr,
01058                                           DEBUG,
01059                       parameters, 
01060                                           subcontext,
01061                       ff_method,
01062                       ex_method,
01063                       m_method,
01064                       NULL, 
01065                                           NULL, 
01066                                           NULL,
01067                       cosmic_mask, 
01068                       flatfielded_variance,
01069                       flatfielded_variance_header,
01070                       resampled_spectrum,
01071                       resampled_mf,
01072                       merged_sky,
01073                                           /* merged_sky will be computed 
01074                          during optimal extraction */
01075                       rebinned_spectrum, 
01076                                           rebinned_noise, 
01077                                           rebinned_header,
01078                       merged_spectrum, 
01079                                           merged_noise, 
01080                                           merged_header,
01081                                           info_tbl,
01082                                           order_trace),
01083            "Error during reduction");
01084         
01085         /* The sky-subtracted spectra are just the optimally extracted spectra
01086          * (since sky-subtraction is done during extraction)
01087          */
01088         check(( *reduced_spectrum = cpl_image_duplicate(*merged_spectrum),
01089             *reduced_noise    = cpl_image_duplicate(*merged_noise),
01090             *reduced_rebinned_spectrum = cpl_image_duplicate(*rebinned_spectrum),
01091             *reduced_rebinned_noise    = cpl_image_duplicate(*rebinned_noise)),
01092           "Error creating sky-subtracted spectra");
01093     }
01094     break;
01095     case EXTRACT_LINEAR: /* Same as average (pass ex-method to uves_extract) */
01096     case EXTRACT_AVERAGE:
01097     {
01098         /* Average/linear extraction.
01099          * Define sky+object+sky windows, 
01100          * extract, rebin, merge, subtract
01101          */
01102         
01103         const char *slicer_name;
01104         double doffset = 0;
01105         double obj_hi, obj_lo;
01106         double sky_lo_slit, sky_hi_slit;
01107         int window_number;
01108         
01109         /*
01110          *  See if there's an image slicer
01111          *  Extract sky only if not
01112          */
01113         
01114         check( slicer_name = uves_pfits_get_slit1_name(raw_header),
01115            "Could not read slicer id");
01116           
01117         uves_msg("Slicer name = '%s'%s", slicer_name, 
01118              (strcmp(slicer_name, "FREE") == 0) ? " (no slicer)" : "");
01119           
01120         if ( strncmp(slicer_name, "SLIC", 4) == 0)
01121         {
01122             /*
01123              *    Use full slit for object, no sky
01124              */
01125               
01126                     obj_hi = uves_min_double(+full_slit/2, obj_offset + full_slit/2);
01127                     obj_lo = uves_max_double(-full_slit/2, obj_offset - full_slit/2);
01128                     
01129                     obj_slit = obj_hi - obj_lo;
01130               
01131             sky_lo_slit = -1;        /* Don't extract sky */
01132             sky_hi_slit = -1;
01133               
01134             uves_msg("Extraction slits (full slit = %.2f pixels)", full_slit);
01135             uves_msg("|* Sky 1 *|******** Obj ********|* Sky 2 *|");
01136             uves_msg("|* %-5.1f *|******* %-5.1f *******|* %-5.1f *|",
01137                  0.0, obj_slit, 0.0);
01138         }
01139         else
01140         {
01141             /* There's no slicer */
01142             assure( strncmp(slicer_name, "FREE", 4) == 0, CPL_ERROR_UNSUPPORTED_MODE,
01143                 "Unrecognized slicer name: '%s'. "
01144                 "Recognized names include 'FREE', 'SLIC#1', 'SLIC#2', 'SLIC#3'.",
01145                 slicer_name);
01146             
01147             /* Measure offset if user didn't specify */
01148             if (obj_slit < 0)
01149             {
01150                 check( obj_offset = 
01151                    get_offset(back_subbed, 
01152                           ordertable, order_locations,
01153                           full_slit/2, /* Offset search range */
01154                           10,          /* Samples per order */
01155                           &doffset),
01156                    "Could not find object offset");
01157                 
01158                 uves_msg("Measured object position = %.2f +- %.2f pixels", 
01159                      obj_offset, doffset);
01160 
01161                 if (sky_sub)
01162                 {
01163                     /* Define object extraction slit length 
01164                        as half of full slit. */
01165                     obj_hi = uves_min_double(+full_slit/2, 
01166                                  obj_offset + full_slit/4.0);
01167                     obj_lo = uves_max_double(-full_slit/2, 
01168                                  obj_offset - full_slit/4.0);
01169                 }
01170                 else
01171                 /* No sky subtraction. Object = full slit */
01172                 {
01173                     obj_hi = uves_min_double(+full_slit/2, 
01174                                  obj_offset + full_slit/2.0);
01175                     obj_lo = uves_max_double(-full_slit/2, 
01176                                  obj_offset - full_slit/2.0);
01177                 }
01178                 obj_slit = obj_hi - obj_lo;
01179             }
01180             else
01181             /* User specified object slit */
01182             {
01183                 uves_msg("Object offset = %.2f pixels", obj_offset);
01184 
01185                 obj_hi = obj_offset + obj_slit / 2;
01186                 obj_lo = obj_offset - obj_slit / 2;
01187             }
01188             
01189             uves_msg("Object slit = %.2f pixels", obj_slit);
01190             
01191             assure( -full_slit / 2 < obj_offset && obj_offset < full_slit / 2, 
01192                 CPL_ERROR_ILLEGAL_INPUT,
01193                 "Object is outside slit! Offset = %f, Slit length = %f",
01194                 obj_offset, full_slit);
01195             
01196             /* Sky slits (might be negative if object has large offset) */
01197             if (sky_sub)
01198             {
01199                 sky_lo_slit = obj_lo - (-full_slit/2);
01200                 sky_hi_slit = full_slit/2 - obj_hi;
01201                 
01202                 assure( sky_lo_slit > 0 || sky_hi_slit > 0, CPL_ERROR_ILLEGAL_INPUT,
01203                     "At least one sky slit length must be positive. "
01204                     "They are %f and %f pixels", sky_lo_slit, sky_hi_slit);
01205             }
01206             else
01207             {
01208                 sky_lo_slit = -1; /* Don't extract sky */
01209                 sky_hi_slit = -1;
01210             }
01211             
01212             uves_msg("Extraction slits (full slit = %.2f pixels)", full_slit);
01213             uves_msg("|*** Sky 1 **%s|**** Obj ****|%s** Sky 2 ***|", 
01214                  (obj_lo > -obj_hi) ? "*" : "",
01215                  (obj_lo > -obj_hi) ? ""  : "*");
01216             uves_msg("|*** %-5.1f **%s|*** %-5.1f ***|%s** %-5.1f ***|",
01217                  sky_lo_slit, (obj_lo > -obj_hi) ? "*" : "",
01218                  obj_slit   , (obj_lo > -obj_hi) ? ""  : "*",
01219                  sky_hi_slit);
01220         }
01221         
01222         /* The window geometry has now been deermined. Extract spectra.
01223 
01224            It is important to use the same rebinning step size,
01225            for sky and object (otherwise the sky spectrum cannot (easily)
01226            be subtracted). If this step size is not specified (i.e. is negative)
01227            in the parameter list, it is determined from the 
01228            average pixelsize, which is read from the line table.
01229            Therefore pass the object's line table also for the sky windows
01230            (but still use different dispersion relations for sky/object)
01231         */
01232         
01233         /* Extract sky 1 */
01234         window_number = 1;
01235         if ( sky_lo_slit > 0 )
01236         {
01237             uves_msg("Processing sky 1 window");
01238             check( extract_ff_rebin_merge(back_subbed, 
01239                                                   backsubbed_noise,
01240                           raw_header,
01241                           master_flat, 
01242                                                   mflat_noise,
01243                           ordertable, 
01244                                                   order_locations,
01245                           linetable[2-1],   /* Object linetable */
01246                           linetable_header,
01247                                                   dispersion_relation,
01248                           sky_lo_slit,     /* Slit length (pixels) */
01249                           -full_slit/2 + sky_lo_slit/2,
01250                           /* Slit center offset */
01251                           window_number,
01252                           chip,
01253                           blaze_corr,
01254                                                   tilt_corr,
01255                                                   DEBUG,
01256                           parameters, 
01257                                                   subcontext,
01258                           ff_method,
01259                           ex_method,
01260                           m_method,
01261                           NULL, 
01262                                                   NULL, 
01263                           NULL, 
01264                                                   NULL,
01265                           NULL, 
01266                                                   NULL,
01267                           NULL, 
01268                                                   NULL, 
01269                                                   NULL,
01270                           &sky_lo_rebinned, 
01271                                                   &sky_lo_rebinned_noise, 
01272                                                   &sky_lo_rebinned_header,
01273                           &sky_lo, 
01274                                                   &sky_lo_noise, 
01275                                                   &sky_lo_header,
01276                                                   NULL, 
01277                                                   NULL),
01278                "Error processing lower sky window");
01279         }
01280         else
01281         {
01282             uves_msg("Skipping sky 1 window");
01283             sky_lo_rebinned = NULL;
01284             sky_lo = NULL;
01285         }
01286         
01287         /* Extract sky 2 */
01288         window_number = 3;
01289         if ( sky_hi_slit > 0 )
01290         {
01291             uves_msg("Processing sky 2 window");
01292               
01293             uves_free_propertylist(rebinned_header);
01294             check( extract_ff_rebin_merge(back_subbed, 
01295                                                   backsubbed_noise,
01296                           raw_header,
01297                           master_flat, 
01298                                                   mflat_noise,
01299                           ordertable, 
01300                                                   order_locations,
01301                           linetable[2-1], /* Object linetable */
01302                           linetable_header, 
01303                                                   dispersion_relation,
01304                           sky_hi_slit,    /* Slit length (pixels) */
01305                                                   full_slit/2 - sky_hi_slit/2,
01306                           /* Slit center offset      */
01307                           window_number,
01308                           chip,
01309                           blaze_corr,
01310                                                   tilt_corr,
01311                                                   DEBUG,
01312                           parameters, 
01313                                                   subcontext,
01314                           ff_method,
01315                           ex_method,
01316                           m_method,
01317                           NULL, 
01318                                                   NULL, 
01319                                                   NULL,
01320                                                   NULL,
01321                           NULL, 
01322                                                   NULL,
01323                           NULL, 
01324                                                   NULL, 
01325                                                   NULL,
01326                           &sky_hi_rebinned, 
01327                                                   &sky_hi_rebinned_noise, 
01328                                                   &sky_hi_rebinned_header,
01329                           &sky_hi, 
01330                                                   &sky_hi_noise, 
01331                                                   &sky_hi_header,
01332                                                   NULL,
01333                                                   NULL),
01334                "Error processing upper sky window");
01335         }
01336         else
01337         {
01338             uves_msg("Skipping sky 2 window");
01339             sky_hi_rebinned = NULL;
01340             sky_hi = NULL;
01341         }
01342           
01343         /* Extract object */
01344         window_number = 2;
01345         uves_msg("Processing object window");
01346         uves_free_propertylist(rebinned_header);
01347         check( extract_ff_rebin_merge(back_subbed, backsubbed_noise, raw_header,
01348                       master_flat, mflat_noise,
01349                       ordertable, order_locations,
01350                       linetable[window_number-1], 
01351                                           linetable_header, 
01352                                           dispersion_relation,
01353                       obj_slit,   /* Slit length (pixels) */
01354                                           obj_offset,   
01355                                           /* Slit center offset */
01356                       window_number,
01357                       chip,
01358                       blaze_corr,
01359                                           tilt_corr,
01360                       DEBUG,
01361                       parameters, 
01362                                           subcontext,
01363                       ff_method,
01364                       ex_method, 
01365                       m_method,
01366                       NULL, 
01367                                           NULL, 
01368                       NULL, 
01369                                           NULL,
01370                       flatfielded_variance,
01371                       flatfielded_variance_header,
01372                       resampled_spectrum, 
01373                                           resampled_mf, 
01374                                           NULL,
01375                       rebinned_spectrum, 
01376                                           rebinned_noise, 
01377                                           rebinned_header,
01378                       merged_spectrum, 
01379                                           merged_noise, 
01380                                           merged_header,
01381                                           info_tbl,
01382                                           NULL),
01383            "Error processing object window");
01384 
01385         if (info_tbl != NULL && *info_tbl != NULL)
01386         {
01387             /* Compute obj. position from sky_lo_slit
01388                for consistency with optimal extraction */
01389             int i;
01390             for (i = 0; i < cpl_table_get_nrow(*info_tbl); i++)
01391             {
01392                 cpl_table_set_double(*info_tbl, "Pos", i,
01393                          cpl_table_get_double(*info_tbl, "Pos", i, NULL)
01394                          + 
01395                          ((sky_lo_slit >= 0) ? sky_lo_slit : 0));
01396 
01397             }
01398         }
01399                 
01400           
01401         /* Now subtract sky from both rebinned spectrum and merged spectrum */
01402           
01403         /* Duplicate, then subtract */
01404         
01405         /* 1d spectrum */
01406         check(( *reduced_spectrum = cpl_image_duplicate(*merged_spectrum),
01407             *reduced_noise    = cpl_image_duplicate(*merged_noise)),
01408           "Error allocating sky-subtracted spectra");
01409         
01410         /* 2d (wavelength, order) spectrum */
01411         check(( *reduced_rebinned_spectrum = 
01412             cpl_image_duplicate(*rebinned_spectrum),
01413             *reduced_rebinned_noise    = 
01414             cpl_image_duplicate(*rebinned_noise)),
01415           "Error allocating sky-subtracted spectra");
01416         
01417         if (sky_lo != NULL || sky_hi != NULL)
01418         {
01419             uves_msg("Subtracting sky");
01420             
01421             check( *merged_sky = 
01422                subtract_sky(*reduced_rebinned_spectrum, 
01423                     *reduced_rebinned_noise, *rebinned_header,
01424                     sky_lo_rebinned, sky_lo_rebinned_noise, 
01425                     sky_lo_rebinned_header,
01426                     sky_hi_rebinned, sky_hi_rebinned_noise,
01427                     sky_hi_rebinned_header,
01428                     reduced_spectrum, reduced_noise, *merged_header,
01429                     sky_lo, sky_lo_noise, sky_lo_header,
01430                     sky_hi, sky_hi_noise, sky_hi_header,
01431                     (ex_method == EXTRACT_AVERAGE) ? 1.0 : obj_slit,
01432                     (ex_method == EXTRACT_AVERAGE) ? 1.0 : sky_lo_slit,
01433                     (ex_method == EXTRACT_AVERAGE) ? 1.0 : sky_hi_slit),
01434                "Could not subtract sky");
01435 
01436             if (*merged_sky == NULL)
01437             {
01438                 uves_msg_warning("Could not subtract sky");
01439             }
01440         }
01441         else
01442         {
01443             uves_msg_low("Skipping sky subtraction");
01444               
01445             /* Return no sky spectrum */
01446             *merged_sky = NULL;
01447         }
01448     } /* Simple extraction */
01449     break;
01450     case EXTRACT_2D:
01451     {
01452         int window_number = 2;    /* Use middle line table for entire
01453                      slit length (like MIDAS) */
01454 
01455         int half_slit_length;     /* The slit length is
01456                      2*half_slit_length = an even number */
01457         
01458         /* Round to nearest integer, remove (noisy) edge (~2 pixels) */
01459         half_slit_length = 
01460         uves_max_int(1, uves_round_double(header_full_slit/2) - 1);
01461 
01462         check( extract_ff_rebin_merge(back_subbed, 
01463                                           backsubbed_noise,
01464                       raw_header,
01465                       master_flat, 
01466                                           mflat_noise,
01467                       ordertable, 
01468                                           order_locations,
01469                       linetable[window_number-1],
01470                       linetable_header,
01471                       dispersion_relation,
01472                       2*half_slit_length, 
01473                                           0, /* offset is not used when method=2d */
01474                       window_number,
01475                       chip,
01476                       blaze_corr,
01477                                           tilt_corr,
01478                                           DEBUG,
01479                       parameters, 
01480                                           subcontext,
01481                       ff_method,
01482                       ex_method,
01483                       m_method,
01484                       x, 
01485                                           x_header, 
01486                                           fx,      /* 2d-ex. output      */
01487                       NULL, 
01488                                           NULL,    /* Optimal-ex. output */
01489                       NULL, 
01490                                           NULL,    /* Don't want resampled_spectrum,
01491                               resampled_mf */
01492                       NULL,    /* Don't want sky spectrum */
01493                                           NULL,    /* Don't want 
01494                               flatfielded_variance+header */
01495                       rebinned_spectrum, 
01496                                           rebinned_noise, 
01497                                           rebinned_header,
01498                       merged_spectrum, 
01499                                           merged_noise, 
01500                                           merged_header, 
01501                                           info_tbl,
01502                                           NULL),
01503            "Error during reduction");
01504 
01505             if (x_header != NULL)
01506                 {
01507                     uves_pfits_set_hs(*x_header, 
01508                                       uves_round_double(2*half_slit_length));
01509                 }
01510             if (rebinned_header != NULL)
01511                 {
01512                     uves_pfits_set_hs(*rebinned_header, 
01513                                       uves_round_double(2*half_slit_length));
01514                 }
01515             if (merged_header != NULL)
01516                 {
01517                     uves_pfits_set_hs(*merged_header, 
01518                                       uves_round_double(2*half_slit_length));
01519                 }
01520 
01521 
01522         /* No sky-subtraction done. Just copy the merged spectra
01523          * to get the 'reduced' (i.e. sky-subtracted) spectra
01524          */
01525         check(( *reduced_spectrum = cpl_image_duplicate(*merged_spectrum),
01526             *reduced_noise    = cpl_image_duplicate(*merged_noise),
01527             *reduced_rebinned_spectrum = cpl_image_duplicate(*rebinned_spectrum),
01528             *reduced_rebinned_noise    = cpl_image_duplicate(*rebinned_noise)),
01529           "Error allocating reduced spectra");
01530     }
01531     break;
01532     default:
01533         assure( false, CPL_ERROR_ILLEGAL_INPUT, 
01534             "Unknown extraction method: %d", ex_method);
01535         break;
01536     } /* switch extraction method optimal/simple/2d */
01537     
01538   cleanup:
01539     uves_free_image(&back_subbed);
01540     uves_free_image(&backsubbed_noise);
01541     uves_free_image(&mflat_noise);
01542     uves_free_image(&simple_extracted);
01543     uves_free_image(&simple_extracted_mf);
01544     uves_free_image(&sky_lo);
01545     uves_free_image(&sky_lo_noise);
01546     uves_free_image(&sky_hi);
01547     uves_free_image(&sky_hi_noise);
01548     uves_free_image(&sky_lo_rebinned);
01549     uves_free_image(&sky_lo_rebinned_noise);
01550     uves_free_image(&sky_hi_rebinned);
01551     uves_free_image(&sky_hi_rebinned_noise);
01552     uves_free_propertylist(&sky_lo_header);
01553     uves_free_propertylist(&sky_hi_header);
01554     uves_free_propertylist(&sky_lo_rebinned_header);
01555     uves_free_propertylist(&sky_hi_rebinned_header);
01556 
01557     cpl_free(subcontext);
01558     cpl_free(ex_method_string);
01559 
01560     if (cpl_error_get_code() != CPL_ERROR_NONE)
01561     {
01562         uves_free_image(background);
01563         uves_free_image(flatfielded_variance);
01564         uves_free_propertylist(flatfielded_variance_header);
01565         uves_free_image(resampled_spectrum);
01566         uves_free_image(resampled_mf);
01567         uves_free_image(merged_sky);
01568         uves_free_image(rebinned_spectrum);
01569         uves_free_image(rebinned_noise);
01570         uves_free_propertylist(rebinned_header);
01571         
01572         uves_free_image(merged_noise);
01573         uves_free_image(merged_spectrum);
01574         uves_free_propertylist(merged_header);
01575     }
01576 
01577     return cpl_error_get_code();
01578 }
01579 
01580 
01581 /*----------------------------------------------------------------------------*/
01591 /*----------------------------------------------------------------------------*/
01592 static polynomial *
01593 interpolate_wave(const polynomial *dispersion_relation[3],
01594                  const uves_propertylist *linetable_header[3],
01595                  double objoffset)
01596 {
01597     polynomial *dispersion = NULL;
01598     polynomial *q1 = NULL;
01599     polynomial *q2 = NULL;
01600     cpl_table *offset = cpl_table_new(3);
01601     int ilow, ihigh;
01602     double offset1, offset2;
01603     
01604     /* We need the sort pattern. Use a table for that */
01605     cpl_table_new_column(offset, "Index", CPL_TYPE_INT);
01606     cpl_table_new_column(offset, "Offset", CPL_TYPE_DOUBLE);
01607     
01608     {
01609         int i;
01610         bool reverse;
01611         for (i = 0; i < 3; i++) {
01612             cpl_table_set_int(offset, "Index", i, i);
01613             cpl_table_set_double(offset, "Offset", i,
01614                                  uves_pfits_get_offset(linetable_header[i]));
01615             
01616             uves_msg_debug("Wavecal %d offset is %f pixels", i, 
01617                            cpl_table_get_double(offset, "Offset", i, NULL));
01618         }
01619 
01620         reverse = false;
01621         uves_sort_table_1(offset, "Offset", reverse);
01622     }
01623     
01624     /* Find indices of the two dispersion solutions neares to the object position */
01625     if (objoffset <= cpl_table_get_double(offset, "Offset", 1, NULL))
01626         {
01627             ilow    = cpl_table_get_int(offset, "Index", 0, NULL);
01628             ihigh   = cpl_table_get_int(offset, "Index", 1, NULL);
01629             offset1 = cpl_table_get_double(offset, "Offset", 0, NULL);
01630             offset2 = cpl_table_get_double(offset, "Offset", 1, NULL);
01631         }
01632     else
01633         {
01634             ilow  = cpl_table_get_int(offset, "Index", 1, NULL);
01635             ihigh = cpl_table_get_int(offset, "Index", 2, NULL);
01636             offset1 = cpl_table_get_double(offset, "Offset", 1, NULL);
01637             offset2 = cpl_table_get_double(offset, "Offset", 2, NULL);
01638         }
01639 
01640     uves_msg("Interpolating dispersion relation at offset = %.2f",
01641              objoffset);
01642 
01643     uves_msg_debug("Using previous solutions at %.2f and %.2f pixels",
01644                    offset1, offset2);
01645     
01646     /* Fail cleanly if 2 dispersion solution were obtained at the same offset
01647        (rather than silently dividing by zero) */
01648     assure( offset1 < offset2,
01649             CPL_ERROR_DIVISION_BY_ZERO,
01650             "Dispersion solution %d offset = %.2f pixels; "
01651             "dispersion solution %d offset = %.2f pixels; cannot extrapolate",
01652             ilow, offset1,
01653             ihigh, offset2);
01654     
01655     /* Do simple linear interpolation =
01656            p = a p1 + b p2
01657        where
01658           a = (offset2 - offset) / (offset2 - offset1)
01659           b = (offset1 - offset) / (offset1 - offset2)
01660 
01661        which corrects for any line tilt to 1st order.
01662 
01663        A 2nd order line tilt correction (distortions) is probably overkill
01664        because of UVES' short slit and very straight arclines.
01665     */
01666     {
01667         double a = (offset2 - objoffset) / (offset2 - offset1);
01668         double b = (offset1 - objoffset) / (offset1 - offset2);
01669         
01670         q1 = uves_polynomial_duplicate(dispersion_relation[ilow]); 
01671         uves_polynomial_rescale(q1, 0, a); 
01672         /* q1 = a p1 */
01673         
01674         q2 = uves_polynomial_duplicate(dispersion_relation[ihigh]); 
01675         uves_polynomial_rescale(q2, 0, b); 
01676         /* q2 = b p2 */
01677         
01678         dispersion = uves_polynomial_add_2d(q1, q2);
01679     }
01680     
01681   cleanup:
01682     uves_free_table(&offset);
01683     uves_polynomial_delete(&q1);
01684     uves_polynomial_delete(&q2);
01685     return dispersion;
01686 }
01687 
01688 
01689 /*----------------------------------------------------------------------------*/
01768 /*----------------------------------------------------------------------------*/
01769 
01770 static cpl_error_code
01771 extract_ff_rebin_merge(cpl_image *back_subbed, 
01772                        cpl_image *backsubbed_noise,
01773                const uves_propertylist *backsubbed_header,
01774                const cpl_image *master_flat, 
01775                        cpl_image *mflat_noise,
01776                const cpl_table *ordertable, 
01777                        const polynomial *order_locations,
01778                const cpl_table *linetable, 
01779                        const uves_propertylist *linetable_header[3], 
01780                        const polynomial *dispersion_relation[3],
01781                double slit_length, 
01782                        double slit_offset,
01783                int window, 
01784                        enum uves_chip chip,
01785                bool blaze_correct,
01786                        bool tilt_corr,
01787                        bool DEBUG,
01788                const cpl_parameterlist *parameters, 
01789                        const char *context,
01790                flatfielding_method ff_method,
01791                extract_method ex_method,
01792                merge_method m_method,
01793                /* Output */
01794                cpl_image **x, uves_propertylist **x_header,
01795                cpl_image **fx,
01796                cpl_table **cosmic_mask,
01797                cpl_image **flatfielded_variance,
01798                uves_propertylist **flatfielded_variance_header,
01799                cpl_image **resampled_spectrum,
01800                cpl_image **resampled_mf,
01801                cpl_image **merged_sky,
01802                cpl_image **rebinned_spectrum, 
01803                        cpl_image **rebinned_noise, 
01804                        uves_propertylist **rebinned_header,
01805                cpl_image **merged_spectrum, 
01806                        cpl_image **merged_noise, 
01807                        uves_propertylist **merged_header,
01808                        cpl_table **info_tbl,
01809                        cpl_table **order_trace)
01810 {
01811     cpl_image *extracted           = NULL;
01812     cpl_image *extracted_noff      = NULL;
01813     cpl_image *extracted_noise     = NULL;
01814     cpl_image *extracted_sky       = NULL;   /* For optimal extraction */
01815     cpl_image *extracted_sky_noise = NULL;
01816     cpl_image *blaze_ratio         = NULL;   /* The (per-order normalized)
01817                                                 ratio of blaze functions */
01818 
01819     cpl_image *cosmic_image        = NULL;
01820     cpl_image *weights             = NULL;
01821     cpl_table *profile_table       = NULL;
01822     uves_propertylist *extracted_header = NULL;
01823 
01824     cpl_image *extracted_mf        = NULL;
01825     cpl_image *extracted_mf_noise  = NULL;
01826 
01827     cpl_image *rebinned_sky        = NULL;
01828     cpl_image *rebinned_sky_noise  = NULL;
01829     cpl_image *merged_sky_noise    = NULL;
01830 
01831     polynomial *dispersion_int     = NULL;    /* interpolated at object position */
01832     polynomial *dispersion_int_sky = NULL;    /* if sky was extracted simultaneously
01833                                                  with the object (optimal extraction)
01834                                                  this is the dispersion at the average
01835                                                  sky position */
01836     cpl_table *poly_table          = NULL;
01837     cpl_image *temp_image          = NULL;
01838 
01839     int n_traces;                             /* Number of traces. Equal to 1,
01840                          unless for 2d reduction */
01841     int first_abs_order, last_abs_order;
01842     int filename_window;                      /* The window number appended to 
01843                          the filenames of local products */
01844 
01845     /* If ff_method is FF_EXTRACT, or if resampled_mf is requested,
01846        then master flat must be provided */
01847     passure((ff_method != FF_EXTRACT && resampled_mf == NULL) 
01848         ||
01849         master_flat != NULL, " ");
01850 
01851     /* Blaze correction only makes sense if flat-fielding,
01852      */
01853     passure( !blaze_correct || ff_method != FF_NO, " ");
01854 
01855     passure( ex_method != EXTRACT_OPTIMAL || merged_sky != NULL, " ");
01856     passure( ex_method != EXTRACT_OPTIMAL || cosmic_mask != NULL, " ");
01857 
01858     passure(1 <= window && window <= 3, "Illegal window: %d", window);
01859 
01860     passure( (x == NULL) == (x_header == NULL) &&
01861          (x == NULL) == (fx == NULL), " ");
01862 
01863     if (ex_method == EXTRACT_OPTIMAL || ex_method == EXTRACT_2D)
01864     {
01865         /* Don't append window number if optimal/2d extraction.
01866            There's only one window in these cases, and
01867            it allows the response/efficiency recipe to save 
01868            both optimally and linearly extracted spectra
01869            (without overwriting). */
01870 
01871         filename_window = -1;  /* -1 means don't append window
01872                       number to filename */
01873     }
01874     else
01875     {
01876         filename_window = window;
01877     }
01878 
01879     n_traces = (ex_method == EXTRACT_2D) ? uves_round_double(slit_length) : 1;
01880     
01881     check( first_abs_order = uves_pfits_get_firstabsorder(linetable_header[0]), 
01882        "Could not read order numbers from line table header");
01883     check( last_abs_order  = uves_pfits_get_lastabsorder (linetable_header[0]), 
01884        "Could not read order numbers from line table header");
01885 
01886     if (window == 2)
01887     {
01888         uves_msg("Extracting object");
01889     }
01890 
01891     check( extracted = 
01892        uves_extract(back_subbed, 
01893             backsubbed_noise,
01894             backsubbed_header,
01895             ordertable, 
01896             order_locations,
01897             slit_length,               /* Slit length (pixels)     */
01898             slit_offset,               /* Slit center offset       */
01899             parameters, 
01900             context,                   /* Extraction method, kappa */
01901             false,                     /* Don't extract partial bins */
01902                         DEBUG,
01903             chip,
01904             &extracted_header,         /* Spectrum header          */
01905             &extracted_noise,          /* Spectrum noise           */
01906             /* Optimal extraction parameters: */
01907             &extracted_sky,            /* Sky                      */
01908             &extracted_sky_noise,      /* Sky                      */
01909             cosmic_mask, 
01910             &cosmic_image,
01911             (DEBUG) ? 
01912             &profile_table : NULL,
01913             &weights,                  /* If optimal, weights
01914                               are defined              */
01915             info_tbl,
01916                         order_trace),
01917        "Error extracting spectrum");
01918 
01919     if (x != NULL) {
01920         *x = cpl_image_duplicate(extracted);
01921         *x_header = uves_propertylist_duplicate(extracted_header);
01922     }
01923 
01924     if (ex_method == EXTRACT_OPTIMAL) {
01925         uves_msg_low("%d hot pixels were detected during optimal extraction",
01926                      cpl_table_get_nrow(*cosmic_mask));
01927         
01928         if (cpl_table_get_nrow(*cosmic_mask) > 0) {
01929             check( uves_plot_table(*cosmic_mask, "X", "Y", 
01930                                    "%d hot/cold pixels",
01931                                    cpl_table_get_nrow(*cosmic_mask)),
01932                    "Plotting failed");
01933         }
01934     }
01935     
01936     /* Save extracted spectrum + noise + sky + noise, and
01937        if optimal: weightmap + crmask + order_trace */
01938     if (DEBUG) {
01939         /* This (bit ugly) code selects filename
01940          *  and description string depending on
01941          *  whether flat-fielding was already done
01942          */
01943 
01944 
01945 
01946       check(uves_propertylist_copy_property_regexp(extracted_header,
01947                            backsubbed_header,
01948                            "^ESO ",0),"error copying hierarch keys");
01949 
01950 
01951         check( uves_save_image_local((ff_method == FF_PIXEL) ? 
01952                                      "Extracted, flatfielded spectrum" :
01953                                      "Extracted spectrum",
01954                                      (ff_method == FF_PIXEL) ? 
01955                                      "xfb" : "xb",
01956                                      extracted, chip, -1,
01957                                      filename_window, extracted_header, true),
01958                "Error saving extracted%s spectrum", 
01959                (ff_method == FF_PIXEL) ? ", flatfielded" : "");
01960         
01961         check( uves_save_image_local((ff_method == FF_PIXEL) ?
01962                                      "Extracted, flatfielded spectrum noise" :
01963                                      "Extracted spectrum noise",
01964                                      (ff_method == FF_PIXEL) ? 
01965                                      "errxfb" : "errxb",
01966                                      extracted_noise, chip, -1, 
01967                                      filename_window, extracted_header, true),
01968                "Error saving noise of extracted%s spectrum", 
01969                (ff_method == FF_PIXEL) ? ", flatfielded" : "");
01970         
01971         if (extracted_sky != NULL)
01972             {
01973                 check( uves_save_image_local((ff_method == FF_PIXEL) ? 
01974                                              "Extracted, flatfielded sky" : 
01975                                              "Extracted sky",
01976                                              (ff_method == FF_PIXEL) ? 
01977                                              "xfsky" : "xsky",
01978                                              extracted_sky, chip, -1,
01979                                              filename_window, extracted_header, true),
01980                        "Error saving extracted%s sky", 
01981                        (ff_method == FF_PIXEL) ? ", flatfielded" : "");
01982                 
01983                 check( uves_save_image_local((ff_method == FF_PIXEL) ? 
01984                                              "Noise of extracted, flatfielded sky" : 
01985                                              "Noise of extracted sky",
01986                                              (ff_method == FF_PIXEL) ? 
01987                                              "errxfsky" : "errxsky",
01988                                              extracted_sky_noise, chip, -1,
01989                                              filename_window, extracted_header, true),
01990                        "Error saving extracted%s sky noise", 
01991                        (ff_method == FF_PIXEL) ? ", flatfielded" : "");
01992             }
01993         
01994         if (ex_method == EXTRACT_OPTIMAL)
01995             {
01996                 check( uves_save_image_local("Optimal extraction weights", 
01997                                              "weights",
01998                                              weights, chip, -1,
01999                                              filename_window, 
02000                                              backsubbed_header, true),
02001                        "Error saving weights map");
02002                 
02003                 check( uves_save_table_local("Cosmic ray table", "crmask", 
02004                                              *cosmic_mask, chip, -1,
02005                                              filename_window, 
02006                                              backsubbed_header, NULL),
02007                        "Error saving cosmic ray mask");
02008 
02009                 check( uves_save_image_local("Cosmic ray image", "crimage",
02010                                              cosmic_image, chip, -1,
02011                                              filename_window, 
02012                                              backsubbed_header, true),
02013                        "Error saving cosmic ray mask");
02014 
02015                 if (profile_table != NULL)
02016                     {
02017                         check( uves_save_table_local("Profile table", "profile", 
02018                                                      profile_table, chip, -1,
02019                                                      filename_window, 
02020                                                      backsubbed_header, NULL),
02021                                "Error saving profile table");
02022                     }
02023             }
02024     }
02025 
02026     /* Extract + resample master flat, only if necessary */
02027     if (master_flat != NULL && (ff_method == FF_EXTRACT || resampled_mf != NULL))
02028     {
02029         uves_msg("Extracting master flat field");
02030         
02031         /* Extract the master flat spectrum.
02032            If object was extracted with method=optimal,
02033            then temporarily set method=weighted */
02034         
02035         if (ex_method == EXTRACT_OPTIMAL)
02036         {
02037             const char *temp_method = "weighted";
02038             
02039             /* Cast to non-const is okay. After extraction, the 
02040                parameter is set to 'optimal' (see below), so there
02041                is not net change (unless the extraction fails, in
02042                which case parameter list will change).
02043             */
02044             check( uves_set_parameter((cpl_parameterlist *) parameters,
02045                           context, UVES_EXTRACT_ID ".method", 
02046                           CPL_TYPE_STRING, &temp_method),
02047                "Error setting extraction method to '%s'", temp_method);
02048         }
02049         
02050         check( extracted_mf = 
02051            uves_extract((cpl_image *)master_flat,  /* const-cast ok,
02052                                   outlier pixels are
02053                                   flagged bad only in
02054                                   optimal extraction */
02055                 mflat_noise,
02056                 NULL,           /* FITS header */
02057                 ordertable, 
02058                 order_locations,
02059                 slit_length,    /* Slit length (pixels)     */
02060                 slit_offset,    /* Slit center offset       */
02061                 parameters, context, /* Extraction method, 
02062                             kappa */
02063                 false,          /* Extraction partial bins? */
02064                                 DEBUG,
02065                 chip,
02066                 NULL,           /* Spectrum header          */
02067                 &extracted_mf_noise,  /* Spectrum noise     */
02068                 NULL, 
02069                 NULL,           /* Sky                      */
02070                 NULL, 
02071                 NULL, 
02072                 NULL,    /* Cosmic ray table/image,
02073                          profile     */
02074                 &weights, /* Weights are used unchanged */
02075                 NULL,
02076                                 NULL),  
02077            "Error extracting master flat spectrum");
02078         
02079         /* Reset parameter to previous value 
02080            (parameter list is declared const) */
02081         if (ex_method == EXTRACT_OPTIMAL) {
02082                 const char *method = "optimal";
02083                 
02084                 /* Cast to non-const is okay. On successful termination,
02085                    there is no net change in the parameter list (see above). */
02086                 check( uves_set_parameter((cpl_parameterlist *) parameters,
02087                                           context, UVES_EXTRACT_ID ".method", 
02088                                           CPL_TYPE_STRING, &method),
02089                        "Error resetting extraction method to '%s'", method);
02090             }
02091         
02092         if (DEBUG) {
02093                 double ff_mean;
02094                 
02095                 /* Save normalized master flat spectrum + noise */
02096                 uves_free_image(&temp_image);
02097                 
02098                 ff_mean    = cpl_image_get_mean(extracted_mf);
02099                 
02100                 check( temp_image = 
02101                        cpl_image_divide_scalar_create(extracted_mf, ff_mean),
02102                        "Could not normalize master flat spectrum");
02103                 
02104                 check( uves_save_image_local("Normalized master flat spectrum",
02105                                              "xmf",
02106                                              temp_image, chip, -1,
02107                                              filename_window, extracted_header, true),
02108                        "Error saving image");
02109                 
02110                 /* Also rescale noise before saving */
02111                 uves_free_image(&temp_image);
02112                 check( temp_image = 
02113                        cpl_image_divide_scalar_create(extracted_mf_noise,
02114                                                       ff_mean),
02115                        "Could not rescale master flat noise spectrum");
02116                 
02117                 check( uves_save_image_local("Noise of normalized "
02118                                              "master flat spectrum", "errxmf",
02119                                              temp_image, chip, -1,
02120                                              filename_window, extracted_header, true),
02121                        "Error saving image");
02122                 
02123                 uves_free_image(&temp_image);
02124             }
02125         
02126         /* Rebin master flat */
02127         if (resampled_mf != NULL) {
02128                 uves_msg("Rebinning master flat spectrum");
02129 
02130 
02131                 /* Use dispersion solution obtained at slit center 
02132                  * (high accuracy is non-essential here, the resampled
02133                  * flat-field is not used in further processing
02134                  */
02135                 check( *resampled_mf = uves_rebin(extracted_mf,
02136                                                   parameters, context,
02137                                                   linetable,
02138                                                   dispersion_relation[1],
02139                                                   first_abs_order,
02140                                                   last_abs_order,
02141                                                   n_traces,
02142                                                   false,
02143                                                   rebinned_header),
02144                        "Error resampling master flat");
02145                 
02146                 if (DEBUG) {
02147 
02148       check(uves_propertylist_copy_property_regexp(*rebinned_header,
02149                            backsubbed_header,
02150                            "^ESO ", 0),"error copying hierarch keys");
02151 
02152 
02153                     check( uves_save_image_local("Resampled master flat spectrum",
02154                                                  "wxmf", *resampled_mf, chip, -1,
02155                                                  filename_window, *rebinned_header, true),
02156                            "Error saving image");
02157                 }
02158             }
02159     } /* Extract, rebin master flat */
02160     
02161     /* If we didn't already, divide by the flat field */
02162     if (ff_method == FF_EXTRACT)
02163     {
02164         uves_msg("Dividing by normalized master flat-field (method = extract)");
02165         
02166             /* Remember this for later */
02167             extracted_noff = cpl_image_duplicate(extracted);
02168 
02169         check( uves_flatfielding(extracted   , extracted_noise,
02170                      extracted_mf, extracted_mf_noise),
02171            "Could not perform flat-fielding");
02172 
02173         if (extracted_sky != NULL)
02174         {
02175             check( uves_flatfielding(extracted_sky, extracted_sky_noise,
02176                          extracted_mf, extracted_mf_noise),
02177                "Could not perform flat-fielding");
02178         }
02179 
02180         /* Save flat-fielded spectrum + noise */
02181         if (DEBUG) 
02182         {
02183             check( uves_save_image_local("Flat-fielded spectrum", "fxb",
02184                          extracted, chip, -1, filename_window,
02185                          extracted_header, true),
02186                "Error saving image");
02187             
02188             check( uves_save_image_local("Flat-fielded spectrum noise", 
02189                          "errfxb", extracted_noise, chip,
02190                          -1, filename_window, extracted_header, true),
02191                "Error saving noise of flat-fielded image");
02192         }
02193 
02194         if (DEBUG && extracted_sky != NULL) 
02195         {
02196             check( uves_save_image_local("Flat-fielded sky", "fxsky", 
02197                          extracted_sky, chip, -1,
02198                          filename_window, extracted_header, true),
02199                "Error saving image");
02200             
02201             check( uves_save_image_local("Flat-fielded sky noise", "errfxsky",
02202                          extracted_sky_noise, chip, -1,
02203                          filename_window, extracted_header, true),
02204                "Error saving noise of flat-fielded image");
02205         }
02206     }
02207 
02208     if (fx != NULL)
02209     {
02210         *fx = cpl_image_duplicate(extracted);
02211     }
02212 
02213     /* Variance of flat-fielded, pre-rebinned spectrum
02214        is a product of science recipe (for whatever reason...) */
02215     if (flatfielded_variance != NULL)
02216     {
02217 
02218         check( *flatfielded_variance = 
02219            cpl_image_multiply_create(extracted_noise, 
02220                          extracted_noise),
02221            "Error creating variance of flatfielded spectrum");
02222 
02223         passure(flatfielded_variance_header != NULL, " ");
02224         check( *flatfielded_variance_header = 
02225            uves_propertylist_duplicate(extracted_header),
02226            "Could not copy extracted spectrum header");
02227     }
02228     
02229     if (blaze_correct)
02230     {
02231         if (ex_method == EXTRACT_2D)
02232         {
02233             /* It requires an extracted spectrum which we don't have in 2d mode */
02234             uves_msg_low("Skipping blaze function correction for 2d extraction mode");
02235         }
02236         else
02237         {
02238             uves_msg("Calculating blaze function correction");
02239             
02240             check( blaze_ratio = uves_get_blaze_ratio(extracted, extracted_noise),
02241                "Error calculating blaze function correction");
02242             
02243             uves_msg("Applying blaze function correction");
02244             
02245             check(( cpl_image_divide(extracted      , blaze_ratio),
02246                 cpl_image_divide(extracted_noise, blaze_ratio)),
02247               "Error applying blaze function correction");
02248             
02249             if (extracted_sky != NULL)   /* If sky was extracted (optimal) */
02250             {
02251                 check(( cpl_image_multiply(extracted_sky, blaze_ratio),
02252                     cpl_image_multiply(extracted_sky_noise, blaze_ratio)),
02253                   "Error applying blaze function correction");
02254             }
02255         }
02256     }
02257 
02258     /* Rebin from (x, order) to (wavelength, order) */
02259     uves_msg("Rebinning spectrum");
02260     if (ex_method == EXTRACT_2D) {
02261         if (tilt_corr) {
02262             uves_msg_warning("Line tilt correction in rebinning "
02263                              "of 2d spectra is unsupported");
02264         }
02265         dispersion_int = uves_polynomial_duplicate(dispersion_relation[window-1]);
02266     }
02267     else if (tilt_corr) { 
02268         double objoffset;
02269         
02270         if (info_tbl != NULL) {
02271             objoffset = cpl_table_get_column_median(*info_tbl, "Pos");
02272             /* This is the object position measured from the bottom of
02273                the of specified extraction window.
02274                Need to convert this to the same coordinates as used in the wavecal.
02275             */
02276             
02277             uves_msg_debug("Object position (from bottom of extraction window) = %.2f pixels",
02278                      objoffset);
02279             
02280             objoffset -= slit_length / 2;
02281             /* Now wrt middle of specified window */
02282             
02283             objoffset += slit_offset;
02284             /* Now wrt order trace center */
02285             
02286             uves_msg_debug("Object position (from slit center) = %.2f pixels",
02287                      objoffset);
02288         }
02289         else {
02290             /* Sky windows */
02291             uves_msg_debug("Object offset not measured during extraction, "
02292                            "using %.2f pixels", slit_offset);
02293             objoffset = slit_offset;
02294         }
02295         
02296         check( dispersion_int = interpolate_wave(dispersion_relation,
02297                                                  linetable_header,
02298                                                  objoffset),
02299                "Could not interpolate dispersion solutions");
02300         
02301         if (DEBUG) {
02302             check( poly_table = uves_polynomial_convert_to_table(dispersion_int), 
02303                    "Error converting polynomial to table");
02304             
02305             check( uves_save_table_local("Interpolated dispersion relation",
02306                                          "intdisp", 
02307                                          poly_table, chip, -1,
02308                                          filename_window, backsubbed_header, NULL),
02309                    "Error saving interpolated dispersion solution");
02310         }
02311     }
02312     else {
02313         dispersion_int = uves_polynomial_duplicate(dispersion_relation[window-1]);
02314     }
02315     
02316     uves_free_propertylist(rebinned_header);
02317     check( *rebinned_spectrum = uves_rebin(extracted,
02318                        parameters, context, 
02319                        linetable, dispersion_int,
02320                        first_abs_order,
02321                        last_abs_order,
02322                        n_traces,
02323                                            false,
02324                        rebinned_header),
02325        "Could not rebin spectrum");
02326     
02327     uves_msg("Rebinning spectrum noise");
02328 
02329     /* As in UVES/MIDAS the noise spectrum is rebinned to the same
02330      * level. It is not propagated using error propagation formula.
02331      * In other words, after this step, the noise level no longer
02332      *  describes the empirical noise actually observed in the spectrum
02333      * (which does change during rebinning depending on the bin width)
02334      */
02335 
02336     {
02337         bool threshold_to_positive = true;
02338 
02339         uves_free_propertylist(rebinned_header);
02340         check( *rebinned_noise = uves_rebin(extracted_noise,
02341                                             parameters, context, 
02342                                             linetable, dispersion_int,
02343                                             first_abs_order,
02344                                             last_abs_order,
02345                                             n_traces,
02346                                             threshold_to_positive,
02347                                             rebinned_header),
02348                "Could not rebin spectrum noise");
02349     }
02350 
02351     if (extracted_sky != NULL) {
02352         uves_msg("Rebinning sky spectrum");
02353 
02354         if (tilt_corr) {
02355             /* Optimal extraction extracts an average of the sky
02356                in the entire extraction window.
02357                
02358                Calibrate the sky spectrum using the dispersion solution
02359                at the extraction window center, i.e. at offset = slit_offset
02360             */
02361             
02362             check( dispersion_int_sky = interpolate_wave(dispersion_relation,
02363                                                          linetable_header,
02364                                                          slit_offset),
02365                    "Could not interpolate dispersion solutions");
02366         }
02367         else {
02368             /* Use middle solution */
02369             dispersion_int_sky = uves_polynomial_duplicate(dispersion_relation[1]);
02370         }
02371 
02372         /* Re-use the same rebinned_header */
02373         uves_free_propertylist(rebinned_header);
02374         check( rebinned_sky = uves_rebin(extracted_sky,
02375                                          parameters, context, 
02376                                          linetable, dispersion_int_sky,
02377                                          first_abs_order,
02378                                          last_abs_order,
02379                                          n_traces,
02380                                          false,
02381                                          rebinned_header),
02382                "Could not rebin sky noise");
02383         
02384         uves_msg("Rebinning sky spectrum noise");
02385         
02386         uves_free_propertylist(rebinned_header);
02387         check( rebinned_sky_noise = uves_rebin(extracted_sky_noise,
02388                                                parameters, context, 
02389                                                linetable, dispersion_int_sky,
02390                                                first_abs_order,
02391                                                last_abs_order,
02392                                                n_traces,
02393                                                true,
02394                                                rebinned_header),
02395                "Could not rebin sky noise");
02396     }
02397     
02398     /* Save rebinned spectrum + noise */
02399     if (DEBUG) 
02400     {
02401         const char *filename         = "";
02402         const char *filename_err     = "";
02403         const char *filename_sky     = "";
02404         const char *filename_sky_err = "";
02405         if (ff_method == FF_PIXEL)
02406         {
02407             filename         = "wxfb";
02408             filename_err     = "errwxfb";
02409             filename_sky     = "wxfsky";
02410             filename_sky_err = "errwxfsky";
02411         }
02412         else if (ff_method == FF_EXTRACT)
02413         {
02414             filename         =    "wfxb";
02415             filename_err     = "errwfxb";
02416             filename_sky     =    "wfxsky";
02417             filename_sky_err = "errwfxsky";
02418         }
02419         else if (ff_method == FF_NO)
02420         {
02421             filename         =    "wxb";
02422             filename_err     = "errwxb";
02423             filename_sky     =    "wxsky";
02424             filename_sky_err = "errwxsky";
02425         }
02426         else
02427         {
02428             passure( false, "Unknown ff_method: %d", ff_method);
02429         }
02430 
02431     check(uves_propertylist_copy_property_regexp(*rebinned_header,
02432                          backsubbed_header,
02433                          "^ESO ", 1),"error copying hierarch keys");
02434 
02435         
02436         check( uves_save_image_local("Rebinned spectrum",
02437                      filename, *rebinned_spectrum,
02438                      chip, -1, filename_window, *rebinned_header, true),
02439            "Error saving image");
02440         
02441         check( uves_save_image_local("Noise of rebinned spectrum", filename_err,
02442                      *rebinned_noise, chip, -1, filename_window, 
02443                      *rebinned_header, true),
02444            "Error saving image");
02445 
02446         if (extracted_sky != NULL)
02447         {
02448             check( uves_save_image_local("Rebinned sky", filename_sky,
02449                          rebinned_sky, chip, -1,
02450                          filename_window, *rebinned_header, true),
02451                "Error saving image");
02452             
02453             check( uves_save_image_local("Noise of rebinned sky",
02454                          filename_sky_err, 
02455                          rebinned_sky_noise, chip, -1,
02456                          filename_window, *rebinned_header, true),
02457                "Error saving image");
02458         }
02459     }
02460 
02461     /* We also need to produce the  rebinned-immediately-after-extraction
02462        (but non flat-fielded) spectrum,
02463        which is a product of the science recipe.
02464        This is trivial unless ff_method is FF_EXTRACT 
02465     */
02466     if (resampled_spectrum != NULL)   /* Not for sky windows */
02467     {
02468         if (ff_method == FF_EXTRACT)
02469         {
02470             /* Rebin the extracted spectrum (before flatfielding) */
02471             uves_msg("Rebinning pre-flatfielded spectrum");    
02472             
02473             uves_free_propertylist(rebinned_header);
02474             check( *resampled_spectrum = 
02475                uves_rebin(extracted_noff,
02476                       parameters, context, 
02477                       linetable, dispersion_int,
02478                       first_abs_order,
02479                       last_abs_order,
02480                       n_traces,
02481                                       false,
02482                       rebinned_header),
02483                "Could not rebin spectrum");
02484             
02485             if (DEBUG) {
02486 
02487                         check( uves_save_image_local("Rebinned, extracted spectrum", 
02488                                                      "wx", *resampled_spectrum, 
02489                                                      chip, -1, filename_window,
02490                                                      *rebinned_header, true),
02491                                "Error saving image");
02492                     }
02493 
02494         }
02495         else
02496         {
02497             check( *resampled_spectrum = cpl_image_duplicate(*rebinned_spectrum),
02498                "Error copying rebinned spectrum");
02499         }
02500     }
02501 
02502     /* Merge orders to 1D spectrum */
02503     if (extracted_sky != NULL)
02504     {
02505         uves_msg("Merging sky");
02506         check( *merged_sky = uves_merge_orders(rebinned_sky, rebinned_sky_noise,
02507                            *rebinned_header,
02508                            m_method,
02509                            n_traces,
02510                            merged_header,
02511                            &merged_sky_noise),
02512            "Error merging sky");
02513     }
02514     
02515     uves_msg("Merging spectrum");
02516     uves_free_propertylist(merged_header);
02517     check( *merged_spectrum = uves_merge_orders(*rebinned_spectrum, *rebinned_noise,
02518                         *rebinned_header,
02519                         m_method,
02520                         n_traces,
02521                         merged_header,
02522                         merged_noise),
02523        "Error merging orders");
02524 
02525     check(uves_propertylist_copy_property_regexp(*merged_header,
02526                          backsubbed_header,
02527                          "^ESO ", 0),"error copying hierarch keys");
02528 
02529     if (DEBUG)
02530     {
02531         check( uves_save_image_local("Merged spectrum", "m", *merged_spectrum, 
02532                      chip, -1, filename_window, *merged_header, true),
02533            "Error saving image");
02534         
02535         check( uves_save_image_local("Noise of merged spectrum", "errm",
02536                      *merged_noise, chip, -1,
02537                      filename_window, *merged_header, true),
02538            "Error saving image");
02539     }
02540     
02541     if (DEBUG && extracted_sky != NULL)
02542     {
02543         check( uves_save_image_local("Merged sky", "msky", *merged_sky, 
02544                      chip, -1,
02545                      filename_window, *merged_header, true),
02546            "Error saving image");
02547         
02548         check( uves_save_image_local("Noise of merged sky", "errmsky", 
02549                      merged_sky_noise, chip, -1,
02550                      filename_window, *merged_header, true),
02551            "Error saving image");
02552     }
02553 
02554   cleanup:
02555     uves_free_image(&extracted);
02556     uves_free_image(&extracted_noff);
02557     uves_free_image(&extracted_noise);
02558     uves_free_image(&extracted_sky);
02559     uves_free_image(&extracted_sky_noise);
02560     uves_free_image(&cosmic_image);
02561     uves_free_image(&blaze_ratio);
02562     uves_free_image(&weights);
02563     uves_polynomial_delete(&dispersion_int);
02564     uves_polynomial_delete(&dispersion_int_sky);
02565     uves_free_table(&poly_table);
02566     uves_free_propertylist(&extracted_header);
02567     uves_free_table(&profile_table);
02568     uves_free_image(&extracted_mf);
02569     uves_free_image(&extracted_mf_noise);
02570     uves_free_image(&rebinned_sky);
02571     uves_free_image(&rebinned_sky_noise);
02572     uves_free_image(&merged_sky_noise);
02573     
02574     uves_free_image(&temp_image);
02575     
02576     return cpl_error_get_code();
02577 }
02578 
02579                       
02580 /*----------------------------------------------------------------------------*/
02616 /*----------------------------------------------------------------------------*/
02617 
02618 static cpl_image *
02619 subtract_sky(cpl_image *rebinned_obj, cpl_image *rebinned_obj_noise,
02620          uves_propertylist *rebinned_obj_header,
02621          const cpl_image *rebinned_sky1, const cpl_image *rebinned_sky1_noise,
02622          const uves_propertylist *rebinned_sky1_header,
02623          const cpl_image *rebinned_sky2, const cpl_image *rebinned_sky2_noise,
02624          const uves_propertylist *rebinned_sky2_header,
02625          cpl_image **merged_obj, cpl_image **merged_obj_noise, 
02626          uves_propertylist *merged_obj_header,
02627          const cpl_image *merged_sky1, const cpl_image *merged_sky1_noise, 
02628          const uves_propertylist *merged_sky1_header,
02629          const cpl_image *merged_sky2, const cpl_image *merged_sky2_noise, 
02630          const uves_propertylist *merged_sky2_header,
02631          double obj_slit, double sky1_slit, double sky2_slit)
02632 {
02633     double wavestep;
02634     int norders;
02635 
02636     cpl_image *merged_sky = NULL;               /* Result */
02637     
02638     passure( rebinned_obj != NULL, " ");
02639     passure( rebinned_obj_noise != NULL, " ");
02640     passure( rebinned_obj_header != NULL, " ");
02641     passure( merged_obj != NULL, " ");
02642     passure( merged_obj_noise != NULL, " ");
02643     passure( merged_obj_header != NULL, " ");
02644     passure( *merged_obj != NULL, " ");
02645     passure( *merged_obj_noise != NULL, " ");
02646     /* Sky spectra may be NULL (if not extracted) */
02647 
02648     check( wavestep = uves_pfits_get_cdelt1(rebinned_obj_header), 
02649        "Error reading wavelength step");
02650     norders = cpl_image_get_size_y(rebinned_obj);
02651 
02652     /* Do some consistency checking
02653        (that 'wavestep' and 'norders' is same for all spectra) */
02654     assure((rebinned_sky1 == NULL || norders == cpl_image_get_size_y(rebinned_sky1)) &&
02655        (rebinned_sky2 == NULL || norders == cpl_image_get_size_y(rebinned_sky2)), 
02656        CPL_ERROR_ILLEGAL_INPUT,
02657        "Different number of orders in object/sky spectra: obj = %d, "
02658        "sky1 = %d, sky3 = %d",
02659        norders,
02660        cpl_image_get_size_y(rebinned_sky1),
02661        cpl_image_get_size_y(rebinned_sky2));
02662 
02663     if (rebinned_sky1 != NULL)
02664     {
02665         double wavestep1;
02666         check( wavestep1 = uves_pfits_get_cdelt1(rebinned_sky1_header), 
02667            "Error reading wavelength step");
02668         assure( fabs(wavestep1 - wavestep) / wavestep < 0.01, 
02669             CPL_ERROR_ILLEGAL_INPUT,
02670             "Different bin widths: sky1 = %f ; obj = %f", 
02671             wavestep1, wavestep);
02672     }
02673     if (rebinned_sky2 != NULL)
02674     {
02675         double wavestep2;
02676         check( wavestep2 = uves_pfits_get_cdelt1(rebinned_sky2_header),
02677            "Error reading wavelength step");
02678         assure( fabs(wavestep2 - wavestep) / wavestep < 0.01,
02679             CPL_ERROR_ILLEGAL_INPUT,
02680             "Different bin widths: sky3 = %f ; obj = %f",
02681             wavestep2, wavestep);
02682     }
02683 
02684     /* Subtract sky (rebinned spectrum) */
02685     {
02686     int order;
02687     for (order = 1; order <= norders; order++)
02688         {
02689         double obj_start   , obj_end;
02690         double sky1_start  , sky1_end;
02691         double sky2_start  , sky2_end;
02692         double common_start, common_end;
02693         
02694         check( obj_start = uves_pfits_get_wstart(rebinned_obj_header, order),
02695                "Error reading start wavelength for order #%d", order);
02696         check( obj_end   = uves_pfits_get_wend  (rebinned_obj_header, order),
02697                "Error reading end wavelength for order #%d", order);
02698         
02699         if (rebinned_sky1 != NULL)
02700             {
02701             check( sky1_start = 
02702                    uves_pfits_get_wstart(rebinned_sky1_header, order), 
02703                    "Error reading start wavelength for order #%d", order);
02704             check( sky1_end   = 
02705                    uves_pfits_get_wend  (rebinned_sky1_header, order), 
02706                    "Error reading end wavelength for order #%d", order);
02707             }
02708         else
02709             {
02710             sky1_start = obj_start;
02711             sky1_end   = obj_end;
02712             }
02713         
02714         if (rebinned_sky2 != NULL)
02715             {
02716             check( sky2_start = 
02717                    uves_pfits_get_wstart(rebinned_sky2_header, order), 
02718                    "Error reading start wavelength for order #%d", order);
02719             check( sky2_end   = 
02720                    uves_pfits_get_wend  (rebinned_sky2_header, order),
02721                    "Error reading end wavelength for order #%d", order);
02722             }
02723         else
02724             {
02725             sky2_start = obj_start;
02726             sky2_end   = obj_end;
02727             }
02728         
02729         check( merged_sky =
02730                subtract_sky_row(rebinned_obj , rebinned_obj_noise , 
02731                     obj_start , obj_end,  obj_slit,
02732                     rebinned_sky1, rebinned_sky1_noise,
02733                     sky1_start, sky1_end, sky1_slit,
02734                     rebinned_sky2, rebinned_sky2_noise, 
02735                     sky2_start, sky2_end, sky2_slit,
02736                     order, wavestep, &common_start, 
02737                     &common_end),
02738                "Could not subtract sky for rebinned spectrum order #%d", order);
02739         uves_free_image(&merged_sky);
02740         
02741         check( uves_pfits_set_wstart(rebinned_obj_header, order, common_start),
02742                "Error updating start wavelength for order #%d", order);
02743         check( uves_pfits_set_wend  (rebinned_obj_header, order, common_end  ), 
02744                "Error updating start wavelength for order #%d", order);
02745         }
02746     }
02747 
02748     /* Subtract sky (merged spectrum) */
02749     {
02750     double obj_start   , obj_end;
02751     double sky1_start  , sky1_end;
02752     double sky2_start  , sky2_end;
02753     double common_start, common_end;
02754     
02755     obj_start = uves_pfits_get_crval1(merged_obj_header);
02756     obj_end   = obj_start + wavestep * (cpl_image_get_size_x(*merged_obj) - 1);
02757     
02758     if (merged_sky1 != NULL)
02759         {
02760         sky1_start = uves_pfits_get_crval1(merged_sky1_header);
02761         sky1_end   = sky1_start +
02762             wavestep * (cpl_image_get_size_x(merged_sky1) - 1);
02763         }
02764     else
02765         {
02766         sky1_start = obj_start;
02767         sky1_end   = obj_end;
02768         }
02769     
02770     if (merged_sky2 != NULL)
02771         {
02772         sky2_start = uves_pfits_get_crval1(merged_sky2_header);
02773         sky2_end   = sky2_start + 
02774             wavestep * (cpl_image_get_size_x(merged_sky2) - 1);
02775         }
02776     else
02777         {
02778         sky2_start = obj_start;
02779         sky2_end   = obj_end;
02780         }
02781     
02782     /* Subtract sky for image row 1 (the only row in the image) */
02783     check(     merged_sky = subtract_sky_row(*merged_obj, *merged_obj_noise, 
02784                           obj_start , obj_end,  obj_slit,
02785                           merged_sky1, merged_sky1_noise,
02786                           sky1_start, sky1_end, sky1_slit,
02787                           merged_sky2, merged_sky2_noise,
02788                           sky2_start, sky2_end, sky2_slit,
02789                           1, wavestep, &common_start,
02790                           &common_end),
02791         "Error subtracting sky of merged spectrum");
02792     
02793     check( uves_pfits_set_crval1(merged_obj_header, common_start),
02794            "Could not update start wavelength");
02795     
02796     /* Make sure that the last bin corresponds to 'common_end' wavelength */
02797     check( uves_crop_image(merged_obj,
02798                    1, 1,
02799                    1 + uves_round_double((common_end - 
02800                               common_start)/wavestep), 
02801                    1),
02802            "Error cropping merged spectrum");
02803     
02804     check( uves_crop_image(merged_obj_noise,
02805                    1, 1,
02806                    1 + uves_round_double((common_end - 
02807                               common_start)/wavestep), 
02808                    1),
02809            "Error cropping merged spectrum noise");
02810     
02811     if (merged_sky != NULL)
02812         {
02813         /* The image header also applies for the sky */
02814         assure( cpl_image_get_size_x(merged_sky) == 
02815             cpl_image_get_size_x(*merged_obj), CPL_ERROR_ILLEGAL_OUTPUT,
02816             "Sky and object spectrum sizes differ, "
02817             "sky = %d bins, obj = %d bins", 
02818             cpl_image_get_size_x(merged_sky),
02819             cpl_image_get_size_x(*merged_obj));
02820         }
02821     }
02822     
02823   cleanup:
02824     if (cpl_error_get_code() != CPL_ERROR_NONE)
02825     {
02826         uves_free_image(&merged_sky);
02827     }
02828     return merged_sky;
02829 }    
02830 
02831 /*----------------------------------------------------------------------------*/
02863 /*----------------------------------------------------------------------------*/
02864 
02865 static cpl_image *
02866 subtract_sky_row(cpl_image *obj, cpl_image *obj_noise, 
02867          double obj_start, double obj_end, double obj_slit,
02868          const cpl_image *sky1, const cpl_image *sky1_noise, 
02869          double sky1_start, double sky1_end, double sky1_slit,
02870          const cpl_image *sky2, const cpl_image *sky2_noise, 
02871          double sky2_start, double sky2_end, double sky2_slit,
02872          int row, double wavestep, 
02873          double *common_start, double *common_end)
02874 {
02875     int first_bin_obj;
02876     int first_bin_sky1;
02877     int first_bin_sky2;
02878     int nbins;
02879     cpl_image *common_obj = NULL;     /* Extract the common wavelength range ... */
02880     cpl_image *common_sky1 = NULL;    /* ... to these 1D images                  */
02881     cpl_image *common_sky2 = NULL;
02882     cpl_image *common_obj_noise = NULL;
02883     cpl_image *common_sky1_noise = NULL;
02884     cpl_image *common_sky2_noise = NULL;
02885     bool is_good1, is_good2;          /* Do the two sky images contain valid pixels? */
02886 
02887     cpl_image *common_sky         = NULL;   /* The combined sky spectrum normalized
02888                            to object slit length (returned) */
02889     cpl_image *common_sky_noise   = NULL;
02890 
02891     cpl_image *temp               = NULL;
02892 
02893     *common_start = uves_max_double(obj_start, uves_max_double(sky1_start, sky2_start));
02894     *common_end   = uves_min_double(obj_end  , uves_min_double(sky1_end  , sky2_end  ));
02895 
02896     if (*common_start <= *common_end)
02897     {
02898         nbins = 1 + uves_round_double((*common_end - *common_start) / wavestep);
02899         
02900         uves_msg_debug("Lower sky range: %f - %f w.l.u.", sky1_start, sky1_end);
02901         uves_msg_debug("Upper sky range: %f - %f w.l.u.", sky2_start, sky2_end);
02902         uves_msg_debug("Object sky range: %f - %f w.l.u.", obj_start, obj_end);
02903         uves_msg_debug("Sky/object common wavelength range in order %d: "
02904                "%f - %f w.l.u. (%d bins)", 
02905                row, *common_start, *common_end, nbins);
02906         
02907         first_bin_obj  = 1 + uves_round_double((*common_start - obj_start )/wavestep);
02908         first_bin_sky1 = 1 + uves_round_double((*common_start - sky1_start)/wavestep);
02909         first_bin_sky2 = 1 + uves_round_double((*common_start - sky2_start)/wavestep);
02910         
02911         /* Extract common bins, normalize sky windows to object slit length */
02912         check( common_obj       = cpl_image_extract(obj, 
02913                             first_bin_obj, row,
02914                             first_bin_obj + nbins-1, row), 
02915            "Error extracting common rows (object)");
02916         
02917         check( common_obj_noise = cpl_image_extract(obj_noise, 
02918                             first_bin_obj, row,
02919                             first_bin_obj + nbins-1, row), 
02920            "Error extracting common rows (object noise)");
02921         
02922         if (sky1 != NULL)
02923         {
02924             check( common_sky1 =
02925                cpl_image_extract(sky1, 
02926                          first_bin_sky1, row,
02927                          first_bin_sky1 + nbins-1, row),
02928                "Error extracting common rows (lower sky)");
02929             
02930             check( common_sky1_noise =
02931                cpl_image_extract(sky1_noise, 
02932                          first_bin_sky1, row,
02933                          first_bin_sky1 + nbins-1, row), 
02934                "Error extracting common rows (lower sky noise)");
02935             
02936             check(( cpl_image_multiply_scalar(common_sky1      , obj_slit / sky1_slit),
02937                 cpl_image_multiply_scalar(common_sky1_noise, obj_slit / sky1_slit)),
02938                "Error normalizing sky flux");
02939             
02940             is_good1 = 
02941             cpl_image_count_rejected(common_sky1) <
02942             cpl_image_get_size_x(common_sky1)*
02943             cpl_image_get_size_y(common_sky1) &&  
02944             /* Note order of evaluation. cpl_image_get_min() would fail if
02945                there were no good pixels */
02946             cpl_image_get_min(common_sky1_noise) > 0;
02947         }
02948         else
02949         {
02950             is_good1 = false;
02951         }
02952         if (sky2 != NULL)
02953         {
02954             check( common_sky2       = cpl_image_extract(sky2, 
02955                                  first_bin_sky2, row,
02956                                  first_bin_sky2 + nbins-1, row), 
02957                "Error extracting common rows (upper sky)");
02958             
02959             check( common_sky2_noise = cpl_image_extract(sky2_noise, 
02960                                  first_bin_sky2, row,
02961                                  first_bin_sky2 + nbins-1, row), 
02962                "Error extracting common rows (upper sky noise)");
02963             
02964             check(( cpl_image_multiply_scalar(common_sky2      , obj_slit / sky2_slit),
02965                 cpl_image_multiply_scalar(common_sky2_noise, obj_slit / sky2_slit)),
02966                "Error normalizing sky flux");
02967 
02968             is_good2 = 
02969             cpl_image_count_rejected(common_sky2) <
02970             cpl_image_get_size_x(common_sky2)*
02971             cpl_image_get_size_y(common_sky2) &&  
02972             cpl_image_get_min(common_sky2_noise) > 0;
02973         }
02974         else
02975         {
02976             is_good2 = false;
02977         }
02978                
02979         
02980         /* Optimally average the two sky windows 
02981            (one of which might not have been extracted) */
02982         if (is_good1 && is_good2)
02983         {
02984                     check( common_sky = 
02985                            uves_average_images(common_sky1, common_sky1_noise,
02986                                                common_sky2, common_sky2_noise,
02987                                                &common_sky_noise),
02988                            "Error combining sky windows");
02989                 }
02990         else if (is_good1 && !is_good2)
02991         {
02992             common_sky       = cpl_image_duplicate(common_sky1);
02993             common_sky_noise = cpl_image_duplicate(common_sky1_noise);
02994         }
02995         else if (!is_good1 && is_good2)
02996         {
02997             common_sky       = cpl_image_duplicate(common_sky2);
02998             common_sky_noise = cpl_image_duplicate(common_sky2_noise);
02999         }
03000         else
03001         {
03002             common_sky = NULL;
03003         }
03004         
03005         if (common_sky != NULL)
03006         {   
03007             /* Do the subtraction, threshold to [0 ; oo [  */
03008       /* Commented out as we should not lower threshold to 0
03009             check(( cpl_image_subtract (common_obj, common_sky),
03010                 cpl_image_threshold(common_obj, 
03011                         0, DBL_MAX,
03012                         0, DBL_MAX)),
03013               "Error subtracting combined sky");
03014       */
03015             check(( cpl_image_subtract (common_obj, common_sky)),
03016               "Error subtracting combined sky");
03017             
03018             /*  Propagate noise:  
03019             obj_noise := sqrt( obj_noise^2 + sky_noise^2 )  */
03020             check(( cpl_image_power(common_obj_noise, 2),
03021                 cpl_image_power(common_sky_noise, 2),
03022                 cpl_image_add  (common_obj_noise, common_sky_noise),
03023                 cpl_image_power(common_obj_noise, 0.5)),
03024               "Error propagating noise during sky subtration");
03025             
03026             /* Copy results to relevant row of input spectrum */
03027             check(( cpl_image_copy(obj,
03028                        common_obj,
03029                        1, row),
03030                 cpl_image_copy(obj_noise,
03031                        common_obj_noise,
03032                        1, row)),
03033               "Error writing subtracted flux to row %d of spectrum", row);
03034         }
03035 
03036     } /* Object and both sky windows do have an overlap in this order */
03037     else
03038     {
03039         int x;
03040         
03041         uves_msg_low("Extracted object and sky spectra have no overlap in order #%d. "
03042              "Order marked as bad", row);
03043         
03044         for (x = 1; x <= cpl_image_get_size_x(obj); x++)
03045         {
03046             check(( cpl_image_reject(obj      , x, row),
03047                 cpl_image_reject(obj_noise, x, row)),
03048               "Error rejecting sky-subtracted spectrum "
03049               "at (x, row) = (%d, %d)", x, row);
03050         }
03051     }
03052     
03053   cleanup:
03054     uves_free_image(&common_obj);
03055     uves_free_image(&common_sky1);
03056     uves_free_image(&common_sky2);
03057     uves_free_image(&common_obj_noise);
03058     uves_free_image(&common_sky_noise);
03059     uves_free_image(&common_sky1_noise);
03060     uves_free_image(&common_sky2_noise);
03061     uves_free_image(&temp);
03062     if (cpl_error_get_code() != CPL_ERROR_NONE)
03063     {
03064         uves_free_image(&common_sky);
03065     }
03066     
03067     return common_sky;
03068 }
03069 
03070 /*----------------------------------------------------------------------------*/
03087 /*----------------------------------------------------------------------------*/
03088 static double get_offset(const cpl_image *back_subbed, 
03089              const cpl_table *ordertable, 
03090              const polynomial *order_locations,
03091              double search_range, int nsamples, double *doffset)
03092 {
03093     cpl_image *chunk = NULL;          /* Chunks                          */
03094     cpl_image *chunk_col = NULL;      /* Chunks median collapsed along x */
03095 
03096     int minorder, maxorder;
03097     int order, x, nx, ny;
03098     double sum = 0, sum_o = 0, sum_oo = 0;         /* Zero'th, first and 
03099                               second moment of offsets */
03100     int s_r_int = uves_round_double(search_range); /* Search range as an integer */
03101 
03102     passure( back_subbed != NULL, " ");
03103     passure( ordertable != NULL, " ");
03104     passure( order_locations != NULL, " ");
03105     /* doffset may be NULL */
03106     assure( nsamples >= 1, CPL_ERROR_ILLEGAL_INPUT, 
03107         "Illegal number of sample points per order: %d", nsamples);
03108 
03109     minorder = cpl_table_get_column_min(ordertable, "Order");
03110     maxorder = cpl_table_get_column_max(ordertable, "Order");
03111     nx = cpl_image_get_size_x(back_subbed);
03112     ny = cpl_image_get_size_y(back_subbed);
03113 
03114     sum    = 0;
03115     sum_o  = 0;
03116     sum_oo = 0;
03117     for (order = minorder; order <= maxorder; order++)
03118     {
03119         int stepx = nx / nsamples;
03120         
03121         for (x = stepx/2; x <= nx; x += stepx)
03122         {
03123             int y = uves_round_double(
03124             uves_polynomial_evaluate_2d(order_locations, x, order));
03125 
03126             if (1 <= y - s_r_int && y + s_r_int <= ny)
03127             {
03128                 double offset;
03129 
03130                 /* Get centroid.pos. of median collapsed window */
03131                 
03132                 chunk = 
03133                 cpl_image_extract(back_subbed,
03134                           uves_max_int(1 , x - stepx/2), 
03135                           y - s_r_int,
03136                           uves_min_int(nx, x + stepx/2),
03137                           y + s_r_int);
03138                 
03139                 chunk_col = 
03140                 /* Result is single column image */
03141                 cpl_image_collapse_median_create(chunk,
03142                                  1,
03143                                  0, 0); /* No filtering */
03144                 
03145                 
03146                 /* Offset in world coordinates: row=1 in 'chunk_col'
03147                    corresponds to row=(y - s_r_int) in 'back_subbed' */
03148                 offset = (y - s_r_int - 1) + 
03149                 cpl_image_get_centroid_y_window(chunk_col,
03150                                 1, 1,
03151                                 1, 
03152                                 cpl_image_get_size_y(chunk_col));
03153 
03154                 /* Get offset relative to slit center */
03155                 offset -= y;
03156 
03157                 uves_free_image(&chunk);
03158                 uves_free_image(&chunk_col);
03159                 
03160                 sum    += 1;
03161                 sum_o  += offset;
03162                 sum_oo += offset*offset;
03163             }
03164         }
03165     }
03166     
03167     /* This should never happen, but if it does 
03168        fail cleanly instead of dividing by zero */
03169     assure( sum > 0, CPL_ERROR_ILLEGAL_OUTPUT,
03170         "No evaluation points inside image!");
03171 
03172     if (doffset != NULL)
03173     {
03174         *doffset = sqrt(sum_oo/(1.0*sum) - 
03175                 (sum_o*sum_o) / (sum*1.0*sum));
03176     }
03177     
03178   cleanup:
03179     uves_free_image(&chunk);
03180     uves_free_image(&chunk_col);
03181 
03182     return (1.0*sum_o) / sum;
03183 }
03184 
03185 
03186 /*----------------------------------------------------------------------------*/
03208 /*----------------------------------------------------------------------------*/
03209 static cpl_image *
03210 uves_get_blaze_ratio(const cpl_image *spectrum,
03211              const cpl_image *spectrum_noise)
03212 {
03213     int nx, ny;
03214     int smooth_x, smooth_y;
03215 
03216     cpl_image *blaze_ratio       = NULL;
03217     cpl_image *blaze_ratio_noise = NULL;
03218 
03219     cpl_table *values = NULL;
03220     polynomial *p = NULL;
03221 
03222     passure( spectrum       != NULL, " ");
03223     passure( spectrum_noise != NULL, " ");
03224     
03225     nx = cpl_image_get_size_x(spectrum);
03226     ny = cpl_image_get_size_y(spectrum);
03227     
03228     blaze_ratio       = cpl_image_duplicate(spectrum);
03229     blaze_ratio_noise = cpl_image_duplicate(spectrum_noise);
03230     assure_mem( blaze_ratio );
03231     assure_mem( blaze_ratio_noise );
03232     
03233     /* Normalize each row in ratio to median = 1,
03234        so that the overall normalization doesn't change 
03235     */
03236 
03237 
03238     {
03239     int x, y;
03240     
03241     for (y = 1; y <= ny; y++)
03242         {
03243         double median = cpl_image_get_median_window(blaze_ratio,
03244                                 1, y,
03245                                 nx, y);
03246         
03247         if (median == 0)
03248             {
03249             /* The cpl_image_get_median_window function is broken;
03250                it doesn't take bad pixels into account. That sometimes
03251                leads to a zero median */
03252             
03253             /* This mostly happens for the first and last orders */
03254             
03255             double max_noise = cpl_image_get_max(blaze_ratio_noise);
03256 
03257             for (x = 1; x <= nx; x++)
03258                 {
03259                 cpl_image_set(blaze_ratio      , x, y, 1);
03260 
03261                 /* effectively exclude from fit: */
03262                 cpl_image_set(blaze_ratio_noise, x, y, max_noise);
03263                 }
03264             }
03265         else
03266             {
03267             /* Divide this row by median,
03268                Exclude pixels deviating more than a factor of, say, 5 */
03269             double exclude = 2;
03270 
03271             for (x = 1; x <= nx; x++)
03272                 {
03273                 int pis_rejected1, pis_rejected2;
03274                 double val1, val2;
03275                 
03276                 val1 = cpl_image_get(blaze_ratio      , 
03277                              x, y, &pis_rejected1);
03278                 val2 = cpl_image_get(blaze_ratio_noise,
03279                              x, y, &pis_rejected2);
03280                 
03281                 if (!pis_rejected1 && !pis_rejected2 && 
03282                     val1/median < exclude && val1/median > 1/exclude)
03283                     {
03284                     cpl_image_set(blaze_ratio      , 
03285                               x, y, val1 / median); 
03286                     cpl_image_set(blaze_ratio_noise, 
03287                               x, y, val2 / median); 
03288                     }
03289                 else
03290                     {
03291                     /* Set to 1, then reject. This is to deal with
03292                        a plotter that might plot bad pixels */
03293                     cpl_image_set   (blaze_ratio      , x, y, 1);
03294 
03295                     cpl_image_reject(blaze_ratio      , x, y);
03296                     cpl_image_reject(blaze_ratio_noise, x, y);
03297                     }
03298                 }
03299             }
03300         }
03301 
03302     uves_plot_image_rows(blaze_ratio, 1, ny, ny/10,
03303                  "x", "y", "ratio (normalized to 1)");
03304     }
03305 
03306     smooth_x = nx / 20 + 1;   /* >0 */
03307     smooth_y = ny / 20 + 1;   /* >0 */
03308     check( uves_filter_image_median(&blaze_ratio,
03309                     smooth_x, smooth_y,       /* x-radius, y-radius */
03310                     false),         /* extrapolate border pixels? */
03311        "Error creating smoothed ratio");
03312 
03313     uves_plot_image_rows(blaze_ratio, 1, ny, ny/10, "x", "y", "ratio (smoothed)");
03314     
03315 
03316     /* For each x, fit polynomial as function of y.
03317      * Use kappa-sigma clipping to eliminate single order
03318      * spectral featues. This should leave only the
03319      * systematics (i.e. the ratio of obj/flat blaze profiles)
03320      */
03321     {
03322     int x, y;
03323     
03324     for (x = 1; x <= nx; x++)
03325         {
03326         int current_row;
03327         /* Table rows are removed when kappa-sigma clipping,
03328            so we have to create a new table for each column */
03329 
03330         uves_free_table(&values);
03331         values = cpl_table_new(ny);
03332         cpl_table_new_column(values, "Y", CPL_TYPE_INT);
03333         cpl_table_new_column(values, "Ratio", CPL_TYPE_DOUBLE);
03334         cpl_table_new_column(values, "dRatio", CPL_TYPE_DOUBLE);
03335         
03336         assure_mem( values );
03337 
03338         current_row = 0;
03339         for (y = 1; y <= ny; y++)
03340             {
03341             double ratio, dratio;
03342             int pis_rejected1, pis_rejected2;
03343 
03344             ratio  = cpl_image_get(blaze_ratio      , 
03345                            x, y, &pis_rejected1);
03346             dratio = cpl_image_get(blaze_ratio_noise, 
03347                            x, y, &pis_rejected2);
03348 
03349             if (!pis_rejected1 && !pis_rejected2)
03350                 {
03351                 cpl_table_set_int   (values, "Y"     , 
03352                              current_row, y);
03353                 cpl_table_set_double(values, "Ratio" , 
03354                              current_row, ratio);
03355                 cpl_table_set_double(values, "dRatio", 
03356                              current_row, dratio);
03357                 current_row += 1;
03358                 }
03359             else
03360                 {
03361                 /* Ignore current order */
03362                 }
03363             }
03364         
03365         cpl_table_set_size(values, current_row);
03366         
03367         {
03368             int degree = 2;
03369             double kappa = 2;
03370             
03371             uves_polynomial_delete(&p);
03372             p = uves_polynomial_regression_1d(values,
03373                               "Y", "Ratio", "dRatio",
03374                               degree,
03375                               NULL, NULL, /* fit, residual^2 */
03376                               NULL,       /* mse             */
03377                               kappa);
03378 
03379             /* If fitting failed because there were too few points,
03380              * or matrix was singular */
03381             if (cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT || 
03382             cpl_error_get_code() == CPL_ERROR_ILLEGAL_OUTPUT)
03383             {
03384                 uves_error_reset();
03385                 
03386                 /* Then set p(x) = 1 
03387                  * by fitting a line through (1,1) - (2,1)
03388                  */
03389 
03390 
03391                 /* The table is in a 'dirty' state (contains
03392                    temporary columns) if fitting routine failed
03393                    (that routine is not exception safe),
03394                    so don't try to reuse current table */
03395                 
03396                 uves_free_table(&values);
03397                 values = cpl_table_new(2);
03398                 cpl_table_new_column(values, "Y", CPL_TYPE_INT);
03399                 cpl_table_new_column(values, "Ratio", CPL_TYPE_DOUBLE);
03400                 cpl_table_set_int   (values, "Y"     , 0, 1);
03401                 cpl_table_set_double(values, "Ratio" , 0, 1);
03402                 cpl_table_set_int   (values, "Y"     , 1, 2);
03403                 cpl_table_set_double(values, "Ratio" , 1, 1);
03404                 
03405                 degree = 2;
03406                 kappa = -1;
03407                 uves_polynomial_delete(&p);
03408                 p = uves_polynomial_regression_1d(values,
03409                                   "Y", "Ratio", NULL,
03410                                   degree,       
03411                                   NULL, NULL, /* fit, residual^2 */
03412                                   NULL,       /* mse             */
03413                                   kappa);                  
03414             }
03415 
03416             assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
03417                 "Could not fit %d. degree polynomial to column %d", degree, x);
03418 
03419         }
03420 
03421         for (y = 1; y <= ny; y++)
03422             {
03423             double interpolated = uves_polynomial_evaluate_1d(p, y);
03424             
03425             cpl_image_set(blaze_ratio, x, y, fabs(interpolated));
03426             }
03427 
03428         }
03429 
03430     /* post smooth */
03431     check( uves_filter_image_median(&blaze_ratio, 
03432                     2*smooth_x, 2*smooth_y, /* x-radius, y-radius */
03433                     false),                 /* extrapolate at border? */
03434            "Error creating smoothed ratio");
03435     
03436     uves_plot_image_rows(blaze_ratio, 1, ny, ny/10, "x", "y", "ratio (poly. fit)");
03437 
03438     }
03439 
03440     
03441     /*
03442       printf("ratio\n");
03443       cpl_stats_dump(cpl_stats_new_from_image(ratio, CPL_STATS_ALL), CPL_STATS_ALL, stdout);
03444       printf("image\n");
03445       cpl_stats_dump(cpl_stats_new_from_image(image, CPL_STATS_ALL), CPL_STATS_ALL, stdout);
03446       printf("noise\n");
03447       cpl_stats_dump(cpl_stats_new_from_image(noise, CPL_STATS_ALL), CPL_STATS_ALL, stdout);
03448       
03449       passure(false, "");
03450       
03451     */
03452 
03453   cleanup:
03454     uves_free_table(&values);
03455     uves_polynomial_delete(&p);
03456     uves_free_image(&blaze_ratio_noise);
03457 
03458     if (cpl_error_get_code() != CPL_ERROR_NONE)
03459     {
03460         uves_free_image(&blaze_ratio);
03461     }
03462     return blaze_ratio;
03463 }
03464 
03465 

Generated on Mon Apr 21 10:56:56 2008 for UVES Pipeline Reference Manual by  doxygen 1.5.1