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