GIRAFFE Pipeline Reference Manual

giextract.c

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.8.8.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Mar 4 10:50:26 2011 by doxygen 1.6.3 written by Dimitri van Heesch, © 1997-2004