GIRAFFE Pipeline Reference Manual

giwlcalibration.c

00001 /* $Id: giwlcalibration.c,v 1.49 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.49 $
00025  * $Name: giraffe-2_10 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #  include <config.h>
00030 #endif
00031 
00032 #include <stdlib.h>
00033 #include <math.h>
00034 
00035 #include <cxmemory.h>
00036 #include <cxstring.h>
00037 #include <cxstrutils.h>
00038 
00039 #include <cpl_error.h>
00040 #include <cpl_msg.h>
00041 
00042 #include "gimacros.h"
00043 #include "gialias.h"
00044 #include "gimatrix.h"
00045 #include "gigrating.h"
00046 #include "gimodel.h"
00047 #include "gilinedata.h"
00048 #include "giwlsolution.h"
00049 #include "gimath.h"
00050 #include "gimessages.h"
00051 #include "gifiberutils.h"
00052 #include "giclip.h"
00053 #include "giwlcalibration.h"
00054 
00055 
00064 /*
00065  * The line type is currently not used. Only ThArNe catalogs will be
00066  * used. This is present as a placeholder, and because it was foreseen in
00067  * the original OGL version.
00068  */
00069 
00070 enum GiLineType {
00071     GI_LINETYPE_UNDEFINED,  /* Undefined */
00072     GI_LINETYPE_THARNE,     /* Use ThArNe lines */
00073     GI_LINETYPE_TELLURIC    /* Use telluric lines */
00074 };
00075 
00076 typedef enum GiLineType GiLineType;
00077 
00078 
00079 /*
00080  * Line status flags (rejection codes) used by the line fit.
00081  */
00082 
00083 enum {
00084     LF_R_NONE  = 0x0000,  /* line is ok */
00085     LF_R_AMPLI = 0x0001,  /* too small or saturated */
00086     LF_R_NITER = 0x0002,  /* max number of iteration reached */
00087     LF_R_CENTR = 0x0004,  /* line center out of window */
00088     LF_R_WIDTH = 0x0008,  /* line width larger than window */
00089     LF_R_LEFT  = 0x0010,  /* line out of window (leftside) */
00090     LF_R_RIGHT = 0x0020,  /* line out of window (rightside) */
00091     LF_R_OFFST = 0x0040,  /* maximum too far from original guess */
00092     LF_R_BADLN = 0x0080,  /* maximum too far from window center */
00093     LF_R_ERROR = 0x0100,  /* error in fit */
00094     LF_R_PSFIT = 0x0200,  /* rejected by fit of PSF width */
00095     LF_R_XRFIT = 0x0400,  /* rejected by fit of X residuals */
00096     LF_R_RESOL = 0x0800,  /* width too small for resolution */
00097     LF_R_XCCD  = 0x1000   /* out of CCD */
00098 };
00099 
00100 
00101 /*
00102  * Slit offset configuration masks
00103  */
00104 
00105 enum {
00106     SLIT_DX  = 0x0001,  /* Slit offset along x was set */
00107     SLIT_DY  = 0x0002,  /* Slit offset along y was set */
00108     SLIT_PHI = 0x0004,  /* Slit rotation offset was set */
00109 };
00110 
00111 
00112 /*
00113  * Optical model parameter flags
00114  */
00115 
00116 enum {
00117     OPTM_FLENGTH = 1 << 0,
00118     OPTM_GCAMERA = 1 << 1,
00119     OPTM_THETA   = 1 << 2,
00120     OPTM_SX      = 1 << 3,
00121     OPTM_SY      = 1 << 4,
00122     OPTM_SPHI    = 1 << 5
00123 };
00124 
00125 
00126 /*
00127  * Optical model info flags
00128  */
00129 
00130 enum GiOpticalModelInfo {
00131     GI_OPTM_PARAMETER_VALUES,
00132     GI_OPTM_PARAMETER_ERRORS,
00133     GI_OPTM_PARAMETER_STATUS
00134 };
00135 
00136 typedef enum GiOpticalModelInfo GiOpticalModelInfo;
00137 
00138 
00139 /*
00140  * The line fit setup parameters definition.
00141  */
00142 
00143 struct GiLineParams {
00144 
00145     const cxchar *model;
00146 
00147     GiLineType type;
00148 
00149     cxdouble grwid;
00150     cxdouble satlv;
00151     cxdouble thres;
00152     cxdouble offst;
00153     cxdouble wfact;
00154     cxdouble psfexp;
00155 
00156     GiFitSetup fit;
00157 
00158 };
00159 
00160 typedef struct GiLineParams GiLineParams;
00161 
00162 
00163 /*
00164  * Optical model fit setup parameter definition.
00165  */
00166 
00167 struct GiOpticalModelParams {
00168 
00169     GiFitSetup fit;
00170     cxint16 flags;
00171 
00172 };
00173 
00174 typedef struct GiOpticalModelParams GiOpticalModelParams;
00175 
00176 
00177 /*
00178  * PSF fit setup information
00179  */
00180 
00181 struct GiSCFitParams {
00182 
00183     cxbool subslits;
00184 
00185     struct {
00186         cxint xorder;
00187         cxint yorder;
00188     } fit;
00189 
00190     GiClipParams clip;
00191 
00192 };
00193 
00194 typedef struct GiSCFitParams GiSCFitParams;
00195 
00196 
00197 struct GiWCalInfo {
00198 
00199     cxint width;
00200 
00201     cxbool residuals;
00202 
00203     cxint nlines;
00204     cxint nfibers;
00205 
00206     cxint ngood;
00207     cxint nreject;
00208 
00209     cxdouble rms;
00210 
00211 };
00212 
00213 typedef struct GiWCalInfo GiWCalInfo;
00214 
00215 
00216 inline static cxint
00217 _giraffe_window_compare(cxcptr first, cxcptr second)
00218 {
00219 
00220     cxint *_first = (cxint *)first;
00221     cxint *_second = (cxint *)second;
00222 
00223     return *_second - *_first;
00224 
00225 }
00226 
00227 
00228 inline static GiLineParams *
00229 _giraffe_lineparams_create(GiLineType type, const GiGrating *grating,
00230                            const GiWCalConfig *config)
00231 {
00232 
00233     GiLineParams *self = NULL;
00234 
00235 
00236     cx_assert(grating != NULL);
00237     cx_assert(config != NULL);
00238 
00239     self = cx_calloc(1, sizeof(GiLineParams));
00240 
00241     self->model = cx_strdup(config->line_model);
00242     self->type = type;
00243 
00244     /*
00245      * Estimated line FWHM per pixel at the central wavelength computed
00246      * from basic grating data:
00247      *    FWHM = npixel / bandwidth * lambda0 / resolution
00248      */
00249 
00250     self->grwid = 1. / grating->band * (grating->wlen0 / grating->resol);
00251     self->satlv = config->line_saturation;
00252     self->thres = config->line_threshold;
00253     self->offst = config->line_offset;
00254     self->wfact = config->line_rwidthratio;
00255 
00256     self->psfexp = config->line_widthexponent;
00257 
00258     self->fit.iterations = config->line_niter;
00259     self->fit.tests = config->line_ntest;
00260     self->fit.delta = config->line_dchisq;
00261 
00262     return self;
00263 
00264 }
00265 
00266 
00267 inline static void
00268 _giraffe_lineparams_delete(GiLineParams *self)
00269 {
00270 
00271     if (self) {
00272 
00273         if (self->model) {
00274             cx_free((cxptr)self->model);
00275         }
00276 
00277         cx_free(self);
00278 
00279     }
00280 
00281     return;
00282 
00283 }
00284 
00285 
00286 inline static cxdouble
00287 _giraffe_get_fiber_position(const cpl_image *locy, cxint cs, cxdouble xccd)
00288 {
00289 
00290     cxint xlower = (cxint)floor(xccd);
00291     cxint xupper = (cxint)ceil(xccd);
00292 
00293     const cxdouble *ldata = cpl_image_get_data_const(locy);
00294 
00295     cxdouble ylower = 0.;
00296     cxdouble yupper = 0.;
00297 
00298 
00299     cx_assert(ldata != NULL);
00300 
00301     ylower = ldata[xlower * cpl_image_get_size_x(locy) + cs];
00302     yupper = ldata[xupper * cpl_image_get_size_x(locy) + cs];
00303 
00304     return giraffe_interpolate_linear(xccd, xlower, ylower, xupper, yupper);
00305 
00306 }
00307 
00308 
00309 inline static cxint
00310 _giraffe_subslit_get_max(const cpl_table *fibers)
00311 {
00312 
00313     return cpl_table_get_column_max((cpl_table *)fibers, "SSN");
00314 
00315 }
00316 
00317 
00318 inline static cpl_table *
00319 _giraffe_subslit_get(const cpl_table *fibers, cxint ssn)
00320 {
00321 
00322     cxint ssn_max = 0;
00323 
00324     cpl_table *_fibers;
00325 
00326 
00327     cx_assert(fibers != NULL);
00328     cx_assert(cpl_table_has_column((cpl_table *)fibers, "SSN"));
00329 
00330     ssn_max = _giraffe_subslit_get_max(fibers);
00331 
00332     if (ssn < 0 || ssn > ssn_max) {
00333         return NULL;
00334     }
00335 
00336     cpl_table_unselect_all((cpl_table *)fibers);
00337     cpl_table_or_selected_int((cpl_table *)fibers, "SSN", CPL_EQUAL_TO, ssn);
00338 
00339     _fibers = cpl_table_extract_selected((cpl_table *)fibers);
00340 
00341     return _fibers;
00342 
00343 }
00344 
00345 
00346 inline static cxint
00347 _giraffe_subslit_range(const cpl_table *subslit, const cpl_image *locy,
00348                        const cpl_image *locw, cxdouble *ymin, cxdouble *ymax)
00349 {
00350 
00351     const cxchar *idx = NULL;
00352 
00353     cxint i;
00354     cxint ns = 0;
00355     cxint nx = 0;
00356 
00357     const cxdouble *_locy = NULL;
00358     const cxdouble *_locw = NULL;
00359 
00360     cxdouble _ymin = CX_MAXDOUBLE;
00361     cxdouble _ymax = 0.;
00362 
00363     cx_assert(subslit != NULL);
00364     cx_assert(locy != NULL);
00365     cx_assert(locw != NULL);
00366 
00367     idx = giraffe_fiberlist_query_index(subslit);
00368 
00369     ns = cpl_image_get_size_x(locy);
00370     nx = cpl_image_get_size_y(locy);
00371 
00372     _locy = cpl_image_get_data_const(locy);
00373     _locw = cpl_image_get_data_const(locw);
00374 
00375     for (i = 0; i < cpl_table_get_nrow((cpl_table *)subslit); i++) {
00376 
00377         cxint j;
00378         cxint cs = cpl_table_get_int((cpl_table *)subslit, idx, i, NULL) - 1;
00379 
00380         for (j = 0; j < nx; j++) {
00381 
00382             register cxint k = j * ns + cs;
00383 
00384             cxdouble ylower = _locy[k] - _locw[k];
00385             cxdouble yupper = _locy[k] + _locw[k];
00386 
00387             _ymin = CX_MIN(_ymin, ylower);
00388             _ymax = CX_MAX(_ymax, yupper);
00389 
00390         }
00391 
00392     }
00393 
00394     if (_ymin > _ymax) {
00395         return 1;
00396     }
00397 
00398     if (ymin != NULL) {
00399         *ymin = _ymin;
00400     }
00401 
00402     if (ymax != NULL) {
00403         *ymax = _ymax;
00404     }
00405 
00406     return 0;
00407 
00408 }
00409 
00410 
00411 inline static cxint
00412 _giraffe_get_residuals(cpl_image *residuals, const cpl_image *positions,
00413                        const cpl_image *fit)
00414 {
00415 
00416     cxint i;
00417     cxint nfibers = 0;
00418     cxint nlines = 0;
00419     cxint nx = 0;
00420 
00421     const cxdouble *_positions = NULL;
00422     const cxdouble *_fit = NULL;
00423 
00424     cxdouble *_residuals = NULL;
00425 
00426 
00427     cx_assert(residuals != NULL);
00428     cx_assert(positions != NULL);
00429     cx_assert(fit != NULL);
00430 
00431     nfibers = cpl_image_get_size_x(positions);
00432     nlines = cpl_image_get_size_y(positions);
00433     nx = cpl_image_get_size_y(fit);
00434 
00435     cx_assert(nfibers == cpl_image_get_size_x(residuals));
00436     cx_assert(nlines == cpl_image_get_size_y(residuals));
00437 
00438     _residuals = cpl_image_get_data(residuals);
00439     _positions = cpl_image_get_data_const(positions);
00440     _fit = cpl_image_get_data_const(fit);
00441 
00442     for (i = 0; i < nlines; i++) {
00443 
00444         register cxint j;
00445 
00446         for (j = 0; j < nfibers; j++) {
00447 
00448             register cxdouble line_pos = _positions[i * nfibers + j];
00449 
00450             line_pos = CX_MIN(CX_MAX(line_pos, 0.), nx - 1);
00451             _residuals[i * nfibers + j] = _fit[(cxint)line_pos * nfibers + j];
00452 
00453         }
00454 
00455     }
00456 
00457     return 0;
00458 
00459 }
00460 
00461 
00462 inline static cxint
00463 _giraffe_apply_residuals(cpl_image *xccd, const cpl_image *residuals,
00464                          const cpl_image *lflags, cxdouble value)
00465 {
00466 
00467     cx_assert(xccd != NULL);
00468     cx_assert(residuals != NULL);
00469 
00470     cpl_image_subtract(xccd, residuals);
00471 
00472     if (lflags != NULL) {
00473 
00474         const cxint *_lflags = cpl_image_get_data_const(lflags);
00475 
00476         cxint i;
00477         cxint nfibers = cpl_image_get_size_x(xccd);
00478         cxint nlines = cpl_image_get_size_y(xccd);
00479 
00480         cxdouble *_xccd = cpl_image_get_data(xccd);
00481 
00482 
00483         cx_assert(nfibers == cpl_image_get_size_x(lflags));
00484         cx_assert(nlines == cpl_image_get_size_y(lflags));
00485 
00486         for (i = 0; i < nlines; i++) {
00487 
00488             cxint j;
00489 
00490             for (j = 0; j < nfibers; j++) {
00491 
00492                 if (_lflags[i * nfibers + j] > 0) {
00493                     _xccd[i * nfibers + j] = value;
00494                 }
00495 
00496             }
00497 
00498         }
00499 
00500     }
00501 
00502     return 0;
00503 
00504 }
00505 
00506 
00507 inline static cxint
00508 _giraffe_linelist_setup(GiTable *lines, GiGrating *grating,
00509                         const GiWCalConfig *config)
00510 {
00511 
00512     const cxchar *const fctid = "_giraffe_linelist_setup";
00513 
00514 
00515     const cxdouble fraction = 500.;
00516 
00517     cxint nlines = 0;
00518     cxint nreject = 0;
00519     cxint status = 0;
00520 
00521     cxdouble wlmin = 0.;
00522     cxdouble wlmax = 0.;
00523     cxdouble margin = 0.;
00524 
00525     cpl_table *_lines = NULL;
00526 
00527 
00528 
00529     cx_assert(lines != NULL);
00530     cx_assert(grating != NULL);
00531     cx_assert(config != NULL);
00532 
00533 
00534     _lines = giraffe_table_get(lines);
00535 
00536     if (_lines == NULL) {
00537         return 1;
00538     }
00539 
00540     if (!cpl_table_has_column(_lines, "WLEN") ||
00541         !cpl_table_has_column(_lines, "FLUX")) {
00542         return 2;
00543     }
00544 
00545 
00546     /*
00547      * Remove lines outside of the given wavelength range taking a safety
00548      * margin into account.
00549      */
00550 
00551     nlines = cpl_table_get_nrow(_lines);
00552     cpl_table_unselect_all(_lines);
00553 
00554     wlmin = grating->wlenmin;
00555     wlmax = grating->wlenmax;
00556 
00557     if (giraffe_range_get_min(config->line_wlrange) > 0.) {
00558         wlmin = giraffe_range_get_min(config->line_wlrange);
00559         grating->wlenmin = wlmin;
00560     }
00561 
00562     if (giraffe_range_get_max(config->line_wlrange) > 0.) {
00563         wlmax = giraffe_range_get_max(config->line_wlrange);
00564         grating->wlenmax = wlmax;
00565     }
00566 
00567     margin = (wlmax - wlmin) / fraction;
00568 
00569     cpl_msg_debug(fctid, "Selecting wavelength range [%.4f, %.4f[ [nm] with "
00570                   "margin %.4f nm.", wlmin, wlmax, margin);
00571 
00572     cpl_table_or_selected_double(_lines, "WLEN", CPL_LESS_THAN,
00573                                  wlmin + margin);
00574     cpl_table_or_selected_double(_lines, "WLEN", CPL_NOT_LESS_THAN,
00575                                  wlmax - margin);
00576 
00577     cpl_table_erase_selected(_lines);
00578 
00579     if (cpl_table_get_nrow(_lines) <= 0) {
00580         cpl_msg_debug(fctid, "Invalid line list! All lines have been "
00581                       "rejected!");
00582         return -1;
00583     }
00584 
00585     nreject = nlines - cpl_table_get_nrow(_lines);
00586     cpl_msg_debug(fctid, "%d of %d lines rejected because of wavelength "
00587                   "range.", nreject, nlines);
00588 
00589 
00590     /*
00591      * Apply brightness criteria
00592      */
00593 
00594     nlines = cpl_table_get_nrow(_lines);
00595 
00596     if (config->line_count != 0) {
00597 
00598         cxint i;
00599         cxint line_count = abs(config->line_count);
00600 
00601         cpl_propertylist *sorting_order = NULL;
00602 
00603 
00604         if (line_count > nlines) {
00605             cpl_msg_debug(fctid, "Too few lines in line list for brightness "
00606                           "selection!");
00607 
00608             if (config->line_count > 0) {
00609                 return 3;
00610             }
00611             else {
00612                 cpl_msg_debug(fctid, "Skipping brightness selection!");
00613                 line_count = nlines;
00614             }
00615         }
00616 
00617         sorting_order = cpl_propertylist_new();
00618         cpl_propertylist_append_bool(sorting_order, "FLUX", 1);
00619 
00620         cpl_table_sort(_lines, sorting_order);
00621 
00622         cpl_propertylist_delete(sorting_order);
00623         sorting_order = NULL;
00624 
00625         cpl_table_select_all(_lines);
00626 
00627         for (i = 0; i < line_count; i++) {
00628             cpl_table_unselect_row(_lines, i);
00629         }
00630 
00631         status = cpl_table_erase_selected(_lines);
00632 
00633         if (cpl_table_get_nrow(_lines) <= 0) {
00634             return -1;
00635         }
00636 
00637         sorting_order = cpl_propertylist_new();
00638         cpl_propertylist_append_bool(sorting_order, "WLEN", 0);
00639 
00640         cpl_table_sort(_lines, sorting_order);
00641 
00642         cpl_propertylist_delete(sorting_order);
00643         sorting_order = NULL;
00644 
00645     }
00646 
00647     if (config->line_brightness > 0.) {
00648 
00649         cpl_table_select_all(_lines);
00650         cpl_table_and_selected_double(_lines, "FLUX", CPL_NOT_GREATER_THAN,
00651                                       config->line_brightness);
00652 
00653         cpl_table_erase_selected(_lines);
00654 
00655         if (cpl_table_get_nrow(_lines) <= 0) {
00656             cpl_msg_debug(fctid, "Invalid line brightness! All lines have "
00657                           "been rejected!");
00658             return -2;
00659         }
00660 
00661     }
00662 
00663     nreject = nlines - cpl_table_get_nrow(_lines);
00664     cpl_msg_debug(fctid, "%d of %d lines rejected because brightness "
00665                   "criteria.", nreject, nlines);
00666 
00667 
00668     return 0;
00669 
00670 }
00671 
00672 
00673 inline static cpl_table *
00674 _giraffe_linelist_select(const GiTable *lines, const GiImage *spectra,
00675                          const GiGrating *grating, cxdouble width,
00676                          const GiWCalConfig *config)
00677 {
00678 
00679     const cxchar *const fctid = "_giraffe_linelist_select";
00680 
00681 
00682     cxint i;
00683     cxint nlines = 0;
00684     cxint nreject = 0;
00685 
00686     cxdouble scale = 0.;
00687     cxdouble separation = 0.;
00688 
00689     cpl_image *_spectra = NULL;
00690 
00691     cpl_table *_lines = NULL;
00692 
00693 
00694     cx_assert(lines != NULL);
00695     cx_assert(spectra != NULL);
00696     cx_assert(grating != NULL);
00697     cx_assert(config != NULL);
00698 
00699     _spectra = giraffe_image_get(spectra);
00700     cx_assert(_spectra != NULL);
00701 
00702     _lines = cpl_table_duplicate(giraffe_table_get(lines));
00703 
00704     if (_lines == NULL) {
00705         return NULL;
00706     }
00707 
00708     nlines = cpl_table_get_nrow(_lines);
00709 
00710 
00711     /*
00712      * Estimate wavelength scale (just a rough guess) and the minimum
00713      * line separation in nm.
00714      */
00715 
00716     scale = fabs(cpl_image_get_size_y(_spectra)) / grating->band;
00717     separation = width / scale * config->line_separation;
00718 
00719     cpl_msg_debug(fctid, "Estimated wavelength scale: %.4e nm/pxl",
00720                   1. / scale);
00721     cpl_msg_debug(fctid, "Minimum required line separation: %.4f nm (%.4f "
00722                   "pxl)", separation, separation * scale);
00723 
00724 
00725     /*
00726      * Reject all lines which violate the `crowding criterium', i.e.
00727      * all lines which could be misidentified because of a distance less
00728      * than the line width times a line separation factor, and a comparable
00729      * line intensity.
00730      *
00731      * Therefore, each line i at the position p with an intensity f is
00732      * eliminated from the line list if any other line j with (_p, _f)
00733      * satisfies the condition:
00734      *
00735      *   abs(p - _p) < line_separation && f < _f * flux_ratio
00736      */
00737 
00738     cpl_table_unselect_all(_lines);
00739 
00740     for (i = 0; i < cpl_table_get_nrow(_lines); i++) {
00741 
00742         register cxint j;
00743 
00744         register cxdouble w = cpl_table_get(_lines, "WLEN", i, NULL);
00745         register cxdouble f = cpl_table_get(_lines, "FLUX", i, NULL);
00746 
00747 
00748         for (j = 0; j < cpl_table_get_nrow(_lines); j++) {
00749 
00750             if (i != j) {
00751 
00752                 register cxdouble _w = cpl_table_get(_lines, "WLEN", j, NULL);
00753                 register cxdouble _f = cpl_table_get(_lines, "FLUX", j, NULL);
00754 
00755 
00756                 if (fabs(w - _w) < separation &&
00757                     f / _f < config->line_fluxratio) {
00758 
00759                     cpl_table_select_row(_lines, i);
00760                     break;
00761 
00762                 }
00763 
00764             }
00765 
00766         }
00767 
00768     }
00769 
00770     cpl_table_erase_selected(_lines);
00771 
00772     if (cpl_table_get_nrow(_lines) <= 0) {
00773         cpl_table_delete(_lines);
00774         return NULL;
00775     }
00776 
00777     nreject = nlines - cpl_table_get_nrow(_lines);
00778     cpl_msg_debug(fctid, "%d of %d lines rejected due to crowding.",
00779                   nreject, nlines);
00780 
00781 
00782     /*
00783      * Remove all lines with bad quality. This is indicated by a non-zero
00784      * value in the line list's column FLAGS for the standard catalogs, or
00785      * in the column COMMENT for the original OGL version.
00786      *
00787      * Note: This must be done last, since line with bad quality must be
00788      *       considered in the crowding check!
00789      */
00790 
00791     cpl_msg_debug(fctid, "Removing lines with non-zero line quality.");
00792 
00793     nlines = cpl_table_get_nrow(_lines);
00794     cpl_table_unselect_all(_lines);
00795 
00796     if (cpl_table_has_column(_lines, "FLAGS")) {
00797 
00798         cpl_table_or_selected_int(_lines, "FLAGS", CPL_NOT_EQUAL_TO, 0);
00799 
00800     }
00801     else {
00802 
00803         if (cpl_table_has_column(_lines, "COMMENT")) {
00804 
00805             for (i = 0; i < nlines; i++) {
00806 
00807                 cxchar *s = cx_strdup(cpl_table_get_string(_lines,
00808                                                            "COMMENT", i));
00809 
00810                 if (strlen(cx_strstrip(s)) > 3) {
00811                     cpl_table_select_row(_lines, i);
00812                 }
00813 
00814                 cx_free(s);
00815 
00816             }
00817 
00818         }
00819         else {
00820 
00821             cpl_msg_debug(fctid, "No comments found in line list! No line "
00822                           "quality checks will be done!");
00823 
00824         }
00825 
00826     }
00827 
00828     cpl_table_erase_selected(_lines);
00829 
00830     if (cpl_table_get_nrow(_lines) <= 0) {
00831         cpl_msg_debug(fctid, "Invalid line list! All lines have been "
00832                       "rejected!");
00833         cpl_table_delete(_lines);
00834         return NULL;
00835     }
00836 
00837     nreject = nlines - cpl_table_get_nrow(_lines);
00838     cpl_msg_debug(fctid, "%d of %d lines rejected because of line quality.",
00839                   nreject, nlines);
00840 
00841 
00842     return _lines;
00843 
00844 }
00845 
00846 
00847 inline static cpl_image *
00848 _giraffe_line_abscissa(const cpl_table *lines, const GiTable *slitgeometry,
00849                        const GiTable *fibers, const GiWlSolution *solution,
00850                        const GiLocalization *localization, cxbool residuals)
00851 {
00852 
00853     const cxchar *const fctid = "_giraffe_line_abscissa";
00854 
00855 
00856     const cxchar *idx = NULL;
00857 
00858     cxint i;
00859     cxint nlines = 0;
00860     cxint nfibers = 0;
00861 
00862     cpl_table *_lines = NULL;
00863     cpl_table *_fibers = NULL;
00864     cpl_table *_slitgeometry = NULL;
00865 
00866     cpl_image *abscissa = NULL;
00867 
00868 
00869     cx_assert(lines != NULL);
00870     cx_assert(slitgeometry != NULL);
00871     cx_assert(fibers != NULL);
00872     cx_assert(solution != NULL);
00873 
00874     _lines = (cpl_table *)lines;
00875 
00876     _fibers = giraffe_table_get(fibers);
00877     cx_assert(_fibers != NULL);
00878 
00879     _slitgeometry = giraffe_table_get(slitgeometry);
00880     cx_assert(_slitgeometry != NULL);
00881 
00882 
00883     nlines = cpl_table_get_nrow(_lines);
00884     nfibers = cpl_table_get_nrow(_fibers);
00885 
00886     if (nfibers != cpl_table_get_nrow(_slitgeometry)) {
00887         cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
00888         return NULL;
00889     }
00890 
00891 
00892     idx = giraffe_fiberlist_query_index(_fibers);
00893 
00894     if (idx == NULL) {
00895         cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
00896         return NULL;
00897     }
00898 
00899 
00900     if (residuals == TRUE) {
00901 
00902         if (localization == NULL) {
00903             cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
00904             return NULL;
00905         }
00906         else {
00907             if (localization->locy == NULL || localization->locw == NULL) {
00908                 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
00909                 return NULL;
00910             }
00911         }
00912 
00913         if (giraffe_wlsolution_get_residuals(solution) == NULL) {
00914             cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
00915             return NULL;
00916         }
00917 
00918     }
00919 
00920 
00921     abscissa = cpl_image_new(nfibers, nlines, CPL_TYPE_DOUBLE);
00922 
00923     for (i = 0; i < nfibers; i++) {
00924 
00925         cxint j;
00926 
00927         cxdouble xf = cpl_table_get(_slitgeometry, "XF", i, NULL);
00928         cxdouble yf = cpl_table_get(_slitgeometry, "YF", i, NULL);
00929         cxdouble *data = cpl_image_get_data(abscissa);
00930 
00931 
00932         for (j = 0; j < nlines; j++) {
00933 
00934             cxint status = 0;
00935 
00936             cxdouble lambda = cpl_table_get(_lines, "WLEN", j, NULL);
00937 
00938             cxdouble xccd = 0.;
00939 
00940 
00941             xccd = giraffe_wlsolution_compute_pixel(solution, lambda, xf, yf,
00942                                                     &status);
00943 
00944             if (status != 0) {
00945                 cpl_image_delete(abscissa);
00946                 return NULL;
00947             }
00948 
00949             if (residuals == TRUE) {
00950 
00951                 cxint cs = cpl_table_get_int(_fibers, idx, i, NULL) - 1;
00952 
00953                 cxdouble yccd = 0.;
00954 
00955                 cpl_image *_locy = giraffe_image_get(localization->locy);
00956 
00957 
00958                 cx_assert(_locy != NULL);
00959 
00960                 if (xccd > 0. && xccd < cpl_image_get_size_y(_locy)) {
00961 
00962                     cxdouble xres = 0.;
00963 
00964                     yccd = _giraffe_get_fiber_position(_locy, cs, xccd);
00965                     xres = giraffe_wlsolution_compute_residual(solution,
00966                                                                xccd, yccd);
00967 
00968                     xccd -= xres;
00969                 }
00970 
00971             }
00972 
00973             data[j * nfibers + i] = xccd;
00974 
00975         }
00976 
00977     }
00978 
00979     return abscissa;
00980 
00981 }
00982 
00983 
00984 inline static cpl_image *
00985 _giraffe_line_ordinate(GiTable *lines, const GiTable *slitgeometry,
00986                        const GiWlSolution *solution)
00987 {
00988 
00989     return NULL;
00990 
00991 }
00992 
00993 
00994 /*
00995  * Sets the fit parameters to their initial values. The function does
00996  * nothing if the line status flags are not zero.
00997  */
00998 
00999 inline static void
01000 _giraffe_line_fit_setup(GiModel *model, cxdouble width, cxint xmin,
01001                         const cpl_matrix *y, const cpl_matrix *sigma,
01002                         const GiLineParams *setup, cxint *lflags)
01003 {
01004 
01005     cxint k;
01006     cxint center = 0;
01007     cxint xline = 0;
01008 
01009     cxdouble amplitude = 0.;
01010     cxdouble background = 0.;
01011     cxdouble _sigma = 0.;
01012 
01013     cpl_matrix *_y = NULL;
01014 
01015 
01016     cx_assert(model != NULL);
01017     cx_assert(y != NULL);
01018     cx_assert(setup != NULL);
01019     cx_assert(lflags != NULL);
01020 
01021 
01022     if (*lflags != LF_R_NONE) {
01023         return;
01024     }
01025 
01026 
01027     /*
01028      * Model parameters: the line amplitude, center, width(s)
01029      * and background. The line background is estimated as the
01030      * mean of the two lowest intensities found in the fit
01031      * interval.
01032      */
01033 
01034     /* Amplitude and center */
01035 
01036     center = xmin;
01037     for (k = 0; k < cpl_matrix_get_nrow((cpl_matrix*)y); k++) {
01038         if (cpl_matrix_get((cpl_matrix *)y, k, 0) >= amplitude) {
01039             center = xmin + k;
01040             xline = k;
01041             amplitude = cpl_matrix_get((cpl_matrix *)y, k, 0);
01042         }
01043     }
01044 
01045 
01046     /* Background */
01047 
01048     _y = cpl_matrix_duplicate((cpl_matrix *)y);
01049 
01050     giraffe_matrix_sort(_y);
01051 
01052     background = 0.5 * (cpl_matrix_get(_y, 0, 0) + cpl_matrix_get(_y, 1, 0));
01053     cpl_matrix_delete(_y);
01054 
01055 
01056     /*
01057      * Line rejection: Discard lines whose flux errors at the
01058      * line center times a threshold is larger than the peak flux
01059      * in the current fit interval, or if maximum flux value
01060      * exceeds the saturation level.
01061      */
01062 
01063     _sigma = cpl_matrix_get((cpl_matrix *)sigma, xline, 0) * setup->thres;
01064 
01065     if (amplitude <= _sigma || amplitude > setup->satlv) {
01066         *lflags |= LF_R_AMPLI;
01067         return;
01068     }
01069 
01070     giraffe_model_set_parameter(model, "Amplitude", amplitude - background);
01071     giraffe_model_set_parameter(model, "Center", center);
01072     giraffe_model_set_parameter(model, "Background", background);
01073     giraffe_model_set_parameter(model, "Width1", width);
01074 
01075     if (strncmp(giraffe_model_get_name(model), "psfexp", 6) == 0) {
01076 
01077         cxdouble width2 = setup->psfexp < 0. ? -setup->psfexp : setup->psfexp;
01078 
01079         giraffe_model_set_parameter(model, "Width2", width2);
01080 
01081 
01082         /*
01083          * If the psf-width exponent is positiv the parameter
01084          * value is kept, otherwise it is fitted.
01085          */
01086 
01087         if (setup->psfexp >= 0.) {
01088             giraffe_model_freeze_parameter(model, "Width2");
01089         }
01090         else {
01091             giraffe_model_thaw_parameter(model, "Width2");
01092         }
01093 
01094     }
01095 
01096     return;
01097 
01098 }
01099 
01100 
01101 inline static cxint
01102 _giraffe_line_fit(GiLineData *lines, const cpl_image *positions, cxint width,
01103                   const GiExtraction *extraction, const GiTable *fibers,
01104                   const GiImage *locy, const GiLineParams *setup)
01105 {
01106 
01107     const cxchar *const fctid = "_giraffe_line_fit";
01108 
01109 
01110     const cxchar *idx = NULL;
01111 
01112     cxint i;
01113     cxint nfibers = 0;
01114     cxint nlines = 0;
01115 
01116     const cxdouble LOG2 = log(2.);
01117     const cxdouble fwhm_ratio = 2. * sqrt(2. * LOG2);
01118 
01119     cpl_image *_spectra = NULL;
01120     cpl_image *_errors = NULL;
01121     cpl_image *_locy = NULL;
01122 
01123     cpl_matrix *x = NULL;
01124     cpl_matrix *y = NULL;
01125     cpl_matrix *sigma = NULL;
01126 
01127     cpl_table *_fibers = NULL;
01128 
01129     GiModel *model = NULL;
01130 
01131 
01132     cx_assert(positions != NULL);
01133     cx_assert(width > 0);
01134 
01135     cx_assert(extraction != NULL);
01136     cx_assert(extraction->spectra != NULL && extraction->error != NULL);
01137 
01138     cx_assert(fibers != NULL);
01139     cx_assert(locy != NULL);
01140     cx_assert(setup != NULL);
01141 
01142 
01143     _fibers = giraffe_table_get(fibers);
01144     cx_assert(_fibers != NULL);
01145 
01146     _spectra = giraffe_image_get(extraction->spectra);
01147     cx_assert(_spectra != NULL);
01148 
01149     _errors = giraffe_image_get(extraction->error);
01150     cx_assert(_errors != NULL);
01151 
01152     _locy = giraffe_image_get(locy);
01153     cx_assert(_locy != NULL);
01154 
01155     nfibers = cpl_table_get_nrow(_fibers);
01156 
01157     cx_assert(nfibers == cpl_image_get_size_x(_spectra));
01158     cx_assert(nfibers == cpl_image_get_size_x(_errors));
01159 
01160     // FIXME: The assertion should not be necessary any more, but better
01161     //        check this
01162     //cx_assert(nfibers == cpl_image_get_size_x(_locy));
01163 
01164     idx = giraffe_fiberlist_query_index(_fibers);
01165     cx_assert(idx != NULL);
01166 
01167     nlines = cpl_image_get_size_y(positions);
01168 
01169 
01170     /*
01171      * Create the selected line fit model. All parameters will be fitted.
01172      */
01173 
01174     if (strcmp(setup->model, "gaussian") != 0 &&
01175         strcmp(setup->model, "psfexp") != 0 &&
01176         strcmp(setup->model, "psfexp2") != 0) {
01177         cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
01178         return 1;
01179     }
01180 
01181     model = giraffe_model_new(setup->model);
01182 
01183     if (giraffe_model_get_type(model) != GI_MODEL_LINE) {
01184         giraffe_model_delete(model);
01185         return 2;
01186     }
01187 
01188     giraffe_model_thaw(model);
01189 
01190     giraffe_model_set_iterations(model, setup->fit.iterations);
01191     giraffe_model_set_tests(model, setup->fit.tests);
01192     giraffe_model_set_delta(model, setup->fit.delta);
01193 
01194 
01195     /*
01196      * For each spectrum in the input image `positions' fit each line.
01197      */
01198 
01199     x = cpl_matrix_new(width, 1);
01200     y = cpl_matrix_new(width, 1);
01201     sigma = cpl_matrix_new(width, 1);
01202 
01203     for (i = 0; i < nfibers; i++) {
01204 
01205         cxint j;
01206 
01207         for (j = 0; j < nlines; j++) {
01208 
01209             cxint k;
01210             cxint lflags = LF_R_NONE;
01211             cxint iterations = 0;
01212             cxint ndata = 0;
01213             cxint xmin = 0;
01214             cxint xmax = 0;
01215             cxint nx = cpl_image_get_size_y(_spectra);
01216 
01217             cxdouble xccd = 0.;
01218             cxdouble yccd = 0.;
01219             cxdouble lwidth = 0.;
01220             cxdouble amplitude = 0.;
01221             cxdouble background = 0.;
01222             cxdouble center = 0.;
01223             cxdouble width1 = 0.;
01224             cxdouble exponent = 0.;
01225             cxdouble error = 0.;
01226             cxdouble gwidth = 0.;
01227 
01228             const cxdouble *_positions = cpl_image_get_data_const(positions);
01229 
01230 
01231             /*
01232              * Compute line position in pixels using the fiber localization
01233              */
01234 
01235             xccd = _positions[j * nfibers + i];
01236 
01237             xmin = xccd;
01238             xmax = xccd;
01239 
01240             if (0 < xccd && xccd < nx) {
01241 
01242                 cxint cs = cpl_table_get_int(_fibers, idx, i, NULL) - 1;
01243 
01244 
01245                 /*
01246                  * Compute fit interval taking the CCD size into account.
01247                  * The entire interval must be within the CCD boundaries.
01248                  */
01249 
01250                 xmin = (cxint)(xccd - 0.5 * width + 0.5);
01251                 xmax = (cxint)(xccd + 0.5 * width + 0.5);
01252 
01253                 xmin = CX_MAX(CX_MIN(xmin, nx - 1), 0);
01254                 xmax = CX_MAX(CX_MIN(xmax, nx - 1), 0);
01255 
01256                 ndata = xmax - xmin;
01257 
01258                 /*
01259                  * Compute fiber centroid position using linear
01260                  * interpolation.
01261                  */
01262 
01263                 yccd = _giraffe_get_fiber_position(_locy, cs, xccd);
01264 
01265             }
01266 
01267 
01268             /*
01269              * At least 3 data points are required for the fit. This covers
01270              * also the situation xmin == xmax
01271              */
01272 
01273             if (ndata < 3) {
01274                 lflags |= LF_R_XCCD;
01275             }
01276             else {
01277 
01278                 /*
01279                  * Resize and fill the fit data vectors
01280                  */
01281 
01282                 if (ndata != cpl_matrix_get_nrow(x)) {
01283                     cpl_matrix_set_size(x, ndata, 1);
01284                     cpl_matrix_set_size(y, ndata, 1);
01285                     cpl_matrix_set_size(sigma, ndata, 1);
01286                 }
01287 
01288                 for (k = 0; k < ndata; k++) {
01289 
01290                     cxint l = xmin + k;
01291 
01292                     cxdouble *sdata = cpl_image_get_data(_spectra);
01293                     cxdouble *edata = cpl_image_get_data(_errors);
01294 
01295                     cpl_matrix_set(x, k, 0, xmin + k);
01296                     cpl_matrix_set(y, k, 0, sdata[l * nfibers + i]);
01297                     cpl_matrix_set(sigma, k, 0, edata[l * nfibers + i]);
01298 
01299                 }
01300 
01301             }
01302 
01303 
01304             /*
01305              * Convert line FWHM to the model's width parameter, i.e.
01306              * gaussian or exponetial width depending on the selected
01307              * model profile.
01308              *
01309              * Note: The absolute value of setup->psfexp is used below,
01310              *       because the sign just determines whether it is
01311              *       fitted or kept fixed.
01312              */
01313 
01314             if (strcmp(setup->model, "psfexp") == 0) {
01315 
01316                 exponent = fabs(setup->psfexp);
01317 
01318                 lwidth = pow(0.5 * setup->grwid * nx, exponent) / LOG2;
01319 
01320             }
01321             else if (strcmp(setup->model, "psfexp2") == 0) {
01322 
01323                 exponent = fabs(setup->psfexp);
01324 
01325                 lwidth = setup->grwid * nx / (2. * pow(LOG2, 1. / exponent));
01326 
01327             }
01328             else if (strcmp(setup->model, "gaussian") == 0) {
01329 
01330                 lwidth = setup->grwid * nx / fwhm_ratio;
01331 
01332             }
01333             else {
01334 
01335                 /*
01336                  * This point should never be reached!
01337                  */
01338 
01339                 gi_error("Unsupported line model encountered!");
01340 
01341             }
01342 
01343 
01344             /*
01345              * Validate and set the initial values of the line model
01346              * fit parameters
01347              */
01348 
01349 
01350             _giraffe_line_fit_setup(model, lwidth, xmin, y, sigma, setup,
01351                                     &lflags);
01352 
01353 
01354             if (lflags == LF_R_NONE) {
01355 
01356                 cxint xline = giraffe_model_get_parameter(model, "Center");
01357 
01358                 cxdouble hwidth = 0.;
01359 
01360                 cxint status = 0;
01361 
01362 
01363                 /*
01364                  * Fit the model
01365                  */
01366 
01367                 status = giraffe_model_fit(model, x, y, sigma);
01368 
01369 
01370                 amplitude = giraffe_model_get_parameter(model, "Amplitude");
01371                 background = giraffe_model_get_parameter(model, "Background");
01372                 center = giraffe_model_get_parameter(model, "Center");
01373 
01374 
01375                 /*
01376                  * Convert the model dependent width parameter back to
01377                  * the line's FWHM .
01378                  */
01379 
01380                 if (strcmp(setup->model, "psfexp") == 0) {
01381 
01382                     width1 = giraffe_model_get_parameter(model, "Width1");
01383                     exponent = giraffe_model_get_parameter(model, "Width2");
01384 
01385                     /* exponential width */
01386                     gwidth = 2. * pow(width1 * LOG2, 1. / exponent);
01387 
01388                 }
01389                 else if (strcmp(setup->model, "psfexp2") == 0) {
01390 
01391                     width1 = giraffe_model_get_parameter(model, "Width1");
01392                     exponent = giraffe_model_get_parameter(model, "Width2");
01393 
01394                     /* exponential width */
01395                     gwidth = 2. * pow(LOG2, 1. / exponent) * width1;
01396 
01397                 }
01398                 else if (strcmp(setup->model, "gaussian") == 0) {
01399 
01400                     width1 = giraffe_model_get_parameter(model, "Width1");
01401 
01402                     /* gaussian width */
01403                     gwidth = width1 * fwhm_ratio;
01404 
01405                 }
01406                 else {
01407 
01408                     /*
01409                      * This point should never be reached!
01410                      */
01411 
01412                     gi_error("Unsupported line model encountered!");
01413 
01414                 }
01415 
01416                 hwidth = gwidth / 2.;
01417                 iterations = giraffe_model_get_position(model);
01418 
01419 
01420                 /*
01421                  * Check line fit. Set rejection code for lines
01422                  * with bad fit parameters.
01423                  */
01424 
01425                 if (status < 0) {
01426 
01427                     /* Fit error */
01428                     lflags |= LF_R_ERROR;
01429 
01430                 }
01431 
01432                 if (iterations >= giraffe_model_get_iterations(model)) {
01433 
01434                     /* Maximum number of iterations reached */
01435                     lflags |= LF_R_NITER;
01436 
01437                 }
01438 
01439                 if (xmin > center || center > xmax) {
01440 
01441                     /* Line center out of window */
01442                     lflags |= LF_R_CENTR;
01443 
01444                 }
01445 
01446                 if ((center - hwidth) < xmin) {
01447 
01448                     /* Line out of window on the left */
01449                     lflags |= LF_R_LEFT;
01450 
01451                 }
01452 
01453                 if ((center + hwidth) > xmax) {
01454 
01455                     /* Line out of window on the right */
01456                     lflags |= LF_R_RIGHT;
01457 
01458                 }
01459 
01460                 if ((center - xline) >= setup->offst) {
01461 
01462                     /* Line center too far from the initial guess */
01463                     lflags |= LF_R_OFFST;
01464 
01465                 }
01466 
01467                 if (width1 < 0. || exponent < 0.) {
01468 
01469                     /* Negative line width */
01470                     lflags |= LF_R_BADLN;
01471 
01472                 }
01473 
01474                 if (gwidth > (xmax - xmin)) {
01475 
01476                     /* Line width is larger than window */
01477                     lflags |= LF_R_WIDTH;
01478 
01479                 }
01480 
01481                 if (gwidth < (setup->grwid * nx * setup->wfact)) {
01482 
01483                     /* Line width too small for resolution */
01484                     lflags |= LF_R_RESOL;
01485 
01486                 }
01487 
01488                 if (gwidth > (setup->grwid * nx / setup->wfact)) {
01489 
01490                     /* Line width too large for resolution */
01491                     lflags |= LF_R_RESOL;
01492 
01493                 }
01494 
01495             }
01496 
01497 
01498             /*
01499              * Save the results
01500              */
01501 
01502             /* Line status code */
01503 
01504             giraffe_linedata_set_status(lines, i, j, lflags);
01505 
01506             giraffe_linedata_set(lines, "Iterations", i, j,iterations);
01507             giraffe_linedata_set(lines, "Chi-square", i, j,
01508                                  giraffe_model_get_chisq(model));
01509             giraffe_linedata_set(lines, "DoF", i, j,
01510                                  giraffe_model_get_df(model));
01511             giraffe_linedata_set(lines, "R-square", i, j,
01512                                  giraffe_model_get_rsquare(model));
01513             giraffe_linedata_set(lines, "Xccd", i, j, xccd);
01514             giraffe_linedata_set(lines, "Yccd", i, j, yccd);
01515 
01516             /* FIXME: Using the two functions prototyped below would
01517              *        improve the code here. But they need support
01518              *        from the GiModel implementation. (RP)
01519              *
01520              *        giraffe_linedata_set_parameters(lines, model, i, j);
01521              *        giraffe_linedata_set_errors(lines, model, i, j);
01522              */
01523 
01524             giraffe_linedata_set(lines, "Amplitude", i, j, amplitude);
01525             giraffe_linedata_set(lines, "Background", i, j, background);
01526             giraffe_linedata_set(lines, "Center", i, j, center);
01527             giraffe_linedata_set(lines, "Width1", i, j, width1);
01528 
01529             if (strncmp(setup->model, "psfexp", 6) == 0) {
01530                 giraffe_linedata_set(lines, "Width2", i, j, exponent);
01531             }
01532 
01533             giraffe_linedata_set(lines, "FWHM", i, j, gwidth);
01534 
01535             /* Uncertainties */
01536 
01537             error = giraffe_model_get_sigma(model, "Amplitude");
01538             giraffe_linedata_set(lines, "dAmplitude", i, j, error);
01539 
01540             error = giraffe_model_get_sigma(model, "Center");
01541             giraffe_linedata_set(lines, "dCenter", i, j, error);
01542 
01543             error = giraffe_model_get_sigma(model, "Background");
01544             giraffe_linedata_set(lines, "dBackground", i, j, error);
01545 
01546             error = giraffe_model_get_sigma(model, "Width1");
01547             giraffe_linedata_set(lines, "dWidth1", i, j, error);
01548 
01549             if (strncmp(setup->model, "psfexp", 6) == 0) {
01550                 error = giraffe_model_get_sigma(model, "Width2");
01551                 giraffe_linedata_set(lines, "dWidth2", i, j, error);
01552             }
01553 
01554         }
01555 
01556     }
01557 
01558     cpl_matrix_delete(x);
01559     cpl_matrix_delete(y);
01560     cpl_matrix_delete(sigma);
01561 
01562     giraffe_model_delete(model);
01563 
01564     return 0;
01565 
01566 }
01567 
01568 
01569 inline static cpl_image *
01570 _giraffe_psf_fit(GiLineData *lines, const GiLocalization *localization,
01571                  GiTable *fibers, GiTable *slitgeometry, GiSCFitParams *setup)
01572 {
01573 
01574     const cxchar *const fctid = "_giraffe_psf_fit";
01575 
01576 
01577     cxint i;
01578     cxint status = 0;
01579     cxint ngood = 0;
01580     cxint nlines = 0;
01581     cxint nsubslits = 1;
01582     cxint nx = 0;
01583 
01584     cpl_table *_fibers = NULL;
01585 
01586     cpl_image *locy = NULL;
01587     cpl_image *locw = NULL;
01588     cpl_image *psfwidth = NULL;
01589 
01590 
01591     cx_assert(lines != NULL);
01592     cx_assert(localization != NULL);
01593     cx_assert(fibers != NULL);
01594     cx_assert(slitgeometry != NULL);
01595     cx_assert(setup != NULL);
01596 
01597     _fibers = giraffe_table_get(fibers);
01598     cx_assert(_fibers != NULL);
01599 
01600     locy = giraffe_image_get(localization->locy);
01601     cx_assert(locy != NULL);
01602 
01603     locw = giraffe_image_get(localization->locw);
01604     cx_assert(locw != NULL);
01605 
01606     nx = cpl_image_get_size_y(locy);
01607     nlines = giraffe_linedata_lines(lines);
01608 
01609     psfwidth = cpl_image_new(cpl_table_get_nrow(_fibers), nx,
01610                              CPL_TYPE_DOUBLE);
01611 
01612     if (setup->subslits == TRUE) {
01613         nsubslits = _giraffe_subslit_get_max(_fibers);
01614     }
01615 
01616     for (i = 0; i < nsubslits; i++) {
01617 
01618         cxint j;
01619         cxint k;
01620         cxint ssn = 0;
01621         cxint nfibers = 0;
01622         cxint ndata = 0;
01623         cxint iterations = 0;
01624         cxint accepted = 0;
01625         cxint total = 0;
01626 
01627         cxdouble ymin = 0.;
01628         cxdouble ymax = 0.;
01629         cxdouble ratio = 1.;
01630 
01631         cpl_matrix *xss = NULL;
01632         cpl_matrix *yss = NULL;
01633         cpl_matrix *wss = NULL;
01634         cpl_matrix *sss = NULL;
01635         cpl_matrix *nss = NULL;
01636         cpl_matrix *lss = NULL;
01637         cpl_matrix *base = NULL;
01638         cpl_matrix *fit = NULL;
01639         cpl_matrix *coeff = NULL;
01640         cpl_matrix *chebyshev = NULL;
01641 
01642         cpl_table *subslit = NULL;
01643 
01644         GiChebyshev2D *psffit = NULL;
01645 
01646 
01647         if (setup->subslits == TRUE) {
01648             subslit = _giraffe_subslit_get(_fibers, i + 1);
01649             ssn = cpl_table_get_int(subslit, "SSN", 0, NULL);
01650 
01651             cx_assert(ssn == i + 1);
01652         }
01653         else {
01654             subslit = cpl_table_duplicate(_fibers);
01655             ssn = 0;
01656         }
01657 
01658         if (subslit == NULL) {
01659             continue;
01660         }
01661 
01662         _giraffe_subslit_range(subslit, locy, locw, &ymin, &ymax);
01663 
01664         nfibers = cpl_table_get_nrow(subslit);
01665         ndata =  nfibers * nlines;
01666 
01667 
01668         xss = cpl_matrix_new(ndata, 1);   /* X abcissas for subslit */
01669         yss = cpl_matrix_new(ndata, 1);   /* Y ordinates */
01670         wss = cpl_matrix_new(1, ndata);   /* widths (transposed) */
01671         sss = cpl_matrix_new(ndata, 1);   /* width sigmas */
01672         nss = cpl_matrix_new(ndata, 1);   /* fiber indices */
01673         lss = cpl_matrix_new(ndata, 1);   /* line indices */
01674 
01675 
01676         /*
01677          * Fills inputs matrices with good lines xccd, yccd, width,
01678          * width sigmas and line status
01679          */
01680 
01681         k = 0;
01682 
01683         for (j = 0; j < nfibers; j++) {
01684 
01685             cxint l;
01686             cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
01687 
01688             for (l = 0; l < nlines; l++) {
01689 
01690                 cxdouble value = 0.;
01691                 cxdouble yccd = giraffe_linedata_get(lines, "Yccd", n, l);
01692 
01693                 if (giraffe_linedata_get_status(lines, n, l) != 0) {
01694                     continue;
01695                 }
01696 
01697                 if (yccd < ymin || yccd > ymax) {
01698                     continue;
01699                 }
01700 
01701                 value = giraffe_linedata_get(lines, "Xccd", n, l);
01702                 cpl_matrix_set(xss, k, 0, value);
01703 
01704                 cpl_matrix_set(yss, k, 0, yccd);
01705 
01706                 /* FIXME: The orignal code fits the FWHM but uses
01707                  *        the uncertainty of the profile's width
01708                  *        parameter to reject lines in the sigma
01709                  *        clipping below. Instead the FWHM's
01710                  *        uncertainty should be used. Check this! (RP)
01711                  */
01712 
01713                 value = giraffe_linedata_get(lines, "FWHM", n, l);
01714                 cpl_matrix_set(wss, 0, k, value);
01715 
01716                 value = giraffe_linedata_get(lines, "dWidth1", n, l);
01717                 cpl_matrix_set(sss, k, 0, value);
01718 
01719                 cpl_matrix_set(nss, k, 0, n);
01720                 cpl_matrix_set(lss, k, 0, l);
01721 
01722                 ++k;
01723 
01724             }
01725 
01726         }
01727 
01728         if (k == 0) {
01729             cpl_msg_debug(fctid, "Skipping subslit %d: No input lines left! "
01730                           "All lines have non-zero status or are beyond the "
01731                           "subslit boundaries (%.4f, %.4f).", ssn, ymin, ymax);
01732             continue;
01733         }
01734 
01735         /*
01736          * Shrink input matrices to their actual size
01737          */
01738 
01739         cpl_matrix_set_size(xss, k, 1);
01740         cpl_matrix_set_size(yss, k, 1);
01741         cpl_matrix_set_size(wss, 1, k);
01742         cpl_matrix_set_size(sss, k, 1);
01743         cpl_matrix_set_size(nss, k, 1);
01744         cpl_matrix_set_size(lss, k, 1);
01745 
01746 
01747         /*
01748          * Sigma clipping
01749          */
01750 
01751         iterations = 0;
01752         ratio = 1.0;
01753         accepted = cpl_matrix_get_ncol(wss);
01754         total = accepted;
01755 
01756         while (accepted > 0 && iterations < setup->clip.iterations &&
01757                ratio > setup->clip.fraction) {
01758 
01759             base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
01760                                             setup->fit.xorder + 1,
01761                                             setup->fit.yorder + 1, xss, yss);
01762 
01763             if (coeff != NULL) {
01764                 cpl_matrix_delete(coeff);
01765                 coeff = NULL;
01766             }
01767 
01768             coeff = giraffe_matrix_leastsq(base, wss);
01769 
01770             if (coeff == NULL) {
01771                 cpl_msg_debug(fctid, "Error solving linear system for "
01772                               "subslit %d, skipping subslit.", ssn);
01773                 break;
01774             }
01775 
01776             fit = cpl_matrix_product_create(coeff, base);
01777 
01778             k = 0;
01779 
01780             for (j = 0; j < cpl_matrix_get_ncol(fit); j++) {
01781 
01782                 cxdouble _fit = cpl_matrix_get(fit, 0, j);
01783                 cxdouble _wss = cpl_matrix_get(wss, 0, j);
01784                 cxdouble _sss = cpl_matrix_get(sss, j, 0);
01785 
01786                 if (fabs(_fit - _wss) >= setup->clip.level * _sss) {
01787 
01788                     cxint n = (cxint)cpl_matrix_get(nss, j, 0);
01789                     cxint l = (cxint)cpl_matrix_get(lss, j, 0);
01790 
01791                     /*
01792                      * Reject this line
01793                      */
01794 
01795                     giraffe_linedata_set_status(lines, n, l, LF_R_PSFIT);
01796                     continue;
01797 
01798                 }
01799 
01800                 cpl_matrix_set(xss, k, 0, cpl_matrix_get(xss, j, 0));
01801                 cpl_matrix_set(yss, k, 0, cpl_matrix_get(yss, j, 0));
01802                 cpl_matrix_set(wss, 0, k, cpl_matrix_get(wss, 0, j));
01803                 cpl_matrix_set(sss, k, 0, cpl_matrix_get(sss, j, 0));
01804                 cpl_matrix_set(nss, k, 0, cpl_matrix_get(nss, j, 0));
01805                 cpl_matrix_set(lss, k, 0, cpl_matrix_get(lss, j, 0));
01806                 ++k;
01807 
01808             }
01809 
01810             cpl_matrix_delete(base);
01811             cpl_matrix_delete(fit);
01812 
01813             if (k == accepted) {
01814 
01815                 /*
01816                  * No new points rejected, no more iterations. That's it.
01817                  */
01818 
01819                 break;
01820             }
01821             else {
01822                 accepted = k;
01823                 ratio = (cxdouble)accepted / (cxdouble)total;
01824 
01825                 cpl_matrix_set_size(xss, k, 1);
01826                 cpl_matrix_set_size(yss, k, 1);
01827                 cpl_matrix_set_size(wss, 1, k);
01828                 cpl_matrix_set_size(sss, k, 1);
01829                 cpl_matrix_set_size(nss, k, 1);
01830                 cpl_matrix_set_size(lss, k, 1);
01831 
01832                 ++iterations;
01833 
01834             }
01835 
01836         }
01837 
01838         if (accepted == 0) {
01839             cpl_msg_debug(fctid, "Subslit %d: All lines rejected.", ssn);
01840             continue;
01841         }
01842 
01843         if (coeff == NULL) {
01844             continue;
01845         }
01846 
01847         ngood += accepted;
01848 
01849         cpl_matrix_delete(xss);
01850         cpl_matrix_delete(yss);
01851         cpl_matrix_delete(wss);
01852         cpl_matrix_delete(sss);
01853         cpl_matrix_delete(nss);
01854         cpl_matrix_delete(lss);
01855 
01856 
01857         /*
01858          * Compute coordinate grid for the whole subslit
01859          */
01860 
01861         xss = cpl_matrix_new(nx * nfibers, 1);
01862         yss = cpl_matrix_new(nx * nfibers, 1);
01863 
01864         giraffe_compute_image_coordinates(nx, nfibers, xss, NULL);
01865 
01866         for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
01867 
01868             const cxchar *idx = giraffe_fiberlist_query_index(subslit);
01869 
01870             cxint l;
01871             cxint ns = cpl_image_get_size_x(locy);
01872             cxint cs = cpl_table_get_int(subslit, idx, j, NULL) - 1;
01873 
01874             cxdouble *data = cpl_image_get_data(locy);
01875 
01876 
01877             for (l = 0; l < nx; l++) {
01878                 cpl_matrix_set(yss, l * nfibers + j, 0, data[l * ns + cs]);
01879             }
01880 
01881         }
01882 
01883 
01884         /*
01885          * Compute fitted PSF width on the whole subslit
01886          */
01887 
01888         base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
01889                                         setup->fit.xorder + 1,
01890                                         setup->fit.yorder + 1, xss, yss);
01891 
01892         fit = cpl_matrix_product_create(coeff, base);
01893 
01894         cpl_matrix_delete(xss);
01895         xss = NULL;
01896 
01897         cpl_matrix_delete(yss);
01898         yss = NULL;
01899 
01900         /* TODO: Store fit coefficients in output structure
01901          */
01902 
01903         /*
01904          * The matrix 'chebyshev' is a view into 'coeff' with the correct
01905          * shape for the subsequent call of giraffe_chebyshev2d_new().
01906          * Therefore, if 'chebyshev' is destroyed afterwards the data
01907          * of the matrix must not be destroyed!
01908          */
01909 
01910         chebyshev = cpl_matrix_wrap(setup->fit.xorder + 1,
01911                                     setup->fit.yorder + 1,
01912                                     cpl_matrix_get_data(coeff));
01913 
01914         psffit = giraffe_chebyshev2d_new(setup->fit.xorder, setup->fit.yorder);
01915         status = giraffe_chebyshev2d_set(psffit, 0., nx, ymin, ymax,
01916                                          chebyshev);
01917 
01918         if (status != 0) {
01919 
01920             giraffe_chebyshev2d_delete(psffit);
01921 
01922             cpl_matrix_unwrap(chebyshev);
01923 
01924             cpl_matrix_delete(base);
01925             cpl_matrix_delete(coeff);
01926             cpl_matrix_delete(fit);
01927 
01928             cpl_table_delete(subslit);
01929 
01930             cpl_image_delete(psfwidth);
01931 
01932             return NULL;
01933 
01934         }
01935 
01936         cpl_matrix_unwrap(chebyshev);
01937         chebyshev = NULL;
01938 
01939         giraffe_chebyshev2d_delete(psffit);
01940         psffit = NULL;
01941 
01942 
01943         /*
01944          * Save fitted PSF of the line profile to the output image.
01945          */
01946 
01947         for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
01948 
01949             cxint l;
01950             cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
01951             cxint ns = cpl_table_get_nrow(_fibers);
01952 
01953             cxdouble *data = cpl_image_get_data(psfwidth);
01954 
01955             for (l = 0; l < nx; l++) {
01956                 data[l * ns + n] = cpl_matrix_get(fit, 0, l * nfibers + j);
01957             }
01958 
01959         }
01960 
01961         cpl_matrix_delete(base);
01962         cpl_matrix_delete(coeff);
01963         cpl_matrix_delete(fit);
01964 
01965         cpl_table_delete(subslit);
01966 
01967     }
01968 
01969     return psfwidth;
01970 
01971 }
01972 
01973 
01974 inline static cxint
01975 _giraffe_opticalmodel_fit(GiWlSolution *solution, GiLineData *lines,
01976                           GiTable *fibers, GiTable *slitgeometry,
01977                           GiOpticalModelParams *setup)
01978 {
01979 
01980     const cxchar *const fctid = "_giraffe_opticalmodel_fit";
01981 
01982 
01983     cxint status = 0;
01984     cxint ndata = 0;
01985     cxint ngood = 0;
01986 
01987     cxsize i;
01988 
01989     cpl_matrix *x = NULL;
01990     cpl_matrix *y = NULL;
01991     cpl_matrix *sigma = NULL;
01992 
01993     cpl_table *_fibers = NULL;
01994     cpl_table *_slitgeometry = NULL;
01995 
01996     GiModel *model = NULL;
01997 
01998 
01999     cx_assert(solution != NULL);
02000     cx_assert(lines != NULL);
02001     cx_assert(fibers != NULL);
02002     cx_assert(slitgeometry != NULL);
02003     cx_assert(setup != NULL);
02004 
02005     _fibers = giraffe_table_get(fibers);
02006     cx_assert(_fibers != NULL);
02007 
02008     _slitgeometry = giraffe_table_get(slitgeometry);
02009     cx_assert(_slitgeometry != NULL);
02010 
02011     model = giraffe_wlsolution_model(solution);
02012 
02013 
02014     /*
02015      * Prepare input data
02016      */
02017 
02018     ndata = giraffe_linedata_lines(lines) * giraffe_linedata_fibers(lines);
02019 
02020     x = cpl_matrix_new(ndata, giraffe_model_count_arguments(model));
02021     y = cpl_matrix_new(ndata, 1);
02022     sigma = cpl_matrix_new(ndata, 1);
02023 
02024     for (i = 0; i < giraffe_linedata_fibers(lines); i++) {
02025 
02026         cxsize j;
02027 
02028         cxdouble xf = cpl_table_get(_slitgeometry, "XF", i, NULL);
02029         cxdouble yf = cpl_table_get(_slitgeometry, "YF", i, NULL);
02030 
02031 
02032         for (j = 0; j < giraffe_linedata_lines(lines); j++) {
02033 
02034             if (giraffe_linedata_get_status(lines, i, j) != 0) {
02035                 continue;
02036             }
02037 
02038             /* FIXME: Is this really needed? The status should be enough! (RP)
02039              */
02040 
02041             if (giraffe_linedata_get(lines, "dCenter", i, j) <= 0.) {
02042                 continue;
02043             }
02044 
02045             cpl_matrix_set(x, ngood, 0,
02046                            giraffe_linedata_get_wavelength(lines, j));
02047             cpl_matrix_set(x, ngood, 1, xf);
02048             cpl_matrix_set(x, ngood, 2, yf);
02049 
02050             cpl_matrix_set(y, ngood, 0,
02051                            giraffe_linedata_get(lines, "Center", i, j));
02052             cpl_matrix_set(sigma, ngood, 0,
02053                            giraffe_linedata_get(lines, "dCenter", i, j));
02054 
02055             ++ngood;
02056 
02057         }
02058 
02059     }
02060 
02061     cpl_msg_debug(fctid, "Using %d of %d line positions for optical "
02062                   "model fit.", ngood, ndata);
02063 
02064     if (ngood == 0) {
02065 
02066         cpl_matrix_delete(x);
02067         cpl_matrix_delete(y);
02068         cpl_matrix_delete(sigma);
02069 
02070         return 1;
02071     }
02072 
02073 
02074     /*
02075      * Shrink input matrices to their actual size
02076      */
02077 
02078     cpl_matrix_set_size(x, ngood, giraffe_model_count_arguments(model));
02079     cpl_matrix_set_size(y, ngood, 1);
02080     cpl_matrix_set_size(sigma, ngood, 1);
02081 
02082 
02083     /*
02084      * Configure the optical model fit.
02085      */
02086 
02087     giraffe_model_freeze(model);
02088 
02089     if (setup->flags & OPTM_FLENGTH) {
02090         giraffe_model_thaw_parameter(model, "FocalLength");
02091     }
02092 
02093     if (setup->flags & OPTM_GCAMERA) {
02094         giraffe_model_thaw_parameter(model, "Magnification");
02095     }
02096 
02097     if (setup->flags & OPTM_THETA) {
02098         giraffe_model_thaw_parameter(model, "Angle");
02099     }
02100 
02101     if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
02102         if (setup->flags & OPTM_SX) {
02103             giraffe_model_thaw_parameter(model, "Sdx");
02104         }
02105 
02106         if (setup->flags & OPTM_SY) {
02107             giraffe_model_thaw_parameter(model, "Sdy");
02108         }
02109 
02110         if (setup->flags & OPTM_SPHI) {
02111             giraffe_model_thaw_parameter(model, "Sphi");
02112         }
02113     }
02114 
02115     giraffe_model_set_iterations(model, setup->fit.iterations);
02116     giraffe_model_set_tests(model, setup->fit.tests);
02117     giraffe_model_set_delta(model, setup->fit.delta);
02118 
02119 
02120     /*
02121      * Fit the model
02122      */
02123 
02124     status = giraffe_model_fit(model, x, y, sigma);
02125 
02126     if (status < 0) {
02127 
02128         cpl_matrix_delete(x);
02129         cpl_matrix_delete(y);
02130         cpl_matrix_delete(sigma);
02131 
02132         return 2;
02133 
02134     }
02135 
02136     cpl_matrix_delete(x);
02137     cpl_matrix_delete(y);
02138     cpl_matrix_delete(sigma);
02139 
02140     return 0;
02141 
02142 }
02143 
02144 
02145 inline static cxint
02146 _giraffe_opticalmodel_format(cx_string *s, const GiModel *model,
02147                              GiOpticalModelInfo info)
02148 {
02149 
02150     const cxchar *name = NULL;
02151 
02152     cxbool offsets = FALSE;
02153 
02154     cxint status = 0;
02155 
02156 
02157     cx_assert(s != NULL);
02158 
02159     if (model == NULL) {
02160         return -1;
02161     }
02162 
02163     name = giraffe_model_get_name(model);
02164 
02165     if (name == NULL || strncmp(name, "xoptmod", 7) != 0) {
02166         return -2;
02167     }
02168     else {
02169         if (strncmp(name, "xoptmod2", 8) == 0) {
02170             offsets = TRUE;
02171         }
02172     }
02173 
02174     switch (info) {
02175 
02176         case GI_OPTM_PARAMETER_VALUES:
02177         {
02178 
02179             cxdouble fcoll = 0.;
02180             cxdouble gcam = 0.;
02181             cxdouble theta = 0.;
02182 
02183             fcoll = giraffe_model_get_parameter(model, "FocalLength");
02184             gcam = giraffe_model_get_parameter(model, "Magnification");
02185             theta = giraffe_model_get_parameter(model, "Angle");
02186 
02187             cx_string_sprintf(s, "focal length = %.6f, camera "
02188                               "magnification = %.6f, grating angle = %.9f",
02189                               fcoll, gcam, theta);
02190 
02191             if (offsets == TRUE) {
02192 
02193                 cxdouble sdx = 0.;
02194                 cxdouble sdy = 0.;
02195                 cxdouble sphi = 0.;
02196 
02197                 cx_string *_s = cx_string_new();
02198 
02199                 sdx = giraffe_model_get_parameter(model, "Sdx");
02200                 sdy = giraffe_model_get_parameter(model, "Sdy");
02201                 sphi = giraffe_model_get_parameter(model, "Sphi");
02202 
02203                 cx_string_sprintf(_s, ", slit x-shift = %.9f, slit "
02204                                   "y-shift = %.9f, slit rotation = %.9f",
02205                                   sdx, sdy, sphi);
02206                 cx_string_append(s, cx_string_get(_s));
02207 
02208                 cx_string_delete(_s);
02209                 _s = NULL;
02210 
02211             }
02212 
02213             break;
02214         }
02215 
02216         case GI_OPTM_PARAMETER_ERRORS:
02217         {
02218 
02219             cxdouble fcoll = 0.;
02220             cxdouble gcam = 0.;
02221             cxdouble theta = 0.;
02222 
02223             fcoll = giraffe_model_get_sigma(model, "FocalLength");
02224             gcam = giraffe_model_get_sigma(model, "Magnification");
02225             theta = giraffe_model_get_sigma(model, "Angle");
02226 
02227             cx_string_sprintf(s, "focal length = %.6f, camera "
02228                               "magnification = %.6f, grating angle = %.9f",
02229                               fcoll, gcam, theta);
02230 
02231             if (offsets == TRUE) {
02232 
02233                 cxdouble sdx = 0.;
02234                 cxdouble sdy = 0.;
02235                 cxdouble sphi = 0.;
02236 
02237                 cx_string *_s = cx_string_new();
02238 
02239                 sdx = giraffe_model_get_sigma(model, "Sdx");
02240                 sdy = giraffe_model_get_sigma(model, "Sdy");
02241                 sphi = giraffe_model_get_sigma(model, "Sphi");
02242 
02243                 cx_string_sprintf(_s, ", slit x-shift = %.9f, slit "
02244                                   "y-shift = %.9f, slit rotation = %.9f",
02245                                   sdx, sdy, sphi);
02246                 cx_string_append(s, cx_string_get(_s));
02247 
02248                 cx_string_delete(_s);
02249                 _s = NULL;
02250 
02251             }
02252 
02253             break;
02254         }
02255 
02256         case GI_OPTM_PARAMETER_STATUS:
02257         {
02258 
02259             const cxchar *const s_free = "free";
02260             const cxchar *const s_frozen = "frozen";
02261             const cxchar *t = NULL;
02262 
02263             cx_string *buffer = cx_string_new();
02264 
02265 
02266             t = giraffe_model_frozen_parameter(model, "FocalLength") ?
02267                 s_frozen : s_free;
02268             cx_string_sprintf(buffer, "focal length = %s", t);
02269             cx_string_set(s, cx_string_get(buffer));
02270 
02271             t = giraffe_model_frozen_parameter(model, "Magnification") ?
02272                 s_frozen : s_free;
02273             cx_string_sprintf(buffer, ", camera magnification = %s", t);
02274             cx_string_append(s, cx_string_get(buffer));
02275 
02276             t = giraffe_model_frozen_parameter(model, "Angle") ?
02277                 s_frozen : s_free;
02278             cx_string_sprintf(buffer, ", grating angle = %s", t);
02279             cx_string_append(s, cx_string_get(buffer));
02280 
02281 
02282             if (offsets == TRUE) {
02283 
02284                 t = giraffe_model_frozen_parameter(model, "Sdx") ?
02285                     s_frozen : s_free;
02286                 cx_string_sprintf(buffer, ", slit x-shift = %s", t);
02287                 cx_string_append(s, cx_string_get(buffer));
02288 
02289                 t = giraffe_model_frozen_parameter(model, "Sdy") ?
02290                     s_frozen : s_free;
02291                 cx_string_sprintf(buffer, ", slit y-shift = %s", t);
02292                 cx_string_append(s, cx_string_get(buffer));
02293 
02294                 t = giraffe_model_frozen_parameter(model, "Sphi") ?
02295                     s_frozen : s_free;
02296                 cx_string_sprintf(buffer, ", slit rotation = %s", t);
02297                 cx_string_append(s, cx_string_get(buffer));
02298 
02299             }
02300 
02301             cx_string_delete(buffer);
02302             buffer = NULL;
02303 
02304             break;
02305         }
02306 
02307         default:
02308             status = -2;
02309             break;
02310 
02311     }
02312 
02313     return status;
02314 
02315 }
02316 
02317 
02318 inline static cpl_image *
02319 _giraffe_residuals_fit(GiWlResiduals *residuals, GiLineData *lines,
02320                        const GiLocalization *localization, GiTable *fibers,
02321                        GiTable *slitgeometry, GiSCFitParams *setup)
02322 {
02323 
02324     const cxchar *const fctid = "_giraffe_residuals_fit";
02325 
02326 
02327     cxint i;
02328     cxint status = 0;
02329     cxint ngood = 0;
02330     cxint nlines = 0;
02331     cxint nsubslits = 1;
02332     cxint nx = 0;
02333 
02334     cpl_table *_fibers = NULL;
02335 
02336     cpl_image *locy = NULL;
02337     cpl_image *locw = NULL;
02338     cpl_image *xresiduals = NULL;
02339 
02340 
02341     cx_assert(lines != NULL);
02342     cx_assert(localization != NULL);
02343     cx_assert(fibers != NULL);
02344     cx_assert(slitgeometry != NULL);
02345     cx_assert(setup != NULL);
02346 
02347     _fibers = giraffe_table_get(fibers);
02348     cx_assert(_fibers != NULL);
02349 
02350     locy = giraffe_image_get(localization->locy);
02351     cx_assert(locy != NULL);
02352 
02353     locw = giraffe_image_get(localization->locw);
02354     cx_assert(locw != NULL);
02355 
02356     nx = cpl_image_get_size_y(locy);
02357     nlines = giraffe_linedata_lines(lines);
02358 
02359     xresiduals = cpl_image_new(cpl_table_get_nrow(_fibers), nx,
02360                                CPL_TYPE_DOUBLE);
02361 
02362     if (setup->subslits == TRUE) {
02363         nsubslits = _giraffe_subslit_get_max(_fibers);
02364     }
02365 
02366     for (i = 0; i < nsubslits; i++) {
02367 
02368         cxint j;
02369         cxint k;
02370         cxint ssn = 0;
02371         cxint nfibers = 0;
02372         cxint ndata = 0;
02373         cxint iterations = 0;
02374         cxint accepted = 0;
02375         cxint total = 0;
02376 
02377         cxdouble ymin = 0.;
02378         cxdouble ymax = 0.;
02379         cxdouble ratio = 1.;
02380         cxdouble sigma = 0.;
02381 
02382         cpl_matrix *xss = NULL;
02383         cpl_matrix *yss = NULL;
02384         cpl_matrix *rss = NULL;
02385         cpl_matrix *sss = NULL;
02386         cpl_matrix *nss = NULL;
02387         cpl_matrix *lss = NULL;
02388         cpl_matrix *base = NULL;
02389         cpl_matrix *fit = NULL;
02390         cpl_matrix *coeff = NULL;
02391         cpl_matrix *chebyshev = NULL;
02392 
02393         cpl_table *subslit = NULL;
02394 
02395         GiChebyshev2D *xwsfit = NULL;
02396 
02397 
02398         if (setup->subslits == TRUE) {
02399             subslit = _giraffe_subslit_get(_fibers, i + 1);
02400             ssn = cpl_table_get_int(subslit, "SSN", 0, NULL);
02401 
02402             cx_assert(ssn == i + 1);
02403         }
02404         else {
02405             subslit = cpl_table_duplicate(_fibers);
02406             ssn = 0;
02407         }
02408 
02409         if (subslit == NULL) {
02410             continue;
02411         }
02412 
02413         _giraffe_subslit_range(subslit, locy, locw, &ymin, &ymax);
02414 
02415         nfibers = cpl_table_get_nrow(subslit);
02416         ndata =  nfibers * nlines;
02417 
02418 
02419         xss = cpl_matrix_new(ndata, 1);   /* X abcissas for subslit */
02420         yss = cpl_matrix_new(ndata, 1);   /* Y ordinates */
02421         rss = cpl_matrix_new(1, ndata);   /* widths (transposed) */
02422         sss = cpl_matrix_new(ndata, 1);   /* width sigmas */
02423         nss = cpl_matrix_new(ndata, 1);   /* fiber indices */
02424         lss = cpl_matrix_new(ndata, 1);   /* line indices */
02425 
02426 
02427         /*
02428          * Fills inputs matrices with good lines xccd, yccd, width,
02429          * width sigmas and line status
02430          */
02431 
02432         k = 0;
02433 
02434         for (j = 0; j < nfibers; j++) {
02435 
02436             cxint l;
02437             cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
02438 
02439             for (l = 0; l < nlines; l++) {
02440 
02441                 cxdouble value = 0.;
02442                 cxdouble xccd = giraffe_linedata_get(lines, "Xccd", n, l);
02443                 cxdouble yccd = giraffe_linedata_get(lines, "Yccd", n, l);
02444 
02445                 if (giraffe_linedata_get_status(lines, n, l) != 0) {
02446                     continue;
02447                 }
02448 
02449                 if (yccd < ymin || yccd > ymax) {
02450                     continue;
02451                 }
02452 
02453                 cpl_matrix_set(xss, k, 0, xccd);
02454                 cpl_matrix_set(yss, k, 0, yccd);
02455 
02456                 value = xccd - giraffe_linedata_get(lines, "Center", n, l);
02457 
02458                 cpl_matrix_set(rss, 0, k, value);
02459                 giraffe_linedata_set(lines, "Xoff", n, l, value);
02460 
02461                 value = giraffe_linedata_get(lines, "dCenter", n, l);
02462                 cpl_matrix_set(sss, k, 0, value);
02463 
02464                 cpl_matrix_set(nss, k, 0, n);
02465                 cpl_matrix_set(lss, k, 0, l);
02466 
02467                 ++k;
02468 
02469             }
02470 
02471         }
02472 
02473         if (k == 0) {
02474             cpl_msg_debug(fctid, "Skipping subslit %d: No input lines left! "
02475                           "All lines have non-zero status or are beyond the "
02476                           "subslit boundaries (%.4f, %.4f).", ssn, ymin, ymax);
02477             continue;
02478         }
02479 
02480         /*
02481          * Shrink input matrices to their actual size
02482          */
02483 
02484         cpl_matrix_set_size(xss, k, 1);
02485         cpl_matrix_set_size(yss, k, 1);
02486         cpl_matrix_set_size(rss, 1, k);
02487         cpl_matrix_set_size(sss, k, 1);
02488         cpl_matrix_set_size(nss, k, 1);
02489         cpl_matrix_set_size(lss, k, 1);
02490 
02491 
02492         /*
02493          * The sigma value used for the sigma-clipping is the
02494          * median value of the line center uncertainties.
02495          */
02496 
02497         sigma = cpl_matrix_get_median(sss);
02498 
02499 
02500         /*
02501          * Sigma clipping
02502          */
02503 
02504         iterations = 0;
02505         ratio = 1.0;
02506         accepted = cpl_matrix_get_ncol(rss);
02507         total = accepted;
02508 
02509         while (accepted > 0 && iterations < setup->clip.iterations &&
02510                ratio > setup->clip.fraction) {
02511 
02512             base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
02513                                             setup->fit.xorder + 1,
02514                                             setup->fit.yorder + 1, xss, yss);
02515 
02516             if (coeff != NULL) {
02517                 cpl_matrix_delete(coeff);
02518                 coeff = NULL;
02519             }
02520 
02521             coeff = giraffe_matrix_leastsq(base, rss);
02522 
02523             if (coeff == NULL) {
02524                 cpl_msg_debug(fctid, "Error solving linear system for "
02525                               "subslit %d, skipping subslit.", ssn);
02526                 break;
02527             }
02528 
02529             fit = cpl_matrix_product_create(coeff, base);
02530 
02531             k = 0;
02532 
02533             for (j = 0; j < cpl_matrix_get_ncol(fit); j++) {
02534 
02535                 cxdouble _fit = cpl_matrix_get(fit, 0, j);
02536                 cxdouble _rss = cpl_matrix_get(rss, 0, j);
02537 
02538                 if (fabs(_fit - _rss) >= setup->clip.level * sigma) {
02539 
02540                     cxint n = (cxint)cpl_matrix_get(nss, j, 0);
02541                     cxint l = (cxint)cpl_matrix_get(lss, j, 0);
02542 
02543                     /*
02544                      * Reject this line
02545                      */
02546 
02547                     giraffe_linedata_set_status(lines, n, l, LF_R_XRFIT);
02548                     continue;
02549 
02550                 }
02551 
02552                 cpl_matrix_set(xss, k, 0, cpl_matrix_get(xss, j, 0));
02553                 cpl_matrix_set(yss, k, 0, cpl_matrix_get(yss, j, 0));
02554                 cpl_matrix_set(rss, 0, k, cpl_matrix_get(rss, 0, j));
02555                 cpl_matrix_set(sss, k, 0, cpl_matrix_get(sss, j, 0));
02556                 cpl_matrix_set(nss, k, 0, cpl_matrix_get(nss, j, 0));
02557                 cpl_matrix_set(lss, k, 0, cpl_matrix_get(lss, j, 0));
02558                 ++k;
02559 
02560             }
02561 
02562             cpl_matrix_delete(base);
02563             cpl_matrix_delete(fit);
02564 
02565             if (k == accepted) {
02566 
02567                 /*
02568                  * No new points rejected, no more iterations. That's it.
02569                  */
02570 
02571                 break;
02572             }
02573             else {
02574                 accepted = k;
02575                 ratio = (cxdouble)accepted / (cxdouble)total;
02576 
02577                 cpl_matrix_set_size(xss, k, 1);
02578                 cpl_matrix_set_size(yss, k, 1);
02579                 cpl_matrix_set_size(rss, 1, k);
02580                 cpl_matrix_set_size(sss, k, 1);
02581                 cpl_matrix_set_size(nss, k, 1);
02582                 cpl_matrix_set_size(lss, k, 1);
02583 
02584                 ++iterations;
02585 
02586             }
02587 
02588         }
02589 
02590         if (accepted == 0) {
02591             cpl_msg_debug(fctid, "Subslit %d: All lines rejected.", ssn);
02592             continue;
02593         }
02594 
02595         if (coeff == NULL) {
02596             continue;
02597         }
02598 
02599         ngood += accepted;
02600 
02601         cpl_matrix_delete(xss);
02602         cpl_matrix_delete(yss);
02603         cpl_matrix_delete(rss);
02604         cpl_matrix_delete(sss);
02605         cpl_matrix_delete(nss);
02606         cpl_matrix_delete(lss);
02607 
02608 
02609         /*
02610          * Compute coordinate grid for the whole subslit
02611          */
02612 
02613         xss = cpl_matrix_new(nx * nfibers, 1);
02614         yss = cpl_matrix_new(nx * nfibers, 1);
02615 
02616         giraffe_compute_image_coordinates(nx, nfibers, xss, NULL);
02617 
02618         for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
02619 
02620             const cxchar *idx = giraffe_fiberlist_query_index(subslit);
02621 
02622             cxint l;
02623             cxint ns = cpl_image_get_size_x(locy);
02624             cxint cs = cpl_table_get_int(subslit, idx, j, NULL) - 1;
02625 
02626             cxdouble *data = cpl_image_get_data(locy);
02627 
02628 
02629             for (l = 0; l < nx; l++) {
02630                 cpl_matrix_set(yss, l * nfibers + j, 0, data[l * ns + cs]);
02631             }
02632 
02633         }
02634 
02635 
02636         /*
02637          * Compute fitted residuals on the whole subslit
02638          */
02639 
02640         base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
02641                                         setup->fit.xorder + 1,
02642                                         setup->fit.yorder + 1, xss, yss);
02643 
02644         fit = cpl_matrix_product_create(coeff, base);
02645 
02646         cpl_matrix_delete(xss);
02647         xss = NULL;
02648 
02649         cpl_matrix_delete(yss);
02650         yss = NULL;
02651 
02652 
02653         /*
02654          * Insert the Chebyshev fit into the results structure
02655          */
02656 
02657         /*
02658          * The matrix 'chebyshev' is a view into 'coeff' with the correct
02659          * shape for the subsequent call of giraffe_chebyshev2d_new().
02660          * Therefore, if 'chebyshev' is destroyed afterwards the data
02661          * of the matrix must not be destroyed!
02662          */
02663 
02664         chebyshev = cpl_matrix_wrap(setup->fit.xorder + 1,
02665                                     setup->fit.yorder + 1,
02666                                     cpl_matrix_get_data(coeff));
02667 
02668         xwsfit = giraffe_chebyshev2d_new(setup->fit.xorder, setup->fit.yorder);
02669         status = giraffe_chebyshev2d_set(xwsfit, 0., nx, ymin, ymax,
02670                                          chebyshev);
02671 
02672         if (status != 0) {
02673 
02674             giraffe_chebyshev2d_delete(xwsfit);
02675 
02676             cpl_matrix_unwrap(chebyshev);
02677 
02678             cpl_matrix_delete(base);
02679             cpl_matrix_delete(coeff);
02680             cpl_matrix_delete(fit);
02681 
02682             cpl_table_delete(subslit);
02683 
02684             cpl_image_delete(xresiduals);
02685 
02686             return NULL;
02687 
02688         }
02689 
02690         cpl_matrix_unwrap(chebyshev);
02691         chebyshev = NULL;
02692 
02693         giraffe_wlresiduals_set(residuals, ssn, xwsfit);
02694         xwsfit = NULL;
02695 
02696 
02697         /*
02698          * Save fitted optical model residuals to the output image.
02699          */
02700 
02701         for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
02702 
02703             cxint l;
02704             cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
02705             cxint ns = cpl_table_get_nrow(_fibers);
02706 
02707             cxdouble *data = cpl_image_get_data(xresiduals);
02708 
02709             for (l = 0; l < nx; l++) {
02710                 data[l * ns + n] = cpl_matrix_get(fit, 0, l * nfibers + j);
02711             }
02712 
02713         }
02714 
02715         cpl_matrix_delete(base);
02716         cpl_matrix_delete(coeff);
02717         cpl_matrix_delete(fit);
02718 
02719         cpl_table_delete(subslit);
02720 
02721     }
02722 
02723     return xresiduals;
02724 
02725 }
02726 
02727 
02728 inline static cxdouble
02729 _giraffe_compute_statistics(const GiLineData *lines, const cpl_image *xwsfit,
02730                             const cpl_image *lflags)
02731 {
02732 
02733     cxint i;
02734     cxint nlines = 0;
02735     cxint nfibers = 0;
02736 
02737     cxdouble sum = 0.;
02738     cxdouble rms = 0.;
02739     cxdouble *_xccd;
02740 
02741     cpl_image *xccd = NULL;
02742 
02743 
02744     cx_assert(lines != NULL);
02745 
02746 
02747     nlines = giraffe_linedata_lines(lines);
02748     nfibers = giraffe_linedata_fibers(lines);
02749 
02750     xccd = cpl_image_duplicate(giraffe_linedata_get_data(lines, "Xccd"));
02751 
02752     if (xwsfit != NULL) {
02753 
02754         cpl_image *residuals = NULL;
02755 
02756 
02757         cx_assert(lflags != NULL);
02758 
02759         residuals = cpl_image_new(giraffe_linedata_fibers(lines),
02760                                   giraffe_linedata_lines(lines),
02761                                   CPL_TYPE_DOUBLE);
02762 
02763         _giraffe_get_residuals(residuals, xccd, xwsfit);
02764         _giraffe_apply_residuals(xccd, residuals, lflags, 0.);
02765 
02766         cpl_image_delete(residuals);
02767         residuals = NULL;
02768 
02769     }
02770 
02771     _xccd = cpl_image_get_data(xccd);
02772 
02773     for (i = 0; i < nfibers; i++) {
02774 
02775         cxint j;
02776 
02777         for (j = 0; j < nlines; j++) {
02778 
02779             if (giraffe_linedata_get_status(lines, i, j) == LF_R_NONE) {
02780 
02781                 cxdouble center = giraffe_linedata_get(lines, "Center", i, j);
02782 
02783                 sum += pow(center - _xccd[j * nfibers + i], 2.);
02784 
02785             }
02786 
02787         }
02788 
02789     }
02790 
02791     cpl_image_delete(xccd);
02792 
02793     rms = sqrt(sum / CX_MAX(giraffe_linedata_accepted(lines), 1.));
02794 
02795     return rms;
02796 
02797 }
02798 
02799 
02800 inline static cxint
02801 _giraffe_convert_wlsolution(GiTable *result, const GiWlSolution *solution,
02802                             const GiImage *spectra, const GiGrating *setup,
02803                             const GiWCalInfo *info, const GiWCalConfig *config)
02804 {
02805 
02806     cxint i;
02807     cxint status = 0;
02808     cxint sign = 1;
02809 
02810     cxdouble value = 0.;
02811     cxdouble scale = 0.;
02812     cxdouble xccd[2] = {0., 0.};
02813 
02814     cx_string *s = NULL;
02815 
02816     cpl_propertylist *properties = NULL;
02817 
02818     cpl_table *coeffs = NULL;
02819 
02820     const GiModel *model = NULL;
02821 
02822     const GiWlResiduals *residuals = NULL;
02823 
02824 
02825     cx_assert(result != NULL);
02826     cx_assert(solution != NULL);
02827 
02828     s = cx_string_new();
02829 
02830     properties =
02831         cpl_propertylist_duplicate(giraffe_image_get_properties(spectra));
02832 
02833 
02834     /* FIXME: Check whether this is still necessary! (RP)
02835      */
02836 
02837     cpl_propertylist_erase(properties, "NAXIS1");
02838     cpl_propertylist_erase(properties, "NAXIS2");
02839     cpl_propertylist_erase(properties, GIALIAS_DATAMIN);
02840     cpl_propertylist_erase(properties, GIALIAS_DATAMAX);
02841     cpl_propertylist_erase(properties, GIALIAS_EXTNAME);
02842 
02843     cpl_propertylist_erase(properties, GIALIAS_PROCATG);
02844     cpl_propertylist_erase(properties, GIALIAS_PROTYPE);
02845     cpl_propertylist_erase(properties, GIALIAS_DATAMEAN);
02846     cpl_propertylist_erase(properties, GIALIAS_DATAMEDI);
02847     cpl_propertylist_erase(properties, GIALIAS_DATASIG);
02848 
02849     cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
02850                                    "WAVCOEFFTAB");
02851     cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
02852                                  "Giraffe frame type.");
02853 
02854 
02855     /*
02856      * Grating data
02857      */
02858 
02859     /* FIXME: Is this needed? (RP)
02860      */
02861 
02862     /*
02863       cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRORDER,
02864       setup->order);
02865       cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRTHETA,
02866       setup->theta);
02867       cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRSPACE,
02868       setup->space);
02869     */
02870 
02871 
02872     /*
02873      * Write line model parameters
02874      */
02875 
02876     cpl_propertylist_update_string(properties, GIALIAS_WSOL_LMNAME,
02877                                    config->line_model);
02878     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMNAME,
02879                                  "Line profile model");
02880 
02881     cpl_propertylist_update_bool(properties, GIALIAS_WSOL_LMRES, info->residuals);
02882     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMRES,
02883                                  "Line detection optical model residuals flag");
02884 
02885     cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMWIDTH, info->width);
02886     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMWIDTH,
02887                                  "Line detection window size [pxl]");
02888 
02889     cpl_propertylist_update_double(properties, GIALIAS_WSOL_LMTHRESH,
02890                                    config->line_threshold);
02891     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMTHRESH,
02892                                  "Calibration line threshold");
02893 
02894     cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMITER,
02895                                 config->line_niter);
02896     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMITER,
02897                                  "Line profile fit maximum number "
02898                                  "of iterations");
02899 
02900     cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMTEST,
02901                                 config->line_ntest);
02902     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMTEST,
02903                                  "Line profile fit maximum number "
02904                                  "of chi-square tests");
02905 
02906     cpl_propertylist_update_double(properties, GIALIAS_WSOL_LMDCHISQ,
02907                                    config->line_dchisq);
02908     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMDCHISQ,
02909                                  "Line profile fit minimum delta "
02910                                  "chi-square");
02911 
02912 
02913     /*
02914      * Write PSF width parameters
02915      */
02916 
02917     cx_string_sprintf(s, "%d:%d", config->pxw_xorder, config->pxw_yorder);
02918 
02919     cpl_propertylist_update_string(properties, GIALIAS_WSOL_PWORDER,
02920                                    cx_string_get(s));
02921     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWORDER,
02922                                  "PSF width fit polynomial order");
02923 
02924     cpl_propertylist_update_double(properties, GIALIAS_WSOL_PWSIGMA,
02925                                    config->pxw_cliplevel);
02926     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWSIGMA,
02927                                  "PSF width fit sigma clipping level");
02928 
02929     cpl_propertylist_update_int(properties, GIALIAS_WSOL_PWITER,
02930                                 config->pxw_clipniter);
02931     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWITER,
02932                                  "PSF width fit maximum number of "
02933                                  "iterations");
02934 
02935     cpl_propertylist_update_double(properties, GIALIAS_WSOL_PWFRAC,
02936                                    config->pxw_clipmfrac);
02937     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWFRAC,
02938                                  "PSF width fit minimum fraction of "
02939                                  "accepted points");
02940 
02941 
02942     /*
02943      * Write optical model parameters to the result table
02944      */
02945 
02946     cpl_propertylist_update_bool(properties, GIALIAS_WSOL_OMFIT,
02947                                  config->opt_solution);
02948     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMFIT,
02949                                  "Optical model fit flag");
02950 
02951     cpl_propertylist_update_string(properties, GIALIAS_WSOL_OMNAME,
02952                                    giraffe_wlsolution_name(solution));
02953     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMNAME,
02954                                  "Optical model name");
02955 
02956     model = giraffe_wlsolution_model(solution);
02957 
02958     sign = giraffe_model_get_parameter(model,"Orientation") < 0 ? -1 : 1;
02959     cpl_propertylist_update_int(properties, GIALIAS_WSOL_OMDIR, sign);
02960     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMDIR,
02961                                  "Optical model orientation");
02962 
02963     value = giraffe_model_get_parameter(model, "FocalLength");
02964     cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMFCOLL, value);
02965     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMFCOLL,
02966                                  "Optical model focal length");
02967 
02968     value = giraffe_model_get_parameter(model, "Magnification");
02969     cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMGCAM, value);
02970     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMGCAM,
02971                                  "Optical model camera factor");
02972 
02973     value = giraffe_model_get_parameter(model, "Angle");
02974     cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMGTHETA, value);
02975     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMGTHETA,
02976                                  "Optical model grating angle");
02977 
02978     if (strcmp(giraffe_wlsolution_name(solution), "xoptmod2") == 0) {
02979 
02980         value = giraffe_model_get_parameter(model, "Sdx");
02981         cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSDX, value);
02982         cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSDX,
02983                                      "Optical model slit x-offset");
02984 
02985         value = giraffe_model_get_parameter(model, "Sdy");
02986         cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSDY, value);
02987         cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSDY,
02988                                      "Optical model slit y-offset");
02989 
02990         value = giraffe_model_get_parameter(model, "Sphi");
02991         cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSPHI, value);
02992         cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSPHI,
02993                                      "Optical model slit rotation");
02994 
02995     }
02996 
02997 
02998     /*
02999      * Add the optical model residuals fit setup parameters
03000      */
03001 
03002 
03003     residuals = giraffe_wlsolution_get_residuals(solution);
03004 
03005     cpl_propertylist_update_bool(properties, GIALIAS_WSOL_SUBSLITS,
03006                                  giraffe_wlsolution_get_subslits(solution));
03007     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_SUBSLITS,
03008                                  "Subslit fit flag");
03009 
03010 
03011     cpl_propertylist_update_int(properties, GIALIAS_WSOL_XRSSN,
03012                                 giraffe_wlresiduals_get_size(residuals));
03013     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRSSN,
03014                                  "Number of subslits");
03015 
03016 
03017     cx_string_sprintf(s, "%d:%d", config->xws_xorder, config->xws_yorder);
03018 
03019     cpl_propertylist_update_string(properties, GIALIAS_WSOL_XRORDER,
03020                                    cx_string_get(s));
03021     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRORDER,
03022                                  "Residual fit polynomial order");
03023 
03024 
03025     cpl_propertylist_update_double(properties, GIALIAS_WSOL_XRSIGMA,
03026                                    config->xws_cliplevel);
03027     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRSIGMA,
03028                                  "Residual fit sigma clipping level");
03029 
03030     cpl_propertylist_update_int(properties, GIALIAS_WSOL_XRITER,
03031                                 config->xws_clipniter);
03032     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRITER,
03033                                  "Residual fit maximum number of "
03034                                  "iterations");
03035 
03036     cpl_propertylist_update_double(properties, GIALIAS_WSOL_XRFRAC,
03037                                    config->xws_clipmfrac);
03038     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRFRAC,
03039                                  "Residual fit minimum fraction of "
03040                                  "accepted points");
03041 
03042     cpl_propertylist_update_int(properties, GIALIAS_WSOL_NLINES,
03043                                 info->nlines);
03044     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NLINES,
03045                                  "Number of calibration lines used.");
03046 
03047     cpl_propertylist_update_int(properties, GIALIAS_WSOL_NACCEPT,
03048                                 info->ngood);
03049     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NACCEPT,
03050                                  "Number of accepted lines");
03051 
03052     cpl_propertylist_update_int(properties, GIALIAS_WSOL_NREJECT,
03053                                 info->nreject);
03054     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NREJECT,
03055                                  "Number of rejected lines");
03056 
03057     cpl_propertylist_update_double(properties, GIALIAS_WSOL_RMS,
03058                                    info->rms);
03059     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_RMS,
03060                                  "Average RMS [pxl] of fitted line "
03061                                  "positions");
03062 
03063 
03064     /*
03065      * Compute approximated pixel to wavelength scale factor
03066      * at the slit center.
03067      */
03068 
03069     xccd[0] = giraffe_wlsolution_compute_pixel(solution, setup->wlenmin,
03070                                                0., 0., &status);
03071     xccd[1] = giraffe_wlsolution_compute_pixel(solution, setup->wlenmax,
03072                                                0., 0., &status);
03073 
03074     scale = (setup->wlenmax - setup->wlenmin) / (xccd[1] - xccd[0]);
03075 
03076 
03077     cpl_propertylist_update_double(properties, GIALIAS_WSOL_WLMIN,
03078                                    setup->wlenmin);
03079     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_WLMIN,
03080                                  "Wavelength solution minimum wavelength");
03081 
03082     cpl_propertylist_update_double(properties, GIALIAS_WSOL_WLMAX,
03083                                    setup->wlenmax);
03084     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_WLMAX,
03085                                  "Wavelength solution maximum wavelength");
03086 
03087     cpl_propertylist_update_double(properties, GIALIAS_WSOL_SCALE, scale);
03088     cpl_propertylist_set_comment(properties, GIALIAS_WSOL_SCALE,
03089                                  "Approximate wavelength scale [nm/pxl]");
03090 
03091 
03092     cx_string_delete(s);
03093     s = NULL;
03094 
03095 
03096     /*
03097      * Consistency check of optical model residuals fit coefficients.
03098      */
03099 
03100     for (i = 0; (cxsize)i < giraffe_wlresiduals_get_size(residuals); i++) {
03101 
03102         const GiChebyshev2D *fit = giraffe_wlresiduals_get(residuals, i);
03103 
03104 
03105         if (fit != NULL) {
03106 
03107             cxint xorder = 0;
03108             cxint yorder = 0;
03109 
03110 
03111             giraffe_chebyshev2d_get_order(fit, &xorder, &yorder);
03112 
03113             if (xorder != config->xws_xorder || yorder != config->xws_yorder) {
03114 
03115                 gi_error("Invalid wavelength solution. Inconsistent residual "
03116                          "fit polynomial order!");
03117 
03118             }
03119 
03120         }
03121 
03122     }
03123 
03124 
03125     /*
03126      * Write optical model residuals fit coefficients.
03127      */
03128 
03129     coeffs = giraffe_wlresiduals_table(residuals);
03130 
03131     if (coeffs == NULL) {
03132         cpl_propertylist_delete(properties);
03133         return 1;
03134     }
03135 
03136     giraffe_table_set_properties(result, properties);
03137     cpl_propertylist_delete(properties);
03138     properties = NULL;
03139 
03140     giraffe_table_set(result, coeffs);
03141 
03142     cpl_table_delete(coeffs);
03143     coeffs = NULL;
03144 
03145     return 0;
03146 
03147 }
03148 
03149 
03150 GiWCalData *
03151 giraffe_wcaldata_new(void)
03152 {
03153 
03154     GiWCalData *self = cx_calloc(1, sizeof *self);
03155 
03156     self->coeffs = NULL;
03157     self->lines = NULL;
03158     self->linedata = NULL;
03159 
03160     return self;
03161 
03162 }
03163 
03164 
03165 void
03166 giraffe_wcaldata_delete(GiWCalData *self)
03167 {
03168 
03169     if (self) {
03170 
03171         if (self->coeffs) {
03172             giraffe_table_delete(self->coeffs);
03173             self->coeffs = NULL;
03174         }
03175 
03176         if (self->lines) {
03177             giraffe_table_delete(self->lines);
03178             self->lines = NULL;
03179         }
03180 
03181         if (self->linedata) {
03182             giraffe_linedata_delete(self->linedata);
03183             self->linedata = NULL;
03184         }
03185 
03186         cx_free(self);
03187 
03188     }
03189 
03190     return;
03191 
03192 }
03193 
03194 
03216 cxint
03217 giraffe_calibrate_wavelength(GiWCalData *result, GiExtraction *extraction,
03218                              GiLocalization *localization, GiTable *fibers,
03219                              GiTable *slitgeometry, GiTable *grating,
03220                              GiTable *lines, GiTable *initial,
03221                              GiWCalConfig *config)
03222 {
03223 
03224     const cxchar *fctid = "giraffe_calibrate_wavelength";
03225 
03226 
03227     cxbool residuals = FALSE;
03228 
03229     cxint i;
03230     cxint status = 0;
03231     cxint nlines = 0;
03232     cxint width = 0;
03233 
03234     cxdouble rms = 0.;
03235 
03236     cpl_image *psf_fit = NULL;
03237     cpl_image *xws_fit = NULL;
03238 
03239     GiTable *tsolution = NULL;
03240 
03241     GiGrating *setup = NULL;
03242 
03243     GiSCFitParams psf_setup;
03244     GiSCFitParams xws_setup;
03245 
03246     GiLineParams *line_setup = NULL;
03247 
03248     GiLineData *line_data = NULL;
03249 
03250     GiWlSolution *solution = NULL;
03251 
03252     GiWCalInfo info;
03253 
03254 
03255     if (extraction == NULL) {
03256         return 1;
03257     }
03258 
03259     if (extraction->spectra == NULL || extraction->error == NULL) {
03260         return 1;
03261     }
03262 
03263 
03264     if (fibers == NULL) {
03265         return 1;
03266     }
03267 
03268     if (slitgeometry == NULL) {
03269         return 1;
03270     }
03271 
03272     if (grating == NULL) {
03273         return 1;
03274     }
03275 
03276     if (lines == NULL) {
03277         return 1;
03278     }
03279 
03280 
03281     /*
03282      * Setup grating data
03283      */
03284 
03285     setup = giraffe_grating_create(extraction->spectra, grating);
03286 
03287     if (setup == NULL) {
03288         cpl_msg_error(fctid, "Cannot initialize grating setup parameters!");
03289         return 2;
03290     }
03291 
03292     if (config->slit_position != 0) {
03293 
03294         if (config->slit_position & SLIT_DX) {
03295             setup->sdx = config->slit_dx;
03296         }
03297 
03298         if (config->slit_position & SLIT_DY) {
03299             setup->sdy = config->slit_dy;
03300         }
03301 
03302         if (config->slit_position & SLIT_PHI) {
03303             setup->sphi = config->slit_phi;
03304         }
03305 
03306         cpl_msg_info(fctid, "Setting initial slit offsets: x-shift = %.9f, "
03307                      "y-shift = %.9f, rotation = %.9f", setup->sdx,
03308                      setup->sdy, setup->sphi);
03309 
03310     }
03311 
03312 
03313     /*
03314      * Setup line fitting parameters
03315      */
03316 
03317     /* FIXME: The following assumes a fixed line type. If this should ever
03318      *        be configurable (requires other than ThArNe line catalogs)
03319      *        this has to be set correctly. (RP)
03320      */
03321 
03322     line_setup = _giraffe_lineparams_create(GI_LINETYPE_THARNE, setup,
03323                                             config);
03324 
03325     if (line_setup == NULL) {
03326         cpl_msg_error(fctid, "Cannot initialize line fit setup parameters!");
03327 
03328         giraffe_grating_delete(setup);
03329 
03330         return 3;
03331     }
03332 
03333 
03334     /*
03335      * Setup initial wavelength solution
03336      */
03337 
03338     if (initial == NULL) {
03339 
03340         cxint npixel = 0;
03341         cxdouble pixelsize = 0.;
03342 
03343         cpl_propertylist *properties = NULL;
03344 
03345         cpl_image *spectra = NULL;
03346 
03347 
03348         properties = giraffe_image_get_properties(extraction->spectra);
03349         cx_assert(properties != NULL);
03350 
03351         spectra = giraffe_image_get(extraction->spectra);
03352         cx_assert(spectra != NULL);
03353 
03354         pixelsize = cpl_propertylist_get_double(properties, GIALIAS_PIXSIZY);
03355         npixel = cpl_image_get_size_y(spectra);
03356 
03357         solution = giraffe_wlsolution_new(config->opt_model,
03358                                           config->opt_direction, npixel,
03359                                           pixelsize, setup);
03360 
03361         if (solution == NULL) {
03362             cpl_msg_error(fctid, "Cannot initialize initial wavelength "
03363                           "solution!");
03364 
03365             giraffe_grating_delete(setup);
03366 
03367             _giraffe_lineparams_delete(line_setup);
03368 
03369             return 4;
03370         }
03371 
03372     }
03373     else {
03374 
03375         const cpl_propertylist* _properties =
03376             giraffe_table_get_properties(initial);
03377 
03378 
03379         /*
03380          * Use a previous wavelength solution as the initial solution.
03381          */
03382 
03383         solution = giraffe_wlsolution_create(initial, extraction->spectra,
03384                                              setup);
03385 
03386         /*
03387          * Set the grating wavelength range to the minimum and maximum
03388          * values of the initial solution, if they are present.
03389          *
03390          * These values will then be used as the range for the line
03391          * selection.
03392          */
03393 
03394         if (cpl_propertylist_has(_properties, GIALIAS_WSOL_WLMIN) == TRUE) {
03395 
03396             setup->wlenmin = cpl_propertylist_get_double(_properties,
03397                     GIALIAS_WSOL_WLMIN);
03398 
03399             cpl_msg_debug(fctid, "Using minimum wavelength %.2f from "
03400                     "initial solution", setup->wlenmin);
03401 
03402         }
03403 
03404         if (cpl_propertylist_has(_properties, GIALIAS_WSOL_WLMAX) == TRUE) {
03405 
03406             setup->wlenmax = cpl_propertylist_get_double(_properties,
03407                     GIALIAS_WSOL_WLMAX);
03408 
03409             cpl_msg_debug(fctid, "Using maximum wavelength %.2f from "
03410                     "initial solution", setup->wlenmax);
03411 
03412         }
03413 
03414     }
03415 
03416     giraffe_wlsolution_set_subslits(solution, config->opt_subslits);
03417 
03418 
03419     cpl_msg_info(fctid, "Computing line positions on the CCD using "
03420                  "model `%s'", giraffe_wlsolution_name(solution));
03421 
03422 
03423     /*
03424      * Check whether optical model residuals should be used.
03425      */
03426 
03427     if (strcmp(config->line_residuals, "enable") == 0) {
03428 
03429         if (giraffe_wlsolution_get_residuals(solution) == NULL) {
03430 
03431             cpl_msg_error(fctid, "Initial wavelength solution does not "
03432                           "provide optical model residuals!");
03433 
03434             giraffe_grating_delete(setup);
03435 
03436             _giraffe_lineparams_delete(line_setup);
03437 
03438             giraffe_wlsolution_delete(solution);
03439 
03440             return 5;
03441         }
03442         else {
03443 
03444             residuals = TRUE;
03445 
03446         }
03447     }
03448     else if (strcmp(config->line_residuals, "auto") == 0) {
03449 
03450         residuals = giraffe_wlsolution_get_residuals(solution) != NULL;
03451 
03452         if (residuals == TRUE) {
03453             cpl_msg_info(fctid, "Using wavelength solution residuals when "
03454                          "computing line positions.");
03455         }
03456 
03457     }
03458     else {
03459 
03460         residuals = FALSE;
03461 
03462     }
03463 
03464 
03465     /*
03466      * Basic selection of lines from the input line list
03467      */
03468 
03469     status = _giraffe_linelist_setup(lines, setup, config);
03470 
03471     if (status) {
03472         cpl_msg_error(fctid, "Line list creation failed!");
03473 
03474         giraffe_grating_delete(setup);
03475 
03476         _giraffe_lineparams_delete(line_setup);
03477 
03478         giraffe_wlsolution_delete(solution);
03479 
03480         return 6;
03481     }
03482 
03483     nlines = cpl_table_get_nrow(giraffe_table_get(lines));
03484     cpl_msg_info(fctid, "%d lines have been selected from the line list.",
03485                  nlines);
03486 
03487 
03488     /*
03489      * Line fitting loop.
03490      */
03491 
03492     line_data = giraffe_linedata_new();
03493 
03494     for (i = 0; i < config->line_nwidths; i++) {
03495 
03496         cxint _nlines = 0;
03497         cxint _nfibers = 0;
03498         cxint _nreject = 0;
03499         cxint _ngood = 0;
03500 
03501         cpl_table *_lines = NULL;
03502         cpl_table *_fibers = giraffe_table_get(fibers);
03503 
03504         cpl_image *xccd = NULL;
03505         cpl_image *xres = NULL;
03506         cpl_image *line_status = NULL;
03507 
03508         GiWlResiduals *xws_coeffs = NULL;
03509 
03510 
03511 
03512         width = config->line_widths[i];
03513 
03514         cpl_msg_info(fctid, "Current search window width: %d pxl", width);
03515         cpl_msg_info(fctid, "Applying crowding criterium to line list.");
03516 
03517         _lines = _giraffe_linelist_select(lines, extraction->spectra, setup,
03518                                           width, config);
03519         if (_lines == NULL) {
03520             cpl_msg_error(fctid, "Removing crowded lines from line list "
03521                           "for search window width %d pxl failed!", width);
03522 
03523             giraffe_grating_delete(setup);
03524 
03525             _giraffe_lineparams_delete(line_setup);
03526 
03527             giraffe_wlsolution_delete(solution);
03528 
03529             return 7;
03530         }
03531 
03532         _nlines = cpl_table_get_nrow(_lines);
03533 
03534 #if 0
03535         cpl_msg_info(fctid, "%d lines used for fit. %d of %d lines rejected "
03536                      "due to crowding.", _nlines, nlines - _nlines, nlines);
03537 #else
03538         cpl_msg_info(fctid, "%d lines used for fit. %d of %d lines "
03539                      "rejected due to crowding and line quality.",
03540                      _nlines, nlines - _nlines, nlines);
03541 #endif
03542 
03543         /*
03544          * Compute line position on the CCD along the dispersion axis
03545          * from the current optical model, and optionally the residuals.
03546          */
03547 
03548         xccd = _giraffe_line_abscissa(_lines, slitgeometry, fibers,
03549                                       solution, localization, residuals);
03550 
03551         _nfibers = cpl_image_get_size_x(xccd);
03552         _nlines = cpl_image_get_size_y(xccd);
03553 
03554 
03555         /*
03556          * Fit a model to individual lines and reject the `bad' ones.
03557          */
03558 
03559         cpl_msg_info(fctid, "Fitting %d line profiles for %d spectra using "
03560                      "line model `%s'", _nlines, _nfibers , line_setup->model);
03561 
03562         cpl_msg_info(fctid, "Total number of lines to fit: %d  (%d x %d)",
03563                      _nlines * _nfibers, _nfibers, _nlines);
03564 
03565 
03566         status = giraffe_linedata_reset(line_data, _lines, _fibers,
03567                                         line_setup->model);
03568 
03569         if (status != 0) {
03570 
03571             cpl_msg_error(fctid, "Line profile fit failed!");
03572 
03573             cpl_image_delete(xccd);
03574 
03575             cpl_table_delete(_lines);
03576             giraffe_linedata_delete(line_data);
03577 
03578             giraffe_grating_delete(setup);
03579 
03580             _giraffe_lineparams_delete(line_setup);
03581 
03582             giraffe_wlsolution_delete(solution);
03583 
03584             return 8;
03585 
03586         }
03587 
03588         status = _giraffe_line_fit(line_data, xccd, width, extraction, fibers,
03589                                    localization->locy, line_setup);
03590 
03591         if (status != 0) {
03592 
03593             cpl_msg_error(fctid, "Line profile fit failed!");
03594 
03595             cpl_image_delete(xccd);
03596 
03597             cpl_table_delete(_lines);
03598             giraffe_linedata_delete(line_data);
03599 
03600             giraffe_grating_delete(setup);
03601 
03602             _giraffe_lineparams_delete(line_setup);
03603 
03604             giraffe_wlsolution_delete(solution);
03605 
03606             return 8;
03607 
03608         }
03609 
03610         cpl_image_delete(xccd);
03611         xccd = NULL;
03612 
03613         if (giraffe_linedata_accepted(line_data) == 0) {
03614             cpl_msg_error(fctid, "No lines left after line profile fit!");
03615 
03616             cpl_table_delete(_lines);
03617             giraffe_linedata_delete(line_data);
03618 
03619             giraffe_grating_delete(setup);
03620 
03621             _giraffe_lineparams_delete(line_setup);
03622 
03623             giraffe_wlsolution_delete(solution);
03624 
03625             return 9;
03626 
03627         }
03628 
03629         _nreject = giraffe_linedata_rejected(line_data);
03630         _ngood = giraffe_linedata_accepted(line_data);
03631 
03632         cpl_msg_info(fctid, "Number of good lines: %d. %d of %d lines "
03633                      "rejected due to line profile fit.", _ngood, _nreject,
03634                      _nlines *_nfibers);
03635 
03636         /*
03637          * Save line fit status for final statistics
03638          */
03639 
03640         line_status = giraffe_linedata_status(line_data);
03641 
03642 
03643         /*
03644          * Fit the lines PSF width variation over the CCD
03645          */
03646 
03647         cpl_msg_info(fctid, "Fit of the line profile PSF width variation.");
03648 
03649         psf_setup.subslits = giraffe_wlsolution_get_subslits(solution);
03650         psf_setup.fit.xorder = config->pxw_xorder;
03651         psf_setup.fit.yorder = config->pxw_yorder;
03652 
03653         cpl_msg_info(fctid, "Chebyshev polynomial order is (%d, %d).",
03654                      psf_setup.fit.xorder, psf_setup.fit.yorder);
03655 
03656         psf_setup.clip.iterations = config->pxw_clipniter;
03657         psf_setup.clip.level = config->pxw_cliplevel;
03658         psf_setup.clip.fraction = config->pxw_clipmfrac;
03659 
03660         cpl_msg_info(fctid, "Sigma clipping: iterations = %d, level = %.4f, "
03661                      "fraction = %.4f", psf_setup.clip.iterations,
03662                      psf_setup.clip.level, psf_setup.clip.fraction);
03663 
03664 
03665         psf_fit = _giraffe_psf_fit(line_data, localization, fibers,
03666                                    slitgeometry, &psf_setup);
03667 
03668 
03669         if (psf_fit == NULL) {
03670 
03671             cpl_msg_error(fctid, "Fit of the line profile PSF width "
03672                           "variation failed!");
03673 
03674             cpl_table_delete(_lines);
03675             cpl_image_delete(line_status);
03676             giraffe_linedata_delete(line_data);
03677 
03678             giraffe_grating_delete(setup);
03679 
03680             _giraffe_lineparams_delete(line_setup);
03681 
03682             giraffe_wlsolution_delete(solution);
03683 
03684             return 10;
03685 
03686         }
03687         else {
03688 
03689             /* FIXME: Not used, debugging only. Maybe it should go away
03690              *        completely (RP)
03691              */
03692 
03693             cpl_image_delete(psf_fit);
03694 
03695         }
03696 
03697         if (giraffe_linedata_accepted(line_data) == 0) {
03698             cpl_msg_error(fctid, "No lines left after line profile PSF "
03699                           "width fit!");
03700 
03701             cpl_table_delete(_lines);
03702             cpl_image_delete(line_status);
03703             giraffe_linedata_delete(line_data);
03704 
03705             giraffe_grating_delete(setup);
03706 
03707             _giraffe_lineparams_delete(line_setup);
03708 
03709             giraffe_wlsolution_delete(solution);
03710 
03711             return 10;
03712 
03713         }
03714 
03715         cpl_msg_info(fctid, "Number of good lines: %"
03716                      CX_PRINTF_FORMAT_SIZE_TYPE ". %"
03717                      CX_PRINTF_FORMAT_SIZE_TYPE "of %d lines "
03718                      "rejected due to line profile PSF width fit.",
03719                      giraffe_linedata_accepted(line_data),
03720                      giraffe_linedata_rejected(line_data) - _nreject, _ngood);
03721 
03722         _ngood = giraffe_linedata_accepted(line_data);
03723         _nreject = giraffe_linedata_rejected(line_data);
03724 
03725 
03726         /*
03727          * If requested, fit of a new optical model using the current
03728          * selection of good lines.
03729          */
03730 
03731         if (config->opt_solution == TRUE) {
03732 
03733             cxint iterations = 0;
03734             cxint df = 0;
03735 
03736             cxdouble chisq = 0.;
03737             cxdouble rsquare = 0.;
03738 
03739             cx_string *s = NULL;
03740 
03741             GiOpticalModelParams om_setup;
03742 
03743             GiModel *guess = NULL;
03744             GiModel *model = giraffe_wlsolution_model(solution);
03745 
03746 
03747             /*
03748              * Save initial guess model
03749              */
03750 
03751             guess = giraffe_model_clone(model);
03752 
03753             om_setup.fit.iterations = config->opt_niter;
03754             om_setup.fit.tests = config->opt_ntest;
03755             om_setup.fit.delta = config->opt_dchisq;
03756             om_setup.flags = config->opt_flags;
03757 
03758             cpl_msg_info(fctid, "Optical model fit setup: iterations = %d, "
03759                          "tests = %d, delta = %.4f", om_setup.fit.iterations,
03760                          om_setup.fit.tests, om_setup.fit.delta);
03761 
03762             status = _giraffe_opticalmodel_fit(solution, line_data, fibers,
03763                                                slitgeometry, &om_setup);
03764 
03765             if (status != 0) {
03766                 cpl_msg_error(fctid, "Optical model fit failed!");
03767 
03768                 giraffe_model_delete(guess);
03769 
03770                 cpl_table_delete(_lines);
03771                 cpl_image_delete(line_status);
03772                 giraffe_linedata_delete(line_data);
03773 
03774                 giraffe_grating_delete(setup);
03775 
03776                 _giraffe_lineparams_delete(line_setup);
03777 
03778                 giraffe_wlsolution_delete(solution);
03779 
03780                 return 11;
03781 
03782             }
03783 
03784 
03785             /*
03786              * Output of fit results
03787              */
03788 
03789             cpl_msg_info(fctid, "Optical model parameters:");
03790 
03791             s = cx_string_new();
03792 
03793             _giraffe_opticalmodel_format(s, guess, GI_OPTM_PARAMETER_VALUES);
03794             cpl_msg_info(fctid, "Initial: %s", cx_string_get(s));
03795 
03796             _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_VALUES);
03797             cpl_msg_info(fctid, " Fitted: %s", cx_string_get(s));
03798 
03799             _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_ERRORS);
03800             cpl_msg_info(fctid, "  Sigma: %s", cx_string_get(s));
03801 
03802             _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_STATUS);
03803             cpl_msg_info(fctid, " Status: %s", cx_string_get(s));
03804 
03805             cx_string_delete(s);
03806             s = NULL;
03807 
03808 #if 0
03809             if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
03810 
03811                 cxdouble flength = 0.;
03812                 cxdouble sdx = 0.;
03813                 cxdouble sdy = 0.;
03814                 cxdouble sphi = 0.;
03815 
03816                 cx_string *s = cx_string_new();
03817 
03818 
03819                 flength = giraffe_model_get_parameter(guess, "FocalLength");
03820                 sdx = giraffe_model_get_parameter(guess, "Sdx");
03821                 sdy = giraffe_model_get_parameter(guess, "Sdy");
03822                 sphi = giraffe_model_get_parameter(guess, "Sphi");
03823 
03824                 cx_string_sprintf(s, "Initial: focal length = %.6f, slit "
03825                                   "x-shift = %.9f, slit y-shift = %.9f, "
03826                                   "slit rotation = %.9f", flength, sdx, sdy,
03827                                   sphi);
03828                 cpl_msg_info(fctid, "%s", cx_string_get(s));
03829 
03830 
03831                 flength = giraffe_model_get_parameter(model, "FocalLength");
03832                 sdx = giraffe_model_get_parameter(model, "Sdx");
03833                 sdy = giraffe_model_get_parameter(model, "Sdy");
03834                 sphi = giraffe_model_get_parameter(model, "Sphi");
03835 
03836                 cx_string_sprintf(s, " Fitted: focal length = %.6f, slit "
03837                                   "x-shift = %.9f, slit y-shift = %.9f, "
03838                                   "slit rotation = %.9f", flength, sdx, sdy,
03839                                   sphi);
03840                 cpl_msg_info(fctid, "%s", cx_string_get(s));
03841 
03842 
03843                 flength = giraffe_model_get_sigma(model, "FocalLength");
03844                 sdx = giraffe_model_get_sigma(model, "Sdx");
03845                 sdy = giraffe_model_get_sigma(model, "Sdy");
03846                 sphi = giraffe_model_get_sigma(model, "Sphi");
03847 
03848                 cx_string_sprintf(s, "  Sigma: focal length = %.6f, slit "
03849                                   "x-shift = %.9f, slit y-shift = %.9f, "
03850                                   "slit rotation = %.9f", flength, sdx, sdy,
03851                                   sphi);
03852                 cpl_msg_info(fctid, "%s", cx_string_get(s));
03853 
03854                 cx_string_delete(s);
03855 
03856             }
03857             else {
03858 
03859                 cxdouble flength = 0.;
03860 
03861                 cx_string *s = cx_string_new();
03862 
03863 
03864                 flength = giraffe_model_get_parameter(guess, "FocalLength");
03865 
03866                 cx_string_sprintf(s, "Initial: focal length = %.6f", flength);
03867                 cpl_msg_info(fctid, "%s", cx_string_get(s));
03868 
03869 
03870                 flength = giraffe_model_get_parameter(model, "FocalLength");
03871 
03872                 cx_string_sprintf(s, " Fitted: focal length = %.6f", flength);
03873                 cpl_msg_info(fctid, "%s", cx_string_get(s));
03874 
03875 
03876                 flength = giraffe_model_get_sigma(model, "FocalLength");
03877 
03878                 cx_string_sprintf(s, "  Sigma: focal length = %.6f", flength);
03879                 cpl_msg_info(fctid, "%s", cx_string_get(s));
03880 
03881                 cx_string_delete(s);
03882 
03883             }
03884 #endif
03885 
03886             giraffe_model_delete(guess);
03887 
03888 
03889             iterations = giraffe_model_get_position(model);
03890             df = giraffe_model_get_df(model);
03891 
03892             chisq = giraffe_model_get_chisq(model);
03893             rsquare = giraffe_model_get_rsquare(model);
03894 
03895             cpl_msg_info(fctid, "Optical model fit statistics: iterations = "
03896                          "%d, DoF = %d, Chi-square = %.6g, Chi-square/DoF = "
03897                          "%.6g, R-square = %.6g", iterations, df, chisq,
03898                          chisq / df, rsquare);
03899 
03900 
03901             /*
03902              * Update grating data structure with fitted parameters
03903              * for next iteration.
03904              */
03905 
03906             setup->fcoll = giraffe_model_get_parameter(model, "FocalLength");
03907             setup->gcam = giraffe_model_get_parameter(model, "Magnification");
03908             setup->theta = giraffe_model_get_parameter(model, "Angle");
03909             setup->order = giraffe_model_get_parameter(model, "Order");
03910             setup->space = giraffe_model_get_parameter(model, "Spacing");
03911 
03912             if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
03913                 setup->sdx = giraffe_model_get_parameter(model, "Sdx");
03914                 setup->sdy = giraffe_model_get_parameter(model, "Sdy");
03915                 setup->sphi = giraffe_model_get_parameter(model, "Sphi");
03916             }
03917 
03918 
03919             /*
03920              * Re-compute line positions using the updated optical model
03921              * and update the line data object accordingly. The residuals,
03922              * which belong to the previous optical model must not be used
03923              * here.
03924              */
03925 
03926             /* FIXME: Check why the OGL code computes the y positions of
03927              *        the lines, but does not use or store them. (RP)
03928              */
03929 
03930             cpl_msg_info(fctid, "Re-computing line positions with updated "
03931                          "optical model");
03932 
03933             xccd = _giraffe_line_abscissa(_lines, slitgeometry, fibers,
03934                                           solution, localization, FALSE);
03935 
03936             giraffe_linedata_set_data(line_data, "Xccd", xccd);
03937             xccd = NULL;
03938 
03939         }
03940         else {
03941 
03942             cx_string *s = cx_string_new();
03943 
03944             GiModel *model = giraffe_wlsolution_model(solution);
03945 
03946 
03947             _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_VALUES);
03948             cpl_msg_info(fctid, "Optical model: %s", cx_string_get(s));
03949 
03950             cx_string_delete(s);
03951             s = NULL;
03952 
03953         }
03954 
03955         rms = _giraffe_compute_statistics(line_data, NULL, NULL);
03956 
03957         cpl_msg_info(fctid, "Average RMS [pxl] of line positions using "
03958                      "%d of %d lines: %.4f", _ngood, _nlines * _nfibers,
03959                      rms);
03960 
03961 
03962         /*
03963          * Fit the wavelength solution coefficients, i.e. the Chebyshev
03964          * correction computed from fitting the optical model residuals.
03965          */
03966 
03967         cpl_msg_info(fctid, "Fit of the wavelength solution coefficients "
03968                      "using %" CX_PRINTF_FORMAT_SIZE_TYPE " lines",
03969                      giraffe_linedata_accepted(line_data));
03970 
03971         xws_setup.subslits = giraffe_wlsolution_get_subslits(solution);
03972         xws_setup.fit.xorder = config->xws_xorder;
03973         xws_setup.fit.yorder = config->xws_yorder;
03974 
03975         cpl_msg_info(fctid, "Chebyshev polynomial order is (%d, %d).",
03976                      xws_setup.fit.xorder, xws_setup.fit.yorder);
03977 
03978         xws_setup.clip.iterations = config->xws_clipniter;
03979         xws_setup.clip.level = config->xws_cliplevel;
03980         xws_setup.clip.fraction = config->xws_clipmfrac;
03981 
03982         cpl_msg_info(fctid, "Sigma clipping: iterations = %d, level = %.4f, "
03983                      "fraction = %.4f", xws_setup.clip.iterations,
03984                      xws_setup.clip.level, xws_setup.clip.fraction);
03985 
03986         xws_coeffs = giraffe_wlresiduals_new();
03987 
03988         xws_fit = _giraffe_residuals_fit(xws_coeffs, line_data, localization,
03989                                          fibers, slitgeometry, &xws_setup);
03990 
03991 
03992         if (xws_fit == NULL) {
03993 
03994             cpl_msg_error(fctid, "Fit of the wavelength solution "
03995                           "coefficients failed!");
03996 
03997             giraffe_wlresiduals_delete(xws_coeffs);
03998 
03999             cpl_table_delete(_lines);
04000             cpl_image_delete(line_status);
04001             giraffe_linedata_delete(line_data);
04002 
04003             giraffe_grating_delete(setup);
04004 
04005             _giraffe_lineparams_delete(line_setup);
04006 
04007             giraffe_wlsolution_delete(solution);
04008 
04009             return 12;
04010 
04011         }
04012 
04013 
04014         /*
04015          * Update the line data object with the fitted residuals of
04016          * the lines used.
04017          */
04018 
04019         xres = cpl_image_new(giraffe_linedata_fibers(line_data),
04020                              giraffe_linedata_lines(line_data),
04021                              CPL_TYPE_DOUBLE);
04022 
04023         _giraffe_get_residuals(xres,
04024                                giraffe_linedata_get_data(line_data, "Xccd"),
04025                                xws_fit);
04026         giraffe_linedata_set_data(line_data, "Xres", xres);
04027 
04028 
04029         if (giraffe_linedata_accepted(line_data) == 0) {
04030             cpl_msg_error(fctid, "No lines left after wavelength solution "
04031                           "coefficients fit!");
04032 
04033             giraffe_wlresiduals_delete(xws_coeffs);
04034 
04035             cpl_table_delete(_lines);
04036             cpl_image_delete(line_status);
04037             cpl_image_delete(xws_fit);
04038             giraffe_linedata_delete(line_data);
04039 
04040             giraffe_grating_delete(setup);
04041 
04042             _giraffe_lineparams_delete(line_setup);
04043 
04044             giraffe_wlsolution_delete(solution);
04045 
04046             return 12;
04047 
04048         }
04049 
04050         cpl_msg_info(fctid, "Number of good lines: %"
04051                      CX_PRINTF_FORMAT_SIZE_TYPE". %"
04052                      CX_PRINTF_FORMAT_SIZE_TYPE" of %d lines "
04053                      "rejected due to wavelength solution coefficients fit.",
04054                      giraffe_linedata_accepted(line_data),
04055                      giraffe_linedata_rejected(line_data) - _nreject, _ngood);
04056 
04057         _ngood = giraffe_linedata_accepted(line_data);
04058         _nreject = giraffe_linedata_rejected(line_data);
04059 
04060 
04061         /*
04062          * Update the wavelength solution with the computed residuals.
04063          */
04064 
04065         giraffe_wlsolution_set_residuals(solution, xws_coeffs);
04066         xws_coeffs = NULL;
04067 
04068 
04069         /*
04070          * Compute RMS over all line positions as computed by the optical
04071          * model corrected for the fitted residuals and the measured
04072          * positions as derived from the fit of the line profiles.
04073          */
04074 
04075         rms = _giraffe_compute_statistics(line_data, xws_fit, line_status);
04076 
04077         cpl_msg_info(fctid, "Average RMS [pxl] of line positions using "
04078                      "%d of %d lines: %.4f", _ngood, _nlines * _nfibers,
04079                      rms);
04080 
04081 
04082         /*
04083          * Update wavelength calibration information structure
04084          */
04085 
04086         info.width = width;
04087 
04088         info.residuals = residuals;
04089 
04090         info.nlines = _nlines;
04091         info.nfibers = _nfibers;
04092 
04093         info.ngood = _ngood;
04094         info.nreject = _nreject;
04095 
04096         info.rms = rms;
04097 
04098 
04099         /*
04100          * Cleanup data created during this iteration step.
04101          */
04102 
04103         cpl_image_delete(xws_fit);
04104 
04105         cpl_table_delete(_lines);
04106         cpl_image_delete(line_status);
04107 
04108     }
04109 
04110 
04111     /*
04112      * Write the final wavelength solution to the output table. The table
04113      * properties are created from the extracted arc-spectra frame.
04114      */
04115 
04116     tsolution = giraffe_table_new();
04117 
04118     status = _giraffe_convert_wlsolution(tsolution, solution,
04119                                          extraction->spectra, setup,
04120                                          &info, config);
04121 
04122     if (status != 0) {
04123 
04124         giraffe_table_delete(tsolution);
04125 
04126         giraffe_grating_delete(setup);
04127 
04128         _giraffe_lineparams_delete(line_setup);
04129 
04130         giraffe_wlsolution_delete(solution);
04131 
04132         return 13;
04133 
04134     }
04135 
04136 
04137     /*
04138      * Fill the results structure.
04139      */
04140 
04141     result->coeffs = tsolution;
04142     tsolution = NULL;
04143 
04144     result->linedata = line_data;
04145 
04146 
04147     /*
04148      * Final cleanup
04149      */
04150 
04151     giraffe_grating_delete(setup);
04152     giraffe_wlsolution_delete(solution);
04153 
04154     _giraffe_lineparams_delete(line_setup);
04155 
04156     return 0;
04157 
04158 }
04159 
04160 
04171 GiWCalConfig *
04172 giraffe_wlcalibration_config_create(cpl_parameterlist *list)
04173 {
04174 
04175     const cxchar *s = NULL;
04176     const cpl_parameter *p = NULL;
04177 
04178     GiWCalConfig *config = NULL;
04179 
04180 
04181     if (!list) {
04182         return NULL;
04183     }
04184 
04185     config = cx_calloc(1, sizeof *config);
04186 
04187 
04188     /*
04189      * The line saturation level is currently not connected to a parameter,
04190      * but it may become one. Therefore it is put in here with a fixed value.
04191      */
04192 
04193     config->line_saturation = 1.e7;
04194 
04195 
04196     /*
04197      *  Set configuration data
04198      */
04199 
04200     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.widths");
04201     s = cpl_parameter_get_string(p);
04202 
04203     if (s) {
04204 
04205         cxchar **values = cx_strsplit(s, ",", -1);
04206 
04207 
04208         if (values == NULL) {
04209 
04210             giraffe_wlcalibration_config_destroy(config);
04211             return NULL;
04212 
04213         }
04214         else {
04215 
04216             cxchar *last;
04217 
04218             cxint n = 0;
04219 
04220 
04221             while (values[n] != NULL) {
04222                 ++n;
04223             }
04224 
04225             config->line_nwidths = n;
04226             config->line_widths = cx_malloc(n * sizeof(cxint));
04227             cx_assert(config->line_widths != NULL);
04228 
04229             n = 0;
04230             while (values[n] != NULL) {
04231 
04232                 cxint w = strtol(values[n], &last, 10);
04233 
04234                 if (*last != '\0') {
04235                     cx_strfreev(values);
04236                     giraffe_wlcalibration_config_destroy(config);
04237 
04238                     return NULL;
04239                 }
04240 
04241                 config->line_widths[n] = w;
04242                 ++n;
04243 
04244             }
04245 
04246             if (n > 1) {
04247                 qsort(config->line_widths, config->line_nwidths,
04248                       sizeof(cxint), _giraffe_window_compare);
04249             }
04250 
04251             cx_strfreev(values);
04252             values = NULL;
04253 
04254         }
04255 
04256     }
04257 
04258 
04259     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.separation");
04260     config->line_separation = fabs(cpl_parameter_get_double(p));
04261 
04262     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.fluxratio");
04263     config->line_fluxratio = cpl_parameter_get_double(p);
04264 
04265     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.brightness");
04266     config->line_brightness = cpl_parameter_get_double(p);
04267 
04268     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.count");
04269     config->line_count = cpl_parameter_get_int(p);
04270 
04271     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.wrange");
04272     s = cpl_parameter_get_string(p);
04273 
04274     if (s) {
04275 
04276         cxchar **values = cx_strsplit(s, ",", 3);
04277 
04278         if (values == NULL) {
04279 
04280             giraffe_wlcalibration_config_destroy(config);
04281             return NULL;
04282 
04283         }
04284         else {
04285 
04286             cxchar *last;
04287 
04288             cxdouble lower = 0.;
04289             cxdouble upper = 0.;
04290 
04291 
04292             lower = strtod(values[0], &last);
04293 
04294             if (*last != '\0') {
04295 
04296                 cx_strfreev(values);
04297                 giraffe_wlcalibration_config_destroy(config);
04298 
04299                 return NULL;
04300 
04301             }
04302 
04303             lower = lower >= 0. ? lower : 0.;
04304 
04305 
04306             if (values[1] != NULL) {
04307 
04308                 upper = strtod(values[1], &last);
04309 
04310                 if (*last != '\0') {
04311 
04312                     cx_strfreev(values);
04313                     giraffe_wlcalibration_config_destroy(config);
04314 
04315                     return NULL;
04316 
04317                 }
04318 
04319                 upper = upper > lower ? upper : 0.;
04320 
04321             }
04322 
04323             config->line_wlrange = giraffe_range_create(lower, upper);
04324             cx_assert(config->line_wlrange != NULL);
04325 
04326         }
04327 
04328         cx_strfreev(values);
04329         values = NULL;
04330 
04331     }
04332 
04333 
04334     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.model");
04335     s = cpl_parameter_get_string(p);
04336 
04337     if (strcmp(s, "psfexp") != 0 &&
04338         strcmp(s, "psfexp2") != 0 &&
04339         strcmp(s, "gaussian") != 0) {
04340 
04341         giraffe_wlcalibration_config_destroy(config);
04342         return NULL;
04343 
04344     }
04345     else {
04346         config->line_model = cx_strdup(s);
04347     }
04348 
04349 
04350     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.residuals");
04351     s = cpl_parameter_get_string(p);
04352 
04353     if (strcmp(s, "auto") != 0 &&
04354         strcmp(s, "enable") != 0 &&
04355         strcmp(s, "disable") != 0) {
04356 
04357         giraffe_wlcalibration_config_destroy(config);
04358         return NULL;
04359 
04360     }
04361     else {
04362         config->line_residuals = cx_strdup(s);
04363     }
04364 
04365 
04366     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.threshold");
04367     config->line_threshold = cpl_parameter_get_double(p);
04368 
04369     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.offset");
04370     config->line_offset = cpl_parameter_get_double(p);
04371 
04372     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.iterations");
04373     config->line_niter = cpl_parameter_get_int(p);
04374 
04375     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.tests");
04376     config->line_ntest = cpl_parameter_get_int(p);
04377 
04378     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.dchisquare");
04379     config->line_dchisq = cpl_parameter_get_double(p);
04380 
04381     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.rwidthratio");
04382     config->line_rwidthratio = cpl_parameter_get_double(p);
04383 
04384     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.exponent");
04385     config->line_widthexponent = cpl_parameter_get_double(p);
04386 
04387 
04388     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.slit.offset");
04389     s = cpl_parameter_get_string(p);
04390 
04391     cx_assert(s != NULL);
04392 
04393     if (cx_strncasecmp(s, "setup", 5) != 0) {
04394 
04395         cxchar **values = cx_strsplit(s, ",", 4);
04396 
04397 
04398         config->slit_position = 0;
04399         config->slit_dx = 0.;
04400         config->slit_dy = 0.;
04401         config->slit_phi = 0.;
04402 
04403         if (values == NULL || values[0] == NULL) {
04404 
04405             giraffe_wlcalibration_config_destroy(config);
04406             return NULL;
04407 
04408         }
04409         else {
04410 
04411             cxbool eol = FALSE;
04412 
04413             if (*values[0] != '\0') {
04414 
04415                 cxchar *last;
04416                 cxdouble sdx = strtod(values[0], &last);
04417 
04418                 if (*last != '\0') {
04419                     cx_strfreev(values);
04420                     values = NULL;
04421 
04422                     giraffe_wlcalibration_config_destroy(config);
04423 
04424                     return NULL;
04425                 }
04426 
04427                 config->slit_position |= SLIT_DX;
04428                 config->slit_dx = sdx;
04429 
04430             }
04431 
04432             if (values[1] == NULL) {
04433                 eol = TRUE;
04434             }
04435             else {
04436 
04437                 if (*values[1] != '\0') {
04438 
04439                     cxchar *last;
04440                     cxdouble sdy = strtod(values[1], &last);
04441 
04442                     if (*last != '\0') {
04443                         cx_strfreev(values);
04444                         values = NULL;
04445 
04446                         giraffe_wlcalibration_config_destroy(config);
04447 
04448                         return NULL;
04449                     }
04450 
04451                     config->slit_position |= SLIT_DY;
04452                     config->slit_dy = sdy;
04453 
04454                 }
04455 
04456             }
04457 
04458             if (!eol && values[2] != NULL) {
04459 
04460                 if (*values[2] != '\0') {
04461 
04462                     cxchar *last;
04463                     cxdouble sphi = strtod(values[2], &last);
04464 
04465                     if (*last != '\0') {
04466                         cx_strfreev(values);
04467                         values = NULL;
04468 
04469                         giraffe_wlcalibration_config_destroy(config);
04470 
04471                         return NULL;
04472                     }
04473 
04474                     config->slit_position |= SLIT_PHI;
04475                     config->slit_phi = sphi;
04476 
04477                 }
04478 
04479             }
04480 
04481             cx_strfreev(values);
04482             values = NULL;
04483 
04484         }
04485 
04486     }
04487 
04488 
04489     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.model");
04490     s = cpl_parameter_get_string(p);
04491 
04492     if (strcmp(s, "xoptmod") != 0 && strcmp(s, "xoptmod2") != 0) {
04493 
04494         giraffe_wlcalibration_config_destroy(config);
04495         return NULL;
04496 
04497     }
04498     else {
04499         config->opt_model = cx_strdup(s);
04500     }
04501 
04502 
04503     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.direction");
04504     config->opt_direction = cpl_parameter_get_int(p);
04505 
04506     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.solution");
04507     config->opt_solution = cpl_parameter_get_bool(p);
04508 
04509     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.subslits");
04510     config->opt_subslits = cpl_parameter_get_bool(p);
04511 
04512     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.flags");
04513     s = cpl_parameter_get_string(p);
04514 
04515     if (s) {
04516 
04517         cxchar **values = cx_strsplit(s, ",", -1);
04518 
04519         if (values == NULL) {
04520 
04521             giraffe_wlcalibration_config_destroy(config);
04522             return NULL;
04523 
04524         }
04525         else {
04526 
04527             cxint i = 0;
04528             cxint n = 0;
04529 
04530             config->opt_flags = 0;
04531 
04532             while (values[i] != NULL) {
04533 
04534                 if (strncmp(values[i], "fcoll", 5) == 0) {
04535                     config->opt_flags |= OPTM_FLENGTH;
04536                     ++n;
04537                 }
04538                 else if (strncmp(values[i], "gcam", 4) == 0) {
04539                     config->opt_flags |= OPTM_GCAMERA;
04540                     ++n;
04541                 }
04542                 else if (strncmp(values[i], "theta", 5) == 0) {
04543                     config->opt_flags |= OPTM_THETA;
04544                     ++n;
04545                 }
04546                 else if (strncmp(values[i], "sdx", 3) == 0) {
04547                     config->opt_flags |= OPTM_SX;
04548                     ++n;
04549                 }
04550                 else if (strncmp(values[i], "sdy", 3) == 0) {
04551                     config->opt_flags |= OPTM_SY;
04552                     ++n;
04553                 }
04554                 else if (strncmp(values[i], "sphi", 4) == 0) {
04555                     config->opt_flags |= OPTM_SPHI;
04556                     ++n;
04557                 }
04558 
04559                 ++i;
04560 
04561             }
04562 
04563             cx_strfreev(values);
04564             values = NULL;
04565 
04566 
04567             /*
04568              * Stop if no valid optical model parameter flag was seen
04569              */
04570 
04571             if (n == 0) {
04572                 giraffe_wlcalibration_config_destroy(config);
04573                 return NULL;
04574             }
04575 
04576         }
04577 
04578     }
04579 
04580 
04581     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.iterations");
04582     config->opt_niter = cpl_parameter_get_int(p);
04583 
04584     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.tests");
04585     config->opt_ntest = cpl_parameter_get_int(p);
04586 
04587     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.dchisquare");
04588     config->opt_dchisq = cpl_parameter_get_double(p);
04589 
04590 
04591     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.sigma");
04592     config->pxw_cliplevel = cpl_parameter_get_double(p);
04593 
04594     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.iterations");
04595     config->pxw_clipniter = cpl_parameter_get_int(p);
04596 
04597     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.fraction");
04598     config->pxw_clipmfrac = cpl_parameter_get_double(p);
04599 
04600     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.order");
04601     s = cpl_parameter_get_string(p);
04602 
04603     if (s) {
04604 
04605         cxchar **values = cx_strsplit(s, ",", 3);
04606 
04607         if (values == NULL || values[1] == NULL) {
04608 
04609             giraffe_wlcalibration_config_destroy(config);
04610             return NULL;
04611 
04612         }
04613         else {
04614 
04615             cxchar *last;
04616 
04617 
04618             config->pxw_xorder = strtol(values[0], &last, 10);
04619 
04620             if (*last != '\0') {
04621 
04622                 cx_strfreev(values);
04623                 giraffe_wlcalibration_config_destroy(config);
04624 
04625                 return NULL;
04626 
04627             }
04628 
04629             config->pxw_yorder = strtol(values[1], &last, 10);
04630 
04631             if (*last != '\0') {
04632 
04633                 cx_strfreev(values);
04634                 giraffe_wlcalibration_config_destroy(config);
04635 
04636                 return NULL;
04637 
04638             }
04639 
04640         }
04641 
04642         cx_strfreev(values);
04643         values = NULL;
04644 
04645     }
04646 
04647 
04648     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.sigma");
04649     config->xws_cliplevel = cpl_parameter_get_double(p);
04650 
04651     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.iterations");
04652     config->xws_clipniter = cpl_parameter_get_int(p);
04653 
04654     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.fraction");
04655     config->xws_clipmfrac = cpl_parameter_get_double(p);
04656 
04657     p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.order");
04658     s = cpl_parameter_get_string(p);
04659 
04660     if (s) {
04661 
04662         cxchar **values = cx_strsplit(s, ",", 3);
04663 
04664         if (values == NULL || values[1] == NULL) {
04665 
04666             giraffe_wlcalibration_config_destroy(config);
04667             return NULL;
04668 
04669         }
04670         else {
04671 
04672             cxchar *last;
04673 
04674 
04675             config->xws_xorder = strtol(values[0], &last, 10);
04676 
04677             if (*last != '\0') {
04678 
04679                 cx_strfreev(values);
04680                 giraffe_wlcalibration_config_destroy(config);
04681 
04682                 return NULL;
04683 
04684             }
04685 
04686             config->xws_yorder = strtol(values[1], &last, 10);
04687 
04688             if (*last != '\0') {
04689 
04690                 cx_strfreev(values);
04691                 giraffe_wlcalibration_config_destroy(config);
04692 
04693                 return NULL;
04694 
04695             }
04696 
04697         }
04698 
04699         cx_strfreev(values);
04700         values = NULL;
04701 
04702     }
04703 
04704     return config;
04705 
04706 }
04707 
04708 
04721 void
04722 giraffe_wlcalibration_config_destroy(GiWCalConfig *config)
04723 {
04724 
04725     if (config) {
04726         if (config->line_widths) {
04727             cx_free(config->line_widths);
04728         }
04729 
04730         if (config->line_wlrange) {
04731             giraffe_range_delete(config->line_wlrange);
04732         }
04733 
04734         if (config->line_model) {
04735             cx_free(config->line_model);
04736         }
04737 
04738         if (config->line_residuals) {
04739             cx_free(config->line_residuals);
04740         }
04741 
04742         if (config->opt_model) {
04743             cx_free(config->opt_model);
04744         }
04745 
04746         cx_free(config);
04747     }
04748 
04749     return;
04750 
04751 }
04752 
04753 
04765 void
04766 giraffe_wlcalibration_config_add(cpl_parameterlist *list)
04767 {
04768 
04769     cpl_parameter *p;
04770 
04771 
04772     if (!list) {
04773         return;
04774     }
04775 
04776 
04777     /*
04778      * Line selection parameters
04779      */
04780 
04781     p = cpl_parameter_new_value("giraffe.wlcalibration.line.widths",
04782                                 CPL_TYPE_STRING,
04783                                 "List of window widths [pxl] used for line "
04784                                 "detection and fit (e.g. '60,40,15').",
04785                                 "giraffe.wlcalibration",
04786                                 "10,10,10,10,10");
04787     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lswidth");
04788     cpl_parameterlist_append(list, p);
04789 
04790 
04791     p = cpl_parameter_new_value("giraffe.wlcalibration.line.separation",
04792                                 CPL_TYPE_DOUBLE,
04793                                 "Factor used to compute the minimum line "
04794                                 "separation from the window width.",
04795                                 "giraffe.wlcalibration",
04796                                 0.9);
04797     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lssep");
04798     cpl_parameterlist_append(list, p);
04799 
04800 
04801     p = cpl_parameter_new_value("giraffe.wlcalibration.line.fluxratio",
04802                                 CPL_TYPE_DOUBLE,
04803                                 "Selects only lines whose neighbours have "
04804                                 "a relative intensity less than "
04805                                 "1. / fluxratio.",
04806                                 "giraffe.wlcalibration",
04807                                 50.);
04808 
04809     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lsfxratio");
04810     cpl_parameterlist_append(list, p);
04811 
04812 
04813     p = cpl_parameter_new_value("giraffe.wlcalibration.line.brightness",
04814                                 CPL_TYPE_DOUBLE,
04815                                 "Selects lines having an intensity greater "
04816                                 "or equal to the given intensity.",
04817                                 "giraffe.wlcalibration",
04818                                 0.);
04819 
04820     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lsbright");
04821     cpl_parameterlist_append(list, p);
04822 
04823 
04824     p = cpl_parameter_new_value("giraffe.wlcalibration.line.count",
04825                                 CPL_TYPE_INT,
04826                                 "Sets the minimum number of lines to select; "
04827                                 "selected are lines with the highest nominal "
04828                                 "intensity. A value of 0 turns this selection "
04829                                 "off. If the value is less than 0 the "
04830                                 "selection is skipped if the line list does "
04831                                 "not contain enough lines.",
04832                                 "giraffe.wlcalibration",
04833                                 -80);
04834 
04835     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lscount");
04836     cpl_parameterlist_append(list, p);
04837 
04838 
04839     p = cpl_parameter_new_value("giraffe.wlcalibration.line.wrange",
04840                                 CPL_TYPE_STRING,
04841                                 "Selects only lines within the given "
04842                                 "wavelength range [nm].",
04843                                 "giraffe.wlcalibration",
04844                                 "0.,0.");
04845 
04846     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lswrange");
04847     cpl_parameterlist_append(list, p);
04848 
04849 
04850     /*
04851      * Line profile fit parameters
04852      */
04853 
04854     p = cpl_parameter_new_enum("giraffe.wlcalibration.line.model",
04855                                CPL_TYPE_STRING,
04856                                "Line profile model.",
04857                                "giraffe.wlcalibration",
04858                                "psfexp", 3, "psfexp", "psfexp2", "gaussian");
04859 
04860     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfmodel");
04861     cpl_parameterlist_append(list, p);
04862 
04863 
04864     p = cpl_parameter_new_enum("giraffe.wlcalibration.line.residuals",
04865                                CPL_TYPE_STRING,
04866                                "Use optical model residuals for line "
04867                                "detection.",
04868                                "giraffe.wlcalibration",
04869                                "auto", 3, "auto", "enable", "disable");
04870 
04871     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfres");
04872     cpl_parameterlist_append(list, p);
04873 
04874 
04875     p = cpl_parameter_new_value("giraffe.wlcalibration.line.threshold",
04876                                 CPL_TYPE_DOUBLE,
04877                                 "Line detection threshold during the "
04878                                 "line fitting (multiple of bias sigma)",
04879                                 "giraffe.wlcalibration",
04880                                 1.);
04881 
04882     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfthreshold");
04883     cpl_parameterlist_append(list, p);
04884 
04885 
04886     p = cpl_parameter_new_value("giraffe.wlcalibration.line.offset",
04887                                 CPL_TYPE_DOUBLE,
04888                                 "Maximum allowed difference between the "
04889                                 "fitted and raw line peak position.",
04890                                 "giraffe.wlcalibration",
04891                                 10.);
04892 
04893     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfoffset");
04894     cpl_parameterlist_append(list, p);
04895 
04896 
04897     p = cpl_parameter_new_value("giraffe.wlcalibration.line.iterations",
04898                                 CPL_TYPE_INT,
04899                                 "Line detection fit maximum number of "
04900                                 "iterations.",
04901                                 "giraffe.wlcalibration",
04902                                 50);
04903 
04904     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfniter");
04905     cpl_parameterlist_append(list, p);
04906 
04907 
04908     p = cpl_parameter_new_value("giraffe.wlcalibration.line.tests",
04909                                 CPL_TYPE_INT,
04910                                 "Line detection fit maximum number of "
04911                                 "tests.",
04912                                 "giraffe.wlcalibration",
04913                                 7);
04914 
04915     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfntest");
04916     cpl_parameterlist_append(list, p);
04917 
04918 
04919     p = cpl_parameter_new_value("giraffe.wlcalibration.line.dchisquare",
04920                                 CPL_TYPE_DOUBLE,
04921                                 "Line detection fit minimum chi-square "
04922                                 "difference.",
04923                                 "giraffe.wlcalibration",
04924                                 0.0001);
04925 
04926     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfdchisq");
04927     cpl_parameterlist_append(list, p);
04928 
04929 
04930     p = cpl_parameter_new_value("giraffe.wlcalibration.line.rwidthratio",
04931                                 CPL_TYPE_DOUBLE,
04932                                 "Line width/resolution width factor.",
04933                                 "giraffe.wlcalibration",
04934                                 0.5);
04935 
04936     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfreswid");
04937     cpl_parameterlist_append(list, p);
04938 
04939 
04940     p = cpl_parameter_new_value("giraffe.wlcalibration.line.exponent",
04941                                 CPL_TYPE_DOUBLE,
04942                                 "Exponential line profile exponent; it will "
04943                                 "not be fitted if it is larger than 0.",
04944                                 "giraffe.wlcalibration",
04945                                 -3.);
04946 
04947     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfexpwid");
04948     cpl_parameterlist_append(list, p);
04949 
04950 
04951     /*
04952      * Slit offsets
04953      */
04954 
04955 
04956     p = cpl_parameter_new_value("giraffe.wlcalibration.slit.offset",
04957                                 CPL_TYPE_STRING,
04958                                 "Initial slit position offsets along the "
04959                                 "x and y direction and rotation angle.",
04960                                 "giraffe.wlcalibration",
04961                                 "setup");
04962 
04963     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-soffset");
04964     cpl_parameterlist_append(list, p);
04965 
04966 
04967     /*
04968      * Optical model parameters
04969      */
04970 
04971     p = cpl_parameter_new_enum("giraffe.wlcalibration.opt.model",
04972                                CPL_TYPE_STRING,
04973                                "Optical model.",
04974                                "giraffe.wlcalibration",
04975                                "xoptmod2", 2, "xoptmod", "xoptmod2");
04976 
04977     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-ommodel");
04978     cpl_parameterlist_append(list, p);
04979 
04980 
04981     p = cpl_parameter_new_value("giraffe.wlcalibration.opt.direction",
04982                                 CPL_TYPE_INT,
04983                                 "Dispersion direction flag.",
04984                                 "giraffe.wlcalibration",
04985                                 1);
04986 
04987     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omdir");
04988     cpl_parameterlist_append(list, p);
04989 
04990 
04991     p = cpl_parameter_new_value("giraffe.wlcalibration.opt.solution",
04992                                 CPL_TYPE_BOOL,
04993                                 "Controls optical model parameter fitting.",
04994                                 "giraffe.wlcalibration",
04995                                 TRUE);
04996 
04997     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omsol");
04998     cpl_parameterlist_append(list, p);
04999 
05000 
05001     p = cpl_parameter_new_value("giraffe.wlcalibration.opt.flags",
05002                                 CPL_TYPE_STRING,
05003                                 "List of flags defining the set of free "
05004                                 "parameters used for fitting the optical "
05005                                 "model. Possible values are: fcoll, gcam, "
05006                                 "theta, sdx, sdy, sphi",
05007                                 "giraffe.wlcalibration",
05008                                 "sdx,sdy,sphi");
05009 
05010     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omflags");
05011     cpl_parameterlist_append(list, p);
05012 
05013 
05014     p = cpl_parameter_new_value("giraffe.wlcalibration.opt.subslits",
05015                                 CPL_TYPE_BOOL,
05016                                 "Controls subslit geometry usage in the "
05017                                 "optical model fit; subslits are used if "
05018                                 "set to `true'.",
05019                                 "giraffe.wlcalibration",
05020                                 FALSE);
05021 
05022     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omsslits");
05023     cpl_parameterlist_append(list, p);
05024 
05025 
05026     p = cpl_parameter_new_value("giraffe.wlcalibration.opt.iterations",
05027                                 CPL_TYPE_INT,
05028                                 "Optical model fit maximum number of "
05029                                 "iterations.",
05030                                 "giraffe.wlcalibration",
05031                                 50);
05032 
05033     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omniter");
05034     cpl_parameterlist_append(list, p);
05035 
05036 
05037     p = cpl_parameter_new_value("giraffe.wlcalibration.opt.tests",
05038                                 CPL_TYPE_INT,
05039                                 "Optical model fit maximum number of "
05040                                 "tests",
05041                                 "giraffe.wlcalibration",
05042                                 7);
05043 
05044     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omntest");
05045     cpl_parameterlist_append(list, p);
05046 
05047 
05048     p = cpl_parameter_new_value("giraffe.wlcalibration.opt.dchisquare",
05049                                 CPL_TYPE_DOUBLE,
05050                                 "Optical model fit minimum chi-square "
05051                                 "difference.",
05052                                 "giraffe.wlcalibration",
05053                                 0.0001);
05054 
05055     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omdchisq");
05056     cpl_parameterlist_append(list, p);
05057 
05058 
05059     /*
05060      * PSF fit parameters
05061      */
05062 
05063     p = cpl_parameter_new_value("giraffe.wlcalibration.psf.sigma",
05064                                 CPL_TYPE_DOUBLE,
05065                                 "PSF width fit sigma clipping factor.",
05066                                 "giraffe.wlcalibration",
05067                                 1.25);
05068 
05069     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwsigma");
05070     cpl_parameterlist_append(list, p);
05071 
05072 
05073     p = cpl_parameter_new_value("giraffe.wlcalibration.psf.iterations",
05074                                 CPL_TYPE_INT,
05075                                 "PSF width fit sigma clipping maximum "
05076                                 "number of iterations.",
05077                                 "giraffe.wlcalibration",
05078                                 10);
05079 
05080     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwniter");
05081     cpl_parameterlist_append(list, p);
05082 
05083 
05084     p = cpl_parameter_new_range("giraffe.wlcalibration.psf.fraction",
05085                                 CPL_TYPE_DOUBLE,
05086                                 "PSF width fit sigma clipping minimum "
05087                                 "fraction of points accepted/total.",
05088                                 "giraffe.wlcalibration",
05089                                 0.9, 0., 1.);
05090 
05091     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwmfrac");
05092     cpl_parameterlist_append(list, p);
05093 
05094 
05095     p = cpl_parameter_new_value("giraffe.wlcalibration.psf.order",
05096                                 CPL_TYPE_STRING,
05097                                 "X and Y polynomial orders for PSF x-width "
05098                                 "Chebyshev fit.",
05099                                 "giraffe.wlcalibration",
05100                                 "2,2");
05101 
05102     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xworder");
05103     cpl_parameterlist_append(list, p);
05104 
05105 
05106     /*
05107      * Parameters of the wavelength solution Chebyshev correction fit
05108      */
05109 
05110     p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.sigma",
05111                                 CPL_TYPE_DOUBLE,
05112                                 "Chebyshev correction sigma clipping factor.",
05113                                 "giraffe.wlcalibration",
05114                                 150.0);
05115 
05116     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wssigma");
05117     cpl_parameterlist_append(list, p);
05118 
05119 
05120     p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.iterations",
05121                                 CPL_TYPE_INT,
05122                                 "Chebyshev correction sigma clipping "
05123                                 "maximum number of iterations",
05124                                 "giraffe.wlcalibration",
05125                                 10);
05126 
05127     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsniter");
05128     cpl_parameterlist_append(list, p);
05129 
05130 
05131     p = cpl_parameter_new_range("giraffe.wlcalibration.wsol.fraction",
05132                                 CPL_TYPE_DOUBLE,
05133                                 "Chebyshev correction sigma clipping "
05134                                 "minimum fraction of points accepted/total.",
05135                                 "giraffe.wlcalibration",
05136                                 0.9, 0., 1.);
05137 
05138     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsmfrac");
05139     cpl_parameterlist_append(list, p);
05140 
05141 
05142     p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.order",
05143                                 CPL_TYPE_STRING,
05144                                 "X and Y polynomial orders for the wavelength "
05145                                 "solution Chebyshev correction.",
05146                                 "giraffe.wlcalibration",
05147                                 "6,4");
05148 
05149     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsorder");
05150     cpl_parameterlist_append(list, p);
05151 
05152     return;
05153 
05154 }

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