39#include "eris_ifu_dfs.h"
40#include "eris_ifu_utils.h"
41#include "eris_ifu_error.h"
42#include "eris_ifu_extract_spec_static.h"
47#define CONTEXT "eris.eris_ifu_extract_spec"
49static const char eris_ifu_extract_spec_description[] =
"\
50This recipe extract a single spectrum from a masked spatial area of an ERIS/SPIFFIER cube.\n\
52The area mask can be specified in the following ways selected by the \"mask_method\" recipe parameter:\n\
53- mask: a user supplied mask in the SOF (tag is MASK)\n\
54- position: a user specified circle (\"center\" and \"radius\")\n\
55- max: a circle (with \"radius\") around the position of the maximum of the median collapsed cube\n\
56- fit: a two dimensional gaussian fit of the median collapsed cube (NOT YET IMPLEMENTED!)\n\
58-----------------------------------------------------------------------------\n\
60 DO CATG Explanation Required #Frames\n\
61 ------- ----------- -------- -------\n\
62 CUBE ERIS/SPIFFIER cube Y 1 \n\
63 MASK spatial mask file N [0,1]\n\
66 DO CATG Explanation Product Depth \n\
67 ------- ------------------------------------------- --------------\n\
68 SCIENCE.SPECTRUM extracted spectrum (table) PD_SCIENCE (0)\n\
69 SPECTRUM extracted spectrum (vector) PD_AUX (1)\n\
70 MASK generated and used spatial mask PD_ALL (2)\n\
71 COLLAPSED_CUBE generated and used collapsed cube image PD_ALL (2)\n\
81cpl_recipe_define(eris_ifu_extract_spec, ERIS_BINARY_VERSION,
"Erich Wiezorrek",
82 "erw@mpe.mpg.de",
"2022",
83 "This recipe extracts a spectrum for a data cube",
84 eris_ifu_extract_spec_description);
87 const cpl_parameterlist * parlist,
89 struct esParamStruct *params);
91cpl_error_code eris_ifu_extract_spec_processSof(cpl_frameset* frames,
92 struct esSofStruct *sof);
94cpl_error_code eris_ifu_extract_spec_save_products(
96 const cpl_parameterlist * parlist,
97 struct esSofStruct sof,
98 cpl_bivector *spectrum,
100 cpl_vector *totalFlux,
101 productDepthType productDepth);
115static cpl_error_code eris_ifu_extract_spec_fill_parameterlist(cpl_parameterlist *pl)
117 cpl_error_code err = CPL_ERROR_NONE;
118 cpl_parameter *p = NULL;
120 char *context = NULL;
125 context = cpl_sprintf(
"eris.%s", REC_NAME_EXTRACT_SPEC));
134 pName = cpl_sprintf(
"%s.%s", context,
"mask_method"));
136 p = cpl_parameter_new_value(pName, CPL_TYPE_STRING,
137 "Method to specify extraction mask : mask, position, max, fit or optimal",
138 context,
"optimal"));
139 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"mask_method");
140 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
141 cpl_parameterlist_append(pl, p);
146 pName = cpl_sprintf(
"%s.%s", context,
"center"));
148 p = cpl_parameter_new_value(pName, CPL_TYPE_STRING,
149 "The centre of the circular mask (pixel)",
151 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"center");
152 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
153 cpl_parameterlist_append(pl, p);
158 pName = cpl_sprintf(
"%s.%s", context,
"radius"));
160 p = cpl_parameter_new_value(pName, CPL_TYPE_DOUBLE,
161 "The radius of the circular mask (pixel)",
163 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"radius");
164 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
165 cpl_parameterlist_append(pl, p);
171 pName = cpl_sprintf(
"%s.%s", context,
"product_depth"));
173 p = cpl_parameter_new_value(pName, CPL_TYPE_INT,
174 "Specifies the product output depth "
175 "(>0 for auxiliary products)",
178 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
181 cpl_parameterlist_append(pl, p));
187 err = cpl_error_get_code();
203 const cpl_parameterlist * parlist)
205 struct esParamStruct params;
206 struct esSofStruct sof;
207 cpl_propertylist *pl = NULL;
208 cpl_image *mask = NULL,
210 cpl_bivector *spectrum = NULL;
211 cpl_vector *error = NULL,
214 hdrl_image *collapsedCube = NULL;
215 double startLambda = 0.,
218 cpl_propertylist *h = NULL,
223 cpl_msg_info(cpl_func,
"Reading recipe parameters");
227 cpl_msg_info(cpl_func,
"Reading SOF");
229 eris_ifu_extract_spec_processSof(frameset, &sof));
231 if (params.mask_method == MASK && sof.mask == NULL) {
233 "missing MASK input in the SOF which is required "
234 "if mask_method is MASK");
236 if (params.mask_method != MASK && sof.mask != NULL) {
237 cpl_msg_warning(cpl_func,
238 "MASK input in the SOF is ignored because mask_method is not MASK");
243 refPixel = cpl_propertylist_get_double(h,
"CRPIX3");
244 if (cpl_error_get_code() == CPL_ERROR_DATA_NOT_FOUND) {
246 cpl_frame *frame = cpl_frameset_find(frameset, ERIS_IFU_UTIL_CUBE);
247 h = cpl_propertylist_load(cpl_frame_get_filename(frame), 1);
248 refPixel = cpl_propertylist_get_double(h,
"CRPIX3");
251 startLambda = cpl_propertylist_get_double(h,
"CRVAL3");
252 if(cpl_propertylist_has(sof.header,
"CDELT3")) {
253 deltaLambda = cpl_propertylist_get_double(h,
"CDELT3");
255 deltaLambda = cpl_propertylist_get_double(h,
"CD3_3");
257 startLambda = startLambda - (refPixel - 1.) * deltaLambda;
266 collapsedCube = eris_ifu_extract_spec_collapse(sof.cube, &contribMap));
275 mask = eris_ifu_extract_spec_create_mask(params, sof,
277 params.productDepth));
279 if (params.productDepth >= PD_ALL) {
281 pl = cpl_propertylist_duplicate(sof.header));
283 cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG,
"COLLAPSED_IMAGE");
285 "collapsedCube.fits", CPL_TYPE_FLOAT, pl, CPL_IO_CREATE);
287 cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG,
"MASK");
288 cpl_image_save(mask,
"mask.fits", CPL_TYPE_FLOAT, pl, CPL_IO_CREATE);
292 if (params.mask_method == OPTIMAL) {
294 spectrum = eris_ifu_optimal_extraction(sof.cube,
297 startLambda, deltaLambda,
303 spectrum = eris_ifu_extract_spectrum(sof.cube, mask,
304 startLambda, deltaLambda,
305 &error, &totalFlux, &qual));
311 eris_ifu_extract_spec_save_products(frameset, parlist,
313 spectrum, error, totalFlux,
314 params.productDepth));
326 return (
int)cpl_error_get_code();
341 const cpl_parameterlist * parlist,
342 struct esParamStruct *params)
346 const char* methodString;
347 methodString = cpl_parameter_get_string(
348 cpl_parameterlist_find_const(parlist,
349 CONTEXT
".mask_method"));
350 if (strncasecmp(methodString,
"mask", strlen(
"mask")) == 0) {
351 params->mask_method = MASK;
352 }
else if (strncasecmp(methodString,
"position", strlen(
"position")) == 0) {
353 params->mask_method = POSITION;
354 }
else if (strncasecmp(methodString,
"max", strlen(
"max")) == 0) {
355 params->mask_method = MAX;
356 }
else if (strncasecmp(methodString,
"fit", strlen(
"fit")) == 0) {
357 params->mask_method = FIT;
358 }
else if (strncasecmp(methodString,
"optimal", strlen(
"optimal")) == 0) {
359 params->mask_method = OPTIMAL;
361 cpl_msg_error(cpl_func,
"The mask_method parameter must be one "
362 "of the list: mask, position, max, fit, optimal");
364 "Error reading recipe parameter, unknown mask method %s",
370 center = cpl_parameter_get_string(
371 cpl_parameterlist_find_const(parlist,
375 nFields = sscanf(center,
"%d,%d%n",
376 ¶ms->center_x, ¶ms->center_y, &end);
377 if (nFields != 2 || end != strlen(center)) {
378 cpl_msg_error(cpl_func,
"The center parameter must be "
379 "a list of two integers separated by a comma");
381 "Error reading recipe parameter, cannot properly read center spec %s",
383 printf(
"Error reading center string\n");
386 params->radius = cpl_parameter_get_double(
387 cpl_parameterlist_find_const(parlist, CONTEXT
".radius"));
389 params->productDepth = cpl_parameter_get_int(
390 cpl_parameterlist_find_const(parlist, CONTEXT
".product_depth"));
397 return cpl_error_get_code();
411cpl_error_code eris_ifu_extract_spec_processSof(
412 cpl_frameset* frames,
415 struct esSofStruct *sof)
417 cpl_frame *frame = NULL;
421 if (frames == NULL) {
425 if (cpl_frameset_is_empty(frames)) {
427 "SOF file is empty or missing");
435 frame = cpl_frameset_find(frames, ERIS_IFU_UTIL_CUBE);
440 cpl_frame_get_filename(frame),
441 &sof->header, &sof->qualImagelist, &sof->qualityType));
447 "missing \"%s\" tag in the SOF, input data cube",
454 frame = cpl_frameset_find(frames, ERIS_IFU_UTIL_MASK);
458 sof->mask = cpl_image_load(
459 cpl_frame_get_filename(frame),
460 CPL_TYPE_DOUBLE, 0, 0));
468 return cpl_error_get_code();
489cpl_error_code eris_ifu_extract_spec_save_products(
490 cpl_frameset* frames,
491 const cpl_parameterlist * parlist,
492 struct esSofStruct sof,
493 cpl_bivector *spectrum,
495 cpl_vector *totalFlux,
496 productDepthType productDepth)
498 cpl_table *productTable = NULL;
499 cpl_propertylist *pl;
500 cpl_vector *lambda = NULL;
501 cpl_vector *data = NULL;
502 const cpl_array *waveArray = NULL;
503 const cpl_array *fluxArray = NULL;
504 const cpl_array *errArray = NULL;
505 const cpl_array *totalFluxArray = NULL;
508 cpl_size spectrumSize;
512 spectrumSize = cpl_bivector_get_size(spectrum);
513 BRK_IF_NULL(lambda = cpl_bivector_get_x(spectrum));
515 firstLambda = cpl_vector_get(lambda,0);
516 deltaLambda = cpl_vector_get(lambda,1) - firstLambda;
518 if (productDepth >= PD_AUXILLIARY) {
519 BRK_IF_NULL(pl = cpl_propertylist_duplicate(sof.header));
520 cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG,
"SPECTRUM");
521 cpl_propertylist_update_string(pl,
"CTYPE1",
"WAVE");
522 cpl_propertylist_update_double(pl,
"CRPIX1", 1.);
523 cpl_propertylist_update_double(pl,
"CRVAL1", firstLambda);
524 cpl_propertylist_update_double(pl,
"CDELT1", deltaLambda);
525 cpl_propertylist_erase(pl,
"CTYPE2");
526 cpl_propertylist_erase(pl,
"CRPIX2");
527 cpl_propertylist_erase(pl,
"CRVAL2");
528 cpl_propertylist_erase(pl,
"CDELT2");
529 cpl_propertylist_erase(pl,
"CTYPE3");
530 cpl_propertylist_erase(pl,
"CRPIX3");
531 cpl_propertylist_erase(pl,
"CRVAL3");
532 cpl_propertylist_erase(pl,
"CDELT3");
533 cpl_propertylist_update_double(pl,
"CD1_1", 0.);
534 cpl_propertylist_update_double(pl,
"CD1_2", deltaLambda);
535 cpl_propertylist_erase(pl,
"CD1_3");
536 cpl_propertylist_erase(pl,
"CD2_1");
537 cpl_propertylist_erase(pl,
"CD2_2");
538 cpl_propertylist_erase(pl,
"CD2_3");
539 cpl_propertylist_erase(pl,
"CD3_1");
540 cpl_propertylist_erase(pl,
"CD3_2");
541 cpl_propertylist_erase(pl,
"CD3_3");
545 cpl_vector_save(data,
"spectrum_vector.fits",
546 CPL_TYPE_DOUBLE, pl, CPL_IO_CREATE));
547 cpl_propertylist_delete(pl);
550 waveArray = cpl_array_wrap_double(cpl_vector_get_data(lambda), spectrumSize);
551 fluxArray = cpl_array_wrap_double(cpl_vector_get_data(data), spectrumSize);
552 errArray = cpl_array_wrap_double(cpl_vector_get_data(error), spectrumSize);
553 if (totalFlux != NULL) {
554 totalFluxArray = cpl_array_wrap_double(cpl_vector_get_data(totalFlux),
557 BRK_IF_NULL(pl = cpl_propertylist_duplicate(sof.header));
558 cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG,
"SCIENCE.SPECTRUM");
560 cpl_table_new_column_array(productTable,
"WAVE",
561 CPL_TYPE_DOUBLE, spectrumSize);
562 if (totalFlux != NULL) {
563 cpl_table_new_column_array(productTable,
"FLUX",
564 CPL_TYPE_DOUBLE, spectrumSize);
566 cpl_table_new_column_array(productTable,
"TOT_FLUX",
567 CPL_TYPE_DOUBLE, spectrumSize);
569 cpl_table_new_column_array(productTable,
"ERR",
570 CPL_TYPE_DOUBLE, spectrumSize);
571 if (totalFlux != NULL) {
572 cpl_table_new_column_array(productTable,
"TOT_FLUX",
573 CPL_TYPE_DOUBLE, spectrumSize);
575 cpl_table_set_array(productTable,
"WAVE", 0, waveArray);
576 if (totalFlux != NULL) {
577 cpl_table_set_array(productTable,
"FLUX", 0, fluxArray);
579 cpl_table_set_array(productTable,
"TOT_FLUX", 0, fluxArray);
581 cpl_table_set_array(productTable,
"ERR", 0, errArray);
582 if (totalFlux != NULL) {
583 cpl_table_set_array(productTable,
"TOT_FLUX", 0, totalFluxArray);
587 cpl_dfs_save_table(frames, NULL, parlist, frames, NULL,
588 productTable, NULL, REC_NAME_EXTRACT_SPEC, pl,
589 NULL, PACKAGE
"/" PACKAGE_VERSION,
"spectrum.fits");
591 cpl_propertylist_delete(pl);
597 return cpl_error_get_code();
hdrl_imagelist * eris_ifu_load_deq_hdrl_imagelist(const char *filename, cpl_propertylist **primaryHeader, cpl_imagelist **qualImagelist, deqQualityType *qualityType)
Load a DEQ-format cube into an HDRL imagelist structure.
cpl_error_code eris_ifu_dfs_set_groups(cpl_frameset *self)
Set the frame group (RAW, CALIB, or PRODUCT) for all frames in a frameset.
#define BRK_IF_ERROR(function)
If function is or returns an error != CPL_ERROR_NONE, then the try-block is exited.
#define RECOVER(void)
Recover the error state which was present during TRY (at the beginning of the try-block).
#define CHECK_ERROR_STATE(void)
Check the CPL error state, and exit the try-block if not CPL_ERROR_NONE.
#define BRK_WITH_ERROR_MSG(code,...)
Set a new CPL error, and exit the try-block.
#define TRY
Beginning of a TRY-block.
#define CATCH
End of a TRY-block, beginning of a CATCH-block.
#define BRK_IF_NULL(function)
If function is or returns a NULL pointer, then the try-block is exited.
#define CATCH_MSGS()
Displays an error message stack.
cpl_error_code eris_ifu_extract_spec(const cpl_imagelist *data_in, const cpl_imagelist *noise_in, cpl_image *mask, cpl_vector **spec_data_out, cpl_vector **spec_noise_out)
Extract a spectrum from 3D data cube.
cpl_error_code eris_ifu_extract_spec_fetch_params(const cpl_parameterlist *parlist, struct esParamStruct *params, const char *context)
Fetch spectral extraction parameters from parameter list.
void eris_ifu_free_propertylist(cpl_propertylist **item)
Free memory and set pointer to null.
void eris_ifu_free_string(char **item)
Free memory and set pointer to null.
void eris_ifu_free_vector(cpl_vector **item)
Free memory and set pointer to null.
void eris_ifu_free_hdrl_image(hdrl_image **item)
Free memory and set pointer to null.
void eris_ifu_free_image(cpl_image **item)
Free memory and set pointer to null.
void eris_ifu_free_bivector(cpl_bivector **item)
Free memory and set pointer to null.
cpl_image * hdrl_image_get_image(hdrl_image *himg)
get data as cpl image
cpl_size hdrl_imagelist_get_size_y(const hdrl_imagelist *himlist)
Get number of rows of images in the imagelist.
cpl_size hdrl_imagelist_get_size(const hdrl_imagelist *himlist)
Get the number of images in the imagelist.
cpl_size hdrl_imagelist_get_size_x(const hdrl_imagelist *himlist)
Get number of colums of images in the imagelist.