KMOS Pipeline Reference Manual  1.3.4
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 ;
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 
00670                 if (fabs(mean_noise) < 1e-3) {
00671                     cpl_image_save(combined_noise_on, "noise.fits",
00672                             CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE);
00673                     cpl_msg_error(__func__, "Division by 0.0") ;
00674                     cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00675                     cpl_free(angles_array) ;
00676                     return -1 ;
00677                 }
00678                 stored_qc_flat_sn[sx] = mean_data / mean_noise;
00679 
00680                 /* Normalize data & noise on the whole detector frame */
00681                 /* The spectral slope on each slitlet has already been  */
00682                 /* normalised in kmo_calc_curvature() */
00683                 cpl_image_divide_scalar(combined_data_on, mean_data);
00684                 cpl_image_divide_scalar(combined_noise_on, mean_data);
00685 
00686                 /* Apply the badpixel mask to the produced frames */
00687                 cpl_image_multiply(combined_data_on, bad_pix_mask_flat);
00688                 cpl_image_multiply(combined_noise_on, bad_pix_mask_flat);
00689                 cpl_image_multiply(xcal, bad_pix_mask_flat) ;
00690                 cpl_image_multiply(ycal, bad_pix_mask_flat) ;
00691 
00692                 /* Store temporarily flat, badpixel and calibration */
00693                 stored_xcal[sx] = xcal;
00694                 stored_ycal[sx] = ycal;
00695 
00696                 /* Save immediate results, free memory */
00697                 kmclipm_image_save(combined_data_on, fn_flat, CPL_TYPE_FLOAT, 
00698                         NULL, save_mode, 0./0.);
00699                 kmclipm_image_save(combined_noise_on, fn_noise, CPL_TYPE_FLOAT, 
00700                         NULL, save_mode, 0./0.);
00701                 kmclipm_image_save(bad_pix_mask_flat, fn_badpix, CPL_TYPE_FLOAT,
00702                         NULL, save_mode, 0./0.);
00703                 /* Next saves will create extensions */
00704                 save_mode = CPL_IO_EXTEND;
00705 
00706             } else if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
00707                 /* All IFUs seem to be deativated */
00708                 cpl_msg_warning(__func__, "All IFUs deactivated") ;
00709                 cpl_error_reset();
00710 
00711                 /* Save immediate results, free memory */
00712                 cpl_image_save(NULL, fn_flat, CPL_TYPE_FLOAT, NULL, save_mode);
00713                 cpl_image_save(NULL, fn_noise, CPL_TYPE_FLOAT, NULL, save_mode);
00714                 cpl_image_save(NULL, fn_badpix, CPL_TYPE_FLOAT, NULL,save_mode);
00715                 /* Next saves will create extensions */
00716                 save_mode = CPL_IO_EXTEND;
00717 
00718                 stored_xcal[sx] = NULL ;
00719                 stored_ycal[sx] = NULL ;
00720             } else {
00721                 // another error occured
00722                 cpl_msg_error(__func__, "Unknown ERROR !") ;
00723                 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00724                 cpl_image_delete(combined_data_on); 
00725                 cpl_image_delete(combined_noise_on); 
00726                 cpl_image_delete(bad_pix_mask_flat); 
00727                 cpl_free(angles_array) ;
00728                 cpl_msg_indent_less() ;
00729                 cpl_msg_indent_less() ;
00730                 return -1 ;
00731             }
00732             cpl_image_delete(combined_data_on); 
00733             cpl_image_delete(combined_noise_on); 
00734             cpl_image_delete(bad_pix_mask_flat); 
00735 
00736             cpl_msg_indent_less() ;
00737         } // for i = 1; i <= ne
00738         cpl_frameset_delete(angle_frameset); 
00739         cpl_msg_indent_less() ;
00740     } // for a = 0; a < nb_angles
00741 
00742     /* Clean OFF frames */
00743     for (i = 1; i <= ne; i++) {
00744         /* Compute only one detector */
00745         if (reduce_det != 0 && i != reduce_det) continue ;
00746 
00747         cpl_image_delete(combined_data_off[i-1]) ;
00748         cpl_image_delete(combined_noise_off[i-1]) ;
00749         cpl_image_delete(bad_pix_mask_dark[i-1]);
00750     }
00751 
00752     /* ----- QC parameters & saving */
00753     /* ---- load, update & save primary header */
00754     main_header = kmo_dfs_load_primary_header(frameset, FLAT_ON);
00755 
00756     /* Update which IFUs are not used */
00757     kmo_print_unused_ifus(unused_ifus_after, TRUE);
00758     kmo_set_unused_ifus(unused_ifus_after, main_header, "kmos_flat");
00759     kmo_free_unused_ifus(unused_ifus_after);
00760 
00761     /* xcal gets additionally the boundaries of the IFUs for reconstruction */
00762     main_header_xcal=kmos_create_bounds_properties(stored_ycal, ne, nb_angles) ;
00763 
00764     /* --------- saving headers  */
00765     if (!suppress_extension)    fn_suffix = cpl_sprintf("%s", suffix);
00766     else                        fn_suffix = cpl_sprintf("%s", "");
00767     cpl_free(suffix);
00768 
00769     cpl_msg_info(__func__, "Saving data...");
00770 
00771     frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00772     kmo_dfs_save_main_header(frameset, MASTER_FLAT, fn_suffix, frame, 
00773             main_header, parlist, cpl_func);
00774     kmo_dfs_save_main_header(frameset, XCAL, fn_suffix, frame, 
00775             main_header_xcal, parlist, cpl_func);
00776     kmo_dfs_save_main_header(frameset, YCAL, fn_suffix, frame, 
00777             main_header, parlist, cpl_func);
00778     kmo_dfs_save_main_header(frameset, BADPIXEL_FLAT, fn_suffix, frame, 
00779             main_header, parlist, cpl_func);
00780     kmo_dfs_save_main_header(frameset, FLAT_EDGE, fn_suffix, frame, 
00781             main_header, parlist, cpl_func);
00782 
00783     cpl_propertylist_delete(main_header);
00784     cpl_propertylist_delete(main_header_xcal);
00785 
00786     /* -------- saving sub frames  */
00787     for (a = 0; a < nb_angles; a++) {
00788         for (i = 1; i <= ne; i++) {
00789             sx = a * ne + (i - 1);
00790 
00791             // load stored data again
00792             stored_flat[sx]=kmclipm_image_load(fn_flat, CPL_TYPE_FLOAT, 0, sx);
00793             cpl_error_reset() ;
00794             stored_noise[sx]=kmclipm_image_load(fn_noise, CPL_TYPE_FLOAT, 0,sx);
00795             cpl_error_reset() ;
00796             stored_badpix[sx]=kmclipm_image_load(fn_badpix,CPL_TYPE_FLOAT,0,sx);
00797             cpl_error_reset() ;
00798 
00799             sub_header = kmo_dfs_load_sub_header(frameset, FLAT_ON, i, FALSE);
00800             kmclipm_update_property_double(sub_header,CAL_ROTANGLE, 
00801                     ((double) angles_array[a]), 
00802                     "[deg] Rotator relative to nasmyth");
00803 
00804             if (spec_found[sx] == CPL_ERROR_NONE) {
00805                 kmclipm_update_property_int(sub_header, QC_FLAT_SAT, 
00806                         stored_qc_flat_sat[sx],
00807                         "[] nr. saturated pixels of master flat");
00808                 /* Load gain */
00809                 gain = kmo_dfs_get_property_double(sub_header, GAIN);
00810 
00811                 kmclipm_update_property_double(sub_header, QC_FLAT_EFF,
00812                         stored_qc_flat_eff[sx]/gain,
00813                         "[e-/s] rel. brightness of flat lamp");
00814                 kmclipm_update_property_double(sub_header, QC_FLAT_SN, 
00815                         stored_qc_flat_sn[sx], "[] S/N of master flat");
00816             }
00817 
00818             /* Store qc parameters only if any slitlet- and gap-width  */
00819             /* has been detected (should be the case when at least */
00820             /* one IFU is active) */
00821             if (stored_xcal[sx] != NULL) {
00822                 kmclipm_update_property_double(sub_header, QC_GAP_MEAN, 
00823                         stored_gapmean[sx],
00824                         "[pix] mean gap width between slitlets");
00825                 kmclipm_update_property_double(sub_header, QC_GAP_SDV, 
00826                         stored_gapsdv[sx],
00827                         "[pix] stdev of gap width between slitlets");
00828                 kmclipm_update_property_double(sub_header, QC_GAP_MAXDEV, 
00829                         stored_gapmaxdev[sx],
00830                         "[pix] max gap deviation between slitlets");
00831                 kmclipm_update_property_double(sub_header, QC_SLIT_MEAN, 
00832                         stored_slitmean[sx], "[pix] mean slitlet width");
00833                 kmclipm_update_property_double(sub_header, QC_SLIT_SDV, 
00834                         stored_slitsdv[sx], "[pix] stdev of slitlet widths");
00835                 kmclipm_update_property_double(sub_header, QC_SLIT_MAXDEV, 
00836                         stored_slitmaxdev[sx],
00837                         "[pix] max slitlet width deviation");
00838             }
00839 
00840         
00841             /* Calculate QC.BADPIX.NCOUNT */
00842             /* Remove 4pixel-border as bad pixels */
00843             nr_bad_pix = 0 ;
00844             if (stored_badpix[sx] != NULL) {
00845                 nr_bad_pix = cpl_image_count_rejected(stored_badpix[sx]);
00846                 nr_bad_pix -= 2*KMOS_BADPIX_BORDER*(nx-2*KMOS_BADPIX_BORDER) +
00847                     2*KMOS_BADPIX_BORDER*ny;
00848             }
00849 
00850             kmclipm_update_property_int(sub_header, QC_NR_BAD_PIX, nr_bad_pix, 
00851                     "[] nr. of bad pixels");
00852 
00853             /* Save flat frame */
00854             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00855             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00856                     "FITS extension name");
00857             cpl_free(extname);
00858 
00859             kmclipm_update_property_int(sub_header, EXTVER, sx+1, 
00860                     "FITS extension ver");
00861 
00862             kmo_dfs_save_image(stored_flat[sx], MASTER_FLAT, fn_suffix, 
00863                     sub_header, 0./0.);
00864 
00865             /* Save noise frame when enough input frames were available */
00866             extname = kmo_extname_creator(detector_frame, i, EXT_NOISE);
00867             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00868                     "FITS extension name");
00869             cpl_free(extname);
00870 
00871             kmo_dfs_save_image(stored_noise[sx], MASTER_FLAT, fn_suffix, 
00872                     sub_header, 0./0.);
00873 
00874             /* Save bad_pix frame */
00875             extname = kmo_extname_creator(detector_frame, i, EXT_BADPIX);
00876             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00877                     "FITS extension name");
00878             cpl_free(extname);
00879 
00880             kmo_dfs_save_image(stored_badpix[sx], BADPIXEL_FLAT, fn_suffix, 
00881                     sub_header, 0.);
00882 
00883             // save xcal and ycal-frame
00884             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00885             kmclipm_update_property_string(sub_header, EXTNAME, extname,
00886                     "FITS extension name");
00887             cpl_free(extname); 
00888 
00889             kmo_dfs_save_image(stored_xcal[sx], XCAL, fn_suffix, sub_header, 
00890                     0./0.);
00891             kmo_dfs_save_image(stored_ycal[sx], YCAL, fn_suffix, sub_header, 
00892                     0./0.);
00893 
00894             /* Save edge_pars-frame */
00895             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00896                 extname = cpl_sprintf("%s_IFU.%d_ANGLE.%d", EXT_LIST, 
00897                         j+1+(i-1)*KMOS_IFUS_PER_DETECTOR, angles_array[a]);
00898                 kmclipm_update_property_string(sub_header, EXTNAME, extname,
00899                         "FITS extension name");
00900                 cpl_free(extname);
00901 
00902                 kmclipm_update_property_int(sub_header, CAL_IFU_NR, 
00903                         j+1+(i-1)*KMOS_IFUS_PER_DETECTOR, "IFU Number {1..24}");
00904 
00905                 /* Save edge-parameters as product */
00906                 if ((spec_found[sx] != CPL_ERROR_DATA_NOT_FOUND) && 
00907                         (edge_table[sx] != NULL)&&(edge_table[sx][j] != NULL)) {
00908                     kmo_dfs_save_table(edge_table[sx][j], FLAT_EDGE, 
00909                             fn_suffix, sub_header);
00910                 } else {
00911                     cpl_propertylist_erase(sub_header, CRVAL1);
00912                     cpl_propertylist_erase(sub_header, CRVAL2);
00913                     cpl_propertylist_erase(sub_header, CD1_1);
00914                     cpl_propertylist_erase(sub_header, CD1_2);
00915                     cpl_propertylist_erase(sub_header, CD2_1);
00916                     cpl_propertylist_erase(sub_header, CD2_2);
00917                     cpl_propertylist_erase(sub_header, CRPIX1);
00918                     cpl_propertylist_erase(sub_header, CRPIX2);
00919                     cpl_propertylist_erase(sub_header, CTYPE1);
00920                     cpl_propertylist_erase(sub_header, CTYPE2);
00921 
00922                     kmo_dfs_save_table(NULL, FLAT_EDGE, fn_suffix, 
00923                             sub_header);
00924                 }
00925             }
00926             cpl_propertylist_delete(sub_header);
00927 
00928             cpl_image_delete(stored_flat[sx]); 
00929             cpl_image_delete(stored_noise[sx]);
00930             cpl_image_delete(stored_badpix[sx]);
00931         } // for (i = ne)
00932     } // for (a = nb_angles)
00933     
00934     // delete temporary files
00935     unlink(fn_flat);
00936     unlink(fn_noise);
00937     unlink(fn_badpix);
00938 
00939     cpl_free(stored_qc_flat_sat);
00940     cpl_free(stored_qc_flat_eff);
00941     cpl_free(stored_qc_flat_sn);
00942     cpl_free(stored_gapmean);
00943     cpl_free(stored_gapsdv);
00944     cpl_free(stored_gapmaxdev);
00945     cpl_free(stored_slitmean);
00946     cpl_free(stored_slitsdv);
00947     cpl_free(stored_slitmaxdev);
00948     cpl_free(fn_suffix);
00949     cpl_free(stored_flat);
00950     cpl_free(stored_noise);
00951     cpl_free(stored_badpix);
00952     for (i = 0; i < ne * nb_angles; i++) {
00953         cpl_image_delete(stored_xcal[i]);
00954         cpl_image_delete(stored_ycal[i]);
00955     }
00956     cpl_free(stored_xcal);
00957     cpl_free(stored_ycal);
00958     if (edge_table != NULL) {
00959         for (i = 0; i < ne * nb_angles; i++) {
00960             if (edge_table[i] != NULL) {
00961                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00962                     cpl_table_delete(edge_table[i][j]);
00963                 }
00964                 cpl_free(edge_table[i]); 
00965             }
00966         }
00967         cpl_free(edge_table);
00968     }
00969     cpl_free(spec_found);
00970     cpl_free(angles_array) ;
00971 
00972     return 0;
00973 }
00974 
00977 /*----------------------------------------------------------------------------*/
00987 /*----------------------------------------------------------------------------*/
00988 static cpl_propertylist * kmos_create_bounds_properties(
00989         cpl_image           **  stored_ycal,
00990         int                     ne,
00991         int                     nb_angles)
00992 {
00993     cpl_propertylist    *   bounds_props ;
00994     int                 *   bounds ;
00995     int                 **  total_bounds ;
00996     char                *   tmpstr ;
00997     int                     a, i, j, sx ;
00998 
00999     /* Check Entries */
01000     if (stored_ycal == NULL) return NULL ;
01001 
01002     /* Add here boundaries for reconstruction */
01003     bounds_props = cpl_propertylist_new();
01004 
01005     /* Initialize total_bounds */
01006     total_bounds = (int**)cpl_malloc(ne*sizeof(int*));
01007     for (i = 0; i < ne; i++) {
01008         total_bounds[i]=(int*)cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int));
01009         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01010             total_bounds[i][2*j] = 2048;
01011             total_bounds[i][2*j+1] = 0;
01012         }
01013     }
01014 
01015     /* Store the min left bound and max right bound for all angles */
01016     for (a = 0; a < nb_angles; a++) {
01017         for (i = 0; i < ne; i++) {
01018             sx = a * ne + i;
01019             if (stored_ycal[sx] != NULL) {
01020                 bounds = kmo_split_frame(stored_ycal[sx]);
01021 
01022                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01023                     if ((total_bounds[i][2*j] == -1)||(bounds[2*j] == -1)) {
01024                         total_bounds[i][2*j] = -1;
01025                     } else {
01026                         if (total_bounds[i][2*j] > bounds[2*j]) {
01027                             total_bounds[i][2*j] = bounds[2*j];
01028                         }
01029                     }
01030 
01031                     if ((total_bounds[i][2*j+1] == -1) || 
01032                             (bounds[2*j+1] == -1)) {
01033                         total_bounds[i][2*j+1] = -1;
01034                     } else {
01035                         if (total_bounds[i][2*j+1] < bounds[2*j+1]) {
01036                             total_bounds[i][2*j+1] = bounds[2*j+1];
01037                         }
01038                     }
01039                 }
01040                 cpl_free(bounds);
01041             } else {
01042                 // whole detector inactive
01043                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01044                     total_bounds[i][2*j] = -1;
01045                     total_bounds[i][2*j+1] = -1;
01046                 }
01047             }
01048         } // for (ne)
01049     } // for (nb_angles)
01050 
01051     /* Write the min left bound and max right bound for all angles */
01052     /* into the main header */
01053     for (i = 0; i < ne; i++) {
01054         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01055             if (total_bounds[i][2*j] > -1) {
01056                 tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
01057                         i*KMOS_IFUS_PER_DETECTOR + j+1, "_L");
01058                 kmclipm_update_property_int(bounds_props, tmpstr, 
01059                         total_bounds[i][2*j],
01060                         "[pix] left boundary for reconstr.");
01061                 cpl_free(tmpstr);
01062             }
01063 
01064             if (total_bounds[i][2*j+1] > -1) {
01065                 tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
01066                         i*KMOS_IFUS_PER_DETECTOR + j+1, "_R");
01067                 kmclipm_update_property_int(bounds_props,tmpstr, 
01068                         total_bounds[i][2*j+1],
01069                         "[pix] right boundary for reconstr.");
01070                 cpl_free(tmpstr);
01071             }
01072         }
01073     } // for (ne)
01074     for (i = 0; i < ne; i++) cpl_free(total_bounds[i]);
01075     cpl_free(total_bounds);
01076 
01077     return bounds_props ;
01078 }
01079 
01080 /*----------------------------------------------------------------------------*/
01090 /*----------------------------------------------------------------------------*/
01091 static int kmos_flat_check_inputs(
01092         cpl_frameset        *   frameset,
01093         int                 *   nx,
01094         int                 *   ny,
01095         int                 *   ne,
01096         double              *   exptime_on)
01097 {
01098     cpl_frame           *   frame ;
01099     cpl_propertylist    *   eh ;
01100     cpl_propertylist    *   mh1 ;
01101     cpl_propertylist    *   main_header ;
01102     int                     ndit ;
01103     double                  exptime ;
01104     const char          *   readmode ;
01105     int                     naxis1, naxis2, next ;
01106 
01107     /* TODO Add frames dimensions checks TODO */
01108 
01109     /* Check Entries */
01110     if (nx == NULL || ny == NULL || frameset == NULL || exptime_on == NULL) 
01111         return -1;
01112 
01113     /* check BADPIXEL_DARK */
01114     frame = kmo_dfs_get_frame(frameset, BADPIXEL_DARK);
01115     if (frame == NULL) {
01116         cpl_msg_warning(__func__, "BADPIXEL_DARK frame is missing") ;
01117         return 0 ;
01118     }
01119     next = cpl_frame_get_nextensions(frame);
01120     if (next != KMOS_NR_DETECTORS) {
01121         cpl_msg_warning(__func__, "BADPIXEL_DARK must have 3 extensions") ;
01122         return 0 ;
01123     }
01124     eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
01125     naxis1 = kmos_pfits_get_naxis1(eh) ;
01126     naxis2 = kmos_pfits_get_naxis2(eh) ;
01127     cpl_propertylist_delete(eh) ;
01128 
01129     /* check FLAT_OFF */
01130     frame = kmo_dfs_get_frame(frameset, FLAT_OFF);
01131     mh1 = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01132     ndit = cpl_propertylist_get_int(mh1, NDIT);
01133     exptime = cpl_propertylist_get_double(mh1, EXPTIME);
01134     readmode = cpl_propertylist_get_string(mh1, READMODE);
01135 
01136     /* Loop through FLAT_OFF frames */
01137     while (frame != NULL) {
01138         main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01139 
01140         if (cpl_propertylist_get_int(main_header, NDIT) != ndit) {
01141             cpl_msg_warning(__func__, "NDIT inconsistent") ;
01142             cpl_propertylist_delete(mh1);
01143             cpl_propertylist_delete(main_header);
01144             return 0 ;
01145         }
01146         if (cpl_propertylist_get_double(main_header, EXPTIME) != exptime) {
01147             cpl_msg_warning(__func__, "EXPTIME inconsistent") ;
01148             cpl_propertylist_delete(mh1);
01149             cpl_propertylist_delete(main_header);
01150             return 0 ;
01151         }
01152         if (strcmp(cpl_propertylist_get_string(main_header, READMODE), 
01153                     readmode) != 0) {
01154             cpl_msg_warning(__func__, "READMODE inconsistent") ;
01155             cpl_propertylist_delete(mh1);
01156             cpl_propertylist_delete(main_header);
01157             return 0 ;
01158         }
01159 
01160         /* Assure that arc lamps are off */
01161         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) != FALSE)
01162                 || (kmo_check_lamp(main_header, INS_LAMP2_ST) != FALSE)) {
01163             cpl_msg_warning(__func__, "Arc lamps must be switched off") ;
01164             cpl_propertylist_delete(mh1);
01165             cpl_propertylist_delete(main_header);
01166             return 0 ;
01167         }
01168         cpl_propertylist_delete(main_header);
01169         
01170         /* Get next FLAT_OFF frame */
01171         frame = kmo_dfs_get_frame(frameset, NULL);
01172     }
01173 
01174     /* Loop through FLAT_ON frames */
01175     frame = kmo_dfs_get_frame(frameset, FLAT_ON);
01176     while (frame != NULL) {
01177         main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01178 
01179         if (cpl_propertylist_get_int(main_header, NDIT) != ndit) {
01180             cpl_msg_warning(__func__, "NDIT inconsistent") ;
01181             cpl_propertylist_delete(mh1);
01182             cpl_propertylist_delete(main_header);
01183             return 0 ;
01184         }
01185         if (cpl_propertylist_get_double(main_header, EXPTIME) != exptime) {
01186             cpl_msg_warning(__func__, "EXPTIME inconsistent") ;
01187             cpl_propertylist_delete(mh1);
01188             cpl_propertylist_delete(main_header);
01189             return 0 ;
01190         }
01191         if (strcmp(cpl_propertylist_get_string(main_header, READMODE), 
01192                     readmode) != 0) {
01193             cpl_msg_warning(__func__, "READMODE inconsistent") ;
01194             cpl_propertylist_delete(mh1);
01195             cpl_propertylist_delete(main_header);
01196             return 0 ;
01197         }
01198 
01199         /* Assure that arc lamps are off */
01200         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) != FALSE)
01201                 || (kmo_check_lamp(main_header, INS_LAMP2_ST) != FALSE)) {
01202             cpl_msg_warning(__func__, "Arc lamps must be switched off") ;
01203             cpl_propertylist_delete(mh1);
01204             cpl_propertylist_delete(main_header);
01205             return 0 ;
01206         }
01207  
01208         /* Assure that at least one flat lamp is on */
01209         if ((kmo_check_lamp(main_header, INS_LAMP3_ST) != TRUE)
01210                 && (kmo_check_lamp(main_header, INS_LAMP4_ST) != TRUE)) {
01211             cpl_msg_warning(__func__, "At least one flat lamps must be on") ;
01212             cpl_propertylist_delete(mh1);
01213             cpl_propertylist_delete(main_header);
01214             return 0 ;
01215         }
01216  
01217         /* Get next FLAT_ON frame */
01218         frame = kmo_dfs_get_frame(frameset, NULL);
01219 
01220         cpl_propertylist_delete(main_header);
01221     }
01222     cpl_msg_info(__func__, "EXPTIME:  %g seconds", exptime);
01223     cpl_msg_info(__func__, "NDIT: %d", ndit);
01224     cpl_msg_info(__func__, "Detector readout mode: %s", readmode);
01225     cpl_propertylist_delete(mh1);
01226 
01227     /* Check Filters consistency */
01228     if (kmo_check_frameset_setup(frameset, FLAT_ON, TRUE, FALSE, FALSE) != 
01229             CPL_ERROR_NONE) {
01230         cpl_msg_warning(__func__, "Filters are not consistent") ;
01231         return 0 ;
01232     }
01233 
01234     /* Return */
01235     *nx = naxis1 ;
01236     *ny = naxis2 ;
01237     *ne = next ;
01238     *exptime_on = exptime ;
01239     return 1 ;
01240 }
01241