28 #include <cxstrutils.h>
30 #include <cpl_propertylist.h>
32 #include <cpl_table.h>
38 #include "gimessages.h"
39 #include "giastroutils.h"
41 #include "gifxcalibration.h"
67 inline static cxdouble
68 _giraffe_compute_separation(cxdouble ra_0, cxdouble dec_0,
69 cxdouble ra_1, cxdouble dec_1)
72 const cxdouble deg2rad = CX_PI / 180.;
82 dist = sin(dec_0) * sin(dec_1) +
83 cos(dec_0) * cos(dec_1) * cos(ra_0 - ra_1);
85 dist = CX_CLAMP(dist, -1., 1.);
86 dist = acos(dist) / deg2rad * 3600.;
111 inline static cxdouble
112 _giraffe_spline_hermite(cxdouble xp,
const cxdouble* x,
const cxdouble* y,
113 cxint n, cxint* istart)
129 if ((x[0] <= x[n - 1]) && ((xp < x[0]) || (xp > x[n - 1]))) {
133 if ((x[0] > x[n - 1]) && ((xp > x[0]) || (xp < x[n - 1]))) {
137 if (x[0] <= x[n - 1]) {
139 for (i = *istart + 1; i <= n && xp >= x[i - 1]; ++i) {
146 for (i = *istart + 1; i <= n && xp <= x[i - 1]; ++i) {
156 lp1 = 1. / (x[i - 1] - x[i]);
160 yp1 = (y[1] - y[0]) / (x[1] - x[0]);
163 yp1 = (y[i] - y[i - 2]) / (x[i] - x[i - 2]);
167 yp2 = (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
170 yp2 = (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]);
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;
209 inline static cxdouble
210 _giraffe_interpolate_spline_hermite(
const cpl_table* tbl,
211 const cxchar* xlabel,
212 const cxchar* ylabel,
213 cxdouble xp, cxint* istart)
216 cxint n = cpl_table_get_nrow(tbl);
218 const cxdouble* x = cpl_table_get_data_double_const(tbl, xlabel);
219 const cxdouble* y = cpl_table_get_data_double_const(tbl, ylabel);
222 return _giraffe_spline_hermite(xp, x, y, n ,istart);
250 inline static cpl_table*
251 _giraffe_create_flux_table(
const cpl_table* catalog, cxint row)
254 const cxchar*
const _id =
"_giraffe_create_flux_table";
257 const cxchar* columns[] = {
"LAMBDA",
"BIN_WIDTH",
"F_LAMBDA"};
258 const cxchar* units = NULL;
260 cxint ndata = cpl_table_get_int(catalog,
"NDATA", row, NULL);
264 const cxdouble ang2nm = 0.1;
266 cpl_table* flux = NULL;
274 giraffe_error_push();
276 flux = cpl_table_new(ndata);
278 for (i = 0; i < CX_N_ELEMENTS(columns); ++i) {
280 const cpl_array* data = cpl_table_get_array(catalog, columns[i], row);
282 cpl_type type = cpl_table_get_column_type(catalog, columns[i]);
285 cpl_table_new_column(flux, columns[i], CPL_TYPE_DOUBLE);
287 if ((data != NULL) && (ndata <= cpl_array_get_size(data))) {
289 switch (type & ~CPL_TYPE_POINTER) {
295 register cxdouble value = 0.;
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);
307 case CPL_TYPE_DOUBLE:
312 register cxdouble value = 0.;
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);
326 cpl_error_set(_id, CPL_ERROR_INVALID_TYPE);
342 cpl_table_multiply_scalar(flux, columns[2], 1.e-16);
352 units = cpl_table_get_column_unit(catalog, columns[0]);
354 if ((units != NULL) && (cx_strncasecmp(units,
"ang", 3) == 0)) {
356 cpl_msg_debug(_id,
"Found units '%s'. Converting flux standard "
357 "from Angstrom to nano meters", units);
359 cpl_table_multiply_scalar(flux, columns[0], ang2nm);
360 cpl_table_set_column_unit(flux, columns[0],
"nm");
362 cpl_table_multiply_scalar(flux, columns[1], ang2nm);
363 cpl_table_set_column_unit(flux, columns[1],
"nm");
365 cpl_table_divide_scalar(flux, columns[2], ang2nm);
366 cpl_table_set_column_unit(flux, columns[2],
"erg/s/cm^2/nm");
373 cpl_msg_debug(_id,
"No units for wavelength column. Assuming "
379 cpl_msg_debug(_id,
"Found unknown units ('%s') for wavelength "
380 "column. Assuming nano meters.", units);
387 if (cpl_error_get_code() != CPL_ERROR_NONE) {
388 cpl_table_delete(flux);
416 inline static cpl_table*
417 _giraffe_setup_extinction(
const cpl_table* extinction)
420 const cxchar*
const _id =
"_giraffe_setup_extinction";
422 const cxchar* site = NULL;
423 const cxchar* units = NULL;
424 const cxchar* sites[] = {
"PARANAL",
"LA_SILLA", NULL};
429 const cxdouble ang2nm = 0.1;
433 cpl_table* _extinction = NULL;
436 if (cpl_table_has_column(extinction,
"LAMBDA") == FALSE) {
438 cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
448 units = cpl_table_get_column_unit(extinction,
"LAMBDA");
450 if ((units != NULL) && (cx_strncasecmp(units,
"ang", 3) == 0)) {
454 cpl_msg_debug(_id,
"Found units '%s'. Converting wavelength"
455 "from Angstrom to nano meters", units);
462 cpl_msg_debug(_id,
"No units for wavelength column. "
463 "Assuming nano meters.");
468 if (cx_strncasecmp(units,
"nm", 2) == 0) {
470 cpl_msg_debug(_id,
"Found units nano meters ('%s') for "
471 "wavelength column.", units);
476 cpl_msg_debug(_id,
"Found unknown units ('%s') for "
477 "wavelength column. Assuming nano meters.", units);
490 while ((site == NULL) && (sites[i] != NULL)) {
492 if (cpl_table_has_column(extinction, sites[i]) == TRUE) {
504 cpl_msg_debug(_id,
"No matching observatory site found!");
513 rows = cpl_table_get_nrow(extinction);
515 giraffe_error_push();
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");
521 cpl_table_new_column(_extinction,
"EXTINCTION", CPL_TYPE_DOUBLE);
522 cpl_table_set_column_unit(_extinction,
"EXTINCTION",
"mag/airmass");
524 if (cpl_error_get_code() != CPL_ERROR_NONE) {
526 cpl_table_delete(_extinction);
536 switch (cpl_table_get_column_type(extinction,
"LAMBDA")) {
539 for (i = 0; i < rows; ++i) {
541 register cxdouble lambda = cpl_table_get_float(extinction,
544 cpl_table_set_double(_extinction,
"LAMBDA", i,
551 case CPL_TYPE_DOUBLE:
553 for (i = 0; i < rows; ++i) {
555 register cxdouble lambda = cpl_table_get_double(extinction,
558 cpl_table_set_double(_extinction,
"LAMBDA", i,
567 cpl_table_delete(_extinction);
570 cpl_msg_debug(_id,
"Column type (%d) is not supported for "
571 "extinction tables!",
572 cpl_table_get_column_type(extinction,
"LAMBDA"));
579 switch (cpl_table_get_column_type(extinction, site)) {
582 for (i = 0; i < rows; ++i) {
584 register cxdouble aext = cpl_table_get_float(extinction,
587 cpl_table_set_double(_extinction,
"EXTINCTION", i, aext);
593 case CPL_TYPE_DOUBLE:
595 for (i = 0; i < rows; ++i) {
597 register cxdouble aext = cpl_table_get_double(extinction,
600 cpl_table_set_double(_extinction,
"EXTINCTION", i, aext);
608 cpl_table_delete(_extinction);
611 cpl_msg_debug(_id,
"Column type (%d) is not supported for "
612 "extinction tables!",
613 cpl_table_get_column_type(extinction, site));
626 inline static cpl_image*
627 _giraffe_compute_mean_sky(
const cpl_image* spectra,
const cpl_table* fibers)
631 cxint ns = cpl_image_get_size_x(spectra);
632 cxint nw = cpl_image_get_size_y(spectra);
634 cxint* sky_fibers = cx_calloc(ns,
sizeof(cxint));
636 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
638 cxdouble* _sky = NULL;
640 cpl_image* sky = NULL;
642 cx_assert(ns == cpl_table_get_nrow(fibers));
649 for (i = 0; i < ns; ++i) {
651 const cxchar* s = cpl_table_get_string(fibers,
"Retractor", i);
653 if (strstr(s,
"-Sky") != NULL) {
655 cpl_table_get_int(fibers,
"INDEX", i, NULL) - 1;
678 sky = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
679 _sky = cpl_image_get_data_double(sky);
683 if (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE) {
685 cxdouble* sky_raw = cx_calloc(nsky,
sizeof(cxdouble));
688 for (i = 0; i < nw; ++i) {
690 register cxint j = 0;
693 for (j = 0; j < nsky; ++j) {
695 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
696 sky_fibers[j], NULL);
701 sky_raw[j] = _spectra[i * ns + sky_fibers[j]] / t;
705 _sky[i] = giraffe_array_median(sky_raw, nsky);
715 cxdouble* sky_raw = cx_calloc(nsky,
sizeof(cxdouble));
718 for (i = 0; i < nw; ++i) {
720 register cxint j = 0;
723 for (j = 0; j < nsky; ++j) {
724 sky_raw[j] = _spectra[i * ns + sky_fibers[j]];
727 _sky[i] = giraffe_array_median(sky_raw, nsky);
739 if (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE) {
741 for (i = 0; i < nsky; ++i) {
743 register cxint j = 0;
745 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
746 sky_fibers[i], NULL);
751 for (j = 0; j < nw; ++j) {
752 _sky[j] += _spectra[j * ns + sky_fibers[i]] / t;
760 for (i = 0; i < nsky; ++i) {
762 register cxint j = 0;
765 for (j = 0; j < nw; ++j) {
766 _sky[j] += _spectra[j * ns + sky_fibers[i]];
773 cpl_image_divide_scalar(sky, nsky);
785 inline static cpl_image*
786 _giraffe_subtract_mean_sky(
const cpl_image* spectra,
const cpl_image* sky,
787 const cpl_table* fibers)
791 cxint ns = cpl_image_get_size_x(spectra);
792 cxint nw = cpl_image_get_size_y(spectra);
794 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
795 const cxdouble* _sky = cpl_image_get_data_double_const(sky);
797 cpl_image* result = cpl_image_new(ns, nw, CPL_TYPE_DOUBLE);
799 cxdouble* _result = cpl_image_get_data_double(result);
802 cx_assert((fibers == NULL) || (ns == cpl_table_get_nrow(fibers)));
803 cx_assert(nw == cpl_image_get_size_y(sky));
812 if ((fibers != NULL) &&
813 (cpl_table_has_column(fibers,
"TRANSMISSION") == TRUE)) {
815 for (i = 0; i < ns; ++i) {
817 register cxint j = 0;
819 cpl_table_get_int(fibers,
"INDEX", i, NULL) - 1;
821 cxdouble t = cpl_table_get_double(fibers,
"TRANSMISSION",
825 for (j = 0; j < nw; ++j) {
827 register cxint l = j * ns + i;
829 _result[l] += _spectra[l] - t * _sky[j];
838 for (i = 0; i < ns; ++i) {
840 register cxint j = 0;
842 for (j = 0; j < nw; ++j) {
844 register cxint k = j * ns + i;
846 _result[k] += _spectra[k] - _sky[j];
859 inline static cpl_image*
860 _giraffe_integrate_flux(
const cpl_image* spectra,
const cpl_table* fibers)
864 cxint nw = cpl_image_get_size_y(spectra);
865 cxint ns = cpl_image_get_size_x(spectra);
867 cpl_image* result = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
870 cx_assert(ns == cpl_table_get_nrow(fibers));
872 for (i = 0; i < ns; ++i) {
874 const cxchar* s = cpl_table_get_string(fibers,
"Retractor", i);
876 cxint rp = cpl_table_get_int(fibers,
"RP", i, NULL);
884 if ((rp != -1) && (strstr(s,
"-Sky") == NULL)) {
886 register cxint j = 0;
888 const cxdouble* _spectra =
889 cpl_image_get_data_double_const(spectra);
891 cxdouble* _result = cpl_image_get_data_double(result);
894 for (j = 0; j < nw; ++j) {
895 _result[j] += _spectra[j * ns + i];
928 _giraffe_correct_extinction(cpl_image* spectrum, cpl_propertylist* properties,
929 const cpl_table* extinction)
932 const cxchar*
const _id =
"_giraffe_correct_extinction";
942 cxdouble latitude = 0.;
943 cxdouble exptime = 0.;
944 cxdouble wlstart = 0.;
945 cxdouble wlstep = 0.;
946 cxdouble airmass = -1.;
947 cxdouble* flx = NULL;
949 cpl_table* _extinction = NULL;
952 if ((spectrum == NULL) || (properties == NULL) || (extinction == NULL)) {
957 if (cpl_image_get_size_x(spectrum) != 1) {
959 cpl_msg_debug(_id,
"Input spectrum is not a 1d spectrum!");
971 _extinction = _giraffe_setup_extinction(extinction);
973 if (_extinction == NULL) {
982 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
983 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
985 cpl_msg_debug(_id,
"Observed spectrum does not have a valid "
988 cpl_table_delete(_extinction);
995 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
996 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1003 giraffe_error_push();
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);
1011 status = cpl_error_get_code();
1013 if (status == CPL_ERROR_NONE) {
1020 if ((airmass < 1.) || (status != CPL_ERROR_NONE)) {
1022 cxbool start = cpl_propertylist_has(properties,
1023 GIALIAS_AIRMASS_START);
1024 cxbool end = cpl_propertylist_has(properties,
1025 GIALIAS_AIRMASS_END);
1027 if ((start == FALSE) || (end == FALSE)) {
1029 cpl_msg_debug(_id,
"Unable to compute airmass of the "
1032 cpl_table_delete(_extinction);
1040 airmass = 0.5 * (cpl_propertylist_get_double(properties,
1041 GIALIAS_AIRMASS_START) +
1042 cpl_propertylist_get_double(properties,
1043 GIALIAS_AIRMASS_END));
1049 giraffe_error_pop();
1056 nw = cpl_image_get_size_y(spectrum);
1057 flx = cpl_image_get_data_double(spectrum);
1059 for (i = 0; i < nw; ++i) {
1063 cxdouble wlen = wlstart + (i - 1) * wlstep;
1067 giraffe_error_push();
1069 ext = _giraffe_interpolate_spline_hermite(_extinction,
1070 "LAMBDA",
"EXTINCTION", wlen, &first);
1072 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1074 cpl_table_delete(_extinction);
1081 giraffe_error_pop();
1096 flx[i] *= pow(10., 0.4 * ext * airmass);
1100 cpl_table_delete(_extinction);
1126 inline static cpl_image*
1127 _giraffe_compute_response(
const cpl_image* spectrum,
1128 const cpl_propertylist* properties,
1129 const cpl_table* refflux)
1132 const cxchar*
const _id =
"giraffe_compute_response";
1138 const cxdouble* flx = NULL;
1140 cxdouble wlstart = 0.;
1141 cxdouble wlstep = 0.;
1142 cxdouble* rdata = NULL;
1145 cpl_image* response = NULL;
1149 if ((spectrum == NULL) || (properties == NULL) || (refflux == NULL)) {
1153 if (cpl_image_get_size_x(spectrum) != 1) {
1155 cpl_msg_debug(_id,
"Observed spectrum is not a 1d spectrum!");
1165 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
1166 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
1168 cpl_msg_debug(_id,
"Observed spectrum does not have a valid "
1169 "wavelength grid!");
1174 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
1175 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
1177 nw = cpl_image_get_size_y(spectrum);
1185 flx = cpl_image_get_data_double_const(spectrum);
1187 response = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
1188 rdata = cpl_image_get_data_double(response);
1190 for (i = 0; i < nw; ++i) {
1194 cxdouble wlen = wlstart + (i - 1) * wlstep;
1198 giraffe_error_push();
1200 sflx = _giraffe_interpolate_spline_hermite(refflux,
1201 "LAMBDA",
"F_LAMBDA", wlen, &first);
1203 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1205 cpl_image_delete(response);
1212 giraffe_error_pop();
1214 rdata[i] = flx[i] / sflx;
1224 giraffe_select_flux_standard(
const GiTable* catalog,
const GiImage* spectra,
1228 const cxchar*
const _id =
"giraffe_select_flux_standard";
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.;
1239 cxdouble std_ra = 0.;
1240 cxdouble std_dec = 0.;
1241 cxdouble min_dist = 0.;
1245 const cpl_table* _catalog = NULL;
1247 cpl_table* _flux = NULL;
1249 const cpl_propertylist* properties = NULL;
1251 GiTable* flux = NULL;
1254 if ((catalog == NULL) || (spectra == NULL)) {
1259 cx_assert(_catalog != NULL);
1268 cx_assert(properties != NULL);
1270 giraffe_error_push();
1272 if (cpl_propertylist_has(properties, GIALIAS_TARG_ALPHA) ==1) {
1273 targ_alpha = cpl_propertylist_get_double(properties, GIALIAS_TARG_ALPHA);
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);
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;
1283 ra = abs(ra - 360.0);
1287 ra = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
1291 if (cpl_propertylist_has(properties, GIALIAS_TARG_DELTA) ==1 ){
1292 targ_delta = cpl_propertylist_get_double(properties, GIALIAS_TARG_DELTA);
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);
1298 dec = dec_dd + (dec_mm/60.0) + (dec_ss/3600.0);
1301 dec = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
1305 if (cpl_error_get_code() != CPL_ERROR_NONE) {
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);
1314 giraffe_error_pop();
1321 cpl_msg_info(_id,
"Searching flux standard by coordinates...");
1323 if ((cpl_table_has_column(_catalog,
"RA_DEG") == FALSE) ||
1324 (cpl_table_has_column(_catalog,
"DEC_DEG") == FALSE)) {
1326 cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
1333 for (
int i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1335 cxdouble cat_ra = cpl_table_get_double(_catalog,
"RA_DEG",
1337 cxdouble cat_dec = cpl_table_get_double(_catalog,
"DEC_DEG",
1348 dist = _giraffe_compute_separation(ra, dec, cat_ra, cat_dec);
1350 if ((i == 0) || (dist < min_dist)) {
1356 if (dist < max_dist) {
1365 cpl_msg_info(_id,
"%d flux standards found...", nmatch);
1372 cpl_msg_info(_id,
"Searching flux standard by name...");
1374 if ((cpl_propertylist_has(properties, GIALIAS_TARGET) == TRUE) &&
1375 (cpl_table_has_column(_catalog,
"OBJECT") == TRUE)) {
1377 const cxchar* target = cpl_propertylist_get_string(properties,
1381 if ((target != NULL) && (target[0] !=
'\0')) {
1383 register cxint i = 0;
1386 for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
1388 const cxchar*
object = cpl_table_get_string(_catalog,
1392 if (strcmp(target,
object) == 0) {
1394 cxdouble cat_ra = cpl_table_get_double(_catalog,
1396 cxdouble cat_dec = cpl_table_get_double(_catalog,
1397 "DEC_DEG", i, NULL);
1400 std_ra = cpl_table_get_double(_catalog,
1402 std_dec = cpl_table_get_double(_catalog,
1403 "DEC_DEG", i, NULL);
1405 min_dist = _giraffe_compute_separation(ra, dec,
1419 cpl_msg_info(_id,
"%d flux standards found...", nmatch);
1429 const cxchar*
object = cpl_table_get_string(_catalog,
1432 cpl_msg_info(_id,
"No flux standard found within %.4f arcsec",
1434 cpl_msg_info(_id,
"The closest object ('%s') at (RA, Dec) = "
1435 "(%.4f, %.4f) is %.4f arcsec away",
object, std_ra,
1438 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1448 const cxchar*
object = cpl_table_get_string(_catalog,
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);
1459 _flux = _giraffe_create_flux_table(_catalog, row);
1468 const cxchar*
object = cpl_table_get_string(_catalog,
1471 cpl_msg_info(_id,
"%d flux standards found within %.4f arcsec",
1473 cpl_msg_info(_id,
"The closest object ('%s') at (RA, Dec) = "
1474 "(%.4f, %.4f) is %.4f arcsec away",
object, std_ra,
1477 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
1486 if (_flux != NULL) {
1493 cpl_table_delete(_flux);
1544 const GiTable* fibers,
const GiImage* flat,
1545 const GiTable* flux,
const GiTable* extinction,
1546 const GiFxCalibrationConfig* config)
1549 const cxchar*
const _id =
"giraffe_calibrate_flux";
1552 const cxint xrad = 0;
1553 const cxint yrad = 7;
1555 const cxdouble UT_M1_VIGNETTED_AREA = 517533.407382;
1556 const cxdouble H_PLANCK = 6.62606896e-27;
1557 const cxdouble C_LIGHT = 2.99792458e17;
1563 const cxdouble* rdata = NULL;
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);
1572 cpl_propertylist* properties = NULL;
1574 cpl_mask* filter = NULL;
1576 cpl_image* _spectra = NULL;
1577 cpl_image* fluxobs = NULL;
1578 cpl_image* response = NULL;
1579 cpl_image* fresponse = NULL;
1581 cpl_table* _extinction = NULL;
1582 cpl_table* _flux = NULL;
1583 cpl_table* efficiency = NULL;
1587 if (result == NULL) {
1591 if ((spectra == NULL) || (spectra->spectra == NULL)) {
1595 if (fibers == NULL) {
1599 if ((flux == NULL) || (extinction == NULL)) {
1603 if (config == NULL) {
1608 if ((result->response != NULL) || (result->efficiency != NULL)) {
1610 gi_warning(
"%s: Results structure at %p is not empty! Contents "
1611 "might be lost.", _id, result);
1616 cx_assert(properties != NULL);
1619 cx_assert(_spectra != NULL);
1622 cx_assert(_extinction != NULL);
1625 cx_assert(_flux != NULL);
1636 if (config->sky_subtraction == TRUE) {
1638 cpl_image* sky_spectrum = NULL;
1639 cpl_image* sspectra = NULL;
1644 sky_spectrum = _giraffe_compute_mean_sky(_spectra, _fibers);
1646 if (sky_spectrum == NULL) {
1650 giraffe_error_push();
1652 avgsky = cpl_image_get_mean(sky_spectrum);
1654 sspectra = _giraffe_subtract_mean_sky(_spectra, sky_spectrum,
1657 fluxobs = _giraffe_integrate_flux(sspectra, _fibers);
1659 cpl_image_delete(sky_spectrum);
1660 sky_spectrum = NULL;
1662 cpl_image_delete(sspectra);
1665 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1669 giraffe_error_pop();
1676 fluxobs = _giraffe_integrate_flux(_spectra, _fibers);
1685 status = _giraffe_correct_extinction(fluxobs, properties, _extinction);
1688 cpl_msg_warning(_id,
"Extinction correction failed!");
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);
1703 cpl_image_multiply_scalar(fluxobs, conad / exptime / wlstep);
1711 response = _giraffe_compute_response(fluxobs, properties, _flux);
1713 cpl_image_delete(fluxobs);
1716 if (response == NULL) {
1720 filter = cpl_mask_new(2 * xrad + 1, 2 * yrad + 1);
1721 for (i = 0; i < cpl_mask_get_size_x(filter); ++i) {
1725 for (j = 0; j < cpl_mask_get_size_y(filter); ++j)
1727 cpl_mask_set(filter, i + 1, j + 1, CPL_BINARY_1);
1732 fresponse = cpl_image_new(cpl_image_get_size_x(response),
1733 cpl_image_get_size_y(response),
1734 cpl_image_get_type(response));
1736 cpl_image_filter_mask(fresponse, response, filter,
1737 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
1739 cpl_mask_delete(filter);
1742 cpl_image_delete(response);
1743 response = fresponse;
1745 if (response == NULL) {
1754 giraffe_error_push();
1756 nw = cpl_image_get_size_y(response);
1758 efficiency = cpl_table_new(nw);
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);
1764 cpl_table_set_column_unit(efficiency,
"WLEN",
"nm");
1765 cpl_table_set_column_unit(efficiency,
"BINWIDTH",
"nm");
1767 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1769 cpl_table_delete(efficiency);
1772 cpl_image_delete(response);
1779 giraffe_error_pop();
1782 rdata = cpl_image_get_data_double_const(response);
1784 for (i = 0; i < nw; ++i) {
1786 cxdouble wl = wlstart + i * wlstep;
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));
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].");
1813 cpl_image_delete(response);
1820 cpl_table_delete(efficiency);
1828 GiFxCalibrationConfig*
1829 giraffe_fxcalibration_config_create(cpl_parameterlist* parameters)
1832 cpl_parameter *p = NULL;
1834 GiFxCalibrationConfig*
self = NULL;
1837 if (parameters == NULL) {
1841 self = cx_calloc(1,
sizeof *
self);
1842 cx_assert(
self != NULL);
1849 self->sky_subtraction = FALSE;
1850 self->max_dist = 3.0;
1857 p = cpl_parameterlist_find(parameters,
1858 "giraffe.fxcalibration.sky.correct");
1861 self->sky_subtraction = cpl_parameter_get_bool(p);
1864 p = cpl_parameterlist_find(parameters,
1865 "giraffe.fxcalibration.max.dist");
1868 self->max_dist = cpl_parameter_get_double(p);
1919 cpl_parameter* p = NULL;
1922 if (parameters == NULL) {
1926 p = cpl_parameter_new_value(
"giraffe.fxcalibration.sky.correct",
1928 "Correct spectra for the sky emission",
1929 "giraffe.fxcalibration",
1931 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"flx-skycorr");
1932 cpl_parameterlist_append(parameters, p);
1934 p = cpl_parameter_new_value(
"giraffe.fxcalibration.max.dist",
1936 "Standar star search radius ",
1937 "giraffe.fxcalibration",
1939 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"max-dist");
1940 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_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
GiImage * giraffe_image_new(cpl_type type)
Creates an empty image container.
cxint giraffe_image_set(GiImage *self, cpl_image *image)
Sets the image data.
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.