00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027
00028
00029
00030
00031 #include <string.h>
00032 #include <strings.h>
00033 #include <cpl.h>
00034
00035 #include "muse_standard_z.h"
00036
00037
00065
00068
00069
00070
00071 static const char *muse_standard_help =
00072 "Merge pixel tables from all IFUs and correct for differential atmospheric refraction. To derive the flux response curve, integrate the flux of all objects detected within the field of view using the given profile. Select one object as the standard star (either the brightest or the one nearest one, depending on --select) and compare its measured fluxes to tabulated fluxes to derive the sensitivity over wavelength. Postprocess this sensitivity curve to mark wavelength ranges affected by telluric absorption. Interpolate over the telluric regions and derive a telluric correction spectrum for them. The final response cuve is then linearly extrapolated to the largest possible MUSE wavelength range and smoothed (with the method given by --smooth). The derivation of the telluric correction spectrum assumes that the star has a smooth spectrum within the telluric regions. If there are more than one exposure given in the input data, the derivation of the flux response and telluric corrections are done separately for each exposure. For each exposure, the datacube used for flux integration is saved, together with collapsed images for each given filter.";
00073
00074 static const char *muse_standard_help_esorex =
00075 "\n\nInput frames for raw frame tag \"PIXTABLE_STD\":\n"
00076 "\n Frame tag Type Req #Fr Description"
00077 "\n -------------------- ---- --- --- ------------"
00078 "\n PIXTABLE_STD raw Y Pixel table of a standard star field"
00079 "\n EXTINCT_TABLE calib Y 1 Atmospheric extinction table"
00080 "\n STD_FLUX_TABLE calib Y Flux reference table for standard stars"
00081 "\n TELLURIC_REGIONS calib . 1 Definition of telluric regions"
00082 "\n FILTER_LIST calib . 1 File to be used to create field-of-view images."
00083 "\n\nProduct frames for raw frame tag \"PIXTABLE_STD\":\n"
00084 "\n Frame tag Level Description"
00085 "\n -------------------- -------- ------------"
00086 "\n DATACUBE_STD final Reduced standard star field exposure"
00087 "\n STD_FLUXES final The integrated flux per wavelength of all detected sources"
00088 "\n STD_RESPONSE final Response curve as derived from standard star(s)"
00089 "\n STD_TELLURIC final Telluric absorption as derived from standard star(s)";
00090
00091
00099
00100 static cpl_recipeconfig *
00101 muse_standard_new_recipeconfig(void)
00102 {
00103 cpl_recipeconfig *recipeconfig = cpl_recipeconfig_new();
00104
00105 cpl_recipeconfig_set_tag(recipeconfig, "PIXTABLE_STD", 1, -1);
00106 cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_STD", "EXTINCT_TABLE", 1, 1);
00107 cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_STD", "STD_FLUX_TABLE", 1, -1);
00108 cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_STD", "TELLURIC_REGIONS", -1, 1);
00109 cpl_recipeconfig_set_input(recipeconfig, "PIXTABLE_STD", "FILTER_LIST", -1, 1);
00110 cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_STD", "DATACUBE_STD");
00111 cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_STD", "STD_FLUXES");
00112 cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_STD", "STD_RESPONSE");
00113 cpl_recipeconfig_set_output(recipeconfig, "PIXTABLE_STD", "STD_TELLURIC");
00114
00115 return recipeconfig;
00116 }
00117
00118
00128
00129 static cpl_error_code
00130 muse_standard_prepare_header(const char *aFrametag, cpl_propertylist *aHeader)
00131 {
00132 cpl_ensure_code(aFrametag, CPL_ERROR_NULL_INPUT);
00133 cpl_ensure_code(aHeader, CPL_ERROR_NULL_INPUT);
00134 if (!strcmp(aFrametag, "DATACUBE_STD")) {
00135 muse_processing_prepare_property(aHeader, "ESO QC STANDARD NDET",
00136 CPL_TYPE_INT,
00137 "Number of detected sources in output cube.");
00138 muse_processing_prepare_property(aHeader, "ESO QC STANDARD LAMBDA",
00139 CPL_TYPE_FLOAT,
00140 "[Angstrom] Wavelength of plane in combined cube that was used for object detection.");
00141 muse_processing_prepare_property(aHeader, "ESO QC STANDARD POS[0-9]+ X",
00142 CPL_TYPE_FLOAT,
00143 "[pix] Position of source k in x-direction in output cube. If the FWHM measurement fails, this value will be -1.");
00144 muse_processing_prepare_property(aHeader, "ESO QC STANDARD POS[0-9]+ Y",
00145 CPL_TYPE_FLOAT,
00146 "[pix] Position of source k in y-direction in output cube. If the FWHM measurement fails, this value will be -1.");
00147 muse_processing_prepare_property(aHeader, "ESO QC STANDARD FWHM[0-9]+ X",
00148 CPL_TYPE_FLOAT,
00149 "[arcsec] FWHM of source k in x-direction in output cube. If the FWHM measurement fails, this value will be -1.");
00150 muse_processing_prepare_property(aHeader, "ESO QC STANDARD FWHM[0-9]+ Y",
00151 CPL_TYPE_FLOAT,
00152 "[arcsec] FWHM of source k in y-direction in output cube. If the FWHM measurement fails, this value will be -1.");
00153 muse_processing_prepare_property(aHeader, "ESO QC STANDARD FWHM NVALID",
00154 CPL_TYPE_INT,
00155 "Number of detected sources with valid FWHM in output cube.");
00156 muse_processing_prepare_property(aHeader, "ESO QC STANDARD FWHM MEDIAN",
00157 CPL_TYPE_FLOAT,
00158 "[arcsec] Median FWHM of all sources with valid FWHM measurement (in x- and y-direction) in output cube. If less than three sources with valid FWHM are detected, this value is zero.");
00159 muse_processing_prepare_property(aHeader, "ESO QC STANDARD FWHM MAD",
00160 CPL_TYPE_FLOAT,
00161 "[arcsec] Median absolute deviation of the FWHM of all sources with valid FWHM measurement (in x- and y-direction) in output cube. If less than three sources with valid FWHM are detected, this value is zero.");
00162 } else if (!strcmp(aFrametag, "STD_FLUXES")) {
00163 } else if (!strcmp(aFrametag, "STD_RESPONSE")) {
00164 } else if (!strcmp(aFrametag, "STD_TELLURIC")) {
00165 } else {
00166 cpl_msg_warning(__func__, "Frame tag %s is not defined", aFrametag);
00167 return CPL_ERROR_ILLEGAL_INPUT;
00168 }
00169 return CPL_ERROR_NONE;
00170 }
00171
00172
00181
00182 static cpl_frame_level
00183 muse_standard_get_frame_level(const char *aFrametag)
00184 {
00185 if (!aFrametag) {
00186 return CPL_FRAME_LEVEL_NONE;
00187 }
00188 if (!strcmp(aFrametag, "DATACUBE_STD")) {
00189 return CPL_FRAME_LEVEL_FINAL;
00190 }
00191 if (!strcmp(aFrametag, "STD_FLUXES")) {
00192 return CPL_FRAME_LEVEL_FINAL;
00193 }
00194 if (!strcmp(aFrametag, "STD_RESPONSE")) {
00195 return CPL_FRAME_LEVEL_FINAL;
00196 }
00197 if (!strcmp(aFrametag, "STD_TELLURIC")) {
00198 return CPL_FRAME_LEVEL_FINAL;
00199 }
00200 return CPL_FRAME_LEVEL_NONE;
00201 }
00202
00203
00212
00213 static muse_frame_mode
00214 muse_standard_get_frame_mode(const char *aFrametag)
00215 {
00216 if (!aFrametag) {
00217 return MUSE_FRAME_MODE_ALL;
00218 }
00219 if (!strcmp(aFrametag, "DATACUBE_STD")) {
00220 return MUSE_FRAME_MODE_DATEOBS;
00221 }
00222 if (!strcmp(aFrametag, "STD_FLUXES")) {
00223 return MUSE_FRAME_MODE_DATEOBS;
00224 }
00225 if (!strcmp(aFrametag, "STD_RESPONSE")) {
00226 return MUSE_FRAME_MODE_DATEOBS;
00227 }
00228 if (!strcmp(aFrametag, "STD_TELLURIC")) {
00229 return MUSE_FRAME_MODE_DATEOBS;
00230 }
00231 return MUSE_FRAME_MODE_ALL;
00232 }
00233
00234
00244
00245 static int
00246 muse_standard_create(cpl_plugin *aPlugin)
00247 {
00248
00249 cpl_recipe *recipe;
00250 if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
00251 recipe = (cpl_recipe *)aPlugin;
00252 } else {
00253 return -1;
00254 }
00255
00256
00257
00258 muse_processinginfo_register(recipe,
00259 muse_standard_new_recipeconfig(),
00260 muse_standard_prepare_header,
00261 muse_standard_get_frame_level,
00262 muse_standard_get_frame_mode);
00263
00264
00265
00266 if (muse_cplframework() == MUSE_CPLFRAMEWORK_ESOREX) {
00267 cpl_msg_set_time_on();
00268 }
00269
00270
00271 recipe->parameters = cpl_parameterlist_new();
00272
00273 cpl_parameter *p;
00274
00275
00276 p = cpl_parameter_new_enum("muse.muse_standard.profile",
00277 CPL_TYPE_STRING,
00278 "Type of flux integration to use. \"gaussian\" and \"moffat\" use 2D profile fitting, circle and square are non-optimal flux integrators.",
00279 "muse.muse_standard",
00280 (const char *)"moffat",
00281 4,
00282 (const char *)"gaussian",
00283 (const char *)"moffat",
00284 (const char *)"circle",
00285 (const char *)"square");
00286 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "profile");
00287 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "profile");
00288
00289 cpl_parameterlist_append(recipe->parameters, p);
00290
00291
00292 p = cpl_parameter_new_enum("muse.muse_standard.select",
00293 CPL_TYPE_STRING,
00294 "How to select the star for flux integration, \"flux\" uses the brightest star in the field, \"distance\" uses the detection nearest to the approximate coordinates of the reference source.",
00295 "muse.muse_standard",
00296 (const char *)"distance",
00297 2,
00298 (const char *)"flux",
00299 (const char *)"distance");
00300 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "select");
00301 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "select");
00302
00303 cpl_parameterlist_append(recipe->parameters, p);
00304
00305
00306 p = cpl_parameter_new_enum("muse.muse_standard.smooth",
00307 CPL_TYPE_STRING,
00308 "How to smooth the response curve before writing it to disk. \"none\" does not do any kind of smoothing (such a response curve is only useful, if smoothed externally; \"median\" does a median-filter of 15 Angstrom half-width; \"ppoly\" fits piecewise cubic polynomials (each one across 2x150 Angstrom width) postprocessed by a sliding average filter of 15 Angstrom half-width.",
00309 "muse.muse_standard",
00310 (const char *)"ppoly",
00311 3,
00312 (const char *)"none",
00313 (const char *)"median",
00314 (const char *)"ppoly");
00315 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "smooth");
00316 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "smooth");
00317
00318 cpl_parameterlist_append(recipe->parameters, p);
00319
00320
00321 p = cpl_parameter_new_value("muse.muse_standard.lambdamin",
00322 CPL_TYPE_DOUBLE,
00323 "Cut off the data below this wavelength after loading the pixel table(s).",
00324 "muse.muse_standard",
00325 (double)4000.);
00326 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lambdamin");
00327 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lambdamin");
00328
00329 cpl_parameterlist_append(recipe->parameters, p);
00330
00331
00332 p = cpl_parameter_new_value("muse.muse_standard.lambdamax",
00333 CPL_TYPE_DOUBLE,
00334 "Cut off the data above this wavelength after loading the pixel table(s).",
00335 "muse.muse_standard",
00336 (double)10000.);
00337 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lambdamax");
00338 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lambdamax");
00339
00340 cpl_parameterlist_append(recipe->parameters, p);
00341
00342
00343 p = cpl_parameter_new_value("muse.muse_standard.lambdaref",
00344 CPL_TYPE_DOUBLE,
00345 "Reference wavelength used for correction of differential atmospheric refraction. The R-band (peak wavelength ~7000 Angstrom) that is usually used for guiding, is close to the central wavelength of MUSE, so a value of 7000.0 Angstrom should be used if nothing else is known. A value less than zero switches DAR correction off.",
00346 "muse.muse_standard",
00347 (double)7000.);
00348 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "lambdaref");
00349 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lambdaref");
00350
00351 cpl_parameterlist_append(recipe->parameters, p);
00352
00353
00354 p = cpl_parameter_new_enum("muse.muse_standard.darcheck",
00355 CPL_TYPE_STRING,
00356 "Carry out a check of the theoretical DAR correction using source centroiding. If \"correct\" it will also apply an empirical correction.",
00357 "muse.muse_standard",
00358 (const char *)"none",
00359 3,
00360 (const char *)"none",
00361 (const char *)"check",
00362 (const char *)"correct");
00363 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "darcheck");
00364 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "darcheck");
00365
00366 cpl_parameterlist_append(recipe->parameters, p);
00367
00368
00369 p = cpl_parameter_new_value("muse.muse_standard.filter",
00370 CPL_TYPE_STRING,
00371 "The filter name(s) to be used for the output field-of-view image. Each name has to correspond to an EXTNAME in an extension of the FILTER_LIST file. If an unsupported filter name is given, creation of the respective image is omitted. If multiple filter names are given, they have to be comma separated.",
00372 "muse.muse_standard",
00373 (const char *)"white");
00374 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG, "filter");
00375 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "filter");
00376
00377 cpl_parameterlist_append(recipe->parameters, p);
00378
00379 return 0;
00380 }
00381
00382
00393
00394 static int
00395 muse_standard_params_fill(muse_standard_params_t *aParams, cpl_parameterlist *aParameters)
00396 {
00397 cpl_ensure_code(aParams, CPL_ERROR_NULL_INPUT);
00398 cpl_ensure_code(aParameters, CPL_ERROR_NULL_INPUT);
00399 cpl_parameter *p;
00400
00401 p = cpl_parameterlist_find(aParameters, "muse.muse_standard.profile");
00402 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
00403 aParams->profile_s = cpl_parameter_get_string(p);
00404 aParams->profile =
00405 (!strcasecmp(aParams->profile_s, "gaussian")) ? MUSE_STANDARD_PARAM_PROFILE_GAUSSIAN :
00406 (!strcasecmp(aParams->profile_s, "moffat")) ? MUSE_STANDARD_PARAM_PROFILE_MOFFAT :
00407 (!strcasecmp(aParams->profile_s, "circle")) ? MUSE_STANDARD_PARAM_PROFILE_CIRCLE :
00408 (!strcasecmp(aParams->profile_s, "square")) ? MUSE_STANDARD_PARAM_PROFILE_SQUARE :
00409 MUSE_STANDARD_PARAM_PROFILE_INVALID_VALUE;
00410 cpl_ensure_code(aParams->profile != MUSE_STANDARD_PARAM_PROFILE_INVALID_VALUE,
00411 CPL_ERROR_ILLEGAL_INPUT);
00412
00413 p = cpl_parameterlist_find(aParameters, "muse.muse_standard.select");
00414 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
00415 aParams->select_s = cpl_parameter_get_string(p);
00416 aParams->select =
00417 (!strcasecmp(aParams->select_s, "flux")) ? MUSE_STANDARD_PARAM_SELECT_FLUX :
00418 (!strcasecmp(aParams->select_s, "distance")) ? MUSE_STANDARD_PARAM_SELECT_DISTANCE :
00419 MUSE_STANDARD_PARAM_SELECT_INVALID_VALUE;
00420 cpl_ensure_code(aParams->select != MUSE_STANDARD_PARAM_SELECT_INVALID_VALUE,
00421 CPL_ERROR_ILLEGAL_INPUT);
00422
00423 p = cpl_parameterlist_find(aParameters, "muse.muse_standard.smooth");
00424 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
00425 aParams->smooth_s = cpl_parameter_get_string(p);
00426 aParams->smooth =
00427 (!strcasecmp(aParams->smooth_s, "none")) ? MUSE_STANDARD_PARAM_SMOOTH_NONE :
00428 (!strcasecmp(aParams->smooth_s, "median")) ? MUSE_STANDARD_PARAM_SMOOTH_MEDIAN :
00429 (!strcasecmp(aParams->smooth_s, "ppoly")) ? MUSE_STANDARD_PARAM_SMOOTH_PPOLY :
00430 MUSE_STANDARD_PARAM_SMOOTH_INVALID_VALUE;
00431 cpl_ensure_code(aParams->smooth != MUSE_STANDARD_PARAM_SMOOTH_INVALID_VALUE,
00432 CPL_ERROR_ILLEGAL_INPUT);
00433
00434 p = cpl_parameterlist_find(aParameters, "muse.muse_standard.lambdamin");
00435 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
00436 aParams->lambdamin = cpl_parameter_get_double(p);
00437
00438 p = cpl_parameterlist_find(aParameters, "muse.muse_standard.lambdamax");
00439 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
00440 aParams->lambdamax = cpl_parameter_get_double(p);
00441
00442 p = cpl_parameterlist_find(aParameters, "muse.muse_standard.lambdaref");
00443 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
00444 aParams->lambdaref = cpl_parameter_get_double(p);
00445
00446 p = cpl_parameterlist_find(aParameters, "muse.muse_standard.darcheck");
00447 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
00448 aParams->darcheck_s = cpl_parameter_get_string(p);
00449 aParams->darcheck =
00450 (!strcasecmp(aParams->darcheck_s, "none")) ? MUSE_STANDARD_PARAM_DARCHECK_NONE :
00451 (!strcasecmp(aParams->darcheck_s, "check")) ? MUSE_STANDARD_PARAM_DARCHECK_CHECK :
00452 (!strcasecmp(aParams->darcheck_s, "correct")) ? MUSE_STANDARD_PARAM_DARCHECK_CORRECT :
00453 MUSE_STANDARD_PARAM_DARCHECK_INVALID_VALUE;
00454 cpl_ensure_code(aParams->darcheck != MUSE_STANDARD_PARAM_DARCHECK_INVALID_VALUE,
00455 CPL_ERROR_ILLEGAL_INPUT);
00456
00457 p = cpl_parameterlist_find(aParameters, "muse.muse_standard.filter");
00458 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
00459 aParams->filter = cpl_parameter_get_string(p);
00460
00461 return 0;
00462 }
00463
00464
00471
00472 static int
00473 muse_standard_exec(cpl_plugin *aPlugin)
00474 {
00475 if (cpl_plugin_get_type(aPlugin) != CPL_PLUGIN_TYPE_RECIPE) {
00476 return -1;
00477 }
00478 muse_processing_recipeinfo(aPlugin);
00479 cpl_recipe *recipe = (cpl_recipe *)aPlugin;
00480 cpl_msg_set_threadid_on();
00481
00482 cpl_frameset *usedframes = cpl_frameset_new(),
00483 *outframes = cpl_frameset_new();
00484 muse_standard_params_t params;
00485 muse_standard_params_fill(¶ms, recipe->parameters);
00486
00487 cpl_errorstate prestate = cpl_errorstate_get();
00488
00489 muse_processing *proc = muse_processing_new("muse_standard",
00490 recipe);
00491 int rc = muse_standard_compute(proc, ¶ms);
00492 cpl_frameset_join(usedframes, proc->usedframes);
00493 cpl_frameset_join(outframes, proc->outframes);
00494 muse_processing_delete(proc);
00495
00496 if (!cpl_errorstate_is_equal(prestate)) {
00497
00498 cpl_errorstate_dump(prestate, CPL_FALSE, muse_cplerrorstate_dump_some);
00499
00500 cpl_msg_set_level(CPL_MSG_INFO);
00501 }
00502
00503 muse_cplframeset_erase_duplicate(usedframes);
00504 muse_cplframeset_erase_duplicate(outframes);
00505
00506
00507
00508
00509
00510 muse_cplframeset_erase_all(recipe->frames);
00511 cpl_frameset_join(recipe->frames, usedframes);
00512 cpl_frameset_join(recipe->frames, outframes);
00513 cpl_frameset_delete(usedframes);
00514 cpl_frameset_delete(outframes);
00515 return rc;
00516 }
00517
00518
00525
00526 static int
00527 muse_standard_destroy(cpl_plugin *aPlugin)
00528 {
00529
00530 cpl_recipe *recipe;
00531 if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
00532 recipe = (cpl_recipe *)aPlugin;
00533 } else {
00534 return -1;
00535 }
00536
00537
00538 cpl_parameterlist_delete(recipe->parameters);
00539 muse_processinginfo_delete(recipe);
00540 return 0;
00541 }
00542
00543
00553
00554 int
00555 cpl_plugin_get_info(cpl_pluginlist *aList)
00556 {
00557 cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00558 cpl_plugin *plugin = &recipe->interface;
00559
00560 char *helptext;
00561 if (muse_cplframework() == MUSE_CPLFRAMEWORK_ESOREX) {
00562 helptext = cpl_sprintf("%s%s", muse_standard_help,
00563 muse_standard_help_esorex);
00564 } else {
00565 helptext = cpl_sprintf("%s", muse_standard_help);
00566 }
00567
00568
00569 cpl_plugin_init(plugin, CPL_PLUGIN_API, MUSE_BINARY_VERSION,
00570 CPL_PLUGIN_TYPE_RECIPE,
00571 "muse_standard",
00572 "Create a flux response curve from a standard star exposure.",
00573 helptext,
00574 "Peter Weilbacher",
00575 "usd-help@eso.org",
00576 muse_get_license(),
00577 muse_standard_create,
00578 muse_standard_exec,
00579 muse_standard_destroy);
00580 cpl_pluginlist_append(aList, plugin);
00581 cpl_free(helptext);
00582
00583 return 0;
00584 }
00585