33 #include "muse_pixtable.h"
34 #include "muse_instrument.h"
36 #include "muse_cplwrappers.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"
45 #include "muse_utils.h"
51 #define CREATE_MINIMAL_PIXTABLE 0
52 #define PIXTABLE_CREATE_CCDSIZED 1
53 #define DEBUG_PIXTABLE_CREATION 0
54 #define DEBUG_PIXTABLE_FEW_SLICES 0
100 #define MUSE_ORIGIN_SHIFT_XSLICE 24
101 #define MUSE_ORIGIN_SHIFT_YPIX 11
102 #define MUSE_ORIGIN_SHIFT_IFU 6
106 #define MUSE_ORIGIN_SLICE_SAFETY_OFFSET -20
110 static inline uint32_t
111 muse_pixtable_origin_encode_fast(
unsigned int aX,
unsigned int aY,
113 unsigned short aSlice,
unsigned int aOffset)
115 return ((aX - aOffset) << MUSE_ORIGIN_SHIFT_XSLICE)
116 | (aY << MUSE_ORIGIN_SHIFT_YPIX)
117 | (aIFU << MUSE_ORIGIN_SHIFT_IFU)
122 static inline unsigned int
123 muse_pixtable_origin_get_x_fast(uint32_t aOrigin, uint32_t aOffset)
125 return ((aOrigin >> MUSE_ORIGIN_SHIFT_XSLICE) & 0x7f) + aOffset;
129 static inline unsigned int
130 muse_pixtable_origin_get_y_fast(uint32_t aOrigin)
132 return (aOrigin >> MUSE_ORIGIN_SHIFT_YPIX) & 0x1fff;
136 static inline unsigned short
137 muse_pixtable_origin_get_ifu_fast(uint32_t aOrigin)
139 return (aOrigin >> MUSE_ORIGIN_SHIFT_IFU) & 0x1f;
143 static inline unsigned short
144 muse_pixtable_origin_get_slice_fast(uint32_t aOrigin)
146 return aOrigin & 0x3f;
169 unsigned short aSlice,
unsigned int aOffset)
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);
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)
189 return muse_pixtable_origin_encode_fast(aX, aY, aIFU, aSlice, aOffset);
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();
219 if (!cpl_errorstate_is_equal(prestate)) {
220 cpl_errorstate_set(prestate);
224 x = muse_pixtable_origin_get_x_fast(aOrigin, offset);
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());
231 cpl_ensure(x <= 8191 && x >= 1 && cpl_errorstate_is_equal(prestate),
232 CPL_ERROR_ILLEGAL_OUTPUT, 0);
248 unsigned int y = muse_pixtable_origin_get_y_fast(aOrigin);
250 if (y > 8191 || y < 1) {
251 cpl_msg_error(__func__,
"aOrigin=%#x y=%d", aOrigin, y);
254 cpl_ensure(y <= 8191 && y >= 1, CPL_ERROR_ILLEGAL_OUTPUT, 0);
270 unsigned short ifu = muse_pixtable_origin_get_ifu_fast(aOrigin);
272 if (ifu > kMuseNumIFUs || ifu < 1) {
273 cpl_msg_error(__func__,
"aOrigin=%#x ifu=%d", aOrigin, ifu);
276 cpl_ensure(ifu <= kMuseNumIFUs && ifu >= 1, CPL_ERROR_ILLEGAL_OUTPUT, 0);
292 unsigned short slice = muse_pixtable_origin_get_slice_fast(aOrigin);
294 if (slice > kMuseSlicesPerCCD || slice < 1) {
295 cpl_msg_error(__func__,
"aOrigin=%#x slice=%d", aOrigin, slice);
298 cpl_ensure(slice <= kMuseSlicesPerCCD && slice >= 1,
299 CPL_ERROR_ILLEGAL_OUTPUT, 0);
321 cpl_polynomial *aLTrace,
322 unsigned short aIFU,
unsigned short aSlice)
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);
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);
356 unsigned short aIFU,
unsigned short aSlice)
358 cpl_ensure(aPixtable && aPixtable->
header, CPL_ERROR_NULL_INPUT, 0);
361 cpl_errorstate prestate = cpl_errorstate_get();
362 unsigned int offset = cpl_propertylist_get_int(aPixtable->
header, keyword);
365 if (offset > 8191 || offset < 1) {
366 cpl_msg_error(__func__,
"aIFU=%d aSlice=%d offset=%d",
367 aIFU, aSlice, offset);
370 cpl_ensure(offset <= 8191 && offset >= 1 && cpl_errorstate_is_equal(prestate),
371 CPL_ERROR_ILLEGAL_OUTPUT, 0);
400 cpl_ensure_code(aOut && aOut->
header, CPL_ERROR_NULL_INPUT);
401 cpl_propertylist *dest = aOut->
header,
403 if (aFrom && aFrom->
header) {
406 char keyword[KEYWORD_LENGTH];
407 unsigned short nifu, nslice;
408 for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
409 for (nslice = 1; nslice <= kMuseSlicesPerCCD; 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)) {
417 cpl_errorstate_set(prestate);
421 cpl_propertylist_erase(from, keyword);
425 cpl_propertylist_update_int(dest, keyword, offset);
426 cpl_propertylist_set_comment(dest, keyword,
427 MUSE_HDR_PT_IFU_SLICE_OFFSET_COMMENT);
431 return CPL_ERROR_NONE;
453 cpl_ensure(aPixtable && aPixtable->
header, CPL_ERROR_NULL_INPUT, 0);
455 CPL_ERROR_ILLEGAL_INPUT, 0);
457 char keyword[KEYWORD_LENGTH];
458 unsigned int exposure = 0;
459 cpl_size lo = 0, hi = 0;
461 cpl_errorstate prestate = cpl_errorstate_get();
463 lo = cpl_propertylist_get_long_long(aPixtable->
header, keyword);
465 hi = cpl_propertylist_get_long_long(aPixtable->
header, keyword);
466 if (!cpl_errorstate_is_equal(prestate)) {
473 cpl_errorstate_set(prestate);
477 cpl_ensure(lo <= aRow && hi >= aRow, CPL_ERROR_ILLEGAL_OUTPUT, 0);
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 }
570 cpl_table *aGeoTable)
572 cpl_ensure(aImage && aWave && aTrace, CPL_ERROR_NULL_INPUT, NULL);
575 cpl_ensure(ifu >= 1 && ifu <= kMuseNumIFUs, CPL_ERROR_DATA_NOT_FOUND, NULL);
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);
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!",
591 cpl_msg_error(__func__,
"%s", msg);
592 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
"%s", msg);
600 int xsize = cpl_image_get_size_x(aImage->
data),
601 ysize = cpl_image_get_size_y(aImage->
data),
602 #if PIXTABLE_CREATE_CCDSIZED
605 isize = xsize * ysize;
609 isize = ysize * kMuseSlicesPerCCD * kMuseSliceHiLikelyWidth;
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);
623 aGeoTable ? MUSE_PIXTABLE_STRING_FULL
624 : MUSE_PIXTABLE_STRING_SIMPLE);
626 aGeoTable ? MUSE_PIXTABLE_COMMENT_FULL
627 : MUSE_PIXTABLE_COMMENT_SIMPLE);
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);
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);
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);
656 unsigned short 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,
663 cpl_table *geopos = NULL;
664 double *slice_x = NULL, *slice_y = NULL, *slice_angle = NULL,
668 slice_x = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_X);
669 slice_y = cpl_table_get_data_double(geopos, MUSE_GEOTABLE_Y);
671 if (!geopos || !slice_x || !slice_y) {
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);
681 cpl_table_delete(geopos);
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);
698 if (!getenv(
"MUSE_EXPERT_USER")) {
699 cpl_table_delete(geopos);
707 cpl_size itablerow = 0;
709 double cputime = cpl_test_get_cputime(),
710 walltime = cpl_test_get_walltime();
713 #if DEBUG_PIXTABLE_FEW_SLICES > 0
714 # define SLICE_LIMIT DEBUG_PIXTABLE_FEW_SLICES
716 # define SLICE_LIMIT kMuseSlicesPerCCD
718 for (islice = 0; islice < SLICE_LIMIT; islice++) {
719 #if DEBUG_PIXTABLE_CREATION
720 cpl_msg_debug(__func__,
"Starting to process slice %2d of IFU %2hhu",
728 cpl_vector *pos = cpl_vector_new(2);
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,
738 cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT,
"%s", msg);
739 cpl_msg_error(__func__,
"%s", msg);
741 cpl_polynomial_delete(pwave);
742 cpl_vector_delete(pos);
743 if (getenv(
"MUSE_EXPERT_USER")) {
746 cpl_table_delete(geopos);
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);
761 cpl_polynomial_delete(pwave);
762 cpl_vector_delete(pos);
763 if (getenv(
"MUSE_EXPERT_USER")) {
766 cpl_table_delete(geopos);
772 ptrace[MUSE_TRACE_LEFT],
777 for (j = 1; j <= ysize; j++) {
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.,
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,
791 int ileft = ceil(x1),
793 cpl_vector_set(pos, 1, j);
797 for (i = ileft; i <= iright; i++) {
798 cpl_vector_set(pos, 0, i);
802 double lambda = cpl_polynomial_eval(pwave, pos);
803 if (lambda < 3000. || lambda > 11000. || !isfinite(lambda)) {
814 float dx = (xcenter - i) / width
815 * (slice_width ? slice_width[islice] : kMuseSliceNominalWidth);
816 #if DEBUG_PIXTABLE_CREATION
817 if (fabs(dx) > 37.5) {
818 cpl_msg_debug(__func__,
"%d - %f -> %f", i, xcenter, dx);
822 #if CREATE_MINIMAL_PIXTABLE
824 if (lambda < 6495. || lambda > 6505. || fabs(dx) > 10.) {
830 cpl_size irow = itablerow++;
831 #if !PIXTABLE_CREATE_CCDSIZED
833 #if DEBUG_PIXTABLE_CREATION
834 printf(
"pixel table before enlargement:\n");
835 cpl_table_dump(pt->
table, irow-5, 10, stdout);
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);
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);
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
865 printf(
"pixel table after filling new rows:\n");
866 cpl_table_dump(pt->
table, irow-5, 10, stdout);
873 double angle = slice_angle ? slice_angle[islice] * CPL_MATH_RAD_DEG
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]
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]);
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]);
894 cdata_lambda[irow] = lambda;
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];
901 cdata_origin[irow] = muse_pixtable_origin_encode_fast(i, j, ifu,
909 cpl_polynomial_delete(pwave);
910 cpl_vector_delete(pos);
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);
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
923 printf(
"end of used part of pixel table before trimming:\n");
924 cpl_table_dump(pt->
table, itablerow-5, 10, stdout);
927 cpl_table_set_size(pt->
table, itablerow);
932 #if DEBUG_PIXTABLE_CREATION
933 printf(
"beginning of pixel table before returning:\n");
934 cpl_table_dump(pt->
table, 0, 5, stdout);
936 printf(
"end of pixel table before returning:\n");
961 if (aPixtable == NULL) {
965 pt->
table = cpl_table_duplicate(aPixtable->
table);
966 pt->
header = cpl_propertylist_duplicate(aPixtable->
header);
989 cpl_table_delete(aPixtable->
table);
990 aPixtable->
table = NULL;
993 cpl_propertylist_delete(aPixtable->
header);
1021 static cpl_error_code
1022 muse_pixtable_save_image(
muse_pixtable *aPixtable,
const char *aFilename)
1024 const char *
id =
"muse_pixtable_save";
1025 cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
1027 cpl_ensure_code(nrow > 0, CPL_ERROR_ILLEGAL_INPUT);
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);
1035 cpl_type type = cpl_table_get_column_type(aPixtable->
table, colname);
1036 cpl_image *image = NULL;
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);
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);
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);
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);
1060 cpl_propertylist_append_string(hext,
"BUNIT", unit);
1062 cpl_image_save(image, aFilename, CPL_TYPE_UNSPECIFIED, hext, CPL_IO_EXTEND);
1063 cpl_image_unwrap(image);
1064 cpl_propertylist_delete(hext);
1066 cpl_array_delete(columns);
1067 return cpl_errorstate_is_equal(state) ? CPL_ERROR_NONE : cpl_error_get_code();
1095 cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
1101 cpl_error_code rc = cpl_propertylist_save(aPixtable->
header, aFilename,
1103 if (rc != CPL_ERROR_NONE) {
1104 cpl_error_set_message(__func__, rc,
"could not save FITS header of pixel "
1105 "table \"%s\"", aFilename);
1109 cpl_boolean astable = getenv(
"MUSE_PIXTABLE_SAVE_AS_TABLE")
1110 && atoi(getenv(
"MUSE_PIXTABLE_SAVE_AS_TABLE")) > 0;
1112 cpl_msg_debug(__func__,
"Saving pixel table \"%s\" as binary table",
1114 rc = cpl_table_save(aPixtable->
table, NULL, NULL, aFilename, CPL_IO_EXTEND);
1116 rc = muse_pixtable_save_image(aPixtable, aFilename);
1145 muse_pixtable_load_window_image(
const char *aFilename,
1146 cpl_size aStart, cpl_size aNRows)
1148 const char *
id =
"muse_pixtable_load";
1151 cpl_size y1 = aStart + 1,
1152 y2 = aStart + aNRows;
1154 int ext = cpl_fits_find_extension(aFilename, MUSE_PIXTABLE_DATA);
1155 cpl_propertylist *hdata = cpl_propertylist_load(aFilename, ext);
1161 cpl_propertylist_delete(hdata);
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);
1177 cpl_propertylist *hext = cpl_propertylist_load(aFilename, iext);
1179 cpl_size nrows = cpl_image_get_size_x(column)
1180 * cpl_image_get_size_y(column);
1182 cpl_table_set_size(table, 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);
1191 cpl_type type = cpl_image_get_type(column);
1193 case CPL_TYPE_FLOAT:
1194 cpl_table_wrap_float(table, cpl_image_unwrap(column), colname);
1197 cpl_table_wrap_int(table, cpl_image_unwrap(column), colname);
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);
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);
1210 cpl_table_set_column_unit(table, colname, unit);
1212 cpl_propertylist_delete(hext);
1239 cpl_size aStart, cpl_size aNRows)
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);
1257 cpl_propertylist *hext = cpl_propertylist_load(aFilename, 1);
1258 cpl_boolean asimage = !strcmp(cpl_propertylist_get_string(hext,
"XTENSION"),
1260 cpl_propertylist_delete(hext);
1264 cpl_msg_info(__func__,
"Loading pixel table \"%s\" (image format)",
1266 pt->
table = muse_pixtable_load_window_image(aFilename, aStart, aNRows);
1268 cpl_msg_info(__func__,
"Loading pixel table \"%s\" (bintable format)",
1270 pt->
table = cpl_table_load_window(aFilename, 1, 0, NULL, aStart, aNRows);
1272 cpl_ensure(cpl_errorstate_is_equal(prestate) && pt->
table,
1273 cpl_error_get_code(), NULL);
1275 if (rc != CPL_ERROR_NONE) {
1276 cpl_error_set_message(__func__, rc,
"pixel table \"%s\" does not contain "
1277 "all expected columns", aFilename);
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);
1339 double aLambdaMin,
double aLambdaMax)
1347 if (rc != CPL_ERROR_NONE) {
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);
1398 double aLambdaMin,
double aLambdaMax)
1400 cpl_ensure(aExposureList, CPL_ERROR_NULL_INPUT, NULL);
1403 if (cpl_table_has_column(aExposureList,
"00")) {
1404 const char *filename = cpl_table_get_string(aExposureList,
"00", 0);
1413 cpl_boolean isfirst = CPL_TRUE;
1414 double fluxlref = 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);
1422 cpl_msg_warning(__func__,
"Channel for IFU %02d is missing", i);
1429 cpl_msg_error(__func__,
"failed to load pixel table from \"%s\"",
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)) {
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);
1454 if (fluxsref == 0. && fluxlref > 0. && !cpl_errorstate_is_equal(prestate)) {
1456 cpl_msg_warning(__func__,
"only found reference lamp-flat flux (%e) in "
1457 "\"%s\", flux levels may vary between IFUs!", fluxlref,
1459 cpl_errorstate_set(prestate);
1461 cpl_msg_debug(__func__,
"reference flat fluxes sky: %e lamp: %e",
1462 fluxsref, fluxlref);
1464 cpl_propertylist_erase(pt->
header, MUSE_HDR_FLAT_FLUX_SKY);
1465 cpl_propertylist_erase(pt->
header, MUSE_HDR_FLAT_FLUX_LAMP);
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),
1479 if (fluxsref > 0. && fluxs > 0.) {
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,
1487 cpl_errorstate_set(state);
1490 cpl_table_divide_scalar(onept->
table, MUSE_PIXTABLE_DATA, scale);
1491 cpl_table_divide_scalar(onept->
table, MUSE_PIXTABLE_STAT, scale*scale);
1495 cpl_msg_debug(__func__,
"big pixel table now has %"CPL_SIZE_FORMAT
" entries,"
1496 " scale was %e (flat fluxes sky: %e lamp: %e)",
1504 cpl_error_set_message(__func__, CPL_ERROR_FILE_NOT_FOUND,
1505 "None of the pixel tables could be loaded");
1513 cpl_propertylist_erase_regexp(pt->
header,
"ESO DET (CHIP|OUT) ", 0);
1514 cpl_propertylist_erase_regexp(pt->
header,
"ESO DET2 ", 0);
1518 MUSE_HDR_PT_MERGED_COMMENT);
1539 const char *type = cpl_propertylist_get_string(aPixtable->
header,
1542 cpl_msg_debug(__func__,
"pixel table type \"%s\"", type);
1547 if (!strncmp(type, MUSE_PIXTABLE_STRING_FULL,
1548 strlen(MUSE_PIXTABLE_STRING_FULL) + 1)) {
1550 }
else if (!strncmp(type, MUSE_PIXTABLE_STRING_SIMPLE,
1551 strlen(MUSE_PIXTABLE_STRING_SIMPLE) + 1)) {
1572 cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, 0);
1573 cpl_ensure(aPixtable->
table, CPL_ERROR_NULL_INPUT, 0);
1576 return cpl_table_get_nrow(aPixtable->
table);
1600 cpl_ensure_code(aPixtable && aPixtable->
table && aPixtable->
header,
1601 CPL_ERROR_NULL_INPUT);
1603 == CPL_ERROR_NONE, CPL_ERROR_DATA_NOT_FOUND);
1606 return CPL_ERROR_NONE;
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);
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;
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;
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);
1641 cpl_propertylist_erase_regexp(aPixtable->
header, MUSE_HDR_PT_LIMITS_REGEXP, 0);
1645 ptxoff = cpl_propertylist_get_double(aPixtable->
header,
"CRVAL1");
1646 ptyoff = cpl_propertylist_get_double(aPixtable->
header,
"CRVAL2");
1659 return CPL_ERROR_NONE;
1673 static cpl_error_code
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;
1681 cpl_array *sel = cpl_table_where_selected(aPixtable->
table);
1682 cpl_size narray = cpl_array_get_size(sel),
1685 const cpl_size *asel = cpl_array_get_data_cplsize_const(sel);
1686 unsigned int nexp = 0;
1691 if (!cpl_propertylist_has(aPixtable->
header, kwfst) ||
1692 !cpl_propertylist_has(aPixtable->
header, kwlst)) {
1697 ifst = cpl_propertylist_get_long_long(aPixtable->
header, kwfst);
1698 ilst = cpl_propertylist_get_long_long(aPixtable->
header, kwlst);
1700 cpl_size i, nsel = 0;
1701 for (i = 0; i < narray; i++) {
1702 if (asel[i] >= ifst && asel[i] <= ilst) {
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);
1718 }
while (ilst >= ifst);
1719 cpl_array_delete(sel);
1720 return CPL_ERROR_NONE;
1737 cpl_ensure_code(aPixtable && aPixtable->
table && aPixtable->
header,
1738 CPL_ERROR_NULL_INPUT);
1742 return CPL_ERROR_NONE;
1744 #pragma omp critical(cpl_table_select)
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);
1752 cpl_table_erase_selected(aPixtable->
table);
1772 cpl_ensure_code(aPixtable && aPixtable->
table && aPixtable->
header,
1773 CPL_ERROR_NULL_INPUT);
1777 return CPL_ERROR_NONE;
1782 ptxoff = cpl_propertylist_get_double(aPixtable->
header,
"CRVAL1");
1784 #pragma omp critical(cpl_table_select)
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);
1792 cpl_table_erase_selected(aPixtable->
table);
1812 cpl_ensure_code(aPixtable && aPixtable->
table && aPixtable->
header,
1813 CPL_ERROR_NULL_INPUT);
1817 return CPL_ERROR_NONE;
1822 ptyoff = cpl_propertylist_get_double(aPixtable->
header,
"CRVAL2");
1824 #pragma omp critical(cpl_table_select)
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);
1832 cpl_table_erase_selected(aPixtable->
table);
1854 unsigned short aSlice)
1856 cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
1858 cpl_ensure_code(nrow > 0, CPL_ERROR_DATA_NOT_FOUND);
1860 cpl_table_unselect_all(aPixtable->
table);
1861 uint32_t *origin = (uint32_t *)cpl_table_get_data_int(aPixtable->
table,
1862 MUSE_PIXTABLE_ORIGIN);
1864 for (irow = 0; irow < nrow; irow++) {
1867 if (ifu == aIFU && slice == aSlice) {
1868 cpl_table_select_row(aPixtable->
table, irow);
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",
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);
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");
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)) {
1939 if (cpl_mask_get(aMask->
mask, ix, iy) != CPL_BINARY_1) {
1940 cpl_table_unselect_row(aPixtable->
table, i_row);
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;
1981 unsigned char aDisplayHeader)
1983 cpl_ensure_code(aPixtable && aPixtable->
table && aPixtable->
header,
1984 CPL_ERROR_NULL_INPUT);
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) {
1993 double ptxoff = 0., ptyoff = 0.;
1996 ptxoff = cpl_propertylist_get_double(aPixtable->
header,
"CRVAL1");
1997 ptyoff = cpl_propertylist_get_double(aPixtable->
header,
"CRVAL2");
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);
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);
2018 if (aDisplayHeader) {
2019 printf(
"# xpos ypos lambda data dq stat"
2020 " weight exposure IFU xCCD yCCD xRaw yRaw slice\n");
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));
2034 for (i = aStart; i < last; i++) {
2036 y = muse_pixtable_origin_get_y_fast(cdata_origin[i]),
2040 printf(
"%14.7e %14.7e %9.3f ", cdata_xpos[i] + ptxoff, cdata_ypos[i] + ptyoff,
2043 printf(
"%14.8f %14.8f %9.3f ", cdata_xpos[i], cdata_ypos[i], cdata_lambda[i]);
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.,
2049 cdata_origin ? muse_pixtable_origin_get_ifu_fast(cdata_origin[i])
2052 cdata_origin ? muse_pixtable_origin_get_slice_fast(cdata_origin[i])
2056 return CPL_ERROR_NONE;
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);
2092 cpl_ensure(!strncmp(unitx, unity, 4), CPL_ERROR_INCOMPATIBLE_INPUT,
2094 if (!strncmp(unitx,
"deg", 4)) {
2097 if (!strncmp(unitx,
"pix", 4)) {
2100 if (!strncmp(unitx,
"rad", 4)) {
2103 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
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);
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);
2159 cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, CPL_FALSE);
2176 cpl_ensure_code(aPixtable, CPL_ERROR_NULL_INPUT);
2178 unsigned int *dq = (
unsigned int *)cpl_table_get_data_int(aPixtable->
table,
2182 #pragma omp parallel for default(none) \
2183 shared(dq, inverse, nrow)
2184 for (i = 0; i < nrow; i++) {
2187 return CPL_ERROR_NONE;
2211 cpl_ensure(aPixtable && aPixtable->
header, CPL_ERROR_NULL_INPUT, NULL);
2215 cpl_ensure(expnum == explast, CPL_ERROR_ILLEGAL_INPUT, NULL);
2225 unsigned short ifu = 0,
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);
2237 if (ifu != muse_pixtable_origin_get_ifu_fast(corigin[0])) {
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);
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);
2250 cpl_msg_error(__func__,
"ipt = %d: no image!", ipt);
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);
2257 ifu = muse_pixtable_origin_get_ifu_fast(corigin[0]);
2258 unsigned short slice = muse_pixtable_origin_get_slice_fast(corigin[0]);
2261 x1 = INT_MAX, x2 = 0,
2263 for (irow = 0; irow < nrow; irow++) {
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];
2278 char *keyword = cpl_sprintf(
"ESO DRS MUSE SLICE%hu CENTER", slice);
2279 cpl_propertylist_update_float(image->
header, keyword, (x2 + x1) / 2. + 1.);
2281 cpl_msg_debug(__func__,
"IFU %hu %s = %.1f", ifu, keyword,
2282 (x2 + x1) / 2. + 1.);
2321 cpl_ensure_code(aPixtable && aPixtable->
header && aList, CPL_ERROR_NULL_INPUT);
2325 cpl_ensure_code(expnum == explast, CPL_ERROR_ILLEGAL_INPUT);
2334 return cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
2338 unsigned short ifu = 0,
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);
2347 if (ifu != muse_pixtable_origin_get_ifu_fast(corigin[0])) {
2351 cpl_msg_error(__func__,
"ipt = %d: no image!", ipt);
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]);
2361 for (irow = 0; irow < nrow; irow++) {
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];
2371 return CPL_ERROR_NONE;
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");
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;
2406 last_ifu_slice = slicedata[i_row];
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);
2418 cpl_propertylist_erase_regexp(aPixtable->
header, MUSE_HDR_PT_EXP_REGEXP, 0);
2419 cpl_msg_debug(__func__,
"pixel table sorted.");
2423 cpl_size n_col = cpl_table_get_ncol(aPixtable->
table);
2424 cpl_array *colnames = cpl_table_get_column_names(aPixtable->
table);
2426 cpl_size n_slices = 0;
2427 while (i_row < n_rows) {
2428 unsigned int ifu_slice = slicedata[i_row];
2430 for (j_row = i_row+1; j_row < n_rows && slicedata[j_row] == ifu_slice;
2433 cpl_size nrows_slice = j_row - i_row;
2435 slice_pixtable->
table = cpl_table_new(nrows_slice);
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)
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);
2455 const char *unit = cpl_table_get_column_unit(aPixtable->
table, cname);
2456 cpl_table_set_column_unit(slice_pixtable->
table, cname, unit);
2459 slice_pixtable->
header = cpl_propertylist_duplicate(aPixtable->
header);
2461 slice_tables = cpl_realloc(slice_tables,
2463 slice_tables[n_slices] = slice_pixtable;
2465 slice_tables[n_slices] = NULL;
2468 cpl_array_delete(colnames);
2469 cpl_table_erase_column(aPixtable->
table,
"ifuslice");
2471 return slice_tables;
2489 cpl_ensure(aPixtables, CPL_ERROR_NULL_INPUT, -1);
2491 while (aPixtables[n] != NULL) {
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);
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);
2523 cpl_array_delete(colnames);
2524 cpl_table_delete((*t)->table);
2525 cpl_propertylist_delete((*t)->header);
2528 cpl_free(aPixtables);
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.
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
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.
cpl_size muse_pfits_get_naxis(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the size of a given axis
#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
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.
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
#define MUSE_HDR_PT_TYPE
Pixel table "type" stored in the FITS header.
#define MUSE_HDR_PT_MERGED
Structure definition of MUSE three extension FITS file.
cpl_table * table
The pixel table.
cpl_propertylist * header
the FITS header
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
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.
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.
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.
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
#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.
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.
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.