KMOS Pipeline Reference Manual  1.3.17
kmos_reconstruct.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_functions.h"
00035 #include "kmo_priv_reconstruct.h"
00036 #include "kmo_priv_functions.h"
00037 #include "kmo_priv_lcorr.h"
00038 #include "kmo_cpl_extensions.h"
00039 #include "kmo_dfs.h"
00040 #include "kmo_error.h"
00041 #include "kmo_utils.h"
00042 #include "kmo_constants.h"
00043 #include "kmo_debug.h"
00044 #include "kmos_oscan.h"
00045 
00046 /*-----------------------------------------------------------------------------
00047  *                          Functions prototypes
00048  *----------------------------------------------------------------------------*/
00049 
00050 static int kmos_reconstruct_check_inputs(cpl_frameset *, const char *);
00051 
00052 static int kmos_reconstruct_create(cpl_plugin *);
00053 static int kmos_reconstruct_exec(cpl_plugin *);
00054 static int kmos_reconstruct_destroy(cpl_plugin *);
00055 static int kmos_reconstruct(cpl_parameterlist *, cpl_frameset *);
00056 
00057 /*-----------------------------------------------------------------------------
00058  *                          Static variables
00059  *----------------------------------------------------------------------------*/
00060 
00061 static char kmos_reconstruct_description[] =
00062 "Data with or without noise is reconstructed into a cube using X/Y/LCAL, YCAL\n"
00063 "The input data can contain noise extensions and will be reconstructed into\n"
00064 "additional extensions.\n"
00065 "If an OH spectrum is given in the SOF file the lambda axis will be corrected\n"
00066 "using the OH lines as reference.\n"
00067 "\n"
00068 "---------------------------------------------------------------------------\n"
00069 "  Input files:\n"
00070 "\n"
00071 "   DO              KMOS                                                    \n"
00072 "   category        Type     Explanation                    Required #Frames\n"
00073 "   --------        -----    -----------                    -------- -------\n"
00074 "   DARK    or      RAW/F2D  data with                          Y       1   \n"
00075 "   FLAT_ON or      RAW/F2D  or without noise                               \n"
00076 "   ARC_ON  or      RAW/F2D                                                 \n"
00077 "   OBJECT  or      RAW                                                     \n"
00078 "   STD     or      RAW                                                     \n"
00079 "   SCIENCE         RAW                                                     \n"
00080 "   XCAL            F2D      x-direction calib. frame           Y       1   \n"
00081 "   YCAL            F2D      y-direction calib. frame           Y       1   \n"
00082 "   LCAL            F2D      Wavelength calib. frame            Y       1   \n"
00083 "   WAVE_BAND       F2L      Table with start-/end-wavelengths  Y       1   \n"
00084 "   OH_SPEC         F1S      Vector holding OH lines            N       1   \n"
00085 "\n"
00086 "  Output files:\n"
00087 "\n"
00088 "   DO                KMOS\n"
00089 "   category          Type     Explanation\n"
00090 "   --------              -----    -----------\n"
00091 "   CUBE_DARK   or    F3I      Reconstructed cube   \n"
00092 "   CUBE_FLAT   or    RAW/F2D  with or without noise\n"
00093 "   CUBE_ARC    or                                  \n"
00094 "   CUBE_OBJECT or                                  \n"
00095 "   CUBE_STD    or                                  \n"
00096 "   CUBE_SCIENCE                                    \n"
00097 "---------------------------------------------------------------------------\n"
00098 "\n";
00099 
00100 /*-----------------------------------------------------------------------------
00101  *                              Functions code
00102  *----------------------------------------------------------------------------*/
00103 
00104 /*----------------------------------------------------------------------------*/
00108 /*----------------------------------------------------------------------------*/
00109 
00112 /*----------------------------------------------------------------------------*/
00121 /*----------------------------------------------------------------------------*/
00122 int cpl_plugin_get_info(cpl_pluginlist *list)
00123 {
00124     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00125     cpl_plugin *plugin = &recipe->interface;
00126 
00127     cpl_plugin_init(plugin,
00128                         CPL_PLUGIN_API,
00129                         KMOS_BINARY_VERSION,
00130                         CPL_PLUGIN_TYPE_RECIPE,
00131                         "kmos_reconstruct",
00132                         "Performs the cube reconstruction",
00133                         kmos_reconstruct_description,
00134                         "Alex Agudo Berbel, Y. Jung",
00135                         "usd-help@eso.org",
00136                         kmos_get_license(),
00137                         kmos_reconstruct_create,
00138                         kmos_reconstruct_exec,
00139                         kmos_reconstruct_destroy);
00140 
00141     cpl_pluginlist_append(list, plugin);
00142 
00143     return 0;
00144 }
00145 
00146 /*----------------------------------------------------------------------------*/
00154 /*----------------------------------------------------------------------------*/
00155 static int kmos_reconstruct_create(cpl_plugin *plugin)
00156 {
00157     cpl_recipe *recipe;
00158     cpl_parameter *p;
00159 
00160     /* Check that the plugin is part of a valid recipe */
00161     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00162         recipe = (cpl_recipe *)plugin;
00163     else
00164         return -1;
00165 
00166     /* Create the parameters list in the cpl_recipe object */
00167     recipe->parameters = cpl_parameterlist_new();
00168 
00169     /* Fill the parameters list */
00170     /* --imethod */
00171     p = cpl_parameter_new_value("kmos.kmos_reconstruct.imethod", 
00172             CPL_TYPE_STRING,
00173             "Method to use for interpolation. [\"NN\" (nearest neighbour), "
00174             "\"lwNN\" (linear weighted nearest neighbor), "
00175             "\"swNN\" (square weighted nearest neighbor), "
00176             "\"MS\" (Modified Shepard's method)"
00177             "\"CS\" (Cubic spline)]",
00178             "kmos.kmos_reconstruct", "CS");
00179     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "imethod");
00180     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00181     cpl_parameterlist_append(recipe->parameters, p);
00182 
00183     /* --neighborhoodRange */
00184     p = cpl_parameter_new_value("kmos.kmos_reconstruct.neighborhoodRange",
00185             CPL_TYPE_DOUBLE,
00186             "Defines the range to search for neighbors. in pixels",
00187             "kmos.kmos_reconstruct", 1.001);
00188     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "neighborhoodRange");
00189     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00190     cpl_parameterlist_append(recipe->parameters, p);
00191 
00192     /* --flux */
00193     p = cpl_parameter_new_value("kmos.kmos_reconstruct.flux", CPL_TYPE_BOOL,
00194             "TRUE: Apply flux conservation. FALSE: otherwise",
00195             "kmos.kmos_reconstruct", FALSE);
00196     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
00197     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00198     cpl_parameterlist_append(recipe->parameters, p);
00199 
00200     /* --detectorimage */
00201     p = cpl_parameter_new_value("kmos.kmos_reconstruct.detectorimage",
00202             CPL_TYPE_BOOL,
00203             "TRUE: if resampled detector frame should be "
00204             "created, FALSE: otherwise",
00205             "kmos.kmos_reconstruct", FALSE);
00206     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "detimg");
00207     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00208     cpl_parameterlist_append(recipe->parameters, p);
00209 
00210     /* --file_extension */
00211     p = cpl_parameter_new_value("kmos.kmos_reconstruct.file_extension",
00212             CPL_TYPE_BOOL,
00213             "TRUE: if OBS_ID keyword should be appended to "
00214             "output frames, FALSE: otherwise",
00215             "kmos.kmos_reconstruct", FALSE);
00216     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "file_extension");
00217     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00218     cpl_parameterlist_append(recipe->parameters, p);
00219 
00220     /* --pix_scale */
00221     p = cpl_parameter_new_value("kmos.kmos_reconstruct.pix_scale",
00222             CPL_TYPE_DOUBLE,
00223             "Change the pixel scale [arcsec]. "
00224             "Default of 0.2\" results into cubes of 14x14pix, "
00225             "a scale of 0.1\" results into cubes of 28x28pix, etc.",
00226             "kmos.kmos_reconstruct", KMOS_PIX_RESOLUTION);
00227     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "pix_scale");
00228     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00229     cpl_parameterlist_append(recipe->parameters, p);
00230 
00231     /* --xcal_interpolation */
00232     p = cpl_parameter_new_value("kmos.kmos_reconstruct.xcal_interpolation",
00233             CPL_TYPE_BOOL,
00234             "TRUE: Interpolate xcal between rotator angles. FALSE: otherwise",
00235             "kmos.kmos_reconstruct", TRUE);
00236     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "xcal_interpolation");
00237     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00238     cpl_parameterlist_append(recipe->parameters, p);
00239 
00240     /* --oscan */
00241     p = cpl_parameter_new_value("kmos.kmos_reconstruct.oscan",
00242             CPL_TYPE_BOOL, "Apply Overscan Correction",
00243             "kmos.kmos_reconstruct", TRUE);
00244     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "oscan");
00245     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00246     cpl_parameterlist_append(recipe->parameters, p);
00247 
00248     /* Add parameters for band-definition */
00249     kmos_band_pars_create(recipe->parameters, "kmos.kmos_reconstruct");
00250     return 0;
00251 }
00252 
00253 /*----------------------------------------------------------------------------*/
00259 /*----------------------------------------------------------------------------*/
00260 static int kmos_reconstruct_exec(cpl_plugin *plugin)
00261 {
00262     cpl_recipe  *recipe;
00263 
00264     /* Get the recipe out of the plugin */
00265     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00266         recipe = (cpl_recipe *)plugin;
00267     else return -1;
00268 
00269     return kmos_reconstruct(recipe->parameters, recipe->frames);
00270 }
00271 
00272 /*----------------------------------------------------------------------------*/
00278 /*----------------------------------------------------------------------------*/
00279 static int kmos_reconstruct_destroy(cpl_plugin *plugin)
00280 {
00281     cpl_recipe *recipe;
00282 
00283     /* Get the recipe out of the plugin */
00284     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00285         recipe = (cpl_recipe *)plugin;
00286     else return -1 ;
00287 
00288     cpl_parameterlist_delete(recipe->parameters);
00289     return 0 ;
00290 }
00291 
00292 /*----------------------------------------------------------------------------*/
00305 /*----------------------------------------------------------------------------*/
00306 static int kmos_reconstruct(cpl_parameterlist *parlist, cpl_frameset *frameset)
00307 {
00308     const cpl_parameter *   par ;
00309     const char          *   imethod ;
00310     int                     flux, detectorimage, xcal_interpolation,
00311                             file_extension, oscan ;
00312     double                  neighborhoodRange, pix_scale, scaling ;
00313     cpl_frame           *   input_frame ;
00314     cpl_frame           *   xcal_frame ;
00315     cpl_frame           *   ycal_frame ;
00316     cpl_frame           *   lcal_frame ;
00317     cpl_frame           *   ref_spectrum_frame ;
00318     float               *   pdet_img_data ;
00319     float               *   pdet_img_noise ;
00320     float               *   slice ;
00321     const char          *   input_frame_name ;
00322     const char          *   output_frame_name ;
00323     const char          *   filter_id ;
00324     char                *   keyword ;
00325     char                *   suffix ;
00326     char                *   obs_suffix ;
00327     char                *   extname ;
00328     int                 *   bounds ;
00329     cpl_image           *   det_img_data ;
00330     cpl_image           *   det_img_noise;
00331     cpl_image           *   tmp_img ;
00332     cpl_imagelist       *   cube_data ;
00333     cpl_imagelist       *   cube_noise ;
00334     cpl_propertylist    *   main_header ;
00335     cpl_propertylist    *   sub_header ;
00336     cpl_propertylist    *   tmp_header ;
00337     cpl_table           *   band_table ;
00338     gridDefinition          gd ;
00339     cpl_polynomial      *   lcorr_coeffs ;
00340     main_fits_desc          desc1 ;
00341     int                     i, j, index, ifu_nr, detImgCube, l, x, y ;
00342 
00343     /* Initialise */
00344     detImgCube = FALSE ;
00345 
00346     /* Check Entries */
00347     if (parlist == NULL || frameset == NULL) {
00348         cpl_msg_error(__func__, "Null Inputs") ;
00349         cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
00350         return -1 ;
00351     }
00352 
00353     /* Get parameters */
00354     par = cpl_parameterlist_find_const(parlist,"kmos.kmos_reconstruct.imethod");
00355     imethod = cpl_parameter_get_string(par) ;
00356     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_reconstruct.flux");
00357     flux = cpl_parameter_get_bool(par);
00358     par = cpl_parameterlist_find_const(parlist, 
00359             "kmos.kmos_reconstruct.detectorimage");
00360     detectorimage = cpl_parameter_get_bool(par);
00361     par = cpl_parameterlist_find_const(parlist,
00362             "kmos.kmos_reconstruct.neighborhoodRange");
00363     neighborhoodRange = cpl_parameter_get_double(par) ;
00364     kmos_band_pars_load(parlist, "kmos.kmos_reconstruct");
00365     par = cpl_parameterlist_find_const(parlist, 
00366             "kmos.kmos_reconstruct.file_extension");
00367     file_extension = cpl_parameter_get_bool(par);
00368     par = cpl_parameterlist_find_const(parlist, 
00369             "kmos.kmos_reconstruct.pix_scale");
00370     pix_scale = cpl_parameter_get_double(par) ;
00371     par = cpl_parameterlist_find_const(parlist,
00372             "kmos.kmos_reconstruct.xcal_interpolation");
00373     xcal_interpolation = cpl_parameter_get_bool(par);
00374     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_reconstruct.oscan");
00375     oscan = cpl_parameter_get_bool(par);
00376 
00377     /* Check Parameters */
00378     if (strcmp(imethod, "NN") && strcmp(imethod, "lwNN") &&
00379             strcmp(imethod, "swNN") && strcmp(imethod, "MS") &&
00380             strcmp(imethod, "CS")) {
00381         cpl_msg_error(__func__,
00382                 "imethod must be 'NN','lwNN','swNN','MS' or 'CS'") ;
00383         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00384         return -1 ;
00385     }
00386     if (neighborhoodRange <= 0.0) {
00387         cpl_msg_error(__func__,
00388                 "neighborhoodRange must be greater than 0.0") ;
00389         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00390         return -1 ;
00391     }
00392     if (pix_scale < 0.01 || pix_scale > 0.4) {
00393         cpl_msg_error(__func__, "pix_scale must be between 0.01 and 0.4");
00394         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00395         return -1 ;
00396     }
00397 
00398     /* Identify the RAW and CALIB frames in the input frameset */
00399     if (kmo_dfs_set_groups(frameset, "kmos_reconstruct") != 1) {
00400         cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
00401         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00402         return -1 ;
00403     }
00404 
00405     /* IO File names */
00406     if (cpl_frameset_count_tags(frameset, DARK) == 1) {
00407         input_frame_name = DARK;
00408         output_frame_name = CUBE_DARK;
00409     } else if (cpl_frameset_count_tags(frameset, FLAT_ON) == 1) {
00410         input_frame_name = FLAT_ON;
00411         output_frame_name = CUBE_FLAT;
00412     } else if (cpl_frameset_count_tags(frameset, ARC_ON) == 1) {
00413         input_frame_name = ARC_ON;
00414         output_frame_name = CUBE_ARC;
00415     } else if (cpl_frameset_count_tags(frameset, OBJECT) == 1) {
00416         input_frame_name = OBJECT;
00417         output_frame_name = CUBE_OBJECT;
00418     } else if (cpl_frameset_count_tags(frameset, STD) == 1) {
00419         input_frame_name = STD;
00420         output_frame_name = CUBE_STD;
00421     } else if (cpl_frameset_count_tags(frameset, SCIENCE) == 1) {
00422         input_frame_name = SCIENCE;
00423         output_frame_name = CUBE_SCIENCE;
00424     } else {
00425         cpl_msg_error(__func__, "Missing Inputs") ;
00426         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00427         return -1 ;
00428     }
00429 
00430     /* Check the inputs consistency */
00431     if (kmos_reconstruct_check_inputs(frameset, input_frame_name) != 1) {
00432         cpl_msg_error(__func__, "Input frameset is not consistent") ;
00433         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00434         return -1 ;
00435     }
00436 
00437     /* Instrument setup */
00438     input_frame = kmo_dfs_get_frame(frameset, input_frame_name);
00439     suffix = kmo_dfs_get_suffix(input_frame, TRUE, TRUE);
00440     cpl_msg_info(__func__, "Detected instrument setup:   %s", suffix+1);
00441     cpl_free(suffix);
00442 
00443     /* Get frames */
00444     xcal_frame = kmo_dfs_get_frame(frameset, XCAL);
00445     ycal_frame = kmo_dfs_get_frame(frameset, YCAL) ;
00446     lcal_frame = kmo_dfs_get_frame(frameset, LCAL) ;
00447     ref_spectrum_frame = kmo_dfs_get_frame(frameset, OH_SPEC) ;
00448 
00449     /* Get input frame primary header */
00450     main_header = kmo_dfs_load_primary_header(frameset, input_frame_name);
00451 
00452     /* Use the OBS ID in the file name */
00453     if (file_extension) {
00454         obs_suffix = cpl_sprintf("_%d",
00455                 cpl_propertylist_get_int(main_header, OBS_ID));
00456     } else {
00457         obs_suffix = cpl_sprintf("%s", "");
00458     }
00459 
00460     /* Save Product (reconstructed) Main Header */
00461     kmo_dfs_save_main_header(frameset, output_frame_name, obs_suffix,
00462             input_frame, NULL, parlist, cpl_func);
00463 
00464     /* Save Product (detector image) Main Header */
00465     if (detectorimage == TRUE) 
00466         kmo_dfs_save_main_header(frameset, DET_IMG_REC, obs_suffix,
00467                 input_frame, NULL, parlist, cpl_func);
00468             
00469     /* Grid definition, wavelength start and end are set later */
00470     kmclipm_setup_grid(&gd, imethod, neighborhoodRange, pix_scale, 0.);
00471 
00472     /* READ Wavelength bounds */
00473     tmp_header = kmo_dfs_load_primary_header(frameset, XCAL);
00474     bounds = kmclipm_extract_bounds(tmp_header);
00475     cpl_propertylist_delete(tmp_header);
00476 
00477     kmo_init_fits_desc(&desc1);
00478     desc1 = kmo_identify_fits_header(cpl_frame_get_filename(input_frame));
00479 
00480     /* Loop through detectors */
00481     for (i = 1; i <= KMOS_NR_DETECTORS; i++) {
00482         cpl_msg_info("","Processing detector No. %d", i);
00483 
00484         /* Load LCAL image close to ROTANGLE 0. assuming that the  */
00485         /* wavelength range doesn't differ with different ROTANGLEs. */
00486         print_cal_angle_msg_once = FALSE;
00487         print_xcal_angle_msg_once = FALSE;
00488         if (i==1) {
00489             print_cal_angle_msg_once = TRUE;
00490             print_xcal_angle_msg_once = TRUE;
00491         }
00492 
00493         /* Read Filter ID */
00494         keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX,i,IFU_FILTID_POSTFIX);
00495         filter_id = cpl_propertylist_get_string(main_header, keyword);
00496         cpl_free(keyword);
00497         
00498         /* Load Band Info */
00499         band_table = kmo_dfs_load_table(frameset, WAVE_BAND, 1, FALSE);
00500         kmclipm_setup_grid_band_lcal(&gd, filter_id, band_table);
00501         cpl_table_delete(band_table); 
00502 
00503         /* Create empty detector images */
00504         if (detectorimage == TRUE) {
00505             det_img_data = cpl_image_new(
00506                     gd.x.dim*gd.y.dim*KMOS_IFUS_PER_DETECTOR, gd.l.dim, 
00507                     CPL_TYPE_FLOAT);
00508             pdet_img_data = cpl_image_get_data_float(det_img_data);
00509 
00510             det_img_noise = cpl_image_new(
00511                     gd.x.dim*gd.y.dim*KMOS_IFUS_PER_DETECTOR, gd.l.dim, 
00512                     CPL_TYPE_FLOAT);
00513                 pdet_img_noise = cpl_image_get_data_float(det_img_noise);
00514         }
00515 
00516         /* Loop on IFUs */
00517         for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00518             ifu_nr = (i-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
00519 
00520             /* Load sub-header*/
00521             sub_header = kmo_dfs_load_sub_header(frameset, input_frame_name,
00522                     i, FALSE);
00523 
00524             /* Check if IFU is valid according to main header keywords */
00525             if (bounds[2*(ifu_nr-1)] != -1 && bounds[2*(ifu_nr-1)+1] != -1) {
00526                 /* IFU valid */
00527 
00528                 /* calc WCS & update subheader */
00529                 kmo_calc_wcs_gd(main_header, sub_header, ifu_nr, gd);
00530                 kmclipm_update_property_int(sub_header, NAXIS, 3,
00531                         "number of data axes");
00532                 kmclipm_update_property_int(sub_header, NAXIS1, 
00533                         gd.x.dim, "length of data axis 1");
00534                 kmclipm_update_property_int(sub_header, NAXIS2, 
00535                         gd.y.dim, "length of data axis 2");
00536                 kmclipm_update_property_int(sub_header, NAXIS3, 
00537                         gd.l.dim, "length of data axis 3");
00538 
00539                 /* Reconstruct data and noise (if available) */
00540                 kmo_reconstruct_sci(ifu_nr, bounds[2*(ifu_nr-1)],
00541                         bounds[2*(ifu_nr-1)+1], input_frame, input_frame_name,
00542                         NULL, NULL, NULL, xcal_frame, ycal_frame, lcal_frame,
00543                         NULL, NULL, &gd, &cube_data, &cube_noise, flux,
00544                         0, xcal_interpolation, oscan);
00545 
00546                 /* Reconstruct again using OH_SPEC */
00547                 if (ref_spectrum_frame != NULL && cube_data != NULL) {
00548                     lcorr_coeffs = kmo_lcorr_get(cube_data, sub_header,
00549                             ref_spectrum_frame, gd, filter_id, ifu_nr);
00550                     cpl_imagelist_delete(cube_data);
00551                     if (cube_noise != NULL) cpl_imagelist_delete(cube_noise);
00552                     kmo_reconstruct_sci(ifu_nr, bounds[2*(ifu_nr-1)],
00553                             bounds[2*(ifu_nr-1)+1], input_frame, 
00554                             input_frame_name, NULL, NULL, NULL, xcal_frame, 
00555                             ycal_frame, lcal_frame, lcorr_coeffs, NULL, &gd, 
00556                             &cube_data, &cube_noise, flux, 0, 
00557                             xcal_interpolation, oscan);
00558                     cpl_polynomial_delete(lcorr_coeffs);
00559                 }
00560 
00561                 /* Scale flux according to pixel_scale */
00562                 tmp_img = cpl_imagelist_get(cube_data, 0);
00563                 scaling = (cpl_image_get_size_x(tmp_img)*
00564                         cpl_image_get_size_y(tmp_img)) /
00565                     (KMOS_SLITLET_X*KMOS_SLITLET_Y);
00566                 cpl_imagelist_divide_scalar(cube_data, scaling);
00567                 if (cube_noise != NULL) 
00568                     cpl_imagelist_divide_scalar(cube_noise, scaling);
00569             } else {
00570                 /* IFU invalid */
00571                 cube_data = cube_noise = NULL ;
00572             }
00573 
00574             /* Fill detector images */
00575             if (detectorimage) {
00576                 if (cube_data != NULL) {
00577                     for (l = 0; l < gd.l.dim; l++) {
00578                         slice = cpl_image_get_data_float(
00579                                 cpl_imagelist_get(cube_data, l));
00580                         for (y = 0; y < gd.y.dim; y++) {
00581                             for (x = 0; x < gd.x.dim; x++) {
00582                                 int ix = x +
00583                                     y* gd.x.dim +
00584                                     j* gd.x.dim*gd.y.dim +    //IFU offset
00585                                     l* gd.x.dim*gd.y.dim*KMOS_IFUS_PER_DETECTOR;
00586                                 pdet_img_data[ix] = slice[x + y*gd.x.dim];
00587                             }
00588                         }
00589                     }
00590                 }
00591                 if (cube_noise != NULL) {
00592                     detImgCube = TRUE;
00593                     for (l = 0; l < gd.l.dim; l++) {
00594                         slice = cpl_image_get_data_float(
00595                                 cpl_imagelist_get(cube_noise, l));
00596                         for (y = 0; y < gd.y.dim; y++) {
00597                             for (x = 0; x < gd.x.dim; x++) {
00598                                 int ix = x +
00599                                     y* gd.x.dim +
00600                                     j* gd.x.dim*gd.y.dim +     //IFU offset
00601                                     l* gd.x.dim*gd.y.dim*KMOS_IFUS_PER_DETECTOR;
00602                                 pdet_img_noise[ix] = slice[x + y*gd.x.dim];
00603                             }
00604                         }
00605                     }
00606                 }
00607             }
00608 
00609             /* Save Product (reconstructed) Data Cube */
00610             extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_DATA);
00611             kmclipm_update_property_string(sub_header, EXTNAME, extname,
00612                     "FITS extension name");
00613             cpl_free(extname);
00614             kmo_dfs_save_cube(cube_data, output_frame_name, obs_suffix,
00615                     sub_header, 0./0.);
00616 
00617             /* Save Product (reconstructed) Noise Cube */
00618             if (cube_noise != NULL) {
00619                 extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_NOISE);
00620                 kmclipm_update_property_string(sub_header, EXTNAME,
00621                         extname, "FITS extension name");
00622                 cpl_free(extname);
00623                 kmo_dfs_save_cube(cube_noise, output_frame_name, obs_suffix,
00624                         sub_header, 0./0.);
00625             }
00626 
00627             cpl_imagelist_delete(cube_data);
00628             if (cube_noise != NULL) cpl_imagelist_delete(cube_noise);
00629             cpl_propertylist_delete(sub_header);
00630         } // IFUs loop
00631 
00632         if (detectorimage) {
00633             /* Save Product (detector image) Data Cube */
00634             index = kmo_identify_index(cpl_frame_get_filename(input_frame), i,
00635                     FALSE);
00636             tmp_header = kmclipm_propertylist_load(
00637                     cpl_frame_get_filename(input_frame), index);
00638             kmo_save_det_img_ext(det_img_data, gd, i, DET_IMG_REC,
00639                     obs_suffix, tmp_header, FALSE, FALSE);
00640             cpl_propertylist_delete(tmp_header);
00641             cpl_image_delete(det_img_data);
00642 
00643             if (detImgCube) {
00644                 /* Save Product (detector image) Noise Cube */
00645                 /* Index changes (*2) if Noise extensions are there */
00646                 if (desc1.ex_noise) {
00647                     index = kmo_identify_index(
00648                             cpl_frame_get_filename(input_frame), i, TRUE);
00649                 }
00650                 tmp_header = kmclipm_propertylist_load(
00651                         cpl_frame_get_filename(input_frame), index);
00652                 kmo_save_det_img_ext(det_img_noise, gd, i, DET_IMG_REC,
00653                         obs_suffix, tmp_header, FALSE, TRUE);
00654                 cpl_propertylist_delete(tmp_header); 
00655             }
00656             cpl_image_delete(det_img_noise);
00657         }
00658     } // Detectors loop
00659     cpl_propertylist_delete(main_header);
00660     cpl_free(obs_suffix);
00661     kmo_free_fits_desc(&desc1);
00662     cpl_free(bounds);
00663 
00664     return 0;
00665 }
00666 
00669 /*----------------------------------------------------------------------------*/
00676 /*----------------------------------------------------------------------------*/
00677 static int kmos_reconstruct_check_inputs(
00678         cpl_frameset            *   frameset,
00679         const char              *   input_frame_name)
00680 {
00681     cpl_propertylist    *   lcal_header ;
00682     cpl_propertylist    *   input_header ;
00683     int                     nb_xcal, nb_ycal, nb_lcal, nb_oh_spec ;
00684     char                *   keyword ;
00685     const char          *   filter_id ;
00686     const char          *   filter_id_tmp ;
00687     int                     i ;
00688 
00689     /* Check entries */
00690     if (frameset == NULL || input_frame_name == NULL) return -1;
00691 
00692     /* Count frames */
00693     if (cpl_frameset_count_tags(frameset, DARK) != 1 &&
00694             cpl_frameset_count_tags(frameset, FLAT_ON) != 1 &&
00695             cpl_frameset_count_tags(frameset, ARC_ON) != 1 &&
00696             cpl_frameset_count_tags(frameset, OBJECT) != 1 &&
00697             cpl_frameset_count_tags(frameset, STD) != 1 &&
00698             cpl_frameset_count_tags(frameset, SCIENCE) != 1) {
00699         cpl_msg_error(__func__, "1 data frame must be provided") ;
00700         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00701         return 0 ;
00702     }
00703     nb_xcal = cpl_frameset_count_tags(frameset, XCAL) ;
00704     nb_ycal = cpl_frameset_count_tags(frameset, YCAL) ;
00705     nb_lcal = cpl_frameset_count_tags(frameset, LCAL) ;
00706     if (nb_xcal != 1 || nb_ycal != 1 || nb_lcal != 1) {
00707         cpl_msg_error(__func__, "Exactly 1 XCAL/YCAL/LCAL expected") ;
00708         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00709         return 0 ;
00710     }
00711     if (cpl_frameset_count_tags(frameset, WAVE_BAND) != 1) {
00712         cpl_msg_error(__func__, "Missing WAVE_BAND") ;
00713         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00714         return 0 ;
00715     }
00716     nb_oh_spec = cpl_frameset_count_tags(frameset, OH_SPEC) ;
00717     if (nb_oh_spec != 0 && nb_oh_spec != 1) {
00718         cpl_msg_error(__func__, "Only 1 reference spectrum can be provided") ;
00719         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00720         return 0 ;
00721     }
00722 
00723     /* Check filters, rotation consistencies */
00724     kmo_check_frame_setup(frameset, XCAL, YCAL, TRUE, FALSE, TRUE);
00725     kmo_check_frame_setup(frameset, XCAL, LCAL, TRUE, FALSE, TRUE);
00726     if (nb_oh_spec == 1)    kmo_check_oh_spec_setup(frameset, XCAL);
00727 
00728     if (cpl_frameset_count_tags(frameset, DARK) != 1) {
00729         kmo_check_frame_setup(frameset, XCAL, input_frame_name, TRUE, FALSE, 
00730                 FALSE);
00731     }
00732     kmo_check_frame_setup_md5_xycal(frameset);
00733     kmo_check_frame_setup_md5(frameset);
00734 
00735     /* Check filters */
00736     input_header = kmo_dfs_load_primary_header(frameset, input_frame_name);
00737     lcal_header = kmo_dfs_load_primary_header(frameset, LCAL);
00738     for (i = 1; i <= KMOS_NR_DETECTORS; i++) {
00739         // ESO INS FILTi ID
00740         keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX,i,IFU_FILTID_POSTFIX);
00741         filter_id = cpl_propertylist_get_string(lcal_header, keyword);
00742         if (strcmp(filter_id, "IZ") && strcmp(filter_id, "YJ") &&
00743                 strcmp(filter_id, "H") && strcmp(filter_id, "K") &&
00744                 strcmp(filter_id, "HK")) {
00745             cpl_free(keyword) ;
00746             cpl_propertylist_delete(input_header) ;
00747             cpl_propertylist_delete(lcal_header) ;
00748             cpl_msg_error(__func__,"LCAL Filter ID must be IZ, YJ, H, HK or K");
00749             cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00750             return 0 ;
00751         }
00752 
00753         /* Dark not taken with filter */
00754         if (!strcmp(input_frame_name, DARK)) {
00755             filter_id_tmp = cpl_propertylist_get_string(input_header, keyword);
00756             if (strcmp(filter_id, filter_id_tmp)) {
00757                 cpl_free(keyword) ;
00758                 cpl_propertylist_delete(input_header) ;
00759                 cpl_propertylist_delete(lcal_header) ;
00760                 cpl_msg_error(__func__,"Filter mismatch");
00761                 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00762                 return 0 ;
00763             }
00764         }
00765         cpl_free(keyword);
00766     }
00767     cpl_propertylist_delete(input_header) ;
00768     cpl_propertylist_delete(lcal_header) ;
00769     
00770     return 1 ;
00771 }
00772