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