MUSE Pipeline Reference Manual  2.1.1
muse_cube_combine.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) 2014-2016 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 #include <muse.h>
23 #include <string.h>
24 
25 /*----------------------------------------------------------------------------*/
56 /*----------------------------------------------------------------------------*/
57 
60 #define PRINT_USAGE(rc) \
61  fprintf(stderr, "Usage: %s CUBE_OUT CUBE_IN_1 CUBE_IN_2 [ CUBE_IN_3 ... ]\n",\
62  argv[0]); \
63  cpl_end(); return (rc);
64 
65 int main(int argc, char **argv)
66 {
67  const char *idstring = "muse_cube_combine";
68  cpl_init(CPL_INIT_DEFAULT);
69  cpl_msg_set_time_on();
71  cpl_msg_set_level(CPL_MSG_DEBUG);
72  cpl_msg_set_component_on();
73  cpl_errorstate state = cpl_errorstate_get();
74 
75  if (argc < 4) {
76  /* three filenames are needed at least */
77  PRINT_USAGE(1);
78  }
79 
80  char *oname = NULL; /* output cube */
81  int i, noverlap = 1;
82 
83  /* argument processing */
84  for (i = 1; i < argc; i++) {
85  if (strncmp(argv[i], "-xxx", 5) == 0) { // XXX
86  /* skip to next arg to get start value */
87  i++;
88 // if (i < argc) {
89 // x1 = atof(argv[i]);
90 // } else {
91 // PRINT_USAGE(2);
92 // }
93  } else if (strncmp(argv[i], "-", 1) == 0) { /* unallowed options */
94  PRINT_USAGE(9);
95  } else {
96  if (oname) {
97  break; /* we have the required name, skip the rest */
98  }
99  oname = argv[i];
100  }
101  } /* for i (all arguments) */
102  if (!oname) {
103  PRINT_USAGE(10);
104  }
105  FILE *fp = fopen(oname, "r");
106  if (fp) {
107  cpl_msg_error(idstring, "Output cube \"%s\" is already present!", oname);
108  cpl_msg_error(idstring, "Please specify another output name or rename the "
109  "existing file with this name.");
110  fclose(fp);
111  PRINT_USAGE(11);
112  }
113 
114  int ncubes = argc - i;
115  cpl_msg_info(idstring, "Will write combination of %d cubes to \"%s\".",
116  ncubes, oname);
117 
118  /* create table of the cubes involved here */
119  cpl_table *table = cpl_table_new(ncubes);
120  cpl_table_new_column(table, "FILENAME", CPL_TYPE_STRING);
121  cpl_table_new_column(table, "LMIN", CPL_TYPE_DOUBLE);
122  cpl_table_new_column(table, "LMAX", CPL_TYPE_DOUBLE);
123  cpl_table_new_column(table, "P1", CPL_TYPE_INT);
124  cpl_table_new_column(table, "P2", CPL_TYPE_INT);
125 
126  /* check ranges and grids of the cubes involved, save in the table */
127  int irecout = -1, /* output receipe parameter set */
128  iparout1 = -1, /* and output parameter numbers for low */
129  iparout2 = -1; /* and high wavelength limits */
130  double lminout = 99999., /* variables to store final output limit for low */
131  lmaxout = -99999.; /* and high wavelength limits */
132  char *ctype1ref = NULL, *ctype2ref = NULL,
133  *cunit1ref = NULL, *cunit2ref = NULL;
134  double crpix1ref = NAN, crpix2ref = NAN, crval1ref = NAN, crval2ref = NAN,
135  cd11ref = NAN, cd12ref = NAN, cd21ref = NAN, cd22ref = NAN;
136  char *ctype3ref = NULL;
137  cpl_boolean loglambda = CPL_FALSE;
138  double crpix3ref = NAN,
139  crval3ref = NAN,
140  cd33ref = NAN;
141  int ic;
142  for (ic = 0; ic < ncubes; ic++) {
143  int argidx = ic + 2;
144  char *iname = argv[argidx];
145  /* search for the data extension */
146  int iext = cpl_fits_find_extension(iname, "DATA");
147  if (iext < 0) {
148  cpl_msg_warning(idstring, "Could not open cube \"%s\"", iname);
149  continue;
150  }
151 
152  /* load primary header and get lambda ranges */
153  cpl_propertylist *header = cpl_propertylist_load(iname, 0);
154  double lmin = -FLT_MAX,
155  lmax = FLT_MAX;
156  /* search for last set of recipe parameters stored in the cube, */
157  cpl_errorstate es = cpl_errorstate_get();
158  int irec = 0;
159  do {
160  irec++;
161  char *hname = cpl_sprintf("ESO PRO REC%d ID", irec);
162  /* recipe name should exist at least, so search that */
163  const char *name = cpl_propertylist_get_string(header, hname);
164  cpl_free(hname);
165  if (!name) {
166  break;
167  }
168  cpl_msg_debug(idstring, "REC = %d, ID = %s", irec, name);
169  } while (cpl_errorstate_is_equal(es));
170  cpl_errorstate_set(es);
171  cpl_msg_info(idstring, "Using recipe parameters ESO PRO REC%d to determine "
172  "wavelength ranges", --irec);
173  if (irecout < 0) {
174  irecout = irec; /* remember output receipe parameter set */
175  }
176 
177  /* loop through the recipe parameters stored in the cube */
178  int ipar = 0;
179  do {
180  ipar++;
181  char *hname = cpl_sprintf("ESO PRO REC%d PARAM%d NAME", irec, ipar);
182  const char *name = cpl_propertylist_get_string(header, hname);
183  cpl_free(hname);
184  cpl_boolean islmin = name && !strncmp(name, "lambdamin", 10),
185  islmax = name && !strncmp(name, "lambdamax", 10);
186  if (islmin || islmax) {
187  char *hval = cpl_sprintf("ESO PRO REC%d PARAM%d VALUE", irec, ipar);
188  if (islmin) {
189  if (iparout1 < 0) {
190  iparout1 = ipar; /* remember parameter number */
191  }
192  lmin = atof(cpl_propertylist_get_string(header, hval));
193  cpl_msg_debug(idstring, "lmin = %f", lmin);
194  if (lmin < lminout) {
195  lminout = lmin; /* remember minimum output limit */
196  }
197  } else {
198  if (iparout2 < 0) {
199  iparout2 = ipar; /* remember parameter number */
200  }
201  lmax = atof(cpl_propertylist_get_string(header, hval));
202  cpl_msg_debug(idstring, "lmax = %f", lmax);
203  if (lmax > lmaxout) {
204  lmaxout = lmax; /* remember maximum output limit */
205  }
206  }
207  cpl_free(hval);
208  } /* if */
209  } while (cpl_errorstate_is_equal(es));
210  cpl_errorstate_set(es);
211  cpl_propertylist_delete(header);
212 
213  /* load DATA header and get lambda solution */
214  header = cpl_propertylist_load(iname, iext);
215  double crpix3 = muse_pfits_get_crpix(header, 3),
216  crval3 = muse_pfits_get_crval(header, 3),
217  cd33 = muse_pfits_get_cd(header, 3, 3);
218  int naxis3 = cpl_propertylist_get_int(header, "NAXIS3");
219 
220  const char *cunit3 = muse_pfits_get_cunit(header, 3),
221  *lunit = cpl_table_get_column_unit(table, "LMIN");
222  if (!lunit) {
223  cpl_table_set_column_unit(table, "LMIN", cunit3);
224  cpl_table_set_column_unit(table, "LMAX", cunit3);
225  } else if (strncmp(lunit, cunit3, strlen(lunit) + 1)) {
226  cpl_msg_warning(idstring, "Cube %d does not contain wavelengths in "
227  "%s (%s), skipping it", ic + 1, lunit, cunit3);
228  cpl_propertylist_delete(header);
229  continue;
230  }
231  const char *ctype3 = muse_pfits_get_ctype(header, 3);
232  if (!ctype3ref) {
233  ctype3ref = cpl_strdup(ctype3);
234  crpix3ref = crpix3;
235  crval3ref = crval3;
236  cd33ref = cd33;
237  loglambda = ctype3ref && (!strncmp(ctype3ref, "AWAV-LOG", 9) ||
238  !strncmp(ctype3ref, "WAVE-LOG", 9));
239  } else if (strncmp(ctype3ref, ctype3, strlen(ctype3ref)) ||
240  fabs(crpix3 - crpix3ref) > DBL_EPSILON ||
241  fabs(crval3 - crval3ref) > DBL_EPSILON ||
242  fabs(cd33 - cd33ref) > DBL_EPSILON) {
243  cpl_msg_warning(idstring, "Cube %d does not match in spectral WCS, "
244  "skipping it", ic + 1);
245  cpl_propertylist_delete(header);
246  continue;
247  }
248  double lpx1 = (1. - crpix3) * cd33 + crval3,
249  lpx2 = (naxis3 - crpix3) * cd33 + crval3;
250  if (loglambda) {
251  lpx1 = crval3 * exp((1 - crpix3) * cd33 / crval3);
252  lpx2 = crval3 * exp((naxis3 - crpix3) * cd33 / crval3);
253  }
254  cpl_msg_debug(idstring, "Cube %d, axis 3 WCS: %f %f %f (%s, %d pixels, "
255  "%f..%f %s)", ic + 1, crpix3, crval3, cd33, ctype3, naxis3,
256  lpx1, lpx2, cunit3);
257  /* check min/max wavelengths against boundary conditions */
258  if (lmin < lpx1) {
259  lmin = lpx1;
260  }
261  if (lmax > lpx2) {
262  lmax = lpx2;
263  }
264 
265  /* check spatial WCS against first cube */
266  const char *ctype1 = muse_pfits_get_ctype(header, 1),
267  *ctype2 = muse_pfits_get_ctype(header, 2),
268  *cunit1 = muse_pfits_get_cunit(header, 1),
269  *cunit2 = muse_pfits_get_cunit(header, 2);
270  double crpix1 = muse_pfits_get_crpix(header, 1),
271  crpix2 = muse_pfits_get_crval(header, 2),
272  crval1 = muse_pfits_get_crval(header, 1),
273  crval2 = muse_pfits_get_crval(header, 2),
274  cd11 = muse_pfits_get_cd(header, 1, 1),
275  cd12 = muse_pfits_get_cd(header, 1, 2),
276  cd21 = muse_pfits_get_cd(header, 2, 1),
277  cd22 = muse_pfits_get_cd(header, 2, 2);
278  if (!ctype1ref) {
279  ctype1ref = cpl_strdup(ctype1);
280  ctype2ref = cpl_strdup(ctype2);
281  cunit1ref = cpl_strdup(cunit1);
282  cunit2ref = cpl_strdup(cunit2);
283  crpix1ref = crpix1;
284  crval1ref = crval1;
285  crpix2ref = crpix2;
286  crval2ref = crval2;
287  cd11ref = cd11;
288  cd12ref = cd12;
289  cd21ref = cd21;
290  cd22ref = cd22;
291  } else if (strncmp(ctype1ref, ctype1, strlen(ctype1ref)) ||
292  strncmp(ctype2ref, ctype2, strlen(ctype2ref)) ||
293  strncmp(cunit1ref, cunit1, strlen(cunit1ref)) ||
294  strncmp(cunit2ref, cunit2, strlen(cunit2ref)) ||
295  fabs(crpix1 - crpix1ref) > DBL_EPSILON ||
296  fabs(crval1 - crval1ref) > DBL_EPSILON ||
297  fabs(crpix2 - crpix2ref) > DBL_EPSILON ||
298  fabs(crval2 - crval2ref) > DBL_EPSILON ||
299  fabs(cd11 - cd11ref) > DBL_EPSILON ||
300  fabs(cd12 - cd12ref) > DBL_EPSILON ||
301  fabs(cd21 - cd21ref) > DBL_EPSILON ||
302  fabs(cd22 - cd22ref) > DBL_EPSILON) {
303  cpl_msg_warning(idstring, "Cube %d does not match in spatial WCS, "
304  "skipping it", ic + 1);
305  cpl_propertylist_delete(header);
306  continue;
307  }
308  cpl_propertylist_delete(header);
309 
310  /* compute good valid range of pixels in dispersion direction */
311  int l1 = lround((lmin - crval3) / cd33 + crpix3),
312  l2 = lround((lmax - crval3) / cd33 + crpix3);
313  if (loglambda) {
314  l1 = lround(crval3 / cd33 * log(lmin / crval3)) + crpix3;
315  l2 = lround(crval3 / cd33 * log(lmax / crval3)) + crpix3;
316  }
317  cpl_msg_debug(idstring, "Cube %d: %d..%d (%f..%f Angstrom)", ic + 1,
318  l1, l2, lmin, lmax);
319  if (l1 <= 1) {
320  l1 = 1;
321  } else if (l1 >= naxis3) {
322  cpl_msg_warning(idstring, "Something is wrong with cube %d: l1 = %d!",
323  ic + 1, l1);
324  continue;
325  } else {
326  /* throw away plane(s) from the starting edge to mitigate resampling edge effects */
327  l1 += noverlap;
328  }
329  if (l2 >= naxis3) {
330  l2 = naxis3;
331  } else if (l2 <= 1) {
332  cpl_msg_warning(idstring, "Something is wrong with cube %d: l2 = %d!",
333  ic + 1, l2);
334  continue;
335  } else {
336  l2 -= noverlap; /* throw away plane(s) from the ending edge */
337  }
338  cpl_msg_debug(idstring, "Cube %d: ---> %d..%d", ic + 1, l1, l2);
339 
340  /* all was OK, so we can save info of this cube to the table */
341  cpl_table_set_string(table, "FILENAME", ic, iname);
342  cpl_table_set(table, "LMIN", ic, lmin);
343  cpl_table_set(table, "LMAX", ic, lmax);
344  cpl_table_set(table, "P1", ic, l1);
345  cpl_table_set(table, "P2", ic, l2);
346  } /* for ic (all input cubes) */
347  cpl_free(ctype1ref);
348  cpl_free(ctype2ref);
349  cpl_free(cunit1ref);
350  cpl_free(cunit2ref);
351  cpl_free(ctype3ref);
352 
353  /* erase bad entries (cubes with missing properties) */
354  cpl_table_select_all(table);
355  cpl_table_and_selected_invalid(table, "FILENAME");
356  cpl_table_erase_selected(table);
357  ncubes = cpl_table_get_nrow(table);
358  if (ncubes < 2) {
359  cpl_msg_error(idstring, "Only %d cube%s with valid info %s found!", ncubes,
360  ncubes ? "" : "s", ncubes ? "was" : "were");
361  cpl_end();
362  return 12;
363  }
364 
365  /* now sort the table by increasing first plane */
366  cpl_propertylist *order = cpl_propertylist_new();
367  cpl_propertylist_append_bool(order, "P1", CPL_FALSE);
368  cpl_table_sort(table, order);
369  cpl_propertylist_delete(order);
370 #if 1
371  printf("Stored and sorted %d cubes:\n", ncubes);
372  cpl_table_dump(table, 0, ncubes, stdout);
373  fflush(stdout);
374 #endif
375 
376  /* clean up overlaps and gaps: *
377  * - if there are overlaps just move the 2nd cube to start at a *
378  * higher plane *
379  * - if there is a gap just move the 1st cube to be more extended, *
380  * even though this will incur resampling edge effects (warn!) */
381  int nover = 0, ngaps = 0;
382  for (ic = 1; ic < cpl_table_get_nrow(table); ic++) { /* start at 2nd row */
383  int p2a = cpl_table_get_int(table, "P2", ic - 1, NULL),
384  p1b = cpl_table_get_int(table, "P1", ic, NULL);
385  double lmaxa = cpl_table_get_double(table, "LMAX", ic - 1, NULL);
386  int pdiff = p1b - p2a;
387  if (pdiff == 1) { /* no gap, no overlap */
388  continue;
389  }
390  if (pdiff < 1) { /* overlap */
391  p2a += pdiff - 1;
392  lmaxa += cd33ref * (pdiff - 1);
393  nover++;
394  } /* if overlap */
395  if (pdiff > 1) { /* gap */
396  p2a += pdiff - 1;
397  lmaxa += cd33ref * (pdiff - 1);
398  ngaps++;
399  } /* if gap */
400  cpl_table_set_int(table, "P2", ic - 1, p2a);
401  cpl_table_set_double(table, "LMAX", ic - 1, lmaxa);
402  } /* for ic (all other valid cubes) */
403 #if 1
404  printf("Cleaned up %d overlaps and %d gaps.\n", nover, ngaps);
405  cpl_table_dump(table, 0, ncubes, stdout);
406  fflush(stdout);
407 #endif
408 
409  /* now create the initial cube from the first exposure in the table */
410  muse_datacube *cube = cpl_calloc(1, sizeof(muse_datacube));
411  const char *fn = cpl_table_get_string(table, "FILENAME", 0);
412  cpl_msg_info(idstring, "Loading cube from \"%s\"", fn);
413  cube->header = cpl_propertylist_load(fn, 0);
414  /* find DATA extension */
415  int iext = cpl_fits_find_extension(fn, "DATA");
416  /* load the WCS from the first cube extension *
417  * and merge it into the cube header */
418  cpl_propertylist *wcs = cpl_propertylist_load(fn, iext);
419  cpl_propertylist_erase_regexp(wcs, MUSE_WCS_KEYS, 1); /* remove others */
420  cpl_propertylist_append(cube->header, wcs);
421  cpl_propertylist_delete(wcs);
422  /* load the cubes from the DATA and STAT extensions */
423  cube->data = cpl_imagelist_load(fn, CPL_TYPE_FLOAT, iext);
424  iext = cpl_fits_find_extension(fn, "STAT");
425  cube->stat = cpl_imagelist_load(fn, CPL_TYPE_FLOAT, iext);
426 
427  /* now create flags vector (starting from array) to erase unused planes */
428  int nplanes = cpl_imagelist_get_size(cube->data),
429  l1 = cpl_table_get_int(table, "P1", 0, NULL),
430  l2 = cpl_table_get_int(table, "P2", 0, NULL);
431  cpl_array *aflags = cpl_array_new(nplanes, CPL_TYPE_DOUBLE);
432  cpl_array_fill_window_double(aflags, 0, nplanes, -1.); /* all bad */
433  cpl_array_fill_window_double(aflags, l1 - 1, l2 - l1 + 1, 1.); /* the good ones */
434 #if 0
435  cpl_array_dump(aflags, 0, 5, stdout);
436  cpl_array_dump(aflags, l1 - 4, 10, stdout);
437  cpl_array_dump(aflags, l2 - 4, 10, stdout);
438  cpl_array_dump(aflags, nplanes - 5, 6, stdout);
439  fflush(stdout);
440 #endif
441  cpl_vector *vflags = cpl_vector_wrap(nplanes, cpl_array_unwrap(aflags));
442  cpl_imagelist_erase(cube->data, vflags);
443  cpl_imagelist_erase(cube->stat, vflags);
444  cpl_vector_delete(vflags);
445  cpl_msg_debug(idstring, "%d of %d planes left in first cube",
446  (int)cpl_imagelist_get_size(cube->data), nplanes);
447  nplanes = cpl_imagelist_get_size(cube->data);
448  /* update the spectral part of the WCS with the new setup, i.e. *
449  * adjust CRPIX3 by the number of deleted planes at the start */
450  cpl_propertylist_update_double(cube->header, "CRPIX3", crpix3ref - (l1 - 1));
451 
452  /* one-by-one fully load the other cubes, and copy *
453  * the valid data from them into the output cube */
454  for (ic = 1; ic < cpl_table_get_nrow(table); ic++) {
455  fn = cpl_table_get_string(table, "FILENAME", ic);
456  cpl_msg_info(idstring, "Loading cube from \"%s\"", fn);
457  iext = cpl_fits_find_extension(fn, "DATA");
458  cpl_imagelist *cubedata = cpl_imagelist_load(fn, CPL_TYPE_FLOAT, iext);
459  iext = cpl_fits_find_extension(fn, "STAT");
460  cpl_imagelist *cubestat = cpl_imagelist_load(fn, CPL_TYPE_FLOAT, iext);
461 
462  /* simply append (copy) the valid image planes to the output cube imagelists */
463  l1 = cpl_table_get_int(table, "P1", ic, NULL);
464  l2 = cpl_table_get_int(table, "P2", ic, NULL);
465  int l;
466  for (l = l1 - 1; l < l2; l++, nplanes = cpl_imagelist_get_size(cube->data)) {
467  cpl_error_code rc1, rc2;
468  rc1 = cpl_imagelist_set(cube->data,
469  cpl_image_duplicate(cpl_imagelist_get(cubedata, l)),
470  nplanes);
471  rc2 = cpl_imagelist_set(cube->stat,
472  cpl_image_duplicate(cpl_imagelist_get(cubestat, l)),
473  nplanes);
474  if (rc1 != rc2 || rc1 != CPL_ERROR_NONE || rc2 != CPL_ERROR_NONE) {
475  cpl_msg_warning(idstring, "Could not append plane %d of cube %d to output cube",
476  l, ic + 1);
477  } /* if bad result */
478  } /* for l (wavelength planes to copy) */
479  cpl_imagelist_delete(cubedata);
480  cpl_imagelist_delete(cubestat);
481  cpl_msg_debug(idstring, "output cube now has %d/%d planes after copying %d "
482  "planes from cube %d", nplanes,
483  (int)cpl_imagelist_get_size(cube->data), l2 - l1 + 1, ic + 1);
484  } /* for ic (all other valid cubes) */
485  cpl_table_delete(table);
486 
487  /* reset the recipe parameters in the output header with the now full range */
488  cpl_msg_info(idstring, "Resetting recipe parameters ESO PRO REC%d to determine "
489  "wavelength ranges", irecout);
490  char *hname = cpl_sprintf("ESO PRO REC%d PARAM%d VALUE", irecout, iparout1),
491  *hvalue = cpl_sprintf("%g", lminout),
492  *hcomment = cpl_sprintf("set with %s!", idstring);
493  cpl_propertylist_update_string(cube->header, hname, hvalue);
494  cpl_propertylist_set_comment(cube->header, hname, hcomment);
495  cpl_free(hname);
496  cpl_free(hvalue);
497  hname = cpl_sprintf("ESO PRO REC%d PARAM%d VALUE", irecout, iparout2),
498  hvalue = cpl_sprintf("%g", lmaxout),
499  cpl_propertylist_update_string(cube->header, hname, hvalue);
500  cpl_propertylist_set_comment(cube->header, hname, hcomment);
501  cpl_free(hname);
502  cpl_free(hvalue);
503  cpl_free(hcomment);
504 
505  /* save output, then check for errors, and end */
506  int rc = muse_datacube_save(cube, oname);
507  muse_datacube_delete(cube);
508  if (rc == CPL_ERROR_NONE) {
509  cpl_msg_info(idstring, "Saved cube to \"%s\".", oname);
510  } else {
511  cpl_msg_error(idstring, "Error while saving cube to \"%s\": %s (%d)", oname,
512  cpl_error_get_message(), rc);
513  rc = 20;
514  }
515 
516  if (!cpl_errorstate_is_equal(state)) {
517  cpl_errorstate_dump(state, CPL_FALSE, muse_cplerrorstate_dump_some);
518  rc = 50;
519  }
520  cpl_memory_dump();
521  cpl_end();
522  return rc;
523 }
524 
Structure definition of a MUSE datacube.
Definition: muse_datacube.h:48
double muse_pfits_get_cd(const cpl_propertylist *aHeaders, unsigned int aAxisI, unsigned int aAxisJ)
find out the WCS coordinate at the reference point
Definition: muse_pfits.c:446
double muse_pfits_get_crval(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS coordinate at the reference point
Definition: muse_pfits.c:423
void muse_datacube_delete(muse_datacube *aCube)
Deallocate memory associated to a muse_datacube object.
const char * muse_pfits_get_cunit(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS axis unit string
Definition: muse_pfits.c:491
cpl_error_code muse_datacube_save(muse_datacube *aCube, const char *aFilename)
Save the three cube extensions and the FITS headers of a MUSE datacube to a file. ...
double muse_pfits_get_crpix(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS reference point
Definition: muse_pfits.c:401
#define MUSE_WCS_KEYS
regular expression for WCS properties
Definition: muse_wcs.h:48
void muse_cplerrorstate_dump_some(unsigned aCurrent, unsigned aFirst, unsigned aLast)
Dump some CPL errors.
cpl_imagelist * data
the cube containing the actual data values
Definition: muse_datacube.h:76
const char * muse_pfits_get_ctype(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS axis type string
Definition: muse_pfits.c:469
cpl_propertylist * header
the FITS header
Definition: muse_datacube.h:57
void muse_processing_recipeinfo(cpl_plugin *)
Output main pipeline configuration, inputs, and parameters.
cpl_imagelist * stat
the cube containing the data variance
Definition: muse_datacube.h:86