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