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
67inline 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
111inline 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
209inline 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
250inline 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
416inline 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
626inline 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
785inline 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
859inline 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
927inline 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
1126inline 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
1223GiTable*
1224giraffe_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
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, 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: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_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:1471

This file is part of the GIRAFFE Pipeline Reference Manual 2.16.14.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Mon Feb 24 2025 09:27:59 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004