MUSE Pipeline Reference Manual  0.18.5
muse_pixtable.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2005-2014 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 /*----------------------------------------------------------------------------*
27  * Includes *
28  *----------------------------------------------------------------------------*/
29 #include <cpl.h>
30 #include <math.h>
31 #include <string.h>
32 
33 #include "muse_pixtable.h"
34 #include "muse_instrument.h"
35 
36 #include "muse_cplwrappers.h"
37 #include "muse_geo.h"
38 #include "muse_mask.h"
39 #include "muse_pfits.h"
40 #include "muse_quality.h"
41 #include "muse_tracing.h"
42 #include "muse_wavecalib.h"
43 #include "muse_wcs.h"
44 #include "muse_utils.h"
45 
46 /*----------------------------------------------------------------------------*
47  * Debugging Macros *
48  * Set these to 1 or higher for (lots of) debugging output *
49  *----------------------------------------------------------------------------*/
50 #define CREATE_MINIMAL_PIXTABLE 0 /* create a very small pixel table */
51 #define PIXTABLE_CREATE_CCDSIZED 1 /* start with pixel table sized like input image */
52 #define DEBUG_PIXTABLE_CREATION 0 /* some table dumps in muse_pixtable_create() */
53 #define DEBUG_PIXTABLE_FEW_SLICES 0 /* when doing quick pixel table creation for *
54  * debugging, set this to a value below 48 */
55 
56 /*----------------------------------------------------------------------------*/
94 /*----------------------------------------------------------------------------*/
95 
98 /* Let's use macros for the shift lengths we need */
99 #define MUSE_ORIGIN_SHIFT_XSLICE 24
100 #define MUSE_ORIGIN_SHIFT_YPIX 11
101 #define MUSE_ORIGIN_SHIFT_IFU 6
102 /* spacing between the left slice edge at the bottom of the slice *
103  * to the origin of the slice from where we could x positions within *
104  * the slice; this is to make sure that no pixel is left of the offset */
105 #define MUSE_ORIGIN_SLICE_SAFETY_OFFSET -20
106 
107 /* private functions without error checking */
109 static inline uint32_t
110 muse_pixtable_origin_encode_fast(unsigned int aX, unsigned int aY,
111  unsigned short aIFU,
112  unsigned short aSlice, unsigned int aOffset)
113 {
114  return ((aX - aOffset) << MUSE_ORIGIN_SHIFT_XSLICE)
115  | (aY << MUSE_ORIGIN_SHIFT_YPIX)
116  | (aIFU << MUSE_ORIGIN_SHIFT_IFU)
117  | aSlice;
118 }
119 
121 static inline unsigned int
122 muse_pixtable_origin_get_x_fast(uint32_t aOrigin, uint32_t aOffset)
123 {
124  return ((aOrigin >> MUSE_ORIGIN_SHIFT_XSLICE) & 0x7f) + aOffset;
125 }
126 
128 static inline unsigned int
129 muse_pixtable_origin_get_y_fast(uint32_t aOrigin)
130 {
131  return (aOrigin >> MUSE_ORIGIN_SHIFT_YPIX) & 0x1fff;
132 }
133 
135 static inline unsigned short
136 muse_pixtable_origin_get_ifu_fast(uint32_t aOrigin)
137 {
138  return (aOrigin >> MUSE_ORIGIN_SHIFT_IFU) & 0x1f;
139 }
140 
142 static inline unsigned short
143 muse_pixtable_origin_get_slice_fast(uint32_t aOrigin)
144 {
145  return aOrigin & 0x3f;
146 }
147 
148 /*---------------------------------------------------------------------------*/
164 /*---------------------------------------------------------------------------*/
165 uint32_t
166 muse_pixtable_origin_encode(unsigned int aX, unsigned int aY,
167  unsigned short aIFU,
168  unsigned short aSlice, unsigned int aOffset)
169  /* use the explicit 32bit return type so that *
170  * this also works on systems with other setups */
171 {
172  /* check for allowed values to fit into 32bit */
173  cpl_ensure(aX < 8192 && aX > 0 && aY < 8192 && aY > 0 &&
174  aIFU <= kMuseNumIFUs && aIFU >= 1 &&
175  aSlice <= kMuseSlicesPerCCD && aSlice >= 1 && aOffset < 8192,
176  CPL_ERROR_ILLEGAL_INPUT, 0);
177 
178 #if 0
179  cpl_msg_debug(__func__, "origin (%d, %d, %d, %d, %d) = 0x%x",
180  aX, aY, aIFU, aSlice, aOffset,
181  ((aX - aOffset) << MUSE_ORIGIN_SHIFT_XSLICE)
182  | (aY << MUSE_ORIGIN_SHIFT_YPIX)
183  | (aIFU << MUSE_ORIGIN_SHIFT_IFU)
184  | aSlice);
185 #endif
186 
187  /* do the bit-shifting "magic" */
188  return muse_pixtable_origin_encode_fast(aX, aY, aIFU, aSlice, aOffset);
189 } /* muse_pixtable_origin_encode() */
190 
191 /*---------------------------------------------------------------------------*/
209 /*---------------------------------------------------------------------------*/
210 unsigned int
211 muse_pixtable_origin_get_x(uint32_t aOrigin, muse_pixtable *aPixtable,
212  cpl_size aRow)
213 {
214  unsigned short slice = muse_pixtable_origin_get_slice_fast(aOrigin),
215  ifu = muse_pixtable_origin_get_ifu_fast(aOrigin);
216  cpl_errorstate prestate = cpl_errorstate_get();
217  unsigned int expnum = muse_pixtable_get_expnum(aPixtable, aRow);
218  if (!cpl_errorstate_is_equal(prestate)) {
219  cpl_errorstate_set(prestate);
220  }
221  unsigned int offset = muse_pixtable_origin_get_offset(aPixtable, expnum, ifu,
222  slice),
223  x = muse_pixtable_origin_get_x_fast(aOrigin, offset);
224 #if 0
225  if (x > 8191 || x < 1 || !cpl_errorstate_is_equal(prestate)) {
226  cpl_msg_error(__func__, "aOrigin=%#x x=%d (%d %d %d), %s",
227  aOrigin, x, slice, ifu, offset, cpl_error_get_message());
228  }
229 #endif
230  cpl_ensure(x <= 8191 && x >= 1 && cpl_errorstate_is_equal(prestate),
231  CPL_ERROR_ILLEGAL_OUTPUT, 0);
232  return x;
233 } /* muse_pixtable_origin_get_x() */
234 
235 /*---------------------------------------------------------------------------*/
243 /*---------------------------------------------------------------------------*/
244 unsigned int
245 muse_pixtable_origin_get_y(uint32_t aOrigin)
246 {
247  unsigned int y = muse_pixtable_origin_get_y_fast(aOrigin);
248 #if 0
249  if (y > 8191 || y < 1) {
250  cpl_msg_error(__func__, "aOrigin=%#x y=%d", aOrigin, y);
251  }
252 #endif
253  cpl_ensure(y <= 8191 && y >= 1, CPL_ERROR_ILLEGAL_OUTPUT, 0);
254  return y;
255 } /* muse_pixtable_origin_get_y() */
256 
257 /*---------------------------------------------------------------------------*/
265 /*---------------------------------------------------------------------------*/
266 unsigned short
268 {
269  unsigned short ifu = muse_pixtable_origin_get_ifu_fast(aOrigin);
270 #if 0
271  if (ifu > kMuseNumIFUs || ifu < 1) {
272  cpl_msg_error(__func__, "aOrigin=%#x ifu=%d", aOrigin, ifu);
273  }
274 #endif
275  cpl_ensure(ifu <= kMuseNumIFUs && ifu >= 1, CPL_ERROR_ILLEGAL_OUTPUT, 0);
276  return ifu;
277 } /* muse_pixtable_origin_get_ifu() */
278 
279 /*---------------------------------------------------------------------------*/
287 /*---------------------------------------------------------------------------*/
288 unsigned short
290 {
291  unsigned short slice = muse_pixtable_origin_get_slice_fast(aOrigin);
292 #if 0
293  if (slice > kMuseSlicesPerCCD || slice < 1) {
294  cpl_msg_error(__func__, "aOrigin=%#x slice=%d", aOrigin, slice);
295  }
296 #endif
297  cpl_ensure(slice <= kMuseSlicesPerCCD && slice >= 1,
298  CPL_ERROR_ILLEGAL_OUTPUT, 0);
299  return slice;
300 } /* muse_pixtable_origin_get_slice() */
301 
302 /*---------------------------------------------------------------------------*/
317 /*---------------------------------------------------------------------------*/
318 unsigned int
320  cpl_polynomial *aLTrace,
321  unsigned short aIFU, unsigned short aSlice)
322 {
323  cpl_ensure(aPixtable && aPixtable->header, CPL_ERROR_NULL_INPUT, 0);
324  cpl_errorstate prestate = cpl_errorstate_get();
325  unsigned int offset = floor(cpl_polynomial_eval_1d(aLTrace, 1, NULL))
326  + MUSE_ORIGIN_SLICE_SAFETY_OFFSET;
327  cpl_ensure(cpl_errorstate_is_equal(prestate), cpl_error_get_code(), 0);
328  /* use exposure number 0 here, this is set only later, *
329  * when merging pixel tables of different exposures */
330  char *keyword = cpl_sprintf(MUSE_HDR_PT_IFU_SLICE_OFFSET, 0, aIFU, aSlice);
331  cpl_propertylist_update_int(aPixtable->header, keyword, offset);
332  cpl_propertylist_set_comment(aPixtable->header, keyword,
333  MUSE_HDR_PT_IFU_SLICE_OFFSET_COMMENT);
334  cpl_free(keyword);
335  return offset;
336 } /* muse_pixtable_origin_set_offset() */
337 
338 /*---------------------------------------------------------------------------*/
352 /*---------------------------------------------------------------------------*/
353 unsigned int
354 muse_pixtable_origin_get_offset(muse_pixtable *aPixtable, unsigned int aExpNum,
355  unsigned short aIFU, unsigned short aSlice)
356 {
357  cpl_ensure(aPixtable && aPixtable->header, CPL_ERROR_NULL_INPUT, 0);
358  char *keyword = cpl_sprintf(MUSE_HDR_PT_IFU_SLICE_OFFSET, aExpNum, aIFU,
359  aSlice);
360  cpl_errorstate prestate = cpl_errorstate_get();
361  unsigned int offset = cpl_propertylist_get_int(aPixtable->header, keyword);
362  cpl_free(keyword);
363 #if 0
364  if (offset > 8191 || offset < 1) {
365  cpl_msg_error(__func__, "aIFU=%d aSlice=%d offset=%d",
366  aIFU, aSlice, offset);
367  }
368 #endif
369  cpl_ensure(offset <= 8191 && offset >= 1 && cpl_errorstate_is_equal(prestate),
370  CPL_ERROR_ILLEGAL_OUTPUT, 0);
371  return offset;
372 } /* muse_pixtable_origin_get_offset() */
373 
374 /*---------------------------------------------------------------------------*/
394 /*---------------------------------------------------------------------------*/
395 cpl_error_code
397  unsigned int aNum)
398 {
399  cpl_ensure_code(aOut && aOut->header, CPL_ERROR_NULL_INPUT);
400  cpl_propertylist *dest = aOut->header,
401  *from = aOut->header;
402  if (aFrom && aFrom->header) {
403  from = aFrom->header;
404  }
405  char keyword[KEYWORD_LENGTH];
406  unsigned short nifu, nslice;
407  for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
408  for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
409  /* keyword to copy */
410  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_IFU_SLICE_OFFSET,
411  0, nifu, nslice);
412  cpl_errorstate prestate = cpl_errorstate_get();
413  unsigned int offset = cpl_propertylist_get_int(from, keyword);
414  if (!cpl_errorstate_is_equal(prestate)) {
415  /* this one was not set, skip to the next one */
416  cpl_errorstate_set(prestate);
417  continue;
418  }
419  /* erase old keyword */
420  cpl_propertylist_erase(from, keyword);
421  /* add new keyword */
422  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_IFU_SLICE_OFFSET,
423  aNum, nifu, nslice);
424  cpl_propertylist_update_int(dest, keyword, offset);
425  cpl_propertylist_set_comment(dest, keyword,
426  MUSE_HDR_PT_IFU_SLICE_OFFSET_COMMENT);
427  } /* for nslice */
428  } /* for nifu */
429 
430  return CPL_ERROR_NONE;
431 } /* muse_pixtable_origin_copy_offsets() */
432 
433 /*---------------------------------------------------------------------------*/
448 /*---------------------------------------------------------------------------*/
449 unsigned int
450 muse_pixtable_get_expnum(muse_pixtable *aPixtable, cpl_size aRow)
451 {
452  cpl_ensure(aPixtable && aPixtable->header, CPL_ERROR_NULL_INPUT, 0);
453  cpl_ensure(aRow >= 0 && muse_pixtable_get_nrow(aPixtable) > aRow,
454  CPL_ERROR_ILLEGAL_INPUT, 0);
455 
456  char keyword[KEYWORD_LENGTH];
457  unsigned int exposure = 0;
458  cpl_size lo = 0, hi = 0;
459  do {
460  cpl_errorstate prestate = cpl_errorstate_get();
461  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST, ++exposure);
462  lo = cpl_propertylist_get_long_long(aPixtable->header, keyword);
463  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST, exposure);
464  hi = cpl_propertylist_get_long_long(aPixtable->header, keyword);
465  if (!cpl_errorstate_is_equal(prestate)) {
466  if (exposure == 1) {
467  /* no exposure headers in the table at all, so it's either the *
468  * only one or the table was sorted, destroying exposure info */
469  lo = hi = aRow;
470  exposure = 0; /* signify the problem to the caller */
471  }
472  cpl_errorstate_set(prestate);
473  break;
474  }
475  } while (hi < aRow);
476  cpl_ensure(lo <= aRow && hi >= aRow, CPL_ERROR_ILLEGAL_OUTPUT, 0);
477  return exposure;
478 } /* muse_pixtable_get_expnum */
479 
480 /*----------------------------------------------------------------------------*/
500 /*----------------------------------------------------------------------------*/
502  { MUSE_PIXTABLE_XPOS, CPL_TYPE_FLOAT, "pix", "%7.2f",
503  "relative x-pixel position in the output datacube", CPL_TRUE},
504  { MUSE_PIXTABLE_YPOS, CPL_TYPE_FLOAT, "pix", "%7.2f",
505  "relative y-pixel position in the output datacube", CPL_TRUE},
506  { MUSE_PIXTABLE_LAMBDA, CPL_TYPE_FLOAT, "Angstrom", "%8.2f",
507  "wavelength of this pixel", CPL_TRUE},
508  { MUSE_PIXTABLE_DATA, CPL_TYPE_FLOAT, "count", "%e",
509  "data value in this pixel", CPL_TRUE},
510  { MUSE_PIXTABLE_DQ, CPL_TYPE_INT, NULL, "%#x",
511  "Euro3D bad pixel status of this pixel", CPL_TRUE},
512  { MUSE_PIXTABLE_STAT, CPL_TYPE_FLOAT, "count**2", "%e",
513  "data variance of this pixel", CPL_TRUE},
514  { MUSE_PIXTABLE_ORIGIN, CPL_TYPE_INT, NULL, "0x%08x",
515  "encoded value of IFU and slice number, as well as x and y "
516  "position in the raw (trimmed) data", CPL_TRUE},
517  { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
518 };
519 
520 /*---------------------------------------------------------------------------*/
566 /*---------------------------------------------------------------------------*/
568 muse_pixtable_create(muse_image *aImage, cpl_table *aTrace, cpl_table *aWave,
569  cpl_table *aGeoTable)
570 {
571  cpl_ensure(aImage && aWave && aTrace, CPL_ERROR_NULL_INPUT, NULL);
572  /* get and check IFU number */
573  unsigned char ifu = muse_utils_get_ifu(aImage->header);
574  cpl_ensure(ifu >= 1 && ifu <= kMuseNumIFUs, CPL_ERROR_DATA_NOT_FOUND, NULL);
575 
576  /* check that the input data is in count units */
577  if (!cpl_propertylist_has(aImage->header, "BUNIT")) {
578  const char *msg = "Input data does not contain a data unit (\"BUNIT\"), "
579  "cannot proceed!";
580  cpl_msg_error(__func__, "%s", msg);
581  cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "%s", msg);
582  return NULL;;
583  }
584  const char *unit = cpl_propertylist_get_string(aImage->header, "BUNIT");
585  if (strncmp(unit, "count", 6)) {
586  char *msg = cpl_sprintf("Input data is not in \"count\" units but in \"%s\""
587  ", not suitable for a pixel table!", unit);
588  cpl_msg_error(__func__, "%s", msg);
589  cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "%s", msg);
590  cpl_free(msg);
591  return NULL;
592  }
593 
594  /* create the output pixel table structure, and NULL out the components */
595  muse_pixtable *pt = cpl_calloc(1, sizeof(muse_pixtable));
596  /* compute initial table size */
597  int xsize = cpl_image_get_size_x(aImage->data),
598  ysize = cpl_image_get_size_y(aImage->data),
599 #if PIXTABLE_CREATE_CCDSIZED
600  /* There cannot be more pixels needed in the table than in the *
601  * input image, so with this we can remove the overflow check below. */
602  isize = xsize * ysize;
603 #else
604  /* It's very likely that no more pixels than these are needed. But then *
605  * we need to check for overflows below (and still cut it in the end). */
606  isize = ysize * kMuseSlicesPerCCD * kMuseSliceHiLikelyWidth;
607 #endif
608  pt->table = muse_cpltable_new(muse_pixtable_def, isize);
609 
610  /* copy the header from the input image to the pixel table, *
611  * but remove some keywords that could cause trouble */
612  pt->header = cpl_propertylist_duplicate(aImage->header);
613  cpl_propertylist_erase_regexp(pt->header,
614  "^SIMPLE$|^BITPIX$|^NAXIS|^EXTEND$|^XTENSION$|"
615  "^DATASUM$|^DATAMIN$|^DATAMAX$|^DATAMD5$|"
616  "^PCOUNT$|^GCOUNT$|^HDUVERS$|^BLANK$|"
617  "^BZERO$|^BSCALE$|^BUNIT$|^CHECKSUM$|^INHERIT$|"
618  MUSE_HDR_OVSC_REGEXP"|"MUSE_WCS_KEYS, 0);
619  cpl_propertylist_append_string(pt->header, MUSE_HDR_PT_TYPE,
620  aGeoTable ? MUSE_PIXTABLE_STRING_FULL
621  : MUSE_PIXTABLE_STRING_SIMPLE);
622  cpl_propertylist_set_comment(pt->header, MUSE_HDR_PT_TYPE,
623  aGeoTable ? MUSE_PIXTABLE_COMMENT_FULL
624  : MUSE_PIXTABLE_COMMENT_SIMPLE);
625 
626  /* make all table cells valid by filling them with zeros; this is a workaround *
627  * suggested by Carlo Izzo to be able to set the table cells using array *
628  * access instead of the slower and non-threadsafe cpl_table_set() function */
629  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_XPOS, 0, isize, 0);
630  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_YPOS, 0, isize, 0);
631  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_LAMBDA, 0, isize, 0);
632  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_DATA, 0, isize, 0);
633  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_STAT, 0, isize, 0);
634  cpl_table_fill_column_window_int(pt->table, MUSE_PIXTABLE_DQ, 0, isize, 0);
635  cpl_table_fill_column_window_int(pt->table, MUSE_PIXTABLE_ORIGIN, 0, isize, 0);
636 
637  /* get columns as pointers */
638  float *cdata_xpos = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_XPOS),
639  *cdata_ypos = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_YPOS),
640  *cdata_lambda = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_LAMBDA),
641  *cdata_data = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_DATA),
642  *cdata_stat = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_STAT);
643  int *cdata_dq = cpl_table_get_data_int(pt->table, MUSE_PIXTABLE_DQ);
644  uint32_t *cdata_origin = (uint32_t *)cpl_table_get_data_int(pt->table,
645  MUSE_PIXTABLE_ORIGIN);
646 
647  /* get input image buffers as pointers, too */
648  const float *pixdata = cpl_image_get_data_float_const(aImage->data),
649  *pixstat = cpl_image_get_data_float_const(aImage->stat);
650  const int *pixdq = cpl_image_get_data_int_const(aImage->dq);
651 
652  /* get columns of the wavecal table */
653  unsigned short wavexorder, waveyorder;
654  muse_wave_table_get_orders(aWave, &wavexorder, &waveyorder);
655  cpl_msg_info(__func__, "Creating pixel table for IFU %hhu, using order %d for "
656  "trace solution and orders %hu/%hu for wavelength solution", ifu,
657  muse_trace_table_get_order(aTrace), wavexorder, waveyorder);
658 
659  /* get position corresponding to the IFU number */
660  cpl_table *geopos = NULL;
661  double *slice_x = NULL, *slice_y = NULL, *slice_angle = NULL,
662  *slice_width = NULL;
663  if (aGeoTable) {
664  geopos = muse_geo_table_extract_ifu(aGeoTable, ifu);
665  slice_x = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_X);
666  slice_y = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_Y);
667  /* fail on missing x/y as they are critical */
668  if (!geopos || !slice_x || !slice_y) {
669  char *msg = !geopos
670  ? cpl_sprintf("Geometry table is missing data for IFU %hhu!", ifu)
671  : cpl_sprintf("Geometry table is missing column%s%s%s!",
672  !slice_x && !slice_y ? "s" : "",
673  slice_x ? "" : " \""MUSE_GEOTABLE_X"\"",
674  slice_y ? "" : " \""MUSE_GEOTABLE_Y"\"");
675  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "%s", msg);
676  cpl_msg_error(__func__, "%s", msg);
677  cpl_free(msg);
678  cpl_table_delete(geopos);
680  return NULL;
681  }
682  /* only output warnings for other missing columns, they *
683  * default to usable values below, ignore CPL errors */
684  cpl_errorstate prestate = cpl_errorstate_get();
685  slice_angle = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_ANGLE);
686  slice_width = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_WIDTH);
687  if (!cpl_errorstate_is_equal(prestate)) {
688  char *msg = cpl_sprintf("Geometry table is missing column%s%s%s!",
689  !slice_angle && !slice_width ? "s" : "",
690  slice_angle ? "" : " \""MUSE_GEOTABLE_ANGLE"\"",
691  slice_width ? "" : " \""MUSE_GEOTABLE_WIDTH"\"");
692  cpl_error_set_message(__func__, CPL_ERROR_BAD_FILE_FORMAT, "%s", msg);
693  cpl_msg_error(__func__, "%s", msg);
694  cpl_free(msg);
695  if (!getenv("MUSE_EXPERT_USER")) {
696  cpl_table_delete(geopos);
698  return NULL;
699  }
700  } /* if */
701  } /* if aGeoTable */
702 
703  /* this counts the current table row that is being entered */
704  cpl_size itablerow = 0;
705  /* time the operation for this IFU */
706  double cputime = cpl_test_get_cputime(),
707  walltime = cpl_test_get_walltime();
708  /* loop through all slices */
709  int islice;
710 #if DEBUG_PIXTABLE_FEW_SLICES > 0
711 # define SLICE_LIMIT DEBUG_PIXTABLE_FEW_SLICES
712 #else
713 # define SLICE_LIMIT kMuseSlicesPerCCD
714 #endif
715  for (islice = 0; islice < SLICE_LIMIT; islice++) {
716 #if DEBUG_PIXTABLE_CREATION /* check how many pixels extend over the nominal width */
717  cpl_msg_debug(__func__, "Starting to process slice %2d of IFU %2d",
718  islice + 1, ifu);
719 #endif
720 
721  /* fill the wavelength calibration polynomial for this slice */
722  cpl_polynomial *pwave = muse_wave_table_get_poly_for_slice(aWave, islice + 1);
723  /* vector for the position within the slice (for evaluation *
724  * of the wavelength solution in two dimensions */
725  cpl_vector *pos = cpl_vector_new(2);
726 
727  /* test evaluate the wavelength polynomial to see if it's valid */
728  cpl_vector_set(pos, 0, kMuseOutputYTop / 2);
729  cpl_vector_set(pos, 1, kMuseOutputYTop / 2);
730  double ltest = cpl_polynomial_eval(pwave, pos);
731  if (!pwave || !isnormal(ltest) || fabs(ltest) < DBL_EPSILON) {
732  char *msg = cpl_sprintf("Wavelength calibration polynomial for slice %d "
733  "is not well defined!", islice + 1);
734  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "%s", msg);
735  cpl_msg_error(__func__, "%s", msg);
736  cpl_free(msg);
737  cpl_polynomial_delete(pwave);
738  cpl_vector_delete(pos);
739  if (getenv("MUSE_EXPERT_USER")) {
740  continue; /* go the next slice */
741  } else { /* give up completely */
742  cpl_table_delete(geopos);
744  return NULL;
745  }
746  } /* if bad wavecal polynomial */
747 
748  /* get the tracing polynomials for this slice */
749  cpl_polynomial **ptrace = muse_trace_table_get_polys_for_slice(aTrace,
750  islice + 1);
751  if (!ptrace) {
752  char *msg = cpl_sprintf("Tracing polynomials for slice %d are not well "
753  "defined!", islice + 1);
754  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "%s", msg);
755  cpl_msg_error(__func__, "%s", msg);
756  cpl_free(msg);
757  cpl_polynomial_delete(pwave);
758  cpl_vector_delete(pos);
759  if (getenv("MUSE_EXPERT_USER")) {
760  continue; /* go the next slice */
761  } else { /* give up completely */
762  cpl_table_delete(geopos);
764  return NULL;
765  }
766  } /* if no tracing polynomials */
767  unsigned offset = muse_pixtable_origin_set_offset(pt,
768  ptrace[MUSE_TRACE_LEFT],
769  ifu, islice + 1);
770 
771  /* within each slice, loop from bottom to top */
772  int j;
773  for (j = 1; j <= ysize; j++) {
774  /* compute slice center for this vertical position and set the *
775  * edge pixels of slice, so that we can loop over those pixels */
776  double x1 = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT], j, NULL),
777  x2 = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT], j, NULL),
778  xcenter = (x1 + x2) / 2.,
779  width = x2 - x1;
780  if (!isnormal(x1) || !isnormal(x2) || x1 < 1 || x2 > xsize || x1 > x2) {
781  cpl_msg_warning(__func__, "slice %2d: faulty polynomial detected at "
782  "y=%d (borders: %f ... %f)", islice + 1, j, x1, x2);
783  break; /* skip the rest of this slice */
784  }
785  /* include pixels that have more than half of them inside the slice */
786  int ileft = ceil(x1),
787  iright = floor(x2);
788  cpl_vector_set(pos, 1, j); /* vertical pos. for wavelength evaluation */
789 
790  /* now loop over all pixels of this slice horizontally */
791  int i;
792  for (i = ileft; i <= iright; i++) {
793  cpl_vector_set(pos, 0, i); /* horiz. pos. for wavelength evaluation */
794 
795  /* do the wavelength evaluation early on, to more quickly *
796  * recover from errors */
797  double lambda = cpl_polynomial_eval(pwave, pos);
798  if (lambda < 3000. || lambda > 11000. || !isfinite(lambda)) {
799  continue; /* skip this pixel */
800  }
801 
802  /* Relative horizontal pixel position within slice; has to be scaled *
803  * by the width of the slice on the CCD. Slice width is measured *
804  * horizontally, not along iso-lambda lines, the difference for *
805  * straight lines is below 0.015 pix (0.02%), and only slightly *
806  * larger for curved lines, so this simplification should not have a *
807  * negative effect on anything. We compute (xcenter - i) to account *
808  * for the reversed sky orientation within each slice. */
809  float dx = (xcenter - i) / width
810  * (slice_width ? slice_width[islice] : kMuseSliceNominalWidth);
811 #if DEBUG_PIXTABLE_CREATION /* check how many pixels extend over the nominal width */
812  if (fabs(dx) > 37.5) {
813  cpl_msg_debug(__func__, "%d - %f -> %f", i, xcenter, dx);
814  }
815 #endif
816 
817 #if CREATE_MINIMAL_PIXTABLE
818  /* if we need to create a very small pixel table for debugging */
819  if (lambda < 6495. || lambda > 6505. || fabs(dx) > 10.) {
820  continue;
821  }
822 #endif
823 
824  /* this pixel is set up to be inside the slice, write it to the table */
825  cpl_size irow = itablerow++; /* started at 0, so increment after assignment */
826 #if !PIXTABLE_CREATE_CCDSIZED /* overflow check, and possibly enlarge the table */
827  if (irow + 1 > (cpl_size)muse_pixtable_get_nrow(pt)) {
828 #if DEBUG_PIXTABLE_CREATION /* make sure that we are filling the right part */
829  printf("pixel table before enlargement:\n");
830  cpl_table_dump(pt->table, irow-5, 10, stdout);
831  fflush(stdout);
832 #endif
833  /* If the table is too small, we can as well add a lot more rows *
834  * right now, than adding them in small portions. If we add too many *
835  * this will only marginally decrease the speed of this procedure */
836  const cpl_size nr = 1000000;
837  cpl_msg_debug(__func__, "expand table to %"CPL_SIZE_FORMAT" rows "
838  "(add %"CPL_SIZE_FORMAT" lines)!", irow + nr, nr);
839  cpl_table_set_size(pt->table, irow + nr);
840 
841  /* again fill the new section of each column */
842  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_XPOS, irow, nr, 0);
843  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_YPOS, irow, nr, 0);
844  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_LAMBDA, irow, nr, 0);
845  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_DATA, irow, nr, 0);
846  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_STAT, irow, nr, 0);
847  cpl_table_fill_column_window_int(pt->table, MUSE_PIXTABLE_DQ, irow, nr, 0);
848  cpl_table_fill_column_window_int(pt->table, MUSE_PIXTABLE_ORIGIN, irow, nr, 0);
849 
850  /* re-get columns as pointers, the memory location might have changed */
851  cdata_xpos = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_XPOS);
852  cdata_ypos = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_YPOS);
853  cdata_lambda = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_LAMBDA);
854  cdata_data = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_DATA);
855  cdata_stat = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_STAT);
856  cdata_dq = cpl_table_get_data_int(pt->table, MUSE_PIXTABLE_DQ);
857  cdata_origin = (uint32_t *)cpl_table_get_data_int(pt->table,
858  MUSE_PIXTABLE_ORIGIN);
859 #if DEBUG_PIXTABLE_CREATION /* make sure that we have filled the right part */
860  printf("pixel table after filling new rows:\n");
861  cpl_table_dump(pt->table, irow-5, 10, stdout);
862  fflush(stdout);
863 #endif
864  } /* if new rows needed */
865 #endif /* !PIXTABLE_CREATE_CCDSIZED */
866 
867  /* Assign the coordinates (slice angle is in degrees). */
868  double angle = slice_angle ? slice_angle[islice] * CPL_MATH_RAD_DEG
869  : 0.;
870  cdata_xpos[irow] = aGeoTable
871  ? dx * cos(angle) + slice_x[islice]
872  : dx + islice * kMuseSliceHiLikelyWidth
873  + kMuseSliceHiLikelyWidth / 2.;
874  cdata_ypos[irow] = aGeoTable
875  ? dx * sin(angle) + slice_y[islice]
876  : 0.; /* no vertical position for simple case */
877 #if DEBUG_PIXTABLE_CREATION
878  if (!isfinite(cdata_xpos[irow]) ||
879  cdata_xpos[irow] < -200 || cdata_xpos[irow] > 4000) {
880  cpl_msg_debug(__func__, "weird data in x: %e %e",
881  cdata_xpos[irow], cdata_ypos[irow]);
882  }
883  if (!isfinite(cdata_ypos[irow]) ||
884  cdata_ypos[irow] < -200 || cdata_ypos[irow] > 200) {
885  cpl_msg_debug(__func__, "weird data in y: %e %e",
886  cdata_xpos[irow], cdata_ypos[irow]);
887  }
888 #endif
889  cdata_lambda[irow] = lambda;
890 
891  /* assign the data values */
892  cdata_data[irow] = pixdata[(i-1) + (j-1)*xsize];
893  cdata_dq[irow] = pixdq[(i-1) + (j-1)*xsize];
894  cdata_stat[irow] = pixstat[(i-1) + (j-1)*xsize];
895  /* assign origin and slice number */
896  cdata_origin[irow] = muse_pixtable_origin_encode_fast(i, j, ifu,
897  islice + 1,
898  offset);
899  } /* for i (horizontal pixels) */
900  } /* for j (vertical direction) */
901 
902  /* we are now done with this slice, clean up */
903  muse_trace_polys_delete(ptrace);
904  cpl_polynomial_delete(pwave);
905  cpl_vector_delete(pos);
906  } /* for islice */
907  cpl_table_delete(geopos);
908  cpl_msg_debug(__func__, "IFU %d took %gs (CPU time), %gs (wall-clock)",
909  ifu, cpl_test_get_cputime() - cputime,
910  cpl_test_get_walltime() - walltime);
911 
912  /* itablerow is the last written to location (starting at zero), so add one */
913  if (muse_pixtable_get_nrow(pt) > itablerow) {
914  cpl_msg_debug(__func__, "Trimming pixel table to %"CPL_SIZE_FORMAT" of %"
915  CPL_SIZE_FORMAT" rows", itablerow, muse_pixtable_get_nrow(pt));
916 #if DEBUG_PIXTABLE_CREATION /* make sure that we are trimming correctly */
917  printf("end of used part of pixel table before trimming:\n");
918  cpl_table_dump(pt->table, itablerow-5, 10, stdout);
919  fflush(stdout);
920 #endif
921  cpl_table_set_size(pt->table, itablerow);
922  }
923  /* now that we are done creating it, we can also *
924  * compute its extremes for the first time */
926 #if DEBUG_PIXTABLE_CREATION
927  printf("beginning of pixel table before returning:\n");
928  cpl_table_dump(pt->table, 0, 5, stdout);
929  fflush(stdout);
930  printf("end of pixel table before returning:\n");
931  cpl_table_dump(pt->table, muse_pixtable_get_nrow(pt)-5, 10, stdout);
932  fflush(stdout);
933 #endif
934 
935  return pt;
936 } /* muse_pixtable_create() */
937 
938 /*---------------------------------------------------------------------------*/
951 /*---------------------------------------------------------------------------*/
954 {
955  if (aPixtable == NULL) {
956  return NULL;
957  }
958  muse_pixtable *pt = cpl_calloc(1, sizeof(muse_pixtable));
959  pt->table = cpl_table_duplicate(aPixtable->table);
960  pt->header = cpl_propertylist_duplicate(aPixtable->header);
961  return pt;
962 }
963 
964 /*---------------------------------------------------------------------------*/
973 /*---------------------------------------------------------------------------*/
974 void
976 {
977  /* if we already get NULL, we don't need to do anything */
978  if (!aPixtable) {
979  return;
980  }
981 
982  /* delete the table portion */
983  cpl_table_delete(aPixtable->table);
984  aPixtable->table = NULL;
985 
986  /* delete the header portion */
987  cpl_propertylist_delete(aPixtable->header);
988  aPixtable->header = NULL;
989 
990  cpl_free(aPixtable);
991 } /* muse_pixtable_delete() */
992 
993 /*---------------------------------------------------------------------------*/
1014 /*---------------------------------------------------------------------------*/
1015 static cpl_error_code
1016 muse_pixtable_save_image(muse_pixtable *aPixtable, const char *aFilename)
1017 {
1018  const char *id = "muse_pixtable_save"; /* pretend to be in that function */
1019  cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
1020  cpl_size nrow = muse_pixtable_get_nrow(aPixtable);
1021  cpl_ensure_code(nrow > 0, CPL_ERROR_ILLEGAL_INPUT);
1022 
1023  cpl_errorstate state = cpl_errorstate_get();
1024  cpl_array *columns = cpl_table_get_column_names(aPixtable->table);
1025  int icol, ncol = cpl_array_get_size(columns);
1026  for (icol = 0; icol < ncol; icol++) {
1027  const char *colname = cpl_array_get_string(columns, icol);
1028  /* access data buffer and wrap as corresponding CPL image type */
1029  cpl_type type = cpl_table_get_column_type(aPixtable->table, colname);
1030  cpl_image *image = NULL;
1031  switch (type) {
1032  case CPL_TYPE_FLOAT: {
1033  float *data = cpl_table_get_data_float(aPixtable->table, colname);
1034  image = cpl_image_wrap_float(1, nrow, data);
1035  break;
1036  }
1037  case CPL_TYPE_INT: {
1038  int *data = cpl_table_get_data_int(aPixtable->table, colname);
1039  image = cpl_image_wrap_int(1, nrow, data);
1040  break;
1041  }
1042  default:
1043  cpl_error_set_message(id, CPL_ERROR_UNSUPPORTED_MODE, "type \"%s\" (of "
1044  "column %s) is not supported for MUSE pixel tables",
1045  cpl_type_get_name(type), colname);
1046  continue;
1047  } /* switch */
1048  /* now construct minimal extension header with EXTNAME and *
1049  * BUNIT and save the image with its internal data type */
1050  cpl_propertylist *hext = cpl_propertylist_new();
1051  cpl_propertylist_append_string(hext, "EXTNAME", colname);
1052  const char *unit = cpl_table_get_column_unit(aPixtable->table, colname);
1053  if (unit) {
1054  cpl_propertylist_append_string(hext, "BUNIT", unit);
1055  }
1056  cpl_image_save(image, aFilename, CPL_TYPE_UNSPECIFIED, hext, CPL_IO_EXTEND);
1057  cpl_image_unwrap(image);
1058  cpl_propertylist_delete(hext);
1059  } /* for icol (all table columns) */
1060  cpl_array_delete(columns);
1061  return cpl_errorstate_is_equal(state) ? CPL_ERROR_NONE : cpl_error_get_code();
1062 } /* muse_pixtable_save_image() */
1063 
1064 /*---------------------------------------------------------------------------*/
1082 /*---------------------------------------------------------------------------*/
1083 cpl_error_code
1084 muse_pixtable_save(muse_pixtable *aPixtable, const char *aFilename)
1085 {
1086  cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
1087 
1088  /* let CPL do the real work and propagate any errors and errorstates that *
1089  * it generates; the secondary header should be generated automatically *
1090  * from the table data we have set in muse_pixtable_create(). */
1091  /* save headers separately to keep WCS information */
1092  cpl_error_code rc = cpl_propertylist_save(aPixtable->header, aFilename,
1093  CPL_IO_CREATE);
1094  if (rc != CPL_ERROR_NONE) {
1095  cpl_error_set_message(__func__, rc, "could not save FITS header of pixel "
1096  "table \"%s\"", aFilename);
1097  return rc;
1098  }
1099  /* test if we need to save it as image */
1100  cpl_boolean asimage = getenv("MUSE_PIXTABLE_SAVE_AS_IMAGE")
1101  && atoi(getenv("MUSE_PIXTABLE_SAVE_AS_IMAGE"));
1102  if (asimage) {
1103  cpl_msg_debug(__func__, "Saving pixel table \"%s\" as image", aFilename);
1104  rc = muse_pixtable_save_image(aPixtable, aFilename);
1105  } else {
1106  rc = cpl_table_save(aPixtable->table, NULL, NULL, aFilename, CPL_IO_EXTEND);
1107  }
1108  return rc;
1109 } /* muse_pixtable_save() */
1110 
1111 /*---------------------------------------------------------------------------*/
1133 /*---------------------------------------------------------------------------*/
1134 static cpl_table *
1135 muse_pixtable_load_window_image(const char *aFilename,
1136  cpl_size aStart, cpl_size aNRows)
1137 {
1138  const char *id = "muse_pixtable_load"; /* pretend to be in that function */
1139  /* determine "image" window to read, the y-direction *
1140  * in the image is the dimension of the rows */
1141  cpl_size y1 = aStart + 1,
1142  y2 = aStart + aNRows;
1143  /* find and load all image extensions using their EXTNAMEs */
1144  cpl_size nrow = 0;
1145  cpl_table *table = cpl_table_new(nrow);
1146  int iext, next = cpl_fits_count_extensions(aFilename);
1147  for (iext = 1; iext <= next; iext++) {
1148  cpl_errorstate ps = cpl_errorstate_get();
1149  cpl_image *column = cpl_image_load_window(aFilename, CPL_TYPE_UNSPECIFIED,
1150  0, iext, 1, y1, 1, y2);
1151  if (!column || !cpl_errorstate_is_equal(ps)) {
1152  cpl_image_delete(column);
1153  cpl_error_set_message(id, cpl_error_get_code(), "could not load extension"
1154  " %d of pixel table \"%s\"", iext, aFilename);
1155  continue;
1156  }
1157  cpl_propertylist *hext = cpl_propertylist_load(aFilename, iext);
1158  const char *colname = muse_pfits_get_extname(hext);
1159  cpl_size nrows = cpl_image_get_size_x(column)
1160  * cpl_image_get_size_y(column);
1161  if (nrow < 1) {
1162  cpl_table_set_size(table, nrows);
1163  nrow = nrows;
1164  } else if (nrows != nrow) {
1165  cpl_error_set_message(id, CPL_ERROR_INCOMPATIBLE_INPUT, "size of column "
1166  "%s does not match", colname);
1167  cpl_propertylist_delete(hext);
1168  cpl_image_delete(column);
1169  continue;
1170  }
1171  cpl_type type = cpl_image_get_type(column);
1172  switch (type) {
1173  case CPL_TYPE_FLOAT:
1174  cpl_table_wrap_float(table, cpl_image_unwrap(column), colname);
1175  break;
1176  case CPL_TYPE_INT:
1177  cpl_table_wrap_int(table, cpl_image_unwrap(column), colname);
1178  break;
1179  default:
1180  cpl_error_set_message(id, CPL_ERROR_UNSUPPORTED_MODE, "type \"%s\" (of "
1181  "column %s) is not supported for MUSE pixel tables",
1182  cpl_type_get_name(type), colname);
1183  } /* switch */
1184  cpl_errorstate state = cpl_errorstate_get();
1185  const char *unit = cpl_propertylist_get_string(hext, "BUNIT");
1186  if (!cpl_errorstate_is_equal(state)) {
1187  cpl_errorstate_set(state);
1188  }
1189  if (unit) {
1190  cpl_table_set_column_unit(table, colname, unit);
1191  }
1192  cpl_propertylist_delete(hext);
1193  } /* for iext (all FITS extensions) */
1194  return table;
1195 } /* muse_pixtable_load_window_image() */
1196 
1197 /*---------------------------------------------------------------------------*/
1216 /*---------------------------------------------------------------------------*/
1217 muse_pixtable *
1218 muse_pixtable_load_window(const char *aFilename,
1219  cpl_size aStart, cpl_size aNRows)
1220 {
1221  /* create the output pixel table structure, and NULL out the components */
1222  muse_pixtable *pt = cpl_calloc(1, sizeof(muse_pixtable));
1223 
1224  /* load the header and return with an error if something went wrong */
1225  cpl_errorstate prestate = cpl_errorstate_get();
1226  pt->header = cpl_propertylist_load(aFilename, 0);
1227  cpl_ensure(cpl_errorstate_is_equal(prestate) && pt->header,
1228  cpl_error_get_code(), NULL);
1230  cpl_msg_error(__func__, "unknown pixel table type found in \"%s\"", aFilename);
1232  return NULL;
1233  }
1234 
1235  /* determine the type of data (FITS binary table extension or *
1236  * FITS multi-extension images) by looking at the first extension */
1237  cpl_propertylist *hext = cpl_propertylist_load(aFilename, 1);
1238  cpl_boolean asimage = !strcmp(cpl_propertylist_get_string(hext, "XTENSION"),
1239  "IMAGE");
1240  cpl_propertylist_delete(hext);
1241 
1242  /* load the table itself and return an error if something went wrong */
1243  if (asimage) {
1244  cpl_msg_info(__func__, "Loading pixel table \"%s\" (as image)", aFilename);
1245  pt->table = muse_pixtable_load_window_image(aFilename, aStart, aNRows);
1246  } else {
1247  cpl_msg_info(__func__, "Loading pixel table \"%s\"", aFilename);
1248  pt->table = cpl_table_load_window(aFilename, 1, 0, NULL, aStart, aNRows);
1249  }
1250  cpl_ensure(cpl_errorstate_is_equal(prestate) && pt->table,
1251  cpl_error_get_code(), NULL);
1252  cpl_error_code rc = muse_cpltable_check(pt->table, muse_pixtable_def);
1253  if (rc != CPL_ERROR_NONE) {
1254  cpl_error_set_message(__func__, rc, "pixel table \"%s\" does not contain "
1255  "all expected columns", aFilename);
1256  }
1257 
1258  return pt;
1259 } /* muse_pixtable_load_window() */
1260 
1261 /*---------------------------------------------------------------------------*/
1281 /*---------------------------------------------------------------------------*/
1282 muse_pixtable *
1283 muse_pixtable_load(const char *aFilename)
1284 {
1285  /* find out original table length from the header of the FITS binary table */
1286  cpl_errorstate prestate = cpl_errorstate_get();
1287  cpl_propertylist *theader = cpl_propertylist_load(aFilename, 1);
1288  cpl_ensure(cpl_errorstate_is_equal(prestate) && theader,
1289  cpl_error_get_code(), NULL);
1290  cpl_size nrow = cpl_propertylist_get_long_long(theader, "NAXIS2");
1291  cpl_propertylist_delete(theader);
1292 
1293  /* now that we know the full size, load all rows */
1294  muse_pixtable *pt = muse_pixtable_load_window(aFilename, 0, nrow);
1295  return pt;
1296 } /* muse_pixtable_load() */
1297 
1298 /*---------------------------------------------------------------------------*/
1315 /*---------------------------------------------------------------------------*/
1316 muse_pixtable *
1318  double aLambdaMin, double aLambdaMax)
1319 {
1320  muse_pixtable *pt = muse_pixtable_load(aFilename);
1321  if (!pt) {
1322  return NULL;
1323  }
1324  cpl_error_code rc = muse_pixtable_restrict_wavelength(pt, aLambdaMin,
1325  aLambdaMax);
1326  if (rc != CPL_ERROR_NONE) {
1328  return NULL;
1329  }
1330  if (muse_pixtable_get_nrow(pt) < 1) {
1331  cpl_msg_error(__func__, "Pixel table contains no entries after cutting to "
1332  "%.3f..%.3f Angstrom", aLambdaMin, aLambdaMax);
1333  cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
1335  return NULL;
1336  }
1337  return pt;
1338 } /* muse_pixtable_load_restricted_wavelength() */
1339 
1340 /*---------------------------------------------------------------------------*/
1374 /*---------------------------------------------------------------------------*/
1375 muse_pixtable *
1376 muse_pixtable_load_merge_channels(cpl_table *aExposureList,
1377  double aLambdaMin, double aLambdaMax)
1378 {
1379  cpl_ensure(aExposureList, CPL_ERROR_NULL_INPUT, NULL);
1380 
1381  muse_pixtable *pt = NULL; /* the big output pixel table */
1382  if (cpl_table_has_column(aExposureList, "00")) {
1383  const char *filename = cpl_table_get_string(aExposureList, "00", 0);
1384  if (filename) {
1385  pt = muse_pixtable_load_restricted_wavelength(filename, aLambdaMin,
1386  aLambdaMax);
1387  }
1388  if (pt) {
1389  return pt;
1390  }
1391  } /* if table with "00" column */
1392  cpl_boolean isfirst = CPL_TRUE;
1393  double fluxlref = 0, /* propagated from lamp flat */
1394  fluxsref = 0; /* propagated from sky flat */
1395  int i, nifu = 0;
1396  for (i = 1; i <= kMuseNumIFUs; i++) {
1397  char *colname = cpl_sprintf("%02d", i);
1398  const char *filename = cpl_table_get_string(aExposureList, colname, 0);
1399  cpl_free(colname);
1400  if (!filename) {
1401  cpl_msg_warning(__func__, "Channel for IFU %02d is missing", i);
1402  continue;
1403  }
1405  aLambdaMin,
1406  aLambdaMax);
1407  if (!onept) {
1408  cpl_msg_error(__func__, "failed to load pixel table from \"%s\"",
1409  filename);
1410  return pt;
1411  }
1412  nifu++;
1413 
1414  /* if this is the first pixel table, use its data to create the big one */
1415  if (isfirst) {
1416  pt = onept;
1417  cpl_msg_debug(__func__, "loaded pixel table with %"CPL_SIZE_FORMAT" rows",
1419  isfirst = CPL_FALSE;
1420  cpl_errorstate prestate = cpl_errorstate_get();
1421  fluxsref = cpl_propertylist_get_double(pt->header, MUSE_HDR_FLAT_FLUX_SKY);
1422  fluxlref = cpl_propertylist_get_double(pt->header, MUSE_HDR_FLAT_FLUX_LAMP);
1423  if (fluxsref == 0. && fluxlref == 0. && !cpl_errorstate_is_equal(prestate)) {
1424  /* If the flat headers are both missing, this can only mean that *
1425  * the exposure was previously merged into the pixel table we just *
1426  * loaded. Then we can recover from the error and stop right here. */
1427  cpl_msg_debug(__func__, "\"%s\" was previously merged (got \"%s\" when"
1428  " asking for flat-field fluxes)", filename,
1429  cpl_error_get_message());
1430  cpl_errorstate_set(prestate);
1431  break;
1432  }
1433  if (fluxsref == 0. && fluxlref > 0. && !cpl_errorstate_is_equal(prestate)) {
1434  /* only the sky-flat level was missing, output a warning */
1435  cpl_msg_warning(__func__, "only found reference lamp-flat flux (%e) in "
1436  "\"%s\", flux levels may vary between IFUs!", fluxlref,
1437  filename);
1438  cpl_errorstate_set(prestate);
1439  } else {
1440  cpl_msg_debug(__func__, "reference flat fluxes sky: %e lamp: %e",
1441  fluxsref, fluxlref);
1442  }
1443  cpl_propertylist_erase(pt->header, MUSE_HDR_FLAT_FLUX_SKY);
1444  cpl_propertylist_erase(pt->header, MUSE_HDR_FLAT_FLUX_LAMP);
1445  continue;
1446  } /* if isfirst */
1447 
1448  /* copy the offset headers to the output pixel table */
1449  muse_pixtable_origin_copy_offsets(pt, onept, 0);
1450 
1451  /* to fix the flux offset (using propagated flat-field fluxes) */
1452  cpl_errorstate state = cpl_errorstate_get();
1453  double fluxs = cpl_propertylist_get_double(onept->header,
1454  MUSE_HDR_FLAT_FLUX_SKY),
1455  fluxl = cpl_propertylist_get_double(onept->header,
1456  MUSE_HDR_FLAT_FLUX_LAMP),
1457  scale = 1.;
1458  if (fluxsref > 0. && fluxs > 0.) { /* prefer sky flat flux scaling */
1459  scale = fluxs / fluxsref;
1460  } else if (fluxlref > 0. && fluxl > 0.) {
1461  scale = fluxl / fluxlref;
1462  if (!cpl_errorstate_is_equal(state)) {
1463  cpl_msg_warning(__func__, "only found relative lamp-flat flux (%e) in "
1464  "\"%s\", flux levels may vary between IFUs!", fluxl,
1465  filename);
1466  cpl_errorstate_set(state);
1467  } /* if bad error state */
1468  } /* else: use lamp-flat values */
1469  cpl_table_divide_scalar(onept->table, MUSE_PIXTABLE_DATA, scale);
1470  cpl_table_divide_scalar(onept->table, MUSE_PIXTABLE_STAT, scale*scale);
1471 
1472  /* append this pixel table to the big one */
1473  cpl_table_insert(pt->table, onept->table, muse_pixtable_get_nrow(pt));
1474  cpl_msg_debug(__func__, "big pixel table now has %"CPL_SIZE_FORMAT" entries,"
1475  " scale was %e (flat fluxes sky: %e lamp: %e)",
1476  muse_pixtable_get_nrow(pt), scale, fluxs, fluxl);
1477 
1478  /* the insertion duplicates the data, so we can delete it now */
1479  muse_pixtable_delete(onept);
1480  } /* for i (all IFUs) */
1482  if (!pt) {
1483  cpl_error_set_message(__func__, CPL_ERROR_FILE_NOT_FOUND,
1484  "None of the pixel tables could be loaded");
1485  return NULL; /* exit here to not crash below! */
1486  }
1487  /* data does not belong to any one IFU any more, so *
1488  * remove the EXTNAME header that contains CHAN%02d */
1489  cpl_propertylist_erase_regexp(pt->header, "^EXTNAME", 0);
1490  /* add the status header */
1491  cpl_propertylist_update_int(pt->header, MUSE_HDR_PT_MERGED, nifu);
1492  cpl_propertylist_set_comment(pt->header, MUSE_HDR_PT_MERGED,
1493  MUSE_HDR_PT_MERGED_COMMENT);
1494  return pt;
1495 } /* muse_pixtable_load_merge_channels() */
1496 
1497 /*---------------------------------------------------------------------------*/
1509 /*---------------------------------------------------------------------------*/
1510 int
1512 {
1513  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, MUSE_PIXTABLE_TYPE_UNKNOWN);
1514  const char *type = cpl_propertylist_get_string(aPixtable->header,
1516 #if 0
1517  cpl_msg_debug(__func__, "pixel table type \"%s\"", type);
1518 #endif
1519  if (!type) {
1521  }
1522  if (!strncmp(type, MUSE_PIXTABLE_STRING_FULL,
1523  strlen(MUSE_PIXTABLE_STRING_FULL) + 1)) {
1524  return MUSE_PIXTABLE_TYPE_FULL;
1525  } else if (!strncmp(type, MUSE_PIXTABLE_STRING_SIMPLE,
1526  strlen(MUSE_PIXTABLE_STRING_SIMPLE) + 1)) {
1528  }
1529 
1531 } /* muse_pixtable_get_type() */
1532 
1533 /*---------------------------------------------------------------------------*/
1542 /*---------------------------------------------------------------------------*/
1543 cpl_size
1545 {
1546  /* make sure that we are getting valid input */
1547  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, 0);
1548  cpl_ensure(aPixtable->table, CPL_ERROR_NULL_INPUT, 0);
1549 
1550  /* let CPL do the work */
1551  return cpl_table_get_nrow(aPixtable->table);
1552 } /* muse_pixtable_get_nrow() */
1553 
1554 /*---------------------------------------------------------------------------*/
1570 /*---------------------------------------------------------------------------*/
1571 cpl_error_code
1573 {
1574  /* make sure that we are getting valid input */
1575  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1576  CPL_ERROR_NULL_INPUT);
1577  cpl_ensure_code(muse_cpltable_check(aPixtable->table, muse_pixtable_def)
1578  == CPL_ERROR_NONE, CPL_ERROR_DATA_NOT_FOUND);
1579 
1580  if (muse_pixtable_get_nrow(aPixtable) == 0) {
1581  return CPL_ERROR_NONE;
1582  }
1583 
1584  float *cdata_xpos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_XPOS),
1585  *cdata_ypos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_YPOS),
1586  *cdata_lambda = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA);
1587  uint32_t *origin = (uint32_t *)cpl_table_get_data_int(aPixtable->table,
1588  MUSE_PIXTABLE_ORIGIN);
1589  /* set to extreme values for a start */
1590  float xlo = FLT_MAX, xhi = -FLT_MAX,
1591  ylo = FLT_MAX, yhi = -FLT_MAX,
1592  llo = FLT_MAX, lhi = -FLT_MAX;
1593  int ifulo = INT_MAX, ifuhi = 0,
1594  slicelo = INT_MAX, slicehi = 0;
1595  cpl_size i, nrow = muse_pixtable_get_nrow(aPixtable);
1596  for (i = 0; i < nrow; i++) {
1597  if (cdata_xpos[i] > xhi) xhi = cdata_xpos[i];
1598  if (cdata_xpos[i] < xlo) xlo = cdata_xpos[i];
1599  if (cdata_ypos[i] > yhi) yhi = cdata_ypos[i];
1600  if (cdata_ypos[i] < ylo) ylo = cdata_ypos[i];
1601  if (cdata_lambda[i] > lhi) lhi = cdata_lambda[i];
1602  if (cdata_lambda[i] < llo) llo = cdata_lambda[i];
1603  int ifu = muse_pixtable_origin_get_ifu_fast(origin[i]),
1604  slice = muse_pixtable_origin_get_slice_fast(origin[i]);
1605  if (ifu > ifuhi) ifuhi = ifu;
1606  if (ifu < ifulo) ifulo = ifu;
1607  if (slice > slicehi) slicehi = slice;
1608  if (slice < slicelo) slicelo = slice;
1609  } /* for i */
1610  char *dodebug = getenv("MUSE_DEBUG_PIXTABLE_LIMITS");
1611  if (dodebug && atoi(dodebug)) {
1612  cpl_msg_debug(__func__, "x: %f...%f, y: %f...%f, lambda: %f...%f, "
1613  "ifu: %d...%d, slice: %d...%d",
1614  xlo, xhi, ylo, yhi, llo, lhi, ifulo, ifuhi, slicelo, slicehi);
1615  }
1616  cpl_propertylist_erase_regexp(aPixtable->header, MUSE_HDR_PT_LIMITS_REGEXP, 0);
1617  double ptxoff = 0., /* zero by default ... */
1618  ptyoff = 0.; /* for pixel coordinates */
1620  ptxoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL1");
1621  ptyoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL2");
1622  }
1623  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_XLO, xlo + ptxoff);
1624  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_XHI, xhi + ptxoff);
1625  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_YLO, ylo + ptyoff);
1626  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_YHI, yhi + ptyoff);
1627  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_LLO, llo);
1628  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_LHI, lhi);
1629  cpl_propertylist_append_int(aPixtable->header, MUSE_HDR_PT_ILO, ifulo);
1630  cpl_propertylist_append_int(aPixtable->header, MUSE_HDR_PT_IHI, ifuhi);
1631  cpl_propertylist_append_int(aPixtable->header, MUSE_HDR_PT_SLO, slicelo);
1632  cpl_propertylist_append_int(aPixtable->header, MUSE_HDR_PT_SHI, slicehi);
1633 
1634  return CPL_ERROR_NONE;
1635 } /* muse_pixtable_compute_limits() */
1636 
1637 /*---------------------------------------------------------------------------*/
1647 /*---------------------------------------------------------------------------*/
1648 static cpl_error_code
1650 {
1651  cpl_ensure_code(aPixtable && aPixtable->header && aPixtable->table,
1652  CPL_ERROR_NULL_INPUT);
1653  if (cpl_table_count_selected(aPixtable->table) < 1) {
1654  return CPL_ERROR_NONE; /* nothing to do */
1655  }
1656  cpl_array *sel = cpl_table_where_selected(aPixtable->table);
1657  cpl_size narray = cpl_array_get_size(sel),
1658  nselprev = 0, /* selected ones in previous exposures */
1659  ifst = 0, ilst = 0;
1660  const cpl_size *asel = cpl_array_get_data_cplsize_const(sel);
1661  unsigned int nexp = 0;
1662  do {
1663  /* get exposure range for (next) exposure */
1664  char *kwfst = cpl_sprintf(MUSE_HDR_PT_EXP_FST, ++nexp),
1665  *kwlst = cpl_sprintf(MUSE_HDR_PT_EXP_LST, nexp);
1666  if (!cpl_propertylist_has(aPixtable->header, kwfst) ||
1667  !cpl_propertylist_has(aPixtable->header, kwlst)) {
1668  cpl_free(kwfst);
1669  cpl_free(kwlst);
1670  break; /* nothing (more) to do */
1671  }
1672  ifst = cpl_propertylist_get_long_long(aPixtable->header, kwfst);
1673  ilst = cpl_propertylist_get_long_long(aPixtable->header, kwlst);
1674  /* count selected */
1675  cpl_size i, nsel = 0;
1676  for (i = 0; i < narray; i++) {
1677  if (asel[i] >= ifst && asel[i] <= ilst) {
1678  nsel++;
1679  } /* if in range of this exposure */
1680  } /* for i (all array entries) */
1681  cpl_size ifst2 = ifst - nselprev,
1682  ilst2 = ilst - nsel - nselprev;
1683  cpl_msg_debug(__func__, "exp %d old %"CPL_SIZE_FORMAT"..%"CPL_SIZE_FORMAT
1684  ", %"CPL_SIZE_FORMAT" selected (previous: %"CPL_SIZE_FORMAT
1685  "), new %"CPL_SIZE_FORMAT"..%"CPL_SIZE_FORMAT, nexp,
1686  ifst, ilst, nsel, nselprev, ifst2, ilst2);
1687  /* finally update the header entries */
1688  muse_cplpropertylist_update_long_long(aPixtable->header, kwfst, ifst2);
1689  muse_cplpropertylist_update_long_long(aPixtable->header, kwlst, ilst2);
1690  cpl_free(kwfst);
1691  cpl_free(kwlst);
1692  nselprev += nsel; /* add selected ones for previous exposures */
1693  } while (ilst >= ifst);
1694  cpl_array_delete(sel);
1695  return CPL_ERROR_NONE;
1696 } /* muse_pixtable_fix_exp_headers() */
1697 
1698 /*---------------------------------------------------------------------------*/
1707 /*---------------------------------------------------------------------------*/
1708 cpl_error_code
1710  double aHigh)
1711 {
1712  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1713  CPL_ERROR_NULL_INPUT);
1714  if (cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_LLO) > aLow &&
1715  cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_LHI) < aHigh) {
1716  /* no need to do anything */
1717  return CPL_ERROR_NONE;
1718  }
1719 #pragma omp critical(cpl_table_select)
1720  {
1721  cpl_table_unselect_all(aPixtable->table);
1722  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA,
1723  CPL_LESS_THAN, aLow);
1724  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA,
1725  CPL_GREATER_THAN, aHigh);
1726  muse_pixtable_fix_exp_headers(aPixtable);
1727  cpl_table_erase_selected(aPixtable->table);
1728  }
1729  return muse_pixtable_compute_limits(aPixtable);
1730 } /* muse_pixtable_restrict_wavelength() */
1731 
1732 /*---------------------------------------------------------------------------*/
1743 /*---------------------------------------------------------------------------*/
1744 cpl_error_code
1745 muse_pixtable_restrict_xpos(muse_pixtable *aPixtable, double aLo, double aHi)
1746 {
1747  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1748  CPL_ERROR_NULL_INPUT);
1749  if (cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_XLO) > aLo &&
1750  cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_XHI) < aHi) {
1751  /* no need to do anything */
1752  return CPL_ERROR_NONE;
1753  }
1754  double ptxoff = 0.;
1756  /* need to use the real coordinate offset for celestial spherical */
1757  ptxoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL1");
1758  }
1759 #pragma omp critical(cpl_table_select)
1760  {
1761  cpl_table_unselect_all(aPixtable->table);
1762  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_XPOS,
1763  CPL_LESS_THAN, aLo - ptxoff);
1764  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_XPOS,
1765  CPL_GREATER_THAN, aHi - ptxoff);
1766  muse_pixtable_fix_exp_headers(aPixtable);
1767  cpl_table_erase_selected(aPixtable->table);
1768  }
1769  return muse_pixtable_compute_limits(aPixtable);
1770 } /* muse_pixtable_restrict_xpos() */
1771 
1772 /*---------------------------------------------------------------------------*/
1783 /*---------------------------------------------------------------------------*/
1784 cpl_error_code
1785 muse_pixtable_restrict_ypos(muse_pixtable *aPixtable, double aLo, double aHi)
1786 {
1787  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1788  CPL_ERROR_NULL_INPUT);
1789  if (cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_YLO) > aLo &&
1790  cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_YHI) < aHi) {
1791  /* no need to do anything */
1792  return CPL_ERROR_NONE;
1793  }
1794  double ptyoff = 0.;
1796  /* need to use the real coordinate offset for celestial spherical */
1797  ptyoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL2");
1798  }
1799 #pragma omp critical(cpl_table_select)
1800  {
1801  cpl_table_unselect_all(aPixtable->table);
1802  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_YPOS,
1803  CPL_LESS_THAN, aLo - ptyoff);
1804  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_YPOS,
1805  CPL_GREATER_THAN, aHi - ptyoff);
1806  muse_pixtable_fix_exp_headers(aPixtable);
1807  cpl_table_erase_selected(aPixtable->table);
1808  }
1809  return muse_pixtable_compute_limits(aPixtable);
1810 } /* muse_pixtable_restrict_ypos() */
1811 
1812 /*---------------------------------------------------------------------------*/
1829 /*---------------------------------------------------------------------------*/
1830 cpl_error_code
1832 {
1833  cpl_ensure_code(aPixtable && aPixtable->table, CPL_ERROR_NULL_INPUT);
1834  cpl_ensure_code(aMask && aMask->mask, CPL_ERROR_NULL_INPUT);
1835  float *cdata_xpos = cpl_table_get_data_float(aPixtable->table,
1836  MUSE_PIXTABLE_XPOS);
1837  float *cdata_ypos = cpl_table_get_data_float(aPixtable->table,
1838  MUSE_PIXTABLE_YPOS);
1839  cpl_size n_rows = cpl_table_get_nrow(aPixtable->table);
1840  cpl_size i_row;
1841  double crval_x = 0;
1842  double crpix_x = 1;
1843  double cdelt_x = 1;
1844  double crval_y = 0;
1845  double crpix_y = 1;
1846  double cdelt_y = 1;
1847  if (aMask->header) {
1848  crval_x = cpl_propertylist_get_double(aMask->header, "CRVAL1");
1849  crpix_x = cpl_propertylist_get_double(aMask->header, "CRPIX1");
1850  cdelt_x = cpl_propertylist_get_double(aMask->header, "CD1_1");
1851  crval_y = cpl_propertylist_get_double(aMask->header, "CRVAL2");
1852  crpix_y = cpl_propertylist_get_double(aMask->header, "CRPIX2");
1853  cdelt_y = cpl_propertylist_get_double(aMask->header, "CD2_2");
1854  }
1855  cpl_size nx = cpl_mask_get_size_x(aMask->mask);
1856  cpl_size ny = cpl_mask_get_size_y(aMask->mask);
1857  cpl_size n_enabled = cpl_mask_count(aMask->mask);
1858  cpl_msg_debug(__func__, "Mask contains %"CPL_SIZE_FORMAT" (%.2f %%) enabled "
1859  "pixels of %"CPL_SIZE_FORMAT" total", n_enabled,
1860  100.*n_enabled/nx/ny, nx*ny);
1861  cpl_size n_sel = n_rows;
1862  cpl_size n_in_table = 0;
1863  for (i_row = 0; i_row < n_rows; i_row++) {
1864  cpl_size ix = lround((cdata_xpos[i_row] - crval_x) / cdelt_x + crpix_x);
1865  cpl_size iy = lround((cdata_ypos[i_row] - crval_y) / cdelt_y + crpix_y);
1866  if ((ix < 1) || (ix > nx) || (iy < 1) || (iy > ny)) {
1867  continue;
1868  }
1869  n_in_table++;
1870  if (cpl_mask_get(aMask->mask, ix, iy) != CPL_BINARY_1) {
1871  cpl_table_unselect_row(aPixtable->table, i_row);
1872  n_sel--;
1873  }
1874  }
1875  cpl_msg_debug(__func__, "Mask selected %"CPL_SIZE_FORMAT" (%.2f %%/%.2f %%) "
1876  "pixels of %"CPL_SIZE_FORMAT" total/%"CPL_SIZE_FORMAT" in mask "
1877  "area", n_sel, 100.*n_sel/n_rows, 100.*n_sel/n_in_table,
1878  n_rows, n_in_table);
1879  return CPL_ERROR_NONE;
1880 } /* muse_pixtable_and_selected_mask() */
1881 
1882 /*---------------------------------------------------------------------------*/
1909 /*---------------------------------------------------------------------------*/
1910 cpl_error_code
1911 muse_pixtable_dump(muse_pixtable *aPixtable, cpl_size aStart, cpl_size aCount,
1912  unsigned char aDisplayHeader)
1913 {
1914  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1915  CPL_ERROR_NULL_INPUT);
1916  cpl_size nrows = muse_pixtable_get_nrow(aPixtable);
1917  cpl_ensure_code(aStart >= 0 && aStart < nrows && aCount >= 0,
1918  CPL_ERROR_ILLEGAL_INPUT);
1919  cpl_size last = aStart + aCount;
1920  if (last > nrows - 1) {
1921  last = nrows;
1922  }
1923  int haswcs = muse_pixtable_wcs_check(aPixtable);
1924  double ptxoff = 0., ptyoff = 0.;
1925  if (haswcs == MUSE_PIXTABLE_WCS_CELSPH) {
1926  /* need to use the real coordinate offset for celestial spherical */
1927  ptxoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL1");
1928  ptyoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL2");
1929  }
1930  /* for non-pixel coordinates, we will need different formats below */
1931  haswcs = (haswcs == MUSE_PIXTABLE_WCS_NATSPH ||
1932  haswcs == MUSE_PIXTABLE_WCS_CELSPH);
1933  float *cdata_xpos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_XPOS),
1934  *cdata_ypos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_YPOS),
1935  *cdata_lambda = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA),
1936  *cdata_data = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_DATA),
1937  *cdata_stat = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_STAT);
1938  cpl_errorstate es = cpl_errorstate_get();
1939  float *cdata_weight = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_WEIGHT);
1940  cpl_errorstate_set(es); /* ignore errors due to missing weight column */
1941  int *cdata_dq = cpl_table_get_data_int(aPixtable->table, MUSE_PIXTABLE_DQ);
1942  uint32_t *cdata_origin = (uint32_t *)cpl_table_get_data_int(aPixtable->table,
1943  MUSE_PIXTABLE_ORIGIN);
1944  cpl_ensure_code(cdata_xpos && cdata_ypos && cdata_lambda &&
1945  cdata_data && cdata_dq && cdata_stat,
1946  CPL_ERROR_BAD_FILE_FORMAT);
1947 
1948  /* print header */
1949  if (aDisplayHeader) {
1950  printf("# xpos ypos lambda data dq stat"
1951  " weight exposure IFU xCCD yCCD xRaw yRaw slice\n");
1952  }
1953  if (aDisplayHeader == 1) {
1954  printf("#%13s %13s %9s %11s flag %11s ---------- No No pix "
1955  " pix pix pix No\n# flux in [%s]\n# flux**2 in [%s]\n",
1956  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_XPOS),
1957  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_YPOS),
1958  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_LAMBDA),
1959  "(flux)", "(flux**2)",
1960  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_DATA),
1961  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_STAT));
1962  }
1963 
1964  cpl_size i;
1965  for (i = aStart; i < last; i++) {
1966  int x = muse_pixtable_origin_get_x(cdata_origin[i], aPixtable, i),
1967  y = muse_pixtable_origin_get_y_fast(cdata_origin[i]);
1968  int xraw = x + kMusePreOverscanSize, /* add prescans */
1969  yraw = y + kMusePreOverscanSize;
1970  /* possibly add 2 overscans */
1971  if (x > kMuseOutputXRight / 2) {
1972  xraw += kMusePreOverscanSize * 2;
1973  }
1974  if (y > kMuseOutputYTop / 2) {
1975  yraw += kMusePreOverscanSize * 2;
1976  }
1977  if (haswcs) {
1978  printf("%14.7e %14.7e %9.3f ", cdata_xpos[i] + ptxoff, cdata_ypos[i] + ptyoff,
1979  cdata_lambda[i]);
1980  } else {
1981  printf("%14.8f %14.8f %9.3f ", cdata_xpos[i], cdata_ypos[i], cdata_lambda[i]);
1982  }
1983  printf("%12.5e 0x%08x %11.5e %10.4e %2d %2d %4d %4d %4d %4d %2d\n",
1984  cdata_data[i], cdata_dq[i], cdata_stat[i],
1985  cdata_weight ? cdata_weight[i] : 0.,
1986  muse_pixtable_get_expnum(aPixtable, i),
1987  cdata_origin ? muse_pixtable_origin_get_ifu_fast(cdata_origin[i])
1988  : 0,
1989  x, y, xraw, yraw,
1990  cdata_origin ? muse_pixtable_origin_get_slice_fast(cdata_origin[i])
1991  : 0);
1992  } /* for i (pixel table rows) */
1993 
1994  return CPL_ERROR_NONE;
1995 } /* muse_pixtable_dump() */
1996 
1997 /*---------------------------------------------------------------------------*/
2019 /*---------------------------------------------------------------------------*/
2022 {
2023  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, MUSE_PIXTABLE_WCS_UNKNOWN);
2024  const char *unitx = cpl_table_get_column_unit(aPixtable->table,
2025  MUSE_PIXTABLE_XPOS),
2026  *unity = cpl_table_get_column_unit(aPixtable->table,
2027  MUSE_PIXTABLE_YPOS);
2028  cpl_ensure(unitx, CPL_ERROR_DATA_NOT_FOUND, MUSE_PIXTABLE_WCS_UNKNOWN);
2029  /* should be equal in the first three characters */
2030  cpl_ensure(!strncmp(unitx, unity, 4), CPL_ERROR_INCOMPATIBLE_INPUT,
2032  if (!strncmp(unitx, "deg", 4)) {
2033  return MUSE_PIXTABLE_WCS_CELSPH;
2034  }
2035  if (!strncmp(unitx, "pix", 4)) {
2036  return MUSE_PIXTABLE_WCS_PIXEL;
2037  }
2038  if (!strncmp(unitx, "rad", 4)) {
2039  return MUSE_PIXTABLE_WCS_NATSPH;
2040  }
2041  cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
2043 } /* muse_pixtable_wcs_check() */
2044 
2045 /*---------------------------------------------------------------------------*/
2053 /*---------------------------------------------------------------------------*/
2054 cpl_boolean
2056 {
2057  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, CPL_FALSE);
2058  cpl_errorstate prestate = cpl_errorstate_get();
2059  cpl_boolean flag = cpl_propertylist_get_bool(aPixtable->header,
2061  cpl_errorstate_set(prestate);
2062  return flag;
2063 }
2064 
2065 /*---------------------------------------------------------------------------*/
2073 /*---------------------------------------------------------------------------*/
2074 cpl_boolean
2076 {
2077  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, CPL_FALSE);
2078  cpl_errorstate prestate = cpl_errorstate_get();
2079  cpl_boolean flag = cpl_propertylist_get_bool(aPixtable->header,
2081  cpl_errorstate_set(prestate);
2082  return flag;
2083 }
2084 
2085 /*---------------------------------------------------------------------------*/
2094 /*---------------------------------------------------------------------------*/
2095 cpl_error_code
2096 muse_pixtable_reset_dq(muse_pixtable *aPixtable, unsigned int aDQ)
2097 {
2098  cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
2099 
2100  unsigned int *dq = (unsigned int *)cpl_table_get_data_int(aPixtable->table,
2101  MUSE_PIXTABLE_DQ),
2102  inverse = ~aDQ; /* inverse bitmask to AND each row's DQ with */
2103  cpl_size i, nrow = muse_pixtable_get_nrow(aPixtable);
2104  #pragma omp parallel for default(none) /* as req. by Ralf */ \
2105  shared(dq, inverse, nrow)
2106  for (i = 0; i < nrow; i++) {
2107  dq[i] &= inverse;
2108  } /* for i (all table rows) */
2109  return CPL_ERROR_NONE;
2110 } /* muse_pixtable_reset_dq() */
2111 
2112 /*---------------------------------------------------------------------------*/
2129 /*---------------------------------------------------------------------------*/
2132 {
2133  cpl_ensure(aPixtable && aPixtable->header, CPL_ERROR_NULL_INPUT, NULL);
2134  unsigned int expnum = muse_pixtable_get_expnum(aPixtable, 0),
2135  explast = muse_pixtable_get_expnum(aPixtable,
2136  muse_pixtable_get_nrow(aPixtable) - 1);
2137  cpl_ensure(expnum == explast, CPL_ERROR_ILLEGAL_INPUT, NULL);
2138 
2139  /* data seems to be valid, we can create the output list */
2141 
2142  /* split the pixel table up into per-slice pixel tables to handle *
2143  * them separately (to get the x-range of each slice for the header) */
2145  /* variables for current image and current IFU */
2146  muse_image *image = NULL;
2147  unsigned short ifu = 0, /* we haven't found any IFU yet */
2148  ilist = 0; /* image index in the list */
2149  int ipt, npt = muse_pixtable_extracted_get_size(pts);
2150  for (ipt = 0; ipt < npt; ipt++) {
2151  float *cdata = cpl_table_get_data_float(pts[ipt]->table, MUSE_PIXTABLE_DATA),
2152  *cstat = cpl_table_get_data_float(pts[ipt]->table, MUSE_PIXTABLE_STAT);
2153  int *cdq = cpl_table_get_data_int(pts[ipt]->table, MUSE_PIXTABLE_DQ);
2154  uint32_t *corigin = (uint32_t *)cpl_table_get_data_int(pts[ipt]->table,
2155  MUSE_PIXTABLE_ORIGIN);
2156  /* if we got to the next (or first) IFU, create new output *
2157  * image of the size of a typical MUSE CCD copy the header *
2158  * from the pixel table but remove specific entries */
2159  if (ifu != muse_pixtable_origin_get_ifu_fast(corigin[0])) {
2160  image = muse_image_new();
2161  image->header = cpl_propertylist_duplicate(pts[ipt]->header);
2162  cpl_propertylist_erase_regexp(image->header, "^ESO DRS MUSE PIXTABLE", 0);
2163  image->data = cpl_image_new(kMuseOutputXRight, kMuseOutputYTop, CPL_TYPE_FLOAT);
2164  image->dq = cpl_image_new(kMuseOutputXRight, kMuseOutputYTop, CPL_TYPE_INT);
2165  /* fill DQ image with EURO3D_MISSDATA, upper value will be cast correctly */
2166  cpl_image_fill_noise_uniform(image->dq, EURO3D_MISSDATA, EURO3D_MISSDATA + 0.1);
2167  image->stat = cpl_image_new(kMuseOutputXRight, kMuseOutputYTop, CPL_TYPE_FLOAT);
2168  cpl_msg_debug(__func__, "new image (index %hu in list)", ilist);
2169  muse_imagelist_set(list, image, ilist++);
2170  } /* if ifu */
2171  if (!image) { /* it cannot really go wrong, but to be sure... */
2172  cpl_msg_error(__func__, "ipt = %d: no image!", ipt);
2173  continue;
2174  }
2175  float *idata = cpl_image_get_data_float(image->data),
2176  *istat = cpl_image_get_data_float(image->stat);
2177  int *idq = cpl_image_get_data_int(image->dq);
2178 
2179  ifu = muse_pixtable_origin_get_ifu_fast(corigin[0]);
2180  unsigned short slice = muse_pixtable_origin_get_slice_fast(corigin[0]);
2181  unsigned int xoff = muse_pixtable_origin_get_offset(pts[ipt], expnum, ifu,
2182  slice),
2183  x1 = INT_MAX, x2 = 0,
2184  irow, nrow = muse_pixtable_get_nrow(pts[ipt]);
2185  for (irow = 0; irow < nrow; irow++) {
2186  /* get coordinate indices from the origin column */
2187  unsigned int x = muse_pixtable_origin_get_x_fast(corigin[irow], xoff) - 1,
2188  y = muse_pixtable_origin_get_y_fast(corigin[irow]) - 1;
2189  idata[x + y*kMuseOutputXRight] = cdata[irow];
2190  idq[x + y*kMuseOutputXRight] = cdq[irow];
2191  istat[x + y*kMuseOutputXRight] = cstat[irow];
2192  if (x < x1) {
2193  x1 = x;
2194  }
2195  if (x > x2) {
2196  x2 = x;
2197  }
2198  } /* for irow */
2199  /* record the approximate horizontal center in the output header */
2200  char *keyword = cpl_sprintf("ESO DRS MUSE SLICE%hu CENTER", slice);
2201  cpl_propertylist_update_float(image->header, keyword, (x2 + x1) / 2. + 1.);
2202 #if 0
2203  cpl_msg_debug(__func__, "IFU %hu %s = %.1f", ifu, keyword,
2204  (x2 + x1) / 2. + 1.);
2205 #endif
2206  cpl_free(keyword);
2207  } /* for ipt */
2209 
2210  return list;
2211 } /* muse_pixtable_to_imagelist() */
2212 
2213 /*---------------------------------------------------------------------------*/
2239 /*---------------------------------------------------------------------------*/
2240 cpl_error_code
2242 {
2243  cpl_ensure_code(aPixtable && aPixtable->header && aList, CPL_ERROR_NULL_INPUT);
2244  unsigned int expnum = muse_pixtable_get_expnum(aPixtable, 0),
2245  explast = muse_pixtable_get_expnum(aPixtable,
2246  muse_pixtable_get_nrow(aPixtable) - 1);
2247  cpl_ensure_code(expnum == explast, CPL_ERROR_ILLEGAL_INPUT);
2248 
2249  /* split the pixel table up into per-slice pixel tables *
2250  * as in muse_pixtable_to_imagelist() and ensure that the *
2251  * number of IFUs matches the number of input images */
2253  if (muse_pixtable_extracted_get_size(pts) / kMuseSlicesPerCCD
2254  != muse_imagelist_get_size(aList)) {
2256  return cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
2257  }
2258  /* variables for current image and current IFU */
2259  muse_image *image = NULL;
2260  unsigned short ifu = 0, /* we haven't worked with any IFU yet */
2261  ilist = 0; /* image index in the list */
2262  int ipt, npt = muse_pixtable_extracted_get_size(pts);
2263  for (ipt = 0; ipt < npt; ipt++) {
2264  float *cdata = cpl_table_get_data_float(pts[ipt]->table, MUSE_PIXTABLE_DATA),
2265  *cstat = cpl_table_get_data_float(pts[ipt]->table, MUSE_PIXTABLE_STAT);
2266  uint32_t *corigin = (uint32_t *)cpl_table_get_data_int(pts[ipt]->table,
2267  MUSE_PIXTABLE_ORIGIN);
2268  /* if we got to the next (or first) IFU, get an image from the list */
2269  if (ifu != muse_pixtable_origin_get_ifu_fast(corigin[0])) {
2270  image = muse_imagelist_get(aList, ilist++);
2271  } /* if ifu */
2272  if (!image) { /* it cannot really go wrong, but to be sure... */
2273  cpl_msg_error(__func__, "ipt = %d: no image!", ipt);
2274  continue;
2275  }
2276  float *idata = cpl_image_get_data_float(image->data),
2277  *istat = cpl_image_get_data_float(image->stat);
2278  ifu = muse_pixtable_origin_get_ifu_fast(corigin[0]);
2279  unsigned short slice = muse_pixtable_origin_get_slice_fast(corigin[0]);
2280  unsigned int xoff = muse_pixtable_origin_get_offset(pts[ipt], expnum, ifu,
2281  slice),
2282  irow, nrow = muse_pixtable_get_nrow(pts[ipt]);
2283  for (irow = 0; irow < nrow; irow++) {
2284  /* get coordinate indices from the origin column */
2285  unsigned int x = muse_pixtable_origin_get_x_fast(corigin[irow], xoff) - 1,
2286  y = muse_pixtable_origin_get_y_fast(corigin[irow]) - 1;
2287  cdata[irow] = idata[x + y*kMuseOutputXRight];
2288  cstat[irow] = istat[x + y*kMuseOutputXRight];
2289  } /* for irow */
2290  } /* for ipt */
2292 
2293  return CPL_ERROR_NONE;
2294 } /* muse_pixtable_from_imagelist() */
2295 
2296 /*---------------------------------------------------------------------------*/
2309 /*---------------------------------------------------------------------------*/
2310 muse_pixtable **
2312 {
2313  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, NULL);
2314  cpl_size n_rows = cpl_table_get_nrow(aPixtable->table);
2315  unsigned int ifu_slice_mask = (0x1f << MUSE_ORIGIN_SHIFT_IFU) | 0x3f;
2316  cpl_table_duplicate_column(aPixtable->table, "ifuslice",
2317  aPixtable->table, MUSE_PIXTABLE_ORIGIN);
2318  unsigned int *slicedata = (unsigned int *)
2319  cpl_table_get_data_int(aPixtable->table, "ifuslice");
2320  cpl_size i_row;
2321  unsigned int last_ifu_slice = 0;
2322  int is_sorted = CPL_TRUE;
2323  for (i_row = 0; i_row < n_rows; i_row++) {
2324  slicedata[i_row] &= ifu_slice_mask;
2325  if (is_sorted && slicedata[i_row] < last_ifu_slice) {
2326  is_sorted = CPL_FALSE;
2327  } else {
2328  last_ifu_slice = slicedata[i_row];
2329  }
2330  }
2331  if (!is_sorted) {
2332  cpl_propertylist *order = cpl_propertylist_new();
2333  cpl_propertylist_append_bool(order, "ifuslice", CPL_FALSE);
2334  cpl_propertylist_append_bool(order, MUSE_PIXTABLE_LAMBDA, CPL_FALSE);
2335  cpl_msg_debug(__func__, "sorting pixel table: quick sort, %"CPL_SIZE_FORMAT
2336  " entries", n_rows);
2337  cpl_table_sort(aPixtable->table, order);
2338  cpl_propertylist_delete(order);
2339  /* erase headers that depend on the order in the pixel table */
2340  cpl_propertylist_erase_regexp(aPixtable->header, MUSE_HDR_PT_EXP_REGEXP, 0);
2341  cpl_msg_debug(__func__, "pixel table sorted.");
2342  } /* if !sorted */
2343 
2344  i_row = 0;
2345  cpl_size n_col = cpl_table_get_ncol(aPixtable->table);
2346  cpl_array *colnames = cpl_table_get_column_names(aPixtable->table);
2347  muse_pixtable **slice_tables = cpl_calloc(1, sizeof(muse_pixtable *));
2348  cpl_size n_slices = 0;
2349  while (i_row < n_rows) {
2350  unsigned int ifu_slice = slicedata[i_row];
2351  cpl_size j_row;
2352  for (j_row = i_row+1; j_row < n_rows && slicedata[j_row] == ifu_slice;
2353  j_row++)
2354  ;
2355  cpl_size nrows_slice = j_row - i_row;
2356  muse_pixtable *slice_pixtable = cpl_calloc(1, sizeof(muse_pixtable));
2357  slice_pixtable->table = cpl_table_new(nrows_slice);
2358  cpl_size i_col;
2359  for (i_col = 0; i_col < n_col; i_col++) {
2360  const char *cname = cpl_array_get_string(colnames, i_col);
2361  if (strcmp(cname, "ifuslice") == 0)
2362  continue;
2363  cpl_type ctype = cpl_table_get_column_type(aPixtable->table, cname);
2364  if (ctype == CPL_TYPE_INT) {
2365  int *cdata = cpl_table_get_data_int(aPixtable->table, cname);
2366  cpl_table_wrap_int(slice_pixtable->table, cdata + i_row, cname);
2367  } else if (ctype == CPL_TYPE_FLOAT) {
2368  float *cdata = cpl_table_get_data_float(aPixtable->table, cname);
2369  cpl_table_wrap_float(slice_pixtable->table, cdata + i_row, cname);
2370  } else if (ctype == CPL_TYPE_DOUBLE) {
2371  double *cdata = cpl_table_get_data_double(aPixtable->table, cname);
2372  cpl_table_wrap_double(slice_pixtable->table, cdata + i_row, cname);
2373  } else if (ctype == CPL_TYPE_STRING) {
2374  char **cdata = cpl_table_get_data_string(aPixtable->table, cname);
2375  cpl_table_wrap_string(slice_pixtable->table, cdata + i_row, cname);
2376  }
2377  const char *unit = cpl_table_get_column_unit(aPixtable->table, cname);
2378  cpl_table_set_column_unit(slice_pixtable->table, cname, unit);
2379  } /* for i_col (all table columns) */
2380 
2381  slice_pixtable->header = cpl_propertylist_duplicate(aPixtable->header);
2382  muse_pixtable_compute_limits(slice_pixtable);
2383  slice_tables = cpl_realloc(slice_tables,
2384  (n_slices + 2) * sizeof(muse_pixtable *));
2385  slice_tables[n_slices] = slice_pixtable;
2386  n_slices++;
2387  slice_tables[n_slices] = NULL;
2388  i_row = j_row;
2389  } /* while (all full pixel table rows) */
2390  cpl_array_delete(colnames);
2391  cpl_table_erase_column(aPixtable->table, "ifuslice");
2392 
2393  return slice_tables;
2394 } /* muse_pixtable_extracted_get_slices() */
2395 
2396 /*---------------------------------------------------------------------------*/
2407 /*---------------------------------------------------------------------------*/
2408 cpl_size
2410 {
2411  cpl_ensure(aPixtables, CPL_ERROR_NULL_INPUT, -1);
2412  cpl_size n = 0;
2413  while (aPixtables[n] != NULL) {
2414  n++;
2415  }
2416  return n;
2417 } /* muse_pixtable_extracted_get_size() */
2418 
2419 /*---------------------------------------------------------------------------*/
2429 /*---------------------------------------------------------------------------*/
2430 void
2432 {
2433  if (!aPixtables) {
2434  return;
2435  }
2436  muse_pixtable **t;
2437  for (t = aPixtables; *t != NULL; t++) {
2438  cpl_array *colnames = cpl_table_get_column_names((*t)->table);
2439  cpl_size n_col = cpl_table_get_ncol((*t)->table);
2440  cpl_size i_col;
2441  for (i_col = 0; i_col < n_col; i_col++) {
2442  const char *cname = cpl_array_get_string(colnames, i_col);
2443  cpl_table_unwrap((*t)->table, cname);
2444  }
2445  cpl_array_delete(colnames);
2446  cpl_table_delete((*t)->table);
2447  cpl_propertylist_delete((*t)->header);
2448  cpl_free(*t);
2449  }
2450  cpl_free(aPixtables);
2451 } /* muse_pixtable_extracted_delete() */
2452 
cpl_polynomial ** muse_trace_table_get_polys_for_slice(const cpl_table *aTable, const unsigned short aSlice)
construct polynomial from the trace table entry for the given slice
muse_pixtable_wcs
State of the astrometric calibration of a MUSE pixel table.
unsigned int muse_pixtable_get_expnum(muse_pixtable *aPixtable, cpl_size aRow)
Get the exposure number of a given row in a pixel table.
cpl_error_code muse_wave_table_get_orders(const cpl_table *aWave, unsigned short *aXOrder, unsigned short *aYOrder)
Determine the x- and y-order of the polynomial stored in a wavelength calibration table...
Structure definition for a collection of muse_images.
int muse_trace_table_get_order(const cpl_table *aTable)
determine order of tracing polynomial from table
#define MUSE_HDR_PT_EXP_FST
FITS header keyword defining the first row index for a given exposure.
Definition: muse_pixtable.h:85
void muse_pixtable_extracted_delete(muse_pixtable **aPixtables)
Delete a pixel table array.
cpl_polynomial * muse_wave_table_get_poly_for_slice(const cpl_table *aTable, const unsigned short aSlice)
Construct polynomial from the wavelength calibration table entry for the given slice.
const char * muse_pfits_get_extname(const cpl_propertylist *aHeaders)
find out the extension name
Definition: muse_pfits.c:188
muse_pixtable * muse_pixtable_load(const char *aFilename)
Load the table itself and the FITS headers of a MUSE pixel table from a file.
unsigned short muse_pixtable_origin_get_slice(uint32_t aOrigin)
Get the slice number from the encoded 32bit origin number.
muse_pixtable * muse_pixtable_duplicate(muse_pixtable *aPixtable)
Make a copy of the pixtanle.
#define MUSE_HDR_PT_XLO
FITS header keyword contains the lower limit of the data in x-direction.
cpl_size muse_pixtable_extracted_get_size(muse_pixtable **aPixtables)
Get the size of an array of extracted pixel tables.
cpl_size muse_pixtable_get_nrow(muse_pixtable *aPixtable)
get the number of rows within the pixel table
unsigned int muse_pixtable_origin_set_offset(muse_pixtable *aPixtable, cpl_polynomial *aLTrace, unsigned short aIFU, unsigned short aSlice)
Set the slice offset from the pixel table header.
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
Definition: muse_utils.c:95
#define MUSE_HDR_PT_FLUXCAL
muse_pixtable ** muse_pixtable_extracted_get_slices(muse_pixtable *aPixtable)
Extract one pixel table per IFU and slice.
cpl_image * data
the data extension
Definition: muse_image.h:47
#define MUSE_HDR_PT_LHI
FITS header keyword contains the upper limit of the data in spectral direction.
#define MUSE_HDR_PT_ILO
FITS header keyword contains the lowest IFU number in the data.
int muse_pixtable_get_type(muse_pixtable *aPixtable)
Determine the type of pixel table.
#define MUSE_HDR_PT_EXP_LST
FITS header keyword defining the last row index for a given exposure.
Definition: muse_pixtable.h:90
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_error_code muse_pixtable_dump(muse_pixtable *aPixtable, cpl_size aStart, cpl_size aCount, unsigned char aDisplayHeader)
Dump a MUSE pixel table to the screen, resolving the origin column.
cpl_image * stat
the statistics extension
Definition: muse_image.h:65
#define MUSE_HDR_PT_TYPE
Pixel table "type" stored in the FITS header.
Definition: muse_pixtable.h:75
#define MUSE_HDR_PT_MERGED
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:41
cpl_table * table
The pixel table.
cpl_propertylist * header
the FITS header
Definition: muse_image.h:73
cpl_error_code muse_pixtable_origin_copy_offsets(muse_pixtable *aOut, muse_pixtable *aFrom, unsigned int aNum)
Copy MUSE_HDR_PT_IFU_SLICE_OFFSET keywords between pixel tables.
cpl_error_code muse_pixtable_reset_dq(muse_pixtable *aPixtable, unsigned int aDQ)
Reset a given bad pixel status (DQ flag) for all pixels in the table.
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.
muse_imagelist * muse_pixtable_to_imagelist(muse_pixtable *aPixtable)
Project a pixel table with data from one IFU back onto its image.
void muse_trace_polys_delete(cpl_polynomial *aPolys[])
Delete the multi-polynomial array created in relation to tracing.
#define MUSE_HDR_PT_SHI
FITS header keyword contains the highest slice number in the data.
cpl_image * dq
the data quality extension
Definition: muse_image.h:57
cpl_error_code muse_pixtable_restrict_wavelength(muse_pixtable *aPixtable, double aLow, double aHigh)
Restrict a pixel table to a certain wavelength range.
cpl_table * muse_cpltable_new(const muse_cpltable_def *aDef, cpl_size aLength)
Create an empty table according to the specified definition.
cpl_boolean muse_pixtable_is_skysub(muse_pixtable *aPixtable)
Determine whether the pixel table is sky subtracted.
unsigned int muse_pixtable_origin_get_x(uint32_t aOrigin, muse_pixtable *aPixtable, cpl_size aRow)
Get the horizontal coordinate from the encoded 32bit origin number.
Structure definition of MUSE pixel table.
#define MUSE_HDR_PT_IFU_SLICE_OFFSET
FITS header keyword for the horizontal slice offset on the CCD.
Definition: muse_pixtable.h:66
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
muse_pixtable_wcs muse_pixtable_wcs_check(muse_pixtable *aPixtable)
Check the state of the world coordinate system of a pixel table.
#define MUSE_HDR_PT_YLO
FITS header keyword contains the lower limit of the data in y-direction.
#define MUSE_HDR_PT_SKYSUB
cpl_boolean muse_pixtable_is_fluxcal(muse_pixtable *aPixtable)
Determine whether the pixel table is flux calibrated.
#define MUSE_HDR_PT_IHI
FITS header keyword contains the highest IFU number in the data.
unsigned int muse_pixtable_origin_get_offset(muse_pixtable *aPixtable, unsigned int aExpNum, unsigned short aIFU, unsigned short aSlice)
Get the slice offset from the pixel table header.
uint32_t muse_pixtable_origin_encode(unsigned int aX, unsigned int aY, unsigned short aIFU, unsigned short aSlice, unsigned int aOffset)
Encode the three CCD coordinates defining the origin of one MUSE pixel into a 32bit integer...
muse_pixtable * muse_pixtable_load_window(const char *aFilename, cpl_size aStart, cpl_size aNRows)
Load a range of rows from the table and all the FITS headers of a MUSE pixel table from a file...
muse_pixtable * muse_pixtable_create(muse_image *aImage, cpl_table *aTrace, cpl_table *aWave, cpl_table *aGeoTable)
Create the pixel table for one CCD.
muse_pixtable * muse_pixtable_load_restricted_wavelength(const char *aFilename, double aLambdaMin, double aLambdaMax)
Load a pixel table from file and cut down the wavelength range.
cpl_error_code muse_cplpropertylist_update_long_long(cpl_propertylist *aHeader, const char *aKeyword, cpl_size aValue)
Update an integer-like property irrespective of the real type.
#define MUSE_HDR_PT_LLO
FITS header keyword contains the lower limit of the data in spectral direction.
cpl_error_code muse_pixtable_save(muse_pixtable *aPixtable, const char *aFilename)
Save a MUSE pixel table to a file on disk.
unsigned short muse_pixtable_origin_get_ifu(uint32_t aOrigin)
Get the IFU number from the encoded 32bit origin number.
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.
cpl_error_code muse_pixtable_restrict_xpos(muse_pixtable *aPixtable, double aLo, double aHi)
Restrict a pixel table to a certain x coordinate range.
cpl_error_code muse_pixtable_from_imagelist(muse_pixtable *aPixtable, muse_imagelist *aList)
Get pixel table values back from a per-IFU imagelist.
Handling of "mask" files.
Definition: muse_mask.h:43
muse_imagelist * muse_imagelist_new(void)
Create a new (empty) MUSE image list.
const muse_cpltable_def muse_pixtable_def[]
MUSE pixel table definition.
cpl_table * muse_geo_table_extract_ifu(const cpl_table *aTable, const unsigned char aIFU)
Extract the part of a geometry table dealing with a given IFU.
Definition: muse_geo.c:233
#define MUSE_HDR_PT_SLO
FITS header keyword contains the lowest slice number in the data.
cpl_propertylist * header
the FITS header
Definition: muse_mask.h:56
Definition of a cpl table structure.
muse_image * muse_image_new(void)
Allocate memory for a new muse_image object.
Definition: muse_image.c:65
unsigned int muse_pixtable_origin_get_y(uint32_t aOrigin)
Get the vertical coordinate from the encoded 32bit origin number.
cpl_mask * mask
The mask data.
Definition: muse_mask.h:49
void muse_pixtable_delete(muse_pixtable *aPixtable)
Deallocate memory associated to a pixel table object.
cpl_error_code muse_pixtable_restrict_ypos(muse_pixtable *aPixtable, double aLo, double aHi)
Restrict a pixel table to a certain y coordinate range.
#define MUSE_HDR_PT_YHI
FITS header keyword contains the upper limit of the data in y-direction.
cpl_error_code muse_imagelist_set(muse_imagelist *aList, muse_image *aImage, unsigned int aIdx)
Set the muse_image of given list index.
cpl_error_code muse_pixtable_compute_limits(muse_pixtable *aPixtable)
(Re-)Compute the limits of the coordinate columns of a pixel table.
static cpl_error_code muse_pixtable_fix_exp_headers(muse_pixtable *aPixtable)
Fix the exposure ranges in the header of a pixel table.
cpl_propertylist * header
The FITS header.
#define MUSE_HDR_PT_XHI
FITS header keyword contains the upper limit of the data in x-ion.