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