KMOS Pipeline Reference Manual 4.5.10
kmo_rotate.c
1/*
2 * This file is part of the KMOS Pipeline
3 * Copyright (C) 2002,2003 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <string.h>
25
26#include <cpl.h>
27
28#include <kmo_debug.h>
29#include <kmo_utils.h>
30#include <kmo_dfs.h>
31#include <kmo_error.h>
32#include <kmo_priv_functions.h>
33#include <kmo_cpl_extensions.h>
34#include <kmo_constants.h>
35#include <kmo_priv_rotate.h>
36
37static int kmo_rotate_create(cpl_plugin *);
38static int kmo_rotate_exec(cpl_plugin *);
39static int kmo_rotate_destroy(cpl_plugin *);
40static int kmo_rotate(cpl_parameterlist *, cpl_frameset *);
41
42static char kmo_rotate_description[] =
43"This recipe rotates a cube spatially (CCW). If the rotation angle isn't\n"
44"a multiple of 90 degrees, the output cube will be interpolated and get larger\n"
45"accordingly.\n"
46"By default all IFUs will be rotated.\n"
47"\n"
48"BASIC PARAMETERS:\n"
49"-----------------\n"
50"--rotations\n"
51"This parameter must be supplied. It contains the amount of rotation to apply.\n"
52"The unit is in degrees. If it contains one value (e.g. “3.5”) all IFUs are\n"
53"rotated by the same amount. If 24 values are supplied each IFU is rotated\n"
54"individually (e.g. “2.3;15.7;…;-3.3”).\n"
55"\n"
56"--imethod\n"
57"The interpolation method to apply when rotating an angle not being a multiple\n"
58"of 90. There are two methods available:\n"
59" * BCS: Bicubic spline\n"
60" * NN: Nearest Neighbor (currently disabled)\n"
61"\n"
62"--ifu\n"
63"If a single IFU should be rotated, it can be defined using the --ifu parameter\n"
64"(--rotations parameter contains only one value).\n"
65"\n"
66"ADVANCED PARAMETERS\n"
67"-------------------\n"
68"--flux\n"
69"Specify if flux conservation should be applied.\n"
70"\n"
71"--extrapolate\n"
72"By default the output frame grows when rotating an angle not being a multiple\n"
73"of 90. In this case none of the input data is lost. When it is desired to keep\n"
74"the same size as the input frame this parameter can be set to TRUE and the\n"
75"data will be clipped.\n"
76"\n"
77"-------------------------------------------------------------------------------\n"
78" Input files:\n"
79"\n"
80" DO KMOS \n"
81" category Type Explanation Required #Frames\n"
82" -------- ----- ----------- -------- -------\n"
83" <none or any> F3I data frame Y 1 \n"
84"\n"
85" Output files:\n"
86"\n"
87" DO KMOS\n"
88" category Type Explanation\n"
89" -------- ----- -----------\n"
90" ROTATE F3I Rotated data cube\n"
91"-------------------------------------------------------------------------------\n"
92"\n";
93
110int cpl_plugin_get_info(cpl_pluginlist *list)
111{
112 cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
113 cpl_plugin *plugin = &recipe->interface;
114
115 cpl_plugin_init(plugin,
116 CPL_PLUGIN_API,
117 KMOS_BINARY_VERSION,
118 CPL_PLUGIN_TYPE_RECIPE,
119 "kmo_rotate",
120 "Rotate a cube spatially",
121 kmo_rotate_description,
122 "Alex Agudo Berbel",
123 "https://support.eso.org/",
124 kmos_get_license(),
125 kmo_rotate_create,
126 kmo_rotate_exec,
127 kmo_rotate_destroy);
128
129 cpl_pluginlist_append(list, plugin);
130
131 return 0;
132}
133
141static int kmo_rotate_create(cpl_plugin *plugin)
142{
143 cpl_recipe *recipe;
144 cpl_parameter *p;
145
146 /* Check that the plugin is part of a valid recipe */
147 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
148 recipe = (cpl_recipe *)plugin;
149 else
150 return -1;
151
152 /* Create the parameters list in the cpl_recipe object */
153 recipe->parameters = cpl_parameterlist_new();
154
155 /* Fill the parameters list */
156 /* --imethod */
157 p = cpl_parameter_new_value("kmos.kmo_rotate.imethod",
158 CPL_TYPE_STRING,
159 "Method to use for interpolation: "
160 "[\"BCS\" (bicubic spline, default), "
161 "\"NN\" (nearest neighbor), not implemented yet]",
162 "kmos.kmo_rotate",
163 "BCS");
164 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "imethod");
165 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
166 cpl_parameterlist_append(recipe->parameters, p);
167
168 /* --extrapolate */
169 p = cpl_parameter_new_value("kmos.kmo_rotate.extrapolate",
170 CPL_TYPE_BOOL,
171 "Applies only when rotation angle is different "
172 "from multiples of 90 degrees: "
173 "FALSE: Output IFU will be larger than the input "
174 "(Default), "
175 "TRUE: The size of input and output IFU remains "
176 "the same. Data will be clipped.",
177 "kmos.kmo_rotate",
178 FALSE);
179 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extrapolate");
180 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
181 cpl_parameterlist_append(recipe->parameters, p);
182
183 /* --rotations */
184 p = cpl_parameter_new_value("kmos.kmo_rotate.rotations",
185 CPL_TYPE_STRING,
186 "The rotations for all specified IFUs. "
187 "\"rot1;rot2;...\" (degrees)",
188 "kmos.kmo_rotate",
189 "");
190 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "rotations");
191 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
192 cpl_parameterlist_append(recipe->parameters, p);
193
194 /* --ifu */
195 p = cpl_parameter_new_value("kmos.kmo_rotate.ifu",
196 CPL_TYPE_INT,
197 "The IFU to rotate [1 to 24] or rotate all IFUs "
198 "[0, default].",
199 "kmos.kmo_rotate",
200 0);
201 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ifu");
202 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
203 cpl_parameterlist_append(recipe->parameters, p);
204
205 /* --flux */
206 p = cpl_parameter_new_value("kmos.kmo_rotate.flux",
207 CPL_TYPE_BOOL,
208 "Apply flux conservation: "
209 "(TRUE (apply) or "
210 "FALSE (don't apply)",
211 "kmos.kmo_rotate",
212 FALSE);
213 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
214 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
215 cpl_parameterlist_append(recipe->parameters, p);
216
217 return 0;
218}
219
225static int kmo_rotate_exec(cpl_plugin *plugin)
226{
227 cpl_recipe *recipe;
228
229 /* Get the recipe out of the plugin */
230 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
231 recipe = (cpl_recipe *)plugin;
232 else return -1 ;
233
234 return kmo_rotate(recipe->parameters, recipe->frames);
235}
236
242static int kmo_rotate_destroy(cpl_plugin *plugin)
243{
244 cpl_recipe *recipe;
245
246 /* Get the recipe out of the plugin */
247 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
248 recipe = (cpl_recipe *)plugin;
249 else return -1 ;
250
251 cpl_parameterlist_delete(recipe->parameters);
252 return 0 ;
253}
254
269static int kmo_rotate(cpl_parameterlist *parlist, cpl_frameset *frameset)
270{
271 const char *method = NULL,
272 *rotations_txt = NULL;
273
274 cpl_imagelist *data = NULL,
275 *noise = NULL;
276
277 cpl_vector *rotations = NULL,
278 *rotations2 = NULL;
279
280 int ret_val = 0,
281 nr_devices = 0,
282 i = 0,
283 valid_ifu = FALSE,
284 flux = 0,
285 size = 0,
286 ifu = 0,
287 extrapolate = 0,
288 devnr = 0,
289 index_data = 0,
290 index_noise = 0;
291
292 enum extrapolationType extrapol_enum = 0;
293
294 const double *protations2 = NULL;
295
296 cpl_propertylist *sub_header_data = NULL,
297 *sub_header_noise = NULL;
298
299 cpl_frame *frame = NULL;
300
301 main_fits_desc desc1;
302
303 KMO_TRY
304 {
305 kmo_init_fits_desc(&desc1);
306
307 /* --- check input --- */
308 KMO_TRY_ASSURE((parlist != NULL) &&
309 (frameset != NULL),
310 CPL_ERROR_NULL_INPUT,
311 "Not all input data is provided!");
312
313 KMO_TRY_ASSURE(cpl_frameset_get_size(frameset) == 1,
314 CPL_ERROR_NULL_INPUT,
315 "A cube must be provided!");
316
317 KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset) == 1,
318 CPL_ERROR_ILLEGAL_INPUT,
319 "Cannot identify RAW and CALIB frames!");
320
321 cpl_msg_info("", "--- Parameter setup for kmo_rotate --------");
322
323 KMO_TRY_EXIT_IF_NULL(
324 method = kmo_dfs_get_parameter_string(parlist,
325 "kmos.kmo_rotate.imethod"));
326 KMO_TRY_EXIT_IF_ERROR(
327 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.imethod"));
328
329 extrapolate = kmo_dfs_get_parameter_bool(parlist,
330 "kmos.kmo_rotate.extrapolate");
331 KMO_TRY_CHECK_ERROR_STATE();
332
333 if (extrapolate == 1) {
334 extrapol_enum = NONE_NANS;
335 } else if (extrapolate == 0) {
336 extrapol_enum = RESIZE_NANS;
337 } else {
338 KMO_TRY_ASSURE(1 == 0,
339 CPL_ERROR_ILLEGAL_INPUT,
340 "extrapolate must be 1 or 0!");
341 }
342
343 KMO_TRY_EXIT_IF_ERROR(
344 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.extrapolate"));
345
346 rotations_txt = kmo_dfs_get_parameter_string(parlist,
347 "kmos.kmo_rotate.rotations");
348 KMO_TRY_CHECK_ERROR_STATE();
349 KMO_TRY_EXIT_IF_ERROR(
350 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.rotations"));
351
352 KMO_TRY_ASSURE(strcmp(rotations_txt, "") != 0,
353 CPL_ERROR_ILLEGAL_INPUT,
354 "At least one value for --rotations parameter must be "
355 "provided!");
356
357 rotations = kmo_identify_values(rotations_txt);
358 KMO_TRY_CHECK_ERROR_STATE();
359
360 size = cpl_vector_get_size(rotations);
361 KMO_TRY_CHECK_ERROR_STATE();
362
363 KMO_TRY_ASSURE((size == 1) || (size == KMOS_NR_IFUS),
364 CPL_ERROR_ILLEGAL_INPUT,
365 "rotations parameter must have either one or 24 elements!");
366
367 ifu = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_rotate.ifu");
368 KMO_TRY_CHECK_ERROR_STATE();
369 KMO_TRY_EXIT_IF_ERROR(
370 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.ifu"));
371
372 if (ifu == 0) {
373 // rotate all IFUs the same or different amounts
374 KMO_TRY_ASSURE((size == 1) || (size == KMOS_NR_IFUS),
375 CPL_ERROR_ILLEGAL_INPUT,
376 "rotations parameter must have exactly 1 elements"
377 "(rotate all IFUs the same amount) or 24 elements "
378 "(rotate all IFUs individually)!");
379 } else {
380 // rotate only one specific IFU
381 KMO_TRY_ASSURE(size == 1,
382 CPL_ERROR_ILLEGAL_INPUT,
383 "rotations parameter must have exactly one elements "
384 "to rotate a single IFU!");
385 }
386
387 // setup a vector of length 24 regardless of how many IFUs to rotate
388 if (size == KMOS_NR_IFUS) {
389 KMO_TRY_EXIT_IF_NULL(
390 rotations2 = cpl_vector_duplicate(rotations));
391 } else {
392 KMO_TRY_EXIT_IF_NULL(
393 rotations2 = cpl_vector_new(KMOS_NR_IFUS));
394 KMO_TRY_EXIT_IF_NULL(
395 protations2 = cpl_vector_get_data_const(rotations));
396 for (i = 0; i < KMOS_NR_IFUS; i++) {
397 cpl_vector_set(rotations2, i, protations2[0]);
398 }
399 }
400
401 KMO_TRY_EXIT_IF_NULL(
402 protations2 = cpl_vector_get_data_const(rotations2));
403
404 KMO_TRY_ASSURE((strcmp(method, "NN") == 0) ||
405 (strcmp(method, "BCS") == 0)
406 /*(strcmp(method, "kriging") == 0) ||
407 (strcmp(method, "cubic") == 0) ||
408 (strcmp(method, "shepard") == 0) ||
409 (strcmp(method, "drizzle") == 0)*/,
410 CPL_ERROR_ILLEGAL_INPUT,
411 "method must be \"BCS\"!");
412
413 flux = kmo_dfs_get_parameter_bool(parlist,
414 "kmos.kmo_rotate.flux");
415 KMO_TRY_CHECK_ERROR_STATE();
416 KMO_TRY_EXIT_IF_ERROR(
417 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_rotate.flux"));
418
419 cpl_msg_info("", "-------------------------------------------");
420
421 KMO_TRY_ASSURE((flux == 0) ||
422 (flux == 1),
423 CPL_ERROR_ILLEGAL_INPUT,
424 "flux must be either 0 or 1 !");
425
426 // load descriptor of first operand
427 KMO_TRY_EXIT_IF_NULL(
428 frame = kmo_dfs_get_frame(frameset, "0"));
429
430 desc1 = kmo_identify_fits_header(
431 cpl_frame_get_filename(frame));
432 KMO_TRY_CHECK_ERROR_STATE_MSG("Provided fits file doesn't seem to be "
433 "in KMOS-format!");
434
435 KMO_TRY_ASSURE(desc1.fits_type == f3i_fits,
436 CPL_ERROR_ILLEGAL_INPUT,
437 "First input file hasn't correct data type "
438 "(KMOSTYPE must be F3I)!");
439
440 // --- load, update & save primary header ---
441 KMO_TRY_EXIT_IF_ERROR(
442 kmo_dfs_save_main_header(frameset, ROTATE, "", frame,
443 NULL, parlist, cpl_func));
444
445 // --- load data ---
446 if (desc1.ex_noise == TRUE) {
447 nr_devices = desc1.nr_ext / 2;
448 } else {
449 nr_devices = desc1.nr_ext;
450 }
451
452 for (i = 1; i <= nr_devices; i++) {
453 if (desc1.ex_noise == FALSE) {
454 devnr = desc1.sub_desc[i - 1].device_nr;
455 } else {
456 devnr = desc1.sub_desc[2 * i - 1].device_nr;
457 }
458
459 if (desc1.ex_badpix == FALSE) {
460 index_data = kmo_identify_index_desc(desc1, devnr, FALSE);
461 } else {
462 index_data = kmo_identify_index_desc(desc1, devnr, 2);
463 }
464 KMO_TRY_CHECK_ERROR_STATE();
465
466 if (desc1.ex_noise) {
467 index_noise = kmo_identify_index_desc(desc1, devnr, TRUE);
468 }
469 KMO_TRY_CHECK_ERROR_STATE();
470
471 KMO_TRY_EXIT_IF_NULL(
472 sub_header_data = kmo_dfs_load_sub_header(frameset, "0", devnr,
473 FALSE));
474
475 // check if IFU is valid
476 valid_ifu = FALSE;
477 if (desc1.sub_desc[index_data-1].valid_data == TRUE) {
478 valid_ifu = TRUE;
479 }
480
481 if (desc1.ex_noise) {
482 // load noise anyway since we have to save it in the output
483 KMO_TRY_EXIT_IF_NULL(
484 sub_header_noise = kmo_dfs_load_sub_header(frameset, "0",
485 devnr, TRUE));
486 }
487
488 if (valid_ifu) {
489 // load data
490 KMO_TRY_EXIT_IF_NULL(
491 data = kmo_dfs_load_cube(frameset, "0", devnr, FALSE));
492
493 // load noise, if existing
494 if (desc1.ex_noise && desc1.sub_desc[index_noise-1].valid_data) {
495 KMO_TRY_EXIT_IF_NULL(
496 noise = kmo_dfs_load_cube(frameset, "0", devnr, TRUE));
497 }
498
499 if ((ifu == 0) || (ifu == devnr)) {
500 // process here
501 KMO_TRY_EXIT_IF_ERROR(
502 kmo_priv_rotate(&data, &noise,
503 &sub_header_data, &sub_header_noise,
504 protations2[i-1],
505 flux, devnr, method, extrapol_enum));
506 } else {
507 // leave data and noise as they are and
508 // save them again unrotated
509 }
510
511 // save data and noise (if existing)
512 KMO_TRY_EXIT_IF_ERROR(
513 kmo_dfs_save_cube(data, ROTATE, "", sub_header_data, 0./0.));
514
515 if (desc1.ex_noise) {
516 KMO_TRY_EXIT_IF_ERROR(
517 kmo_dfs_save_cube(noise, ROTATE, "", sub_header_noise,
518 0./0.));
519 }
520
521 // free memory
522 cpl_imagelist_delete(data); data = NULL;
523 cpl_imagelist_delete(noise); noise = NULL;
524 } else {
525 // invalid IFU, just save sub_headers
526 KMO_TRY_EXIT_IF_ERROR(
527 kmo_dfs_save_sub_header(ROTATE, "", sub_header_data));
528
529 if (desc1.ex_noise) {
530 KMO_TRY_EXIT_IF_ERROR(
531 kmo_dfs_save_sub_header(ROTATE, "", sub_header_noise));
532 }
533 }
534
535 // free memory
536 cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
537 cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;
538 }
539 }
540 KMO_CATCH
541 {
542 KMO_CATCH_MSG();
543 ret_val = -1;
544 }
545
546 kmo_free_fits_desc(&desc1);
547 cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
548 cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;
549 cpl_imagelist_delete(data); data = NULL;
550 cpl_imagelist_delete(noise); noise = NULL;
551 cpl_vector_delete(rotations); rotations = NULL;
552 cpl_vector_delete(rotations2); rotations2 = NULL;
553
554 return ret_val;
555}
556
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.
Definition: kmo_rotate.c:110