00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifdef HAVE_CONFIG_H
00029 # include <config.h>
00030 #endif
00031
00032
00033 #include <math.h>
00034
00035 #include <cxmacros.h>
00036 #include <cxstrutils.h>
00037
00038 #include <cpl_propertylist.h>
00039 #include <cpl_table.h>
00040 #include <cpl_msg.h>
00041
00042 #include "gialias.h"
00043 #include "gierror.h"
00044 #include "gimessages.h"
00045 #include "giastroutils.h"
00046 #include "gifxcalibration.h"
00047
00048
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072 inline static cxdouble
00073 _giraffe_compute_separation(cxdouble ra_0, cxdouble dec_0,
00074 cxdouble ra_1, cxdouble dec_1)
00075 {
00076
00077 const cxdouble deg2rad = CX_PI / 360.;
00078
00079 cxdouble dist = 0.;
00080
00081
00082 ra_0 *= deg2rad;
00083 ra_1 *= deg2rad;
00084 dec_0 *= deg2rad;
00085 dec_1 *= deg2rad;
00086
00087 dist = sin(dec_0) * sin(dec_1) +
00088 cos(dec_0) * cos(dec_1) * cos(ra_0 - ra_1);
00089
00090 dist = CX_CLAMP(dist, -1., 1.);
00091 dist = acos(dist) / deg2rad * 3600.;
00092
00093 return dist;
00094
00095 }
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116 inline static cxdouble
00117 _giraffe_spline_hermite(cxdouble xp, const cxdouble* x, const cxdouble* y,
00118 cxint n, cxint* istart)
00119 {
00120
00121 cxint i = 0;
00122
00123 cxdouble yp = 0.;
00124 cxdouble yp1 = 0.;
00125 cxdouble yp2 = 0.;
00126 cxdouble xpi = 0.;
00127 cxdouble xpi1 = 0.;
00128 cxdouble l1 = 0.;
00129 cxdouble l2 = 0.;
00130 cxdouble lp1 = 0.;
00131 cxdouble lp2 = 0.;
00132
00133
00134 if ((x[0] <= x[n - 1]) && ((xp < x[0]) || (xp > x[n - 1]))) {
00135 return 0.;
00136 }
00137
00138 if ((x[0] > x[n - 1]) && ((xp > x[0]) || (xp < x[n - 1]))) {
00139 return 0.;
00140 }
00141
00142 if (x[0] <= x[n - 1]) {
00143
00144 for (i = *istart + 1; i <= n && xp >= x[i - 1]; ++i) {
00145 ;
00146 }
00147
00148 }
00149 else {
00150
00151 for (i = *istart + 1; i <= n && xp <= x[i - 1]; ++i) {
00152 ;
00153 }
00154
00155 }
00156
00157 *istart = i;
00158 --i;
00159
00160
00161 lp1 = 1. / (x[i - 1] - x[i]);
00162 lp2 = -lp1;
00163
00164 if (i == 1) {
00165 yp1 = (y[1] - y[0]) / (x[1] - x[0]);
00166 }
00167 else {
00168 yp1 = (y[i] - y[i - 2]) / (x[i] - x[i - 2]);
00169 }
00170
00171 if (i >= n - 1) {
00172 yp2 = (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
00173 }
00174 else {
00175 yp2 = (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]);
00176 }
00177
00178
00179 xpi = xp - x[i - 1];
00180 xpi1 = xp - x[i];
00181
00182 l1 = xpi1 * lp1;
00183 l2 = xpi * lp2;
00184
00185 yp = y[i - 1] * (1. - 2. * lp1 * xpi) * l1 * l1 +
00186 y[i] * (1. - 2. * lp2 * xpi1) * l2 * l2 +
00187 yp1 * xpi * l1 * l1 +
00188 yp2 * xpi1 * l2 * l2;
00189
00190 return yp;
00191
00192 }
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214 inline static cxdouble
00215 _giraffe_interpolate_spline_hermite(const cpl_table* tbl,
00216 const cxchar* xlabel,
00217 const cxchar* ylabel,
00218 cxdouble xp, cxint* istart)
00219 {
00220
00221 cxint n = cpl_table_get_nrow(tbl);
00222
00223 const cxdouble* x = cpl_table_get_data_double_const(tbl, xlabel);
00224 const cxdouble* y = cpl_table_get_data_double_const(tbl, ylabel);
00225
00226
00227 return _giraffe_spline_hermite(xp, x, y, n ,istart);
00228
00229 }
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 inline static cpl_table*
00256 _giraffe_create_flux_table(const cpl_table* catalog, cxint row)
00257 {
00258
00259 const cxchar* const _id = "_giraffe_create_flux_table";
00260
00261
00262 const cxchar* columns[] = {"LAMBDA", "BIN_WIDTH", "F_LAMBDA"};
00263 const cxchar* units = NULL;
00264
00265 cxint ndata = cpl_table_get_int(catalog, "NDATA", row, NULL);
00266
00267 cxsize i = 0;
00268
00269 const cxdouble ang2nm = 0.1;
00270
00271 cpl_table* flux = NULL;
00272
00273
00274
00275 if (ndata <= 0) {
00276 return NULL;
00277 }
00278
00279 giraffe_error_push();
00280
00281 flux = cpl_table_new(ndata);
00282
00283 for (i = 0; i < CX_N_ELEMENTS(columns); ++i) {
00284
00285 const cpl_array* data = cpl_table_get_array(catalog, columns[i], row);
00286
00287 cpl_type type = cpl_table_get_column_type(catalog, columns[i]);
00288
00289
00290 cpl_table_new_column(flux, columns[i], CPL_TYPE_DOUBLE);
00291
00292 if ((data != NULL) && (ndata <= cpl_array_get_size(data))) {
00293
00294 switch (type & ~CPL_TYPE_POINTER) {
00295 case CPL_TYPE_FLOAT:
00296 {
00297
00298 cxint j = 0;
00299
00300 register cxdouble value = 0.;
00301
00302
00303 for (j = 0; j < ndata; ++j) {
00304 value = cpl_array_get_float(data, j, NULL);
00305 cpl_table_set_double(flux, columns[i], j, value);
00306 }
00307
00308 break;
00309
00310 }
00311
00312 case CPL_TYPE_DOUBLE:
00313 {
00314
00315 cxint j = 0;
00316
00317 register cxdouble value = 0.;
00318
00319
00320 for (j = 0; j < ndata; ++j) {
00321 value = cpl_array_get_double(data, j, NULL);
00322 cpl_table_set_double(flux, columns[i], j, value);
00323 }
00324
00325 break;
00326
00327 }
00328
00329 default:
00330 {
00331 cpl_error_set(_id, CPL_ERROR_INVALID_TYPE);
00332 break;
00333
00334 }
00335 }
00336
00337 }
00338
00339 }
00340
00341
00342
00343
00344
00345
00346
00347 cpl_table_multiply_scalar(flux, columns[2], 1.e-16);
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357 units = cpl_table_get_column_unit(catalog, columns[0]);
00358
00359 if ((units != NULL) && (cx_strncasecmp(units, "ang", 3) == 0)) {
00360
00361 cpl_msg_debug(_id, "Found units '%s'. Converting flux standard "
00362 "from Angstrom to nano meters", units);
00363
00364 cpl_table_multiply_scalar(flux, columns[0], ang2nm);
00365 cpl_table_set_column_unit(flux, columns[0], "nm");
00366
00367 cpl_table_multiply_scalar(flux, columns[1], ang2nm);
00368 cpl_table_set_column_unit(flux, columns[1], "nm");
00369
00370 cpl_table_divide_scalar(flux, columns[2], ang2nm);
00371 cpl_table_set_column_unit(flux, columns[2], "erg/s/cm^2/nm");
00372
00373 }
00374 else {
00375
00376 if (units == NULL) {
00377
00378 cpl_msg_debug(_id, "No units for wavelength column. Assuming "
00379 "nano meters.");
00380
00381 }
00382 else {
00383
00384 cpl_msg_debug(_id, "Found unknown units ('%s') for wavelength "
00385 "column. Assuming nano meters.", units);
00386
00387 }
00388
00389 }
00390
00391
00392 if (cpl_error_get_code() != CPL_ERROR_NONE) {
00393 cpl_table_delete(flux);
00394 flux = NULL;
00395
00396 return NULL;
00397 }
00398
00399 giraffe_error_pop();
00400
00401 return flux;
00402
00403 }
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421 inline static cpl_table*
00422 _giraffe_setup_extinction(const cpl_table* extinction)
00423 {
00424
00425 const cxchar* const _id = "_giraffe_setup_extinction";
00426
00427 const cxchar* site = NULL;
00428 const cxchar* units = NULL;
00429 const cxchar* sites[] = {"PARANAL", "LA_SILLA", NULL};
00430
00431 cxint i = 0;
00432 cxint rows = 0;
00433
00434 const cxdouble ang2nm = 0.1;
00435
00436 cxdouble scale = 1.;
00437
00438 cpl_table* _extinction = NULL;
00439
00440
00441 if (cpl_table_has_column(extinction, "LAMBDA") == FALSE) {
00442
00443 cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
00444 return NULL;
00445
00446 }
00447
00448
00449
00450
00451
00452
00453 units = cpl_table_get_column_unit(extinction, "LAMBDA");
00454
00455 if ((units != NULL) && (cx_strncasecmp(units, "ang", 3) == 0)) {
00456
00457 scale = ang2nm;
00458
00459 cpl_msg_debug(_id, "Found units '%s'. Converting wavelength"
00460 "from Angstrom to nano meters", units);
00461
00462 }
00463 else {
00464
00465 if (units == NULL) {
00466
00467 cpl_msg_debug(_id, "No units for wavelength column. "
00468 "Assuming nano meters.");
00469
00470 }
00471 else {
00472
00473 if (cx_strncasecmp(units, "nm", 2) == 0) {
00474
00475 cpl_msg_debug(_id, "Found units nano meters ('%s') for "
00476 "wavelength column.", units);
00477
00478 }
00479 else {
00480
00481 cpl_msg_debug(_id, "Found unknown units ('%s') for "
00482 "wavelength column. Assuming nano meters.", units);
00483
00484 }
00485
00486 }
00487
00488 }
00489
00490
00491
00492
00493
00494
00495 while ((site == NULL) && (sites[i] != NULL)) {
00496
00497 if (cpl_table_has_column(extinction, sites[i]) == TRUE) {
00498
00499 site = sites[i];
00500 break;
00501
00502 }
00503
00504 ++i;
00505
00506 }
00507
00508 if (site == NULL) {
00509 cpl_msg_debug(_id, "No matching observatory site found!");
00510 return NULL;
00511 }
00512
00513
00514
00515
00516
00517
00518 rows = cpl_table_get_nrow(extinction);
00519
00520 giraffe_error_push();
00521
00522 _extinction = cpl_table_new(rows);
00523 cpl_table_new_column(_extinction, "LAMBDA", CPL_TYPE_DOUBLE);
00524 cpl_table_set_column_unit(_extinction, "LAMBDA", "nm");
00525
00526 cpl_table_new_column(_extinction, "EXTINCTION", CPL_TYPE_DOUBLE);
00527 cpl_table_set_column_unit(_extinction, "EXTINCTION", "mag/airmass");
00528
00529 if (cpl_error_get_code() != CPL_ERROR_NONE) {
00530
00531 cpl_table_delete(_extinction);
00532 _extinction = NULL;
00533
00534 return NULL;
00535
00536 }
00537
00538 giraffe_error_pop();
00539
00540
00541 switch (cpl_table_get_column_type(extinction, "LAMBDA")) {
00542 case CPL_TYPE_FLOAT:
00543 {
00544 for (i = 0; i < rows; ++i) {
00545
00546 register cxdouble lambda = cpl_table_get_float(extinction,
00547 "LAMBDA", i, NULL);
00548
00549 cpl_table_set_double(_extinction, "LAMBDA", i,
00550 scale * lambda);
00551
00552 }
00553 break;
00554 }
00555
00556 case CPL_TYPE_DOUBLE:
00557 {
00558 for (i = 0; i < rows; ++i) {
00559
00560 register cxdouble lambda = cpl_table_get_double(extinction,
00561 "LAMBDA", i, NULL);
00562
00563 cpl_table_set_double(_extinction, "LAMBDA", i,
00564 scale * lambda);
00565
00566 }
00567 break;
00568 }
00569
00570 default:
00571 {
00572 cpl_table_delete(_extinction);
00573 _extinction = NULL;
00574
00575 cpl_msg_debug(_id, "Column type (%d) is not supported for "
00576 "extinction tables!",
00577 cpl_table_get_column_type(extinction, "LAMBDA"));
00578
00579 return NULL;
00580 break;
00581 }
00582 }
00583
00584 switch (cpl_table_get_column_type(extinction, site)) {
00585 case CPL_TYPE_FLOAT:
00586 {
00587 for (i = 0; i < rows; ++i) {
00588
00589 register cxdouble aext = cpl_table_get_float(extinction,
00590 site, i, NULL);
00591
00592 cpl_table_set_double(_extinction, "EXTINCTION", i, aext);
00593
00594 }
00595 break;
00596 }
00597
00598 case CPL_TYPE_DOUBLE:
00599 {
00600 for (i = 0; i < rows; ++i) {
00601
00602 register cxdouble aext = cpl_table_get_double(extinction,
00603 site, i, NULL);
00604
00605 cpl_table_set_double(_extinction, "EXTINCTION", i, aext);
00606
00607 }
00608 break;
00609 }
00610
00611 default:
00612 {
00613 cpl_table_delete(_extinction);
00614 _extinction = NULL;
00615
00616 cpl_msg_debug(_id, "Column type (%d) is not supported for "
00617 "extinction tables!",
00618 cpl_table_get_column_type(extinction, site));
00619
00620 return NULL;
00621 break;
00622 }
00623 }
00624
00625
00626 return _extinction;
00627
00628 }
00629
00630 inline static cpl_image*
00631 _giraffe_compute_mean_sky(const cpl_image* spectra, const cpl_table* fibers)
00632 {
00633
00634 cxint i = 0;
00635 cxint nsky = 0;
00636 cxint ns = cpl_image_get_size_x(spectra);
00637 cxint nw = cpl_image_get_size_y(spectra);
00638 cxint nr = cpl_table_get_nrow(fibers);
00639
00640 const cxdouble* _spectra = cpl_image_get_data_double_const(spectra);
00641
00642 cpl_image* sky = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
00643
00644 cxdouble* _sky = cpl_image_get_data_double(sky);
00645
00646
00647 if (sky == NULL) {
00648 return NULL;
00649 }
00650
00651
00652 for (i = 0; i < nr; ++i) {
00653
00654 const cxchar* s = cpl_table_get_string(fibers, "Retractor", i);
00655
00656 if (strstr(s, "-Sky") != NULL) {
00657
00658 register cxint j = cpl_table_get_int(fibers, "INDEX",
00659 i, NULL) - 1;
00660 register cxint k = 0;
00661
00662
00663 for (k = 0; k < nw; ++k) {
00664 _sky[k] += _spectra[k * ns + j];
00665 }
00666
00667 ++nsky;
00668
00669 }
00670
00671 }
00672
00673 if (nsky == 0) {
00674 cpl_image_delete(sky);
00675 sky = NULL;
00676 }
00677 else {
00678
00679 cxint status = cpl_image_divide_scalar(sky, nsky);
00680
00681 if (status != CPL_ERROR_NONE) {
00682 cpl_image_delete(sky);
00683 sky = NULL;
00684 }
00685
00686 }
00687
00688 return sky;
00689
00690 }
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713 inline static cxint
00714 _giraffe_correct_extinction(cpl_image* spectrum, cpl_propertylist* properties,
00715 const cpl_table* extinction)
00716 {
00717
00718 const cxchar* const _id = "_giraffe_correct_extinction";
00719
00720
00721 cxint i = 0;
00722 cxint status = 0;
00723 cxint nw = 0;
00724
00725 cxdouble alpha = 0.;
00726 cxdouble delta = 0.;
00727 cxdouble lst = 0.;
00728 cxdouble latitude = 0.;
00729 cxdouble exptime = 0.;
00730 cxdouble wlstart = 0.;
00731 cxdouble wlstep = 0.;
00732 cxdouble airmass = -1.;
00733 cxdouble* flx = NULL;
00734
00735 cpl_table* _extinction = NULL;
00736
00737
00738 if ((spectrum == NULL) || (properties == NULL) || (extinction == NULL)) {
00739 return -1;
00740 }
00741
00742
00743 if (cpl_image_get_size_x(spectrum) != 1) {
00744
00745 cpl_msg_debug(_id, "Input spectrum is not a 1d spectrum!");
00746 return -1;
00747
00748 }
00749
00750
00751
00752
00753
00754
00755
00756
00757 _extinction = _giraffe_setup_extinction(extinction);
00758
00759 if (_extinction == NULL) {
00760 return 1;
00761 }
00762
00763
00764
00765
00766
00767
00768 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
00769 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
00770
00771 cpl_msg_debug(_id, "Observed spectrum does not have a valid "
00772 "wavelength grid!");
00773
00774 cpl_table_delete(_extinction);
00775 _extinction = NULL;
00776
00777 return 2;
00778
00779 }
00780
00781 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
00782 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
00783
00784
00785
00786
00787
00788
00789 giraffe_error_push();
00790
00791 alpha = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
00792 delta = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
00793 lst = cpl_propertylist_get_double(properties, GIALIAS_LST);
00794 latitude = cpl_propertylist_get_double(properties, GIALIAS_TEL_LAT);
00795 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
00796
00797 status = cpl_error_get_code();
00798
00799 if (status == CPL_ERROR_NONE) {
00800
00801 airmass = giraffe_compute_airmass(alpha, delta, lst, exptime,
00802 latitude);
00803
00804 }
00805
00806 if ((airmass < 1.) || (status != CPL_ERROR_NONE)) {
00807
00808 cxbool start = cpl_propertylist_has(properties,
00809 GIALIAS_AIRMASS_START);
00810 cxbool end = cpl_propertylist_has(properties,
00811 GIALIAS_AIRMASS_END);
00812
00813 if ((start == FALSE) || (end == FALSE)) {
00814
00815 cpl_msg_debug(_id, "Unable to compute airmass of the "
00816 "observation!");
00817
00818 cpl_table_delete(_extinction);
00819 _extinction = NULL;
00820
00821 return 3;
00822
00823 }
00824 else {
00825
00826 airmass = 0.5 * (cpl_propertylist_get_double(properties,
00827 GIALIAS_AIRMASS_START) +
00828 cpl_propertylist_get_double(properties,
00829 GIALIAS_AIRMASS_END));
00830
00831 }
00832
00833 }
00834
00835 giraffe_error_pop();
00836
00837
00838
00839
00840
00841
00842 nw = cpl_image_get_size_y(spectrum);
00843 flx = cpl_image_get_data_double(spectrum);
00844
00845 for (i = 0; i < nw; ++i) {
00846
00847 cxint first = 0;
00848
00849 cxdouble wlen = wlstart + (i - 1) * wlstep;
00850 cxdouble ext = 1.;
00851
00852
00853 giraffe_error_push();
00854
00855 ext = _giraffe_interpolate_spline_hermite(_extinction,
00856 "LAMBDA", "EXTINCTION", wlen, &first);
00857
00858 if (cpl_error_get_code() != CPL_ERROR_NONE) {
00859
00860 cpl_table_delete(_extinction);
00861 _extinction = NULL;
00862
00863 return 3;
00864
00865 }
00866
00867 giraffe_error_pop();
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882 flx[i] *= pow(10., 0.4 * ext * airmass);
00883
00884 }
00885
00886 cpl_table_delete(_extinction);
00887 _extinction = NULL;
00888
00889 return 0;
00890
00891 }
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912 inline static cpl_image*
00913 _giraffe_compute_response(const cpl_image* spectrum,
00914 const cpl_propertylist* properties,
00915 const cpl_table* refflux)
00916 {
00917
00918 const cxchar* const _id = "giraffe_compute_response";
00919
00920
00921 cxint i = 0;
00922 cxint nw = 0;
00923
00924 const cxdouble* flx = NULL;
00925
00926 cxdouble wlstart = 0.;
00927 cxdouble wlstep = 0.;
00928 cxdouble* rdata = NULL;
00929
00930
00931 cpl_image* response = NULL;
00932
00933
00934
00935 if ((spectrum == NULL) || (properties == NULL) || (refflux == NULL)) {
00936 return NULL;
00937 }
00938
00939 if (cpl_image_get_size_x(spectrum) != 1) {
00940
00941 cpl_msg_debug(_id, "Observed spectrum is not a 1d spectrum!");
00942 return NULL;
00943
00944 }
00945
00946
00947
00948
00949
00950
00951 if ((cpl_propertylist_has(properties, GIALIAS_BINWLMIN) == FALSE) ||
00952 (cpl_propertylist_has(properties, GIALIAS_BINSTEP) == FALSE)) {
00953
00954 cpl_msg_debug(_id, "Observed spectrum does not have a valid "
00955 "wavelength grid!");
00956 return NULL;
00957
00958 }
00959
00960 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
00961 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
00962
00963 nw = cpl_image_get_size_y(spectrum);
00964
00965
00966
00967
00968
00969
00970
00971 flx = cpl_image_get_data_double_const(spectrum);
00972
00973 response = cpl_image_new(1, nw, CPL_TYPE_DOUBLE);
00974 rdata = cpl_image_get_data_double(response);
00975
00976 for (i = 0; i < nw; ++i) {
00977
00978 cxint first = 0;
00979
00980 cxdouble wlen = wlstart + (i - 1) * wlstep;
00981 cxdouble sflx = 0.;
00982
00983
00984 giraffe_error_push();
00985
00986 sflx = _giraffe_interpolate_spline_hermite(refflux,
00987 "LAMBDA", "F_LAMBDA", wlen, &first);
00988
00989 if (cpl_error_get_code() != CPL_ERROR_NONE) {
00990
00991 cpl_image_delete(response);
00992 response = NULL;
00993
00994 return NULL;
00995
00996 }
00997
00998 giraffe_error_pop();
00999
01000 rdata[i] = flx[i] / sflx;
01001
01002 }
01003
01004 return response;
01005
01006 }
01007
01008
01009 GiTable*
01010 giraffe_select_flux_standard(const GiTable* catalog, const GiImage* spectra,
01011 cxdouble max_dist)
01012 {
01013
01014 const cxchar* const _id = "giraffe_select_flux_standard";
01015
01016 cxint row = 0;
01017 cxint nmatch = 0;
01018
01019 cxdouble ra = 0.;
01020 cxdouble dec = 0.;
01021 cxdouble std_ra = 0.;
01022 cxdouble std_dec = 0.;
01023 cxdouble min_dist = 0.;
01024
01025 const cpl_table* _catalog = NULL;
01026
01027 cpl_table* _flux = NULL;
01028
01029 const cpl_propertylist* properties = NULL;
01030
01031 GiTable* flux = NULL;
01032
01033
01034 if ((catalog == NULL) || (spectra == NULL)) {
01035 return NULL;
01036 }
01037
01038 _catalog = giraffe_table_get(catalog);
01039 cx_assert(_catalog != NULL);
01040
01041
01042
01043
01044
01045
01046
01047 properties = giraffe_image_get_properties(spectra);
01048 cx_assert(properties != NULL);
01049
01050 giraffe_error_push();
01051
01052 ra = cpl_propertylist_get_double(properties, GIALIAS_RADEG);
01053 dec = cpl_propertylist_get_double(properties, GIALIAS_DECDEG);
01054
01055 if (cpl_error_get_code() != CPL_ERROR_NONE) {
01056 return NULL;
01057 }
01058
01059 giraffe_error_pop();
01060
01061
01062
01063
01064
01065
01066 cpl_msg_debug(_id, "Searching flux standard by name...");
01067
01068 if ((cpl_propertylist_has(properties, GIALIAS_TARGET) == TRUE) &&
01069 (cpl_table_has_column(_catalog, "OBJECT") == TRUE)) {
01070
01071 const cxchar* target = cpl_propertylist_get_string(properties,
01072 GIALIAS_TARGET);
01073
01074
01075 if ((target != NULL) && (target[0] != '\0')) {
01076
01077 register cxint i = 0;
01078
01079
01080 for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
01081
01082 const cxchar* object = cpl_table_get_string(_catalog,
01083 "OBJECT", i);
01084
01085
01086 if (strcmp(target, object) == 0) {
01087
01088 cxdouble cat_ra = cpl_table_get_double(_catalog,
01089 "RA_DEG", i, NULL);
01090 cxdouble cat_dec = cpl_table_get_double(_catalog,
01091 "DEC_DEG", i, NULL);
01092
01093
01094 std_ra = cpl_table_get_double(_catalog,
01095 "RA_DEG", i, NULL);
01096 std_dec = cpl_table_get_double(_catalog,
01097 "DEC_DEG", i, NULL);
01098
01099 min_dist = _giraffe_compute_separation(ra, dec,
01100 cat_ra, cat_dec);
01101
01102 row = i;
01103 ++nmatch;
01104
01105 }
01106
01107 }
01108
01109 }
01110
01111 }
01112
01113 cpl_msg_debug(_id, "%d flux standards found...", nmatch);
01114
01115
01116 if (nmatch == 0) {
01117
01118 cxint i = 0;
01119
01120
01121 cpl_msg_debug(_id, "Searching flux standard by coordinates...");
01122
01123 if ((cpl_table_has_column(_catalog, "RA_DEG") == FALSE) ||
01124 (cpl_table_has_column(_catalog, "DEC_DEG") == FALSE)) {
01125
01126 cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
01127
01128 return NULL;
01129
01130 }
01131
01132
01133 for (i = 0; i < cpl_table_get_nrow(_catalog); ++i) {
01134
01135 cxdouble cat_ra = cpl_table_get_double(_catalog, "RA_DEG",
01136 i, NULL);
01137 cxdouble cat_dec = cpl_table_get_double(_catalog, "DEC_DEG",
01138 i, NULL);
01139
01140 cxdouble dist = 0.;
01141
01142
01143
01144
01145
01146
01147
01148 dist = _giraffe_compute_separation(ra, dec, cat_ra, cat_dec);
01149
01150 if ((i == 0) || (dist < min_dist)) {
01151
01152 std_ra = cat_ra;
01153 std_dec = cat_dec;
01154 min_dist = dist;
01155
01156 if (dist < max_dist) {
01157 ++nmatch;
01158 row = i;
01159 }
01160
01161 }
01162
01163 }
01164
01165 cpl_msg_debug(_id, "%d flux standards found...", nmatch);
01166
01167 }
01168
01169
01170 switch (nmatch) {
01171
01172 case 0:
01173 {
01174
01175 const cxchar* object = cpl_table_get_string(_catalog,
01176 "OBJECT", row);
01177
01178 cpl_msg_debug(_id, "No flux standard found within %.4f arcsec",
01179 max_dist);
01180 cpl_msg_debug(_id, "The closest object ('%s') at (RA, Dec) = "
01181 "(%.4f, %.4f) is %.4f arcsec away", object, std_ra,
01182 std_dec, min_dist);
01183
01184 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
01185
01186 return NULL;
01187 break;
01188
01189 }
01190
01191 case 1:
01192 {
01193
01194 const cxchar* object = cpl_table_get_string(_catalog,
01195 "OBJECT", row);
01196
01197 cpl_msg_debug(_id, "Flux standard ('%s') at (RA, Dec) = "
01198 "(%.4f, %.4f) found at a distance of %.4f arcsec",
01199 object, std_ra, std_dec, min_dist);
01200
01201
01202
01203
01204
01205 _flux = _giraffe_create_flux_table(_catalog, row);
01206
01207 break;
01208
01209 }
01210
01211 default:
01212 {
01213
01214 const cxchar* object = cpl_table_get_string(_catalog,
01215 "OBJECT", row);
01216
01217 cpl_msg_debug(_id, "%d flux standards found within %.4f arcsec",
01218 nmatch, max_dist);
01219 cpl_msg_debug(_id, "The closest object ('%s') at (RA, Dec) = "
01220 "(%.4f, %.4f) is %.4f arcsec away", object, std_ra,
01221 std_dec, min_dist);
01222
01223 cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);
01224
01225 return NULL;
01226 break;
01227
01228 }
01229
01230 }
01231
01232 if (_flux != NULL) {
01233
01234 flux = giraffe_table_new();
01235 giraffe_table_set(flux, _flux);
01236
01237 }
01238
01239
01240
01241
01242 cpl_table_delete(_flux);
01243 _flux = NULL;
01244
01245 return flux;
01246
01247 }
01248
01249
01291 cxint
01292 giraffe_calibrate_flux(GiResponse* result, const GiRebinning* spectra,
01293 const GiTable* fibers, const GiImage* flat,
01294 const GiTable* flux, const GiTable* extinction,
01295 const GiFxCalibrationConfig* config)
01296 {
01297
01298 const cxchar* const _id = "giraffe_calibrate_flux";
01299
01300
01301 const cxint xrad = 7;
01302 const cxint yrad = 0;
01303
01304 const cxdouble UT_M1_VIGNETTED_AREA = 517533.407382;
01305 const cxdouble H_PLANCK = 6.62606896e-27;
01306 const cxdouble C_LIGHT = 2.99792458e17;
01307
01308 cxint i = 0;
01309 cxint status = 0;
01310 cxint nw = 0;
01311 cxint ns = 0;
01312
01313 const cxdouble* rdata = NULL;
01314
01315 cxdouble conad = 0.;
01316 cxdouble wlstep = 0.;
01317 cxdouble wlstart = 0.;
01318 cxdouble exptime = 0.;
01319 cxdouble scale = UT_M1_VIGNETTED_AREA / (H_PLANCK * C_LIGHT);
01320
01321 cpl_propertylist* properties = NULL;
01322
01323 cpl_matrix* filter = NULL;
01324
01325 cpl_image* _spectra = NULL;
01326 cpl_image* fluxobs = NULL;
01327 cpl_image* response = NULL;
01328 cpl_image* fresponse = NULL;
01329
01330 cpl_table* _extinction = NULL;
01331 cpl_table* _flux = NULL;
01332 cpl_table* efficiency = NULL;
01333
01334
01335 if (result == NULL) {
01336 return -1;
01337 }
01338
01339 if ((spectra == NULL) || (spectra->spectra == NULL)) {
01340 return -2;
01341 }
01342
01343 if (fibers == NULL) {
01344 return -3;
01345 }
01346
01347 if ((flux == NULL) || (extinction == NULL)) {
01348 return -4;
01349 }
01350
01351 if (config == NULL) {
01352 return -5;
01353 }
01354
01355
01356 if ((result->response != NULL) || (result->efficiency != NULL)) {
01357
01358 gi_warning("%s: Results structure at %p is not empty! Contents "
01359 "might be lost.", _id, result);
01360
01361 }
01362
01363 properties = giraffe_image_get_properties(spectra->spectra);
01364 cx_assert(properties != NULL);
01365
01366 _spectra = giraffe_image_get(spectra->spectra);
01367 cx_assert(_spectra != NULL);
01368
01369 _extinction = giraffe_table_get(extinction);
01370 cx_assert(_extinction != NULL);
01371
01372 _flux = giraffe_table_get(flux);
01373 cx_assert(_flux != NULL);
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384 fluxobs = cpl_image_collapse_create(_spectra, 1);
01385
01386
01387
01388
01389
01390
01391 if (config->sky_subtraction == TRUE) {
01392
01393 cpl_image* sky_spectrum = NULL;
01394
01395 cpl_table* _fibers = giraffe_table_get(fibers);
01396
01397
01398 ns = cpl_image_get_size_x(_spectra);
01399
01400 sky_spectrum = _giraffe_compute_mean_sky(_spectra, _fibers);
01401
01402 if (sky_spectrum == NULL) {
01403
01404 cpl_image_delete(fluxobs);
01405 fluxobs = NULL;
01406
01407 return 1;
01408
01409 }
01410
01411
01412
01413
01414
01415
01416 giraffe_error_push();
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426
01427
01428 cpl_image_multiply_scalar(sky_spectrum, ns);
01429
01430
01431
01432
01433
01434
01435 cpl_image_subtract(fluxobs, sky_spectrum);
01436
01437 cpl_image_delete(sky_spectrum);
01438 sky_spectrum = NULL;
01439
01440 if (cpl_error_get_code() != CPL_ERROR_NONE) {
01441
01442 cpl_image_delete(fluxobs);
01443 fluxobs = NULL;
01444
01445 return 1;
01446
01447 }
01448
01449 giraffe_error_pop();
01450
01451 }
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462 status = _giraffe_correct_extinction(fluxobs, properties, _extinction);
01463
01464 if (status != 0) {
01465 cpl_msg_warning(_id, "Extinction correction failed!");
01466 }
01467
01468
01469
01470
01471
01472
01473 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
01474 conad = cpl_propertylist_get_double(properties, GIALIAS_CONAD);
01475 wlstep = cpl_propertylist_get_double(properties, GIALIAS_BINSTEP);
01476 wlstart = cpl_propertylist_get_double(properties, GIALIAS_BINWLMIN);
01477
01478
01479
01480
01481
01482
01483 cpl_image_multiply_scalar(fluxobs, conad / exptime / wlstep);
01484
01485
01486
01487
01488
01489
01490
01491
01492
01493
01494
01495 response = _giraffe_compute_response(fluxobs, properties, _flux);
01496
01497 cpl_image_delete(fluxobs);
01498 fluxobs = NULL;
01499
01500 if (response == NULL) {
01501 return 2;
01502 }
01503
01504
01505
01506
01507
01508
01509 filter = cpl_matrix_new(2 * xrad + 1, 2 * yrad + 1);
01510 cpl_matrix_fill(filter, 1.);
01511
01512 fresponse = cpl_image_filter_median(response, filter);
01513
01514 cpl_matrix_delete(filter);
01515 filter = NULL;
01516
01517 cpl_image_delete(response);
01518 response = fresponse;
01519
01520 if (response == NULL) {
01521 return 3;
01522 }
01523
01524
01525
01526
01527
01528
01529
01530
01531
01532
01533
01534 giraffe_error_push();
01535
01536 nw = cpl_image_get_size_y(response);
01537
01538 efficiency = cpl_table_new(nw);
01539
01540 cpl_table_new_column(efficiency, "WLEN", CPL_TYPE_DOUBLE);
01541 cpl_table_new_column(efficiency, "BINWIDTH", CPL_TYPE_DOUBLE);
01542 cpl_table_new_column(efficiency, "EFFICIENCY", CPL_TYPE_DOUBLE);
01543
01544 cpl_table_set_column_unit(efficiency, "WLEN", "nm");
01545 cpl_table_set_column_unit(efficiency, "BINWIDTH", "nm");
01546
01547 if (cpl_error_get_code() != CPL_ERROR_NONE) {
01548
01549 cpl_table_delete(efficiency);
01550 efficiency = NULL;
01551
01552 cpl_image_delete(response);
01553 response = NULL;
01554
01555 return 4;
01556
01557 }
01558
01559 giraffe_error_pop();
01560
01561
01562 rdata = cpl_image_get_data_double_const(response);
01563
01564 for (i = 0; i < nw; ++i) {
01565
01566 cxdouble wl = wlstart + i * wlstep;
01567
01568 cpl_table_set_double(efficiency, "WLEN", i, wl);
01569 cpl_table_set_double(efficiency, "BINWIDTH", i, wlstep);
01570 cpl_table_set_double(efficiency, "EFFICIENCY", i,
01571 rdata[i] / (scale * wl));
01572
01573 }
01574
01575 rdata = NULL;
01576
01577
01578
01579
01580
01581
01582
01583 result->response = giraffe_image_new(CPL_TYPE_DOUBLE);
01584 giraffe_image_set_properties(result->response, properties);
01585 giraffe_image_set(result->response, response);
01586
01587 cpl_image_delete(response);
01588 response = NULL;
01589
01590 result->efficiency = giraffe_table_new();
01591 giraffe_table_set_properties(result->efficiency, properties);
01592 giraffe_table_set(result->efficiency, efficiency);
01593
01594 cpl_table_delete(efficiency);
01595 efficiency = NULL;
01596
01597 return 0;
01598
01599 }
01600
01601
01602 GiFxCalibrationConfig*
01603 giraffe_fxcalibration_config_create(cpl_parameterlist* parameters)
01604 {
01605
01606 cpl_parameter *p = NULL;
01607
01608 GiFxCalibrationConfig* self = NULL;
01609
01610
01611 if (parameters == NULL) {
01612 return NULL;
01613 }
01614
01615 self = cx_calloc(1, sizeof *self);
01616 cx_assert(self != NULL);
01617
01618
01619
01620
01621
01622
01623 self->sky_subtraction = FALSE;
01624
01625
01626
01627
01628
01629
01630 p = cpl_parameterlist_find(parameters,
01631 "giraffe.fxcalibration.sky.correct");
01632
01633 if (p != NULL) {
01634 self->sky_subtraction = cpl_parameter_get_bool(p);
01635 }
01636
01637 return self;
01638
01639 }
01640
01641
01655 void
01656 giraffe_fxcalibration_config_destroy(GiFxCalibrationConfig* self)
01657 {
01658
01659 if (self != NULL) {
01660 cx_free(self);
01661 }
01662
01663 return;
01664
01665 }
01666
01667
01681 void
01682 giraffe_fxcalibration_config_add(cpl_parameterlist* parameters)
01683 {
01684
01685 cpl_parameter* p = NULL;
01686
01687
01688 if (parameters == NULL) {
01689 return;
01690 }
01691
01692 p = cpl_parameter_new_value("giraffe.fxcalibration.sky.correct",
01693 CPL_TYPE_BOOL,
01694 "Correct spectra for the sky emission",
01695 "giraffe.fxcalibration",
01696 FALSE);
01697 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flx-skycorr");
01698 cpl_parameterlist_append(parameters, p);
01699
01700 return;
01701
01702 }