00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifdef HAVE_CONFIG_H
00023 #include <config.h>
00024 #endif
00025
00026
00027
00028
00029 #include <string.h>
00030 #include <strings.h>
00031 #include "muse_postproc.h"
00032 #include "muse_instrument.h"
00033
00034 #include "muse_astro.h"
00035 #include "muse_dar.h"
00036 #include "muse_flux.h"
00037 #include "muse_imagelist.h"
00038 #include "muse_pfits.h"
00039 #include "muse_phys.h"
00040 #include "muse_rvcorrect.h"
00041 #include "muse_utils.h"
00042 #include "muse_wcs.h"
00043 #include "muse_idp.h"
00044
00045 #include "muse_lsf_params.h"
00046 #ifdef USE_LSF_PARAMS
00047 #include "muse_sky_old.h"
00048 #endif
00049
00050 #undef CREATE_IDP
00051
00052
00059
00060
00063
00074
00075 muse_postproc_properties *
00076 muse_postproc_properties_new(muse_postproc_type aType)
00077 {
00078 muse_postproc_properties *prop = cpl_calloc(1, sizeof(muse_postproc_properties));
00079 switch (aType) {
00080 case MUSE_POSTPROC_SCIPOST:
00081 case MUSE_POSTPROC_STANDARD:
00082 case MUSE_POSTPROC_ASTROMETRY:
00083 prop->type = aType;
00084 break;
00085 default:
00086 cpl_msg_error(__func__, "No such setup known: %d", aType);
00087 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
00088 cpl_free(prop);
00089 return NULL;
00090 }
00091
00092 return prop;
00093 }
00094
00095
00101
00102 void
00103 muse_postproc_properties_delete(muse_postproc_properties *aProp)
00104 {
00105 if (!aProp) {
00106 return;
00107 }
00108 cpl_table_delete(aProp->exposures);
00109 cpl_table_delete(aProp->response);
00110 cpl_table_delete(aProp->extinction);
00111 cpl_table_delete(aProp->telluric);
00112 cpl_propertylist_delete(aProp->wcs);
00113 muse_lsf_cube_delete_all(aProp->lsf_cube);
00114 #ifdef USE_LSF_PARAMS
00115 muse_lsf_params_delete_all(aProp->lsf_params);
00116 #endif
00117 cpl_table_delete(aProp->sky_lines);
00118 cpl_table_delete(aProp->sky_continuum);
00119 muse_mask_delete(aProp->sky_mask);
00120 cpl_frame_delete(aProp->refframe);
00121 cpl_table_delete(aProp->tellregions);
00122 cpl_free(aProp);
00123 }
00124
00125
00132
00133 cpl_boolean
00134 muse_postproc_check_save_param(const char *aSave, const char *aValid)
00135 {
00136 cpl_ensure(aSave, CPL_ERROR_NULL_INPUT, CPL_FALSE);
00137 if (strlen(aSave) < 4) {
00138 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
00139 "no (valid) save option given!");
00140 return CPL_FALSE;
00141 }
00142
00143 cpl_boolean allvalid = CPL_TRUE;
00144 cpl_array *asave = muse_cplarray_new_from_delimited_string(aSave, ","),
00145 *avalid = muse_cplarray_new_from_delimited_string(aValid, ",");
00146 int i, j,
00147 ns = cpl_array_get_size(asave),
00148 nv = cpl_array_get_size(avalid);
00149 for (i = 0; i < ns; i++) {
00150 cpl_boolean valid = CPL_FALSE;
00151 for (j = 0; j < nv; j++) {
00152 if (!strcmp(cpl_array_get_string(asave, i),
00153 cpl_array_get_string(avalid, j))) {
00154 valid = CPL_TRUE;
00155 }
00156 }
00157 if (!valid) {
00158 cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT,
00159 "save option %d (%s) is not valid!", i + 1,
00160 cpl_array_get_string(asave, i));
00161 allvalid = CPL_FALSE;
00162 }
00163 }
00164 cpl_array_delete(asave);
00165 cpl_array_delete(avalid);
00166 return allvalid;
00167 }
00168
00169
00184
00185 muse_resampling_type
00186 muse_postproc_get_resampling_type(const char *aResampleString)
00187 {
00188 cpl_ensure(aResampleString, CPL_ERROR_NULL_INPUT, MUSE_RESAMPLE_WEIGHTED_DRIZZLE);
00189 if (!strncmp(aResampleString, "nearest", 8)) {
00190 return MUSE_RESAMPLE_NEAREST;
00191 }
00192 if (!strncmp(aResampleString, "linear", 7)) {
00193 return MUSE_RESAMPLE_WEIGHTED_LINEAR;
00194 }
00195 if (!strncmp(aResampleString, "quadratic", 10)) {
00196 return MUSE_RESAMPLE_WEIGHTED_QUADRATIC;
00197 }
00198 if (!strncmp(aResampleString, "renka", 6)) {
00199 return MUSE_RESAMPLE_WEIGHTED_RENKA;
00200 }
00201 if (!strncmp(aResampleString, "drizzle", 8)) {
00202 return MUSE_RESAMPLE_WEIGHTED_DRIZZLE;
00203 }
00204 if (!strncmp(aResampleString, "lanczos", 8)) {
00205 return MUSE_RESAMPLE_WEIGHTED_LANCZOS;
00206 }
00207 return MUSE_RESAMPLE_WEIGHTED_DRIZZLE;
00208 }
00209
00210
00225
00226 muse_resampling_crstats_type
00227 muse_postproc_get_cr_type(const char *aCRTypeString)
00228 {
00229 cpl_ensure(aCRTypeString, CPL_ERROR_NULL_INPUT, MUSE_RESAMPLING_CRSTATS_IRAF);
00230 if (!strncmp(aCRTypeString, "iraf", 5)) {
00231 return MUSE_RESAMPLING_CRSTATS_IRAF;
00232 }
00233 if (!strncmp(aCRTypeString, "mean", 5)) {
00234 return MUSE_RESAMPLING_CRSTATS_MEAN;
00235 }
00236 if (!strncmp(aCRTypeString, "median", 7)) {
00237 return MUSE_RESAMPLING_CRSTATS_MEDIAN;
00238 }
00239 return MUSE_RESAMPLING_CRSTATS_MEDIAN;
00240 }
00241
00242
00256
00257 muse_cube_type
00258 muse_postproc_get_cube_format(const char *aFormatString)
00259 {
00260 cpl_ensure(aFormatString, CPL_ERROR_NULL_INPUT, MUSE_CUBE_TYPE_FITS);
00261 if (!strncmp(aFormatString, "Cube", 5)) {
00262 return MUSE_CUBE_TYPE_FITS;
00263 }
00264 if (!strncmp(aFormatString, "Euro3D", 7)) {
00265 return MUSE_CUBE_TYPE_EURO3D;
00266 }
00267 if (!strncmp(aFormatString, "xCube", 6)) {
00268 return MUSE_CUBE_TYPE_FITS_X;
00269 }
00270 if (!strncmp(aFormatString, "xEuro3D", 8)) {
00271 return MUSE_CUBE_TYPE_EURO3D_X;
00272 }
00273 if (!strncmp(aFormatString, "sdpCube", 8)) {
00274 return MUSE_CUBE_TYPE_SDP;
00275 }
00276 return MUSE_CUBE_TYPE_FITS;
00277 }
00278
00279
00293
00294 muse_xcombine_types
00295 muse_postproc_get_weight_type(const char *aWeightString)
00296 {
00297 cpl_ensure(aWeightString, CPL_ERROR_NULL_INPUT, MUSE_XCOMBINE_EXPTIME);
00298 if (!strncmp(aWeightString, "exptime", 8)) {
00299 return MUSE_XCOMBINE_EXPTIME;
00300 }
00301 if (!strncmp(aWeightString, "fwhm", 5)) {
00302 return MUSE_XCOMBINE_FWHM;
00303 }
00304 if (!strncmp(aWeightString, "none", 5)) {
00305 return MUSE_XCOMBINE_NONE;
00306 }
00307 return MUSE_XCOMBINE_EXPTIME;
00308 }
00309
00310
00335
00336 cpl_table *
00337 muse_postproc_load_nearest(const cpl_propertylist *aHeader,
00338 const cpl_frame *aFrame, float aWarnLimit,
00339 float aErrLimit, double *aRA, double *aDEC)
00340 {
00341 cpl_ensure(aHeader && aFrame, CPL_ERROR_NULL_INPUT, NULL);
00342 cpl_errorstate state = cpl_errorstate_get();
00343 double raref = muse_pfits_get_ra(aHeader),
00344 decref = muse_pfits_get_dec(aHeader);
00345 cpl_ensure(cpl_errorstate_is_equal(state), CPL_ERROR_DATA_NOT_FOUND, NULL);
00346 cpl_msg_debug(__func__, "reference coordinates: RA = %e deg, DEC =%e deg",
00347 raref, decref);
00348 if (aRA) {
00349 *aRA = raref;
00350 }
00351 if (aDEC) {
00352 *aDEC = decref;
00353 }
00354
00355 const char *fn = cpl_frame_get_filename(aFrame);
00356 cpl_propertylist *header;
00357 double dmin = FLT_MAX;
00358 int iext, inearest = -1, next = cpl_fits_count_extensions(fn);
00359 for (iext = 1; iext <= next; iext++) {
00360 header = cpl_propertylist_load(fn, iext);
00361 const char *extname = cpl_propertylist_get_string(header, "EXTNAME");
00362 double ra = muse_pfits_get_ra(header),
00363 dec = muse_pfits_get_dec(header),
00364 d = muse_astro_angular_distance(ra, dec, raref, decref);
00365 cpl_msg_debug(__func__, "extension %d [%s]: RA = %e deg, DEC = %e deg --> "
00366 "d = %e deg", iext, extname, ra, dec, d);
00367 if (d < dmin) {
00368 dmin = d;
00369 inearest = iext;
00370 }
00371 cpl_propertylist_delete(header);
00372 }
00373
00374 if (dmin * 3600. > aErrLimit) {
00375 char *msg = cpl_sprintf("Distance of nearest reference table to observed "
00376 "position is %.2f arcmin, certainly a wrong "
00377 "reference object!", dmin * 60.);
00378 cpl_msg_error(__func__, "%s", msg);
00379 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "%s", msg);
00380 cpl_free(msg);
00381 return NULL;
00382 }
00383 if (dmin * 3600. > aWarnLimit) {
00384 cpl_msg_warning(__func__, "Distance of nearest reference table to observed "
00385 "position is %.2f arcmin! (Wrong reference object?)",
00386 dmin * 60.);
00387 }
00388 header = cpl_propertylist_load(fn, inearest);
00389 const char *extname = cpl_propertylist_get_string(header, "EXTNAME");
00390 cpl_msg_info(__func__, "Loading \"%s[%s]\" (distance %.1f arcsec)", fn,
00391 extname, dmin * 3600.);
00392 cpl_propertylist_delete(header);
00393 cpl_table *table = cpl_table_load(fn, inearest, 1);
00394 return table;
00395 }
00396
00397
00440
00441 void *
00442 muse_postproc_process_exposure(muse_postproc_properties *aProp,
00443 unsigned int aIndex,
00444 muse_postproc_sky_outputs *aSkyOut)
00445 {
00446 cpl_ensure(aProp && aProp->exposures, CPL_ERROR_NULL_INPUT, NULL);
00447 cpl_ensure((int)aIndex < cpl_table_get_nrow(aProp->exposures),
00448 CPL_ERROR_ILLEGAL_INPUT, NULL);
00449 cpl_msg_info(__func__, "Starting to process exposure %d (DATE-OBS=%s)",
00450 aIndex + 1, cpl_table_get_string(aProp->exposures, "DATE-OBS",
00451 aIndex));
00452 cpl_ensure(aProp->exposures, CPL_ERROR_NULL_INPUT, NULL);
00453
00454 cpl_table *exposure = cpl_table_extract(aProp->exposures, aIndex, 1);
00455 muse_pixtable *pt = muse_pixtable_load_merge_channels(exposure,
00456 aProp->lambdamin,
00457 aProp->lambdamax);
00458 cpl_table_delete(exposure);
00459 if (!pt) {
00460 return NULL;
00461 }
00462
00463 cpl_propertylist_erase_regexp(pt->header, "ESO QC ", 0);
00464
00465
00466
00467 cpl_boolean labdata = CPL_FALSE;
00468 cpl_errorstate prestate = cpl_errorstate_get();
00469 double airmass = muse_astro_airmass(pt->header);
00470 double ra = muse_pfits_get_ra(pt->header),
00471 dec = muse_pfits_get_dec(pt->header);
00472 if (!cpl_errorstate_is_equal(prestate) || airmass < 1.) {
00473 cpl_msg_warning(__func__, "This seems to be lab data (RA=%f DEC=%f, "
00474 "airmass=%f), not doing any DAR correction!", ra, dec,
00475 airmass);
00476 cpl_errorstate_dump(prestate, CPL_FALSE, NULL);
00477 cpl_errorstate_set(prestate);
00478 labdata = CPL_TRUE;
00479 }
00480 cpl_error_code rc = CPL_ERROR_NONE;
00481 if (!labdata && muse_pfits_get_mode(pt->header) <= MUSE_MODE_WFM_AO_N) {
00482 cpl_msg_debug(__func__, "WFM detected: starting DAR correction");
00483 rc = muse_dar_correct(pt, aProp->lambdaref);
00484 cpl_msg_debug(__func__, "DAR correction returned rc=%d: %s", rc,
00485 rc != CPL_ERROR_NONE ? cpl_error_get_message() : "");
00486
00487 if (aProp->darcheck != MUSE_POSTPROC_DARCHECK_NONE) {
00488 cpl_boolean dorefine = aProp->darcheck == MUSE_POSTPROC_DARCHECK_CORRECT;
00489 cpl_msg_info(__func__, "Carrying out DAR %s", dorefine ? "correction" : "check");
00490 double maxshift = 0;
00491 rc = muse_dar_check(pt, &maxshift, dorefine, NULL);
00492 if (rc != CPL_ERROR_NONE) {
00493 cpl_msg_warning(__func__, "Maximum detected shift %f\'\' (rc = %d: %s)",
00494 maxshift, rc, cpl_error_get_message());
00495 } else {
00496 cpl_msg_info(__func__, "Maximum detected shift %f\'\'", maxshift);
00497 }
00498 }
00499 }
00500
00501
00502 if (aProp->type != MUSE_POSTPROC_STANDARD) {
00503 if (aProp->response) {
00504 rc = muse_flux_calibrate(pt, aProp->response, aProp->extinction,
00505 aProp->telluric);
00506 cpl_msg_debug(__func__, "Flux calibration returned rc=%d: %s", rc,
00507 rc != CPL_ERROR_NONE ? cpl_error_get_message_default(rc) : "");
00508 } else {
00509 cpl_msg_info(__func__, "Skipping flux calibration (no %s curve)",
00510 MUSE_TAG_STD_RESPONSE);
00511 }
00512 }
00513
00514 cpl_table *sky_cont = NULL,
00515 *sky_lines = NULL;
00516 if ((aProp->skymethod == MUSE_POSTPROC_SKYMETHOD_MODEL && aProp->sky_lines) ||
00517 (aProp->skymethod == MUSE_POSTPROC_SKYMETHOD_SIMPLE)) {
00518
00519 muse_pixtable *sky_pt = muse_pixtable_duplicate(pt);
00520 if (aProp->sky_mask) {
00521 cpl_table_select_all(sky_pt->table);
00522 muse_pixtable_and_selected_mask(sky_pt, aProp->sky_mask);
00523 }
00524
00525 cpl_msg_info(__func__, "Intermediate resampling to produce white-light image"
00526 " for sky-spectrum creation:");
00527 cpl_msg_indent_more();
00528 muse_resampling_params *params = muse_resampling_params_new(MUSE_RESAMPLE_WEIGHTED_DRIZZLE);
00529 params->crtype = MUSE_RESAMPLING_CRSTATS_MEDIAN;
00530 params->crsigma = aProp->skymodel_params.crsigmac;
00531 muse_datacube *cube = muse_resampling_cube(sky_pt, params, NULL);
00532 if (!cube) {
00533 cpl_msg_error(__func__, "Could not create cube for whitelight image");
00534 muse_pixtable_delete(sky_pt);
00535 muse_pixtable_delete(pt);
00536 return NULL;
00537 }
00538 cpl_table *fwhite = muse_table_load_filter(NULL, "white");
00539 muse_image *whitelight = muse_datacube_collapse(cube, fwhite);
00540 cpl_msg_indent_less();
00541 muse_mask *sky_mask = muse_sky_create_skymask(whitelight,
00542 aProp->skymodel_params.ignore,
00543 aProp->skymodel_params.fraction,
00544 "ESO QC SCIPOST");
00545
00546
00547 cpl_table_select_all(sky_pt->table);
00548 muse_pixtable_and_selected_mask(sky_pt, sky_mask);
00549 cpl_table *spectrum =
00550 muse_resampling_spectrum_iterate(sky_pt, aProp->skymodel_params.sampling,
00551 0., aProp->skymodel_params.crsigmas, 1);
00552 if (aProp->skymethod == MUSE_POSTPROC_SKYMETHOD_MODEL) {
00553
00554 cpl_array *lambda = muse_cpltable_extract_column(spectrum, "lambda");
00555 double lambda_low = cpl_array_get_min(lambda);
00556 double lambda_high = cpl_array_get_max(lambda);
00557 sky_lines = cpl_table_duplicate(aProp->sky_lines);
00558 muse_sky_lines_set_range(sky_lines, lambda_low-5, lambda_high+5);
00559
00560
00561 prestate = cpl_errorstate_get();
00562 if (aProp->lsf_cube) {
00563 cpl_image *lsfImage = muse_lsf_average_cube_all(aProp->lsf_cube, sky_pt);
00564 muse_wcs *lsfWCS = muse_lsf_cube_get_wcs_all(aProp->lsf_cube);
00565
00566 muse_lsf_fold_rectangle(lsfImage, lsfWCS, aProp->skymodel_params.sampling);
00567
00568 muse_sky_lines_fit(spectrum, sky_lines, lsfImage, lsfWCS);
00569 if (!cpl_errorstate_is_equal(prestate)) {
00570 cpl_errorstate_dump(prestate, CPL_FALSE, NULL);
00571 cpl_errorstate_set(prestate);
00572 }
00573
00574
00575 if (!aProp->sky_continuum) {
00576 cpl_msg_info(__func__, "No sky continuum given, create it from the data");
00577 sky_cont = muse_sky_continuum_create(spectrum, sky_lines, lsfImage, lsfWCS,
00578 aProp->skymodel_params.csampling);
00579 } else {
00580 cpl_msg_info(__func__, "Using sky continuum given as input");
00581 }
00582
00583
00584 cpl_image_delete(lsfImage);
00585 #ifdef USE_LSF_PARAMS
00586 } else if (aProp->lsf_params) {
00587
00588 muse_sky_lines_fit_old(spectrum, sky_lines);
00589 if (!cpl_errorstate_is_equal(prestate)) {
00590 cpl_errorstate_dump(prestate, CPL_FALSE, NULL);
00591 cpl_errorstate_set(prestate);
00592 }
00593
00594 if (!aProp->sky_continuum) {
00595 cpl_msg_info(__func__, "No sky continuum given, create it from the data");
00596 muse_sky_subtract_lines_old(sky_pt, sky_lines, aProp->lsf_params);
00597 sky_cont = muse_resampling_spectrum(sky_pt,
00598 aProp->skymodel_params.csampling);
00599 cpl_table_erase_column(sky_cont, "stat");
00600 cpl_table_erase_column(sky_cont, "dq");
00601 cpl_table_name_column(sky_cont, "data", "flux");
00602 }
00603 #endif
00604 }
00605 cpl_array_unwrap(lambda);
00606 } else {
00607
00608
00609 cpl_table *spec2 = cpl_table_duplicate(spectrum);
00610 cpl_table_name_column(spec2, "data", "flux");
00611 rc = muse_sky_subtract_continuum(pt, spec2);
00612 cpl_table_delete(spec2);
00613 }
00614 if (aSkyOut) {
00615 aSkyOut->mask = sky_mask;
00616 aSkyOut->spectrum = spectrum;
00617 } else {
00618 muse_mask_delete(sky_mask);
00619 cpl_table_delete(spectrum);
00620 }
00621 muse_image_delete(whitelight);
00622 cpl_table_delete(fwhite);
00623 muse_resampling_params_delete(params);
00624 muse_datacube_delete(cube);
00625 muse_pixtable_delete(sky_pt);
00626 }
00627
00628
00629
00630 if ((aProp->skymethod == MUSE_POSTPROC_SKYMETHOD_NONE) ||
00631 (aProp->skymethod == MUSE_POSTPROC_SKYMETHOD_MODEL)) {
00632 if (aProp->sky_continuum) {
00633 cpl_msg_debug(__func__, "doing sky continuum subtraction with input "
00634 "spectrum");
00635 rc = muse_sky_subtract_continuum(pt, aProp->sky_continuum);
00636 } else if (sky_cont) {
00637 cpl_msg_debug(__func__, "doing sky continuum subtraction with created "
00638 "spectrum");
00639 rc = muse_sky_subtract_continuum(pt, sky_cont);
00640 }
00641 if (sky_lines) {
00642 cpl_msg_debug(__func__, "doing sky line subtraction with created list");
00643 if (aProp->lsf_cube != NULL) {
00644 rc = muse_sky_subtract_lines(pt, sky_lines, aProp->lsf_cube);
00645 #ifdef USE_LSF_PARAMS
00646 } else if (aProp->lsf_params != NULL) {
00647 rc = muse_sky_subtract_lines_old(pt, sky_lines, aProp->lsf_params);
00648 #endif
00649 }
00650 } else if (aProp->sky_lines) {
00651 cpl_msg_debug(__func__, "doing sky line subtraction with input list");
00652 if (aProp->lsf_cube != NULL) {
00653 rc = muse_sky_subtract_lines(pt, aProp->sky_lines, aProp->lsf_cube);
00654 #ifdef USE_LSF_PARAMS
00655 } else if (aProp->lsf_params != NULL) {
00656 rc = muse_sky_subtract_lines_old(pt, aProp->sky_lines, aProp->lsf_params);
00657 #endif
00658 }
00659 }
00660 }
00661
00662
00663 if (aSkyOut) {
00664 aSkyOut->lines = sky_lines;
00665 aSkyOut->continuum = sky_cont;
00666 } else {
00667 cpl_table_delete(sky_lines);
00668 cpl_table_delete(sky_cont);
00669 }
00670
00671
00672 muse_rvcorrect(pt, aProp->rvtype);
00673
00674
00675 if (aProp->type == MUSE_POSTPROC_STANDARD) {
00676 double raref, decref;
00677 cpl_table *reftable = muse_postproc_load_nearest(pt->header,
00678 aProp->refframe, 20., 60.,
00679 &raref, &decref);
00680 if (!reftable) {
00681 cpl_msg_error(__func__, "Correct %s could not be loaded, observed "
00682 "target not listed?", MUSE_TAG_STD_FLUX_TABLE);
00683 muse_pixtable_delete(pt);
00684 return NULL;
00685 }
00686 if (muse_flux_reference_table_check(reftable) != CPL_ERROR_NONE) {
00687 cpl_msg_error(__func__, "%s does not have a recognized format!",
00688 MUSE_TAG_STD_FLUX_TABLE);
00689 cpl_table_delete(reftable);
00690 muse_pixtable_delete(pt);
00691 return NULL;
00692 }
00693
00694 prestate = cpl_errorstate_get();
00695 muse_flux_object *flux = muse_flux_object_new();
00696 rc = muse_flux_integrate_std(pt, aProp->profile, flux);
00697 if (flux->intimage) {
00698 cpl_msg_debug(__func__, "Flux integration found %"CPL_SIZE_FORMAT" stars",
00699 cpl_image_get_size_y(flux->intimage->data));
00700 }
00701 muse_pixtable_delete(pt);
00702 if (airmass < 0) {
00703 cpl_msg_warning(__func__, "Invalid airmass derived (%.3e), resetting to "
00704 " zero", airmass);
00705 airmass = 0.;
00706 }
00707 flux->raref = raref;
00708 flux->decref = decref;
00709 rc = muse_flux_response_compute(flux, aProp->select, airmass, reftable,
00710 aProp->tellregions, aProp->extinction);
00711 cpl_table_delete(reftable);
00712 rc = muse_flux_get_response_table(flux, aProp->smooth);
00713 rc = muse_flux_get_telluric_table(flux);
00714
00715 if (!cpl_errorstate_is_equal(prestate)) {
00716 cpl_msg_warning(__func__, "The following errors happened while computing "
00717 "the flux calibration (rc = %d/%s):", rc,
00718 cpl_error_get_message_default(rc));
00719 cpl_errorstate_dump(prestate, CPL_FALSE, muse_cplerrorstate_dump_some);
00720 }
00721 return flux;
00722 }
00723
00724
00725 if (aProp->type == MUSE_POSTPROC_ASTROMETRY) {
00726 cpl_table *reftable = muse_postproc_load_nearest(pt->header,
00727 aProp->refframe, 20., 60.,
00728 NULL, NULL);
00729 if (!reftable) {
00730 cpl_msg_error(__func__, "Correct %s could not be loaded, observed "
00731 "target not listed?", MUSE_TAG_ASTROMETRY_REFERENCE);
00732 muse_pixtable_delete(pt);
00733 return NULL;
00734 }
00735 prestate = cpl_errorstate_get();
00736
00737 muse_wcs_object *wcs = muse_wcs_object_new();
00738 wcs->crpix1 = aProp->crpix1;
00739 wcs->crpix2 = aProp->crpix2;
00740 rc = muse_wcs_locate_sources(pt, fabsf(aProp->detsigma), aProp->centroid,
00741 wcs);
00742 if (aProp->detsigma < 0.) {
00743
00744 rc = muse_wcs_optimize_solution(wcs, aProp->detsigma, aProp->centroid,
00745 reftable, aProp->radius, aProp->faccuracy,
00746 aProp->niter, aProp->rejsigma);
00747 } else {
00748
00749 rc = muse_wcs_solve(wcs, reftable, aProp->radius, aProp->faccuracy,
00750 aProp->niter, aProp->rejsigma);
00751 if (wcs->wcs) {
00752 cpl_propertylist_update_float(wcs->wcs, MUSE_HDR_WCS_DETSIGMA,
00753 aProp->detsigma);
00754 cpl_propertylist_set_comment(wcs->wcs, MUSE_HDR_WCS_DETSIGMA,
00755 MUSE_HDR_WCS_DETSIGMA_C_ONE);
00756 }
00757 }
00758 cpl_table_delete(reftable);
00759 cpl_msg_debug(__func__, "Solving astrometry (initial radius %.1f pix, "
00760 "initial relative accuracy %.1f, %d iterations with rejection"
00761 " sigma %.2f) returned rc=%d: %s", aProp->radius,
00762 aProp->faccuracy, aProp->niter, aProp->rejsigma,
00763 rc, rc != CPL_ERROR_NONE ? cpl_error_get_message() : "");
00764 muse_pixtable_delete(pt);
00765
00766 if (!cpl_errorstate_is_equal(prestate)) {
00767 cpl_msg_warning(__func__, "The following errors happened while computing "
00768 "the astrometric solution (rc = %d/%s):", rc,
00769 cpl_error_get_message());
00770 cpl_errorstate_dump(prestate, CPL_FALSE, muse_cplerrorstate_dump_some);
00771 }
00772 return wcs;
00773 }
00774
00775 rc = muse_wcs_project_tan(pt, aProp->wcs);
00776 cpl_msg_debug(__func__, "Astrometry correction returned rc=%d: %s", rc,
00777 rc != CPL_ERROR_NONE ? cpl_error_get_message() : "");
00778
00779 return pt;
00780 }
00781
00782
00800
00801 cpl_propertylist *
00802 muse_postproc_cube_load_output_wcs(muse_processing *aProcessing)
00803 {
00804 cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
00805
00806 cpl_frameset *fwcs = muse_frameset_find(aProcessing->inframes,
00807 MUSE_TAG_OUTPUT_WCS, 0, CPL_FALSE);
00808 if (!fwcs || cpl_frameset_get_size(fwcs) <= 0) {
00809
00810 cpl_frameset_delete(fwcs);
00811 return NULL;
00812 }
00813 cpl_frame *frame = cpl_frameset_get_position(fwcs, 0);
00814 const char *fn = cpl_frame_get_filename(frame);
00815 cpl_propertylist *header = NULL;
00816 int iext, next = cpl_fits_count_extensions(fn);
00817 for (iext = 0; iext <= next; iext++) {
00818 header = cpl_propertylist_load(fn, iext);
00819 cpl_errorstate es = cpl_errorstate_get();
00820 cpl_wcs *wcs = cpl_wcs_new_from_propertylist(header);
00821 if (!cpl_errorstate_is_equal(es)) {
00822 cpl_errorstate_set(es);
00823 }
00824 if (!wcs) {
00825 cpl_propertylist_delete(header);
00826 header = NULL;
00827 continue;
00828 }
00829
00830 int naxis = cpl_wcs_get_image_naxis(wcs);
00831 cpl_boolean valid = naxis == 2 || naxis == 3;
00832
00833 const cpl_array *ctypes = cpl_wcs_get_ctype(wcs);
00834 if (valid && cpl_array_get_string(ctypes, 0)) {
00835 valid = valid && !strncmp(cpl_array_get_string(ctypes, 0), "RA---TAN", 9);
00836 }
00837 if (valid && cpl_array_get_string(ctypes, 1)) {
00838 valid = valid && !strncmp(cpl_array_get_string(ctypes, 1), "DEC--TAN", 9);
00839 }
00840
00841 if (valid && cpl_array_get_string(ctypes, 2)) {
00842 const char *ctype3 = cpl_array_get_string(ctypes, 2);
00843 valid = valid
00844 && (!strncmp(ctype3, "AWAV", 5) || !strncmp(ctype3, "AWAV-LOG", 9) ||
00845 !strncmp(ctype3, "WAVE", 5) || !strncmp(ctype3, "WAVE-LOG", 9));
00846 }
00847 if (valid) {
00848
00849 cpl_propertylist *pc = cpl_propertylist_new();
00850 cpl_propertylist_copy_property_regexp(pc, header, "^PC[0-9]+_[0-9]+", 0);
00851 valid = cpl_propertylist_get_size(pc) == 0;
00852 cpl_propertylist_delete(pc);
00853 }
00854 cpl_wcs_delete(wcs);
00855 if (valid) {
00856 cpl_msg_debug(__func__, "Apparently valid header %s found in extension %d"
00857 " of \"%s\"", MUSE_TAG_OUTPUT_WCS, iext, fn);
00858 muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_CALIB, 1);
00859 break;
00860 }
00861 cpl_propertylist_delete(header);
00862 header = NULL;
00863 }
00864 if (!header) {
00865 cpl_msg_warning(__func__, "No valid headers for %s found in \"%s\"",
00866 MUSE_TAG_OUTPUT_WCS, fn);
00867 }
00868 cpl_frameset_delete(fwcs);
00869 return header;
00870 }
00871
00872
00890
00891 cpl_error_code
00892 muse_postproc_cube_resample_and_collapse(muse_processing *aProcessing,
00893 muse_pixtable *aPixtable,
00894 muse_cube_type aFormat,
00895 muse_resampling_params *aParams,
00896 const char *aFilter)
00897 {
00898 cpl_ensure_code(aProcessing && aPixtable && aParams, CPL_ERROR_NULL_INPUT);
00899 cpl_ensure_code(aFormat == MUSE_CUBE_TYPE_EURO3D ||
00900 aFormat == MUSE_CUBE_TYPE_FITS ||
00901 aFormat == MUSE_CUBE_TYPE_EURO3D_X ||
00902 aFormat == MUSE_CUBE_TYPE_FITS_X ||
00903 aFormat == MUSE_CUBE_TYPE_SDP, CPL_ERROR_ILLEGAL_INPUT);
00904
00905
00906 if (aParams->tlambda == MUSE_RESAMPLING_DISP_WAVE ||
00907 aParams->tlambda == MUSE_RESAMPLING_DISP_WAVE_LOG) {
00908
00909 muse_phys_air_to_vacuum(aPixtable, MUSE_PHYS_AIR_STANDARD);
00910 }
00911
00912
00913 void *cube = NULL;
00914 muse_pixgrid *grid = NULL;
00915 if (aFormat == MUSE_CUBE_TYPE_EURO3D || aFormat == MUSE_CUBE_TYPE_EURO3D_X) {
00916 cpl_msg_info(__func__, "Resampling to final cube follows, output format "
00917 "\"Euro3D\"");
00918 cpl_msg_indent_more();
00919 muse_euro3dcube *e3d = muse_resampling_euro3d(aPixtable, aParams);
00920 cpl_msg_indent_less();
00921 cpl_ensure_code(e3d, cpl_error_get_code());
00922 cube = e3d;
00923 } else if (aFormat == MUSE_CUBE_TYPE_FITS ||
00924 aFormat == MUSE_CUBE_TYPE_FITS_X || aFormat == MUSE_CUBE_TYPE_SDP) {
00925 cpl_msg_info(__func__, "Resampling to final cube follows, output format "
00926 "\"FITS cube\"");
00927 cpl_msg_indent_more();
00928 muse_datacube *fits = muse_resampling_cube(aPixtable, aParams, &grid);
00929 cpl_msg_indent_less();
00930 cpl_ensure_code(fits, cpl_error_get_code());
00931 muse_postproc_qc_fwhm(aProcessing, fits);
00932 cube = fits;
00933 }
00934
00935 cpl_array *filters = muse_cplarray_new_from_delimited_string(aFilter, ",");
00936 int i, ipos = 0, nfilters = cpl_array_get_size(filters);
00937 for (i = 0; i < nfilters; i++) {
00938
00939 cpl_table *filter = muse_table_load_filter(aProcessing,
00940 cpl_array_get_string(filters, i));
00941 if (!filter) {
00942 continue;
00943 }
00944
00945
00946 muse_image *fov = NULL;
00947 if (aFormat == MUSE_CUBE_TYPE_EURO3D || aFormat == MUSE_CUBE_TYPE_EURO3D_X) {
00948 fov = muse_euro3dcube_collapse(cube, filter);
00949 } else if (aFormat == MUSE_CUBE_TYPE_FITS ||
00950 aFormat == MUSE_CUBE_TYPE_FITS_X || aFormat == MUSE_CUBE_TYPE_SDP) {
00951 if (getenv("MUSE_COLLAPSE_PIXTABLE") &&
00952 atoi(getenv("MUSE_COLLAPSE_PIXTABLE")) > 0) {
00953
00954 fov = muse_resampling_collapse_pixgrid(aPixtable, grid,
00955 (muse_datacube *)cube,
00956 filter, aParams);
00957 } else {
00958 fov = muse_datacube_collapse(cube, filter);
00959 }
00960 }
00961 cpl_table_delete(filter);
00962
00963 if (aFormat == MUSE_CUBE_TYPE_EURO3D_X || aFormat == MUSE_CUBE_TYPE_FITS_X) {
00964 if (!((muse_datacube *)cube)->recimages) {
00965
00966 ((muse_datacube *)cube)->recimages = muse_imagelist_new();
00967 ((muse_datacube *)cube)->recnames = cpl_array_new(0, CPL_TYPE_STRING);
00968 }
00969
00970 muse_imagelist_set(((muse_datacube *)cube)->recimages, fov,
00971 muse_imagelist_get_size(((muse_datacube *)cube)->recimages));
00972 cpl_array_set_size(((muse_datacube *)cube)->recnames, ipos+1);
00973 cpl_array_set_string(((muse_datacube *)cube)->recnames, ipos,
00974 cpl_array_get_string(filters, i));
00975 } else {
00976
00977 muse_image_dq_to_nan(fov);
00978
00979 muse_utils_copy_modified_header(fov->header, fov->header, "OBJECT",
00980 cpl_array_get_string(filters, i));
00981 cpl_propertylist_update_string(fov->header, MUSE_HDR_FILTER,
00982 cpl_array_get_string(filters, i));
00983 if (aFormat == MUSE_CUBE_TYPE_SDP) {
00984 if (muse_idp_properties_update_fov(fov) != CPL_ERROR_NONE) {
00985 cpl_msg_warning(__func__, "Writing IDP keywords to field-of-view "
00986 "image failed!");
00987 }
00988 }
00989 muse_processing_save_image(aProcessing, -1, fov, MUSE_TAG_IMAGE_FOV);
00990 muse_image_delete(fov);
00991 }
00992 ipos++;
00993 }
00994 cpl_array_delete(filters);
00995 muse_pixgrid_delete(grid);
00996
00997 #ifdef CREATE_IDP
00998
00999
01000 muse_idp_properties *properties =
01001 muse_idp_properties_collect(aProcessing, ((muse_datacube *)cube)->header,
01002 MUSE_TAG_CUBE_FINAL);
01003
01004
01005
01006 muse_idp_properties_update((muse_datacube *)cube)->header, properties);
01007 #endif
01008
01009 cpl_error_code rc = CPL_ERROR_NONE;
01010 if (aFormat == MUSE_CUBE_TYPE_EURO3D || aFormat == MUSE_CUBE_TYPE_EURO3D_X) {
01011
01012 rc = muse_processing_save_cube(aProcessing, -1, cube, MUSE_TAG_CUBE_FINAL,
01013 MUSE_CUBE_TYPE_EURO3D);
01014 muse_euro3dcube_delete(cube);
01015 } else if (aFormat == MUSE_CUBE_TYPE_FITS || aFormat == MUSE_CUBE_TYPE_FITS_X ||
01016 aFormat == MUSE_CUBE_TYPE_SDP) {
01017
01018 muse_datacube_convert_dq(cube);
01019
01020 if (aFormat == MUSE_CUBE_TYPE_SDP) {
01021 rc = muse_processing_save_cube(aProcessing, -1, cube, MUSE_TAG_CUBE_FINAL,
01022 MUSE_CUBE_TYPE_SDP);
01023 } else {
01024 rc = muse_processing_save_cube(aProcessing, -1, cube, MUSE_TAG_CUBE_FINAL,
01025 MUSE_CUBE_TYPE_FITS);
01026 }
01027 muse_datacube_delete(cube);
01028 }
01029 #ifdef CREATE_IDP
01030 muse_idp_properties_delete(properties);
01031 #endif
01032 return rc;
01033 }
01034
01035
01058
01059 cpl_error_code
01060 muse_postproc_qc_fwhm(muse_processing *aProcessing, muse_datacube *aCube)
01061 {
01062 cpl_ensure_code(aProcessing && aCube, CPL_ERROR_NULL_INPUT);
01063
01064
01065 char prefix[KEYWORD_LENGTH] = "";
01066 if (!strncmp(aProcessing->name, "muse_scipost", 13)) {
01067 strncpy(prefix, QC_POST_PREFIX_SCIPOST, KEYWORD_LENGTH);
01068 } else if (!strncmp(aProcessing->name, "muse_exp_combine", 17)) {
01069 strncpy(prefix, QC_POST_PREFIX_EXPCOMBINE, KEYWORD_LENGTH);
01070 } else if (!strncmp(aProcessing->name, "muse_standard", 14)) {
01071 strncpy(prefix, QC_POST_PREFIX_STANDARD, KEYWORD_LENGTH);
01072 } else if (!strncmp(aProcessing->name, "muse_astrometry", 16)) {
01073 strncpy(prefix, QC_POST_PREFIX_ASTROMETRY, KEYWORD_LENGTH);
01074 } else {
01075 cpl_msg_info(__func__, "Recipe \"%s\" found, not generating QC1 keywords",
01076 aProcessing->name);
01077 return CPL_ERROR_NONE;
01078 }
01079
01080
01081
01082 int plane = cpl_imagelist_get_size(aCube->data) / 2;
01083 cpl_image *cim = cpl_imagelist_get(aCube->data, plane);
01084 double dsigmas[] = { 5., 4., 3. };
01085 int ndsigmas = sizeof(dsigmas) / sizeof(double);
01086 cpl_vector *vsigmas = cpl_vector_wrap(ndsigmas, dsigmas);
01087 cpl_size isigma = -1;
01088 cpl_errorstate prestate = cpl_errorstate_get();
01089 cpl_apertures *apertures = cpl_apertures_extract(cim, vsigmas, &isigma);
01090 cpl_vector_unwrap(vsigmas);
01091
01092
01093 char keyword[KEYWORD_LENGTH];
01094 cpl_boolean loglambda = !strncmp(muse_pfits_get_ctype(aCube->header, 3),
01095 "AWAV-LOG", 9);
01096 double crpix3 = muse_pfits_get_crpix(aCube->header, 3),
01097 cd33 = muse_pfits_get_cd(aCube->header, 3, 3),
01098 crval3 = muse_pfits_get_crval(aCube->header, 3),
01099 lambda = loglambda
01100 ? crval3 * exp((plane + 1. - crpix3) * cd33 / crval3)
01101 : (plane + 1. - crpix3) * cd33 + crval3;
01102 snprintf(keyword, KEYWORD_LENGTH, QC_POST_LAMBDA, prefix);
01103 cpl_propertylist_update_float(aCube->header, keyword, lambda);
01104
01105
01106 if (!apertures || !cpl_errorstate_is_equal(prestate)) {
01107
01108 snprintf(keyword, KEYWORD_LENGTH, QC_POST_NDET, prefix);
01109 cpl_propertylist_update_int(aCube->header, keyword, 0);
01110
01111 snprintf(keyword, KEYWORD_LENGTH, QC_POST_POSX, prefix, 0);
01112 cpl_propertylist_update_float(aCube->header, keyword, -1.);
01113 snprintf(keyword, KEYWORD_LENGTH, QC_POST_POSY, prefix, 0);
01114 cpl_propertylist_update_float(aCube->header, keyword, -1.);
01115 snprintf(keyword, KEYWORD_LENGTH, QC_POST_FWHMX, prefix, 0);
01116 cpl_propertylist_update_float(aCube->header, keyword, -1.);
01117 snprintf(keyword, KEYWORD_LENGTH, QC_POST_FWHMY, prefix, 0);
01118 cpl_propertylist_update_float(aCube->header, keyword, -1.);
01119
01120 cpl_errorstate_set(prestate);
01121 cpl_msg_warning(__func__, "No sources found for FWHM measurement down to "
01122 "%.1f sigma limit on plane %d, QC parameters will not "
01123 "contain useful information", dsigmas[ndsigmas-1], plane+1);
01124 return CPL_ERROR_DATA_NOT_FOUND;
01125 }
01126 int ndet = cpl_apertures_get_size(apertures);
01127 snprintf(keyword, KEYWORD_LENGTH, QC_POST_NDET, prefix);
01128 cpl_propertylist_update_int(aCube->header, keyword, ndet);
01129
01130
01131
01132 double cd11 = kMuseSpaxelSizeX_WFM, cd12 = 0., cd21 = 0.,
01133 cd22 = kMuseSpaxelSizeY_WFM;
01134 cpl_errorstate es = cpl_errorstate_get();
01135 cpl_wcs *wcs = cpl_wcs_new_from_propertylist(aCube->header);
01136 if (!cpl_errorstate_is_equal(es)) {
01137 cpl_errorstate_set(es);
01138 }
01139 if (!wcs || !strncasecmp(muse_pfits_get_ctype(aCube->header, 1), "PIXEL", 5)) {
01140 if (muse_pfits_get_mode(aCube->header) > MUSE_MODE_WFM_AO_N) {
01141 cd11 = kMuseSpaxelSizeX_NFM;
01142 cd22 = kMuseSpaxelSizeY_NFM;
01143 }
01144 } else {
01145 const cpl_matrix *cd = cpl_wcs_get_cd(wcs);
01146
01147 cd11 = fabs(cpl_matrix_get(cd, 0, 0)) * 3600.,
01148 cd12 = fabs(cpl_matrix_get(cd, 0, 1)) * 3600.,
01149 cd21 = fabs(cpl_matrix_get(cd, 1, 0)) * 3600.,
01150 cd22 = fabs(cpl_matrix_get(cd, 1, 1)) * 3600.;
01151 }
01152 cpl_wcs_delete(wcs);
01153
01154
01155
01156 cpl_image *statimage = cpl_image_new(ndet, 2, CPL_TYPE_DOUBLE);
01157 #if 0
01158 printf("index RA_FWHM DEC_FWHM\n");
01159 #endif
01160 int n, nbad = 0;
01161 for (n = 1; n <= ndet; n++) {
01162 cpl_size xcen = lround(cpl_apertures_get_centroid_x(apertures, n)),
01163 ycen = lround(cpl_apertures_get_centroid_y(apertures, n));
01164 double x, y;
01165 cpl_errorstate state = cpl_errorstate_get();
01166 cpl_image_get_fwhm(cim, xcen, ycen, &x, &y);
01167 const char *fwhmcomment = NULL;
01168 if (x < 0 || y < 0 || !cpl_errorstate_is_equal(state)) {
01169
01170 x = -1.;
01171 y = -1.;
01172
01173 cpl_image_reject(statimage, n, 1);
01174 cpl_image_reject(statimage, n, 2);
01175 fwhmcomment = "[arcsec] failure determining FWHM";
01176 nbad++;
01177
01178 cpl_errorstate_set(state);
01179 } else {
01180
01181 x = cd11 * x + cd12 * y;
01182 y = cd22 * y + cd21 * x;
01183 cpl_image_set(statimage, n, 1, x);
01184 cpl_image_set(statimage, n, 2, y);
01185 }
01186 #if 0
01187 printf("%4d %f %f\n", n, x, y);
01188 #endif
01189
01190
01191 snprintf(keyword, KEYWORD_LENGTH, QC_POST_POSX, prefix, n);
01192 cpl_propertylist_update_float(aCube->header, keyword, xcen);
01193 snprintf(keyword, KEYWORD_LENGTH, QC_POST_POSY, prefix, n);
01194 cpl_propertylist_update_float(aCube->header, keyword, ycen);
01195 snprintf(keyword, KEYWORD_LENGTH, QC_POST_FWHMX, prefix, n);
01196 cpl_propertylist_update_float(aCube->header, keyword, x);
01197 if (fwhmcomment) {
01198 cpl_propertylist_set_comment(aCube->header, keyword, fwhmcomment);
01199 }
01200 snprintf(keyword, KEYWORD_LENGTH, QC_POST_FWHMY, prefix, n);
01201 cpl_propertylist_update_float(aCube->header, keyword, y);
01202 if (fwhmcomment) {
01203 cpl_propertylist_set_comment(aCube->header, keyword, fwhmcomment);
01204 }
01205 }
01206 #if 0
01207 fflush(stdout);
01208 #endif
01209 cpl_apertures_delete(apertures);
01210
01211
01212 snprintf(keyword, KEYWORD_LENGTH, QC_POST_FWHM_NVALID, prefix);
01213 cpl_propertylist_update_int(aCube->header, keyword, ndet - nbad);
01214 cpl_errorstate state = cpl_errorstate_get();
01215 cpl_stats *s = cpl_stats_new_from_image(statimage,
01216 CPL_STATS_MEDIAN | CPL_STATS_MAD);
01217 cpl_boolean error = cpl_errorstate_is_equal(state);
01218 cpl_errorstate_set(state);
01219 snprintf(keyword, KEYWORD_LENGTH, QC_POST_FWHM_MEDIAN, prefix);
01220 if (error || ndet < 3) {
01221 cpl_propertylist_update_float(aCube->header, keyword, 0.);
01222 } else {
01223 cpl_propertylist_update_float(aCube->header, keyword, cpl_stats_get_median(s));
01224 }
01225 snprintf(keyword, KEYWORD_LENGTH, QC_POST_FWHM_MAD, prefix);
01226 if (error || ndet < 3) {
01227 cpl_propertylist_update_float(aCube->header, keyword, 0.);
01228 } else {
01229 cpl_propertylist_update_float(aCube->header, keyword, cpl_stats_get_mad(s));
01230 }
01231 cpl_stats_delete(s);
01232 cpl_image_delete(statimage);
01233 cpl_msg_info(__func__, "Computed FWHM QC parameters for %d of %d source%s "
01234 "found down to the %.1f sigma threshold on plane %d",
01235 ndet - nbad, ndet, ndet == 1 ? "" : "s", dsigmas[isigma],
01236 plane + 1);
01237
01238 return CPL_ERROR_NONE;
01239 }
01240