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  /* Unused parameters: */
990  (void) lines;
991  (void) slitgeometry;
992  (void) solution;
993 
994  return NULL;
995 
996 }
997 
998 
999 /*
1000  * Sets the fit parameters to their initial values. The function does
1001  * nothing if the line status flags are not zero.
1002  */
1003 
1004 inline static void
1005 _giraffe_line_fit_setup(GiModel *model, cxdouble width, cxint xmin,
1006  const cpl_matrix *y, const cpl_matrix *sigma,
1007  const GiLineParams *setup, cxint *lflags)
1008 {
1009 
1010  cxint k;
1011  cxint center = 0;
1012  cxint xline = 0;
1013 
1014  cxdouble amplitude = 0.;
1015  cxdouble background = 0.;
1016  cxdouble _sigma = 0.;
1017 
1018  cpl_matrix *_y = NULL;
1019 
1020 
1021  cx_assert(model != NULL);
1022  cx_assert(y != NULL);
1023  cx_assert(setup != NULL);
1024  cx_assert(lflags != NULL);
1025 
1026 
1027  if (*lflags != LF_R_NONE) {
1028  return;
1029  }
1030 
1031 
1032  /*
1033  * Model parameters: the line amplitude, center, width(s)
1034  * and background. The line background is estimated as the
1035  * mean of the two lowest intensities found in the fit
1036  * interval.
1037  */
1038 
1039  /* Amplitude and center */
1040 
1041  center = xmin;
1042  for (k = 0; k < cpl_matrix_get_nrow((cpl_matrix*)y); k++) {
1043  if (cpl_matrix_get((cpl_matrix *)y, k, 0) >= amplitude) {
1044  center = xmin + k;
1045  xline = k;
1046  amplitude = cpl_matrix_get((cpl_matrix *)y, k, 0);
1047  }
1048  }
1049 
1050 
1051  /* Background */
1052 
1053  _y = cpl_matrix_duplicate((cpl_matrix *)y);
1054 
1055  giraffe_matrix_sort(_y);
1056 
1057  background = 0.5 * (cpl_matrix_get(_y, 0, 0) + cpl_matrix_get(_y, 1, 0));
1058  cpl_matrix_delete(_y);
1059 
1060 
1061  /*
1062  * Line rejection: Discard lines whose flux errors at the
1063  * line center times a threshold is larger than the peak flux
1064  * in the current fit interval, or if maximum flux value
1065  * exceeds the saturation level.
1066  */
1067 
1068  _sigma = cpl_matrix_get((cpl_matrix *)sigma, xline, 0) * setup->thres;
1069 
1070  if (amplitude <= _sigma || amplitude > setup->satlv) {
1071  *lflags |= LF_R_AMPLI;
1072  return;
1073  }
1074 
1075  giraffe_model_set_parameter(model, "Amplitude", amplitude - background);
1076  giraffe_model_set_parameter(model, "Center", center);
1077  giraffe_model_set_parameter(model, "Background", background);
1078  giraffe_model_set_parameter(model, "Width1", width);
1079 
1080  if (strncmp(giraffe_model_get_name(model), "psfexp", 6) == 0) {
1081 
1082  cxdouble width2 = setup->psfexp < 0. ? -setup->psfexp : setup->psfexp;
1083 
1084  giraffe_model_set_parameter(model, "Width2", width2);
1085 
1086 
1087  /*
1088  * If the psf-width exponent is positiv the parameter
1089  * value is kept, otherwise it is fitted.
1090  */
1091 
1092  if (setup->psfexp >= 0.) {
1093  giraffe_model_freeze_parameter(model, "Width2");
1094  }
1095  else {
1096  giraffe_model_thaw_parameter(model, "Width2");
1097  }
1098 
1099  }
1100 
1101  return;
1102 
1103 }
1104 
1105 
1106 inline static cxint
1107 _giraffe_line_fit(GiLineData *lines, const cpl_image *positions, cxint width,
1108  const GiExtraction *extraction, const GiTable *fibers,
1109  const GiImage *locy, const GiLineParams *setup)
1110 {
1111 
1112  const cxchar *const fctid = "_giraffe_line_fit";
1113 
1114 
1115  const cxchar *idx = NULL;
1116 
1117  cxint i;
1118  cxint nfibers = 0;
1119  cxint nlines = 0;
1120 
1121  const cxdouble LOG2 = log(2.);
1122  const cxdouble fwhm_ratio = 2. * sqrt(2. * LOG2);
1123 
1124  cpl_image *_spectra = NULL;
1125  cpl_image *_errors = NULL;
1126  cpl_image *_locy = NULL;
1127 
1128  cpl_matrix *x = NULL;
1129  cpl_matrix *y = NULL;
1130  cpl_matrix *sigma = NULL;
1131 
1132  cpl_table *_fibers = NULL;
1133 
1134  GiModel *model = NULL;
1135 
1136 
1137  cx_assert(positions != NULL);
1138  cx_assert(width > 0);
1139 
1140  cx_assert(extraction != NULL);
1141  cx_assert(extraction->spectra != NULL && extraction->error != NULL);
1142 
1143  cx_assert(fibers != NULL);
1144  cx_assert(locy != NULL);
1145  cx_assert(setup != NULL);
1146 
1147 
1148  _fibers = giraffe_table_get(fibers);
1149  cx_assert(_fibers != NULL);
1150 
1151  _spectra = giraffe_image_get(extraction->spectra);
1152  cx_assert(_spectra != NULL);
1153 
1154  _errors = giraffe_image_get(extraction->error);
1155  cx_assert(_errors != NULL);
1156 
1157  _locy = giraffe_image_get(locy);
1158  cx_assert(_locy != NULL);
1159 
1160  nfibers = cpl_table_get_nrow(_fibers);
1161 
1162  cx_assert(nfibers == cpl_image_get_size_x(_spectra));
1163  cx_assert(nfibers == cpl_image_get_size_x(_errors));
1164 
1165  // FIXME: The assertion should not be necessary any more, but better
1166  // check this
1167  //cx_assert(nfibers == cpl_image_get_size_x(_locy));
1168 
1169  idx = giraffe_fiberlist_query_index(_fibers);
1170  cx_assert(idx != NULL);
1171 
1172  nlines = cpl_image_get_size_y(positions);
1173 
1174 
1175  /*
1176  * Create the selected line fit model. All parameters will be fitted.
1177  */
1178 
1179  if (strcmp(setup->model, "gaussian") != 0 &&
1180  strcmp(setup->model, "psfexp") != 0 &&
1181  strcmp(setup->model, "psfexp2") != 0) {
1182  cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
1183  return 1;
1184  }
1185 
1186  model = giraffe_model_new(setup->model);
1187 
1188  if (giraffe_model_get_type(model) != GI_MODEL_LINE) {
1189  giraffe_model_delete(model);
1190  return 2;
1191  }
1192 
1193  giraffe_model_thaw(model);
1194 
1195  giraffe_model_set_iterations(model, setup->fit.iterations);
1196  giraffe_model_set_tests(model, setup->fit.tests);
1197  giraffe_model_set_delta(model, setup->fit.delta);
1198 
1199 
1200  /*
1201  * For each spectrum in the input image `positions' fit each line.
1202  */
1203 
1204  x = cpl_matrix_new(width, 1);
1205  y = cpl_matrix_new(width, 1);
1206  sigma = cpl_matrix_new(width, 1);
1207 
1208  for (i = 0; i < nfibers; i++) {
1209 
1210  cxint j;
1211 
1212  for (j = 0; j < nlines; j++) {
1213 
1214  cxint k;
1215  cxint lflags = LF_R_NONE;
1216  cxint iterations = 0;
1217  cxint ndata = 0;
1218  cxint xmin = 0;
1219  cxint xmax = 0;
1220  cxint nx = cpl_image_get_size_y(_spectra);
1221 
1222  cxdouble xccd = 0.;
1223  cxdouble yccd = 0.;
1224  cxdouble lwidth = 0.;
1225  cxdouble amplitude = 0.;
1226  cxdouble background = 0.;
1227  cxdouble center = 0.;
1228  cxdouble width1 = 0.;
1229  cxdouble exponent = 0.;
1230  cxdouble error = 0.;
1231  cxdouble gwidth = 0.;
1232 
1233  const cxdouble *_positions = cpl_image_get_data_const(positions);
1234 
1235 
1236  /*
1237  * Compute line position in pixels using the fiber localization
1238  */
1239 
1240  xccd = _positions[j * nfibers + i];
1241 
1242  xmin = xccd;
1243  xmax = xccd;
1244 
1245  if (0 < xccd && xccd < nx) {
1246 
1247  cxint cs = cpl_table_get_int(_fibers, idx, i, NULL) - 1;
1248 
1249 
1250  /*
1251  * Compute fit interval taking the CCD size into account.
1252  * The entire interval must be within the CCD boundaries.
1253  */
1254 
1255  xmin = (cxint)(xccd - 0.5 * width + 0.5);
1256  xmax = (cxint)(xccd + 0.5 * width + 0.5);
1257 
1258  xmin = CX_MAX(CX_MIN(xmin, nx - 1), 0);
1259  xmax = CX_MAX(CX_MIN(xmax, nx - 1), 0);
1260 
1261  ndata = xmax - xmin;
1262 
1263  /*
1264  * Compute fiber centroid position using linear
1265  * interpolation.
1266  */
1267 
1268  yccd = _giraffe_get_fiber_position(_locy, cs, xccd);
1269 
1270  }
1271 
1272 
1273  /*
1274  * At least 3 data points are required for the fit. This covers
1275  * also the situation xmin == xmax
1276  */
1277 
1278  if (ndata < 3) {
1279  lflags |= LF_R_XCCD;
1280  }
1281  else {
1282 
1283  /*
1284  * Resize and fill the fit data vectors
1285  */
1286 
1287  if (ndata != cpl_matrix_get_nrow(x)) {
1288  cpl_matrix_set_size(x, ndata, 1);
1289  cpl_matrix_set_size(y, ndata, 1);
1290  cpl_matrix_set_size(sigma, ndata, 1);
1291  }
1292 
1293  for (k = 0; k < ndata; k++) {
1294 
1295  cxint l = xmin + k;
1296 
1297  cxdouble *sdata = cpl_image_get_data(_spectra);
1298  cxdouble *edata = cpl_image_get_data(_errors);
1299 
1300  cpl_matrix_set(x, k, 0, xmin + k);
1301  cpl_matrix_set(y, k, 0, sdata[l * nfibers + i]);
1302  cpl_matrix_set(sigma, k, 0, edata[l * nfibers + i]);
1303 
1304  }
1305 
1306  }
1307 
1308 
1309  /*
1310  * Convert line FWHM to the model's width parameter, i.e.
1311  * gaussian or exponetial width depending on the selected
1312  * model profile.
1313  *
1314  * Note: The absolute value of setup->psfexp is used below,
1315  * because the sign just determines whether it is
1316  * fitted or kept fixed.
1317  */
1318 
1319  if (strcmp(setup->model, "psfexp") == 0) {
1320 
1321  exponent = fabs(setup->psfexp);
1322 
1323  lwidth = pow(0.5 * setup->grwid * nx, exponent) / LOG2;
1324 
1325  }
1326  else if (strcmp(setup->model, "psfexp2") == 0) {
1327 
1328  exponent = fabs(setup->psfexp);
1329 
1330  lwidth = setup->grwid * nx / (2. * pow(LOG2, 1. / exponent));
1331 
1332  }
1333  else if (strcmp(setup->model, "gaussian") == 0) {
1334 
1335  lwidth = setup->grwid * nx / fwhm_ratio;
1336 
1337  }
1338  else {
1339 
1340  /*
1341  * This point should never be reached!
1342  */
1343 
1344  gi_error("Unsupported line model encountered!");
1345 
1346  }
1347 
1348 
1349  /*
1350  * Validate and set the initial values of the line model
1351  * fit parameters
1352  */
1353 
1354 
1355  _giraffe_line_fit_setup(model, lwidth, xmin, y, sigma, setup,
1356  &lflags);
1357 
1358 
1359  if (lflags == LF_R_NONE) {
1360 
1361  cxint xline = giraffe_model_get_parameter(model, "Center");
1362 
1363  cxdouble hwidth = 0.;
1364 
1365  cxint status = 0;
1366 
1367 
1368  /*
1369  * Fit the model
1370  */
1371 
1372  status = giraffe_model_fit(model, x, y, sigma);
1373 
1374 
1375  amplitude = giraffe_model_get_parameter(model, "Amplitude");
1376  background = giraffe_model_get_parameter(model, "Background");
1377  center = giraffe_model_get_parameter(model, "Center");
1378 
1379 
1380  /*
1381  * Convert the model dependent width parameter back to
1382  * the line's FWHM .
1383  */
1384 
1385  if (strcmp(setup->model, "psfexp") == 0) {
1386 
1387  width1 = giraffe_model_get_parameter(model, "Width1");
1388  exponent = giraffe_model_get_parameter(model, "Width2");
1389 
1390  /* exponential width */
1391  gwidth = 2. * pow(width1 * LOG2, 1. / exponent);
1392 
1393  }
1394  else if (strcmp(setup->model, "psfexp2") == 0) {
1395 
1396  width1 = giraffe_model_get_parameter(model, "Width1");
1397  exponent = giraffe_model_get_parameter(model, "Width2");
1398 
1399  /* exponential width */
1400  gwidth = 2. * pow(LOG2, 1. / exponent) * width1;
1401 
1402  }
1403  else if (strcmp(setup->model, "gaussian") == 0) {
1404 
1405  width1 = giraffe_model_get_parameter(model, "Width1");
1406 
1407  /* gaussian width */
1408  gwidth = width1 * fwhm_ratio;
1409 
1410  }
1411  else {
1412 
1413  /*
1414  * This point should never be reached!
1415  */
1416 
1417  gi_error("Unsupported line model encountered!");
1418 
1419  }
1420 
1421  hwidth = gwidth / 2.;
1422  iterations = giraffe_model_get_position(model);
1423 
1424 
1425  /*
1426  * Check line fit. Set rejection code for lines
1427  * with bad fit parameters.
1428  */
1429 
1430  if (status < 0) {
1431 
1432  /* Fit error */
1433  lflags |= LF_R_ERROR;
1434 
1435  }
1436 
1437  if (iterations >= giraffe_model_get_iterations(model)) {
1438 
1439  /* Maximum number of iterations reached */
1440  lflags |= LF_R_NITER;
1441 
1442  }
1443 
1444  if (xmin > center || center > xmax) {
1445 
1446  /* Line center out of window */
1447  lflags |= LF_R_CENTR;
1448 
1449  }
1450 
1451  if ((center - hwidth) < xmin) {
1452 
1453  /* Line out of window on the left */
1454  lflags |= LF_R_LEFT;
1455 
1456  }
1457 
1458  if ((center + hwidth) > xmax) {
1459 
1460  /* Line out of window on the right */
1461  lflags |= LF_R_RIGHT;
1462 
1463  }
1464 
1465  if ((center - xline) >= setup->offst) {
1466 
1467  /* Line center too far from the initial guess */
1468  lflags |= LF_R_OFFST;
1469 
1470  }
1471 
1472  if (width1 < 0. || exponent < 0.) {
1473 
1474  /* Negative line width */
1475  lflags |= LF_R_BADLN;
1476 
1477  }
1478 
1479  if (gwidth > (xmax - xmin)) {
1480 
1481  /* Line width is larger than window */
1482  lflags |= LF_R_WIDTH;
1483 
1484  }
1485 
1486  if (gwidth < (setup->grwid * nx * setup->wfact)) {
1487 
1488  /* Line width too small for resolution */
1489  lflags |= LF_R_RESOL;
1490 
1491  }
1492 
1493  if (gwidth > (setup->grwid * nx / setup->wfact)) {
1494 
1495  /* Line width too large for resolution */
1496  lflags |= LF_R_RESOL;
1497 
1498  }
1499 
1500  }
1501 
1502 
1503  /*
1504  * Save the results
1505  */
1506 
1507  /* Line status code */
1508 
1509  giraffe_linedata_set_status(lines, i, j, lflags);
1510 
1511  giraffe_linedata_set(lines, "Iterations", i, j,iterations);
1512  giraffe_linedata_set(lines, "Chi-square", i, j,
1513  giraffe_model_get_chisq(model));
1514  giraffe_linedata_set(lines, "DoF", i, j,
1515  giraffe_model_get_df(model));
1516  giraffe_linedata_set(lines, "R-square", i, j,
1517  giraffe_model_get_rsquare(model));
1518  giraffe_linedata_set(lines, "Xccd", i, j, xccd);
1519  giraffe_linedata_set(lines, "Yccd", i, j, yccd);
1520 
1521  /* FIXME: Using the two functions prototyped below would
1522  * improve the code here. But they need support
1523  * from the GiModel implementation. (RP)
1524  *
1525  * giraffe_linedata_set_parameters(lines, model, i, j);
1526  * giraffe_linedata_set_errors(lines, model, i, j);
1527  */
1528 
1529  giraffe_linedata_set(lines, "Amplitude", i, j, amplitude);
1530  giraffe_linedata_set(lines, "Background", i, j, background);
1531  giraffe_linedata_set(lines, "Center", i, j, center);
1532  giraffe_linedata_set(lines, "Width1", i, j, width1);
1533 
1534  if (strncmp(setup->model, "psfexp", 6) == 0) {
1535  giraffe_linedata_set(lines, "Width2", i, j, exponent);
1536  }
1537 
1538  giraffe_linedata_set(lines, "FWHM", i, j, gwidth);
1539 
1540  /* Uncertainties */
1541 
1542  error = giraffe_model_get_sigma(model, "Amplitude");
1543  giraffe_linedata_set(lines, "dAmplitude", i, j, error);
1544 
1545  error = giraffe_model_get_sigma(model, "Center");
1546  giraffe_linedata_set(lines, "dCenter", i, j, error);
1547 
1548  error = giraffe_model_get_sigma(model, "Background");
1549  giraffe_linedata_set(lines, "dBackground", i, j, error);
1550 
1551  error = giraffe_model_get_sigma(model, "Width1");
1552  giraffe_linedata_set(lines, "dWidth1", i, j, error);
1553 
1554  if (strncmp(setup->model, "psfexp", 6) == 0) {
1555  error = giraffe_model_get_sigma(model, "Width2");
1556  giraffe_linedata_set(lines, "dWidth2", i, j, error);
1557  }
1558 
1559  }
1560 
1561  }
1562 
1563  cpl_matrix_delete(x);
1564  cpl_matrix_delete(y);
1565  cpl_matrix_delete(sigma);
1566 
1567  giraffe_model_delete(model);
1568 
1569  return 0;
1570 
1571 }
1572 
1573 
1574 inline static cpl_image *
1575 _giraffe_psf_fit(GiLineData *lines, const GiLocalization *localization,
1576  GiTable *fibers, GiTable *slitgeometry, GiSCFitParams *setup)
1577 {
1578 
1579  const cxchar *const fctid = "_giraffe_psf_fit";
1580 
1581 
1582  cxint i;
1583  cxint status = 0;
1584  cxint ngood = 0;
1585  cxint nlines = 0;
1586  cxint nsubslits = 1;
1587  cxint nx = 0;
1588 
1589  cpl_table *_fibers = NULL;
1590 
1591  cpl_image *locy = NULL;
1592  cpl_image *locw = NULL;
1593  cpl_image *psfwidth = NULL;
1594 
1595 
1596  cx_assert(lines != NULL);
1597  cx_assert(localization != NULL);
1598  cx_assert(fibers != NULL);
1599  cx_assert(slitgeometry != NULL);
1600  cx_assert(setup != NULL);
1601 
1602  _fibers = giraffe_table_get(fibers);
1603  cx_assert(_fibers != NULL);
1604 
1605  locy = giraffe_image_get(localization->locy);
1606  cx_assert(locy != NULL);
1607 
1608  locw = giraffe_image_get(localization->locw);
1609  cx_assert(locw != NULL);
1610 
1611  nx = cpl_image_get_size_y(locy);
1612  nlines = giraffe_linedata_lines(lines);
1613 
1614  psfwidth = cpl_image_new(cpl_table_get_nrow(_fibers), nx,
1615  CPL_TYPE_DOUBLE);
1616 
1617  if (setup->subslits == TRUE) {
1618  nsubslits = _giraffe_subslit_get_max(_fibers);
1619  }
1620 
1621  for (i = 0; i < nsubslits; i++) {
1622 
1623  cxint j;
1624  cxint k;
1625  cxint ssn = 0;
1626  cxint nfibers = 0;
1627  cxint ndata = 0;
1628  cxint iterations = 0;
1629  cxint accepted = 0;
1630  cxint total = 0;
1631 
1632  cxdouble ymin = 0.;
1633  cxdouble ymax = 0.;
1634  cxdouble ratio = 1.;
1635 
1636  cpl_matrix *xss = NULL;
1637  cpl_matrix *yss = NULL;
1638  cpl_matrix *wss = NULL;
1639  cpl_matrix *sss = NULL;
1640  cpl_matrix *nss = NULL;
1641  cpl_matrix *lss = NULL;
1642  cpl_matrix *base = NULL;
1643  cpl_matrix *fit = NULL;
1644  cpl_matrix *coeff = NULL;
1645  cpl_matrix *chebyshev = NULL;
1646 
1647  cpl_table *subslit = NULL;
1648 
1649  GiChebyshev2D *psffit = NULL;
1650 
1651 
1652  if (setup->subslits == TRUE) {
1653  subslit = _giraffe_subslit_get(_fibers, i + 1);
1654  ssn = cpl_table_get_int(subslit, "SSN", 0, NULL);
1655 
1656  cx_assert(ssn == i + 1);
1657  }
1658  else {
1659  subslit = cpl_table_duplicate(_fibers);
1660  ssn = 0;
1661  }
1662 
1663  if (subslit == NULL) {
1664  continue;
1665  }
1666 
1667  _giraffe_subslit_range(subslit, locy, locw, &ymin, &ymax);
1668 
1669  nfibers = cpl_table_get_nrow(subslit);
1670  ndata = nfibers * nlines;
1671 
1672 
1673  xss = cpl_matrix_new(ndata, 1); /* X abcissas for subslit */
1674  yss = cpl_matrix_new(ndata, 1); /* Y ordinates */
1675  wss = cpl_matrix_new(1, ndata); /* widths (transposed) */
1676  sss = cpl_matrix_new(ndata, 1); /* width sigmas */
1677  nss = cpl_matrix_new(ndata, 1); /* fiber indices */
1678  lss = cpl_matrix_new(ndata, 1); /* line indices */
1679 
1680 
1681  /*
1682  * Fills inputs matrices with good lines xccd, yccd, width,
1683  * width sigmas and line status
1684  */
1685 
1686  k = 0;
1687 
1688  for (j = 0; j < nfibers; j++) {
1689 
1690  cxint l;
1691  cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
1692 
1693  for (l = 0; l < nlines; l++) {
1694 
1695  cxdouble value = 0.;
1696  cxdouble yccd = giraffe_linedata_get(lines, "Yccd", n, l);
1697 
1698  if (giraffe_linedata_get_status(lines, n, l) != 0) {
1699  continue;
1700  }
1701 
1702  if (yccd < ymin || yccd > ymax) {
1703  continue;
1704  }
1705 
1706  value = giraffe_linedata_get(lines, "Xccd", n, l);
1707  cpl_matrix_set(xss, k, 0, value);
1708 
1709  cpl_matrix_set(yss, k, 0, yccd);
1710 
1711  /* FIXME: The orignal code fits the FWHM but uses
1712  * the uncertainty of the profile's width
1713  * parameter to reject lines in the sigma
1714  * clipping below. Instead the FWHM's
1715  * uncertainty should be used. Check this! (RP)
1716  */
1717 
1718  value = giraffe_linedata_get(lines, "FWHM", n, l);
1719  cpl_matrix_set(wss, 0, k, value);
1720 
1721  value = giraffe_linedata_get(lines, "dWidth1", n, l);
1722  cpl_matrix_set(sss, k, 0, value);
1723 
1724  cpl_matrix_set(nss, k, 0, n);
1725  cpl_matrix_set(lss, k, 0, l);
1726 
1727  ++k;
1728 
1729  }
1730 
1731  }
1732 
1733  if (k == 0) {
1734  cpl_msg_debug(fctid, "Skipping subslit %d: No input lines left! "
1735  "All lines have non-zero status or are beyond the "
1736  "subslit boundaries (%.4f, %.4f).", ssn, ymin, ymax);
1737  continue;
1738  }
1739 
1740  /*
1741  * Shrink input matrices to their actual size
1742  */
1743 
1744  cpl_matrix_set_size(xss, k, 1);
1745  cpl_matrix_set_size(yss, k, 1);
1746  cpl_matrix_set_size(wss, 1, k);
1747  cpl_matrix_set_size(sss, k, 1);
1748  cpl_matrix_set_size(nss, k, 1);
1749  cpl_matrix_set_size(lss, k, 1);
1750 
1751 
1752  /*
1753  * Sigma clipping
1754  */
1755 
1756  iterations = 0;
1757  ratio = 1.0;
1758  accepted = cpl_matrix_get_ncol(wss);
1759  total = accepted;
1760 
1761  while (accepted > 0 && iterations < setup->clip.iterations &&
1762  ratio > setup->clip.fraction) {
1763 
1764  base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
1765  setup->fit.xorder + 1,
1766  setup->fit.yorder + 1, xss, yss);
1767 
1768  if (coeff != NULL) {
1769  cpl_matrix_delete(coeff);
1770  coeff = NULL;
1771  }
1772 
1773  coeff = giraffe_matrix_leastsq(base, wss);
1774 
1775  if (coeff == NULL) {
1776  cpl_msg_debug(fctid, "Error solving linear system for "
1777  "subslit %d, skipping subslit.", ssn);
1778  break;
1779  }
1780 
1781  fit = cpl_matrix_product_create(coeff, base);
1782 
1783  k = 0;
1784 
1785  for (j = 0; j < cpl_matrix_get_ncol(fit); j++) {
1786 
1787  cxdouble _fit = cpl_matrix_get(fit, 0, j);
1788  cxdouble _wss = cpl_matrix_get(wss, 0, j);
1789  cxdouble _sss = cpl_matrix_get(sss, j, 0);
1790 
1791  if (fabs(_fit - _wss) >= setup->clip.level * _sss) {
1792 
1793  cxint n = (cxint)cpl_matrix_get(nss, j, 0);
1794  cxint l = (cxint)cpl_matrix_get(lss, j, 0);
1795 
1796  /*
1797  * Reject this line
1798  */
1799 
1800  giraffe_linedata_set_status(lines, n, l, LF_R_PSFIT);
1801  continue;
1802 
1803  }
1804 
1805  cpl_matrix_set(xss, k, 0, cpl_matrix_get(xss, j, 0));
1806  cpl_matrix_set(yss, k, 0, cpl_matrix_get(yss, j, 0));
1807  cpl_matrix_set(wss, 0, k, cpl_matrix_get(wss, 0, j));
1808  cpl_matrix_set(sss, k, 0, cpl_matrix_get(sss, j, 0));
1809  cpl_matrix_set(nss, k, 0, cpl_matrix_get(nss, j, 0));
1810  cpl_matrix_set(lss, k, 0, cpl_matrix_get(lss, j, 0));
1811  ++k;
1812 
1813  }
1814 
1815  cpl_matrix_delete(base);
1816  cpl_matrix_delete(fit);
1817 
1818  if (k == accepted) {
1819 
1820  /*
1821  * No new points rejected, no more iterations. That's it.
1822  */
1823 
1824  break;
1825  }
1826  else {
1827  accepted = k;
1828  ratio = (cxdouble)accepted / (cxdouble)total;
1829 
1830  cpl_matrix_set_size(xss, k, 1);
1831  cpl_matrix_set_size(yss, k, 1);
1832  cpl_matrix_set_size(wss, 1, k);
1833  cpl_matrix_set_size(sss, k, 1);
1834  cpl_matrix_set_size(nss, k, 1);
1835  cpl_matrix_set_size(lss, k, 1);
1836 
1837  ++iterations;
1838 
1839  }
1840 
1841  }
1842 
1843  if (accepted == 0) {
1844  cpl_msg_debug(fctid, "Subslit %d: All lines rejected.", ssn);
1845  continue;
1846  }
1847 
1848  if (coeff == NULL) {
1849  continue;
1850  }
1851 
1852  ngood += accepted;
1853 
1854  cpl_matrix_delete(xss);
1855  cpl_matrix_delete(yss);
1856  cpl_matrix_delete(wss);
1857  cpl_matrix_delete(sss);
1858  cpl_matrix_delete(nss);
1859  cpl_matrix_delete(lss);
1860 
1861 
1862  /*
1863  * Compute coordinate grid for the whole subslit
1864  */
1865 
1866  xss = cpl_matrix_new(nx * nfibers, 1);
1867  yss = cpl_matrix_new(nx * nfibers, 1);
1868 
1869  giraffe_compute_image_coordinates(nx, nfibers, xss, NULL);
1870 
1871  for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
1872 
1873  const cxchar *idx = giraffe_fiberlist_query_index(subslit);
1874 
1875  cxint l;
1876  cxint ns = cpl_image_get_size_x(locy);
1877  cxint cs = cpl_table_get_int(subslit, idx, j, NULL) - 1;
1878 
1879  cxdouble *data = cpl_image_get_data(locy);
1880 
1881 
1882  for (l = 0; l < nx; l++) {
1883  cpl_matrix_set(yss, l * nfibers + j, 0, data[l * ns + cs]);
1884  }
1885 
1886  }
1887 
1888 
1889  /*
1890  * Compute fitted PSF width on the whole subslit
1891  */
1892 
1893  base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
1894  setup->fit.xorder + 1,
1895  setup->fit.yorder + 1, xss, yss);
1896 
1897  fit = cpl_matrix_product_create(coeff, base);
1898 
1899  cpl_matrix_delete(xss);
1900  xss = NULL;
1901 
1902  cpl_matrix_delete(yss);
1903  yss = NULL;
1904 
1905  /* TODO: Store fit coefficients in output structure
1906  */
1907 
1908  /*
1909  * The matrix 'chebyshev' is a view into 'coeff' with the correct
1910  * shape for the subsequent call of giraffe_chebyshev2d_new().
1911  * Therefore, if 'chebyshev' is destroyed afterwards the data
1912  * of the matrix must not be destroyed!
1913  */
1914 
1915  chebyshev = cpl_matrix_wrap(setup->fit.xorder + 1,
1916  setup->fit.yorder + 1,
1917  cpl_matrix_get_data(coeff));
1918 
1919  psffit = giraffe_chebyshev2d_new(setup->fit.xorder, setup->fit.yorder);
1920  status = giraffe_chebyshev2d_set(psffit, 0., nx, ymin, ymax,
1921  chebyshev);
1922 
1923  if (status != 0) {
1924 
1925  giraffe_chebyshev2d_delete(psffit);
1926 
1927  cpl_matrix_unwrap(chebyshev);
1928 
1929  cpl_matrix_delete(base);
1930  cpl_matrix_delete(coeff);
1931  cpl_matrix_delete(fit);
1932 
1933  cpl_table_delete(subslit);
1934 
1935  cpl_image_delete(psfwidth);
1936 
1937  return NULL;
1938 
1939  }
1940 
1941  cpl_matrix_unwrap(chebyshev);
1942  chebyshev = NULL;
1943 
1944  giraffe_chebyshev2d_delete(psffit);
1945  psffit = NULL;
1946 
1947 
1948  /*
1949  * Save fitted PSF of the line profile to the output image.
1950  */
1951 
1952  for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
1953 
1954  cxint l;
1955  cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
1956  cxint ns = cpl_table_get_nrow(_fibers);
1957 
1958  cxdouble *data = cpl_image_get_data(psfwidth);
1959 
1960  for (l = 0; l < nx; l++) {
1961  data[l * ns + n] = cpl_matrix_get(fit, 0, l * nfibers + j);
1962  }
1963 
1964  }
1965 
1966  cpl_matrix_delete(base);
1967  cpl_matrix_delete(coeff);
1968  cpl_matrix_delete(fit);
1969 
1970  cpl_table_delete(subslit);
1971 
1972  }
1973 
1974  return psfwidth;
1975 
1976 }
1977 
1978 
1979 inline static cxint
1980 _giraffe_opticalmodel_fit(GiWlSolution *solution, GiLineData *lines,
1981  GiTable *fibers, GiTable *slitgeometry,
1982  GiOpticalModelParams *setup)
1983 {
1984 
1985  const cxchar *const fctid = "_giraffe_opticalmodel_fit";
1986 
1987 
1988  cxint status = 0;
1989  cxint ndata = 0;
1990  cxint ngood = 0;
1991 
1992  cxsize i;
1993 
1994  cpl_matrix *x = NULL;
1995  cpl_matrix *y = NULL;
1996  cpl_matrix *sigma = NULL;
1997 
1998  cpl_table *_fibers = NULL;
1999  cpl_table *_slitgeometry = NULL;
2000 
2001  GiModel *model = NULL;
2002 
2003 
2004  cx_assert(solution != NULL);
2005  cx_assert(lines != NULL);
2006  cx_assert(fibers != NULL);
2007  cx_assert(slitgeometry != NULL);
2008  cx_assert(setup != NULL);
2009 
2010  _fibers = giraffe_table_get(fibers);
2011  cx_assert(_fibers != NULL);
2012 
2013  _slitgeometry = giraffe_table_get(slitgeometry);
2014  cx_assert(_slitgeometry != NULL);
2015 
2016  model = giraffe_wlsolution_model(solution);
2017 
2018 
2019  /*
2020  * Prepare input data
2021  */
2022 
2023  ndata = giraffe_linedata_lines(lines) * giraffe_linedata_fibers(lines);
2024 
2025  x = cpl_matrix_new(ndata, giraffe_model_count_arguments(model));
2026  y = cpl_matrix_new(ndata, 1);
2027  sigma = cpl_matrix_new(ndata, 1);
2028 
2029  for (i = 0; i < giraffe_linedata_fibers(lines); i++) {
2030 
2031  cxsize j;
2032 
2033  cxdouble xf = cpl_table_get(_slitgeometry, "XF", i, NULL);
2034  cxdouble yf = cpl_table_get(_slitgeometry, "YF", i, NULL);
2035 
2036 
2037  for (j = 0; j < giraffe_linedata_lines(lines); j++) {
2038 
2039  if (giraffe_linedata_get_status(lines, i, j) != 0) {
2040  continue;
2041  }
2042 
2043  /* FIXME: Is this really needed? The status should be enough! (RP)
2044  */
2045 
2046  if (giraffe_linedata_get(lines, "dCenter", i, j) <= 0.) {
2047  continue;
2048  }
2049 
2050  cpl_matrix_set(x, ngood, 0,
2051  giraffe_linedata_get_wavelength(lines, j));
2052  cpl_matrix_set(x, ngood, 1, xf);
2053  cpl_matrix_set(x, ngood, 2, yf);
2054 
2055  cpl_matrix_set(y, ngood, 0,
2056  giraffe_linedata_get(lines, "Center", i, j));
2057  cpl_matrix_set(sigma, ngood, 0,
2058  giraffe_linedata_get(lines, "dCenter", i, j));
2059 
2060  ++ngood;
2061 
2062  }
2063 
2064  }
2065 
2066  cpl_msg_debug(fctid, "Using %d of %d line positions for optical "
2067  "model fit.", ngood, ndata);
2068 
2069  if (ngood == 0) {
2070 
2071  cpl_matrix_delete(x);
2072  cpl_matrix_delete(y);
2073  cpl_matrix_delete(sigma);
2074 
2075  return 1;
2076  }
2077 
2078 
2079  /*
2080  * Shrink input matrices to their actual size
2081  */
2082 
2083  cpl_matrix_set_size(x, ngood, giraffe_model_count_arguments(model));
2084  cpl_matrix_set_size(y, ngood, 1);
2085  cpl_matrix_set_size(sigma, ngood, 1);
2086 
2087 
2088  /*
2089  * Configure the optical model fit.
2090  */
2091 
2092  giraffe_model_freeze(model);
2093 
2094  if (setup->flags & OPTM_FLENGTH) {
2095  giraffe_model_thaw_parameter(model, "FocalLength");
2096  }
2097 
2098  if (setup->flags & OPTM_GCAMERA) {
2099  giraffe_model_thaw_parameter(model, "Magnification");
2100  }
2101 
2102  if (setup->flags & OPTM_THETA) {
2103  giraffe_model_thaw_parameter(model, "Angle");
2104  }
2105 
2106  if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
2107  if (setup->flags & OPTM_SX) {
2108  giraffe_model_thaw_parameter(model, "Sdx");
2109  }
2110 
2111  if (setup->flags & OPTM_SY) {
2112  giraffe_model_thaw_parameter(model, "Sdy");
2113  }
2114 
2115  if (setup->flags & OPTM_SPHI) {
2116  giraffe_model_thaw_parameter(model, "Sphi");
2117  }
2118  }
2119 
2120  giraffe_model_set_iterations(model, setup->fit.iterations);
2121  giraffe_model_set_tests(model, setup->fit.tests);
2122  giraffe_model_set_delta(model, setup->fit.delta);
2123 
2124 
2125  /*
2126  * Fit the model
2127  */
2128 
2129  status = giraffe_model_fit(model, x, y, sigma);
2130 
2131  if (status < 0) {
2132 
2133  cpl_matrix_delete(x);
2134  cpl_matrix_delete(y);
2135  cpl_matrix_delete(sigma);
2136 
2137  return 2;
2138 
2139  }
2140 
2141  cpl_matrix_delete(x);
2142  cpl_matrix_delete(y);
2143  cpl_matrix_delete(sigma);
2144 
2145  return 0;
2146 
2147 }
2148 
2149 
2150 inline static cxint
2151 _giraffe_opticalmodel_format(cx_string *s, const GiModel *model,
2152  GiOpticalModelInfo info)
2153 {
2154 
2155  const cxchar *name = NULL;
2156 
2157  cxbool offsets = FALSE;
2158 
2159  cxint status = 0;
2160 
2161 
2162  cx_assert(s != NULL);
2163 
2164  if (model == NULL) {
2165  return -1;
2166  }
2167 
2168  name = giraffe_model_get_name(model);
2169 
2170  if (name == NULL || strncmp(name, "xoptmod", 7) != 0) {
2171  return -2;
2172  }
2173  else {
2174  if (strncmp(name, "xoptmod2", 8) == 0) {
2175  offsets = TRUE;
2176  }
2177  }
2178 
2179  switch (info) {
2180 
2181  case GI_OPTM_PARAMETER_VALUES:
2182  {
2183 
2184  cxdouble fcoll = 0.;
2185  cxdouble gcam = 0.;
2186  cxdouble theta = 0.;
2187 
2188  fcoll = giraffe_model_get_parameter(model, "FocalLength");
2189  gcam = giraffe_model_get_parameter(model, "Magnification");
2190  theta = giraffe_model_get_parameter(model, "Angle");
2191 
2192  cx_string_sprintf(s, "focal length = %.6f, camera "
2193  "magnification = %.6f, grating angle = %.9f",
2194  fcoll, gcam, theta);
2195 
2196  if (offsets == TRUE) {
2197 
2198  cxdouble sdx = 0.;
2199  cxdouble sdy = 0.;
2200  cxdouble sphi = 0.;
2201 
2202  cx_string *_s = cx_string_new();
2203 
2204  sdx = giraffe_model_get_parameter(model, "Sdx");
2205  sdy = giraffe_model_get_parameter(model, "Sdy");
2206  sphi = giraffe_model_get_parameter(model, "Sphi");
2207 
2208  cx_string_sprintf(_s, ", slit x-shift = %.9f, slit "
2209  "y-shift = %.9f, slit rotation = %.9f",
2210  sdx, sdy, sphi);
2211  cx_string_append(s, cx_string_get(_s));
2212 
2213  cx_string_delete(_s);
2214  _s = NULL;
2215 
2216  }
2217 
2218  break;
2219  }
2220 
2221  case GI_OPTM_PARAMETER_ERRORS:
2222  {
2223 
2224  cxdouble fcoll = 0.;
2225  cxdouble gcam = 0.;
2226  cxdouble theta = 0.;
2227 
2228  fcoll = giraffe_model_get_sigma(model, "FocalLength");
2229  gcam = giraffe_model_get_sigma(model, "Magnification");
2230  theta = giraffe_model_get_sigma(model, "Angle");
2231 
2232  cx_string_sprintf(s, "focal length = %.6f, camera "
2233  "magnification = %.6f, grating angle = %.9f",
2234  fcoll, gcam, theta);
2235 
2236  if (offsets == TRUE) {
2237 
2238  cxdouble sdx = 0.;
2239  cxdouble sdy = 0.;
2240  cxdouble sphi = 0.;
2241 
2242  cx_string *_s = cx_string_new();
2243 
2244  sdx = giraffe_model_get_sigma(model, "Sdx");
2245  sdy = giraffe_model_get_sigma(model, "Sdy");
2246  sphi = giraffe_model_get_sigma(model, "Sphi");
2247 
2248  cx_string_sprintf(_s, ", slit x-shift = %.9f, slit "
2249  "y-shift = %.9f, slit rotation = %.9f",
2250  sdx, sdy, sphi);
2251  cx_string_append(s, cx_string_get(_s));
2252 
2253  cx_string_delete(_s);
2254  _s = NULL;
2255 
2256  }
2257 
2258  break;
2259  }
2260 
2261  case GI_OPTM_PARAMETER_STATUS:
2262  {
2263 
2264  const cxchar *const s_free = "free";
2265  const cxchar *const s_frozen = "frozen";
2266  const cxchar *t = NULL;
2267 
2268  cx_string *buffer = cx_string_new();
2269 
2270 
2271  t = giraffe_model_frozen_parameter(model, "FocalLength") ?
2272  s_frozen : s_free;
2273  cx_string_sprintf(buffer, "focal length = %s", t);
2274  cx_string_set(s, cx_string_get(buffer));
2275 
2276  t = giraffe_model_frozen_parameter(model, "Magnification") ?
2277  s_frozen : s_free;
2278  cx_string_sprintf(buffer, ", camera magnification = %s", t);
2279  cx_string_append(s, cx_string_get(buffer));
2280 
2281  t = giraffe_model_frozen_parameter(model, "Angle") ?
2282  s_frozen : s_free;
2283  cx_string_sprintf(buffer, ", grating angle = %s", t);
2284  cx_string_append(s, cx_string_get(buffer));
2285 
2286 
2287  if (offsets == TRUE) {
2288 
2289  t = giraffe_model_frozen_parameter(model, "Sdx") ?
2290  s_frozen : s_free;
2291  cx_string_sprintf(buffer, ", slit x-shift = %s", t);
2292  cx_string_append(s, cx_string_get(buffer));
2293 
2294  t = giraffe_model_frozen_parameter(model, "Sdy") ?
2295  s_frozen : s_free;
2296  cx_string_sprintf(buffer, ", slit y-shift = %s", t);
2297  cx_string_append(s, cx_string_get(buffer));
2298 
2299  t = giraffe_model_frozen_parameter(model, "Sphi") ?
2300  s_frozen : s_free;
2301  cx_string_sprintf(buffer, ", slit rotation = %s", t);
2302  cx_string_append(s, cx_string_get(buffer));
2303 
2304  }
2305 
2306  cx_string_delete(buffer);
2307  buffer = NULL;
2308 
2309  break;
2310  }
2311 
2312  default:
2313  status = -2;
2314  break;
2315 
2316  }
2317 
2318  return status;
2319 
2320 }
2321 
2322 
2323 inline static cpl_image *
2324 _giraffe_residuals_fit(GiWlResiduals *residuals, GiLineData *lines,
2325  const GiLocalization *localization, GiTable *fibers,
2326  GiTable *slitgeometry, GiSCFitParams *setup)
2327 {
2328 
2329  const cxchar *const fctid = "_giraffe_residuals_fit";
2330 
2331 
2332  cxint i;
2333  cxint status = 0;
2334  cxint ngood = 0;
2335  cxint nlines = 0;
2336  cxint nsubslits = 1;
2337  cxint nx = 0;
2338 
2339  cpl_table *_fibers = NULL;
2340 
2341  cpl_image *locy = NULL;
2342  cpl_image *locw = NULL;
2343  cpl_image *xresiduals = NULL;
2344 
2345 
2346  cx_assert(lines != NULL);
2347  cx_assert(localization != NULL);
2348  cx_assert(fibers != NULL);
2349  cx_assert(slitgeometry != NULL);
2350  cx_assert(setup != NULL);
2351 
2352  _fibers = giraffe_table_get(fibers);
2353  cx_assert(_fibers != NULL);
2354 
2355  locy = giraffe_image_get(localization->locy);
2356  cx_assert(locy != NULL);
2357 
2358  locw = giraffe_image_get(localization->locw);
2359  cx_assert(locw != NULL);
2360 
2361  nx = cpl_image_get_size_y(locy);
2362  nlines = giraffe_linedata_lines(lines);
2363 
2364  xresiduals = cpl_image_new(cpl_table_get_nrow(_fibers), nx,
2365  CPL_TYPE_DOUBLE);
2366 
2367  if (setup->subslits == TRUE) {
2368  nsubslits = _giraffe_subslit_get_max(_fibers);
2369  }
2370 
2371  for (i = 0; i < nsubslits; i++) {
2372 
2373  cxint j;
2374  cxint k;
2375  cxint ssn = 0;
2376  cxint nfibers = 0;
2377  cxint ndata = 0;
2378  cxint iterations = 0;
2379  cxint accepted = 0;
2380  cxint total = 0;
2381 
2382  cxdouble ymin = 0.;
2383  cxdouble ymax = 0.;
2384  cxdouble ratio = 1.;
2385  cxdouble sigma = 0.;
2386 
2387  cpl_matrix *xss = NULL;
2388  cpl_matrix *yss = NULL;
2389  cpl_matrix *rss = NULL;
2390  cpl_matrix *sss = NULL;
2391  cpl_matrix *nss = NULL;
2392  cpl_matrix *lss = NULL;
2393  cpl_matrix *base = NULL;
2394  cpl_matrix *fit = NULL;
2395  cpl_matrix *coeff = NULL;
2396  cpl_matrix *chebyshev = NULL;
2397 
2398  cpl_table *subslit = NULL;
2399 
2400  GiChebyshev2D *xwsfit = NULL;
2401 
2402 
2403  if (setup->subslits == TRUE) {
2404  subslit = _giraffe_subslit_get(_fibers, i + 1);
2405  ssn = cpl_table_get_int(subslit, "SSN", 0, NULL);
2406 
2407  cx_assert(ssn == i + 1);
2408  }
2409  else {
2410  subslit = cpl_table_duplicate(_fibers);
2411  ssn = 0;
2412  }
2413 
2414  if (subslit == NULL) {
2415  continue;
2416  }
2417 
2418  _giraffe_subslit_range(subslit, locy, locw, &ymin, &ymax);
2419 
2420  nfibers = cpl_table_get_nrow(subslit);
2421  ndata = nfibers * nlines;
2422 
2423 
2424  xss = cpl_matrix_new(ndata, 1); /* X abcissas for subslit */
2425  yss = cpl_matrix_new(ndata, 1); /* Y ordinates */
2426  rss = cpl_matrix_new(1, ndata); /* widths (transposed) */
2427  sss = cpl_matrix_new(ndata, 1); /* width sigmas */
2428  nss = cpl_matrix_new(ndata, 1); /* fiber indices */
2429  lss = cpl_matrix_new(ndata, 1); /* line indices */
2430 
2431 
2432  /*
2433  * Fills inputs matrices with good lines xccd, yccd, width,
2434  * width sigmas and line status
2435  */
2436 
2437  k = 0;
2438 
2439  for (j = 0; j < nfibers; j++) {
2440 
2441  cxint l;
2442  cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
2443 
2444  for (l = 0; l < nlines; l++) {
2445 
2446  cxdouble value = 0.;
2447  cxdouble xccd = giraffe_linedata_get(lines, "Xccd", n, l);
2448  cxdouble yccd = giraffe_linedata_get(lines, "Yccd", n, l);
2449 
2450  if (giraffe_linedata_get_status(lines, n, l) != 0) {
2451  continue;
2452  }
2453 
2454  if (yccd < ymin || yccd > ymax) {
2455  continue;
2456  }
2457 
2458  cpl_matrix_set(xss, k, 0, xccd);
2459  cpl_matrix_set(yss, k, 0, yccd);
2460 
2461  value = xccd - giraffe_linedata_get(lines, "Center", n, l);
2462 
2463  cpl_matrix_set(rss, 0, k, value);
2464  giraffe_linedata_set(lines, "Xoff", n, l, value);
2465 
2466  value = giraffe_linedata_get(lines, "dCenter", n, l);
2467  cpl_matrix_set(sss, k, 0, value);
2468 
2469  cpl_matrix_set(nss, k, 0, n);
2470  cpl_matrix_set(lss, k, 0, l);
2471 
2472  ++k;
2473 
2474  }
2475 
2476  }
2477 
2478  if (k == 0) {
2479  cpl_msg_debug(fctid, "Skipping subslit %d: No input lines left! "
2480  "All lines have non-zero status or are beyond the "
2481  "subslit boundaries (%.4f, %.4f).", ssn, ymin, ymax);
2482  continue;
2483  }
2484 
2485  /*
2486  * Shrink input matrices to their actual size
2487  */
2488 
2489  cpl_matrix_set_size(xss, k, 1);
2490  cpl_matrix_set_size(yss, k, 1);
2491  cpl_matrix_set_size(rss, 1, k);
2492  cpl_matrix_set_size(sss, k, 1);
2493  cpl_matrix_set_size(nss, k, 1);
2494  cpl_matrix_set_size(lss, k, 1);
2495 
2496 
2497  /*
2498  * The sigma value used for the sigma-clipping is the
2499  * median value of the line center uncertainties.
2500  */
2501 
2502  sigma = cpl_matrix_get_median(sss);
2503 
2504 
2505  /*
2506  * Sigma clipping
2507  */
2508 
2509  iterations = 0;
2510  ratio = 1.0;
2511  accepted = cpl_matrix_get_ncol(rss);
2512  total = accepted;
2513 
2514  while (accepted > 0 && iterations < setup->clip.iterations &&
2515  ratio > setup->clip.fraction) {
2516 
2517  base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
2518  setup->fit.xorder + 1,
2519  setup->fit.yorder + 1, xss, yss);
2520 
2521  if (coeff != NULL) {
2522  cpl_matrix_delete(coeff);
2523  coeff = NULL;
2524  }
2525 
2526  coeff = giraffe_matrix_leastsq(base, rss);
2527 
2528  if (coeff == NULL) {
2529  cpl_msg_debug(fctid, "Error solving linear system for "
2530  "subslit %d, skipping subslit.", ssn);
2531  break;
2532  }
2533 
2534  fit = cpl_matrix_product_create(coeff, base);
2535 
2536  k = 0;
2537 
2538  for (j = 0; j < cpl_matrix_get_ncol(fit); j++) {
2539 
2540  cxdouble _fit = cpl_matrix_get(fit, 0, j);
2541  cxdouble _rss = cpl_matrix_get(rss, 0, j);
2542 
2543  if (fabs(_fit - _rss) >= setup->clip.level * sigma) {
2544 
2545  cxint n = (cxint)cpl_matrix_get(nss, j, 0);
2546  cxint l = (cxint)cpl_matrix_get(lss, j, 0);
2547 
2548  /*
2549  * Reject this line
2550  */
2551 
2552  giraffe_linedata_set_status(lines, n, l, LF_R_XRFIT);
2553  continue;
2554 
2555  }
2556 
2557  cpl_matrix_set(xss, k, 0, cpl_matrix_get(xss, j, 0));
2558  cpl_matrix_set(yss, k, 0, cpl_matrix_get(yss, j, 0));
2559  cpl_matrix_set(rss, 0, k, cpl_matrix_get(rss, 0, j));
2560  cpl_matrix_set(sss, k, 0, cpl_matrix_get(sss, j, 0));
2561  cpl_matrix_set(nss, k, 0, cpl_matrix_get(nss, j, 0));
2562  cpl_matrix_set(lss, k, 0, cpl_matrix_get(lss, j, 0));
2563  ++k;
2564 
2565  }
2566 
2567  cpl_matrix_delete(base);
2568  cpl_matrix_delete(fit);
2569 
2570  if (k == accepted) {
2571 
2572  /*
2573  * No new points rejected, no more iterations. That's it.
2574  */
2575 
2576  break;
2577  }
2578  else {
2579  accepted = k;
2580  ratio = (cxdouble)accepted / (cxdouble)total;
2581 
2582  cpl_matrix_set_size(xss, k, 1);
2583  cpl_matrix_set_size(yss, k, 1);
2584  cpl_matrix_set_size(rss, 1, k);
2585  cpl_matrix_set_size(sss, k, 1);
2586  cpl_matrix_set_size(nss, k, 1);
2587  cpl_matrix_set_size(lss, k, 1);
2588 
2589  ++iterations;
2590 
2591  }
2592 
2593  }
2594 
2595  if (accepted == 0) {
2596  cpl_msg_debug(fctid, "Subslit %d: All lines rejected.", ssn);
2597  continue;
2598  }
2599 
2600  if (coeff == NULL) {
2601  continue;
2602  }
2603 
2604  ngood += accepted;
2605 
2606  cpl_matrix_delete(xss);
2607  cpl_matrix_delete(yss);
2608  cpl_matrix_delete(rss);
2609  cpl_matrix_delete(sss);
2610  cpl_matrix_delete(nss);
2611  cpl_matrix_delete(lss);
2612 
2613 
2614  /*
2615  * Compute coordinate grid for the whole subslit
2616  */
2617 
2618  xss = cpl_matrix_new(nx * nfibers, 1);
2619  yss = cpl_matrix_new(nx * nfibers, 1);
2620 
2621  giraffe_compute_image_coordinates(nx, nfibers, xss, NULL);
2622 
2623  for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
2624 
2625  const cxchar *idx = giraffe_fiberlist_query_index(subslit);
2626 
2627  cxint l;
2628  cxint ns = cpl_image_get_size_x(locy);
2629  cxint cs = cpl_table_get_int(subslit, idx, j, NULL) - 1;
2630 
2631  cxdouble *data = cpl_image_get_data(locy);
2632 
2633 
2634  for (l = 0; l < nx; l++) {
2635  cpl_matrix_set(yss, l * nfibers + j, 0, data[l * ns + cs]);
2636  }
2637 
2638  }
2639 
2640 
2641  /*
2642  * Compute fitted residuals on the whole subslit
2643  */
2644 
2645  base = giraffe_chebyshev_base2d(0., ymin, nx, ymax - ymin + 1.,
2646  setup->fit.xorder + 1,
2647  setup->fit.yorder + 1, xss, yss);
2648 
2649  fit = cpl_matrix_product_create(coeff, base);
2650 
2651  cpl_matrix_delete(xss);
2652  xss = NULL;
2653 
2654  cpl_matrix_delete(yss);
2655  yss = NULL;
2656 
2657 
2658  /*
2659  * Insert the Chebyshev fit into the results structure
2660  */
2661 
2662  /*
2663  * The matrix 'chebyshev' is a view into 'coeff' with the correct
2664  * shape for the subsequent call of giraffe_chebyshev2d_new().
2665  * Therefore, if 'chebyshev' is destroyed afterwards the data
2666  * of the matrix must not be destroyed!
2667  */
2668 
2669  chebyshev = cpl_matrix_wrap(setup->fit.xorder + 1,
2670  setup->fit.yorder + 1,
2671  cpl_matrix_get_data(coeff));
2672 
2673  xwsfit = giraffe_chebyshev2d_new(setup->fit.xorder, setup->fit.yorder);
2674  status = giraffe_chebyshev2d_set(xwsfit, 0., nx, ymin, ymax,
2675  chebyshev);
2676 
2677  if (status != 0) {
2678 
2679  giraffe_chebyshev2d_delete(xwsfit);
2680 
2681  cpl_matrix_unwrap(chebyshev);
2682 
2683  cpl_matrix_delete(base);
2684  cpl_matrix_delete(coeff);
2685  cpl_matrix_delete(fit);
2686 
2687  cpl_table_delete(subslit);
2688 
2689  cpl_image_delete(xresiduals);
2690 
2691  return NULL;
2692 
2693  }
2694 
2695  cpl_matrix_unwrap(chebyshev);
2696  chebyshev = NULL;
2697 
2698  giraffe_wlresiduals_set(residuals, ssn, xwsfit);
2699  xwsfit = NULL;
2700 
2701 
2702  /*
2703  * Save fitted optical model residuals to the output image.
2704  */
2705 
2706  for (j = 0; j < cpl_table_get_nrow(subslit); j++) {
2707 
2708  cxint l;
2709  cxint n = cpl_table_get_int(subslit, "INDEX", j, NULL) - 1;
2710  cxint ns = cpl_table_get_nrow(_fibers);
2711 
2712  cxdouble *data = cpl_image_get_data(xresiduals);
2713 
2714  for (l = 0; l < nx; l++) {
2715  data[l * ns + n] = cpl_matrix_get(fit, 0, l * nfibers + j);
2716  }
2717 
2718  }
2719 
2720  cpl_matrix_delete(base);
2721  cpl_matrix_delete(coeff);
2722  cpl_matrix_delete(fit);
2723 
2724  cpl_table_delete(subslit);
2725 
2726  }
2727 
2728  return xresiduals;
2729 
2730 }
2731 
2732 
2733 inline static cxdouble
2734 _giraffe_compute_statistics(const GiLineData *lines, const cpl_image *xwsfit,
2735  const cpl_image *lflags)
2736 {
2737 
2738  cxint i;
2739  cxint nlines = 0;
2740  cxint nfibers = 0;
2741 
2742  cxdouble sum = 0.;
2743  cxdouble rms = 0.;
2744  cxdouble *_xccd;
2745 
2746  cpl_image *xccd = NULL;
2747 
2748 
2749  cx_assert(lines != NULL);
2750 
2751 
2752  nlines = giraffe_linedata_lines(lines);
2753  nfibers = giraffe_linedata_fibers(lines);
2754 
2755  xccd = cpl_image_duplicate(giraffe_linedata_get_data(lines, "Xccd"));
2756 
2757  if (xwsfit != NULL) {
2758 
2759  cpl_image *residuals = NULL;
2760 
2761 
2762  cx_assert(lflags != NULL);
2763 
2764  residuals = cpl_image_new(giraffe_linedata_fibers(lines),
2765  giraffe_linedata_lines(lines),
2766  CPL_TYPE_DOUBLE);
2767 
2768  _giraffe_get_residuals(residuals, xccd, xwsfit);
2769  _giraffe_apply_residuals(xccd, residuals, lflags, 0.);
2770 
2771  cpl_image_delete(residuals);
2772  residuals = NULL;
2773 
2774  }
2775 
2776  _xccd = cpl_image_get_data(xccd);
2777 
2778  for (i = 0; i < nfibers; i++) {
2779 
2780  cxint j;
2781 
2782  for (j = 0; j < nlines; j++) {
2783 
2784  if (giraffe_linedata_get_status(lines, i, j) == LF_R_NONE) {
2785 
2786  cxdouble center = giraffe_linedata_get(lines, "Center", i, j);
2787 
2788  sum += pow(center - _xccd[j * nfibers + i], 2.);
2789 
2790  }
2791 
2792  }
2793 
2794  }
2795 
2796  cpl_image_delete(xccd);
2797 
2798  rms = sqrt(sum / CX_MAX(giraffe_linedata_accepted(lines), 1.));
2799 
2800  return rms;
2801 
2802 }
2803 
2804 
2805 inline static cxint
2806 _giraffe_convert_wlsolution(GiTable *result, const GiWlSolution *solution,
2807  const GiImage *spectra, const GiGrating *setup,
2808  const GiWCalInfo *info, const GiWCalConfig *config)
2809 {
2810 
2811  cxint i;
2812  cxint status = 0;
2813  cxint sign = 1;
2814 
2815  cxdouble value = 0.;
2816  cxdouble scale = 0.;
2817  cxdouble xccd[2] = {0., 0.};
2818 
2819  cx_string *s = NULL;
2820 
2821  cpl_propertylist *properties = NULL;
2822 
2823  cpl_table *coeffs = NULL;
2824 
2825  const GiModel *model = NULL;
2826 
2827  const GiWlResiduals *residuals = NULL;
2828 
2829 
2830  cx_assert(result != NULL);
2831  cx_assert(solution != NULL);
2832 
2833  s = cx_string_new();
2834 
2835  properties =
2836  cpl_propertylist_duplicate(giraffe_image_get_properties(spectra));
2837 
2838 
2839  /* FIXME: Check whether this is still necessary! (RP)
2840  */
2841 
2842  cpl_propertylist_erase(properties, "NAXIS1");
2843  cpl_propertylist_erase(properties, "NAXIS2");
2844  cpl_propertylist_erase(properties, GIALIAS_DATAMIN);
2845  cpl_propertylist_erase(properties, GIALIAS_DATAMAX);
2846  cpl_propertylist_erase(properties, GIALIAS_EXTNAME);
2847 
2848  cpl_propertylist_erase(properties, GIALIAS_PROCATG);
2849  cpl_propertylist_erase(properties, GIALIAS_PROTYPE);
2850  cpl_propertylist_erase(properties, GIALIAS_DATAMEAN);
2851  cpl_propertylist_erase(properties, GIALIAS_DATAMEDI);
2852  cpl_propertylist_erase(properties, GIALIAS_DATASIG);
2853 
2854  cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
2855  "WAVCOEFFTAB");
2856  cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
2857  "Giraffe frame type.");
2858 
2859 
2860  /*
2861  * Grating data
2862  */
2863 
2864  /* FIXME: Is this needed? (RP)
2865  */
2866 
2867  /*
2868  cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRORDER,
2869  setup->order);
2870  cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRTHETA,
2871  setup->theta);
2872  cpl_propertylist_update_double(properties, GIALIAS_WSOL_GRSPACE,
2873  setup->space);
2874  */
2875 
2876 
2877  /*
2878  * Write line model parameters
2879  */
2880 
2881  cpl_propertylist_update_string(properties, GIALIAS_WSOL_LMNAME,
2882  config->line_model);
2883  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMNAME,
2884  "Line profile model");
2885 
2886  cpl_propertylist_update_bool(properties, GIALIAS_WSOL_LMRES, info->residuals);
2887  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMRES,
2888  "Line detection optical model residuals flag");
2889 
2890  cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMWIDTH, info->width);
2891  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMWIDTH,
2892  "Line detection window size [pxl]");
2893 
2894  cpl_propertylist_update_double(properties, GIALIAS_WSOL_LMTHRESH,
2895  config->line_threshold);
2896  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMTHRESH,
2897  "Calibration line threshold");
2898 
2899  cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMITER,
2900  config->line_niter);
2901  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMITER,
2902  "Line profile fit maximum number "
2903  "of iterations");
2904 
2905  cpl_propertylist_update_int(properties, GIALIAS_WSOL_LMTEST,
2906  config->line_ntest);
2907  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMTEST,
2908  "Line profile fit maximum number "
2909  "of chi-square tests");
2910 
2911  cpl_propertylist_update_double(properties, GIALIAS_WSOL_LMDCHISQ,
2912  config->line_dchisq);
2913  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_LMDCHISQ,
2914  "Line profile fit minimum delta "
2915  "chi-square");
2916 
2917 
2918  /*
2919  * Write PSF width parameters
2920  */
2921 
2922  cx_string_sprintf(s, "%d:%d", config->pxw_xorder, config->pxw_yorder);
2923 
2924  cpl_propertylist_update_string(properties, GIALIAS_WSOL_PWORDER,
2925  cx_string_get(s));
2926  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWORDER,
2927  "PSF width fit polynomial order");
2928 
2929  cpl_propertylist_update_double(properties, GIALIAS_WSOL_PWSIGMA,
2930  config->pxw_cliplevel);
2931  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWSIGMA,
2932  "PSF width fit sigma clipping level");
2933 
2934  cpl_propertylist_update_int(properties, GIALIAS_WSOL_PWITER,
2935  config->pxw_clipniter);
2936  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWITER,
2937  "PSF width fit maximum number of "
2938  "iterations");
2939 
2940  cpl_propertylist_update_double(properties, GIALIAS_WSOL_PWFRAC,
2941  config->pxw_clipmfrac);
2942  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_PWFRAC,
2943  "PSF width fit minimum fraction of "
2944  "accepted points");
2945 
2946 
2947  /*
2948  * Write optical model parameters to the result table
2949  */
2950 
2951  cpl_propertylist_update_bool(properties, GIALIAS_WSOL_OMFIT,
2952  config->opt_solution);
2953  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMFIT,
2954  "Optical model fit flag");
2955 
2956  cpl_propertylist_update_string(properties, GIALIAS_WSOL_OMNAME,
2957  giraffe_wlsolution_name(solution));
2958  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMNAME,
2959  "Optical model name");
2960 
2961  model = giraffe_wlsolution_model(solution);
2962 
2963  sign = giraffe_model_get_parameter(model,"Orientation") < 0 ? -1 : 1;
2964  cpl_propertylist_update_int(properties, GIALIAS_WSOL_OMDIR, sign);
2965  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMDIR,
2966  "Optical model orientation");
2967 
2968  value = giraffe_model_get_parameter(model, "FocalLength");
2969  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMFCOLL, value);
2970  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMFCOLL,
2971  "Optical model focal length");
2972 
2973  value = giraffe_model_get_parameter(model, "Magnification");
2974  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMGCAM, value);
2975  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMGCAM,
2976  "Optical model camera factor");
2977 
2978  value = giraffe_model_get_parameter(model, "Angle");
2979  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMGTHETA, value);
2980  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMGTHETA,
2981  "Optical model grating angle");
2982 
2983  if (strcmp(giraffe_wlsolution_name(solution), "xoptmod2") == 0) {
2984 
2985  value = giraffe_model_get_parameter(model, "Sdx");
2986  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSDX, value);
2987  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSDX,
2988  "Optical model slit x-offset");
2989 
2990  value = giraffe_model_get_parameter(model, "Sdy");
2991  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSDY, value);
2992  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSDY,
2993  "Optical model slit y-offset");
2994 
2995  value = giraffe_model_get_parameter(model, "Sphi");
2996  cpl_propertylist_update_double(properties, GIALIAS_WSOL_OMSPHI, value);
2997  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_OMSPHI,
2998  "Optical model slit rotation");
2999 
3000  }
3001 
3002 
3003  /*
3004  * Add the optical model residuals fit setup parameters
3005  */
3006 
3007 
3008  residuals = giraffe_wlsolution_get_residuals(solution);
3009 
3010  cpl_propertylist_update_bool(properties, GIALIAS_WSOL_SUBSLITS,
3011  giraffe_wlsolution_get_subslits(solution));
3012  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_SUBSLITS,
3013  "Subslit fit flag");
3014 
3015 
3016  cpl_propertylist_update_int(properties, GIALIAS_WSOL_XRSSN,
3017  giraffe_wlresiduals_get_size(residuals));
3018  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRSSN,
3019  "Number of subslits");
3020 
3021 
3022  cx_string_sprintf(s, "%d:%d", config->xws_xorder, config->xws_yorder);
3023 
3024  cpl_propertylist_update_string(properties, GIALIAS_WSOL_XRORDER,
3025  cx_string_get(s));
3026  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRORDER,
3027  "Residual fit polynomial order");
3028 
3029 
3030  cpl_propertylist_update_double(properties, GIALIAS_WSOL_XRSIGMA,
3031  config->xws_cliplevel);
3032  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRSIGMA,
3033  "Residual fit sigma clipping level");
3034 
3035  cpl_propertylist_update_int(properties, GIALIAS_WSOL_XRITER,
3036  config->xws_clipniter);
3037  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRITER,
3038  "Residual fit maximum number of "
3039  "iterations");
3040 
3041  cpl_propertylist_update_double(properties, GIALIAS_WSOL_XRFRAC,
3042  config->xws_clipmfrac);
3043  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_XRFRAC,
3044  "Residual fit minimum fraction of "
3045  "accepted points");
3046 
3047  cpl_propertylist_update_int(properties, GIALIAS_WSOL_NLINES,
3048  info->nlines);
3049  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NLINES,
3050  "Number of calibration lines used.");
3051 
3052  cpl_propertylist_update_int(properties, GIALIAS_WSOL_NACCEPT,
3053  info->ngood);
3054  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NACCEPT,
3055  "Number of accepted lines");
3056 
3057  cpl_propertylist_update_int(properties, GIALIAS_WSOL_NREJECT,
3058  info->nreject);
3059  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_NREJECT,
3060  "Number of rejected lines");
3061 
3062  cpl_propertylist_update_double(properties, GIALIAS_WSOL_RMS,
3063  info->rms);
3064  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_RMS,
3065  "Average RMS [pxl] of fitted line "
3066  "positions");
3067 
3068 
3069  /*
3070  * Compute approximated pixel to wavelength scale factor
3071  * at the slit center.
3072  */
3073 
3074  xccd[0] = giraffe_wlsolution_compute_pixel(solution, setup->wlenmin,
3075  0., 0., &status);
3076  xccd[1] = giraffe_wlsolution_compute_pixel(solution, setup->wlenmax,
3077  0., 0., &status);
3078 
3079  scale = (setup->wlenmax - setup->wlenmin) / (xccd[1] - xccd[0]);
3080 
3081 
3082  cpl_propertylist_update_double(properties, GIALIAS_WSOL_WLMIN,
3083  setup->wlenmin);
3084  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_WLMIN,
3085  "Wavelength solution minimum wavelength");
3086 
3087  cpl_propertylist_update_double(properties, GIALIAS_WSOL_WLMAX,
3088  setup->wlenmax);
3089  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_WLMAX,
3090  "Wavelength solution maximum wavelength");
3091 
3092  cpl_propertylist_update_double(properties, GIALIAS_WSOL_SCALE, scale);
3093  cpl_propertylist_set_comment(properties, GIALIAS_WSOL_SCALE,
3094  "Approximate wavelength scale [nm/pxl]");
3095 
3096 
3097  cx_string_delete(s);
3098  s = NULL;
3099 
3100 
3101  /*
3102  * Consistency check of optical model residuals fit coefficients.
3103  */
3104 
3105  for (i = 0; (cxsize)i < giraffe_wlresiduals_get_size(residuals); i++) {
3106 
3107  const GiChebyshev2D *fit = giraffe_wlresiduals_get(residuals, i);
3108 
3109 
3110  if (fit != NULL) {
3111 
3112  cxint xorder = 0;
3113  cxint yorder = 0;
3114 
3115 
3116  giraffe_chebyshev2d_get_order(fit, &xorder, &yorder);
3117 
3118  if (xorder != config->xws_xorder || yorder != config->xws_yorder) {
3119 
3120  gi_error("Invalid wavelength solution. Inconsistent residual "
3121  "fit polynomial order!");
3122 
3123  }
3124 
3125  }
3126 
3127  }
3128 
3129 
3130  /*
3131  * Write optical model residuals fit coefficients.
3132  */
3133 
3134  coeffs = giraffe_wlresiduals_table(residuals);
3135 
3136  if (coeffs == NULL) {
3137  cpl_propertylist_delete(properties);
3138  return 1;
3139  }
3140 
3141  giraffe_table_set_properties(result, properties);
3142  cpl_propertylist_delete(properties);
3143  properties = NULL;
3144 
3145  giraffe_table_set(result, coeffs);
3146 
3147  cpl_table_delete(coeffs);
3148  coeffs = NULL;
3149 
3150  return 0;
3151 
3152 }
3153 
3154 
3155 GiWCalData *
3156 giraffe_wcaldata_new(void)
3157 {
3158 
3159  GiWCalData *self = cx_calloc(1, sizeof *self);
3160 
3161  self->coeffs = NULL;
3162  self->lines = NULL;
3163  self->linedata = NULL;
3164 
3165  return self;
3166 
3167 }
3168 
3169 
3170 void
3171 giraffe_wcaldata_delete(GiWCalData *self)
3172 {
3173 
3174  if (self) {
3175 
3176  if (self->coeffs) {
3177  giraffe_table_delete(self->coeffs);
3178  self->coeffs = NULL;
3179  }
3180 
3181  if (self->lines) {
3182  giraffe_table_delete(self->lines);
3183  self->lines = NULL;
3184  }
3185 
3186  if (self->linedata) {
3187  giraffe_linedata_delete(self->linedata);
3188  self->linedata = NULL;
3189  }
3190 
3191  cx_free(self);
3192 
3193  }
3194 
3195  return;
3196 
3197 }
3198 
3199 
3221 cxint
3222 giraffe_calibrate_wavelength(GiWCalData *result, GiExtraction *extraction,
3223  GiLocalization *localization, GiTable *fibers,
3224  GiTable *slitgeometry, GiTable *grating,
3225  GiTable *lines, GiTable *initial,
3226  GiWCalConfig *config)
3227 {
3228 
3229  const cxchar *fctid = "giraffe_calibrate_wavelength";
3230 
3231 
3232  cxbool residuals = FALSE;
3233 
3234  cxint i;
3235  cxint status = 0;
3236  cxint nlines = 0;
3237  cxint width = 0;
3238 
3239  cxdouble rms = 0.;
3240 
3241  cpl_image *psf_fit = NULL;
3242  cpl_image *xws_fit = NULL;
3243 
3244  GiTable *tsolution = NULL;
3245 
3246  GiGrating *setup = NULL;
3247 
3248  GiSCFitParams psf_setup;
3249  GiSCFitParams xws_setup;
3250 
3251  GiLineParams *line_setup = NULL;
3252 
3253  GiLineData *line_data = NULL;
3254 
3255  GiWlSolution *solution = NULL;
3256 
3257  GiWCalInfo info;
3258 
3259 
3260  if (extraction == NULL) {
3261  return 1;
3262  }
3263 
3264  if (extraction->spectra == NULL || extraction->error == NULL) {
3265  return 1;
3266  }
3267 
3268 
3269  if (fibers == NULL) {
3270  return 1;
3271  }
3272 
3273  if (slitgeometry == NULL) {
3274  return 1;
3275  }
3276 
3277  if (grating == NULL) {
3278  return 1;
3279  }
3280 
3281  if (lines == NULL) {
3282  return 1;
3283  }
3284 
3285 
3286  /*
3287  * Setup grating data
3288  */
3289 
3290  setup = giraffe_grating_create(extraction->spectra, grating);
3291 
3292  if (setup == NULL) {
3293  cpl_msg_error(fctid, "Cannot initialize grating setup parameters!");
3294  return 2;
3295  }
3296 
3297  if (config->slit_position != 0) {
3298 
3299  if (config->slit_position & SLIT_DX) {
3300  setup->sdx = config->slit_dx;
3301  }
3302 
3303  if (config->slit_position & SLIT_DY) {
3304  setup->sdy = config->slit_dy;
3305  }
3306 
3307  if (config->slit_position & SLIT_PHI) {
3308  setup->sphi = config->slit_phi;
3309  }
3310 
3311  cpl_msg_info(fctid, "Setting initial slit offsets: x-shift = %.9f, "
3312  "y-shift = %.9f, rotation = %.9f", setup->sdx,
3313  setup->sdy, setup->sphi);
3314 
3315  }
3316 
3317 
3318  /*
3319  * Setup line fitting parameters
3320  */
3321 
3322  /* FIXME: The following assumes a fixed line type. If this should ever
3323  * be configurable (requires other than ThArNe line catalogs)
3324  * this has to be set correctly. (RP)
3325  */
3326 
3327  line_setup = _giraffe_lineparams_create(GI_LINETYPE_THARNE, setup,
3328  config);
3329 
3330  if (line_setup == NULL) {
3331  cpl_msg_error(fctid, "Cannot initialize line fit setup parameters!");
3332 
3333  giraffe_grating_delete(setup);
3334 
3335  return 3;
3336  }
3337 
3338 
3339  /*
3340  * Setup initial wavelength solution
3341  */
3342 
3343  if (initial == NULL) {
3344 
3345  cxint npixel = 0;
3346  cxdouble pixelsize = 0.;
3347 
3348  cpl_propertylist *properties = NULL;
3349 
3350  cpl_image *spectra = NULL;
3351 
3352 
3353  properties = giraffe_image_get_properties(extraction->spectra);
3354  cx_assert(properties != NULL);
3355 
3356  spectra = giraffe_image_get(extraction->spectra);
3357  cx_assert(spectra != NULL);
3358 
3359  pixelsize = cpl_propertylist_get_double(properties, GIALIAS_PIXSIZY);
3360  npixel = cpl_image_get_size_y(spectra);
3361 
3362  solution = giraffe_wlsolution_new(config->opt_model,
3363  config->opt_direction, npixel,
3364  pixelsize, setup);
3365 
3366  if (solution == NULL) {
3367  cpl_msg_error(fctid, "Cannot initialize initial wavelength "
3368  "solution!");
3369 
3370  giraffe_grating_delete(setup);
3371 
3372  _giraffe_lineparams_delete(line_setup);
3373 
3374  return 4;
3375  }
3376 
3377  }
3378  else {
3379 
3380  const cpl_propertylist* _properties =
3382 
3383 
3384  /*
3385  * Use a previous wavelength solution as the initial solution.
3386  */
3387 
3388  solution = giraffe_wlsolution_create(initial, extraction->spectra,
3389  setup);
3390 
3391  /*
3392  * Set the grating wavelength range to the minimum and maximum
3393  * values of the initial solution, if they are present.
3394  *
3395  * These values will then be used as the range for the line
3396  * selection.
3397  */
3398 
3399  if (cpl_propertylist_has(_properties, GIALIAS_WSOL_WLMIN) == TRUE) {
3400 
3401  setup->wlenmin = cpl_propertylist_get_double(_properties,
3402  GIALIAS_WSOL_WLMIN);
3403 
3404  cpl_msg_debug(fctid, "Using minimum wavelength %.2f from "
3405  "initial solution", setup->wlenmin);
3406 
3407  }
3408 
3409  if (cpl_propertylist_has(_properties, GIALIAS_WSOL_WLMAX) == TRUE) {
3410 
3411  setup->wlenmax = cpl_propertylist_get_double(_properties,
3412  GIALIAS_WSOL_WLMAX);
3413 
3414  cpl_msg_debug(fctid, "Using maximum wavelength %.2f from "
3415  "initial solution", setup->wlenmax);
3416 
3417  }
3418 
3419  }
3420 
3421  giraffe_wlsolution_set_subslits(solution, config->opt_subslits);
3422 
3423 
3424  cpl_msg_info(fctid, "Computing line positions on the CCD using "
3425  "model `%s'", giraffe_wlsolution_name(solution));
3426 
3427 
3428  /*
3429  * Check whether optical model residuals should be used.
3430  */
3431 
3432  if (strcmp(config->line_residuals, "enable") == 0) {
3433 
3434  if (giraffe_wlsolution_get_residuals(solution) == NULL) {
3435 
3436  cpl_msg_error(fctid, "Initial wavelength solution does not "
3437  "provide optical model residuals!");
3438 
3439  giraffe_grating_delete(setup);
3440 
3441  _giraffe_lineparams_delete(line_setup);
3442 
3443  giraffe_wlsolution_delete(solution);
3444 
3445  return 5;
3446  }
3447  else {
3448 
3449  residuals = TRUE;
3450 
3451  }
3452  }
3453  else if (strcmp(config->line_residuals, "auto") == 0) {
3454 
3455  residuals = giraffe_wlsolution_get_residuals(solution) != NULL;
3456 
3457  if (residuals == TRUE) {
3458  cpl_msg_info(fctid, "Using wavelength solution residuals when "
3459  "computing line positions.");
3460  }
3461 
3462  }
3463  else {
3464 
3465  residuals = FALSE;
3466 
3467  }
3468 
3469 
3470  /*
3471  * Basic selection of lines from the input line list
3472  */
3473 
3474  status = _giraffe_linelist_setup(lines, setup, config);
3475 
3476  if (status) {
3477  cpl_msg_error(fctid, "Line list creation failed!");
3478 
3479  giraffe_grating_delete(setup);
3480 
3481  _giraffe_lineparams_delete(line_setup);
3482 
3483  giraffe_wlsolution_delete(solution);
3484 
3485  return 6;
3486  }
3487 
3488  nlines = cpl_table_get_nrow(giraffe_table_get(lines));
3489  cpl_msg_info(fctid, "%d lines have been selected from the line list.",
3490  nlines);
3491 
3492 
3493  /*
3494  * Line fitting loop.
3495  */
3496 
3497  line_data = giraffe_linedata_new();
3498 
3499  for (i = 0; i < config->line_nwidths; i++) {
3500 
3501  cxint _nlines = 0;
3502  cxint _nfibers = 0;
3503  cxint _nreject = 0;
3504  cxint _ngood = 0;
3505 
3506  cpl_table *_lines = NULL;
3507  cpl_table *_fibers = giraffe_table_get(fibers);
3508 
3509  cpl_image *xccd = NULL;
3510  cpl_image *xres = NULL;
3511  cpl_image *line_status = NULL;
3512 
3513  GiWlResiduals *xws_coeffs = NULL;
3514 
3515 
3516 
3517  width = config->line_widths[i];
3518 
3519  cpl_msg_info(fctid, "Current search window width: %d pxl", width);
3520  cpl_msg_info(fctid, "Applying crowding criterium to line list.");
3521 
3522  _lines = _giraffe_linelist_select(lines, extraction->spectra, setup,
3523  width, config);
3524  if (_lines == NULL) {
3525  cpl_msg_error(fctid, "Removing crowded lines from line list "
3526  "for search window width %d pxl failed!", width);
3527 
3528  giraffe_grating_delete(setup);
3529 
3530  _giraffe_lineparams_delete(line_setup);
3531 
3532  giraffe_wlsolution_delete(solution);
3533 
3534  return 7;
3535  }
3536 
3537  _nlines = cpl_table_get_nrow(_lines);
3538 
3539 #if 0
3540  cpl_msg_info(fctid, "%d lines used for fit. %d of %d lines rejected "
3541  "due to crowding.", _nlines, nlines - _nlines, nlines);
3542 #else
3543  cpl_msg_info(fctid, "%d lines used for fit. %d of %d lines "
3544  "rejected due to crowding and line quality.",
3545  _nlines, nlines - _nlines, nlines);
3546 #endif
3547 
3548  /*
3549  * Compute line position on the CCD along the dispersion axis
3550  * from the current optical model, and optionally the residuals.
3551  */
3552 
3553  xccd = _giraffe_line_abscissa(_lines, slitgeometry, fibers,
3554  solution, localization, residuals);
3555 
3556  _nfibers = cpl_image_get_size_x(xccd);
3557  _nlines = cpl_image_get_size_y(xccd);
3558 
3559 
3560  /*
3561  * Fit a model to individual lines and reject the `bad' ones.
3562  */
3563 
3564  cpl_msg_info(fctid, "Fitting %d line profiles for %d spectra using "
3565  "line model `%s'", _nlines, _nfibers , line_setup->model);
3566 
3567  cpl_msg_info(fctid, "Total number of lines to fit: %d (%d x %d)",
3568  _nlines * _nfibers, _nfibers, _nlines);
3569 
3570 
3571  status = giraffe_linedata_reset(line_data, _lines, _fibers,
3572  line_setup->model);
3573 
3574  if (status != 0) {
3575 
3576  cpl_msg_error(fctid, "Line profile fit failed!");
3577 
3578  cpl_image_delete(xccd);
3579 
3580  cpl_table_delete(_lines);
3581  giraffe_linedata_delete(line_data);
3582 
3583  giraffe_grating_delete(setup);
3584 
3585  _giraffe_lineparams_delete(line_setup);
3586 
3587  giraffe_wlsolution_delete(solution);
3588 
3589  return 8;
3590 
3591  }
3592 
3593  status = _giraffe_line_fit(line_data, xccd, width, extraction, fibers,
3594  localization->locy, line_setup);
3595 
3596  if (status != 0) {
3597 
3598  cpl_msg_error(fctid, "Line profile fit failed!");
3599 
3600  cpl_image_delete(xccd);
3601 
3602  cpl_table_delete(_lines);
3603  giraffe_linedata_delete(line_data);
3604 
3605  giraffe_grating_delete(setup);
3606 
3607  _giraffe_lineparams_delete(line_setup);
3608 
3609  giraffe_wlsolution_delete(solution);
3610 
3611  return 8;
3612 
3613  }
3614 
3615  cpl_image_delete(xccd);
3616  xccd = NULL;
3617 
3618  if (giraffe_linedata_accepted(line_data) == 0) {
3619  cpl_msg_error(fctid, "No lines left after line profile fit!");
3620 
3621  cpl_table_delete(_lines);
3622  giraffe_linedata_delete(line_data);
3623 
3624  giraffe_grating_delete(setup);
3625 
3626  _giraffe_lineparams_delete(line_setup);
3627 
3628  giraffe_wlsolution_delete(solution);
3629 
3630  return 9;
3631 
3632  }
3633 
3634  _nreject = giraffe_linedata_rejected(line_data);
3635  _ngood = giraffe_linedata_accepted(line_data);
3636 
3637  cpl_msg_info(fctid, "Number of good lines: %d. %d of %d lines "
3638  "rejected due to line profile fit.", _ngood, _nreject,
3639  _nlines *_nfibers);
3640 
3641  /*
3642  * Save line fit status for final statistics
3643  */
3644 
3645  line_status = giraffe_linedata_status(line_data);
3646 
3647 
3648  /*
3649  * Fit the lines PSF width variation over the CCD
3650  */
3651 
3652  cpl_msg_info(fctid, "Fit of the line profile PSF width variation.");
3653 
3654  psf_setup.subslits = giraffe_wlsolution_get_subslits(solution);
3655  psf_setup.fit.xorder = config->pxw_xorder;
3656  psf_setup.fit.yorder = config->pxw_yorder;
3657 
3658  cpl_msg_info(fctid, "Chebyshev polynomial order is (%d, %d).",
3659  psf_setup.fit.xorder, psf_setup.fit.yorder);
3660 
3661  psf_setup.clip.iterations = config->pxw_clipniter;
3662  psf_setup.clip.level = config->pxw_cliplevel;
3663  psf_setup.clip.fraction = config->pxw_clipmfrac;
3664 
3665  cpl_msg_info(fctid, "Sigma clipping: iterations = %d, level = %.4f, "
3666  "fraction = %.4f", psf_setup.clip.iterations,
3667  psf_setup.clip.level, psf_setup.clip.fraction);
3668 
3669 
3670  psf_fit = _giraffe_psf_fit(line_data, localization, fibers,
3671  slitgeometry, &psf_setup);
3672 
3673 
3674  if (psf_fit == NULL) {
3675 
3676  cpl_msg_error(fctid, "Fit of the line profile PSF width "
3677  "variation failed!");
3678 
3679  cpl_table_delete(_lines);
3680  cpl_image_delete(line_status);
3681  giraffe_linedata_delete(line_data);
3682 
3683  giraffe_grating_delete(setup);
3684 
3685  _giraffe_lineparams_delete(line_setup);
3686 
3687  giraffe_wlsolution_delete(solution);
3688 
3689  return 10;
3690 
3691  }
3692  else {
3693 
3694  /* FIXME: Not used, debugging only. Maybe it should go away
3695  * completely (RP)
3696  */
3697 
3698  cpl_image_delete(psf_fit);
3699 
3700  }
3701 
3702  if (giraffe_linedata_accepted(line_data) == 0) {
3703  cpl_msg_error(fctid, "No lines left after line profile PSF "
3704  "width fit!");
3705 
3706  cpl_table_delete(_lines);
3707  cpl_image_delete(line_status);
3708  giraffe_linedata_delete(line_data);
3709 
3710  giraffe_grating_delete(setup);
3711 
3712  _giraffe_lineparams_delete(line_setup);
3713 
3714  giraffe_wlsolution_delete(solution);
3715 
3716  return 10;
3717 
3718  }
3719 
3720  cpl_msg_info(fctid, "Number of good lines: %"
3721  CX_PRINTF_FORMAT_SIZE_TYPE ". %"
3722  CX_PRINTF_FORMAT_SIZE_TYPE " of %d lines "
3723  "rejected due to line profile PSF width fit.",
3724  giraffe_linedata_accepted(line_data),
3725  giraffe_linedata_rejected(line_data) - _nreject, _ngood);
3726 
3727  _ngood = giraffe_linedata_accepted(line_data);
3728  _nreject = giraffe_linedata_rejected(line_data);
3729 
3730 
3731  /*
3732  * If requested, fit of a new optical model using the current
3733  * selection of good lines.
3734  */
3735 
3736  if (config->opt_solution == TRUE) {
3737 
3738  cxint iterations = 0;
3739  cxint df = 0;
3740 
3741  cxdouble chisq = 0.;
3742  cxdouble rsquare = 0.;
3743 
3744  cx_string *s = NULL;
3745 
3746  GiOpticalModelParams om_setup;
3747 
3748  GiModel *guess = NULL;
3749  GiModel *model = giraffe_wlsolution_model(solution);
3750 
3751 
3752  /*
3753  * Save initial guess model
3754  */
3755 
3756  guess = giraffe_model_clone(model);
3757 
3758  om_setup.fit.iterations = config->opt_niter;
3759  om_setup.fit.tests = config->opt_ntest;
3760  om_setup.fit.delta = config->opt_dchisq;
3761  om_setup.flags = config->opt_flags;
3762 
3763  cpl_msg_info(fctid, "Optical model fit setup: iterations = %d, "
3764  "tests = %d, delta = %.4f", om_setup.fit.iterations,
3765  om_setup.fit.tests, om_setup.fit.delta);
3766 
3767  status = _giraffe_opticalmodel_fit(solution, line_data, fibers,
3768  slitgeometry, &om_setup);
3769 
3770  if (status != 0) {
3771  cpl_msg_error(fctid, "Optical model fit failed!");
3772 
3773  giraffe_model_delete(guess);
3774 
3775  cpl_table_delete(_lines);
3776  cpl_image_delete(line_status);
3777  giraffe_linedata_delete(line_data);
3778 
3779  giraffe_grating_delete(setup);
3780 
3781  _giraffe_lineparams_delete(line_setup);
3782 
3783  giraffe_wlsolution_delete(solution);
3784 
3785  return 11;
3786 
3787  }
3788 
3789 
3790  /*
3791  * Output of fit results
3792  */
3793 
3794  cpl_msg_info(fctid, "Optical model parameters:");
3795 
3796  s = cx_string_new();
3797 
3798  _giraffe_opticalmodel_format(s, guess, GI_OPTM_PARAMETER_VALUES);
3799  cpl_msg_info(fctid, "Initial: %s", cx_string_get(s));
3800 
3801  _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_VALUES);
3802  cpl_msg_info(fctid, " Fitted: %s", cx_string_get(s));
3803 
3804  _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_ERRORS);
3805  cpl_msg_info(fctid, " Sigma: %s", cx_string_get(s));
3806 
3807  _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_STATUS);
3808  cpl_msg_info(fctid, " Status: %s", cx_string_get(s));
3809 
3810  cx_string_delete(s);
3811  s = NULL;
3812 
3813 #if 0
3814  if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
3815 
3816  cxdouble flength = 0.;
3817  cxdouble sdx = 0.;
3818  cxdouble sdy = 0.;
3819  cxdouble sphi = 0.;
3820 
3821  cx_string *s = cx_string_new();
3822 
3823 
3824  flength = giraffe_model_get_parameter(guess, "FocalLength");
3825  sdx = giraffe_model_get_parameter(guess, "Sdx");
3826  sdy = giraffe_model_get_parameter(guess, "Sdy");
3827  sphi = giraffe_model_get_parameter(guess, "Sphi");
3828 
3829  cx_string_sprintf(s, "Initial: focal length = %.6f, slit "
3830  "x-shift = %.9f, slit y-shift = %.9f, "
3831  "slit rotation = %.9f", flength, sdx, sdy,
3832  sphi);
3833  cpl_msg_info(fctid, "%s", cx_string_get(s));
3834 
3835 
3836  flength = giraffe_model_get_parameter(model, "FocalLength");
3837  sdx = giraffe_model_get_parameter(model, "Sdx");
3838  sdy = giraffe_model_get_parameter(model, "Sdy");
3839  sphi = giraffe_model_get_parameter(model, "Sphi");
3840 
3841  cx_string_sprintf(s, " Fitted: focal length = %.6f, slit "
3842  "x-shift = %.9f, slit y-shift = %.9f, "
3843  "slit rotation = %.9f", flength, sdx, sdy,
3844  sphi);
3845  cpl_msg_info(fctid, "%s", cx_string_get(s));
3846 
3847 
3848  flength = giraffe_model_get_sigma(model, "FocalLength");
3849  sdx = giraffe_model_get_sigma(model, "Sdx");
3850  sdy = giraffe_model_get_sigma(model, "Sdy");
3851  sphi = giraffe_model_get_sigma(model, "Sphi");
3852 
3853  cx_string_sprintf(s, " Sigma: focal length = %.6f, slit "
3854  "x-shift = %.9f, slit y-shift = %.9f, "
3855  "slit rotation = %.9f", flength, sdx, sdy,
3856  sphi);
3857  cpl_msg_info(fctid, "%s", cx_string_get(s));
3858 
3859  cx_string_delete(s);
3860 
3861  }
3862  else {
3863 
3864  cxdouble flength = 0.;
3865 
3866  cx_string *s = cx_string_new();
3867 
3868 
3869  flength = giraffe_model_get_parameter(guess, "FocalLength");
3870 
3871  cx_string_sprintf(s, "Initial: focal length = %.6f", flength);
3872  cpl_msg_info(fctid, "%s", cx_string_get(s));
3873 
3874 
3875  flength = giraffe_model_get_parameter(model, "FocalLength");
3876 
3877  cx_string_sprintf(s, " Fitted: focal length = %.6f", flength);
3878  cpl_msg_info(fctid, "%s", cx_string_get(s));
3879 
3880 
3881  flength = giraffe_model_get_sigma(model, "FocalLength");
3882 
3883  cx_string_sprintf(s, " Sigma: focal length = %.6f", flength);
3884  cpl_msg_info(fctid, "%s", cx_string_get(s));
3885 
3886  cx_string_delete(s);
3887 
3888  }
3889 #endif
3890 
3891  giraffe_model_delete(guess);
3892 
3893 
3894  iterations = giraffe_model_get_position(model);
3895  df = giraffe_model_get_df(model);
3896 
3897  chisq = giraffe_model_get_chisq(model);
3898  rsquare = giraffe_model_get_rsquare(model);
3899 
3900  cpl_msg_info(fctid, "Optical model fit statistics: iterations = "
3901  "%d, DoF = %d, Chi-square = %.6g, Chi-square/DoF = "
3902  "%.6g, R-square = %.6g", iterations, df, chisq,
3903  chisq / df, rsquare);
3904 
3905 
3906  /*
3907  * Update grating data structure with fitted parameters
3908  * for next iteration.
3909  */
3910 
3911  setup->fcoll = giraffe_model_get_parameter(model, "FocalLength");
3912  setup->gcam = giraffe_model_get_parameter(model, "Magnification");
3913  setup->theta = giraffe_model_get_parameter(model, "Angle");
3914  setup->order = giraffe_model_get_parameter(model, "Order");
3915  setup->space = giraffe_model_get_parameter(model, "Spacing");
3916 
3917  if (strcmp(giraffe_model_get_name(model), "xoptmod2") == 0) {
3918  setup->sdx = giraffe_model_get_parameter(model, "Sdx");
3919  setup->sdy = giraffe_model_get_parameter(model, "Sdy");
3920  setup->sphi = giraffe_model_get_parameter(model, "Sphi");
3921  }
3922 
3923 
3924  /*
3925  * Re-compute line positions using the updated optical model
3926  * and update the line data object accordingly. The residuals,
3927  * which belong to the previous optical model must not be used
3928  * here.
3929  */
3930 
3931  /* FIXME: Check why the OGL code computes the y positions of
3932  * the lines, but does not use or store them. (RP)
3933  */
3934 
3935  cpl_msg_info(fctid, "Re-computing line positions with updated "
3936  "optical model");
3937 
3938  xccd = _giraffe_line_abscissa(_lines, slitgeometry, fibers,
3939  solution, localization, FALSE);
3940 
3941  giraffe_linedata_set_data(line_data, "Xccd", xccd);
3942  xccd = NULL;
3943 
3944  }
3945  else {
3946 
3947  cx_string *s = cx_string_new();
3948 
3949  GiModel *model = giraffe_wlsolution_model(solution);
3950 
3951 
3952  _giraffe_opticalmodel_format(s, model, GI_OPTM_PARAMETER_VALUES);
3953  cpl_msg_info(fctid, "Optical model: %s", cx_string_get(s));
3954 
3955  cx_string_delete(s);
3956  s = NULL;
3957 
3958  }
3959 
3960  rms = _giraffe_compute_statistics(line_data, NULL, NULL);
3961 
3962  cpl_msg_info(fctid, "Average RMS [pxl] of line positions using "
3963  "%d of %d lines: %.4f", _ngood, _nlines * _nfibers,
3964  rms);
3965 
3966 
3967  /*
3968  * Fit the wavelength solution coefficients, i.e. the Chebyshev
3969  * correction computed from fitting the optical model residuals.
3970  */
3971 
3972  cpl_msg_info(fctid, "Fit of the wavelength solution coefficients "
3973  "using %" CX_PRINTF_FORMAT_SIZE_TYPE " lines",
3974  giraffe_linedata_accepted(line_data));
3975 
3976  xws_setup.subslits = giraffe_wlsolution_get_subslits(solution);
3977  xws_setup.fit.xorder = config->xws_xorder;
3978  xws_setup.fit.yorder = config->xws_yorder;
3979 
3980  cpl_msg_info(fctid, "Chebyshev polynomial order is (%d, %d).",
3981  xws_setup.fit.xorder, xws_setup.fit.yorder);
3982 
3983  xws_setup.clip.iterations = config->xws_clipniter;
3984  xws_setup.clip.level = config->xws_cliplevel;
3985  xws_setup.clip.fraction = config->xws_clipmfrac;
3986 
3987  cpl_msg_info(fctid, "Sigma clipping: iterations = %d, level = %.4f, "
3988  "fraction = %.4f", xws_setup.clip.iterations,
3989  xws_setup.clip.level, xws_setup.clip.fraction);
3990 
3991  xws_coeffs = giraffe_wlresiduals_new();
3992 
3993  xws_fit = _giraffe_residuals_fit(xws_coeffs, line_data, localization,
3994  fibers, slitgeometry, &xws_setup);
3995 
3996 
3997  if (xws_fit == NULL) {
3998 
3999  cpl_msg_error(fctid, "Fit of the wavelength solution "
4000  "coefficients failed!");
4001 
4002  giraffe_wlresiduals_delete(xws_coeffs);
4003 
4004  cpl_table_delete(_lines);
4005  cpl_image_delete(line_status);
4006  giraffe_linedata_delete(line_data);
4007 
4008  giraffe_grating_delete(setup);
4009 
4010  _giraffe_lineparams_delete(line_setup);
4011 
4012  giraffe_wlsolution_delete(solution);
4013 
4014  return 12;
4015 
4016  }
4017 
4018 
4019  /*
4020  * Update the line data object with the fitted residuals of
4021  * the lines used.
4022  */
4023 
4024  xres = cpl_image_new(giraffe_linedata_fibers(line_data),
4025  giraffe_linedata_lines(line_data),
4026  CPL_TYPE_DOUBLE);
4027 
4028  _giraffe_get_residuals(xres,
4029  giraffe_linedata_get_data(line_data, "Xccd"),
4030  xws_fit);
4031  giraffe_linedata_set_data(line_data, "Xres", xres);
4032 
4033 
4034  if (giraffe_linedata_accepted(line_data) == 0) {
4035  cpl_msg_error(fctid, "No lines left after wavelength solution "
4036  "coefficients fit!");
4037 
4038  giraffe_wlresiduals_delete(xws_coeffs);
4039 
4040  cpl_table_delete(_lines);
4041  cpl_image_delete(line_status);
4042  cpl_image_delete(xws_fit);
4043  giraffe_linedata_delete(line_data);
4044 
4045  giraffe_grating_delete(setup);
4046 
4047  _giraffe_lineparams_delete(line_setup);
4048 
4049  giraffe_wlsolution_delete(solution);
4050 
4051  return 12;
4052 
4053  }
4054 
4055  cpl_msg_info(fctid, "Number of good lines: %"
4056  CX_PRINTF_FORMAT_SIZE_TYPE". %"
4057  CX_PRINTF_FORMAT_SIZE_TYPE" of %d lines "
4058  "rejected due to wavelength solution coefficients fit.",
4059  giraffe_linedata_accepted(line_data),
4060  giraffe_linedata_rejected(line_data) - _nreject, _ngood);
4061 
4062  _ngood = giraffe_linedata_accepted(line_data);
4063  _nreject = giraffe_linedata_rejected(line_data);
4064 
4065 
4066  /*
4067  * Update the wavelength solution with the computed residuals.
4068  */
4069 
4070  giraffe_wlsolution_set_residuals(solution, xws_coeffs);
4071  xws_coeffs = NULL;
4072 
4073 
4074  /*
4075  * Compute RMS over all line positions as computed by the optical
4076  * model corrected for the fitted residuals and the measured
4077  * positions as derived from the fit of the line profiles.
4078  */
4079 
4080  rms = _giraffe_compute_statistics(line_data, xws_fit, line_status);
4081 
4082  cpl_msg_info(fctid, "Average RMS [pxl] of line positions using "
4083  "%d of %d lines: %.4f", _ngood, _nlines * _nfibers,
4084  rms);
4085 
4086 
4087  /*
4088  * Update wavelength calibration information structure
4089  */
4090 
4091  info.width = width;
4092 
4093  info.residuals = residuals;
4094 
4095  info.nlines = _nlines;
4096  info.nfibers = _nfibers;
4097 
4098  info.ngood = _ngood;
4099  info.nreject = _nreject;
4100 
4101  info.rms = rms;
4102 
4103 
4104  /*
4105  * Cleanup data created during this iteration step.
4106  */
4107 
4108  cpl_image_delete(xws_fit);
4109 
4110  cpl_table_delete(_lines);
4111  cpl_image_delete(line_status);
4112 
4113  }
4114 
4115 
4116  /*
4117  * Write the final wavelength solution to the output table. The table
4118  * properties are created from the extracted arc-spectra frame.
4119  */
4120 
4121  tsolution = giraffe_table_new();
4122 
4123  status = _giraffe_convert_wlsolution(tsolution, solution,
4124  extraction->spectra, setup,
4125  &info, config);
4126 
4127  if (status != 0) {
4128 
4129  giraffe_table_delete(tsolution);
4130 
4131  giraffe_grating_delete(setup);
4132 
4133  _giraffe_lineparams_delete(line_setup);
4134 
4135  giraffe_wlsolution_delete(solution);
4136 
4137  return 13;
4138 
4139  }
4140 
4141 
4142  /*
4143  * Fill the results structure.
4144  */
4145 
4146  result->coeffs = tsolution;
4147  tsolution = NULL;
4148 
4149  result->linedata = line_data;
4150 
4151 
4152  /*
4153  * Final cleanup
4154  */
4155 
4156  giraffe_grating_delete(setup);
4157  giraffe_wlsolution_delete(solution);
4158 
4159  _giraffe_lineparams_delete(line_setup);
4160 
4161  return 0;
4162 
4163 }
4164 
4165 
4176 GiWCalConfig *
4177 giraffe_wlcalibration_config_create(cpl_parameterlist *list)
4178 {
4179 
4180  const cxchar *s = NULL;
4181  const cpl_parameter *p = NULL;
4182 
4183  GiWCalConfig *config = NULL;
4184 
4185 
4186  if (!list) {
4187  return NULL;
4188  }
4189 
4190  config = cx_calloc(1, sizeof *config);
4191 
4192 
4193  /*
4194  * The line saturation level is currently not connected to a parameter,
4195  * but it may become one. Therefore it is put in here with a fixed value.
4196  */
4197 
4198  config->line_saturation = 1.e7;
4199 
4200 
4201  /*
4202  * Set configuration data
4203  */
4204 
4205  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.widths");
4206  s = cpl_parameter_get_string(p);
4207 
4208  if (s) {
4209 
4210  cxchar **values = cx_strsplit(s, ",", -1);
4211 
4212 
4213  if (values == NULL) {
4214 
4216  return NULL;
4217 
4218  }
4219  else {
4220 
4221  cxchar *last;
4222 
4223  cxint n = 0;
4224 
4225 
4226  while (values[n] != NULL) {
4227  ++n;
4228  }
4229 
4230  config->line_nwidths = n;
4231  config->line_widths = cx_malloc(n * sizeof(cxint));
4232  cx_assert(config->line_widths != NULL);
4233 
4234  n = 0;
4235  while (values[n] != NULL) {
4236 
4237  cxint w = strtol(values[n], &last, 10);
4238 
4239  if (*last != '\0') {
4240  cx_strfreev(values);
4242 
4243  return NULL;
4244  }
4245 
4246  config->line_widths[n] = w;
4247  ++n;
4248 
4249  }
4250 
4251  if (n > 1) {
4252  qsort(config->line_widths, config->line_nwidths,
4253  sizeof(cxint), _giraffe_window_compare);
4254  }
4255 
4256  cx_strfreev(values);
4257  values = NULL;
4258 
4259  }
4260 
4261  }
4262 
4263 
4264  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.separation");
4265  config->line_separation = fabs(cpl_parameter_get_double(p));
4266 
4267  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.fluxratio");
4268  config->line_fluxratio = cpl_parameter_get_double(p);
4269 
4270  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.brightness");
4271  config->line_brightness = cpl_parameter_get_double(p);
4272 
4273  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.count");
4274  config->line_count = cpl_parameter_get_int(p);
4275 
4276  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.wrange");
4277  s = cpl_parameter_get_string(p);
4278 
4279  if (s) {
4280 
4281  cxchar **values = cx_strsplit(s, ",", 3);
4282 
4283  if (values == NULL) {
4284 
4286  return NULL;
4287 
4288  }
4289  else {
4290 
4291  cxchar *last;
4292 
4293  cxdouble lower = 0.;
4294  cxdouble upper = 0.;
4295 
4296 
4297  lower = strtod(values[0], &last);
4298 
4299  if (*last != '\0') {
4300 
4301  cx_strfreev(values);
4303 
4304  return NULL;
4305 
4306  }
4307 
4308  lower = lower >= 0. ? lower : 0.;
4309 
4310 
4311  if (values[1] != NULL) {
4312 
4313  upper = strtod(values[1], &last);
4314 
4315  if (*last != '\0') {
4316 
4317  cx_strfreev(values);
4319 
4320  return NULL;
4321 
4322  }
4323 
4324  upper = upper > lower ? upper : 0.;
4325 
4326  }
4327 
4328  config->line_wlrange = giraffe_range_create(lower, upper);
4329  cx_assert(config->line_wlrange != NULL);
4330 
4331  }
4332 
4333  cx_strfreev(values);
4334  values = NULL;
4335 
4336  }
4337 
4338 
4339  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.model");
4340  s = cpl_parameter_get_string(p);
4341 
4342  if (strcmp(s, "psfexp") != 0 &&
4343  strcmp(s, "psfexp2") != 0 &&
4344  strcmp(s, "gaussian") != 0) {
4345 
4347  return NULL;
4348 
4349  }
4350  else {
4351  config->line_model = cx_strdup(s);
4352  }
4353 
4354 
4355  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.residuals");
4356  s = cpl_parameter_get_string(p);
4357 
4358  if (strcmp(s, "auto") != 0 &&
4359  strcmp(s, "enable") != 0 &&
4360  strcmp(s, "disable") != 0) {
4361 
4363  return NULL;
4364 
4365  }
4366  else {
4367  config->line_residuals = cx_strdup(s);
4368  }
4369 
4370 
4371  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.threshold");
4372  config->line_threshold = cpl_parameter_get_double(p);
4373 
4374  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.offset");
4375  config->line_offset = cpl_parameter_get_double(p);
4376 
4377  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.iterations");
4378  config->line_niter = cpl_parameter_get_int(p);
4379 
4380  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.tests");
4381  config->line_ntest = cpl_parameter_get_int(p);
4382 
4383  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.dchisquare");
4384  config->line_dchisq = cpl_parameter_get_double(p);
4385 
4386  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.rwidthratio");
4387  config->line_rwidthratio = cpl_parameter_get_double(p);
4388 
4389  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.line.exponent");
4390  config->line_widthexponent = cpl_parameter_get_double(p);
4391 
4392 
4393  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.slit.offset");
4394  s = cpl_parameter_get_string(p);
4395 
4396  cx_assert(s != NULL);
4397 
4398  if (cx_strncasecmp(s, "setup", 5) != 0) {
4399 
4400  cxchar **values = cx_strsplit(s, ",", 4);
4401 
4402 
4403  config->slit_position = 0;
4404  config->slit_dx = 0.;
4405  config->slit_dy = 0.;
4406  config->slit_phi = 0.;
4407 
4408  if (values == NULL || values[0] == NULL) {
4409 
4411  return NULL;
4412 
4413  }
4414  else {
4415 
4416  cxbool eol = FALSE;
4417 
4418  if (*values[0] != '\0') {
4419 
4420  cxchar *last;
4421  cxdouble sdx = strtod(values[0], &last);
4422 
4423  if (*last != '\0') {
4424  cx_strfreev(values);
4425  values = NULL;
4426 
4428 
4429  return NULL;
4430  }
4431 
4432  config->slit_position |= SLIT_DX;
4433  config->slit_dx = sdx;
4434 
4435  }
4436 
4437  if (values[1] == NULL) {
4438  eol = TRUE;
4439  }
4440  else {
4441 
4442  if (*values[1] != '\0') {
4443 
4444  cxchar *last;
4445  cxdouble sdy = strtod(values[1], &last);
4446 
4447  if (*last != '\0') {
4448  cx_strfreev(values);
4449  values = NULL;
4450 
4452 
4453  return NULL;
4454  }
4455 
4456  config->slit_position |= SLIT_DY;
4457  config->slit_dy = sdy;
4458 
4459  }
4460 
4461  }
4462 
4463  if (!eol && values[2] != NULL) {
4464 
4465  if (*values[2] != '\0') {
4466 
4467  cxchar *last;
4468  cxdouble sphi = strtod(values[2], &last);
4469 
4470  if (*last != '\0') {
4471  cx_strfreev(values);
4472  values = NULL;
4473 
4475 
4476  return NULL;
4477  }
4478 
4479  config->slit_position |= SLIT_PHI;
4480  config->slit_phi = sphi;
4481 
4482  }
4483 
4484  }
4485 
4486  cx_strfreev(values);
4487  values = NULL;
4488 
4489  }
4490 
4491  }
4492 
4493 
4494  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.model");
4495  s = cpl_parameter_get_string(p);
4496 
4497  if (strcmp(s, "xoptmod") != 0 && strcmp(s, "xoptmod2") != 0) {
4498 
4500  return NULL;
4501 
4502  }
4503  else {
4504  config->opt_model = cx_strdup(s);
4505  }
4506 
4507 
4508  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.direction");
4509  config->opt_direction = cpl_parameter_get_int(p);
4510 
4511  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.solution");
4512  config->opt_solution = cpl_parameter_get_bool(p);
4513 
4514  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.subslits");
4515  config->opt_subslits = cpl_parameter_get_bool(p);
4516 
4517  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.flags");
4518  s = cpl_parameter_get_string(p);
4519 
4520  if (s) {
4521 
4522  cxchar **values = cx_strsplit(s, ",", -1);
4523 
4524  if (values == NULL) {
4525 
4527  return NULL;
4528 
4529  }
4530  else {
4531 
4532  cxint i = 0;
4533  cxint n = 0;
4534 
4535  config->opt_flags = 0;
4536 
4537  while (values[i] != NULL) {
4538 
4539  if (strncmp(values[i], "fcoll", 5) == 0) {
4540  config->opt_flags |= OPTM_FLENGTH;
4541  ++n;
4542  }
4543  else if (strncmp(values[i], "gcam", 4) == 0) {
4544  config->opt_flags |= OPTM_GCAMERA;
4545  ++n;
4546  }
4547  else if (strncmp(values[i], "theta", 5) == 0) {
4548  config->opt_flags |= OPTM_THETA;
4549  ++n;
4550  }
4551  else if (strncmp(values[i], "sdx", 3) == 0) {
4552  config->opt_flags |= OPTM_SX;
4553  ++n;
4554  }
4555  else if (strncmp(values[i], "sdy", 3) == 0) {
4556  config->opt_flags |= OPTM_SY;
4557  ++n;
4558  }
4559  else if (strncmp(values[i], "sphi", 4) == 0) {
4560  config->opt_flags |= OPTM_SPHI;
4561  ++n;
4562  }
4563 
4564  ++i;
4565 
4566  }
4567 
4568  cx_strfreev(values);
4569  values = NULL;
4570 
4571 
4572  /*
4573  * Stop if no valid optical model parameter flag was seen
4574  */
4575 
4576  if (n == 0) {
4578  return NULL;
4579  }
4580 
4581  }
4582 
4583  }
4584 
4585 
4586  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.iterations");
4587  config->opt_niter = cpl_parameter_get_int(p);
4588 
4589  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.tests");
4590  config->opt_ntest = cpl_parameter_get_int(p);
4591 
4592  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.opt.dchisquare");
4593  config->opt_dchisq = cpl_parameter_get_double(p);
4594 
4595 
4596  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.sigma");
4597  config->pxw_cliplevel = cpl_parameter_get_double(p);
4598 
4599  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.iterations");
4600  config->pxw_clipniter = cpl_parameter_get_int(p);
4601 
4602  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.fraction");
4603  config->pxw_clipmfrac = cpl_parameter_get_double(p);
4604 
4605  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.psf.order");
4606  s = cpl_parameter_get_string(p);
4607 
4608  if (s) {
4609 
4610  cxchar **values = cx_strsplit(s, ",", 3);
4611 
4612  if (values == NULL || values[1] == NULL) {
4613 
4615  return NULL;
4616 
4617  }
4618  else {
4619 
4620  cxchar *last;
4621 
4622 
4623  config->pxw_xorder = strtol(values[0], &last, 10);
4624 
4625  if (*last != '\0') {
4626 
4627  cx_strfreev(values);
4629 
4630  return NULL;
4631 
4632  }
4633 
4634  config->pxw_yorder = strtol(values[1], &last, 10);
4635 
4636  if (*last != '\0') {
4637 
4638  cx_strfreev(values);
4640 
4641  return NULL;
4642 
4643  }
4644 
4645  }
4646 
4647  cx_strfreev(values);
4648  values = NULL;
4649 
4650  }
4651 
4652 
4653  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.sigma");
4654  config->xws_cliplevel = cpl_parameter_get_double(p);
4655 
4656  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.iterations");
4657  config->xws_clipniter = cpl_parameter_get_int(p);
4658 
4659  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.fraction");
4660  config->xws_clipmfrac = cpl_parameter_get_double(p);
4661 
4662  p = cpl_parameterlist_find(list, "giraffe.wlcalibration.wsol.order");
4663  s = cpl_parameter_get_string(p);
4664 
4665  if (s) {
4666 
4667  cxchar **values = cx_strsplit(s, ",", 3);
4668 
4669  if (values == NULL || values[1] == NULL) {
4670 
4672  return NULL;
4673 
4674  }
4675  else {
4676 
4677  cxchar *last;
4678 
4679 
4680  config->xws_xorder = strtol(values[0], &last, 10);
4681 
4682  if (*last != '\0') {
4683 
4684  cx_strfreev(values);
4686 
4687  return NULL;
4688 
4689  }
4690 
4691  config->xws_yorder = strtol(values[1], &last, 10);
4692 
4693  if (*last != '\0') {
4694 
4695  cx_strfreev(values);
4697 
4698  return NULL;
4699 
4700  }
4701 
4702  }
4703 
4704  cx_strfreev(values);
4705  values = NULL;
4706 
4707  }
4708 
4709  return config;
4710 
4711 }
4712 
4713 
4726 void
4728 {
4729 
4730  if (config) {
4731  if (config->line_widths) {
4732  cx_free(config->line_widths);
4733  }
4734 
4735  if (config->line_wlrange) {
4737  }
4738 
4739  if (config->line_model) {
4740  cx_free(config->line_model);
4741  }
4742 
4743  if (config->line_residuals) {
4744  cx_free(config->line_residuals);
4745  }
4746 
4747  if (config->opt_model) {
4748  cx_free(config->opt_model);
4749  }
4750 
4751  cx_free(config);
4752  }
4753 
4754  return;
4755 
4756 }
4757 
4758 
4770 void
4771 giraffe_wlcalibration_config_add(cpl_parameterlist *list)
4772 {
4773 
4774  cpl_parameter *p;
4775 
4776 
4777  if (!list) {
4778  return;
4779  }
4780 
4781 
4782  /*
4783  * Line selection parameters
4784  */
4785 
4786  p = cpl_parameter_new_value("giraffe.wlcalibration.line.widths",
4787  CPL_TYPE_STRING,
4788  "List of window widths [pxl] used for line "
4789  "detection and fit (e.g. '60,40,15').",
4790  "giraffe.wlcalibration",
4791  "10,10,10,10,10");
4792  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lswidth");
4793  cpl_parameterlist_append(list, p);
4794 
4795 
4796  p = cpl_parameter_new_value("giraffe.wlcalibration.line.separation",
4797  CPL_TYPE_DOUBLE,
4798  "Factor used to compute the minimum line "
4799  "separation from the window width.",
4800  "giraffe.wlcalibration",
4801  0.9);
4802  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lssep");
4803  cpl_parameterlist_append(list, p);
4804 
4805 
4806  p = cpl_parameter_new_value("giraffe.wlcalibration.line.fluxratio",
4807  CPL_TYPE_DOUBLE,
4808  "Selects only lines whose neighbours have "
4809  "a relative intensity less than "
4810  "1. / fluxratio.",
4811  "giraffe.wlcalibration",
4812  50.);
4813 
4814  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lsfxratio");
4815  cpl_parameterlist_append(list, p);
4816 
4817 
4818  p = cpl_parameter_new_value("giraffe.wlcalibration.line.brightness",
4819  CPL_TYPE_DOUBLE,
4820  "Selects lines having an intensity greater "
4821  "or equal to the given intensity.",
4822  "giraffe.wlcalibration",
4823  0.);
4824 
4825  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lsbright");
4826  cpl_parameterlist_append(list, p);
4827 
4828 
4829  p = cpl_parameter_new_value("giraffe.wlcalibration.line.count",
4830  CPL_TYPE_INT,
4831  "Sets the minimum number of lines to select; "
4832  "selected are lines with the highest nominal "
4833  "intensity. A value of 0 turns this selection "
4834  "off. If the value is less than 0 the "
4835  "selection is skipped if the line list does "
4836  "not contain enough lines.",
4837  "giraffe.wlcalibration",
4838  -80);
4839 
4840  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lscount");
4841  cpl_parameterlist_append(list, p);
4842 
4843 
4844  p = cpl_parameter_new_value("giraffe.wlcalibration.line.wrange",
4845  CPL_TYPE_STRING,
4846  "Selects only lines within the given "
4847  "wavelength range [nm].",
4848  "giraffe.wlcalibration",
4849  "0.,0.");
4850 
4851  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lswrange");
4852  cpl_parameterlist_append(list, p);
4853 
4854 
4855  /*
4856  * Line profile fit parameters
4857  */
4858 
4859  p = cpl_parameter_new_enum("giraffe.wlcalibration.line.model",
4860  CPL_TYPE_STRING,
4861  "Line profile model.",
4862  "giraffe.wlcalibration",
4863  "psfexp", 3, "psfexp", "psfexp2", "gaussian");
4864 
4865  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfmodel");
4866  cpl_parameterlist_append(list, p);
4867 
4868 
4869  p = cpl_parameter_new_enum("giraffe.wlcalibration.line.residuals",
4870  CPL_TYPE_STRING,
4871  "Use optical model residuals for line "
4872  "detection.",
4873  "giraffe.wlcalibration",
4874  "auto", 3, "auto", "enable", "disable");
4875 
4876  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfres");
4877  cpl_parameterlist_append(list, p);
4878 
4879 
4880  p = cpl_parameter_new_value("giraffe.wlcalibration.line.threshold",
4881  CPL_TYPE_DOUBLE,
4882  "Line detection threshold during the "
4883  "line fitting (multiple of bias sigma)",
4884  "giraffe.wlcalibration",
4885  1.);
4886 
4887  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfthreshold");
4888  cpl_parameterlist_append(list, p);
4889 
4890 
4891  p = cpl_parameter_new_value("giraffe.wlcalibration.line.offset",
4892  CPL_TYPE_DOUBLE,
4893  "Maximum allowed difference between the "
4894  "fitted and raw line peak position.",
4895  "giraffe.wlcalibration",
4896  10.);
4897 
4898  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfoffset");
4899  cpl_parameterlist_append(list, p);
4900 
4901 
4902  p = cpl_parameter_new_value("giraffe.wlcalibration.line.iterations",
4903  CPL_TYPE_INT,
4904  "Line detection fit maximum number of "
4905  "iterations.",
4906  "giraffe.wlcalibration",
4907  50);
4908 
4909  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfniter");
4910  cpl_parameterlist_append(list, p);
4911 
4912 
4913  p = cpl_parameter_new_value("giraffe.wlcalibration.line.tests",
4914  CPL_TYPE_INT,
4915  "Line detection fit maximum number of "
4916  "tests.",
4917  "giraffe.wlcalibration",
4918  7);
4919 
4920  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfntest");
4921  cpl_parameterlist_append(list, p);
4922 
4923 
4924  p = cpl_parameter_new_value("giraffe.wlcalibration.line.dchisquare",
4925  CPL_TYPE_DOUBLE,
4926  "Line detection fit minimum chi-square "
4927  "difference.",
4928  "giraffe.wlcalibration",
4929  0.0001);
4930 
4931  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfdchisq");
4932  cpl_parameterlist_append(list, p);
4933 
4934 
4935  p = cpl_parameter_new_value("giraffe.wlcalibration.line.rwidthratio",
4936  CPL_TYPE_DOUBLE,
4937  "Line width/resolution width factor.",
4938  "giraffe.wlcalibration",
4939  0.5);
4940 
4941  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfreswid");
4942  cpl_parameterlist_append(list, p);
4943 
4944 
4945  p = cpl_parameter_new_value("giraffe.wlcalibration.line.exponent",
4946  CPL_TYPE_DOUBLE,
4947  "Exponential line profile exponent; it will "
4948  "not be fitted if it is larger than 0.",
4949  "giraffe.wlcalibration",
4950  -3.);
4951 
4952  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-lfexpwid");
4953  cpl_parameterlist_append(list, p);
4954 
4955 
4956  /*
4957  * Slit offsets
4958  */
4959 
4960 
4961  p = cpl_parameter_new_value("giraffe.wlcalibration.slit.offset",
4962  CPL_TYPE_STRING,
4963  "Initial slit position offsets along the "
4964  "x and y direction and rotation angle.",
4965  "giraffe.wlcalibration",
4966  "setup");
4967 
4968  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-soffset");
4969  cpl_parameterlist_append(list, p);
4970 
4971 
4972  /*
4973  * Optical model parameters
4974  */
4975 
4976  p = cpl_parameter_new_enum("giraffe.wlcalibration.opt.model",
4977  CPL_TYPE_STRING,
4978  "Optical model.",
4979  "giraffe.wlcalibration",
4980  "xoptmod2", 2, "xoptmod", "xoptmod2");
4981 
4982  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-ommodel");
4983  cpl_parameterlist_append(list, p);
4984 
4985 
4986  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.direction",
4987  CPL_TYPE_INT,
4988  "Dispersion direction flag.",
4989  "giraffe.wlcalibration",
4990  1);
4991 
4992  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omdir");
4993  cpl_parameterlist_append(list, p);
4994 
4995 
4996  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.solution",
4997  CPL_TYPE_BOOL,
4998  "Controls optical model parameter fitting.",
4999  "giraffe.wlcalibration",
5000  TRUE);
5001 
5002  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omsol");
5003  cpl_parameterlist_append(list, p);
5004 
5005 
5006  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.flags",
5007  CPL_TYPE_STRING,
5008  "List of flags defining the set of free "
5009  "parameters used for fitting the optical "
5010  "model. Possible values are: fcoll, gcam, "
5011  "theta, sdx, sdy, sphi",
5012  "giraffe.wlcalibration",
5013  "sdx,sdy,sphi");
5014 
5015  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omflags");
5016  cpl_parameterlist_append(list, p);
5017 
5018 
5019  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.subslits",
5020  CPL_TYPE_BOOL,
5021  "Controls subslit geometry usage in the "
5022  "optical model fit; subslits are used if "
5023  "set to `true'.",
5024  "giraffe.wlcalibration",
5025  FALSE);
5026 
5027  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omsslits");
5028  cpl_parameterlist_append(list, p);
5029 
5030 
5031  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.iterations",
5032  CPL_TYPE_INT,
5033  "Optical model fit maximum number of "
5034  "iterations.",
5035  "giraffe.wlcalibration",
5036  50);
5037 
5038  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omniter");
5039  cpl_parameterlist_append(list, p);
5040 
5041 
5042  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.tests",
5043  CPL_TYPE_INT,
5044  "Optical model fit maximum number of "
5045  "tests",
5046  "giraffe.wlcalibration",
5047  7);
5048 
5049  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omntest");
5050  cpl_parameterlist_append(list, p);
5051 
5052 
5053  p = cpl_parameter_new_value("giraffe.wlcalibration.opt.dchisquare",
5054  CPL_TYPE_DOUBLE,
5055  "Optical model fit minimum chi-square "
5056  "difference.",
5057  "giraffe.wlcalibration",
5058  0.0001);
5059 
5060  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-omdchisq");
5061  cpl_parameterlist_append(list, p);
5062 
5063 
5064  /*
5065  * PSF fit parameters
5066  */
5067 
5068  p = cpl_parameter_new_value("giraffe.wlcalibration.psf.sigma",
5069  CPL_TYPE_DOUBLE,
5070  "PSF width fit sigma clipping factor.",
5071  "giraffe.wlcalibration",
5072  1.25);
5073 
5074  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwsigma");
5075  cpl_parameterlist_append(list, p);
5076 
5077 
5078  p = cpl_parameter_new_value("giraffe.wlcalibration.psf.iterations",
5079  CPL_TYPE_INT,
5080  "PSF width fit sigma clipping maximum "
5081  "number of iterations.",
5082  "giraffe.wlcalibration",
5083  10);
5084 
5085  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwniter");
5086  cpl_parameterlist_append(list, p);
5087 
5088 
5089  p = cpl_parameter_new_range("giraffe.wlcalibration.psf.fraction",
5090  CPL_TYPE_DOUBLE,
5091  "PSF width fit sigma clipping minimum "
5092  "fraction of points accepted/total.",
5093  "giraffe.wlcalibration",
5094  0.9, 0., 1.);
5095 
5096  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xwmfrac");
5097  cpl_parameterlist_append(list, p);
5098 
5099 
5100  p = cpl_parameter_new_value("giraffe.wlcalibration.psf.order",
5101  CPL_TYPE_STRING,
5102  "X and Y polynomial orders for PSF x-width "
5103  "Chebyshev fit.",
5104  "giraffe.wlcalibration",
5105  "2,2");
5106 
5107  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-xworder");
5108  cpl_parameterlist_append(list, p);
5109 
5110 
5111  /*
5112  * Parameters of the wavelength solution Chebyshev correction fit
5113  */
5114 
5115  p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.sigma",
5116  CPL_TYPE_DOUBLE,
5117  "Chebyshev correction sigma clipping factor.",
5118  "giraffe.wlcalibration",
5119  150.0);
5120 
5121  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wssigma");
5122  cpl_parameterlist_append(list, p);
5123 
5124 
5125  p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.iterations",
5126  CPL_TYPE_INT,
5127  "Chebyshev correction sigma clipping "
5128  "maximum number of iterations",
5129  "giraffe.wlcalibration",
5130  10);
5131 
5132  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsniter");
5133  cpl_parameterlist_append(list, p);
5134 
5135 
5136  p = cpl_parameter_new_range("giraffe.wlcalibration.wsol.fraction",
5137  CPL_TYPE_DOUBLE,
5138  "Chebyshev correction sigma clipping "
5139  "minimum fraction of points accepted/total.",
5140  "giraffe.wlcalibration",
5141  0.9, 0., 1.);
5142 
5143  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsmfrac");
5144  cpl_parameterlist_append(list, p);
5145 
5146 
5147  p = cpl_parameter_new_value("giraffe.wlcalibration.wsol.order",
5148  CPL_TYPE_STRING,
5149  "X and Y polynomial orders for the wavelength "
5150  "solution Chebyshev correction.",
5151  "giraffe.wlcalibration",
5152  "6,4");
5153 
5154  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcal-wsorder");
5155  cpl_parameterlist_append(list, p);
5156 
5157  return;
5158 
5159 }
cxdouble giraffe_range_get_min(const GiRange *const self)
Get the minimum of a range.
Definition: girange.c:175
cxchar * opt_model
cxdouble line_saturation
GiWCalConfig * giraffe_wlcalibration_config_create(cpl_parameterlist *list)
Creates a setup structure for the wavelength calibration.
cxdouble line_dchisq
GiRange * line_wlrange
cxdouble wlenmin
Definition: gigrating.h:59
cxdouble sphi
Definition: gigrating.h:69
GiGrating * giraffe_grating_create(const GiImage *spectra, const GiTable *grating)
Create a GiGrating from a reference image.
Definition: gigrating.c:226
cxdouble band
Definition: gigrating.h:61
cxchar * line_model
cxdouble theta
Definition: gigrating.h:64
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
Definition: gitable.c:441
Wavelength calibration configuration data structure.
cxint giraffe_table_set(GiTable *self, cpl_table *table)
Sets the table data.
Definition: gitable.c:464
cxdouble line_brightness
void giraffe_table_delete(GiTable *self)
Destroys a Giraffe table.
Definition: gitable.c:162
cxdouble slit_dx
GiRange * giraffe_range_create(cxdouble min, cxdouble max)
Creates a new range from the given minimum and maximum values.
Definition: girange.c:91
const cxchar * giraffe_fiberlist_query_index(const cpl_table *fibers)
Query a fiber list for the name of the fiber reference index column.
GiTable * giraffe_table_new(void)
Creates a new, empty Giraffe table.
Definition: gitable.c:93
GiWlSolution * giraffe_wlsolution_create(GiTable *solution, GiImage *spectra, GiGrating *grating)
Create a new wavelength solution from a wavelength solution table.
Definition: giwlsolution.c:214
void gi_error(const cxchar *format,...)
Log an error message.
Definition: gimessages.c:67
cxdouble line_widthexponent
cxint order
Definition: gigrating.h:57
cxint giraffe_table_set_properties(GiTable *self, cpl_propertylist *properties)
Attaches a property list to an table.
Definition: gitable.c:524
cxdouble line_rwidthratio
cxdouble gcam
Definition: gigrating.h:66
cxdouble slit_dy
cxint resol
Definition: gigrating.h:62
cxdouble line_offset
cxint giraffe_matrix_sort(cpl_matrix *mA)
Sort in place the matrix elements in ascending order.
Definition: gimatrix.c:388
cxint16 slit_position
cxdouble line_fluxratio
cxdouble wlenmax
Definition: gigrating.h:60
cxdouble sdx
Definition: gigrating.h:67
void giraffe_wlcalibration_config_add(cpl_parameterlist *list)
Adds parameters for the wavelength calibration.
Structure to handle Grating Information.
Definition: gigrating.h:52
cxdouble wlen0
Definition: gigrating.h:58
void giraffe_grating_delete(GiGrating *self)
Destroys an GiGrating object.
Definition: gigrating.c:429
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
Definition: giimage.c:226
cpl_matrix * giraffe_matrix_leastsq(const cpl_matrix *mA, const cpl_matrix *mB)
Computes the solution of an equation using a pseudo-inverse.
Definition: gimatrix.c:511
void giraffe_wlcalibration_config_destroy(GiWCalConfig *config)
Destroys a wavelength calibration setup structure.
cxdouble line_threshold
cpl_propertylist * giraffe_table_get_properties(const GiTable *self)
Gets the table properties.
Definition: gitable.c:497
cxint giraffe_calibrate_wavelength(GiWCalData *result, GiExtraction *extraction, GiLocalization *localization, GiTable *fibers, GiTable *slitgeometry, GiTable *grating, GiTable *lines, GiTable *initial, GiWCalConfig *config)
Compute the wavelength solution for the given extracted arc-lamp spectra.
cxdouble slit_phi
void giraffe_range_delete(GiRange *self)
Destroys a range object.
Definition: girange.c:126
cxdouble pxw_clipmfrac
cxdouble xws_clipmfrac
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
Definition: giimage.c:290
cxdouble giraffe_range_get_max(const GiRange *const self)
Get the maximum of a range.
Definition: girange.c:221
cxint * line_widths
cxdouble fcoll
Definition: gigrating.h:65
cxdouble opt_dchisq
cxdouble xws_cliplevel
cxdouble space
Definition: gigrating.h:63
cxdouble sdy
Definition: gigrating.h:68
cxdouble line_separation
cxchar * line_residuals
cxdouble pxw_cliplevel

This file is part of the GIRAFFE Pipeline Reference Manual 2.14.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Wed Mar 11 2015 13:19:42 by doxygen 1.8.9.1 written by Dimitri van Heesch, © 1997-2004