33 #include "muse_flux.h" 34 #include "muse_instrument.h" 36 #include "muse_astro.h" 37 #include "muse_cplwrappers.h" 38 #include "muse_pfits.h" 39 #include "muse_quality.h" 40 #include "muse_resampling.h" 41 #include "muse_utils.h" 95 aFluxObj->
cube = NULL;
123 cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, 0.);
124 cpl_table_unselect_all(aTable);
125 cpl_table_or_selected_double(aTable,
"lambda", CPL_NOT_LESS_THAN,
126 kMuseNominalLambdaMin);
127 cpl_table_and_selected_double(aTable,
"lambda", CPL_NOT_GREATER_THAN,
128 kMuseNominalLambdaMax);
129 cpl_size nsel = cpl_table_count_selected(aTable);
130 cpl_array *asel = cpl_table_where_selected(aTable);
131 cpl_size *sel = cpl_array_get_data_cplsize(asel);
132 double lmin = cpl_table_get_double(aTable,
"lambda", sel[0], NULL),
133 lmax = cpl_table_get_double(aTable,
"lambda", sel[nsel - 1], NULL);
134 cpl_array_delete(asel);
135 return (lmax - lmin) / nsel;
172 cpl_ensure_code(aTable, CPL_ERROR_NULL_INPUT);
174 const char *flam1 =
"erg/s/cm**2/Angstrom",
175 *flam2 =
"erg/s/cm^2/Angstrom";
177 cpl_error_code rc = CPL_ERROR_NONE;
178 cpl_errorstate prestate = cpl_errorstate_get();
180 if (cpl_table_has_column(aTable,
"lambda") &&
181 cpl_table_has_column(aTable,
"flux") &&
182 cpl_table_get_column_unit(aTable,
"lambda") &&
183 cpl_table_get_column_unit(aTable,
"flux") &&
184 !strncmp(cpl_table_get_column_unit(aTable,
"lambda"),
"Angstrom", 9) &&
185 (!strncmp(cpl_table_get_column_unit(aTable,
"flux"), flam1, strlen(flam1)) ||
186 !strncmp(cpl_table_get_column_unit(aTable,
"flux"), flam2, strlen(flam2)))) {
189 if (cpl_table_get_column_type(aTable,
"lambda") != CPL_TYPE_DOUBLE) {
190 cpl_msg_debug(__func__,
"Casting lambda column to double");
191 cpl_table_cast_column(aTable,
"lambda", NULL, CPL_TYPE_DOUBLE);
193 if (cpl_table_get_column_type(aTable,
"flux") != CPL_TYPE_DOUBLE) {
194 cpl_msg_debug(__func__,
"Casting flux column to double");
195 cpl_table_cast_column(aTable,
"flux", NULL, CPL_TYPE_DOUBLE);
198 if (cpl_table_has_column(aTable,
"fluxerr")) {
199 if (cpl_table_get_column_type(aTable,
"fluxerr") != CPL_TYPE_DOUBLE) {
200 cpl_msg_debug(__func__,
"Casting fluxerr column to double");
201 cpl_table_cast_column(aTable,
"fluxerr", NULL, CPL_TYPE_DOUBLE);
203 const char *unit = cpl_table_get_column_unit(aTable,
"fluxerr");
204 if (!unit || (strncmp(unit, flam1, strlen(flam1)) &&
205 strncmp(unit, flam2, strlen(flam2)))) {
206 cpl_msg_debug(__func__,
"Erasing fluxerr column because of unexpected " 208 cpl_table_erase_column(aTable,
"fluxerr");
211 cpl_msg_info(__func__,
"Found MUSE format, average sampling %.3f Angstrom/bin" 213 }
else if (cpl_table_has_column(aTable,
"WAVELENGTH") &&
214 cpl_table_has_column(aTable,
"FLUX") &&
215 cpl_table_get_column_unit(aTable,
"WAVELENGTH") &&
216 cpl_table_get_column_unit(aTable,
"FLUX") &&
217 !strncmp(cpl_table_get_column_unit(aTable,
"WAVELENGTH"),
"ANGSTROMS", 10) &&
218 !strncmp(cpl_table_get_column_unit(aTable,
"FLUX"),
"FLAM", 5)) {
220 printf(
"input HST CALSPEC table:\n");
221 cpl_table_dump_structure(aTable, stdout);
222 cpl_table_dump(aTable, cpl_table_get_nrow(aTable)/2, 3, stdout);
226 cpl_table_cast_column(aTable,
"WAVELENGTH",
"lambda", CPL_TYPE_DOUBLE);
227 cpl_table_cast_column(aTable,
"FLUX",
"flux", CPL_TYPE_DOUBLE);
228 cpl_table_erase_column(aTable,
"WAVELENGTH");
229 cpl_table_erase_column(aTable,
"FLUX");
230 cpl_table_set_column_unit(aTable,
"lambda",
"Angstrom");
231 cpl_table_set_column_unit(aTable,
"flux", flam1);
234 if (cpl_table_has_column(aTable,
"STATERROR") &&
235 cpl_table_has_column(aTable,
"SYSERROR") &&
236 cpl_table_get_column_unit(aTable,
"STATERROR") &&
237 cpl_table_get_column_unit(aTable,
"SYSERROR") &&
238 !strncmp(cpl_table_get_column_unit(aTable,
"STATERROR"),
"FLAM", 5) &&
239 !strncmp(cpl_table_get_column_unit(aTable,
"SYSERROR"),
"FLAM", 5)) {
242 cpl_table_cast_column(aTable,
"STATERROR",
"fluxerr", CPL_TYPE_DOUBLE);
243 cpl_table_erase_column(aTable,
"STATERROR");
244 cpl_table_cast_column(aTable,
"SYSERROR", NULL, CPL_TYPE_DOUBLE);
245 cpl_table_power_column(aTable,
"fluxerr", 2);
246 cpl_table_power_column(aTable,
"SYSERROR", 2);
247 cpl_table_add_columns(aTable,
"fluxerr",
"SYSERROR");
248 cpl_table_erase_column(aTable,
"SYSERROR");
249 cpl_table_power_column(aTable,
"fluxerr", 0.5);
250 cpl_table_set_column_unit(aTable,
"fluxerr", flam1);
257 if (cpl_table_has_column(aTable,
"FWHM")) {
258 cpl_table_erase_column(aTable,
"FWHM");
260 if (cpl_table_has_column(aTable,
"DATAQUAL")) {
261 cpl_table_erase_column(aTable,
"DATAQUAL");
263 if (cpl_table_has_column(aTable,
"TOTEXP")) {
264 cpl_table_erase_column(aTable,
"TOTEXP");
267 cpl_size irow, nrow = cpl_table_get_nrow(aTable);
268 for (irow = 0; irow < nrow; irow++) {
269 double lambda = cpl_table_get_double(aTable,
"lambda", irow, NULL);
270 cpl_table_set_double(aTable,
"lambda", irow,
274 printf(
"converted HST CALSPEC table:\n");
275 cpl_table_dump_structure(aTable, stdout);
276 cpl_table_dump(aTable, cpl_table_get_nrow(aTable)/2, 3, stdout);
279 cpl_msg_info(__func__,
"Found HST CALSPEC format on input, converted to " 280 "MUSE format; average sampling %.3f Angstrom/bin over MUSE " 281 "range (assumed vacuum wavelengths on input, converted to air).",
284 cpl_msg_error(__func__,
"Unknown format found!");
286 cpl_table_dump_structure(aTable, stdout);
288 rc = CPL_ERROR_INCOMPATIBLE_INPUT;
292 if (!cpl_errorstate_is_equal(prestate)) {
293 rc = cpl_error_get_code();
345 cpl_ensure(aResponse, CPL_ERROR_NULL_INPUT, rv);
346 int size = cpl_table_get_nrow(aResponse);
347 cpl_ensure(size > 0, cpl_error_get_code(), rv);
350 const double *lbda = cpl_table_get_data_double_const(aResponse,
"lambda"),
351 *resp = NULL, *rerr = NULL;
354 resp = cpl_table_get_data_double_const(aResponse,
"throughput");
357 resp = cpl_table_get_data_double_const(aResponse,
"response");
359 rerr = cpl_table_get_data_double_const(aResponse,
"resperr");
363 resp = cpl_table_get_data_double_const(aResponse,
"flux");
365 rerr = cpl_table_get_data_double_const(aResponse,
"fluxerr");
369 resp = cpl_table_get_data_double_const(aResponse,
"extinction");
372 resp = cpl_table_get_data_double_const(aResponse,
"ftelluric");
374 rerr = cpl_table_get_data_double_const(aResponse,
"ftellerr");
378 cpl_error_set(__func__, CPL_ERROR_UNSUPPORTED_MODE);
381 cpl_ensure(lbda && resp, cpl_error_get_code(), rv);
383 cpl_ensure(rerr, cpl_error_get_code(), rv);
387 if (aLambda < lbda[0]) {
390 if (aLambda > lbda[size-1]) {
395 double response = rv, resperror = 0.;
396 int l = 0, r = size - 1,
399 if (aLambda >= lbda[m] && aLambda <= lbda[m+1]) {
401 double lquot = (aLambda - lbda[m]) / (lbda[m+1] - lbda[m]);
402 response = resp[m] + (resp[m+1] - resp[m]) * lquot;
407 resperror = sqrt(pow(rerr[m] * (1 - lquot), 2.)
408 + pow(rerr[m+1] * lquot, 2.));
411 cpl_msg_debug(__func__,
"Found at m=%d (%f: %f+/-%f) / " 412 "m+1=%d (%f: %f+/-%f) -> %f: %f+/-%f",
413 m, lbda[m], resp[m], rerr ? rerr[m] : 0.,
414 m+1, lbda[m+1], resp[m+1], rerr ? rerr[m+1] : 0.,
415 aLambda, response, resperror);
420 if (aLambda < lbda[m]) {
423 if (aLambda > lbda[m]) {
430 cpl_msg_debug(__func__,
"Response %g+/-%g at lambda=%fA", response, resperror,
433 if (aError && rerr) {
456 muse_flux_image_sky(cpl_image *aImage,
double aX,
double aY,
double aHalfSize,
457 unsigned int aDSky,
float *aError)
463 int x1 = aX - aHalfSize, x2 = aX + aHalfSize,
464 y1 = aY - aHalfSize, y2 = aY + aHalfSize;
465 unsigned char nskyarea = 0;
466 double skylevel = 0., skyerror = 0.;
468 cpl_errorstate state = cpl_errorstate_get();
469 cpl_stats_mode mode = CPL_STATS_MEDIAN | CPL_STATS_MEDIAN_DEV;
470 cpl_stats *s = cpl_stats_new_from_image_window(aImage, mode,
471 x1 - aDSky, y1, x1 - 1, y2);
476 skylevel += cpl_stats_get_median(s);
477 skyerror += pow(cpl_stats_get_median_dev(s), 2);
481 s = cpl_stats_new_from_image_window(aImage,mode,
482 x2 + 1, y1, x2 + aDSky, y2);
485 skylevel += cpl_stats_get_median(s);
486 skyerror += pow(cpl_stats_get_median_dev(s), 2);
490 s = cpl_stats_new_from_image_window(aImage,mode,
491 x1, y1 - aDSky, x2, y1 - 1);
494 skylevel += cpl_stats_get_median(s);
495 skyerror += pow(cpl_stats_get_median_dev(s), 2);
499 s = cpl_stats_new_from_image_window(aImage,mode,
500 x1, y2 + 1, x2, y2 + aDSky);
503 skylevel += cpl_stats_get_median(s);
504 skyerror += pow(cpl_stats_get_median_dev(s), 2);
510 skylevel /= nskyarea;
511 skyerror = sqrt(skyerror) / nskyarea;
512 if (!cpl_errorstate_is_equal(state)) {
513 cpl_errorstate_set(state);
516 cpl_msg_debug(__func__,
"skylevel = %f +/- %f (%u sky areas)",
517 skylevel, skyerror, nskyarea);
544 muse_flux_image_gaussian(cpl_image *aImage, cpl_image *aImErr,
double aX,
545 double aY,
double aHalfSize,
unsigned int aDSky,
546 unsigned int aMaxBad,
float *aFErr)
550 UNUSED_ARGUMENT(aMaxBad);
556 cpl_array *params = cpl_array_new(7, CPL_TYPE_DOUBLE),
557 *parerr = cpl_array_new(7, CPL_TYPE_DOUBLE);
561 cpl_errorstate state = cpl_errorstate_get();
562 double skylevel = muse_flux_image_sky(aImage, aX, aY, aHalfSize, aDSky, NULL);
563 if (!cpl_errorstate_is_equal(state)) {
566 cpl_errorstate_set(state);
568 cpl_array_set_double(params, 0, skylevel);
569 cpl_array_set_double(params, 3, aX);
570 cpl_array_set_double(params, 4, aY);
571 double rms = 0, chisq = 0;
573 int nx = cpl_image_get_size_x(aImage),
574 ny = cpl_image_get_size_y(aImage),
575 xsize = fmin(aHalfSize, fmin(aX - 1., nx - aX)) * 2,
576 ysize = fmin(aHalfSize, fmin(aY - 1., ny - aY)) * 2;
577 cpl_error_code rc = cpl_fit_image_gaussian(aImage, aImErr, aX, aY, xsize, ysize,
578 params, parerr, NULL, &rms, &chisq,
579 NULL, NULL, NULL, NULL, NULL);
580 if (rc != CPL_ERROR_NONE) {
581 if (rc != CPL_ERROR_ILLEGAL_INPUT) {
582 cpl_msg_debug(__func__,
"rc = %d: %s", rc, cpl_error_get_message());
584 cpl_array_delete(params);
585 cpl_array_delete(parerr);
588 double flux = cpl_array_get_double(params, 1, NULL),
589 ferr = cpl_array_get_double(parerr, 1, NULL);
591 double fwhmx = cpl_array_get_double(params, 5, NULL) * CPL_MATH_FWHM_SIG,
592 fwhmy = cpl_array_get_double(params, 6, NULL) * CPL_MATH_FWHM_SIG;
593 cpl_msg_debug(__func__,
"%.3f,%.3f: %g+/-%g (bg: %g, FWHM: %.3f,%.3f, %g, %g)",
594 cpl_array_get_double(params, 3, NULL), cpl_array_get_double(params, 4, NULL),
595 flux, ferr, cpl_array_get_double(params, 0, NULL), fwhmx, fwhmy,
599 cpl_msg_debug(__func__,
"skylevel = %f", cpl_array_get_double(params, 0, NULL));
600 cpl_msg_debug(__func__,
"measured flux %f +/- %f", flux, ferr);
602 cpl_array_delete(params);
603 cpl_array_delete(parerr);
631 muse_flux_image_moffat(cpl_image *aImage, cpl_image *aImErr,
double aX,
632 double aY,
double aHalfSize,
unsigned int aDSky,
633 unsigned int aMaxBad,
float *aFErr)
639 int x1 = aX - aHalfSize, x2 = aX + aHalfSize,
640 y1 = aY - aHalfSize, y2 = aY + aHalfSize,
641 nx = cpl_image_get_size_x(aImage),
642 ny = cpl_image_get_size_y(aImage);
655 int npoints = (x2 - x1 + 1) * (y2 - y1 + 1);
657 cpl_matrix *pos = cpl_matrix_new(npoints, 2);
658 cpl_vector *values = cpl_vector_new(npoints),
659 *errors = cpl_vector_new(npoints);
660 float *derr = cpl_image_get_data_float(aImErr);
662 for (i = x1; i <= x2; i++) {
664 for (j = y1; j <= y2; j++) {
666 double data = cpl_image_get(aImage, i, j, &err);
670 cpl_matrix_set(pos, idx, 0, i);
671 cpl_matrix_set(pos, idx, 1, j);
672 cpl_vector_set(values, idx, data);
673 cpl_vector_set(errors, idx, derr[(i-1) + (j-1)*nx]);
679 if (idx < 16 || (npoints - idx) > (
int)aMaxBad) {
680 cpl_matrix_delete(pos);
681 cpl_vector_delete(values);
682 cpl_vector_delete(errors);
685 cpl_matrix_set_size(pos, idx, 2);
686 cpl_vector_set_size(values, idx);
687 cpl_vector_set_size(errors, idx);
689 cpl_array *params = cpl_array_new(8, CPL_TYPE_DOUBLE),
690 *parerr = cpl_array_new(8, CPL_TYPE_DOUBLE);
692 cpl_errorstate state = cpl_errorstate_get();
693 double skylevel = muse_flux_image_sky(aImage, aX, aY, aHalfSize, aDSky, NULL);
694 if (!cpl_errorstate_is_equal(state)) {
697 cpl_errorstate_set(state);
699 cpl_array_set_double(params, 0, skylevel);
700 cpl_array_set_double(params, 2, aX);
701 cpl_array_set_double(params, 3, aY);
702 double rms = 0, chisq = 0;
704 params, parerr, NULL,
706 cpl_matrix_delete(pos);
707 cpl_vector_delete(values);
708 cpl_vector_delete(errors);
709 if (rc != CPL_ERROR_NONE) {
710 if (rc != CPL_ERROR_ILLEGAL_INPUT) {
711 cpl_msg_debug(__func__,
"rc = %d: %s", rc, cpl_error_get_message());
713 cpl_array_delete(params);
714 cpl_array_delete(parerr);
718 double flux = cpl_array_get_double(params, 1, NULL);
720 *aFErr = cpl_array_get_double(parerr, 1, NULL);
723 cpl_msg_debug(__func__,
"skylevel = %f", cpl_array_get_double(params, 0, NULL));
724 cpl_msg_debug(__func__,
"measured flux %f +/- %f", flux, cpl_array_get_double(parerr, 1, NULL));
726 cpl_array_delete(params);
727 cpl_array_delete(parerr);
752 muse_flux_image_square(cpl_image *aImage, cpl_image *aImErr,
double aX,
753 double aY,
double aHalfSize,
unsigned int aDSky,
754 unsigned int aMaxBad,
float *aFErr)
759 int x1 = aX - aHalfSize, x2 = aX + aHalfSize,
760 y1 = aY - aHalfSize, y2 = aY + aHalfSize,
761 nx = cpl_image_get_size_x(aImage),
762 ny = cpl_image_get_size_y(aImage);
776 cpl_errorstate state = cpl_errorstate_get();
777 double skylevel = muse_flux_image_sky(aImage, aX, aY, aHalfSize, aDSky,
779 if (!cpl_errorstate_is_equal(state)) {
782 cpl_errorstate_set(state);
783 cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
789 cpl_image *region = cpl_image_extract(aImage, x1, y1, x2, y2);
791 cpl_msg_debug(__func__,
"region [%d:%d,%d:%d] %"CPL_SIZE_FORMAT
" bad pixels",
792 x1, y1, x2, y2, cpl_image_count_rejected(region));
794 if (cpl_image_count_rejected(region) > aMaxBad) {
795 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
796 cpl_image_delete(region);
799 cpl_image *regerr = cpl_image_extract(aImErr, x1, y1, x2, y2);
800 if (cpl_image_count_rejected(region) > 0) {
801 cpl_detector_interpolate_rejected(region);
802 cpl_detector_interpolate_rejected(regerr);
807 int npoints = (x2 - x1 + 1) * (y2 - y1 + 1),
811 nsky = 2 * aDSky * (x2 - x1 + y2 - y1 + 2);
812 double flux = cpl_image_get_flux(region) - skylevel * npoints,
813 ferr = sqrt(cpl_image_get_sqflux(regerr)
814 + npoints * skyerror*skyerror * (1. + (
double)npoints / nsky));
816 cpl_msg_debug(__func__,
"measured flux %f +/- %f (%d object pixels, %d pixels" 817 " with %f with sky %f)", flux, ferr, npoints, nsky,
818 cpl_image_get_flux(region), cpl_image_get_sqflux(regerr));
820 cpl_image_delete(region);
821 cpl_image_delete(regerr);
850 muse_flux_image_circle(cpl_image *aImage, cpl_image *aImErr,
double aX,
851 double aY,
double aAper,
double aAnnu,
double aDAnnu,
852 unsigned int aMaxBad,
float *aFErr)
857 double rmax = ceil(fmax(aAper, aAnnu + aDAnnu));
858 int x1 = aX - rmax, x2 = aX + rmax,
859 y1 = aY - rmax, y2 = aY + rmax,
860 nx = cpl_image_get_size_x(aImage),
861 ny = cpl_image_get_size_y(aImage);
876 cpl_vector *vbg = cpl_vector_new((x2 - x1 + 1) * (y2 - y1 + 1)),
877 *vbe = cpl_vector_new((x2 - x1 + 1) * (y2 - y1 + 1));
878 unsigned int nbad = 0, nbg = 0;
880 for (i = x1; i <= x2; i++) {
882 for (j = y1; j <= y2; j++) {
883 double r = sqrt(pow(aX - i, 2) + pow(aY - j, 2));
885 nbad += cpl_image_is_rejected(aImage, i, j) == 1;
887 if (r < aAnnu || r > aAnnu + aDAnnu) {
891 double value = cpl_image_get(aImage, i, j, &err);
895 cpl_vector_set(vbg, nbg, value);
896 cpl_vector_set(vbe, nbg, cpl_image_get(aImErr, i, j, &err));
901 cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
902 cpl_vector_delete(vbg);
903 cpl_vector_delete(vbe);
906 cpl_vector_set_size(vbg, nbg);
907 cpl_vector_set_size(vbe, nbg);
908 cpl_matrix *pos = cpl_matrix_new(1, nbg);
913 unsigned int nrej = nbg - cpl_vector_get_size(vbg);
915 nbg = cpl_vector_get_size(vbg);
917 double smean = cpl_polynomial_get_coeff(fit, &pows),
919 cpl_polynomial_delete(fit);
920 cpl_matrix_delete(pos);
921 cpl_vector_delete(vbg);
922 cpl_vector_delete(vbe);
924 cpl_msg_debug(__func__,
"sky: %d pixels (%d rejected), %f +/- %f; found %d " 925 "bad pixels inside aperture", nbg, nrej, smean, sstdev, nbad);
927 if (nbad > aMaxBad) {
928 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
934 cpl_detector_interpolate_rejected(aImage);
935 cpl_detector_interpolate_rejected(aImErr);
941 unsigned int nobj = 0;
942 for (i = x1; i <= x2; i++) {
944 for (j = y1; j <= y2; j++) {
945 double r = sqrt(pow(aX - i, 2) + pow(aY - j, 2));
950 double value = cpl_image_get(aImage, i, j, &err),
951 error = cpl_image_get(aImErr, i, j, &err);
957 flux -= smean * nobj;
962 ferr = sqrt(ferr + nobj * sstdev*sstdev * (1. + (
double)nobj / nbg));
964 cpl_msg_debug(__func__,
"flux: %d pixels (%d interpolated), %f +/- %f",
965 nobj, nbad, flux, ferr);
1012 cpl_ensure(aCube && aApertures, CPL_ERROR_NULL_INPUT, NULL);
1015 cpl_msg_info(__func__,
"Gaussian profile fits for flux integration");
1018 cpl_msg_info(__func__,
"Moffat profile fits for flux integration");
1021 cpl_msg_info(__func__,
"Circular flux integration");
1024 cpl_msg_info(__func__,
"Simple square window flux integration");
1027 cpl_msg_error(__func__,
"Unknown flux integration method!");
1028 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
1033 int naper = cpl_apertures_get_size(aApertures),
1034 nlambda = cpl_imagelist_get_size(aCube->
data),
1035 nplane = cpl_imagelist_get_size(aCube->
data) / 2;
1036 cpl_image *cim = cpl_imagelist_get(aCube->
data, nplane);
1038 intimage->
data = cpl_image_new(nlambda, naper, CPL_TYPE_FLOAT);
1039 intimage->
dq = cpl_image_new(nlambda, naper, CPL_TYPE_INT);
1040 intimage->
stat = cpl_image_new(nlambda, naper, CPL_TYPE_FLOAT);
1042 intimage->
header = cpl_propertylist_new();
1043 cpl_propertylist_append_double(intimage->
header,
"CRVAL1",
1045 cpl_propertylist_append_double(intimage->
header,
"CRPIX1",
1047 cpl_propertylist_append_double(intimage->
header,
"CD1_1",
1049 cpl_propertylist_append_string(intimage->
header,
"CTYPE1",
1051 cpl_propertylist_append_string(intimage->
header,
"CUNIT1",
1054 cpl_propertylist_append_double(intimage->
header,
"CRVAL2", 1.);
1055 cpl_propertylist_append_double(intimage->
header,
"CRPIX2", 1.);
1056 cpl_propertylist_append_double(intimage->
header,
"CD2_2", 1.);
1057 cpl_propertylist_append_string(intimage->
header,
"CTYPE2",
"PIXEL");
1058 cpl_propertylist_append_string(intimage->
header,
"CUNIT2",
"pixel");
1059 cpl_propertylist_append_double(intimage->
header,
"CD1_2", 0.);
1060 cpl_propertylist_append_double(intimage->
header,
"CD2_1", 0.);
1062 cpl_propertylist_append_string(intimage->
header,
"DATE-OBS",
1063 cpl_propertylist_get_string(aCube->
header,
1065 cpl_propertylist_append_string(intimage->
header,
"BUNIT",
1067 cpl_propertylist_append_double(intimage->
header,
"EXPTIME",
1069 cpl_propertylist_append_string(intimage->
header,
"ESO INS MODE",
1070 cpl_propertylist_get_string(aCube->
header,
1072 if (cpl_propertylist_has(aCube->
header, MUSE_HDR_FLUX_FFCORR)) {
1073 cpl_propertylist_append_bool(intimage->
header, MUSE_HDR_FLUX_FFCORR, CPL_TRUE);
1074 cpl_propertylist_set_comment(intimage->
header, MUSE_HDR_FLUX_FFCORR,
1075 MUSE_HDR_FLUX_FFCORR_C);
1079 cpl_errorstate ps = cpl_errorstate_get();
1083 fwhm /= (kMuseSpaxelSizeX_WFM + kMuseSpaxelSizeY_WFM) / 2.;
1085 fwhm /= (kMuseSpaxelSizeX_NFM + kMuseSpaxelSizeY_NFM) / 2.;
1087 if (!cpl_errorstate_is_equal(ps)) {
1088 double xc = cpl_apertures_get_centroid_x(aApertures, 1),
1089 yc = cpl_apertures_get_centroid_y(aApertures, 1),
1091 cpl_image_get_fwhm(cim, lround(xc), lround(yc), &xfwhm, &yfwhm);
1092 if (xfwhm > 0. && yfwhm > 0.) {
1093 fwhm = (xfwhm + yfwhm) / 2.;
1094 }
else if (xfwhm > 0.) {
1096 }
else if (yfwhm > 0.) {
1101 cpl_errorstate_set(ps);
1102 cpl_msg_debug(__func__,
"Using roughly estimated reference FWHM (%.3f pix) " 1103 "instead of DIMM seeing", fwhm);
1105 cpl_msg_debug(__func__,
"Using DIMM seeing of %.3f pix for reference FWHM",
1111 cpl_image *sizes = cpl_image_new(nlambda, naper, CPL_TYPE_DOUBLE);
1112 double *psizes = cpl_image_get_data_double(sizes);
1114 float *data = cpl_image_get_data_float(intimage->
data),
1115 *stat = cpl_image_get_data_float(intimage->
stat);
1116 int *dq = cpl_image_get_data_int(intimage->
dq);
1118 int l, ngood = 0, nillegal = 0, nbadbg = 0;
1119 #pragma omp parallel for default(none) \ 1120 shared(aApertures, aCube, aProfile, data, dq, fwhm, naper, nbadbg, \ 1121 ngood, nillegal, nlambda, psizes, stat) 1122 for (l = 0; l < nlambda; l++) {
1123 cpl_image *plane = cpl_imagelist_get(aCube->
data, l),
1124 *pldq = aCube->
dq ? cpl_imagelist_get(aCube->
dq, l) : NULL,
1125 *plerr = cpl_image_duplicate(cpl_imagelist_get(aCube->
stat, l));
1127 cpl_stats *stats = cpl_stats_new_from_image(plerr, CPL_STATS_ALL);
1128 cpl_msg_debug(__func__,
"lambda = %d/%f %s", l + 1,
1133 cpl_msg_debug(__func__,
"variance: %g...%g...%g", cpl_stats_get_min(stats),
1134 cpl_stats_get_mean(stats), cpl_stats_get_max(stats));
1135 cpl_stats_delete(stats);
1141 cpl_image_reject_value(plane, CPL_VALUE_NAN);
1142 cpl_image_reject_value(plerr, CPL_VALUE_NAN);
1145 stats = cpl_stats_new_from_image(plerr, CPL_STATS_ALL);
1146 cpl_msg_debug(__func__,
"cut variance: %g...%g...%g (%"CPL_SIZE_FORMAT
" bad" 1147 " pixel)", cpl_stats_get_min(stats), cpl_stats_get_mean(stats),
1148 cpl_stats_get_max(stats), cpl_image_count_rejected(plane));
1149 cpl_stats_delete(stats);
1152 cpl_image_power(plerr, 0.5);
1154 stats = cpl_stats_new_from_image(plerr, CPL_STATS_ALL);
1155 cpl_msg_debug(__func__,
"errors: %g...%g...%g", cpl_stats_get_min(stats),
1156 cpl_stats_get_mean(stats), cpl_stats_get_max(stats));
1157 cpl_stats_delete(stats);
1159 cpl_errorstate state = cpl_errorstate_get();
1161 for (n = 1; n <= naper; n++) {
1163 double xc = cpl_apertures_get_centroid_x(aApertures, n),
1164 yc = cpl_apertures_get_centroid_y(aApertures, n),
1165 size = sqrt(cpl_apertures_get_npix(aApertures, n)),
1167 cpl_errorstate prestate = cpl_errorstate_get();
1168 cpl_image_get_fwhm(plane, lround(xc), lround(yc), &xfwhm, &yfwhm);
1169 if (xfwhm < 0 || yfwhm < 0) {
1170 data[l + (n-1) * nlambda] = 0.;
1171 stat[l + (n-1) * nlambda] = FLT_MAX;
1172 cpl_errorstate_set(prestate);
1176 double halfsize = fmax(1.5 * (xfwhm + yfwhm), 3. * fwhm);
1177 if (halfsize < size / 2) {
1178 halfsize = size / 2;
1180 psizes[l + (n-1) * nlambda] = halfsize;
1182 cpl_msg_debug(__func__,
"%.2f,%.2f FWHM %.2f %.2f size %.2f --> %.2f",
1183 xc, yc, xfwhm, yfwhm, size, halfsize * 2.);
1188 data[l + (n-1) * nlambda] = muse_flux_image_gaussian(plane, plerr, xc, yc,
1190 &stat[l + (n-1) * nlambda]);
1193 data[l + (n-1) * nlambda] = muse_flux_image_moffat(plane, plerr, xc, yc,
1195 &stat[l + (n-1) * nlambda]);
1200 double radius = 4./3. * halfsize,
1201 rannu = radius * 5. / 4.;
1202 psizes[l + (n-1) * nlambda] = radius;
1203 data[l + (n-1) * nlambda] = muse_flux_image_circle(plane, plerr, xc, yc,
1204 radius, rannu, 10, 10,
1205 &stat[l + (n-1) * nlambda]);
1209 data[l + (n-1) * nlambda] = muse_flux_image_square(plane, plerr, xc, yc,
1211 &stat[l + (n-1) * nlambda]);
1213 if (data[l + (n-1) * nlambda] < 0 || !isfinite(data[l + (n-1) * nlambda])) {
1214 data[l + (n-1) * nlambda] = 0.;
1215 dq[l + (n-1) * nlambda] = EURO3D_MISSDATA;
1216 stat[l + (n-1) * nlambda] = FLT_MAX;
1222 if (!cpl_errorstate_is_equal(state)) {
1223 if (cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT) {
1224 cpl_errorstate_set(state);
1227 }
else if (cpl_error_get_code() == CPL_ERROR_DATA_NOT_FOUND) {
1228 cpl_errorstate_set(state);
1237 cpl_image_delete(plerr);
1241 cpl_image_reject_value(sizes, CPL_VALUE_ZERO);
1243 for (n = 1; n <= naper; n++) {
1245 cpl_msg_info(__func__,
"Radiuses used for circular flux integration for " 1246 "object %d: %f +/- %f (%f) %f..%f", n,
1247 cpl_image_get_mean_window(sizes, 1, n, nlambda, n),
1248 cpl_image_get_stdev_window(sizes, 1, n, nlambda, n),
1249 cpl_image_get_median_window(sizes, 1, n, nlambda, n),
1250 cpl_image_get_min_window(sizes, 1, n, nlambda, n),
1251 cpl_image_get_max_window(sizes, 1, n, nlambda, n));
1253 cpl_msg_info(__func__,
"Half-sizes used for flux integration for object " 1254 "%d: %f +/- %f (%f) %f..%f", n,
1255 cpl_image_get_mean_window(sizes, 1, n, nlambda, n),
1256 cpl_image_get_stdev_window(sizes, 1, n, nlambda, n),
1257 cpl_image_get_median_window(sizes, 1, n, nlambda, n),
1258 cpl_image_get_min_window(sizes, 1, n, nlambda, n),
1259 cpl_image_get_max_window(sizes, 1, n, nlambda, n));
1263 cpl_image_save(sizes,
"sizes.fits", CPL_TYPE_UNSPECIFIED, NULL, CPL_IO_CREATE);
1265 cpl_image_delete(sizes);
1268 cpl_propertylist_append_int(intimage->
header, MUSE_HDR_FLUX_NOBJ, naper);
1269 cpl_propertylist_set_comment(intimage->
header, MUSE_HDR_FLUX_NOBJ,
1270 MUSE_HDR_FLUX_NOBJ_C);
1275 cpl_propertylist_delete(wcs1);
1278 + (1. + cpl_image_get_size_x(cim)) / 2.,
1280 + (1. + cpl_image_get_size_y(cim)) / 2.;
1281 cpl_propertylist_update_double(wcs,
"CRPIX1", crpix1);
1282 cpl_propertylist_update_double(wcs,
"CRPIX2", crpix2);
1285 for (n = 1; n <= naper; n++) {
1287 double xc = cpl_apertures_get_centroid_x(aApertures, n),
1288 yc = cpl_apertures_get_centroid_y(aApertures, n),
1291 double flux = cpl_image_get_flux_window(intimage->
data, 1, n, nlambda, n);
1292 cpl_msg_debug(__func__,
"Object %02d: %.3f,%.3f pix, %f,%f deg, flux %e %s",
1294 char kw[KEYWORD_LENGTH];
1295 snprintf(kw, KEYWORD_LENGTH, MUSE_HDR_FLUX_OBJn_X, n);
1296 cpl_propertylist_append_float(intimage->
header, kw, xc);
1297 cpl_propertylist_set_comment(intimage->
header, kw, MUSE_HDR_FLUX_OBJn_X_C);
1298 snprintf(kw, KEYWORD_LENGTH, MUSE_HDR_FLUX_OBJn_Y, n);
1299 cpl_propertylist_append_float(intimage->
header, kw, yc);
1300 cpl_propertylist_set_comment(intimage->
header, kw, MUSE_HDR_FLUX_OBJn_Y_C);
1301 snprintf(kw, KEYWORD_LENGTH, MUSE_HDR_FLUX_OBJn_RA, n);
1302 cpl_propertylist_append_double(intimage->
header, kw, ra);
1303 cpl_propertylist_set_comment(intimage->
header, kw, MUSE_HDR_FLUX_OBJn_RA_C);
1304 snprintf(kw, KEYWORD_LENGTH, MUSE_HDR_FLUX_OBJn_DEC, n);
1305 cpl_propertylist_append_double(intimage->
header, kw, dec);
1306 cpl_propertylist_set_comment(intimage->
header, kw, MUSE_HDR_FLUX_OBJn_DEC_C);
1307 snprintf(kw, KEYWORD_LENGTH, MUSE_HDR_FLUX_OBJn_FLUX, n);
1308 cpl_propertylist_append_double(intimage->
header, kw, flux);
1309 cpl_propertylist_set_comment(intimage->
header, kw, MUSE_HDR_FLUX_OBJn_FLUX_C);
1311 cpl_propertylist_delete(wcs);
1313 if (nillegal > 0 || nbadbg > 0) {
1314 cpl_msg_warning(__func__,
"Successful fits in %d wavelength planes, but " 1315 "encountered %d \"Illegal input\" errors and %d bad " 1316 "background determinations", ngood, nillegal, nbadbg);
1318 cpl_msg_info(__func__,
"Successful fits in %d wavelength planes", ngood);
1365 cpl_ensure_code(aPixtable && aFluxObj, CPL_ERROR_NULL_INPUT);
1373 return cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
1380 if (insmode >= MUSE_MODE_WFM_AO_E) {
1385 for (irow = 0; irow < nrow; irow++) {
1386 if (dq[irow] & EURO3D_NOTCH_NAD) {
1387 dq[irow] &= ~EURO3D_NOTCH_NAD;
1392 if (getenv(
"MUSE_DEBUG_FLUX") && atoi(getenv(
"MUSE_DEBUG_FLUX")) > 2) {
1393 const char *fn =
"flux__pixtable.fits";
1394 cpl_msg_info(__func__,
"Saving pixel table as \"%s\"", fn);
1404 params->dlambda = kMuseSpectralSamplingA;
1409 aFluxObj->
cube = cube;
1418 if (getenv(
"MUSE_DEBUG_FLUX") && atoi(getenv(
"MUSE_DEBUG_FLUX")) >= 2) {
1419 const char *fn =
"flux__cube.fits";
1420 cpl_msg_info(__func__,
"Saving cube as \"%s\"", fn);
1423 int nplane = cpl_imagelist_get_size(cube->
data) / 2;
1424 cpl_image *cim = cpl_imagelist_get(cube->
data, nplane),
1425 *cdq = cpl_imagelist_get(cube->
dq, nplane);
1430 double dsigmas[] = { 50., 30., 10., 8., 6., 5. };
1431 cpl_vector *vsigmas = cpl_vector_wrap(
sizeof(dsigmas) /
sizeof(
double),
1433 cpl_size isigma = -1;
1434 cpl_apertures *apertures = cpl_apertures_extract(cim, vsigmas, &isigma);
1435 int napertures = apertures ? cpl_apertures_get_size(apertures) : 0;
1436 if (napertures < 1) {
1438 cpl_msg_error(__func__,
"No object for flux integration found down to %.1f" 1439 " sigma limit on plane %d",
1440 cpl_vector_get(vsigmas, cpl_vector_get_size(vsigmas) - 1),
1442 cpl_vector_unwrap(vsigmas);
1443 cpl_apertures_delete(apertures);
1444 return cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
1446 cpl_msg_debug(__func__,
"The %.1f sigma threshold was used to find %d object%s" 1447 " on plane %d", cpl_vector_get(vsigmas, isigma), napertures,
1448 napertures == 1 ?
"" :
"s", nplane + 1);
1449 cpl_vector_unwrap(vsigmas);
1451 cpl_apertures_dump(apertures, stdout);
1457 cpl_apertures_delete(apertures);
1460 return CPL_ERROR_NONE;
1470 static const double kTelluricBands[][4] = {
1471 { 6273., 6320., 6213., 6380. },
1472 { 6864., 6967., 6750., 7130. },
1473 { 7164., 7325., 7070., 7580. },
1474 { 7590., 7700., 7470., 7830. },
1475 { 8131., 8345., 7900., 8600. },
1476 { 8952., 9028., 8850., 9082. },
1477 { 9274., 9770., 9080., 9263. },
1478 { -1., -1., -1., -1. }
1487 {
"lmin", CPL_TYPE_DOUBLE,
"Angstrom",
"%8.3f",
1488 "lower limit of the telluric region", CPL_TRUE },
1489 {
"lmax", CPL_TYPE_DOUBLE,
"Angstrom",
"%8.3f",
1490 "upper limit of the telluric region", CPL_TRUE },
1491 {
"bgmin", CPL_TYPE_DOUBLE,
"Angstrom",
"%8.3f",
1492 "lower limit of the background region", CPL_TRUE },
1493 {
"bgmax", CPL_TYPE_DOUBLE,
"Angstrom",
"%8.3f",
1494 "upper limit of the background region", CPL_TRUE },
1495 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
1509 static cpl_error_code
1511 const cpl_table *aTellBands)
1513 cpl_ensure_code(aFluxObj, CPL_ERROR_NULL_INPUT);
1517 == CPL_ERROR_NONE) {
1518 cpl_msg_debug(__func__,
"using given table for telluric bands");
1519 aFluxObj->
tellbands = cpl_table_duplicate(aTellBands);
1520 return CPL_ERROR_NONE;
1523 unsigned int ntell =
sizeof(kTelluricBands) /
sizeof(kTelluricBands[0]) - 1;
1524 cpl_msg_debug(__func__,
"using builtin regions for telluric bands (%u " 1529 for (k = 0; k < ntell; k++) {
1530 cpl_table_set_double(tb,
"lmin", k, kTelluricBands[k][0]);
1531 cpl_table_set_double(tb,
"lmax", k, kTelluricBands[k][1]);
1532 cpl_table_set_double(tb,
"bgmin", k, kTelluricBands[k][2]);
1533 cpl_table_set_double(tb,
"bgmax", k, kTelluricBands[k][3]);
1535 if (getenv(
"MUSE_DEBUG_FLUX") && atoi(getenv(
"MUSE_DEBUG_FLUX")) >= 2) {
1536 const char *fn =
"flux__tellregions.fits";
1537 cpl_msg_info(__func__,
"Saving telluric bands table as \"%s\"", fn);
1538 cpl_table_save(tb, NULL, NULL, fn, CPL_IO_CREATE);
1540 return CPL_ERROR_NONE;
1561 char *dodebug = getenv(
"MUSE_DEBUG_FLUX");
1562 if (!dodebug || (dodebug && atoi(dodebug) <= 0)) {
1565 char *fn = cpl_sprintf(
"flux__sens_%s.ascii", aName);
1566 FILE *fp = fopen(fn,
"w");
1571 cpl_msg_debug(__func__,
"Written %"CPL_SIZE_FORMAT
" datapoints to \"%s\"",
1598 static cpl_error_code
1600 unsigned int aStar,
const cpl_table *aReference,
1601 double aAirmass,
const cpl_table *aExtinct)
1603 cpl_ensure_code(aFluxObj && aFluxObj->
intimage, CPL_ERROR_NULL_INPUT);
1609 int nlambda = cpl_image_get_size_x(aFluxObj->
intimage->
data);
1613 cpl_table_new_column(sensitivity,
"lambda", CPL_TYPE_DOUBLE);
1614 cpl_table_new_column(sensitivity,
"sens", CPL_TYPE_DOUBLE);
1615 cpl_table_new_column(sensitivity,
"serr", CPL_TYPE_DOUBLE);
1616 cpl_table_new_column(sensitivity,
"dq", CPL_TYPE_INT);
1617 cpl_table_set_column_format(sensitivity,
"dq",
"%u");
1618 float *data = cpl_image_get_data_float(aFluxObj->
intimage->
data),
1619 *stat = cpl_image_get_data_float(aFluxObj->
intimage->
stat);
1621 for (l = 0; l < nlambda; l++) {
1622 if (data[l + aStar*nlambda] <= 0. ||
1623 stat[l + aStar*nlambda] <= 0. ||
1624 stat[l + aStar*nlambda] == FLT_MAX) {
1628 double lambda = crval + cdelt * (l + 1 - crpix),
1630 extinct = !aExtinct ? 0.
1633 cpl_errorstate prestate = cpl_errorstate_get();
1638 if (!cpl_errorstate_is_equal(prestate)) {
1639 cpl_errorstate_set(prestate);
1645 double c = 2.5 * log10(data[l + aStar*nlambda]
1646 / exptime / cdelt / ref)
1647 + aAirmass * extinct,
1648 cerr = sqrt(pow(referr / ref, 2) + stat[l + aStar*nlambda]
1649 / pow(data[l + aStar*nlambda], 2))
1650 * 2.5 / CPL_MATH_LN10;
1651 cpl_table_set_double(sensitivity,
"lambda", idx, lambda);
1652 cpl_table_set_double(sensitivity,
"sens", idx, c);
1653 cpl_table_set_double(sensitivity,
"serr", idx, cerr);
1654 cpl_table_set_int(sensitivity,
"dq", idx, EURO3D_GOODPIXEL);
1658 cpl_table_set_size(sensitivity, idx);
1659 return CPL_ERROR_NONE;
1679 static cpl_error_code
1682 cpl_ensure_code(aFluxObj && aFluxObj->
intimage, CPL_ERROR_NULL_INPUT);
1694 int irow, nfluxes = cpl_table_get_nrow(tsens);
1695 for (irow = 0; irow < nfluxes; irow++) {
1696 double lambda = cpl_table_get_double(tsens,
"lambda", irow, NULL);
1697 unsigned int dq = EURO3D_GOODPIXEL,
1698 k, nk = cpl_table_get_nrow(tb);
1699 for (k = 0; k < nk; k++) {
1700 double lmin = cpl_table_get_double(tb,
"lmin", k, NULL),
1701 lmax = cpl_table_get_double(tb,
"lmax", k, NULL);
1702 if (lambda >= lmin && lambda <= lmax) {
1703 dq |= EURO3D_TELLURIC;
1706 if ((isnominal && lambda < kMuseNominalCutoff) ||
1707 (isAON && lambda < kMuseAOCutoff)) {
1708 dq |= EURO3D_OUTSDRANGE;
1710 cpl_table_set_int(tsens,
"dq", irow, dq);
1712 return CPL_ERROR_NONE;
1740 static cpl_polynomial *
1742 double aLambda1,
double aLambda2,
1743 unsigned int aOrder,
double aRSigma,
double *aRMSE)
1745 cpl_ensure(aFluxObj && aFluxObj->
sensitivity, CPL_ERROR_NULL_INPUT, NULL);
1748 cpl_table_select_all(tsens);
1749 cpl_table_and_selected_int(tsens,
"dq", CPL_NOT_EQUAL_TO, EURO3D_GOODPIXEL);
1750 cpl_table_and_selected_int(tsens,
"dq", CPL_NOT_EQUAL_TO, EURO3D_TELLCOR);
1751 cpl_table_or_selected_double(tsens,
"lambda", CPL_LESS_THAN, aLambda1);
1752 cpl_table_or_selected_double(tsens,
"lambda", CPL_GREATER_THAN, aLambda2);
1754 cpl_table *tunwanted = cpl_table_extract_selected(tsens);
1755 cpl_table_erase_selected(tsens);
1756 muse_flux_response_dump_sensitivity(aFluxObj,
"fitinput");
1760 int nrow = cpl_table_get_nrow(tsens);
1761 cpl_matrix *lambdas = cpl_matrix_new(1, nrow);
1762 cpl_vector *sens = cpl_vector_new(nrow),
1763 *serr = cpl_vector_new(nrow);
1764 memcpy(cpl_matrix_get_data(lambdas),
1765 cpl_table_get_data_double_const(tsens,
"lambda"), nrow*
sizeof(
double));
1766 memcpy(cpl_vector_get_data(sens),
1767 cpl_table_get_data_double_const(tsens,
"sens"), nrow*
sizeof(
double));
1768 memcpy(cpl_vector_get_data(serr),
1769 cpl_table_get_data_double_const(tsens,
"serr"), nrow*
sizeof(
double));
1774 tsens, aOrder, aRSigma,
1776 int nout = cpl_vector_get_size(sens);
1778 cpl_msg_debug(__func__,
"transferred %d entries (%.3f...%.3f) for the " 1779 "order %u fit, %d entries are left, RMS %f", nrow, aLambda1,
1780 aLambda2, aOrder, nout, sqrt(mse));
1782 cpl_matrix_delete(lambdas);
1783 cpl_vector_delete(sens);
1784 cpl_vector_delete(serr);
1786 *aRMSE = mse / (nout - aOrder - 1);
1790 cpl_table_insert(tsens, tunwanted, nout);
1791 cpl_table_delete(tunwanted);
1816 static cpl_error_code
1819 cpl_ensure_code(aFluxObj, CPL_ERROR_NULL_INPUT);
1823 cpl_table_new_column(tsens,
"sens_orig", CPL_TYPE_DOUBLE);
1824 cpl_table_new_column(tsens,
"serr_orig", CPL_TYPE_DOUBLE);
1825 cpl_table_new_column(tsens,
"tellcor", CPL_TYPE_DOUBLE);
1826 unsigned int k, nk = cpl_table_get_nrow(tb);
1827 for (k = 0; k < nk; k++) {
1828 double lmin = cpl_table_get_double(tb,
"lmin", k, NULL),
1829 lmax = cpl_table_get_double(tb,
"lmax", k, NULL),
1830 bgmin = cpl_table_get_double(tb,
"bgmin", k, NULL),
1831 bgmax = cpl_table_get_double(tb,
"bgmax", k, NULL),
1832 datamin = cpl_table_get_column_min(tsens,
"lambda"),
1833 datamax = cpl_table_get_column_max(tsens,
"lambda");
1834 cpl_boolean extrapolate = CPL_FALSE;
1835 if (bgmax < lmax || datamax < lmax) {
1836 extrapolate = CPL_TRUE;
1838 if (bgmin > lmin || datamin > lmin) {
1839 extrapolate = CPL_TRUE;
1841 if (datamin > lmax || datamax < lmin) {
1843 cpl_msg_warning(__func__,
"Telluric region %u (range %.2f...%.2f, " 1844 "reference region %.2f...%.2f) outside data range " 1845 "(%.2f..%.2f)!", k + 1, lmin, lmax, bgmin, bgmax,
1850 unsigned int order = extrapolate ? 1 : 2;
1854 cpl_errorstate state = cpl_errorstate_get();
1855 cpl_polynomial *fit = muse_flux_response_fit(aFluxObj, bgmin, bgmax,
1857 if (!cpl_errorstate_is_equal(state) || !fit) {
1858 cpl_msg_warning(__func__,
"Telluric region %u (range %.2f...%.2f, " 1859 "reference region %.2f...%.2f) could not be fitted!",
1860 k + 1, lmin, lmax, bgmin, bgmax);
1861 cpl_errorstate_set(state);
1862 cpl_polynomial_delete(fit);
1865 cpl_msg_debug(__func__,
"Telluric region %u: %.2f...%.2f, reference region " 1866 "%.2f...%.2f", k + 1, lmin, lmax, bgmin, bgmax);
1868 cpl_polynomial_dump(fit, stdout);
1871 int irow, nrow = cpl_table_get_nrow(tsens);
1872 for (irow = 0; irow < nrow; irow++) {
1873 double lambda = cpl_table_get_double(tsens,
"lambda", irow, NULL);
1874 if (lambda >= lmin && lambda <= lmax &&
1875 (
unsigned int)cpl_table_get_int(tsens,
"dq", irow, NULL)
1876 == EURO3D_TELLURIC) {
1877 double origval = cpl_table_get_double(tsens,
"sens", irow, NULL),
1878 origerr = cpl_table_get_double(tsens,
"serr", irow, NULL),
1879 interpval = cpl_polynomial_eval_1d(fit, lambda, NULL);
1880 cpl_table_set_int(tsens,
"dq", irow, EURO3D_TELLCOR);
1881 cpl_table_set_double(tsens,
"sens_orig", irow, origval);
1882 cpl_table_set_double(tsens,
"sens", irow, interpval);
1885 cpl_table_set_double(tsens,
"serr_orig", irow, origerr);
1886 cpl_table_set_double(tsens,
"serr", irow, sqrt(origerr*origerr + rmse));
1888 if (interpval > origval) {
1890 double ftelluric = pow(10, -0.4 * (interpval - origval));
1891 cpl_table_set(tsens,
"tellcor", irow, ftelluric);
1893 cpl_table_set_double(tsens,
"tellcor", irow, 1.);
1897 cpl_polynomial_delete(fit);
1900 cpl_table_power_column(tsens,
"tellcor", 1. / aAirmass);
1901 return CPL_ERROR_NONE;
1923 static cpl_error_code
1924 muse_flux_response_extrapolate(
muse_flux_object *aFluxObj,
double aDistance)
1926 cpl_ensure_code(aFluxObj, CPL_ERROR_NULL_INPUT);
1929 cpl_propertylist *order = cpl_propertylist_new();
1930 cpl_propertylist_append_bool(order,
"lambda", CPL_FALSE);
1931 cpl_table_sort(tsens, order);
1933 int nrow = cpl_table_get_nrow(tsens);
1935 double lambda1 = cpl_table_get_double(tsens,
"lambda", 0, NULL),
1936 serr1 = cpl_table_get_double(tsens,
"serr", 0, NULL);
1937 unsigned int dq = cpl_table_get_int(tsens,
"dq", 0, NULL);
1939 while (dq != EURO3D_GOODPIXEL && dq != EURO3D_TELLCOR) {
1940 lambda1 = cpl_table_get_double(tsens,
"lambda", ++irow, NULL);
1941 serr1 = cpl_table_get_double(tsens,
"serr", irow, NULL);
1942 dq = cpl_table_get_int(tsens,
"dq", irow, NULL);
1944 double lambda2 = cpl_table_get_double(tsens,
"lambda", nrow - 1, NULL),
1945 serr2 = cpl_table_get_double(tsens,
"serr", nrow - 1, NULL);
1946 dq = cpl_table_get_int(tsens,
"dq", nrow - 1, NULL);
1948 while (dq != EURO3D_GOODPIXEL && dq != EURO3D_TELLCOR) {
1949 lambda2 = cpl_table_get_double(tsens,
"lambda", --irow, NULL);
1950 serr2 = cpl_table_get_double(tsens,
"serr", irow, NULL);
1951 dq = cpl_table_get_int(tsens,
"dq", irow, NULL);
1953 cpl_polynomial *fit1 = muse_flux_response_fit(aFluxObj, lambda1,
1954 lambda1 + aDistance, 1, 5., NULL),
1955 *fit2 = muse_flux_response_fit(aFluxObj, lambda2 - aDistance,
1956 lambda2, 1, 5., NULL);
1957 nrow = cpl_table_get_nrow(tsens);
1958 double d1 = (lambda1 - kMuseLambdaMinX) / 100.,
1959 d2 = (kMuseLambdaMaxX - lambda2) / 100.;
1960 cpl_table_set_size(tsens, nrow + 200);
1964 for (l = kMuseLambdaMinX; l <= lambda1 - d1; l += d1) {
1965 double sens = cpl_polynomial_eval_1d(fit1, l, NULL),
1967 serr = 2. * (lambda1 - l) / 50. * serr1 + serr1;
1969 cpl_table_set_invalid(tsens,
"lambda", irow++);
1970 cpl_msg_debug(__func__,
"invalid blueward extrapolation: %.3f %f +/- %f",
1974 cpl_table_set_double(tsens,
"lambda", irow, l);
1975 cpl_table_set_double(tsens,
"sens", irow, sens);
1976 cpl_table_set_double(tsens,
"serr", irow, serr);
1977 cpl_table_set_int(tsens,
"dq", irow++, (
int)EURO3D_OUTSDRANGE);
1979 cpl_msg_debug(__func__,
"Extrapolated blue end: %.1f...%.1f Angstrom (using" 1980 " data from %.1f...%.1f Angstrom)", kMuseLambdaMinX,
1981 lambda1 - d1, lambda1, lambda1 + aDistance);
1984 for (l = lambda2 + d2; fit2 && l <= kMuseLambdaMaxX; l += d2) {
1985 double sens = cpl_polynomial_eval_1d(fit2, l, NULL),
1986 serr = 2. * (l - lambda2) / 50. * serr2 + serr2;
1988 cpl_table_set_invalid(tsens,
"lambda", irow++);
1989 cpl_msg_debug(__func__,
"invalid redward extrapolation: %.3f %f +/- %f",
1993 cpl_table_set_double(tsens,
"lambda", irow, l);
1994 cpl_table_set_double(tsens,
"sens", irow, sens);
1995 cpl_table_set_double(tsens,
"serr", irow, serr);
1996 cpl_table_set_int(tsens,
"dq", irow++, (
int)EURO3D_OUTSDRANGE);
1998 cpl_msg_debug(__func__,
"Extrapolated red end: %.1f...%.1f Angstrom (using " 1999 "data from %.1f...%.1f Angstrom)", lambda2 + d2,
2000 kMuseLambdaMaxX, lambda2 - aDistance, lambda2);
2003 cpl_polynomial_dump(fit1, stdout);
2004 cpl_polynomial_dump(fit2, stdout);
2007 cpl_polynomial_delete(fit1);
2008 cpl_polynomial_delete(fit2);
2010 cpl_table_select_all(tsens);
2011 cpl_table_and_selected_invalid(tsens,
"sens");
2012 cpl_table_erase_selected(tsens);
2014 cpl_table_sort(tsens, order);
2015 cpl_propertylist_delete(order);
2016 return CPL_ERROR_NONE;
2072 const cpl_table *aReference,
2073 const cpl_table *aTellBands,
2074 const cpl_table *aExtinct)
2076 cpl_ensure_code(aFluxObj && aFluxObj->
intimage && aReference,
2077 CPL_ERROR_NULL_INPUT);
2078 cpl_ensure_code(aAirmass >= 1., CPL_ERROR_ILLEGAL_INPUT);
2080 cpl_msg_warning(__func__,
"Extinction table not given!");
2083 (!isfinite(aFluxObj->
raref) || !isfinite(aFluxObj->
decref))) {
2084 cpl_msg_warning(__func__,
"Reference position %f,%f contains infinite " 2085 "values, using flux to select star!", aFluxObj->
raref,
2089 muse_flux_response_set_telluric_bands(aFluxObj, aTellBands);
2091 int nobjects = cpl_image_get_size_y(aFluxObj->
intimage->
data);
2094 double flux = 0., dmin = DBL_MAX;
2095 int n, nstar = 1, nstardist = 1;
2096 for (n = 1; n <= nobjects; n++) {
2097 char kw[KEYWORD_LENGTH];
2098 snprintf(kw, KEYWORD_LENGTH, MUSE_HDR_FLUX_OBJn_RA, n);
2099 double ra = cpl_propertylist_get_double(aFluxObj->
intimage->
header, kw);
2100 snprintf(kw, KEYWORD_LENGTH, MUSE_HDR_FLUX_OBJn_DEC, n);
2101 double dec = cpl_propertylist_get_double(aFluxObj->
intimage->
header, kw),
2104 cpl_msg_debug(__func__,
"distance(%d) = %f arcsec", n, dthis * 3600.);
2105 if (fabs(dthis) < dmin) {
2109 snprintf(kw, KEYWORD_LENGTH, MUSE_HDR_FLUX_OBJn_FLUX, n);
2110 double this = cpl_propertylist_get_double(aFluxObj->
intimage->
header, kw);
2111 cpl_msg_debug(__func__,
"flux(%d) = %e %s", n,
this, bunit);
2118 char *outstring = NULL,
2121 outstring = cpl_sprintf(
"Selected the brightest star (%d of %d; %.3e %s)" 2122 " as reference object", nstar, nobjects, flux, bunit);
2123 comment = cpl_sprintf(MUSE_HDR_FLUX_NSEL_C,
"brightest");
2126 outstring = cpl_sprintf(
"Selected the nearest star (%d of %d; %.2f arcsec) " 2127 "as reference object", nstar, nobjects, dmin*3600.);
2128 comment = cpl_sprintf(MUSE_HDR_FLUX_NSEL_C,
"nearest");
2129 nselected = nstardist;
2131 cpl_msg_info(__func__,
"%s", outstring);
2132 cpl_free(outstring);
2136 cpl_propertylist_append_int(aFluxObj->
intimage->
header, MUSE_HDR_FLUX_NSEL,
2138 cpl_propertylist_set_comment(aFluxObj->
intimage->
header, MUSE_HDR_FLUX_NSEL,
2143 muse_flux_response_sensitivity(aFluxObj,
2144 nselected - 1, aReference,
2145 aAirmass, aExtinct);
2146 muse_flux_response_dump_sensitivity(aFluxObj,
"initial");
2148 muse_flux_response_mark_questionable(aFluxObj);
2149 muse_flux_response_dump_sensitivity(aFluxObj,
"intermediate");
2152 cpl_table_and_selected_int(aFluxObj->
sensitivity,
"dq", CPL_EQUAL_TO,
2153 (
int)EURO3D_OUTSDRANGE);
2155 muse_flux_response_dump_sensitivity(aFluxObj,
"intercut");
2159 muse_flux_response_telluric(aFluxObj, aAirmass);
2160 muse_flux_response_dump_sensitivity(aFluxObj,
"interpolated");
2162 double width = 150.;
2163 if (cpl_propertylist_has(aFluxObj->
intimage->
header, MUSE_HDR_FLUX_FFCORR)) {
2170 muse_flux_response_extrapolate(aFluxObj, width);
2171 muse_flux_response_dump_sensitivity(aFluxObj,
"extrapolated");
2175 aFluxObj->
reference = cpl_table_duplicate(aReference);
2177 return CPL_ERROR_NONE;
2191 {
"lambda", CPL_TYPE_DOUBLE,
"Angstrom",
"%7.2f",
"wavelength", CPL_TRUE },
2192 {
"response", CPL_TYPE_DOUBLE,
2193 "2.5*log10((count/s/Angstrom)/(erg/s/cm**2/Angstrom))",
"%.4e",
2194 "instrument response derived from standard star", CPL_TRUE },
2195 {
"resperr", CPL_TYPE_DOUBLE,
2196 "2.5*log10((count/s/Angstrom)/(erg/s/cm**2/Angstrom))",
"%.4e",
2197 "instrument response error derived from standard star", CPL_TRUE },
2198 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
2212 {
"lambda", CPL_TYPE_DOUBLE,
"Angstrom",
"%7.2f",
"wavelength", CPL_TRUE },
2213 {
"ftelluric", CPL_TYPE_DOUBLE,
"",
"%.5f",
2214 "the telluric correction factor, normalized to an airmass of 1", CPL_TRUE },
2215 {
"ftellerr", CPL_TYPE_DOUBLE,
"",
"%.5f",
2216 "the error of the telluric correction factor", CPL_TRUE },
2217 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
2245 muse_flux_get_response_table_smooth(cpl_table *aResp,
double aHalfwidth,
2246 double aLambdaMin,
double aLambdaMax,
2247 double aGapMin,
double aGapMax,
2248 cpl_boolean aAverage)
2250 cpl_msg_debug(__func__,
"gap (%.3f..%.3f) wavelength range (%.3f..%.3f)",
2251 aGapMin, aGapMax, aLambdaMin, aLambdaMax);
2254 if (aGapMin < aGapMax) {
2255 if (aGapMin > aLambdaMin && aGapMax < aLambdaMax) {
2258 muse_flux_get_response_table_smooth(aResp, aHalfwidth, aLambdaMin, aGapMin,
2259 0.1, -0.1, aAverage);
2260 muse_flux_get_response_table_smooth(aResp, aHalfwidth, aGapMax, aLambdaMax,
2261 0.1, -0.1, aAverage);
2263 }
else if (aGapMin < aLambdaMin && aGapMax > aLambdaMax) {
2265 cpl_msg_warning(__func__,
"No smoothing, gap (%.3f..%.3f) is larger than " 2266 "wavelength range (%.3f..%.3f).", aGapMin, aGapMax,
2267 aLambdaMin, aLambdaMax);
2268 }
else if (aGapMin <= aLambdaMin && aGapMax < aLambdaMax) {
2270 muse_flux_get_response_table_smooth(aResp, aHalfwidth, aGapMax, aLambdaMax,
2271 0.1, -0.1, aAverage);
2272 }
else if (aGapMin > aLambdaMin && aGapMax >= aLambdaMax) {
2274 muse_flux_get_response_table_smooth(aResp, aHalfwidth, aLambdaMin, aGapMin,
2275 0.1, -0.1, aAverage);
2282 cpl_table_duplicate_column(aResp,
"sens", aResp,
"response");
2283 cpl_table_duplicate_column(aResp,
"serr", aResp,
"resperr");
2286 cpl_table_select_all(aResp);
2287 cpl_table_and_selected_double(aResp,
"lambda", CPL_NOT_LESS_THAN, aLambdaMin);
2288 cpl_table_and_selected_double(aResp,
"lambda", CPL_NOT_GREATER_THAN, aLambdaMax);
2290 cpl_boolean sym = cpl_table_count_selected(aResp) < cpl_table_get_nrow(aResp);
2291 cpl_msg_debug(__func__,
"%s smoothing response +/- %.3f Angstrom between %.3f " 2292 "and %.3f Angstrom", sym ?
"symmetrical" :
"", aHalfwidth,
2293 aLambdaMin, aLambdaMax);
2296 int i, n = cpl_table_get_nrow(aResp);
2297 for (i = 0; i < n; i++) {
2298 if (!cpl_table_is_selected(aResp, i)) {
2301 double lambda = cpl_table_get_double(aResp,
"lambda", i, NULL);
2302 int j = i, j1 = i, j2 = i;
2304 while (--j > 0 && cpl_table_is_selected(aResp, j) &&
2305 lambda - cpl_table_get_double(aResp,
"lambda", j, NULL) <= aHalfwidth) {
2309 while (++j < n && cpl_table_is_selected(aResp, j) &&
2310 cpl_table_get_double(aResp,
"lambda", j, NULL) - lambda <= aHalfwidth) {
2322 double *sens = cpl_table_get_data_double(aResp,
"sens"),
2323 *serr = cpl_table_get_data_double(aResp,
"serr");
2324 cpl_vector *v = cpl_vector_wrap(j2 - j1 + 1, sens + j1),
2325 *ve = cpl_vector_wrap(j2 - j1 + 1, serr + j1);
2328 double mean = cpl_vector_get_mean(v),
2329 stdev = j2 == j1 ? 0. : cpl_vector_get_stdev(v),
2330 rerr = cpl_table_get_double(aResp,
"resperr", i, NULL);
2331 cpl_table_set_double(aResp,
"response", i, mean);
2332 cpl_table_set_double(aResp,
"resperr", i, sqrt(rerr*rerr + stdev*stdev));
2334 cpl_msg_debug(__func__,
"%d %.3f %d...%d --> %f +/- %f", i, lambda, j1, j2,
2335 mean, sqrt(rerr*rerr + stdev*stdev));
2339 double median = cpl_vector_get_median_const(v),
2341 mederr = cpl_vector_get_median_const(ve);
2343 mdev = cpl_table_get_double(aResp,
"serr", j1, NULL);
2345 if (mdev < mederr) {
2349 cpl_msg_debug(__func__,
"%d %.3f %d...%d --> %f +/- %f", i, lambda, j1, j2,
2352 cpl_table_set_double(aResp,
"response", i, median);
2353 cpl_table_set_double(aResp,
"resperr", i, mdev);
2355 cpl_vector_unwrap(v);
2356 cpl_vector_unwrap(ve);
2360 cpl_table_erase_column(aResp,
"sens");
2361 cpl_table_erase_column(aResp,
"serr");
2381 muse_flux_get_response_table_collect_points(
const cpl_table *aTable,
2382 double aLambda,
double aLDist,
2383 cpl_matrix *aPos, cpl_vector *aVal,
2387 unsigned int np = 0;
2388 int irow, nrow = cpl_table_get_nrow(aTable),
2389 nsel = cpl_table_count_selected(aTable);
2391 cpl_boolean selected = nsel > 0 && nsel != nrow;
2392 for (irow = 0; irow < nrow; irow++) {
2393 if (selected && !cpl_table_is_selected(aTable, irow)) {
2396 double lambda = cpl_table_get(aTable,
"lambda", irow, NULL);
2397 if (lambda < aLambda - aLDist || lambda > aLambda + aLDist) {
2400 cpl_matrix_set(aPos, 0, np, lambda);
2401 cpl_vector_set(aVal, np, cpl_table_get(aTable,
"sens", irow, NULL));
2402 cpl_vector_set(aErr, np, cpl_table_get(aTable,
"serr", irow, NULL));
2405 cpl_matrix_set_size(aPos, 1, np);
2406 cpl_vector_set_size(aVal, np);
2407 cpl_vector_set_size(aErr, np);
2435 muse_flux_get_response_table_piecewise_poly(cpl_table *aResp,
double aLambdaMin,
2436 double aLambdaMax,
double aGapMin,
2437 double aGapMax,
double aDStep,
2442 cpl_msg_debug(__func__,
"gap (%.3f..%.3f) wavelength range (%.3f..%.3f)",
2443 aGapMin, aGapMax, aLambdaMin, aLambdaMax);
2444 if (aGapMin < aGapMax) {
2445 if (aGapMin > aLambdaMin && aGapMax < aLambdaMax) {
2448 muse_flux_get_response_table_piecewise_poly(aResp, aLambdaMin, aGapMin,
2449 0.1, -0.1, aDStep, aRSigma);
2450 muse_flux_get_response_table_piecewise_poly(aResp, aGapMax, aLambdaMax,
2451 0.1, -0.1, aDStep, aRSigma);
2453 }
else if (aGapMin < aLambdaMin && aGapMax > aLambdaMax) {
2455 cpl_msg_warning(__func__,
"No piecewise polynomial smoothing, gap (%.3f.." 2456 "%.3f) is larger than wavelength range (%.3f..%.3f).",
2457 aGapMin, aGapMax, aLambdaMin, aLambdaMax);
2458 }
else if (aGapMin <= aLambdaMin && aGapMax < aLambdaMax) {
2460 muse_flux_get_response_table_piecewise_poly(aResp, aGapMax, aLambdaMax,
2461 0.1, -0.1, aDStep, aRSigma);
2462 }
else if (aGapMin > aLambdaMin && aGapMax >= aLambdaMax) {
2464 muse_flux_get_response_table_piecewise_poly(aResp, aLambdaMin, aGapMin,
2465 0.1, -0.1, aDStep, aRSigma);
2472 cpl_table_duplicate_column(aResp,
"sens", aResp,
"response");
2473 cpl_table_duplicate_column(aResp,
"serr", aResp,
"resperr");
2476 cpl_table_select_all(aResp);
2477 cpl_table_and_selected_double(aResp,
"lambda", CPL_NOT_LESS_THAN, aLambdaMin);
2478 cpl_table_and_selected_double(aResp,
"lambda", CPL_NOT_GREATER_THAN, aLambdaMax);
2481 unsigned int npold = 0, njumps = 0;
2482 double ldistold = -1,
2484 cpl_array *jumppos = cpl_array_new(0, CPL_TYPE_DOUBLE),
2485 *jumplen = cpl_array_new(0, CPL_TYPE_DOUBLE);
2488 int irow, nrow = cpl_table_get_nrow(aResp);
2489 for (irow = 0; irow < nrow; irow++) {
2490 if (!cpl_table_is_selected(aResp, irow)) {
2494 double lambda = cpl_table_get(aResp,
"lambda", irow, NULL);
2496 double ldist = aDStep;
2498 cpl_matrix *pos = cpl_matrix_new(1, nrow);
2499 cpl_vector *val = cpl_vector_new(nrow),
2500 *err = cpl_vector_new(nrow);
2501 unsigned int np = muse_flux_get_response_table_collect_points(aResp,
2510 printf(
"%f Angstrom %u points (%u, %.3f, %f):\n", lambda, np, npold,
2511 (
double)np / npold - 1., cpl_vector_get_mean(val));
2512 cpl_matrix_dump(pos, stdout);
2516 if (np > 10 && fabs((
double)np / npold - 1.) > 0.1) {
2517 cpl_msg_debug(__func__,
"possible jump, changed at lambda %.3f (%u -> %u, " 2518 "%.3f -> %.3f)", lambda, npold, np, ldistold, ldist);
2519 cpl_array_set_size(jumppos, ++njumps);
2520 cpl_array_set_size(jumplen, njumps);
2521 cpl_array_set_double(jumppos, njumps - 1, (lambdaold + lambda) / 2.);
2522 cpl_array_set_double(jumplen, njumps - 1, (ldistold + ldist) / 2.);
2526 unsigned int order = np > 3 ? 3 : np - 1;
2530 NULL, order, aRSigma,
2533 if (fabs(lambda - 4861.3) < 1.) {
2534 cpl_vector *res = cpl_vector_new(cpl_vector_get_size(val));
2535 cpl_vector_fill_polynomial_fit_residual(res, val, NULL, poly, pos, NULL);
2536 double rms = sqrt(cpl_vector_product(res, res) / cpl_vector_get_size(res));
2537 cpl_msg_debug(__func__,
"lambda %f rms %f (%u/%d points)", lambda, rms,
2538 (
unsigned)cpl_vector_get_size(val), np);
2540 cpl_plot_vector(NULL, NULL, NULL, val);
2541 cpl_plot_vector(NULL, NULL, NULL, res);
2542 cpl_vector_delete(res);
2545 cpl_matrix_delete(pos);
2546 cpl_vector_delete(val);
2547 cpl_vector_delete(err);
2548 double resp = cpl_polynomial_eval_1d(poly, lambda, NULL);
2549 cpl_polynomial_delete(poly);
2550 cpl_table_set(aResp,
"lambda", irow, lambda);
2551 cpl_table_set(aResp,
"response", irow, resp);
2552 double serr = cpl_table_get(aResp,
"serr", irow, NULL);
2553 cpl_table_set(aResp,
"resperr", irow, sqrt(mse + serr*serr));
2561 cpl_table_erase_column(aResp,
"sens");
2562 cpl_table_erase_column(aResp,
"serr");
2565 printf(
"jumppos (%u):\n", njumps);
2566 cpl_array_dump(jumppos, 0, 10000, stdout);
2567 printf(
"jumplen:\n");
2568 cpl_array_dump(jumplen, 0, 10000, stdout);
2575 for (iarr = 0; iarr < njumps; iarr++) {
2576 double lambda = cpl_array_get_double(jumppos, iarr, NULL),
2577 ldist = cpl_array_get_double(jumplen, iarr, NULL) / 2;
2579 cpl_table_select_all(aResp);
2580 cpl_table_and_selected_double(aResp,
"lambda", CPL_NOT_LESS_THAN, lambda - 5.);
2581 cpl_table_and_selected_double(aResp,
"lambda", CPL_NOT_GREATER_THAN, lambda + 5.);
2582 int nsel = cpl_table_count_selected(aResp);
2584 cpl_msg_debug(__func__,
"Only %d points near jump around %.1f Angstrom, " 2585 "not doing anything", nsel, lambda);
2588 cpl_table *xresp = cpl_table_extract_selected(aResp);
2589 double stdev = cpl_table_get_column_stdev(xresp,
"response");
2591 cpl_table_dump(xresp, 0, nsel, stdout);
2594 cpl_table_delete(xresp);
2595 if (stdev < 0.001) {
2596 cpl_msg_debug(__func__,
"%d points near jump around %.1f Angstrom, stdev " 2597 "only %f, not doing anything", nsel, lambda, stdev);
2600 cpl_msg_debug(__func__,
"%d points near jump around %.1f Angstrom, stdev " 2601 "%f, erasing the region", nsel, lambda, stdev);
2605 cpl_table_select_all(aResp);
2606 cpl_table_and_selected_double(aResp,
"lambda", CPL_NOT_LESS_THAN, lambda - ldist);
2607 cpl_table_and_selected_double(aResp,
"lambda", CPL_NOT_GREATER_THAN, lambda + ldist);
2608 cpl_table_erase_selected(aResp);
2610 cpl_array_delete(jumppos);
2611 cpl_array_delete(jumplen);
2647 cpl_ensure_code(aFluxObj && aFluxObj->
sensitivity, CPL_ERROR_NULL_INPUT);
2650 int nrow = cpl_table_get_nrow(aFluxObj->
sensitivity);
2653 const double *lambdas = cpl_table_get_data_double_const(aFluxObj->
sensitivity,
2655 *sens = cpl_table_get_data_double_const(aFluxObj->
sensitivity,
2657 *serr = cpl_table_get_data_double_const(aFluxObj->
sensitivity,
2659 cpl_table_copy_data_double(resp,
"lambda", lambdas);
2660 cpl_table_copy_data_double(resp,
"response", sens);
2661 cpl_table_copy_data_double(resp,
"resperr", serr);
2668 double blueend = kMuseLambdaMinX,
2669 gapmin = 0.1, gapmax = -0.1;
2671 cpl_propertylist_has(aFluxObj->
cube->
header, MUSE_HDR_FLUX_FFCORR)) {
2673 if (mode == MUSE_MODE_WFM_NONAO_N) {
2675 blueend = kMuseNominalCutoffKink;
2676 }
else if ((mode == MUSE_MODE_WFM_AO_N) || (mode == MUSE_MODE_NFM_AO_N)) {
2678 blueend = kMuseAOCutoffKink;
2679 gapmin = kMuseNa2LambdaMin;
2680 gapmax = kMuseNa2LambdaMax;
2681 }
else if (mode == MUSE_MODE_WFM_AO_E) {
2683 gapmin = kMuseNaLambdaMin;
2684 gapmax = kMuseNaLambdaMax;
2690 cpl_msg_info(__func__,
"Smoothing response curve with median filter");
2692 cpl_table_duplicate_column(resp,
"response_unsmoothed", resp,
"response");
2693 cpl_table_duplicate_column(resp,
"resperr_unsmoothed", resp,
"resperr");
2695 muse_flux_get_response_table_smooth(resp, 15., blueend, kMuseLambdaMaxX,
2696 gapmin, gapmax, CPL_FALSE);
2698 cpl_msg_info(__func__,
"Smoothing response curve with piecewise polynomial");
2700 cpl_table_duplicate_column(resp,
"response_unsmoothed", resp,
"response");
2701 cpl_table_duplicate_column(resp,
"resperr_unsmoothed", resp,
"resperr");
2703 muse_flux_get_response_table_piecewise_poly(resp, blueend, kMuseLambdaMaxX,
2704 gapmin, gapmax, 150., 3.);
2707 muse_flux_get_response_table_smooth(resp, 15., blueend, kMuseLambdaMaxX,
2708 gapmin, gapmax, CPL_TRUE);
2710 cpl_msg_warning(__func__,
"NOT smoothing the response curve at all!");
2717 if (aFluxObj->
cube) {
2724 "|^NAXIS|BUNIT", 0);
2728 return CPL_ERROR_NONE;
2754 cpl_ensure_code(aFluxObj && aFluxObj->
sensitivity, CPL_ERROR_NULL_INPUT);
2756 int nrow = cpl_table_get_nrow(tsens);
2759 cpl_table_fill_column_window_double(tell,
"lambda", 0, nrow, 0);
2760 cpl_table_copy_data_double(tell,
"lambda",
2761 cpl_table_get_data_double_const(tsens,
"lambda"));
2762 cpl_table_fill_column_window_double(tell,
"ftelluric", 0, nrow, 0);
2763 cpl_table_copy_data_double(tell,
"ftelluric",
2764 cpl_table_get_data_double_const(tsens,
"tellcor"));
2766 #define TELL_MAX_ERR 0.1 2767 #define TELL_MIN_ERR 1e-4 2768 cpl_table_fill_column_window_double(tell,
"ftellerr", 0, nrow, TELL_MAX_ERR);
2772 cpl_table_duplicate_column(tell,
"tellcor", tsens,
"tellcor");
2774 cpl_table_unselect_all(tell);
2776 for (irow = 0; irow < nrow; irow++) {
2778 cpl_table_get_double(tell,
"tellcor", irow, &err);
2783 cpl_errorstate state = cpl_errorstate_get();
2784 double ftellcor = cpl_table_get_double(tell,
"tellcor", irow - 1, &err);
2785 if (!cpl_errorstate_is_equal(state)) {
2786 cpl_errorstate_set(state);
2788 if (err == 0 && ftellcor != 1.) {
2789 cpl_table_set_double(tell,
"ftelluric", irow, 1.);
2793 state = cpl_errorstate_get();
2794 ftellcor = cpl_table_get_double(tell,
"tellcor", irow + 1, &err);
2795 if (!cpl_errorstate_is_equal(state)) {
2796 cpl_errorstate_set(state);
2798 if (err == 0 && ftellcor != 1.) {
2799 cpl_table_set_double(tell,
"ftelluric", irow, 1.);
2802 cpl_table_select_row(tell, irow);
2804 cpl_table_erase_selected(tell);
2805 cpl_table_erase_column(tell,
"tellcor");
2808 nrow = cpl_table_get_nrow(tell);
2809 for (irow = 0; irow < nrow; irow++) {
2811 double dftell = 1. - cpl_table_get_double(tell,
"ftelluric", irow, &err),
2812 ftellerr = cpl_table_get_double(tell,
"ftellerr", irow, &err);
2813 if (ftellerr > dftell) {
2814 ftellerr = fmax(dftell, TELL_MIN_ERR);
2815 cpl_table_set_double(tell,
"ftellerr", irow, ftellerr);
2824 "|^NAXIS|BUNIT", 0);
2826 return CPL_ERROR_NONE;
2861 CPL_ERROR_NULL_INPUT);
2862 cpl_boolean hasresp = aFluxObj->
response != NULL;
2868 cpl_errorstate state = cpl_errorstate_get();
2871 cpl_msg_warning(__func__,
"Unknown standard star in exposure (missing " 2873 cpl_errorstate_set(state);
2876 cpl_propertylist_update_string(aFluxObj->
response->
header, QC_STD_NAME,
2884 cpl_ensure_code(thruput, CPL_ERROR_DATA_NOT_FOUND);
2886 cpl_msg_info(__func__,
"Computing throughput using effective VLT area of %.1f" 2887 " cm**2, from %s curve", kVLTArea,
2888 hasresp ?
"smoothed response" :
"unsmoothed sensitivity");
2890 const double h = CPL_PHYS_H * 1e7,
2891 c = CPL_PHYS_C * 1e10;
2892 if (!cpl_table_has_column(thruput,
"throughput")) {
2893 cpl_table_new_column(thruput,
"throughput", CPL_TYPE_DOUBLE);
2896 int i, n = cpl_table_get_nrow(thruput);
2897 for (i = 0; i < n; i++) {
2899 double lambda = cpl_table_get(thruput,
"lambda", i, &err1),
2900 sens = cpl_table_get(thruput, hasresp ?
"response" :
"sens", i, &err2);
2902 cpl_table_set_invalid(thruput,
"throughput", i);
2905 double value = h * c / kVLTArea / lambda * pow(10., 0.4 * sens);
2907 cpl_msg_debug(__func__,
"%.3f %.3f --> %e, %e ==> %.3f", lambda,
2908 sens, h * c / kVLTArea / lambda, pow(10., 0.4 * sens),
2911 cpl_table_set_double(thruput,
"throughput", i, value);
2915 cpl_msg_indent_more();
2917 for (lambda = 5000.; lambda < 9100.; lambda += 1000.) {
2918 cpl_table_unselect_all(thruput);
2919 cpl_table_or_selected_double(thruput,
"lambda", CPL_NOT_LESS_THAN,
2921 cpl_table_and_selected_double(thruput,
"lambda", CPL_NOT_GREATER_THAN,
2923 cpl_table *t = cpl_table_extract_selected(thruput);
2924 double mean = cpl_table_get_column_mean(t,
"throughput"),
2925 stdev = cpl_table_get_column_stdev(t,
"throughput");
2926 cpl_msg_info(__func__,
"Throughput at %.0f +/- 100 Angstrom: %.4f +/- %.4f",
2927 lambda, mean, stdev);
2928 cpl_table_delete(t);
2932 char *kw = cpl_sprintf(QC_STD_THRU, lambda);
2933 cpl_propertylist_update_float(aFluxObj->
response->
header, kw, mean);
2937 cpl_msg_indent_less();
2939 return CPL_ERROR_NONE;
2977 CPL_ERROR_NULL_INPUT);
2980 char *p = aName ? strrchr(aName,
'_') : NULL;
2981 const char *fshort = NULL;
2986 cpl_msg_warning(__func__,
"%s filter given for QC zeropoint computation!",
2989 char *kw = cpl_sprintf(QC_STD_ZP, fshort);
2992 cpl_boolean hasresp = aFluxObj->
response != NULL;
2995 const double h = CPL_PHYS_H * 1e7,
2996 c = CPL_PHYS_C * 1e10;
3002 int i, n = cpl_table_get_nrow(thruput);
3003 for (i = 0; i < n; i++) {
3005 double lambda = cpl_table_get(thruput,
"lambda", i, &err1),
3006 sens = cpl_table_get(thruput, hasresp ?
"response" :
"sens", i, &err2),
3016 fobs += pow(10., 0.4 * sens)
3019 * h * c / kVLTArea / lambda;
3021 fref += ref * fweight;
3026 double zp = -2.5 * log10(fobs / fref);
3027 cpl_msg_indent_more();
3028 cpl_msg_info(__func__,
"Zeropoint in filter %s: %.3f mag (throughput %.3f)",
3029 fshort, zp, pow(10., -0.4 * zp));
3030 cpl_msg_indent_less();
3033 cpl_propertylist_update_float(aFluxObj->
response->
header, kw, zp);
3037 return CPL_ERROR_NONE;
3084 const cpl_table *aExtinction,
const muse_table *aTelluric)
3086 cpl_ensure_code(aPixtable && aPixtable->
header && aResponse,
3087 CPL_ERROR_NULL_INPUT);
3088 const char *unitdata = cpl_table_get_column_unit(aPixtable->
table,
3090 cpl_ensure_code(unitdata && !strncmp(unitdata,
"count", 6),
3091 CPL_ERROR_INCOMPATIBLE_INPUT);
3095 cpl_table *response = NULL,
3099 #define FF_MSG "(flat-field spectrum %scorrected)" 3101 response = aResponse->
table;
3104 cpl_boolean respff = cpl_propertylist_has(aResponse->
header,
3105 MUSE_HDR_FLUX_FFCORR),
3106 ptff = cpl_propertylist_has(aPixtable->
header,
3108 if ((respff && !ptff) || (!respff && ptff)) {
3109 return cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
3110 "Cannot apply this %s "FF_MSG
" to this %s "FF_MSG,
3111 MUSE_TAG_STD_RESPONSE, !respff ?
"un" :
"",
3112 catg, !ptff ?
"un" :
"");
3117 cpl_msg_warning(__func__,
"Airmass unknown, not doing extinction " 3118 "correction: %s", cpl_error_get_message());
3124 telluric = cpl_table_duplicate(aTelluric->
table);
3127 cpl_table_power_column(telluric,
"ftelluric", -airmass);
3129 cpl_boolean tellff = cpl_propertylist_has(aTelluric->
header,
3130 MUSE_HDR_FLUX_FFCORR),
3131 ptff = cpl_propertylist_has(aPixtable->
header,
3133 if ((tellff && !ptff) || (!tellff && ptff)) {
3134 cpl_msg_warning(__func__,
"Applying %s "FF_MSG
" to %s "FF_MSG
", this may " 3135 "not be correct!", MUSE_TAG_STD_TELLURIC,
3136 !tellff ?
"un" :
"", catg, !ptff ?
"un" :
"");
3142 cpl_msg_warning(__func__,
"%s missing!", MUSE_TAG_EXTINCT_TABLE);
3146 if (exptime <= 0.) {
3147 cpl_msg_warning(__func__,
"Non-positive EXPTIME, not doing flux calibration!");
3148 cpl_table_delete(telluric);
3149 return CPL_ERROR_ILLEGAL_INPUT;
3152 cpl_msg_info(__func__,
"Starting flux calibration (exptime=%.2fs, airmass=%.4f)," 3153 " %s telluric correction", exptime, airmass,
3154 aTelluric ?
"with" :
"without ("MUSE_TAG_STD_TELLURIC
" not given)");
3159 #pragma omp parallel for default(none) \ 3160 shared(aExtinction, airmass, data, exptime, lambda, nrow, response, \ 3162 for (i = 0; i < nrow; i++) {
3164 double ddata = data[i], dstat = stat[i];
3168 double fext = pow(10., 0.4 * airmass
3173 printf(
"%f, data/stat = %f/%f -> ", fext, ddata, dstat);
3176 dstat *= (fext * fext);
3178 printf(
" --> %f/%f\n", ddata, dstat), fflush(NULL);
3185 double dlambda = kMuseSpectralSamplingA,
3192 resp = pow(10., 0.4 * resp);
3195 rerr = rerr * CPL_MATH_LN10 * resp / 2.5;
3197 printf(
"%f/%f/%f, %e/%e, data/stat = %e/%e -> ", lambda[i], dlambda, dlamerr, resp, rerr,
3200 dstat = dstat * pow((1./(resp * exptime * dlambda)), 2)
3201 + pow(ddata * rerr / (resp*resp * exptime * dlambda), 2)
3202 + pow(ddata * dlamerr / (resp * exptime * dlambda*dlambda), 2);
3203 ddata /= (resp * exptime * dlambda);
3205 printf(
"%e/%e\n", ddata, dstat), fflush(NULL);
3210 ddata *= kMuseFluxUnitFactor;
3211 dstat *= kMuseFluxStatFactor;
3215 if (lambda[i] < kTelluricBands[0][0] || !telluric) {
3223 data[i] = ddata * tell;
3224 stat[i] = tell*tell * dstat + ddata*ddata * terr*terr;
3226 cpl_table_delete(telluric);
3230 kMuseFluxUnitString);
3232 kMuseFluxStatString);
3238 MUSE_HDR_PT_FLUXCAL_COMMENT);
3239 return CPL_ERROR_NONE;
Structure definition of a MUSE datacube.
cpl_error_code muse_flux_compute_qc(muse_flux_object *aFluxObj)
Compute QC parameters, related to on-sky throughput.
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
double muse_pfits_get_cd(const cpl_propertylist *aHeaders, unsigned int aAxisI, unsigned int aAxisJ)
find out the WCS coordinate at the reference point
muse_flux_smooth_type
Type of response curve smoothing to use.
double muse_pfits_get_crval(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS coordinate at the reference point
double muse_pfits_get_ra(const cpl_propertylist *aHeaders)
find out the right ascension
#define MUSE_HDR_PT_FLUXCAL
cpl_image * data
the data extension
const muse_cpltable_def muse_flux_responsetable_def[]
MUSE response table definition.
cpl_size muse_pixtable_get_nrow(const muse_pixtable *aPixtable)
get the number of rows within the pixel table
muse_flux_profile_type
Type of optimal profile to use.
muse_datacube * muse_resampling_cube(muse_pixtable *aPixtable, muse_resampling_params *aParams, muse_pixgrid **aGrid)
Resample a pixel table onto a regular grid structure representing a FITS NAXIS=3 datacube.
cpl_image * stat
the statistics extension
void muse_datacube_delete(muse_datacube *aCube)
Deallocate memory associated to a muse_datacube object.
#define MUSE_HDR_PT_FFCORR
cpl_error_code muse_flux_reference_table_check(cpl_table *aTable)
Check and/or adapt the standard flux reference table format.
const char * muse_pfits_get_bunit(const cpl_propertylist *aHeaders)
find out the unit string
muse_table * muse_table_new(void)
Allocate memory for a new muse_table object.
const muse_cpltable_def muse_response_tellbands_def[]
Table definition for a telluric bands table.
Structure definition of MUSE three extension FITS file.
cpl_error_code muse_flux_calibrate(muse_pixtable *aPixtable, const muse_table *aResponse, const cpl_table *aExtinction, const muse_table *aTelluric)
Convert the input pixel table from counts to fluxes.
muse_flux_object * muse_flux_object_new(void)
Allocate memory for a new muse_flux_object object.
static double muse_flux_reference_table_sampling(cpl_table *aTable)
Compute average sampling for a MUSE-format flux reference table.
cpl_table * table
The pixel table.
const char * muse_pfits_get_cunit(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS axis unit string
cpl_propertylist * header
the FITS header
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.
cpl_error_code muse_datacube_save(muse_datacube *aCube, const char *aFilename)
Save the three cube extensions and the FITS headers of a MUSE datacube to a file. ...
#define MUSE_PIXTABLE_DATA
muse_resampling_crstats_type crtype
cpl_image * dq
the data quality extension
const char * muse_pfits_get_targname(const cpl_propertylist *aHeaders)
find out the ESO observation target name
cpl_table * muse_cpltable_new(const muse_cpltable_def *aDef, cpl_size aLength)
Create an empty table according to the specified definition.
double muse_pfits_get_crpix(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS reference point
cpl_error_code muse_flux_get_telluric_table(muse_flux_object *aFluxObj)
Get the table of the telluric correction.
cpl_error_code muse_flux_compute_qc_zp(muse_flux_object *aFluxObj, const muse_table *aFilter, const char *aName)
Compute QC zeropoint for given filter.
double muse_flux_response_interpolate(const cpl_table *aResponse, double aLambda, double *aError, muse_flux_interpolation_type aType)
Compute linearly interpolated response of some kind at given wavelength.
double muse_pfits_get_fwhm_end(const cpl_propertylist *aHeaders)
find out the ambient seeing at end of exposure (in arcsec)
Structure definition of MUSE pixel table.
Flux object to store data needed while computing the flux calibration.
double muse_astro_wavelength_vacuum_to_air(double aVac)
Compute air wavelength for a given vacuum wavelength.
#define MUSE_WCS_KEYS
regular expression for WCS properties
cpl_error_code muse_wcs_celestial_from_pixel(cpl_propertylist *aHeader, double aX, double aY, double *aRA, double *aDEC)
Convert from pixel coordinates to celestial spherical coordinates.
cpl_error_code muse_utils_fit_moffat_2d(const cpl_matrix *aPositions, const cpl_vector *aValues, const cpl_vector *aErrors, cpl_array *aParams, cpl_array *aPErrors, const cpl_array *aPFlags, double *aRMS, double *aRedChisq)
Fit a 2D Moffat function to a given set of data.
cpl_error_code muse_flux_response_compute(muse_flux_object *aFluxObj, muse_flux_selection_type aSelect, double aAirmass, const cpl_table *aReference, const cpl_table *aTellBands, const cpl_table *aExtinct)
Compare measured flux distribution over wavelength with calibrated stellar fluxes and derive instrume...
muse_resampling_params * muse_resampling_params_new(muse_resampling_type aMethod)
Create the resampling parameters structure.
cpl_polynomial * muse_utils_iterate_fit_polynomial(cpl_matrix *aPos, cpl_vector *aVal, cpl_vector *aErr, cpl_table *aExtra, const unsigned int aOrder, const double aRSigma, double *aMSE, double *aChiSq)
Iterate a polynomial fit.
cpl_table * table
The table.
double muse_pfits_get_fwhm_start(const cpl_propertylist *aHeaders)
find out the ambient seeing at start of exposure (in arcsec)
cpl_imagelist * data
the cube containing the actual data values
double muse_pfits_get_dec(const cpl_propertylist *aHeaders)
find out the declination
muse_flux_interpolation_type
Type of table interpolation to use.
Structure to store a table together with a property list.
cpl_error_code muse_flux_get_response_table(muse_flux_object *aFluxObj, muse_flux_smooth_type aSmooth)
Get the table of the standard star response function.
void muse_table_delete(muse_table *aTable)
Deallocate memory associated to a muse_table object.
cpl_imagelist * dq
the optional cube containing the bad pixel status
cpl_propertylist * header
the header
const muse_cpltable_def muse_flux_tellurictable_def[]
MUSE telluric correction table definition.
const char * muse_pfits_get_ctype(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS axis type string
cpl_error_code muse_pixtable_save(muse_pixtable *aPixtable, const char *aFilename)
Save a MUSE pixel table to a file on disk.
muse_image * muse_flux_integrate_cube(muse_datacube *aCube, cpl_apertures *aApertures, muse_flux_profile_type aProfile)
Integrate the flux of the standard star(s) given a datacube.
cpl_error_code muse_flux_integrate_std(muse_pixtable *aPixtable, muse_flux_profile_type aProfile, muse_flux_object *aFluxObj)
Reconstruct a cube, detect the standard star, and integrate its flux.
cpl_propertylist * header
the FITS header
#define MUSE_PIXTABLE_STAT
#define MUSE_PIXTABLE_LAMBDA
double muse_pfits_get_exptime(const cpl_propertylist *aHeaders)
find out the exposure time
const char * muse_pfits_get_pro_catg(const cpl_propertylist *aHeaders)
find out the PRO category
cpl_propertylist * muse_wcs_apply_cd(const cpl_propertylist *aExpHeader, const cpl_propertylist *aCalHeader)
Apply the CDi_j matrix of an astrometric solution to an observation.
Definition of a cpl table structure.
void muse_resampling_params_delete(muse_resampling_params *aParams)
Delete a resampling parameters structure.
double muse_cplvector_get_adev_const(const cpl_vector *aVector, double aCenter)
Compute the average absolute deviation of a (constant) vector.
muse_image * muse_image_new(void)
Allocate memory for a new muse_image object.
muse_flux_selection_type
Type of star selection to use.
void muse_flux_object_delete(muse_flux_object *aFluxObj)
Deallocate memory associated to a muse_flux_object.
int muse_quality_image_reject_using_dq(cpl_image *aData, cpl_image *aDQ, cpl_image *aStat)
Reject pixels of one or two images on a DQ image.
double muse_astro_angular_distance(double aRA1, double aDEC1, double aRA2, double aDEC2)
Compute angular distance in the sky between two positions.
muse_ins_mode muse_pfits_get_mode(const cpl_propertylist *aHeaders)
find out the observation mode
double muse_astro_airmass(cpl_propertylist *aHeader)
Derive the effective airmass of an observation from information in a FITS header. ...
cpl_imagelist * stat
the cube containing the data variance
cpl_propertylist * header
The FITS header.
cpl_propertylist * muse_wcs_create_default(void)
Create FITS headers containing a default (relative) WCS.