00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031
00032 #include <math.h>
00033 #include <cpl.h>
00034 #include <moses.h>
00035 #include <fors_dfs.h>
00036
00037 static int fors_subtract_sky_create(cpl_plugin *);
00038 static int fors_subtract_sky_exec(cpl_plugin *);
00039 static int fors_subtract_sky_destroy(cpl_plugin *);
00040 static int fors_subtract_sky(cpl_parameterlist *, cpl_frameset *);
00041
00042 static char fors_subtract_sky_description[] =
00043 "This recipe is used to subtract the sky emission from unrebinned slit\n"
00044 "spectra. This is obtained by robust fitting (i.e., excluding the signal\n"
00045 "from possible point-like objects in slit) of the emission along the CCD\n"
00046 "columns within each spectrum). This method doesn't work if extended\n"
00047 "objects are in slit (it really destroys the object spectra), and is\n"
00048 "not applicable to LSS data. The input scientific frames are produced\n"
00049 "by the recipes fors_remove_bias and fors_flatfield.\n"
00050 "\n"
00051 "This recipe cannot be applied to LSS or long-slit like data (MOS/MXU with\n"
00052 "all slits at the same offset). No automatic recipe is available for this.\n"
00053 "Please refer to the FORS Pipeline User's Manual for more details.\n"
00054 "\n"
00055 "In the table below the MXU acronym can be alternatively read as MOS, and\n"
00056 "SCI as STD.\n\n"
00057 "Input files:\n\n"
00058 " DO category: Type: Explanation: Required:\n"
00059 " SCIENCE_UNBIAS_MXU\n"
00060 " or SCIENCE_UNFLAT_MXU\n"
00061 " or STANDARD_UNBIAS_MXU\n"
00062 " or STANDARD_UNFLAT_MXU Calib Frame with sky lines Y\n"
00063 " CURV_COEFF_MXU Calib Spectral curvature Y\n"
00064 " SLIT_LOCATION_MXU Calib Slit location on CCD Y\n"
00065 " GRISM_TABLE Calib Grism table .\n\n"
00066 "Output files:\n\n"
00067 " DO category: Data type: Explanation:\n"
00068 " UNMAPPED_SCI_MXU\n"
00069 " or UNMAPPED_STD_MXU FITS image Sky subtracted scientific frame\n"
00070 " UNMAPPED_SKY_SCI_MXU\n"
00071 " or UNMAPPED_SKY_STD_MXU FITS image Subtracted sky frame\n\n";
00072
00073 #define fors_subtract_sky_exit(message) \
00074 { \
00075 if ((const char *)message != NULL) cpl_msg_error(recipe, message); \
00076 cpl_image_delete(spectra); \
00077 cpl_image_delete(skymap); \
00078 cpl_table_delete(grism_table); \
00079 cpl_table_delete(maskslits); \
00080 cpl_table_delete(slits); \
00081 cpl_table_delete(polytraces); \
00082 cpl_propertylist_delete(header); \
00083 cpl_msg_indent_less(); \
00084 return -1; \
00085 }
00086
00087 #define fors_subtract_sky_exit_memcheck(message) \
00088 { \
00089 if ((const char *)message != NULL) cpl_msg_info(recipe, message); \
00090 printf("free spectra (%p)\n", spectra); \
00091 cpl_image_delete(spectra); \
00092 printf("free skymap (%p)\n", skymap); \
00093 cpl_image_delete(skymap); \
00094 printf("free grism_table (%p)\n", grism_table); \
00095 cpl_table_delete(grism_table); \
00096 printf("free maskslits (%p)\n", maskslits); \
00097 cpl_table_delete(maskslits); \
00098 printf("free slits (%p)\n", slits); \
00099 cpl_table_delete(slits); \
00100 printf("free polytraces (%p)\n", polytraces); \
00101 cpl_table_delete(polytraces); \
00102 printf("free header (%p)\n", header); \
00103 cpl_propertylist_delete(header); \
00104 cpl_msg_indent_less(); \
00105 return 0; \
00106 }
00107
00108
00120 int cpl_plugin_get_info(cpl_pluginlist *list)
00121 {
00122 cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00123 cpl_plugin *plugin = &recipe->interface;
00124
00125 cpl_plugin_init(plugin,
00126 CPL_PLUGIN_API,
00127 FORS_BINARY_VERSION,
00128 CPL_PLUGIN_TYPE_RECIPE,
00129 "fors_subtract_sky",
00130 "Subtract sky from scientific spectra",
00131 fors_subtract_sky_description,
00132 "Carlo Izzo",
00133 PACKAGE_BUGREPORT,
00134 "This file is currently part of the FORS Instrument Pipeline\n"
00135 "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00136 "This program is free software; you can redistribute it and/or modify\n"
00137 "it under the terms of the GNU General Public License as published by\n"
00138 "the Free Software Foundation; either version 2 of the License, or\n"
00139 "(at your option) any later version.\n\n"
00140 "This program is distributed in the hope that it will be useful,\n"
00141 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00142 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00143 "GNU General Public License for more details.\n\n"
00144 "You should have received a copy of the GNU General Public License\n"
00145 "along with this program; if not, write to the Free Software Foundation,\n"
00146 "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n",
00147 fors_subtract_sky_create,
00148 fors_subtract_sky_exec,
00149 fors_subtract_sky_destroy);
00150
00151 cpl_pluginlist_append(list, plugin);
00152
00153 return 0;
00154 }
00155
00156
00167 static int fors_subtract_sky_create(cpl_plugin *plugin)
00168 {
00169 cpl_recipe *recipe;
00170 cpl_parameter *p;
00171
00172
00173
00174
00175
00176 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
00177 recipe = (cpl_recipe *)plugin;
00178 else
00179 return -1;
00180
00181
00182
00183
00184
00185 recipe->parameters = cpl_parameterlist_new();
00186
00187
00188
00189
00190
00191 p = cpl_parameter_new_value("fors.fors_subtract_sky.dispersion",
00192 CPL_TYPE_DOUBLE,
00193 "Expected spectral dispersion (Angstrom/pixel)",
00194 "fors.fors_subtract_sky",
00195 0.0);
00196 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00197 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00198 cpl_parameterlist_append(recipe->parameters, p);
00199
00200
00201
00202
00203
00204 p = cpl_parameter_new_value("fors.fors_subtract_sky.startwavelength",
00205 CPL_TYPE_DOUBLE,
00206 "Start wavelength in spectral extraction",
00207 "fors.fors_subtract_sky",
00208 0.0);
00209 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00210 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00211 cpl_parameterlist_append(recipe->parameters, p);
00212
00213
00214
00215
00216
00217 p = cpl_parameter_new_value("fors.fors_subtract_sky.endwavelength",
00218 CPL_TYPE_DOUBLE,
00219 "End wavelength in spectral extraction",
00220 "fors.fors_subtract_sky",
00221 0.0);
00222 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00223 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00224 cpl_parameterlist_append(recipe->parameters, p);
00225
00226
00227
00228
00229
00230 p = cpl_parameter_new_value("fors.fors_subtract_sky.cosmics",
00231 CPL_TYPE_BOOL,
00232 "Eliminate cosmic rays hits",
00233 "fors.fors_subtract_sky",
00234 FALSE);
00235 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cosmics");
00236 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00237 cpl_parameterlist_append(recipe->parameters, p);
00238
00239 return 0;
00240 }
00241
00242
00251 static int fors_subtract_sky_exec(cpl_plugin *plugin)
00252 {
00253 cpl_recipe *recipe;
00254
00255 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
00256 recipe = (cpl_recipe *)plugin;
00257 else
00258 return -1;
00259
00260 return fors_subtract_sky(recipe->parameters, recipe->frames);
00261 }
00262
00263
00272 static int fors_subtract_sky_destroy(cpl_plugin *plugin)
00273 {
00274 cpl_recipe *recipe;
00275
00276 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
00277 recipe = (cpl_recipe *)plugin;
00278 else
00279 return -1;
00280
00281 cpl_parameterlist_delete(recipe->parameters);
00282
00283 return 0;
00284 }
00285
00286
00296 static int fors_subtract_sky(cpl_parameterlist *parlist,
00297 cpl_frameset *frameset)
00298 {
00299
00300 const char *recipe = "fors_subtract_sky";
00301
00302
00303
00304
00305
00306
00307 double dispersion;
00308 double startwavelength;
00309 double endwavelength;
00310 int cosmics;
00311
00312
00313
00314
00315
00316 cpl_image *spectra = NULL;
00317 cpl_image *skymap = NULL;
00318 cpl_table *grism_table = NULL;
00319 cpl_table *polytraces = NULL;
00320 cpl_table *slits = NULL;
00321 cpl_table *maskslits = NULL;
00322 cpl_propertylist *header = NULL;
00323
00324
00325
00326
00327
00328 char version[80];
00329 const char *slit_location_tag;
00330 const char *input_tag;
00331 const char *curv_coeff_tag;
00332 const char *unmapped_tag;
00333 const char *unmapped_sky_tag;
00334 int nframes;
00335 int rebin;
00336 int nslits;
00337 int treat_as_lss;
00338 int i;
00339 double reference;
00340 double gain;
00341 double *xpos;
00342 double mxpos;
00343 int mxu, mos, lss;
00344 int rec_scib;
00345 int rec_stdb;
00346 int rec_scif;
00347 int rec_stdf;
00348 int nslits_out_det = 0;
00349
00350 char *instrume = NULL;
00351
00352
00353 cpl_msg_set_indentation(2);
00354
00355
00356
00357
00358
00359
00360 cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00361 cpl_msg_indent_more();
00362
00363 if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00364 fors_subtract_sky_exit("Too many in input: GRISM_TABLE");
00365
00366 grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00367
00368 dispersion = dfs_get_parameter_double(parlist,
00369 "fors.fors_subtract_sky.dispersion", grism_table);
00370
00371 if (dispersion <= 0.0)
00372 fors_subtract_sky_exit("Invalid spectral dispersion value");
00373
00374 startwavelength = dfs_get_parameter_double(parlist,
00375 "fors.fors_subtract_sky.startwavelength", grism_table);
00376 if (startwavelength > 1.0)
00377 if (startwavelength < 3000.0 || startwavelength > 13000.0)
00378 fors_subtract_sky_exit("Invalid wavelength");
00379
00380 endwavelength = dfs_get_parameter_double(parlist,
00381 "fors.fors_subtract_sky.endwavelength", grism_table);
00382 if (endwavelength > 1.0) {
00383 if (endwavelength < 3000.0 || endwavelength > 13000.0)
00384 fors_subtract_sky_exit("Invalid wavelength");
00385 if (startwavelength < 1.0)
00386 fors_subtract_sky_exit("Invalid wavelength interval");
00387 }
00388
00389 if (startwavelength > 1.0)
00390 if (endwavelength - startwavelength <= 0.0)
00391 fors_subtract_sky_exit("Invalid wavelength interval");
00392
00393 cosmics = dfs_get_parameter_bool(parlist,
00394 "fors.fors_subtract_sky.cosmics", NULL);
00395
00396 cpl_table_delete(grism_table); grism_table = NULL;
00397
00398 if (cpl_error_get_code())
00399 fors_subtract_sky_exit("Failure reading the configuration parameters");
00400
00401
00402 cpl_msg_indent_less();
00403 cpl_msg_info(recipe, "Check input set-of-frames:");
00404 cpl_msg_indent_more();
00405
00406 mxu = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_MXU");
00407 mos = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_MOS");
00408 lss = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_LSS");
00409
00410 if (lss)
00411 fors_subtract_sky_exit("Use this recipe just with MOS/MXU data.");
00412
00413 nframes = mos + mxu;
00414
00415 if (nframes == 0) {
00416 fors_subtract_sky_exit("Missing input slit location table");
00417 }
00418 if (nframes > 1) {
00419 cpl_msg_error(recipe,
00420 "Too many input slit location tables (%d > 1)", nframes);
00421 fors_subtract_sky_exit(NULL);
00422 }
00423
00424 if (mxu)
00425 curv_coeff_tag = "CURV_COEFF_MXU";
00426 else
00427 curv_coeff_tag = "CURV_COEFF_MXU";
00428
00429
00430 nframes = cpl_frameset_count_tags(frameset, curv_coeff_tag);
00431
00432 if (nframes == 0) {
00433 cpl_msg_error(recipe, "Missing input %s", curv_coeff_tag);
00434 fors_subtract_sky_exit(NULL);
00435 }
00436 if (nframes > 1) {
00437 cpl_msg_error(recipe, "Too many input %s (%d > 1)", curv_coeff_tag,
00438 nframes);
00439 fors_subtract_sky_exit(NULL);
00440 }
00441
00442 if (mxu) {
00443 rec_scib = cpl_frameset_count_tags(frameset, "SCIENCE_UNBIAS_MXU");
00444 rec_stdb = cpl_frameset_count_tags(frameset, "STANDARD_UNBIAS_MXU");
00445 rec_scif = cpl_frameset_count_tags(frameset, "SCIENCE_UNFLAT_MXU");
00446 rec_stdf = cpl_frameset_count_tags(frameset, "STANDARD_UNFLAT_MXU");
00447 }
00448 else {
00449 rec_scib = cpl_frameset_count_tags(frameset, "SCIENCE_UNBIAS_MOS");
00450 rec_stdb = cpl_frameset_count_tags(frameset, "STANDARD_UNBIAS_MOS");
00451 rec_scif = cpl_frameset_count_tags(frameset, "SCIENCE_UNFLAT_MOS");
00452 rec_stdf = cpl_frameset_count_tags(frameset, "STANDARD_UNFLAT_MOS");
00453 }
00454
00455 nframes = rec_scib + rec_stdb + rec_scif + rec_stdf;
00456
00457 if (nframes == 0) {
00458 fors_subtract_sky_exit("Missing input scientific spectra");
00459 }
00460 if (nframes > 1) {
00461 cpl_msg_error(recipe, "Too many input scientific spectra (%d > 1)",
00462 nframes);
00463 fors_subtract_sky_exit(NULL);
00464 }
00465
00466 if (rec_scib) {
00467 if (mxu) {
00468 input_tag = "SCIENCE_UNBIAS_MXU";
00469 slit_location_tag = "SLIT_LOCATION_MXU";
00470 unmapped_tag = "UNMAPPED_SCI_MXU";
00471 unmapped_sky_tag = "UNMAPPED_SKY_SCI_MXU";
00472 }
00473 else {
00474 input_tag = "SCIENCE_UNBIAS_MOS";
00475 slit_location_tag = "SLIT_LOCATION_MOS";
00476 unmapped_tag = "UNMAPPED_SCI_MOS";
00477 unmapped_sky_tag = "UNMAPPED_SKY_SCI_MOS";
00478 }
00479 }
00480 else if (rec_stdb) {
00481 if (mxu) {
00482 input_tag = "STANDARD_UNBIAS_MXU";
00483 slit_location_tag = "SLIT_LOCATION_MXU";
00484 unmapped_tag = "UNMAPPED_STD_MXU";
00485 unmapped_sky_tag = "UNMAPPED_SKY_STD_MXU";
00486 }
00487 else {
00488 input_tag = "STANDARD_UNBIAS_MOS";
00489 slit_location_tag = "SLIT_LOCATION_MOS";
00490 unmapped_tag = "UNMAPPED_STD_MOS";
00491 unmapped_sky_tag = "UNMAPPED_SKY_STD_MOS";
00492 }
00493 }
00494 else if (rec_scif) {
00495 if (mxu) {
00496 input_tag = "SCIENCE_UNFLAT_MXU";
00497 slit_location_tag = "SLIT_LOCATION_MXU";
00498 unmapped_tag = "UNMAPPED_SCI_MXU";
00499 unmapped_sky_tag = "UNMAPPED_SKY_SCI_MXU";
00500 }
00501 else {
00502 input_tag = "SCIENCE_UNFLAT_MOS";
00503 slit_location_tag = "SLIT_LOCATION_MOS";
00504 unmapped_tag = "UNMAPPED_SCI_MOS";
00505 unmapped_sky_tag = "UNMAPPED_SKY_SCI_MOS";
00506 }
00507 }
00508 else if (rec_stdf) {
00509 if (mxu) {
00510 input_tag = "STANDARD_UNFLAT_MXU";
00511 slit_location_tag = "SLIT_LOCATION_MXU";
00512 unmapped_tag = "UNMAPPED_STD_MXU";
00513 unmapped_sky_tag = "UNMAPPED_SKY_STD_MXU";
00514 }
00515 else {
00516 input_tag = "STANDARD_UNFLAT_MOS";
00517 slit_location_tag = "SLIT_LOCATION_MOS";
00518 unmapped_tag = "UNMAPPED_STD_MOS";
00519 unmapped_sky_tag = "UNMAPPED_SKY_STD_MOS";
00520 }
00521 }
00522
00523
00524 header = dfs_load_header(frameset, input_tag, 0);
00525
00526 if (header == NULL)
00527 fors_subtract_sky_exit("Cannot load scientific frame header");
00528
00529 if (mos)
00530 maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
00531 else
00532 maskslits = mos_load_slits_fors_mxu(header);
00533
00534
00535
00536
00537
00538 treat_as_lss = fors_mos_is_lss_like(maskslits, nslits_out_det);
00539
00540 cpl_table_delete(maskslits); maskslits = NULL;
00541
00542 if (treat_as_lss)
00543 fors_subtract_sky_exit("This recipe cannot process MOS/MXU "
00544 "data with all slits at the same offset.");
00545
00546
00547 if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
00548 cpl_msg_warning(cpl_func,"Input frames are not from the same grism");
00549
00550 if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
00551 cpl_msg_warning(cpl_func,"Input frames are not from the same filter");
00552
00553 if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
00554 cpl_msg_warning(cpl_func,"Input frames are not from the same chip");
00555
00556
00557
00558
00559
00560
00561
00562 instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
00563 if (instrume == NULL)
00564 fors_subtract_sky_exit("Missing keyword INSTRUME in reference frame "
00565 "header");
00566
00567 if (instrume[4] == '1')
00568 snprintf(version, 80, "%s/%s", "fors1", VERSION);
00569 if (instrume[4] == '2')
00570 snprintf(version, 80, "%s/%s", "fors2", VERSION);
00571
00572 reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
00573
00574 if (cpl_error_get_code() != CPL_ERROR_NONE)
00575 fors_subtract_sky_exit("Missing keyword ESO INS GRIS1 WLEN "
00576 "in reference frame header");
00577
00578 if (reference < 3000.0)
00579 reference *= 10;
00580
00581 if (reference < 3000.0 || reference > 13000.0) {
00582 cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
00583 "keyword ESO INS GRIS1 WLEN in reference frame header",
00584 reference);
00585 fors_subtract_sky_exit(NULL);
00586 }
00587
00588 cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
00589
00590 rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
00591
00592 if (cpl_error_get_code() != CPL_ERROR_NONE)
00593 fors_subtract_sky_exit("Missing keyword ESO DET WIN1 BINX "
00594 "in reference frame header");
00595
00596 if (rebin != 1) {
00597 dispersion *= rebin;
00598 cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
00599 "working dispersion used is %f A/pixel", rebin,
00600 dispersion);
00601 }
00602
00603 if (cosmics) {
00604 gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
00605
00606 if (cpl_error_get_code() != CPL_ERROR_NONE)
00607 fors_subtract_sky_exit("Missing keyword ESO DET OUT1 CONAD in "
00608 "scientific frame header");
00609
00610 cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
00611 }
00612
00613
00614 cpl_msg_indent_less();
00615 cpl_msg_info(recipe, "Load input frames...");
00616 cpl_msg_indent_more();
00617
00618 spectra = dfs_load_image(frameset, input_tag, CPL_TYPE_FLOAT, 0, 0);
00619 if (spectra == NULL)
00620 fors_subtract_sky_exit("Cannot load input scientific frame");
00621
00622 slits = dfs_load_table(frameset, slit_location_tag, 1);
00623 if (slits == NULL)
00624 fors_subtract_sky_exit("Cannot load slits location table");
00625
00626 polytraces = dfs_load_table(frameset, curv_coeff_tag, 1);
00627 if (polytraces == NULL)
00628 fors_subtract_sky_exit("Cannot load spectral curvature table");
00629
00630 cpl_msg_indent_less();
00631 cpl_msg_info(recipe, "Local sky determination...");
00632 cpl_msg_indent_more();
00633 skymap = mos_subtract_sky(spectra, slits, polytraces, reference,
00634 startwavelength, endwavelength, dispersion);
00635
00636 cpl_table_delete(polytraces); polytraces = NULL;
00637 cpl_table_delete(slits); slits = NULL;
00638
00639 if (cosmics) {
00640 cpl_msg_info(recipe, "Removing cosmic rays...");
00641 mos_clean_cosmics(spectra, gain, -1., -1.);
00642 }
00643
00644 if (dfs_save_image(frameset, spectra, unmapped_tag,
00645 header, parlist, recipe, version))
00646 fors_subtract_sky_exit(NULL);
00647
00648 cpl_image_delete(spectra); spectra = NULL;
00649
00650 if (dfs_save_image(frameset, skymap, unmapped_sky_tag,
00651 header, parlist, recipe, version))
00652 fors_subtract_sky_exit(NULL);
00653
00654 cpl_image_delete(skymap); skymap = NULL;
00655
00656 cpl_propertylist_delete(header); header = NULL;
00657
00658 return 0;
00659 }