KMOS Pipeline Reference Manual  1.3.4
kmos_illumination.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 <math.h>
00029 #include <string.h>
00030 
00031 #include <cpl.h>
00032 
00033 #include "kmclipm_priv_splines.h"
00034 
00035 #include "kmo_priv_reconstruct.h"
00036 #include "kmo_priv_functions.h"
00037 #include "kmo_priv_flat.h"
00038 #include "kmo_priv_wave_cal.h"
00039 #include "kmo_priv_combine.h"
00040 #include "kmo_functions.h"
00041 #include "kmo_cpl_extensions.h"
00042 #include "kmo_dfs.h"
00043 #include "kmos_pfits.h"
00044 #include "kmo_error.h"
00045 #include "kmo_constants.h"
00046 #include "kmo_debug.h"
00047 
00048 /*-----------------------------------------------------------------------------
00049  *                          Functions prototypes
00050  *----------------------------------------------------------------------------*/
00051 
00052 static int kmos_illumination_check_inputs(cpl_frameset *, int, int *, int *, 
00053         int *, int *, int *, double *) ;
00054 static cpl_table ** kmos_illumination_edge_shift_correct(cpl_image *,
00055         cpl_image *, int, const cpl_image *, int, cpl_array *,
00056         const char *, double) ;
00057 
00058 static int kmos_illumination_create(cpl_plugin *);
00059 static int kmos_illumination_exec(cpl_plugin *);
00060 static int kmos_illumination_destroy(cpl_plugin *);
00061 static int kmos_illumination(cpl_parameterlist *, cpl_frameset *);
00062 
00063 /*-----------------------------------------------------------------------------
00064  *                          Static variables
00065  *----------------------------------------------------------------------------*/
00066 
00067 static char kmos_illumination_description[] =
00068 "This recipe creates the spatial non-uniformity calibration frame needed for\n"
00069 "all three detectors. It must be called after the kmo_wave_cal-recipe, which\n"
00070 "generates the spectral calibration frame needed in this recipe. As input at\n"
00071 "least a sky, a master dark, a master flat and the spatial and spectral cali-\n"
00072 "bration frames are required.\n"
00073 "The created product, the illumination correction, can be used as input for\n"
00074 "kmo_std_star and kmo_sci_red.\n"
00075 "\n"
00076 "BASIC PARAMETERS:\n"
00077 "-----------------\n"
00078 "--imethod\n"
00079 "The interpolation method used for reconstruction.\n"
00080 "\n"
00081 "--range\n"
00082 "The spectral ranges to combine when collapsing the reconstructed cubes. e.g.\n"
00083 "\"x1_start,x1_end;x2_start,x2_end\" (microns)\n"
00084 "\n"
00085 "ADVANCED PARAMETERS\n"
00086 "-------------------\n"
00087 "--flux\n"
00088 "Specify if flux conservation should be applied.\n"
00089 "\n"
00090 "--add-all\n"
00091 "By default the first FLAT_SKY frame is omitted, since in the\n"
00092 "KMOS_spec_cal_skyflat template this is an acquisition frame to estimate the\n"
00093 "needed exposure time for the subsequent FLAT_SKY frames. If anyway all\n"
00094 "frames should be considered, set this parameter to TRUE.\n"
00095 "\n"
00096 "--neighborhoodRange\n"
00097 "Defines the range to search for neighbors during reconstruction\n"
00098 "\n"
00099 "--b_samples\n"
00100 "The number of samples in spectral direction for the reconstructed cube.\n"
00101 "Ideally this number should be greater than 2048, the detector size.\n"
00102 "\n"
00103 "--b_start\n"
00104 "--b_end\n"
00105 "Used to define manually the start and end wavelength for the reconstructed\n"
00106 "cube. By default the internally defined values are used.\n"
00107 "\n"
00108 "--cmethod\n"
00109 "Following methods of frame combination are available:\n"
00110 "   * 'ksigma' (Default)\n"
00111 "   An iterative sigma clipping. For each position all pixels in the spectrum\n"
00112 "   are examined. If they deviate significantly, they will be rejected\n"
00113 "   according to the conditions:\n"
00114 "       val > mean + stdev * cpos_rej\n"
00115 "   and\n"
00116 "       val < mean - stdev * cneg_rej\n"
00117 "   where --cpos_rej, --cneg_rej and --citer are the corresponding\n"
00118 "   configuration parameters. In the first iteration median and percentile\n"
00119 "   level are used.\n"
00120 "\n"
00121 "   * 'median'\n"
00122 "   At each pixel position the median is calculated.\n"
00123 "\n"
00124 "   * 'average'\n"
00125 "   At each pixel position the average is calculated.\n"
00126 "\n"
00127 "   * 'sum'\n"
00128 "   At each pixel position the sum is calculated.\n"
00129 "\n"
00130 "   * 'min_max'\n"
00131 "   The specified nb of minimum and maximum pixel values will be rejected.\n"
00132 "   --cmax and --cmin apply to this method.\n"
00133 "\n"
00134 "--cpos_rej\n"
00135 "--cneg_rej\n"
00136 "--citer\n"
00137 "see --cmethod='ksigma'\n"
00138 "\n"
00139 "--cmax\n"
00140 "--cmin\n"
00141 "see --cmethod='min_max'\n"
00142 "\n"
00143 "--pix_scale\n"
00144 "Change the pixel scale [arcsec]. Default of 0.2\" results into cubes of\n"
00145 "14x14pix, a scale of 0.1\" results into cubes of 28x28pix, etc.\n"
00146 "\n"
00147 "--suppress_extension\n"
00148 "If set to TRUE, the arbitrary filename extensions are supressed. If multiple\n"
00149 "products with the same category are produced, they will be numbered\n"
00150 "consecutively starting from 0.\n"
00151 "\n"
00152 "---------------------------------------------------------------------------\n"
00153 "  Input files:\n"
00154 "\n"
00155 "   DO CATG           Type   Explanation                    Required #Frames\n"
00156 "   --------          -----  -----------                    -------- -------\n"
00157 "   FLAT_SKY           F2D   Sky exposures                     Y      1-n   \n"
00158 "                            (at least 3 frames recommended)                \n"
00159 "   MASTER_DARK        F2D   Master dark                       Y       1    \n"
00160 "   MASTER_FLAT        F2D   Master flat                       Y       1    \n"
00161 "   XCAL               F2D   x calibration frame               Y       1    \n"
00162 "   YCAL               F2D   y calibration frame               Y       1    \n"
00163 "   LCAL               F2D   Wavelength calib. frame           Y       1    \n"
00164 "   WAVE_BAND          F2L   Table with start-/end-wavelengths Y       1    \n"
00165 "   FLAT_EDGE          F2L   Table with fitted slitlet edges   N      0,1   \n"
00166 "\n"
00167 "  Output files:\n"
00168 "\n"
00169 "   DO CATG           Type   Explanation\n"
00170 "   --------          -----  -----------\n"
00171 "   ILLUM_CORR        F2I    Illumination calibration frame   \n"
00172 "   If FLAT_EDGE is provided: \n"
00173 "   SKYFLAT_EDGE      F2L    Frame containing parameters of fitted \n"
00174 "                            slitlets of all IFUs of all detectors\n"
00175 "---------------------------------------------------------------------------\n"
00176 "\n";
00177 
00178 /*-----------------------------------------------------------------------------
00179  *                              Functions code
00180  *----------------------------------------------------------------------------*/
00181 
00182 /*----------------------------------------------------------------------------*/
00187 /*----------------------------------------------------------------------------*/
00188 
00191 /*----------------------------------------------------------------------------*/
00200 /*----------------------------------------------------------------------------*/
00201 int cpl_plugin_get_info(cpl_pluginlist *list)
00202 {
00203     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00204     cpl_plugin *plugin = &recipe->interface;
00205 
00206     cpl_plugin_init(plugin,
00207             CPL_PLUGIN_API,
00208             KMOS_BINARY_VERSION,
00209             CPL_PLUGIN_TYPE_RECIPE,
00210             "kmos_illumination",
00211             "Create a frame to correct spatial non-uniformity of flatfield",
00212             kmos_illumination_description,
00213             "Alex Agudo Berbel, Yves Jung",
00214             "usd-help@eso.org",
00215             kmos_get_license(),
00216             kmos_illumination_create,
00217             kmos_illumination_exec,
00218             kmos_illumination_destroy);
00219     cpl_pluginlist_append(list, plugin);
00220 
00221     return 0;
00222 }
00223 
00224 /*----------------------------------------------------------------------------*/
00232 /*----------------------------------------------------------------------------*/
00233 static int kmos_illumination_create(cpl_plugin *plugin)
00234 {
00235     cpl_recipe *recipe;
00236     cpl_parameter *p;
00237 
00238     /* Check that the plugin is part of a valid recipe */
00239     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00240         recipe = (cpl_recipe *)plugin;
00241     else
00242         return -1;
00243 
00244     /* Create the parameters list in the cpl_recipe object */
00245     recipe->parameters = cpl_parameterlist_new();
00246 
00247     /* Fill the parameters list */
00248     /* --imethod */
00249     p = cpl_parameter_new_value("kmos.kmos_illumination.imethod",
00250             CPL_TYPE_STRING,
00251             "Method to use for interpolation: "
00252             "[\"NN\" (nearest neighbour), "
00253             "\"lwNN\" (linear weighted nearest neighbor), "
00254             "\"swNN\" (square weighted nearest neighbor), "
00255             "\"MS\" (Modified Shepard's method), "
00256             "\"CS\" (Cubic spline)]",
00257             "kmos.kmos_illumination", "CS");
00258     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "imethod");
00259     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00260     cpl_parameterlist_append(recipe->parameters, p);
00261 
00262     /* --neighborhoodRange */
00263     p = cpl_parameter_new_value("kmos.kmos_illumination.neighborhoodRange",
00264             CPL_TYPE_DOUBLE, "Range (pixels) to search for neighbors",
00265             "kmos.kmos_illumination", 1.001);
00266     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "neighborhoodRange");
00267     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00268     cpl_parameterlist_append(recipe->parameters, p);
00269 
00270     /* --range */
00271     p = cpl_parameter_new_value("kmos.kmos_illumination.range",
00272             CPL_TYPE_STRING,
00273             "The spectral ranges to combine when collapsing the reconstructed cubes. e.g. " "\"x1_start,x1_end;x2_start,x2_end\" (microns)",
00274             "kmos.kmos_illumination", "");
00275     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "range");
00276     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00277     cpl_parameterlist_append(recipe->parameters, p);
00278 
00279     /* --flux */
00280     p = cpl_parameter_new_value("kmos.kmos_illumination.flux",
00281             CPL_TYPE_BOOL, "TRUE: Apply flux conservation. FALSE: otherwise",
00282             "kmos.kmos_illumination", FALSE);
00283     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
00284     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00285     cpl_parameterlist_append(recipe->parameters, p);
00286 
00287     /* --add-all */
00288     p = cpl_parameter_new_value("kmos.kmos_illumination.add-all", CPL_TYPE_BOOL,
00289             "FALSE: omit 1st FLAT_SKY frame (acquisition), "
00290             "TRUE: don't perform any checks, add them all",
00291             "kmos.kmos_illumination", FALSE);
00292     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "add-all");
00293     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00294     cpl_parameterlist_append(recipe->parameters, p);
00295 
00296     /* --pix_scale */
00297     p = cpl_parameter_new_value("kmos.kmos_illumination.pix_scale",
00298             CPL_TYPE_DOUBLE,
00299             "Change the pixel scale [arcsec]. "
00300             "Default of 0.2\" results into cubes of 14x14pix, "
00301             "a scale of 0.1\" results into cubes of 28x28pix, etc.",
00302             "kmos.kmos_illumination", KMOS_PIX_RESOLUTION);
00303     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "pix_scale");
00304     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00305     cpl_parameterlist_append(recipe->parameters, p);
00306 
00307     /* --suppress_extension */
00308     p = cpl_parameter_new_value("kmos.kmos_illumination.suppress_extension",
00309             CPL_TYPE_BOOL,
00310             "Suppress filename extension. (TRUE (apply) or FALSE (don't apply)",
00311             "kmos.kmos_illumination", FALSE);
00312     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
00313     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00314     cpl_parameterlist_append(recipe->parameters, p);
00315 
00316     /* Add parameters for band-definition */
00317     kmos_band_pars_create(recipe->parameters, "kmos.kmos_illumination");
00318 
00319     /* Add parameters for combining */
00320     kmos_combine_pars_create(recipe->parameters, "kmos.kmos_illumination",
00321             DEF_REJ_METHOD, FALSE);
00322 
00323     /* --detector */
00324     p = cpl_parameter_new_value("kmos.kmos_illumination.detector",
00325             CPL_TYPE_INT, "Only reduce the specified detector",
00326             "kmos.kmos_illumination", 0);
00327     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "det");
00328     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00329     cpl_parameterlist_append(recipe->parameters, p);
00330 
00331     return 0 ;
00332 }
00333 
00334 /*----------------------------------------------------------------------------*/
00340 /*----------------------------------------------------------------------------*/
00341 static int kmos_illumination_exec(cpl_plugin *plugin)
00342 {
00343     cpl_recipe  *recipe;
00344 
00345     /* Get the recipe out of the plugin */
00346     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00347         recipe = (cpl_recipe *)plugin;
00348     else return -1;
00349 
00350     return kmos_illumination(recipe->parameters, recipe->frames);
00351 }
00352 
00353 /*----------------------------------------------------------------------------*/
00359 /*----------------------------------------------------------------------------*/
00360 static int kmos_illumination_destroy(cpl_plugin *plugin)
00361 {
00362     cpl_recipe *recipe;
00363 
00364     /* Get the recipe out of the plugin */
00365     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00366         recipe = (cpl_recipe *)plugin;
00367     else return -1 ;
00368 
00369     cpl_parameterlist_delete(recipe->parameters);
00370     return 0 ;
00371 }
00372 
00373 /*----------------------------------------------------------------------------*/
00386 /*----------------------------------------------------------------------------*/
00387 static int kmos_illumination(
00388         cpl_parameterlist   *   parlist,
00389         cpl_frameset        *   frameset)
00390 {
00391     const cpl_parameter *   par ;
00392     const char          *   method ;
00393     const char          *   cmethod ;
00394     const char          *   ranges_txt ;
00395     const char          *   sval ;
00396     int                     ne, nx, ny, flux, add_all_sky, cmax, cmin, citer,
00397                             suppress_extension, reduce_det, dark_has_noise, 
00398                             flat_has_noise,
00399                             process_noise, cnt, has_flat_edge ;
00400     double                  neighborhoodRange, pix_scale, cpos_rej, cneg_rej, 
00401                             rotangle, rotangle_found ;
00402     char                *   suffix ;
00403     char                *   fn_lut ;
00404     char                *   fn_suffix ;
00405     char                *   extname ;
00406     cpl_array           **  unused_ifus ;
00407     const int           *   punused_ifus ;
00408     cpl_frame           *   frame ;
00409     cpl_frameset        *   frameset_sky ;
00410     char                *   filter ;
00411     char                *   keyword ;
00412     cpl_vector          *   ranges ;
00413     gridDefinition          gd;
00414     cpl_propertylist    *   main_header ;
00415     cpl_propertylist    *   tmp_header ;
00416     int                 *   bounds ;
00417     cpl_array           *   calTimestamp ;
00418     cpl_imagelist       **  stored_data_cubes ;
00419     cpl_imagelist       **  stored_noise_cubes ;
00420     cpl_image           **  stored_data_images ;
00421     cpl_image           **  stored_noise_images ;
00422     cpl_propertylist    **  stored_sub_data_headers ;
00423     cpl_propertylist    **  stored_sub_noise_headers ;
00424     cpl_table           *** edge_table_sky ;
00425     cpl_vector          *   calAngles ;
00426     cpl_imagelist       *   detector_in ;
00427     cpl_image           *   img_in ;
00428     cpl_image           *   combined_data ;
00429     cpl_image           *   combined_noise ;
00430     cpl_image           *   xcal ;
00431     cpl_image           *   ycal ;
00432     cpl_image           *   lcal ;
00433     cpl_image           *   bad_pix_mask ;
00434     float               *   pbad_pix_mask ;
00435     cpl_image           *   img_dark ;
00436     cpl_image           *   img_dark_noise ;
00437     cpl_image           *   img_flat ;
00438     cpl_image           *   img_flat_noise ;
00439     cpl_table           *   band_table ;
00440     cpl_propertylist    *   sub_header ;
00441     cpl_imagelist       *   cube_data ;
00442     cpl_imagelist       *   cube_noise ;
00443     cpl_image           *   data_ifu ;
00444     cpl_image           *   noise_ifu ;
00445     cpl_vector          *   identified_slices ;
00446     double                  ifu_crpix, ifu_crval, ifu_cdelt, mean_data,
00447                             qc_spat_unif, qc_max_dev, qc_max_nonunif,
00448                             tmp_stdev, tmp_mean ;
00449     int                     x, y, det_nr, i, j, ifu_nr, qc_max_dev_id,
00450                             qc_max_nonunif_id ;
00451     
00452     /* Check entries */
00453     if (parlist == NULL || frameset == NULL) {
00454         cpl_msg_error(__func__, "Null Inputs") ;
00455         cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
00456         return -1 ;
00457     }
00458     
00459     /* Get parameters */
00460     par = cpl_parameterlist_find_const(parlist,
00461             "kmos.kmos_illumination.imethod") ;
00462     method = cpl_parameter_get_string(par);
00463     par = cpl_parameterlist_find_const(parlist,
00464             "kmos.kmos_illumination.neighborhoodRange");
00465     neighborhoodRange = cpl_parameter_get_double(par) ;
00466     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_illumination.range");
00467     ranges_txt = cpl_parameter_get_string(par) ;
00468     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_illumination.flux");
00469     flux = cpl_parameter_get_bool(par);
00470     par = cpl_parameterlist_find_const(parlist,
00471             "kmos.kmos_illumination.add-all");
00472     add_all_sky = cpl_parameter_get_bool(par) ;
00473     par = cpl_parameterlist_find_const(parlist,
00474             "kmos.kmos_illumination.pix_scale");
00475     pix_scale = cpl_parameter_get_double(par) ;
00476     par = cpl_parameterlist_find_const(parlist,
00477             "kmos.kmos_illumination.suppress_extension");
00478     suppress_extension = cpl_parameter_get_bool(par) ;
00479     par = cpl_parameterlist_find_const(parlist, 
00480             "kmos.kmos_illumination.detector");
00481     reduce_det = cpl_parameter_get_int(par);
00482 
00483     kmos_band_pars_load(parlist, "kmos.kmos_illumination");
00484     kmos_combine_pars_load(parlist, "kmos.kmos_illumination", &cmethod, 
00485             &cpos_rej, &cneg_rej, &citer, &cmin, &cmax, FALSE);
00486 
00487     /* Check Parameters */
00488     if (strcmp(method, "NN") && strcmp(method, "lwNN") && strcmp(method, "swNN")
00489             && strcmp(method, "MS") && strcmp(method, "CS")) {
00490         cpl_msg_error(__func__,
00491                 "method must be \"NN\", \"lwNN\", \"swNN\", \"MS\" or \"CS\"") ;
00492         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00493         return -1 ;
00494     }
00495     if (neighborhoodRange <= 0.0) {
00496         cpl_msg_error(__func__, "neighborhoodRange must be > 0") ;
00497         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00498         return -1 ;
00499     }
00500     if (pix_scale < 0.01 || pix_scale > 0.4) {
00501         cpl_msg_error(__func__, 
00502                 "pix_scale must be in [0.01,0.4] -> 7x7 to 280x280 pixels") ;
00503         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00504         return -1 ;
00505     }
00506     if (reduce_det < 0 || reduce_det > 3) {
00507         cpl_msg_error(__func__, "detector must be in [1,3]") ;
00508         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00509         return -1 ;
00510     }
00511     if (cpl_frameset_count_tags(frameset, FLAT_SKY) == 1) {
00512         cpl_msg_warning(cpl_func, "1 input FLAT -> cmethod changed to average");
00513         cmethod = "average";
00514     }
00515 
00516     /* Identify the RAW and CALIB frames in the input frameset */
00517     if (kmo_dfs_set_groups(frameset, "kmos_illumination") != 1) {
00518         cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
00519         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00520         return -1 ;
00521     }
00522 
00523     /* Check the inputs consistency */
00524     if (kmos_illumination_check_inputs(frameset, add_all_sky, &dark_has_noise,
00525                 &flat_has_noise, &ne, &nx, &ny, &rotangle) != 1) {
00526         cpl_msg_error(__func__, "Input frameset is not consistent") ;
00527         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00528         return -1 ;
00529     }
00530 
00531     /* Instrument setup */
00532     suffix = kmo_dfs_get_suffix(kmo_dfs_get_frame(frameset, XCAL), TRUE, FALSE);
00533     cpl_msg_info(__func__, "Detected instrument setup:   %s", suffix+1);
00534 
00535     /* Check which IFUs are active for all frames */
00536     unused_ifus = kmo_get_unused_ifus(frameset, 0, 0);
00537     kmo_print_unused_ifus(unused_ifus, FALSE);
00538 
00539     has_flat_edge = cpl_frameset_count_tags(frameset, FLAT_EDGE);
00540     
00541     /* Decide here if noise is propagated */
00542     if (cpl_frameset_count_tags(frameset, FLAT_SKY) >= 2 && 
00543             dark_has_noise && flat_has_noise)   process_noise = 1 ;
00544     else                                        process_noise = 0 ;
00545 
00546     /* Load the FLAT_SKY frames in a frameset */
00547     frameset_sky = cpl_frameset_new();
00548     frame = kmo_dfs_get_frame(frameset, FLAT_SKY);
00549     if (add_all_sky) {
00550         cpl_msg_info(__func__, "Use all FLAT_SKY frames");
00551     } else {
00552         /* Omit the first frame */
00553         cpl_msg_info(__func__, "Use all FLAT_SKY frames but the first");
00554         frame = kmo_dfs_get_frame(frameset, NULL);
00555     }
00556     while (frame != NULL) {
00557         cpl_frameset_insert(frameset_sky, cpl_frame_duplicate(frame));
00558         frame = kmo_dfs_get_frame(frameset, NULL);
00559     }
00560 
00561     /* Load first file primary header */
00562     frame = kmo_dfs_get_frame(frameset_sky, FLAT_SKY);
00563     if (frame == NULL) {
00564         cpl_free(suffix) ;
00565         kmo_free_unused_ifus(unused_ifus);
00566         cpl_frameset_delete(frameset_sky) ;
00567         cpl_msg_error(__func__, "Missing FLAT_SKY in input") ;
00568         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00569         return -1 ;
00570     }
00571 
00572     main_header = kmo_dfs_load_primary_header(frameset_sky, FLAT_SKY);
00573 
00574     /* Set default band-specific ranges for collapsing */
00575     if (!strcmp(ranges_txt, "")) {
00576         keyword = cpl_sprintf("%s%d%s", IFU_GRATID_PREFIX,1,IFU_GRATID_POSTFIX);
00577         filter = cpl_sprintf("%s",
00578                 cpl_propertylist_get_string(main_header,keyword));
00579         cpl_free(keyword); 
00580         if (strcmp(filter, "IZ") == 0)      ranges_txt = "0.81,1.05";
00581         else if (strcmp(filter, "YJ") == 0) ranges_txt = "1.025,1.3";
00582         else if (strcmp(filter, "H") == 0)  ranges_txt = "1.5,1.7";
00583         else if (strcmp(filter, "K") == 0)  ranges_txt = "2.1,2.35";
00584         else if (strcmp(filter, "HK") == 0) ranges_txt = "1.5,1.7;2.1,2.35";
00585         else {
00586             cpl_msg_error(__func__, "Filter %s not supported", filter) ;
00587             cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00588             cpl_free(suffix) ;
00589             kmo_free_unused_ifus(unused_ifus);
00590             cpl_frameset_delete(frameset_sky) ;
00591             cpl_propertylist_delete(main_header);
00592             cpl_free(filter) ;
00593             return -1 ;
00594         }
00595         cpl_free(filter) ;
00596     }
00597     cpl_msg_info(__func__, "Spectral range to collapse: %s um", ranges_txt);
00598 
00599     /* Set grid definition, wl start/end points will be set in the loop */
00600     kmclipm_setup_grid(&gd, method, neighborhoodRange, pix_scale, 0.);
00601 
00602     // create filename for LUT
00603     fn_lut = cpl_sprintf("%s%s", "lut", suffix);
00604 
00605     // extract bounds
00606     tmp_header = kmo_dfs_load_primary_header(frameset, XCAL);
00607     bounds = kmclipm_extract_bounds(tmp_header);
00608     cpl_propertylist_delete(tmp_header);
00609 
00610     /* Get timestamps of xcal, ycal & lcal */
00611     calTimestamp = kmo_get_timestamps(
00612             kmo_dfs_get_frame(frameset, XCAL), 
00613             kmo_dfs_get_frame(frameset, YCAL), 
00614             kmo_dfs_get_frame(frameset, LCAL)) ;
00615 
00616     /* Create holders for reconstructed data, noise cubes and headers */
00617     stored_data_cubes = (cpl_imagelist**)cpl_calloc(ne*KMOS_IFUS_PER_DETECTOR, 
00618             sizeof(cpl_imagelist*));
00619     stored_noise_cubes = (cpl_imagelist**)cpl_calloc(ne*KMOS_IFUS_PER_DETECTOR,
00620             sizeof(cpl_imagelist*));
00621     stored_data_images = (cpl_image**)cpl_calloc(ne*KMOS_IFUS_PER_DETECTOR, 
00622             sizeof(cpl_image*));
00623     stored_noise_images = (cpl_image**)cpl_calloc(ne*KMOS_IFUS_PER_DETECTOR, 
00624             sizeof(cpl_image*));
00625     stored_sub_data_headers = (cpl_propertylist**)cpl_calloc(
00626             ne*KMOS_IFUS_PER_DETECTOR, sizeof(cpl_propertylist*));
00627     stored_sub_noise_headers = (cpl_propertylist**)cpl_calloc(
00628             ne*KMOS_IFUS_PER_DETECTOR, sizeof(cpl_propertylist*));
00629     if (has_flat_edge) edge_table_sky = (cpl_table***)cpl_calloc(
00630             KMOS_NR_DETECTORS, sizeof(cpl_table**));
00631     calAngles = cpl_vector_new(3);
00632 
00633     /* Loop through all detectors */
00634     for (det_nr = 1; det_nr <= ne; det_nr++) {
00635 
00636         /* Compute only one detector */
00637         if (reduce_det != 0 && det_nr != reduce_det) continue ;
00638 
00639         cpl_msg_info(__func__, "Processing detector No. %d", det_nr);
00640         cpl_msg_indent_more() ;
00641 
00642         detector_in = cpl_imagelist_new();
00643 
00644         /* Load all images of this detector */
00645         img_in = kmo_dfs_load_image(frameset_sky, FLAT_SKY, det_nr, FALSE, 
00646                 TRUE,NULL);
00647         cnt = 0;
00648         while (img_in != NULL) {
00649             cpl_imagelist_set(detector_in, img_in, cnt);
00650 
00651             /* load same extension of next FLAT_SKY frame*/
00652             img_in = kmo_dfs_load_image(frameset_sky, NULL, det_nr, FALSE, 
00653                     TRUE, NULL);
00654             cnt++;
00655         }
00656 
00657         /* Combine images (data only) and create noise (stdev of data) */
00658         cpl_msg_info(__func__, "Combining frames");
00659         if (process_noise) {
00660             kmos_combine_frames(detector_in, cmethod, cpos_rej, cneg_rej, 
00661                     citer, cmax, cmin, &combined_data, &combined_noise, -1.0);
00662         } else {
00663             kmos_combine_frames(detector_in, cmethod, cpos_rej, cneg_rej, 
00664                     citer, cmax, cmin, &combined_data, NULL, -1.0);
00665         }
00666         cpl_imagelist_delete(detector_in);
00667 
00668         /* Check if combination succesfull */
00669         if (cpl_error_get_code() != CPL_ERROR_NONE) {
00670             cpl_free(suffix) ;
00671             kmo_free_unused_ifus(unused_ifus);
00672             cpl_frameset_delete(frameset_sky) ;
00673             cpl_propertylist_delete(main_header);
00674             cpl_free(fn_lut) ;
00675             cpl_free(bounds) ;
00676             cpl_array_delete(calTimestamp);
00677             cpl_free(stored_data_cubes);
00678             cpl_free(stored_noise_cubes);
00679             cpl_free(stored_data_images);
00680             cpl_free(stored_noise_images);
00681             cpl_free(stored_sub_data_headers);
00682             cpl_free(stored_sub_noise_headers); 
00683             if (has_flat_edge) cpl_free(edge_table_sky) ;
00684             cpl_vector_delete(calAngles) ;
00685             cpl_msg_error(__func__, "Combination failed") ;
00686             cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00687             cpl_msg_indent_less() ;
00688             return -1 ;
00689         }
00690 
00691         /*
00692         cpl_image_save(combined_data, "combined_image.fits", 
00693                 CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE) ;
00694         */
00695 
00696         if (kmclipm_omit_warning_one_slice > 10) 
00697             kmclipm_omit_warning_one_slice = FALSE;
00698 
00699         /* Load calibration files */
00700         xcal = kmo_dfs_load_cal_image(frameset, XCAL, det_nr, FALSE, rotangle, 
00701                 FALSE, NULL, &rotangle_found, -1, 0, 0);
00702         cpl_vector_set(calAngles, 0, rotangle_found);
00703         ycal = kmo_dfs_load_cal_image(frameset, YCAL, det_nr, FALSE, rotangle, 
00704                 FALSE, NULL, &rotangle_found, -1, 0, 0);
00705         cpl_vector_set(calAngles, 1, rotangle_found);
00706         lcal = kmo_dfs_load_cal_image(frameset, LCAL, det_nr, FALSE, rotangle, 
00707                 FALSE, NULL, &rotangle_found, -1, 0, 0);
00708         cpl_vector_set(calAngles, 2, rotangle_found);
00709 
00710         /* Load bad pixel mask from XCAL and set NaNs to 0 other values to 1 */
00711         bad_pix_mask = cpl_image_duplicate(xcal);
00712         pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask);
00713         for (x = 0; x < nx; x++) {
00714             for (y = 0; y < ny; y++) {
00715                 if (isnan(pbad_pix_mask[x+nx*y])) {
00716                     pbad_pix_mask[x+nx*y] = 0.;
00717                 } else {
00718                     pbad_pix_mask[x+nx*y] = 1.;
00719                 }
00720             }
00721         }
00722 
00723         /* Compute SKYFLAT_EDGE */
00724         if (has_flat_edge) {
00725             edge_table_sky[det_nr-1] = kmos_illumination_edge_shift_correct(
00726                     combined_data, combined_noise, process_noise, bad_pix_mask,
00727                     det_nr, unused_ifus[det_nr-1],
00728                     cpl_frame_get_filename(kmo_dfs_get_frame(frameset, 
00729                             FLAT_EDGE)), rotangle);
00730             if (edge_table_sky[det_nr-1] == NULL) {
00731                 cpl_free(suffix) ;
00732                 kmo_free_unused_ifus(unused_ifus);
00733                 cpl_frameset_delete(frameset_sky) ;
00734                 cpl_propertylist_delete(main_header);
00735                 cpl_free(fn_lut) ;
00736                 cpl_free(bounds) ;
00737                 cpl_array_delete(calTimestamp);
00738                 cpl_free(stored_data_cubes);
00739                 cpl_free(stored_noise_cubes);
00740                 cpl_free(stored_data_images);
00741                 cpl_free(stored_noise_images);
00742                 cpl_free(stored_sub_data_headers);
00743                 cpl_free(stored_sub_noise_headers); 
00744                 if (has_flat_edge) cpl_free(edge_table_sky) ;
00745                 cpl_image_delete(bad_pix_mask); 
00746                 cpl_image_delete(combined_data); 
00747                 if (process_noise) {
00748                     cpl_image_delete(combined_noise);
00749                 }
00750                 cpl_image_delete(xcal);
00751                 cpl_image_delete(ycal);
00752                 cpl_image_delete(lcal);
00753                 cpl_vector_delete(calAngles) ;
00754                 cpl_msg_error(__func__, "Edge Shift Correction failed") ;
00755                 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00756                 cpl_msg_indent_less() ;
00757                 return -1 ;
00758             }
00759         }
00760 
00761         /* Reconstruct */
00762         /* Load MASTER_DARK and MASTER_FLAT */
00763         img_dark = kmo_dfs_load_image(frameset, MASTER_DARK, det_nr, FALSE, 
00764                 FALSE, NULL);
00765         if (process_noise) {
00766             img_dark_noise = kmo_dfs_load_image(frameset, MASTER_DARK, det_nr, 
00767                     TRUE, FALSE, NULL);
00768         }
00769         img_flat = kmo_dfs_load_cal_image(frameset, MASTER_FLAT, det_nr, FALSE,
00770                 rotangle, FALSE, NULL, &rotangle_found, -1, 0, 0);
00771         if (process_noise) {
00772             img_flat_noise = kmo_dfs_load_cal_image(frameset, MASTER_FLAT, 
00773                     det_nr, TRUE, rotangle, FALSE, NULL, &rotangle_found, -1, 
00774                     0, 0);
00775         }
00776 
00777         /* ESO INS FILTi ID */
00778         keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX, det_nr, 
00779                 IFU_FILTID_POSTFIX);
00780         band_table = kmo_dfs_load_table(frameset, WAVE_BAND, 1, 0);
00781         kmclipm_setup_grid_band_lcal(&gd,
00782                 cpl_propertylist_get_string(main_header, keyword), band_table);
00783         cpl_free(keyword); 
00784         cpl_table_delete(band_table);
00785 
00786         cpl_msg_info(__func__, "Reconstructing cubes");
00787         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00788             // update sub-header
00789             ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
00790 
00791             // load raw image and sub-header
00792             sub_header = kmo_dfs_load_sub_header(frameset_sky, FLAT_SKY, 
00793                     det_nr, FALSE);
00794 
00795             punused_ifus = cpl_array_get_data_int_const(unused_ifus[det_nr-1]);
00796 
00797             /* Check if IFU is valid  */
00798             keyword = cpl_sprintf("%s%d%s", IFU_VALID_PREFIX, ifu_nr, 
00799                     IFU_VALID_POSTFIX);
00800             sval = cpl_propertylist_get_string(main_header, keyword);
00801             cpl_free(keyword);
00802 
00803             if ((cpl_error_get_code() == CPL_ERROR_DATA_NOT_FOUND) &&
00804                 (bounds[2*(ifu_nr-1)] != -1) &&
00805                 (bounds[2*(ifu_nr-1)+1] != -1) && (punused_ifus[j] == 0)) {
00806                 // IFU is valid
00807                 cpl_error_reset();
00808 
00809                 // calculate WCS
00810                 kmo_calc_wcs_gd(main_header, sub_header, ifu_nr, gd);
00811 
00812                 // reconstruct data
00813                 kmo_reconstruct_sci_image(ifu_nr, bounds[2*(ifu_nr-1)],
00814                         bounds[2*(ifu_nr-1)+1], combined_data, combined_noise, 
00815                         img_dark, img_dark_noise, img_flat, img_flat_noise, 
00816                         xcal, ycal, lcal, &gd, calTimestamp, calAngles, 
00817                         fn_lut, &cube_data, &cube_noise, flux, 0, 
00818                         NULL, NULL, NULL);
00819             } else {
00820                 // IFU is invalid
00821                 cpl_error_reset();
00822             } 
00823 
00824             /* Save output */
00825             extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_DATA);
00826 
00827             kmclipm_update_property_string(sub_header, EXTNAME, extname, 
00828                     "FITS extension name");
00829             cpl_free(extname);
00830 
00831             // store cube and sub header into array for later
00832             stored_data_cubes[ifu_nr - 1] = cube_data;
00833             stored_sub_data_headers[ifu_nr - 1] = sub_header;
00834 
00835             if (process_noise) {
00836                 sub_header=cpl_propertylist_duplicate(
00837                         stored_sub_data_headers[ifu_nr - 1]);
00838                 extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_NOISE);
00839                 kmclipm_update_property_string(sub_header, EXTNAME, extname,
00840                         "FITS extension name");
00841                 cpl_free(extname); 
00842 
00843                 stored_noise_cubes[ifu_nr - 1] = cube_noise;
00844                 stored_sub_noise_headers[ifu_nr - 1] = sub_header;
00845             }
00846             cpl_image_delete(data_ifu); data_ifu = NULL;
00847             cpl_image_delete(noise_ifu); noise_ifu = NULL;
00848             cube_data = NULL;
00849             cube_noise = NULL;
00850         } 
00851 
00852         /* Free memory */
00853         cpl_image_delete(combined_data); 
00854         cpl_image_delete(xcal);
00855         cpl_image_delete(ycal);
00856         cpl_image_delete(lcal);
00857         cpl_image_delete(img_dark);
00858         cpl_image_delete(img_flat);
00859         cpl_image_delete(bad_pix_mask); 
00860         if (process_noise) {
00861             cpl_image_delete(combined_noise);
00862             cpl_image_delete(img_dark_noise); 
00863             cpl_image_delete(img_flat_noise);
00864         }
00865         cpl_msg_indent_less() ;
00866     } 
00867     cpl_vector_delete(calAngles) ;
00868     cpl_free(fn_lut) ;
00869     cpl_free(bounds);
00870     cpl_array_delete(calTimestamp);
00871 
00872     /*
00873 cpl_frameset_delete(frameset_sky) ;
00874 cpl_free(suffix) ;
00875 kmo_free_unused_ifus(unused_ifus);
00876 cpl_propertylist_delete(main_header);
00877 cpl_free(stored_data_cubes);
00878 cpl_free(stored_noise_cubes);
00879 cpl_free(stored_data_images);
00880 cpl_free(stored_noise_images);
00881 cpl_free(stored_sub_data_headers);
00882 cpl_free(stored_sub_noise_headers); 
00883 if (has_flat_edge) cpl_free(edge_table_sky) ;
00884 */
00885 
00886     ranges = kmo_identify_ranges(ranges_txt);
00887     
00888     /* Collapse cubes using rejection */
00889     cpl_msg_info(__func__, "Collapse cubes");
00890     for (det_nr = 1; det_nr <= ne; det_nr++) {
00891 
00892         /* Compute only one detector */
00893         if (reduce_det != 0 && det_nr != reduce_det) continue ;
00894 
00895         cpl_msg_info(__func__, "Processing detector No. %d", det_nr);
00896         cpl_msg_indent_more() ;
00897 
00898         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00899             ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
00900 
00901             punused_ifus=cpl_array_get_data_int_const(
00902                     unused_ifus[det_nr-1]);
00903             if (punused_ifus[j] == 0) {
00904                 if (stored_sub_data_headers[ifu_nr-1] != NULL) {
00905                     // IFU is valid
00906                     ifu_crpix = cpl_propertylist_get_double(
00907                             stored_sub_data_headers[ifu_nr-1], CRPIX3);
00908                     ifu_crval = cpl_propertylist_get_double(
00909                             stored_sub_data_headers[ifu_nr-1], CRVAL3);
00910                     ifu_cdelt = cpl_propertylist_get_double(
00911                             stored_sub_data_headers[ifu_nr-1], CDELT3);
00912                     identified_slices = kmo_identify_slices(ranges, ifu_crpix, 
00913                             ifu_crval, ifu_cdelt, gd.l.dim);
00914                 }
00915 
00916                 if (stored_data_cubes[ifu_nr-1] != NULL) {
00917                     kmclipm_make_image(stored_data_cubes[ifu_nr-1],
00918                             stored_noise_cubes[ifu_nr-1],
00919                             &stored_data_images[ifu_nr-1],
00920                             &stored_noise_images[ifu_nr-1], identified_slices,
00921                             cmethod, cpos_rej, cneg_rej, citer, cmax, cmin);
00922                 }
00923                 cpl_vector_delete(identified_slices);
00924             } else {
00925                 // IFU is invalid
00926             }
00927         }
00928         cpl_msg_indent_less() ;
00929     }
00930     cpl_vector_delete(ranges);
00931     for (i = 0; i < ne * KMOS_IFUS_PER_DETECTOR; i++) {
00932         if (stored_data_cubes != NULL) {
00933             cpl_imagelist_delete(stored_data_cubes[i]);
00934         }
00935         if (stored_noise_cubes != NULL) {
00936             cpl_imagelist_delete(stored_noise_cubes[i]);
00937         }
00938     }
00939     cpl_free(stored_data_cubes);
00940     cpl_free(stored_noise_cubes);
00941  
00942     // normalise all IFUs of a detector as a group.
00943     // Calculate mean of each IFU, add up and divide by number of successful
00944     // averaged IFUs.
00945     // Then divide all valid IFUs with mean value
00946     for (j = 0; j < ne; j++) {
00947 
00948         /* Compute only one detector */
00949         if (reduce_det != 0 && j+1 != reduce_det) continue ;
00950 
00951         cnt = 0;
00952         mean_data = 0;
00953         for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
00954             ifu_nr = j*KMOS_IFUS_PER_DETECTOR + i;
00955             if (stored_data_images[ifu_nr] != NULL) {
00956                 if (cpl_image_count_rejected(stored_data_images[ifu_nr]) >=
00957                         cpl_image_get_size_x(stored_data_images[ifu_nr])*
00958                         cpl_image_get_size_y(stored_data_images[ifu_nr])) {
00959                     /* TODO - Deallocate */
00960                     cpl_msg_error(__func__, 
00961                         "The collapsed, dark-subtracted image contains "
00962                         "only invalid values! Probably the provided "
00963                         "FLAT_SKY frames are exactly the same as the "
00964                         "frames used for MASTER_DARK calculation.");
00965                     cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00966                     return -1 ;
00967                 }
00968                 mean_data += cpl_image_get_mean(stored_data_images[ifu_nr]);
00969                 cnt++;
00970             }
00971         }
00972         mean_data /= cnt;
00973 
00974         if (mean_data != 0.0) {
00975             for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
00976                 ifu_nr = j*KMOS_IFUS_PER_DETECTOR + i;
00977                 if (stored_data_images[ifu_nr] != NULL) {
00978                     cpl_image_divide_scalar(stored_data_images[ifu_nr], 
00979                             mean_data);
00980                 }
00981             }
00982         } else {
00983             cpl_msg_warning(__func__, 
00984                     "Data cannot be normalised (mean=0.0)");
00985         }
00986 
00987         if (process_noise) {
00988             if (mean_data != 0.0) {
00989                 for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
00990                     ifu_nr = j*KMOS_IFUS_PER_DETECTOR + i;
00991                     if (stored_noise_images[ifu_nr] != NULL) {
00992                         cpl_image_divide_scalar(stored_noise_images[ifu_nr],
00993                                 mean_data);
00994                     }
00995                 }
00996             } else {
00997                 cpl_msg_warning(__func__, 
00998                         "Noise cannot be normalised (mean=0.0)");
00999             }
01000         }
01001     } 
01002 
01003     /* Compute qc parameters on normalised data */
01004     qc_spat_unif = 0.0;
01005     qc_max_nonunif_id = 0 ;
01006     qc_max_dev_id = 0 ;
01007     cnt = 0;
01008     for (i = 0; i < ne * KMOS_IFUS_PER_DETECTOR; i++) {
01009         if (stored_data_images[i] != NULL) {
01010             tmp_mean = cpl_image_get_mean(stored_data_images[i]);
01011             tmp_stdev = cpl_image_get_stdev (stored_data_images[i]);
01012 
01013             qc_spat_unif += pow(tmp_mean-1, 2);
01014             if (fabs(tmp_mean) > qc_max_dev) {
01015                 qc_max_dev = tmp_mean-1;
01016                 qc_max_dev_id = i+1;
01017             }
01018             if (fabs(tmp_stdev) > qc_max_nonunif) {
01019                 qc_max_nonunif = tmp_stdev;
01020                 qc_max_nonunif_id = i+1;
01021             }
01022             cnt++;
01023         }
01024     }
01025     qc_spat_unif = sqrt(qc_spat_unif / cnt);
01026 
01027     /* Udate which IFUs are not used */
01028     kmo_print_unused_ifus(unused_ifus, TRUE);
01029     kmo_set_unused_ifus(unused_ifus, main_header, "kmos_illumination");
01030     kmo_free_unused_ifus(unused_ifus);
01031     
01032     cpl_msg_info(__func__, "Save data");
01033     kmclipm_update_property_double(main_header, QC_SPAT_UNIF, qc_spat_unif, 
01034             "[adu] uniformity of illumination correction");
01035     kmclipm_update_property_double(main_header, QC_SPAT_MAX_DEV, qc_max_dev, 
01036             "[adu] max. deviation from unity");
01037     kmclipm_update_property_int(main_header, QC_SPAT_MAX_DEV_ID, qc_max_dev_id,
01038             "[] IFU ID with max. dev. from unity");
01039     kmclipm_update_property_double(main_header, QC_SPAT_MAX_NONUNIF, 
01040             qc_max_nonunif, "[adu] max. stdev of illumination corr.");
01041     kmclipm_update_property_int(main_header, QC_SPAT_MAX_NONUNIF_ID, 
01042             qc_max_nonunif_id, "[] IFU ID with max. stdev in illum. corr.");
01043 
01044     if (!suppress_extension)    fn_suffix = cpl_sprintf("%s", suffix);
01045     else                        fn_suffix = cpl_sprintf("%s", "");
01046     cpl_free(suffix) ;
01047 
01048     kmo_dfs_save_main_header(frameset, ILLUM_CORR, fn_suffix, frame, 
01049             main_header, parlist, cpl_func);
01050     if (has_flat_edge) {
01051         kmo_dfs_save_main_header(frameset, SKYFLAT_EDGE, fn_suffix, frame, 
01052                 main_header, parlist, cpl_func);
01053     }
01054     cpl_propertylist_delete(main_header);
01055 
01056     for (i = 0; i < ne * KMOS_IFUS_PER_DETECTOR; i++) {
01057         kmo_dfs_save_image(stored_data_images[i], ILLUM_CORR, fn_suffix, 
01058                 stored_sub_data_headers[i], 0./0.);
01059         if (process_noise) {
01060             kmo_dfs_save_image(stored_noise_images[i], ILLUM_CORR, fn_suffix, 
01061                     stored_sub_noise_headers[i], 0./0.);
01062         }
01063     }
01064     for (i = 0; i < ne * KMOS_IFUS_PER_DETECTOR; i++) {
01065         if (stored_data_images != NULL) {
01066             cpl_image_delete(stored_data_images[i]);
01067         }
01068         if (stored_noise_images != NULL) {
01069             cpl_image_delete(stored_noise_images[i]);
01070         }
01071     }
01072     cpl_free(stored_data_images);
01073     cpl_free(stored_noise_images);
01074 
01075     for (det_nr = 1; det_nr <= ne; det_nr++) {
01076 
01077         /* Compute only one detector */
01078         if (reduce_det != 0 && det_nr != reduce_det) continue ;
01079 
01080         for (ifu_nr = 0; ifu_nr < KMOS_IFUS_PER_DETECTOR; ifu_nr++) {
01081             kmclipm_update_property_int(
01082             stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr],
01083                     CAL_IFU_NR, ifu_nr+1+(det_nr-1)*KMOS_IFUS_PER_DETECTOR,
01084                     "IFU Number {1..24}");
01085             kmclipm_update_property_double(
01086             stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr],
01087                     CAL_ROTANGLE, rotangle_found, 
01088                     "[deg] Rotator relative to nasmyth");
01089             if (has_flat_edge) {
01090                 // save edge-parameters as product
01091                 kmo_dfs_save_table(edge_table_sky[det_nr-1][ifu_nr],
01092                         SKYFLAT_EDGE, fn_suffix, 
01093             stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr]);
01094             }
01095         }
01096     }
01097     cpl_frameset_delete(frameset_sky) ;
01098     cpl_free(fn_suffix);
01099    
01100     for (i = 0; i < ne * KMOS_IFUS_PER_DETECTOR; i++) {
01101         if (stored_sub_data_headers != NULL) {
01102             cpl_propertylist_delete(stored_sub_data_headers[i]);
01103         }
01104         if (stored_sub_noise_headers != NULL) {
01105             cpl_propertylist_delete(stored_sub_noise_headers[i]);
01106         }
01107     }
01108     cpl_free(stored_sub_data_headers);
01109     cpl_free(stored_sub_noise_headers); 
01110     if (edge_table_sky != NULL) {
01111         for (i = 0; i < KMOS_NR_DETECTORS; i++) {
01112             if (edge_table_sky[i] != NULL) {
01113                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01114                     cpl_table_delete(edge_table_sky[i][j]);
01115                 }
01116                 cpl_free(edge_table_sky[i]); 
01117             }
01118         }
01119         cpl_free(edge_table_sky); 
01120     }
01121     return 0 ;
01122 }
01123 
01126 /*----------------------------------------------------------------------------*/
01139 /*----------------------------------------------------------------------------*/
01140 static int kmos_illumination_check_inputs(
01141         cpl_frameset        *   frameset,
01142         int                     add_all_sky,
01143         int                 *   dark_has_noise,
01144         int                 *   flat_has_noise,
01145         int                 *   ne,
01146         int                 *   nx,
01147         int                 *   ny,
01148         double              *   rotangle)
01149 {
01150     cpl_frame           *   frame ;
01151     cpl_propertylist    *   main_header ;
01152     cpl_propertylist    *   tmp_header ;
01153     cpl_propertylist    *   eh ;
01154     char                *   keyword ;
01155     const char          *   filter_id ;
01156     const char          *   filter_id_l ;
01157     double                  exptime, exptime_cur, rotangle_loc, tmp_rotangle ;
01158     cpl_error_code          err ;
01159     int                     naxis1, naxis2, naxis1_cur, naxis2_cur,
01160                             next, next_cur, i ;
01161 
01162     /* Check Entries */
01163     if (nx == NULL || ny == NULL || frameset == NULL || dark_has_noise == NULL
01164             || flat_has_noise == NULL || ne == NULL || rotangle == NULL) 
01165         return -1;
01166 
01167     /* Check Exptime consistency */
01168     frame = kmo_dfs_get_frame(frameset, FLAT_SKY);
01169     /* Skip first file */
01170     if (!add_all_sky) frame = kmo_dfs_get_frame(frameset, NULL);
01171 
01172     /* Get first exptime */
01173     main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01174     exptime = kmos_pfits_get_exptime(main_header);
01175     cpl_propertylist_delete(main_header);
01176 
01177     /* Get second frame and the next */
01178     frame = kmo_dfs_get_frame(frameset, NULL);
01179     while (frame != NULL) {
01180         /* Get exptime */
01181         main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01182         exptime_cur = kmos_pfits_get_exptime(main_header);
01183         cpl_propertylist_delete(main_header);
01184         
01185         if (fabs(exptime-exptime_cur) > 0.01) {
01186             cpl_msg_warning(__func__, "EXPTIME is not consistent") ;
01187             return 0 ;
01188         }
01189         frame = kmo_dfs_get_frame(frameset, NULL);
01190     }
01191 
01192     /* Check frames numbers */
01193     if (cpl_frameset_count_tags(frameset, FLAT_SKY) < 3) {
01194         cpl_msg_warning(cpl_func, "3 or more FLAT_SKY frames is wished");
01195     }
01196     if (cpl_frameset_count_tags(frameset, MASTER_DARK) != 1) {
01197         cpl_msg_warning(__func__, "Need 1 MASTER_DARK") ;
01198         return 0 ;
01199     }
01200     if (cpl_frameset_count_tags(frameset, MASTER_FLAT) != 1) {
01201         cpl_msg_warning(__func__, "Need 1 MASTER_FLAT") ;
01202         return 0 ;
01203     }
01204     if (cpl_frameset_count_tags(frameset, XCAL) != 1) {
01205         cpl_msg_warning(__func__, "Need 1 XCAL") ;
01206         return 0 ;
01207     }
01208     if (cpl_frameset_count_tags(frameset, YCAL) != 1) {
01209         cpl_msg_warning(__func__, "Need 1 YCAL") ;
01210         return 0 ;
01211     }
01212     if (cpl_frameset_count_tags(frameset, LCAL) != 1) {
01213         cpl_msg_warning(__func__, "Need 1 LCAL") ;
01214         return 0 ;
01215     }
01216     if (cpl_frameset_count_tags(frameset, WAVE_BAND) != 1) {
01217         cpl_msg_warning(__func__, "Need 1 WAVE_BAND") ;
01218         return 0 ;
01219     }
01220 
01221     /* filter_id, grating_id and rotator offset match all detectors */
01222     err = CPL_ERROR_NONE ;
01223     err += kmo_check_frameset_setup(frameset, FLAT_SKY, TRUE, FALSE, TRUE);
01224     err += kmo_check_frame_setup(frameset, FLAT_SKY, XCAL, TRUE, FALSE, TRUE);
01225     err += kmo_check_frame_setup(frameset, XCAL, YCAL, TRUE, FALSE, TRUE);
01226     err += kmo_check_frame_setup(frameset, XCAL, LCAL, TRUE, FALSE, TRUE);
01227     err += kmo_check_frame_setup(frameset, XCAL, MASTER_FLAT, TRUE, FALSE,TRUE);
01228     err += kmo_check_frame_setup_md5_xycal(frameset);
01229     err += kmo_check_frame_setup_md5(frameset);
01230     if (err != CPL_ERROR_NONE) {
01231         cpl_msg_warning(__func__, "Frames are inconsistent") ;
01232         return 0 ;
01233     }
01234 
01235     /* Check MASTER_DARK  */
01236     frame = kmo_dfs_get_frame(frameset, MASTER_DARK);
01237     next = cpl_frame_get_nextensions(frame);
01238     if (next != 2*KMOS_NR_DETECTORS) {
01239         cpl_msg_warning(__func__, "MASTER_DARK must have 6 extensions") ;
01240         return 0 ;
01241     }
01242     eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
01243     naxis1 = kmos_pfits_get_naxis1(eh) ;
01244     naxis2 = kmos_pfits_get_naxis2(eh) ;
01245     cpl_propertylist_delete(eh) ;
01246  
01247     /* Check MASTER_FLAT  */
01248     frame = kmo_dfs_get_frame(frameset, MASTER_FLAT);
01249     next = cpl_frame_get_nextensions(frame);
01250     if (next % (2*KMOS_NR_DETECTORS) != 0) {
01251         cpl_msg_warning(__func__, "MASTER_FLAT must have 6*n extensions") ;
01252         return 0 ;
01253     }
01254     
01255     /* Check XCAL  */
01256     frame = kmo_dfs_get_frame(frameset, XCAL) ;
01257     next = cpl_frame_get_nextensions(frame);
01258     if (next % KMOS_NR_DETECTORS != 0) {
01259         cpl_msg_warning(__func__, "XCAL must have 3*n extensions") ;
01260         return 0 ;
01261     }
01262     eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
01263     naxis1_cur = kmos_pfits_get_naxis1(eh) ;
01264     naxis2_cur = kmos_pfits_get_naxis2(eh) ;
01265     cpl_propertylist_delete(eh) ;
01266     if (naxis1_cur != naxis1 || naxis2_cur != naxis2) {
01267         cpl_msg_warning(__func__, "XCAL and MASTER_DARK sizes differ") ;
01268         return 0 ;
01269     }
01270     
01271     /* Check YCAL  */
01272     frame = kmo_dfs_get_frame(frameset, YCAL);
01273     next_cur = cpl_frame_get_nextensions(frame);
01274     if (next_cur != next) {
01275         cpl_msg_warning(__func__, "XCAL and YCAL nb of extensions differ") ;
01276         return 0 ;
01277     }
01278     eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
01279     naxis1_cur = kmos_pfits_get_naxis1(eh) ;
01280     naxis2_cur = kmos_pfits_get_naxis2(eh) ;
01281     cpl_propertylist_delete(eh) ;
01282     if (naxis1_cur != naxis1 || naxis2_cur != naxis2) {
01283         cpl_msg_warning(__func__, "YCAL and MASTER_DARK sizes differ") ;
01284         return 0 ;
01285     }
01286 
01287     /* Check LCAL  */
01288     frame = kmo_dfs_get_frame(frameset, LCAL);
01289     next_cur = cpl_frame_get_nextensions(frame);
01290     if (next_cur != next) {
01291         cpl_msg_warning(__func__, "XCAL and LCAL nb of extensions differ") ;
01292         return 0 ;
01293     }
01294     eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
01295     naxis1_cur = kmos_pfits_get_naxis1(eh) ;
01296     naxis2_cur = kmos_pfits_get_naxis2(eh) ;
01297     cpl_propertylist_delete(eh) ;
01298     if (naxis1_cur != naxis1 || naxis2_cur != naxis2) {
01299         cpl_msg_warning(__func__, "LCAL and MASTER_DARK sizes differ") ;
01300         return 0 ;
01301     }
01302 
01303     tmp_header = kmo_dfs_load_primary_header(frameset, LCAL);
01304 
01305     /* Check FLAT_SKY */
01306     frame = kmo_dfs_get_frame(frameset, FLAT_SKY);
01307     main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01308     rotangle_loc = kmos_pfits_get_rotangle(main_header) ;
01309     cpl_propertylist_delete(main_header); 
01310     kmclipm_strip_angle(&rotangle_loc);
01311 
01312     while (frame != NULL) {
01313         next = cpl_frame_get_nextensions(frame);
01314         if (next != KMOS_NR_DETECTORS) {
01315             cpl_msg_warning(__func__, "FLAT_SKY has wrong nb of estensions") ;
01316             return 0 ;
01317         }
01318         eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
01319         naxis1_cur = kmos_pfits_get_naxis1(eh) ;
01320         naxis2_cur = kmos_pfits_get_naxis2(eh) ;
01321         cpl_propertylist_delete(eh) ;
01322         if (naxis1_cur != naxis1 || naxis2_cur != naxis2) {
01323             cpl_msg_warning(__func__, "FLAT_SKY and MASTER_DARK sizes differ") ;
01324             return 0 ;
01325         }
01326 
01327         /* Check Lamps */
01328         main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
01329         if (kmo_check_lamp(main_header, INS_LAMP1_ST) != FALSE ||
01330                 kmo_check_lamp(main_header, INS_LAMP2_ST) != FALSE ||
01331                 kmo_check_lamp(main_header, INS_LAMP3_ST) != FALSE ||
01332                 kmo_check_lamp(main_header, INS_LAMP4_ST) != FALSE) {
01333             cpl_msg_warning(__func__, "Some FLAT_SKY lamps are ON") ;
01334             cpl_propertylist_delete(main_header) ;
01335             return 0 ;
01336         }
01337         
01338         /* Check filters */
01339         for (i = 1; i <= KMOS_NR_DETECTORS; i++) {
01340             /* ESO INS FILTi ID */
01341             keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX, i, 
01342                     IFU_FILTID_POSTFIX);
01343             filter_id = cpl_propertylist_get_string(main_header, keyword);
01344             filter_id_l = cpl_propertylist_get_string(tmp_header, keyword);
01345             cpl_free(keyword);
01346 
01347             if (strcmp(filter_id, "IZ") && strcmp(filter_id, "YJ") && 
01348                     strcmp(filter_id, "H") && strcmp(filter_id, "K") &&
01349                     strcmp(filter_id, "HK")) {
01350                 cpl_msg_warning(__func__, 
01351                         "Filter ID must be 'IZ', 'YJ', 'H', 'K' or 'HK' ") ;
01352                 cpl_propertylist_delete(main_header) ;
01353                 cpl_propertylist_delete(tmp_header) ;
01354                 return 0 ;
01355             }
01356 
01357             if (strcmp(filter_id, filter_id_l)) {
01358                 cpl_msg_warning(__func__, 
01359                         "Filter IDs in FLAT_SKY and LCAL don't match") ;
01360                 cpl_propertylist_delete(main_header) ;
01361                 cpl_propertylist_delete(tmp_header) ;
01362                 return 0 ;
01363             }
01364 
01365             /* ESO INS GRATi ID */
01366             keyword = cpl_sprintf("%s%d%s", IFU_GRATID_PREFIX, i, 
01367                     IFU_GRATID_POSTFIX);
01368             filter_id = cpl_propertylist_get_string(main_header, keyword);
01369             filter_id_l = cpl_propertylist_get_string(tmp_header, keyword);
01370             cpl_free(keyword);
01371 
01372             if (strcmp(filter_id, "IZ") && strcmp(filter_id, "YJ") && 
01373                     strcmp(filter_id, "H") && strcmp(filter_id, "K") &&
01374                     strcmp(filter_id, "HK")) {
01375                 cpl_msg_warning(__func__, 
01376                         "Grating ID must be 'IZ', 'YJ', 'H', 'K' or 'HK' ") ;
01377                 cpl_propertylist_delete(main_header) ;
01378                 cpl_propertylist_delete(tmp_header) ;
01379                 return 0 ;
01380             }
01381             if (strcmp(filter_id, filter_id_l)) {
01382                 cpl_msg_warning(__func__, 
01383                         "Grating IDs in FLAT_SKY and LCAL don't match") ;
01384                 cpl_propertylist_delete(main_header) ;
01385                 cpl_propertylist_delete(tmp_header) ;
01386                 return 0 ;
01387             }
01388 
01389             tmp_rotangle = kmos_pfits_get_rotangle(main_header) ;
01390             kmclipm_strip_angle(&tmp_rotangle);
01391             
01392             if (fabs(rotangle_loc - tmp_rotangle) > 10.0 &&
01393                     fabs(rotangle_loc - tmp_rotangle) < 360.-10.) {
01394                 cpl_msg_warning(__func__, 
01395                         "OCS ROT NAANGLE of sky flats differ too much: %f %f",
01396                         rotangle_loc, tmp_rotangle);
01397                 cpl_propertylist_delete(main_header) ;
01398                 cpl_propertylist_delete(tmp_header) ;
01399                 return 0 ;
01400             }
01401         }
01402         cpl_propertylist_delete(main_header);
01403 
01404         // get next FLAT_SKY frame
01405         frame = kmo_dfs_get_frame(frameset, NULL);
01406     }
01407     cpl_propertylist_delete(tmp_header);
01408 
01409     /* Return */
01410     *nx = naxis1 ;
01411     *ny = naxis2 ;
01412     *ne = next ;
01413     *rotangle = rotangle_loc ;
01414     /* TODO */
01415     *dark_has_noise = 1 ;
01416     *flat_has_noise = 1 ;
01417     return 1 ;
01418 }
01419 
01420 /*----------------------------------------------------------------------------*/
01426 /*----------------------------------------------------------------------------*/
01427 static cpl_table ** kmos_illumination_edge_shift_correct( 
01428         cpl_image       *   combined_data,
01429         cpl_image       *   combined_noise,
01430         int                 process_noise,
01431         const cpl_image *   bad_pix_mask,
01432         int                 det_nr,
01433         cpl_array       *   unused_ifus,
01434         const char      *   flat_edge_filename,
01435         double              rotangle)
01436 {
01437     int                 middle_row ;
01438     cpl_vector      **  slitlet_ids = NULL ;
01439     cpl_matrix      **  edgepars = NULL ;
01440     cpl_table       **  edges ;
01441     cpl_vector      *   shift_vec ;
01442     const int       *   punused_ifus ;
01443     cpl_table       *   edge_table_flat ;
01444     cpl_vector      *   edge_vec ;
01445     kmclipm_vector  *   kv ;
01446     float           *   pcombined_data ;
01447     float           *   pcombined_noise ;
01448     double          *   array_in ;
01449     double          *   array_out ;
01450     double              tmp_rotangle, flatval, skyval, shift_val ;
01451     int                 ifu_nr, i, nx, ny, ix, iy, edgeNr ;
01452     
01453     /* Check Entries */
01454 
01455     /* Initialise */
01456     middle_row = 1024 ;
01457 
01458     /* Get edge-edgepars from FLAT_SKY */
01459     kmos_calc_edgepars(combined_data, unused_ifus, bad_pix_mask, det_nr, 
01460             &slitlet_ids, &edgepars);
01461     if (cpl_error_get_code() != CPL_ERROR_NONE) {
01462         cpl_msg_error(__func__, "Cannot compute edges parameters") ;
01463         return NULL ;
01464     }
01465 
01466     /* Copy edgepars to table for saving later on */
01467     edges = kmo_edgepars_to_table(slitlet_ids, edgepars);
01468     if (edgepars != NULL) {
01469         for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) 
01470             cpl_matrix_delete(edgepars[i]);
01471         cpl_free(edgepars); 
01472     }
01473     if (slitlet_ids != NULL) {
01474         for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) 
01475             cpl_vector_delete(slitlet_ids[i]); 
01476         cpl_free(slitlet_ids);
01477     }
01478 
01479     /* Correlate FLAT_EDGE and SKYFLAT_EDGE */
01480     shift_vec = cpl_vector_new(KMOS_IFUS_PER_DETECTOR);
01481     for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
01482         ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + i + 1;
01483         punused_ifus = cpl_array_get_data_int_const(unused_ifus);
01484         if (punused_ifus[i] == 0) {
01485             edge_table_flat = kmclipm_cal_table_load(flat_edge_filename, 
01486                     ifu_nr, rotangle, 0, &tmp_rotangle);
01487             /* Shift values for each IFU by comparing edge parameters */
01488             if (edge_table_flat != NULL) {
01489                 edge_vec = cpl_vector_new(2*KMOS_SLITLET_X);
01490                 for (edgeNr = 0; edgeNr < 2*KMOS_SLITLET_X; edgeNr++) {
01491                     flatval = kmo_calc_fitted_slitlet_edge(edge_table_flat, 
01492                             edgeNr, middle_row);
01493                     skyval = kmo_calc_fitted_slitlet_edge(edges[i], edgeNr, 
01494                             middle_row);
01495                     cpl_vector_set(edge_vec, edgeNr, flatval-skyval);
01496                 }
01497                 cpl_table_delete(edge_table_flat);
01498  
01499                 /* Reject deviating edge-differences */
01500                 kv = kmclipm_vector_create(edge_vec);
01501                 kmclipm_reject_deviant(kv, 3, 3, NULL, NULL);
01502 
01503                 /* Set shift value for each IFU */
01504                 cpl_vector_set(shift_vec, i, 
01505                         kmclipm_vector_get_median(kv, KMCLIPM_ARITHMETIC));
01506                 kmclipm_vector_delete(kv); 
01507             } else {
01508                 cpl_vector_set(shift_vec, i, 0.0) ;
01509             }
01510         } else {
01511             cpl_vector_set(shift_vec, i, 0.0) ;
01512         }
01513     }
01514 
01515     /* Take median of all IFU-shift-values */
01516     shift_val = -cpl_vector_get_median(shift_vec);
01517     cpl_vector_delete(shift_vec); 
01518 
01519     cpl_msg_info(__func__, "Shift detector %d by %g pixels", det_nr, shift_val);
01520     nx = cpl_image_get_size_x(combined_data),
01521     ny = cpl_image_get_size_x(combined_data),
01522     pcombined_data = cpl_image_get_data_float(combined_data) ;
01523     if (process_noise) {
01524         pcombined_noise = cpl_image_get_data_float(combined_noise);
01525     }
01526 
01527     array_in = cpl_calloc(nx, sizeof(double)) ;
01528     /* Apply shift - Cubic spline */
01529     for (iy = 0; iy < ny; iy++) {
01530         for (ix = 0; ix < nx; ix++) array_in[ix] = pcombined_data[ix+iy*nx];
01531         array_out = cubicspline_reg_reg(nx, 0., 1., array_in, nx, shift_val, 
01532                 1.0, NATURAL);
01533         for (ix = 0; ix < nx; ix++) pcombined_data[ix+iy*nx] = array_out[ix];
01534         cpl_free(array_out);
01535 
01536         if (process_noise) {
01537             for (ix = 0; ix < nx; ix++) array_in[ix]=pcombined_noise[ix+iy*nx];
01538             array_out = cubicspline_reg_reg(nx, 0., 1., array_in, nx, shift_val,
01539                     1.0, NATURAL);
01540             for (ix = 0; ix < nx; ix++) pcombined_noise[ix+iy*nx]=array_out[ix];
01541             cpl_free(array_out);
01542         }
01543     }
01544     cpl_free(array_in);
01545     return edges ;
01546 }
01547 
01548