GIRAFFE Pipeline Reference Manual

giwlcalibration.c
1 /* $Id$
2  *
3  * This file is part of the GIRAFFE Pipeline
4  * Copyright (C) 2002-2006 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * $Author$
23  * $Date$
24  * $Revision$
25  * $Name$
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 #include <stdlib.h>
33 #include <math.h>
34 
35 #include <cxmemory.h>
36 #include <cxstring.h>
37 #include <cxstrutils.h>
38 
39 #include <cpl_error.h>
40 #include <cpl_msg.h>
41 
42 #include "gimacros.h"
43 #include "gialias.h"
44 #include "gimatrix.h"
45 #include "gigrating.h"
46 #include "gimodel.h"
47 #include "gilinedata.h"
48 #include "giwlsolution.h"
49 #include "gimath.h"
50 #include "gimessages.h"
51 #include "gifiberutils.h"
52 #include "giclip.h"
53 #include "giwlcalibration.h"
54 
55 
64 /*
65  * The line type is currently not used. Only ThArNe catalogs will be
66  * used. This is present as a placeholder, and because it was foreseen in
67  * the original OGL version.
68  */
69 
70 enum GiLineType {
71  GI_LINETYPE_UNDEFINED, /* Undefined */
72  GI_LINETYPE_THARNE, /* Use ThArNe lines */
73  GI_LINETYPE_TELLURIC /* Use telluric lines */
74 };
75 
76 typedef enum GiLineType GiLineType;
77 
78 
79 /*
80  * Line status flags (rejection codes) used by the line fit.
81  */
82 
83 enum {
84  LF_R_NONE = 0x0000, /* line is ok */
85  LF_R_AMPLI = 0x0001, /* too small or saturated */
86  LF_R_NITER = 0x0002, /* max number of iteration reached */
87  LF_R_CENTR = 0x0004, /* line center out of window */
88  LF_R_WIDTH = 0x0008, /* line width larger than window */
89  LF_R_LEFT = 0x0010, /* line out of window (leftside) */
90  LF_R_RIGHT = 0x0020, /* line out of window (rightside) */
91  LF_R_OFFST = 0x0040, /* maximum too far from original guess */
92  LF_R_BADLN = 0x0080, /* maximum too far from window center */
93  LF_R_ERROR = 0x0100, /* error in fit */
94  LF_R_PSFIT = 0x0200, /* rejected by fit of PSF width */
95  LF_R_XRFIT = 0x0400, /* rejected by fit of X residuals */
96  LF_R_RESOL = 0x0800, /* width too small for resolution */
97  LF_R_XCCD = 0x1000 /* out of CCD */
98 };
99 
100 
101 /*
102  * Slit offset configuration masks
103  */
104 
105 enum {
106  SLIT_DX = 0x0001, /* Slit offset along x was set */
107  SLIT_DY = 0x0002, /* Slit offset along y was set */
108  SLIT_PHI = 0x0004, /* Slit rotation offset was set */
109 };
110 
111 
112 /*
113  * Optical model parameter flags
114  */
115 
116 enum {
117  OPTM_FLENGTH = 1 << 0,
118  OPTM_GCAMERA = 1 << 1,
119  OPTM_THETA = 1 << 2,
120  OPTM_SX = 1 << 3,
121  OPTM_SY = 1 << 4,
122  OPTM_SPHI = 1 << 5
123 };
124 
125 
126 /*
127  * Optical model info flags
128  */
129 
130 enum GiOpticalModelInfo {
131  GI_OPTM_PARAMETER_VALUES,
132  GI_OPTM_PARAMETER_ERRORS,
133  GI_OPTM_PARAMETER_STATUS
134 };
135 
136 typedef enum GiOpticalModelInfo GiOpticalModelInfo;
137 
138 
139 /*
140  * The line fit setup parameters definition.
141  */
142 
143 struct GiLineParams {
144 
145  const cxchar *model;
146 
147  GiLineType type;
148 
149  cxdouble grwid;
150  cxdouble satlv;
151  cxdouble thres;
152  cxdouble offst;
153  cxdouble wfact;
154  cxdouble psfexp;
155 
156  GiFitSetup fit;
157 
158 };
159 
160 typedef struct GiLineParams GiLineParams;
161 
162 
163 /*
164  * Optical model fit setup parameter definition.
165  */
166 
167 struct GiOpticalModelParams {
168 
169  GiFitSetup fit;
170  cxint16 flags;
171 
172 };
173 
174 typedef struct GiOpticalModelParams GiOpticalModelParams;
175 
176 
177 /*
178  * PSF fit setup information
179  */
180 
181 struct GiSCFitParams {
182 
183  cxbool subslits;
184 
185  struct {
186  cxint xorder;
187  cxint yorder;
188  } fit;
189 
190  GiClipParams clip;
191 
192 };
193 
194 typedef struct GiSCFitParams GiSCFitParams;
195 
196 
197 struct GiWCalInfo {
198 
199  cxint width;
200 
201  cxbool residuals;
202 
203  cxint nlines;
204  cxint nfibers;
205 
206  cxint ngood;
207  cxint nreject;
208 
209  cxdouble rms;
210 
211 };
212 
213 typedef struct GiWCalInfo GiWCalInfo;
214 
215 
216 inline static cxint
217 _giraffe_window_compare(cxcptr first, cxcptr second)
218 {
219 
220  cxint *_first = (cxint *)first;
221  cxint *_second = (cxint *)second;
222 
223  return *_second - *_first;
224 
225 }
226 
227 
228 inline static GiLineParams *
229 _giraffe_lineparams_create(GiLineType type, const GiGrating *grating,
230  const GiWCalConfig *config)
231 {
232 
233  GiLineParams *self = NULL;
234 
235 
236  cx_assert(grating != NULL);
237  cx_assert(config != NULL);
238 
239  self = cx_calloc(1, sizeof(GiLineParams));
240 
241  self->model = cx_strdup(config->line_model);
242  self->type = type;
243 
244  /*
245  * Estimated line FWHM per pixel at the central wavelength computed
246  * from basic grating data:
247  * FWHM = npixel / bandwidth * lambda0 / resolution
248  */
249 
250  self->grwid = 1. / grating->band * (grating->wlen0 / grating->resol);
251  self->satlv = config->line_saturation;
252  self->thres = config->line_threshold;
253  self->offst = config->line_offset;
254  self->wfact = config->line_rwidthratio;
255 
256  self->psfexp = config->line_widthexponent;
257 
258  self->fit.iterations = config->line_niter;
259  self->fit.tests = config->line_ntest;
260  self->fit.delta = config->line_dchisq;
261 
262  return self;
263 
264 }
265 
266 
267 inline static void
268 _giraffe_lineparams_delete(GiLineParams *self)
269 {
270 
271  if (self) {
272 
273  if (self->model) {
274  cx_free((cxptr)self->model);
275  }
276 
277  cx_free(self);
278 
279  }
280 
281  return;
282 
283 }
284 
285 
286 inline static cxdouble
287 _giraffe_get_fiber_position(const cpl_image *locy, cxint cs, cxdouble xccd)
288 {
289 
290  cxint xlower = (cxint)floor(xccd);
291  cxint xupper = (cxint)ceil(xccd);
292 
293  const cxdouble *ldata = cpl_image_get_data_const(locy);
294 
295  cxdouble ylower = 0.;
296  cxdouble yupper = 0.;
297 
298 
299  cx_assert(ldata != NULL);
300 
301  ylower = ldata[xlower * cpl_image_get_size_x(locy) + cs];
302  yupper = ldata[xupper * cpl_image_get_size_x(locy) + cs];
303 
304  return giraffe_interpolate_linear(xccd, xlower, ylower, xupper, yupper);
305 
306 }
307 
308 
309 inline static cxint
310 _giraffe_subslit_get_max(const cpl_table *fibers)
311 {
312 
313  return cpl_table_get_column_max((cpl_table *)fibers, "SSN");
314 
315 }
316 
317 
318 inline static cpl_table *
319 _giraffe_subslit_get(const cpl_table *fibers, cxint ssn)
320 {
321 
322  cxint ssn_max = 0;
323 
324  cpl_table *_fibers;
325 
326 
327  cx_assert(fibers != NULL);
328  cx_assert(cpl_table_has_column((cpl_table *)fibers, "SSN"));
329 
330  ssn_max = _giraffe_subslit_get_max(fibers);
331 
332  if (ssn < 0 || ssn > ssn_max) {
333  return NULL;
334  }
335 
336  cpl_table_unselect_all((cpl_table *)fibers);
337  cpl_table_or_selected_int((cpl_table *)fibers, "SSN", CPL_EQUAL_TO, ssn);
338 
339  _fibers = cpl_table_extract_selected((cpl_table *)fibers);
340 
341  return _fibers;
342 
343 }
344 
345 
346 inline static cxint
347 _giraffe_subslit_range(const cpl_table *subslit, const cpl_image *locy,
348  const cpl_image *locw, cxdouble *ymin, cxdouble *ymax)
349 {
350 
351  const cxchar *idx = NULL;
352 
353  cxint i;
354  cxint ns = 0;
355  cxint nx = 0;
356 
357  const cxdouble *_locy = NULL;
358  const cxdouble *_locw = NULL;
359 
360  cxdouble _ymin = CX_MAXDOUBLE;
361  cxdouble _ymax = 0.;
362 
363  cx_assert(subslit != NULL);
364  cx_assert(locy != NULL);
365  cx_assert(locw != NULL);
366 
367  idx = giraffe_fiberlist_query_index(subslit);
368 
369  ns = cpl_image_get_size_x(locy);
370  nx = cpl_image_get_size_y(locy);
371 
372  _locy = cpl_image_get_data_const(locy);
373  _locw = cpl_image_get_data_const(locw);
374 
375  for (i = 0; i < cpl_table_get_nrow((cpl_table *)subslit); i++) {
376 
377  cxint j;
378  cxint cs = cpl_table_get_int((cpl_table *)subslit, idx, i, NULL) - 1;
379 
380  for (j = 0; j < nx; j++) {
381 
382  register cxint k = j * ns + cs;
383 
384  cxdouble ylower = _locy[k] - _locw[k];
385  cxdouble yupper = _locy[k] + _locw[k];
386 
387  _ymin = CX_MIN(_ymin, ylower);
388  _ymax = CX_MAX(_ymax, yupper);
389 
390  }
391 
392  }
393 
394  if (_ymin > _ymax) {
395  return 1;
396  }
397 
398  if (ymin != NULL) {
399  *ymin = _ymin;
400  }
401 
402  if (ymax != NULL) {
403  *ymax = _ymax;
404  }
405 
406  return 0;
407 
408 }
409 
410 
411 inline static cxint
412 _giraffe_get_residuals(cpl_image *residuals, const cpl_image *positions,
413  const cpl_image *fit)
414 {
415 
416  cxint i;
417  cxint nfibers = 0;
418  cxint nlines = 0;
419  cxint nx = 0;
420 
421  const cxdouble *_positions = NULL;
422  const cxdouble *_fit = NULL;
423 
424  cxdouble *_residuals = NULL;
425 
426 
427  cx_assert(residuals != NULL);
428  cx_assert(positions != NULL);
429  cx_assert(fit != NULL);
430 
431  nfibers = cpl_image_get_size_x(positions);
432  nlines = cpl_image_get_size_y(positions);
433  nx = cpl_image_get_size_y(fit);
434 
435  cx_assert(nfibers == cpl_image_get_size_x(residuals));
436  cx_assert(nlines == cpl_image_get_size_y(residuals));
437 
438  _residuals = cpl_image_get_data(residuals);
439  _positions = cpl_image_get_data_const(positions);
440  _fit = cpl_image_get_data_const(fit);
441 
442  for (i = 0; i < nlines; i++) {
443 
444  register cxint j;
445 
446  for (j = 0; j < nfibers; j++) {
447 
448  register cxdouble line_pos = _positions[i * nfibers + j];
449 
450  line_pos = CX_MIN(CX_MAX(line_pos, 0.), nx - 1);
451  _residuals[i * nfibers + j] = _fit[(cxint)line_pos * nfibers + j];
452 
453  }
454 
455  }
456 
457  return 0;
458 
459 }
460 
461 
462 inline static cxint
463 _giraffe_apply_residuals(cpl_image *xccd, const cpl_image *residuals,
464  const cpl_image *lflags, cxdouble value)
465 {
466 
467  cx_assert(xccd != NULL);
468  cx_assert(residuals != NULL);
469 
470  cpl_image_subtract(xccd, residuals);
471 
472  if (lflags != NULL) {
473 
474  const cxint *_lflags = cpl_image_get_data_const(lflags);
475 
476  cxint i;
477  cxint nfibers = cpl_image_get_size_x(xccd);
478  cxint nlines = cpl_image_get_size_y(xccd);
479 
480  cxdouble *_xccd = cpl_image_get_data(xccd);
481 
482 
483  cx_assert(nfibers == cpl_image_get_size_x(lflags));
484  cx_assert(nlines == cpl_image_get_size_y(lflags));
485 
486  for (i = 0; i < nlines; i++) {
487 
488  cxint j;
489 
490  for (j = 0; j < nfibers; j++) {
491 
492  if (_lflags[i * nfibers + j] > 0) {
493  _xccd[i * nfibers + j] = value;
494  }
495 
496  }
497 
498  }
499 
500  }
501 
502  return 0;
503 
504 }
505 
506 
507 inline static cxint
508 _giraffe_linelist_setup(GiTable *lines, GiGrating *grating,
509  const GiWCalConfig *config)
510 {
511 
512  const cxchar *const fctid = "_giraffe_linelist_setup";
513 
514 
515  const cxdouble fraction = 500.;
516 
517  cxint nlines = 0;
518  cxint nreject = 0;
519  cxint status = 0;
520 
521  cxdouble wlmin = 0.;
522  cxdouble wlmax = 0.;
523  cxdouble margin = 0.;
524 
525  cpl_table *_lines = NULL;
526 
527 
528 
529  cx_assert(lines != NULL);
530  cx_assert(grating != NULL);
531  cx_assert(config != NULL);
532 
533 
534  _lines = giraffe_table_get(lines);
535 
536  if (_lines == NULL) {
537  return 1;
538  }
539 
540  if (!cpl_table_has_column(_lines, "WLEN") ||
541  !cpl_table_has_column(_lines, "FLUX")) {
542  return 2;
543  }
544 
545 
546  /*
547  * Remove lines outside of the given wavelength range taking a safety
548  * margin into account.
549  */
550 
551  nlines = cpl_table_get_nrow(_lines);
552  cpl_table_unselect_all(_lines);
553 
554  wlmin = grating->wlenmin;
555  wlmax = grating->wlenmax;
556 
557  if (giraffe_range_get_min(config->line_wlrange) > 0.) {
558  wlmin = giraffe_range_get_min(config->line_wlrange);
559  grating->wlenmin = wlmin;
560  }
561 
562  if (giraffe_range_get_max(config->line_wlrange) > 0.) {
563  wlmax = giraffe_range_get_max(config->line_wlrange);
564  grating->wlenmax = wlmax;
565  }
566 
567  margin = (wlmax - wlmin) / fraction;
568 
569  cpl_msg_debug(fctid, "Selecting wavelength range [%.4f, %.4f[ [nm] with "
570  "margin %.4f nm.", wlmin, wlmax, margin);
571 
572  cpl_table_or_selected_double(_lines, "WLEN", CPL_LESS_THAN,
573  wlmin + margin);
574  cpl_table_or_selected_double(_lines, "WLEN", CPL_NOT_LESS_THAN,
575  wlmax - margin);
576 
577  cpl_table_erase_selected(_lines);
578 
579  if (cpl_table_get_nrow(_lines) <= 0) {
580  cpl_msg_debug(fctid, "Invalid line list! All lines have been "
581  "rejected!");
582  return -1;
583  }
584 
585  nreject = nlines - cpl_table_get_nrow(_lines);
586  cpl_msg_debug(fctid, "%d of %d lines rejected because of wavelength "
587  "range.", nreject, nlines);
588 
589 
590  /*
591  * Apply brightness criteria
592  */
593 
594  nlines = cpl_table_get_nrow(_lines);
595 
596  if (config->line_count != 0) {
597 
598  cxint i;
599  cxint line_count = abs(config->line_count);
600 
601  cpl_propertylist *sorting_order = NULL;
602 
603 
604  if (line_count > nlines) {
605  cpl_msg_debug(fctid, "Too few lines in line list for brightness "
606  "selection!");
607 
608  if (config->line_count > 0) {
609  return 3;
610  }
611  else {
612  cpl_msg_debug(fctid, "Skipping brightness selection!");
613  line_count = nlines;
614  }
615  }
616 
617  sorting_order = cpl_propertylist_new();
618  cpl_propertylist_append_bool(sorting_order, "FLUX", 1);
619 
620  cpl_table_sort(_lines, sorting_order);
621 
622  cpl_propertylist_delete(sorting_order);
623  sorting_order = NULL;
624 
625  cpl_table_select_all(_lines);
626 
627  for (i = 0; i < line_count; i++) {
628  cpl_table_unselect_row(_lines, i);
629  }
630 
631  status = cpl_table_erase_selected(_lines);
632 
633  if (cpl_table_get_nrow(_lines) <= 0) {
634  return -1;
635  }
636 
637  sorting_order = cpl_propertylist_new();
638  cpl_propertylist_append_bool(sorting_order, "WLEN", 0);
639 
640  cpl_table_sort(_lines, sorting_order);
641 
642  cpl_propertylist_delete(sorting_order);
643  sorting_order = NULL;
644 
645  }
646 
647  if (config->line_brightness > 0.) {
648 
649  cpl_table_select_all(_lines);
650  cpl_table_and_selected_double(_lines, "FLUX", CPL_NOT_GREATER_THAN,
651  config->line_brightness);
652 
653  cpl_table_erase_selected(_lines);
654 
655  if (cpl_table_get_nrow(_lines) <= 0) {
656  cpl_msg_debug(fctid, "Invalid line brightness! All lines have "
657  "been rejected!");
658  return -2;
659  }
660 
661  }
662 
663  nreject = nlines - cpl_table_get_nrow(_lines);
664  cpl_msg_debug(fctid, "%d of %d lines rejected because brightness "
665  "criteria.", nreject, nlines);
666 
667 
668  return 0;
669 
670 }
671 
672 
673 inline static cpl_table *
674 _giraffe_linelist_select(const GiTable *lines, const GiImage *spectra,
675  const GiGrating *grating, cxdouble width,
676  const GiWCalConfig *config)
677 {
678 
679  const cxchar *const fctid = "_giraffe_linelist_select";
680 
681 
682  cxint i;
683  cxint nlines = 0;
684  cxint nreject = 0;
685 
686  cxdouble scale = 0.;
687  cxdouble separation = 0.;
688 
689  cpl_image *_spectra = NULL;
690 
691  cpl_table *_lines = NULL;
692 
693 
694  cx_assert(lines != NULL);
695  cx_assert(spectra != NULL);
696  cx_assert(grating != NULL);
697  cx_assert(config != NULL);
698 
699  _spectra = giraffe_image_get(spectra);
700  cx_assert(_spectra != NULL);
701 
702  _lines = cpl_table_duplicate(giraffe_table_get(lines));
703 
704  if (_lines == NULL) {
705  return NULL;
706  }
707 
708  nlines = cpl_table_get_nrow(_lines);
709 
710 
711  /*
712  * Estimate wavelength scale (just a rough guess) and the minimum
713  * line separation in nm.
714  */
715 
716  scale = fabs(cpl_image_get_size_y(_spectra)) / grating->band;
717  separation = width / scale * config->line_separation;
718 
719  cpl_msg_debug(fctid, "Estimated wavelength scale: %.4e nm/pxl",
720  1. / scale);
721  cpl_msg_debug(fctid, "Minimum required line separation: %.4f nm (%.4f "
722  "pxl)", separation, separation * scale);
723 
724 
725  /*
726  * Reject all lines which violate the `crowding criterium', i.e.
727  * all lines which could be misidentified because of a distance less
728  * than the line width times a line separation factor, and a comparable
729  * line intensity.
730  *
731  * Therefore, each line i at the position p with an intensity f is
732  * eliminated from the line list if any other line j with (_p, _f)
733  * satisfies the condition:
734  *
735  * abs(p - _p) < line_separation && f < _f * flux_ratio
736  */
737 
738  cpl_table_unselect_all(_lines);
739 
740  for (i = 0; i < cpl_table_get_nrow(_lines); i++) {
741 
742  register cxint j;
743 
744  register cxdouble w = cpl_table_get(_lines, "WLEN", i, NULL);
745  register cxdouble f = cpl_table_get(_lines, "FLUX", i, NULL);
746 
747 
748  for (j = 0; j < cpl_table_get_nrow(_lines); j++) {
749 
750  if (i != j) {
751 
752  register cxdouble _w = cpl_table_get(_lines, "WLEN", j, NULL);
753  register cxdouble _f = cpl_table_get(_lines, "FLUX", j, NULL);
754 
755 
756  if (fabs(w - _w) < separation &&
757  f / _f < config->line_fluxratio) {
758 
759  cpl_table_select_row(_lines, i);
760  break;
761 
762  }
763 
764  }
765 
766  }
767 
768  }
769 
770  cpl_table_erase_selected(_lines);
771 
772  if (cpl_table_get_nrow(_lines) <= 0) {
773  cpl_table_delete(_lines);
774  return NULL;
775  }
776 
777  nreject = nlines - cpl_table_get_nrow(_lines);
778  cpl_msg_debug(fctid, "%d of %d lines rejected due to crowding.",
779  nreject, nlines);
780 
781 
782  /*
783  * Remove all lines with bad quality. This is indicated by a non-zero
784  * value in the line list's column FLAGS for the standard catalogs, or
785  * in the column COMMENT for the original OGL version.
786  *
787  * Note: This must be done last, since line with bad quality must be
788  * considered in the crowding check!
789  */
790 
791  cpl_msg_debug(fctid, "Removing lines with non-zero line quality.");
792 
793  nlines = cpl_table_get_nrow(_lines);
794  cpl_table_unselect_all(_lines);
795 
796  if (cpl_table_has_column(_lines, "FLAGS")) {
797 
798  cpl_table_or_selected_int(_lines, "FLAGS", CPL_NOT_EQUAL_TO, 0);
799 
800  }
801  else {
802 
803  if (cpl_table_has_column(_lines, "COMMENT")) {
804 
805  for (i = 0; i < nlines; i++) {
806 
807  cxchar *s = cx_strdup(cpl_table_get_string(_lines,
808  "COMMENT", i));
809 
810  if (strlen(cx_strstrip(s)) > 3) {
811  cpl_table_select_row(_lines, i);
812  }
813 
814  cx_free(s);
815 
816  }
817 
818  }
819  else {
820 
821  cpl_msg_debug(fctid, "No comments found in line list! No line "
822  "quality checks will be done!");
823 
824  }
825 
826  }
827 
828  cpl_table_erase_selected(_lines);
829 
830  if (cpl_table_get_nrow(_lines) <= 0) {
831  cpl_msg_debug(fctid, "Invalid line list! All lines have been "
832  "rejected!");
833  cpl_table_delete(_lines);
834  return NULL;
835  }
836 
837  nreject = nlines - cpl_table_get_nrow(_lines);
838  cpl_msg_debug(fctid, "%d of %d lines rejected because of line quality.",
839  nreject, nlines);
840 
841 
842  return _lines;
843 
844 }
845 
846 
847 inline static cpl_image *
848 _giraffe_line_abscissa(const cpl_table *lines, const GiTable *slitgeometry,
849  const GiTable *fibers, const GiWlSolution *solution,
850  const GiLocalization *localization, cxbool residuals)
851 {
852 
853  const cxchar *const fctid = "_giraffe_line_abscissa";
854 
855 
856  const cxchar *idx = NULL;
857 
858  cxint i;
859  cxint nlines = 0;
860  cxint nfibers = 0;
861 
862  cpl_table *_lines = NULL;
863  cpl_table *_fibers = NULL;
864  cpl_table *_slitgeometry = NULL;
865 
866  cpl_image *abscissa = NULL;
867 
868 
869  cx_assert(lines != NULL);
870  cx_assert(slitgeometry != NULL);
871  cx_assert(fibers != NULL);
872  cx_assert(solution != NULL);
873 
874  _lines = (cpl_table *)lines;
875 
876  _fibers = giraffe_table_get(fibers);
877  cx_assert(_fibers != NULL);
878 
879  _slitgeometry = giraffe_table_get(slitgeometry);
880  cx_assert(_slitgeometry != NULL);
881 
882 
883  nlines = cpl_table_get_nrow(_lines);
884  nfibers = cpl_table_get_nrow(_fibers);
885 
886  if (nfibers != cpl_table_get_nrow(_slitgeometry)) {
887  cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
888  return NULL;
889  }
890 
891 
892  idx = giraffe_fiberlist_query_index(_fibers);
893 
894  if (idx == NULL) {
895  cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
896  return NULL;
897  }
898 
899 
900  if (residuals == TRUE) {
901 
902  if (localization == NULL) {
903  cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
904  return NULL;
905  }
906  else {
907  if (localization->locy == NULL || localization->locw == NULL) {
908  cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
909  return NULL;
910  }
911  }
912 
913  if (giraffe_wlsolution_get_residuals(solution) == NULL) {
914  cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
915  return NULL;
916  }
917 
918  }
919 
920 
921  abscissa = cpl_image_new(nfibers, nlines, CPL_TYPE_DOUBLE);
922 
923  for (i = 0; i < nfibers; i++) {
924 
925  cxint j;
926 
927  cxdouble xf = cpl_table_get(_slitgeometry, "XF", i, NULL);
928  cxdouble yf = cpl_table_get(_slitgeometry, "YF", i, NULL);
929  cxdouble *data = cpl_image_get_data(abscissa);
930 
931 
932  for (j = 0; j < nlines; j++) {
933 
934  cxint status = 0;
935 
936  cxdouble lambda = cpl_table_get(_lines, "WLEN", j, NULL);
937 
938  cxdouble xccd = 0.;
939 
940 
941  xccd = giraffe_wlsolution_compute_pixel(solution, lambda, xf, yf,
942  &status);
943 
944  if (status != 0) {
945  cpl_image_delete(abscissa);
946  return NULL;
947  }
948 
949  if (residuals == TRUE) {
950 
951  cxint cs = cpl_table_get_int(_fibers, idx, i, NULL) - 1;
952 
953  cxdouble yccd = 0.;
954 
955  cpl_image *_locy = giraffe_image_get(localization->locy);
956 
957 
958  cx_assert(_locy != NULL);
959 
960  if (xccd > 0. && xccd < cpl_image_get_size_y(_locy)) {
961 
962  cxdouble xres = 0.;
963 
964  yccd = _giraffe_get_fiber_position(_locy, cs, xccd);
965  xres = giraffe_wlsolution_compute_residual(solution,
966  xccd, yccd);
967 
968  xccd -= xres;
969  }
970 
971  }
972 
973  data[j * nfibers + i] = xccd;
974 
975  }
976 
977  }
978 
979  return abscissa;
980 
981 }
982 
983 
984 inline static cpl_image *
985 _giraffe_line_ordinate(GiTable *lines, const GiTable *slitgeometry,
986  const GiWlSolution *solution)
987 {
988 
989  return NULL;
990 
991 }
992 
993 
994 /*
995  * Sets the fit parameters to their initial values. The function does
996  * nothing if the line status flags are not zero.
997  */
998 
999 inline static void
1000 _giraffe_line_fit_setup(GiModel *model, cxdouble width, cxint xmin,
1001  const cpl_matrix *y, const cpl_matrix *sigma,
1002  const GiLineParams *setup, cxint *lflags)
1003 {
1004 
1005  cxint k;
1006  cxint center = 0;
1007  cxint xline = 0;
1008 
1009  cxdouble amplitude = 0.;
1010  cxdouble background = 0.;
1011  cxdouble _sigma = 0.;
1012 
1013  cpl_matrix *_y = NULL;
1014 
1015 
1016  cx_assert(model != NULL);
1017  cx_assert(y != NULL);
1018  cx_assert(setup != NULL);
1019  cx_assert(lflags != NULL);
1020 
1021 
1022  if (*lflags != LF_R_NONE) {
1023  return;
1024  }
1025 
1026 
1027  /*
1028  * Model parameters: the line amplitude, center, width(s)
1029  * and background. The line background is estimated as the
1030  * mean of the two lowest intensities found in the fit
1031  * interval.
1032  */
1033 
1034  /* Amplitude and center */
1035 
1036  center = xmin;
1037  for (k = 0; k < cpl_matrix_get_nrow((cpl_matrix*)y); k++) {
1038  if (cpl_matrix_get((cpl_matrix *)y, k, 0) >= amplitude) {
1039  center = xmin + k;
1040  xline = k;
1041  amplitude = cpl_matrix_get((cpl_matrix *)y, k, 0);
1042  }
1043  }
1044 
1045 
1046  /* Background */
1047 
1048  _y = cpl_matrix_duplicate((cpl_matrix *)y);
1049 
1050  giraffe_matrix_sort(_y);
1051 
1052  background = 0.5 * (cpl_matrix_get(_y, 0, 0) + cpl_matrix_get(_y, 1, 0));
1053  cpl_matrix_delete(_y);
1054 
1055 
1056  /*
1057  * Line rejection: Discard lines whose flux errors at the
1058  * line center times a threshold is larger than the peak flux
1059  * in the current fit interval, or if maximum flux value
1060  * exceeds the saturation level.
1061  */
1062 
1063  _sigma = cpl_matrix_get((cpl_matrix *)sigma, xline, 0) * setup->thres;
1064 
1065  if (amplitude <= _sigma || amplitude > setup->satlv) {
1066  *lflags |= LF_R_AMPLI;
1067  return;
1068  }
1069 
1070  giraffe_model_set_parameter(model, "Amplitude", amplitude - background);
1071  giraffe_model_set_parameter(model, "Center", center);
1072  giraffe_model_set_parameter(model, "Background", background);
1073  giraffe_model_set_parameter(model, "Width1", width);
1074 
1075  if (strncmp(giraffe_model_get_name(model), "psfexp", 6) == 0) {
1076 
1077  cxdouble width2 = setup->psfexp < 0. ? -setup->psfexp : setup->psfexp;
1078 
1079  giraffe_model_set_parameter(model, "Width2", width2);
1080 
1081 
1082  /*
1083  * If the psf-width exponent is positiv the parameter
1084  * value is kept, otherwise it is fitted.
1085  */
1086 
1087  if (setup->psfexp >= 0.) {
1088  giraffe_model_freeze_parameter(model, "Width2");
1089  }
1090  else {
1091  giraffe_model_thaw_parameter(model, "Width2");
1092  }
1093 
1094  }
1095 
1096  return;
1097 
1098 }
1099 
1100 
1101 inline static cxint
1102 _giraffe_line_fit(GiLineData *lines, const cpl_image *positions, cxint width,
1103  const GiExtraction *extraction, const GiTable *fibers,
1104  const GiImage *locy, const GiLineParams *setup)
1105 {
1106 
1107  const cxchar *const fctid = "_giraffe_line_fit";
1108 
1109 
1110  const cxchar *idx = NULL;
1111 
1112  cxint i;
1113  cxint nfibers = 0;
1114  cxint nlines = 0;
1115 
1116  const cxdouble LOG2 = log(2.);
1117  const cxdouble fwhm_ratio = 2. * sqrt(2. * LOG2);
1118 
1119  cpl_image *_spectra = NULL;
1120  cpl_image *_errors = NULL;
1121  cpl_image *_locy = NULL;
1122 
1123  cpl_matrix *x = NULL;
1124  cpl_matrix *y = NULL;
1125  cpl_matrix *sigma = NULL;
1126 
1127  cpl_table *_fibers = NULL;
1128 
1129  GiModel *model = NULL;
1130 
1131 
1132  cx_assert(positions != NULL);
1133  cx_assert(width > 0);
1134 
1135  cx_assert(extraction != NULL);
1136  cx_assert(extraction->spectra != NULL && extraction->error != NULL);
1137 
1138  cx_assert(fibers != NULL);
1139  cx_assert(locy != NULL);
1140  cx_assert(setup != NULL);
1141 
1142 
1143  _fibers = giraffe_table_get(fibers);
1144  cx_assert(_fibers != NULL);
1145 
1146  _spectra = giraffe_image_get(extraction->spectra);
1147  cx_assert(_spectra != NULL);
1148 
1149  _errors = giraffe_image_get(extraction->error);
1150  cx_assert(_errors != NULL);
1151 
1152  _locy = giraffe_image_get(locy);
1153  cx_assert(_locy != NULL);
1154 
1155  nfibers = cpl_table_get_nrow(_fibers);
1156 
1157  cx_assert(nfibers == cpl_image_get_size_x(_spectra));
1158  cx_assert(nfibers == cpl_image_get_size_x(_errors));
1159 
1160  // FIXME: The assertion should not be necessary any more, but better
1161  // check this
1162  //cx_assert(nfibers == cpl_image_get_size_x(_locy));
1163 
1164  idx = giraffe_fiberlist_query_index(_fibers);
1165  cx_assert(idx != NULL);
1166 
1167  nlines = cpl_image_get_size_y(positions);
1168 
1169 
1170  /*
1171  * Create the selected line fit model. All parameters will be fitted.
1172  */
1173 
1174  if (strcmp(setup->model, "gaussian") != 0 &&
1175  strcmp(setup->model, "psfexp") != 0 &&
1176  strcmp(setup->model, "psfexp2") != 0) {
1177  cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
1178  return 1;
1179  }
1180 
1181  model = giraffe_model_new(setup->model);
1182 
1183  if (giraffe_model_get_type(model) != GI_MODEL_LINE) {
1184  giraffe_model_delete(model);
1185  return 2;
1186  }
1187 
1188  giraffe_model_thaw(model);
1189 
1190  giraffe_model_set_iterations(model, setup->fit.iterations);
1191  giraffe_model_set_tests(model, setup->fit.tests);
1192  giraffe_model_set_delta(model, setup->fit.delta);
1193 
1194 
1195  /*
1196  * For each spectrum in the input image `positions' fit each line.
1197  */
1198 
1199  x = cpl_matrix_new(width, 1);
1200  y = cpl_matrix_new(width, 1);
1201  sigma = cpl_matrix_new(width, 1);
1202 
1203  for (i = 0; i < nfibers; i++) {
1204 
1205  cxint j;
1206 
1207  for (j = 0; j < nlines; j++) {
1208 
1209  cxint k;
1210  cxint lflags = LF_R_NONE;
1211  cxint iterations = 0;
1212  cxint ndata = 0;
1213  cxint xmin = 0;
1214  cxint xmax = 0;
1215  cxint nx = cpl_image_get_size_y(_spectra);
1216 
1217  cxdouble xccd = 0.;
1218  cxdouble yccd = 0.;
1219  cxdouble lwidth = 0.;
1220  cxdouble amplitude = 0.;
1221  cxdouble background = 0.;
1222  cxdouble center = 0.;
1223  cxdouble width1 = 0.;
1224  cxdouble exponent = 0.;
1225  cxdouble error = 0.;
1226  cxdouble gwidth = 0.;
1227 
1228  const cxdouble *_positions = cpl_image_get_data_const(positions);
1229 
1230 
1231  /*
1232  * Compute line position in pixels using the fiber localization
1233  */
1234 
1235  xccd = _positions[j * nfibers + i];
1236 
1237  xmin = xccd;
1238  xmax = xccd;
1239 
1240  if (0 < xccd && xccd < nx) {
1241 
1242  cxint cs = cpl_table_get_int(_fibers, idx, i, NULL) - 1;
1243 
1244 
1245  /*
1246  * Compute fit interval taking the CCD size into account.
1247  * The entire interval must be within the CCD boundaries.
1248  */
1249 
1250  xmin = (cxint)(xccd - 0.5 * width + 0.5);
1251  xmax = (cxint)(xccd + 0.5 * width + 0.5);
1252 
1253  xmin = CX_MAX(CX_MIN(xmin, nx - 1), 0);
1254  xmax = CX_MAX(CX_MIN(xmax, nx - 1), 0);
1255 
1256  ndata = xmax - xmin;
1257 
1258  /*
1259  * Compute fiber centroid position using linear
1260  * interpolation.
1261  */
1262 
1263  yccd = _giraffe_get_fiber_position(_locy, cs, xccd);
1264 
1265  }
1266 
1267 
1268  /*
1269  * At least 3 data points are required for the fit. This covers
1270  * also the situation xmin == xmax
1271  */
1272 
1273  if (ndata < 3) {
1274  lflags |= LF_R_XCCD;
1275  }
1276  else {
1277 
1278  /*
1279  * Resize and fill the fit data vectors
1280  */
1281 
1282  if (ndata != cpl_matrix_get_nrow(x)) {
1283  cpl_matrix_set_size(x, ndata, 1);
1284  cpl_matrix_set_size(y, ndata, 1);
1285  cpl_matrix_set_size(sigma, ndata, 1);
1286  }
1287 
1288  for (k = 0; k < ndata; k++) {
1289 
1290  cxint l = xmin + k;
1291 
1292  cxdouble *sdata = cpl_image_get_data(_spectra);
1293  cxdouble *edata = cpl_image_get_data(_errors);
1294 
1295  cpl_matrix_set(x, k, 0, xmin + k);
1296  cpl_matrix_set(y, k, 0, sdata[l * nfibers + i]);
1297  cpl_matrix_set(sigma, k, 0, edata[l * nfibers + i]);
1298 
1299  }
1300 
1301  }
1302 
1303 
1304  /*
1305  * Convert line FWHM to the model's width parameter, i.e.
1306  * gaussian or exponetial width depending on the selected
1307  * model profile.
1308  *
1309  * Note: The absolute value of setup->psfexp is used below,
1310  * because the sign just determines whether it is
1311  * fitted or kept fixed.
1312  */
1313 
1314  if (strcmp(setup->model, "psfexp") == 0) {
1315 
1316  exponent = fabs(setup->psfexp);
1317 
1318  lwidth = pow(0.5 * setup->grwid * nx, exponent) / LOG2;
1319 
1320  }
1321  else if (strcmp(setup->model, "psfexp2") == 0) {
1322 
1323  exponent = fabs(setup->psfexp);
1324 
1325  lwidth = setup->grwid * nx / (2. * pow(LOG2, 1. / exponent));
1326 
1327  }
1328  else if (strcmp(setup->model, "gaussian") == 0) {
1329 
1330  lwidth = setup->grwid * nx / fwhm_ratio;
1331 
1332  }
1333  else {
1334 
1335  /*
1336  * This point should never be reached!
1337  */
1338 
1339  gi_error("Unsupported line model encountered!");
1340 
1341  }
1342 
1343 
1344  /*
1345  * Validate and set the initial values of the line model
1346  * fit parameters
1347  */
1348 
1349 
1350  _giraffe_line_fit_setup(model, lwidth, xmin, y, sigma, setup,
1351  &lflags);
1352 
1353 
1354  if (lflags == LF_R_NONE) {
1355 
1356  cxint xline = giraffe_model_get_parameter(model, "Center");
1357 
1358  cxdouble hwidth = 0.;
1359 
1360  cxint status = 0;
1361 
1362 
1363  /*
1364  * Fit the model
1365  */
1366 
1367  status = giraffe_model_fit(model, x, y, sigma);
1368 
1369 
1370  amplitude = giraffe_model_get_parameter(model, "Amplitude");
1371  background = giraffe_model_get_parameter(model, "Background");
1372  center = giraffe_model_get_parameter(model, "Center");
1373 
1374 
1375  /*
1376  * Convert the model dependent width parameter back to
1377  * the line's FWHM .
1378  */
1379 
1380  if (strcmp(setup->model, "psfexp") == 0) {
1381 
1382  width1 = giraffe_model_get_parameter(model, "Width1");
1383  exponent = giraffe_model_get_parameter(model, "Width2");
1384 
1385  /* exponential width */
1386  gwidth = 2. * pow(width1 * LOG2, 1. / exponent);
1387 
1388  }
1389  else if (strcmp(setup->model, "psfexp2") == 0) {
1390 
1391  width1 = giraffe_model_get_parameter(model, "Width1");
1392  exponent = giraffe_model_get_parameter(model, "Width2");
1393 
1394  /* exponential width */
1395  gwidth = 2. * pow(LOG2, 1. / exponent) * width1;
1396 
1397  }
1398  else if (strcmp(setup->model, "gaussian") == 0) {
1399 
1400  width1 = giraffe_model_get_parameter(model, "Width1");
1401 
1402  /* gaussian width */
1403  gwidth = width1 * fwhm_ratio;
1404 
1405  }
1406  else {
1407 
1408  /*
1409  * This point should never be reached!
1410  */
1411 
1412  gi_error("Unsupported line model encountered!");
1413 
1414  }
1415 
1416  hwidth = gwidth / 2.;
1417  iterations = giraffe_model_get_position(model);
1418 
1419 
1420  /*
1421  * Check line fit. Set rejection code for lines
1422  * with bad fit parameters.
1423  */
1424 
1425  if (status < 0) {
1426 
1427  /* Fit error */
1428  lflags |= LF_R_ERROR;
1429 
1430  }
1431 
1432  if (iterations >= giraffe_model_get_iterations(model)) {
1433 
1434  /* Maximum number of iterations reached */
1435  lflags |= LF_R_NITER;
1436 
1437  }
1438 
1439  if (xmin > center || center > xmax) {
1440 
1441  /* Line center out of window */
1442  lflags |= LF_R_CENTR;
1443 
1444  }
1445 
1446  if ((center - hwidth) < xmin) {
1447 
1448  /* Line out of window on the left */
1449  lflags |= LF_R_LEFT;
1450 
1451  }
1452 
1453  if ((center + hwidth) > xmax) {
1454 
1455  /* Line out of window on the right */
1456  lflags |= LF_R_RIGHT;
1457 
1458  }
1459 
1460  if ((center - xline) >= setup->offst) {
1461 
1462  /* Line center too far from the initial guess */
1463  lflags |= LF_R_OFFST;
1464 
1465  }
1466 
1467  if (width1 < 0. || exponent < 0.) {
1468 
1469  /* Negative line width */
1470  lflags |= LF_R_BADLN;
1471 
1472  }
1473 
1474  if (gwidth > (xmax - xmin)) {
1475 
1476  /* Line width is larger than window */
1477  lflags |= LF_R_WIDTH;
1478 
1479  }
1480 
1481  if (gwidth < (setup->grwid * nx * setup->wfact)) {
1482 
1483  /* Line width too small for resolution */
1484  lflags |= LF_R_RESOL;
1485 
1486  }
1487 
1488  if (gwidth > (setup->grwid * nx / setup->wfact)) {
1489 
1490  /* Line width too large for resolution */
1491  lflags |= LF_R_RESOL;
1492 
1493  }
1494 
1495  }
1496 
1497 
1498  /*
1499  * Save the results
1500  */
1501 
1502  /* Line status code */
1503 
1504  giraffe_linedata_set_status(lines, i, j, lflags);
1505 
1506  giraffe_linedata_set(lines, "Iterations", i, j,iterations);
1507  giraffe_linedata_set(lines, "Chi-square", i, j,
1508  giraffe_model_get_chisq(model));
1509  giraffe_linedata_set(lines, "DoF", i, j,
1510  giraffe_model_get_df(model));
1511  giraffe_linedata_set(lines, "R-square", i, j,
1512  giraffe_model_get_rsquare(model));
1513  giraffe_linedata_set(lines, "Xccd", i, j, xccd);
1514  giraffe_linedata_set(lines, "Yccd", i, j, yccd);
1515 
1516  /* FIXME: Using the two functions prototyped below would
1517  * improve the code here. But they need support
1518  * from the GiModel implementation. (RP)
1519  *
1520  * giraffe_linedata_set_parameters(lines, model, i, j);
1521  * giraffe_linedata_set_errors(lines, model, i, j);
1522  */
1523 
1524  giraffe_linedata_set(lines, "Amplitude", i, j, amplitude);
1525  giraffe_linedata_set(lines, "Background", i, j, background);
1526  giraffe_linedata_set(lines, "Center", i, j, center);
1527  giraffe_linedata_set(lines, "Width1", i, j, width1);
1528 
1529  if (strncmp(setup->model, "psfexp", 6) == 0) {
1530  giraffe_linedata_set(lines, "Width2", i, j, exponent);
1531  }
1532 
1533  giraffe_linedata_set(lines, "FWHM", i, j, gwidth);
1534 
1535  /* Uncertainties */
1536 
1537  error = giraffe_model_get_sigma(model, "Amplitude");
1538  giraffe_linedata_set(lines, "dAmplitude", i, j, error);
1539 
1540  error = giraffe_model_get_sigma(model, "Center");
1541  giraffe_linedata_set(lines, "dCenter", i, j, error);
1542 
1543  error = giraffe_model_get_sigma(model, "Background");
1544  giraffe_linedata_set(lines, "dBackground", i, j, error);
1545 
1546  error = giraffe_model_get_sigma(model, "Width1");
1547  giraffe_linedata_set(lines, "dWidth1", i, j, error);
1548 
1549  if (strncmp(setup->model, "psfexp", 6) == 0) {
1550  error = giraffe_model_get_sigma(model, "Width2");
1551  giraffe_linedata_set(lines, "dWidth2", i, j, error);
1552  }
1553 
1554  }
1555 
1556  }
1557 
1558  cpl_matrix_delete(x);
1559  cpl_matrix_delete(y);
1560  cpl_matrix_delete(sigma);
1561 
1562  giraffe_model_delete(model);
1563 
1564  return 0;
1565 
1566 }
1567 
1568 
1569 inline static cpl_image *
1570 _giraffe_psf_fit(GiLineData *lines, const GiLocalization *localization,
1571  GiTable *fibers, GiTable *slitgeometry, GiSCFitParams *setup)
1572 {
1573 
1574  const cxchar *const fctid = "_giraffe_psf_fit";
1575 
1576 
1577  cxint i;
1578  cxint status = 0;
1579  cxint ngood = 0;
1580  cxint nlines = 0;
1581  cxint nsubslits = 1;
1582  cxint nx = 0;
1583 
1584  cpl_table *_fibers = NULL;
1585 
1586  cpl_image *locy = NULL;
1587  cpl_image *locw = NULL;
1588  cpl_image *psfwidth = NULL;
1589 
1590 
1591  cx_assert(lines != NULL);
1592  cx_assert(localization != NULL);
1593  cx_assert(fibers != NULL);
1594  cx_assert(slitgeometry != NULL);
1595  cx_assert(setup != NULL);
1596 
1597  _fibers = giraffe_table_get(fibers);
1598  cx_assert(_fibers != NULL);
1599 
1600  locy = giraffe_image_get(localization->locy);
1601  cx_assert(locy != NULL);
1602 
1603  locw = giraffe_image_get(localization->locw);
1604  cx_assert(locw != NULL);
1605 
1606  nx = cpl_image_get_size_y(locy);
1607  nlines = giraffe_linedata_lines(lines);
1608 
1609  psfwidth = cpl_image_new(cpl_table_get_nrow(_fibers), nx,
1610  CPL_TYPE_DOUBLE);
1611 
1612  if (setup->subslits == TRUE) {
1613  nsubslits = _giraffe_subslit_get_max(_fibers);
1614  }
1615 
1616  for (i = 0; i < nsubslits; i++) {
1617 
1618  cxint j;
1619  cxint k;
1620  cxint ssn = 0;
1621  cxint nfibers = 0;
1622  cxint ndata = 0;
1623  cxint iterations = 0;
1624  cxint accepted = 0;
1625  cxint total = 0;
1626 
1627  cxdouble ymin = 0.;
1628  cxdouble ymax = 0.;
1629  cxdouble ratio = 1.;
1630 
1631  cpl_matrix *xss = NULL;
1632  cpl_matrix *yss = NULL;
1633  cpl_matrix *wss = NULL;
1634  cpl_matrix *sss = NULL;
1635  cpl_matrix *nss = NULL;
1636  cpl_matrix *lss = NULL;
1637  cpl_matrix *base = NULL;
1638  cpl_matrix *fit = NULL;
1639  cpl_matrix *coeff = NULL;
1640  cpl_matrix *chebyshev = NULL;
1641 
1642  cpl_table *subslit = NULL;
1643 
1644  GiChebyshev2D *psffit = NULL;
1645 
1646 
1647  if (setup->subslits == TRUE) {
1648  subslit = _giraffe_subslit_get(_fibers, i + 1);
1649  ssn = cpl_table_get_int(subslit, "SSN", 0, NULL);
1650 
1651  cx_assert(ssn == i + 1);
1652  }
1653  else {
1654  subslit = cpl_table_duplicate(_fibers);
1655  ssn = 0;
1656  }
1657 
1658  if (subslit == NULL) {
1659  continue;
1660  }
1661 
1662  _giraffe_subslit_range(subslit, locy, locw, &ymin, &ymax);
1663 
1664  nfibers = cpl_table_get_nrow(subslit);
1665  ndata = nfibers * nlines;
1666 
1667 
1668  xss = cpl_matrix_new(ndata, 1); /* X abcissas for subslit */
1669  yss = cpl_matrix_new(ndata, 1); /* Y ordinates */
1670  wss = cpl_matrix_new(1, ndata); /* widths (transposed) */
1671  sss = cpl_matrix_new(ndata, 1); /* width sigmas */
1672  nss = cpl_matrix_new(ndata, 1); /* fiber indices */
1673  lss = cpl_matrix_new(ndata, 1); /* line indices */
1674 
1675 
1676  /*
1677  * Fills inputs matrices with good lines xccd, yccd, width,
1678  * width sigmas and line status
1679  */
1680 
1681  k = 0;
1682 
1683  for (j = 0; j < nfibers; j++) {
1684 
1685  cxint l;
1686  cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
1687 
1688  for (l = 0; l < nlines; l++) {
1689 
1690  cxdouble value = 0.;
1691  cxdouble yccd = giraffe_linedata_get(lines, "Yccd", n, l);
1692 
1693  if (giraffe_linedata_get_status(lines, n, l) != 0) {
1694  continue;
1695  }
1696 
1697  if (yccd < ymin || yccd > ymax) {
1698  continue;
1699  }
1700 
1701  value = giraffe_linedata_get(lines, "Xccd", n, l);
1702  cpl_matrix_set(xss, k, 0, value);
1703 
1704  cpl_matrix_set(yss, k, 0, yccd);
1705 
1706  /* FIXME: The orignal code fits the FWHM but uses
1707  * the uncertainty of the profile's width
1708  * parameter to reject lines in the sigma
1709  * clipping below. Instead the FWHM's
1710  * uncertainty should be used. Check this! (RP)
1711  */
1712 
1713  value = giraffe_linedata_get(lines, "FWHM", n, l);
1714  cpl_matrix_set(wss, 0, k, value);
1715 
1716  value = giraffe_linedata_get(lines, "dWidth1", n, l);
1717  cpl_matrix_set(sss, k, 0, value);
1718 
1719  cpl_matrix_set(nss, k, 0, n);
1720  cpl_matrix_set(lss, k, 0, l);
1721 
1722  ++k;
1723 
1724  }
1725 
1726  }
1727 
1728  if (k == 0) {
1729  cpl_msg_debug(fctid, "Skipping subslit %d: No input lines left! "
1730  "All lines have non-zero status or are beyond the "
1731  "subslit boundaries (%.4f, %.4f).", ssn, ymin, ymax);
1732  continue;
1733  }
1734 
1735  /*
1736  * Shrink input matrices to their actual size
1737  */
1738 
1739  cpl_matrix_set_size(xss, k, 1);
1740  cpl_matrix_set_size(yss, k, 1);
1741  cpl_matrix_set_size(wss, 1, k);
1742  cpl_matrix_set_size(sss, k, 1);
1743  cpl_matrix_set_size(nss, k, 1);
1744  cpl_matrix_set_size(lss, k, 1);
1745 
1746 
1747  /*
1748  * Sigma clipping
1749  */
1750 
1751  iterations = 0;
1752  ratio = 1.0;
1753  accepted = cpl_matrix_get_ncol(wss);
1754  total = accepted;
1755 
1756  while (accepted > 0 && iterations < setup->clip.iterations &&
1757  ratio > setup->clip.fraction) {
1758 
1759  base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
1760  setup->fit.xorder + 1,
1761  setup->fit.yorder + 1, xss, yss);
1762 
1763  if (coeff != NULL) {
1764  cpl_matrix_delete(coeff);
1765  coeff = NULL;
1766  }
1767 
1768  coeff = giraffe_matrix_leastsq(base, wss);
1769 
1770  if (coeff == NULL) {
1771  cpl_msg_debug(fctid, "Error solving linear system for "
1772  "subslit %d, skipping subslit.", ssn);
1773  break;
1774  }
1775 
1776  fit = cpl_matrix_product_create(coeff, base);
1777 
1778  k = 0;
1779 
1780  for (j = 0; j < cpl_matrix_get_ncol(fit); j++) {
1781 
1782  cxdouble _fit = cpl_matrix_get(fit, 0, j);
1783  cxdouble _wss = cpl_matrix_get(wss, 0, j);
1784  cxdouble _sss = cpl_matrix_get(sss, j, 0);
1785 
1786  if (fabs(_fit - _wss) >= setup->clip.level * _sss) {
1787 
1788  cxint n = (cxint)cpl_matrix_get(nss, j, 0);
1789  cxint l = (cxint)cpl_matrix_get(lss, j, 0);
1790 
1791  /*
1792  * Reject this line
1793  */
1794 
1795  giraffe_linedata_set_status(lines, n, l, LF_R_PSFIT);
1796  continue;
1797 
1798  }
1799 
1800  cpl_matrix_set(xss, k, 0, cpl_matrix_get(xss, j, 0));
1801  cpl_matrix_set(yss, k, 0, cpl_matrix_get(yss, j, 0));
1802  cpl_matrix_set(wss, 0, k, cpl_matrix_get(wss, 0, j));
1803  cpl_matrix_set(sss, k, 0, cpl_matrix_get(sss, j, 0));
1804  cpl_matrix_set(nss, k, 0, cpl_matrix_get(nss, j, 0));
1805  cpl_matrix_set(lss, k, 0, cpl_matrix_get(lss, j, 0));
1806  ++k;
1807 
1808  }
1809 
1810  cpl_matrix_delete(base);
1811  cpl_matrix_delete(fit);
1812 
1813  if (k == accepted) {
1814 
1815  /*
1816  * No new points rejected, no more iterations. That's it.
1817  */
1818 
1819  break;
1820  }
1821  else {
1822  accepted = k;
1823  ratio = (cxdouble)accepted / (cxdouble)total;
1824 
1825  cpl_matrix_set_size(xss, k, 1);
1826  cpl_matrix_set_size(yss, k, 1);
1827  cpl_matrix_set_size(wss, 1, k);
1828  cpl_matrix_set_size(sss, k, 1);
1829  cpl_matrix_set_size(nss, k, 1);
1830  cpl_matrix_set_size(lss, k, 1);
1831 
1832  ++iterations;
1833 
1834  }
1835 
1836  }
1837 
1838  if (accepted == 0) {
1839  cpl_msg_debug(fctid, "Subslit %d: All lines rejected.", ssn);
1840  continue;
1841  }
1842 
1843  if (coeff == NULL) {
1844  continue;
1845  }
1846 
1847  ngood += accepted;
1848 
1849  cpl_matrix_delete(xss);
1850  cpl_matrix_delete(yss);
1851  cpl_matrix_delete(wss);
1852  cpl_matrix_delete(sss);
1853  cpl_matrix_delete(nss);
1854  cpl_matrix_delete(lss);
1855 
1856 
1857  /*
1858  * Compute coordinate grid for the whole subslit
1859  */
1860 
1861  xss = cpl_matrix_new(nx * nfibers, 1);
1862  yss = cpl_matrix_new(nx * nfibers, 1);
1863 
1864  giraffe_compute_image_coordinates(nx, nfibers, xss, NULL);
1865 
1866  for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
1867 
1868  const cxchar *idx = giraffe_fiberlist_query_index(subslit);
1869 
1870  cxint l;
1871  cxint ns = cpl_image_get_size_x(locy);
1872  cxint cs = cpl_table_get_int(subslit, idx, j, NULL) - 1;
1873 
1874  cxdouble *data = cpl_image_get_data(locy);
1875 
1876 
1877  for (l = 0; l < nx; l++) {
1878  cpl_matrix_set(yss, l * nfibers + j, 0, data[l * ns + cs]);
1879  }
1880 
1881  }
1882 
1883 
1884  /*
1885  * Compute fitted PSF width on the whole subslit
1886  */
1887 
1888  base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
1889  setup->fit.xorder + 1,
1890  setup->fit.yorder + 1, xss, yss);
1891 
1892  fit = cpl_matrix_product_create(coeff, base);
1893 
1894  cpl_matrix_delete(xss);
1895  xss = NULL;
1896 
1897  cpl_matrix_delete(yss);
1898  yss = NULL;
1899 
1900  /* TODO: Store fit coefficients in output structure
1901  */
1902 
1903  /*
1904  * The matrix 'chebyshev' is a view into 'coeff' with the correct
1905  * shape for the subsequent call of giraffe_chebyshev2d_new().
1906  * Therefore, if 'chebyshev' is destroyed afterwards the data
1907  * of the matrix must not be destroyed!
1908  */
1909 
1910  chebyshev = cpl_matrix_wrap(setup->fit.xorder + 1,
1911  setup->fit.yorder + 1,
1912  cpl_matrix_get_data(coeff));
1913 
1914  psffit = giraffe_chebyshev2d_new(setup->fit.xorder, setup->fit.yorder);
1915  status = giraffe_chebyshev2d_set(psffit, 0., nx, ymin, ymax,
1916  chebyshev);
1917 
1918  if (status != 0) {
1919 
1920  giraffe_chebyshev2d_delete(psffit);
1921 
1922  cpl_matrix_unwrap(chebyshev);
1923 
1924  cpl_matrix_delete(base);
1925  cpl_matrix_delete(coeff);
1926  cpl_matrix_delete(fit);
1927 
1928  cpl_table_delete(subslit);
1929 
1930  cpl_image_delete(psfwidth);
1931 
1932  return NULL;
1933 
1934  }
1935 
1936  cpl_matrix_unwrap(chebyshev);
1937  chebyshev = NULL;
1938 
1939  giraffe_chebyshev2d_delete(psffit);
1940  psffit = NULL;
1941 
1942 
1943  /*
1944  * Save fitted PSF of the line profile to the output image.
1945  */
1946 
1947  for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
1948 
1949  cxint l;
1950  cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
1951  cxint ns = cpl_table_get_nrow(_fibers);
1952 
1953  cxdouble *data = cpl_image_get_data(psfwidth);
1954 
1955  for (l = 0; l < nx; l++) {
1956  data[l * ns + n] = cpl_matrix_get(fit, 0, l * nfibers + j);
1957  }
1958 
1959  }
1960 
1961  cpl_matrix_delete(base);
1962  cpl_matrix_delete(coeff);
1963  cpl_matrix_delete(fit);
1964 
1965  cpl_table_delete(subslit);
1966 
1967  }
1968 
1969  return psfwidth;
1970 
1971 }
1972 
1973 
1974 inline static cxint
1975 _giraffe_opticalmodel_fit(GiWlSolution *solution, GiLineData *lines,
1976  GiTable *fibers, GiTable *slitgeometry,
1977  GiOpticalModelParams *setup)
1978 {
1979 
1980  const cxchar *const fctid = "_giraffe_opticalmodel_fit";
1981 
1982 
1983  cxint status = 0;
1984  cxint ndata = 0;
1985  cxint ngood = 0;
1986 
1987  cxsize i;
1988 
1989  cpl_matrix *x = NULL;
1990  cpl_matrix *y = NULL;
1991  cpl_matrix *sigma = NULL;
1992 
1993  cpl_table *_fibers = NULL;
1994  cpl_table *_slitgeometry = NULL;
1995 
1996  GiModel *model = NULL;
1997 
1998 
1999  cx_assert(solution != NULL);
2000  cx_assert(lines != NULL);
2001  cx_assert(fibers != NULL);
2002  cx_assert(slitgeometry != NULL);
2003  cx_assert(setup != NULL);
2004 
2005  _fibers = giraffe_table_get(fibers);
2006  cx_assert(_fibers != NULL);
2007 
2008  _slitgeometry = giraffe_table_get(slitgeometry);
2009  cx_assert(_slitgeometry != NULL);
2010 
2011  model = giraffe_wlsolution_model(solution);
2012 
2013 
2014  /*
2015  * Prepare input data
2016  */
2017 
2018  ndata = giraffe_linedata_lines(lines) * giraffe_linedata_fibers(lines);
2019 
2020  x = cpl_matrix_new(ndata, giraffe_model_count_arguments(model));
2021  y = cpl_matrix_new(ndata, 1);
2022  sigma = cpl_matrix_new(ndata, 1);
2023 
2024  for (i = 0; i < giraffe_linedata_fibers(lines); i++) {
2025 
2026  cxsize j;
2027 
2028  cxdouble xf = cpl_table_get(_slitgeometry, "XF", i, NULL);
2029  cxdouble yf = cpl_table_get(_slitgeometry, "YF", i, NULL);
2030 
2031 
2032  for (j = 0; j < giraffe_linedata_lines(lines); j++) {
2033 
2034  if (giraffe_linedata_get_status(lines, i, j) != 0) {
2035  continue;
2036  }
2037 
2038  /* FIXME: Is this really needed? The status should be enough! (RP)
2039  */
2040 
2041  if (giraffe_linedata_get(lines, "dCenter", i, j) <= 0.) {
2042  continue;
2043  }
2044 
2045  cpl_matrix_set(x, ngood, 0,
2046  giraffe_linedata_get_wavelength(lines, j));
2047  cpl_matrix_set(x, ngood, 1, xf);
2048  cpl_matrix_set(x, ngood, 2, yf);
2049 
2050  cpl_matrix_set(y, ngood, 0,
2051  giraffe_linedata_get(lines, "Center", i, j));
2052  cpl_matrix_set(sigma, ngood, 0,
2053  giraffe_linedata_get(lines, "dCenter", i, j));
2054 
2055  ++ngood;
2056 
2057  }
2058 
2059  }
2060 
2061  cpl_msg_debug(fctid, "Using %d of %d line positions for optical "
2062  "model fit.", ngood, ndata);
2063 
2064  if (ngood == 0) {
2065 
2066  cpl_matrix_delete(x);
2067  cpl_matrix_delete(y);
2068  cpl_matrix_delete(sigma);
2069 
2070  return 1;
2071  }
2072 
2073 
2074  /*
2075  * Shrink input matrices to their actual size
2076  */
2077 
2078  cpl_matrix_set_size(x, ngood, giraffe_model_count_arguments(model));
2079  cpl_matrix_set_size(y, ngood, 1);
2080  cpl_matrix_set_size(sigma, ngood, 1);
2081 
2082 
2083  /*
2084  * Configure the optical model fit.
2085  */
2086 
2087  giraffe_model_freeze(model);
2088 
2089  if (setup->flags & OPTM_FLENGTH) {
2090  giraffe_model_thaw_parameter(model, "FocalLength");
2091  }
2092 
2093  if (setup->flags & OPTM_GCAMERA) {
2094  giraffe_model_thaw_parameter(model, "Magnification");
2095  }
2096 
2097  if (setup->flags & OPTM_THETA) {
2098  giraffe_model_thaw_parameter(model, "Angle");
2099  }
2100 
2101  if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
2102  if (setup->flags & OPTM_SX) {
2103  giraffe_model_thaw_parameter(model, "Sdx");
2104  }
2105 
2106  if (setup->flags & OPTM_SY) {
2107  giraffe_model_thaw_parameter(model, "Sdy");
2108  }
2109 
2110  if (setup->flags & OPTM_SPHI) {
2111  giraffe_model_thaw_parameter(model, "Sphi");
2112  }
2113  }
2114 
2115  giraffe_model_set_iterations(model, setup->fit.iterations);
2116  giraffe_model_set_tests(model, setup->fit.tests);
2117  giraffe_model_set_delta(model, setup->fit.delta);
2118 
2119 
2120  /*
2121  * Fit the model
2122  */
2123 
2124  status = giraffe_model_fit(model, x, y, sigma);
2125 
2126  if (status < 0) {
2127 
2128  cpl_matrix_delete(x);
2129  cpl_matrix_delete(y);
2130  cpl_matrix_delete(sigma);
2131 
2132  return 2;
2133 
2134  }
2135 
2136  cpl_matrix_delete(x);
2137  cpl_matrix_delete(y);
2138  cpl_matrix_delete(sigma);
2139 
2140  return 0;
2141 
2142 }
2143 
2144 
2145 inline static cxint
2146 _giraffe_opticalmodel_format(cx_string *s, const GiModel *model,
2147  GiOpticalModelInfo info)
2148 {
2149 
2150  const cxchar *name = NULL;
2151 
2152  cxbool offsets = FALSE;
2153 
2154  cxint status = 0;
2155 
2156 
2157  cx_assert(s != NULL);
2158 
2159  if (model == NULL) {
2160  return -1;
2161  }
2162 
2163  name = giraffe_model_get_name(model);
2164 
2165  if (name == NULL || strncmp(name, "xoptmod", 7) != 0) {
2166  return -2;
2167  }
2168  else {
2169  if (strncmp(name, "xoptmod2", 8) == 0) {
2170  offsets = TRUE;
2171  }
2172  }
2173 
2174  switch (info) {
2175 
2176  case GI_OPTM_PARAMETER_VALUES:
2177  {
2178 
2179  cxdouble fcoll = 0.;
2180  cxdouble gcam = 0.;
2181  cxdouble theta = 0.;
2182 
2183  fcoll = giraffe_model_get_parameter(model, "FocalLength");
2184  gcam = giraffe_model_get_parameter(model, "Magnification");
2185  theta = giraffe_model_get_parameter(model, "Angle");
2186 
2187  cx_string_sprintf(s, "focal length = %.6f, camera "
2188  "magnification = %.6f, grating angle = %.9f",
2189  fcoll, gcam, theta);
2190 
2191  if (offsets == TRUE) {
2192 
2193  cxdouble sdx = 0.;
2194  cxdouble sdy = 0.;
2195  cxdouble sphi = 0.;
2196 
2197  cx_string *_s = cx_string_new();
2198 
2199  sdx = giraffe_model_get_parameter(model, "Sdx");
2200  sdy = giraffe_model_get_parameter(model, "Sdy");
2201  sphi = giraffe_model_get_parameter(model, "Sphi");
2202 
2203  cx_string_sprintf(_s, ", slit x-shift = %.9f, slit "
2204  "y-shift = %.9f, slit rotation = %.9f",
2205  sdx, sdy, sphi);
2206  cx_string_append(s, cx_string_get(_s));
2207 
2208  cx_string_delete(_s);
2209  _s = NULL;
2210 
2211  }
2212 
2213  break;
2214  }
2215 
2216  case GI_OPTM_PARAMETER_ERRORS:
2217  {
2218 
2219  cxdouble fcoll = 0.;
2220  cxdouble gcam = 0.;
2221  cxdouble theta = 0.;
2222 
2223  fcoll = giraffe_model_get_sigma(model, "FocalLength");
2224  gcam = giraffe_model_get_sigma(model, "Magnification");
2225  theta = giraffe_model_get_sigma(model, "Angle");
2226 
2227  cx_string_sprintf(s, "focal length = %.6f, camera "
2228  "magnification = %.6f, grating angle = %.9f",
2229  fcoll, gcam, theta);
2230 
2231  if (offsets == TRUE) {
2232 
2233  cxdouble sdx = 0.;
2234  cxdouble sdy = 0.;
2235  cxdouble sphi = 0.;
2236 
2237  cx_string *_s = cx_string_new();
2238 
2239  sdx = giraffe_model_get_sigma(model, "Sdx");
2240  sdy = giraffe_model_get_sigma(model, "Sdy");
2241  sphi = giraffe_model_get_sigma(model, "Sphi");
2242 
2243  cx_string_sprintf(_s, ", slit x-shift = %.9f, slit "
2244  "y-shift = %.9f, slit rotation = %.9f",
2245  sdx, sdy, sphi);
2246  cx_string_append(s, cx_string_get(_s));
2247 
2248  cx_string_delete(_s);
2249  _s = NULL;
2250 
2251  }
2252 
2253  break;
2254  }
2255 
2256  case GI_OPTM_PARAMETER_STATUS:
2257  {
2258 
2259  const cxchar *const s_free = "free";
2260  const cxchar *const s_frozen = "frozen";
2261  const cxchar *t = NULL;
2262 
2263  cx_string *buffer = cx_string_new();
2264 
2265 
2266  t = giraffe_model_frozen_parameter(model, "FocalLength") ?
2267  s_frozen : s_free;
2268  cx_string_sprintf(buffer, "focal length = %s", t);
2269  cx_string_set(s, cx_string_get(buffer));
2270 
2271  t = giraffe_model_frozen_parameter(model, "Magnification") ?
2272  s_frozen : s_free;
2273  cx_string_sprintf(buffer, ", camera magnification = %s", t);
2274  cx_string_append(s, cx_string_get(buffer));
2275 
2276  t = giraffe_model_frozen_parameter(model, "Angle") ?
2277  s_frozen : s_free;
2278  cx_string_sprintf(buffer, ", grating angle = %s", t);
2279  cx_string_append(s, cx_string_get(buffer));
2280 
2281 
2282  if (offsets == TRUE) {
2283 
2284  t = giraffe_model_frozen_parameter(model, "Sdx") ?
2285  s_frozen : s_free;
2286  cx_string_sprintf(buffer, ", slit x-shift = %s", t);
2287  cx_string_append(s, cx_string_get(buffer));
2288 
2289  t = giraffe_model_frozen_parameter(model, "Sdy") ?
2290  s_frozen : s_free;
2291  cx_string_sprintf(buffer, ", slit y-shift = %s", t);
2292  cx_string_append(s, cx_string_get(buffer));
2293 
2294  t = giraffe_model_frozen_parameter(model, "Sphi") ?
2295  s_frozen : s_free;
2296  cx_string_sprintf(buffer, ", slit rotation = %s", t);
2297  cx_string_append(s, cx_string_get(buffer));
2298 
2299  }
2300 
2301  cx_string_delete(buffer);
2302  buffer = NULL;
2303 
2304  break;
2305  }
2306 
2307  default:
2308  status = -2;
2309  break;
2310 
2311  }
2312 
2313  return status;
2314 
2315 }
2316 
2317 
2318 inline static cpl_image *
2319 _giraffe_residuals_fit(GiWlResiduals *residuals, GiLineData *lines,
2320  const GiLocalization *localization, GiTable *fibers,
2321  GiTable *slitgeometry, GiSCFitParams *setup)
2322 {
2323 
2324  const cxchar *const fctid = "_giraffe_residuals_fit";
2325 
2326 
2327  cxint i;
2328  cxint status = 0;
2329  cxint ngood = 0;
2330  cxint nlines = 0;
2331  cxint nsubslits = 1;
2332  cxint nx = 0;
2333 
2334  cpl_table *_fibers = NULL;
2335 
2336  cpl_image *locy = NULL;
2337  cpl_image *locw = NULL;
2338  cpl_image *xresiduals = NULL;
2339 
2340 
2341  cx_assert(lines != NULL);
2342  cx_assert(localization != NULL);
2343  cx_assert(fibers != NULL);
2344  cx_assert(slitgeometry != NULL);
2345  cx_assert(setup != NULL);
2346 
2347  _fibers = giraffe_table_get(fibers);
2348  cx_assert(_fibers != NULL);
2349 
2350  locy = giraffe_image_get(localization->locy);
2351  cx_assert(locy != NULL);
2352 
2353  locw = giraffe_image_get(localization->locw);
2354  cx_assert(locw != NULL);
2355 
2356  nx = cpl_image_get_size_y(locy);
2357  nlines = giraffe_linedata_lines(lines);
2358 
2359  xresiduals = cpl_image_new(cpl_table_get_nrow(_fibers), nx,
2360  CPL_TYPE_DOUBLE);
2361 
2362  if (setup->subslits == TRUE) {
2363  nsubslits = _giraffe_subslit_get_max(_fibers);
2364  }
2365 
2366  for (i = 0; i < nsubslits; i++) {
2367 
2368  cxint j;
2369  cxint k;
2370  cxint ssn = 0;
2371  cxint nfibers = 0;
2372  cxint ndata = 0;
2373  cxint iterations = 0;
2374  cxint accepted = 0;
2375  cxint total = 0;
2376 
2377  cxdouble ymin = 0.;
2378  cxdouble ymax = 0.;
2379  cxdouble ratio = 1.;
2380  cxdouble sigma = 0.;
2381 
2382  cpl_matrix *xss = NULL;
2383  cpl_matrix *yss = NULL;
2384  cpl_matrix *rss = NULL;
2385  cpl_matrix *sss = NULL;
2386  cpl_matrix *nss = NULL;
2387  cpl_matrix *lss = NULL;
2388  cpl_matrix *base = NULL;
2389  cpl_matrix *fit = NULL;
2390  cpl_matrix *coeff = NULL;
2391  cpl_matrix *chebyshev = NULL;
2392 
2393  cpl_table *subslit = NULL;
2394 
2395  GiChebyshev2D *xwsfit = NULL;
2396 
2397 
2398  if (setup->subslits == TRUE) {
2399  subslit = _giraffe_subslit_get(_fibers, i + 1);
2400  ssn = cpl_table_get_int(subslit, "SSN", 0, NULL);
2401 
2402  cx_assert(ssn == i + 1);
2403  }
2404  else {
2405  subslit = cpl_table_duplicate(_fibers);
2406  ssn = 0;
2407  }
2408 
2409  if (subslit == NULL) {
2410  continue;
2411  }
2412 
2413  _giraffe_subslit_range(subslit, locy, locw, &ymin, &ymax);
2414 
2415  nfibers = cpl_table_get_nrow(subslit);
2416  ndata = nfibers * nlines;
2417 
2418 
2419  xss = cpl_matrix_new(ndata, 1); /* X abcissas for subslit */
2420  yss = cpl_matrix_new(ndata, 1); /* Y ordinates */
2421  rss = cpl_matrix_new(1, ndata); /* widths (transposed) */
2422  sss = cpl_matrix_new(ndata, 1); /* width sigmas */
2423  nss = cpl_matrix_new(ndata, 1); /* fiber indices */
2424  lss = cpl_matrix_new(ndata, 1); /* line indices */
2425 
2426 
2427  /*
2428  * Fills inputs matrices with good lines xccd, yccd, width,
2429  * width sigmas and line status
2430  */
2431 
2432  k = 0;
2433 
2434  for (j = 0; j < nfibers; j++) {
2435 
2436  cxint l;
2437  cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
2438 
2439  for (l = 0; l < nlines; l++) {
2440 
2441  cxdouble value = 0.;
2442  cxdouble xccd = giraffe_linedata_get(lines, "Xccd", n, l);
2443  cxdouble yccd = giraffe_linedata_get(lines, "Yccd", n, l);
2444 
2445  if (giraffe_linedata_get_status(lines, n, l) != 0) {
2446  continue;
2447  }
2448 
2449  if (yccd < ymin || yccd > ymax) {
2450  continue;
2451  }
2452 
2453  cpl_matrix_set(xss, k, 0, xccd);
2454  cpl_matrix_set(yss, k, 0, yccd);
2455 
2456  value = xccd - giraffe_linedata_get(lines, "Center", n, l);
2457 
2458  cpl_matrix_set(rss, 0, k, value);
2459  giraffe_linedata_set(lines, "Xoff", n, l, value);
2460 
2461  value = giraffe_linedata_get(lines, "dCenter", n, l);
2462  cpl_matrix_set(sss, k, 0, value);
2463 
2464  cpl_matrix_set(nss, k, 0, n);
2465  cpl_matrix_set(lss, k, 0, l);
2466 
2467  ++k;
2468 
2469  }
2470 
2471  }
2472 
2473  if (k == 0) {
2474  cpl_msg_debug(fctid, "Skipping subslit %d: No input lines left! "
2475  "All lines have non-zero status or are beyond the "
2476  "subslit boundaries (%.4f, %.4f).", ssn, ymin, ymax);
2477  continue;
2478  }
2479 
2480  /*
2481  * Shrink input matrices to their actual size
2482  */
2483 
2484  cpl_matrix_set_size(xss, k, 1);
2485  cpl_matrix_set_size(yss, k, 1);
2486  cpl_matrix_set_size(rss, 1, k);
2487  cpl_matrix_set_size(sss, k, 1);
2488  cpl_matrix_set_size(nss, k, 1);
2489  cpl_matrix_set_size(lss, k, 1);
2490 
2491 
2492  /*
2493  * The sigma value used for the sigma-clipping is the
2494  * median value of the line center uncertainties.
2495  */
2496 
2497  sigma = cpl_matrix_get_median(sss);
2498 
2499 
2500  /*
2501  * Sigma clipping
2502  */
2503 
2504  iterations = 0;
2505  ratio = 1.0;
2506  accepted = cpl_matrix_get_ncol(rss);
2507  total = accepted;
2508 
2509  while (accepted > 0 && iterations < setup->clip.iterations &&
2510  ratio > setup->clip.fraction) {
2511 
2512  base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
2513  setup->fit.xorder + 1,
2514  setup->fit.yorder + 1, xss, yss);
2515 
2516  if (coeff != NULL) {
2517  cpl_matrix_delete(coeff);
2518  coeff = NULL;
2519  }
2520 
2521  coeff = giraffe_matrix_leastsq(base, rss);
2522 
2523  if (coeff == NULL) {
2524  cpl_msg_debug(fctid, "Error solving linear system for "
2525  "subslit %d, skipping subslit.", ssn);
2526  break;
2527  }
2528 
2529  fit = cpl_matrix_product_create(coeff, base);
2530 
2531  k = 0;
2532 
2533  for (j = 0; j < cpl_matrix_get_ncol(fit); j++) {
2534 
2535  cxdouble _fit = cpl_matrix_get(fit, 0, j);
2536  cxdouble _rss = cpl_matrix_get(rss, 0, j);
2537 
2538  if (fabs(_fit - _rss) >= setup->clip.level * sigma) {
2539 
2540  cxint n = (cxint)cpl_matrix_get(nss, j, 0);
2541  cxint l = (cxint)cpl_matrix_get(lss, j, 0);
2542 
2543  /*
2544  * Reject this line
2545  */
2546 
2547  giraffe_linedata_set_status(lines, n, l, LF_R_XRFIT);
2548  continue;
2549 
2550  }
2551 
2552  cpl_matrix_set(xss, k, 0, cpl_matrix_get(xss, j, 0));
2553  cpl_matrix_set(yss, k, 0, cpl_matrix_get(yss, j, 0));
2554  cpl_matrix_set(rss, 0, k, cpl_matrix_get(rss, 0, j));
2555  cpl_matrix_set(sss, k, 0, cpl_matrix_get(sss, j, 0));
2556  cpl_matrix_set(nss, k, 0, cpl_matrix_get(nss, j, 0));
2557  cpl_matrix_set(lss, k, 0, cpl_matrix_get(lss, j, 0));
2558  ++k;
2559 
2560  }
2561 
2562  cpl_matrix_delete(base);
2563  cpl_matrix_delete(fit);
2564 
2565  if (k == accepted) {
2566 
2567  /*
2568  * No new points rejected, no more iterations. That's it.
2569  */
2570 
2571  break;
2572  }
2573  else {
2574  accepted = k;
2575  ratio = (cxdouble)accepted / (cxdouble)total;
2576 
2577  cpl_matrix_set_size(xss, k, 1);
2578  cpl_matrix_set_size(yss, k, 1);
2579  cpl_matrix_set_size(rss, 1, k);
2580  cpl_matrix_set_size(sss, k, 1);
2581  cpl_matrix_set_size(nss, k, 1);
2582  cpl_matrix_set_size(lss, k, 1);
2583 
2584  ++iterations;
2585 
2586  }
2587 
2588  }
2589 
2590  if (accepted == 0) {
2591  cpl_msg_debug(fctid, "Subslit %d: All lines rejected.", ssn);
2592  continue;
2593  }
2594 
2595  if (coeff == NULL) {
2596  continue;
2597  }
2598 
2599  ngood += accepted;
2600 
2601  cpl_matrix_delete(xss);
2602  cpl_matrix_delete(yss);
2603  cpl_matrix_delete(rss);
2604  cpl_matrix_delete(sss);
2605  cpl_matrix_delete(nss);
2606  cpl_matrix_delete(lss);
2607 
2608 
2609  /*
2610  * Compute coordinate grid for the whole subslit
2611  */
2612 
2613  xss = cpl_matrix_new(nx * nfibers, 1);
2614  yss = cpl_matrix_new(nx * nfibers, 1);
2615 
2616  giraffe_compute_image_coordinates(nx, nfibers, xss, NULL);
2617 
2618  for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
2619 
2620  const cxchar *idx = giraffe_fiberlist_query_index(subslit);
2621 
2622  cxint l;
2623  cxint ns = cpl_image_get_size_x(locy);
2624  cxint cs = cpl_table_get_int(subslit, idx, j, NULL) - 1;
2625 
2626  cxdouble *data = cpl_image_get_data(locy);
2627 
2628 
2629  for (l = 0; l < nx; l++) {
2630  cpl_matrix_set(yss, l * nfibers + j, 0, data[l * ns + cs]);
2631  }
2632 
2633  }
2634 
2635 
2636  /*
2637  * Compute fitted residuals on the whole subslit
2638  */
2639 
2640  base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
2641  setup->fit.xorder + 1,
2642  setup->fit.yorder + 1, xss, yss);
2643 
2644  fit = cpl_matrix_product_create(coeff, base);
2645 
2646  cpl_matrix_delete(xss);
2647  xss = NULL;
2648 
2649  cpl_matrix_delete(yss);
2650  yss = NULL;
2651 
2652 
2653  /*
2654  * Insert the Chebyshev fit into the results structure
2655  */
2656 
2657  /*
2658  * The matrix 'chebyshev' is a view into 'coeff' with the correct
2659  * shape for the subsequent call of giraffe_chebyshev2d_new().
2660  * Therefore, if 'chebyshev' is destroyed afterwards the data
2661  * of the matrix must not be destroyed!
2662  */
2663 
2664  chebyshev = cpl_matrix_wrap(setup->fit.xorder + 1,
2665  setup->fit.yorder + 1,
2666  cpl_matrix_get_data(coeff));
2667 
2668  xwsfit = giraffe_chebyshev2d_new(setup->fit.xorder, setup->fit.yorder);
2669  status = giraffe_chebyshev2d_set(xwsfit, 0., nx, ymin, ymax,
2670  chebyshev);
2671 
2672  if (status != 0) {
2673 
2674  giraffe_chebyshev2d_delete(xwsfit);
2675 
2676  cpl_matrix_unwrap(chebyshev);
2677 
2678  cpl_matrix_delete(base);
2679  cpl_matrix_delete(coeff);
2680  cpl_matrix_delete(fit);
2681 
2682  cpl_table_delete(subslit);
2683 
2684  cpl_image_delete(xresiduals);
2685 
2686  return NULL;
2687 
2688  }
2689 
2690  cpl_matrix_unwrap(chebyshev);
2691  chebyshev = NULL;
2692 
2693  giraffe_wlresiduals_set(residuals, ssn, xwsfit);
2694  xwsfit = NULL;
2695 
2696 
2697  /*
2698  * Save fitted optical model residuals to the output image.
2699  */
2700 
2701  for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
2702 
2703  cxint l;
2704  cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
2705  cxint ns = cpl_table_get_nrow(_fibers);
2706 
2707  cxdouble *data = cpl_image_get_data(xresiduals);
2708 
2709  for (l = 0; l < nx; l++) {
2710  data[l * ns + n] = cpl_matrix_get(fit, 0, l * nfibers + j);
2711  }
2712 
2713  }
2714 
2715  cpl_matrix_delete(base);
2716  cpl_matrix_delete(coeff);
2717  cpl_matrix_delete(fit);
2718 
2719  cpl_table_delete(subslit);
2720 
2721  }
2722 
2723  return xresiduals;
2724 
2725 }
2726 
2727 
2728 inline static cxdouble
2729 _giraffe_compute_statistics(const GiLineData *lines, const cpl_image *xwsfit,
2730  const cpl_image *lflags)
2731 {
2732 
2733  cxint i;
2734  cxint nlines = 0;
2735  cxint nfibers = 0;
2736 
2737  cxdouble sum = 0.;
2738  cxdouble rms = 0.;
2739  cxdouble *_xccd;
2740 
2741  cpl_image *xccd = NULL;
2742 
2743 
2744  cx_assert(lines != NULL);
2745 
2746 
2747  nlines = giraffe_linedata_lines(lines);
2748  nfibers = giraffe_linedata_fibers(lines);
2749 
2750  xccd = cpl_image_duplicate(giraffe_linedata_get_data(lines, "Xccd"));
2751 
2752  if (xwsfit != NULL) {
2753 
2754  cpl_image *residuals = NULL;
2755 
2756 
2757  cx_assert(lflags != NULL);
2758 
2759  residuals = cpl_image_new(giraffe_linedata_fibers(lines),
2760  giraffe_linedata_lines(lines),
2761  CPL_TYPE_DOUBLE);
2762 
2763  _giraffe_get_residuals(residuals, xccd, xwsfit);
2764  _giraffe_apply_residuals(xccd, residuals, lflags, 0.);
2765 
2766  cpl_image_delete(residuals);
2767  residuals = NULL;
2768 
2769  }
2770 
2771  _xccd = cpl_image_get_data(xccd);
2772 
2773  for (i = 0; i < nfibers; i++) {
2774 
2775  cxint j;
2776 
2777  for (j = 0; j < nlines; j++) {
2778 
2779  if (giraffe_linedata_get_status(lines, i, j) == LF_R_NONE) {
2780 
2781  cxdouble center = giraffe_linedata_get(lines, "Center", i, j);
2782 
2783  sum += pow(center - _xccd[j * nfibers + i], 2.);
2784 
2785  }
2786 
2787  }
2788 
2789  }
2790 
2791  cpl_image_delete(xccd);
2792 
2793  rms = sqrt(sum / CX_MAX(giraffe_linedata_accepted(lines), 1.));
2794 
2795  return rms;
2796 
2797 }
2798 
2799 
2800 inline static cxint
2801 _giraffe_convert_wlsolution(GiTable *result, const GiWlSolution *solution,
2802  const GiImage *spectra, const GiGrating *setup,
2803  const GiWCalInfo *info, const GiWCalConfig *config)
2804 {
2805 
2806  cxint i;
2807  cxint status = 0;
2808  cxint sign = 1;
2809 
2810  cxdouble value = 0.;
2811  cxdouble scale = 0.;
2812  cxdouble xccd[2] = {0., 0.};
2813 
2814  cx_string *s = NULL;
2815 
2816  cpl_propertylist *properties = NULL;
2817 
2818  cpl_table *coeffs = NULL;
2819 
2820  const GiModel *model = NULL;
2821 
2822  const GiWlResiduals *residuals = NULL;
2823 
2824 
2825  cx_assert(result != NULL);
2826  cx_assert(solution != NULL);
2827 
2828  s = cx_string_new();
2829 
2830  properties =
2831  cpl_propertylist_duplicate(giraffe_image_get_properties(spectra));
2832 
2833 
2834  /* FIXME: Check whether this is still necessary! (RP)
2835  */
2836 
2837  cpl_propertylist_erase(properties, "NAXIS1");
2838  cpl_propertylist_erase(properties, "NAXIS2");
2839  cpl_propertylist_erase(properties, GIALIAS_DATAMIN);
2840  cpl_propertylist_erase(properties, GIALIAS_DATAMAX);
2841  cpl_propertylist_erase(properties, GIALIAS_EXTNAME);
2842 
2843  cpl_propertylist_erase(properties, GIALIAS_PROCATG);
2844  cpl_propertylist_erase(properties, GIALIAS_PROTYPE);
2845  cpl_propertylist_erase(properties, GIALIAS_DATAMEAN);
2846  cpl_propertylist_erase(properties, GIALIAS_DATAMEDI);
2847  cpl_propertylist_erase(properties, GIALIAS_DATASIG);
2848 
2849  cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
2850  "WAVCOEFFTAB");
2851  cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
2852  "Giraffe frame type.");
2853 
2854 
2855  /*
2856  * Grating data
2857  */
2858 
2859  /* FIXME: Is this needed? (RP)
2860  */
2861 
2862  /*
2863  cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRORDER,
2864  setup->order);
2865  cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRTHETA,
2866  setup->theta);
2867  cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRSPACE,
2868  setup->space);
2869  */
2870 
2871 
2872  /*
2873  * Write line model parameters
2874  */
2875 
2876  cpl_propertylist_update_string(properties, GIALIAS_WSOL_LMNAME,
2877  config->line_model);
2878  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMNAME,
2879  "Line profile model");
2880 
2881  cpl_propertylist_update_bool(properties, GIALIAS_WSOL_LMRES, info->residuals);
2882  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMRES,
2883  "Line detection optical model residuals flag");
2884 
2885  cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMWIDTH, info->width);
2886  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMWIDTH,
2887  "Line detection window size [pxl]");
2888 
2889  cpl_propertylist_update_double(properties, GIALIAS_WSOL_LMTHRESH,
2890  config->line_threshold);
2891  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMTHRESH,
2892  "Calibration line threshold");
2893 
2894  cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMITER,
2895  config->line_niter);
2896  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMITER,
2897  "Line profile fit maximum number "
2898  "of iterations");
2899 
2900  cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMTEST,
2901  config->line_ntest);
2902  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMTEST,
2903  "Line profile fit maximum number "
2904  "of chi-square tests");
2905 
2906  cpl_propertylist_update_double(properties, GIALIAS_WSOL_LMDCHISQ,
2907  config->line_dchisq);
2908  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMDCHISQ,
2909  "Line profile fit minimum delta "
2910  "chi-square");
2911 
2912 
2913  /*
2914  * Write PSF width parameters
2915  */
2916 
2917  cx_string_sprintf(s, "%d:%d", config->pxw_xorder, config->pxw_yorder);
2918 
2919  cpl_propertylist_update_string(properties, GIALIAS_WSOL_PWORDER,
2920  cx_string_get(s));
2921  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWORDER,
2922  "PSF width fit polynomial order");
2923 
2924  cpl_propertylist_update_double(properties, GIALIAS_WSOL_PWSIGMA,
2925  config->pxw_cliplevel);
2926  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWSIGMA,
2927  "PSF width fit sigma clipping level");
2928 
2929  cpl_propertylist_update_int(properties, GIALIAS_WSOL_PWITER,
2930  config->pxw_clipniter);
2931  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWITER,
2932  "PSF width fit maximum number of "
2933  "iterations");
2934 
2935  cpl_propertylist_update_double(properties, GIALIAS_WSOL_PWFRAC,
2936  config->pxw_clipmfrac);
2937  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWFRAC,
2938  "PSF width fit minimum fraction of "
2939  "accepted points");
2940 
2941 
2942  /*
2943  * Write optical model parameters to the result table
2944  */
2945 
2946  cpl_propertylist_update_bool(properties, GIALIAS_WSOL_OMFIT,
2947  config->opt_solution);
2948  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMFIT,
2949  "Optical model fit flag");
2950 
2951  cpl_propertylist_update_string(properties, GIALIAS_WSOL_OMNAME,
2952  giraffe_wlsolution_name(solution));
2953  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMNAME,
2954  "Optical model name");
2955 
2956  model = giraffe_wlsolution_model(solution);
2957 
2958  sign = giraffe_model_get_parameter(model,"Orientation") < 0 ? -1 : 1;
2959  cpl_propertylist_update_int(properties, GIALIAS_WSOL_OMDIR, sign);
2960  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMDIR,
2961  "Optical model orientation");
2962 
2963  value = giraffe_model_get_parameter(model, "FocalLength");
2964  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMFCOLL, value);
2965  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMFCOLL,
2966  "Optical model focal length");
2967 
2968  value = giraffe_model_get_parameter(model, "Magnification");
2969  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMGCAM, value);
2970  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMGCAM,
2971  "Optical model camera factor");
2972 
2973  value = giraffe_model_get_parameter(model, "Angle");
2974  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMGTHETA, value);
2975  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMGTHETA,
2976  "Optical model grating angle");
2977 
2978  if (strcmp(giraffe_wlsolution_name(solution), "xoptmod2") == 0) {
2979 
2980  value = giraffe_model_get_parameter(model, "Sdx");
2981  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSDX, value);
2982  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSDX,
2983  "Optical model slit x-offset");
2984 
2985  value = giraffe_model_get_parameter(model, "Sdy");
2986  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSDY, value);
2987  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSDY,
2988  "Optical model slit y-offset");
2989 
2990  value = giraffe_model_get_parameter(model, "Sphi");
2991  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSPHI, value);
2992  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSPHI,
2993  "Optical model slit rotation");
2994 
2995  }
2996 
2997 
2998  /*
2999  * Add the optical model residuals fit setup parameters
3000  */
3001 
3002 
3003  residuals = giraffe_wlsolution_get_residuals(solution);
3004 
3005  cpl_propertylist_update_bool(properties, GIALIAS_WSOL_SUBSLITS,
3006  giraffe_wlsolution_get_subslits(solution));
3007  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_SUBSLITS,
3008  "Subslit fit flag");
3009 
3010 
3011  cpl_propertylist_update_int(properties, GIALIAS_WSOL_XRSSN,
3012  giraffe_wlresiduals_get_size(residuals));
3013  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRSSN,
3014  "Number of subslits");
3015 
3016 
3017  cx_string_sprintf(s, "%d:%d", config->xws_xorder, config->xws_yorder);
3018 
3019  cpl_propertylist_update_string(properties, GIALIAS_WSOL_XRORDER,
3020  cx_string_get(s));
3021  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRORDER,
3022  "Residual fit polynomial order");
3023 
3024 
3025  cpl_propertylist_update_double(properties, GIALIAS_WSOL_XRSIGMA,
3026  config->xws_cliplevel);
3027  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRSIGMA,
3028  "Residual fit sigma clipping level");
3029 
3030  cpl_propertylist_update_int(properties, GIALIAS_WSOL_XRITER,
3031  config->xws_clipniter);
3032  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRITER,
3033  "Residual fit maximum number of "
3034  "iterations");
3035 
3036  cpl_propertylist_update_double(properties, GIALIAS_WSOL_XRFRAC,
3037  config->xws_clipmfrac);
3038  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRFRAC,
3039  "Residual fit minimum fraction of "
3040  "accepted points");
3041 
3042  cpl_propertylist_update_int(properties, GIALIAS_WSOL_NLINES,
3043  info->nlines);
3044  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NLINES,
3045  "Number of calibration lines used.");
3046 
3047  cpl_propertylist_update_int(properties, GIALIAS_WSOL_NACCEPT,
3048  info->ngood);
3049  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NACCEPT,
3050  "Number of accepted lines");
3051 
3052  cpl_propertylist_update_int(properties, GIALIAS_WSOL_NREJECT,
3053  info->nreject);
3054  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NREJECT,
3055  "Number of rejected lines");
3056 
3057  cpl_propertylist_update_double(properties, GIALIAS_WSOL_RMS,
3058  info->rms);
3059  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_RMS,
3060  "Average RMS [pxl] of fitted line "
3061  "positions");
3062 
3063 
3064  /*
3065  * Compute approximated pixel to wavelength scale factor
3066  * at the slit center.
3067  */
3068 
3069  xccd[0] = giraffe_wlsolution_compute_pixel(solution, setup->wlenmin,
3070  0., 0., &status);
3071  xccd[1] = giraffe_wlsolution_compute_pixel(solution, setup->wlenmax,
3072  0., 0., &status);
3073 
3074  scale = (setup->wlenmax - setup->wlenmin) / (xccd[1] - xccd[0]);
3075 
3076 
3077  cpl_propertylist_update_double(properties, GIALIAS_WSOL_WLMIN,
3078  setup->wlenmin);
3079  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_WLMIN,
3080  "Wavelength solution minimum wavelength");
3081 
3082  cpl_propertylist_update_double(properties, GIALIAS_WSOL_WLMAX,
3083  setup->wlenmax);
3084  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_WLMAX,
3085  "Wavelength solution maximum wavelength");
3086 
3087  cpl_propertylist_update_double(properties, GIALIAS_WSOL_SCALE, scale);
3088  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_SCALE,
3089  "Approximate wavelength scale [nm/pxl]");
3090 
3091 
3092  cx_string_delete(s);
3093  s = NULL;
3094 
3095 
3096  /*
3097  * Consistency check of optical model residuals fit coefficients.
3098  */
3099 
3100  for (i = 0; (cxsize)i < giraffe_wlresiduals_get_size(residuals); i++) {
3101 
3102  const GiChebyshev2D *fit = giraffe_wlresiduals_get(residuals, i);
3103 
3104 
3105  if (fit != NULL) {
3106 
3107  cxint xorder = 0;
3108  cxint yorder = 0;
3109 
3110 
3111  giraffe_chebyshev2d_get_order(fit, &xorder, &yorder);
3112 
3113  if (xorder != config->xws_xorder || yorder != config->xws_yorder) {
3114 
3115  gi_error("Invalid wavelength solution. Inconsistent residual "
3116  "fit polynomial order!");
3117 
3118  }
3119 
3120  }
3121 
3122  }
3123 
3124 
3125  /*
3126  * Write optical model residuals fit coefficients.
3127  */
3128 
3129  coeffs = giraffe_wlresiduals_table(residuals);
3130 
3131  if (coeffs == NULL) {
3132  cpl_propertylist_delete(properties);
3133  return 1;
3134  }
3135 
3136  giraffe_table_set_properties(result, properties);
3137  cpl_propertylist_delete(properties);
3138  properties = NULL;
3139 
3140  giraffe_table_set(result, coeffs);
3141 
3142  cpl_table_delete(coeffs);
3143  coeffs = NULL;
3144 
3145  return 0;
3146 
3147 }
3148 
3149 
3150 GiWCalData *
3151 giraffe_wcaldata_new(void)
3152 {
3153 
3154  GiWCalData *self = cx_calloc(1, sizeof *self);
3155 
3156  self->coeffs = NULL;
3157  self->lines = NULL;
3158  self->linedata = NULL;
3159 
3160  return self;
3161 
3162 }
3163 
3164 
3165 void
3166 giraffe_wcaldata_delete(GiWCalData *self)
3167 {
3168 
3169  if (self) {
3170 
3171  if (self->coeffs) {
3172  giraffe_table_delete(self->coeffs);
3173  self->coeffs = NULL;
3174  }
3175 
3176  if (self->lines) {
3177  giraffe_table_delete(self->lines);
3178  self->lines = NULL;
3179  }
3180 
3181  if (self->linedata) {
3182  giraffe_linedata_delete(self->linedata);
3183  self->linedata = NULL;
3184  }
3185 
3186  cx_free(self);
3187 
3188  }
3189 
3190  return;
3191 
3192 }
3193 
3194 
3216 cxint
3217 giraffe_calibrate_wavelength(GiWCalData *result, GiExtraction *extraction,
3218  GiLocalization *localization, GiTable *fibers,
3219  GiTable *slitgeometry, GiTable *grating,
3220  GiTable *lines, GiTable *initial,
3221  GiWCalConfig *config)
3222 {
3223 
3224  const cxchar *fctid = "giraffe_calibrate_wavelength";
3225 
3226 
3227  cxbool residuals = FALSE;
3228 
3229  cxint i;
3230  cxint status = 0;
3231  cxint nlines = 0;
3232  cxint width = 0;
3233 
3234  cxdouble rms = 0.;
3235 
3236  cpl_image *psf_fit = NULL;
3237  cpl_image *xws_fit = NULL;
3238 
3239  GiTable *tsolution = NULL;
3240 
3241  GiGrating *setup = NULL;
3242 
3243  GiSCFitParams psf_setup;
3244  GiSCFitParams xws_setup;
3245 
3246  GiLineParams *line_setup = NULL;
3247 
3248  GiLineData *line_data = NULL;
3249 
3250  GiWlSolution *solution = NULL;
3251 
3252  GiWCalInfo info;
3253 
3254 
3255  if (extraction == NULL) {
3256  return 1;
3257  }
3258 
3259  if (extraction->spectra == NULL || extraction->error == NULL) {
3260  return 1;
3261  }
3262 
3263 
3264  if (fibers == NULL) {
3265  return 1;
3266  }
3267 
3268  if (slitgeometry == NULL) {
3269  return 1;
3270  }
3271 
3272  if (grating == NULL) {
3273  return 1;
3274  }
3275 
3276  if (lines == NULL) {
3277  return 1;
3278  }
3279 
3280 
3281  /*
3282  * Setup grating data
3283  */
3284 
3285  setup = giraffe_grating_create(extraction->spectra, grating);
3286 
3287  if (setup == NULL) {
3288  cpl_msg_error(fctid, "Cannot initialize grating setup parameters!");
3289  return 2;
3290  }
3291 
3292  if (config->slit_position != 0) {
3293 
3294  if (config->slit_position & SLIT_DX) {
3295  setup->sdx = config->slit_dx;
3296  }
3297 
3298  if (config->slit_position & SLIT_DY) {
3299  setup->sdy = config->slit_dy;
3300  }
3301 
3302  if (config->slit_position & SLIT_PHI) {
3303  setup->sphi = config->slit_phi;
3304  }
3305 
3306  cpl_msg_info(fctid, "Setting initial slit offsets: x-shift = %.9f, "
3307  "y-shift = %.9f, rotation = %.9f", setup->sdx,
3308  setup->sdy, setup->sphi);
3309 
3310  }
3311 
3312 
3313  /*
3314  * Setup line fitting parameters
3315  */
3316 
3317  /* FIXME: The following assumes a fixed line type. If this should ever
3318  * be configurable (requires other than ThArNe line catalogs)
3319  * this has to be set correctly. (RP)
3320  */
3321 
3322  line_setup = _giraffe_lineparams_create(GI_LINETYPE_THARNE, setup,
3323  config);
3324 
3325  if (line_setup == NULL) {
3326  cpl_msg_error(fctid, "Cannot initialize line fit setup parameters!");
3327 
3328  giraffe_grating_delete(setup);
3329 
3330  return 3;
3331  }
3332 
3333 
3334  /*
3335  * Setup initial wavelength solution
3336  */
3337 
3338  if (initial == NULL) {
3339 
3340  cxint npixel = 0;
3341  cxdouble pixelsize = 0.;
3342 
3343  cpl_propertylist *properties = NULL;
3344 
3345  cpl_image *spectra = NULL;
3346 
3347 
3348  properties = giraffe_image_get_properties(extraction->spectra);
3349  cx_assert(properties != NULL);
3350 
3351  spectra = giraffe_image_get(extraction->spectra);
3352  cx_assert(spectra != NULL);
3353 
3354  pixelsize = cpl_propertylist_get_double(properties, GIALIAS_PIXSIZY);
3355  npixel = cpl_image_get_size_y(spectra);
3356 
3357  solution = giraffe_wlsolution_new(config->opt_model,
3358  config->opt_direction, npixel,
3359  pixelsize, setup);
3360 
3361  if (solution == NULL) {
3362  cpl_msg_error(fctid, "Cannot initialize initial wavelength "
3363  "solution!");
3364 
3365  giraffe_grating_delete(setup);
3366 
3367  _giraffe_lineparams_delete(line_setup);
3368 
3369  return 4;
3370  }
3371 
3372  }
3373  else {
3374 
3375  const cpl_propertylist* _properties =
3377 
3378 
3379  /*
3380  * Use a previous wavelength solution as the initial solution.
3381  */
3382 
3383  solution = giraffe_wlsolution_create(initial, extraction->spectra,
3384  setup);
3385 
3386  /*
3387  * Set the grating wavelength range to the minimum and maximum
3388  * values of the initial solution, if they are present.
3389  *
3390  * These values will then be used as the range for the line
3391  * selection.
3392  */
3393 
3394  if (cpl_propertylist_has(_properties, GIALIAS_WSOL_WLMIN) == TRUE) {
3395 
3396  setup->wlenmin = cpl_propertylist_get_double(_properties,
3397  GIALIAS_WSOL_WLMIN);
3398 
3399  cpl_msg_debug(fctid, "Using minimum wavelength %.2f from "
3400  "initial solution", setup->wlenmin);
3401 
3402  }
3403 
3404  if (cpl_propertylist_has(_properties, GIALIAS_WSOL_WLMAX) == TRUE) {
3405 
3406  setup->wlenmax = cpl_propertylist_get_double(_properties,
3407  GIALIAS_WSOL_WLMAX);
3408 
3409  cpl_msg_debug(fctid, "Using maximum wavelength %.2f from "
3410  "initial solution", setup->wlenmax);
3411 
3412  }
3413 
3414  }
3415 
3416  giraffe_wlsolution_set_subslits(solution, config->opt_subslits);
3417 
3418 
3419  cpl_msg_info(fctid, "Computing line positions on the CCD using "
3420  "model `%s'", giraffe_wlsolution_name(solution));
3421 
3422 
3423  /*
3424  * Check whether optical model residuals should be used.
3425  */
3426 
3427  if (strcmp(config->line_residuals, "enable") == 0) {
3428 
3429  if (giraffe_wlsolution_get_residuals(solution) == NULL) {
3430 
3431  cpl_msg_error(fctid, "Initial wavelength solution does not "
3432  "provide optical model residuals!");
3433 
3434  giraffe_grating_delete(setup);
3435 
3436  _giraffe_lineparams_delete(line_setup);
3437 
3438  giraffe_wlsolution_delete(solution);
3439 
3440  return 5;
3441  }
3442  else {
3443 
3444  residuals = TRUE;
3445 
3446  }
3447  }
3448  else if (strcmp(config->line_residuals, "auto") == 0) {
3449 
3450  residuals = giraffe_wlsolution_get_residuals(solution) != NULL;
3451 
3452  if (residuals == TRUE) {
3453  cpl_msg_info(fctid, "Using wavelength solution residuals when "
3454  "computing line positions.");
3455  }
3456 
3457  }
3458  else {
3459 
3460  residuals = FALSE;
3461 
3462  }
3463 
3464 
3465  /*
3466  * Basic selection of lines from the input line list
3467  */
3468 
3469  status = _giraffe_linelist_setup(lines, setup, config);
3470 
3471  if (status) {
3472  cpl_msg_error(fctid, "Line list creation failed!");
3473 
3474  giraffe_grating_delete(setup);
3475 
3476  _giraffe_lineparams_delete(line_setup);
3477 
3478  giraffe_wlsolution_delete(solution);
3479 
3480  return 6;
3481  }
3482 
3483  nlines = cpl_table_get_nrow(giraffe_table_get(lines));
3484  cpl_msg_info(fctid, "%d lines have been selected from the line list.",
3485  nlines);
3486 
3487 
3488  /*
3489  * Line fitting loop.
3490  */
3491 
3492  line_data = giraffe_linedata_new();
3493 
3494  for (i = 0; i < config->line_nwidths; i++) {
3495 
3496  cxint _nlines = 0;
3497  cxint _nfibers = 0;
3498  cxint _nreject = 0;
3499  cxint _ngood = 0;
3500 
3501  cpl_table *_lines = NULL;
3502  cpl_table *_fibers = giraffe_table_get(fibers);
3503 
3504  cpl_image *xccd = NULL;
3505  cpl_image *xres = NULL;
3506  cpl_image *line_status = NULL;
3507 
3508  GiWlResiduals *xws_coeffs = NULL;
3509 
3510 
3511 
3512  width = config->line_widths[i];
3513 
3514  cpl_msg_info(fctid, "Current search window width: %d pxl", width);
3515  cpl_msg_info(fctid, "Applying crowding criterium to line list.");
3516 
3517  _lines = _giraffe_linelist_select(lines, extraction->spectra, setup,
3518  width, config);
3519  if (_lines == NULL) {
3520  cpl_msg_error(fctid, "Removing crowded lines from line list "
3521  "for search window width %d pxl failed!", width);
3522 
3523  giraffe_grating_delete(setup);
3524 
3525  _giraffe_lineparams_delete(line_setup);
3526 
3527  giraffe_wlsolution_delete(solution);
3528 
3529  return 7;
3530  }
3531 
3532  _nlines = cpl_table_get_nrow(_lines);
3533 
3534 #if 0
3535  cpl_msg_info(fctid, "%d lines used for fit. %d of %d lines rejected "
3536  "due to crowding.", _nlines, nlines - _nlines, nlines);
3537 #else
3538  cpl_msg_info(fctid, "%d lines used for fit. %d of %d lines "
3539  "rejected due to crowding and line quality.",
3540  _nlines, nlines - _nlines, nlines);
3541 #endif
3542 
3543  /*
3544  * Compute line position on the CCD along the dispersion axis
3545  * from the current optical model, and optionally the residuals.
3546  */
3547 
3548  xccd = _giraffe_line_abscissa(_lines, slitgeometry, fibers,
3549  solution, localization, residuals);
3550 
3551  _nfibers = cpl_image_get_size_x(xccd);
3552  _nlines = cpl_image_get_size_y(xccd);
3553 
3554 
3555  /*
3556  * Fit a model to individual lines and reject the `bad' ones.
3557  */
3558 
3559  cpl_msg_info(fctid, "Fitting %d line profiles for %d spectra using "
3560  "line model `%s'", _nlines, _nfibers , line_setup->model);
3561 
3562  cpl_msg_info(fctid, "Total number of lines to fit: %d (%d x %d)",
3563  _nlines * _nfibers, _nfibers, _nlines);
3564 
3565 
3566  status = giraffe_linedata_reset(line_data, _lines, _fibers,
3567  line_setup->model);
3568 
3569  if (status != 0) {
3570 
3571  cpl_msg_error(fctid, "Line profile fit failed!");
3572 
3573  cpl_image_delete(xccd);
3574 
3575  cpl_table_delete(_lines);
3576  giraffe_linedata_delete(line_data);
3577 
3578  giraffe_grating_delete(setup);
3579 
3580  _giraffe_lineparams_delete(line_setup);
3581 
3582  giraffe_wlsolution_delete(solution);
3583 
3584  return 8;
3585 
3586  }
3587 
3588  status = _giraffe_line_fit(line_data, xccd, width, extraction, fibers,
3589  localization->locy, line_setup);
3590 
3591  if (status != 0) {
3592 
3593  cpl_msg_error(fctid, "Line profile fit failed!");
3594 
3595  cpl_image_delete(xccd);
3596 
3597  cpl_table_delete(_lines);
3598  giraffe_linedata_delete(line_data);
3599 
3600  giraffe_grating_delete(setup);
3601 
3602  _giraffe_lineparams_delete(line_setup);
3603 
3604  giraffe_wlsolution_delete(solution);
3605 
3606  return 8;
3607 
3608  }
3609 
3610  cpl_image_delete(xccd);
3611  xccd = NULL;
3612 
3613  if (giraffe_linedata_accepted(line_data) == 0) {
3614  cpl_msg_error(fctid, "No lines left after line profile fit!");
3615 
3616  cpl_table_delete(_lines);
3617  giraffe_linedata_delete(line_data);
3618 
3619  giraffe_grating_delete(setup);
3620 
3621  _giraffe_lineparams_delete(line_setup);
3622 
3623  giraffe_wlsolution_delete(solution);
3624 
3625  return 9;
3626 
3627  }
3628 
3629  _nreject = giraffe_linedata_rejected(line_data);
3630  _ngood = giraffe_linedata_accepted(line_data);
3631 
3632  cpl_msg_info(fctid, "Number of good lines: %d. %d of %d lines "
3633  "rejected due to line profile fit.", _ngood, _nreject,
3634  _nlines *_nfibers);
3635 
3636  /*
3637  * Save line fit status for final statistics
3638  */
3639 
3640  line_status = giraffe_linedata_status(line_data);
3641 
3642 
3643  /*
3644  * Fit the lines PSF width variation over the CCD
3645  */
3646 
3647  cpl_msg_info(fctid, "Fit of the line profile PSF width variation.");
3648 
3649  psf_setup.subslits = giraffe_wlsolution_get_subslits(solution);
3650  psf_setup.fit.xorder = config->pxw_xorder;
3651  psf_setup.fit.yorder = config->pxw_yorder;
3652 
3653  cpl_msg_info(fctid, "Chebyshev polynomial order is (%d, %d).",
3654  psf_setup.fit.xorder, psf_setup.fit.yorder);
3655 
3656  psf_setup.clip.iterations = config->pxw_clipniter;
3657  psf_setup.clip.level = config->pxw_cliplevel;
3658  psf_setup.clip.fraction = config->pxw_clipmfrac;
3659 
3660  cpl_msg_info(fctid, "Sigma clipping: iterations = %d, level = %.4f, "
3661  "fraction = %.4f", psf_setup.clip.iterations,
3662  psf_setup.clip.level, psf_setup.clip.fraction);
3663 
3664 
3665  psf_fit = _giraffe_psf_fit(line_data, localization, fibers,
3666  slitgeometry, &psf_setup);
3667 
3668 
3669  if (psf_fit == NULL) {
3670 
3671  cpl_msg_error(fctid, "Fit of the line profile PSF width "
3672  "variation failed!");
3673 
3674  cpl_table_delete(_lines);
3675  cpl_image_delete(line_status);
3676  giraffe_linedata_delete(line_data);
3677 
3678  giraffe_grating_delete(setup);
3679 
3680  _giraffe_lineparams_delete(line_setup);
3681 
3682  giraffe_wlsolution_delete(solution);
3683 
3684  return 10;
3685 
3686  }
3687  else {
3688 
3689  /* FIXME: Not used, debugging only. Maybe it should go away
3690  * completely (RP)
3691  */
3692 
3693  cpl_image_delete(psf_fit);
3694 
3695  }
3696 
3697  if (giraffe_linedata_accepted(line_data) == 0) {
3698  cpl_msg_error(fctid, "No lines left after line profile PSF "
3699  "width fit!");
3700 
3701  cpl_table_delete(_lines);
3702  cpl_image_delete(line_status);
3703  giraffe_linedata_delete(line_data);
3704 
3705  giraffe_grating_delete(setup);
3706 
3707  _giraffe_lineparams_delete(line_setup);
3708 
3709  giraffe_wlsolution_delete(solution);
3710 
3711  return 10;
3712 
3713  }
3714 
3715  cpl_msg_info(fctid, "Number of good lines: %"
3716  CX_PRINTF_FORMAT_SIZE_TYPE ". %"
3717  CX_PRINTF_FORMAT_SIZE_TYPE " of %d lines "
3718  "rejected due to line profile PSF width fit.",
3719  giraffe_linedata_accepted(line_data),
3720  giraffe_linedata_rejected(line_data) - _nreject, _ngood);
3721 
3722  _ngood = giraffe_linedata_accepted(line_data);
3723  _nreject = giraffe_linedata_rejected(line_data);
3724 
3725 
3726  /*
3727  * If requested, fit of a new optical model using the current
3728  * selection of good lines.
3729  */
3730 
3731  if (config->opt_solution == TRUE) {
3732 
3733  cxint iterations = 0;
3734  cxint df = 0;
3735 
3736  cxdouble chisq = 0.;
3737  cxdouble rsquare = 0.;
3738 
3739  cx_string *s = NULL;
3740 
3741  GiOpticalModelParams om_setup;
3742 
3743  GiModel *guess = NULL;
3744  GiModel *model = giraffe_wlsolution_model(solution);
3745 
3746 
3747  /*
3748  * Save initial guess model
3749  */
3750 
3751  guess = giraffe_model_clone(model);
3752 
3753  om_setup.fit.iterations = config->opt_niter;
3754  om_setup.fit.tests = config->opt_ntest;
3755  om_setup.fit.delta = config->opt_dchisq;
3756  om_setup.flags = config->opt_flags;
3757 
3758  cpl_msg_info(fctid, "Optical model fit setup: iterations = %d, "
3759  "tests = %d, delta = %.4f", om_setup.fit.iterations,
3760  om_setup.fit.tests, om_setup.fit.delta);
3761 
3762  status = _giraffe_opticalmodel_fit(solution, line_data, fibers,
3763  slitgeometry, &om_setup);
3764 
3765  if (status != 0) {
3766  cpl_msg_error(fctid, "Optical model fit failed!");
3767 
3768  giraffe_model_delete(guess);
3769 
3770  cpl_table_delete(_lines);
3771  cpl_image_delete(line_status);
3772  giraffe_linedata_delete(line_data);
3773 
3774  giraffe_grating_delete(setup);
3775 
3776  _giraffe_lineparams_delete(line_setup);
3777 
3778  giraffe_wlsolution_delete(solution);
3779 
3780  return 11;
3781 
3782  }
3783 
3784 
3785  /*
3786  * Output of fit results
3787  */
3788 
3789  cpl_msg_info(fctid, "Optical model parameters:");
3790 
3791  s = cx_string_new();
3792 
3793  _giraffe_opticalmodel_format(s, guess, GI_OPTM_PARAMETER_VALUES);
3794  cpl_msg_info(fctid, "Initial: %s", cx_string_get(s));
3795 
3796  _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_VALUES);
3797  cpl_msg_info(fctid, " Fitted: %s", cx_string_get(s));
3798 
3799  _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_ERRORS);
3800  cpl_msg_info(fctid, " Sigma: %s", cx_string_get(s));
3801 
3802  _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_STATUS);
3803  cpl_msg_info(fctid, " Status: %s", cx_string_get(s));
3804 
3805  cx_string_delete(s);
3806  s = NULL;
3807 
3808 #if 0
3809  if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
3810 
3811  cxdouble flength = 0.;
3812  cxdouble sdx = 0.;
3813  cxdouble sdy = 0.;
3814  cxdouble sphi = 0.;
3815 
3816  cx_string *s = cx_string_new();
3817 
3818 
3819  flength = giraffe_model_get_parameter(guess, "FocalLength");
3820  sdx = giraffe_model_get_parameter(guess, "Sdx");
3821  sdy = giraffe_model_get_parameter(guess, "Sdy");
3822  sphi = giraffe_model_get_parameter(guess, "Sphi");
3823 
3824  cx_string_sprintf(s, "Initial: focal length = %.6f, slit "
3825  "x-shift = %.9f, slit y-shift = %.9f, "
3826  "slit rotation = %.9f", flength, sdx, sdy,
3827  sphi);
3828  cpl_msg_info(fctid, "%s", cx_string_get(s));
3829 
3830 
3831  flength = giraffe_model_get_parameter(model, "FocalLength");
3832  sdx = giraffe_model_get_parameter(model, "Sdx");
3833  sdy = giraffe_model_get_parameter(model, "Sdy");
3834  sphi = giraffe_model_get_parameter(model, "Sphi");
3835 
3836  cx_string_sprintf(s, " Fitted: focal length = %.6f, slit "
3837  "x-shift = %.9f, slit y-shift = %.9f, "
3838  "slit rotation = %.9f", flength, sdx, sdy,
3839  sphi);
3840  cpl_msg_info(fctid, "%s", cx_string_get(s));
3841 
3842 
3843  flength = giraffe_model_get_sigma(model, "FocalLength");
3844  sdx = giraffe_model_get_sigma(model, "Sdx");
3845  sdy = giraffe_model_get_sigma(model, "Sdy");
3846  sphi = giraffe_model_get_sigma(model, "Sphi");
3847 
3848  cx_string_sprintf(s, " Sigma: focal length = %.6f, slit "
3849  "x-shift = %.9f, slit y-shift = %.9f, "
3850  "slit rotation = %.9f", flength, sdx, sdy,
3851  sphi);
3852  cpl_msg_info(fctid, "%s", cx_string_get(s));
3853 
3854  cx_string_delete(s);
3855 
3856  }
3857  else {
3858 
3859  cxdouble flength = 0.;
3860 
3861  cx_string *s = cx_string_new();
3862 
3863 
3864  flength = giraffe_model_get_parameter(guess, "FocalLength");
3865 
3866  cx_string_sprintf(s, "Initial: focal length = %.6f", flength);
3867  cpl_msg_info(fctid, "%s", cx_string_get(s));
3868 
3869 
3870  flength = giraffe_model_get_parameter(model, "FocalLength");
3871 
3872  cx_string_sprintf(s, " Fitted: focal length = %.6f", flength);
3873  cpl_msg_info(fctid, "%s", cx_string_get(s));
3874 
3875 
3876  flength = giraffe_model_get_sigma(model, "FocalLength");
3877 
3878  cx_string_sprintf(s, " Sigma: focal length = %.6f", flength);
3879  cpl_msg_info(fctid, "%s", cx_string_get(s));
3880 
3881  cx_string_delete(s);
3882 
3883  }
3884 #endif
3885 
3886  giraffe_model_delete(guess);
3887 
3888 
3889  iterations = giraffe_model_get_position(model);
3890  df = giraffe_model_get_df(model);
3891 
3892  chisq = giraffe_model_get_chisq(model);
3893  rsquare = giraffe_model_get_rsquare(model);
3894 
3895  cpl_msg_info(fctid, "Optical model fit statistics: iterations = "
3896  "%d, DoF = %d, Chi-square = %.6g, Chi-square/DoF = "
3897  "%.6g, R-square = %.6g", iterations, df, chisq,
3898  chisq / df, rsquare);
3899 
3900 
3901  /*
3902  * Update grating data structure with fitted parameters
3903  * for next iteration.
3904  */
3905 
3906  setup->fcoll = giraffe_model_get_parameter(model, "FocalLength");
3907  setup->gcam = giraffe_model_get_parameter(model, "Magnification");
3908  setup->theta = giraffe_model_get_parameter(model, "Angle");
3909  setup->order = giraffe_model_get_parameter(model, "Order");
3910  setup->space = giraffe_model_get_parameter(model, "Spacing");
3911 
3912  if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
3913  setup->sdx = giraffe_model_get_parameter(model, "Sdx");
3914  setup->sdy = giraffe_model_get_parameter(model, "Sdy");
3915  setup->sphi = giraffe_model_get_parameter(model, "Sphi");
3916  }
3917 
3918 
3919  /*
3920  * Re-compute line positions using the updated optical model
3921  * and update the line data object accordingly. The residuals,
3922  * which belong to the previous optical model must not be used
3923  * here.
3924  */
3925 
3926  /* FIXME: Check why the OGL code computes the y positions of
3927  * the lines, but does not use or store them. (RP)
3928  */
3929 
3930  cpl_msg_info(fctid, "Re-computing line positions with updated "
3931  "optical model");
3932 
3933  xccd = _giraffe_line_abscissa(_lines, slitgeometry, fibers,
3934  solution, localization, FALSE);
3935 
3936  giraffe_linedata_set_data(line_data, "Xccd", xccd);
3937  xccd = NULL;
3938 
3939  }
3940  else {
3941 
3942  cx_string *s = cx_string_new();
3943 
3944  GiModel *model = giraffe_wlsolution_model(solution);
3945 
3946 
3947  _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_VALUES);
3948  cpl_msg_info(fctid, "Optical model: %s", cx_string_get(s));
3949 
3950  cx_string_delete(s);
3951  s = NULL;
3952 
3953  }
3954 
3955  rms = _giraffe_compute_statistics(line_data, NULL, NULL);
3956 
3957  cpl_msg_info(fctid, "Average RMS [pxl] of line positions using "
3958  "%d of %d lines: %.4f", _ngood, _nlines * _nfibers,
3959  rms);
3960 
3961 
3962  /*
3963  * Fit the wavelength solution coefficients, i.e. the Chebyshev
3964  * correction computed from fitting the optical model residuals.
3965  */
3966 
3967  cpl_msg_info(fctid, "Fit of the wavelength solution coefficients "
3968  "using %" CX_PRINTF_FORMAT_SIZE_TYPE " lines",
3969  giraffe_linedata_accepted(line_data));
3970 
3971  xws_setup.subslits = giraffe_wlsolution_get_subslits(solution);
3972  xws_setup.fit.xorder = config->xws_xorder;
3973  xws_setup.fit.yorder = config->xws_yorder;
3974 
3975  cpl_msg_info(fctid, "Chebyshev polynomial order is (%d, %d).",
3976  xws_setup.fit.xorder, xws_setup.fit.yorder);
3977 
3978  xws_setup.clip.iterations = config->xws_clipniter;
3979  xws_setup.clip.level = config->xws_cliplevel;
3980  xws_setup.clip.fraction = config->xws_clipmfrac;
3981 
3982  cpl_msg_info(fctid, "Sigma clipping: iterations = %d, level = %.4f, "
3983  "fraction = %.4f", xws_setup.clip.iterations,
3984  xws_setup.clip.level, xws_setup.clip.fraction);
3985 
3986  xws_coeffs = giraffe_wlresiduals_new();
3987 
3988  xws_fit = _giraffe_residuals_fit(xws_coeffs, line_data, localization,
3989  fibers, slitgeometry, &xws_setup);
3990 
3991 
3992  if (xws_fit == NULL) {
3993 
3994  cpl_msg_error(fctid, "Fit of the wavelength solution "
3995  "coefficients failed!");
3996 
3997  giraffe_wlresiduals_delete(xws_coeffs);
3998 
3999  cpl_table_delete(_lines);
4000  cpl_image_delete(line_status);
4001  giraffe_linedata_delete(line_data);
4002 
4003  giraffe_grating_delete(setup);
4004 
4005  _giraffe_lineparams_delete(line_setup);
4006 
4007  giraffe_wlsolution_delete(solution);
4008 
4009  return 12;
4010 
4011  }
4012 
4013 
4014  /*
4015  * Update the line data object with the fitted residuals of
4016  * the lines used.
4017  */
4018 
4019  xres = cpl_image_new(giraffe_linedata_fibers(line_data),
4020  giraffe_linedata_lines(line_data),
4021  CPL_TYPE_DOUBLE);
4022 
4023  _giraffe_get_residuals(xres,
4024  giraffe_linedata_get_data(line_data, "Xccd"),
4025  xws_fit);
4026  giraffe_linedata_set_data(line_data, "Xres", xres);
4027 
4028 
4029  if (giraffe_linedata_accepted(line_data) == 0) {
4030  cpl_msg_error(fctid, "No lines left after wavelength solution "
4031  "coefficients fit!");
4032 
4033  giraffe_wlresiduals_delete(xws_coeffs);
4034 
4035  cpl_table_delete(_lines);
4036  cpl_image_delete(line_status);
4037  cpl_image_delete(xws_fit);
4038  giraffe_linedata_delete(line_data);
4039 
4040  giraffe_grating_delete(setup);
4041 
4042  _giraffe_lineparams_delete(line_setup);
4043 
4044  giraffe_wlsolution_delete(solution);
4045 
4046  return 12;
4047 
4048  }
4049 
4050  cpl_msg_info(fctid, "Number of good lines: %"
4051  CX_PRINTF_FORMAT_SIZE_TYPE". %"
4052  CX_PRINTF_FORMAT_SIZE_TYPE" of %d lines "
4053  "rejected due to wavelength solution coefficients fit.",
4054  giraffe_linedata_accepted(line_data),
4055  giraffe_linedata_rejected(line_data) - _nreject, _ngood);
4056 
4057  _ngood = giraffe_linedata_accepted(line_data);
4058  _nreject = giraffe_linedata_rejected(line_data);
4059 
4060 
4061  /*
4062  * Update the wavelength solution with the computed residuals.
4063  */
4064 
4065  giraffe_wlsolution_set_residuals(solution, xws_coeffs);
4066  xws_coeffs = NULL;
4067 
4068 
4069  /*
4070  * Compute RMS over all line positions as computed by the optical
4071  * model corrected for the fitted residuals and the measured
4072  * positions as derived from the fit of the line profiles.
4073  */
4074 
4075  rms = _giraffe_compute_statistics(line_data, xws_fit, line_status);
4076 
4077  cpl_msg_info(fctid, "Average RMS [pxl] of line positions using "
4078  "%d of %d lines: %.4f", _ngood, _nlines * _nfibers,
4079  rms);
4080 
4081 
4082  /*
4083  * Update wavelength calibration information structure
4084  */
4085 
4086  info.width = width;
4087 
4088  info.residuals = residuals;
4089 
4090  info.nlines = _nlines;
4091  info.nfibers = _nfibers;
4092 
4093  info.ngood = _ngood;
4094  info.nreject = _nreject;
4095 
4096  info.rms = rms;
4097 
4098 
4099  /*
4100  * Cleanup data created during this iteration step.
4101  */
4102 
4103  cpl_image_delete(xws_fit);
4104 
4105  cpl_table_delete(_lines);
4106  cpl_image_delete(line_status);
4107 
4108  }
4109 
4110 
4111  /*
4112  * Write the final wavelength solution to the output table. The table
4113  * properties are created from the extracted arc-spectra frame.
4114  */
4115 
4116  tsolution = giraffe_table_new();
4117 
4118  status = _giraffe_convert_wlsolution(tsolution, solution,
4119  extraction->spectra, setup,
4120  &info, config);
4121 
4122  if (status != 0) {
4123 
4124  giraffe_table_delete(tsolution);
4125 
4126  giraffe_grating_delete(setup);
4127 
4128  _giraffe_lineparams_delete(line_setup);
4129 
4130  giraffe_wlsolution_delete(solution);
4131 
4132  return 13;
4133 
4134  }
4135 
4136 
4137  /*
4138  * Fill the results structure.
4139  */
4140 
4141  result->coeffs = tsolution;
4142  tsolution = NULL;
4143 
4144  result->linedata = line_data;
4145 
4146 
4147  /*
4148  * Final cleanup
4149  */
4150 
4151  giraffe_grating_delete(setup);
4152  giraffe_wlsolution_delete(solution);
4153 
4154  _giraffe_lineparams_delete(line_setup);
4155 
4156  return 0;
4157 
4158 }
4159 
4160 
4171 GiWCalConfig *
4172 giraffe_wlcalibration_config_create(cpl_parameterlist *list)
4173 {
4174 
4175  const cxchar *s = NULL;
4176  const cpl_parameter *p = NULL;
4177 
4178  GiWCalConfig *config = NULL;
4179 
4180 
4181  if (!list) {
4182  return NULL;
4183  }
4184 
4185  config = cx_calloc(1, sizeof *config);
4186 
4187 
4188  /*
4189  * The line saturation level is currently not connected to a parameter,
4190  * but it may become one. Therefore it is put in here with a fixed value.
4191  */
4192 
4193  config->line_saturation = 1.e7;
4194 
4195 
4196  /*
4197  * Set configuration data
4198  */
4199 
4200  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.widths");
4201  s = cpl_parameter_get_string(p);
4202 
4203  if (s) {
4204 
4205  cxchar **values = cx_strsplit(s, ",", -1);
4206 
4207 
4208  if (values == NULL) {
4209 
4211  return NULL;
4212 
4213  }
4214  else {
4215 
4216  cxchar *last;
4217 
4218  cxint n = 0;
4219 
4220 
4221  while (values[n] != NULL) {
4222  ++n;
4223  }
4224 
4225  config->line_nwidths = n;
4226  config->line_widths = cx_malloc(n * sizeof(cxint));
4227  cx_assert(config->line_widths != NULL);
4228 
4229  n = 0;
4230  while (values[n] != NULL) {
4231 
4232  cxint w = strtol(values[n], &last, 10);
4233 
4234  if (*last != '\0') {
4235  cx_strfreev(values);
4237 
4238  return NULL;
4239  }
4240 
4241  config->line_widths[n] = w;
4242  ++n;
4243 
4244  }
4245 
4246  if (n > 1) {
4247  qsort(config->line_widths, config->line_nwidths,
4248  sizeof(cxint), _giraffe_window_compare);
4249  }
4250 
4251  cx_strfreev(values);
4252  values = NULL;
4253 
4254  }
4255 
4256  }
4257 
4258 
4259  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.separation");
4260  config->line_separation = fabs(cpl_parameter_get_double(p));
4261 
4262  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.fluxratio");
4263  config->line_fluxratio = cpl_parameter_get_double(p);
4264 
4265  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.brightness");
4266  config->line_brightness = cpl_parameter_get_double(p);
4267 
4268  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.count");
4269  config->line_count = cpl_parameter_get_int(p);
4270 
4271  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.wrange");
4272  s = cpl_parameter_get_string(p);
4273 
4274  if (s) {
4275 
4276  cxchar **values = cx_strsplit(s, ",", 3);
4277 
4278  if (values == NULL) {
4279 
4281  return NULL;
4282 
4283  }
4284  else {
4285 
4286  cxchar *last;
4287 
4288  cxdouble lower = 0.;
4289  cxdouble upper = 0.;
4290 
4291 
4292  lower = strtod(values[0], &last);
4293 
4294  if (*last != '\0') {
4295 
4296  cx_strfreev(values);
4298 
4299  return NULL;
4300 
4301  }
4302 
4303  lower = lower >= 0. ? lower : 0.;
4304 
4305 
4306  if (values[1] != NULL) {
4307 
4308  upper = strtod(values[1], &last);
4309 
4310  if (*last != '\0') {
4311 
4312  cx_strfreev(values);
4314 
4315  return NULL;
4316 
4317  }
4318 
4319  upper = upper > lower ? upper : 0.;
4320 
4321  }
4322 
4323  config->line_wlrange = giraffe_range_create(lower, upper);
4324  cx_assert(config->line_wlrange != NULL);
4325 
4326  }
4327 
4328  cx_strfreev(values);
4329  values = NULL;
4330 
4331  }
4332 
4333 
4334  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.model");
4335  s = cpl_parameter_get_string(p);
4336 
4337  if (strcmp(s, "psfexp") != 0 &&
4338  strcmp(s, "psfexp2") != 0 &&
4339  strcmp(s, "gaussian") != 0) {
4340 
4342  return NULL;
4343 
4344  }
4345  else {
4346  config->line_model = cx_strdup(s);
4347  }
4348 
4349 
4350  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.residuals");
4351  s = cpl_parameter_get_string(p);
4352 
4353  if (strcmp(s, "auto") != 0 &&
4354  strcmp(s, "enable") != 0 &&
4355  strcmp(s, "disable") != 0) {
4356 
4358  return NULL;
4359 
4360  }
4361  else {
4362  config->line_residuals = cx_strdup(s);
4363  }
4364 
4365 
4366  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.threshold");
4367  config->line_threshold = cpl_parameter_get_double(p);
4368 
4369  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.offset");
4370  config->line_offset = cpl_parameter_get_double(p);
4371 
4372  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.iterations");
4373  config->line_niter = cpl_parameter_get_int(p);
4374 
4375  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.tests");
4376  config->line_ntest = cpl_parameter_get_int(p);
4377 
4378  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.dchisquare");
4379  config->line_dchisq = cpl_parameter_get_double(p);
4380 
4381  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.rwidthratio");
4382  config->line_rwidthratio = cpl_parameter_get_double(p);
4383 
4384  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.exponent");
4385  config->line_widthexponent = cpl_parameter_get_double(p);
4386 
4387 
4388  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.slit.offset");
4389  s = cpl_parameter_get_string(p);
4390 
4391  cx_assert(s != NULL);
4392 
4393  if (cx_strncasecmp(s, "setup", 5) != 0) {
4394 
4395  cxchar **values = cx_strsplit(s, ",", 4);
4396 
4397 
4398  config->slit_position = 0;
4399  config->slit_dx = 0.;
4400  config->slit_dy = 0.;
4401  config->slit_phi = 0.;
4402 
4403  if (values == NULL || values[0] == NULL) {
4404 
4406  return NULL;
4407 
4408  }
4409  else {
4410 
4411  cxbool eol = FALSE;
4412 
4413  if (*values[0] != '\0') {
4414 
4415  cxchar *last;
4416  cxdouble sdx = strtod(values[0], &last);
4417 
4418  if (*last != '\0') {
4419  cx_strfreev(values);
4420  values = NULL;
4421 
4423 
4424  return NULL;
4425  }
4426 
4427  config->slit_position |= SLIT_DX;
4428  config->slit_dx = sdx;
4429 
4430  }
4431 
4432  if (values[1] == NULL) {
4433  eol = TRUE;
4434  }
4435  else {
4436 
4437  if (*values[1] != '\0') {
4438 
4439  cxchar *last;
4440  cxdouble sdy = strtod(values[1], &last);
4441 
4442  if (*last != '\0') {
4443  cx_strfreev(values);
4444  values = NULL;
4445 
4447 
4448  return NULL;
4449  }
4450 
4451  config->slit_position |= SLIT_DY;
4452  config->slit_dy = sdy;
4453 
4454  }
4455 
4456  }
4457 
4458  if (!eol && values[2] != NULL) {
4459 
4460  if (*values[2] != '\0') {
4461 
4462  cxchar *last;
4463  cxdouble sphi = strtod(values[2], &last);
4464 
4465  if (*last != '\0') {
4466  cx_strfreev(values);
4467  values = NULL;
4468 
4470 
4471  return NULL;
4472  }
4473 
4474  config->slit_position |= SLIT_PHI;
4475  config->slit_phi = sphi;
4476 
4477  }
4478 
4479  }
4480 
4481  cx_strfreev(values);
4482  values = NULL;
4483 
4484  }
4485 
4486  }
4487 
4488 
4489  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.model");
4490  s = cpl_parameter_get_string(p);
4491 
4492  if (strcmp(s, "xoptmod") != 0 && strcmp(s, "xoptmod2") != 0) {
4493 
4495  return NULL;
4496 
4497  }
4498  else {
4499  config->opt_model = cx_strdup(s);
4500  }
4501 
4502 
4503  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.direction");
4504  config->opt_direction = cpl_parameter_get_int(p);
4505 
4506  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.solution");
4507  config->opt_solution = cpl_parameter_get_bool(p);
4508 
4509  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.subslits");
4510  config->opt_subslits = cpl_parameter_get_bool(p);
4511 
4512  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.flags");
4513  s = cpl_parameter_get_string(p);
4514 
4515  if (s) {
4516 
4517  cxchar **values = cx_strsplit(s, ",", -1);
4518 
4519  if (values == NULL) {
4520 
4522  return NULL;
4523 
4524  }
4525  else {
4526 
4527  cxint i = 0;
4528  cxint n = 0;
4529 
4530  config->opt_flags = 0;
4531 
4532  while (values[i] != NULL) {
4533 
4534  if (strncmp(values[i], "fcoll", 5) == 0) {
4535  config->opt_flags |= OPTM_FLENGTH;
4536  ++n;
4537  }
4538  else if (strncmp(values[i], "gcam", 4) == 0) {
4539  config->opt_flags |= OPTM_GCAMERA;
4540  ++n;
4541  }
4542  else if (strncmp(values[i], "theta", 5) == 0) {
4543  config->opt_flags |= OPTM_THETA;
4544  ++n;
4545  }
4546  else if (strncmp(values[i], "sdx", 3) == 0) {
4547  config->opt_flags |= OPTM_SX;
4548  ++n;
4549  }
4550  else if (strncmp(values[i], "sdy", 3) == 0) {
4551  config->opt_flags |= OPTM_SY;
4552  ++n;
4553  }
4554  else if (strncmp(values[i], "sphi", 4) == 0) {
4555  config->opt_flags |= OPTM_SPHI;
4556  ++n;
4557  }
4558 
4559  ++i;
4560 
4561  }
4562 
4563  cx_strfreev(values);
4564  values = NULL;
4565 
4566 
4567  /*
4568  * Stop if no valid optical model parameter flag was seen
4569  */
4570 
4571  if (n == 0) {
4573  return NULL;
4574  }
4575 
4576  }
4577 
4578  }
4579 
4580 
4581  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.iterations");
4582  config->opt_niter = cpl_parameter_get_int(p);
4583 
4584  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.tests");
4585  config->opt_ntest = cpl_parameter_get_int(p);
4586 
4587  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.dchisquare");
4588  config->opt_dchisq = cpl_parameter_get_double(p);
4589 
4590 
4591  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.sigma");
4592  config->pxw_cliplevel = cpl_parameter_get_double(p);
4593 
4594  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.iterations");
4595  config->pxw_clipniter = cpl_parameter_get_int(p);
4596 
4597  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.fraction");
4598  config->pxw_clipmfrac = cpl_parameter_get_double(p);
4599 
4600  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.order");
4601  s = cpl_parameter_get_string(p);
4602 
4603  if (s) {
4604 
4605  cxchar **values = cx_strsplit(s, ",", 3);
4606 
4607  if (values == NULL || values[1] == NULL) {
4608 
4610  return NULL;
4611 
4612  }
4613  else {
4614 
4615  cxchar *last;
4616 
4617 
4618  config->pxw_xorder = strtol(values[0], &last, 10);
4619 
4620  if (*last != '\0') {
4621 
4622  cx_strfreev(values);
4624 
4625  return NULL;
4626 
4627  }
4628 
4629  config->pxw_yorder = strtol(values[1], &last, 10);
4630 
4631  if (*last != '\0') {
4632 
4633  cx_strfreev(values);
4635 
4636  return NULL;
4637 
4638  }
4639 
4640  }
4641 
4642  cx_strfreev(values);
4643  values = NULL;
4644 
4645  }
4646 
4647 
4648  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.sigma");
4649  config->xws_cliplevel = cpl_parameter_get_double(p);
4650 
4651  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.iterations");
4652  config->xws_clipniter = cpl_parameter_get_int(p);
4653 
4654  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.fraction");
4655  config->xws_clipmfrac = cpl_parameter_get_double(p);
4656 
4657  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.order");
4658  s = cpl_parameter_get_string(p);
4659 
4660  if (s) {
4661 
4662  cxchar **values = cx_strsplit(s, ",", 3);
4663 
4664  if (values == NULL || values[1] == NULL) {
4665 
4667  return NULL;
4668 
4669  }
4670  else {
4671 
4672  cxchar *last;
4673 
4674 
4675  config->xws_xorder = strtol(values[0], &last, 10);
4676 
4677  if (*last != '\0') {
4678 
4679  cx_strfreev(values);
4681 
4682  return NULL;
4683 
4684  }
4685 
4686  config->xws_yorder = strtol(values[1], &last, 10);
4687 
4688  if (*last != '\0') {
4689 
4690  cx_strfreev(values);
4692 
4693  return NULL;
4694 
4695  }
4696 
4697  }
4698 
4699  cx_strfreev(values);
4700  values = NULL;
4701 
4702  }
4703 
4704  return config;
4705 
4706 }
4707 
4708 
4721 void
4723 {
4724 
4725  if (config) {
4726  if (config->line_widths) {
4727  cx_free(config->line_widths);
4728  }
4729 
4730  if (config->line_wlrange) {
4732  }
4733 
4734  if (config->line_model) {
4735  cx_free(config->line_model);
4736  }
4737 
4738  if (config->line_residuals) {
4739  cx_free(config->line_residuals);
4740  }
4741 
4742  if (config->opt_model) {
4743  cx_free(config->opt_model);
4744  }
4745 
4746  cx_free(config);
4747  }
4748 
4749  return;
4750 
4751 }
4752 
4753 
4765 void
4766 giraffe_wlcalibration_config_add(cpl_parameterlist *list)
4767 {
4768 
4769  cpl_parameter *p;
4770 
4771 
4772  if (!list) {
4773  return;
4774  }
4775 
4776 
4777  /*
4778  * Line selection parameters
4779  */
4780 
4781  p = cpl_parameter_new_value("giraffe.wlcalibration.line.widths",
4782  CPL_TYPE_STRING,
4783  "List of window widths [pxl] used for line "
4784  "detection and fit (e.g. '60,40,15').",
4785  "giraffe.wlcalibration",
4786  "10,10,10,10,10");
4787  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lswidth");
4788  cpl_parameterlist_append(list, p);
4789 
4790 
4791  p = cpl_parameter_new_value("giraffe.wlcalibration.line.separation",
4792  CPL_TYPE_DOUBLE,
4793  "Factor used to compute the minimum line "
4794  "separation from the window width.",
4795  "giraffe.wlcalibration",
4796  0.9);
4797  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lssep");
4798  cpl_parameterlist_append(list, p);
4799 
4800 
4801  p = cpl_parameter_new_value("giraffe.wlcalibration.line.fluxratio",
4802  CPL_TYPE_DOUBLE,
4803  "Selects only lines whose neighbours have "
4804  "a relative intensity less than "
4805  "1. / fluxratio.",
4806  "giraffe.wlcalibration",
4807  50.);
4808 
4809  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lsfxratio");
4810  cpl_parameterlist_append(list, p);
4811 
4812 
4813  p = cpl_parameter_new_value("giraffe.wlcalibration.line.brightness",
4814  CPL_TYPE_DOUBLE,
4815  "Selects lines having an intensity greater "
4816  "or equal to the given intensity.",
4817  "giraffe.wlcalibration",
4818  0.);
4819 
4820  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lsbright");
4821  cpl_parameterlist_append(list, p);
4822 
4823 
4824  p = cpl_parameter_new_value("giraffe.wlcalibration.line.count",
4825  CPL_TYPE_INT,
4826  "Sets the minimum number of lines to select; "
4827  "selected are lines with the highest nominal "
4828  "intensity. A value of 0 turns this selection "
4829  "off. If the value is less than 0 the "
4830  "selection is skipped if the line list does "
4831  "not contain enough lines.",
4832  "giraffe.wlcalibration",
4833  -80);
4834 
4835  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lscount");
4836  cpl_parameterlist_append(list, p);
4837 
4838 
4839  p = cpl_parameter_new_value("giraffe.wlcalibration.line.wrange",
4840  CPL_TYPE_STRING,
4841  "Selects only lines within the given "
4842  "wavelength range [nm].",
4843  "giraffe.wlcalibration",
4844  "0.,0.");
4845 
4846  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lswrange");
4847  cpl_parameterlist_append(list, p);
4848 
4849 
4850  /*
4851  * Line profile fit parameters
4852  */
4853 
4854  p = cpl_parameter_new_enum("giraffe.wlcalibration.line.model",
4855  CPL_TYPE_STRING,
4856  "Line profile model.",
4857  "giraffe.wlcalibration",
4858  "psfexp", 3, "psfexp", "psfexp2", "gaussian");
4859 
4860  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfmodel");
4861  cpl_parameterlist_append(list, p);
4862 
4863 
4864  p = cpl_parameter_new_enum("giraffe.wlcalibration.line.residuals",
4865  CPL_TYPE_STRING,
4866  "Use optical model residuals for line "
4867  "detection.",
4868  "giraffe.wlcalibration",
4869  "auto", 3, "auto", "enable", "disable");
4870 
4871  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfres");
4872  cpl_parameterlist_append(list, p);
4873 
4874 
4875  p = cpl_parameter_new_value("giraffe.wlcalibration.line.threshold",
4876  CPL_TYPE_DOUBLE,
4877  "Line detection threshold during the "
4878  "line fitting (multiple of bias sigma)",
4879  "giraffe.wlcalibration",
4880  1.);
4881 
4882  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfthreshold");
4883  cpl_parameterlist_append(list, p);
4884 
4885 
4886  p = cpl_parameter_new_value("giraffe.wlcalibration.line.offset",
4887  CPL_TYPE_DOUBLE,
4888  "Maximum allowed difference between the "
4889  "fitted and raw line peak position.",
4890  "giraffe.wlcalibration",
4891  10.);
4892 
4893  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfoffset");
4894  cpl_parameterlist_append(list, p);
4895 
4896 
4897  p = cpl_parameter_new_value("giraffe.wlcalibration.line.iterations",
4898  CPL_TYPE_INT,
4899  "Line detection fit maximum number of "
4900  "iterations.",
4901  "giraffe.wlcalibration",
4902  50);
4903 
4904  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfniter");
4905  cpl_parameterlist_append(list, p);
4906 
4907 
4908  p = cpl_parameter_new_value("giraffe.wlcalibration.line.tests",
4909  CPL_TYPE_INT,
4910  "Line detection fit maximum number of "
4911  "tests.",
4912  "giraffe.wlcalibration",
4913  7);
4914 
4915  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfntest");
4916  cpl_parameterlist_append(list, p);
4917 
4918 
4919  p = cpl_parameter_new_value("giraffe.wlcalibration.line.dchisquare",
4920  CPL_TYPE_DOUBLE,
4921  "Line detection fit minimum chi-square "
4922  "difference.",
4923  "giraffe.wlcalibration",
4924  0.0001);
4925 
4926  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfdchisq");
4927  cpl_parameterlist_append(list, p);
4928 
4929 
4930  p = cpl_parameter_new_value("giraffe.wlcalibration.line.rwidthratio",
4931  CPL_TYPE_DOUBLE,
4932  "Line width/resolution width factor.",
4933  "giraffe.wlcalibration",
4934  0.5);
4935 
4936  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfreswid");
4937  cpl_parameterlist_append(list, p);
4938 
4939 
4940  p = cpl_parameter_new_value("giraffe.wlcalibration.line.exponent",
4941  CPL_TYPE_DOUBLE,
4942  "Exponential line profile exponent; it will "
4943  "not be fitted if it is larger than 0.",
4944  "giraffe.wlcalibration",
4945  -3.);
4946 
4947  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfexpwid");
4948  cpl_parameterlist_append(list, p);
4949 
4950 
4951  /*
4952  * Slit offsets
4953  */
4954 
4955 
4956  p = cpl_parameter_new_value("giraffe.wlcalibration.slit.offset",
4957  CPL_TYPE_STRING,
4958  "Initial slit position offsets along the "
4959  "x and y direction and rotation angle.",
4960  "giraffe.wlcalibration",
4961  "setup");
4962 
4963  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-soffset");
4964  cpl_parameterlist_append(list, p);
4965 
4966 
4967  /*
4968  * Optical model parameters
4969  */
4970 
4971  p = cpl_parameter_new_enum("giraffe.wlcalibration.opt.model",
4972  CPL_TYPE_STRING,
4973  "Optical model.",
4974  "giraffe.wlcalibration",
4975  "xoptmod2", 2, "xoptmod", "xoptmod2");
4976 
4977  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-ommodel");
4978  cpl_parameterlist_append(list, p);
4979 
4980 
4981  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.direction",
4982  CPL_TYPE_INT,
4983  "Dispersion direction flag.",
4984  "giraffe.wlcalibration",
4985  1);
4986 
4987  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omdir");
4988  cpl_parameterlist_append(list, p);
4989 
4990 
4991  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.solution",
4992  CPL_TYPE_BOOL,
4993  "Controls optical model parameter fitting.",
4994  "giraffe.wlcalibration",
4995  TRUE);
4996 
4997  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omsol");
4998  cpl_parameterlist_append(list, p);
4999 
5000 
5001  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.flags",
5002  CPL_TYPE_STRING,
5003  "List of flags defining the set of free "
5004  "parameters used for fitting the optical "
5005  "model. Possible values are: fcoll, gcam, "
5006  "theta, sdx, sdy, sphi",
5007  "giraffe.wlcalibration",
5008  "sdx,sdy,sphi");
5009 
5010  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omflags");
5011  cpl_parameterlist_append(list, p);
5012 
5013 
5014  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.subslits",
5015  CPL_TYPE_BOOL,
5016  "Controls subslit geometry usage in the "
5017  "optical model fit; subslits are used if "
5018  "set to `true'.",
5019  "giraffe.wlcalibration",
5020  FALSE);
5021 
5022  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omsslits");
5023  cpl_parameterlist_append(list, p);
5024 
5025 
5026  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.iterations",
5027  CPL_TYPE_INT,
5028  "Optical model fit maximum number of "
5029  "iterations.",
5030  "giraffe.wlcalibration",
5031  50);
5032 
5033  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omniter");
5034  cpl_parameterlist_append(list, p);
5035 
5036 
5037  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.tests",
5038  CPL_TYPE_INT,
5039  "Optical model fit maximum number of "
5040  "tests",
5041  "giraffe.wlcalibration",
5042  7);
5043 
5044  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omntest");
5045  cpl_parameterlist_append(list, p);
5046 
5047 
5048  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.dchisquare",
5049  CPL_TYPE_DOUBLE,
5050  "Optical model fit minimum chi-square "
5051  "difference.",
5052  "giraffe.wlcalibration",
5053  0.0001);
5054 
5055  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omdchisq");
5056  cpl_parameterlist_append(list, p);
5057 
5058 
5059  /*
5060  * PSF fit parameters
5061  */
5062 
5063  p = cpl_parameter_new_value("giraffe.wlcalibration.psf.sigma",
5064  CPL_TYPE_DOUBLE,
5065  "PSF width fit sigma clipping factor.",
5066  "giraffe.wlcalibration",
5067  1.25);
5068 
5069  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwsigma");
5070  cpl_parameterlist_append(list, p);
5071 
5072 
5073  p = cpl_parameter_new_value("giraffe.wlcalibration.psf.iterations",
5074  CPL_TYPE_INT,
5075  "PSF width fit sigma clipping maximum "
5076  "number of iterations.",
5077  "giraffe.wlcalibration",
5078  10);
5079 
5080  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwniter");
5081  cpl_parameterlist_append(list, p);
5082 
5083 
5084  p = cpl_parameter_new_range("giraffe.wlcalibration.psf.fraction",
5085  CPL_TYPE_DOUBLE,
5086  "PSF width fit sigma clipping minimum "
5087  "fraction of points accepted/total.",
5088  "giraffe.wlcalibration",
5089  0.9, 0., 1.);
5090 
5091  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwmfrac");
5092  cpl_parameterlist_append(list, p);
5093 
5094 
5095  p = cpl_parameter_new_value("giraffe.wlcalibration.psf.order",
5096  CPL_TYPE_STRING,
5097  "X and Y polynomial orders for PSF x-width "
5098  "Chebyshev fit.",
5099  "giraffe.wlcalibration",
5100  "2,2");
5101 
5102  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xworder");
5103  cpl_parameterlist_append(list, p);
5104 
5105 
5106  /*
5107  * Parameters of the wavelength solution Chebyshev correction fit
5108  */
5109 
5110  p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.sigma",
5111  CPL_TYPE_DOUBLE,
5112  "Chebyshev correction sigma clipping factor.",
5113  "giraffe.wlcalibration",
5114  150.0);
5115 
5116  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wssigma");
5117  cpl_parameterlist_append(list, p);
5118 
5119 
5120  p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.iterations",
5121  CPL_TYPE_INT,
5122  "Chebyshev correction sigma clipping "
5123  "maximum number of iterations",
5124  "giraffe.wlcalibration",
5125  10);
5126 
5127  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsniter");
5128  cpl_parameterlist_append(list, p);
5129 
5130 
5131  p = cpl_parameter_new_range("giraffe.wlcalibration.wsol.fraction",
5132  CPL_TYPE_DOUBLE,
5133  "Chebyshev correction sigma clipping "
5134  "minimum fraction of points accepted/total.",
5135  "giraffe.wlcalibration",
5136  0.9, 0., 1.);
5137 
5138  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsmfrac");
5139  cpl_parameterlist_append(list, p);
5140 
5141 
5142  p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.order",
5143  CPL_TYPE_STRING,
5144  "X and Y polynomial orders for the wavelength "
5145  "solution Chebyshev correction.",
5146  "giraffe.wlcalibration",
5147  "6,4");
5148 
5149  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsorder");
5150  cpl_parameterlist_append(list, p);
5151 
5152  return;
5153 
5154 }

This file is part of the GIRAFFE Pipeline Reference Manual 2.12.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Mon Mar 24 2014 11:43:53 by doxygen 1.8.2 written by Dimitri van Heesch, © 1997-2004