KMOS Pipeline Reference Manual 4.5.10
kmo_shift.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_shift.h>
36
37static int kmo_shift_create(cpl_plugin *);
38static int kmo_shift_exec(cpl_plugin *);
39static int kmo_shift_destroy(cpl_plugin *);
40static int kmo_shift(cpl_parameterlist *, cpl_frameset *);
41
42static char kmo_shift_description[] =
43"This recipe shifts a cube spatially. A positive x-shift shifts the data to the\n"
44"left, a positive y-shift shifts upwards, where a shift of one pixel equals\n"
45"0.2arcsec. The output will still have the same dimensions, but the borders \n"
46"will be filled with NaNs accordingly.\n"
47"\n"
48"BASIC PARAMETERS:\n"
49"-----------------\n"
50"--shifts\n"
51"This parameter must be supplied. It contains the amount of shift to apply. The\n"
52"unit is in arcsec. If the --shifts parameter contains only two values (x,y),\n"
53"all IFUs will be shifted by the same amount. If it contains 48 values\n"
54"(x1,y1;x2,y2;...;x24,y24), the IFUs are shifted individually.\n"
55"\n"
56"--imethod\n"
57"The interpolation method to apply when the shift value isn’t a multiple of the\n"
58"pixel scale. There are two methods available:\n"
59" * BCS: Bicubic spline\n"
60" * NN: Nearest Neighbor\n"
61"\n"
62"--ifu\n"
63"If a single IFU should be shifted, it can be defined using the --ifu parameter\n"
64"(--shifts parameter contains only two values).\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 no extrapolation is applied. This means that the output frame will\n"
73"shrink at most one pixel, because the data is shifted out of the frame. When\n"
74"turning extrapolation on, the size of the output frame stays the same as for\n"
75"the input frame.\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" SHIFT F3I Shifted 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_shift",
120 "Shift a cube spatially",
121 kmo_shift_description,
122 "Alex Agudo Berbel",
123 "https://support.eso.org/",
124 kmos_get_license(),
125 kmo_shift_create,
126 kmo_shift_exec,
127 kmo_shift_destroy);
128
129 cpl_pluginlist_append(list, plugin);
130
131 return 0;
132}
133
141static int kmo_shift_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_shift.imethod",
158 CPL_TYPE_STRING,
159 "Method to use for interpolation.\n"
160 "[\"BCS\" (bicubic spline, default), "
161 "\"NN\" (nearest neighbor)]",
162 "kmos.kmo_shift",
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_shift.extrapolate",
170 CPL_TYPE_BOOL,
171 "Applies only to 'method=BCS' when doing sub-"
172 "pixel shifts: "
173 "FALSE: shifted IFU will be filled with NaN's "
174 "at the borders,"
175 "TRUE: shifted IFU will be extrapolated at "
176 "the borders",
177 "kmos.kmo_shift",
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 /* --shifts */
184 p = cpl_parameter_new_value("kmos.kmo_shift.shifts",
185 CPL_TYPE_STRING,
186 "The shifts for each spatial dimension for all "
187 "specified IFUs."
188 "\"x1,y1;x2,y2;...\" (arcsec)",
189 "kmos.kmo_shift",
190 "");
191 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "shifts");
192 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
193 cpl_parameterlist_append(recipe->parameters, p);
194
195 /* --ifu */
196 p = cpl_parameter_new_value("kmos.kmo_shift.ifu",
197 CPL_TYPE_INT,
198 "The IFU to shift [1 to 24] or shift all IFUs "
199 "Default value of 0 applies shift to all IFUs.",
200 "kmos.kmo_shift",
201 0);
202 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ifu");
203 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
204 cpl_parameterlist_append(recipe->parameters, p);
205
206 /* --flux */
207 p = cpl_parameter_new_value("kmos.kmo_shift.flux",
208 CPL_TYPE_BOOL,
209 "Apply flux conservation: "
210 "(TRUE (apply) or "
211 "FALSE (don't apply)",
212 "kmos.kmo_shift",
213 FALSE);
214 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
215 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
216 cpl_parameterlist_append(recipe->parameters, p);
217
218 return 0;
219}
220
226static int kmo_shift_exec(cpl_plugin *plugin)
227{
228 cpl_recipe *recipe;
229
230 /* Get the recipe out of the plugin */
231 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
232 recipe = (cpl_recipe *)plugin;
233 else return -1 ;
234
235 return kmo_shift(recipe->parameters, recipe->frames);
236}
237
243static int kmo_shift_destroy(cpl_plugin *plugin)
244{
245 cpl_recipe *recipe;
246
247 /* Get the recipe out of the plugin */
248 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
249 recipe = (cpl_recipe *)plugin;
250 else return -1 ;
251
252 cpl_parameterlist_delete(recipe->parameters);
253 return 0 ;
254}
255
270static int kmo_shift(cpl_parameterlist *parlist, cpl_frameset *frameset)
271{
272 const char *method = NULL,
273 *shifts_txt = NULL;
274
275 cpl_imagelist *data = NULL,
276 *noise = NULL;
277
278 cpl_vector *shifts = NULL,
279 *shifts2 = NULL;
280
281 int ret_val = 0,
282 nr_devices = 0,
283 i = 0,
284 valid_ifu = FALSE,
285 flux = FALSE,
286 size = 0,
287 ifu = 0,
288 extrapolate = 0,
289 devnr = 0,
290 index_data = 0,
291 index_noise = 0;
292
293 enum extrapolationType extrapol_enum = 0;
294
295 const double *pshifts2 = NULL;
296
297 cpl_propertylist *sub_header_data = NULL,
298 *sub_header_noise = NULL;
299
300 cpl_frame *frame = NULL;
301
302 main_fits_desc desc1;
303
304 KMO_TRY
305 {
306 kmo_init_fits_desc(&desc1);
307
308 /* --- check input --- */
309 KMO_TRY_ASSURE((parlist != NULL) &&
310 (frameset != NULL),
311 CPL_ERROR_NULL_INPUT,
312 "Not all input data is provided!");
313
314 KMO_TRY_ASSURE(cpl_frameset_get_size(frameset) == 1,
315 CPL_ERROR_NULL_INPUT,
316 "A cube must be provided!");
317
318 KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset) == 1,
319 CPL_ERROR_ILLEGAL_INPUT,
320 "Cannot identify RAW and CALIB frames!");
321
322 cpl_msg_info("", "--- Parameter setup for kmo_shift ---------");
323
324 KMO_TRY_EXIT_IF_NULL(
325 method = kmo_dfs_get_parameter_string(parlist,
326 "kmos.kmo_shift.imethod"));
327 KMO_TRY_EXIT_IF_ERROR(
328 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_shift.imethod"));
329
330 extrapolate = kmo_dfs_get_parameter_bool(parlist,
331 "kmos.kmo_shift.extrapolate");
332 KMO_TRY_CHECK_ERROR_STATE();
333
334 if (strcmp(method, "NN") == 0) {
335 extrapol_enum = NONE_NANS;
336 } else if (strcmp(method, "BCS") == 0) {
337 if (extrapolate == FALSE) {
338 extrapol_enum = NONE_NANS;
339 } else if (extrapolate == TRUE) {
340 extrapol_enum = BCS_NATURAL;
341 } else {
342 KMO_TRY_ASSURE(1 == 0,
343 CPL_ERROR_ILLEGAL_INPUT,
344 "extrapolate must either FALSE or TRUE!");
345 }
346 } else {
347 KMO_TRY_ASSURE(1 == 0,
348 CPL_ERROR_ILLEGAL_INPUT,
349 "method must be either \"BCS\" or \"NN\" !");
350 }
351
352 KMO_TRY_EXIT_IF_ERROR(
353 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_shift.extrapolate"));
354
355 shifts_txt = kmo_dfs_get_parameter_string(parlist,
356 "kmos.kmo_shift.shifts");
357 KMO_TRY_CHECK_ERROR_STATE();
358 KMO_TRY_EXIT_IF_ERROR(
359 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_shift.shifts"));
360
361 KMO_TRY_ASSURE(strcmp(shifts_txt, "") != 0,
362 CPL_ERROR_ILLEGAL_INPUT,
363 "At least two values for --shifts parameter must be "
364 "provided!");
365
366 shifts = kmo_identify_ranges(shifts_txt);
367 KMO_TRY_CHECK_ERROR_STATE();
368
369 size = cpl_vector_get_size(shifts);
370 KMO_TRY_CHECK_ERROR_STATE();
371
372 KMO_TRY_ASSURE((size % 2) == 0,
373 CPL_ERROR_ILLEGAL_INPUT,
374 "shifts parameter must have an even number of elements!");
375
376 ifu = kmo_dfs_get_parameter_int(parlist, "kmos.kmo_shift.ifu");
377 KMO_TRY_CHECK_ERROR_STATE();
378 KMO_TRY_EXIT_IF_ERROR(
379 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_shift.ifu"));
380
381 if (ifu == 0) {
382 // shift all IFUs the same or different amounts
383 KMO_TRY_ASSURE((size == 2) || (size == 2*KMOS_NR_IFUS),
384 CPL_ERROR_ILLEGAL_INPUT,
385 "shifts parameter must have exactly 2 elements"
386 "(shift all IFUs the same amount) or 48 elements "
387 "(shift all IFUs individually)!");
388 } else {
389 // shift only one specific IFU
390 KMO_TRY_ASSURE(size == 2,
391 CPL_ERROR_ILLEGAL_INPUT,
392 "shifts parameter must have exactly 2 elements to "
393 "shift a single IFU!");
394 }
395
396 // setup a vector of length 48 regardless of how many IFUs to shift
397 if (size == 2*KMOS_NR_IFUS) {
398 KMO_TRY_EXIT_IF_NULL(
399 shifts2 = cpl_vector_duplicate(shifts));
400 } else {
401 KMO_TRY_EXIT_IF_NULL(
402 shifts2 = cpl_vector_new(2*KMOS_NR_IFUS));
403 KMO_TRY_EXIT_IF_NULL(
404 pshifts2 = cpl_vector_get_data_const(shifts));
405 for (i = 0; i < KMOS_NR_IFUS; i++) {
406 cpl_vector_set(shifts2, 2*i, pshifts2[0]);
407 cpl_vector_set(shifts2, 2*i+1, pshifts2[1]);
408 }
409 }
410
411 KMO_TRY_EXIT_IF_NULL(
412 pshifts2 = cpl_vector_get_data_const(shifts2));
413
414 flux = kmo_dfs_get_parameter_bool(parlist,
415 "kmos.kmo_shift.flux");
416 KMO_TRY_CHECK_ERROR_STATE();
417 KMO_TRY_EXIT_IF_ERROR(
418 kmo_dfs_print_parameter_help(parlist, "kmos.kmo_shift.flux"));
419
420 KMO_TRY_ASSURE((flux == TRUE) || (flux == FALSE),
421 CPL_ERROR_ILLEGAL_INPUT,
422 "flux must be TRUE or FALSE!");
423
424 KMO_TRY_CHECK_ERROR_STATE();
425
426 cpl_msg_info("", "-------------------------------------------");
427
428 KMO_TRY_ASSURE((flux == 0) ||
429 (flux == 1),
430 CPL_ERROR_ILLEGAL_INPUT,
431 "flux must be either 0 or 1 !");
432
433 // load descriptor of first operand
434 KMO_TRY_EXIT_IF_NULL(
435 frame = kmo_dfs_get_frame(frameset, "0"));
436
437 desc1 = kmo_identify_fits_header(
438 cpl_frame_get_filename(frame));
439 KMO_TRY_CHECK_ERROR_STATE_MSG("Provided fits file doesn't seem to be "
440 "in KMOS-format!");
441
442 KMO_TRY_ASSURE(desc1.fits_type == f3i_fits,
443 CPL_ERROR_ILLEGAL_INPUT,
444 "First input file hasn't correct data type "
445 "(KMOSTYPE must be F3I)!");
446
447 // --- load, update & save primary header ---
448 KMO_TRY_EXIT_IF_ERROR(
449 kmo_dfs_save_main_header(frameset, SHIFT, "", frame, NULL,
450 parlist, cpl_func));
451
452 // --- load data ---
453 if (desc1.ex_noise == TRUE) {
454 nr_devices = desc1.nr_ext / 2;
455 } else {
456 nr_devices = desc1.nr_ext;
457 }
458
459 for (i = 1; i <= nr_devices; i++) {
460 if (desc1.ex_noise == FALSE) {
461 devnr = desc1.sub_desc[i - 1].device_nr;
462 } else {
463 devnr = desc1.sub_desc[2 * i - 1].device_nr;
464 }
465
466 if (desc1.ex_badpix == FALSE) {
467 index_data = kmo_identify_index_desc(desc1, devnr, FALSE);
468 } else {
469 index_data = kmo_identify_index_desc(desc1, devnr, 2);
470 }
471 KMO_TRY_CHECK_ERROR_STATE();
472
473 if (desc1.ex_noise) {
474 index_noise = kmo_identify_index_desc(desc1, devnr, TRUE);
475 }
476 KMO_TRY_CHECK_ERROR_STATE();
477
478 KMO_TRY_EXIT_IF_NULL(
479 sub_header_data = kmo_dfs_load_sub_header(frameset, "0", devnr,
480 FALSE));
481
482 // check if IFU is valid
483 valid_ifu = FALSE;
484 if (desc1.sub_desc[index_data-1].valid_data == TRUE) {
485 valid_ifu = TRUE;
486 }
487
488 if (desc1.ex_noise) {
489 // load noise anyway since we have to save it in the output
490 KMO_TRY_EXIT_IF_NULL(
491 sub_header_noise = kmo_dfs_load_sub_header(frameset, "0",
492 devnr, TRUE));
493 }
494
495 if (valid_ifu) {
496 // load data
497 KMO_TRY_EXIT_IF_NULL(
498 data = kmo_dfs_load_cube(frameset, "0", devnr, FALSE));
499
500 // load noise, if existing
501 if (desc1.ex_noise && desc1.sub_desc[index_noise-1].valid_data) {
502 KMO_TRY_EXIT_IF_NULL(
503 noise = kmo_dfs_load_cube(frameset, "0", devnr, TRUE));
504 }
505
506 if ((ifu == 0) || (ifu == devnr)) {
507 // process here
508 KMO_TRY_EXIT_IF_ERROR(
509 kmo_priv_shift(&data, &noise,
510 &sub_header_data, &sub_header_noise,
511 pshifts2[2*i-2] / KMOS_PIX_RESOLUTION,
512 pshifts2[2*i-1] / KMOS_PIX_RESOLUTION,
513 flux, method, extrapol_enum));
514 } else {
515 // leave data and noise as they are and
516 // save them again unshifted
517 }
518
519 // save data and noise (if existing)
520 KMO_TRY_EXIT_IF_ERROR(
521 kmo_dfs_save_cube(data, SHIFT, "", sub_header_data, 0./0.));
522
523 if (desc1.ex_noise) {
524 KMO_TRY_EXIT_IF_ERROR(
525 kmo_dfs_save_cube(noise, SHIFT, "", sub_header_noise, 0./0.));
526 }
527
528 // free memory
529 cpl_imagelist_delete(data); data = NULL;
530 cpl_imagelist_delete(noise); noise = NULL;
531 } else {
532 // invalid IFU, just save sub_headers
533 KMO_TRY_EXIT_IF_ERROR(
534 kmo_dfs_save_sub_header(SHIFT, "", sub_header_data));
535
536 if (desc1.ex_noise == TRUE) {
537 KMO_TRY_EXIT_IF_ERROR(
538 kmo_dfs_save_sub_header(SHIFT, "", sub_header_noise));
539 }
540 }
541
542 // free memory
543 cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
544 cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;
545 }
546 }
547 KMO_CATCH
548 {
549 KMO_CATCH_MSG();
550 ret_val = -1;
551 }
552
553 kmo_free_fits_desc(&desc1);
554 cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
555 cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;
556 cpl_imagelist_delete(data); data = NULL;
557 cpl_imagelist_delete(noise); noise = NULL;
558 cpl_vector_delete(shifts); shifts = NULL;
559 cpl_vector_delete(shifts2); shifts2 = NULL;
560
561 return ret_val;
562}
563
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.
Definition: kmo_shift.c:110