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 <cpl.h>
00030 #include <string.h>
00031
00032 #include "muse_wcs.h"
00033 #include "muse_instrument.h"
00034
00035 #include "muse_astro.h"
00036 #include "muse_combine.h"
00037 #include "muse_dar.h"
00038 #include "muse_pfits.h"
00039 #include "muse_quality.h"
00040 #include "muse_resampling.h"
00041 #include "muse_utils.h"
00042
00043
00044
00045
00046
00047 #define FAKE_POS_ROT 0
00048
00049
00053
00054
00057
00069
00070 muse_wcs_object *
00071 muse_wcs_object_new(void)
00072 {
00073 muse_wcs_object *wcs = cpl_calloc(1, sizeof(muse_wcs_object));
00074 return wcs;
00075 }
00076
00077
00087
00088 void
00089 muse_wcs_object_delete(muse_wcs_object *aWCSObj)
00090 {
00091 if (!aWCSObj) {
00092 return;
00093 }
00094 muse_datacube_delete(aWCSObj->cube);
00095 aWCSObj->cube = NULL;
00096 cpl_table_delete(aWCSObj->detected);
00097 aWCSObj->detected = NULL;
00098 cpl_propertylist_delete(aWCSObj->wcs);
00099 aWCSObj->wcs = NULL;
00100 cpl_free(aWCSObj);
00101 }
00102
00103
00115
00116 const muse_cpltable_def muse_wcs_detections_def[] = {
00117 { "XPOS", CPL_TYPE_DOUBLE, "pix", "%f", "horizontal position", CPL_TRUE },
00118 { "YPOS", CPL_TYPE_DOUBLE, "pix", "%f", "vertical position", CPL_TRUE },
00119 { "XERR", CPL_TYPE_DOUBLE, "pix", "%f",
00120 "error estimate of the horizontal position", CPL_TRUE },
00121 { "YERR", CPL_TYPE_DOUBLE, "pix", "%f",
00122 "error estimate of the vertical position", CPL_TRUE },
00123 { "FLUX", CPL_TYPE_DOUBLE, "count", "%e", "(relative) flux of the source", CPL_TRUE },
00124 { "XFWHM", CPL_TYPE_DOUBLE, "pix", "%f", "horizontal FWHM", CPL_TRUE },
00125 { "YFWHM", CPL_TYPE_DOUBLE, "pix", "%f", "vertical FWHM", CPL_TRUE },
00126 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
00127 };
00128
00129
00140
00141 const muse_cpltable_def muse_wcs_reference_def[] = {
00142 { "SourceID", CPL_TYPE_STRING, "", "%s", "the source identification", CPL_TRUE },
00143 { "RA", CPL_TYPE_DOUBLE, "deg", "%f", "right ascension (decimal degrees)", CPL_TRUE },
00144 { "DEC", CPL_TYPE_DOUBLE, "deg", "%f", "declination (decimal degrees)", CPL_TRUE },
00145 { "filter", CPL_TYPE_STRING, "", "%s", "the filter used for the magnitude", CPL_TRUE },
00146 { "mag", CPL_TYPE_DOUBLE, "mag", "%f", "the object (Vega) magnitude", CPL_TRUE },
00147 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
00148 };
00149
00150
00151
00176
00177 cpl_table *
00178 muse_wcs_centroid_stars(muse_image *aImage, float aSigma,
00179 muse_wcs_centroid_type aCentroid)
00180 {
00181 cpl_ensure(aImage && aImage->data && aImage->stat, CPL_ERROR_NULL_INPUT,
00182 NULL);
00183 cpl_ensure(aSigma > 0., CPL_ERROR_ILLEGAL_INPUT, NULL);
00184 switch (aCentroid) {
00185 case MUSE_WCS_CENTROID_GAUSSIAN:
00186 cpl_msg_info(__func__, "Gaussian profile fits for object centroiding");
00187 break;
00188 case MUSE_WCS_CENTROID_MOFFAT:
00189 cpl_msg_info(__func__, "Moffat profile fits for object centroiding");
00190 break;
00191 case MUSE_WCS_CENTROID_BOX:
00192 cpl_msg_info(__func__, "Simple square box object centroiding");
00193 break;
00194 default:
00195 cpl_msg_error(__func__, "Unknown centroiding method!");
00196 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
00197 return NULL;
00198 }
00199
00200
00201
00202
00203 muse_image *image = muse_image_new();
00204 image->data = cpl_image_duplicate(aImage->data);
00205 image->stat = cpl_image_power_create(aImage->stat, 0.5);
00206 if (aImage->header) {
00207 image->header = cpl_propertylist_duplicate(aImage->header);
00208 }
00209
00210
00211 if (aImage->dq) {
00212 image->dq = cpl_image_duplicate(aImage->dq);
00213 muse_quality_image_reject_using_dq(image->data, image->dq, image->stat);
00214 } else {
00215 cpl_image_reject_value(image->data, CPL_VALUE_NAN);
00216 cpl_image_reject_value(image->stat, CPL_VALUE_NAN);
00217 }
00218
00219
00220 cpl_image_fill_rejected(image->data, cpl_image_get_min(image->data));
00221 cpl_image_fill_rejected(image->stat, cpl_image_get_max(image->stat));
00222
00223
00224 cpl_apertures *apertures = cpl_apertures_extract_sigma(image->data, aSigma);
00225 int ndet = apertures ? cpl_apertures_get_size(apertures) : 0;
00226 if (ndet < 1) {
00227 muse_image_delete(image);
00228 cpl_msg_error(__func__, "No sources for astrometric calibration found down "
00229 "to %.3f sigma limit", aSigma);
00230 cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
00231 return NULL;
00232 }
00233 cpl_msg_debug(__func__, "The %.3f sigma threshold was used to find %d sources",
00234 aSigma, ndet);
00235
00236 int nx = cpl_image_get_size_x(image->data),
00237 ny = cpl_image_get_size_y(image->data);
00238
00239 const double bgguess = cpl_image_get_median(image->data),
00240 beta = 2.5,
00241 moffat_alpha_to_fwhm = 1 / (2 * sqrt(pow(2, 1/beta) - 1));
00242 #if 0
00243 double fwhmguess = (muse_pfits_get_fwhm_start(image->header)
00244 + muse_pfits_get_fwhm_end(image->header)) / 2.;
00245 if (fwhmguess < FLT_EPSILON) {
00246 fwhmguess = 4.;
00247 } else {
00248 if (muse_pfits_get_mode(image->header) < MUSE_MODE_NFM_AO_N) {
00249 fwhmguess /= (kMuseSpaxelSizeX_WFM + kMuseSpaxelSizeX_WFM) / 2;
00250 } else {
00251 fwhmguess /= (kMuseSpaxelSizeX_NFM + kMuseSpaxelSizeX_NFM) / 2;
00252 }
00253 }
00254 #endif
00255
00256
00257
00258 cpl_table *detections = muse_cpltable_new(muse_wcs_detections_def, ndet);
00259 cpl_table_unselect_all(detections);
00260 int idet;
00261 for (idet = 0; idet < ndet; idet++) {
00262 double xc = cpl_apertures_get_centroid_x(apertures, idet+1),
00263 yc = cpl_apertures_get_centroid_y(apertures, idet+1),
00264 fluxaper = cpl_apertures_get_flux(apertures, idet+1);
00265 double xwaper, ywaper;
00266 cpl_image_get_fwhm(image->data, lround(xc), lround(yc), &xwaper, &ywaper);
00267
00268
00269
00270 if (xwaper < 0 || ywaper < 0) {
00271 cpl_msg_debug(__func__, "FWHM computation unsuccessful at %f,%f, result "
00272 "was %.3f,%.3f", xc, yc, xwaper, ywaper);
00273 cpl_table_select_row(detections, idet);
00274 continue;
00275 }
00276
00277
00278
00279 int x1 = floor(xc) - 5,
00280 x2 = ceil(xc) + 5,
00281 y1 = floor(yc) - 5,
00282 y2 = ceil(yc) + 5;
00283
00284 if (x1 < 1) x1 = 1;
00285 if (y1 < 1) y1 = 1;
00286 if (x2 > nx) x2 = nx;
00287 if (y2 > ny) y2 = ny;
00288
00289 double xcen, ycen, xerr = 0., yerr = 0., xw, yw, flux;
00290 cpl_errorstate state = cpl_errorstate_get();
00291 switch (aCentroid) {
00292 case MUSE_WCS_CENTROID_GAUSSIAN: {
00293 cpl_array *pgauss = cpl_array_new(7, CPL_TYPE_DOUBLE),
00294 *pgerr = cpl_array_new(7, CPL_TYPE_DOUBLE),
00295 *pgfit = cpl_array_new(7, CPL_TYPE_INT);
00296
00297 cpl_array_set(pgfit, 3, 1);
00298 cpl_array_set(pgfit, 4, 1);
00299 cpl_array_set(pgfit, 0, 0);
00300 cpl_array_set(pgfit, 1, 0);
00301 cpl_array_set(pgfit, 2, 0);
00302 cpl_array_set(pgfit, 5, 0);
00303 cpl_array_set(pgfit, 6, 0);
00304 cpl_array_set(pgauss, 3, xc);
00305 cpl_array_set(pgauss, 4, yc);
00306 cpl_array_set(pgauss, 0, bgguess);
00307 cpl_array_set(pgauss, 1, fluxaper);
00308 cpl_array_set(pgauss, 2, 0.);
00309 cpl_array_set(pgauss, 5, xwaper * CPL_MATH_SIG_FWHM);
00310 cpl_array_set(pgauss, 6, ywaper * CPL_MATH_SIG_FWHM);
00311 cpl_fit_image_gaussian(image->data, image->stat, lround(xc), lround(yc),
00312 x2-x1+1, y2-y1+1, pgauss, pgerr, pgfit, NULL, NULL,
00313 NULL, NULL, NULL, NULL, NULL);
00314 xcen = cpl_array_get(pgauss, 3, NULL);
00315 ycen = cpl_array_get(pgauss, 4, NULL);
00316 xerr = cpl_array_get(pgerr, 3, NULL);
00317 yerr = cpl_array_get(pgerr, 4, NULL);
00318
00319 cpl_array_set(pgfit, 3, 0);
00320 cpl_array_set(pgfit, 4, 0);
00321 cpl_array_set(pgfit, 1, 1);
00322 cpl_array_set(pgfit, 5, 1);
00323 cpl_array_set(pgfit, 6, 1);
00324 cpl_fit_image_gaussian(image->data, image->stat, lround(xc), lround(yc),
00325 x2-x1+1, y2-y1+1, pgauss, pgerr, pgfit, NULL, NULL,
00326 NULL, NULL, NULL, NULL, NULL);
00327 xw = cpl_array_get(pgauss, 5, NULL) * CPL_MATH_FWHM_SIG;
00328 yw = cpl_array_get(pgauss, 6, NULL) * CPL_MATH_FWHM_SIG;
00329 flux = cpl_array_get(pgauss, 1, NULL);
00330 cpl_array_delete(pgauss);
00331 cpl_array_delete(pgerr);
00332 cpl_array_delete(pgfit);
00333 break;
00334 }
00335 case MUSE_WCS_CENTROID_MOFFAT: {
00336 cpl_size nmax = (x2-x1+1) * (y2-y1+1);
00337 cpl_matrix *pos = cpl_matrix_new(nmax, 2);
00338 cpl_vector *val = cpl_vector_new(nmax),
00339 *err = cpl_vector_new(nmax);
00340 int i, npix = 0;
00341 for (i = x1; i <= x2; i++) {
00342 int j;
00343 for (j = y1; j <= y2; j++) {
00344 int error;
00345 double value = cpl_image_get(image->data, i, j, &error);
00346 if (error != 0) {
00347 continue;
00348 }
00349 cpl_matrix_set(pos, npix, 0, i);
00350 cpl_matrix_set(pos, npix, 1, j);
00351 cpl_vector_set(val, npix, value);
00352
00353 cpl_vector_set(err, npix, cpl_image_get(image->stat, i, j, &error));
00354 npix++;
00355 }
00356 }
00357 cpl_matrix_set_size(pos, npix, 2);
00358 cpl_vector_set_size(val, npix);
00359 cpl_vector_set_size(err, npix);
00360 cpl_array *pmoffat = cpl_array_new(8, CPL_TYPE_DOUBLE),
00361 *pmerror = cpl_array_new(8, CPL_TYPE_DOUBLE),
00362 *pmfit = cpl_array_new(8, CPL_TYPE_INT);
00363
00364 cpl_array_set(pmfit, 2, 1);
00365 cpl_array_set(pmfit, 3, 1);
00366 cpl_array_set(pmfit, 0, 0);
00367 cpl_array_set(pmfit, 1, 0);
00368 cpl_array_set(pmfit, 4, 0);
00369 cpl_array_set(pmfit, 5, 0);
00370 cpl_array_set(pmfit, 6, 0);
00371 cpl_array_set(pmfit, 7, 0);
00372 cpl_array_set(pmoffat, 2, xc);
00373 cpl_array_set(pmoffat, 3, yc);
00374 cpl_array_set(pmoffat, 0, bgguess);
00375 cpl_array_set(pmoffat, 1, fluxaper);
00376 cpl_array_set(pmoffat, 4, xwaper * moffat_alpha_to_fwhm);
00377 cpl_array_set(pmoffat, 5, ywaper * moffat_alpha_to_fwhm);
00378 cpl_array_set(pmoffat, 6, beta);
00379 cpl_array_set(pmoffat, 7, 0.);
00380 muse_utils_fit_moffat_2d(pos, val, err, pmoffat, pmerror, pmfit, NULL, NULL);
00381 xcen = cpl_array_get(pmoffat, 2, NULL);
00382 ycen = cpl_array_get(pmoffat, 3, NULL);
00383 xerr = cpl_array_get(pmerror, 2, NULL);
00384 yerr = cpl_array_get(pmerror, 3, NULL);
00385
00386 cpl_array_set(pmfit, 2, 0);
00387 cpl_array_set(pmfit, 3, 0);
00388 cpl_array_set(pmfit, 1, 1);
00389 cpl_array_set(pmfit, 4, 1);
00390 cpl_array_set(pmfit, 5, 1);
00391 muse_utils_fit_moffat_2d(pos, val, err, pmoffat, pmerror, pmfit, NULL, NULL);
00392 xw = cpl_array_get(pmoffat, 4, NULL) / moffat_alpha_to_fwhm;
00393 yw = cpl_array_get(pmoffat, 5, NULL) / moffat_alpha_to_fwhm;
00394 flux = cpl_array_get(pmoffat, 1, NULL);
00395 cpl_array_delete(pmoffat);
00396 cpl_array_delete(pmerror);
00397 cpl_array_delete(pmfit);
00398 cpl_matrix_delete(pos);
00399 cpl_vector_delete(val);
00400 cpl_vector_delete(err);
00401 break;
00402 }
00403 default:
00404 muse_utils_image_get_centroid_window(image->data, x1, y1, x2, y2,
00405 &xcen, &ycen,
00406 MUSE_UTILS_CENTROID_MEDIAN);
00407 #if 0
00408 printf("%d apertures %f %f boxes %f %f deltas %f %f\n", idet+1, xc, yc,
00409 xcen, ycen, xcen - xc, ycen - yc);
00410 fflush(stdout);
00411 #endif
00412
00413
00414 xerr = 0.15;
00415 yerr = 0.15;
00416
00417 cpl_image_get_fwhm(image->data, lround(xcen), lround(ycen), &xw, &yw);
00418 flux = fluxaper;
00419 }
00420
00421
00422 cpl_table_set(detections, "XPOS", idet, xcen);
00423 cpl_table_set(detections, "YPOS", idet, ycen);
00424
00425 cpl_table_set(detections, "XERR", idet, xerr);
00426 cpl_table_set(detections, "YERR", idet, yerr);
00427
00428 cpl_table_set(detections, "XFWHM", idet, xw);
00429 cpl_table_set(detections, "YFWHM", idet, yw);
00430
00431 cpl_table_set(detections, "FLUX", idet, flux);
00432
00433 if (cpl_errorstate_is_equal(state) && xw > 0 && yw > 0 &&
00434 xcen >= 1 && xcen <= nx && ycen >= 1 && ycen <= ny) {
00435 continue;
00436 }
00437
00438 cpl_table_select_row(detections, idet);
00439 }
00440 cpl_table_erase_selected(detections);
00441 cpl_apertures_delete(apertures);
00442 muse_image_delete(image);
00443 cpl_msg_debug(__func__, "%d of %d sources were recorded in the detections "
00444 "table", (int)cpl_table_get_nrow(detections), ndet);
00445
00446 return detections;
00447 }
00448
00449
00491
00492 cpl_error_code
00493 muse_wcs_locate_sources(muse_pixtable *aPixtable, float aSigma,
00494 muse_wcs_centroid_type aCentroid,
00495 muse_wcs_object *aWCSObj)
00496 {
00497 cpl_ensure_code(aPixtable && aPixtable->header && aWCSObj,
00498 CPL_ERROR_NULL_INPUT);
00499 cpl_ensure_code(aSigma > 0., CPL_ERROR_ILLEGAL_INPUT);
00500 switch (aCentroid) {
00501 case MUSE_WCS_CENTROID_GAUSSIAN:
00502 case MUSE_WCS_CENTROID_MOFFAT:
00503 case MUSE_WCS_CENTROID_BOX:
00504 break;
00505 default:
00506 return cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
00507 }
00508 if (getenv("MUSE_DEBUG_WCS") && atoi(getenv("MUSE_DEBUG_WCS")) > 2) {
00509 const char *fn = "wcs__pixtable.fits";
00510 cpl_msg_info(__func__, "Saving pixel table as \"%s\"", fn);
00511 muse_pixtable_save(aPixtable, fn);
00512 }
00513
00514
00515 cpl_boolean darcorrected = CPL_FALSE;
00516 if (cpl_propertylist_has(aPixtable->header, MUSE_HDR_PT_DAR_NAME) &&
00517 cpl_propertylist_get_double(aPixtable->header, MUSE_HDR_PT_DAR_NAME) > 0.) {
00518 darcorrected = CPL_TRUE;
00519 } else if (cpl_propertylist_has(aPixtable->header, MUSE_HDR_PT_DAR_CORR)) {
00520 darcorrected = CPL_TRUE;
00521 }
00522 if (!darcorrected) {
00523 const char *message = "Correction for differential atmospheric refraction "
00524 "was not applied! Deriving astrometric correction "
00525 "from such data is unlikely to give good results!";
00526 cpl_msg_warning(__func__, "%s", message);
00527 cpl_error_set_message(__func__, CPL_ERROR_UNSUPPORTED_MODE, "%s", message);
00528 }
00529
00530 muse_resampling_params *params =
00531 muse_resampling_params_new(MUSE_RESAMPLE_WEIGHTED_DRIZZLE);
00532 params->pfx = 1.;
00533 params->pfy = 1.;
00534 params->pfl = 1.;
00535 params->dlambda = 50.;
00536 params->crtype = MUSE_RESAMPLING_CRSTATS_MEDIAN;
00537 params->crsigma = 25.;
00538 muse_datacube *cube = muse_resampling_cube(aPixtable, params, NULL);
00539 muse_resampling_params_delete(params);
00540
00541
00542 muse_pixtable_reset_dq(aPixtable, EURO3D_COSMICRAY);
00543 if (!cube) {
00544 return cpl_error_set_message(__func__, cpl_error_get_code(),
00545 "Failure while creating cube!");
00546 }
00547 aWCSObj->cube = cube;
00548 if (getenv("MUSE_DEBUG_WCS") && atoi(getenv("MUSE_DEBUG_WCS")) >= 2) {
00549 const char *fn = "wcs__cube.fits";
00550 cpl_msg_info(__func__, "Saving cube as \"%s\"", fn);
00551 muse_datacube_save(cube, fn);
00552 }
00553
00554
00555 int iplane, irefplane = cpl_imagelist_get_size(cube->data) / 2;
00556
00557
00558
00559 muse_imagelist *list = muse_imagelist_new();
00560 unsigned int ilist = 0;
00561 for (iplane = irefplane - 1; iplane <= irefplane + 1; iplane++) {
00562 muse_image *image = muse_image_new();
00563 image->data = cpl_image_duplicate(cpl_imagelist_get(cube->data, iplane));
00564 image->dq = cpl_image_duplicate(cpl_imagelist_get(cube->dq, iplane));
00565 image->stat = cpl_image_duplicate(cpl_imagelist_get(cube->stat, iplane));
00566 muse_imagelist_set(list, image, ilist++);
00567 }
00568 muse_image *median = muse_combine_median_create(list);
00569
00570
00571 cpl_propertylist_copy_property_regexp(median->header, cube->header,
00572 "^C...*3$|^CD3_.$", 1);
00573 muse_imagelist_delete(list);
00574 if (!median) {
00575 return cpl_error_set_message(__func__, cpl_error_get_code(),
00576 "Failure while creating detection image!");
00577 }
00578 if (getenv("MUSE_DEBUG_WCS") && atoi(getenv("MUSE_DEBUG_WCS")) >= 2) {
00579 const char *fn = "wcs__image_median.fits";
00580 cpl_msg_info(__func__, "Saving median detection image as \"%s\"", fn);
00581 muse_image_save(median, fn);
00582 }
00583 cube->recimages = muse_imagelist_new();
00584 cube->recnames = cpl_array_new(2, CPL_TYPE_STRING);
00585
00586
00587 cpl_table *fwhite = muse_table_load_filter(NULL, "white");
00588 muse_image *white = muse_datacube_collapse(cube, fwhite);
00589 cpl_table_delete(fwhite);
00590 muse_imagelist_set(cube->recimages, white, 0);
00591 cpl_array_set_string(cube->recnames, 0, "white");
00592
00593
00594 muse_imagelist_set(cube->recimages, median, 1);
00595 cpl_array_set_string(cube->recnames, 1, MUSE_WCS_DETIMAGE_EXTNAME);
00596
00597
00598 cpl_table *detections = muse_wcs_centroid_stars(median, aSigma, aCentroid);
00599 if (!detections || (detections && cpl_table_get_nrow(detections) < 1)) {
00600 return cpl_error_get_code();
00601 }
00602
00603 aWCSObj->xcenter = cpl_image_get_size_x(median->data) / 2.,
00604 aWCSObj->ycenter = cpl_image_get_size_y(median->data) / 2.;
00605 aWCSObj->ra = muse_pfits_get_ra(median->header);
00606 aWCSObj->dec = muse_pfits_get_dec(median->header);
00607 #if 0
00608 cpl_msg_debug(__func__, "image size: %d x %d --> center %f, %f",
00609 (int)cpl_image_get_size_x(median->data),
00610 (int)cpl_image_get_size_y(median->data),
00611 aWCSObj->xcenter, aWCSObj->ycenter);
00612 #endif
00613
00614 if (getenv("MUSE_DEBUG_WCS") && atoi(getenv("MUSE_DEBUG_WCS")) >= 2) {
00615 const char *fn = "wcs__detections.fits";
00616 cpl_msg_info(__func__, "Saving table of detections as \"%s\"", fn);
00617 cpl_table_save(detections, NULL, NULL, fn, CPL_IO_CREATE);
00618 }
00619
00620 aWCSObj->detected = detections;
00621 return CPL_ERROR_NONE;
00622 }
00623
00624
00666
00667 cpl_error_code
00668 muse_wcs_solve(muse_wcs_object *aWCSObj, cpl_table *aReference,
00669 float aRadius, float aFAccuracy, int aIter, float aThresh)
00670 {
00671 cpl_ensure_code(aWCSObj, CPL_ERROR_NULL_INPUT);
00672 cpl_table *detected = aWCSObj->detected;
00673 int ndet = cpl_table_get_nrow(detected),
00674 nref = cpl_table_get_nrow(aReference);
00675 cpl_ensure_code(ndet > 0 && nref > 0, CPL_ERROR_ILLEGAL_INPUT);
00676 cpl_ensure_code(muse_cpltable_check(detected, muse_wcs_detections_def)
00677 == CPL_ERROR_NONE &&
00678 muse_cpltable_check(aReference, muse_wcs_reference_def)
00679 == CPL_ERROR_NONE,
00680 CPL_ERROR_BAD_FILE_FORMAT);
00681 cpl_ensure_code(aRadius > 0.&& aFAccuracy > 0., CPL_ERROR_ILLEGAL_INPUT);
00682
00683
00684 cpl_propertylist *order = cpl_propertylist_new();
00685 cpl_propertylist_append_bool(order, "FLUX", TRUE);
00686 cpl_table_sort(detected, order);
00687 cpl_propertylist_erase(order, "FLUX");
00688 cpl_propertylist_append_bool(order, "mag", FALSE);
00689 cpl_table_sort(aReference, order);
00690 cpl_propertylist_delete(order);
00691 if (getenv("MUSE_DEBUG_WCS") && atoi(getenv("MUSE_DEBUG_WCS")) >= 2) {
00692 FILE *fp = fopen("wcs__detections.ascii", "w");
00693 fprintf(fp, "%s: table of detected sources (sorted by flux):\n", __func__);
00694 cpl_table_dump(detected, 0, 1000, fp);
00695 fclose(fp);
00696 fp = fopen("wcs__references.ascii", "w");
00697 fprintf(fp, "%s: table of reference objects (sorted by flux):\n", __func__);
00698 cpl_table_dump(aReference, 0, 1000, fp);
00699 fclose(fp);
00700 }
00701
00702
00703 cpl_propertylist *wcsin = muse_wcs_create_default();
00704 cpl_propertylist_append_double(wcsin, "CRVAL1", aWCSObj->ra);
00705 cpl_propertylist_append_double(wcsin, "CRVAL2", aWCSObj->dec);
00706 cpl_propertylist_update_double(wcsin, "CRPIX1", aWCSObj->crpix1);
00707 cpl_propertylist_update_double(wcsin, "CRPIX2", aWCSObj->crpix2);
00708
00709 cpl_propertylist_append_int(wcsin, "NAXIS", 2);
00710 cpl_propertylist_append_int(wcsin, "NAXIS1", aWCSObj->xcenter * 2.);
00711 cpl_propertylist_append_int(wcsin, "NAXIS2", aWCSObj->ycenter * 2.);
00712
00713
00714 cpl_matrix *data = cpl_matrix_new(2, ndet),
00715 *patt = cpl_matrix_new(2, nref);
00716 int i;
00717 for (i = 0; i < ndet; i++) {
00718 cpl_matrix_set(data, 0, i, cpl_table_get(detected, "XPOS", i, NULL));
00719 cpl_matrix_set(data, 1, i, cpl_table_get(detected, "YPOS", i, NULL));
00720 }
00721
00722
00723
00724
00725 for (i = 0; i < nref; i++) {
00726 double ra = cpl_table_get(aReference, "RA", i, NULL),
00727 dec = cpl_table_get(aReference, "DEC", i, NULL),
00728 x, y;
00729 muse_wcs_pixel_from_celestial(wcsin, ra, dec, &x, &y);
00730 cpl_matrix_set(patt, 0, i, x);
00731 cpl_matrix_set(patt, 1, i, y);
00732 #if 0
00733 printf("conversion: %2d\t%.7f %.7f\t%6.2f %6.2f\n", i + 1, ra, dec, x, y);
00734 fflush(stdout);
00735 #endif
00736 }
00737 #if 0
00738 if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
00739 printf("%s: data matrix:\n", __func__);
00740 cpl_matrix_dump(data, stdout);
00741 printf("%s: patt matrix:\n", __func__);
00742 cpl_matrix_dump(patt, stdout);
00743 fflush(stdout);
00744 }
00745 #endif
00746
00747
00748
00749 double accuracy0 = sqrt(pow(cpl_table_get_column_mean(detected, "XERR"), 2) +
00750 pow(cpl_table_get_column_mean(detected, "YERR"), 2));
00751
00752
00753 double accuracy = accuracy0 * aFAccuracy,
00754 radius = aRadius;
00755 int nid = INT_MAX;
00756 cpl_array *aid = NULL;
00757 cpl_boolean dupes = CPL_FALSE;
00758 double xscale, yscale;
00759 do {
00760 double scale, angle;
00761
00762
00763
00764 #define USE_DATA 15
00765 #define USE_PATT 10
00766 int ndata = ndet < USE_DATA ? ndet : USE_DATA,
00767 npatt = nref < USE_PATT ? nref : USE_PATT;
00768 cpl_array_delete(aid);
00769 do {
00770 cpl_msg_debug(__func__, "trying pattern matching with accuracy %g and "
00771 "radius %g", accuracy, radius);
00772 aid = cpl_ppm_match_points(data, ndata, accuracy,
00773 patt, npatt, 1. ,
00774 0.1 , radius, NULL, NULL,
00775 &scale, &angle);
00776
00777 accuracy /= aid ? 1. : 1.5;
00778 if (accuracy < FLT_EPSILON) {
00779 break;
00780 }
00781 } while (!aid);
00782 cpl_errorstate state = cpl_errorstate_get();
00783 nid = cpl_array_get_size(aid);
00784 if (nid > 0) {
00785 nid -= cpl_array_count_invalid(aid);
00786 }
00787 #if 0
00788 printf("aid (valid=%d):\n", nid);
00789 cpl_array_dump(aid, 0, cpl_array_get_size(aid), stdout);
00790 fflush(stdout);
00791 #endif
00792 if (nid < 1) {
00793 cpl_array_delete(aid);
00794 cpl_matrix_delete(data);
00795 cpl_matrix_delete(patt);
00796 cpl_errorstate_set(state);
00797 cpl_propertylist_delete(wcsin);
00798 return cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND, "None of "
00799 "the %d detections could be identified with "
00800 "the %d reference positions with radius %.3f "
00801 "pix (starting value %.3f) and data accuracy "
00802 "%.3e pix (intrinsic mean error %.3e)", ndet,
00803 nref, radius, aRadius, accuracy, accuracy0);
00804 }
00805
00806
00807 muse_wcs_get_scales(wcsin, &xscale, &yscale);
00808 dupes = muse_cplarray_has_duplicate(aid);
00809 cpl_msg_debug(__func__, "%d %sidentified points [scale = %g, angle = %g; "
00810 "used radius = %.3f pix (starting value %.3f), data accuracy "
00811 "= %.3e pix (intrinsic mean error %.3e)]", nid,
00812 dupes ? "(non-unique!) " : "unique ",
00813 scale*1800.*(xscale+yscale), angle, radius, aRadius, accuracy,
00814 accuracy0);
00815 radius /= 1.25;
00816 } while (dupes);
00817 cpl_matrix_delete(data);
00818 cpl_matrix_delete(patt);
00819
00820
00821
00822
00823 cpl_matrix *mpx = cpl_matrix_new(nid, 2),
00824 *msky = cpl_matrix_new(nid, 2);
00825 int iid = 0;
00826 for (i = 0; i < cpl_array_get_size(aid); i++) {
00827 if (!cpl_array_is_valid(aid, i)) {
00828 continue;
00829 }
00830 int idata = cpl_array_get_int(aid, i, NULL);
00831 cpl_matrix_set(mpx, iid, 0, cpl_table_get(detected, "XPOS", idata, NULL));
00832 cpl_matrix_set(mpx, iid, 1, cpl_table_get(detected, "YPOS", idata, NULL));
00833 cpl_matrix_set(msky, iid, 0, cpl_table_get(aReference, "RA", i, NULL));
00834 cpl_matrix_set(msky, iid, 1, cpl_table_get(aReference, "DEC", i, NULL));
00835 #if 0
00836 printf("matrices: %2d\t%.7f %.7f\t%6.2f %6.2f\n", iid + 1,
00837 cpl_table_get(aReference, "RA", i, NULL),
00838 cpl_table_get(aReference, "DEC", i, NULL),
00839 cpl_table_get(detected, "XPOS", idata, NULL),
00840 cpl_table_get(detected, "YPOS", idata, NULL));
00841 #endif
00842 iid++;
00843 }
00844 #if 0
00845 printf("mpx:\n");
00846 cpl_matrix_dump(mpx, stdout);
00847 printf("msky:\n");
00848 cpl_matrix_dump(msky, stdout);
00849 fflush(stdout);
00850 #endif
00851 cpl_array_delete(aid);
00852
00853
00854 cpl_propertylist *wcsout = NULL;
00855 cpl_error_code rc = cpl_wcs_platesol(wcsin, msky, mpx, aIter, aThresh,
00856 CPL_WCS_PLATESOL_6, CPL_WCS_MV_CRVAL,
00857 &wcsout);
00858 if (aWCSObj->cube) {
00859 cpl_propertylist_copy_property_regexp(wcsout, aWCSObj->cube->header,
00860 CPL_WCS_REGEXP, 1);
00861 }
00862 cpl_matrix_delete(mpx);
00863 cpl_matrix_delete(msky);
00864 cpl_propertylist_delete(wcsin);
00865 if (rc != CPL_ERROR_NONE) {
00866 cpl_msg_warning(__func__, "Computing the WCS solution returned an error "
00867 "(%d): %s", rc, cpl_error_get_message());
00868 }
00869
00870
00871 double cd11 = muse_pfits_get_cd(wcsout, 1, 1),
00872 cd22 = muse_pfits_get_cd(wcsout, 2, 2),
00873 cd12 = muse_pfits_get_cd(wcsout, 1, 2),
00874 cd21 = muse_pfits_get_cd(wcsout, 2, 1),
00875 det = cd11 * cd22 - cd12 * cd21;
00876 if (det < 0.) {
00877 cd12 *= -1;
00878 cd11 *= -1;
00879 }
00880
00881 double xang, yang;
00882 muse_wcs_get_angles(wcsout, &xang, &yang);
00883 muse_wcs_get_scales(wcsout, &xscale, &yscale);
00884 xscale *= 3600.;
00885 yscale *= 3600.;
00886 cpl_msg_info(__func__, "astrometric calibration results: scales %f/%f "
00887 "arcsec/spaxel, rotation %g/%g deg", xscale, yscale, xang, yang);
00888
00889 #if 0
00890 printf("%s: output propertylist (rc = %d):\n", __func__, rc);
00891 fflush(stdout);
00892 cpl_propertylist_save(wcsout, "astrometry_wcsout.fits", CPL_IO_CREATE);
00893 system("fold -w 80 astrometry_wcsout.fits | grep -v \"^ \"");
00894 remove("astrometry_wcsout.fits");
00895 #endif
00896
00897
00898 cpl_propertylist_update_int(wcsout, QC_ASTROMETRY_NSTARS, nid);
00899
00900 cpl_propertylist_update_float(wcsout, QC_ASTROMETRY_SCX, xscale);
00901 cpl_propertylist_update_float(wcsout, QC_ASTROMETRY_SCY, yscale);
00902
00903 cpl_propertylist_update_float(wcsout, QC_ASTROMETRY_ANGX, xang);
00904 cpl_propertylist_update_float(wcsout, QC_ASTROMETRY_ANGY, yang);
00905
00906 double medresx = cpl_propertylist_get_double(wcsout, "CSYER1") * 3600.,
00907 medresy = cpl_propertylist_get_double(wcsout, "CSYER2") * 3600.;
00908 cpl_propertylist_update_float(wcsout, QC_ASTROMETRY_RESX, medresx);
00909 cpl_propertylist_update_float(wcsout, QC_ASTROMETRY_RESY, medresy);
00910
00911
00912 cpl_propertylist_update_float(wcsout, MUSE_HDR_WCS_RADIUS, radius);
00913 cpl_propertylist_set_comment(wcsout, MUSE_HDR_WCS_RADIUS,
00914 MUSE_HDR_WCS_RADIUS_C);
00915 cpl_propertylist_update_float(wcsout, MUSE_HDR_WCS_ACCURACY, accuracy);
00916 cpl_propertylist_set_comment(wcsout, MUSE_HDR_WCS_ACCURACY,
00917 MUSE_HDR_WCS_ACCURACY_C);
00918 cpl_propertylist_update_float(wcsout, MUSE_HDR_WCS_FACCURACY,
00919 accuracy / accuracy0);
00920 cpl_propertylist_set_comment(wcsout, MUSE_HDR_WCS_FACCURACY,
00921 MUSE_HDR_WCS_FACCURACY_C);
00922
00923 aWCSObj->wcs = wcsout;
00924 return rc;
00925 }
00926
00927
00983
00984 cpl_error_code
00985 muse_wcs_optimize_solution(muse_wcs_object *aWCSObj, float aDetSigma,
00986 muse_wcs_centroid_type aCentroid,
00987 cpl_table *aReference, float aRadius,
00988 float aFAccuracy, int aIter, float aRejSigma)
00989 {
00990 cpl_ensure_code(aWCSObj && aWCSObj->cube, CPL_ERROR_NULL_INPUT);
00991
00992 cpl_ensure_code(!strncmp(cpl_array_get_string(aWCSObj->cube->recnames, 1),
00993 MUSE_WCS_DETIMAGE_EXTNAME, strlen(MUSE_WCS_DETIMAGE_EXTNAME) + 1),
00994 CPL_ERROR_DATA_NOT_FOUND);
00995 cpl_ensure_code(aDetSigma < 0., CPL_ERROR_ILLEGAL_INPUT);
00996 switch (aCentroid) {
00997 case MUSE_WCS_CENTROID_GAUSSIAN:
00998 case MUSE_WCS_CENTROID_MOFFAT:
00999 case MUSE_WCS_CENTROID_BOX:
01000 break;
01001 default:
01002 return cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
01003 }
01004 int nref = cpl_table_get_nrow(aReference);
01005 cpl_ensure_code(nref > 0, CPL_ERROR_ILLEGAL_INPUT);
01006 cpl_ensure_code(muse_cpltable_check(aReference, muse_wcs_reference_def)
01007 == CPL_ERROR_NONE, CPL_ERROR_BAD_FILE_FORMAT);
01008 cpl_ensure_code(aRadius > 0.&& aFAccuracy > 0., CPL_ERROR_ILLEGAL_INPUT);
01009
01010 float detsigma = fabsf(aDetSigma),
01011 lolimit = 0.9999;
01012 muse_image *detimage = muse_imagelist_get(aWCSObj->cube->recimages, 1);
01013
01014
01015 cpl_table_delete(aWCSObj->detected);
01016 aWCSObj->detected = NULL;
01017 cpl_propertylist_delete(aWCSObj->wcs);
01018 aWCSObj->wcs = NULL;
01019
01020 cpl_table *tres = cpl_table_new(lround((detsigma - lolimit) / 0.1) + 1);
01021 cpl_table_new_column(tres, "detsigma", CPL_TYPE_FLOAT);
01022 cpl_table_set_column_format(tres, "detsigma", "%.3f");
01023 cpl_table_new_column(tres, "ndet", CPL_TYPE_INT);
01024 cpl_table_new_column(tres, "nid", CPL_TYPE_INT);
01025 cpl_table_new_column(tres, "scalex", CPL_TYPE_FLOAT);
01026 cpl_table_set_column_format(tres, "scalex", "%.4f");
01027 cpl_table_new_column(tres, "scaley", CPL_TYPE_FLOAT);
01028 cpl_table_set_column_format(tres, "scaley", "%.4f");
01029 cpl_table_new_column(tres, "anglex", CPL_TYPE_FLOAT);
01030 cpl_table_set_column_format(tres, "anglex", "%.3f");
01031 cpl_table_new_column(tres, "angley", CPL_TYPE_FLOAT);
01032 cpl_table_set_column_format(tres, "angley", "%.3f");
01033 cpl_table_new_column(tres, "medresx", CPL_TYPE_FLOAT);
01034 cpl_table_set_column_format(tres, "medresx", "%.3f");
01035 cpl_table_new_column(tres, "medresy", CPL_TYPE_FLOAT);
01036 cpl_table_set_column_format(tres, "medresy", "%.3f");
01037
01038
01039 cpl_error_code rc = CPL_ERROR_NONE;
01040 float ds;
01041 int irow;
01042 for (ds = detsigma, irow = 0; ds > lolimit; ds -= 0.1, irow++) {
01043 cpl_msg_debug(__func__, "searching for solution with detection sigma %.3f", ds);
01044 cpl_msg_indent_more();
01045
01046
01047 cpl_msg_severity level = cpl_msg_get_level();
01048 cpl_msg_set_level(CPL_MSG_WARNING);
01049
01050
01051 aWCSObj->detected = muse_wcs_centroid_stars(detimage, ds, aCentroid);
01052 cpl_table_set_float(tres, "detsigma", irow, ds);
01053 cpl_table_set_int(tres, "ndet", irow, cpl_table_get_nrow(aWCSObj->detected));
01054
01055
01056 cpl_errorstate state = cpl_errorstate_get();
01057 rc = muse_wcs_solve(aWCSObj, aReference, aRadius, aFAccuracy, aIter,
01058 aRejSigma);
01059 if (rc == CPL_ERROR_NONE && aWCSObj->wcs) {
01060 cpl_table_set_int(tres, "nid", irow,
01061 cpl_propertylist_get_int(aWCSObj->wcs, QC_ASTROMETRY_NSTARS));
01062 cpl_table_set_float(tres, "scalex", irow,
01063 cpl_propertylist_get_float(aWCSObj->wcs, QC_ASTROMETRY_SCX));
01064 cpl_table_set_float(tres, "scaley", irow,
01065 cpl_propertylist_get_float(aWCSObj->wcs, QC_ASTROMETRY_SCY));
01066 cpl_table_set_float(tres, "anglex", irow,
01067 cpl_propertylist_get_float(aWCSObj->wcs, QC_ASTROMETRY_ANGX));
01068 cpl_table_set_float(tres, "angley", irow,
01069 cpl_propertylist_get_float(aWCSObj->wcs, QC_ASTROMETRY_ANGY));
01070 cpl_table_set_float(tres, "medresx", irow,
01071 cpl_propertylist_get_float(aWCSObj->wcs, QC_ASTROMETRY_RESX));
01072 cpl_table_set_float(tres, "medresy", irow,
01073 cpl_propertylist_get_float(aWCSObj->wcs, QC_ASTROMETRY_RESY));
01074 cpl_propertylist_delete(aWCSObj->wcs);
01075 aWCSObj->wcs = NULL;
01076 } else {
01077 cpl_errorstate_set(state);
01078 }
01079 cpl_table_delete(aWCSObj->detected);
01080 aWCSObj->detected = NULL;
01081 cpl_msg_set_level(level);
01082 cpl_msg_indent_less();
01083 }
01084
01085 cpl_boolean debug = getenv("MUSE_DEBUG_WCS") && atoi(getenv("MUSE_DEBUG_WCS")) >= 1;
01086 if (debug) {
01087 printf("%s: full table of results:\n", __func__);
01088 cpl_table_dump(tres, 0, 1000, stdout);
01089 fflush(stdout);
01090 }
01091
01092
01093 double scale = muse_pfits_get_mode(aWCSObj->cube->header) > MUSE_MODE_WFM_AO_N
01094 ? kMuseSpaxelSizeX_NFM : kMuseSpaxelSizeY_WFM;
01095 cpl_msg_debug(__func__, "pruning results +/-10%% away from expected spaxel "
01096 "scale of %.3f arcsec/pixel", scale);
01097 cpl_table_unselect_all(tres);
01098 cpl_table_or_selected_float(tres, "scalex", CPL_GREATER_THAN, scale * 1.1);
01099 cpl_table_or_selected_float(tres, "scalex", CPL_LESS_THAN, scale * 0.9);
01100 cpl_table_or_selected_float(tres, "scaley", CPL_GREATER_THAN, scale * 1.1);
01101 cpl_table_or_selected_float(tres, "scaley", CPL_LESS_THAN, scale * 0.9);
01102 cpl_table_or_selected_invalid(tres, "nid");
01103 cpl_table_erase_selected(tres);
01104 if (debug) {
01105 printf("%s: pruned table of results:\n", __func__);
01106 cpl_table_dump(tres, 0, 1000, stdout);
01107 fflush(stdout);
01108 }
01109
01110 if (cpl_table_get_nrow(tres) < 1) {
01111 cpl_table_delete(tres);
01112 cpl_msg_error(__func__, "No valid solution found in the range %.3f .. %.3f "
01113 "sigma", detsigma, lolimit);
01114 return cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
01115 }
01116
01117
01118
01119
01120
01121 cpl_table_cast_column(tres, "nid", "weight", CPL_TYPE_DOUBLE);
01122 cpl_table_set_column_format(tres, "weight", "%.5g");
01123 cpl_table_divide_scalar(tres, "weight", 50.);
01124 cpl_table_divide_columns(tres, "weight", "medresx");
01125 cpl_table_multiply_scalar(tres, "weight",
01126 cpl_table_get_column_min(tres, "medresx"));
01127 cpl_table_divide_columns(tres, "weight", "medresy");
01128 cpl_table_multiply_scalar(tres, "weight",
01129 cpl_table_get_column_min(tres, "medresy"));
01130 cpl_propertylist *sort = cpl_propertylist_new();
01131 cpl_propertylist_append_bool(sort, "weight", CPL_TRUE);
01132 cpl_propertylist_append_bool(sort, "nid", CPL_TRUE);
01133 cpl_table_sort(tres, sort);
01134 cpl_propertylist_delete(sort);
01135
01136 detsigma = cpl_table_get_float(tres, "detsigma", 0, NULL);
01137 if (debug) {
01138 printf("%s: pruned and sorted table of results:\n", __func__);
01139 cpl_table_dump(tres, 0, 1000, stdout);
01140 printf("%s: ===> use the %.3f-sigma level\n", __func__, detsigma);
01141 fflush(stdout);
01142 }
01143 cpl_table_delete(tres);
01144
01145
01146 aWCSObj->detected = muse_wcs_centroid_stars(detimage, detsigma, aCentroid);
01147 rc = muse_wcs_solve(aWCSObj, aReference, aRadius, aFAccuracy, aIter, aRejSigma);
01148
01149
01150 if (aWCSObj->wcs) {
01151 cpl_propertylist_update_float(aWCSObj->wcs, MUSE_HDR_WCS_DETSIGMA, detsigma);
01152 cpl_propertylist_set_comment(aWCSObj->wcs, MUSE_HDR_WCS_DETSIGMA,
01153 MUSE_HDR_WCS_DETSIGMA_C);
01154 }
01155
01156 return rc;
01157 }
01158
01159
01169
01170 cpl_propertylist *
01171 muse_wcs_create_default(void)
01172 {
01173 cpl_propertylist *wcs = cpl_propertylist_new();
01174
01175
01176
01177 cpl_propertylist_append_int(wcs, "WCSAXES", 2);
01178
01179
01180
01181
01182 double smallvalue = FLT_MIN;
01183 const char *cpldesc = cpl_get_description(CPL_DESCRIPTION_DEFAULT);
01184
01185 char *pcpldesc = strstr(cpldesc, "WCSLIB = ");
01186 if (pcpldesc) {
01187 pcpldesc += 8;
01188 double wcslibversion = atof(pcpldesc);
01189 if (wcslibversion >= 4.23) {
01190 smallvalue = 0.;
01191 }
01192 }
01193
01194
01195
01196 cpl_propertylist_append_double(wcs, "CRPIX1", smallvalue);
01197
01198 cpl_propertylist_append_double(wcs, "CD1_1", -kMuseSpaxelSizeX_WFM / 3600);
01199 cpl_propertylist_append_string(wcs, "CTYPE1", "RA---TAN");
01200 cpl_propertylist_append_string(wcs, "CUNIT1", "deg");
01201
01202
01203 cpl_propertylist_append_double(wcs, "CRPIX2", smallvalue);
01204 cpl_propertylist_append_double(wcs, "CD2_2", kMuseSpaxelSizeY_WFM / 3600);
01205 cpl_propertylist_append_string(wcs, "CTYPE2", "DEC--TAN");
01206 cpl_propertylist_append_string(wcs, "CUNIT2", "deg");
01207
01208
01209 cpl_propertylist_append_double(wcs, "CD1_2", 0.);
01210 cpl_propertylist_append_double(wcs, "CD2_1", 0.);
01211
01212
01213
01214 return wcs;
01215 }
01216
01217
01232
01233 cpl_propertylist *
01234 muse_wcs_apply_cd(const cpl_propertylist *aExpHeader,
01235 const cpl_propertylist *aCalHeader)
01236 {
01237 cpl_ensure(aCalHeader && aExpHeader, CPL_ERROR_NULL_INPUT, NULL);
01238
01239
01240 cpl_propertylist *header = cpl_propertylist_duplicate(aCalHeader);
01241
01242
01243
01244 double pa = muse_astro_posangle(aExpHeader) * CPL_MATH_RAD_DEG,
01245 xang, yang, xsc, ysc;
01246 muse_wcs_get_scales(header, &xsc, &ysc);
01247 muse_wcs_get_angles(header, &xang, &yang);
01248 cpl_msg_debug(__func__, "WCS solution: scales %f / %f arcsec, angles %f / %f "
01249 "deg", xsc * 3600., ysc * 3600., xang, yang);
01250
01251 cpl_matrix *pc = cpl_matrix_new(2, 2);
01252 cpl_matrix_set(pc, 0, 0, muse_pfits_get_cd(header, 1, 1) / xsc);
01253 cpl_matrix_set(pc, 0, 1, muse_pfits_get_cd(header, 1, 2) / xsc);
01254 cpl_matrix_set(pc, 1, 0, muse_pfits_get_cd(header, 2, 1) / ysc);
01255 cpl_matrix_set(pc, 1, 1, muse_pfits_get_cd(header, 2, 2) / ysc);
01256 cpl_matrix *pcinv = cpl_matrix_invert_create(pc);
01257 cpl_matrix_delete(pc);
01258
01259 double cd11cor, cd12cor, cd21cor, cd22cor;
01260 if (pcinv) {
01261 cd11cor = xsc * cpl_matrix_get(pcinv, 0, 0);
01262 cd12cor = xsc * cpl_matrix_get(pcinv, 0, 1);
01263 cd21cor = ysc * cpl_matrix_get(pcinv, 1, 0);
01264 cd22cor = ysc * cpl_matrix_get(pcinv, 1, 1);
01265 cpl_matrix_delete(pcinv);
01266 } else {
01267 cpl_msg_warning(__func__, "CD matrix of astrometric solution could not "
01268 "be inverted! Assuming negligible zeropoint rotation.");
01269 cd11cor = xsc * 1.;
01270 cd12cor = xsc * 0.;
01271 cd21cor = ysc * 0.;
01272 cd22cor = ysc * 1.;
01273 }
01274
01275 double cd11 = cos(pa) * cd11cor - sin(pa) * cd21cor,
01276 cd12 = cos(pa) * cd12cor - sin(pa) * cd22cor,
01277 cd21 = sin(pa) * cd11cor + cos(pa) * cd21cor,
01278 cd22 = sin(pa) * cd12cor + cos(pa) * cd22cor;
01279 cpl_propertylist_update_double(header, "CD1_1", cd11),
01280 cpl_propertylist_update_double(header, "CD1_2", cd12),
01281 cpl_propertylist_update_double(header, "CD2_1", cd21);
01282 cpl_propertylist_update_double(header, "CD2_2", cd22),
01283 muse_wcs_get_scales(header, &xsc, &ysc);
01284 muse_wcs_get_angles(header, &xang, &yang);
01285 cpl_msg_debug(__func__, "Updated CD matrix (%f deg field rotation): "
01286 "%e %e %e %e (scales %f / %f arcsec, angles %f / %f deg)",
01287 pa * CPL_MATH_DEG_RAD, cd11, cd12, cd21, cd22,
01288 xsc * 3600., ysc * 3600., xang, yang);
01289 return header;
01290 }
01291
01292
01321
01322 cpl_error_code
01323 muse_wcs_project_tan(muse_pixtable *aPixtable, const cpl_propertylist *aWCS)
01324 {
01325 cpl_size nrow = muse_pixtable_get_nrow(aPixtable);
01326
01327 cpl_ensure_code(nrow > 0 && aPixtable->header && aWCS, CPL_ERROR_NULL_INPUT);
01328 cpl_ensure_code(muse_pixtable_wcs_check(aPixtable) == MUSE_PIXTABLE_WCS_PIXEL,
01329 CPL_ERROR_INCOMPATIBLE_INPUT);
01330
01331 const char *type1 = muse_pfits_get_ctype(aWCS, 1),
01332 *type2 = muse_pfits_get_ctype(aWCS, 2);
01333 cpl_ensure_code(type1 && type2 && !strncmp(type1, "RA---TAN", 9) &&
01334 !strncmp(type2, "DEC--TAN", 9),
01335 CPL_ERROR_UNSUPPORTED_MODE);
01336
01337
01338
01339 cpl_propertylist_erase_regexp(aPixtable->header, MUSE_WCS_KEYS, 0);
01340
01341
01342
01343 cpl_propertylist *header = muse_wcs_apply_cd(aPixtable->header, aWCS);
01344
01345
01346 cpl_propertylist_erase_regexp(header, "^CRVAL[0-9]+$|^L[OA][NT]POLE$", 0);
01347 double cd11 = muse_pfits_get_cd(header, 1, 1),
01348 cd12 = muse_pfits_get_cd(header, 1, 2),
01349 cd21 = muse_pfits_get_cd(header, 2, 1),
01350 cd22 = muse_pfits_get_cd(header, 2, 2);
01351
01352
01353
01354
01355 cpl_errorstate prestate = cpl_errorstate_get();
01356 double xlo = cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_PREDAR_XLO),
01357 xhi = cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_PREDAR_XHI),
01358 ylo = cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_PREDAR_YLO),
01359 yhi = cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_PREDAR_YHI);
01360 if (!cpl_errorstate_is_equal(prestate)) {
01361 cpl_errorstate_set(prestate);
01362
01363 xlo = cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_XLO);
01364 xhi = cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_XHI);
01365 ylo = cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_YLO);
01366 yhi = cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_YHI);
01367 }
01368 double wcspix1 = muse_pfits_get_crpix(header, 1),
01369 wcspix2 = muse_pfits_get_crpix(header, 2),
01370 crpix1 = (xhi + xlo) / 2. + wcspix1,
01371 crpix2 = (yhi + ylo) / 2. + wcspix2;
01372 cpl_propertylist_update_double(header, "CRPIX1", crpix1),
01373 cpl_propertylist_update_double(header, "CRPIX2", crpix2),
01374 cpl_msg_debug(__func__, "Using reference pixel %f/%f (limits in pixel table "
01375 "%f..%f/%f..%f, WCS correction %f,%f)", crpix1, crpix2,
01376 xlo, xhi, ylo, yhi, wcspix1, wcspix2);
01377
01378
01379 cpl_table_set_column_unit(aPixtable->table, MUSE_PIXTABLE_XPOS, "");
01380 cpl_table_set_column_unit(aPixtable->table, MUSE_PIXTABLE_YPOS, "");
01381
01382
01383
01384 float *xpos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_XPOS),
01385 *ypos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_YPOS);
01386 cpl_size n;
01387 #pragma omp parallel for default(none) \
01388 shared(cd11, cd12, cd21, cd22, crpix1, crpix2, nrow, xpos, ypos)
01389 for (n = 0; n < nrow; n++) {
01390
01391
01392 double x = cd11 * (xpos[n] - crpix1) + cd12 * (ypos[n] - crpix2),
01393 y = cd22 * (ypos[n] - crpix2) + cd21 * (xpos[n] - crpix1);
01394
01395
01396
01397
01398
01399
01400
01401 double phi = atan2(x, -y),
01402 theta = atan(CPL_MATH_DEG_RAD / sqrt(x*x + y*y));
01403 if (phi < 0) {
01404 phi += CPL_MATH_2PI;
01405 }
01406
01407
01408
01409 xpos[n] = phi;
01410 ypos[n] = theta - CPL_MATH_PI_2;
01411 }
01412
01413
01414
01415
01416 cpl_table_set_column_unit(aPixtable->table, MUSE_PIXTABLE_XPOS, "rad");
01417 cpl_table_set_column_unit(aPixtable->table, MUSE_PIXTABLE_YPOS, "rad");
01418 muse_pixtable_compute_limits(aPixtable);
01419
01420 cpl_propertylist_copy_property_regexp(aPixtable->header, header,
01421 MUSE_WCS_KEYS, 0);
01422 cpl_propertylist_delete(header);
01423
01424
01425 cpl_propertylist_update_string(aPixtable->header, MUSE_HDR_PT_WCS,
01426 MUSE_HDR_PT_WCS_PROJ);
01427 cpl_propertylist_set_comment(aPixtable->header, MUSE_HDR_PT_WCS,
01428 MUSE_HDR_PT_WCS_COMMENT_PROJ);
01429 return CPL_ERROR_NONE;
01430 }
01431
01432
01461
01462 cpl_error_code
01463 muse_wcs_position_celestial(muse_pixtable *aPixtable, double aRA, double aDEC)
01464 {
01465 cpl_size nrow = muse_pixtable_get_nrow(aPixtable);
01466
01467 cpl_ensure_code(nrow > 0 && aPixtable->header, CPL_ERROR_NULL_INPUT);
01468 cpl_ensure_code(muse_pixtable_wcs_check(aPixtable) == MUSE_PIXTABLE_WCS_NATSPH,
01469 CPL_ERROR_INCOMPATIBLE_INPUT);
01470
01471 const char *type1 = muse_pfits_get_ctype(aPixtable->header, 1),
01472 *type2 = muse_pfits_get_ctype(aPixtable->header, 2);
01473 cpl_ensure_code(type1 && type2 && !strncmp(type1, "RA---TAN", 9) &&
01474 !strncmp(type2, "DEC--TAN", 9),
01475 CPL_ERROR_UNSUPPORTED_MODE);
01476
01477 cpl_msg_info(__func__, "Adapting WCS to RA/DEC=%f/%f deg", aRA, aDEC);
01478
01479
01480 cpl_table_set_column_unit(aPixtable->table, MUSE_PIXTABLE_XPOS, "");
01481 cpl_table_set_column_unit(aPixtable->table, MUSE_PIXTABLE_YPOS, "");
01482
01483
01484
01485 float *xpos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_XPOS),
01486 *ypos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_YPOS);
01487 double dp = aDEC / CPL_MATH_DEG_RAD;
01488 cpl_size n;
01489 #pragma omp parallel for default(none) \
01490 shared(aDEC, dp, nrow, xpos, ypos)
01491 for (n = 0; n < nrow; n++) {
01492
01493
01494
01495
01496
01497 double phi = xpos[n],
01498 theta = ypos[n] + CPL_MATH_PI_2,
01499 ra = atan2(cos(theta) * sin(phi),
01500 sin(theta) * cos(dp) + cos(theta) * sin(dp) * cos(phi))
01501 * CPL_MATH_DEG_RAD,
01502 dec = asin(sin(theta) * sin(dp) - cos(theta) * cos(dp) * cos(phi))
01503 * CPL_MATH_DEG_RAD;
01504
01505
01506
01507
01508
01509 xpos[n] = ra;
01510 ypos[n] = dec - aDEC;
01511 }
01512
01513
01514
01515 cpl_table_set_column_unit(aPixtable->table, MUSE_PIXTABLE_XPOS, "deg");
01516 cpl_table_set_column_unit(aPixtable->table, MUSE_PIXTABLE_YPOS, "deg");
01517
01518 cpl_propertylist_update_double(aPixtable->header, "CRVAL1", aRA);
01519 cpl_propertylist_update_double(aPixtable->header, "CRVAL2", aDEC);
01520
01521 muse_pixtable_compute_limits(aPixtable);
01522
01523
01524 cpl_propertylist_update_string(aPixtable->header, MUSE_HDR_PT_WCS,
01525 MUSE_HDR_PT_WCS_POSI);
01526 cpl_propertylist_set_comment(aPixtable->header, MUSE_HDR_PT_WCS,
01527 MUSE_HDR_PT_WCS_COMMENT_POSI);
01528 return CPL_ERROR_NONE;
01529 }
01530
01531
01554
01555 cpl_error_code
01556 muse_wcs_celestial_from_pixel(cpl_propertylist *aHeader, double aX, double aY,
01557 double *aRA, double *aDEC)
01558 {
01559 cpl_ensure_code(aHeader && aRA && aDEC, CPL_ERROR_NULL_INPUT);
01560 const char *type1 = muse_pfits_get_ctype(aHeader, 1),
01561 *type2 = muse_pfits_get_ctype(aHeader, 2);
01562 cpl_ensure_code(type1 && type2 && !strncmp(type1, "RA---TAN", 9) &&
01563 !strncmp(type2, "DEC--TAN", 9),
01564 CPL_ERROR_UNSUPPORTED_MODE);
01565
01566 muse_wcs *wcs = muse_wcs_new(aHeader);
01567 muse_wcs_celestial_from_pixel_fast(wcs, aX, aY, aRA, aDEC);
01568 cpl_free(wcs);
01569
01570 return CPL_ERROR_NONE;
01571 }
01572
01573
01598
01599 cpl_error_code
01600 muse_wcs_pixel_from_celestial(cpl_propertylist *aHeader, double aRA, double aDEC,
01601 double *aX, double *aY)
01602 {
01603 cpl_ensure_code(aHeader && aX && aY, CPL_ERROR_NULL_INPUT);
01604
01605 const char *type1 = muse_pfits_get_ctype(aHeader, 1),
01606 *type2 = muse_pfits_get_ctype(aHeader, 2);
01607 cpl_ensure_code(type1 && type2 && !strncmp(type1, "RA---TAN", 9) &&
01608 !strncmp(type2, "DEC--TAN", 9),
01609 CPL_ERROR_UNSUPPORTED_MODE);
01610
01611 muse_wcs *wcs = muse_wcs_new(aHeader);
01612 if (wcs->cddet == 0.) {
01613 *aX = *aY = NAN;
01614 cpl_error_set(__func__, CPL_ERROR_SINGULAR_MATRIX);
01615 cpl_free(wcs);
01616 return CPL_ERROR_SINGULAR_MATRIX;
01617 }
01618 wcs->crval1 /= CPL_MATH_DEG_RAD;
01619 wcs->crval2 /= CPL_MATH_DEG_RAD;
01620 muse_wcs_pixel_from_celestial_fast(wcs, aRA / CPL_MATH_DEG_RAD,
01621 aDEC / CPL_MATH_DEG_RAD, aX, aY);
01622 cpl_free(wcs);
01623
01624 return CPL_ERROR_NONE;
01625 }
01626
01627
01653
01654 cpl_error_code
01655 muse_wcs_projplane_from_celestial(cpl_propertylist *aHeader, double aRA,
01656 double aDEC, double *aX, double *aY)
01657 {
01658 cpl_ensure_code(aHeader && aX && aY, CPL_ERROR_NULL_INPUT);
01659
01660 const char *type1 = muse_pfits_get_ctype(aHeader, 1),
01661 *type2 = muse_pfits_get_ctype(aHeader, 2);
01662 cpl_ensure_code(type1 && type2 && !strncmp(type1, "RA---TAN", 9) &&
01663 !strncmp(type2, "DEC--TAN", 9),
01664 CPL_ERROR_UNSUPPORTED_MODE);
01665
01666
01667 double a = aRA / CPL_MATH_DEG_RAD,
01668 d = aDEC / CPL_MATH_DEG_RAD,
01669
01670 ap = muse_pfits_get_crval(aHeader, 1) / CPL_MATH_DEG_RAD,
01671 dp = muse_pfits_get_crval(aHeader, 2) / CPL_MATH_DEG_RAD,
01672 phi = atan2(-cos(d) * sin(a - ap),
01673 sin(d) * cos(dp) - cos(d) * sin(dp) * cos(a-ap))
01674 + 180 / CPL_MATH_DEG_RAD,
01675 theta = asin(sin(d) * sin(dp) + cos(d) * cos(dp) * cos(a-ap)),
01676 R_theta = CPL_MATH_DEG_RAD / tan(theta);
01677
01678 *aX = R_theta * sin(phi),
01679 *aY = -R_theta * cos(phi);
01680
01681 return CPL_ERROR_NONE;
01682 }
01683
01684
01701
01702 cpl_error_code
01703 muse_wcs_projplane_from_pixel(cpl_propertylist *aHeader, double aX, double aY,
01704 double *aXOut, double *aYOut)
01705 {
01706 cpl_ensure_code(aHeader && aXOut && aYOut, CPL_ERROR_NULL_INPUT);
01707
01708 muse_wcs *wcs = muse_wcs_new(aHeader);
01709 muse_wcs_projplane_from_pixel_fast(wcs, aX, aY, aXOut, aYOut);
01710 cpl_free(wcs);
01711
01712 return CPL_ERROR_NONE;
01713 }
01714
01715
01734
01735 cpl_error_code
01736 muse_wcs_pixel_from_projplane(cpl_propertylist *aHeader, double aX, double aY,
01737 double *aXOut, double *aYOut)
01738 {
01739 cpl_ensure_code(aHeader && aXOut && aYOut, CPL_ERROR_NULL_INPUT);
01740
01741 muse_wcs *wcs = muse_wcs_new(aHeader);
01742 if (wcs->cddet == 0.) {
01743 *aXOut = *aYOut = NAN;
01744 cpl_error_set(__func__, CPL_ERROR_SINGULAR_MATRIX);
01745 cpl_free(wcs);
01746 return CPL_ERROR_SINGULAR_MATRIX;
01747 }
01748 muse_wcs_pixel_from_projplane_fast(wcs, aX, aY, aXOut, aYOut);
01749 cpl_free(wcs);
01750
01751 return CPL_ERROR_NONE;
01752 }
01753
01754
01773
01774 cpl_error_code
01775 muse_wcs_get_angles(cpl_propertylist *aHeader, double *aXAngle, double *aYAngle)
01776 {
01777 cpl_ensure_code(aHeader && aXAngle && aYAngle, CPL_ERROR_NULL_INPUT);
01778
01779 cpl_errorstate prestate = cpl_errorstate_get();
01780 double cd11 = muse_pfits_get_cd(aHeader, 1, 1),
01781 cd22 = muse_pfits_get_cd(aHeader, 2, 2),
01782 cd12 = muse_pfits_get_cd(aHeader, 1, 2),
01783 cd21 = muse_pfits_get_cd(aHeader, 2, 1),
01784 det = cd11 * cd22 - cd12 * cd21;
01785 cpl_ensure_code(cpl_errorstate_is_equal(prestate), cpl_error_get_code());
01786 if (det < 0.) {
01787 cd12 *= -1;
01788 cd11 *= -1;
01789 }
01790 if (cd12 == 0. && cd21 == 0.) {
01791 *aXAngle = 0.;
01792 *aYAngle = 0.;
01793 return CPL_ERROR_NONE;
01794 }
01795
01796 *aXAngle = atan2(cd12, cd11) * CPL_MATH_DEG_RAD;
01797 *aYAngle = atan2(-cd21, cd22) * CPL_MATH_DEG_RAD;
01798 return CPL_ERROR_NONE;
01799 }
01800
01801
01820
01821 cpl_error_code
01822 muse_wcs_get_scales(cpl_propertylist *aHeader, double *aXScale, double *aYScale)
01823 {
01824 cpl_ensure_code(aHeader && aXScale && aYScale, CPL_ERROR_NULL_INPUT);
01825
01826 cpl_errorstate prestate = cpl_errorstate_get();
01827 double cd11 = muse_pfits_get_cd(aHeader, 1, 1),
01828 cd22 = muse_pfits_get_cd(aHeader, 2, 2),
01829 cd12 = muse_pfits_get_cd(aHeader, 1, 2),
01830 cd21 = muse_pfits_get_cd(aHeader, 2, 1),
01831 det = cd11 * cd22 - cd12 * cd21;
01832 cpl_ensure_code(cpl_errorstate_is_equal(prestate), cpl_error_get_code());
01833
01834 if (det < 0.) {
01835 cd12 *= -1;
01836 cd11 *= -1;
01837 }
01838 if (cd12 == 0. && cd21 == 0.) {
01839 *aXScale = cd11;
01840 *aYScale = cd22;
01841 return CPL_ERROR_NONE;
01842 }
01843 *aXScale = sqrt(cd11*cd11 + cd12*cd12);
01844 *aYScale = sqrt(cd22*cd22 + cd21*cd21);
01845 return CPL_ERROR_NONE;
01846 }
01847
01848
01865
01866 muse_wcs *
01867 muse_wcs_new(cpl_propertylist *aHeader)
01868 {
01869 muse_wcs *wcs = cpl_calloc(1, sizeof(muse_wcs));
01870 if (!aHeader) {
01871 wcs->cd11 = wcs->cd22 = wcs->cddet = 1.;
01872 return wcs;
01873 }
01874
01875 cpl_errorstate prestate = cpl_errorstate_get();
01876 wcs->crpix1 = muse_pfits_get_crpix(aHeader, 1);
01877 wcs->crpix2 = muse_pfits_get_crpix(aHeader, 2);
01878 wcs->crval1 = muse_pfits_get_crval(aHeader, 1);
01879 wcs->crval2 = muse_pfits_get_crval(aHeader, 2);
01880 if (!cpl_errorstate_is_equal(prestate)) {
01881
01882
01883 cpl_errorstate_set(prestate);
01884 }
01885
01886 prestate = cpl_errorstate_get();
01887 wcs->cd11 = muse_pfits_get_cd(aHeader, 1, 1);
01888 wcs->cd22 = muse_pfits_get_cd(aHeader, 2, 2);
01889 wcs->cd12 = muse_pfits_get_cd(aHeader, 1, 2);
01890 wcs->cd21 = muse_pfits_get_cd(aHeader, 2, 1);
01891 if (!cpl_errorstate_is_equal(prestate) &&
01892 wcs->cd11 == 0. && wcs->cd12 == 0. && wcs->cd21 == 0. && wcs->cd22 == 0.) {
01893
01894
01895 wcs->cd11 = wcs->cd22 = wcs->cddet = 1.;
01896 cpl_errorstate_set(prestate);
01897 }
01898 wcs->cddet = wcs->cd11 * wcs->cd22 - wcs->cd12 * wcs->cd21;
01899 if (wcs->cddet == 0.) {
01900 cpl_error_set(__func__, CPL_ERROR_SINGULAR_MATRIX);
01901 }
01902
01903
01904 if (getenv("MUSE_DEBUG_WCS") && atoi(getenv("MUSE_DEBUG_WCS")) > 0) {
01905 cpl_msg_debug(__func__, "wcs: axis1 = { %f %f %e }, axis2 = { %f %f %e }, "
01906 "crossterms = { %e %e }, det = %e",
01907 wcs->crpix1, wcs->crval1, wcs->cd11,
01908 wcs->crpix2, wcs->crval2, wcs->cd22,
01909 wcs->cd12, wcs->cd21, wcs->cddet);
01910 }
01911 return wcs;
01912 }
01913