35 #include "muse_dark_z.h"
64 static const char *muse_dark_help =
65 "This recipe combines several separate dark images into one master dark file. The master dark contains the combined pixel values of the raw dark exposures, with respect to the image combination method used and normalization time specified. Processing trims the raw data and records the overscan statistics, subtracts the bias (taking account of the overscan, if --overscan is not &none&) from each raw input image, converts them from adu to count, scales them according to their exposure time, and combines them using input parameters. Hot pixels are then identified using image statistics and marked in the data quality extension. The combined image is normalized to the specified exposure time. QC statistics are computed on the output master dark.";
67 static const char *muse_dark_help_esorex =
68 "\n\nInput frames for raw frame tag \"DARK\":\n"
69 "\n Frame tag Type Req #Fr Description"
70 "\n -------------------- ---- --- --- ------------"
71 "\n DARK raw Y >=3 Raw dark"
72 "\n MASTER_BIAS calib Y 1 Master bias"
73 "\n BADPIX_TABLE calib . 1 Bad pixel table"
74 "\n\nProduct frames for raw frame tag \"DARK\":\n"
75 "\n Frame tag Level Description"
76 "\n -------------------- -------- ------------"
77 "\n MASTER_DARK final Master dark";
88 static cpl_recipeconfig *
89 muse_dark_new_recipeconfig(
void)
91 cpl_recipeconfig *recipeconfig = cpl_recipeconfig_new();
95 cpl_recipeconfig_set_tag(recipeconfig, tag, 3, -1);
96 cpl_recipeconfig_set_input(recipeconfig, tag,
"MASTER_BIAS", 1, 1);
97 cpl_recipeconfig_set_input(recipeconfig, tag,
"BADPIX_TABLE", -1, 1);
98 cpl_recipeconfig_set_output(recipeconfig, tag,
"MASTER_DARK");
114 static cpl_error_code
115 muse_dark_prepare_header(
const char *aFrametag, cpl_propertylist *aHeader)
117 cpl_ensure_code(aFrametag, CPL_ERROR_NULL_INPUT);
118 cpl_ensure_code(aHeader, CPL_ERROR_NULL_INPUT);
119 if (!strcmp(aFrametag,
"MASTER_DARK")) {
122 "Number of saturated pixels in raw dark i in input list");
125 "Number of bad pixels determined from master dark");
128 "Median value of the master dark");
131 "Mean value of the master dark");
134 "Standard deviation of the master dark");
137 "Minimum value of the master dark");
140 "Maximum value of the master dark");
143 "[count/pix/h] Dark current measured on master dark in randomly placed windows");
146 "[count/pix/h] Dark current error measured on master dark in randomly placed windows");
149 "Number of saturated pixels in output data");
151 cpl_msg_warning(__func__,
"Frame tag %s is not defined", aFrametag);
152 return CPL_ERROR_ILLEGAL_INPUT;
154 return CPL_ERROR_NONE;
167 static cpl_frame_level
168 muse_dark_get_frame_level(
const char *aFrametag)
171 return CPL_FRAME_LEVEL_NONE;
173 if (!strcmp(aFrametag,
"MASTER_DARK")) {
174 return CPL_FRAME_LEVEL_FINAL;
176 return CPL_FRAME_LEVEL_NONE;
190 muse_dark_get_frame_mode(
const char *aFrametag)
195 if (!strcmp(aFrametag,
"MASTER_DARK")) {
213 muse_dark_create(cpl_plugin *aPlugin)
217 if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
218 recipe = (cpl_recipe *)aPlugin;
226 muse_dark_new_recipeconfig(),
227 muse_dark_prepare_header,
228 muse_dark_get_frame_level,
229 muse_dark_get_frame_mode);
234 cpl_msg_set_time_on();
238 recipe->parameters = cpl_parameterlist_new();
243 p = cpl_parameter_new_range(
"muse.muse_dark.nifu",
245 "IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in parallel.",
250 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"nifu");
251 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"nifu");
253 cpl_parameterlist_append(recipe->parameters, p);
256 p = cpl_parameter_new_value(
"muse.muse_dark.overscan",
258 "If this is \"none\", stop when detecting discrepant overscan levels (see ovscsigma), for \"offset\" it assumes that the mean overscan level represents the real offset in the bias levels of the exposures involved, and adjusts the data accordingly; for \"vpoly\", a polynomial is fit to the vertical overscan and subtracted from the whole quadrant.",
260 (
const char *)
"vpoly");
261 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"overscan");
262 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"overscan");
264 cpl_parameterlist_append(recipe->parameters, p);
267 p = cpl_parameter_new_value(
"muse.muse_dark.ovscreject",
269 "This influences how values are rejected when computing overscan statistics. Either no rejection at all (\"none\"), rejection using the DCR algorithm (\"dcr\"), or rejection using an iterative constant fit (\"fit\").",
271 (
const char *)
"dcr");
272 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"ovscreject");
273 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"ovscreject");
275 cpl_parameterlist_append(recipe->parameters, p);
278 p = cpl_parameter_new_value(
"muse.muse_dark.ovscsigma",
280 "If the deviation of mean overscan levels between a raw input image and the reference image is higher than |ovscsigma x stdev|, stop the processing. If overscan=\"vpoly\", this is used as sigma rejection level for the iterative polynomial fit (the level comparison is then done afterwards with |100 x stdev| to guard against incompatible settings). Has no effect for overscan=\"offset\".",
283 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"ovscsigma");
284 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"ovscsigma");
286 cpl_parameterlist_append(recipe->parameters, p);
289 p = cpl_parameter_new_value(
"muse.muse_dark.ovscignore",
291 "The number of pixels of the overscan adjacent to the data region of the CCD that are ignored when computing statistics or fits.",
294 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"ovscignore");
295 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"ovscignore");
297 cpl_parameterlist_append(recipe->parameters, p);
300 p = cpl_parameter_new_enum(
"muse.muse_dark.combine",
302 "Type of image combination to use.",
304 (
const char *)
"sigclip",
306 (
const char *)
"average",
307 (
const char *)
"median",
308 (
const char *)
"minmax",
309 (
const char *)
"sigclip");
310 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"combine");
311 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"combine");
313 cpl_parameterlist_append(recipe->parameters, p);
316 p = cpl_parameter_new_value(
"muse.muse_dark.nlow",
318 "Number of minimum pixels to reject with minmax.",
321 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"nlow");
322 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"nlow");
324 cpl_parameterlist_append(recipe->parameters, p);
327 p = cpl_parameter_new_value(
"muse.muse_dark.nhigh",
329 "Number of maximum pixels to reject with minmax.",
332 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"nhigh");
333 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"nhigh");
335 cpl_parameterlist_append(recipe->parameters, p);
338 p = cpl_parameter_new_value(
"muse.muse_dark.nkeep",
340 "Number of pixels to keep with minmax.",
343 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"nkeep");
344 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"nkeep");
346 cpl_parameterlist_append(recipe->parameters, p);
349 p = cpl_parameter_new_value(
"muse.muse_dark.lsigma",
351 "Low sigma for pixel rejection with sigclip.",
354 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"lsigma");
355 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"lsigma");
357 cpl_parameterlist_append(recipe->parameters, p);
360 p = cpl_parameter_new_value(
"muse.muse_dark.hsigma",
362 "High sigma for pixel rejection with sigclip.",
365 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"hsigma");
366 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"hsigma");
368 cpl_parameterlist_append(recipe->parameters, p);
371 p = cpl_parameter_new_value(
"muse.muse_dark.scale",
373 "Scale the individual images to a common exposure time before combining them.",
376 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"scale");
377 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"scale");
379 cpl_parameterlist_append(recipe->parameters, p);
382 p = cpl_parameter_new_value(
"muse.muse_dark.normalize",
384 "Normalize the master dark to this exposure time (in seconds). To disable normalization, set this to a negative value.",
387 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"normalize");
388 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"normalize");
390 cpl_parameterlist_append(recipe->parameters, p);
393 p = cpl_parameter_new_value(
"muse.muse_dark.hotsigma",
395 "Sigma level, in terms of median deviation above the median dark level, above which a pixel is detected and marked as 'hot'.",
398 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"hotsigma");
399 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"hotsigma");
401 cpl_parameterlist_append(recipe->parameters, p);
404 p = cpl_parameter_new_value(
"muse.muse_dark.merge",
406 "Merge output products from different IFUs into a common file.",
409 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CFG,
"merge");
410 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"merge");
412 cpl_parameterlist_append(recipe->parameters, p);
432 cpl_ensure_code(aParams, CPL_ERROR_NULL_INPUT);
433 cpl_ensure_code(aParameters, CPL_ERROR_NULL_INPUT);
436 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.nifu");
437 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
438 aParams->
nifu = cpl_parameter_get_int(p);
440 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.overscan");
441 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
442 aParams->
overscan = cpl_parameter_get_string(p);
444 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.ovscreject");
445 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
446 aParams->
ovscreject = cpl_parameter_get_string(p);
448 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.ovscsigma");
449 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
450 aParams->
ovscsigma = cpl_parameter_get_double(p);
452 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.ovscignore");
453 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
454 aParams->
ovscignore = cpl_parameter_get_int(p);
456 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.combine");
457 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
458 aParams->
combine_s = cpl_parameter_get_string(p);
460 (!strcasecmp(aParams->
combine_s,
"average")) ? MUSE_DARK_PARAM_COMBINE_AVERAGE :
461 (!strcasecmp(aParams->
combine_s,
"median")) ? MUSE_DARK_PARAM_COMBINE_MEDIAN :
462 (!strcasecmp(aParams->
combine_s,
"minmax")) ? MUSE_DARK_PARAM_COMBINE_MINMAX :
463 (!strcasecmp(aParams->
combine_s,
"sigclip")) ? MUSE_DARK_PARAM_COMBINE_SIGCLIP :
464 MUSE_DARK_PARAM_COMBINE_INVALID_VALUE;
465 cpl_ensure_code(aParams->
combine != MUSE_DARK_PARAM_COMBINE_INVALID_VALUE,
466 CPL_ERROR_ILLEGAL_INPUT);
468 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.nlow");
469 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
470 aParams->
nlow = cpl_parameter_get_int(p);
472 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.nhigh");
473 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
474 aParams->
nhigh = cpl_parameter_get_int(p);
476 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.nkeep");
477 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
478 aParams->
nkeep = cpl_parameter_get_int(p);
480 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.lsigma");
481 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
482 aParams->
lsigma = cpl_parameter_get_double(p);
484 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.hsigma");
485 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
486 aParams->
hsigma = cpl_parameter_get_double(p);
488 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.scale");
489 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
490 aParams->
scale = cpl_parameter_get_bool(p);
492 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.normalize");
493 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
494 aParams->
normalize = cpl_parameter_get_double(p);
496 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.hotsigma");
497 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
498 aParams->
hotsigma = cpl_parameter_get_double(p);
500 p = cpl_parameterlist_find(aParameters,
"muse.muse_dark.merge");
501 cpl_ensure_code(p, CPL_ERROR_DATA_NOT_FOUND);
502 aParams->
merge = cpl_parameter_get_bool(p);
516 muse_dark_exec(cpl_plugin *aPlugin)
518 if (cpl_plugin_get_type(aPlugin) != CPL_PLUGIN_TYPE_RECIPE) {
521 cpl_recipe *recipe = (cpl_recipe *)aPlugin;
522 cpl_msg_set_threadid_on();
524 cpl_frameset *usedframes = cpl_frameset_new(),
525 *outframes = cpl_frameset_new();
527 muse_dark_params_fill(¶ms, recipe->parameters);
529 cpl_errorstate prestate = cpl_errorstate_get();
531 if (params.
nifu < -1 || params.
nifu > kMuseNumIFUs) {
532 cpl_msg_error(__func__,
"Please specify a valid IFU number (between 1 and "
533 "%d), 0 (to process all IFUs consecutively), or -1 (to "
534 "process all IFUs in parallel) using --nifu.", kMuseNumIFUs);
538 cpl_boolean donotmerge = CPL_FALSE;
540 if (params.
nifu > 0) {
543 rc = muse_dark_compute(proc, ¶ms);
544 cpl_frameset_join(usedframes, proc->
usedframes);
545 cpl_frameset_join(outframes, proc->
outframes);
547 donotmerge = CPL_TRUE;
548 }
else if (params.
nifu < 0) {
549 int *rcs = cpl_calloc(kMuseNumIFUs,
sizeof(
int));
551 #pragma omp parallel for default(none) \
552 shared(outframes, params, rcs, recipe, usedframes)
553 for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
559 int *rci = rcs + (nifu - 1);
560 *rci = muse_dark_compute(proc, pars);
561 if (rci && cpl_error_get_code() == MUSE_ERROR_CHIP_NOT_LIVE) {
565 #pragma omp critical(muse_processing_used_frames)
566 cpl_frameset_join(usedframes, proc->
usedframes);
567 #pragma omp critical(muse_processing_output_frames)
568 cpl_frameset_join(outframes, proc->
outframes);
573 for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
574 if (rcs[nifu-1] != 0) {
580 for (params.
nifu = 1; params.
nifu <= kMuseNumIFUs && !rc; params.
nifu++) {
583 rc = muse_dark_compute(proc, ¶ms);
584 if (rc && cpl_error_get_code() == MUSE_ERROR_CHIP_NOT_LIVE) {
587 cpl_frameset_join(usedframes, proc->
usedframes);
588 cpl_frameset_join(outframes, proc->
outframes);
592 UNUSED_ARGUMENT(donotmerge);
594 if (!cpl_errorstate_is_equal(prestate)) {
598 cpl_msg_set_level(CPL_MSG_INFO);
605 if (params.
merge && !donotmerge) {
606 muse_utils_frameset_merge_frames(outframes);
614 cpl_frameset_join(recipe->frames, usedframes);
615 cpl_frameset_join(recipe->frames, outframes);
616 cpl_frameset_delete(usedframes);
617 cpl_frameset_delete(outframes);
630 muse_dark_destroy(cpl_plugin *aPlugin)
634 if (cpl_plugin_get_type(aPlugin) == CPL_PLUGIN_TYPE_RECIPE) {
635 recipe = (cpl_recipe *)aPlugin;
641 cpl_parameterlist_delete(recipe->parameters);
658 cpl_plugin_get_info(cpl_pluginlist *aList)
660 cpl_recipe *recipe = cpl_calloc(1,
sizeof *recipe);
661 cpl_plugin *plugin = &recipe->interface;
665 helptext = cpl_sprintf(
"%s%s", muse_dark_help,
666 muse_dark_help_esorex);
668 helptext = cpl_sprintf(
"%s", muse_dark_help);
672 cpl_plugin_init(plugin, CPL_PLUGIN_API, MUSE_BINARY_VERSION,
673 CPL_PLUGIN_TYPE_RECIPE,
675 "Combine several separate dark images into one master dark file and locate hot pixels.",
683 cpl_pluginlist_append(aList, plugin);
void muse_processing_delete(muse_processing *aProcessing)
Free the muse_processing structure.
int ovscignore
The number of pixels of the overscan adjacent to the data region of the CCD that are ignored when com...
const char * combine_s
Type of image combination to use. (as string)
muse_cplframework_type muse_cplframework(void)
Return the CPL framework the recipe is run under.
int scale
Scale the individual images to a common exposure time before combining them.
const char * ovscreject
This influences how values are rejected when computing overscan statistics. Either no rejection at al...
cpl_frameset * usedframes
muse_processing * muse_processing_new(const char *aName, cpl_recipe *aRecipe)
Create a new processing structure.
const char * muse_get_license(void)
Get the pipeline copyright and license.
double hotsigma
Sigma level, in terms of median deviation above the median dark level, above which a pixel is detecte...
int nkeep
Number of pixels to keep with minmax.
Structure to hold the parameters of the muse_dark recipe.
void muse_cplerrorstate_dump_some(unsigned aCurrent, unsigned aFirst, unsigned aLast)
Dump some CPL errors.
void muse_processinginfo_delete(cpl_recipe *)
Clear all information from the processing info and from the recipe config.
double ovscsigma
If the deviation of mean overscan levels between a raw input image and the reference image is higher ...
int merge
Merge output products from different IFUs into a common file.
double lsigma
Low sigma for pixel rejection with sigclip.
cpl_error_code muse_cplframeset_erase_duplicate(cpl_frameset *aFrames)
Erase all duplicate frames from a frameset.
cpl_error_code muse_cplframeset_erase_all(cpl_frameset *aFrames)
Erase all frames in a frameset.
int combine
Type of image combination to use.
int nifu
IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in ...
double normalize
Normalize the master dark to this exposure time (in seconds). To disable normalization, set this to a negative value.
void muse_processinginfo_register(cpl_recipe *, cpl_recipeconfig *, muse_processing_prepare_header_func *, muse_processing_get_frame_level_func *, muse_processing_get_frame_mode_func *)
Register extended functionalities for MUSE recipes.
const char * overscan
If this is "none", stop when detecting discrepant overscan levels (see ovscsigma), for "offset" it assumes that the mean overscan level represents the real offset in the bias levels of the exposures involved, and adjusts the data accordingly; for "vpoly", a polynomial is fit to the vertical overscan and subtracted from the whole quadrant.
int nlow
Number of minimum pixels to reject with minmax.
int nhigh
Number of maximum pixels to reject with minmax.
double hsigma
High sigma for pixel rejection with sigclip.
cpl_error_code muse_processing_prepare_property(cpl_propertylist *, const char *, cpl_type, const char *)
Prepare and check the specified property.