GIRAFFE Pipeline Reference Manual

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.17.1.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Tue Jan 7 2025 16:15:13 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004