KMOS Pipeline Reference Manual  1.3.10
kmos_flat.c
00001 /* 
00002  * This file is part of the KMOS Pipeline
00003  * Copyright (C) 2002,2003 European Southern Observatory
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #ifdef HAVE_CONFIG_H
00021 #include <config.h>
00022 #endif
00023 
00024 /*-----------------------------------------------------------------------------
00025  *                              Includes
00026  *----------------------------------------------------------------------------*/
00027 
00028 #include <string.h>
00029 #include <math.h>
00030 
00031 #include <cpl.h>
00032 
00033 #include "kmo_utils.h"
00034 #include "kmo_priv_flat.h"
00035 #include "kmo_priv_functions.h"
00036 #include "kmo_dfs.h"
00037 #include "kmo_error.h"
00038 #include "kmo_constants.h"
00039 #include "kmo_cpl_extensions.h"
00040 #include "kmo_debug.h"
00041 
00042 /*-----------------------------------------------------------------------------
00043  *                          Functions prototypes
00044  *----------------------------------------------------------------------------*/
00045 
00046 static int kmos_flat_check_inputs(cpl_frameset *, int *, int *, int *,double *);
00047 static cpl_propertylist * kmos_create_bounds_properties(cpl_image **,int, int) ;
00048 
00049 static int kmos_flat_create(cpl_plugin *);
00050 static int kmos_flat_exec(cpl_plugin *);
00051 static int kmos_flat_destroy(cpl_plugin *);
00052 static int kmos_flat(cpl_parameterlist *, cpl_frameset *);
00053 
00054 /*-----------------------------------------------------------------------------
00055  *                          Static variables
00056  *----------------------------------------------------------------------------*/
00057 
00058 static char kmos_flat_description[] =
00059 "This recipe creates the master flat field and calibration frames needed for\n"
00060 "spatial calibration for all three detectors. It must be called after the \n"
00061 "kmo_dark-recipe, which generates a bad pixel mask (badpixel_dark.fits). The\n"
00062 "bad pixel mask will be updated in this recipe.\n"
00063 "As input at least 3 dark frames, 3 frames with the flat lamp on are\n"
00064 "recommended. Additionally a badpixel mask from kmo_dark is required.\n"
00065 "\n"
00066 "The badpixel mask contains 0 for bad pixels and 1 for good ones.\n"
00067 "\n"
00068 "The structure of the resulting xcal and ycal frames is quite complex since\n"
00069 "the arrangement of the IFUs isn't just linear on the detector. Basically the\n"
00070 "integer part of the calibration data shows the offset of each pixels centre\n"
00071 "in mas (Milli arcsec) from the field centre. The viewing of an IFU is\n"
00072 "2800 mas (14pix*0.2arcsec/pix). So the values in these two frames will vary\n"
00073 "between +/-1500 (One would expect 1400, but since the slitlets aren't\n"
00074 "expected to be exactly vertical, the values can even go up to around 1500).\n"
00075 "Additionally in the calibration data in y-direction the decimal part of the\n"
00076 "data designates the IFU to which the slitlet corresponds to (for each\n"
00077 "detector from 1 to 8).\n"
00078 "Because of the irregular arrangement of the IFUs not all x-direction\n"
00079 "calibration data is found in xcal and similarly not all y-direction\n"
00080 "calibration data is located in ycal. For certain IFUs they are switched\n"
00081 " and/or flipped in x- or y-direction:\n"
00082 "For IFUs 1,2,3,4,13,14,15,16:  x- and y- data is switched\n"
00083 "For IFUs 17,18,19,20:          y-data is flipped \n"
00084 "For IFUs 21,22,23,24:          x-data is flipped \n"
00085 "For IFUs 5,6,7,8,9,10,11,12:   x- and y- data is switched and\n"
00086 "                               x- and y- data is flipped\n"
00087 "\n"
00088 "Furthermore frames can be provided for several rotator angles. In this case\n"
00089 "the resulting calibration frames for each detector are repeatedly saved as \n"
00090 "extension for every angle.\n"
00091 "\n"
00092 "Advanced features:\n"
00093 "------------------\n"
00094 "To create the badpixel mask the edges of all slitlets are fitted to a\n"
00095 "polynomial. Since it can happen that some of these fits (3 detectors\n"
00096 "8 IFUs * 14slitlets * 2 edges  (left and right edge of slitlet)= 672 edges)\n"
00097 "fail, the fit parameters are themselves fitted again to detect any outliers.\n"
00098 "By default, the parameters of all left and all right edges are grouped\n"
00099 "individually and then fitted using chebyshev polynomials. The advantage of\n"
00100 "a chebyshev polynomial is, that it consists in fact of a series of\n"
00101 "orthogonal polynomials. This implies that the parameters of the polynomials\n"
00102 "are independent. This fact predestines the use of chebyshev polynomials\n"
00103 "for our case. So each individual parameter can be examined independently.\n"
00104 "The reason why the left and right edges are fitted individually is that\n"
00105 "there is a systematic pattern specific to these groups. The reason for\n"
00106 "this pattern is probably to be found in the optical path the light is\n"
00107 "traversing.\n"
00108 "\n"
00109 "The behaviour of this fitting step can be influenced via environment\n"
00110 "parameters:\n"
00111 "* KF_ALLPARS (default: 1)\n"
00112 "  When set to 1 all coefficients of the polynomial of an edge are to be\n"
00113 "  corrected, also when just one of these coefficients is an outlier. When\n"
00114 "  set to 0 only the outlier is to be corrected.\n"
00115 "* KF_CH (default: 1)\n"
00116 "  When set to 1 chebyshev polynomials are used to fit the fitted parameters.\n"
00117 "  When set to 0 normal polynomials are used.\n"
00118 "* KF_SIDES (default: 2)\n"
00119 "  This variable can either be set to 1 or 2. When set to 2 the left and\n"
00120 "  right edges are examined individually. When set to 1 all edges are\n"
00121 "  examined as one group.\n"
00122 "* KF_FACTOR(default: 4)\n"
00123 "  This factor defines the threshold factor. All parameters deviating \n"
00124 "  KF_FACTOR*stddev are to be corrected\n"
00125 "\n"
00126 "BASIC PARAMETERS:\n"
00127 "-----------------\n"
00128 "--badpix_thresh\n"
00129 "The threshold level to mark pixels as bad on the dark subtracted frames [%]"
00130 "\n"
00131 "--surrounding_pixels\n"
00132 "The amount of bad pixels to surround a specific pixel, to let it be marked\n"
00133 "bad as well.\n"
00134 "\n"
00135 "--cmethod\n"
00136 "Following methods of frame combination are available:\n"
00137 "   * 'ksigma' (Default)\n"
00138 "   An iterative sigma clipping. For each position all pixels in the\n"
00139 "   spectrum are examined. If they deviate significantly, they will be\n"
00140 "   rejected according to the conditions:\n"
00141 "       val > mean + stdev * cpos_rej\n"
00142 "   and\n"
00143 "       val < mean - stdev * cneg_rej\n"
00144 "   where --cpos_rej, --cneg_rej and --citer are the configuration\n"
00145 "   parameters. In the first iteration median and percentile level are used.\n"
00146 "\n"
00147 "   * 'median'\n"
00148 "   At each pixel position the median is calculated.\n"
00149 "\n"
00150 "   * 'average'\n"
00151 "   At each pixel position the average is calculated.\n"
00152 "\n"
00153 "   * 'sum'\n"
00154 "   At each pixel position the sum is calculated.\n"
00155 "\n"
00156 "   * 'min_max'\n"
00157 "   The specified number of min and max pixel values will be rejected.\n"
00158 "   --cmax and --cmin apply to this method.\n"
00159 "\n"
00160 "ADVANCED PARAMETERS\n"
00161 "-------------------\n"
00162 "--cpos_rej\n"
00163 "--cneg_rej\n"
00164 "--citer\n"
00165 "see --cmethod='ksigma'\n"
00166 "\n"
00167 "--cmax\n"
00168 "--cmin\n"
00169 "see --cmethod='min_max'\n"
00170 "\n"
00171 "--suppress_extension\n"
00172 "If set to TRUE, the arbitrary filename extensions are supressed. If\n"
00173 "multiple products with the same category are produced, they will be\n"
00174 "numered consecutively starting from 0.\n"
00175 "\n"
00176 "-------------------------------------------------------------------------------\n"
00177 "  Input files:\n"
00178 "   DO CATG           Type   Explanation                    Required #Frames\n"
00179 "   -------           -----  -----------                    -------- -------\n"
00180 "   FLAT_ON           RAW    Flatlamp-on exposures             Y       1-n  \n"
00181 "                            (at least 3 frames recommended)                \n"
00182 "   FLAT_OFF          RAW    Flatlamp-off exposures            Y       1-n  \n"
00183 "                            (at least 3 frames recommended)                \n"
00184 "   BADPIXEL_DARK     B2D    Bad pixel mask                    Y        1   \n"
00185 "\n"
00186 "  Output files:\n"
00187 "   DO CATG           Type   Explanation\n"
00188 "   -------           -----  -----------\n"
00189 "   MASTER_FLAT       F2D    Normalised flat field\n"
00190 "                            (6 extensions: alternating data & noise\n"
00191 "   BADPIXEL_FLAT     B2D    Updated bad pixel mask (3 Extensions)\n"
00192 "   XCAL              F2D    Calibration frame 1 (3 Extensions)\n"
00193 "   YCAL              F2D    Calibration frame 2 (3 Extensions)\n"
00194 "   FLAT_EDGE         F2L    Frame containing parameters of fitted \n"
00195 "                            slitlets of all IFUs of all detectors\n"
00196 "---------------------------------------------------------------------------"
00197 "\n";
00198 
00199 /*----------------------------------------------------------------------------*/
00203 /*----------------------------------------------------------------------------*/
00204 
00207 /*-----------------------------------------------------------------------------
00208  *                              Functions code
00209  *----------------------------------------------------------------------------*/
00210 
00211 /*----------------------------------------------------------------------------*/
00220 /*----------------------------------------------------------------------------*/
00221 int cpl_plugin_get_info(cpl_pluginlist *list)
00222 {
00223     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00224     cpl_plugin *plugin = &recipe->interface;
00225 
00226     cpl_plugin_init(plugin,
00227             CPL_PLUGIN_API,
00228             KMOS_BINARY_VERSION,
00229             CPL_PLUGIN_TYPE_RECIPE,
00230             "kmos_flat",
00231             "Create master flatfield frame and badpixel map",
00232             kmos_flat_description,
00233             "Alex Agudo Berbel, Yves Jung",
00234             "usd-help@eso.org",
00235             kmos_get_license(),
00236             kmos_flat_create,
00237             kmos_flat_exec,
00238             kmos_flat_destroy);
00239 
00240     cpl_pluginlist_append(list, plugin);
00241 
00242     return 0;
00243 }
00244 
00245 /*----------------------------------------------------------------------------*/
00253 /*----------------------------------------------------------------------------*/
00254 static int kmos_flat_create(cpl_plugin *plugin)
00255 {
00256     cpl_recipe *recipe;
00257     cpl_parameter *p;
00258 
00259     /* Check that the plugin is part of a valid recipe */
00260     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00261         recipe = (cpl_recipe *)plugin;
00262     else
00263         return -1;
00264 
00265     /* Create the parameters list in the cpl_recipe object */
00266     recipe->parameters = cpl_parameterlist_new();
00267 
00268     /* Fill the parameters list */
00269 
00270     /* --badpix_thresh */
00271     p = cpl_parameter_new_value("kmos.kmos_flat.badpix_thresh", CPL_TYPE_INT,
00272             "The threshold level to mark bad pixels [%].","kmos.kmos_flat", 35);
00273     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "badpix_thresh");
00274     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00275     cpl_parameterlist_append(recipe->parameters, p);
00276 
00277     /* --surrounding_pixels */
00278     p = cpl_parameter_new_value("kmos.kmos_flat.surrounding_pixels",
00279             CPL_TYPE_INT, "The nb of bad surrounding pix to mark a pixel bad",
00280             "kmos.kmos_flat", 5);
00281     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "surrounding_pixels");
00282     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00283     cpl_parameterlist_append(recipe->parameters, p);
00284 
00285     /* --suppress_extension */
00286     p = cpl_parameter_new_value("kmos.kmos_flat.suppress_extension",
00287             CPL_TYPE_BOOL, "Suppress arbitrary filename extension",
00288             "kmos.kmos_flat", FALSE);
00289     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
00290     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00291     cpl_parameterlist_append(recipe->parameters, p);
00292 
00293     /* Add parameters for combination */
00294     kmos_combine_pars_create(recipe->parameters, "kmos.kmos_flat", 
00295             DEF_REJ_METHOD, FALSE);
00296 
00297     /* --detector */
00298     p = cpl_parameter_new_value("kmos.kmos_flat.detector",
00299             CPL_TYPE_INT, "Only reduce the specified detector",
00300             "kmos.kmos_flat", 0);
00301     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "det");
00302     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00303     cpl_parameterlist_append(recipe->parameters, p);
00304 
00305     /* --angle */
00306     p = cpl_parameter_new_value("kmos.kmos_flat.angle",
00307             CPL_TYPE_DOUBLE, "Only reduce the specified angle",
00308             "kmos.kmos_flat", 370.0);
00309     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "angle");
00310     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00311     cpl_parameterlist_append(recipe->parameters, p);
00312 
00313     return 0 ;
00314 }
00315 
00316 /*----------------------------------------------------------------------------*/
00322 /*----------------------------------------------------------------------------*/
00323 static int kmos_flat_exec(cpl_plugin *plugin)
00324 {
00325     cpl_recipe  *recipe;
00326 
00327     /* Get the recipe out of the plugin */
00328     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00329         recipe = (cpl_recipe *)plugin;
00330     else return -1;
00331 
00332     return kmos_flat(recipe->parameters, recipe->frames);
00333 }
00334 
00335 /*----------------------------------------------------------------------------*/
00341 /*----------------------------------------------------------------------------*/
00342 static int kmos_flat_destroy(cpl_plugin *plugin)
00343 {
00344     cpl_recipe *recipe;
00345 
00346     /* Get the recipe out of the plugin */
00347     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00348         recipe = (cpl_recipe *)plugin;
00349     else return -1 ;
00350 
00351     cpl_parameterlist_delete(recipe->parameters);
00352     return 0 ;
00353 }
00354 
00355 /*----------------------------------------------------------------------------*/
00369 /*----------------------------------------------------------------------------*/
00370 static int kmos_flat(cpl_parameterlist *parlist, cpl_frameset *frameset)
00371 {
00372     const cpl_parameter *   par ;
00373     int                     surrounding_pixels, badpix_thresh, 
00374                             suppress_extension, reduce_det ;
00375     double                  reduce_angle, cpos_rej, cneg_rej, ext_index ;
00376     int                     cmax, cmin, citer ;
00377     const char          *   cmethod ;
00378     cpl_frame           *   frame ;
00379     cpl_propertylist    *   plist ;
00380     int                     nx, ny, ne ;
00381     int                 *   angles_array ;
00382     int                     nb_angles ;
00383     cpl_image           **  stored_flat ;
00384     cpl_image           **  stored_noise ;
00385     cpl_image           **  stored_badpix ;
00386     cpl_image           **  stored_xcal ;
00387     cpl_image           **  stored_ycal ;
00388     double              *   stored_gapmean ;
00389     double              *   stored_gapsdv ;
00390     double              *   stored_gapmaxdev ;
00391     double              *   stored_slitmean ;
00392     double              *   stored_slitsdv ;
00393     double              *   stored_slitmaxdev ;
00394     double              *   stored_qc_flat_eff ;
00395     double              *   stored_qc_flat_sn ;
00396     int                 *   stored_qc_flat_sat ;
00397     cpl_frameset        *   angle_frameset ;
00398     char                *   extname ;
00399     char                *   suffix ;
00400     char                *   fn_suffix ;
00401     unsigned int            save_mode ;
00402     char                *   fn_flat = "flat_tmp.fits" ;
00403     char                *   fn_noise = "flat_noise.fits" ;
00404     char                *   fn_badpix = "badpix_tmp.fits" ;
00405     cpl_imagelist       *   det_lamp_on ;
00406     cpl_imagelist       *   det_lamp_off ;
00407     cpl_image           *   img_in ;
00408     cpl_image           *   combined_data_on ;
00409     cpl_image           *   combined_noise_on ;
00410     cpl_image           *   combined_data_off[KMOS_NR_DETECTORS] ;
00411     cpl_image           *   combined_noise_off[KMOS_NR_DETECTORS] ;
00412     cpl_image           *   bad_pix_mask_flat ;
00413     cpl_image           *   bad_pix_mask_dark[KMOS_NR_DETECTORS] ;
00414     cpl_image           *   xcal ;
00415     cpl_image           *   ycal ;
00416     cpl_array           **  unused_ifus_before ;
00417     cpl_array           **  unused_ifus_after ;
00418     cpl_propertylist    *   main_header ;
00419     cpl_propertylist    *   main_header_xcal ;
00420     cpl_propertylist    *   sub_header ;
00421     cpl_table           *** edge_table ;
00422     cpl_error_code      *   spec_found ;
00423     double                  gain, exptime, mean_data, mean_noise ;
00424     int                     sx, nr_bad_pix, nr_sat, i, j, a ;
00425 
00426     /* Check entries */
00427     if (parlist == NULL || frameset == NULL) {
00428         cpl_msg_error(__func__, "Null Inputs") ;
00429         cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
00430         return -1 ;
00431     }
00432 
00433     /* Get Parameters */
00434     par = cpl_parameterlist_find_const(parlist,
00435             "kmos.kmos_flat.surrounding_pixels");
00436     surrounding_pixels = cpl_parameter_get_int(par);
00437     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_flat.badpix_thresh");
00438     badpix_thresh = cpl_parameter_get_int(par);
00439     par = cpl_parameterlist_find_const(parlist,
00440             "kmos.kmos_flat.suppress_extension");
00441     suppress_extension = cpl_parameter_get_bool(par);
00442     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_flat.angle");
00443     reduce_angle = cpl_parameter_get_double(par);
00444     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_flat.detector");
00445     reduce_det = cpl_parameter_get_int(par);
00446  
00447     kmos_combine_pars_load(parlist, "kmos.kmos_flat", &cmethod, &cpos_rej,
00448        &cneg_rej, &citer, &cmin, &cmax, FALSE);
00449 
00450     /* Check Parameters */
00451     if (surrounding_pixels < 0 || surrounding_pixels > 8) {
00452         cpl_msg_error(__func__, "surrounding_pixels must be in [0,8]") ;
00453         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00454         return -1 ;
00455     }
00456     if (badpix_thresh < 0 || badpix_thresh > 100) {
00457         cpl_msg_error(__func__, "badpix_thresh must be in [0,100]") ;
00458         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00459         return -1 ;
00460     }
00461     if (reduce_det < 0 || reduce_det > 3) {
00462         cpl_msg_error(__func__, "detector must be in [1,3]") ;
00463         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00464         return -1 ;
00465     }
00466 
00467     /* Identify the RAW and CALIB frames in the input frameset */
00468     if (kmo_dfs_set_groups(frameset, "kmos_flat") != 1) {
00469         cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
00470         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00471         return -1 ;
00472     }
00473 
00474     /* Check the inputs consistency */
00475     if (kmos_flat_check_inputs(frameset, &nx, &ny, &ne, &exptime) != 1) {
00476         cpl_msg_error(__func__, "Input frameset is not consistent") ;
00477         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00478         return -1 ;
00479     }
00480     
00481     /* Instrument setup */
00482     suffix = kmo_dfs_get_suffix(kmo_dfs_get_frame(frameset,FLAT_ON),TRUE,FALSE);
00483     cpl_msg_info(__func__, "Detected instrument setup:   %s", suffix+1);
00484 
00485     /* Get Rotator angles */
00486     if ((angles_array = kmos_get_angles(frameset, &nb_angles,FLAT_ON)) == NULL){
00487         cpl_msg_error(__func__, "Cannot get Angles informations") ;
00488         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00489         return -1 ;
00490     }
00491  
00492     /* The frames have to be stored temporarily because the QC parameters */
00493     /* for the main header are calculated from each detector.  */
00494     /* So they can be stored only when all detectors are processed */
00495     stored_flat = (cpl_image**)cpl_calloc(ne*nb_angles, sizeof(cpl_image*));
00496     stored_noise = (cpl_image**)cpl_calloc(ne * nb_angles, sizeof(cpl_image*));
00497     stored_badpix = (cpl_image**)cpl_calloc(ne * nb_angles, sizeof(cpl_image*));
00498     stored_xcal = (cpl_image**)cpl_calloc(ne * nb_angles, sizeof(cpl_image*));
00499     stored_ycal = (cpl_image**)cpl_calloc(ne * nb_angles, sizeof(cpl_image*));
00500     stored_qc_flat_sat = (int*)cpl_malloc(ne * nb_angles * sizeof(int));
00501     stored_qc_flat_eff = (double*)cpl_malloc(ne * nb_angles * sizeof(double));
00502     stored_qc_flat_sn = (double*)cpl_malloc(ne * nb_angles * sizeof(double));
00503     stored_gapmean = (double*)cpl_malloc(ne * nb_angles * sizeof(double));
00504     stored_gapsdv = (double*)cpl_malloc(ne * nb_angles * sizeof(double));
00505     stored_gapmaxdev = (double*)cpl_malloc(ne * nb_angles * sizeof(double));
00506     stored_slitmean = (double*)cpl_malloc(ne * nb_angles * sizeof(double));
00507     stored_slitsdv = (double*)cpl_malloc(ne * nb_angles * sizeof(double));
00508     stored_slitmaxdev = (double*)cpl_malloc(ne * nb_angles * sizeof(double));
00509     spec_found = (cpl_error_code*)cpl_malloc(ne * nb_angles * 
00510             sizeof(cpl_error_code));
00511 
00512     /* Initialise */
00513     for (i = 0; i < ne * nb_angles ; i++) {
00514         stored_qc_flat_sat[i] = 0;
00515         stored_qc_flat_eff[i] = 0.0;
00516         stored_qc_flat_sn[i] = 0.0;
00517         stored_gapmean[i] = 0.0;
00518         stored_gapsdv[i] = 0.0;
00519         stored_gapmaxdev[i] = 0.0;
00520         stored_slitmean[i] = 0.0;
00521         stored_slitsdv[i] = 0.0;
00522         stored_slitmaxdev[i] = 0.0;
00523         spec_found[i] = CPL_ERROR_NONE;
00524     }
00525 
00526     /* TODO : Improve handling of edge_table !!!! */
00527     edge_table = (cpl_table***)cpl_malloc(ne * nb_angles * sizeof(cpl_table**));
00528     for (i = 0; i < ne * nb_angles; i++) edge_table[i] = NULL;
00529 
00530     /* Check which IFUs are active for all FLAT_ON frames */
00531     unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0);
00532     unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before);
00533     kmo_print_unused_ifus(unused_ifus_before, FALSE);
00534     kmo_free_unused_ifus(unused_ifus_before);
00535 
00536     /* Combine the FLAT_OFF frames for the 3 detectors */
00537     for (i = 1; i <= ne; i++) {
00538         /* Compute only one detector */
00539         if (reduce_det != 0 && i != reduce_det) continue ;
00540 
00541         /* Load the badpixel masks */
00542         bad_pix_mask_dark[i-1] = kmo_dfs_load_image(frameset, BADPIXEL_DARK, 
00543                 i, 2, FALSE, NULL) ;
00544 
00545         /* Load lamp-off images */
00546         det_lamp_off = cpl_imagelist_new();
00547         frame = kmo_dfs_get_frame(frameset, FLAT_OFF);
00548         j = 0;
00549         while (frame != NULL) {
00550             img_in = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, NULL);
00551             kmo_image_reject_from_mask(img_in, bad_pix_mask_dark[i-1]);
00552             cpl_imagelist_set(det_lamp_off, img_in, j++);
00553             frame = kmo_dfs_get_frame(frameset, NULL);
00554         }
00555 
00556         /* Combine FLAT_OFF frames */
00557         cpl_msg_info(__func__, "Combine FLAT_OFF frames for Detector %d", i) ;
00558         kmos_combine_frames(det_lamp_off, cmethod, cpos_rej,
00559                 cneg_rej, citer, cmax, cmin, &(combined_data_off[i-1]), 
00560                 &(combined_noise_off[i-1]), -1.0);
00561         /*
00562         cpl_image_save(combined_data_off[i-1], "off.fits",
00563                 CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE) ;
00564         */
00565         cpl_imagelist_delete(det_lamp_off);
00566         cpl_image_power(combined_noise_off[i-1], 2.0);
00567     }
00568 
00569     save_mode = CPL_IO_CREATE;
00570     /* Loop all Rotator Angles and Detectors  */
00571     for (a = 0; a < nb_angles; a++) {
00572         /* Reduce only one angle */
00573         if (reduce_angle <= 360 && angles_array[a] != reduce_angle) continue ;
00574 
00575         cpl_msg_info(__func__, "Processing rotator angle %d -> %d degree", 
00576                 a, angles_array[a]);
00577         cpl_msg_indent_more() ;
00578         
00579         /* Get the frameset with this angle */
00580         angle_frameset = kmos_get_angle_frameset(frameset, angles_array[a],
00581                 FLAT_ON);
00582         if (angle_frameset == NULL) {
00583             cpl_msg_error(__func__, "Cannot get angle frameset") ;
00584             cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00585             cpl_msg_indent_less() ;
00586             cpl_free(angles_array) ;
00587             return -1 ;
00588         }
00589 
00590         for (i = 1; i <= ne; i++) {
00591             /* Compute only one detector */
00592             if (reduce_det != 0 && i != reduce_det) continue ;
00593 
00594             cpl_msg_info(__func__, "Processing detector No. %d", i);
00595             cpl_msg_indent_more() ;
00596 
00597             sx = a * ne + (i - 1);
00598 
00599             /* Load lamp-on images for Angle a */
00600             det_lamp_on = cpl_imagelist_new();
00601             frame = kmo_dfs_get_frame(angle_frameset, FLAT_ON);
00602             j = 0;
00603             while (frame != NULL) {
00604                 img_in=kmo_dfs_load_image_frame(frame, i, FALSE, TRUE, &nr_sat);
00605                 kmo_image_reject_from_mask(img_in, bad_pix_mask_dark[i-1]);
00606                 cpl_imagelist_set(det_lamp_on, img_in, j++);
00607                 frame = kmo_dfs_get_frame(angle_frameset, NULL);
00608             }
00609 
00610             /* Count saturated pixels for each detector */
00611             cpl_msg_info(__func__, "Count Saturated pixels on the detector") ;
00612             frame = kmo_dfs_get_frame(angle_frameset, FLAT_ON);
00613             main_header = kmclipm_propertylist_load(
00614                     cpl_frame_get_filename(frame), 0);
00615             if (strcmp(cpl_propertylist_get_string(main_header, READMODE), 
00616                         "Nondest") == 0) {
00617                 // NDR: non-destructive readout mode
00618                 stored_qc_flat_sat[sx] = nr_sat;
00619             } else {
00620                 // normal readout mode
00621                 stored_qc_flat_sat[sx] = kmo_imagelist_get_saturated(
00622                         det_lamp_on, KMO_FLAT_SATURATED, KMO_FLAT_SAT_MIN);
00623             }
00624             cpl_propertylist_delete(main_header); 
00625 
00626             /* Combine imagelists and create noise */
00627             cpl_msg_info(__func__, "Combine FLAT_ON frames") ;
00628             kmos_combine_frames(det_lamp_on, cmethod, cpos_rej, 
00629                     cneg_rej, citer, cmax, cmin, &combined_data_on, 
00630                     &combined_noise_on, -1.0);
00631             cpl_imagelist_delete(det_lamp_on); 
00632 
00633             if (kmclipm_omit_warning_one_slice > 10) 
00634                 kmclipm_omit_warning_one_slice = FALSE;
00635 
00636             /* Subtract combined lamp_off from lamp_on */
00637             cpl_image_subtract(combined_data_on, combined_data_off[i-1]);
00638 
00639             /* noise: sig_x = sqrt(sig_u^2 + sig_v^2 */
00640             cpl_msg_info(__func__, "Compute the noise") ;
00641             cpl_image_power(combined_noise_on, 2.0);
00642             cpl_image_add(combined_noise_on, combined_noise_off[i-1]);
00643             cpl_image_power(combined_noise_on, 0.5);
00644 
00645             /* Create bad-pixel-mask */
00646             bad_pix_mask_flat = kmo_create_bad_pix_flat_thresh(combined_data_on,
00647                         surrounding_pixels, badpix_thresh);
00648 
00649             /* Calculate spectral curvature here */
00650             cpl_msg_info(__func__, "Compute the spectral curvature") ;
00651             cpl_msg_indent_more() ;
00652             spec_found[sx] = kmo_calc_curvature(combined_data_on,
00653                     combined_noise_on, unused_ifus_after[i-1],
00654                     bad_pix_mask_flat, i, &xcal, &ycal, stored_gapmean+(sx),
00655                     stored_gapsdv+(sx), stored_gapmaxdev+(sx),
00656                     stored_slitmean+(sx), stored_slitsdv+(sx),
00657                     stored_slitmaxdev+(sx), &edge_table[sx]);
00658             cpl_msg_indent_less() ;
00659 
00660             if (spec_found[sx] == CPL_ERROR_NONE) {
00661                 // in kmo_calc_curvature() the spectral slope of each 
00662                 // slitlet has been normalised individually. Now the 
00663                 // normalisation on the whole frame is applied. 
00664                 // (cpl_image_get_mean() ignores bad pixels when 
00665                 // calculating the mean)
00666                 mean_data = cpl_image_get_mean(combined_data_on);
00667                 stored_qc_flat_eff[sx] = mean_data / exptime;
00668                 mean_noise = cpl_image_get_mean(combined_noise_on);
00669                 if (fabs(mean_noise) < 1e-3) {
00670                     cpl_msg_error(__func__, "Division by 0.0") ;
00671                     cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00672                     cpl_free(angles_array) ;
00673                     return -1 ;
00674                 }
00675                 stored_qc_flat_sn[sx] = mean_data / mean_noise;
00676 
00677                 /* Normalize data & noise on the whole detector frame */
00678                 /* The spectral slope on each slitlet has already been  */
00679                 /* normalised in kmo_calc_curvature() */
00680                 cpl_image_divide_scalar(combined_data_on, mean_data);
00681                 cpl_image_divide_scalar(combined_noise_on, mean_data);
00682 
00683                 /* Apply the badpixel mask to the produced frames */
00684                 cpl_image_multiply(combined_data_on, bad_pix_mask_flat);
00685                 cpl_image_multiply(combined_noise_on, bad_pix_mask_flat);
00686                 cpl_image_multiply(xcal, bad_pix_mask_flat) ;
00687                 cpl_image_multiply(ycal, bad_pix_mask_flat) ;
00688 
00689                 /* Store temporarily flat, badpixel and calibration */
00690                 stored_xcal[sx] = xcal;
00691                 stored_ycal[sx] = ycal;
00692 
00693                 /* Save immediate results, free memory */
00694                 kmclipm_image_save(combined_data_on, fn_flat, CPL_TYPE_FLOAT, 
00695                         NULL, save_mode, 0./0.);
00696                 kmclipm_image_save(combined_noise_on, fn_noise, CPL_TYPE_FLOAT, 
00697                         NULL, save_mode, 0./0.);
00698                 kmclipm_image_save(bad_pix_mask_flat, fn_badpix, CPL_TYPE_FLOAT,
00699                         NULL, save_mode, 0./0.);
00700                 /* Next saves will create extensions */
00701                 save_mode = CPL_IO_EXTEND;
00702 
00703             } else if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
00704                 /* All IFUs seem to be deativated */
00705                 cpl_msg_warning(__func__, "All IFUs deactivated") ;
00706                 cpl_error_reset();
00707 
00708                 /* Save immediate results, free memory */
00709                 cpl_image_save(NULL, fn_flat, CPL_TYPE_FLOAT, NULL, save_mode);
00710                 cpl_image_save(NULL, fn_noise, CPL_TYPE_FLOAT, NULL, save_mode);
00711                 cpl_image_save(NULL, fn_badpix, CPL_TYPE_FLOAT, NULL,save_mode);
00712                 /* Next saves will create extensions */
00713                 save_mode = CPL_IO_EXTEND;
00714 
00715                 stored_xcal[sx] = NULL ;
00716                 stored_ycal[sx] = NULL ;
00717             } else {
00718                 // another error occured
00719                 cpl_msg_error(__func__, "Unknown ERROR !") ;
00720                 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00721                 cpl_image_delete(combined_data_on); 
00722                 cpl_image_delete(combined_noise_on); 
00723                 cpl_image_delete(bad_pix_mask_flat); 
00724                 cpl_free(angles_array) ;
00725                 cpl_msg_indent_less() ;
00726                 cpl_msg_indent_less() ;
00727                 return -1 ;
00728             }
00729             cpl_image_delete(combined_data_on); 
00730             cpl_image_delete(combined_noise_on); 
00731             cpl_image_delete(bad_pix_mask_flat); 
00732 
00733             cpl_msg_indent_less() ;
00734         } // for i = 1; i <= ne
00735         cpl_frameset_delete(angle_frameset); 
00736         cpl_msg_indent_less() ;
00737     } // for a = 0; a < nb_angles
00738 
00739     /* Clean OFF frames */
00740     for (i = 1; i <= ne; i++) {
00741         /* Compute only one detector */
00742         if (reduce_det != 0 && i != reduce_det) continue ;
00743 
00744         cpl_image_delete(combined_data_off[i-1]) ;
00745         cpl_image_delete(combined_noise_off[i-1]) ;
00746         cpl_image_delete(bad_pix_mask_dark[i-1]);
00747     }
00748 
00749     /* ----- QC parameters & saving */
00750     /* ---- load, update & save primary header */
00751     main_header = kmo_dfs_load_primary_header(frameset, FLAT_ON);
00752 
00753     /* Update which IFUs are not used */
00754     kmo_print_unused_ifus(unused_ifus_after, TRUE);
00755     kmo_set_unused_ifus(unused_ifus_after, main_header, "kmos_flat");
00756     kmo_free_unused_ifus(unused_ifus_after);
00757 
00758     /* xcal gets additionally the boundaries of the IFUs for reconstruction */
00759     main_header_xcal=kmos_create_bounds_properties(stored_ycal, ne, nb_angles) ;
00760 
00761     /* --------- saving headers  */
00762     if (!suppress_extension)    fn_suffix = cpl_sprintf("%s", suffix);
00763     else                        fn_suffix = cpl_sprintf("%s", "");
00764     cpl_free(suffix);
00765 
00766     cpl_msg_info(__func__, "Saving data...");
00767 
00768     frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00769     kmo_dfs_save_main_header(frameset, MASTER_FLAT, fn_suffix, frame, 
00770             main_header, parlist, cpl_func);
00771     kmo_dfs_save_main_header(frameset, XCAL, fn_suffix, frame, 
00772             main_header_xcal, parlist, cpl_func);
00773     kmo_dfs_save_main_header(frameset, YCAL, fn_suffix, frame, 
00774             main_header, parlist, cpl_func);
00775     kmo_dfs_save_main_header(frameset, BADPIXEL_FLAT, fn_suffix, frame, 
00776             main_header, parlist, cpl_func);
00777     kmo_dfs_save_main_header(frameset, FLAT_EDGE, fn_suffix, frame, 
00778             main_header, parlist, cpl_func);
00779 
00780     cpl_propertylist_delete(main_header);
00781     cpl_propertylist_delete(main_header_xcal);
00782 
00783     /* -------- saving sub frames  */
00784     ext_index = 0 ;
00785     for (a = 0; a < nb_angles; a++) {
00786         /* Reduce only one angle */
00787         if (reduce_angle <= 360 && angles_array[a] != reduce_angle) continue ;
00788         
00789         for (i = 1; i <= ne; i++) {
00790             /* Compute only one detector */
00791             if (reduce_det != 0 && i != reduce_det) continue ;
00792             
00793             sx = a * ne + (i - 1);
00794             // load stored data again
00795             stored_flat[sx]=kmclipm_image_load(fn_flat, CPL_TYPE_FLOAT, 0, 
00796                     ext_index);
00797             cpl_error_reset() ;
00798             stored_noise[sx]=kmclipm_image_load(fn_noise, CPL_TYPE_FLOAT, 0, 
00799                     ext_index);
00800             cpl_error_reset() ;
00801             stored_badpix[sx]=kmclipm_image_load(fn_badpix,CPL_TYPE_FLOAT, 0,
00802                     ext_index);
00803             cpl_error_reset() ;
00804             ext_index ++ ;
00805             
00806             sub_header = kmo_dfs_load_sub_header(frameset, FLAT_ON, i, FALSE);
00807             kmclipm_update_property_double(sub_header,CAL_ROTANGLE, 
00808                     ((double) angles_array[a]), 
00809                     "[deg] Rotator relative to nasmyth");
00810             
00811             if (spec_found[sx] == CPL_ERROR_NONE) {
00812                 kmclipm_update_property_int(sub_header, QC_FLAT_SAT, 
00813                         stored_qc_flat_sat[sx],
00814                         "[] nr. saturated pixels of master flat");
00815                 /* Load gain */
00816                 gain = kmo_dfs_get_property_double(sub_header, GAIN);
00817                 kmclipm_update_property_double(sub_header, QC_FLAT_EFF,
00818                         stored_qc_flat_eff[sx]/gain,
00819                         "[e-/s] rel. brightness of flat lamp");
00820              
00821                 kmclipm_update_property_double(sub_header, QC_FLAT_SN, 
00822                         stored_qc_flat_sn[sx], "[] S/N of master flat");
00823             }
00824 
00825             /* Store qc parameters only if any slitlet- and gap-width  */
00826             /* has been detected (should be the case when at least */
00827             /* one IFU is active) */
00828             if (stored_xcal[sx] != NULL) {
00829                 kmclipm_update_property_double(sub_header, QC_GAP_MEAN, 
00830                         stored_gapmean[sx],
00831                         "[pix] mean gap width between slitlets");
00832                 kmclipm_update_property_double(sub_header, QC_GAP_SDV, 
00833                         stored_gapsdv[sx],
00834                         "[pix] stdev of gap width between slitlets");
00835                 kmclipm_update_property_double(sub_header, QC_GAP_MAXDEV, 
00836                         stored_gapmaxdev[sx],
00837                         "[pix] max gap deviation between slitlets");
00838                 kmclipm_update_property_double(sub_header, QC_SLIT_MEAN, 
00839                         stored_slitmean[sx], "[pix] mean slitlet width");
00840                 kmclipm_update_property_double(sub_header, QC_SLIT_SDV, 
00841                         stored_slitsdv[sx], "[pix] stdev of slitlet widths");
00842                 kmclipm_update_property_double(sub_header, QC_SLIT_MAXDEV, 
00843                         stored_slitmaxdev[sx],
00844                         "[pix] max slitlet width deviation");
00845             }
00846         
00847             /* Calculate QC.BADPIX.NCOUNT */
00848             /* Remove 4pixel-border as bad pixels */
00849             nr_bad_pix = 0 ;
00850             if (stored_badpix[sx] != NULL) {
00851                 nr_bad_pix = cpl_image_count_rejected(stored_badpix[sx]);
00852                 nr_bad_pix -= 2*KMOS_BADPIX_BORDER*(nx-2*KMOS_BADPIX_BORDER) +
00853                     2*KMOS_BADPIX_BORDER*ny;
00854             }
00855 
00856             kmclipm_update_property_int(sub_header, QC_NR_BAD_PIX, nr_bad_pix, 
00857                     "[] nr. of bad pixels");
00858 
00859             /* Save flat frame */
00860             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00861             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00862                     "FITS extension name");
00863             cpl_free(extname);
00864 
00865             kmclipm_update_property_int(sub_header, EXTVER, sx+1, 
00866                     "FITS extension ver");
00867 
00868             kmo_dfs_save_image(stored_flat[sx], MASTER_FLAT, fn_suffix, 
00869                     sub_header, 0./0.);
00870 
00871             /* Save noise frame when enough input frames were available */
00872             extname = kmo_extname_creator(detector_frame, i, EXT_NOISE);
00873             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00874                     "FITS extension name");
00875             cpl_free(extname);
00876 
00877             kmo_dfs_save_image(stored_noise[sx], MASTER_FLAT, fn_suffix, 
00878                     sub_header, 0./0.);
00879 
00880             /* Save bad_pix frame */
00881             extname = kmo_extname_creator(detector_frame, i, EXT_BADPIX);
00882             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00883                     "FITS extension name");
00884             cpl_free(extname);
00885 
00886             kmo_dfs_save_image(stored_badpix[sx], BADPIXEL_FLAT, fn_suffix, 
00887                     sub_header, 0.);
00888 
00889             // save xcal and ycal-frame
00890             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00891             kmclipm_update_property_string(sub_header, EXTNAME, extname,
00892                     "FITS extension name");
00893             cpl_free(extname); 
00894 
00895             kmo_dfs_save_image(stored_xcal[sx], XCAL, fn_suffix, sub_header, 
00896                     0./0.);
00897             kmo_dfs_save_image(stored_ycal[sx], YCAL, fn_suffix, sub_header, 
00898                     0./0.);
00899 
00900             /* Save edge_pars-frame */
00901             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00902                 extname = cpl_sprintf("%s_IFU.%d_ANGLE.%d", EXT_LIST, 
00903                         j+1+(i-1)*KMOS_IFUS_PER_DETECTOR, angles_array[a]);
00904                 kmclipm_update_property_string(sub_header, EXTNAME, extname,
00905                         "FITS extension name");
00906                 cpl_free(extname);
00907 
00908                 kmclipm_update_property_int(sub_header, CAL_IFU_NR, 
00909                         j+1+(i-1)*KMOS_IFUS_PER_DETECTOR, "IFU Number {1..24}");
00910 
00911                 /* Save edge-parameters as product */
00912                 if ((spec_found[sx] != CPL_ERROR_DATA_NOT_FOUND) && 
00913                         (edge_table[sx] != NULL)&&(edge_table[sx][j] != NULL)) {
00914                     kmo_dfs_save_table(edge_table[sx][j], FLAT_EDGE, 
00915                             fn_suffix, sub_header);
00916                 } else {
00917                     cpl_propertylist_erase(sub_header, CRVAL1);
00918                     cpl_propertylist_erase(sub_header, CRVAL2);
00919                     cpl_propertylist_erase(sub_header, CD1_1);
00920                     cpl_propertylist_erase(sub_header, CD1_2);
00921                     cpl_propertylist_erase(sub_header, CD2_1);
00922                     cpl_propertylist_erase(sub_header, CD2_2);
00923                     cpl_propertylist_erase(sub_header, CRPIX1);
00924                     cpl_propertylist_erase(sub_header, CRPIX2);
00925                     cpl_propertylist_erase(sub_header, CTYPE1);
00926                     cpl_propertylist_erase(sub_header, CTYPE2);
00927 
00928                     kmo_dfs_save_table(NULL, FLAT_EDGE, fn_suffix, 
00929                             sub_header);
00930                 }
00931             }
00932             cpl_propertylist_delete(sub_header);
00933 
00934             cpl_image_delete(stored_flat[sx]); 
00935             cpl_image_delete(stored_noise[sx]);
00936             cpl_image_delete(stored_badpix[sx]);
00937         } // for (i = ne)
00938     } // for (a = nb_angles)
00939     
00940     // delete temporary files
00941     unlink(fn_flat);
00942     unlink(fn_noise);
00943     unlink(fn_badpix);
00944 
00945     cpl_free(stored_qc_flat_sat);
00946     cpl_free(stored_qc_flat_eff);
00947     cpl_free(stored_qc_flat_sn);
00948     cpl_free(stored_gapmean);
00949     cpl_free(stored_gapsdv);
00950     cpl_free(stored_gapmaxdev);
00951     cpl_free(stored_slitmean);
00952     cpl_free(stored_slitsdv);
00953     cpl_free(stored_slitmaxdev);
00954     cpl_free(fn_suffix);
00955     cpl_free(stored_flat);
00956     cpl_free(stored_noise);
00957     cpl_free(stored_badpix);
00958     for (i = 0; i < ne * nb_angles; i++) {
00959         cpl_image_delete(stored_xcal[i]);
00960         cpl_image_delete(stored_ycal[i]);
00961     }
00962     cpl_free(stored_xcal);
00963     cpl_free(stored_ycal);
00964     if (edge_table != NULL) {
00965         for (i = 0; i < ne * nb_angles; i++) {
00966             if (edge_table[i] != NULL) {
00967                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00968                     cpl_table_delete(edge_table[i][j]);
00969                 }
00970                 cpl_free(edge_table[i]); 
00971             }
00972         }
00973         cpl_free(edge_table);
00974     }
00975     cpl_free(spec_found);
00976     cpl_free(angles_array) ;
00977 
00978     return 0;
00979 }
00980 
00983 /*----------------------------------------------------------------------------*/
00993 /*----------------------------------------------------------------------------*/
00994 static cpl_propertylist * kmos_create_bounds_properties(
00995         cpl_image           **  stored_ycal,
00996         int                     ne,
00997         int                     nb_angles)
00998 {
00999     cpl_propertylist    *   bounds_props ;
01000     int                 *   bounds ;
01001     int                 **  total_bounds ;
01002     char                *   tmpstr ;
01003     int                     a, i, j, sx ;
01004 
01005     /* Check Entries */
01006     if (stored_ycal == NULL) return NULL ;
01007 
01008     /* Add here boundaries for reconstruction */
01009     bounds_props = cpl_propertylist_new();
01010 
01011     /* Initialize total_bounds */
01012     total_bounds = (int**)cpl_malloc(ne*sizeof(int*));
01013     for (i = 0; i < ne; i++) {
01014         total_bounds[i]=(int*)cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int));
01015         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01016             total_bounds[i][2*j] = 2048;
01017             total_bounds[i][2*j+1] = 0;
01018         }
01019     }
01020 
01021     /* Store the min left bound and max right bound for all angles */
01022     for (a = 0; a < nb_angles; a++) {
01023         for (i = 0; i < ne; i++) {
01024             sx = a * ne + i;
01025             if (stored_ycal[sx] != NULL) {
01026                 bounds = kmo_split_frame(stored_ycal[sx]);
01027 
01028                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01029                     if ((total_bounds[i][2*j] == -1)||(bounds[2*j] == -1)) {
01030                         total_bounds[i][2*j] = -1;
01031                     } else {
01032                         if (total_bounds[i][2*j] > bounds[2*j]) {
01033                             total_bounds[i][2*j] = bounds[2*j];
01034                         }
01035                     }
01036 
01037                     if ((total_bounds[i][2*j+1] == -1) || 
01038                             (bounds[2*j+1] == -1)) {
01039                         total_bounds[i][2*j+1] = -1;
01040                     } else {
01041                         if (total_bounds[i][2*j+1] < bounds[2*j+1]) {
01042                             total_bounds[i][2*j+1] = bounds[2*j+1];
01043                         }
01044                     }
01045                 }
01046                 cpl_free(bounds);
01047             } else {
01048                 // whole detector inactive
01049                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01050                     total_bounds[i][2*j] = -1;
01051                     total_bounds[i][2*j+1] = -1;
01052                 }
01053             }
01054         } // for (ne)
01055     } // for (nb_angles)
01056 
01057     /* Write the min left bound and max right bound for all angles */
01058     /* into the main header */
01059     for (i = 0; i < ne; i++) {
01060         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01061             if (total_bounds[i][2*j] > -1) {
01062                 tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
01063                         i*KMOS_IFUS_PER_DETECTOR + j+1, "_L");
01064                 kmclipm_update_property_int(bounds_props, tmpstr, 
01065                         total_bounds[i][2*j],
01066                         "[pix] left boundary for reconstr.");
01067                 cpl_free(tmpstr);
01068             }
01069 
01070             if (total_bounds[i][2*j+1] > -1) {
01071                 tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
01072                         i*KMOS_IFUS_PER_DETECTOR + j+1, "_R");
01073                 kmclipm_update_property_int(bounds_props,tmpstr, 
01074                         total_bounds[i][2*j+1],
01075                         "[pix] right boundary for reconstr.");
01076                 cpl_free(tmpstr);
01077             }
01078         }
01079     } // for (ne)
01080     for (i = 0; i < ne; i++) cpl_free(total_bounds[i]);
01081     cpl_free(total_bounds);
01082 
01083     return bounds_props ;
01084 }
01085 
01086 /*----------------------------------------------------------------------------*/
01096 /*----------------------------------------------------------------------------*/
01097 static int kmos_flat_check_inputs(
01098         cpl_frameset        *   frameset,
01099         int                 *   nx,
01100         int                 *   ny,
01101         int                 *   ne,
01102         double              *   exptime_on)
01103 {
01104     cpl_frame           *   frame ;
01105     cpl_propertylist    *   eh ;
01106     cpl_propertylist    *   mh1 ;
01107     cpl_propertylist    *   main_header ;
01108     int                     ndit ;
01109     double                  exptime ;
01110     const char          *   readmode ;
01111     int                     naxis1, naxis2, next ;
01112 
01113     /* TODO Add frames dimensions checks TODO */
01114 
01115     /* Check Entries */
01116     if (nx == NULL || ny == NULL || frameset == NULL || exptime_on == NULL) 
01117         return -1;
01118 
01119     /* check BADPIXEL_DARK */
01120     frame = kmo_dfs_get_frame(frameset, BADPIXEL_DARK);
01121     if (frame == NULL) {
01122         cpl_msg_warning(__func__, "BADPIXEL_DARK frame is missing") ;
01123         return 0 ;
01124     }
01125     next = cpl_frame_get_nextensions(frame);
01126     if (next != KMOS_NR_DETECTORS) {
01127         cpl_msg_warning(__func__, "BADPIXEL_DARK must have 3 extensions") ;
01128         return 0 ;
01129     }
01130     eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
01131     naxis1 = kmos_pfits_get_naxis1(eh) ;
01132     naxis2 = kmos_pfits_get_naxis2(eh) ;
01133     cpl_propertylist_delete(eh) ;
01134 
01135     /* check FLAT_OFF */
01136     frame = kmo_dfs_get_frame(frameset, FLAT_OFF);
01137     mh1 = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01138     ndit = cpl_propertylist_get_int(mh1, NDIT);
01139     exptime = cpl_propertylist_get_double(mh1, EXPTIME);
01140     readmode = cpl_propertylist_get_string(mh1, READMODE);
01141 
01142     /* Loop through FLAT_OFF frames */
01143     while (frame != NULL) {
01144         main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01145 
01146         if (cpl_propertylist_get_int(main_header, NDIT) != ndit) {
01147             cpl_msg_warning(__func__, "NDIT inconsistent") ;
01148             cpl_propertylist_delete(mh1);
01149             cpl_propertylist_delete(main_header);
01150             return 0 ;
01151         }
01152         if (cpl_propertylist_get_double(main_header, EXPTIME) != exptime) {
01153             cpl_msg_warning(__func__, "EXPTIME inconsistent") ;
01154             cpl_propertylist_delete(mh1);
01155             cpl_propertylist_delete(main_header);
01156             return 0 ;
01157         }
01158         if (strcmp(cpl_propertylist_get_string(main_header, READMODE), 
01159                     readmode) != 0) {
01160             cpl_msg_warning(__func__, "READMODE inconsistent") ;
01161             cpl_propertylist_delete(mh1);
01162             cpl_propertylist_delete(main_header);
01163             return 0 ;
01164         }
01165 
01166         /* Assure that arc lamps are off */
01167         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) != FALSE)
01168                 || (kmo_check_lamp(main_header, INS_LAMP2_ST) != FALSE)) {
01169             cpl_msg_warning(__func__, "Arc lamps must be switched off") ;
01170             cpl_propertylist_delete(mh1);
01171             cpl_propertylist_delete(main_header);
01172             return 0 ;
01173         }
01174         cpl_propertylist_delete(main_header);
01175         
01176         /* Get next FLAT_OFF frame */
01177         frame = kmo_dfs_get_frame(frameset, NULL);
01178     }
01179 
01180     /* Loop through FLAT_ON frames */
01181     frame = kmo_dfs_get_frame(frameset, FLAT_ON);
01182     while (frame != NULL) {
01183         main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01184 
01185         if (cpl_propertylist_get_int(main_header, NDIT) != ndit) {
01186             cpl_msg_warning(__func__, "NDIT inconsistent") ;
01187             cpl_propertylist_delete(mh1);
01188             cpl_propertylist_delete(main_header);
01189             return 0 ;
01190         }
01191         if (cpl_propertylist_get_double(main_header, EXPTIME) != exptime) {
01192             cpl_msg_warning(__func__, "EXPTIME inconsistent") ;
01193             cpl_propertylist_delete(mh1);
01194             cpl_propertylist_delete(main_header);
01195             return 0 ;
01196         }
01197         if (strcmp(cpl_propertylist_get_string(main_header, READMODE), 
01198                     readmode) != 0) {
01199             cpl_msg_warning(__func__, "READMODE inconsistent") ;
01200             cpl_propertylist_delete(mh1);
01201             cpl_propertylist_delete(main_header);
01202             return 0 ;
01203         }
01204 
01205         /* Assure that arc lamps are off */
01206         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) != FALSE)
01207                 || (kmo_check_lamp(main_header, INS_LAMP2_ST) != FALSE)) {
01208             cpl_msg_warning(__func__, "Arc lamps must be switched off") ;
01209             cpl_propertylist_delete(mh1);
01210             cpl_propertylist_delete(main_header);
01211             return 0 ;
01212         }
01213  
01214         /* Assure that at least one flat lamp is on */
01215         if ((kmo_check_lamp(main_header, INS_LAMP3_ST) != TRUE)
01216                 && (kmo_check_lamp(main_header, INS_LAMP4_ST) != TRUE)) {
01217             cpl_msg_warning(__func__, "At least one flat lamps must be on") ;
01218             cpl_propertylist_delete(mh1);
01219             cpl_propertylist_delete(main_header);
01220             return 0 ;
01221         }
01222  
01223         /* Get next FLAT_ON frame */
01224         frame = kmo_dfs_get_frame(frameset, NULL);
01225 
01226         cpl_propertylist_delete(main_header);
01227     }
01228     cpl_msg_info(__func__, "EXPTIME:  %g seconds", exptime);
01229     cpl_msg_info(__func__, "NDIT: %d", ndit);
01230     cpl_msg_info(__func__, "Detector readout mode: %s", readmode);
01231     cpl_propertylist_delete(mh1);
01232 
01233     /* Check Filters consistency */
01234     if (kmo_check_frameset_setup(frameset, FLAT_ON, TRUE, FALSE, FALSE) != 
01235             CPL_ERROR_NONE) {
01236         cpl_msg_warning(__func__, "Filters are not consistent") ;
01237         return 0 ;
01238     }
01239 
01240     /* Return */
01241     *nx = naxis1 ;
01242     *ny = naxis2 ;
01243     *ne = next ;
01244     *exptime_on = exptime ;
01245     return 1 ;
01246 }
01247