GIRAFFE Pipeline Reference Manual

giextract.c

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Mar 7 14:11:02 2013 by doxygen 1.4.7 written by Dimitri van Heesch, © 1997-2004