KMOS Pipeline Reference Manual  1.1.3
kmo_flat.c
00001 /* $Id: kmo_flat.c,v 1.38 2013/04/19 14:39:24 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/04/19 14:39:24 $
00024  * $Revision: 1.38 $
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         KMO_TRY_CHECK_ERROR_STATE();
01149 
01150 // #############################################################################
01151 // ###           QC parameters & saving
01152 // #############################################################################
01153         //
01154         // ------------ load, update & save primary header ------------
01155         //
01156         KMO_TRY_EXIT_IF_NULL(
01157             main_header = kmo_dfs_load_primary_header(frameset, FLAT_ON));
01158 
01159         // update which IFUs are not used
01160         kmo_print_unused_ifus(unused_ifus_after, TRUE);
01161 
01162         KMO_TRY_EXIT_IF_ERROR(
01163             kmo_set_unused_ifus(unused_ifus_after, main_header, "kmo_flat"));
01164 
01165         // write main_header for data-, noise-, ycal- and badpix-frame
01166         // xcal gets additionally the boundaries of the IFUs for reconstruction
01167 
01168         // add here boundaries for reconstruction
01169         KMO_TRY_EXIT_IF_NULL(
01170             main_header_xcal = cpl_propertylist_new());
01171 
01172         KMO_TRY_EXIT_IF_NULL(
01173             total_bounds = (int**)cpl_malloc(nr_devices*sizeof(int*)));
01174         for (int i = 0; i < nr_devices; i++) {
01175             KMO_TRY_EXIT_IF_NULL(
01176                 total_bounds[i] = (int*) cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int)));
01177             for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01178                 total_bounds[i][2*j] = 2048;
01179                 total_bounds[i][2*j+1] = 0;
01180             }
01181         }
01182         KMO_TRY_CHECK_ERROR_STATE();
01183 
01184         KMO_TRY_EXIT_IF_NULL(
01185             all_bounds = (int***) cpl_malloc(nr_angles*sizeof(int**)));
01186         for (int a = 0; a < nr_angles; a++) {
01187             KMO_TRY_EXIT_IF_NULL(
01188                 all_bounds[a] = (int**) cpl_malloc(nr_devices*sizeof(int*)));
01189             for (int i = 0; i < nr_devices; i++) {
01190                 KMO_TRY_EXIT_IF_NULL(
01191                     all_bounds[a][i] = (int*) cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int)));
01192             }
01193         }
01194         KMO_TRY_CHECK_ERROR_STATE();
01195 
01196         for (int a = 0; a < nr_angles; a++) {
01197             for (int i = 0; i < nr_devices; i++) {
01198                 sx = a * nr_devices + i;
01199                 if (stored_ycal[sx] != NULL) {
01200                     KMO_TRY_EXIT_IF_NULL(
01201                         bounds = kmo_split_frame(stored_ycal[sx]));
01202                     for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01203                         all_bounds[a][i][2*j] = bounds[2*j];
01204                         all_bounds[a][i][2*j+1] = bounds[2*j+1];
01205 
01206                         if (total_bounds[i][2*j] >= 0 ) {
01207                             if (bounds[2*j] < 0) {
01208                                 total_bounds[i][2*j] = bounds[2*j];
01209                             } else {
01210                                 if (total_bounds[i][2*j] > bounds[2*j]) {
01211                                     total_bounds[i][2*j] = bounds[2*j];
01212                                 }
01213                             }
01214                         }
01215                         if (total_bounds[i][2*j+1] >= 0 ) {
01216                             if (bounds[2*j+1] < 0) {
01217                                 total_bounds[i][2*j+1] = bounds[2*j+1];
01218                             } else {
01219                                 if (total_bounds[i][2*j+1] < bounds[2*j+1]) {
01220                                     total_bounds[i][2*j+1] = bounds[2*j+1];
01221                                 }
01222                             }
01223                         }
01224                     }
01225                 } else {
01226                     // whole detector inactive
01227                     for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01228                         all_bounds[a][i][2*j] = -1;
01229                         all_bounds[a][i][2*j+1] = -1;
01230                         total_bounds[i][2*j] = -1;
01231                         total_bounds[i][2*j+1] = -1;
01232                     }
01233                 }
01234                 if (bounds != NULL) {
01235                     cpl_free(bounds); bounds = NULL;
01236                 }
01237             } // for (nr_devices)
01238         } // for (nr_angles)
01239 
01240         for (int i = 0; i < nr_devices; i++) {
01241             for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01242                 if (total_bounds[i][2*j] > -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                                     "_L"));
01248                     KMO_TRY_EXIT_IF_ERROR(
01249                             kmclipm_update_property_int(main_header_xcal,
01250                                     tmpstr, total_bounds[i][2*j],
01251                                     "[pix] left boundary for reconstr."));
01252                     cpl_free(tmpstr); tmpstr = NULL;
01253                 }
01254 
01255                 if (total_bounds[i][2*j+1] > -1) {
01256                     KMO_TRY_EXIT_IF_NULL(
01257                             tmpstr= cpl_sprintf("%s%d%s",
01258                                     BOUNDS_PREFIX,
01259                                     i*KMOS_IFUS_PER_DETECTOR + j+1,
01260                                     "_R"));
01261                     KMO_TRY_EXIT_IF_ERROR(
01262                             kmclipm_update_property_int(main_header_xcal,
01263                                     tmpstr, total_bounds[i][2*j+1],
01264                                     "[pix] right boundary for reconstr."));
01265                     cpl_free(tmpstr); tmpstr = NULL;
01266                 }
01267             }
01268         } // for (nr_devices)
01269 
01270         //
01271         // ------------ saving headers ------------
01272         //
01273         cpl_msg_info("","Saving data...");
01274 
01275         KMO_TRY_EXIT_IF_NULL(
01276             frame = kmo_dfs_get_frame(frameset, FLAT_ON));
01277 
01278         KMO_TRY_EXIT_IF_ERROR(
01279             kmo_dfs_save_main_header(frameset, filename_flat, suffix, frame,
01280                                      main_header, parlist, cpl_func));
01281         KMO_TRY_EXIT_IF_ERROR(
01282             kmo_dfs_save_main_header(frameset, filename_xcal, suffix, frame,
01283                                      main_header_xcal, parlist, cpl_func));
01284         KMO_TRY_EXIT_IF_ERROR(
01285             kmo_dfs_save_main_header(frameset, filename_ycal, suffix, frame,
01286                                      main_header, parlist, cpl_func));
01287         KMO_TRY_EXIT_IF_ERROR(
01288             kmo_dfs_save_main_header(frameset, filename_bad, suffix, frame,
01289                                      main_header, parlist, cpl_func));
01290         KMO_TRY_EXIT_IF_ERROR(
01291             kmo_dfs_save_main_header(frameset, filename_edge, suffix, frame,
01292                                      main_header, parlist, cpl_func));
01293 
01294         cpl_propertylist_delete(main_header); main_header = NULL;
01295         cpl_propertylist_delete(main_header_xcal); main_header_xcal = NULL;
01296 
01297         //
01298         // ------------ saving sub frames ------------
01299         //
01300         for (int a = 0; a < nr_angles; a++) {
01301             for (int i = 1; i <= nr_devices; i++) {
01302                 sx = a * nr_devices + (i - 1);
01303 
01304                 // load stored data again
01305                 KMO_TRY_EXIT_IF_NULL(
01306                         stored_flat[sx] = kmclipm_image_load(fn_flat,
01307                                 CPL_TYPE_FLOAT, 0, sx));
01308                 KMO_TRY_EXIT_IF_NULL(
01309                         stored_noise[sx] = kmclipm_image_load(fn_noise,
01310                                 CPL_TYPE_FLOAT, 0, sx));
01311                 KMO_TRY_EXIT_IF_NULL(
01312                         stored_badpix[sx] = kmclipm_image_load(fn_badpix,
01313                                 CPL_TYPE_FLOAT, 0, sx));
01314 
01315                 KMO_TRY_EXIT_IF_NULL(
01316                     sub_header = kmo_dfs_load_sub_header(frameset, FLAT_ON, i,
01317                                                          FALSE));
01318                 cpl_propertylist_erase(sub_header, CRPIX1);
01319                 cpl_propertylist_erase(sub_header, CRPIX2);
01320 
01321                 KMO_TRY_EXIT_IF_ERROR(
01322                     kmclipm_update_property_double(sub_header,CAL_ROTANGLE,
01323                                                    ((double) rotang_found[a]),
01324                                                    "[deg] Rotator relative to nasmyth"));
01325 
01326                 if (i == 1) {
01327                     for (int ii = 0; ii < nr_devices; ii++) {
01328                         for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01329                             if (all_bounds[a][ii][2*j] > -1) {
01330                                 KMO_TRY_EXIT_IF_NULL(
01331                                         tmpstr= cpl_sprintf("%s%d%s",
01332                                                 BOUNDS_PREFIX,
01333                                                 ii*KMOS_IFUS_PER_DETECTOR + j+1,
01334                                                 "_L"));
01335                                 KMO_TRY_EXIT_IF_ERROR(
01336                                         kmclipm_update_property_int(sub_header,
01337                                                 tmpstr, all_bounds[a][ii][2*j],
01338                                                 "[pix] left boundary for reconstr."));
01339                                 cpl_free(tmpstr); tmpstr = NULL;
01340                             }
01341 
01342                             if (all_bounds[a][ii][2*j+1] > -1) {
01343                                 KMO_TRY_EXIT_IF_NULL(
01344                                         tmpstr= cpl_sprintf("%s%d%s",
01345                                                 BOUNDS_PREFIX,
01346                                                 ii-1*KMOS_IFUS_PER_DETECTOR + j+1,
01347                                                 "_R"));
01348                                 KMO_TRY_EXIT_IF_ERROR(
01349                                         kmclipm_update_property_int(sub_header,
01350                                                 tmpstr, all_bounds[a][ii][2*j+1],
01351                                                 "[pix] right boundary for reconstr."));
01352                                 cpl_free(tmpstr); tmpstr = NULL;
01353                             }
01354                         }
01355                     } // for (nr_devices)
01356 
01357                 }
01358 
01359                 if (spec_found[sx] == CPL_ERROR_NONE) {
01360                     KMO_TRY_EXIT_IF_ERROR(
01361                         kmclipm_update_property_int(sub_header,
01362                                                     QC_FLAT_SAT,
01363                                                     stored_qc_flat_sat[sx],
01364                                          "[] nr. saturated pixels of master flat"));
01365                     // load gain
01366                     gain = kmo_dfs_get_property_double(sub_header, GAIN);
01367                     KMO_TRY_CHECK_ERROR_STATE_MSG(
01368                                          "GAIN-keyword in fits-header is missing!");
01369 
01370                     KMO_TRY_EXIT_IF_ERROR(
01371                         kmclipm_update_property_double(sub_header,
01372                                                        QC_FLAT_EFF,
01373                                                        stored_qc_flat_eff[sx]/gain,
01374                                             "[e-/s] rel. brightness of flat lamp"));
01375                     KMO_TRY_EXIT_IF_ERROR(
01376                         kmclipm_update_property_double(sub_header,
01377                                                        QC_FLAT_SN,
01378                                                        stored_qc_flat_sn[sx],
01379                                                        "[] S/N of master flat"));
01380                 }
01381 
01382                 // store qc parameters only if any slitlet- and gap-width has been
01383                 // detected (should be the case when at least one IFU is active)
01384                 if (stored_xcal[sx] != NULL) {
01385                     KMO_TRY_EXIT_IF_ERROR(
01386                         kmclipm_update_property_double(sub_header,
01387                                                        QC_GAP_MEAN,
01388                                                        stored_gapmean[sx],
01389                                           "[pix] mean gap width between slitlets"));
01390                     KMO_TRY_EXIT_IF_ERROR(
01391                         kmclipm_update_property_double(sub_header,
01392                                                        QC_GAP_SDV,
01393                                                        stored_gapsdv[sx],
01394                                       "[pix] stdev of gap width between slitlets"));
01395                     KMO_TRY_EXIT_IF_ERROR(
01396                         kmclipm_update_property_double(sub_header,
01397                                                        QC_GAP_MAXDEV,
01398                                                        stored_gapmaxdev[sx],
01399                                        "[pix] max gap deviation between slitlets"));
01400                     KMO_TRY_EXIT_IF_ERROR(
01401                         kmclipm_update_property_double(sub_header,
01402                                                        QC_SLIT_MEAN,
01403                                                        stored_slitmean[sx],
01404                                                        "[pix] mean slitlet width"));
01405                     KMO_TRY_EXIT_IF_ERROR(
01406                         kmclipm_update_property_double(sub_header,
01407                                                        QC_SLIT_SDV,
01408                                                        stored_slitsdv[sx],
01409                                                   "[pix] stdev of slitlet widths"));
01410                     KMO_TRY_EXIT_IF_ERROR(
01411                         kmclipm_update_property_double(sub_header,
01412                                                        QC_SLIT_MAXDEV,
01413                                                        stored_slitmaxdev[sx],
01414                                               "[pix] max slitlet width deviation"));
01415                 }
01416 
01417                 if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
01418                     stored_xcal[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01419                     stored_ycal[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01420                 }
01421 
01422                 // calculate QC.BADPIX.NCOUNT
01423                 nr_bad_pix = cpl_image_count_rejected(stored_badpix[sx]);
01424                 KMO_TRY_CHECK_ERROR_STATE();
01425 
01426                 // remove 4pixel-border as bad pixels
01427                 nr_bad_pix -= 2*KMOS_BADPIX_BORDER*(nx-2*KMOS_BADPIX_BORDER) +
01428                                   2*KMOS_BADPIX_BORDER*ny;
01429 
01430                 KMO_TRY_EXIT_IF_ERROR(
01431                     kmclipm_update_property_int(sub_header,
01432                                                 QC_NR_BAD_PIX,
01433                                                 nr_bad_pix,
01434                                                 "[] nr. of bad pixels"));
01435                 // save flat frame
01436                 KMO_TRY_EXIT_IF_NULL(
01437                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
01438                 KMO_TRY_EXIT_IF_ERROR(
01439                     kmclipm_update_property_string(sub_header, EXTNAME,
01440                                                    extname,
01441                                                    "FITS extension name"));
01442                 cpl_free(extname); extname = NULL;
01443 
01444                 KMO_TRY_EXIT_IF_ERROR(
01445                     kmo_dfs_save_image(stored_flat[sx], filename_flat,
01446                                        suffix, sub_header, 0./0.));
01447 
01448                 // save noise frame only when enough input frames were available
01449                 KMO_TRY_EXIT_IF_NULL(
01450                     extname = kmo_extname_creator(detector_frame, i,
01451                                                   EXT_NOISE));
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_noise[sx], filename_flat,
01460                                        suffix, sub_header, 0./0.));
01461 
01462                 // save bad_pix frame
01463                 KMO_TRY_EXIT_IF_NULL(
01464                     extname = kmo_extname_creator(detector_frame, i, EXT_BADPIX));
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_badpix[sx], filename_bad,
01473                                        suffix, sub_header, 0.));
01474 
01475                 // save xcal and ycal-frame
01476                 KMO_TRY_EXIT_IF_NULL(
01477                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
01478                 KMO_TRY_EXIT_IF_ERROR(
01479                     kmclipm_update_property_string(sub_header, EXTNAME,
01480                                                    extname,
01481                                                    "FITS extension name"));
01482                 cpl_free(extname); extname = NULL;
01483 
01484                 KMO_TRY_EXIT_IF_ERROR(
01485                     kmo_dfs_save_image(stored_xcal[sx], filename_xcal,
01486                                        suffix, sub_header, 0./0.));
01487 
01488                 KMO_TRY_EXIT_IF_ERROR(
01489                     kmo_dfs_save_image(stored_ycal[sx], filename_ycal,
01490                                        suffix, sub_header, 0./0.));
01491 
01492                 // save edge_pars-frame
01493                 KMO_TRY_EXIT_IF_NULL(
01494                     extname = kmo_extname_creator(list_frame, i, EXT_DATA));
01495                 KMO_TRY_EXIT_IF_ERROR(
01496                     kmclipm_update_property_string(sub_header, EXTNAME,
01497                                                    extname,
01498                                                    "FITS extension name"));
01499                 cpl_free(extname); extname = NULL;
01500 
01501                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01502                     KMO_TRY_EXIT_IF_ERROR(
01503                         kmclipm_update_property_int(sub_header, CAL_IFU_NR,
01504                                                        j+1+(i-1)*KMOS_IFUS_PER_DETECTOR,
01505                                                        "IFU Number {1..24}"));
01506 
01507                     // save edge-parameters as product
01508                     if (spec_found[sx] != CPL_ERROR_DATA_NOT_FOUND) {
01509                         if (edge_table[sx][j] != NULL) {
01510                             KMO_TRY_EXIT_IF_ERROR(
01511                                 kmo_dfs_save_table(edge_table[sx][j], filename_edge, suffix, sub_header));
01512                         } else {
01513                             KMO_TRY_EXIT_IF_ERROR(
01514                                 kmo_dfs_save_table(NULL, filename_edge, suffix, sub_header));
01515                         }
01516                     } else {
01517                         KMO_TRY_EXIT_IF_ERROR(
01518                             kmo_dfs_save_table(NULL, filename_edge, suffix, sub_header));
01519                     }
01520                 }
01521 
01522                 cpl_propertylist_delete(sub_header); sub_header = NULL;
01523 
01524                 cpl_image_delete(stored_flat[sx]); stored_flat[sx] = NULL;
01525                 cpl_image_delete(stored_noise[sx]); stored_noise[sx] = NULL;
01526                 cpl_image_delete(stored_badpix[sx]); stored_badpix[sx] = NULL;
01527             } // for (i = nr_devices)
01528         } // for (a = nr_angles)
01529     }
01530     KMO_CATCH
01531     {
01532         KMO_CATCH_MSG();
01533         ret_val = -1;
01534     }
01535     // delete temporary files
01536     unlink(fn_flat);
01537     unlink(fn_noise);
01538     unlink(fn_badpix);
01539 
01540     kmo_free_fits_desc(&desc1);
01541     kmo_free_fits_desc(&desc2);
01542     kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
01543     kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
01544     cpl_propertylist_delete(main_header); main_header = NULL;
01545     cpl_propertylist_delete(main_header_xcal); main_header_xcal = NULL;
01546     cpl_propertylist_delete(sub_header); sub_header = NULL;
01547     cpl_imagelist_delete(det_lamp_on); det_lamp_on = NULL;
01548     cpl_imagelist_delete(det_lamp_off); det_lamp_off = NULL;
01549     cpl_image_delete(combined_data_off); combined_data_off = NULL;
01550     cpl_image_delete(combined_noise_off); combined_noise_off = NULL;
01551     cpl_image_delete(bad_pix_mask_dark); bad_pix_mask_dark = NULL;
01552     cpl_free(stored_qc_flat_sat); stored_qc_flat_sat = NULL;
01553     cpl_free(stored_qc_flat_eff); stored_qc_flat_eff = NULL;
01554     cpl_free(stored_qc_flat_sn); stored_qc_flat_sn = NULL;
01555     cpl_free(stored_gapmean); stored_gapmean = NULL;
01556     cpl_free(stored_gapsdv); stored_gapsdv = NULL;
01557     cpl_free(stored_gapmaxdev); stored_gapmaxdev = NULL;
01558     cpl_free(stored_slitmean); stored_slitmean = NULL;
01559     cpl_free(stored_slitsdv); stored_slitsdv = NULL;
01560     cpl_free(stored_slitmaxdev); stored_slitmaxdev = NULL;
01561     cpl_free(readmode); readmode = NULL;
01562     cpl_free(suffix); suffix = NULL;
01563     if (total_bounds != NULL) {
01564         for (int i = 0; i < nr_devices; i++) {
01565             cpl_free(total_bounds[i]); total_bounds[i] = NULL;
01566         }
01567     }
01568     cpl_free(total_bounds); total_bounds = NULL;
01569     for (int i = 0; i < nr_devices * nr_angles; i++) {
01570         cpl_image_delete(stored_flat[i]); stored_flat[i] = NULL;
01571         cpl_image_delete(stored_noise[i]); stored_noise[i] = NULL;
01572         cpl_image_delete(stored_badpix[i]); stored_badpix[i] = NULL;
01573         cpl_image_delete(stored_xcal[i]); stored_xcal[i] = NULL;
01574         cpl_image_delete(stored_ycal[i]); stored_ycal[i] = NULL;
01575     }
01576     cpl_free(stored_flat); stored_flat = NULL;
01577     cpl_free(stored_noise); stored_noise = NULL;
01578     cpl_free(stored_badpix); stored_badpix = NULL;
01579     cpl_free(stored_xcal); stored_xcal = NULL;
01580     cpl_free(stored_ycal); stored_ycal = NULL;
01581     for (int a = 0; a < nr_angles; a++) {
01582         cpl_frameset_delete(angle_frameset[a]); angle_frameset[a] = NULL;
01583         if ((all_bounds != NULL) && (all_bounds[a] != NULL)) {
01584             for (int i = 0; i < nr_devices; i++) {
01585                 cpl_free(all_bounds[a][i]); all_bounds[a][i] = NULL;
01586             }
01587             cpl_free(all_bounds[a]); all_bounds[a] = NULL;
01588         }
01589     }
01590     cpl_free(angle_frameset); angle_frameset = NULL;
01591     cpl_free(all_bounds); all_bounds = NULL;
01592     if (edge_table != NULL) {
01593         for (int i = 0; i < KMOS_NR_DETECTORS * nr_angles; i++) {
01594             if (edge_table[i] != NULL) {
01595                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01596                     cpl_table_delete(edge_table[i][j]);
01597                     edge_table[i][j] = NULL;
01598                 }
01599                 cpl_free(edge_table[i]); edge_table[i] = NULL;
01600             }
01601         }
01602         cpl_free(edge_table); edge_table = NULL;
01603     }
01604     if (bounds != NULL) {
01605         cpl_free(bounds); bounds = NULL;
01606     }
01607     if (spec_found != NULL) {
01608         cpl_free(spec_found); spec_found = NULL;
01609     }
01610 
01611     return ret_val;
01612 }
01613