00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifdef HAVE_CONFIG_H
00023 #include <config.h>
00024 #endif
00025
00026
00027
00028
00029 #include <stdio.h>
00030 #include <float.h>
00031 #include <math.h>
00032 #include <string.h>
00033 #include <cpl.h>
00034 #include <muse.h>
00035 #include "muse_flat_z.h"
00036
00037
00038
00039
00040
00041
00047
00048 static void
00049 muse_flat_qc_header(muse_image *aImage, muse_imagelist *aList)
00050 {
00051 cpl_msg_debug(__func__, "Adding QC keywords");
00052
00053 unsigned stats = CPL_STATS_MEDIAN | CPL_STATS_MEAN | CPL_STATS_STDEV
00054 | CPL_STATS_MIN | CPL_STATS_MAX;
00055
00056
00057 unsigned int k;
00058 for (k = 0; k < muse_imagelist_get_size(aList); k++) {
00059 char *keyword = cpl_sprintf(QC_FLAT_PREFIXi, k+1);
00060 muse_basicproc_stats_append_header(muse_imagelist_get(aList, k)->data,
00061 aImage->header, keyword, stats);
00062 cpl_free(keyword);
00063 keyword = cpl_sprintf(QC_FLAT_PREFIXi" "QC_BASIC_NSATURATED, k+1);
00064 int nsaturated = cpl_propertylist_get_int(muse_imagelist_get(aList, k)->header,
00065 MUSE_HDR_TMP_NSAT);
00066 cpl_propertylist_update_int(aImage->header, keyword, nsaturated);
00067 cpl_free(keyword);
00068 }
00069
00070
00071 stats |= CPL_STATS_FLUX;
00072 muse_basicproc_stats_append_header(aImage->data, aImage->header,
00073 QC_FLAT_MASTER_PREFIX, stats);
00074 }
00075
00076
00082
00083 static void
00084 muse_flat_qc_trace_header(cpl_propertylist *aHeader, const cpl_table *aTrace)
00085 {
00086 if (!aHeader || !aTrace) {
00087 return;
00088 }
00089 unsigned char ifu = muse_utils_get_ifu(aHeader);
00090 cpl_msg_debug(__func__, "Adding tracing QC keywords for IFU %hhu", ifu);
00091
00092
00093 cpl_array *gaps = cpl_array_new(kMuseSlicesPerCCD - 1, CPL_TYPE_DOUBLE);
00094 double redge = -1;
00095 int nslice;
00096 for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
00097 cpl_polynomial **ptrace = muse_trace_table_get_polys_for_slice(aTrace,
00098 nslice);
00099 if (!ptrace) {
00100 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: tracing polynomials "
00101 "missing!", nslice, ifu);
00102 continue;
00103 }
00104
00105
00106 float xpos = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_CENTER],
00107 kMuseOutputYTop/2, NULL);
00108 if (xpos < 1 || xpos > kMuseOutputXRight || !isnormal(xpos)) {
00109 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: faulty polynomial "
00110 "detected at y=%d (center: %f)", nslice, ifu,
00111 kMuseOutputYTop/2, xpos);
00112 muse_trace_polys_delete(ptrace);
00113 continue;
00114 }
00115
00116
00117 double x1 = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_CENTER], 1, NULL),
00118 x2 = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_CENTER],
00119 kMuseOutputYTop, NULL);
00120 float tilt = atan((x2 - x1) / (kMuseOutputYTop - 1)) * CPL_MATH_DEG_RAD;
00121 if (nslice == 1) {
00122 cpl_propertylist_append_float(aHeader, QC_TRACE_L_XPOS, xpos);
00123 cpl_propertylist_append_float(aHeader, QC_TRACE_L_TILT, tilt);
00124 } else if (nslice == kMuseSlicesPerCCD) {
00125 cpl_propertylist_append_float(aHeader, QC_TRACE_R_XPOS, xpos);
00126 cpl_propertylist_append_float(aHeader, QC_TRACE_R_TILT, tilt);
00127 }
00128 if (redge > 0) {
00129
00130
00131 double gap = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT],
00132 kMuseOutputYTop/2, NULL)
00133 - redge;
00134 cpl_array_set_double(gaps, nslice - 2, gap);
00135 }
00136
00137 redge = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT], kMuseOutputYTop/2,
00138 NULL);
00139
00140
00141
00142 double maxslope = 0.;
00143 int ipoly, j, jmax = -1, ipolymax = -1;
00144 for (ipoly = MUSE_TRACE_CENTER; ipoly <= MUSE_TRACE_RIGHT; ipoly++) {
00145 cpl_polynomial_derivative(ptrace[ipoly], 0);
00146
00147
00148 for (j = 1; j <= kMuseOutputYTop; j++) {
00149 double slope = cpl_polynomial_eval_1d(ptrace[ipoly], j, NULL);
00150 if (fabs(slope) > fabs(maxslope)) {
00151 maxslope = slope;
00152 jmax = j;
00153 ipolymax = ipoly;
00154 }
00155 }
00156 }
00157 #define MAX_NORMAL_SLOPE 0.015
00158 if (fabs(maxslope) > MAX_NORMAL_SLOPE) {
00159 cpl_msg_warning(__func__, "Slope in slice %2d of IFU %hhu is unusually "
00160 "high (maximum in polynomial %d at y=%d is |%f| > %f)",
00161 nslice, ifu, ipolymax, jmax, maxslope, MAX_NORMAL_SLOPE);
00162 }
00163 char *kw = cpl_sprintf(QC_TRACE_SLICEj_MXSLOP, nslice);
00164 cpl_propertylist_append_float(aHeader, kw, maxslope);
00165 cpl_free(kw);
00166 muse_trace_polys_delete(ptrace);
00167
00168
00169 if (nslice == 10 || nslice == 46 || nslice == 3 || nslice == 39) {
00170
00171 int irow, err, nrow = cpl_table_get_nrow(aTrace);
00172 for (irow = 0; irow < nrow; irow++) {
00173 int slice = cpl_table_get_int(aTrace, MUSE_TRACE_TABLE_COL_SLICE_NO,
00174 irow, &err);
00175 if (slice == nslice && !err) {
00176 break;
00177 }
00178 }
00179 if (irow >= nrow) {
00180 cpl_msg_warning(__func__, "Slice %d not found in trace table!", nslice);
00181 continue;
00182 }
00183 float width = cpl_table_get(aTrace, MUSE_TRACE_TABLE_COL_WIDTH, irow, &err);
00184 kw = cpl_sprintf(QC_TRACE_SLICEj_WIDTH, nslice);
00185 cpl_propertylist_append_float(aHeader, kw, width);
00186 cpl_free(kw);
00187 }
00188 }
00189
00190 float median, mean, stdev, min, max;
00191
00192 median = cpl_array_get_median(gaps);
00193 mean = cpl_array_get_mean(gaps);
00194 stdev = cpl_array_get_stdev(gaps);
00195 min = cpl_array_get_min(gaps);
00196 max = cpl_array_get_max(gaps);
00197 cpl_propertylist_append_float(aHeader, QC_TRACE_GAPS_MEDIAN, median);
00198 cpl_propertylist_append_float(aHeader, QC_TRACE_GAPS_MEAN, mean);
00199 cpl_propertylist_append_float(aHeader, QC_TRACE_GAPS_STDEV, stdev);
00200 cpl_propertylist_append_float(aHeader, QC_TRACE_GAPS_MIN, min);
00201 cpl_propertylist_append_float(aHeader, QC_TRACE_GAPS_MAX, max);
00202 cpl_array_delete(gaps);
00203
00204
00205 median = cpl_table_get_column_median(aTrace, MUSE_TRACE_TABLE_COL_WIDTH);
00206 mean = cpl_table_get_column_mean(aTrace, MUSE_TRACE_TABLE_COL_WIDTH);
00207 stdev = cpl_table_get_column_stdev(aTrace, MUSE_TRACE_TABLE_COL_WIDTH);
00208 min = cpl_table_get_column_min(aTrace, MUSE_TRACE_TABLE_COL_WIDTH);
00209 max = cpl_table_get_column_max(aTrace, MUSE_TRACE_TABLE_COL_WIDTH);
00210 cpl_propertylist_append_float(aHeader, QC_TRACE_WIDTHS_MEDIAN, median);
00211 cpl_propertylist_append_float(aHeader, QC_TRACE_WIDTHS_MEAN, mean);
00212 cpl_propertylist_append_float(aHeader, QC_TRACE_WIDTHS_STDEV, stdev);
00213 cpl_propertylist_append_float(aHeader, QC_TRACE_WIDTHS_MIN, min);
00214 cpl_propertylist_append_float(aHeader, QC_TRACE_WIDTHS_MAX, max);
00215 }
00216
00217
00223
00224 static void
00225 muse_flat_qc_per_slice(muse_image *aImage, const cpl_table *aTrace)
00226 {
00227 if (!aImage->header || !aTrace) {
00228 return;
00229 }
00230 unsigned char ifu = muse_utils_get_ifu(aImage->header);
00231 cpl_msg_debug(__func__, "Adding per-slice QC statistics for IFU %hhu", ifu);
00232
00233
00234 const int y1 = kMuseOutputYTop/2 - 100,
00235 y2 = kMuseOutputYTop/2 + 100;
00236 int nslice;
00237 for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
00238 cpl_polynomial **ptrace = muse_trace_table_get_polys_for_slice(aTrace,
00239 nslice);
00240 if (!ptrace) {
00241 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: tracing polynomials "
00242 "missing!", nslice, ifu);
00243 continue;
00244 }
00245 char *kwmean = cpl_sprintf(QC_FLAT_MASTER_SLICEj_MEAN, nslice),
00246 *kwstdev = cpl_sprintf(QC_FLAT_MASTER_SLICEj_STDEV, nslice);
00247
00248 int x1 = lround(cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT],
00249 kMuseOutputYTop/2, NULL)) + 1,
00250 x2 = lround(cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT],
00251 kMuseOutputYTop/2, NULL)) - 1;
00252 if (x1 < 1 || x2 > kMuseOutputXRight || x1 > x2) {
00253 cpl_msg_warning(__func__, "slice %2d of IFU %hhu: faulty polynomial "
00254 "detected at y=%d (borders: %d ... %d)", nslice, ifu,
00255 kMuseOutputYTop/2, x1, x2);
00256 muse_trace_polys_delete(ptrace);
00257 continue;
00258 }
00259 double mean = cpl_image_get_mean_window(aImage->data, x1, y1, x2, y2),
00260 stdev = cpl_image_get_stdev_window(aImage->data, x1, y1, x2, y2);
00261 cpl_propertylist_update_float(aImage->header, kwmean, mean);
00262 cpl_propertylist_update_float(aImage->header, kwstdev, stdev);
00263
00264 muse_trace_polys_delete(ptrace);
00265 cpl_free(kwmean);
00266 cpl_free(kwstdev);
00267 }
00268 }
00269
00270
00278
00279 static cpl_error_code
00280 muse_flat_trace_badpix(muse_processing *aProcessing,
00281 muse_flat_params_t *aParams, muse_image *aImage)
00282 {
00283 cpl_table *samples = NULL;
00284 cpl_table *tracetable = muse_trace(aImage, aParams->nsum, aParams->edgefrac,
00285 aParams->order,
00286 aParams->samples ? &samples : NULL);
00287 if (!tracetable) {
00288 cpl_table_delete(samples);
00289 return cpl_error_get_code();
00290 }
00291
00292
00293 cpl_propertylist *header = cpl_propertylist_duplicate(aImage->header);
00294 cpl_propertylist_erase_regexp(header,
00295 "^SIMPLE$|^BITPIX$|^NAXIS|^EXTEND$|^XTENSION$|"
00296 "^DATASUM$|^DATAMIN$|^DATAMAX$|^DATAMD5$|"
00297 "^PCOUNT$|^GCOUNT$|^HDUVERS$|^BLANK$|"
00298 "^BZERO$|^BSCALE$|^BUNIT$|^CHECKSUM$|^INHERIT$|"
00299 "^PIPEFILE$|^ESO QC |^ESO PRO ", 0);
00300
00301 muse_flat_qc_trace_header(header, tracetable);
00302 cpl_error_code rc = muse_processing_save_table(aProcessing, aParams->nifu,
00303 tracetable, header,
00304 MUSE_TAG_TRACE_TABLE,
00305 MUSE_TABLE_TYPE_CPL);
00306 if (samples) {
00307
00308 cpl_propertylist_erase_regexp(header, QC_TRACE_PREFIX, 0);
00309 muse_processing_save_table(aProcessing, aParams->nifu, samples, header,
00310 MUSE_TAG_TRACE_SAMPLES, MUSE_TABLE_TYPE_CPL);
00311 cpl_table_delete(samples);
00312 }
00313 cpl_propertylist_delete(header);
00314 if (rc != CPL_ERROR_NONE) {
00315 cpl_table_delete(tracetable);
00316 return rc;
00317 }
00318
00319
00320 int nbad = muse_quality_flat_badpix(aImage, tracetable,
00321 aParams->losigmabadpix,
00322 aParams->hisigmabadpix);
00323 cpl_msg_info(__func__, "Found %d bad pixels in the flat-field image of IFU %d",
00324 nbad, aParams->nifu);
00325
00326
00327 muse_flat_qc_per_slice(aImage, tracetable);
00328
00329 cpl_table_delete(tracetable);
00330 return rc;
00331 }
00332
00333
00340
00341 int
00342 muse_flat_compute(muse_processing *aProcessing, muse_flat_params_t *aParams)
00343 {
00344 muse_basicproc_params *bpars = muse_basicproc_params_new(aProcessing->parameters,
00345 "muse.muse_flat");
00346 muse_imagelist *images = muse_basicproc_load(aProcessing, aParams->nifu, bpars);
00347 muse_basicproc_params_delete(bpars);
00348 cpl_ensure(images, cpl_error_get_code(), -1);
00349
00350 muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
00351 "muse.muse_flat");
00352 muse_image *masterimage = muse_combine_images(cpars, images);
00353 muse_combinepar_delete(cpars);
00354 if (!masterimage) {
00355 cpl_msg_error(__func__, "Combining input frames failed for IFU %d!",
00356 aParams->nifu);
00357 muse_imagelist_delete(images);
00358 return -1;
00359 }
00360 cpl_propertylist_erase_regexp(masterimage->header, MUSE_WCS_KEYS, 0);
00361
00362 muse_flat_qc_header(masterimage, images);
00363 muse_imagelist_delete(images);
00364
00365 cpl_error_code rc1 = CPL_ERROR_NONE;
00366 if (aParams->trace) {
00367
00368
00369 rc1 = muse_flat_trace_badpix(aProcessing, aParams, masterimage);
00370 if (rc1 != CPL_ERROR_NONE) {
00371
00372
00373 cpl_msg_error(__func__, "Tracing/bad pixel search failed in IFU %d",
00374 aParams->nifu);
00375 }
00376 }
00377
00378
00379 if (aParams->normalize) {
00380 double mean = cpl_propertylist_get_float(masterimage->header,
00381 QC_FLAT_MASTER_PREFIX" MEAN");
00382 muse_image_scale(masterimage, 1. / mean);
00383 }
00384
00385 muse_basicproc_qc_saturated(masterimage, QC_FLAT_MASTER_PREFIX);
00386 cpl_error_code rc2 = muse_processing_save_image(aProcessing, aParams->nifu,
00387 masterimage,
00388 MUSE_TAG_MASTER_FLAT);
00389 muse_image_delete(masterimage);
00390 return rc1 == CPL_ERROR_NONE && rc2 == CPL_ERROR_NONE ? 0 : -1;
00391 }