MUSE Pipeline Reference Manual  1.0.2
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_strdup(cpl_parameter_get_string(param));
88  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "ovscreject");
89  bpars->rejection = cpl_strdup(cpl_parameter_get_string(param));
90  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "ovscsigma");
91  cpl_errorstate state = cpl_errorstate_get();
92  bpars->ovscsigma = cpl_parameter_get_double(param);
93  if (!cpl_errorstate_is_equal(state)) { /* try again as int, may be misidentified */
94  cpl_errorstate_set(state);
95  bpars->ovscsigma = cpl_parameter_get_int(param);
96  }
97  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "ovscignore");
98  bpars->ovscignore = cpl_parameter_get_int(param);
99 
100  if (strstr(aPrefix, "muse_scibasic")) {
101  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "cr");
102  bpars->crmethod = cpl_strdup(cpl_parameter_get_string(param));
103  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "xbox");
104  bpars->dcrxbox = cpl_parameter_get_int(param);
105  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "ybox");
106  bpars->dcrybox = cpl_parameter_get_int(param);
107  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "passes");
108  bpars->dcrpasses = cpl_parameter_get_int(param);
109  param = muse_cplparamerterlist_find_prefix(aParameters, aPrefix, "thres");
110  state = cpl_errorstate_get();
111  bpars->dcrthres = cpl_parameter_get_double(param);
112  if (!cpl_errorstate_is_equal(state)) { /* try again as int */
113  cpl_errorstate_set(state);
114  bpars->dcrthres = cpl_parameter_get_int(param);
115  }
116  } /* if scibasic prefix */
117  return bpars;
118 } /* muse_basicproc_params_new() */
119 
120 /*---------------------------------------------------------------------------*/
136 /*---------------------------------------------------------------------------*/
138 muse_basicproc_params_new_from_propertylist(const cpl_propertylist *aHeader)
139 {
140  cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, NULL);
141 
142  /* create a parameter list from the input header, so *
143  * that we can give it to muse_basicproc_params_new() */
144  cpl_parameterlist *parlist = muse_cplparameterlist_from_propertylist(aHeader, 1);
145  cpl_ensure(parlist, CPL_ERROR_ILLEGAL_INPUT, NULL);
146  const char *recipe = cpl_propertylist_get_string(aHeader, "ESO PRO REC1 ID");
147  char *prefix = cpl_sprintf("muse.%s", recipe);
148  muse_basicproc_params *bpars = muse_basicproc_params_new(parlist, prefix);
149  cpl_parameterlist_delete(parlist);
150  cpl_free(prefix);
151  return bpars;
152 } /* muse_basicproc_params_new_from_propertylist() */
153 
154 /*---------------------------------------------------------------------------*/
161 /*---------------------------------------------------------------------------*/
162 void
164 {
165  if (!aBPars) {
166  return;
167  }
168  cpl_free(aBPars->overscan);
169  cpl_free(aBPars->rejection);
170  cpl_free(aBPars->crmethod);
171  cpl_free(aBPars);
172 }
173 
174 /*---------------------------------------------------------------------------*/
193 /*---------------------------------------------------------------------------*/
194 static cpl_error_code
195 muse_basicproc_verify_setup(const muse_image *aImage, const muse_image *aRef)
196 {
197  cpl_ensure_code(aImage && aRef, CPL_ERROR_NULL_INPUT);
198  cpl_ensure_code(aImage->header && aRef->header, CPL_ERROR_NULL_INPUT);
199  /* shortcuts to the headers */
200  cpl_propertylist *him = aImage->header,
201  *href = aRef->header;
202  /* reference image need to have a processed category (PRO.CATG) */
203  const char *fn1 = cpl_propertylist_get_string(him, MUSE_HDR_TMP_FN),
204  *fn2 = cpl_propertylist_get_string(href, MUSE_HDR_TMP_FN),
205  *catg = muse_pfits_get_pro_catg(href);
206  if (!catg) {
207  cpl_msg_error(__func__, "\"%s\" does not contain a category (ESO.PRO.CATG)!",
208  fn2);
209  return CPL_ERROR_ILLEGAL_INPUT;
210  }
211 
212  muse_ins_mode mode1 = muse_pfits_get_mode(him),
213  mode2 = muse_pfits_get_mode(href);
214  const char *modestr1 = muse_pfits_get_insmode(him),
215  *modestr2 = muse_pfits_get_insmode(href),
216  *rawtag = cpl_propertylist_get_string(him, MUSE_HDR_TMP_INTAG);
217  int binx1 = muse_pfits_get_binx(him),
218  biny1 = muse_pfits_get_biny(him),
219  readid1 = muse_pfits_get_read_id(him),
220  binx2 = muse_pfits_get_binx(href),
221  biny2 = muse_pfits_get_biny(href),
222  readid2 = muse_pfits_get_read_id(href);
223  const char *readname1 = muse_pfits_get_read_name(him),
224  *readname2 = muse_pfits_get_read_name(href),
225  *chipname1 = muse_pfits_get_chip_name(him),
226  *chipid1 = muse_pfits_get_chip_id(him),
227  *chipname2 = muse_pfits_get_chip_name(href),
228  *chipid2 = muse_pfits_get_chip_id(href);
229  cpl_boolean chipinfo = chipname1 && chipid1
230  && chipname2 && chipid2;
231  if (!chipinfo) {
232  cpl_msg_warning(__func__, "CHIP information is missing (ESO.DET.CHIP."
233  "{NAME,ID}) from \"%s\" (%s, %s) or \"%s\" (%s, %s)",
234  fn1, chipname1, chipid1, fn2, chipname2, chipid2);
235  }
236  /* Everything should fail for non-matching binning. */
237  if (binx1 != binx2 || biny1 != biny2) {
238  cpl_msg_error(__func__, "Binning of \"%s\" (%dx%d) and \"%s\" (%dx%d) does "
239  "not match", fn1, binx1, biny1, fn2, binx2, biny2);
240  return CPL_ERROR_TYPE_MISMATCH;
241  }
242 
243  /* The pipeline should refuse to work if the bias is not taken in the *
244  * same read-out as the image that is being bias-subtracted. Hence it *
245  * should give an ERROR message when searching for bias files and stop. */
246  if (!strncmp(catg, "MASTER_BIAS", 12)) {
247  if (readid1 != readid2) {
248  cpl_msg_error(__func__, "Read-out mode of \"%s\" (%d: %s) and \"%s\" (%d:"
249  " %s) does not match", fn1, readid1, readname1, fn2,
250  readid2, readname2);
251  return CPL_ERROR_TYPE_MISMATCH;
252  }
253  if (chipinfo && (strcmp(chipname1, chipname2) || strcmp(chipid1, chipid2))) {
254  cpl_msg_error(__func__, "CHIP information (ESO.DET.CHIP.{NAME,ID}) "
255  "does not match for \"%s\" (%s, %s) and \"%s\" (%s, %s)",
256  fn1, chipname1, chipid1, fn2, chipname2, chipid2);
257  return CPL_ERROR_TYPE_MISMATCH;
258  }
259  } /* if ref is bias */
260 
261  /* We probably need similar guards for the instrument mode, so that one *
262  * cannot use a flat-field in WFM-NOAO-N for data taken with WFM-NOAO-E. */
263  if (!strncmp(catg, "MASTER_FLAT", 12)) {
264  if (mode1 != mode2) {
265  if (rawtag && !strncmp(rawtag, MUSE_TAG_ILLUM, strlen(MUSE_TAG_ILLUM) + 1)) {
266  /* ignore mode differences between ILLUM and other calibrations */
267  cpl_msg_debug(__func__, "Instrument modes for \"%s\" (%s, is %s) and \"%s\""
268  " (%s) do not match", fn1, modestr1, rawtag, fn2, modestr2);
269  } else {
270  cpl_msg_error(__func__, "Instrument modes for \"%s\" (%s) and \"%s\" (%s)"
271  " do not match", fn1, modestr1, fn2, modestr2);
272  return CPL_ERROR_TYPE_MISMATCH;
273  } /* else */
274  } /* if modes different */
275  } /* if ref is flat */
276 
277  /* XXX add check to not mix WFM and NFM for illuminated exposures */
278 
279  /* It should output WARNINGs (but continue processing), when other *
280  * calibrations are not in the same read-out mode or if the images *
281  * originate from different chips or chip installation dates. */
282  if (readid1 != readid2) {
283  cpl_msg_warning(__func__, "Read-out mode of \"%s\" (%d: %s) and \"%s\" (%d:"
284  " %s) does not match", fn1, readid1, readname1, fn2,
285  readid2, readname2);
286  }
287  if (chipinfo && (strcmp(chipname1, chipname2) || strcmp(chipid1, chipid2))) {
288  cpl_msg_warning(__func__, "CHIP information (ESO.DET.CHIP.{NAME,ID,DATE}) "
289  "does not match for \"%s\" (%s, %s) and \"%s\" (%s, %s)",
290  fn1, chipname1, chipid1, fn2, chipname2, chipid2);
291  }
292 
293  return CPL_ERROR_NONE;
294 } /* muse_basicproc_verify_setup() */
295 
296 /*---------------------------------------------------------------------------*/
311 /*---------------------------------------------------------------------------*/
312 static cpl_error_code
313 muse_basicproc_overscans_compute_stats(muse_imagelist *aList,
314  muse_basicproc_params *aBPars)
315 {
316  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
317  unsigned int k;
318  for (k = 0; k < aList->size; k++) {
319  muse_image *image = muse_imagelist_get(aList, k);
320  if (muse_quadrants_overscan_stats(image, aBPars ? aBPars->rejection : NULL,
321  aBPars ? aBPars->ovscignore : 0)
322  != CPL_ERROR_NONE) {
323  cpl_msg_warning(__func__, "Could not compute overscan statistics in IFU "
324  "%hhu of exposure %u: %s", muse_utils_get_ifu(image->header),
325  k+1, cpl_error_get_message());
326  } /* if */
327  } /* for k (all images) */
328  return CPL_ERROR_NONE;
329 } /* muse_basicproc_overscans_compute_stats() */
330 
331 /*---------------------------------------------------------------------------*/
344 /*---------------------------------------------------------------------------*/
345 static cpl_error_code
346 muse_basicproc_correct_overscans_vpoly(muse_imagelist *aList,
347  muse_basicproc_params *aBPars)
348 {
349  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
350  cpl_boolean ovscvpoly = aBPars && aBPars->overscan
351  && !strncmp(aBPars->overscan, "vpoly", 5);
352  if (!ovscvpoly) {
353  cpl_msg_debug(__func__, "not vpoly: %s!", aBPars ? aBPars->overscan : "");
354  return CPL_ERROR_NONE;
355  }
356  /* vertical polyfit requested, see if there are more parameters */
357  unsigned char ovscvorder = 5;
358  double frms = 1.01,
359  fchisq = 1.04;
360  char *rest = strchr(aBPars->overscan, ':');
361  if (strlen(aBPars->overscan) > 6 && rest) { /* try to access info after "vpoly:" */
362  ovscvorder = strtol(++rest, &rest, 10);
363  if (strlen(rest) > 0) {
364  frms = strtod(++rest, &rest); /* ++ to skip over the comma */
365  if (strlen(rest) > 0) {
366  fchisq = strtod(++rest, &rest);
367  }
368  }
369  } /* if */
370  cpl_msg_debug(__func__, "vpoly: %s (vorder=%hhu, frms=%f, fchisq=%f, ignore=%u,"
371  " sigma=%.3f)", aBPars->overscan, ovscvorder, frms, fchisq,
372  aBPars->ovscignore, aBPars->ovscsigma);
373 
374  cpl_error_code rc = CPL_ERROR_NONE;
375  unsigned int k;
376  for (k = 0; k < aList->size; k++) {
377  muse_image *image = muse_imagelist_get(aList, k);
379  ovscvorder, aBPars->ovscsigma,
380  frms, fchisq);
381  if (rc != CPL_ERROR_NONE) {
382  unsigned char ifu = muse_utils_get_ifu(image->header);
383  cpl_msg_error(__func__, "Could not correct quadrants levels using vertical"
384  " overscan fit in IFU %hhu: %s", ifu, cpl_error_get_message());
385  } /* if */
386  } /* for k (all images) */
387  return rc;
388 } /* muse_basicproc_correct_overscans_vpoly() */
389 
390 /*---------------------------------------------------------------------------*/
404 /*---------------------------------------------------------------------------*/
405 static cpl_error_code
406 muse_basicproc_trim_images(muse_imagelist *aList)
407 {
408  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
409 
410  unsigned int k;
411  for (k = 0; k < aList->size; k++) {
412  muse_image *image = muse_imagelist_get(aList, k);
413  muse_image *trimmed = muse_quadrants_trim_image(image);
414  cpl_ensure_code(trimmed, cpl_error_get_code());
415 
416  /* setting a new one deletes the old, so no need to free |image| */
417  muse_imagelist_set(aList, trimmed, k);
418  } /* for k (all images) */
419  return muse_imagelist_is_uniform(aList) == 0 ? CPL_ERROR_NONE
420  : CPL_ERROR_ILLEGAL_OUTPUT;
421 } /* muse_basicproc_trim_images() */
422 
423 /*---------------------------------------------------------------------------*/
441 /*---------------------------------------------------------------------------*/
442 static cpl_error_code
443 muse_basicproc_correct_overscans_offset(muse_imagelist *aList,
444  muse_processing *aProcessing,
445  muse_basicproc_params *aBPars)
446 {
447  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
448  /* this only makes sense to do for inputs that are bias */
449  if (!muse_processing_check_intags(aProcessing, MUSE_TAG_BIAS, 5)) {
450  return CPL_ERROR_NONE;
451  }
452  cpl_boolean ovscoffset = aBPars && aBPars->overscan
453  && !strncmp(aBPars->overscan, "offset", 7);
454  if (!ovscoffset) {
455  return CPL_ERROR_NONE; /* no correction necessary */
456  }
457  unsigned char ifu = muse_utils_get_ifu(muse_imagelist_get(aList, 0)->header);
458  cpl_msg_info(__func__, "Running overscan correction using %u %s images in IFU"
459  " %hhu", aList->size, MUSE_TAG_BIAS, ifu);
460  muse_image *ref = muse_imagelist_get(aList, 0);
461  unsigned int k;
462  for (k = 1; k < aList->size; k++) {
464  } /* for k (all images except first) */
465  return CPL_ERROR_NONE;
466 } /* muse_basicproc_correct_overscans_offset() */
467 
468 /*---------------------------------------------------------------------------*/
493 /*---------------------------------------------------------------------------*/
494 static cpl_error_code
495 muse_basicproc_check_overscans(muse_imagelist *aList,
496  muse_processing *aProcessing,
497  muse_basicproc_params *aBPars)
498 {
499  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
500  /* files other than bias are already checked when *
501  * subtracting the bias, so we can skip this check */
502  if (!muse_processing_check_intags(aProcessing, MUSE_TAG_BIAS, 5)) {
503  return CPL_ERROR_NONE;
504  }
505  if (aList->size < 2) { /* doesn't make sense for single image lists */
506  return CPL_ERROR_NONE;
507  }
508  cpl_boolean ovscnone = aBPars && aBPars->overscan
509  && !strncmp(aBPars->overscan, "none", 5);
510  if (!ovscnone) { /* check not needed */
511  return CPL_ERROR_NONE;
512  }
513  double sigma = aBPars ? aBPars->ovscsigma : 1.;
514 
515  /* header of the first image */
516  muse_image *refimage = muse_imagelist_get(aList, 0);
517  cpl_propertylist *refheader = refimage->header;
518  cpl_error_code rc = CPL_ERROR_NONE;
519  unsigned char n, ifu = muse_utils_get_ifu(refheader);
520  for (n = 1; n <= 4; n++) {
521  /* create correct header keyword names */
522  char *keywordmean = cpl_sprintf(MUSE_HDR_OVSC_MEAN, n),
523  *keywordstdev = cpl_sprintf(MUSE_HDR_OVSC_STDEV, n);
524 
525  /* overscan stats for first image in list */
526  const char *reffn = cpl_propertylist_get_string(refheader, MUSE_HDR_TMP_FN);
527  float refmean = cpl_propertylist_get_float(refheader, keywordmean),
528  refstdev = cpl_propertylist_get_float(refheader, keywordstdev),
529  hilimit = refmean + sigma * refstdev,
530  lolimit = refmean - sigma * refstdev;
531  /* variables to create average output values */
532  double summean = refmean,
533  sumstdev = pow(refstdev, 2.);
534 
535  /* compare with the other images */
536  unsigned int k;
537  for (k = 1; k < aList->size; k++) {
538  cpl_propertylist *h = muse_imagelist_get(aList, k)->header;
539  float mean = cpl_propertylist_get_float(h, keywordmean),
540  stdev = cpl_propertylist_get_float(h, keywordstdev);
541  summean += mean;
542  sumstdev += pow(stdev, 2.) + pow(mean - refmean, 2.);
543  const char *fn = cpl_propertylist_get_string(h, MUSE_HDR_TMP_FN);
544  if (mean > hilimit || mean < lolimit) {
545  cpl_msg_error(__func__, "Overscan of IFU %hhu, quadrant %1hhu of image %u [%s] "
546  "(%.3f+/-%.3f) differs from first image [%s] (%.3f+/-%.3f)!",
547  ifu, n, k+1, fn, mean, stdev, reffn, refmean, refstdev);
548  rc = cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
549  continue; /* debug output only if there was no error */
550  }
551  cpl_msg_debug(__func__, "Overscan of IFU %hhu, quadrant %1hhu of image %u [%s] "
552  "%.3f+/-%.3f (first image [%s] %.3f+/-%.3f)",
553  ifu, n, k+1, fn, mean, stdev, reffn, refmean, refstdev);
554  } /* for k (all images except first) */
555 
556  /* update values in the 1st header to be the averaged values *
557  * which should be propagated to the final combined frame */
558  summean /= aList->size;
559  sumstdev = sqrt(sumstdev) / aList->size;
560  cpl_msg_debug(__func__, "Averaged overscan values in IFU %hhu, quadrant %1hhu: "
561  "%.3f +/- %.3f (%d images)", ifu, n, summean, sumstdev, aList->size);
562  cpl_propertylist_update_float(refheader, keywordmean, summean);
563  cpl_propertylist_update_float(refheader, keywordstdev, sumstdev);
564 
565  cpl_free(keywordmean);
566  cpl_free(keywordstdev);
567  } /* for n (quadrants) */
568 
569  return rc;
570 } /* muse_basicproc_check_overscans() */
571 
572 /*---------------------------------------------------------------------------*/
590 /*---------------------------------------------------------------------------*/
591 static cpl_error_code
592 muse_basicproc_apply_badpix(muse_imagelist *aList, muse_processing *aProcessing,
593  unsigned char aIFU)
594 {
595  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
596  cpl_table *table = muse_table_load(aProcessing, MUSE_TAG_BADPIX_TABLE, aIFU);
597  if (table == NULL) {
598  return CPL_ERROR_NONE; /* file couldn't be loaded */
599  }
600  cpl_error_code rc = muse_cpltable_check(table, muse_badpix_table_def);
601  if (rc != CPL_ERROR_NONE) {
602  cpl_table_delete(table);
603  return CPL_ERROR_INCOMPATIBLE_INPUT;
604  }
605 
606  int nrow = cpl_table_get_nrow(table);
607  unsigned int k, nbadpix = 0, /* could transferred bad pixels */
608  nbadpos = 0; /* and entries with bad positions */
609  for (k = 0; k < aList->size && rc == CPL_ERROR_NONE; k++) {
610  muse_image *image = muse_imagelist_get(aList, k);
611  /* XXX need to verify the detector/chip properties here, too! */
612  int i;
613  for (i = 0; i < nrow; i++) {
614  int x = cpl_table_get(table, MUSE_BADPIX_X, i, NULL),
615  y = cpl_table_get(table, MUSE_BADPIX_Y, i, NULL);
616 
617  /* get value from table and check that getting *
618  * the value from the DQ image succeeds */
619  uint32_t dq = cpl_table_get(table, MUSE_BADPIX_DQ, i, NULL);
620  cpl_errorstate state = cpl_errorstate_get();
621  int err;
622  uint32_t value = cpl_image_get(image->dq, x, y, &err);
623  if (err != 0 || !cpl_errorstate_is_equal(state)) {
624  cpl_errorstate_set(state); /* swallow the error, nothing was changed */
625  if (k == 0) {
626  nbadpos++;
627  } /* if first image */
628  continue;
629  } /* if error occurred */
630 
631  /* OR the data in the DQ extension with what's in the badpix table */
632  rc = cpl_image_set(image->dq, x, y, dq | value);
633  if (k == 0) {
634  nbadpix++; /* count bad pixels transferred to 1st image */
635  } /* if first image */
636  } /* for i (all table rows) */
637  } /* for k (all images) */
638  cpl_table_delete(table);
639  cpl_msg_debug(__func__, "Applied %u bad pixels from %s in IFU %hhu.", nbadpix,
640  MUSE_TAG_BADPIX_TABLE, aIFU);
641  /* warn, if there were bad entries */
642  if (nbadpos > 0) {
643  cpl_msg_warning(__func__, "%s contained %u entries outside the CCD in IFU "
644  "%hhu!", MUSE_TAG_BADPIX_TABLE, nbadpos, aIFU);
645  } /* if bad entries */
646  return rc;
647 } /* muse_basicproc_apply_badpix() */
648 
649 /*---------------------------------------------------------------------------*/
658 /*---------------------------------------------------------------------------*/
659 static cpl_error_code
660 muse_basicproc_check_saturation(muse_imagelist *aList)
661 {
662  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
663  unsigned int k;
664  for (k = 0; k < aList->size; k++) {
665  muse_image *image = muse_imagelist_get(aList, k);
666  unsigned char ifu = muse_utils_get_ifu(image->header);
667  int nsaturated = muse_quality_set_saturated(image);
668  /* if we have more than 10% of saturated pixels then something is wrong */
669  int npix = cpl_image_get_size_x(image->data)
670  * cpl_image_get_size_y(image->data);
671  if (nsaturated > (0.01 * npix)) {
672  const char *fn = cpl_propertylist_get_string(image->header,
673  MUSE_HDR_TMP_FN);
674  cpl_msg_error(__func__, "Raw exposure %u [%s] is strongly saturated in "
675  "IFU %hhu (%d of %d pixels)!", k+1, fn, ifu, nsaturated,
676  npix);
677  } else if (nsaturated > (0.001 * npix)) {
678  const char *fn = cpl_propertylist_get_string(image->header,
679  MUSE_HDR_TMP_FN);
680  cpl_msg_warning(__func__, "Raw exposure %u [%s] is probably saturated in "
681  "IFU %hhu (%d of %d pixels)!", k+1, fn, ifu, nsaturated,
682  npix);
683  } else {
684  cpl_msg_debug(__func__, "Raw exposure %u in IFU %hhu (%d of %d pixels "
685  "saturated)!", k+1, ifu, nsaturated, npix);
686  }
687  cpl_propertylist_update_int(image->header, MUSE_HDR_TMP_NSAT, nsaturated);
688  } /* for k (all images) */
689  return CPL_ERROR_NONE;
690 } /* muse_basicproc_check_saturation() */
691 
692 /*---------------------------------------------------------------------------*/
705 /*---------------------------------------------------------------------------*/
706 static cpl_error_code
707 muse_basicproc_quadrant_statistics(muse_imagelist *aList,
708  muse_processing *aProcessing)
709 {
710  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
711  /* this only makes sense to do for inputs that are bias */
712  if (!muse_processing_check_intags(aProcessing, MUSE_TAG_BIAS, 5)) {
713  return CPL_ERROR_NONE;
714  }
715  unsigned char ifu = muse_utils_get_ifu(muse_imagelist_get(aList, 0)->header);
716  cpl_msg_info(__func__, "Computing per-quadrant medians for %u exposures in "
717  "IFU %hhu", aList->size, ifu);
718  unsigned int k;
719  for (k = 0; k < aList->size; k++) {
720  muse_image *image = muse_imagelist_get(aList, k);
721  unsigned char n;
722  for (n = 1; n <= 4; n++) {
723  cpl_size *w = muse_quadrants_get_window(muse_imagelist_get(aList, k), n);
724  float median = cpl_image_get_median_window(image->data, w[0], w[2],
725  w[1], w[3]);
726  cpl_free(w);
727  char *kw = cpl_sprintf(MUSE_HDR_TMP_QUADnMED, n);
728  cpl_propertylist_append_float(image->header, kw, median);
729  cpl_free(kw);
730  } /* for n (quadrants) */
731  } /* for k (all images) */
732  return CPL_ERROR_NONE;
733 } /* muse_basicproc_quadrant_statistics() */
734 
735 /*---------------------------------------------------------------------------*/
757 /*---------------------------------------------------------------------------*/
758 static cpl_error_code
759 muse_basicproc_apply_bias(muse_imagelist *aList, muse_processing *aProcessing,
760  unsigned char aIFU, muse_basicproc_params *aBPars)
761 {
762  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
763  cpl_frame *biasframe = muse_frameset_find_master(aProcessing->inframes,
764  MUSE_TAG_MASTER_BIAS, aIFU);
765  cpl_errorstate prestate = cpl_errorstate_get();
766  if (!biasframe) return CPL_ERROR_NONE;
767  cpl_errorstate_set(prestate);
768  const char *biasname = cpl_frame_get_filename(biasframe);
769  muse_image *biasimage = muse_image_load(biasname);
770  if (!biasimage) {
771  /* remember error message for a while, but reset the state, *
772  * before trying to load from channel extensions */
773  char *errmsg = cpl_strdup(cpl_error_get_message());
774  cpl_errorstate_set(prestate);
775  biasimage = muse_image_load_from_extensions(biasname, aIFU);
776  if (!biasimage) {
777  /* now display both the older and the new error messages, *
778  * so that they cannot be swallowed by parallelization */
779  cpl_msg_error(__func__, "%s", errmsg);
780  cpl_msg_error(__func__, "%s", cpl_error_get_message());
781  cpl_free(errmsg);
782  cpl_frame_delete(biasframe);
783  return cpl_error_get_code();
784  } /* if 2nd load failure */
785  cpl_free(errmsg);
786  cpl_msg_info(__func__, "Bias correction in IFU %hhu using \"%s[CHAN%02hhu."
787  "DATA]\"", aIFU, biasname, aIFU);
788  } else {
789  cpl_msg_info(__func__, "Bias correction in IFU %hhu using \"%s[DATA]\"",
790  aIFU, biasname);
791  }
792  /* add temporary input filename for diagnostics */
793  cpl_propertylist_append_string(biasimage->header, MUSE_HDR_TMP_FN, biasname);
794 
795  cpl_boolean ovscoffset = aBPars && aBPars->overscan
796  && !strncmp(aBPars->overscan, "offset", 7),
797  ovscvpoly = aBPars && aBPars->overscan
798  && !strncmp(aBPars->overscan, "vpoly", 5); /* up to the : */
799  muse_processing_append_used(aProcessing, biasframe, CPL_FRAME_GROUP_CALIB, 0);
800  cpl_error_code rc = CPL_ERROR_NONE;
801  unsigned int k;
802  for (k = 0; k < aList->size && rc == CPL_ERROR_NONE; k++) {
803  muse_image *image = muse_imagelist_get(aList, k);
804  rc = muse_basicproc_verify_setup(image, biasimage);
805  if (rc != CPL_ERROR_NONE) {
806  break;
807  }
808  rc = muse_image_variance_create(image, biasimage);
809  if (ovscoffset) {
810  muse_quadrants_overscan_correct(image, biasimage);
811  } else if (ovscvpoly) {
812  /* create error message and code, if the bias was not *
813  * vpoly-handled, i.e. still has the original bias level *
814  * of ~1000 adu or > 100xsigma above ~zero */
815  cpl_boolean good = muse_quadrants_overscan_check(image, biasimage, 100.);
816  if (!good) {
817  cpl_msg_error(__func__, "Very different overscan levels found between "
818  "%s and raw exposure %u in IFU %hhu: incompatible overscan"
819  " parameters?", MUSE_TAG_MASTER_BIAS, k + 1, aIFU);
820  rc = cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
821  break;
822  } /* if not good */
823  } else {
824  /* just warn above indicated sigma level, ignore failure */
825  muse_quadrants_overscan_check(image, biasimage,
826  aBPars ? aBPars->ovscsigma : 1.);
827  }
828  rc = muse_image_subtract(image, biasimage);
829 #if GENERATE_TEST_IMAGES
830  /* if we need to generate images for automated tests, here is a good *
831  * place to write them out, as they are trimmed and bias corrected */
832  if (k == 0) {
833  muse_image_save(image, "trimmed_bias_sub.fits");
834  }
835 #endif
836  } /* for k (all images) */
837  muse_image_delete(biasimage);
838  return rc;
839 } /* muse_basicproc_apply_bias() */
840 
841 /*---------------------------------------------------------------------------*/
860 /*---------------------------------------------------------------------------*/
861 static cpl_error_code
862 muse_basicproc_check_gain(muse_imagelist *aList, muse_processing *aProcessing,
863  unsigned char aIFU)
864 {
865  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
866  /* this only makes sense to do for input flat-fields */
867  if (!muse_processing_check_intags(aProcessing, MUSE_TAG_FLAT, 5)) {
868  return CPL_ERROR_NONE;
869  }
870  cpl_ensure_code(muse_imagelist_get_size(aList) >= 2,
871  CPL_ERROR_INCOMPATIBLE_INPUT);
872 
873  cpl_frame *fbias = muse_frameset_find_master(aProcessing->inframes,
874  MUSE_TAG_MASTER_BIAS, aIFU);
875  if (!fbias) {
876  /* it's OK if there is no bias frame, probably we are not in muse_bias */
877  return CPL_ERROR_NONE;
878  }
879  const char *biasname = cpl_frame_get_filename(fbias);
880  cpl_propertylist *hbias = cpl_propertylist_load(biasname, 0);
881  if (!cpl_propertylist_has(hbias, QC_BIAS_MASTER_RON)) {
882  cpl_propertylist_delete(hbias);
883  /* try to load from channel extension */
884  char *extname = cpl_sprintf("CHAN%02hhu.%s", aIFU, EXTNAME_DATA);
885  int extension = cpl_fits_find_extension(biasname, extname);
886  hbias = cpl_propertylist_load(biasname, extension);
887  cpl_free(extname);
888  }
889  cpl_frame_delete(fbias);
890  cpl_image *f1 = muse_imagelist_get(aList, 0)->data,
891  *f2 = muse_imagelist_get(aList, 1)->data;
892  cpl_propertylist *header = muse_imagelist_get(aList, 0)->header;
893  cpl_image *diff = cpl_image_subtract_create(f1, f2);
894 
895  unsigned char n;
896  for (n = 1; n <= 4; n++) {
897  cpl_size *w = muse_quadrants_get_window(muse_imagelist_get(aList, 0), n);
898  double m1 = cpl_image_get_mean_window(f1, w[0], w[2], w[1], w[3]),
899  m2 = cpl_image_get_mean_window(f2, w[0], w[2], w[1], w[3]),
900  sf = cpl_image_get_stdev_window(diff, w[0], w[2], w[1], w[3]);
901  char *keyword = cpl_sprintf(QC_BIAS_MASTERn_PREFIX" MEAN", n);
902  float mb = cpl_propertylist_get_float(hbias, keyword);
903  cpl_free(keyword);
904  keyword = cpl_sprintf(QC_BIAS_MASTER_RON, n);
905  /* the RON formula taken from Howell inverted to give sigma(b1-b2) */
906  double gainheader = muse_pfits_get_gain(header, n),
907  sb = cpl_propertylist_get_float(hbias, keyword) * sqrt(2.)
908  / gainheader, /* gain in count/adu */
909  /* the gain formula taken from Howell: */
910  gain = (m1 + m2 - 2*mb) / (sf*sf - sb*sb),
911  dgain = 1. - gain / gainheader;
912  /* output warning for difference larger than 20%, info message otherwise */
913  if (dgain > 0.2) {
914  cpl_msg_warning(__func__, "IFU %hhu, quadrant %1hhu: estimated gain %.3f "
915  "count/adu but header gives %.3f!", aIFU, n, gain,
916  gainheader);
917  } else {
918  cpl_msg_info(__func__, "IFU %hhu, quadrant %1hhu: estimated gain %.3f "
919  "count/adu (header %.3f, delta %.3f)", aIFU, n, gain,
920  gainheader, dgain);
921  }
922  cpl_free(keyword);
923  cpl_free(w);
924  } /* for n (quadrants) */
925 
926  cpl_image_delete(diff);
927  cpl_propertylist_delete(hbias);
928 
929  return CPL_ERROR_NONE;
930 } /* muse_basicproc_check_gain() */
931 
932 /*---------------------------------------------------------------------------*/
951 /*---------------------------------------------------------------------------*/
952 static cpl_error_code
953 muse_basicproc_gain_override(muse_imagelist *aList,
954  muse_processing *aProcessing, unsigned char aIFU)
955 {
956  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
957  cpl_frame *fnonlingain = muse_frameset_find_master(aProcessing->inframes,
958  MUSE_TAG_NONLINGAIN, aIFU);
959  if (!fnonlingain) {
960  /* it's OK if there is no nonlinearity frame, it's optional... */
961  return CPL_ERROR_NONE;
962  }
963  if (getenv("MUSE_BASICPROC_SKIP_GAIN_OVERRIDE") &&
964  atoi(getenv("MUSE_BASICPROC_SKIP_GAIN_OVERRIDE")) > 0) {
965  cpl_msg_info(__func__, "Skipping gain override, although %s is given",
966  MUSE_TAG_NONLINGAIN);
967  return CPL_ERROR_NONE;
968  }
969  cpl_errorstate state = cpl_errorstate_get();
970  const char *fn = cpl_frame_get_filename(fnonlingain);
971  /* immediately try to load from channel extension */
972  char *extname = cpl_sprintf("CHAN%02hhu", aIFU);
973  int extension = cpl_fits_find_extension(fn, extname);
974  cpl_propertylist *header = cpl_propertylist_load(fn, extension);
975  cpl_msg_info(__func__, "Overriding gain in IFU %hhu using \"%s[%s]\"",
976  aIFU, fn, extname);
977  cpl_free(extname);
978  muse_processing_append_used(aProcessing, fnonlingain, CPL_FRAME_GROUP_CALIB, 0);
979 
980  unsigned int k;
981  for (k = 0; k < aList->size; k++) {
982  muse_image *image = muse_imagelist_get(aList, k);
983  /* XXX need to verify the detector/chip properties here, too! */
984  unsigned char n;
985  for (n = 1; n <= 4; n++) {
986  /* transfer the GAIN of this quadrant into the image */
987  double gain = muse_pfits_get_gain(header, n);
988  char *kw = cpl_sprintf("ESO DET OUT%d GAIN", n);
989  cpl_propertylist_update_double(image->header, kw, gain);
990  cpl_free(kw);
991  } /* for n (quadrants) */
992  } /* for k (all images) */
993  cpl_propertylist_delete(header);
994  return cpl_errorstate_is_equal(state) ? CPL_ERROR_NONE : cpl_error_get_code();
995 } /* muse_basicproc_gain_override() */
996 
997 /*---------------------------------------------------------------------------*/
1011 /*---------------------------------------------------------------------------*/
1012 static cpl_error_code
1013 muse_basicproc_adu_to_count(muse_imagelist *aList, muse_processing *aProcessing)
1014 {
1015  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
1016  /* this only makes sense to do for inputs that are not bias */
1017  if (muse_processing_check_intags(aProcessing, MUSE_TAG_BIAS, 5)) {
1018  return CPL_ERROR_NONE;
1019  }
1020 
1021  unsigned char ifu = muse_utils_get_ifu(muse_imagelist_get(aList, 0)->header);
1022  cpl_msg_info(__func__, "Converting %u exposures from adu to count (= electron)"
1023  " units in IFU %hhu", aList->size, ifu);
1024  cpl_error_code rc = CPL_ERROR_NONE;
1025  unsigned int k;
1026  for (k = 0; k < aList->size; k++) {
1028  } /* for k (all images) */
1029  return rc;
1030 } /* muse_basicproc_adu_to_count() */
1031 
1032 /*---------------------------------------------------------------------------*/
1054 /*---------------------------------------------------------------------------*/
1055 static cpl_error_code
1056 muse_basicproc_corr_nonlinearity(muse_imagelist *aList,
1057  muse_processing *aProcessing,
1058  unsigned char aIFU)
1059 {
1060  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
1061  /* this only makes sense to do for inputs that are illuminated */
1062  if (muse_processing_check_intags(aProcessing, MUSE_TAG_BIAS, 5) ||
1063  muse_processing_check_intags(aProcessing, MUSE_TAG_DARK, 5)) {
1064  return CPL_ERROR_NONE;
1065  }
1066 
1067  cpl_frame *fnonlingain = muse_frameset_find_master(aProcessing->inframes,
1068  MUSE_TAG_NONLINGAIN, aIFU);
1069  if (!fnonlingain) {
1070  /* it's OK if there is no nonlinearity frame, it's optional... */
1071  return CPL_ERROR_NONE;
1072  }
1073  if (getenv("MUSE_BASICPROC_SKIP_NONLIN_CORR") &&
1074  atoi(getenv("MUSE_BASICPROC_SKIP_NONLIN_CORR")) > 0) {
1075  cpl_msg_debug(__func__, "Skipping nonlinearity correction, although %s is "
1076  "given", MUSE_TAG_NONLINGAIN);
1077  return CPL_ERROR_NONE;
1078  }
1079  const char *fn = cpl_frame_get_filename(fnonlingain);
1080  /* immediately try to load from channel extension */
1081  char *extname = cpl_sprintf("CHAN%02hhu", aIFU);
1082  int extension = cpl_fits_find_extension(fn, extname);
1083  cpl_propertylist *header = cpl_propertylist_load(fn, extension);
1084  cpl_msg_info(__func__, "Correcting nonlinearity in IFU %hhu using \"%s[%s]\"",
1085  aIFU, fn, extname);
1086  cpl_free(extname);
1087  muse_processing_append_used(aProcessing, fnonlingain, CPL_FRAME_GROUP_CALIB, 0);
1088 
1089  cpl_error_code rc = CPL_ERROR_NONE;
1090  unsigned int k;
1091  for (k = 0; k < aList->size; k++) {
1092  muse_image *image = muse_imagelist_get(aList, k);
1093  int nx = cpl_image_get_size_x(image->data);
1094  float *data = cpl_image_get_data_float(image->data),
1095  *stat = cpl_image_get_data_float(image->stat);
1096 
1097  /* XXX need to verify the detector/chip properties here, too! */
1098 
1099  unsigned char n;
1100  for (n = 1; n <= 4; n++) {
1101  /* create the 1D nonlinearity polynomial for this quadrant, then read *
1102  * all the parameters from the header set the polynomial up accordingly */
1103  cpl_polynomial *poly = cpl_polynomial_new(1);
1104  char *kw = cpl_sprintf(MUSE_HDR_NONLINn_ORDER, n);
1105  unsigned char o, order = cpl_propertylist_get_int(header, kw);
1106  cpl_free(kw);
1107  for (o = 0; o <= order; o++) {
1108  kw = cpl_sprintf(MUSE_HDR_NONLINn_COEFFo, n, o);
1109  cpl_size pows = o;
1110  cpl_polynomial_set_coeff(poly, &pows,
1111  cpl_propertylist_get_double(header, kw));
1112  cpl_free(kw);
1113  } /* for i (polynomial orders) */
1114  /* compute linear extrapolations */
1115  kw = cpl_sprintf(MUSE_HDR_NONLINn_LLO, n);
1116  double lolim = cpl_propertylist_get_double(header, kw);
1117  cpl_free(kw);
1118  kw = cpl_sprintf(MUSE_HDR_NONLINn_LHI, n);
1119  double hilim = cpl_propertylist_get_double(header, kw);
1120  cpl_free(kw);
1121  /* coefficients for linear extrapolation beyond low and high limits */
1122  double p1lo, p0lo = cpl_polynomial_eval_1d(poly, lolim, &p1lo),
1123  p1hi, p0hi = cpl_polynomial_eval_1d(poly, hilim, &p1hi);
1124  /* convert limits from log10(adu) to adu */
1125  lolim = pow(10, lolim);
1126  hilim = pow(10, hilim);
1127 #if 0
1128  double values[] = { 1., 20., 200., 2000., 20000., 65000.,
1129  lolim, hilim, -1. };
1130  int idx = 0;
1131  while (values[idx] > 0) {
1132  double v = values[idx],
1133  logv = log10(v);
1134  cpl_msg_debug(__func__, "%f adu -> %f log10(adu) ==> poly = %f ==> x %f",
1135  v, logv, cpl_polynomial_eval_1d(poly, logv, NULL),
1136  1. / (1. + cpl_polynomial_eval_1d(poly, logv, NULL)));
1137  idx++;
1138  } /* while */
1139  cpl_polynomial_dump(poly, stdout);
1140  fflush(stdout);
1141  cpl_msg_debug(__func__, "beyond low limit (%f adu): %f + (%f) * (log10(adu) - %f)",
1142  lolim, p0lo, p1lo, log10(lolim));
1143  cpl_msg_debug(__func__, "beyond high limit (%f adu): %f + (%f) * (log10(adu) - %f)",
1144  hilim, p0hi, p1hi, log10(hilim));
1145 #endif
1146 
1147  /* now loop through the full quadrant and correct the scaling */
1148  cpl_size *w = muse_quadrants_get_window(image, n);
1149  int i;
1150  for (i = w[0] - 1; i < w[1]; i++) {
1151  int j;
1152  for (j = w[2] - 1; j < w[3]; j++) {
1153  if (data[i + j*nx] <= 0) {
1154  continue; /* don't do anything for non-positive datapoints */
1155  }
1156  /* compute the percent-level deviation (applying to log10(adu)) */
1157  double pcor;
1158  if (data[i + j*nx] < lolim) {
1159  pcor = p0lo + p1lo * (log10(data[i + j*nx]) - log10(lolim));
1160  } else if (data[i + j*nx] > hilim) {
1161  pcor = p0hi + p1hi * (log10(data[i + j*nx]) - log10(hilim));
1162  } else {
1163  pcor = cpl_polynomial_eval_1d(poly, log10(data[i + j*nx]), NULL);
1164  }
1165  /* then derive the multiplicative correction factor */
1166  double fcor = 1. / (1. + pcor);
1167  data[i + j*nx] *= fcor;
1168  stat[i + j*nx] *= fcor*fcor;
1169  } /* for j (vertical pixels) */
1170  } /* for i (horizontal pixels) */
1171  cpl_free(w);
1172  cpl_polynomial_delete(poly);
1173  } /* for n (quadrants) */
1174  } /* for k (all images) */
1175  cpl_propertylist_delete(header);
1176 
1177  return rc;
1178 } /* muse_basicproc_corr_nonlinearity() */
1179 
1180 /*---------------------------------------------------------------------------*/
1196 /*---------------------------------------------------------------------------*/
1197 static cpl_error_code
1198 muse_basicproc_apply_dark(muse_imagelist *aList, muse_processing *aProcessing,
1199  unsigned char aIFU)
1200 {
1201  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
1202 
1203  cpl_frame *darkframe = muse_frameset_find_master(aProcessing->inframes,
1204  MUSE_TAG_MASTER_DARK, aIFU);
1205  cpl_errorstate prestate = cpl_errorstate_get();
1206  if (!darkframe) return CPL_ERROR_NONE;
1207  cpl_errorstate_set(prestate);
1208  const char *darkname = cpl_frame_get_filename(darkframe);
1209  muse_image *darkimage = muse_image_load(darkname);
1210  if (!darkimage) {
1211  char *errmsg = cpl_strdup(cpl_error_get_message());
1212  cpl_errorstate_set(prestate);
1213  darkimage = muse_image_load_from_extensions(darkname, aIFU);
1214  if (!darkimage) {
1215  cpl_msg_error(__func__, "%s", errmsg);
1216  cpl_msg_error(__func__, "%s", cpl_error_get_message());
1217  cpl_free(errmsg);
1218  cpl_frame_delete(darkframe);
1219  return cpl_error_get_code();
1220  } /* if 2nd load failure */
1221  cpl_free(errmsg);
1222  cpl_msg_info(__func__, "Dark correction in IFU %hhu using \"%s[CHAN%02hhu."
1223  "DATA]\"", aIFU, darkname, aIFU);
1224  } else {
1225  cpl_msg_info(__func__, "Dark correction in IFU %hhu using \"%s[DATA]\"",
1226  aIFU, darkname);
1227  }
1228  cpl_propertylist_append_string(darkimage->header, MUSE_HDR_TMP_FN, darkname);
1229 
1230  muse_processing_append_used(aProcessing, darkframe, CPL_FRAME_GROUP_CALIB, 0);
1231  cpl_error_code rc = CPL_ERROR_NONE;
1232  unsigned int k;
1233  for (k = 0; k < aList->size; k++) {
1234  muse_image *image = muse_imagelist_get(aList, k);
1235  rc = muse_basicproc_verify_setup(image, darkimage);
1236  if (rc != CPL_ERROR_NONE) {
1237  break;
1238  }
1239 
1240  /* duplicate the dark, because the scaling will destroy its normalization */
1241  muse_image *dark = muse_image_duplicate(darkimage);
1242  /* scale and subtract the dark by comparing exposure time *
1243  * of the dark and the other image */
1244  double scale = muse_pfits_get_exptime(image->header);
1245  if (muse_pfits_get_exptime(dark->header) > 0) {
1246  scale /= muse_pfits_get_exptime(dark->header);
1247  }
1248  rc = muse_image_scale(dark, scale);
1249  rc = muse_image_subtract(image, dark);
1250 
1251  muse_image_delete(dark);
1252  } /* for k (all images) */
1253  muse_image_delete(darkimage);
1254 
1255  return rc;
1256 } /* muse_basicproc_apply_dark() */
1257 
1258 /*---------------------------------------------------------------------------*/
1273 /*---------------------------------------------------------------------------*/
1274 static cpl_error_code
1275 muse_basicproc_apply_cr(muse_imagelist *aList,
1276  muse_basicproc_params *aBPars)
1277 {
1278  cpl_ensure_code(aList, CPL_ERROR_NULL_INPUT);
1279 
1280  /* if we didn't get cr parameters we were not supposed *
1281  * to find cosmic rays, so just return without error */
1282  cpl_boolean isdcr = aBPars && aBPars->crmethod
1283  && !strncmp(aBPars->crmethod, "dcr", 4);
1284  if (!isdcr) {
1285  return CPL_ERROR_NONE;
1286  }
1287 
1288  cpl_error_code rc = CPL_ERROR_NONE;
1289  unsigned int k;
1290  for (k = 0; k < aList->size; k++) {
1291  muse_image *image = muse_imagelist_get(aList, k);
1292  unsigned char ifu = muse_utils_get_ifu(image->header);
1293  int ncr = 0;
1294  ncr = muse_cosmics_dcr(image, aBPars->dcrxbox, aBPars->dcrybox,
1295  aBPars->dcrpasses, aBPars->dcrthres);
1296  if (ncr <= 0) {
1297  cpl_msg_error(__func__, "Cosmic ray rejection using DCR in IFU %hhu failed"
1298  " for image %u (ncr = %d): %s", ifu, k+1, ncr,
1299  cpl_error_get_message());
1300  rc = cpl_error_get_code();
1301  } else {
1302  cpl_msg_info(__func__, "Cosmic ray rejection using DCR in IFU %hhu found "
1303  "%d affected pixels in image %u", ifu, ncr, k+1);
1304  }
1305  } /* for k (all images) */
1306 
1307  return rc;
1308 } /* muse_basicproc_apply_cr() */
1309 
1310 /*---------------------------------------------------------------------------*/
1325 /*---------------------------------------------------------------------------*/
1326 static cpl_error_code
1327 muse_basicproc_apply_flat(muse_imagelist *aList, muse_processing *aProcessing,
1328  unsigned char aIFU)
1329 {
1330  cpl_ensure_code(aList && aProcessing, CPL_ERROR_NULL_INPUT);
1331 
1332  cpl_frame *flatframe = muse_frameset_find_master(aProcessing->inframes,
1333  MUSE_TAG_MASTER_FLAT, aIFU);
1334  cpl_errorstate prestate = cpl_errorstate_get();
1335  if (!flatframe) return CPL_ERROR_NONE;
1336  cpl_errorstate_set(prestate);
1337  const char *flatname = cpl_frame_get_filename(flatframe);
1338  prestate = cpl_errorstate_get();
1339  muse_image *flatimage = muse_image_load(flatname);
1340  if (!flatimage) {
1341  char *errmsg = cpl_strdup(cpl_error_get_message());
1342  cpl_errorstate_set(prestate);
1343  flatimage = muse_image_load_from_extensions(flatname, aIFU);
1344  if (!flatimage) {
1345  cpl_msg_error(__func__, "%s", errmsg);
1346  cpl_msg_error(__func__, "%s", cpl_error_get_message());
1347  cpl_free(errmsg);
1348  cpl_frame_delete(flatframe);
1349  return cpl_error_get_code();
1350  } /* if 2nd load failure */
1351  cpl_free(errmsg);
1352  cpl_msg_info(__func__, "Flat-field correction in IFU %hhu using \"%s[CHAN%02hhu."
1353  "DATA]\"", aIFU, flatname, aIFU);
1354  } else {
1355  cpl_msg_info(__func__, "Flat-field correction in IFU %hhu using \"%s[DATA]\"",
1356  aIFU, flatname);
1357  }
1358  /* copy QC parameter containing integrated flux */
1359  double fflux = cpl_propertylist_get_double(flatimage->header,
1360  QC_FLAT_MASTER_INTFLUX);
1361  cpl_propertylist_append_string(flatimage->header, MUSE_HDR_TMP_FN, flatname);
1362  muse_processing_append_used(aProcessing, flatframe, CPL_FRAME_GROUP_CALIB, 0);
1363 
1364  cpl_error_code rc = CPL_ERROR_NONE;
1365  unsigned int k;
1366  for (k = 0; k < aList->size; k++) {
1367  cpl_msg_debug(__func__, "Flat-field division in IFU %hhu of image %u of %u",
1368  aIFU, k+1, aList->size);
1369  muse_image *image = muse_imagelist_get(aList, k);
1370  rc = muse_basicproc_verify_setup(image, flatimage);
1371  if (rc != CPL_ERROR_NONE) {
1372  break;
1373  }
1374  rc = muse_image_divide(image, flatimage);
1375  cpl_propertylist_update_double(image->header, MUSE_HDR_FLAT_FLUX_LAMP, fflux);
1376  } /* for k (all images) */
1377 
1378  muse_image_delete(flatimage);
1379  return rc;
1380 } /* muse_basicproc_apply_flat() */
1381 
1382 /*---------------------------------------------------------------------------*/
1403 /*---------------------------------------------------------------------------*/
1404 static muse_imagelist *
1405 muse_basicproc_load_raw(muse_processing *aProcessing, unsigned char aIFU)
1406 {
1407  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
1408 
1409  /* rawframes contains the list of input files that we want to load */
1410  cpl_frameset *rawframes = muse_frameset_check_raw(aProcessing->inframes,
1411  aProcessing->intags, aIFU);
1412  cpl_ensure(rawframes, CPL_ERROR_NULL_INPUT, NULL);
1413 
1414  muse_imagelist *images = muse_imagelist_new();
1415  unsigned int k = 0;
1416  cpl_size iframe, nframes = cpl_frameset_get_size(rawframes);
1417  for (iframe = 0; iframe < nframes; iframe++) {
1418  cpl_frame *frame = cpl_frameset_get_position(rawframes, iframe);
1419  const char *fileName = cpl_frame_get_filename(frame);
1420  int extension = muse_utils_get_extension_for_ifu(fileName, aIFU);
1421  if (extension == -1) {
1422  cpl_msg_error(__func__, "\"%s\" does not contain data from IFU %hhu",
1423  fileName, aIFU);
1424  break;
1425  }
1426  muse_image *raw = muse_image_load_from_raw(fileName, extension);
1427  if (!raw) {
1428  continue;
1429  }
1430  /* add temporary input filename for diagnostics */
1431  cpl_propertylist_append_string(raw->header, MUSE_HDR_TMP_FN, fileName);
1432  /* add temporary input tag, for use when deriving the output tag */
1433  cpl_propertylist_append_string(raw->header, MUSE_HDR_TMP_INTAG,
1434  cpl_frame_get_tag(frame));
1435 
1436  muse_imagelist_set(images, raw, k);
1437  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_RAW, 1);
1438  k++;
1439  } /* for frame (all rawframes) */
1440  cpl_frameset_delete(rawframes);
1441 
1442  /* some checks if we ended up with a valid imagelist */
1443  if (!images->size) {
1444  if (cpl_error_get_code() != MUSE_ERROR_CHIP_NOT_LIVE) {
1445  cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
1446  cpl_msg_error(__func__, "No raw images loaded for IFU %hhu", aIFU);
1447  }
1448  muse_imagelist_delete(images); /* still free the structure */
1449  return NULL;
1450  }
1451  if (muse_imagelist_is_uniform(images) != 0) {
1452  cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
1453  cpl_msg_error(__func__, "Non-uniform imagelist for IFU %hhu", aIFU);
1454  muse_imagelist_delete(images);
1455  return NULL;
1456  }
1457 
1458  return images;
1459 } /* muse_basicproc_load_raw() */
1460 
1461 /*---------------------------------------------------------------------------*/
1504 /*---------------------------------------------------------------------------*/
1506 muse_basicproc_load(muse_processing *aProcessing, unsigned char aIFU,
1507  muse_basicproc_params *aBPars)
1508 {
1509  if (muse_processing_check_input(aProcessing, aIFU) != CPL_ERROR_NONE) {
1510  return NULL;
1511  }
1512  muse_imagelist *images = muse_basicproc_load_raw(aProcessing, aIFU);
1513  if (!images) {
1514  return NULL;
1515  }
1516  cpl_errorstate prestate = cpl_errorstate_get();
1517  if (muse_basicproc_apply_badpix(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1518  cpl_msg_warning(__func__, "Applying bad pixel table to IFU %hhu failed: %s",
1519  aIFU, cpl_error_get_message());
1520  cpl_errorstate_set(prestate); /* this is not critical, continue */
1521  }
1522  if (muse_basicproc_check_saturation(images) != CPL_ERROR_NONE) {
1523  muse_imagelist_delete(images);
1524  return NULL;
1525  }
1526  muse_basicproc_quadrant_statistics(images, aProcessing); /* non-fatal */
1527  if (muse_basicproc_overscans_compute_stats(images, aBPars) /* incl. CR rejection */
1528  != CPL_ERROR_NONE) {
1529  muse_imagelist_delete(images); /* this failure should be fatal */
1530  return NULL;
1531  }
1532  if (muse_basicproc_correct_overscans_vpoly(images, aBPars)
1533  != CPL_ERROR_NONE) {
1534  muse_imagelist_delete(images); /* this failures should be fatal! */
1535  return NULL;
1536  }
1537  if (muse_basicproc_trim_images(images) != CPL_ERROR_NONE) {
1538  muse_imagelist_delete(images);
1539  return NULL;
1540  }
1541  if (muse_basicproc_correct_overscans_offset(images, aProcessing, aBPars)
1542  != CPL_ERROR_NONE) { /* for BIAS files only */
1543  cpl_msg_warning(__func__, "Bias-level correction using overscans failed in "
1544  "IFU %hhu: %s", aIFU, cpl_error_get_message());
1545  }
1546  if (muse_basicproc_check_overscans(images, aProcessing, aBPars)
1547  != CPL_ERROR_NONE) { /* only for BIAS files and when overscan==none */
1548  muse_imagelist_delete(images);
1549  return NULL;
1550  }
1551  muse_basicproc_gain_override(images, aProcessing, aIFU);
1552  muse_basicproc_check_gain(images, aProcessing, aIFU); /* for flats */
1553  if (muse_basicproc_apply_bias(images, aProcessing, aIFU, aBPars)
1554  != CPL_ERROR_NONE) {
1555  muse_imagelist_delete(images);
1556  return NULL;
1557  }
1558  if (muse_basicproc_corr_nonlinearity(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1559  muse_imagelist_delete(images);
1560  return NULL;
1561  }
1562  if (muse_basicproc_adu_to_count(images, aProcessing) != CPL_ERROR_NONE) {
1563  muse_imagelist_delete(images);
1564  return NULL;
1565  }
1566  if (muse_basicproc_apply_dark(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1567  muse_imagelist_delete(images);
1568  return NULL;
1569  }
1570  if (muse_basicproc_apply_cr(images, aBPars) != CPL_ERROR_NONE) {
1571  muse_imagelist_delete(images);
1572  return NULL;
1573  }
1574  if (muse_basicproc_apply_flat(images, aProcessing, aIFU) != CPL_ERROR_NONE) {
1575  muse_imagelist_delete(images);
1576  return NULL;
1577  }
1578  return images;
1579 } /* muse_basicproc_load() */
1580 
1581 /*---------------------------------------------------------------------------*/
1594 /*---------------------------------------------------------------------------*/
1596 muse_basicproc_load_reduced(muse_processing *aProcessing, unsigned char aIFU)
1597 {
1598  muse_imagelist *images = muse_imagelist_new();
1599  cpl_frameset *redframes = muse_frameset_find_tags(aProcessing->inframes,
1600  aProcessing->intags,
1601  aIFU, CPL_FALSE);
1602  cpl_size iframe, nframes = cpl_frameset_get_size(redframes);
1603  for (iframe = 0; iframe < nframes; iframe++) {
1604  cpl_frame *frame = cpl_frameset_get_position(redframes, iframe);
1605  cpl_errorstate prestate = cpl_errorstate_get();
1606  const char *imagename = cpl_frame_get_filename(frame);
1607  muse_image *image = muse_image_load(imagename);
1608  if (!image) {
1609  cpl_errorstate_set(prestate);
1610  image = muse_image_load_from_extensions(imagename, aIFU);
1611  }
1612  muse_imagelist_set(images, image, iframe);
1613  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_RAW, 1);
1614  } /* for iframe (all used frames) */
1615  cpl_frameset_delete(redframes);
1616  return images;
1617 } /* muse_basicproc_load_reduced() */
1618 
1619 
1620 /*---------------------------------------------------------------------------*/
1633 /*---------------------------------------------------------------------------*/
1634 cpl_table *
1636 {
1637  cpl_ensure(aPT && aPT->header && aPT->table,
1638  CPL_ERROR_NULL_INPUT, NULL);
1639 
1640  /* crop pixel table to small wavelength range *
1641  * and separate into per-slice tables */
1642  muse_pixtable_restrict_wavelength(aPT, 6500., 7500.);
1644  int ipt, npt = muse_pixtable_extracted_get_size(pts);
1645 
1646  unsigned char ifu = muse_utils_get_ifu(aPT->header);
1647  cpl_msg_info(__func__, "Preparing %s flat: %d slices in the data of IFU %hhu "
1648  "found.", MUSE_TAG_ILLUM, npt, ifu);
1649  cpl_table *tattached = cpl_table_new(npt);
1650  cpl_table_new_column(tattached, "slice", CPL_TYPE_INT);
1651  cpl_table_new_column(tattached, "fflat", CPL_TYPE_DOUBLE);
1652  for (ipt = 0; ipt < npt; ipt++) {
1653  uint32_t origin = cpl_table_get_int(pts[ipt]->table, MUSE_PIXTABLE_ORIGIN, 0, NULL);
1654  unsigned short slice = muse_pixtable_origin_get_slice(origin);
1655  double median = cpl_table_get_column_median(pts[ipt]->table,
1656  MUSE_PIXTABLE_DATA);
1657  cpl_msg_debug(__func__, "Found median of %f in slice %d of IFU %hhu "
1658  "of illum flat.", median, slice, ifu);
1659  cpl_table_set_int(tattached, "slice", ipt, slice);
1660  cpl_table_set_double(tattached, "fflat", ipt, 1. / median);
1661  } /* for ipt */
1663  /* normalize the flat-field scales */
1664  double mean = cpl_table_get_column_mean(tattached, "fflat");
1665  cpl_msg_debug(__func__, "Normalizing whole illum-flat table if IFU %hhu to "
1666  "%e.", ifu, mean);
1667  cpl_table_multiply_scalar(tattached, "fflat", 1. / mean);
1668  cpl_table_set_column_format(tattached, "fflat", "%.6f");
1669  return tattached;
1670 } /* muse_basicproc_prepare_illum() */
1671 
1672 /*---------------------------------------------------------------------------*/
1685 /*---------------------------------------------------------------------------*/
1686 cpl_error_code
1687 muse_basicproc_apply_illum(muse_pixtable *aPT, cpl_table *aAttached)
1688 {
1689  cpl_ensure_code(aPT && aPT->header && aPT->table && aAttached,
1690  CPL_ERROR_NULL_INPUT);
1691 
1692  unsigned char ifu = muse_utils_get_ifu(aPT->header);
1693 
1695  int ipt, npt = muse_pixtable_extracted_get_size(pts);
1696  cpl_msg_info(__func__, "Applying %s flat-field in IFU %hhu (%d slices)",
1697  MUSE_TAG_ILLUM, ifu, npt);
1698  cpl_array *afactors = cpl_array_new(npt, CPL_TYPE_DOUBLE);
1699  for (ipt = 0; ipt < npt; ipt++) {
1700  uint32_t origin = cpl_table_get_int(pts[ipt]->table, MUSE_PIXTABLE_ORIGIN, 0, NULL);
1701  unsigned short slice = muse_pixtable_origin_get_slice(origin),
1702  fslice = cpl_table_get_int(aAttached, "slice", ipt, NULL);
1703  int err;
1704  double fflat = cpl_table_get_double(aAttached, "fflat", ipt, &err);
1705  if (!err && slice == fslice) {
1706  cpl_table_multiply_scalar(pts[ipt]->table, MUSE_PIXTABLE_DATA, fflat);
1707  cpl_table_multiply_scalar(pts[ipt]->table, MUSE_PIXTABLE_STAT, fflat*fflat);
1708  cpl_array_set_double(afactors, ipt, fflat);
1709  char *kw = cpl_sprintf(MUSE_HDR_PT_ILLUMi, slice);
1710  cpl_propertylist_update_double(aPT->header, kw, fflat);
1711  cpl_free(kw);
1712  } else {
1713  cpl_msg_warning(__func__, "some error (%d) occurred when applying illum-"
1714  "flat correction to slice %hu/%hu of IFU %hhu: %s", err,
1715  slice, fslice, ifu, cpl_error_get_message());
1716  } /* else */
1717  } /* for ipt */
1719  cpl_propertylist_update_double(aPT->header, MUSE_HDR_PT_ILLUM_MEAN,
1720  cpl_array_get_mean(afactors));
1721  cpl_propertylist_update_double(aPT->header, MUSE_HDR_PT_ILLUM_STDEV,
1722  cpl_array_get_stdev(afactors));
1723  cpl_array_delete(afactors);
1724  return CPL_ERROR_NONE;
1725 } /* muse_basicproc_apply_illum() */
1726 
1727 /*---------------------------------------------------------------------------*/
1744 /*---------------------------------------------------------------------------*/
1745 cpl_error_code
1747 {
1748  cpl_ensure_code(aPT && aPT->header && aPT->table && aTwilight,
1749  CPL_ERROR_NULL_INPUT);
1750 
1751  // XXX should check, if the twilight cube is of the same INS.MODE as the data
1752 
1753  unsigned char ifu = muse_utils_get_ifu(aPT->header);
1754 
1755  /* transfer the sky flat flux value */
1756  char *kw = cpl_sprintf(MUSE_HDR_FLAT_FLUX_SKY"%hhu", ifu);
1757  double flux = cpl_propertylist_get_double(aTwilight->header, kw);
1758  cpl_free(kw);
1759  cpl_propertylist_update_double(aPT->header, MUSE_HDR_FLAT_FLUX_SKY, flux);
1760 
1761  /* get the WCS from the twilight cube */
1762  int nx = cpl_image_get_size_x(cpl_imagelist_get(aTwilight->data, 0)),
1763  ny = cpl_image_get_size_y(cpl_imagelist_get(aTwilight->data, 0)),
1764  nz = cpl_imagelist_get_size(aTwilight->data);
1765  cpl_msg_debug(__func__, "Working with %d planes in twilight cube", nz);
1766  double cd12 = cpl_propertylist_get_double(aTwilight->header, "CD1_2"),
1767  cd21 = cpl_propertylist_get_double(aTwilight->header, "CD2_1");
1768  if (cd12 > DBL_EPSILON || cd21 > DBL_EPSILON) {
1769  cpl_msg_warning(__func__, "Twilight cube contains WCS cross-terms (CD1_2"
1770  "=%e, CD2_1=%e), results will be inaccurate!", cd12, cd21);
1771  }
1772  // XXX guard against more rubbish (CTYPEi, CUNITi, cross-terms with axis3
1773  double crval1 = cpl_propertylist_get_double(aTwilight->header, "CRVAL1"),
1774  crpix1 = cpl_propertylist_get_double(aTwilight->header, "CRPIX1"),
1775  cd11 = cpl_propertylist_get_double(aTwilight->header, "CD1_1"),
1776  crval2 = cpl_propertylist_get_double(aTwilight->header, "CRVAL2"),
1777  crpix2 = cpl_propertylist_get_double(aTwilight->header, "CRPIX2"),
1778  cd22 = cpl_propertylist_get_double(aTwilight->header, "CD2_2"),
1779  crval3 = cpl_propertylist_get_double(aTwilight->header, "CRVAL3"),
1780  crpix3 = cpl_propertylist_get_double(aTwilight->header, "CRPIX3"),
1781  cd33 = cpl_propertylist_get_double(aTwilight->header, "CD3_3");
1782 
1783  /* loop through the pixel table and apply the correction */
1784  float *data = cpl_table_get_data_float(aPT->table, MUSE_PIXTABLE_DATA),
1785  *stat = cpl_table_get_data_float(aPT->table, MUSE_PIXTABLE_STAT),
1786  *xpos = cpl_table_get_data_float(aPT->table, MUSE_PIXTABLE_XPOS),
1787  *ypos = cpl_table_get_data_float(aPT->table, MUSE_PIXTABLE_YPOS),
1788  *lbda = cpl_table_get_data_float(aPT->table, MUSE_PIXTABLE_LAMBDA);
1789  cpl_size irow, nrow = muse_pixtable_get_nrow(aPT),
1790  nfailed = 0;
1791  for (irow = 0; irow < nrow; irow++) {
1792  /* find closest spatial pixel in twilight cube *
1793  * using the coordinates from the pixel table */
1794  int x = lround((xpos[irow] - crval1) / cd11 + crpix1), /* nearest neighbor */
1795  y = lround((ypos[irow] - crval2) / cd22 + crpix2); /* nearest neighbor */
1796  /* boundary conditions */
1797  if (x < 1) {
1798  x = 1;
1799  }
1800  if (x > nx) {
1801  x = nx;
1802  }
1803  if (y < 1) {
1804  y = 1;
1805  }
1806  if (y > ny) {
1807  y = ny;
1808  }
1809  double z = (lbda[irow] - crval3) / cd33 + crpix3; /* plane */
1810 #if 0
1811  cpl_msg_debug(__func__, "%"CPL_SIZE_FORMAT": %.3f %.3f %.3f => %d %d %.3f",
1812  irow, xpos[irow], ypos[irow], lbda[irow], x, y, z);
1813 #endif
1814  /* get indices of the two closest image planes for linear interpolation */
1815  int zp1 = floor(z) - 1,
1816  zp2 = ceil(z) - 1;
1817  /* boundary conditions */
1818  if (zp1 < 1) {
1819  zp1 = 0;
1820  }
1821  if (zp1 >= nz) {
1822  zp1 = nz - 1;
1823  }
1824  if (zp2< 1) {
1825  zp2 = 0;
1826  }
1827  if (zp2 >= nz) {
1828  zp2 = nz - 1;
1829  }
1830  int err1, err2;
1831  double v1 = cpl_image_get(cpl_imagelist_get(aTwilight->data, zp1),
1832  x, y, &err1),
1833  v2 = cpl_image_get(cpl_imagelist_get(aTwilight->data, zp2),
1834  x, y, &err2);
1835  double villum = 1.;
1836  double f = 1.;
1837  /* linearly interpolate the given twilight factors from both planes */
1838  if (err1 && err2) {
1839  nfailed++;
1840  continue;
1841  }
1842  if (zp1 == zp2) {
1843  villum = v1; /* same planes, just take the first value */
1844  } else if (err1) {
1845  villum = v2;
1846  } else if (err2) {
1847  villum = v1;
1848  } else { /* real interpolation */
1849  f = fabs(z - 1 - zp1);
1850  villum = v1 * (1. - f) + v2 * f;
1851  }
1852  double fillum = 1. / villum; /* the inverse as correction factor */
1853 #if 0
1854  cpl_msg_debug(__func__, "%d/%d, %f/%f -> %f -> %f => %f", zp1, zp2, v1, v2,
1855  f, villum, fillum);
1856 #endif
1857 
1858  /* multiply the data value by the inverse twilight factor */
1859  data[irow] *= fillum;
1860  /* multiply the stat value by the squared inverse twilight factor */
1861  stat[irow] *= fillum*fillum;
1862  } /* for irow (all pixel table rows) */
1863  if (nfailed) {
1864  cpl_msg_warning(__func__, "Failed to correct twilight in %"CPL_SIZE_FORMAT
1865  " of %"CPL_SIZE_FORMAT", pixels in IFU %hhu!", nfailed,
1866  nrow, ifu);
1867  } else {
1868  cpl_msg_debug(__func__, "No failures during twilight correction of %"
1869  CPL_SIZE_FORMAT" pixels in IFU %hhu", nrow, ifu);
1870  }
1871 
1872  return CPL_ERROR_NONE;
1873 } /* muse_basicproc_apply_twilight() */
1874 
1875 /*----------------------------------------------------------------------------*/
1901 /*----------------------------------------------------------------------------*/
1902 static int
1903 muse_basicproc_combine_compare_lamp(const cpl_frame *aFrame1, const cpl_frame *aFrame2)
1904 {
1905  cpl_ensure(aFrame1 && aFrame2, CPL_ERROR_NULL_INPUT, -1);
1906  const char *fn1 = cpl_frame_get_filename(aFrame1),
1907  *fn2 = cpl_frame_get_filename(aFrame2);
1908  cpl_propertylist *head1 = cpl_propertylist_load(fn1, 0),
1909  *head2 = cpl_propertylist_load(fn2, 0);
1910  if (!head1 || !head2) {
1911  cpl_propertylist_delete(head1); /* in case one was loaded... */
1912  cpl_propertylist_delete(head2);
1913  return -1;
1914  }
1915 
1916  /* Loop through all lamps in the header and find their status. The first *
1917  * missing shutter entry will cause the FITS query to throw an error, that's *
1918  * when we stop. Otherwise, we are done when the lamp status is not equal. */
1919  int nlamp = 1, status1, status2;
1920  cpl_errorstate prestate = cpl_errorstate_get();
1921  do {
1922  /* ensure that we are dealing with the same lamps! */
1923  const char *name1 = muse_pfits_get_lamp_name(head1, nlamp),
1924  *name2 = muse_pfits_get_lamp_name(head2, nlamp);
1925  cpl_errorstate_set(prestate); /* lamps may be missing */
1926  if (name1 && name2 && strncmp(name1, name2, strlen(name1) + 1)) {
1927  cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
1928  "Files \"%s\" and \"%s\" have incompatible lamp "
1929  "setups", fn1, fn2);
1930  cpl_propertylist_delete(head1);
1931  cpl_propertylist_delete(head2);
1932  return -1;
1933  }
1934  name1 = muse_pfits_get_shut_name(head1, nlamp);
1935  name2 = muse_pfits_get_shut_name(head2, nlamp);
1936  if (name1 && name2 && strncmp(name1, name2, strlen(name1) + 1)) {
1937  cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
1938  "Files \"%s\" and \"%s\" have incompatible shutter "
1939  "setups", fn1, fn2);
1940  cpl_propertylist_delete(head1);
1941  cpl_propertylist_delete(head2);
1942  return -1;
1943  }
1944 
1945  status1 = muse_pfits_get_lamp_status(head1, nlamp);
1946  status2 = muse_pfits_get_lamp_status(head2, nlamp);
1947  cpl_errorstate_set(prestate); /* lamps may be missing */
1948  if (status1 != status2) {
1949  break;
1950  }
1951  status1 = muse_pfits_get_shut_status(head1, nlamp);
1952  status2 = muse_pfits_get_shut_status(head2, nlamp);
1953  if (status1 != status2) {
1954  break;
1955  }
1956  nlamp++;
1957  } while (cpl_errorstate_is_equal(prestate));
1958  cpl_errorstate_set(prestate);
1959 
1960  cpl_propertylist_delete(head1);
1961  cpl_propertylist_delete(head2);
1962  return status1 == status2;
1963 } /* muse_basicproc_combine_compare_lamp() */
1964 
1965 /*---------------------------------------------------------------------------*/
1991 /*---------------------------------------------------------------------------*/
1994  unsigned char aIFU,
1995  muse_basicproc_params *aBPars,
1996  cpl_frameset ***aLabeledFrames)
1997 {
1998  if (aLabeledFrames) { /* NULL out return pointer, in case it's given... */
1999  *aLabeledFrames = NULL; /* ... so it's always NULL in case of problems */
2000  }
2001  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
2002 
2003  /* find out different lamps in input */
2004  cpl_frameset *rawframes = muse_frameset_find_tags(aProcessing->inframes,
2005  aProcessing->intags, aIFU,
2006  CPL_FALSE);
2007  char *prefix = cpl_sprintf("muse.%s", aProcessing->name);
2008  muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
2009  prefix);
2010  cpl_free(prefix);
2011 #if 0
2012  printf("rawframes\n");
2013  cpl_frameset_dump(rawframes, stdout);
2014  fflush(stdout);
2015 #endif
2016  cpl_size nlabels,
2017  *labels = cpl_frameset_labelise(rawframes,
2018  muse_basicproc_combine_compare_lamp,
2019  &nlabels);
2020  if (!labels || nlabels <= 1) {
2021  /* if labeling didn't work, process the list of one lamp and return */
2022  cpl_free(labels);
2023  cpl_frameset_delete(rawframes);
2024  muse_imagelist *list = muse_basicproc_load(aProcessing, aIFU, aBPars),
2025  *images = NULL;
2026  if (nlabels == 1) {
2027  muse_image *image = muse_combine_images(cpars, list);
2028  images = muse_imagelist_new();
2029  muse_imagelist_set(images, image, 0);
2030  if (aLabeledFrames) {
2031  *aLabeledFrames = cpl_calloc(1, sizeof(cpl_frameset *));
2032  (*aLabeledFrames)[0] = cpl_frameset_duplicate(aProcessing->usedframes);
2033  } /* if */
2034  } /* if only one label */
2035  muse_imagelist_delete(list);
2036  muse_combinepar_delete(cpars);
2037  return images;
2038  } /* if one of no labels */
2039 
2040 #if 0
2041  cpl_array *alabels = cpl_array_wrap_int(labels, cpl_frameset_get_size(rawframes));
2042  cpl_array_dump(alabels, 0, 1000, stdout);
2043  fflush(stdout);
2044  cpl_array_unwrap(alabels);
2045 #endif
2046  /* output list of lampwise combined images */
2047  muse_imagelist *images = muse_imagelist_new();
2048  if (aLabeledFrames) {
2049  *aLabeledFrames = cpl_calloc(nlabels, sizeof(cpl_frameset *));
2050  }
2051 
2052  /* duplicate aProcessing into a local structure the contents of which *
2053  * we can manipulate here (don't change it directly for threadsafety!) */
2054  muse_processing *proc = cpl_malloc(sizeof(muse_processing));
2055  memcpy(proc, aProcessing, sizeof(muse_processing));
2056  cpl_frameset *inframes = proc->inframes;
2057  /* copy frames with all extra frames somewhere else */
2058  cpl_frameset *auxframes = muse_frameset_find_tags(inframes, aProcessing->intags,
2059  aIFU, CPL_TRUE);
2060  /* loop through labels for all lamps */
2061  int ilabel, ipos = 0;
2062  for (ilabel = 0; ilabel < nlabels; ilabel++) {
2063  /* create new sub-frameset for this lamp */
2064  cpl_frameset *frames = cpl_frameset_extract(rawframes, labels, ilabel);
2065  /* append other files for the initial processing */
2066  cpl_frameset_join(frames, auxframes);
2067  /* substitute aProcessing->inframes */
2068  proc->inframes = frames;
2069  /* load and combine frames for this sub-frameset */
2070  muse_imagelist *list = muse_basicproc_load(proc, aIFU, aBPars);
2071  /* reinstate original aProcessing->inframes */
2072  proc->inframes = inframes;
2073  if (!list) { /* break this loop to fail the function below */
2074  muse_imagelist_delete(images);
2075  cpl_frameset_delete(frames);
2076  images = NULL;
2077  /* if muse_basicproc_load() fails then because of some missing *
2078  * calibration, and then it will already fail for the first *
2079  * ilabel; it is therefore enough to free the pointer */
2080  if (aLabeledFrames) {
2081  cpl_free(*aLabeledFrames);
2082  *aLabeledFrames = NULL;
2083  }
2084  break;
2085  }
2086 
2087  muse_image *lampimage = muse_combine_images(cpars, list);
2088  if (!lampimage) {
2089  cpl_msg_error(__func__, "Image combination failed for IFU %hhu for lamp "
2090  "with label %d of %"CPL_SIZE_FORMAT, aIFU, ilabel + 1, nlabels);
2091  muse_imagelist_delete(list);
2092  cpl_frameset_delete(frames);
2093  continue;
2094  }
2095 
2096  if (aLabeledFrames) {
2097  /* if a given frame was used now, copy its group */
2098  cpl_size iframe, nframes = cpl_frameset_get_size(frames);
2099  for (iframe = 0; iframe < nframes; iframe++) {
2100  cpl_frame *frame = cpl_frameset_get_position(frames, iframe);
2101  const char *fn = cpl_frame_get_filename(frame),
2102  *tag = cpl_frame_get_tag(frame);
2103  cpl_size iuframe, nuframes = cpl_frameset_get_size(aProcessing->usedframes);
2104  for (iuframe = 0;
2105  (iuframe < nuframes) && fn && tag; /* only check with valid info */
2106  iuframe++) {
2107  cpl_frame *uframe = cpl_frameset_get_position(aProcessing->usedframes,
2108  iuframe);
2109  const char *fnu = cpl_frame_get_filename(uframe),
2110  *tagu = cpl_frame_get_tag(uframe);
2111  if (fnu && !strncmp(fn, fnu, strlen(fn) + 1) &&
2112  tagu && !strncmp(tag, tagu, strlen(tag) + 1)) {
2113  cpl_frame_set_group(frame, cpl_frame_get_group(uframe));
2114  break;
2115  }
2116  } /* for uframe (all usedframes) */
2117  } /* for frame */
2118  (*aLabeledFrames)[ipos] = frames;
2119  } else {
2120  cpl_frameset_delete(frames);
2121  }
2122 
2123  /* transfer NSATURATION headers from invidual images to lamp-combined image */
2124  unsigned int k;
2125  for (k = 0; k < muse_imagelist_get_size(list); k++) {
2126  char *keyword = cpl_sprintf(QC_WAVECAL_PREFIXi" "QC_BASIC_NSATURATED, k+1);
2127  int nsaturated = cpl_propertylist_get_int(muse_imagelist_get(list, k)->header,
2128  MUSE_HDR_TMP_NSAT);
2129  cpl_propertylist_update_int(lampimage->header, keyword, nsaturated);
2130  cpl_free(keyword);
2131  }
2132  muse_imagelist_delete(list);
2133  /* append to imagelist */
2134  muse_imagelist_set(images, lampimage, ipos++);
2135  } /* for ilabel (labels) */
2136  cpl_free(labels);
2137  cpl_free(proc);
2138  muse_combinepar_delete(cpars);
2139  cpl_frameset_delete(rawframes);
2140  cpl_frameset_delete(auxframes);
2141 
2142  if (images && muse_imagelist_get_size(images) == 0) {
2143  muse_imagelist_delete(images);
2144  images = NULL;
2145  if (aLabeledFrames) {
2146  cpl_free(*aLabeledFrames);
2147  *aLabeledFrames = NULL;
2148  }
2149  }
2150 
2151  return images;
2152 } /* muse_basicproc_combine_images_lampwise() */
2153 
2154 /*---------------------------------------------------------------------------*/
2173 /*---------------------------------------------------------------------------*/
2174 cpl_error_code
2176  double aHalfWidth, double aBinWidth)
2177 {
2178  cpl_ensure_code(aPt && aLines, CPL_ERROR_NULL_INPUT);
2179  cpl_ensure_code(cpl_array_get_type(aLines) == CPL_TYPE_DOUBLE ||
2180  cpl_array_get_type(aLines) == CPL_TYPE_FLOAT,
2181  CPL_ERROR_ILLEGAL_INPUT);
2182 
2183  double lmin = cpl_propertylist_get_float(aPt->header, MUSE_HDR_PT_LLO),
2184  lmax = cpl_propertylist_get_float(aPt->header, MUSE_HDR_PT_LHI);
2185  double shift = 0., wshift = 0.;
2186  cpl_array *errors = cpl_array_new(4, CPL_TYPE_DOUBLE);
2187  int i, n = cpl_array_get_size(aLines), nvalid = 0;
2188  for (i = 0; i < n; i++) {
2189  int err;
2190  double lambda = cpl_array_get(aLines, i, &err);
2191  if (err || lambda >= lmax || lambda <= lmin) {
2192  cpl_msg_debug(__func__, "Invalid wavelength at position %d of %d in "
2193  "skylines", i + 1, n);
2194  continue;
2195  }
2196  nvalid++;
2197  double center = muse_utils_pixtable_fit_line_gaussian(aPt, lambda, aHalfWidth,
2198  aBinWidth, NULL, errors),
2199  cerr = cpl_array_get_double(errors, 0, NULL);
2200  shift += (lambda - center) / cerr;
2201  wshift += 1. / cerr;
2202  cpl_msg_debug(__func__, "dlambda = %.4f +/- %.4f (for skyline at %.4f "
2203  "Angstrom)", lambda - center, cerr, lambda);
2204  } /* for i (all entries in aLines) */
2205  cpl_array_delete(errors);
2206  shift /= wshift;
2207  if (nvalid > 0 && isfinite(shift)) {
2208  cpl_msg_info(__func__, "Skyline correction (%d lines): shifting data of IFU "
2209  "%hhu by %.4f Angstrom", nvalid, muse_utils_get_ifu(aPt->header),
2210  shift);
2211  cpl_table_add_scalar(aPt->table, MUSE_PIXTABLE_LAMBDA, shift);
2212  cpl_propertylist_update_float(aPt->header, QC_SCIBASIC_SHIFT, shift);
2213  } else {
2214  cpl_propertylist_update_float(aPt->header, QC_SCIBASIC_SHIFT, 0.);
2215  }
2216  return CPL_ERROR_NONE;
2217 } /* muse_basicproc_shift_pixtable() */
2218 
2219 /*---------------------------------------------------------------------------*/
2231 /*---------------------------------------------------------------------------*/
2232 cpl_error_code
2233 muse_basicproc_stats_append_header(cpl_image *aImage, cpl_propertylist *aHeader,
2234  const char *aPrefix, unsigned aStats)
2235 {
2236  cpl_ensure_code(aImage, CPL_ERROR_NULL_INPUT);
2237 
2238  int nx = cpl_image_get_size_x(aImage),
2239  ny = cpl_image_get_size_y(aImage);
2240  return muse_basicproc_stats_append_header_window(aImage, aHeader, aPrefix,
2241  aStats, 1, 1, nx, ny);
2242 } /* muse_basicproc_stats_append_header() */
2243 
2244 /*---------------------------------------------------------------------------*/
2261 /*---------------------------------------------------------------------------*/
2262 cpl_error_code
2264  cpl_propertylist *aHeader,
2265  const char *aPrefix, unsigned aStats,
2266  int aX1, int aY1, int aX2, int aY2)
2267 {
2268  cpl_ensure_code(aImage && aHeader, CPL_ERROR_NULL_INPUT);
2269  cpl_ensure_code(aPrefix, CPL_ERROR_ILLEGAL_INPUT);
2270  cpl_ensure_code(aPrefix, CPL_ERROR_ILLEGAL_INPUT);
2271 
2272  cpl_stats *stats = cpl_stats_new_from_image_window(aImage, aStats,
2273  aX1, aY1, aX2, aY2);
2274  if (!stats) {
2275  return cpl_error_get_code();
2276  }
2277 
2278  char keyword[KEYWORD_LENGTH];
2279  if (aStats & CPL_STATS_MEDIAN) {
2280  snprintf(keyword, KEYWORD_LENGTH, "%s MEDIAN", aPrefix);
2281  cpl_propertylist_append_float(aHeader, keyword,
2282  cpl_stats_get_median(stats));
2283  }
2284  if (aStats & CPL_STATS_MEAN) {
2285  snprintf(keyword, KEYWORD_LENGTH, "%s MEAN", aPrefix);
2286  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_mean(stats));
2287  }
2288  if (aStats & CPL_STATS_STDEV) {
2289  snprintf(keyword, KEYWORD_LENGTH, "%s STDEV", aPrefix);
2290  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_stdev(stats));
2291  }
2292  if (aStats & CPL_STATS_MIN) {
2293  snprintf(keyword, KEYWORD_LENGTH, "%s MIN", aPrefix);
2294  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_min(stats));
2295  }
2296  if (aStats & CPL_STATS_MAX) {
2297  snprintf(keyword, KEYWORD_LENGTH, "%s MAX", aPrefix);
2298  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_max(stats));
2299  }
2300  if (aStats & CPL_STATS_FLUX) {
2301  snprintf(keyword, KEYWORD_LENGTH, "%s INTFLUX", aPrefix);
2302  cpl_propertylist_append_float(aHeader, keyword, cpl_stats_get_flux(stats));
2303  }
2304 
2305  cpl_stats_delete(stats);
2306 
2307  return CPL_ERROR_NONE;
2308 } /* muse_basicproc_stats_append_header_window() */
2309 
2310 /*---------------------------------------------------------------------------*/
2325 /*---------------------------------------------------------------------------*/
2326 cpl_error_code
2327 muse_basicproc_qc_saturated(muse_image *aImage, const char *aPrefix)
2328 {
2329  cpl_ensure_code(aImage && aImage->dq && aImage->header && aPrefix,
2330  CPL_ERROR_NULL_INPUT);
2331 
2332  cpl_mask *mask = cpl_mask_threshold_image_create(aImage->dq,
2333  EURO3D_SATURATED - 0.1,
2334  EURO3D_SATURATED + 0.1);
2335  int nsaturated = cpl_mask_count(mask);
2336  cpl_mask_delete(mask);
2337  /* check if the prefix has a trailing space, add it if not */
2338  char *keyword = NULL;
2339  if (aPrefix[strlen(aPrefix)-1] == ' ') {
2340  keyword = cpl_sprintf("%s%s", aPrefix, QC_BASIC_NSATURATED);
2341  } else {
2342  keyword = cpl_sprintf("%s %s", aPrefix, QC_BASIC_NSATURATED);
2343  }
2344  cpl_error_code rc = cpl_propertylist_update_int(aImage->header, keyword,
2345  nsaturated);
2346  cpl_free(keyword);
2347  return rc;
2348 } /* muse_basicproc_qc_saturated() */
2349 
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.
Structure definition of a MUSE datacube.
Definition: muse_datacube.h:47
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:85
void muse_pixtable_extracted_delete(muse_pixtable **aPixtables)
Delete a pixel table array.
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:631
const char * muse_pfits_get_insmode(const cpl_propertylist *aHeaders)
find out the observation mode
Definition: muse_pfits.c:1125
unsigned short muse_pixtable_origin_get_slice(uint32_t aOrigin)
Get the slice number from the encoded 32bit origin number.
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:695
int muse_pfits_get_read_id(const cpl_propertylist *aHeaders)
find out the readout mode id
Definition: muse_pfits.c:365
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:119
cpl_size muse_pixtable_extracted_get_size(muse_pixtable **aPixtables)
Get the size of an array of extracted pixel tables.
const char * name
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
Definition: muse_utils.c:99
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:2382
muse_pixtable ** muse_pixtable_extracted_get_slices(muse_pixtable *aPixtable)
Extract one pixel table per IFU and slice.
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:539
cpl_image * data
the data extension
Definition: muse_image.h:46
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:287
cpl_size muse_pixtable_get_nrow(const muse_pixtable *aPixtable)
get the number of rows within the pixel table
int muse_pfits_get_shut_status(const cpl_propertylist *aHeaders, int aShutter)
query the status of one shutter
Definition: muse_pfits.c:1311
#define MUSE_HDR_PT_LHI
FITS header keyword contains the upper limit of the data in spectral direction.
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:502
#define MUSE_HDR_PT_ILLUMi
Definition: muse_pixtable.h:98
cpl_image * stat
the statistics extension
Definition: muse_image.h:64
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:585
void muse_imagelist_delete(muse_imagelist *aList)
Free the memory of the MUSE image list.
#define MUSE_HDR_PT_ILLUM_MEAN
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.
muse_image * muse_combine_images(muse_combinepar *aCPars, muse_imagelist *aImages)
Combine several images into one.
Definition: muse_combine.c:741
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.
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:1290
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:72
cpl_boolean muse_processing_check_intags(muse_processing *aProcessing, const char *aTag, int aNChars)
Check that a tag is part of the input tags of a processing structure.
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:739
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.
cpl_frameset * usedframes
void muse_combinepar_delete(muse_combinepar *aCPars)
Clear the combination parameters.
Definition: muse_combine.c:715
cpl_image * dq
the data quality extension
Definition: muse_image.h:56
cpl_error_code muse_pixtable_restrict_wavelength(muse_pixtable *aPixtable, double aLow, double aHigh)
Restrict a pixel table to a certain wavelength range.
int muse_pfits_get_biny(const cpl_propertylist *aHeaders)
find out the binning factor in y direction
Definition: muse_pfits.c:419
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:1248
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
cpl_error_code muse_basicproc_apply_twilight(muse_pixtable *aPT, muse_datacube *aTwilight)
Apply an attached flat-field to a pixel table.
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:473
const char * muse_pfits_get_read_name(const cpl_propertylist *aHeaders)
find out the readout mode name
Definition: muse_pfits.c:383
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:672
cpl_imagelist * data
the cube containing the actual data values
Definition: muse_datacube.h:75
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:455
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:401
cpl_propertylist * header
the FITS header
Definition: muse_datacube.h:56
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:399
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:223
int muse_pfits_get_lamp_status(const cpl_propertylist *aHeaders, int aLamp)
query the status of one lamp
Definition: muse_pfits.c:1269
cpl_array * intags
muse_imagelist * muse_imagelist_new(void)
Create a new (empty) MUSE image list.
cpl_frameset * inframes
muse_basicproc_params * muse_basicproc_params_new_from_propertylist(const cpl_propertylist *aHeader)
Create a structure of basic processing parameters from a FITS header.
double muse_pfits_get_exptime(const cpl_propertylist *aHeaders)
find out the exposure time
Definition: muse_pfits.c:347
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:721
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
#define MUSE_HDR_PT_ILLUM_STDEV
cpl_error_code muse_basicproc_apply_illum(muse_pixtable *aPT, cpl_table *aAttached)
Apply an illum/attached flat-field to a pixel table.
cpl_frameset * muse_frameset_check_raw(const cpl_frameset *aFrames, const cpl_array *aTags, unsigned char aIFU)
return frameset containing good raw input data
Definition: muse_utils.c:274
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:803
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:257
cpl_table * muse_basicproc_prepare_illum(muse_pixtable *aPT)
Apply an illum/attached flat-field to a pixel table.
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:437
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:468
muse_ins_mode muse_pfits_get_mode(const cpl_propertylist *aHeaders)
find out the observation mode
Definition: muse_pfits.c:1097
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_parameterlist * muse_cplparameterlist_from_propertylist(const cpl_propertylist *aHeader, int aRecNum)
Recreate a cpl_parameterlist from the RECi headers of an output MUSE product.
cpl_propertylist * header
The FITS header.
cpl_frameset * muse_frameset_find_tags(const cpl_frameset *aFrames, const cpl_array *aTags, unsigned char aIFU, cpl_boolean aInvert)
return frameset containing data from an IFU/channel with the given tag(s)
Definition: muse_utils.c:243
cpl_error_code muse_basicproc_qc_saturated(muse_image *aImage, const char *aPrefix)
Add QC parameter about saturated pixels to a muse_image.