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