36 #include <cxstrutils.h>
38 #include <cpl_propertylist.h>
40 #include <cpl_table.h>
46 #include "gimessages.h"
47 #include "giastroutils.h"
49 #include "gifxcalibration.h"
75 inline static cxdouble
76 _giraffe_compute_separation(cxdouble ra_0, cxdouble dec_0,
77 cxdouble ra_1, cxdouble dec_1)
80 const cxdouble deg2rad = CX_PI / 180.;
90 dist = sin(dec_0) * sin(dec_1) +
91 cos(dec_0) * cos(dec_1) * cos(ra_0 - ra_1);
93 dist = CX_CLAMP(dist, -1., 1.);
94 dist = acos(dist) / deg2rad * 3600.;
119 inline static cxdouble
120 _giraffe_spline_hermite(cxdouble xp,
const cxdouble* x,
const cxdouble* y,
121 cxint n, cxint* istart)
137 if ((x[0] <= x[n - 1]) && ((xp < x[0]) || (xp > x[n - 1]))) {
141 if ((x[0] > x[n - 1]) && ((xp > x[0]) || (xp < x[n - 1]))) {
145 if (x[0] <= x[n - 1]) {
147 for (i = *istart + 1; i <= n && xp >= x[i - 1]; ++i) {
154 for (i = *istart + 1; i <= n && xp <= x[i - 1]; ++i) {
164 lp1 = 1. / (x[i - 1] - x[i]);
168 yp1 = (y[1] - y[0]) / (x[1] - x[0]);
171 yp1 = (y[i] - y[i - 2]) / (x[i] - x[i - 2]);
175 yp2 = (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
178 yp2 = (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]);
188 yp = y[i - 1] * (1. - 2. * lp1 * xpi) * l1 * l1 +
189 y[i] * (1. - 2. * lp2 * xpi1) * l2 * l2 +
190 yp1 * xpi * l1 * l1 +
191 yp2 * xpi1 * l2 * l2;
217 inline static cxdouble
218 _giraffe_interpolate_spline_hermite(
const cpl_table* tbl,
219 const cxchar* xlabel,
220 const cxchar* ylabel,
221 cxdouble xp, cxint* istart)
224 cxint n = cpl_table_get_nrow(tbl);
226 const cxdouble* x = cpl_table_get_data_double_const(tbl, xlabel);
227 const cxdouble* y = cpl_table_get_data_double_const(tbl, ylabel);
230 return _giraffe_spline_hermite(xp, x, y, n ,istart);
258 inline static cpl_table*
259 _giraffe_create_flux_table(
const cpl_table* catalog, cxint row)
262 const cxchar*
const _id =
"_giraffe_create_flux_table";
265 const cxchar* columns[] = {
"LAMBDA",
"BIN_WIDTH",
"F_LAMBDA"};
266 const cxchar* units = NULL;
268 cxint ndata = cpl_table_get_int(catalog,
"NDATA", row, NULL);
272 const cxdouble ang2nm = 0.1;
274 cpl_table* flux = NULL;
282 giraffe_error_push();
284 flux = cpl_table_new(ndata);
286 for (i = 0; i < CX_N_ELEMENTS(columns); ++i) {
288 const cpl_array* data = cpl_table_get_array(catalog, columns[i], row);
290 cpl_type type = cpl_table_get_column_type(catalog, columns[i]);
293 cpl_table_new_column(flux, columns[i], CPL_TYPE_DOUBLE);
295 if ((data != NULL) && (ndata <= cpl_array_get_size(data))) {
297 switch (type & ~CPL_TYPE_POINTER) {
303 register cxdouble value = 0.;
306 for (j = 0; j < ndata; ++j) {
307 value = cpl_array_get_float(data, j, NULL);
308 cpl_table_set_double(flux, columns[i], j, value);
315 case CPL_TYPE_DOUBLE:
320 register cxdouble value = 0.;
323 for (j = 0; j < ndata; ++j) {
324 value = cpl_array_get_double(data, j, NULL);
325 cpl_table_set_double(flux, columns[i], j, value);
334 cpl_error_set(_id, CPL_ERROR_INVALID_TYPE);
350 cpl_table_multiply_scalar(flux, columns[2], 1.e-16);
360 units = cpl_table_get_column_unit(catalog, columns[0]);
362 if ((units != NULL) && (cx_strncasecmp(units,
"ang", 3) == 0)) {
364 cpl_msg_debug(_id,
"Found units '%s'. Converting flux standard "
365 "from Angstrom to nano meters", units);
367 cpl_table_multiply_scalar(flux, columns[0], ang2nm);
368 cpl_table_set_column_unit(flux, columns[0],
"nm");
370 cpl_table_multiply_scalar(flux, columns[1], ang2nm);
371 cpl_table_set_column_unit(flux, columns[1],
"nm");
373 cpl_table_divide_scalar(flux, columns[2], ang2nm);
374 cpl_table_set_column_unit(flux, columns[2],
"erg/s/cm^2/nm");
381 cpl_msg_debug(_id,
"No units for wavelength column. Assuming "
387 cpl_msg_debug(_id,
"Found unknown units ('%s') for wavelength "
388 "column. Assuming nano meters.", units);
395 if (cpl_error_get_code() != CPL_ERROR_NONE) {
396 cpl_table_delete(flux);
424 inline static cpl_table*
425 _giraffe_setup_extinction(
const cpl_table* extinction)
428 const cxchar*
const _id =
"_giraffe_setup_extinction";
430 const cxchar* site = NULL;
431 const cxchar* units = NULL;
432 const cxchar* sites[] = {
"PARANAL",
"LA_SILLA", NULL};
437 const cxdouble ang2nm = 0.1;
441 cpl_table* _extinction = NULL;
444 if (cpl_table_has_column(extinction,
"LAMBDA") == FALSE) {
446 cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
456 units = cpl_table_get_column_unit(extinction,
"LAMBDA");
458 if ((units != NULL) && (cx_strncasecmp(units,
"ang", 3) == 0)) {
462 cpl_msg_debug(_id,
"Found units '%s'. Converting wavelength"
463 "from Angstrom to nano meters", units);
470 cpl_msg_debug(_id,
"No units for wavelength column. "
471 "Assuming nano meters.");
476 if (cx_strncasecmp(units,
"nm", 2) == 0) {
478 cpl_msg_debug(_id,
"Found units nano meters ('%s') for "
479 "wavelength column.", units);
484 cpl_msg_debug(_id,
"Found unknown units ('%s') for "
485 "wavelength column. Assuming nano meters.", units);
498 while ((site == NULL) && (sites[i] != NULL)) {
500 if (cpl_table_has_column(extinction, sites[i]) == TRUE) {
512 cpl_msg_debug(_id,
"No matching observatory site found!");
521 rows = cpl_table_get_nrow(extinction);
523 giraffe_error_push();
525 _extinction = cpl_table_new(rows);
526 cpl_table_new_column(_extinction,
"LAMBDA", CPL_TYPE_DOUBLE);
527 cpl_table_set_column_unit(_extinction,
"LAMBDA",
"nm");
529 cpl_table_new_column(_extinction,
"EXTINCTION", CPL_TYPE_DOUBLE);
530 cpl_table_set_column_unit(_extinction,
"EXTINCTION",
"mag/airmass");
532 if (cpl_error_get_code() != CPL_ERROR_NONE) {
534 cpl_table_delete(_extinction);
544 switch (cpl_table_get_column_type(extinction,
"LAMBDA")) {
547 for (i = 0; i < rows; ++i) {
549 register cxdouble lambda = cpl_table_get_float(extinction,
552 cpl_table_set_double(_extinction,
"LAMBDA", i,
559 case CPL_TYPE_DOUBLE:
561 for (i = 0; i < rows; ++i) {
563 register cxdouble lambda = cpl_table_get_double(extinction,
566 cpl_table_set_double(_extinction,
"LAMBDA", i,
575 cpl_table_delete(_extinction);
578 cpl_msg_debug(_id,
"Column type (%d) is not supported for "
579 "extinction tables!",
580 cpl_table_get_column_type(extinction,
"LAMBDA"));
587 switch (cpl_table_get_column_type(extinction, site)) {
590 for (i = 0; i < rows; ++i) {
592 register cxdouble aext = cpl_table_get_float(extinction,
595 cpl_table_set_double(_extinction,
"EXTINCTION", i, aext);
601 case CPL_TYPE_DOUBLE:
603 for (i = 0; i < rows; ++i) {
605 register cxdouble aext = cpl_table_get_double(extinction,
608 cpl_table_set_double(_extinction,
"EXTINCTION", i, aext);
616 cpl_table_delete(_extinction);
619 cpl_msg_debug(_id,
"Column type (%d) is not supported for "
620 "extinction tables!",
621 cpl_table_get_column_type(extinction, site));
634 inline static cpl_image*
635 _giraffe_compute_mean_sky(
const cpl_image* spectra,
const cpl_table* fibers)
639 cxint ns = cpl_image_get_size_x(spectra);
640 cxint nw = cpl_image_get_size_y(spectra);
642 cxint* sky_fibers = cx_calloc(ns,
sizeof(cxint));
644 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
646 cxdouble* _sky = NULL;
648 cpl_image* sky = NULL;
651 cx_assert(ns == cpl_table_get_nrow(fibers));
658 for (i = 0; i < ns; ++i) {
660 const cxchar* s = cpl_table_get_string(fibers,
"Retractor", i);
662 if (strstr(s,
"-Sky") != NULL) {
664 cpl_table_get_int(fibers,
"INDEX", i, NULL) - 1;
687 sky = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
688 _sky = cpl_image_get_data_double(sky);
692 if (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE) {
694 cxdouble* sky_raw = cx_calloc(nsky,
sizeof(cxdouble));
697 for (i = 0; i < nw; ++i) {
699 register cxint j = 0;
702 for (j = 0; j < nsky; ++j) {
704 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
705 sky_fibers[j], NULL);
710 sky_raw[j] = _spectra[i * ns + sky_fibers[j]] / t;
714 _sky[i] = giraffe_array_median(sky_raw, nsky);
724 cxdouble* sky_raw = cx_calloc(nsky,
sizeof(cxdouble));
727 for (i = 0; i < nw; ++i) {
729 register cxint j = 0;
732 for (j = 0; j < nsky; ++j) {
733 sky_raw[j] = _spectra[i * ns + sky_fibers[j]];
736 _sky[i] = giraffe_array_median(sky_raw, nsky);
748 if (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE) {
750 for (i = 0; i < nsky; ++i) {
752 register cxint j = 0;
754 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
755 sky_fibers[i], NULL);
760 for (j = 0; j < nw; ++j) {
761 _sky[j] += _spectra[j * ns + sky_fibers[i]] / t;
769 for (i = 0; i < nsky; ++i) {
771 register cxint j = 0;
774 for (j = 0; j < nw; ++j) {
775 _sky[j] += _spectra[j * ns + sky_fibers[i]];
782 cpl_image_divide_scalar(sky, nsky);
794 inline static cpl_image*
795 _giraffe_subtract_mean_sky(
const cpl_image* spectra,
const cpl_image* sky,
796 const cpl_table* fibers)
800 cxint ns = cpl_image_get_size_x(spectra);
801 cxint nw = cpl_image_get_size_y(spectra);
803 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
804 const cxdouble* _sky = cpl_image_get_data_double_const(sky);
806 cpl_image* result = cpl_image_new(ns, nw, CPL_TYPE_DOUBLE);
808 cxdouble* _result = cpl_image_get_data_double(result);
811 cx_assert((fibers == NULL) || (ns == cpl_table_get_nrow(fibers)));
812 cx_assert(nw == cpl_image_get_size_y(sky));
821 if ((fibers != NULL) &&
822 (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE)) {
824 for (i = 0; i < ns; ++i) {
826 register cxint j = 0;
828 cpl_table_get_int(fibers,
"INDEX", i, NULL) - 1;
830 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
834 for (j = 0; j < nw; ++j) {
836 register cxint l = j * ns + i;
838 _result[l] += _spectra[l] - t * _sky[j];
847 for (i = 0; i < ns; ++i) {
849 register cxint j = 0;
851 for (j = 0; j < nw; ++j) {
853 register cxint k = j * ns + i;
855 _result[k] += _spectra[k] - _sky[j];
868 inline static cpl_image*
869 _giraffe_integrate_flux(
const cpl_image* spectra,
const cpl_table* fibers)
873 cxint nw = cpl_image_get_size_y(spectra);
874 cxint ns = cpl_image_get_size_x(spectra);
876 cpl_image* result = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
879 cx_assert(ns == cpl_table_get_nrow(fibers));
881 for (i = 0; i < ns; ++i) {
883 const cxchar* s = cpl_table_get_string(fibers,
"Retractor", i);
885 cxint rp = cpl_table_get_int(fibers,
"RP", i, NULL);
893 if ((rp != -1) && (strstr(s,
"-Sky") == NULL)) {
895 register cxint j = 0;
897 const cxdouble* _spectra =
898 cpl_image_get_data_double_const(spectra);
900 cxdouble* _result = cpl_image_get_data_double(result);
903 for (j = 0; j < nw; ++j) {
904 _result[j] += _spectra[j * ns + i];
937 _giraffe_correct_extinction(cpl_image* spectrum, cpl_propertylist* properties,
938 const cpl_table* extinction)
941 const cxchar*
const _id =
"_giraffe_correct_extinction";
951 cxdouble latitude = 0.;
952 cxdouble exptime = 0.;
953 cxdouble wlstart = 0.;
954 cxdouble wlstep = 0.;
955 cxdouble airmass = -1.;
956 cxdouble* flx = NULL;
958 cpl_table* _extinction = NULL;
961 if ((spectrum == NULL) || (properties == NULL) || (extinction == NULL)) {
966 if (cpl_image_get_size_x(spectrum) != 1) {
968 cpl_msg_debug(_id,
"Input spectrum is not a 1d spectrum!");
980 _extinction = _giraffe_setup_extinction(extinction);
982 if (_extinction == NULL) {
991 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
992 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
994 cpl_msg_debug(_id,
"Observed spectrum does not have a valid "
997 cpl_table_delete(_extinction);
1004 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1005 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1012 giraffe_error_push();
1014 alpha = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1015 delta = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1016 lst = cpl_propertylist_get_double(properties, GIALIAS_LST);
1017 latitude = cpl_propertylist_get_double(properties, GIALIAS_TEL_LAT);
1018 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
1020 status = cpl_error_get_code();
1022 if (status == CPL_ERROR_NONE) {
1029 if ((airmass < 1.) || (status != CPL_ERROR_NONE)) {
1031 cxbool start = cpl_propertylist_has(properties,
1032 GIALIAS_AIRMASS_START);
1033 cxbool end = cpl_propertylist_has(properties,
1034 GIALIAS_AIRMASS_END);
1036 if ((start == FALSE) || (end == FALSE)) {
1038 cpl_msg_debug(_id,
"Unable to compute airmass of the "
1041 cpl_table_delete(_extinction);
1049 airmass = 0.5 * (cpl_propertylist_get_double(properties,
1050 GIALIAS_AIRMASS_START) +
1051 cpl_propertylist_get_double(properties,
1052 GIALIAS_AIRMASS_END));
1058 giraffe_error_pop();
1065 nw = cpl_image_get_size_y(spectrum);
1066 flx = cpl_image_get_data_double(spectrum);
1068 for (i = 0; i < nw; ++i) {
1072 cxdouble wlen = wlstart + (i - 1) * wlstep;
1076 giraffe_error_push();
1078 ext = _giraffe_interpolate_spline_hermite(_extinction,
1079 "LAMBDA",
"EXTINCTION", wlen, &first);
1081 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1083 cpl_table_delete(_extinction);
1090 giraffe_error_pop();
1105 flx[i] *= pow(10., 0.4 * ext * airmass);
1109 cpl_table_delete(_extinction);
1135 inline static cpl_image*
1136 _giraffe_compute_response(
const cpl_image* spectrum,
1137 const cpl_propertylist* properties,
1138 const cpl_table* refflux)
1141 const cxchar*
const _id =
"giraffe_compute_response";
1147 const cxdouble* flx = NULL;
1149 cxdouble wlstart = 0.;
1150 cxdouble wlstep = 0.;
1151 cxdouble* rdata = NULL;
1154 cpl_image* response = NULL;
1158 if ((spectrum == NULL) || (properties == NULL) || (refflux == NULL)) {
1162 if (cpl_image_get_size_x(spectrum) != 1) {
1164 cpl_msg_debug(_id,
"Observed spectrum is not a 1d spectrum!");
1174 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
1175 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
1177 cpl_msg_debug(_id,
"Observed spectrum does not have a valid "
1178 "wavelength grid!");
1183 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1184 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1186 nw = cpl_image_get_size_y(spectrum);
1194 flx = cpl_image_get_data_double_const(spectrum);
1196 response = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
1197 rdata = cpl_image_get_data_double(response);
1199 for (i = 0; i < nw; ++i) {
1203 cxdouble wlen = wlstart + (i - 1) * wlstep;
1207 giraffe_error_push();
1209 sflx = _giraffe_interpolate_spline_hermite(refflux,
1210 "LAMBDA",
"F_LAMBDA", wlen, &first);
1212 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1214 cpl_image_delete(response);
1221 giraffe_error_pop();
1223 rdata[i] = flx[i] / sflx;
1233 giraffe_select_flux_standard(
const GiTable* catalog,
const GiImage* spectra,
1237 const cxchar*
const _id =
"giraffe_select_flux_standard";
1244 cxdouble std_ra = 0.;
1245 cxdouble std_dec = 0.;
1246 cxdouble min_dist = 0.;
1248 const cpl_table* _catalog = NULL;
1250 cpl_table* _flux = NULL;
1252 const cpl_propertylist* properties = NULL;
1254 GiTable* flux = NULL;
1257 if ((catalog == NULL) || (spectra == NULL)) {
1262 cx_assert(_catalog != NULL);
1271 cx_assert(properties != NULL);
1273 giraffe_error_push();
1275 ra = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1276 dec = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1278 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1282 giraffe_error_pop();
1289 cpl_msg_debug(_id,
"Searching flux standard by name...");
1291 if ((cpl_propertylist_has(properties, GIALIAS_TARGET) == TRUE) &&
1292 (cpl_table_has_column(_catalog,
"OBJECT") == TRUE)) {
1294 const cxchar* target = cpl_propertylist_get_string(properties,
1298 if ((target != NULL) && (target[0] !=
'\0')) {
1300 register cxint i = 0;
1303 for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1305 const cxchar*
object = cpl_table_get_string(_catalog,
1309 if (strcmp(target,
object) == 0) {
1311 cxdouble cat_ra = cpl_table_get_double(_catalog,
1313 cxdouble cat_dec = cpl_table_get_double(_catalog,
1314 "DEC_DEG", i, NULL);
1317 std_ra = cpl_table_get_double(_catalog,
1319 std_dec = cpl_table_get_double(_catalog,
1320 "DEC_DEG", i, NULL);
1322 min_dist = _giraffe_compute_separation(ra, dec,
1336 cpl_msg_debug(_id,
"%d flux standards found...", nmatch);
1344 cpl_msg_debug(_id,
"Searching flux standard by coordinates...");
1346 if ((cpl_table_has_column(_catalog,
"RA_DEG") == FALSE) ||
1347 (cpl_table_has_column(_catalog,
"DEC_DEG") == FALSE)) {
1349 cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
1356 for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1358 cxdouble cat_ra = cpl_table_get_double(_catalog,
"RA_DEG",
1360 cxdouble cat_dec = cpl_table_get_double(_catalog,
"DEC_DEG",
1371 dist = _giraffe_compute_separation(ra, dec, cat_ra, cat_dec);
1373 if ((i == 0) || (dist < min_dist)) {
1379 if (dist < max_dist) {
1388 cpl_msg_debug(_id,
"%d flux standards found...", nmatch);
1398 const cxchar*
object = cpl_table_get_string(_catalog,
1401 cpl_msg_debug(_id,
"No flux standard found within %.4f arcsec",
1403 cpl_msg_debug(_id,
"The closest object ('%s') at (RA, Dec) = "
1404 "(%.4f, %.4f) is %.4f arcsec away",
object, std_ra,
1407 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1417 const cxchar*
object = cpl_table_get_string(_catalog,
1420 cpl_msg_debug(_id,
"Flux standard ('%s') at (RA, Dec) = "
1421 "(%.4f, %.4f) found at a distance of %.4f arcsec",
1422 object, std_ra, std_dec, min_dist);
1428 _flux = _giraffe_create_flux_table(_catalog, row);
1437 const cxchar*
object = cpl_table_get_string(_catalog,
1440 cpl_msg_debug(_id,
"%d flux standards found within %.4f arcsec",
1442 cpl_msg_debug(_id,
"The closest object ('%s') at (RA, Dec) = "
1443 "(%.4f, %.4f) is %.4f arcsec away",
object, std_ra,
1446 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1455 if (_flux != NULL) {
1462 cpl_table_delete(_flux);
1513 const GiTable* fibers,
const GiImage* flat,
1514 const GiTable* flux,
const GiTable* extinction,
1515 const GiFxCalibrationConfig* config)
1518 const cxchar*
const _id =
"giraffe_calibrate_flux";
1521 const cxint xrad = 0;
1522 const cxint yrad = 7;
1524 const cxdouble UT_M1_VIGNETTED_AREA = 517533.407382;
1525 const cxdouble H_PLANCK = 6.62606896e-27;
1526 const cxdouble C_LIGHT = 2.99792458e17;
1532 const cxdouble* rdata = NULL;
1534 cxdouble conad = 0.;
1535 cxdouble wlstep = 0.;
1536 cxdouble wlstart = 0.;
1537 cxdouble exptime = 0.;
1538 cxdouble avgsky = 0.;
1539 cxdouble scale = UT_M1_VIGNETTED_AREA / (H_PLANCK * C_LIGHT);
1541 cpl_propertylist* properties = NULL;
1543 cpl_mask* filter = NULL;
1545 cpl_image* _spectra = NULL;
1546 cpl_image* fluxobs = NULL;
1547 cpl_image* response = NULL;
1548 cpl_image* fresponse = NULL;
1550 cpl_table* _extinction = NULL;
1551 cpl_table* _flux = NULL;
1552 cpl_table* efficiency = NULL;
1556 if (result == NULL) {
1560 if ((spectra == NULL) || (spectra->spectra == NULL)) {
1564 if (fibers == NULL) {
1568 if ((flux == NULL) || (extinction == NULL)) {
1572 if (config == NULL) {
1577 if ((result->response != NULL) || (result->efficiency != NULL)) {
1579 gi_warning(
"%s: Results structure at %p is not empty! Contents "
1580 "might be lost.", _id, result);
1585 cx_assert(properties != NULL);
1588 cx_assert(_spectra != NULL);
1591 cx_assert(_extinction != NULL);
1594 cx_assert(_flux != NULL);
1605 if (config->sky_subtraction == TRUE) {
1607 cpl_image* sky_spectrum = NULL;
1608 cpl_image* sspectra = NULL;
1613 sky_spectrum = _giraffe_compute_mean_sky(_spectra, _fibers);
1615 if (sky_spectrum == NULL) {
1619 giraffe_error_push();
1621 avgsky = cpl_image_get_mean(sky_spectrum);
1623 sspectra = _giraffe_subtract_mean_sky(_spectra, sky_spectrum,
1626 fluxobs = _giraffe_integrate_flux(sspectra, _fibers);
1628 cpl_image_delete(sky_spectrum);
1629 sky_spectrum = NULL;
1631 cpl_image_delete(sspectra);
1634 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1638 giraffe_error_pop();
1645 fluxobs = _giraffe_integrate_flux(_spectra, _fibers);
1654 status = _giraffe_correct_extinction(fluxobs, properties, _extinction);
1657 cpl_msg_warning(_id,
"Extinction correction failed!");
1661 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
1662 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1663 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1672 cpl_image_multiply_scalar(fluxobs, conad / exptime / wlstep);
1680 response = _giraffe_compute_response(fluxobs, properties, _flux);
1682 cpl_image_delete(fluxobs);
1685 if (response == NULL) {
1689 filter = cpl_mask_new(2 * xrad + 1, 2 * yrad + 1);
1690 for (i = 0; i < cpl_mask_get_size_x(filter); ++i) {
1694 for (j = 0; j < cpl_mask_get_size_y(filter); ++j)
1696 cpl_mask_set(filter, i + 1, j + 1, CPL_BINARY_1);
1701 fresponse = cpl_image_new(cpl_image_get_size_x(response),
1702 cpl_image_get_size_y(response),
1703 cpl_image_get_type(response));
1705 cpl_image_filter_mask(fresponse, response, filter,
1706 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
1708 cpl_mask_delete(filter);
1711 cpl_image_delete(response);
1712 response = fresponse;
1714 if (response == NULL) {
1723 giraffe_error_push();
1725 nw = cpl_image_get_size_y(response);
1727 efficiency = cpl_table_new(nw);
1729 cpl_table_new_column(efficiency,
"WLEN", CPL_TYPE_DOUBLE);
1730 cpl_table_new_column(efficiency,
"BINWIDTH", CPL_TYPE_DOUBLE);
1731 cpl_table_new_column(efficiency,
"EFFICIENCY", CPL_TYPE_DOUBLE);
1733 cpl_table_set_column_unit(efficiency,
"WLEN",
"nm");
1734 cpl_table_set_column_unit(efficiency,
"BINWIDTH",
"nm");
1736 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1738 cpl_table_delete(efficiency);
1741 cpl_image_delete(response);
1748 giraffe_error_pop();
1751 rdata = cpl_image_get_data_double_const(response);
1753 for (i = 0; i < nw; ++i) {
1755 cxdouble wl = wlstart + i * wlstep;
1757 cpl_table_set_double(efficiency,
"WLEN", i, wl);
1758 cpl_table_set_double(efficiency,
"BINWIDTH", i, wlstep);
1759 cpl_table_set_double(efficiency,
"EFFICIENCY", i,
1760 rdata[i] / (scale * wl));
1772 if (config->sky_subtraction == TRUE) {
1773 cpl_propertylist_update_double(properties, GIALIAS_SKY_LEVEL, avgsky);
1774 cpl_propertylist_set_comment(properties, GIALIAS_SKY_LEVEL,
1775 "Mean sky level used [ADU].");
1782 cpl_image_delete(response);
1789 cpl_table_delete(efficiency);
1797 GiFxCalibrationConfig*
1798 giraffe_fxcalibration_config_create(cpl_parameterlist* parameters)
1801 cpl_parameter *p = NULL;
1803 GiFxCalibrationConfig*
self = NULL;
1806 if (parameters == NULL) {
1810 self = cx_calloc(1,
sizeof *
self);
1811 cx_assert(
self != NULL);
1818 self->sky_subtraction = FALSE;
1825 p = cpl_parameterlist_find(parameters,
1826 "giraffe.fxcalibration.sky.correct");
1829 self->sky_subtraction = cpl_parameter_get_bool(p);
1880 cpl_parameter* p = NULL;
1883 if (parameters == NULL) {
1887 p = cpl_parameter_new_value(
"giraffe.fxcalibration.sky.correct",
1889 "Correct spectra for the sky emission",
1890 "giraffe.fxcalibration",
1892 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"flx-skycorr");
1893 cpl_parameterlist_append(parameters, p);
void gi_warning(const cxchar *format,...)
Log a warning.
cxint giraffe_image_set(GiImage *self, cpl_image *image)
Sets the image data.
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
cxint giraffe_table_set(GiTable *self, cpl_table *table)
Sets the table data.
GiTable * giraffe_table_new(void)
Creates a new, empty Giraffe table.
cxint giraffe_table_set_properties(GiTable *self, cpl_propertylist *properties)
Attaches a property list to an table.
void giraffe_fxcalibration_config_add(cpl_parameterlist *parameters)
Add flux calibration parameters to a parameter list.
cxint giraffe_calibrate_flux(GiResponse *result, const GiRebinning *spectra, const GiTable *fibers, const GiImage *flat, const GiTable *flux, const GiTable *extinction, const GiFxCalibrationConfig *config)
Compute the response and efficiency curves.
cxdouble giraffe_propertylist_get_conad(const cpl_propertylist *properties)
Retrieve the ADU to electrons conversion factor from the given properties.
GiImage * giraffe_image_new(cpl_type type)
Creates an empty image container.
void giraffe_fxcalibration_config_destroy(GiFxCalibrationConfig *self)
Destroy a flux calibration setup structure.
cxdouble giraffe_compute_airmass(cxdouble alpha, cxdouble delta, cxdouble lst, cxdouble exptime, cxdouble latitude)
Compute the airmass for a given pointing direction and observing site.
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.