GIRAFFE Pipeline Reference Manual

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.16.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Dec 15 2022 21:18:51 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2004