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 #if HAVE_POPEN && HAVE_PCLOSE
00030 #define _BSD_SOURCE
00031 #endif
00032 #include <cpl.h>
00033 #include <math.h>
00034 #include <string.h>
00035
00036 #include "muse_wavecalib.h"
00037 #include "muse_instrument.h"
00038
00039 #include "muse_combine.h"
00040 #include "muse_cplwrappers.h"
00041 #include "muse_data_format_z.h"
00042 #include "muse_dfs.h"
00043 #include "muse_pfits.h"
00044 #include "muse_tracing.h"
00045 #include "muse_utils.h"
00046
00047
00048
00049
00050
00051 #define SEARCH_DEBUG 0
00052 #define SEARCH_DEBUG_FILES 0
00053
00054 #define DEBUG_GAUSSFIT 0
00055 #define MUSE_WAVE_LINES_SEARCH_SHIFT_WARN 3.0
00056
00057 #define MUSE_WAVE_LINE_FIT_MAXSHIFT 2.0
00058
00059 #define MUSE_WAVE_LINE_HANDLE_SHIFT_LIMIT 0.25
00060
00061
00062
00066
00067
00070
00095
00096 const muse_cpltable_def muse_wavelines_def[] = {
00097 { "lampno", CPL_TYPE_INT, "", "%d", "Number of the lamp", CPL_TRUE },
00098 { "lampname", CPL_TYPE_STRING, "", "%s", "Name of the lamp", CPL_TRUE },
00099 { "x", CPL_TYPE_DOUBLE, "pix", "%.2f", "x-position on CCD", CPL_TRUE },
00100 { "y", CPL_TYPE_DOUBLE, "pix", "%.2f", "first-guess y-position on CCD", CPL_TRUE },
00101 { "peak", CPL_TYPE_DOUBLE, "pix", "%g", "Peak of line", CPL_TRUE },
00102 { "center", CPL_TYPE_DOUBLE, "pix", "%.4f", "Gaussian line center", CPL_TRUE },
00103 { "cerr", CPL_TYPE_DOUBLE, "pix", "%.4f", "error estimate of line center", CPL_TRUE },
00104 { "sigma", CPL_TYPE_DOUBLE, "pix", "%.3f", "Gaussian sigma", CPL_TRUE },
00105 { "fwhm", CPL_TYPE_DOUBLE, "pix", "%.3f", "Gaussian FWHM", CPL_TRUE },
00106 { "flux", CPL_TYPE_DOUBLE, "count", "%g", "Gaussian area (flux)", CPL_TRUE },
00107 { "bg", CPL_TYPE_DOUBLE, "count", "%.2f", "background level", CPL_TRUE },
00108 { "mse", CPL_TYPE_DOUBLE, "", "%e", "mean squared error", CPL_TRUE },
00109 { "lambda", CPL_TYPE_DOUBLE, "Angstrom", "%9.3f",
00110 "identified wavelength of the line", CPL_TRUE },
00111 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
00112 };
00113
00114
00118
00119 const muse_cpltable_def muse_wavedebug_def[] = {
00120 { "slice", CPL_TYPE_INT, "", "%02d", "slice number", CPL_TRUE},
00121 { "iteration", CPL_TYPE_INT, "", "%d", "iteration", CPL_TRUE},
00122 { "x", CPL_TYPE_INT, "pix", "%04d", "x-position on CCD", CPL_TRUE},
00123 { "y", CPL_TYPE_FLOAT, "pix", "%8.3f", "y-position on CCD", CPL_TRUE},
00124 { "lambda", CPL_TYPE_FLOAT, "Angstrom", "%8.3f", "wavelength", CPL_TRUE},
00125 { "residual", CPL_TYPE_DOUBLE, "Angstrom", "%.4e", "residual at this point", CPL_TRUE},
00126 { "rejlimit", CPL_TYPE_DOUBLE, "Angstrom", "%.4e",
00127 "rejection limit for this iteration", CPL_TRUE},
00128 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
00129 };
00130
00131
00132
00133 const char *muse_wave_weighting_string[] = {
00134 "uniform",
00135 "centroid error",
00136 "per-line RMS scatter",
00137 "centroid error plus per-line RMS scatter"
00138 };
00139
00140
00153
00154 muse_wave_params *
00155 muse_wave_params_new(void)
00156 {
00157 muse_wave_params *p = cpl_malloc(sizeof(muse_wave_params));
00158
00159 p->xorder = 2;
00160 p->yorder = 6;
00161 p->detsigma = 1.;
00162 p->ddisp = 0.05;
00163 p->tolerance = 0.1;
00164 p->linesigma = -1.;
00165 p->rflag = CPL_FALSE;
00166 p->residuals = NULL;
00167 p->fitsigma = -1.;
00168 p->fitweighting = MUSE_WAVE_WEIGHTING_UNIFORM;
00169 p->targetrms = 0.03;
00170 return p;
00171 }
00172
00173
00183
00184 void
00185 muse_wave_params_delete(muse_wave_params *aParams)
00186 {
00187 if (!aParams) {
00188 return;
00189 }
00190 cpl_table_delete(aParams->residuals);
00191 aParams->residuals = NULL;
00192 memset(aParams, 0, sizeof(muse_wave_params));
00193 cpl_free(aParams);
00194 }
00195
00196
00208
00209 static void
00210 muse_wave_calib_qc_peaks(cpl_table *aLines, cpl_propertylist *aHeader,
00211 const unsigned short aSlice)
00212 {
00213 char keyword[KEYWORD_LENGTH];
00214 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_PEAK_MEAN, aSlice);
00215 cpl_propertylist_append_float(aHeader, keyword,
00216 cpl_table_get_column_mean(aLines, "peak"));
00217 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_PEAK_STDEV, aSlice);
00218 cpl_propertylist_append_float(aHeader, keyword,
00219 cpl_table_get_column_stdev(aLines, "peak"));
00220 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_PEAK_MIN, aSlice);
00221 cpl_propertylist_append_float(aHeader, keyword,
00222 cpl_table_get_column_min(aLines, "peak"));
00223 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_PEAK_MAX, aSlice);
00224 cpl_propertylist_append_float(aHeader, keyword,
00225 cpl_table_get_column_max(aLines, "peak"));
00226 if (aSlice != 20) {
00227 return;
00228 }
00229
00230
00231 int n, nlamps = muse_pfits_get_lampnum(aHeader);
00232 for (n = 1; n < nlamps; n++) {
00233
00234 cpl_table_unselect_all(aLines);
00235 cpl_table_or_selected_int(aLines, "lampno", CPL_EQUAL_TO, n);
00236 cpl_table *lamplines = cpl_table_extract_selected(aLines);
00237 if (cpl_table_get_nrow(lamplines) < 1) {
00238 cpl_table_delete(lamplines);
00239 continue;
00240 }
00241 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LAMPl_LINES_PEAK_MEAN,
00242 aSlice, n);
00243 cpl_propertylist_append_float(aHeader, keyword,
00244 cpl_table_get_column_mean(lamplines, "peak"));
00245 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LAMPl_LINES_PEAK_STDEV,
00246 aSlice, n);
00247 cpl_propertylist_append_float(aHeader, keyword,
00248 cpl_table_get_column_stdev(lamplines, "peak"));
00249 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LAMPl_LINES_PEAK_MAX,
00250 aSlice, n);
00251 cpl_propertylist_append_float(aHeader, keyword,
00252 cpl_table_get_column_max(lamplines, "peak"));
00253 cpl_table_delete(lamplines);
00254 }
00255 }
00256
00257
00266
00267 static void
00268 muse_wave_calib_qc_fwhm_old(cpl_table *aLines, cpl_propertylist *aHeader,
00269 cpl_vector *aRefLam, const unsigned short aSlice)
00270 {
00271 int nlines = cpl_table_get_nrow(aLines);
00272
00273
00274 double Rmin = DBL_MAX, Rmax = -DBL_MAX, wlmin = DBL_MAX, wlmax = -DBL_MAX;
00275
00276
00277
00278 cpl_vector *fwhms = cpl_vector_new(nlines),
00279 *resol = cpl_vector_new(cpl_vector_get_size(aRefLam));
00280 int i, iresol = 0;
00281 for (i = 0; i < nlines; i++) {
00282
00283 cpl_errorstate prestate = cpl_errorstate_get();
00284 double fwhm = cpl_table_get(aLines, "fwhm", i, NULL),
00285 sampling = kMuseSpectralSamplingA,
00286 lambda = cpl_table_get(aLines, "lambda", i, NULL),
00287 s1 = (lambda - cpl_table_get(aLines, "lambda", i - 1, NULL))
00288 / (cpl_table_get(aLines, "center", i, NULL)
00289 - cpl_table_get(aLines, "center", i - 1, NULL)),
00290 s2 = (cpl_table_get(aLines, "lambda", i + 1, NULL)
00291 - cpl_table_get(aLines, "lambda", i, NULL))
00292 / (cpl_table_get(aLines, "center", i + 1, NULL)
00293 - cpl_table_get(aLines, "center", i, NULL));
00294 if (!cpl_errorstate_is_equal(prestate)) {
00295 cpl_errorstate_set(prestate);
00296 }
00297 if (i == 0) {
00298 sampling = s2;
00299 } else if (i == nlines - 1) {
00300 sampling = s1;
00301 } else {
00302 sampling = (s1 + s2) / 2.;
00303 }
00304 fwhm *= sampling;
00305 cpl_vector_set(fwhms, i, fwhm);
00306
00307 double R = lambda / fwhm;
00308
00309 if (fabs(cpl_vector_get(aRefLam, cpl_vector_find(aRefLam, lambda)) - lambda)
00310 < FLT_EPSILON) {
00311 if (R < Rmin) {
00312 Rmin = R;
00313 wlmin = lambda;
00314 }
00315 if (R > Rmax) {
00316 Rmax = R;
00317 wlmax = lambda;
00318 }
00319 cpl_vector_set(resol, iresol++, R);
00320 }
00321 }
00322 cpl_vector_set_size(resol, iresol);
00323
00324 char keyword[KEYWORD_LENGTH];
00325 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_FWHM_MEAN, aSlice);
00326 cpl_propertylist_append_float(aHeader, keyword, cpl_vector_get_mean(fwhms));
00327 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_FWHM_STDEV, aSlice);
00328 cpl_propertylist_append_float(aHeader, keyword, cpl_vector_get_stdev(fwhms));
00329 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_FWHM_MIN, aSlice);
00330 cpl_propertylist_append_float(aHeader, keyword, cpl_vector_get_min(fwhms));
00331 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_FWHM_MAX, aSlice);
00332 cpl_propertylist_append_float(aHeader, keyword, cpl_vector_get_max(fwhms));
00333 cpl_vector_delete(fwhms);
00334
00335 cpl_msg_debug(__func__, "Average spectral resolution in IFU %hhu is R=%4.0f, "
00336 "ranging from %4.0f (at %6.1fA) to %4.0f (at %6.1fA)",
00337 muse_utils_get_ifu(aHeader), cpl_vector_get_mean(resol),
00338 Rmin, wlmin, Rmax, wlmax);
00339 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_RESOL, aSlice);
00340 cpl_propertylist_append_float(aHeader, keyword, cpl_vector_get_mean(resol));
00341 cpl_vector_delete(resol);
00342 }
00343
00344
00352
00353 static void
00354 muse_wave_calib_qc_fwhm(cpl_table *aFWHM, cpl_propertylist *aHeader,
00355 const unsigned short aSlice)
00356 {
00357
00358
00359
00360 cpl_table_duplicate_column(aFWHM, "R", aFWHM, "lambda");
00361 cpl_table_divide_columns(aFWHM, "R", "fwhm");
00362 cpl_table_set_column_unit(aFWHM, "R", "");
00363
00364 #if 0
00365
00366 cpl_table_unselect_all(aFWHM);
00367 cpl_table_or_selected_double(aFWHM, "lambda", CPL_LESS_THAN, 6000.);
00368 cpl_table *tblue = cpl_table_extract_selected(aFWHM);
00369
00370 cpl_table_unselect_all(aFWHM);
00371 cpl_table_or_selected_double(aFWHM, "lambda", CPL_GREATER_THAN, 8000.);
00372 cpl_table *tred = cpl_table_extract_selected(aFWHM);
00373
00374 cpl_table_unselect_all(aFWHM);
00375 cpl_table_or_selected_double(aFWHM, "lambda", CPL_NOT_LESS_THAN, 6000.);
00376 cpl_table_and_selected_double(aFWHM, "lambda", CPL_NOT_GREATER_THAN, 8000.);
00377 cpl_table *tgreen = cpl_table_extract_selected(aFWHM);
00378 #if 0
00379 printf("all:\n");
00380 cpl_table_dump(aFWHM, 0, 100000, stdout);
00381 fflush(stdout);
00382 printf("blue:\n");
00383 cpl_table_dump(tblue, 0, 100000, stdout);
00384 fflush(stdout);
00385 printf("green:\n");
00386 cpl_table_dump(tgreen, 0, 100000, stdout);
00387 fflush(stdout);
00388 printf("red:\n");
00389 cpl_table_dump(tred, 0, 100000, stdout);
00390 fflush(stdout);
00391 #endif
00392 #endif
00393
00394
00395 char keyword[KEYWORD_LENGTH];
00396 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_RESOL, aSlice);
00397 double rmean = cpl_table_get_column_mean(aFWHM, "R");
00398 cpl_propertylist_update_float(aHeader, keyword, rmean);
00399
00400 double fmean = cpl_table_get_column_mean(aFWHM, "fwhm"),
00401 fstdev = cpl_table_get_column_stdev(aFWHM, "fwhm"),
00402 flo = cpl_table_get_column_min(aFWHM, "fwhm"),
00403 fhi = cpl_table_get_column_max(aFWHM, "fwhm");
00404 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_FWHM_MEAN, aSlice);
00405 cpl_propertylist_update_float(aHeader, keyword, fmean);
00406 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_FWHM_STDEV, aSlice);
00407 cpl_propertylist_update_float(aHeader, keyword, fstdev);
00408 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_FWHM_MIN, aSlice);
00409 cpl_propertylist_update_float(aHeader, keyword, flo);
00410 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_FWHM_MAX, aSlice);
00411 cpl_propertylist_update_float(aHeader, keyword, fhi);
00412
00413 #if 0
00414
00415 cpl_msg_debug(__func__, "Gaussian FWHM [%s]:\n"
00416 "\tFWHM all (%d values)\t%.3f +/- %.3f (%.3f) %.3f...%.3f\n"
00417 "\tFWHM blue (%d values)\t%.3f +/- %.3f (%.3f)\n"
00418 "\tFWHM green (%d values)\t%.3f +/- %.3f (%.3f)\n"
00419 "\tFWHM red (%d values)\t%.3f +/- %.3f (%.3f)",
00420 cpl_table_get_column_unit(aFWHM, "fwhm"),
00421 (int)cpl_table_get_nrow(aFWHM), fmean, fstdev, flo, fhi,
00422 cpl_table_get_column_median(aFWHM, "fwhm"),
00423 (int)cpl_table_get_nrow(tblue),
00424 cpl_table_get_column_mean(tblue, "fwhm"),
00425 cpl_table_get_column_stdev(tblue, "fwhm"),
00426 cpl_table_get_column_median(tblue, "fwhm"),
00427 (int)cpl_table_get_nrow(tgreen),
00428 cpl_table_get_column_mean(tgreen, "fwhm"),
00429 cpl_table_get_column_stdev(tgreen, "fwhm"),
00430 cpl_table_get_column_median(tgreen, "fwhm"),
00431 (int)cpl_table_get_nrow(tred),
00432 cpl_table_get_column_mean(tred, "fwhm"),
00433 cpl_table_get_column_stdev(tred, "fwhm"),
00434 cpl_table_get_column_median(tred, "fwhm"));
00435 cpl_msg_debug(__func__, "Gaussian spectral resolution R:\n"
00436 "\tR all (%d values)\t%.1f +/- %.1f (%.1f) %.1f...%.1f\n"
00437 "\tR blue (%d values)\t%.1f +/- %.1f (%.1f)\n"
00438 "\tR green (%d values)\t%.1f +/- %.1f (%.1f)\n"
00439 "\tR red (%d values)\t%.1f +/- %.1f (%.1f)",
00440 (int)cpl_table_get_nrow(aFWHM), rmean,
00441 cpl_table_get_column_stdev(aFWHM, "R"),
00442 cpl_table_get_column_median(aFWHM, "R"),
00443 cpl_table_get_column_min(aFWHM, "R"),
00444 cpl_table_get_column_max(aFWHM, "R"),
00445 (int)cpl_table_get_nrow(tblue),
00446 cpl_table_get_column_mean(tblue, "R"),
00447 cpl_table_get_column_stdev(tblue, "R"),
00448 cpl_table_get_column_median(tblue, "R"),
00449 (int)cpl_table_get_nrow(tgreen),
00450 cpl_table_get_column_mean(tgreen, "R"),
00451 cpl_table_get_column_stdev(tgreen, "R"),
00452 cpl_table_get_column_median(tgreen, "R"),
00453 (int)cpl_table_get_nrow(tred),
00454 cpl_table_get_column_mean(tred, "R"),
00455 cpl_table_get_column_stdev(tred, "R"),
00456 cpl_table_get_column_median(tred, "R"));
00457 cpl_table_delete(tblue);
00458 cpl_table_delete(tgreen);
00459 cpl_table_delete(tred);
00460 #endif
00461 cpl_table_erase_column(aFWHM, "R");
00462 }
00463
00464
00476
00477 static void
00478 muse_wave_calib_qc_lambda(muse_image *aImage, const unsigned short aSlice,
00479 cpl_polynomial **aTrace, cpl_polynomial *aFit)
00480 {
00481 int ny = cpl_image_get_size_y(aImage->data);
00482 unsigned char ifu = muse_utils_get_ifu(aImage->header);
00483
00484
00485 double xbotl = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_LEFT], 1, NULL),
00486 xbotr = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_RIGHT], 1, NULL),
00487 xtopl = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_LEFT], ny, NULL),
00488 xtopr = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_RIGHT], ny, NULL);
00489 cpl_vector *pos = cpl_vector_new(2);
00490 cpl_vector_set(pos, 0, xbotl);
00491 cpl_vector_set(pos, 1, 1);
00492 double wlbotl = cpl_polynomial_eval(aFit, pos);
00493 cpl_vector_set(pos, 0, xbotr);
00494 cpl_vector_set(pos, 1, 1);
00495 double wlbotr = cpl_polynomial_eval(aFit, pos);
00496 cpl_vector_set(pos, 0, xtopl);
00497 cpl_vector_set(pos, 1, ny);
00498 double wltopl = cpl_polynomial_eval(aFit, pos);
00499 cpl_vector_set(pos, 0, xtopr);
00500 cpl_vector_set(pos, 1, ny);
00501 double wltopr = cpl_polynomial_eval(aFit, pos);
00502 #if 0
00503 cpl_msg_debug(__func__, "Wavelengths at slice corners in slice %hu of IFU %hhu: "
00504 "botl(%.2f,1)=%f, botr(%.2f,1)=%f, topl(%.2f,%d)=%f, topr(%.2f,%d)"
00505 "=%f", aSlice, ifu, xbotl, wlbotl, xbotr, wlbotr,
00506 xtopl, ny, wltopl, xtopr, ny, wltopr);
00507 #endif
00508 char *keyword = cpl_sprintf(QC_WAVECAL_SLICEj_DWLEN_BOT, aSlice);
00509 cpl_propertylist_append_float(aImage->header, keyword, wlbotl - wlbotr);
00510 cpl_free(keyword);
00511 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_DWLEN_TOP, aSlice);
00512 cpl_propertylist_append_float(aImage->header, keyword, wltopl - wltopr);
00513 cpl_free(keyword);
00514
00515 #define DWLEN_WARN_LIMIT_N 6.5
00516 #define DWLEN_WARN_LIMIT_E 5.9
00517 double limit = DWLEN_WARN_LIMIT_E;
00518 cpl_errorstate prestate = cpl_errorstate_get();
00519 if (muse_pfits_get_mode(aImage->header) != MUSE_MODE_WFM_NONAO_X) {
00520 limit = DWLEN_WARN_LIMIT_N;
00521 }
00522 cpl_errorstate_set(prestate);
00523 if (fabs(wlbotl - wlbotr) > limit) {
00524 cpl_msg_warning(__func__, "Wavelength differences at bottom of slice %hu "
00525 "of IFU %hhu are large (%f Angstrom)!", aSlice, ifu,
00526 wlbotl - wlbotr);
00527 }
00528 if (fabs(wltopl - wltopr) > DWLEN_WARN_LIMIT_E) {
00529 cpl_msg_warning(__func__, "Wavelength differences at top of slice %hu of IFU "
00530 "%hhu are large (%f Angstrom)!", aSlice, ifu, wltopl - wltopr);
00531 }
00532
00533
00534
00535 const double yc = (1. + ny) / 2.,
00536 xc = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_CENTER], yc, NULL);
00537 cpl_vector_set(pos, 0, xc);
00538 cpl_vector_set(pos, 1, yc);
00539 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_WLPOS, aSlice);
00540 cpl_propertylist_append_float(aImage->header, keyword, yc);
00541 cpl_free(keyword);
00542 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_WLEN, aSlice);
00543 cpl_propertylist_append_float(aImage->header, keyword,
00544 cpl_polynomial_eval(aFit, pos));
00545 cpl_free(keyword);
00546 cpl_vector_delete(pos);
00547 }
00548
00549
00562
00563 static void
00564 muse_wave_calib_output_summary(const cpl_propertylist *aHeader,
00565 const cpl_table *aWave)
00566 {
00567 unsigned char ifu = muse_utils_get_ifu(aHeader);
00568 if (!aWave) {
00569 cpl_msg_warning(__func__, "Wavelength solution missing for IFU %hhu, no "
00570 "summary!", ifu);
00571 return;
00572 }
00573 cpl_vector *found = cpl_vector_new(kMuseSlicesPerCCD),
00574 *ident = cpl_vector_new(kMuseSlicesPerCCD),
00575 *used = cpl_vector_new(kMuseSlicesPerCCD),
00576 *resolution = cpl_vector_new(kMuseSlicesPerCCD);
00577 unsigned short islice;
00578 for (islice = 0; islice < kMuseSlicesPerCCD; islice++) {
00579 char keyword[KEYWORD_LENGTH];
00580 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_NDET,
00581 (unsigned short)(islice + 1));
00582 cpl_vector_set(found, islice, cpl_propertylist_get_int(aHeader, keyword));
00583 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_LINES_NID,
00584 (unsigned short)(islice + 1));
00585 cpl_vector_set(ident, islice, cpl_propertylist_get_int(aHeader, keyword));
00586 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_FIT_NLINES,
00587 (unsigned short)(islice + 1));
00588 cpl_vector_set(used, islice, cpl_propertylist_get_int(aHeader, keyword));
00589 snprintf(keyword, KEYWORD_LENGTH, QC_WAVECAL_SLICEj_RESOL,
00590 (unsigned short)(islice + 1));
00591 cpl_vector_set(resolution, islice, cpl_propertylist_get_float(aHeader, keyword));
00592 }
00593 cpl_msg_info(__func__, "Summary of wavelength solution for IFU %hhu:\n"
00594 "\tDetections per slice: %d ... %d\n"
00595 "\tIdentified lines per slice: %d ... %d\n"
00596 "\tLines per slice used in fit: %d ... %d\n"
00597 "\tRMS of fit [Angstrom]: %5.3f +/- %5.3f (%5.3f ... %5.3f)\n"
00598 "\tMean spectral resolution R: %6.1f +/- %5.1f", ifu,
00599 (int)cpl_vector_get_min(found), (int)cpl_vector_get_max(found),
00600 (int)cpl_vector_get_min(ident), (int)cpl_vector_get_max(ident),
00601 (int)cpl_vector_get_min(used), (int)cpl_vector_get_max(used),
00602 sqrt(cpl_table_get_column_mean(aWave, MUSE_WAVECAL_TABLE_COL_MSE)),
00603 sqrt(cpl_table_get_column_stdev(aWave, MUSE_WAVECAL_TABLE_COL_MSE)),
00604 sqrt(cpl_table_get_column_min(aWave, MUSE_WAVECAL_TABLE_COL_MSE)),
00605 sqrt(cpl_table_get_column_max(aWave, MUSE_WAVECAL_TABLE_COL_MSE)),
00606 cpl_vector_get_mean(resolution), cpl_vector_get_stdev(resolution));
00607 cpl_vector_delete(found);
00608 cpl_vector_delete(ident);
00609 cpl_vector_delete(used);
00610 cpl_vector_delete(resolution);
00611 }
00612
00613
00659
00660 cpl_table *
00661 muse_wave_calib(muse_image *aImage, cpl_table *aTrace, cpl_table *aLinelist,
00662 muse_wave_params *aParams)
00663 {
00664 if (!aImage) {
00665 cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT, "arc image missing!");
00666 return NULL;
00667 }
00668 unsigned char ifu = muse_utils_get_ifu(aImage->header);
00669
00670 double minstat = cpl_image_get_min(aImage->stat);
00671 if (minstat <= 0.) {
00672 cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "arc image %d does"
00673 " not have valid STAT extension in IFU %hhu (minimum "
00674 "is %e)!", 0, ifu, minstat);
00675 return NULL;
00676 }
00677
00678 if (!aTrace) {
00679 cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT, "trace table missing "
00680 "for IFU %hhu, cannot create wavelength calibration!",
00681 ifu);
00682 return NULL;
00683 } else if (cpl_table_get_nrow(aTrace) != kMuseSlicesPerCCD) {
00684 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "trace table "
00685 "not valid for this dataset of IFU %hhu!", ifu);
00686 return NULL;
00687 }
00688
00689 if (!aLinelist) {
00690 cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT, "no arc line list "
00691 "supplied for IFU %hhu!", ifu);
00692 return NULL;
00693 }
00694 if (!aParams) {
00695 cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT, "wavelength "
00696 "calibration parameters missing for IFU %hhu!", ifu);
00697 return NULL;
00698 }
00699 if (aParams->fitweighting > MUSE_WAVE_WEIGHTING_CERRSCATTER) {
00700 cpl_error_set_message(__func__, CPL_ERROR_UNSUPPORTED_MODE,
00701 "unknown weighting scheme for IFU %hhu", ifu);
00702 return NULL;
00703 }
00704
00705
00706 int nx = cpl_image_get_size_x(aImage->data),
00707 ny = cpl_image_get_size_y(aImage->data);
00708 if (ny < 4000) {
00709 cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "this dataset of "
00710 "IFU %hhu is too small (%dx%d pix) to be supported",
00711 ifu, nx, ny);
00712 return NULL;
00713 }
00714
00715 cpl_msg_info(__func__, "Using polynomial orders %hu (x) and %hu (y), %s "
00716 "weighting, assuming initial sampling of %.3f +/- %.3f Angstrom"
00717 "/pix, in IFU %hhu", aParams->xorder, aParams->yorder,
00718 muse_wave_weighting_string[aParams->fitweighting],
00719 kMuseSpectralSamplingA, aParams->ddisp, ifu);
00720
00721 cpl_vector *vreflam = muse_wave_lines_get(aLinelist, 5, 0.);
00722 if (!vreflam) {
00723 cpl_error_set_message(__func__, CPL_ERROR_BAD_FILE_FORMAT, "could not "
00724 "create list of FWHM reference arc wavelengths for "
00725 "IFU %hhu", ifu);
00726 return NULL;
00727 }
00728
00729 cpl_propertylist_erase_regexp(aImage->header, "^ESO QC", 0);
00730 cpl_table *wavecaltable = NULL;
00731
00732 int debug = getenv("MUSE_DEBUG_WAVECAL")
00733 ? atoi(getenv("MUSE_DEBUG_WAVECAL")) : 0;
00734 char fn_debug[100];
00735 FILE *fp_debug = NULL;
00736 if (debug >= 3) {
00737 snprintf(fn_debug, 99, "MUSE_DEBUG_WAVE_LINES-%02hhu.ascii", ifu);
00738 cpl_msg_info(__func__, "Will write all single line fits to \"%s\"", fn_debug);
00739 fp_debug = fopen(fn_debug, "w");
00740 if (fp_debug) {
00741 fprintf(fp_debug, "#slice x y lambda lambdaerr\n");
00742 }
00743 }
00744
00745
00746 unsigned short islice;
00747 for (islice = 0; islice < kMuseSlicesPerCCD; islice++) {
00748 if (debug > 0) {
00749 printf("\n\nSlice %d of IFU %hhu\n", (int)islice + 1, ifu);
00750 fflush(stdout);
00751 }
00752
00753 cpl_polynomial **ptrace = muse_trace_table_get_polys_for_slice(aTrace,
00754 islice + 1);
00755 if (!ptrace) {
00756 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: tracing polynomials "
00757 "missing!", (int)islice + 1, ifu);
00758 continue;
00759 }
00760
00761
00762 int imid = lround(cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_CENTER],
00763 ny/2., NULL));
00764 if (imid < 1 || imid > nx) {
00765 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: faulty trace polynomial"
00766 " detected", (int)islice + 1, ifu);
00767 muse_trace_polys_delete(ptrace);
00768 continue;
00769 }
00770
00771 const int kWidth = 3;
00772 int k, kcol1 = imid - kWidth/2, kcol2 = kcol1 + kWidth;
00773 muse_imagelist *collist = muse_imagelist_new();
00774 for (k = kcol1; k <= kcol2; k++) {
00775 muse_image *column = muse_image_new();
00776 column->data = cpl_image_extract(aImage->data, k, 1, k, ny);
00777 column->dq = cpl_image_extract(aImage->dq, k, 1, k, ny);
00778 column->stat = cpl_image_extract(aImage->stat, k, 1, k, ny);
00779 column->header = cpl_propertylist_new();
00780 muse_imagelist_set(collist, column, k - kcol1);
00781 }
00782 muse_image *column = muse_combine_median_create(collist);
00783 cpl_propertylist_append_string(column->header, "BUNIT",
00784 muse_pfits_get_bunit(aImage->header));
00785 muse_imagelist_delete(collist);
00786
00787
00788 cpl_table *detlines = muse_wave_lines_search(column, aParams->detsigma,
00789 islice + 1, ifu);
00790 if (!detlines) {
00791 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND, "problem when "
00792 "searching for arc lines in slice %d of IFU %hhu, "
00793 "columns %d..%d", (int)islice + 1, ifu, kcol1, kcol2);
00794 muse_image_delete(column);
00795 cpl_vector_delete(vreflam);
00796 muse_trace_polys_delete(ptrace);
00797 cpl_table_delete(wavecaltable);
00798 cpl_table_delete(aParams->residuals);
00799 aParams->residuals = NULL;
00800 return NULL;
00801 }
00802
00803 muse_image_delete(column);
00804 if (debug >= 2) {
00805 printf("Detected arc lines in slice %d of IFU %hhu:\n", (int)islice + 1, ifu);
00806 cpl_table_dump(detlines, 0, cpl_table_get_nrow(detlines), stdout);
00807 fflush(stdout);
00808 }
00809
00810
00811
00812 int nfound = cpl_table_get_nrow(detlines);
00813
00814 double minflux = cpl_table_get_column_min(detlines, "flux") * 1.15;
00815 cpl_vector *vlambda = muse_wave_lines_get(aLinelist, 1, minflux);
00816 muse_wave_lines_identify(detlines, vlambda, aParams);
00817 cpl_vector_delete(vlambda);
00818 int nlines = cpl_table_get_nrow(detlines);
00819 cpl_msg_debug(__func__, "Identified %d of %d arc lines in slice %d of IFU "
00820 "%hhu (column %d, %d..%d)", nlines, nfound, (int)islice + 1, ifu,
00821 imid, kcol1, kcol2);
00822 if (debug >= 2) {
00823 printf("Identified arc lines with wavelengths in slice %d of IFU %hhu:\n",
00824 (int)islice + 1, ifu);
00825 cpl_table_dump(detlines, 0, cpl_table_get_nrow(detlines), stdout);
00826 fflush(stdout);
00827 }
00828
00829
00830 char *keyword = cpl_sprintf(QC_WAVECAL_SLICEj_LINES_NDET,
00831 (unsigned short)(islice + 1));
00832 cpl_propertylist_append_int(aImage->header, keyword, nfound);
00833 cpl_free(keyword);
00834
00835 if (nlines < aParams->yorder + 1) {
00836
00837 cpl_msg_error(__func__, "Could not identify enough arc lines in slice %d"
00838 " of IFU %hhu (%d of %d, required %d)", (int)islice + 1, ifu,
00839 nlines, nfound, (int)aParams->yorder + 1);
00840 muse_trace_polys_delete(ptrace);
00841 cpl_table_delete(detlines);
00842 continue;
00843 }
00844
00845
00846 muse_wave_calib_qc_peaks(detlines, aImage->header, islice + 1);
00847 muse_wave_calib_qc_fwhm_old(detlines, aImage->header, vreflam, islice + 1);
00848
00849
00850 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_LINES_NID, (unsigned short)(islice + 1));
00851 cpl_propertylist_append_int(aImage->header, keyword, nlines);
00852 cpl_free(keyword);
00853
00854
00855
00856 cpl_matrix *xypos = cpl_matrix_new(2, 1);
00857 cpl_vector *lambdas = cpl_vector_new(1),
00858 *dlambdas = NULL;
00859 if (aParams->fitweighting != MUSE_WAVE_WEIGHTING_UNIFORM) {
00860 dlambdas = cpl_vector_new(1);
00861 }
00862 int ientry = 0;
00863
00864
00865 int j;
00866 for (j = 0; j < nlines; j++) {
00867
00868 double lambda = cpl_table_get(detlines, "lambda", j, NULL);
00869 double ypos = cpl_table_get(detlines, "center", j, NULL);
00870
00871
00872 double sigma = cpl_table_get(detlines, "sigma", j, NULL);
00873 int n = 0,
00874 halfwidth = 2.*cpl_table_get(detlines, "fwhm", j, NULL);
00875
00876 double dleft = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT],
00877 ypos, NULL),
00878 dright = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT],
00879 ypos, NULL),
00880 dmid = (dleft + dright) / 2.;
00881 int ileft = ceil(dleft),
00882 iright = floor(dright);
00883 #if 0
00884 cpl_msg_debug(__func__, "limits at y=%f: %f < %f < %f", ypos, dleft, dmid,
00885 dright);
00886 #endif
00887
00888
00889 cpl_table *fittable = muse_cpltable_new(muse_wavelines_def,
00890 (int)kMuseSliceHiLikelyWidth + 5);
00891
00892
00893
00894 int i;
00895 for (i = dmid; i >= ileft; i--) {
00896 cpl_error_code rc = muse_wave_line_fit_single(aImage, i, ypos, halfwidth,
00897 sigma, fittable, ++n);
00898 if (rc != CPL_ERROR_NONE) {
00899 --n;
00900 }
00901 }
00902 #if 0
00903 printf("arc line j=%d, columns i=%d...", j + 1, i);
00904 #endif
00905 for (i = dmid + 1; i <= iright; i++) {
00906 cpl_error_code rc = muse_wave_line_fit_single(aImage, i, ypos, halfwidth,
00907 sigma, fittable, ++n);
00908 if (rc != CPL_ERROR_NONE) {
00909 --n;
00910 }
00911 }
00912
00913
00914
00915 cpl_table_select_all(fittable);
00916 cpl_table_and_selected_invalid(fittable, "center");
00917 cpl_table_erase_selected(fittable);
00918 #if 0
00919 printf("line %d, %d line fits\n", j + 1, i);
00920 cpl_table_dump(fittable, 0, n, stdout);
00921 fflush(stdout);
00922 #endif
00923 cpl_errorstate state = cpl_errorstate_get();
00924 muse_wave_line_fit_iterate(fittable, -1, aParams);
00925 int npos = cpl_table_get_nrow(fittable);
00926 if (npos <= aParams->xorder) {
00927 cpl_msg_debug(__func__, "Polynomial fit failed in slice %d of IFU %hhu"
00928 " for line at %.3fA (y-position near %.2f pix): %s",
00929 (int)islice + 1, ifu, lambda, ypos, cpl_error_get_message());
00930 cpl_errorstate_set(state);
00931 }
00932 #if 0
00933 else {
00934 printf("%s: line %2d, %d line fits, %d with low residuals:\n", __func__,
00935 j + 1, i, npos);
00936 cpl_table_dump(fittable, 0, npos, stdout);
00937 fflush(stdout);
00938 }
00939 #endif
00940
00941
00942 cpl_matrix_resize(xypos, 0, 0,
00943 0, ientry + npos - cpl_matrix_get_ncol(xypos));
00944 cpl_vector_set_size(lambdas, ientry + npos);
00945 if (aParams->fitweighting != MUSE_WAVE_WEIGHTING_UNIFORM) {
00946 cpl_vector_set_size(dlambdas, ientry + npos);
00947 }
00948
00949
00950 int ipos;
00951 for (ipos = 0; ipos < npos; ipos++) {
00952
00953 cpl_matrix_set(xypos, 0, ientry, cpl_table_get(fittable, "x", ipos, NULL));
00954
00955 cpl_matrix_set(xypos, 1, ientry, cpl_table_get(fittable, "center", ipos, NULL));
00956
00957 cpl_vector_set(lambdas, ientry, lambda);
00958
00959
00960
00961 if (aParams->fitweighting != MUSE_WAVE_WEIGHTING_UNIFORM) {
00962 cpl_vector_set(dlambdas, ientry, cpl_table_get(fittable, "cerr", ipos, NULL)
00963 * kMuseSpectralSamplingA);
00964 }
00965
00966 ientry++;
00967 if (fp_debug) {
00968 fprintf(fp_debug, " %02d %04d %9.4f %10.4f %e\n", (int)islice + 1,
00969 (int)cpl_table_get(fittable, "x", ipos, NULL),
00970 cpl_table_get(fittable, "center", ipos, NULL), lambda,
00971 cpl_table_get(fittable, "cerr", ipos, NULL) * kMuseSpectralSamplingA);
00972 }
00973 }
00974 cpl_table_delete(fittable);
00975 }
00976 cpl_table_delete(detlines);
00977
00978
00979 cpl_polynomial *poly = NULL;
00980 double mse = 0;
00981 cpl_error_code rc = muse_wave_poly_fit(xypos, lambdas, dlambdas,
00982 &poly, &mse, aParams, islice + 1);
00983 cpl_matrix_delete(xypos);
00984 int nfinal = muse_cplvector_count_unique(lambdas);
00985 cpl_vector_delete(lambdas);
00986 cpl_vector_delete(dlambdas);
00987
00988
00989 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_FIT_NLINES, (unsigned short)(islice + 1));
00990 cpl_propertylist_append_int(aImage->header, keyword, nfinal);
00991 cpl_free(keyword);
00992
00993 muse_wave_calib_qc_lambda(aImage, islice + 1, ptrace, poly);
00994
00995 muse_trace_polys_delete(ptrace);
00996
00997 if (rc != CPL_ERROR_NONE) {
00998 cpl_msg_warning(__func__, "Something went wrong while fitting in slice "
00999 "%d of IFU %hhu: %s", (int)islice + 1, ifu,
01000 cpl_error_get_message_default(rc));
01001 cpl_polynomial_delete(poly);
01002 continue;
01003 }
01004
01005 if (!wavecaltable) {
01006
01007
01008 wavecaltable = muse_wave_table_create(kMuseSlicesPerCCD,
01009 aParams->xorder, aParams->yorder);
01010 }
01011 rc = muse_wave_table_add_poly(wavecaltable, poly, mse,
01012 aParams->xorder, aParams->yorder, islice);
01013
01014
01015
01016 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_FIT_RMS, (unsigned short)(islice + 1));
01017 cpl_propertylist_append_float(aImage->header, keyword, sqrt(mse));
01018 cpl_free(keyword);
01019
01020 if (rc != CPL_ERROR_NONE) {
01021 cpl_msg_warning(__func__, "Could not write polynomial to wavecal table "
01022 "for slice %d of IFU %hhu: %s", (int)islice + 1, ifu,
01023 cpl_error_get_message_default(rc));
01024 }
01025
01026 cpl_polynomial_delete(poly);
01027 }
01028 cpl_vector_delete(vreflam);
01029 if (fp_debug) {
01030 cpl_msg_info(__func__, "Done writing line fits to \"%s\"", fn_debug);
01031 fclose(fp_debug);
01032 }
01033
01034 muse_wave_calib_output_summary(aImage->header, wavecaltable);
01035 #if 0
01036 if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
01037 cpl_table_dump(wavecaltable, 0, 3, stdout); fflush(stdout);
01038 }
01039 #endif
01040
01041 return wavecaltable;
01042 }
01043
01044
01108
01109 cpl_table *
01110 muse_wave_calib_lampwise(muse_imagelist *aImages, cpl_table *aTrace,
01111 cpl_table *aLinelist, muse_wave_params *aParams)
01112 {
01113 if (!aImages || muse_imagelist_get_size(aImages) < 1) {
01114 cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT, "arc imagelist is "
01115 "missing or empty!");
01116 return NULL;
01117 }
01118 unsigned char ifu = muse_utils_get_ifu(muse_imagelist_get(aImages, 0)->header);
01119 if (!aTrace) {
01120 cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT, "trace table missing "
01121 "for IFU %hhu, cannot create wavelength calibration!",
01122 ifu);
01123 return NULL;
01124 } else if (cpl_table_get_nrow(aTrace) != kMuseSlicesPerCCD) {
01125 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "trace table "
01126 "not valid for this dataset of IFU %hhu!", ifu);
01127 return NULL;
01128 }
01129 if (!aLinelist) {
01130 cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT, "no arc line list "
01131 "supplied for IFU %hhu!", ifu);
01132 return NULL;
01133 }
01134 if (!aParams) {
01135 cpl_error_set_message(__func__, CPL_ERROR_NULL_INPUT, "wavelength "
01136 "calibration parameters missing for IFU %hhu!", ifu);
01137 return NULL;
01138 }
01139 if (aParams->fitweighting > MUSE_WAVE_WEIGHTING_CERRSCATTER) {
01140 cpl_error_set_message(__func__, CPL_ERROR_UNSUPPORTED_MODE,
01141 "unknown weighting scheme for IFU %hhu", ifu);
01142 return NULL;
01143 }
01144
01145 muse_image *firstimage = muse_imagelist_get(aImages, 0);
01146 int nx = cpl_image_get_size_x(firstimage->data),
01147 ny = cpl_image_get_size_y(firstimage->data);
01148
01149 if (ny < 4000) {
01150 cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "this dataset of "
01151 "IFU %hhu is too small (%dx%d pix) to be supported",
01152 ifu, nx, ny);
01153 return NULL;
01154 }
01155
01156 cpl_msg_info(__func__, "Using polynomial orders %hu (x) and %hu (y), %s "
01157 "weighting, assuming initial sampling of %.3f +/- %.3f Angstrom"
01158 "/pix, in IFU %hhu", aParams->xorder, aParams->yorder,
01159 muse_wave_weighting_string[aParams->fitweighting],
01160 kMuseSpectralSamplingA, aParams->ddisp, ifu);
01161
01162 cpl_propertylist_erase_regexp(firstimage->header, "^ESO QC", 0);
01163 char *lamp = muse_utils_header_get_lamp_names(firstimage->header, ',');
01164 cpl_msg_debug(__func__, "Image 1 was taken with lamp %s", lamp);
01165 cpl_free(lamp);
01166 unsigned int k;
01167 for (k = 1; k < muse_imagelist_get_size(aImages); k++) {
01168 int nxk = cpl_image_get_size_x(muse_imagelist_get(aImages, k)->data),
01169 nyk = cpl_image_get_size_y(muse_imagelist_get(aImages, k)->data);
01170 if (nxk != nx || nyk != ny) {
01171 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "arc "
01172 "imagelist is not uniform (image %u is %dx%d, "
01173 "first image is %dx%d)", k + 1, nxk, nyk, nx, ny);
01174 return NULL;
01175 }
01176 cpl_propertylist_erase_regexp(muse_imagelist_get(aImages, k)->header,
01177 "^ESO QC", 0);
01178 lamp = muse_utils_header_get_lamp_names(muse_imagelist_get(aImages, k)->header, ',');
01179 cpl_msg_debug(__func__, "Image %d was taken with lamp %s", k + 1, lamp);
01180 cpl_free(lamp);
01181 }
01182
01183 cpl_vector *vreflam = muse_wave_lines_get(aLinelist, 5, 0.);
01184 if (!vreflam) {
01185 cpl_error_set_message(__func__, CPL_ERROR_BAD_FILE_FORMAT, "could not "
01186 "create list of FWHM reference arc wavelengths for "
01187 "IFU %hhu", ifu);
01188 return NULL;
01189 }
01190 cpl_table *wavecal = NULL;
01191 int debug = getenv("MUSE_DEBUG_WAVECAL")
01192 ? atoi(getenv("MUSE_DEBUG_WAVECAL")) : 0;
01193 char fn_debug[100];
01194 FILE *fp_debug = NULL;
01195 if (debug >= 3) {
01196 snprintf(fn_debug, 99, "MUSE_DEBUG_WAVE_LINES-%02hhu.ascii", ifu);
01197 cpl_msg_info(__func__, "Will write all single line fits to \"%s\"", fn_debug);
01198 fp_debug = fopen(fn_debug, "w");
01199 if (fp_debug) {
01200 fprintf(fp_debug, "#slice x y lambda lambdaerr\n");
01201 }
01202 }
01203
01204
01205 unsigned short islice;
01206 for (islice = 0; islice < kMuseSlicesPerCCD; islice++) {
01207 if (debug > 0) {
01208 printf("\n\nSlice %d of IFU %hhu\n", (int)islice + 1, ifu);
01209 fflush(stdout);
01210 }
01211
01212 cpl_polynomial **ptrace = muse_trace_table_get_polys_for_slice(aTrace,
01213 islice + 1);
01214 if (!ptrace) {
01215 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: tracing polynomials "
01216 "missing!", (int)islice + 1, ifu);
01217 continue;
01218 }
01219
01220 const int imid = lround(cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_CENTER],
01221 ny/2., NULL)),
01222 iWidth = 3,
01223 icol1 = imid - iWidth/2,
01224 icol2 = icol1 + iWidth;
01225 if (imid < 1 || imid > nx) {
01226 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: faulty trace polynomial"
01227 "detected", (int)islice + 1, ifu);
01228 muse_trace_polys_delete(ptrace);
01229 continue;
01230 }
01231 cpl_table *detlines = NULL;
01232 int ndet = 0;
01233 for (k = 0; k < muse_imagelist_get_size(aImages); k++) {
01234 muse_image *arc = muse_imagelist_get(aImages, k);
01235
01236 muse_imagelist *collist = muse_imagelist_new();
01237 int i;
01238 for (i = icol1; i <= icol2; i++) {
01239 muse_image *column = muse_image_new();
01240 column->data = cpl_image_extract(arc->data, i, 1, i, ny);
01241 column->dq = cpl_image_extract(arc->dq, i, 1, i, ny);
01242 column->stat = cpl_image_extract(arc->stat, i, 1, i, ny);
01243 muse_imagelist_set(collist, column, i - icol1);
01244 }
01245 muse_image *column = muse_combine_median_create(collist);
01246 cpl_propertylist_append_string(column->header, "BUNIT",
01247 muse_pfits_get_bunit(arc->header));
01248 muse_imagelist_delete(collist);
01249
01250
01251 cpl_table *detections = muse_wave_lines_search(column, aParams->detsigma,
01252 islice + 1, ifu);
01253 muse_image_delete(column);
01254 int nlampdet = cpl_table_get_nrow(detections);
01255 ndet += nlampdet;
01256 char *lampname = muse_utils_header_get_lamp_names(arc->header, ',');
01257 cpl_table_fill_column_window_string(detections, "lampname", 0, nlampdet,
01258 lampname);
01259 cpl_array *lampnumbers = muse_utils_header_get_lamp_numbers(arc->header);
01260 cpl_table_fill_column_window_int(detections, "lampno", 0, nlampdet,
01261 cpl_array_get_int(lampnumbers, 0, NULL));
01262 cpl_array_delete(lampnumbers);
01263
01264 double minflux = cpl_table_get_column_min(detections, "flux") * 1.15;
01265 cpl_vector *ionlambdas = muse_wave_lines_get_for_lamp(aLinelist, lampname,
01266 1, minflux);
01267 cpl_free(lampname);
01268 muse_wave_lines_identify(detections, ionlambdas, aParams);
01269 cpl_vector_delete(ionlambdas);
01270
01271 if (!detlines) {
01272 detlines = detections;
01273 } else {
01274 cpl_table_insert(detlines, detections, cpl_table_get_nrow(detlines));
01275 cpl_table_delete(detections);
01276 }
01277 }
01278 cpl_propertylist *order = cpl_propertylist_new();
01279 cpl_propertylist_append_bool(order, "y", CPL_FALSE);
01280 cpl_table_sort(detlines, order);
01281 cpl_propertylist_delete(order);
01282 int nlines = cpl_table_get_nrow(detlines);
01283 if (debug >= 2) {
01284 printf("Detected and identified %d arc lines in slice %d of IFU %hhu:\n",
01285 nlines, (int)islice + 1, ifu);
01286 cpl_table_dump(detlines, 0, cpl_table_get_nrow(detlines), stdout);
01287 fflush(stdout);
01288 }
01289
01290 char *keyword = cpl_sprintf(QC_WAVECAL_SLICEj_LINES_NDET, (unsigned short)(islice + 1));
01291 cpl_propertylist_append_int(firstimage->header, keyword, ndet);
01292 cpl_free(keyword);
01293
01294 if (nlines < aParams->yorder + 1) {
01295
01296 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND, "could not "
01297 "detect and/or identify enough arc lines in slice "
01298 "%d of IFU %hhu (%d of %d, required %d)",
01299 (int)islice + 1, ifu, nlines, ndet, (int)aParams->yorder + 1);
01300 muse_trace_polys_delete(ptrace);
01301 cpl_table_delete(detlines);
01302 continue;
01303 }
01304 cpl_msg_info(__func__, "Identified %d of %d detected arc lines in slice %d"
01305 " of IFU %hhu (column %d, %d..%d)", nlines, ndet, (int)islice + 1,
01306 ifu, imid, icol1, icol2);
01307
01308
01309 muse_wave_calib_qc_peaks(detlines, firstimage->header, islice + 1);
01310
01311
01312 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_LINES_NID, (unsigned short)(islice + 1));
01313 cpl_propertylist_append_int(firstimage->header, keyword, nlines);
01314 cpl_free(keyword);
01315
01316
01317
01318 cpl_vector *cen = cpl_vector_new(nlines);
01319 cpl_matrix *lbda = cpl_matrix_new(1, nlines);
01320 int idx;
01321 for (idx = 0; idx < nlines; idx++) {
01322 cpl_vector_set(cen, idx, cpl_table_get(detlines, "center", idx, NULL));
01323 cpl_matrix_set(lbda, 0, idx, cpl_table_get(detlines, "lambda", idx, NULL));
01324 }
01325 #if 0
01326 char *fn = cpl_sprintf("slice%02d_lbda1.dat", (int)islice + 1);
01327 FILE *file = fopen(fn, "w");
01328 cpl_matrix_dump(lbda, file);
01329 fclose(file);
01330 cpl_free(fn);
01331 #endif
01332 double mse1d, chisq1d;
01333 cpl_polynomial *fit
01334 = muse_utils_iterate_fit_polynomial(lbda, cen, NULL, detlines,
01335 aParams->yorder, 3.,
01336 &mse1d, &chisq1d);
01337 #if 0
01338 cpl_vector *res = cpl_vector_new(cpl_vector_get_size(cen));
01339 cpl_vector_fill_polynomial_fit_residual(res, cen, NULL, fit, lbda, NULL);
01340 cpl_bivector *biv = cpl_bivector_wrap_vectors(cen, res);
01341 cpl_plot_bivector(NULL, NULL, NULL, biv);
01342 cpl_bivector_unwrap_vectors(biv);
01343 cpl_vector_delete(res);
01344 #endif
01345 if (debug >= 1) {
01346 printf("Initial (inverse) polynomial fit in slice %2d of IFU %hhu "
01347 "(RMS = %f, chisq = %e, %d of %d input points left)\n", (int)islice + 1,
01348 ifu, sqrt(mse1d), chisq1d, (int)cpl_vector_get_size(cen), nlines);
01349 cpl_polynomial_dump(fit, stdout);
01350 fflush(stdout);
01351 }
01352 nlines = cpl_vector_get_size(cen);
01353 #if 0
01354 fn = cpl_sprintf("slice%02hu_lbda2.dat", islice + 1);
01355 file = fopen(fn, "w");
01356 cpl_matrix_dump(lbda, file);
01357 fclose(file);
01358 cpl_free(fn);
01359 #endif
01360 cpl_vector_delete(cen);
01361 cpl_matrix_delete(lbda);
01362
01363
01364
01365 cpl_matrix *xypos = cpl_matrix_new(2, 1);
01366 cpl_vector *lambdas = cpl_vector_new(1),
01367 *dlambdas = NULL;
01368 if (aParams->fitweighting != MUSE_WAVE_WEIGHTING_UNIFORM) {
01369 dlambdas = cpl_vector_new(1);
01370 }
01371 int ientry = 0;
01372
01373
01374 cpl_table *fwhmtable = muse_cpltable_new(muse_wavelines_def, 0);
01375 cpl_table_set_column_unit(fwhmtable, "fwhm", "Angstrom");
01376 int j, narclines = cpl_table_get_nrow(aLinelist);
01377 for (j = 0; j < narclines; j++) {
01378 int quality = cpl_table_get_int(aLinelist, MUSE_LINE_CATALOG_QUALITY, j, NULL);
01379 if (quality <= 1) {
01380 continue;
01381 }
01382
01383
01384 const char *lampname = muse_wave_lines_get_lampname(aLinelist, j);
01385 muse_image *arc = NULL;
01386 for (k = 0; k < muse_imagelist_get_size(aImages); k++) {
01387 arc = muse_imagelist_get(aImages, k);
01388 char *thislamp = muse_utils_header_get_lamp_names(arc->header, ',');
01389 cpl_boolean match = CPL_FALSE;
01390 if (lampname && thislamp) {
01391 match = strncmp(lampname, thislamp, strlen(lampname)) == 0;
01392 }
01393 cpl_free(thislamp);
01394 if (match) {
01395 break;
01396 }
01397 }
01398
01399 cpl_table *fittable = NULL;
01400 if (quality == 2) {
01401 fittable = muse_wave_line_handle_multiplet(arc, aLinelist, j, fit, ptrace,
01402 aParams, islice + 1, debug);
01403 } else {
01404 fittable = muse_wave_line_handle_singlet(arc, aLinelist, j, fit, ptrace,
01405 aParams, islice + 1, debug);
01406 }
01407 if (!fittable) {
01408 continue;
01409 }
01410 int nnew = cpl_table_get_nrow(fittable);
01411
01412
01413 cpl_matrix_resize(xypos, 0, 0,
01414 0, ientry + nnew - cpl_matrix_get_ncol(xypos));
01415 cpl_vector_set_size(lambdas, ientry + nnew);
01416 if (aParams->fitweighting != MUSE_WAVE_WEIGHTING_UNIFORM) {
01417 cpl_vector_set_size(dlambdas, ientry + nnew);
01418 }
01419
01420
01421 cpl_table_set_column_unit(fittable, "fwhm", "");
01422 int ipos;
01423 for (ipos = 0; ipos < nnew; ipos++) {
01424
01425 cpl_matrix_set(xypos, 0, ientry, cpl_table_get(fittable, "x", ipos, NULL));
01426
01427 cpl_matrix_set(xypos, 1, ientry, cpl_table_get(fittable, "center", ipos, NULL));
01428
01429 double lambda = cpl_table_get(fittable, "lambda", ipos, NULL);
01430 cpl_vector_set(lambdas, ientry, lambda);
01431
01432
01433 double dlbda;
01434 cpl_polynomial_eval_1d(fit, lambda, &dlbda);
01435 double cerr = 0.;
01436 if (aParams->fitweighting != MUSE_WAVE_WEIGHTING_UNIFORM) {
01437 cerr = cpl_table_get(fittable, "cerr", ipos, NULL) / dlbda;
01438 cpl_vector_set(dlambdas, ientry, cerr);
01439 }
01440
01441 int err;
01442 double fwhm = cpl_table_get(fittable, "fwhm", ipos, &err) / dlbda;
01443 if (!err) {
01444 cpl_table_set(fittable, "fwhm", ipos, fwhm);
01445 }
01446 ientry++;
01447 if (fp_debug) {
01448 fprintf(fp_debug, " %02d %04d %9.4f %10.4f %e\n", (int)islice + 1,
01449 (int)cpl_table_get(fittable, "x", ipos, NULL),
01450 cpl_table_get(fittable, "center", ipos, NULL), lambda, cerr);
01451 }
01452 }
01453 cpl_table_set_column_unit(fittable, "fwhm", "Angstrom");
01454
01455 cpl_table_select_all(fittable);
01456 cpl_table_and_selected_invalid(fittable, "fwhm");
01457 cpl_table_erase_selected(fittable);
01458 cpl_table_insert(fwhmtable, fittable, cpl_table_get_nrow(fwhmtable));
01459 cpl_table_delete(fittable);
01460 }
01461
01462 cpl_table_abs_column(aLinelist, MUSE_LINE_CATALOG_QUALITY);
01463 cpl_table_delete(detlines);
01464 cpl_polynomial_delete(fit);
01465 muse_wave_calib_qc_fwhm(fwhmtable, firstimage->header, islice + 1);
01466 cpl_table_delete(fwhmtable);
01467
01468
01469 cpl_polynomial *poly = NULL;
01470 double mse2d;
01471 cpl_error_code rc = muse_wave_poly_fit(xypos, lambdas, dlambdas,
01472 &poly, &mse2d, aParams, islice + 1);
01473 cpl_msg_info(__func__, "Polynomial fit of %"CPL_SIZE_FORMAT" of %d positions"
01474 " in slice %d of IFU %hhu gave an RMS of %f Angstrom",
01475 cpl_vector_get_size(lambdas), ientry, (int)islice + 1, ifu,
01476 sqrt(mse2d));
01477 cpl_matrix_delete(xypos);
01478 int nfinal = muse_cplvector_count_unique(lambdas);
01479 cpl_vector_delete(lambdas);
01480 cpl_vector_delete(dlambdas);
01481
01482
01483 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_FIT_NLINES, (unsigned short)(islice + 1));
01484 cpl_propertylist_append_int(firstimage->header, keyword, nfinal);
01485 cpl_free(keyword);
01486
01487 muse_wave_calib_qc_lambda(firstimage, islice + 1, ptrace, poly);
01488
01489 muse_trace_polys_delete(ptrace);
01490
01491 if (rc != CPL_ERROR_NONE) {
01492 cpl_error_set_message(__func__, rc, "a failure while computing the two-di"
01493 "mensional polynomial fit in slice %d of IFU %hhu",
01494 (int)islice + 1, ifu);
01495 cpl_polynomial_delete(poly);
01496 continue;
01497 }
01498
01499 if (!wavecal) {
01500
01501
01502 wavecal = muse_wave_table_create(kMuseSlicesPerCCD,
01503 aParams->xorder, aParams->yorder);
01504 }
01505 rc = muse_wave_table_add_poly(wavecal, poly, mse2d,
01506 aParams->xorder, aParams->yorder, islice);
01507
01508
01509
01510 keyword = cpl_sprintf(QC_WAVECAL_SLICEj_FIT_RMS, (unsigned short)(islice + 1));
01511 cpl_propertylist_append_float(firstimage->header, keyword, sqrt(mse2d));
01512 cpl_free(keyword);
01513
01514 if (rc != CPL_ERROR_NONE) {
01515 cpl_msg_warning(__func__, "Could not write polynomial to wavecal table "
01516 "for slice %d of IFU %hhu: %s", (int)islice + 1, ifu,
01517 cpl_error_get_message_default(rc));
01518 }
01519
01520 cpl_polynomial_delete(poly);
01521 }
01522 cpl_vector_delete(vreflam);
01523 if (fp_debug) {
01524 cpl_msg_info(__func__, "Done writing line fits to \"%s\"", fn_debug);
01525 fclose(fp_debug);
01526 }
01527
01528 muse_wave_calib_output_summary(firstimage->header, wavecal);
01529
01530 for (k = 1; k < muse_imagelist_get_size(aImages); k++) {
01531 cpl_propertylist *header = muse_imagelist_get(aImages, k)->header;
01532 cpl_propertylist_erase_regexp(header, "^ESO QC", 0);
01533 cpl_propertylist_copy_property_regexp(header, firstimage->header, "^ESO QC", 0);
01534 }
01535
01536 return wavecal;
01537 }
01538
01539
01557
01558 cpl_boolean
01559 muse_wave_lines_check(cpl_table *aTable, cpl_propertylist *aHeader)
01560 {
01561 cpl_ensure(aTable && aHeader, CPL_ERROR_NULL_INPUT, CPL_FALSE);
01562
01563 int nrow = cpl_table_get_nrow(aTable);
01564 cpl_ensure(nrow > 0, CPL_ERROR_NULL_INPUT, CPL_FALSE);
01565 cpl_ensure(muse_cpltable_check(aTable, muse_line_catalog_def) == CPL_ERROR_NONE,
01566 CPL_ERROR_DATA_NOT_FOUND, CPL_FALSE);
01567
01568
01569 if (!cpl_propertylist_has(aHeader, MUSE_HDR_LINE_CATALOG_VERSION)) {
01570 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "%s does not "
01571 "contain a VERSION header entry!", MUSE_TAG_LINE_CATALOG);
01572 return CPL_FALSE;
01573 }
01574 int version = cpl_propertylist_get_int(aHeader, MUSE_HDR_LINE_CATALOG_VERSION);
01575 #define LINE_CATALOG_EXPECTED_VERSION 3
01576 if (version != LINE_CATALOG_EXPECTED_VERSION) {
01577 cpl_error_set_message(__func__, CPL_ERROR_BAD_FILE_FORMAT, "VERSION = %d "
01578 "is wrong, we need a %s with VERSION = %d", version,
01579 MUSE_TAG_LINE_CATALOG, LINE_CATALOG_EXPECTED_VERSION);
01580 return CPL_FALSE;
01581 }
01582 return CPL_TRUE;
01583 }
01584
01585
01607
01608 cpl_vector *
01609 muse_wave_lines_get(cpl_table *aTable, int aGoodnessLimit, double aFluxLimit)
01610 {
01611 cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, NULL);
01612 int nrow = cpl_table_get_nrow(aTable);
01613 cpl_ensure(nrow > 0, CPL_ERROR_NULL_INPUT, NULL);
01614
01615 cpl_ensure(cpl_table_has_column(aTable, MUSE_LINE_CATALOG_LAMBDA) == 1
01616 && cpl_table_has_column(aTable, MUSE_LINE_CATALOG_QUALITY) == 1,
01617 CPL_ERROR_DATA_NOT_FOUND, NULL);
01618
01619
01620 cpl_vector *lambdas = cpl_vector_new(nrow);
01621 int i, pos = 0;
01622 for (i = 0; i < nrow; i++) {
01623 double lambda = cpl_table_get(aTable, MUSE_LINE_CATALOG_LAMBDA, i, NULL),
01624 flux = cpl_table_get(aTable, MUSE_LINE_CATALOG_FLUX, i, NULL);
01625
01626
01627 if (i > 0 && lambda < cpl_table_get(aTable, MUSE_LINE_CATALOG_LAMBDA, i-1,
01628 NULL)) {
01629 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "%s is not "
01630 "sorted by increasing lambda (at row %d)!",
01631 MUSE_TAG_LINE_CATALOG, i+1);
01632 cpl_vector_delete(lambdas);
01633 return NULL;
01634 }
01635
01636 if (cpl_table_get(aTable, MUSE_LINE_CATALOG_QUALITY, i, NULL)
01637 < aGoodnessLimit || flux < aFluxLimit) {
01638
01639 continue;
01640 }
01641 cpl_vector_set(lambdas, pos, lambda);
01642 pos++;
01643 }
01644 if (!pos) {
01645 cpl_vector_delete(lambdas);
01646 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND, "No lines with "
01647 "%s >= %d found", MUSE_LINE_CATALOG_QUALITY,
01648 aGoodnessLimit);
01649 return NULL;
01650 }
01651
01652 cpl_vector_set_size(lambdas, pos);
01653
01654
01655 for (i = 0; i < cpl_vector_get_size(lambdas) - 1; i++) {
01656 double l1 = cpl_vector_get(lambdas, i),
01657 l2 = cpl_vector_get(lambdas, i + 1);
01658 if ((l2 - l1) < 1.5) {
01659 cpl_msg_debug(__func__, "Excluding lines at %.3f and %.3f (d = %.3f) "
01660 "Angstrom", l1, l2, l2 - l1);
01661 muse_cplvector_erase_element(lambdas, i + 1);
01662 muse_cplvector_erase_element(lambdas, i);
01663 i--;
01664 }
01665 }
01666 #if 0
01667 printf("%d arc lines in input list are usable:\n", pos);
01668 cpl_vector_dump(lambdas, stdout);
01669 #endif
01670 cpl_msg_debug(__func__, "Using a list of %d %s arc lines (from %6.1f to %6.1f "
01671 "Angstrom)", pos,
01672 aGoodnessLimit == 1 ? "good"
01673 : (aGoodnessLimit == 5 ? "FWHM reference"
01674 : "all"),
01675 cpl_vector_get(lambdas, 0),
01676 cpl_vector_get(lambdas, cpl_vector_get_size(lambdas) - 1));
01677
01678 return lambdas;
01679 }
01680
01681
01698
01699 cpl_vector *
01700 muse_wave_lines_get_for_lamp(cpl_table *aTable, const char *aLamp,
01701 int aGoodnessLimit, double aFluxLimit)
01702 {
01703 cpl_ensure(aTable && aLamp, CPL_ERROR_NULL_INPUT, NULL);
01704 int nrow = cpl_table_get_nrow(aTable);
01705 cpl_ensure(nrow > 0, CPL_ERROR_NULL_INPUT, NULL);
01706
01707 cpl_table_unselect_all(aTable);
01708 int i;
01709 for (i = 0; i < nrow; i++) {
01710 const char *lampname = muse_wave_lines_get_lampname(aTable, i);
01711 if (!strcmp(lampname, aLamp)) {
01712 cpl_table_select_row(aTable, i);
01713 }
01714 }
01715 cpl_table *lamplines = cpl_table_extract_selected(aTable);
01716 #if 0
01717 printf("lamplines for %s\n", aLamp);
01718 cpl_table_dump(lamplines, 0, 10000, stdout);
01719 fflush(stdout);
01720 #endif
01721 cpl_vector *lamplambdas = muse_wave_lines_get(lamplines, aGoodnessLimit,
01722 aFluxLimit);
01723 cpl_table_delete(lamplines);
01724 return lamplambdas;
01725 }
01726
01727
01741
01742 const char *
01743 muse_wave_lines_get_lampname(cpl_table *aTable, const int aIdx)
01744 {
01745 cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, "Unknown_Lamp");
01746 const char *ion = cpl_table_get_string(aTable, "ion", aIdx);
01747 cpl_ensure(ion, CPL_ERROR_ILLEGAL_INPUT, "Unknown_Lamp");
01748
01749
01750 if (!strncmp(ion, "Hg", 2) || !strncmp(ion, "Cd", 2)) {
01751 return "HgCd";
01752 }
01753
01754 if (!strncmp(ion, "Ne", 2)) {
01755 return "Ne";
01756 }
01757
01758 if (!strncmp(ion, "Xe", 2)) {
01759 return "Xe";
01760 }
01761 return "Unknown_Lamp";
01762 }
01763
01764
01791
01792 cpl_table *
01793 muse_wave_lines_search(muse_image *aColumnImage, double aSigma,
01794 const unsigned short aSlice, const unsigned char aIFU)
01795 {
01796 #if !SEARCH_DEBUG_FILES
01797 UNUSED_ARGUMENT(aSlice);
01798 #endif
01799 cpl_ensure(aColumnImage, CPL_ERROR_NULL_INPUT, NULL);
01800 cpl_ensure(cpl_image_get_min(aColumnImage->stat) > 0.,
01801 CPL_ERROR_DATA_NOT_FOUND, NULL);
01802 cpl_ensure(aSigma > 0., CPL_ERROR_ILLEGAL_INPUT, NULL);
01803
01804 #define SEARCH_SUBTRACT_BG 1
01805 #if SEARCH_SUBTRACT_BG
01806
01807
01808
01809 cpl_image *bgmedian = cpl_image_duplicate(aColumnImage->data);
01810 cpl_image_fill_noise_uniform(bgmedian, -FLT_MIN, FLT_MIN);
01811 #define BG_KERNEL_WIDTH 51
01812 cpl_mask *filter = cpl_mask_new(1, BG_KERNEL_WIDTH);
01813 cpl_mask_not(filter);
01814 cpl_image_filter_mask(bgmedian, aColumnImage->data, filter, CPL_FILTER_MEDIAN,
01815 CPL_BORDER_FILTER);
01816 cpl_mask_delete(filter);
01817 #if SEARCH_DEBUG_FILES
01818 char fn[FILENAME_MAX];
01819 sprintf(fn, "column_slice%02hu.fits", aSlice);
01820 muse_image_save(aColumnImage, fn);
01821 sprintf(fn, "column_slice%02hu_bgmedian.fits", aSlice);
01822 cpl_image_save(bgmedian, fn, CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE);
01823 #endif
01824 cpl_image *bgsub = cpl_image_subtract_create(aColumnImage->data, bgmedian);
01825 cpl_image_delete(bgmedian);
01826 #if SEARCH_DEBUG_FILES
01827 sprintf(fn, "column_slice%02hu_bgsub.fits", aSlice);
01828 cpl_image_save(bgsub, fn, CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE);
01829 #endif
01830 #endif
01831
01832
01833 cpl_image *N = cpl_image_power_create(aColumnImage->stat, 0.5),
01834 #if SEARCH_SUBTRACT_BG
01835 *SN = cpl_image_divide_create(bgsub, N);
01836 cpl_image_delete(bgsub);
01837 #else
01838 *SN = cpl_image_divide_create(aColumnImage->data, N);
01839 #endif
01840 cpl_image_delete(N);
01841 #if SEARCH_DEBUG_FILES
01842 sprintf(fn, "column_slice%02hu_SN.fits", aSlice);
01843 cpl_image_save(SN, fn, CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE);
01844 #endif
01845
01846
01847 double mdev, median = cpl_image_get_median_dev(SN, &mdev),
01848 limitSN = fmax(0.1, median + aSigma*mdev);
01849 cpl_mask *mask = cpl_mask_threshold_image_create(SN, limitSN, FLT_MAX);
01850 cpl_size nlines = 0;
01851 cpl_image *label = cpl_image_labelise_mask_create(mask, &nlines);
01852 cpl_mask_delete(mask);
01853 #if SEARCH_DEBUG_FILES
01854 sprintf(fn, "column_slice%02hu_label.fits", aSlice);
01855 cpl_image_save(label, fn, CPL_TYPE_USHORT, NULL, CPL_IO_CREATE);
01856 #endif
01857 #if SEARCH_DEBUG || SEARCH_DEBUG_FILES
01858 double mean = cpl_image_get_mean(SN),
01859 stdev = cpl_image_get_stdev(SN),
01860 min = cpl_image_get_min(SN),
01861 max = cpl_image_get_max(SN);
01862 cpl_msg_debug(__func__, "%"CPL_SIZE_FORMAT" lines found; parameters: sigma=%f, "
01863 "med=%f+/-%f " "mean=%f+/-%f min/max=%f/%f limit(S/N)=%f", nlines,
01864 aSigma, median, mdev, mean, stdev, min, max, limitSN);
01865 #endif
01866 cpl_image_delete(SN);
01867
01868
01869 cpl_table *linesresult = muse_cpltable_new(muse_wavelines_def, nlines);
01870
01871
01872 int i;
01873 for (i = 0; i < nlines; i++) {
01874
01875 cpl_mask *linemask = cpl_mask_threshold_image_create(label, i+0.5, i+1.5);
01876 int masksize = cpl_mask_get_size_y(linemask);
01877
01878
01879 int j = 1;
01880 while (j <= masksize && cpl_mask_get(linemask, 1, j) == CPL_BINARY_0) {
01881 j++;
01882 }
01883 int lopix = j;
01884 while (j <= masksize && cpl_mask_get(linemask, 1, j) == CPL_BINARY_1) {
01885 j++;
01886 }
01887 int hipix = j - 1;
01888 double cenpix = (hipix + lopix) / 2.;
01889 #if SEARCH_DEBUG
01890 int err;
01891 cpl_msg_debug(__func__, "line %d lo/hi/cen=%d,%d,%f: values=%f,%f", i + 1,
01892 lopix, hipix, cenpix,
01893 cpl_image_get(aColumnImage->data, 1, lopix, &err),
01894 cpl_image_get(aColumnImage->data, 1, hipix, &err));
01895 #endif
01896 cpl_mask_delete(linemask);
01897 if (lopix == hipix) {
01898
01899
01900 cpl_table_set(linesresult, "flux", i, -1.);
01901 continue;
01902 }
01903
01904
01905
01906
01907 #define MAXENLARGE 5
01908
01909 int err0, err1 = 0, err2 = 0;
01910 double valref = cpl_image_get(aColumnImage->data, 1, lopix, &err0);
01911 cpl_errorstate prestate = cpl_errorstate_get();
01912 j = lopix;
01913 double value = -FLT_MAX;
01914 while (err1 == 0 && value < valref && (lopix - j) <= MAXENLARGE) {
01915 j--;
01916 value = cpl_image_get(aColumnImage->data, 1, j, &err1);
01917 }
01918 lopix = j + 1;
01919
01920
01921 valref = cpl_image_get(aColumnImage->data, 1, hipix, &err2);
01922 j = hipix;
01923 value = -FLT_MAX;
01924 while (err2 == 0 && value < valref && (j - hipix) <= MAXENLARGE) {
01925 j++;
01926 value = cpl_image_get(aColumnImage->data, 1, j, &err2);
01927 }
01928 hipix = j - 1;
01929 if (lopix > hipix) {
01930
01931 continue;
01932 }
01933 #if SEARCH_DEBUG
01934 cpl_msg_debug(__func__, "region=%d...%d, size=%d", lopix, hipix,
01935 hipix - lopix + 1);
01936 #endif
01937 if (err1 < 0 || err2 < 0) {
01938 cpl_errorstate_set(prestate);
01939 }
01940
01941
01942 cpl_vector *positions = cpl_vector_new(hipix - lopix + 1),
01943 *values = cpl_vector_new(hipix - lopix + 1),
01944 *valuessigma = cpl_vector_new(hipix - lopix + 1);
01945 int k;
01946 for (j = lopix, k = 0; j <= hipix; j++, k++) {
01947 cpl_vector_set(positions, k, j);
01948 cpl_vector_set(values, k, cpl_image_get(aColumnImage->data, 1, j, &err0));
01949
01950
01951 cpl_vector_set(valuessigma, k, sqrt(cpl_image_get(aColumnImage->stat,
01952 1, j, &err0)));
01953 }
01954
01955 #if SEARCH_DEBUG
01956 cpl_bivector *biv = cpl_bivector_wrap_vectors(positions, values);
01957 cpl_bivector_dump(biv, stdout);
01958 cpl_bivector_unwrap_vectors(biv);
01959 #endif
01960
01961
01962
01963 double center, cerr, sigma, area, bglevel, mse;
01964 cpl_matrix *covariance;
01965 prestate = cpl_errorstate_get();
01966 cpl_error_code rc = cpl_vector_fit_gaussian(positions, NULL, values,
01967 valuessigma, CPL_FIT_ALL,
01968 ¢er, &sigma, &area, &bglevel,
01969 &mse, NULL, &covariance);
01970
01971
01972 if (rc == CPL_ERROR_CONTINUE) {
01973
01974
01975 cerr = sqrt(sigma * sigma / area);
01976 cpl_msg_debug(__func__, "Gaussian fit in slice %hu of IFU %hhu around "
01977 "position %6.1f: %s", aSlice, aIFU, cenpix,
01978 cpl_error_get_message());
01979 #if DEBUG_GAUSSFIT
01980 if (cpl_msg_get_log_level() == CPL_MSG_DEBUG) {
01981
01982
01983 cpl_bivector *bv = cpl_bivector_wrap_vectors(positions, values);
01984 printf("Gaussian fit: %f+/-%f, %f, %f, %f %f\nand the input data:\n",
01985 center, cerr, bglevel, area, sigma, mse);
01986 cpl_bivector_dump(bv, stdout);
01987 cpl_bivector_unwrap_vectors(bv);
01988 fflush(stdout);
01989 }
01990 #endif
01991 } else if (rc == CPL_ERROR_SINGULAR_MATRIX || !covariance) {
01992 cerr = sqrt(sigma * sigma / area);
01993 cpl_msg_debug(__func__, "Gaussian fit in slice %hu of IFU %hhu around "
01994 "position %6.1f: %s", aSlice, aIFU, cenpix,
01995 cpl_error_get_message());
01996 } else if (rc != CPL_ERROR_NONE) {
01997
01998
01999
02000 cpl_msg_debug(__func__, "Gaussian fit in slice %hu of IFU %hhu around "
02001 "position %6.1f: %s", aSlice, aIFU, cenpix,
02002 cpl_error_get_message());
02003 cerr = 100.;
02004 } else {
02005
02006
02007 cerr = sqrt(cpl_matrix_get(covariance, 0, 0));
02008 #if SEARCH_DEBUG
02009 cpl_matrix_dump(covariance, stdout);
02010 #endif
02011 cpl_matrix_delete(covariance);
02012 }
02013 cpl_errorstate_set(prestate);
02014 if (fabs(center - cenpix) > MUSE_WAVE_LINES_SEARCH_SHIFT_WARN) {
02015 cpl_msg_debug(__func__, "Large shift in Gaussian centering in slice %hu "
02016 "of IFU %hhu: initial %7.2f, fit %7.2f", aSlice, aIFU,
02017 cenpix, center);
02018 }
02019
02020
02021
02022 cpl_table_set(linesresult, "y", i, cenpix);
02023
02024
02025
02026 cpl_table_set(linesresult, "peak", i, cpl_vector_get_max(values));
02027 cpl_table_set(linesresult, "center", i, center);
02028 cpl_table_set(linesresult, "cerr", i, cerr);
02029 cpl_table_set(linesresult, "fwhm", i, CPL_MATH_FWHM_SIG * sigma);
02030 cpl_table_set(linesresult, "sigma", i, sigma);
02031 cpl_table_set(linesresult, "flux", i, area);
02032 cpl_table_set(linesresult, "bg", i, bglevel);
02033 cpl_table_set(linesresult, "mse", i, mse);
02034
02035 cpl_vector_delete(positions);
02036 cpl_vector_delete(values);
02037 cpl_vector_delete(valuessigma);
02038 #if SEARCH_DEBUG
02039 printf("%s: results of fit stored in table row %d:\n", __func__, i + 1);
02040 cpl_table_dump(linesresult, i, 1, stdout);
02041 fflush(stdout);
02042 #endif
02043 }
02044 cpl_image_delete(label);
02045
02046
02047 cpl_table_unselect_all(linesresult);
02048 for (i = 0; i < cpl_table_get_nrow(linesresult); i++) {
02049 if (cpl_table_get(linesresult, "cerr", i, NULL) > kMuseArcMaxCenteringError ||
02050 cpl_table_get(linesresult, "fwhm", i, NULL) < kMuseArcMinFWHM ||
02051 cpl_table_get(linesresult, "fwhm", i, NULL) > kMuseArcMaxFWHM ||
02052 cpl_table_get(linesresult, "flux", i, NULL) < kMuseArcMinFlux) {
02053 cpl_table_select_row(linesresult, i);
02054 }
02055 }
02056 cpl_table_erase_selected(linesresult);
02057
02058 return linesresult;
02059 }
02060
02061
02081
02082 cpl_error_code
02083 muse_wave_lines_identify(cpl_table *aLines, cpl_vector *aLambdas,
02084 const muse_wave_params *aParams)
02085 {
02086 cpl_ensure_code(aLines && aLambdas, CPL_ERROR_NULL_INPUT);
02087
02088
02089 int i, nlines = cpl_table_get_nrow(aLines);
02090 cpl_vector *vcenter = cpl_vector_new(nlines);
02091 for (i = 0; i < nlines; i++) {
02092 cpl_vector_set(vcenter, i, cpl_table_get(aLines, "center", i, NULL));
02093 }
02094 #if 0
02095 cpl_vector_dump(vcenter, stdout);
02096 #endif
02097 double dmin = kMuseSpectralSamplingA - kMuseSpectralSamplingA * aParams->ddisp,
02098 dmax = kMuseSpectralSamplingA + kMuseSpectralSamplingA * aParams->ddisp;
02099 cpl_bivector *id_lines = cpl_ppm_match_positions(vcenter, aLambdas, dmin, dmax,
02100 aParams->tolerance, NULL, NULL);
02101 #if 0
02102 cpl_msg_debug(__func__, "%"CPL_SIZE_FORMAT" identified lines (of %"
02103 CPL_SIZE_FORMAT" detected lines and %"CPL_SIZE_FORMAT
02104 " from linelist)", cpl_bivector_get_size(id_lines),
02105 cpl_vector_get_size(vcenter), cpl_vector_get_size(aLambdas));
02106 cpl_bivector_dump(id_lines, stdout);
02107 fflush(stdout);
02108 #endif
02109 cpl_vector_delete(vcenter);
02110
02111 #if 0
02112 printf("detected lines before cleanup:\n");
02113 cpl_table_dump(aLines, 0, nlines, stdout);
02114 fflush(stdout);
02115 #endif
02116
02117 const double *id_center = cpl_bivector_get_x_data_const(id_lines),
02118 *id_lambda = cpl_bivector_get_y_data_const(id_lines);
02119 cpl_table_unselect_all(aLines);
02120 int j, nid = cpl_bivector_get_size(id_lines);
02121 for (i = 0, j = 0; i < cpl_table_get_nrow(aLines) && id_center && id_lambda; i++) {
02122 #if 0
02123 cpl_msg_debug(__func__, "c=%f, l=%f, c_det=%f", id_center[j], id_lambda[j],
02124 cpl_table_get(aLines, "center", i, NULL));
02125 #endif
02126 if (j < nid && fabs(id_center[j] - cpl_table_get(aLines, "center", i, NULL))
02127 < DBL_EPSILON) {
02128
02129 cpl_table_set(aLines, "lambda", i, id_lambda[j]);
02130 j++;
02131 continue;
02132 }
02133 cpl_table_select_row(aLines, i);
02134 }
02135 cpl_table_erase_selected(aLines);
02136 cpl_bivector_delete(id_lines);
02137 int debug = getenv("MUSE_DEBUG_WAVECAL")
02138 ? atoi(getenv("MUSE_DEBUG_WAVECAL")) : 0;
02139 if (debug >= 2) {
02140 printf("identified %d lines, %"CPL_SIZE_FORMAT" after cleanup:\n", nid,
02141 cpl_table_get_nrow(aLines));
02142 cpl_table_dump(aLines, 0, nid, stdout);
02143 fflush(stdout);
02144 }
02145 nid = cpl_table_get_nrow(aLines);
02146
02147 if (nid <= 0) {
02148 return CPL_ERROR_DATA_NOT_FOUND;
02149 } else if (nid < aParams->yorder + 1) {
02150 return CPL_ERROR_ILLEGAL_OUTPUT;
02151 }
02152 return CPL_ERROR_NONE;
02153 }
02154
02155
02187
02188 cpl_table *
02189 muse_wave_line_handle_singlet(muse_image *aImage, cpl_table *aLinelist,
02190 unsigned int aIdx, cpl_polynomial *aPoly,
02191 cpl_polynomial **aTrace,
02192 const muse_wave_params *aParams,
02193 const unsigned short aSlice, int aDebug)
02194 {
02195 cpl_ensure(aImage && aLinelist && aPoly && aTrace, CPL_ERROR_NULL_INPUT, NULL);
02196
02197
02198
02199
02200 double sigma = kMuseSliceSlitWidthA / kMuseSpectralSamplingA / CPL_MATH_FWHM_SIG;
02201
02202 if (cpl_table_get(aLinelist, MUSE_LINE_CATALOG_QUALITY, aIdx, NULL) != 5) {
02203 sigma *= -1;
02204 }
02205 int halfwidth = 3.*kMuseSliceSlitWidthA / kMuseSpectralSamplingA;
02206
02207
02208 double lambda = cpl_table_get(aLinelist, MUSE_LINE_CATALOG_LAMBDA, aIdx, NULL),
02209 ypos = cpl_polynomial_eval_1d(aPoly, lambda, NULL);
02210 if ((ypos - halfwidth) < 1 ||
02211 (ypos + halfwidth) > cpl_image_get_size_y(aImage->data)) {
02212 if (aDebug >= 2) {
02213 cpl_msg_debug(__func__, "%f is supposed to lie near %.3f in slice %2hu of"
02214 " IFU %hhu, i.e. outside!", lambda, ypos, aSlice,
02215 muse_utils_get_ifu(aImage->header));
02216 }
02217 return NULL;
02218 }
02219 if (aDebug >= 2) {
02220 cpl_msg_debug(__func__, "%f is supposed to lie near %.3f in slice %2hu of "
02221 "IFU %hhu", lambda, ypos, aSlice,
02222 muse_utils_get_ifu(aImage->header));
02223 }
02224
02225 double dleft = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_LEFT],
02226 ypos, NULL),
02227 dright = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_RIGHT],
02228 ypos, NULL),
02229 dmid = (dleft + dright) / 2.;
02230 int ileft = ceil(dleft),
02231 iright = floor(dright);
02232 #if 0
02233 cpl_msg_debug(__func__, "limits at y=%f: %f < %f < %f", ypos, dleft, dmid,
02234 dright);
02235 #endif
02236
02237
02238 cpl_table *fittable = muse_cpltable_new(muse_wavelines_def,
02239 (int)kMuseSliceHiLikelyWidth + 5);
02240
02241
02242
02243 double y = ypos;
02244 int i, n = 0;
02245 for (i = dmid; i >= ileft; i--) {
02246 cpl_error_code rc = muse_wave_line_fit_single(aImage, i, y, halfwidth,
02247 sigma, fittable, ++n);
02248 if (rc != CPL_ERROR_NONE) {
02249 --n;
02250 } else {
02251 double ynew = cpl_table_get(fittable, "center", n - 1, NULL);
02252 if (fabs(y - ynew) < MUSE_WAVE_LINE_HANDLE_SHIFT_LIMIT) {
02253 y = ynew;
02254 }
02255 }
02256 }
02257 #if 0
02258 printf("arc line aIdx=%u, columns i=%d...", aIdx + 1, i);
02259 #endif
02260 y = ypos;
02261 for (i = dmid + 1; i <= iright; i++) {
02262 cpl_error_code rc = muse_wave_line_fit_single(aImage, i, ypos, halfwidth,
02263 sigma, fittable, ++n);
02264 if (rc != CPL_ERROR_NONE) {
02265 --n;
02266 } else {
02267 double ynew = cpl_table_get(fittable, "center", n - 1, NULL);
02268 if (fabs(y - ynew) < MUSE_WAVE_LINE_HANDLE_SHIFT_LIMIT) {
02269 y = ynew;
02270 }
02271 }
02272 }
02273
02274
02275
02276 cpl_table_select_all(fittable);
02277 cpl_table_and_selected_invalid(fittable, "center");
02278 cpl_table_erase_selected(fittable);
02279 cpl_table_fill_column_window(fittable, "lambda", 0,
02280 cpl_table_get_nrow(fittable), lambda);
02281 #if 0
02282 printf("line %u, %d line fits\n", aIdx + 1, n);
02283 cpl_table_dump(fittable, 0, n, stdout);
02284 fflush(stdout);
02285 #endif
02286
02287 muse_wave_line_fit_iterate(fittable, lambda, aParams);
02288 int npos = cpl_table_get_nrow(fittable);
02289 if (npos <= 0) {
02290 cpl_msg_warning(__func__, "Polynomial fit failed in slice %hu of IFU %hhu "
02291 "for line %u (y-position near %.2f pix): %s", aSlice,
02292 muse_utils_get_ifu(aImage->header), aIdx + 1, ypos,
02293 cpl_error_get_message());
02294 }
02295 #if 0
02296 else {
02297 printf("%s: line %2u, %d line fits, %d with low residuals:\n", __func__,
02298 aIdx + 1, n, npos);
02299 cpl_table_dump(fittable, 0, npos, stdout);
02300 fflush(stdout);
02301 }
02302 #endif
02303 return fittable;
02304 }
02305
02306
02342
02343 cpl_table *
02344 muse_wave_line_handle_multiplet(muse_image *aImage, cpl_table *aLinelist,
02345 unsigned int aIdx, cpl_polynomial *aPoly,
02346 cpl_polynomial **aTrace,
02347 const muse_wave_params *aParams,
02348 const unsigned short aSlice, int aDebug)
02349 {
02350 cpl_ensure(aImage && aLinelist && aPoly && aTrace, CPL_ERROR_NULL_INPUT, NULL);
02351
02352
02353 #define MULTIPLET_SEARCH_RANGE 40.
02354 unsigned int nlines = 1;
02355 double lbda1 = cpl_table_get(aLinelist, MUSE_LINE_CATALOG_LAMBDA, aIdx, NULL);
02356 const char *lamp1 = muse_wave_lines_get_lampname(aLinelist, aIdx);
02357 cpl_vector *lambdas = cpl_vector_new(nlines),
02358 *fluxes = cpl_vector_new(nlines);
02359 cpl_vector_set(lambdas, 0, lbda1);
02360 cpl_vector_set(fluxes, 0, cpl_table_get(aLinelist, MUSE_LINE_CATALOG_FLUX,
02361 aIdx, NULL));
02362 double lambda;
02363 unsigned int j = aIdx;
02364 for (lambda = cpl_table_get(aLinelist, MUSE_LINE_CATALOG_LAMBDA, ++j, NULL);
02365 fabs(lambda - lbda1) < MULTIPLET_SEARCH_RANGE;
02366 lambda = cpl_table_get(aLinelist, MUSE_LINE_CATALOG_LAMBDA, ++j, NULL)) {
02367 int quality = cpl_table_get(aLinelist, MUSE_LINE_CATALOG_QUALITY, j, NULL);
02368 const char *lamp = muse_wave_lines_get_lampname(aLinelist, j);
02369 if (quality == 2 && !strcmp(lamp1, lamp)) {
02370 nlines++;
02371 cpl_vector_set_size(lambdas, nlines);
02372 cpl_vector_set_size(fluxes, nlines);
02373 cpl_vector_set(lambdas, nlines - 1, lambda);
02374 double flux = cpl_table_get(aLinelist, MUSE_LINE_CATALOG_FLUX, j, NULL);
02375 cpl_vector_set(fluxes, nlines - 1, flux);
02376
02377 cpl_table_set(aLinelist, MUSE_LINE_CATALOG_QUALITY, j, -quality);
02378 }
02379 }
02380
02381 if (aDebug >= 2) {
02382 printf("found multiplet of lamp %s with %u lines:\n", lamp1, nlines);
02383 cpl_bivector *bv = cpl_bivector_wrap_vectors(lambdas, fluxes);
02384 cpl_bivector_dump(bv, stdout);
02385 cpl_bivector_unwrap_vectors(bv);
02386 fflush(stdout);
02387 }
02388
02389
02390
02391 double sigma = kMuseSliceSlitWidthA / kMuseSpectralSamplingA / CPL_MATH_FWHM_SIG;
02392 int halfwidth = 3.*kMuseSliceSlitWidthA / kMuseSpectralSamplingA;
02393
02394 cpl_vector *ypos = cpl_vector_new(nlines);
02395 for (j = 0; j < nlines; j++) {
02396 double yp = cpl_polynomial_eval_1d(aPoly, cpl_vector_get(lambdas, j), NULL);
02397 cpl_vector_set(ypos, j, yp);
02398 }
02399 double ypos1 = cpl_vector_get(ypos, 0),
02400 ypos2 = cpl_vector_get(ypos, nlines - 1);
02401 cpl_bivector *peaks = cpl_bivector_wrap_vectors(ypos, fluxes);
02402 if ((ypos1 - halfwidth) < 1 ||
02403 (ypos2 + halfwidth) > cpl_image_get_size_y(aImage->data)) {
02404 if (aDebug >= 2) {
02405 cpl_msg_debug(__func__, "%f is supposed to lie at %.3f..%.3f in slice "
02406 "%2hu of IFU %hhu, i.e. outside!", lambda, ypos1, ypos2,
02407 aSlice, muse_utils_get_ifu(aImage->header));
02408 }
02409 cpl_bivector_delete(peaks);
02410 cpl_vector_delete(lambdas);
02411 return NULL;
02412 }
02413 if (aDebug >= 2) {
02414 cpl_msg_debug(__func__, "%f is supposed to lie at %.3f..%.3f in slice %2hu "
02415 "of IFU %hhu", lambda, ypos1, ypos2, aSlice,
02416 muse_utils_get_ifu(aImage->header));
02417 }
02418
02419 double dleft = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_LEFT],
02420 (ypos1 + ypos2)/2., NULL),
02421 dright = cpl_polynomial_eval_1d(aTrace[MUSE_TRACE_RIGHT],
02422 (ypos1 + ypos2)/2., NULL),
02423 dmid = (dleft + dright) / 2.;
02424 int ileft = ceil(dleft),
02425 iright = floor(dright);
02426
02427
02428 cpl_table *fittable = muse_cpltable_new(muse_wavelines_def,
02429 ((int)kMuseSliceHiLikelyWidth + 5) * nlines);
02430
02431
02432
02433 cpl_bivector *bp = cpl_bivector_duplicate(peaks),
02434 *bpgood = cpl_bivector_duplicate(peaks);
02435 int i, n = 0;
02436 for (i = dmid; i >= ileft; i--) {
02437 n += nlines;
02438 cpl_error_code rc = muse_wave_line_fit_multiple(aImage, i, bp, lambdas,
02439 halfwidth, sigma, fittable, n);
02440 if (rc != CPL_ERROR_NONE) {
02441 cpl_bivector_delete(bp);
02442 bp = cpl_bivector_duplicate(bpgood);
02443 n -= nlines;
02444 } else {
02445 cpl_vector *shifts = cpl_vector_duplicate(cpl_bivector_get_x(bp));
02446 cpl_vector_subtract(shifts, cpl_bivector_get_x(bpgood));
02447 double shift = cpl_vector_get_median(shifts);
02448 cpl_vector_delete(shifts);
02449 if (fabs(shift) < MUSE_WAVE_LINE_HANDLE_SHIFT_LIMIT) {
02450 cpl_bivector_delete(bpgood);
02451 bpgood = cpl_bivector_duplicate(bp);
02452 } else {
02453 cpl_bivector_delete(bp);
02454 bp = cpl_bivector_duplicate(bpgood);
02455 }
02456 }
02457 }
02458 cpl_bivector_delete(bp);
02459 cpl_bivector_delete(bpgood);
02460 bp = cpl_bivector_duplicate(peaks);
02461 bpgood = cpl_bivector_duplicate(peaks);
02462 for (i = dmid + 1; i <= iright; i++) {
02463 n += nlines;
02464 cpl_error_code rc = muse_wave_line_fit_multiple(aImage, i, bp, lambdas,
02465 halfwidth, sigma, fittable, n);
02466 if (rc != CPL_ERROR_NONE) {
02467 cpl_bivector_delete(bp);
02468 bp = cpl_bivector_duplicate(bpgood);
02469 n -= nlines;
02470 } else {
02471 cpl_vector *shifts = cpl_vector_duplicate(cpl_bivector_get_x(bp));
02472 cpl_vector_subtract(shifts, cpl_bivector_get_x(bpgood));
02473 double shift = cpl_vector_get_median(shifts);
02474 cpl_vector_delete(shifts);
02475 if (fabs(shift) < MUSE_WAVE_LINE_HANDLE_SHIFT_LIMIT) {
02476 cpl_bivector_delete(bpgood);
02477 bpgood = cpl_bivector_duplicate(bp);
02478 } else {
02479 cpl_bivector_delete(bp);
02480 bp = cpl_bivector_duplicate(bpgood);
02481 }
02482 }
02483 }
02484 cpl_bivector_delete(bp);
02485 cpl_bivector_delete(bpgood);
02486
02487
02488 cpl_table_select_all(fittable);
02489 cpl_table_and_selected_invalid(fittable, "center");
02490 cpl_table_erase_selected(fittable);
02491 cpl_bivector_delete(peaks);
02492
02493 for (j = 0; j < nlines; j++) {
02494 muse_wave_line_fit_iterate(fittable, cpl_vector_get(lambdas, j), aParams);
02495 }
02496 cpl_vector_delete(lambdas);
02497
02498 return fittable;
02499 }
02500
02501
02532
02533 cpl_error_code
02534 muse_wave_line_fit_single(muse_image *aImage, int aX, double aY, int aHalfWidth,
02535 double aSigma, cpl_table *aFitTable, int aRowsNeeded)
02536 {
02537 cpl_ensure_code(aImage && aImage->data && aImage->stat && aFitTable,
02538 CPL_ERROR_NULL_INPUT);
02539 #if 0
02540 cpl_msg_debug(__func__, "%d %d %d -> %d, %d", aX, (int)aY, aHalfWidth,
02541 2*aHalfWidth+1, aRowsNeeded);
02542 #endif
02543
02544 cpl_vector *positions = cpl_vector_new(2*aHalfWidth+1),
02545 *values = cpl_vector_new(2*aHalfWidth+1),
02546 *valuessigma = cpl_vector_new(2*aHalfWidth+1);
02547 int i, j, ny = cpl_image_get_size_y(aImage->data);
02548 for (i = (int)aY - aHalfWidth, j = 0; i <= (int)aY + aHalfWidth && i <= ny;
02549 i++, j++) {
02550 int err;
02551 cpl_vector_set(positions, j, i);
02552 cpl_vector_set(values, j, cpl_image_get(aImage->data, aX, i, &err));
02553 cpl_vector_set(valuessigma, j,
02554 sqrt(cpl_image_get(aImage->stat, aX, i, &err)));
02555 }
02556 #if 0
02557 printf("input vectors: positions, values\n");
02558 cpl_bivector *biv = cpl_bivector_wrap_vectors(positions, values);
02559 cpl_bivector_dump(biv, stdout);
02560 fflush(stdout);
02561 cpl_bivector_unwrap_vectors(biv);
02562 #if 0
02563 printf("input vectors: values, valuessigma\n");
02564 biv = cpl_bivector_wrap_vectors(values, valuessigma);
02565 cpl_bivector_dump(biv, stdout);
02566 fflush(stdout);
02567 cpl_bivector_unwrap_vectors(biv);
02568 #endif
02569 #endif
02570
02571
02572 cpl_errorstate prestate = cpl_errorstate_get();
02573 double center, cerror, area, bglevel, mse;
02574 cpl_matrix *covariance = NULL;
02575 cpl_fit_mode mode = CPL_FIT_ALL;
02576 double sigma = aSigma;
02577 if (sigma < 0) {
02578 mode = CPL_FIT_CENTROID | CPL_FIT_AREA | CPL_FIT_OFFSET;
02579 sigma *= -1;
02580 }
02581 cpl_error_code rc
02582 = cpl_vector_fit_gaussian(positions, NULL, values, valuessigma, mode,
02583 ¢er, &sigma, &area, &bglevel, &mse, NULL,
02584 &covariance);
02585 #if 0
02586 printf("--> rc=%d (%d) %s %#x %#x\n", rc, CPL_ERROR_NONE,
02587 cpl_error_get_message(), covariance, &covariance);
02588 fflush(stdout);
02589 #endif
02590 cpl_vector_delete(positions);
02591 cpl_vector_delete(values);
02592 cpl_vector_delete(valuessigma);
02593
02594
02595
02596 if (covariance) {
02597 cerror = sqrt(cpl_matrix_get(covariance, 0, 0));
02598 cpl_matrix_delete(covariance);
02599 } else {
02600 cpl_msg_debug(__func__, "Gauss fit produced no covariance matrix (y=%.3f in "
02601 "column=%d): %s", aY, aX, cpl_error_get_message());
02602 cpl_errorstate_set(prestate);
02603 return rc == CPL_ERROR_NONE ? CPL_ERROR_ILLEGAL_OUTPUT : rc;
02604 }
02605 if (rc == CPL_ERROR_CONTINUE) {
02606
02607
02608 cerror = sqrt(sigma * sigma / area);
02609 cpl_errorstate_set(prestate);
02610 rc = CPL_ERROR_NONE;
02611 }
02612 if (rc != CPL_ERROR_NONE) {
02613
02614 cpl_msg_debug(__func__, "Gauss fit failed with some error (y=%.3f in "
02615 "column=%d): %s", aY, aX, cpl_error_get_message());
02616 cpl_errorstate_set(prestate);
02617 return rc;
02618 }
02619 if (fabs(center - aY) > MUSE_WAVE_LINE_FIT_MAXSHIFT) {
02620
02621 cpl_msg_debug(__func__, "Gauss fit gave unexpectedly large offset "
02622 "(shifted %.3f pix from y=%.3f in column=%d)",
02623 center - aY, aY, aX);
02624 return CPL_ERROR_ACCESS_OUT_OF_RANGE;
02625 }
02626
02627
02628 if (cpl_table_get_nrow(aFitTable) < aRowsNeeded) {
02629 cpl_table_set_size(aFitTable, aRowsNeeded);
02630 }
02631 cpl_table_set(aFitTable, "center", aRowsNeeded - 1, center);
02632 cpl_table_set(aFitTable, "cerr", aRowsNeeded - 1, cerror);
02633 cpl_table_set(aFitTable, "sigma", aRowsNeeded - 1, sigma);
02634 if (mode == CPL_FIT_ALL) {
02635 cpl_table_set(aFitTable, "fwhm", aRowsNeeded - 1, CPL_MATH_FWHM_SIG * sigma);
02636 }
02637 cpl_table_set(aFitTable, "flux", aRowsNeeded - 1, area);
02638 cpl_table_set(aFitTable, "bg", aRowsNeeded - 1, bglevel);
02639 cpl_table_set(aFitTable, "mse", aRowsNeeded - 1, mse);
02640 cpl_table_set(aFitTable, "x", aRowsNeeded - 1, aX);
02641 cpl_table_set(aFitTable, "y", aRowsNeeded - 1, aY);
02642
02643 #if 0
02644 printf("--> Gaussian fit: %f +/- %f, %f\n", center, cerror, area);
02645 fflush(stdout);
02646 #endif
02647 return rc;
02648 }
02649
02650
02688
02689 cpl_error_code
02690 muse_wave_line_fit_multiple(muse_image *aImage, int aX, cpl_bivector *aPeaks,
02691 cpl_vector *aLambdas, int aHalfWidth, double aSigma,
02692 cpl_table *aFitTable, int aRowsNeeded)
02693 {
02694 cpl_ensure_code(aImage && aImage->data && aImage->stat && aFitTable,
02695 CPL_ERROR_NULL_INPUT);
02696 cpl_vector *ypeaks = cpl_bivector_get_x(aPeaks),
02697 *fluxes = cpl_bivector_get_y(aPeaks);
02698 int np = cpl_vector_get_size(ypeaks);
02699 double yp1 = cpl_vector_get(ypeaks, 0),
02700 yp2 = cpl_vector_get(ypeaks, np - 1);
02701 int i1 = floor(yp1),
02702 i2 = ceil(yp2);
02703 #if 0
02704 cpl_msg_debug(__func__, "%d %d..%d %d -> %d", aX, i1, i2, aHalfWidth,
02705 aRowsNeeded);
02706 #endif
02707
02708 int npoints = (i2 + aHalfWidth) - (i1 - aHalfWidth) + 1;
02709 cpl_vector *positions = cpl_vector_new(npoints),
02710 *values = cpl_vector_new(npoints),
02711 *valuessigma = cpl_vector_new(npoints);
02712 double minval = DBL_MAX;
02713 int i, j, ny = cpl_image_get_size_y(aImage->data);
02714 for (i = i1 - aHalfWidth, j = 0; i <= i2 + aHalfWidth && i <= ny; i++, j++) {
02715 int err;
02716 cpl_vector_set(positions, j, i);
02717 double value = cpl_image_get(aImage->data, aX, i, &err);
02718 cpl_vector_set(values, j, value);
02719 if (value < minval) {
02720 minval = value;
02721 }
02722 cpl_vector_set(valuessigma, j,
02723 sqrt(cpl_image_get(aImage->stat, aX, i, &err)));
02724 }
02725 minval = minval > 0 ? minval : 0.;
02726 #if 0
02727 printf("input vectors: positions, values\n");
02728 cpl_bivector *biv = cpl_bivector_wrap_vectors(positions, values);
02729 cpl_bivector_dump(biv, stdout);
02730 fflush(stdout);
02731 cpl_bivector_unwrap_vectors(biv);
02732 #endif
02733 cpl_bivector *bvalues = cpl_bivector_wrap_vectors(values, valuessigma);
02734
02735
02736
02737 cpl_vector *poly = cpl_vector_new(2);
02738 cpl_vector_set(poly, 0, minval);
02739 cpl_vector_set(poly, 1, 0.);
02740
02741 cpl_array *afluxes = cpl_array_wrap_double(cpl_vector_get_data(fluxes), np);
02742 cpl_size imaxflux;
02743 cpl_array_get_maxpos(afluxes, &imaxflux);
02744 double yposmax = cpl_vector_get(ypeaks, imaxflux);
02745 cpl_array_unwrap(afluxes);
02746
02747 cpl_errorstate prestate = cpl_errorstate_get();
02748 double sigma = aSigma,
02749 mse, chisq;
02750 cpl_matrix *covariance;
02751 cpl_error_code rc = muse_utils_fit_multigauss_1d(positions, bvalues, ypeaks,
02752 &sigma, fluxes, poly,
02753 &mse, &chisq, &covariance);
02754 cpl_vector_delete(positions);
02755 cpl_bivector_delete(bvalues);
02756
02757
02758
02759
02760 if (!covariance) {
02761 cpl_msg_debug(__func__, "Multi-Gauss fit produced no covariance matrix (y=%."
02762 "3f..%.3f in column=%d): %s", yp1, yp2, aX, cpl_error_get_message());
02763 cpl_errorstate_set(prestate);
02764 cpl_vector_delete(poly);
02765 return rc == CPL_ERROR_NONE ? CPL_ERROR_ILLEGAL_OUTPUT : rc;
02766 }
02767 if (rc != CPL_ERROR_NONE) {
02768
02769 cpl_msg_debug(__func__, "Multi-Gauss fit failed with some error (y=%.3f..%."
02770 "3f in column=%d): %s", yp1, yp2, aX, cpl_error_get_message());
02771 cpl_errorstate_set(prestate);
02772 cpl_matrix_delete(covariance);
02773 cpl_vector_delete(poly);
02774 return rc;
02775 }
02776
02777 double yfitmax = cpl_vector_get(ypeaks, imaxflux);
02778 if (fabs(yposmax - yfitmax) > MUSE_WAVE_LINE_FIT_MAXSHIFT) {
02779 cpl_msg_debug(__func__, "Multi-Gauss fit gave unexpectedly large offset "
02780 "(shifted %.3f pix from y=%.3f..%.3f in column=%d)",
02781 yposmax - yfitmax, yp1, yp2, aX);
02782 cpl_matrix_delete(covariance);
02783 cpl_vector_delete(poly);
02784 return CPL_ERROR_ACCESS_OUT_OF_RANGE;
02785 }
02786
02787 double yfitmin = cpl_vector_get_min(fluxes);
02788 if (yfitmin < 0) {
02789 cpl_msg_debug(__func__, "Multi-Gauss fit gave negative flux (%e in "
02790 "multiplet from y=%.3f..%.3f in column=%d)", yfitmin,
02791 yp1, yp2, aX);
02792 cpl_matrix_delete(covariance);
02793 cpl_vector_delete(poly);
02794 return CPL_ERROR_ILLEGAL_OUTPUT;
02795 }
02796
02797
02798 if (cpl_table_get_nrow(aFitTable) < aRowsNeeded) {
02799 cpl_table_set_size(aFitTable, aRowsNeeded);
02800 }
02801
02802 cpl_table_fill_column_window(aFitTable, "mse", aRowsNeeded - np, np, mse);
02803 cpl_table_fill_column_window(aFitTable, "x", aRowsNeeded - np, np, aX);
02804 cpl_table_fill_column_window(aFitTable, "sigma", aRowsNeeded - np, np, sigma);
02805
02806 for (i = 0, j = aRowsNeeded - np; i < np; i++, j++) {
02807 cpl_table_set(aFitTable, "lambda", j, cpl_vector_get(aLambdas, i));
02808 cpl_table_set(aFitTable, "y", j, cpl_vector_get(ypeaks, i));
02809 double center = cpl_vector_get(ypeaks, i);
02810 cpl_table_set(aFitTable, "center", j, center);
02811
02812
02813 double cerror = sqrt(cpl_matrix_get(covariance, 3 + i, 3 + i));
02814 cpl_table_set(aFitTable, "cerr", j, cerror);
02815 cpl_table_set(aFitTable, "flux", j, cpl_vector_get(fluxes, i));
02816
02817 double bg = cpl_vector_get(poly, 0) + cpl_vector_get(poly, 1) * center;
02818 cpl_table_set(aFitTable, "bg", j, bg);
02819 }
02820 cpl_vector_delete(poly);
02821 cpl_matrix_delete(covariance);
02822 #if 0
02823 printf("stored %d fitted multiplet values:\n", np);
02824 cpl_table_dump(aFitTable, aRowsNeeded - np - 2 < 0 ? 0 : aRowsNeeded - np - 2, np + 5, stdout);
02825 fflush(stdout);
02826 #endif
02827
02828 return rc;
02829 }
02830
02831
02870
02871 cpl_error_code
02872 muse_wave_line_fit_iterate(cpl_table *aFitTable, double aLambda,
02873 const muse_wave_params *aParams)
02874 {
02875 cpl_ensure_code(aFitTable, CPL_ERROR_NULL_INPUT);
02876 int npos = cpl_table_get_nrow(aFitTable);
02877 cpl_ensure_code(npos > 0, CPL_ERROR_ILLEGAL_INPUT);
02878 double rsigma = aParams->linesigma < 0 ? 2.5 : aParams->linesigma;
02879
02880
02881 cpl_table *fittable = NULL;
02882 if (aLambda > 0) {
02883 cpl_table_unselect_all(aFitTable);
02884 cpl_table_or_selected_double(aFitTable, "lambda", CPL_EQUAL_TO, aLambda);
02885 npos = cpl_table_count_selected(aFitTable);
02886 cpl_ensure_code(npos > 0, CPL_ERROR_ILLEGAL_INPUT);
02887 fittable = cpl_table_extract_selected(aFitTable);
02888 cpl_table_erase_selected(aFitTable);
02889 } else {
02890 fittable = aFitTable;
02891 }
02892
02893
02894 cpl_matrix *pos = cpl_matrix_new(1, npos);
02895 cpl_vector *val = cpl_vector_new(npos);
02896 int i;
02897 for (i = 0; i < npos; i++) {
02898 cpl_matrix_set(pos, 0, i, cpl_table_get(fittable, "x", i, NULL));
02899 cpl_vector_set(val, i, cpl_table_get(fittable, "center", i, NULL));
02900 }
02901
02902
02903 cpl_errorstate state = cpl_errorstate_get();
02904 double mse;
02905 cpl_polynomial *fit = muse_utils_iterate_fit_polynomial(pos, val, NULL, fittable,
02906 aParams->xorder, rsigma,
02907 &mse, NULL);
02908 cpl_matrix_delete(pos);
02909 cpl_vector_delete(val);
02910 cpl_polynomial_delete(fit);
02911 if (!cpl_errorstate_is_equal(state)) {
02912
02913
02914 cpl_table_fill_column_window(fittable, "cerr",
02915 0, cpl_table_get_nrow(fittable), 10.);
02916 } else if (aParams->fitweighting == MUSE_WAVE_WEIGHTING_SCATTER) {
02917
02918 cpl_table_fill_column_window(fittable, "cerr",
02919 0, cpl_table_get_nrow(fittable), sqrt(mse));
02920 } else if (aParams->fitweighting == MUSE_WAVE_WEIGHTING_CERRSCATTER) {
02921
02922
02923 cpl_table_power_column(fittable, "cerr", 2.);
02924 cpl_table_add_scalar(fittable, "cerr", mse);
02925 cpl_table_power_column(fittable, "cerr", 0.5);
02926 }
02927 #if 0
02928 printf("line at %.3f Angstrom: rms = %f (mean error %f)\n", aLambda,
02929 sqrt(mse), cpl_table_get_column_mean(fittable, "cerr"));
02930 cpl_table_dump(fittable, 0, npos, stdout);
02931 fflush(stdout);
02932 #endif
02933 if (aLambda > 0) {
02934 cpl_table_insert(aFitTable, fittable, cpl_table_get_nrow(aFitTable));
02935 cpl_table_delete(fittable);
02936 }
02937 return CPL_ERROR_NONE;
02938 }
02939
02940
02952
02953 static inline double
02954 muse_wave_ipow(double x, unsigned int y)
02955 {
02956 if (!y) {
02957 return 1;
02958 }
02959 if (y == 1) {
02960 return x;
02961 }
02962 double x2 = x*x;
02963 if (y == 2) {
02964 return x2;
02965 }
02966 if (y == 3) {
02967 return x2*x;
02968 }
02969 if (y == 4) {
02970 return x2*x2;
02971 }
02972 double result;
02973
02974
02975 result = y & 1 ? x : 1.;
02976 while (y >>= 1) {
02977 x *= x;
02978 if (y & 1) {
02979 result *= x;
02980 }
02981 }
02982 return result;
02983 }
02984
02985
03000
03001 static int
03002 muse_wave_poly_2d(const double xy[], const double p[], double *f)
03003 {
03004 unsigned short xorder = p[0],
03005 yorder = p[1];
03006
03007 *f = 0;
03008 double x = xy[0],
03009 y = xy[1];
03010 unsigned int i, idx = 2;
03011 for (i = 0; i <= xorder; i++) {
03012 double xi = muse_wave_ipow(x, i);
03013 unsigned int j;
03014 for (j = 0; j <= yorder; j++) {
03015 *f += p[idx++] * xi * muse_wave_ipow(y, j);
03016 }
03017 }
03018 return 0;
03019 }
03020
03021
03034
03035 static int
03036 muse_wave_dpoly_2d(const double xy[], const double p[], double f[])
03037 {
03038 unsigned short xorder = p[0],
03039 yorder = p[1];
03040 f[0] = f[1] = 0;
03041
03042 double x = xy[0],
03043 y = xy[1];
03044 unsigned int i, idx = 2;
03045 for (i = 0; i <= xorder; i++) {
03046 double xi = muse_wave_ipow(x, i);
03047 unsigned int j;
03048 for (j = 0; j <= yorder; j++) {
03049 f[idx++] = xi * muse_wave_ipow(y, j);
03050 }
03051 }
03052 return 0;
03053 }
03054
03055
03090
03091 cpl_error_code
03092 muse_wave_poly_fit(cpl_matrix *aXYPos, cpl_vector *aLambdas, cpl_vector *aDLambdas,
03093 cpl_polynomial **aPoly, double *aMSE, muse_wave_params *aParams,
03094 const unsigned short aSlice)
03095 {
03096 cpl_ensure_code(aPoly && aXYPos && aLambdas, CPL_ERROR_NULL_INPUT);
03097 #if 0
03098 printf("aXYPos (%"CPL_SIZE_FORMAT"x%"CPL_SIZE_FORMAT"):\n",
03099 cpl_matrix_get_ncol(aXYPos), cpl_matrix_get_nrow(aXYPos));
03100 cpl_matrix_dump(aXYPos, stdout);
03101 printf("aLambdas (%"CPL_SIZE_FORMAT"):\n", cpl_vector_get_size(aLambdas));
03102 cpl_vector_dump(aLambdas, stdout);
03103 printf("aDLambdas (%"CPL_SIZE_FORMAT"):\n", cpl_vector_get_size(aDLambdas));
03104 cpl_vector_dump(aDLambdas, stdout);
03105 fflush(stdout);
03106 #endif
03107 double rsigma = aParams->fitsigma < 0 ? 3.0 : aParams->fitsigma;
03108 int debug = getenv("MUSE_DEBUG_WAVECAL")
03109 ? atoi(getenv("MUSE_DEBUG_WAVECAL")) : 0;
03110
03111 *aPoly = cpl_polynomial_new(2);
03112
03113 double rms = -1;
03114 int large_residuals = 1,
03115 niter = 1;
03116 while (large_residuals > 0) {
03117
03118 cpl_error_code errfit = CPL_ERROR_SINGULAR_MATRIX;
03119
03120 if (aDLambdas) {
03121
03122
03123 cpl_matrix *xypos = cpl_matrix_transpose_create(aXYPos);
03124
03125 int npar = 2 + (aParams->xorder + 1) * (aParams->yorder + 1);
03126 cpl_vector *pars = cpl_vector_new(npar);
03127
03128 cpl_vector_fill(pars, 0.);
03129
03130 cpl_vector_set(pars, 0, aParams->xorder);
03131 cpl_vector_set(pars, 1, aParams->yorder);
03132 cpl_vector_set(pars, 2, 5000.);
03133 cpl_array *aflags = cpl_array_new(npar, CPL_TYPE_INT);
03134
03135 cpl_array_set_int(aflags, 0, 0);
03136 cpl_array_set_int(aflags, 1, 0);
03137 cpl_array_fill_window_int(aflags, 2, npar - 2, 1);
03138 int *pflags = cpl_array_get_data_int(aflags);
03139 errfit = cpl_fit_lvmq(xypos, NULL,
03140 aLambdas, aDLambdas, pars, pflags,
03141 muse_wave_poly_2d, muse_wave_dpoly_2d,
03142 CPL_FIT_LVMQ_TOLERANCE, CPL_FIT_LVMQ_COUNT,
03143 CPL_FIT_LVMQ_MAXITER, NULL, NULL, NULL);
03144 cpl_array_delete(aflags);
03145 cpl_size k = 2;
03146 unsigned short i;
03147 for (i = 0; i <= aParams->xorder; i++) {
03148 unsigned short j;
03149 for (j = 0; j <= aParams->yorder; j++) {
03150 cpl_size pows[2] = { i, j };
03151 cpl_polynomial_set_coeff(*aPoly, pows,
03152 cpl_vector_get(pars, k++));
03153 }
03154 }
03155 cpl_matrix_delete(xypos);
03156 cpl_vector_delete(pars);
03157 } else {
03158
03159 const cpl_boolean sym = CPL_FALSE;
03160 const cpl_size mindeg2d[] = { 0, 0 },
03161 maxdeg2d[] = { aParams->xorder, aParams->yorder };
03162 errfit = cpl_polynomial_fit(*aPoly, aXYPos, &sym, aLambdas, NULL,
03163 CPL_TRUE, mindeg2d, maxdeg2d);
03164 }
03165 #if 0
03166 printf("polynomial (orders=%hu/%hu, degree=%"CPL_SIZE_FORMAT"):\n",
03167 aParams->xorder, aParams->yorder, cpl_polynomial_get_degree(*aPoly));
03168 cpl_polynomial_dump(*aPoly, stdout);
03169 fflush(stdout);
03170 #endif
03171
03172 if (errfit) {
03173
03174 cpl_msg_error(__func__, "The polynomial fit in slice %hu failed: %s",
03175 aSlice, cpl_error_get_message());
03176
03177 #if 0
03178
03179
03180 FILE *gp = popen("gnuplot -persist", "w");
03181 if (gp) {
03182
03183 fprintf(gp, "set title \"2D polynomial fit residuals (failed fit: "
03184 "\'%s\')\n", cpl_error_get_message());
03185
03186 fprintf(gp, "set palette defined ( 0 \"dark-violet\","
03187 "1 \"dark-blue\", 4 \"green\", 6 \"yellow\", 8 \"orange\","
03188 "9 \"red\", 10 \"dark-red\")\n");
03189 fprintf(gp, "unset key\n");
03190 cpl_matrix *xpos = cpl_matrix_extract_row(aXYPos, 0);
03191 fprintf(gp, "set xrange [%f:%f]\n", cpl_matrix_get_min(xpos),
03192 cpl_matrix_get_max(xpos));
03193 cpl_matrix_delete(xpos);
03194 fprintf(gp, "set cbrange [%f:%f]\n", cpl_vector_get_min(aLambdas),
03195 cpl_vector_get_max(aLambdas));
03196 fprintf(gp, "plot \"-\" w p pal\n");
03197
03198 printf("# X Y lambda\n");
03199 int n;
03200 for (n = 0; n < cpl_vector_get_size(aLambdas); n++) {
03201 printf("%4d %7.2f %8.3f\n", (int)cpl_matrix_get(aXYPos, 0, n),
03202 cpl_matrix_get(aXYPos, 1, n), cpl_vector_get(aLambdas, n));
03203 fprintf(gp, "%f %f %f\n", cpl_matrix_get(aXYPos, 0, n),
03204 cpl_matrix_get(aXYPos, 1, n), cpl_vector_get(aLambdas, n));
03205 }
03206 fflush(stdout);
03207 fprintf(gp, "EOF\n");
03208 pclose(gp);
03209 }
03210 #endif
03211
03212
03213 cpl_polynomial_delete(*aPoly);
03214 *aPoly = NULL;
03215 return CPL_ERROR_ILLEGAL_OUTPUT;
03216 }
03217
03218 int npoints = cpl_vector_get_size(aLambdas);
03219 cpl_vector *res = cpl_vector_new(npoints);
03220
03221
03222 cpl_vector_fill_polynomial_fit_residual(res, aLambdas, aDLambdas, *aPoly,
03223 aXYPos, NULL);
03224
03225 double mse = 0;
03226
03227 if (aDLambdas) {
03228 double wsum = 0;
03229 int i;
03230 for (i = 0; i < npoints; i++) {
03231 double weight = 1. / cpl_vector_get(aDLambdas, i);
03232 mse += pow(cpl_vector_get(res, i), 2) * weight;
03233 wsum += weight;
03234 }
03235 mse /= wsum;
03236 } else {
03237 mse = cpl_vector_product(res, res) / npoints;
03238 }
03239 double rlimit = rsigma * sqrt(mse);
03240 if (debug) {
03241 printf("Resulting 2D polynomial of slice %hu (%d points, RMS = %f dRMS = "
03242 "%f), %.1f-sigma limit now %f (target RMS = %f):\n", aSlice,
03243 npoints, sqrt(mse), rms < 0 ? 0. : rms - sqrt(mse), rsigma, rlimit,
03244 aParams->targetrms);
03245 if (debug > 1) {
03246 cpl_polynomial_dump(*aPoly, stdout);
03247 }
03248 fflush(stdout);
03249 }
03250 #if 0
03251
03252
03253 cpl_plot_vector("set title \"res\"\n", "", "", res);
03254 #endif
03255 if (aParams->rflag) {
03256 int nnew = cpl_vector_get_size(res),
03257 nrow = aParams->residuals ? cpl_table_get_nrow(aParams->residuals) : 0;
03258
03259 if (!aParams->residuals) {
03260 aParams->residuals = muse_cpltable_new(muse_wavedebug_def, nnew);
03261 cpl_table_set_column_savetype(aParams->residuals, "slice", CPL_TYPE_UCHAR);
03262 } else {
03263 cpl_table_set_size(aParams->residuals, nrow + nnew);
03264 }
03265 cpl_table_fill_column_window_int(aParams->residuals, "slice",
03266 nrow, nnew, aSlice);
03267 cpl_table_fill_column_window_int(aParams->residuals, "iteration",
03268 nrow, nnew, niter);
03269 cpl_table_fill_column_window_double(aParams->residuals, "rejlimit",
03270 nrow, nnew, rlimit);
03271 int n;
03272 for (n = 0; n < cpl_vector_get_size(res); n++) {
03273 cpl_table_set_int(aParams->residuals, "x", nrow + n,
03274 (int)cpl_matrix_get(aXYPos, 0, n));
03275 cpl_table_set_float(aParams->residuals, "y", nrow + n,
03276 cpl_matrix_get(aXYPos, 1, n));
03277 cpl_table_set_float(aParams->residuals, "lambda", nrow + n,
03278 cpl_vector_get(aLambdas, n));
03279 cpl_table_set_double(aParams->residuals, "residual", nrow + n,
03280 cpl_vector_get(res, n));
03281 }
03282 if (debug) {
03283 cpl_msg_debug(__func__, "%"CPL_SIZE_FORMAT" entries in residuals table "
03284 "after iteration %d of slice %hu",
03285 cpl_table_get_nrow(aParams->residuals), niter, aSlice);
03286 }
03287 }
03288 niter++;
03289
03290
03291
03292 cpl_boolean isgoodenough = (rms < 0 ? CPL_FALSE : rms - sqrt(mse) < 0.001)
03293 || (sqrt(mse) <= aParams->targetrms);
03294 large_residuals = 0;
03295 int i;
03296 for (i = 0; !isgoodenough && i < cpl_vector_get_size(res); i++) {
03297
03298 if (fabs(cpl_vector_get(res, i)) < rlimit) {
03299
03300 continue;
03301 }
03302
03303 #if 0
03304 cpl_msg_debug(__func__, "residual = %f (position %fx%f, lambda=%f+/-%f)",
03305 cpl_vector_get(res, i),
03306 cpl_matrix_get(aXYPos, 0, i), cpl_matrix_get(aXYPos, 1, i),
03307 cpl_vector_get(aLambdas, i),
03308 aDLambdas ? cpl_vector_get(aDLambdas, i) : 1.);
03309 #endif
03310 if (cpl_vector_get_size(res) == 1) {
03311 cpl_msg_debug(__func__, "trying to remove the last vector/matrix "
03312 "element when checking against fit sigma (slice %hu)",
03313 aSlice);
03314 break;
03315 }
03316
03317 muse_cplvector_erase_element(res, i);
03318 cpl_matrix_erase_columns(aXYPos, i, 1);
03319 muse_cplvector_erase_element(aLambdas, i);
03320 if (aDLambdas) {
03321 muse_cplvector_erase_element(aDLambdas, i);
03322 }
03323
03324 large_residuals++;
03325 i--;
03326 }
03327
03328 rms = sqrt(mse);
03329 if (!large_residuals) {
03330
03331
03332
03333 if (aMSE) {
03334 *aMSE = mse;
03335 }
03336 }
03337
03338 cpl_vector_delete(res);
03339 }
03340
03341 return CPL_ERROR_NONE;
03342 }
03343
03344
03354
03355 cpl_table *
03356 muse_wave_table_create(const unsigned short aNSlices, const unsigned short aXOrder,
03357 const unsigned short aYOrder)
03358 {
03359 cpl_table *table = cpl_table_new(aNSlices);
03360 cpl_ensure(table, CPL_ERROR_UNSPECIFIED, NULL);
03361
03362
03363 cpl_table_new_column(table, MUSE_WAVECAL_TABLE_COL_SLICE_NO, CPL_TYPE_INT);
03364 cpl_table_set_column_unit(table, MUSE_WAVECAL_TABLE_COL_SLICE_NO, "No");
03365 cpl_table_set_column_format(table, MUSE_WAVECAL_TABLE_COL_SLICE_NO, "%2d");
03366
03367
03368 unsigned short i;
03369 for (i = 0; i <= aXOrder; i++) {
03370 unsigned short j;
03371 for (j = 0; j <= aYOrder; j++) {
03372
03373 char *colname = cpl_sprintf(MUSE_WAVECAL_TABLE_COL_COEFF, i, j);
03374 cpl_table_new_column(table, colname, CPL_TYPE_DOUBLE);
03375
03376 cpl_table_set_column_unit(table, colname, "Angstrom");
03377 cpl_table_set_column_format(table, colname, "%12.5e");
03378 cpl_free(colname);
03379 }
03380 }
03381
03382 cpl_table_new_column(table, MUSE_WAVECAL_TABLE_COL_MSE, CPL_TYPE_DOUBLE);
03383 cpl_table_set_column_format(table, MUSE_WAVECAL_TABLE_COL_MSE, "%f");
03384
03385 return table;
03386 }
03387
03388
03402
03403 cpl_error_code
03404 muse_wave_table_add_poly(cpl_table *aTable, cpl_polynomial *aPoly,
03405 double aMSE, unsigned short aXOrder,
03406 unsigned short aYOrder, const unsigned short aRow)
03407 {
03408 cpl_ensure_code(aTable && aPoly, CPL_ERROR_NULL_INPUT);
03409 cpl_ensure_code(cpl_polynomial_get_dimension(aPoly) == 2,
03410 CPL_ERROR_ILLEGAL_INPUT);
03411
03412
03413 cpl_table_set_int(aTable, MUSE_WAVECAL_TABLE_COL_SLICE_NO, aRow, aRow + 1);
03414 cpl_table_set_double(aTable, MUSE_WAVECAL_TABLE_COL_MSE, aRow, aMSE);
03415
03416
03417 unsigned short i;
03418 for (i = 0; i <= aXOrder; i++) {
03419 unsigned short j;
03420 for (j = 0; j <= aYOrder; j++) {
03421 cpl_size pows[2] = { i, j };
03422
03423 char *colname = cpl_sprintf(MUSE_WAVECAL_TABLE_COL_COEFF, i, j);
03424 cpl_error_code rc = cpl_table_set_double(aTable, colname, aRow,
03425 cpl_polynomial_get_coeff(aPoly,
03426 pows));
03427 if (rc != CPL_ERROR_NONE) {
03428 cpl_msg_warning(__func__, "Problem writing %f to field %s in "
03429 "wavelength table: %s",
03430 cpl_polynomial_get_coeff(aPoly, pows), colname,
03431 cpl_error_get_message());
03432 cpl_polynomial_dump(aPoly, stdout);
03433 cpl_table_dump(aTable, aRow, 1, stdout);
03434 fflush(stdout);
03435 }
03436 cpl_free(colname);
03437 }
03438 }
03439
03440 return CPL_ERROR_NONE;
03441 }
03442
03443
03458
03459 cpl_error_code
03460 muse_wave_table_get_orders(const cpl_table *aWave, unsigned short *aXOrder,
03461 unsigned short *aYOrder)
03462 {
03463 cpl_ensure_code(aWave && aXOrder && aYOrder, CPL_ERROR_NULL_INPUT);
03464 cpl_array *cols = cpl_table_get_column_names(aWave);
03465
03466
03467 const char *highcol = cpl_array_get_string(cols, cpl_array_get_size(cols) - 2);
03468 char *colname = cpl_strdup(highcol);
03469 cpl_array_delete(cols);
03470
03471
03472 *aYOrder = atoi(colname+4);
03473
03474 colname[4] = '\0';
03475 *aXOrder = atoi(colname+3);
03476 cpl_free(colname);
03477
03478 return CPL_ERROR_NONE;
03479 }
03480
03481
03502
03503 cpl_polynomial *
03504 muse_wave_table_get_poly_for_slice(const cpl_table *aTable,
03505 const unsigned short aSlice)
03506 {
03507 cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, NULL);
03508 cpl_ensure(aSlice >= 1 && aSlice <= kMuseSlicesPerCCD,
03509 CPL_ERROR_ILLEGAL_INPUT, NULL);
03510
03511
03512 int irow, nrow = cpl_table_get_nrow(aTable);
03513 for (irow = 0; irow < nrow; irow++) {
03514 int err;
03515 unsigned short slice = cpl_table_get_int(aTable,
03516 MUSE_WAVECAL_TABLE_COL_SLICE_NO,
03517 irow, &err);
03518 if (slice == aSlice && !err) {
03519 break;
03520 }
03521 }
03522 cpl_ensure(irow < nrow, CPL_ERROR_DATA_NOT_FOUND, NULL);
03523
03524 cpl_polynomial *pwave = cpl_polynomial_new(2);
03525 char colname[15];
03526 unsigned short wavexorder, waveyorder;
03527 muse_wave_table_get_orders(aTable, &wavexorder, &waveyorder);
03528 unsigned short l;
03529 for (l = 0; l <= wavexorder; l++) {
03530 unsigned short k;
03531 for (k = 0; k <= waveyorder; k++) {
03532 cpl_size pows[2] = { l, k };
03533 sprintf(colname, MUSE_WAVECAL_TABLE_COL_COEFF, l, k);
03534 int err;
03535 cpl_polynomial_set_coeff(pwave, pows,
03536 cpl_table_get_double(aTable, colname, irow,
03537 &err));
03538 if (err != 0) {
03539 cpl_polynomial_delete(pwave);
03540 cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT, "Wavelength "
03541 "calibration table broken in slice %hu (row index"
03542 " %d) column %s", aSlice, irow, colname);
03543 return NULL;
03544 }
03545 }
03546 }
03547 return pwave;
03548 }
03549
03550
03573
03574 cpl_image *
03575 muse_wave_map(muse_image *aImage, const cpl_table *aWave,
03576 const cpl_table *aTrace)
03577 {
03578 cpl_ensure(aImage && aWave && aTrace, CPL_ERROR_NULL_INPUT, NULL);
03579 int nx = cpl_image_get_size_x(aImage->data),
03580 ny = cpl_image_get_size_y(aImage->data);
03581 cpl_image *wavemap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
03582 cpl_ensure(wavemap, cpl_error_get_code(), NULL);
03583 unsigned char ifu = muse_utils_get_ifu(aImage->header);
03584
03585
03586 float *wdata = cpl_image_get_data_float(wavemap);
03587
03588 unsigned short wavexorder, waveyorder;
03589 muse_wave_table_get_orders(aWave, &wavexorder, &waveyorder);
03590 cpl_msg_debug(__func__, "Order for trace solution is %d, for wavelength "
03591 "solution %hu/%hu, IFU %hhu", muse_trace_table_get_order(aTrace),
03592 wavexorder, waveyorder, ifu);
03593
03594
03595 unsigned short islice;
03596 for (islice = 0; islice < kMuseSlicesPerCCD; islice++) {
03597 #if 0
03598 cpl_msg_debug(__func__, "Starting to process slice %d of IFU %hhu",
03599 (int)islice + 1, ifu);
03600 #endif
03601
03602
03603 cpl_polynomial *pwave = muse_wave_table_get_poly_for_slice(aWave, islice + 1);
03604
03605
03606 cpl_vector *pos = cpl_vector_new(2);
03607
03608
03609 cpl_polynomial **ptrace = muse_trace_table_get_polys_for_slice(aTrace,
03610 islice + 1);
03611 if (!ptrace) {
03612 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: tracing polynomials "
03613 "missing!", (int)islice + 1, ifu);
03614 continue;
03615 }
03616 #if 0
03617 printf("polynomials for slice %d:\n", (int)islice + 1);
03618 cpl_polynomial_dump(ptrace[MUSE_TRACE_LEFT], stdout);
03619 cpl_polynomial_dump(ptrace[MUSE_TRACE_RIGHT], stdout);
03620 cpl_polynomial_dump(pwave, stdout);
03621 fflush(stdout);
03622 #endif
03623
03624
03625 int j;
03626 for (j = 1; j <= ny; j++) {
03627
03628
03629 int ileft = ceil(cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT],
03630 j, NULL)),
03631 iright = floor(cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT],
03632 j, NULL));
03633
03634 if (ileft < 1 || iright > nx || ileft > iright) {
03635 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: faulty polynomial "
03636 "detected at y=%d", (int)islice + 1, ifu, j);
03637 break;
03638 }
03639 cpl_vector_set(pos, 1, j);
03640
03641
03642 int i;
03643 for (i = ileft; i <= iright; i++) {
03644 cpl_vector_set(pos, 0, i);
03645
03646
03647 wdata[(i-1) + (j-1)*nx] = cpl_polynomial_eval(pwave, pos);
03648 }
03649 }
03650
03651
03652 muse_trace_polys_delete(ptrace);
03653 cpl_polynomial_delete(pwave);
03654 cpl_vector_delete(pos);
03655 }
03656
03657 return wavemap;
03658 }
03659
03660
03685
03686 cpl_error_code
03687 muse_wave_plot_residuals(cpl_table *aTable, unsigned char aIFU,
03688 unsigned short aSlice, unsigned int aIter,
03689 cpl_boolean aPlotLambda, cpl_vector *aCuts)
03690 {
03691 #if HAVE_POPEN && HAVE_PCLOSE
03692 cpl_ensure_code(aTable, CPL_ERROR_NULL_INPUT);
03693 cpl_error_code rc = muse_cpltable_check(aTable, muse_wavedebug_def);
03694 cpl_ensure_code(rc == CPL_ERROR_NONE, rc);
03695
03696 FILE *gp = popen("gnuplot", "w");
03697 if (!gp) {
03698 return CPL_ERROR_ASSIGNING_STREAM;
03699 }
03700
03701
03702 cpl_table_unselect_all(aTable);
03703
03704 int n, nrow = cpl_table_get_nrow(aTable),
03705 error = 0;
03706 if (aSlice == 0) {
03707 printf("Selecting data of all slices");
03708 if (aIFU > 0) {
03709 printf(" of IFU %hhu", aIFU);
03710 }
03711 printf(".\n");
03712
03713 const int *slice = cpl_table_get_data_int_const(aTable, "slice"),
03714 *iter = cpl_table_get_data_int_const(aTable, "iteration");
03715 if (!aIter) {
03716 fprintf(stderr, "Selecting data of last iteration of all slices\n");
03717
03718 int sliceno = slice[nrow - 1],
03719 iterlast = iter[nrow - 1];
03720 for (n = nrow - 2; n >= 0; n--) {
03721 if (slice[n] == sliceno && iter[n] != iterlast) {
03722 cpl_table_select_row(aTable, n);
03723 }
03724 if (slice[n] != sliceno) {
03725 sliceno = slice[n];
03726 iterlast = iter[n];
03727 }
03728 }
03729 cpl_table_erase_selected(aTable);
03730
03731 fprintf(gp, "set title \"");
03732 if (aIFU > 0) {
03733 fprintf(gp, "IFU %hhu, ", aIFU);
03734 }
03735 fprintf(gp, "slices %d..%d, iterations %d..%d: 2D polynomial fit "
03736 "residuals (limits %f..%f)\n",
03737 (int)cpl_table_get_column_min(aTable, "slice"),
03738 (int)cpl_table_get_column_max(aTable, "slice"),
03739 (int)cpl_table_get_column_min(aTable, "iteration"),
03740 (int)cpl_table_get_column_max(aTable, "iteration"),
03741 cpl_table_get_column_min(aTable, "rejlimit"),
03742 cpl_table_get_column_max(aTable, "rejlimit"));
03743 } else {
03744 printf("Selecting data of iteration %d.\n", aIter);
03745 for (n = 0; n < nrow; n++) {
03746 if (iter[n] != (int)aIter) {
03747 cpl_table_select_row(aTable, n);
03748 }
03749 }
03750 cpl_table_erase_selected(aTable);
03751
03752 fprintf(gp, "set title \"");
03753 if (aIFU > 0) {
03754 fprintf(gp, "IFU %hhu, ", aIFU);
03755 }
03756 fprintf(gp, "slices %d..%d, iteration %d: 2D polynomial fit residuals "
03757 "(limits %f..%f)\n",
03758 (int)cpl_table_get_column_min(aTable, "slice"),
03759 (int)cpl_table_get_column_max(aTable, "slice"), aIter,
03760 cpl_table_get_column_min(aTable, "rejlimit"),
03761 cpl_table_get_column_max(aTable, "rejlimit"));
03762 }
03763 } else {
03764 printf("Selecting data of ");
03765 if (aIFU > 0) {
03766 printf("IFU %hhu ", aIFU);
03767 }
03768 printf("slice %hu.\n", aSlice);
03769 const int *slice = cpl_table_get_data_int_const(aTable, "slice");
03770 for (n = 0; n < nrow; n++) {
03771 if (slice[n] != aSlice) {
03772 cpl_table_select_row(aTable, n);
03773 }
03774 }
03775 cpl_table_erase_selected(aTable);
03776 nrow = cpl_table_get_nrow(aTable);
03777 cpl_table_unselect_all(aTable);
03778
03779 const int *iter = cpl_table_get_data_int_const(aTable, "iteration");
03780 if (!aIter) {
03781
03782 aIter = iter[nrow - 1];
03783 }
03784 printf("Selecting data of iteration %d.\n", aIter);
03785 for (n = 0; n < nrow; n++) {
03786 if (iter[n] != (int)aIter) {
03787 cpl_table_select_row(aTable, n);
03788 }
03789 }
03790 cpl_table_erase_selected(aTable);
03791
03792
03793 fprintf(gp, "set title \"");
03794 if (aIFU > 0) {
03795 fprintf(gp, "IFU %hhu, ", aIFU);
03796 }
03797 fprintf(gp, "slice %hu, iteration %d: 2D polynomial fit residuals "
03798 "(limit=%f)\n", aSlice, aIter,
03799 cpl_table_get_double(aTable, "rejlimit", 0, &error));
03800 }
03801
03802
03803 nrow = cpl_table_get_nrow(aTable);
03804 cpl_ensure_code(nrow > 0, CPL_ERROR_DATA_NOT_FOUND);
03805 printf("Plotting %d points.\n", nrow);
03806 const int *x = cpl_table_get_data_int_const(aTable, "x");
03807 const float *y = cpl_table_get_data_float_const(aTable, "y"),
03808 *lambda = cpl_table_get_data_float_const(aTable, "lambda");
03809 const double *r = cpl_table_get_data_double_const(aTable, "residual");
03810
03811
03812
03813 int xmin = cpl_table_get_column_min(aTable, "x") - 2,
03814 xmax = cpl_table_get_column_max(aTable, "x") + 2;
03815 float ymin = cpl_table_get_column_min(aTable, "y") - 2,
03816 ymax = cpl_table_get_column_max(aTable, "y") + 2,
03817 lmin = cpl_table_get_column_min(aTable, "lambda") - 2,
03818 lmax = cpl_table_get_column_max(aTable, "lambda") + 2;
03819 double rmin = cpl_table_get_column_min(aTable, "residual"),
03820 rmax = cpl_table_get_column_max(aTable, "residual");
03821 if (aCuts && cpl_vector_get_size(aCuts) == 2) {
03822 rmin = cpl_vector_get(aCuts, 0);
03823 rmax = cpl_vector_get(aCuts, 1);
03824 }
03825
03826
03827 fprintf(gp, "set palette defined ( 0 \"dark-violet\","
03828 "1 \"dark-blue\", 4 \"green\", 6 \"yellow\", 8 \"orange\","
03829 "9 \"red\", 10 \"dark-red\")\n");
03830 fprintf(gp, "unset key\n");
03831 printf("Setting plotting limits: [%d:%d][%.2f:%.2f][%.4f:%.4f]\n",
03832 xmin, xmax, aPlotLambda ? lmin : ymin, aPlotLambda ? lmax : ymax,
03833 rmin, rmax);
03834 fprintf(gp, "set xrange [%d:%d]\n", xmin, xmax);
03835 if (aPlotLambda) {
03836 fprintf(gp, "set yrange [%f:%f]\n", lmin, lmax);
03837 } else {
03838 fprintf(gp, "set yrange [%f:%f]\n", ymin, ymax);
03839 }
03840 fprintf(gp, "set cbrange [%f:%f]\n", rmin, rmax);
03841 fprintf(gp, "set view map\n");
03842 fprintf(gp, "splot \"-\" w p pal\n");
03843 for (n = 0; n < nrow; n++) {
03844 if (aPlotLambda) {
03845 fprintf(gp, "%d %f %e\n", x[n], lambda[n], r[n]);
03846 } else {
03847 fprintf(gp, "%d %f %e\n", x[n], y[n], r[n]);
03848 }
03849 }
03850 fprintf(gp, "EOF\n");
03851 fflush(gp);
03852
03853
03854
03855 printf("Press ENTER to end program and close plot\n");
03856 getchar();
03857 pclose(gp);
03858 return CPL_ERROR_NONE;
03859 #else
03860 return CPL_ERROR_UNSUPPORTED_MODE;
03861 #endif
03862 }
03863
03864
03891
03892 cpl_error_code
03893 muse_wave_plot_column(cpl_table *aCTable, cpl_table *aRTable,
03894 unsigned char aIFU, unsigned short aSlice,
03895 unsigned int aColumn, unsigned int aIter,
03896 cpl_boolean aPlotRes)
03897 {
03898 #if HAVE_POPEN && HAVE_PCLOSE
03899 cpl_ensure_code(aCTable && aRTable, CPL_ERROR_NULL_INPUT);
03900 cpl_error_code rc = muse_cpltable_check(aRTable, muse_wavedebug_def);
03901 cpl_ensure_code(rc == CPL_ERROR_NONE, rc);
03902
03903 unsigned short xorder, yorder;
03904 muse_wave_table_get_orders(aCTable, &xorder, &yorder);
03905 cpl_ensure_code(xorder > 0 && yorder > 0, CPL_ERROR_ILLEGAL_INPUT);
03906 cpl_ensure_code(aSlice >= 1 && aSlice <= kMuseSlicesPerCCD,
03907 CPL_ERROR_ACCESS_OUT_OF_RANGE);
03908
03909 FILE *gp = popen("gnuplot", "w");
03910 if (!gp) {
03911 return CPL_ERROR_ASSIGNING_STREAM;
03912 }
03913
03914
03915 cpl_table_unselect_all(aRTable);
03916
03917 printf("Selecting data of ");
03918 if (aIFU > 0) {
03919 printf("IFU %hhu ", aIFU);
03920 }
03921 printf("slice %hu.\n", aSlice);
03922 const int *slice = cpl_table_get_data_int_const(aRTable, "slice");
03923 int n, nrow = cpl_table_get_nrow(aRTable);
03924 for (n = 0; n < nrow; n++) {
03925 if (slice[n] != aSlice) {
03926 cpl_table_select_row(aRTable, n);
03927 }
03928 }
03929 cpl_table_erase_selected(aRTable);
03930 nrow = cpl_table_get_nrow(aRTable);
03931 cpl_ensure_code(nrow > 0, CPL_ERROR_DATA_NOT_FOUND);
03932 cpl_table_unselect_all(aRTable);
03933
03934 const int *iter = cpl_table_get_data_int_const(aRTable, "iteration");
03935 if (!aIter) {
03936
03937 aIter = iter[nrow - 1];
03938 }
03939 printf("Selecting data of iteration %d.\n", aIter);
03940 for (n = 0; n < nrow; n++) {
03941 if (iter[n] != (int)aIter) {
03942 cpl_table_select_row(aRTable, n);
03943 }
03944 }
03945 cpl_table_erase_selected(aRTable);
03946 nrow = cpl_table_get_nrow(aRTable);
03947 cpl_ensure_code(nrow > 0, CPL_ERROR_DATA_NOT_FOUND);
03948 cpl_table_unselect_all(aRTable);
03949
03950 unsigned int col1 = cpl_table_get_column_min(aRTable, "x"),
03951 col2 = cpl_table_get_column_max(aRTable, "x");
03952 if (aColumn > 0) {
03953 col1 = col2 = aColumn;
03954 } else if (aColumn > (unsigned)kMuseOutputXRight) {
03955
03956 col1 = col2 = (col1 + col2) / 2;
03957 }
03958 printf("Plotting data of columns %u..%u.\n", col1, col2);
03959
03960
03961 float ymin = cpl_table_get_column_min(aRTable, "y") - 10,
03962 ymax = cpl_table_get_column_max(aRTable, "y") + 10,
03963 lmin = cpl_table_get_column_min(aRTable, "lambda") - 10,
03964 lmax = cpl_table_get_column_max(aRTable, "lambda") + 10;
03965 double rmin = cpl_table_get_column_min(aRTable, "residual") * 1.03,
03966 rmax = cpl_table_get_column_max(aRTable, "residual") * 1.03;
03967
03968 fprintf(gp, "set title \"");
03969 if (aIFU > 0) {
03970 fprintf(gp, "IFU %hhu, ", aIFU);
03971 }
03972 fprintf(gp, "slice %hu, iteration %d, column %u..%u: polynomial and ", aSlice,
03973 aIter, col1, col2);
03974 printf("Setting plotting limits: ");
03975 if (aPlotRes) {
03976 fprintf(gp, "residuals (limit=%f)\"\n",
03977 cpl_table_get_double(aRTable, "rejlimit", 0, NULL));
03978 printf("[%.2f:%.2f][%.4f:%.4f]\n", lmin, lmax, rmin, rmax);
03979 fprintf(gp, "set xrange [%f:%f]\n", lmin, lmax);
03980 fprintf(gp, "set yrange [%f:%f]\n", rmin, rmax);
03981 fprintf(gp, "set xlabel \"Wavelength [Angstrom]\"\n");
03982 fprintf(gp, "set ylabel \"Residuals [Angstrom]\"\n");
03983 } else {
03984 fprintf(gp, "arc line positions\"\n");
03985 printf("[%.2f:%.2f][%.2f:%.2f]\n", ymin, ymax, lmin, lmax);
03986 fprintf(gp, "set xrange [%g:%g]\n", ymin, ymax);
03987 fprintf(gp, "set yrange [%f:%f]\n", lmin, lmax);
03988 fprintf(gp, "set xlabel \"y-position [pix]\"\n");
03989 fprintf(gp, "set ylabel \"Wavelength [Angstrom]\"\n");
03990 }
03991 fprintf(gp, "set key outside below\n");
03992 fprintf(gp, "set samples 1000\n");
03993
03994
03995 fprintf(gp, "p(x,y) = 0 ");
03996 if (!aPlotRes) {
03997 unsigned short i;
03998 for (i = 0; i <= xorder; i++) {
03999 unsigned short j;
04000 for (j = 0; j <= yorder; j++) {
04001 char *coeff = cpl_sprintf(MUSE_WAVECAL_TABLE_COL_COEFF, i, j);
04002 double cvalue = cpl_table_get(aCTable, coeff, aSlice - 1, NULL);
04003 cpl_free(coeff);
04004 fprintf(gp, " + (%g) * x**(%hu) * y**(%hu)", cvalue, i, j);
04005 }
04006 }
04007 }
04008 fprintf(gp, "\n");
04009
04010 const int *x = cpl_table_get_data_int_const(aRTable, "x");
04011 const float *y = cpl_table_get_data_float_const(aRTable, "y"),
04012 *lambda = cpl_table_get_data_float_const(aRTable, "lambda");
04013 const double *r = cpl_table_get_data_double_const(aRTable, "residual");
04014
04015
04016 double dcol = (col2 - col1) / 255.;
04017 if (dcol == 0.) {
04018 dcol = 1.;
04019 }
04020
04021 fprintf(gp, "plot ");
04022 if (aPlotRes) {
04023 fprintf(gp, "0 t \"\", ");
04024 }
04025 unsigned int ncol, npoints = 0;
04026 for (ncol = col1; ncol <= col2; ncol++) {
04027
04028 int red = (ncol - col1) / dcol,
04029 grn = (col2 - ncol) / dcol,
04030 blu = 0;
04031 if (aPlotRes) {
04032 fprintf(gp, "\"-\" u 2:3 t \"col %u\" w p ps 0.8 lt rgb \"#%02x%02x%02x\"",
04033 ncol, red, grn, blu);
04034 } else {
04035
04036 fprintf(gp, "p(%u, x) t \"\" w l lw 0.7 lt rgb \"#%02x%02x%02x\", "
04037 "\"-\" u 1:(p(%u,$1)+$3) t \"col %u\" w p ps 0.8 lt rgb \"#%02x%02x%02x\"",
04038 ncol, red, grn, blu, ncol, ncol, red, grn, blu);
04039 }
04040 if (ncol == col2) {
04041 fprintf(gp, "\n");
04042 } else {
04043 fprintf(gp, ", ");
04044 }
04045 }
04046 for (ncol = col1; ncol <= col2; ncol++) {
04047 for (n = 0; n < nrow; n++) {
04048 if (x[n] == (int)ncol) {
04049 fprintf(gp, "%f %f %g\n", y[n], lambda[n], r[n]);
04050 npoints++;
04051 }
04052 }
04053 fprintf(gp, "EOF\n");
04054 }
04055 printf("Plotted %u points.\n", npoints);
04056 fflush(gp);
04057
04058
04059
04060 printf("Press ENTER to end program and close plot\n");
04061 getchar();
04062 pclose(gp);
04063 return CPL_ERROR_NONE;
04064 #else
04065 return CPL_ERROR_UNSUPPORTED_MODE;
04066 #endif
04067 }
04068