KMOS Pipeline Reference Manual 4.5.10
kmos_illumination.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 <math.h>
29#include <string.h>
30
31#include <cpl.h>
32
33#include "kmclipm_priv_splines.h"
34
35#include "kmo_priv_reconstruct.h"
36#include "kmo_priv_functions.h"
37#include "kmo_priv_flat.h"
38#include "kmo_priv_wave_cal.h"
39#include "kmo_priv_combine.h"
40#include "kmo_functions.h"
41#include "kmo_cpl_extensions.h"
42#include "kmo_dfs.h"
43#include "kmos_pfits.h"
44#include "kmo_error.h"
45#include "kmo_constants.h"
46#include "kmo_debug.h"
47
48/*-----------------------------------------------------------------------------
49 * Functions prototypes
50 *----------------------------------------------------------------------------*/
51
52static int kmos_illumination_one_angle(
53 cpl_frameset * frameset,
54 int rotangle,
55 const char * do_catg_used,
56 int reduce_det,
57 double pix_scale,
58 double neighborhoodRange,
59 const char * method,
60 const char * cmethod,
61 double cpos_rej,
62 double cneg_rej,
63 int citer,
64 int cmin,
65 int cmax,
66 int flux,
67 const char * ranges_txt,
68 const char * suffix,
69 const char * fn_suffix,
70 int has_flat_edge,
71 int add_all_sky) ;
72
73static int kmos_illumination_check_inputs(cpl_frameset *, const char *, int,
74 int *, int *, int *) ;
75static cpl_table ** kmos_illumination_edge_shift_correct(cpl_image *,
76 cpl_image *, int, const cpl_image *, int, cpl_array *,
77 const char *, double) ;
78
79static int kmos_illumination_create(cpl_plugin *);
80static int kmos_illumination_exec(cpl_plugin *);
81static int kmos_illumination_destroy(cpl_plugin *);
82static int kmos_illumination(cpl_parameterlist *, cpl_frameset *);
83
84/*-----------------------------------------------------------------------------
85 * Static variables
86 *----------------------------------------------------------------------------*/
87
88static char kmos_illumination_description[] =
89"This recipe creates the spatial non-uniformity calibration frame needed for\n"
90"all three detectors. It must be called after the kmo_wave_cal-recipe, which\n"
91"generates the spectral calibration frame needed in this recipe. As input at\n"
92"least a sky or flat, a master dark, a master flat and the spatial and \n"
93"spectral calibration frames are required.\n"
94"The created product, the illumination correction, can be used as input for\n"
95"kmo_std_star and kmo_sci_red.\n"
96"\n"
97"---------------------------------------------------------------------------\n"
98" Input files:\n"
99"\n"
100" DO CATG Type Explanation Required #Frames\n"
101" -------- ----- ----------- -------- -------\n"
102" FLAT_SKY F2D Sky exposures Y 1-n \n"
103" (at least 3 frames recommended) \n"
104"or FLAT_ON F2D Flat exposures Y 1-n \n"
105" (at least 3 frames recommended) \n"
106" MASTER_DARK F2D Master dark Y 1 \n"
107" MASTER_FLAT F2D Master flat Y 1 \n"
108" XCAL F2D x calibration frame Y 1 \n"
109" YCAL F2D y calibration frame Y 1 \n"
110" LCAL F2D Wavelength calib. frame Y 1 \n"
111" WAVE_BAND F2L Table with start-/end-wavelengths Y 1 \n"
112" FLAT_EDGE F2L Table with fitted slitlet edges N 0,1 \n"
113"\n"
114" Output files:\n"
115"\n"
116" DO CATG Type Explanation\n"
117" -------- ----- -----------\n"
118" ILLUM_CORR F2I Illumination calibration frame \n"
119" If FLAT_EDGE is provided: \n"
120" SKYFLAT_EDGE F2L Frame containing parameters of fitted \n"
121" slitlets of all IFUs of all detectors\n"
122"---------------------------------------------------------------------------\n"
123"\n";
124
125/*-----------------------------------------------------------------------------
126 * Functions code
127 *----------------------------------------------------------------------------*/
128
129/*----------------------------------------------------------------------------*/
134/*----------------------------------------------------------------------------*/
135
138/*----------------------------------------------------------------------------*/
147/*----------------------------------------------------------------------------*/
148int cpl_plugin_get_info(cpl_pluginlist *list)
149{
150 cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
151 cpl_plugin *plugin = &recipe->interface;
152
153 cpl_plugin_init(plugin,
154 CPL_PLUGIN_API,
155 KMOS_BINARY_VERSION,
156 CPL_PLUGIN_TYPE_RECIPE,
157 "kmos_illumination",
158 "Create a frame to correct spatial non-uniformity of flatfield",
159 kmos_illumination_description,
160 "Alex Agudo Berbel, Yves Jung",
161 "https://support.eso.org/",
162 kmos_get_license(),
163 kmos_illumination_create,
164 kmos_illumination_exec,
165 kmos_illumination_destroy);
166 cpl_pluginlist_append(list, plugin);
167
168 return 0;
169}
170
171/*----------------------------------------------------------------------------*/
179/*----------------------------------------------------------------------------*/
180static int kmos_illumination_create(cpl_plugin *plugin)
181{
182 cpl_recipe *recipe;
183 cpl_parameter *p;
184
185 /* Check that the plugin is part of a valid recipe */
186 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
187 recipe = (cpl_recipe *)plugin;
188 else
189 return -1;
190
191 /* Create the parameters list in the cpl_recipe object */
192 recipe->parameters = cpl_parameterlist_new();
193
194 /* Fill the parameters list */
195 /* --used_flat_type */
196 p = cpl_parameter_new_value("kmos.kmos_illumination.used_flat_type",
197 CPL_TYPE_STRING,
198 "Type (sky/lamp) of input to use: (only if 2 types in input)",
199 "kmos.kmos_illumination", "sky");
200 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "used_flat_type");
201 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
202 cpl_parameterlist_append(recipe->parameters, p);
203
204 /* --imethod */
205 p = cpl_parameter_new_value("kmos.kmos_illumination.imethod",
206 CPL_TYPE_STRING,
207 "Method to use for interpolation: "
208 "[\"NN\" (nearest neighbour), "
209 "\"lwNN\" (linear weighted nearest neighbor), "
210 "\"swNN\" (square weighted nearest neighbor), "
211 "\"MS\" (Modified Shepard's method), "
212 "\"CS\" (Cubic spline)]",
213 "kmos.kmos_illumination", "CS");
214 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "imethod");
215 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
216 cpl_parameterlist_append(recipe->parameters, p);
217
218 /* --neighborhoodRange */
219 p = cpl_parameter_new_value("kmos.kmos_illumination.neighborhoodRange",
220 CPL_TYPE_DOUBLE, "Range (pixels) to search for neighbors",
221 "kmos.kmos_illumination", 1.001);
222 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "neighborhoodRange");
223 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
224 cpl_parameterlist_append(recipe->parameters, p);
225
226 /* --range */
227 p = cpl_parameter_new_value("kmos.kmos_illumination.range",
228 CPL_TYPE_STRING,
229 "The spectral ranges to combine when collapsing the reconstructed cubes. e.g. " "\"x1_start,x1_end;x2_start,x2_end\" (microns)",
230 "kmos.kmos_illumination", "");
231 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "range");
232 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
233 cpl_parameterlist_append(recipe->parameters, p);
234
235 /* --flux */
236 p = cpl_parameter_new_value("kmos.kmos_illumination.flux",
237 CPL_TYPE_BOOL, "TRUE: Apply flux conservation. FALSE: otherwise",
238 "kmos.kmos_illumination", FALSE);
239 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
240 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
241 cpl_parameterlist_append(recipe->parameters, p);
242
243 /* --add-all */
244 p = cpl_parameter_new_value("kmos.kmos_illumination.add-all", CPL_TYPE_BOOL,
245 "FALSE: omit 1st FLAT_SKY frame (acquisition), "
246 "TRUE: don't perform any checks, add them all",
247 "kmos.kmos_illumination", FALSE);
248 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "add-all");
249 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
250 cpl_parameterlist_append(recipe->parameters, p);
251
252 /* --pix_scale */
253 p = cpl_parameter_new_value("kmos.kmos_illumination.pix_scale",
254 CPL_TYPE_DOUBLE,
255 "Change the pixel scale [arcsec]. "
256 "Default of 0.2\" results into cubes of 14x14pix, "
257 "a scale of 0.1\" results into cubes of 28x28pix, etc.",
258 "kmos.kmos_illumination", KMOS_PIX_RESOLUTION);
259 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "pix_scale");
260 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
261 cpl_parameterlist_append(recipe->parameters, p);
262
263 /* --suppress_extension */
264 p = cpl_parameter_new_value("kmos.kmos_illumination.suppress_extension",
265 CPL_TYPE_BOOL,
266 "Suppress filename extension. (TRUE (apply) or FALSE (don't apply)",
267 "kmos.kmos_illumination", FALSE);
268 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
269 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
270 cpl_parameterlist_append(recipe->parameters, p);
271
272 /* Add parameters for band-definition */
273 kmos_band_pars_create(recipe->parameters, "kmos.kmos_illumination");
274
275 /* Add parameters for combining */
276 kmos_combine_pars_create(recipe->parameters, "kmos.kmos_illumination",
277 DEF_REJ_METHOD, FALSE);
278
279 /* --detector */
280 p = cpl_parameter_new_value("kmos.kmos_illumination.detector",
281 CPL_TYPE_INT, "Only reduce the specified detector",
282 "kmos.kmos_illumination", 0);
283 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "det");
284 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
285 cpl_parameterlist_append(recipe->parameters, p);
286
287 /* --angle */
288 p = cpl_parameter_new_value("kmos.kmos_illumination.angle",
289 CPL_TYPE_DOUBLE, "Only reduce the specified angle",
290 "kmos.kmos_illumination", 370.0);
291 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "angle");
292 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
293 cpl_parameterlist_append(recipe->parameters, p);
294
295 return 0 ;
296}
297
298/*----------------------------------------------------------------------------*/
304/*----------------------------------------------------------------------------*/
305static int kmos_illumination_exec(cpl_plugin *plugin)
306{
307 cpl_recipe *recipe;
308
309 /* Get the recipe out of the plugin */
310 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
311 recipe = (cpl_recipe *)plugin;
312 else return -1;
313
314 return kmos_illumination(recipe->parameters, recipe->frames);
315}
316
317/*----------------------------------------------------------------------------*/
323/*----------------------------------------------------------------------------*/
324static int kmos_illumination_destroy(cpl_plugin *plugin)
325{
326 cpl_recipe *recipe;
327
328 /* Get the recipe out of the plugin */
329 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
330 recipe = (cpl_recipe *)plugin;
331 else return -1 ;
332
333 cpl_parameterlist_delete(recipe->parameters);
334 return 0 ;
335}
336
337/*----------------------------------------------------------------------------*/
350/*----------------------------------------------------------------------------*/
351static int kmos_illumination(
352 cpl_parameterlist * parlist,
353 cpl_frameset * frameset)
354{
355 const cpl_parameter * par ;
356 const char * method ;
357 const char * used_flat_type ;
358 const char * cmethod ;
359 const char * cmethod_loc ;
360 const char * ranges_txt ;
361 int flux, add_all_sky, cmax, cmin, citer,
362 suppress_extension, reduce_det,
363 has_flat_edge ;
364 double neighborhoodRange, pix_scale, cpos_rej, cneg_rej,
365 reduce_angle ;
366 int nb_raw_sky, nb_raw_lamp ;
367 const char * do_catg_used ;
368 int * angles_array ;
369 int a, nb_angles ;
370 cpl_frameset * angle_frameset ;
371 char * suffix ;
372 char * fn_suffix ;
373 cpl_frame * frame ;
374 cpl_propertylist * main_header ;
375
376 /* Check initial Entries */
377 if (kmos_check_and_set_groups(frameset) != CPL_ERROR_NONE) {
378 return cpl_error_get_code();
379 }
380
381 /* Get parameters */
382 par = cpl_parameterlist_find_const(parlist,
383 "kmos.kmos_illumination.used_flat_type") ;
384 used_flat_type = cpl_parameter_get_string(par);
385 par = cpl_parameterlist_find_const(parlist,
386 "kmos.kmos_illumination.imethod") ;
387 method = cpl_parameter_get_string(par);
388 par = cpl_parameterlist_find_const(parlist,
389 "kmos.kmos_illumination.neighborhoodRange");
390 neighborhoodRange = cpl_parameter_get_double(par) ;
391 par = cpl_parameterlist_find_const(parlist, "kmos.kmos_illumination.range");
392 ranges_txt = cpl_parameter_get_string(par) ;
393 par = cpl_parameterlist_find_const(parlist, "kmos.kmos_illumination.flux");
394 flux = cpl_parameter_get_bool(par);
395 par = cpl_parameterlist_find_const(parlist,
396 "kmos.kmos_illumination.add-all");
397 add_all_sky = cpl_parameter_get_bool(par) ;
398 par = cpl_parameterlist_find_const(parlist,
399 "kmos.kmos_illumination.pix_scale");
400 pix_scale = cpl_parameter_get_double(par) ;
401 par = cpl_parameterlist_find_const(parlist,
402 "kmos.kmos_illumination.suppress_extension");
403 suppress_extension = cpl_parameter_get_bool(par) ;
404 par = cpl_parameterlist_find_const(parlist,
405 "kmos.kmos_illumination.detector");
406 reduce_det = cpl_parameter_get_int(par);
407 par = cpl_parameterlist_find_const(parlist, "kmos.kmos_illumination.angle");
408 reduce_angle = cpl_parameter_get_double(par);
409
410 kmos_band_pars_load(parlist, "kmos.kmos_illumination");
411 kmos_combine_pars_load(parlist, "kmos.kmos_illumination", &cmethod,
412 &cpos_rej, &cneg_rej, &citer, &cmin, &cmax, FALSE);
413
414 /* Check Parameters */
415 if (strcmp(used_flat_type, "sky") && strcmp(used_flat_type, "lamp")) {
416 cpl_msg_error(__func__, "used_flat_type must be \"lamp\" or \"sky\"") ;
417 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
418 return -1 ;
419 }
420 if (strcmp(method, "NN") && strcmp(method, "lwNN") && strcmp(method, "swNN")
421 && strcmp(method, "MS") && strcmp(method, "CS")) {
422 cpl_msg_error(__func__,
423 "method must be \"NN\", \"lwNN\", \"swNN\", \"MS\" or \"CS\"") ;
424 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
425 return -1 ;
426 }
427 if (neighborhoodRange <= 0.0) {
428 cpl_msg_error(__func__, "neighborhoodRange must be > 0") ;
429 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
430 return -1 ;
431 }
432 if (pix_scale < 0.01 || pix_scale > 0.4) {
433 cpl_msg_error(__func__,
434 "pix_scale must be in [0.01,0.4] -> 7x7 to 280x280 pixels") ;
435 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
436 return -1 ;
437 }
438 if (reduce_det < 0 || reduce_det > 3) {
439 cpl_msg_error(__func__, "detector must be in [1,3]") ;
440 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
441 return -1 ;
442 }
443
444 has_flat_edge = cpl_frameset_count_tags(frameset, FLAT_EDGE);
445
446 /* Determine the used rawfiles */
447 nb_raw_sky = cpl_frameset_count_tags(frameset, FLAT_SKY);
448 nb_raw_lamp = cpl_frameset_count_tags(frameset, FLAT_ON);
449 if (nb_raw_sky == 0 && nb_raw_lamp == 0) {
450 cpl_msg_error(__func__, "Input frameset has no RAW frame") ;
451 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
452 return -1 ;
453 }
454 if (nb_raw_sky == 0 && nb_raw_lamp > 0) {
455 do_catg_used = FLAT_ON ;
456 }
457 if (nb_raw_sky > 0 && nb_raw_lamp == 0) {
458 do_catg_used = FLAT_SKY ;
459 }
460 if (nb_raw_sky > 0 && nb_raw_lamp > 0) {
461 if (!strcmp(used_flat_type, "sky")) {
462 do_catg_used = FLAT_SKY ;
463 } else {
464 do_catg_used = FLAT_ON ;
465 }
466 }
467 cpl_msg_info(__func__, "Use %s RAW frames", do_catg_used) ;
468
469 /* Get Rotator angles */
470 if ((angles_array = kmos_get_angles(frameset, &nb_angles,
471 do_catg_used)) == NULL) {
472 cpl_msg_error(__func__, "Cannot get Angles informations") ;
473 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
474 return -1 ;
475 }
476
477 /* Instrument setup */
478 suffix = kmo_dfs_get_suffix(kmo_dfs_get_frame(frameset, XCAL), TRUE, FALSE);
479 cpl_msg_info(__func__, "Detected instrument setup: %s", suffix+1);
480
481 /* Load main header */
482 main_header = kmo_dfs_load_primary_header(frameset, do_catg_used);
483 frame = kmo_dfs_get_frame(frameset, do_catg_used);
484
485 /* Compute fn_suffix */
486 if (!suppress_extension) fn_suffix = cpl_sprintf("%s", suffix);
487 else fn_suffix = cpl_sprintf("%s", "");
488
489 /* Save Products primary HDU */
490 kmo_dfs_save_main_header(frameset, ILLUM_CORR, fn_suffix, frame,
491 main_header, parlist, cpl_func);
492 if (has_flat_edge) {
493 kmo_dfs_save_main_header(frameset, SKYFLAT_EDGE, fn_suffix, frame,
494 main_header, parlist, cpl_func);
495 }
496 cpl_propertylist_delete(main_header) ;
497
498 /* Loop all Rotator Angles and Detectors */
499 for (a = 0; a < nb_angles; a++) {
500 /* Reduce only one angle */
501 if (reduce_angle <= 360 && angles_array[a] != reduce_angle) continue ;
502
503 cpl_msg_info(__func__, "Processing rotator angle %d -> %d degree",
504 a, angles_array[a]);
505 cpl_msg_indent_more() ;
506
507 /* Get the frameset with this angle */
508 angle_frameset = kmos_purge_wrong_angles_frameset(frameset,
509 angles_array[a], do_catg_used);
510 if (angle_frameset == NULL) {
511 cpl_msg_error(__func__, "Cannot get angle frameset") ;
512 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
513 cpl_free(angles_array) ;
514 cpl_free(fn_suffix) ;
515 cpl_free(suffix) ;
516 cpl_msg_indent_less() ;
517 return -1 ;
518 }
519
520 /* Check the number of RAW */
521 if (cpl_frameset_count_tags(angle_frameset, do_catg_used) == 1) {
522 cpl_msg_warning(cpl_func,
523 "1 input FLAT -> cmethod changed to average");
524 cmethod_loc = "average";
525 } else {
526 cmethod_loc = cmethod ;
527 }
528
529 /* Apply the reduction for this angle */
530 if (kmos_illumination_one_angle(angle_frameset, angles_array[a],
531 do_catg_used, reduce_det, pix_scale, neighborhoodRange, method,
532 cmethod_loc, cpos_rej, cneg_rej, citer, cmin, cmax,
533 flux, ranges_txt, suffix, fn_suffix, has_flat_edge,
534 add_all_sky) != 0) {
535 cpl_msg_error(__func__, "Failed Computation") ;
536 cpl_frameset_delete(angle_frameset) ;
537 cpl_free(suffix) ;
538 cpl_free(fn_suffix) ;
539 cpl_free(angles_array) ;
540 cpl_msg_indent_less() ;
541 return -1 ;
542 }
543 cpl_frameset_delete(angle_frameset) ;
544 cpl_msg_indent_less() ;
545 }
546 cpl_free(angles_array);
547 cpl_free(fn_suffix) ;
548 cpl_free(suffix) ;
549 return 0 ;
550}
551
554static int kmos_illumination_one_angle(
555 cpl_frameset * frameset,
556 int rotangle,
557 const char * do_catg_used,
558 int reduce_det,
559 double pix_scale,
560 double neighborhoodRange,
561 const char * method,
562 const char * cmethod,
563 double cpos_rej,
564 double cneg_rej,
565 int citer,
566 int cmin,
567 int cmax,
568 int flux,
569 const char * ranges_txt,
570 const char * suffix,
571 const char * fn_suffix,
572 int has_flat_edge,
573 int add_all_sky)
574{
575 cpl_array ** unused_ifus ;
576 const int * punused_ifus ;
577 cpl_frameset * frameset_raw ;
578 cpl_frame * frame ;
579 cpl_propertylist * main_header ;
580 char * filter ;
581 char * keyword ;
582 gridDefinition gd;
583 char * fn_lut ;
584 cpl_propertylist * tmp_header ;
585 int * bounds ;
586 cpl_array * calTimestamp ;
587 cpl_imagelist ** stored_data_cubes ;
588 cpl_imagelist ** stored_noise_cubes ;
589 cpl_image ** stored_data_images ;
590 cpl_image ** stored_noise_images ;
591 cpl_propertylist ** stored_sub_data_headers ;
592 cpl_propertylist ** stored_sub_noise_headers ;
593 cpl_table *** edge_table_sky = NULL;
594 cpl_vector * calAngles ;
595 cpl_imagelist * detector_in ;
596 cpl_image * img_in ;
597 cpl_image * combined_data ;
598 cpl_image * combined_noise ;
599 cpl_image * xcal ;
600 cpl_image * ycal ;
601 cpl_image * lcal ;
602 cpl_image * bad_pix_mask ;
603 float * pbad_pix_mask ;
604 cpl_image * img_dark ;
605 cpl_image * img_dark_noise ;
606 cpl_image * img_flat ;
607 cpl_image * img_flat_noise ;
608 cpl_table * band_table ;
609 cpl_propertylist * sub_header ;
610 char * extname ;
611 cpl_vector * identified_slices = NULL;
612 cpl_image * tmp_img ;
613 float * pdata ;
614 float * pnoise = NULL;
615 cpl_vector * ranges ;
616 cpl_imagelist * cube_data ;
617 cpl_imagelist * cube_noise ;
618 double ifu_crpix, ifu_crval, ifu_cdelt, mean_data,
619 qc_spat_unif, qc_max_dev, qc_max_nonunif,
620 tmp_stdev, tmp_mean, rotangle_found,
621 new_val, old_val ;
622 int next, nx, ny, process_noise, det_nr, cnt,
623 x, y, i, j, ifu_nr, qc_max_dev_id,
624 qc_max_nonunif_id, ix, iy, xmin, xmax, ymin,
625 ymax, nbdarks, nbflats ;
626
627 /* Check the inputs consistency */
628 if (kmos_illumination_check_inputs(frameset, do_catg_used, add_all_sky,
629 &next, &nx, &ny) != 1) {
630 cpl_msg_error(__func__, "Input frameset is not consistent") ;
631 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
632 return -1 ;
633 }
634
635 /* Check which IFUs are active for all frames */
636 unused_ifus = kmo_get_unused_ifus(frameset, 0, 0);
637 kmo_print_unused_ifus(unused_ifus, FALSE);
638
639 /* Decide here if noise is propagated */
640 if (cpl_frameset_count_tags(frameset, do_catg_used) >= 2 &&
641 !strcmp(do_catg_used, FLAT_SKY)) process_noise = 1 ;
642 else process_noise = 0 ;
643
644 /* Load the RAW frames in a frameset */
645 frameset_raw = cpl_frameset_new();
646 frame = kmo_dfs_get_frame(frameset, do_catg_used);
647 if (!add_all_sky && !strcmp(do_catg_used, FLAT_SKY)) {
648 /* Omit the first frame */
649 cpl_msg_info(__func__, "Use all FLAT_SKY frames but the first");
650 frame = kmo_dfs_get_frame(frameset, NULL);
651 }
652 while (frame != NULL) {
653 cpl_frameset_insert(frameset_raw, cpl_frame_duplicate(frame));
654 frame = kmo_dfs_get_frame(frameset, NULL);
655 }
656
657 /* Load first file primary header */
658 frame = kmo_dfs_get_frame(frameset_raw, do_catg_used);
659 if (frame == NULL) {
660 kmo_free_unused_ifus(unused_ifus);
661 cpl_frameset_delete(frameset_raw) ;
662 cpl_msg_error(__func__, "Missing RAW in input") ;
663 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
664 return -1 ;
665 }
666
667 main_header = kmo_dfs_load_primary_header(frameset_raw, do_catg_used);
668
669 /* Set default band-specific ranges for collapsing */
670 if (!strcmp(ranges_txt, "")) {
671 keyword = cpl_sprintf("%s%d%s", IFU_GRATID_PREFIX,1,IFU_GRATID_POSTFIX);
672 filter = cpl_sprintf("%s",
673 cpl_propertylist_get_string(main_header,keyword));
674 cpl_free(keyword);
675 if (!strcmp(filter, "IZ")) ranges_txt = "0.81,1.05";
676 else if (!strcmp(filter, "YJ")) ranges_txt = "1.025,1.3";
677 else if (!strcmp(filter, "H")) ranges_txt = "1.5,1.7";
678 else if (!strcmp(filter, "K")) ranges_txt = "2.1,2.35";
679 else if (!strcmp(filter, "HK")) ranges_txt = "1.5,1.7;2.1,2.35";
680 else {
681 cpl_msg_error(__func__, "Filter %s not supported", filter) ;
682 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
683 kmo_free_unused_ifus(unused_ifus);
684 cpl_frameset_delete(frameset_raw) ;
685 cpl_propertylist_delete(main_header);
686 cpl_free(filter) ;
687 return -1 ;
688 }
689 cpl_free(filter) ;
690 }
691 cpl_msg_info(__func__, "Spectral range to collapse: %s um", ranges_txt);
692
693 /* Set grid definition, wl start/end points will be set in the loop */
694 kmclipm_setup_grid(&gd, method, neighborhoodRange, pix_scale, 0.);
695
696 /* Create filename for LUT */
697 fn_lut = cpl_sprintf("%s%s", "lut", suffix);
698
699 /* Extract bounds */
700 tmp_header = kmo_dfs_load_primary_header(frameset, XCAL);
701 bounds = kmclipm_extract_bounds(tmp_header);
702 cpl_propertylist_delete(tmp_header);
703
704 /* Get timestamps of xcal, ycal & lcal */
705 calTimestamp = kmo_get_timestamps(
706 kmo_dfs_get_frame(frameset, XCAL),
707 kmo_dfs_get_frame(frameset, YCAL),
708 kmo_dfs_get_frame(frameset, LCAL)) ;
709
710 /* Create holders for reconstructed data, noise cubes and headers */
711 stored_data_cubes=(cpl_imagelist**)cpl_calloc(next*KMOS_IFUS_PER_DETECTOR,
712 sizeof(cpl_imagelist*));
713 stored_noise_cubes=(cpl_imagelist**)cpl_calloc(next*KMOS_IFUS_PER_DETECTOR,
714 sizeof(cpl_imagelist*));
715 stored_data_images = (cpl_image**)cpl_calloc(next*KMOS_IFUS_PER_DETECTOR,
716 sizeof(cpl_image*));
717 stored_noise_images = (cpl_image**)cpl_calloc(next*KMOS_IFUS_PER_DETECTOR,
718 sizeof(cpl_image*));
719 stored_sub_data_headers = (cpl_propertylist**)cpl_calloc(
720 next*KMOS_IFUS_PER_DETECTOR, sizeof(cpl_propertylist*));
721 stored_sub_noise_headers = (cpl_propertylist**)cpl_calloc(
722 next*KMOS_IFUS_PER_DETECTOR, sizeof(cpl_propertylist*));
723 if (has_flat_edge) edge_table_sky = (cpl_table***)cpl_calloc(
724 KMOS_NR_DETECTORS, sizeof(cpl_table**));
725 calAngles = cpl_vector_new(3);
726 /* Initialise */
727 for (det_nr = 1; det_nr <= next; det_nr++) {
728 if (has_flat_edge)
729 edge_table_sky[det_nr-1] = NULL;
730 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
731 ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j ;
732 stored_data_cubes[ifu_nr] = NULL ;
733 stored_noise_cubes[ifu_nr] = NULL ;
734 stored_data_images[ifu_nr] = NULL ;
735 stored_noise_images[ifu_nr] = NULL ;
736 stored_sub_data_headers[ifu_nr] = NULL ;
737 stored_sub_noise_headers[ifu_nr] = NULL ;
738 }
739 }
740
741 cpl_bivector * skystats = cpl_bivector_new(next * cpl_frameset_count_tags(frameset_raw, do_catg_used));
742 cpl_vector * skymed = cpl_bivector_get_x(skystats);
743 cpl_vector * skystd = cpl_bivector_get_y(skystats);
744 /* Loop through all detectors */
745 int cnt_max = 0;
746 for (det_nr = 1; det_nr <= next; det_nr++) {
747
748 /* Compute only one detector */
749 if (reduce_det != 0 && det_nr != reduce_det) continue ;
750
751 cpl_msg_info(__func__, "Processing detector No. %d", det_nr);
752 cpl_msg_indent_more() ;
753
754 detector_in = cpl_imagelist_new();
755 /* Load all images of this detector */
756 img_in = kmo_dfs_load_image(frameset_raw, do_catg_used, det_nr, FALSE,
757 TRUE, NULL);
758 cnt = 0;
759 while (img_in != NULL) {
760 double skystatstemp;
761 cpl_imagelist_set(detector_in, img_in, cnt);
762 if (!strcmp(do_catg_used, FLAT_SKY))
763 {
764 skystatstemp = cpl_image_get_median(img_in);
765 cpl_vector_set(skymed, cnt + (det_nr - 1) * cnt_max, skystatstemp);
766 skystatstemp = cpl_image_get_stdev(img_in);
767 cpl_vector_set(skystd, cnt + (det_nr - 1) * cnt_max, skystatstemp);
768 }
769
770 /* load same extension of next RAW frame*/
771 img_in = kmo_dfs_load_image(frameset_raw, NULL, det_nr, FALSE,
772 TRUE, NULL);
773 cnt++;
774 if (cnt > cnt_max) cnt_max = cnt;
775 }
776
777 /* Combine images (data only) and create noise (stdev of data) */
778 cpl_msg_info(__func__, "Combining frames");
779 combined_data = combined_noise = NULL ;
780 if (process_noise) {
781 kmos_combine_frames(detector_in, cmethod, cpos_rej, cneg_rej,
782 citer, cmax, cmin, &combined_data, &combined_noise, -1.0);
783 } else {
784 kmos_combine_frames(detector_in, cmethod, cpos_rej, cneg_rej,
785 citer, cmax, cmin, &combined_data, NULL, -1.0);
786 }
787 cpl_imagelist_delete(detector_in);
788
789 /* Check if combination succesfull */
790 if (cpl_error_get_code() != CPL_ERROR_NONE) {
791 kmo_free_unused_ifus(unused_ifus);
792 cpl_frameset_delete(frameset_raw) ;
793 cpl_propertylist_delete(main_header);
794 cpl_free(fn_lut) ;
795 cpl_free(bounds) ;
796 cpl_array_delete(calTimestamp);
797 cpl_free(stored_data_cubes);
798 cpl_free(stored_noise_cubes);
799 cpl_free(stored_data_images);
800 cpl_free(stored_noise_images);
801 cpl_free(stored_sub_data_headers);
802 cpl_free(stored_sub_noise_headers);
803 if (edge_table_sky) cpl_free(edge_table_sky) ;
804 cpl_vector_delete(calAngles) ;
805 cpl_msg_error(__func__, "Combination failed") ;
806 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
807 cpl_msg_indent_less() ;
808 return -1 ;
809 }
810
811 if (kmclipm_omit_warning_one_slice > 10)
812 kmclipm_omit_warning_one_slice = FALSE;
813
814 /* Load calibration files */
815 xcal = kmo_dfs_load_cal_image(frameset, XCAL, det_nr, FALSE, rotangle,
816 FALSE, NULL, &rotangle_found, -1, 0, 0);
817 cpl_vector_set(calAngles, 0, rotangle_found);
818 ycal = kmo_dfs_load_cal_image(frameset, YCAL, det_nr, FALSE, rotangle,
819 FALSE, NULL, &rotangle_found, -1, 0, 0);
820 cpl_vector_set(calAngles, 1, rotangle_found);
821 lcal = kmo_dfs_load_cal_image(frameset, LCAL, det_nr, FALSE, rotangle,
822 FALSE, NULL, &rotangle_found, -1, 0, 0);
823 cpl_vector_set(calAngles, 2, rotangle_found);
824
825 /* Load bad pixel mask from XCAL and set NaNs to 0 other values to 1 */
826 bad_pix_mask = cpl_image_duplicate(xcal);
827 pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask);
828 for (x = 0; x < nx; x++) {
829 for (y = 0; y < ny; y++) {
830 if (isnan(pbad_pix_mask[x+nx*y])) {
831 pbad_pix_mask[x+nx*y] = 0.;
832 } else {
833 pbad_pix_mask[x+nx*y] = 1.;
834 }
835 }
836 }
837
838 /* Compute SKYFLAT_EDGE */
839 if (has_flat_edge) {
840 edge_table_sky[det_nr-1] = kmos_illumination_edge_shift_correct(
841 combined_data, combined_noise, process_noise, bad_pix_mask,
842 det_nr, unused_ifus[det_nr-1],
843 cpl_frame_get_filename(kmo_dfs_get_frame(frameset,
844 FLAT_EDGE)), rotangle);
845 if (edge_table_sky[det_nr-1] == NULL) {
846 kmo_free_unused_ifus(unused_ifus);
847 cpl_frameset_delete(frameset_raw) ;
848 cpl_propertylist_delete(main_header);
849 cpl_free(fn_lut) ;
850 cpl_free(bounds) ;
851 cpl_array_delete(calTimestamp);
852 cpl_free(stored_data_cubes);
853 cpl_free(stored_noise_cubes);
854 cpl_free(stored_data_images);
855 cpl_free(stored_noise_images);
856 cpl_free(stored_sub_data_headers);
857 cpl_free(stored_sub_noise_headers);
858 if (has_flat_edge) cpl_free(edge_table_sky) ;
859 cpl_vector_delete(calAngles) ;
860 cpl_image_delete(xcal);
861 cpl_image_delete(ycal);
862 cpl_image_delete(lcal);
863 cpl_image_delete(bad_pix_mask);
864 cpl_image_delete(combined_data);
865 if (process_noise) cpl_image_delete(combined_noise);
866 cpl_msg_error(__func__, "Edge Shift Correction failed") ;
867 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
868 cpl_msg_indent_less() ;
869 return -1 ;
870 }
871 }
872
873 /* Reconstruct */
874
875 /* Load MASTER_DARK */
876 nbdarks = cpl_frameset_count_tags(frameset, MASTER_DARK) ;
877 if (nbdarks == 1) {
878 img_dark = kmo_dfs_load_image(frameset, MASTER_DARK, det_nr, FALSE,
879 FALSE, NULL);
880 if (process_noise) {
881 img_dark_noise = kmo_dfs_load_image(frameset, MASTER_DARK,
882 det_nr, TRUE, FALSE, NULL);
883 } else {
884 img_dark_noise = NULL ;
885 }
886 } else {
887 img_dark = cpl_image_duplicate(combined_data);
888 cpl_image_multiply_scalar(img_dark, 0);
889 img_dark_noise = NULL ;
890 }
891
892 /* Load MASTER_FLAT */
893 nbflats = cpl_frameset_count_tags(frameset, MASTER_FLAT) ;
894 if (nbflats == 1) {
895 img_flat = kmo_dfs_load_cal_image(frameset, MASTER_FLAT, det_nr,
896 FALSE, rotangle, FALSE, NULL, &rotangle_found, -1, 0, 0);
897 if (process_noise) {
898 img_flat_noise = kmo_dfs_load_cal_image(frameset, MASTER_FLAT,
899 det_nr, TRUE, rotangle, FALSE, NULL, &rotangle_found,
900 -1, 0, 0);
901 } else {
902 img_flat_noise = NULL ;
903 }
904 } else {
905 img_flat = cpl_image_duplicate(combined_data);
906 cpl_image_multiply_scalar(img_flat, 0);
907 cpl_image_add_scalar(img_flat, 1);
908 img_flat_noise = NULL ;
909 }
910
911 /* ESO INS FILTi ID */
912 keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX, det_nr,
913 IFU_FILTID_POSTFIX);
914 band_table = kmo_dfs_load_table(frameset, WAVE_BAND, 1, 0);
915 kmclipm_setup_grid_band_lcal(&gd,
916 cpl_propertylist_get_string(main_header, keyword), band_table);
917 cpl_free(keyword);
918 cpl_table_delete(band_table);
919
920 cpl_msg_info(__func__, "Reconstructing cubes");
921 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
922 /* Update sub-header */
923 ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
924
925 /* Load raw image and sub-header */
926 sub_header = kmo_dfs_load_sub_header(frameset_raw, do_catg_used,
927 det_nr, FALSE);
928
929 punused_ifus = cpl_array_get_data_int_const(unused_ifus[det_nr-1]);
930
931 /* Check if IFU is valid */
932 keyword = cpl_sprintf("%s%d%s", IFU_VALID_PREFIX, ifu_nr,
933 IFU_VALID_POSTFIX);
934 cpl_propertylist_get_string(main_header, keyword);
935 cpl_free(keyword);
936 if ((cpl_error_get_code() == CPL_ERROR_DATA_NOT_FOUND) &&
937 (bounds[2*(ifu_nr-1)] != -1) &&
938 (bounds[2*(ifu_nr-1)+1] != -1) && (punused_ifus[j] == 0)) {
939 /* IFU is valid */
940 cpl_error_reset();
941
942 /* Compute WCS */
943 kmo_calc_wcs_gd(main_header, sub_header, ifu_nr, gd);
944
945 /* Reconstruct Cube */
946 cube_noise = NULL ;
947 kmo_reconstruct_sci_image(ifu_nr, bounds[2*(ifu_nr-1)],
948 bounds[2*(ifu_nr-1)+1], combined_data, combined_noise,
949 img_dark, img_dark_noise, img_flat, img_flat_noise,
950 xcal, ycal, lcal, &gd, calTimestamp, calAngles,
951 fn_lut, &cube_data, &cube_noise, flux, 0,
952 NULL, NULL, NULL);
953 } else {
954 /* IFU is invalid */
955 cpl_error_reset();
956 }
957
958 /* Store output */
959 extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_DATA);
960
961 kmclipm_update_property_string(sub_header, EXTNAME, extname,
962 "FITS extension name");
963 cpl_free(extname);
964
965 /* Store cube and sub header into array for later */
966 stored_data_cubes[ifu_nr - 1] = cube_data;
967 stored_sub_data_headers[ifu_nr - 1] = sub_header;
968
969 if (process_noise) {
970 sub_header=cpl_propertylist_duplicate(
971 stored_sub_data_headers[ifu_nr - 1]);
972 extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_NOISE);
973 kmclipm_update_property_string(sub_header, EXTNAME, extname,
974 "FITS extension name");
975 cpl_free(extname);
976
977 stored_noise_cubes[ifu_nr - 1] = cube_noise;
978 stored_sub_noise_headers[ifu_nr - 1] = sub_header;
979 }
980 cube_data = NULL;
981 cube_noise = NULL;
982 }
983
984 /* Free memory */
985 cpl_image_delete(xcal);
986 cpl_image_delete(ycal);
987 cpl_image_delete(lcal);
988 cpl_image_delete(combined_data);
989 cpl_image_delete(bad_pix_mask);
990 cpl_image_delete(img_dark);
991 cpl_image_delete(img_flat);
992 if (process_noise) {
993 cpl_image_delete(combined_noise);
994 cpl_image_delete(img_dark_noise);
995 cpl_image_delete(img_flat_noise);
996 }
997 cpl_msg_indent_less() ;
998 }
999 cpl_vector_delete(calAngles) ;
1000 cpl_free(fn_lut) ;
1001 cpl_free(bounds);
1002 cpl_array_delete(calTimestamp);
1003
1004 ranges = kmo_identify_ranges(ranges_txt);
1005
1006 /* Collapse cubes using rejection */
1007 cpl_msg_info(__func__, "Collapse cubes");
1008 for (det_nr = 1; det_nr <= next; det_nr++) {
1009
1010 /* Compute only one detector */
1011 if (reduce_det != 0 && det_nr != reduce_det) continue ;
1012
1013 cpl_msg_info(__func__, "Processing detector No. %d", det_nr);
1014 cpl_msg_indent_more() ;
1015
1016 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
1017 ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
1018
1019 punused_ifus=cpl_array_get_data_int_const(unused_ifus[det_nr-1]);
1020 if (punused_ifus[j] == 0) {
1021 if (stored_sub_data_headers[ifu_nr-1] != NULL) {
1022 /* IFU is valid */
1023 ifu_crpix = cpl_propertylist_get_double(
1024 stored_sub_data_headers[ifu_nr-1], CRPIX3);
1025 ifu_crval = cpl_propertylist_get_double(
1026 stored_sub_data_headers[ifu_nr-1], CRVAL3);
1027 ifu_cdelt = cpl_propertylist_get_double(
1028 stored_sub_data_headers[ifu_nr-1], CDELT3);
1029 identified_slices = kmo_identify_slices(ranges, ifu_crpix,
1030 ifu_crval, ifu_cdelt, gd.l.dim);
1031 }
1032
1033 if (stored_data_cubes[ifu_nr-1] != NULL) {
1034 kmclipm_make_image(stored_data_cubes[ifu_nr-1],
1035 stored_noise_cubes[ifu_nr-1],
1036 &stored_data_images[ifu_nr-1],
1037 &stored_noise_images[ifu_nr-1], identified_slices,
1038 cmethod, cpos_rej, cneg_rej, citer, cmax, cmin);
1039 }
1040
1041 /* DEBUG MODE */
1042 /* Save the IFU 1 collapsed image of the reconstructed cube */
1043 if (ifu_nr == 1 && cpl_msg_get_level() == CPL_MSG_DEBUG) {
1044 cpl_msg_debug(__func__, "Save IFU 1 collapsed image") ;
1045 cpl_image_save(stored_data_images[ifu_nr-1],
1046 "debug_collapsed_ifu1.fits", CPL_TYPE_DOUBLE, NULL,
1047 CPL_IO_CREATE);
1048 }
1049
1050 /* ONLY for FLAT_ON data */
1051 /* Apply median smoothing - Taking care of edges */
1052 /* (IFUs 1-16 top/bottom, IFUs 17-24 left/right) */
1053 if (!strcmp(do_catg_used, FLAT_ON)) {
1054 nx = cpl_image_get_size_x(stored_data_images[ifu_nr-1]) ;
1055 ny = cpl_image_get_size_x(stored_data_images[ifu_nr-1]) ;
1056 int firstx = 0, lastx = 0, firsty = 0, lasty = 0 ;
1057 if (ifu_nr <= 2*KMOS_IFUS_PER_DETECTOR) {
1058 firstx = 0;
1059 lastx = nx-1;
1060 firsty = 1;
1061 lasty = ny-2;
1062 } else {
1063 firstx = 1;
1064 lastx= nx-2;
1065 firsty = 0;
1066 lasty = ny-1;
1067 }
1068
1069 tmp_img = cpl_image_duplicate(stored_data_images[ifu_nr-1]);
1070 pdata = cpl_image_get_data_float(tmp_img);
1071 pnoise =
1072 cpl_image_get_data_float(stored_noise_images[ifu_nr-1]);
1073
1074 /* Median filtering */
1075 int mhalf = 3 ;
1076 for (ix = 0; ix < nx; ix++) {
1077 for (iy = 0; iy < ny; iy++) {
1078 if (ix-mhalf > firstx) xmin = ix-mhalf;
1079 else xmin = firstx;
1080 if (ix+mhalf < lastx) xmax = ix+mhalf;
1081 else xmax = lastx;
1082 if (iy-mhalf > firsty) ymin = iy-mhalf;
1083 else ymin = firsty;
1084 if (iy+mhalf < lasty) ymax = iy+mhalf;
1085 else ymax = lasty;
1086 pdata[ix+nx*iy] = cpl_image_get_median_window(
1087 stored_data_images[ifu_nr-1],xmin+1,ymin+1,
1088 xmax+1, ymax+1);
1089 if (stored_noise_images[ifu_nr-1] != NULL) {
1090 pnoise[ix+nx*iy]/=(xmax-xmin+1)*(ymax-ymin+1);
1091 }
1092 }
1093 }
1094
1095 /* Replace images */
1096 cpl_image_delete(stored_data_images[ifu_nr-1]);
1097 stored_data_images[ifu_nr-1] = tmp_img;
1098 }
1099 if(identified_slices) cpl_vector_delete(identified_slices);
1100 } else {
1101 /* IFU is invalid */
1102 }
1103 }
1104 cpl_msg_indent_less() ;
1105
1106 }
1107 cpl_vector_delete(ranges);
1108 for (i = 0; i < next * KMOS_IFUS_PER_DETECTOR; i++) {
1109 if (stored_data_cubes != NULL) {
1110 cpl_imagelist_delete(stored_data_cubes[i]);
1111 }
1112 if (stored_noise_cubes != NULL) {
1113 cpl_imagelist_delete(stored_noise_cubes[i]);
1114 }
1115 }
1116 cpl_free(stored_data_cubes);
1117 cpl_free(stored_noise_cubes);
1118
1119 /* Normalise all IFUs of a detector as a group. */
1120 // Calculate mean of each IFU, add up and divide by number of successful
1121 // averaged IFUs.
1122 // Then divide all valid IFUs with mean value
1123 for (j = 0; j < next; j++) {
1124 /* Compute only one detector */
1125 if (reduce_det != 0 && j+1 != reduce_det) continue ;
1126 cnt = 0;
1127 mean_data = 0;
1128 for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
1129 ifu_nr = j*KMOS_IFUS_PER_DETECTOR + i;
1130 if (stored_data_images[ifu_nr] != NULL) {
1131 if (cpl_image_count_rejected(stored_data_images[ifu_nr]) >=
1132 cpl_image_get_size_x(stored_data_images[ifu_nr])*
1133 cpl_image_get_size_y(stored_data_images[ifu_nr])) {
1134 /* TODO - Deallocate */
1135 cpl_msg_error(__func__,
1136 "The collapsed, dark-subtracted image contains "
1137 "only invalid values! Probably the provided "
1138 "RAW frames are exactly the same as the "
1139 "frames used for MASTER_DARK calculation.");
1140 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
1141
1142 if (stored_sub_data_headers) {
1143 for (int k = 0; k < next * KMOS_IFUS_PER_DETECTOR; k++) {
1144 if (stored_sub_data_headers[k]) {
1145 cpl_propertylist_delete(stored_sub_data_headers[k]);
1146 }
1147 }
1148 cpl_free(stored_sub_data_headers);
1149 }
1150
1151 if (stored_sub_noise_headers) {
1152 for (int k = 0; k < next * KMOS_IFUS_PER_DETECTOR; k++) {
1153 if (stored_sub_noise_headers[k]) {
1154 cpl_propertylist_delete(stored_sub_noise_headers[k]);
1155 }
1156 }
1157 cpl_free(stored_sub_noise_headers);
1158 }
1159
1160 return -1 ;
1161 }
1162 mean_data += cpl_image_get_mean(stored_data_images[ifu_nr]);
1163 cnt++;
1164 }
1165 }
1166 mean_data /= cnt;
1167
1168 if (mean_data != 0.0) {
1169 for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
1170 ifu_nr = j*KMOS_IFUS_PER_DETECTOR + i;
1171 if (stored_data_images[ifu_nr] != NULL) {
1172 cpl_image_divide_scalar(stored_data_images[ifu_nr],
1173 mean_data);
1174 }
1175 }
1176 } else {
1177 cpl_msg_warning(__func__, "Data cannot be normalised (mean=0.0)");
1178 }
1179
1180 if (process_noise) {
1181 if (mean_data != 0.0) {
1182 for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
1183 ifu_nr = j*KMOS_IFUS_PER_DETECTOR + i;
1184 if (stored_noise_images[ifu_nr] != NULL) {
1185 cpl_image_divide_scalar(stored_noise_images[ifu_nr],
1186 mean_data);
1187 }
1188 }
1189 } else {
1190 cpl_msg_warning(__func__, "Noise cant be normalised (mean=0)");
1191 }
1192 }
1193 }
1194
1195 /* Invert data and noise */
1196 if (!strcmp(do_catg_used, FLAT_ON)) {
1197 for (j = 0; j < next; j++) {
1198 /* Compute only one detector */
1199 if (reduce_det != 0 && j+1 != reduce_det) continue ;
1200 for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
1201 ifu_nr = j*KMOS_IFUS_PER_DETECTOR + i;
1202 if (stored_data_images[ifu_nr] != NULL) {
1203 /* Invert data */
1204 pdata=cpl_image_get_data_float(stored_data_images[ifu_nr]);
1205 if (stored_noise_images[ifu_nr] != NULL) {
1206 pnoise=cpl_image_get_data_float(
1207 stored_noise_images[ifu_nr]);
1208 }
1209 for (ix = 0; ix < nx; ix++) {
1210 for (iy = 0; iy < ny; iy++) {
1211 old_val = pdata[ix+nx*iy];
1212 pdata[ix+nx*iy] = 1. / pdata[ix+nx*iy];
1213 if (stored_noise_images[ifu_nr] != NULL) {
1214 new_val = pdata[ix+nx*iy];
1215 if (pnoise) {
1216 pnoise[ix+nx*iy] = sqrt(pow(new_val, 2) *
1217 pow(pnoise[ix+nx*iy],2)/pow(old_val,2));
1218 }
1219 }
1220 }
1221 }
1222 }
1223 }
1224 }
1225 }
1226
1227 /* Compute qc parameters on normalised data */
1228 qc_spat_unif = qc_max_nonunif = qc_max_dev = 0.0;
1229 qc_max_nonunif_id = qc_max_dev_id = 0 ;
1230 qc_max_dev_id = 0 ;
1231 cnt = 0;
1232 for (i = 0; i < next * KMOS_IFUS_PER_DETECTOR; i++) {
1233 if (stored_data_images[i] != NULL) {
1234 tmp_mean = cpl_image_get_mean(stored_data_images[i]);
1235 tmp_stdev = cpl_image_get_stdev (stored_data_images[i]);
1236
1237 qc_spat_unif += pow(tmp_mean-1, 2);
1238 if (fabs(tmp_mean) > qc_max_dev) {
1239 qc_max_dev = tmp_mean-1;
1240 qc_max_dev_id = i+1;
1241 }
1242 if (fabs(tmp_stdev) > qc_max_nonunif) {
1243 qc_max_nonunif = tmp_stdev;
1244 qc_max_nonunif_id = i+1;
1245 }
1246 cnt++;
1247 }
1248 }
1249 qc_spat_unif = sqrt(qc_spat_unif / cnt);
1250
1251 /* Udate which IFUs are not used */
1252 kmo_print_unused_ifus(unused_ifus, TRUE);
1253 kmo_set_unused_ifus(unused_ifus, main_header, "kmos_illumination");
1254 kmo_free_unused_ifus(unused_ifus);
1255 cpl_propertylist_delete(main_header);
1256
1257 cpl_msg_info(__func__, "Save data");
1258 for (i = 0; i < next * KMOS_IFUS_PER_DETECTOR; i++) {
1259 if (stored_sub_data_headers[i] != NULL) {
1260 /* Store ROTANGLE */
1261 kmclipm_update_property_double(stored_sub_data_headers[i],
1262 CAL_ROTANGLE, ((double)rotangle),
1263 "[deg] Rotator relative to nasmyth");
1264
1265 /* Write QCs in data extensions */
1266 kmclipm_update_property_double(stored_sub_data_headers[i],
1267 QC_SPAT_UNIF, qc_spat_unif,
1268 "[adu] uniformity of illumination correction");
1269 kmclipm_update_property_double(stored_sub_data_headers[i],
1270 QC_SPAT_MAX_DEV, qc_max_dev,
1271 "[adu] max. deviation from unity");
1272 kmclipm_update_property_int(stored_sub_data_headers[i],
1273 QC_SPAT_MAX_DEV_ID, qc_max_dev_id,
1274 "[] IFU ID with max. dev. from unity");
1275 kmclipm_update_property_double(stored_sub_data_headers[i],
1276 QC_SPAT_MAX_NONUNIF, qc_max_nonunif,
1277 "[adu] max. stdev of illumination corr.");
1278 kmclipm_update_property_int(stored_sub_data_headers[i],
1279 QC_SPAT_MAX_NONUNIF_ID, qc_max_nonunif_id,
1280 "[] IFU ID with max. stdev in illum. corr.");
1281 if (!strcmp(do_catg_used, FLAT_SKY))
1282 {
1283 cpl_size nsky = cpl_bivector_get_size(skystats) / next;
1284 for (cpl_size sky_nr = 0; sky_nr < nsky; sky_nr++)
1285 {
1286 keyword = cpl_sprintf("%s%lld %s%s", QC_SKYFLAT_PREFIX, sky_nr + 1,
1287 QC_SKYFLAT_INFIX, QC_MED_SUFFIX);
1288 kmclipm_update_property_double(
1289 stored_sub_data_headers[i],
1290 keyword, cpl_vector_get(skymed, sky_nr + (i / KMOS_IFUS_PER_DETECTOR) * nsky),
1291 "skyflat median");
1292 cpl_free(keyword);
1293 keyword = cpl_sprintf("%s%lld %s%s", QC_SKYFLAT_PREFIX, sky_nr + 1,
1294 QC_SKYFLAT_INFIX, QC_RMS_SUFFIX);
1295 kmclipm_update_property_double(
1296 stored_sub_data_headers[i],
1297 keyword, cpl_vector_get(skystd, sky_nr + (i / KMOS_IFUS_PER_DETECTOR) * nsky),
1298 "skyflat stddev");
1299 cpl_free(keyword);
1300 }
1301 }
1302 }
1303
1304 /* Add EXTVER */
1305 kmclipm_update_property_int(stored_sub_data_headers[i], EXTVER, 2*(i+1),
1306 "FITS extension ver");
1307 /* Remove 3rd dim */
1308 cpl_propertylist_erase(stored_sub_data_headers[i], CDELT1);
1309 cpl_propertylist_erase(stored_sub_data_headers[i], CDELT2);
1310 cpl_propertylist_erase(stored_sub_data_headers[i], CDELT3);
1311 cpl_propertylist_erase(stored_sub_data_headers[i], CRVAL3);
1312 cpl_propertylist_erase(stored_sub_data_headers[i], CRPIX3);
1313 cpl_propertylist_erase(stored_sub_data_headers[i], CTYPE3);
1314 /* Remove 1/2 dims if image is missing */
1315 if (stored_data_images[i]==NULL) {
1316 cpl_propertylist_erase(stored_sub_data_headers[i], CTYPE1);
1317 cpl_propertylist_erase(stored_sub_data_headers[i], CTYPE2);
1318 cpl_propertylist_erase(stored_sub_data_headers[i], CRPIX1);
1319 cpl_propertylist_erase(stored_sub_data_headers[i], CRPIX2);
1320 cpl_propertylist_erase(stored_sub_data_headers[i], CRVAL1);
1321 cpl_propertylist_erase(stored_sub_data_headers[i], CRVAL2);
1322 cpl_propertylist_erase(stored_sub_data_headers[i], CD1_1);
1323 cpl_propertylist_erase(stored_sub_data_headers[i], CD2_1);
1324 cpl_propertylist_erase(stored_sub_data_headers[i], CD1_2);
1325 cpl_propertylist_erase(stored_sub_data_headers[i], CD2_2);
1326 }
1327
1328 kmo_dfs_save_image(stored_data_images[i], ILLUM_CORR, fn_suffix,
1329 stored_sub_data_headers[i], 0./0.);
1330 if (process_noise) {
1331 if (stored_sub_noise_headers[i] != NULL) {
1332 /* Add EXTVER */
1333 kmclipm_update_property_int(stored_sub_noise_headers[i], EXTVER,
1334 2*(i+1)+2, "FITS extension ver");
1335 /* Remove 3rd dim */
1336 cpl_propertylist_erase(stored_sub_noise_headers[i], CDELT1);
1337 cpl_propertylist_erase(stored_sub_noise_headers[i], CDELT2);
1338 cpl_propertylist_erase(stored_sub_noise_headers[i], CDELT3);
1339 cpl_propertylist_erase(stored_sub_noise_headers[i], CRVAL3);
1340 cpl_propertylist_erase(stored_sub_noise_headers[i], CRPIX3);
1341 cpl_propertylist_erase(stored_sub_noise_headers[i], CTYPE3);
1342 /* Remove 1/2 dims if image is missing */
1343 if (stored_data_images[i]==NULL) {
1344 cpl_propertylist_erase(stored_sub_noise_headers[i], CTYPE1);
1345 cpl_propertylist_erase(stored_sub_noise_headers[i], CTYPE2);
1346 cpl_propertylist_erase(stored_sub_noise_headers[i], CRPIX1);
1347 cpl_propertylist_erase(stored_sub_noise_headers[i], CRPIX2);
1348 cpl_propertylist_erase(stored_sub_noise_headers[i], CRVAL1);
1349 cpl_propertylist_erase(stored_sub_noise_headers[i], CRVAL2);
1350 cpl_propertylist_erase(stored_sub_noise_headers[i], CD1_1);
1351 cpl_propertylist_erase(stored_sub_noise_headers[i], CD2_1);
1352 cpl_propertylist_erase(stored_sub_noise_headers[i], CD1_2);
1353 cpl_propertylist_erase(stored_sub_noise_headers[i], CD2_2);
1354 }
1355 }
1356 kmo_dfs_save_image(stored_noise_images[i], ILLUM_CORR, fn_suffix,
1357 stored_sub_noise_headers[i], 0./0.);
1358 }
1359 }
1360
1361 skymed = NULL;
1362 skystd = NULL;
1363 cpl_bivector_delete(skystats);
1364
1365 for (i = 0; i < next * KMOS_IFUS_PER_DETECTOR; i++) {
1366 if (stored_data_images != NULL) {
1367 cpl_image_delete(stored_data_images[i]);
1368 }
1369 if (stored_noise_images != NULL) {
1370 cpl_image_delete(stored_noise_images[i]);
1371 }
1372 }
1373 cpl_free(stored_data_images);
1374 cpl_free(stored_noise_images);
1375
1376 for (det_nr = 1; det_nr <= next; det_nr++) {
1377 /* Compute only one detector */
1378 if (reduce_det != 0 && det_nr != reduce_det) continue ;
1379
1380 for (ifu_nr = 0; ifu_nr < KMOS_IFUS_PER_DETECTOR; ifu_nr++) {
1381 kmclipm_update_property_int(
1382 stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr],
1383 CAL_IFU_NR, ifu_nr+1+(det_nr-1)*KMOS_IFUS_PER_DETECTOR,
1384 "IFU Number {1..24}");
1385 kmclipm_update_property_double(
1386 stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr],
1387 CAL_ROTANGLE, rotangle_found,
1388 "[deg] Rotator relative to nasmyth");
1389 if (has_flat_edge) {
1390 /* Save edge-parameters as product */
1391 kmo_dfs_save_table(edge_table_sky[det_nr-1][ifu_nr],
1392 SKYFLAT_EDGE, fn_suffix,
1393 stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr]);
1394 }
1395 }
1396 }
1397
1398 /* De-allocate */
1399 cpl_frameset_delete(frameset_raw) ;
1400 for (i = 0; i < next * KMOS_IFUS_PER_DETECTOR; i++) {
1401 if (stored_sub_data_headers != NULL)
1402 cpl_propertylist_delete(stored_sub_data_headers[i]);
1403 if (stored_sub_noise_headers != NULL)
1404 cpl_propertylist_delete(stored_sub_noise_headers[i]);
1405 }
1406 cpl_free(stored_sub_data_headers);
1407 cpl_free(stored_sub_noise_headers);
1408 if (edge_table_sky) {
1409 for (i = 0; i < KMOS_NR_DETECTORS; i++) {
1410 if (edge_table_sky[i]) {
1411 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++)
1412 cpl_table_delete(edge_table_sky[i][j]);
1413 cpl_free(edge_table_sky[i]);
1414 }
1415 }
1416 cpl_free(edge_table_sky);
1417 }
1418 return 0 ;
1419}
1420
1421/*----------------------------------------------------------------------------*/
1431/*----------------------------------------------------------------------------*/
1432static int kmos_illumination_check_inputs(
1433 cpl_frameset * frameset,
1434 const char * do_catg_used,
1435 int add_all_sky,
1436 int * next,
1437 int * nx,
1438 int * ny)
1439{
1440 cpl_frame * frame ;
1441 cpl_propertylist * main_header ;
1442 cpl_propertylist * tmp_header ;
1443 cpl_propertylist * eh ;
1444 char * keyword ;
1445 const char * filter_id ;
1446 const char * filter_id_l ;
1447 double exptime, exptime_cur ;
1448 cpl_error_code err ;
1449 int naxis1, naxis2, naxis1_cur, naxis2_cur,
1450 n_ext, n_ext_cur, i, nbdarks, nbflats ;
1451
1452 /* Check Entries */
1453 if (nx == NULL || ny == NULL || frameset == NULL || next == NULL)
1454 return -1;
1455
1456 /* Check Exptime consistency */
1457 frame = kmo_dfs_get_frame(frameset, do_catg_used);
1458 /* Skip first file only with FLAT_SKY */
1459 if (!add_all_sky && !strcmp(do_catg_used, FLAT_SKY))
1460 frame = kmo_dfs_get_frame(frameset, NULL);
1461
1462 if (frame == NULL) {
1463 cpl_msg_warning(__func__, "The only RAW frame is skipped - abort") ;
1464 return -1 ;
1465 }
1466
1467 /* Get first exptime */
1468 main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
1469 exptime = kmos_pfits_get_exptime(main_header);
1470 cpl_propertylist_delete(main_header);
1471
1472 /* Get second frame and the next */
1473 frame = kmo_dfs_get_frame(frameset, NULL);
1474 while (frame != NULL) {
1475 /* Get exptime */
1476 main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
1477 exptime_cur = kmos_pfits_get_exptime(main_header);
1478 cpl_propertylist_delete(main_header);
1479 if (fabs(exptime-exptime_cur) > 0.01) {
1480 cpl_msg_warning(__func__, "EXPTIME is not consistent") ;
1481 return 0 ;
1482 }
1483 frame = kmo_dfs_get_frame(frameset, NULL);
1484 }
1485
1486 /* Check frames numbers */
1487 if (cpl_frameset_count_tags(frameset, do_catg_used) < 3) {
1488 cpl_msg_warning(cpl_func, "3 or more RAW frames is wished");
1489 }
1490 nbdarks = cpl_frameset_count_tags(frameset, MASTER_DARK) ;
1491 nbflats = cpl_frameset_count_tags(frameset, MASTER_FLAT) ;
1492 if (cpl_frameset_count_tags(frameset, XCAL) != 1) {
1493 cpl_msg_warning(__func__, "Need 1 XCAL") ;
1494 return 0 ;
1495 }
1496 if (cpl_frameset_count_tags(frameset, YCAL) != 1) {
1497 cpl_msg_warning(__func__, "Need 1 YCAL") ;
1498 return 0 ;
1499 }
1500 if (cpl_frameset_count_tags(frameset, LCAL) != 1) {
1501 cpl_msg_warning(__func__, "Need 1 LCAL") ;
1502 return 0 ;
1503 }
1504 if (cpl_frameset_count_tags(frameset, WAVE_BAND) != 1) {
1505 cpl_msg_warning(__func__, "Need 1 WAVE_BAND") ;
1506 return 0 ;
1507 }
1508
1509 /* filter_id, grating_id and rotator offset match all detectors */
1510 err = CPL_ERROR_NONE ;
1511 err += kmo_check_frameset_setup(frameset, do_catg_used, TRUE, FALSE, TRUE);
1512 err += kmo_check_frame_setup(frameset, do_catg_used, XCAL, TRUE, FALSE,
1513 TRUE);
1514 err += kmo_check_frame_setup(frameset, XCAL, YCAL, TRUE, FALSE, TRUE);
1515 err += kmo_check_frame_setup(frameset, XCAL, LCAL, TRUE, FALSE, TRUE);
1516 if (nbflats == 1) {
1517 err += kmo_check_frame_setup(frameset, XCAL, MASTER_FLAT, TRUE, FALSE,
1518 TRUE);
1519 }
1520 err += kmo_check_frame_setup_md5_xycal(frameset);
1521 err += kmo_check_frame_setup_md5(frameset);
1522 if (err != CPL_ERROR_NONE) {
1523 cpl_msg_warning(__func__, "Frames are inconsistent") ;
1524 return 0 ;
1525 }
1526
1527 /* Check XCAL */
1528 frame = kmo_dfs_get_frame(frameset, XCAL) ;
1529 n_ext = cpl_frame_get_nextensions(frame);
1530 if (n_ext % KMOS_NR_DETECTORS != 0) {
1531 cpl_msg_warning(__func__, "XCAL must have 3*n extensions") ;
1532 return 0 ;
1533 }
1534 eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
1535 naxis1 = kmos_pfits_get_naxis1(eh) ;
1536 naxis2 = kmos_pfits_get_naxis2(eh) ;
1537 cpl_propertylist_delete(eh) ;
1538
1539 /* Check YCAL */
1540 frame = kmo_dfs_get_frame(frameset, YCAL);
1541 n_ext_cur = cpl_frame_get_nextensions(frame);
1542 if (n_ext_cur != n_ext) {
1543 cpl_msg_warning(__func__, "XCAL and YCAL nb of extensions differ") ;
1544 return 0 ;
1545 }
1546 eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
1547 naxis1_cur = kmos_pfits_get_naxis1(eh) ;
1548 naxis2_cur = kmos_pfits_get_naxis2(eh) ;
1549 cpl_propertylist_delete(eh) ;
1550 if (naxis1_cur != naxis1 || naxis2_cur != naxis2) {
1551 cpl_msg_warning(__func__, "XCAL and YCAL sizes differ") ;
1552 return 0 ;
1553 }
1554
1555 /* Check LCAL */
1556 frame = kmo_dfs_get_frame(frameset, LCAL);
1557 n_ext_cur = cpl_frame_get_nextensions(frame);
1558 if (n_ext_cur != n_ext) {
1559 cpl_msg_warning(__func__, "XCAL and LCAL nb of extensions differ") ;
1560 return 0 ;
1561 }
1562 eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
1563 naxis1_cur = kmos_pfits_get_naxis1(eh) ;
1564 naxis2_cur = kmos_pfits_get_naxis2(eh) ;
1565 cpl_propertylist_delete(eh) ;
1566 if (naxis1_cur != naxis1 || naxis2_cur != naxis2) {
1567 cpl_msg_warning(__func__, "XCAL and LCAL sizes differ") ;
1568 return 0 ;
1569 }
1570
1571 if (nbdarks == 1) {
1572 /* Check MASTER_DARK */
1573 frame = kmo_dfs_get_frame(frameset, MASTER_DARK);
1574 n_ext_cur = cpl_frame_get_nextensions(frame);
1575 if (n_ext_cur != 2*KMOS_NR_DETECTORS) {
1576 cpl_msg_warning(__func__, "MASTER_DARK must have 6 extensions") ;
1577 return 0 ;
1578 }
1579 }
1580
1581 if (nbflats == 1) {
1582 /* Check MASTER_FLAT */
1583 frame = kmo_dfs_get_frame(frameset, MASTER_FLAT);
1584 n_ext_cur = cpl_frame_get_nextensions(frame);
1585 if (n_ext_cur % (2*KMOS_NR_DETECTORS) != 0) {
1586 cpl_msg_warning(__func__, "MASTER_FLAT must have 6*n extensions") ;
1587 return 0 ;
1588 }
1589 }
1590
1591 /* Check RAW files */
1592 frame = kmo_dfs_get_frame(frameset, do_catg_used);
1593 tmp_header = kmo_dfs_load_primary_header(frameset, LCAL);
1594
1595 while (frame != NULL) {
1596 n_ext = cpl_frame_get_nextensions(frame);
1597 if (n_ext != KMOS_NR_DETECTORS) {
1598 cpl_msg_warning(__func__, "Raw file has wrong nb of extensions") ;
1599 return 0 ;
1600 }
1601 eh = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
1602 naxis1_cur = kmos_pfits_get_naxis1(eh) ;
1603 naxis2_cur = kmos_pfits_get_naxis2(eh) ;
1604 cpl_propertylist_delete(eh) ;
1605 if (naxis1_cur != naxis1 || naxis2_cur != naxis2) {
1606 cpl_msg_warning(__func__, "RAW file and XCAL sizes differ") ;
1607 return 0 ;
1608 }
1609
1610 /* Check Lamps */
1611 main_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
1612 if (!strcmp(do_catg_used, FLAT_SKY)) {
1613 if (kmo_check_lamp(main_header, INS_LAMP1_ST) != FALSE ||
1614 kmo_check_lamp(main_header, INS_LAMP2_ST) != FALSE ||
1615 kmo_check_lamp(main_header, INS_LAMP3_ST) != FALSE ||
1616 kmo_check_lamp(main_header, INS_LAMP4_ST) != FALSE) {
1617 cpl_msg_warning(__func__, "Some FLAT_SKY lamps are ON") ;
1618 cpl_propertylist_delete(main_header) ;
1619 return 0 ;
1620 }
1621 }
1622
1623 /* Check filters */
1624 for (i = 1; i <= KMOS_NR_DETECTORS; i++) {
1625 /* ESO INS FILTi ID */
1626 keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX, i,
1627 IFU_FILTID_POSTFIX);
1628 filter_id = cpl_propertylist_get_string(main_header, keyword);
1629 filter_id_l = cpl_propertylist_get_string(tmp_header, keyword);
1630 cpl_free(keyword);
1631
1632 if (strcmp(filter_id, "IZ") && strcmp(filter_id, "YJ") &&
1633 strcmp(filter_id, "H") && strcmp(filter_id, "K") &&
1634 strcmp(filter_id, "HK")) {
1635 cpl_msg_warning(__func__,
1636 "Filter ID must be 'IZ', 'YJ', 'H', 'K' or 'HK' ") ;
1637 cpl_propertylist_delete(main_header) ;
1638 cpl_propertylist_delete(tmp_header) ;
1639 return 0 ;
1640 }
1641
1642 if (strcmp(filter_id, filter_id_l)) {
1643 cpl_msg_warning(__func__,
1644 "Filter IDs in RAW and LCAL don't match") ;
1645 cpl_propertylist_delete(main_header) ;
1646 cpl_propertylist_delete(tmp_header) ;
1647 return 0 ;
1648 }
1649
1650 /* ESO INS GRATi ID */
1651 keyword = cpl_sprintf("%s%d%s", IFU_GRATID_PREFIX, i,
1652 IFU_GRATID_POSTFIX);
1653 filter_id = cpl_propertylist_get_string(main_header, keyword);
1654 filter_id_l = cpl_propertylist_get_string(tmp_header, keyword);
1655 cpl_free(keyword);
1656
1657 if (strcmp(filter_id, "IZ") && strcmp(filter_id, "YJ") &&
1658 strcmp(filter_id, "H") && strcmp(filter_id, "K") &&
1659 strcmp(filter_id, "HK")) {
1660 cpl_msg_warning(__func__,
1661 "Grating ID must be 'IZ', 'YJ', 'H', 'K' or 'HK' ") ;
1662 cpl_propertylist_delete(main_header) ;
1663 cpl_propertylist_delete(tmp_header) ;
1664 return 0 ;
1665 }
1666 if (strcmp(filter_id, filter_id_l)) {
1667 cpl_msg_warning(__func__,
1668 "Grating IDs in RAW and LCAL don't match") ;
1669 cpl_propertylist_delete(main_header) ;
1670 cpl_propertylist_delete(tmp_header) ;
1671 return 0 ;
1672 }
1673 }
1674 cpl_propertylist_delete(main_header);
1675
1676 /* Get next RAW frame */
1677 frame = kmo_dfs_get_frame(frameset, NULL);
1678 }
1679 cpl_propertylist_delete(tmp_header);
1680
1681 /* Return */
1682 *nx = naxis1 ;
1683 *ny = naxis2 ;
1684 *next = n_ext ;
1685 return 1 ;
1686}
1687
1688/*----------------------------------------------------------------------------*/
1694/*----------------------------------------------------------------------------*/
1695static cpl_table ** kmos_illumination_edge_shift_correct(
1696 cpl_image * combined_data,
1697 cpl_image * combined_noise,
1698 int process_noise,
1699 const cpl_image * bad_pix_mask,
1700 int det_nr,
1701 cpl_array * unused_ifus,
1702 const char * flat_edge_filename,
1703 double rotangle)
1704{
1705 int middle_row ;
1706 cpl_vector ** slitlet_ids = NULL ;
1707 cpl_matrix ** edgepars = NULL ;
1708 cpl_table ** edges ;
1709 cpl_vector * shift_vec ;
1710 const int * punused_ifus ;
1711 cpl_table * edge_table_flat ;
1712 cpl_vector * edge_vec ;
1713 kmclipm_vector * kv ;
1714 float * pcombined_data ;
1715 float * pcombined_noise ;
1716 double * array_in ;
1717 double * array_out ;
1718 double tmp_rotangle, flatval, skyval, shift_val ;
1719 int ifu_nr, i, nx, ny, ix, iy, edgeNr ;
1720
1721 /* Check Entries */
1722
1723 /* Initialise */
1724 middle_row = 1024 ;
1725
1726 /* Get edge-edgepars from RAW */
1727 kmos_calc_edgepars(combined_data, unused_ifus, bad_pix_mask, det_nr,
1728 &slitlet_ids, &edgepars);
1729 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1730 cpl_msg_error(__func__, "Cannot compute edges parameters") ;
1731 return NULL ;
1732 }
1733
1734 /* Copy edgepars to table for saving later on */
1735 edges = kmo_edgepars_to_table(slitlet_ids, edgepars);
1736 if (edgepars != NULL) {
1737 for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++)
1738 cpl_matrix_delete(edgepars[i]);
1739 cpl_free(edgepars);
1740 }
1741 if (slitlet_ids != NULL) {
1742 for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++)
1743 cpl_vector_delete(slitlet_ids[i]);
1744 cpl_free(slitlet_ids);
1745 }
1746
1747 /* Correlate FLAT_EDGE and SKYFLAT_EDGE */
1748 shift_vec = cpl_vector_new(KMOS_IFUS_PER_DETECTOR);
1749 for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
1750 ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + i + 1;
1751 punused_ifus = cpl_array_get_data_int_const(unused_ifus);
1752 if (punused_ifus[i] == 0) {
1753 edge_table_flat = kmclipm_cal_table_load(flat_edge_filename,
1754 ifu_nr, rotangle, 0, &tmp_rotangle);
1755 /* Shift values for each IFU by comparing edge parameters */
1756 if (edge_table_flat != NULL) {
1757 edge_vec = cpl_vector_new(2*KMOS_SLITLET_X);
1758 for (edgeNr = 0; edgeNr < 2*KMOS_SLITLET_X; edgeNr++) {
1759 flatval = kmo_calc_fitted_slitlet_edge(edge_table_flat,
1760 edgeNr, middle_row);
1761 skyval = kmo_calc_fitted_slitlet_edge(edges[i], edgeNr,
1762 middle_row);
1763 cpl_vector_set(edge_vec, edgeNr, flatval-skyval);
1764 }
1765 cpl_table_delete(edge_table_flat);
1766
1767 /* Reject deviating edge-differences */
1768 kv = kmclipm_vector_create(edge_vec);
1769 kmclipm_reject_deviant(kv, 3, 3, NULL, NULL);
1770
1771 /* Set shift value for each IFU */
1772 cpl_vector_set(shift_vec, i,
1773 kmclipm_vector_get_median(kv, KMCLIPM_ARITHMETIC));
1774 kmclipm_vector_delete(kv);
1775 } else {
1776 cpl_vector_set(shift_vec, i, 0.0) ;
1777 }
1778 } else {
1779 cpl_vector_set(shift_vec, i, 0.0) ;
1780 }
1781 }
1782
1783 /* Take median of all IFU-shift-values */
1784 shift_val = -cpl_vector_get_median(shift_vec);
1785 cpl_vector_delete(shift_vec);
1786
1787 cpl_msg_info(__func__, "Shift detector %d by %g pixels", det_nr, shift_val);
1788 nx = cpl_image_get_size_x(combined_data),
1789 ny = cpl_image_get_size_x(combined_data),
1790 pcombined_data = cpl_image_get_data_float(combined_data) ;
1791 if (process_noise) {
1792 pcombined_noise = cpl_image_get_data_float(combined_noise);
1793 }
1794
1795 array_in = cpl_calloc(nx, sizeof(double)) ;
1796 /* Apply shift - Cubic spline */
1797 for (iy = 0; iy < ny; iy++) {
1798 for (ix = 0; ix < nx; ix++) array_in[ix] = pcombined_data[ix+iy*nx];
1799 array_out = cubicspline_reg_reg(nx, 0., 1., array_in, nx, shift_val,
1800 1.0, NATURAL);
1801 for (ix = 0; ix < nx; ix++) pcombined_data[ix+iy*nx] = array_out[ix];
1802 cpl_free(array_out);
1803
1804 if (process_noise) {
1805 for (ix = 0; ix < nx; ix++) array_in[ix]=pcombined_noise[ix+iy*nx];
1806 array_out = cubicspline_reg_reg(nx, 0., 1., array_in, nx, shift_val,
1807 1.0, NATURAL);
1808 for (ix = 0; ix < nx; ix++) pcombined_noise[ix+iy*nx]=array_out[ix];
1809 cpl_free(array_out);
1810 }
1811 }
1812 cpl_free(array_in);
1813 return edges ;
1814}
1815
1816
int cpl_plugin_get_info(cpl_pluginlist *list)
Build the list of available plugins, for this module.