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 (message) 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 (message) 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
00349 char *instrume = NULL;
00350
00351
00352 cpl_msg_set_indentation(2);
00353
00354 if (dfs_files_dont_exist(frameset))
00355 fors_subtract_sky_exit(NULL);
00356
00357
00358
00359
00360
00361
00362 cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00363 cpl_msg_indent_more();
00364
00365 if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00366 fors_subtract_sky_exit("Too many in input: GRISM_TABLE");
00367
00368 grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00369
00370 dispersion = dfs_get_parameter_double(parlist,
00371 "fors.fors_subtract_sky.dispersion", grism_table);
00372
00373 if (dispersion <= 0.0)
00374 fors_subtract_sky_exit("Invalid spectral dispersion value");
00375
00376 startwavelength = dfs_get_parameter_double(parlist,
00377 "fors.fors_subtract_sky.startwavelength", grism_table);
00378 if (startwavelength > 1.0)
00379 if (startwavelength < 3000.0 || startwavelength > 13000.0)
00380 fors_subtract_sky_exit("Invalid wavelength");
00381
00382 endwavelength = dfs_get_parameter_double(parlist,
00383 "fors.fors_subtract_sky.endwavelength", grism_table);
00384 if (endwavelength > 1.0) {
00385 if (endwavelength < 3000.0 || endwavelength > 13000.0)
00386 fors_subtract_sky_exit("Invalid wavelength");
00387 if (startwavelength < 1.0)
00388 fors_subtract_sky_exit("Invalid wavelength interval");
00389 }
00390
00391 if (startwavelength > 1.0)
00392 if (endwavelength - startwavelength <= 0.0)
00393 fors_subtract_sky_exit("Invalid wavelength interval");
00394
00395 cosmics = dfs_get_parameter_bool(parlist,
00396 "fors.fors_subtract_sky.cosmics", NULL);
00397
00398 cpl_table_delete(grism_table); grism_table = NULL;
00399
00400 if (cpl_error_get_code())
00401 fors_subtract_sky_exit("Failure reading the configuration parameters");
00402
00403
00404 cpl_msg_indent_less();
00405 cpl_msg_info(recipe, "Check input set-of-frames:");
00406 cpl_msg_indent_more();
00407
00408 mxu = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_MXU");
00409 mos = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_MOS");
00410 lss = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_LSS");
00411
00412 if (lss)
00413 fors_subtract_sky_exit("Use this recipe just with MOS/MXU data.");
00414
00415 nframes = mos + mxu;
00416
00417 if (nframes == 0) {
00418 fors_subtract_sky_exit("Missing input slit location table");
00419 }
00420 if (nframes > 1) {
00421 cpl_msg_error(recipe,
00422 "Too many input slit location tables (%d > 1)", nframes);
00423 fors_subtract_sky_exit(NULL);
00424 }
00425
00426 if (mxu)
00427 curv_coeff_tag = "CURV_COEFF_MXU";
00428 else
00429 curv_coeff_tag = "CURV_COEFF_MXU";
00430
00431
00432 nframes = cpl_frameset_count_tags(frameset, curv_coeff_tag);
00433
00434 if (nframes == 0) {
00435 cpl_msg_error(recipe, "Missing input %s", curv_coeff_tag);
00436 fors_subtract_sky_exit(NULL);
00437 }
00438 if (nframes > 1) {
00439 cpl_msg_error(recipe, "Too many input %s (%d > 1)", curv_coeff_tag,
00440 nframes);
00441 fors_subtract_sky_exit(NULL);
00442 }
00443
00444 if (mxu) {
00445 rec_scib = cpl_frameset_count_tags(frameset, "SCIENCE_UNBIAS_MXU");
00446 rec_stdb = cpl_frameset_count_tags(frameset, "STANDARD_UNBIAS_MXU");
00447 rec_scif = cpl_frameset_count_tags(frameset, "SCIENCE_UNFLAT_MXU");
00448 rec_stdf = cpl_frameset_count_tags(frameset, "STANDARD_UNFLAT_MXU");
00449 }
00450 else {
00451 rec_scib = cpl_frameset_count_tags(frameset, "SCIENCE_UNBIAS_MOS");
00452 rec_stdb = cpl_frameset_count_tags(frameset, "STANDARD_UNBIAS_MOS");
00453 rec_scif = cpl_frameset_count_tags(frameset, "SCIENCE_UNFLAT_MOS");
00454 rec_stdf = cpl_frameset_count_tags(frameset, "STANDARD_UNFLAT_MOS");
00455 }
00456
00457 nframes = rec_scib + rec_stdb + rec_scif + rec_stdf;
00458
00459 if (nframes == 0) {
00460 fors_subtract_sky_exit("Missing input scientific spectra");
00461 }
00462 if (nframes > 1) {
00463 cpl_msg_error(recipe, "Too many input scientific spectra (%d > 1)",
00464 nframes);
00465 fors_subtract_sky_exit(NULL);
00466 }
00467
00468 if (rec_scib) {
00469 if (mxu) {
00470 input_tag = "SCIENCE_UNBIAS_MXU";
00471 slit_location_tag = "SLIT_LOCATION_MXU";
00472 unmapped_tag = "UNMAPPED_SCI_MXU";
00473 unmapped_sky_tag = "UNMAPPED_SKY_SCI_MXU";
00474 }
00475 else {
00476 input_tag = "SCIENCE_UNBIAS_MOS";
00477 slit_location_tag = "SLIT_LOCATION_MOS";
00478 unmapped_tag = "UNMAPPED_SCI_MOS";
00479 unmapped_sky_tag = "UNMAPPED_SKY_SCI_MOS";
00480 }
00481 }
00482 else if (rec_stdb) {
00483 if (mxu) {
00484 input_tag = "STANDARD_UNBIAS_MXU";
00485 slit_location_tag = "SLIT_LOCATION_MXU";
00486 unmapped_tag = "UNMAPPED_STD_MXU";
00487 unmapped_sky_tag = "UNMAPPED_SKY_STD_MXU";
00488 }
00489 else {
00490 input_tag = "STANDARD_UNBIAS_MOS";
00491 slit_location_tag = "SLIT_LOCATION_MOS";
00492 unmapped_tag = "UNMAPPED_STD_MOS";
00493 unmapped_sky_tag = "UNMAPPED_SKY_STD_MOS";
00494 }
00495 }
00496 else if (rec_scif) {
00497 if (mxu) {
00498 input_tag = "SCIENCE_UNFLAT_MXU";
00499 slit_location_tag = "SLIT_LOCATION_MXU";
00500 unmapped_tag = "UNMAPPED_SCI_MXU";
00501 unmapped_sky_tag = "UNMAPPED_SKY_SCI_MXU";
00502 }
00503 else {
00504 input_tag = "SCIENCE_UNFLAT_MOS";
00505 slit_location_tag = "SLIT_LOCATION_MOS";
00506 unmapped_tag = "UNMAPPED_SCI_MOS";
00507 unmapped_sky_tag = "UNMAPPED_SKY_SCI_MOS";
00508 }
00509 }
00510 else if (rec_stdf) {
00511 if (mxu) {
00512 input_tag = "STANDARD_UNFLAT_MXU";
00513 slit_location_tag = "SLIT_LOCATION_MXU";
00514 unmapped_tag = "UNMAPPED_STD_MXU";
00515 unmapped_sky_tag = "UNMAPPED_SKY_STD_MXU";
00516 }
00517 else {
00518 input_tag = "STANDARD_UNFLAT_MOS";
00519 slit_location_tag = "SLIT_LOCATION_MOS";
00520 unmapped_tag = "UNMAPPED_STD_MOS";
00521 unmapped_sky_tag = "UNMAPPED_SKY_STD_MOS";
00522 }
00523 }
00524
00525
00526 header = dfs_load_header(frameset, input_tag, 0);
00527
00528 if (header == NULL)
00529 fors_subtract_sky_exit("Cannot load scientific frame header");
00530
00531 if (mos)
00532 maskslits = mos_load_slits_fors_mos(header);
00533 else
00534 maskslits = mos_load_slits_fors_mxu(header);
00535
00536
00537
00538
00539
00540 mxpos = cpl_table_get_column_median(maskslits, "xtop");
00541 xpos = cpl_table_get_data_double(maskslits, "xtop");
00542 nslits = cpl_table_get_nrow(maskslits);
00543
00544 treat_as_lss = 1;
00545 for (i = 0; i < nslits; i++) {
00546 if (fabs(mxpos-xpos[i]) > 0.01) {
00547 treat_as_lss = 0;
00548 break;
00549 }
00550 }
00551
00552 cpl_table_delete(maskslits); maskslits = NULL;
00553
00554 if (treat_as_lss)
00555 fors_subtract_sky_exit("This recipe cannot process MOS/MXU "
00556 "data with all slits at the same offset.");
00557
00558
00559 if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
00560 fors_subtract_sky_exit("Input frames are not from the same grism");
00561
00562 if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
00563 fors_subtract_sky_exit("Input frames are not from the same filter");
00564
00565 if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
00566 fors_subtract_sky_exit("Input frames are not from the same chip");
00567
00568
00569
00570
00571
00572
00573
00574 instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
00575 if (instrume == NULL)
00576 fors_subtract_sky_exit("Missing keyword INSTRUME in reference frame "
00577 "header");
00578
00579 if (instrume[4] == '1')
00580 snprintf(version, 80, "%s/%s", "fors1", VERSION);
00581 if (instrume[4] == '2')
00582 snprintf(version, 80, "%s/%s", "fors2", VERSION);
00583
00584 reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
00585
00586 if (cpl_error_get_code() != CPL_ERROR_NONE)
00587 fors_subtract_sky_exit("Missing keyword ESO INS GRIS1 WLEN "
00588 "in reference frame header");
00589
00590 if (reference < 3000.0)
00591 reference *= 10;
00592
00593 if (reference < 3000.0 || reference > 13000.0) {
00594 cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
00595 "keyword ESO INS GRIS1 WLEN in reference frame header",
00596 reference);
00597 fors_subtract_sky_exit(NULL);
00598 }
00599
00600 cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
00601
00602 rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
00603
00604 if (cpl_error_get_code() != CPL_ERROR_NONE)
00605 fors_subtract_sky_exit("Missing keyword ESO DET WIN1 BINX "
00606 "in reference frame header");
00607
00608 if (rebin != 1) {
00609 dispersion *= rebin;
00610 cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
00611 "working dispersion used is %f A/pixel", rebin,
00612 dispersion);
00613 }
00614
00615 if (cosmics) {
00616 gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
00617
00618 if (cpl_error_get_code() != CPL_ERROR_NONE)
00619 fors_subtract_sky_exit("Missing keyword ESO DET OUT1 CONAD in "
00620 "scientific frame header");
00621
00622 cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
00623 }
00624
00625
00626 cpl_msg_indent_less();
00627 cpl_msg_info(recipe, "Load input frames...");
00628 cpl_msg_indent_more();
00629
00630 spectra = dfs_load_image(frameset, input_tag, CPL_TYPE_FLOAT, 0, 0);
00631 if (spectra == NULL)
00632 fors_subtract_sky_exit("Cannot load input scientific frame");
00633
00634 slits = dfs_load_table(frameset, slit_location_tag, 1);
00635 if (slits == NULL)
00636 fors_subtract_sky_exit("Cannot load slits location table");
00637
00638 polytraces = dfs_load_table(frameset, curv_coeff_tag, 1);
00639 if (polytraces == NULL)
00640 fors_subtract_sky_exit("Cannot load spectral curvature table");
00641
00642 cpl_msg_indent_less();
00643 cpl_msg_info(recipe, "Local sky determination...");
00644 cpl_msg_indent_more();
00645 skymap = mos_subtract_sky(spectra, slits, polytraces, reference,
00646 startwavelength, endwavelength, dispersion);
00647
00648 cpl_table_delete(polytraces); polytraces = NULL;
00649 cpl_table_delete(slits); slits = NULL;
00650
00651 if (cosmics) {
00652 cpl_msg_info(recipe, "Removing cosmic rays...");
00653 mos_clean_cosmics(spectra, gain, -1., -1.);
00654 }
00655
00656 if (dfs_save_image(frameset, spectra, unmapped_tag,
00657 header, parlist, recipe, version))
00658 fors_subtract_sky_exit(NULL);
00659
00660 cpl_image_delete(spectra); spectra = NULL;
00661
00662 if (dfs_save_image(frameset, skymap, unmapped_sky_tag,
00663 header, parlist, recipe, version))
00664 fors_subtract_sky_exit(NULL);
00665
00666 cpl_image_delete(skymap); skymap = NULL;
00667
00668 cpl_propertylist_delete(header); header = NULL;
00669
00670 return 0;
00671 }