GIRAFFE Pipeline Reference Manual

giextract.c

00001 /* $Id: giextract.c,v 1.41 2013/04/23 13:14:47 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: 2013/04/23 13:14:47 $
00024  * $Revision: 1.41 $
00025  * $Name: giraffe-2_11 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #  include <config.h>
00030 #endif
00031 
00032 #include <math.h>
00033 #include <float.h>
00034 
00035 #include <cxmemory.h>
00036 #include <cxstring.h>
00037 #include <cxstrutils.h>
00038 
00039 #include <cpl_parameterlist.h>
00040 #include <cpl_matrix.h>
00041 #include <cpl_table.h>
00042 #include <cpl_msg.h>
00043 
00044 #include "gimacros.h"
00045 #include "gialias.h"
00046 #include "giclip.h"
00047 #include "giarray.h"
00048 #include "giimage.h"
00049 #include "gimatrix.h"
00050 #include "giwindow.h"
00051 #include "gipsfdata.h"
00052 #include "gimodel.h"
00053 #include "gimath.h"
00054 #include "gilocalization.h"
00055 #include "gimessages.h"
00056 #include "gifiberutils.h"
00057 #include "giutils.h"
00058 #include "giextract.h"
00059 
00060 
00069 enum GiProfileId {
00070     PROFILE_PSFEXP   = 1 << 1,
00071     PROFILE_PSFEXP2  = 1 << 2,
00072     PROFILE_GAUSSIAN = 1 << 3
00073 };
00074 
00075 typedef enum GiProfileId GiProfileId;
00076 
00077 
00078 /*
00079  * Optimal spectrum extraction algorithm configuration data
00080  */
00081 
00082 struct GiExtractOptimalConfig {
00083 
00084     GiClipParams clip;
00085 
00086     cxbool limits;
00087 
00088     cxint bkgorder;
00089 
00090     cxdouble exptime;
00091     cxdouble ron;
00092     cxdouble dark;
00093     cxdouble ewidth;
00094 };
00095 
00096 typedef struct GiExtractOptimalConfig GiExtractOptimalConfig;
00097 
00098 
00099 /*
00100  * Original Horne spectrum extraction algorithm configuration data
00101  */
00102 
00103 struct GiExtractHorneConfig {
00104     GiClipParams clip;
00105 
00106     cxdouble exptime;
00107     cxdouble ron;
00108     cxdouble dark;
00109     cxdouble ewidth;
00110 };
00111 
00112 typedef struct GiExtractHorneConfig GiExtractHorneConfig;
00113 
00114 
00115 struct GiExtractionData {
00116     cxdouble value;
00117     cxdouble error;
00118     cxdouble position;
00119     cxdouble npixels;
00120 };
00121 
00122 typedef struct GiExtractionData GiExtractionData;
00123 
00124 
00125 struct GiExtractionSlice {
00126     cxint fsize;
00127     cxint msize;
00128 
00129     cxint nflx;
00130     cxint nbkg;
00131 
00132     cpl_matrix* flux;
00133     cpl_matrix* variance;
00134     cpl_matrix* model;
00135 };
00136 
00137 typedef struct GiExtractionSlice GiExtractionSlice;
00138 
00139 
00140 struct GiExtractionPsfLimits {
00141     cxint size;
00142 
00143     cxint* ymin;
00144     cxint* ymax;
00145 };
00146 
00147 typedef struct GiExtractionPsfLimits GiExtractionPsfLimits;
00148 
00149 
00150 struct GiExtractionWorkspace {
00151     cpl_matrix* atw;
00152     cpl_matrix* atwa;
00153     cpl_matrix* atws;
00154     cpl_matrix* c;
00155     cpl_matrix* tmp;
00156 };
00157 
00158 typedef struct GiExtractionWorkspace GiExtractionWorkspace;
00159 
00160 
00161 struct GiVirtualSlit {
00162     cxint width;
00163 
00164     cxdouble center;
00165     cxdouble extra_width;
00166 
00167     cxdouble* position;
00168     cxdouble* signal;
00169     cxdouble* variance;
00170     cxdouble* fraction;
00171 
00172     cxint* mask;
00173     cxint* offset;
00174 };
00175 
00176 typedef struct GiVirtualSlit GiVirtualSlit;
00177 
00178 
00179 /*
00180  * Extraction slice implementation
00181  */
00182 
00183 inline static GiExtractionSlice*
00184 _giraffe_extractionslice_new(cxint nflx, cxint ndata, cxint nbkg)
00185 {
00186 
00187     GiExtractionSlice* self = cx_malloc(sizeof *self);
00188 
00189     self->nflx = nflx;
00190     self->nbkg = nbkg;
00191 
00192     self->fsize = nflx + nbkg;
00193     self->msize = ndata;
00194 
00195     self->flux = cpl_matrix_new(self->fsize, 1);
00196     self->variance = cpl_matrix_new(self->fsize, 1);
00197     self->model = cpl_matrix_new(self->msize, 1);
00198 
00199     return self;
00200 
00201 }
00202 
00203 
00204 inline static void
00205 _giraffe_extractionslice_delete(GiExtractionSlice* self)
00206 {
00207 
00208     if (self != NULL) {
00209         if (self->model != NULL) {
00210             cpl_matrix_delete(self->model);
00211             self->model = NULL;
00212         }
00213 
00214         if (self->variance != NULL) {
00215             cpl_matrix_delete(self->variance);
00216             self->variance = NULL;
00217         }
00218 
00219         if (self->flux != NULL) {
00220             cpl_matrix_delete(self->flux);
00221             self->flux = NULL;
00222         }
00223 
00224         cx_free(self);
00225     }
00226 
00227     return;
00228 
00229 }
00230 
00231 
00232 inline static GiExtractionPsfLimits*
00233 _giraffe_extraction_psflimits_new(cxint size)
00234 {
00235 
00236     GiExtractionPsfLimits* self = cx_malloc(sizeof *self);
00237 
00238     self->size = size;
00239 
00240     self->ymin = cx_calloc(self->size, sizeof(cxint));
00241     self->ymax = cx_calloc(self->size, sizeof(cxint));
00242 
00243     return self;
00244 
00245 }
00246 
00247 
00248 inline static void
00249 _giraffe_extraction_psflimits_delete(GiExtractionPsfLimits* self)
00250 {
00251 
00252     if (self != NULL) {
00253         if (self->ymin != NULL) {
00254             cx_free(self->ymin);
00255         }
00256 
00257         if (self->ymax != NULL) {
00258             cx_free(self->ymax);
00259         }
00260 
00261         cx_free(self);
00262     }
00263 
00264     return;
00265 
00266 }
00267 
00268 
00269 inline static GiExtractionWorkspace*
00270 _giraffe_optimal_workspace_new(cxint m, cxint n)
00271 {
00272 
00273     GiExtractionWorkspace* self = cx_malloc(sizeof *self);
00274 
00275 
00276     self->atw  = cpl_matrix_new(m, n);
00277     self->atwa = cpl_matrix_new(m, m);
00278     self->c    = cpl_matrix_new(m, m);
00279     self->atws = cpl_matrix_new(m, 1);
00280 
00281     self->tmp  = cpl_matrix_new(m, m);
00282 
00283     return self;
00284 
00285 }
00286 
00287 
00288 inline static void
00289 _giraffe_optimal_workspace_delete(GiExtractionWorkspace* self)
00290 {
00291 
00292     if (self != NULL) {
00293         if (self->atws != NULL) {
00294             cpl_matrix_delete(self->atws);
00295         }
00296 
00297         if (self->atwa != NULL) {
00298             cpl_matrix_delete(self->atwa);
00299         }
00300 
00301         if (self->c != NULL) {
00302             cpl_matrix_delete(self->c);
00303         }
00304 
00305         if (self->atw != NULL) {
00306             cpl_matrix_delete(self->atw);
00307         }
00308 
00309         if (self->tmp != NULL) {
00310             cpl_matrix_delete(self->tmp);
00311         }
00312 
00313         cx_free(self);
00314 
00315     }
00316 
00317     return;
00318 
00319 }
00320 
00321 
00322 /*
00323  * Virtual slit implementation
00324  */
00325 
00326 inline static void
00327 _giraffe_virtualslit_allocate(GiVirtualSlit* self)
00328 {
00329 
00330     if ((self != NULL) && (self->width > 0)) {
00331 
00332         self->position = cx_calloc(self->width, sizeof(cxdouble));
00333         self->signal = cx_calloc(self->width, sizeof(cxdouble));
00334         self->variance = cx_calloc(self->width, sizeof(cxdouble));
00335         self->fraction = cx_calloc(self->width, sizeof(cxdouble));
00336 
00337         self->mask = cx_calloc(self->width, sizeof(cxdouble));
00338         self->offset = cx_calloc(self->width, sizeof(cxdouble));
00339 
00340     }
00341 
00342     return;
00343 
00344 }
00345 
00346 
00347 inline static GiVirtualSlit*
00348 _giraffe_virtualslit_new(cxdouble extra_width)
00349 {
00350 
00351     GiVirtualSlit* self = cx_calloc(1, sizeof *self);
00352 
00353     self->width = 0;
00354     self->center = 0.;
00355     self->extra_width = extra_width;
00356 
00357     self->position = NULL;
00358     self->signal = NULL;
00359     self->variance = NULL;
00360     self->fraction = NULL;
00361     self->mask = NULL;
00362     self->offset = NULL;
00363 
00364     return self;
00365 
00366 }
00367 
00368 
00369 inline static void
00370 _giraffe_virtualslit_clear(GiVirtualSlit* self)
00371 {
00372 
00373     if (self != NULL) {
00374 
00375         if (self->position != NULL) {
00376             cx_free(self->position);
00377             self->position = NULL;
00378         }
00379 
00380         if (self->signal != NULL) {
00381             cx_free(self->signal);
00382             self->signal = NULL;
00383         }
00384 
00385         if (self->variance != NULL) {
00386             cx_free(self->variance);
00387             self->variance = NULL;
00388         }
00389 
00390         if (self->fraction != NULL) {
00391             cx_free(self->fraction);
00392             self->fraction = NULL;
00393         }
00394 
00395         if (self->mask != NULL) {
00396             cx_free(self->mask);
00397             self->mask = NULL;
00398         }
00399 
00400         if (self->offset != NULL) {
00401             cx_free(self->offset);
00402             self->offset = NULL;
00403         }
00404 
00405         self->extra_width = 0.;
00406         self->center = 0.;
00407         self->width = 0;
00408 
00409     }
00410 
00411     return;
00412 
00413 }
00414 
00415 
00416 inline static void
00417 _giraffe_virtualslit_delete(GiVirtualSlit* self)
00418 {
00419 
00420     if (self != NULL) {
00421         _giraffe_virtualslit_clear(self);
00422 
00423         cx_free(self);
00424     }
00425 
00426     return;
00427 
00428 }
00429 
00430 
00431 inline static cxint
00432 _giraffe_virtualslit_setup(GiVirtualSlit* self, cxint bin,
00433                            cxdouble center, cxdouble width,
00434                            const cpl_image* signal, const cpl_image* variance,
00435                            const cpl_image* bpixel)
00436 {
00437 
00438     register cxint ny = cpl_image_get_size_x(signal);
00439     register cxint offset = bin * cpl_image_get_size_x(signal);
00440 
00441     register cxdouble lower = center - (width + self->extra_width);
00442     register cxdouble upper = center + (width + self->extra_width);
00443 
00444     register cxint first = (cxint) floor(lower);
00445     register cxint last = (cxint) ceil(upper);
00446 
00447     const cxdouble* s = cpl_image_get_data_double_const(signal);
00448     const cxdouble* v = cpl_image_get_data_double_const(variance);
00449 
00450 
00451     /*
00452      * Upper, lower border and width of the virtual slit
00453      */
00454 
00455     lower = CX_MAX(0., lower);
00456     upper = CX_MIN(ny, upper);
00457 
00458     first = CX_MAX(0, first);
00459     last = CX_MIN(ny, last);
00460 
00461     self->center = center;
00462     self->width = last - first + 1;
00463 
00464 
00465     /*
00466      * Create and fill the buffers
00467      */
00468 
00469     _giraffe_virtualslit_allocate(self);
00470 
00471     if (bpixel != NULL) {
00472 
00473         register cxint k = 0;
00474         register cxint y = 0;
00475 
00476         const cxint* _bpixel = cpl_image_get_data_int_const(bpixel);
00477 
00478 
00479         for (y = first; y <= last; y++) {
00480 
00481             register cxint ypos = offset + y;
00482 
00483             cxint ok = (_bpixel[ypos] & GIR_M_PIX_SET) == 0 ? 1 : 0;
00484 
00485 
00486             self->position[k] = y - center;
00487             self->fraction[k]  = 1.;
00488 
00489             self->signal[k] = s[ypos];
00490             self->variance[k] = v[ypos];
00491 
00492             self->mask[k] = ok;
00493             self->offset[k] = ypos;
00494             ++k;
00495 
00496         }
00497 
00498     }
00499     else {
00500 
00501         register cxint k = 0;
00502         register cxint y = 0;
00503 
00504 
00505         for (y = first; y <= last; y++) {
00506 
00507             register cxint ypos = offset + y;
00508 
00509             cxint ok = 1;
00510 
00511 
00512             self->position[k] = y - center;
00513             self->fraction[k]  = 1.;
00514 
00515             self->signal[k] = s[ypos];
00516             self->variance[k] = v[ypos];
00517 
00518             self->mask[k] = ok;
00519             self->offset[k] = ypos;
00520             ++k;
00521 
00522         }
00523 
00524     }
00525 
00526 
00527     /*
00528      * Correct for pixel fractions at the borders of the
00529      * virtual slit, since they have been set to the full
00530      * pixel in the above loop.
00531      */
00532 
00533     self->fraction[0] = ((cxdouble)first + 1.) - lower;
00534     self->fraction[self->width - 1] = upper - ((cxdouble)last - 1.);
00535 
00536     return self->width;
00537 
00538 }
00539 
00540 
00541 /*
00542  * Compute the inverse of a square matrix
00543  */
00544 
00545 inline static cxint
00546 _giraffe_matrix_invert(cpl_matrix* m_inv, const cpl_matrix* m, cpl_matrix* lu)
00547 {
00548 
00549     cxint i = 0;
00550     cxint status = 0;
00551     cxint n = cpl_matrix_get_ncol(m);
00552 
00553     register cxint sz = n * n * sizeof(cxdouble);
00554 
00555     const cxdouble* _m = cpl_matrix_get_data_const(m);
00556 
00557     cxdouble* _m_inv = cpl_matrix_get_data(m_inv);
00558     cxdouble* _m_lu = cpl_matrix_get_data(lu);
00559 
00560     cpl_array* perm = cpl_array_new(n, CPL_TYPE_INT);
00561 
00562     register cxint* perm_data = cpl_array_get_data_int(perm);
00563 
00564 
00565     memset(_m_inv, 0, sz);
00566     memcpy(_m_lu, _m, sz);
00567 
00568     if (cpl_matrix_decomp_lu(lu, perm, &i) != 0) {
00569         cpl_array_delete(perm);
00570         return 1;
00571     }
00572 
00573 
00574     /*
00575      * Create an identity matrix with the rows permuted
00576      */
00577 
00578     for (i = 0; i < n; ++i) {
00579         _m_inv[i * n + perm_data[i]] = 1.;
00580     }
00581 
00582     cpl_array_delete(perm);
00583 
00584 
00585     status = cpl_matrix_solve_lu(lu, m_inv, NULL);
00586 
00587     if (status != 0) {
00588         cpl_matrix_delete(m_inv);
00589         return 2;
00590     }
00591 
00592     return 0;
00593 
00594 }
00595 
00596 
00597 /*
00598  * Compute the PSF profile for a set of abscissa values.
00599  */
00600 
00601 inline static cpl_matrix*
00602 _giraffe_compute_psf(GiModel* psf, const cpl_matrix* x)
00603 {
00604 
00605     register cxint i = 0;
00606     register cxint n = 0;
00607 
00608     cxint status = 0;
00609 
00610     const cxdouble* _x = NULL;
00611 
00612     cxdouble* _y = NULL;
00613 
00614     cpl_matrix* y = NULL;
00615 
00616     cx_assert(psf != NULL);
00617     cx_assert(x != NULL);
00618     cx_assert(cpl_matrix_get_ncol(x) == 1);
00619 
00620     n = cpl_matrix_get_nrow(x);
00621 
00622     y = cpl_matrix_new(n, 1);
00623 
00624     _x = cpl_matrix_get_data_const(x);
00625     _y = cpl_matrix_get_data(y);
00626 
00627     for (i = 0; i < n; i++) {
00628         giraffe_model_set_argument(psf, "x", _x[i]);
00629         giraffe_model_evaluate(psf, &_y[i], &status);
00630 
00631         if (status != 0) {
00632             cpl_matrix_delete(y);
00633             return NULL;
00634         }
00635     }
00636 
00637     return y;
00638 
00639 }
00640 
00641 
00642 /*
00643  * Horne extraction of a single wavelength bin for the given virtual
00644  * slit.
00645  */
00646 
00647 inline static cxint
00648 _giraffe_horne_extract_slit(GiExtractionData* result,
00649                             const GiVirtualSlit* vslit, GiModel* psf,
00650                             const GiExtractHorneConfig* config)
00651 {
00652 
00653     cxint i = 0;
00654     cxint ngood = 0;
00655 
00656     cxdouble var = 0.;
00657     cxdouble bkg = 0.;
00658     cxdouble flx = 0.;
00659     cxdouble norm = 0.;
00660     cxdouble* tdata = NULL;
00661     cxdouble* _mnpsf = NULL;
00662 
00663     cpl_matrix* mnpsf = NULL;
00664     cpl_matrix* mvslit = NULL;
00665 
00666 
00667 
00668     /*
00669      * Compute the PSF model.
00670      */
00671 
00672     mvslit = cpl_matrix_wrap(vslit->width, 1, vslit->position);
00673     mnpsf = _giraffe_compute_psf(psf, mvslit);
00674 
00675     cpl_matrix_unwrap(mvslit);
00676     mvslit = NULL;
00677 
00678     if (mnpsf == NULL) {
00679         return -1;
00680     }
00681 
00682 
00683     /*
00684      * Enforce positivity and normalization of the profile model.
00685      */
00686 
00687     _mnpsf = cpl_matrix_get_data(mnpsf);
00688 
00689     norm = 0.;
00690 
00691     for (i = 0; i < vslit->width; ++i) {
00692         _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
00693         norm += _mnpsf[i];
00694     }
00695 
00696     for (i = 0; i < vslit->width; ++i) {
00697         _mnpsf[i] /= norm;
00698     }
00699 
00700 
00701     /*
00702      * Estimate background and determine the number of valid pixels
00703      */
00704 
00705     tdata = cx_malloc(vslit->width * sizeof(cxdouble));
00706 
00707     i = 0;
00708     ngood = 0;
00709 
00710     while (i < vslit->width) {
00711         if (vslit->mask[i] > 0) {
00712             tdata[ngood] = CX_MAX(vslit->signal[i], 0.);
00713             ++ngood;
00714         }
00715         ++i;
00716     }
00717 
00718     if (ngood > 1) {
00719         giraffe_array_sort(tdata, ngood);
00720         bkg = 0.5 * (tdata[0] + tdata[1]);
00721     }
00722 
00723     cx_free(tdata);
00724     tdata = NULL;
00725 
00726 
00727     /*
00728      * Try extraction only if there are good pixels available. If no good
00729      * pixels are left skip this spectral bin and set the flux and the variance
00730      * to zero.
00731      */
00732 
00733     if (ngood > 0) {
00734 
00735         cxint iteration = 0;
00736         cxint nreject = -1;
00737         cxint niter = config->clip.iterations;
00738         cxint nmin = (cxint)config->clip.fraction;
00739 
00740         cxdouble sigma = config->clip.level * config->clip.level;
00741         cxdouble* variance = NULL;
00742 
00743 
00744         /*
00745          * Compute standard extraction flux and rescale it to account for
00746          * bad pixels.
00747          */
00748 
00749         norm = 0.;
00750         flx = 0.;
00751 
00752         for (i = 0; i < vslit->width; ++i) {
00753             if (vslit->mask[i] != 0) {
00754                 flx += (vslit->signal[i] - bkg) * vslit->fraction[i];
00755                 norm += vslit->fraction[i] * _mnpsf[i];
00756             }
00757         }
00758 
00759         flx /= norm;
00760 
00761 
00762         /*
00763          * Allocate buffer for the variance estimates and compute the initial
00764          * variances from the expected profile.
00765          */
00766 
00767         variance = cx_calloc(vslit->width, sizeof(cxdouble));
00768 
00769         for (i = 0; i < vslit->width; ++i) {
00770 
00771             register cxdouble ve = flx * _mnpsf[i] + bkg;
00772 
00773             variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
00774 
00775         }
00776 
00777 
00778         /*
00779          * Reject cosmics and extract spectrum
00780          */
00781 
00782         nreject = -1;
00783 
00784         while ((iteration < niter) && (ngood > nmin) && (nreject != 0)) {
00785 
00786             cxint imax = 0;
00787 
00788             cxdouble _flx = 0.;
00789             cxdouble mmax = 0.;
00790 
00791 
00792             norm = 0.;
00793             var = 0.;
00794             nreject = 0;
00795 
00796 
00797             /*
00798              * Reject cosmics
00799              */
00800 
00801             for (i = 0; i < vslit->width; ++i) {
00802 
00803                 if (vslit->mask[i] != 0) {
00804 
00805                     cxdouble m = vslit->signal[i] - bkg - flx * _mnpsf[i];
00806 
00807                     m *= vslit->fraction[i];
00808                     m *= m / variance[i] ;
00809 
00810                     if (m > mmax) {
00811                         mmax = m;
00812                         imax = i;
00813                     }
00814 
00815                 }
00816 
00817             }
00818 
00819             if ((sigma > 0.) && (mmax > sigma)) {
00820                 vslit->mask[imax] = 0;
00821                 ++nreject;
00822                 --ngood;
00823             }
00824 
00825 
00826             /*
00827              * Compute flux and variance estimates.
00828              */
00829 
00830             for (i = 0; i < vslit->width; ++i) {
00831 
00832                 if (vslit->mask[i] != 0) {
00833 
00834                     register cxdouble data = vslit->signal[i] - bkg;
00835                     register cxdouble p    = _mnpsf[i];
00836 
00837                     data *= vslit->fraction[i];
00838                     p    *= vslit->fraction[i];
00839 
00840                     norm += p * p / variance[i];
00841                     _flx += p * data / variance[i];
00842                     var  += p;
00843 
00844                 }
00845 
00846             }
00847 
00848             flx = _flx / norm;
00849             var /= norm;
00850 
00851 
00852             /*
00853              * Update variance estimates
00854              */
00855 
00856             for (i = 0; i < vslit->width; ++i) {
00857 
00858                 register cxdouble ve = flx * _mnpsf[i] + bkg;
00859 
00860                 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
00861 
00862             }
00863 
00864             ++iteration;
00865 
00866         }
00867 
00868         cx_free(variance);
00869         variance = NULL;
00870 
00871     }
00872 
00873     cpl_matrix_delete(mnpsf);
00874     mnpsf = NULL;
00875 
00876     result->value = flx;
00877     result->error = sqrt(var);
00878     result->position = vslit->center;
00879     result->npixels = ngood;
00880 
00881     return ngood == 0 ? 1 : 0;
00882 
00883 }
00884 
00885 
00886 /*
00887  * @brief
00888  *   Compute the optimal extracted flux and its variance for a single
00889  *   wavelength bin.
00890  *
00891  * @param slice   The results container to store the flux, variance and
00892  *                extraction model.
00893  * @param AT      The transposed design matrix of the linear system.
00894  * @param S       Column vector of the measured signal.
00895  * @param W       Matrix of weights of the measured signals.
00896  * @param limits  Cutoff parameters.
00897  * @param ws      Workspace for the matrix operations.
00898  *
00899  * @return
00900  *   The function returns 0 on success, and a non-zero value if an error
00901  *   occurred.
00902  *
00903  * The functions computes the optimal extracted fluxes for a single wavelength
00904  * bin by solving the linear system:
00905  * @f[
00906  *   \mathbf{f}\left(x\right) =
00907  *       \left(\mathbf{A}^\mathrm{T}\mathbf{W}\mathbf{A}\right)^{-1}
00908  *       \mathbf{A}^\mathrm{T}\mathbf{W}\mathbf{s}
00909  * @f]
00910  * where the @f$\mathbf{s}@f$ is the column vector of the measured fluxes
00911  * written as an @f$\left(n \times 1\right)@f$ matrix, @f$\mathbf{W}@f$ is the
00912  * diagonal @f$\left(n \times n\right)@f$ matrix of the weights, and
00913  * @f$\mathbf{A}^\mathrm{T}@f$ is the transposed of the
00914  * @f$\left(n \times m\right)@f$ design matrix @f$\mathbf{A}@f$.
00915  *
00916  * Defining the matrix @f$\mathbf{C} = \left(c_\mathrm{ij}\right) \equiv
00917  * \left(\mathbf{A}^\mathrm{T}\mathbf{W}\mathbf{A}\right)^{-1}@f$, and using
00918  * @f$a_\mathrm{ij}@f$ and @f$w_\mathrm{ij}@f$ to denote the elements of the
00919  * transposed design matrix and the weight matrix respectively, the extracted
00920  * flux can be written as the following sum:
00921  * @f[
00922  *   f_\mathrm{i} = \sum\limits_{\mathrm{l} = 0}^{\mathrm{m} - 1} c_\mathrm{il}
00923  *                  \sum\limits_{\mathrm{k} = 0}^{\mathrm{n} - 1} a_\mathrm{lk}
00924  *                    w_\mathrm{kk} s_\mathrm{k}
00925  * @f]
00926  *
00927  */
00928 
00929 inline static cxint
00930 _giraffe_optimal_extract_slice(GiExtractionSlice* slice,
00931                                const cpl_matrix* AT,
00932                                const cpl_matrix* S,
00933                                const cpl_matrix* W,
00934                                GiExtractionPsfLimits* limits,
00935                                GiExtractionWorkspace* ws)
00936 {
00937 
00938     register cxint i = 0;
00939     register cxint n = cpl_matrix_get_ncol(AT);
00940     register cxint m = cpl_matrix_get_nrow(AT);
00941 
00942     cxint status = 0;
00943 
00944     const cxdouble* at = cpl_matrix_get_data_const(AT);
00945     const cxdouble* w = cpl_matrix_get_data_const(W);
00946     const cxdouble* s = cpl_matrix_get_data_const(S);
00947     const cxdouble* c = cpl_matrix_get_data_const(ws->c);
00948 
00949     cxdouble* atw = cpl_matrix_get_data(ws->atw);
00950     cxdouble* atwa = cpl_matrix_get_data(ws->atwa);
00951     cxdouble* atws = cpl_matrix_get_data(ws->atws);
00952     cxdouble* sf = cpl_matrix_get_data(slice->flux);
00953     cxdouble* sv = cpl_matrix_get_data(slice->variance);
00954     cxdouble* sm = cpl_matrix_get_data(slice->model);
00955 
00956 
00957     for (i = 0; i < m; ++i) {
00958 
00959         register cxint j = 0;
00960         register cxint im = i * m;
00961         register cxint in = i * n;
00962         register cxint ymin = limits->ymin[i];
00963         register cxint ymax = limits->ymax[i];
00964 
00965 
00966         atws[i] = 0.;
00967 
00968         for (j = 0; j < n; ++j) {
00969 
00970             register cxint k = in + j;
00971 
00972 
00973             atw[k] = w[j] * at[k];
00974             atws[i] += atw[k] * s[j];
00975 
00976         }
00977 
00978         for (j = 0; j < i; ++j) {
00979 
00980             register cxint k = 0;
00981             register cxint l = im + j;
00982 
00983             atwa[l] = 0.;
00984             for (k = ymin; k < ymax; ++k) {
00985                 atwa[l] += atw[in + k] * at[j * n + k];
00986             }
00987 
00988             atwa[j * m + i] = atwa[l];
00989 
00990         }
00991 
00992         atwa[im + i] = 0.;
00993 
00994         for (j = ymin; j < ymax; ++j) {
00995             atwa[im + i] += atw[in + j] * at[in + j];
00996         }
00997 
00998     }
00999 
01000 
01001     status = _giraffe_matrix_invert(ws->c, ws->atwa, ws->tmp);
01002 
01003     if (status != 0) {
01004         return 1;
01005     }
01006 
01007     for (i = 0; i < m; ++i) {
01008 
01009         register cxint j = 0;
01010         register cxint im = i * m;
01011 
01012 
01013         sf[i] = 0.;
01014         sv[i] = c[im + i];
01015 
01016         for (j = 0; j < m; ++j) {
01017             sf[i] += c[im + j] * atws[j];
01018         }
01019 
01020     }
01021 
01022     for (i = 0; i < n; ++i) {
01023 
01024         register cxint j = 0;
01025 
01026 
01027         sm[i] = 0.;
01028 
01029         for (j = 0; j < m; ++j) {
01030             sm[i] += at[j * n + i] * sf[j];
01031         }
01032 
01033     }
01034 
01035     return 0;
01036 
01037 }
01038 
01039 
01040 /*
01041  * @brief
01042  *   Extract spectra by simple summation.
01043  *
01044  * @param mz      Pixels values [nx, ny]
01045  * @param mslz    Scattered light model pixel values [nx, ny]
01046  * @param fibers  List of fibers to extract
01047  * @param my      Fiber centroid positions [ns, ny]
01048  * @param mw      Fiber widths [ns, ny]
01049  * @param ms      Extracted flux [ns, ny]
01050  * @param mse     Extracted flux error [ns, ny]
01051  * @param msn     Number of extracted pixels [ns, ny]
01052  * @param msy     Extracted flux centroid position [ns, ny]
01053  *
01054  * For each X bin and each spectrum the flux is computed as the sum of
01055  * pixels value from @em mz[nx,ny] along the virtual slit computed using
01056  * the localization mask @em my[ns, ny] and @em mw[ns, ny].
01057  * The table @em fibers specifies the spectra to extract.
01058  *
01059  * The images @em ms[ns, ny], @em mse[ns, ny], @em msn[ns, ny] and
01060  * @em msy[ns, ny] must be allocated by the caller.
01061  */
01062 
01063 inline static cxint
01064 _giraffe_extract_summation(const cpl_image* mz, const cpl_image* mvarz,
01065                            const cpl_table* fibers, const cpl_image* my,
01066                            const cpl_image* mw, cpl_image* mbpx,
01067                            cpl_image* ms, cpl_image* mse,
01068                            cpl_image* msn, cpl_image* msy)
01069 {
01070 
01071     register cxint nn;
01072 
01073     const cxchar* idx = NULL;
01074 
01075     cxint ny = cpl_image_get_size_x(mz);
01076     cxint nfibers = cpl_table_get_nrow(fibers);
01077     cxint nspectra = cpl_image_get_size_x(my);
01078     cxint nbins = cpl_image_get_size_y(my);
01079 
01080     const cxdouble* pixels = cpl_image_get_data_double_const(mz);
01081     const cxdouble* variances = cpl_image_get_data_double_const(mvarz);
01082     const cxdouble* locy = cpl_image_get_data_double_const(my);
01083     const cxdouble* locw = cpl_image_get_data_double_const(mw);
01084 
01085     cxdouble* flux = cpl_image_get_data_double(ms);
01086     cxdouble* flux_error = cpl_image_get_data_double(mse);
01087     cxdouble* flux_npixels = cpl_image_get_data_double(msn);
01088     cxdouble* flux_ypos = cpl_image_get_data_double(msy);
01089 
01090 
01091     /*
01092      * The number of fibers to be process must be less or equal to the
01093      * number of spectra available in the localization.
01094      */
01095 
01096     cx_assert(nfibers <= nspectra);
01097 
01098     idx = giraffe_fiberlist_query_index(fibers);
01099 
01100     cx_assert(cpl_table_has_column(fibers, idx) != 0);
01101 
01102     if (mbpx != NULL) {
01103 
01104         const cxint* bpx = cpl_image_get_data_int(mbpx);
01105 
01106         for (nn = 0; nn < nfibers; nn++) {
01107 
01108             register cxint x;
01109             register cxint ns = cpl_table_get_int(fibers, idx, nn, NULL) - 1;
01110 
01111 
01112             for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
01113 
01114                 cxint y;
01115                 cxint yup, ylo;
01116                 cxint lx = x * nspectra + ns;
01117                 cxint sx = x * nfibers + nn;
01118 
01119                 cxdouble ylower = locy[lx] - locw[lx];
01120                 cxdouble yupper = locy[lx] + locw[lx];
01121                 cxdouble zsum = 0.;
01122                 cxdouble ysum = 0.;
01123                 cxdouble error2 = 0.;
01124 
01125 
01126                 flux[sx] = 0.;
01127                 flux_npixels[sx] = 0.;
01128                 flux_error[sx] = 0.;
01129                 flux_ypos[sx] = 0.;
01130 
01131 
01132                 /*
01133                  * Skip zero-width (invalid) spectra
01134                  */
01135 
01136                 if (locw[lx] <= 0.0) {
01137                     continue;
01138                 }
01139 
01140 
01141                 /*
01142                  * Upper and lower border of the virtual slit. The real ones
01143                  * and the borders corrected for pixel fractions. If we are
01144                  * out of the the image boundaries we skip the extraction
01145                  * for this bin and fiber.
01146                  */
01147 
01148                 ylo = (cxint) ceil(ylower);
01149                 yup = (cxint) floor(yupper);
01150 
01151 
01152                 if (yup < 0. || ylo - 1 >= ny) {
01153                     continue;
01154                 }
01155 
01156 
01157                 /*
01158                  * Summation along the virtual slit. Check that y is always
01159                  * in the range of valid pixels [0, ny[. Take into account
01160                  * pixel fractions at the beginning and the end of the
01161                  * virtual slit.
01162                  */
01163 
01164                 /*
01165                  * Lower ordinate pixel fraction
01166                  */
01167 
01168                 y = ylo - 1;
01169 
01170                 if (y >= 0) {
01171 
01172                     if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
01173 
01174                         cxdouble extcoeff  = (cxdouble)ylo - ylower;
01175                         cxdouble extcoeff2 = extcoeff * extcoeff;
01176                         cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
01177 
01178                         flux[sx] = pixels[x * ny + y] * extcoeff;
01179                         flux_npixels[sx] = extcoeff;
01180                         error2 = variances[x * ny + y] * extcoeff2;
01181 
01182                         zsum = px * extcoeff;
01183                         ysum = y * px * extcoeff;
01184 
01185                     }
01186 
01187                 }
01188 
01189 
01190                 /*
01191                  * Sum pixel values along virtual slit.
01192                  */
01193 
01194                 for (y = CX_MAX(ylo, 0); y < yup && y < ny; y++) {
01195 
01196                     if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
01197 
01198                         cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
01199 
01200                         flux[sx] += pixels[x * ny + y];
01201                         flux_npixels[sx] += 1.0;
01202                         error2 += variances[x * ny + y];
01203 
01204                         zsum += px;
01205                         ysum += y * px;
01206 
01207                     }
01208 
01209                 }
01210 
01211 
01212                 /*
01213                  * Upper ordinate pixel fraction
01214                  */
01215 
01216                 y = yup;
01217 
01218                 if (y < ny) {
01219 
01220                     if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
01221 
01222                         cxdouble extcoeff  = yupper - (cxdouble)yup;
01223                         cxdouble extcoeff2 = extcoeff * extcoeff;
01224                         cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
01225 
01226                         flux[sx] += pixels[x * ny + y] * extcoeff;
01227                         flux_npixels[sx] += extcoeff;
01228                         error2 += variances[x * ny + y] * extcoeff2;
01229 
01230                         zsum += px * extcoeff;
01231                         ysum += y * px * extcoeff;
01232 
01233                     }
01234 
01235                 }
01236 
01237                 flux_error[sx] = sqrt(error2);
01238 
01239                 // FIXME: Check this protection from division by zero. Also
01240                 //        the minimum condition for the pixel values above.
01241 
01242                 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
01243                     flux_ypos[sx] = 0.5 * (yupper + ylower);
01244                 }
01245                 else {
01246                     flux_ypos[sx] = ysum / zsum;
01247                 }
01248 
01249             }
01250 
01251         }
01252 
01253     }
01254     else {
01255 
01256         for (nn = 0; nn < nfibers; nn++) {
01257 
01258             register cxint x;
01259             register cxint ns = cpl_table_get_int(fibers, idx,
01260                                                   nn, NULL) - 1;
01261 
01262 
01263             for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
01264 
01265                 cxint y;
01266                 cxint yup, ylo;
01267                 cxint lx = x * nspectra + ns;
01268                 cxint sx = x * nfibers + nn;
01269 
01270                 cxdouble yupper, ylower;
01271                 cxdouble zsum = 0.;
01272                 cxdouble ysum = 0.;
01273                 cxdouble error2 = 0.;
01274 
01275 
01276                 flux[sx] = 0.;
01277                 flux_npixels[sx] = 0.;
01278                 flux_error[sx] = 0.;
01279                 flux_ypos[sx] = 0.;
01280 
01281 
01282                 /*
01283                  * Skip zero-width (invalid) spectra
01284                  */
01285 
01286                 if (locw[lx] <= 0.0) {
01287                     continue;
01288                 }
01289 
01290 
01291                 /*
01292                  * Upper and lower border of the virtual slit. The real ones
01293                  * and the borders corrected for pixel fractions. If we are
01294                  * out of the the image boundaries we skip the extraction
01295                  * for this bin and fiber.
01296                  */
01297 
01298                 yupper = locy[lx] + locw[lx];
01299                 ylower = locy[lx] - locw[lx];
01300 
01301                 ylo = (cxint) ceil(ylower);
01302                 yup = (cxint) floor(yupper);
01303 
01304 
01305                 if (yup < 0. || ylo - 1 >= ny) {
01306                     continue;
01307                 }
01308 
01309 
01310                 /*
01311                  * Summation along the virtual slit. Check that y is always
01312                  * in the range of valid pixels [0, ny[. Take into account
01313                  * pixel fractions at the beginning and the end of the
01314                  * virtual slit.
01315                  */
01316 
01317                 /*
01318                  * Lower ordinate pixel fraction
01319                  */
01320 
01321                 y = ylo - 1;
01322 
01323                 if (y >= 0) {
01324 
01325                     cxdouble extcoeff  = (cxdouble)ylo - ylower;
01326                     cxdouble extcoeff2 = extcoeff * extcoeff;
01327                     cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
01328 
01329                     flux[sx] = pixels[x * ny + y] * extcoeff;
01330                     flux_npixels[sx] = extcoeff;
01331                     error2 = variances[x * ny + y] * extcoeff2;
01332 
01333                     zsum = px * extcoeff;
01334                     ysum = y * px * extcoeff;
01335 
01336                 }
01337 
01338 
01339                 /*
01340                  * Sum pixel values along virtual slit.
01341                  */
01342 
01343                 for (y = CX_MAX(ylo, 0); y < yup && y < ny; y++) {
01344 
01345                     cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
01346 
01347                     flux[sx] += pixels[x * ny + y];
01348                     flux_npixels[sx] += 1.0;
01349                     error2 += variances[x * ny + y];
01350 
01351                     zsum += px;
01352                     ysum += y * px;
01353                 }
01354 
01355 
01356                 /*
01357                  * Upper ordinate pixel fraction
01358                  */
01359 
01360                 y = yup;
01361 
01362                 if (y < ny) {
01363 
01364                     cxdouble extcoeff  = yupper - (cxdouble)yup;
01365                     cxdouble extcoeff2 = extcoeff * extcoeff;
01366                     cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
01367 
01368                     flux[sx] += pixels[x * ny + y] * extcoeff;
01369                     flux_npixels[sx] += extcoeff;
01370                     error2 += variances[x * ny + y] * extcoeff2;
01371 
01372                     zsum += px * extcoeff;
01373                     ysum += y * px * extcoeff;
01374 
01375                 }
01376 
01377                 flux_error[sx] = sqrt(error2);
01378 
01379                 // FIXME: Check this protection from division by zero. Also
01380                 //        the minimum condition for the pixel values above.
01381 
01382                 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
01383                     flux_ypos[sx] = 0.5 * (yupper + ylower);
01384                 }
01385                 else {
01386                     flux_ypos[sx] = ysum / zsum;
01387                 }
01388 
01389             }
01390 
01391         }
01392 
01393     }
01394 
01395     return 0;
01396 
01397 }
01398 
01399 
01400 /*
01401  * @brief
01402  *   Extract spectra using the optimal extraction method.
01403  *
01404  * @param mz      Pixels values [nx, ny]
01405  * @param mvar    Initial variance [nx, ny]
01406  * @param fibers  List of fibers to extract
01407  * @param my      Fiber centroid positions [ns, ny]
01408  * @param mw      Fiber widths [ns, ny]
01409  * @param ms      Extracted flux [ns, ny]
01410  * @param mse     Extracted flux error [ns, ny]
01411  * @param msn     Number of extracted pixels [ns, ny]
01412  * @param msy     Extracted flux centroid position [ns, ny]
01413  * @param config  Optimal extraction method setup
01414  *
01415  * TBD
01416  *
01417  * The images @em ms[ns, ny], @em mse[ns, ny], @em msn[ns, ny] and
01418  * @em msy[ns, ny] must be allocated by the caller.
01419  */
01420 
01421 inline static cxint
01422 _giraffe_extract_horne(const cpl_image* mz, const cpl_image* mzvar,
01423                        const cpl_table* fibers, const cpl_image* my,
01424                        const cpl_image* mw, const GiPsfData* psfdata,
01425                        cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
01426                        cpl_image* msn, cpl_image* msy,
01427                        const GiExtractHorneConfig* config)
01428 {
01429 
01430     const cxchar* idx = NULL;
01431 
01432     cxint nx = 0;
01433     cxint ny = 0;
01434     cxint fiber = 0;
01435     cxint nfibers = 0;
01436 
01437     const cxdouble* locy = NULL;
01438     const cxdouble* locw = NULL;
01439     const cxdouble* width = NULL;
01440     const cxdouble* exponent = NULL;
01441 
01442     GiModel* psfmodel = NULL;
01443 
01444 
01445     cx_assert(mz != NULL);
01446     cx_assert(mzvar != NULL);
01447 
01448     cx_assert(fibers != NULL);
01449 
01450     cx_assert(my != NULL);
01451     cx_assert(mw != NULL);
01452 
01453     cx_assert(psfdata != NULL);
01454 
01455     cx_assert(ms != NULL);
01456     cx_assert(mse != NULL);
01457     cx_assert(msn != NULL);
01458     cx_assert(msy != NULL);
01459 
01460     cx_assert(config != NULL);
01461 
01462     ny = cpl_image_get_size_x(mz);
01463     nx = cpl_image_get_size_y(mz);
01464     nfibers = cpl_table_get_nrow(fibers);
01465 
01466     locy = cpl_image_get_data_double_const(my);
01467     locw = cpl_image_get_data_double_const(mw);
01468 
01469     cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
01470               (nx == cpl_image_get_size_y(mzvar)));
01471 
01472     cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
01473     cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
01474 
01475     cx_assert(giraffe_psfdata_fibers(psfdata) ==
01476               (cxsize)cpl_image_get_size_x(my));
01477     cx_assert(giraffe_psfdata_bins(psfdata) ==
01478               (cxsize)cpl_image_get_size_y(my));
01479 
01480     cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
01481               (nx == cpl_image_get_size_y(ms)));
01482     cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
01483               (nx == cpl_image_get_size_y(mse)));
01484     cx_assert((nfibers == cpl_image_get_size_x(msn)) &&
01485               (nx == cpl_image_get_size_y(msn)));
01486     cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
01487               (nx == cpl_image_get_size_y(msy)));
01488 
01489     cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
01490                                  (nx == cpl_image_get_size_y(mbpx))));
01491 
01492 
01493     /*
01494      * Get the index column mapping the current spectum number to the
01495      * corresponding reference localization spectrum number.
01496      */
01497 
01498     idx = giraffe_fiberlist_query_index(fibers);
01499 
01500     cx_assert(cpl_table_has_column(fibers, idx) != 0);
01501 
01502 
01503     /*
01504      * Get the PSF profile data arrays for efficency reasons in the
01505      * following loops.
01506      */
01507 
01508     if (giraffe_psfdata_contains(psfdata, "Center") == FALSE) {
01509         return -1;
01510     }
01511 
01512     if (giraffe_psfdata_contains(psfdata, "Width2") == TRUE) {
01513         exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
01514             "Width2"));
01515     }
01516 
01517     width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
01518         "Width1"));
01519 
01520 
01521     /*
01522      * Create the PSF profile model from the PSF data object.
01523      */
01524 
01525     psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
01526 
01527     if (psfmodel == NULL) {
01528         return -2;
01529     }
01530 
01531     giraffe_model_set_parameter(psfmodel, "Center", 0.);
01532     giraffe_model_set_parameter(psfmodel, "Amplitude", 1.);
01533     giraffe_model_set_parameter(psfmodel, "Background", 0.);
01534 
01535 
01536     /*
01537      * Extract each fiber spectrum
01538      */
01539 
01540     for (fiber = 0; fiber < nfibers; ++fiber) {
01541 
01542         register cxint bin = 0;
01543         register cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
01544 
01545         cxint nbins = CX_MIN(nx, cpl_image_get_size_y(my));
01546 
01547         cxdouble* _ms = cpl_image_get_data_double(ms);
01548         cxdouble* _mse  = cpl_image_get_data_double(mse);
01549         cxdouble* _msy = cpl_image_get_data_double(msy);
01550         cxdouble* _msn = cpl_image_get_data_double(msn);
01551 
01552 
01553         for (bin = 0; bin < nbins; bin++) {
01554 
01555             register cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
01556             register cxint spos = bin * nfibers + fiber;
01557 
01558             cxint status = 0;
01559             cxint vwidth = 0;
01560 
01561             register cxdouble lcenter = locy[lpos];
01562             register cxdouble lwidth = locw[lpos];
01563 
01564             register cxdouble ylower = lcenter - lwidth;
01565             register cxdouble yupper = lcenter + lwidth;
01566 
01567             GiVirtualSlit* vslit = NULL;
01568 
01569             GiExtractionData result = {0., 0., 0., 0.};
01570 
01571 
01572             /*
01573              * Skip zero-width, invalid spectra
01574              */
01575 
01576             if ((lwidth <= 0.) || (yupper < 0.) || (ylower > ny)) {
01577                 continue;
01578             }
01579 
01580             /*
01581              * Fill the virtual slit with data
01582              */
01583 
01584             vslit = _giraffe_virtualslit_new(config->ewidth);
01585 
01586             vwidth = _giraffe_virtualslit_setup(vslit, bin, lcenter, lwidth,
01587                                                 mz, mzvar, mbpx);
01588 
01589             if (vwidth == 0) {
01590                 _giraffe_virtualslit_delete(vslit);
01591                 vslit = NULL;
01592 
01593                 continue;
01594             }
01595 
01596 
01597             /*
01598              * Update PSF profile model width and exponent
01599              */
01600 
01601             giraffe_model_set_parameter(psfmodel, "Width1", width[lpos]);
01602 
01603             if (exponent != NULL) {
01604                 giraffe_model_set_parameter(psfmodel, "Width2",
01605                                             exponent[lpos]);
01606             }
01607 
01608 
01609             /*
01610              * Compute flux from the virtual slit using Horne's optimal
01611              * extraction algorithm.
01612              */
01613 
01614             status = _giraffe_horne_extract_slit(&result, vslit, psfmodel,
01615                                                  config);
01616 
01617             _giraffe_virtualslit_delete(vslit);
01618             vslit = NULL;
01619 
01620             if (status < 0) {
01621 
01622                 giraffe_model_delete(psfmodel);
01623                 psfmodel = NULL;
01624 
01625                 return 1;
01626             }
01627 
01628             _ms[spos] = result.value;
01629             _mse[spos] = result.error;
01630             _msy[spos] = result.position;
01631             _msn[spos] = result.npixels;
01632 
01633         }
01634 
01635     }
01636 
01637 
01638     giraffe_model_delete(psfmodel);
01639     psfmodel = NULL;
01640 
01641     return 0;
01642 
01643 }
01644 
01645 
01646 /*
01647  * Fill extraction matrix with the fiber profiles and the coefficients of
01648  * the Chebyshev polynomial model of the background.
01649  */
01650 
01651 inline static cxint
01652 _giraffe_optimal_build_profiles(cpl_matrix* profiles,
01653                                 GiExtractionPsfLimits* limits,
01654                                 const cpl_image* my, const cpl_image* mw,
01655                                 const cpl_table* fibers, cxint bin,
01656                                 GiModel* psf, const cxdouble* width,
01657                                 const cxdouble* exponent, cxdouble wfactor)
01658 {
01659 
01660     const cxchar* idx = giraffe_fiberlist_query_index(fibers);
01661 
01662     cxint fiber = 0;
01663     cxint nfibers = cpl_table_get_nrow(fibers);
01664     cxint ny = cpl_matrix_get_ncol(profiles);
01665 
01666     const cxdouble* locy = cpl_image_get_data_double_const(my);
01667     const cxdouble* locw = cpl_image_get_data_double_const(mw);
01668 
01669     cxdouble* _profiles = cpl_matrix_get_data(profiles);
01670 
01671     cxdouble* ypos = NULL;
01672 
01673 
01674     cx_assert(cpl_table_has_column(fibers, idx) != 0);
01675     cx_assert((limits == NULL) ||
01676               (cpl_matrix_get_nrow(profiles) == limits->size));
01677 
01678     ypos = cx_calloc(ny, sizeof(cxdouble));
01679 
01680     for (fiber = 0; fiber < nfibers; ++fiber) {
01681 
01682         register cxint i = 0;
01683         register cxint y = 0;
01684         register cxint k = 0;
01685 
01686         cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
01687         cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
01688 
01689         register cxdouble lcenter = locy[lpos];
01690         register cxdouble lwidth = locw[lpos];
01691 
01692         register cxdouble ylower = lcenter - fabs(wfactor) * lwidth;
01693         register cxdouble yupper = lcenter + fabs(wfactor) * lwidth;
01694 
01695         register cxint first = (cxint) floor(ylower);
01696         register cxint last = (cxint) ceil(yupper);
01697 
01698         register cxint vwidth = 0;
01699 
01700         cxdouble norm = 0.;
01701         cxdouble* _mnpsf = NULL;
01702 
01703         cpl_matrix* positions = NULL;
01704         cpl_matrix* mnpsf = NULL;
01705 
01706 
01707         /*
01708          * Upper, lower border and width of the virtual slit
01709          */
01710 
01711         ylower = CX_MAX(0., ylower);
01712         yupper = CX_MIN(ny - 1., yupper);
01713 
01714         first = CX_MAX(0, first);
01715         last = CX_MIN(ny - 1, last);
01716 
01717         vwidth = last - first + 1;
01718 
01719         if (limits != NULL) {
01720             limits->ymin[fiber] = first;
01721             limits->ymax[fiber] = last + 1;
01722         }
01723 
01724 
01725         /*
01726          * Update PSF profile model width and exponent
01727          */
01728 
01729         giraffe_model_set_parameter(psf, "Width1", width[lpos]);
01730 
01731         if (exponent != NULL) {
01732             giraffe_model_set_parameter(psf, "Width2", exponent[lpos]);
01733         }
01734 
01735 
01736         /*
01737          * Compute normalized psf model
01738          */
01739 
01740         k = 0;
01741         for (y = first; y <= last; ++y) {
01742             ypos[k] = y - lcenter;
01743             ++k;
01744         }
01745 
01746         positions = cpl_matrix_wrap(vwidth, 1, ypos);
01747         mnpsf = _giraffe_compute_psf(psf, positions);
01748 
01749         cpl_matrix_unwrap(positions);
01750         positions = NULL;
01751 
01752         if (mnpsf == NULL) {
01753             cx_free(ypos);
01754             ypos = NULL;
01755 
01756             return 1;
01757         }
01758 
01759         _mnpsf = cpl_matrix_get_data(mnpsf);
01760 
01761         for (i = 0; i < vwidth; ++i) {
01762             _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
01763             norm += _mnpsf[i];
01764         }
01765 
01766         for (i = 0; i < vwidth; ++i) {
01767             _mnpsf[i] /= norm;
01768         }
01769 
01770         k = fiber * ny + first;
01771         for (y = 0; y < vwidth; ++y) {
01772             _profiles[k + y] = _mnpsf[y];
01773         }
01774 
01775         cpl_matrix_delete(mnpsf);
01776         mnpsf = NULL;
01777 
01778     }
01779 
01780     cx_free(ypos);
01781     ypos = NULL;
01782 
01783     return 0;
01784 
01785 }
01786 
01787 
01788 inline static cxint
01789 _giraffe_extract_optimal(const cpl_image* mz, const cpl_image* mzvar,
01790                          const cpl_table* fibers, const cpl_image* my,
01791                          const cpl_image* mw, const GiPsfData* psfdata,
01792                          cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
01793                          cpl_image* msm, cpl_image* msy,
01794                          const GiExtractOptimalConfig* config)
01795 {
01796 
01797     const cxbool nolimits = (config->limits == TRUE) ? FALSE : TRUE;
01798 
01799     const cxint bkg_nc = config->bkgorder + 1;
01800     const cxint niter = config->clip.iterations;
01801 
01802     register cxint i = 0;
01803 
01804     cxint nx = 0;
01805     cxint ny = 0;
01806     cxint bin = 0;
01807     cxint nbins = 0;
01808     cxint nfibers = 0;
01809 
01810     const cxdouble wfactor = config->ewidth;
01811     const cxdouble sigma = config->clip.level * config->clip.level;
01812     const cxdouble fraction = config->clip.fraction;
01813 
01814     const cxdouble* width = NULL;
01815     const cxdouble* exponent = NULL;
01816 
01817     cxdouble* _ypos = NULL;
01818     cxdouble* _bkg_base = NULL;
01819     cxdouble* _profiles = NULL;
01820     cxdouble* _signal = NULL;
01821     cxdouble* _variance = NULL;
01822     cxdouble* _mask = NULL;
01823     cxdouble* _weights = NULL;
01824 
01825     cpl_matrix* ypos = NULL;
01826     cpl_matrix* bkg_base = NULL;
01827     cpl_matrix* profiles = NULL;
01828     cpl_matrix* weights = NULL;
01829     cpl_matrix* signal = NULL;
01830     cpl_matrix* variance = NULL;
01831     cpl_matrix* mask = NULL;
01832 
01833     GiModel* psfmodel = NULL;
01834 
01835     GiExtractionPsfLimits* limits = NULL;
01836 
01837     GiExtractionSlice* slice = NULL;
01838 
01839     GiExtractionWorkspace* workspace;
01840 
01841 
01842     cx_assert(mz != NULL);
01843     cx_assert(mzvar != NULL);
01844 
01845     cx_assert(fibers != NULL);
01846 
01847     cx_assert(my != NULL);
01848     cx_assert(mw != NULL);
01849 
01850     cx_assert(psfdata != NULL);
01851 
01852     cx_assert(ms != NULL);
01853     cx_assert(mse != NULL);
01854     cx_assert(msm != NULL);
01855     cx_assert(msy != NULL);
01856 
01857     ny = cpl_image_get_size_x(mz);
01858     nx = cpl_image_get_size_y(mz);
01859 
01860     nfibers = cpl_table_get_nrow(fibers);
01861     nbins = CX_MIN(nx, cpl_image_get_size_y(my));
01862 
01863     cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
01864               (nx == cpl_image_get_size_y(mzvar)));
01865 
01866     cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
01867     cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
01868 
01869     cx_assert(giraffe_psfdata_fibers(psfdata) ==
01870               (cxsize)cpl_image_get_size_x(my));
01871     cx_assert(giraffe_psfdata_bins(psfdata) ==
01872               (cxsize)cpl_image_get_size_y(my));
01873 
01874     cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
01875               (nx == cpl_image_get_size_y(ms)));
01876     cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
01877               (nx == cpl_image_get_size_y(mse)));
01878     cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
01879               (nx == cpl_image_get_size_y(msy)));
01880     cx_assert((ny == cpl_image_get_size_x(msm)) &&
01881               (nx == cpl_image_get_size_y(msm)));
01882 
01883     cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
01884                                  (nx == cpl_image_get_size_y(mbpx))));
01885 
01886 
01887     /*
01888      * Get the PSF profile data arrays for efficiency reasons in the
01889      * following loops.
01890      */
01891 
01892     if (giraffe_psfdata_contains(psfdata, "Center") == FALSE) {
01893         return -1;
01894     }
01895 
01896     if (giraffe_psfdata_contains(psfdata, "Width2") == TRUE) {
01897         exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
01898             "Width2"));
01899     }
01900 
01901     width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
01902         "Width1"));
01903 
01904 
01905     /*
01906      * Create the PSF profile model from the PSF data object.
01907      */
01908 
01909     psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
01910 
01911     if (psfmodel == NULL) {
01912         return -2;
01913     }
01914 
01915     giraffe_model_set_parameter(psfmodel, "Amplitude", 1.);
01916     giraffe_model_set_parameter(psfmodel, "Background", 0.);
01917     giraffe_model_set_parameter(psfmodel, "Center", 0.);
01918 
01919 
01920     /*
01921      * Set up the vector of pixel positions
01922      */
01923 
01924     ypos = cpl_matrix_new(ny, 1);
01925 
01926     if (ypos == NULL) {
01927         giraffe_model_delete(psfmodel);
01928         psfmodel = NULL;
01929 
01930         return -3;
01931     }
01932 
01933     _ypos = cpl_matrix_get_data(ypos);
01934 
01935     for (i = 0; i < ny; ++i) {
01936         _ypos[i] = i;
01937     }
01938 
01939 
01940     /*
01941      * Create profile matrix and the matrices for the signal, bad
01942      * pixel mask, variance and the weights.
01943      */
01944 
01945     profiles = cpl_matrix_new(nfibers + bkg_nc, ny);
01946 
01947     if (profiles == NULL) {
01948         cpl_matrix_delete(ypos);
01949         ypos = NULL;
01950 
01951         giraffe_model_delete(psfmodel);
01952         psfmodel = NULL;
01953 
01954         return -3;
01955     }
01956 
01957     _profiles = cpl_matrix_get_data(profiles);
01958 
01959 
01960     signal = cpl_matrix_new(ny, 1);
01961 
01962     if (signal == NULL) {
01963         cpl_matrix_delete(profiles);
01964         profiles = NULL;
01965 
01966         cpl_matrix_delete(ypos);
01967         ypos = NULL;
01968 
01969         giraffe_model_delete(psfmodel);
01970         psfmodel = NULL;
01971 
01972         return -3;
01973     }
01974 
01975     _signal = cpl_matrix_get_data(signal);
01976 
01977 
01978     variance = cpl_matrix_new(ny, 1);
01979 
01980     if (variance == NULL) {
01981         cpl_matrix_delete(signal);
01982         signal = NULL;
01983 
01984         cpl_matrix_delete(profiles);
01985         profiles = NULL;
01986 
01987         cpl_matrix_delete(ypos);
01988         ypos = NULL;
01989 
01990         giraffe_model_delete(psfmodel);
01991         psfmodel = NULL;
01992 
01993         return -3;
01994     }
01995 
01996     _variance = cpl_matrix_get_data(variance);
01997 
01998 
01999     mask = cpl_matrix_new(ny, 1);
02000 
02001     if (mask == NULL) {
02002         cpl_matrix_delete(variance);
02003         variance = NULL;
02004 
02005         cpl_matrix_delete(signal);
02006         signal = NULL;
02007 
02008         cpl_matrix_delete(profiles);
02009         profiles = NULL;
02010 
02011         cpl_matrix_delete(ypos);
02012         ypos = NULL;
02013 
02014         giraffe_model_delete(psfmodel);
02015         psfmodel = NULL;
02016 
02017         return -3;
02018     }
02019 
02020     _mask = cpl_matrix_get_data(mask);
02021 
02022 
02023     weights = cpl_matrix_new(ny, 1);
02024 
02025     if (mask == NULL) {
02026         cpl_matrix_delete(mask);
02027         mask = NULL;
02028 
02029         cpl_matrix_delete(variance);
02030         variance = NULL;
02031 
02032         cpl_matrix_delete(signal);
02033         signal = NULL;
02034 
02035         cpl_matrix_delete(profiles);
02036         profiles = NULL;
02037 
02038         cpl_matrix_delete(ypos);
02039         ypos = NULL;
02040 
02041         giraffe_model_delete(psfmodel);
02042         psfmodel = NULL;
02043 
02044         return -3;
02045     }
02046 
02047     _weights = cpl_matrix_get_data(weights);
02048 
02049 
02050     /*
02051      * Fill design matrix with the basis functions of the
02052      * background polynomial model.
02053      */
02054 
02055     bkg_base = giraffe_chebyshev_base1d(0., ny, bkg_nc, ypos);
02056 
02057     cpl_matrix_delete(ypos);
02058     ypos = NULL;
02059 
02060     if (bkg_base == NULL) {
02061         cpl_matrix_delete(weights);
02062         weights = NULL;
02063 
02064         cpl_matrix_delete(mask);
02065         mask = NULL;
02066 
02067         cpl_matrix_delete(variance);
02068         variance = NULL;
02069 
02070         cpl_matrix_delete(signal);
02071         signal = NULL;
02072 
02073         cpl_matrix_delete(profiles);
02074         profiles = NULL;
02075 
02076         cpl_matrix_delete(ypos);
02077         ypos = NULL;
02078 
02079         giraffe_model_delete(psfmodel);
02080         psfmodel = NULL;
02081 
02082         return -3;
02083     }
02084 
02085     _bkg_base = cpl_matrix_get_data(bkg_base);
02086 
02087     for (i = 0; i < bkg_nc; ++i) {
02088 
02089         register cxint j = 0;
02090         register cxint offset = nfibers * ny;
02091 
02092         for (j = 0; j < ny; ++j) {
02093             _profiles[i * ny + j + offset] = _bkg_base[i * ny + j];
02094         }
02095 
02096     }
02097 
02098     _bkg_base = NULL;
02099 
02100     cpl_matrix_delete(bkg_base);
02101     bkg_base = NULL;
02102 
02103 
02104     /*
02105      * Extract all fiber spectra simultaneously for each wavelength bin
02106      */
02107 
02108     slice = _giraffe_extractionslice_new(nfibers, ny, bkg_nc);
02109 
02110     if (slice == NULL) {
02111         cpl_matrix_delete(weights);
02112         weights = NULL;
02113 
02114         cpl_matrix_delete(mask);
02115         mask = NULL;
02116 
02117         cpl_matrix_delete(variance);
02118         variance = NULL;
02119 
02120         cpl_matrix_delete(signal);
02121         signal = NULL;
02122 
02123         cpl_matrix_delete(profiles);
02124         profiles = NULL;
02125 
02126         cpl_matrix_delete(ypos);
02127         ypos = NULL;
02128 
02129         giraffe_model_delete(psfmodel);
02130         psfmodel = NULL;
02131 
02132         return -3;
02133     }
02134 
02135 
02136     limits = _giraffe_extraction_psflimits_new(nfibers + bkg_nc);
02137 
02138     if (limits == NULL) {
02139 
02140         _giraffe_extractionslice_delete(slice);
02141         slice = NULL;
02142 
02143         cpl_matrix_delete(weights);
02144         weights = NULL;
02145 
02146         cpl_matrix_delete(mask);
02147         mask = NULL;
02148 
02149         cpl_matrix_delete(variance);
02150         variance = NULL;
02151 
02152         cpl_matrix_delete(signal);
02153         signal = NULL;
02154 
02155         cpl_matrix_delete(profiles);
02156         profiles = NULL;
02157 
02158         cpl_matrix_delete(ypos);
02159         ypos = NULL;
02160 
02161         giraffe_model_delete(psfmodel);
02162         psfmodel = NULL;
02163 
02164         return -3;
02165 
02166     }
02167 
02168     for (i = 0; i < limits->size; ++i) {
02169         limits->ymin[i] = 0;
02170         limits->ymax[i] = ny;
02171     }
02172 
02173 
02174     /*
02175      * Allocate workspace for matrix multiplications
02176      */
02177 
02178     workspace = _giraffe_optimal_workspace_new(nfibers + bkg_nc, ny);
02179 
02180     for (bin = 0; bin < nbins; ++bin) {
02181 
02182         cxbool stop = FALSE;
02183 
02184         cxint iter = 0;
02185         cxint nmin = 0;
02186         cxint ngood = ny;
02187 
02188         const cxdouble* _my = cpl_image_get_data_double_const(my);
02189         const cxdouble* _mz = cpl_image_get_data_double_const(mz);
02190         const cxdouble* _mzvar = cpl_image_get_data_double_const(mzvar);
02191 
02192         cxdouble* _ms = cpl_image_get_data_double(ms);
02193         cxdouble* _mse = cpl_image_get_data_double(mse);
02194         cxdouble* _msy = cpl_image_get_data_double(msy);
02195         cxdouble* _msm = cpl_image_get_data_double(msm);
02196 
02197         cxint status = 0;
02198 
02199         GiExtractionPsfLimits* _limits = (nolimits == FALSE) ? limits : NULL;
02200 
02201         cx_assert(_mz != NULL);
02202         cx_assert(_mzvar != NULL);
02203 
02204 
02205         /*
02206          * Fill the design matrix with the fiber profiles for the
02207          * current wavelength bin
02208          */
02209 
02210         status = _giraffe_optimal_build_profiles(profiles, _limits, my, mw,
02211                                                  fibers, bin, psfmodel, width,
02212                                                  exponent, wfactor);
02213 
02214         if (status != 0) {
02215             _giraffe_optimal_workspace_delete(workspace);
02216             workspace = NULL;
02217 
02218             _giraffe_extraction_psflimits_delete(limits);
02219             limits = NULL;
02220 
02221             _giraffe_extractionslice_delete(slice);
02222             slice = NULL;
02223 
02224             cpl_matrix_delete(weights);
02225             weights = NULL;
02226 
02227             cpl_matrix_delete(mask);
02228             mask = NULL;
02229 
02230             cpl_matrix_delete(variance);
02231             variance = NULL;
02232 
02233             cpl_matrix_delete(signal);
02234             signal = NULL;
02235 
02236             cpl_matrix_delete(profiles);
02237             profiles = NULL;
02238 
02239             cpl_matrix_delete(ypos);
02240             ypos = NULL;
02241 
02242             giraffe_model_delete(psfmodel);
02243             psfmodel = NULL;
02244 
02245             return -4;
02246         }
02247 
02248 
02249         /*
02250          * Fill the signal, variance, mask and weight matrices
02251          */
02252 
02253 
02254         if (mbpx != NULL) {
02255 
02256             const cxint* _mbpx = cpl_image_get_data_int_const(mbpx);
02257 
02258 
02259             cx_assert(_mbpx != NULL);
02260 
02261             for (i = 0; i < ny; ++i) {
02262 
02263                 cxbool bad = (_mbpx[bin * ny + i] & GIR_M_PIX_SET) ||
02264                     (_mz[bin * ny + i] < 0.);
02265 
02266                 _signal[i] = _mz[bin * ny + i];
02267                 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
02268                 _mask[i] = 1.;
02269 
02270                 if (bad == TRUE) {
02271                     _mask[i] = 0.;
02272                     --ngood;
02273                 }
02274 
02275                 _weights[i] = _mask[i] / _variance[i];
02276 
02277             }
02278 
02279         }
02280         else {
02281 
02282             for (i = 0; i < ny; ++i) {
02283 
02284                 cxbool bad = (_mz[bin * ny + i] < 0.);
02285 
02286                 _signal[i] = _mz[bin * ny + i];
02287                 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
02288                 _mask[i] = 1.;
02289 
02290                 if (bad == TRUE) {
02291                     _mask[i] = 0.;
02292                     --ngood;
02293                 }
02294 
02295                 _weights[i] = _mask[i] / _variance[i];
02296 
02297             }
02298 
02299         }
02300 
02301 
02302         /*
02303          * Extract simultaneously the fluxes of all fibers for the current
02304          * wavelength bin
02305          */
02306 
02307         nmin = (cxint)(fraction * ngood);
02308 
02309         while ((iter < niter) && (stop == FALSE)) {
02310 
02311             cxint nreject = 0;
02312 
02313             const cxdouble* _model = NULL;
02314 
02315 
02316             status = _giraffe_optimal_extract_slice(slice, profiles,
02317                 signal, weights, limits, workspace);
02318 
02319             if (status != 0) {
02320                 _giraffe_optimal_workspace_delete(workspace);
02321                 workspace = NULL;
02322 
02323                 _giraffe_extraction_psflimits_delete(limits);
02324                 limits = NULL;
02325 
02326                 _giraffe_extractionslice_delete(slice);
02327                 slice = NULL;
02328 
02329                 cpl_matrix_delete(weights);
02330                 weights = NULL;
02331 
02332                 cpl_matrix_delete(mask);
02333                 mask = NULL;
02334 
02335                 cpl_matrix_delete(variance);
02336                 variance = NULL;
02337 
02338                 cpl_matrix_delete(signal);
02339                 signal = NULL;
02340 
02341                 cpl_matrix_delete(profiles);
02342                 profiles = NULL;
02343 
02344                 cpl_matrix_delete(ypos);
02345                 ypos = NULL;
02346 
02347                 giraffe_model_delete(psfmodel);
02348                 psfmodel = NULL;
02349 
02350                 return -5;
02351             }
02352 
02353 
02354             /*
02355              * Update weighting factors
02356              */
02357 
02358             _model = cpl_matrix_get_data(slice->model);
02359 
02360             for (i = 0; i < ny; ++i) {
02361 
02362                 if (_mask[i] > 0.) {
02363 
02364                     cxbool bad = FALSE;
02365                     cxdouble residual = _signal[i] - _model[i];
02366 
02367 
02368                     _variance[i] = _model[i] + _mzvar[bin * ny + i];
02369 
02370                     bad = (residual * residual) > (sigma * _variance[i]) ?
02371                         TRUE : FALSE;
02372 
02373                     if (bad == TRUE) {
02374                         _mask[i] = 0.;
02375                         ++nreject;
02376                         --ngood;
02377                     }
02378 
02379                     _weights[i] = _mask[i] / _variance[i];
02380 
02381                 }
02382 
02383             }
02384 
02385             if ((nreject == 0) || (ngood <= nmin)) {
02386                 stop = TRUE;
02387             }
02388 
02389             ++iter;
02390 
02391         }
02392 
02393 
02394         /*
02395          * Copy the extracted fluxes, their variance and the modeled signal
02396          * to the result images.
02397          */
02398 
02399         memcpy(&_ms[bin * nfibers], cpl_matrix_get_data(slice->flux),
02400                slice->nflx * sizeof(cxdouble));
02401         memcpy(&_mse[bin * nfibers], cpl_matrix_get_data(slice->variance),
02402                slice->nflx * sizeof(cxdouble));
02403         memcpy(&_msm[bin * ny], cpl_matrix_get_data(slice->model),
02404                slice->msize * sizeof(cxdouble));
02405 
02406         memcpy(&_msy[bin * nfibers], &_my[bin * nfibers],
02407                nfibers * sizeof(cxdouble));
02408 
02409 
02410         /*
02411          * Reset the profile part of the design matrix
02412          */
02413 
02414         cpl_matrix_fill_window(profiles, 0., 0, 0, nfibers, ny);
02415 
02416     }
02417 
02418 
02419     /*
02420      * Compute errors of the extracted spectra from the variance
02421      */
02422 
02423     cpl_image_power(mse, 0.5);
02424 
02425     _giraffe_optimal_workspace_delete(workspace);
02426     workspace = NULL;
02427 
02428     _giraffe_extraction_psflimits_delete(limits);
02429     limits = NULL;
02430 
02431     _giraffe_extractionslice_delete(slice);
02432     slice = NULL;
02433 
02434     cpl_matrix_delete(weights);
02435     weights = NULL;
02436 
02437     cpl_matrix_delete(mask);
02438     mask = NULL;
02439 
02440     cpl_matrix_delete(variance);
02441     variance = NULL;
02442 
02443     cpl_matrix_delete(signal);
02444     signal = NULL;
02445 
02446     cpl_matrix_delete(profiles);
02447     profiles = NULL;
02448 
02449     giraffe_model_delete(psfmodel);
02450     psfmodel = NULL;
02451 
02452     return 0;
02453 
02454 }
02455 
02456 
02481 cxint
02482 giraffe_extract_spectra(GiExtraction* result, GiImage* image,
02483                         GiTable* fibers, GiLocalization* sloc,
02484                         GiImage* bpixel, GiImage* slight,
02485                         GiExtractConfig* config)
02486 {
02487 
02488     const cxchar *fctid = "giraffe_extract_spectra";
02489 
02490 
02491     cxint ns = 0;
02492     cxint nx = 0;
02493     cxint ny = 0;
02494     cxint status  = 0;
02495     cxint nframes = 1;
02496 
02497     cxdouble bias_ron   = 0.;
02498     cxdouble bias_sigma = 0.;
02499     cxdouble dark_value = 0.;
02500     cxdouble exptime    = 0.;
02501     cxdouble conad      = 1.;
02502 
02503     cpl_propertylist *properties;
02504 
02505     cpl_image* _image = NULL;
02506     cpl_image* _locy = NULL;
02507     cpl_image* _locw = NULL;
02508     cpl_image* _spectra = NULL;
02509     cpl_image* _error = NULL;
02510     cpl_image* _npixels = NULL;
02511     cpl_image* _centroid = NULL;
02512     cpl_image* _model = NULL;
02513 
02514     cpl_table* _fibers = NULL;
02515 
02516 
02517     /*
02518      * Preprocessing
02519      */
02520 
02521     if (!result || !image || !fibers || !sloc || !config) {
02522         cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
02523         return 1;
02524     }
02525 
02526 
02527     if ((sloc->locy == NULL) || (sloc->locw == NULL)) {
02528         cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
02529         return 1;
02530     }
02531 
02532 
02533     if (result->spectra != NULL || result->error != NULL ||
02534         result->npixels != NULL || result->centroid != NULL ||
02535         result->model != NULL) {
02536         gi_warning("%s: Results structure at %p is not empty! Contents "
02537                    "might be lost.", fctid, result);
02538     }
02539 
02540 
02541     _fibers = giraffe_table_get(fibers);
02542 
02543     if (_fibers == NULL) {
02544         cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
02545         return 1;
02546     }
02547 
02548 
02549     if ((config->emethod == GIEXTRACT_OPTIMAL) && (sloc->psf == NULL)) {
02550         cpl_msg_error(fctid, "Missing data: PSF profile data is required "
02551                       "for optimal spectrum extraction!");
02552         cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
02553 
02554         return 1;
02555     }
02556 
02557 
02558     properties = giraffe_image_get_properties(image);
02559 
02560     if (properties == NULL) {
02561         cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
02562         return 1;
02563     }
02564 
02565 
02566     if (!cpl_propertylist_has(properties, GIALIAS_CONAD)) {
02567         cpl_msg_error(fctid, "Missing detector gain property (%s)! ",
02568                       GIALIAS_CONAD);
02569         return 1;
02570     }
02571     else {
02572         conad = cpl_propertylist_get_double(properties, GIALIAS_CONAD);
02573     }
02574 
02575 
02576     if (!cpl_propertylist_has(properties, GIALIAS_BIASERROR)) {
02577         cpl_msg_warning(fctid, "Missing bias error property (%s)! Setting "
02578                         "bias error to 0.", GIALIAS_BIASERROR);
02579         bias_sigma = 0.;
02580     }
02581     else {
02582         bias_sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASERROR);
02583     }
02584 
02585 
02586     if (config->ron > 0.) {
02587 
02588         cpl_msg_info(fctid, "Setting bias RMS property (%s) to %.4g ADU",
02589                      GIALIAS_BIASSIGMA, config->ron);
02590 
02591         cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
02592                                        config->ron);
02593     }
02594 
02595     bias_ron = giraffe_propertylist_get_ron(properties);
02596 
02597 
02598     if (!cpl_propertylist_has(properties, GIALIAS_DARKVALUE)) {
02599 
02600         dark_value = 0.;
02601 
02602         cpl_msg_warning(fctid, "Missing dark value property (%s), will be "
02603                         "set to 0.!", GIALIAS_DARKVALUE);
02604         cpl_propertylist_append_double(properties, GIALIAS_DARKVALUE,
02605                                        dark_value);
02606 
02607     }
02608     else {
02609         dark_value = cpl_propertylist_get_double(properties,
02610                                                  GIALIAS_DARKVALUE);
02611     }
02612 
02613 
02614     if (!cpl_propertylist_has(properties, GIALIAS_EXPTIME)) {
02615         cpl_msg_error(fctid, "Missing exposure time property (%s)!",
02616                       GIALIAS_EXPTIME);
02617         return 1;
02618     }
02619     else {
02620         exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
02621     }
02622 
02623 
02624     if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
02625         nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
02626     }
02627 
02628 
02629     /*
02630      * Processing
02631      */
02632 
02633     /*
02634      * Convert the bias and dark errors from ADU to electrons.
02635      */
02636 
02637     bias_sigma *= conad;
02638     dark_value *= conad;
02639 
02640     /*
02641      * For extracting the spectra, the bias and dark corrected raw image is
02642      * converted from ADU to electrons, and, in case it is an averaged frame,
02643      * it is scaled by the number of frames which were used. This turns the
02644      * raw frame into an image of the total number of the recorded
02645      * photoelectrons.
02646      *
02647      * To compensate for that, the extracted spectra, their errors, and,
02648      * possibly the spectrum model are rescaled after the extraction step
02649      * is completed.
02650      */
02651 
02652     _image = cpl_image_multiply_scalar_create(giraffe_image_get(image),
02653                                               nframes * conad);
02654 
02655     _locy = giraffe_image_get(sloc->locy);
02656     _locw = giraffe_image_get(sloc->locw);
02657 
02658     ny = cpl_image_get_size_x(_image);
02659     nx = cpl_image_get_size_y(_locw);
02660     ns = cpl_table_get_nrow(_fibers);
02661 
02662 
02663     switch (config->emethod) {
02664         case GIEXTRACT_SUM:
02665         {
02666 
02667             cxint xsize = cpl_image_get_size_x(_image);
02668             cxint ysize = cpl_image_get_size_y(_image);
02669 
02670             cxdouble ron_variance  = bias_ron * bias_ron;
02671             cxdouble bias_variance = bias_sigma * bias_sigma;
02672             cxdouble dark_variance = dark_value * exptime;
02673 
02674             cpl_image* bpixmap = NULL;
02675             cpl_image* variance = NULL;
02676 
02677 
02678             result->spectra = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02679             result->error = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02680             result->npixels = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02681             result->centroid = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02682             result->model = NULL;
02683 
02684             _spectra = giraffe_image_get(result->spectra);
02685             _error = giraffe_image_get(result->error);
02686             _npixels = giraffe_image_get(result->npixels);
02687             _centroid = giraffe_image_get(result->centroid);
02688 
02689             if (bpixel != NULL) {
02690 
02691                 bpixmap = giraffe_image_get(bpixel);
02692 
02693                 if (cpl_image_get_size_x(bpixmap) != xsize ||
02694                     cpl_image_get_size_y(bpixmap) != ysize) {
02695 
02696                     cxbool crop = FALSE;
02697 
02698                     cpl_propertylist *p =
02699                             giraffe_image_get_properties(bpixel);
02700 
02701                     GiWindow w = {1, 1, 0, 0};
02702 
02703 
02704                     w.x1 = cpl_image_get_size_x(bpixmap);
02705                     w.y1 = cpl_image_get_size_y(bpixmap);
02706 
02707                     if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
02708                         w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
02709                         crop = TRUE;
02710                     }
02711 
02712                     if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
02713                         w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
02714                         crop = TRUE;
02715                     }
02716 
02717                     if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
02718                         w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
02719                         crop = TRUE;
02720                     }
02721 
02722                     if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
02723                         w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
02724                         crop = TRUE;
02725                     }
02726 
02727                     if ((w.x1 - w.x0 + 1) != xsize ||
02728                         (w.y1 - w.y0 + 1) != ysize) {
02729                         cpl_msg_error(fctid, "Invalid bad pixel map! Image "
02730                                       "sizes do not match!");
02731 
02732                         giraffe_image_delete(result->spectra);
02733                         result->spectra = NULL;
02734 
02735                         giraffe_image_delete(result->error);
02736                         result->error = NULL;
02737 
02738                         giraffe_image_delete(result->npixels);
02739                         result->npixels = NULL;
02740 
02741                         giraffe_image_delete(result->centroid);
02742                         result->centroid = NULL;
02743 
02744                         giraffe_image_delete(result->model);
02745                         result->model = NULL;
02746 
02747                         cpl_image_delete(_image);
02748                         _image = NULL;
02749 
02750                         return 1;
02751                     }
02752 
02753                     if (crop == TRUE) {
02754                         bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
02755                                                     w.x1, w.y1);
02756                     }
02757 
02758                 }
02759 
02760             }
02761 
02762             if (slight != NULL) {
02763                 cpl_msg_warning(fctid, "Scattered light model will be "
02764                                 "ignored for extraction method `SUM'");
02765             }
02766 
02767             variance = cpl_image_abs_create(_image);
02768 
02769             /*
02770              * Add readout noise for the raw frame, and the errors due
02771              * to bias and dark subtraction, rescaled to the number of
02772              * frames used to create the input frame.
02773              */
02774 
02775             cpl_image_add_scalar(variance, nframes * (ron_variance + nframes *
02776                                  (bias_variance + dark_variance)));
02777 
02778             status = _giraffe_extract_summation(_image, variance, _fibers,
02779                                                 _locy, _locw, bpixmap,
02780                                                 _spectra, _error, _npixels,
02781                                                 _centroid);
02782 
02783             cpl_image_delete(variance);
02784             if (bpixmap != giraffe_image_get(bpixel)) {
02785                 cpl_image_delete(bpixmap);
02786             }
02787             bpixmap = NULL;
02788 
02789             break;
02790 
02791         }
02792 
02793         case GIEXTRACT_OPTIMAL:
02794         {
02795 
02796             cxint xsize = cpl_image_get_size_x(_image);
02797             cxint ysize = cpl_image_get_size_y(_image);
02798 
02799             cxdouble v0 = 0.;
02800             cxdouble ron_variance  = bias_ron * bias_ron;
02801             cxdouble bias_variance = bias_sigma * bias_sigma;
02802             cxdouble dark_variance = dark_value * exptime;
02803 
02804             cpl_image* variance = NULL;
02805             cpl_image* bpixmap = NULL;
02806 
02807             GiExtractOptimalConfig setup;
02808 
02809 
02810             result->spectra = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02811             result->error = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02812             result->npixels = NULL;
02813             result->model = giraffe_image_create(CPL_TYPE_DOUBLE, ny, nx);
02814             result->centroid = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02815 
02816             _spectra = giraffe_image_get(result->spectra);
02817             _error = giraffe_image_get(result->error);
02818             _model = giraffe_image_get(result->model);
02819             _centroid = giraffe_image_get(result->centroid);
02820 
02821             setup.clip.iterations = config->psf.iterations;
02822             setup.clip.level = config->psf.sigma;
02823             setup.clip.fraction = config->optimal.fraction;
02824             setup.limits = config->optimal.wfactor < 0. ? FALSE : TRUE;
02825             setup.ewidth = CX_MAX(1., fabs(config->optimal.wfactor));
02826             setup.bkgorder = config->optimal.bkgorder;
02827             setup.exptime = exptime;
02828             setup.ron = bias_sigma;
02829             setup.dark = dark_value;
02830 
02831 
02832             if (bpixel != NULL) {
02833 
02834                 bpixmap = giraffe_image_get(bpixel);
02835 
02836                 if (cpl_image_get_size_x(bpixmap) != xsize ||
02837                     cpl_image_get_size_y(bpixmap) != ysize) {
02838 
02839                         cxbool crop = FALSE;
02840 
02841                         cpl_propertylist *p =
02842                             giraffe_image_get_properties(bpixel);
02843 
02844                         GiWindow w = {1, 1, 0, 0};
02845 
02846 
02847                         w.x1 = cpl_image_get_size_x(bpixmap);
02848                         w.y1 = cpl_image_get_size_y(bpixmap);
02849 
02850                         if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
02851                             w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
02852                             crop = TRUE;
02853                         }
02854 
02855                         if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
02856                             w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
02857                             crop = TRUE;
02858                         }
02859 
02860                         if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
02861                             w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
02862                             crop = TRUE;
02863                         }
02864 
02865                         if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
02866                             w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
02867                             crop = TRUE;
02868                         }
02869 
02870                         if ((w.x1 - w.x0 + 1) != xsize ||
02871                             (w.y1 - w.y0 + 1) != ysize) {
02872 
02873                                 cpl_msg_error(fctid, "Invalid bad pixel map! "
02874                                     "Image sizes do not match!");
02875 
02876                                 giraffe_image_delete(result->spectra);
02877                                 result->spectra = NULL;
02878 
02879                                 giraffe_image_delete(result->error);
02880                                 result->error = NULL;
02881 
02882                                 giraffe_image_delete(result->npixels);
02883                                 result->npixels = NULL;
02884 
02885                                 giraffe_image_delete(result->centroid);
02886                                 result->centroid = NULL;
02887 
02888                                 giraffe_image_delete(result->model);
02889                                 result->model = NULL;
02890 
02891                                 cpl_image_delete(_image);
02892                                 _image = NULL;
02893 
02894                                 return 1;
02895 
02896                             }
02897 
02898                         if (crop == TRUE) {
02899                             bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
02900                                 w.x1, w.y1);
02901                         }
02902 
02903                     }
02904 
02905             }
02906 
02907             variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
02908 
02909             /*
02910              * Add readout noise for the raw frame, and the errors due
02911              * to bias and dark subtraction, rescaled to the number of
02912              * frames used to create the input frame.
02913              */
02914 
02915             v0 = nframes * (ron_variance + nframes *
02916                     (bias_variance + dark_variance));
02917 
02918 
02919             /*
02920              * If a scattered light map has been used, add its contribution
02921              * to the variance, rescaled to the number of raw frames used, and
02922              * converted to photoelectrons.
02923              */
02924 
02925             if (slight != NULL) {
02926 
02927                 register cxsize i = 0;
02928                 register cxsize npixels = xsize * ysize;
02929 
02930                 const cxdouble* _slight =
02931                     cpl_image_get_data_double(giraffe_image_get(slight));
02932 
02933                 cxdouble* _variance = cpl_image_get_data_double(variance);
02934 
02935                 for (i = 0; i < npixels; i++) {
02936                     _variance[i] = v0 + fabs(_slight[i]) * conad * nframes;
02937                 }
02938 
02939             }
02940             else {
02941 
02942                 register cxsize i = 0;
02943                 register cxsize npixels = xsize * ysize;
02944 
02945                 cxdouble* _variance = cpl_image_get_data_double(variance);
02946 
02947                 for (i = 0; i < npixels; i++) {
02948                     _variance[i] = v0;
02949                 }
02950 
02951             }
02952 
02953 
02954             status = _giraffe_extract_optimal(_image, variance, _fibers,
02955                                               _locy, _locw, sloc->psf,
02956                                               bpixmap, _spectra, _error,
02957                                               _model, _centroid, &setup);
02958 
02959             cpl_image_delete(variance);
02960             variance = NULL;
02961 
02962             if (bpixmap != giraffe_image_get(bpixel)) {
02963                 cpl_image_delete(bpixmap);
02964             }
02965             bpixmap = NULL;
02966 
02967             break;
02968 
02969         }
02970 
02971         case GIEXTRACT_HORNE:
02972         {
02973 
02974             cxint xsize = cpl_image_get_size_x(_image);
02975             cxint ysize = cpl_image_get_size_y(_image);
02976 
02977             cxdouble v0 = 0.;
02978             cxdouble ron_variance  = bias_ron * bias_ron;
02979             cxdouble bias_variance = bias_sigma * bias_sigma;
02980             cxdouble dark_variance = dark_value * exptime;
02981 
02982             cpl_image* variance = NULL;
02983             cpl_image* bpixmap = NULL;
02984 
02985             GiExtractHorneConfig setup;
02986 
02987 
02988             result->spectra = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02989             result->error = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02990             result->npixels = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02991             result->centroid = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
02992             result->model = NULL;
02993 
02994             _spectra = giraffe_image_get(result->spectra);
02995             _error = giraffe_image_get(result->error);
02996             _npixels = giraffe_image_get(result->npixels);
02997             _centroid = giraffe_image_get(result->centroid);
02998 
02999             setup.clip.iterations = config->psf.iterations;
03000             setup.clip.level = config->psf.sigma;
03001             setup.clip.fraction = config->horne.mingood;
03002             setup.ewidth = config->horne.ewidth;
03003             setup.exptime = exptime;
03004             setup.ron = bias_sigma;
03005             setup.dark = dark_value;
03006 
03007             if (bpixel != NULL) {
03008 
03009                 bpixmap = giraffe_image_get(bpixel);
03010 
03011                 if (cpl_image_get_size_x(bpixmap) != xsize ||
03012                     cpl_image_get_size_y(bpixmap) != ysize) {
03013 
03014                     cxbool crop = FALSE;
03015 
03016                     cpl_propertylist *p =
03017                         giraffe_image_get_properties(bpixel);
03018 
03019                     GiWindow w = {1, 1, 0, 0};
03020 
03021 
03022                     w.x1 = cpl_image_get_size_x(bpixmap);
03023                     w.y1 = cpl_image_get_size_y(bpixmap);
03024 
03025                     if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
03026                         w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
03027                         crop = TRUE;
03028                     }
03029 
03030                     if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
03031                         w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
03032                         crop = TRUE;
03033                     }
03034 
03035                     if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
03036                         w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
03037                         crop = TRUE;
03038                     }
03039 
03040                     if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
03041                         w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
03042                         crop = TRUE;
03043                     }
03044 
03045                     if ((w.x1 - w.x0 + 1) != xsize ||
03046                         (w.y1 - w.y0 + 1) != ysize) {
03047 
03048                         cpl_msg_error(fctid, "Invalid bad pixel map! "
03049                                         "Image sizes do not match!");
03050 
03051                         giraffe_image_delete(result->spectra);
03052                         result->spectra = NULL;
03053 
03054                         giraffe_image_delete(result->error);
03055                         result->error = NULL;
03056 
03057                         giraffe_image_delete(result->npixels);
03058                         result->npixels = NULL;
03059 
03060                         giraffe_image_delete(result->centroid);
03061                         result->centroid = NULL;
03062 
03063                         giraffe_image_delete(result->model);
03064                         result->model = NULL;
03065 
03066                         cpl_image_delete(_image);
03067                         _image = NULL;
03068 
03069                         return 1;
03070 
03071                     }
03072 
03073                     if (crop == TRUE) {
03074                         bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
03075                             w.x1, w.y1);
03076                     }
03077 
03078                 }
03079 
03080             }
03081 
03082             variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
03083 
03084             /*
03085              * Add readout noise for the raw frame, and the errors due
03086              * to bias and dark subtraction, rescaled to the number of
03087              * frames used to create the input frame.
03088              */
03089 
03090             v0 = nframes * (ron_variance + nframes *
03091                     (bias_variance + dark_variance));
03092 
03093 
03094             /*
03095              * If a scattered light map has been used, add its contribution
03096              * to the variance, rescaled to the number of raw frames used, and
03097              * converted to photoelectrons.
03098              */
03099 
03100 
03101             if (slight != NULL) {
03102 
03103                 register cxsize i = 0;
03104                 register cxsize npixels = xsize * ysize;
03105 
03106                 const cxdouble* _slight =
03107                     cpl_image_get_data_double(giraffe_image_get(slight));
03108 
03109                 cxdouble* _variance = cpl_image_get_data_double(variance);
03110 
03111                 for (i = 0; i < npixels; i++) {
03112                     _variance[i] = v0 + fabs(_slight[i]) * nframes * conad;
03113                 }
03114 
03115             }
03116             else {
03117 
03118                 register cxsize i = 0;
03119                 register cxsize npixels = xsize * ysize;
03120 
03121                 cxdouble* _variance = cpl_image_get_data_double(variance);
03122 
03123                 for (i = 0; i < npixels; i++) {
03124                     _variance[i] = v0;
03125                 }
03126 
03127             }
03128 
03129 
03130             status = _giraffe_extract_horne(_image, variance, _fibers,
03131                                             _locy, _locw, sloc->psf,
03132                                             bpixmap, _spectra, _error,
03133                                             _npixels, _centroid, &setup);
03134 
03135             cpl_image_delete(variance);
03136             variance = NULL;
03137 
03138             if (bpixmap != giraffe_image_get(bpixel)) {
03139                 cpl_image_delete(bpixmap);
03140             }
03141             bpixmap = NULL;
03142 
03143             break;
03144 
03145         }
03146 
03147         default:
03148             gi_message("%s: Method %d selected for spectrum extraction.",
03149                        fctid, config->emethod);
03150             cpl_msg_error(fctid, "Invalid extraction method!");
03151 
03152             status = 1;
03153             break;
03154     }
03155 
03156     cpl_image_delete(_image);
03157     _image = NULL;
03158 
03159     if (status) {
03160 
03161         giraffe_image_delete(result->spectra);
03162         result->spectra = NULL;
03163 
03164         giraffe_image_delete(result->error);
03165         result->error = NULL;
03166 
03167         giraffe_image_delete(result->npixels);
03168         result->npixels = NULL;
03169 
03170         giraffe_image_delete(result->centroid);
03171         result->centroid = NULL;
03172 
03173         giraffe_image_delete(result->model);
03174         result->model = NULL;
03175 
03176         cpl_msg_error(fctid, "Spectrum extraction (method %d) failed!",
03177                       config->emethod);
03178 
03179         cpl_image_delete(_image);
03180         _image = NULL;
03181 
03182         return 1;
03183 
03184     }
03185 
03186 
03187     /*
03188      * Postprocessing
03189      */
03190 
03191 
03192     /*
03193      * Rescale the spectrum extraction products to the original, averaged
03194      * input raw frame.
03195      */
03196 
03197     if (result->spectra) {
03198         cpl_image_divide_scalar(giraffe_image_get(result->spectra),
03199                                 nframes * conad);
03200     }
03201 
03202     if (result->model) {
03203         cpl_image_divide_scalar(giraffe_image_get(result->model),
03204                                 nframes * conad);
03205     }
03206 
03207     if (result->error) {
03208         cpl_image_divide_scalar(giraffe_image_get(result->error),
03209                                 nframes * conad);
03210     }
03211 
03212 
03213     /*
03214      * Extracted spectra frame
03215      */
03216 
03217     properties = giraffe_image_get_properties(image);
03218     giraffe_image_set_properties(result->spectra, properties);
03219 
03220     properties = giraffe_image_get_properties(result->spectra);
03221 
03222     /*
03223      * Copy some properties from the localization frame.
03224      */
03225 
03226     // FIXME: Is this really needed? (RP)
03227 
03228     giraffe_propertylist_update(properties,
03229                                 giraffe_image_get_properties(sloc->locy),
03230                                 "^ESO PRO LOC.*");
03231 
03232     cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
03233                              cpl_image_get_size_x(_spectra));
03234     cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
03235                              cpl_image_get_size_y(_spectra));
03236 
03237     cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
03238     cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
03239     cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
03240 
03241     cpl_propertylist_update_int(properties, GIALIAS_NFIBERS,
03242                                 cpl_image_get_size_x(_spectra));
03243 
03244     cpl_propertylist_append_int(properties, GIALIAS_EXT_NX,
03245                                 cpl_image_get_size_y(_spectra));
03246     cpl_propertylist_append_int(properties, GIALIAS_EXT_NS,
03247                                 cpl_image_get_size_x(_spectra));
03248 
03249     switch (config->emethod) {
03250         case GIEXTRACT_SUM:
03251             cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
03252                                            "SUM");
03253             cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
03254                                          "Spectrum extraction method");
03255         break;
03256 
03257         case GIEXTRACT_HORNE:
03258         {
03259 
03260             cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
03261                                            "HORNE");
03262             cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
03263                                          "Spectrum extraction method");
03264 
03265             cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
03266                                            config->psf.model);
03267             cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
03268                                          "PSF model used");
03269             cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
03270                                            config->psf.sigma);
03271             cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
03272                                          "PSF fit sigma clipping threshold");
03273             cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
03274                                         config->psf.iterations);
03275             cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
03276                                          "PSF fit maximum number of "
03277                                          "iterations");
03278 
03279             cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_EWIDTH,
03280                                         config->horne.ewidth);
03281             cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_EWIDTH,
03282                                          "Number of extra pixels used");
03283             cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_MINGOOD,
03284                                            config->horne.mingood);
03285             cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_MINGOOD,
03286                                          "Minimum number of pixels to keep");
03287 
03288 
03289             break;
03290         }
03291 
03292         case GIEXTRACT_OPTIMAL:
03293             cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
03294                                            "OPTIMAL");
03295             cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
03296                                          "Spectrum extraction method");
03297 
03298             cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
03299                                            config->psf.model);
03300             cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
03301                                          "PSF model used");
03302             cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
03303                                            config->psf.sigma);
03304             cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
03305                                          "PSF fit sigma clipping threshold");
03306             cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
03307                                         config->psf.iterations);
03308             cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
03309                                          "PSF fit maximum number of "
03310                                          "iterations");
03311 
03312             cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_FRACTION,
03313                                         config->optimal.fraction);
03314             cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_FRACTION,
03315                                          "Minimum fraction of pixels used.");
03316             cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_WFACTOR,
03317                                            config->optimal.wfactor);
03318             cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_WFACTOR,
03319                                          "Multiple of the fiber PSF half "
03320                                          "width used for spectrum "
03321                                          "extraction.");
03322             cpl_propertylist_append_int(properties, GIALIAS_EXTOPT_BGORDER,
03323                                         config->optimal.bkgorder);
03324             cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_BGORDER,
03325                                          "Order of the background polynomial "
03326                                          "model along the spatial direction.");
03327 
03328             break;
03329 
03330         default:
03331             break;
03332     }
03333 
03334     cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE, "EXTSP");
03335     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
03336                                  "Extracted spectra");
03337 
03338 
03339     /*
03340      * Extracted spectra errors frame
03341      */
03342 
03343     giraffe_image_set_properties(result->error, properties);
03344     properties = giraffe_image_get_properties(result->error);
03345 
03346     cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "EXTERRS");
03347     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
03348                                  "Extracted spectra errors");
03349 
03350 
03351     /*
03352      * Extracted spectra centroids frame
03353      */
03354 
03355     giraffe_image_set_properties(result->centroid, properties);
03356     properties = giraffe_image_get_properties(result->centroid);
03357 
03358     cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "EXTYCEN");
03359     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
03360                                  "Extracted spectra centroids");
03361 
03362 
03363     /*
03364      * Extracted spectra npixels frame
03365      */
03366 
03367     if (result->npixels != NULL) {
03368         giraffe_image_set_properties(result->npixels, properties);
03369         properties = giraffe_image_get_properties(result->npixels);
03370 
03371         cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "EXTNPIX");
03372         cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
03373                                     "Extracted spectra npixels");
03374     }
03375 
03376 
03377     /*
03378      * Model spectra frame
03379      */
03380 
03381     if (result->model != NULL) {
03382         giraffe_image_set_properties(result->model, properties);
03383         properties = giraffe_image_get_properties(result->model);
03384 
03385         cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "EXTMODEL");
03386         cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
03387                                      "Model spectra used for extraction");
03388     }
03389 
03390     return 0;
03391 
03392 }
03393 
03394 
03405 GiExtractConfig*
03406 giraffe_extract_config_create(cpl_parameterlist* list)
03407 {
03408 
03409     const cxchar* s;
03410     cpl_parameter* p;
03411 
03412     GiExtractConfig* config = NULL;
03413 
03414 
03415     if (!list) {
03416         return NULL;
03417     }
03418 
03419     config = cx_calloc(1, sizeof *config);
03420 
03421     p = cpl_parameterlist_find(list, "giraffe.extraction.method");
03422     s = cpl_parameter_get_string(p);
03423     if (!strcmp(s, "OPTIMAL")) {
03424         config->emethod = GIEXTRACT_OPTIMAL;
03425     }
03426     else if (!strcmp(s, "HORNE")) {
03427         config->emethod = GIEXTRACT_HORNE;
03428     }
03429     else {
03430         config->emethod = GIEXTRACT_SUM;
03431     }
03432 
03433     p = cpl_parameterlist_find(list, "giraffe.extraction.ron");
03434     config->ron = cpl_parameter_get_double(p);
03435 
03436     p = cpl_parameterlist_find(list, "giraffe.extraction.psf.model");
03437     config->psf.model = cx_strdup(cpl_parameter_get_string(p));
03438 
03439     p = cpl_parameterlist_find(list, "giraffe.extraction.psf.sigma");
03440     config->psf.sigma = cpl_parameter_get_double(p);
03441 
03442     p = cpl_parameterlist_find(list, "giraffe.extraction.psf.iterations");
03443     config->psf.iterations = cpl_parameter_get_int(p);
03444 
03445 
03446     p = cpl_parameterlist_find(list, "giraffe.extraction.horne.extrawidth");
03447     config->horne.ewidth = cpl_parameter_get_int(p);
03448 
03449     p = cpl_parameterlist_find(list, "giraffe.extraction.horne.mingood");
03450     config->horne.mingood = cpl_parameter_get_double(p);
03451 
03452 
03453     p = cpl_parameterlist_find(list, "giraffe.extraction.optimal.fraction");
03454     config->optimal.fraction = cpl_parameter_get_double(p);
03455 
03456     p = cpl_parameterlist_find(list, "giraffe.extraction.optimal.wfactor");
03457     config->optimal.wfactor = cpl_parameter_get_double(p);
03458 
03459     p = cpl_parameterlist_find(list, "giraffe.extraction.optimal.bkgorder");
03460     config->optimal.bkgorder = cpl_parameter_get_int(p);
03461 
03462     return config;
03463 
03464 }
03465 
03466 
03479 void
03480 giraffe_extract_config_destroy(GiExtractConfig* config)
03481 {
03482 
03483     if (config) {
03484 
03485         if (config->psf.model) {
03486             cx_free(config->psf.model);
03487         }
03488 
03489         cx_free(config);
03490 
03491     }
03492 
03493     return;
03494 
03495 }
03496 
03497 
03509 void
03510 giraffe_extract_config_add(cpl_parameterlist* list)
03511 {
03512 
03513     cpl_parameter* p = NULL;
03514 
03515 
03516     if (list == NULL) {
03517         return;
03518     }
03519 
03520     p = cpl_parameter_new_enum("giraffe.extraction.method",
03521                                CPL_TYPE_STRING,
03522                                "Extraction method: 'SUM', 'HORNE' or "
03523                                "'OPTIMAL'",
03524                                "giraffe.extraction",
03525                                "SUM", 3, "SUM", "OPTIMAL", "HORNE");
03526     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-method");
03527     cpl_parameterlist_append(list, p);
03528 
03529 
03530     p = cpl_parameter_new_value("giraffe.extraction.ron",
03531                                 CPL_TYPE_DOUBLE,
03532                                 "New bias sigma (RON) value for "
03533                                 "bias and dark "
03534                                 "corrected image",
03535                                 "giraffe.extraction",
03536                                 -1.);
03537     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-ron");
03538     cpl_parameterlist_append(list, p);
03539 
03540 
03541     p = cpl_parameter_new_enum("giraffe.extraction.psf.model",
03542                                CPL_TYPE_STRING,
03543                                "PSF profile model: `psfexp', `psfexp2'",
03544                                "giraffe.extraction.psf",
03545                                "psfexp2", 2, "psfexp", "psfexp2");
03546     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-psfmodel");
03547     cpl_parameterlist_append(list, p);
03548 
03549 
03550     p = cpl_parameter_new_value("giraffe.extraction.psf.sigma",
03551                                 CPL_TYPE_DOUBLE,
03552                                 "Sigma clippging threshold used for "
03553                                 "rejecting data points during PSF fitting "
03554                                 "(Horne's sigma). It is used to reject bad "
03555                                 "pixels and cosmics.",
03556                                 "giraffe.extraction.psf",
03557                                 7.);
03558     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-psfsigma");
03559     cpl_parameterlist_append(list, p);
03560 
03561 
03562     p = cpl_parameter_new_value("giraffe.extraction.psf.iterations",
03563                                 CPL_TYPE_INT,
03564                                 "Maximum number of iterations used for "
03565                                 "fitting the PSF profile.",
03566                                 "giraffe.extraction.psf",
03567                                 2);
03568     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-psfniter");
03569     cpl_parameterlist_append(list, p);
03570 
03571 
03572     p = cpl_parameter_new_value("giraffe.extraction.horne.extrawidth",
03573                                 CPL_TYPE_INT,
03574                                 "Horne extraction method: Number of "
03575                                 "extra pixels added to the fiber "
03576                                 "half-width.",
03577                                 "giraffe.extraction.horne",
03578                                 2);
03579     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-hewidth");
03580     cpl_parameterlist_append(list, p);
03581 
03582 
03583     p = cpl_parameter_new_value("giraffe.extraction.horne.mingood",
03584                                 CPL_TYPE_INT,
03585                                 "Horne extraction method: Minimum number of "
03586                                 "points used for the profile fit. It sets "
03587                                 "the lower limit of data points for the "
03588                                 "pixel rejection.",
03589                                 "giraffe.extraction.horne",
03590                                 3);
03591     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-hmingood");
03592     cpl_parameterlist_append(list, p);
03593 
03594 
03595     p = cpl_parameter_new_range("giraffe.extraction.optimal.fraction",
03596                                 CPL_TYPE_DOUBLE,
03597                                 "Optimal extraction method: Minimum fraction "
03598                                 "of the data points used for fitting the "
03599                                 "fiber profiles. It sets the lower limit "
03600                                 "for the pixel rejection.",
03601                                 "giraffe.extraction.optimal",
03602                                 0.9, 0.0, 1.0);
03603     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-omfrac");
03604     cpl_parameterlist_append(list, p);
03605 
03606 
03607     p = cpl_parameter_new_value("giraffe.extraction.optimal.wfactor",
03608                                 CPL_TYPE_DOUBLE,
03609                                 "Optimal extraction method: Factor by which "
03610                                 "the fiber PSF half width is multiplied. "
03611                                 "Adjacent spectra within this area are "
03612                                 "assumed to affect the spectrum being "
03613                                 "extracted.",
03614                                 "giraffe.extraction.optimal",
03615                                 3.);
03616     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-owfactor");
03617     cpl_parameterlist_append(list, p);
03618 
03619 
03620     p = cpl_parameter_new_value("giraffe.extraction.optimal.bkgorder",
03621                                 CPL_TYPE_INT,
03622                                 "Optimal extraction method: Order of the "
03623                                 "polynomial background model, which is "
03624                                 "fitted for each wavelength bin along the "
03625                                 "spatial direction.",
03626                                 "giraffe.extraction.optimal",
03627                                 2);
03628     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-obkgorder");
03629     cpl_parameterlist_append(list, p);
03630 
03631 
03632     return;
03633 
03634 }

This file is part of the GIRAFFE Pipeline Reference Manual 2.11.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Mon Apr 29 09:40:54 2013 by doxygen 1.4.7 written by Dimitri van Heesch, © 1997-2004