GIRAFFE Pipeline Reference Manual

gipsf.c

00001 /* $Id: gipsf.c,v 1.16 2010/07/19 13:56:06 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: 2010/07/19 13:56:06 $
00024  * $Revision: 1.16 $
00025  * $Name: giraffe-2_9 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #  include <config.h>
00030 #endif
00031 
00032 
00033 #include <string.h>
00034 #include <math.h>
00035 
00036 #include <cxmacros.h>
00037 #include <cxmessages.h>
00038 #include <cxmemory.h>
00039 #include <cxstrutils.h>
00040 
00041 #include <cpl_error.h>
00042 #include <cpl_image.h>
00043 #include <cpl_table.h>
00044 #include <cpl_parameterlist.h>
00045 #include <cpl_msg.h>
00046 
00047 #include "gialias.h"
00048 #include "gierror.h"
00049 #include "gimessages.h"
00050 #include "gimatrix.h"
00051 #include "gichebyshev.h"
00052 #include "gimodel.h"
00053 #include "gipsfdata.h"
00054 #include "gilocalization.h"
00055 #include "gimask.h"
00056 #include "gimath.h"
00057 #include "giclip.h"
00058 #include "gifiberutils.h"
00059 #include "gipsf.h"
00060 
00061 
00070 enum GiProfileId {
00071     PROFILE_PSFEXP   = 1 << 1,
00072     PROFILE_PSFEXP2  = 1 << 2,
00073     PROFILE_GAUSSIAN = 1 << 3
00074 };
00075 
00076 typedef enum GiProfileId GiProfileId;
00077 
00078 
00079 struct GiPsfParams {
00080     cxint bsize;        /* Size of X bins for the fit */
00081     cxint mwidth;       /* Maximum spectrum half-width for the fit */
00082     cxdouble limit;     /* Profile computation limit. Fraction of amplitude */
00083     cxbool normalize;   /* Use normalized pixel values */
00084 };
00085 
00086 typedef struct GiPsfParams GiPsfParams;
00087 
00088 
00089 struct GiPsfBin {
00090     cxdouble zmin;
00091     cxdouble zmax;
00092     cxdouble xcenter;
00093     cxdouble ycenter;
00094     cxdouble ywidth;
00095 };
00096 
00097 typedef struct GiPsfBin GiPsfBin;
00098 
00099 struct GiPsfParameterFit {
00100     cpl_image* fit;
00101     cpl_matrix* coeffs;
00102 };
00103 
00104 typedef struct GiPsfParameterFit GiPsfParameterFit;
00105 
00106 
00107 /*
00108  * @brief
00109  *   Perform a 1d fit of a PSF profile parameter.
00110  *
00111  * @param result   Container for the fit results.
00112  * @param psfdata  PSF profile data.
00113  * @param name     PSF profile parameter name to fit.
00114  * @param fibers   Table of fibers used.
00115  * @param order    Order of the Chebyshev polynomial to fit.
00116  * @param setup    Kappa-sigma clipping algorithm setup information.
00117  *
00118  * @return
00119  *   The function returns 0 on success, or a non-zero value otherwise.
00120  *
00121  * The function fits a 1d Chebyshev polynomial of the order @em order to the
00122  * PSF profile parameter data given by @em psfdata and @em name. The object
00123  * @em psfdata contains the data for each parameter (peak position, width,
00124  * etc.) of the used profile model. The parameter for which the fit is
00125  * performed is selected by the name @em name. The list of fibers for which
00126  * profile data are available in @em psfdata is given by @em fibers.
00127  *
00128  * When fitting the parameter data, data points deviating too much from
00129  * the fit are rejected using a kappa-sigma rejection algorithm. The
00130  * kappa-sigma rejection is configured by @em setup.
00131  *
00132  * The results, i.e. the fit coefficients and the fit of the parameter data
00133  * are written to the results container @em result. The matrix and image
00134  * to which the results are written must have been created by the caller
00135  * with the appropriate size.
00136  */
00137 
00138 inline static cxint
00139 _giraffe_psf_fit_profile1d(GiPsfParameterFit* result,
00140                            const GiPsfData* psfdata, const cxchar* name,
00141                            const cpl_table* fibers, cxint order,
00142                            const GiClipParams* setup)
00143 {
00144 
00145     cxint i = 0;
00146     cxint ns = 0;
00147     cxint nx = 0;
00148     cxint nb = 0;
00149 
00150     cpl_matrix* x = NULL;
00151     cpl_matrix* base = NULL;
00152 
00153     const cpl_image* parameter = NULL;
00154 
00155 
00156     cx_assert(result != NULL);
00157     cx_assert(result->coeffs != NULL);
00158     cx_assert(result->fit != NULL);
00159     cx_assert(psfdata != NULL && name != NULL);
00160     cx_assert(fibers != NULL);
00161     cx_assert(setup != NULL);
00162 
00163     nb = giraffe_psfdata_bins(psfdata);
00164     ns = giraffe_psfdata_fibers(psfdata);
00165     nx = giraffe_psfdata_ysize(psfdata);
00166 
00167     if (ns != cpl_table_get_nrow(fibers)) {
00168         return -1;
00169     }
00170 
00171     if ((cpl_image_get_size_x(result->fit) != ns) ||
00172         (cpl_image_get_size_y(result->fit) != nx)) {
00173             return -1;
00174     }
00175 
00176     if ((cpl_matrix_get_nrow(result->coeffs) != order + 1) ||
00177         (cpl_matrix_get_ncol(result->coeffs) != ns)) {
00178             return -1;
00179     }
00180 
00181     for (i = 0; i < ns; i++) {
00182 
00183         register cxint j = 0;
00184         register cxint valid_bins = 0;
00185 
00186         for (j = 0; j < nb; j++) {
00187             if (giraffe_psfdata_get_bin(psfdata, i, j) >= 0.) {
00188                 ++valid_bins;
00189             }
00190         }
00191 
00192         if (valid_bins < order + 1) {
00193             return 1;
00194         }
00195 
00196     }
00197 
00198 
00199     /*
00200      * Compute the Chebyshev base for all points
00201      */
00202 
00203     x = cpl_matrix_new(nx, 1);
00204 
00205     for (i = 0; i < nx; i++) {
00206         cpl_matrix_set(x, i, 0, i);
00207     }
00208 
00209     base = giraffe_chebyshev_base1d(0., (cxdouble)nx, order + 1, x);
00210 
00211     if (base == NULL) {
00212         cpl_matrix_delete(x);
00213         x = NULL;
00214 
00215         return 2;
00216     }
00217 
00218     cpl_matrix_delete(x);
00219     x = NULL;
00220 
00221 
00222     /*
00223      * Fit PSF profile parameter data
00224      */
00225 
00226     parameter = giraffe_psfdata_get_data(psfdata, name);
00227 
00228     if (parameter == NULL) {
00229         return 3;
00230     }
00231 
00232     for (i = 0; i < ns; i++) {
00233 
00234         cxint j = 0;
00235         cxint k = 0;
00236         cxint naccepted = 0;
00237         cxint ntotal = 0;
00238         cxint iteration = 0;
00239 
00240         const cxdouble* _parameter =
00241             cpl_image_get_data_double_const(parameter);
00242 
00243         cxdouble ratio = 1.;
00244         cxdouble* _fit = cpl_image_get_data_double(result->fit);
00245 
00246         cpl_matrix* y = NULL;
00247         cpl_matrix* ydiff = NULL;
00248         cpl_matrix* coeffs = NULL;
00249         cpl_matrix* fit = NULL;
00250 
00251 
00252         x = cpl_matrix_new(nb, 1);
00253         y = cpl_matrix_new(1, nb);
00254         ydiff = cpl_matrix_new(1, nb);
00255 
00256         for (j = 0; j < nb; j++) {
00257 
00258             cxdouble bin = giraffe_psfdata_get_bin(psfdata, i, j);
00259 
00260 
00261             if (bin >= 0.) {
00262                 cpl_matrix_set(x, k, 0, bin);
00263                 cpl_matrix_set(y, 0, k, _parameter[j * ns + i]);
00264                 ++k;
00265             }
00266 
00267         }
00268 
00269 
00270         /*
00271          * Shrink matrices to their actual size.
00272          */
00273 
00274         cpl_matrix_set_size(x, k, 1);
00275         cpl_matrix_set_size(y, 1, k);
00276         cpl_matrix_set_size(ydiff, 1, k);
00277 
00278         ntotal = cpl_matrix_get_nrow(x);
00279         naccepted = ntotal;
00280 
00281         while ((naccepted > 0) && (iteration < setup->iterations) &&
00282                (ratio > setup->fraction)) {
00283 
00284             cxdouble sigma = 0.;
00285 
00286             cpl_matrix* _base = NULL;
00287 
00288 
00289             if (coeffs != NULL) {
00290                 cpl_matrix_delete(coeffs);
00291                 coeffs = NULL;
00292             }
00293 
00294             if (fit != NULL) {
00295                 cpl_matrix_delete(fit);
00296                 fit = NULL;
00297             }
00298 
00299             _base = giraffe_chebyshev_base1d(0., (cxdouble)nx, order + 1, x);
00300             coeffs =  giraffe_matrix_leastsq(_base, y);
00301 
00302             if (coeffs == NULL) {
00303                 cpl_matrix_delete(_base);
00304                 _base = NULL;
00305             }
00306 
00307             cpl_matrix_delete(_base);
00308             _base = NULL;
00309 
00310             fit = cpl_matrix_product_create(coeffs, base);
00311 
00312             for (j = 0; j < cpl_matrix_get_nrow(x); j++) {
00313 
00314                 cxint xlower = (cxint) ceil(cpl_matrix_get(x, j, 0));
00315                 cxint xupper = (cxint) floor(cpl_matrix_get(x, j, 0));
00316 
00317                 cxdouble ylower = cpl_matrix_get(fit, 0, xlower);
00318                 cxdouble yupper = cpl_matrix_get(fit, 0, xupper);
00319                 cxdouble yfit = (yupper + ylower) / 2.;
00320 
00321                 cpl_matrix_set(ydiff, 0, j, cpl_matrix_get(y, 0, j) - yfit);
00322 
00323             }
00324 
00325             sigma = setup->level * giraffe_matrix_sigma_mean(ydiff, 0.);
00326 
00327 
00328             /*
00329              * Reject data points deviating too much.
00330              */
00331 
00332              k = 0;
00333              for (j = 0; j < cpl_matrix_get_ncol(ydiff); j++) {
00334                  if (fabs(cpl_matrix_get(ydiff, 0, j)) <= sigma) {
00335                      cpl_matrix_set(x, k, 0, cpl_matrix_get(x, j, 0));
00336                      cpl_matrix_set(y, 0, k, cpl_matrix_get(y, 0, j));
00337                      ++k;
00338                  }
00339              }
00340 
00341              cpl_matrix_set_size(x, k, 1);
00342              cpl_matrix_set_size(y, 1, k);
00343              cpl_matrix_set_size(ydiff, 1, k);
00344 
00345 
00346              /*
00347               * Stop if no additional data points have been rejected
00348               * in the last iteration. Otherwise update the number of
00349               * accepted points, the ratio, and reset the fit coefficients
00350               * and the parameter fit.
00351               */
00352 
00353             if (k == naccepted) {
00354                 break;
00355             }
00356             else {
00357                 naccepted = k;
00358                 ratio = (cxdouble)naccepted / (cxdouble) ntotal;
00359                 ++iteration;
00360             }
00361 
00362         }
00363 
00364 
00365         /*
00366          * Save the fit coefficients and the parameter fit to the
00367          * results object.
00368          */
00369 
00370         cx_assert(cpl_matrix_get_ncol(coeffs) == order + 1);
00371 
00372         for (j = 0; j < cpl_matrix_get_ncol(coeffs); j++) {
00373             cpl_matrix_set(result->coeffs, j, i,
00374                            cpl_matrix_get(coeffs, 0, j));
00375         }
00376 
00377         for (j = 0; j < nx; j++) {
00378             _fit[j * ns + i] = cpl_matrix_get(fit, 0, j);
00379         }
00380 
00381 
00382         /*
00383          * Cleanup temporary buffers
00384          */
00385 
00386         cpl_matrix_delete(x);
00387         x = NULL;
00388 
00389         cpl_matrix_delete(y);
00390         y = NULL;
00391 
00392         cpl_matrix_delete(ydiff);
00393         ydiff = NULL;
00394 
00395         cpl_matrix_delete(coeffs);
00396         coeffs = NULL;
00397 
00398         cpl_matrix_delete(fit);
00399         fit = NULL;
00400 
00401     }
00402 
00403     cpl_matrix_delete(base);
00404     base = NULL;
00405 
00406     return 0;
00407 
00408 }
00409 
00410 
00411 /*
00412  * @brief
00413  *   Perform a 2d fit of a PSF profile parameter.
00414  *
00415  * @param result   Container for the fit results.
00416  * @param fibers   Table of fibers used.
00417  * @param psfdata  Binned parameter data to fit.
00418  * @param xbin     Abscissa values of the bins.
00419  * @param ybin     Ordinate values of the bins.
00420  * @param xorder   X order of the Chebyshev polynomial to fit.
00421  * @param yorder   Y order of the Chebyshev polynomial to fit.
00422  * @param yfit     Ordinate values for which the fit is computed.
00423  * @param ystart   Minimum value of the ordinate values range.
00424  * @param yend     Maximum value of the ordinate values range.
00425  * @param setup    Kappa-sigma clipping algorithm setup information.
00426  *
00427  * @return
00428  *   The function returns 0 on success, or a non-zero value otherwise.
00429  *
00430  * The function fits a 2d Chebyshev polynomial of the order @em xorder and
00431  * @em yorder, along x and y respectively, to the PSF profile parameter
00432  * data given by @em psfdata. The object @em psfdata contains the data for
00433  * a single PSF profile parameter (peak position, width, etc.) of the used
00434  * profile model. The list of fibers for which profile data are available
00435  * in @em psfdata is given by @em fibers. The abscissa and ordinate values
00436  * for which the PSF parameter data have been measured.
00437  *
00438  * When fitting the parameter data, data points deviating too much from
00439  * the fit are rejected using a kappa-sigma rejection algorithm. The
00440  * kappa-sigma rejection is configured by @em setup.
00441  *
00442  * The results, i.e. the fit coefficients and the fit of the parameter data
00443  * are written to the results container @em result. The matrix and image
00444  * to which the results are written must have been created by the caller
00445  * with the appropriate sizes.
00446  *
00447  * The fit is computed for the positions given by @em yfit, @em ystart and
00448  * @em yend., where the latter define the ordinate domain for which @em yfit
00449  * is given. The object @em yfit itself gives the ordinate values, and, by its
00450  * bin structure (row index), the abscissa values for whichthe fit will
00451  * be computed.
00452  */
00453 
00454 inline static cxint
00455 _giraffe_psf_fit_profile2d(GiPsfParameterFit* result, const cpl_table* fibers,
00456                            const cpl_image* psfdata, const cpl_image* xbin,
00457                            const cpl_image* ybin, cxint xorder, cxint yorder,
00458                            const cpl_image* yfit, cxint ystart, cxint yend,
00459                            const GiClipParams* setup)
00460 {
00461 
00462 
00463     cxint i = 0;
00464     cxint k = 0;
00465     cxint ns = 0;
00466     cxint nb = 0;
00467     cxint nx = 0;
00468     cxint iteration = 0;
00469     cxint ntotal = 0;
00470     cxint naccepted = 0;
00471     cxint nspectra = 0;
00472     cxint status = 0;
00473     cxint ncx = xorder + 1;
00474     cxint ncy = yorder + 1;
00475 
00476     cxdouble ratio = 1.;
00477 
00478     cpl_matrix* x = NULL;
00479     cpl_matrix* y = NULL;
00480     cpl_matrix* z = NULL;
00481     cpl_matrix* zdiff = NULL;
00482     cpl_matrix* nbins = NULL;
00483 
00484     GiChebyshev2D* fit = NULL;
00485 
00486 
00487     cx_assert(result != NULL);
00488     cx_assert(result->coeffs != NULL);
00489     cx_assert(result->fit != NULL);
00490     cx_assert(fibers != NULL);
00491     cx_assert(psfdata != NULL);
00492     cx_assert(xbin != NULL && ybin != NULL);
00493     cx_assert(yfit != NULL);
00494     cx_assert(setup != NULL);
00495 
00496     nb = cpl_image_get_size_y(xbin);
00497     ns = cpl_image_get_size_x(xbin);
00498     nx = cpl_image_get_size_y(result->fit);
00499 
00500     if (ns != cpl_table_get_nrow(fibers)) {
00501         return -1;
00502     }
00503 
00504     if ((cpl_image_get_size_x(result->fit) != ns) ||
00505         (cpl_image_get_size_y(result->fit) != nx)) {
00506             return -1;
00507     }
00508 
00509     if ((cpl_matrix_get_nrow(result->coeffs) != ncx) ||
00510         (cpl_matrix_get_ncol(result->coeffs) != ncy)) {
00511             return -1;
00512     }
00513 
00514     for (i = 0; i < ns; i++) {
00515 
00516         register cxint j = 0;
00517         register cxint valid_bins = 0;
00518 
00519         const cxdouble* _xbin = cpl_image_get_data_double_const(xbin);
00520 
00521         for (j = 0; j < nb; j++) {
00522             if (_xbin[j * ns + i] >= 0.) {
00523                 ++valid_bins;
00524             }
00525         }
00526 
00527         if (valid_bins < ncx * ncy) {
00528             return 1;
00529         }
00530 
00531     }
00532 
00533 
00534     /*
00535      * Fill working buffers
00536      */
00537 
00538     x = cpl_matrix_new(nb * ns, 1);
00539     y = cpl_matrix_new(nb * ns, 1);
00540     z = cpl_matrix_new(1, nb * ns);
00541     zdiff = cpl_matrix_new(1, nb * ns);
00542     nbins = cpl_matrix_new(nb * ns, 1);
00543 
00544     for (i = 0; i < ns; i++) {
00545 
00546         register cxint j = 0;
00547 
00548         const cxdouble* _xbin = cpl_image_get_data_double_const(xbin);
00549         const cxdouble* _ybin = cpl_image_get_data_double_const(ybin);
00550         const cxdouble* _zbin = cpl_image_get_data_double_const(psfdata);
00551 
00552 
00553         for ( j = 0; j < nb; j++) {
00554 
00555             register cxint l = j * ns + i;
00556 
00557 
00558             if (_xbin[l] >= 0.) {
00559                 cpl_matrix_set(nbins, k, 0, nspectra);
00560                 cpl_matrix_set(x, k, 0, _xbin[l]);
00561                 cpl_matrix_set(y, k, 0, _ybin[l]);
00562                 cpl_matrix_set(z, 0, k, _zbin[l]);
00563                 ++k;
00564             }
00565 
00566         }
00567 
00568         ++nspectra;
00569 
00570     }
00571 
00572 
00573     /*
00574      * Shrink working buffers to their actual size
00575      */
00576 
00577     cpl_matrix_set_size(x, k, 1);
00578     cpl_matrix_set_size(y, k, 1);
00579     cpl_matrix_set_size(z, 1, k);
00580     cpl_matrix_set_size(zdiff, 1, k);
00581     cpl_matrix_set_size(nbins, k, 1);
00582 
00583     ntotal = cpl_matrix_get_nrow(x);
00584     naccepted = ntotal;
00585 
00586     while ((naccepted > 0) && (iteration < setup->iterations) &&
00587            (ratio > setup->fraction))
00588     {
00589 
00590         cxdouble sigma = 0.;
00591 
00592         cpl_matrix* base = NULL;
00593         cpl_matrix* coeffs = NULL;
00594         cpl_matrix* _coeffs = NULL;
00595 
00596         register cxdouble* _pfit = cpl_image_get_data_double(result->fit);
00597 
00598 
00599         base = giraffe_chebyshev_base2d(0., ystart, nx, yend, ncx, ncy, x, y);
00600 
00601         if (base == NULL) {
00602             cpl_matrix_delete(nbins);
00603             nbins = NULL;
00604 
00605             cpl_matrix_delete(zdiff);
00606             zdiff = NULL;
00607 
00608             cpl_matrix_delete(z);
00609             z = NULL;
00610 
00611             cpl_matrix_delete(y);
00612             y = NULL;
00613 
00614             cpl_matrix_delete(x);
00615             x = NULL;
00616 
00617             return 1;
00618         }
00619 
00620         _coeffs = giraffe_matrix_leastsq(base, z);
00621 
00622         if (_coeffs == NULL) {
00623             cpl_matrix_delete(base);
00624             base = NULL;
00625 
00626             cpl_matrix_delete(nbins);
00627             nbins = NULL;
00628 
00629             cpl_matrix_delete(zdiff);
00630             zdiff = NULL;
00631 
00632             cpl_matrix_delete(z);
00633             z = NULL;
00634 
00635             cpl_matrix_delete(y);
00636             y = NULL;
00637 
00638             cpl_matrix_delete(x);
00639             x = NULL;
00640 
00641             return 1;
00642         }
00643 
00644         cpl_matrix_delete(base);
00645         base = NULL;
00646 
00647 
00648         /*
00649          * Compute parameter fit and reject data points deviating too
00650          * much from the fit
00651          */
00652 
00653         coeffs = cpl_matrix_wrap(xorder + 1, yorder + 1,
00654                                  cpl_matrix_get_data(_coeffs));
00655 
00656         if (fit != NULL) {
00657             giraffe_chebyshev2d_delete(fit);
00658             fit = NULL;
00659         }
00660 
00661         fit = giraffe_chebyshev2d_new(xorder, yorder);
00662         status = giraffe_chebyshev2d_set(fit, 0., nx, ystart, yend, coeffs);
00663 
00664         if (status != 0) {
00665             giraffe_chebyshev2d_delete(fit);
00666             fit = NULL;
00667 
00668             cpl_matrix_unwrap(coeffs);
00669             coeffs = NULL;
00670 
00671             cpl_matrix_delete(_coeffs);
00672             _coeffs = NULL;
00673 
00674             cpl_matrix_delete(nbins);
00675             nbins = NULL;
00676 
00677             cpl_matrix_delete(zdiff);
00678             zdiff = NULL;
00679 
00680             cpl_matrix_delete(z);
00681             z = NULL;
00682 
00683             cpl_matrix_delete(y);
00684             y = NULL;
00685 
00686             cpl_matrix_delete(x);
00687             x = NULL;
00688 
00689             return 1;
00690         }
00691 
00692         cpl_matrix_unwrap(coeffs);
00693         coeffs = NULL;
00694 
00695         cpl_matrix_delete(_coeffs);
00696         _coeffs = NULL;
00697 
00698 
00699         /* FIXME: Check whether performance can be improved if the fit
00700          *        is only computed for the bins instead of the full
00701          *        image y-axis pixels. Note that this needs an additional
00702          *        buffer and a computation of the last fit on the full
00703          *        grid.
00704          */
00705 
00706         for (i = 0; i < ns; i++) {
00707 
00708             register cxint j = 0;
00709 
00710             register const cxdouble* _yfit =
00711                 cpl_image_get_data_double_const(yfit);
00712 
00713             for (j = 0; j < nx; j++) {
00714 
00715                 register cxint l = j * ns + i;
00716 
00717                 _pfit[l] = giraffe_chebyshev2d_eval(fit, j, _yfit[l]);
00718 
00719             }
00720 
00721         }
00722 
00723         for (i = 0; i < cpl_matrix_get_nrow(x); i++) {
00724 
00725             cxint n = cpl_matrix_get(nbins, i, 0);
00726             cxint lower = (cxint) ceil(cpl_matrix_get(x, i, 0)) * ns + n;
00727             cxint upper = (cxint) floor(cpl_matrix_get(x, i, 0)) * ns + n;
00728 
00729             cxdouble zfit = (_pfit[lower] + _pfit[upper]) / 2.;
00730 
00731             cpl_matrix_set(zdiff, 0, i, cpl_matrix_get(z, 0, i) - zfit);
00732 
00733         }
00734 
00735         sigma = setup->level * giraffe_matrix_sigma_mean(zdiff, 0.);
00736 
00737         k = 0;
00738         for (i = 0; i < cpl_matrix_get_ncol(zdiff); i++) {
00739             if (fabs(cpl_matrix_get(zdiff, 0, i)) <= sigma) {
00740                 cpl_matrix_set(x, k, 0, cpl_matrix_get(x, i, 0));
00741                 cpl_matrix_set(y, k, 0, cpl_matrix_get(y, i, 0));
00742                 cpl_matrix_set(z, 0, k, cpl_matrix_get(z, 0, i));
00743                 cpl_matrix_set(nbins, k, 0, cpl_matrix_get(nbins, i, 0));
00744                 ++k;
00745             }
00746         }
00747 
00748         cpl_matrix_set_size(x, k, 1);
00749         cpl_matrix_set_size(y, k, 1);
00750         cpl_matrix_set_size(z, 1, k);
00751         cpl_matrix_set_size(zdiff, 1, k);
00752         cpl_matrix_set_size(nbins, k, 1);
00753 
00754 
00755         /*
00756          * Stop if no additional data points have been rejected
00757          * in the last iteration. Otherwise update the number of
00758          * accepted points, the ratio, and reset the fit coefficients
00759          * and the parameter fit.
00760          */
00761 
00762         if (k == naccepted) {
00763             break;
00764         }
00765         else {
00766             naccepted = k;
00767             ratio = (cxdouble)naccepted / (cxdouble) ntotal;
00768             ++iteration;
00769         }
00770 
00771     }
00772 
00773 
00774     /*
00775      * Copy the fit coefficients to the results container.
00776      */
00777 
00778     for (i = 0; i < cpl_matrix_get_nrow(result->coeffs); i++) {
00779 
00780         register cxint j = 0;
00781 
00782         const cpl_matrix* c = giraffe_chebyshev2d_coeffs(fit);
00783 
00784 
00785         for (j = 0; j < cpl_matrix_get_ncol(result->coeffs); j++) {
00786             cpl_matrix_set(result->coeffs, i, j, cpl_matrix_get(c, i, j));
00787         }
00788 
00789     }
00790 
00791 
00792     /*
00793      * Cleanup temporary buffers
00794      */
00795 
00796     giraffe_chebyshev2d_delete(fit);
00797     fit = NULL;
00798 
00799     cpl_matrix_delete(nbins);
00800     nbins = NULL;
00801 
00802     cpl_matrix_delete(zdiff);
00803     zdiff = NULL;
00804 
00805     cpl_matrix_delete(z);
00806     z = NULL;
00807 
00808     cpl_matrix_delete(y);
00809     y = NULL;
00810 
00811     cpl_matrix_delete(x);
00812     x = NULL;
00813 
00814     return 0;
00815 }
00816 
00817 
00818 /*
00819  * @brief
00820  *  Fit a PSF profile model to each fiber spectrum.
00821  *
00822  * @param result    Fitted PSF profile model parameters.
00823  * @param zraw      Raw image of the fiber spectra.
00824  * @param zvar      Raw image of flux errors.
00825  * @param locy      Fiber centroid position.
00826  * @param locw      Fiber width.
00827  * @param fibers    List of fibers to process.
00828  * @param bpm       Optional bad pixel map.
00829  * @param config    PSF profile fit setup parameters.
00830  *
00831  * @return
00832  *  The function returns @c 0 on success, or a non-zero value otherwise.
00833  *
00834  * The function fits a profile model given by @em psfmodel to each bin along
00835  * the dispersion direction (x-axis) for all fibers listed in @em fibers.
00836  * The bin size is given by the setup parameters @em config. It also
00837  * specifies the maximum allowed half width of a fiber spectrum.
00838  *
00839  * @note
00840  *  Currently only the line models "psfexp", "psfexp2" and "gaussian"
00841  *  are supported.
00842  */
00843 
00844 inline static cxint
00845 _giraffe_psf_compute_profile(GiPsfData* result, cpl_image* zraw,
00846                              cpl_image* zvar, cpl_image* locy,
00847                              cpl_image* locw, cpl_table* fibers,
00848                              cpl_image* bpm, GiModel* profile,
00849                              GiPsfParams* config)
00850 {
00851 
00852     const cxchar* model = NULL;
00853     const cxchar* ridx = NULL;
00854 
00855     const cxdouble cutoff = log(config->limit);
00856 
00857     cxint nx = 0;
00858     cxint ny = 0;
00859     cxint ns = 0;
00860     cxint fiber = 0;
00861     cxint nbins = 0;
00862     cxint nspectra = 0;
00863 
00864     cxsize n = 0;
00865 
00866     cxdouble exponent;    /* PSF profile exponent initial guess */
00867 
00868     cpl_matrix* mx = NULL;
00869     cpl_matrix* my = NULL;
00870     cpl_matrix* ms = NULL;
00871 
00872     cpl_image* zx = NULL;
00873     cpl_image* zv = NULL;
00874 
00875     GiProfileId psfmodel = 0;
00876 
00877 
00878     cx_assert(result != NULL);
00879     cx_assert((zraw != NULL) && (zvar != NULL));
00880     cx_assert((locy != NULL) && (locw != NULL));
00881     cx_assert(fibers != NULL);
00882     cx_assert(profile != NULL);
00883     cx_assert(config != NULL);
00884 
00885     cx_assert(cpl_image_get_size_x(zraw) == cpl_image_get_size_x(zvar));
00886     cx_assert(cpl_image_get_size_y(zraw) == cpl_image_get_size_y(zvar));
00887 
00888     cx_assert(cpl_image_get_size_x(locy) == cpl_image_get_size_x(locw));
00889     cx_assert(cpl_image_get_size_y(locy) == cpl_image_get_size_y(locw));
00890 
00891 
00892     nx = cpl_image_get_size_y(zraw);
00893     ny = cpl_image_get_size_x(zraw);
00894     ns = cpl_table_get_nrow(fibers);
00895 
00896     nbins = (cxint) giraffe_psfdata_bins(result);
00897 
00898     if (ns != cpl_image_get_size_x(locy)) {
00899         return -1;
00900     }
00901 
00902     if ((bpm != NULL) && (cpl_image_get_type(bpm) != CPL_TYPE_INT)) {
00903         return -2;
00904     }
00905 
00906     if (giraffe_psfdata_fibers(result) != (cxsize) ns) {
00907         return -3;
00908     }
00909 
00910     if ((giraffe_psfdata_xsize(result) != (cxsize) ny) ||
00911         (giraffe_psfdata_ysize(result) != (cxsize) nx)) {
00912         return -3;
00913     }
00914 
00915 
00916     /*
00917      * Check the model type. Only the line models "psfexp", "psfexp2" and
00918      * "gaussian" are supported.
00919      */
00920 
00921     model = giraffe_model_get_name(profile);
00922 
00923     if (strcmp(model, "psfexp") == 0) {
00924         psfmodel = PROFILE_PSFEXP;
00925     }
00926     else if (strcmp(model, "psfexp2") == 0) {
00927         psfmodel = PROFILE_PSFEXP2;
00928     }
00929     else if (strcmp(model, "gaussian") == 0) {
00930         psfmodel = PROFILE_GAUSSIAN;
00931     }
00932     else {
00933         return -4;
00934     }
00935 
00936 
00937     if (config->normalize != FALSE) {
00938 
00939         cxint x = 0;
00940 
00941         cxdouble zmax = 0.;
00942         cxdouble* zsum = cx_calloc(nx, sizeof(cxdouble));
00943 
00944 
00945         /*
00946          * Find maximum pixel value, taking bad pixels into account if the
00947          * bad pixel map is present.
00948          */
00949 
00950         if (bpm == NULL) {
00951 
00952             for (x = 0; x < nx; x++) {
00953 
00954                 register cxint y = 0;
00955 
00956                 register const cxdouble* _zraw =
00957                     cpl_image_get_data_double(zraw);
00958 
00959 
00960                 for (y = 0; y < ny; y++) {
00961                     zsum[x] += _zraw[x * ny + y];
00962                 }
00963 
00964                 if (zsum[x] > zmax) {
00965                     zmax = zsum[x];
00966                 }
00967             }
00968 
00969         }
00970         else {
00971 
00972             for (x = 0; x < nx; x++) {
00973 
00974                 register cxint y = 0;
00975                 register const cxint* _bpm = cpl_image_get_data_int(bpm);
00976 
00977                 register const cxdouble* _zraw =
00978                     cpl_image_get_data_double(zraw);
00979 
00980 
00981                 for (y = 0; y < ny; y++) {
00982                     register cxint i = x * ny + y;
00983 
00984                     if (_bpm[i] == 0) {
00985                         zsum[x] += _zraw[i];
00986                     }
00987                 }
00988 
00989                 if (zsum[x] > zmax) {
00990                     zmax = zsum[x];
00991                 }
00992             }
00993 
00994         }
00995 
00996 
00997         /*
00998          * Allocate the buffers for the normalized images and scale
00999          * the fiber spectrum fluxes and errors.
01000          */
01001 
01002         zx = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
01003         zv = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
01004 
01005 
01006         for (x = 0; x < nx; x++) {
01007 
01008             register cxint y = 0;
01009 
01010             register cxdouble scale = zmax / zsum[x];
01011             register const cxdouble* _zraw = cpl_image_get_data_double(zraw);
01012             register const cxdouble* _zvar = cpl_image_get_data_double(zvar);
01013             register cxdouble* _zx = cpl_image_get_data_double(zx);
01014             register cxdouble* _zv = cpl_image_get_data_double(zv);
01015 
01016             for(y = 0; y < nx; y++) {
01017                 register cxint i = x * ny + y;
01018 
01019                 _zx[i] = _zraw[i] * scale;
01020                 _zv[i] = _zvar[i] * scale;
01021             }
01022 
01023         }
01024 
01025         cx_free(zsum);
01026         zsum = NULL;
01027 
01028     }
01029     else {
01030         zx = zraw;
01031         zv = zvar;
01032     }
01033 
01034 
01035     /*
01036      * Save the initial values of the profile models exponent parameter,
01037      * since it must be reset after each bin has been fitted.
01038      */
01039 
01040     giraffe_error_push();
01041 
01042     exponent = giraffe_model_get_parameter(profile, "Width2");
01043 
01044     if (cpl_error_get_code() != CPL_ERROR_NONE) {
01045         exponent = 0.;
01046     }
01047 
01048     giraffe_error_pop();
01049 
01050 
01051     /*
01052      * Get the calibration spectrum reference index column from the
01053      * fiber list.
01054      */
01055 
01056     ridx = giraffe_fiberlist_query_index(fibers);
01057 
01058 
01059     /*
01060      * Allocate buffers for the profile data points and their errors. The
01061      * buffer size is choosen to be large enough for the number of bins of
01062      * requested psf data object and the given maximum fiber width.
01063      */
01064 
01065     mx = cpl_matrix_new(nbins * config->mwidth, 1);
01066     my = cpl_matrix_new(nbins * config->mwidth, 1);
01067     ms = cpl_matrix_new(nbins * config->mwidth, 1);
01068 
01069     if ((mx == NULL) || (my == NULL) || (ms == NULL)) {
01070         if (config->normalize == TRUE) {
01071             cpl_image_delete(zx);
01072             zx = NULL;
01073 
01074             cpl_image_delete(zv);
01075             zv = NULL;
01076         }
01077 
01078         if (mx != NULL) {
01079             cpl_matrix_delete(mx);
01080             mx = NULL;
01081         }
01082 
01083         if (my != NULL) {
01084             cpl_matrix_delete(my);
01085             my = NULL;
01086         }
01087 
01088         if (ms != NULL) {
01089             cpl_matrix_delete(ms);
01090             ms = NULL;
01091         }
01092 
01093         return 1;
01094     }
01095 
01096 
01097     /*
01098      * Allocate the buffers of the results structure here, to avoid
01099      * complicated error handling in the nested loops.
01100      */
01101 
01102     giraffe_psfdata_set_model(result, giraffe_model_get_name(profile));
01103 
01104     for (n = 0; n < giraffe_model_count_parameters(profile); n++) {
01105 
01106         const cxchar* name = giraffe_model_parameter_name(profile, n);
01107 
01108         cpl_image* values = cpl_image_new(ns, nbins, CPL_TYPE_DOUBLE);
01109 
01110         if ((name == NULL) || (values == NULL)) {
01111 
01112             giraffe_psfdata_clear(result);
01113 
01114             cpl_matrix_delete(mx);
01115             mx = NULL;
01116 
01117             cpl_matrix_delete(my);
01118             my = NULL;
01119 
01120             cpl_matrix_delete(ms);
01121             ms = NULL;
01122 
01123             if (config->normalize == TRUE) {
01124                 cpl_image_delete(zx);
01125                 zx = NULL;
01126 
01127                 cpl_image_delete(zv);
01128                 zv = NULL;
01129             }
01130 
01131             return 1;
01132         }
01133 
01134         giraffe_psfdata_set_data(result, name, values);
01135 
01136     }
01137 
01138 
01139     /*
01140      * Loop over all available fibers
01141      */
01142 
01143     for (fiber = 0; fiber < ns; fiber++) {
01144 
01145         cxint x = 0;
01146         cxint bin = 0;
01147         cxint cs = cpl_table_get_int(fibers, ridx, fiber, NULL) - 1;
01148         const cxint* _bpm = NULL;
01149 
01150         const cxdouble* _locy = cpl_image_get_data_double(locy);
01151         const cxdouble* _locw = cpl_image_get_data_double(locw);
01152         const cxdouble* _zx = cpl_image_get_data_double(zx);
01153         const cxdouble* _zv = cpl_image_get_data_double(zv);
01154 
01155 
01156         if (bpm != NULL) {
01157             _bpm = cpl_image_get_data_int(bpm);
01158         }
01159 
01160 
01161         /*
01162          * Fit a profile for each bin
01163          */
01164 
01165         for (x = 0, bin = 0; x < nx; x += config->bsize, bin++) {
01166 
01167             register cxint k = 0;
01168             register cxint xx = 0;
01169 
01170             cxint status = 0;
01171             cxint ndata = 0;
01172             cxint iterations = giraffe_model_get_iterations(profile);
01173 
01174             cxdouble amplitude = 0.;
01175             cxdouble bckground = 0.;
01176             cxdouble center = 0.;
01177             cxdouble width1 = 0.;
01178             cxdouble width2 = 0.;
01179 
01180             GiPsfBin xbin = {0., 0., 0., 0., 0.};
01181 
01182 
01183             /*
01184              * Loop over each element of this bin
01185              */
01186 
01187             for (k = 0, xx = x; (k < config->bsize) && (xx < nx); k++, xx++) {
01188 
01189                 register cxint y = 0;
01190                 register cxint l = xx * ns + cs;
01191                 register cxint m = xx * ny;
01192 
01193                 cxdouble zxmin = CX_MAXDOUBLE;
01194                 cxdouble zxmax = 0.;
01195                 cxdouble swidth = CX_MIN(_locw[l], config->mwidth);
01196                 cxdouble ylo = (cxint) floor(_locy[l] - swidth);
01197                 cxdouble yup = (cxint) ceil(_locy[l] + swidth);
01198                 cxdouble ycenter = _locy[l];
01199 
01200 
01201                 ylo = CX_MAX(0., ylo);
01202                 yup = CX_MIN(ny, yup);
01203 
01204                 if (_bpm == NULL) {
01205 
01206                     for (y = ylo; y < yup; y++) {
01207 
01208                         register cxint i = m + y;
01209 
01210                         cpl_matrix_set(mx, ndata, 0, (cxdouble)y - ycenter);
01211                         cpl_matrix_set(my, ndata, 0, _zx[i]);
01212                         cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
01213 
01214                         if (_zx[i] > zxmax) {
01215                             zxmax = _zx[i];
01216                         }
01217 
01218                         if (_zx[i] < zxmin) {
01219                             zxmin = _zx[i];
01220                         }
01221 
01222                         ++ndata;
01223 
01224                     }
01225 
01226                 }
01227                 else {
01228 
01229                     for (y = ylo; y < yup; y++) {
01230 
01231                         register cxint i = m + y;
01232 
01233                         if (_bpm[i] == 0) {
01234                             cpl_matrix_set(mx, ndata, 0,
01235                                            (cxdouble)y - ycenter);
01236                             cpl_matrix_set(my, ndata, 0, _zx[i]);
01237                             cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
01238 
01239                             if (_zx[i] > zxmax) {
01240                                 zxmax = _zx[i];
01241                             }
01242 
01243                             if (_zx[i] < zxmin) {
01244                                 zxmin = _zx[i];
01245                             }
01246 
01247                             ++ndata;
01248                         }
01249 
01250                     }
01251 
01252                 }
01253 
01254                 xbin.zmin += zxmin;
01255                 xbin.zmax += zxmax;
01256                 xbin.xcenter += xx;
01257                 xbin.ycenter += ycenter;
01258                 xbin.ywidth += swidth;
01259 
01260             }
01261 
01262 
01263             /*
01264              * Compute per bin average values
01265              */
01266 
01267             xbin.zmin /= k;
01268             xbin.zmax /= k;
01269             xbin.xcenter /= k;
01270             xbin.ycenter /= k;
01271             xbin.ywidth /= k;
01272 
01273 
01274             /*
01275              * Avoid negative background values
01276              */
01277 
01278             xbin.zmin = CX_MAX(0., xbin.zmin);
01279 
01280 
01281             /*
01282              * Setup model for this bin
01283              */
01284 
01285             giraffe_model_set_parameter(profile, "Amplitude",
01286                                         xbin.zmax - xbin.zmin);
01287             giraffe_model_set_parameter(profile, "Center", 0.);
01288             giraffe_model_set_parameter(profile, "Background", xbin.zmin);
01289 
01290             switch (psfmodel) {
01291             case PROFILE_PSFEXP:
01292                 width1 = pow(xbin.ywidth, exponent) / cutoff;
01293                 giraffe_model_set_parameter(profile, "Width2", exponent);
01294                 break;
01295 
01296             case PROFILE_PSFEXP2:
01297                 width1 = xbin.ywidth / pow(cutoff, 1. / exponent);
01298                 giraffe_model_set_parameter(profile, "Width2", exponent);
01299                 break;
01300 
01301             case PROFILE_GAUSSIAN:
01302                 width1 = xbin.ywidth / pow(cutoff, 0.5);
01303                 break;
01304 
01305             default:
01306                 break;
01307             }
01308 
01309             giraffe_model_set_parameter(profile, "Width1", width1);
01310 
01311 
01312             /*
01313              * Fit the profile
01314              */
01315 
01316             status = giraffe_model_fit_sequence(profile, mx, my, ms,
01317                                                 ndata, 0, 1);
01318 
01319             amplitude = giraffe_model_get_parameter(profile, "Amplitude");
01320             bckground = giraffe_model_get_parameter(profile, "Background");
01321             center = giraffe_model_get_parameter(profile, "Center");
01322             width1 = giraffe_model_get_parameter(profile, "Width1");
01323 
01324             if ((psfmodel == PROFILE_PSFEXP) ||
01325                 (psfmodel == PROFILE_PSFEXP2)) {
01326                 width2 = giraffe_model_get_parameter(profile, "Width2");
01327             }
01328 
01329             /*
01330              * Check fit results. The fit failed, if the maximum
01331              * number of iterations has been reached, fitted amplitude
01332              * is negative or the fitted width is negative.
01333              */
01334 
01335             if ((status != 0) ||
01336                 (giraffe_model_get_position(profile) >= iterations) ||
01337                 (amplitude <= 0.) ||
01338                 (width1 <= 0.)) {
01339 
01340                 xbin.xcenter = -1.;
01341                 amplitude = 0.;
01342                 bckground = 0.;
01343                 center = 0.;
01344                 width1 = 0.;
01345                 width2 = 0.;
01346 
01347             }
01348 
01349             giraffe_psfdata_set_bin(result, fiber, bin, xbin.xcenter);
01350 
01351             giraffe_psfdata_set(result, "Amplitude", fiber, bin, amplitude);
01352             giraffe_psfdata_set(result, "Center", fiber, bin,
01353                                 xbin.ycenter + center);
01354             giraffe_psfdata_set(result, "Background", fiber, bin, bckground);
01355             giraffe_psfdata_set(result, "Width1", fiber, bin, width1);
01356 
01357             if ((psfmodel == PROFILE_PSFEXP) ||
01358                 (psfmodel == PROFILE_PSFEXP2)) {
01359                 giraffe_psfdata_set(result, "Width2", fiber, bin, width2);
01360             }
01361 
01362         }
01363 
01364         ++nspectra;
01365 
01366     }
01367 
01368 
01369     /*
01370      * Cleanup
01371      */
01372 
01373     cpl_matrix_delete(mx);
01374     mx = NULL;
01375 
01376     cpl_matrix_delete(my);
01377     my = NULL;
01378 
01379     cpl_matrix_delete(ms);
01380     ms = NULL;
01381 
01382     if (config->normalize == TRUE) {
01383         cpl_image_delete(zx);
01384         zx = NULL;
01385 
01386         cpl_image_delete(zv);
01387         zv = NULL;
01388     }
01389 
01390     return 0;
01391 
01392 }
01393 
01394 
01395 /*
01396  * @brief
01397  *  Fit a PSF profile model to each fiber spectrum.
01398  *
01399  * @param result    Fitted PSF profile model parameters.
01400  * @param zraw      Raw image of the fiber spectra.
01401  * @param zvar      Raw image of flux errors.
01402  * @param locy      Fiber centroid position.
01403  * @param locw      Fiber width.
01404  * @param fibers    List of fibers to process.
01405  * @param bpm       Optional bad pixel map.
01406  * @param config    PSF profile fit setup parameters.
01407  *
01408  * @return
01409  *  The function returns @c 0 on success, or a non-zero value otherwise.
01410  *
01411  * The function fits a profile model given by @em psfmodel to each bin along
01412  * the dispersion direction (x-axis) for all fibers listed in @em fibers.
01413  * The bin size is given by the setup parameters @em config. It also
01414  * specifies the maximum allowed half width of a fiber spectrum.
01415  *
01416  * @note
01417  *  Currently only the line models "psfexp", "psfexp2" and "gaussian"
01418  *  are supported.
01419  */
01420 
01421 inline static cxint
01422 _giraffe_psf_refine_profile(GiPsfData* result, const GiPsfData* psfdata,
01423                             cpl_image* zraw, cpl_image* zvar,
01424                             cpl_table* fibers, cpl_image* bpm,
01425                             GiModel* profile, GiPsfParams* config)
01426 {
01427 
01428     const cxchar* model = NULL;
01429 
01430     const cxdouble cutoff = log(config->limit);
01431 
01432     cxint nx = 0;
01433     cxint ny = 0;
01434     cxint ns = 0;
01435     cxint fiber = 0;
01436     cxint nbins = 0;
01437     cxint nspectra = 0;
01438     cxint binsize = 0;
01439 
01440     cxsize n = 0;
01441 
01442     cpl_matrix* mx = NULL;
01443     cpl_matrix* my = NULL;
01444     cpl_matrix* ms = NULL;
01445 
01446     GiProfileId psfmodel = 0;
01447 
01448 
01449     cx_assert(result != NULL);
01450     cx_assert(psfdata != NULL);
01451     cx_assert((zraw != NULL) && (zvar != NULL));
01452     cx_assert(fibers != NULL);
01453     cx_assert(profile != NULL);
01454     cx_assert(config != NULL);
01455 
01456     cx_assert(cpl_image_get_size_x(zraw) == cpl_image_get_size_x(zvar));
01457     cx_assert(cpl_image_get_size_y(zraw) == cpl_image_get_size_y(zvar));
01458 
01459 
01460     nx = cpl_image_get_size_y(zraw);
01461     ny = cpl_image_get_size_x(zraw);
01462     ns = cpl_table_get_nrow(fibers);
01463 
01464     if ((bpm != NULL) && (cpl_image_get_type(bpm) != CPL_TYPE_INT)) {
01465         return -1;
01466     }
01467 
01468     if ((giraffe_psfdata_fibers(result) != (cxsize) ns) ||
01469         (giraffe_psfdata_bins(result) != (cxsize) nx)) {
01470         return -2;
01471     }
01472 
01473     if ((giraffe_psfdata_xsize(result) != (cxsize) ny) ||
01474         (giraffe_psfdata_ysize(result) != (cxsize) nx)) {
01475         return -2;
01476     }
01477 
01478     nbins = giraffe_psfdata_bins(result);
01479 
01480     if ((giraffe_psfdata_fibers(psfdata) != (cxsize) ns)) {
01481         return -3;
01482     }
01483 
01484     if ((giraffe_psfdata_xsize(psfdata) != (cxsize) ny) ||
01485         (giraffe_psfdata_ysize(psfdata) != (cxsize) nx)) {
01486         return -3;
01487     }
01488 
01489     binsize = nx / giraffe_psfdata_bins(psfdata);
01490 
01491 
01492     /*
01493      * Check the model type. Only the line models "psfexp", "psfexp2" and
01494      * "gaussian" are supported.
01495      */
01496 
01497      model = giraffe_model_get_name(profile);
01498 
01499      if (strcmp(model, "psfexp") == 0) {
01500          psfmodel = PROFILE_PSFEXP;
01501      }
01502      else if (strcmp(model, "psfexp2") == 0) {
01503          psfmodel = PROFILE_PSFEXP2;
01504      }
01505      else if (strcmp(model, "gaussian") == 0) {
01506          psfmodel = PROFILE_GAUSSIAN;
01507      }
01508      else {
01509          return -4;
01510      }
01511 
01512 
01513     /*
01514      * Allocate buffers for the profile data points and their errors. The
01515      * buffer size is choosen to be large enough for the number of bins of
01516      * requested psf data object and the given maximum fiber width.
01517      */
01518 
01519      mx = cpl_matrix_new(nbins * config->mwidth, 1);
01520      my = cpl_matrix_new(nbins * config->mwidth, 1);
01521      ms = cpl_matrix_new(nbins * config->mwidth, 1);
01522 
01523      if ((mx == NULL) || (my == NULL) || (ms == NULL)) {
01524 
01525          if (mx != NULL) {
01526              cpl_matrix_delete(mx);
01527              mx = NULL;
01528          }
01529 
01530          if (my != NULL) {
01531              cpl_matrix_delete(my);
01532              my = NULL;
01533          }
01534 
01535          if (ms != NULL) {
01536              cpl_matrix_delete(ms);
01537              ms = NULL;
01538          }
01539 
01540          return 1;
01541 
01542      }
01543 
01544 
01545     /*
01546      * Allocate the buffers of the results structure here, to avoid
01547      * complicated error handling in the nested loops.
01548      */
01549 
01550     giraffe_psfdata_set_model(result, giraffe_model_get_name(profile));
01551 
01552     for (n = 0; n < giraffe_model_count_parameters(profile); n++) {
01553 
01554         const cxchar* name = giraffe_model_parameter_name(profile, n);
01555 
01556         cpl_image* values = cpl_image_new(ns, nbins, CPL_TYPE_DOUBLE);
01557 
01558 
01559         if ((name == NULL) || (values == NULL)) {
01560 
01561             giraffe_psfdata_clear(result);
01562 
01563             cpl_matrix_delete(mx);
01564             mx = NULL;
01565 
01566             cpl_matrix_delete(my);
01567             my = NULL;
01568 
01569             cpl_matrix_delete(ms);
01570             ms = NULL;
01571 
01572             return 1;
01573 
01574         }
01575 
01576         giraffe_psfdata_set_data(result, name, values);
01577 
01578     }
01579 
01580 
01581     /*
01582      * Loop over all available fibers
01583      */
01584 
01585     for (fiber = 0; fiber < ns; fiber++) {
01586 
01587         cxint x = 0;
01588         const cxint* _bpm = NULL;
01589 
01590         const cxdouble* _zx = cpl_image_get_data_double(zraw);
01591         const cxdouble* _zv = cpl_image_get_data_double(zvar);
01592 
01593 
01594         if (bpm != NULL) {
01595             _bpm = cpl_image_get_data_int(bpm);
01596         }
01597 
01598 
01599         /*
01600          * Fit a profile for each bin
01601          */
01602 
01603         for (x = 0; x < nx; x++) {
01604 
01605             register cxint y = 0;
01606             register cxint m = x * ny;
01607             register cxint bin = CX_MAX(0, CX_MIN((cxint) floor(x / binsize),
01608                                         nbins));
01609 
01610             cxint status = 0;
01611             cxint ndata = 0;
01612             cxint iterations = giraffe_model_get_iterations(profile);
01613 
01614             cxdouble xcenter = 0.;
01615             cxdouble ycenter = 0.;
01616             cxdouble swidth = 0.;
01617             cxdouble ylo = 0.;
01618             cxdouble yup = ny;
01619             cxdouble amplitude = giraffe_psfdata_get(psfdata, "Amplitude",
01620                                                      fiber, bin);
01621             cxdouble bckground = giraffe_psfdata_get(psfdata, "Background",
01622                                                      fiber, bin);
01623             cxdouble center = giraffe_psfdata_get(psfdata, "Center",
01624                                                   fiber, bin);
01625             cxdouble width1 = giraffe_psfdata_get(psfdata, "Width1",
01626                                                   fiber, bin);
01627             cxdouble width2 = 0.;
01628 
01629 
01630             switch (psfmodel) {
01631                 case PROFILE_PSFEXP:
01632                     width2 = giraffe_psfdata_get(psfdata, "Width2",
01633                                                  fiber, bin);
01634                     swidth = pow(width1 * cutoff, 1./ width2);
01635                     break;
01636 
01637                 case PROFILE_PSFEXP2:
01638                     width2 = giraffe_psfdata_get(psfdata, "Width2",
01639                                                  fiber, bin);
01640                     swidth = width1 * pow(cutoff, 1./ width2);
01641                     break;
01642 
01643                 case PROFILE_GAUSSIAN:
01644                     swidth = width1 * pow(cutoff, 0.5);
01645                     break;
01646 
01647                 default:
01648                     break;
01649             }
01650 
01651             swidth = CX_MIN(swidth, config->mwidth);
01652 
01653             ylo = (cxint) floor(center - swidth);
01654             ylo = CX_MAX(0., ylo);
01655 
01656             yup = (cxint) ceil(center + swidth);
01657             yup = CX_MIN(ny, yup);
01658 
01659             if (_bpm == NULL) {
01660 
01661                 for (y = ylo; y < yup; y++) {
01662 
01663                     register cxint i = m + y;
01664 
01665                     cpl_matrix_set(mx, ndata, 0, (cxdouble)y - center);
01666                     cpl_matrix_set(my, ndata, 0, _zx[i]);
01667                     cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
01668 
01669                     ++ndata;
01670 
01671                 }
01672 
01673             }
01674             else {
01675 
01676                 for (y = ylo; y < yup; y++) {
01677 
01678                     register cxint i = m + y;
01679 
01680                     if (_bpm[i] == 0) {
01681                         cpl_matrix_set(mx, ndata, 0, (cxdouble)y - center);
01682                         cpl_matrix_set(my, ndata, 0, _zx[i]);
01683                         cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
01684 
01685                         ++ndata;
01686                     }
01687 
01688                 }
01689 
01690             }
01691 
01692 
01693             /*
01694              * Avoid negative background values
01695              */
01696 
01697             bckground = CX_MAX(0., bckground);
01698 
01699 
01700             /*
01701              * Setup model for this bin
01702              */
01703 
01704             giraffe_model_set_parameter(profile, "Amplitude", amplitude);
01705             giraffe_model_set_parameter(profile, "Center", 0.);
01706             giraffe_model_set_parameter(profile, "Background", bckground);
01707             giraffe_model_set_parameter(profile, "Width1", width1);
01708 
01709             switch (psfmodel) {
01710                 case PROFILE_PSFEXP:
01711                     giraffe_model_set_parameter(profile, "Width2", width2);
01712                     break;
01713 
01714                 case PROFILE_PSFEXP2:
01715                     giraffe_model_set_parameter(profile, "Width2", width2);
01716                     break;
01717 
01718                 case PROFILE_GAUSSIAN:
01719                     break;
01720 
01721                 default:
01722                     break;
01723             }
01724 
01725 
01726             /*
01727              * Fit the profile
01728              */
01729 
01730             status = giraffe_model_fit_sequence(profile, mx, my, ms,
01731                                                 ndata, 0, 1);
01732 
01733             amplitude = giraffe_model_get_parameter(profile, "Amplitude");
01734             bckground = giraffe_model_get_parameter(profile, "Background");
01735             ycenter = giraffe_model_get_parameter(profile, "Center");
01736             width1 = giraffe_model_get_parameter(profile, "Width1");
01737 
01738             if ((psfmodel == PROFILE_PSFEXP) ||
01739                 (psfmodel == PROFILE_PSFEXP2)) {
01740                 width2 = giraffe_model_get_parameter(profile, "Width2");
01741             }
01742 
01743 
01744             /*
01745              * Check fit results. The fit failed, if the maximum
01746              * number of iterations has been reached, fitted amplitude
01747              * is negative or the fitted width is negative.
01748              */
01749 
01750             if ((status != 0) ||
01751                 (giraffe_model_get_position(profile) >= iterations) ||
01752                 (amplitude <= 0.) ||
01753                 (width1 <= 0.)) {
01754 
01755                 xcenter = -1.;
01756                 ycenter = 0.;
01757                 amplitude = 0.;
01758                 bckground = 0.;
01759                 width1 = 0.;
01760                 width2 = 0.;
01761 
01762             }
01763             else {
01764                 xcenter = x;
01765             }
01766 
01767             giraffe_psfdata_set_bin(result, fiber, x, xcenter);
01768 
01769             giraffe_psfdata_set(result, "Amplitude", fiber, x, amplitude);
01770             giraffe_psfdata_set(result, "Center", fiber, x,
01771                                 ycenter + center);
01772             giraffe_psfdata_set(result, "Background", fiber, x, bckground);
01773             giraffe_psfdata_set(result, "Width1", fiber, x, width1);
01774 
01775             if ((psfmodel == PROFILE_PSFEXP) ||
01776                 (psfmodel == PROFILE_PSFEXP2)) {
01777                 giraffe_psfdata_set(result, "Width2", fiber, x, width2);
01778             }
01779 
01780         }
01781 
01782         ++nspectra;
01783 
01784     }
01785 
01786 
01787     /*
01788      * Cleanup
01789      */
01790 
01791     cpl_matrix_delete(mx);
01792     mx = NULL;
01793 
01794     cpl_matrix_delete(my);
01795     my = NULL;
01796 
01797     cpl_matrix_delete(ms);
01798     ms = NULL;
01799 
01800     return 0;
01801 
01802 }
01803 
01804 
01805 /*
01806  * @brief
01807  *  Compute a one dimensional fit of all PSF profile model parameters for
01808  *  the given grid.
01809  *
01810  * @param psfdata  PSF profile parameters to be fitted.
01811  * @param fibers   The list of fibers for which a PSF profile is available.
01812  * @param order    Order of the Chebyshev polynomial to fit.
01813  * @param setup    Sigma clipping algorithm configuration object.
01814  *
01815  * @return
01816  *  The function returns a newly created PSF data object containing the
01817  *  fitted parameters on success, or @c NULL if an error occurrs.
01818  *
01819  * TBD
01820  */
01821 
01822 inline static GiPsfData*
01823 _giraffe_psf_fit_parameters1d(const GiPsfData* psfdata,
01824                               const cpl_table* fibers,
01825                               const cxchar** names,
01826                               cxint order,
01827                               const GiClipParams* setup)
01828 {
01829 
01830     cxint i = 0;
01831     cxint ns = 0;
01832     cxint nx = 0;
01833     cxint ny = 0;
01834     cxint status = 0;
01835 
01836     GiPsfData* psffit = NULL;
01837 
01838 
01839     cx_assert(psfdata != NULL);
01840     cx_assert(fibers != NULL);
01841     cx_assert(setup != NULL);
01842 
01843     ns = giraffe_psfdata_fibers(psfdata);
01844     nx = giraffe_psfdata_ysize(psfdata);
01845     ny = giraffe_psfdata_xsize(psfdata);
01846 
01847     psffit = giraffe_psfdata_create(ns, nx, ny, nx);
01848 
01849     giraffe_psfdata_set_model(psffit, giraffe_psfdata_get_model(psfdata));
01850 
01851 
01852     /*
01853      * Create the bin data for the fitted PSF parameter data. This
01854      * is actually not needed, but makes psffit a complete, valid
01855      * PSF data object.
01856      */
01857 
01858     for (i = 0; i < ns; i++) {
01859 
01860         register cxint j = 0;
01861 
01862         for (j = 0; j < nx; j++) {
01863             giraffe_psfdata_set_bin(psffit, i, j, j);
01864         }
01865 
01866     }
01867 
01868 
01869     if (names == NULL) {
01870 
01871         cxsize j = 0;
01872         cxsize count = giraffe_psfdata_parameters(psfdata);
01873 
01874         for (j = 0; j < count; j++) {
01875 
01876             const cxchar* name = giraffe_psfdata_get_name(psfdata, j);
01877 
01878             GiPsfParameterFit pfit = {NULL, NULL};
01879 
01880 
01881             pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
01882             pfit.coeffs = cpl_matrix_new(order + 1, ns);
01883 
01884             status = _giraffe_psf_fit_profile1d(&pfit, psfdata, name,
01885                                                 fibers, order, setup);
01886 
01887             if (status != 0) {
01888                 cpl_matrix_delete(pfit.coeffs);
01889                 pfit.coeffs = NULL;
01890 
01891                 cpl_image_delete(pfit.fit);
01892                 pfit.fit = NULL;
01893 
01894                 giraffe_psfdata_delete(psffit);
01895                 psffit = NULL;
01896 
01897                 return NULL;
01898             }
01899             else {
01900                 giraffe_psfdata_set_data(psffit, name, pfit.fit);
01901                 pfit.fit = NULL;
01902 
01903                 cpl_matrix_delete(pfit.coeffs);
01904                 pfit.coeffs = NULL;
01905 
01906             }
01907 
01908         }
01909 
01910     }
01911     else {
01912 
01913         /*
01914          * For each PSF parameter, whose name is listed in name and present in
01915          * the PSF data object, a one dimensional polynomial model is created.
01916          */
01917 
01918         i = 0;
01919         while (names[i] != NULL) {
01920 
01921             if (giraffe_psfdata_contains(psfdata, names[i]) == TRUE) {
01922 
01923                 GiPsfParameterFit pfit = {NULL, NULL};
01924 
01925 
01926                 pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
01927                 pfit.coeffs = cpl_matrix_new(order + 1, ns);
01928 
01929                 status = _giraffe_psf_fit_profile1d(&pfit, psfdata, names[i],
01930                                                     fibers, order, setup);
01931 
01932                 if (status != 0) {
01933                     cpl_matrix_delete(pfit.coeffs);
01934                     pfit.coeffs = NULL;
01935 
01936                     cpl_image_delete(pfit.fit);
01937                     pfit.fit = NULL;
01938 
01939                     giraffe_psfdata_delete(psffit);
01940                     psffit = NULL;
01941 
01942                     return NULL;
01943                 }
01944                 else {
01945                     giraffe_psfdata_set_data(psffit, names[i], pfit.fit);
01946                     pfit.fit = NULL;
01947 
01948                     cpl_matrix_delete(pfit.coeffs);
01949                     pfit.coeffs = NULL;
01950 
01951                 }
01952 
01953             }
01954 
01955             ++i;
01956 
01957         }
01958 
01959     }
01960 
01961     return psffit;
01962 
01963 }
01964 
01965 
01966 /*
01967  * @brief
01968  *  Compute the fit of all PSF profile model parameters for the given grid.
01969  *
01970  * @param result  Container for the fitted PSF parameters.
01971  *
01972  * @return
01973  *  The function returns 0 on success, or a non-zero value otherwise.
01974  *
01975  * TBD
01976  */
01977 
01978 inline static GiPsfData*
01979 _giraffe_psf_fit_parameters(const GiPsfData* psfdata,
01980                             const cpl_table* fibers,
01981                             const cxchar** names,
01982                             cxint yorder, cxint worder,
01983                             const GiClipParams* setup)
01984 {
01985 
01986     const cxchar* center = NULL;
01987 
01988     cxint i = 0;
01989     cxint ns = 0;
01990     cxint nx = 0;
01991     cxint ny = 0;
01992     cxint status = 0;
01993 
01994     GiPsfData* psffit = NULL;
01995 
01996 
01997     cx_assert(psfdata != NULL);
01998     cx_assert(fibers != NULL);
01999     cx_assert(names != NULL);
02000     cx_assert(setup != NULL);
02001 
02002     ns = giraffe_psfdata_fibers(psfdata);
02003     nx = giraffe_psfdata_ysize(psfdata);
02004     ny = giraffe_psfdata_xsize(psfdata);
02005 
02006     psffit = giraffe_psfdata_create(ns, nx, ny, nx);
02007 
02008     giraffe_psfdata_set_model(psffit, giraffe_psfdata_get_model(psfdata));
02009 
02010 
02011     /*
02012      * Create the bin data for the fitted PSF parameter data. This
02013      * is actually not needed, but makes psffit a complete, valid
02014      * PSF data object.
02015      */
02016 
02017     for (i = 0; i < ns; i++) {
02018 
02019         register cxint j = 0;
02020 
02021         for (j = 0; j < nx; j++) {
02022             giraffe_psfdata_set_bin(psffit, i, j, j);
02023         }
02024 
02025     }
02026 
02027     center = names[0];
02028     if (giraffe_psfdata_contains(psfdata, center) == FALSE) {
02029 
02030         giraffe_psfdata_delete(psffit);
02031         psffit = NULL;
02032 
02033         return NULL;
02034 
02035     }
02036     else {
02037 
02038         GiPsfParameterFit pfit = {NULL, NULL};
02039 
02040 
02041         pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
02042         pfit.coeffs = cpl_matrix_new(yorder + 1, ns);
02043 
02044         status = _giraffe_psf_fit_profile1d(&pfit, psfdata, center, fibers,
02045                                             yorder, setup);
02046 
02047         if (status != 0) {
02048             cpl_matrix_delete(pfit.coeffs);
02049             pfit.coeffs = NULL;
02050 
02051             cpl_image_delete(pfit.fit);
02052             pfit.fit = NULL;
02053 
02054             giraffe_psfdata_delete(psffit);
02055             psffit = NULL;
02056 
02057             return NULL;
02058         }
02059         else {
02060             giraffe_psfdata_set_data(psffit, center, pfit.fit);
02061             pfit.fit = NULL;
02062 
02063             cpl_matrix_delete(pfit.coeffs);
02064             pfit.coeffs = NULL;
02065 
02066         }
02067 
02068     }
02069 
02070 
02071     i = 1;
02072     while (names[i] != NULL) {
02073 
02074         if (giraffe_psfdata_contains(psfdata, names[i]) == TRUE) {
02075 
02076             const cpl_image* xbin = giraffe_psfdata_get_bins(psfdata);
02077             const cpl_image* ybin = giraffe_psfdata_get_data(psfdata, center);
02078             const cpl_image* yfit = giraffe_psfdata_get_data(psffit, center);
02079             const cpl_image* pdata = giraffe_psfdata_get_data(psfdata,
02080                                                               names[i]);
02081 
02082             GiPsfParameterFit pfit = {NULL, NULL};
02083 
02084 
02085             pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
02086             pfit.coeffs = cpl_matrix_new(yorder + 1, worder + 1);
02087 
02088             status = _giraffe_psf_fit_profile2d(&pfit, fibers, pdata, xbin,
02089                                                 ybin, yorder, worder, yfit,
02090                                                 0, ny, setup);
02091 
02092             if (status != 0) {
02093                 cpl_matrix_delete(pfit.coeffs);
02094                 pfit.coeffs = NULL;
02095 
02096                 cpl_image_delete(pfit.fit);
02097                 pfit.fit = NULL;
02098 
02099                 giraffe_psfdata_delete(psffit);
02100                 psffit = NULL;
02101 
02102                 return NULL;
02103             }
02104             else {
02105                 giraffe_psfdata_set_data(psffit, names[i], pfit.fit);
02106                 pfit.fit = NULL;
02107 
02108                 cpl_matrix_delete(pfit.coeffs);
02109                 pfit.coeffs = NULL;
02110 
02111             }
02112         }
02113 
02114         ++i;
02115 
02116     }
02117 
02118     return psffit;
02119 
02120 }
02121 
02122 
02123 /*
02124  * @brief
02125  *  Compute a localization mask and coefficients from the PSF profile
02126  *  parameters.
02127  *
02128  * @return
02129  *  The function returns 0 on success, or a non-zero value otherwise.
02130  *
02131  * TBD
02132  */
02133 
02134 inline static int
02135 _giraffe_psf_compute_mask(GiMaskPosition* positions, GiMaskPosition* coeffs,
02136                           const GiPsfData* psfdata, const cpl_table* fibers,
02137                           cxint yorder, cxint worder,
02138                           const GiClipParams* setup)
02139 {
02140 
02141     const cxchar* const lcenter = "Center";
02142     const cxchar* const lwidth = "Width1";
02143     const cxchar* const lexponent = "Width2";
02144     const cxchar* model = NULL;
02145 
02146     cxint i = 0;
02147     cxint ns = 0;
02148     cxint nx = 0;
02149     cxint ny = 0;
02150     cxint status = 0;
02151 
02152     const cpl_image* xbin = NULL;
02153     const cpl_image* ybin = NULL;
02154     cpl_image* width = NULL;
02155 
02156     GiPsfParameterFit center = {NULL, NULL};
02157     GiPsfParameterFit halfwidth = {NULL, NULL};
02158 
02159     GiProfileId psfmodel = 0;
02160 
02161 
02162     cx_assert((positions != NULL) &&
02163               (positions->type == GIMASK_FITTED_DATA) &&
02164               (positions->my != NULL) &&
02165               (positions->mw != NULL));
02166     cx_assert((coeffs != NULL) &&
02167               (coeffs->type == GIMASK_FIT_COEFFS) &&
02168               (coeffs->my != NULL) &&
02169               (coeffs->mw != NULL));
02170     cx_assert(psfdata != NULL);
02171     cx_assert(fibers != NULL);
02172     cx_assert(setup != NULL);
02173 
02174     model = giraffe_psfdata_get_model(psfdata);
02175 
02176     if (strcmp(model, "psfexp") == 0) {
02177         psfmodel = PROFILE_PSFEXP;
02178     }
02179     else if (strcmp(model, "psfexp2") == 0) {
02180         psfmodel = PROFILE_PSFEXP2;
02181     }
02182     else if (strcmp(model, "gaussian") == 0) {
02183         psfmodel = PROFILE_GAUSSIAN;
02184     }
02185     else {
02186         return -1;
02187     }
02188 
02189     ns = giraffe_psfdata_fibers(psfdata);
02190     nx = giraffe_psfdata_ysize(psfdata);
02191     ny = giraffe_psfdata_xsize(psfdata);
02192 
02193     if ((cpl_matrix_get_nrow(positions->my) != nx) ||
02194         (cpl_matrix_get_ncol(positions->my) != ns) ||
02195         (cpl_matrix_get_nrow(positions->mw) != nx) ||
02196         (cpl_matrix_get_ncol(positions->mw) != ns)) {
02197             return -1;
02198     }
02199 
02200     if ((cpl_matrix_get_nrow(coeffs->my) != yorder + 1) ||
02201         (cpl_matrix_get_ncol(coeffs->my) != ns)) {
02202         return -1;
02203     }
02204 
02205     if ((cpl_matrix_get_nrow(coeffs->mw) != worder + 1) ||
02206         (cpl_matrix_get_ncol(coeffs->mw) != worder + 1)) {
02207             return -1;
02208     }
02209 
02210     if (giraffe_psfdata_contains(psfdata, lcenter) == FALSE ||
02211         giraffe_psfdata_contains(psfdata, lwidth) == FALSE) {
02212         return 1;
02213     }
02214 
02215     center.fit = cpl_image_wrap_double(ns, nx,
02216                                        cpl_matrix_get_data(positions->my));
02217     center.coeffs = coeffs->my;
02218 
02219     status = _giraffe_psf_fit_profile1d(&center, psfdata, lcenter, fibers,
02220                                         yorder, setup);
02221 
02222     if (status != 0) {
02223         cpl_image_unwrap(center.fit);
02224 
02225         center.fit = NULL;
02226         center.coeffs = NULL;
02227 
02228         return 1;
02229     }
02230 
02231     width = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
02232 
02233     switch (psfmodel) {
02234     case PROFILE_PSFEXP:
02235         {
02236 
02237             const cxdouble LOG2 = log(2.);
02238 
02239             if (giraffe_psfdata_contains(psfdata, lexponent) == FALSE) {
02240                 cpl_image_delete(width);
02241                 width = NULL;
02242 
02243                 cpl_image_unwrap(center.fit);
02244                 center.fit = NULL;
02245                 center.coeffs = NULL;
02246 
02247                 return 1;
02248             }
02249 
02250             for (i = 0; i < ns; i++) {
02251 
02252                 register cxint j = 0;
02253 
02254                 register cxdouble* _width = cpl_image_get_data_double(width);
02255 
02256 
02257                 for (j = 0; j < nx; j++) {
02258 
02259                     register cxint k = j * ns + i;
02260 
02261                     cxdouble width1 =
02262                         giraffe_psfdata_get(psfdata, lwidth, i, j);
02263                     cxdouble width2 =
02264                         giraffe_psfdata_get(psfdata, lexponent, i, j);
02265 
02266 
02267                     _width[k] = 2. * pow(LOG2 * width1, 1. / width2);
02268 
02269                 }
02270 
02271             }
02272 
02273         }
02274         break;
02275 
02276     case PROFILE_PSFEXP2:
02277         {
02278 
02279             const cxdouble LOG2 = log(2.);
02280 
02281             if (giraffe_psfdata_contains(psfdata, lexponent) == FALSE) {
02282                 cpl_image_delete(width);
02283                 width = NULL;
02284 
02285                 cpl_image_unwrap(center.fit);
02286                 center.fit = NULL;
02287                 center.coeffs = NULL;
02288 
02289                 return 1;
02290             }
02291 
02292             for (i = 0; i < ns; i++) {
02293 
02294                 register cxint j = 0;
02295 
02296                 register cxdouble* _width = cpl_image_get_data_double(width);
02297 
02298 
02299                 for (j = 0; j < nx; j++) {
02300 
02301                     register cxint k = j * ns + i;
02302 
02303                     cxdouble width1 =
02304                         giraffe_psfdata_get(psfdata, lwidth, i, j);
02305                     cxdouble width2 =
02306                         giraffe_psfdata_get(psfdata, lexponent, i, j);
02307 
02308 
02309                     _width[k] = 2. * pow(LOG2, 1. / width2) * width1;
02310 
02311                 }
02312 
02313             }
02314 
02315         }
02316         break;
02317 
02318     case PROFILE_GAUSSIAN:
02319         {
02320 
02321             const cxdouble fwhmscale = 4. * sqrt(2. * log(2.));
02322 
02323             for (i = 0; i < ns; i++) {
02324 
02325                 register cxint j = 0;
02326 
02327                 register cxdouble* _width = cpl_image_get_data_double(width);
02328 
02329 
02330                 for (j = 0; j < nx; j++) {
02331 
02332                     register cxint k = j * ns + i;
02333 
02334                     _width[k] = fwhmscale *
02335                         giraffe_psfdata_get(psfdata, lwidth, i, j);
02336                 }
02337 
02338             }
02339 
02340         }
02341         break;
02342 
02343     default:
02344         /* This point should never be reached! */
02345 
02346         cpl_image_delete(width);
02347         width = NULL;
02348 
02349         cpl_image_unwrap(center.fit);
02350         center.fit = NULL;
02351         center.coeffs = NULL;
02352 
02353         gi_error("Unsupported PSF profile model encountered!");
02354         break;
02355     }
02356 
02357 
02358     xbin = giraffe_psfdata_get_bins(psfdata);
02359     ybin = giraffe_psfdata_get_data(psfdata, lcenter);
02360 
02361 
02362     halfwidth.fit = cpl_image_wrap_double(ns, nx,
02363                                           cpl_matrix_get_data(positions->mw));
02364     halfwidth.coeffs = coeffs->mw;
02365 
02366     status = _giraffe_psf_fit_profile2d(&halfwidth, fibers, width, xbin,
02367                                         ybin, worder, worder, center.fit,
02368                                         0, ny, setup);
02369 
02370     if (status != 0) {
02371         cpl_image_unwrap(halfwidth.fit);
02372         halfwidth.fit = NULL;
02373         halfwidth.coeffs = NULL;
02374 
02375         cpl_image_delete(width);
02376         width = NULL;
02377 
02378         cpl_image_unwrap(center.fit);
02379         center.fit = NULL;
02380         center.coeffs = NULL;
02381 
02382         return 1;
02383     }
02384 
02385     cpl_image_unwrap(halfwidth.fit);
02386     halfwidth.fit = NULL;
02387     halfwidth.coeffs = NULL;
02388 
02389     cpl_image_delete(width);
02390     width = NULL;
02391 
02392     cpl_image_unwrap(center.fit);
02393     center.fit = NULL;
02394     center.coeffs = NULL;
02395 
02396     return 0;
02397 
02398 }
02399 
02400 
02401 inline static cpl_image*
02402 _giraffe_psf_simulate_mask(const GiPsfData* psfdata,
02403                            const cpl_image* amplitude,
02404                            const cpl_image* background,
02405                            cxdouble cutoff)
02406 {
02407 
02408     const cxchar* model = NULL;
02409 
02410     cxint i = 0;
02411     cxint nfibers = 0;
02412     cxint nbins = 0;
02413     cxint _nbins = 0;
02414     cxint nx = 0;
02415     cxint ny = 0;
02416     cxint fiber = 0;
02417 
02418     cxdouble bsize = 1.;
02419     cxdouble _bsize = 1.;
02420     cxdouble* _mask = NULL;
02421 
02422     const cpl_image* center = NULL;
02423     const cpl_image* width = NULL;
02424     const cpl_image* exponent = NULL;
02425 
02426     cpl_image* mask = NULL;
02427 
02428     GiModel* profile = NULL;
02429 
02430     GiProfileId psfmodel = 0;
02431 
02432 
02433     cx_assert(psfdata != NULL);
02434 
02435     model = giraffe_psfdata_get_model(psfdata);
02436 
02437     if (strcmp(model, "psfexp") == 0) {
02438         psfmodel = PROFILE_PSFEXP;
02439     }
02440     else if (strcmp(model, "psfexp2") == 0) {
02441         psfmodel = PROFILE_PSFEXP2;
02442     }
02443     else if (strcmp(model, "gaussian") == 0) {
02444         psfmodel = PROFILE_GAUSSIAN;
02445     }
02446     else {
02447         return NULL;
02448     }
02449 
02450     nfibers = giraffe_psfdata_fibers(psfdata);
02451     nbins = giraffe_psfdata_bins(psfdata);
02452     nx = giraffe_psfdata_ysize(psfdata);
02453     ny = giraffe_psfdata_xsize(psfdata);
02454 
02455     center = giraffe_psfdata_get_data(psfdata, "Center");
02456     width = giraffe_psfdata_get_data(psfdata, "Width1");
02457     exponent = giraffe_psfdata_get_data(psfdata, "Width2");
02458 
02459     if (amplitude == NULL) {
02460         amplitude = giraffe_psfdata_get_data(psfdata, "Amplitude");
02461     }
02462     else {
02463         if ((cpl_image_get_size_x(amplitude) != nfibers) ||
02464             (cpl_image_get_size_y(amplitude) > nbins)) {
02465                 return NULL;
02466         }
02467     }
02468 
02469     if (background == NULL) {
02470         background = giraffe_psfdata_get_data(psfdata, "Background");
02471     }
02472     else {
02473         if ((cpl_image_get_size_x(background) != nfibers) ||
02474             (cpl_image_get_size_y(background) > nbins)) {
02475                 return NULL;
02476         }
02477     }
02478 
02479     bsize = (cxdouble)nx / (cxdouble)nbins;
02480 
02481     _nbins = cpl_image_get_size_y(amplitude);
02482     _bsize = (cxdouble)nx / (cxdouble)_nbins;
02483 
02484     mask = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
02485     _mask = cpl_image_get_data_double(mask);
02486 
02487     profile = giraffe_model_new(model);
02488 
02489     for (fiber = 0; fiber < nfibers; fiber++) {
02490 
02491         cxint ylower = 0;
02492         cxint yupper = ny;
02493 
02494         const cxdouble* _amplitude =
02495             cpl_image_get_data_double_const(amplitude);
02496         const cxdouble* _center =
02497             cpl_image_get_data_double_const(center);
02498         const cxdouble* _width =
02499             cpl_image_get_data_double_const(width);
02500         const cxdouble* _exponent =
02501             cpl_image_get_data_double_const(exponent);
02502 
02503         for (i = 0; i < nx; i++) {
02504 
02505             register cxint j = 0;
02506             register cxint k = 0;
02507             register cxint l = 0;
02508             register cxint bin = 0;
02509 
02510             register cxdouble a = 1.;
02511             register cxdouble b = 0.;
02512             register cxdouble c = 0.;
02513             register cxdouble s = 0.;
02514             register cxdouble e = 0.;
02515 
02516 
02517             bin = CX_MAX(0, CX_MIN((cxint)floor(i / bsize), nbins - 1));
02518             k = bin * nfibers + fiber;
02519 
02520             bin = CX_MAX(0, CX_MIN((cxint)floor(i / _bsize), _nbins - 1));
02521             l = bin * nfibers + fiber;
02522 
02523             a = _amplitude[l];
02524             c = _center[k];
02525             s = _width[k];
02526             e = _exponent[k];
02527 
02528             giraffe_model_set_parameter(profile, "Amplitude", a);
02529             giraffe_model_set_parameter(profile, "Background", b);
02530             giraffe_model_set_parameter(profile, "Center", c);
02531             giraffe_model_set_parameter(profile, "Width1", s);
02532             giraffe_model_set_parameter(profile, "Width2", e);
02533 
02534             switch (psfmodel) {
02535             case PROFILE_PSFEXP:
02536                 {
02537                     cxdouble w = pow(s * log(1. / cutoff), 1. / e);
02538 
02539                     ylower = (cxint) floor(c - w);
02540                     yupper = (cxint) ceil(c + w);
02541                 }
02542                 break;
02543 
02544             case PROFILE_PSFEXP2:
02545                 {
02546                     cxdouble w = s * pow(log(1. / cutoff), 1. / e);
02547 
02548                     ylower = (cxint) floor(c - w);
02549                     yupper = (cxint) ceil(c + w);
02550                 }
02551                 break;
02552 
02553             case PROFILE_GAUSSIAN:
02554                 {
02555                     cxdouble w = s * sqrt(log(1. / cutoff));
02556                     ylower = (cxint) floor(c - w);
02557                     yupper = (cxint) ceil(c + w);
02558                 }
02559                 break;
02560 
02561             default:
02562                 gi_error("Unsupported PSF profile model encountered!");
02563                 break;
02564             }
02565 
02566             ylower = CX_MAX(0, ylower);
02567             yupper = CX_MIN(ny, yupper);
02568 
02569             for (j = ylower; j < yupper; j++) {
02570 
02571                 cxint status = 0;
02572 
02573                 cxdouble value = 0.;
02574 
02575                 // FIXME: Performance problem? Check this!
02576                 //register cxdouble value =
02577                 //    a * exp(-pow(fabs(j - c) / s, e)) + b;
02578 
02579                 giraffe_model_set_argument(profile, "x", j);
02580                 giraffe_model_evaluate(profile, &value, &status);
02581 
02582                 _mask[i * ny + j] += value;
02583 
02584             }
02585 
02586         }
02587 
02588     }
02589 
02590     giraffe_model_delete(profile);
02591     profile = NULL;
02592 
02593     return mask;
02594 
02595 }
02596 
02597 
02608 cxint
02609 giraffe_compute_fiber_profiles(GiLocalization* result, GiImage* image,
02610                                GiTable* fibers, GiLocalization* master,
02611                                GiImage* bpixel, GiPsfConfig* config)
02612 {
02613 
02614     const cxchar* const _func = "giraffe_compute_fiber_profiles";
02615 
02616     cxint i = 0;
02617     cxint status = 0;
02618     cxint nfibers = 0;
02619     cxint nframes = 1;
02620     cxint nbins = 0;
02621     cxint nx = 0;
02622     cxint ny = 0;
02623 
02624     cxdouble conad      = 1.;
02625     cxdouble bias_ron   = 0.;
02626     cxdouble bias_sigma = 0.;
02627     cxdouble dark_value = 0.;
02628 
02629     cx_string* s = NULL;
02630 
02631     cpl_table* _fibers = NULL;
02632     cpl_table* locc = NULL;
02633 
02634     cpl_matrix* my = NULL;
02635 
02636     cpl_image* _image = NULL;
02637     cpl_image* _variance = NULL;
02638     cpl_image* _locy = NULL;
02639     cpl_image* _locw = NULL;
02640     cpl_image* _bpixel = NULL;
02641 
02642     cpl_propertylist* properties = NULL;
02643 
02644     GiModel* psfmodel = NULL;
02645 
02646     GiPsfData* psfdata = NULL;
02647     GiPsfData* psffit = NULL;
02648 
02649     GiMaskPosition positions = {GIMASK_FITTED_DATA, NULL, NULL};
02650     GiMaskPosition coeffs = {GIMASK_FIT_COEFFS, NULL, NULL};
02651 
02652     GiPsfParams psf_setup = {0, 0, 1000., FALSE};
02653 
02654 
02655     if ((result == NULL) || (image == NULL) || (fibers == NULL) ||
02656         (master == NULL) || (config == NULL)) {
02657         cpl_error_set(_func, CPL_ERROR_NULL_INPUT);
02658         return 1;
02659     }
02660 
02661     if ((master->locy == NULL) || (master->locw == NULL)) {
02662         cpl_error_set(_func, CPL_ERROR_NULL_INPUT);
02663         return 1;
02664     }
02665 
02666     if ((result->locy != NULL) || (result->locw != NULL) ||
02667         (result->locc != NULL) || (result->psf != NULL)) {
02668         cpl_error_set(_func, CPL_ERROR_ILLEGAL_INPUT);
02669         return 1;
02670     }
02671 
02672     _image = giraffe_image_get(image);
02673     _locy = giraffe_image_get(master->locy);
02674     _locw = giraffe_image_get(master->locw);
02675 
02676     if (bpixel != NULL) {
02677         _bpixel = giraffe_image_get(bpixel);
02678     }
02679 
02680     _fibers = giraffe_table_get(fibers);
02681 
02682     if (_fibers == NULL) {
02683         cpl_error_set(_func, CPL_ERROR_DATA_NOT_FOUND);
02684         return 1;
02685     }
02686 
02687     nfibers = cpl_table_get_nrow(_fibers);
02688 
02689     nx = cpl_image_get_size_y(_image);
02690     ny = cpl_image_get_size_x(_image);
02691 
02692     nbins = (cxint) ceil(nx / config->binsize);
02693 
02694 
02695     /*
02696      * Get raw image properties.
02697      */
02698 
02699     properties = giraffe_image_get_properties(image);
02700 
02701     if (cpl_propertylist_has(properties, GIALIAS_NFIBERS) == FALSE) {
02702         cpl_propertylist_append_int(properties, GIALIAS_NFIBERS, nfibers);
02703         cpl_propertylist_set_comment(properties, GIALIAS_NFIBERS,
02704                                      "Number of fibres");
02705     }
02706     else {
02707 
02708         cxint _nfibers = cpl_propertylist_get_int(properties,
02709                                                   GIALIAS_NFIBERS);
02710 
02711         if (nfibers != _nfibers) {
02712             cpl_error_set(_func, CPL_ERROR_ILLEGAL_INPUT);
02713             return 1;
02714         }
02715 
02716     }
02717 
02718 
02719     if (!cpl_propertylist_has(properties, GIALIAS_CONAD)) {
02720         cpl_msg_error(_func, "Missing detector gain property (%s)! ",
02721                       GIALIAS_CONAD);
02722         return 1;
02723     }
02724     else {
02725         conad = cpl_propertylist_get_double(properties, GIALIAS_CONAD);
02726     }
02727 
02728 
02729     if (!cpl_propertylist_has(properties, GIALIAS_BIASERROR)) {
02730         cpl_msg_warning(_func, "Missing bias error property (%s)! Setting "
02731                         "bias error to 0.", GIALIAS_BIASERROR);
02732         bias_sigma = 0.;
02733     }
02734     else {
02735         bias_sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASERROR);
02736     }
02737 
02738 
02739     giraffe_error_push();
02740 
02741     bias_ron = giraffe_propertylist_get_ron(properties);
02742 
02743     if (cpl_error_get_code() != CPL_ERROR_NONE) {
02744         return 1;
02745     }
02746 
02747     giraffe_error_pop();
02748 
02749 
02750     if (cpl_propertylist_has(properties, GIALIAS_DARKVALUE) == FALSE) {
02751         cpl_msg_warning(_func, "Missing dark value property (%s) will be "
02752                         "set to %.2f!", GIALIAS_DARKVALUE, dark_value);
02753         cpl_propertylist_append_double(properties, GIALIAS_DARKVALUE,
02754                                        dark_value);
02755     }
02756     else {
02757         dark_value = cpl_propertylist_get_double(properties,
02758                                                  GIALIAS_DARKVALUE);
02759     }
02760 
02761 
02762     if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
02763         nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
02764     }
02765 
02766 
02767     /*
02768      * Convert the bias and dark errors from ADU to electrons.
02769      */
02770 
02771     bias_sigma *= conad;
02772     dark_value *= conad;
02773 
02774 
02775     /*
02776      * Prepare the input image and the variance image for the profile fitting.
02777      */
02778 
02779     giraffe_error_push();
02780 
02781     _image = cpl_image_multiply_scalar_create(_image, nframes * conad);
02782     _variance = cpl_image_abs_create(_image);
02783 
02784     cpl_image_add_scalar(_variance,
02785                          nframes * (bias_ron * bias_ron + nframes *
02786                          (bias_sigma * bias_sigma + dark_value * dark_value)));
02787 
02788     if (cpl_error_get_code() != CPL_ERROR_NONE) {
02789         if (_variance != NULL) {
02790             cpl_image_delete(_variance);
02791             _variance = NULL;
02792         }
02793 
02794         cpl_image_delete(_image);
02795         _image = NULL;
02796 
02797         return 1;
02798     }
02799 
02800     giraffe_error_pop();
02801 
02802 
02803     /*
02804      * Initialize PSF profile model.
02805      */
02806 
02807     psfmodel = giraffe_model_new(config->profile);
02808 
02809     giraffe_model_thaw(psfmodel);
02810 
02811     giraffe_model_set_parameter(psfmodel, "Amplitude", 1.);
02812     giraffe_model_set_parameter(psfmodel, "Background", 0.);
02813     giraffe_model_set_parameter(psfmodel, "Center", 0.);
02814     giraffe_model_set_parameter(psfmodel, "Width1", config->width);
02815 
02816     if (cx_strncasecmp(config->profile, "psfexp", 6) == 0) {
02817 
02818         cxdouble _exponent = fabs(config->exponent);
02819 
02820         giraffe_model_set_parameter(psfmodel, "Width2", _exponent);
02821 
02822         if (config->exponent > 0) {
02823             giraffe_model_freeze_parameter(psfmodel, "Width2");
02824         }
02825 
02826     }
02827 
02828     giraffe_model_set_iterations(psfmodel, config->fit.iterations);
02829     giraffe_model_set_delta(psfmodel, config->fit.delta);
02830     giraffe_model_set_tests(psfmodel, config->fit.tests);
02831 
02832 
02833     /*
02834      * Fit a PSF profile model to each fiber and compute the profile
02835      * parameters.
02836      */
02837 
02838     cpl_msg_info(_func, "Fitting fiber profiles ...");
02839 
02840     psf_setup.bsize = config->binsize;
02841     psf_setup.mwidth = config->maxwidth;
02842     psf_setup.normalize = config->normalize;
02843 
02844     psfdata = giraffe_psfdata_create(nfibers, nbins, ny, nx);
02845 
02846     status = _giraffe_psf_compute_profile(psfdata, _image, _variance, _locy,
02847                                           _locw, _fibers, _bpixel, psfmodel,
02848                                           &psf_setup);
02849 
02850     cpl_image_delete(_image);
02851     _image = NULL;
02852 
02853     if (status != 0) {
02854         giraffe_psfdata_delete(psfdata);
02855         psfdata= NULL;
02856 
02857         giraffe_model_delete(psfmodel);
02858         psfmodel = NULL;
02859 
02860         cpl_image_delete(_variance);
02861         _variance = NULL;
02862 
02863         cpl_msg_error(_func, "Fiber profile fit failed!");
02864 
02865         return 2;
02866     }
02867 
02868 
02869     /*
02870      * Scale the computed profiles to the level of the actual, average
02871      * input frame.
02872      */
02873 
02874     _image = (cpl_image*) giraffe_psfdata_get_data(psfdata, "Amplitude");
02875     cpl_image_divide_scalar(_image, nframes * conad);
02876 
02877     _image = (cpl_image*) giraffe_psfdata_get_data(psfdata, "Background");
02878     cpl_image_divide_scalar(_image, nframes * conad);
02879 
02880     _image = NULL;
02881 
02882 
02883     /*
02884      * Fit a polynomial model to each PSF profile parameter if
02885      * it was requested.
02886      */
02887 
02888     cpl_msg_info(_func, "Fitting PSF profile parameters ...");
02889 
02890     if (config->parameter_fit == TRUE) {
02891 
02892         const cxchar* parameters[] = {"Center", "Amplitude", "Background",
02893                 "Width1", "Width2", NULL};
02894 
02895         psffit = _giraffe_psf_fit_parameters(psfdata, _fibers, parameters,
02896                                              config->yorder, config->worder,
02897                                              &config->clip);
02898 
02899     }
02900     else {
02901 
02902         psffit = _giraffe_psf_fit_parameters1d(psfdata, _fibers,
02903                                                NULL, config->yorder,
02904                                                &config->clip);
02905 
02906     }
02907 
02908     if (psffit == NULL) {
02909         giraffe_psfdata_delete(psfdata);
02910         psfdata= NULL;
02911 
02912         giraffe_model_delete(psfmodel);
02913         psfmodel = NULL;
02914 
02915         cpl_image_delete(_variance);
02916         _variance = NULL;
02917 
02918         cpl_msg_error(_func, "PSF parameter fit failed!");
02919         return 3;
02920     }
02921 
02922     giraffe_model_delete(psfmodel);
02923     psfmodel = NULL;
02924 
02925     cpl_image_delete(_variance);
02926     _variance = NULL;
02927 
02928 
02929     /*
02930      * Compute a fiber localization mask from the fitted fiber profiles.
02931      */
02932 
02933     positions.my = cpl_matrix_new(nx, nfibers);
02934     positions.mw = cpl_matrix_new(nx, nfibers);
02935 
02936     coeffs.my = cpl_matrix_new(config->yorder + 1, nfibers);
02937     coeffs.mw = cpl_matrix_new(config->worder + 1, config->worder + 1);
02938 
02939     status = _giraffe_psf_compute_mask(&positions, &coeffs, psfdata, _fibers,
02940                                        config->yorder, config->worder,
02941                                        &config->clip);
02942 
02943     if (status != 0) {
02944 
02945         giraffe_psfdata_delete(psffit);
02946         psffit = NULL;
02947 
02948         giraffe_psfdata_delete(psfdata);
02949         psfdata= NULL;
02950 
02951         cpl_msg_error(_func, "Computation of localization mask from "
02952                       "the fiber profile failed!");
02953 
02954         return 4;
02955     }
02956 
02957     giraffe_psfdata_delete(psfdata);
02958     psfdata= NULL;
02959 
02960 
02961     /*
02962      * Fill the results object. Convert the matrices to images and tables
02963      * and add the necessary properties.
02964      */
02965 
02966     properties = giraffe_image_get_properties(image);
02967 
02968     cpl_propertylist_update_string(properties, GIALIAS_PSFMODEL,
02969                                    config->profile);
02970     cpl_propertylist_set_comment(properties, GIALIAS_PSFMODEL,
02971                                  "PSF profile model identifier");
02972 
02973     cpl_propertylist_update_int(properties, GIALIAS_PSFXBINS,
02974                                 config->binsize);
02975     cpl_propertylist_set_comment(properties, GIALIAS_PSFXBINS,
02976                                  "Size of bins along the dispersion "
02977                                  "direction.");
02978 
02979     cpl_propertylist_update_int(properties, GIALIAS_PSFYDEG,
02980                                 config->yorder);
02981     cpl_propertylist_set_comment(properties, GIALIAS_PSFYDEG,
02982                                  "Order of the fiber center polynomial "
02983                                  "model.");
02984 
02985     cpl_propertylist_update_int(properties, GIALIAS_PSFWDEG,
02986                                 config->worder);
02987     cpl_propertylist_set_comment(properties, GIALIAS_PSFWDEG,
02988                                  "Order of the fiber width 2d polynomial "
02989                                  "model.");
02990 
02991     cpl_propertylist_update_int(properties, GIALIAS_PSFWDEG,
02992                                 config->worder);
02993     cpl_propertylist_set_comment(properties, GIALIAS_PSFWDEG,
02994                                  "Order of the fiber width 2d polynomial "
02995                                  "model.");
02996 
02997     cpl_propertylist_update_bool(properties, GIALIAS_PSFNORM,
02998                                  config->normalize);
02999     cpl_propertylist_set_comment(properties, GIALIAS_PSFNORM,
03000                                  "Pixel value normalization.");
03001 
03002     cpl_propertylist_update_int(properties, GIALIAS_PSFNX,
03003                                 cpl_matrix_get_nrow(positions.my));
03004     cpl_propertylist_set_comment(properties, GIALIAS_PSFNX,
03005                                  "Number of pixels per spectrum.");
03006 
03007     cpl_propertylist_update_int(properties, GIALIAS_PSFNS,
03008                                 cpl_matrix_get_ncol(positions.my));
03009     cpl_propertylist_set_comment(properties, GIALIAS_PSFNS,
03010                                  "Number of detected fibers.");
03011 
03012     cpl_propertylist_update_double(properties, GIALIAS_PSFSIGMA,
03013                                    config->clip.level);
03014     cpl_propertylist_set_comment(properties, GIALIAS_PSFSIGMA,
03015                                  "Sigma multiplier used for the PSF "
03016                                  "parmeter fit.");
03017 
03018     cpl_propertylist_update_double(properties, GIALIAS_PSFSIGMA,
03019                                    config->clip.level);
03020     cpl_propertylist_set_comment(properties, GIALIAS_PSFSIGMA,
03021                                  "Sigma multiplier used for the fit of PSF "
03022                                  "parameters.");
03023 
03024     cpl_propertylist_update_int(properties, GIALIAS_PSFNITER,
03025                                    config->clip.iterations);
03026     cpl_propertylist_set_comment(properties, GIALIAS_PSFNITER,
03027                                  "Number of iterations used for the fit "
03028                                  "of PSF parameters.");
03029 
03030     cpl_propertylist_update_double(properties, GIALIAS_PSFMFRAC,
03031                                    config->clip.fraction);
03032     cpl_propertylist_set_comment(properties, GIALIAS_PSFMFRAC,
03033                                  "Minimum allowed fraction of accepted "
03034                                  "over total data points used for the "
03035                                  "fit of PSF parameters.");
03036 
03037 
03038     /* Fiber profile center position */
03039 
03040     result->locy = giraffe_image_create(CPL_TYPE_DOUBLE,
03041                                         cpl_matrix_get_ncol(positions.my),
03042                                         cpl_matrix_get_nrow(positions.my));
03043     giraffe_image_copy_matrix(result->locy, positions.my);
03044 
03045     cpl_matrix_delete(positions.my);
03046     positions.my = NULL;
03047 
03048     giraffe_image_set_properties(result->locy, properties);
03049     properties = giraffe_image_get_properties(result->locy);
03050 
03051     cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE, "LOCY");
03052     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
03053                                  "GIRAFFE localization centroid");
03054 
03055 
03056     /* Fiber profile widths */
03057 
03058     result->locw = giraffe_image_create(CPL_TYPE_DOUBLE,
03059                                         cpl_matrix_get_ncol(positions.mw),
03060                                         cpl_matrix_get_nrow(positions.mw));
03061     giraffe_image_copy_matrix(result->locw, positions.mw);
03062 
03063     cpl_matrix_delete(positions.mw);
03064     positions.mw = NULL;
03065 
03066     properties = giraffe_image_get_properties(result->locy);
03067 
03068     giraffe_image_set_properties(result->locw, properties);
03069     properties = giraffe_image_get_properties(result->locw);
03070 
03071     cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE, "LOCWY");
03072     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
03073                                  "GIRAFFE localization half-width");
03074 
03075 
03076     /* Fiber polynomial model coefficients table */
03077 
03078     locc = cpl_table_new(cpl_matrix_get_ncol(coeffs.my));
03079 
03080     cpl_table_new_column(locc, "BUTTON", CPL_TYPE_INT);
03081     for (i = 0; i < cpl_table_get_nrow(locc); i++) {
03082         cpl_table_set_int(locc, "BUTTON", i, i);
03083     }
03084 
03085     for (i = 0; i < cpl_matrix_get_nrow(coeffs.my); i++) {
03086 
03087         cxchar* label = NULL;
03088 
03089         cx_asprintf(&label, "YC%d", i);
03090         cpl_table_new_column(locc, label, CPL_TYPE_DOUBLE);
03091         cx_free(label);
03092 
03093     }
03094 
03095     properties = giraffe_image_get_properties(result->locy);
03096 
03097     result->locc = giraffe_table_create(locc, properties);
03098     properties = giraffe_table_get_properties(result->locc);
03099 
03100     cpl_table_delete(locc);
03101     locc = NULL;
03102 
03103     my = cpl_matrix_transpose_create(coeffs.my);
03104     giraffe_table_copy_matrix(result->locc, "YC0", my);
03105 
03106     cpl_matrix_delete(my);
03107     my = NULL;
03108 
03109     cpl_matrix_delete(coeffs.my);
03110     coeffs.my = NULL;
03111 
03112 
03113     /* Add coefficients of the 2D fit of the fiber widths as properties */
03114 
03115     s = cx_string_new();
03116 
03117     for (i = 0; i < cpl_matrix_get_ncol(coeffs.mw); i++) {
03118         cx_string_sprintf(s, "%s%d", GIALIAS_LOCWIDCOEF, i);
03119         cpl_propertylist_update_double(properties, cx_string_get(s),
03120                                        cpl_matrix_get(coeffs.mw, 0, i));
03121     }
03122 
03123     cx_string_delete(s);
03124     s = NULL;
03125 
03126     cpl_matrix_delete(coeffs.mw);
03127     coeffs.mw = NULL;
03128 
03129     cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
03130                                    "LOCYWCHEB");
03131     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
03132                                  "GIRAFFE localization fit coefficients");
03133 
03134 
03135     /* Fiber profile PSF parameters */
03136 
03137     if (psffit != NULL) {
03138         result->psf = psffit;
03139     }
03140 
03141     return 0;
03142 
03143 }
03144 
03145 
03156 GiPsfConfig*
03157 giraffe_psf_config_create(cpl_parameterlist* list)
03158 {
03159 
03160     cpl_parameter *p;
03161 
03162     GiPsfConfig *self = NULL;
03163 
03164 
03165     if (list == NULL) {
03166         return NULL;
03167     }
03168 
03169     self = cx_calloc(1, sizeof *self);
03170 
03171     p = cpl_parameterlist_find(list, "giraffe.psf.model");
03172     self->profile = cx_strdup(cpl_parameter_get_string(p));
03173 
03174     if (cx_strncasecmp(self->profile, "psfexp", 6) == 0) {
03175         self->width = 16.;
03176     }
03177     else {
03178         self->width = 4.;
03179     }
03180 
03181     p = cpl_parameterlist_find(list, "giraffe.psf.binsize");
03182     self->binsize = cpl_parameter_get_int(p);
03183 
03184     if (self->binsize < 1) {
03185         self->binsize = 1;
03186     }
03187 
03188     p = cpl_parameterlist_find(list, "giraffe.psf.maxwidth");
03189     self->maxwidth = cpl_parameter_get_double(p);
03190 
03191     if (self->width > 0.) {
03192         p = cpl_parameterlist_find(list, "giraffe.psf.width");
03193         self->width = cpl_parameter_get_double(p);
03194     }
03195 
03196     if (self->width > self->maxwidth) {
03197         self->width = self->maxwidth;
03198     }
03199 
03200     p = cpl_parameterlist_find(list, "giraffe.psf.exponent");
03201     self->exponent = cpl_parameter_get_double(p);
03202 
03203     p = cpl_parameterlist_find(list, "giraffe.psf.normalize");
03204     self->normalize = cpl_parameter_get_bool(p);
03205 
03206     p = cpl_parameterlist_find(list, "giraffe.psf.profile.iterations");
03207     self->fit.iterations = cpl_parameter_get_int(p);
03208 
03209     p = cpl_parameterlist_find(list, "giraffe.psf.profile.tests");
03210     self->fit.tests = cpl_parameter_get_int(p);
03211 
03212     p = cpl_parameterlist_find(list, "giraffe.psf.profile.dchisquare");
03213     self->fit.delta = cpl_parameter_get_double(p);
03214 
03215     p = cpl_parameterlist_find(list, "giraffe.psf.parameters.fit");
03216     self->parameter_fit = cpl_parameter_get_bool(p);
03217 
03218     p = cpl_parameterlist_find(list, "giraffe.psf.parameters.yorder");
03219     self->yorder = cpl_parameter_get_int(p);
03220 
03221     if (self->yorder < 0) {
03222         giraffe_psf_config_destroy(self);
03223         self = NULL;
03224     }
03225 
03226     p = cpl_parameterlist_find(list, "giraffe.psf.parameters.worder");
03227     self->worder = cpl_parameter_get_int(p);
03228 
03229     if (self->worder < 0) {
03230         giraffe_psf_config_destroy(self);
03231         self = NULL;
03232     }
03233 
03234     p = cpl_parameterlist_find(list, "giraffe.psf.parameters.sigma");
03235     self->clip.level = cpl_parameter_get_double(p);
03236 
03237     p = cpl_parameterlist_find(list, "giraffe.psf.parameters.iterations");
03238     self->clip.iterations = cpl_parameter_get_int(p);
03239 
03240     p = cpl_parameterlist_find(list, "giraffe.psf.parameters.fraction");
03241     self->clip.fraction = cpl_parameter_get_double(p);
03242 
03243     return self;
03244 
03245 }
03246 
03247 
03260 void
03261 giraffe_psf_config_destroy(GiPsfConfig* self)
03262 {
03263 
03264     if (self != NULL) {
03265         if (self->profile != NULL) {
03266             cx_free((cxptr) self->profile);
03267             self->profile = NULL;
03268         }
03269 
03270         cx_free(self);
03271         self = NULL;
03272     }
03273 
03274     return;
03275 
03276 }
03277 
03278 
03290 void
03291 giraffe_psf_config_add(cpl_parameterlist* list)
03292 {
03293 
03294     cpl_parameter* p = NULL;
03295 
03296 
03297     if (list == NULL) {
03298         return;
03299     }
03300 
03301     p = cpl_parameter_new_enum("giraffe.psf.model",
03302                                CPL_TYPE_STRING,
03303                                "PSF profile model: `psfexp', `psfexp2'",
03304                                "giraffe.psf",
03305                                "psfexp2", 3, "psfexp", "psfexp2", "gaussian");
03306     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-model");
03307     cpl_parameterlist_append(list, p);
03308 
03309     p = cpl_parameter_new_value("giraffe.psf.normalize",
03310                                 CPL_TYPE_BOOL,
03311                                 "Use normalized pixel values.",
03312                                 "giraffe.psf",
03313                                 FALSE);
03314     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-norm");
03315     cpl_parameterlist_append(list, p);
03316 
03317 
03318     p = cpl_parameter_new_value("giraffe.psf.binsize",
03319                                 CPL_TYPE_INT,
03320                                 "Size of bin along dispersion axis",
03321                                 "giraffe.psf",
03322                                 64);
03323     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-binsize");
03324     cpl_parameterlist_append(list, p);
03325 
03326 
03327     p = cpl_parameter_new_value("giraffe.psf.maxwidth",
03328                                 CPL_TYPE_DOUBLE,
03329                                 "Maximum width of the PSF profile.",
03330                                 "giraffe.psf",
03331                                 16.);
03332     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-maxwidth");
03333     cpl_parameterlist_append(list, p);
03334 
03335 
03336     p = cpl_parameter_new_value("giraffe.psf.width",
03337                                 CPL_TYPE_DOUBLE,
03338                                 "Initial width of the PSF profile.",
03339                                 "giraffe.psf",
03340                                 0.);
03341     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-width");
03342     cpl_parameterlist_append(list, p);
03343 
03344 
03345     p = cpl_parameter_new_value("giraffe.psf.exponent",
03346                                 CPL_TYPE_DOUBLE,
03347                                 "Exponent of the exponential PSF profile "
03348                                 "(will not be fitted if > 0).",
03349                                 "giraffe.psf",
03350                                 -3.);
03351     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-exponent");
03352     cpl_parameterlist_append(list, p);
03353 
03354 
03355     p = cpl_parameter_new_value("giraffe.psf.profile.iterations",
03356                                 CPL_TYPE_INT,
03357                                 "Maximum number of iterations used for "
03358                                 "the fit of the fiber PSF profile.",
03359                                 "giraffe.psf",
03360                                 120);
03361 
03362     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfniter");
03363     cpl_parameterlist_append(list, p);
03364 
03365 
03366     p = cpl_parameter_new_value("giraffe.psf.profile.tests",
03367                                 CPL_TYPE_INT,
03368                                 "Maximum number of tests used for the fit "
03369                                 "of the fiber PSF profile",
03370                                 "giraffe.psf",
03371                                 7);
03372 
03373     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfntest");
03374     cpl_parameterlist_append(list, p);
03375 
03376 
03377     p = cpl_parameter_new_value("giraffe.psf.profile.dchisquare",
03378                                 CPL_TYPE_DOUBLE,
03379                                 "Minimum chi-square difference used for the "
03380                                 "fit of the fiber PSF profile.",
03381                                 "giraffe.psf",
03382                                 0.001);
03383 
03384     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfdchisq");
03385     cpl_parameterlist_append(list, p);
03386 
03387 
03388     p = cpl_parameter_new_value("giraffe.psf.parameters.fit",
03389                                 CPL_TYPE_BOOL,
03390                                 "2D fit of the PSF profile parameters "
03391                                 "using a Chebyshev polynomial model.",
03392                                 "giraffe.psf",
03393                                 FALSE);
03394     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-prmfit");
03395     cpl_parameterlist_append(list, p);
03396 
03397 
03398     p = cpl_parameter_new_value("giraffe.psf.parameters.yorder",
03399                                 CPL_TYPE_INT,
03400                                 "Order of Chebyshev polynomial fit.",
03401                                 "giraffe.psf",
03402                                 4);
03403     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-yorder");
03404     cpl_parameterlist_append(list, p);
03405 
03406 
03407     p = cpl_parameter_new_value("giraffe.psf.parameters.worder",
03408                                 CPL_TYPE_INT,
03409                                 "Order of Chebyshev 2D polynomial fit.",
03410                                 "giraffe.psf",
03411                                 4);
03412     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-worder");
03413     cpl_parameterlist_append(list, p);
03414 
03415 
03416     p = cpl_parameter_new_value("giraffe.psf.parameters.sigma",
03417                                 CPL_TYPE_DOUBLE,
03418                                 "PSF parameter fitting: sigma threshold "
03419                                 "factor",
03420                                 "giraffe.psf",
03421                                 3.5);
03422     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-sigma");
03423     cpl_parameterlist_append(list, p);
03424 
03425 
03426     p = cpl_parameter_new_value("giraffe.psf.parameters.iterations",
03427                                 CPL_TYPE_INT,
03428                                 "PSF parameter fitting: number of "
03429                                 "iterations",
03430                                 "giraffe.psf",
03431                                 10);
03432     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-niter");
03433     cpl_parameterlist_append(list, p);
03434 
03435 
03436     p = cpl_parameter_new_range("giraffe.psf.parameters.fraction",
03437                                 CPL_TYPE_DOUBLE,
03438                                 "PSF parameter fitting: minimum fraction "
03439                                 "of points accepted/total.",
03440                                 "giraffe.psf",
03441                                 0.8, 0.0, 1.0);
03442     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-mfrac");
03443     cpl_parameterlist_append(list, p);
03444 
03445     return;
03446 
03447 }

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:28 2012 by doxygen 1.6.3 written by Dimitri van Heesch, © 1997-2004