36 #include <cxstrutils.h>
38 #include <cpl_propertylist.h>
40 #include <cpl_table.h>
46 #include "gimessages.h"
47 #include "giastroutils.h"
48 #include "gifxcalibration.h"
74 inline static cxdouble
75 _giraffe_compute_separation(cxdouble ra_0, cxdouble dec_0,
76 cxdouble ra_1, cxdouble dec_1)
79 const cxdouble deg2rad = CX_PI / 180.;
89 dist = sin(dec_0) * sin(dec_1) +
90 cos(dec_0) * cos(dec_1) * cos(ra_0 - ra_1);
92 dist = CX_CLAMP(dist, -1., 1.);
93 dist = acos(dist) / deg2rad * 3600.;
118 inline static cxdouble
119 _giraffe_spline_hermite(cxdouble xp,
const cxdouble* x,
const cxdouble* y,
120 cxint n, cxint* istart)
136 if ((x[0] <= x[n - 1]) && ((xp < x[0]) || (xp > x[n - 1]))) {
140 if ((x[0] > x[n - 1]) && ((xp > x[0]) || (xp < x[n - 1]))) {
144 if (x[0] <= x[n - 1]) {
146 for (i = *istart + 1; i <= n && xp >= x[i - 1]; ++i) {
153 for (i = *istart + 1; i <= n && xp <= x[i - 1]; ++i) {
163 lp1 = 1. / (x[i - 1] - x[i]);
167 yp1 = (y[1] - y[0]) / (x[1] - x[0]);
170 yp1 = (y[i] - y[i - 2]) / (x[i] - x[i - 2]);
174 yp2 = (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
177 yp2 = (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]);
187 yp = y[i - 1] * (1. - 2. * lp1 * xpi) * l1 * l1 +
188 y[i] * (1. - 2. * lp2 * xpi1) * l2 * l2 +
189 yp1 * xpi * l1 * l1 +
190 yp2 * xpi1 * l2 * l2;
216 inline static cxdouble
217 _giraffe_interpolate_spline_hermite(
const cpl_table* tbl,
218 const cxchar* xlabel,
219 const cxchar* ylabel,
220 cxdouble xp, cxint* istart)
223 cxint n = cpl_table_get_nrow(tbl);
225 const cxdouble* x = cpl_table_get_data_double_const(tbl, xlabel);
226 const cxdouble* y = cpl_table_get_data_double_const(tbl, ylabel);
229 return _giraffe_spline_hermite(xp, x, y, n ,istart);
257 inline static cpl_table*
258 _giraffe_create_flux_table(
const cpl_table* catalog, cxint row)
261 const cxchar*
const _id =
"_giraffe_create_flux_table";
264 const cxchar* columns[] = {
"LAMBDA",
"BIN_WIDTH",
"F_LAMBDA"};
265 const cxchar* units = NULL;
267 cxint ndata = cpl_table_get_int(catalog,
"NDATA", row, NULL);
271 const cxdouble ang2nm = 0.1;
273 cpl_table* flux = NULL;
281 giraffe_error_push();
283 flux = cpl_table_new(ndata);
285 for (i = 0; i < CX_N_ELEMENTS(columns); ++i) {
287 const cpl_array* data = cpl_table_get_array(catalog, columns[i], row);
289 cpl_type type = cpl_table_get_column_type(catalog, columns[i]);
292 cpl_table_new_column(flux, columns[i], CPL_TYPE_DOUBLE);
294 if ((data != NULL) && (ndata <= cpl_array_get_size(data))) {
296 switch (type & ~CPL_TYPE_POINTER) {
302 register cxdouble value = 0.;
305 for (j = 0; j < ndata; ++j) {
306 value = cpl_array_get_float(data, j, NULL);
307 cpl_table_set_double(flux, columns[i], j, value);
314 case CPL_TYPE_DOUBLE:
319 register cxdouble value = 0.;
322 for (j = 0; j < ndata; ++j) {
323 value = cpl_array_get_double(data, j, NULL);
324 cpl_table_set_double(flux, columns[i], j, value);
333 cpl_error_set(_id, CPL_ERROR_INVALID_TYPE);
349 cpl_table_multiply_scalar(flux, columns[2], 1.e-16);
359 units = cpl_table_get_column_unit(catalog, columns[0]);
361 if ((units != NULL) && (cx_strncasecmp(units,
"ang", 3) == 0)) {
363 cpl_msg_debug(_id,
"Found units '%s'. Converting flux standard "
364 "from Angstrom to nano meters", units);
366 cpl_table_multiply_scalar(flux, columns[0], ang2nm);
367 cpl_table_set_column_unit(flux, columns[0],
"nm");
369 cpl_table_multiply_scalar(flux, columns[1], ang2nm);
370 cpl_table_set_column_unit(flux, columns[1],
"nm");
372 cpl_table_divide_scalar(flux, columns[2], ang2nm);
373 cpl_table_set_column_unit(flux, columns[2],
"erg/s/cm^2/nm");
380 cpl_msg_debug(_id,
"No units for wavelength column. Assuming "
386 cpl_msg_debug(_id,
"Found unknown units ('%s') for wavelength "
387 "column. Assuming nano meters.", units);
394 if (cpl_error_get_code() != CPL_ERROR_NONE) {
395 cpl_table_delete(flux);
423 inline static cpl_table*
424 _giraffe_setup_extinction(
const cpl_table* extinction)
427 const cxchar*
const _id =
"_giraffe_setup_extinction";
429 const cxchar* site = NULL;
430 const cxchar* units = NULL;
431 const cxchar* sites[] = {
"PARANAL",
"LA_SILLA", NULL};
436 const cxdouble ang2nm = 0.1;
440 cpl_table* _extinction = NULL;
443 if (cpl_table_has_column(extinction,
"LAMBDA") == FALSE) {
445 cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
455 units = cpl_table_get_column_unit(extinction,
"LAMBDA");
457 if ((units != NULL) && (cx_strncasecmp(units,
"ang", 3) == 0)) {
461 cpl_msg_debug(_id,
"Found units '%s'. Converting wavelength"
462 "from Angstrom to nano meters", units);
469 cpl_msg_debug(_id,
"No units for wavelength column. "
470 "Assuming nano meters.");
475 if (cx_strncasecmp(units,
"nm", 2) == 0) {
477 cpl_msg_debug(_id,
"Found units nano meters ('%s') for "
478 "wavelength column.", units);
483 cpl_msg_debug(_id,
"Found unknown units ('%s') for "
484 "wavelength column. Assuming nano meters.", units);
497 while ((site == NULL) && (sites[i] != NULL)) {
499 if (cpl_table_has_column(extinction, sites[i]) == TRUE) {
511 cpl_msg_debug(_id,
"No matching observatory site found!");
520 rows = cpl_table_get_nrow(extinction);
522 giraffe_error_push();
524 _extinction = cpl_table_new(rows);
525 cpl_table_new_column(_extinction,
"LAMBDA", CPL_TYPE_DOUBLE);
526 cpl_table_set_column_unit(_extinction,
"LAMBDA",
"nm");
528 cpl_table_new_column(_extinction,
"EXTINCTION", CPL_TYPE_DOUBLE);
529 cpl_table_set_column_unit(_extinction,
"EXTINCTION",
"mag/airmass");
531 if (cpl_error_get_code() != CPL_ERROR_NONE) {
533 cpl_table_delete(_extinction);
543 switch (cpl_table_get_column_type(extinction,
"LAMBDA")) {
546 for (i = 0; i < rows; ++i) {
548 register cxdouble lambda = cpl_table_get_float(extinction,
551 cpl_table_set_double(_extinction,
"LAMBDA", i,
558 case CPL_TYPE_DOUBLE:
560 for (i = 0; i < rows; ++i) {
562 register cxdouble lambda = cpl_table_get_double(extinction,
565 cpl_table_set_double(_extinction,
"LAMBDA", i,
574 cpl_table_delete(_extinction);
577 cpl_msg_debug(_id,
"Column type (%d) is not supported for "
578 "extinction tables!",
579 cpl_table_get_column_type(extinction,
"LAMBDA"));
586 switch (cpl_table_get_column_type(extinction, site)) {
589 for (i = 0; i < rows; ++i) {
591 register cxdouble aext = cpl_table_get_float(extinction,
594 cpl_table_set_double(_extinction,
"EXTINCTION", i, aext);
600 case CPL_TYPE_DOUBLE:
602 for (i = 0; i < rows; ++i) {
604 register cxdouble aext = cpl_table_get_double(extinction,
607 cpl_table_set_double(_extinction,
"EXTINCTION", i, aext);
615 cpl_table_delete(_extinction);
618 cpl_msg_debug(_id,
"Column type (%d) is not supported for "
619 "extinction tables!",
620 cpl_table_get_column_type(extinction, site));
633 inline static cpl_image*
634 _giraffe_compute_mean_sky(
const cpl_image* spectra,
const cpl_table* fibers)
638 cxint ns = cpl_image_get_size_x(spectra);
639 cxint nw = cpl_image_get_size_y(spectra);
641 cxint* sky_fibers = cx_calloc(ns,
sizeof(cxint));
643 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
645 cxdouble* _sky = NULL;
647 cpl_image* sky = NULL;
650 cx_assert(ns == cpl_table_get_nrow(fibers));
657 for (i = 0; i < ns; ++i) {
659 const cxchar* s = cpl_table_get_string(fibers,
"Retractor", i);
661 if (strstr(s,
"-Sky") != NULL) {
663 cpl_table_get_int(fibers,
"INDEX", i, NULL) - 1;
686 sky = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
687 _sky = cpl_image_get_data_double(sky);
691 if (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE) {
693 cxdouble* sky_raw = cx_calloc(nsky,
sizeof(cxdouble));
696 for (i = 0; i < nw; ++i) {
698 register cxint j = 0;
701 for (j = 0; j < nsky; ++j) {
703 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
704 sky_fibers[j], NULL);
709 sky_raw[j] = _spectra[i * ns + sky_fibers[j]] / t;
713 _sky[i] = giraffe_array_median(sky_raw, nsky);
723 cxdouble* sky_raw = cx_calloc(nsky,
sizeof(cxdouble));
726 for (i = 0; i < nw; ++i) {
728 register cxint j = 0;
731 for (j = 0; j < nsky; ++j) {
732 sky_raw[j] = _spectra[i * ns + sky_fibers[j]];
735 _sky[i] = giraffe_array_median(sky_raw, nsky);
747 if (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE) {
749 for (i = 0; i < nsky; ++i) {
751 register cxint j = 0;
753 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
754 sky_fibers[i], NULL);
759 for (j = 0; j < nw; ++j) {
760 _sky[j] += _spectra[j * ns + sky_fibers[i]] / t;
768 for (i = 0; i < nsky; ++i) {
770 register cxint j = 0;
773 for (j = 0; j < nw; ++j) {
774 _sky[j] += _spectra[j * ns + sky_fibers[i]];
781 cpl_image_divide_scalar(sky, nsky);
793 inline static cpl_image*
794 _giraffe_subtract_mean_sky(
const cpl_image* spectra,
const cpl_image* sky,
795 const cpl_table* fibers)
799 cxint ns = cpl_image_get_size_x(spectra);
800 cxint nw = cpl_image_get_size_y(spectra);
802 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
803 const cxdouble* _sky = cpl_image_get_data_double_const(sky);
805 cpl_image* result = cpl_image_new(ns, nw, CPL_TYPE_DOUBLE);
807 cxdouble* _result = cpl_image_get_data_double(result);
810 cx_assert((fibers == NULL) || (ns == cpl_table_get_nrow(fibers)));
811 cx_assert(nw == cpl_image_get_size_y(sky));
820 if ((fibers != NULL) &&
821 (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE)) {
823 for (i = 0; i < ns; ++i) {
825 register cxint j = 0;
827 cpl_table_get_int(fibers,
"INDEX", i, NULL) - 1;
829 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
833 for (j = 0; j < nw; ++j) {
835 register cxint l = j * ns + i;
837 _result[l] += _spectra[l] - t * _sky[j];
846 for (i = 0; i < ns; ++i) {
848 register cxint j = 0;
850 for (j = 0; j < nw; ++j) {
852 register cxint k = j * ns + i;
854 _result[k] += _spectra[k] - _sky[j];
867 inline static cpl_image*
868 _giraffe_integrate_flux(
const cpl_image* spectra,
const cpl_table* fibers)
872 cxint nw = cpl_image_get_size_y(spectra);
873 cxint ns = cpl_image_get_size_x(spectra);
875 cpl_image* result = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
878 cx_assert(ns == cpl_table_get_nrow(fibers));
880 for (i = 0; i < ns; ++i) {
882 const cxchar* s = cpl_table_get_string(fibers,
"Retractor", i);
884 cxint rp = cpl_table_get_int(fibers,
"RP", i, NULL);
892 if ((rp != -1) && (strstr(s,
"-Sky") == NULL)) {
894 register cxint j = 0;
896 const cxdouble* _spectra =
897 cpl_image_get_data_double_const(spectra);
899 cxdouble* _result = cpl_image_get_data_double(result);
902 for (j = 0; j < nw; ++j) {
903 _result[j] += _spectra[j * ns + i];
936 _giraffe_correct_extinction(cpl_image* spectrum, cpl_propertylist* properties,
937 const cpl_table* extinction)
940 const cxchar*
const _id =
"_giraffe_correct_extinction";
950 cxdouble latitude = 0.;
951 cxdouble exptime = 0.;
952 cxdouble wlstart = 0.;
953 cxdouble wlstep = 0.;
954 cxdouble airmass = -1.;
955 cxdouble* flx = NULL;
957 cpl_table* _extinction = NULL;
960 if ((spectrum == NULL) || (properties == NULL) || (extinction == NULL)) {
965 if (cpl_image_get_size_x(spectrum) != 1) {
967 cpl_msg_debug(_id,
"Input spectrum is not a 1d spectrum!");
979 _extinction = _giraffe_setup_extinction(extinction);
981 if (_extinction == NULL) {
990 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
991 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
993 cpl_msg_debug(_id,
"Observed spectrum does not have a valid "
996 cpl_table_delete(_extinction);
1003 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1004 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1011 giraffe_error_push();
1013 alpha = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1014 delta = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1015 lst = cpl_propertylist_get_double(properties, GIALIAS_LST);
1016 latitude = cpl_propertylist_get_double(properties, GIALIAS_TEL_LAT);
1017 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
1019 status = cpl_error_get_code();
1021 if (status == CPL_ERROR_NONE) {
1028 if ((airmass < 1.) || (status != CPL_ERROR_NONE)) {
1030 cxbool start = cpl_propertylist_has(properties,
1031 GIALIAS_AIRMASS_START);
1032 cxbool end = cpl_propertylist_has(properties,
1033 GIALIAS_AIRMASS_END);
1035 if ((start == FALSE) || (end == FALSE)) {
1037 cpl_msg_debug(_id,
"Unable to compute airmass of the "
1040 cpl_table_delete(_extinction);
1048 airmass = 0.5 * (cpl_propertylist_get_double(properties,
1049 GIALIAS_AIRMASS_START) +
1050 cpl_propertylist_get_double(properties,
1051 GIALIAS_AIRMASS_END));
1057 giraffe_error_pop();
1064 nw = cpl_image_get_size_y(spectrum);
1065 flx = cpl_image_get_data_double(spectrum);
1067 for (i = 0; i < nw; ++i) {
1071 cxdouble wlen = wlstart + (i - 1) * wlstep;
1075 giraffe_error_push();
1077 ext = _giraffe_interpolate_spline_hermite(_extinction,
1078 "LAMBDA",
"EXTINCTION", wlen, &first);
1080 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1082 cpl_table_delete(_extinction);
1089 giraffe_error_pop();
1104 flx[i] *= pow(10., 0.4 * ext * airmass);
1108 cpl_table_delete(_extinction);
1134 inline static cpl_image*
1135 _giraffe_compute_response(
const cpl_image* spectrum,
1136 const cpl_propertylist* properties,
1137 const cpl_table* refflux)
1140 const cxchar*
const _id =
"giraffe_compute_response";
1146 const cxdouble* flx = NULL;
1148 cxdouble wlstart = 0.;
1149 cxdouble wlstep = 0.;
1150 cxdouble* rdata = NULL;
1153 cpl_image* response = NULL;
1157 if ((spectrum == NULL) || (properties == NULL) || (refflux == NULL)) {
1161 if (cpl_image_get_size_x(spectrum) != 1) {
1163 cpl_msg_debug(_id,
"Observed spectrum is not a 1d spectrum!");
1173 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
1174 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
1176 cpl_msg_debug(_id,
"Observed spectrum does not have a valid "
1177 "wavelength grid!");
1182 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1183 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1185 nw = cpl_image_get_size_y(spectrum);
1193 flx = cpl_image_get_data_double_const(spectrum);
1195 response = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
1196 rdata = cpl_image_get_data_double(response);
1198 for (i = 0; i < nw; ++i) {
1202 cxdouble wlen = wlstart + (i - 1) * wlstep;
1206 giraffe_error_push();
1208 sflx = _giraffe_interpolate_spline_hermite(refflux,
1209 "LAMBDA",
"F_LAMBDA", wlen, &first);
1211 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1213 cpl_image_delete(response);
1220 giraffe_error_pop();
1222 rdata[i] = flx[i] / sflx;
1232 giraffe_select_flux_standard(
const GiTable* catalog,
const GiImage* spectra,
1236 const cxchar*
const _id =
"giraffe_select_flux_standard";
1243 cxdouble std_ra = 0.;
1244 cxdouble std_dec = 0.;
1245 cxdouble min_dist = 0.;
1247 const cpl_table* _catalog = NULL;
1249 cpl_table* _flux = NULL;
1251 const cpl_propertylist* properties = NULL;
1253 GiTable* flux = NULL;
1256 if ((catalog == NULL) || (spectra == NULL)) {
1261 cx_assert(_catalog != NULL);
1270 cx_assert(properties != NULL);
1272 giraffe_error_push();
1274 ra = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1275 dec = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1277 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1281 giraffe_error_pop();
1288 cpl_msg_debug(_id,
"Searching flux standard by name...");
1290 if ((cpl_propertylist_has(properties, GIALIAS_TARGET) == TRUE) &&
1291 (cpl_table_has_column(_catalog,
"OBJECT") == TRUE)) {
1293 const cxchar* target = cpl_propertylist_get_string(properties,
1297 if ((target != NULL) && (target[0] !=
'\0')) {
1299 register cxint i = 0;
1302 for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1304 const cxchar*
object = cpl_table_get_string(_catalog,
1308 if (strcmp(target,
object) == 0) {
1310 cxdouble cat_ra = cpl_table_get_double(_catalog,
1312 cxdouble cat_dec = cpl_table_get_double(_catalog,
1313 "DEC_DEG", i, NULL);
1316 std_ra = cpl_table_get_double(_catalog,
1318 std_dec = cpl_table_get_double(_catalog,
1319 "DEC_DEG", i, NULL);
1321 min_dist = _giraffe_compute_separation(ra, dec,
1335 cpl_msg_debug(_id,
"%d flux standards found...", nmatch);
1343 cpl_msg_debug(_id,
"Searching flux standard by coordinates...");
1345 if ((cpl_table_has_column(_catalog,
"RA_DEG") == FALSE) ||
1346 (cpl_table_has_column(_catalog,
"DEC_DEG") == FALSE)) {
1348 cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
1355 for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1357 cxdouble cat_ra = cpl_table_get_double(_catalog,
"RA_DEG",
1359 cxdouble cat_dec = cpl_table_get_double(_catalog,
"DEC_DEG",
1370 dist = _giraffe_compute_separation(ra, dec, cat_ra, cat_dec);
1372 if ((i == 0) || (dist < min_dist)) {
1378 if (dist < max_dist) {
1387 cpl_msg_debug(_id,
"%d flux standards found...", nmatch);
1397 const cxchar*
object = cpl_table_get_string(_catalog,
1400 cpl_msg_debug(_id,
"No flux standard found within %.4f arcsec",
1402 cpl_msg_debug(_id,
"The closest object ('%s') at (RA, Dec) = "
1403 "(%.4f, %.4f) is %.4f arcsec away",
object, std_ra,
1406 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1416 const cxchar*
object = cpl_table_get_string(_catalog,
1419 cpl_msg_debug(_id,
"Flux standard ('%s') at (RA, Dec) = "
1420 "(%.4f, %.4f) found at a distance of %.4f arcsec",
1421 object, std_ra, std_dec, min_dist);
1427 _flux = _giraffe_create_flux_table(_catalog, row);
1436 const cxchar*
object = cpl_table_get_string(_catalog,
1439 cpl_msg_debug(_id,
"%d flux standards found within %.4f arcsec",
1441 cpl_msg_debug(_id,
"The closest object ('%s') at (RA, Dec) = "
1442 "(%.4f, %.4f) is %.4f arcsec away",
object, std_ra,
1445 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1454 if (_flux != NULL) {
1461 cpl_table_delete(_flux);
1512 const GiTable* fibers,
const GiImage* flat,
1513 const GiTable* flux,
const GiTable* extinction,
1514 const GiFxCalibrationConfig* config)
1517 const cxchar*
const _id =
"giraffe_calibrate_flux";
1520 const cxint xrad = 0;
1521 const cxint yrad = 7;
1523 const cxdouble UT_M1_VIGNETTED_AREA = 517533.407382;
1524 const cxdouble H_PLANCK = 6.62606896e-27;
1525 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;
1555 if (result == NULL) {
1559 if ((spectra == NULL) || (spectra->spectra == NULL)) {
1563 if (fibers == NULL) {
1567 if ((flux == NULL) || (extinction == NULL)) {
1571 if (config == NULL) {
1576 if ((result->response != NULL) || (result->efficiency != NULL)) {
1578 gi_warning(
"%s: Results structure at %p is not empty! Contents "
1579 "might be lost.", _id, result);
1584 cx_assert(properties != NULL);
1587 cx_assert(_spectra != NULL);
1590 cx_assert(_extinction != NULL);
1593 cx_assert(_flux != NULL);
1604 if (config->sky_subtraction == TRUE) {
1606 cpl_image* sky_spectrum = NULL;
1607 cpl_image* sspectra = NULL;
1612 ns = cpl_image_get_size_x(_spectra);
1614 sky_spectrum = _giraffe_compute_mean_sky(_spectra, _fibers);
1616 if (sky_spectrum == NULL) {
1620 giraffe_error_push();
1622 avgsky = cpl_image_get_mean(sky_spectrum);
1624 sspectra = _giraffe_subtract_mean_sky(_spectra, sky_spectrum,
1627 fluxobs = _giraffe_integrate_flux(sspectra, _fibers);
1629 cpl_image_delete(sky_spectrum);
1630 sky_spectrum = NULL;
1632 cpl_image_delete(sspectra);
1635 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1639 giraffe_error_pop();
1646 fluxobs = _giraffe_integrate_flux(_spectra, _fibers);
1655 status = _giraffe_correct_extinction(fluxobs, properties, _extinction);
1658 cpl_msg_warning(_id,
"Extinction correction failed!");
1662 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
1663 conad = cpl_propertylist_get_double(properties, GIALIAS_CONAD);
1664 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1665 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
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);