GIRAFFE Pipeline Reference Manual

gipsf.c
1 /* $Id$
2  *
3  * This file is part of the GIRAFFE Pipeline
4  * Copyright (C) 2002-2006 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * $Author$
23  * $Date$
24  * $Revision$
25  * $Name$
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 
33 #include <string.h>
34 #include <math.h>
35 
36 #include <cxmacros.h>
37 #include <cxmessages.h>
38 #include <cxmemory.h>
39 #include <cxstrutils.h>
40 
41 #include <cpl_error.h>
42 #include <cpl_image.h>
43 #include <cpl_table.h>
44 #include <cpl_parameterlist.h>
45 #include <cpl_msg.h>
46 
47 #include "gialias.h"
48 #include "gierror.h"
49 #include "gimessages.h"
50 #include "gimatrix.h"
51 #include "gichebyshev.h"
52 #include "gimodel.h"
53 #include "gipsfdata.h"
54 #include "gilocalization.h"
55 #include "gimask.h"
56 #include "gimath.h"
57 #include "giclip.h"
58 #include "gifiberutils.h"
59 #include "gipsf.h"
60 
61 
70 enum GiProfileId {
71  PROFILE_PSFEXP = 1 << 1,
72  PROFILE_PSFEXP2 = 1 << 2,
73  PROFILE_GAUSSIAN = 1 << 3
74 };
75 
76 typedef enum GiProfileId GiProfileId;
77 
78 
79 struct GiPsfParams {
80  cxint bsize; /* Size of X bins for the fit */
81  cxint mwidth; /* Maximum spectrum half-width for the fit */
82  cxdouble limit; /* Profile computation limit. Fraction of amplitude */
83  cxbool normalize; /* Use normalized pixel values */
84 };
85 
86 typedef struct GiPsfParams GiPsfParams;
87 
88 
89 struct GiPsfBin {
90  cxdouble zmin;
91  cxdouble zmax;
92  cxdouble xcenter;
93  cxdouble ycenter;
94  cxdouble ywidth;
95 };
96 
97 typedef struct GiPsfBin GiPsfBin;
98 
99 struct GiPsfParameterFit {
100  cpl_image* fit;
101  cpl_matrix* coeffs;
102 };
103 
104 typedef struct GiPsfParameterFit GiPsfParameterFit;
105 
106 
107 /*
108  * @brief
109  * Perform a 1d fit of a PSF profile parameter.
110  *
111  * @param result Container for the fit results.
112  * @param psfdata PSF profile data.
113  * @param name PSF profile parameter name to fit.
114  * @param fibers Table of fibers used.
115  * @param order Order of the Chebyshev polynomial to fit.
116  * @param setup Kappa-sigma clipping algorithm setup information.
117  *
118  * @return
119  * The function returns 0 on success, or a non-zero value otherwise.
120  *
121  * The function fits a 1d Chebyshev polynomial of the order @em order to the
122  * PSF profile parameter data given by @em psfdata and @em name. The object
123  * @em psfdata contains the data for each parameter (peak position, width,
124  * etc.) of the used profile model. The parameter for which the fit is
125  * performed is selected by the name @em name. The list of fibers for which
126  * profile data are available in @em psfdata is given by @em fibers.
127  *
128  * When fitting the parameter data, data points deviating too much from
129  * the fit are rejected using a kappa-sigma rejection algorithm. The
130  * kappa-sigma rejection is configured by @em setup.
131  *
132  * The results, i.e. the fit coefficients and the fit of the parameter data
133  * are written to the results container @em result. The matrix and image
134  * to which the results are written must have been created by the caller
135  * with the appropriate size.
136  */
137 
138 inline static cxint
139 _giraffe_psf_fit_profile1d(GiPsfParameterFit* result,
140  const GiPsfData* psfdata, const cxchar* name,
141  const cpl_table* fibers, cxint order,
142  const GiClipParams* setup)
143 {
144 
145  cxint i = 0;
146  cxint ns = 0;
147  cxint nx = 0;
148  cxint nb = 0;
149 
150  cpl_matrix* x = NULL;
151  cpl_matrix* base = NULL;
152 
153  const cpl_image* parameter = NULL;
154 
155 
156  cx_assert(result != NULL);
157  cx_assert(result->coeffs != NULL);
158  cx_assert(result->fit != NULL);
159  cx_assert(psfdata != NULL && name != NULL);
160  cx_assert(fibers != NULL);
161  cx_assert(setup != NULL);
162 
163  nb = giraffe_psfdata_bins(psfdata);
164  ns = giraffe_psfdata_fibers(psfdata);
165  nx = giraffe_psfdata_ysize(psfdata);
166 
167  if (ns != cpl_table_get_nrow(fibers)) {
168  return -1;
169  }
170 
171  if ((cpl_image_get_size_x(result->fit) != ns) ||
172  (cpl_image_get_size_y(result->fit) != nx)) {
173  return -1;
174  }
175 
176  if ((cpl_matrix_get_nrow(result->coeffs) != order + 1) ||
177  (cpl_matrix_get_ncol(result->coeffs) != ns)) {
178  return -1;
179  }
180 
181  for (i = 0; i < ns; i++) {
182 
183  register cxint j = 0;
184  register cxint valid_bins = 0;
185 
186  for (j = 0; j < nb; j++) {
187  if (giraffe_psfdata_get_bin(psfdata, i, j) >= 0.) {
188  ++valid_bins;
189  }
190  }
191 
192  if (valid_bins < order + 1) {
193  return 1;
194  }
195 
196  }
197 
198 
199  /*
200  * Compute the Chebyshev base for all points
201  */
202 
203  x = cpl_matrix_new(nx, 1);
204 
205  for (i = 0; i < nx; i++) {
206  cpl_matrix_set(x, i, 0, i);
207  }
208 
209  base = giraffe_chebyshev_base1d(0., (cxdouble)nx, order + 1, x);
210 
211  if (base == NULL) {
212  cpl_matrix_delete(x);
213  x = NULL;
214 
215  return 2;
216  }
217 
218  cpl_matrix_delete(x);
219  x = NULL;
220 
221 
222  /*
223  * Fit PSF profile parameter data
224  */
225 
226  parameter = giraffe_psfdata_get_data(psfdata, name);
227 
228  if (parameter == NULL) {
229  return 3;
230  }
231 
232  for (i = 0; i < ns; i++) {
233 
234  cxint j = 0;
235  cxint k = 0;
236  cxint naccepted = 0;
237  cxint ntotal = 0;
238  cxint iteration = 0;
239 
240  const cxdouble* _parameter =
241  cpl_image_get_data_double_const(parameter);
242 
243  cxdouble ratio = 1.;
244  cxdouble* _fit = cpl_image_get_data_double(result->fit);
245 
246  cpl_matrix* y = NULL;
247  cpl_matrix* ydiff = NULL;
248  cpl_matrix* coeffs = NULL;
249  cpl_matrix* fit = NULL;
250 
251 
252  x = cpl_matrix_new(nb, 1);
253  y = cpl_matrix_new(1, nb);
254  ydiff = cpl_matrix_new(1, nb);
255 
256  for (j = 0; j < nb; j++) {
257 
258  cxdouble bin = giraffe_psfdata_get_bin(psfdata, i, j);
259 
260 
261  if (bin >= 0.) {
262  cpl_matrix_set(x, k, 0, bin);
263  cpl_matrix_set(y, 0, k, _parameter[j * ns + i]);
264  ++k;
265  }
266 
267  }
268 
269 
270  /*
271  * Shrink matrices to their actual size.
272  */
273 
274  cpl_matrix_set_size(x, k, 1);
275  cpl_matrix_set_size(y, 1, k);
276  cpl_matrix_set_size(ydiff, 1, k);
277 
278  ntotal = cpl_matrix_get_nrow(x);
279  naccepted = ntotal;
280 
281  while ((naccepted > 0) && (iteration < setup->iterations) &&
282  (ratio > setup->fraction)) {
283 
284  cxdouble sigma = 0.;
285 
286  cpl_matrix* _base = NULL;
287 
288 
289  if (coeffs != NULL) {
290  cpl_matrix_delete(coeffs);
291  coeffs = NULL;
292  }
293 
294  if (fit != NULL) {
295  cpl_matrix_delete(fit);
296  fit = NULL;
297  }
298 
299  _base = giraffe_chebyshev_base1d(0., (cxdouble)nx, order + 1, x);
300  coeffs = giraffe_matrix_leastsq(_base, y);
301 
302  if (coeffs == NULL) {
303  cpl_matrix_delete(_base);
304  _base = NULL;
305  }
306 
307  cpl_matrix_delete(_base);
308  _base = NULL;
309 
310  fit = cpl_matrix_product_create(coeffs, base);
311 
312  for (j = 0; j < cpl_matrix_get_nrow(x); j++) {
313 
314  cxint xlower = (cxint) ceil(cpl_matrix_get(x, j, 0));
315  cxint xupper = (cxint) floor(cpl_matrix_get(x, j, 0));
316 
317  cxdouble ylower = cpl_matrix_get(fit, 0, xlower);
318  cxdouble yupper = cpl_matrix_get(fit, 0, xupper);
319  cxdouble yfit = (yupper + ylower) / 2.;
320 
321  cpl_matrix_set(ydiff, 0, j, cpl_matrix_get(y, 0, j) - yfit);
322 
323  }
324 
325  sigma = setup->level * giraffe_matrix_sigma_mean(ydiff, 0.);
326 
327 
328  /*
329  * Reject data points deviating too much.
330  */
331 
332  k = 0;
333  for (j = 0; j < cpl_matrix_get_ncol(ydiff); j++) {
334  if (fabs(cpl_matrix_get(ydiff, 0, j)) <= sigma) {
335  cpl_matrix_set(x, k, 0, cpl_matrix_get(x, j, 0));
336  cpl_matrix_set(y, 0, k, cpl_matrix_get(y, 0, j));
337  ++k;
338  }
339  }
340 
341  cpl_matrix_set_size(x, k, 1);
342  cpl_matrix_set_size(y, 1, k);
343  cpl_matrix_set_size(ydiff, 1, k);
344 
345 
346  /*
347  * Stop if no additional data points have been rejected
348  * in the last iteration. Otherwise update the number of
349  * accepted points, the ratio, and reset the fit coefficients
350  * and the parameter fit.
351  */
352 
353  if (k == naccepted) {
354  break;
355  }
356  else {
357  naccepted = k;
358  ratio = (cxdouble)naccepted / (cxdouble) ntotal;
359  ++iteration;
360  }
361 
362  }
363 
364 
365  /*
366  * Save the fit coefficients and the parameter fit to the
367  * results object.
368  */
369 
370  cx_assert(cpl_matrix_get_ncol(coeffs) == order + 1);
371 
372  for (j = 0; j < cpl_matrix_get_ncol(coeffs); j++) {
373  cpl_matrix_set(result->coeffs, j, i,
374  cpl_matrix_get(coeffs, 0, j));
375  }
376 
377  for (j = 0; j < nx; j++) {
378  _fit[j * ns + i] = cpl_matrix_get(fit, 0, j);
379  }
380 
381 
382  /*
383  * Cleanup temporary buffers
384  */
385 
386  cpl_matrix_delete(x);
387  x = NULL;
388 
389  cpl_matrix_delete(y);
390  y = NULL;
391 
392  cpl_matrix_delete(ydiff);
393  ydiff = NULL;
394 
395  cpl_matrix_delete(coeffs);
396  coeffs = NULL;
397 
398  cpl_matrix_delete(fit);
399  fit = NULL;
400 
401  }
402 
403  cpl_matrix_delete(base);
404  base = NULL;
405 
406  return 0;
407 
408 }
409 
410 
411 /*
412  * @brief
413  * Perform a 2d fit of a PSF profile parameter.
414  *
415  * @param result Container for the fit results.
416  * @param fibers Table of fibers used.
417  * @param psfdata Binned parameter data to fit.
418  * @param xbin Abscissa values of the bins.
419  * @param ybin Ordinate values of the bins.
420  * @param xorder X order of the Chebyshev polynomial to fit.
421  * @param yorder Y order of the Chebyshev polynomial to fit.
422  * @param yfit Ordinate values for which the fit is computed.
423  * @param ystart Minimum value of the ordinate values range.
424  * @param yend Maximum value of the ordinate values range.
425  * @param setup Kappa-sigma clipping algorithm setup information.
426  *
427  * @return
428  * The function returns 0 on success, or a non-zero value otherwise.
429  *
430  * The function fits a 2d Chebyshev polynomial of the order @em xorder and
431  * @em yorder, along x and y respectively, to the PSF profile parameter
432  * data given by @em psfdata. The object @em psfdata contains the data for
433  * a single PSF profile parameter (peak position, width, etc.) of the used
434  * profile model. The list of fibers for which profile data are available
435  * in @em psfdata is given by @em fibers. The abscissa and ordinate values
436  * for which the PSF parameter data have been measured.
437  *
438  * When fitting the parameter data, data points deviating too much from
439  * the fit are rejected using a kappa-sigma rejection algorithm. The
440  * kappa-sigma rejection is configured by @em setup.
441  *
442  * The results, i.e. the fit coefficients and the fit of the parameter data
443  * are written to the results container @em result. The matrix and image
444  * to which the results are written must have been created by the caller
445  * with the appropriate sizes.
446  *
447  * The fit is computed for the positions given by @em yfit, @em ystart and
448  * @em yend., where the latter define the ordinate domain for which @em yfit
449  * is given. The object @em yfit itself gives the ordinate values, and, by its
450  * bin structure (row index), the abscissa values for whichthe fit will
451  * be computed.
452  */
453 
454 inline static cxint
455 _giraffe_psf_fit_profile2d(GiPsfParameterFit* result, const cpl_table* fibers,
456  const cpl_image* psfdata, const cpl_image* xbin,
457  const cpl_image* ybin, cxint xorder, cxint yorder,
458  const cpl_image* yfit, cxint ystart, cxint yend,
459  const GiClipParams* setup)
460 {
461 
462 
463  cxint i = 0;
464  cxint k = 0;
465  cxint ns = 0;
466  cxint nb = 0;
467  cxint nx = 0;
468  cxint iteration = 0;
469  cxint ntotal = 0;
470  cxint naccepted = 0;
471  cxint nspectra = 0;
472  cxint status = 0;
473  cxint ncx = xorder + 1;
474  cxint ncy = yorder + 1;
475 
476  cxdouble ratio = 1.;
477 
478  cpl_matrix* x = NULL;
479  cpl_matrix* y = NULL;
480  cpl_matrix* z = NULL;
481  cpl_matrix* zdiff = NULL;
482  cpl_matrix* nbins = NULL;
483 
484  GiChebyshev2D* fit = NULL;
485 
486 
487  cx_assert(result != NULL);
488  cx_assert(result->coeffs != NULL);
489  cx_assert(result->fit != NULL);
490  cx_assert(fibers != NULL);
491  cx_assert(psfdata != NULL);
492  cx_assert(xbin != NULL && ybin != NULL);
493  cx_assert(yfit != NULL);
494  cx_assert(setup != NULL);
495 
496  nb = cpl_image_get_size_y(xbin);
497  ns = cpl_image_get_size_x(xbin);
498  nx = cpl_image_get_size_y(result->fit);
499 
500  if (ns != cpl_table_get_nrow(fibers)) {
501  return -1;
502  }
503 
504  if ((cpl_image_get_size_x(result->fit) != ns) ||
505  (cpl_image_get_size_y(result->fit) != nx)) {
506  return -1;
507  }
508 
509  if ((cpl_matrix_get_nrow(result->coeffs) != ncx) ||
510  (cpl_matrix_get_ncol(result->coeffs) != ncy)) {
511  return -1;
512  }
513 
514  for (i = 0; i < ns; i++) {
515 
516  register cxint j = 0;
517  register cxint valid_bins = 0;
518 
519  const cxdouble* _xbin = cpl_image_get_data_double_const(xbin);
520 
521  for (j = 0; j < nb; j++) {
522  if (_xbin[j * ns + i] >= 0.) {
523  ++valid_bins;
524  }
525  }
526 
527  if (valid_bins < ncx * ncy) {
528  return 1;
529  }
530 
531  }
532 
533 
534  /*
535  * Fill working buffers
536  */
537 
538  x = cpl_matrix_new(nb * ns, 1);
539  y = cpl_matrix_new(nb * ns, 1);
540  z = cpl_matrix_new(1, nb * ns);
541  zdiff = cpl_matrix_new(1, nb * ns);
542  nbins = cpl_matrix_new(nb * ns, 1);
543 
544  for (i = 0; i < ns; i++) {
545 
546  register cxint j = 0;
547 
548  const cxdouble* _xbin = cpl_image_get_data_double_const(xbin);
549  const cxdouble* _ybin = cpl_image_get_data_double_const(ybin);
550  const cxdouble* _zbin = cpl_image_get_data_double_const(psfdata);
551 
552 
553  for ( j = 0; j < nb; j++) {
554 
555  register cxint l = j * ns + i;
556 
557 
558  if (_xbin[l] >= 0.) {
559  cpl_matrix_set(nbins, k, 0, nspectra);
560  cpl_matrix_set(x, k, 0, _xbin[l]);
561  cpl_matrix_set(y, k, 0, _ybin[l]);
562  cpl_matrix_set(z, 0, k, _zbin[l]);
563  ++k;
564  }
565 
566  }
567 
568  ++nspectra;
569 
570  }
571 
572 
573  /*
574  * Shrink working buffers to their actual size
575  */
576 
577  cpl_matrix_set_size(x, k, 1);
578  cpl_matrix_set_size(y, k, 1);
579  cpl_matrix_set_size(z, 1, k);
580  cpl_matrix_set_size(zdiff, 1, k);
581  cpl_matrix_set_size(nbins, k, 1);
582 
583  ntotal = cpl_matrix_get_nrow(x);
584  naccepted = ntotal;
585 
586  while ((naccepted > 0) && (iteration < setup->iterations) &&
587  (ratio > setup->fraction))
588  {
589 
590  cxdouble sigma = 0.;
591 
592  cpl_matrix* base = NULL;
593  cpl_matrix* coeffs = NULL;
594  cpl_matrix* _coeffs = NULL;
595 
596  register cxdouble* _pfit = cpl_image_get_data_double(result->fit);
597 
598 
599  base = giraffe_chebyshev_base2d(0., ystart, nx, yend, ncx, ncy, x, y);
600 
601  if (base == NULL) {
602  cpl_matrix_delete(nbins);
603  nbins = NULL;
604 
605  cpl_matrix_delete(zdiff);
606  zdiff = NULL;
607 
608  cpl_matrix_delete(z);
609  z = NULL;
610 
611  cpl_matrix_delete(y);
612  y = NULL;
613 
614  cpl_matrix_delete(x);
615  x = NULL;
616 
617  return 1;
618  }
619 
620  _coeffs = giraffe_matrix_leastsq(base, z);
621 
622  if (_coeffs == NULL) {
623  cpl_matrix_delete(base);
624  base = NULL;
625 
626  cpl_matrix_delete(nbins);
627  nbins = NULL;
628 
629  cpl_matrix_delete(zdiff);
630  zdiff = NULL;
631 
632  cpl_matrix_delete(z);
633  z = NULL;
634 
635  cpl_matrix_delete(y);
636  y = NULL;
637 
638  cpl_matrix_delete(x);
639  x = NULL;
640 
641  return 1;
642  }
643 
644  cpl_matrix_delete(base);
645  base = NULL;
646 
647 
648  /*
649  * Compute parameter fit and reject data points deviating too
650  * much from the fit
651  */
652 
653  coeffs = cpl_matrix_wrap(xorder + 1, yorder + 1,
654  cpl_matrix_get_data(_coeffs));
655 
656  if (fit != NULL) {
657  giraffe_chebyshev2d_delete(fit);
658  fit = NULL;
659  }
660 
661  fit = giraffe_chebyshev2d_new(xorder, yorder);
662  status = giraffe_chebyshev2d_set(fit, 0., nx, ystart, yend, coeffs);
663 
664  if (status != 0) {
665  giraffe_chebyshev2d_delete(fit);
666  fit = NULL;
667 
668  cpl_matrix_unwrap(coeffs);
669  coeffs = NULL;
670 
671  cpl_matrix_delete(_coeffs);
672  _coeffs = NULL;
673 
674  cpl_matrix_delete(nbins);
675  nbins = NULL;
676 
677  cpl_matrix_delete(zdiff);
678  zdiff = NULL;
679 
680  cpl_matrix_delete(z);
681  z = NULL;
682 
683  cpl_matrix_delete(y);
684  y = NULL;
685 
686  cpl_matrix_delete(x);
687  x = NULL;
688 
689  return 1;
690  }
691 
692  cpl_matrix_unwrap(coeffs);
693  coeffs = NULL;
694 
695  cpl_matrix_delete(_coeffs);
696  _coeffs = NULL;
697 
698 
699  /* FIXME: Check whether performance can be improved if the fit
700  * is only computed for the bins instead of the full
701  * image y-axis pixels. Note that this needs an additional
702  * buffer and a computation of the last fit on the full
703  * grid.
704  */
705 
706  for (i = 0; i < ns; i++) {
707 
708  register cxint j = 0;
709 
710  register const cxdouble* _yfit =
711  cpl_image_get_data_double_const(yfit);
712 
713  for (j = 0; j < nx; j++) {
714 
715  register cxint l = j * ns + i;
716 
717  _pfit[l] = giraffe_chebyshev2d_eval(fit, j, _yfit[l]);
718 
719  }
720 
721  }
722 
723  for (i = 0; i < cpl_matrix_get_nrow(x); i++) {
724 
725  cxint n = cpl_matrix_get(nbins, i, 0);
726  cxint lower = (cxint) ceil(cpl_matrix_get(x, i, 0)) * ns + n;
727  cxint upper = (cxint) floor(cpl_matrix_get(x, i, 0)) * ns + n;
728 
729  cxdouble zfit = (_pfit[lower] + _pfit[upper]) / 2.;
730 
731  cpl_matrix_set(zdiff, 0, i, cpl_matrix_get(z, 0, i) - zfit);
732 
733  }
734 
735  sigma = setup->level * giraffe_matrix_sigma_mean(zdiff, 0.);
736 
737  k = 0;
738  for (i = 0; i < cpl_matrix_get_ncol(zdiff); i++) {
739  if (fabs(cpl_matrix_get(zdiff, 0, i)) <= sigma) {
740  cpl_matrix_set(x, k, 0, cpl_matrix_get(x, i, 0));
741  cpl_matrix_set(y, k, 0, cpl_matrix_get(y, i, 0));
742  cpl_matrix_set(z, 0, k, cpl_matrix_get(z, 0, i));
743  cpl_matrix_set(nbins, k, 0, cpl_matrix_get(nbins, i, 0));
744  ++k;
745  }
746  }
747 
748  cpl_matrix_set_size(x, k, 1);
749  cpl_matrix_set_size(y, k, 1);
750  cpl_matrix_set_size(z, 1, k);
751  cpl_matrix_set_size(zdiff, 1, k);
752  cpl_matrix_set_size(nbins, k, 1);
753 
754 
755  /*
756  * Stop if no additional data points have been rejected
757  * in the last iteration. Otherwise update the number of
758  * accepted points, the ratio, and reset the fit coefficients
759  * and the parameter fit.
760  */
761 
762  if (k == naccepted) {
763  break;
764  }
765  else {
766  naccepted = k;
767  ratio = (cxdouble)naccepted / (cxdouble) ntotal;
768  ++iteration;
769  }
770 
771  }
772 
773 
774  /*
775  * Copy the fit coefficients to the results container.
776  */
777 
778  for (i = 0; i < cpl_matrix_get_nrow(result->coeffs); i++) {
779 
780  register cxint j = 0;
781 
782  const cpl_matrix* c = giraffe_chebyshev2d_coeffs(fit);
783 
784 
785  for (j = 0; j < cpl_matrix_get_ncol(result->coeffs); j++) {
786  cpl_matrix_set(result->coeffs, i, j, cpl_matrix_get(c, i, j));
787  }
788 
789  }
790 
791 
792  /*
793  * Cleanup temporary buffers
794  */
795 
796  giraffe_chebyshev2d_delete(fit);
797  fit = NULL;
798 
799  cpl_matrix_delete(nbins);
800  nbins = NULL;
801 
802  cpl_matrix_delete(zdiff);
803  zdiff = NULL;
804 
805  cpl_matrix_delete(z);
806  z = NULL;
807 
808  cpl_matrix_delete(y);
809  y = NULL;
810 
811  cpl_matrix_delete(x);
812  x = NULL;
813 
814  return 0;
815 }
816 
817 
818 /*
819  * @brief
820  * Fit a PSF profile model to each fiber spectrum.
821  *
822  * @param result Fitted PSF profile model parameters.
823  * @param zraw Raw image of the fiber spectra.
824  * @param zvar Raw image of flux errors.
825  * @param locy Fiber centroid position.
826  * @param locw Fiber width.
827  * @param fibers List of fibers to process.
828  * @param bpm Optional bad pixel map.
829  * @param config PSF profile fit setup parameters.
830  *
831  * @return
832  * The function returns @c 0 on success, or a non-zero value otherwise.
833  *
834  * The function fits a profile model given by @em psfmodel to each bin along
835  * the dispersion direction (x-axis) for all fibers listed in @em fibers.
836  * The bin size is given by the setup parameters @em config. It also
837  * specifies the maximum allowed half width of a fiber spectrum.
838  *
839  * @note
840  * Currently only the line models "psfexp", "psfexp2" and "gaussian"
841  * are supported.
842  */
843 
844 inline static cxint
845 _giraffe_psf_compute_profile(GiPsfData* result, cpl_image* zraw,
846  cpl_image* zvar, cpl_image* locy,
847  cpl_image* locw, cpl_table* fibers,
848  cpl_image* bpm, GiModel* profile,
849  GiPsfParams* config)
850 {
851 
852  const cxchar* model = NULL;
853  const cxchar* ridx = NULL;
854 
855  const cxdouble cutoff = log(config->limit);
856 
857  cxint nx = 0;
858  cxint ny = 0;
859  cxint ns = 0;
860  cxint fiber = 0;
861  cxint nbins = 0;
862  cxint nspectra = 0;
863 
864  cxsize n = 0;
865 
866  cxdouble exponent; /* PSF profile exponent initial guess */
867 
868  cpl_matrix* mx = NULL;
869  cpl_matrix* my = NULL;
870  cpl_matrix* ms = NULL;
871 
872  cpl_image* zx = NULL;
873  cpl_image* zv = NULL;
874 
875  GiProfileId psfmodel = 0;
876 
877 
878  cx_assert(result != NULL);
879  cx_assert((zraw != NULL) && (zvar != NULL));
880  cx_assert((locy != NULL) && (locw != NULL));
881  cx_assert(fibers != NULL);
882  cx_assert(profile != NULL);
883  cx_assert(config != NULL);
884 
885  cx_assert(cpl_image_get_size_x(zraw) == cpl_image_get_size_x(zvar));
886  cx_assert(cpl_image_get_size_y(zraw) == cpl_image_get_size_y(zvar));
887 
888  cx_assert(cpl_image_get_size_x(locy) == cpl_image_get_size_x(locw));
889  cx_assert(cpl_image_get_size_y(locy) == cpl_image_get_size_y(locw));
890 
891 
892  nx = cpl_image_get_size_y(zraw);
893  ny = cpl_image_get_size_x(zraw);
894  ns = cpl_table_get_nrow(fibers);
895 
896  nbins = (cxint) giraffe_psfdata_bins(result);
897 
898  if (ns != cpl_image_get_size_x(locy)) {
899  return -1;
900  }
901 
902  if ((bpm != NULL) && (cpl_image_get_type(bpm) != CPL_TYPE_INT)) {
903  return -2;
904  }
905 
906  if (giraffe_psfdata_fibers(result) != (cxsize) ns) {
907  return -3;
908  }
909 
910  if ((giraffe_psfdata_xsize(result) != (cxsize) ny) ||
911  (giraffe_psfdata_ysize(result) != (cxsize) nx)) {
912  return -3;
913  }
914 
915 
916  /*
917  * Check the model type. Only the line models "psfexp", "psfexp2" and
918  * "gaussian" are supported.
919  */
920 
921  model = giraffe_model_get_name(profile);
922 
923  if (strcmp(model, "psfexp") == 0) {
924  psfmodel = PROFILE_PSFEXP;
925  }
926  else if (strcmp(model, "psfexp2") == 0) {
927  psfmodel = PROFILE_PSFEXP2;
928  }
929  else if (strcmp(model, "gaussian") == 0) {
930  psfmodel = PROFILE_GAUSSIAN;
931  }
932  else {
933  return -4;
934  }
935 
936 
937  if (config->normalize != FALSE) {
938 
939  cxint x = 0;
940 
941  cxdouble zmax = 0.;
942  cxdouble* zsum = cx_calloc(nx, sizeof(cxdouble));
943 
944 
945  /*
946  * Find maximum pixel value, taking bad pixels into account if the
947  * bad pixel map is present.
948  */
949 
950  if (bpm == NULL) {
951 
952  for (x = 0; x < nx; x++) {
953 
954  register cxint y = 0;
955 
956  register const cxdouble* _zraw =
957  cpl_image_get_data_double(zraw);
958 
959 
960  for (y = 0; y < ny; y++) {
961  zsum[x] += _zraw[x * ny + y];
962  }
963 
964  if (zsum[x] > zmax) {
965  zmax = zsum[x];
966  }
967  }
968 
969  }
970  else {
971 
972  for (x = 0; x < nx; x++) {
973 
974  register cxint y = 0;
975  register const cxint* _bpm = cpl_image_get_data_int(bpm);
976 
977  register const cxdouble* _zraw =
978  cpl_image_get_data_double(zraw);
979 
980 
981  for (y = 0; y < ny; y++) {
982  register cxint i = x * ny + y;
983 
984  if (_bpm[i] == 0) {
985  zsum[x] += _zraw[i];
986  }
987  }
988 
989  if (zsum[x] > zmax) {
990  zmax = zsum[x];
991  }
992  }
993 
994  }
995 
996 
997  /*
998  * Allocate the buffers for the normalized images and scale
999  * the fiber spectrum fluxes and errors.
1000  */
1001 
1002  zx = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
1003  zv = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
1004 
1005 
1006  for (x = 0; x < nx; x++) {
1007 
1008  register cxint y = 0;
1009 
1010  register cxdouble scale = zmax / zsum[x];
1011  register const cxdouble* _zraw = cpl_image_get_data_double(zraw);
1012  register const cxdouble* _zvar = cpl_image_get_data_double(zvar);
1013  register cxdouble* _zx = cpl_image_get_data_double(zx);
1014  register cxdouble* _zv = cpl_image_get_data_double(zv);
1015 
1016  for(y = 0; y < nx; y++) {
1017  register cxint i = x * ny + y;
1018 
1019  _zx[i] = _zraw[i] * scale;
1020  _zv[i] = _zvar[i] * scale;
1021  }
1022 
1023  }
1024 
1025  cx_free(zsum);
1026  zsum = NULL;
1027 
1028  }
1029  else {
1030  zx = zraw;
1031  zv = zvar;
1032  }
1033 
1034 
1035  /*
1036  * Save the initial values of the profile models exponent parameter,
1037  * since it must be reset after each bin has been fitted.
1038  */
1039 
1040  giraffe_error_push();
1041 
1042  exponent = giraffe_model_get_parameter(profile, "Width2");
1043 
1044  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1045  exponent = 0.;
1046  }
1047 
1048  giraffe_error_pop();
1049 
1050 
1051  /*
1052  * Get the calibration spectrum reference index column from the
1053  * fiber list.
1054  */
1055 
1056  ridx = giraffe_fiberlist_query_index(fibers);
1057 
1058 
1059  /*
1060  * Allocate buffers for the profile data points and their errors. The
1061  * buffer size is choosen to be large enough for the number of bins of
1062  * requested psf data object and the given maximum fiber width.
1063  */
1064 
1065  mx = cpl_matrix_new(nbins * config->mwidth, 1);
1066  my = cpl_matrix_new(nbins * config->mwidth, 1);
1067  ms = cpl_matrix_new(nbins * config->mwidth, 1);
1068 
1069  if ((mx == NULL) || (my == NULL) || (ms == NULL)) {
1070  if (config->normalize == TRUE) {
1071  cpl_image_delete(zx);
1072  zx = NULL;
1073 
1074  cpl_image_delete(zv);
1075  zv = NULL;
1076  }
1077 
1078  if (mx != NULL) {
1079  cpl_matrix_delete(mx);
1080  mx = NULL;
1081  }
1082 
1083  if (my != NULL) {
1084  cpl_matrix_delete(my);
1085  my = NULL;
1086  }
1087 
1088  if (ms != NULL) {
1089  cpl_matrix_delete(ms);
1090  ms = NULL;
1091  }
1092 
1093  return 1;
1094  }
1095 
1096 
1097  /*
1098  * Allocate the buffers of the results structure here, to avoid
1099  * complicated error handling in the nested loops.
1100  */
1101 
1102  giraffe_psfdata_set_model(result, giraffe_model_get_name(profile));
1103 
1104  for (n = 0; n < giraffe_model_count_parameters(profile); n++) {
1105 
1106  const cxchar* name = giraffe_model_parameter_name(profile, n);
1107 
1108  cpl_image* values = cpl_image_new(ns, nbins, CPL_TYPE_DOUBLE);
1109 
1110  if ((name == NULL) || (values == NULL)) {
1111 
1112  giraffe_psfdata_clear(result);
1113 
1114  cpl_matrix_delete(mx);
1115  mx = NULL;
1116 
1117  cpl_matrix_delete(my);
1118  my = NULL;
1119 
1120  cpl_matrix_delete(ms);
1121  ms = NULL;
1122 
1123  if (config->normalize == TRUE) {
1124  cpl_image_delete(zx);
1125  zx = NULL;
1126 
1127  cpl_image_delete(zv);
1128  zv = NULL;
1129  }
1130 
1131  return 1;
1132  }
1133 
1134  giraffe_psfdata_set_data(result, name, values);
1135 
1136  }
1137 
1138 
1139  /*
1140  * Loop over all available fibers
1141  */
1142 
1143  for (fiber = 0; fiber < ns; fiber++) {
1144 
1145  cxint x = 0;
1146  cxint bin = 0;
1147  cxint cs = cpl_table_get_int(fibers, ridx, fiber, NULL) - 1;
1148  const cxint* _bpm = NULL;
1149 
1150  const cxdouble* _locy = cpl_image_get_data_double(locy);
1151  const cxdouble* _locw = cpl_image_get_data_double(locw);
1152  const cxdouble* _zx = cpl_image_get_data_double(zx);
1153  const cxdouble* _zv = cpl_image_get_data_double(zv);
1154 
1155 
1156  if (bpm != NULL) {
1157  _bpm = cpl_image_get_data_int(bpm);
1158  }
1159 
1160 
1161  /*
1162  * Fit a profile for each bin
1163  */
1164 
1165  for (x = 0, bin = 0; x < nx; x += config->bsize, bin++) {
1166 
1167  register cxint k = 0;
1168  register cxint xx = 0;
1169 
1170  cxint status = 0;
1171  cxint ndata = 0;
1172  cxint iterations = giraffe_model_get_iterations(profile);
1173 
1174  cxdouble amplitude = 0.;
1175  cxdouble bckground = 0.;
1176  cxdouble center = 0.;
1177  cxdouble width1 = 0.;
1178  cxdouble width2 = 0.;
1179 
1180  GiPsfBin xbin = {0., 0., 0., 0., 0.};
1181 
1182 
1183  /*
1184  * Loop over each element of this bin
1185  */
1186 
1187  for (k = 0, xx = x; (k < config->bsize) && (xx < nx); k++, xx++) {
1188 
1189  register cxint y = 0;
1190  register cxint l = xx * ns + cs;
1191  register cxint m = xx * ny;
1192 
1193  cxdouble zxmin = CX_MAXDOUBLE;
1194  cxdouble zxmax = 0.;
1195  cxdouble swidth = CX_MIN(_locw[l], config->mwidth);
1196  cxdouble ylo = (cxint) floor(_locy[l] - swidth);
1197  cxdouble yup = (cxint) ceil(_locy[l] + swidth);
1198  cxdouble ycenter = _locy[l];
1199 
1200 
1201  ylo = CX_MAX(0., ylo);
1202  yup = CX_MIN(ny, yup);
1203 
1204  if (_bpm == NULL) {
1205 
1206  for (y = ylo; y < yup; y++) {
1207 
1208  register cxint i = m + y;
1209 
1210  cpl_matrix_set(mx, ndata, 0, (cxdouble)y - ycenter);
1211  cpl_matrix_set(my, ndata, 0, _zx[i]);
1212  cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
1213 
1214  if (_zx[i] > zxmax) {
1215  zxmax = _zx[i];
1216  }
1217 
1218  if (_zx[i] < zxmin) {
1219  zxmin = _zx[i];
1220  }
1221 
1222  ++ndata;
1223 
1224  }
1225 
1226  }
1227  else {
1228 
1229  for (y = ylo; y < yup; y++) {
1230 
1231  register cxint i = m + y;
1232 
1233  if (_bpm[i] == 0) {
1234  cpl_matrix_set(mx, ndata, 0,
1235  (cxdouble)y - ycenter);
1236  cpl_matrix_set(my, ndata, 0, _zx[i]);
1237  cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
1238 
1239  if (_zx[i] > zxmax) {
1240  zxmax = _zx[i];
1241  }
1242 
1243  if (_zx[i] < zxmin) {
1244  zxmin = _zx[i];
1245  }
1246 
1247  ++ndata;
1248  }
1249 
1250  }
1251 
1252  }
1253 
1254  xbin.zmin += zxmin;
1255  xbin.zmax += zxmax;
1256  xbin.xcenter += xx;
1257  xbin.ycenter += ycenter;
1258  xbin.ywidth += swidth;
1259 
1260  }
1261 
1262 
1263  /*
1264  * Compute per bin average values
1265  */
1266 
1267  xbin.zmin /= k;
1268  xbin.zmax /= k;
1269  xbin.xcenter /= k;
1270  xbin.ycenter /= k;
1271  xbin.ywidth /= k;
1272 
1273 
1274  /*
1275  * Avoid negative background values
1276  */
1277 
1278  xbin.zmin = CX_MAX(0., xbin.zmin);
1279 
1280 
1281  /*
1282  * Setup model for this bin
1283  */
1284 
1285  giraffe_model_set_parameter(profile, "Amplitude",
1286  xbin.zmax - xbin.zmin);
1287  giraffe_model_set_parameter(profile, "Center", 0.);
1288  giraffe_model_set_parameter(profile, "Background", xbin.zmin);
1289 
1290  switch (psfmodel) {
1291  case PROFILE_PSFEXP:
1292  width1 = pow(xbin.ywidth, exponent) / cutoff;
1293  giraffe_model_set_parameter(profile, "Width2", exponent);
1294  break;
1295 
1296  case PROFILE_PSFEXP2:
1297  width1 = xbin.ywidth / pow(cutoff, 1. / exponent);
1298  giraffe_model_set_parameter(profile, "Width2", exponent);
1299  break;
1300 
1301  case PROFILE_GAUSSIAN:
1302  width1 = xbin.ywidth / pow(cutoff, 0.5);
1303  break;
1304 
1305  default:
1306  break;
1307  }
1308 
1309  giraffe_model_set_parameter(profile, "Width1", width1);
1310 
1311 
1312  /*
1313  * Fit the profile
1314  */
1315 
1316  status = giraffe_model_fit_sequence(profile, mx, my, ms,
1317  ndata, 0, 1);
1318 
1319  amplitude = giraffe_model_get_parameter(profile, "Amplitude");
1320  bckground = giraffe_model_get_parameter(profile, "Background");
1321  center = giraffe_model_get_parameter(profile, "Center");
1322  width1 = giraffe_model_get_parameter(profile, "Width1");
1323 
1324  if ((psfmodel == PROFILE_PSFEXP) ||
1325  (psfmodel == PROFILE_PSFEXP2)) {
1326  width2 = giraffe_model_get_parameter(profile, "Width2");
1327  }
1328 
1329  /*
1330  * Check fit results. The fit failed, if the maximum
1331  * number of iterations has been reached, fitted amplitude
1332  * is negative or the fitted width is negative.
1333  */
1334 
1335  if ((status != 0) ||
1336  (giraffe_model_get_position(profile) >= iterations) ||
1337  (amplitude <= 0.) ||
1338  (width1 <= 0.)) {
1339 
1340  xbin.xcenter = -1.;
1341  amplitude = 0.;
1342  bckground = 0.;
1343  center = 0.;
1344  width1 = 0.;
1345  width2 = 0.;
1346 
1347  }
1348 
1349  giraffe_psfdata_set_bin(result, fiber, bin, xbin.xcenter);
1350 
1351  giraffe_psfdata_set(result, "Amplitude", fiber, bin, amplitude);
1352  giraffe_psfdata_set(result, "Center", fiber, bin,
1353  xbin.ycenter + center);
1354  giraffe_psfdata_set(result, "Background", fiber, bin, bckground);
1355  giraffe_psfdata_set(result, "Width1", fiber, bin, width1);
1356 
1357  if ((psfmodel == PROFILE_PSFEXP) ||
1358  (psfmodel == PROFILE_PSFEXP2)) {
1359  giraffe_psfdata_set(result, "Width2", fiber, bin, width2);
1360  }
1361 
1362  }
1363 
1364  ++nspectra;
1365 
1366  }
1367 
1368 
1369  /*
1370  * Cleanup
1371  */
1372 
1373  cpl_matrix_delete(mx);
1374  mx = NULL;
1375 
1376  cpl_matrix_delete(my);
1377  my = NULL;
1378 
1379  cpl_matrix_delete(ms);
1380  ms = NULL;
1381 
1382  if (config->normalize == TRUE) {
1383  cpl_image_delete(zx);
1384  zx = NULL;
1385 
1386  cpl_image_delete(zv);
1387  zv = NULL;
1388  }
1389 
1390  return 0;
1391 
1392 }
1393 
1394 
1395 /*
1396  * @brief
1397  * Fit a PSF profile model to each fiber spectrum.
1398  *
1399  * @param result Fitted PSF profile model parameters.
1400  * @param zraw Raw image of the fiber spectra.
1401  * @param zvar Raw image of flux errors.
1402  * @param locy Fiber centroid position.
1403  * @param locw Fiber width.
1404  * @param fibers List of fibers to process.
1405  * @param bpm Optional bad pixel map.
1406  * @param config PSF profile fit setup parameters.
1407  *
1408  * @return
1409  * The function returns @c 0 on success, or a non-zero value otherwise.
1410  *
1411  * The function fits a profile model given by @em psfmodel to each bin along
1412  * the dispersion direction (x-axis) for all fibers listed in @em fibers.
1413  * The bin size is given by the setup parameters @em config. It also
1414  * specifies the maximum allowed half width of a fiber spectrum.
1415  *
1416  * @note
1417  * Currently only the line models "psfexp", "psfexp2" and "gaussian"
1418  * are supported.
1419  */
1420 
1421 inline static cxint
1422 _giraffe_psf_refine_profile(GiPsfData* result, const GiPsfData* psfdata,
1423  cpl_image* zraw, cpl_image* zvar,
1424  cpl_table* fibers, cpl_image* bpm,
1425  GiModel* profile, GiPsfParams* config)
1426 {
1427 
1428  const cxchar* model = NULL;
1429 
1430  const cxdouble cutoff = log(config->limit);
1431 
1432  cxint nx = 0;
1433  cxint ny = 0;
1434  cxint ns = 0;
1435  cxint fiber = 0;
1436  cxint nbins = 0;
1437  cxint nspectra = 0;
1438  cxint binsize = 0;
1439 
1440  cxsize n = 0;
1441 
1442  cpl_matrix* mx = NULL;
1443  cpl_matrix* my = NULL;
1444  cpl_matrix* ms = NULL;
1445 
1446  GiProfileId psfmodel = 0;
1447 
1448 
1449  cx_assert(result != NULL);
1450  cx_assert(psfdata != NULL);
1451  cx_assert((zraw != NULL) && (zvar != NULL));
1452  cx_assert(fibers != NULL);
1453  cx_assert(profile != NULL);
1454  cx_assert(config != NULL);
1455 
1456  cx_assert(cpl_image_get_size_x(zraw) == cpl_image_get_size_x(zvar));
1457  cx_assert(cpl_image_get_size_y(zraw) == cpl_image_get_size_y(zvar));
1458 
1459 
1460  nx = cpl_image_get_size_y(zraw);
1461  ny = cpl_image_get_size_x(zraw);
1462  ns = cpl_table_get_nrow(fibers);
1463 
1464  if ((bpm != NULL) && (cpl_image_get_type(bpm) != CPL_TYPE_INT)) {
1465  return -1;
1466  }
1467 
1468  if ((giraffe_psfdata_fibers(result) != (cxsize) ns) ||
1469  (giraffe_psfdata_bins(result) != (cxsize) nx)) {
1470  return -2;
1471  }
1472 
1473  if ((giraffe_psfdata_xsize(result) != (cxsize) ny) ||
1474  (giraffe_psfdata_ysize(result) != (cxsize) nx)) {
1475  return -2;
1476  }
1477 
1478  nbins = giraffe_psfdata_bins(result);
1479 
1480  if ((giraffe_psfdata_fibers(psfdata) != (cxsize) ns)) {
1481  return -3;
1482  }
1483 
1484  if ((giraffe_psfdata_xsize(psfdata) != (cxsize) ny) ||
1485  (giraffe_psfdata_ysize(psfdata) != (cxsize) nx)) {
1486  return -3;
1487  }
1488 
1489  binsize = nx / giraffe_psfdata_bins(psfdata);
1490 
1491 
1492  /*
1493  * Check the model type. Only the line models "psfexp", "psfexp2" and
1494  * "gaussian" are supported.
1495  */
1496 
1497  model = giraffe_model_get_name(profile);
1498 
1499  if (strcmp(model, "psfexp") == 0) {
1500  psfmodel = PROFILE_PSFEXP;
1501  }
1502  else if (strcmp(model, "psfexp2") == 0) {
1503  psfmodel = PROFILE_PSFEXP2;
1504  }
1505  else if (strcmp(model, "gaussian") == 0) {
1506  psfmodel = PROFILE_GAUSSIAN;
1507  }
1508  else {
1509  return -4;
1510  }
1511 
1512 
1513  /*
1514  * Allocate buffers for the profile data points and their errors. The
1515  * buffer size is choosen to be large enough for the number of bins of
1516  * requested psf data object and the given maximum fiber width.
1517  */
1518 
1519  mx = cpl_matrix_new(nbins * config->mwidth, 1);
1520  my = cpl_matrix_new(nbins * config->mwidth, 1);
1521  ms = cpl_matrix_new(nbins * config->mwidth, 1);
1522 
1523  if ((mx == NULL) || (my == NULL) || (ms == NULL)) {
1524 
1525  if (mx != NULL) {
1526  cpl_matrix_delete(mx);
1527  mx = NULL;
1528  }
1529 
1530  if (my != NULL) {
1531  cpl_matrix_delete(my);
1532  my = NULL;
1533  }
1534 
1535  if (ms != NULL) {
1536  cpl_matrix_delete(ms);
1537  ms = NULL;
1538  }
1539 
1540  return 1;
1541 
1542  }
1543 
1544 
1545  /*
1546  * Allocate the buffers of the results structure here, to avoid
1547  * complicated error handling in the nested loops.
1548  */
1549 
1550  giraffe_psfdata_set_model(result, giraffe_model_get_name(profile));
1551 
1552  for (n = 0; n < giraffe_model_count_parameters(profile); n++) {
1553 
1554  const cxchar* name = giraffe_model_parameter_name(profile, n);
1555 
1556  cpl_image* values = cpl_image_new(ns, nbins, CPL_TYPE_DOUBLE);
1557 
1558 
1559  if ((name == NULL) || (values == NULL)) {
1560 
1561  giraffe_psfdata_clear(result);
1562 
1563  cpl_matrix_delete(mx);
1564  mx = NULL;
1565 
1566  cpl_matrix_delete(my);
1567  my = NULL;
1568 
1569  cpl_matrix_delete(ms);
1570  ms = NULL;
1571 
1572  return 1;
1573 
1574  }
1575 
1576  giraffe_psfdata_set_data(result, name, values);
1577 
1578  }
1579 
1580 
1581  /*
1582  * Loop over all available fibers
1583  */
1584 
1585  for (fiber = 0; fiber < ns; fiber++) {
1586 
1587  cxint x = 0;
1588  const cxint* _bpm = NULL;
1589 
1590  const cxdouble* _zx = cpl_image_get_data_double(zraw);
1591  const cxdouble* _zv = cpl_image_get_data_double(zvar);
1592 
1593 
1594  if (bpm != NULL) {
1595  _bpm = cpl_image_get_data_int(bpm);
1596  }
1597 
1598 
1599  /*
1600  * Fit a profile for each bin
1601  */
1602 
1603  for (x = 0; x < nx; x++) {
1604 
1605  register cxint y = 0;
1606  register cxint m = x * ny;
1607  register cxint bin = CX_MAX(0, CX_MIN((cxint) floor(x / binsize),
1608  nbins));
1609 
1610  cxint status = 0;
1611  cxint ndata = 0;
1612  cxint iterations = giraffe_model_get_iterations(profile);
1613 
1614  cxdouble xcenter = 0.;
1615  cxdouble ycenter = 0.;
1616  cxdouble swidth = 0.;
1617  cxdouble ylo = 0.;
1618  cxdouble yup = ny;
1619  cxdouble amplitude = giraffe_psfdata_get(psfdata, "Amplitude",
1620  fiber, bin);
1621  cxdouble bckground = giraffe_psfdata_get(psfdata, "Background",
1622  fiber, bin);
1623  cxdouble center = giraffe_psfdata_get(psfdata, "Center",
1624  fiber, bin);
1625  cxdouble width1 = giraffe_psfdata_get(psfdata, "Width1",
1626  fiber, bin);
1627  cxdouble width2 = 0.;
1628 
1629 
1630  switch (psfmodel) {
1631  case PROFILE_PSFEXP:
1632  width2 = giraffe_psfdata_get(psfdata, "Width2",
1633  fiber, bin);
1634  swidth = pow(width1 * cutoff, 1./ width2);
1635  break;
1636 
1637  case PROFILE_PSFEXP2:
1638  width2 = giraffe_psfdata_get(psfdata, "Width2",
1639  fiber, bin);
1640  swidth = width1 * pow(cutoff, 1./ width2);
1641  break;
1642 
1643  case PROFILE_GAUSSIAN:
1644  swidth = width1 * pow(cutoff, 0.5);
1645  break;
1646 
1647  default:
1648  break;
1649  }
1650 
1651  swidth = CX_MIN(swidth, config->mwidth);
1652 
1653  ylo = (cxint) floor(center - swidth);
1654  ylo = CX_MAX(0., ylo);
1655 
1656  yup = (cxint) ceil(center + swidth);
1657  yup = CX_MIN(ny, yup);
1658 
1659  if (_bpm == NULL) {
1660 
1661  for (y = ylo; y < yup; y++) {
1662 
1663  register cxint i = m + y;
1664 
1665  cpl_matrix_set(mx, ndata, 0, (cxdouble)y - center);
1666  cpl_matrix_set(my, ndata, 0, _zx[i]);
1667  cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
1668 
1669  ++ndata;
1670 
1671  }
1672 
1673  }
1674  else {
1675 
1676  for (y = ylo; y < yup; y++) {
1677 
1678  register cxint i = m + y;
1679 
1680  if (_bpm[i] == 0) {
1681  cpl_matrix_set(mx, ndata, 0, (cxdouble)y - center);
1682  cpl_matrix_set(my, ndata, 0, _zx[i]);
1683  cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
1684 
1685  ++ndata;
1686  }
1687 
1688  }
1689 
1690  }
1691 
1692 
1693  /*
1694  * Avoid negative background values
1695  */
1696 
1697  bckground = CX_MAX(0., bckground);
1698 
1699 
1700  /*
1701  * Setup model for this bin
1702  */
1703 
1704  giraffe_model_set_parameter(profile, "Amplitude", amplitude);
1705  giraffe_model_set_parameter(profile, "Center", 0.);
1706  giraffe_model_set_parameter(profile, "Background", bckground);
1707  giraffe_model_set_parameter(profile, "Width1", width1);
1708 
1709  switch (psfmodel) {
1710  case PROFILE_PSFEXP:
1711  giraffe_model_set_parameter(profile, "Width2", width2);
1712  break;
1713 
1714  case PROFILE_PSFEXP2:
1715  giraffe_model_set_parameter(profile, "Width2", width2);
1716  break;
1717 
1718  case PROFILE_GAUSSIAN:
1719  break;
1720 
1721  default:
1722  break;
1723  }
1724 
1725 
1726  /*
1727  * Fit the profile
1728  */
1729 
1730  status = giraffe_model_fit_sequence(profile, mx, my, ms,
1731  ndata, 0, 1);
1732 
1733  amplitude = giraffe_model_get_parameter(profile, "Amplitude");
1734  bckground = giraffe_model_get_parameter(profile, "Background");
1735  ycenter = giraffe_model_get_parameter(profile, "Center");
1736  width1 = giraffe_model_get_parameter(profile, "Width1");
1737 
1738  if ((psfmodel == PROFILE_PSFEXP) ||
1739  (psfmodel == PROFILE_PSFEXP2)) {
1740  width2 = giraffe_model_get_parameter(profile, "Width2");
1741  }
1742 
1743 
1744  /*
1745  * Check fit results. The fit failed, if the maximum
1746  * number of iterations has been reached, fitted amplitude
1747  * is negative or the fitted width is negative.
1748  */
1749 
1750  if ((status != 0) ||
1751  (giraffe_model_get_position(profile) >= iterations) ||
1752  (amplitude <= 0.) ||
1753  (width1 <= 0.)) {
1754 
1755  xcenter = -1.;
1756  ycenter = 0.;
1757  amplitude = 0.;
1758  bckground = 0.;
1759  width1 = 0.;
1760  width2 = 0.;
1761 
1762  }
1763  else {
1764  xcenter = x;
1765  }
1766 
1767  giraffe_psfdata_set_bin(result, fiber, x, xcenter);
1768 
1769  giraffe_psfdata_set(result, "Amplitude", fiber, x, amplitude);
1770  giraffe_psfdata_set(result, "Center", fiber, x,
1771  ycenter + center);
1772  giraffe_psfdata_set(result, "Background", fiber, x, bckground);
1773  giraffe_psfdata_set(result, "Width1", fiber, x, width1);
1774 
1775  if ((psfmodel == PROFILE_PSFEXP) ||
1776  (psfmodel == PROFILE_PSFEXP2)) {
1777  giraffe_psfdata_set(result, "Width2", fiber, x, width2);
1778  }
1779 
1780  }
1781 
1782  ++nspectra;
1783 
1784  }
1785 
1786 
1787  /*
1788  * Cleanup
1789  */
1790 
1791  cpl_matrix_delete(mx);
1792  mx = NULL;
1793 
1794  cpl_matrix_delete(my);
1795  my = NULL;
1796 
1797  cpl_matrix_delete(ms);
1798  ms = NULL;
1799 
1800  return 0;
1801 
1802 }
1803 
1804 
1805 /*
1806  * @brief
1807  * Compute a one dimensional fit of all PSF profile model parameters for
1808  * the given grid.
1809  *
1810  * @param psfdata PSF profile parameters to be fitted.
1811  * @param fibers The list of fibers for which a PSF profile is available.
1812  * @param order Order of the Chebyshev polynomial to fit.
1813  * @param setup Sigma clipping algorithm configuration object.
1814  *
1815  * @return
1816  * The function returns a newly created PSF data object containing the
1817  * fitted parameters on success, or @c NULL if an error occurrs.
1818  *
1819  * TBD
1820  */
1821 
1822 inline static GiPsfData*
1823 _giraffe_psf_fit_parameters1d(const GiPsfData* psfdata,
1824  const cpl_table* fibers,
1825  const cxchar** names,
1826  cxint order,
1827  const GiClipParams* setup)
1828 {
1829 
1830  cxint i = 0;
1831  cxint ns = 0;
1832  cxint nx = 0;
1833  cxint ny = 0;
1834  cxint status = 0;
1835 
1836  GiPsfData* psffit = NULL;
1837 
1838 
1839  cx_assert(psfdata != NULL);
1840  cx_assert(fibers != NULL);
1841  cx_assert(setup != NULL);
1842 
1843  ns = giraffe_psfdata_fibers(psfdata);
1844  nx = giraffe_psfdata_ysize(psfdata);
1845  ny = giraffe_psfdata_xsize(psfdata);
1846 
1847  psffit = giraffe_psfdata_create(ns, nx, ny, nx);
1848 
1849  giraffe_psfdata_set_model(psffit, giraffe_psfdata_get_model(psfdata));
1850 
1851 
1852  /*
1853  * Create the bin data for the fitted PSF parameter data. This
1854  * is actually not needed, but makes psffit a complete, valid
1855  * PSF data object.
1856  */
1857 
1858  for (i = 0; i < ns; i++) {
1859 
1860  register cxint j = 0;
1861 
1862  for (j = 0; j < nx; j++) {
1863  giraffe_psfdata_set_bin(psffit, i, j, j);
1864  }
1865 
1866  }
1867 
1868 
1869  if (names == NULL) {
1870 
1871  cxsize j = 0;
1872  cxsize count = giraffe_psfdata_parameters(psfdata);
1873 
1874  for (j = 0; j < count; j++) {
1875 
1876  const cxchar* name = giraffe_psfdata_get_name(psfdata, j);
1877 
1878  GiPsfParameterFit pfit = {NULL, NULL};
1879 
1880 
1881  pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
1882  pfit.coeffs = cpl_matrix_new(order + 1, ns);
1883 
1884  status = _giraffe_psf_fit_profile1d(&pfit, psfdata, name,
1885  fibers, order, setup);
1886 
1887  if (status != 0) {
1888  cpl_matrix_delete(pfit.coeffs);
1889  pfit.coeffs = NULL;
1890 
1891  cpl_image_delete(pfit.fit);
1892  pfit.fit = NULL;
1893 
1894  giraffe_psfdata_delete(psffit);
1895  psffit = NULL;
1896 
1897  return NULL;
1898  }
1899  else {
1900  giraffe_psfdata_set_data(psffit, name, pfit.fit);
1901  pfit.fit = NULL;
1902 
1903  cpl_matrix_delete(pfit.coeffs);
1904  pfit.coeffs = NULL;
1905 
1906  }
1907 
1908  }
1909 
1910  }
1911  else {
1912 
1913  /*
1914  * For each PSF parameter, whose name is listed in name and present in
1915  * the PSF data object, a one dimensional polynomial model is created.
1916  */
1917 
1918  i = 0;
1919  while (names[i] != NULL) {
1920 
1921  if (giraffe_psfdata_contains(psfdata, names[i]) == TRUE) {
1922 
1923  GiPsfParameterFit pfit = {NULL, NULL};
1924 
1925 
1926  pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
1927  pfit.coeffs = cpl_matrix_new(order + 1, ns);
1928 
1929  status = _giraffe_psf_fit_profile1d(&pfit, psfdata, names[i],
1930  fibers, order, setup);
1931 
1932  if (status != 0) {
1933  cpl_matrix_delete(pfit.coeffs);
1934  pfit.coeffs = NULL;
1935 
1936  cpl_image_delete(pfit.fit);
1937  pfit.fit = NULL;
1938 
1939  giraffe_psfdata_delete(psffit);
1940  psffit = NULL;
1941 
1942  return NULL;
1943  }
1944  else {
1945  giraffe_psfdata_set_data(psffit, names[i], pfit.fit);
1946  pfit.fit = NULL;
1947 
1948  cpl_matrix_delete(pfit.coeffs);
1949  pfit.coeffs = NULL;
1950 
1951  }
1952 
1953  }
1954 
1955  ++i;
1956 
1957  }
1958 
1959  }
1960 
1961  return psffit;
1962 
1963 }
1964 
1965 
1966 /*
1967  * @brief
1968  * Compute the fit of all PSF profile model parameters for the given grid.
1969  *
1970  * @param result Container for the fitted PSF parameters.
1971  *
1972  * @return
1973  * The function returns 0 on success, or a non-zero value otherwise.
1974  *
1975  * TBD
1976  */
1977 
1978 inline static GiPsfData*
1979 _giraffe_psf_fit_parameters(const GiPsfData* psfdata,
1980  const cpl_table* fibers,
1981  const cxchar** names,
1982  cxint yorder, cxint worder,
1983  const GiClipParams* setup)
1984 {
1985 
1986  const cxchar* center = NULL;
1987 
1988  cxint i = 0;
1989  cxint ns = 0;
1990  cxint nx = 0;
1991  cxint ny = 0;
1992  cxint status = 0;
1993 
1994  GiPsfData* psffit = NULL;
1995 
1996 
1997  cx_assert(psfdata != NULL);
1998  cx_assert(fibers != NULL);
1999  cx_assert(names != NULL);
2000  cx_assert(setup != NULL);
2001 
2002  ns = giraffe_psfdata_fibers(psfdata);
2003  nx = giraffe_psfdata_ysize(psfdata);
2004  ny = giraffe_psfdata_xsize(psfdata);
2005 
2006  psffit = giraffe_psfdata_create(ns, nx, ny, nx);
2007 
2008  giraffe_psfdata_set_model(psffit, giraffe_psfdata_get_model(psfdata));
2009 
2010 
2011  /*
2012  * Create the bin data for the fitted PSF parameter data. This
2013  * is actually not needed, but makes psffit a complete, valid
2014  * PSF data object.
2015  */
2016 
2017  for (i = 0; i < ns; i++) {
2018 
2019  register cxint j = 0;
2020 
2021  for (j = 0; j < nx; j++) {
2022  giraffe_psfdata_set_bin(psffit, i, j, j);
2023  }
2024 
2025  }
2026 
2027  center = names[0];
2028  if (giraffe_psfdata_contains(psfdata, center) == FALSE) {
2029 
2030  giraffe_psfdata_delete(psffit);
2031  psffit = NULL;
2032 
2033  return NULL;
2034 
2035  }
2036  else {
2037 
2038  GiPsfParameterFit pfit = {NULL, NULL};
2039 
2040 
2041  pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
2042  pfit.coeffs = cpl_matrix_new(yorder + 1, ns);
2043 
2044  status = _giraffe_psf_fit_profile1d(&pfit, psfdata, center, fibers,
2045  yorder, setup);
2046 
2047  if (status != 0) {
2048  cpl_matrix_delete(pfit.coeffs);
2049  pfit.coeffs = NULL;
2050 
2051  cpl_image_delete(pfit.fit);
2052  pfit.fit = NULL;
2053 
2054  giraffe_psfdata_delete(psffit);
2055  psffit = NULL;
2056 
2057  return NULL;
2058  }
2059  else {
2060  giraffe_psfdata_set_data(psffit, center, pfit.fit);
2061  pfit.fit = NULL;
2062 
2063  cpl_matrix_delete(pfit.coeffs);
2064  pfit.coeffs = NULL;
2065 
2066  }
2067 
2068  }
2069 
2070 
2071  i = 1;
2072  while (names[i] != NULL) {
2073 
2074  if (giraffe_psfdata_contains(psfdata, names[i]) == TRUE) {
2075 
2076  const cpl_image* xbin = giraffe_psfdata_get_bins(psfdata);
2077  const cpl_image* ybin = giraffe_psfdata_get_data(psfdata, center);
2078  const cpl_image* yfit = giraffe_psfdata_get_data(psffit, center);
2079  const cpl_image* pdata = giraffe_psfdata_get_data(psfdata,
2080  names[i]);
2081 
2082  GiPsfParameterFit pfit = {NULL, NULL};
2083 
2084 
2085  pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
2086  pfit.coeffs = cpl_matrix_new(yorder + 1, worder + 1);
2087 
2088  status = _giraffe_psf_fit_profile2d(&pfit, fibers, pdata, xbin,
2089  ybin, yorder, worder, yfit,
2090  0, ny, setup);
2091 
2092  if (status != 0) {
2093  cpl_matrix_delete(pfit.coeffs);
2094  pfit.coeffs = NULL;
2095 
2096  cpl_image_delete(pfit.fit);
2097  pfit.fit = NULL;
2098 
2099  giraffe_psfdata_delete(psffit);
2100  psffit = NULL;
2101 
2102  return NULL;
2103  }
2104  else {
2105  giraffe_psfdata_set_data(psffit, names[i], pfit.fit);
2106  pfit.fit = NULL;
2107 
2108  cpl_matrix_delete(pfit.coeffs);
2109  pfit.coeffs = NULL;
2110 
2111  }
2112  }
2113 
2114  ++i;
2115 
2116  }
2117 
2118  return psffit;
2119 
2120 }
2121 
2122 
2123 /*
2124  * @brief
2125  * Compute a localization mask and coefficients from the PSF profile
2126  * parameters.
2127  *
2128  * @return
2129  * The function returns 0 on success, or a non-zero value otherwise.
2130  *
2131  * TBD
2132  */
2133 
2134 inline static int
2135 _giraffe_psf_compute_mask(GiMaskPosition* positions, GiMaskPosition* coeffs,
2136  const GiPsfData* psfdata, const cpl_table* fibers,
2137  cxint yorder, cxint worder,
2138  const GiClipParams* setup)
2139 {
2140 
2141  const cxchar* const lcenter = "Center";
2142  const cxchar* const lwidth = "Width1";
2143  const cxchar* const lexponent = "Width2";
2144  const cxchar* model = NULL;
2145 
2146  cxint i = 0;
2147  cxint ns = 0;
2148  cxint nx = 0;
2149  cxint ny = 0;
2150  cxint status = 0;
2151 
2152  const cpl_image* xbin = NULL;
2153  const cpl_image* ybin = NULL;
2154  cpl_image* width = NULL;
2155 
2156  GiPsfParameterFit center = {NULL, NULL};
2157  GiPsfParameterFit halfwidth = {NULL, NULL};
2158 
2159  GiProfileId psfmodel = 0;
2160 
2161 
2162  cx_assert((positions != NULL) &&
2163  (positions->type == GIMASK_FITTED_DATA) &&
2164  (positions->my != NULL) &&
2165  (positions->mw != NULL));
2166  cx_assert((coeffs != NULL) &&
2167  (coeffs->type == GIMASK_FIT_COEFFS) &&
2168  (coeffs->my != NULL) &&
2169  (coeffs->mw != NULL));
2170  cx_assert(psfdata != NULL);
2171  cx_assert(fibers != NULL);
2172  cx_assert(setup != NULL);
2173 
2174  model = giraffe_psfdata_get_model(psfdata);
2175 
2176  if (strcmp(model, "psfexp") == 0) {
2177  psfmodel = PROFILE_PSFEXP;
2178  }
2179  else if (strcmp(model, "psfexp2") == 0) {
2180  psfmodel = PROFILE_PSFEXP2;
2181  }
2182  else if (strcmp(model, "gaussian") == 0) {
2183  psfmodel = PROFILE_GAUSSIAN;
2184  }
2185  else {
2186  return -1;
2187  }
2188 
2189  ns = giraffe_psfdata_fibers(psfdata);
2190  nx = giraffe_psfdata_ysize(psfdata);
2191  ny = giraffe_psfdata_xsize(psfdata);
2192 
2193  if ((cpl_matrix_get_nrow(positions->my) != nx) ||
2194  (cpl_matrix_get_ncol(positions->my) != ns) ||
2195  (cpl_matrix_get_nrow(positions->mw) != nx) ||
2196  (cpl_matrix_get_ncol(positions->mw) != ns)) {
2197  return -1;
2198  }
2199 
2200  if ((cpl_matrix_get_nrow(coeffs->my) != yorder + 1) ||
2201  (cpl_matrix_get_ncol(coeffs->my) != ns)) {
2202  return -1;
2203  }
2204 
2205  if ((cpl_matrix_get_nrow(coeffs->mw) != worder + 1) ||
2206  (cpl_matrix_get_ncol(coeffs->mw) != worder + 1)) {
2207  return -1;
2208  }
2209 
2210  if (giraffe_psfdata_contains(psfdata, lcenter) == FALSE ||
2211  giraffe_psfdata_contains(psfdata, lwidth) == FALSE) {
2212  return 1;
2213  }
2214 
2215  center.fit = cpl_image_wrap_double(ns, nx,
2216  cpl_matrix_get_data(positions->my));
2217  center.coeffs = coeffs->my;
2218 
2219  status = _giraffe_psf_fit_profile1d(&center, psfdata, lcenter, fibers,
2220  yorder, setup);
2221 
2222  if (status != 0) {
2223  cpl_image_unwrap(center.fit);
2224 
2225  center.fit = NULL;
2226  center.coeffs = NULL;
2227 
2228  return 1;
2229  }
2230 
2231  width = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
2232 
2233  switch (psfmodel) {
2234  case PROFILE_PSFEXP:
2235  {
2236 
2237  const cxdouble LOG2 = log(2.);
2238 
2239  if (giraffe_psfdata_contains(psfdata, lexponent) == FALSE) {
2240  cpl_image_delete(width);
2241  width = NULL;
2242 
2243  cpl_image_unwrap(center.fit);
2244  center.fit = NULL;
2245  center.coeffs = NULL;
2246 
2247  return 1;
2248  }
2249 
2250  for (i = 0; i < ns; i++) {
2251 
2252  register cxint j = 0;
2253 
2254  register cxdouble* _width = cpl_image_get_data_double(width);
2255 
2256 
2257  for (j = 0; j < nx; j++) {
2258 
2259  register cxint k = j * ns + i;
2260 
2261  cxdouble width1 =
2262  giraffe_psfdata_get(psfdata, lwidth, i, j);
2263  cxdouble width2 =
2264  giraffe_psfdata_get(psfdata, lexponent, i, j);
2265 
2266 
2267  _width[k] = 2. * pow(LOG2 * width1, 1. / width2);
2268 
2269  }
2270 
2271  }
2272 
2273  }
2274  break;
2275 
2276  case PROFILE_PSFEXP2:
2277  {
2278 
2279  const cxdouble LOG2 = log(2.);
2280 
2281  if (giraffe_psfdata_contains(psfdata, lexponent) == FALSE) {
2282  cpl_image_delete(width);
2283  width = NULL;
2284 
2285  cpl_image_unwrap(center.fit);
2286  center.fit = NULL;
2287  center.coeffs = NULL;
2288 
2289  return 1;
2290  }
2291 
2292  for (i = 0; i < ns; i++) {
2293 
2294  register cxint j = 0;
2295 
2296  register cxdouble* _width = cpl_image_get_data_double(width);
2297 
2298 
2299  for (j = 0; j < nx; j++) {
2300 
2301  register cxint k = j * ns + i;
2302 
2303  cxdouble width1 =
2304  giraffe_psfdata_get(psfdata, lwidth, i, j);
2305  cxdouble width2 =
2306  giraffe_psfdata_get(psfdata, lexponent, i, j);
2307 
2308 
2309  _width[k] = 2. * pow(LOG2, 1. / width2) * width1;
2310 
2311  }
2312 
2313  }
2314 
2315  }
2316  break;
2317 
2318  case PROFILE_GAUSSIAN:
2319  {
2320 
2321  const cxdouble fwhmscale = 4. * sqrt(2. * log(2.));
2322 
2323  for (i = 0; i < ns; i++) {
2324 
2325  register cxint j = 0;
2326 
2327  register cxdouble* _width = cpl_image_get_data_double(width);
2328 
2329 
2330  for (j = 0; j < nx; j++) {
2331 
2332  register cxint k = j * ns + i;
2333 
2334  _width[k] = fwhmscale *
2335  giraffe_psfdata_get(psfdata, lwidth, i, j);
2336  }
2337 
2338  }
2339 
2340  }
2341  break;
2342 
2343  default:
2344  /* This point should never be reached! */
2345 
2346  cpl_image_delete(width);
2347  width = NULL;
2348 
2349  cpl_image_unwrap(center.fit);
2350  center.fit = NULL;
2351  center.coeffs = NULL;
2352 
2353  gi_error("Unsupported PSF profile model encountered!");
2354  break;
2355  }
2356 
2357 
2358  xbin = giraffe_psfdata_get_bins(psfdata);
2359  ybin = giraffe_psfdata_get_data(psfdata, lcenter);
2360 
2361 
2362  halfwidth.fit = cpl_image_wrap_double(ns, nx,
2363  cpl_matrix_get_data(positions->mw));
2364  halfwidth.coeffs = coeffs->mw;
2365 
2366  status = _giraffe_psf_fit_profile2d(&halfwidth, fibers, width, xbin,
2367  ybin, worder, worder, center.fit,
2368  0, ny, setup);
2369 
2370  if (status != 0) {
2371  cpl_image_unwrap(halfwidth.fit);
2372  halfwidth.fit = NULL;
2373  halfwidth.coeffs = NULL;
2374 
2375  cpl_image_delete(width);
2376  width = NULL;
2377 
2378  cpl_image_unwrap(center.fit);
2379  center.fit = NULL;
2380  center.coeffs = NULL;
2381 
2382  return 1;
2383  }
2384 
2385  cpl_image_unwrap(halfwidth.fit);
2386  halfwidth.fit = NULL;
2387  halfwidth.coeffs = NULL;
2388 
2389  cpl_image_delete(width);
2390  width = NULL;
2391 
2392  cpl_image_unwrap(center.fit);
2393  center.fit = NULL;
2394  center.coeffs = NULL;
2395 
2396  return 0;
2397 
2398 }
2399 
2400 
2401 inline static cpl_image*
2402 _giraffe_psf_simulate_mask(const GiPsfData* psfdata,
2403  const cpl_image* amplitude,
2404  const cpl_image* background,
2405  cxdouble cutoff)
2406 {
2407 
2408  const cxchar* model = NULL;
2409 
2410  cxint i = 0;
2411  cxint nfibers = 0;
2412  cxint nbins = 0;
2413  cxint _nbins = 0;
2414  cxint nx = 0;
2415  cxint ny = 0;
2416  cxint fiber = 0;
2417 
2418  cxdouble bsize = 1.;
2419  cxdouble _bsize = 1.;
2420  cxdouble* _mask = NULL;
2421 
2422  const cpl_image* center = NULL;
2423  const cpl_image* width = NULL;
2424  const cpl_image* exponent = NULL;
2425 
2426  cpl_image* mask = NULL;
2427 
2428  GiModel* profile = NULL;
2429 
2430  GiProfileId psfmodel = 0;
2431 
2432 
2433  cx_assert(psfdata != NULL);
2434 
2435  model = giraffe_psfdata_get_model(psfdata);
2436 
2437  if (strcmp(model, "psfexp") == 0) {
2438  psfmodel = PROFILE_PSFEXP;
2439  }
2440  else if (strcmp(model, "psfexp2") == 0) {
2441  psfmodel = PROFILE_PSFEXP2;
2442  }
2443  else if (strcmp(model, "gaussian") == 0) {
2444  psfmodel = PROFILE_GAUSSIAN;
2445  }
2446  else {
2447  return NULL;
2448  }
2449 
2450  nfibers = giraffe_psfdata_fibers(psfdata);
2451  nbins = giraffe_psfdata_bins(psfdata);
2452  nx = giraffe_psfdata_ysize(psfdata);
2453  ny = giraffe_psfdata_xsize(psfdata);
2454 
2455  center = giraffe_psfdata_get_data(psfdata, "Center");
2456  width = giraffe_psfdata_get_data(psfdata, "Width1");
2457  exponent = giraffe_psfdata_get_data(psfdata, "Width2");
2458 
2459  if (amplitude == NULL) {
2460  amplitude = giraffe_psfdata_get_data(psfdata, "Amplitude");
2461  }
2462  else {
2463  if ((cpl_image_get_size_x(amplitude) != nfibers) ||
2464  (cpl_image_get_size_y(amplitude) > nbins)) {
2465  return NULL;
2466  }
2467  }
2468 
2469  if (background == NULL) {
2470  background = giraffe_psfdata_get_data(psfdata, "Background");
2471  }
2472  else {
2473  if ((cpl_image_get_size_x(background) != nfibers) ||
2474  (cpl_image_get_size_y(background) > nbins)) {
2475  return NULL;
2476  }
2477  }
2478 
2479  bsize = (cxdouble)nx / (cxdouble)nbins;
2480 
2481  _nbins = cpl_image_get_size_y(amplitude);
2482  _bsize = (cxdouble)nx / (cxdouble)_nbins;
2483 
2484  mask = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
2485  _mask = cpl_image_get_data_double(mask);
2486 
2487  profile = giraffe_model_new(model);
2488 
2489  for (fiber = 0; fiber < nfibers; fiber++) {
2490 
2491  cxint ylower = 0;
2492  cxint yupper = ny;
2493 
2494  const cxdouble* _amplitude =
2495  cpl_image_get_data_double_const(amplitude);
2496  const cxdouble* _center =
2497  cpl_image_get_data_double_const(center);
2498  const cxdouble* _width =
2499  cpl_image_get_data_double_const(width);
2500  const cxdouble* _exponent =
2501  cpl_image_get_data_double_const(exponent);
2502 
2503  for (i = 0; i < nx; i++) {
2504 
2505  register cxint j = 0;
2506  register cxint k = 0;
2507  register cxint l = 0;
2508  register cxint bin = 0;
2509 
2510  register cxdouble a = 1.;
2511  register cxdouble b = 0.;
2512  register cxdouble c = 0.;
2513  register cxdouble s = 0.;
2514  register cxdouble e = 0.;
2515 
2516 
2517  bin = CX_MAX(0, CX_MIN((cxint)floor(i / bsize), nbins - 1));
2518  k = bin * nfibers + fiber;
2519 
2520  bin = CX_MAX(0, CX_MIN((cxint)floor(i / _bsize), _nbins - 1));
2521  l = bin * nfibers + fiber;
2522 
2523  a = _amplitude[l];
2524  c = _center[k];
2525  s = _width[k];
2526  e = _exponent[k];
2527 
2528  giraffe_model_set_parameter(profile, "Amplitude", a);
2529  giraffe_model_set_parameter(profile, "Background", b);
2530  giraffe_model_set_parameter(profile, "Center", c);
2531  giraffe_model_set_parameter(profile, "Width1", s);
2532  giraffe_model_set_parameter(profile, "Width2", e);
2533 
2534  switch (psfmodel) {
2535  case PROFILE_PSFEXP:
2536  {
2537  cxdouble w = pow(s * log(1. / cutoff), 1. / e);
2538 
2539  ylower = (cxint) floor(c - w);
2540  yupper = (cxint) ceil(c + w);
2541  }
2542  break;
2543 
2544  case PROFILE_PSFEXP2:
2545  {
2546  cxdouble w = s * pow(log(1. / cutoff), 1. / e);
2547 
2548  ylower = (cxint) floor(c - w);
2549  yupper = (cxint) ceil(c + w);
2550  }
2551  break;
2552 
2553  case PROFILE_GAUSSIAN:
2554  {
2555  cxdouble w = s * sqrt(log(1. / cutoff));
2556  ylower = (cxint) floor(c - w);
2557  yupper = (cxint) ceil(c + w);
2558  }
2559  break;
2560 
2561  default:
2562  gi_error("Unsupported PSF profile model encountered!");
2563  break;
2564  }
2565 
2566  ylower = CX_MAX(0, ylower);
2567  yupper = CX_MIN(ny, yupper);
2568 
2569  for (j = ylower; j < yupper; j++) {
2570 
2571  cxint status = 0;
2572 
2573  cxdouble value = 0.;
2574 
2575  // FIXME: Performance problem? Check this!
2576  //register cxdouble value =
2577  // a * exp(-pow(fabs(j - c) / s, e)) + b;
2578 
2579  giraffe_model_set_argument(profile, "x", j);
2580  giraffe_model_evaluate(profile, &value, &status);
2581 
2582  _mask[i * ny + j] += value;
2583 
2584  }
2585 
2586  }
2587 
2588  }
2589 
2590  giraffe_model_delete(profile);
2591  profile = NULL;
2592 
2593  return mask;
2594 
2595 }
2596 
2597 
2608 cxint
2609 giraffe_compute_fiber_profiles(GiLocalization* result, GiImage* image,
2610  GiTable* fibers, GiLocalization* master,
2611  GiImage* bpixel, GiPsfConfig* config)
2612 {
2613 
2614  const cxchar* const _func = "giraffe_compute_fiber_profiles";
2615 
2616  cxint i = 0;
2617  cxint status = 0;
2618  cxint nfibers = 0;
2619  cxint nframes = 1;
2620  cxint nbins = 0;
2621  cxint nx = 0;
2622  cxint ny = 0;
2623 
2624  cxdouble conad = 1.;
2625  cxdouble bias_ron = 0.;
2626  cxdouble bias_sigma = 0.;
2627  cxdouble dark_value = 0.;
2628 
2629  cx_string* s = NULL;
2630 
2631  cpl_table* _fibers = NULL;
2632  cpl_table* locc = NULL;
2633 
2634  cpl_matrix* my = NULL;
2635 
2636  cpl_image* _image = NULL;
2637  cpl_image* _variance = NULL;
2638  cpl_image* _locy = NULL;
2639  cpl_image* _locw = NULL;
2640  cpl_image* _bpixel = NULL;
2641 
2642  cpl_propertylist* properties = NULL;
2643 
2644  GiModel* psfmodel = NULL;
2645 
2646  GiPsfData* psfdata = NULL;
2647  GiPsfData* psffit = NULL;
2648 
2649  GiMaskPosition positions = {GIMASK_FITTED_DATA, NULL, NULL};
2650  GiMaskPosition coeffs = {GIMASK_FIT_COEFFS, NULL, NULL};
2651 
2652  GiPsfParams psf_setup = {0, 0, 1000., FALSE};
2653 
2654 
2655  if ((result == NULL) || (image == NULL) || (fibers == NULL) ||
2656  (master == NULL) || (config == NULL)) {
2657  cpl_error_set(_func, CPL_ERROR_NULL_INPUT);
2658  return 1;
2659  }
2660 
2661  if ((master->locy == NULL) || (master->locw == NULL)) {
2662  cpl_error_set(_func, CPL_ERROR_NULL_INPUT);
2663  return 1;
2664  }
2665 
2666  if ((result->locy != NULL) || (result->locw != NULL) ||
2667  (result->locc != NULL) || (result->psf != NULL)) {
2668  cpl_error_set(_func, CPL_ERROR_ILLEGAL_INPUT);
2669  return 1;
2670  }
2671 
2672  _image = giraffe_image_get(image);
2673  _locy = giraffe_image_get(master->locy);
2674  _locw = giraffe_image_get(master->locw);
2675 
2676  if (bpixel != NULL) {
2677  _bpixel = giraffe_image_get(bpixel);
2678  }
2679 
2680  _fibers = giraffe_table_get(fibers);
2681 
2682  if (_fibers == NULL) {
2683  cpl_error_set(_func, CPL_ERROR_DATA_NOT_FOUND);
2684  return 1;
2685  }
2686 
2687  nfibers = cpl_table_get_nrow(_fibers);
2688 
2689  nx = cpl_image_get_size_y(_image);
2690  ny = cpl_image_get_size_x(_image);
2691 
2692  nbins = (cxint) ceil(nx / config->binsize);
2693 
2694 
2695  /*
2696  * Get raw image properties.
2697  */
2698 
2699  properties = giraffe_image_get_properties(image);
2700 
2701  if (cpl_propertylist_has(properties, GIALIAS_NFIBERS) == FALSE) {
2702  cpl_propertylist_append_int(properties, GIALIAS_NFIBERS, nfibers);
2703  cpl_propertylist_set_comment(properties, GIALIAS_NFIBERS,
2704  "Number of fibres");
2705  }
2706  else {
2707 
2708  cxint _nfibers = cpl_propertylist_get_int(properties,
2709  GIALIAS_NFIBERS);
2710 
2711  if (nfibers != _nfibers) {
2712  cpl_error_set(_func, CPL_ERROR_ILLEGAL_INPUT);
2713  return 1;
2714  }
2715 
2716  }
2717 
2718 
2719  if (!cpl_propertylist_has(properties, GIALIAS_CONAD)) {
2720  cpl_msg_error(_func, "Missing detector gain property (%s)! ",
2721  GIALIAS_CONAD);
2722  return 1;
2723  }
2724  else {
2725  conad = cpl_propertylist_get_double(properties, GIALIAS_CONAD);
2726  }
2727 
2728 
2729  if (!cpl_propertylist_has(properties, GIALIAS_BIASERROR)) {
2730  cpl_msg_warning(_func, "Missing bias error property (%s)! Setting "
2731  "bias error to 0.", GIALIAS_BIASERROR);
2732  bias_sigma = 0.;
2733  }
2734  else {
2735  bias_sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASERROR);
2736  }
2737 
2738 
2739  giraffe_error_push();
2740 
2741  bias_ron = giraffe_propertylist_get_ron(properties);
2742 
2743  if (cpl_error_get_code() != CPL_ERROR_NONE) {
2744  return 1;
2745  }
2746 
2747  giraffe_error_pop();
2748 
2749 
2750  if (cpl_propertylist_has(properties, GIALIAS_DARKVALUE) == FALSE) {
2751  cpl_msg_warning(_func, "Missing dark value property (%s) will be "
2752  "set to %.2f!", GIALIAS_DARKVALUE, dark_value);
2753  cpl_propertylist_append_double(properties, GIALIAS_DARKVALUE,
2754  dark_value);
2755  }
2756  else {
2757  dark_value = cpl_propertylist_get_double(properties,
2758  GIALIAS_DARKVALUE);
2759  }
2760 
2761 
2762  if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
2763  nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
2764  }
2765 
2766 
2767  /*
2768  * Convert the bias and dark errors from ADU to electrons.
2769  */
2770 
2771  bias_sigma *= conad;
2772  dark_value *= conad;
2773 
2774 
2775  /*
2776  * Prepare the input image and the variance image for the profile fitting.
2777  */
2778 
2779  giraffe_error_push();
2780 
2781  _image = cpl_image_multiply_scalar_create(_image, nframes * conad);
2782  _variance = cpl_image_abs_create(_image);
2783 
2784  cpl_image_add_scalar(_variance,
2785  nframes * (bias_ron * bias_ron + nframes *
2786  (bias_sigma * bias_sigma + dark_value * dark_value)));
2787 
2788  if (cpl_error_get_code() != CPL_ERROR_NONE) {
2789  if (_variance != NULL) {
2790  cpl_image_delete(_variance);
2791  _variance = NULL;
2792  }
2793 
2794  cpl_image_delete(_image);
2795  _image = NULL;
2796 
2797  return 1;
2798  }
2799 
2800  giraffe_error_pop();
2801 
2802 
2803  /*
2804  * Initialize PSF profile model.
2805  */
2806 
2807  psfmodel = giraffe_model_new(config->profile);
2808 
2809  giraffe_model_thaw(psfmodel);
2810 
2811  giraffe_model_set_parameter(psfmodel, "Amplitude", 1.);
2812  giraffe_model_set_parameter(psfmodel, "Background", 0.);
2813  giraffe_model_set_parameter(psfmodel, "Center", 0.);
2814  giraffe_model_set_parameter(psfmodel, "Width1", config->width);
2815 
2816  if (cx_strncasecmp(config->profile, "psfexp", 6) == 0) {
2817 
2818  cxdouble _exponent = fabs(config->exponent);
2819 
2820  giraffe_model_set_parameter(psfmodel, "Width2", _exponent);
2821 
2822  if (config->exponent > 0) {
2823  giraffe_model_freeze_parameter(psfmodel, "Width2");
2824  }
2825 
2826  }
2827 
2828  giraffe_model_set_iterations(psfmodel, config->fit.iterations);
2829  giraffe_model_set_delta(psfmodel, config->fit.delta);
2830  giraffe_model_set_tests(psfmodel, config->fit.tests);
2831 
2832 
2833  /*
2834  * Fit a PSF profile model to each fiber and compute the profile
2835  * parameters.
2836  */
2837 
2838  cpl_msg_info(_func, "Fitting fiber profiles ...");
2839 
2840  psf_setup.bsize = config->binsize;
2841  psf_setup.mwidth = config->maxwidth;
2842  psf_setup.normalize = config->normalize;
2843 
2844  psfdata = giraffe_psfdata_create(nfibers, nbins, ny, nx);
2845 
2846  status = _giraffe_psf_compute_profile(psfdata, _image, _variance, _locy,
2847  _locw, _fibers, _bpixel, psfmodel,
2848  &psf_setup);
2849 
2850  cpl_image_delete(_image);
2851  _image = NULL;
2852 
2853  if (status != 0) {
2854  giraffe_psfdata_delete(psfdata);
2855  psfdata= NULL;
2856 
2857  giraffe_model_delete(psfmodel);
2858  psfmodel = NULL;
2859 
2860  cpl_image_delete(_variance);
2861  _variance = NULL;
2862 
2863  cpl_msg_error(_func, "Fiber profile fit failed!");
2864 
2865  return 2;
2866  }
2867 
2868 
2869  /*
2870  * Scale the computed profiles to the level of the actual, average
2871  * input frame.
2872  */
2873 
2874  _image = (cpl_image*) giraffe_psfdata_get_data(psfdata, "Amplitude");
2875  cpl_image_divide_scalar(_image, nframes * conad);
2876 
2877  _image = (cpl_image*) giraffe_psfdata_get_data(psfdata, "Background");
2878  cpl_image_divide_scalar(_image, nframes * conad);
2879 
2880  _image = NULL;
2881 
2882 
2883  /*
2884  * Fit a polynomial model to each PSF profile parameter if
2885  * it was requested.
2886  */
2887 
2888  cpl_msg_info(_func, "Fitting PSF profile parameters ...");
2889 
2890  if (config->parameter_fit == TRUE) {
2891 
2892  const cxchar* parameters[] = {"Center", "Amplitude", "Background",
2893  "Width1", "Width2", NULL};
2894 
2895  psffit = _giraffe_psf_fit_parameters(psfdata, _fibers, parameters,
2896  config->yorder, config->worder,
2897  &config->clip);
2898 
2899  }
2900  else {
2901 
2902  psffit = _giraffe_psf_fit_parameters1d(psfdata, _fibers,
2903  NULL, config->yorder,
2904  &config->clip);
2905 
2906  }
2907 
2908  if (psffit == NULL) {
2909  giraffe_psfdata_delete(psfdata);
2910  psfdata= NULL;
2911 
2912  giraffe_model_delete(psfmodel);
2913  psfmodel = NULL;
2914 
2915  cpl_image_delete(_variance);
2916  _variance = NULL;
2917 
2918  cpl_msg_error(_func, "PSF parameter fit failed!");
2919  return 3;
2920  }
2921 
2922  giraffe_model_delete(psfmodel);
2923  psfmodel = NULL;
2924 
2925  cpl_image_delete(_variance);
2926  _variance = NULL;
2927 
2928 
2929  /*
2930  * Compute a fiber localization mask from the fitted fiber profiles.
2931  */
2932 
2933  positions.my = cpl_matrix_new(nx, nfibers);
2934  positions.mw = cpl_matrix_new(nx, nfibers);
2935 
2936  coeffs.my = cpl_matrix_new(config->yorder + 1, nfibers);
2937  coeffs.mw = cpl_matrix_new(config->worder + 1, config->worder + 1);
2938 
2939  status = _giraffe_psf_compute_mask(&positions, &coeffs, psfdata, _fibers,
2940  config->yorder, config->worder,
2941  &config->clip);
2942 
2943  if (status != 0) {
2944 
2945  giraffe_psfdata_delete(psffit);
2946  psffit = NULL;
2947 
2948  giraffe_psfdata_delete(psfdata);
2949  psfdata= NULL;
2950 
2951  cpl_msg_error(_func, "Computation of localization mask from "
2952  "the fiber profile failed!");
2953 
2954  return 4;
2955  }
2956 
2957  giraffe_psfdata_delete(psfdata);
2958  psfdata= NULL;
2959 
2960 
2961  /*
2962  * Fill the results object. Convert the matrices to images and tables
2963  * and add the necessary properties.
2964  */
2965 
2966  properties = giraffe_image_get_properties(image);
2967 
2968  cpl_propertylist_update_string(properties, GIALIAS_PSFMODEL,
2969  config->profile);
2970  cpl_propertylist_set_comment(properties, GIALIAS_PSFMODEL,
2971  "PSF profile model identifier");
2972 
2973  cpl_propertylist_update_int(properties, GIALIAS_PSFXBINS,
2974  config->binsize);
2975  cpl_propertylist_set_comment(properties, GIALIAS_PSFXBINS,
2976  "Size of bins along the dispersion "
2977  "direction.");
2978 
2979  cpl_propertylist_update_int(properties, GIALIAS_PSFYDEG,
2980  config->yorder);
2981  cpl_propertylist_set_comment(properties, GIALIAS_PSFYDEG,
2982  "Order of the fiber center polynomial "
2983  "model.");
2984 
2985  cpl_propertylist_update_int(properties, GIALIAS_PSFWDEG,
2986  config->worder);
2987  cpl_propertylist_set_comment(properties, GIALIAS_PSFWDEG,
2988  "Order of the fiber width 2d polynomial "
2989  "model.");
2990 
2991  cpl_propertylist_update_int(properties, GIALIAS_PSFWDEG,
2992  config->worder);
2993  cpl_propertylist_set_comment(properties, GIALIAS_PSFWDEG,
2994  "Order of the fiber width 2d polynomial "
2995  "model.");
2996 
2997  cpl_propertylist_update_bool(properties, GIALIAS_PSFNORM,
2998  config->normalize);
2999  cpl_propertylist_set_comment(properties, GIALIAS_PSFNORM,
3000  "Pixel value normalization.");
3001 
3002  cpl_propertylist_update_int(properties, GIALIAS_PSFNX,
3003  cpl_matrix_get_nrow(positions.my));
3004  cpl_propertylist_set_comment(properties, GIALIAS_PSFNX,
3005  "Number of pixels per spectrum.");
3006 
3007  cpl_propertylist_update_int(properties, GIALIAS_PSFNS,
3008  cpl_matrix_get_ncol(positions.my));
3009  cpl_propertylist_set_comment(properties, GIALIAS_PSFNS,
3010  "Number of detected fibers.");
3011 
3012  cpl_propertylist_update_double(properties, GIALIAS_PSFSIGMA,
3013  config->clip.level);
3014  cpl_propertylist_set_comment(properties, GIALIAS_PSFSIGMA,
3015  "Sigma multiplier used for the PSF "
3016  "parmeter fit.");
3017 
3018  cpl_propertylist_update_double(properties, GIALIAS_PSFSIGMA,
3019  config->clip.level);
3020  cpl_propertylist_set_comment(properties, GIALIAS_PSFSIGMA,
3021  "Sigma multiplier used for the fit of PSF "
3022  "parameters.");
3023 
3024  cpl_propertylist_update_int(properties, GIALIAS_PSFNITER,
3025  config->clip.iterations);
3026  cpl_propertylist_set_comment(properties, GIALIAS_PSFNITER,
3027  "Number of iterations used for the fit "
3028  "of PSF parameters.");
3029 
3030  cpl_propertylist_update_double(properties, GIALIAS_PSFMFRAC,
3031  config->clip.fraction);
3032  cpl_propertylist_set_comment(properties, GIALIAS_PSFMFRAC,
3033  "Minimum allowed fraction of accepted "
3034  "over total data points used for the "
3035  "fit of PSF parameters.");
3036 
3037 
3038  /* Fiber profile center position */
3039 
3040  result->locy = giraffe_image_create(CPL_TYPE_DOUBLE,
3041  cpl_matrix_get_ncol(positions.my),
3042  cpl_matrix_get_nrow(positions.my));
3043  giraffe_image_copy_matrix(result->locy, positions.my);
3044 
3045  cpl_matrix_delete(positions.my);
3046  positions.my = NULL;
3047 
3048  giraffe_image_set_properties(result->locy, properties);
3049  properties = giraffe_image_get_properties(result->locy);
3050 
3051  cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE, "LOCY");
3052  cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3053  "GIRAFFE localization centroid");
3054 
3055 
3056  /* Fiber profile widths */
3057 
3058  result->locw = giraffe_image_create(CPL_TYPE_DOUBLE,
3059  cpl_matrix_get_ncol(positions.mw),
3060  cpl_matrix_get_nrow(positions.mw));
3061  giraffe_image_copy_matrix(result->locw, positions.mw);
3062 
3063  cpl_matrix_delete(positions.mw);
3064  positions.mw = NULL;
3065 
3066  properties = giraffe_image_get_properties(result->locy);
3067 
3068  giraffe_image_set_properties(result->locw, properties);
3069  properties = giraffe_image_get_properties(result->locw);
3070 
3071  cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE, "LOCWY");
3072  cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3073  "GIRAFFE localization half-width");
3074 
3075 
3076  /* Fiber polynomial model coefficients table */
3077 
3078  locc = cpl_table_new(cpl_matrix_get_ncol(coeffs.my));
3079 
3080  cpl_table_new_column(locc, "BUTTON", CPL_TYPE_INT);
3081  for (i = 0; i < cpl_table_get_nrow(locc); i++) {
3082  cpl_table_set_int(locc, "BUTTON", i, i);
3083  }
3084 
3085  for (i = 0; i < cpl_matrix_get_nrow(coeffs.my); i++) {
3086 
3087  cxchar* label = NULL;
3088 
3089  cx_asprintf(&label, "YC%d", i);
3090  cpl_table_new_column(locc, label, CPL_TYPE_DOUBLE);
3091  cx_free(label);
3092 
3093  }
3094 
3095  properties = giraffe_image_get_properties(result->locy);
3096 
3097  result->locc = giraffe_table_create(locc, properties);
3098  properties = giraffe_table_get_properties(result->locc);
3099 
3100  cpl_table_delete(locc);
3101  locc = NULL;
3102 
3103  my = cpl_matrix_transpose_create(coeffs.my);
3104  giraffe_table_copy_matrix(result->locc, "YC0", my);
3105 
3106  cpl_matrix_delete(my);
3107  my = NULL;
3108 
3109  cpl_matrix_delete(coeffs.my);
3110  coeffs.my = NULL;
3111 
3112 
3113  /* Add coefficients of the 2D fit of the fiber widths as properties */
3114 
3115  s = cx_string_new();
3116 
3117  for (i = 0; i < cpl_matrix_get_ncol(coeffs.mw); i++) {
3118  cx_string_sprintf(s, "%s%d", GIALIAS_LOCWIDCOEF, i);
3119  cpl_propertylist_update_double(properties, cx_string_get(s),
3120  cpl_matrix_get(coeffs.mw, 0, i));
3121  }
3122 
3123  cx_string_delete(s);
3124  s = NULL;
3125 
3126  cpl_matrix_delete(coeffs.mw);
3127  coeffs.mw = NULL;
3128 
3129  cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
3130  "LOCYWCHEB");
3131  cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3132  "GIRAFFE localization fit coefficients");
3133 
3134 
3135  /* Fiber profile PSF parameters */
3136 
3137  if (psffit != NULL) {
3138  result->psf = psffit;
3139  }
3140 
3141  return 0;
3142 
3143 }
3144 
3145 
3156 GiPsfConfig*
3157 giraffe_psf_config_create(cpl_parameterlist* list)
3158 {
3159 
3160  cpl_parameter *p;
3161 
3162  GiPsfConfig *self = NULL;
3163 
3164 
3165  if (list == NULL) {
3166  return NULL;
3167  }
3168 
3169  self = cx_calloc(1, sizeof *self);
3170 
3171  p = cpl_parameterlist_find(list, "giraffe.psf.model");
3172  self->profile = cx_strdup(cpl_parameter_get_string(p));
3173 
3174  if (cx_strncasecmp(self->profile, "psfexp", 6) == 0) {
3175  self->width = 16.;
3176  }
3177  else {
3178  self->width = 4.;
3179  }
3180 
3181  p = cpl_parameterlist_find(list, "giraffe.psf.binsize");
3182  self->binsize = cpl_parameter_get_int(p);
3183 
3184  if (self->binsize < 1) {
3185  self->binsize = 1;
3186  }
3187 
3188  p = cpl_parameterlist_find(list, "giraffe.psf.maxwidth");
3189  self->maxwidth = cpl_parameter_get_double(p);
3190 
3191  if (self->width > 0.) {
3192  p = cpl_parameterlist_find(list, "giraffe.psf.width");
3193  self->width = cpl_parameter_get_double(p);
3194  }
3195 
3196  if (self->width > self->maxwidth) {
3197  self->width = self->maxwidth;
3198  }
3199 
3200  p = cpl_parameterlist_find(list, "giraffe.psf.exponent");
3201  self->exponent = cpl_parameter_get_double(p);
3202 
3203  p = cpl_parameterlist_find(list, "giraffe.psf.normalize");
3204  self->normalize = cpl_parameter_get_bool(p);
3205 
3206  p = cpl_parameterlist_find(list, "giraffe.psf.profile.iterations");
3207  self->fit.iterations = cpl_parameter_get_int(p);
3208 
3209  p = cpl_parameterlist_find(list, "giraffe.psf.profile.tests");
3210  self->fit.tests = cpl_parameter_get_int(p);
3211 
3212  p = cpl_parameterlist_find(list, "giraffe.psf.profile.dchisquare");
3213  self->fit.delta = cpl_parameter_get_double(p);
3214 
3215  p = cpl_parameterlist_find(list, "giraffe.psf.parameters.fit");
3216  self->parameter_fit = cpl_parameter_get_bool(p);
3217 
3218  p = cpl_parameterlist_find(list, "giraffe.psf.parameters.yorder");
3219  self->yorder = cpl_parameter_get_int(p);
3220 
3221  if (self->yorder < 0) {
3223  self = NULL;
3224  }
3225 
3226  p = cpl_parameterlist_find(list, "giraffe.psf.parameters.worder");
3227  self->worder = cpl_parameter_get_int(p);
3228 
3229  if (self->worder < 0) {
3231  self = NULL;
3232  }
3233 
3234  p = cpl_parameterlist_find(list, "giraffe.psf.parameters.sigma");
3235  self->clip.level = cpl_parameter_get_double(p);
3236 
3237  p = cpl_parameterlist_find(list, "giraffe.psf.parameters.iterations");
3238  self->clip.iterations = cpl_parameter_get_int(p);
3239 
3240  p = cpl_parameterlist_find(list, "giraffe.psf.parameters.fraction");
3241  self->clip.fraction = cpl_parameter_get_double(p);
3242 
3243  return self;
3244 
3245 }
3246 
3247 
3260 void
3261 giraffe_psf_config_destroy(GiPsfConfig* self)
3262 {
3263 
3264  if (self != NULL) {
3265  if (self->profile != NULL) {
3266  cx_free((cxptr) self->profile);
3267  self->profile = NULL;
3268  }
3269 
3270  cx_free(self);
3271  }
3272 
3273  return;
3274 
3275 }
3276 
3277 
3289 void
3290 giraffe_psf_config_add(cpl_parameterlist* list)
3291 {
3292 
3293  cpl_parameter* p = NULL;
3294 
3295 
3296  if (list == NULL) {
3297  return;
3298  }
3299 
3300  p = cpl_parameter_new_enum("giraffe.psf.model",
3301  CPL_TYPE_STRING,
3302  "PSF profile model: `psfexp', `psfexp2'",
3303  "giraffe.psf",
3304  "psfexp2", 3, "psfexp", "psfexp2", "gaussian");
3305  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-model");
3306  cpl_parameterlist_append(list, p);
3307 
3308  p = cpl_parameter_new_value("giraffe.psf.normalize",
3309  CPL_TYPE_BOOL,
3310  "Use normalized pixel values.",
3311  "giraffe.psf",
3312  FALSE);
3313  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-norm");
3314  cpl_parameterlist_append(list, p);
3315 
3316 
3317  p = cpl_parameter_new_value("giraffe.psf.binsize",
3318  CPL_TYPE_INT,
3319  "Size of bin along dispersion axis",
3320  "giraffe.psf",
3321  64);
3322  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-binsize");
3323  cpl_parameterlist_append(list, p);
3324 
3325 
3326  p = cpl_parameter_new_value("giraffe.psf.maxwidth",
3327  CPL_TYPE_DOUBLE,
3328  "Maximum width of the PSF profile.",
3329  "giraffe.psf",
3330  16.);
3331  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-maxwidth");
3332  cpl_parameterlist_append(list, p);
3333 
3334 
3335  p = cpl_parameter_new_value("giraffe.psf.width",
3336  CPL_TYPE_DOUBLE,
3337  "Initial width of the PSF profile.",
3338  "giraffe.psf",
3339  0.);
3340  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-width");
3341  cpl_parameterlist_append(list, p);
3342 
3343 
3344  p = cpl_parameter_new_value("giraffe.psf.exponent",
3345  CPL_TYPE_DOUBLE,
3346  "Exponent of the exponential PSF profile "
3347  "(will not be fitted if > 0).",
3348  "giraffe.psf",
3349  -3.);
3350  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-exponent");
3351  cpl_parameterlist_append(list, p);
3352 
3353 
3354  p = cpl_parameter_new_value("giraffe.psf.profile.iterations",
3355  CPL_TYPE_INT,
3356  "Maximum number of iterations used for "
3357  "the fit of the fiber PSF profile.",
3358  "giraffe.psf",
3359  120);
3360 
3361  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfniter");
3362  cpl_parameterlist_append(list, p);
3363 
3364 
3365  p = cpl_parameter_new_value("giraffe.psf.profile.tests",
3366  CPL_TYPE_INT,
3367  "Maximum number of tests used for the fit "
3368  "of the fiber PSF profile",
3369  "giraffe.psf",
3370  7);
3371 
3372  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfntest");
3373  cpl_parameterlist_append(list, p);
3374 
3375 
3376  p = cpl_parameter_new_value("giraffe.psf.profile.dchisquare",
3377  CPL_TYPE_DOUBLE,
3378  "Minimum chi-square difference used for the "
3379  "fit of the fiber PSF profile.",
3380  "giraffe.psf",
3381  0.001);
3382 
3383  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfdchisq");
3384  cpl_parameterlist_append(list, p);
3385 
3386 
3387  p = cpl_parameter_new_value("giraffe.psf.parameters.fit",
3388  CPL_TYPE_BOOL,
3389  "2D fit of the PSF profile parameters "
3390  "using a Chebyshev polynomial model.",
3391  "giraffe.psf",
3392  FALSE);
3393  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-prmfit");
3394  cpl_parameterlist_append(list, p);
3395 
3396 
3397  p = cpl_parameter_new_value("giraffe.psf.parameters.yorder",
3398  CPL_TYPE_INT,
3399  "Order of Chebyshev polynomial fit.",
3400  "giraffe.psf",
3401  4);
3402  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-yorder");
3403  cpl_parameterlist_append(list, p);
3404 
3405 
3406  p = cpl_parameter_new_value("giraffe.psf.parameters.worder",
3407  CPL_TYPE_INT,
3408  "Order of Chebyshev 2D polynomial fit.",
3409  "giraffe.psf",
3410  4);
3411  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-worder");
3412  cpl_parameterlist_append(list, p);
3413 
3414 
3415  p = cpl_parameter_new_value("giraffe.psf.parameters.sigma",
3416  CPL_TYPE_DOUBLE,
3417  "PSF parameter fitting: sigma threshold "
3418  "factor",
3419  "giraffe.psf",
3420  3.5);
3421  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-sigma");
3422  cpl_parameterlist_append(list, p);
3423 
3424 
3425  p = cpl_parameter_new_value("giraffe.psf.parameters.iterations",
3426  CPL_TYPE_INT,
3427  "PSF parameter fitting: number of "
3428  "iterations",
3429  "giraffe.psf",
3430  10);
3431  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-niter");
3432  cpl_parameterlist_append(list, p);
3433 
3434 
3435  p = cpl_parameter_new_range("giraffe.psf.parameters.fraction",
3436  CPL_TYPE_DOUBLE,
3437  "PSF parameter fitting: minimum fraction "
3438  "of points accepted/total.",
3439  "giraffe.psf",
3440  0.8, 0.0, 1.0);
3441  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-mfrac");
3442  cpl_parameterlist_append(list, p);
3443 
3444  return;
3445 
3446 }

This file is part of the GIRAFFE Pipeline Reference Manual 2.12.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Mon Mar 24 2014 11:43:52 by doxygen 1.8.2 written by Dimitri van Heesch, © 1997-2004