GIRAFFE Pipeline Reference Manual

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

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