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