KMOS Pipeline Reference Manual  1.2.0
kmo_flat.c
00001 /* $Id: kmo_flat.c,v 1.43 2013/06/17 07:52:26 aagudo Exp $
00002  *
00003  * This file is part of the KMOS Pipeline
00004  * Copyright (C) 2002,2003 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 /*
00022  * $Author: aagudo $
00023  * $Date: 2013/06/17 07:52:26 $
00024  * $Revision: 1.43 $
00025  * $Name: HEAD $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 /*-----------------------------------------------------------------------------
00033  *                              Includes
00034  *----------------------------------------------------------------------------*/
00035 #include <string.h>
00036 #include <math.h>
00037 
00038 #include <cpl.h>
00039 
00040 #include "kmo_utils.h"
00041 #include "kmo_priv_flat.h"
00042 #include "kmo_priv_functions.h"
00043 #include "kmo_dfs.h"
00044 #include "kmo_error.h"
00045 #include "kmo_constants.h"
00046 #include "kmo_cpl_extensions.h"
00047 #include "kmo_debug.h"
00048 
00049 /*-----------------------------------------------------------------------------
00050  *                          Functions prototypes
00051  *----------------------------------------------------------------------------*/
00052 
00053 static int kmo_flat_create(cpl_plugin *);
00054 static int kmo_flat_exec(cpl_plugin *);
00055 static int kmo_flat_destroy(cpl_plugin *);
00056 static int kmo_flat(cpl_parameterlist *, cpl_frameset *);
00057 
00058 /*-----------------------------------------------------------------------------
00059  *                          Static variables
00060  *----------------------------------------------------------------------------*/
00061 
00062 static char kmo_flat_description[15000] =
00063 "This recipe creates the master flat field and calibration frames needed for \n"
00064 "spatial calibration for all three detectors. It must be called after the \n"
00065 "kmo_dark-recipe, which generates a bad pixel mask (badpixel_dark.fits). The \n"
00066 "bad pixel mask will be updated in this recipe (goes into badpixel_flat.fits).\n"
00067 "As input at least 3 dark frames, 3 frames with the flat lamp on are recommen-\n"
00068 "ded. Additionally a badpixel mask from kmo_dark is required.\n"
00069 "\n"
00070 "The badpixel mask contains 0 for bad pixels and 1 for good ones.\n"
00071 "\n"
00072 "The structure of the resulting xcal and ycal frames is quite complex since the\n"
00073 "arrangement of the IFUs isn't just linear on the detector. Basically the inte-\n"
00074 "ger part of the calibration data shows the offset of each pixels centre in mas\n"
00075 "(Milli arcsec) from the field centre. The viewing of an IFU is 2800mas \n"
00076 "(14pix*0.2arcsec/pix). So the values in these two frames will vary between \n"
00077 "+/-1500 (One would expect 1400, but since the slitlets aren't expected to be \n"
00078 "exactly vertical, the values can even go up to around 1500). Additionally in \n"
00079 "the calibration data in y-direction the decimal part of the data designates \n"
00080 "the IFU to which the slitlet corresponds to (for each detector from 1 to 8).\n"
00081 "Because of the irregular arrangement of the IFUs not all x-direction calibra-\n"
00082 "tion data is found in xcal and similarly not all y-direction calibration data\n"
00083 "is located in ycal. For certain IFUs they are switched and/or flipped in x- or\n"
00084 "y-direction:\n"
00085 "For IFUs 1,2,3,4,13,14,15,16:      x- and y- data is switched\n"
00086 "For IFUs 17,18,19,20:          y-data is flipped \n"
00087 "For IFUs 21,22,23,24:          x-data is flipped \n"
00088 "For IFUs 5,6,7,8,9,10,11,12:       x- and y- data is switched and x- and \n"
00089 "                                      y- data is flipped\n"
00090 "\n"
00091 "Furthermore frames can be provided for several rotator angles. In this case\n"
00092 "the resulting calibration frames for each detector are repeatedly saved as \n"
00093 "extension for every angle.\n"
00094 "\n"
00095 "Advanced features:\n"
00096 "------------------\n"
00097 "To create the badpixel mask the edges of all slitlets are fitted to a polyno-\n"
00098 "mial. Since it can happen that some of these fits (3 detectors * 8 IFUs * \n"
00099 "14slitlets * 2 edges  (left and right edge of slitlet)= 672 edges) fail, the\n"
00100 "fit parameters are themselves fitted again to detect any outliers. By default\n"
00101 "the parameters of all left and all right edges are grouped individually and \n"
00102 "then fitted using chebyshev polynomials. The advantage of a chebyshev polyno-\n"
00103 "mial is, that it consists in fact of a series of orthogonal polynomials. This\n"
00104 "implies that the parameters of the polynomials are independent. This fact pre-\n"
00105 "destines the use of chebyshev polynomials for our case. So each individual pa-\n"
00106 "rameter can be examined independently. The reason why the left and right edges\n"
00107 "are fitted individually is that there is a systematic pattern specific to \n"
00108 "these groups. The reason for this pattern is probably to be found in the opti-\n"
00109 "cal path the light is traversing.\n"
00110 "\n"
00111 "The behaviour of this fitting step can be influenced via environment parameters:\n"
00112 "* KF_ALLPARS (default: 1)\n"
00113 "  When set to 1 all coefficients of the polynomial of an edge are to be cor-\n"
00114 "  rected, also when just one of these coefficients is an outlier. When set to\n"
00115 "  0 only the outlier is to be corrected.\n"
00116 "* KF_CH (default: 1)\n"
00117 "  When set to 1 chebyshev polynomials are used to fit the fitted parameters.\n"
00118 "  When set to 0 normal polynomials are used.\n"
00119 "* KF_SIDES (default: 2)\n"
00120 "  This variable can either be set to 1 or 2. When set to 2 the left and right\n"
00121 "  edges are examined individually. When set to 1 all edges are examined as one\n"
00122 "  group.\n"
00123 "* KF_FACTOR(default: 4)\n"
00124 "  This factor defines the threshold factor. All parameters deviating \n"
00125 "  KF_FACTOR*stddev are to be corrected\n"
00126 "\n"
00127 "BASIC PARAMETERS:\n"
00128 "-----------------\n"
00129 "--badpix_thresh\n"
00130 "The threshold level to mark pixels as bad on the dark subtracted frames [%]"
00131 "\n"
00132 "--surrounding_pixels\n"
00133 "The amount of bad pixels to surround a specific pixel, to let it be marked\n"
00134 "bad as well.\n"
00135 "\n"
00136 "--cmethod\n"
00137 "Following methods of frame combination are available:\n"
00138 "   * 'ksigma' (Default)\n"
00139 "   An iterative sigma clipping. For each position all pixels in the spectrum\n"
00140 "   are examined. If they deviate significantly, they will be rejected according\n"
00141 "   to the conditions:\n"
00142 "       val > mean + stdev * cpos_rej\n"
00143 "   and\n"
00144 "       val < mean - stdev * cneg_rej\n"
00145 "   where --cpos_rej, --cneg_rej and --citer are the corresponding configuration\n"
00146 "   parameters. In the first iteration median and percentile level are used.\n"
00147 "\n"
00148 "   * 'median'\n"
00149 "   At each pixel position the median is calculated.\n"
00150 "\n"
00151 "   * 'average'\n"
00152 "   At each pixel position the average is calculated.\n"
00153 "\n"
00154 "   * 'sum'\n"
00155 "   At each pixel position the sum is calculated.\n"
00156 "\n"
00157 "   * 'min_max'\n"
00158 "   The specified number of minimum and maximum pixel values will be rejected.\n"
00159 "   --cmax and --cmin apply to this method.\n"
00160 "\n"
00161 "ADVANCED PARAMETERS\n"
00162 "-------------------\n"
00163 "--cpos_rej\n"
00164 "--cneg_rej\n"
00165 "--citer\n"
00166 "see --cmethod='ksigma'\n"
00167 "\n"
00168 "--cmax\n"
00169 "--cmin\n"
00170 "see --cmethod='min_max'\n"
00171 "\n"
00172 "--suppress_extension\n"
00173 "If set to TRUE, the arbitrary filename extensions are supressed. If multiple\n"
00174 "products with the same category are produced, they will be numered consecutively\n"
00175 "starting from 0.\n"
00176 "\n"
00177 "-------------------------------------------------------------------------------\n"
00178 "  Input files:\n"
00179 "\n"
00180 "   DO                    KMOS                                                  \n"
00181 "   category              Type   Explanation                    Required #Frames\n"
00182 "   --------              -----  -----------                    -------- -------\n"
00183 "   FLAT_ON               RAW    Flatlamp-on exposures             Y       1-n  \n"
00184 "                                (at least 3 frames recommended)                \n"
00185 "   FLAT_OFF              RAW    Flatlamp-off exposures            Y       1-n  \n"
00186 "                                (at least 3 frames recommended)                \n"
00187 "   BADPIXEL_DARK         B2D    Bad pixel mask                    Y        1   \n"
00188 "\n"
00189 "  Output files:\n"
00190 "\n"
00191 "   DO                    KMOS\n"
00192 "   category              Type   Explanation\n"
00193 "   --------              -----  -----------\n"
00194 "   MASTER_FLAT           F2D    Normalised flat field\n"
00195 "                                (6 extensions: alternating data & noise\n"
00196 "   BADPIXEL_FLAT         B2D    Updated bad pixel mask (3 Extensions)\n"
00197 "   XCAL                  F2D    Calibration frame 1 (3 Extensions)\n"
00198 "   YCAL                  F2D    Calibration frame 2 (3 Extensions)\n"
00199 "   FLAT_EDGE             F2L    Frame containing parameters of fitted \n"
00200 "                                slitlets of all IFUs of all detectors\n"
00201 "-------------------------------------------------------------------------------"
00202 "\n";
00203 
00204 /*-----------------------------------------------------------------------------
00205  *                              Functions code
00206  *----------------------------------------------------------------------------*/
00224 int cpl_plugin_get_info(cpl_pluginlist *list)
00225 {
00226     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00227     cpl_plugin *plugin = &recipe->interface;
00228 
00229     cpl_plugin_init(plugin,
00230                         CPL_PLUGIN_API,
00231                         KMOS_BINARY_VERSION,
00232                         CPL_PLUGIN_TYPE_RECIPE,
00233                         "kmo_flat",
00234                         "Create master flatfield frame and badpixel map to be "
00235                         "used during science reduction",
00236                         kmo_flat_description,
00237                         "Alex Agudo Berbel",
00238                         "kmos-spark@mpe.mpg.de",
00239                         kmos_get_license(),
00240                         kmo_flat_create,
00241                         kmo_flat_exec,
00242                         kmo_flat_destroy);
00243 
00244     cpl_pluginlist_append(list, plugin);
00245 
00246     return 0;
00247 }
00248 
00256 static int kmo_flat_create(cpl_plugin *plugin)
00257 {
00258     cpl_recipe *recipe;
00259     cpl_parameter *p;
00260 
00261     // Check that the plugin is part of a valid recipe
00262     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00263         recipe = (cpl_recipe *)plugin;
00264     else
00265         return -1;
00266 
00267     // Create the parameters list in the cpl_recipe object
00268     recipe->parameters = cpl_parameterlist_new();
00269 
00270     // Fill the parameters list
00271     // --badpix_thresh
00272     p = cpl_parameter_new_value("kmos.kmo_flat.badpix_thresh",
00273                                 CPL_TYPE_INT,
00274                                 "The threshold level to mark pixels as bad on "
00275                                 "the dark subtracted frames [%].",
00276                                 "kmos.kmo_flat",
00277                                 35);
00278     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "badpix_thresh");
00279     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00280     cpl_parameterlist_append(recipe->parameters, p);
00281 
00282     // --surrounding_pixels
00283     p = cpl_parameter_new_value("kmos.kmo_flat.surrounding_pixels",
00284                                 CPL_TYPE_INT,
00285                                 "The amount of bad pixels to surround a specific "
00286                                 "pixel, to let it be marked bad as well.",
00287                                 "kmos.kmo_flat",
00288                                 5);
00289     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "surrounding_pixels");
00290     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00291     cpl_parameterlist_append(recipe->parameters, p);
00292 
00293     /* --suppress_extension */
00294     p = cpl_parameter_new_value("kmos.kmo_flat.suppress_extension",
00295                                 CPL_TYPE_BOOL,
00296                                 "Suppress arbitrary filename extension."
00297                                 "(TRUE (apply) or FALSE (don't apply)",
00298                                 "kmos.kmo_flat",
00299                                 FALSE);
00300     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
00301     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00302     cpl_parameterlist_append(recipe->parameters, p);
00303 
00304     return kmo_combine_pars_create(recipe->parameters,
00305                                    "kmos.kmo_flat",
00306                                    DEF_REJ_METHOD,
00307                                    FALSE);
00308 }
00309 
00315 static int kmo_flat_exec(cpl_plugin *plugin)
00316 {
00317     cpl_recipe  *recipe;
00318 
00319     // Get the recipe out of the plugin
00320     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00321         recipe = (cpl_recipe *)plugin;
00322     else return -1;
00323 
00324     return kmo_flat(recipe->parameters, recipe->frames);
00325 }
00326 
00332 static int kmo_flat_destroy(cpl_plugin *plugin)
00333 {
00334     cpl_recipe *recipe;
00335 
00336     // Get the recipe out of the plugin
00337     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00338         recipe = (cpl_recipe *)plugin;
00339     else return -1 ;
00340 
00341     cpl_parameterlist_delete(recipe->parameters);
00342     return 0 ;
00343 }
00344 
00359 static int kmo_flat(cpl_parameterlist *parlist, cpl_frameset *frameset)
00360 {
00361     cpl_imagelist    *det_lamp_on           = NULL,
00362                      *det_lamp_off          = NULL;
00363 
00364     cpl_image        *img_in                = NULL,
00365                      *combined_data_on      = NULL,
00366                      *combined_noise_on     = NULL,
00367                      *combined_data_off     = NULL,
00368                      *combined_noise_off    = NULL,
00369                      *bad_pix_mask_flat     = NULL,
00370                      *bad_pix_mask_dark     = NULL,
00371                      *xcal                  = NULL,
00372                      *ycal                  = NULL;
00373 
00374     cpl_image        **stored_flat          = NULL,
00375                      **stored_noise         = NULL,
00376                      **stored_badpix        = NULL,
00377                      **stored_xcal          = NULL,
00378                      **stored_ycal          = NULL;
00379 
00380     cpl_frame        *frame                 = NULL;
00381     cpl_frameset     ** angle_frameset     = NULL;
00382 
00383     int              ret_val                = 0,
00384                      nr_devices             = 0,
00385                      sx                     = 0,
00386                      cmax                   = 0,
00387                      cmin                   = 0,
00388                      citer                  = 0,
00389                      surrounding_pixels     = 0,
00390                      badpix_thresh          = 0,
00391                      nx                     = 0,
00392                      ny                     = 0,
00393                      nz                     = 0,
00394                      *stored_qc_flat_sat    = NULL,
00395                      *bounds                = NULL,
00396                      **total_bounds         = NULL,
00397                      ***all_bounds          = NULL,
00398                      nr_bad_pix             = 0,
00399                      ndit                   = 0,
00400                      suppress_extension     = FALSE,
00401                      nr_sat                 = 0,
00402                      nr_angles              = 0,
00403                      i = 0, ii = 0, j = 0, ax = 0, a = 0;
00404 
00405     double           cpos_rej               = 0.0,
00406                      cneg_rej               = 0.0,
00407                      gain                   = 0.0,
00408                      exptime                = 0.0,
00409                      *stored_qc_flat_eff    = NULL,
00410                      *stored_qc_flat_sn     = NULL,
00411                      mean_data              = 0.0,
00412                      mean_noise             = 0.0,
00413                      *stored_gapmean        = NULL,
00414                      *stored_gapsdv         = NULL,
00415                      *stored_gapmaxdev      = NULL,
00416                      *stored_slitmean       = NULL,
00417                      *stored_slitsdv        = NULL,
00418                      *stored_slitmaxdev     = NULL;
00419 
00420     const char       *cmethod               = NULL;
00421 
00422     char             *extname               = NULL,
00423                      filename_flat[256],
00424                      filename_xcal[256],
00425                      filename_ycal[256],
00426                      filename_bad[256],
00427                      filename_edge[256],
00428                      *suffix                = NULL,
00429                      *fn_suffix             = NULL,
00430                      *tmpstr                = NULL,
00431                      *readmode              = NULL;
00432 
00433     cpl_propertylist *main_header           = NULL,
00434                      *main_header_xcal      = NULL,
00435                      *sub_header            = NULL;
00436 
00437     cpl_table       ***edge_table           = NULL;
00438 
00439     main_fits_desc   desc1, desc2;
00440 
00441     cpl_array        **unused_ifus_before   = NULL,
00442                      **unused_ifus_after    = NULL;
00443 
00444     cpl_error_code  *spec_found             = NULL;
00445 
00446     char            *fn_flat   = "flat_tmp.fits",
00447                     *fn_noise  = "flat_noise_tmp.fits",
00448                     *fn_badpix = "badpix_tmp.fits";
00449     unsigned int    save_mode = CPL_IO_CREATE; // at first files must be created
00450 
00451     KMO_TRY
00452     {
00453 
00454         kmo_init_fits_desc(&desc1);
00455         kmo_init_fits_desc(&desc2);
00456 
00457 // #############################################################################
00458 // ###           check inputs
00459 // #############################################################################
00460         KMO_TRY_ASSURE((parlist != NULL) &&
00461                        (frameset != NULL),
00462                        CPL_ERROR_NULL_INPUT,
00463                        "Not all input data is provided!");
00464 
00465         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, FLAT_OFF) >= 1,
00466                        CPL_ERROR_NULL_INPUT,
00467                        "At least 1 FLAT_OFF frame must be provided (3 or more "
00468                        "recommended)!");
00469 
00470         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, FLAT_ON) >= 1,
00471                        CPL_ERROR_NULL_INPUT,
00472                        "At least 1 FLAT_ON frame must be provided (3 or more "
00473                        "recommended)!");
00474 
00475         if (cpl_frameset_count_tags(frameset, FLAT_OFF) < 3) {
00476             cpl_msg_warning(cpl_func, "It is recommended to provide at least "
00477                                       "3 FLAT_OFF frames (Generated noise frames"
00478                                       " will not be very representative)!");
00479         }
00480 
00481         if (cpl_frameset_count_tags(frameset, FLAT_ON) < 3) {
00482             cpl_msg_warning(cpl_func, "It is recommended to provide at least "
00483                                       "3 FLAT_ON frames (Generated noise frames"
00484                                       " will not be very representative)!");
00485         }
00486 
00487         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, BADPIXEL_DARK) == 1,
00488                        CPL_ERROR_NULL_INPUT,
00489                        "A BADPIXEL_DARK frame must be provided!");
00490 
00491         KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_flat") == 1,
00492                        CPL_ERROR_ILLEGAL_INPUT,
00493                        "Cannot identify RAW and CALIB frames!");
00494 
00495 
00496         //
00497         // ------------ get parameters ------------
00498         //
00499         cpl_msg_info("", "--- Parameter setup for kmo_flat ----------");
00500 
00501         surrounding_pixels = kmo_dfs_get_parameter_int(parlist,
00502                                          "kmos.kmo_flat.surrounding_pixels");
00503         KMO_TRY_CHECK_ERROR_STATE();
00504         KMO_TRY_EXIT_IF_ERROR(
00505             kmo_dfs_print_parameter_help(parlist,
00506                                          "kmos.kmo_flat.surrounding_pixels"));
00507 
00508         badpix_thresh = kmo_dfs_get_parameter_int(parlist,
00509                                          "kmos.kmo_flat.badpix_thresh");
00510         KMO_TRY_CHECK_ERROR_STATE();
00511         KMO_TRY_EXIT_IF_ERROR(
00512             kmo_dfs_print_parameter_help(parlist,
00513                                          "kmos.kmo_flat.badpix_thresh"));
00514 
00515         suppress_extension = kmo_dfs_get_parameter_bool(parlist,
00516                                           "kmos.kmo_flat.suppress_extension");
00517         KMO_TRY_CHECK_ERROR_STATE();
00518         KMO_TRY_EXIT_IF_ERROR(
00519             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_flat.suppress_extension"));
00520 
00521         KMO_TRY_ASSURE((suppress_extension == TRUE) || (suppress_extension == FALSE),
00522                        CPL_ERROR_ILLEGAL_INPUT,
00523                        "suppress_extension must be TRUE or FALSE!");
00524 
00525         KMO_TRY_EXIT_IF_ERROR(
00526             kmo_combine_pars_load(parlist, "kmos.kmo_flat", &cmethod, &cpos_rej,
00527                                   &cneg_rej, &citer, &cmin, &cmax,
00528                                   FALSE));
00529 
00530         cpl_msg_info("", "-------------------------------------------");
00531 
00532         // check BADPIXEL_DARK
00533         KMO_TRY_EXIT_IF_NULL(
00534             frame = kmo_dfs_get_frame(frameset, BADPIXEL_DARK));
00535 
00536         desc2 = kmo_identify_fits_header(
00537                     cpl_frame_get_filename(frame));
00538         KMO_TRY_CHECK_ERROR_STATE();
00539 
00540         KMO_TRY_ASSURE((desc2.nr_ext == 3) &&
00541                        (desc2.ex_badpix == TRUE) &&
00542                        (desc2.fits_type == b2d_fits) &&
00543                        (desc2.frame_type == detector_frame),
00544                        CPL_ERROR_ILLEGAL_INPUT,
00545                        "BADPIXEL_DARK isn't in the correct format!!!");
00546 
00547         nx = desc2.naxis1;
00548         ny = desc2.naxis2;
00549         nz = desc2.naxis3;
00550 
00551         KMO_TRY_ASSURE((surrounding_pixels >= 0) && (surrounding_pixels <= 8),
00552                        CPL_ERROR_ILLEGAL_INPUT,
00553                        "surrounding_pixels must be between 0 and 8!");
00554 
00555         KMO_TRY_ASSURE((badpix_thresh >= 0) && (badpix_thresh <= 100),
00556                        CPL_ERROR_ILLEGAL_INPUT,
00557                        "badpix_thresh must be between 0 and 100%%!");
00558 
00559         //
00560         // ------------ check EXPTIME, NDIT, READMODE and lamps ------------
00561         //    EXPTIME, NDIT, READMODE: the same for all frames
00562         //    lamps:   at least 3 lamp on and 3 lamp off frames
00563         //
00564         KMO_TRY_EXIT_IF_NULL(
00565             frame = kmo_dfs_get_frame(frameset, FLAT_OFF));
00566 
00567         KMO_TRY_EXIT_IF_NULL(
00568             main_header = kmclipm_propertylist_load(
00569                                          cpl_frame_get_filename(frame), 0));
00570 
00571         ndit = cpl_propertylist_get_int(main_header, NDIT);
00572         KMO_TRY_CHECK_ERROR_STATE("NDIT keyword in main header "
00573                                   "missing!");
00574 
00575         exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00576         KMO_TRY_CHECK_ERROR_STATE("EXPTIME keyword in main header "
00577                                   "missing!");
00578 
00579         readmode = cpl_strdup(cpl_propertylist_get_string(main_header, READMODE));
00580         KMO_TRY_CHECK_ERROR_STATE("ESO DET READ CURNAME keyword in main "
00581                                   "header missing!");
00582 
00583         cpl_propertylist_delete(main_header); main_header = NULL;
00584 
00585         while (frame != NULL) {
00586             KMO_TRY_EXIT_IF_NULL(
00587                 main_header = kmclipm_propertylist_load(
00588                                              cpl_frame_get_filename(frame), 0));
00589 
00590             KMO_TRY_ASSURE(cpl_propertylist_get_int(main_header, NDIT) == ndit,
00591                            CPL_ERROR_ILLEGAL_INPUT,
00592                            "NDIT isn't the same for all frames: (is %d and %d).",
00593                             cpl_propertylist_get_int(main_header, NDIT), ndit);
00594 
00595             KMO_TRY_ASSURE(cpl_propertylist_get_double(main_header, EXPTIME) == exptime,
00596                            CPL_ERROR_ILLEGAL_INPUT,
00597                            "EXPTIME isn't the same for all frames: (is %g and %g).",
00598                            cpl_propertylist_get_double(main_header, EXPTIME), exptime);
00599 
00600             KMO_TRY_ASSURE(strcmp(cpl_propertylist_get_string(main_header, READMODE), readmode) == 0,
00601                            CPL_ERROR_ILLEGAL_INPUT,
00602                            "ESO DET READ CURNAME isn't the same for all frames: (is %s and %s).",
00603                            cpl_propertylist_get_string(main_header, READMODE), readmode);
00604 
00605             desc1 = kmo_identify_fits_header(
00606                         cpl_frame_get_filename(frame));
00607             KMO_TRY_CHECK_ERROR_STATE_MSG(
00608                     cpl_sprintf("File (%s) doesn't seem to be in KMOS-format!",
00609                             cpl_frame_get_filename(frame)));
00610 
00611             KMO_TRY_ASSURE(desc1.fits_type == raw_fits,
00612                            CPL_ERROR_ILLEGAL_INPUT,
00613                            "File hasn't correct data type "
00614                            "(%s must be a raw, unprocessed file)!",
00615                            cpl_frame_get_filename(frame));
00616 
00617             KMO_TRY_ASSURE((desc1.naxis1 == nx) &&
00618                            (desc1.naxis2 == ny) &&
00619                            (desc1.naxis3 == nz),
00620                            CPL_ERROR_ILLEGAL_INPUT,
00621                            "File (%s) hasn't correct dimensions! (x,y): "
00622                            "(%d,%d) vs (%d,%d)",
00623                            cpl_frame_get_filename(frame),
00624                            desc1.naxis1, desc1.naxis2, nx, ny);
00625 
00626             // assure that arc lamps are off
00627             KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP1_ST) == FALSE) &&
00628                            (kmo_check_lamp(main_header, INS_LAMP2_ST) == FALSE),
00629                            CPL_ERROR_ILLEGAL_INPUT,
00630                            "Arc lamps must be switched off (%s)!",
00631                            cpl_frame_get_filename(frame));
00632 
00633             // check if flat lamps are off
00634             if ((kmo_check_lamp(main_header, INS_LAMP3_ST) == TRUE) ||
00635                 (kmo_check_lamp(main_header, INS_LAMP4_ST) == TRUE))
00636             {
00637                 if (!(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT1 ID"), "Block") == 0) ||
00638                     !(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT2 ID"), "Block") == 0) ||
00639                     !(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT3 ID"), "Block") == 0))
00640                 {
00641                     cpl_msg_warning("","At least one flat lamp is on! Check if the lamps are blocked!");
00642                 }
00643             }
00644 
00645             kmo_free_fits_desc(&desc1);
00646 
00647             // get next FLAT_OFF frame
00648             frame = kmo_dfs_get_frame(frameset, NULL);
00649             KMO_TRY_CHECK_ERROR_STATE();
00650 
00651             cpl_propertylist_delete(main_header); main_header = NULL;
00652         }
00653 
00654         KMO_TRY_EXIT_IF_NULL(
00655             frame = kmo_dfs_get_frame(frameset, FLAT_ON));
00656 
00657         // uncomment this if these keywords can be different for FLAT_OFF and FLAT_ON
00658 //        KMO_TRY_EXIT_IF_NULL(
00659 //            main_header = kmclipm_propertylist_load(
00660 //                                         cpl_frame_get_filename(frame), 0));
00661 //        ndit = cpl_propertylist_get_int(main_header, NDIT);
00662 //        KMO_TRY_CHECK_ERROR_STATE("NDIT keyword in main header "
00663 //                                  "missing!");
00664 //        exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00665 //        KMO_TRY_CHECK_ERROR_STATE("EXPTIME keyword in main header "
00666 //                                  "missing!");
00667 
00668 //        readmode = cpl_propertylist_get_string(main_header, READMODE);
00669 //        KMO_TRY_CHECK_ERROR_STATE("ESO DET READ CURNAME keyword in main "
00670 //                                  "header missing!");
00671 
00672 //        cpl_propertylist_delete(main_header); main_header = NULL;
00673 
00674         while (frame != NULL) {
00675             KMO_TRY_EXIT_IF_NULL(
00676                 main_header = kmclipm_propertylist_load(
00677                                              cpl_frame_get_filename(frame), 0));
00678 
00679             KMO_TRY_ASSURE(cpl_propertylist_get_int(main_header, NDIT) == ndit,
00680                            CPL_ERROR_ILLEGAL_INPUT,
00681                            "NDIT isn't the same for all frames: (is %d and %d).",
00682                             cpl_propertylist_get_int(main_header, NDIT), ndit);
00683 
00684             KMO_TRY_ASSURE(cpl_propertylist_get_double(main_header, EXPTIME) == exptime,
00685                            CPL_ERROR_ILLEGAL_INPUT,
00686                            "EXPTIME isn't the same for all frames: (is %g and %g).",
00687                            cpl_propertylist_get_double(main_header, EXPTIME), exptime);
00688 
00689             KMO_TRY_ASSURE(strcmp(cpl_propertylist_get_string(main_header, READMODE), readmode) == 0,
00690                            CPL_ERROR_ILLEGAL_INPUT,
00691                            "ESO DET READ CURNAME isn't the same for all frames: (is %s and %s).",
00692                            cpl_propertylist_get_string(main_header, READMODE), readmode);
00693 
00694             desc1 = kmo_identify_fits_header(
00695                         cpl_frame_get_filename(frame));
00696             KMO_TRY_CHECK_ERROR_STATE_MSG(
00697                     cpl_sprintf("File (%s) doesn't seem to be in KMOS-format!",
00698                             cpl_frame_get_filename(frame)));
00699 
00700             KMO_TRY_ASSURE(desc1.fits_type == raw_fits,
00701                            CPL_ERROR_ILLEGAL_INPUT,
00702                            "File hasn't correct data type "
00703                            "(%s must be a raw, unprocessed file)!",
00704                            cpl_frame_get_filename(frame));
00705 
00706             KMO_TRY_ASSURE((desc1.naxis1 == nx) &&
00707                            (desc1.naxis2 == ny) &&
00708                            (desc1.naxis3 == nz),
00709                            CPL_ERROR_ILLEGAL_INPUT,
00710                            "File (%s) hasn't correct dimensions! (x,y): "
00711                            "(%d,%d) vs (%d,%d)",
00712                            cpl_frame_get_filename(frame),
00713                            desc1.naxis1, desc1.naxis2, nx, ny);
00714 
00715             // assure that arc lamps are off
00716             KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP1_ST) == FALSE) &&
00717                            (kmo_check_lamp(main_header, INS_LAMP2_ST) == FALSE),
00718                            CPL_ERROR_ILLEGAL_INPUT,
00719                            "Arc lamps must be switched off (%s)!",
00720                            cpl_frame_get_filename(frame));
00721 
00722             // assure that at least one flat lamp is on
00723             KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP3_ST) == TRUE) ||
00724                            (kmo_check_lamp(main_header, INS_LAMP4_ST) == TRUE),
00725                            CPL_ERROR_ILLEGAL_INPUT,
00726                            "At least one flat lamps must be switched on (%s)!",
00727                            cpl_frame_get_filename(frame));
00728 
00729             kmo_free_fits_desc(&desc1);
00730 
00731             // get next FLAT_ON frame
00732             frame = kmo_dfs_get_frame(frameset, NULL);
00733             KMO_TRY_CHECK_ERROR_STATE();
00734 
00735             cpl_propertylist_delete(main_header); main_header = NULL;
00736         }
00737 
00738         //
00739         // ------------ check filter_id, grating_id and rotator offset ---------
00740         //              must match for all detectors
00741         //
00742         KMO_TRY_EXIT_IF_ERROR(
00743             kmo_check_frameset_setup(frameset, FLAT_ON,
00744                                      TRUE, FALSE, FALSE));
00745 
00746         strcpy(filename_flat, MASTER_FLAT);
00747         strcpy(filename_bad, BADPIXEL_FLAT);
00748         strcpy(filename_xcal, XCAL);
00749         strcpy(filename_ycal, YCAL);
00750         strcpy(filename_edge, FLAT_EDGE);
00751 
00752         KMO_TRY_EXIT_IF_NULL(
00753             frame = kmo_dfs_get_frame(frameset, FLAT_ON));
00754         KMO_TRY_EXIT_IF_NULL(
00755             suffix = kmo_dfs_get_suffix(frame, TRUE, FALSE));
00756 
00757         cpl_msg_info("", "Detected instrument setup:   %s", suffix+1);
00758         cpl_msg_info("", "(grating 1, 2 & 3)");
00759 
00760         //
00761         // ---- scan for rotator angles
00762         //
00763         #define ANGLE_DIM 360
00764         int rotang_found[ANGLE_DIM];
00765         int rotang_cnt[ANGLE_DIM];
00766         for (i = 0; i < ANGLE_DIM; i++) {
00767             rotang_found[i] = 0;
00768             rotang_cnt[i] = 0;
00769         }
00770         KMO_TRY_EXIT_IF_NULL(
00771             frame = kmo_dfs_get_frame(frameset, FLAT_ON));
00772         while (frame != NULL) {
00773             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00774             if (cpl_propertylist_has(main_header, ROTANGLE)) {
00775                 int rot_angle = (int) rint(cpl_propertylist_get_double(main_header, ROTANGLE));
00776                 if (rot_angle < 0) {
00777                     rot_angle += 360;
00778                 }
00779                 if (rot_angle < 360 && rot_angle >= 0) {
00780                     rotang_cnt[rot_angle]++;
00781 //                    char * tag = cpl_sprintf("FLAT_ON_%3.3d",rot_angle);
00782 //                    KMO_TRY_EXIT_IF_ERROR(
00783 //                            cpl_frame_set_tag(frame, tag));
00784 //                    cpl_free(tag);
00785                 }
00786             } else {
00787                 cpl_msg_warning("","File %s has no keyword \"ROTANGLE\"",
00788                         cpl_frame_get_filename(frame));
00789             }
00790 
00791             cpl_propertylist_delete(main_header); main_header = NULL;
00792             frame = kmo_dfs_get_frame(frameset, NULL);
00793             KMO_TRY_CHECK_ERROR_STATE();
00794         }
00795         nr_angles = 0;
00796         for (ax = 0; ax < ANGLE_DIM; ax++) {
00797             if (rotang_cnt[ax] != 0) {
00798 //                if (rotang_cnt[ax] >=3) {
00799                     cpl_msg_info("","Found %d frames with rotator angle %d",rotang_cnt[ax],ax);
00800                     rotang_found[nr_angles] = ax;
00801                     nr_angles++;
00802 //                } else {
00803 //                    cpl_msg_warning("","Found %d frames with rotator angle %d but at least three are required",
00804 //                            rotang_cnt[ax],ax);
00805 //                }
00806             }
00807         }
00808 
00809         KMO_TRY_EXIT_IF_NULL (
00810             angle_frameset = (cpl_frameset **) cpl_malloc(nr_angles * sizeof(cpl_frameset*)));
00811         for (i = 0; i < nr_angles; i++) {
00812             angle_frameset[i] = cpl_frameset_new();
00813         }
00814 
00815         KMO_TRY_EXIT_IF_NULL(
00816             frame = kmo_dfs_get_frame(frameset, FLAT_ON));
00817         while (frame != NULL) {
00818             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00819             if (cpl_propertylist_has(main_header, ROTANGLE)) {
00820                 int rot_angle = (int) rint(cpl_propertylist_get_double(main_header, ROTANGLE));
00821                 if (rot_angle < 0) {
00822                     rot_angle += 360;
00823                 }
00824                 int ix = -1;
00825                 for (ix = 0; ix<nr_angles; ix++) {
00826                     if (rotang_found[ix] == rot_angle) {
00827                         break;
00828                     }
00829                 }
00830                 if (ix<nr_angles) {
00831                     KMO_TRY_EXIT_IF_ERROR(
00832                         cpl_frameset_insert(angle_frameset[ix], cpl_frame_duplicate(frame)));
00833                 }
00834             }
00835 
00836             cpl_propertylist_delete(main_header); main_header = NULL;
00837             frame = kmo_dfs_get_frame(frameset, NULL);
00838             KMO_TRY_CHECK_ERROR_STATE();
00839         }
00840 
00841 
00842 // #############################################################################
00843 // ###           allocate temporary memory
00844 // #############################################################################
00845         // load descriptor and header of first operand
00846         KMO_TRY_EXIT_IF_NULL(
00847             frame = kmo_dfs_get_frame(frameset, FLAT_ON));
00848 
00849         desc1 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00850         KMO_TRY_CHECK_ERROR_STATE();
00851 
00852         nr_devices = desc1.nr_ext;
00853 
00854         // the frames have to be stored temporarily because the QC parameters
00855         // for the main header are calculated from each detector. So they can be
00856         // stored only when all detectors are processed
00857         KMO_TRY_EXIT_IF_NULL(
00858             stored_flat = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00859                                                   sizeof(cpl_image*)));
00860         KMO_TRY_EXIT_IF_NULL(
00861             stored_noise = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00862                                                    sizeof(cpl_image*)));
00863         KMO_TRY_EXIT_IF_NULL(
00864             stored_badpix = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00865                                                     sizeof(cpl_image*)));
00866         KMO_TRY_EXIT_IF_NULL(
00867             stored_xcal = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00868                                                     sizeof(cpl_image*)));
00869         KMO_TRY_EXIT_IF_NULL(
00870             stored_ycal = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00871                                                     sizeof(cpl_image*)));
00872         KMO_TRY_EXIT_IF_NULL(
00873             stored_qc_flat_sat = (int*)cpl_malloc(nr_devices * nr_angles *
00874                                                   sizeof(int)));
00875         KMO_TRY_EXIT_IF_NULL(
00876             stored_qc_flat_eff = (double*)cpl_malloc(nr_devices * nr_angles *
00877                                                      sizeof(double)));
00878         KMO_TRY_EXIT_IF_NULL(
00879             stored_qc_flat_sn = (double*)cpl_malloc(nr_devices * nr_angles *
00880                                                     sizeof(double)));
00881         KMO_TRY_EXIT_IF_NULL(
00882             stored_gapmean = (double*)cpl_malloc(nr_devices * nr_angles *
00883                                                  sizeof(double)));
00884         KMO_TRY_EXIT_IF_NULL(
00885             stored_gapsdv = (double*)cpl_malloc(nr_devices * nr_angles *
00886                                                 sizeof(double)));
00887         KMO_TRY_EXIT_IF_NULL(
00888             stored_gapmaxdev = (double*)cpl_malloc(nr_devices * nr_angles *
00889                                                    sizeof(double)));
00890         KMO_TRY_EXIT_IF_NULL(
00891             stored_slitmean = (double*)cpl_malloc(nr_devices * nr_angles *
00892                                                   sizeof(double)));
00893         KMO_TRY_EXIT_IF_NULL(
00894             stored_slitsdv = (double*)cpl_malloc(nr_devices * nr_angles *
00895                                                  sizeof(double)));
00896         KMO_TRY_EXIT_IF_NULL(
00897             stored_slitmaxdev = (double*)cpl_malloc(nr_devices * nr_angles *
00898                                                     sizeof(double)));
00899         KMO_TRY_EXIT_IF_NULL(
00900             spec_found = (cpl_error_code*)cpl_malloc(nr_devices * nr_angles *
00901                                                      sizeof(cpl_error_code)));
00902         KMO_TRY_EXIT_IF_NULL(
00903             edge_table = (cpl_table***)cpl_malloc(KMOS_NR_DETECTORS * nr_angles *
00904                                                   sizeof(cpl_table**)));
00905 
00906         // initialize to NULL
00907         for (i = 0; i < nr_devices * nr_angles ; i++) {
00908             stored_qc_flat_sat[i] = 0.0;
00909             stored_qc_flat_eff[i] = 0.0;
00910             stored_qc_flat_sn[i] = 0.0;
00911             stored_gapmean[i] = 0.0;
00912             stored_gapsdv[i] = 0.0;
00913             stored_gapmaxdev[i] = 0.0;
00914             stored_slitmean[i] = 0.0;
00915             stored_slitsdv[i] = 0.0;
00916             stored_slitmaxdev[i] = 0.0;
00917             spec_found[i] = CPL_ERROR_NONE;
00918         }
00919 
00920         for (i = 0; i < KMOS_NR_DETECTORS * nr_angles; i++) {
00921             edge_table[i] = NULL;
00922         }
00923 
00924 // #############################################################################
00925 // ###           process data
00926 // #############################################################################
00927 
00928         // check which IFUs are active for all FLAT_ON frames
00929         KMO_TRY_EXIT_IF_NULL(
00930             unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0));
00931 
00932         KMO_TRY_EXIT_IF_NULL(
00933             unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before));
00934 
00935         kmo_print_unused_ifus(unused_ifus_before, FALSE);
00936 
00937         cpl_msg_info("", "EXPTIME:  %g seconds", exptime);
00938         cpl_msg_info("", "NDIT: %d", ndit);
00939         cpl_msg_info("", "Detector readout mode: %s", readmode);
00940         cpl_msg_info("", "-------------------------------------------");
00941 
00942         //
00943         // ------------ loop all rotator angles and detectors ------------
00944         //
00945 
00946         for (a = 0; a < nr_angles; a++) {
00947             cpl_msg_info("","Processing rotator angle %d -> %d degree", a,rotang_found[a]);
00948             for (i = 1; i <= nr_devices; i++) {
00949                 cpl_msg_info("","Processing detector No. %d", i);
00950 
00951                 sx = a * nr_devices + (i - 1);
00952 
00953                 KMO_TRY_EXIT_IF_NULL(
00954                     bad_pix_mask_dark = kmo_dfs_load_image(frameset, BADPIXEL_DARK,
00955                                                            i, 2, FALSE, NULL));
00956 
00957                 //
00958                 // ------------ load all lamp_on and lamp_off images ------------
00959                 //
00960                 KMO_TRY_EXIT_IF_NULL(
00961                     det_lamp_on = cpl_imagelist_new());
00962                 KMO_TRY_EXIT_IF_NULL(
00963                     det_lamp_off = cpl_imagelist_new());
00964 
00965                 // load lamp-on images
00966                 KMO_TRY_EXIT_IF_NULL(
00967                     frame = kmo_dfs_get_frame(angle_frameset[a], FLAT_ON));
00968                 j = 0;
00969                 while (frame != NULL) {
00970                     KMO_TRY_EXIT_IF_NULL(
00971                         img_in = kmo_dfs_load_image_frame(frame, i, FALSE, TRUE, &nr_sat));
00972 
00973                     KMO_TRY_EXIT_IF_ERROR(
00974                         kmo_image_reject_from_mask(img_in, bad_pix_mask_dark));
00975 
00976                     cpl_imagelist_set(det_lamp_on, img_in, j++);
00977                     KMO_TRY_CHECK_ERROR_STATE();
00978 
00979                     frame = kmo_dfs_get_frame(angle_frameset[a], NULL);
00980                     KMO_TRY_CHECK_ERROR_STATE();
00981                 }
00982 
00983                 // load lamp-off images
00984                 KMO_TRY_EXIT_IF_NULL(
00985                     frame = kmo_dfs_get_frame(frameset, FLAT_OFF));
00986                 j = 0;
00987                 while (frame != NULL) {
00988                     KMO_TRY_EXIT_IF_NULL(
00989                         img_in = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, NULL));
00990 
00991                     KMO_TRY_EXIT_IF_ERROR(
00992                         kmo_image_reject_from_mask(img_in, bad_pix_mask_dark));
00993 
00994                     cpl_imagelist_set(det_lamp_off, img_in, j++);
00995                     KMO_TRY_CHECK_ERROR_STATE();
00996 
00997                     // get next frame
00998                     frame = kmo_dfs_get_frame(frameset, NULL);
00999                     KMO_TRY_CHECK_ERROR_STATE();
01000                 }
01001 
01002                 //
01003                 // ------------ process imagelist ------------
01004                 //
01005 
01006                 // count saturated pixels for each detector
01007                 KMO_TRY_EXIT_IF_NULL(
01008                     frame = kmo_dfs_get_frame(angle_frameset[a], FLAT_ON));
01009                 KMO_TRY_EXIT_IF_NULL(
01010                     main_header = kmclipm_propertylist_load(
01011                                                  cpl_frame_get_filename(frame), 0));
01012                 if (strcmp(cpl_propertylist_get_string(main_header, READMODE), "Nondest") == 0) {
01013                     // NDR: non-destructive readout mode
01014                     stored_qc_flat_sat[sx] = nr_sat;
01015                 } else {
01016                     // normal readout mode
01017                     stored_qc_flat_sat[sx] =
01018                                      kmo_imagelist_get_saturated(det_lamp_on,
01019                                                                  KMO_FLAT_SATURATED,
01020                                                                  KMO_FLAT_SAT_MIN);
01021                 }
01022                 cpl_propertylist_delete(main_header); main_header = NULL;
01023                 KMO_TRY_CHECK_ERROR_STATE();
01024 
01025                 // combine imagelists and create noise
01026                 KMO_TRY_EXIT_IF_ERROR(
01027                     kmclipm_combine_frames(det_lamp_on, NULL,
01028                                            NULL,
01029                                            cmethod, cpos_rej, cneg_rej, citer,
01030                                            cmax, cmin,
01031                                            &combined_data_on,
01032                                            &combined_noise_on,
01033                                            -1.0));
01034 
01035                 KMO_TRY_EXIT_IF_ERROR(
01036                     kmclipm_combine_frames(det_lamp_off, NULL,
01037                                            NULL,
01038                                            cmethod, cpos_rej, cneg_rej, citer,
01039                                            cmax, cmin,
01040                                            &combined_data_off,
01041                                            &combined_noise_off,
01042                                            -1.0));
01043 
01044                 if (kmclipm_omit_warning_one_slice > 10) {
01045                     cpl_msg_warning(cpl_func, "Previous warning (number of "
01046                                               "identified slices) occured %d times.",
01047                                     kmclipm_omit_warning_one_slice);
01048                     kmclipm_omit_warning_one_slice = FALSE;
01049                 }
01050 
01051                 // subtract combined lamp_off from lamp_on
01052                 // (for noise: sig_x = sqrt(sig_u^2 + sig_v^2)
01053                 KMO_TRY_EXIT_IF_ERROR(
01054                     cpl_image_subtract(combined_data_on, combined_data_off));
01055 
01056                 KMO_TRY_EXIT_IF_ERROR(
01057                     cpl_image_power(combined_noise_on, 2.0));
01058                 KMO_TRY_EXIT_IF_ERROR(
01059                     cpl_image_power(combined_noise_off, 2.0));
01060                 KMO_TRY_EXIT_IF_ERROR(
01061                     cpl_image_add(combined_noise_on, combined_noise_off));
01062                 KMO_TRY_EXIT_IF_ERROR(
01063                     cpl_image_power(combined_noise_on, 0.5));
01064 
01065                 // create bad-pixel-mask
01066                 KMO_TRY_EXIT_IF_NULL(
01067                     bad_pix_mask_flat = kmo_create_bad_pix_flat_thresh(
01068                                                                 combined_data_on,
01069                                                                 surrounding_pixels,
01070                                                                 badpix_thresh));
01071 
01072                 // calculate spectral curvature here
01073                 spec_found[sx] = kmo_calc_curvature(combined_data_on,
01074                                                     combined_noise_on,
01075                                                     unused_ifus_after[i-1],
01076                                                     bad_pix_mask_flat,
01077                                                     i,
01078                                                     &xcal,
01079                                                     &ycal,
01080                                                     stored_gapmean+(sx),
01081                                                     stored_gapsdv+(sx),
01082                                                     stored_gapmaxdev+(sx),
01083                                                     stored_slitmean+(sx),
01084                                                     stored_slitsdv+(sx),
01085                                                     stored_slitmaxdev+(sx),
01086                                                     &edge_table[sx]);
01087 
01088                 if (spec_found[sx] == CPL_ERROR_NONE) {
01089                     // all fine
01090 
01091                     // in kmo_calc_curvature() the spectral slope of each slitlet
01092                     // has been normalised individually. Now the normalisation on
01093                     // the whole frame is applied. (cpl_image_get_mean()
01094                     // ignores bad pixels when calculating the mean)
01095                     mean_data = cpl_image_get_mean(combined_data_on);
01096                     KMO_TRY_CHECK_ERROR_STATE();
01097 
01098                     stored_qc_flat_eff[sx] = mean_data / exptime;
01099 
01100                     mean_noise = cpl_image_get_mean(combined_noise_on);
01101                     KMO_TRY_CHECK_ERROR_STATE();
01102 
01103                     if ((cpl_frameset_count_tags(frameset, FLAT_OFF) > 1) ||
01104                         (cpl_frameset_count_tags(frameset, FLAT_ON) > 1))
01105                     {
01106                         KMO_TRY_ASSURE(mean_noise != 0.0,
01107                                        CPL_ERROR_ILLEGAL_INPUT,
01108                                        "All frames of detector %i are exactly "
01109                                        "the same!", i);
01110 
01111                         stored_qc_flat_sn[sx] = mean_data / mean_noise;
01112                     }
01113 
01114                     // normalize data & noise on the whole detector frame (the
01115                     // spectral slope on each slitlet has already been normalised in
01116                     // kmo_calc_curvature())
01117                     KMO_TRY_EXIT_IF_ERROR(
01118                         cpl_image_divide_scalar(combined_data_on, mean_data));
01119 
01120                     KMO_TRY_EXIT_IF_ERROR(
01121                         cpl_image_divide_scalar(combined_noise_on, mean_data));
01122 
01123                     // apply the badpixel mask to the produced frames
01124                     KMO_TRY_EXIT_IF_ERROR(
01125                         cpl_image_multiply(combined_data_on, bad_pix_mask_flat));
01126 
01127                     KMO_TRY_EXIT_IF_ERROR(
01128                         cpl_image_multiply(combined_noise_on, bad_pix_mask_flat));
01129 
01130                     KMO_TRY_EXIT_IF_ERROR(
01131                         cpl_image_multiply(xcal, bad_pix_mask_flat));
01132 
01133                     KMO_TRY_EXIT_IF_ERROR(
01134                         cpl_image_multiply(ycal, bad_pix_mask_flat));
01135 
01136                     //
01137                     // ------ store temporarily flat, badpixel and calibration -----
01138                     //
01139                     stored_flat[sx] = combined_data_on;
01140                     stored_noise[sx] = combined_noise_on;
01141                     stored_badpix[sx] = bad_pix_mask_flat;
01142                     stored_xcal[sx] = xcal;
01143                     stored_ycal[sx] = ycal;
01144                 } else if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
01145                     // all IFUs seem to be deativated, continue processing
01146                     // just save empty frames
01147                     cpl_error_reset();
01148                     cpl_image_delete(combined_data_on); combined_data_on = NULL;
01149                     cpl_image_delete(combined_noise_on); combined_noise_on = NULL;
01150                     cpl_image_delete(bad_pix_mask_flat); bad_pix_mask_flat = NULL;
01151                     stored_flat[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01152                     stored_noise[sx]  = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01153                     stored_badpix[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01154                 } else {
01155                     // another error occured
01156                     KMO_TRY_CHECK_ERROR_STATE();
01157                 }
01158 
01159                 // store immediate results, free memory
01160                 KMO_TRY_EXIT_IF_ERROR(
01161                         kmclipm_image_save(stored_flat[sx], fn_flat,
01162                                 CPL_TYPE_FLOAT, NULL, save_mode, 0./0.));
01163                 KMO_TRY_EXIT_IF_ERROR(
01164                         kmclipm_image_save(stored_noise[sx], fn_noise,
01165                                 CPL_TYPE_FLOAT, NULL, save_mode, 0./0.));
01166                 KMO_TRY_EXIT_IF_ERROR(
01167                         kmclipm_image_save(stored_badpix[sx], fn_badpix,
01168                                 CPL_TYPE_FLOAT, NULL, save_mode, 0./0.));
01169                 save_mode = CPL_IO_EXTEND; //all other saves will create extensions
01170                 cpl_image_delete(stored_flat[sx]); stored_flat[sx] = NULL;
01171                 cpl_image_delete(stored_noise[sx]); stored_noise[sx] = NULL;
01172                 cpl_image_delete(stored_badpix[sx]); stored_badpix[sx] = NULL;
01173 
01174 
01175                 // free memory
01176                 cpl_imagelist_delete(det_lamp_on); det_lamp_on = NULL;
01177                 cpl_imagelist_delete(det_lamp_off); det_lamp_off = NULL;
01178                 cpl_image_delete(combined_data_off); combined_data_off = NULL;
01179                 cpl_image_delete(combined_noise_off); combined_noise_off = NULL;
01180                 cpl_image_delete(bad_pix_mask_dark); bad_pix_mask_dark = NULL;
01181             } // for i = 1; i <= nr_devices
01182         } // for a = 0; a < nr_angles
01183         KMO_TRY_CHECK_ERROR_STATE();
01184 
01185 // #############################################################################
01186 // ###           QC parameters & saving
01187 // #############################################################################
01188         //
01189         // ------------ load, update & save primary header ------------
01190         //
01191         KMO_TRY_EXIT_IF_NULL(
01192             main_header = kmo_dfs_load_primary_header(frameset, FLAT_ON));
01193 
01194         // update which IFUs are not used
01195         kmo_print_unused_ifus(unused_ifus_after, TRUE);
01196 
01197         KMO_TRY_EXIT_IF_ERROR(
01198             kmo_set_unused_ifus(unused_ifus_after, main_header, "kmo_flat"));
01199 
01200         // write main_header for data-, noise-, ycal- and badpix-frame
01201         // xcal gets additionally the boundaries of the IFUs for reconstruction
01202 
01203         // add here boundaries for reconstruction
01204         KMO_TRY_EXIT_IF_NULL(
01205             main_header_xcal = cpl_propertylist_new());
01206 
01207         KMO_TRY_EXIT_IF_NULL(
01208             total_bounds = (int**)cpl_malloc(nr_devices*sizeof(int*)));
01209         for (i = 0; i < nr_devices; i++) {
01210             KMO_TRY_EXIT_IF_NULL(
01211                 total_bounds[i] = (int*) cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int)));
01212             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01213                 total_bounds[i][2*j] = 2048;
01214                 total_bounds[i][2*j+1] = 0;
01215             }
01216         }
01217         KMO_TRY_CHECK_ERROR_STATE();
01218 
01219         KMO_TRY_EXIT_IF_NULL(
01220             all_bounds = (int***) cpl_malloc(nr_angles*sizeof(int**)));
01221         for (a = 0; a < nr_angles; a++) {
01222             KMO_TRY_EXIT_IF_NULL(
01223                 all_bounds[a] = (int**) cpl_malloc(nr_devices*sizeof(int*)));
01224             for (i = 0; i < nr_devices; i++) {
01225                 KMO_TRY_EXIT_IF_NULL(
01226                     all_bounds[a][i] = (int*) cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int)));
01227             }
01228         }
01229         KMO_TRY_CHECK_ERROR_STATE();
01230 
01231         for (a = 0; a < nr_angles; a++) {
01232             for (i = 0; i < nr_devices; i++) {
01233                 sx = a * nr_devices + i;
01234                 if (stored_ycal[sx] != NULL) {
01235                     KMO_TRY_EXIT_IF_NULL(
01236                         bounds = kmo_split_frame(stored_ycal[sx]));
01237                     for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01238                         all_bounds[a][i][2*j] = bounds[2*j];
01239                         all_bounds[a][i][2*j+1] = bounds[2*j+1];
01240 
01241                         if (total_bounds[i][2*j] >= 0 ) {
01242                             if (bounds[2*j] < 0) {
01243                                 total_bounds[i][2*j] = bounds[2*j];
01244                             } else {
01245                                 if (total_bounds[i][2*j] > bounds[2*j]) {
01246                                     total_bounds[i][2*j] = bounds[2*j];
01247                                 }
01248                             }
01249                         }
01250                         if (total_bounds[i][2*j+1] >= 0 ) {
01251                             if (bounds[2*j+1] < 0) {
01252                                 total_bounds[i][2*j+1] = bounds[2*j+1];
01253                             } else {
01254                                 if (total_bounds[i][2*j+1] < bounds[2*j+1]) {
01255                                     total_bounds[i][2*j+1] = bounds[2*j+1];
01256                                 }
01257                             }
01258                         }
01259                     }
01260                 } else {
01261                     // whole detector inactive
01262                     for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01263                         all_bounds[a][i][2*j] = -1;
01264                         all_bounds[a][i][2*j+1] = -1;
01265                         total_bounds[i][2*j] = -1;
01266                         total_bounds[i][2*j+1] = -1;
01267                     }
01268                 }
01269                 if (bounds != NULL) {
01270                     cpl_free(bounds); bounds = NULL;
01271                 }
01272             } // for (nr_devices)
01273         } // for (nr_angles)
01274 
01275         for (i = 0; i < nr_devices; i++) {
01276             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01277                 if (total_bounds[i][2*j] > -1) {
01278                     KMO_TRY_EXIT_IF_NULL(
01279                             tmpstr= cpl_sprintf("%s%d%s",
01280                                     BOUNDS_PREFIX,
01281                                     i*KMOS_IFUS_PER_DETECTOR + j+1,
01282                                     "_L"));
01283                     KMO_TRY_EXIT_IF_ERROR(
01284                             kmclipm_update_property_int(main_header_xcal,
01285                                     tmpstr, total_bounds[i][2*j],
01286                                     "[pix] left boundary for reconstr."));
01287                     cpl_free(tmpstr); tmpstr = NULL;
01288                 }
01289 
01290                 if (total_bounds[i][2*j+1] > -1) {
01291                     KMO_TRY_EXIT_IF_NULL(
01292                             tmpstr= cpl_sprintf("%s%d%s",
01293                                     BOUNDS_PREFIX,
01294                                     i*KMOS_IFUS_PER_DETECTOR + j+1,
01295                                     "_R"));
01296                     KMO_TRY_EXIT_IF_ERROR(
01297                             kmclipm_update_property_int(main_header_xcal,
01298                                     tmpstr, total_bounds[i][2*j+1],
01299                                     "[pix] right boundary for reconstr."));
01300                     cpl_free(tmpstr); tmpstr = NULL;
01301                 }
01302             }
01303         } // for (nr_devices)
01304 
01305         //
01306         // ------------ saving headers ------------
01307         //
01308         if (!suppress_extension) {
01309             KMO_TRY_EXIT_IF_NULL(
01310                 fn_suffix = cpl_sprintf("%s", suffix));
01311         } else {
01312             KMO_TRY_EXIT_IF_NULL(
01313                 fn_suffix = cpl_sprintf("%s", ""));
01314         }
01315 
01316         cpl_msg_info("","Saving data...");
01317 
01318         KMO_TRY_EXIT_IF_NULL(
01319             frame = kmo_dfs_get_frame(frameset, FLAT_ON));
01320 
01321         KMO_TRY_EXIT_IF_ERROR(
01322             kmo_dfs_save_main_header(frameset, filename_flat, fn_suffix, frame,
01323                                      main_header, parlist, cpl_func));
01324         KMO_TRY_EXIT_IF_ERROR(
01325             kmo_dfs_save_main_header(frameset, filename_xcal, fn_suffix, frame,
01326                                      main_header_xcal, parlist, cpl_func));
01327         KMO_TRY_EXIT_IF_ERROR(
01328             kmo_dfs_save_main_header(frameset, filename_ycal, fn_suffix, frame,
01329                                      main_header, parlist, cpl_func));
01330         KMO_TRY_EXIT_IF_ERROR(
01331             kmo_dfs_save_main_header(frameset, filename_bad, fn_suffix, frame,
01332                                      main_header, parlist, cpl_func));
01333         KMO_TRY_EXIT_IF_ERROR(
01334             kmo_dfs_save_main_header(frameset, filename_edge, fn_suffix, frame,
01335                                      main_header, parlist, cpl_func));
01336 
01337         cpl_propertylist_delete(main_header); main_header = NULL;
01338         cpl_propertylist_delete(main_header_xcal); main_header_xcal = NULL;
01339 
01340         //
01341         // ------------ saving sub frames ------------
01342         //
01343         for (a = 0; a < nr_angles; a++) {
01344             for (i = 1; i <= nr_devices; i++) {
01345                 sx = a * nr_devices + (i - 1);
01346 
01347                 // load stored data again
01348                 KMO_TRY_EXIT_IF_NULL(
01349                         stored_flat[sx] = kmclipm_image_load(fn_flat,
01350                                 CPL_TYPE_FLOAT, 0, sx));
01351                 KMO_TRY_EXIT_IF_NULL(
01352                         stored_noise[sx] = kmclipm_image_load(fn_noise,
01353                                 CPL_TYPE_FLOAT, 0, sx));
01354                 KMO_TRY_EXIT_IF_NULL(
01355                         stored_badpix[sx] = kmclipm_image_load(fn_badpix,
01356                                 CPL_TYPE_FLOAT, 0, sx));
01357 
01358                 KMO_TRY_EXIT_IF_NULL(
01359                     sub_header = kmo_dfs_load_sub_header(frameset, FLAT_ON, i,
01360                                                          FALSE));
01361                 cpl_propertylist_erase(sub_header, CRPIX1);
01362                 cpl_propertylist_erase(sub_header, CRPIX2);
01363 
01364                 KMO_TRY_EXIT_IF_ERROR(
01365                     kmclipm_update_property_double(sub_header,CAL_ROTANGLE,
01366                                                    ((double) rotang_found[a]),
01367                                                    "[deg] Rotator relative to nasmyth"));
01368 
01369                 if (i == 1) {
01370                     for (ii = 0; ii < nr_devices; ii++) {
01371                         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01372                             if (all_bounds[a][ii][2*j] > -1) {
01373                                 KMO_TRY_EXIT_IF_NULL(
01374                                         tmpstr= cpl_sprintf("%s%d%s",
01375                                                 BOUNDS_PREFIX,
01376                                                 ii*KMOS_IFUS_PER_DETECTOR + j+1,
01377                                                 "_L"));
01378                                 KMO_TRY_EXIT_IF_ERROR(
01379                                         kmclipm_update_property_int(sub_header,
01380                                                 tmpstr, all_bounds[a][ii][2*j],
01381                                                 "[pix] left boundary for reconstr."));
01382                                 cpl_free(tmpstr); tmpstr = NULL;
01383                             }
01384 
01385                             if (all_bounds[a][ii][2*j+1] > -1) {
01386                                 KMO_TRY_EXIT_IF_NULL(
01387                                         tmpstr= cpl_sprintf("%s%d%s",
01388                                                 BOUNDS_PREFIX,
01389                                                 ii-1*KMOS_IFUS_PER_DETECTOR + j+1,
01390                                                 "_R"));
01391                                 KMO_TRY_EXIT_IF_ERROR(
01392                                         kmclipm_update_property_int(sub_header,
01393                                                 tmpstr, all_bounds[a][ii][2*j+1],
01394                                                 "[pix] right boundary for reconstr."));
01395                                 cpl_free(tmpstr); tmpstr = NULL;
01396                             }
01397                         }
01398                     } // for (nr_devices)
01399 
01400                 }
01401 
01402                 if (spec_found[sx] == CPL_ERROR_NONE) {
01403                     KMO_TRY_EXIT_IF_ERROR(
01404                         kmclipm_update_property_int(sub_header,
01405                                                     QC_FLAT_SAT,
01406                                                     stored_qc_flat_sat[sx],
01407                                          "[] nr. saturated pixels of master flat"));
01408                     // load gain
01409                     gain = kmo_dfs_get_property_double(sub_header, GAIN);
01410                     KMO_TRY_CHECK_ERROR_STATE_MSG(
01411                                          "GAIN-keyword in fits-header is missing!");
01412 
01413                     KMO_TRY_EXIT_IF_ERROR(
01414                         kmclipm_update_property_double(sub_header,
01415                                                        QC_FLAT_EFF,
01416                                                        stored_qc_flat_eff[sx]/gain,
01417                                             "[e-/s] rel. brightness of flat lamp"));
01418                     KMO_TRY_EXIT_IF_ERROR(
01419                         kmclipm_update_property_double(sub_header,
01420                                                        QC_FLAT_SN,
01421                                                        stored_qc_flat_sn[sx],
01422                                                        "[] S/N of master flat"));
01423                 }
01424 
01425                 // store qc parameters only if any slitlet- and gap-width has been
01426                 // detected (should be the case when at least one IFU is active)
01427                 if (stored_xcal[sx] != NULL) {
01428                     KMO_TRY_EXIT_IF_ERROR(
01429                         kmclipm_update_property_double(sub_header,
01430                                                        QC_GAP_MEAN,
01431                                                        stored_gapmean[sx],
01432                                           "[pix] mean gap width between slitlets"));
01433                     KMO_TRY_EXIT_IF_ERROR(
01434                         kmclipm_update_property_double(sub_header,
01435                                                        QC_GAP_SDV,
01436                                                        stored_gapsdv[sx],
01437                                       "[pix] stdev of gap width between slitlets"));
01438                     KMO_TRY_EXIT_IF_ERROR(
01439                         kmclipm_update_property_double(sub_header,
01440                                                        QC_GAP_MAXDEV,
01441                                                        stored_gapmaxdev[sx],
01442                                        "[pix] max gap deviation between slitlets"));
01443                     KMO_TRY_EXIT_IF_ERROR(
01444                         kmclipm_update_property_double(sub_header,
01445                                                        QC_SLIT_MEAN,
01446                                                        stored_slitmean[sx],
01447                                                        "[pix] mean slitlet width"));
01448                     KMO_TRY_EXIT_IF_ERROR(
01449                         kmclipm_update_property_double(sub_header,
01450                                                        QC_SLIT_SDV,
01451                                                        stored_slitsdv[sx],
01452                                                   "[pix] stdev of slitlet widths"));
01453                     KMO_TRY_EXIT_IF_ERROR(
01454                         kmclipm_update_property_double(sub_header,
01455                                                        QC_SLIT_MAXDEV,
01456                                                        stored_slitmaxdev[sx],
01457                                               "[pix] max slitlet width deviation"));
01458                 }
01459 
01460                 if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
01461                     stored_xcal[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01462                     stored_ycal[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01463                 }
01464 
01465                 // calculate QC.BADPIX.NCOUNT
01466                 nr_bad_pix = cpl_image_count_rejected(stored_badpix[sx]);
01467                 KMO_TRY_CHECK_ERROR_STATE();
01468 
01469                 // remove 4pixel-border as bad pixels
01470                 nr_bad_pix -= 2*KMOS_BADPIX_BORDER*(nx-2*KMOS_BADPIX_BORDER) +
01471                                   2*KMOS_BADPIX_BORDER*ny;
01472 
01473                 KMO_TRY_EXIT_IF_ERROR(
01474                     kmclipm_update_property_int(sub_header,
01475                                                 QC_NR_BAD_PIX,
01476                                                 nr_bad_pix,
01477                                                 "[] nr. of bad pixels"));
01478                 // save flat frame
01479                 KMO_TRY_EXIT_IF_NULL(
01480                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
01481                 KMO_TRY_EXIT_IF_ERROR(
01482                     kmclipm_update_property_string(sub_header, EXTNAME,
01483                                                    extname,
01484                                                    "FITS extension name"));
01485                 cpl_free(extname); extname = NULL;
01486 
01487                 KMO_TRY_EXIT_IF_ERROR(
01488                     kmclipm_update_property_int(sub_header,
01489                                                 EXTVER,
01490                                                 sx+1,
01491                                                 "FITS extension ver"));
01492 
01493                 KMO_TRY_EXIT_IF_ERROR(
01494                     kmo_dfs_save_image(stored_flat[sx], filename_flat,
01495                                        fn_suffix, sub_header, 0./0.));
01496 
01497                 // save noise frame only when enough input frames were available
01498                 KMO_TRY_EXIT_IF_NULL(
01499                     extname = kmo_extname_creator(detector_frame, i,
01500                                                   EXT_NOISE));
01501                 KMO_TRY_EXIT_IF_ERROR(
01502                     kmclipm_update_property_string(sub_header, EXTNAME,
01503                                                    extname,
01504                                                    "FITS extension name"));
01505                 cpl_free(extname); extname = NULL;
01506 
01507                 KMO_TRY_EXIT_IF_ERROR(
01508                     kmo_dfs_save_image(stored_noise[sx], filename_flat,
01509                                        fn_suffix, sub_header, 0./0.));
01510 
01511                 // save bad_pix frame
01512                 KMO_TRY_EXIT_IF_NULL(
01513                     extname = kmo_extname_creator(detector_frame, i, EXT_BADPIX));
01514                 KMO_TRY_EXIT_IF_ERROR(
01515                     kmclipm_update_property_string(sub_header, EXTNAME,
01516                                                    extname,
01517                                                    "FITS extension name"));
01518                 cpl_free(extname); extname = NULL;
01519 
01520                 KMO_TRY_EXIT_IF_ERROR(
01521                     kmo_dfs_save_image(stored_badpix[sx], filename_bad,
01522                                        fn_suffix, sub_header, 0.));
01523 
01524                 // save xcal and ycal-frame
01525                 KMO_TRY_EXIT_IF_NULL(
01526                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
01527                 KMO_TRY_EXIT_IF_ERROR(
01528                     kmclipm_update_property_string(sub_header, EXTNAME,
01529                                                    extname,
01530                                                    "FITS extension name"));
01531                 cpl_free(extname); extname = NULL;
01532 
01533                 KMO_TRY_EXIT_IF_ERROR(
01534                     kmo_dfs_save_image(stored_xcal[sx], filename_xcal,
01535                                        fn_suffix, sub_header, 0./0.));
01536 
01537                 KMO_TRY_EXIT_IF_ERROR(
01538                     kmo_dfs_save_image(stored_ycal[sx], filename_ycal,
01539                                        fn_suffix, sub_header, 0./0.));
01540 
01541                 // save edge_pars-frame
01542                 KMO_TRY_EXIT_IF_NULL(
01543                     extname = kmo_extname_creator(list_frame, i, EXT_DATA));
01544                 KMO_TRY_EXIT_IF_ERROR(
01545                     kmclipm_update_property_string(sub_header, EXTNAME,
01546                                                    extname,
01547                                                    "FITS extension name"));
01548                 cpl_free(extname); extname = NULL;
01549 
01550                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01551                     KMO_TRY_EXIT_IF_ERROR(
01552                         kmclipm_update_property_int(sub_header, CAL_IFU_NR,
01553                                                        j+1+(i-1)*KMOS_IFUS_PER_DETECTOR,
01554                                                        "IFU Number {1..24}"));
01555 
01556                     // save edge-parameters as product
01557                     if (spec_found[sx] != CPL_ERROR_DATA_NOT_FOUND) {
01558                         if (edge_table[sx][j] != NULL) {
01559                             KMO_TRY_EXIT_IF_ERROR(
01560                                 kmo_dfs_save_table(edge_table[sx][j], filename_edge, fn_suffix, sub_header));
01561                         } else {
01562                             KMO_TRY_EXIT_IF_ERROR(
01563                                 kmo_dfs_save_table(NULL, filename_edge, fn_suffix, sub_header));
01564                         }
01565                     } else {
01566                         KMO_TRY_EXIT_IF_ERROR(
01567                             kmo_dfs_save_table(NULL, filename_edge, fn_suffix, sub_header));
01568                     }
01569                 }
01570 
01571                 cpl_propertylist_delete(sub_header); sub_header = NULL;
01572 
01573                 cpl_image_delete(stored_flat[sx]); stored_flat[sx] = NULL;
01574                 cpl_image_delete(stored_noise[sx]); stored_noise[sx] = NULL;
01575                 cpl_image_delete(stored_badpix[sx]); stored_badpix[sx] = NULL;
01576             } // for (i = nr_devices)
01577         } // for (a = nr_angles)
01578     }
01579     KMO_CATCH
01580     {
01581         KMO_CATCH_MSG();
01582         ret_val = -1;
01583     }
01584     // delete temporary files
01585     unlink(fn_flat);
01586     unlink(fn_noise);
01587     unlink(fn_badpix);
01588 
01589     kmo_free_fits_desc(&desc1);
01590     kmo_free_fits_desc(&desc2);
01591     kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
01592     kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
01593     cpl_propertylist_delete(main_header); main_header = NULL;
01594     cpl_propertylist_delete(main_header_xcal); main_header_xcal = NULL;
01595     cpl_propertylist_delete(sub_header); sub_header = NULL;
01596     cpl_imagelist_delete(det_lamp_on); det_lamp_on = NULL;
01597     cpl_imagelist_delete(det_lamp_off); det_lamp_off = NULL;
01598     cpl_image_delete(combined_data_off); combined_data_off = NULL;
01599     cpl_image_delete(combined_noise_off); combined_noise_off = NULL;
01600     cpl_image_delete(bad_pix_mask_dark); bad_pix_mask_dark = NULL;
01601     cpl_free(stored_qc_flat_sat); stored_qc_flat_sat = NULL;
01602     cpl_free(stored_qc_flat_eff); stored_qc_flat_eff = NULL;
01603     cpl_free(stored_qc_flat_sn); stored_qc_flat_sn = NULL;
01604     cpl_free(stored_gapmean); stored_gapmean = NULL;
01605     cpl_free(stored_gapsdv); stored_gapsdv = NULL;
01606     cpl_free(stored_gapmaxdev); stored_gapmaxdev = NULL;
01607     cpl_free(stored_slitmean); stored_slitmean = NULL;
01608     cpl_free(stored_slitsdv); stored_slitsdv = NULL;
01609     cpl_free(stored_slitmaxdev); stored_slitmaxdev = NULL;
01610     cpl_free(readmode); readmode = NULL;
01611     cpl_free(suffix); suffix = NULL;
01612     cpl_free(fn_suffix); fn_suffix = NULL;
01613     if (total_bounds != NULL) {
01614         for (i = 0; i < nr_devices; i++) {
01615             cpl_free(total_bounds[i]); total_bounds[i] = NULL;
01616         }
01617     }
01618     cpl_free(total_bounds); total_bounds = NULL;
01619     for (i = 0; i < nr_devices * nr_angles; i++) {
01620         cpl_image_delete(stored_flat[i]); stored_flat[i] = NULL;
01621         cpl_image_delete(stored_noise[i]); stored_noise[i] = NULL;
01622         cpl_image_delete(stored_badpix[i]); stored_badpix[i] = NULL;
01623         cpl_image_delete(stored_xcal[i]); stored_xcal[i] = NULL;
01624         cpl_image_delete(stored_ycal[i]); stored_ycal[i] = NULL;
01625     }
01626     cpl_free(stored_flat); stored_flat = NULL;
01627     cpl_free(stored_noise); stored_noise = NULL;
01628     cpl_free(stored_badpix); stored_badpix = NULL;
01629     cpl_free(stored_xcal); stored_xcal = NULL;
01630     cpl_free(stored_ycal); stored_ycal = NULL;
01631     for (a = 0; a < nr_angles; a++) {
01632         cpl_frameset_delete(angle_frameset[a]); angle_frameset[a] = NULL;
01633         if ((all_bounds != NULL) && (all_bounds[a] != NULL)) {
01634             for (i = 0; i < nr_devices; i++) {
01635                 cpl_free(all_bounds[a][i]); all_bounds[a][i] = NULL;
01636             }
01637             cpl_free(all_bounds[a]); all_bounds[a] = NULL;
01638         }
01639     }
01640     cpl_free(angle_frameset); angle_frameset = NULL;
01641     cpl_free(all_bounds); all_bounds = NULL;
01642     if (edge_table != NULL) {
01643         for (i = 0; i < KMOS_NR_DETECTORS * nr_angles; i++) {
01644             if (edge_table[i] != NULL) {
01645                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01646                     cpl_table_delete(edge_table[i][j]);
01647                     edge_table[i][j] = NULL;
01648                 }
01649                 cpl_free(edge_table[i]); edge_table[i] = NULL;
01650             }
01651         }
01652         cpl_free(edge_table); edge_table = NULL;
01653     }
01654     if (bounds != NULL) {
01655         cpl_free(bounds); bounds = NULL;
01656     }
01657     if (spec_found != NULL) {
01658         cpl_free(spec_found); spec_found = NULL;
01659     }
01660 
01661     return ret_val;
01662 }
01663