35 #include "muse_basicproc.h"
37 #include "muse_combine.h"
38 #include "muse_pfits.h"
39 #include "muse_quadrants.h"
40 #include "muse_quality.h"
41 #include "muse_utils.h"
42 #include "muse_data_format_z.h"
48 #define GENERATE_TEST_IMAGES 0
87 bpars->
overscan = cpl_parameter_get_string(param);
89 bpars->
rejection = cpl_parameter_get_string(param);
91 bpars->
ovscsigma = cpl_parameter_get_double(param);
93 bpars->
ovscignore = cpl_parameter_get_int(param);
95 if (strstr(aPrefix,
"muse_scibasic")) {
97 bpars->
crmethod = cpl_parameter_get_string(param);
99 bpars->
dcrxbox = cpl_parameter_get_int(param);
101 bpars->
dcrybox = cpl_parameter_get_int(param);
103 bpars->
dcrpasses = cpl_parameter_get_int(param);
105 bpars->
dcrthres = cpl_parameter_get_double(param);
142 static cpl_error_code
145 cpl_ensure_code(aImage && aRef, CPL_ERROR_NULL_INPUT);
146 cpl_ensure_code(aImage->
header && aRef->
header, CPL_ERROR_NULL_INPUT);
148 cpl_propertylist *him = aImage->
header,
151 const char *fn1 = cpl_propertylist_get_string(him, MUSE_HDR_TMP_FN),
152 *fn2 = cpl_propertylist_get_string(href, MUSE_HDR_TMP_FN),
155 cpl_msg_error(__func__,
"\"%s\" does not contain a category (ESO.PRO.CATG)!",
157 return CPL_ERROR_ILLEGAL_INPUT;
162 const char *modestr1 = cpl_propertylist_get_string(him,
"ESO INS MODE"),
163 *modestr2 = cpl_propertylist_get_string(href,
"ESO INS MODE");
176 cpl_boolean chipinfo = chipname1 && chipid1
177 && chipname2 && chipid2;
179 cpl_msg_warning(__func__,
"CHIP information is missing (ESO.DET.CHIP."
180 "{NAME,ID}) from \"%s\" (%s, %s) or \"%s\" (%s, %s)",
181 fn1, chipname1, chipid1, fn2, chipname2, chipid2);
184 if (binx1 != binx2 || biny1 != biny2) {
185 cpl_msg_error(__func__,
"Binning of \"%s\" (%dx%d) and \"%s\" (%dx%d) does "
186 "not match", fn1, binx1, biny1, fn2, binx2, biny2);
187 return CPL_ERROR_TYPE_MISMATCH;
193 if (!strncmp(catg,
"MASTER_BIAS", 12)) {
194 if (readid1 != readid2) {
195 cpl_msg_error(__func__,
"Read-out mode of \"%s\" (%d: %s) and \"%s\" (%d:"
196 " %s) does not match", fn1, readid1, readname1, fn2,
198 return CPL_ERROR_TYPE_MISMATCH;
200 if (chipinfo && (strcmp(chipname1, chipname2) || strcmp(chipid1, chipid2))) {
201 cpl_msg_error(__func__,
"CHIP information (ESO.DET.CHIP.{NAME,ID}) "
202 "does not match for \"%s\" (%s, %s) and \"%s\" (%s, %s)",
203 fn1, chipname1, chipid1, fn2, chipname2, chipid2);
204 return CPL_ERROR_TYPE_MISMATCH;
210 if (!strncmp(catg,
"MASTER_FLAT", 12)) {
211 if (mode1 != mode2) {
212 cpl_msg_error(__func__,
"Instrument modes for \"%s\" (%s) and \"%s\" (%s)"
213 " do not match", fn1, modestr1, fn2, modestr2);
214 return CPL_ERROR_TYPE_MISMATCH;
223 if (readid1 != readid2) {
224 cpl_msg_warning(__func__,
"Read-out mode of \"%s\" (%d: %s) and \"%s\" (%d:"
225 " %s) does not match", fn1, readid1, readname1, fn2,
228 if (chipinfo && (strcmp(chipname1, chipname2) || strcmp(chipid1, chipid2))) {
229 cpl_msg_warning(__func__,
"CHIP information (ESO.DET.CHIP.{NAME,ID,DATE}) "
230 "does not match for \"%s\" (%s, %s) and \"%s\" (%s, %s)",
231 fn1, chipname1, chipid1, fn2, chipname2, chipid2);
234 return CPL_ERROR_NONE;
251 static cpl_error_code
255 cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
256 cpl_boolean ovscvpoly = aBPars && aBPars->
overscan
257 && !strncmp(aBPars->
overscan,
"vpoly", 5);
259 cpl_msg_debug(__func__,
"not vpoly: %s!", aBPars ? aBPars->
overscan :
"");
260 return CPL_ERROR_NONE;
263 unsigned char ovscvorder = 3;
266 char *rest = strchr(aBPars->
overscan,
':');
267 if (strlen(aBPars->
overscan) > 6 && rest) {
268 ovscvorder = strtol(++rest, &rest, 10);
269 if (strlen(rest) > 0) {
270 frms = strtod(++rest, &rest);
271 if (strlen(rest) > 0) {
272 fchisq = strtod(++rest, &rest);
276 cpl_msg_debug(__func__,
"is vpoly: %s --> %hhu, %f, %f (ignore=%u, sigma=%f)",
280 cpl_error_code rc = CPL_ERROR_NONE;
282 for (k = 0; k < aList->
size; k++) {
287 if (rc != CPL_ERROR_NONE) {
289 cpl_msg_error(__func__,
"Could not correct quadrants levels using vertical"
290 " overscan fit in IFU %hhu: %s", ifu, cpl_error_get_message());
315 static cpl_error_code
318 cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
321 for (k = 0; k < aList->
size; k++) {
326 cpl_msg_warning(__func__,
"Could not compute overscan statistics in IFU "
328 cpl_error_get_message());
331 cpl_ensure_code(trimmed, cpl_error_get_code());
337 : CPL_ERROR_ILLEGAL_OUTPUT;
367 static cpl_error_code
372 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
375 if (strncmp(aProcessing->
inputTag, MUSE_TAG_BIAS, 5)) {
376 return CPL_ERROR_NONE;
378 if (aList->
size < 2) {
379 return CPL_ERROR_NONE;
381 double sigma = aBPars ? aBPars->
ovscsigma : 1.;
382 cpl_boolean ovscoffset = aBPars && aBPars->
overscan
383 && !strncmp(aBPars->
overscan,
"offset", 7);
387 cpl_propertylist *refheader = refimage->
header;
389 cpl_error_code rc = CPL_ERROR_NONE;
391 for (n = 1; n <= 4; n++) {
393 char *keywordmean = cpl_sprintf(MUSE_HDR_OVSC_MEAN, n),
394 *keywordstdev = cpl_sprintf(MUSE_HDR_OVSC_STDEV, n);
397 const char *reffn = cpl_propertylist_get_string(refheader, MUSE_HDR_TMP_FN);
398 float refmean = cpl_propertylist_get_float(refheader, keywordmean),
399 refstdev = cpl_propertylist_get_float(refheader, keywordstdev),
400 hilimit = refmean + sigma * refstdev,
401 lolimit = refmean - sigma * refstdev;
403 double summean = refmean,
404 sumstdev = pow(refstdev, 2.);
408 for (k = 1; k < aList->
size; k++) {
414 float mean = cpl_propertylist_get_float(h, keywordmean),
415 stdev = cpl_propertylist_get_float(h, keywordstdev);
417 sumstdev += pow(stdev, 2.) + pow(mean - refmean, 2.);
418 const char *fn = cpl_propertylist_get_string(h, MUSE_HDR_TMP_FN);
419 if (mean > hilimit || mean < lolimit) {
420 cpl_msg_error(__func__,
"Overscan of IFU %hhu, quadrant %1hhu of image %u [%s] "
421 "(%.3f+/-%.3f) differs from first image [%s] (%.3f+/-%.3f)!",
422 ifu, n, k+1, fn, mean, stdev, reffn, refmean, refstdev);
423 rc = cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
426 cpl_msg_debug(__func__,
"Overscan of IFU %hhu, quadrant %1hhu of image %u [%s] "
427 "%.3f+/-%.3f (first image [%s] %.3f+/-%.3f)",
428 ifu, n, k+1, fn, mean, stdev, reffn, refmean, refstdev);
433 summean /= aList->
size;
434 sumstdev = sqrt(sumstdev) / aList->
size;
435 cpl_msg_debug(__func__,
"Averaged overscan values in IFU %hhu, quadrant %1hhu: "
436 "%.3f +/- %.3f (%d images)", ifu, n, summean, sumstdev, aList->
size);
437 cpl_propertylist_update_float(refheader, keywordmean, summean);
438 cpl_propertylist_update_float(refheader, keywordstdev, sumstdev);
440 cpl_free(keywordmean);
441 cpl_free(keywordstdev);
466 static cpl_error_code
470 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
471 cpl_table *table =
muse_table_load(aProcessing, MUSE_TAG_BADPIX_TABLE, aIFU);
473 return CPL_ERROR_NONE;
476 if (rc != CPL_ERROR_NONE) {
477 cpl_table_delete(table);
478 return CPL_ERROR_INCOMPATIBLE_INPUT;
481 int nrow = cpl_table_get_nrow(table);
482 unsigned int k, nbadpix = 0;
483 for (k = 0; k < aList->
size && rc == CPL_ERROR_NONE; k++) {
487 for (i = 0; i < nrow; i++) {
488 int x = (int)cpl_table_get(table, MUSE_BADPIX_X, i, NULL),
489 y = (int)cpl_table_get(table, MUSE_BADPIX_Y, i, NULL),
490 dq = (int)cpl_table_get(table, MUSE_BADPIX_DQ, i, NULL),
493 rc = cpl_image_set(image->
dq, x, y,
494 dq | (
int)cpl_image_get(image->
dq, x, y, &err));
495 if (k == 0 && rc == CPL_ERROR_NONE) {
500 cpl_table_delete(table);
501 cpl_msg_debug(__func__,
"Applied %u bad pixels from %s in IFU %hhu.", nbadpix,
502 MUSE_TAG_BADPIX_TABLE, aIFU);
516 static cpl_error_code
519 cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
521 for (k = 0; k < aList->
size; k++) {
526 int npix = cpl_image_get_size_x(image->
data)
527 * cpl_image_get_size_y(image->
data);
528 if (nsaturated > (0.1 * npix)) {
529 const char *fn = cpl_propertylist_get_string(image->
header,
531 cpl_msg_warning(__func__,
"Raw exposure %u [%s] is probably saturated "
532 "in IFU %hhu (%d of %d pixels)!", k+1, fn, ifu,
535 cpl_msg_debug(__func__,
"Raw exposure %u in IFU %hhu (%d of %d pixels "
536 "saturated)!", k+1, ifu, nsaturated, npix);
538 cpl_propertylist_update_int(image->
header, MUSE_HDR_TMP_NSAT, nsaturated);
540 return CPL_ERROR_NONE;
566 static cpl_error_code
570 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
572 MUSE_TAG_MASTER_BIAS, aIFU);
573 cpl_errorstate prestate = cpl_errorstate_get();
574 if (!biasframe)
return CPL_ERROR_NONE;
575 cpl_errorstate_set(prestate);
576 const char *biasname = cpl_frame_get_filename(biasframe);
581 char *errmsg = cpl_strdup(cpl_error_get_message());
582 cpl_errorstate_set(prestate);
587 cpl_msg_error(__func__,
"%s", errmsg);
588 cpl_msg_error(__func__,
"%s", cpl_error_get_message());
590 cpl_frame_delete(biasframe);
591 return cpl_error_get_code();
594 cpl_msg_info(__func__,
"Bias correction in IFU %hhu using \"%s[CHAN%02hhu."
595 "DATA]\"", aIFU, biasname, aIFU);
597 cpl_msg_info(__func__,
"Bias correction in IFU %hhu using \"%s[DATA]\"",
601 cpl_propertylist_append_string(biasimage->
header, MUSE_HDR_TMP_FN, biasname);
603 cpl_boolean ovscoffset = aBPars && aBPars->
overscan
604 && !strncmp(aBPars->
overscan,
"offset", 7),
605 ovscvpoly = aBPars && aBPars->
overscan
606 && !strncmp(aBPars->
overscan,
"vpoly", 5);
608 cpl_error_code rc = CPL_ERROR_NONE;
610 for (k = 0; k < aList->
size && rc == CPL_ERROR_NONE; k++) {
612 rc = muse_basicproc_verify_setup(image, biasimage);
613 if (rc != CPL_ERROR_NONE) {
619 }
else if (ovscvpoly) {
625 cpl_msg_error(__func__,
"Very different overscan levels found between "
626 "%s and raw exposure %u in IFU %hhu: incompatible overscan"
627 " parameters?", MUSE_TAG_MASTER_BIAS, k + 1, aIFU);
628 rc = cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
637 #if GENERATE_TEST_IMAGES
668 static cpl_error_code
673 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
675 if (strncmp(aProcessing->
inputTag, MUSE_TAG_BIAS, 5)) {
676 return CPL_ERROR_NONE;
678 cpl_boolean ovscoffset = aBPars && aBPars->
overscan
679 && !strncmp(aBPars->
overscan,
"offset", 7);
681 return CPL_ERROR_NONE;
684 cpl_msg_info(__func__,
"Running overscan correction on %u %s files in IFU"
685 " %hhu", aList->
size, MUSE_TAG_BIAS, ifu);
688 for (k = 1; k < aList->
size; k++) {
691 return CPL_ERROR_NONE;
714 static cpl_error_code
718 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
720 if (strncmp(aProcessing->
inputTag, MUSE_TAG_FLAT, 5)) {
721 return CPL_ERROR_NONE;
724 CPL_ERROR_INCOMPATIBLE_INPUT);
727 MUSE_TAG_MASTER_BIAS, aIFU);
730 return CPL_ERROR_NONE;
732 const char *biasname = cpl_frame_get_filename(fbias);
733 cpl_propertylist *hbias = cpl_propertylist_load(biasname, 0);
734 if (!cpl_propertylist_has(hbias, QC_BIAS_MASTER_RON)) {
735 cpl_propertylist_delete(hbias);
737 char *extname = cpl_sprintf(
"CHAN%02hhu.%s", aIFU, EXTNAME_DATA);
738 int extension = cpl_fits_find_extension(biasname, extname);
739 hbias = cpl_propertylist_load(biasname, extension);
742 cpl_frame_delete(fbias);
746 cpl_image *diff = cpl_image_subtract_create(f1, f2);
749 for (n = 1; n <= 4; n++) {
751 double m1 = cpl_image_get_mean_window(f1, w[0], w[2], w[1], w[3]),
752 m2 = cpl_image_get_mean_window(f2, w[0], w[2], w[1], w[3]),
753 sf = cpl_image_get_stdev_window(diff, w[0], w[2], w[1], w[3]);
754 char *keyword = cpl_sprintf(QC_BIAS_MASTERn_PREFIX
" MEAN", n);
755 float mb = cpl_propertylist_get_float(hbias, keyword);
757 keyword = cpl_sprintf(QC_BIAS_MASTER_RON, n);
760 sb = cpl_propertylist_get_float(hbias, keyword) * sqrt(2.)
763 gain = (m1 + m2 - 2*mb) / (sf*sf - sb*sb),
764 dgain = 1. - gain / gainheader;
767 cpl_msg_warning(__func__,
"IFU %hhu, quadrant %1hhu: estimated gain %.3f "
768 "count/adu but header gives %.3f!", aIFU, n, gain,
771 cpl_msg_info(__func__,
"IFU %hhu, quadrant %1hhu: estimated gain %.3f "
772 "count/adu (header %.3f, delta %.3f)", aIFU, n, gain,
779 cpl_image_delete(diff);
780 cpl_propertylist_delete(hbias);
782 return CPL_ERROR_NONE;
800 static cpl_error_code
803 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
805 if (!strncmp(aProcessing->
inputTag, MUSE_TAG_BIAS, 5)) {
806 return CPL_ERROR_NONE;
810 cpl_msg_info(__func__,
"Converting %u exposures from adu to count (= electron)"
811 " units in IFU %hhu", aList->
size, ifu);
812 cpl_error_code rc = CPL_ERROR_NONE;
814 for (k = 0; k < aList->
size; k++) {
837 static cpl_error_code
842 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
844 if (!strncmp(aProcessing->
inputTag, MUSE_TAG_BIAS, 5)) {
845 return CPL_ERROR_NONE;
849 MUSE_TAG_NONLINCORR, aIFU);
852 return CPL_ERROR_NONE;
854 const char *fn = cpl_frame_get_filename(fnonlincorr);
856 char *extname = cpl_sprintf(
"CHAN%02hhu", aIFU);
857 int extension = cpl_fits_find_extension(fn, extname);
858 cpl_propertylist *header = cpl_propertylist_load(fn, extension);
859 cpl_msg_info(__func__,
"Correcting nonlinearity in IFU %hhu using \"%s[%s]\"",
864 cpl_error_code rc = CPL_ERROR_NONE;
866 for (k = 0; k < aList->
size; k++) {
868 int nx = cpl_image_get_size_x(image->
data);
869 float *data = cpl_image_get_data_float(image->
data),
870 *stat = cpl_image_get_data_float(image->
stat);
875 for (n = 1; n <= 4; n++) {
878 cpl_polynomial *poly = cpl_polynomial_new(1);
879 char *kw = cpl_sprintf(MUSE_HDR_NONLINn_ORDER, n);
880 unsigned char o, order = cpl_propertylist_get_int(header, kw);
882 for (o = 0; o <= order; o++) {
883 kw = cpl_sprintf(MUSE_HDR_NONLINn_COEFFo, n, o);
885 cpl_polynomial_set_coeff(poly, &pow,
886 cpl_propertylist_get_double(header, kw));
890 cpl_msg_debug(__func__,
"polynomial correction (200..65000 adu): %f, %f, %f, %f",
891 cpl_polynomial_eval_1d(poly, log10(200.), NULL),
892 cpl_polynomial_eval_1d(poly, log10(2000.), NULL),
893 cpl_polynomial_eval_1d(poly, log10(20000.), NULL),
894 cpl_polynomial_eval_1d(poly, log10(65000.), NULL));
895 cpl_polynomial_dump(poly, stdout);
902 for (i = w[0] - 1; i < w[1]; i++) {
904 for (j = w[2] - 1; j < w[3]; j++) {
905 if (data[i + j*nx] <= 0) {
909 double pcor = cpl_polynomial_eval_1d(poly, log10(data[i + j*nx]), NULL);
911 double fcor = 1. / (1. + pcor);
912 data[i + j*nx] *= fcor;
913 stat[i + j*nx] *= fcor*fcor;
917 cpl_polynomial_delete(poly);
920 cpl_propertylist_delete(header);
942 static cpl_error_code
946 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
949 MUSE_TAG_MASTER_DARK, aIFU);
950 cpl_errorstate prestate = cpl_errorstate_get();
951 if (!darkframe)
return CPL_ERROR_NONE;
952 cpl_errorstate_set(prestate);
953 const char *darkname = cpl_frame_get_filename(darkframe);
956 char *errmsg = cpl_strdup(cpl_error_get_message());
957 cpl_errorstate_set(prestate);
960 cpl_msg_error(__func__,
"%s", errmsg);
961 cpl_msg_error(__func__,
"%s", cpl_error_get_message());
963 cpl_frame_delete(darkframe);
964 return cpl_error_get_code();
967 cpl_msg_info(__func__,
"Dark correction in IFU %hhu using \"%s[CHAN%02hhu."
968 "DATA]\"", aIFU, darkname, aIFU);
970 cpl_msg_info(__func__,
"Dark correction in IFU %hhu using \"%s[DATA]\"",
973 cpl_propertylist_append_string(darkimage->
header, MUSE_HDR_TMP_FN, darkname);
976 cpl_error_code rc = CPL_ERROR_NONE;
978 for (k = 0; k < aList->
size; k++) {
980 rc = muse_basicproc_verify_setup(image, darkimage);
981 if (rc != CPL_ERROR_NONE) {
1019 static cpl_error_code
1023 cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
1027 cpl_boolean isdcr = aBPars && aBPars->
crmethod
1028 && !strncmp(aBPars->
crmethod,
"dcr", 4);
1030 return CPL_ERROR_NONE;
1033 cpl_error_code rc = CPL_ERROR_NONE;
1035 for (k = 0; k < aList->
size; k++) {
1042 cpl_msg_error(__func__,
"Cosmic ray rejection using DCR in IFU %hhu failed"
1043 " for image %u (ncr = %d): %s", ifu, k+1, ncr,
1044 cpl_error_get_message());
1045 rc = cpl_error_get_code();
1047 cpl_msg_info(__func__,
"Cosmic ray rejection using DCR in IFU %hhu found "
1048 "%d affected pixels in image %u", ifu, ncr, k+1);
1071 static cpl_error_code
1075 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
1078 MUSE_TAG_MASTER_FLAT, aIFU);
1079 cpl_errorstate prestate = cpl_errorstate_get();
1080 if (!flatframe)
return CPL_ERROR_NONE;
1081 cpl_errorstate_set(prestate);
1082 const char *flatname = cpl_frame_get_filename(flatframe);
1083 prestate = cpl_errorstate_get();
1086 char *errmsg = cpl_strdup(cpl_error_get_message());
1087 cpl_errorstate_set(prestate);
1090 cpl_msg_error(__func__,
"%s", errmsg);
1091 cpl_msg_error(__func__,
"%s", cpl_error_get_message());
1093 cpl_frame_delete(flatframe);
1094 return cpl_error_get_code();
1097 cpl_msg_info(__func__,
"Flat-field correction in IFU %hhu using \"%s[CHAN%02hhu."
1098 "DATA]\"", aIFU, flatname, aIFU);
1100 cpl_msg_info(__func__,
"Flat-field correction in IFU %hhu using \"%s[DATA]\"",
1104 double fflux = cpl_propertylist_get_double(flatimage->
header,
1105 QC_FLAT_MASTER_INTFLUX);
1106 cpl_propertylist_append_string(flatimage->
header, MUSE_HDR_TMP_FN, flatname);
1109 cpl_error_code rc = CPL_ERROR_NONE;
1111 for (k = 0; k < aList->
size; k++) {
1112 cpl_msg_debug(__func__,
"Flat-field division in IFU %hhu of image %u of %u",
1113 aIFU, k+1, aList->
size);
1115 rc = muse_basicproc_verify_setup(image, flatimage);
1116 if (rc != CPL_ERROR_NONE) {
1120 cpl_propertylist_update_double(image->
header, MUSE_HDR_FLAT_FLUX_LAMP, fflux);
1144 static cpl_error_code
1148 cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
1151 MUSE_TAG_MASTER_SKYFLAT, aIFU);
1152 cpl_errorstate prestate = cpl_errorstate_get();
1153 if (!flatframe)
return CPL_ERROR_NONE;
1154 cpl_errorstate_set(prestate);
1155 const char *flatname = cpl_frame_get_filename(flatframe);
1158 cpl_propertylist *hflat = cpl_propertylist_load(flatname, 0);
1159 if (!cpl_propertylist_has(hflat, QC_SKYFLAT_MASTER_INTFLUX)) {
1160 cpl_propertylist_delete(hflat);
1162 char *extname = cpl_sprintf(
"CHAN%02hhu.%s", aIFU, EXTNAME_DATA);
1163 int extension = cpl_fits_find_extension(flatname, extname);
1164 hflat = cpl_propertylist_load(flatname, extension);
1165 cpl_msg_info(__func__,
"Propagate SkyFlat flux value in IFU %hhu from "
1166 "\"%s[%s]\"", aIFU, flatname, extname);
1169 cpl_msg_info(__func__,
"Propagate SkyFlat flux value in IFU %hhu from "
1170 "\"%s\"", aIFU, flatname);
1172 double fflux = cpl_propertylist_get_double(hflat, QC_SKYFLAT_MASTER_INTFLUX);
1173 cpl_propertylist_delete(hflat);
1176 cpl_error_code rc = CPL_ERROR_NONE;
1178 for (k = 0; k < aList->
size; k++) {
1179 cpl_msg_debug(__func__,
"SkyFlat propagation in IFU %hhu to image %u of %u",
1180 aIFU, k+1, aList->
size);
1183 cpl_propertylist_update_double(image->
header, MUSE_HDR_FLAT_FLUX_SKY, fflux);
1211 muse_basicproc_load_raw(
muse_processing *aProcessing,
unsigned char aIFU)
1213 cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
1219 cpl_ensure(usedFrames, CPL_ERROR_NULL_INPUT, NULL);
1223 cpl_size iframe, nframes = cpl_frameset_get_size(usedFrames);
1224 for (iframe = 0; iframe < nframes; iframe++) {
1225 cpl_frame *frame = cpl_frameset_get_position(usedFrames, iframe);
1226 const char *fileName = cpl_frame_get_filename(frame);
1228 if (extension == -1) {
1229 cpl_msg_error(__func__,
"\"%s\" does not contain data from IFU %hhu",
1238 cpl_propertylist_append_string(raw->
header, MUSE_HDR_TMP_FN, fileName);
1244 cpl_frameset_delete(usedFrames);
1247 if (!images->
size) {
1248 if (cpl_error_get_code() != MUSE_ERROR_CHIP_NOT_LIVE) {
1249 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
1250 cpl_msg_error(__func__,
"No raw images loaded for IFU %hhu", aIFU);
1256 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
1257 cpl_msg_error(__func__,
"Non-uniform imagelist for IFU %hhu", aIFU);
1314 muse_imagelist *images = muse_basicproc_load_raw(aProcessing, aIFU);
1318 cpl_errorstate prestate = cpl_errorstate_get();
1319 if (muse_basicproc_apply_badpix(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1320 cpl_msg_warning(__func__,
"Applying bad pixel table to IFU %hhu failed: %s",
1321 aIFU, cpl_error_get_message());
1322 cpl_errorstate_set(prestate);
1324 if (muse_basicproc_check_saturation(images) != CPL_ERROR_NONE) {
1328 if (muse_basicproc_correct_overscans_vpoly(images, aBPars)
1329 != CPL_ERROR_NONE) {
1333 if (muse_basicproc_trim_images(images, aBPars) != CPL_ERROR_NONE) {
1337 if (muse_basicproc_check_overscans(images, aProcessing, aBPars)
1338 != CPL_ERROR_NONE) {
1343 muse_basicproc_check_gain(images, aProcessing, aIFU);
1344 if (muse_basicproc_apply_bias(images, aProcessing, aIFU, aBPars)
1345 != CPL_ERROR_NONE) {
1349 if (muse_basicproc_correct_overscans_offset(images, aProcessing, aBPars)
1350 != CPL_ERROR_NONE) {
1353 cpl_msg_warning(__func__,
"Bias-level correction using overscans failed in "
1354 "IFU %hhu: %s", aIFU, cpl_error_get_message());
1356 if (muse_basicproc_corr_nonlinearity(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1360 if (muse_basicproc_adu_to_count(images, aProcessing) != CPL_ERROR_NONE) {
1364 if (muse_basicproc_apply_dark(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1368 if (muse_basicproc_apply_cr(images, aBPars) != CPL_ERROR_NONE) {
1372 if (muse_basicproc_apply_flat(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1376 if (muse_basicproc_apply_skyflat(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1404 cpl_size iframe, nframes = cpl_frameset_get_size(usedFrames);
1405 for (iframe = 0; iframe < nframes; iframe++) {
1406 cpl_frame *frame = cpl_frameset_get_position(usedFrames, iframe);
1407 cpl_errorstate prestate = cpl_errorstate_get();
1408 const char *imagename = cpl_frame_get_filename(frame);
1411 cpl_errorstate_set(prestate);
1417 cpl_frameset_delete(usedFrames);
1449 muse_basicproc_combine_compare_lamp(
const cpl_frame *aFrame1,
const cpl_frame *aFrame2)
1451 cpl_ensure(aFrame1 && aFrame2, CPL_ERROR_NULL_INPUT, -1);
1452 const char *fn1 = cpl_frame_get_filename(aFrame1),
1453 *fn2 = cpl_frame_get_filename(aFrame2);
1454 cpl_propertylist *head1 = cpl_propertylist_load(fn1, 0),
1455 *head2 = cpl_propertylist_load(fn2, 0);
1456 if (!head1 || !head2) {
1457 cpl_propertylist_delete(head1);
1458 cpl_propertylist_delete(head2);
1465 int nlamp = 1, status1, status2;
1466 cpl_errorstate prestate = cpl_errorstate_get();
1471 cpl_errorstate_set(prestate);
1472 if (name1 && name2 && strncmp(name1, name2, strlen(name1) + 1)) {
1473 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
1474 "Files \"%s\" and \"%s\" have incompatible lamp "
1475 "setups", fn1, fn2);
1476 cpl_propertylist_delete(head1);
1477 cpl_propertylist_delete(head2);
1482 if (name1 && name2 && strncmp(name1, name2, strlen(name1) + 1)) {
1483 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
1484 "Files \"%s\" and \"%s\" have incompatible shutter "
1485 "setups", fn1, fn2);
1486 cpl_propertylist_delete(head1);
1487 cpl_propertylist_delete(head2);
1493 cpl_errorstate_set(prestate);
1494 if (status1 != status2) {
1499 if (status1 != status2) {
1503 }
while (cpl_errorstate_is_equal(prestate));
1504 cpl_errorstate_set(prestate);
1506 cpl_propertylist_delete(head1);
1507 cpl_propertylist_delete(head2);
1508 return status1 == status2;
1542 cpl_frameset ***aLabeledFrames)
1544 if (aLabeledFrames) {
1545 *aLabeledFrames = NULL;
1547 cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
1553 char *prefix = cpl_sprintf(
"muse.%s", aProcessing->
recipeName);
1558 printf(
"rawframes\n");
1559 cpl_frameset_dump(rawframes, stdout);
1563 *labels = cpl_frameset_labelise(rawframes,
1564 muse_basicproc_combine_compare_lamp,
1566 if (!labels || nlabels <= 1) {
1569 cpl_frameset_delete(rawframes);
1576 if (aLabeledFrames) {
1577 *aLabeledFrames = cpl_calloc(1,
sizeof(cpl_frameset *));
1578 (*aLabeledFrames)[0] = cpl_frameset_duplicate(aProcessing->
usedFrames);
1587 cpl_array *alabels = cpl_array_wrap_int(labels, cpl_frameset_get_size(rawframes));
1588 cpl_array_dump(alabels, 0, 1000, stdout);
1590 cpl_array_unwrap(alabels);
1594 if (aLabeledFrames) {
1595 *aLabeledFrames = cpl_calloc(nlabels,
sizeof(cpl_frameset *));
1607 int ilabel, ipos = 0;
1608 for (ilabel = 0; ilabel < nlabels; ilabel++) {
1610 cpl_frameset *frames = cpl_frameset_extract(rawframes, labels, ilabel);
1612 cpl_frameset_join(frames, auxframes);
1621 cpl_frameset_delete(frames);
1626 if (aLabeledFrames) {
1627 cpl_free(*aLabeledFrames);
1628 *aLabeledFrames = NULL;
1635 cpl_msg_error(__func__,
"Image combination failed for IFU %hhu for lamp "
1636 "with label %d of %"CPL_SIZE_FORMAT, aIFU, ilabel + 1, nlabels);
1638 cpl_frameset_delete(frames);
1642 if (aLabeledFrames) {
1644 cpl_size iframe, nframes = cpl_frameset_get_size(frames);
1645 for (iframe = 0; iframe < nframes; iframe++) {
1646 cpl_frame *frame = cpl_frameset_get_position(frames, iframe);
1647 const char *fn = cpl_frame_get_filename(frame),
1648 *tag = cpl_frame_get_tag(frame);
1649 cpl_size iuframe, nuframes = cpl_frameset_get_size(aProcessing->
usedFrames);
1651 (iuframe < nuframes) && fn && tag;
1653 cpl_frame *uframe = cpl_frameset_get_position(aProcessing->
usedFrames,
1655 const char *fnu = cpl_frame_get_filename(uframe),
1656 *tagu = cpl_frame_get_tag(uframe);
1657 if (fnu && !strncmp(fn, fnu, strlen(fn) + 1) &&
1658 tagu && !strncmp(tag, tagu, strlen(tag) + 1)) {
1659 cpl_frame_set_group(frame, cpl_frame_get_group(uframe));
1664 (*aLabeledFrames)[ipos] = frames;
1666 cpl_frameset_delete(frames);
1672 char *keyword = cpl_sprintf(QC_WAVECAL_PREFIXi
" "QC_BASIC_NSATURATED, k+1);
1675 cpl_propertylist_update_int(lampimage->
header, keyword, nsaturated);
1685 cpl_frameset_delete(rawframes);
1686 cpl_frameset_delete(auxframes);
1691 if (aLabeledFrames) {
1692 cpl_free(*aLabeledFrames);
1693 *aLabeledFrames = NULL;
1722 double aHalfWidth,
double aBinWidth)
1724 cpl_ensure_code(aPt && aLines, CPL_ERROR_NULL_INPUT);
1725 cpl_ensure_code(cpl_array_get_type(aLines) == CPL_TYPE_DOUBLE ||
1726 cpl_array_get_type(aLines) == CPL_TYPE_FLOAT,
1727 CPL_ERROR_ILLEGAL_INPUT);
1731 double shift = 0., wshift = 0.;
1732 cpl_array *errors = cpl_array_new(4, CPL_TYPE_DOUBLE);
1733 int i, n = cpl_array_get_size(aLines), nvalid = 0;
1734 for (i = 0; i < n; i++) {
1736 double lambda = cpl_array_get(aLines, i, &err);
1737 if (err || lambda >= lmax || lambda <= lmin) {
1738 cpl_msg_debug(__func__,
"Invalid wavelength at position %d of %d in "
1739 "skylines", i + 1, n);
1744 aBinWidth, NULL, errors),
1745 cerr = cpl_array_get_double(errors, 0, NULL);
1746 shift += (lambda - center) / cerr;
1747 wshift += 1. / cerr;
1748 cpl_msg_debug(__func__,
"dlambda = %.4f +/- %.4f (for skyline at %.4f "
1749 "Angstrom)", lambda - center, cerr, lambda);
1751 cpl_array_delete(errors);
1753 if (nvalid > 0 && isfinite(shift)) {
1754 cpl_msg_info(__func__,
"Skyline correction (%d lines): shifting data of IFU "
1757 cpl_table_add_scalar(aPt->
table, MUSE_PIXTABLE_LAMBDA, shift);
1758 cpl_propertylist_update_float(aPt->
header, QC_SCIBASIC_SHIFT, shift);
1760 cpl_propertylist_update_float(aPt->
header, QC_SCIBASIC_SHIFT, 0.);
1762 return CPL_ERROR_NONE;
1780 const char *aPrefix,
unsigned aStats)
1782 cpl_ensure_code(aImage, CPL_ERROR_NULL_INPUT);
1784 int nx = cpl_image_get_size_x(aImage),
1785 ny = cpl_image_get_size_y(aImage);
1787 aStats, 1, 1, nx, ny);
1810 cpl_propertylist *aHeader,
1811 const char *aPrefix,
unsigned aStats,
1812 int aX1,
int aY1,
int aX2,
int aY2)
1814 cpl_ensure_code(aImage && aHeader, CPL_ERROR_NULL_INPUT);
1815 cpl_ensure_code(aPrefix, CPL_ERROR_ILLEGAL_INPUT);
1816 cpl_ensure_code(aPrefix, CPL_ERROR_ILLEGAL_INPUT);
1818 cpl_stats *stats = cpl_stats_new_from_image_window(aImage, aStats,
1819 aX1, aY1, aX2, aY2);
1821 return cpl_error_get_code();
1824 char keyword[KEYWORD_LENGTH];
1825 if (aStats & CPL_STATS_MEDIAN) {
1826 snprintf(keyword, KEYWORD_LENGTH,
"%s MEDIAN", aPrefix);
1827 cpl_propertylist_append_float(aHeader, keyword,
1828 cpl_stats_get_median(stats));
1830 if (aStats & CPL_STATS_MEAN) {
1831 snprintf(keyword, KEYWORD_LENGTH,
"%s MEAN", aPrefix);
1832 cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_mean(stats));
1834 if (aStats & CPL_STATS_STDEV) {
1835 snprintf(keyword, KEYWORD_LENGTH,
"%s STDEV", aPrefix);
1836 cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_stdev(stats));
1838 if (aStats & CPL_STATS_MIN) {
1839 snprintf(keyword, KEYWORD_LENGTH,
"%s MIN", aPrefix);
1840 cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_min(stats));
1842 if (aStats & CPL_STATS_MAX) {
1843 snprintf(keyword, KEYWORD_LENGTH,
"%s MAX", aPrefix);
1844 cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_max(stats));
1846 if (aStats & CPL_STATS_FLUX) {
1847 snprintf(keyword, KEYWORD_LENGTH,
"%s INTFLUX", aPrefix);
1848 cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_flux(stats));
1851 cpl_stats_delete(stats);
1853 return CPL_ERROR_NONE;
1875 cpl_ensure_code(aImage && aImage->
dq && aImage->
header && aPrefix,
1876 CPL_ERROR_NULL_INPUT);
1878 cpl_mask *mask = cpl_mask_threshold_image_create(aImage->
dq,
1879 EURO3D_SATURATED - 0.1,
1880 EURO3D_SATURATED + 0.1);
1881 int nsaturated = cpl_mask_count(mask);
1882 cpl_mask_delete(mask);
1884 char *keyword = NULL;
1885 if (aPrefix[strlen(aPrefix)-1] ==
' ') {
1886 keyword = cpl_sprintf(
"%s%s", aPrefix, QC_BASIC_NSATURATED);
1888 keyword = cpl_sprintf(
"%s %s", aPrefix, QC_BASIC_NSATURATED);
1890 cpl_error_code rc = cpl_propertylist_update_int(aImage->
header, keyword,
cpl_error_code muse_quadrants_overscan_correct(muse_image *aImage, muse_image *aRefImage)
Adapt bias level to reference image using overscan statistics.
muse_imagelist * muse_basicproc_load(muse_processing *aProcessing, unsigned char aIFU, muse_basicproc_params *aBPars)
Load the raw input files from disk and do basic processing.
Structure definition for a collection of muse_images.
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
int muse_image_divide(muse_image *aImage, muse_image *aDivisor)
Divide a muse_image by another with correct treatment of bad pixels and variance. ...
cpl_size * muse_quadrants_get_window(const muse_image *aImage, unsigned char aQuadrant)
Determine the data window of a given quadrant on the CCD.
int muse_image_scale(muse_image *aImage, double aScale)
Scale a muse_image with correct treatment of variance.
int muse_pfits_get_read_id(const cpl_propertylist *aHeaders)
find out the readout mode id
int muse_utils_get_extension_for_ifu(const char *aFilename, unsigned char aIFU)
Return extension number that corresponds to this IFU/channel number.
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
muse_imagelist * muse_basicproc_combine_images_lampwise(muse_processing *aProcessing, unsigned char aIFU, muse_basicproc_params *aBPars, cpl_frameset ***aLabeledFrames)
Combine several images into a lampwise image list.
double muse_utils_pixtable_fit_line_gaussian(muse_pixtable *aPixtable, double aLambda, double aHalfWidth, double aBinSize, cpl_array *aResults, cpl_array *aErrors)
Fit a 1D Gaussian to a given wavelength range in a pixel table.
double muse_pfits_get_gain(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find the detector gain (in units of count/adu)
cpl_image * data
the data extension
muse_image * muse_image_load_from_raw(const char *aFilename, int aExtension)
Load raw image into the data extension of a MUSE image.
int muse_pfits_get_shut_status(const cpl_propertylist *aHeaders, int aShutter)
query the status of one shutter
#define MUSE_HDR_PT_LHI
FITS header keyword contains the upper limit of the data in spectral direction.
int muse_cosmics_dcr(muse_image *aImage, unsigned int aXBox, unsigned int aYBox, unsigned int aPasses, float aThres)
Quickly mark cosmic rays in an image using the DCR algorithm.
muse_image * muse_image_duplicate(const muse_image *aImage)
Duplicate the three image extensions and the FITS headers of a MUSE image.
cpl_image * stat
the statistics extension
int muse_image_subtract(muse_image *aImage, muse_image *aSubtract)
Subtract a muse_image from another with correct treatment of bad pixels and variance.
void muse_imagelist_delete(muse_imagelist *aList)
Free the memory of the MUSE image list.
muse_imagelist * muse_basicproc_load_reduced(muse_processing *aProcessing, unsigned char aIFU)
Load reduced input files from disk.
muse_basicproc_params * muse_basicproc_params_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new structure of basic processing parameters.
cpl_frameset * muse_frameset_check_raw(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU)
return frameset containing good raw input data
muse_image * muse_combine_images(muse_combinepar *aCPars, muse_imagelist *aImages)
Combine several images into one.
Structure definition of MUSE three extension FITS file.
cpl_error_code muse_quadrants_overscan_polyfit_vertical(muse_image *aImage, unsigned aIgnore, unsigned char aOrder, double aSigma, const double aFRMS, double aFChiSq)
Correct quadrants by polynomial representation of vertical overscan.
const char * muse_pfits_get_shut_name(const cpl_propertylist *aHeaders, int aShutter)
query the name of one shutter
cpl_table * table
The pixel table.
void muse_basicproc_params_delete(muse_basicproc_params *aBPars)
Free a structure of basic processing parameters.
cpl_propertylist * header
the FITS header
int muse_image_variance_create(muse_image *aImage, muse_image *aBias)
Create the photon noise-based variance in the stat extension.
cpl_error_code muse_cpltable_check(const cpl_table *aTable, const muse_cpltable_def *aDef)
Check whether the table contains the fields of the definition.
unsigned int muse_imagelist_get_size(muse_imagelist *aList)
Return the number of stored images.
void muse_combinepar_delete(muse_combinepar *aCPars)
Clear the combination parameters.
cpl_image * dq
the data quality extension
int muse_pfits_get_biny(const cpl_propertylist *aHeaders)
find out the binning factor in y direction
Structure definition of MUSE pixel table.
cpl_boolean muse_quadrants_overscan_check(muse_image *aImage, muse_image *aRefImage, double aSigma)
Compare overscan statistics of all quadrants to those of reference image.
const char * muse_pfits_get_lamp_name(const cpl_propertylist *aHeaders, int aLamp)
query the name of one lamp
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
cpl_error_code muse_basicproc_stats_append_header_window(cpl_image *aImage, cpl_propertylist *aHeader, const char *aPrefix, unsigned aStats, int aX1, int aY1, int aX2, int aY2)
Compute image statistics of an image window and add them to a header.
int muse_quality_set_saturated(muse_image *aImage)
Set all pixels above the saturation limit in the bad pixel image.
const char * muse_pfits_get_read_name(const cpl_propertylist *aHeaders)
find out the readout mode name
int muse_imagelist_is_uniform(muse_imagelist *aList)
Check that all images in the muse_imagelist have the same size.
muse_combinepar * muse_combinepar_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new set of combination parameters.
void muse_processing_append_used(muse_processing *aProcessing, cpl_frame *aFrame, cpl_frame_group aGroup, int aDuplicate)
Add a frame to the set of used frames.
#define MUSE_HDR_PT_LLO
FITS header keyword contains the lower limit of the data in spectral direction.
const char * muse_pfits_get_chip_id(const cpl_propertylist *aHeaders)
find out the chip id
cpl_error_code muse_basicproc_shift_pixtable(muse_pixtable *aPt, cpl_array *aLines, double aHalfWidth, double aBinWidth)
Compute wavelength corrections for science data based on reference sky lines.
cpl_error_code muse_quadrants_overscan_stats(muse_image *aImage, const char *aRejection, unsigned int aIgnore)
Compute overscan statistics of all quadrants and save in FITS header.
cpl_error_code muse_basicproc_stats_append_header(cpl_image *aImage, cpl_propertylist *aHeader, const char *aPrefix, unsigned aStats)
Compute image statistics of an image and add them to a header.
int muse_pfits_get_binx(const cpl_propertylist *aHeaders)
find out the binning factor in x direction
cpl_error_code muse_image_save(muse_image *aImage, const char *aFilename)
Save the three image extensions and the FITS headers of a MUSE image to a file.
muse_image * muse_image_load(const char *aFilename)
Load the three extensions and the FITS headers of a MUSE image from a file.
int muse_pfits_get_lamp_status(const cpl_propertylist *aHeaders, int aLamp)
query the status of one lamp
muse_imagelist * muse_imagelist_new(void)
Create a new (empty) MUSE image list.
double muse_pfits_get_exptime(const cpl_propertylist *aHeaders)
find out the exposure time
cpl_table * muse_table_load(muse_processing *aProcessing, const char *aTag, unsigned char aIFU)
load a table according to its tag and IFU/channel number
cpl_error_code muse_processing_check_input(muse_processing *aProcessing, unsigned char aIFU)
Check the input files for completeness.
Structure of basic processing parameters.
const char * muse_pfits_get_pro_catg(const cpl_propertylist *aHeaders)
find out the PRO category
cpl_error_code muse_image_adu_to_count(muse_image *aImage)
Convert the data units from raw adu to count (= electron) units.
cpl_frameset * muse_frameset_find(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU, cpl_boolean aInvert)
return frameset containing data from an IFU/channel with a certain tag
cpl_parameter * muse_cplparamerterlist_find_prefix(cpl_parameterlist *aParameters, const char *aPrefix, const char *aName)
Return the full recipe parameter belonging to prefix and shortname.
muse_image * muse_image_load_from_extensions(const char *aFilename, unsigned char aIFU)
Load the three extensions and the FITS headers of a MUSE image from extensions of a merged file...
cpl_frameset * inputFrames
cpl_frameset * usedFrames
cpl_error_code muse_imagelist_set(muse_imagelist *aList, muse_image *aImage, unsigned int aIdx)
Set the muse_image of given list index.
const char * muse_pfits_get_chip_name(const cpl_propertylist *aHeaders)
find out the chip name
cpl_frame * muse_frameset_find_master(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU)
find the master frame according to its CCD number and tag
muse_ins_mode muse_pfits_get_mode(const cpl_propertylist *aHeaders)
find out the observation mode
cpl_parameterlist * parameters
muse_image * muse_quadrants_trim_image(muse_image *aImage)
Trim the input image of pre- and over-scan regions of all quadrants.
cpl_propertylist * header
The FITS header.
cpl_error_code muse_basicproc_qc_saturated(muse_image *aImage, const char *aPrefix)
Add QC parameter about saturated pixels to a muse_image.