MUSE Pipeline Reference Manual  0.18.1
muse_lsf.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 #define STORE_SLIT_WIDTH
27 #define STORE_BIN_WIDTH
28 
29 /*----------------------------------------------------------------------------*
30  * Includes *
31  *----------------------------------------------------------------------------*/
32 #include <math.h>
33 #include <string.h>
34 #include <fenv.h>
35 
36 #include <cpl.h>
37 
38 #include "muse_lsf.h"
39 #include "muse_resampling.h"
40 #include "muse_pfits.h"
41 #include "muse_quality.h"
42 #include "muse_instrument.h"
43 #include "muse_optimize.h"
44 #include "muse_tracing.h"
45 #include "muse_utils.h"
46 
47 /*----------------------------------------------------------------------------*/
51 /*----------------------------------------------------------------------------*/
52 
55 /*----------------------------------------------------------------------------*
56  * Declaration of only internally used functions *
57  *----------------------------------------------------------------------------*/
58 static cpl_error_code
59 muse_lsf_line_apply(const cpl_array *, cpl_array *,
60  const muse_lsf_params *, double, double);
61 
62 /*----------------------------------------------------------------------------*/
72 /*----------------------------------------------------------------------------*/
74 muse_lsf_params_new(cpl_size n_sensit, cpl_size n_lsf_width, cpl_size n_hermit)
75 {
76  muse_lsf_params *res = cpl_calloc(1, sizeof(muse_lsf_params));
77  res->refraction = 1.0;
78  res->offset = 0.0;
79  res->slit_width = kMuseSliceSlitWidthA;
80  res->bin_width = kMuseSpectralSamplingA;
81  res->lambda_ref = 7000;
82  int i;
83  if (n_hermit > 0) {
84  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
85  res->hermit[i] = cpl_array_new(n_hermit, CPL_TYPE_DOUBLE);
86  cpl_array_fill_window_double(res->hermit[i], 0, n_hermit, 0.0);
87  }
88  }
89  res->lsf_width = cpl_array_new(n_lsf_width, CPL_TYPE_DOUBLE);
90  if (n_lsf_width > 0) {
91  cpl_array_fill_window_double(res->lsf_width, 0, n_lsf_width, 0.0);
92  cpl_array_set_double(res->lsf_width, 0, 1.0);
93  }
94  res->sensitivity = cpl_array_new(n_sensit, CPL_TYPE_DOUBLE);
95  if (n_sensit > 0) {
96  cpl_array_fill_window_double(res->sensitivity, 0, n_sensit, 0.0);
97  cpl_array_set_double(res->sensitivity, 0, 1.0);
98  }
99  return res;
100 }
101 
102 /*----------------------------------------------------------------------------*/
108 /*----------------------------------------------------------------------------*/
109 cpl_size
111  if (aParams == NULL) {
112  return 0;
113  }
114  cpl_size i;
115  for (i = 0; *aParams != NULL; i++) {
116  aParams++;
117  }
118  return i;
119 }
120 
121 /*----------------------------------------------------------------------------*/
126 /*----------------------------------------------------------------------------*/
127 void
129  if (aParams != NULL) {
130  cpl_array_delete(aParams->sensitivity);
131  int i;
132  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
133  cpl_array_delete(aParams->hermit[i]);
134  }
135  cpl_array_delete(aParams->lsf_width);
136  cpl_free(aParams);
137  }
138 }
139 
140 /*----------------------------------------------------------------------------*/
145 /*----------------------------------------------------------------------------*/
146 void
148  if (aParams != NULL) {
149  muse_lsf_params **det;
150  for (det = aParams; *det != NULL; det++) {
152  }
153  cpl_free(aParams);
154  }
155 }
156 
157 /*----------------------------------------------------------------------------*/
171 /*----------------------------------------------------------------------------*/
173  {"ifu", CPL_TYPE_INT, NULL, "%i", "IFU number", CPL_TRUE},
174  {"slice", CPL_TYPE_INT, NULL, "%i", "slice number within the IFU", CPL_TRUE},
175  {"sensitivity", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
176  "detector sensitivity, relative to the reference", CPL_TRUE},
177  {"offset", CPL_TYPE_DOUBLE, NULL, "%e", "wavelength calibration offset", CPL_TRUE},
178  {"refraction", CPL_TYPE_DOUBLE, NULL, "%e", "relative refraction index", CPL_TRUE},
179 #ifdef STORE_SLIT_WIDTH
180  {"slit_width", CPL_TYPE_DOUBLE, "Angstrom", "%e", "slit width", CPL_TRUE},
181 #endif
182 #ifdef STORE_BIN_WIDTH
183  {"bin_width", CPL_TYPE_DOUBLE, "Angstrom", "%e", "bin width", CPL_TRUE},
184 #endif
185  {"lsf_width", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, "Angstrom", "%e",
186  " LSF gauss-hermitean width", CPL_TRUE},
187  {"hermit3", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
188  "3rd order hermitean coefficient", CPL_TRUE},
189  {"hermit4", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
190  "4th order hermitean coefficient", CPL_TRUE},
191  {"hermit5", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
192  "5th order hermitean coefficient", CPL_TRUE},
193  {"hermit6", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
194  "6th order hermitean coefficient", CPL_TRUE},
195  { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
196 };
197 
198 /*----------------------------------------------------------------------------*/
212 /*----------------------------------------------------------------------------*/
213 cpl_error_code
214 muse_lsf_params_save(const muse_lsf_params **aLsf, const char *aFile) {
215  cpl_ensure_code(aLsf != NULL, CPL_ERROR_NULL_INPUT);
216  cpl_ensure_code(*aLsf != NULL, CPL_ERROR_DATA_NOT_FOUND);
217  cpl_ensure_code(aFile != NULL, CPL_ERROR_NULL_INPUT);
218 
219  cpl_size nrows = 0;
220  const muse_lsf_params **det;
221  cpl_size sensitivity_order = 1;
222  cpl_size lsf_order = 1;
223  cpl_size hermit_order[MAX_HERMIT_ORDER];
224  int i;
225  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
226  hermit_order[i] = 1;
227  }
228  for (det = aLsf; *det != NULL; det++, nrows++) {
229  sensitivity_order = fmax(sensitivity_order,
230  cpl_array_get_size((*det)->sensitivity));
231  lsf_order = fmax(lsf_order, cpl_array_get_size((*det)->lsf_width));
232  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
233  hermit_order[i] = fmax(hermit_order[i],
234  cpl_array_get_size((*det)->hermit[i]));
235  }
236  }
237 
238  cpl_table *slice_param = cpl_table_new(nrows);
239  cpl_table_new_column(slice_param, "ifu", CPL_TYPE_INT);
240  cpl_table_new_column(slice_param, "slice", CPL_TYPE_INT);
241  cpl_table_new_column_array(slice_param, "sensitivity",
242  cpl_array_get_type(aLsf[0]->sensitivity),
243  sensitivity_order);
244  cpl_table_new_column(slice_param, "offset", CPL_TYPE_DOUBLE);
245  cpl_table_new_column(slice_param, "refraction", CPL_TYPE_DOUBLE);
246 #ifdef STORE_SLIT_WIDTH
247  cpl_table_new_column(slice_param, "slit_width", CPL_TYPE_DOUBLE);
248 #endif
249 #ifdef STORE_BIN_WIDTH
250  cpl_table_new_column(slice_param, "bin_width", CPL_TYPE_DOUBLE);
251 #endif
252  cpl_table_new_column_array(slice_param, "lsf_width",
253  cpl_array_get_type(aLsf[0]->lsf_width),
254  lsf_order);
255  cpl_table_new_column_array(slice_param, "hermit3",
256  cpl_array_get_type(aLsf[0]->hermit[0]),
257  hermit_order[0]);
258  cpl_table_new_column_array(slice_param, "hermit4",
259  cpl_array_get_type(aLsf[0]->hermit[1]),
260  hermit_order[1]);
261  cpl_table_new_column_array(slice_param, "hermit5",
262  cpl_array_get_type(aLsf[0]->hermit[2]),
263  hermit_order[2]);
264  cpl_table_new_column_array(slice_param, "hermit6",
265  cpl_array_get_type(aLsf[0]->hermit[3]),
266  hermit_order[3]);
267 
268  cpl_size iRow = 0;
269  for (det = aLsf; *det != NULL; det++, iRow++) {
270  cpl_table_set(slice_param, "ifu", iRow, (*det)->ifu);
271  cpl_table_set(slice_param, "slice", iRow, (*det)->slice);
272  cpl_table_set_array(slice_param, "sensitivity", iRow, (*det)->sensitivity);
273  cpl_table_set(slice_param, "offset", iRow, (*det)->offset);
274  cpl_table_set(slice_param, "refraction", iRow, (*det)->refraction);
275 #ifdef STORE_SLIT_WIDTH
276  cpl_table_set(slice_param, "slit_width", iRow, (*det)->slit_width);
277 #endif
278 #ifdef STORE_BIN_WIDTH
279  cpl_table_set(slice_param, "bin_width", iRow, (*det)->bin_width);
280 #endif
281  cpl_table_set_array(slice_param, "lsf_width", iRow, (*det)->lsf_width);
282  cpl_table_set_array(slice_param, "hermit3", iRow, (*det)->hermit[0]);
283  cpl_table_set_array(slice_param, "hermit4", iRow, (*det)->hermit[1]);
284  cpl_table_set_array(slice_param, "hermit5", iRow, (*det)->hermit[2]);
285  cpl_table_set_array(slice_param, "hermit6", iRow, (*det)->hermit[3]);
286  }
287 
288  int r = muse_cpltable_append_file(slice_param, aFile, "SLICE_PARAM",
289  muse_lsfparams_def);
290  cpl_table_delete(slice_param);
291  return r;
292 }
293 
294 /*----------------------------------------------------------------------------*/
306 /*----------------------------------------------------------------------------*/
308 muse_lsf_params_load(const char *aFile, muse_lsf_params **aParams, int aIFU)
309 {
310  cpl_table *lsfTable = muse_cpltable_load(aFile, "SLICE_PARAM",
311  muse_lsfparams_def);
312  if (!lsfTable) {
313  /* try to load from channel extensions */
314  char *extname = cpl_sprintf("CHAN%02d.SLICE_PARAM", aIFU);
315  lsfTable = muse_cpltable_load(aFile, extname, muse_lsfparams_def);
316  cpl_free(extname);
317  if (!lsfTable) {
318  /* now display both the older and the new error messages */
319  cpl_error_set_message(__func__, cpl_error_get_code(), "Loading LSF data from "
320  "\"%s[SLICE_PARAMS]\" and \"%s[CHAH%02d.SLICE_PARAMS]\" "
321  "failed", aFile, aFile, aIFU);
322  return aParams;
323  } /* if 2nd load failure */
324  } /* if 1st load failure */
325 
326  cpl_size n_rows = cpl_table_get_nrow(lsfTable);
327  cpl_size n_rows_old = muse_lsf_params_get_size(aParams);
328  muse_lsf_params **lsfParams
329  = cpl_realloc(aParams, (n_rows + n_rows_old + 1) * sizeof(muse_lsf_params *));
330  lsfParams[n_rows + n_rows_old] = NULL;
331  cpl_size i_row_new = n_rows_old;
332  cpl_size i_row;
333  for (i_row = 0; i_row < n_rows; i_row++) {
334  int ifu = cpl_table_get(lsfTable, "ifu", i_row, NULL);
335  lsfParams[i_row + n_rows_old] = NULL;
336  if ((aIFU <= 0) || (ifu == aIFU)) {
337  muse_lsf_params *det = muse_lsf_params_new(0,0,0);
338  lsfParams[i_row_new] = det;
339  i_row_new++;
340  det->ifu = ifu;
341  det->slice = cpl_table_get(lsfTable, "slice", i_row, NULL);
342  cpl_array_delete(det->sensitivity);
343  det->sensitivity
344  = muse_cpltable_get_array_copy(lsfTable, "sensitivity",i_row);
345  det->offset = cpl_table_get(lsfTable, "offset", i_row, NULL);
346  det->refraction = cpl_table_get(lsfTable, "refraction", i_row, NULL);
347 #ifdef STORE_SLIT_WIDTH
348  det->slit_width = cpl_table_get(lsfTable, "slit_width", i_row, NULL);
349 #endif
350 #ifdef STORE_BIN_WIDTH
351  det->bin_width = cpl_table_get(lsfTable, "bin_width", i_row, NULL);
352 #endif
353  cpl_array_delete(det->lsf_width);
354  det->lsf_width
355  = muse_cpltable_get_array_copy(lsfTable, "lsf_width", i_row);
356  cpl_array_delete(det->hermit[0]);
357  det->hermit[0]
358  = muse_cpltable_get_array_copy(lsfTable, "hermit3", i_row);
359  cpl_array_delete(det->hermit[1]);
360  det->hermit[1]
361  = muse_cpltable_get_array_copy(lsfTable, "hermit4", i_row);
362  cpl_array_delete(det->hermit[2]);
363  det->hermit[2]
364  = muse_cpltable_get_array_copy(lsfTable, "hermit5", i_row);
365  cpl_array_delete(det->hermit[3]);
366  det->hermit[3]
367  = muse_cpltable_get_array_copy(lsfTable, "hermit6", i_row);
368  }
369  }
370  cpl_table_delete(lsfTable);
371 
372  return lsfParams;
373 }
374 
375 /*----------------------------------------------------------------------------*/
390 /*----------------------------------------------------------------------------*/
393 {
394  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
395  cpl_frameset *frames = muse_frameset_find(aProcessing->inputFrames,
396  "LSF_TABLE", aIFU, CPL_FALSE);
397  if (frames == NULL) {
398  return NULL;
399  }
400  /* try standard format first: one file per IFU */
401  cpl_errorstate state = cpl_errorstate_get();
402  cpl_size iframe, nframes = cpl_frameset_get_size(frames);
403  muse_lsf_params **lsfParams = NULL;
404  for (iframe = 0; iframe < nframes; iframe++) {
405  cpl_frame *frame = cpl_frameset_get_position(frames, iframe);
406  lsfParams = muse_lsf_params_load(cpl_frame_get_filename(frame),
407  lsfParams, aIFU);
408  if (lsfParams) {
409  cpl_msg_info(__func__, "Loaded slice LSF params from \"%s\"",
410  cpl_frame_get_filename(frame));
411  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_CALIB, 1);
412  }
413  } /* for iframe (all frames) */
414  char *errmsg = NULL;
415  if (!cpl_errorstate_is_equal(state)) {
416  errmsg = cpl_strdup(cpl_error_get_message());
417  }
418  cpl_errorstate_set(state); /* hide error(s) */
419 
420  /* extra loop to support the merged format */
421  if (!lsfParams && aIFU == 0 && nframes == 1) {
422  cpl_msg_debug(__func__, "No LSF parameters loaded yet, trying merged table "
423  "format.");
424 
425  cpl_frame *frame = cpl_frameset_get_position(frames, 0);
426  const char *fname = cpl_frame_get_filename(frame);
427  state = cpl_errorstate_get();
428  unsigned char ifu;
429  for (ifu = 1; ifu <= kMuseNumIFUs; ifu++) {
430  lsfParams = muse_lsf_params_load(fname, lsfParams, ifu);
431  } /* for ifu */
432  cpl_errorstate_set(state); /* ignore errors */
433  if (lsfParams) {
434  cpl_msg_info(__func__, "Loaded (merged) slice LSF params from \"%s\"", fname);
435  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_CALIB, 1);
436  }
437  } /* if */
438  cpl_frameset_delete(frames);
439 
440  /* now possible output error message */
441  if (!lsfParams) {
442  cpl_msg_debug(__func__, "Loading LSF_TABLEs from input frameset did not "
443  "succeed: %s", errmsg);
444  } /* if still no lsfParams */
445  cpl_free(errmsg);
446  return lsfParams;
447 } /* muse_processing_lsf_params_load() */
448 
449 /*----------------------------------------------------------------------------*/
454 /*----------------------------------------------------------------------------*/
456 muse_lsf_params_get(muse_lsf_params **aParams, int aIFU, int aSlice) {
457  if (aParams == NULL) {
458  return NULL;
459  }
460  int i_det;
461  for (i_det = 0; aParams[i_det] != NULL; i_det++) {
462  if (aParams[i_det]->ifu == aIFU && aParams[i_det]->slice == aSlice) {
463  return aParams[i_det];
464  }
465  }
466  return NULL;
467 }
468 
469 /*----------------------------------------------------------------------------*/
486 /*----------------------------------------------------------------------------*/
487 static cpl_array *
488 muse_lsf_G(cpl_array *aX, cpl_array *aCoeffs) {
489  cpl_ensure(aX != NULL, CPL_ERROR_NULL_INPUT, NULL);
490  cpl_ensure(aCoeffs != NULL, CPL_ERROR_NULL_INPUT, NULL);
491 
492  cpl_array *y = cpl_array_duplicate(aX);
493  cpl_array_multiply(y, y);
494  cpl_array_multiply_scalar(y, -1); // y = - x**2
495 
496  cpl_array *y2 = cpl_array_duplicate(y);
497  muse_cplarray_exp(y2);
498  cpl_array_multiply_scalar(y2, 1.0/60); // y2 = exp(-x**2)/60
499 
500  cpl_array_multiply_scalar(y, 0.5);
501  muse_cplarray_exp(y); // y = exp(-0.5 * x**2)
502 
503  cpl_array *R = cpl_array_duplicate(aX);
504  muse_cplarray_poly1d(R, aCoeffs);
505  cpl_array_multiply(y2, R); // y2 = R * exp(-x**2)/60
506  cpl_array_delete(R);
507  cpl_array_add(y, y2);
508 
509  cpl_array_copy_data_double(y2, cpl_array_get_data_double(aX));
510  cpl_array_multiply_scalar(y2, sqrt(0.5));
511  muse_cplarray_erf(y2);
512  cpl_array_multiply_scalar(y2, sqrt(CPL_MATH_PI / 2));
513  cpl_array_multiply(y2, aX); // y3 = x * sqrt(Pi/2) * erf(x/sqrt(2))
514  cpl_array_add(y, y2);
515  cpl_array_delete(y2);
516 
517  return y;
518 }
519 
520 /*----------------------------------------------------------------------------*/
545 /*----------------------------------------------------------------------------*/
546 static cpl_error_code
547 muse_lsf_line_apply(const cpl_array *aLambda, cpl_array *aSpectrum,
548  const muse_lsf_params *aLsf,
549  double aLineLambda, double aLineFlux) {
550  cpl_ensure_code(aSpectrum != NULL, CPL_ERROR_NULL_INPUT);
551  cpl_ensure_code(aLambda != NULL, CPL_ERROR_NULL_INPUT);
552  cpl_ensure_code(aLsf != NULL, CPL_ERROR_NULL_INPUT);
553 
554  aLineLambda *= aLsf->refraction;
555  aLineLambda += aLsf->offset;
556 
557  double slit_width = aLsf->slit_width;
558  double bin_width = aLsf->bin_width;
559  double width = muse_cplarray_poly1d_double(aLineLambda - aLsf->lambda_ref,
560  aLsf->lsf_width);
561  double h3 = muse_cplarray_poly1d_double(aLineLambda - aLsf->lambda_ref,
562  aLsf->hermit[0]);
563  double h4 = muse_cplarray_poly1d_double(aLineLambda - aLsf->lambda_ref,
564  aLsf->hermit[1]);
565  double h5 = muse_cplarray_poly1d_double(aLineLambda - aLsf->lambda_ref,
566  aLsf->hermit[2]);
567  double h6 = muse_cplarray_poly1d_double(aLineLambda - aLsf->lambda_ref,
568  aLsf->hermit[3]);
569 
570  cpl_array *coeff = cpl_array_new(5, CPL_TYPE_DOUBLE);
571  cpl_array_set(coeff, 4, 2 * sqrt(5) * h6);
572  cpl_array_set(coeff, 3, 2 * sqrt(15) * h5);
573  cpl_array_set(coeff, 2, 5 * sqrt(6) * h4 - 6 * sqrt(5) * h6);
574  cpl_array_set(coeff, 1, 10 * sqrt(3) * h3 - 3 * sqrt(15) * h5);
575  cpl_array_set(coeff, 0, -2.5 * sqrt(6) * h4 + 1.5 * sqrt(5) * h6);
576 
577  int imin = muse_cplarray_find_sorted(aLambda,
578  aLineLambda - (5 * width +
579  0.6*(bin_width + slit_width)));
580  int imax = muse_cplarray_find_sorted(aLambda,
581  aLineLambda + (5 * width +
582  0.6*(bin_width + slit_width)));
583  if (imax < imin) {
584  cpl_array_delete(coeff);
585  return CPL_ERROR_NONE;
586  }
587 
588  cpl_array *l0 = cpl_array_extract(aLambda, imin, imax-imin+1);
589 
590  cpl_array_subtract_scalar(l0, aLineLambda);
591  cpl_array_divide_scalar(l0, width);
592  bin_width /= 2 * width;
593  slit_width /= 2 * width;
594 
595  cpl_array *x = cpl_array_duplicate(l0);
596  cpl_array *y;
597  cpl_array *y1;
598  cpl_array_add_scalar(x, slit_width + bin_width);
599  y = muse_lsf_G(x, coeff);
600 
601  cpl_array_copy_data_double(x, cpl_array_get_data_double(l0));
602  cpl_array_add_scalar(x, slit_width - bin_width);
603  y1 = muse_lsf_G(x, coeff);
604  cpl_array_subtract(y, y1);
605  cpl_array_delete(y1);
606 
607  cpl_array_copy_data_double(x, cpl_array_get_data_double(l0));
608  cpl_array_add_scalar(x, -slit_width + bin_width);
609  y1 = muse_lsf_G(x, coeff);
610  cpl_array_subtract(y, y1);
611  cpl_array_delete(y1);
612 
613  cpl_array_copy_data_double(x, cpl_array_get_data_double(l0));
614  cpl_array_add_scalar(x, -slit_width - bin_width);
615  y1 = muse_lsf_G(x, coeff);
616  cpl_array_delete(x);
617  cpl_array_add(y, y1);
618  cpl_array_delete(y1);
619 
620 // cpl_array_divide_scalar(y, sqrt(CPL_MATH_PI * width / 2));
621  cpl_array_multiply_scalar(y, width);
622  cpl_array_multiply_scalar(y, aLineFlux);
623 
624  muse_cplarray_add_window(aSpectrum, imin, y);
625  cpl_array_delete(y);
626  cpl_array_delete(l0);
627  cpl_array_delete(coeff);
628 
629  return CPL_ERROR_NONE;
630 }
631 
632 /*----------------------------------------------------------------------------*/
651 /*----------------------------------------------------------------------------*/
652 cpl_array *
653 muse_lsf_spectrum_get_lines(const cpl_array *aLambda,
654  const cpl_array *aLinesLambda,
655  const cpl_array *aLinesFlux,
656  const muse_lsf_params *aLsf) {
657  cpl_ensure(aLambda != NULL, CPL_ERROR_NULL_INPUT, NULL);
658  cpl_ensure(aLinesLambda != NULL, CPL_ERROR_NULL_INPUT, NULL);
659  cpl_ensure(aLinesFlux != NULL, CPL_ERROR_NULL_INPUT, NULL);
660  cpl_ensure(aLsf != NULL, CPL_ERROR_NULL_INPUT, NULL);
661 
662  int nrows = cpl_array_get_size(aLambda);
663  cpl_array *spectrum = cpl_array_new(nrows, CPL_TYPE_DOUBLE);
664  cpl_array_fill_window_double(spectrum, 0, nrows, 0.0);
665  int i;
666  int linescount = cpl_array_get_size(aLinesLambda);
667  int errold = errno;
668  feclearexcept(FE_UNDERFLOW);
669  for (i = 0; i < linescount; i++) {
670  double lambda = cpl_array_get(aLinesLambda, i, NULL);
671  double flux = cpl_array_get(aLinesFlux, i, NULL);
672  muse_lsf_line_apply(aLambda, spectrum, aLsf, lambda, flux);
673  }
674  if (fetestexcept(FE_UNDERFLOW)) {
675  errno = errold;
676  feclearexcept(FE_UNDERFLOW);
677  }
678  return spectrum;
679 }
680 
681 /*----------------------------------------------------------------------------*/
706 /*----------------------------------------------------------------------------*/
707 double
708 muse_lsf_fwhm_lambda(const muse_lsf_params *aDP, double aLambda,
709  double aFlux, double aSampling, unsigned int aLength,
710  FILE *aOutstream)
711 {
712  cpl_ensure(aDP, CPL_ERROR_NULL_INPUT, 0.);
713  /* if the lsf_width was not fitted correctly this value would be useless */
714  cpl_ensure(cpl_array_get(aDP->lsf_width, 0, NULL) != 1 &&
715  cpl_array_get(aDP->lsf_width, 0, NULL) != 0,
716  CPL_ERROR_ILLEGAL_INPUT, 0.);
717 
718  cpl_array *llambda = cpl_array_new(1, CPL_TYPE_DOUBLE),
719  *lflux = cpl_array_new(1, CPL_TYPE_DOUBLE);
720  cpl_array_set_double(llambda, 0, aLambda);
721  cpl_array_set_double(lflux, 0, aFlux);
722 
723  cpl_array *lambda = cpl_array_new(aLength, CPL_TYPE_DOUBLE);
724  cpl_size i;
725  for (i = 0; i < aLength; i++) {
726  cpl_array_set_double(lambda, i, (i + 1. - aLength/2) * aSampling + aLambda);
727  } /* for i */
728 
729  cpl_array *spec = muse_lsf_spectrum_get_lines(lambda, llambda, lflux, aDP);
730  cpl_size imax;
731  cpl_array_get_maxpos(spec, &imax);
732  double max = cpl_array_get_max(spec),
733  vl = 0., vr = 0.; /* exact values at half maximum */
734  i = imax;
735  while (--i >= 0 &&
736  (vl = cpl_array_get_double(spec, i, NULL)) > max/2.) ;
737  cpl_size il = i; /* first point with value below half max */
738  i = imax;
739  while (++i < aLength &&
740  (vr = cpl_array_get_double(spec, i, NULL)) > max/2.) ;
741  cpl_size ir = i;
742  /* compute the FWHM that we really want; do not use (ir - il + 1) *
743  * because on average we stepped two half pixels too far... */
744  double fwhm = (ir - il) * aSampling;
745  if (aOutstream) {
746  fprintf(aOutstream, " %.1f: %02d.%02d\t%f %f %f\t%f +/- %f (%f) %f..%f "
747  "(%"CPL_SIZE_FORMAT")\t%f (%"CPL_SIZE_FORMAT") %f (%"CPL_SIZE_FORMAT
748  ") ==> %f\n", aLambda, aDP->ifu, aDP->slice, aDP->lambda_ref,
749  aDP->slit_width, aDP->bin_width, cpl_array_get_mean(spec),
750  cpl_array_get_stdev(spec), cpl_array_get_median(spec),
751  cpl_array_get_min(spec), max, imax, vl, il, vr, ir, fwhm);
752  }
753 #if 0
754  cpl_array_dump(spec, 0, aLength, stdout);
755 #endif
756 #if 0
757  cpl_vector *vspec = cpl_vector_wrap(aLength, cpl_array_get_data_double(spec));
758  cpl_vector_save(vspec, "vspec.fits", CPL_TYPE_DOUBLE, NULL, CPL_IO_CREATE);
759  cpl_vector_unwrap(vspec);
760 #endif
761  cpl_array_delete(spec);
762  cpl_array_delete(lambda);
763  cpl_array_delete(llambda);
764  cpl_array_delete(lflux);
765  cpl_ensure(il > 0 && ir < aLength, CPL_ERROR_ILLEGAL_OUTPUT, 0.);
766  return fwhm;
767 } /* muse_lsf_fwhm_lambda() */
768 
void muse_lsf_params_delete_one(muse_lsf_params *aParams)
Delete an allocated muse_lsf_params structure.
Definition: muse_lsf.c:128
cpl_error_code muse_lsf_params_save(const muse_lsf_params **aLsf, const char *aFile)
Save slice LSF parameters to the extension "slice" on disk.
Definition: muse_lsf.c:214
muse_lsf_params * muse_lsf_params_get(muse_lsf_params **aParams, int aIFU, int aSlice)
Get the slice LSF parameters for one slice.
Definition: muse_lsf.c:456
cpl_error_code muse_cplarray_poly1d(cpl_array *aArray, const cpl_array *aCoeff)
Apply a polynomial to an array.
cpl_table * muse_cpltable_load(const char *aFile, const char *aExtension, const muse_cpltable_def aDefinition[])
Load a table from disk (and check against definition).
cpl_array * muse_lsf_spectrum_get_lines(const cpl_array *aLambda, const cpl_array *aLinesLambda, const cpl_array *aLinesFlux, const muse_lsf_params *aLsf)
Set the lines spectrum from the lines table and the detector LSF.
Definition: muse_lsf.c:653
double bin_width
Bin width.
Definition: muse_lsf.h:63
cpl_array * hermit[MAX_HERMIT_ORDER]
coefficients for the damped gauss-hermitean parametrization
Definition: muse_lsf.h:67
muse_lsf_params * muse_lsf_params_new(cpl_size n_sensit, cpl_size n_lsf_width, cpl_size n_hermit)
Create a new lsf_params structure.
Definition: muse_lsf.c:74
cpl_array * sensitivity
Relative detector sensitivity parametrization.
Definition: muse_lsf.h:59
cpl_error_code muse_cplarray_exp(cpl_array *aArray)
Compute the exponential function of array elements.
double muse_lsf_fwhm_lambda(const muse_lsf_params *aDP, double aLambda, double aFlux, double aSampling, unsigned int aLength, FILE *aOutstream)
Measure the FWHM of an LSF at a given wavelength.
Definition: muse_lsf.c:708
double slit_width
Slit width.
Definition: muse_lsf.h:61
cpl_array * muse_cpltable_get_array_copy(cpl_table *aTable, const char *aColumn, cpl_size aRow)
Return the copy of an array of a table cell.
cpl_array * lsf_width
LSF width.
Definition: muse_lsf.h:65
static cpl_error_code muse_lsf_line_apply(const cpl_array *, cpl_array *, const muse_lsf_params *, double, double)
Apply the MUSE LSF function to a single line.
Definition: muse_lsf.c:547
void muse_processing_append_used(muse_processing *aProcessing, cpl_frame *aFrame, cpl_frame_group aGroup, int aDuplicate)
Add a frame to the set of used frames.
cpl_error_code muse_cplarray_erf(cpl_array *aArray)
Compute the error function of array elements.
cpl_size muse_lsf_params_get_size(muse_lsf_params **aParams)
Count the number of entries in the array.
Definition: muse_lsf.c:110
cpl_error_code muse_cplarray_add_window(cpl_array *aDest, cpl_size aStart, const cpl_array *aArray)
Add the value of an array to a window of a table column.
const muse_cpltable_def muse_lsfparams_def[]
Definition of a lsf parameters table.
Definition: muse_lsf.c:172
muse_lsf_params ** muse_lsf_params_load(const char *aFile, muse_lsf_params **aParams, int aIFU)
Load slice LSF parameters from the extension "SLICE_PARAM".
Definition: muse_lsf.c:308
double lambda_ref
Reference wavelength for polynomial parametrizations.
Definition: muse_lsf.h:57
static cpl_array * muse_lsf_G(cpl_array *aX, cpl_array *aCoeffs)
Helper function "G" for integrated damped gauss-hermitean function.
Definition: muse_lsf.c:488
Definition of a cpl table structure.
muse_lsf_params ** muse_processing_lsf_params_load(muse_processing *aProcessing, int aIFU)
Load slice LSF parameters.
Definition: muse_lsf.c:392
cpl_frameset * muse_frameset_find(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU, cpl_boolean aInvert)
return frameset containing data from an IFU/channel with a certain tag
Definition: muse_utils.c:154
cpl_frameset * inputFrames
Structure definition of detector (slice) parameters.
Definition: muse_lsf.h:51
double muse_cplarray_poly1d_double(double aDouble, const cpl_array *aCoeff)
Apply a polynomial to a double value.
cpl_size muse_cplarray_find_sorted(const cpl_array *aArray, double aValue)
Find a row in an array.
void muse_lsf_params_delete(muse_lsf_params **aParams)
Delete an allocated array of muse_lsf_params structure.
Definition: muse_lsf.c:147
cpl_error_code muse_cpltable_append_file(const cpl_table *aTable, const char *aFile, const char *aExtension, const muse_cpltable_def aDefinition[])
Save a table to disk (into a FITS extension)