KMOS Pipeline Reference Manual 4.5.10
kmos_extract_spec.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/*-----------------------------------------------------------------------------
25 * Includes
26 *----------------------------------------------------------------------------*/
27
28#include <string.h>
29#include <math.h>
30
31#include <cpl.h>
32
33#include "kmo_utils.h"
34#include "kmo_dfs.h"
35#include "kmo_error.h"
36#include "kmo_priv_extract_spec.h"
37#include "kmo_priv_functions.h"
38#include "kmo_cpl_extensions.h"
39#include "kmo_constants.h"
40#include "kmo_priv_fit_profile.h"
41#include "kmo_debug.h"
42
43/*-----------------------------------------------------------------------------
44 * Functions prototypes
45 *----------------------------------------------------------------------------*/
46
47static int kmos_extract_spec_create(cpl_plugin *);
48static int kmos_extract_spec_exec(cpl_plugin *);
49static int kmos_extract_spec_destroy(cpl_plugin *);
50static int kmos_extract_spec(cpl_parameterlist *, cpl_frameset *);
51
52/*-----------------------------------------------------------------------------
53 * Static variables
54 *----------------------------------------------------------------------------*/
55
56static char kmos_extract_spec_description[] =
57"This recipe extracts a spectrum from a datacube. The datacube is with or \n"
58"without noise). The output will be a similarly formatted FITS file.\n"
59"\n"
60"---------------------------------------------------------------------------\n"
61" Input files:\n"
62"\n"
63" DO KMOS \n"
64" category Type Explanation Required #Frames\n"
65" -------- ----- ----------- -------- -------\n"
66" <none or any> F3I The datacubes Y 1 \n"
67" <none or any> F2I The mask N 0,1 \n"
68"\n"
69" Output files:\n"
70"\n"
71" DO KMOS\n"
72" category Type Explanation\n"
73" -------- ----- -----------\n"
74" EXTRACT_SPEC F1I Extracted spectrum \n"
75" EXTRACT_SPEC_MASK F2I (optional, if --save_mask=true and \n"
76" --mask_method='optimal': The calculated mask) \n"
77"---------------------------------------------------------------------------\n"
78"\n";
79
80/*-----------------------------------------------------------------------------
81 * Functions code
82 *----------------------------------------------------------------------------*/
83
84/*----------------------------------------------------------------------------*/
88/*----------------------------------------------------------------------------*/
89
92/*----------------------------------------------------------------------------*/
101/*----------------------------------------------------------------------------*/
102int cpl_plugin_get_info(cpl_pluginlist *list)
103{
104 cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
105 cpl_plugin *plugin = &recipe->interface;
106
107 cpl_plugin_init(plugin,
108 CPL_PLUGIN_API,
109 KMOS_BINARY_VERSION,
110 CPL_PLUGIN_TYPE_RECIPE,
111 "kmos_extract_spec",
112 "Extract a spectrum from a cube",
113 kmos_extract_spec_description,
114 "Alex Agudo Berbel, Y. Jung",
115 "https://support.eso.org/",
116 kmos_get_license(),
117 kmos_extract_spec_create,
118 kmos_extract_spec_exec,
119 kmos_extract_spec_destroy);
120
121 cpl_pluginlist_append(list, plugin);
122 return 0;
123}
124
125/*----------------------------------------------------------------------------*/
133/*----------------------------------------------------------------------------*/
134static int kmos_extract_spec_create(cpl_plugin *plugin)
135{
136 cpl_recipe *recipe;
137 cpl_parameter *p;
138
139 /* Check that the plugin is part of a valid recipe */
140 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
141 recipe = (cpl_recipe *)plugin;
142 else
143 return -1;
144
145 /* Create the parameters list in the cpl_recipe object */
146 recipe->parameters = cpl_parameterlist_new();
147
148 /* Fill the parameters list */
149 /* --mask_method */
150 p = cpl_parameter_new_value("kmos.kmos_extract_spec.mask_method",
151 CPL_TYPE_STRING, "Method used : mask, integrated or optimal",
152 "kmos.kmos_extract_spec", "integrated");
153 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "mask_method");
154 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
155 cpl_parameterlist_append(recipe->parameters, p);
156
157 /* --centre */
158 p = cpl_parameter_new_value("kmos.kmos_extract_spec.centre",
159 CPL_TYPE_STRING, "The centre of the circular mask (pixel)",
160 "kmos.kmos_extract_spec", "7.5,7.5");
161 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "centre");
162 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
163 cpl_parameterlist_append(recipe->parameters, p);
164
165 /* --radius */
166 p = cpl_parameter_new_value("kmos.kmos_extract_spec.radius",
167 CPL_TYPE_DOUBLE, "The radius of the circular mask (pixel)",
168 "kmos.kmos_extract_spec", 3.0);
169 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "radius");
170 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
171 cpl_parameterlist_append(recipe->parameters, p);
172
173 /* --save_mask */
174 p = cpl_parameter_new_value("kmos.kmos_extract_spec.save_mask",
175 CPL_TYPE_BOOL, "Flag to save the mask", "kmos.kmos_extract_spec",
176 FALSE);
177 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "save_mask");
178 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
179 cpl_parameterlist_append(recipe->parameters, p);
180
181 /* --extra_outputs */
182 p = cpl_parameter_new_value("kmos.kmos_extract_spec.extra_outputs",
183 CPL_TYPE_BOOL, "Flag to save extra outputs",
184 "kmos.kmos_extract_spec", FALSE);
185 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extra_outputs");
186 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
187 cpl_parameterlist_append(recipe->parameters, p);
188
189 return kmos_combine_pars_create(recipe->parameters,
190 "kmos.kmos_extract_spec", DEF_REJ_METHOD, FALSE);
191}
192
193/*----------------------------------------------------------------------------*/
199/*----------------------------------------------------------------------------*/
200static int kmos_extract_spec_exec(cpl_plugin *plugin)
201{
202 cpl_recipe *recipe;
203
204 /* Get the recipe out of the plugin */
205 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
206 recipe = (cpl_recipe *)plugin;
207 else return -1;
208
209 return kmos_extract_spec(recipe->parameters, recipe->frames);
210}
211
212/*----------------------------------------------------------------------------*/
218/*----------------------------------------------------------------------------*/
219static int kmos_extract_spec_destroy(cpl_plugin *plugin)
220{
221 cpl_recipe *recipe;
222
223 /* Get the recipe out of the plugin */
224 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
225 recipe = (cpl_recipe *)plugin;
226 else return -1 ;
227
228 cpl_parameterlist_delete(recipe->parameters);
229 return 0 ;
230}
231
232/*----------------------------------------------------------------------------*/
246/*----------------------------------------------------------------------------*/
247static int kmos_extract_spec(
248 cpl_parameterlist * parlist,
249 cpl_frameset * frameset)
250{
251 const cpl_parameter * par ;
252 const char * mask_method ;
253 const char * cmethod ;
254 const char * centre_txt ;
255 cpl_vector * centre ;
256 int cmin, cmax, valid_ifu, citer, save_mask,
257 devnr1, extra_outputs;
258 int index_data ;
259 int index_noise;
260 int nr_devices ;
261 double cpos_rej, cneg_rej, r, x_lo, y_lo,
262 x_hi, y_hi, loc_cen_x, loc_cen_y ;
263 double cen_x = 0., cen_y = 0., radius = -1;
264 cpl_size auto_cen_x, auto_cen_y ;
265 cpl_imagelist * data_in ;
266 cpl_imagelist * noise_in ;
267 cpl_image * mask ;
268 cpl_image * made_data_img ;
269 cpl_vector * spec_data_out ;
270 cpl_vector * spec_noise_out ;
271 cpl_vector * fit_par ;
272 cpl_propertylist * sub_header_data ;
273 cpl_propertylist * sub_header_noise = NULL ;
274 cpl_propertylist * sub_header_mask ;
275 cpl_propertylist * fit_pl ;
276 cpl_frame * op1_frame ;
277 cpl_frame * op2_frame ;
278 float * pmask ;
279 main_fits_desc desc1, desc2;
280 int i, x, y ;
281
282 /* Check entries */
283 if (parlist == NULL || frameset == NULL) {
284 cpl_msg_error(__func__, "Null Inputs") ;
285 cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
286 return -1 ;
287 }
288
289 /* Initialise */
290 spec_data_out = spec_noise_out = NULL ;
291
292 /* Get parameters */
293 par = cpl_parameterlist_find_const(parlist,
294 "kmos.kmos_extract_spec.save_mask");
295 save_mask = cpl_parameter_get_bool(par);
296 par = cpl_parameterlist_find_const(parlist,
297 "kmos.kmos_extract_spec.extra_outputs");
298 extra_outputs = cpl_parameter_get_bool(par);
299 par = cpl_parameterlist_find_const(parlist,
300 "kmos.kmos_extract_spec.mask_method");
301 mask_method = cpl_parameter_get_string(par) ;
302 if (!strcmp(mask_method, "integrated")) {
303 par = cpl_parameterlist_find_const(parlist,
304 "kmos.kmos_extract_spec.centre");
305 centre_txt = cpl_parameter_get_string(par) ;
306 centre = kmo_identify_ranges(centre_txt);
307 if (cpl_vector_get_size(centre) != 2) {
308 cpl_msg_error(__func__, "centre must have 2 values like a,b") ;
309 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
310 return -1 ;
311 }
312 cen_x = cpl_vector_get(centre, 0);
313 cen_y = cpl_vector_get(centre, 1);
314 cpl_vector_delete(centre);
315 par = cpl_parameterlist_find_const(parlist,
316 "kmos.kmos_extract_spec.radius");
317 radius = cpl_parameter_get_double(par) ;
318 if (radius < 0.0) {
319 cpl_msg_error(__func__, "radius must be greater than 0.0") ;
320 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
321 return -1 ;
322 }
323 } else if (strcmp(mask_method, "mask") == 0) {
324 } else if (strcmp(mask_method, "optimal") == 0) {
325 kmos_combine_pars_load(parlist, "kmos.kmos_extract_spec", &cmethod,
326 &cpos_rej, &cneg_rej, &citer, &cmin, &cmax, FALSE);
327 } else {
328 cpl_msg_error(__func__, "Unsupported mask method: %s", mask_method) ;
329 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
330 return -1 ;
331 }
332
333 /* Identify the RAW and CALIB frames in the input frameset */
334 if (kmo_dfs_set_groups(frameset) != 1) {
335 cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
336 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
337 return -1 ;
338 }
339
340 /* Check Inputs */
341 if (cpl_frameset_get_size(frameset) != 1 &&
342 cpl_frameset_get_size(frameset) != 2) {
343 cpl_msg_error(__func__, "1 or 2 frames expected") ;
344 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
345 return -1 ;
346 }
347
348 /* Load frames */
349 op1_frame = kmo_dfs_get_frame(frameset, "0");
350 kmo_init_fits_desc(&desc1);
351 kmo_init_fits_desc(&desc2);
352 desc1 = kmo_identify_fits_header(cpl_frame_get_filename(op1_frame));
353 if (cpl_frameset_get_size(frameset) == 2) {
354 op2_frame = kmo_dfs_get_frame(frameset, "1");
355 desc2 = kmo_identify_fits_header(cpl_frame_get_filename(op2_frame));
356 } else {
357 op2_frame = NULL ;
358 }
359
360 /* --- load, update & save primary header --- */
361 cpl_propertylist * mh =
362 cpl_propertylist_load(cpl_frame_get_filename(op1_frame), 0) ;
363 kmo_dfs_save_main_header(frameset, EXTRACT_SPEC, "", op1_frame, mh,
364 parlist, cpl_func);
365 cpl_propertylist_delete(mh) ;
366
367 if (save_mask) {
368 kmo_dfs_save_main_header(frameset, EXTRACT_SPEC_MASK, "", op1_frame,
369 NULL, parlist, cpl_func);
370 }
371
372 /* Number of extensions to extract */
373 if (desc1.ex_noise == TRUE) {
374 nr_devices = desc1.nr_ext / 2;
375 } else {
376 nr_devices = desc1.nr_ext;
377 }
378
379 /* Loop on the devices */
380 for (i = 1; i <= nr_devices ; i++) {
381 if (desc1.ex_noise == FALSE) {
382 devnr1 = desc1.sub_desc[i - 1].device_nr;
383 } else {
384 devnr1 = desc1.sub_desc[2 * i - 1].device_nr;
385 }
386
387 if (desc1.ex_badpix == FALSE) {
388 index_data = kmo_identify_index_desc(desc1, devnr1, FALSE);
389 } else {
390 index_data = kmo_identify_index_desc(desc1, devnr1, 2);
391 }
392 if (desc1.ex_noise) {
393 index_noise = kmo_identify_index_desc(desc1, devnr1, TRUE);
394 } else {
395 index_noise = -1;
396 }
397 sub_header_data = kmo_dfs_load_sub_header(frameset, "0", devnr1, FALSE);
398
399 /* Check if IFU is valid */
400 valid_ifu = FALSE;
401 if (desc1.sub_desc[index_data-1].valid_data == TRUE) {
402 if ((strcmp(mask_method, "mask") != 0) ||
403 ((strcmp(mask_method, "mask") == 0) &&
404 (desc2.sub_desc[i - 1].valid_data == TRUE))) valid_ifu = TRUE;
405 }
406 if (desc1.ex_noise) {
407 sub_header_noise = kmo_dfs_load_sub_header(frameset, "0", devnr1,
408 TRUE);
409 }
410
411 if (valid_ifu) {
412 // load data
413 data_in = kmo_dfs_load_cube(frameset, "0", devnr1, FALSE);
414
415 // load noise, if existing
416 if (desc1.ex_noise && desc1.sub_desc[index_noise-1].valid_data) {
417 noise_in = kmo_dfs_load_cube(frameset, "0", devnr1, TRUE);
418 } else {
419 noise_in = NULL ;
420 }
421
422 /* Create the mask */
423 if (!strcmp(mask_method, "mask")) {
424 mask = kmo_dfs_load_image(frameset, "1",
425 desc2.sub_desc[i - 1].device_nr, FALSE, FALSE, NULL);
426 } else if (!strcmp(mask_method, "optimal")) {
427 kmclipm_make_image(data_in, NULL, &made_data_img, NULL, NULL,
428 cmethod, cpos_rej, cneg_rej, citer, cmax, cmin);
429 fit_par = kmo_fit_profile_2D(made_data_img, NULL, "gauss",
430 &mask, &fit_pl);
431
432 /* Update subheader with fit parameters */
433 cpl_propertylist_append(sub_header_data, fit_pl);
434 cpl_propertylist_delete(fit_pl);
435
436 /* Normalise mask */
437 cpl_image_subtract_scalar(mask, cpl_vector_get(fit_par, 0));
438 cpl_image_divide_scalar(mask, cpl_vector_get(fit_par, 1));
439 cpl_vector_delete(fit_par);
440 cpl_image_delete(made_data_img);
441 } else if (!strcmp(mask_method, "integrated")) {
442 if (cen_x < 1.0 || cen_y < 1.0) {
443 kmclipm_make_image(data_in, NULL, &made_data_img, NULL,
444 NULL, "median", 3.0, 3.0, 3, 1, 1);
445 cpl_image_get_maxpos(made_data_img,&auto_cen_x,&auto_cen_y);
446 loc_cen_x = (double)auto_cen_x - 1.0 ;
447 loc_cen_y = (double)auto_cen_y - 1.0 ;
448 cpl_image_delete(made_data_img);
449 } else {
450 loc_cen_x = cen_x - 1.0 ;
451 loc_cen_y = cen_y - 1.0 ;
452 }
453 mask = cpl_image_new(desc1.naxis1, desc1.naxis2,CPL_TYPE_FLOAT);
454 kmo_image_fill(mask,0.0);
455 pmask = cpl_image_get_data_float(mask);
456
457 /* draw circle */
458 x_lo = floor(loc_cen_x - radius);
459 if (x_lo < 0) x_lo = 0;
460 y_lo = floor(loc_cen_y - radius);
461 if (y_lo < 0) y_lo = 0;
462 x_hi = ceil(loc_cen_x + radius);
463 if (x_hi > desc1.naxis1) x_hi = desc1.naxis1;
464 y_hi = ceil(loc_cen_y + radius);
465 if (y_hi > desc1.naxis2) y_hi = desc1.naxis2;
466 for (x = x_lo; x < x_hi; x++) {
467 for (y = y_lo; y < y_hi; y++) {
468 r = sqrt(pow(x - loc_cen_x,2) + pow(y - loc_cen_y,2));
469 if (r <= radius) pmask[x + y * desc1.naxis1] = 1.0;
470 }
471 }
472 }
473
474 /* Process & save data */
475 kmo_priv_extract_spec(data_in, noise_in, mask, &spec_data_out,
476 &spec_noise_out);
477
478 sub_header_mask = cpl_propertylist_duplicate(sub_header_data);
479
480 /* Change WCS here (CRPIX3 goes to CRPIX1 etc...) */
481 sub_header_data = kmo_priv_update_header(sub_header_data);
482
483 kmclipm_vector *ddd = kmclipm_vector_create(spec_data_out);
484 kmo_dfs_save_vector(ddd, EXTRACT_SPEC, "", sub_header_data, 0./0.);
485 kmclipm_vector_delete(ddd);
486 if (save_mask) {
487 /* Delete WCS for 3rd dimension since mask is 2D */
488 cpl_propertylist_erase(sub_header_mask, CRPIX3);
489 cpl_propertylist_erase(sub_header_mask, CRVAL3);
490 cpl_propertylist_erase(sub_header_mask, CDELT3);
491 cpl_propertylist_erase(sub_header_mask, CTYPE3);
492 cpl_propertylist_erase(sub_header_mask, CD1_3);
493 cpl_propertylist_erase(sub_header_mask, CD2_3);
494 cpl_propertylist_erase(sub_header_mask, CD3_3);
495 cpl_propertylist_erase(sub_header_mask, CD3_1);
496 cpl_propertylist_erase(sub_header_mask, CD3_2);
497 kmo_dfs_save_image(mask, EXTRACT_SPEC_MASK, "",
498 sub_header_mask, 0.);
499 }
500 cpl_propertylist_delete(sub_header_mask);
501
502 /* Process & save noise, if existing */
503 if (desc1.ex_noise && sub_header_noise) {
504 kmclipm_vector *nnn = NULL;
505 if (spec_noise_out != NULL) {
506 nnn = kmclipm_vector_create(spec_noise_out);
507 }
508 sub_header_noise = kmo_priv_update_header(sub_header_noise);
509
510 kmo_dfs_save_vector(nnn, EXTRACT_SPEC, "", sub_header_noise,
511 0./0.);
512 kmclipm_vector_delete(nnn);
513 }
514 cpl_imagelist_delete(data_in);
515 cpl_imagelist_delete(noise_in);
516 cpl_image_delete(mask);
517 } else {
518 /* Invalid IFU */
519 kmo_dfs_save_sub_header(EXTRACT_SPEC, "", sub_header_data);
520 if (desc1.ex_noise) {
521 kmo_dfs_save_sub_header(EXTRACT_SPEC, "", sub_header_noise);
522 }
523 }
524 cpl_propertylist_delete(sub_header_data);
525 if (desc1.ex_noise && sub_header_noise) {
526 cpl_propertylist_delete(sub_header_noise);
527 }
528 }
529 kmo_free_fits_desc(&desc1);
530 kmo_free_fits_desc(&desc2);
531
532 /* Save extra outputs if needed */
533 if (extra_outputs) {
534 kmos_idp_save_extra_outputs(frameset, parlist, cpl_func, EXTRACT_SPEC) ;
535 }
536
537 return 0 ;
538}
539
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.