GIRAFFE Pipeline Reference Manual

gifxcalibration.c
1 /* $Id$
2  *
3  * This file is part of the GIRAFFE Pipeline
4  * Copyright (C) 2002-2006 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * $Author$
23  * $Date$
24  * $Revision$
25  * $Name$
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 
33 #include <math.h>
34 
35 #include <cxmacros.h>
36 #include <cxstrutils.h>
37 
38 #include <cpl_propertylist.h>
39 #include <cpl_mask.h>
40 #include <cpl_table.h>
41 #include <cpl_msg.h>
42 
43 #include "gialias.h"
44 #include "gierror.h"
45 #include "giarray.h"
46 #include "gimessages.h"
47 #include "giastroutils.h"
48 #include "giutils.h"
49 #include "gifxcalibration.h"
50 
51 
60 /*
61  * @brief
62  * Compute the angular separation of two objects.
63  *
64  * @param ra_0 Rightascension of the first object in degrees.
65  * @param dec_0 Declination of the first object in degrees.
66  * @param ra_1 Rightascension of the second object in degrees.
67  * @param dec_1 Declination of the second object in degrees.
68  *
69  * @return
70  * The angular separation of the two objects in arcsec.
71  *
72  * Computes the angular separation of the two coordinate pairs given.
73  */
74 
75 inline static cxdouble
76 _giraffe_compute_separation(cxdouble ra_0, cxdouble dec_0,
77  cxdouble ra_1, cxdouble dec_1)
78 {
79 
80  const cxdouble deg2rad = CX_PI / 180.;
81 
82  cxdouble dist = 0.;
83 
84 
85  ra_0 *= deg2rad;
86  ra_1 *= deg2rad;
87  dec_0 *= deg2rad;
88  dec_1 *= deg2rad;
89 
90  dist = sin(dec_0) * sin(dec_1) +
91  cos(dec_0) * cos(dec_1) * cos(ra_0 - ra_1);
92 
93  dist = CX_CLAMP(dist, -1., 1.);
94  dist = acos(dist) / deg2rad * 3600.;
95 
96  return dist;
97 
98 }
99 
100 
101 /*
102  * @brief
103  * Spline interpolation using Hermite polynomials.
104  *
105  * @param xp Abscissa value for which the interpolation is computed.
106  * @param x Array of abscissa values
107  * @param y Array of ordinate values
108  * @param istart Index of the first array element used.
109  *
110  * @return
111  * The interpolated value of the column @em ylabel.
112  *
113  * The function performs a spline interpolation using Hermite polynomials.
114  * The array @em x and @em y provide the abscissa and ordinate values of
115  * the function to be interpolated. The interpolation is computed for the
116  * abscissa value @em xp.
117  */
118 
119 inline static cxdouble
120 _giraffe_spline_hermite(cxdouble xp, const cxdouble* x, const cxdouble* y,
121  cxint n, cxint* istart)
122 {
123 
124  cxint i = 0;
125 
126  cxdouble yp = 0.;
127  cxdouble yp1 = 0.;
128  cxdouble yp2 = 0.;
129  cxdouble xpi = 0.;
130  cxdouble xpi1 = 0.;
131  cxdouble l1 = 0.;
132  cxdouble l2 = 0.;
133  cxdouble lp1 = 0.;
134  cxdouble lp2 = 0.;
135 
136 
137  if ((x[0] <= x[n - 1]) && ((xp < x[0]) || (xp > x[n - 1]))) {
138  return 0.;
139  }
140 
141  if ((x[0] > x[n - 1]) && ((xp > x[0]) || (xp < x[n - 1]))) {
142  return 0.;
143  }
144 
145  if (x[0] <= x[n - 1]) {
146 
147  for (i = *istart + 1; i <= n && xp >= x[i - 1]; ++i) {
148  ;
149  }
150 
151  }
152  else {
153 
154  for (i = *istart + 1; i <= n && xp <= x[i - 1]; ++i) {
155  ;
156  }
157 
158  }
159 
160  *istart = i;
161  --i;
162 
163 
164  lp1 = 1. / (x[i - 1] - x[i]);
165  lp2 = -lp1;
166 
167  if (i == 1) {
168  yp1 = (y[1] - y[0]) / (x[1] - x[0]);
169  }
170  else {
171  yp1 = (y[i] - y[i - 2]) / (x[i] - x[i - 2]);
172  }
173 
174  if (i >= n - 1) {
175  yp2 = (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
176  }
177  else {
178  yp2 = (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]);
179  }
180 
181 
182  xpi = xp - x[i - 1];
183  xpi1 = xp - x[i];
184 
185  l1 = xpi1 * lp1;
186  l2 = xpi * lp2;
187 
188  yp = y[i - 1] * (1. - 2. * lp1 * xpi) * l1 * l1 +
189  y[i] * (1. - 2. * lp2 * xpi1) * l2 * l2 +
190  yp1 * xpi * l1 * l1 +
191  yp2 * xpi1 * l2 * l2;
192 
193  return yp;
194 
195 }
196 
197 
198 /*
199  * @brief
200  * Spline interpolation of a table column.
201  *
202  * @param tbl Table with column to interpolate
203  * @param xlabel Column label of the abscissa column
204  * @param ylabel Column label of the ordinate column
205  * @param xp Abscissa value for which the interpolation is computed.
206  * @param istart Row index of the first table row used.
207  *
208  * @return
209  * The interpolated value of the column @em ylabel.
210  *
211  * The function performs a spline interpolation using Hermite polynomials
212  * of the table column given by the column label @em ylabel. The column
213  * @em xlabel are the corresponding abscissa values, and @em xp is the
214  * abscissa value for which the interpolation is computed.
215  */
216 
217 inline static cxdouble
218 _giraffe_interpolate_spline_hermite(const cpl_table* tbl,
219  const cxchar* xlabel,
220  const cxchar* ylabel,
221  cxdouble xp, cxint* istart)
222 {
223 
224  cxint n = cpl_table_get_nrow(tbl);
225 
226  const cxdouble* x = cpl_table_get_data_double_const(tbl, xlabel);
227  const cxdouble* y = cpl_table_get_data_double_const(tbl, ylabel);
228 
229 
230  return _giraffe_spline_hermite(xp, x, y, n ,istart);
231 
232 }
233 
234 
235 /*
236  * @brief
237  * Creates a 2d table of a flux standard star catalog spectrum
238  *
239  * @param catalog 3d table of flux standard reference spectra.
240  * @param row Table row from which the reference spectrum is extracted.
241  *
242  * @return
243  * The function returns a newly allocated 2d table containing the
244  * tabulated spectrum of the flux standard, or @c NULL if an error
245  * occurred.
246  *
247  * The input catalog @em catalog contains one flux standard spectrum per
248  * table row. The function takes a spectrum from the the given row @em row
249  * and stores it in an ordinary table structures. The output table contains
250  * 3 columns, which give the wavelength and the width of each wavelength bin,
251  * and the reference flux at this wavelength. The column names are
252  * "LAMBDA", "BIN_WIDTH" and "F_LAMBDA" respectively.
253  *
254  * If the catalog contains wavelength in Angstrom, all 3 columns are
255  * converted to nano meters.
256  */
257 
258 inline static cpl_table*
259 _giraffe_create_flux_table(const cpl_table* catalog, cxint row)
260 {
261 
262  const cxchar* const _id = "_giraffe_create_flux_table";
263 
264 
265  const cxchar* columns[] = {"LAMBDA", "BIN_WIDTH", "F_LAMBDA"};
266  const cxchar* units = NULL;
267 
268  cxint ndata = cpl_table_get_int(catalog, "NDATA", row, NULL);
269 
270  cxsize i = 0;
271 
272  const cxdouble ang2nm = 0.1; /* Conversion factor Angstroem to nm */
273 
274  cpl_table* flux = NULL;
275 
276 
277 
278  if (ndata <= 0) {
279  return NULL;
280  }
281 
282  giraffe_error_push();
283 
284  flux = cpl_table_new(ndata);
285 
286  for (i = 0; i < CX_N_ELEMENTS(columns); ++i) {
287 
288  const cpl_array* data = cpl_table_get_array(catalog, columns[i], row);
289 
290  cpl_type type = cpl_table_get_column_type(catalog, columns[i]);
291 
292 
293  cpl_table_new_column(flux, columns[i], CPL_TYPE_DOUBLE);
294 
295  if ((data != NULL) && (ndata <= cpl_array_get_size(data))) {
296 
297  switch (type & ~CPL_TYPE_POINTER) {
298  case CPL_TYPE_FLOAT:
299  {
300 
301  cxint j = 0;
302 
303  register cxdouble value = 0.;
304 
305 
306  for (j = 0; j < ndata; ++j) {
307  value = cpl_array_get_float(data, j, NULL);
308  cpl_table_set_double(flux, columns[i], j, value);
309  }
310 
311  break;
312 
313  }
314 
315  case CPL_TYPE_DOUBLE:
316  {
317 
318  cxint j = 0;
319 
320  register cxdouble value = 0.;
321 
322 
323  for (j = 0; j < ndata; ++j) {
324  value = cpl_array_get_double(data, j, NULL);
325  cpl_table_set_double(flux, columns[i], j, value);
326  }
327 
328  break;
329 
330  }
331 
332  default:
333  {
334  cpl_error_set(_id, CPL_ERROR_INVALID_TYPE);
335  break;
336 
337  }
338  }
339 
340  }
341 
342  }
343 
344 
345  /*
346  * The fluxes of the catalog spectra are scaled by a factor of 1.e+16.
347  * This scaling is reversed here.
348  */
349 
350  cpl_table_multiply_scalar(flux, columns[2], 1.e-16);
351 
352 
353  /*
354  * If catalog wavelengths are given in terms of Angstrom all columns
355  * are converted to nm, assuming that the catalog columns are consistent.
356  * If no unit is given, or the unit string is empty, it is assumed that
357  * the table data is already given in nm.
358  */
359 
360  units = cpl_table_get_column_unit(catalog, columns[0]);
361 
362  if ((units != NULL) && (cx_strncasecmp(units, "ang", 3) == 0)) {
363 
364  cpl_msg_debug(_id, "Found units '%s'. Converting flux standard "
365  "from Angstrom to nano meters", units);
366 
367  cpl_table_multiply_scalar(flux, columns[0], ang2nm);
368  cpl_table_set_column_unit(flux, columns[0], "nm");
369 
370  cpl_table_multiply_scalar(flux, columns[1], ang2nm);
371  cpl_table_set_column_unit(flux, columns[1], "nm");
372 
373  cpl_table_divide_scalar(flux, columns[2], ang2nm);
374  cpl_table_set_column_unit(flux, columns[2], "erg/s/cm^2/nm");
375 
376  }
377  else {
378 
379  if (units == NULL) {
380 
381  cpl_msg_debug(_id, "No units for wavelength column. Assuming "
382  "nano meters.");
383 
384  }
385  else {
386 
387  cpl_msg_debug(_id, "Found unknown units ('%s') for wavelength "
388  "column. Assuming nano meters.", units);
389 
390  }
391 
392  }
393 
394 
395  if (cpl_error_get_code() != CPL_ERROR_NONE) {
396  cpl_table_delete(flux);
397  flux = NULL;
398 
399  return NULL;
400  }
401 
402  giraffe_error_pop();
403 
404  return flux;
405 
406 }
407 
408 
409 /*
410  * @brief
411  * Create a standardized extinction table from input table.
412  *
413  * @param extinction Input extinction table.
414  *
415  * @return
416  * The created standard extinction table, or @c NULL if an error occurred.
417  *
418  * The function creates a new extinction table from the input table. If the
419  * input table contains wavelength in units of Angstroem the wavelength are
420  * converted to nano meters. The column of the extinction coefficients to
421  * use is copied from the input table and renamed to "EXTINCTION".
422  */
423 
424 inline static cpl_table*
425 _giraffe_setup_extinction(const cpl_table* extinction)
426 {
427 
428  const cxchar* const _id = "_giraffe_setup_extinction";
429 
430  const cxchar* site = NULL;
431  const cxchar* units = NULL;
432  const cxchar* sites[] = {"PARANAL", "LA_SILLA", NULL};
433 
434  cxint i = 0;
435  cxint rows = 0;
436 
437  const cxdouble ang2nm = 0.1; /* Conversion factor Angstroem to nm */
438 
439  cxdouble scale = 1.;
440 
441  cpl_table* _extinction = NULL;
442 
443 
444  if (cpl_table_has_column(extinction, "LAMBDA") == FALSE) {
445 
446  cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
447  return NULL;
448 
449  }
450 
451 
452  /*
453  * Convert wavelength units from Angstroem to nano meters
454  */
455 
456  units = cpl_table_get_column_unit(extinction, "LAMBDA");
457 
458  if ((units != NULL) && (cx_strncasecmp(units, "ang", 3) == 0)) {
459 
460  scale = ang2nm;
461 
462  cpl_msg_debug(_id, "Found units '%s'. Converting wavelength"
463  "from Angstrom to nano meters", units);
464 
465  }
466  else {
467 
468  if (units == NULL) {
469 
470  cpl_msg_debug(_id, "No units for wavelength column. "
471  "Assuming nano meters.");
472 
473  }
474  else {
475 
476  if (cx_strncasecmp(units, "nm", 2) == 0) {
477 
478  cpl_msg_debug(_id, "Found units nano meters ('%s') for "
479  "wavelength column.", units);
480 
481  }
482  else {
483 
484  cpl_msg_debug(_id, "Found unknown units ('%s') for "
485  "wavelength column. Assuming nano meters.", units);
486 
487  }
488 
489  }
490 
491  }
492 
493 
494  /*
495  * Select the extinction coefficients for the
496  */
497 
498  while ((site == NULL) && (sites[i] != NULL)) {
499 
500  if (cpl_table_has_column(extinction, sites[i]) == TRUE) {
501 
502  site = sites[i];
503  break;
504 
505  }
506 
507  ++i;
508 
509  }
510 
511  if (site == NULL) {
512  cpl_msg_debug(_id, "No matching observatory site found!");
513  return NULL;
514  }
515 
516 
517  /*
518  * Setup the final extinction table
519  */
520 
521  rows = cpl_table_get_nrow(extinction);
522 
523  giraffe_error_push();
524 
525  _extinction = cpl_table_new(rows);
526  cpl_table_new_column(_extinction, "LAMBDA", CPL_TYPE_DOUBLE);
527  cpl_table_set_column_unit(_extinction, "LAMBDA", "nm");
528 
529  cpl_table_new_column(_extinction, "EXTINCTION", CPL_TYPE_DOUBLE);
530  cpl_table_set_column_unit(_extinction, "EXTINCTION", "mag/airmass");
531 
532  if (cpl_error_get_code() != CPL_ERROR_NONE) {
533 
534  cpl_table_delete(_extinction);
535  _extinction = NULL;
536 
537  return NULL;
538 
539  }
540 
541  giraffe_error_pop();
542 
543 
544  switch (cpl_table_get_column_type(extinction, "LAMBDA")) {
545  case CPL_TYPE_FLOAT:
546  {
547  for (i = 0; i < rows; ++i) {
548 
549  register cxdouble lambda = cpl_table_get_float(extinction,
550  "LAMBDA", i, NULL);
551 
552  cpl_table_set_double(_extinction, "LAMBDA", i,
553  scale * lambda);
554 
555  }
556  break;
557  }
558 
559  case CPL_TYPE_DOUBLE:
560  {
561  for (i = 0; i < rows; ++i) {
562 
563  register cxdouble lambda = cpl_table_get_double(extinction,
564  "LAMBDA", i, NULL);
565 
566  cpl_table_set_double(_extinction, "LAMBDA", i,
567  scale * lambda);
568 
569  }
570  break;
571  }
572 
573  default:
574  {
575  cpl_table_delete(_extinction);
576  _extinction = NULL;
577 
578  cpl_msg_debug(_id, "Column type (%d) is not supported for "
579  "extinction tables!",
580  cpl_table_get_column_type(extinction, "LAMBDA"));
581 
582  return NULL;
583  break;
584  }
585  }
586 
587  switch (cpl_table_get_column_type(extinction, site)) {
588  case CPL_TYPE_FLOAT:
589  {
590  for (i = 0; i < rows; ++i) {
591 
592  register cxdouble aext = cpl_table_get_float(extinction,
593  site, i, NULL);
594 
595  cpl_table_set_double(_extinction, "EXTINCTION", i, aext);
596 
597  }
598  break;
599  }
600 
601  case CPL_TYPE_DOUBLE:
602  {
603  for (i = 0; i < rows; ++i) {
604 
605  register cxdouble aext = cpl_table_get_double(extinction,
606  site, i, NULL);
607 
608  cpl_table_set_double(_extinction, "EXTINCTION", i, aext);
609 
610  }
611  break;
612  }
613 
614  default:
615  {
616  cpl_table_delete(_extinction);
617  _extinction = NULL;
618 
619  cpl_msg_debug(_id, "Column type (%d) is not supported for "
620  "extinction tables!",
621  cpl_table_get_column_type(extinction, site));
622 
623  return NULL;
624  break;
625  }
626  }
627 
628 
629  return _extinction;
630 
631 }
632 
633 
634 inline static cpl_image*
635 _giraffe_compute_mean_sky(const cpl_image* spectra, const cpl_table* fibers)
636 {
637 
638  cxint i = 0;
639  cxint ns = cpl_image_get_size_x(spectra);
640  cxint nw = cpl_image_get_size_y(spectra);
641  cxint nsky = 0;
642  cxint* sky_fibers = cx_calloc(ns, sizeof(cxint));
643 
644  const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
645 
646  cxdouble* _sky = NULL;
647 
648  cpl_image* sky = NULL;
649 
650 
651  cx_assert(ns == cpl_table_get_nrow(fibers));
652 
653 
654  /*
655  * Count the available sky fibers, and save their position indices.
656  */
657 
658  for (i = 0; i < ns; ++i) {
659 
660  const cxchar* s = cpl_table_get_string(fibers, "Retractor", i);
661 
662  if (strstr(s, "-Sky") != NULL) {
663  sky_fibers[nsky] =
664  cpl_table_get_int(fibers, "INDEX", i, NULL) - 1;
665  ++nsky;
666  }
667 
668  }
669 
670  if (nsky == 0) {
671 
672  cx_free(sky_fibers);
673  sky_fibers = NULL;
674 
675  return NULL;
676 
677  }
678 
679 
680  /*
681  * Compute the mean sky. If more than 2 sky fibers were used, the mean
682  * sky is the median of all sky fibers, in order to get rid of spurious
683  * artifacts, cosmics for instance. If there are less than 3 sky fibers
684  * the mean sky is the simple average of all sky fibers.
685  */
686 
687  sky = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
688  _sky = cpl_image_get_data_double(sky);
689 
690  if (nsky > 2) {
691 
692  if (cpl_table_has_column(fibers, "TRANSMISSION") == TRUE) {
693 
694  cxdouble* sky_raw = cx_calloc(nsky, sizeof(cxdouble));
695 
696 
697  for (i = 0; i < nw; ++i) {
698 
699  register cxint j = 0;
700 
701 
702  for (j = 0; j < nsky; ++j) {
703 
704  cxdouble t = cpl_table_get_double(fibers, "TRANSMISSION",
705  sky_fibers[j], NULL);
706 
707 
708  cx_assert(t > 0.);
709 
710  sky_raw[j] = _spectra[i * ns + sky_fibers[j]] / t;
711 
712  }
713 
714  _sky[i] = giraffe_array_median(sky_raw, nsky);
715 
716  }
717 
718  cx_free(sky_raw);
719  sky_raw = NULL;
720 
721  }
722  else {
723 
724  cxdouble* sky_raw = cx_calloc(nsky, sizeof(cxdouble));
725 
726 
727  for (i = 0; i < nw; ++i) {
728 
729  register cxint j = 0;
730 
731 
732  for (j = 0; j < nsky; ++j) {
733  sky_raw[j] = _spectra[i * ns + sky_fibers[j]];
734  }
735 
736  _sky[i] = giraffe_array_median(sky_raw, nsky);
737 
738  }
739 
740  cx_free(sky_raw);
741  sky_raw = NULL;
742 
743  }
744 
745  }
746  else {
747 
748  if (cpl_table_has_column(fibers, "TRANSMISSION") == TRUE) {
749 
750  for (i = 0; i < nsky; ++i) {
751 
752  register cxint j = 0;
753 
754  cxdouble t = cpl_table_get_double(fibers, "TRANSMISSION",
755  sky_fibers[i], NULL);
756 
757 
758  cx_assert(t > 0.);
759 
760  for (j = 0; j < nw; ++j) {
761  _sky[j] += _spectra[j * ns + sky_fibers[i]] / t;
762  }
763 
764  }
765 
766  }
767  else {
768 
769  for (i = 0; i < nsky; ++i) {
770 
771  register cxint j = 0;
772 
773 
774  for (j = 0; j < nw; ++j) {
775  _sky[j] += _spectra[j * ns + sky_fibers[i]];
776  }
777 
778  }
779 
780  }
781 
782  cpl_image_divide_scalar(sky, nsky);
783 
784  }
785 
786  cx_free(sky_fibers);
787  sky_fibers = NULL;
788 
789  return sky;
790 
791 }
792 
793 
794 inline static cpl_image*
795 _giraffe_subtract_mean_sky(const cpl_image* spectra, const cpl_image* sky,
796  const cpl_table* fibers)
797 {
798 
799  cxint i = 0;
800  cxint ns = cpl_image_get_size_x(spectra);
801  cxint nw = cpl_image_get_size_y(spectra);
802 
803  const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
804  const cxdouble* _sky = cpl_image_get_data_double_const(sky);
805 
806  cpl_image* result = cpl_image_new(ns, nw, CPL_TYPE_DOUBLE);
807 
808  cxdouble* _result = cpl_image_get_data_double(result);
809 
810 
811  cx_assert((fibers == NULL) || (ns == cpl_table_get_nrow(fibers)));
812  cx_assert(nw == cpl_image_get_size_y(sky));
813 
814 
815  /*
816  * If a fiber table is present and it contains the relative fiber
817  * transmission data, the sky spectrum is corrected for that before
818  * it is subtracted. Otherwise a fiber transmission of 1. is assumed.
819  */
820 
821  if ((fibers != NULL) &&
822  (cpl_table_has_column(fibers, "TRANSMISSION") == TRUE)) {
823 
824  for (i = 0; i < ns; ++i) {
825 
826  register cxint j = 0;
827  register cxint k =
828  cpl_table_get_int(fibers, "INDEX", i, NULL) - 1;
829 
830  cxdouble t = cpl_table_get_double(fibers, "TRANSMISSION",
831  k, NULL);
832 
833 
834  for (j = 0; j < nw; ++j) {
835 
836  register cxint l = j * ns + i;
837 
838  _result[l] += _spectra[l] - t * _sky[j];
839 
840  }
841 
842  }
843 
844  }
845  else {
846 
847  for (i = 0; i < ns; ++i) {
848 
849  register cxint j = 0;
850 
851  for (j = 0; j < nw; ++j) {
852 
853  register cxint k = j * ns + i;
854 
855  _result[k] += _spectra[k] - _sky[j];
856 
857  }
858 
859  }
860 
861  }
862 
863  return result;
864 
865 }
866 
867 
868 inline static cpl_image*
869 _giraffe_integrate_flux(const cpl_image* spectra, const cpl_table* fibers)
870 {
871 
872  cxint i = 0;
873  cxint nw = cpl_image_get_size_y(spectra);
874  cxint ns = cpl_image_get_size_x(spectra);
875 
876  cpl_image* result = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
877 
878 
879  cx_assert(ns == cpl_table_get_nrow(fibers));
880 
881  for (i = 0; i < ns; ++i) {
882 
883  const cxchar* s = cpl_table_get_string(fibers, "Retractor", i);
884 
885  cxint rp = cpl_table_get_int(fibers, "RP", i, NULL);
886 
887 
888  /*
889  * Add up the flux of all fibers for each wavelength bin,
890  * ignoring all sky and simultaneous calibration fibers.
891  */
892 
893  if ((rp != -1) && (strstr(s, "-Sky") == NULL)) {
894 
895  register cxint j = 0;
896 
897  const cxdouble* _spectra =
898  cpl_image_get_data_double_const(spectra);
899 
900  cxdouble* _result = cpl_image_get_data_double(result);
901 
902 
903  for (j = 0; j < nw; ++j) {
904  _result[j] += _spectra[j * ns + i];
905  }
906 
907  }
908 
909  }
910 
911  return result;
912 
913 }
914 
915 
916 /*
917  * @brief
918  * Correct a spectrum for the atmospheric extinction.
919  *
920  * @param spectrum The spectrum to be corrected.
921  * @param properties Properties of the input spectrum.
922  * @param extinction The table containing the atmospheric extinction.
923  *
924  * @return
925  * The function returns 0 on success, or a non-zero value otherwise.
926  *
927  * The function corrects the input spectrum @em spectrum for the atmospheric
928  * extinction. The atmospheric extinction as a function of wavelength is
929  * taken from the table @em extinction.
930  *
931  * The extinction table is expected to contain a wavelength column "LAMBDA"
932  * in units of nano meters. To support legacy tables "Angstroem" are
933  * also accepted, but not recommended.
934  */
935 
936 inline static cxint
937 _giraffe_correct_extinction(cpl_image* spectrum, cpl_propertylist* properties,
938  const cpl_table* extinction)
939 {
940 
941  const cxchar* const _id = "_giraffe_correct_extinction";
942 
943 
944  cxint i = 0;
945  cxint status = 0;
946  cxint nw = 0;
947 
948  cxdouble alpha = 0.;
949  cxdouble delta = 0.;
950  cxdouble lst = 0.;
951  cxdouble latitude = 0.;
952  cxdouble exptime = 0.;
953  cxdouble wlstart = 0.;
954  cxdouble wlstep = 0.;
955  cxdouble airmass = -1.;
956  cxdouble* flx = NULL;
957 
958  cpl_table* _extinction = NULL;
959 
960 
961  if ((spectrum == NULL) || (properties == NULL) || (extinction == NULL)) {
962  return -1;
963  }
964 
965 
966  if (cpl_image_get_size_x(spectrum) != 1) {
967 
968  cpl_msg_debug(_id, "Input spectrum is not a 1d spectrum!");
969  return -1;
970 
971  }
972 
973 
974  /*
975  * Setup the extinction table. This will convert wavelengths from
976  * Angstroem to nano meters if it is necessary, and set the
977  * name of the column of extinction coefficients to "Extinction".
978  */
979 
980  _extinction = _giraffe_setup_extinction(extinction);
981 
982  if (_extinction == NULL) {
983  return 1;
984  }
985 
986 
987  /*
988  * Get the wavelength grid parameters of the observered spectrum
989  */
990 
991  if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
992  (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
993 
994  cpl_msg_debug(_id, "Observed spectrum does not have a valid "
995  "wavelength grid!");
996 
997  cpl_table_delete(_extinction);
998  _extinction = NULL;
999 
1000  return 2;
1001 
1002  }
1003 
1004  wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1005  wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1006 
1007 
1008  /*
1009  * Compute the airmass for the time of the observation
1010  */
1011 
1012  giraffe_error_push();
1013 
1014  alpha = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1015  delta = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1016  lst = cpl_propertylist_get_double(properties, GIALIAS_LST);
1017  latitude = cpl_propertylist_get_double(properties, GIALIAS_TEL_LAT);
1018  exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
1019 
1020  status = cpl_error_get_code();
1021 
1022  if (status == CPL_ERROR_NONE) {
1023 
1024  airmass = giraffe_compute_airmass(alpha, delta, lst, exptime,
1025  latitude);
1026 
1027  }
1028 
1029  if ((airmass < 1.) || (status != CPL_ERROR_NONE)) {
1030 
1031  cxbool start = cpl_propertylist_has(properties,
1032  GIALIAS_AIRMASS_START);
1033  cxbool end = cpl_propertylist_has(properties,
1034  GIALIAS_AIRMASS_END);
1035 
1036  if ((start == FALSE) || (end == FALSE)) {
1037 
1038  cpl_msg_debug(_id, "Unable to compute airmass of the "
1039  "observation!");
1040 
1041  cpl_table_delete(_extinction);
1042  _extinction = NULL;
1043 
1044  return 3;
1045 
1046  }
1047  else {
1048 
1049  airmass = 0.5 * (cpl_propertylist_get_double(properties,
1050  GIALIAS_AIRMASS_START) +
1051  cpl_propertylist_get_double(properties,
1052  GIALIAS_AIRMASS_END));
1053 
1054  }
1055 
1056  }
1057 
1058  giraffe_error_pop();
1059 
1060 
1061  /*
1062  * Apply the correction to the input spectrum
1063  */
1064 
1065  nw = cpl_image_get_size_y(spectrum);
1066  flx = cpl_image_get_data_double(spectrum);
1067 
1068  for (i = 0; i < nw; ++i) {
1069 
1070  cxint first = 0;
1071 
1072  cxdouble wlen = wlstart + (i - 1) * wlstep;
1073  cxdouble ext = 1.;
1074 
1075 
1076  giraffe_error_push();
1077 
1078  ext = _giraffe_interpolate_spline_hermite(_extinction,
1079  "LAMBDA", "EXTINCTION", wlen, &first);
1080 
1081  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1082 
1083  cpl_table_delete(_extinction);
1084  _extinction = NULL;
1085 
1086  return 3;
1087 
1088  }
1089 
1090  giraffe_error_pop();
1091 
1092 
1093  /*
1094  * Correct the spectrum flux of this wavelength bin for
1095  * atmospheric extinction.
1096  *
1097  * The extinction ext is given in mag/airmass. The correction for
1098  * the effect of the earth atmoshpere gives for the observed
1099  * magnitude at the top of the atmosphere m_top = m - ext * airmass
1100  *
1101  * Converting the magnitude into a flux using m = -2.5 * log10(flux)
1102  * the correction factor to be applied is 10^(0.4 * ext * airmass)
1103  */
1104 
1105  flx[i] *= pow(10., 0.4 * ext * airmass);
1106 
1107  }
1108 
1109  cpl_table_delete(_extinction);
1110  _extinction = NULL;
1111 
1112  return 0;
1113 
1114 }
1115 
1116 
1117 /*
1118  * @brief
1119  * Compute the instrument spectral response function.
1120  *
1121  * @param spectrum Observed flux standard spectrum image.
1122  * @param properties Properties of the observed flux standard.
1123  * @param refflux Reference fluxes of the flux standard.
1124  *
1125  * @return
1126  * On success the function returns the instruments spectral response
1127  * function as an image, or @c NULL otherwise.
1128  *
1129  * The instruments spectral response function is computed, by interpolating
1130  * the tabulated reference flux of the observed flux standard onto the
1131  * wavelength grid of the observed spectrum, and dividing the observed
1132  * flux by the reference flux values.
1133  */
1134 
1135 inline static cpl_image*
1136 _giraffe_compute_response(const cpl_image* spectrum,
1137  const cpl_propertylist* properties,
1138  const cpl_table* refflux)
1139 {
1140 
1141  const cxchar* const _id = "giraffe_compute_response";
1142 
1143 
1144  cxint i = 0;
1145  cxint nw = 0;
1146 
1147  const cxdouble* flx = NULL;
1148 
1149  cxdouble wlstart = 0.;
1150  cxdouble wlstep = 0.;
1151  cxdouble* rdata = NULL;
1152 
1153 
1154  cpl_image* response = NULL;
1155 
1156 
1157 
1158  if ((spectrum == NULL) || (properties == NULL) || (refflux == NULL)) {
1159  return NULL;
1160  }
1161 
1162  if (cpl_image_get_size_x(spectrum) != 1) {
1163 
1164  cpl_msg_debug(_id, "Observed spectrum is not a 1d spectrum!");
1165  return NULL;
1166 
1167  }
1168 
1169 
1170  /*
1171  * Get the wavelength grid parameters of the observered spectrum
1172  */
1173 
1174  if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
1175  (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
1176 
1177  cpl_msg_debug(_id, "Observed spectrum does not have a valid "
1178  "wavelength grid!");
1179  return NULL;
1180 
1181  }
1182 
1183  wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1184  wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1185 
1186  nw = cpl_image_get_size_y(spectrum);
1187 
1188 
1189  /*
1190  * Compute the response for each spectrum and wavelength bin of the
1191  * observed spectrum image.
1192  */
1193 
1194  flx = cpl_image_get_data_double_const(spectrum);
1195 
1196  response = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
1197  rdata = cpl_image_get_data_double(response);
1198 
1199  for (i = 0; i < nw; ++i) {
1200 
1201  cxint first = 0;
1202 
1203  cxdouble wlen = wlstart + (i - 1) * wlstep;
1204  cxdouble sflx = 0.;
1205 
1206 
1207  giraffe_error_push();
1208 
1209  sflx = _giraffe_interpolate_spline_hermite(refflux,
1210  "LAMBDA", "F_LAMBDA", wlen, &first);
1211 
1212  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1213 
1214  cpl_image_delete(response);
1215  response = NULL;
1216 
1217  return NULL;
1218 
1219  }
1220 
1221  giraffe_error_pop();
1222 
1223  rdata[i] = flx[i] / sflx;
1224 
1225  }
1226 
1227  return response;
1228 
1229 }
1230 
1231 
1232 GiTable*
1233 giraffe_select_flux_standard(const GiTable* catalog, const GiImage* spectra,
1234  cxdouble max_dist)
1235 {
1236 
1237  const cxchar* const _id = "giraffe_select_flux_standard";
1238 
1239  cxint row = 0;
1240  cxint nmatch = 0;
1241 
1242  cxdouble ra = 0.;
1243  cxdouble dec = 0.;
1244  cxdouble std_ra = 0.;
1245  cxdouble std_dec = 0.;
1246  cxdouble min_dist = 0.;
1247 
1248  const cpl_table* _catalog = NULL;
1249 
1250  cpl_table* _flux = NULL;
1251 
1252  const cpl_propertylist* properties = NULL;
1253 
1254  GiTable* flux = NULL;
1255 
1256 
1257  if ((catalog == NULL) || (spectra == NULL)) {
1258  return NULL;
1259  }
1260 
1261  _catalog = giraffe_table_get(catalog);
1262  cx_assert(_catalog != NULL);
1263 
1264 
1265  /*
1266  * Get the telescope pointing from the properties of the
1267  * rebinned spectra
1268  */
1269 
1270  properties = giraffe_image_get_properties(spectra);
1271  cx_assert(properties != NULL);
1272 
1273  giraffe_error_push();
1274 
1275  ra = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1276  dec = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1277 
1278  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1279  return NULL;
1280  }
1281 
1282  giraffe_error_pop();
1283 
1284 
1285  /*
1286  * Search for matching objects in the flux standards catalog
1287  */
1288 
1289  cpl_msg_debug(_id, "Searching flux standard by name...");
1290 
1291  if ((cpl_propertylist_has(properties, GIALIAS_TARGET) == TRUE) &&
1292  (cpl_table_has_column(_catalog, "OBJECT") == TRUE)) {
1293 
1294  const cxchar* target = cpl_propertylist_get_string(properties,
1295  GIALIAS_TARGET);
1296 
1297 
1298  if ((target != NULL) && (target[0] != '\0')) {
1299 
1300  register cxint i = 0;
1301 
1302 
1303  for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1304 
1305  const cxchar* object = cpl_table_get_string(_catalog,
1306  "OBJECT", i);
1307 
1308 
1309  if (strcmp(target, object) == 0) {
1310 
1311  cxdouble cat_ra = cpl_table_get_double(_catalog,
1312  "RA_DEG", i, NULL);
1313  cxdouble cat_dec = cpl_table_get_double(_catalog,
1314  "DEC_DEG", i, NULL);
1315 
1316 
1317  std_ra = cpl_table_get_double(_catalog,
1318  "RA_DEG", i, NULL);
1319  std_dec = cpl_table_get_double(_catalog,
1320  "DEC_DEG", i, NULL);
1321 
1322  min_dist = _giraffe_compute_separation(ra, dec,
1323  cat_ra, cat_dec);
1324 
1325  row = i;
1326  ++nmatch;
1327 
1328  }
1329 
1330  }
1331 
1332  }
1333 
1334  }
1335 
1336  cpl_msg_debug(_id, "%d flux standards found...", nmatch);
1337 
1338 
1339  if (nmatch == 0) {
1340 
1341  cxint i = 0;
1342 
1343 
1344  cpl_msg_debug(_id, "Searching flux standard by coordinates...");
1345 
1346  if ((cpl_table_has_column(_catalog, "RA_DEG") == FALSE) ||
1347  (cpl_table_has_column(_catalog, "DEC_DEG") == FALSE)) {
1348 
1349  cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
1350 
1351  return NULL;
1352 
1353  }
1354 
1355 
1356  for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1357 
1358  cxdouble cat_ra = cpl_table_get_double(_catalog, "RA_DEG",
1359  i, NULL);
1360  cxdouble cat_dec = cpl_table_get_double(_catalog, "DEC_DEG",
1361  i, NULL);
1362 
1363  cxdouble dist = 0.;
1364 
1365 
1366  /*
1367  * Compute angular separation between the observation and the
1368  * positions given in the flux standards catalog.
1369  */
1370 
1371  dist = _giraffe_compute_separation(ra, dec, cat_ra, cat_dec);
1372 
1373  if ((i == 0) || (dist < min_dist)) {
1374 
1375  std_ra = cat_ra;
1376  std_dec = cat_dec;
1377  min_dist = dist;
1378 
1379  if (dist < max_dist) {
1380  ++nmatch;
1381  row = i;
1382  }
1383 
1384  }
1385 
1386  }
1387 
1388  cpl_msg_debug(_id, "%d flux standards found...", nmatch);
1389 
1390  }
1391 
1392 
1393  switch (nmatch) {
1394 
1395  case 0:
1396  {
1397 
1398  const cxchar* object = cpl_table_get_string(_catalog,
1399  "OBJECT", row);
1400 
1401  cpl_msg_debug(_id, "No flux standard found within %.4f arcsec",
1402  max_dist);
1403  cpl_msg_debug(_id, "The closest object ('%s') at (RA, Dec) = "
1404  "(%.4f, %.4f) is %.4f arcsec away", object, std_ra,
1405  std_dec, min_dist);
1406 
1407  cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1408 
1409  return NULL;
1410  break;
1411 
1412  }
1413 
1414  case 1:
1415  {
1416 
1417  const cxchar* object = cpl_table_get_string(_catalog,
1418  "OBJECT", row);
1419 
1420  cpl_msg_debug(_id, "Flux standard ('%s') at (RA, Dec) = "
1421  "(%.4f, %.4f) found at a distance of %.4f arcsec",
1422  object, std_ra, std_dec, min_dist);
1423 
1424  /*
1425  * Create flux table of the flux standart object found.
1426  */
1427 
1428  _flux = _giraffe_create_flux_table(_catalog, row);
1429 
1430  break;
1431 
1432  }
1433 
1434  default:
1435  {
1436 
1437  const cxchar* object = cpl_table_get_string(_catalog,
1438  "OBJECT", row);
1439 
1440  cpl_msg_debug(_id, "%d flux standards found within %.4f arcsec",
1441  nmatch, max_dist);
1442  cpl_msg_debug(_id, "The closest object ('%s') at (RA, Dec) = "
1443  "(%.4f, %.4f) is %.4f arcsec away", object, std_ra,
1444  std_dec, min_dist);
1445 
1446  cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1447 
1448  return NULL;
1449  break;
1450 
1451  }
1452 
1453  }
1454 
1455  if (_flux != NULL) {
1456 
1457  flux = giraffe_table_new();
1458  giraffe_table_set(flux, _flux);
1459 
1460  }
1461 
1462  cpl_table_delete(_flux);
1463  _flux = NULL;
1464 
1465  return flux;
1466 
1467 }
1468 
1469 
1511 cxint
1512 giraffe_calibrate_flux(GiResponse* result, const GiRebinning* spectra,
1513  const GiTable* fibers, const GiImage* flat,
1514  const GiTable* flux, const GiTable* extinction,
1515  const GiFxCalibrationConfig* config)
1516 {
1517 
1518  const cxchar* const _id = "giraffe_calibrate_flux";
1519 
1520 
1521  const cxint xrad = 0;
1522  const cxint yrad = 7;
1523 
1524  const cxdouble UT_M1_VIGNETTED_AREA = 517533.407382; /* cm^2 */
1525  const cxdouble H_PLANCK = 6.62606896e-27; /* erg s */
1526  const cxdouble C_LIGHT = 2.99792458e17; /* nm s^-1 */
1527 
1528  cxint i = 0;
1529  cxint status = 0;
1530  cxint nw = 0;
1531 
1532  const cxdouble* rdata = NULL;
1533 
1534  cxdouble conad = 0.;
1535  cxdouble wlstep = 0.;
1536  cxdouble wlstart = 0.;
1537  cxdouble exptime = 0.;
1538  cxdouble avgsky = 0.;
1539  cxdouble scale = UT_M1_VIGNETTED_AREA / (H_PLANCK * C_LIGHT);
1540 
1541  cpl_propertylist* properties = NULL;
1542 
1543  cpl_mask* filter = NULL;
1544 
1545  cpl_image* _spectra = NULL;
1546  cpl_image* fluxobs = NULL;
1547  cpl_image* response = NULL;
1548  cpl_image* fresponse = NULL;
1549 
1550  cpl_table* _extinction = NULL;
1551  cpl_table* _flux = NULL;
1552  cpl_table* efficiency = NULL;
1553 
1554  (void) flat; /* Not used. */
1555 
1556  if (result == NULL) {
1557  return -1;
1558  }
1559 
1560  if ((spectra == NULL) || (spectra->spectra == NULL)) {
1561  return -2;
1562  }
1563 
1564  if (fibers == NULL) {
1565  return -3;
1566  }
1567 
1568  if ((flux == NULL) || (extinction == NULL)) {
1569  return -4;
1570  }
1571 
1572  if (config == NULL) {
1573  return -5;
1574  }
1575 
1576 
1577  if ((result->response != NULL) || (result->efficiency != NULL)) {
1578 
1579  gi_warning("%s: Results structure at %p is not empty! Contents "
1580  "might be lost.", _id, result);
1581 
1582  }
1583 
1584  properties = giraffe_image_get_properties(spectra->spectra);
1585  cx_assert(properties != NULL);
1586 
1587  _spectra = giraffe_image_get(spectra->spectra);
1588  cx_assert(_spectra != NULL);
1589 
1590  _extinction = giraffe_table_get(extinction);
1591  cx_assert(_extinction != NULL);
1592 
1593  _flux = giraffe_table_get(flux);
1594  cx_assert(_flux != NULL);
1595 
1596 
1597 
1598  /*
1599  * Compute the total observed flux. No background subtraction is
1600  * performed, but it is assumed that the background is negligible.
1601  * Also, no attempt is made to figure out which fibers actually
1602  * were illuminated by the object.
1603  */
1604 
1605  if (config->sky_subtraction == TRUE) {
1606 
1607  cpl_image* sky_spectrum = NULL;
1608  cpl_image* sspectra = NULL;
1609 
1610  cpl_table* _fibers = giraffe_table_get(fibers);
1611 
1612 
1613  sky_spectrum = _giraffe_compute_mean_sky(_spectra, _fibers);
1614 
1615  if (sky_spectrum == NULL) {
1616  return 1;
1617  }
1618 
1619  giraffe_error_push();
1620 
1621  avgsky = cpl_image_get_mean(sky_spectrum);
1622 
1623  sspectra = _giraffe_subtract_mean_sky(_spectra, sky_spectrum,
1624  _fibers);
1625 
1626  fluxobs = _giraffe_integrate_flux(sspectra, _fibers);
1627 
1628  cpl_image_delete(sky_spectrum);
1629  sky_spectrum = NULL;
1630 
1631  cpl_image_delete(sspectra);
1632  sspectra = NULL;
1633 
1634  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1635  return 1;
1636  }
1637 
1638  giraffe_error_pop();
1639 
1640  }
1641  else {
1642 
1643  cpl_table* _fibers = giraffe_table_get(fibers);
1644 
1645  fluxobs = _giraffe_integrate_flux(_spectra, _fibers);
1646 
1647  }
1648 
1649 
1650  /*
1651  * Correct for the atmospheric extinction
1652  */
1653 
1654  status = _giraffe_correct_extinction(fluxobs, properties, _extinction);
1655 
1656  if (status != 0) {
1657  cpl_msg_warning(_id, "Extinction correction failed!");
1658  }
1659 
1660 
1661  exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
1662  wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1663  wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1664 
1665  conad = giraffe_propertylist_get_conad(properties);
1666 
1667 
1668  /*
1669  * Convert the observed spectrum from ADU to e- / s / nm
1670  */
1671 
1672  cpl_image_multiply_scalar(fluxobs, conad / exptime / wlstep);
1673 
1674 
1675  /*
1676  * Compute the response function R = Fobs / Fref in units of
1677  * 'e- erg^-1 cm^2'
1678  */
1679 
1680  response = _giraffe_compute_response(fluxobs, properties, _flux);
1681 
1682  cpl_image_delete(fluxobs);
1683  fluxobs = NULL;
1684 
1685  if (response == NULL) {
1686  return 2;
1687  }
1688 
1689  filter = cpl_mask_new(2 * xrad + 1, 2 * yrad + 1);
1690  for (i = 0; i < cpl_mask_get_size_x(filter); ++i) {
1691 
1692  cxint j = 0;
1693 
1694  for (j = 0; j < cpl_mask_get_size_y(filter); ++j)
1695  {
1696  cpl_mask_set(filter, i + 1, j + 1, CPL_BINARY_1);
1697  }
1698 
1699  }
1700 
1701  fresponse = cpl_image_new(cpl_image_get_size_x(response),
1702  cpl_image_get_size_y(response),
1703  cpl_image_get_type(response));
1704 
1705  cpl_image_filter_mask(fresponse, response, filter,
1706  CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
1707 
1708  cpl_mask_delete(filter);
1709  filter = NULL;
1710 
1711  cpl_image_delete(response);
1712  response = fresponse;
1713 
1714  if (response == NULL) {
1715  return 3;
1716  }
1717 
1718 
1719  /*
1720  * Compute instrument efficiency
1721  */
1722 
1723  giraffe_error_push();
1724 
1725  nw = cpl_image_get_size_y(response);
1726 
1727  efficiency = cpl_table_new(nw);
1728 
1729  cpl_table_new_column(efficiency, "WLEN", CPL_TYPE_DOUBLE);
1730  cpl_table_new_column(efficiency, "BINWIDTH", CPL_TYPE_DOUBLE);
1731  cpl_table_new_column(efficiency, "EFFICIENCY", CPL_TYPE_DOUBLE);
1732 
1733  cpl_table_set_column_unit(efficiency, "WLEN", "nm");
1734  cpl_table_set_column_unit(efficiency, "BINWIDTH", "nm");
1735 
1736  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1737 
1738  cpl_table_delete(efficiency);
1739  efficiency = NULL;
1740 
1741  cpl_image_delete(response);
1742  response = NULL;
1743 
1744  return 4;
1745 
1746  }
1747 
1748  giraffe_error_pop();
1749 
1750 
1751  rdata = cpl_image_get_data_double_const(response);
1752 
1753  for (i = 0; i < nw; ++i) {
1754 
1755  cxdouble wl = wlstart + i * wlstep;
1756 
1757  cpl_table_set_double(efficiency, "WLEN", i, wl);
1758  cpl_table_set_double(efficiency, "BINWIDTH", i, wlstep);
1759  cpl_table_set_double(efficiency, "EFFICIENCY", i,
1760  rdata[i] / (scale * wl));
1761 
1762  }
1763 
1764  rdata = NULL;
1765 
1766 
1767  /*
1768  * Fill the results object
1769  */
1770 
1771 
1772  if (config->sky_subtraction == TRUE) {
1773  cpl_propertylist_update_double(properties, GIALIAS_SKY_LEVEL, avgsky);
1774  cpl_propertylist_set_comment(properties, GIALIAS_SKY_LEVEL,
1775  "Mean sky level used [ADU].");
1776  }
1777 
1778  result->response = giraffe_image_new(CPL_TYPE_DOUBLE);
1779  giraffe_image_set_properties(result->response, properties);
1780  giraffe_image_set(result->response, response);
1781 
1782  cpl_image_delete(response);
1783  response = NULL;
1784 
1785  result->efficiency = giraffe_table_new();
1786  giraffe_table_set_properties(result->efficiency, properties);
1787  giraffe_table_set(result->efficiency, efficiency);
1788 
1789  cpl_table_delete(efficiency);
1790  efficiency = NULL;
1791 
1792  return 0;
1793 
1794 }
1795 
1796 
1797 GiFxCalibrationConfig*
1798 giraffe_fxcalibration_config_create(cpl_parameterlist* parameters)
1799 {
1800 
1801  cpl_parameter *p = NULL;
1802 
1803  GiFxCalibrationConfig* self = NULL;
1804 
1805 
1806  if (parameters == NULL) {
1807  return NULL;
1808  }
1809 
1810  self = cx_calloc(1, sizeof *self);
1811  cx_assert(self != NULL);
1812 
1813 
1814  /*
1815  * Set application defaults
1816  */
1817 
1818  self->sky_subtraction = FALSE;
1819 
1820  /*
1821  * Lookup the parameters in the given parameter list and set the
1822  * value accordingly if it is found.
1823  */
1824 
1825  p = cpl_parameterlist_find(parameters,
1826  "giraffe.fxcalibration.sky.correct");
1827 
1828  if (p != NULL) {
1829  self->sky_subtraction = cpl_parameter_get_bool(p);
1830  }
1831 
1832  return self;
1833 
1834 }
1835 
1836 
1850 void
1851 giraffe_fxcalibration_config_destroy(GiFxCalibrationConfig* self)
1852 {
1853 
1854  if (self != NULL) {
1855  cx_free(self);
1856  }
1857 
1858  return;
1859 
1860 }
1861 
1862 
1876 void
1877 giraffe_fxcalibration_config_add(cpl_parameterlist* parameters)
1878 {
1879 
1880  cpl_parameter* p = NULL;
1881 
1882 
1883  if (parameters == NULL) {
1884  return;
1885  }
1886 
1887  p = cpl_parameter_new_value("giraffe.fxcalibration.sky.correct",
1888  CPL_TYPE_BOOL,
1889  "Correct spectra for the sky emission",
1890  "giraffe.fxcalibration",
1891  FALSE);
1892  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flx-skycorr");
1893  cpl_parameterlist_append(parameters, p);
1894 
1895  return;
1896 
1897 }
void gi_warning(const cxchar *format,...)
Log a warning.
Definition: gimessages.c:127
cxint giraffe_image_set(GiImage *self, cpl_image *image)
Sets the image data.
Definition: giimage.c:252
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
Definition: gitable.c:441
cxint giraffe_table_set(GiTable *self, cpl_table *table)
Sets the table data.
Definition: gitable.c:464
GiTable * giraffe_table_new(void)
Creates a new, empty Giraffe table.
Definition: gitable.c:93
cxint giraffe_table_set_properties(GiTable *self, cpl_propertylist *properties)
Attaches a property list to an table.
Definition: gitable.c:524
void giraffe_fxcalibration_config_add(cpl_parameterlist *parameters)
Add flux calibration parameters to a parameter list.
cxint giraffe_calibrate_flux(GiResponse *result, const GiRebinning *spectra, const GiTable *fibers, const GiImage *flat, const GiTable *flux, const GiTable *extinction, const GiFxCalibrationConfig *config)
Compute the response and efficiency curves.
cxdouble giraffe_propertylist_get_conad(const cpl_propertylist *properties)
Retrieve the ADU to electrons conversion factor from the given properties.
Definition: giutils.c:1274
GiImage * giraffe_image_new(cpl_type type)
Creates an empty image container.
Definition: giimage.c:73
void giraffe_fxcalibration_config_destroy(GiFxCalibrationConfig *self)
Destroy a flux calibration setup structure.
cxdouble giraffe_compute_airmass(cxdouble alpha, cxdouble delta, cxdouble lst, cxdouble exptime, cxdouble latitude)
Compute the airmass for a given pointing direction and observing site.
Definition: giastroutils.c:185
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
Definition: giimage.c:226
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
Definition: giimage.c:320
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
Definition: giimage.c:290

This file is part of the GIRAFFE Pipeline Reference Manual 2.14.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Wed Mar 11 2015 13:19:41 by doxygen 1.8.9.1 written by Dimitri van Heesch, © 1997-2004