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

Generated on 9 Mar 2012 for UVES Pipeline Reference Manual by  doxygen 1.6.1