MUSE Pipeline Reference Manual  1.0.2
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_quadrants.h"
41 #include "muse_quality.h"
42 #include "muse_tracing.h"
43 #include "muse_wavecalib.h"
44 #include "muse_wcs.h"
45 #include "muse_utils.h"
46 
47 /*----------------------------------------------------------------------------*
48  * Debugging Macros *
49  * Set these to 1 or higher for (lots of) debugging output *
50  *----------------------------------------------------------------------------*/
51 #define CREATE_MINIMAL_PIXTABLE 0 /* create a very small pixel table */
52 #define PIXTABLE_CREATE_CCDSIZED 1 /* start with pixel table sized like input image */
53 #define DEBUG_PIXTABLE_CREATION 0 /* some table dumps in muse_pixtable_create() */
54 #define DEBUG_PIXTABLE_FEW_SLICES 0 /* when doing quick pixel table creation for *
55  * debugging, set this to a value below 48 */
56 
57 /*----------------------------------------------------------------------------*/
95 /*----------------------------------------------------------------------------*/
96 
99 /* Let's use macros for the shift lengths we need */
100 #define MUSE_ORIGIN_SHIFT_XSLICE 24
101 #define MUSE_ORIGIN_SHIFT_YPIX 11
102 #define MUSE_ORIGIN_SHIFT_IFU 6
103 /* spacing between the left slice edge at the bottom of the slice *
104  * to the origin of the slice from where we could x positions within *
105  * the slice; this is to make sure that no pixel is left of the offset */
106 #define MUSE_ORIGIN_SLICE_SAFETY_OFFSET -20
107 
108 /* private functions without error checking */
110 static inline uint32_t
111 muse_pixtable_origin_encode_fast(unsigned int aX, unsigned int aY,
112  unsigned short aIFU,
113  unsigned short aSlice, unsigned int aOffset)
114 {
115  return ((aX - aOffset) << MUSE_ORIGIN_SHIFT_XSLICE)
116  | (aY << MUSE_ORIGIN_SHIFT_YPIX)
117  | (aIFU << MUSE_ORIGIN_SHIFT_IFU)
118  | aSlice;
119 }
120 
122 static inline unsigned int
123 muse_pixtable_origin_get_x_fast(uint32_t aOrigin, uint32_t aOffset)
124 {
125  return ((aOrigin >> MUSE_ORIGIN_SHIFT_XSLICE) & 0x7f) + aOffset;
126 }
127 
129 static inline unsigned int
130 muse_pixtable_origin_get_y_fast(uint32_t aOrigin)
131 {
132  return (aOrigin >> MUSE_ORIGIN_SHIFT_YPIX) & 0x1fff;
133 }
134 
136 static inline unsigned short
137 muse_pixtable_origin_get_ifu_fast(uint32_t aOrigin)
138 {
139  return (aOrigin >> MUSE_ORIGIN_SHIFT_IFU) & 0x1f;
140 }
141 
143 static inline unsigned short
144 muse_pixtable_origin_get_slice_fast(uint32_t aOrigin)
145 {
146  return aOrigin & 0x3f;
147 }
148 
149 /*---------------------------------------------------------------------------*/
165 /*---------------------------------------------------------------------------*/
166 uint32_t
167 muse_pixtable_origin_encode(unsigned int aX, unsigned int aY,
168  unsigned short aIFU,
169  unsigned short aSlice, unsigned int aOffset)
170  /* use the explicit 32bit return type so that *
171  * this also works on systems with other setups */
172 {
173  /* check for allowed values to fit into 32bit */
174  cpl_ensure(aX < 8192 && aX > 0 && aY < 8192 && aY > 0 &&
175  aIFU <= kMuseNumIFUs && aIFU >= 1 &&
176  aSlice <= kMuseSlicesPerCCD && aSlice >= 1 && aOffset < 8192,
177  CPL_ERROR_ILLEGAL_INPUT, 0);
178 
179 #if 0
180  cpl_msg_debug(__func__, "origin (%d, %d, %d, %d, %d) = 0x%x",
181  aX, aY, aIFU, aSlice, aOffset,
182  ((aX - aOffset) << MUSE_ORIGIN_SHIFT_XSLICE)
183  | (aY << MUSE_ORIGIN_SHIFT_YPIX)
184  | (aIFU << MUSE_ORIGIN_SHIFT_IFU)
185  | aSlice);
186 #endif
187 
188  /* do the bit-shifting "magic" */
189  return muse_pixtable_origin_encode_fast(aX, aY, aIFU, aSlice, aOffset);
190 } /* muse_pixtable_origin_encode() */
191 
192 /*---------------------------------------------------------------------------*/
210 /*---------------------------------------------------------------------------*/
211 unsigned int
212 muse_pixtable_origin_get_x(uint32_t aOrigin, muse_pixtable *aPixtable,
213  cpl_size aRow)
214 {
215  unsigned short slice = muse_pixtable_origin_get_slice_fast(aOrigin),
216  ifu = muse_pixtable_origin_get_ifu_fast(aOrigin);
217  cpl_errorstate prestate = cpl_errorstate_get();
218  unsigned int expnum = muse_pixtable_get_expnum(aPixtable, aRow);
219  if (!cpl_errorstate_is_equal(prestate)) {
220  cpl_errorstate_set(prestate);
221  }
222  unsigned int offset = muse_pixtable_origin_get_offset(aPixtable, expnum, ifu,
223  slice),
224  x = muse_pixtable_origin_get_x_fast(aOrigin, offset);
225 #if 0
226  if (x > 8191 || x < 1 || !cpl_errorstate_is_equal(prestate)) {
227  cpl_msg_error(__func__, "aOrigin=%#x x=%d (%d %d %d), %s",
228  aOrigin, x, slice, ifu, offset, cpl_error_get_message());
229  }
230 #endif
231  cpl_ensure(x <= 8191 && x >= 1 && cpl_errorstate_is_equal(prestate),
232  CPL_ERROR_ILLEGAL_OUTPUT, 0);
233  return x;
234 } /* muse_pixtable_origin_get_x() */
235 
236 /*---------------------------------------------------------------------------*/
244 /*---------------------------------------------------------------------------*/
245 unsigned int
246 muse_pixtable_origin_get_y(uint32_t aOrigin)
247 {
248  unsigned int y = muse_pixtable_origin_get_y_fast(aOrigin);
249 #if 0
250  if (y > 8191 || y < 1) {
251  cpl_msg_error(__func__, "aOrigin=%#x y=%d", aOrigin, y);
252  }
253 #endif
254  cpl_ensure(y <= 8191 && y >= 1, CPL_ERROR_ILLEGAL_OUTPUT, 0);
255  return y;
256 } /* muse_pixtable_origin_get_y() */
257 
258 /*---------------------------------------------------------------------------*/
266 /*---------------------------------------------------------------------------*/
267 unsigned short
269 {
270  unsigned short ifu = muse_pixtable_origin_get_ifu_fast(aOrigin);
271 #if 0
272  if (ifu > kMuseNumIFUs || ifu < 1) {
273  cpl_msg_error(__func__, "aOrigin=%#x ifu=%d", aOrigin, ifu);
274  }
275 #endif
276  cpl_ensure(ifu <= kMuseNumIFUs && ifu >= 1, CPL_ERROR_ILLEGAL_OUTPUT, 0);
277  return ifu;
278 } /* muse_pixtable_origin_get_ifu() */
279 
280 /*---------------------------------------------------------------------------*/
288 /*---------------------------------------------------------------------------*/
289 unsigned short
291 {
292  unsigned short slice = muse_pixtable_origin_get_slice_fast(aOrigin);
293 #if 0
294  if (slice > kMuseSlicesPerCCD || slice < 1) {
295  cpl_msg_error(__func__, "aOrigin=%#x slice=%d", aOrigin, slice);
296  }
297 #endif
298  cpl_ensure(slice <= kMuseSlicesPerCCD && slice >= 1,
299  CPL_ERROR_ILLEGAL_OUTPUT, 0);
300  return slice;
301 } /* muse_pixtable_origin_get_slice() */
302 
303 /*---------------------------------------------------------------------------*/
318 /*---------------------------------------------------------------------------*/
319 unsigned int
321  cpl_polynomial *aLTrace,
322  unsigned short aIFU, unsigned short aSlice)
323 {
324  cpl_ensure(aPixtable && aPixtable->header, CPL_ERROR_NULL_INPUT, 0);
325  cpl_errorstate prestate = cpl_errorstate_get();
326  unsigned int offset = floor(cpl_polynomial_eval_1d(aLTrace, 1, NULL))
327  + MUSE_ORIGIN_SLICE_SAFETY_OFFSET;
328  cpl_ensure(cpl_errorstate_is_equal(prestate), cpl_error_get_code(), 0);
329  /* use exposure number 0 here, this is set only later, *
330  * when merging pixel tables of different exposures */
331  char *keyword = cpl_sprintf(MUSE_HDR_PT_IFU_SLICE_OFFSET, 0, aIFU, aSlice);
332  cpl_propertylist_update_int(aPixtable->header, keyword, offset);
333  cpl_propertylist_set_comment(aPixtable->header, keyword,
334  MUSE_HDR_PT_IFU_SLICE_OFFSET_COMMENT);
335  cpl_free(keyword);
336  return offset;
337 } /* muse_pixtable_origin_set_offset() */
338 
339 /*---------------------------------------------------------------------------*/
353 /*---------------------------------------------------------------------------*/
354 unsigned int
355 muse_pixtable_origin_get_offset(muse_pixtable *aPixtable, unsigned int aExpNum,
356  unsigned short aIFU, unsigned short aSlice)
357 {
358  cpl_ensure(aPixtable && aPixtable->header, CPL_ERROR_NULL_INPUT, 0);
359  char *keyword = cpl_sprintf(MUSE_HDR_PT_IFU_SLICE_OFFSET, aExpNum, aIFU,
360  aSlice);
361  cpl_errorstate prestate = cpl_errorstate_get();
362  unsigned int offset = cpl_propertylist_get_int(aPixtable->header, keyword);
363  cpl_free(keyword);
364 #if 0
365  if (offset > 8191 || offset < 1) {
366  cpl_msg_error(__func__, "aIFU=%d aSlice=%d offset=%d",
367  aIFU, aSlice, offset);
368  }
369 #endif
370  cpl_ensure(offset <= 8191 && offset >= 1 && cpl_errorstate_is_equal(prestate),
371  CPL_ERROR_ILLEGAL_OUTPUT, 0);
372  return offset;
373 } /* muse_pixtable_origin_get_offset() */
374 
375 /*---------------------------------------------------------------------------*/
395 /*---------------------------------------------------------------------------*/
396 cpl_error_code
398  unsigned int aNum)
399 {
400  cpl_ensure_code(aOut && aOut->header, CPL_ERROR_NULL_INPUT);
401  cpl_propertylist *dest = aOut->header,
402  *from = aOut->header;
403  if (aFrom && aFrom->header) {
404  from = aFrom->header;
405  }
406  char keyword[KEYWORD_LENGTH];
407  unsigned short nifu, nslice;
408  for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
409  for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
410  /* keyword to copy */
411  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_IFU_SLICE_OFFSET,
412  0, nifu, nslice);
413  cpl_errorstate prestate = cpl_errorstate_get();
414  unsigned int offset = cpl_propertylist_get_int(from, keyword);
415  if (!cpl_errorstate_is_equal(prestate)) {
416  /* this one was not set, skip to the next one */
417  cpl_errorstate_set(prestate);
418  continue;
419  }
420  /* erase old keyword */
421  cpl_propertylist_erase(from, keyword);
422  /* add new keyword */
423  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_IFU_SLICE_OFFSET,
424  aNum, nifu, nslice);
425  cpl_propertylist_update_int(dest, keyword, offset);
426  cpl_propertylist_set_comment(dest, keyword,
427  MUSE_HDR_PT_IFU_SLICE_OFFSET_COMMENT);
428  } /* for nslice */
429  } /* for nifu */
430 
431  return CPL_ERROR_NONE;
432 } /* muse_pixtable_origin_copy_offsets() */
433 
434 /*---------------------------------------------------------------------------*/
449 /*---------------------------------------------------------------------------*/
450 unsigned int
451 muse_pixtable_get_expnum(muse_pixtable *aPixtable, cpl_size aRow)
452 {
453  cpl_ensure(aPixtable && aPixtable->header, CPL_ERROR_NULL_INPUT, 0);
454  cpl_ensure(aRow >= 0 && muse_pixtable_get_nrow(aPixtable) > aRow,
455  CPL_ERROR_ILLEGAL_INPUT, 0);
456 
457  char keyword[KEYWORD_LENGTH];
458  unsigned int exposure = 0;
459  cpl_size lo = 0, hi = 0;
460  do {
461  cpl_errorstate prestate = cpl_errorstate_get();
462  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST, ++exposure);
463  lo = cpl_propertylist_get_long_long(aPixtable->header, keyword);
464  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST, exposure);
465  hi = cpl_propertylist_get_long_long(aPixtable->header, keyword);
466  if (!cpl_errorstate_is_equal(prestate)) {
467  if (exposure == 1) {
468  /* no exposure headers in the table at all, so it's either the *
469  * only one or the table was sorted, destroying exposure info */
470  lo = hi = aRow;
471  exposure = 0; /* signify the problem to the caller */
472  }
473  cpl_errorstate_set(prestate);
474  break;
475  }
476  } while (hi < aRow);
477  cpl_ensure(lo <= aRow && hi >= aRow, CPL_ERROR_ILLEGAL_OUTPUT, 0);
478  return exposure;
479 } /* muse_pixtable_get_expnum */
480 
481 /*----------------------------------------------------------------------------*/
501 /*----------------------------------------------------------------------------*/
503  { MUSE_PIXTABLE_XPOS, CPL_TYPE_FLOAT, "pix", "%7.2f",
504  "relative x-pixel position in the output datacube", CPL_TRUE},
505  { MUSE_PIXTABLE_YPOS, CPL_TYPE_FLOAT, "pix", "%7.2f",
506  "relative y-pixel position in the output datacube", CPL_TRUE},
507  { MUSE_PIXTABLE_LAMBDA, CPL_TYPE_FLOAT, "Angstrom", "%8.2f",
508  "wavelength of this pixel", CPL_TRUE},
509  { MUSE_PIXTABLE_DATA, CPL_TYPE_FLOAT, "count", "%e",
510  "data value in this pixel", CPL_TRUE},
511  { MUSE_PIXTABLE_DQ, CPL_TYPE_INT, NULL, "%#x",
512  "Euro3D bad pixel status of this pixel", CPL_TRUE},
513  { MUSE_PIXTABLE_STAT, CPL_TYPE_FLOAT, "count**2", "%e",
514  "data variance of this pixel", CPL_TRUE},
515  { MUSE_PIXTABLE_ORIGIN, CPL_TYPE_INT, NULL, "0x%08x",
516  "encoded value of IFU and slice number, as well as x and y "
517  "position in the raw (trimmed) data", CPL_TRUE},
518  { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
519 };
520 
521 /*---------------------------------------------------------------------------*/
567 /*---------------------------------------------------------------------------*/
569 muse_pixtable_create(muse_image *aImage, cpl_table *aTrace, cpl_table *aWave,
570  cpl_table *aGeoTable)
571 {
572  cpl_ensure(aImage && aWave && aTrace, CPL_ERROR_NULL_INPUT, NULL);
573  /* get and check IFU number */
574  unsigned char ifu = muse_utils_get_ifu(aImage->header);
575  cpl_ensure(ifu >= 1 && ifu <= kMuseNumIFUs, CPL_ERROR_DATA_NOT_FOUND, NULL);
576 
577  /* check that the input data is in count units */
578  if (!cpl_propertylist_has(aImage->header, "BUNIT")) {
579  char *msg = cpl_sprintf("Input data of IFU %hhu does not contain a data "
580  "unit (\"BUNIT\"), cannot proceed!", ifu);
581  cpl_msg_error(__func__, "%s", msg);
582  cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "%s", msg);
583  cpl_free(msg);
584  return NULL;;
585  }
586  const char *unit = cpl_propertylist_get_string(aImage->header, "BUNIT");
587  if (strncmp(unit, "count", 6)) {
588  char *msg = cpl_sprintf("Input data of IFU %hhu is not in \"count\" units "
589  "but in \"%s\", not suitable for a new pixel table!",
590  ifu, unit);
591  cpl_msg_error(__func__, "%s", msg);
592  cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT, "%s", msg);
593  cpl_free(msg);
594  return NULL;
595  }
596 
597  /* create the output pixel table structure, and NULL out the components */
598  muse_pixtable *pt = cpl_calloc(1, sizeof(muse_pixtable));
599  /* compute initial table size */
600  int xsize = cpl_image_get_size_x(aImage->data),
601  ysize = cpl_image_get_size_y(aImage->data),
602 #if PIXTABLE_CREATE_CCDSIZED
603  /* There cannot be more pixels needed in the table than in the *
604  * input image, so with this we can remove the overflow check below. */
605  isize = xsize * ysize;
606 #else
607  /* It's very likely that no more pixels than these are needed. But then *
608  * we need to check for overflows below (and still cut it in the end). */
609  isize = ysize * kMuseSlicesPerCCD * kMuseSliceHiLikelyWidth;
610 #endif
611  pt->table = muse_cpltable_new(muse_pixtable_def, isize);
612 
613  /* copy the header from the input image to the pixel table, *
614  * but remove some keywords that could cause trouble */
615  pt->header = cpl_propertylist_duplicate(aImage->header);
616  cpl_propertylist_erase_regexp(pt->header,
617  "^SIMPLE$|^BITPIX$|^NAXIS|^EXTEND$|^XTENSION$|"
618  "^DATASUM$|^DATAMIN$|^DATAMAX$|^DATAMD5$|"
619  "^PCOUNT$|^GCOUNT$|^HDUVERS$|^BLANK$|"
620  "^BZERO$|^BSCALE$|^BUNIT$|^CHECKSUM$|^INHERIT$|"
621  MUSE_HDR_OVSC_REGEXP"|"MUSE_WCS_KEYS, 0);
622  cpl_propertylist_append_string(pt->header, MUSE_HDR_PT_TYPE,
623  aGeoTable ? MUSE_PIXTABLE_STRING_FULL
624  : MUSE_PIXTABLE_STRING_SIMPLE);
625  cpl_propertylist_set_comment(pt->header, MUSE_HDR_PT_TYPE,
626  aGeoTable ? MUSE_PIXTABLE_COMMENT_FULL
627  : MUSE_PIXTABLE_COMMENT_SIMPLE);
628 
629  /* make all table cells valid by filling them with zeros; this is a workaround *
630  * suggested by Carlo Izzo to be able to set the table cells using array *
631  * access instead of the slower and non-threadsafe cpl_table_set() function */
632  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_XPOS, 0, isize, 0);
633  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_YPOS, 0, isize, 0);
634  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_LAMBDA, 0, isize, 0);
635  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_DATA, 0, isize, 0);
636  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_STAT, 0, isize, 0);
637  cpl_table_fill_column_window_int(pt->table, MUSE_PIXTABLE_DQ, 0, isize, 0);
638  cpl_table_fill_column_window_int(pt->table, MUSE_PIXTABLE_ORIGIN, 0, isize, 0);
639 
640  /* get columns as pointers */
641  float *cdata_xpos = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_XPOS),
642  *cdata_ypos = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_YPOS),
643  *cdata_lambda = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_LAMBDA),
644  *cdata_data = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_DATA),
645  *cdata_stat = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_STAT);
646  int *cdata_dq = cpl_table_get_data_int(pt->table, MUSE_PIXTABLE_DQ);
647  uint32_t *cdata_origin = (uint32_t *)cpl_table_get_data_int(pt->table,
648  MUSE_PIXTABLE_ORIGIN);
649 
650  /* get input image buffers as pointers, too */
651  const float *pixdata = cpl_image_get_data_float_const(aImage->data),
652  *pixstat = cpl_image_get_data_float_const(aImage->stat);
653  const int *pixdq = cpl_image_get_data_int_const(aImage->dq);
654 
655  /* get columns of the wavecal table */
656  unsigned short wavexorder, waveyorder;
657  muse_wave_table_get_orders(aWave, &wavexorder, &waveyorder);
658  cpl_msg_info(__func__, "Creating pixel table for IFU %hhu, using order %d for "
659  "trace solution and orders %hu/%hu for wavelength solution", ifu,
660  muse_trace_table_get_order(aTrace), wavexorder, waveyorder);
661 
662  /* get position corresponding to the IFU number */
663  cpl_table *geopos = NULL;
664  double *slice_x = NULL, *slice_y = NULL, *slice_angle = NULL,
665  *slice_width = NULL;
666  if (aGeoTable) {
667  geopos = muse_geo_table_extract_ifu(aGeoTable, ifu);
668  slice_x = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_X);
669  slice_y = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_Y);
670  /* fail on missing x/y as they are critical */
671  if (!geopos || !slice_x || !slice_y) {
672  char *msg = !geopos
673  ? cpl_sprintf("Geometry table is missing data for IFU %hhu!", ifu)
674  : cpl_sprintf("Geometry table is missing column%s%s%s!",
675  !slice_x && !slice_y ? "s" : "",
676  slice_x ? "" : " \""MUSE_GEOTABLE_X"\"",
677  slice_y ? "" : " \""MUSE_GEOTABLE_Y"\"");
678  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "%s", msg);
679  cpl_msg_error(__func__, "%s", msg);
680  cpl_free(msg);
681  cpl_table_delete(geopos);
683  return NULL;
684  }
685  /* only output warnings for other missing columns, they *
686  * default to usable values below, ignore CPL errors */
687  cpl_errorstate prestate = cpl_errorstate_get();
688  slice_angle = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_ANGLE);
689  slice_width = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_WIDTH);
690  if (!cpl_errorstate_is_equal(prestate)) {
691  char *msg = cpl_sprintf("Geometry table is missing column%s%s%s!",
692  !slice_angle && !slice_width ? "s" : "",
693  slice_angle ? "" : " \""MUSE_GEOTABLE_ANGLE"\"",
694  slice_width ? "" : " \""MUSE_GEOTABLE_WIDTH"\"");
695  cpl_error_set_message(__func__, CPL_ERROR_BAD_FILE_FORMAT, "%s", msg);
696  cpl_msg_error(__func__, "%s", msg);
697  cpl_free(msg);
698  if (!getenv("MUSE_EXPERT_USER")) {
699  cpl_table_delete(geopos);
701  return NULL;
702  }
703  } /* if */
704  } /* if aGeoTable */
705 
706  /* this counts the current table row that is being entered */
707  cpl_size itablerow = 0;
708  /* time the operation for this IFU */
709  double cputime = cpl_test_get_cputime(),
710  walltime = cpl_test_get_walltime();
711  /* loop through all slices */
712  int islice;
713 #if DEBUG_PIXTABLE_FEW_SLICES > 0
714 # define SLICE_LIMIT DEBUG_PIXTABLE_FEW_SLICES
715 #else
716 # define SLICE_LIMIT kMuseSlicesPerCCD
717 #endif
718  for (islice = 0; islice < SLICE_LIMIT; islice++) {
719 #if DEBUG_PIXTABLE_CREATION /* check how many pixels extend over the nominal width */
720  cpl_msg_debug(__func__, "Starting to process slice %2d of IFU %2hhu",
721  islice + 1, ifu);
722 #endif
723 
724  /* fill the wavelength calibration polynomial for this slice */
725  cpl_polynomial *pwave = muse_wave_table_get_poly_for_slice(aWave, islice + 1);
726  /* vector for the position within the slice (for evaluation *
727  * of the wavelength solution in two dimensions */
728  cpl_vector *pos = cpl_vector_new(2);
729 
730  /* test evaluate the wavelength polynomial to see if it's valid */
731  cpl_vector_set(pos, 0, kMuseOutputYTop / 2);
732  cpl_vector_set(pos, 1, kMuseOutputYTop / 2);
733  double ltest = cpl_polynomial_eval(pwave, pos);
734  if (!pwave || !isnormal(ltest) || fabs(ltest) < DBL_EPSILON) {
735  char *msg = cpl_sprintf("Wavelength calibration polynomial for slice %d "
736  "of IFU %hhu is not well defined!", islice + 1,
737  ifu);
738  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "%s", msg);
739  cpl_msg_error(__func__, "%s", msg);
740  cpl_free(msg);
741  cpl_polynomial_delete(pwave);
742  cpl_vector_delete(pos);
743  if (getenv("MUSE_EXPERT_USER")) {
744  continue; /* go the next slice */
745  } else { /* give up completely */
746  cpl_table_delete(geopos);
748  return NULL;
749  }
750  } /* if bad wavecal polynomial */
751 
752  /* get the tracing polynomials for this slice */
753  cpl_polynomial **ptrace = muse_trace_table_get_polys_for_slice(aTrace,
754  islice + 1);
755  if (!ptrace) {
756  char *msg = cpl_sprintf("Tracing polynomials for slice %d of IFU %hhu are"
757  " not well defined!", islice + 1, ifu);
758  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "%s", msg);
759  cpl_msg_error(__func__, "%s", msg);
760  cpl_free(msg);
761  cpl_polynomial_delete(pwave);
762  cpl_vector_delete(pos);
763  if (getenv("MUSE_EXPERT_USER")) {
764  continue; /* go the next slice */
765  } else { /* give up completely */
766  cpl_table_delete(geopos);
768  return NULL;
769  }
770  } /* if no tracing polynomials */
771  unsigned offset = muse_pixtable_origin_set_offset(pt,
772  ptrace[MUSE_TRACE_LEFT],
773  ifu, islice + 1);
774 
775  /* within each slice, loop from bottom to top */
776  int j;
777  for (j = 1; j <= ysize; j++) {
778  /* compute slice center for this vertical position and set the *
779  * edge pixels of slice, so that we can loop over those pixels */
780  double x1 = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT], j, NULL),
781  x2 = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT], j, NULL),
782  xcenter = (x1 + x2) / 2.,
783  width = x2 - x1;
784  if (!isnormal(x1) || !isnormal(x2) || x1 < 1 || x2 > xsize || x1 > x2) {
785  cpl_msg_warning(__func__, "slice %2d of IFU %hhu: faulty polynomial "
786  "detected at y=%d (borders: %f ... %f)", islice + 1,
787  ifu, j, x1, x2);
788  break; /* skip the rest of this slice */
789  }
790  /* include pixels that have more than half of them inside the slice */
791  int ileft = ceil(x1),
792  iright = floor(x2);
793  cpl_vector_set(pos, 1, j); /* vertical pos. for wavelength evaluation */
794 
795  /* now loop over all pixels of this slice horizontally */
796  int i;
797  for (i = ileft; i <= iright; i++) {
798  cpl_vector_set(pos, 0, i); /* horiz. pos. for wavelength evaluation */
799 
800  /* do the wavelength evaluation early on, to more quickly *
801  * recover from errors */
802  double lambda = cpl_polynomial_eval(pwave, pos);
803  if (lambda < 3000. || lambda > 11000. || !isfinite(lambda)) {
804  continue; /* skip this pixel */
805  }
806 
807  /* Relative horizontal pixel position within slice; has to be scaled *
808  * by the width of the slice on the CCD. Slice width is measured *
809  * horizontally, not along iso-lambda lines, the difference for *
810  * straight lines is below 0.015 pix (0.02%), and only slightly *
811  * larger for curved lines, so this simplification should not have a *
812  * negative effect on anything. We compute (xcenter - i) to account *
813  * for the reversed sky orientation within each slice. */
814  float dx = (xcenter - i) / width
815  * (slice_width ? slice_width[islice] : kMuseSliceNominalWidth);
816 #if DEBUG_PIXTABLE_CREATION /* check how many pixels extend over the nominal width */
817  if (fabs(dx) > 37.5) {
818  cpl_msg_debug(__func__, "%d - %f -> %f", i, xcenter, dx);
819  }
820 #endif
821 
822 #if CREATE_MINIMAL_PIXTABLE
823  /* if we need to create a very small pixel table for debugging */
824  if (lambda < 6495. || lambda > 6505. || fabs(dx) > 10.) {
825  continue;
826  }
827 #endif
828 
829  /* this pixel is set up to be inside the slice, write it to the table */
830  cpl_size irow = itablerow++; /* started at 0, so increment after assignment */
831 #if !PIXTABLE_CREATE_CCDSIZED /* overflow check, and possibly enlarge the table */
832  if (irow + 1 > (cpl_size)muse_pixtable_get_nrow(pt)) {
833 #if DEBUG_PIXTABLE_CREATION /* make sure that we are filling the right part */
834  printf("pixel table before enlargement:\n");
835  cpl_table_dump(pt->table, irow-5, 10, stdout);
836  fflush(stdout);
837 #endif
838  /* If the table is too small, we can as well add a lot more rows *
839  * right now, than adding them in small portions. If we add too many *
840  * this will only marginally decrease the speed of this procedure */
841  const cpl_size nr = 1000000;
842  cpl_msg_debug(__func__, "expand table to %"CPL_SIZE_FORMAT" rows "
843  "(add %"CPL_SIZE_FORMAT" lines)!", irow + nr, nr);
844  cpl_table_set_size(pt->table, irow + nr);
845 
846  /* again fill the new section of each column */
847  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_XPOS, irow, nr, 0);
848  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_YPOS, irow, nr, 0);
849  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_LAMBDA, irow, nr, 0);
850  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_DATA, irow, nr, 0);
851  cpl_table_fill_column_window_float(pt->table, MUSE_PIXTABLE_STAT, irow, nr, 0);
852  cpl_table_fill_column_window_int(pt->table, MUSE_PIXTABLE_DQ, irow, nr, 0);
853  cpl_table_fill_column_window_int(pt->table, MUSE_PIXTABLE_ORIGIN, irow, nr, 0);
854 
855  /* re-get columns as pointers, the memory location might have changed */
856  cdata_xpos = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_XPOS);
857  cdata_ypos = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_YPOS);
858  cdata_lambda = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_LAMBDA);
859  cdata_data = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_DATA);
860  cdata_stat = cpl_table_get_data_float(pt->table, MUSE_PIXTABLE_STAT);
861  cdata_dq = cpl_table_get_data_int(pt->table, MUSE_PIXTABLE_DQ);
862  cdata_origin = (uint32_t *)cpl_table_get_data_int(pt->table,
863  MUSE_PIXTABLE_ORIGIN);
864 #if DEBUG_PIXTABLE_CREATION /* make sure that we have filled the right part */
865  printf("pixel table after filling new rows:\n");
866  cpl_table_dump(pt->table, irow-5, 10, stdout);
867  fflush(stdout);
868 #endif
869  } /* if new rows needed */
870 #endif /* !PIXTABLE_CREATE_CCDSIZED */
871 
872  /* Assign the coordinates (slice angle is in degrees). */
873  double angle = slice_angle ? slice_angle[islice] * CPL_MATH_RAD_DEG
874  : 0.;
875  cdata_xpos[irow] = aGeoTable
876  ? dx * cos(angle) + slice_x[islice]
877  : dx + islice * kMuseSliceHiLikelyWidth
878  + kMuseSliceHiLikelyWidth / 2.;
879  cdata_ypos[irow] = aGeoTable
880  ? dx * sin(angle) + slice_y[islice]
881  : 0.; /* no vertical position for simple case */
882 #if DEBUG_PIXTABLE_CREATION
883  if (!isfinite(cdata_xpos[irow]) ||
884  cdata_xpos[irow] < -200 || cdata_xpos[irow] > 4000) {
885  cpl_msg_debug(__func__, "weird data in x: %e %e",
886  cdata_xpos[irow], cdata_ypos[irow]);
887  }
888  if (!isfinite(cdata_ypos[irow]) ||
889  cdata_ypos[irow] < -200 || cdata_ypos[irow] > 200) {
890  cpl_msg_debug(__func__, "weird data in y: %e %e",
891  cdata_xpos[irow], cdata_ypos[irow]);
892  }
893 #endif
894  cdata_lambda[irow] = lambda;
895 
896  /* assign the data values */
897  cdata_data[irow] = pixdata[(i-1) + (j-1)*xsize];
898  cdata_dq[irow] = pixdq[(i-1) + (j-1)*xsize];
899  cdata_stat[irow] = pixstat[(i-1) + (j-1)*xsize];
900  /* assign origin and slice number */
901  cdata_origin[irow] = muse_pixtable_origin_encode_fast(i, j, ifu,
902  islice + 1,
903  offset);
904  } /* for i (horizontal pixels) */
905  } /* for j (vertical direction) */
906 
907  /* we are now done with this slice, clean up */
908  muse_trace_polys_delete(ptrace);
909  cpl_polynomial_delete(pwave);
910  cpl_vector_delete(pos);
911  } /* for islice */
912  cpl_table_delete(geopos);
913  cpl_msg_debug(__func__, "IFU %hhu took %gs (CPU time), %gs (wall-clock)",
914  ifu, cpl_test_get_cputime() - cputime,
915  cpl_test_get_walltime() - walltime);
916 
917  /* itablerow is the last written to location (starting at zero), so add one */
918  if (muse_pixtable_get_nrow(pt) > itablerow) {
919  cpl_msg_debug(__func__, "Trimming pixel table of IFU %hhu to %"CPL_SIZE_FORMAT
920  " of %"CPL_SIZE_FORMAT" rows", ifu, itablerow,
922 #if DEBUG_PIXTABLE_CREATION /* make sure that we are trimming correctly */
923  printf("end of used part of pixel table before trimming:\n");
924  cpl_table_dump(pt->table, itablerow-5, 10, stdout);
925  fflush(stdout);
926 #endif
927  cpl_table_set_size(pt->table, itablerow);
928  }
929  /* now that we are done creating it, we can also *
930  * compute its extremes for the first time */
932 #if DEBUG_PIXTABLE_CREATION
933  printf("beginning of pixel table before returning:\n");
934  cpl_table_dump(pt->table, 0, 5, stdout);
935  fflush(stdout);
936  printf("end of pixel table before returning:\n");
937  cpl_table_dump(pt->table, muse_pixtable_get_nrow(pt)-5, 10, stdout);
938  fflush(stdout);
939 #endif
940 
941  return pt;
942 } /* muse_pixtable_create() */
943 
944 /*---------------------------------------------------------------------------*/
957 /*---------------------------------------------------------------------------*/
960 {
961  if (aPixtable == NULL) {
962  return NULL;
963  }
964  muse_pixtable *pt = cpl_calloc(1, sizeof(muse_pixtable));
965  pt->table = cpl_table_duplicate(aPixtable->table);
966  pt->header = cpl_propertylist_duplicate(aPixtable->header);
967  return pt;
968 }
969 
970 /*---------------------------------------------------------------------------*/
979 /*---------------------------------------------------------------------------*/
980 void
982 {
983  /* if we already get NULL, we don't need to do anything */
984  if (!aPixtable) {
985  return;
986  }
987 
988  /* delete the table portion */
989  cpl_table_delete(aPixtable->table);
990  aPixtable->table = NULL;
991 
992  /* delete the header portion */
993  cpl_propertylist_delete(aPixtable->header);
994  aPixtable->header = NULL;
995 
996  cpl_free(aPixtable);
997 } /* muse_pixtable_delete() */
998 
999 /*---------------------------------------------------------------------------*/
1020 /*---------------------------------------------------------------------------*/
1021 static cpl_error_code
1022 muse_pixtable_save_image(muse_pixtable *aPixtable, const char *aFilename)
1023 {
1024  const char *id = "muse_pixtable_save"; /* pretend to be in that function */
1025  cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
1026  cpl_size nrow = muse_pixtable_get_nrow(aPixtable);
1027  cpl_ensure_code(nrow > 0, CPL_ERROR_ILLEGAL_INPUT);
1028 
1029  cpl_errorstate state = cpl_errorstate_get();
1030  cpl_array *columns = cpl_table_get_column_names(aPixtable->table);
1031  int icol, ncol = cpl_array_get_size(columns);
1032  for (icol = 0; icol < ncol; icol++) {
1033  const char *colname = cpl_array_get_string(columns, icol);
1034  /* access data buffer and wrap as corresponding CPL image type */
1035  cpl_type type = cpl_table_get_column_type(aPixtable->table, colname);
1036  cpl_image *image = NULL;
1037  switch (type) {
1038  case CPL_TYPE_FLOAT: {
1039  float *data = cpl_table_get_data_float(aPixtable->table, colname);
1040  image = cpl_image_wrap_float(1, nrow, data);
1041  break;
1042  }
1043  case CPL_TYPE_INT: {
1044  int *data = cpl_table_get_data_int(aPixtable->table, colname);
1045  image = cpl_image_wrap_int(1, nrow, data);
1046  break;
1047  }
1048  default:
1049  cpl_error_set_message(id, CPL_ERROR_UNSUPPORTED_MODE, "type \"%s\" (of "
1050  "column %s) is not supported for MUSE pixel tables",
1051  cpl_type_get_name(type), colname);
1052  continue;
1053  } /* switch */
1054  /* now construct minimal extension header with EXTNAME and *
1055  * BUNIT and save the image with its internal data type */
1056  cpl_propertylist *hext = cpl_propertylist_new();
1057  cpl_propertylist_append_string(hext, "EXTNAME", colname);
1058  const char *unit = cpl_table_get_column_unit(aPixtable->table, colname);
1059  if (unit) {
1060  cpl_propertylist_append_string(hext, "BUNIT", unit);
1061  }
1062  cpl_image_save(image, aFilename, CPL_TYPE_UNSPECIFIED, hext, CPL_IO_EXTEND);
1063  cpl_image_unwrap(image);
1064  cpl_propertylist_delete(hext);
1065  } /* for icol (all table columns) */
1066  cpl_array_delete(columns);
1067  return cpl_errorstate_is_equal(state) ? CPL_ERROR_NONE : cpl_error_get_code();
1068 } /* muse_pixtable_save_image() */
1069 
1070 /*---------------------------------------------------------------------------*/
1091 /*---------------------------------------------------------------------------*/
1092 cpl_error_code
1093 muse_pixtable_save(muse_pixtable *aPixtable, const char *aFilename)
1094 {
1095  cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
1096 
1097  /* let CPL do the real work and propagate any errors and errorstates that *
1098  * it generates; the secondary header should be generated automatically *
1099  * from the table data we have set in muse_pixtable_create(). */
1100  /* save headers separately to keep WCS information */
1101  cpl_error_code rc = cpl_propertylist_save(aPixtable->header, aFilename,
1102  CPL_IO_CREATE);
1103  if (rc != CPL_ERROR_NONE) {
1104  cpl_error_set_message(__func__, rc, "could not save FITS header of pixel "
1105  "table \"%s\"", aFilename);
1106  return rc;
1107  }
1108  /* test if we need to save it as image */
1109  cpl_boolean astable = getenv("MUSE_PIXTABLE_SAVE_AS_TABLE")
1110  && atoi(getenv("MUSE_PIXTABLE_SAVE_AS_TABLE")) > 0;
1111  if (astable) {
1112  cpl_msg_debug(__func__, "Saving pixel table \"%s\" as binary table",
1113  aFilename);
1114  rc = cpl_table_save(aPixtable->table, NULL, NULL, aFilename, CPL_IO_EXTEND);
1115  } else {
1116  rc = muse_pixtable_save_image(aPixtable, aFilename);
1117  }
1118  return rc;
1119 } /* muse_pixtable_save() */
1120 
1121 /*---------------------------------------------------------------------------*/
1143 /*---------------------------------------------------------------------------*/
1144 static cpl_table *
1145 muse_pixtable_load_window_image(const char *aFilename,
1146  cpl_size aStart, cpl_size aNRows)
1147 {
1148  const char *id = "muse_pixtable_load"; /* pretend to be in that function */
1149  /* determine "image" window to read, the y-direction *
1150  * in the image is the dimension of the rows */
1151  cpl_size y1 = aStart + 1,
1152  y2 = aStart + aNRows;
1153  /* get header of "data" extension, and check/correct the size of the dataset */
1154  int ext = cpl_fits_find_extension(aFilename, MUSE_PIXTABLE_DATA);
1155  cpl_propertylist *hdata = cpl_propertylist_load(aFilename, ext);
1156  /* get NAXIS2 which contains contains the number of rows */
1157  cpl_size naxis2 = muse_pfits_get_naxis(hdata, 2);
1158  if (y2 > naxis2) {
1159  y2 = naxis2;
1160  }
1161  cpl_propertylist_delete(hdata);
1162 
1163  /* find and load all image extensions using their EXTNAMEs */
1164  cpl_size nrow = 0;
1165  cpl_table *table = cpl_table_new(nrow);
1166  int iext, next = cpl_fits_count_extensions(aFilename);
1167  for (iext = 1; iext <= next; iext++) {
1168  cpl_errorstate ps = cpl_errorstate_get();
1169  cpl_image *column = cpl_image_load_window(aFilename, CPL_TYPE_UNSPECIFIED,
1170  0, iext, 1, y1, 1, y2);
1171  if (!column || !cpl_errorstate_is_equal(ps)) {
1172  cpl_image_delete(column);
1173  cpl_error_set_message(id, cpl_error_get_code(), "could not load extension"
1174  " %d of pixel table \"%s\"", iext, aFilename);
1175  continue;
1176  }
1177  cpl_propertylist *hext = cpl_propertylist_load(aFilename, iext);
1178  const char *colname = muse_pfits_get_extname(hext);
1179  cpl_size nrows = cpl_image_get_size_x(column)
1180  * cpl_image_get_size_y(column);
1181  if (nrow < 1) {
1182  cpl_table_set_size(table, nrows);
1183  nrow = nrows;
1184  } else if (nrows != nrow) {
1185  cpl_error_set_message(id, CPL_ERROR_INCOMPATIBLE_INPUT, "size of column "
1186  "%s does not match", colname);
1187  cpl_propertylist_delete(hext);
1188  cpl_image_delete(column);
1189  continue;
1190  }
1191  cpl_type type = cpl_image_get_type(column);
1192  switch (type) {
1193  case CPL_TYPE_FLOAT:
1194  cpl_table_wrap_float(table, cpl_image_unwrap(column), colname);
1195  break;
1196  case CPL_TYPE_INT:
1197  cpl_table_wrap_int(table, cpl_image_unwrap(column), colname);
1198  break;
1199  default:
1200  cpl_error_set_message(id, CPL_ERROR_UNSUPPORTED_MODE, "type \"%s\" (of "
1201  "column %s) is not supported for MUSE pixel tables",
1202  cpl_type_get_name(type), colname);
1203  } /* switch */
1204  cpl_errorstate state = cpl_errorstate_get();
1205  const char *unit = cpl_propertylist_get_string(hext, "BUNIT");
1206  if (!cpl_errorstate_is_equal(state)) {
1207  cpl_errorstate_set(state);
1208  }
1209  if (unit) {
1210  cpl_table_set_column_unit(table, colname, unit);
1211  }
1212  cpl_propertylist_delete(hext);
1213  } /* for iext (all FITS extensions) */
1214  return table;
1215 } /* muse_pixtable_load_window_image() */
1216 
1217 /*---------------------------------------------------------------------------*/
1236 /*---------------------------------------------------------------------------*/
1237 muse_pixtable *
1238 muse_pixtable_load_window(const char *aFilename,
1239  cpl_size aStart, cpl_size aNRows)
1240 {
1241  /* create the output pixel table structure, and NULL out the components */
1242  muse_pixtable *pt = cpl_calloc(1, sizeof(muse_pixtable));
1243 
1244  /* load the header and return with an error if something went wrong */
1245  cpl_errorstate prestate = cpl_errorstate_get();
1246  pt->header = cpl_propertylist_load(aFilename, 0);
1247  cpl_ensure(cpl_errorstate_is_equal(prestate) && pt->header,
1248  cpl_error_get_code(), NULL);
1250  cpl_msg_error(__func__, "unknown pixel table type found in \"%s\"", aFilename);
1252  return NULL;
1253  }
1254 
1255  /* determine the type of data (FITS binary table extension or *
1256  * FITS multi-extension images) by looking at the first extension */
1257  cpl_propertylist *hext = cpl_propertylist_load(aFilename, 1);
1258  cpl_boolean asimage = !strcmp(cpl_propertylist_get_string(hext, "XTENSION"),
1259  "IMAGE");
1260  cpl_propertylist_delete(hext);
1261 
1262  /* load the table itself and return an error if something went wrong */
1263  if (asimage) {
1264  cpl_msg_info(__func__, "Loading pixel table \"%s\" (image format)",
1265  aFilename);
1266  pt->table = muse_pixtable_load_window_image(aFilename, aStart, aNRows);
1267  } else {
1268  cpl_msg_info(__func__, "Loading pixel table \"%s\" (bintable format)",
1269  aFilename);
1270  pt->table = cpl_table_load_window(aFilename, 1, 0, NULL, aStart, aNRows);
1271  }
1272  cpl_ensure(cpl_errorstate_is_equal(prestate) && pt->table,
1273  cpl_error_get_code(), NULL);
1274  cpl_error_code rc = muse_cpltable_check(pt->table, muse_pixtable_def);
1275  if (rc != CPL_ERROR_NONE) {
1276  cpl_error_set_message(__func__, rc, "pixel table \"%s\" does not contain "
1277  "all expected columns", aFilename);
1278  }
1279  return pt;
1280 } /* muse_pixtable_load_window() */
1281 
1282 /*---------------------------------------------------------------------------*/
1302 /*---------------------------------------------------------------------------*/
1303 muse_pixtable *
1304 muse_pixtable_load(const char *aFilename)
1305 {
1306  /* find out original table length from the header of the FITS binary table */
1307  cpl_errorstate prestate = cpl_errorstate_get();
1308  cpl_propertylist *theader = cpl_propertylist_load(aFilename, 1);
1309  cpl_ensure(cpl_errorstate_is_equal(prestate) && theader,
1310  cpl_error_get_code(), NULL);
1311  cpl_size nrow = cpl_propertylist_get_long_long(theader, "NAXIS2");
1312  cpl_propertylist_delete(theader);
1313 
1314  /* now that we know the full size, load all rows */
1315  muse_pixtable *pt = muse_pixtable_load_window(aFilename, 0, nrow);
1316  return pt;
1317 } /* muse_pixtable_load() */
1318 
1319 /*---------------------------------------------------------------------------*/
1336 /*---------------------------------------------------------------------------*/
1337 muse_pixtable *
1339  double aLambdaMin, double aLambdaMax)
1340 {
1341  muse_pixtable *pt = muse_pixtable_load(aFilename);
1342  if (!pt) {
1343  return NULL;
1344  }
1345  cpl_error_code rc = muse_pixtable_restrict_wavelength(pt, aLambdaMin,
1346  aLambdaMax);
1347  if (rc != CPL_ERROR_NONE) {
1349  return NULL;
1350  }
1351  if (muse_pixtable_get_nrow(pt) < 1) {
1352  cpl_msg_error(__func__, "Pixel table contains no entries after cutting to "
1353  "%.3f..%.3f Angstrom", aLambdaMin, aLambdaMax);
1354  cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
1356  return NULL;
1357  }
1358  return pt;
1359 } /* muse_pixtable_load_restricted_wavelength() */
1360 
1361 /*---------------------------------------------------------------------------*/
1395 /*---------------------------------------------------------------------------*/
1396 muse_pixtable *
1397 muse_pixtable_load_merge_channels(cpl_table *aExposureList,
1398  double aLambdaMin, double aLambdaMax)
1399 {
1400  cpl_ensure(aExposureList, CPL_ERROR_NULL_INPUT, NULL);
1401 
1402  muse_pixtable *pt = NULL; /* the big output pixel table */
1403  if (cpl_table_has_column(aExposureList, "00")) {
1404  const char *filename = cpl_table_get_string(aExposureList, "00", 0);
1405  if (filename) {
1406  pt = muse_pixtable_load_restricted_wavelength(filename, aLambdaMin,
1407  aLambdaMax);
1408  }
1409  if (pt) {
1410  return pt;
1411  }
1412  } /* if table with "00" column */
1413  cpl_boolean isfirst = CPL_TRUE;
1414  double fluxlref = 0, /* propagated from lamp flat */
1415  fluxsref = 0; /* propagated from sky flat */
1416  int i, nifu = 0;
1417  for (i = 1; i <= kMuseNumIFUs; i++) {
1418  char *colname = cpl_sprintf("%02d", i);
1419  const char *filename = cpl_table_get_string(aExposureList, colname, 0);
1420  cpl_free(colname);
1421  if (!filename) {
1422  cpl_msg_warning(__func__, "Channel for IFU %02d is missing", i);
1423  continue;
1424  }
1426  aLambdaMin,
1427  aLambdaMax);
1428  if (!onept) {
1429  cpl_msg_error(__func__, "failed to load pixel table from \"%s\"",
1430  filename);
1431  return pt;
1432  }
1433  nifu++;
1434 
1435  /* if this is the first pixel table, use its data to create the big one */
1436  if (isfirst) {
1437  pt = onept;
1438  cpl_msg_debug(__func__, "loaded pixel table with %"CPL_SIZE_FORMAT" rows",
1440  isfirst = CPL_FALSE;
1441  cpl_errorstate prestate = cpl_errorstate_get();
1442  fluxsref = cpl_propertylist_get_double(pt->header, MUSE_HDR_FLAT_FLUX_SKY);
1443  fluxlref = cpl_propertylist_get_double(pt->header, MUSE_HDR_FLAT_FLUX_LAMP);
1444  if (fluxsref == 0. && fluxlref == 0. && !cpl_errorstate_is_equal(prestate)) {
1445  /* If the flat headers are both missing, this can only mean that *
1446  * the exposure was previously merged into the pixel table we just *
1447  * loaded. Then we can recover from the error and stop right here. */
1448  cpl_msg_debug(__func__, "\"%s\" was previously merged (got \"%s\" when"
1449  " asking for flat-field fluxes)", filename,
1450  cpl_error_get_message());
1451  cpl_errorstate_set(prestate);
1452  break;
1453  }
1454  if (fluxsref == 0. && fluxlref > 0. && !cpl_errorstate_is_equal(prestate)) {
1455  /* only the sky-flat level was missing, output a warning */
1456  cpl_msg_warning(__func__, "only found reference lamp-flat flux (%e) in "
1457  "\"%s\", flux levels may vary between IFUs!", fluxlref,
1458  filename);
1459  cpl_errorstate_set(prestate);
1460  } else {
1461  cpl_msg_debug(__func__, "reference flat fluxes sky: %e lamp: %e",
1462  fluxsref, fluxlref);
1463  }
1464  cpl_propertylist_erase(pt->header, MUSE_HDR_FLAT_FLUX_SKY);
1465  cpl_propertylist_erase(pt->header, MUSE_HDR_FLAT_FLUX_LAMP);
1466  continue;
1467  } /* if isfirst */
1468 
1469  /* copy the offset headers to the output pixel table */
1470  muse_pixtable_origin_copy_offsets(pt, onept, 0);
1471 
1472  /* to fix the flux offset (using propagated flat-field fluxes) */
1473  cpl_errorstate state = cpl_errorstate_get();
1474  double fluxs = cpl_propertylist_get_double(onept->header,
1475  MUSE_HDR_FLAT_FLUX_SKY),
1476  fluxl = cpl_propertylist_get_double(onept->header,
1477  MUSE_HDR_FLAT_FLUX_LAMP),
1478  scale = 1.;
1479  if (fluxsref > 0. && fluxs > 0.) { /* prefer sky flat flux scaling */
1480  scale = fluxs / fluxsref;
1481  } else if (fluxlref > 0. && fluxl > 0.) {
1482  scale = fluxl / fluxlref;
1483  if (!cpl_errorstate_is_equal(state)) {
1484  cpl_msg_warning(__func__, "only found relative lamp-flat flux (%e) in "
1485  "\"%s\", flux levels may vary between IFUs!", fluxl,
1486  filename);
1487  cpl_errorstate_set(state);
1488  } /* if bad error state */
1489  } /* else: use lamp-flat values */
1490  cpl_table_divide_scalar(onept->table, MUSE_PIXTABLE_DATA, scale);
1491  cpl_table_divide_scalar(onept->table, MUSE_PIXTABLE_STAT, scale*scale);
1492 
1493  /* append this pixel table to the big one */
1494  cpl_table_insert(pt->table, onept->table, muse_pixtable_get_nrow(pt));
1495  cpl_msg_debug(__func__, "big pixel table now has %"CPL_SIZE_FORMAT" entries,"
1496  " scale was %e (flat fluxes sky: %e lamp: %e)",
1497  muse_pixtable_get_nrow(pt), scale, fluxs, fluxl);
1498 
1499  /* the insertion duplicates the data, so we can delete it now */
1500  muse_pixtable_delete(onept);
1501  } /* for i (all IFUs) */
1503  if (!pt) {
1504  cpl_error_set_message(__func__, CPL_ERROR_FILE_NOT_FOUND,
1505  "None of the pixel tables could be loaded");
1506  return NULL; /* exit here to not crash below! */
1507  }
1508  /* data does not belong to any one IFU any more, so *
1509  * remove the EXTNAME header that contains CHAN%02d */
1510  cpl_propertylist_erase_regexp(pt->header, "^EXTNAME|"MUSE_HDR_PT_ILLUM_REGEXP,
1511  0);
1512  /* also remove chip-specific info that doesn't belong to the merged data */
1513  cpl_propertylist_erase_regexp(pt->header, "ESO DET (CHIP|OUT) ", 0);
1514  cpl_propertylist_erase_regexp(pt->header, "ESO DET2 ", 0);
1515  /* add the status header */
1516  cpl_propertylist_update_int(pt->header, MUSE_HDR_PT_MERGED, nifu);
1517  cpl_propertylist_set_comment(pt->header, MUSE_HDR_PT_MERGED,
1518  MUSE_HDR_PT_MERGED_COMMENT);
1519  return pt;
1520 } /* muse_pixtable_load_merge_channels() */
1521 
1522 /*---------------------------------------------------------------------------*/
1534 /*---------------------------------------------------------------------------*/
1535 int
1537 {
1538  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, MUSE_PIXTABLE_TYPE_UNKNOWN);
1539  const char *type = cpl_propertylist_get_string(aPixtable->header,
1541 #if 0
1542  cpl_msg_debug(__func__, "pixel table type \"%s\"", type);
1543 #endif
1544  if (!type) {
1546  }
1547  if (!strncmp(type, MUSE_PIXTABLE_STRING_FULL,
1548  strlen(MUSE_PIXTABLE_STRING_FULL) + 1)) {
1549  return MUSE_PIXTABLE_TYPE_FULL;
1550  } else if (!strncmp(type, MUSE_PIXTABLE_STRING_SIMPLE,
1551  strlen(MUSE_PIXTABLE_STRING_SIMPLE) + 1)) {
1553  }
1554 
1556 } /* muse_pixtable_get_type() */
1557 
1558 /*---------------------------------------------------------------------------*/
1567 /*---------------------------------------------------------------------------*/
1568 cpl_size
1570 {
1571  /* make sure that we are getting valid input */
1572  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, 0);
1573  cpl_ensure(aPixtable->table, CPL_ERROR_NULL_INPUT, 0);
1574 
1575  /* let CPL do the work */
1576  return cpl_table_get_nrow(aPixtable->table);
1577 } /* muse_pixtable_get_nrow() */
1578 
1579 /*---------------------------------------------------------------------------*/
1595 /*---------------------------------------------------------------------------*/
1596 cpl_error_code
1598 {
1599  /* make sure that we are getting valid input */
1600  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1601  CPL_ERROR_NULL_INPUT);
1602  cpl_ensure_code(muse_cpltable_check(aPixtable->table, muse_pixtable_def)
1603  == CPL_ERROR_NONE, CPL_ERROR_DATA_NOT_FOUND);
1604 
1605  if (muse_pixtable_get_nrow(aPixtable) == 0) {
1606  return CPL_ERROR_NONE;
1607  }
1608 
1609  float *cdata_xpos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_XPOS),
1610  *cdata_ypos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_YPOS),
1611  *cdata_lambda = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA);
1612  uint32_t *origin = (uint32_t *)cpl_table_get_data_int(aPixtable->table,
1613  MUSE_PIXTABLE_ORIGIN);
1614  /* set to extreme values for a start */
1615  float xlo = FLT_MAX, xhi = -FLT_MAX,
1616  ylo = FLT_MAX, yhi = -FLT_MAX,
1617  llo = FLT_MAX, lhi = -FLT_MAX;
1618  int ifulo = INT_MAX, ifuhi = 0,
1619  slicelo = INT_MAX, slicehi = 0;
1620  cpl_size i, nrow = muse_pixtable_get_nrow(aPixtable);
1621  for (i = 0; i < nrow; i++) {
1622  if (cdata_xpos[i] > xhi) xhi = cdata_xpos[i];
1623  if (cdata_xpos[i] < xlo) xlo = cdata_xpos[i];
1624  if (cdata_ypos[i] > yhi) yhi = cdata_ypos[i];
1625  if (cdata_ypos[i] < ylo) ylo = cdata_ypos[i];
1626  if (cdata_lambda[i] > lhi) lhi = cdata_lambda[i];
1627  if (cdata_lambda[i] < llo) llo = cdata_lambda[i];
1628  int ifu = muse_pixtable_origin_get_ifu_fast(origin[i]),
1629  slice = muse_pixtable_origin_get_slice_fast(origin[i]);
1630  if (ifu > ifuhi) ifuhi = ifu;
1631  if (ifu < ifulo) ifulo = ifu;
1632  if (slice > slicehi) slicehi = slice;
1633  if (slice < slicelo) slicelo = slice;
1634  } /* for i */
1635  char *dodebug = getenv("MUSE_DEBUG_PIXTABLE_LIMITS");
1636  if (dodebug && atoi(dodebug)) {
1637  cpl_msg_debug(__func__, "x: %f...%f, y: %f...%f, lambda: %f...%f, "
1638  "ifu: %d...%d, slice: %d...%d",
1639  xlo, xhi, ylo, yhi, llo, lhi, ifulo, ifuhi, slicelo, slicehi);
1640  }
1641  cpl_propertylist_erase_regexp(aPixtable->header, MUSE_HDR_PT_LIMITS_REGEXP, 0);
1642  double ptxoff = 0., /* zero by default ... */
1643  ptyoff = 0.; /* for pixel coordinates */
1645  ptxoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL1");
1646  ptyoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL2");
1647  }
1648  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_XLO, xlo + ptxoff);
1649  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_XHI, xhi + ptxoff);
1650  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_YLO, ylo + ptyoff);
1651  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_YHI, yhi + ptyoff);
1652  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_LLO, llo);
1653  cpl_propertylist_append_float(aPixtable->header, MUSE_HDR_PT_LHI, lhi);
1654  cpl_propertylist_append_int(aPixtable->header, MUSE_HDR_PT_ILO, ifulo);
1655  cpl_propertylist_append_int(aPixtable->header, MUSE_HDR_PT_IHI, ifuhi);
1656  cpl_propertylist_append_int(aPixtable->header, MUSE_HDR_PT_SLO, slicelo);
1657  cpl_propertylist_append_int(aPixtable->header, MUSE_HDR_PT_SHI, slicehi);
1658 
1659  return CPL_ERROR_NONE;
1660 } /* muse_pixtable_compute_limits() */
1661 
1662 /*---------------------------------------------------------------------------*/
1672 /*---------------------------------------------------------------------------*/
1673 static cpl_error_code
1675 {
1676  cpl_ensure_code(aPixtable && aPixtable->header && aPixtable->table,
1677  CPL_ERROR_NULL_INPUT);
1678  if (cpl_table_count_selected(aPixtable->table) < 1) {
1679  return CPL_ERROR_NONE; /* nothing to do */
1680  }
1681  cpl_array *sel = cpl_table_where_selected(aPixtable->table);
1682  cpl_size narray = cpl_array_get_size(sel),
1683  nselprev = 0, /* selected ones in previous exposures */
1684  ifst = 0, ilst = 0;
1685  const cpl_size *asel = cpl_array_get_data_cplsize_const(sel);
1686  unsigned int nexp = 0;
1687  do {
1688  /* get exposure range for (next) exposure */
1689  char *kwfst = cpl_sprintf(MUSE_HDR_PT_EXP_FST, ++nexp),
1690  *kwlst = cpl_sprintf(MUSE_HDR_PT_EXP_LST, nexp);
1691  if (!cpl_propertylist_has(aPixtable->header, kwfst) ||
1692  !cpl_propertylist_has(aPixtable->header, kwlst)) {
1693  cpl_free(kwfst);
1694  cpl_free(kwlst);
1695  break; /* nothing (more) to do */
1696  }
1697  ifst = cpl_propertylist_get_long_long(aPixtable->header, kwfst);
1698  ilst = cpl_propertylist_get_long_long(aPixtable->header, kwlst);
1699  /* count selected */
1700  cpl_size i, nsel = 0;
1701  for (i = 0; i < narray; i++) {
1702  if (asel[i] >= ifst && asel[i] <= ilst) {
1703  nsel++;
1704  } /* if in range of this exposure */
1705  } /* for i (all array entries) */
1706  cpl_size ifst2 = ifst - nselprev,
1707  ilst2 = ilst - nsel - nselprev;
1708  cpl_msg_debug(__func__, "exp %d old %"CPL_SIZE_FORMAT"..%"CPL_SIZE_FORMAT
1709  ", %"CPL_SIZE_FORMAT" selected (previous: %"CPL_SIZE_FORMAT
1710  "), new %"CPL_SIZE_FORMAT"..%"CPL_SIZE_FORMAT, nexp,
1711  ifst, ilst, nsel, nselprev, ifst2, ilst2);
1712  /* finally update the header entries */
1713  muse_cplpropertylist_update_long_long(aPixtable->header, kwfst, ifst2);
1714  muse_cplpropertylist_update_long_long(aPixtable->header, kwlst, ilst2);
1715  cpl_free(kwfst);
1716  cpl_free(kwlst);
1717  nselprev += nsel; /* add selected ones for previous exposures */
1718  } while (ilst >= ifst);
1719  cpl_array_delete(sel);
1720  return CPL_ERROR_NONE;
1721 } /* muse_pixtable_fix_exp_headers() */
1722 
1723 /*---------------------------------------------------------------------------*/
1732 /*---------------------------------------------------------------------------*/
1733 cpl_error_code
1735  double aHigh)
1736 {
1737  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1738  CPL_ERROR_NULL_INPUT);
1739  if (cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_LLO) > aLow &&
1740  cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_LHI) < aHigh) {
1741  /* no need to do anything */
1742  return CPL_ERROR_NONE;
1743  }
1744 #pragma omp critical(cpl_table_select)
1745  {
1746  cpl_table_unselect_all(aPixtable->table);
1747  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA,
1748  CPL_LESS_THAN, aLow);
1749  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA,
1750  CPL_GREATER_THAN, aHigh);
1751  muse_pixtable_fix_exp_headers(aPixtable);
1752  cpl_table_erase_selected(aPixtable->table);
1753  }
1754  return muse_pixtable_compute_limits(aPixtable);
1755 } /* muse_pixtable_restrict_wavelength() */
1756 
1757 /*---------------------------------------------------------------------------*/
1768 /*---------------------------------------------------------------------------*/
1769 cpl_error_code
1770 muse_pixtable_restrict_xpos(muse_pixtable *aPixtable, double aLo, double aHi)
1771 {
1772  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1773  CPL_ERROR_NULL_INPUT);
1774  if (cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_XLO) > aLo &&
1775  cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_XHI) < aHi) {
1776  /* no need to do anything */
1777  return CPL_ERROR_NONE;
1778  }
1779  double ptxoff = 0.;
1781  /* need to use the real coordinate offset for celestial spherical */
1782  ptxoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL1");
1783  }
1784 #pragma omp critical(cpl_table_select)
1785  {
1786  cpl_table_unselect_all(aPixtable->table);
1787  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_XPOS,
1788  CPL_LESS_THAN, aLo - ptxoff);
1789  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_XPOS,
1790  CPL_GREATER_THAN, aHi - ptxoff);
1791  muse_pixtable_fix_exp_headers(aPixtable);
1792  cpl_table_erase_selected(aPixtable->table);
1793  }
1794  return muse_pixtable_compute_limits(aPixtable);
1795 } /* muse_pixtable_restrict_xpos() */
1796 
1797 /*---------------------------------------------------------------------------*/
1808 /*---------------------------------------------------------------------------*/
1809 cpl_error_code
1810 muse_pixtable_restrict_ypos(muse_pixtable *aPixtable, double aLo, double aHi)
1811 {
1812  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1813  CPL_ERROR_NULL_INPUT);
1814  if (cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_YLO) > aLo &&
1815  cpl_propertylist_get_float(aPixtable->header, MUSE_HDR_PT_YHI) < aHi) {
1816  /* no need to do anything */
1817  return CPL_ERROR_NONE;
1818  }
1819  double ptyoff = 0.;
1821  /* need to use the real coordinate offset for celestial spherical */
1822  ptyoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL2");
1823  }
1824 #pragma omp critical(cpl_table_select)
1825  {
1826  cpl_table_unselect_all(aPixtable->table);
1827  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_YPOS,
1828  CPL_LESS_THAN, aLo - ptyoff);
1829  cpl_table_or_selected_float(aPixtable->table, MUSE_PIXTABLE_YPOS,
1830  CPL_GREATER_THAN, aHi - ptyoff);
1831  muse_pixtable_fix_exp_headers(aPixtable);
1832  cpl_table_erase_selected(aPixtable->table);
1833  }
1834  return muse_pixtable_compute_limits(aPixtable);
1835 } /* muse_pixtable_restrict_ypos() */
1836 
1837 /*---------------------------------------------------------------------------*/
1851 /*---------------------------------------------------------------------------*/
1852 cpl_error_code
1853 muse_pixtable_erase_ifu_slice(muse_pixtable *aPixtable, unsigned char aIFU,
1854  unsigned short aSlice)
1855 {
1856  cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
1857  cpl_size nrow = muse_pixtable_get_nrow(aPixtable);
1858  cpl_ensure_code(nrow > 0, CPL_ERROR_DATA_NOT_FOUND);
1859 
1860  cpl_table_unselect_all(aPixtable->table);
1861  uint32_t *origin = (uint32_t *)cpl_table_get_data_int(aPixtable->table,
1862  MUSE_PIXTABLE_ORIGIN);
1863  cpl_size irow;
1864  for (irow = 0; irow < nrow; irow++) {
1865  unsigned char ifu = muse_pixtable_origin_get_ifu(origin[irow]);
1866  unsigned short slice = muse_pixtable_origin_get_slice(origin[irow]);
1867  if (ifu == aIFU && slice == aSlice) {
1868  cpl_table_select_row(aPixtable->table, irow);
1869  } /* if same IFU and slice */
1870  } /* for irow (all pixtable rows) */
1871  cpl_size nsel = cpl_table_count_selected(aPixtable->table);
1872  cpl_error_code rc = cpl_table_erase_selected(aPixtable->table);
1873  cpl_msg_debug(__func__, "Erased %"CPL_SIZE_FORMAT" rows from pixel table",
1874  nsel);
1875 
1876  muse_pixtable_fix_exp_headers(aPixtable);
1877  muse_pixtable_compute_limits(aPixtable);
1878  return rc;
1879 } /* muse_pixtable_erase_ifu_slice() */
1880 
1881 /*---------------------------------------------------------------------------*/
1898 /*---------------------------------------------------------------------------*/
1899 cpl_error_code
1901 {
1902  cpl_ensure_code(aPixtable && aPixtable->table, CPL_ERROR_NULL_INPUT);
1903  cpl_ensure_code(aMask && aMask->mask, CPL_ERROR_NULL_INPUT);
1904  float *cdata_xpos = cpl_table_get_data_float(aPixtable->table,
1905  MUSE_PIXTABLE_XPOS);
1906  float *cdata_ypos = cpl_table_get_data_float(aPixtable->table,
1907  MUSE_PIXTABLE_YPOS);
1908  cpl_size n_rows = cpl_table_get_nrow(aPixtable->table);
1909  cpl_size i_row;
1910  double crval_x = 0;
1911  double crpix_x = 1;
1912  double cdelt_x = 1;
1913  double crval_y = 0;
1914  double crpix_y = 1;
1915  double cdelt_y = 1;
1916  if (aMask->header) {
1917  crval_x = cpl_propertylist_get_double(aMask->header, "CRVAL1");
1918  crpix_x = cpl_propertylist_get_double(aMask->header, "CRPIX1");
1919  cdelt_x = cpl_propertylist_get_double(aMask->header, "CD1_1");
1920  crval_y = cpl_propertylist_get_double(aMask->header, "CRVAL2");
1921  crpix_y = cpl_propertylist_get_double(aMask->header, "CRPIX2");
1922  cdelt_y = cpl_propertylist_get_double(aMask->header, "CD2_2");
1923  }
1924  cpl_size nx = cpl_mask_get_size_x(aMask->mask);
1925  cpl_size ny = cpl_mask_get_size_y(aMask->mask);
1926  cpl_size n_enabled = cpl_mask_count(aMask->mask);
1927  cpl_msg_debug(__func__, "Mask contains %"CPL_SIZE_FORMAT" (%.2f %%) enabled "
1928  "pixels of %"CPL_SIZE_FORMAT" total", n_enabled,
1929  100.*n_enabled/nx/ny, nx*ny);
1930  cpl_size n_sel = n_rows;
1931  cpl_size n_in_table = 0;
1932  for (i_row = 0; i_row < n_rows; i_row++) {
1933  cpl_size ix = lround((cdata_xpos[i_row] - crval_x) / cdelt_x + crpix_x);
1934  cpl_size iy = lround((cdata_ypos[i_row] - crval_y) / cdelt_y + crpix_y);
1935  if ((ix < 1) || (ix > nx) || (iy < 1) || (iy > ny)) {
1936  continue;
1937  }
1938  n_in_table++;
1939  if (cpl_mask_get(aMask->mask, ix, iy) != CPL_BINARY_1) {
1940  cpl_table_unselect_row(aPixtable->table, i_row);
1941  n_sel--;
1942  }
1943  }
1944  cpl_msg_debug(__func__, "Mask selected %"CPL_SIZE_FORMAT" (%.2f %%/%.2f %%) "
1945  "pixels of %"CPL_SIZE_FORMAT" total/%"CPL_SIZE_FORMAT" in mask "
1946  "area", n_sel, 100.*n_sel/n_rows, 100.*n_sel/n_in_table,
1947  n_rows, n_in_table);
1948  return CPL_ERROR_NONE;
1949 } /* muse_pixtable_and_selected_mask() */
1950 
1951 /*---------------------------------------------------------------------------*/
1978 /*---------------------------------------------------------------------------*/
1979 cpl_error_code
1980 muse_pixtable_dump(muse_pixtable *aPixtable, cpl_size aStart, cpl_size aCount,
1981  unsigned char aDisplayHeader)
1982 {
1983  cpl_ensure_code(aPixtable && aPixtable->table && aPixtable->header,
1984  CPL_ERROR_NULL_INPUT);
1985  cpl_size nrows = muse_pixtable_get_nrow(aPixtable);
1986  cpl_ensure_code(aStart >= 0 && aStart < nrows && aCount >= 0,
1987  CPL_ERROR_ILLEGAL_INPUT);
1988  cpl_size last = aStart + aCount;
1989  if (last > nrows - 1) {
1990  last = nrows;
1991  }
1992  int haswcs = muse_pixtable_wcs_check(aPixtable);
1993  double ptxoff = 0., ptyoff = 0.;
1994  if (haswcs == MUSE_PIXTABLE_WCS_CELSPH) {
1995  /* need to use the real coordinate offset for celestial spherical */
1996  ptxoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL1");
1997  ptyoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL2");
1998  }
1999  /* for non-pixel coordinates, we will need different formats below */
2000  haswcs = (haswcs == MUSE_PIXTABLE_WCS_NATSPH ||
2001  haswcs == MUSE_PIXTABLE_WCS_CELSPH);
2002  float *cdata_xpos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_XPOS),
2003  *cdata_ypos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_YPOS),
2004  *cdata_lambda = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA),
2005  *cdata_data = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_DATA),
2006  *cdata_stat = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_STAT);
2007  cpl_errorstate es = cpl_errorstate_get();
2008  float *cdata_weight = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_WEIGHT);
2009  cpl_errorstate_set(es); /* ignore errors due to missing weight column */
2010  int *cdata_dq = cpl_table_get_data_int(aPixtable->table, MUSE_PIXTABLE_DQ);
2011  uint32_t *cdata_origin = (uint32_t *)cpl_table_get_data_int(aPixtable->table,
2012  MUSE_PIXTABLE_ORIGIN);
2013  cpl_ensure_code(cdata_xpos && cdata_ypos && cdata_lambda &&
2014  cdata_data && cdata_dq && cdata_stat,
2015  CPL_ERROR_BAD_FILE_FORMAT);
2016 
2017  /* print header */
2018  if (aDisplayHeader) {
2019  printf("# xpos ypos lambda data dq stat"
2020  " weight exposure IFU xCCD yCCD xRaw yRaw slice\n");
2021  }
2022  if (aDisplayHeader == 1) {
2023  printf("#%13s %13s %9s %11s flag %11s ---------- No No pix "
2024  " pix pix pix No\n# flux in [%s]\n# flux**2 in [%s]\n",
2025  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_XPOS),
2026  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_YPOS),
2027  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_LAMBDA),
2028  "(flux)", "(flux**2)",
2029  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_DATA),
2030  cpl_table_get_column_unit(aPixtable->table, MUSE_PIXTABLE_STAT));
2031  }
2032 
2033  cpl_size i;
2034  for (i = aStart; i < last; i++) {
2035  int x = muse_pixtable_origin_get_x(cdata_origin[i], aPixtable, i),
2036  y = muse_pixtable_origin_get_y_fast(cdata_origin[i]),
2037  xraw = x, yraw = y;
2038  muse_quadrants_coords_to_raw(NULL, &xraw, &yraw);
2039  if (haswcs) {
2040  printf("%14.7e %14.7e %9.3f ", cdata_xpos[i] + ptxoff, cdata_ypos[i] + ptyoff,
2041  cdata_lambda[i]);
2042  } else {
2043  printf("%14.8f %14.8f %9.3f ", cdata_xpos[i], cdata_ypos[i], cdata_lambda[i]);
2044  }
2045  printf("%12.5e 0x%08x %11.5e %10.4e %2d %2d %4d %4d %4d %4d %2d\n",
2046  cdata_data[i], cdata_dq[i], cdata_stat[i],
2047  cdata_weight ? cdata_weight[i] : 0.,
2048  muse_pixtable_get_expnum(aPixtable, i),
2049  cdata_origin ? muse_pixtable_origin_get_ifu_fast(cdata_origin[i])
2050  : 0,
2051  x, y, xraw, yraw,
2052  cdata_origin ? muse_pixtable_origin_get_slice_fast(cdata_origin[i])
2053  : 0);
2054  } /* for i (pixel table rows) */
2055 
2056  return CPL_ERROR_NONE;
2057 } /* muse_pixtable_dump() */
2058 
2059 /*---------------------------------------------------------------------------*/
2081 /*---------------------------------------------------------------------------*/
2084 {
2085  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, MUSE_PIXTABLE_WCS_UNKNOWN);
2086  const char *unitx = cpl_table_get_column_unit(aPixtable->table,
2087  MUSE_PIXTABLE_XPOS),
2088  *unity = cpl_table_get_column_unit(aPixtable->table,
2089  MUSE_PIXTABLE_YPOS);
2090  cpl_ensure(unitx, CPL_ERROR_DATA_NOT_FOUND, MUSE_PIXTABLE_WCS_UNKNOWN);
2091  /* should be equal in the first three characters */
2092  cpl_ensure(!strncmp(unitx, unity, 4), CPL_ERROR_INCOMPATIBLE_INPUT,
2094  if (!strncmp(unitx, "deg", 4)) {
2095  return MUSE_PIXTABLE_WCS_CELSPH;
2096  }
2097  if (!strncmp(unitx, "pix", 4)) {
2098  return MUSE_PIXTABLE_WCS_PIXEL;
2099  }
2100  if (!strncmp(unitx, "rad", 4)) {
2101  return MUSE_PIXTABLE_WCS_NATSPH;
2102  }
2103  cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
2105 } /* muse_pixtable_wcs_check() */
2106 
2107 /*---------------------------------------------------------------------------*/
2115 /*---------------------------------------------------------------------------*/
2116 cpl_boolean
2118 {
2119  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, CPL_FALSE);
2120  cpl_errorstate prestate = cpl_errorstate_get();
2121  cpl_boolean flag = cpl_propertylist_get_bool(aPixtable->header,
2123  cpl_errorstate_set(prestate);
2124  return flag;
2125 }
2126 
2127 /*---------------------------------------------------------------------------*/
2135 /*---------------------------------------------------------------------------*/
2136 cpl_boolean
2138 {
2139  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, CPL_FALSE);
2140  cpl_errorstate prestate = cpl_errorstate_get();
2141  cpl_boolean flag = cpl_propertylist_get_bool(aPixtable->header,
2143  cpl_errorstate_set(prestate);
2144  return flag;
2145 }
2146 
2147 /*---------------------------------------------------------------------------*/
2155 /*---------------------------------------------------------------------------*/
2156 cpl_boolean
2158 {
2159  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, CPL_FALSE);
2160  return cpl_propertylist_has(aPixtable->header, MUSE_HDR_PT_RVCORR);
2161 } /* muse_pixtable_is_rvcorr() */
2162 
2163 /*---------------------------------------------------------------------------*/
2172 /*---------------------------------------------------------------------------*/
2173 cpl_error_code
2174 muse_pixtable_reset_dq(muse_pixtable *aPixtable, unsigned int aDQ)
2175 {
2176  cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
2177 
2178  unsigned int *dq = (unsigned int *)cpl_table_get_data_int(aPixtable->table,
2179  MUSE_PIXTABLE_DQ),
2180  inverse = ~aDQ; /* inverse bitmask to AND each row's DQ with */
2181  cpl_size i, nrow = muse_pixtable_get_nrow(aPixtable);
2182  #pragma omp parallel for default(none) /* as req. by Ralf */ \
2183  shared(dq, inverse, nrow)
2184  for (i = 0; i < nrow; i++) {
2185  dq[i] &= inverse;
2186  } /* for i (all table rows) */
2187  return CPL_ERROR_NONE;
2188 } /* muse_pixtable_reset_dq() */
2189 
2190 /*---------------------------------------------------------------------------*/
2207 /*---------------------------------------------------------------------------*/
2210 {
2211  cpl_ensure(aPixtable && aPixtable->header, CPL_ERROR_NULL_INPUT, NULL);
2212  unsigned int expnum = muse_pixtable_get_expnum(aPixtable, 0),
2213  explast = muse_pixtable_get_expnum(aPixtable,
2214  muse_pixtable_get_nrow(aPixtable) - 1);
2215  cpl_ensure(expnum == explast, CPL_ERROR_ILLEGAL_INPUT, NULL);
2216 
2217  /* data seems to be valid, we can create the output list */
2219 
2220  /* split the pixel table up into per-slice pixel tables to handle *
2221  * them separately (to get the x-range of each slice for the header) */
2223  /* variables for current image and current IFU */
2224  muse_image *image = NULL;
2225  unsigned short ifu = 0, /* we haven't found any IFU yet */
2226  ilist = 0; /* image index in the list */
2227  int ipt, npt = muse_pixtable_extracted_get_size(pts);
2228  for (ipt = 0; ipt < npt; ipt++) {
2229  float *cdata = cpl_table_get_data_float(pts[ipt]->table, MUSE_PIXTABLE_DATA),
2230  *cstat = cpl_table_get_data_float(pts[ipt]->table, MUSE_PIXTABLE_STAT);
2231  int *cdq = cpl_table_get_data_int(pts[ipt]->table, MUSE_PIXTABLE_DQ);
2232  uint32_t *corigin = (uint32_t *)cpl_table_get_data_int(pts[ipt]->table,
2233  MUSE_PIXTABLE_ORIGIN);
2234  /* if we got to the next (or first) IFU, create new output *
2235  * image of the size of a typical MUSE CCD copy the header *
2236  * from the pixel table but remove specific entries */
2237  if (ifu != muse_pixtable_origin_get_ifu_fast(corigin[0])) {
2238  image = muse_image_new();
2239  image->header = cpl_propertylist_duplicate(pts[ipt]->header);
2240  cpl_propertylist_erase_regexp(image->header, "^ESO DRS MUSE PIXTABLE", 0);
2241  image->data = cpl_image_new(kMuseOutputXRight, kMuseOutputYTop, CPL_TYPE_FLOAT);
2242  image->dq = cpl_image_new(kMuseOutputXRight, kMuseOutputYTop, CPL_TYPE_INT);
2243  /* fill DQ image with EURO3D_MISSDATA, upper value will be cast correctly */
2244  cpl_image_fill_noise_uniform(image->dq, EURO3D_MISSDATA, EURO3D_MISSDATA + 0.1);
2245  image->stat = cpl_image_new(kMuseOutputXRight, kMuseOutputYTop, CPL_TYPE_FLOAT);
2246  cpl_msg_debug(__func__, "new image (index %hu in list)", ilist);
2247  muse_imagelist_set(list, image, ilist++);
2248  } /* if ifu */
2249  if (!image) { /* it cannot really go wrong, but to be sure... */
2250  cpl_msg_error(__func__, "ipt = %d: no image!", ipt);
2251  continue;
2252  }
2253  float *idata = cpl_image_get_data_float(image->data),
2254  *istat = cpl_image_get_data_float(image->stat);
2255  int *idq = cpl_image_get_data_int(image->dq);
2256 
2257  ifu = muse_pixtable_origin_get_ifu_fast(corigin[0]);
2258  unsigned short slice = muse_pixtable_origin_get_slice_fast(corigin[0]);
2259  unsigned int xoff = muse_pixtable_origin_get_offset(pts[ipt], expnum, ifu,
2260  slice),
2261  x1 = INT_MAX, x2 = 0,
2262  irow, nrow = muse_pixtable_get_nrow(pts[ipt]);
2263  for (irow = 0; irow < nrow; irow++) {
2264  /* get coordinate indices from the origin column */
2265  unsigned int x = muse_pixtable_origin_get_x_fast(corigin[irow], xoff) - 1,
2266  y = muse_pixtable_origin_get_y_fast(corigin[irow]) - 1;
2267  idata[x + y*kMuseOutputXRight] = cdata[irow];
2268  idq[x + y*kMuseOutputXRight] = cdq[irow];
2269  istat[x + y*kMuseOutputXRight] = cstat[irow];
2270  if (x < x1) {
2271  x1 = x;
2272  }
2273  if (x > x2) {
2274  x2 = x;
2275  }
2276  } /* for irow */
2277  /* record the approximate horizontal center in the output header */
2278  char *keyword = cpl_sprintf("ESO DRS MUSE SLICE%hu CENTER", slice);
2279  cpl_propertylist_update_float(image->header, keyword, (x2 + x1) / 2. + 1.);
2280 #if 0
2281  cpl_msg_debug(__func__, "IFU %hu %s = %.1f", ifu, keyword,
2282  (x2 + x1) / 2. + 1.);
2283 #endif
2284  cpl_free(keyword);
2285  } /* for ipt */
2287 
2288  return list;
2289 } /* muse_pixtable_to_imagelist() */
2290 
2291 /*---------------------------------------------------------------------------*/
2317 /*---------------------------------------------------------------------------*/
2318 cpl_error_code
2320 {
2321  cpl_ensure_code(aPixtable && aPixtable->header && aList, CPL_ERROR_NULL_INPUT);
2322  unsigned int expnum = muse_pixtable_get_expnum(aPixtable, 0),
2323  explast = muse_pixtable_get_expnum(aPixtable,
2324  muse_pixtable_get_nrow(aPixtable) - 1);
2325  cpl_ensure_code(expnum == explast, CPL_ERROR_ILLEGAL_INPUT);
2326 
2327  /* split the pixel table up into per-slice pixel tables *
2328  * as in muse_pixtable_to_imagelist() and ensure that the *
2329  * number of IFUs matches the number of input images */
2331  if (muse_pixtable_extracted_get_size(pts) / kMuseSlicesPerCCD
2332  != muse_imagelist_get_size(aList)) {
2334  return cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
2335  }
2336  /* variables for current image and current IFU */
2337  muse_image *image = NULL;
2338  unsigned short ifu = 0, /* we haven't worked with any IFU yet */
2339  ilist = 0; /* image index in the list */
2340  int ipt, npt = muse_pixtable_extracted_get_size(pts);
2341  for (ipt = 0; ipt < npt; ipt++) {
2342  float *cdata = cpl_table_get_data_float(pts[ipt]->table, MUSE_PIXTABLE_DATA),
2343  *cstat = cpl_table_get_data_float(pts[ipt]->table, MUSE_PIXTABLE_STAT);
2344  uint32_t *corigin = (uint32_t *)cpl_table_get_data_int(pts[ipt]->table,
2345  MUSE_PIXTABLE_ORIGIN);
2346  /* if we got to the next (or first) IFU, get an image from the list */
2347  if (ifu != muse_pixtable_origin_get_ifu_fast(corigin[0])) {
2348  image = muse_imagelist_get(aList, ilist++);
2349  } /* if ifu */
2350  if (!image) { /* it cannot really go wrong, but to be sure... */
2351  cpl_msg_error(__func__, "ipt = %d: no image!", ipt);
2352  continue;
2353  }
2354  float *idata = cpl_image_get_data_float(image->data),
2355  *istat = cpl_image_get_data_float(image->stat);
2356  ifu = muse_pixtable_origin_get_ifu_fast(corigin[0]);
2357  unsigned short slice = muse_pixtable_origin_get_slice_fast(corigin[0]);
2358  unsigned int xoff = muse_pixtable_origin_get_offset(pts[ipt], expnum, ifu,
2359  slice),
2360  irow, nrow = muse_pixtable_get_nrow(pts[ipt]);
2361  for (irow = 0; irow < nrow; irow++) {
2362  /* get coordinate indices from the origin column */
2363  unsigned int x = muse_pixtable_origin_get_x_fast(corigin[irow], xoff) - 1,
2364  y = muse_pixtable_origin_get_y_fast(corigin[irow]) - 1;
2365  cdata[irow] = idata[x + y*kMuseOutputXRight];
2366  cstat[irow] = istat[x + y*kMuseOutputXRight];
2367  } /* for irow */
2368  } /* for ipt */
2370 
2371  return CPL_ERROR_NONE;
2372 } /* muse_pixtable_from_imagelist() */
2373 
2374 /*---------------------------------------------------------------------------*/
2387 /*---------------------------------------------------------------------------*/
2388 muse_pixtable **
2390 {
2391  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, NULL);
2392  cpl_size n_rows = cpl_table_get_nrow(aPixtable->table);
2393  unsigned int ifu_slice_mask = (0x1f << MUSE_ORIGIN_SHIFT_IFU) | 0x3f;
2394  cpl_table_duplicate_column(aPixtable->table, "ifuslice",
2395  aPixtable->table, MUSE_PIXTABLE_ORIGIN);
2396  unsigned int *slicedata = (unsigned int *)
2397  cpl_table_get_data_int(aPixtable->table, "ifuslice");
2398  cpl_size i_row;
2399  unsigned int last_ifu_slice = 0;
2400  int is_sorted = CPL_TRUE;
2401  for (i_row = 0; i_row < n_rows; i_row++) {
2402  slicedata[i_row] &= ifu_slice_mask;
2403  if (is_sorted && slicedata[i_row] < last_ifu_slice) {
2404  is_sorted = CPL_FALSE;
2405  } else {
2406  last_ifu_slice = slicedata[i_row];
2407  }
2408  }
2409  if (!is_sorted) {
2410  cpl_propertylist *order = cpl_propertylist_new();
2411  cpl_propertylist_append_bool(order, "ifuslice", CPL_FALSE);
2412  cpl_propertylist_append_bool(order, MUSE_PIXTABLE_LAMBDA, CPL_FALSE);
2413  cpl_msg_debug(__func__, "sorting pixel table: quick sort, %"CPL_SIZE_FORMAT
2414  " entries", n_rows);
2415  cpl_table_sort(aPixtable->table, order);
2416  cpl_propertylist_delete(order);
2417  /* erase headers that depend on the order in the pixel table */
2418  cpl_propertylist_erase_regexp(aPixtable->header, MUSE_HDR_PT_EXP_REGEXP, 0);
2419  cpl_msg_debug(__func__, "pixel table sorted.");
2420  } /* if !sorted */
2421 
2422  i_row = 0;
2423  cpl_size n_col = cpl_table_get_ncol(aPixtable->table);
2424  cpl_array *colnames = cpl_table_get_column_names(aPixtable->table);
2425  muse_pixtable **slice_tables = cpl_calloc(1, sizeof(muse_pixtable *));
2426  cpl_size n_slices = 0;
2427  while (i_row < n_rows) {
2428  unsigned int ifu_slice = slicedata[i_row];
2429  cpl_size j_row;
2430  for (j_row = i_row+1; j_row < n_rows && slicedata[j_row] == ifu_slice;
2431  j_row++)
2432  ;
2433  cpl_size nrows_slice = j_row - i_row;
2434  muse_pixtable *slice_pixtable = cpl_calloc(1, sizeof(muse_pixtable));
2435  slice_pixtable->table = cpl_table_new(nrows_slice);
2436  cpl_size i_col;
2437  for (i_col = 0; i_col < n_col; i_col++) {
2438  const char *cname = cpl_array_get_string(colnames, i_col);
2439  if (strcmp(cname, "ifuslice") == 0)
2440  continue;
2441  cpl_type ctype = cpl_table_get_column_type(aPixtable->table, cname);
2442  if (ctype == CPL_TYPE_INT) {
2443  int *cdata = cpl_table_get_data_int(aPixtable->table, cname);
2444  cpl_table_wrap_int(slice_pixtable->table, cdata + i_row, cname);
2445  } else if (ctype == CPL_TYPE_FLOAT) {
2446  float *cdata = cpl_table_get_data_float(aPixtable->table, cname);
2447  cpl_table_wrap_float(slice_pixtable->table, cdata + i_row, cname);
2448  } else if (ctype == CPL_TYPE_DOUBLE) {
2449  double *cdata = cpl_table_get_data_double(aPixtable->table, cname);
2450  cpl_table_wrap_double(slice_pixtable->table, cdata + i_row, cname);
2451  } else if (ctype == CPL_TYPE_STRING) {
2452  char **cdata = cpl_table_get_data_string(aPixtable->table, cname);
2453  cpl_table_wrap_string(slice_pixtable->table, cdata + i_row, cname);
2454  }
2455  const char *unit = cpl_table_get_column_unit(aPixtable->table, cname);
2456  cpl_table_set_column_unit(slice_pixtable->table, cname, unit);
2457  } /* for i_col (all table columns) */
2458 
2459  slice_pixtable->header = cpl_propertylist_duplicate(aPixtable->header);
2460  muse_pixtable_compute_limits(slice_pixtable);
2461  slice_tables = cpl_realloc(slice_tables,
2462  (n_slices + 2) * sizeof(muse_pixtable *));
2463  slice_tables[n_slices] = slice_pixtable;
2464  n_slices++;
2465  slice_tables[n_slices] = NULL;
2466  i_row = j_row;
2467  } /* while (all full pixel table rows) */
2468  cpl_array_delete(colnames);
2469  cpl_table_erase_column(aPixtable->table, "ifuslice");
2470 
2471  return slice_tables;
2472 } /* muse_pixtable_extracted_get_slices() */
2473 
2474 /*---------------------------------------------------------------------------*/
2485 /*---------------------------------------------------------------------------*/
2486 cpl_size
2488 {
2489  cpl_ensure(aPixtables, CPL_ERROR_NULL_INPUT, -1);
2490  cpl_size n = 0;
2491  while (aPixtables[n] != NULL) {
2492  n++;
2493  }
2494  return n;
2495 } /* muse_pixtable_extracted_get_size() */
2496 
2497 /*---------------------------------------------------------------------------*/
2507 /*---------------------------------------------------------------------------*/
2508 void
2510 {
2511  if (!aPixtables) {
2512  return;
2513  }
2514  muse_pixtable **t;
2515  for (t = aPixtables; *t != NULL; t++) {
2516  cpl_array *colnames = cpl_table_get_column_names((*t)->table);
2517  cpl_size n_col = cpl_table_get_ncol((*t)->table);
2518  cpl_size i_col;
2519  for (i_col = 0; i_col < n_col; i_col++) {
2520  const char *cname = cpl_array_get_string(colnames, i_col);
2521  cpl_table_unwrap((*t)->table, cname);
2522  }
2523  cpl_array_delete(colnames);
2524  cpl_table_delete((*t)->table);
2525  cpl_propertylist_delete((*t)->header);
2526  cpl_free(*t);
2527  }
2528  cpl_free(aPixtables);
2529 } /* muse_pixtable_extracted_delete() */
2530 
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:87
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.
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:99
cpl_size muse_pfits_get_naxis(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the size of a given axis
Definition: muse_pfits.c:207
#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:46
cpl_size muse_pixtable_get_nrow(const muse_pixtable *aPixtable)
get the number of rows within the pixel table
#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:92
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_erase_ifu_slice(muse_pixtable *aPixtable, unsigned char aIFU, unsigned short aSlice)
Erase pixel table rows related to one slice of one IFU.
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:64
#define MUSE_HDR_PT_TYPE
Pixel table "type" stored in the FITS header.
Definition: muse_pixtable.h:77
#define MUSE_HDR_PT_MERGED
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:40
cpl_table * table
The pixel table.
cpl_propertylist * header
the FITS header
Definition: muse_image.h:72
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:56
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:68
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_ILLUM_REGEXP
#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:42
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:235
cpl_error_code muse_quadrants_coords_to_raw(cpl_propertylist *aHeader, int *aX, int *aY)
Convert coordinates of a trimmed image to raw-image coordinates.
#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:55
#define MUSE_HDR_PT_RVCORR
Definition of a cpl table structure.
muse_image * muse_image_new(void)
Allocate memory for a new muse_image object.
Definition: muse_image.c:66
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:48
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.
cpl_boolean muse_pixtable_is_rvcorr(muse_pixtable *aPixtable)
Determine whether the pixel table is radial-velocity corrected.