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

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