KMOS Pipeline Reference Manual  1.3.0
kmo_rotate.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 #include <string.h>
00025 
00026 #include <cpl.h>
00027 
00028 #include <kmo_debug.h>
00029 #include <kmo_utils.h>
00030 #include <kmo_dfs.h>
00031 #include <kmo_error.h>
00032 #include <kmo_priv_functions.h>
00033 #include <kmo_cpl_extensions.h>
00034 #include <kmo_constants.h>
00035 #include <kmo_priv_rotate.h>
00036 
00037 static int kmo_rotate_create(cpl_plugin *);
00038 static int kmo_rotate_exec(cpl_plugin *);
00039 static int kmo_rotate_destroy(cpl_plugin *);
00040 static int kmo_rotate(cpl_parameterlist *, cpl_frameset *);
00041 
00042 static char kmo_rotate_description[] =
00043 "This recipe rotates a cube spatially (CCW). If the rotation angle isn't\n"
00044 "a multiple of 90 degrees, the output cube will be interpolated and get larger\n"
00045 "accordingly.\n"
00046 "By default all IFUs will be rotated.\n"
00047 "\n"
00048 "BASIC PARAMETERS:\n"
00049 "-----------------\n"
00050 "--rotations\n"
00051 "This parameter must be supplied. It contains the amount of rotation to apply.\n"
00052 "The unit is in degrees. If it contains one value (e.g. “3.5”) all IFUs are\n"
00053 "rotated by the same amount. If 24 values are supplied each IFU is rotated\n"
00054 "individually (e.g. “2.3;15.7;…;-3.3”).\n"
00055 "\n"
00056 "--imethod\n"
00057 "The interpolation method to apply when rotating an angle not being a multiple\n"
00058 "of 90. There are two methods available:\n"
00059 "   * BCS: Bicubic spline\n"
00060 "   * NN:  Nearest Neighbor (currently disabled)\n"
00061 "\n"
00062 "--ifu\n"
00063 "If a single IFU should be rotated, it can be defined using the --ifu parameter\n"
00064 "(--rotations parameter contains only one value).\n"
00065 "\n"
00066 "ADVANCED PARAMETERS\n"
00067 "-------------------\n"
00068 "--flux\n"
00069 "Specify if flux conservation should be applied.\n"
00070 "\n"
00071 "--extrapolate\n"
00072 "By default the output frame grows when rotating an angle not being a multiple\n"
00073 "of 90. In this case none of the input data is lost. When it is desired to keep\n"
00074 "the same size as the input frame this parameter can be set to TRUE and the\n"
00075 "data will be clipped.\n"
00076 "\n"
00077 "-------------------------------------------------------------------------------\n"
00078 "  Input files:\n"
00079 "\n"
00080 "   DO                    KMOS                                                  \n"
00081 "   category              Type   Explanation                    Required #Frames\n"
00082 "   --------              -----  -----------                    -------- -------\n"
00083 "   <none or any>         F3I    data frame                         Y       1   \n"
00084 "\n"
00085 "  Output files:\n"
00086 "\n"
00087 "   DO                    KMOS\n"
00088 "   category              Type   Explanation\n"
00089 "   --------              -----  -----------\n"
00090 "   ROTATE                F3I    Rotated data cube\n"
00091 "-------------------------------------------------------------------------------\n"
00092 "\n";
00093 
00110 int cpl_plugin_get_info(cpl_pluginlist *list)
00111 {
00112     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00113     cpl_plugin *plugin = &recipe->interface;
00114 
00115     cpl_plugin_init(plugin,
00116                         CPL_PLUGIN_API,
00117                         KMOS_BINARY_VERSION,
00118                         CPL_PLUGIN_TYPE_RECIPE,
00119                         "kmo_rotate",
00120                         "Rotate a cube spatially",
00121                         kmo_rotate_description,
00122                         "Alex Agudo Berbel",
00123                         "usd-help@eso.org",
00124                         kmos_get_license(),
00125                         kmo_rotate_create,
00126                         kmo_rotate_exec,
00127                         kmo_rotate_destroy);
00128 
00129     cpl_pluginlist_append(list, plugin);
00130 
00131     return 0;
00132 }
00133 
00141 static int kmo_rotate_create(cpl_plugin *plugin)
00142 {
00143     cpl_recipe *recipe;
00144     cpl_parameter *p;
00145 
00146     /* Check that the plugin is part of a valid recipe */
00147     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00148         recipe = (cpl_recipe *)plugin;
00149     else
00150         return -1;
00151 
00152     /* Create the parameters list in the cpl_recipe object */
00153     recipe->parameters = cpl_parameterlist_new();
00154 
00155     /* Fill the parameters list */
00156     /* --imethod */
00157     p = cpl_parameter_new_value("kmos.kmo_rotate.imethod",
00158                                 CPL_TYPE_STRING,
00159                                 "Method to use for interpolation: "
00160                                 "[\"BCS\" (bicubic spline, default), "
00161                                 "\"NN\" (nearest neighbor), not implemented yet]",
00162                                 "kmos.kmo_rotate",
00163                                 "BCS");
00164     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "imethod");
00165     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00166     cpl_parameterlist_append(recipe->parameters, p);
00167 
00168     /* --extrapolate */
00169     p = cpl_parameter_new_value("kmos.kmo_rotate.extrapolate",
00170                                 CPL_TYPE_BOOL,
00171                                 "Applies only when rotation angle is different "
00172                                 "from multiples of 90 degrees: "
00173                                 "FALSE: Output IFU will be larger than the input "
00174                                 "(Default), "
00175                                 "TRUE: The size of input and output IFU remains "
00176                                 "the same. Data will be clipped.",
00177                                 "kmos.kmo_rotate",
00178                                 FALSE);
00179     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extrapolate");
00180     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00181     cpl_parameterlist_append(recipe->parameters, p);
00182 
00183     /* --rotations */
00184     p = cpl_parameter_new_value("kmos.kmo_rotate.rotations",
00185                                 CPL_TYPE_STRING,
00186                                 "The rotations for all specified IFUs. "
00187                                 "\"rot1;rot2;...\" (degrees)",
00188                                 "kmos.kmo_rotate",
00189                                 "");
00190     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rotations");
00191     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00192     cpl_parameterlist_append(recipe->parameters, p);
00193 
00194     /* --ifu */
00195     p = cpl_parameter_new_value("kmos.kmo_rotate.ifu",
00196                                 CPL_TYPE_INT,
00197                                 "The IFU to rotate [1 to 24] or rotate all IFUs "
00198                                 "[0, default].",
00199                                 "kmos.kmo_rotate",
00200                                 0);
00201     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ifu");
00202     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00203     cpl_parameterlist_append(recipe->parameters, p);
00204 
00205     /* --flux */
00206     p = cpl_parameter_new_value("kmos.kmo_rotate.flux",
00207                                 CPL_TYPE_BOOL,
00208                                 "Apply flux conservation: "
00209                                 "(TRUE (apply) or "
00210                                 "FALSE (don't apply)",
00211                                 "kmos.kmo_rotate",
00212                                 FALSE);
00213     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
00214     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00215     cpl_parameterlist_append(recipe->parameters, p);
00216 
00217     return 0;
00218 }
00219 
00225 static int kmo_rotate_exec(cpl_plugin *plugin)
00226 {
00227     cpl_recipe  *recipe;
00228 
00229     /* Get the recipe out of the plugin */
00230     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00231         recipe = (cpl_recipe *)plugin;
00232     else return -1 ;
00233 
00234     return kmo_rotate(recipe->parameters, recipe->frames);
00235 }
00236 
00242 static int kmo_rotate_destroy(cpl_plugin *plugin)
00243 {
00244     cpl_recipe *recipe;
00245 
00246     /* Get the recipe out of the plugin */
00247     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00248         recipe = (cpl_recipe *)plugin;
00249     else return -1 ;
00250 
00251     cpl_parameterlist_delete(recipe->parameters);
00252     return 0 ;
00253 }
00254 
00269 static int kmo_rotate(cpl_parameterlist *parlist, cpl_frameset *frameset)
00270 {
00271     const char       *method             = NULL,
00272                      *rotations_txt      = NULL;
00273 
00274     cpl_imagelist    *data               = NULL,
00275                      *noise              = NULL;
00276 
00277     cpl_vector       *rotations          = NULL,
00278                      *rotations2         = NULL;
00279 
00280     int              ret_val             = 0,
00281                      nr_devices          = 0,
00282                      i                   = 0,
00283                      valid_ifu           = FALSE,
00284                      flux                = 0,
00285                      size                = 0,
00286                      ifu                 = 0,
00287                      extrapolate         = 0,
00288                      devnr               = 0,
00289                      index_data          = 0,
00290                      index_noise         = 0;
00291 
00292     enum extrapolationType extrapol_enum = 0;
00293 
00294     const double     *protations2        = NULL;
00295 
00296     cpl_propertylist *sub_header_data    = NULL,
00297                      *sub_header_noise   = NULL;
00298 
00299     cpl_frame        *frame              = NULL;
00300 
00301     main_fits_desc   desc1;
00302 
00303     KMO_TRY
00304     {
00305         kmo_init_fits_desc(&desc1);
00306 
00307         /* --- check input --- */
00308         KMO_TRY_ASSURE((parlist != NULL) &&
00309                        (frameset != NULL),
00310                        CPL_ERROR_NULL_INPUT,
00311                        "Not all input data is provided!");
00312 
00313         KMO_TRY_ASSURE(cpl_frameset_get_size(frameset) == 1,
00314                        CPL_ERROR_NULL_INPUT,
00315                        "A cube must be provided!");
00316 
00317         KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_rotate") == 1,
00318                        CPL_ERROR_ILLEGAL_INPUT,
00319                        "Cannot identify RAW and CALIB frames!");
00320 
00321         cpl_msg_info("", "--- Parameter setup for kmo_rotate --------");
00322 
00323         KMO_TRY_EXIT_IF_NULL(
00324             method = kmo_dfs_get_parameter_string(parlist,
00325                                            "kmos.kmo_rotate.imethod"));
00326         KMO_TRY_EXIT_IF_ERROR(
00327             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.imethod"));
00328 
00329         extrapolate = kmo_dfs_get_parameter_bool(parlist,
00330                                                 "kmos.kmo_rotate.extrapolate");
00331         KMO_TRY_CHECK_ERROR_STATE();
00332 
00333         if (extrapolate == 1) {
00334             extrapol_enum = NONE_NANS;
00335         } else if (extrapolate == 0) {
00336             extrapol_enum = RESIZE_NANS;
00337         } else {
00338             KMO_TRY_ASSURE(1 == 0,
00339                            CPL_ERROR_ILLEGAL_INPUT,
00340                            "extrapolate must be 1 or 0!");
00341         }
00342 
00343         KMO_TRY_EXIT_IF_ERROR(
00344             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.extrapolate"));
00345 
00346         rotations_txt = kmo_dfs_get_parameter_string(parlist,
00347                                                   "kmos.kmo_rotate.rotations");
00348         KMO_TRY_CHECK_ERROR_STATE();
00349         KMO_TRY_EXIT_IF_ERROR(
00350             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.rotations"));
00351 
00352         KMO_TRY_ASSURE(strcmp(rotations_txt, "") != 0,
00353                        CPL_ERROR_ILLEGAL_INPUT,
00354                        "At least one value for --rotations parameter must be "
00355                        "provided!");
00356 
00357         rotations = kmo_identify_values(rotations_txt);
00358         KMO_TRY_CHECK_ERROR_STATE();
00359 
00360         size = cpl_vector_get_size(rotations);
00361         KMO_TRY_CHECK_ERROR_STATE();
00362 
00363         KMO_TRY_ASSURE((size == 1) || (size == KMOS_NR_IFUS),
00364                        CPL_ERROR_ILLEGAL_INPUT,
00365                        "rotations parameter must have either one or 24 elements!");
00366 
00367         ifu = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_rotate.ifu");
00368         KMO_TRY_CHECK_ERROR_STATE();
00369         KMO_TRY_EXIT_IF_ERROR(
00370             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.ifu"));
00371 
00372         if (ifu == 0) {
00373             // rotate all IFUs the same or different amounts
00374             KMO_TRY_ASSURE((size == 1) || (size == KMOS_NR_IFUS),
00375                            CPL_ERROR_ILLEGAL_INPUT,
00376                            "rotations parameter must have exactly 1 elements"
00377                            "(rotate all IFUs the same amount) or 24 elements "
00378                            "(rotate all IFUs individually)!");
00379         } else {
00380             // rotate only one specific IFU
00381             KMO_TRY_ASSURE(size == 1,
00382                            CPL_ERROR_ILLEGAL_INPUT,
00383                            "rotations parameter must have exactly one elements "
00384                            "to rotate a single IFU!");
00385         }
00386 
00387         // setup a vector of length 24 regardless of how many IFUs to rotate
00388         if (size == KMOS_NR_IFUS) {
00389             KMO_TRY_EXIT_IF_NULL(
00390                 rotations2 = cpl_vector_duplicate(rotations));
00391         } else {
00392             KMO_TRY_EXIT_IF_NULL(
00393                 rotations2 = cpl_vector_new(KMOS_NR_IFUS));
00394             KMO_TRY_EXIT_IF_NULL(
00395                 protations2 = cpl_vector_get_data_const(rotations));
00396             for (i = 0; i < KMOS_NR_IFUS; i++) {
00397                 cpl_vector_set(rotations2, i, protations2[0]);
00398             }
00399         }
00400 
00401         KMO_TRY_EXIT_IF_NULL(
00402                 protations2 = cpl_vector_get_data_const(rotations2));
00403 
00404         KMO_TRY_ASSURE((strcmp(method, "NN") == 0) ||
00405                        (strcmp(method, "BCS") == 0)
00406                        /*(strcmp(method, "kriging") == 0) ||
00407                        (strcmp(method, "cubic") == 0) ||
00408                        (strcmp(method, "shepard") == 0) ||
00409                        (strcmp(method, "drizzle") == 0)*/,
00410                        CPL_ERROR_ILLEGAL_INPUT,
00411                        "method must be \"BCS\"!");
00412 
00413         flux = kmo_dfs_get_parameter_bool(parlist,
00414                                           "kmos.kmo_rotate.flux");
00415         KMO_TRY_CHECK_ERROR_STATE();
00416         KMO_TRY_EXIT_IF_ERROR(
00417             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.flux"));
00418 
00419         cpl_msg_info("", "-------------------------------------------");
00420 
00421         KMO_TRY_ASSURE((flux == 0) ||
00422                        (flux == 1),
00423                        CPL_ERROR_ILLEGAL_INPUT,
00424                        "flux must be either 0 or 1 !");
00425 
00426         // load descriptor of first operand
00427         KMO_TRY_EXIT_IF_NULL(
00428             frame = kmo_dfs_get_frame(frameset, "0"));
00429 
00430         desc1 = kmo_identify_fits_header(
00431                     cpl_frame_get_filename(frame));
00432         KMO_TRY_CHECK_ERROR_STATE_MSG("Provided fits file doesn't seem to be "
00433                                       "in KMOS-format!");
00434 
00435         KMO_TRY_ASSURE(desc1.fits_type == f3i_fits,
00436                        CPL_ERROR_ILLEGAL_INPUT,
00437                        "First input file hasn't correct data type "
00438                        "(KMOSTYPE must be F3I)!");
00439 
00440         // --- load, update & save primary header ---
00441         KMO_TRY_EXIT_IF_ERROR(
00442             kmo_dfs_save_main_header(frameset, ROTATE, "", frame,
00443                                      NULL, parlist, cpl_func));
00444 
00445         // --- load data ---
00446         if (desc1.ex_noise == TRUE) {
00447             nr_devices = desc1.nr_ext / 2;
00448         } else {
00449             nr_devices = desc1.nr_ext;
00450         }
00451 
00452         for (i = 1; i <= nr_devices; i++) {
00453             if (desc1.ex_noise == FALSE) {
00454                 devnr = desc1.sub_desc[i - 1].device_nr;
00455             } else {
00456                 devnr = desc1.sub_desc[2 * i - 1].device_nr;
00457             }
00458 
00459             if (desc1.ex_badpix == FALSE) {
00460                 index_data = kmo_identify_index_desc(desc1, devnr, FALSE);
00461             } else {
00462                 index_data = kmo_identify_index_desc(desc1, devnr, 2);
00463             }
00464             KMO_TRY_CHECK_ERROR_STATE();
00465 
00466             if (desc1.ex_noise) {
00467                 index_noise = kmo_identify_index_desc(desc1, devnr, TRUE);
00468             }
00469             KMO_TRY_CHECK_ERROR_STATE();
00470 
00471             KMO_TRY_EXIT_IF_NULL(
00472                 sub_header_data = kmo_dfs_load_sub_header(frameset, "0", devnr,
00473                                                           FALSE));
00474 
00475             // check if IFU is valid
00476             valid_ifu = FALSE;
00477             if (desc1.sub_desc[index_data-1].valid_data == TRUE) {
00478                 valid_ifu = TRUE;
00479             }
00480 
00481             if (desc1.ex_noise) {
00482                 // load noise anyway since we have to save it in the output
00483                 KMO_TRY_EXIT_IF_NULL(
00484                     sub_header_noise = kmo_dfs_load_sub_header(frameset, "0",
00485                                                                devnr, TRUE));
00486             }
00487 
00488             if (valid_ifu) {
00489                 // load data
00490                 KMO_TRY_EXIT_IF_NULL(
00491                     data = kmo_dfs_load_cube(frameset, "0", devnr, FALSE));
00492 
00493                 // load noise, if existing
00494                 if (desc1.ex_noise && desc1.sub_desc[index_noise-1].valid_data) {
00495                     KMO_TRY_EXIT_IF_NULL(
00496                         noise = kmo_dfs_load_cube(frameset, "0", devnr, TRUE));
00497                 }
00498 
00499                 if ((ifu == 0) || (ifu == devnr)) {
00500                     // process here
00501                     KMO_TRY_EXIT_IF_ERROR(
00502                         kmo_priv_rotate(&data, &noise,
00503                                         &sub_header_data, &sub_header_noise,
00504                                         protations2[i-1],
00505                                         flux, devnr, method, extrapol_enum));
00506                 } else {
00507                     // leave data and noise as they are and
00508                     // save them again unrotated
00509                 }
00510 
00511                 // save data and noise (if existing)
00512                 KMO_TRY_EXIT_IF_ERROR(
00513                     kmo_dfs_save_cube(data, ROTATE, "", sub_header_data, 0./0.));
00514 
00515                 if (desc1.ex_noise) {
00516                     KMO_TRY_EXIT_IF_ERROR(
00517                         kmo_dfs_save_cube(noise, ROTATE, "", sub_header_noise,
00518                                           0./0.));
00519                 }
00520 
00521                 // free memory
00522                 cpl_imagelist_delete(data); data = NULL;
00523                 cpl_imagelist_delete(noise); noise = NULL;
00524             } else {
00525                 // invalid IFU, just save sub_headers
00526                 KMO_TRY_EXIT_IF_ERROR(
00527                     kmo_dfs_save_sub_header(ROTATE, "", sub_header_data));
00528 
00529                 if (desc1.ex_noise) {
00530                     KMO_TRY_EXIT_IF_ERROR(
00531                         kmo_dfs_save_sub_header(ROTATE, "", sub_header_noise));
00532                 }
00533             }
00534 
00535             // free memory
00536             cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
00537             cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;
00538         }
00539     }
00540     KMO_CATCH
00541     {
00542         KMO_CATCH_MSG();
00543         ret_val = -1;
00544     }
00545 
00546     kmo_free_fits_desc(&desc1);
00547     cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
00548     cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;
00549     cpl_imagelist_delete(data); data = NULL;
00550     cpl_imagelist_delete(noise); noise = NULL;
00551     cpl_vector_delete(rotations); rotations = NULL;
00552     cpl_vector_delete(rotations2); rotations2 = NULL;
00553 
00554     return ret_val;
00555 }
00556