MUSE Pipeline Reference Manual  0.18.1
muse_create_sky.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 <string.h>
30 #include <math.h>
31 
32 #include <muse.h>
33 #include "muse_create_sky_z.h"
34 
35 /*---------------------------------------------------------------------------*
36  * Functions code *
37  *---------------------------------------------------------------------------*/
38 static muse_pixtable *
39 muse_create_sky_load_pixtable(muse_processing *aProcessing,
40  muse_create_sky_params_t *aParams)
41 {
42  muse_pixtable *pixtable = NULL;
43 
44  /* sort input pixel tables into different exposures */
45  cpl_table *exposures = muse_processing_sort_exposures(aProcessing);
46  if (!exposures) {
47  cpl_msg_error(__func__, "no science exposures found in input");
48  return NULL;
49  }
50  int nexposures = cpl_table_get_nrow(exposures);
51  if (nexposures != 1) {
52  cpl_msg_error(__func__, "More than one exposure (%i) in sky creation",
53  nexposures);
54  }
55 
56  /* now process all the pixel tables, do it separately for each exposure */
57  int i;
58  for (i = 0; i < nexposures; i++) {
59  cpl_table *thisexp = cpl_table_extract(exposures, i, 1);
61  aParams->lambdamin,
62  aParams->lambdamax);
63  cpl_table_delete(thisexp);
64  /* erase pre-existing QC parameters */
65  cpl_propertylist_erase_regexp(p->header, "ESO QC ", 0);
66  if (pixtable == NULL) {
67  pixtable = p;
68  } else {
69  cpl_table_insert(pixtable->table, p->table,
70  cpl_table_get_nrow(pixtable->table));
72  }
73  }
74  cpl_table_delete(exposures);
75 
76  if ((pixtable != NULL) && (muse_pixtable_is_skysub(pixtable) == CPL_TRUE)) {
77  cpl_msg_error(__func__, "Pixel table already sky subtracted");
78  muse_pixtable_delete(pixtable);
79  pixtable = NULL;
80  }
81 
82  cpl_table *response = muse_table_load(aProcessing, MUSE_TAG_STD_RESPONSE, 0);
83  cpl_table *extinction = muse_table_load(aProcessing, MUSE_TAG_EXTINCT_TABLE, 0);
84  cpl_table *telluric = muse_table_load(aProcessing, MUSE_TAG_STD_TELLURIC, 0);
85 
86  if ((pixtable != NULL) && (response != NULL)) {
87  if (muse_pixtable_is_fluxcal(pixtable) == CPL_TRUE) {
88  cpl_msg_error(__func__,
89  "Pixel table already flux calibrated. Dont specify %s, %s, %s",
90  MUSE_TAG_STD_RESPONSE, MUSE_TAG_EXTINCT_TABLE,
91  MUSE_TAG_STD_TELLURIC);
92  muse_pixtable_delete(pixtable);
93  pixtable = NULL;
94  } else {
95  cpl_error_code rc = muse_flux_calibrate(pixtable, response, extinction, telluric);
96  if (rc != CPL_ERROR_NONE) {
97  cpl_msg_error(__func__, "while muse_flux_calibrate");
98  muse_pixtable_delete(pixtable);
99  pixtable = NULL;
100  }
101  }
102  }
103 
104  cpl_table_delete(response);
105  cpl_table_delete(extinction);
106  cpl_table_delete(telluric);
107 
108  if ((pixtable != NULL) && (muse_pixtable_is_fluxcal(pixtable) != CPL_TRUE)) {
109  cpl_msg_error(__func__, "Pixel table not flux calibrated");
110  muse_pixtable_delete(pixtable);
111  pixtable = NULL;
112  }
113 
114  if (pixtable != NULL) {
115  cpl_table_and_selected_int(pixtable->table, MUSE_PIXTABLE_DQ,
116  CPL_NOT_EQUAL_TO, EURO3D_GOODPIXEL);
117  cpl_table_erase_selected(pixtable->table);
118  }
119  return pixtable;
120 }
121 
122 static muse_sky_master *
123 muse_create_sky_compute_master(cpl_table *spectrum, muse_processing *aProcessing)
124 {
125  cpl_array *lambda = muse_cpltable_extract_column(spectrum, "lambda");
126  double lambda_low = cpl_array_get_min(lambda);
127  double lambda_high = cpl_array_get_max(lambda);
128  cpl_table *sky_lines = muse_sky_lines_load(aProcessing);
129  if (sky_lines != NULL) {
130  muse_sky_lines_set_range(sky_lines, lambda_low-10, lambda_high+10);
131  }
132 
133  cpl_array *data = muse_cpltable_extract_column(spectrum, "data");
134  cpl_array *stat = muse_cpltable_extract_column(spectrum, "stat");
135  // do the fit, ignoring possible errors
136  cpl_errorstate prestate = cpl_errorstate_get();
137  muse_sky_master *res = muse_sky_master_fit(lambda, data, stat, sky_lines);
138  if (!cpl_errorstate_is_equal(prestate)) {
139  cpl_errorstate_dump(prestate, CPL_FALSE, NULL);
140  cpl_errorstate_set(prestate);
141  }
142 
143  cpl_table_delete(sky_lines);
144  cpl_array_unwrap(lambda);
145  cpl_array_unwrap(data);
146  cpl_array_unwrap(stat);
147  cpl_table_delete(spectrum);
148 
149  return res;
150 }
151 
152 static void
153 muse_create_sky_save_master(cpl_propertylist *aHeader,
154  const muse_sky_master *aMaster,
155  muse_processing *aProcessing)
156 {
157  cpl_frame *frame
158  = muse_processing_new_frame(aProcessing, -1, aHeader,
159  "SKY_LINES", CPL_FRAME_TYPE_TABLE);
160  if (frame != NULL) {
161  const char *filename = cpl_frame_get_filename(frame);
162  int r;
163  r = cpl_propertylist_save(aHeader, filename, CPL_IO_CREATE);
164  r = muse_cpltable_append_file(aMaster->lines, filename,
165  "LINES", muse_sky_lines_lines_def);
166  if (r == CPL_ERROR_NONE) {
167  cpl_frameset_insert(aProcessing->outputFrames, frame);
168  } else {
169  cpl_frame_delete(frame);
170  }
171  }
172  frame = muse_processing_new_frame(aProcessing, -1, aHeader,
173  "SKY_CONTINUUM", CPL_FRAME_TYPE_TABLE);
174  if (frame != NULL) {
175  const char *filename = cpl_frame_get_filename(frame);
176  int r;
177  r = cpl_propertylist_save(aHeader, filename, CPL_IO_CREATE);
178  r = muse_cpltable_append_file(aMaster->continuum, filename,
179  "CONTINUUM", muse_fluxspectrum_def);
180  if (r == CPL_ERROR_NONE) {
181  cpl_frameset_insert(aProcessing->outputFrames, frame);
182  } else {
183  cpl_frame_delete(frame);
184  }
185  }
186 }
187 
188 /*----------------------------------------------------------------------------*/
194 /*----------------------------------------------------------------------------*/
195 static cpl_error_code
196 muse_create_sky_qc_sky(cpl_propertylist *aHeader,
197  const muse_sky_master *aMaster)
198 {
199  cpl_ensure_code(aHeader && aMaster, CPL_ERROR_NULL_INPUT);
200 
201  cpl_ensure_code(aMaster->lines && cpl_table_get_nrow(aMaster->lines) > 0,
202  CPL_ERROR_DATA_NOT_FOUND);
203  char keyword[KEYWORD_LENGTH];
204  int i, ngroups = cpl_table_get_column_max(aMaster->lines, "group") + 1;
205  for (i = 0; i < ngroups; i++) {
206  cpl_table_unselect_all(aMaster->lines);
207  cpl_table_or_selected_int(aMaster->lines, "group", CPL_EQUAL_TO, i);
208  cpl_table *gtable = cpl_table_extract_selected(aMaster->lines);
209  cpl_size row;
210  cpl_table_get_column_maxpos(gtable, "flux", &row);
211  const char *name = cpl_table_get_string(gtable, "name", row);
212  double wavelength = cpl_table_get_double(gtable, "lambda", row, NULL),
213  flux = cpl_table_get_double(gtable, "flux", row, NULL);
214  snprintf(keyword, KEYWORD_LENGTH, "ESO QC SKY LINE%i NAME", i+1);
215  cpl_propertylist_append_string(aHeader, keyword, name);
216  snprintf(keyword, KEYWORD_LENGTH, "ESO QC SKY LINE%i AWAV", i+1);
217  cpl_propertylist_append_double(aHeader, keyword, wavelength);
218  snprintf(keyword, KEYWORD_LENGTH, "ESO QC SKY LINE%i FLUX", i+1);
219  if (!isfinite(flux)) {
220  /* add obviously wrong value to the header, *
221  * since CFITSIO chokes on NANs and the like */
222  cpl_propertylist_append_double(aHeader, keyword, -9999.999);
223  cpl_msg_error(__func__, "Sky-line fit failed for group %d, computed "
224  "flux is infinite!", i+1);
225  } else {
226  cpl_propertylist_append_double(aHeader, keyword, flux);
227  }
228  cpl_table_delete(gtable);
229  double offset = aMaster->lsf[0]->offset
230  + (aMaster->lsf[0]->refraction - 1) * wavelength;
231  snprintf(keyword, KEYWORD_LENGTH, "ESO QC SKY LINE%i OFFSET", i+1);
232  cpl_propertylist_append_double(aHeader, keyword, offset);
233  } /* for ngroups */
234  cpl_table_unselect_all(aMaster->lines);
235 
236  cpl_ensure_code(aMaster->continuum && cpl_table_get_nrow(aMaster->continuum) > 0,
237  CPL_ERROR_DATA_NOT_FOUND);
238  cpl_size row, nrows = cpl_table_get_nrow(aMaster->continuum);
239  double flux = 0.0;
240  for (row = 0; row < nrows; row++) {
241  flux += cpl_table_get_double(aMaster->continuum, "flux", row, NULL);
242  }
243  snprintf(keyword, KEYWORD_LENGTH, "ESO QC SKY CONT FLUX");
244  if (!isfinite(flux)) {
245  /* again, workaround for weird CFITSIO error */
246  cpl_propertylist_append_double(aHeader, keyword, -9999.999);
247  cpl_msg_error(__func__, "Sky-continuum contains infinite values, fit may "
248  "have failed!");
249  } else {
250  cpl_propertylist_append_double(aHeader, keyword, flux);
251  }
252  double maxdev = 0.0,
253  prev = cpl_table_get_double(aMaster->continuum, "flux", 0, NULL),
254  l_prev = cpl_table_get_double(aMaster->continuum, "lambda", 0, NULL);
255  for (row = 1; row < nrows; row++) {
256  double cur = cpl_table_get_double(aMaster->continuum, "flux", row, NULL),
257  l_cur = cpl_table_get_double(aMaster->continuum, "lambda", row, NULL),
258  dev = fabs((cur - prev)/ (l_cur - l_prev));
259  if (maxdev < dev) {
260  maxdev = dev;
261  }
262  prev = cur;
263  l_prev = l_cur;
264  } /* for row (continuum table rows) */
265  snprintf(keyword, KEYWORD_LENGTH, "ESO QC SKY CONT MAXDEV");
266  cpl_propertylist_append_double(aHeader, keyword, maxdev);
267 
268  return CPL_ERROR_NONE;
269 } /* muse_create_sky_qc_sky() */
270 
271 /*----------------------------------------------------------------------------*/
279 /*----------------------------------------------------------------------------*/
280 static muse_image *
281 muse_create_sky_whitelight_image(muse_pixtable *aPixtable, int aCr)
282 {
283  cpl_boolean usegrid = getenv("MUSE_COLLAPSE_PIXTABLE")
284  && atoi(getenv("MUSE_COLLAPSE_PIXTABLE")) > 0;
288  if (aCr) {
290  params->crsigma = 5.;
291  }
292  muse_pixgrid *grid = NULL;
293  muse_datacube *cube = muse_resampling_cube(aPixtable, params,
294  usegrid ? &grid : NULL);
295  muse_image *image = NULL;
296  cpl_table *fwhite = muse_table_load_filter(NULL, "white");
297  if (usegrid) {
299  image = muse_resampling_collapse_pixgrid(aPixtable, grid,
300  cube, fwhite, params);
301  } else {
302  image = muse_datacube_collapse(cube, fwhite);
303  }
305  muse_pixgrid_delete(grid);
306  muse_datacube_delete(cube);
307  cpl_table_delete(fwhite);
308  return image;
309 } /* muse_create_sky_whitelight_image() */
310 
311 /*----------------------------------------------------------------------------*/
323 /*----------------------------------------------------------------------------*/
324 
325 static void
326 muse_create_sky_create_continuum(muse_pixtable *aPixtable,
327  muse_sky_master *aMaster,
328  muse_lsf_params **aLsf,
329  double binwidth) {
330  cpl_table_delete(aMaster->continuum);
331  aMaster->continuum = NULL;
332  muse_sky_subtract_pixtable(aPixtable, aMaster, aLsf);
333  aMaster->continuum = muse_resampling_spectrum(aPixtable, binwidth);
334  cpl_table_erase_column(aMaster->continuum, "stat");
335  cpl_table_erase_column(aMaster->continuum, "dq");
336  cpl_table_name_column(aMaster->continuum, "data", "flux");
337 }
338 
339 /*----------------------------------------------------------------------------*/
346 /*----------------------------------------------------------------------------*/
347 int
348 muse_create_sky_compute(muse_processing *aProcessing,
349  muse_create_sky_params_t *aParams)
350 {
351  muse_pixtable *pixtable = muse_create_sky_load_pixtable(aProcessing, aParams);
352  if (pixtable == NULL) {
353  cpl_msg_error(__func__, "Could not load pixel table");
354  return -1;
355  }
356 
357  /*
358  First step: find the regions containing sky:
359  - create a whitelight image
360  - create the mask from it
361  - select pixtable rows containing sky
362  - create spectrum from selected rows
363  */
364  muse_mask *smask = muse_processing_mask_load(aProcessing, MUSE_TAG_SKY_MASK);
365  if (smask) { /* apply existing mask on input pixel table */
366  cpl_table_select_all(pixtable->table);
367  muse_pixtable_and_selected_mask(pixtable, smask);
368  muse_mask_delete(smask);
369  }
370 
371  int cube_cr = (!strncmp(aParams->cr_s, "cube", 5));
372  muse_image *whitelight = muse_create_sky_whitelight_image(pixtable, cube_cr);
373  muse_processing_save_image(aProcessing, -1, whitelight, MUSE_TAG_IMAGE_FOV);
374 
375  muse_mask *sky_mask = muse_sky_create_skymask(whitelight, aParams->fraction);
376  muse_processing_save_mask(aProcessing, -1, sky_mask, MUSE_TAG_SKY_MASK);
377 
378  cpl_table_select_all(pixtable->table);
379  muse_pixtable_and_selected_mask(pixtable, sky_mask);
380  muse_image_delete(whitelight);
381  muse_mask_delete(sky_mask);
382 
383  cpl_table *spectrum = muse_resampling_spectrum(pixtable, aParams->sampling);
384 
385  if (!strncmp(aParams->cr_s, "spectrum", 9)) {
386  muse_sky_mark_cosmic(spectrum, pixtable);
387  cpl_table_delete(spectrum);
388  spectrum = muse_resampling_spectrum(pixtable, aParams->sampling);
389  }
390 
391  if (spectrum == NULL) {
392  muse_pixtable_delete(pixtable);
393  return -1;
394  }
395  muse_processing_save_table(aProcessing, -1, spectrum, pixtable->header,
396  MUSE_TAG_SKY_SPECTRUM, MUSE_TABLE_TYPE_CPL);
397 
398  /*
399  Second step: create master sky, containing line list and
400  continuum. If a continuum was given as calibration frame, we
401  ignore the computed one and replace it by the external one.
402  */
403  cpl_msg_info(__func__, "Creating master sky spectrum using fits to lines "
404  "(fluxes) and residual continuum");
405  muse_sky_master *master
406  = muse_create_sky_compute_master(spectrum, aProcessing);
407  if (master == NULL) {
408  muse_pixtable_delete(pixtable);
409  return -1;
410  }
411 
412  cpl_errorstate prestate = cpl_errorstate_get();
413 
414  cpl_table *continuum = muse_sky_continuum_load(aProcessing);
415  muse_lsf_params **lsfParam =
416  muse_processing_lsf_params_load(aProcessing, 0);
417  if (continuum != NULL) {
418  cpl_table_delete(master->continuum);
419  master->continuum = continuum;
420  } else if (lsfParam != NULL) {
421  muse_create_sky_create_continuum(pixtable, master,
422  lsfParam, aParams->csampling);
423  }
424  muse_lsf_params_delete(lsfParam);
425 
426  cpl_propertylist *qc_header = cpl_propertylist_new();
427  muse_create_sky_qc_sky(qc_header, master);
428 
429  muse_create_sky_save_master(qc_header, master, aProcessing);
430 
431  /* Clean up the local objects. */
432  muse_sky_master_delete(master);
433  cpl_propertylist_delete(qc_header);
434  muse_pixtable_delete(pixtable);
435  return cpl_errorstate_is_equal(prestate) ? 0 : -1;
436 }
Structure definition of a MUSE datacube.
Definition: muse_datacube.h:48
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
Definition: muse_image.c:84
int muse_processing_save_mask(muse_processing *aProcessing, int aIFU, muse_mask *aMask, const char *aTag)
Save a computed MUSE mask to disk.
cpl_error_code muse_pixtable_and_selected_mask(muse_pixtable *aPixtable, muse_mask *aMask)
Select all pixels where the (x,y) positions are enabled in the given mask.
cpl_table * muse_sky_lines_load(muse_processing *)
Load the sky data files.
Structure to hold the MASTER SKY result.
Definition: muse_sky.h:59
double csampling
Spectral sampling of the continuum spectrum [Angstrom].
void muse_datacube_delete(muse_datacube *aCube)
Deallocate memory associated to a muse_datacube object.
const muse_cpltable_def muse_sky_lines_lines_def[]
muse_image * muse_datacube_collapse(muse_datacube *aCube, cpl_table *aFilter)
Integrate a FITS NAXIS=3 datacube along the wavelength direction.
muse_mask * muse_sky_create_skymask(muse_image *, double)
Select spaxels to be considered as sky.
The pixel grid.
Definition: muse_pixgrid.h:56
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:41
muse_mask * muse_processing_mask_load(muse_processing *aProcessing, const char *aTag)
Load a mask file and its FITS header.
cpl_table * table
The pixel table.
cpl_table * muse_table_load_filter(muse_processing *aProcessing, const char *aFilterName)
Load a table for a given filter name.
Definition: muse_utils.c:585
void muse_sky_mark_cosmic(cpl_table *aSpectrum, muse_pixtable *aPixtable)
Mark all pixel above a certain limit as COSMIC.
muse_resampling_crstats_type crtype
cpl_boolean muse_pixtable_is_skysub(muse_pixtable *aPixtable)
Determine whether the pixel table is sky subtracted.
cpl_table * muse_sky_continuum_load(muse_processing *)
Load the SKY_CONTINUUM spectrum.
Structure definition of MUSE pixel table.
cpl_frame * muse_processing_new_frame(muse_processing *aProcessing, int aIFU, cpl_propertylist *aHeader, const char *aTag, cpl_frame_type aType)
Create a new frame for a result file.
cpl_array * muse_cpltable_extract_column(cpl_table *aTable, const char *aColumn)
Create an array from a section of a column.
cpl_table * lines
Table of Atmospheric emission lines and their intensities.
Definition: muse_sky.h:63
muse_resampling_params * muse_resampling_params_new(muse_resampling_type aMethod)
Create the resampling parameters structure.
cpl_boolean muse_pixtable_is_fluxcal(muse_pixtable *aPixtable)
Determine whether the pixel table is flux calibrated.
cpl_frameset * outputFrames
cpl_error_code muse_flux_calibrate(muse_pixtable *aPixtable, const cpl_table *aResponse, const cpl_table *aExtinction, const cpl_table *aTelluric)
Convert the input pixel table from counts to fluxes.
Definition: muse_flux.c:2161
const muse_cpltable_def muse_fluxspectrum_def[]
Definition of the flux spectrum table structure.
cpl_error_code muse_sky_lines_set_range(cpl_table *, double, double)
Limit the lines in the table to a wavelength range.
void muse_sky_master_delete(muse_sky_master *)
Delete a MASTER SKY structure.
muse_datacube * muse_resampling_cube(muse_pixtable *aPixtable, muse_resampling_params *aParams, muse_pixgrid **aPixgrid)
Resample a pixel table onto a regular grid structure representing a FITS NAXIS=3 datacube.
muse_sky_master * muse_sky_master_fit(const cpl_array *, const cpl_array *, const cpl_array *, const cpl_table *)
Fit all entries of the pixel table to the master sky.
muse_image * muse_resampling_collapse_pixgrid(muse_pixtable *aPixtable, muse_pixgrid *aPixgrid, muse_datacube *aCube, cpl_table *aFilter, muse_resampling_params *aParams)
Integrate a pixel table / pixel grid along the wavelength direction.
void muse_pixgrid_delete(muse_pixgrid *aPixels)
Delete a pixgrid and remove its memory.
Definition: muse_pixgrid.c:398
int muse_processing_save_image(muse_processing *aProcessing, int aIFU, muse_image *aImage, const char *aTag)
Save a computed MUSE image to disk.
muse_pixtable * muse_pixtable_load_merge_channels(cpl_table *aExposureList, double aLambdaMin, double aLambdaMax)
Load and merge the pixel tables of the 24 MUSE sub-fields.
Handling of "mask" files.
Definition: muse_mask.h:43
double lambdamin
Cut off the data below this wavelength after loading the pixel table(s).
double sampling
Spectral sampling of the sky spectrum [Angstrom].
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_save_table(muse_processing *aProcessing, int aIFU, void *aTable, cpl_propertylist *aHeader, const char *aTag, muse_table_type aType)
Save a computed table to disk.
double fraction
Fraction of the image to be considered as sky. If an input sky mask is provided, the fraction is appl...
muse_resampling_type
Resampling types.
double lambdamax
Cut off the data above this wavelength after loading the pixel table(s).
Resampling parameters.
void muse_resampling_params_delete(muse_resampling_params *aParams)
Delete a resampling parameters structure.
muse_resampling_type method
muse_lsf_params ** muse_processing_lsf_params_load(muse_processing *aProcessing, int aIFU)
Load slice LSF parameters.
Definition: muse_lsf.c:392
void muse_mask_delete(muse_mask *aMask)
Deallocate memory associated to a muse_mask object.
Definition: muse_mask.c:70
const char * cr_s
Type of cosmic ray cleaning to use. "Cube" is the standard CR cleaning which works on a datacube...
cpl_table * continuum
Continuum flux table
Definition: muse_sky.h:65
cpl_table * muse_resampling_spectrum(muse_pixtable *aPixtable, double aBinwidth)
Resample the selected pixels of a pixel table into a spectrum.
cpl_table * muse_processing_sort_exposures(muse_processing *aProcessing)
Sort input frames (containing lists of pixel table filenames) into different exposures.
Structure definition of detector (slice) parameters.
Definition: muse_lsf.h:51
void muse_pixtable_delete(muse_pixtable *aPixtable)
Deallocate memory associated to a pixel table object.
Structure to hold the parameters of the muse_create_sky recipe.
cpl_error_code muse_sky_subtract_pixtable(muse_pixtable *a, muse_sky_master *, muse_lsf_params **)
Subtract the sky spectrum from the "data" column of a pixel table.
muse_lsf_params ** lsf
LSF parameter for the resampled spectrum.
Definition: muse_sky.h:67
void muse_lsf_params_delete(muse_lsf_params **aParams)
Delete an allocated array of muse_lsf_params structure.
Definition: muse_lsf.c:147
cpl_error_code muse_cpltable_append_file(const cpl_table *aTable, const char *aFile, const char *aExtension, const muse_cpltable_def aDefinition[])
Save a table to disk (into a FITS extension)
cpl_propertylist * header
The FITS header.