KMOS Pipeline Reference Manual  1.3.11
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     int                     nx, ny, next ;
00380     int                 *   angles_array ;
00381     int                     nb_angles ;
00382     cpl_image           **  stored_flat ;
00383     cpl_image           **  stored_noise ;
00384     cpl_image           **  stored_badpix ;
00385     cpl_image           **  stored_xcal ;
00386     cpl_image           **  stored_ycal ;
00387     double              *   stored_gapmean ;
00388     double              *   stored_gapsdv ;
00389     double              *   stored_gapmaxdev ;
00390     double              *   stored_slitmean ;
00391     double              *   stored_slitsdv ;
00392     double              *   stored_slitmaxdev ;
00393     double              *   stored_qc_flat_eff ;
00394     double              *   stored_qc_flat_sn ;
00395     int                 *   stored_qc_flat_sat ;
00396     cpl_frameset        *   angle_frameset ;
00397     char                *   extname ;
00398     char                *   suffix ;
00399     char                *   fn_suffix ;
00400     unsigned int            save_mode ;
00401     char                *   fn_flat = "flat_tmp.fits" ;
00402     char                *   fn_noise = "flat_noise.fits" ;
00403     char                *   fn_badpix = "badpix_tmp.fits" ;
00404     cpl_imagelist       *   det_lamp_on ;
00405     cpl_imagelist       *   det_lamp_off ;
00406     cpl_image           *   img_in ;
00407     cpl_image           *   combined_data_on ;
00408     cpl_image           *   combined_noise_on ;
00409     cpl_image           *   combined_data_off[KMOS_NR_DETECTORS] ;
00410     cpl_image           *   combined_noise_off[KMOS_NR_DETECTORS] ;
00411     cpl_image           *   bad_pix_mask_flat ;
00412     cpl_image           *   bad_pix_mask_dark[KMOS_NR_DETECTORS] ;
00413     cpl_image           *   xcal ;
00414     cpl_image           *   ycal ;
00415     cpl_array           **  unused_ifus_before ;
00416     cpl_array           **  unused_ifus_after ;
00417     cpl_propertylist    *   main_header ;
00418     cpl_propertylist    *   main_header_xcal ;
00419     cpl_propertylist    *   sub_header ;
00420     cpl_table           *** edge_table ;
00421     cpl_error_code      *   spec_found ;
00422     double                  gain, exptime, mean_data, mean_noise ;
00423     int                     sx, nr_bad_pix, nr_sat, i, j, a ;
00424 
00425     /* Check entries */
00426     if (parlist == NULL || frameset == NULL) {
00427         cpl_msg_error(__func__, "Null Inputs") ;
00428         cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
00429         return -1 ;
00430     }
00431 
00432     /* Get Parameters */
00433     par = cpl_parameterlist_find_const(parlist,
00434             "kmos.kmos_flat.surrounding_pixels");
00435     surrounding_pixels = cpl_parameter_get_int(par);
00436     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_flat.badpix_thresh");
00437     badpix_thresh = cpl_parameter_get_int(par);
00438     par = cpl_parameterlist_find_const(parlist,
00439             "kmos.kmos_flat.suppress_extension");
00440     suppress_extension = cpl_parameter_get_bool(par);
00441     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_flat.angle");
00442     reduce_angle = cpl_parameter_get_double(par);
00443     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_flat.detector");
00444     reduce_det = cpl_parameter_get_int(par);
00445  
00446     kmos_combine_pars_load(parlist, "kmos.kmos_flat", &cmethod, &cpos_rej,
00447        &cneg_rej, &citer, &cmin, &cmax, FALSE);
00448 
00449     /* Check Parameters */
00450     if (surrounding_pixels < 0 || surrounding_pixels > 8) {
00451         cpl_msg_error(__func__, "surrounding_pixels must be in [0,8]") ;
00452         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00453         return -1 ;
00454     }
00455     if (badpix_thresh < 0 || badpix_thresh > 100) {
00456         cpl_msg_error(__func__, "badpix_thresh must be in [0,100]") ;
00457         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00458         return -1 ;
00459     }
00460     if (reduce_det < 0 || reduce_det > 3) {
00461         cpl_msg_error(__func__, "detector must be in [1,3]") ;
00462         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00463         return -1 ;
00464     }
00465 
00466     /* Identify the RAW and CALIB frames in the input frameset */
00467     if (kmo_dfs_set_groups(frameset, "kmos_flat") != 1) {
00468         cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
00469         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00470         return -1 ;
00471     }
00472 
00473     /* Check the inputs consistency */
00474     if (kmos_flat_check_inputs(frameset, &nx, &ny, &next, &exptime) != 1) {
00475         cpl_msg_error(__func__, "Input frameset is not consistent") ;
00476         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00477         return -1 ;
00478     }
00479     
00480     /* Instrument setup */
00481     suffix = kmo_dfs_get_suffix(kmo_dfs_get_frame(frameset,FLAT_ON),TRUE,FALSE);
00482     cpl_msg_info(__func__, "Detected instrument setup:   %s", suffix+1);
00483 
00484     /* Get Rotator angles */
00485     if ((angles_array = kmos_get_angles(frameset, &nb_angles,FLAT_ON)) == NULL){
00486         cpl_msg_error(__func__, "Cannot get Angles informations") ;
00487         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00488         return -1 ;
00489     }
00490  
00491     /* The frames have to be stored temporarily because the QC parameters */
00492     /* for the main header are calculated from each detector.  */
00493     /* So they can be stored only when all detectors are processed */
00494     stored_flat = (cpl_image**)cpl_calloc(next*nb_angles, sizeof(cpl_image*));
00495     stored_noise = (cpl_image**)cpl_calloc(next*nb_angles, sizeof(cpl_image*));
00496     stored_badpix = (cpl_image**)cpl_calloc(next*nb_angles, sizeof(cpl_image*));
00497     stored_xcal = (cpl_image**)cpl_calloc(next*nb_angles, sizeof(cpl_image*));
00498     stored_ycal = (cpl_image**)cpl_calloc(next * nb_angles, sizeof(cpl_image*));
00499     stored_qc_flat_sat = (int*)cpl_malloc(next * nb_angles * sizeof(int));
00500     stored_qc_flat_eff = (double*)cpl_malloc(next * nb_angles * sizeof(double));
00501     stored_qc_flat_sn = (double*)cpl_malloc(next * nb_angles * sizeof(double));
00502     stored_gapmean = (double*)cpl_malloc(next * nb_angles * sizeof(double));
00503     stored_gapsdv = (double*)cpl_malloc(next * nb_angles * sizeof(double));
00504     stored_gapmaxdev = (double*)cpl_malloc(next * nb_angles * sizeof(double));
00505     stored_slitmean = (double*)cpl_malloc(next * nb_angles * sizeof(double));
00506     stored_slitsdv = (double*)cpl_malloc(next * nb_angles * sizeof(double));
00507     stored_slitmaxdev = (double*)cpl_malloc(next * nb_angles * sizeof(double));
00508     spec_found = (cpl_error_code*)cpl_malloc(next * nb_angles * 
00509             sizeof(cpl_error_code));
00510 
00511     /* Initialise */
00512     for (i = 0; i < next * nb_angles ; i++) {
00513         stored_qc_flat_sat[i] = 0;
00514         stored_qc_flat_eff[i] = 0.0;
00515         stored_qc_flat_sn[i] = 0.0;
00516         stored_gapmean[i] = 0.0;
00517         stored_gapsdv[i] = 0.0;
00518         stored_gapmaxdev[i] = 0.0;
00519         stored_slitmean[i] = 0.0;
00520         stored_slitsdv[i] = 0.0;
00521         stored_slitmaxdev[i] = 0.0;
00522         spec_found[i] = CPL_ERROR_NONE;
00523     }
00524 
00525     /* TODO : Improve handling of edge_table !!!! */
00526     edge_table = (cpl_table***)cpl_malloc(next*nb_angles * sizeof(cpl_table**));
00527     for (i = 0; i < next * nb_angles; i++) edge_table[i] = NULL;
00528 
00529     /* Check which IFUs are active for all FLAT_ON frames */
00530     unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0);
00531     unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before);
00532     kmo_print_unused_ifus(unused_ifus_before, FALSE);
00533     kmo_free_unused_ifus(unused_ifus_before);
00534 
00535     /* Combine the FLAT_OFF frames for the 3 detectors */
00536     for (i = 1; i <= next; i++) {
00537         /* Compute only one detector */
00538         if (reduce_det != 0 && i != reduce_det) continue ;
00539 
00540         /* Load the badpixel masks */
00541         bad_pix_mask_dark[i-1] = kmo_dfs_load_image(frameset, BADPIXEL_DARK, 
00542                 i, 2, FALSE, NULL) ;
00543 
00544         /* Load lamp-off images */
00545         det_lamp_off = cpl_imagelist_new();
00546         frame = kmo_dfs_get_frame(frameset, FLAT_OFF);
00547         j = 0;
00548         while (frame != NULL) {
00549             img_in = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, NULL);
00550             kmo_image_reject_from_mask(img_in, bad_pix_mask_dark[i-1]);
00551             cpl_imagelist_set(det_lamp_off, img_in, j++);
00552             frame = kmo_dfs_get_frame(frameset, NULL);
00553         }
00554 
00555         /* Combine FLAT_OFF frames */
00556         cpl_msg_info(__func__, "Combine FLAT_OFF frames for Detector %d", i) ;
00557         kmos_combine_frames(det_lamp_off, cmethod, cpos_rej,
00558                 cneg_rej, citer, cmax, cmin, &(combined_data_off[i-1]), 
00559                 &(combined_noise_off[i-1]), -1.0);
00560         /*
00561         cpl_image_save(combined_data_off[i-1], "off.fits",
00562                 CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE) ;
00563         */
00564         cpl_imagelist_delete(det_lamp_off);
00565         cpl_image_power(combined_noise_off[i-1], 2.0);
00566     }
00567 
00568     save_mode = CPL_IO_CREATE;
00569     /* Loop all Rotator Angles and Detectors  */
00570     for (a = 0; a < nb_angles; a++) {
00571         /* Reduce only one angle */
00572         if (reduce_angle <= 360 && angles_array[a] != reduce_angle) continue ;
00573 
00574         cpl_msg_info(__func__, "Processing rotator angle %d -> %d degree", 
00575                 a, angles_array[a]);
00576         cpl_msg_indent_more() ;
00577         
00578         /* Get the frameset with this angle */
00579         angle_frameset = kmos_get_angle_frameset(frameset, angles_array[a],
00580                 FLAT_ON);
00581         if (angle_frameset == NULL) {
00582             cpl_msg_error(__func__, "Cannot get angle frameset") ;
00583             cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00584             cpl_msg_indent_less() ;
00585             cpl_free(angles_array) ;
00586             return -1 ;
00587         }
00588 
00589         for (i = 1; i <= next; i++) {
00590             /* Compute only one detector */
00591             if (reduce_det != 0 && i != reduce_det) continue ;
00592 
00593             cpl_msg_info(__func__, "Processing detector No. %d", i);
00594             cpl_msg_indent_more() ;
00595 
00596             sx = a * next + (i - 1);
00597 
00598             /* Load lamp-on images for Angle a */
00599             det_lamp_on = cpl_imagelist_new();
00600             frame = kmo_dfs_get_frame(angle_frameset, FLAT_ON);
00601             j = 0;
00602             while (frame != NULL) {
00603                 img_in=kmo_dfs_load_image_frame(frame, i, FALSE, TRUE, &nr_sat);
00604                 kmo_image_reject_from_mask(img_in, bad_pix_mask_dark[i-1]);
00605                 cpl_imagelist_set(det_lamp_on, img_in, j++);
00606                 frame = kmo_dfs_get_frame(angle_frameset, NULL);
00607             }
00608 
00609             /* Count saturated pixels for each detector */
00610             cpl_msg_info(__func__, "Count Saturated pixels on the detector") ;
00611             frame = kmo_dfs_get_frame(angle_frameset, FLAT_ON);
00612             main_header = kmclipm_propertylist_load(
00613                     cpl_frame_get_filename(frame), 0);
00614             if (strcmp(cpl_propertylist_get_string(main_header, READMODE), 
00615                         "Nondest") == 0) {
00616                 // NDR: non-destructive readout mode
00617                 stored_qc_flat_sat[sx] = nr_sat;
00618             } else {
00619                 // normal readout mode
00620                 stored_qc_flat_sat[sx] = kmo_imagelist_get_saturated(
00621                         det_lamp_on, KMO_FLAT_SATURATED, KMO_FLAT_SAT_MIN);
00622             }
00623             cpl_propertylist_delete(main_header); 
00624 
00625             /* Combine imagelists and create noise */
00626             cpl_msg_info(__func__, "Combine FLAT_ON frames") ;
00627             kmos_combine_frames(det_lamp_on, cmethod, cpos_rej, 
00628                     cneg_rej, citer, cmax, cmin, &combined_data_on, 
00629                     &combined_noise_on, -1.0);
00630             cpl_imagelist_delete(det_lamp_on); 
00631 
00632             if (kmclipm_omit_warning_one_slice > 10) 
00633                 kmclipm_omit_warning_one_slice = FALSE;
00634 
00635             /* Subtract combined lamp_off from lamp_on */
00636             cpl_image_subtract(combined_data_on, combined_data_off[i-1]);
00637 
00638             /* noise: sig_x = sqrt(sig_u^2 + sig_v^2 */
00639             cpl_msg_info(__func__, "Compute the noise") ;
00640             cpl_image_power(combined_noise_on, 2.0);
00641             cpl_image_add(combined_noise_on, combined_noise_off[i-1]);
00642             cpl_image_power(combined_noise_on, 0.5);
00643 
00644             /* Create bad-pixel-mask */
00645             bad_pix_mask_flat = kmo_create_bad_pix_flat_thresh(combined_data_on,
00646                         surrounding_pixels, badpix_thresh);
00647 
00648             /* Calculate spectral curvature here */
00649             cpl_msg_info(__func__, "Compute the spectral curvature") ;
00650             cpl_msg_indent_more() ;
00651             spec_found[sx] = kmo_calc_curvature(combined_data_on,
00652                     combined_noise_on, unused_ifus_after[i-1],
00653                     bad_pix_mask_flat, i, &xcal, &ycal, stored_gapmean+(sx),
00654                     stored_gapsdv+(sx), stored_gapmaxdev+(sx),
00655                     stored_slitmean+(sx), stored_slitsdv+(sx),
00656                     stored_slitmaxdev+(sx), &edge_table[sx]);
00657             cpl_msg_indent_less() ;
00658 
00659             if (spec_found[sx] == CPL_ERROR_NONE) {
00660                 // in kmo_calc_curvature() the spectral slope of each 
00661                 // slitlet has been normalised individually. Now the 
00662                 // normalisation on the whole frame is applied. 
00663                 // (cpl_image_get_mean() ignores bad pixels when 
00664                 // calculating the mean)
00665                 mean_data = cpl_image_get_mean(combined_data_on);
00666                 stored_qc_flat_eff[sx] = mean_data / exptime;
00667                 mean_noise = cpl_image_get_mean(combined_noise_on);
00668                 if (fabs(mean_noise) < 1e-3) {
00669                     cpl_msg_error(__func__, "Division by 0.0") ;
00670                     cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00671                     cpl_free(angles_array) ;
00672                     return -1 ;
00673                 }
00674                 stored_qc_flat_sn[sx] = mean_data / mean_noise;
00675 
00676                 /* Normalize data & noise on the whole detector frame */
00677                 /* The spectral slope on each slitlet has already been  */
00678                 /* normalised in kmo_calc_curvature() */
00679                 cpl_image_divide_scalar(combined_data_on, mean_data);
00680                 cpl_image_divide_scalar(combined_noise_on, mean_data);
00681 
00682                 /* Apply the badpixel mask to the produced frames */
00683                 cpl_image_multiply(combined_data_on, bad_pix_mask_flat);
00684                 cpl_image_multiply(combined_noise_on, bad_pix_mask_flat);
00685                 cpl_image_multiply(xcal, bad_pix_mask_flat) ;
00686                 cpl_image_multiply(ycal, bad_pix_mask_flat) ;
00687 
00688                 /* Store temporarily flat, badpixel and calibration */
00689                 stored_xcal[sx] = xcal;
00690                 stored_ycal[sx] = ycal;
00691 
00692                 /* Save immediate results, free memory */
00693                 kmclipm_image_save(combined_data_on, fn_flat, CPL_TYPE_FLOAT, 
00694                         NULL, save_mode, 0./0.);
00695                 kmclipm_image_save(combined_noise_on, fn_noise, CPL_TYPE_FLOAT, 
00696                         NULL, save_mode, 0./0.);
00697                 kmclipm_image_save(bad_pix_mask_flat, fn_badpix, CPL_TYPE_FLOAT,
00698                         NULL, save_mode, 0./0.);
00699                 /* Next saves will create extensions */
00700                 save_mode = CPL_IO_EXTEND;
00701 
00702             } else if (spec_found[sx] == CPL_ERROR_DATA_NOT_FOUND) {
00703                 /* All IFUs seem to be deativated */
00704                 cpl_msg_warning(__func__, "All IFUs deactivated") ;
00705                 cpl_error_reset();
00706 
00707                 /* Save immediate results, free memory */
00708                 cpl_image_save(NULL, fn_flat, CPL_TYPE_FLOAT, NULL, save_mode);
00709                 cpl_image_save(NULL, fn_noise, CPL_TYPE_FLOAT, NULL, save_mode);
00710                 cpl_image_save(NULL, fn_badpix, CPL_TYPE_FLOAT, NULL,save_mode);
00711                 /* Next saves will create extensions */
00712                 save_mode = CPL_IO_EXTEND;
00713 
00714                 stored_xcal[sx] = NULL ;
00715                 stored_ycal[sx] = NULL ;
00716             } else {
00717                 // another error occured
00718                 cpl_msg_error(__func__, "Unknown ERROR !") ;
00719                 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00720                 cpl_image_delete(combined_data_on); 
00721                 cpl_image_delete(combined_noise_on); 
00722                 cpl_image_delete(bad_pix_mask_flat); 
00723                 cpl_free(angles_array) ;
00724                 cpl_msg_indent_less() ;
00725                 cpl_msg_indent_less() ;
00726                 return -1 ;
00727             }
00728             cpl_image_delete(combined_data_on); 
00729             cpl_image_delete(combined_noise_on); 
00730             cpl_image_delete(bad_pix_mask_flat); 
00731 
00732             cpl_msg_indent_less() ;
00733         } // for i = 1; i <= next
00734         cpl_frameset_delete(angle_frameset); 
00735         cpl_msg_indent_less() ;
00736     } // for a = 0; a < nb_angles
00737 
00738     /* Clean OFF frames */
00739     for (i = 1; i <= next; i++) {
00740         /* Compute only one detector */
00741         if (reduce_det != 0 && i != reduce_det) continue ;
00742 
00743         cpl_image_delete(combined_data_off[i-1]) ;
00744         cpl_image_delete(combined_noise_off[i-1]) ;
00745         cpl_image_delete(bad_pix_mask_dark[i-1]);
00746     }
00747 
00748     /* ----- QC parameters & saving */
00749     /* ---- load, update & save primary header */
00750     main_header = kmo_dfs_load_primary_header(frameset, FLAT_ON);
00751 
00752     /* Update which IFUs are not used */
00753     kmo_print_unused_ifus(unused_ifus_after, TRUE);
00754     kmo_set_unused_ifus(unused_ifus_after, main_header, "kmos_flat");
00755     kmo_free_unused_ifus(unused_ifus_after);
00756 
00757     /* xcal gets additionally the boundaries of the IFUs for reconstruction */
00758     main_header_xcal=kmos_create_bounds_properties(stored_ycal,next,nb_angles) ;
00759 
00760     /* --------- saving headers  */
00761     if (!suppress_extension)    fn_suffix = cpl_sprintf("%s", suffix);
00762     else                        fn_suffix = cpl_sprintf("%s", "");
00763     cpl_free(suffix);
00764 
00765     cpl_msg_info(__func__, "Saving data...");
00766 
00767     frame = kmo_dfs_get_frame(frameset, FLAT_ON);
00768     kmo_dfs_save_main_header(frameset, MASTER_FLAT, fn_suffix, frame, 
00769             main_header, parlist, cpl_func);
00770     kmo_dfs_save_main_header(frameset, XCAL, fn_suffix, frame, 
00771             main_header_xcal, parlist, cpl_func);
00772     kmo_dfs_save_main_header(frameset, YCAL, fn_suffix, frame, 
00773             main_header, parlist, cpl_func);
00774     kmo_dfs_save_main_header(frameset, BADPIXEL_FLAT, fn_suffix, frame, 
00775             main_header, parlist, cpl_func);
00776     kmo_dfs_save_main_header(frameset, FLAT_EDGE, fn_suffix, frame, 
00777             main_header, parlist, cpl_func);
00778 
00779     cpl_propertylist_delete(main_header);
00780     cpl_propertylist_delete(main_header_xcal);
00781 
00782     /* -------- saving sub frames  */
00783     ext_index = 0 ;
00784     for (a = 0; a < nb_angles; a++) {
00785         /* Reduce only one angle */
00786         if (reduce_angle <= 360 && angles_array[a] != reduce_angle) continue ;
00787         
00788         for (i = 1; i <= next; i++) {
00789             /* Compute only one detector */
00790             if (reduce_det != 0 && i != reduce_det) continue ;
00791             
00792             sx = a * next + (i - 1);
00793             // load stored data again
00794             stored_flat[sx]=kmclipm_image_load(fn_flat, CPL_TYPE_FLOAT, 0, 
00795                     ext_index);
00796             cpl_error_reset() ;
00797             stored_noise[sx]=kmclipm_image_load(fn_noise, CPL_TYPE_FLOAT, 0, 
00798                     ext_index);
00799             cpl_error_reset() ;
00800             stored_badpix[sx]=kmclipm_image_load(fn_badpix,CPL_TYPE_FLOAT, 0,
00801                     ext_index);
00802             cpl_error_reset() ;
00803             ext_index ++ ;
00804             
00805             sub_header = kmo_dfs_load_sub_header(frameset, FLAT_ON, i, FALSE);
00806             kmclipm_update_property_double(sub_header,CAL_ROTANGLE, 
00807                     ((double) angles_array[a]), 
00808                     "[deg] Rotator relative to nasmyth");
00809             
00810             if (spec_found[sx] == CPL_ERROR_NONE) {
00811                 kmclipm_update_property_int(sub_header, QC_FLAT_SAT, 
00812                         stored_qc_flat_sat[sx],
00813                         "[] nr. saturated pixels of master flat");
00814                 /* Load gain */
00815                 gain = kmo_dfs_get_property_double(sub_header, GAIN);
00816                 kmclipm_update_property_double(sub_header, QC_FLAT_EFF,
00817                         stored_qc_flat_eff[sx]/gain,
00818                         "[e-/s] rel. brightness of flat lamp");
00819              
00820                 kmclipm_update_property_double(sub_header, QC_FLAT_SN, 
00821                         stored_qc_flat_sn[sx], "[] S/N of master flat");
00822             }
00823 
00824             /* Store qc parameters only if any slitlet- and gap-width  */
00825             /* has been detected (should be the case when at least */
00826             /* one IFU is active) */
00827             if (stored_xcal[sx] != NULL) {
00828                 kmclipm_update_property_double(sub_header, QC_GAP_MEAN, 
00829                         stored_gapmean[sx],
00830                         "[pix] mean gap width between slitlets");
00831                 kmclipm_update_property_double(sub_header, QC_GAP_SDV, 
00832                         stored_gapsdv[sx],
00833                         "[pix] stdev of gap width between slitlets");
00834                 kmclipm_update_property_double(sub_header, QC_GAP_MAXDEV, 
00835                         stored_gapmaxdev[sx],
00836                         "[pix] max gap deviation between slitlets");
00837                 kmclipm_update_property_double(sub_header, QC_SLIT_MEAN, 
00838                         stored_slitmean[sx], "[pix] mean slitlet width");
00839                 kmclipm_update_property_double(sub_header, QC_SLIT_SDV, 
00840                         stored_slitsdv[sx], "[pix] stdev of slitlet widths");
00841                 kmclipm_update_property_double(sub_header, QC_SLIT_MAXDEV, 
00842                         stored_slitmaxdev[sx],
00843                         "[pix] max slitlet width deviation");
00844             }
00845         
00846             /* Calculate QC.BADPIX.NCOUNT */
00847             /* Remove 4pixel-border as bad pixels */
00848             nr_bad_pix = 0 ;
00849             if (stored_badpix[sx] != NULL) {
00850                 nr_bad_pix = cpl_image_count_rejected(stored_badpix[sx]);
00851                 nr_bad_pix -= 2*KMOS_BADPIX_BORDER*(nx-2*KMOS_BADPIX_BORDER) +
00852                     2*KMOS_BADPIX_BORDER*ny;
00853             }
00854 
00855             kmclipm_update_property_int(sub_header, QC_NR_BAD_PIX, nr_bad_pix, 
00856                     "[] nr. of bad pixels");
00857 
00858             /* Save flat frame */
00859             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00860             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00861                     "FITS extension name");
00862             cpl_free(extname);
00863 
00864             kmclipm_update_property_int(sub_header, EXTVER, sx+1, 
00865                     "FITS extension ver");
00866 
00867             kmo_dfs_save_image(stored_flat[sx], MASTER_FLAT, fn_suffix, 
00868                     sub_header, 0./0.);
00869 
00870             /* Save noise frame when enough input frames were available */
00871             extname = kmo_extname_creator(detector_frame, i, EXT_NOISE);
00872             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00873                     "FITS extension name");
00874             cpl_free(extname);
00875 
00876             kmo_dfs_save_image(stored_noise[sx], MASTER_FLAT, fn_suffix, 
00877                     sub_header, 0./0.);
00878 
00879             /* Save bad_pix frame */
00880             extname = kmo_extname_creator(detector_frame, i, EXT_BADPIX);
00881             kmclipm_update_property_string(sub_header, EXTNAME,extname,
00882                     "FITS extension name");
00883             cpl_free(extname);
00884 
00885             kmo_dfs_save_image(stored_badpix[sx], BADPIXEL_FLAT, fn_suffix, 
00886                     sub_header, 0.);
00887 
00888             // save xcal and ycal-frame
00889             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00890             kmclipm_update_property_string(sub_header, EXTNAME, extname,
00891                     "FITS extension name");
00892             cpl_free(extname); 
00893 
00894             kmo_dfs_save_image(stored_xcal[sx], XCAL, fn_suffix, sub_header, 
00895                     0./0.);
00896             kmo_dfs_save_image(stored_ycal[sx], YCAL, fn_suffix, sub_header, 
00897                     0./0.);
00898 
00899             /* Save edge_pars-frame */
00900             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00901                 extname = cpl_sprintf("%s_IFU.%d_ANGLE.%d", EXT_LIST, 
00902                         j+1+(i-1)*KMOS_IFUS_PER_DETECTOR, angles_array[a]);
00903                 kmclipm_update_property_string(sub_header, EXTNAME, extname,
00904                         "FITS extension name");
00905                 cpl_free(extname);
00906 
00907                 kmclipm_update_property_int(sub_header, CAL_IFU_NR, 
00908                         j+1+(i-1)*KMOS_IFUS_PER_DETECTOR, "IFU Number {1..24}");
00909 
00910                 /* Save edge-parameters as product */
00911                 if ((spec_found[sx] != CPL_ERROR_DATA_NOT_FOUND) && 
00912                         (edge_table[sx] != NULL)&&(edge_table[sx][j] != NULL)) {
00913                     kmo_dfs_save_table(edge_table[sx][j], FLAT_EDGE, 
00914                             fn_suffix, sub_header);
00915                 } else {
00916                     cpl_propertylist_erase(sub_header, CRVAL1);
00917                     cpl_propertylist_erase(sub_header, CRVAL2);
00918                     cpl_propertylist_erase(sub_header, CD1_1);
00919                     cpl_propertylist_erase(sub_header, CD1_2);
00920                     cpl_propertylist_erase(sub_header, CD2_1);
00921                     cpl_propertylist_erase(sub_header, CD2_2);
00922                     cpl_propertylist_erase(sub_header, CRPIX1);
00923                     cpl_propertylist_erase(sub_header, CRPIX2);
00924                     cpl_propertylist_erase(sub_header, CTYPE1);
00925                     cpl_propertylist_erase(sub_header, CTYPE2);
00926 
00927                     kmo_dfs_save_table(NULL, FLAT_EDGE, fn_suffix, 
00928                             sub_header);
00929                 }
00930             }
00931             cpl_propertylist_delete(sub_header);
00932 
00933             cpl_image_delete(stored_flat[sx]); 
00934             cpl_image_delete(stored_noise[sx]);
00935             cpl_image_delete(stored_badpix[sx]);
00936         } // for (i = next)
00937     } // for (a = nb_angles)
00938     
00939     // delete temporary files
00940     unlink(fn_flat);
00941     unlink(fn_noise);
00942     unlink(fn_badpix);
00943 
00944     cpl_free(stored_qc_flat_sat);
00945     cpl_free(stored_qc_flat_eff);
00946     cpl_free(stored_qc_flat_sn);
00947     cpl_free(stored_gapmean);
00948     cpl_free(stored_gapsdv);
00949     cpl_free(stored_gapmaxdev);
00950     cpl_free(stored_slitmean);
00951     cpl_free(stored_slitsdv);
00952     cpl_free(stored_slitmaxdev);
00953     cpl_free(fn_suffix);
00954     cpl_free(stored_flat);
00955     cpl_free(stored_noise);
00956     cpl_free(stored_badpix);
00957     for (i = 0; i < next * nb_angles; i++) {
00958         cpl_image_delete(stored_xcal[i]);
00959         cpl_image_delete(stored_ycal[i]);
00960     }
00961     cpl_free(stored_xcal);
00962     cpl_free(stored_ycal);
00963     if (edge_table != NULL) {
00964         for (i = 0; i < next * nb_angles; i++) {
00965             if (edge_table[i] != NULL) {
00966                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00967                     cpl_table_delete(edge_table[i][j]);
00968                 }
00969                 cpl_free(edge_table[i]); 
00970             }
00971         }
00972         cpl_free(edge_table);
00973     }
00974     cpl_free(spec_found);
00975     cpl_free(angles_array) ;
00976 
00977     return 0;
00978 }
00979 
00982 /*----------------------------------------------------------------------------*/
00992 /*----------------------------------------------------------------------------*/
00993 static cpl_propertylist * kmos_create_bounds_properties(
00994         cpl_image           **  stored_ycal,
00995         int                     next,
00996         int                     nb_angles)
00997 {
00998     cpl_propertylist    *   bounds_props ;
00999     int                 *   bounds ;
01000     int                 **  total_bounds ;
01001     char                *   tmpstr ;
01002     int                     a, i, j, sx ;
01003 
01004     /* Check Entries */
01005     if (stored_ycal == NULL) return NULL ;
01006 
01007     /* Add here boundaries for reconstruction */
01008     bounds_props = cpl_propertylist_new();
01009 
01010     /* Initialize total_bounds */
01011     total_bounds = (int**)cpl_malloc(next*sizeof(int*));
01012     for (i = 0; i < next; i++) {
01013         total_bounds[i]=(int*)cpl_calloc(2*KMOS_IFUS_PER_DETECTOR,sizeof(int));
01014         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01015             total_bounds[i][2*j] = 2048;
01016             total_bounds[i][2*j+1] = 0;
01017         }
01018     }
01019 
01020     /* Store the min left bound and max right bound for all angles */
01021     for (a = 0; a < nb_angles; a++) {
01022         for (i = 0; i < next; i++) {
01023             sx = a * next + i;
01024             if (stored_ycal[sx] != NULL) {
01025                 bounds = kmo_split_frame(stored_ycal[sx]);
01026 
01027                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01028                     if ((total_bounds[i][2*j] == -1)||(bounds[2*j] == -1)) {
01029                         total_bounds[i][2*j] = -1;
01030                     } else {
01031                         if (total_bounds[i][2*j] > bounds[2*j]) {
01032                             total_bounds[i][2*j] = bounds[2*j];
01033                         }
01034                     }
01035 
01036                     if ((total_bounds[i][2*j+1] == -1) || 
01037                             (bounds[2*j+1] == -1)) {
01038                         total_bounds[i][2*j+1] = -1;
01039                     } else {
01040                         if (total_bounds[i][2*j+1] < bounds[2*j+1]) {
01041                             total_bounds[i][2*j+1] = bounds[2*j+1];
01042                         }
01043                     }
01044                 }
01045                 cpl_free(bounds);
01046             } else {
01047                 // whole detector inactive
01048                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01049                     total_bounds[i][2*j] = -1;
01050                     total_bounds[i][2*j+1] = -1;
01051                 }
01052             }
01053         } // for (next)
01054     } // for (nb_angles)
01055 
01056     /* Write the min left bound and max right bound for all angles */
01057     /* into the main header */
01058     for (i = 0; i < next; i++) {
01059         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01060             if (total_bounds[i][2*j] > -1) {
01061                 tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
01062                         i*KMOS_IFUS_PER_DETECTOR + j+1, "_L");
01063                 kmclipm_update_property_int(bounds_props, tmpstr, 
01064                         total_bounds[i][2*j],
01065                         "[pix] left boundary for reconstr.");
01066                 cpl_free(tmpstr);
01067             }
01068 
01069             if (total_bounds[i][2*j+1] > -1) {
01070                 tmpstr= cpl_sprintf("%s%d%s", BOUNDS_PREFIX, 
01071                         i*KMOS_IFUS_PER_DETECTOR + j+1, "_R");
01072                 kmclipm_update_property_int(bounds_props,tmpstr, 
01073                         total_bounds[i][2*j+1],
01074                         "[pix] right boundary for reconstr.");
01075                 cpl_free(tmpstr);
01076             }
01077         }
01078     } // for (next)
01079     for (i = 0; i < next; i++) cpl_free(total_bounds[i]);
01080     cpl_free(total_bounds);
01081 
01082     return bounds_props ;
01083 }
01084 
01085 /*----------------------------------------------------------------------------*/
01095 /*----------------------------------------------------------------------------*/
01096 static int kmos_flat_check_inputs(
01097         cpl_frameset        *   frameset,
01098         int                 *   nx,
01099         int                 *   ny,
01100         int                 *   next,
01101         double              *   exptime_on)
01102 {
01103     cpl_frame           *   frame ;
01104     cpl_propertylist    *   eh ;
01105     cpl_propertylist    *   mh1 ;
01106     cpl_propertylist    *   main_header ;
01107     int                     ndit ;
01108     double                  exptime ;
01109     const char          *   readmode ;
01110     int                     naxis1, naxis2, n_ext ;
01111 
01112     /* TODO Add frames dimensions checks TODO */
01113 
01114     /* Check Entries */
01115     if (nx == NULL || ny == NULL || frameset == NULL || exptime_on == NULL) 
01116         return -1;
01117 
01118     /* check BADPIXEL_DARK */
01119     frame = kmo_dfs_get_frame(frameset, BADPIXEL_DARK);
01120     if (frame == NULL) {
01121         cpl_msg_warning(__func__, "BADPIXEL_DARK frame is missing") ;
01122         return 0 ;
01123     }
01124     n_ext = cpl_frame_get_nextensions(frame);
01125     if (n_ext != KMOS_NR_DETECTORS) {
01126         cpl_msg_warning(__func__, "BADPIXEL_DARK must have 3 extensions") ;
01127         return 0 ;
01128     }
01129     eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
01130     naxis1 = kmos_pfits_get_naxis1(eh) ;
01131     naxis2 = kmos_pfits_get_naxis2(eh) ;
01132     cpl_propertylist_delete(eh) ;
01133 
01134     /* check FLAT_OFF */
01135     frame = kmo_dfs_get_frame(frameset, FLAT_OFF);
01136     mh1 = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01137     ndit = cpl_propertylist_get_int(mh1, NDIT);
01138     exptime = cpl_propertylist_get_double(mh1, EXPTIME);
01139     readmode = cpl_propertylist_get_string(mh1, READMODE);
01140 
01141     /* Loop through FLAT_OFF frames */
01142     while (frame != NULL) {
01143         main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01144 
01145         if (cpl_propertylist_get_int(main_header, NDIT) != ndit) {
01146             cpl_msg_warning(__func__, "NDIT inconsistent") ;
01147             cpl_propertylist_delete(mh1);
01148             cpl_propertylist_delete(main_header);
01149             return 0 ;
01150         }
01151         if (cpl_propertylist_get_double(main_header, EXPTIME) != exptime) {
01152             cpl_msg_warning(__func__, "EXPTIME inconsistent") ;
01153             cpl_propertylist_delete(mh1);
01154             cpl_propertylist_delete(main_header);
01155             return 0 ;
01156         }
01157         if (strcmp(cpl_propertylist_get_string(main_header, READMODE), 
01158                     readmode) != 0) {
01159             cpl_msg_warning(__func__, "READMODE inconsistent") ;
01160             cpl_propertylist_delete(mh1);
01161             cpl_propertylist_delete(main_header);
01162             return 0 ;
01163         }
01164 
01165         /* Assure that arc lamps are off */
01166         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) != FALSE)
01167                 || (kmo_check_lamp(main_header, INS_LAMP2_ST) != FALSE)) {
01168             cpl_msg_warning(__func__, "Arc lamps must be switched off") ;
01169             cpl_propertylist_delete(mh1);
01170             cpl_propertylist_delete(main_header);
01171             return 0 ;
01172         }
01173         cpl_propertylist_delete(main_header);
01174         
01175         /* Get next FLAT_OFF frame */
01176         frame = kmo_dfs_get_frame(frameset, NULL);
01177     }
01178 
01179     /* Loop through FLAT_ON frames */
01180     frame = kmo_dfs_get_frame(frameset, FLAT_ON);
01181     while (frame != NULL) {
01182         main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01183 
01184         if (cpl_propertylist_get_int(main_header, NDIT) != ndit) {
01185             cpl_msg_warning(__func__, "NDIT inconsistent") ;
01186             cpl_propertylist_delete(mh1);
01187             cpl_propertylist_delete(main_header);
01188             return 0 ;
01189         }
01190         if (cpl_propertylist_get_double(main_header, EXPTIME) != exptime) {
01191             cpl_msg_warning(__func__, "EXPTIME inconsistent") ;
01192             cpl_propertylist_delete(mh1);
01193             cpl_propertylist_delete(main_header);
01194             return 0 ;
01195         }
01196         if (strcmp(cpl_propertylist_get_string(main_header, READMODE), 
01197                     readmode) != 0) {
01198             cpl_msg_warning(__func__, "READMODE inconsistent") ;
01199             cpl_propertylist_delete(mh1);
01200             cpl_propertylist_delete(main_header);
01201             return 0 ;
01202         }
01203 
01204         /* Assure that arc lamps are off */
01205         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) != FALSE)
01206                 || (kmo_check_lamp(main_header, INS_LAMP2_ST) != FALSE)) {
01207             cpl_msg_warning(__func__, "Arc lamps must be switched off") ;
01208             cpl_propertylist_delete(mh1);
01209             cpl_propertylist_delete(main_header);
01210             return 0 ;
01211         }
01212  
01213         /* Assure that at least one flat lamp is on */
01214         if ((kmo_check_lamp(main_header, INS_LAMP3_ST) != TRUE)
01215                 && (kmo_check_lamp(main_header, INS_LAMP4_ST) != TRUE)) {
01216             cpl_msg_warning(__func__, "At least one flat lamps must be on") ;
01217             cpl_propertylist_delete(mh1);
01218             cpl_propertylist_delete(main_header);
01219             return 0 ;
01220         }
01221  
01222         /* Get next FLAT_ON frame */
01223         frame = kmo_dfs_get_frame(frameset, NULL);
01224 
01225         cpl_propertylist_delete(main_header);
01226     }
01227     cpl_msg_info(__func__, "EXPTIME:  %g seconds", exptime);
01228     cpl_msg_info(__func__, "NDIT: %d", ndit);
01229     cpl_msg_info(__func__, "Detector readout mode: %s", readmode);
01230     cpl_propertylist_delete(mh1);
01231 
01232     /* Check Filters consistency */
01233     if (kmo_check_frameset_setup(frameset, FLAT_ON, TRUE, FALSE, FALSE) != 
01234             CPL_ERROR_NONE) {
01235         cpl_msg_warning(__func__, "Filters are not consistent") ;
01236         return 0 ;
01237     }
01238 
01239     /* Return */
01240     *nx = naxis1 ;
01241     *ny = naxis2 ;
01242     *next = n_ext ;
01243     *exptime_on = exptime ;
01244     return 1 ;
01245 }
01246