FORS Pipeline Reference Manual 4.9.20
|
00001 /* $Id: fors_detect_spectra.c,v 1.7 2013/02/28 15:13:31 cgarcia Exp $ 00002 * 00003 * This file is part of the FORS Data Reduction Pipeline 00004 * Copyright (C) 2002-2010 European Southern Observatory 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00019 */ 00020 00021 /* 00022 * $Author: cgarcia $ 00023 * $Date: 2013/02/28 15:13:31 $ 00024 * $Revision: 1.7 $ 00025 * $Name: fors-4_9_20 $ 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_detect_spectra_create(cpl_plugin *); 00038 static int fors_detect_spectra_exec(cpl_plugin *); 00039 static int fors_detect_spectra_destroy(cpl_plugin *); 00040 static int fors_detect_spectra(cpl_parameterlist *, cpl_frameset *); 00041 00042 static char fors_detect_spectra_description[] = 00043 "This recipe is used to detect and locate MOS/MXU slit spectra on the CCD,\n" 00044 "applying a pattern-matching algorithm. The input spectral exposure must\n" 00045 "contain spectra with the dispersion direction approximately horizontal,\n" 00046 "with blue on the left and red on the right. Use recipe fors_wave_calib_lss\n" 00047 "for LSS data, or for MOS/MXU data where all slits have the same offset.\n" 00048 "\n" 00049 "The rows of the input spectral exposure are processed separately, one\n" 00050 "by one. First, the background continuum is removed. Second, a list of\n" 00051 "positions of reference lines candidates is created. Only peaks above a\n" 00052 "given threshold (specified by the parameter --peakdetection) are selected.\n" 00053 "Third, the pattern-matching task selects from the found peaks the ones\n" 00054 "corresponding to the reference lines, listed in the input line catalog,\n" 00055 "associating them to the appropriate wavelengths. The ensuing polynomial\n" 00056 "fit is used to locate the central wavelength of the applied grism along\n" 00057 "each image row. The contributions from all rows form an image of the\n" 00058 "location of all spectra, that can be used as a starting point for the\n" 00059 "proper modeling of the optical and spectral distortions. For more details\n" 00060 "on this reduction strategy please refer to the FORS Pipeline User's Manual.\n" 00061 "\n" 00062 "Note that specifying an input GRISM_TABLE will set some of the recipe\n" 00063 "configuration parameters to default values valid for a particular grism.\n" 00064 "Again, see the pipeline manual for more details.\n" 00065 "\n" 00066 "In the table below the MXU acronym can be alternatively read as MOS.\n\n" 00067 "Input files:\n\n" 00068 " DO category: Type: Explanation: Required:\n" 00069 " LAMP_UNBIAS_MXU Calib Bias subtracted arc Y\n" 00070 " MASTER_LINECAT Calib Line catalog Y\n" 00071 " GRISM_TABLE Calib Grism table .\n\n" 00072 "Output files:\n\n" 00073 " DO category: Data type: Explanation:\n" 00074 " SLIT_MAP_MXU FITS image Map of central wavelength on CCD\n" 00075 " SLIT_LOCATION_DETECT_MXU FITS table Slits positions on CCD\n" 00076 " SPECTRA_DETECTION_MXU FITS image Check of preliminary detection\n\n"; 00077 00078 #define fors_detect_spectra_exit(message) \ 00079 { \ 00080 if (message) cpl_msg_error(recipe, message); \ 00081 cpl_image_delete(spectra); \ 00082 cpl_image_delete(checkwave); \ 00083 cpl_image_delete(refimage); \ 00084 cpl_mask_delete(refmask); \ 00085 cpl_table_delete(grism_table); \ 00086 cpl_table_delete(wavelengths); \ 00087 cpl_table_delete(maskslits); \ 00088 cpl_table_delete(slits); \ 00089 cpl_vector_delete(lines); \ 00090 cpl_propertylist_delete(header); \ 00091 cpl_msg_indent_less(); \ 00092 return -1; \ 00093 } 00094 00095 #define fors_detect_spectra_exit_memcheck(message) \ 00096 { \ 00097 if (message) cpl_msg_info(recipe, message); \ 00098 printf("free spectra (%p)\n", spectra); \ 00099 cpl_image_delete(spectra); \ 00100 printf("free checkwave (%p)\n", checkwave); \ 00101 cpl_image_delete(checkwave); \ 00102 printf("free refimage (%p)\n", refimage); \ 00103 cpl_image_delete(refimage); \ 00104 printf("free refmask (%p)\n", refmask); \ 00105 cpl_mask_delete(refmask); \ 00106 printf("free grism_table (%p)\n", grism_table); \ 00107 cpl_table_delete(grism_table); \ 00108 printf("free wavelengths (%p)\n", wavelengths); \ 00109 cpl_table_delete(wavelengths); \ 00110 printf("free maskslits (%p)\n", maskslits); \ 00111 cpl_table_delete(maskslits); \ 00112 printf("free slits (%p)\n", slits); \ 00113 cpl_table_delete(slits); \ 00114 printf("free lines (%p)\n", lines); \ 00115 cpl_vector_delete(lines); \ 00116 printf("free header (%p)\n", header); \ 00117 cpl_propertylist_delete(header); \ 00118 cpl_msg_indent_less(); \ 00119 return 0; \ 00120 } 00121 00122 00134 int cpl_plugin_get_info(cpl_pluginlist *list) 00135 { 00136 cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe ); 00137 cpl_plugin *plugin = &recipe->interface; 00138 00139 cpl_plugin_init(plugin, 00140 CPL_PLUGIN_API, 00141 FORS_BINARY_VERSION, 00142 CPL_PLUGIN_TYPE_RECIPE, 00143 "fors_detect_spectra", 00144 "Detect MOS/MXU spectra on CCD", 00145 fors_detect_spectra_description, 00146 "Carlo Izzo", 00147 PACKAGE_BUGREPORT, 00148 "This file is currently part of the FORS Instrument Pipeline\n" 00149 "Copyright (C) 2002-2010 European Southern Observatory\n\n" 00150 "This program is free software; you can redistribute it and/or modify\n" 00151 "it under the terms of the GNU General Public License as published by\n" 00152 "the Free Software Foundation; either version 2 of the License, or\n" 00153 "(at your option) any later version.\n\n" 00154 "This program is distributed in the hope that it will be useful,\n" 00155 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" 00156 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" 00157 "GNU General Public License for more details.\n\n" 00158 "You should have received a copy of the GNU General Public License\n" 00159 "along with this program; if not, write to the Free Software Foundation,\n" 00160 "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n", 00161 fors_detect_spectra_create, 00162 fors_detect_spectra_exec, 00163 fors_detect_spectra_destroy); 00164 00165 cpl_pluginlist_append(list, plugin); 00166 00167 return 0; 00168 } 00169 00170 00181 static int fors_detect_spectra_create(cpl_plugin *plugin) 00182 { 00183 cpl_recipe *recipe; 00184 cpl_parameter *p; 00185 00186 /* 00187 * Check that the plugin is part of a valid recipe 00188 */ 00189 00190 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 00191 recipe = (cpl_recipe *)plugin; 00192 else 00193 return -1; 00194 00195 /* 00196 * Create the (empty) parameters list in the cpl_recipe object 00197 */ 00198 00199 recipe->parameters = cpl_parameterlist_new(); 00200 00201 /* 00202 * Dispersion 00203 */ 00204 00205 p = cpl_parameter_new_value("fors.fors_detect_spectra.dispersion", 00206 CPL_TYPE_DOUBLE, 00207 "Expected spectral dispersion (Angstrom/pixel)", 00208 "fors.fors_detect_spectra", 00209 0.0); 00210 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion"); 00211 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00212 cpl_parameterlist_append(recipe->parameters, p); 00213 00214 /* 00215 * Peak detection level 00216 */ 00217 00218 p = cpl_parameter_new_value("fors.fors_detect_spectra.peakdetection", 00219 CPL_TYPE_DOUBLE, 00220 "Initial peak detection threshold (ADU)", 00221 "fors.fors_detect_spectra", 00222 0.0); 00223 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "peakdetection"); 00224 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00225 cpl_parameterlist_append(recipe->parameters, p); 00226 00227 /* 00228 * Degree of wavelength calibration polynomial 00229 */ 00230 00231 p = cpl_parameter_new_value("fors.fors_detect_spectra.wdegree", 00232 CPL_TYPE_INT, 00233 "Degree of wavelength calibration polynomial", 00234 "fors.fors_detect_spectra", 00235 0); 00236 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wdegree"); 00237 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00238 cpl_parameterlist_append(recipe->parameters, p); 00239 00240 /* 00241 * Reference lines search radius 00242 */ 00243 00244 p = cpl_parameter_new_value("fors.fors_detect_spectra.wradius", 00245 CPL_TYPE_INT, 00246 "Search radius if iterating pattern-matching " 00247 "with first-guess method", 00248 "fors.fors_detect_spectra", 00249 4); 00250 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wradius"); 00251 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00252 cpl_parameterlist_append(recipe->parameters, p); 00253 00254 /* 00255 * Rejection threshold in dispersion relation polynomial fitting 00256 */ 00257 00258 p = cpl_parameter_new_value("fors.fors_detect_spectra.wreject", 00259 CPL_TYPE_DOUBLE, 00260 "Rejection threshold in dispersion " 00261 "relation fit (pixel)", 00262 "fors.fors_detect_spectra", 00263 0.7); 00264 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wreject"); 00265 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00266 cpl_parameterlist_append(recipe->parameters, p); 00267 00268 /* 00269 * Line catalog table column containing the reference wavelengths 00270 */ 00271 00272 p = cpl_parameter_new_value("fors.fors_detect_spectra.wcolumn", 00273 CPL_TYPE_STRING, 00274 "Name of line catalog table column " 00275 "with wavelengths", 00276 "fors.fors_detect_spectra", 00277 "WLEN"); 00278 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn"); 00279 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00280 cpl_parameterlist_append(recipe->parameters, p); 00281 00282 /* 00283 * Start wavelength for spectral extraction 00284 */ 00285 00286 p = cpl_parameter_new_value("fors.fors_detect_spectra.startwavelength", 00287 CPL_TYPE_DOUBLE, 00288 "Start wavelength in spectral extraction", 00289 "fors.fors_detect_spectra", 00290 0.0); 00291 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength"); 00292 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00293 cpl_parameterlist_append(recipe->parameters, p); 00294 00295 /* 00296 * End wavelength for spectral extraction 00297 */ 00298 00299 p = cpl_parameter_new_value("fors.fors_detect_spectra.endwavelength", 00300 CPL_TYPE_DOUBLE, 00301 "End wavelength in spectral extraction", 00302 "fors.fors_detect_spectra", 00303 0.0); 00304 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength"); 00305 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00306 cpl_parameterlist_append(recipe->parameters, p); 00307 00308 /* 00309 * Try slit identification 00310 */ 00311 00312 p = cpl_parameter_new_value("fors.fors_detect_spectra.slit_ident", 00313 CPL_TYPE_BOOL, 00314 "Attempt slit identification for MOS or MXU", 00315 "fors.fors_detect_spectra", 00316 TRUE); 00317 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "slit_ident"); 00318 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00319 cpl_parameterlist_append(recipe->parameters, p); 00320 00321 00322 return 0; 00323 } 00324 00325 00334 static int fors_detect_spectra_exec(cpl_plugin *plugin) 00335 { 00336 cpl_recipe *recipe; 00337 00338 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 00339 recipe = (cpl_recipe *)plugin; 00340 else 00341 return -1; 00342 00343 return fors_detect_spectra(recipe->parameters, recipe->frames); 00344 } 00345 00346 00355 static int fors_detect_spectra_destroy(cpl_plugin *plugin) 00356 { 00357 cpl_recipe *recipe; 00358 00359 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 00360 recipe = (cpl_recipe *)plugin; 00361 else 00362 return -1; 00363 00364 cpl_parameterlist_delete(recipe->parameters); 00365 00366 return 0; 00367 } 00368 00369 00379 static int fors_detect_spectra(cpl_parameterlist *parlist, 00380 cpl_frameset *frameset) 00381 { 00382 00383 const char *recipe = "fors_detect_spectra"; 00384 00385 00386 /* 00387 * Input parameters 00388 */ 00389 00390 double dispersion; 00391 double peakdetection; 00392 int wdegree; 00393 int wradius; 00394 double wreject; 00395 const char *wcolumn; 00396 double startwavelength; 00397 double endwavelength; 00398 int slit_ident; 00399 00400 /* 00401 * CPL objects 00402 */ 00403 00404 cpl_image *spectra = NULL; 00405 cpl_image *checkwave = NULL; 00406 cpl_image *refimage = NULL; 00407 cpl_mask *refmask = NULL; 00408 cpl_table *grism_table = NULL; 00409 cpl_table *wavelengths = NULL; 00410 cpl_table *slits = NULL; 00411 cpl_table *positions = NULL; 00412 cpl_table *maskslits = NULL; 00413 cpl_vector *lines = NULL; 00414 cpl_propertylist *header = NULL; 00415 00416 /* 00417 * Auxiliary variables 00418 */ 00419 00420 char version[80]; 00421 const char *arc_tag; 00422 const char *spectra_detection_tag; 00423 const char *slit_map_tag; 00424 const char *slit_location_tag; 00425 int lamp_mxu; 00426 int lamp_mos; 00427 int lamp_lss; 00428 int mxu, mos; 00429 int treat_as_lss = 0; 00430 int nslits; 00431 double *xpos; 00432 double mxpos; 00433 int narc; 00434 int nlines; 00435 int rebin; 00436 double *line; 00437 int nx, ny; 00438 double reference; 00439 int i; 00440 int nslits_out_det = 0; 00441 00442 char *instrume = NULL; 00443 00444 00445 cpl_msg_set_indentation(2); 00446 00447 if (dfs_files_dont_exist(frameset)) 00448 fors_detect_spectra_exit(NULL); 00449 00450 /* 00451 * Get configuration parameters 00452 */ 00453 00454 cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe); 00455 cpl_msg_indent_more(); 00456 00457 if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1) 00458 fors_detect_spectra_exit("Too many in input: GRISM_TABLE"); 00459 00460 grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1); 00461 00462 dispersion = dfs_get_parameter_double(parlist, 00463 "fors.fors_detect_spectra.dispersion", grism_table); 00464 00465 if (dispersion <= 0.0) 00466 fors_detect_spectra_exit("Invalid spectral dispersion value"); 00467 00468 peakdetection = dfs_get_parameter_double(parlist, 00469 "fors.fors_detect_spectra.peakdetection", grism_table); 00470 if (peakdetection <= 0.0) 00471 fors_detect_spectra_exit("Invalid peak detection level"); 00472 00473 wdegree = dfs_get_parameter_int(parlist, 00474 "fors.fors_detect_spectra.wdegree", grism_table); 00475 00476 if (wdegree < 1) 00477 fors_detect_spectra_exit("Invalid polynomial degree"); 00478 00479 if (wdegree > 5) 00480 fors_detect_spectra_exit("Max allowed polynomial degree is 5"); 00481 00482 wradius = dfs_get_parameter_int(parlist, 00483 "fors.fors_detect_spectra.wradius", NULL); 00484 00485 if (wradius < 0) 00486 fors_detect_spectra_exit("Invalid search radius"); 00487 00488 wreject = dfs_get_parameter_double(parlist, 00489 "fors.fors_detect_spectra.wreject", NULL); 00490 00491 if (wreject <= 0.0) 00492 fors_detect_spectra_exit("Invalid rejection threshold"); 00493 00494 wcolumn = dfs_get_parameter_string(parlist, 00495 "fors.fors_detect_spectra.wcolumn", NULL); 00496 00497 startwavelength = dfs_get_parameter_double(parlist, 00498 "fors.fors_detect_spectra.startwavelength", grism_table); 00499 if (startwavelength > 1.0) 00500 if (startwavelength < 3000.0 || startwavelength > 13000.0) 00501 fors_detect_spectra_exit("Invalid wavelength"); 00502 00503 endwavelength = dfs_get_parameter_double(parlist, 00504 "fors.fors_detect_spectra.endwavelength", grism_table); 00505 if (endwavelength > 1.0) { 00506 if (endwavelength < 3000.0 || endwavelength > 13000.0) 00507 fors_detect_spectra_exit("Invalid wavelength"); 00508 if (startwavelength < 1.0) 00509 fors_detect_spectra_exit("Invalid wavelength interval"); 00510 } 00511 00512 if (startwavelength > 1.0) 00513 if (endwavelength - startwavelength <= 0.0) 00514 fors_detect_spectra_exit("Invalid wavelength interval"); 00515 00516 slit_ident = dfs_get_parameter_bool(parlist, 00517 "fors.fors_detect_spectra.slit_ident", NULL); 00518 00519 cpl_table_delete(grism_table); grism_table = NULL; 00520 00521 if (cpl_error_get_code()) 00522 fors_detect_spectra_exit("Failure reading the " 00523 "configuration parameters"); 00524 00525 00526 cpl_msg_indent_less(); 00527 cpl_msg_info(recipe, "Check input set-of-frames:"); 00528 cpl_msg_indent_more(); 00529 00530 narc = lamp_mxu = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MXU"); 00531 narc += lamp_mos = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MOS"); 00532 narc += lamp_lss = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_LSS"); 00533 00534 if (narc == 0) { 00535 fors_detect_spectra_exit("Missing input arc lamp frame"); 00536 } 00537 if (narc > 1) { 00538 cpl_msg_error(recipe, "Too many input arc lamp frames (%d > 1)", narc); 00539 fors_detect_spectra_exit(NULL); 00540 } 00541 00542 mxu = mos = 0; 00543 00544 if (lamp_mxu) { 00545 mxu = 1; 00546 arc_tag = "LAMP_UNBIAS_MXU"; 00547 spectra_detection_tag = "SPECTRA_DETECTION_MXU"; 00548 slit_map_tag = "SLIT_MAP_MXU"; 00549 slit_location_tag = "SLIT_LOCATION_DETECT_MXU"; 00550 } 00551 else if (lamp_mos) { 00552 mos = 1; 00553 arc_tag = "LAMP_UNBIAS_MOS"; 00554 spectra_detection_tag = "SPECTRA_DETECTION_MOS"; 00555 slit_map_tag = "SLIT_MAP_MOS"; 00556 slit_location_tag = "SLIT_LOCATION_DETECT_MOS"; 00557 } 00558 else if (lamp_lss) { 00559 fors_detect_spectra_exit("Use recipe fors_wave_calib_lss " 00560 "for LSS data reduction"); 00561 } 00562 00563 00564 if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0) 00565 fors_detect_spectra_exit("Missing required input: MASTER_LINECAT"); 00566 00567 if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1) 00568 fors_detect_spectra_exit("Too many in input: MASTER_LINECAT"); 00569 00570 if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID")) 00571 fors_detect_spectra_exit("Input frames are not from the same grism"); 00572 00573 if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID")) 00574 fors_detect_spectra_exit("Input frames are not from the same filter"); 00575 00576 if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID")) 00577 fors_detect_spectra_exit("Input frames are not from the same chip"); 00578 00579 00580 /* 00581 * Get the reference wavelength and the rebin factor along the 00582 * dispersion direction from the arc lamp exposure 00583 */ 00584 00585 header = dfs_load_header(frameset, arc_tag, 0); 00586 00587 if (header == NULL) 00588 fors_detect_spectra_exit("Cannot load arc lamp header"); 00589 00590 instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME"); 00591 if (instrume == NULL) 00592 fors_detect_spectra_exit("Missing keyword INSTRUME in arc lamp header"); 00593 00594 if (instrume[4] == '1') 00595 snprintf(version, 80, "%s/%s", "fors1", VERSION); 00596 if (instrume[4] == '2') 00597 snprintf(version, 80, "%s/%s", "fors2", VERSION); 00598 00599 reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN"); 00600 00601 if (cpl_error_get_code() != CPL_ERROR_NONE) 00602 fors_detect_spectra_exit("Missing keyword ESO INS GRIS1 WLEN " 00603 "in arc lamp frame header"); 00604 00605 if (reference < 3000.0) /* Perhaps in nanometers... */ 00606 reference *= 10; 00607 00608 if (reference < 3000.0 || reference > 13000.0) { 00609 cpl_msg_error(recipe, "Invalid central wavelength %.2f read from " 00610 "keyword ESO INS GRIS1 WLEN in arc lamp frame header", 00611 reference); 00612 fors_detect_spectra_exit(NULL); 00613 } 00614 00615 cpl_msg_info(recipe, "The central wavelength is: %.2f", reference); 00616 00617 rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX"); 00618 00619 if (cpl_error_get_code() != CPL_ERROR_NONE) 00620 fors_detect_spectra_exit("Missing keyword ESO DET WIN1 BINX " 00621 "in arc lamp frame header"); 00622 00623 if (rebin != 1) { 00624 dispersion *= rebin; 00625 cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the " 00626 "working dispersion used is %f A/pixel", rebin, 00627 dispersion); 00628 } 00629 00630 00631 cpl_msg_info(recipe, "Produce mask slit position table..."); 00632 00633 if (mos) 00634 maskslits = mos_load_slits_fors_mos(header, &nslits_out_det); 00635 else 00636 maskslits = mos_load_slits_fors_mxu(header); 00637 00638 cpl_propertylist_delete(header); header = NULL; 00639 00640 00641 /* 00642 * Check if all slits have the same X offset: in such case, 00643 * treat the observation as a long-slit one! 00644 */ 00645 00646 treat_as_lss = fors_mos_is_lss_like(maskslits, nslits_out_det); 00647 00648 if (treat_as_lss) { 00649 cpl_msg_error(recipe, "All slits have the same offset: %.2f mm\n" 00650 "The LSS data reduction strategy must be applied: " 00651 "please use recipe fors_wave_calib_lss", mxpos); 00652 fors_detect_spectra_exit(NULL); 00653 } 00654 00655 if (slit_ident == 0) { 00656 cpl_table_delete(maskslits); maskslits = NULL; 00657 } 00658 00659 00660 cpl_msg_indent_less(); 00661 cpl_msg_info(recipe, "Load arc lamp exposure..."); 00662 cpl_msg_indent_more(); 00663 00664 spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0); 00665 00666 if (spectra == NULL) 00667 fors_detect_spectra_exit("Cannot load arc lamp exposure"); 00668 00669 00670 cpl_msg_indent_less(); 00671 cpl_msg_info(recipe, "Load input line catalog..."); 00672 cpl_msg_indent_more(); 00673 00674 wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1); 00675 00676 if (wavelengths == NULL) 00677 fors_detect_spectra_exit("Cannot load line catalog"); 00678 00679 00680 /* 00681 * Cast the wavelengths into a (double precision) CPL vector 00682 */ 00683 00684 nlines = cpl_table_get_nrow(wavelengths); 00685 00686 if (nlines == 0) 00687 fors_detect_spectra_exit("Empty input line catalog"); 00688 00689 if (cpl_table_has_column(wavelengths, wcolumn) != 1) { 00690 cpl_msg_error(recipe, "Missing column %s in input line catalog table", 00691 wcolumn); 00692 fors_detect_spectra_exit(NULL); 00693 } 00694 00695 line = cpl_malloc(nlines * sizeof(double)); 00696 00697 for (i = 0; i < nlines; i++) 00698 line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL); 00699 00700 cpl_table_delete(wavelengths); wavelengths = NULL; 00701 00702 lines = cpl_vector_wrap(nlines, line); 00703 00704 00705 cpl_msg_indent_less(); 00706 cpl_msg_info(recipe, "Detecting spectra on CCD..."); 00707 cpl_msg_indent_more(); 00708 00709 nx = cpl_image_get_size_x(spectra); 00710 ny = cpl_image_get_size_y(spectra); 00711 00712 refmask = cpl_mask_new(nx, ny); 00713 00714 if (mos_saturation_process(spectra)) 00715 fors_detect_spectra_exit("Cannot process saturation"); 00716 00717 if (mos_subtract_background(spectra)) 00718 fors_detect_spectra_exit("Cannot subtract the background"); 00719 00720 checkwave = mos_wavelength_calibration_raw(spectra, lines, dispersion, 00721 peakdetection, wradius, 00722 wdegree, wreject, reference, 00723 &startwavelength, &endwavelength, 00724 NULL, NULL, NULL, NULL, NULL, 00725 NULL, refmask); 00726 00727 cpl_image_delete(spectra); spectra = NULL; 00728 cpl_vector_delete(lines); lines = NULL; 00729 00730 if (checkwave == NULL) 00731 fors_detect_spectra_exit("Wavelength calibration failure."); 00732 00733 00734 /* 00735 * Save check image to disk 00736 */ 00737 00738 header = cpl_propertylist_new(); 00739 cpl_propertylist_update_double(header, "CRPIX1", 1.0); 00740 cpl_propertylist_update_double(header, "CRPIX2", 1.0); 00741 cpl_propertylist_update_double(header, "CRVAL1", 00742 startwavelength + dispersion/2); 00743 cpl_propertylist_update_double(header, "CRVAL2", 1.0); 00744 /* cpl_propertylist_update_double(header, "CDELT1", dispersion); 00745 cpl_propertylist_update_double(header, "CDELT2", 1.0); */ 00746 cpl_propertylist_update_double(header, "CD1_1", dispersion); 00747 cpl_propertylist_update_double(header, "CD1_2", 0.0); 00748 cpl_propertylist_update_double(header, "CD2_1", 0.0); 00749 cpl_propertylist_update_double(header, "CD2_2", 1.0); 00750 cpl_propertylist_update_string(header, "CTYPE1", "LINEAR"); 00751 cpl_propertylist_update_string(header, "CTYPE2", "PIXEL"); 00752 00753 if (dfs_save_image(frameset, checkwave, spectra_detection_tag, header, 00754 parlist, recipe, version)) 00755 fors_detect_spectra_exit(NULL); 00756 00757 cpl_image_delete(checkwave); checkwave = NULL; 00758 cpl_propertylist_delete(header); header = NULL; 00759 00760 00761 cpl_msg_info(recipe, "Locate slits at reference wavelength on CCD..."); 00762 slits = mos_locate_spectra(refmask); 00763 00764 if (!slits) { 00765 cpl_msg_error(cpl_error_get_where(), cpl_error_get_message()); 00766 fors_detect_spectra_exit("No slits could be detected!"); 00767 } 00768 00769 refimage = cpl_image_new_from_mask(refmask); 00770 cpl_mask_delete(refmask); refmask = NULL; 00771 00772 header = dfs_load_header(frameset, arc_tag, 0); 00773 if (dfs_save_image(frameset, refimage, slit_map_tag, header, 00774 parlist, recipe, version)) 00775 fors_detect_spectra_exit(NULL); 00776 cpl_propertylist_delete(header); header = NULL; 00777 00778 cpl_image_delete(refimage); refimage = NULL; 00779 00780 if (slit_ident) { 00781 00782 /* 00783 * Attempt slit identification: this recipe may continue even 00784 * in case of failed identification (i.e., the position table is 00785 * not produced, but an error is not set). In case of failure, 00786 * the spectra would be still extracted, even if they would not 00787 * be associated to slits on the mask. 00788 * 00789 * The reason for making the slit identification an user option 00790 * (via the parameter slit_ident) is to offer the possibility 00791 * to avoid identifications that are only apparently successful, 00792 * as it would happen in the case of an incorrect slit description 00793 * in the data header. 00794 */ 00795 00796 cpl_msg_indent_less(); 00797 cpl_msg_info(recipe, "Attempt slit identification (optional)..."); 00798 cpl_msg_indent_more(); 00799 00800 positions = mos_identify_slits(slits, maskslits, NULL); 00801 cpl_table_delete(maskslits); maskslits = NULL; 00802 00803 if (positions) { 00804 cpl_table_delete(slits); 00805 slits = positions; 00806 00807 /* 00808 * Eliminate slits which are _entirely_ outside the CCD 00809 */ 00810 00811 cpl_table_and_selected_double(slits, 00812 "ybottom", CPL_GREATER_THAN, ny-1); 00813 cpl_table_or_selected_double(slits, 00814 "ytop", CPL_LESS_THAN, 0); 00815 cpl_table_erase_selected(slits); 00816 00817 nslits = cpl_table_get_nrow(slits); 00818 00819 if (nslits == 0) 00820 fors_detect_spectra_exit("No slits found on the CCD"); 00821 00822 cpl_msg_info(recipe, "%d slits are entirely or partially " 00823 "contained in CCD", nslits); 00824 00825 } 00826 else { 00827 slit_ident = 0; 00828 cpl_msg_info(recipe, "Global distortion model cannot be computed"); 00829 if (cpl_error_get_code() != CPL_ERROR_NONE) { 00830 fors_detect_spectra_exit(NULL); 00831 } 00832 } 00833 } 00834 00835 if (dfs_save_table(frameset, slits, slit_location_tag, NULL, 00836 parlist, recipe, version)) 00837 fors_detect_spectra_exit(NULL); 00838 00839 cpl_table_delete(slits); slits = NULL; 00840 00841 return 0; 00842 }