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

This file is part of the GIRAFFE Pipeline Reference Manual 2.19.4.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Feb 6 2026 13:47:22 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004