MUSE Pipeline Reference Manual  0.18.1
muse_basicproc.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 <stdio.h>
30 #include <float.h>
31 #include <math.h>
32 #include <string.h>
33 #include <cpl.h>
34 
35 #include "muse_basicproc.h"
36 
37 #include "muse_combine.h"
38 #include "muse_pfits.h"
39 #include "muse_quadrants.h"
40 #include "muse_quality.h"
41 #include "muse_utils.h"
42 #include "muse_data_format_z.h"
43 
44 /*----------------------------------------------------------------------------*
45  * Debugging Macros *
46  * Set these to 1 or higher for (lots of) debugging output *
47  *----------------------------------------------------------------------------*/
48 #define GENERATE_TEST_IMAGES 0 /* generate FITS file(s) to be used for testing */
49 
50 /*---------------------------------------------------------------------------*/
57 /*---------------------------------------------------------------------------*/
58 
61 /*---------------------------------------------------------------------------*/
80 /*---------------------------------------------------------------------------*/
82 muse_basicproc_params_new(cpl_parameterlist *aParameters, const char *aPrefix)
83 {
84  muse_basicproc_params *bpars = cpl_calloc(1, sizeof(muse_basicproc_params));
85  cpl_parameter *param;
86  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "overscan");
87  bpars->overscan = cpl_parameter_get_string(param);
88  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "ovscreject");
89  bpars->rejection = cpl_parameter_get_string(param);
90  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "ovscsigma");
91  bpars->ovscsigma = cpl_parameter_get_double(param);
92  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "ovscignore");
93  bpars->ovscignore = cpl_parameter_get_int(param);
94 
95  if (strstr(aPrefix, "muse_scibasic")) {
96  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "cr");
97  bpars->crmethod = cpl_parameter_get_string(param);
98  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "xbox");
99  bpars->dcrxbox = cpl_parameter_get_int(param);
100  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "ybox");
101  bpars->dcrybox = cpl_parameter_get_int(param);
102  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "passes");
103  bpars->dcrpasses = cpl_parameter_get_int(param);
104  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "thres");
105  bpars->dcrthres = cpl_parameter_get_double(param);
106  }
107  return bpars;
108 } /* muse_basicproc_params_new() */
109 
110 /*---------------------------------------------------------------------------*/
115 /*---------------------------------------------------------------------------*/
116 void
118 {
119  cpl_free(aBPars); /* this checks for NULL */
120 }
121 
122 /*---------------------------------------------------------------------------*/
141 /*---------------------------------------------------------------------------*/
142 static cpl_error_code
143 muse_basicproc_verify_setup(const muse_image *aImage, const muse_image *aRef)
144 {
145  cpl_ensure_code(aImage && aRef, CPL_ERROR_NULL_INPUT);
146  cpl_ensure_code(aImage->header && aRef->header, CPL_ERROR_NULL_INPUT);
147  /* shortcuts to the headers */
148  cpl_propertylist *him = aImage->header,
149  *href = aRef->header;
150  /* reference image need to have a processed category (PRO.CATG) */
151  const char *fn1 = cpl_propertylist_get_string(him, MUSE_HDR_TMP_FN),
152  *fn2 = cpl_propertylist_get_string(href, MUSE_HDR_TMP_FN),
153  *catg = muse_pfits_get_pro_catg(href);
154  if (!catg) {
155  cpl_msg_error(__func__, "\"%s\" does not contain a category (ESO.PRO.CATG)!",
156  fn2);
157  return CPL_ERROR_ILLEGAL_INPUT;
158  }
159 
160  muse_ins_mode mode1 = muse_pfits_get_mode(him),
161  mode2 = muse_pfits_get_mode(href);
162  const char *modestr1 = cpl_propertylist_get_string(him, "ESO INS MODE"),
163  *modestr2 = cpl_propertylist_get_string(href, "ESO INS MODE");
164  int binx1 = muse_pfits_get_binx(him),
165  biny1 = muse_pfits_get_biny(him),
166  readid1 = muse_pfits_get_read_id(him),
167  binx2 = muse_pfits_get_binx(href),
168  biny2 = muse_pfits_get_biny(href),
169  readid2 = muse_pfits_get_read_id(href);
170  const char *readname1 = muse_pfits_get_read_name(him),
171  *readname2 = muse_pfits_get_read_name(href),
172  *chipname1 = muse_pfits_get_chip_name(him),
173  *chipid1 = muse_pfits_get_chip_id(him),
174  *chipname2 = muse_pfits_get_chip_name(href),
175  *chipid2 = muse_pfits_get_chip_id(href);
176  cpl_boolean chipinfo = chipname1 && chipid1
177  && chipname2 && chipid2;
178  if (!chipinfo) {
179  cpl_msg_warning(__func__, "CHIP information is missing (ESO.DET.CHIP."
180  "{NAME,ID}) from \"%s\" (%s, %s) or \"%s\" (%s, %s)",
181  fn1, chipname1, chipid1, fn2, chipname2, chipid2);
182  }
183  /* Everything should fail for non-matching binning. */
184  if (binx1 != binx2 || biny1 != biny2) {
185  cpl_msg_error(__func__, "Binning of \"%s\" (%dx%d) and \"%s\" (%dx%d) does "
186  "not match", fn1, binx1, biny1, fn2, binx2, biny2);
187  return CPL_ERROR_TYPE_MISMATCH;
188  }
189 
190  /* The pipeline should refuse to work if the bias is not taken in the *
191  * same read-out as the image that is being bias-subtracted. Hence it *
192  * should give an ERROR message when searching for bias files and stop. */
193  if (!strncmp(catg, "MASTER_BIAS", 12)) {
194  if (readid1 != readid2) {
195  cpl_msg_error(__func__, "Read-out mode of \"%s\" (%d: %s) and \"%s\" (%d:"
196  " %s) does not match", fn1, readid1, readname1, fn2,
197  readid2, readname2);
198  return CPL_ERROR_TYPE_MISMATCH;
199  }
200  if (chipinfo && (strcmp(chipname1, chipname2) || strcmp(chipid1, chipid2))) {
201  cpl_msg_error(__func__, "CHIP information (ESO.DET.CHIP.{NAME,ID}) "
202  "does not match for \"%s\" (%s, %s) and \"%s\" (%s, %s)",
203  fn1, chipname1, chipid1, fn2, chipname2, chipid2);
204  return CPL_ERROR_TYPE_MISMATCH;
205  }
206  } /* if ref is bias */
207 
208  /* We probably need similar guards for the instrument mode, so that one *
209  * cannot use a flat-field in WFM-NOAO-N for data taken with WFM-NOAO-E. */
210  if (!strncmp(catg, "MASTER_FLAT", 12)) {
211  if (mode1 != mode2) {
212  cpl_msg_error(__func__, "Instrument modes for \"%s\" (%s) and \"%s\" (%s)"
213  " do not match", fn1, modestr1, fn2, modestr2);
214  return CPL_ERROR_TYPE_MISMATCH;
215  }
216  } /* if ref is flat */
217 
218  /* XXX add check to not mix WFM and NFM for illuminated exposures */
219 
220  /* It should output WARNINGs (but continue processing), when other *
221  * calibrations are not in the same read-out mode or if the images *
222  * originate from different chips or chip installation dates. */
223  if (readid1 != readid2) {
224  cpl_msg_warning(__func__, "Read-out mode of \"%s\" (%d: %s) and \"%s\" (%d:"
225  " %s) does not match", fn1, readid1, readname1, fn2,
226  readid2, readname2);
227  }
228  if (chipinfo && (strcmp(chipname1, chipname2) || strcmp(chipid1, chipid2))) {
229  cpl_msg_warning(__func__, "CHIP information (ESO.DET.CHIP.{NAME,ID,DATE}) "
230  "does not match for \"%s\" (%s, %s) and \"%s\" (%s, %s)",
231  fn1, chipname1, chipid1, fn2, chipname2, chipid2);
232  }
233 
234  return CPL_ERROR_NONE;
235 } /* muse_basicproc_verify_setup() */
236 
237 /*---------------------------------------------------------------------------*/
250 /*---------------------------------------------------------------------------*/
251 static cpl_error_code
252 muse_basicproc_correct_overscans_vpoly(muse_imagelist *aList,
253  muse_basicproc_params *aBPars)
254 {
255  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
256  cpl_boolean ovscvpoly = aBPars && aBPars->overscan
257  && !strncmp(aBPars->overscan, "vpoly", 5);
258  if (!ovscvpoly) {
259  cpl_msg_debug(__func__, "not vpoly: %s!", aBPars ? aBPars->overscan : "");
260  return CPL_ERROR_NONE;
261  }
262  /* vertical polyfit requested, see if there are more parameters */
263  unsigned char ovscvorder = 3;
264  double frms = 1.01,
265  fchisq = 1.04;
266  char *rest = strchr(aBPars->overscan, ':');
267  if (strlen(aBPars->overscan) > 6 && rest) { /* try to access info after "vpoly:" */
268  ovscvorder = strtol(++rest, &rest, 10);
269  if (strlen(rest) > 0) {
270  frms = strtod(++rest, &rest); /* ++ to skip over the comma */
271  if (strlen(rest) > 0) {
272  fchisq = strtod(++rest, &rest);
273  }
274  }
275  } /* if */
276  cpl_msg_debug(__func__, "is vpoly: %s --> %hhu, %f, %f (ignore=%u, sigma=%f)",
277  aBPars->overscan, ovscvorder, frms, fchisq, aBPars->ovscignore,
278  aBPars->ovscsigma);
279 
280  cpl_error_code rc = CPL_ERROR_NONE;
281  unsigned int k;
282  for (k = 0; k < aList->size; k++) {
283  muse_image *image = muse_imagelist_get(aList, k);
285  ovscvorder, aBPars->ovscsigma,
286  frms, fchisq);
287  if (rc != CPL_ERROR_NONE) {
288  unsigned char ifu = muse_utils_get_ifu(image->header);
289  cpl_msg_error(__func__, "Could not correct quadrants levels using vertical"
290  " overscan fit in IFU %hhu: %s", ifu, cpl_error_get_message());
291  } /* if */
292  } /* for k (all images) */
293  return rc;
294 } /* muse_basicproc_correct_overscans_vpoly() */
295 
296 /*---------------------------------------------------------------------------*/
314 /*---------------------------------------------------------------------------*/
315 static cpl_error_code
316 muse_basicproc_trim_images(muse_imagelist *aList, muse_basicproc_params *aBPars)
317 {
318  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
319 
320  unsigned int k;
321  for (k = 0; k < aList->size; k++) {
322  muse_image *image = muse_imagelist_get(aList, k);
323  if (muse_quadrants_overscan_stats(image, aBPars ? aBPars->rejection : NULL,
324  aBPars ? aBPars->ovscignore : 0)
325  != CPL_ERROR_NONE) {
326  cpl_msg_warning(__func__, "Could not compute overscan statistics in IFU "
327  "%hhu: %s", muse_utils_get_ifu(image->header),
328  cpl_error_get_message());
329  }
330  muse_image *trimmed = muse_quadrants_trim_image(image);
331  cpl_ensure_code(trimmed, cpl_error_get_code());
332 
333  /* setting a new one deletes the old, so no need to free |image| */
334  muse_imagelist_set(aList, trimmed, k);
335  } /* for k (all images) */
336  return muse_imagelist_is_uniform(aList) == 0 ? CPL_ERROR_NONE
337  : CPL_ERROR_ILLEGAL_OUTPUT;
338 } /* muse_basicproc_trim_images() */
339 
340 /*---------------------------------------------------------------------------*/
366 /*---------------------------------------------------------------------------*/
367 static cpl_error_code
368 muse_basicproc_check_overscans(muse_imagelist *aList,
369  muse_processing *aProcessing,
370  muse_basicproc_params *aBPars)
371 {
372  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
373  /* files other than bias are already checked when *
374  * subtracting the bias, so we can skip this check */
375  if (strncmp(aProcessing->inputTag, MUSE_TAG_BIAS, 5)) {
376  return CPL_ERROR_NONE;
377  }
378  if (aList->size < 2) { /* doesn't make sense for single image lists */
379  return CPL_ERROR_NONE;
380  }
381  double sigma = aBPars ? aBPars->ovscsigma : 1.;
382  cpl_boolean ovscoffset = aBPars && aBPars->overscan
383  && !strncmp(aBPars->overscan, "offset", 7);
384 
385  /* header of the first image */
386  muse_image *refimage = muse_imagelist_get(aList, 0);
387  cpl_propertylist *refheader = refimage->header;
388  unsigned char ifu = muse_utils_get_ifu(refheader);
389  cpl_error_code rc = CPL_ERROR_NONE;
390  int n;
391  for (n = 1; n <= 4; n++) {
392  /* create correct header keyword names */
393  char *keywordmean = cpl_sprintf(MUSE_HDR_OVSC_MEAN, n),
394  *keywordstdev = cpl_sprintf(MUSE_HDR_OVSC_STDEV, n);
395 
396  /* overscan stats for first image in list */
397  const char *reffn = cpl_propertylist_get_string(refheader, MUSE_HDR_TMP_FN);
398  float refmean = cpl_propertylist_get_float(refheader, keywordmean),
399  refstdev = cpl_propertylist_get_float(refheader, keywordstdev),
400  hilimit = refmean + sigma * refstdev,
401  lolimit = refmean - sigma * refstdev;
402  /* variables to create average output values */
403  double summean = refmean,
404  sumstdev = pow(refstdev, 2.);
405 
406  /* compare with the other images */
407  unsigned int k;
408  for (k = 1; k < aList->size; k++) {
409  /* if we offset using overscans, do it first as it adapts the statistics */
410  if (ovscoffset) {
412  }
413  cpl_propertylist *h = muse_imagelist_get(aList, k)->header;
414  float mean = cpl_propertylist_get_float(h, keywordmean),
415  stdev = cpl_propertylist_get_float(h, keywordstdev);
416  summean += mean;
417  sumstdev += pow(stdev, 2.) + pow(mean - refmean, 2.);
418  const char *fn = cpl_propertylist_get_string(h, MUSE_HDR_TMP_FN);
419  if (mean > hilimit || mean < lolimit) {
420  cpl_msg_error(__func__, "Overscan of IFU %hhu, quadrant %1hhu of image %u [%s] "
421  "(%.3f+/-%.3f) differs from first image [%s] (%.3f+/-%.3f)!",
422  ifu, n, k+1, fn, mean, stdev, reffn, refmean, refstdev);
423  rc = cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
424  continue; /* debug output only if there was no error */
425  }
426  cpl_msg_debug(__func__, "Overscan of IFU %hhu, quadrant %1hhu of image %u [%s] "
427  "%.3f+/-%.3f (first image [%s] %.3f+/-%.3f)",
428  ifu, n, k+1, fn, mean, stdev, reffn, refmean, refstdev);
429  } /* for k (all images except first) */
430 
431  /* update values in the 1st header to be the averaged values *
432  * which should be propagated to the final combined frame */
433  summean /= aList->size;
434  sumstdev = sqrt(sumstdev) / aList->size;
435  cpl_msg_debug(__func__, "Averaged overscan values in IFU %hhu, quadrant %1hhu: "
436  "%.3f +/- %.3f (%d images)", ifu, n, summean, sumstdev, aList->size);
437  cpl_propertylist_update_float(refheader, keywordmean, summean);
438  cpl_propertylist_update_float(refheader, keywordstdev, sumstdev);
439 
440  cpl_free(keywordmean);
441  cpl_free(keywordstdev);
442  } /* for n (quadrants) */
443 
444  return rc;
445 } /* muse_basicproc_check_overscans() */
446 
447 /*---------------------------------------------------------------------------*/
465 /*---------------------------------------------------------------------------*/
466 static cpl_error_code
467 muse_basicproc_apply_badpix(muse_imagelist *aList, muse_processing *aProcessing,
468  unsigned char aIFU)
469 {
470  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
471  cpl_table *table = muse_table_load(aProcessing, MUSE_TAG_BADPIX_TABLE, aIFU);
472  if (table == NULL) {
473  return CPL_ERROR_NONE; /* file couldn't be loaded */
474  }
475  cpl_error_code rc = muse_cpltable_check(table, muse_badpix_table_def);
476  if (rc != CPL_ERROR_NONE) {
477  cpl_table_delete(table);
478  return CPL_ERROR_INCOMPATIBLE_INPUT;
479  }
480 
481  int nrow = cpl_table_get_nrow(table);
482  unsigned int k, nbadpix = 0;
483  for (k = 0; k < aList->size && rc == CPL_ERROR_NONE; k++) {
484  muse_image *image = muse_imagelist_get(aList, k);
485  /* XXX need to verify the detector/chip properties here, too! */
486  int i;
487  for (i = 0; i < nrow; i++) {
488  int x = (int)cpl_table_get(table, MUSE_BADPIX_X, i, NULL),
489  y = (int)cpl_table_get(table, MUSE_BADPIX_Y, i, NULL),
490  dq = (int)cpl_table_get(table, MUSE_BADPIX_DQ, i, NULL),
491  err;
492  /* OR the data in the DQ extension with what's in the badpix table */
493  rc = cpl_image_set(image->dq, x, y,
494  dq | (int)cpl_image_get(image->dq, x, y, &err));
495  if (k == 0 && rc == CPL_ERROR_NONE) {
496  nbadpix++;
497  }
498  } /* for i (all table rows) */
499  } /* for k (all images) */
500  cpl_table_delete(table);
501  cpl_msg_debug(__func__, "Applied %u bad pixels from %s in IFU %hhu.", nbadpix,
502  MUSE_TAG_BADPIX_TABLE, aIFU);
503  return rc;
504 } /* muse_basicproc_apply_badpix() */
505 
506 /*---------------------------------------------------------------------------*/
515 /*---------------------------------------------------------------------------*/
516 static cpl_error_code
517 muse_basicproc_check_saturation(muse_imagelist *aList)
518 {
519  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
520  unsigned int k;
521  for (k = 0; k < aList->size; k++) {
522  muse_image *image = muse_imagelist_get(aList, k);
523  unsigned char ifu = muse_utils_get_ifu(image->header);
524  int nsaturated = muse_quality_set_saturated(image);
525  /* if we have more than 10% of saturated pixels then something is wrong */
526  int npix = cpl_image_get_size_x(image->data)
527  * cpl_image_get_size_y(image->data);
528  if (nsaturated > (0.1 * npix)) {
529  const char *fn = cpl_propertylist_get_string(image->header,
530  MUSE_HDR_TMP_FN);
531  cpl_msg_warning(__func__, "Raw exposure %u [%s] is probably saturated "
532  "in IFU %hhu (%d of %d pixels)!", k+1, fn, ifu,
533  nsaturated, npix);
534  } else {
535  cpl_msg_debug(__func__, "Raw exposure %u in IFU %hhu (%d of %d pixels "
536  "saturated)!", k+1, ifu, nsaturated, npix);
537  }
538  cpl_propertylist_update_int(image->header, MUSE_HDR_TMP_NSAT, nsaturated);
539  } /* for k (all images) */
540  return CPL_ERROR_NONE;
541 } /* muse_basicproc_check_saturation() */
542 
543 /*---------------------------------------------------------------------------*/
565 /*---------------------------------------------------------------------------*/
566 static cpl_error_code
567 muse_basicproc_apply_bias(muse_imagelist *aList, muse_processing *aProcessing,
568  unsigned char aIFU, muse_basicproc_params *aBPars)
569 {
570  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
571  cpl_frame *biasframe = muse_frameset_find_master(aProcessing->inputFrames,
572  MUSE_TAG_MASTER_BIAS, aIFU);
573  cpl_errorstate prestate = cpl_errorstate_get();
574  if (!biasframe) return CPL_ERROR_NONE;
575  cpl_errorstate_set(prestate);
576  const char *biasname = cpl_frame_get_filename(biasframe);
577  muse_image *biasimage = muse_image_load(biasname);
578  if (!biasimage) {
579  /* remember error message for a while, but reset the state, *
580  * before trying to load from channel extensions */
581  char *errmsg = cpl_strdup(cpl_error_get_message());
582  cpl_errorstate_set(prestate);
583  biasimage = muse_image_load_from_extensions(biasname, aIFU);
584  if (!biasimage) {
585  /* now display both the older and the new error messages, *
586  * so that they cannot be swallowed by parallelization */
587  cpl_msg_error(__func__, "%s", errmsg);
588  cpl_msg_error(__func__, "%s", cpl_error_get_message());
589  cpl_free(errmsg);
590  cpl_frame_delete(biasframe);
591  return cpl_error_get_code();
592  } /* if 2nd load failure */
593  cpl_free(errmsg);
594  cpl_msg_info(__func__, "Bias correction in IFU %hhu using \"%s[CHAN%02hhu."
595  "DATA]\"", aIFU, biasname, aIFU);
596  } else {
597  cpl_msg_info(__func__, "Bias correction in IFU %hhu using \"%s[DATA]\"",
598  aIFU, biasname);
599  }
600  /* add temporary input filename for diagnostics */
601  cpl_propertylist_append_string(biasimage->header, MUSE_HDR_TMP_FN, biasname);
602 
603  cpl_boolean ovscoffset = aBPars && aBPars->overscan
604  && !strncmp(aBPars->overscan, "offset", 7),
605  ovscvpoly = aBPars && aBPars->overscan
606  && !strncmp(aBPars->overscan, "vpoly", 5); /* up to the : */
607  muse_processing_append_used(aProcessing, biasframe, CPL_FRAME_GROUP_CALIB, 0);
608  cpl_error_code rc = CPL_ERROR_NONE;
609  unsigned int k;
610  for (k = 0; k < aList->size && rc == CPL_ERROR_NONE; k++) {
611  muse_image *image = muse_imagelist_get(aList, k);
612  rc = muse_basicproc_verify_setup(image, biasimage);
613  if (rc != CPL_ERROR_NONE) {
614  break;
615  }
616  rc = muse_image_variance_create(image, biasimage);
617  if (ovscoffset) {
618  muse_quadrants_overscan_correct(image, biasimage);
619  } else if (ovscvpoly) {
620  /* create error message and code, if the bias was not *
621  * vpoly-handled, i.e. still has the original bias level *
622  * of ~1000 adu or > 100xsigma above ~zero */
623  cpl_boolean good = muse_quadrants_overscan_check(image, biasimage, 100.);
624  if (!good) {
625  cpl_msg_error(__func__, "Very different overscan levels found between "
626  "%s and raw exposure %u in IFU %hhu: incompatible overscan"
627  " parameters?", MUSE_TAG_MASTER_BIAS, k + 1, aIFU);
628  rc = cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
629  break;
630  } /* if not good */
631  } else {
632  /* just warn above indicated sigma level, ignore failure */
633  muse_quadrants_overscan_check(image, biasimage,
634  aBPars ? aBPars->ovscsigma : 1.);
635  }
636  rc = muse_image_subtract(image, biasimage);
637 #if GENERATE_TEST_IMAGES
638  /* if we need to generate images for automated tests, here is a good *
639  * place to write them out, as they are trimmed and bias corrected */
640  if (k == 0) {
641  muse_image_save(image, "trimmed_bias_sub.fits");
642  }
643 #endif
644  } /* for k (all images) */
645  muse_image_delete(biasimage);
646  return rc;
647 } /* muse_basicproc_apply_bias() */
648 
649 /*---------------------------------------------------------------------------*/
667 /*---------------------------------------------------------------------------*/
668 static cpl_error_code
669 muse_basicproc_correct_overscans_offset(muse_imagelist *aList,
670  muse_processing *aProcessing,
671  muse_basicproc_params *aBPars)
672 {
673  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
674  /* this only makes sense to do for inputs that are bias */
675  if (strncmp(aProcessing->inputTag, MUSE_TAG_BIAS, 5)) {
676  return CPL_ERROR_NONE;
677  }
678  cpl_boolean ovscoffset = aBPars && aBPars->overscan
679  && !strncmp(aBPars->overscan, "offset", 7);
680  if (!ovscoffset) {
681  return CPL_ERROR_NONE; /* no correction necessary */
682  }
683  unsigned char ifu = muse_utils_get_ifu(muse_imagelist_get(aList, 0)->header);
684  cpl_msg_info(__func__, "Running overscan correction on %u %s files in IFU"
685  " %hhu", aList->size, MUSE_TAG_BIAS, ifu);
686  muse_image *ref = muse_imagelist_get(aList, 0);
687  unsigned int k;
688  for (k = 1; k < aList->size; k++) {
690  } /* for k (all images except first) */
691  return CPL_ERROR_NONE;
692 } /* muse_basicproc_correct_overscans_offset() */
693 
694 /*---------------------------------------------------------------------------*/
713 /*---------------------------------------------------------------------------*/
714 static cpl_error_code
715 muse_basicproc_check_gain(muse_imagelist *aList, muse_processing *aProcessing,
716  unsigned char aIFU)
717 {
718  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
719  /* this only makes sense to do for input flat-fields */
720  if (strncmp(aProcessing->inputTag, MUSE_TAG_FLAT, 5)) {
721  return CPL_ERROR_NONE;
722  }
723  cpl_ensure_code(muse_imagelist_get_size(aList) >= 2,
724  CPL_ERROR_INCOMPATIBLE_INPUT);
725 
726  cpl_frame *fbias = muse_frameset_find_master(aProcessing->inputFrames,
727  MUSE_TAG_MASTER_BIAS, aIFU);
728  if (!fbias) {
729  /* it's OK if there is no bias frame, probably we are not in muse_bias */
730  return CPL_ERROR_NONE;
731  }
732  const char *biasname = cpl_frame_get_filename(fbias);
733  cpl_propertylist *hbias = cpl_propertylist_load(biasname, 0);
734  if (!cpl_propertylist_has(hbias, QC_BIAS_MASTER_RON)) {
735  cpl_propertylist_delete(hbias);
736  /* try to load from channel extension */
737  char *extname = cpl_sprintf("CHAN%02hhu.%s", aIFU, EXTNAME_DATA);
738  int extension = cpl_fits_find_extension(biasname, extname);
739  hbias = cpl_propertylist_load(biasname, extension);
740  cpl_free(extname);
741  }
742  cpl_frame_delete(fbias);
743  cpl_image *f1 = muse_imagelist_get(aList, 0)->data,
744  *f2 = muse_imagelist_get(aList, 1)->data;
745  cpl_propertylist *header = muse_imagelist_get(aList, 0)->header;
746  cpl_image *diff = cpl_image_subtract_create(f1, f2);
747 
748  unsigned char n;
749  for (n = 1; n <= 4; n++) {
750  cpl_size *w = muse_quadrants_get_window(muse_imagelist_get(aList, 0), n);
751  double m1 = cpl_image_get_mean_window(f1, w[0], w[2], w[1], w[3]),
752  m2 = cpl_image_get_mean_window(f2, w[0], w[2], w[1], w[3]),
753  sf = cpl_image_get_stdev_window(diff, w[0], w[2], w[1], w[3]);
754  char *keyword = cpl_sprintf(QC_BIAS_MASTERn_PREFIX" MEAN", n);
755  float mb = cpl_propertylist_get_float(hbias, keyword);
756  cpl_free(keyword);
757  keyword = cpl_sprintf(QC_BIAS_MASTER_RON, n);
758  /* the RON formula taken from Howell inverted to give sigma(b1-b2) */
759  double gainheader = muse_pfits_get_gain(header, n),
760  sb = cpl_propertylist_get_float(hbias, keyword) * sqrt(2.)
761  / gainheader, /* gain in count/adu */
762  /* the gain formula taken from Howell: */
763  gain = (m1 + m2 - 2*mb) / (sf*sf - sb*sb),
764  dgain = 1. - gain / gainheader;
765  /* output warning for difference larger than 20%, info message otherwise */
766  if (dgain > 0.2) {
767  cpl_msg_warning(__func__, "IFU %hhu, quadrant %1hhu: estimated gain %.3f "
768  "count/adu but header gives %.3f!", aIFU, n, gain,
769  gainheader);
770  } else {
771  cpl_msg_info(__func__, "IFU %hhu, quadrant %1hhu: estimated gain %.3f "
772  "count/adu (header %.3f, delta %.3f)", aIFU, n, gain,
773  gainheader, dgain);
774  }
775  cpl_free(keyword);
776  cpl_free(w);
777  } /* for n (quadrants) */
778 
779  cpl_image_delete(diff);
780  cpl_propertylist_delete(hbias);
781 
782  return CPL_ERROR_NONE;
783 } /* muse_basicproc_check_gain() */
784 
785 /*---------------------------------------------------------------------------*/
799 /*---------------------------------------------------------------------------*/
800 static cpl_error_code
801 muse_basicproc_adu_to_count(muse_imagelist *aList, muse_processing *aProcessing)
802 {
803  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
804  /* this only makes sense to do for inputs that are not bias */
805  if (!strncmp(aProcessing->inputTag, MUSE_TAG_BIAS, 5)) {
806  return CPL_ERROR_NONE;
807  }
808 
809  unsigned char ifu = muse_utils_get_ifu(muse_imagelist_get(aList, 0)->header);
810  cpl_msg_info(__func__, "Converting %u exposures from adu to count (= electron)"
811  " units in IFU %hhu", aList->size, ifu);
812  cpl_error_code rc = CPL_ERROR_NONE;
813  unsigned int k;
814  for (k = 0; k < aList->size; k++) {
816  } /* for k (all images) */
817  return rc;
818 } /* muse_basicproc_adu_to_count() */
819 
820 /*---------------------------------------------------------------------------*/
836 /*---------------------------------------------------------------------------*/
837 static cpl_error_code
838 muse_basicproc_corr_nonlinearity(muse_imagelist *aList,
839  muse_processing *aProcessing,
840  unsigned char aIFU)
841 {
842  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
843  /* this only makes sense to do for inputs that are not bias */
844  if (!strncmp(aProcessing->inputTag, MUSE_TAG_BIAS, 5)) {
845  return CPL_ERROR_NONE;
846  }
847 
848  cpl_frame *fnonlincorr = muse_frameset_find_master(aProcessing->inputFrames,
849  MUSE_TAG_NONLINCORR, aIFU);
850  if (!fnonlincorr) {
851  /* it's OK if there is no nonlinearity frame, it's optional... */
852  return CPL_ERROR_NONE;
853  }
854  const char *fn = cpl_frame_get_filename(fnonlincorr);
855  /* immediately try to load from channel extension */
856  char *extname = cpl_sprintf("CHAN%02hhu", aIFU);
857  int extension = cpl_fits_find_extension(fn, extname);
858  cpl_propertylist *header = cpl_propertylist_load(fn, extension);
859  cpl_msg_info(__func__, "Correcting nonlinearity in IFU %hhu using \"%s[%s]\"",
860  aIFU, fn, extname);
861  cpl_free(extname);
862  muse_processing_append_used(aProcessing, fnonlincorr, CPL_FRAME_GROUP_CALIB, 0);
863 
864  cpl_error_code rc = CPL_ERROR_NONE;
865  unsigned int k;
866  for (k = 0; k < aList->size; k++) {
867  muse_image *image = muse_imagelist_get(aList, k);
868  int nx = cpl_image_get_size_x(image->data);
869  float *data = cpl_image_get_data_float(image->data),
870  *stat = cpl_image_get_data_float(image->stat);
871 
872  /* XXX need to verify the detector/chip properties here, too! */
873 
874  unsigned char n;
875  for (n = 1; n <= 4; n++) {
876  /* create the 1D nonlinearity polynomial for this quadrant, then read *
877  * all the parameters from the header set the polynomial up accordingly */
878  cpl_polynomial *poly = cpl_polynomial_new(1);
879  char *kw = cpl_sprintf(MUSE_HDR_NONLINn_ORDER, n);
880  unsigned char o, order = cpl_propertylist_get_int(header, kw);
881  cpl_free(kw);
882  for (o = 0; o <= order; o++) {
883  kw = cpl_sprintf(MUSE_HDR_NONLINn_COEFFo, n, o);
884  cpl_size pow = o;
885  cpl_polynomial_set_coeff(poly, &pow,
886  cpl_propertylist_get_double(header, kw));
887  cpl_free(kw);
888  } /* for i (polynomial orders) */
889 #if 1
890  cpl_msg_debug(__func__, "polynomial correction (200..65000 adu): %f, %f, %f, %f",
891  cpl_polynomial_eval_1d(poly, log10(200.), NULL),
892  cpl_polynomial_eval_1d(poly, log10(2000.), NULL),
893  cpl_polynomial_eval_1d(poly, log10(20000.), NULL),
894  cpl_polynomial_eval_1d(poly, log10(65000.), NULL));
895  cpl_polynomial_dump(poly, stdout);
896  fflush(stdout);
897 #endif
898 
899  /* now loop through the full quadrant and correct the scaling */
900  cpl_size *w = muse_quadrants_get_window(image, n);
901  int i;
902  for (i = w[0] - 1; i < w[1]; i++) {
903  int j;
904  for (j = w[2] - 1; j < w[3]; j++) {
905  if (data[i + j*nx] <= 0) {
906  continue; /* don't do anything for non-positive datapoints */
907  }
908  /* compute the percent-level deviation (applying to log10(adu)) */
909  double pcor = cpl_polynomial_eval_1d(poly, log10(data[i + j*nx]), NULL);
910  /* then derive the multiplicative correction factor */
911  double fcor = 1. / (1. + pcor);
912  data[i + j*nx] *= fcor;
913  stat[i + j*nx] *= fcor*fcor;
914  } /* for j (vertical pixels) */
915  } /* for i (horizontal pixels) */
916  cpl_free(w);
917  cpl_polynomial_delete(poly);
918  } /* for n (quadrants) */
919  } /* for k (all images) */
920  cpl_propertylist_delete(header);
921 
922  return rc;
923 } /* mmuse_basicproc_corr_nonlinearity() */
924 
925 /*---------------------------------------------------------------------------*/
941 /*---------------------------------------------------------------------------*/
942 static cpl_error_code
943 muse_basicproc_apply_dark(muse_imagelist *aList, muse_processing *aProcessing,
944  unsigned char aIFU)
945 {
946  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
947 
948  cpl_frame *darkframe = muse_frameset_find_master(aProcessing->inputFrames,
949  MUSE_TAG_MASTER_DARK, aIFU);
950  cpl_errorstate prestate = cpl_errorstate_get();
951  if (!darkframe) return CPL_ERROR_NONE;
952  cpl_errorstate_set(prestate);
953  const char *darkname = cpl_frame_get_filename(darkframe);
954  muse_image *darkimage = muse_image_load(darkname);
955  if (!darkimage) {
956  char *errmsg = cpl_strdup(cpl_error_get_message());
957  cpl_errorstate_set(prestate);
958  darkimage = muse_image_load_from_extensions(darkname, aIFU);
959  if (!darkimage) {
960  cpl_msg_error(__func__, "%s", errmsg);
961  cpl_msg_error(__func__, "%s", cpl_error_get_message());
962  cpl_free(errmsg);
963  cpl_frame_delete(darkframe);
964  return cpl_error_get_code();
965  } /* if 2nd load failure */
966  cpl_free(errmsg);
967  cpl_msg_info(__func__, "Dark correction in IFU %hhu using \"%s[CHAN%02hhu."
968  "DATA]\"", aIFU, darkname, aIFU);
969  } else {
970  cpl_msg_info(__func__, "Dark correction in IFU %hhu using \"%s[DATA]\"",
971  aIFU, darkname);
972  }
973  cpl_propertylist_append_string(darkimage->header, MUSE_HDR_TMP_FN, darkname);
974 
975  muse_processing_append_used(aProcessing, darkframe, CPL_FRAME_GROUP_CALIB, 0);
976  cpl_error_code rc = CPL_ERROR_NONE;
977  unsigned int k;
978  for (k = 0; k < aList->size; k++) {
979  muse_image *image = muse_imagelist_get(aList, k);
980  rc = muse_basicproc_verify_setup(image, darkimage);
981  if (rc != CPL_ERROR_NONE) {
982  break;
983  }
984 
985  /* duplicate the dark, because the scaling will destroy its normalization */
986  muse_image *dark = muse_image_duplicate(darkimage);
987  /* scale and subtract the dark by comparing exposure time *
988  * of the dark and the other image */
989  double scale = muse_pfits_get_exptime(image->header);
990  if (muse_pfits_get_exptime(dark->header) > 0) {
991  scale /= muse_pfits_get_exptime(dark->header);
992  }
993  rc = muse_image_scale(dark, scale);
994  rc = muse_image_subtract(image, dark);
995 
996  muse_image_delete(dark);
997  } /* for k (all images) */
998  muse_image_delete(darkimage);
999 
1000  return rc;
1001 } /* muse_basicproc_apply_dark() */
1002 
1003 /*---------------------------------------------------------------------------*/
1018 /*---------------------------------------------------------------------------*/
1019 static cpl_error_code
1020 muse_basicproc_apply_cr(muse_imagelist *aList,
1021  muse_basicproc_params *aBPars)
1022 {
1023  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
1024 
1025  /* if we didn't get cr parameters we were not supposed *
1026  * to find cosmic rays, so just return without error */
1027  cpl_boolean isdcr = aBPars && aBPars->crmethod
1028  && !strncmp(aBPars->crmethod, "dcr", 4);
1029  if (!isdcr) {
1030  return CPL_ERROR_NONE;
1031  }
1032 
1033  cpl_error_code rc = CPL_ERROR_NONE;
1034  unsigned int k;
1035  for (k = 0; k < aList->size; k++) {
1036  muse_image *image = muse_imagelist_get(aList, k);
1037  unsigned char ifu = muse_utils_get_ifu(image->header);
1038  int ncr = 0;
1039  ncr = muse_cosmics_dcr(image, aBPars->dcrxbox, aBPars->dcrybox,
1040  aBPars->dcrpasses, aBPars->dcrthres);
1041  if (ncr <= 0) {
1042  cpl_msg_error(__func__, "Cosmic ray rejection using DCR in IFU %hhu failed"
1043  " for image %u (ncr = %d): %s", ifu, k+1, ncr,
1044  cpl_error_get_message());
1045  rc = cpl_error_get_code();
1046  } else {
1047  cpl_msg_info(__func__, "Cosmic ray rejection using DCR in IFU %hhu found "
1048  "%d affected pixels in image %u", ifu, ncr, k+1);
1049  }
1050  } /* for k (all images) */
1051 
1052  return rc;
1053 } /* muse_basicproc_apply_cr() */
1054 
1055 /*---------------------------------------------------------------------------*/
1070 /*---------------------------------------------------------------------------*/
1071 static cpl_error_code
1072 muse_basicproc_apply_flat(muse_imagelist *aList, muse_processing *aProcessing,
1073  unsigned char aIFU)
1074 {
1075  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
1076 
1077  cpl_frame *flatframe = muse_frameset_find_master(aProcessing->inputFrames,
1078  MUSE_TAG_MASTER_FLAT, aIFU);
1079  cpl_errorstate prestate = cpl_errorstate_get();
1080  if (!flatframe) return CPL_ERROR_NONE;
1081  cpl_errorstate_set(prestate);
1082  const char *flatname = cpl_frame_get_filename(flatframe);
1083  prestate = cpl_errorstate_get();
1084  muse_image *flatimage = muse_image_load(flatname);
1085  if (!flatimage) {
1086  char *errmsg = cpl_strdup(cpl_error_get_message());
1087  cpl_errorstate_set(prestate);
1088  flatimage = muse_image_load_from_extensions(flatname, aIFU);
1089  if (!flatimage) {
1090  cpl_msg_error(__func__, "%s", errmsg);
1091  cpl_msg_error(__func__, "%s", cpl_error_get_message());
1092  cpl_free(errmsg);
1093  cpl_frame_delete(flatframe);
1094  return cpl_error_get_code();
1095  } /* if 2nd load failure */
1096  cpl_free(errmsg);
1097  cpl_msg_info(__func__, "Flat-field correction in IFU %hhu using \"%s[CHAN%02hhu."
1098  "DATA]\"", aIFU, flatname, aIFU);
1099  } else {
1100  cpl_msg_info(__func__, "Flat-field correction in IFU %hhu using \"%s[DATA]\"",
1101  aIFU, flatname);
1102  }
1103  /* copy QC parameter containing integrated flux */
1104  double fflux = cpl_propertylist_get_double(flatimage->header,
1105  QC_FLAT_MASTER_INTFLUX);
1106  cpl_propertylist_append_string(flatimage->header, MUSE_HDR_TMP_FN, flatname);
1107  muse_processing_append_used(aProcessing, flatframe, CPL_FRAME_GROUP_CALIB, 0);
1108 
1109  cpl_error_code rc = CPL_ERROR_NONE;
1110  unsigned int k;
1111  for (k = 0; k < aList->size; k++) {
1112  cpl_msg_debug(__func__, "Flat-field division in IFU %hhu of image %u of %u",
1113  aIFU, k+1, aList->size);
1114  muse_image *image = muse_imagelist_get(aList, k);
1115  rc = muse_basicproc_verify_setup(image, flatimage);
1116  if (rc != CPL_ERROR_NONE) {
1117  break;
1118  }
1119  rc = muse_image_divide(image, flatimage);
1120  cpl_propertylist_update_double(image->header, MUSE_HDR_FLAT_FLUX_LAMP, fflux);
1121  } /* for k (all images) */
1122 
1123  muse_image_delete(flatimage);
1124  return rc;
1125 } /* muse_basicproc_apply_flat() */
1126 
1127 /*---------------------------------------------------------------------------*/
1143 /*---------------------------------------------------------------------------*/
1144 static cpl_error_code
1145 muse_basicproc_apply_skyflat(muse_imagelist *aList,
1146  muse_processing *aProcessing, unsigned char aIFU)
1147 {
1148  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
1149 
1150  cpl_frame *flatframe = muse_frameset_find_master(aProcessing->inputFrames,
1151  MUSE_TAG_MASTER_SKYFLAT, aIFU);
1152  cpl_errorstate prestate = cpl_errorstate_get();
1153  if (!flatframe) return CPL_ERROR_NONE;
1154  cpl_errorstate_set(prestate);
1155  const char *flatname = cpl_frame_get_filename(flatframe);
1156 
1157  /* copy QC parameter containing integrated flux */
1158  cpl_propertylist *hflat = cpl_propertylist_load(flatname, 0);
1159  if (!cpl_propertylist_has(hflat, QC_SKYFLAT_MASTER_INTFLUX)) {
1160  cpl_propertylist_delete(hflat);
1161  /* try to load from channel extension */
1162  char *extname = cpl_sprintf("CHAN%02hhu.%s", aIFU, EXTNAME_DATA);
1163  int extension = cpl_fits_find_extension(flatname, extname);
1164  hflat = cpl_propertylist_load(flatname, extension);
1165  cpl_msg_info(__func__, "Propagate SkyFlat flux value in IFU %hhu from "
1166  "\"%s[%s]\"", aIFU, flatname, extname);
1167  cpl_free(extname);
1168  } else {
1169  cpl_msg_info(__func__, "Propagate SkyFlat flux value in IFU %hhu from "
1170  "\"%s\"", aIFU, flatname);
1171  }
1172  double fflux = cpl_propertylist_get_double(hflat, QC_SKYFLAT_MASTER_INTFLUX);
1173  cpl_propertylist_delete(hflat);
1174  muse_processing_append_used(aProcessing, flatframe, CPL_FRAME_GROUP_CALIB, 0);
1175 
1176  cpl_error_code rc = CPL_ERROR_NONE;
1177  unsigned int k;
1178  for (k = 0; k < aList->size; k++) {
1179  cpl_msg_debug(__func__, "SkyFlat propagation in IFU %hhu to image %u of %u",
1180  aIFU, k+1, aList->size);
1181  muse_image *image = muse_imagelist_get(aList, k);
1182  /* XXX need to verify the detector/chip properties here, too! */
1183  cpl_propertylist_update_double(image->header, MUSE_HDR_FLAT_FLUX_SKY, fflux);
1184  } /* for k (all images) */
1185  return rc;
1186 } /* muse_basicproc_apply_skyflat() */
1187 
1188 /*---------------------------------------------------------------------------*/
1209 /*---------------------------------------------------------------------------*/
1210 static muse_imagelist *
1211 muse_basicproc_load_raw(muse_processing *aProcessing, unsigned char aIFU)
1212 {
1213  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
1214 
1215  /* usedFrames contains the list of input files that we actually *use* */
1216  cpl_frameset *usedFrames = muse_frameset_check_raw(aProcessing->inputFrames,
1217  aProcessing->inputTag,
1218  aIFU);
1219  cpl_ensure(usedFrames, CPL_ERROR_NULL_INPUT, NULL);
1220 
1221  muse_imagelist *images = muse_imagelist_new();
1222  unsigned int k = 0;
1223  cpl_size iframe, nframes = cpl_frameset_get_size(usedFrames);
1224  for (iframe = 0; iframe < nframes; iframe++) {
1225  cpl_frame *frame = cpl_frameset_get_position(usedFrames, iframe);
1226  const char *fileName = cpl_frame_get_filename(frame);
1227  int extension = muse_utils_get_extension_for_ifu(fileName, aIFU);
1228  if (extension == -1) {
1229  cpl_msg_error(__func__, "\"%s\" does not contain data from IFU %hhu",
1230  fileName, aIFU);
1231  break;
1232  }
1233  muse_image *raw = muse_image_load_from_raw(fileName, extension);
1234  if (!raw) {
1235  continue;
1236  }
1237  /* add temporary input filename for diagnostics */
1238  cpl_propertylist_append_string(raw->header, MUSE_HDR_TMP_FN, fileName);
1239 
1240  muse_imagelist_set(images, raw, k);
1241  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_RAW, 1);
1242  k++;
1243  } /* for frame (all usedFrames) */
1244  cpl_frameset_delete(usedFrames);
1245 
1246  /* some checks if we ended up with a valid imagelist */
1247  if (!images->size) {
1248  if (cpl_error_get_code() != MUSE_ERROR_CHIP_NOT_LIVE) {
1249  cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
1250  cpl_msg_error(__func__, "No raw images loaded for IFU %hhu", aIFU);
1251  }
1252  muse_imagelist_delete(images); /* still free the structure */
1253  return NULL;
1254  }
1255  if (muse_imagelist_is_uniform(images) != 0) {
1256  cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
1257  cpl_msg_error(__func__, "Non-uniform imagelist for IFU %hhu", aIFU);
1258  muse_imagelist_delete(images);
1259  return NULL;
1260  }
1261 
1262  return images;
1263 } /* muse_basicproc_load_raw() */
1264 
1265 /*---------------------------------------------------------------------------*/
1306 /*---------------------------------------------------------------------------*/
1308 muse_basicproc_load(muse_processing *aProcessing, unsigned char aIFU,
1309  muse_basicproc_params *aBPars)
1310 {
1311  if (muse_processing_check_input(aProcessing, aIFU) != CPL_ERROR_NONE) {
1312  return NULL;
1313  }
1314  muse_imagelist *images = muse_basicproc_load_raw(aProcessing, aIFU);
1315  if (!images) {
1316  return NULL;
1317  }
1318  cpl_errorstate prestate = cpl_errorstate_get();
1319  if (muse_basicproc_apply_badpix(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1320  cpl_msg_warning(__func__, "Applying bad pixel table to IFU %hhu failed: %s",
1321  aIFU, cpl_error_get_message());
1322  cpl_errorstate_set(prestate); /* this is not critical, continue */
1323  }
1324  if (muse_basicproc_check_saturation(images) != CPL_ERROR_NONE) {
1325  muse_imagelist_delete(images);
1326  return NULL;
1327  }
1328  if (muse_basicproc_correct_overscans_vpoly(images, aBPars)
1329  != CPL_ERROR_NONE) {
1330  muse_imagelist_delete(images); /* this failures should be fatal! */
1331  return NULL;
1332  }
1333  if (muse_basicproc_trim_images(images, aBPars) != CPL_ERROR_NONE) {
1334  muse_imagelist_delete(images);
1335  return NULL;
1336  }
1337  if (muse_basicproc_check_overscans(images, aProcessing, aBPars)
1338  != CPL_ERROR_NONE) { /* for BIAS files only */
1339  // XXX this already does the offset-correction?!?
1340  muse_imagelist_delete(images);
1341  return NULL;
1342  }
1343  muse_basicproc_check_gain(images, aProcessing, aIFU);
1344  if (muse_basicproc_apply_bias(images, aProcessing, aIFU, aBPars)
1345  != CPL_ERROR_NONE) {
1346  muse_imagelist_delete(images);
1347  return NULL;
1348  }
1349  if (muse_basicproc_correct_overscans_offset(images, aProcessing, aBPars)
1350  != CPL_ERROR_NONE) { /* for BIAS files only */
1351  // XXX if muse_basicproc_check_overscans() already does the correction,
1352  // what does this do?!?
1353  cpl_msg_warning(__func__, "Bias-level correction using overscans failed in "
1354  "IFU %hhu: %s", aIFU, cpl_error_get_message());
1355  }
1356  if (muse_basicproc_corr_nonlinearity(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1357  muse_imagelist_delete(images);
1358  return NULL;
1359  }
1360  if (muse_basicproc_adu_to_count(images, aProcessing) != CPL_ERROR_NONE) {
1361  muse_imagelist_delete(images);
1362  return NULL;
1363  }
1364  if (muse_basicproc_apply_dark(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1365  muse_imagelist_delete(images);
1366  return NULL;
1367  }
1368  if (muse_basicproc_apply_cr(images, aBPars) != CPL_ERROR_NONE) {
1369  muse_imagelist_delete(images);
1370  return NULL;
1371  }
1372  if (muse_basicproc_apply_flat(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1373  muse_imagelist_delete(images);
1374  return NULL;
1375  }
1376  if (muse_basicproc_apply_skyflat(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1377  muse_imagelist_delete(images);
1378  return NULL;
1379  }
1380  return images;
1381 } /* muse_basicproc_load() */
1382 
1383 /*---------------------------------------------------------------------------*/
1396 /*---------------------------------------------------------------------------*/
1398 muse_basicproc_load_reduced(muse_processing *aProcessing, unsigned char aIFU)
1399 {
1400  muse_imagelist *images = muse_imagelist_new();
1401  cpl_frameset *usedFrames = muse_frameset_find(aProcessing->inputFrames,
1402  aProcessing->inputTag,
1403  aIFU, CPL_FALSE);
1404  cpl_size iframe, nframes = cpl_frameset_get_size(usedFrames);
1405  for (iframe = 0; iframe < nframes; iframe++) {
1406  cpl_frame *frame = cpl_frameset_get_position(usedFrames, iframe);
1407  cpl_errorstate prestate = cpl_errorstate_get();
1408  const char *imagename = cpl_frame_get_filename(frame);
1409  muse_image *image = muse_image_load(imagename);
1410  if (!image) {
1411  cpl_errorstate_set(prestate);
1412  image = muse_image_load_from_extensions(imagename, aIFU);
1413  }
1414  muse_imagelist_set(images, image, iframe);
1415  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_RAW, 1);
1416  } /* for iframe (all used frames) */
1417  cpl_frameset_delete(usedFrames);
1418  return images;
1419 } /* muse_basicproc_load_reduced() */
1420 
1421 /*----------------------------------------------------------------------------*/
1447 /*----------------------------------------------------------------------------*/
1448 static int
1449 muse_basicproc_combine_compare_lamp(const cpl_frame *aFrame1, const cpl_frame *aFrame2)
1450 {
1451  cpl_ensure(aFrame1 && aFrame2, CPL_ERROR_NULL_INPUT, -1);
1452  const char *fn1 = cpl_frame_get_filename(aFrame1),
1453  *fn2 = cpl_frame_get_filename(aFrame2);
1454  cpl_propertylist *head1 = cpl_propertylist_load(fn1, 0),
1455  *head2 = cpl_propertylist_load(fn2, 0);
1456  if (!head1 || !head2) {
1457  cpl_propertylist_delete(head1); /* in case one was loaded... */
1458  cpl_propertylist_delete(head2);
1459  return -1;
1460  }
1461 
1462  /* Loop through all lamps in the header and find their status. The first *
1463  * missing shutter entry will cause the FITS query to throw an error, that's *
1464  * when we stop. Otherwise, we are done when the lamp status is not equal. */
1465  int nlamp = 1, status1, status2;
1466  cpl_errorstate prestate = cpl_errorstate_get();
1467  do {
1468  /* ensure that we are dealing with the same lamps! */
1469  const char *name1 = muse_pfits_get_lamp_name(head1, nlamp),
1470  *name2 = muse_pfits_get_lamp_name(head2, nlamp);
1471  cpl_errorstate_set(prestate); /* lamps may be missing */
1472  if (name1 && name2 && strncmp(name1, name2, strlen(name1) + 1)) {
1473  cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
1474  "Files \"%s\" and \"%s\" have incompatible lamp "
1475  "setups", fn1, fn2);
1476  cpl_propertylist_delete(head1);
1477  cpl_propertylist_delete(head2);
1478  return -1;
1479  }
1480  name1 = muse_pfits_get_shut_name(head1, nlamp);
1481  name2 = muse_pfits_get_shut_name(head2, nlamp);
1482  if (name1 && name2 && strncmp(name1, name2, strlen(name1) + 1)) {
1483  cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
1484  "Files \"%s\" and \"%s\" have incompatible shutter "
1485  "setups", fn1, fn2);
1486  cpl_propertylist_delete(head1);
1487  cpl_propertylist_delete(head2);
1488  return -1;
1489  }
1490 
1491  status1 = muse_pfits_get_lamp_status(head1, nlamp);
1492  status2 = muse_pfits_get_lamp_status(head2, nlamp);
1493  cpl_errorstate_set(prestate); /* lamps may be missing */
1494  if (status1 != status2) {
1495  break;
1496  }
1497  status1 = muse_pfits_get_shut_status(head1, nlamp);
1498  status2 = muse_pfits_get_shut_status(head2, nlamp);
1499  if (status1 != status2) {
1500  break;
1501  }
1502  nlamp++;
1503  } while (cpl_errorstate_is_equal(prestate));
1504  cpl_errorstate_set(prestate);
1505 
1506  cpl_propertylist_delete(head1);
1507  cpl_propertylist_delete(head2);
1508  return status1 == status2;
1509 } /* muse_basicproc_combine_compare_lamp() */
1510 
1511 /*---------------------------------------------------------------------------*/
1537 /*---------------------------------------------------------------------------*/
1540  unsigned char aIFU,
1541  muse_basicproc_params *aBPars,
1542  cpl_frameset ***aLabeledFrames)
1543 {
1544  if (aLabeledFrames) { /* NULL out return pointer, in case it's given... */
1545  *aLabeledFrames = NULL; /* ... so it's always NULL in case of problems */
1546  }
1547  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
1548 
1549  /* find out different lamps in input */
1550  cpl_frameset *rawframes = muse_frameset_find(aProcessing->inputFrames,
1551  aProcessing->inputTag, aIFU,
1552  CPL_FALSE);
1553  char *prefix = cpl_sprintf("muse.%s", aProcessing->recipeName);
1554  muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
1555  prefix);
1556  cpl_free(prefix);
1557 #if 0
1558  printf("rawframes\n");
1559  cpl_frameset_dump(rawframes, stdout);
1560  fflush(stdout);
1561 #endif
1562  cpl_size nlabels,
1563  *labels = cpl_frameset_labelise(rawframes,
1564  muse_basicproc_combine_compare_lamp,
1565  &nlabels);
1566  if (!labels || nlabels <= 1) {
1567  /* if labeling didn't work, process the list of one lamp and return */
1568  cpl_free(labels);
1569  cpl_frameset_delete(rawframes);
1570  muse_imagelist *list = muse_basicproc_load(aProcessing, aIFU, aBPars),
1571  *images = NULL;
1572  if (nlabels == 1) {
1573  muse_image *image = muse_combine_images(cpars, list);
1574  images = muse_imagelist_new();
1575  muse_imagelist_set(images, image, 0);
1576  if (aLabeledFrames) {
1577  *aLabeledFrames = cpl_calloc(1, sizeof(cpl_frameset *));
1578  (*aLabeledFrames)[0] = cpl_frameset_duplicate(aProcessing->usedFrames);
1579  } /* if */
1580  } /* if only one label */
1581  muse_imagelist_delete(list);
1582  muse_combinepar_delete(cpars);
1583  return images;
1584  } /* if one of no labels */
1585 
1586 #if 0
1587  cpl_array *alabels = cpl_array_wrap_int(labels, cpl_frameset_get_size(rawframes));
1588  cpl_array_dump(alabels, 0, 1000, stdout);
1589  fflush(stdout);
1590  cpl_array_unwrap(alabels);
1591 #endif
1592  /* output list of lampwise combined images */
1593  muse_imagelist *images = muse_imagelist_new();
1594  if (aLabeledFrames) {
1595  *aLabeledFrames = cpl_calloc(nlabels, sizeof(cpl_frameset *));
1596  }
1597 
1598  /* duplicate aProcessing into a local structure the contents of which *
1599  * we can manipulate here (don't change it directly for threadsafety!) */
1600  muse_processing *proc = cpl_malloc(sizeof(muse_processing));
1601  memcpy(proc, aProcessing, sizeof(muse_processing));
1602  cpl_frameset *inframes = proc->inputFrames;
1603  /* copy frames with all extra frames somewhere else */
1604  cpl_frameset *auxframes = muse_frameset_find(inframes, aProcessing->inputTag,
1605  aIFU, CPL_TRUE);
1606  /* loop through labels for all lamps */
1607  int ilabel, ipos = 0;
1608  for (ilabel = 0; ilabel < nlabels; ilabel++) {
1609  /* create new sub-frameset for this lamp */
1610  cpl_frameset *frames = cpl_frameset_extract(rawframes, labels, ilabel);
1611  /* append other files for the initial processing */
1612  cpl_frameset_join(frames, auxframes);
1613  /* substitute aProcessing->inputFrames */
1614  proc->inputFrames = frames;
1615  /* load and combine frames for this sub-frameset */
1616  muse_imagelist *list = muse_basicproc_load(proc, aIFU, aBPars);
1617  /* reinstate original aProcessing->inputFrames */
1618  proc->inputFrames = inframes;
1619  if (!list) { /* break this loop to fail the function below */
1620  muse_imagelist_delete(images);
1621  cpl_frameset_delete(frames);
1622  images = NULL;
1623  /* if muse_basicproc_load() fails then because of some missing *
1624  * calibration, and then it will already fail for the first *
1625  * ilabel; it is therefore enough to free the pointer */
1626  if (aLabeledFrames) {
1627  cpl_free(*aLabeledFrames);
1628  *aLabeledFrames = NULL;
1629  }
1630  break;
1631  }
1632 
1633  muse_image *lampimage = muse_combine_images(cpars, list);
1634  if (!lampimage) {
1635  cpl_msg_error(__func__, "Image combination failed for IFU %hhu for lamp "
1636  "with label %d of %"CPL_SIZE_FORMAT, aIFU, ilabel + 1, nlabels);
1637  muse_imagelist_delete(list);
1638  cpl_frameset_delete(frames);
1639  continue;
1640  }
1641 
1642  if (aLabeledFrames) {
1643  /* if a given frame was used now, copy its group */
1644  cpl_size iframe, nframes = cpl_frameset_get_size(frames);
1645  for (iframe = 0; iframe < nframes; iframe++) {
1646  cpl_frame *frame = cpl_frameset_get_position(frames, iframe);
1647  const char *fn = cpl_frame_get_filename(frame),
1648  *tag = cpl_frame_get_tag(frame);
1649  cpl_size iuframe, nuframes = cpl_frameset_get_size(aProcessing->usedFrames);
1650  for (iuframe = 0;
1651  (iuframe < nuframes) && fn && tag; /* only check with valid info */
1652  iuframe++) {
1653  cpl_frame *uframe = cpl_frameset_get_position(aProcessing->usedFrames,
1654  iuframe);
1655  const char *fnu = cpl_frame_get_filename(uframe),
1656  *tagu = cpl_frame_get_tag(uframe);
1657  if (fnu && !strncmp(fn, fnu, strlen(fn) + 1) &&
1658  tagu && !strncmp(tag, tagu, strlen(tag) + 1)) {
1659  cpl_frame_set_group(frame, cpl_frame_get_group(uframe));
1660  break;
1661  }
1662  } /* for uframe (all usedFrames) */
1663  } /* for frame */
1664  (*aLabeledFrames)[ipos] = frames;
1665  } else {
1666  cpl_frameset_delete(frames);
1667  }
1668 
1669  /* transfer NSATURATION headers from invidual images to lamp-combined image */
1670  unsigned int k;
1671  for (k = 0; k < muse_imagelist_get_size(list); k++) {
1672  char *keyword = cpl_sprintf(QC_WAVECAL_PREFIXi" "QC_BASIC_NSATURATED, k+1);
1673  int nsaturated = cpl_propertylist_get_int(muse_imagelist_get(list, k)->header,
1674  MUSE_HDR_TMP_NSAT);
1675  cpl_propertylist_update_int(lampimage->header, keyword, nsaturated);
1676  cpl_free(keyword);
1677  }
1678  muse_imagelist_delete(list);
1679  /* append to imagelist */
1680  muse_imagelist_set(images, lampimage, ipos++);
1681  } /* for ilabel (labels) */
1682  cpl_free(labels);
1683  cpl_free(proc);
1684  muse_combinepar_delete(cpars);
1685  cpl_frameset_delete(rawframes);
1686  cpl_frameset_delete(auxframes);
1687 
1688  if (images && muse_imagelist_get_size(images) == 0) {
1689  muse_imagelist_delete(images);
1690  images = NULL;
1691  if (aLabeledFrames) {
1692  cpl_free(*aLabeledFrames);
1693  *aLabeledFrames = NULL;
1694  }
1695  }
1696 
1697  return images;
1698 } /* muse_basicproc_combine_images_lampwise() */
1699 
1700 /*---------------------------------------------------------------------------*/
1719 /*---------------------------------------------------------------------------*/
1720 cpl_error_code
1722  double aHalfWidth, double aBinWidth)
1723 {
1724  cpl_ensure_code(aPt && aLines, CPL_ERROR_NULL_INPUT);
1725  cpl_ensure_code(cpl_array_get_type(aLines) == CPL_TYPE_DOUBLE ||
1726  cpl_array_get_type(aLines) == CPL_TYPE_FLOAT,
1727  CPL_ERROR_ILLEGAL_INPUT);
1728 
1729  double lmin = cpl_propertylist_get_float(aPt->header, MUSE_HDR_PT_LLO),
1730  lmax = cpl_propertylist_get_float(aPt->header, MUSE_HDR_PT_LHI);
1731  double shift = 0., wshift = 0.;
1732  cpl_array *errors = cpl_array_new(4, CPL_TYPE_DOUBLE);
1733  int i, n = cpl_array_get_size(aLines), nvalid = 0;
1734  for (i = 0; i < n; i++) {
1735  int err;
1736  double lambda = cpl_array_get(aLines, i, &err);
1737  if (err || lambda >= lmax || lambda <= lmin) {
1738  cpl_msg_debug(__func__, "Invalid wavelength at position %d of %d in "
1739  "skylines", i + 1, n);
1740  continue;
1741  }
1742  nvalid++;
1743  double center = muse_utils_pixtable_fit_line_gaussian(aPt, lambda, aHalfWidth,
1744  aBinWidth, NULL, errors),
1745  cerr = cpl_array_get_double(errors, 0, NULL);
1746  shift += (lambda - center) / cerr;
1747  wshift += 1. / cerr;
1748  cpl_msg_debug(__func__, "dlambda = %.4f +/- %.4f (for skyline at %.4f "
1749  "Angstrom)", lambda - center, cerr, lambda);
1750  } /* for i (all entries in aLines) */
1751  cpl_array_delete(errors);
1752  shift /= wshift;
1753  if (nvalid > 0 && isfinite(shift)) {
1754  cpl_msg_info(__func__, "Skyline correction (%d lines): shifting data of IFU "
1755  "%hhu by %.4f Angstrom", nvalid, muse_utils_get_ifu(aPt->header),
1756  shift);
1757  cpl_table_add_scalar(aPt->table, MUSE_PIXTABLE_LAMBDA, shift);
1758  cpl_propertylist_update_float(aPt->header, QC_SCIBASIC_SHIFT, shift);
1759  } else {
1760  cpl_propertylist_update_float(aPt->header, QC_SCIBASIC_SHIFT, 0.);
1761  }
1762  return CPL_ERROR_NONE;
1763 } /* muse_basicproc_shift_pixtable() */
1764 
1765 /*---------------------------------------------------------------------------*/
1777 /*---------------------------------------------------------------------------*/
1778 cpl_error_code
1779 muse_basicproc_stats_append_header(cpl_image *aImage, cpl_propertylist *aHeader,
1780  const char *aPrefix, unsigned aStats)
1781 {
1782  cpl_ensure_code(aImage, CPL_ERROR_NULL_INPUT);
1783 
1784  int nx = cpl_image_get_size_x(aImage),
1785  ny = cpl_image_get_size_y(aImage);
1786  return muse_basicproc_stats_append_header_window(aImage, aHeader, aPrefix,
1787  aStats, 1, 1, nx, ny);
1788 } /* muse_basicproc_stats_append_header() */
1789 
1790 /*---------------------------------------------------------------------------*/
1807 /*---------------------------------------------------------------------------*/
1808 cpl_error_code
1810  cpl_propertylist *aHeader,
1811  const char *aPrefix, unsigned aStats,
1812  int aX1, int aY1, int aX2, int aY2)
1813 {
1814  cpl_ensure_code(aImage && aHeader, CPL_ERROR_NULL_INPUT);
1815  cpl_ensure_code(aPrefix, CPL_ERROR_ILLEGAL_INPUT);
1816  cpl_ensure_code(aPrefix, CPL_ERROR_ILLEGAL_INPUT);
1817 
1818  cpl_stats *stats = cpl_stats_new_from_image_window(aImage, aStats,
1819  aX1, aY1, aX2, aY2);
1820  if (!stats) {
1821  return cpl_error_get_code();
1822  }
1823 
1824  char keyword[KEYWORD_LENGTH];
1825  if (aStats & CPL_STATS_MEDIAN) {
1826  snprintf(keyword, KEYWORD_LENGTH, "%s MEDIAN", aPrefix);
1827  cpl_propertylist_append_float(aHeader, keyword,
1828  cpl_stats_get_median(stats));
1829  }
1830  if (aStats & CPL_STATS_MEAN) {
1831  snprintf(keyword, KEYWORD_LENGTH, "%s MEAN", aPrefix);
1832  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_mean(stats));
1833  }
1834  if (aStats & CPL_STATS_STDEV) {
1835  snprintf(keyword, KEYWORD_LENGTH, "%s STDEV", aPrefix);
1836  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_stdev(stats));
1837  }
1838  if (aStats & CPL_STATS_MIN) {
1839  snprintf(keyword, KEYWORD_LENGTH, "%s MIN", aPrefix);
1840  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_min(stats));
1841  }
1842  if (aStats & CPL_STATS_MAX) {
1843  snprintf(keyword, KEYWORD_LENGTH, "%s MAX", aPrefix);
1844  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_max(stats));
1845  }
1846  if (aStats & CPL_STATS_FLUX) {
1847  snprintf(keyword, KEYWORD_LENGTH, "%s INTFLUX", aPrefix);
1848  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_flux(stats));
1849  }
1850 
1851  cpl_stats_delete(stats);
1852 
1853  return CPL_ERROR_NONE;
1854 } /* muse_basicproc_stats_append_header_window() */
1855 
1856 /*---------------------------------------------------------------------------*/
1871 /*---------------------------------------------------------------------------*/
1872 cpl_error_code
1873 muse_basicproc_qc_saturated(muse_image *aImage, const char *aPrefix)
1874 {
1875  cpl_ensure_code(aImage && aImage->dq && aImage->header && aPrefix,
1876  CPL_ERROR_NULL_INPUT);
1877 
1878  cpl_mask *mask = cpl_mask_threshold_image_create(aImage->dq,
1879  EURO3D_SATURATED - 0.1,
1880  EURO3D_SATURATED + 0.1);
1881  int nsaturated = cpl_mask_count(mask);
1882  cpl_mask_delete(mask);
1883  /* check if the prefix has a trailing space, add it if not */
1884  char *keyword = NULL;
1885  if (aPrefix[strlen(aPrefix)-1] == ' ') {
1886  keyword = cpl_sprintf("%s%s", aPrefix, QC_BASIC_NSATURATED);
1887  } else {
1888  keyword = cpl_sprintf("%s %s", aPrefix, QC_BASIC_NSATURATED);
1889  }
1890  cpl_error_code rc = cpl_propertylist_update_int(aImage->header, keyword,
1891  nsaturated);
1892  cpl_free(keyword);
1893  return rc;
1894 } /* muse_basicproc_qc_saturated() */
1895 
cpl_error_code muse_quadrants_overscan_correct(muse_image *aImage, muse_image *aRefImage)
Adapt bias level to reference image using overscan statistics.
muse_imagelist * muse_basicproc_load(muse_processing *aProcessing, unsigned char aIFU, muse_basicproc_params *aBPars)
Load the raw input files from disk and do basic processing.
const char * inputTag
Structure definition for a collection of muse_images.
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
Definition: muse_image.c:84
int muse_image_divide(muse_image *aImage, muse_image *aDivisor)
Divide a muse_image by another with correct treatment of bad pixels and variance. ...
Definition: muse_image.c:623
cpl_size * muse_quadrants_get_window(const muse_image *aImage, unsigned char aQuadrant)
Determine the data window of a given quadrant on the CCD.
int muse_image_scale(muse_image *aImage, double aScale)
Scale a muse_image with correct treatment of variance.
Definition: muse_image.c:687
int muse_pfits_get_read_id(const cpl_propertylist *aHeaders)
find out the readout mode id
Definition: muse_pfits.c:296
int muse_utils_get_extension_for_ifu(const char *aFilename, unsigned char aIFU)
Return extension number that corresponds to this IFU/channel number.
Definition: muse_utils.c:115
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
Definition: muse_utils.c:95
muse_imagelist * muse_basicproc_combine_images_lampwise(muse_processing *aProcessing, unsigned char aIFU, muse_basicproc_params *aBPars, cpl_frameset ***aLabeledFrames)
Combine several images into a lampwise image list.
double muse_utils_pixtable_fit_line_gaussian(muse_pixtable *aPixtable, double aLambda, double aHalfWidth, double aBinSize, cpl_array *aResults, cpl_array *aErrors)
Fit a 1D Gaussian to a given wavelength range in a pixel table.
Definition: muse_utils.c:2033
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:470
cpl_image * data
the data extension
Definition: muse_image.h:47
muse_image * muse_image_load_from_raw(const char *aFilename, int aExtension)
Load raw image into the data extension of a MUSE image.
Definition: muse_image.c:286
int muse_pfits_get_shut_status(const cpl_propertylist *aHeaders, int aShutter)
query the status of one shutter
Definition: muse_pfits.c:1199
#define MUSE_HDR_PT_LHI
FITS header keyword contains the upper limit of the data in spectral direction.
const char * rejection
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.
muse_image * muse_image_duplicate(const muse_image *aImage)
Duplicate the three image extensions and the FITS headers of a MUSE image.
Definition: muse_image.c:494
cpl_image * stat
the statistics extension
Definition: muse_image.h:65
int muse_image_subtract(muse_image *aImage, muse_image *aSubtract)
Subtract a muse_image from another with correct treatment of bad pixels and variance.
Definition: muse_image.c:577
void muse_imagelist_delete(muse_imagelist *aList)
Free the memory of the MUSE image list.
const char * recipeName
muse_imagelist * muse_basicproc_load_reduced(muse_processing *aProcessing, unsigned char aIFU)
Load reduced input files from disk.
muse_basicproc_params * muse_basicproc_params_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new structure of basic processing parameters.
cpl_frameset * muse_frameset_check_raw(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU)
return frameset containing good raw input data
Definition: muse_utils.c:232
muse_image * muse_combine_images(muse_combinepar *aCPars, muse_imagelist *aImages)
Combine several images into one.
Definition: muse_combine.c:742
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:41
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.
unsigned int ovscignore
const char * muse_pfits_get_shut_name(const cpl_propertylist *aHeaders, int aShutter)
query the name of one shutter
Definition: muse_pfits.c:1178
cpl_table * table
The pixel table.
void muse_basicproc_params_delete(muse_basicproc_params *aBPars)
Free a structure of basic processing parameters.
cpl_propertylist * header
the FITS header
Definition: muse_image.h:73
int muse_image_variance_create(muse_image *aImage, muse_image *aBias)
Create the photon noise-based variance in the stat extension.
Definition: muse_image.c:731
cpl_error_code muse_cpltable_check(const cpl_table *aTable, const muse_cpltable_def *aDef)
Check whether the table contains the fields of the definition.
unsigned int muse_imagelist_get_size(muse_imagelist *aList)
Return the number of stored images.
void muse_combinepar_delete(muse_combinepar *aCPars)
Clear the combination parameters.
Definition: muse_combine.c:716
cpl_image * dq
the data quality extension
Definition: muse_image.h:57
int muse_pfits_get_biny(const cpl_propertylist *aHeaders)
find out the binning factor in y direction
Definition: muse_pfits.c:350
Structure definition of MUSE pixel table.
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.
const char * muse_pfits_get_lamp_name(const cpl_propertylist *aHeaders, int aLamp)
query the name of one lamp
Definition: muse_pfits.c:1136
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
cpl_error_code muse_basicproc_stats_append_header_window(cpl_image *aImage, cpl_propertylist *aHeader, const char *aPrefix, unsigned aStats, int aX1, int aY1, int aX2, int aY2)
Compute image statistics of an image window and add them to a header.
int muse_quality_set_saturated(muse_image *aImage)
Set all pixels above the saturation limit in the bad pixel image.
Definition: muse_quality.c:469
const char * muse_pfits_get_read_name(const cpl_propertylist *aHeaders)
find out the readout mode name
Definition: muse_pfits.c:314
int muse_imagelist_is_uniform(muse_imagelist *aList)
Check that all images in the muse_imagelist have the same size.
const muse_cpltable_def muse_badpix_table_def[]
muse_combinepar * muse_combinepar_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new set of combination parameters.
Definition: muse_combine.c:673
void muse_processing_append_used(muse_processing *aProcessing, cpl_frame *aFrame, cpl_frame_group aGroup, int aDuplicate)
Add a frame to the set of used frames.
#define MUSE_HDR_PT_LLO
FITS header keyword contains the lower limit of the data in spectral direction.
const char * muse_pfits_get_chip_id(const cpl_propertylist *aHeaders)
find out the chip id
Definition: muse_pfits.c:386
cpl_error_code muse_basicproc_shift_pixtable(muse_pixtable *aPt, cpl_array *aLines, double aHalfWidth, double aBinWidth)
Compute wavelength corrections for science data based on reference sky lines.
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.
cpl_error_code muse_basicproc_stats_append_header(cpl_image *aImage, cpl_propertylist *aHeader, const char *aPrefix, unsigned aStats)
Compute image statistics of an image and add them to a header.
int muse_pfits_get_binx(const cpl_propertylist *aHeaders)
find out the binning factor in x direction
Definition: muse_pfits.c:332
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:396
muse_image * muse_image_load(const char *aFilename)
Load the three extensions and the FITS headers of a MUSE image from a file.
Definition: muse_image.c:222
int muse_pfits_get_lamp_status(const cpl_propertylist *aHeaders, int aLamp)
query the status of one lamp
Definition: muse_pfits.c:1157
muse_imagelist * muse_imagelist_new(void)
Create a new (empty) MUSE image list.
double muse_pfits_get_exptime(const cpl_propertylist *aHeaders)
find out the exposure time
Definition: muse_pfits.c:278
cpl_table * muse_table_load(muse_processing *aProcessing, const char *aTag, unsigned char aIFU)
load a table according to its tag and IFU/channel number
Definition: muse_utils.c:452
cpl_error_code muse_processing_check_input(muse_processing *aProcessing, unsigned char aIFU)
Check the input files for completeness.
Structure of basic processing parameters.
const char * muse_pfits_get_pro_catg(const cpl_propertylist *aHeaders)
find out the PRO category
Definition: muse_pfits.c:142
unsigned int size
cpl_error_code muse_image_adu_to_count(muse_image *aImage)
Convert the data units from raw adu to count (= electron) units.
Definition: muse_image.c:795
cpl_frameset * muse_frameset_find(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU, cpl_boolean aInvert)
return frameset containing data from an IFU/channel with a certain tag
Definition: muse_utils.c:154
cpl_parameter * muse_cplparamerterlist_find_prefix(cpl_parameterlist *aParameters, const char *aPrefix, const char *aName)
Return the full recipe parameter belonging to prefix and shortname.
muse_image * muse_image_load_from_extensions(const char *aFilename, unsigned char aIFU)
Load the three extensions and the FITS headers of a MUSE image from extensions of a merged file...
Definition: muse_image.c:256
cpl_frameset * inputFrames
cpl_frameset * usedFrames
cpl_error_code muse_imagelist_set(muse_imagelist *aList, muse_image *aImage, unsigned int aIdx)
Set the muse_image of given list index.
const char * muse_pfits_get_chip_name(const cpl_propertylist *aHeaders)
find out the chip name
Definition: muse_pfits.c:368
cpl_frame * muse_frameset_find_master(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU)
find the master frame according to its CCD number and tag
Definition: muse_utils.c:425
muse_ins_mode muse_pfits_get_mode(const cpl_propertylist *aHeaders)
find out the observation mode
Definition: muse_pfits.c:1003
cpl_parameterlist * parameters
muse_image * muse_quadrants_trim_image(muse_image *aImage)
Trim the input image of pre- and over-scan regions of all quadrants.
cpl_propertylist * header
The FITS header.
cpl_error_code muse_basicproc_qc_saturated(muse_image *aImage, const char *aPrefix)
Add QC parameter about saturated pixels to a muse_image.