KMOS Pipeline Reference Manual  1.2.4
yves_kmo_flat.c
00001 /* $Id: yves_kmo_flat.c,v 1.4 2013/01/27 03:07:33 yjung 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: yjung $
00023  * $Date: 2013/01/27 03:07:33 $
00024  * $Revision: 1.4 $
00025  * $Name: kmosp_v1_2_4__20130807 $
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  *                              Define
00051  *----------------------------------------------------------------------------*/
00052 
00053 #define FLAT_NAME       "MASTER_FLAT"
00054 #define BAD_NAME        "BADPIXEL_FLAT"
00055 #define XCAL_NAME       "XCAL"
00056 #define YCAL_NAME       "YCAL"
00057 #define LCAL_NAME       "LCAL"
00058 #define FLAT_EDGE_NAME  "FLAT_EDGE"
00059 #define ANGLE_DIM       360
00060 
00061 /*-----------------------------------------------------------------------------
00062  *                          Functions prototypes
00063  *----------------------------------------------------------------------------*/
00064 
00065 static int kmo_flat_create(cpl_plugin *);
00066 static int kmo_flat_exec(cpl_plugin *);
00067 static int kmo_flat_destroy(cpl_plugin *);
00068 static int kmo_flat(cpl_parameterlist *, cpl_frameset *);
00069 
00070 /*-----------------------------------------------------------------------------
00071  *                          Static variables
00072  *----------------------------------------------------------------------------*/
00073 
00074 static char kmo_flat_description[15000] =
00075 "This recipe creates the master flat field and calibration frames needed for \n"
00076 "spatial calibration for all three detectors. It must be called after the \n"
00077 "kmo_dark-recipe, which generates a bad pixel mask (badpixel_dark.fits). The \n"
00078 "bad pixel mask will be updated in this recipe (goes into badpixel_flat.fits).\n"
00079 "As input at least 3 dark frames, 3 frames with the flat lamp on are recommen-\n"
00080 "ded. Additionally a badpixel mask from kmo_dark is required.\n"
00081 "\n"
00082 "The badpixel mask contains 0 for bad pixels and 1 for good ones.\n"
00083 "\n"
00084 "The structure of the resulting xcal and ycal frames is quite complex since the\n"
00085 "arrangement of the IFUs isn't just linear on the detector. Basically the inte-\n"
00086 "ger part of the calibration data shows the offset of each pixels centre in mas\n"
00087 "(Milli arcsec) from the field centre. The viewing of an IFU is 2800mas \n"
00088 "(14pix*0.2arcsec/pix). So the values in these two frames will vary between \n"
00089 "+/-1500 (One would expect 1400, but since the slitlets aren't expected to be \n"
00090 "exactly vertical, the values can even go up to around 1500). Additionally in \n"
00091 "the calibration data in y-direction the decimal part of the data designates \n"
00092 "the IFU to which the slitlet corresponds to (for each detector from 1 to 8).\n"
00093 "Because of the irregular arrangement of the IFUs not all x-direction calibra-\n"
00094 "tion data is found in xcal and similarly not all y-direction calibration data\n"
00095 "is located in ycal. For certain IFUs they are switched and/or flipped in x- or\n"
00096 "y-direction:\n"
00097 "For IFUs 1,2,3,4,13,14,15,16:      x- and y- data is switched\n"
00098 "For IFUs 17,18,19,20:          y-data is flipped \n"
00099 "For IFUs 21,22,23,24:          x-data is flipped \n"
00100 "For IFUs 5,6,7,8,9,10,11,12:       x- and y- data is switched and x- and \n"
00101 "                                      y- data is flipped\n"
00102 "\n"
00103 "Advanced features:\n"
00104 "------------------\n"
00105 "To create the badpixel mask the edges of all slitlets are fitted to a polyno-\n"
00106 "mial. Since it can happen that some of these fits (3 detectors * 8 IFUs * \n"
00107 "14slitlets * 2 edges  (left and right edge of slitlet)= 672 edges) fail, the\n"
00108 "fit parameters are themselves fitted again to detect any outliers. By default\n"
00109 "the parameters of all left and all right edges are grouped individually and \n"
00110 "then fitted using chebyshev polynomials. The advantage of a chebyshev polyno-\n"
00111 "mial is, that it consists in fact of a series of orthogonal polynomials. This\n"
00112 "implies that the parameters of the polynomials are independent. This fact pre-\n"
00113 "destines the use of chebyshev polynomials for our case. So each individual pa-\n"
00114 "rameter can be examined independently. The reason why the left and right edges\n"
00115 "are fitted individually is that there is a systematic pattern specific to \n"
00116 "these groups. The reason for this pattern is probably to be found in the opti-\n"
00117 "cal path the light is traversing.\n"
00118 "\n"
00119 "The behaviour of this fitting step can be influenced via environment parameters:\n"
00120 "* KF_ALLPARS (default: 1)\n"
00121 "  When set to 1 all coefficients of the polynomial of an edge are to be cor-\n"
00122 "  rected, also when just one of these coefficients is an outlier. When set to\n"
00123 "  0 only the outlier is to be corrected.\n"
00124 "* KF_CH (default: 1)\n"
00125 "  When set to 1 chebyshev polynomials are used to fit the fitted parameters.\n"
00126 "  When set to 0 normal polynomials are used.\n"
00127 "* KF_SIDES (default: 2)\n"
00128 "  This variable can either be set to 1 or 2. When set to 2 the left and right\n"
00129 "  edges are examined individually. When set to 1 all edges are examined as one\n"
00130 "  group.\n"
00131 "* KF_FACTOR(default: 4)\n"
00132 "  This factor defines the threshold factor. All parameters deviating \n"
00133 "  KF_FACTOR*stddev are to be corrected\n"
00134 "\n"
00135 "BASIC PARAMETERS:\n"
00136 "-----------------\n"
00137 "--surrounding_pixels\n"
00138 "The amount of bad pixels to surround a specific pixel, to let it be marked\n"
00139 "bad as well.\n"
00140 "\n"
00141 "--cmethod\n"
00142 "Following methods of frame combination are available:\n"
00143 "   * 'ksigma' (Default)\n"
00144 "   An iterative sigma clipping. For each position all pixels in the spectrum\n"
00145 "   are examined. If they deviate significantly, they will be rejected according\n"
00146 "   to the conditions:\n"
00147 "       val > mean + stdev * cpos_rej\n"
00148 "   and\n"
00149 "       val < mean - stdev * cneg_rej\n"
00150 "   where --cpos_rej, --cneg_rej and --citer are the corresponding configuration\n"
00151 "   parameters. In the first iteration median and percentile level are used.\n"
00152 "\n"
00153 "   * 'median'\n"
00154 "   At each pixel position the median is calculated.\n"
00155 "\n"
00156 "   * 'average'\n"
00157 "   At each pixel position the average is calculated.\n"
00158 "\n"
00159 "   * 'sum'\n"
00160 "   At each pixel position the sum is calculated.\n"
00161 "\n"
00162 "   * 'min_max'\n"
00163 "   The specified number of minimum and maximum pixel values will be rejected.\n"
00164 "   --cmax and --cmin apply to this method.\n"
00165 "\n"
00166 "ADVANCED PARAMETERS\n"
00167 "-------------------\n"
00168 "--cpos_rej\n"
00169 "--cneg_rej\n"
00170 "--citer\n"
00171 "see --cmethod='ksigma'\n"
00172 "\n"
00173 "--cmax\n"
00174 "--cmin\n"
00175 "see --cmethod='min_max'\n"
00176 "\n"
00177 "-------------------------------------------------------------------------------\n"
00178 "  Input files:\n"
00179 "\n"
00180 "   DO                    KMOS                                                  \n"
00181 "   category              Type   Explanation                    Required #Frames\n"
00182 "   --------              -----  -----------                    -------- -------\n"
00183 "   FLAT_ON               RAW    Flatlamp-on exposures             Y       1-n  \n"
00184 "                                (at least 3 frames recommended)                \n"
00185 "   FLAT_OFF              RAW    Flatlamp-off exposures            Y       1-n  \n"
00186 "                                (at least 3 frames recommended)                \n"
00187 "   BADPIXEL_DARK         B2D    Bad pixel mask                    Y        1   \n"
00188 "\n"
00189 "  Output files:\n"
00190 "\n"
00191 "   DO                    KMOS\n"
00192 "   category              Type   Explanation\n"
00193 "   --------              -----  -----------\n"
00194 "   MASTER_FLAT           F2D    Normalised flat field\n"
00195 "                                (6 extensions: alternating data & noise\n"
00196 "   BADPIXEL_FLAT         B2D    Updated bad pixel mask (3 Extensions)\n"
00197 "   XCAL                  F2D    Calibration frame 1 (3 Extensions)\n"
00198 "   YCAL                  F2D    Calibration frame 2 (3 Extensions)\n"
00199 "   FLAT_EDGE             F2L    Frame containing parameters of fitted \n"
00200 "                                slitlets of all IFUs of all detectors\n"
00201 "-------------------------------------------------------------------------------"
00202 "\n";
00203 
00204 /*-----------------------------------------------------------------------------
00205  *                              Functions code
00206  *----------------------------------------------------------------------------*/
00224 int cpl_plugin_get_info(cpl_pluginlist *list)
00225 {
00226     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00227     cpl_plugin *plugin = &recipe->interface;
00228 
00229     cpl_plugin_init(plugin,
00230                         CPL_PLUGIN_API,
00231                         KMOS_BINARY_VERSION,
00232                         CPL_PLUGIN_TYPE_RECIPE,
00233                         "kmo_flat",
00234                         "Create master flatfield frame and badpixel map to be "
00235                         "used during science reduction",
00236                         kmo_flat_description,
00237                         "Alex Agudo Berbel",
00238                         "agudo@mpe.mpg.de",
00239                         kmos_get_license(),
00240                         kmo_flat_create,
00241                         kmo_flat_exec,
00242                         kmo_flat_destroy);
00243 
00244     cpl_pluginlist_append(list, plugin);
00245 
00246     return 0;
00247 }
00248 
00256 static int kmo_flat_create(cpl_plugin *plugin)
00257 {
00258     cpl_recipe *recipe;
00259     cpl_parameter *p;
00260 
00261     // Check that the plugin is part of a valid recipe
00262     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00263         recipe = (cpl_recipe *)plugin;
00264     else
00265         return -1;
00266 
00267     // Create the parameters list in the cpl_recipe object
00268     recipe->parameters = cpl_parameterlist_new();
00269 
00270     // Fill the parameters list
00271     // --badpix_thresh
00272     p = cpl_parameter_new_value("kmos.kmo_flat.badpix_thresh",
00273                                 CPL_TYPE_INT,
00274                                 "The threshold level to mark pixels as bad on "
00275                                 "the dark subtracted frames [%].",
00276                                 "kmos.kmo_flat",
00277                                 35);
00278     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "badpix_thresh");
00279     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00280     cpl_parameterlist_append(recipe->parameters, p);
00281 
00282     // --surrounding_pixels
00283     p = cpl_parameter_new_value("kmos.kmo_flat.surrounding_pixels",
00284                                 CPL_TYPE_INT,
00285                                 "The amount of bad pixels to surround a specific "
00286                                 "pixel, to let it be marked bad as well.",
00287                                 "kmos.kmo_flat",
00288                                 5);
00289     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "surrounding_pixels");
00290     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00291     cpl_parameterlist_append(recipe->parameters, p);
00292 
00293     return kmo_combine_pars_create(recipe->parameters,
00294                                    "kmos.kmo_flat",
00295                                    DEF_REJ_METHOD,
00296                                    FALSE);
00297 }
00298 
00304 static int kmo_flat_exec(cpl_plugin *plugin)
00305 {
00306     cpl_recipe  *recipe;
00307 
00308     // Get the recipe out of the plugin
00309     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00310         recipe = (cpl_recipe *)plugin;
00311     else return -1;
00312 
00313     return kmo_flat(recipe->parameters, recipe->frames);
00314 }
00315 
00321 static int kmo_flat_destroy(cpl_plugin *plugin)
00322 {
00323     cpl_recipe *recipe;
00324 
00325     // Get the recipe out of the plugin
00326     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00327         recipe = (cpl_recipe *)plugin;
00328     else return -1 ;
00329 
00330     cpl_parameterlist_delete(recipe->parameters);
00331     return 0 ;
00332 }
00333 
00348 static int kmo_flat(cpl_parameterlist *parlist, cpl_frameset *frameset)
00349 {
00350     cpl_imagelist    *det_lamp_on           = NULL,
00351                      *det_lamp_off          = NULL;
00352 
00353     cpl_image        *img_in                = NULL,
00354                      *combined_data_on      = NULL,
00355                      *combined_noise_on     = NULL,
00356                      *combined_data_off     = NULL,
00357                      *combined_noise_off    = NULL,
00358                      *bad_pix_mask_flat     = NULL,
00359                      *bad_pix_mask_dark     = NULL,
00360                      *xcal                  = NULL,
00361                      *ycal                  = NULL;
00362 
00363     cpl_image        **stored_flat          = NULL,
00364                      **stored_noise         = NULL,
00365                      **stored_badpix        = NULL,
00366                      **stored_xcal          = NULL,
00367                      **stored_ycal          = NULL;
00368 
00369     cpl_frame        *frame                 = NULL;
00370     cpl_frameset     ** angle_frameset     = NULL;
00371 
00372     int              ret_val                = 0,
00373                      nr_devices             = 0,
00374                      i                      = 0,
00375                      ii                     = 0,
00376                      a                      = 0,
00377                      sx                     = 0,
00378                      j                      = 0,
00379                      cmax                   = 0,
00380                      cmin                   = 0,
00381                      citer                  = 0,
00382                      surrounding_pixels     = 0,
00383                      badpix_thresh          = 0,
00384                      nx                     = 0,
00385                      ny                     = 0,
00386                      nz                     = 0,
00387                      *stored_qc_flat_sat    = NULL,
00388                      *bounds                = NULL,
00389                      **total_bounds         = NULL,
00390                      ***all_bounds          = NULL,
00391                      nr_bad_pix             = 0,
00392                      ndit                   = 0,
00393                      nr_sat                 = 0,
00394                      nr_angles              = 0;
00395 
00396     double           cpos_rej               = 0.0,
00397                      cneg_rej               = 0.0,
00398                      gain                   = 0.0,
00399                      exptime                = 0.0,
00400                      *stored_qc_flat_eff    = NULL,
00401                      *stored_qc_flat_sn     = NULL,
00402                      mean_data              = 0.0,
00403                      mean_noise             = 0.0,
00404                      *stored_gapmean        = NULL,
00405                      *stored_gapsdv         = NULL,
00406                      *stored_gapmaxdev      = NULL,
00407                      *stored_slitmean       = NULL,
00408                      *stored_slitsdv        = NULL,
00409                      *stored_slitmaxdev     = NULL;
00410 
00411     const char       *cmethod               = NULL;
00412 
00413     char             *extname               = NULL,
00414                      *suffix                = NULL,
00415                      *tmpstr                = NULL,
00416                      *readmode              = NULL;
00417 
00418     cpl_propertylist *main_header           = NULL,
00419                      *main_header_xcal      = NULL,
00420                      *sub_header            = NULL;
00421 
00422     cpl_table       ***edge_table           = NULL;
00423 
00424     main_fits_desc   desc1, desc2;
00425 
00426     cpl_array        **unused_ifus_before   = NULL,
00427                      **unused_ifus_after    = NULL;
00428 
00429     cpl_error_code  *spec_found             = NULL;
00430 
00431     KMO_TRY
00432     {
00433         kmo_init_fits_desc(&desc1);
00434         kmo_init_fits_desc(&desc2);
00435 
00436         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, BADPIXEL_DARK) == 1,
00437                        CPL_ERROR_NULL_INPUT,
00438                        "A BADPIXEL_DARK frame must be provided!");
00439 
00440         KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_flat") == 1,
00441                        CPL_ERROR_ILLEGAL_INPUT,
00442                        "Cannot identify RAW and CALIB frames!");
00443 
00444         // ------------ get parameters ------------
00445         surrounding_pixels = kmo_dfs_get_parameter_int(parlist,
00446                 "kmos.kmo_flat.surrounding_pixels");
00447         kmo_dfs_print_parameter_help(parlist, 
00448                 "kmos.kmo_flat.surrounding_pixels");
00449         badpix_thresh = kmo_dfs_get_parameter_int(parlist,
00450                 "kmos.kmo_flat.badpix_thresh");
00451         kmo_dfs_print_parameter_help(parlist,
00452                 "kmos.kmo_flat.badpix_thresh");
00453         kmo_combine_pars_load(parlist, "kmos.kmo_flat", &cmethod, &cpos_rej,
00454                               &cneg_rej, &citer, &cmin, &cmax, FALSE);
00455 
00456         // check BADPIXEL_DARK
00457         frame = kmo_dfs_get_frame(frameset, BADPIXEL_DARK);
00458 
00459         desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00460 
00461         KMO_TRY_ASSURE((desc2.nr_ext == 3) &&
00462                        (desc2.ex_badpix == TRUE) &&
00463                        (desc2.fits_type == b2d_fits) &&
00464                        (desc2.frame_type == detector_frame),
00465                        CPL_ERROR_ILLEGAL_INPUT,
00466                        "BADPIXEL_DARK isn't in the correct format!!!");
00467 
00468         nx = desc2.naxis1;
00469         ny = desc2.naxis2;
00470         nz = desc2.naxis3;
00471 
00472         KMO_TRY_ASSURE((surrounding_pixels >= 0) && (surrounding_pixels <= 8),
00473                        CPL_ERROR_ILLEGAL_INPUT,
00474                        "surrounding_pixels must be between 0 and 8!");
00475 
00476         KMO_TRY_ASSURE((badpix_thresh >= 0) && (badpix_thresh <= 100),
00477                        CPL_ERROR_ILLEGAL_INPUT,
00478                        "badpix_thresh must be between 0 and 100%%!");
00479 
00480         // Get the first OFF frame
00481         frame = kmo_dfs_get_frame(frameset, FLAT_OFF);
00482 
00483         // Get EXPTIME, NDIT, READMODE
00484         main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00485         ndit = cpl_propertylist_get_int(main_header, NDIT);
00486         exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00487         readmode = cpl_strdup(cpl_propertylist_get_string(main_header, READMODE));
00488         cpl_propertylist_delete(main_header); main_header = NULL;
00489 
00490         // Loop on the other OFF frames
00491         while (frame != NULL) {
00492             // Get the Header of the current frame (again ?)
00493             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00494 
00495             KMO_TRY_ASSURE(cpl_propertylist_get_int(main_header, NDIT) == ndit,
00496                            CPL_ERROR_ILLEGAL_INPUT,
00497                            "NDIT isn't the same for all frames: (is %d and %d).",
00498                             cpl_propertylist_get_int(main_header, NDIT), ndit);
00499             KMO_TRY_ASSURE(cpl_propertylist_get_double(main_header, EXPTIME) == exptime,
00500                            CPL_ERROR_ILLEGAL_INPUT,
00501                            "EXPTIME isn't the same for all frames: (is %g and %g).",
00502                            cpl_propertylist_get_double(main_header, EXPTIME), exptime);
00503             KMO_TRY_ASSURE(strcmp(cpl_propertylist_get_string(main_header, READMODE), readmode) == 0,
00504                            CPL_ERROR_ILLEGAL_INPUT,
00505                            "ESO DET READ CURNAME isn't the same for all frames: (is %s and %s).",
00506                            cpl_propertylist_get_string(main_header, READMODE), readmode);
00507 
00508             desc1 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00509 
00510             // Verifications
00511             KMO_TRY_ASSURE((desc1.naxis1 == nx) &&
00512                            (desc1.naxis2 == ny) &&
00513                            (desc1.naxis3 == nz),
00514                            CPL_ERROR_ILLEGAL_INPUT,
00515                            "File (%s) hasn't correct dimensions! (x,y): "
00516                            "(%d,%d) vs (%d,%d)",
00517                            cpl_frame_get_filename(frame),
00518                            desc1.naxis1, desc1.naxis2, nx, ny);
00519             KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP1_ST) == FALSE) &&
00520                            (kmo_check_lamp(main_header, INS_LAMP2_ST) == FALSE),
00521                            CPL_ERROR_ILLEGAL_INPUT,
00522                            "Arc lamps must be switched off (%s)!",
00523                            cpl_frame_get_filename(frame));
00524             cpl_propertylist_delete(main_header); main_header = NULL;
00525             kmo_free_fits_desc(&desc1);
00526 
00527             // get next FLAT_OFF frame
00528             frame = kmo_dfs_get_frame(frameset, NULL);
00529         }
00530         cpl_free(readmode) ;
00531 
00532         // Get the first ON frame
00533         frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00534 
00535         // Get EXPTIME, NDIT, READMODE
00536         main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00537         ndit = cpl_propertylist_get_int(main_header, NDIT);
00538         exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00539         readmode = cpl_strdup(cpl_propertylist_get_string(main_header, READMODE));
00540         cpl_propertylist_delete(main_header); main_header = NULL;
00541 
00542         // Loop on the other ON frames
00543         while (frame != NULL) {
00544             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00545 
00546             KMO_TRY_ASSURE(cpl_propertylist_get_int(main_header, NDIT) == ndit,
00547                            CPL_ERROR_ILLEGAL_INPUT,
00548                            "NDIT isn't the same for all frames: (is %d and %d).",
00549                             cpl_propertylist_get_int(main_header, NDIT), ndit);
00550             KMO_TRY_ASSURE(cpl_propertylist_get_double(main_header, EXPTIME) == exptime,
00551                            CPL_ERROR_ILLEGAL_INPUT,
00552                            "EXPTIME isn't the same for all frames: (is %g and %g).",
00553                            cpl_propertylist_get_double(main_header, EXPTIME), exptime);
00554             KMO_TRY_ASSURE(strcmp(cpl_propertylist_get_string(main_header, READMODE), readmode) == 0,
00555                            CPL_ERROR_ILLEGAL_INPUT,
00556                            "ESO DET READ CURNAME isn't the same for all frames: (is %s and %s).",
00557                            cpl_propertylist_get_string(main_header, READMODE), readmode);
00558 
00559             desc1 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00560 
00561             // Verifications
00562             KMO_TRY_ASSURE((desc1.naxis1 == nx) &&
00563                            (desc1.naxis2 == ny) &&
00564                            (desc1.naxis3 == nz),
00565                            CPL_ERROR_ILLEGAL_INPUT,
00566                            "File (%s) hasn't correct dimensions! (x,y): "
00567                            "(%d,%d) vs (%d,%d)",
00568                            cpl_frame_get_filename(frame),
00569                            desc1.naxis1, desc1.naxis2, nx, ny);
00570             KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP1_ST) == FALSE) &&
00571                            (kmo_check_lamp(main_header, INS_LAMP2_ST) == FALSE),
00572                            CPL_ERROR_ILLEGAL_INPUT,
00573                            "Arc lamps must be switched off (%s)!",
00574                            cpl_frame_get_filename(frame));
00575             KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP3_ST) == TRUE) ||
00576                            (kmo_check_lamp(main_header, INS_LAMP4_ST) == TRUE),
00577                            CPL_ERROR_ILLEGAL_INPUT,
00578                            "At least one flat lamps must be switched on (%s)!",
00579                            cpl_frame_get_filename(frame));
00580 
00581             cpl_propertylist_delete(main_header); main_header = NULL;
00582             kmo_free_fits_desc(&desc1);
00583 
00584             // get next FLAT_ON frame
00585             frame = kmo_dfs_get_frame(frameset, NULL);
00586         }
00587 
00588         // ------------ check filter_id, grating_id and rotator offset ---------
00589         //              must match for all detectors
00590         kmo_check_frameset_setup(frameset, FLAT_ON, TRUE, FALSE, FALSE);
00591 
00592         // Get the first ON frame
00593         frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00594         suffix = kmo_dfs_get_suffix(frame, TRUE, FALSE);
00595 
00596         cpl_msg_info("", "Detected instrument setup:   %s", suffix+1);
00597 
00598         /*********************************************************/
00599         /* Comment - This should be done in an external Function */
00600         int rotang_found[ANGLE_DIM];
00601         int rotang_cnt[ANGLE_DIM];
00602         for (int i=0; i<ANGLE_DIM; i++) {
00603             rotang_found[i] = 0;
00604             rotang_cnt[i] = 0;
00605         }
00606         // Get the first ON frame
00607         frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00608         while (frame != NULL) {
00609             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00610             if (cpl_propertylist_has(main_header, ROTANGLE)) {
00611                 int rot_angle = (int) rint(cpl_propertylist_get_double(main_header, ROTANGLE));
00612                 if (rot_angle < 0) {
00613                     rot_angle += 360;
00614                 }
00615                 if (rot_angle < 360 && rot_angle >= 0) 
00616                     rotang_cnt[rot_angle]++;
00617             } else {
00618                 cpl_msg_warning("","File %s has no keyword \"ROTANGLE\"",
00619                         cpl_frame_get_filename(frame));
00620             }
00621 
00622             cpl_propertylist_delete(main_header); main_header = NULL;
00623             frame = kmo_dfs_get_frame(frameset, NULL);
00624         }
00625         nr_angles = 0;
00626         for (int ax=0; ax<ANGLE_DIM; ax++) {
00627             if (rotang_cnt[ax] != 0) {
00628                 rotang_found[nr_angles] = ax;
00629                 nr_angles++;
00630             }
00631         }
00632 
00633         /* Create one frameset per angle */
00634         angle_frameset = (cpl_frameset **) cpl_malloc(nr_angles * sizeof(cpl_frameset*));
00635         for (int i =0; i<nr_angles; i++) {
00636             angle_frameset[i] = cpl_frameset_new();
00637         }
00638 
00639         // Get the first ON frame
00640         frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00641         while (frame != NULL) {
00642             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00643             if (cpl_propertylist_has(main_header, ROTANGLE)) {
00644                 int rot_angle = (int) rint(cpl_propertylist_get_double(main_header, ROTANGLE));
00645                 if (rot_angle < 0) {
00646                     rot_angle += 360;
00647                 }
00648                 int ix = -1;
00649                 for (ix = 0; ix<nr_angles; ix++) {
00650                     if (rotang_found[ix] == rot_angle) {
00651                         break;
00652                     }
00653                 }
00654                 if (ix<nr_angles) {
00655                     KMO_TRY_EXIT_IF_ERROR(
00656                         cpl_frameset_insert(angle_frameset[ix], cpl_frame_duplicate(frame)));
00657                 }
00658             }
00659 
00660             cpl_propertylist_delete(main_header); main_header = NULL;
00661             frame = kmo_dfs_get_frame(frameset, NULL);
00662             KMO_TRY_CHECK_ERROR_STATE();
00663         }
00664         /* End Comment */
00665         /********************************************************/
00666 
00667         // check which IFUs are active for all FLAT_ON frames
00668         unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0);
00669         unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before);
00670         kmo_print_unused_ifus(unused_ifus_before, FALSE);
00671         cpl_msg_info("", "EXPTIME:  %g seconds", exptime);
00672         cpl_msg_info("", "NDIT: %d", ndit);
00673         cpl_msg_info("", "Detector readout mode: %s", readmode);
00674 
00675         // #########################################################
00676         // ###           allocate temporary memory
00677         // #########################################################
00678         // Get the first ON frame
00679         frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00680         
00681         desc1 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00682 
00683         nr_devices = desc1.nr_ext;
00684 
00685         // the frames have to be stored temporarily because the QC parameters
00686         // for the main header are calculated from each detector. So they can be
00687         // stored only when all detectors are processed
00688         stored_flat =   (cpl_image**)cpl_calloc(nr_devices * nr_angles, sizeof(cpl_image*));
00689         stored_noise =  (cpl_image**)cpl_calloc(nr_devices * nr_angles, sizeof(cpl_image*));
00690         stored_badpix = (cpl_image**)cpl_calloc(nr_devices * nr_angles, sizeof(cpl_image*));
00691         stored_xcal =   (cpl_image**)cpl_calloc(nr_devices * nr_angles, sizeof(cpl_image*));
00692         stored_ycal =   (cpl_image**)cpl_calloc(nr_devices * nr_angles, sizeof(cpl_image*));
00693         stored_qc_flat_sat =    (int*)cpl_malloc(nr_devices * nr_angles * sizeof(int));
00694         stored_qc_flat_eff =    (double*)cpl_malloc(nr_devices * nr_angles * sizeof(double));
00695         stored_qc_flat_sn =     (double*)cpl_malloc(nr_devices * nr_angles * sizeof(double));
00696         stored_gapmean =        (double*)cpl_malloc(nr_devices * nr_angles * sizeof(double));
00697         stored_gapsdv =         (double*)cpl_malloc(nr_devices * nr_angles * sizeof(double));
00698         stored_gapmaxdev =      (double*)cpl_malloc(nr_devices * nr_angles * sizeof(double));
00699         stored_slitmean =       (double*)cpl_malloc(nr_devices * nr_angles * sizeof(double));
00700         stored_slitsdv =        (double*)cpl_malloc(nr_devices * nr_angles * sizeof(double));
00701         stored_slitmaxdev =     (double*)cpl_malloc(nr_devices * nr_angles * sizeof(double));
00702         spec_found =    (cpl_error_code*)cpl_malloc(nr_devices * nr_angles * sizeof(cpl_error_code));
00703         edge_table =    (cpl_table***)cpl_malloc(KMOS_NR_DETECTORS * nr_angles * sizeof(cpl_table**));
00704 
00705         // initialize to NULL
00706         for (i = 0; i < nr_devices * nr_angles ; i++) {
00707             stored_qc_flat_sat[i] = 0.0;
00708             stored_qc_flat_eff[i] = 0.0;
00709             stored_qc_flat_sn[i] = 0.0;
00710             stored_gapmean[i] = 0.0;
00711             stored_gapsdv[i] = 0.0;
00712             stored_gapmaxdev[i] = 0.0;
00713             stored_slitmean[i] = 0.0;
00714             stored_slitsdv[i] = 0.0;
00715             stored_slitmaxdev[i] = 0.0;
00716             spec_found[i] = CPL_ERROR_NONE;
00717         }
00718         for (int i = 0; i < KMOS_NR_DETECTORS * nr_angles; i++) edge_table[i] = NULL;
00719 
00720         // #############################################################################
00721         // ###           process data
00722         // #############################################################################
00723           
00724         /* ------------ loop all rotator angles and detectors ------------ */
00725         for (a = 0; a<nr_angles; a++) {
00726             cpl_msg_info("","Processing rotator angle %d -> %d degree", a,rotang_found[a]);
00727             for (i = 1; i <= nr_devices; i++) {
00728                 cpl_msg_info("","    Processing detector No. %d", i);
00729                 
00730                 sx = a * nr_devices + (i - 1);
00731 
00732                 bad_pix_mask_dark = kmo_dfs_load_image(frameset, BADPIXEL_DARK,
00733                         i, 2, FALSE, NULL);
00734 
00735                 /* ------------ load all lamp_on and lamp_off images ------------ */
00736                 det_lamp_on = cpl_imagelist_new();
00737                 det_lamp_off = cpl_imagelist_new();
00738 
00739                 /* load lamp-on images */
00740                 frame = kmo_dfs_get_frame(angle_frameset[a], FLAT_ON);
00741                 j = 0;
00742                 while (frame != NULL) {
00743                     img_in = kmo_dfs_load_image_frame(frame, i, FALSE, TRUE, &nr_sat);
00744                     kmo_image_reject_from_mask(img_in, bad_pix_mask_dark);
00745                     cpl_imagelist_set(det_lamp_on, img_in, j++);
00746                     frame = kmo_dfs_get_frame(angle_frameset[a], NULL);
00747                 }
00748 
00749                 /* load lamp-off images */
00750                 frame = kmo_dfs_get_frame(frameset, FLAT_OFF);
00751                 j = 0;
00752                 while (frame != NULL) {
00753                     img_in = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, NULL);
00754                     kmo_image_reject_from_mask(img_in, bad_pix_mask_dark);
00755                     cpl_imagelist_set(det_lamp_off, img_in, j++);
00756                     frame = kmo_dfs_get_frame(frameset, NULL);
00757                 }
00758 
00759                 /* ------------ process imagelist ------------ */
00760          
00761                 /* count saturated pixels for each detector */
00762                 frame = kmo_dfs_get_frame(angle_frameset[a], FLAT_ON);
00763                 main_header = kmclipm_propertylist_load(
00764                         cpl_frame_get_filename(frame), 0);
00765                 if (strcmp(cpl_propertylist_get_string(main_header, READMODE), "Nondest") == 0) {
00766                     // NDR: non-destructive readout mode
00767                     stored_qc_flat_sat[sx] = nr_sat;
00768                 } else {
00769                     // normal readout mode
00770                     stored_qc_flat_sat[sx] = kmo_imagelist_get_saturated(det_lamp_on, 
00771                             KMO_FLAT_SATURATED, KMO_FLAT_SAT_MIN);
00772                 }
00773                 cpl_propertylist_delete(main_header); main_header = NULL;
00774 
00775                 // combine imagelists and create noise
00776                 kmclipm_combine_frames(det_lamp_on, NULL,
00777                                        NULL,
00778                                        cmethod, cpos_rej, cneg_rej, citer,
00779                                        cmax, cmin,
00780                                        &combined_data_on,
00781                                        &combined_noise_on,
00782                                        -1.0);
00783 
00784                 kmclipm_combine_frames(det_lamp_off, NULL,
00785                                        NULL,
00786                                        cmethod, cpos_rej, cneg_rej, citer,
00787                                        cmax, cmin,
00788                                        &combined_data_off,
00789                                        &combined_noise_off,
00790                                        -1.0);
00791 
00792                 if (kmclipm_omit_warning_one_slice > 10) {
00793                     cpl_msg_warning(cpl_func, "Previous warning (number of "
00794                                               "identified slices) occured %d times.",
00795                                     kmclipm_omit_warning_one_slice);
00796                     kmclipm_omit_warning_one_slice = FALSE;
00797                 }
00798 
00799                 // subtract combined lamp_off from lamp_on
00800                 // (for noise: sig_x = sqrt(sig_u^2 + sig_v^2)
00801                 cpl_image_subtract(combined_data_on, combined_data_off);
00802 
00803                 cpl_image_power(combined_noise_on, 2.0);
00804                 cpl_image_power(combined_noise_off, 2.0);
00805                 cpl_image_add(combined_noise_on, combined_noise_off);
00806                 cpl_image_power(combined_noise_on, 0.5);
00807 
00808                 // create bad-pixel-mask
00809                 bad_pix_mask_flat = kmo_create_bad_pix_flat_thresh(
00810                         combined_data_on, surrounding_pixels, badpix_thresh);
00811 
00812                 // calculate spectral curvature here
00813                 spec_found[sx] = kmo_calc_curvature(combined_data_on,
00814                         combined_noise_on, unused_ifus_after[i-1], bad_pix_mask_flat,
00815                         i, &xcal, &ycal, stored_gapmean+(sx), stored_gapsdv+(sx),
00816                         stored_gapmaxdev+(sx), stored_slitmean+(sx), stored_slitsdv+(sx),
00817                         stored_slitmaxdev+(sx), &edge_table[sx]);
00818 
00819                 if (spec_found[sx] == CPL_ERROR_NONE) {
00820                     // in kmo_calc_curvature() the spectral slope of each slitlet
00821                     // has been normalised individually. Now the normalisation on
00822                     // the whole frame is applied. (cpl_image_get_mean()
00823                     // ignores bad pixels when calculating the mean)
00824                     mean_data = cpl_image_get_mean(combined_data_on);
00825 
00826                     stored_qc_flat_eff[sx] = mean_data / exptime;
00827 
00828                     mean_noise = cpl_image_get_mean(combined_noise_on);
00829 
00830                     if ((cpl_frameset_count_tags(frameset, FLAT_OFF) > 1) ||
00831                         (cpl_frameset_count_tags(frameset, FLAT_ON) > 1)) {
00832                         KMO_TRY_ASSURE(mean_noise != 0.0,
00833                                        CPL_ERROR_ILLEGAL_INPUT,
00834                                        "All frames of detector %i are exactly "
00835                                        "the same!", i);
00836                         stored_qc_flat_sn[sx] = mean_data / mean_noise;
00837                     }
00838 
00839                     // normalize data & noise on the whole detector frame (the
00840                     // spectral slope on each slitlet has already been normalised in
00841                     // kmo_calc_curvature())
00842                     cpl_image_divide_scalar(combined_data_on, mean_data);
00843                     cpl_image_divide_scalar(combined_noise_on, mean_data);
00844 
00845                     // apply the badpixel mask to the produced frames
00846                     cpl_image_multiply(combined_data_on, bad_pix_mask_flat);
00847                     cpl_image_multiply(combined_noise_on, bad_pix_mask_flat);
00848                     cpl_image_multiply(xcal, bad_pix_mask_flat);
00849                     cpl_image_multiply(ycal, bad_pix_mask_flat);
00850 
00851                      /* ------ store temporarily flat, badpixel and calibration ----- */
00852                     stored_flat[sx] = combined_data_on;
00853                     stored_noise[sx] = combined_noise_on;
00854                     stored_badpix[sx] = bad_pix_mask_flat;
00855                     stored_xcal[sx] = xcal;
00856                     stored_ycal[sx] = ycal;
00857                 } else if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
00858                     // all IFUs seem to be deativated, continue processing
00859                     // just save empty frames
00860                     cpl_error_reset();
00861                     cpl_image_delete(combined_data_on); combined_data_on = NULL;
00862                     cpl_image_delete(combined_noise_on); combined_noise_on = NULL;
00863                     cpl_image_delete(bad_pix_mask_flat); bad_pix_mask_flat = NULL;
00864                 } else {
00865                     // another error occured
00866                     KMO_TRY_CHECK_ERROR_STATE();
00867                 }
00868                 // free memory
00869                 cpl_imagelist_delete(det_lamp_on); det_lamp_on = NULL;
00870                 cpl_imagelist_delete(det_lamp_off); det_lamp_off = NULL;
00871                 cpl_image_delete(combined_data_off); combined_data_off = NULL;
00872                 cpl_image_delete(combined_noise_off); combined_noise_off = NULL;
00873                 cpl_image_delete(bad_pix_mask_dark); bad_pix_mask_dark = NULL;
00874             } // for i = 1; i <= nr_devices
00875         } // for a = 0; a < nr_angles
00876 
00877     
00878         // #############################################################################
00879         // ###           QC parameters & saving
00880         // #############################################################################
00881         /* ------------ load, update & save primary header ------------ */
00882         main_header = kmo_dfs_load_primary_header(frameset, FLAT_ON);
00883 
00884         // update which IFUs are not used
00885         kmo_print_unused_ifus(unused_ifus_after, TRUE);
00886 
00887         kmo_set_unused_ifus(unused_ifus_after, main_header, "kmo_flat");
00888 
00889         // write main_header for data-, noise-, ycal- and badpix-frame
00890         // xcal gets additionally the boundaries of the IFUs for reconstruction
00891 
00892         // add here boundaries for reconstruction
00893         main_header_xcal = cpl_propertylist_new();
00894 
00895         total_bounds = (int**)cpl_malloc(nr_devices*sizeof(int*));
00896         for (i = 0; i<nr_devices; i++) {
00897             total_bounds[i] = (int*) cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int));
00898             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00899                 total_bounds[i][2*j] = 2048;
00900                 total_bounds[i][2*j+1] = 0;
00901             }
00902         }
00903 
00904         all_bounds = (int***) cpl_malloc(nr_angles*sizeof(int**));
00905         for (a = 0; a<nr_angles; a++) {
00906             all_bounds[a] = (int**) cpl_malloc(nr_devices*sizeof(int*));
00907             for (i = 0; i<nr_devices; i++) {
00908                 all_bounds[a][i] = (int*) cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int));
00909             }
00910         }
00911 
00912         for (a = 0; a<nr_angles; a++) {
00913             for (i = 0; i < nr_devices; i++) {
00914                 sx = a * nr_devices + i;
00915                 bounds = kmo_split_frame(stored_ycal[sx]);
00916                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00917                     all_bounds[a][i][2*j] = bounds[2*j];
00918                     all_bounds[a][i][2*j+1] = bounds[2*j+1];
00919 
00920                     if (total_bounds[i][2*j] >= 0 ) {
00921                         if (bounds[2*j] < 0) {
00922                             total_bounds[i][2*j] = bounds[2*j];
00923                         } else {
00924                             if (total_bounds[i][2*j] > bounds[2*j]) {
00925                                 total_bounds[i][2*j] = bounds[2*j];
00926                             }
00927                         }
00928                     }
00929                     if (total_bounds[i][2*j+1] >= 0 ) {
00930                         if (bounds[2*j+1] < 0) {
00931                             total_bounds[i][2*j+1] = bounds[2*j+1];
00932                         } else {
00933                             if (total_bounds[i][2*j+1] < bounds[2*j+1]) {
00934                                 total_bounds[i][2*j+1] = bounds[2*j+1];
00935                             }
00936                         }
00937                     }
00938                 }
00939                 if (bounds != NULL) {
00940                     cpl_free(bounds); bounds = NULL;
00941                 }
00942 
00943             }
00944         }
00945         for (i = 0; i < nr_devices; i++) {
00946             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00947                 if (total_bounds[i][2*j] > -1) {
00948                     tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
00949                             i*KMOS_IFUS_PER_DETECTOR + j+1, "_L");
00950                     kmclipm_update_property_int(main_header_xcal, tmpstr, 
00951                             total_bounds[i][2*j], "[pix] left boundary for reconstr.");
00952                     cpl_free(tmpstr); tmpstr = NULL;
00953                 }
00954 
00955                 if (total_bounds[i][2*j+1] > -1) {
00956                     tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX,
00957                             i*KMOS_IFUS_PER_DETECTOR + j+1, "_R");
00958                     kmclipm_update_property_int(main_header_xcal, tmpstr, 
00959                             total_bounds[i][2*j+1], "[pix] right boundary for reconstr.");
00960                     cpl_free(tmpstr); tmpstr = NULL;
00961                 }
00962             }
00963         } // for (nr_devices)
00964 
00965         // ------------ saving headers ------------
00966         cpl_msg_info("","Saving data...");
00967 
00968         frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00969 
00970         kmo_dfs_save_main_header(frameset, FLAT_NAME, suffix, frame, main_header, parlist, cpl_func);
00971         kmo_dfs_save_main_header(frameset, XCAL_NAME, suffix, frame, main_header_xcal, parlist, cpl_func);
00972         kmo_dfs_save_main_header(frameset, YCAL, suffix, frame, main_header, parlist, cpl_func);
00973         kmo_dfs_save_main_header(frameset, BAD_NAME, suffix, frame, main_header, parlist, cpl_func);
00974         kmo_dfs_save_main_header(frameset, FLAT_EDGE_NAME, suffix, frame, main_header, parlist, cpl_func);
00975 
00976         cpl_propertylist_delete(main_header); main_header = NULL;
00977         cpl_propertylist_delete(main_header_xcal); main_header_xcal = NULL;
00978 
00979         // ------------ saving sub frames ------------
00980         for (a = 0; a<nr_angles; a++) {
00981             for (i = 1; i <= nr_devices; i++) {
00982                 sx = a * nr_devices + (i - 1);
00983 
00984                 sub_header = kmo_dfs_load_sub_header(frameset, FLAT_ON, i, FALSE);
00985                 cpl_propertylist_erase(sub_header, CRPIX1);
00986                 cpl_propertylist_erase(sub_header, CRPIX2);
00987 
00988                 kmclipm_update_property_double(sub_header,CAL_ROTANGLE, ((double) rotang_found[a]),
00989                         "[deg] Rotator relative to nasmyth");
00990                 if (i == 1) {
00991                     for (ii = 0; ii < nr_devices; ii++) {
00992                         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00993                             if (all_bounds[a][ii][2*j] > -1) {
00994                                 tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
00995                                         ii*KMOS_IFUS_PER_DETECTOR + j+1, "_L");
00996                                 kmclipm_update_property_int(sub_header, tmpstr, 
00997                                         all_bounds[a][ii][2*j], "[pix] left boundary for reconstr.");
00998                                 cpl_free(tmpstr); tmpstr = NULL;
00999                             }
01000                             if (all_bounds[a][ii][2*j+1] > -1) {
01001                                 tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
01002                                         ii-1*KMOS_IFUS_PER_DETECTOR + j+1, "_R");
01003                                 kmclipm_update_property_int(sub_header, tmpstr, 
01004                                         all_bounds[a][ii][2*j+1], "[pix] right boundary for reconstr.");
01005                                 cpl_free(tmpstr); tmpstr = NULL;
01006                             }
01007                         }
01008                     } // for (nr_devices)
01009                 }
01010 
01011                 if (spec_found[sx] == CPL_ERROR_NONE) {
01012                     kmclipm_update_property_int(sub_header, QC_FLAT_SAT, 
01013                             stored_qc_flat_sat[sx], "[] nr. saturated pixels of master flat");
01014                     // load gain
01015                     gain = kmo_dfs_get_property_double(sub_header, GAIN);
01016                     KMO_TRY_CHECK_ERROR_STATE_MSG("GAIN-keyword in fits-header is missing!");
01017 
01018                     kmclipm_update_property_double(sub_header, QC_FLAT_EFF, 
01019                             stored_qc_flat_eff[sx]/gain, "[e-/s] rel. brightness of flat lamp");
01020                     kmclipm_update_property_double(sub_header, QC_FLAT_SN, stored_qc_flat_sn[sx], 
01021                             "[] S/N of master flat");
01022                 }
01023 
01024                 // store qc parameters only if any slitlet- and gap-width has been
01025                 // detected (should be the case when at least one IFU is active)
01026                 if (stored_xcal[sx] != NULL) {
01027                     kmclipm_update_property_double(sub_header, QC_GAP_MEAN, 
01028                             stored_gapmean[sx], "[pix] mean gap width between slitlets");
01029                     kmclipm_update_property_double(sub_header, QC_GAP_SDV, 
01030                             stored_gapsdv[sx], "[pix] stdev of gap width between slitlets");
01031                     kmclipm_update_property_double(sub_header, QC_GAP_MAXDEV, 
01032                             stored_gapmaxdev[sx], "[pix] max gap deviation between slitlets");
01033                     kmclipm_update_property_double(sub_header, QC_SLIT_MEAN, 
01034                             stored_slitmean[sx], "[pix] mean slitlet width");
01035                     kmclipm_update_property_double(sub_header, QC_SLIT_SDV, 
01036                             stored_slitsdv[sx], "[pix] stdev of slitlet widths");
01037                     kmclipm_update_property_double(sub_header, QC_SLIT_MAXDEV, 
01038                             stored_slitmaxdev[sx], "[pix] max slitlet width deviation");
01039                 }
01040 
01041                 if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
01042                     stored_flat[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01043                     stored_noise[sx]  = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01044                     stored_badpix[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01045                     stored_xcal[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01046                     stored_ycal[sx]   = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01047                 }
01048 
01049                 // calculate QC.BADPIX.NCOUNT
01050                 nr_bad_pix = cpl_image_count_rejected(stored_badpix[sx]);
01051 
01052                 // remove 4pixel-border as bad pixels
01053                 nr_bad_pix -= 2*KMOS_BADPIX_BORDER*(nx-2*KMOS_BADPIX_BORDER) +
01054                                   2*KMOS_BADPIX_BORDER*ny;
01055 
01056                 kmclipm_update_property_int(sub_header, QC_NR_BAD_PIX, nr_bad_pix, 
01057                         "[] nr. of bad pixels");
01058 
01059                 // save flat frame
01060                 extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
01061                 kmclipm_update_property_string(sub_header, EXTNAME, extname, "FITS extension name");
01062                 cpl_free(extname); extname = NULL;
01063 
01064                 kmo_dfs_save_image(stored_flat[sx], FLAT_NAME, suffix, sub_header, 0./0.);
01065 
01066                 // save noise frame only when enough input frames were available
01067                 extname = kmo_extname_creator(detector_frame, i, EXT_NOISE);
01068                 kmclipm_update_property_string(sub_header, EXTNAME, extname, "FITS extension name");
01069                 cpl_free(extname); extname = NULL;
01070 
01071                 kmo_dfs_save_image(stored_noise[sx], FLAT_NAME, suffix, sub_header, 0./0.);
01072 
01073                 // save bad_pix frame
01074                 extname = kmo_extname_creator(detector_frame, i, EXT_BADPIX);
01075                 kmclipm_update_property_string(sub_header, EXTNAME, extname, "FITS extension name");
01076                 cpl_free(extname); extname = NULL;
01077 
01078                 kmo_dfs_save_image(stored_badpix[sx], BAD_NAME, suffix, sub_header, 0.);
01079 
01080                 // save xcal and ycal-frame
01081                 extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
01082                 kmclipm_update_property_string(sub_header, EXTNAME, extname, "FITS extension name");
01083                 cpl_free(extname); extname = NULL;
01084 
01085                 kmo_dfs_save_image(stored_xcal[sx], XCAL_NAME, suffix, sub_header, 0./0.);
01086                 kmo_dfs_save_image(stored_ycal[sx], YCAL_NAME, suffix, sub_header, 0./0.);
01087 
01088                 // save edge_pars-frame
01089                 extname = kmo_extname_creator(list_frame, i, EXT_DATA);
01090                 kmclipm_update_property_string(sub_header, EXTNAME, extname, "FITS extension name");
01091                 cpl_free(extname); extname = NULL;
01092 
01093                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01094                     kmclipm_update_property_int(sub_header, CAL_IFU_NR, 
01095                             j+1+(i-1)*KMOS_IFUS_PER_DETECTOR, "IFU Number {1..24}");
01096 
01097                     // save edge-parameters as product
01098                     kmo_dfs_save_table(edge_table[sx][j], FLAT_EDGE_NAME, suffix, sub_header);
01099                 }
01100                 cpl_propertylist_delete(sub_header); sub_header = NULL;
01101             }
01102         }
01103     }
01104     KMO_CATCH
01105     {
01106         KMO_CATCH_MSG();
01107         ret_val = -1;
01108     }
01109 
01110     kmo_free_fits_desc(&desc1);
01111     kmo_free_fits_desc(&desc2);
01112     kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
01113     kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
01114     cpl_propertylist_delete(main_header); main_header = NULL;
01115     cpl_propertylist_delete(main_header_xcal); main_header_xcal = NULL;
01116     cpl_propertylist_delete(sub_header); sub_header = NULL;
01117     cpl_imagelist_delete(det_lamp_on); det_lamp_on = NULL;
01118     cpl_imagelist_delete(det_lamp_off); det_lamp_off = NULL;
01119     cpl_image_delete(combined_data_off); combined_data_off = NULL;
01120     cpl_image_delete(combined_noise_off); combined_noise_off = NULL;
01121     cpl_image_delete(bad_pix_mask_dark); bad_pix_mask_dark = NULL;
01122     cpl_free(stored_qc_flat_sat); stored_qc_flat_sat = NULL;
01123     cpl_free(stored_qc_flat_eff); stored_qc_flat_eff = NULL;
01124     cpl_free(stored_qc_flat_sn); stored_qc_flat_sn = NULL;
01125     cpl_free(stored_gapmean); stored_gapmean = NULL;
01126     cpl_free(stored_gapsdv); stored_gapsdv = NULL;
01127     cpl_free(stored_gapmaxdev); stored_gapmaxdev = NULL;
01128     cpl_free(stored_slitmean); stored_slitmean = NULL;
01129     cpl_free(stored_slitsdv); stored_slitsdv = NULL;
01130     cpl_free(stored_slitmaxdev); stored_slitmaxdev = NULL;
01131     cpl_free(readmode); readmode = NULL;
01132     cpl_free(suffix); suffix = NULL;
01133 
01134     for (int i = 0; i < nr_devices; i++) {
01135         cpl_free(total_bounds[i]); total_bounds[i] = NULL;
01136     }
01137     cpl_free(total_bounds); total_bounds = NULL;
01138 
01139     for (int i = 0; i < nr_devices * nr_angles; i++) {
01140         cpl_image_delete(stored_flat[i]); stored_flat[i] = NULL;
01141         cpl_image_delete(stored_noise[i]); stored_noise[i] = NULL;
01142         cpl_image_delete(stored_badpix[i]); stored_badpix[i] = NULL;
01143         cpl_image_delete(stored_xcal[i]); stored_xcal[i] = NULL;
01144         cpl_image_delete(stored_ycal[i]); stored_ycal[i] = NULL;
01145     }
01146     cpl_free(stored_flat); stored_flat = NULL;
01147     cpl_free(stored_noise); stored_noise = NULL;
01148     cpl_free(stored_badpix); stored_badpix = NULL;
01149     cpl_free(stored_xcal); stored_xcal = NULL;
01150     cpl_free(stored_ycal); stored_ycal = NULL;
01151     for (int i = 0; i < nr_angles; i++) {
01152         cpl_frameset_delete(angle_frameset[i]); angle_frameset[i] = NULL;
01153     }
01154     cpl_free(angle_frameset); angle_frameset = NULL;
01155     if (edge_table != NULL) {
01156         for (int i = 0; i < KMOS_NR_DETECTORS * nr_angles; i++) {
01157             if (edge_table[i] != NULL) {
01158                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01159                     cpl_table_delete(edge_table[i][j]);
01160                     edge_table[i][j] = NULL;
01161                 }
01162                 cpl_free(edge_table[i]); edge_table[i] = NULL;
01163             }
01164         }
01165         cpl_free(edge_table); edge_table = NULL;
01166     }
01167     if (bounds != NULL) {
01168         cpl_free(bounds); bounds = NULL;
01169     }
01170     if (spec_found != NULL) {
01171         cpl_free(spec_found); spec_found = NULL;
01172     }
01173 
01174     return ret_val;
01175 }
01176