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

This file is part of the GIRAFFE Pipeline Reference Manual 2.12.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Mon Mar 24 2014 11:43:52 by doxygen 1.8.2 written by Dimitri van Heesch, © 1997-2004