KMOS Pipeline Reference Manual  1.1.0
kmo_flat.c
00001 /* $Id: kmo_flat.c,v 1.36 2013/03/12 11:24:13 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/03/12 11:24:13 $
00024  * $Revision: 1.36 $
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 "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, parlist, cpl_func));
01268         KMO_TRY_EXIT_IF_ERROR(
01269             kmo_dfs_save_main_header(frameset, filename_xcal, suffix, frame,
01270                                      main_header_xcal, parlist, cpl_func));
01271         KMO_TRY_EXIT_IF_ERROR(
01272             kmo_dfs_save_main_header(frameset, filename_ycal, suffix, frame,
01273                                      main_header, parlist, cpl_func));
01274         KMO_TRY_EXIT_IF_ERROR(
01275             kmo_dfs_save_main_header(frameset, filename_bad, suffix, frame,
01276                                      main_header, parlist, cpl_func));
01277         KMO_TRY_EXIT_IF_ERROR(
01278             kmo_dfs_save_main_header(frameset, filename_edge, suffix, frame,
01279                                      main_header, parlist, cpl_func));
01280 
01281         cpl_propertylist_delete(main_header); main_header = NULL;
01282         cpl_propertylist_delete(main_header_xcal); main_header_xcal = NULL;
01283 
01284         //
01285         // ------------ saving sub frames ------------
01286         //
01287         for (int a = 0; a < nr_angles; a++) {
01288             for (int i = 1; i <= nr_devices; i++) {
01289                 sx = a * nr_devices + (i - 1);
01290 
01291                 // load stored data again
01292                 KMO_TRY_EXIT_IF_NULL(
01293                         stored_flat[sx] = kmclipm_image_load(fn_flat,
01294                                 CPL_TYPE_FLOAT, 0, sx));
01295                 KMO_TRY_EXIT_IF_NULL(
01296                         stored_noise[sx] = kmclipm_image_load(fn_noise,
01297                                 CPL_TYPE_FLOAT, 0, sx));
01298                 KMO_TRY_EXIT_IF_NULL(
01299                         stored_badpix[sx] = kmclipm_image_load(fn_badpix,
01300                                 CPL_TYPE_FLOAT, 0, sx));
01301 
01302                 KMO_TRY_EXIT_IF_NULL(
01303                     sub_header = kmo_dfs_load_sub_header(frameset, FLAT_ON, i,
01304                                                          FALSE));
01305                 cpl_propertylist_erase(sub_header, CRPIX1);
01306                 cpl_propertylist_erase(sub_header, CRPIX2);
01307 
01308                 KMO_TRY_EXIT_IF_ERROR(
01309                     kmclipm_update_property_double(sub_header,CAL_ROTANGLE,
01310                                                    ((double) rotang_found[a]),
01311                                                    "[deg] Rotator relative to nasmyth"));
01312 
01313                 if (i == 1) {
01314                     for (int ii = 0; ii < nr_devices; ii++) {
01315                         for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01316                             if (all_bounds[a][ii][2*j] > -1) {
01317                                 KMO_TRY_EXIT_IF_NULL(
01318                                         tmpstr= cpl_sprintf("%s%d%s",
01319                                                 BOUNDS_PREFIX,
01320                                                 ii*KMOS_IFUS_PER_DETECTOR + j+1,
01321                                                 "_L"));
01322                                 KMO_TRY_EXIT_IF_ERROR(
01323                                         kmclipm_update_property_int(sub_header,
01324                                                 tmpstr, all_bounds[a][ii][2*j],
01325                                                 "[pix] left boundary for reconstr."));
01326                                 cpl_free(tmpstr); tmpstr = NULL;
01327                             }
01328 
01329                             if (all_bounds[a][ii][2*j+1] > -1) {
01330                                 KMO_TRY_EXIT_IF_NULL(
01331                                         tmpstr= cpl_sprintf("%s%d%s",
01332                                                 BOUNDS_PREFIX,
01333                                                 ii-1*KMOS_IFUS_PER_DETECTOR + j+1,
01334                                                 "_R"));
01335                                 KMO_TRY_EXIT_IF_ERROR(
01336                                         kmclipm_update_property_int(sub_header,
01337                                                 tmpstr, all_bounds[a][ii][2*j+1],
01338                                                 "[pix] right boundary for reconstr."));
01339                                 cpl_free(tmpstr); tmpstr = NULL;
01340                             }
01341                         }
01342                     } // for (nr_devices)
01343 
01344                 }
01345 
01346                 if (spec_found[sx] == CPL_ERROR_NONE) {
01347                     KMO_TRY_EXIT_IF_ERROR(
01348                         kmclipm_update_property_int(sub_header,
01349                                                     QC_FLAT_SAT,
01350                                                     stored_qc_flat_sat[sx],
01351                                          "[] nr. saturated pixels of master flat"));
01352                     // load gain
01353                     gain = kmo_dfs_get_property_double(sub_header, GAIN);
01354                     KMO_TRY_CHECK_ERROR_STATE_MSG(
01355                                          "GAIN-keyword in fits-header is missing!");
01356 
01357                     KMO_TRY_EXIT_IF_ERROR(
01358                         kmclipm_update_property_double(sub_header,
01359                                                        QC_FLAT_EFF,
01360                                                        stored_qc_flat_eff[sx]/gain,
01361                                             "[e-/s] rel. brightness of flat lamp"));
01362                     KMO_TRY_EXIT_IF_ERROR(
01363                         kmclipm_update_property_double(sub_header,
01364                                                        QC_FLAT_SN,
01365                                                        stored_qc_flat_sn[sx],
01366                                                        "[] S/N of master flat"));
01367                 }
01368 
01369                 // store qc parameters only if any slitlet- and gap-width has been
01370                 // detected (should be the case when at least one IFU is active)
01371                 if (stored_xcal[sx] != NULL) {
01372                     KMO_TRY_EXIT_IF_ERROR(
01373                         kmclipm_update_property_double(sub_header,
01374                                                        QC_GAP_MEAN,
01375                                                        stored_gapmean[sx],
01376                                           "[pix] mean gap width between slitlets"));
01377                     KMO_TRY_EXIT_IF_ERROR(
01378                         kmclipm_update_property_double(sub_header,
01379                                                        QC_GAP_SDV,
01380                                                        stored_gapsdv[sx],
01381                                       "[pix] stdev of gap width between slitlets"));
01382                     KMO_TRY_EXIT_IF_ERROR(
01383                         kmclipm_update_property_double(sub_header,
01384                                                        QC_GAP_MAXDEV,
01385                                                        stored_gapmaxdev[sx],
01386                                        "[pix] max gap deviation between slitlets"));
01387                     KMO_TRY_EXIT_IF_ERROR(
01388                         kmclipm_update_property_double(sub_header,
01389                                                        QC_SLIT_MEAN,
01390                                                        stored_slitmean[sx],
01391                                                        "[pix] mean slitlet width"));
01392                     KMO_TRY_EXIT_IF_ERROR(
01393                         kmclipm_update_property_double(sub_header,
01394                                                        QC_SLIT_SDV,
01395                                                        stored_slitsdv[sx],
01396                                                   "[pix] stdev of slitlet widths"));
01397                     KMO_TRY_EXIT_IF_ERROR(
01398                         kmclipm_update_property_double(sub_header,
01399                                                        QC_SLIT_MAXDEV,
01400                                                        stored_slitmaxdev[sx],
01401                                               "[pix] max slitlet width deviation"));
01402                 }
01403 
01404                 if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
01405                     stored_xcal[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01406                     stored_ycal[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01407                 }
01408 
01409                 // calculate QC.BADPIX.NCOUNT
01410                 nr_bad_pix = cpl_image_count_rejected(stored_badpix[sx]);
01411                 KMO_TRY_CHECK_ERROR_STATE();
01412 
01413                 // remove 4pixel-border as bad pixels
01414                 nr_bad_pix -= 2*KMOS_BADPIX_BORDER*(nx-2*KMOS_BADPIX_BORDER) +
01415                                   2*KMOS_BADPIX_BORDER*ny;
01416 
01417                 KMO_TRY_EXIT_IF_ERROR(
01418                     kmclipm_update_property_int(sub_header,
01419                                                 QC_NR_BAD_PIX,
01420                                                 nr_bad_pix,
01421                                                 "[] nr. of bad pixels"));
01422                 // save flat frame
01423                 KMO_TRY_EXIT_IF_NULL(
01424                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
01425                 KMO_TRY_EXIT_IF_ERROR(
01426                     kmclipm_update_property_string(sub_header, EXTNAME,
01427                                                    extname,
01428                                                    "FITS extension name"));
01429                 cpl_free(extname); extname = NULL;
01430 
01431                KMO_TRY_EXIT_IF_ERROR(
01432                     kmo_dfs_save_image(stored_flat[sx], filename_flat,
01433                                        suffix, sub_header, 0./0.));
01434 
01435                 // save noise frame only when enough input frames were available
01436                 KMO_TRY_EXIT_IF_NULL(
01437                     extname = kmo_extname_creator(detector_frame, i,
01438                                                   EXT_NOISE));
01439                 KMO_TRY_EXIT_IF_ERROR(
01440                     kmclipm_update_property_string(sub_header, EXTNAME,
01441                                                    extname,
01442                                                    "FITS extension name"));
01443                 cpl_free(extname); extname = NULL;
01444 
01445                 KMO_TRY_EXIT_IF_ERROR(
01446                     kmo_dfs_save_image(stored_noise[sx], filename_flat,
01447                                        suffix, sub_header, 0./0.));
01448 
01449                 // save bad_pix frame
01450                 KMO_TRY_EXIT_IF_NULL(
01451                     extname = kmo_extname_creator(detector_frame, i, EXT_BADPIX));
01452                 KMO_TRY_EXIT_IF_ERROR(
01453                     kmclipm_update_property_string(sub_header, EXTNAME,
01454                                                    extname,
01455                                                    "FITS extension name"));
01456                 cpl_free(extname); extname = NULL;
01457 
01458                 KMO_TRY_EXIT_IF_ERROR(
01459                     kmo_dfs_save_image(stored_badpix[sx], filename_bad,
01460                                        suffix, sub_header, 0.));
01461 
01462                 // save xcal and ycal-frame
01463                 KMO_TRY_EXIT_IF_NULL(
01464                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
01465                 KMO_TRY_EXIT_IF_ERROR(
01466                     kmclipm_update_property_string(sub_header, EXTNAME,
01467                                                    extname,
01468                                                    "FITS extension name"));
01469                 cpl_free(extname); extname = NULL;
01470 
01471                 KMO_TRY_EXIT_IF_ERROR(
01472                     kmo_dfs_save_image(stored_xcal[sx], filename_xcal,
01473                                        suffix, sub_header, 0./0.));
01474 
01475                 KMO_TRY_EXIT_IF_ERROR(
01476                     kmo_dfs_save_image(stored_ycal[sx], filename_ycal,
01477                                        suffix, sub_header, 0./0.));
01478 
01479                 // save edge_pars-frame
01480                 KMO_TRY_EXIT_IF_NULL(
01481                     extname = kmo_extname_creator(list_frame, i, EXT_DATA));
01482                 KMO_TRY_EXIT_IF_ERROR(
01483                     kmclipm_update_property_string(sub_header, EXTNAME,
01484                                                    extname,
01485                                                    "FITS extension name"));
01486                 cpl_free(extname); extname = NULL;
01487 
01488                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01489                     KMO_TRY_EXIT_IF_ERROR(
01490                         kmclipm_update_property_int(sub_header, CAL_IFU_NR,
01491                                                        j+1+(i-1)*KMOS_IFUS_PER_DETECTOR,
01492                                                        "IFU Number {1..24}"));
01493 
01494                     // save edge-parameters as product
01495                     KMO_TRY_EXIT_IF_ERROR(
01496                         kmo_dfs_save_table(edge_table[sx][j], filename_edge, suffix,
01497                                            sub_header));
01498                 }
01499 
01500                 cpl_propertylist_delete(sub_header); sub_header = NULL;
01501 
01502                 cpl_image_delete(stored_flat[sx]); stored_flat[sx] = NULL;
01503                 cpl_image_delete(stored_noise[sx]); stored_noise[sx] = NULL;
01504                 cpl_image_delete(stored_badpix[sx]); stored_badpix[sx] = NULL;
01505 
01506             }
01507         }
01508     }
01509     KMO_CATCH
01510     {
01511         KMO_CATCH_MSG();
01512         ret_val = -1;
01513     }
01514     // delete temporary files
01515     unlink(fn_flat);
01516     unlink(fn_noise);
01517     unlink(fn_badpix);
01518 
01519     kmo_free_fits_desc(&desc1);
01520     kmo_free_fits_desc(&desc2);
01521     kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
01522     kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
01523     cpl_propertylist_delete(main_header); main_header = NULL;
01524     cpl_propertylist_delete(main_header_xcal); main_header_xcal = NULL;
01525     cpl_propertylist_delete(sub_header); sub_header = NULL;
01526     cpl_imagelist_delete(det_lamp_on); det_lamp_on = NULL;
01527     cpl_imagelist_delete(det_lamp_off); det_lamp_off = NULL;
01528     cpl_image_delete(combined_data_off); combined_data_off = NULL;
01529     cpl_image_delete(combined_noise_off); combined_noise_off = NULL;
01530     cpl_image_delete(bad_pix_mask_dark); bad_pix_mask_dark = NULL;
01531     cpl_free(stored_qc_flat_sat); stored_qc_flat_sat = NULL;
01532     cpl_free(stored_qc_flat_eff); stored_qc_flat_eff = NULL;
01533     cpl_free(stored_qc_flat_sn); stored_qc_flat_sn = NULL;
01534     cpl_free(stored_gapmean); stored_gapmean = NULL;
01535     cpl_free(stored_gapsdv); stored_gapsdv = NULL;
01536     cpl_free(stored_gapmaxdev); stored_gapmaxdev = NULL;
01537     cpl_free(stored_slitmean); stored_slitmean = NULL;
01538     cpl_free(stored_slitsdv); stored_slitsdv = NULL;
01539     cpl_free(stored_slitmaxdev); stored_slitmaxdev = NULL;
01540     cpl_free(readmode); readmode = NULL;
01541     cpl_free(suffix); suffix = NULL;
01542     for (int i = 0; i < nr_devices; i++) {
01543         cpl_free(total_bounds[i]); total_bounds[i] = NULL;
01544     }
01545     cpl_free(total_bounds); total_bounds = NULL;
01546     for (int i = 0; i < nr_devices * nr_angles; i++) {
01547         cpl_image_delete(stored_flat[i]); stored_flat[i] = NULL;
01548         cpl_image_delete(stored_noise[i]); stored_noise[i] = NULL;
01549         cpl_image_delete(stored_badpix[i]); stored_badpix[i] = NULL;
01550         cpl_image_delete(stored_xcal[i]); stored_xcal[i] = NULL;
01551         cpl_image_delete(stored_ycal[i]); stored_ycal[i] = NULL;
01552     }
01553     cpl_free(stored_flat); stored_flat = NULL;
01554     cpl_free(stored_noise); stored_noise = NULL;
01555     cpl_free(stored_badpix); stored_badpix = NULL;
01556     cpl_free(stored_xcal); stored_xcal = NULL;
01557     cpl_free(stored_ycal); stored_ycal = NULL;
01558     for (int a = 0; a < nr_angles; a++) {
01559         cpl_frameset_delete(angle_frameset[a]); angle_frameset[a] = NULL;
01560         for (int i = 0; i < nr_devices; i++) {
01561             cpl_free(all_bounds[a][i]); all_bounds[a][i] = NULL;
01562         }
01563         cpl_free(all_bounds[a]); all_bounds[a] = NULL;
01564     }
01565     cpl_free(angle_frameset); angle_frameset = NULL;
01566     cpl_free(all_bounds); all_bounds = NULL;
01567     if (edge_table != NULL) {
01568         for (int i = 0; i < KMOS_NR_DETECTORS * nr_angles; i++) {
01569             if (edge_table[i] != NULL) {
01570                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01571                     cpl_table_delete(edge_table[i][j]);
01572                     edge_table[i][j] = NULL;
01573                 }
01574                 cpl_free(edge_table[i]); edge_table[i] = NULL;
01575             }
01576         }
01577         cpl_free(edge_table); edge_table = NULL;
01578     }
01579     if (bounds != NULL) {
01580         cpl_free(bounds); bounds = NULL;
01581     }
01582     if (spec_found != NULL) {
01583         cpl_free(spec_found); spec_found = NULL;
01584     }
01585 
01586     return ret_val;
01587 }
01588