GIRAFFE Pipeline Reference Manual

gisgcalibration.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 <math.h>
25
26#include <cxmemory.h>
27#include <cxmessages.h>
28#include <cxstrutils.h>
29
30#include <cpl_error.h>
31#include <cpl_parameterlist.h>
32#include <cpl_propertylist.h>
33#include <cpl_matrix.h>
34#include <cpl_image.h>
35#include <cpl_table.h>
36
37#include "gialias.h"
38#include "gierror.h"
39#include "gimatrix.h"
40#include "gifiberutils.h"
41#include "gigrating.h"
42#include "gimodel.h"
43#include "gilocalization.h"
44#include "giextraction.h"
45#include "girebinning.h"
46#include "gisgcalibration.h"
47
48
57struct GiMeasurement {
58 cxdouble value;
59 cxdouble sigma;
60};
61
62typedef struct GiMeasurement GiMeasurement;
63
64
65struct GiSGSetup {
66
67 cxint nx;
68 cxint nex;
69
70 GiRebinScale scale;
71
72 cxdouble wlmin;
73 cxdouble wlmax;
74 cxdouble wlstep;
75
76 cxdouble pixelsize;
77
78};
79
80typedef struct GiSGSetup GiSGSetup;
81
82
83struct GiCPFitParams {
84
85 cxint dnmin;
86 cxint iterations;
87
88 cxdouble step;
89 cxdouble wfactor;
90 cxdouble sigma;
91
92 GiRebinScale scale;
93
94 GiFitSetup fit;
95
96};
97
98typedef struct GiCPFitParams GiCPFitParams;
99
100
101struct GiCPeakFit {
102 GiMeasurement amplitude;
103 GiMeasurement background;
104 GiMeasurement center;
105 GiMeasurement width;
106
107 cxint status;
108};
109
110typedef struct GiCPeakFit GiCPeakFit;
111
112
113struct GiSGMask {
114
115 cxsize size;
116 cxsize nholes;
117
118 GiRebinScale scale;
119
120 cxdouble start;
121 cxdouble step;
122
123 cpl_matrix* wavelength;
124 cpl_matrix* flux;
125
126};
127
128typedef struct GiSGMask GiSGMask;
129
130
131inline static GiSGMask*
132_giraffe_sgmask_new(cxsize size)
133{
134
135 GiSGMask* self = cx_calloc(1, sizeof *self);
136
137 self->wavelength = cpl_matrix_new(1, size);
138 self->flux = cpl_matrix_new(1, size);
139
140 self->size = size;
141 self->nholes = 0;
142
143 self->scale = GIREBIN_SCALE_LINEAR;
144
145 self->start = 0.;
146 self->step = 1.;
147
148 return self;
149
150}
151
152
153inline static void
154_giraffe_sgmask_delete(GiSGMask* self)
155{
156
157 if (self) {
158
159 if (self->wavelength != NULL) {
160 cpl_matrix_delete(self->wavelength);
161 self->wavelength = NULL;
162 }
163
164 if (self->flux != NULL) {
165 cpl_matrix_delete(self->flux);
166 self->flux = NULL;
167 }
168
169 cx_free(self);
170
171 }
172
173 return;
174
175}
176
177
178inline static GiSGMask*
179_giraffe_sgmask_create(cxsize size, cxdouble start, cxdouble step,
180 GiRebinScale scale, const GiTable* mask)
181{
182
183 register cxsize i;
184
185 cxdouble wlmin = 0.;
186 cxdouble wlmax = 0.;
187 cxdouble wlstep = 0.;
188
189 cpl_table* _mask = NULL;
190
191 GiSGMask* self = NULL;
192
193
194 cx_assert(mask != NULL);
195
196 _mask = giraffe_table_get(mask);
197 cx_assert(_mask != NULL);
198
199 self = _giraffe_sgmask_new(size);
200
201 self->start = start;
202 self->step = step;
203 self->scale = scale;
204
205
206 /*
207 * Fill wavelength array
208 */
209
210 for (i = 0; i < self->size; i++) {
211 cpl_matrix_set(self->wavelength, 0, i, self->start + i * self->step);
212 }
213
214
215 wlmin = cpl_matrix_get(self->wavelength, 0, 0);
216 wlmax = cpl_matrix_get(self->wavelength, 0, self->size - 1);
217 wlstep = self->step;
218
219 if (self->scale == GIREBIN_SCALE_LOG) {
220
221 wlmin = exp(wlmin);
222 wlmax = exp(wlmax);
223 wlstep = exp(wlstep);
224
225 }
226
227
228 /*
229 * Create the mask's flux array from the mask template `mask', i.e.
230 * the flux values are set to 1. within the holes and 0. otherwise.
231 */
232
233 cpl_table_select_all(_mask);
234
235 cpl_table_and_selected_double(_mask, "WLEN1", CPL_GREATER_THAN, wlmin);
236 cpl_table_and_selected_double(_mask, "WLEN2", CPL_LESS_THAN, wlmax);
237
238 _mask = cpl_table_extract_selected(_mask);
239
240 if (_mask == NULL || cpl_table_get_nrow(_mask) <= 0) {
241 _giraffe_sgmask_delete(self);
242 self = NULL;
243
244 return NULL;
245 }
246
247
248 self->nholes = cpl_table_get_nrow(_mask);
249
250 for (i = 0; i < self->nholes; i++) {
251
252 register cxsize j;
253
254 cxdouble hstart = cpl_table_get(_mask, "WLEN1", i, NULL) - wlmin;
255 cxdouble hend = cpl_table_get(_mask, "WLEN2", i, NULL) - wlmin;
256
257
258 hstart /= wlstep;
259 hend /= wlstep;
260
261 for (j = (cxsize)(hstart + 0.5); j <= (cxsize)(hend + 0.5); j++) {
262
263 cpl_matrix_set(self->flux, 0, j, 1.);
264
265 }
266
267 }
268
269 cpl_table_delete(_mask);
270
271 return self;
272
273}
274
275
276inline static cxsize
277_giraffe_sgmask_size(const GiSGMask* self)
278{
279
280 cx_assert(self != NULL);
281
282 return self->size;
283
284}
285
286
287inline static cxsize
288_giraffe_sgmask_holes(const GiSGMask* self)
289{
290
291 cx_assert(self != NULL);
292
293 return self->nholes;
294
295}
296
297
298inline static cxint
299_giraffe_sgmask_set_flux(GiSGMask* self, cxsize position, cxdouble value)
300{
301
302 cx_assert(self != NULL);
303
304 if (position >= (cxsize)cpl_matrix_get_ncol(self->flux)) {
305 return 1;
306 }
307
308 cpl_matrix_set(self->flux, 0, position, value);
309
310 return 0;
311
312}
313
314
315inline static cxdouble
316_giraffe_sgmask_get_flux(GiSGMask* self, cxsize position)
317{
318
319 const cxchar* const fctid = "_giraffe_sgmask_get_flux";
320
321
322 cx_assert(self != NULL);
323
324 if (position >= (cxsize)cpl_matrix_get_ncol(self->flux)) {
325 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
326 return 0.;
327 }
328
329 return cpl_matrix_get(self->flux, 0, position);
330
331}
332
333
334inline static const cpl_matrix*
335_giraffe_sgmask_get(GiSGMask* self)
336{
337
338 cx_assert(self != NULL);
339
340 return self->flux;
341
342}
343
344
345inline static cxint
346_giraffe_sgmask_crop(GiSGMask* self, cxsize begin, cxsize end)
347{
348
349 cxsize size = 0;
350
351 cpl_matrix* buffer = NULL;
352
353
354 cx_assert(self != NULL);
355 cx_assert(end > begin);
356 cx_assert(cpl_matrix_get_nrow(self->wavelength) == 1);
357 cx_assert(cpl_matrix_get_nrow(self->flux) == 1);
358
359 if (begin >= (cxsize)cpl_matrix_get_ncol(self->flux)) {
360 return 1;
361 }
362
363 if (end > (cxsize)cpl_matrix_get_ncol(self->flux)) {
364 end = cpl_matrix_get_ncol(self->flux);
365 }
366
367 if (begin == 0 && end == (cxsize)cpl_matrix_get_ncol(self->flux)) {
368 return 0;
369 }
370
371 size = end - begin;
372
373 buffer = cpl_matrix_extract(self->wavelength, 0, begin, 1, 1, 1, size);
374 cpl_matrix_delete(self->wavelength);
375 self->wavelength = buffer;
376
377 buffer = cpl_matrix_extract(self->flux, 0, begin, 1, 1, 1, size);
378 cpl_matrix_delete(self->flux);
379 self->flux = buffer;
380
381 cx_assert(cpl_matrix_get_nrow(self->flux) == 1);
382 cx_assert((cxsize)cpl_matrix_get_ncol(self->flux) == size);
383
384 self->size = size;
385
386 return 0;
387
388}
389
390
391inline static cxdouble
392_giraffe_clip_value(cxdouble value, cxdouble low, cxdouble high,
393 cxbool* flag)
394{
395
396 cxbool status = FALSE;
397
398 if (value < low) {
399 value = low;
400 status = TRUE;
401 }
402
403 if (value >= high) {
404 value = high;
405 status = TRUE;
406 }
407
408 if (flag != NULL) {
409 *flag = status;
410 }
411
412 return value;
413
414}
415
416
417/*
418 * The function performs a linear interpolation and simultaneous re-sampling
419 * of the input image `signal' from the input bin size `step1' to a signal
420 * with a sampling of `step2'.
421 */
422
423inline static cpl_image*
424_giraffe_resample_image(cpl_image* signal, cxdouble step1, cxdouble step2)
425{
426
427 cxint i;
428 cxint nx1 = 0;
429 cxint ny = 0;
430 cxint nx2 = 0;
431 cxint step = CX_MAX(1, (cxint)(step1/step2));
432
433 cpl_image* _signal = NULL;
434
435
436 cx_assert(signal != NULL);
437
438 ny = cpl_image_get_size_x(signal);
439 nx1 = cpl_image_get_size_y(signal);
440
441 nx2 = (nx1 - 1) * step + 1;
442
443 _signal = cpl_image_new(ny, nx2, CPL_TYPE_DOUBLE);
444
445 for (i = 0; i < ny; i++) {
446
447 register cxint j;
448
449 register cxdouble* data = cpl_image_get_data(signal);
450 register cxdouble* _data = cpl_image_get_data(_signal);
451
452
453 for (j = 0; j < nx1 - 1; j++) {
454
455 register cxint k;
456 register cxint l = j * ny + i;
457 register cxint m = j * ny * step + i;
458
459 for (k = 0; k < step; k++) {
460
461 cxdouble f = (cxdouble)k / (cxdouble)step;
462
463 _data[m + k * ny] = (1. - f) * data[l] + f * data[l + ny];
464
465 }
466
467 }
468
469 _data[nx2 - 1] = data[nx1 - 1];
470
471 }
472
473 return _signal;
474
475}
476
477
478/*
479 * Compute cross-correlation function s * T for the window [start, end]
480 */
481
482inline static cpl_matrix*
483_giraffe_compute_cross_correlation(const cpl_matrix* signal,
484 const cpl_matrix* template,
485 cxint start, cxint end)
486{
487
488 const cxchar* const fctid = "_giraffe_compute_cross_correlation";
489
490
491 cxint i;
492 cxint n = 0;
493 cxint nmax = 0;
494 cxint ns = 0;
495 cxint nccf = 0;
496
497 cxdouble sum = 0.;
498
499 cpl_matrix* _signal = (cpl_matrix*)signal;
500 cpl_matrix* _template = (cpl_matrix*)template;
501 cpl_matrix* ccf = NULL;
502 cpl_matrix* _ccf = NULL;
503
504
505 cx_assert(_signal != NULL);
506 cx_assert(cpl_matrix_get_nrow(_signal) == 1);
507
508 cx_assert(_template != NULL);
509 cx_assert(cpl_matrix_get_nrow(_template) == 1);
510
511 ns = cpl_matrix_get_ncol(_signal);
512 cx_assert(ns == cpl_matrix_get_ncol(_template));
513
514 cx_assert(start <= end);
515
516
517 /*
518 * The number of shifts should not exceed the half-window
519 */
520
521 nmax = cpl_matrix_get_ncol(_signal) / 2;
522
523 start = CX_MAX(CX_MIN(start, nmax), -nmax);
524 end = CX_MAX(CX_MIN(end, nmax), -nmax);
525
526 nccf = end - start;
527
528 cpl_msg_debug(fctid, "Cross-correlation function: signal size = %"
529 CPL_SIZE_FORMAT ", template size = %" CPL_SIZE_FORMAT
530 ", window start = %d, window end = %d",
531 cpl_matrix_get_ncol(_signal), cpl_matrix_get_ncol(_template),
532 start, end);
533
534
535 ccf = cpl_matrix_new(1, nccf);
536
537 for (i = start; i < end; i++) {
538
539 if (i < 0) {
540
541 cxint j;
542
543
544 /*
545 * - shift template i < 0
546 */
547
548 sum = 0.;
549
550 for (j = 0; j < ns + i; j++) {
551
552 cxdouble s = cpl_matrix_get(_signal, 0, j);
553 cxdouble t = cpl_matrix_get(_template, 0, j - i);
554
555 sum += t * s;
556
557 }
558
559 sum /= (cxdouble)(ns + i);
560
561 cpl_matrix_set(ccf, 0, i - start, sum);
562
563 }
564 else if (i > 0) {
565
566 cxint j;
567
568
569 /*
570 * + shift template i > 0
571 */
572
573 sum = 0.;
574
575 for (j = i; j < ns; j++) {
576
577 cxdouble s = cpl_matrix_get(_signal, 0, j);
578 cxdouble t = cpl_matrix_get(_template, 0, j - i);
579
580 sum += t * s;
581
582 }
583
584 sum /= (cxdouble)(ns - i);
585
586 cpl_matrix_set(ccf, 0, i - start, sum);
587
588 }
589 else {
590
591 cxint j;
592
593
594 /*
595 * The central value
596 */
597
598 sum = 0.;
599
600 for (j = 0; j < ns; j++) {
601
602 cxdouble t = cpl_matrix_get(_template, 0, j);
603 cxdouble s = cpl_matrix_get(_signal, 0, j);
604
605 sum += t * s;
606
607 }
608
609 sum /= (cxdouble)ns;
610
611 cpl_matrix_set(ccf, 0, -start, sum);
612
613 }
614
615
616 }
617
618
619 /*
620 * Normalize peak to approximately 1.0. For this purpose the 10% of
621 * the cross-correlation function's data points with the highest
622 * values are used.
623 */
624
625 n = CX_MAX(1, nccf / 10);
626
627 _ccf = cpl_matrix_duplicate(ccf);
629
630 sum = 0.;
631
632 for (i = nccf - n; i < nccf; i++) {
633 sum += cpl_matrix_get(_ccf, 0, i);
634 }
635
636 sum /= (cxdouble)n;
637
638 cpl_matrix_delete(_ccf);
639 _ccf = NULL;
640
641 if (sum != 0.) {
642
643 for (i = 0; i < nccf; i++) {
644 cpl_matrix_set(ccf, 0, i, cpl_matrix_get(ccf, 0, i) / sum);
645 }
646
647 }
648
649 return ccf;
650
651}
652
653
654inline static cxint
655_giraffe_create_setup(GiSGSetup* setup, const GiImage* spectra)
656{
657
658 cpl_propertylist* properties = NULL;
659
660 cpl_image* _spectra = NULL;
661
662
663 cx_assert(setup != NULL);
664 cx_assert(spectra != NULL);
665
666 properties = giraffe_image_get_properties(spectra);
667 cx_assert(properties != NULL);
668
669 _spectra = giraffe_image_get(spectra);
670 cx_assert(_spectra != NULL);
671
672
673 /*
674 * Retrieve rebinned spectra information.
675 */
676
677 setup->nx = cpl_image_get_size_y(_spectra);
678
679
680 if (!cpl_propertylist_has(properties, GIALIAS_EXT_NX)) {
681 return 1;
682 }
683 else {
684
685 setup->nex = cpl_propertylist_get_int(properties, GIALIAS_EXT_NX);
686
687 }
688
689 if (!cpl_propertylist_has(properties, GIALIAS_BINSCALE)) {
690 return 1;
691 }
692 else {
693
694 const cxchar* s = cpl_propertylist_get_string(properties,
695 GIALIAS_BINSCALE);
696
697
698 if (cx_strncasecmp(s, "log", 3) == 0) {
699 setup->scale = GIREBIN_SCALE_LOG;
700 }
701 else {
702 setup->scale = GIREBIN_SCALE_LINEAR;
703 }
704
705 }
706
707 if (!cpl_propertylist_has(properties, GIALIAS_BINWLMIN)) {
708 return 1;
709 }
710 else {
711 setup->wlmin = cpl_propertylist_get_double(properties,
712 GIALIAS_BINWLMIN);
713 }
714
715 if (!cpl_propertylist_has(properties, GIALIAS_BINSTEP)) {
716 return 1;
717 }
718 else {
719 setup->wlstep = cpl_propertylist_get_double(properties,
720 GIALIAS_BINSTEP);
721 }
722
723 setup->wlmax = setup->wlmin + (setup->nx - 1) * setup->wlstep;
724
725
726 if (!cpl_propertylist_has(properties, GIALIAS_PIXSIZY)) {
727 return 1;
728 }
729 else {
730 setup->pixelsize = cpl_propertylist_get_double(properties,
731 GIALIAS_PIXSIZY);
732 }
733
734 return 0;
735
736}
737
738
739inline static cxint
740_giraffe_peak_fit(GiCPeakFit* peak, const cpl_matrix* lambda,
741 const cpl_matrix* ccf, const GiGrating* grating,
742 const GiCPFitParams* setup)
743{
744
745 const cxchar* const fctid = "_giraffe_peak_fit";
746
747
748 cxbool stop = FALSE;
749
750 cxint i;
751 cxint dn1 = 0;
752 cxint dn2 = 0;
753
754 cxdouble amplitude = 0.;
755 cxdouble background = 0.;
756 cxdouble center = 0.;
757 cxdouble width = 0.;
758 cxdouble lower = 0.;
759 cxdouble upper = 0.;
760
761 struct {
762 cxdouble amplitude;
763 cxdouble background;
764 cxdouble center;
765 cxdouble width;
766 } initial = {0., 0., 0., 0.};
767
768 cpl_size nr = 0;
769 cpl_size nc = 0;
770
771 GiModel* model = giraffe_model_new("gaussian");
772
773
774
775 cx_assert(model != NULL);
776 cx_assert(strcmp(giraffe_model_get_name(model), "gaussian") == 0);
777 cx_assert(lambda != NULL);
778 cx_assert(ccf != NULL);
779 cx_assert(grating != NULL);
780 cx_assert(setup != NULL);
781
782
783 /*
784 * Initial guesses of the peak profile model. For the background
785 * 0. can be used in case of ThAr spectra, otherwise the mean of
786 * the 2 lowest values of the CCF should be used. The half-width
787 * is derived from the nominal resolution of the grating.
788 */
789
790 background = 0.;
791
792 amplitude = cpl_matrix_get_max((cpl_matrix*)ccf) - background;
793
794 cpl_matrix_get_maxpos((cpl_matrix*)ccf, &nr, &nc);
795 cx_assert(nr == 0);
796
797 center = cpl_matrix_get((cpl_matrix*)lambda, 0, nc);
798
799
800 if (setup->scale == GIREBIN_SCALE_LOG) {
801 width = 0.5 / grating->resol;
802 }
803 else {
804 width = 0.5 / grating->resol * grating->wlen0;
805 }
806
807 giraffe_model_set_parameter(model, "Background", background);
808 giraffe_model_set_parameter(model, "Amplitude", amplitude);
809 giraffe_model_set_parameter(model, "Center", center);
810 giraffe_model_set_parameter(model, "Width1", width);
811
812 giraffe_model_thaw(model);
813
814 giraffe_model_set_iterations(model, setup->fit.iterations);
815 giraffe_model_set_tests(model, setup->fit.tests);
816 giraffe_model_set_delta(model, setup->fit.delta);
817
818
819 /*
820 * Save the initial parameter values.
821 */
822
823 initial.amplitude = amplitude;
824 initial.background = background;
825 initial.center = center;
826 initial.width = width;
827
828 i = 0;
829
830 while (i < setup->iterations && !stop) {
831
832 cxint j;
833 cxint _dn1 = 0;
834 cxint _dn2 = 0;
835
836 cxdouble dwc = 0.;
837 cxdouble dwd = 0.;
838
839 cpl_matrix* tlambda = (cpl_matrix*)lambda;
840 cpl_matrix* tccf = (cpl_matrix*)ccf;
841
842
843 /*
844 * The second iteration uses a weighted mean of the initial guess and
845 * the first result. For all further iterations the new parameter
846 * values are just taken from the previous iteration.
847 */
848
849 if (i == 1) {
850
851 const cxdouble da = 0.2;
852 const cxdouble dc = 1.;
853 const cxdouble db = 1.;
854 const cxdouble dw = 0.2;
855
856 cxdouble value = 0.;
857
858 value = giraffe_model_get_parameter(model, "Amplitude") * da;
859 value += (1. - da) * initial.amplitude;
860
861 giraffe_model_set_parameter(model, "Amplitude", value);
862
863
864 value = giraffe_model_get_parameter(model, "Center") * dc;
865 value += (1. - dc) * initial.center;
866
867 giraffe_model_set_parameter(model, "Center", value);
868
869
870 value = giraffe_model_get_parameter(model, "Background") * db;
871 value += (1. - db) * initial.background;
872
873 giraffe_model_set_parameter(model, "Background", value);
874
875
876 value = giraffe_model_get_parameter(model, "Width1") * dw;
877 value += (1. - dw) * initial.width;
878
879 giraffe_model_set_parameter(model, "Width1", value);
880
881 }
882
883
884 /*
885 * Set the window center and width. For the width a lower limit is
886 * established to guarantee a minimum number of point for the fit.
887 */
888
889 dwd = 2. * giraffe_model_get_parameter(model, "Width1") *
890 setup->wfactor;
891 dwc = giraffe_model_get_parameter(model, "Center");
892
893 dwd = CX_MAX(setup->dnmin, 2. * dwd / setup->step) * setup->step / 2.;
894
895 lower = dwc + 0.5 * setup->step - dwd;
896 upper = dwc + 0.5 * setup->step + dwd;
897
898
899 /*
900 * Extract the slices corresponding to the reduced window size
901 * from the input data arrays. This is the data set which is
902 * actually fitted.
903 */
904
905 for (j = 0; j < cpl_matrix_get_ncol(tlambda); j++) {
906
907 if (cpl_matrix_get(tlambda, 0, j) > lower) {
908 _dn1 = j;
909 break;
910 }
911
912 }
913
914 for (j = cpl_matrix_get_ncol(tlambda) - 1; j >= 0; j--) {
915
916 if (cpl_matrix_get(tlambda, 0, j) < upper) {
917 _dn2 = j + 1;
918 break;
919 }
920
921 }
922
923
924 if (i > 0 && dn1 == _dn1 && dn2 == _dn2) {
925
926 cxdouble _width = giraffe_model_get_parameter(model, "Width1");
927
928 /*
929 * This is the same set of points. The fitting stops after
930 * one last iteration on the further reduced data set.
931 */
932
933 dwd = CX_MAX(setup->dnmin, 4. * _width * setup->wfactor /
934 setup->step) * setup->step / 2.;
935
936 lower = dwc + 0.5 * setup->step - dwd;
937 upper = dwc + 0.5 * setup->step + dwd;
938
939 for (j = 0; j < cpl_matrix_get_ncol(tlambda); j++) {
940
941 if (cpl_matrix_get(tlambda, 0, j) > lower) {
942 _dn1 = j;
943 break;
944 }
945
946 }
947
948 for (j = cpl_matrix_get_ncol(tlambda) - 1; j <= 0; j--) {
949
950 if (cpl_matrix_get(tlambda, 0, j) < upper) {
951 _dn2 = j + 1;
952 break;
953 }
954
955 }
956
957 stop = TRUE;
958
959 }
960
961
962 /* FIXME: The original code uses i == 0 instead of i <= 1. Check
963 * whether there is a reason for that or if this is just
964 * a bug.
965 */
966
967 if (i <= 1 || dn1 != _dn1 || dn2 != _dn2) {
968
969 cxbool flag = FALSE;
970
971 const cxint pflag = 1;
972 cxint status = 0;
973
974 cxdouble damplitude = 0.;
975 cxdouble dbackground = 0.;
976 cxdouble dcenter = 0.;
977 cxdouble dwidth = 0.;
978
979 cpl_matrix* x = NULL;
980 cpl_matrix* y = NULL;
981 cpl_matrix* sigma = NULL;
982
983
984 dn1 = _dn1;
985 dn2 = _dn2;
986
987 x = cpl_matrix_new(dn2 - dn1, 1);
988 y = cpl_matrix_new(dn2 - dn1, 1);
989 sigma = cpl_matrix_new(dn2 - dn1, 1);
990
991 for (j = 0; j < cpl_matrix_get_nrow(y); j++) {
992
993 cpl_matrix_set(x, j, 0, cpl_matrix_get(tlambda, 0, dn1 + j));
994 cpl_matrix_set(y, j, 0, cpl_matrix_get(tccf, 0, dn1 + j));
995 cpl_matrix_set(sigma, j, 0, setup->sigma);
996
997 }
998
999
1000 /*
1001 * Finally, fit the peak profile.
1002 */
1003
1004 status = giraffe_model_fit(model, x, y, sigma);
1005
1006 if (status != 0) {
1007
1008 peak->amplitude.value = initial.amplitude;
1009 peak->background.value = initial.background;
1010 peak->center.value = initial.center;
1011 peak->width.value = initial.width;
1012
1013 peak->amplitude.sigma = 1.;
1014 peak->background.sigma = 1.;
1015 peak->center.sigma = 1.;
1016 peak->width.sigma = 1.;
1017
1018 peak->status = -1;
1019
1020 cpl_matrix_delete(x);
1021 cpl_matrix_delete(y);
1022 cpl_matrix_delete(sigma);
1023
1024 giraffe_model_delete(model);
1025
1026 return 1;
1027
1028 }
1029
1030
1031 /*
1032 * Check `out of bounds' condition for the fitted parameters.
1033 * and their uncertainties.
1034 */
1035
1036 amplitude = giraffe_model_get_parameter(model, "Amplitude");
1037 damplitude = giraffe_model_get_sigma(model, "Amplitude");
1038
1039 center = giraffe_model_get_parameter(model, "Center");
1040 dcenter = giraffe_model_get_sigma(model, "Center");
1041
1042 background = giraffe_model_get_parameter(model, "Background");
1043 dbackground = giraffe_model_get_sigma(model, "Background");
1044
1045 width = giraffe_model_get_parameter(model, "Width1");
1046 dwidth = giraffe_model_get_sigma(model, "Width1");
1047
1048
1049 /* FIXME: Where do these limits come from? (RP)
1050 */
1051
1052 /* Amplitude */
1053
1054 lower = -9. * (1 - pflag) + 1.e-5 * pflag;
1055 upper = 9. * pflag - 1.e-5 * (1 - pflag);
1056
1057 peak->amplitude.value = _giraffe_clip_value(amplitude, lower,
1058 upper, &flag);
1059 peak->amplitude.sigma = _giraffe_clip_value(damplitude, 0.,
1060 1., NULL);
1061
1062 stop = stop == FALSE ? flag == TRUE ? TRUE : FALSE : stop;
1063
1064 /* Center */
1065
1066 lower = cpl_matrix_get(x, 1, 0);
1067 upper = cpl_matrix_get(x, cpl_matrix_get_nrow(x) - 2, 0);
1068
1069 peak->center.value = _giraffe_clip_value(center, lower,
1070 upper, &flag);
1071 peak->center.sigma = _giraffe_clip_value(dcenter, 0.,
1072 initial.width, NULL);
1073
1074 stop = stop == FALSE ? flag == TRUE ? TRUE : FALSE : stop;
1075
1076 /* Background */
1077
1078 lower = -2;
1079 upper = 2.;
1080
1081 peak->background.value = _giraffe_clip_value(background, lower,
1082 upper, &flag);
1083 peak->background.sigma = _giraffe_clip_value(dbackground, 0.,
1084 1., NULL);
1085
1086 stop = stop == FALSE ? flag == TRUE ? TRUE : FALSE : stop;
1087
1088 /* Width */
1089
1090 lower = 0.5 * initial.width;
1091 upper = 2. * (cpl_matrix_get(x, cpl_matrix_get_nrow(x) - 2, 0) -
1092 cpl_matrix_get(x, 0, 0));
1093
1094 peak->width.value = _giraffe_clip_value(width, lower,
1095 upper, &flag);
1096 peak->width.sigma = _giraffe_clip_value(dwidth, 0.,
1097 9., NULL);
1098
1099 stop = stop == FALSE ? flag == TRUE ? TRUE : FALSE : stop;
1100
1101 cpl_matrix_delete(x);
1102 cpl_matrix_delete(y);
1103 cpl_matrix_delete(sigma);
1104
1105 if (stop == TRUE) {
1106 cpl_msg_debug(fctid, "Cross-correlation peak fit "
1107 "parameter out of bounds!");
1108
1109 peak->status = 1;
1110 }
1111 else {
1112 peak->status = 0;
1113 }
1114
1115 ++i;
1116
1117 }
1118 else {
1119
1120 stop = TRUE;
1121
1122 }
1123
1124 }
1125
1126 giraffe_model_delete(model);
1127
1128 return 0;
1129
1130}
1131
1132
1133inline static cxint
1134_giraffe_compute_fiber_offsets(cpl_table* offsets,
1135 const GiGrating* grating,
1136 const GiSGSetup* setup)
1137{
1138
1139 cxint i;
1140
1141 const cxdouble ccdfactor = 1.1;
1142
1143 cxdouble gcamera = 1.;
1144 cxdouble cfactor = 1.;
1145 cxdouble lincorr = 1.;
1146 cxdouble wlen0 = 0.;
1147
1148
1149 cx_assert(offsets != NULL);
1150
1151 if (!cpl_table_has_column(offsets, "WAVELENGTH")) {
1152 return 1;
1153 }
1154
1155 if (!cpl_table_has_column(offsets, "DWF")) {
1156 cpl_table_new_column(offsets, "DWF", CPL_TYPE_DOUBLE);
1157 }
1158
1159 if (!cpl_table_has_column(offsets, "DXF")) {
1160 cpl_table_new_column(offsets, "DXF", CPL_TYPE_DOUBLE);
1161 }
1162
1163
1164 /*
1165 * Compute the central wavelength of the spectral band, taking into
1166 * account the scaling used to rebin the spectra.
1167 */
1168
1169 if (setup->scale == GIREBIN_SCALE_LOG) {
1170 wlen0 = 0.5 * (exp(setup->wlmin) + exp(setup->wlmax));
1171 }
1172 else {
1173 wlen0 = 0.5 * (setup->wlmin + setup->wlmax);
1174 }
1175
1176
1177 /*
1178 * Approximate magnification of the camera.
1179 */
1180
1181 /*
1182 * FIXME: Any hint on these numbers? (RP)
1183 */
1184
1185 gcamera = 0.3894 - 5. * (1. / wlen0 - 1. / 550.) -
1186 0.00025 * pow(1. / wlen0 - 1. / 550., 2.);
1187
1188 /*
1189 * Conversion factor from CCD displacement to slit geometry.
1190 */
1191
1192 /* FIXME: This will be used until there is a better formula
1193 * (OGL comment).
1194 */
1195
1196 cfactor = (setup->nex * setup->pixelsize / 1000. * ccdfactor) /
1197 ((grating->wlenmax - grating->wlenmin) * gcamera);
1198
1199
1200 /*
1201 * Correction factor for linear scale on the correlation
1202 */
1203
1204 if (setup->scale == GIREBIN_SCALE_LOG) {
1205 lincorr = 1.0;
1206 }
1207 else {
1208 lincorr = 0.5 * (setup->wlmin + setup->wlmax) /
1209 exp(0.5 * (log(setup->wlmin) + log(setup->wlmax)));
1210 }
1211
1212
1213 /*
1214 * Compute slit offsets
1215 */
1216
1217 for (i = 0; i < cpl_table_get_nrow(offsets); i++) {
1218
1219
1220 cxdouble dwf = cpl_table_get_double(offsets, "WAVELENGTH", i, NULL);
1221 cxdouble dxf = 0.;
1222
1223
1224 dwf *= -lincorr;
1225 dxf = dwf * cfactor;
1226
1227 cpl_table_set_double(offsets, "DWF", i, dwf);
1228 cpl_table_set_double(offsets, "DXF", i, dxf);
1229
1230 }
1231
1232 return 0;
1233
1234}
1235
1236
1237inline static cpl_table*
1238_giraffe_compute_offsets(const GiImage* spectra, const GiTable* mask,
1239 const cpl_table* fibers, const GiGrating* grating,
1240 const GiSGSetup* setup, const GiSGCalConfig* config)
1241{
1242
1243 const cxchar* const fctid = "_giraffe_compute_offsets";
1244
1245 const cxint dnmin = 7; /* Minimum number of points */
1246
1247 cxint i;
1248 cxint k;
1249 cxint status = 0;
1250 cxint sampling = 0;
1251 cxint pixel0 = 0;
1252 cxint dn1 = 0;
1253 cxint dn2 = 0;
1254 cxint dnc = 0;
1255 cxint dnd = 0;
1256 cxint xc1 = 0;
1257 cxint xc2 = 0;
1258
1259 const cxdouble clight = 299702.547; /* light speed in air [km/s] */
1260
1261 cxdouble cstep = 0.;
1262 cxdouble wlen0 = 0.;
1263 cxdouble nm2km = clight;
1264 cxdouble hpixels = 0.;
1265 cxdouble dv1 = 0.;
1266 cxdouble dv2 = 0.;
1267 cxdouble dw1 = 0.;
1268 cxdouble dw2 = 0.;
1269 cxdouble dwc = 0.;
1270 cxdouble dwd = 0.;
1271
1272 cpl_matrix* spectrum = NULL;
1273
1274 cpl_image* _spectra = NULL;
1275 cpl_image* tspectra = NULL;
1276
1277 cpl_table* peakdata = NULL;
1278
1279 GiSGMask* _mask = NULL;
1280
1281
1282 cx_assert(spectra != NULL);
1283 cx_assert(mask != NULL);
1284 cx_assert(fibers != NULL);
1285 cx_assert(grating != NULL);
1286 cx_assert(setup != NULL);
1287 cx_assert(config != NULL);
1288
1289
1290 /*
1291 * Compute the sampling step size
1292 */
1293
1294 if (config->cc_step <= 0.) {
1295 sampling = 1;
1296 }
1297 else {
1298
1299 if (setup->scale == GIREBIN_SCALE_LOG) {
1300
1301 cxdouble wlstep = (exp(setup->wlmax) - exp(setup->wlmin)) /
1302 setup->nx;
1303
1304 sampling = (cxint)(0.5 + wlstep / config->cc_step);
1305
1306 }
1307 else {
1308
1309 sampling = (cxint)(0.5 + setup->wlstep / config->cc_step);
1310
1311 }
1312
1313 }
1314
1315 cstep = setup->wlstep / sampling;
1316
1317
1318 /*
1319 * Create and initialize the final mask
1320 */
1321
1322 _mask = _giraffe_sgmask_create((setup->nx - 1) * sampling + 1,
1323 setup->wlmin, cstep, setup->scale,
1324 mask);
1325
1326 if (_mask == NULL) {
1327 return NULL;
1328 }
1329
1330
1331 /*
1332 * Prepare the initial window
1333 */
1334
1335 pixel0 = setup->nx / 2;
1336
1337 if (setup->scale == GIREBIN_SCALE_LOG) {
1338
1339 /*
1340 * Logarithmic scale: dv / clight = d(log(lambda))
1341 */
1342
1343 wlen0 = 0.5 * (exp(setup->wlmin) + exp(setup->wlmax));
1344 nm2km = clight;
1345
1346 }
1347 else {
1348
1349 /*
1350 * Linear scale: dv / clight = d(log(lambda)) / lambda
1351 */
1352
1353 wlen0 = 0.5 * (setup->wlmin + setup->wlmax);
1354 nm2km = clight / wlen0;
1355
1356 }
1357
1358
1359 /*
1360 * Window limits in km/s, nm and pxl and window center and
1361 * half-width in nm and pxl.
1362 */
1363
1364 dv1 = giraffe_range_get_min(config->rv_limits);
1365 dv2 = giraffe_range_get_max(config->rv_limits);
1366
1367 dw1 = dv1 / nm2km;
1368 dw2 = dv2 / nm2km;
1369
1370 cpl_msg_debug(fctid, "Cross-correlation limits: RVlow = %.4f km/s "
1371 "(%.4f nm), RVhigh = %.4f km/s (%.4f nm)", dv1, dw1,
1372 dv2, dw2);
1373
1374 dwd = (dw2 - dw1) / 2.;
1375 dwc = (dw2 + dw1) / 2.;
1376
1377 dnd = CX_MIN(pixel0, CX_MAX(dnmin, (cxint)(dwd / cstep + 0.5)));
1378 dnc = CX_MIN(pixel0, CX_MAX(-pixel0, (cxint)(dwc / cstep + 0.5)));
1379
1380 dn1 = CX_MIN(pixel0 + 1, CX_MAX(-pixel0, dnc - dnd));
1381 dn2 = CX_MIN(pixel0 + 1, CX_MAX(-pixel0, dnc + dnd + 1));
1382
1383 cpl_msg_debug(fctid, "Cross-correlation window: center = %.4f nm "
1384 "(%d pxl) half-width = %.4f nm (%d pxl)", dwc, dnc,
1385 dwd, dnd);
1386
1387
1388 /*
1389 * Select spectral range of the spectra and the template which should
1390 * be used for the cross-correlation.
1391 */
1392
1393 xc1 = (cxint)(giraffe_range_get_min(config->cc_domain) * sampling);
1394 xc2 = (cxint)(giraffe_range_get_max(config->cc_domain) * sampling);
1395
1396 if (xc1 > 0 || xc2 > 0) {
1397 _giraffe_sgmask_crop(_mask, xc1, xc2);
1398 }
1399
1400 for (i = 0; (cxsize)i < _giraffe_sgmask_size(_mask); i++) {
1401
1402 cxdouble value = _giraffe_sgmask_get_flux(_mask, i);
1403
1404 if (value > 0.) {
1405 hpixels += value;
1406 }
1407
1408 }
1409
1410 hpixels /= _giraffe_sgmask_holes(_mask);
1411
1412
1413 /*
1414 * The left- and rightmost dn1 points of the mask are set to 0. In
1415 * addition partial holes at the beginning and the end of the mask
1416 * removed, i.e. set to 0 flux.
1417 */
1418
1419 i = 0;
1420 k = CX_MAX(0, -dn1);
1421
1422 while (i < k || _giraffe_sgmask_get_flux(_mask, i) > 0.) {
1423
1424 _giraffe_sgmask_set_flux(_mask, i, 0.);
1425 ++i;
1426
1427 }
1428
1429 cpl_msg_debug(fctid, "Mask cleared from 0 to %d", i - 1);
1430
1431 i = _giraffe_sgmask_size(_mask);
1432 k = _giraffe_sgmask_size(_mask) - CX_MAX(0, dn2);
1433
1434 while (i > k || _giraffe_sgmask_get_flux(_mask, i) > 0.) {
1435
1436 _giraffe_sgmask_set_flux(_mask, i, 0.);
1437 --i;
1438
1439 }
1440
1441 cpl_msg_debug(fctid, "Mask cleared from %d to %ld", k,
1442 (long)(_giraffe_sgmask_size(_mask) - 1));
1443
1444
1445 /*
1446 * Resample the input image to the mask's sampling step and crop its
1447 * spectral range so that it matches the template.
1448 */
1449
1450 _spectra = cpl_image_duplicate(giraffe_image_get(spectra));
1451
1452 if (_spectra == NULL) {
1453
1454 _giraffe_sgmask_delete(_mask);
1455
1456 return NULL;
1457
1458 }
1459
1460
1461 if (config->zmax > 0.) {
1462
1463 cpl_image_threshold(_spectra, CX_MINDOUBLE, config->zmax,
1464 0., config->zmax);
1465
1466 }
1467
1468
1469 tspectra = _giraffe_resample_image(_spectra, setup->wlstep, cstep);
1470
1471 if (tspectra == NULL) {
1472
1473 cpl_image_delete(_spectra);
1474
1475 _giraffe_sgmask_delete(_mask);
1476
1477 return NULL;
1478
1479 }
1480
1481 cpl_image_delete(_spectra);
1482 _spectra = NULL;
1483
1484 if (xc1 > 0 || xc2 > 0) {
1485
1486 _spectra = cpl_image_extract(tspectra, 1, xc1 + 1,
1487 cpl_image_get_size_x(tspectra), xc2 + 1);
1488
1489 if (_spectra == NULL) {
1490
1491 cpl_image_delete(tspectra);
1492
1493 _giraffe_sgmask_delete(_mask);
1494
1495 return NULL;
1496
1497 }
1498
1499 cpl_image_delete(tspectra);
1500 tspectra = NULL;
1501
1502 }
1503 else {
1504
1505 _spectra = tspectra;
1506 tspectra = NULL;
1507
1508 }
1509
1510
1511 /*
1512 * Create the table to record the results from the cross-correlation
1513 * peak fitting for each fiber.
1514 */
1515
1516 peakdata = cpl_table_new(cpl_table_get_nrow(fibers));
1517
1518 cpl_table_duplicate_column(peakdata, "INDEX", (cpl_table*)fibers,
1519 "INDEX");
1520 cpl_table_duplicate_column(peakdata, "FPS", (cpl_table*)fibers,
1521 "FPS");
1522
1523 cpl_table_new_column(peakdata, "WAVELENGTH", CPL_TYPE_DOUBLE);
1524 cpl_table_new_column(peakdata, "FWHM", CPL_TYPE_DOUBLE);
1525 cpl_table_new_column(peakdata, "AMPLITUDE", CPL_TYPE_DOUBLE);
1526 cpl_table_new_column(peakdata, "BACKGROUND", CPL_TYPE_DOUBLE);
1527 cpl_table_new_column(peakdata, "RV", CPL_TYPE_DOUBLE);
1528 cpl_table_new_column(peakdata, "RVERR", CPL_TYPE_DOUBLE);
1529 cpl_table_new_column(peakdata, "RESOLUTION", CPL_TYPE_DOUBLE);
1530 cpl_table_new_column(peakdata, "STATUS", CPL_TYPE_INT);
1531
1532
1533 /*
1534 * Compute the cross-correlation with the mask for each spectrum in
1535 * the input image.
1536 */
1537
1538 cpl_msg_debug(fctid, "Computing cross-correlation: central wavelength = "
1539 "%.4f, window = [%.4f, %.4f] [km/s]", wlen0, dv1, dv2);
1540
1541 spectrum = cpl_matrix_new(1, cpl_image_get_size_y(_spectra));
1542
1543 for (i = 0; i < cpl_table_get_nrow(fibers); i++) {
1544
1545 cxint j;
1546 cxint ns = cpl_image_get_size_x(_spectra);
1547 cxint fiber = cpl_table_get_int(fibers, "FPS", i, NULL);
1548 cxint idx = cpl_table_get_int(fibers, "INDEX", i, NULL) - 1;
1549
1550 const cxdouble fwhm_ratio = 2. * sqrt(2. * log(2.));
1551
1552 cxdouble avsigma = 0.;
1553 cxdouble fx = 0.;
1554 cxdouble fxtotal = 0.;
1555 /*cxdouble fxaverage = 0.;*/
1556 cxdouble fxmask = 0.;
1557 cxdouble sum = 0.;
1558 cxdouble position = 0.;
1559 cxdouble fwhm = 0.;
1560 cxdouble width = 0.;
1561 cxdouble resolution = 0.;
1562 cxdouble rv = 0.;
1563 cxdouble rverr = 0.;
1564 cxdouble* data = cpl_image_get_data(_spectra);
1565
1566 const cpl_matrix* template = NULL;
1567 cpl_matrix* ccf = NULL;
1568 cpl_matrix* lambda = NULL;
1569
1570 GiCPFitParams peak_setup;
1571 GiCPeakFit peak;
1572
1573
1574
1575 /*
1576 * Copy the current spectrum to the working matrix and
1577 * compute the total flux of the masked spectrum.
1578 */
1579
1580 for (j = 0; j < cpl_matrix_get_ncol(spectrum); j++) {
1581
1582 cxdouble flux = data[j * ns + idx];
1583
1584
1585 cpl_matrix_set(spectrum, 0, j, flux);
1586
1587 fxtotal += flux;
1588 fxmask += _giraffe_sgmask_get_flux(_mask, j);
1589 fx += flux * _giraffe_sgmask_get_flux(_mask, j);
1590
1591 }
1592
1593 fx /= sampling;
1594 /*fxaverage = fxtotal / fxmask;*/
1595
1596 if (fx > 0.) {
1597 avsigma = 1. / sqrt(fx);
1598 }
1599
1600 cpl_msg_debug(fctid, "Cross-correlation of spectrum %d in window "
1601 "from %d pxl to %d pxl (%.4f nm to %.4f nm)", fiber,
1602 dn1, dn2, dw1, dw2);
1603
1604
1605 /*
1606 * Wavelength within the cross-correlation window
1607 */
1608
1609 lambda = cpl_matrix_new(1, dn2 - dn1);
1610
1611 for (j = dn1; j < dn2; j++) {
1612 cpl_matrix_set(lambda, 0, j - dn1, j * cstep);
1613 }
1614
1615
1616 /*
1617 * Cross-correlation
1618 */
1619
1620 template = _giraffe_sgmask_get(_mask);
1621
1622 ccf = _giraffe_compute_cross_correlation(spectrum, template, dn1, dn2);
1623
1624 if (ccf == NULL) {
1625
1626 cpl_matrix_delete(lambda);
1627 cpl_matrix_delete(spectrum);
1628
1629 cpl_image_delete(_spectra);
1630
1631 cpl_table_delete(peakdata);
1632
1633 _giraffe_sgmask_delete(_mask);
1634
1635 return NULL;
1636
1637 }
1638
1639 sum = 0.;
1640
1641 for (j = 0; j < cpl_matrix_get_ncol(ccf); j++) {
1642 sum += cpl_matrix_get(ccf, 0, j);
1643 }
1644
1645 if (sum <= 0.) {
1646 cpl_msg_debug(fctid, "Cross-correlation failed: Skipping "
1647 "spectrum %d.", fiber);
1648
1649 cpl_matrix_delete(lambda);
1650 lambda = NULL;
1651
1652 continue;
1653 }
1654
1655
1656 /*
1657 * Fit the cross-correlation peak
1658 */
1659
1660 peak_setup.dnmin = dnmin;
1661 peak_setup.iterations = config->rv_niter;
1662 peak_setup.step = cstep;
1663 peak_setup.wfactor = config->rv_wfactor;
1664 peak_setup.sigma = avsigma;
1665 peak_setup.scale = setup->scale;
1666
1667 peak_setup.fit.iterations = config->pf_niter;
1668 peak_setup.fit.tests = config->pf_ntest;
1669 peak_setup.fit.delta = config->pf_dchisq;
1670
1671 status = _giraffe_peak_fit(&peak, lambda, ccf, grating, &peak_setup);
1672
1673 if (status < 0) {
1674
1675 cpl_matrix_delete(ccf);
1676 cpl_matrix_delete(lambda);
1677
1678 cpl_matrix_delete(spectrum);
1679 cpl_image_delete(_spectra);
1680
1681 cpl_table_delete(peakdata);
1682
1683 _giraffe_sgmask_delete(_mask);
1684
1685 return NULL;
1686
1687 }
1688
1689
1690 /*
1691 * Save the results to the output table.
1692 */
1693
1694 if (setup->scale == GIREBIN_SCALE_LOG) {
1695 position = peak.center.value * wlen0;
1696 fwhm = (exp(peak.width.value) - 1.) * wlen0;
1697 }
1698 else {
1699 position = peak.center.value;
1700 fwhm = peak.width.value;
1701 }
1702
1703 width = pow(fwhm_ratio * fwhm, 2.) - pow(0.6 * hpixels * cstep, 2.);
1704 resolution = width > 0. ? wlen0 / sqrt(width) : 0.;
1705
1706 fwhm *= 2.;
1707
1708 rv = CX_MAX(dv1, CX_MIN(dv2, peak.center.value * nm2km));
1709 rverr = CX_MIN(dv2 - dv1, peak.center.sigma * nm2km);
1710
1711 cpl_table_set_double(peakdata, "WAVELENGTH", i, position);
1712 cpl_table_set_double(peakdata, "FWHM", i, fwhm);
1713 cpl_table_set_double(peakdata, "AMPLITUDE", i, peak.amplitude.value);
1714 cpl_table_set_double(peakdata, "BACKGROUND", i,
1715 peak.background.value);
1716 cpl_table_set_double(peakdata, "RESOLUTION", i, resolution);
1717 cpl_table_set_double(peakdata, "RV", i, rv);
1718 cpl_table_set_double(peakdata, "RVERR", i, rverr);
1719 cpl_table_set_int(peakdata, "STATUS", i, peak.status);
1720
1721 cpl_matrix_delete(lambda);
1722 cpl_matrix_delete(ccf);
1723
1724 }
1725
1726 cpl_matrix_delete(spectrum);
1727 cpl_image_delete(_spectra);
1728
1729 _giraffe_sgmask_delete(_mask);
1730
1731 return peakdata;
1732
1733}
1734
1735
1736inline static cpl_table*
1737_giraffe_compute_slitgeometry(const GiImage* spectra, const GiTable* mask,
1738 const GiTable* slitgeometry,
1739 const GiGrating* grating,
1740 const GiSGCalConfig* config)
1741{
1742
1743 cxint status = 0;
1744
1745 cpl_table* _slitgeometry = giraffe_table_get(slitgeometry);
1746 cpl_table* peakdata = NULL;
1747
1748 GiSGSetup setup;
1749
1750
1751 /*
1752 * Get setup information from the rebinned spectra frame
1753 */
1754
1755 status = _giraffe_create_setup(&setup, spectra);
1756
1757 if (status != 0) {
1758 return NULL;
1759 }
1760
1761 /*
1762 * Compute the wavelength shifts between the reference mask and
1763 * the arc-lamp spectra, from the position of the cross-correlation
1764 * peak.
1765 *
1766 * Note that either a fiber, or a slitgeometry table may be passed to
1767 * _giraffe_compute_offsets(). Actually any table providing the
1768 * columns "INDEX" and "FPS", describing the pixel column of each
1769 * spectrum in the input image and the fiber position within the
1770 * pseudo slit.
1771 */
1772
1773 peakdata = _giraffe_compute_offsets(spectra, mask, _slitgeometry,
1774 grating, &setup, config);
1775
1776 if (peakdata == NULL) {
1777 return NULL;
1778 }
1779
1780
1781 /*
1782 * Compute the offsets of the fibers in the pseudo-slit (i.e. in the
1783 * focal plane.
1784 */
1785
1786 status = _giraffe_compute_fiber_offsets(peakdata, grating, &setup);
1787
1788 if (status != 0) {
1789 cpl_table_delete(peakdata);
1790 return NULL;
1791 }
1792
1793
1794 /*
1795 * Compute fiber positions
1796 */
1797
1798 cpl_table_duplicate_column(peakdata, "XF", _slitgeometry, "XF");
1799 cpl_table_add_columns(peakdata, "XF", "DXF");
1800
1801 return peakdata;
1802
1803}
1804
1805
1806inline static GiTable*
1807_giraffe_slitgeometry_table(const cpl_table* offsets,
1808 const GiImage* spectra,
1809 const GiTable* fibers,
1810 const GiTable* slitgeometry,
1811 const GiSGCalConfig* config)
1812{
1813
1814 const cxchar* idx = NULL;
1815
1816 cxint i;
1817
1818 cpl_propertylist* properties = NULL;
1819 cpl_propertylist* _properties = NULL;
1820
1821 cpl_table* _slit = NULL;
1822 cpl_table* _fibers = NULL;
1823 cpl_table* _slitgeometry = NULL;
1824
1825 GiTable* slit = NULL;
1826
1827
1828 cx_assert(spectra != NULL);
1829 cx_assert(fibers != NULL);
1830
1831 _fibers = giraffe_table_get(fibers);
1832 cx_assert(_fibers != NULL);
1833
1834 _slitgeometry = giraffe_table_get(slitgeometry);
1835 cx_assert(_slitgeometry != NULL);
1836
1837 if (offsets == NULL) {
1838 return NULL;
1839 }
1840
1841
1842 slit = giraffe_table_new();
1843
1844 properties = giraffe_image_get_properties(spectra);
1845 cx_assert(properties != NULL);
1846
1847 giraffe_error_push();
1848
1849 _properties = cpl_propertylist_new();
1850
1851 giraffe_propertylist_copy(_properties, GIALIAS_INSTRUMENT, properties,
1852 GIALIAS_INSTRUMENT);
1853
1854 giraffe_propertylist_copy(_properties, GIALIAS_DATEOBS, properties,
1855 GIALIAS_DATEOBS);
1856
1857 giraffe_propertylist_copy(_properties, GIALIAS_MJDOBS, properties,
1858 GIALIAS_MJDOBS);
1859
1860 giraffe_propertylist_copy(_properties, GIALIAS_INSMODE, properties,
1861 GIALIAS_INSMODE);
1862
1863 giraffe_propertylist_copy(_properties, GIALIAS_INSMODE, properties,
1864 GIALIAS_INSMODE);
1865
1866 giraffe_propertylist_copy(_properties, GIALIAS_SETUPNAME, properties,
1867 GIALIAS_SETUPNAME);
1868
1869 giraffe_propertylist_copy(_properties, GIALIAS_SLITNAME, properties,
1870 GIALIAS_SLITNAME);
1871
1872 giraffe_propertylist_copy(_properties, GIALIAS_FILTNAME, properties,
1873 GIALIAS_FILTNAME);
1874
1875 giraffe_propertylist_copy(_properties, GIALIAS_GRATNAME, properties,
1876 GIALIAS_GRATNAME);
1877
1878 giraffe_propertylist_copy(_properties, GIALIAS_GRATWLEN, properties,
1879 GIALIAS_GRATWLEN);
1880
1881 giraffe_propertylist_copy(_properties, GIALIAS_GRATORDER, properties,
1882 GIALIAS_GRATORDER);
1883
1884 cpl_propertylist_update_double(_properties, GIALIAS_SCAL_CUTOFF,
1885 config->zmax);
1886 cpl_propertylist_set_comment(_properties, GIALIAS_SCAL_CUTOFF,
1887 "Cutoff pixel value.");
1888
1889 cpl_propertylist_update_string(_properties, GIALIAS_GIRFTYPE, "SLITGEOTAB");
1890 cpl_propertylist_set_comment(_properties, GIALIAS_GIRFTYPE,
1891 "Giraffe frame type.");
1892
1893
1894 _slit = cpl_table_new(cpl_table_get_nrow(_fibers));
1895
1896 cpl_table_new_column(_slit, "INDEX", CPL_TYPE_INT);
1897
1898 for (i = 0; i < cpl_table_get_nrow(_slit); i++) {
1899 cpl_table_set_int(_slit, "INDEX", i, i + 1);
1900 }
1901
1902 cpl_table_duplicate_column(_slit, "FPS", (cpl_table*)_fibers, "FPS");
1903 cpl_table_duplicate_column(_slit, "SSN", (cpl_table*)_fibers, "SSN");
1904 cpl_table_duplicate_column(_slit, "XF", (cpl_table*)offsets, "XF");
1905 cpl_table_duplicate_column(_slit, "YF", (cpl_table*)_slitgeometry, "YF");
1906
1907 if (cpl_table_has_column(_slitgeometry, "ZF")) {
1908 cpl_table_duplicate_column(_slit, "ZF",
1909 (cpl_table*)_slitgeometry, "ZF");
1910 }
1911
1912 if (cpl_table_has_column(_slitgeometry, "ZDEFOCUS")) {
1913 cpl_table_duplicate_column(_slit, "ZDEFOCUS",
1914 (cpl_table*)_slitgeometry, "ZDEFOCUS");
1915 }
1916
1917 cpl_table_duplicate_column(_slit, "RV", (cpl_table*)offsets, "RV");
1918 cpl_table_duplicate_column(_slit, "RVERR", (cpl_table*)offsets, "RVERR");
1919 cpl_table_duplicate_column(_slit, "RESOLUTION", (cpl_table*)offsets,
1920 "RESOLUTION");
1921
1922 idx = giraffe_fiberlist_query_index(_fibers);
1923 cpl_table_duplicate_column(_slit, "RINDEX", _fibers, idx);
1924
1925 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1926 cpl_propertylist_delete(_properties);
1927 cpl_table_delete(_slit);
1928
1930
1931 return NULL;
1932 }
1933
1934 giraffe_error_pop();
1935
1936 giraffe_table_set_properties(slit, _properties);
1937 cpl_propertylist_delete(_properties);
1938
1939 giraffe_table_set(slit, _slit);
1940 cpl_table_delete(_slit);
1941
1942 return slit;
1943
1944}
1945
1946
1952cxint
1953giraffe_calibrate_slit(GiTable* result, const GiExtraction* extraction,
1954 const GiLocalization* localization,
1955 const GiTable* fibers, const GiTable* wlsolution,
1956 const GiTable* slitgeometry, const GiTable* grating,
1957 const GiTable* mask, const GiSGCalConfig* config)
1958{
1959
1960 const cxchar* const fctid = "giraffe_calibrate_slit";
1961
1962 cxint i;
1963 cxint status = 0;
1964
1965 cpl_table* _fibers = NULL;
1966 cpl_table* _slitgeometry = NULL;
1967 cpl_table* offsets = NULL;
1968
1969 GiTable* slit = NULL;
1970
1971 GiGrating* setup = NULL;
1972
1973 GiExtraction* _extraction = NULL;
1974
1975
1976 if (result == NULL) {
1977 return 1;
1978 }
1979
1980 if (extraction == NULL) {
1981 return 1;
1982 }
1983
1984 if (extraction->spectra == NULL) {
1985 return 1;
1986 }
1987
1988 if (localization == NULL) {
1989 return 1;
1990 }
1991
1992 if (fibers == NULL) {
1993 return 1;
1994 }
1995
1996 if (wlsolution == NULL) {
1997 return 1;
1998 }
1999
2000 if (slitgeometry == NULL) {
2001 return 1;
2002 }
2003
2004 if (grating == NULL) {
2005 return 1;
2006 }
2007
2008 if (mask == NULL) {
2009 return 1;
2010 }
2011
2012 if (config == NULL) {
2013 return 1;
2014 }
2015
2016
2017 _fibers = giraffe_table_get(fibers);
2018 cx_assert(_fibers != NULL);
2019
2020 _slitgeometry = giraffe_table_get(slitgeometry);
2021 cx_assert(_slitgeometry != NULL);
2022
2023 if (cpl_table_get_nrow(_fibers) != cpl_table_get_nrow(_slitgeometry)) {
2024 return 2;
2025 }
2026
2027
2028 /*
2029 * Create grating setup
2030 */
2031
2032 setup = giraffe_grating_create(extraction->spectra, grating);
2033
2034 if (setup == NULL) {
2035 return 3;
2036 }
2037
2038
2039 /*
2040 * Set up the spectrum rebinning. Make sure that only the spectra are
2041 * rebinned.
2042 */
2043
2044 _extraction = giraffe_extraction_new();
2045
2046 _extraction->spectra = extraction->spectra;
2047 _extraction->error = NULL;
2048
2049
2050 slit = giraffe_table_duplicate(slitgeometry);
2051
2052 for (i = 0; i < config->repeat; i++) {
2053
2054 cxint fps_rvmin = 0;
2055 cxint fps_rvmax = 0;
2056
2057 cxdouble rvmin = 0.;
2058 cxdouble rvmax = 0.;
2059 cxdouble rvmean = 0.;
2060
2061 cpl_size row = 0;
2062
2063 GiRebinning* rebinning = giraffe_rebinning_new();
2064
2065 GiRebinConfig rebin_config = {
2066 GIREBIN_METHOD_LINEAR,
2067 TRUE,
2068 0.005,
2069 GIREBIN_SCALE_LINEAR,
2070 0,
2071 GIREBIN_RANGE_SETUP
2072 };
2073
2074
2075 status = giraffe_rebin_spectra(rebinning, _extraction, fibers,
2076 localization, grating, slit,
2077 wlsolution, &rebin_config);
2078
2079 if (status != 0) {
2081
2082 giraffe_extraction_delete(_extraction);
2083 giraffe_rebinning_destroy(rebinning);
2084
2086
2087 return 4;
2088 }
2089
2090 offsets = _giraffe_compute_slitgeometry(rebinning->spectra, mask,
2091 slit, setup, config);
2092
2093 if (offsets == NULL) {
2095
2096 giraffe_extraction_delete(_extraction);
2097 giraffe_rebinning_destroy(rebinning);
2098
2100
2101 return 5;
2102 }
2103
2104
2105 /*
2106 * Build new slit geometry table
2107 */
2108
2109 cx_assert(cpl_table_get_nrow(offsets) == cpl_table_get_nrow(_fibers));
2110
2112 slit = _giraffe_slitgeometry_table(offsets, rebinning->spectra,
2113 fibers, slitgeometry, config);
2114
2115 if (slit == NULL) {
2116
2117 cpl_table_delete(offsets);
2118
2119 giraffe_extraction_delete(_extraction);
2120 giraffe_rebinning_destroy(rebinning);
2121
2123
2124 return 6;
2125 }
2126
2127 cpl_table_delete(offsets);
2128 offsets = NULL;
2129
2130 rvmin = cpl_table_get_column_min(giraffe_table_get(slit), "RV");
2131 cpl_table_get_column_minpos(giraffe_table_get(slit), "RV", &row);
2132 fps_rvmin = cpl_table_get_int(giraffe_table_get(slit), "FPS",
2133 row, NULL);
2134
2135 rvmax = cpl_table_get_column_max(giraffe_table_get(slit), "RV");
2136 cpl_table_get_column_maxpos(giraffe_table_get(slit), "RV", &row);
2137 fps_rvmax = cpl_table_get_int(giraffe_table_get(slit), "FPS",
2138 row, NULL);
2139
2140 rvmean = cpl_table_get_column_mean(giraffe_table_get(slit), "RV");
2141
2142 cpl_msg_info(fctid, "Iteration %d: Fiber offsets [km/s]: minimum = "
2143 "%.6e (fps %d), maximum = %.6e (fps %d), mean = %.6e",
2144 i + 1, rvmin, fps_rvmin, rvmax, fps_rvmax, rvmean);
2145
2146 giraffe_rebinning_destroy(rebinning);
2147 rebinning = NULL;
2148
2149 }
2150
2151 giraffe_extraction_delete(_extraction);
2153
2154 cx_assert(slit != NULL);
2155
2157 giraffe_table_set(result, giraffe_table_get(slit));
2158
2160
2161 return 0;
2162
2163}
2164
2165
2190cxint
2191giraffe_compute_offsets(GiTable* fibers, const GiRebinning* rebinning,
2192 const GiTable* grating, const GiTable* mask,
2193 const GiSGCalConfig* config)
2194{
2195
2196 cxint status = 0;
2197 cxint nfibers = 0;
2198 cxint fiber = 0;
2199 cxint peak = 0;
2200 cxint fps = 0;
2201 cxint fps0 = 0;
2202 cxint fps1 = 0;
2203
2204 cxdouble dwf0 = 0.;
2205
2206 cpl_table* _fibers = NULL;
2207 cpl_table* peakdata = NULL;
2208
2209 GiGrating* _grating = NULL;
2210
2211 GiSGSetup setup;
2212
2213
2214 if ((rebinning == NULL) || (rebinning->spectra == NULL)) {
2215 return -1;
2216 }
2217
2218 if (fibers == NULL) {
2219 return -2;
2220 }
2221
2222 if (grating == NULL) {
2223 return -3;
2224 }
2225
2226 if (mask == NULL) {
2227 return -4;
2228 }
2229
2230 if (config == NULL) {
2231 return -5;
2232 }
2233
2234
2235 _fibers = giraffe_table_get(fibers);
2236 cx_assert(_fibers != NULL);
2237
2238
2239 /*
2240 * Extract the SIWC fibers from the fiber table. The simultaneous
2241 * calibration spectra are indicated by a -1 as retractor position.
2242 */
2243
2244 cpl_table_unselect_all(_fibers);
2245 cpl_table_or_selected_int(_fibers, "RP", CPL_EQUAL_TO, -1);
2246
2247 _fibers = cpl_table_extract_selected(_fibers);
2248
2249 if (_fibers == NULL) {
2250 return 1;
2251 }
2252
2253
2254 /*
2255 * Create grating setup
2256 */
2257
2258 _grating = giraffe_grating_create(rebinning->spectra, grating);
2259
2260 if (_grating == NULL) {
2261 cpl_table_delete(_fibers);
2262 return 2;
2263 }
2264
2265 /*
2266 * Get setup information from the rebinned spectra frame
2267 */
2268
2269 status = _giraffe_create_setup(&setup, rebinning->spectra);
2270
2271 if (status != 0) {
2272
2273 giraffe_grating_delete(_grating);
2274 cpl_table_delete(_fibers);
2275
2276 return 3;
2277
2278 }
2279
2280
2281 /*
2282 * Compute the wavelength shifts between the reference mask and
2283 * the arc-lamp spectra, from the position of the cross-correlation
2284 * peak.
2285 *
2286 * Note that either a fiber, or a slitgeometry table may be passed to
2287 * _giraffe_compute_offsets(). Actually any table providing the
2288 * columns "INDEX" and "FPS", describing the pixel column of each
2289 * spectrum in the input image and the fiber position within the
2290 * pseudo slit.
2291 */
2292
2293 peakdata = _giraffe_compute_offsets(rebinning->spectra, mask, _fibers,
2294 _grating, &setup, config);
2295
2296 if (peakdata == NULL) {
2297
2298 giraffe_grating_delete(_grating);
2299 cpl_table_delete(_fibers);
2300
2301 return 4;
2302
2303 }
2304
2305
2306 /*
2307 * Compute the offsets of the fibers in the pseudo-slit (i.e. in the
2308 * focal plane.
2309 */
2310
2311 status = _giraffe_compute_fiber_offsets(peakdata, _grating, &setup);
2312
2313 if (status != 0) {
2314 cpl_table_delete(peakdata);
2315 cpl_table_delete(_fibers);
2316
2317 return 5;
2318 }
2319
2320 giraffe_grating_delete(_grating);
2321 cpl_table_delete(_fibers);
2322
2323
2324 /*
2325 * Interpolate the wavelength offsets between the position of 2 adjacent
2326 * simultaneous calibration fibers.
2327 *
2328 * Note: The while loops traversing the fiber table _fiber just check
2329 * whether the two fiber positions are equal or not. In that way
2330 * it is possible to process Argus data, where the order of the
2331 * fibers is reversed, without sorting the fiber and peakdata
2332 * tables, or using dedicated code for Argus observations.
2333 */
2334
2335 _fibers = giraffe_table_get(fibers);
2336
2337 giraffe_error_push();
2338
2339 cpl_table_new_column(_fibers, "WLRES", CPL_TYPE_DOUBLE);
2340 cpl_table_set_column_unit(_fibers, "WLRES", "nm");
2341
2342 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2343 cpl_table_delete(peakdata);
2344 return 6;
2345 }
2346
2347 giraffe_error_pop();
2348
2349
2350 giraffe_error_push();
2351
2352 fps0 = cpl_table_get_int(peakdata, "FPS", 0, NULL);
2353 dwf0 = cpl_table_get_double(peakdata, "DWF", 0, NULL);
2354
2355 nfibers = cpl_table_get_nrow(_fibers);
2356
2357 fps = cpl_table_get_int(_fibers, "FPS", 0, NULL);
2358
2359 while (fps != fps0) {
2360
2361 cpl_table_set_double(_fibers, "WLRES", fiber, dwf0);
2362
2363 ++fiber;
2364 fps = cpl_table_get_int(_fibers, "FPS", fiber, NULL);
2365
2366 }
2367
2368 for (peak = 1; peak < cpl_table_get_nrow(peakdata); ++peak) {
2369
2370 cxdouble dwf1 = cpl_table_get_double(peakdata, "DWF", peak, NULL);
2371 cxdouble slope = 0.;
2372
2373
2374 fps1 = cpl_table_get_int(peakdata, "FPS", peak, NULL);
2375
2376 slope = (dwf1 - dwf0) / ((cxdouble)(fps1 - fps0));
2377
2378 while (fps != fps1) {
2379
2380 cpl_table_set_double(_fibers, "WLRES", fiber,
2381 slope * (fps - fps0) + dwf0);
2382
2383 ++fiber;
2384 fps = cpl_table_get_int(_fibers, "FPS", fiber, NULL);
2385 }
2386
2387 fps0 = fps1;
2388 dwf0 = dwf1;
2389
2390 }
2391
2392 fps1 = cpl_table_get_int(_fibers, "FPS", nfibers - 1, NULL);
2393
2394 while (fps != fps1) {
2395
2396 cpl_table_set_double(_fibers, "WLRES", fiber, dwf0);
2397
2398 ++fiber;
2399 fps = cpl_table_get_int(_fibers, "FPS", fiber, NULL);
2400
2401 }
2402
2403 cpl_table_set_double(_fibers, "WLRES", fiber, dwf0);
2404
2405 cx_assert(fiber == nfibers - 1);
2406
2407 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2408 cpl_table_delete(peakdata);
2409 return 7;
2410 }
2411
2412 cpl_table_delete(peakdata);
2413
2414 giraffe_error_pop();
2415
2416 return 0;
2417
2418}
2419
2420
2435{
2436
2437 const cxchar* s = NULL;
2438
2439 cpl_parameter* p = NULL;
2440
2441 GiSGCalConfig* config = NULL;
2442
2443
2444 if (!list) {
2445 return NULL;
2446 }
2447
2448 config = cx_calloc(1, sizeof *config);
2449
2450 config->cc_wdomain = FALSE;
2451
2452 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.iterations");
2453 config->repeat = cpl_parameter_get_int(p);
2454
2455 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.zmax");
2456 config->zmax = cpl_parameter_get_double(p);
2457
2458 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.cc.step");
2459 config->cc_step = cpl_parameter_get_double(p);
2460
2461 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.cc.domain");
2462 s = cpl_parameter_get_string(p);
2463
2464 if (s) {
2465
2466 cxchar** values = cx_strsplit(s, ",", 3);
2467
2468 if (values == NULL) {
2469
2471 return NULL;
2472
2473 }
2474 else {
2475
2476 cxchar* last;
2477
2478 cxdouble lower = 0.;
2479 cxdouble upper = 0.;
2480
2481
2482 lower = strtod(values[0], &last);
2483
2484 if (*last != '\0') {
2485
2486 cx_strfreev(values);
2488
2489 return NULL;
2490
2491 }
2492
2493 lower = lower >= 0. ? lower : 0.;
2494
2495
2496 if (values[1] != NULL) {
2497
2498 upper = strtod(values[1], &last);
2499
2500 if (*last != '\0') {
2501
2502 cx_strfreev(values);
2504
2505 return NULL;
2506
2507 }
2508
2509 upper = upper > lower ? upper : 0.;
2510
2511 }
2512
2513 config->cc_domain = giraffe_range_create(lower, upper);
2514 cx_assert(config->cc_domain != NULL);
2515
2516 }
2517
2518 cx_strfreev(values);
2519 values = NULL;
2520
2521 }
2522
2523
2524 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.rv.limits");
2525 s = cpl_parameter_get_string(p);
2526
2527 if (s) {
2528
2529 cxchar** values = cx_strsplit(s, ",", 3);
2530
2531 if (values == NULL) {
2532
2534 return NULL;
2535
2536 }
2537 else {
2538
2539 cxchar* last;
2540
2541 cxdouble lower = 0.;
2542 cxdouble upper = 0.;
2543
2544
2545 lower = strtod(values[0], &last);
2546
2547 if (*last != '\0') {
2548
2549 cx_strfreev(values);
2551
2552 return NULL;
2553
2554 }
2555
2556 if (values[1] != NULL) {
2557
2558 upper = strtod(values[1], &last);
2559
2560 if (*last != '\0') {
2561
2562 cx_strfreev(values);
2564
2565 return NULL;
2566
2567 }
2568
2569 if (lower > 0 || upper < lower) {
2570
2571 cx_strfreev(values);
2573
2574 return NULL;
2575
2576 }
2577
2578 }
2579 else {
2580
2581 if (lower > 0.) {
2582
2583 upper = lower;
2584 lower = -upper;
2585
2586 }
2587 else {
2588
2589 upper = -lower;
2590
2591 }
2592
2593 }
2594
2595 cx_assert(lower <= 0);
2596 cx_assert(lower < upper);
2597
2598 config->rv_limits = giraffe_range_create(lower, upper);
2599 cx_assert(config->rv_limits != NULL);
2600
2601 }
2602
2603 cx_strfreev(values);
2604 values = NULL;
2605
2606 }
2607
2608
2609 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.rv.iterations");
2610 config->rv_niter = cpl_parameter_get_int(p);
2611
2612 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.rv.wfactor");
2613 config->rv_wfactor = cpl_parameter_get_double(p);
2614
2615 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.peak.iterations");
2616 config->pf_niter = cpl_parameter_get_int(p);
2617
2618 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.peak.tests");
2619 config->pf_ntest = cpl_parameter_get_int(p);
2620
2621 p = cpl_parameterlist_find(list, "giraffe.sgcalibration.peak.dchisquare");
2622 config->pf_dchisq = cpl_parameter_get_double(p);
2623
2624 return config;
2625
2626}
2627
2628
2643void
2645{
2646
2647 if (config) {
2648 if (config->cc_domain) {
2650 }
2651
2652 if (config->rv_limits) {
2654 }
2655
2656 cx_free(config);
2657 }
2658
2659 return;
2660}
2661
2662
2674void
2675giraffe_sgcalibration_config_add(cpl_parameterlist* list)
2676{
2677
2678 cpl_parameter* p;
2679
2680
2681 if (!list) {
2682 return;
2683 }
2684
2685 p = cpl_parameter_new_value("giraffe.sgcalibration.iterations",
2686 CPL_TYPE_INT,
2687 "Slit geometry calibration maximum number "
2688 "of iterations.",
2689 "giraffe.sgcalibration",
2690 1);
2691 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-cniter");
2692 cpl_parameterlist_append(list, p);
2693
2694
2695 p = cpl_parameter_new_value("giraffe.sgcalibration.zmax",
2696 CPL_TYPE_DOUBLE,
2697 "Maximum allowed pixel value. To be "
2698 "effective it must be larger than 0.",
2699 "giraffe.sgcalibration",
2700 10000.);
2701 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-zmax");
2702 cpl_parameterlist_append(list, p);
2703
2704
2705 p = cpl_parameter_new_value("giraffe.sgcalibration.cc.step",
2706 CPL_TYPE_DOUBLE,
2707 "Cross-correlation step.",
2708 "giraffe.sgcalibration",
2709 -0.005);
2710 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-cstep");
2711 cpl_parameterlist_append(list, p);
2712
2713
2714 p = cpl_parameter_new_value("giraffe.sgcalibration.cc.domain",
2715 CPL_TYPE_STRING,
2716 "Restricts the cross-correlation to the "
2717 "given domain.",
2718 "giraffe.sgcalibration",
2719 "0.,0.");
2720 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-cdomain");
2721 cpl_parameterlist_append(list, p);
2722
2723
2724 p = cpl_parameter_new_value("giraffe.sgcalibration.rv.limits",
2725 CPL_TYPE_STRING,
2726 "Delta RV limits of the cross-correlation "
2727 "window in km/s.",
2728 "giraffe.sgcalibration",
2729 "-200.,200.");
2730 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-rvlimits");
2731 cpl_parameterlist_append(list, p);
2732
2733
2734 p = cpl_parameter_new_value("giraffe.sgcalibration.rv.iterations",
2735 CPL_TYPE_INT,
2736 "Maximum number of iterations used for the "
2737 "RV determination.",
2738 "giraffe.sgcalibration",
2739 3);
2740 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-rvniter");
2741 cpl_parameterlist_append(list, p);
2742
2743
2744 p = cpl_parameter_new_value("giraffe.sgcalibration.rv.wfactor",
2745 CPL_TYPE_DOUBLE,
2746 "Data window width factor. The FWHM times "
2747 "this value determines the window width.",
2748 "giraffe.sgcalibration",
2749 1.5);
2750 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-rvwfactor");
2751 cpl_parameterlist_append(list, p);
2752
2753
2754 p = cpl_parameter_new_value("giraffe.sgcalibration.peak.iterations",
2755 CPL_TYPE_INT,
2756 "Peak model fit maximum number of "
2757 "iterations.",
2758 "giraffe.sgcalibration",
2759 50);
2760
2761 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-pfniter");
2762 cpl_parameterlist_append(list, p);
2763
2764
2765 p = cpl_parameter_new_value("giraffe.sgcalibration.peak.tests",
2766 CPL_TYPE_INT,
2767 "Cross-correlation peak fit maximum number "
2768 "of tests",
2769 "giraffe.sgcalibration",
2770 7);
2771
2772 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-pfntest");
2773 cpl_parameterlist_append(list, p);
2774
2775
2776 p = cpl_parameter_new_value("giraffe.sgcalibration.peak.dchisquare",
2777 CPL_TYPE_DOUBLE,
2778 "Cross-correlation peak fit minimum "
2779 "chi-square difference.",
2780 "giraffe.sgcalibration",
2781 0.0001);
2782
2783 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "scal-pfdchisq");
2784 cpl_parameterlist_append(list, p);
2785
2786 return;
2787
2788}
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
cxint giraffe_matrix_sort(cpl_matrix *mA)
Sort in place the matrix elements in ascending order.
Definition: gimatrix.c:380
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_rebin_spectra(GiRebinning *rebinning, const GiExtraction *extraction, const GiTable *fibers, const GiLocalization *localization, const GiTable *grating, const GiTable *slitgeo, const GiTable *solution, const GiRebinConfig *config)
Rebin an Extracted Spectra Frame and associated Errors Frame.
Definition: girebinning.c:4051
GiRebinning * giraffe_rebinning_new(void)
Create an empty rebinning results container.
Definition: girebinning.c:4693
void giraffe_rebinning_destroy(GiRebinning *rebinning)
Destroys a rebinning results container and its contents.
Definition: girebinning.c:4787
GiSGCalConfig * giraffe_sgcalibration_config_create(cpl_parameterlist *list)
Creates a setup structure for the slit geometry calibration.
cxint giraffe_compute_offsets(GiTable *fibers, const GiRebinning *rebinning, const GiTable *grating, const GiTable *mask, const GiSGCalConfig *config)
Compute wavelength offsets for a set of rebinned input spectrum.
void giraffe_sgcalibration_config_destroy(GiSGCalConfig *config)
Destroys a sgcalibration field setup structure.
void giraffe_sgcalibration_config_add(cpl_parameterlist *list)
Adds parameters for the sgcalibration correction computation.
cxint giraffe_calibrate_slit(GiTable *result, const GiExtraction *extraction, const GiLocalization *localization, const GiTable *fibers, const GiTable *wlsolution, const GiTable *slitgeometry, const GiTable *grating, const GiTable *mask, const GiSGCalConfig *config)
Compute a slit geometry corresponding to the given rebinned spectrum.
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
GiTable * giraffe_table_duplicate(const GiTable *src)
Duplicate a Giraffe table.
Definition: gitable.c:176
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
cxint giraffe_propertylist_copy(cpl_propertylist *self, const cxchar *name, const cpl_propertylist *other, const cxchar *othername)
Copy a property from one list to another.
Definition: giutils.c:1106
Structure to handle Grating Information.
Definition: gigrating.h:44
cxdouble wlenmax
Definition: gigrating.h:52
cxdouble wlen0
Definition: gigrating.h:50
cxint resol
Definition: gigrating.h:54
cxdouble wlenmin
Definition: gigrating.h:51
Slit geometry calibration configuration data structure.
GiRange * cc_domain
cxdouble pf_dchisq
GiRange * rv_limits
cxdouble rv_wfactor
cxdouble cc_step

This file is part of the GIRAFFE Pipeline Reference Manual 2.16.14.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Wed Nov 20 2024 21:40:14 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004