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