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 
57 struct GiMeasurement {
58  cxdouble value;
59  cxdouble sigma;
60 };
61 
62 typedef struct GiMeasurement GiMeasurement;
63 
64 
65 struct 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 
80 typedef struct GiSGSetup GiSGSetup;
81 
82 
83 struct 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 
98 typedef struct GiCPFitParams GiCPFitParams;
99 
100 
101 struct GiCPeakFit {
102  GiMeasurement amplitude;
103  GiMeasurement background;
104  GiMeasurement center;
105  GiMeasurement width;
106 
107  cxint status;
108 };
109 
110 typedef struct GiCPeakFit GiCPeakFit;
111 
112 
113 struct 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 
128 typedef struct GiSGMask GiSGMask;
129 
130 
131 inline 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 
153 inline 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 
178 inline 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 
276 inline 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 
287 inline 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 
298 inline 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 
315 inline 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 
334 inline 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 
345 inline 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 
391 inline 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 
423 inline 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 
482 inline 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);
628  giraffe_matrix_sort(_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 
654 inline 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 
739 inline 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 
1133 inline 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 
1237 inline 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 
1736 inline 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 
1806 inline 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 
1929  giraffe_table_delete(slit);
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 
1952 cxint
1953 giraffe_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) {
2080  giraffe_table_delete(slit);
2081 
2082  giraffe_extraction_delete(_extraction);
2083  giraffe_rebinning_destroy(rebinning);
2084 
2085  giraffe_grating_delete(setup);
2086 
2087  return 4;
2088  }
2089 
2090  offsets = _giraffe_compute_slitgeometry(rebinning->spectra, mask,
2091  slit, setup, config);
2092 
2093  if (offsets == NULL) {
2094  giraffe_table_delete(slit);
2095 
2096  giraffe_extraction_delete(_extraction);
2097  giraffe_rebinning_destroy(rebinning);
2098 
2099  giraffe_grating_delete(setup);
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 
2111  giraffe_table_delete(slit);
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 
2122  giraffe_grating_delete(setup);
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);
2152  giraffe_grating_delete(setup);
2153 
2154  cx_assert(slit != NULL);
2155 
2157  giraffe_table_set(result, giraffe_table_get(slit));
2158 
2159  giraffe_table_delete(slit);
2160 
2161  return 0;
2162 
2163 }
2164 
2165 
2190 cxint
2191 giraffe_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 
2434 giraffe_sgcalibration_config_create(cpl_parameterlist* list)
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 
2643 void
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 
2674 void
2675 giraffe_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.
void giraffe_grating_delete(GiGrating *self)
Destroys an GiGrating object.
Definition: gigrating.c:421
GiGrating * giraffe_grating_create(const GiImage *spectra, const GiTable *grating)
Create a GiGrating from a reference image.
Definition: gigrating.c:218
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
Definition: giimage.c:282
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
Definition: giimage.c:218
cxint giraffe_matrix_sort(cpl_matrix *mA)
Sort in place the matrix elements in ascending order.
Definition: gimatrix.c:380
GiRange * giraffe_range_create(cxdouble min, cxdouble max)
Creates a new range from the given minimum and maximum values.
Definition: girange.c:83
void giraffe_range_delete(GiRange *self)
Destroys a range object.
Definition: girange.c:118
cxdouble giraffe_range_get_min(const GiRange *const self)
Get the minimum of a range.
Definition: girange.c:167
cxdouble giraffe_range_get_max(const GiRange *const self)
Get the maximum of a range.
Definition: girange.c:213
cxint giraffe_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
void giraffe_rebinning_destroy(GiRebinning *rebinning)
Destroys a rebinning results container and its contents.
Definition: girebinning.c:4787
GiRebinning * giraffe_rebinning_new(void)
Create an empty rebinning results container.
Definition: girebinning.c:4693
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
cpl_propertylist * giraffe_table_get_properties(const GiTable *self)
Gets the table properties.
Definition: gitable.c:489
GiTable * giraffe_table_new(void)
Creates a new, empty Giraffe table.
Definition: gitable.c:85
void giraffe_table_delete(GiTable *self)
Destroys a Giraffe table.
Definition: gitable.c:154
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
Definition: gitable.c:433
GiTable * giraffe_table_duplicate(const GiTable *src)
Duplicate a Giraffe table.
Definition: gitable.c:176
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:1104
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.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Dec 15 2022 21:18:51 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2004