MUSE Pipeline Reference Manual  2.1.1
muse_xcombine.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-2015 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 <string.h>
31 
32 #include "muse_xcombine.h"
33 
34 #include "muse_pfits.h"
35 #include "muse_wcs.h"
36 #include "muse_utils.h"
37 #include "muse_data_format_z.h"
38 
39 /*----------------------------------------------------------------------------*/
43 /*----------------------------------------------------------------------------*/
44 
47 /*----------------------------------------------------------------------------*/
61 /*----------------------------------------------------------------------------*/
62 static cpl_table *
63 muse_xcombine_weights_infotable(muse_pixtable **aPixtables)
64 {
65  unsigned int npt = 0;
66  while (aPixtables[npt++]) ; /* count tables, including first NULL table */
67  --npt; /* subtract NULL table */
68 
69  /* create table for the values */
70  cpl_table *table = cpl_table_new(npt);
71  cpl_table_new_column(table, "EXPTIME", CPL_TYPE_DOUBLE);
72  cpl_table_new_column(table, "AGX_AVG", CPL_TYPE_DOUBLE);
73  cpl_table_new_column(table, "AGX_RMS", CPL_TYPE_DOUBLE);
74  cpl_table_new_column(table, "AGY_AVG", CPL_TYPE_DOUBLE);
75  cpl_table_new_column(table, "AGY_RMS", CPL_TYPE_DOUBLE);
76  cpl_table_new_column(table, "IA_FWHM", CPL_TYPE_DOUBLE);
77  cpl_table_new_column(table, "DIMM_START", CPL_TYPE_DOUBLE);
78  cpl_table_new_column(table, "DIMM_END", CPL_TYPE_DOUBLE);
79 
80  /* fill the table with the values */
81  cpl_errorstate prestate = cpl_errorstate_get();
82  unsigned int i;
83  for (i = 0; i < npt; i++) {
84  /* exposure time */
85  cpl_errorstate state = cpl_errorstate_get();
86  double value = muse_pfits_get_exptime(aPixtables[i]->header);
87  if (cpl_errorstate_is_equal(state)) {
88  cpl_table_set_double(table, "EXPTIME", i, value);
89  }
90 
91  /* AG values */
92  state = cpl_errorstate_get();
93  value = muse_pfits_get_agx_avg(aPixtables[i]->header);
94  if (cpl_errorstate_is_equal(state)) {
95  cpl_table_set_double(table, "AGX_AVG", i, value);
96  }
97  state = cpl_errorstate_get();
98  value = muse_pfits_get_agx_rms(aPixtables[i]->header);
99  if (cpl_errorstate_is_equal(state)) {
100  cpl_table_set_double(table, "AGX_RMS", i, value);
101  }
102  state = cpl_errorstate_get();
103  value = muse_pfits_get_agy_avg(aPixtables[i]->header);
104  if (cpl_errorstate_is_equal(state)) {
105  cpl_table_set_double(table, "AGY_AVG", i, value);
106  }
107  state = cpl_errorstate_get();
108  value = muse_pfits_get_agy_rms(aPixtables[i]->header);
109  if (cpl_errorstate_is_equal(state)) {
110  cpl_table_set_double(table, "AGY_RMS", i, value);
111  }
112 
113  /* IA value */
114  state = cpl_errorstate_get();
115  value = muse_pfits_get_ia_fwhm(aPixtables[i]->header);
116  if (cpl_errorstate_is_equal(state)) {
117  cpl_table_set_double(table, "IA_FWHM", i, value);
118  }
119 
120  /* DIMM values */
121  state = cpl_errorstate_get();
122  value = muse_pfits_get_fwhm_start(aPixtables[i]->header);
123  if (cpl_errorstate_is_equal(state) && value > 0.) {
124  cpl_table_set_double(table, "DIMM_START", i, value);
125  }
126  state = cpl_errorstate_get();
127  value = muse_pfits_get_fwhm_end(aPixtables[i]->header);
128  if (cpl_errorstate_is_equal(state) && value > 0.) {
129  cpl_table_set_double(table, "DIMM_END", i, value);
130  }
131  } /* for i (table index) */
132  cpl_errorstate_set(prestate); /* swallow all errors */
133 
134  /* compute AG_AVG = (AGX + AGY) / 2 */
135  cpl_table_duplicate_column(table, "AG_AVG", table, "AGX_AVG");
136  cpl_table_add_columns(table, "AG_AVG", "AGY_AVG");
137  cpl_table_multiply_scalar(table, "AG_AVG", 0.5);
138  /* compute AG_RMS = sqrt(AGX_RMS**2 + AGY_RMS**2) */
139  cpl_table_duplicate_column(table, "AG_RMS", table, "AGX_RMS");
140  cpl_table_power_column(table, "AG_RMS", 2); /* **2 */
141  cpl_table_duplicate_column(table, "AG2_RMS", table, "AGY_RMS");
142  cpl_table_power_column(table, "AG2_RMS", 2); /* **2 */
143  cpl_table_add_columns(table, "AG_RMS", "AG2_RMS");
144  cpl_table_erase_column(table, "AG2_RMS");
145  cpl_table_power_column(table, "AG_RMS", 0.5); /* sqrt() */
146  /* compute DIMM = (DIMM_START + DIMM_END) / 2 */
147  cpl_table_duplicate_column(table, "DIMM", table, "DIMM_START");
148  cpl_table_add_columns(table, "DIMM", "DIMM_END");
149  cpl_table_multiply_scalar(table, "DIMM", 0.5);
150 
151 #if 0
152  printf("%s:\n", __func__);
153  const char *p, *cols[] = { "EXPTIME", "AG_AVG", "IA_FWHM", "DIMM", NULL };
154  for (i = 0, p = cols[i] ; p; p = cols[i++ + 1]) {
155  prestate = cpl_errorstate_get();
156  printf("%s: %.3f +/- %.3f (%"CPL_SIZE_FORMAT" invalid)\n", p,
157  cpl_table_get_column_mean(table, p), cpl_table_get_column_stdev(table, p),
158  cpl_table_count_invalid(table, p));
159  /* swallow errors due to completely invalid column */
160  cpl_errorstate_set(prestate);
161  }
162  cpl_table_dump(table, 0, npt, stdout);
163  fflush(stdout);
164 #endif
165 
166  return table;
167 } /* muse_xcombine_weights_infotable() */
168 
169 /*----------------------------------------------------------------------------*/
203 /*----------------------------------------------------------------------------*/
204 cpl_error_code
206 {
207  cpl_ensure_code(aPixtables, CPL_ERROR_NULL_INPUT);
208  unsigned int npt = 0;
209  while (aPixtables[npt++]) ; /* count tables, including first NULL table */
210  cpl_ensure_code(--npt > 1, CPL_ERROR_ILLEGAL_INPUT); /* subtract NULL table */
211  if (aWeighting == MUSE_XCOMBINE_NONE) {
212  cpl_msg_info(__func__, "%d tables, not weighting them", npt);
213  return CPL_ERROR_NONE;
214  }
215  if (aWeighting != MUSE_XCOMBINE_EXPTIME && aWeighting != MUSE_XCOMBINE_FWHM) {
216  cpl_msg_warning(__func__, "Unknown exposure weighting scheme (%d)",
217  aWeighting);
218  return cpl_error_set(__func__, CPL_ERROR_UNSUPPORTED_MODE);
219  }
220 
221  /* create table with weighting information: exposure times, *
222  * and several seeing FWHM estimators from the FITS header */
223  cpl_table *tinfo = muse_xcombine_weights_infotable(aPixtables);
224  int err;
225  double exptime0 = cpl_table_get_double(tinfo, "EXPTIME", 0, &err);
226  if (err || exptime0 == 0.0) {
227  cpl_table_delete(tinfo);
228  return cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
229  }
230  /* select FWHM estimator for FWHM-based weighting */
231  double fwhm0 = 0.;
232  const char *fwhmcolumn = NULL,
233  *fwhmerrcol = NULL;
234  if (aWeighting == MUSE_XCOMBINE_FWHM) {
235  cpl_size ninvalid = cpl_table_count_invalid(tinfo, "AG_AVG");
236  if (ninvalid > 0) { /* if any entry is invalid */
237  ninvalid = cpl_table_count_invalid(tinfo, "IA_FWHM");
238  if (ninvalid > 0) { /* any more entry invalid */
239  ninvalid = cpl_table_count_invalid(tinfo, "DIMM");
240  if (!ninvalid) { /* success at last */
241  fwhmcolumn = "DIMM";
242  cpl_msg_info(__func__, "%d tables to be weighted using EXPTIME & FWHM "
243  "(using DIMM measurements)", npt);
244  }
245  } else {
246  fwhmcolumn = "IA_FWHM";
247  cpl_msg_info(__func__, "%d tables to be weighted using EXPTIME & FWHM "
248  "(using active optics image analysis)", npt);
249  }
250  } else {
251  fwhmcolumn = "AG_AVG";
252  fwhmerrcol = "AG_RMS";
253  cpl_msg_info(__func__, "%d tables to be weighted using EXPTIME & FWHM "
254  "(using auto-guider info)", npt);
255  }
256  if (!fwhmcolumn) {
257  cpl_msg_warning(__func__, "%d tables to be weighted using EXPTIME.", npt);
258  cpl_msg_warning(__func__, "(FWHM-based weighting was requested but cannot"
259  " be carried due to incomplete FITS headers in some "
260  "exposures.)");
261  cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND, "missing FITS "
262  "headers for FWHM-based exposure weighting");
263  } else {
264  fwhm0 = cpl_table_get_double(tinfo, fwhmcolumn, 0, &err);
265  }
266  } else {
267  /* simple exptime-time weighting */
268  cpl_msg_info(__func__, "%d tables to be weighted using EXPTIME.", npt);
269  }
270 
271  /* add and fill the "weight" column in all pixel tables */
272  unsigned int i;
273  for (i = 0; i < npt; i++) {
274  double exptime = cpl_table_get_double(tinfo, "EXPTIME", i, &err),
275  weight = exptime / exptime0;
276  if (!cpl_table_has_column(aPixtables[i]->table, MUSE_PIXTABLE_WEIGHT)) {
277  cpl_table_new_column(aPixtables[i]->table, MUSE_PIXTABLE_WEIGHT,
278  CPL_TYPE_FLOAT);
279  }
280  /* modify the "weight" column depending on ambient seeing */
281  char *fwhmstr = NULL;
282  if (fwhmcolumn) {
283  double fwhm = cpl_table_get_double(tinfo, fwhmcolumn, i, &err),
284  fwhmerr = fwhmerrcol
285  ? cpl_table_get_double(tinfo, fwhmerrcol, i, NULL) : 0.;
286  weight *= fwhm0 / fwhm;
287  if (fwhmerrcol) {
288  fwhmstr = cpl_sprintf(", FWHM = %.2f +/- %.2f", fwhm, fwhmerr);
289  } else {
290  fwhmstr = cpl_sprintf(", FWHM = %.2f", fwhm);
291  }
292  }
293  cpl_msg_debug(__func__, "Table %d, weight = %f (EXPTIME = %f%s)", i+1, weight,
294  exptime, fwhmstr ? fwhmstr : "");
295  cpl_free(fwhmstr);
296  cpl_table_fill_column_window_float(aPixtables[i]->table,
298  0, muse_pixtable_get_nrow(aPixtables[i]),
299  weight);
300  /* add the status header */
301  cpl_propertylist_update_bool(aPixtables[i]->header, MUSE_HDR_PT_WEIGHTED,
302  CPL_TRUE);
303  cpl_propertylist_set_comment(aPixtables[i]->header, MUSE_HDR_PT_WEIGHTED,
304  MUSE_HDR_PT_WEIGHTED_COMMENT);
305  } /* for i (table index) */
306  cpl_table_delete(tinfo);
307 
308  return CPL_ERROR_NONE;
309 } /* muse_xcombine_weights() */
310 
311 /*----------------------------------------------------------------------------*/
332 /*----------------------------------------------------------------------------*/
333 double *
334 muse_xcombine_find_offsets(const cpl_table *aOffsets, const char *aDateObs)
335 {
336  const char *id = "muse_xcombine_tables"; /* pretend to be in that function */
337  cpl_ensure(aOffsets && aDateObs, CPL_ERROR_NULL_INPUT, NULL);
338  /* DATE-OBS format allowed by FITS are YYYY-MM-DDThh:mm:ss and optional *
339  * fractional seconds that fit within the 68 char FITS limit */
340  cpl_ensure(strlen(aDateObs) >= 19 && strlen(aDateObs) <= 68,
341  CPL_ERROR_ILLEGAL_INPUT, NULL);
342 
343  int ioff, noff = cpl_table_get_nrow(aOffsets);
344  for (ioff = 0; ioff < noff; ioff++) {
345  const char *dateobs = cpl_table_get_string(aOffsets, MUSE_OFFSETS_DATEOBS,
346  ioff);
347  /* compare DATE-OBS to the 23-char limit that's used in *
348  * normal ESO DATE keywords, that include milliseconds */
349  if (dateobs && !strncmp(dateobs, aDateObs, 23)) {
350  double *offsets = cpl_calloc(3, sizeof(double));
351 #if 0
352  cpl_msg_debug(__func__, "found:");
353  cpl_table_dump(aOffsets, ioff, 1, stdout);
354  fflush(stdout);
355 #endif
356  int err;
357  offsets[0] = cpl_table_get_double(aOffsets, MUSE_OFFSETS_DRA, ioff, &err);
358  if (err) {
359  cpl_msg_warning(id, "%s for %s could not be read from %s!",
360  MUSE_OFFSETS_DRA, aDateObs, MUSE_TAG_OFFSET_LIST);
361  offsets[0] = NAN;
362  }
363  offsets[1] = cpl_table_get_double(aOffsets, MUSE_OFFSETS_DDEC, ioff, &err);
364  if (err) {
365  cpl_msg_warning(id, "%s for %s could not be read from %s!",
366  MUSE_OFFSETS_DDEC, aDateObs, MUSE_TAG_OFFSET_LIST);
367  offsets[1] = NAN;
368  }
369  offsets[2] = cpl_table_has_column(aOffsets, MUSE_OFFSETS_FSCALE)
370  ? cpl_table_get_double(aOffsets, MUSE_OFFSETS_FSCALE, ioff, &err)
371  : NAN; /* no scaling by default */
372  if (err) {
373  offsets[2] = NAN; /* fall back to no scaling */
374  }
375  return offsets;
376  }
377  } /* for ioff (all offset table rows) */
378  return NULL;
379 } /* muse_xcombine_find_offsets() */
380 
381 /*----------------------------------------------------------------------------*/
433 /*----------------------------------------------------------------------------*/
435 muse_xcombine_tables(muse_pixtable **aPixtables, const cpl_table *aOffsets)
436 {
437  cpl_ensure(aPixtables, CPL_ERROR_NULL_INPUT, NULL);
438  unsigned int npt = 0;
439  while (aPixtables[npt++]) ; /* count tables, including first NULL table */
440  cpl_ensure(--npt > 1, CPL_ERROR_ILLEGAL_INPUT, NULL); /* subtract NULL table */
441  cpl_ensure(muse_pixtable_wcs_check(aPixtables[0]) == MUSE_PIXTABLE_WCS_NATSPH,
442  CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
443  cpl_msg_info(__func__, "%u tables to be combined", npt);
444 
445  double timeinit = cpl_test_get_walltime(),
446  cpuinit = cpl_test_get_cputime();
447  muse_utils_memory_dump("muse_xcombine_tables() start");
448  muse_pixtable *pt = aPixtables[0];
449  aPixtables[0] = NULL;
450  /* warn, if the table was not RV corrected */
451  if (!muse_pixtable_is_rvcorr(pt)) {
452  cpl_msg_warning(__func__, "Data of exposure 1 (DATE-OBS=%s) was not radial-"
453  "velocity corrected!", muse_pfits_get_dateobs(pt->header));
454  }
455  /* change exposure number in offset keywords to 1 */
457  /* add exposure range */
458  char keyword[KEYWORD_LENGTH], comment[KEYWORD_LENGTH];
459  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST, 1);
460  cpl_propertylist_append_long_long(pt->header, keyword, 0);
461  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST_COMMENT, 1);
462  cpl_propertylist_set_comment(pt->header, keyword, comment);
463  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST, 1);
464  cpl_propertylist_append_long_long(pt->header, keyword,
465  muse_pixtable_get_nrow(pt) - 1);
466  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST_COMMENT, 1);
467  cpl_propertylist_set_comment(pt->header, keyword, comment);
468 
469  double ra0 = muse_pfits_get_ra(pt->header),
470  dec0 = muse_pfits_get_dec(pt->header),
471  *offsets = aOffsets
472  ? muse_xcombine_find_offsets(aOffsets,
474  : NULL;
475  if (offsets) {
476  if (isfinite(offsets[0]) && isfinite(offsets[1])) {
477  ra0 -= offsets[0];
478  dec0 -= offsets[1];
479  cpl_msg_debug(__func__, "Applying coordinate offsets to exposure 1: %e/%e"
480  " deg", offsets[0], offsets[1]);
481  /* store in the header of the output pixel table */
482  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DRA, 1);
483  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DRA_C, offsets[0] * 3600.);
484  cpl_propertylist_append_double(pt->header, keyword, offsets[0]);
485  cpl_propertylist_set_comment(pt->header, keyword, comment);
486  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DDEC, 1);
487  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DDEC_C, offsets[1] * 3600.);
488  cpl_propertylist_append_double(pt->header, keyword, offsets[1]);
489  cpl_propertylist_set_comment(pt->header, keyword, comment);
490  } /* if spatial offsets */
491  if (isnormal(offsets[2])) { /* valid flux scale */
492  /* get and apply the scale */
493  cpl_msg_debug(__func__, "Scaling flux of exposure 1 by %g.", offsets[2]);
494  muse_pixtable_flux_multiply(pt, offsets[2]);
495  /* store in the header of the output pixel table */
496  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_FLUX_SCALEi, 1);
497  cpl_propertylist_append_double(pt->header, keyword, offsets[2]);
498  cpl_propertylist_set_comment(pt->header, keyword, MUSE_HDR_FLUX_SCALEi_C);
499  } /* if scale */
500  /* store date of changed exposure in the header of the output pixel table */
501  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DATEOBS, 1);
502  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DATEOBS_C, 1);
503  cpl_propertylist_append_string(pt->header, keyword,
505  cpl_propertylist_set_comment(pt->header, keyword, comment);
506  } /* if offsets */
507  cpl_free(offsets);
508  muse_wcs_position_celestial(pt, ra0, dec0);
509 
510  unsigned int i, nskipped = 0;
511  for (i = 1; i < npt; i++) {
512  if (muse_pixtable_wcs_check(aPixtables[i]) != MUSE_PIXTABLE_WCS_NATSPH) {
513  cpl_msg_warning(__func__, "Exposure %d was not projected to native "
514  "spherical coordinates, skipping this one!", i + 1);
515  nskipped++;
516  continue;
517  }
518  if (!muse_pixtable_is_rvcorr(pt)) {
519  cpl_msg_warning(__func__, "Data of exposure %u (DATE-OBS=%s) was not "
520  "radial-velocity corrected!", i+1,
521  muse_pfits_get_dateobs(aPixtables[i]->header));
522  }
523 
524  /* apply spherical coordinate rotation to coordinates of this exposure */
525  double ra = muse_pfits_get_ra(aPixtables[i]->header),
526  dec = muse_pfits_get_dec(aPixtables[i]->header);
527  offsets = aOffsets
528  ? muse_xcombine_find_offsets(aOffsets,
529  muse_pfits_get_dateobs(aPixtables[i]->header))
530  : NULL;
531  cpl_boolean offcor = CPL_FALSE; /* if any offset correction was applied */
532  if (offsets) {
533  if (isfinite(offsets[0]) && isfinite(offsets[1])) {
534  ra -= offsets[0];
535  dec -= offsets[1];
536  cpl_msg_debug(__func__, "Applying coordinate offsets to exposure %d: "
537  "%e/%e deg", i + 1, offsets[0], offsets[1]);
538  offcor = CPL_TRUE;
539  /* store in the header of the output pixel table */
540  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DRA, i + 1);
541  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DRA_C, offsets[0] * 3600.);
542  cpl_propertylist_append_double(pt->header, keyword, offsets[0]);
543  cpl_propertylist_set_comment(pt->header, keyword, comment);
544  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DDEC, i + 1);
545  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DDEC_C, offsets[1] * 3600.);
546  cpl_propertylist_append_double(pt->header, keyword, offsets[1]);
547  cpl_propertylist_set_comment(pt->header, keyword, comment);
548  } /* if spatial offsets */
549  if (isnormal(offsets[2])) { /* valid flux scale */
550  cpl_msg_debug(__func__, "Scaling flux of exposure %u by %g.", i + 1,
551  offsets[2]);
552  muse_pixtable_flux_multiply(aPixtables[i], offsets[2]);
553  /* store in the header of the output pixel table */
554  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_FLUX_SCALEi, i + 1);
555  cpl_propertylist_append_double(pt->header, keyword, offsets[2]);
556  cpl_propertylist_set_comment(pt->header, keyword, MUSE_HDR_FLUX_SCALEi_C);
557  } /* if scale */
558  /* store in the header of the output pixel table */
559  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DATEOBS, i + 1);
560  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DATEOBS_C, i + 1);
561  cpl_propertylist_append_string(pt->header, keyword,
562  muse_pfits_get_dateobs(aPixtables[i]->header));
563  cpl_propertylist_set_comment(pt->header, keyword, comment);
564  } /* if offsets */
565  cpl_free(offsets);
566  muse_wcs_position_celestial(aPixtables[i], ra, dec);
567 
568  /* Shift the x/y coordinates depending on their relative zeropoint! */
569  double raoffset = ra - ra0,
570  decoffset = dec - dec0;
571 #if 0
572  /* this is not switched on, since it actually degrades performance by *
573  * 10% compared to the state before, without this offset correction */
574  float *xpos = cpl_table_get_data_float(aPixtables[i]->table, MUSE_PIXTABLE_XPOS),
575  *ypos = cpl_table_get_data_float(aPixtables[i]->table, MUSE_PIXTABLE_YPOS);
576  cpl_size irow, nrowi = muse_pixtable_get_nrow(aPixtables[i]);
577  #pragma omp parallel for default(none) /* as req. by Ralf */ \
578  shared(decoffset, nrowi, raoffset, xpos, ypos)
579  for (irow = 0; irow < nrowi; irow++) {
580  xpos[irow] += raoffset;
581  ypos[irow] += decoffset;
582  } /* for irow */
583 #else
584  /* using these functions, the speed degradation is not measurable */
585  cpl_table_add_scalar(aPixtables[i]->table, MUSE_PIXTABLE_XPOS, raoffset);
586  cpl_table_add_scalar(aPixtables[i]->table, MUSE_PIXTABLE_YPOS, decoffset);
587 #endif
588 
589  /* compute simple (inaccurate!) offset for information */
590  double avdec = (dec + dec0) / 2.,
591  raoff = (ra - ra0) * cos(avdec * CPL_MATH_RAD_DEG) * 3600.,
592  decoff = (dec - dec0) * 3600.;
593  cpl_msg_info(__func__, "Distance of exposure %u (relative to exp. 1): "
594  "%.1f,%.1f arcsec%s", i+1, raoff, decoff,
595  offcor ? " (corrected offset)" : "");
596 
597  /* append the next pixel table to the end and delete the original */
598  cpl_size nrow = muse_pixtable_get_nrow(pt);
599  cpl_table_insert(pt->table, aPixtables[i]->table, nrow);
600  /* copy the offset headers to the output pixel table before deleting it */
601  muse_pixtable_origin_copy_offsets(pt, aPixtables[i], i + 1);
602  muse_pixtable_delete(aPixtables[i]);
603  aPixtables[i] = NULL;
604 
605  /* add respective exposure range to the header */
606  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST, i + 1);
607  cpl_propertylist_append_long_long(pt->header, keyword, nrow);
608  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST_COMMENT, i + 1);
609  cpl_propertylist_set_comment(pt->header, keyword, comment);
610  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST, i + 1);
611  cpl_propertylist_append_long_long(pt->header, keyword,
612  muse_pixtable_get_nrow(pt) - 1);
613  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST_COMMENT, i + 1);
614  cpl_propertylist_set_comment(pt->header, keyword, comment);
615  } /* for i (pixel tables) */
617  /* update the merge-related status header */
618  cpl_propertylist_update_int(pt->header, MUSE_HDR_PT_COMBINED, npt - nskipped);
619  cpl_propertylist_set_comment(pt->header, MUSE_HDR_PT_COMBINED,
620  MUSE_HDR_PT_COMBINED_COMMENT);
621  /* debug timing */
622  double timefini = cpl_test_get_walltime(),
623  cpufini = cpl_test_get_cputime();
624  muse_utils_memory_dump("muse_xcombine_tables() end");
625  cpl_msg_debug(__func__, "Combining %u tables took %gs (wall-clock) and %gs "
626  "(CPU)", npt, timefini - timeinit, cpufini - cpuinit);
627  return pt;
628 } /* muse_xcombine_tables() */
629 
#define MUSE_PIXTABLE_XPOS
Definition: muse_pixtable.h:51
#define MUSE_HDR_PT_EXP_FST
FITS header keyword defining the first row index for a given exposure.
Definition: muse_pixtable.h:93
double muse_pfits_get_ra(const cpl_propertylist *aHeaders)
find out the right ascension
Definition: muse_pfits.c:267
cpl_size muse_pixtable_get_nrow(const muse_pixtable *aPixtable)
get the number of rows within the pixel table
double muse_pfits_get_ia_fwhm(const cpl_propertylist *aHeaders)
find out the image analysis FWHM corrected by airmass (in arcsec)
Definition: muse_pfits.c:1242
#define MUSE_HDR_PT_EXP_LST
FITS header keyword defining the last row index for a given exposure.
Definition: muse_pixtable.h:98
double muse_pfits_get_agy_avg(const cpl_propertylist *aHeaders)
find out the y-FWHM average value from the auto-guider (in arcsec)
Definition: muse_pfits.c:1206
void muse_utils_memory_dump(const char *aMarker)
Display the current memory usage of the given program.
Definition: muse_utils.c:2702
const char * muse_pfits_get_dateobs(const cpl_propertylist *aHeaders)
find out the date of observations
Definition: muse_pfits.c:364
#define MUSE_HDR_PT_COMBINED
cpl_table * table
The pixel table.
cpl_error_code muse_xcombine_weights(muse_pixtable **aPixtables, muse_xcombine_types aWeighting)
compute the weights for combination of two or more exposures
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.
#define MUSE_PIXTABLE_WEIGHT
Definition: muse_pixtable.h:56
double muse_pfits_get_fwhm_end(const cpl_propertylist *aHeaders)
find out the ambient seeing at end of exposure (in arcsec)
Definition: muse_pfits.c:1097
Structure definition of MUSE pixel table.
double * muse_xcombine_find_offsets(const cpl_table *aOffsets, const char *aDateObs)
Get offsets and scale from table row with matching DATE-OBS entry.
#define MUSE_HDR_PT_WEIGHTED
muse_pixtable_wcs muse_pixtable_wcs_check(muse_pixtable *aPixtable)
Check the state of the world coordinate system of a pixel table.
muse_pixtable * muse_xcombine_tables(muse_pixtable **aPixtables, const cpl_table *aOffsets)
combine the pixel tables of several exposures into one
double muse_pfits_get_fwhm_start(const cpl_propertylist *aHeaders)
find out the ambient seeing at start of exposure (in arcsec)
Definition: muse_pfits.c:1078
double muse_pfits_get_dec(const cpl_propertylist *aHeaders)
find out the declination
Definition: muse_pfits.c:285
double muse_pfits_get_agy_rms(const cpl_propertylist *aHeaders)
find out the y-FWHM root mean square from the auto-guider (in arcsec)
Definition: muse_pfits.c:1224
double muse_pfits_get_agx_rms(const cpl_propertylist *aHeaders)
find out the x-FWHM root mean square from the auto-guider (in arcsec)
Definition: muse_pfits.c:1188
double muse_pfits_get_exptime(const cpl_propertylist *aHeaders)
find out the exposure time
Definition: muse_pfits.c:382
cpl_error_code muse_wcs_position_celestial(muse_pixtable *aPixtable, double aRA, double aDEC)
Convert native to celestial spherical coordinates in a pixel table.
Definition: muse_wcs.c:1466
double muse_pfits_get_agx_avg(const cpl_propertylist *aHeaders)
find out the x-FWHM average value from the auto-guider (in arcsec)
Definition: muse_pfits.c:1170
#define MUSE_PIXTABLE_YPOS
Definition: muse_pixtable.h:52
void muse_pixtable_delete(muse_pixtable *aPixtable)
Deallocate memory associated to a pixel table object.
muse_xcombine_types
Xposure combination types.
Definition: muse_xcombine.h:44
cpl_error_code muse_pixtable_compute_limits(muse_pixtable *aPixtable)
(Re-)Compute the limits of the coordinate columns of a pixel table.
cpl_propertylist * header
The FITS header.
cpl_error_code muse_pixtable_flux_multiply(muse_pixtable *aPixtable, double aScale)
Scale the flux of a pixel table with correct treatment of variance.
cpl_boolean muse_pixtable_is_rvcorr(muse_pixtable *aPixtable)
Determine whether the pixel table is radial-velocity corrected.