GIRAFFE Pipeline Reference Manual

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.16.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Dec 15 2022 21:18:52 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2004