ERIS Pipeline Reference Manual 1.9.2
eris_nix_lss_skysub.c
1/* $Id$
2 *
3 * This file is part of the ERIS Pipeline
4 * Copyright (C) 2017 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/*
22 * $Author$
23 * $Date$
24 * $Revision$
25 */
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31/*-----------------------------------------------------------------------------
32 Includes
33 -----------------------------------------------------------------------------*/
34
35#include <libgen.h>
36#include <string.h>
37
38#include "eris_nix_utils.h"
39#include "eris_nix_lss_utils.h"
40#include "eris_nix_dfs.h"
41#include "eris_utils.h"
42#include <hdrl.h>
43#include <cpl.h>
44
45/*-----------------------------------------------------------------------------
46 Static variables
47 -----------------------------------------------------------------------------*/
48
49static const char eris_nix_lss_skysub_description[] =
50"This recipe estimates and subtracts the sky background from a set of \n"
51ERIS_NIX_CAL_DET_OBJECT_LSS_JITTER_PRO_CATG" or "
52ERIS_NIX_CAL_DET_STD_LSS_JITTER_PRO_CATG" frames.\n"
53"\n"
54"Input files:\n"
55"\n"
56" The recipe has 2 modes of operation.\n"
57" If --sky-source='target' then the sky background will be estimated \n"
58" from the target frames themselves.\n"
59"\n"
60" DO CATG Explanation Req. #Frames\n"
61" ------- ----------- --- -------\n"
62" "ERIS_NIX_CAL_DET_OBJECT_LSS_JITTER_PRO_CATG
63 " target frames with the Y 1-n\n"
64" or detector signature\n"
65" "ERIS_NIX_CAL_DET_STD_LSS_JITTER_PRO_CATG
66 " removed.\n"
67"\n"
68" If --sky-source='offset' and for "
69ERIS_NIX_CAL_DET_OBJECT_LSS_JITTER_PRO_CATG"\n"
70" data only, the sky background will be estimated from a set of \n"
71" frames offset from the target region.\n"
72"\n"
73" DO CATG Explanation Req. #Frames\n"
74" ------- ----------- --- -------\n"
75" "ERIS_NIX_CAL_DET_OBJECT_LSS_JITTER_PRO_CATG
76 " target frames with the Y 1-n\n"
77" detector signature\n"
78" removed.\n"
79" "ERIS_NIX_CAL_DET_SKY_LSS_JITTER_PRO_CATG
80 " sky frames with the Y 1-m\n"
81" detector signature\n"
82" removed.\n"
83"\n"
84"Output files:\n"
85"\n"
86" DO CATG Explanation \n"
87" ------- ----------- \n"
88" "ERIS_NIX_SKYSUB_OBJECT_LSS_JITTER_PRO_CATG
89 " target frames with the sky removed.\n"
90" or\n"
91" "ERIS_NIX_SKYSUB_STD_LSS_JITTER_PRO_CATG"\n"
92"\n"
93" The output will be FITS files named 'skysub.<input filename>',\n"
94" containing extensions:\n"
95" - DATA, with the target data.\n"
96" - ERR, with the target error plane.\n"
97" - DQ, with the target data quality plane.\n"
98" - CONFIDENCE, with the target confidence plane.\n"
99" - BKG_DATA, with the estimated sky background.\n"
100" - BKG_ERR, with the error on the sky background.\n"
101" - BKG_CONF, with the confidence of the sky background.\n"
102"\n"
103"Notes on the method.\n"
104" Sky frames are taken from the pool of target or offset\n"
105" according to param --sky-source.\n"
106"\n"
107" The background for each frame is estimated from sky frames\n"
108" taken within sky-bracket-time/2 seconds of it.\n"
109"\n"
110" A first estimate of the background is made.\n"
111" If param --sky-method='collapse-median':\n"
112" - The estimate is a median collapse of the sky frames\n"
113" selected. This is prone to problems if the field is\n"
114" very crowded.\n"
115" If --sky-method='median-median':\n"
116" - The median of each sky frame is obtained, and the\n"
117" background frame set at the median of those values.\n"
118" The background is forced to be flat, which is more\n"
119" robust in crowded fields, or where there is\n"
120" nebulosity.\n"
121"\n"
122" The estimated backgrounds are subtracted from the sky\n"
123" frames, and a search made for objects in each image.\n"
124" Object masks are constructed which blank out astronomical\n"
125" flux the sky frames. The --catalogue.xxx params listed\n"
126" below are used in this search for objects.\n"
127"\n"
128" The masked sky frames are then used to re-estimate the\n"
129" sky backgrounds following the same sky-method.\n";
130
131
132#define RECIPE_NAME "eris.eris_nix_lss_skysub"
133
134/*-----------------------------------------------------------------------------
135 Private function prototypes
136 -----------------------------------------------------------------------------*/
137
138cpl_recipe_define(eris_nix_lss_skysub, ERIS_BINARY_VERSION,
139 "John Lightfoot",
140 PACKAGE_BUGREPORT, "2017",
141 "Subtract sky background from LSS frames",
142 eris_nix_lss_skysub_description);
143
144/*-----------------------------------------------------------------------------
145 Function code
146 -----------------------------------------------------------------------------*/
147
148/*----------------------------------------------------------------------------*/
156/*----------------------------------------------------------------------------*/
157
158static cpl_error_code eris_nix_lss_skysub_fill_parameterlist(
159 cpl_parameterlist * self) {
160
161 if (cpl_error_get_code() != CPL_ERROR_NONE) return cpl_error_get_code();
162
163 cpl_parameter * p = NULL;
164
165 /* recipe-specific parameters */
166
167 p = cpl_parameter_new_enum(RECIPE_NAME".sky-source", CPL_TYPE_STRING,
168 "data to be used for calculation of sky "
169 "background", RECIPE_NAME,
170 "auto", 3, "auto", "target", "offset");
171 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sky-source");
172 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
173 cpl_parameterlist_append(self, p);
174
175 p = cpl_parameter_new_enum(RECIPE_NAME".sky-selector", CPL_TYPE_STRING,
176 "method for selecting sky frames",
177 RECIPE_NAME, "bracket", 1, "bracket");
178 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sky-selector");
179 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
180 cpl_parameterlist_append(self, p);
181
182 p = cpl_parameter_new_range(RECIPE_NAME".sky-bracket-time", CPL_TYPE_DOUBLE,
183 "2 * max.time between target and sky measurement",
184 RECIPE_NAME, 1800.0, 60.0, 18000.0);
185 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sky-bracket-time");
186 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
187 cpl_parameterlist_append(self, p);
188
189 p = cpl_parameter_new_value(RECIPE_NAME".premask_objects",
190 CPL_TYPE_BOOL,
191 "create object masks before background estimation",
192 RECIPE_NAME, CPL_TRUE);
193 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "premask-objects");
194 cpl_parameterlist_append(self, p);
195
196 p = cpl_parameter_new_value(RECIPE_NAME".premask_nsigma",
197 CPL_TYPE_DOUBLE,
198 "sigma cutoff for premask object detection",
199 RECIPE_NAME, 5.0);
200 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "premask-nsigma");
201 cpl_parameterlist_append(self, p);
202
203 p = cpl_parameter_new_value(RECIPE_NAME".debug-data",
204 CPL_TYPE_BOOL, "true to save interim results",
205 RECIPE_NAME, CPL_FALSE);
206 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "debug-data");
207 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
208 cpl_parameterlist_append(self, p);
209
210 /* coords of pixel to be used for diagnostics during reduction */
211
212 p = cpl_parameter_new_value(RECIPE_NAME".x_probe", CPL_TYPE_INT,
213 "x coord of diagnostic pixel",
214 RECIPE_NAME, -1);
215 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "x-probe");
216 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
217 cpl_parameterlist_append(self, p);
218
219 p = cpl_parameter_new_value(RECIPE_NAME".y_probe", CPL_TYPE_INT,
220 "y coord of diagnostic pixel",
221 RECIPE_NAME, -1);
222 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "y-probe");
223 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
224 cpl_parameterlist_append(self, p);
225
226 return 0;
227}
228
229
230/*----------------------------------------------------------------------------*/
237/*----------------------------------------------------------------------------*/
238
239static int eris_nix_lss_skysub(cpl_frameset * frameset,
240 const cpl_parameterlist * parlist) {
241
242 located_imagelist * object_jitters = NULL;
243 const char * out_tag = NULL;
244 const cpl_parameter * p = NULL;
245 located_imagelist * sky_jitters = NULL;
246 located_imagelist * std_jitters = NULL;
247 located_imagelist * target_jitters = NULL;
248 cpl_frameset * used_frameset = NULL;
249
250 enu_check_error_code("%s():%d: An error is already set: %s",
251 cpl_func, __LINE__, cpl_error_get_where());
252
253 /* Check input parameters */
254
255 cpl_ensure_code(frameset, CPL_ERROR_NULL_INPUT);
256 cpl_ensure_code(parlist, CPL_ERROR_NULL_INPUT);
257
258 /* Set the msg verbosity level from environment variable CPL_MSG_LEVEL */
259
260 cpl_msg_set_level_from_env();
261 cpl_msg_severity severity = cpl_msg_get_level();
262 cpl_msg_info(cpl_func, "level %d", (int) severity);
263
264 /* check for invalid input */
265 if(eris_files_dont_exist(frameset) != CPL_ERROR_NONE) {
266 return CPL_ERROR_BAD_FILE_FORMAT;
267 }
268 /* Retrieve input parameters */
269
270 p = cpl_parameterlist_find_const(parlist, RECIPE_NAME".sky-source");
271 const char * sky_source = cpl_parameter_get_string(p);
272 p = cpl_parameterlist_find_const(parlist, RECIPE_NAME".sky-selector");
273 const char * sky_selector = cpl_parameter_get_string(p);
274 p = cpl_parameterlist_find_const(parlist, RECIPE_NAME".sky-bracket-time");
275 const double sky_time_range = cpl_parameter_get_double(p);
276 p = cpl_parameterlist_find_const(parlist, RECIPE_NAME".premask_objects");
277 int premask_objects = cpl_parameter_get_bool(p);
278 p = cpl_parameterlist_find_const(parlist, RECIPE_NAME".premask_nsigma");
279 double premask_nsigma = cpl_parameter_get_double(p);
280/*
281 p = cpl_parameterlist_find_const(parlist, RECIPE_NAME".debug-data");
282 const int debug_data = cpl_parameter_get_bool(p);
283*/
284 p = cpl_parameterlist_find_const(parlist, RECIPE_NAME".x_probe");
285 cpl_size x_probe = (cpl_size) cpl_parameter_get_int(p);
286 p = cpl_parameterlist_find_const(parlist, RECIPE_NAME".y_probe");
287 cpl_size y_probe = (cpl_size) cpl_parameter_get_int(p);
288
289 enu_check_error_code("Could not retrieve input parameters");
290
291 /* Identify the RAW and CALIB frames in the input frameset */
292
293 eris_nix_dfs_set_groups(frameset);
294 enu_check_error_code("Could not identify RAW and CALIB frames");
295
296 used_frameset = cpl_frameset_new();
297
298 /* Read in CAL_DET_OBJECT_LSS_JITTER data, CAL_DET_SKY_LSS_JITTER, or
299 CAL_DET_STD_LSS_JITTER frames if present */
300
301 object_jitters = enu_limlist_load_from_frameset(frameset,
302 ERIS_NIX_CAL_DET_OBJECT_LSS_JITTER_PRO_CATG,
303 used_frameset);
304 sky_jitters = enu_limlist_load_from_frameset(frameset,
305 ERIS_NIX_CAL_DET_SKY_LSS_JITTER_PRO_CATG, used_frameset);
306 std_jitters = enu_limlist_load_from_frameset(frameset,
307 ERIS_NIX_CAL_DET_STD_LSS_JITTER_PRO_CATG, used_frameset);
308 enu_check_error_code("Could not load input frames");
309
310 cpl_msg_info(cpl_func, "%d "
311 ERIS_NIX_CAL_DET_OBJECT_LSS_JITTER_PRO_CATG" frames read",
312 (int) object_jitters->size);
313 cpl_msg_info(cpl_func, "%d "
314 ERIS_NIX_CAL_DET_SKY_LSS_JITTER_PRO_CATG" frames read",
315 (int) sky_jitters->size);
316 cpl_msg_info(cpl_func, "%d "
317 ERIS_NIX_CAL_DET_STD_LSS_JITTER_PRO_CATG" frames read",
318 (int) std_jitters->size);
319
320 /* check that the SOF collection makes sense */
321
322 enu_check(!(object_jitters->size == 0 && std_jitters->size == 0),
323 CPL_ERROR_DATA_NOT_FOUND, "SoF contains no "
324 ERIS_NIX_CAL_DET_OBJECT_LSS_JITTER_PRO_CATG" or "
325 ERIS_NIX_CAL_DET_STD_LSS_JITTER_PRO_CATG" frames");
326 enu_check(!(object_jitters->size > 0 && std_jitters->size > 0),
327 CPL_ERROR_ILLEGAL_INPUT, "SoF contains both "
328 ERIS_NIX_CAL_DET_OBJECT_LSS_JITTER_PRO_CATG" and "
329 ERIS_NIX_CAL_DET_STD_LSS_JITTER_PRO_CATG" frames");
330 enu_check(!(!strcmp(sky_source,"offset") && sky_jitters->size==0),
331 CPL_ERROR_INCOMPATIBLE_INPUT,
332 "offset sky position requested but no"
333 ERIS_NIX_CAL_DET_SKY_LSS_JITTER_PRO_CATG" in sof");
334
335 /* is target an object or std? */
336
337 if (object_jitters->size > 0) {
338 target_jitters = object_jitters;
339 out_tag = ERIS_NIX_SKYSUB_OBJECT_LSS_JITTER_PRO_CATG;
340 } else if (std_jitters->size > 0) {
341 target_jitters = std_jitters;
342 out_tag = ERIS_NIX_SKYSUB_STD_LSS_JITTER_PRO_CATG;
343 }
344
345 /* do right thing when sky-source=="auto", namely use offset
346 sky data if available */
347
348 if (!strcmp(sky_source, "auto")) {
349 if (sky_jitters->size > 0) {
350 sky_source = "offset";
351 } else {
352 sky_source = "target";
353 }
354 cpl_msg_info(cpl_func, "sky-source=='auto', resetting to '%s'",
355 sky_source);
356 }
357
358 if (!strcmp(sky_source, "offset")) {
359
360 if (premask_objects) {
361
362 /* Calculate object masks for the 'offset' jitter images
363 and attach them the data.*/
364
365 enu_opm_lss_limlist(sky_jitters, premask_nsigma);
366 }
367
368 /* Use the masked images to estimate the sky background and
369 subtract it */
370
371 cpl_msg_info(cpl_func, "Estimating sky background with source blanking ...");
372 enu_sky_subtract_limlist("collapse-median", sky_selector, sky_time_range,
373 sky_jitters, x_probe, y_probe,
374 target_jitters);
375 enu_check_error_code("error in sky background subtraction");
376
377 } else if (!strcmp(sky_source, "target")) {
378
379 /* Use the target calibrated jitters to estimate the sky background.
380
381 This would work for a 'random' jitter pattern. However, NACO
382 data switches between 2 nod positions on the slit, in which
383 case a straight median would not - instead need to mask out
384 the objects first. */
385
386 cpl_msg_info(cpl_func, "Estimating sky background:");
387 if (premask_objects) {
388
389 /* Calculate object masks for the target jitter images
390 and attach them the data.*/
391
392 enu_opm_lss_limlist(target_jitters, premask_nsigma);
393 }
394 enu_sky_subtract_limlist("collapse-median", sky_selector,
395 sky_time_range, target_jitters,
396 x_probe, y_probe, target_jitters);
397 enu_check_error_code("error in sky background: sky-subtract");
398 }
399
400 enu_check_error_code("failed to remove sky background");
401
402 /* estimate the slit profile from the sky background and divide by it */
403
404 enlu_divide_slit_response(target_jitters);
405
406 /* save the sky-subtracted jitter images to FITS files */
407
408 for (cpl_size i = 0; i < target_jitters->size; i++) {
409 cpl_propertylist * applist = cpl_propertylist_new();
410 cpl_propertylist_update_string(applist, CPL_DFS_PRO_CATG, out_tag);
411 cpl_propertylist_update_string(applist, "PRODCATG", "ANCILLARY.IMAGE");
412
413 /* Generate output file name, write the file */
414
415 char * out_fname = enu_repreface(cpl_frame_get_filename(
416 target_jitters->limages[i]->frame),
417 "skysub");
418 enu_dfs_save_limage(frameset,
419 parlist,
420 frameset,
421 CPL_TRUE,
422 target_jitters->limages[i],
423 RECIPE_NAME,
424 target_jitters->limages[i]->frame,
425 applist,
426 PACKAGE "/" PACKAGE_VERSION,
427 out_fname);
428
429 cpl_free(out_fname);
430 cpl_propertylist_delete(applist);
431 }
432
433cleanup:
434 enu_located_imagelist_delete(object_jitters);
435 enu_located_imagelist_delete(sky_jitters);
436 enu_located_imagelist_delete(std_jitters);
437 cpl_frameset_delete(used_frameset);
438
439 return (int) cpl_error_get_code();
440}
cpl_error_code enu_dfs_save_limage(cpl_frameset *allframes, const cpl_parameterlist *parlist, const cpl_frameset *provenance, const cpl_boolean prov_raw, const located_image *limage, const char *recipe, const cpl_frame *inherit, cpl_propertylist *applist, const char *pipe_id, const char *filename)
Save a located image structure to a MEF.
Definition: eris_nix_dfs.c:909
cpl_error_code eris_nix_dfs_set_groups(cpl_frameset *set)
Set the group as RAW or CALIB in a frameset.
Definition: eris_nix_dfs.c:58
cpl_error_code enlu_divide_slit_response(located_imagelist *jitters)
Divide LSS 2d-spectra by the slit response.
void enu_located_imagelist_delete(located_imagelist *limlist)
Delete a located_imagelist and its contents.
cpl_error_code enu_opm_lss_limlist(located_imagelist *limlist, const int nsigma_cut)
Calculate object masks for LSS images in a located_imagelist.
cpl_error_code enu_sky_subtract_limlist(const char *method, const char *select_method, const double timerange, const located_imagelist *sky_data, const cpl_size x_probe, const cpl_size y_probe, located_imagelist *target_data)
Estimate and subtract sky backgrounds for a list of target images.
located_imagelist * enu_limlist_load_from_frameset(cpl_frameset *frameset, const char *tag, cpl_frameset *used)
Load tagged data from a frameset into a located_imagelist.
char * enu_repreface(const char *filename, const char *preface)
Preface a raw filename with a string.
cpl_error_code eris_files_dont_exist(cpl_frameset *frameset)
Check if all SOF files exist.
Definition: eris_utils.c:867