GIRAFFE Pipeline Reference Manual

gifxcalibration.c

00001 /* $Id: gifxcalibration.c,v 1.11 2011/12/23 13:37:12 rpalsa Exp $
00002  *
00003  * This file is part of the GIRAFFE Pipeline
00004  * Copyright (C) 2002-2006 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 /*
00022  * $Author: rpalsa $
00023  * $Date: 2011/12/23 13:37:12 $
00024  * $Revision: 1.11 $
00025  * $Name: giraffe-2_9 $
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  * @brief
00061  *   Compute the angular separation of two objects.
00062  *
00063  * @param ra_0   Rightascension of the first object in degrees.
00064  * @param dec_0  Declination of the first object in degrees.
00065  * @param ra_1   Rightascension of the second object in degrees.
00066  * @param dec_1  Declination of the second object in degrees.
00067  *
00068  * @return
00069  *   The angular separation of the two objects in arcsec.
00070  *
00071  * Computes the angular separation of the two coordinate pairs given.
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  * @brief
00102  *  Spline interpolation using Hermite polynomials.
00103  *
00104  * @param xp      Abscissa value for which the interpolation is computed.
00105  * @param x       Array of abscissa values
00106  * @param y       Array of ordinate values
00107  * @param istart  Index of the first array element used.
00108  *
00109  * @return
00110  *  The interpolated value of the column @em ylabel.
00111  *
00112  * The function performs a spline interpolation using Hermite polynomials.
00113  * The array @em x and @em y provide the abscissa and ordinate values of
00114  * the function to be interpolated. The interpolation is computed for the
00115  * abscissa value @em xp.
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  * @brief
00199  *  Spline interpolation of a table column.
00200  *
00201  * @param tbl     Table with column to interpolate
00202  * @param xlabel  Column label of the abscissa column
00203  * @param ylabel  Column label of the ordinate column
00204  * @param xp      Abscissa value for which the interpolation is computed.
00205  * @param istart  Row index of the first table row used.
00206  *
00207  * @return
00208  *  The interpolated value of the column @em ylabel.
00209  *
00210  * The function performs a spline interpolation using Hermite polynomials
00211  * of the table column given by the column label @em ylabel. The column
00212  * @em xlabel are the corresponding abscissa values, and @em xp is the
00213  * abscissa value for which the interpolation is computed.
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  * @brief
00236  *  Creates a 2d table of a flux standard star catalog spectrum
00237  *
00238  * @param catalog  3d table of flux standard reference spectra.
00239  * @param row      Table row from which the reference spectrum is extracted.
00240  *
00241  * @return
00242  *  The function returns a newly allocated 2d table containing the
00243  *  tabulated spectrum of the flux standard, or @c NULL if an error
00244  *  occurred.
00245  *
00246  * The input catalog @em catalog contains one flux standard spectrum per
00247  * table row. The function takes a spectrum from the the given row @em row
00248  * and stores it in an ordinary table structures. The output table contains
00249  * 3 columns, which give the wavelength and the width of each wavelength bin,
00250  * and the reference flux at this wavelength. The column names are
00251  * "LAMBDA", "BIN_WIDTH" and "F_LAMBDA" respectively.
00252  *
00253  * If the catalog contains wavelength in Angstrom, all 3 columns are
00254  * converted to nano meters.
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;  /* Conversion factor Angstroem to nm */
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     * The fluxes of the catalog spectra are scaled by a factor of 1.e+16.
00346     * This scaling is reversed here.
00347     */
00348 
00349     cpl_table_multiply_scalar(flux, columns[2], 1.e-16);
00350 
00351 
00352     /*
00353      * If catalog wavelengths are given in terms of Angstrom all columns
00354      * are converted to nm, assuming that the catalog columns are consistent.
00355      * If no unit is given, or the unit string is empty, it is assumed that
00356      * the table data is already given in nm.
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  * @brief
00410  *  Create a standardized extinction table from input table.
00411  *
00412  * @param extinction  Input extinction table.
00413  *
00414  * @return
00415  *  The created standard extinction table, or @c NULL if an error occurred.
00416  *
00417  * The function creates a new extinction table from the input table. If the
00418  * input table contains wavelength in units of Angstroem the wavelength are
00419  * converted to nano meters. The column of the extinction coefficients to
00420  * use is copied from the input table and renamed to "EXTINCTION".
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;  /* Conversion factor Angstroem to nm */
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      * Convert wavelength units from Angstroem to nano meters
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      * Select the extinction coefficients for the
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      * Setup the final extinction table
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      * Count the available sky fibers, and save their position indices.
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      * Compute the mean sky. If more than 2 sky fibers were used, the mean
00681      * sky is the median of all sky fibers, in order to get rid of spurious
00682      * artifacts, cosmics for instance. If there are less than 3 sky fibers
00683      * the mean sky is the simple average of all sky fibers.
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      * If a fiber table is present and it contains the relative fiber
00816      * transmission data, the sky spectrum is corrected for that before
00817      * it is subtracted. Otherwise a fiber transmission of 1. is assumed.
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          * Add up the flux of all fibers for each wavelength bin,
00889          * ignoring all sky and simultaneous calibration fibers.
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  * @brief
00917  *   Correct a spectrum for the atmospheric extinction.
00918  *
00919  * @param spectrum    The spectrum to be corrected.
00920  * @param properties  Properties of the input spectrum.
00921  * @param extinction  The table containing the atmospheric extinction.
00922  *
00923  * @return
00924  *   The function returns 0 on success, or a non-zero value otherwise.
00925  *
00926  * The function corrects the input spectrum @em spectrum for the atmospheric
00927  * extinction. The atmospheric extinction as a function of wavelength is
00928  * taken from the table @em extinction.
00929  *
00930  * The extinction table is expected to contain a wavelength column "LAMBDA"
00931  * in units of nano meters. To support legacy tables "Angstroem" are
00932  * also accepted, but not recommended.
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      * Setup the extinction table. This will convert wavelengths from
00975      * Angstroem to nano meters if it is necessary, and set the
00976      * name of the column of extinction coefficients to "Extinction".
00977      */
00978 
00979     _extinction = _giraffe_setup_extinction(extinction);
00980 
00981     if (_extinction == NULL) {
00982         return 1;
00983     }
00984 
00985 
00986     /*
00987      * Get the wavelength grid parameters of the observered spectrum
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      * Compute the airmass for the time of the observation
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      * Apply the correction to the input spectrum
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          * Correct the spectrum flux of this wavelength bin for
01094          * atmospheric extinction.
01095          *
01096          * The extinction ext is given in mag/airmass. The correction for
01097          * the effect of the earth atmoshpere gives for the observed
01098          * magnitude at the top of the atmosphere m_top = m - ext * airmass
01099          *
01100          * Converting the magnitude into a flux using m = -2.5 * log10(flux)
01101          * the correction factor to be applied is 10^(0.4 * ext * airmass)
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  * @brief
01118  *   Compute the instrument spectral response function.
01119  *
01120  * @param spectrum    Observed flux standard spectrum image.
01121  * @param properties  Properties of the observed flux standard.
01122  * @param refflux     Reference fluxes of the flux standard.
01123  *
01124  * @return
01125  *   On success the function returns the instruments spectral response
01126  *   function as an image, or @c NULL otherwise.
01127  *
01128  * The instruments spectral response function is computed, by interpolating
01129  * the tabulated reference flux of the observed flux standard onto the
01130  * wavelength grid of the observed spectrum, and dividing the observed
01131  * flux by the reference flux values.
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      * Get the wavelength grid parameters of the observered spectrum
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      * Compute the response for each spectrum and wavelength bin of the
01190      * observed spectrum image.
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     * Get the telescope pointing from the properties of the
01266     * rebinned spectra
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      * Search for matching objects in the flux standards catalog
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              * Compute angular separation between the observation and the
01367              * positions given in the flux standards catalog.
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              * Create flux table of the flux standart object found.
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;  /* cm^2    */
01524     const cxdouble H_PLANCK = 6.62606896e-27;             /* erg s   */
01525     const cxdouble C_LIGHT = 2.99792458e17;               /* nm s^-1 */
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      * Compute the total observed flux. No background subtraction is
01599      * performed, but it is assumed that the background is negligible.
01600      * Also, no attempt is made to figure out which fibers actually
01601      * were illuminated by the object.
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      * Correct for the atmospheric extinction
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      * Convert the observed spectrum from ADU to e- / s / nm
01670      */
01671 
01672     cpl_image_multiply_scalar(fluxobs, conad / exptime / wlstep);
01673 
01674 
01675     /*
01676      * Compute the response function R = Fobs / Fref in units of
01677      * 'e- erg^-1 cm^2'
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      * Compute instrument efficiency
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      * Fill the results object
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      * Set application defaults
01816      */
01817 
01818     self->sky_subtraction = FALSE;
01819 
01820     /*
01821      * Lookup the parameters in the given parameter list and set the
01822      * value accordingly if it is found.
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 }

This file is part of the GIRAFFE Pipeline Reference Manual 2.9.0.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Jan 26 14:20:27 2012 by doxygen 1.6.3 written by Dimitri van Heesch, © 1997-2004