MUSE Pipeline Reference Manual  2.1.1
muse_quadrants.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2005-2014 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 /*----------------------------------------------------------------------------*
27  * Includes *
28  *----------------------------------------------------------------------------*/
29 #include <cpl.h>
30 #include <string.h> /* strncmp() */
31 
32 #include "muse_quadrants.h"
33 #include "muse_instrument.h"
34 
35 #include "muse_artifacts.h"
36 #include "muse_dfs.h"
37 #include "muse_pfits.h"
38 #include "muse_utils.h"
39 
40 /*----------------------------------------------------------------------------*/
47 /*----------------------------------------------------------------------------*/
48 
51 /*---------------------------------------------------------------------------*/
77 /*---------------------------------------------------------------------------*/
78 cpl_error_code
79 muse_quadrants_overscan_stats(muse_image *aImage, const char *aRejection,
80  unsigned int aIgnore)
81 {
82  cpl_ensure_code(aImage && aImage->data && aImage->header, CPL_ERROR_NULL_INPUT);
83  /* ensure that the section sizes are the same for all quadrants */
84  cpl_ensure_code(muse_quadrants_verify(aImage->header),
85  CPL_ERROR_BAD_FILE_FORMAT);
86  /* Check that the overscan windows make sense for all quadrants; *
87  * do this early to not waste time on other computations before. *
88  * This implicitly checks, if the input aIgnore value is usable. */
89  unsigned char n; /* quadrant counter */
90  for (n = 1; n <= 4; n++) {
91  cpl_size *w = muse_quadrants_overscan_get_window(aImage, n, aIgnore);
92  cpl_ensure_code(w, CPL_ERROR_ILLEGAL_INPUT);
93  cpl_free(w);
94  } /* for n (quadrants) */
95  int nx = cpl_image_get_size_x(aImage->data);
96 
97  enum { REJ_NONE, REJ_DCR, REJ_FIT } reject = REJ_NONE;
98  if (aRejection) {
99  if (!strncmp(aRejection, "dcr", 3)) { /* allow parameters after string! */
100  reject = REJ_DCR;
101  } else if (!strncmp(aRejection, "fit", 3)) { /* allow parameters after string! */
102  reject = REJ_FIT;
103  } else if (strncmp(aRejection, "none", 5)) {
104  return cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT,
105  "Unknown rejection type \"%s\"", aRejection);
106  }
107  } /* if aRejection */
108 
109  unsigned int binx = muse_pfits_get_binx(aImage->header),
110  biny = muse_pfits_get_biny(aImage->header),
111  outnx = muse_pfits_get_out_nx(aImage->header, 1) / binx,
112  outny = muse_pfits_get_out_ny(aImage->header, 1) / biny,
113  overx = muse_pfits_get_out_overscan_x(aImage->header, 1) / binx,
114  overy = muse_pfits_get_out_overscan_y(aImage->header, 1) / biny;
115  unsigned long dcrbox1 = 128, dcrbox2 = 32, dcrnpass = 3;
116  float dcrthres = 2.5,
117  sigma = 5.0;
118  if (reject == REJ_DCR) {
119  char *rest = strchr(aRejection, ':');
120  if (strlen(aRejection) > 4 && rest++) { /* try to access info after "dcr:" */
121  dcrbox1 = strtoul(rest, &rest, 10);
122  if (strlen(rest++) > 0) { /* ++ to skip over the comma */
123  dcrbox2 = strtoul(rest, &rest, 10);
124  if (strlen(rest++) > 0) {
125  dcrnpass = strtoul(rest, &rest, 10);
126  if (strlen(rest++) > 0) {
127  dcrthres = strtof(rest, NULL);
128  }
129  }
130  }
131  }
132  /* check that box sizes in both directions stay inside overscan region */
133  if (dcrbox2 > overx || dcrbox2 > overy ||
134  dcrbox1 > outnx || dcrbox1 > outny ||
135  dcrnpass <= 0 || dcrthres <= 0.) {
136  cpl_msg_warning(__func__, "Detected illegal DCR parameters for overscan "
137  "statistics (%lux%lu, %lu, %f; overscan: %ux%u, output: "
138  "%ux%u), not rejecting anything!", dcrbox1, dcrbox2,
139  dcrnpass, dcrthres, overx, overy, outnx, outny);
140  reject = REJ_NONE;
141  } else {
142  cpl_msg_debug(__func__, "Using DCR cosmic ray rejection for overscan "
143  "statistics (%lux%lu, %lu, %f)", dcrbox1, dcrbox2, dcrnpass,
144  dcrthres);
145  }
146  } else if (reject == REJ_FIT) {
147  char *rest = strchr(aRejection, ':');
148  if (strlen(aRejection) > 4 && rest++) { /* try to access info after "fit:" */
149  sigma = strtof(rest, NULL);
150  }
151  /* check that box sizes in both directions stay inside overscan regions */
152  if (sigma <= 0.) {
153  cpl_msg_warning(__func__, "Detected illegal fit sigma for overscan "
154  "statistics (%f; overscan: %ux%u, output: %ux%u) not "
155  "rejecting anything!", sigma, overx, overy, outnx, outny);
156  reject = REJ_NONE;
157  } else {
158  cpl_msg_debug(__func__, "Using constant fit for overscan statistics (0, "
159  "%f)", sigma);
160  }
161  } /* if REJ_DCR */
162 
163  const float *data = cpl_image_get_data_float_const(aImage->data);
164  const int *dq = cpl_image_get_data_int_const(aImage->dq);
165 
166  /* get data section for each quadrant and compute the size of the output image */
167  for (n = 1; n <= 4; n++) {
168  /* get overscan regions and compute pixel numbers */
169  cpl_size *w = muse_quadrants_overscan_get_window(aImage, n, aIgnore);
170  int nh1 = w[1] - w[0] + 1,
171  nh2 = w[3] - w[2] + 1,
172  nhori = nh1 * nh2,
173  nv1 = w[5] - w[4] + 1,
174  nv2 = w[7] - w[6] + 1,
175  nvert = nv1 * nv2,
176  ntot = nhori + nvert,
177  nhcr = 0, nvcr = 0;
178 
179  if (reject == REJ_DCR && dcrnpass > 0 && dcrbox1 > 0 && dcrbox2 > 0) {
180  /* extract subframes to use for cosmic ray marking, then put them back */
181  /* horizontal part */
182  muse_image *image = muse_image_new();
183  image->data = cpl_image_extract(aImage->data, w[0], w[2], w[1], w[3]);
184  image->dq = cpl_image_extract(aImage->dq, w[0], w[2], w[1], w[3]);
185  image->stat = cpl_image_extract(aImage->stat, w[0], w[2], w[1], w[3]);
186  /* make sure to not cause illegal inputs due to too large boxes */
187  nhcr = muse_cosmics_dcr(image, CPL_MIN((int)dcrbox1, nh1),
188  CPL_MIN((int)dcrbox2, nh2),
189  dcrnpass, dcrthres);
190  cpl_image_copy(aImage->data, image->data, w[0], w[2]);
191  cpl_image_copy(aImage->dq, image->dq, w[0], w[2]);
192  cpl_image_copy(aImage->stat, image->stat, w[0], w[2]);
193  muse_image_delete(image);
194  /* vertical part */
195  image = muse_image_new();
196  image->data = cpl_image_extract(aImage->data, w[4], w[6], w[5], w[7]);
197  image->dq = cpl_image_extract(aImage->dq, w[4], w[6], w[5], w[7]);
198  image->stat = cpl_image_extract(aImage->stat, w[4], w[6], w[5], w[7]);
199  /* inverted box size */
200  nvcr = muse_cosmics_dcr(image, CPL_MIN((int)dcrbox2, nv1),
201  CPL_MIN((int)dcrbox1, nv2),
202  dcrnpass, dcrthres);
203  cpl_image_copy(aImage->data, image->data, w[4], w[6]);
204  cpl_image_copy(aImage->dq, image->dq, w[4], w[6]);
205  cpl_image_copy(aImage->stat, image->stat, w[4], w[6]);
206  muse_image_delete(image);
207  } /* if REJ_DCR */
208 
209  /* now loop over both regions and fill vectors for the statistics */
210  cpl_vector *hori = cpl_vector_new(nhori),
211  *vert = cpl_vector_new(nvert),
212  *tot = cpl_vector_new(ntot);
213  /* collect all pixels in the horizontal overscan */
214  int i, j, ihori = 0, ivert = 0;
215  for (i = w[0] - 1; i < w[1]; i++) {
216  for (j = w[2] - 1; j < w[3]; j++) {
217  if (dq[i + j*nx]) { /* exclude pixels with bad status */
218  continue;
219  }
220  cpl_vector_set(tot, ihori + ivert, data[i + j*nx]);
221  cpl_vector_set(hori, ihori++, data[i + j*nx]);
222  }
223  }
224  for (i = w[4] - 1; i < w[5]; i++) {
225  for (j = w[6] - 1; j < w[7]; j++) {
226  if (dq[i + j*nx]) {
227  continue;
228  }
229  cpl_vector_set(tot, ihori + ivert, data[i + j*nx]);
230  cpl_vector_set(vert, ivert++, data[i + j*nx]);
231  }
232  }
233  cpl_vector_set_size(hori, ihori);
234  cpl_vector_set_size(vert, ivert);
235  cpl_vector_set_size(tot, ihori + ivert);
236 
237  int nrej = 0;
238  double mean, stdev;
239  if (reject == REJ_FIT) {
240  /* For the iterative fit, we only care about the mean, not *
241  * about the position, so leave the matrix filled with zeros. */
242  cpl_matrix *pos = cpl_matrix_new(1, ihori + ivert);
243  cpl_vector *val = cpl_vector_duplicate(tot);
244  double mse;
245  cpl_polynomial *fit = muse_utils_iterate_fit_polynomial(pos, val, NULL, NULL,
246  0, sigma, &mse, NULL);
247  nrej = ihori + ivert - cpl_vector_get_size(val);
248  cpl_size pows = 0; /* get the zero-order coefficient */
249  mean = cpl_polynomial_get_coeff(fit, &pows);
250  stdev = sqrt(mse);
251  cpl_polynomial_delete(fit);
252  cpl_matrix_delete(pos);
253  cpl_vector_delete(val);
254  } else { /* no fitting */
255  nrej = nhcr + nvcr;
256  mean = cpl_vector_get_mean(tot);
257  stdev = cpl_vector_get_stdev(tot);
258  } /* else */
259 
260  double hmean = cpl_vector_get_mean(hori),
261  vmean = cpl_vector_get_mean(vert),
262  hstdev = cpl_vector_get_stdev(hori),
263  vstdev = cpl_vector_get_stdev(vert);
264  cpl_msg_debug(__func__, "quadrant %1hhu: %.3f+/-%.3f (h=%.1f+/-%.1f; "
265  "v=%.1f+/-%.1f; nrej=%d) [%s]", n, mean, stdev,
266  hmean, hstdev, vmean, vstdev, nrej, aRejection);
267  /* cross check vertical and horizontal against total */
268  double lo = mean - stdev,
269  hi = mean + stdev;
270  if (hmean < lo || hmean > hi) {
271  cpl_msg_warning(__func__, "Horizontal overscan differs from total "
272  "(%.3f outside %.3f+/-%.3f)", hmean, mean, stdev);
273  }
274  if (vmean < lo || vmean > hi) {
275  cpl_msg_warning(__func__, "Vertical overscan differs from total "
276  "(%.3f outside %.3f+/-%.3f)", vmean, mean, stdev);
277  }
278 
279  char *keyword = cpl_sprintf(MUSE_HDR_OVSC_MEAN, n);
280  cpl_propertylist_append_float(aImage->header, keyword, mean);
281  cpl_free(keyword);
282  keyword = cpl_sprintf(MUSE_HDR_OVSC_STDEV, n);
283  cpl_propertylist_append_float(aImage->header, keyword, stdev);
284  cpl_free(keyword);
285 
286  cpl_vector_delete(hori);
287  cpl_vector_delete(vert);
288  cpl_vector_delete(tot);
289  cpl_free(w);
290  } /* for n (quadrants) */
291 
292  return CPL_ERROR_NONE;
293 } /* muse_quadrants_overscan_stats() */
294 
295 /*---------------------------------------------------------------------------*/
307 /*---------------------------------------------------------------------------*/
308 cpl_boolean
310  double aSigma)
311 {
312  cpl_ensure(aImage && aImage->header && aRefImage && aRefImage->header,
313  CPL_ERROR_NULL_INPUT, CPL_FALSE);
314 
315  int rc = CPL_TRUE;
316  unsigned char n; /* quadrant counter */
317  for (n = 1; n <= 4; n++) {
318  /* create correct header keyword names */
319  char *keywordmean = cpl_sprintf(MUSE_HDR_OVSC_MEAN, n),
320  *keywordstdev = cpl_sprintf(MUSE_HDR_OVSC_STDEV, n);
321 
322  /* overscan stats for reference image */
323  float refmean = cpl_propertylist_get_float(aRefImage->header, keywordmean),
324  refstdev = cpl_propertylist_get_float(aRefImage->header, keywordstdev),
325  hilimit = refmean + aSigma * refstdev,
326  lolimit = refmean - aSigma * refstdev;
327 
328  float mean = cpl_propertylist_get_float(aImage->header, keywordmean),
329  stdev = cpl_propertylist_get_float(aImage->header, keywordstdev);
330  if (mean > hilimit || mean < lolimit) {
331  const char *fn = cpl_propertylist_get_string(aImage->header, MUSE_HDR_TMP_FN),
332  *reffn = cpl_propertylist_get_string(aRefImage->header, MUSE_HDR_TMP_FN);
333  cpl_msg_warning(__func__, "Overscan of quadrant %1u of image [%s] "
334  "(%.3f+/-%.3f) differs from reference image [%s] "
335  "(%.3f+/-%.3f)!", n, fn, mean, stdev, reffn, refmean,
336  refstdev);
337  rc = CPL_FALSE;
338  }
339 
340  cpl_free(keywordmean);
341  cpl_free(keywordstdev);
342  } /* for n (quadrants) */
343 
344  return rc;
345 } /* muse_quadrants_overscan_check() */
346 
347 /*---------------------------------------------------------------------------*/
361 /*---------------------------------------------------------------------------*/
362 cpl_error_code
364 {
365  cpl_ensure_code(aImage && aImage->header && aRefImage && aRefImage->header,
366  CPL_ERROR_NULL_INPUT);
367 
368  unsigned char n; /* quadrant counter */
369  for (n = 1; n <= 4; n++) {
370  char *keywordmean = cpl_sprintf(MUSE_HDR_OVSC_MEAN, n),
371  *keywordstdev = cpl_sprintf(MUSE_HDR_OVSC_STDEV, n);
372  float refmean = cpl_propertylist_get_float(aRefImage->header, keywordmean),
373  refstdev = cpl_propertylist_get_float(aRefImage->header, keywordstdev),
374  mean = cpl_propertylist_get_float(aImage->header, keywordmean),
375  stdev = cpl_propertylist_get_float(aImage->header, keywordstdev);
376 #if 0
377  cpl_msg_debug(__func__, "ref=%f+/-%f, image=%f+/-%f", refmean, refstdev,
378  mean, stdev);
379 #endif
380  cpl_size *w = muse_quadrants_get_window(aImage, n);
381 
382  /* subtract difference from this quadrant of aImage->data */
383  /* get the data for this quadrant */
384  cpl_image *data = cpl_image_extract(aImage->data, w[0], w[2], w[1], w[3]);
385  cpl_image_add_scalar(data, refmean - mean);
386  /* copy the data back into the original image */
387  cpl_image_copy(aImage->data, data, w[0], w[2]);
388  cpl_image_delete(data);
389 
390  /* somehow add the stdev to this quadrant of aImage->stat in quadrature */
391  cpl_image *stat = cpl_image_extract(aImage->stat, w[0], w[2], w[1], w[3]);
392  /* use the gain to correct the variances, as in muse_image_variance_create() */
393  double gain = muse_pfits_get_gain(aImage->header, n); /* in count/adu */
394 #if 0
395  cpl_msg_debug(__func__, "variance: %f", (refstdev*refstdev + stdev*stdev) / gain);
396 #endif
397  cpl_image_add_scalar(stat, (refstdev*refstdev + stdev*stdev) / gain);
398  cpl_image_copy(aImage->stat, stat, w[0], w[2]);
399  cpl_image_delete(stat);
400 
401  /* update the overscans */
402  cpl_propertylist_update_float(aImage->header, keywordmean, refmean);
403 
404  cpl_free(w);
405  cpl_free(keywordmean);
406  cpl_free(keywordstdev);
407  } /* for n (quadrants) */
408 
409  return CPL_ERROR_NONE;
410 } /* muse_quadrants_overscan_correct() */
411 
412 /*---------------------------------------------------------------------------*/
459 /*---------------------------------------------------------------------------*/
460 cpl_error_code
462  unsigned char aOrder, double aSigma,
463  const double aFRMS, double aFChiSq)
464 {
465  cpl_ensure_code(aImage && aImage->data && aImage->dq && aImage->header,
466  CPL_ERROR_NULL_INPUT);
467  cpl_ensure_code(aFRMS > 1. && aFChiSq > 1., CPL_ERROR_ILLEGAL_INPUT);
468  /* ensure that the region sizes are the same for all quadrants */
469  cpl_ensure_code(muse_quadrants_verify(aImage->header),
470  CPL_ERROR_BAD_FILE_FORMAT);
471 
472  /* get output sizes for data sections, and pre- and overscans */
473  int nx = cpl_image_get_size_x(aImage->data),
474  binx = muse_pfits_get_binx(aImage->header),
475  biny = muse_pfits_get_biny(aImage->header),
476  outnx = muse_pfits_get_out_nx(aImage->header, 1) / binx,
477  outny = muse_pfits_get_out_ny(aImage->header, 1) / biny,
478  prex = muse_pfits_get_out_prescan_x(aImage->header, 1) / binx,
479  prey = muse_pfits_get_out_prescan_y(aImage->header, 1) / biny,
480  overx = muse_pfits_get_out_overscan_x(aImage->header, 1) / binx,
481  overy = muse_pfits_get_out_overscan_y(aImage->header, 1) / biny;
482  cpl_ensure_code(outnx > 0 && outny > 0 && overx > 0 && overy > 0 &&
483  prex > 0 && prey > 0, CPL_ERROR_INCOMPATIBLE_INPUT);
484  unsigned char ifu = muse_utils_get_ifu(aImage->header);
485 
486  /* Check that the overscan windows make sense for all quadrants; *
487  * do this for all quadrants before everything else, so that we *
488  * don't end up with a partially corrected image! This implicitly *
489  * checks, if the input aIgnore value is usable. */
490  unsigned char n; /* quadrant counter */
491  for (n = 1; n <= 4; n++) {
492  cpl_size *w = muse_quadrants_overscan_get_window(aImage, n, aIgnore);
493  cpl_ensure_code(w, CPL_ERROR_ILLEGAL_INPUT);
494  cpl_free(w);
495  } /* for n (quadrants) */
496 
497  int debug = 0;
498  if (getenv("MUSE_DEBUG_QUADRANTS")) {
499  debug = atoi(getenv("MUSE_DEBUG_QUADRANTS"));
500  }
501 
502  float *data = cpl_image_get_data_float(aImage->data);
503  const int *dq = cpl_image_get_data_int_const(aImage->dq);
504  for (n = 1; n <= 4; n++) {
505  cpl_size *w = muse_quadrants_overscan_get_window(aImage, n, aIgnore);
506  /* over vertical overscan only, indices 4 - 7 */
507  int nvert = (w[5] - w[4] + 1) * (w[7] - w[6] + 1);
508  cpl_matrix *pos = cpl_matrix_new(1, nvert);
509  cpl_vector *val = cpl_vector_new(nvert);
510  int i, j, ivert = 0;
511  for (i = w[4] - 1; i < w[5]; i++) {
512  for (j = w[6] - 1; j < w[7]; j++) {
513  if (dq[i + j*nx]) { /* exclude pixels with bad status */
514  continue;
515  }
516  cpl_matrix_set(pos, 0, ivert, j + 1);
517  cpl_vector_set(val, ivert++, data[i + j*nx]);
518  } /* for j */
519  } /* for i */
520  cpl_matrix_set_size(pos, 1, ivert);
521  cpl_vector_set_size(val, ivert);
522 
523  /* Fit a constant first, and compute the RMS and chi**2 of the fit; *
524  * do not iterate yet, so pass sigma = FLT_MAX */
525  double rms1, rms2, chisq1, chisq2;
526  cpl_polynomial *fit0 = muse_utils_iterate_fit_polynomial(pos, val, NULL, NULL, 0,
527  FLT_MAX, &rms1, &chisq1),
528  *fit = fit0;
529  rms1 = sqrt(rms1);
530  if (debug) {
531  cpl_msg_debug(__func__, "IFU %2hhu Q%1hhu order 0: %f / %f, p(%"
532  CPL_SIZE_FORMAT")=%f .. p(%"CPL_SIZE_FORMAT")=%f",
533  ifu, n, rms1, chisq1,
534  w[6], cpl_polynomial_eval_1d(fit, w[6], NULL),
535  w[7], cpl_polynomial_eval_1d(fit, w[7], NULL));
536  } /* if debug */
537  /* now see, how much a higher order improves the fit */
538  unsigned char norder;
539  for (norder = 1; norder <= aOrder; norder++) {
540  cpl_polynomial *fit2 = muse_utils_iterate_fit_polynomial(pos, val, NULL, NULL,
541  norder, FLT_MAX, &rms2,
542  &chisq2);
543  rms2 = sqrt(rms2);
544  /* improvement factors in RMS and chi**2 */
545  double frms = rms1 / rms2,
546  fchisq = chisq1 / chisq2;
547  if (debug) {
548  cpl_msg_debug(__func__, "IFU %2hhu Q%1hhu order %hhu: %f / %f, %f / %f,"
549  " p(%"CPL_SIZE_FORMAT")=%f .. p(%"CPL_SIZE_FORMAT")=%f",
550  ifu, n, norder, rms2, chisq2, frms, fchisq,
551  w[6], cpl_polynomial_eval_1d(fit2, w[6], NULL),
552  w[7], cpl_polynomial_eval_1d(fit2, w[7], NULL));
553  } /* if debug */
554  if (norder == aOrder || (frms < aFRMS && fchisq < aFChiSq)) {
555  fit = fit2;
556  break;
557  }
558  cpl_polynomial_delete(fit2);
559  rms1 = rms2;
560  chisq1 = chisq2;
561  } /* for norder */
562  /* now use the selected order to fit the final solution, *
563  * iteratively, using the given sigma level */
564  unsigned char order = cpl_polynomial_get_degree(fit);
565  cpl_polynomial_delete(fit);
566  fit = muse_utils_iterate_fit_polynomial(pos, val, NULL, NULL, order,
567  aSigma, &rms2, &chisq2);
568  rms2 = sqrt(rms2);
569  if (debug) {
570  cpl_msg_debug(__func__, "IFU %2hhu Q%1hhu order %hhu (final, sigma %.3f):"
571  " %f / %f, p(%"CPL_SIZE_FORMAT")=%f .. p(%"CPL_SIZE_FORMAT")=%f",
572  ifu, n, order, aSigma, rms2, chisq2,
573  w[6], cpl_polynomial_eval_1d(fit, w[6], NULL),
574  w[7], cpl_polynomial_eval_1d(fit, w[7], NULL));
575  if (debug > 1) {
576  char *fntmp = cpl_strdup(cpl_propertylist_get_string(aImage->header,
577  MUSE_HDR_TMP_FN)),
578  *dotfits = strstr(fntmp, ".fits");
579  if (dotfits) {
580  *dotfits = '\0'; /* strip off the .fits extension */
581  }
582  char *fn = cpl_sprintf("%s_vpoly_IFU%02hhu_Q%1hhu.ascii", fntmp, ifu, n);
583  cpl_msg_debug(__func__, "Saving extra debug data to \"%s\"", fn);
584  cpl_free(fntmp);
585  FILE *fp = fopen(fn, "w");
586  cpl_free(fn);
587  fprintf(fp, "# IFU %2hhu Q%1hhu order %hhu (final, sigma %.3f): %f / %f,"
588  " p(%"CPL_SIZE_FORMAT")=%f .. p(%"CPL_SIZE_FORMAT")=%f\n",
589  ifu, n, order, aSigma, rms2, chisq2,
590  w[6], cpl_polynomial_eval_1d(fit, w[6], NULL),
591  w[7], cpl_polynomial_eval_1d(fit, w[7], NULL));
592  /* output the polynomial fit to the file as well */
593  fprintf(fp, "# p(y) =");
594  for (i = 0; i <= order; i++) {
595  cpl_size pows[] = { i };
596  fprintf(fp, " + (%.10e) * y**%d", cpl_polynomial_get_coeff(fit, pows),
597  i);
598  }
599  fprintf(fp, "\n\n# y\tvalue\n");
600  /* output all (non-rejected) data points to the file */
601  for (i = 0; i < cpl_vector_get_size(val); i++) {
602  fprintf(fp, "%4d\t%f\n", (int)cpl_matrix_get(pos, 0, i),
603  cpl_vector_get(val, i));
604  } /* for i (all datapoints) */
605  fclose(fp);
606  } /* if debug > 1 */
607  } /* if debug */
608  cpl_matrix_delete(pos);
609  cpl_vector_delete(val);
610  char *kw = cpl_sprintf(MUSE_HDR_OVSC_PNC, n);
611  cpl_propertylist_append_int(aImage->header, kw, order + 1);
612  cpl_free(kw);
613  for (norder = 0; norder <= order; norder++) {
614  cpl_size pows = norder;
615  kw = cpl_sprintf(MUSE_HDR_OVSC_PY, n, norder);
616  cpl_propertylist_append_double(aImage->header, kw,
617  cpl_polynomial_get_coeff(fit, &pows));
618  cpl_free(kw);
619  } /* for norder */
620 
621  /* Now create a vector containing a sampling from the polynomial, *
622  * and subtract this from every column of the given quadrant window. */
623  int outx = muse_pfits_get_out_output_x(aImage->header, n),
624  outy = muse_pfits_get_out_output_y(aImage->header, n),
625  i1 = outx == 1 ? 1 : prex + outnx + overx + 1,
626  i2 = outx == 1 ? prex + outnx + overx : 2 * (prex + outnx + overx),
627  j1 = outy == 1 ? 1 : prey + outny + overy + 1,
628  j2 = outy == 1 ? prey + outny + overy : 2 * (prey + outny + overy);
629  if (debug > 1) {
630  cpl_msg_debug(__func__, "quad %1hhu fill region [%d:%d,%d:%d]", n,
631  i1, i2, j1, j2);
632  }
633  /* Evaluate the polynomial at every vertical position in the *
634  * quadrant, then subtract it from the whole row in that quadrant. */
635  for (j = j1 - 1; j < j2; j++) {
636  double subval = cpl_polynomial_eval_1d(fit, j + 1, NULL);
637  for (i = i1 - 1; i < i2; i++) {
638  data[i + j*nx] -= subval;
639  } /* for i */
640  } /* for j */
641 
642  if (fit != fit0) {
643  cpl_polynomial_delete(fit);
644  }
645  cpl_polynomial_delete(fit0);
646  cpl_free(w);
647  } /* for n (quadrants) */
648 
649  return CPL_ERROR_NONE;
650 } /* muse_quadrants_overscan_polyfit_vertical() */
651 
652 /*---------------------------------------------------------------------------*/
684 /*---------------------------------------------------------------------------*/
685 cpl_size *
687  unsigned char aQuadrant,
688  unsigned int aIgnore)
689 {
690  cpl_ensure(aImage && aImage->data && aImage->header, CPL_ERROR_NULL_INPUT,
691  NULL);
692  cpl_ensure(aQuadrant >= 1 && aQuadrant <= 4, CPL_ERROR_ILLEGAL_INPUT, NULL);
693  /* get output sizes for data sections, and pre- and overscans */
694  cpl_errorstate state = cpl_errorstate_get();
695  int binx = muse_pfits_get_binx(aImage->header),
696  biny = muse_pfits_get_biny(aImage->header),
697  outnx = muse_pfits_get_out_nx(aImage->header, aQuadrant) / binx,
698  outny = muse_pfits_get_out_ny(aImage->header, aQuadrant) / biny,
699  prex = muse_pfits_get_out_prescan_x(aImage->header, aQuadrant) / binx,
700  prey = muse_pfits_get_out_prescan_y(aImage->header, aQuadrant) / biny,
701  overx = muse_pfits_get_out_overscan_x(aImage->header, aQuadrant) / binx,
702  overy = muse_pfits_get_out_overscan_y(aImage->header, aQuadrant) / biny,
703  portx = muse_pfits_get_out_output_x(aImage->header, aQuadrant),
704  porty = muse_pfits_get_out_output_y(aImage->header, aQuadrant);
705 #if 0
706  cpl_msg_debug(__func__, "%d/%d, out %d/%d, pre %d/%d, over %d/%d, port %d/%d",
707  binx, biny, outnx, outny, prex, prey, overx, overy, portx, porty);
708 #endif
709  cpl_ensure(cpl_errorstate_is_equal(state) &&
710  outnx > 0 && outny > 0 && overx > 0 && overy > 0 &&
711  prex >= 0 && prey >= 0 && binx > 0 && biny > 0 &&
712  (portx == 1 || portx == kMuseOutputXRight) &&
713  (porty == 1 || porty == kMuseOutputYTop),
714  CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
715  cpl_ensure(aIgnore < (unsigned int)overx, CPL_ERROR_ILLEGAL_INPUT, NULL);
716 
717  /* horizontal overscan region: x1, x2, y1, y2 (indices: 0 - 3) *
718  * vertical overscan region: x1, x2, y1, y2 (indices: 4 - 7) */
719  cpl_size *over = cpl_calloc(8, sizeof(cpl_size));
720  if (portx == 1) {
721  over[0] = prex + 1;
722  over[1] = prex + outnx;
723  over[4] = prex + outnx + 1 + aIgnore;
724  over[5] = prex + outnx + overx;
725  } else { /* portx == kMuseOutputXRight */
726  over[0] = prex + outnx + 2*overx + 1;
727  over[1] = prex + 2*outnx + 2*overx;
728  over[4] = prex + outnx + overx + 1;
729  over[5] = prex + outnx + 2*overx - aIgnore;
730  }
731  if (porty == 1) {
732  over[2] = prey + outny + 1 + aIgnore;
733  over[3] = prey + outny + overy;
734  over[6] = prey + 1;
735  over[7] = prey + outny + overy;
736  } else { /* porty == kMuseOutputYTop */
737  over[2] = prey + outny + overy + 1;
738  over[3] = prey + outny + 2*overy - aIgnore;
739  over[6] = prey + outny + overy + 1;
740  over[7] = prey + 2*outny + 2*overy;
741  }
742 
743  if (getenv("MUSE_DEBUG_QUADRANTS") &&
744  atoi(getenv("MUSE_DEBUG_QUADRANTS")) > 0) {
745  cpl_msg_debug(__func__, "Quadrant %hhu overscan regions: "
746  "[%"CPL_SIZE_FORMAT":%"CPL_SIZE_FORMAT
747  ",%"CPL_SIZE_FORMAT":%"CPL_SIZE_FORMAT"] and "
748  "[%"CPL_SIZE_FORMAT":%"CPL_SIZE_FORMAT
749  ",%"CPL_SIZE_FORMAT":%"CPL_SIZE_FORMAT"]", aQuadrant,
750  over[0], over[1], over[2], over[3],
751  over[4], over[5], over[6], over[7]);
752  } /* if debug */
753  return over;
754 } /* muse_quadrants_overscan_get_window() */
755 
756 /*---------------------------------------------------------------------------*/
771 /*---------------------------------------------------------------------------*/
772 muse_image *
774 {
775  cpl_ensure(aImage && aImage->data && aImage->header, CPL_ERROR_NULL_INPUT,
776  NULL);
777  cpl_boolean debug = CPL_FALSE;
778  if (getenv("MUSE_DEBUG_QUADRANTS")) {
779  debug = atoi(getenv("MUSE_DEBUG_QUADRANTS")) > 0;
780  }
781 
782  /* output image size */
783  int xout = 0, yout = 0;
784  /* arrays of input data section sizes, and output port locations */
785  int nx[4], ny[4], portx[4], porty[4];
786  /* binning to take into account */
787  int binx = muse_pfits_get_binx(aImage->header),
788  biny = muse_pfits_get_biny(aImage->header);
789 
790  /* get data section for each quadrant and compute the size of the output image */
791  unsigned char n; /* quadrant counter */
792  for (n = 1; n <= 4; n++) {
793  nx[n-1] = muse_pfits_get_out_nx(aImage->header, n) / binx;
794  ny[n-1] = muse_pfits_get_out_ny(aImage->header, n) / biny;
795  portx[n-1] = muse_pfits_get_out_output_x(aImage->header, n);
796  porty[n-1] = muse_pfits_get_out_output_y(aImage->header, n);
797  /* check values to see if the relevant headers were present, otherwise abort */
798  if (nx[n-1] < 0 || ny[n-1] < 0 || portx[n-1] < 0 || porty[n-1] < 0) {
799  cpl_msg_error(__func__, "FITS headers necessary for trimming are missing "
800  "from quadrant %1d: NX=%d, NY=%d at OUT X=%d/OUT Y=%d", n,
801  nx[n-1], ny[n-1], portx[n-1], porty[n-1]);
802  cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
803  return NULL;
804  }
805  /* check values to see if the outputs are valid, otherwise abort */
806  if ((portx[n-1] != 1 && portx[n-1] != kMuseOutputXRight) ||
807  (porty[n-1] != 1 && porty[n-1] != kMuseOutputYTop)) {
808  cpl_msg_error(__func__, "FITS headers necessary for trimming are "
809  "unsupported for quadrant %1d: OUT X=%d/OUT Y=%d", n,
810  portx[n-1], porty[n-1]);
811  cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
812  return NULL;
813  }
814 
815  /* use the left two quadrants to count the output height */
816  if (portx[n-1] == 1) {
817  yout += ny[n-1];
818  }
819  /* use the bottom two quadrants to count the output width */
820  if (porty[n-1] == 1) {
821  xout += nx[n-1];
822  }
823  } /* for n (quadrants) */
824 
825  /* now we should have a valid output size */
826  int x_size = cpl_image_get_size_x(aImage->data),
827  y_size = cpl_image_get_size_y(aImage->data);
828  if (xout > x_size || yout > y_size) {
829  cpl_msg_error(__func__, "output size (%dx%d) is larger than input size "
830  "(%dx%d): wrong binning?!", xout, yout, x_size, y_size);
831  cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
832  return NULL;
833  }
834  if (debug) {
835  cpl_msg_debug(__func__, "output size %dx%d", xout, yout);
836  }
837  cpl_ensure(xout > 0 && yout > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
838 
839  /* verify that all the data sections have the same size */
840  for (n = 1; n < 4; n++) {
841  if (nx[n] != nx[0] || ny[n] != ny[0]) {
842  cpl_msg_error(__func__, "Data section of quadrant %d is different from "
843  "quadrant 1!", n+1);
844  cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
845  return NULL;
846  }
847  }
848 
849  /* create the trimmed output image and its components */
850  muse_image *trimmed = muse_image_new();
851  trimmed->data = cpl_image_new(xout, yout, CPL_TYPE_FLOAT);
852  /* only create the other two components if they exist in the input image */
853  if (aImage->dq) {
854  trimmed->dq = cpl_image_new(xout, yout, CPL_TYPE_INT);
855  }
856  if (aImage->stat) {
857  trimmed->stat = cpl_image_new(xout, yout, CPL_TYPE_FLOAT);
858  }
859 
860  /* copy the input header but erase those that are no longer relevant */
861  trimmed->header = cpl_propertylist_duplicate(aImage->header);
862  cpl_propertylist_erase_regexp(trimmed->header,
863  "^NAXIS|^DATASUM$|^DATAMIN$|^DATAMAX$|^DATAMD5$|"
864  "^ESO DET OUT.*PRSC|^ESO DET OUT.*OVSC",
865  0);
866 
867  /* copy data sections (of all three extensions, if they exist) *
868  * and for all quadrants into the output image */
869  for (n = 1; n <= 4; n++) {
870  /* read properties from input header */
871  int x_prescan = muse_pfits_get_out_prescan_x(aImage->header, n) / binx,
872  y_prescan = muse_pfits_get_out_prescan_y(aImage->header, n) / biny;
873 
874  /* determine the data sections to extract and the target output positions */
875  int x1 = 0, x2 = 0, y1 = 0, y2= 0,
876  xtarget = 0, ytarget = 0;
877 
878  /* quadrant at left or at right */
879  if (portx[n-1] == 1) {
880 #if 0
881  cpl_msg_debug(__func__, "left quadrant (OUT%d)", n);
882 #endif
883  x1 = x_prescan + 1;
884  x2 = x_prescan + nx[0];
885  xtarget = 1;
886  } else if (portx[n-1] == kMuseOutputXRight) {
887 #if 0
888  cpl_msg_debug(__func__, "right quadrant (OUT%d)", n);
889 #endif
890  x1 = x_size - x_prescan - nx[0] + 1;
891  x2 = x_size - x_prescan;
892  xtarget = nx[0] + 1;
893  }
894 
895  /* quadrant at bottom or at top */
896  if (porty[n-1] == 1) {
897 #if 0
898  cpl_msg_debug(__func__, "bottom quadrant (OUT%d)", n);
899 #endif
900  y1 = y_prescan + 1;
901  y2 = y_prescan + ny[0];
902  ytarget = 1;
903  } else if (porty[n-1] == kMuseOutputYTop) {
904 #if 0
905  cpl_msg_debug(__func__, "top quadrant (OUT%d)", n);
906 #endif
907  y1 = y_size - y_prescan - ny[0] + 1;
908  y2 = y_size - y_prescan;
909  ytarget = ny[0] + 1;
910  }
911 
912  /* copy data and maybe extensions */
913  cpl_image *image = cpl_image_extract(aImage->data, x1, y1, x2, y2);
914  if (debug) {
915  cpl_msg_debug(__func__, "port at %d,%d: %d,%d - %d,%d, extracted: "
916  "%"CPL_SIZE_FORMAT"x%"CPL_SIZE_FORMAT" -> %d,%d",
917  portx[n-1], porty[n-1], x1,y1, x2,y2,
918  cpl_image_get_size_x(image), cpl_image_get_size_y(image),
919  xtarget, ytarget);
920  }
921  cpl_image_copy(trimmed->data, image, xtarget, ytarget);
922  cpl_image_delete(image);
923  if (aImage->dq) {
924  image = cpl_image_extract(aImage->dq, x1, y1, x2, y2);
925  cpl_image_copy(trimmed->dq, image, xtarget, ytarget);
926  cpl_image_delete(image);
927  }
928  if (aImage->stat) {
929  image = cpl_image_extract(aImage->stat, x1, y1, x2, y2);
930  cpl_image_copy(trimmed->stat, image, xtarget, ytarget);
931  cpl_image_delete(image);
932  }
933  } /* for n (quadrants) */
934 
935 #if 0
936  muse_image_save(aImage, "input.fits");
937  muse_image_save(trimmed, "trimmed.fits");
938 #endif
939 
940  return trimmed;
941 } /* muse_quadrants_trim_image() */
942 
943 /*---------------------------------------------------------------------------*/
956 /*---------------------------------------------------------------------------*/
957 cpl_error_code
958 muse_quadrants_coords_to_raw(cpl_propertylist *aHeader, int *aX, int *aY)
959 {
960  cpl_ensure_code(aX || aY, CPL_ERROR_NULL_INPUT);
961  if (!aHeader) {
962  /* assume standard quadrants */
963  if (aX) {
964  int xraw = *aX + kMusePreOverscanSize; /* add one prescan in any case */
965  if (*aX > kMuseOutputXRight/2) {
966  xraw += 2 * kMusePreOverscanSize; /* add the two overscans, too */
967  } /* if in right quadrant */
968  *aX = xraw;
969  } /* if aX */
970  if (aY) {
971  int yraw = *aY + kMusePreOverscanSize;
972  if (*aY > kMuseOutputYTop/2) {
973  yraw += 2 * kMusePreOverscanSize;
974  } /* if in top quadrant */
975  *aY = yraw;
976  } /* if aY */
977  return CPL_ERROR_NONE;
978  }
979  /* XXX do futher checks if the header was passed */
980  return CPL_ERROR_NONE;
981 } /* muse_quadrants_coords_to_raw() */
982 
983 /*---------------------------------------------------------------------------*/
1004 /*---------------------------------------------------------------------------*/
1005 cpl_boolean
1006 muse_quadrants_verify(cpl_propertylist *aHeader)
1007 {
1008  cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, CPL_FALSE);
1009 
1010  int portx[4], porty[4], nx[4], ny[4],
1011  prex[4], prey[4], overx[4], overy[4];
1012 
1013  /* get the output position for each quadrant */
1014  int binx = muse_pfits_get_binx(aHeader),
1015  biny = muse_pfits_get_biny(aHeader);
1016  unsigned char n; /* quadrant counter */
1017  for (n = 1; n <= 4; n++) {
1018  portx[n-1] = muse_pfits_get_out_output_x(aHeader, n);
1019  porty[n-1] = muse_pfits_get_out_output_y(aHeader, n);
1020  nx[n-1] = muse_pfits_get_out_nx(aHeader, n) / binx;
1021  ny[n-1] = muse_pfits_get_out_ny(aHeader, n) / biny;
1022  prex[n-1] = muse_pfits_get_out_prescan_x(aHeader, n) / binx;
1023  prey[n-1] = muse_pfits_get_out_prescan_y(aHeader, n) / biny;
1024  overx[n-1] = muse_pfits_get_out_overscan_x(aHeader, n) / binx;
1025  overy[n-1] = muse_pfits_get_out_overscan_y(aHeader, n) / biny;
1026 #if 0
1027  cpl_msg_debug(__func__, "quadrant %1hhu: port=%d,%d, n=%d,%d, pre=%d,%d, over=%d,%d",
1028  n+1, portx[n-1], porty[n-1], nx[n-1], ny[n-1],
1029  prex[n-1], prey[n-1], overx[n-1], overy[n-1]);
1030 #endif
1031  }
1032 
1033  cpl_ensure(portx[0] < portx[1], CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);
1034  /* XXX don't check the x locations for the INM */
1035  if (!cpl_propertylist_has(aHeader, "INMMODEL")) {
1036  cpl_ensure(portx[0] < portx[2], CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);
1037  cpl_ensure(portx[0] == portx[3], CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);
1038  }
1039  cpl_ensure(porty[0] == porty[1], CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);
1040  cpl_ensure(porty[0] < porty[2], CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);
1041  cpl_ensure(porty[0] < porty[3], CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);
1042 
1043  for (n = 1; n < 4; n++) {
1044  cpl_ensure(nx[0] == nx[n] && ny[0] == ny[n], CPL_ERROR_INCOMPATIBLE_INPUT,
1045  0);
1046  cpl_ensure(prex[0] == prex[n] && prey[0] == prey[n],
1047  CPL_ERROR_INCOMPATIBLE_INPUT, CPL_FALSE);
1048  cpl_ensure(overx[0] == overx[n] && overy[0] == overy[n],
1049  CPL_ERROR_INCOMPATIBLE_INPUT, CPL_FALSE);
1050  }
1051 
1052  return CPL_TRUE;
1053 } /* muse_quadrants_verify() */
1054 
1055 /*---------------------------------------------------------------------------*/
1072 /*---------------------------------------------------------------------------*/
1073 cpl_size *
1074 muse_quadrants_get_window(const muse_image *aImage, unsigned char aQuadrant)
1075 {
1076  cpl_ensure(aImage && aImage->data && aImage->header, CPL_ERROR_NULL_INPUT,
1077  NULL);
1078  cpl_ensure(aQuadrant >= 1 && aQuadrant <= 4, CPL_ERROR_ILLEGAL_INPUT, NULL);
1079 
1080  cpl_boolean debug = CPL_FALSE;
1081  if (getenv("MUSE_DEBUG_QUADRANTS")) {
1082  debug = atoi(getenv("MUSE_DEBUG_QUADRANTS")) > 0;
1083  }
1084 
1085  /* get the total image size and the output size for each quadrant */
1086  int binx = muse_pfits_get_binx(aImage->header),
1087  biny = muse_pfits_get_biny(aImage->header),
1088  nx[5], ny[5]; /* element 0 is the total size, others are the quadrants */
1089  nx[0] = cpl_image_get_size_x(aImage->data);
1090  ny[0] = cpl_image_get_size_y(aImage->data);
1091  unsigned char n; /* quadrant counter */
1092  for (n = 1; n <= 4; n++) {
1093  nx[n] = muse_pfits_get_out_nx(aImage->header, n) / binx;
1094  ny[n] = muse_pfits_get_out_ny(aImage->header, n) / biny;
1095  }
1096 
1097  cpl_size *window = (cpl_size *)cpl_calloc(sizeof(cpl_size), 4);
1098  switch (aQuadrant) {
1099  case 1: /* bottom left */
1100  window[0] = 1;
1101  window[1] = nx[1];
1102  window[2] = 1;
1103  window[3] = ny[1];
1104  break;
1105  case 2: /* bottom right */
1106  window[0] = nx[1] + 1;
1107  window[1] = nx[1] + nx[2];
1108  window[2] = 1;
1109  window[3] = ny[2];
1110  break;
1111  case 3: /* top right */
1112  window[0] = nx[3] + 1; /* XXX switch for INM */
1113  window[1] = nx[3] + nx[4];
1114  window[2] = ny[2] + 1;
1115  window[3] = ny[2] + ny[4];
1116  break;
1117  case 4: /* top left */
1118  window[0] = 1; /* XXX switch for INM */
1119  window[1] = nx[3];
1120  window[2] = ny[1] + 1;
1121  window[3] = ny[1] + ny[3];
1122  break;
1123  }
1124 
1125  /* if the total image size is equal to the added size of the output ports, *
1126  * then the image is trimmed already, and we can return what we have */
1127  if ((nx[1] + nx[2]) == nx[0] && (ny[1] + ny[3]) == ny[0]) {
1128  if (debug) {
1129  cpl_msg_debug(__func__, "quadrant %d, trimmed: %"CPL_SIZE_FORMAT",%"
1130  CPL_SIZE_FORMAT " -> %"CPL_SIZE_FORMAT",%"CPL_SIZE_FORMAT"",
1131  aQuadrant, window[0], window[2], window[1], window[3]);
1132  }
1133  return window;
1134  }
1135 
1136  /* as the image seems to be untrimmed, add the pre- and over-scan sizes *
1137  * of the relevant regions to the positions of the output window */
1138  int overx[5], overy[5], /* element 0 is unused, for consistency with above! */
1139  prex[5], prey[5];
1140  for (n = 1; n <= 4; n++) {
1141  prex[n] = muse_pfits_get_out_prescan_x(aImage->header, n) / binx;
1142  prey[n] = muse_pfits_get_out_prescan_y(aImage->header, n) / biny;
1143  overx[n] = muse_pfits_get_out_overscan_x(aImage->header, n) / binx;
1144  overy[n] = muse_pfits_get_out_overscan_y(aImage->header, n) / biny;
1145  }
1146 
1147  int addx = 0, addy = 0;
1148  switch (aQuadrant) {
1149  case 1: /* bottom left, only add prescans */
1150  addx = prex[1];
1151  addy = prey[1];
1152  break;
1153  case 2: /* bottom right, add prescans, plus twice the horizontal overscan */
1154  addx = prex[1] + overx[1] + overx[2];
1155  addy = prey[2];
1156  break;
1157  case 3: /* top right, add prescans, plus twice the horizontal+vertical overscan */
1158  addx = prex[3] + overx[3] + overx[4]; /* XXX switch for INM */
1159  addy = prey[1] + overy[1] + overy[3];
1160  break;
1161  case 4: /* top left, add prescans, plus twice the vertical overscan */
1162  addx = prex[3]; /* XXX switch for INM */
1163  addy = prey[2] + overy[2] + overy[4];
1164  break;
1165  }
1166  window[0] += addx;
1167  window[1] += addx;
1168  window[2] += addy;
1169  window[3] += addy;
1170 
1171  if (debug) {
1172  cpl_msg_debug(__func__, "quadrant %d, not trimmed: %"CPL_SIZE_FORMAT",%"
1173  CPL_SIZE_FORMAT " -> %"CPL_SIZE_FORMAT",%"CPL_SIZE_FORMAT,
1174  aQuadrant, window[0], window[2], window[1], window[3]);
1175  }
1176 
1177  return window;
1178 } /* muse_quadrants_get_window() */
1179 
cpl_error_code muse_quadrants_overscan_correct(muse_image *aImage, muse_image *aRefImage)
Adapt bias level to reference image using overscan statistics.
int muse_pfits_get_out_overscan_y(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find out the vertical size of the overscan region of one quadrant
Definition: muse_pfits.c:856
int muse_pfits_get_out_output_y(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find out the vertical location of the output port of one quadrant
Definition: muse_pfits.c:726
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
Definition: muse_image.c:85
cpl_size * muse_quadrants_get_window(const muse_image *aImage, unsigned char aQuadrant)
Determine the data window of a given quadrant on the CCD.
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
Definition: muse_utils.c:98
double muse_pfits_get_gain(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find the detector gain (in units of count/adu)
Definition: muse_pfits.c:686
cpl_image * data
the data extension
Definition: muse_image.h:46
int muse_pfits_get_out_overscan_x(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find out the horizontal size of the overscan region of one quadrant
Definition: muse_pfits.c:834
int muse_cosmics_dcr(muse_image *aImage, unsigned int aXBox, unsigned int aYBox, unsigned int aPasses, float aThres)
Quickly mark cosmic rays in an image using the DCR algorithm.
int muse_pfits_get_out_output_x(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find out the horizontal location of the output port of one quadrant
Definition: muse_pfits.c:704
cpl_image * stat
the statistics extension
Definition: muse_image.h:64
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:40
cpl_error_code muse_quadrants_overscan_polyfit_vertical(muse_image *aImage, unsigned aIgnore, unsigned char aOrder, double aSigma, const double aFRMS, double aFChiSq)
Correct quadrants by polynomial representation of vertical overscan.
cpl_propertylist * header
the FITS header
Definition: muse_image.h:72
cpl_image * dq
the data quality extension
Definition: muse_image.h:56
cpl_boolean muse_quadrants_verify(cpl_propertylist *aHeader)
Verify that quadrant locations and sizes meet the expectations.
int muse_pfits_get_biny(const cpl_propertylist *aHeaders)
find out the binning factor in y direction
Definition: muse_pfits.c:566
cpl_boolean muse_quadrants_overscan_check(muse_image *aImage, muse_image *aRefImage, double aSigma)
Compare overscan statistics of all quadrants to those of reference image.
int muse_pfits_get_out_prescan_y(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find out the vertical size of the prescan region of one quadrant
Definition: muse_pfits.c:812
int muse_pfits_get_out_nx(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find out the horizontal size of the data section of one quadrant
Definition: muse_pfits.c:748
cpl_polynomial * muse_utils_iterate_fit_polynomial(cpl_matrix *aPos, cpl_vector *aVal, cpl_vector *aErr, cpl_table *aExtra, const unsigned int aOrder, const double aRSigma, double *aMSE, double *aChiSq)
Iterate a polynomial fit.
Definition: muse_utils.c:2234
cpl_error_code muse_quadrants_overscan_stats(muse_image *aImage, const char *aRejection, unsigned int aIgnore)
Compute overscan statistics of all quadrants and save in FITS header.
int muse_pfits_get_binx(const cpl_propertylist *aHeaders)
find out the binning factor in x direction
Definition: muse_pfits.c:548
cpl_error_code muse_image_save(muse_image *aImage, const char *aFilename)
Save the three image extensions and the FITS headers of a MUSE image to a file.
Definition: muse_image.c:405
cpl_error_code muse_quadrants_coords_to_raw(cpl_propertylist *aHeader, int *aX, int *aY)
Convert coordinates of a trimmed image to raw-image coordinates.
int muse_pfits_get_out_prescan_x(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find out the horizontal size of the prescan region of one quadrant
Definition: muse_pfits.c:790
muse_image * muse_image_new(void)
Allocate memory for a new muse_image object.
Definition: muse_image.c:66
int muse_pfits_get_out_ny(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find out the vertical size of the data section of one quadrant
Definition: muse_pfits.c:769
muse_image * muse_quadrants_trim_image(muse_image *aImage)
Trim the input image of pre- and over-scan regions of all quadrants.
cpl_size * muse_quadrants_overscan_get_window(const muse_image *aImage, unsigned char aQuadrant, unsigned int aIgnore)
Determine the overscan windows of a given quadrant on the CCD.