29#include <cxmessages.h>
30#include <cxstrutils.h>
32#include <cpl_propertylist.h>
40#include "gimessages.h"
41#include "giastroutils.h"
43#include "gifxcalibration.h"
70_giraffe_compute_separation(cxdouble ra_0, cxdouble dec_0,
71 cxdouble ra_1, cxdouble dec_1)
74 const cxdouble deg2rad = CX_PI / 180.;
84 dist = sin(dec_0) * sin(dec_1) +
85 cos(dec_0) * cos(dec_1) * cos(ra_0 - ra_1);
87 dist = CX_CLAMP(dist, -1., 1.);
88 dist = acos(dist) / deg2rad * 3600.;
113inline static cxdouble
114_giraffe_spline_hermite(cxdouble xp,
const cxdouble* x,
const cxdouble* y,
115 cxint n, cxint* istart)
131 if ((x[0] <= x[n - 1]) && ((xp < x[0]) || (xp > x[n - 1]))) {
135 if ((x[0] > x[n - 1]) && ((xp > x[0]) || (xp < x[n - 1]))) {
139 if (x[0] <= x[n - 1]) {
141 for (i = *istart + 1; i <= n && xp >= x[i - 1]; ++i) {
148 for (i = *istart + 1; i <= n && xp <= x[i - 1]; ++i) {
158 lp1 = 1. / (x[i - 1] - x[i]);
162 yp1 = (y[1] - y[0]) / (x[1] - x[0]);
165 yp1 = (y[i] - y[i - 2]) / (x[i] - x[i - 2]);
169 yp2 = (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
172 yp2 = (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]);
182 yp = y[i - 1] * (1. - 2. * lp1 * xpi) * l1 * l1 +
183 y[i] * (1. - 2. * lp2 * xpi1) * l2 * l2 +
184 yp1 * xpi * l1 * l1 +
185 yp2 * xpi1 * l2 * l2;
211inline static cxdouble
212_giraffe_interpolate_spline_hermite(
const cpl_table* tbl,
213 const cxchar* xlabel,
214 const cxchar* ylabel,
215 cxdouble xp, cxint* istart)
218 cxint n = cpl_table_get_nrow(tbl);
220 const cxdouble* x = cpl_table_get_data_double_const(tbl, xlabel);
221 const cxdouble* y = cpl_table_get_data_double_const(tbl, ylabel);
224 return _giraffe_spline_hermite(xp, x, y, n ,istart);
252inline static cpl_table*
253_giraffe_create_flux_table(
const cpl_table* catalog, cxint row)
256 const cxchar*
const _id =
"_giraffe_create_flux_table";
259 const cxchar* columns[] = {
"LAMBDA",
"BIN_WIDTH",
"F_LAMBDA"};
260 const cxchar* units = NULL;
262 cxint ndata = cpl_table_get_int(catalog,
"NDATA", row, NULL);
266 const cxdouble ang2nm = 0.1;
268 cpl_table* flux = NULL;
276 giraffe_error_push();
278 flux = cpl_table_new(ndata);
280 for (i = 0; i < CX_N_ELEMENTS(columns); ++i) {
282 const cpl_array* data = cpl_table_get_array(catalog, columns[i], row);
284 cpl_type type = cpl_table_get_column_type(catalog, columns[i]);
287 cpl_table_new_column(flux, columns[i], CPL_TYPE_DOUBLE);
289 if ((data != NULL) && (ndata <= cpl_array_get_size(data))) {
291 switch (type & ~CPL_TYPE_POINTER) {
297 register cxdouble value = 0.;
300 for (j = 0; j < ndata; ++j) {
301 value = cpl_array_get_float(data, j, NULL);
302 cpl_table_set_double(flux, columns[i], j, value);
309 case CPL_TYPE_DOUBLE:
314 register cxdouble value = 0.;
317 for (j = 0; j < ndata; ++j) {
318 value = cpl_array_get_double(data, j, NULL);
319 cpl_table_set_double(flux, columns[i], j, value);
328 cpl_error_set(_id, CPL_ERROR_INVALID_TYPE);
344 cpl_table_multiply_scalar(flux, columns[2], 1.e-16);
354 units = cpl_table_get_column_unit(catalog, columns[0]);
356 if ((units != NULL) && (cx_strncasecmp(units,
"ang", 3) == 0)) {
358 cpl_msg_debug(_id,
"Found units '%s'. Converting flux standard "
359 "from Angstrom to nano meters", units);
361 cpl_table_multiply_scalar(flux, columns[0], ang2nm);
362 cpl_table_set_column_unit(flux, columns[0],
"nm");
364 cpl_table_multiply_scalar(flux, columns[1], ang2nm);
365 cpl_table_set_column_unit(flux, columns[1],
"nm");
367 cpl_table_divide_scalar(flux, columns[2], ang2nm);
368 cpl_table_set_column_unit(flux, columns[2],
"erg/s/cm^2/nm");
375 cpl_msg_debug(_id,
"No units for wavelength column. Assuming "
381 cpl_msg_debug(_id,
"Found unknown units ('%s') for wavelength "
382 "column. Assuming nano meters.", units);
389 if (cpl_error_get_code() != CPL_ERROR_NONE) {
390 cpl_table_delete(flux);
418inline static cpl_table*
419_giraffe_setup_extinction(
const cpl_table* extinction)
422 const cxchar*
const _id =
"_giraffe_setup_extinction";
424 const cxchar* site = NULL;
425 const cxchar* units = NULL;
426 const cxchar* sites[] = {
"PARANAL",
"LA_SILLA", NULL};
431 const cxdouble ang2nm = 0.1;
435 cpl_table* _extinction = NULL;
438 if (cpl_table_has_column(extinction,
"LAMBDA") == FALSE) {
440 cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
450 units = cpl_table_get_column_unit(extinction,
"LAMBDA");
452 if ((units != NULL) && (cx_strncasecmp(units,
"ang", 3) == 0)) {
456 cpl_msg_debug(_id,
"Found units '%s'. Converting wavelength"
457 "from Angstrom to nano meters", units);
464 cpl_msg_debug(_id,
"No units for wavelength column. "
465 "Assuming nano meters.");
470 if (cx_strncasecmp(units,
"nm", 2) == 0) {
472 cpl_msg_debug(_id,
"Found units nano meters ('%s') for "
473 "wavelength column.", units);
478 cpl_msg_debug(_id,
"Found unknown units ('%s') for "
479 "wavelength column. Assuming nano meters.", units);
492 while ((site == NULL) && (sites[i] != NULL)) {
494 if (cpl_table_has_column(extinction, sites[i]) == TRUE) {
506 cpl_msg_debug(_id,
"No matching observatory site found!");
515 rows = cpl_table_get_nrow(extinction);
517 giraffe_error_push();
519 _extinction = cpl_table_new(rows);
520 cpl_table_new_column(_extinction,
"LAMBDA", CPL_TYPE_DOUBLE);
521 cpl_table_set_column_unit(_extinction,
"LAMBDA",
"nm");
523 cpl_table_new_column(_extinction,
"EXTINCTION", CPL_TYPE_DOUBLE);
524 cpl_table_set_column_unit(_extinction,
"EXTINCTION",
"mag/airmass");
526 if (cpl_error_get_code() != CPL_ERROR_NONE) {
528 cpl_table_delete(_extinction);
538 switch (cpl_table_get_column_type(extinction,
"LAMBDA")) {
541 for (i = 0; i < rows; ++i) {
543 register cxdouble lambda = cpl_table_get_float(extinction,
546 cpl_table_set_double(_extinction,
"LAMBDA", i,
553 case CPL_TYPE_DOUBLE:
555 for (i = 0; i < rows; ++i) {
557 register cxdouble lambda = cpl_table_get_double(extinction,
560 cpl_table_set_double(_extinction,
"LAMBDA", i,
569 cpl_table_delete(_extinction);
572 cpl_msg_debug(_id,
"Column type (%d) is not supported for "
573 "extinction tables!",
574 cpl_table_get_column_type(extinction,
"LAMBDA"));
581 switch (cpl_table_get_column_type(extinction, site)) {
584 for (i = 0; i < rows; ++i) {
586 register cxdouble aext = cpl_table_get_float(extinction,
589 cpl_table_set_double(_extinction,
"EXTINCTION", i, aext);
595 case CPL_TYPE_DOUBLE:
597 for (i = 0; i < rows; ++i) {
599 register cxdouble aext = cpl_table_get_double(extinction,
602 cpl_table_set_double(_extinction,
"EXTINCTION", i, aext);
610 cpl_table_delete(_extinction);
613 cpl_msg_debug(_id,
"Column type (%d) is not supported for "
614 "extinction tables!",
615 cpl_table_get_column_type(extinction, site));
628inline static cpl_image*
629_giraffe_compute_mean_sky(
const cpl_image* spectra,
const cpl_table* fibers)
633 cxint ns = cpl_image_get_size_x(spectra);
634 cxint nw = cpl_image_get_size_y(spectra);
636 cxint* sky_fibers = cx_calloc(ns,
sizeof(cxint));
638 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
640 cxdouble* _sky = NULL;
642 cpl_image* sky = NULL;
644 cx_assert(ns == cpl_table_get_nrow(fibers));
651 for (i = 0; i < ns; ++i) {
653 const cxchar* s = cpl_table_get_string(fibers,
"Retractor", i);
655 if (strstr(s,
"-Sky") != NULL) {
657 cpl_table_get_int(fibers,
"INDEX", i, NULL) - 1;
680 sky = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
681 _sky = cpl_image_get_data_double(sky);
685 if (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE) {
687 cxdouble* sky_raw = cx_calloc(nsky,
sizeof(cxdouble));
690 for (i = 0; i < nw; ++i) {
692 register cxint j = 0;
695 for (j = 0; j < nsky; ++j) {
697 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
698 sky_fibers[j], NULL);
703 sky_raw[j] = _spectra[i * ns + sky_fibers[j]] / t;
707 _sky[i] = giraffe_array_median(sky_raw, nsky);
717 cxdouble* sky_raw = cx_calloc(nsky,
sizeof(cxdouble));
720 for (i = 0; i < nw; ++i) {
722 register cxint j = 0;
725 for (j = 0; j < nsky; ++j) {
726 sky_raw[j] = _spectra[i * ns + sky_fibers[j]];
729 _sky[i] = giraffe_array_median(sky_raw, nsky);
741 if (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE) {
743 for (i = 0; i < nsky; ++i) {
745 register cxint j = 0;
747 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
748 sky_fibers[i], NULL);
753 for (j = 0; j < nw; ++j) {
754 _sky[j] += _spectra[j * ns + sky_fibers[i]] / t;
762 for (i = 0; i < nsky; ++i) {
764 register cxint j = 0;
767 for (j = 0; j < nw; ++j) {
768 _sky[j] += _spectra[j * ns + sky_fibers[i]];
775 cpl_image_divide_scalar(sky, nsky);
787inline static cpl_image*
788_giraffe_subtract_mean_sky(
const cpl_image* spectra,
const cpl_image* sky,
789 const cpl_table* fibers)
793 cxint ns = cpl_image_get_size_x(spectra);
794 cxint nw = cpl_image_get_size_y(spectra);
796 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
797 const cxdouble* _sky = cpl_image_get_data_double_const(sky);
799 cpl_image* result = cpl_image_new(ns, nw, CPL_TYPE_DOUBLE);
801 cxdouble* _result = cpl_image_get_data_double(result);
804 cx_assert((fibers == NULL) || (ns == cpl_table_get_nrow(fibers)));
805 cx_assert(nw == cpl_image_get_size_y(sky));
814 if ((fibers != NULL) &&
815 (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE)) {
817 for (i = 0; i < ns; ++i) {
819 register cxint j = 0;
821 cpl_table_get_int(fibers,
"INDEX", i, NULL) - 1;
823 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
827 for (j = 0; j < nw; ++j) {
829 register cxint l = j * ns + i;
831 _result[l] += _spectra[l] - t * _sky[j];
840 for (i = 0; i < ns; ++i) {
842 register cxint j = 0;
844 for (j = 0; j < nw; ++j) {
846 register cxint k = j * ns + i;
848 _result[k] += _spectra[k] - _sky[j];
861inline static cpl_image*
862_giraffe_integrate_flux(
const cpl_image* spectra,
const cpl_table* fibers)
866 cxint nw = cpl_image_get_size_y(spectra);
867 cxint ns = cpl_image_get_size_x(spectra);
869 cpl_image* result = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
872 cx_assert(ns == cpl_table_get_nrow(fibers));
874 for (i = 0; i < ns; ++i) {
876 const cxchar* s = cpl_table_get_string(fibers,
"Retractor", i);
878 cxint rp = cpl_table_get_int(fibers,
"RP", i, NULL);
886 if ((rp != -1) && (strstr(s,
"-Sky") == NULL)) {
888 register cxint j = 0;
890 const cxdouble* _spectra =
891 cpl_image_get_data_double_const(spectra);
893 cxdouble* _result = cpl_image_get_data_double(result);
896 for (j = 0; j < nw; ++j) {
897 _result[j] += _spectra[j * ns + i];
930_giraffe_correct_extinction(cpl_image* spectrum, cpl_propertylist* properties,
931 const cpl_table* extinction)
934 const cxchar*
const _id =
"_giraffe_correct_extinction";
944 cxdouble latitude = 0.;
945 cxdouble exptime = 0.;
946 cxdouble wlstart = 0.;
947 cxdouble wlstep = 0.;
948 cxdouble airmass = -1.;
949 cxdouble* flx = NULL;
951 cpl_table* _extinction = NULL;
954 if ((spectrum == NULL) || (properties == NULL) || (extinction == NULL)) {
959 if (cpl_image_get_size_x(spectrum) != 1) {
961 cpl_msg_debug(_id,
"Input spectrum is not a 1d spectrum!");
973 _extinction = _giraffe_setup_extinction(extinction);
975 if (_extinction == NULL) {
984 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
985 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
987 cpl_msg_debug(_id,
"Observed spectrum does not have a valid "
990 cpl_table_delete(_extinction);
997 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
998 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1005 giraffe_error_push();
1007 alpha = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1008 delta = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1009 lst = cpl_propertylist_get_double(properties, GIALIAS_LST);
1010 latitude = cpl_propertylist_get_double(properties, GIALIAS_TEL_LAT);
1011 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
1013 status = cpl_error_get_code();
1015 if (status == CPL_ERROR_NONE) {
1022 if ((airmass < 1.) || (status != CPL_ERROR_NONE)) {
1024 cxbool start = cpl_propertylist_has(properties,
1025 GIALIAS_AIRMASS_START);
1026 cxbool end = cpl_propertylist_has(properties,
1027 GIALIAS_AIRMASS_END);
1029 if ((start == FALSE) || (end == FALSE)) {
1031 cpl_msg_debug(_id,
"Unable to compute airmass of the "
1034 cpl_table_delete(_extinction);
1042 airmass = 0.5 * (cpl_propertylist_get_double(properties,
1043 GIALIAS_AIRMASS_START) +
1044 cpl_propertylist_get_double(properties,
1045 GIALIAS_AIRMASS_END));
1051 giraffe_error_pop();
1058 nw = cpl_image_get_size_y(spectrum);
1059 flx = cpl_image_get_data_double(spectrum);
1061 for (i = 0; i < nw; ++i) {
1065 cxdouble wlen = wlstart + (i - 1) * wlstep;
1069 giraffe_error_push();
1071 ext = _giraffe_interpolate_spline_hermite(_extinction,
1072 "LAMBDA",
"EXTINCTION", wlen, &first);
1074 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1076 cpl_table_delete(_extinction);
1083 giraffe_error_pop();
1098 flx[i] *= pow(10., 0.4 * ext * airmass);
1102 cpl_table_delete(_extinction);
1128inline static cpl_image*
1129_giraffe_compute_response(
const cpl_image* spectrum,
1130 const cpl_propertylist* properties,
1131 const cpl_table* refflux)
1134 const cxchar*
const _id =
"giraffe_compute_response";
1140 const cxdouble* flx = NULL;
1142 cxdouble wlstart = 0.;
1143 cxdouble wlstep = 0.;
1144 cxdouble* rdata = NULL;
1147 cpl_image* response = NULL;
1151 if ((spectrum == NULL) || (properties == NULL) || (refflux == NULL)) {
1155 if (cpl_image_get_size_x(spectrum) != 1) {
1157 cpl_msg_debug(_id,
"Observed spectrum is not a 1d spectrum!");
1167 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
1168 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
1170 cpl_msg_debug(_id,
"Observed spectrum does not have a valid "
1171 "wavelength grid!");
1176 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1177 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1179 nw = cpl_image_get_size_y(spectrum);
1187 flx = cpl_image_get_data_double_const(spectrum);
1189 response = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
1190 rdata = cpl_image_get_data_double(response);
1192 for (i = 0; i < nw; ++i) {
1196 cxdouble wlen = wlstart + (i - 1) * wlstep;
1200 giraffe_error_push();
1202 sflx = _giraffe_interpolate_spline_hermite(refflux,
1203 "LAMBDA",
"F_LAMBDA", wlen, &first);
1205 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1207 cpl_image_delete(response);
1214 giraffe_error_pop();
1216 rdata[i] = flx[i] / sflx;
1226giraffe_select_flux_standard(
const GiTable* catalog,
const GiImage* spectra,
1230 const cxchar*
const _id =
"giraffe_select_flux_standard";
1235 cxdouble targ_alpha = 0.;
1236 cxdouble targ_delta = 0.;
1237 cxdouble ra_hh = 0.; cxdouble ra_mm = 0.; cxdouble ra_ss = 0.;
1238 cxdouble dec_dd = 0.; cxdouble dec_mm = 0.; cxdouble dec_ss = 0.;
1241 cxdouble std_ra = 0.;
1242 cxdouble std_dec = 0.;
1243 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 if (cpl_propertylist_has(properties, GIALIAS_TARG_ALPHA) ==1) {
1275 targ_alpha = cpl_propertylist_get_double(properties, GIALIAS_TARG_ALPHA);
1276 ra_hh = (int) (targ_alpha/10000);
1277 ra_mm = (int) ((targ_alpha-ra_hh*10000)/100);
1278 ra_ss = targ_alpha -(ra_hh*10000 + ra_mm*100);
1280 ra_hh = ra_hh * 15.0;
1281 ra_mm = ra_mm * 15.0 / 60.0;
1282 ra_ss = ra_ss * 15.0 / 3600.0;
1283 ra = ra_hh + ra_mm + ra_ss;
1285 ra = abs(ra - 360.0);
1289 ra = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1293 if (cpl_propertylist_has(properties, GIALIAS_TARG_DELTA) ==1 ){
1294 targ_delta = cpl_propertylist_get_double(properties, GIALIAS_TARG_DELTA);
1295 dec_dd = (int) (targ_delta/10000);
1296 dec_mm = (int) ((targ_delta-dec_dd*10000)/100);
1297 dec_ss = targ_delta -(dec_dd*10000 + dec_mm*100);
1300 dec = dec_dd + (dec_mm/60.0) + (dec_ss/3600.0);
1303 dec = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1307 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1311 cpl_msg_info(_id,
"TARG.ALPHA : %f ", targ_alpha);
1312 cpl_msg_info(_id,
"RA Used : %f ", ra);
1313 cpl_msg_info(_id,
"TARG.DELTA : %f ", targ_delta);
1314 cpl_msg_info(_id,
"DEC Used : %f ", dec);
1316 giraffe_error_pop();
1323 cpl_msg_info(_id,
"Searching flux standard by coordinates...");
1325 if ((cpl_table_has_column(_catalog,
"RA_DEG") == FALSE) ||
1326 (cpl_table_has_column(_catalog,
"DEC_DEG") == FALSE)) {
1328 cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
1335 for (
int i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1337 cxdouble cat_ra = cpl_table_get_double(_catalog,
"RA_DEG",
1339 cxdouble cat_dec = cpl_table_get_double(_catalog,
"DEC_DEG",
1350 dist = _giraffe_compute_separation(ra, dec, cat_ra, cat_dec);
1352 if ((i == 0) || (dist < min_dist)) {
1358 if (dist < max_dist) {
1367 cpl_msg_info(_id,
"%d flux standards found...", nmatch);
1374 cpl_msg_info(_id,
"Searching flux standard by name...");
1376 if ((cpl_propertylist_has(properties, GIALIAS_TARGET) == TRUE) &&
1377 (cpl_table_has_column(_catalog,
"OBJECT") == TRUE)) {
1379 const cxchar* target = cpl_propertylist_get_string(properties,
1383 if ((target != NULL) && (target[0] !=
'\0')) {
1385 register cxint i = 0;
1388 for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1390 const cxchar*
object = cpl_table_get_string(_catalog,
1394 if (strcmp(target,
object) == 0) {
1396 cxdouble cat_ra = cpl_table_get_double(_catalog,
1398 cxdouble cat_dec = cpl_table_get_double(_catalog,
1399 "DEC_DEG", i, NULL);
1402 std_ra = cpl_table_get_double(_catalog,
1404 std_dec = cpl_table_get_double(_catalog,
1405 "DEC_DEG", i, NULL);
1407 min_dist = _giraffe_compute_separation(ra, dec,
1421 cpl_msg_info(_id,
"%d flux standards found...", nmatch);
1431 const cxchar*
object = cpl_table_get_string(_catalog,
1434 cpl_msg_info(_id,
"No flux standard found within %.4f arcsec",
1436 cpl_msg_info(_id,
"The closest object ('%s') at (RA, Dec) = "
1437 "(%.4f, %.4f) is %.4f arcsec away",
object, std_ra,
1440 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1450 const cxchar*
object = cpl_table_get_string(_catalog,
1453 cpl_msg_info(_id,
"Flux standard ('%s') at (RA, Dec) = "
1454 "(%.4f, %.4f) found at a distance of %.4f arcsec",
1455 object, std_ra, std_dec, min_dist);
1461 _flux = _giraffe_create_flux_table(_catalog, row);
1470 const cxchar*
object = cpl_table_get_string(_catalog,
1473 cpl_msg_info(_id,
"%d flux standards found within %.4f arcsec",
1475 cpl_msg_info(_id,
"The closest object ('%s') at (RA, Dec) = "
1476 "(%.4f, %.4f) is %.4f arcsec away",
object, std_ra,
1479 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1488 if (_flux != NULL) {
1495 cpl_table_delete(_flux);
1546 const GiTable* fibers,
const GiImage* flat,
1547 const GiTable* flux,
const GiTable* extinction,
1548 const GiFxCalibrationConfig* config)
1551 const cxchar*
const _id =
"giraffe_calibrate_flux";
1554 const cxint xrad = 0;
1555 const cxint yrad = 7;
1557 const cxdouble UT_M1_VIGNETTED_AREA = 517533.407382;
1558 const cxdouble H_PLANCK = 6.62606896e-27;
1559 const cxdouble C_LIGHT = 2.99792458e17;
1565 const cxdouble* rdata = NULL;
1567 cxdouble conad = 0.;
1568 cxdouble wlstep = 0.;
1569 cxdouble wlstart = 0.;
1570 cxdouble exptime = 0.;
1571 cxdouble avgsky = 0.;
1572 cxdouble scale = UT_M1_VIGNETTED_AREA / (H_PLANCK * C_LIGHT);
1574 cpl_propertylist* properties = NULL;
1576 cpl_mask* filter = NULL;
1578 cpl_image* _spectra = NULL;
1579 cpl_image* fluxobs = NULL;
1580 cpl_image* response = NULL;
1581 cpl_image* fresponse = NULL;
1583 cpl_table* _extinction = NULL;
1584 cpl_table* _flux = NULL;
1585 cpl_table* efficiency = NULL;
1589 if (result == NULL) {
1593 if ((spectra == NULL) || (spectra->spectra == NULL)) {
1597 if (fibers == NULL) {
1601 if ((flux == NULL) || (extinction == NULL)) {
1605 if (config == NULL) {
1610 if ((result->response != NULL) || (result->efficiency != NULL)) {
1612 gi_warning(
"%s: Results structure at %p is not empty! Contents "
1613 "might be lost.", _id, result);
1618 cx_assert(properties != NULL);
1621 cx_assert(_spectra != NULL);
1624 cx_assert(_extinction != NULL);
1627 cx_assert(_flux != NULL);
1638 if (config->sky_subtraction == TRUE) {
1640 cpl_image* sky_spectrum = NULL;
1641 cpl_image* sspectra = NULL;
1646 sky_spectrum = _giraffe_compute_mean_sky(_spectra, _fibers);
1648 if (sky_spectrum == NULL) {
1652 giraffe_error_push();
1654 avgsky = cpl_image_get_mean(sky_spectrum);
1656 sspectra = _giraffe_subtract_mean_sky(_spectra, sky_spectrum,
1659 fluxobs = _giraffe_integrate_flux(sspectra, _fibers);
1661 cpl_image_delete(sky_spectrum);
1662 sky_spectrum = NULL;
1664 cpl_image_delete(sspectra);
1667 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1671 giraffe_error_pop();
1678 fluxobs = _giraffe_integrate_flux(_spectra, _fibers);
1687 status = _giraffe_correct_extinction(fluxobs, properties, _extinction);
1690 cpl_msg_warning(_id,
"Extinction correction failed!");
1694 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
1695 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1696 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1705 cpl_image_multiply_scalar(fluxobs, conad / exptime / wlstep);
1713 response = _giraffe_compute_response(fluxobs, properties, _flux);
1715 cpl_image_delete(fluxobs);
1718 if (response == NULL) {
1722 filter = cpl_mask_new(2 * xrad + 1, 2 * yrad + 1);
1723 for (i = 0; i < cpl_mask_get_size_x(filter); ++i) {
1727 for (j = 0; j < cpl_mask_get_size_y(filter); ++j)
1729 cpl_mask_set(filter, i + 1, j + 1, CPL_BINARY_1);
1734 fresponse = cpl_image_new(cpl_image_get_size_x(response),
1735 cpl_image_get_size_y(response),
1736 cpl_image_get_type(response));
1738 cpl_image_filter_mask(fresponse, response, filter,
1739 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
1741 cpl_mask_delete(filter);
1744 cpl_image_delete(response);
1745 response = fresponse;
1747 if (response == NULL) {
1756 giraffe_error_push();
1758 nw = cpl_image_get_size_y(response);
1760 efficiency = cpl_table_new(nw);
1762 cpl_table_new_column(efficiency,
"WLEN", CPL_TYPE_DOUBLE);
1763 cpl_table_new_column(efficiency,
"BINWIDTH", CPL_TYPE_DOUBLE);
1764 cpl_table_new_column(efficiency,
"EFFICIENCY", CPL_TYPE_DOUBLE);
1766 cpl_table_set_column_unit(efficiency,
"WLEN",
"nm");
1767 cpl_table_set_column_unit(efficiency,
"BINWIDTH",
"nm");
1769 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1771 cpl_table_delete(efficiency);
1774 cpl_image_delete(response);
1781 giraffe_error_pop();
1784 rdata = cpl_image_get_data_double_const(response);
1786 for (i = 0; i < nw; ++i) {
1788 cxdouble wl = wlstart + i * wlstep;
1790 cpl_table_set_double(efficiency,
"WLEN", i, wl);
1791 cpl_table_set_double(efficiency,
"BINWIDTH", i, wlstep);
1792 cpl_table_set_double(efficiency,
"EFFICIENCY", i,
1793 rdata[i] / (scale * wl));
1805 if (config->sky_subtraction == TRUE) {
1806 cpl_propertylist_update_double(properties, GIALIAS_SKY_LEVEL, avgsky);
1807 cpl_propertylist_set_comment(properties, GIALIAS_SKY_LEVEL,
1808 "Mean sky level used [ADU].");
1815 cpl_image_delete(response);
1822 cpl_table_delete(efficiency);
1830GiFxCalibrationConfig*
1831giraffe_fxcalibration_config_create(cpl_parameterlist* parameters)
1834 cpl_parameter *p = NULL;
1836 GiFxCalibrationConfig* self = NULL;
1839 if (parameters == NULL) {
1843 self = cx_calloc(1,
sizeof *self);
1844 cx_assert(self != NULL);
1851 self->sky_subtraction = FALSE;
1852 self->max_dist = 3.0;
1859 p = cpl_parameterlist_find(parameters,
1860 "giraffe.fxcalibration.sky.correct");
1863 self->sky_subtraction = cpl_parameter_get_bool(p);
1866 p = cpl_parameterlist_find(parameters,
1867 "giraffe.fxcalibration.max.dist");
1870 self->max_dist = cpl_parameter_get_double(p);
1921 cpl_parameter* p = NULL;
1924 if (parameters == NULL) {
1928 p = cpl_parameter_new_value(
"giraffe.fxcalibration.sky.correct",
1930 "Correct spectra for the sky emission",
1931 "giraffe.fxcalibration",
1933 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"flx-skycorr");
1934 cpl_parameterlist_append(parameters, p);
1936 p = cpl_parameter_new_value(
"giraffe.fxcalibration.max.dist",
1938 "Standar star search radius ",
1939 "giraffe.fxcalibration",
1941 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"max-dist");
1942 cpl_parameterlist_append(parameters, p);
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.
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.
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
cxint giraffe_image_set(GiImage *self, cpl_image *image)
Sets the image data.
GiImage * giraffe_image_new(cpl_type type)
Creates an empty image container.
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
void gi_warning(const cxchar *format,...)
Log a warning.
cxint giraffe_table_set(GiTable *self, cpl_table *table)
Sets the table data.
GiTable * giraffe_table_new(void)
Creates a new, empty Giraffe table.
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
cxint giraffe_table_set_properties(GiTable *self, cpl_propertylist *properties)
Attaches a property list to an table.
cxdouble giraffe_propertylist_get_conad(const cpl_propertylist *properties)
Retrieve the ADU to electrons conversion factor from the given properties.