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

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