GIRAFFE Pipeline Reference Manual

giextract.c

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

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