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
62enum GiProfileId {
63 PROFILE_PSFEXP = 1 << 1,
64 PROFILE_PSFEXP2 = 1 << 2,
65 PROFILE_GAUSSIAN = 1 << 3
66};
67
68typedef enum GiProfileId GiProfileId;
69
70
71struct 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
78typedef struct GiPsfParams GiPsfParams;
79
80
81struct GiPsfBin {
82 cxdouble zmin;
83 cxdouble zmax;
84 cxdouble xcenter;
85 cxdouble ycenter;
86 cxdouble ywidth;
87};
88
89typedef struct GiPsfBin GiPsfBin;
90
91struct GiPsfParameterFit {
92 cpl_image* fit;
93 cpl_matrix* coeffs;
94};
95
96typedef 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
130inline 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
446inline 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
836inline 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
855 cxsize n = 0;
856
857 cxdouble exponent; /* PSF profile exponent initial guess */
858
859 cpl_matrix* mx = NULL;
860 cpl_matrix* my = NULL;
861 cpl_matrix* ms = NULL;
862
863 cpl_image* zx = NULL;
864 cpl_image* zv = NULL;
865
866 GiProfileId psfmodel = 0;
867
868
869 cx_assert(result != NULL);
870 cx_assert((zraw != NULL) && (zvar != NULL));
871 cx_assert((locy != NULL) && (locw != NULL));
872 cx_assert(fibers != NULL);
873 cx_assert(profile != NULL);
874 cx_assert(config != NULL);
875
876 cx_assert(cpl_image_get_size_x(zraw) == cpl_image_get_size_x(zvar));
877 cx_assert(cpl_image_get_size_y(zraw) == cpl_image_get_size_y(zvar));
878
879 cx_assert(cpl_image_get_size_x(locy) == cpl_image_get_size_x(locw));
880 cx_assert(cpl_image_get_size_y(locy) == cpl_image_get_size_y(locw));
881
882
883 nx = cpl_image_get_size_y(zraw);
884 ny = cpl_image_get_size_x(zraw);
885 ns = cpl_table_get_nrow(fibers);
886
887 nbins = (cxint) giraffe_psfdata_bins(result);
888
889 if (ns != cpl_image_get_size_x(locy)) {
890 return -1;
891 }
892
893 if ((bpm != NULL) && (cpl_image_get_type(bpm) != CPL_TYPE_INT)) {
894 return -2;
895 }
896
897 if (giraffe_psfdata_fibers(result) != (cxsize) ns) {
898 return -3;
899 }
900
901 if ((giraffe_psfdata_xsize(result) != (cxsize) ny) ||
902 (giraffe_psfdata_ysize(result) != (cxsize) nx)) {
903 return -3;
904 }
905
906
907 /*
908 * Check the model type. Only the line models "psfexp", "psfexp2" and
909 * "gaussian" are supported.
910 */
911
912 model = giraffe_model_get_name(profile);
913
914 if (strcmp(model, "psfexp") == 0) {
915 psfmodel = PROFILE_PSFEXP;
916 }
917 else if (strcmp(model, "psfexp2") == 0) {
918 psfmodel = PROFILE_PSFEXP2;
919 }
920 else if (strcmp(model, "gaussian") == 0) {
921 psfmodel = PROFILE_GAUSSIAN;
922 }
923 else {
924 return -4;
925 }
926
927
928 if (config->normalize != FALSE) {
929
930 cxint x = 0;
931
932 cxdouble zmax = 0.;
933 cxdouble* zsum = cx_calloc(nx, sizeof(cxdouble));
934
935
936 /*
937 * Find maximum pixel value, taking bad pixels into account if the
938 * bad pixel map is present.
939 */
940
941 if (bpm == NULL) {
942
943 for (x = 0; x < nx; x++) {
944
945 register cxint y = 0;
946
947 register const cxdouble* _zraw =
948 cpl_image_get_data_double(zraw);
949
950
951 for (y = 0; y < ny; y++) {
952 zsum[x] += _zraw[x * ny + y];
953 }
954
955 if (zsum[x] > zmax) {
956 zmax = zsum[x];
957 }
958 }
959
960 }
961 else {
962
963 for (x = 0; x < nx; x++) {
964
965 register cxint y = 0;
966 register const cxint* _bpm = cpl_image_get_data_int(bpm);
967
968 register const cxdouble* _zraw =
969 cpl_image_get_data_double(zraw);
970
971
972 for (y = 0; y < ny; y++) {
973 register cxint i = x * ny + y;
974
975 if (_bpm[i] == 0) {
976 zsum[x] += _zraw[i];
977 }
978 }
979
980 if (zsum[x] > zmax) {
981 zmax = zsum[x];
982 }
983 }
984
985 }
986
987
988 /*
989 * Allocate the buffers for the normalized images and scale
990 * the fiber spectrum fluxes and errors.
991 */
992
993 zx = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
994 zv = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
995
996
997 for (x = 0; x < nx; x++) {
998
999 register cxint y = 0;
1000
1001 register cxdouble scale = zmax / zsum[x];
1002 register const cxdouble* _zraw = cpl_image_get_data_double(zraw);
1003 register const cxdouble* _zvar = cpl_image_get_data_double(zvar);
1004 register cxdouble* _zx = cpl_image_get_data_double(zx);
1005 register cxdouble* _zv = cpl_image_get_data_double(zv);
1006
1007 for(y = 0; y < nx; y++) {
1008 register cxint i = x * ny + y;
1009
1010 _zx[i] = _zraw[i] * scale;
1011 _zv[i] = _zvar[i] * scale;
1012 }
1013
1014 }
1015
1016 cx_free(zsum);
1017 zsum = NULL;
1018
1019 }
1020 else {
1021 zx = zraw;
1022 zv = zvar;
1023 }
1024
1025
1026 /*
1027 * Save the initial values of the profile models exponent parameter,
1028 * since it must be reset after each bin has been fitted.
1029 */
1030
1031 giraffe_error_push();
1032
1033 exponent = giraffe_model_get_parameter(profile, "Width2");
1034
1035 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1036 exponent = 0.;
1037 }
1038
1039 giraffe_error_pop();
1040
1041
1042 /*
1043 * Get the calibration spectrum reference index column from the
1044 * fiber list.
1045 */
1046
1047 ridx = giraffe_fiberlist_query_index(fibers);
1048
1049
1050 /*
1051 * Allocate buffers for the profile data points and their errors. The
1052 * buffer size is choosen to be large enough for the number of bins of
1053 * requested psf data object and the given maximum fiber width.
1054 */
1055
1056 mx = cpl_matrix_new(nbins * config->mwidth, 1);
1057 my = cpl_matrix_new(nbins * config->mwidth, 1);
1058 ms = cpl_matrix_new(nbins * config->mwidth, 1);
1059
1060 if ((mx == NULL) || (my == NULL) || (ms == NULL)) {
1061 if (config->normalize == TRUE) {
1062 cpl_image_delete(zx);
1063 zx = NULL;
1064
1065 cpl_image_delete(zv);
1066 zv = NULL;
1067 }
1068
1069 if (mx != NULL) {
1070 cpl_matrix_delete(mx);
1071 mx = NULL;
1072 }
1073
1074 if (my != NULL) {
1075 cpl_matrix_delete(my);
1076 my = NULL;
1077 }
1078
1079 if (ms != NULL) {
1080 cpl_matrix_delete(ms);
1081 ms = NULL;
1082 }
1083
1084 return 1;
1085 }
1086
1087
1088 /*
1089 * Allocate the buffers of the results structure here, to avoid
1090 * complicated error handling in the nested loops.
1091 */
1092
1093 giraffe_psfdata_set_model(result, giraffe_model_get_name(profile));
1094
1095 for (n = 0; n < giraffe_model_count_parameters(profile); n++) {
1096
1097 const cxchar* name = giraffe_model_parameter_name(profile, n);
1098
1099 cpl_image* values = cpl_image_new(ns, nbins, CPL_TYPE_DOUBLE);
1100
1101 if ((name == NULL) || (values == NULL)) {
1102
1103 giraffe_psfdata_clear(result);
1104
1105 cpl_matrix_delete(mx);
1106 mx = NULL;
1107
1108 cpl_matrix_delete(my);
1109 my = NULL;
1110
1111 cpl_matrix_delete(ms);
1112 ms = NULL;
1113
1114 if (config->normalize == TRUE) {
1115 cpl_image_delete(zx);
1116 zx = NULL;
1117
1118 cpl_image_delete(zv);
1119 zv = NULL;
1120 }
1121
1122 return 1;
1123 }
1124
1125 giraffe_psfdata_set_data(result, name, values);
1126
1127 }
1128
1129
1130 /*
1131 * Loop over all available fibers
1132 */
1133
1134 for (fiber = 0; fiber < ns; fiber++) {
1135
1136 cxint x = 0;
1137 cxint bin = 0;
1138 cxint cs = cpl_table_get_int(fibers, ridx, fiber, NULL) - 1;
1139 const cxint* _bpm = NULL;
1140
1141 const cxdouble* _locy = cpl_image_get_data_double(locy);
1142 const cxdouble* _locw = cpl_image_get_data_double(locw);
1143 const cxdouble* _zx = cpl_image_get_data_double(zx);
1144 const cxdouble* _zv = cpl_image_get_data_double(zv);
1145
1146
1147 if (bpm != NULL) {
1148 _bpm = cpl_image_get_data_int(bpm);
1149 }
1150
1151
1152 /*
1153 * Fit a profile for each bin
1154 */
1155
1156 for (x = 0, bin = 0; x < nx; x += config->bsize, bin++) {
1157
1158 register cxint k = 0;
1159 register cxint xx = 0;
1160
1161 cxint status = 0;
1162 cxint ndata = 0;
1163 cxint iterations = giraffe_model_get_iterations(profile);
1164
1165 cxdouble amplitude = 0.;
1166 cxdouble bckground = 0.;
1167 cxdouble center = 0.;
1168 cxdouble width1 = 0.;
1169 cxdouble width2 = 0.;
1170
1171 GiPsfBin xbin = {0., 0., 0., 0., 0.};
1172
1173
1174 /*
1175 * Loop over each element of this bin
1176 */
1177
1178 for (k = 0, xx = x; (k < config->bsize) && (xx < nx); k++, xx++) {
1179
1180 register cxint y = 0;
1181 register cxint l = xx * ns + cs;
1182 register cxint m = xx * ny;
1183
1184 cxdouble zxmin = CX_MAXDOUBLE;
1185 cxdouble zxmax = 0.;
1186 cxdouble swidth = CX_MIN(_locw[l], config->mwidth);
1187 cxdouble ylo = (cxint) floor(_locy[l] - swidth);
1188 cxdouble yup = (cxint) ceil(_locy[l] + swidth);
1189 cxdouble ycenter = _locy[l];
1190
1191
1192 ylo = CX_MAX(0., ylo);
1193 yup = CX_MIN(ny, yup);
1194
1195 if (_bpm == NULL) {
1196
1197 for (y = ylo; y < yup; y++) {
1198
1199 register cxint i = m + y;
1200
1201 cpl_matrix_set(mx, ndata, 0, (cxdouble)y - ycenter);
1202 cpl_matrix_set(my, ndata, 0, _zx[i]);
1203 cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
1204
1205 if (_zx[i] > zxmax) {
1206 zxmax = _zx[i];
1207 }
1208
1209 if (_zx[i] < zxmin) {
1210 zxmin = _zx[i];
1211 }
1212
1213 ++ndata;
1214
1215 }
1216
1217 }
1218 else {
1219
1220 for (y = ylo; y < yup; y++) {
1221
1222 register cxint i = m + y;
1223
1224 if (_bpm[i] == 0) {
1225 cpl_matrix_set(mx, ndata, 0,
1226 (cxdouble)y - ycenter);
1227 cpl_matrix_set(my, ndata, 0, _zx[i]);
1228 cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
1229
1230 if (_zx[i] > zxmax) {
1231 zxmax = _zx[i];
1232 }
1233
1234 if (_zx[i] < zxmin) {
1235 zxmin = _zx[i];
1236 }
1237
1238 ++ndata;
1239 }
1240
1241 }
1242
1243 }
1244
1245 xbin.zmin += zxmin;
1246 xbin.zmax += zxmax;
1247 xbin.xcenter += xx;
1248 xbin.ycenter += ycenter;
1249 xbin.ywidth += swidth;
1250
1251 }
1252
1253
1254 /*
1255 * Compute per bin average values
1256 */
1257
1258 xbin.zmin /= k;
1259 xbin.zmax /= k;
1260 xbin.xcenter /= k;
1261 xbin.ycenter /= k;
1262 xbin.ywidth /= k;
1263
1264
1265 /*
1266 * Avoid negative background values
1267 */
1268
1269 xbin.zmin = CX_MAX(0., xbin.zmin);
1270
1271
1272 /*
1273 * Setup model for this bin
1274 */
1275
1276 giraffe_model_set_parameter(profile, "Amplitude",
1277 xbin.zmax - xbin.zmin);
1278 giraffe_model_set_parameter(profile, "Center", 0.);
1279 giraffe_model_set_parameter(profile, "Background", xbin.zmin);
1280
1281 switch (psfmodel) {
1282 case PROFILE_PSFEXP:
1283 width1 = pow(xbin.ywidth, exponent) / cutoff;
1284 giraffe_model_set_parameter(profile, "Width2", exponent);
1285 break;
1286
1287 case PROFILE_PSFEXP2:
1288 width1 = xbin.ywidth / pow(cutoff, 1. / exponent);
1289 giraffe_model_set_parameter(profile, "Width2", exponent);
1290 break;
1291
1292 case PROFILE_GAUSSIAN:
1293 width1 = xbin.ywidth / pow(cutoff, 0.5);
1294 break;
1295
1296 default:
1297 break;
1298 }
1299
1300 giraffe_model_set_parameter(profile, "Width1", width1);
1301
1302
1303 /*
1304 * Fit the profile
1305 */
1306
1307 status = giraffe_model_fit_sequence(profile, mx, my, ms,
1308 ndata, 0, 1);
1309
1310 amplitude = giraffe_model_get_parameter(profile, "Amplitude");
1311 bckground = giraffe_model_get_parameter(profile, "Background");
1312 center = giraffe_model_get_parameter(profile, "Center");
1313 width1 = giraffe_model_get_parameter(profile, "Width1");
1314
1315 if ((psfmodel == PROFILE_PSFEXP) ||
1316 (psfmodel == PROFILE_PSFEXP2)) {
1317 width2 = giraffe_model_get_parameter(profile, "Width2");
1318 }
1319
1320 /*
1321 * Check fit results. The fit failed, if the maximum
1322 * number of iterations has been reached, fitted amplitude
1323 * is negative or the fitted width is negative.
1324 */
1325
1326 if ((status != 0) ||
1327 (giraffe_model_get_position(profile) >= iterations) ||
1328 (amplitude <= 0.) ||
1329 (width1 <= 0.)) {
1330
1331 xbin.xcenter = -1.;
1332 amplitude = 0.;
1333 bckground = 0.;
1334 center = 0.;
1335 width1 = 0.;
1336 width2 = 0.;
1337
1338 }
1339
1340 giraffe_psfdata_set_bin(result, fiber, bin, xbin.xcenter);
1341
1342 giraffe_psfdata_set(result, "Amplitude", fiber, bin, amplitude);
1343 giraffe_psfdata_set(result, "Center", fiber, bin,
1344 xbin.ycenter + center);
1345 giraffe_psfdata_set(result, "Background", fiber, bin, bckground);
1346 giraffe_psfdata_set(result, "Width1", fiber, bin, width1);
1347
1348 if ((psfmodel == PROFILE_PSFEXP) ||
1349 (psfmodel == PROFILE_PSFEXP2)) {
1350 giraffe_psfdata_set(result, "Width2", fiber, bin, width2);
1351 }
1352
1353 }
1354
1355 }
1356
1357
1358 /*
1359 * Cleanup
1360 */
1361
1362 cpl_matrix_delete(mx);
1363 mx = NULL;
1364
1365 cpl_matrix_delete(my);
1366 my = NULL;
1367
1368 cpl_matrix_delete(ms);
1369 ms = NULL;
1370
1371 if (config->normalize == TRUE) {
1372 cpl_image_delete(zx);
1373 zx = NULL;
1374
1375 cpl_image_delete(zv);
1376 zv = NULL;
1377 }
1378
1379 return 0;
1380
1381}
1382
1383#if 0
1384/*
1385 * @brief
1386 * Fit a PSF profile model to each fiber spectrum.
1387 *
1388 * @param result Fitted PSF profile model parameters.
1389 * @param zraw Raw image of the fiber spectra.
1390 * @param zvar Raw image of flux errors.
1391 * @param locy Fiber centroid position.
1392 * @param locw Fiber width.
1393 * @param fibers List of fibers to process.
1394 * @param bpm Optional bad pixel map.
1395 * @param config PSF profile fit setup parameters.
1396 *
1397 * @return
1398 * The function returns @c 0 on success, or a non-zero value otherwise.
1399 *
1400 * The function fits a profile model given by @em psfmodel to each bin along
1401 * the dispersion direction (x-axis) for all fibers listed in @em fibers.
1402 * The bin size is given by the setup parameters @em config. It also
1403 * specifies the maximum allowed half width of a fiber spectrum.
1404 *
1405 * @note
1406 * Currently only the line models "psfexp", "psfexp2" and "gaussian"
1407 * are supported.
1408 */
1409
1410inline static cxint
1411_giraffe_psf_refine_profile(GiPsfData* result, const GiPsfData* psfdata,
1412 cpl_image* zraw, cpl_image* zvar,
1413 cpl_table* fibers, cpl_image* bpm,
1414 GiModel* profile, GiPsfParams* config)
1415{
1416
1417 const cxchar* model = NULL;
1418
1419 const cxdouble cutoff = log(config->limit);
1420
1421 cxint nx = 0;
1422 cxint ny = 0;
1423 cxint ns = 0;
1424 cxint fiber = 0;
1425 cxint nbins = 0;
1426 cxint nspectra = 0;
1427 cxint binsize = 0;
1428
1429 cxsize n = 0;
1430
1431 cpl_matrix* mx = NULL;
1432 cpl_matrix* my = NULL;
1433 cpl_matrix* ms = NULL;
1434
1435 GiProfileId psfmodel = 0;
1436
1437
1438 cx_assert(result != NULL);
1439 cx_assert(psfdata != NULL);
1440 cx_assert((zraw != NULL) && (zvar != NULL));
1441 cx_assert(fibers != NULL);
1442 cx_assert(profile != NULL);
1443 cx_assert(config != NULL);
1444
1445 cx_assert(cpl_image_get_size_x(zraw) == cpl_image_get_size_x(zvar));
1446 cx_assert(cpl_image_get_size_y(zraw) == cpl_image_get_size_y(zvar));
1447
1448
1449 nx = cpl_image_get_size_y(zraw);
1450 ny = cpl_image_get_size_x(zraw);
1451 ns = cpl_table_get_nrow(fibers);
1452
1453 if ((bpm != NULL) && (cpl_image_get_type(bpm) != CPL_TYPE_INT)) {
1454 return -1;
1455 }
1456
1457 if ((giraffe_psfdata_fibers(result) != (cxsize) ns) ||
1458 (giraffe_psfdata_bins(result) != (cxsize) nx)) {
1459 return -2;
1460 }
1461
1462 if ((giraffe_psfdata_xsize(result) != (cxsize) ny) ||
1463 (giraffe_psfdata_ysize(result) != (cxsize) nx)) {
1464 return -2;
1465 }
1466
1467 nbins = giraffe_psfdata_bins(result);
1468
1469 if ((giraffe_psfdata_fibers(psfdata) != (cxsize) ns)) {
1470 return -3;
1471 }
1472
1473 if ((giraffe_psfdata_xsize(psfdata) != (cxsize) ny) ||
1474 (giraffe_psfdata_ysize(psfdata) != (cxsize) nx)) {
1475 return -3;
1476 }
1477
1478 binsize = nx / giraffe_psfdata_bins(psfdata);
1479
1480
1481 /*
1482 * Check the model type. Only the line models "psfexp", "psfexp2" and
1483 * "gaussian" are supported.
1484 */
1485
1486 model = giraffe_model_get_name(profile);
1487
1488 if (strcmp(model, "psfexp") == 0) {
1489 psfmodel = PROFILE_PSFEXP;
1490 }
1491 else if (strcmp(model, "psfexp2") == 0) {
1492 psfmodel = PROFILE_PSFEXP2;
1493 }
1494 else if (strcmp(model, "gaussian") == 0) {
1495 psfmodel = PROFILE_GAUSSIAN;
1496 }
1497 else {
1498 return -4;
1499 }
1500
1501
1502 /*
1503 * Allocate buffers for the profile data points and their errors. The
1504 * buffer size is choosen to be large enough for the number of bins of
1505 * requested psf data object and the given maximum fiber width.
1506 */
1507
1508 mx = cpl_matrix_new(nbins * config->mwidth, 1);
1509 my = cpl_matrix_new(nbins * config->mwidth, 1);
1510 ms = cpl_matrix_new(nbins * config->mwidth, 1);
1511
1512 if ((mx == NULL) || (my == NULL) || (ms == NULL)) {
1513
1514 if (mx != NULL) {
1515 cpl_matrix_delete(mx);
1516 mx = NULL;
1517 }
1518
1519 if (my != NULL) {
1520 cpl_matrix_delete(my);
1521 my = NULL;
1522 }
1523
1524 if (ms != NULL) {
1525 cpl_matrix_delete(ms);
1526 ms = NULL;
1527 }
1528
1529 return 1;
1530
1531 }
1532
1533
1534 /*
1535 * Allocate the buffers of the results structure here, to avoid
1536 * complicated error handling in the nested loops.
1537 */
1538
1539 giraffe_psfdata_set_model(result, giraffe_model_get_name(profile));
1540
1541 for (n = 0; n < giraffe_model_count_parameters(profile); n++) {
1542
1543 const cxchar* name = giraffe_model_parameter_name(profile, n);
1544
1545 cpl_image* values = cpl_image_new(ns, nbins, CPL_TYPE_DOUBLE);
1546
1547
1548 if ((name == NULL) || (values == NULL)) {
1549
1550 giraffe_psfdata_clear(result);
1551
1552 cpl_matrix_delete(mx);
1553 mx = NULL;
1554
1555 cpl_matrix_delete(my);
1556 my = NULL;
1557
1558 cpl_matrix_delete(ms);
1559 ms = NULL;
1560
1561 return 1;
1562
1563 }
1564
1565 giraffe_psfdata_set_data(result, name, values);
1566
1567 }
1568
1569
1570 /*
1571 * Loop over all available fibers
1572 */
1573
1574 for (fiber = 0; fiber < ns; fiber++) {
1575
1576 cxint x = 0;
1577 const cxint* _bpm = NULL;
1578
1579 const cxdouble* _zx = cpl_image_get_data_double(zraw);
1580 const cxdouble* _zv = cpl_image_get_data_double(zvar);
1581
1582
1583 if (bpm != NULL) {
1584 _bpm = cpl_image_get_data_int(bpm);
1585 }
1586
1587
1588 /*
1589 * Fit a profile for each bin
1590 */
1591
1592 for (x = 0; x < nx; x++) {
1593
1594 register cxint y = 0;
1595 register cxint m = x * ny;
1596 register cxint bin = CX_MAX(0, CX_MIN((cxint) floor(x / binsize),
1597 nbins));
1598
1599 cxint status = 0;
1600 cxint ndata = 0;
1601 cxint iterations = giraffe_model_get_iterations(profile);
1602
1603 cxdouble xcenter = 0.;
1604 cxdouble ycenter = 0.;
1605 cxdouble swidth = 0.;
1606 cxdouble ylo = 0.;
1607 cxdouble yup = ny;
1608 cxdouble amplitude = giraffe_psfdata_get(psfdata, "Amplitude",
1609 fiber, bin);
1610 cxdouble bckground = giraffe_psfdata_get(psfdata, "Background",
1611 fiber, bin);
1612 cxdouble center = giraffe_psfdata_get(psfdata, "Center",
1613 fiber, bin);
1614 cxdouble width1 = giraffe_psfdata_get(psfdata, "Width1",
1615 fiber, bin);
1616 cxdouble width2 = 0.;
1617
1618
1619 switch (psfmodel) {
1620 case PROFILE_PSFEXP:
1621 width2 = giraffe_psfdata_get(psfdata, "Width2",
1622 fiber, bin);
1623 swidth = pow(width1 * cutoff, 1./ width2);
1624 break;
1625
1626 case PROFILE_PSFEXP2:
1627 width2 = giraffe_psfdata_get(psfdata, "Width2",
1628 fiber, bin);
1629 swidth = width1 * pow(cutoff, 1./ width2);
1630 break;
1631
1632 case PROFILE_GAUSSIAN:
1633 swidth = width1 * pow(cutoff, 0.5);
1634 break;
1635
1636 default:
1637 break;
1638 }
1639
1640 swidth = CX_MIN(swidth, config->mwidth);
1641
1642 ylo = (cxint) floor(center - swidth);
1643 ylo = CX_MAX(0., ylo);
1644
1645 yup = (cxint) ceil(center + swidth);
1646 yup = CX_MIN(ny, yup);
1647
1648 if (_bpm == NULL) {
1649
1650 for (y = ylo; y < yup; y++) {
1651
1652 register cxint i = m + y;
1653
1654 cpl_matrix_set(mx, ndata, 0, (cxdouble)y - center);
1655 cpl_matrix_set(my, ndata, 0, _zx[i]);
1656 cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
1657
1658 ++ndata;
1659
1660 }
1661
1662 }
1663 else {
1664
1665 for (y = ylo; y < yup; y++) {
1666
1667 register cxint i = m + y;
1668
1669 if (_bpm[i] == 0) {
1670 cpl_matrix_set(mx, ndata, 0, (cxdouble)y - center);
1671 cpl_matrix_set(my, ndata, 0, _zx[i]);
1672 cpl_matrix_set(ms, ndata, 0, sqrt(_zv[i]));
1673
1674 ++ndata;
1675 }
1676
1677 }
1678
1679 }
1680
1681
1682 /*
1683 * Avoid negative background values
1684 */
1685
1686 bckground = CX_MAX(0., bckground);
1687
1688
1689 /*
1690 * Setup model for this bin
1691 */
1692
1693 giraffe_model_set_parameter(profile, "Amplitude", amplitude);
1694 giraffe_model_set_parameter(profile, "Center", 0.);
1695 giraffe_model_set_parameter(profile, "Background", bckground);
1696 giraffe_model_set_parameter(profile, "Width1", width1);
1697
1698 switch (psfmodel) {
1699 case PROFILE_PSFEXP:
1700 giraffe_model_set_parameter(profile, "Width2", width2);
1701 break;
1702
1703 case PROFILE_PSFEXP2:
1704 giraffe_model_set_parameter(profile, "Width2", width2);
1705 break;
1706
1707 case PROFILE_GAUSSIAN:
1708 break;
1709
1710 default:
1711 break;
1712 }
1713
1714
1715 /*
1716 * Fit the profile
1717 */
1718
1719 status = giraffe_model_fit_sequence(profile, mx, my, ms,
1720 ndata, 0, 1);
1721
1722 amplitude = giraffe_model_get_parameter(profile, "Amplitude");
1723 bckground = giraffe_model_get_parameter(profile, "Background");
1724 ycenter = giraffe_model_get_parameter(profile, "Center");
1725 width1 = giraffe_model_get_parameter(profile, "Width1");
1726
1727 if ((psfmodel == PROFILE_PSFEXP) ||
1728 (psfmodel == PROFILE_PSFEXP2)) {
1729 width2 = giraffe_model_get_parameter(profile, "Width2");
1730 }
1731
1732
1733 /*
1734 * Check fit results. The fit failed, if the maximum
1735 * number of iterations has been reached, fitted amplitude
1736 * is negative or the fitted width is negative.
1737 */
1738
1739 if ((status != 0) ||
1740 (giraffe_model_get_position(profile) >= iterations) ||
1741 (amplitude <= 0.) ||
1742 (width1 <= 0.)) {
1743
1744 xcenter = -1.;
1745 ycenter = 0.;
1746 amplitude = 0.;
1747 bckground = 0.;
1748 width1 = 0.;
1749 width2 = 0.;
1750
1751 }
1752 else {
1753 xcenter = x;
1754 }
1755
1756 giraffe_psfdata_set_bin(result, fiber, x, xcenter);
1757
1758 giraffe_psfdata_set(result, "Amplitude", fiber, x, amplitude);
1759 giraffe_psfdata_set(result, "Center", fiber, x,
1760 ycenter + center);
1761 giraffe_psfdata_set(result, "Background", fiber, x, bckground);
1762 giraffe_psfdata_set(result, "Width1", fiber, x, width1);
1763
1764 if ((psfmodel == PROFILE_PSFEXP) ||
1765 (psfmodel == PROFILE_PSFEXP2)) {
1766 giraffe_psfdata_set(result, "Width2", fiber, x, width2);
1767 }
1768
1769 }
1770
1771 ++nspectra;
1772
1773 }
1774
1775
1776 /*
1777 * Cleanup
1778 */
1779
1780 cpl_matrix_delete(mx);
1781 mx = NULL;
1782
1783 cpl_matrix_delete(my);
1784 my = NULL;
1785
1786 cpl_matrix_delete(ms);
1787 ms = NULL;
1788
1789 return 0;
1790
1791}
1792#endif
1793
1794/*
1795 * @brief
1796 * Compute a one dimensional fit of all PSF profile model parameters for
1797 * the given grid.
1798 *
1799 * @param psfdata PSF profile parameters to be fitted.
1800 * @param fibers The list of fibers for which a PSF profile is available.
1801 * @param order Order of the Chebyshev polynomial to fit.
1802 * @param setup Sigma clipping algorithm configuration object.
1803 *
1804 * @return
1805 * The function returns a newly created PSF data object containing the
1806 * fitted parameters on success, or @c NULL if an error occurrs.
1807 *
1808 * TBD
1809 */
1810
1811inline static GiPsfData*
1812_giraffe_psf_fit_parameters1d(const GiPsfData* psfdata,
1813 const cpl_table* fibers,
1814 const cxchar** names,
1815 cxint order,
1816 const GiClipParams* setup)
1817{
1818
1819 cxint i = 0;
1820 cxint ns = 0;
1821 cxint nx = 0;
1822 cxint ny = 0;
1823 cxint status = 0;
1824
1825 GiPsfData* psffit = NULL;
1826
1827
1828 cx_assert(psfdata != NULL);
1829 cx_assert(fibers != NULL);
1830 cx_assert(setup != NULL);
1831
1832 ns = giraffe_psfdata_fibers(psfdata);
1833 nx = giraffe_psfdata_ysize(psfdata);
1834 ny = giraffe_psfdata_xsize(psfdata);
1835
1836 psffit = giraffe_psfdata_create(ns, nx, ny, nx);
1837
1838 giraffe_psfdata_set_model(psffit, giraffe_psfdata_get_model(psfdata));
1839
1840
1841 /*
1842 * Create the bin data for the fitted PSF parameter data. This
1843 * is actually not needed, but makes psffit a complete, valid
1844 * PSF data object.
1845 */
1846
1847 for (i = 0; i < ns; i++) {
1848
1849 register cxint j = 0;
1850
1851 for (j = 0; j < nx; j++) {
1852 giraffe_psfdata_set_bin(psffit, i, j, j);
1853 }
1854
1855 }
1856
1857
1858 if (names == NULL) {
1859
1860 cxsize j = 0;
1861 cxsize count = giraffe_psfdata_parameters(psfdata);
1862
1863 for (j = 0; j < count; j++) {
1864
1865 const cxchar* name = giraffe_psfdata_get_name(psfdata, j);
1866
1867 GiPsfParameterFit pfit = {NULL, NULL};
1868
1869
1870 pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
1871 pfit.coeffs = cpl_matrix_new(order + 1, ns);
1872
1873 status = _giraffe_psf_fit_profile1d(&pfit, psfdata, name,
1874 fibers, order, setup);
1875
1876 if (status != 0) {
1877 cpl_matrix_delete(pfit.coeffs);
1878 pfit.coeffs = NULL;
1879
1880 cpl_image_delete(pfit.fit);
1881 pfit.fit = NULL;
1882
1883 giraffe_psfdata_delete(psffit);
1884 psffit = NULL;
1885
1886 return NULL;
1887 }
1888 else {
1889 giraffe_psfdata_set_data(psffit, name, pfit.fit);
1890 pfit.fit = NULL;
1891
1892 cpl_matrix_delete(pfit.coeffs);
1893 pfit.coeffs = NULL;
1894
1895 }
1896
1897 }
1898
1899 }
1900 else {
1901
1902 /*
1903 * For each PSF parameter, whose name is listed in name and present in
1904 * the PSF data object, a one dimensional polynomial model is created.
1905 */
1906
1907 i = 0;
1908 while (names[i] != NULL) {
1909
1910 if (giraffe_psfdata_contains(psfdata, names[i]) == TRUE) {
1911
1912 GiPsfParameterFit pfit = {NULL, NULL};
1913
1914
1915 pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
1916 pfit.coeffs = cpl_matrix_new(order + 1, ns);
1917
1918 status = _giraffe_psf_fit_profile1d(&pfit, psfdata, names[i],
1919 fibers, order, setup);
1920
1921 if (status != 0) {
1922 cpl_matrix_delete(pfit.coeffs);
1923 pfit.coeffs = NULL;
1924
1925 cpl_image_delete(pfit.fit);
1926 pfit.fit = NULL;
1927
1928 giraffe_psfdata_delete(psffit);
1929 psffit = NULL;
1930
1931 return NULL;
1932 }
1933 else {
1934 giraffe_psfdata_set_data(psffit, names[i], pfit.fit);
1935 pfit.fit = NULL;
1936
1937 cpl_matrix_delete(pfit.coeffs);
1938 pfit.coeffs = NULL;
1939
1940 }
1941
1942 }
1943
1944 ++i;
1945
1946 }
1947
1948 }
1949
1950 return psffit;
1951
1952}
1953
1954
1955/*
1956 * @brief
1957 * Compute the fit of all PSF profile model parameters for the given grid.
1958 *
1959 * @param result Container for the fitted PSF parameters.
1960 *
1961 * @return
1962 * The function returns 0 on success, or a non-zero value otherwise.
1963 *
1964 * TBD
1965 */
1966
1967inline static GiPsfData*
1968_giraffe_psf_fit_parameters(const GiPsfData* psfdata,
1969 const cpl_table* fibers,
1970 const cxchar** names,
1971 cxint yorder, cxint worder,
1972 const GiClipParams* setup)
1973{
1974
1975 const cxchar* center = NULL;
1976
1977 cxint i = 0;
1978 cxint ns = 0;
1979 cxint nx = 0;
1980 cxint ny = 0;
1981 cxint status = 0;
1982
1983 GiPsfData* psffit = NULL;
1984
1985
1986 cx_assert(psfdata != NULL);
1987 cx_assert(fibers != NULL);
1988 cx_assert(names != NULL);
1989 cx_assert(setup != NULL);
1990
1991 ns = giraffe_psfdata_fibers(psfdata);
1992 nx = giraffe_psfdata_ysize(psfdata);
1993 ny = giraffe_psfdata_xsize(psfdata);
1994
1995 psffit = giraffe_psfdata_create(ns, nx, ny, nx);
1996
1997 giraffe_psfdata_set_model(psffit, giraffe_psfdata_get_model(psfdata));
1998
1999
2000 /*
2001 * Create the bin data for the fitted PSF parameter data. This
2002 * is actually not needed, but makes psffit a complete, valid
2003 * PSF data object.
2004 */
2005
2006 for (i = 0; i < ns; i++) {
2007
2008 register cxint j = 0;
2009
2010 for (j = 0; j < nx; j++) {
2011 giraffe_psfdata_set_bin(psffit, i, j, j);
2012 }
2013
2014 }
2015
2016 center = names[0];
2017 if (giraffe_psfdata_contains(psfdata, center) == FALSE) {
2018
2019 giraffe_psfdata_delete(psffit);
2020 psffit = NULL;
2021
2022 return NULL;
2023
2024 }
2025 else {
2026
2027 GiPsfParameterFit pfit = {NULL, NULL};
2028
2029
2030 pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
2031 pfit.coeffs = cpl_matrix_new(yorder + 1, ns);
2032
2033 status = _giraffe_psf_fit_profile1d(&pfit, psfdata, center, fibers,
2034 yorder, setup);
2035
2036 if (status != 0) {
2037 cpl_matrix_delete(pfit.coeffs);
2038 pfit.coeffs = NULL;
2039
2040 cpl_image_delete(pfit.fit);
2041 pfit.fit = NULL;
2042
2043 giraffe_psfdata_delete(psffit);
2044 psffit = NULL;
2045
2046 return NULL;
2047 }
2048 else {
2049 giraffe_psfdata_set_data(psffit, center, pfit.fit);
2050 pfit.fit = NULL;
2051
2052 cpl_matrix_delete(pfit.coeffs);
2053 pfit.coeffs = NULL;
2054
2055 }
2056
2057 }
2058
2059
2060 i = 1;
2061 while (names[i] != NULL) {
2062
2063 if (giraffe_psfdata_contains(psfdata, names[i]) == TRUE) {
2064
2065 const cpl_image* xbin = giraffe_psfdata_get_bins(psfdata);
2066 const cpl_image* ybin = giraffe_psfdata_get_data(psfdata, center);
2067 const cpl_image* yfit = giraffe_psfdata_get_data(psffit, center);
2068 const cpl_image* pdata = giraffe_psfdata_get_data(psfdata,
2069 names[i]);
2070
2071 GiPsfParameterFit pfit = {NULL, NULL};
2072
2073
2074 pfit.fit = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
2075 pfit.coeffs = cpl_matrix_new(yorder + 1, worder + 1);
2076
2077 status = _giraffe_psf_fit_profile2d(&pfit, fibers, pdata, xbin,
2078 ybin, yorder, worder, yfit,
2079 0, ny, setup);
2080
2081 if (status != 0) {
2082 cpl_matrix_delete(pfit.coeffs);
2083 pfit.coeffs = NULL;
2084
2085 cpl_image_delete(pfit.fit);
2086 pfit.fit = NULL;
2087
2088 giraffe_psfdata_delete(psffit);
2089 psffit = NULL;
2090
2091 return NULL;
2092 }
2093 else {
2094 giraffe_psfdata_set_data(psffit, names[i], pfit.fit);
2095 pfit.fit = NULL;
2096
2097 cpl_matrix_delete(pfit.coeffs);
2098 pfit.coeffs = NULL;
2099
2100 }
2101 }
2102
2103 ++i;
2104
2105 }
2106
2107 return psffit;
2108
2109}
2110
2111
2112/*
2113 * @brief
2114 * Compute a localization mask and coefficients from the PSF profile
2115 * parameters.
2116 *
2117 * @return
2118 * The function returns 0 on success, or a non-zero value otherwise.
2119 *
2120 * TBD
2121 */
2122
2123inline static int
2124_giraffe_psf_compute_mask(GiMaskPosition* positions, GiMaskPosition* coeffs,
2125 const GiPsfData* psfdata, const cpl_table* fibers,
2126 cxint yorder, cxint worder,
2127 const GiClipParams* setup)
2128{
2129
2130 const cxchar* const lcenter = "Center";
2131 const cxchar* const lwidth = "Width1";
2132 const cxchar* const lexponent = "Width2";
2133 const cxchar* model = NULL;
2134
2135 cxint i = 0;
2136 cxint ns = 0;
2137 cxint nx = 0;
2138 cxint ny = 0;
2139 cxint status = 0;
2140
2141 const cpl_image* xbin = NULL;
2142 const cpl_image* ybin = NULL;
2143 cpl_image* width = NULL;
2144
2145 GiPsfParameterFit center = {NULL, NULL};
2146 GiPsfParameterFit halfwidth = {NULL, NULL};
2147
2148 GiProfileId psfmodel = 0;
2149
2150
2151 cx_assert((positions != NULL) &&
2152 (positions->type == GIMASK_FITTED_DATA) &&
2153 (positions->my != NULL) &&
2154 (positions->mw != NULL));
2155 cx_assert((coeffs != NULL) &&
2156 (coeffs->type == GIMASK_FIT_COEFFS) &&
2157 (coeffs->my != NULL) &&
2158 (coeffs->mw != NULL));
2159 cx_assert(psfdata != NULL);
2160 cx_assert(fibers != NULL);
2161 cx_assert(setup != NULL);
2162
2163 model = giraffe_psfdata_get_model(psfdata);
2164
2165 if (strcmp(model, "psfexp") == 0) {
2166 psfmodel = PROFILE_PSFEXP;
2167 }
2168 else if (strcmp(model, "psfexp2") == 0) {
2169 psfmodel = PROFILE_PSFEXP2;
2170 }
2171 else if (strcmp(model, "gaussian") == 0) {
2172 psfmodel = PROFILE_GAUSSIAN;
2173 }
2174 else {
2175 return -1;
2176 }
2177
2178 ns = giraffe_psfdata_fibers(psfdata);
2179 nx = giraffe_psfdata_ysize(psfdata);
2180 ny = giraffe_psfdata_xsize(psfdata);
2181
2182 if ((cpl_matrix_get_nrow(positions->my) != nx) ||
2183 (cpl_matrix_get_ncol(positions->my) != ns) ||
2184 (cpl_matrix_get_nrow(positions->mw) != nx) ||
2185 (cpl_matrix_get_ncol(positions->mw) != ns)) {
2186 return -1;
2187 }
2188
2189 if ((cpl_matrix_get_nrow(coeffs->my) != yorder + 1) ||
2190 (cpl_matrix_get_ncol(coeffs->my) != ns)) {
2191 return -1;
2192 }
2193
2194 if ((cpl_matrix_get_nrow(coeffs->mw) != worder + 1) ||
2195 (cpl_matrix_get_ncol(coeffs->mw) != worder + 1)) {
2196 return -1;
2197 }
2198
2199 if (giraffe_psfdata_contains(psfdata, lcenter) == FALSE ||
2200 giraffe_psfdata_contains(psfdata, lwidth) == FALSE) {
2201 return 1;
2202 }
2203
2204 center.fit = cpl_image_wrap_double(ns, nx,
2205 cpl_matrix_get_data(positions->my));
2206 center.coeffs = coeffs->my;
2207
2208 status = _giraffe_psf_fit_profile1d(&center, psfdata, lcenter, fibers,
2209 yorder, setup);
2210
2211 if (status != 0) {
2212 cpl_image_unwrap(center.fit);
2213
2214 center.fit = NULL;
2215 center.coeffs = NULL;
2216
2217 return 1;
2218 }
2219
2220 width = cpl_image_new(ns, nx, CPL_TYPE_DOUBLE);
2221
2222 switch (psfmodel) {
2223 case PROFILE_PSFEXP:
2224 {
2225
2226 const cxdouble LOG2 = log(2.);
2227
2228 if (giraffe_psfdata_contains(psfdata, lexponent) == FALSE) {
2229 cpl_image_delete(width);
2230 width = NULL;
2231
2232 cpl_image_unwrap(center.fit);
2233 center.fit = NULL;
2234 center.coeffs = NULL;
2235
2236 return 1;
2237 }
2238
2239 for (i = 0; i < ns; i++) {
2240
2241 register cxint j = 0;
2242
2243 register cxdouble* _width = cpl_image_get_data_double(width);
2244
2245
2246 for (j = 0; j < nx; j++) {
2247
2248 register cxint k = j * ns + i;
2249
2250 cxdouble width1 =
2251 giraffe_psfdata_get(psfdata, lwidth, i, j);
2252 cxdouble width2 =
2253 giraffe_psfdata_get(psfdata, lexponent, i, j);
2254
2255
2256 _width[k] = 2. * pow(LOG2 * width1, 1. / width2);
2257
2258 }
2259
2260 }
2261
2262 }
2263 break;
2264
2265 case PROFILE_PSFEXP2:
2266 {
2267
2268 const cxdouble LOG2 = log(2.);
2269
2270 if (giraffe_psfdata_contains(psfdata, lexponent) == FALSE) {
2271 cpl_image_delete(width);
2272 width = NULL;
2273
2274 cpl_image_unwrap(center.fit);
2275 center.fit = NULL;
2276 center.coeffs = NULL;
2277
2278 return 1;
2279 }
2280
2281 for (i = 0; i < ns; i++) {
2282
2283 register cxint j = 0;
2284
2285 register cxdouble* _width = cpl_image_get_data_double(width);
2286
2287
2288 for (j = 0; j < nx; j++) {
2289
2290 register cxint k = j * ns + i;
2291
2292 cxdouble width1 =
2293 giraffe_psfdata_get(psfdata, lwidth, i, j);
2294 cxdouble width2 =
2295 giraffe_psfdata_get(psfdata, lexponent, i, j);
2296
2297
2298 _width[k] = 2. * pow(LOG2, 1. / width2) * width1;
2299
2300 }
2301
2302 }
2303
2304 }
2305 break;
2306
2307 case PROFILE_GAUSSIAN:
2308 {
2309
2310 const cxdouble fwhmscale = 4. * sqrt(2. * log(2.));
2311
2312 for (i = 0; i < ns; i++) {
2313
2314 register cxint j = 0;
2315
2316 register cxdouble* _width = cpl_image_get_data_double(width);
2317
2318
2319 for (j = 0; j < nx; j++) {
2320
2321 register cxint k = j * ns + i;
2322
2323 _width[k] = fwhmscale *
2324 giraffe_psfdata_get(psfdata, lwidth, i, j);
2325 }
2326
2327 }
2328
2329 }
2330 break;
2331
2332 default:
2333 /* This point should never be reached! */
2334
2335 cpl_image_delete(width);
2336 width = NULL;
2337
2338 cpl_image_unwrap(center.fit);
2339 center.fit = NULL;
2340 center.coeffs = NULL;
2341
2342 gi_error("Unsupported PSF profile model encountered!");
2343 break;
2344 }
2345
2346
2347 xbin = giraffe_psfdata_get_bins(psfdata);
2348 ybin = giraffe_psfdata_get_data(psfdata, lcenter);
2349
2350
2351 halfwidth.fit = cpl_image_wrap_double(ns, nx,
2352 cpl_matrix_get_data(positions->mw));
2353 halfwidth.coeffs = coeffs->mw;
2354
2355 status = _giraffe_psf_fit_profile2d(&halfwidth, fibers, width, xbin,
2356 ybin, worder, worder, center.fit,
2357 0, ny, setup);
2358
2359 if (status != 0) {
2360 cpl_image_unwrap(halfwidth.fit);
2361 halfwidth.fit = NULL;
2362 halfwidth.coeffs = NULL;
2363
2364 cpl_image_delete(width);
2365 width = NULL;
2366
2367 cpl_image_unwrap(center.fit);
2368 center.fit = NULL;
2369 center.coeffs = NULL;
2370
2371 return 1;
2372 }
2373
2374 cpl_image_unwrap(halfwidth.fit);
2375 halfwidth.fit = NULL;
2376 halfwidth.coeffs = NULL;
2377
2378 cpl_image_delete(width);
2379 width = NULL;
2380
2381 cpl_image_unwrap(center.fit);
2382 center.fit = NULL;
2383 center.coeffs = NULL;
2384
2385 return 0;
2386
2387}
2388
2389#if 0
2390inline static cpl_image*
2391_giraffe_psf_simulate_mask(const GiPsfData* psfdata,
2392 const cpl_image* amplitude,
2393 const cpl_image* background,
2394 cxdouble cutoff)
2395{
2396
2397 const cxchar* model = NULL;
2398
2399 cxint i = 0;
2400 cxint nfibers = 0;
2401 cxint nbins = 0;
2402 cxint _nbins = 0;
2403 cxint nx = 0;
2404 cxint ny = 0;
2405 cxint fiber = 0;
2406
2407 cxdouble bsize = 1.;
2408 cxdouble _bsize = 1.;
2409 cxdouble* _mask = NULL;
2410
2411 const cpl_image* center = NULL;
2412 const cpl_image* width = NULL;
2413 const cpl_image* exponent = NULL;
2414
2415 cpl_image* mask = NULL;
2416
2417 GiModel* profile = NULL;
2418
2419 GiProfileId psfmodel = 0;
2420
2421
2422 cx_assert(psfdata != NULL);
2423
2424 model = giraffe_psfdata_get_model(psfdata);
2425
2426 if (strcmp(model, "psfexp") == 0) {
2427 psfmodel = PROFILE_PSFEXP;
2428 }
2429 else if (strcmp(model, "psfexp2") == 0) {
2430 psfmodel = PROFILE_PSFEXP2;
2431 }
2432 else if (strcmp(model, "gaussian") == 0) {
2433 psfmodel = PROFILE_GAUSSIAN;
2434 }
2435 else {
2436 return NULL;
2437 }
2438
2439 nfibers = giraffe_psfdata_fibers(psfdata);
2440 nbins = giraffe_psfdata_bins(psfdata);
2441 nx = giraffe_psfdata_ysize(psfdata);
2442 ny = giraffe_psfdata_xsize(psfdata);
2443
2444 center = giraffe_psfdata_get_data(psfdata, "Center");
2445 width = giraffe_psfdata_get_data(psfdata, "Width1");
2446 exponent = giraffe_psfdata_get_data(psfdata, "Width2");
2447
2448 if (amplitude == NULL) {
2449 amplitude = giraffe_psfdata_get_data(psfdata, "Amplitude");
2450 }
2451 else {
2452 if ((cpl_image_get_size_x(amplitude) != nfibers) ||
2453 (cpl_image_get_size_y(amplitude) > nbins)) {
2454 return NULL;
2455 }
2456 }
2457
2458 if (background == NULL) {
2459 background = giraffe_psfdata_get_data(psfdata, "Background");
2460 }
2461 else {
2462 if ((cpl_image_get_size_x(background) != nfibers) ||
2463 (cpl_image_get_size_y(background) > nbins)) {
2464 return NULL;
2465 }
2466 }
2467
2468 bsize = (cxdouble)nx / (cxdouble)nbins;
2469
2470 _nbins = cpl_image_get_size_y(amplitude);
2471 _bsize = (cxdouble)nx / (cxdouble)_nbins;
2472
2473 mask = cpl_image_new(ny, nx, CPL_TYPE_DOUBLE);
2474 _mask = cpl_image_get_data_double(mask);
2475
2476 profile = giraffe_model_new(model);
2477
2478 for (fiber = 0; fiber < nfibers; fiber++) {
2479
2480 cxint ylower = 0;
2481 cxint yupper = ny;
2482
2483 const cxdouble* _amplitude =
2484 cpl_image_get_data_double_const(amplitude);
2485 const cxdouble* _center =
2486 cpl_image_get_data_double_const(center);
2487 const cxdouble* _width =
2488 cpl_image_get_data_double_const(width);
2489 const cxdouble* _exponent =
2490 cpl_image_get_data_double_const(exponent);
2491
2492 for (i = 0; i < nx; i++) {
2493
2494 register cxint j = 0;
2495 register cxint k = 0;
2496 register cxint l = 0;
2497 register cxint bin = 0;
2498
2499 register cxdouble a = 1.;
2500 register cxdouble b = 0.;
2501 register cxdouble c = 0.;
2502 register cxdouble s = 0.;
2503 register cxdouble e = 0.;
2504
2505
2506 bin = CX_MAX(0, CX_MIN((cxint)floor(i / bsize), nbins - 1));
2507 k = bin * nfibers + fiber;
2508
2509 bin = CX_MAX(0, CX_MIN((cxint)floor(i / _bsize), _nbins - 1));
2510 l = bin * nfibers + fiber;
2511
2512 a = _amplitude[l];
2513 c = _center[k];
2514 s = _width[k];
2515 e = _exponent[k];
2516
2517 giraffe_model_set_parameter(profile, "Amplitude", a);
2518 giraffe_model_set_parameter(profile, "Background", b);
2519 giraffe_model_set_parameter(profile, "Center", c);
2520 giraffe_model_set_parameter(profile, "Width1", s);
2521 giraffe_model_set_parameter(profile, "Width2", e);
2522
2523 switch (psfmodel) {
2524 case PROFILE_PSFEXP:
2525 {
2526 cxdouble w = pow(s * log(1. / cutoff), 1. / e);
2527
2528 ylower = (cxint) floor(c - w);
2529 yupper = (cxint) ceil(c + w);
2530 }
2531 break;
2532
2533 case PROFILE_PSFEXP2:
2534 {
2535 cxdouble w = s * pow(log(1. / cutoff), 1. / e);
2536
2537 ylower = (cxint) floor(c - w);
2538 yupper = (cxint) ceil(c + w);
2539 }
2540 break;
2541
2542 case PROFILE_GAUSSIAN:
2543 {
2544 cxdouble w = s * sqrt(log(1. / cutoff));
2545 ylower = (cxint) floor(c - w);
2546 yupper = (cxint) ceil(c + w);
2547 }
2548 break;
2549
2550 default:
2551 gi_error("Unsupported PSF profile model encountered!");
2552 break;
2553 }
2554
2555 ylower = CX_MAX(0, ylower);
2556 yupper = CX_MIN(ny, yupper);
2557
2558 for (j = ylower; j < yupper; j++) {
2559
2560 cxint status = 0;
2561
2562 cxdouble value = 0.;
2563
2564 // FIXME: Performance problem? Check this!
2565 //register cxdouble value =
2566 // a * exp(-pow(fabs(j - c) / s, e)) + b;
2567
2568 giraffe_model_set_argument(profile, "x", j);
2569 giraffe_model_evaluate(profile, &value, &status);
2570
2571 _mask[i * ny + j] += value;
2572
2573 }
2574
2575 }
2576
2577 }
2578
2579 giraffe_model_delete(profile);
2580 profile = NULL;
2581
2582 return mask;
2583
2584}
2585#endif
2586
2587
2598cxint
2599giraffe_compute_fiber_profiles(GiLocalization* result, GiImage* image,
2600 GiTable* fibers, GiLocalization* master,
2601 GiImage* bpixel, GiPsfConfig* config)
2602{
2603
2604 const cxchar* const _func = "giraffe_compute_fiber_profiles";
2605
2606 cxint i = 0;
2607 cxint status = 0;
2608 cxint nfibers = 0;
2609 cxint nframes = 1;
2610 cxint nbins = 0;
2611 cxint nx = 0;
2612 cxint ny = 0;
2613
2614 cxdouble conad = 1.;
2615 cxdouble bias_ron = 0.;
2616 cxdouble bias_sigma = 0.;
2617 cxdouble dark_value = 0.;
2618
2619 cx_string* s = NULL;
2620
2621 cpl_table* _fibers = NULL;
2622 cpl_table* locc = NULL;
2623
2624 cpl_matrix* my = NULL;
2625
2626 cpl_image* _image = NULL;
2627 cpl_image* _variance = NULL;
2628 cpl_image* _locy = NULL;
2629 cpl_image* _locw = NULL;
2630 cpl_image* _bpixel = NULL;
2631
2632 cpl_propertylist* properties = NULL;
2633
2634 GiModel* psfmodel = NULL;
2635
2636 GiPsfData* psfdata = NULL;
2637 GiPsfData* psffit = NULL;
2638
2639 GiMaskPosition positions = {GIMASK_FITTED_DATA, NULL, NULL};
2640 GiMaskPosition coeffs = {GIMASK_FIT_COEFFS, NULL, NULL};
2641
2642 GiPsfParams psf_setup = {0, 0, 1000., FALSE};
2643
2644
2645 if ((result == NULL) || (image == NULL) || (fibers == NULL) ||
2646 (master == NULL) || (config == NULL)) {
2647 cpl_error_set(_func, CPL_ERROR_NULL_INPUT);
2648 return 1;
2649 }
2650
2651 if ((master->locy == NULL) || (master->locw == NULL)) {
2652 cpl_error_set(_func, CPL_ERROR_NULL_INPUT);
2653 return 1;
2654 }
2655
2656 if ((result->locy != NULL) || (result->locw != NULL) ||
2657 (result->locc != NULL) || (result->psf != NULL)) {
2658 cpl_error_set(_func, CPL_ERROR_ILLEGAL_INPUT);
2659 return 1;
2660 }
2661
2662 _image = giraffe_image_get(image);
2663 _locy = giraffe_image_get(master->locy);
2664 _locw = giraffe_image_get(master->locw);
2665
2666 if (bpixel != NULL) {
2667 _bpixel = giraffe_image_get(bpixel);
2668 }
2669
2670 _fibers = giraffe_table_get(fibers);
2671
2672 if (_fibers == NULL) {
2673 cpl_error_set(_func, CPL_ERROR_DATA_NOT_FOUND);
2674 return 1;
2675 }
2676
2677 nfibers = cpl_table_get_nrow(_fibers);
2678
2679 nx = cpl_image_get_size_y(_image);
2680 ny = cpl_image_get_size_x(_image);
2681
2682 nbins = (cxint) ceil(nx / config->binsize);
2683
2684
2685 /*
2686 * Get raw image properties.
2687 */
2688
2689 properties = giraffe_image_get_properties(image);
2690
2691 if (cpl_propertylist_has(properties, GIALIAS_NFIBERS) == FALSE) {
2692 cpl_propertylist_append_int(properties, GIALIAS_NFIBERS, nfibers);
2693 cpl_propertylist_set_comment(properties, GIALIAS_NFIBERS,
2694 "Number of fibres");
2695 }
2696 else {
2697
2698 cxint _nfibers = cpl_propertylist_get_int(properties,
2699 GIALIAS_NFIBERS);
2700
2701 if (nfibers != _nfibers) {
2702 cpl_error_set(_func, CPL_ERROR_ILLEGAL_INPUT);
2703 return 1;
2704 }
2705
2706 }
2707
2708
2709 giraffe_error_push();
2710
2711 conad = giraffe_propertylist_get_conad(properties);
2712
2713 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2714 return 1;
2715 }
2716
2717 giraffe_error_pop();
2718
2719
2720 if (!cpl_propertylist_has(properties, GIALIAS_BIASERROR)) {
2721 cpl_msg_warning(_func, "Missing bias error property (%s)! Setting "
2722 "bias error to 0.", GIALIAS_BIASERROR);
2723 bias_sigma = 0.;
2724 }
2725 else {
2726 bias_sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASERROR);
2727 }
2728
2729
2730 giraffe_error_push();
2731
2732 bias_ron = giraffe_propertylist_get_ron(properties);
2733
2734 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2735 return 1;
2736 }
2737
2738 giraffe_error_pop();
2739
2740
2741 if (cpl_propertylist_has(properties, GIALIAS_DARKVALUE) == FALSE) {
2742 cpl_msg_warning(_func, "Missing dark value property (%s) will be "
2743 "set to %.2f!", GIALIAS_DARKVALUE, dark_value);
2744 cpl_propertylist_append_double(properties, GIALIAS_DARKVALUE,
2745 dark_value);
2746 }
2747 else {
2748 dark_value = cpl_propertylist_get_double(properties,
2749 GIALIAS_DARKVALUE);
2750 }
2751
2752
2753 if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
2754 nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
2755 }
2756
2757
2758 /*
2759 * Convert the bias and dark errors from ADU to electrons.
2760 */
2761
2762 bias_sigma *= conad;
2763 dark_value *= conad;
2764
2765
2766 /*
2767 * Prepare the input image and the variance image for the profile fitting.
2768 */
2769
2770 giraffe_error_push();
2771
2772 _image = cpl_image_multiply_scalar_create(_image, nframes * conad);
2773 _variance = cpl_image_abs_create(_image);
2774
2775 cpl_image_add_scalar(_variance,
2776 nframes * (bias_ron * bias_ron + nframes *
2777 (bias_sigma * bias_sigma + dark_value * dark_value)));
2778
2779 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2780 if (_variance != NULL) {
2781 cpl_image_delete(_variance);
2782 _variance = NULL;
2783 }
2784
2785 cpl_image_delete(_image);
2786 _image = NULL;
2787
2788 return 1;
2789 }
2790
2791 giraffe_error_pop();
2792
2793
2794 /*
2795 * Initialize PSF profile model.
2796 */
2797
2798 psfmodel = giraffe_model_new(config->profile);
2799
2800 giraffe_model_thaw(psfmodel);
2801
2802 giraffe_model_set_parameter(psfmodel, "Amplitude", 1.);
2803 giraffe_model_set_parameter(psfmodel, "Background", 0.);
2804 giraffe_model_set_parameter(psfmodel, "Center", 0.);
2805 giraffe_model_set_parameter(psfmodel, "Width1", config->width);
2806
2807 if (cx_strncasecmp(config->profile, "psfexp", 6) == 0) {
2808
2809 cxdouble _exponent = fabs(config->exponent);
2810
2811 giraffe_model_set_parameter(psfmodel, "Width2", _exponent);
2812
2813 if (config->exponent > 0) {
2814 giraffe_model_freeze_parameter(psfmodel, "Width2");
2815 }
2816
2817 }
2818
2819 giraffe_model_set_iterations(psfmodel, config->fit.iterations);
2820 giraffe_model_set_delta(psfmodel, config->fit.delta);
2821 giraffe_model_set_tests(psfmodel, config->fit.tests);
2822
2823
2824 /*
2825 * Fit a PSF profile model to each fiber and compute the profile
2826 * parameters.
2827 */
2828
2829 cpl_msg_info(_func, "Fitting fiber profiles ...");
2830
2831 psf_setup.bsize = config->binsize;
2832 psf_setup.mwidth = config->maxwidth;
2833 psf_setup.normalize = config->normalize;
2834
2835 psfdata = giraffe_psfdata_create(nfibers, nbins, ny, nx);
2836
2837 status = _giraffe_psf_compute_profile(psfdata, _image, _variance, _locy,
2838 _locw, _fibers, _bpixel, psfmodel,
2839 &psf_setup);
2840
2841 cpl_image_delete(_image);
2842 _image = NULL;
2843
2844 if (status != 0) {
2845 giraffe_psfdata_delete(psfdata);
2846 psfdata= NULL;
2847
2848 giraffe_model_delete(psfmodel);
2849 psfmodel = NULL;
2850
2851 cpl_image_delete(_variance);
2852 _variance = NULL;
2853
2854 cpl_msg_error(_func, "Fiber profile fit failed!");
2855
2856 return 2;
2857 }
2858
2859
2860 /*
2861 * Scale the computed profiles to the level of the actual, average
2862 * input frame.
2863 */
2864
2865 _image = (cpl_image*) giraffe_psfdata_get_data(psfdata, "Amplitude");
2866 cpl_image_divide_scalar(_image, nframes * conad);
2867
2868 _image = (cpl_image*) giraffe_psfdata_get_data(psfdata, "Background");
2869 cpl_image_divide_scalar(_image, nframes * conad);
2870
2871 _image = NULL;
2872
2873
2874 /*
2875 * Fit a polynomial model to each PSF profile parameter if
2876 * it was requested.
2877 */
2878
2879 cpl_msg_info(_func, "Fitting PSF profile parameters ...");
2880
2881 if (config->parameter_fit == TRUE) {
2882
2883 const cxchar* parameters[] = {"Center", "Amplitude", "Background",
2884 "Width1", "Width2", NULL};
2885
2886 psffit = _giraffe_psf_fit_parameters(psfdata, _fibers, parameters,
2887 config->yorder, config->worder,
2888 &config->clip);
2889
2890 }
2891 else {
2892
2893 psffit = _giraffe_psf_fit_parameters1d(psfdata, _fibers,
2894 NULL, config->yorder,
2895 &config->clip);
2896
2897 }
2898
2899 if (psffit == NULL) {
2900 giraffe_psfdata_delete(psfdata);
2901 psfdata= NULL;
2902
2903 giraffe_model_delete(psfmodel);
2904 psfmodel = NULL;
2905
2906 cpl_image_delete(_variance);
2907 _variance = NULL;
2908
2909 cpl_msg_error(_func, "PSF parameter fit failed!");
2910 return 3;
2911 }
2912
2913 giraffe_model_delete(psfmodel);
2914 psfmodel = NULL;
2915
2916 cpl_image_delete(_variance);
2917 _variance = NULL;
2918
2919
2920 /*
2921 * Compute a fiber localization mask from the fitted fiber profiles.
2922 */
2923
2924 positions.my = cpl_matrix_new(nx, nfibers);
2925 positions.mw = cpl_matrix_new(nx, nfibers);
2926
2927 coeffs.my = cpl_matrix_new(config->yorder + 1, nfibers);
2928 coeffs.mw = cpl_matrix_new(config->worder + 1, config->worder + 1);
2929
2930 status = _giraffe_psf_compute_mask(&positions, &coeffs, psfdata, _fibers,
2931 config->yorder, config->worder,
2932 &config->clip);
2933
2934 if (status != 0) {
2935
2936 giraffe_psfdata_delete(psffit);
2937 psffit = NULL;
2938
2939 giraffe_psfdata_delete(psfdata);
2940 psfdata= NULL;
2941
2942 cpl_msg_error(_func, "Computation of localization mask from "
2943 "the fiber profile failed!");
2944
2945 return 4;
2946 }
2947
2948 giraffe_psfdata_delete(psfdata);
2949 psfdata= NULL;
2950
2951
2952 /*
2953 * Fill the results object. Convert the matrices to images and tables
2954 * and add the necessary properties.
2955 */
2956
2957 properties = giraffe_image_get_properties(image);
2958
2959 cpl_propertylist_update_string(properties, GIALIAS_PSFMODEL,
2960 config->profile);
2961 cpl_propertylist_set_comment(properties, GIALIAS_PSFMODEL,
2962 "PSF profile model identifier");
2963
2964 cpl_propertylist_update_int(properties, GIALIAS_PSFXBINS,
2965 config->binsize);
2966 cpl_propertylist_set_comment(properties, GIALIAS_PSFXBINS,
2967 "Size of bins along the dispersion "
2968 "direction.");
2969
2970 cpl_propertylist_update_int(properties, GIALIAS_PSFYDEG,
2971 config->yorder);
2972 cpl_propertylist_set_comment(properties, GIALIAS_PSFYDEG,
2973 "Order of the fiber center polynomial "
2974 "model.");
2975
2976 cpl_propertylist_update_int(properties, GIALIAS_PSFWDEG,
2977 config->worder);
2978 cpl_propertylist_set_comment(properties, GIALIAS_PSFWDEG,
2979 "Order of the fiber width 2d polynomial "
2980 "model.");
2981
2982 cpl_propertylist_update_int(properties, GIALIAS_PSFWDEG,
2983 config->worder);
2984 cpl_propertylist_set_comment(properties, GIALIAS_PSFWDEG,
2985 "Order of the fiber width 2d polynomial "
2986 "model.");
2987
2988 cpl_propertylist_update_bool(properties, GIALIAS_PSFNORM,
2989 config->normalize);
2990 cpl_propertylist_set_comment(properties, GIALIAS_PSFNORM,
2991 "Pixel value normalization.");
2992
2993 cpl_propertylist_update_int(properties, GIALIAS_PSFNX,
2994 cpl_matrix_get_nrow(positions.my));
2995 cpl_propertylist_set_comment(properties, GIALIAS_PSFNX,
2996 "Number of pixels per spectrum.");
2997
2998 cpl_propertylist_update_int(properties, GIALIAS_PSFNS,
2999 cpl_matrix_get_ncol(positions.my));
3000 cpl_propertylist_set_comment(properties, GIALIAS_PSFNS,
3001 "Number of detected fibers.");
3002
3003 cpl_propertylist_update_double(properties, GIALIAS_PSFSIGMA,
3004 config->clip.level);
3005 cpl_propertylist_set_comment(properties, GIALIAS_PSFSIGMA,
3006 "Sigma multiplier used for the PSF "
3007 "parmeter fit.");
3008
3009 cpl_propertylist_update_double(properties, GIALIAS_PSFSIGMA,
3010 config->clip.level);
3011 cpl_propertylist_set_comment(properties, GIALIAS_PSFSIGMA,
3012 "Sigma multiplier used for the fit of PSF "
3013 "parameters.");
3014
3015 cpl_propertylist_update_int(properties, GIALIAS_PSFNITER,
3016 config->clip.iterations);
3017 cpl_propertylist_set_comment(properties, GIALIAS_PSFNITER,
3018 "Number of iterations used for the fit "
3019 "of PSF parameters.");
3020
3021 cpl_propertylist_update_double(properties, GIALIAS_PSFMFRAC,
3022 config->clip.fraction);
3023 cpl_propertylist_set_comment(properties, GIALIAS_PSFMFRAC,
3024 "Minimum allowed fraction of accepted "
3025 "over total data points used for the "
3026 "fit of PSF parameters.");
3027
3028
3029 /* Fiber profile center position */
3030
3031 result->locy = giraffe_image_create(CPL_TYPE_DOUBLE,
3032 cpl_matrix_get_ncol(positions.my),
3033 cpl_matrix_get_nrow(positions.my));
3034 giraffe_image_copy_matrix(result->locy, positions.my);
3035
3036 cpl_matrix_delete(positions.my);
3037 positions.my = NULL;
3038
3039 giraffe_image_set_properties(result->locy, properties);
3040 properties = giraffe_image_get_properties(result->locy);
3041
3042 cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE, "LOCY");
3043 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3044 "GIRAFFE localization centroid");
3045
3046
3047 /* Fiber profile widths */
3048
3049 result->locw = giraffe_image_create(CPL_TYPE_DOUBLE,
3050 cpl_matrix_get_ncol(positions.mw),
3051 cpl_matrix_get_nrow(positions.mw));
3052 giraffe_image_copy_matrix(result->locw, positions.mw);
3053
3054 cpl_matrix_delete(positions.mw);
3055 positions.mw = NULL;
3056
3057 properties = giraffe_image_get_properties(result->locy);
3058
3059 giraffe_image_set_properties(result->locw, properties);
3060 properties = giraffe_image_get_properties(result->locw);
3061
3062 cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE, "LOCWY");
3063 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3064 "GIRAFFE localization half-width");
3065
3066
3067 /* Fiber polynomial model coefficients table */
3068
3069 locc = cpl_table_new(cpl_matrix_get_ncol(coeffs.my));
3070
3071 cpl_table_new_column(locc, "BUTTON", CPL_TYPE_INT);
3072 for (i = 0; i < cpl_table_get_nrow(locc); i++) {
3073 cpl_table_set_int(locc, "BUTTON", i, i);
3074 }
3075
3076 for (i = 0; i < cpl_matrix_get_nrow(coeffs.my); i++) {
3077
3078 cxchar* label = NULL;
3079
3080 cx_asprintf(&label, "YC%d", i);
3081 cpl_table_new_column(locc, label, CPL_TYPE_DOUBLE);
3082 cx_free(label);
3083
3084 }
3085
3086 properties = giraffe_image_get_properties(result->locy);
3087
3088 result->locc = giraffe_table_create(locc, properties);
3089 properties = giraffe_table_get_properties(result->locc);
3090
3091 cpl_table_delete(locc);
3092 locc = NULL;
3093
3094 my = cpl_matrix_transpose_create(coeffs.my);
3095 giraffe_table_copy_matrix(result->locc, "YC0", my);
3096
3097 cpl_matrix_delete(my);
3098 my = NULL;
3099
3100 cpl_matrix_delete(coeffs.my);
3101 coeffs.my = NULL;
3102
3103
3104 /* Add coefficients of the 2D fit of the fiber widths as properties */
3105
3106 s = cx_string_new();
3107
3108 for (i = 0; i < cpl_matrix_get_ncol(coeffs.mw); i++) {
3109 cx_string_sprintf(s, "%s%d", GIALIAS_LOCWIDCOEF, i);
3110 cpl_propertylist_update_double(properties, cx_string_get(s),
3111 cpl_matrix_get(coeffs.mw, 0, i));
3112 }
3113
3114 cx_string_delete(s);
3115 s = NULL;
3116
3117 cpl_matrix_delete(coeffs.mw);
3118 coeffs.mw = NULL;
3119
3120 cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE,
3121 "LOCYWCHEB");
3122 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3123 "GIRAFFE localization fit coefficients");
3124
3125
3126 /* Fiber profile PSF parameters */
3127
3128 if (psffit != NULL) {
3129 result->psf = psffit;
3130 }
3131
3132 return 0;
3133
3134}
3135
3136
3147GiPsfConfig*
3148giraffe_psf_config_create(cpl_parameterlist* list)
3149{
3150
3151 cpl_parameter *p;
3152
3153 GiPsfConfig *self = NULL;
3154
3155
3156 if (list == NULL) {
3157 return NULL;
3158 }
3159
3160 self = cx_calloc(1, sizeof *self);
3161
3162 p = cpl_parameterlist_find(list, "giraffe.psf.model");
3163 self->profile = cx_strdup(cpl_parameter_get_string(p));
3164
3165 if (cx_strncasecmp(self->profile, "psfexp", 6) == 0) {
3166 self->width = 16.;
3167 }
3168 else {
3169 self->width = 4.;
3170 }
3171
3172 p = cpl_parameterlist_find(list, "giraffe.psf.binsize");
3173 self->binsize = cpl_parameter_get_int(p);
3174
3175 if (self->binsize < 1) {
3176 self->binsize = 1;
3177 }
3178
3179 p = cpl_parameterlist_find(list, "giraffe.psf.maxwidth");
3180 self->maxwidth = cpl_parameter_get_double(p);
3181
3182 if (self->width > 0.) {
3183 p = cpl_parameterlist_find(list, "giraffe.psf.width");
3184 self->width = cpl_parameter_get_double(p);
3185 }
3186
3187 if (self->width > self->maxwidth) {
3188 self->width = self->maxwidth;
3189 }
3190
3191 p = cpl_parameterlist_find(list, "giraffe.psf.exponent");
3192 self->exponent = cpl_parameter_get_double(p);
3193
3194 p = cpl_parameterlist_find(list, "giraffe.psf.normalize");
3195 self->normalize = cpl_parameter_get_bool(p);
3196
3197 p = cpl_parameterlist_find(list, "giraffe.psf.profile.iterations");
3198 self->fit.iterations = cpl_parameter_get_int(p);
3199
3200 p = cpl_parameterlist_find(list, "giraffe.psf.profile.tests");
3201 self->fit.tests = cpl_parameter_get_int(p);
3202
3203 p = cpl_parameterlist_find(list, "giraffe.psf.profile.dchisquare");
3204 self->fit.delta = cpl_parameter_get_double(p);
3205
3206 p = cpl_parameterlist_find(list, "giraffe.psf.parameters.fit");
3207 self->parameter_fit = cpl_parameter_get_bool(p);
3208
3209 p = cpl_parameterlist_find(list, "giraffe.psf.parameters.yorder");
3210 self->yorder = cpl_parameter_get_int(p);
3211
3212 if (self->yorder < 0) {
3214 return NULL;
3215 }
3216
3217 p = cpl_parameterlist_find(list, "giraffe.psf.parameters.worder");
3218 self->worder = cpl_parameter_get_int(p);
3219
3220 if (self->worder < 0) {
3222 return NULL;
3223 }
3224
3225 p = cpl_parameterlist_find(list, "giraffe.psf.parameters.sigma");
3226 self->clip.level = cpl_parameter_get_double(p);
3227
3228 p = cpl_parameterlist_find(list, "giraffe.psf.parameters.iterations");
3229 self->clip.iterations = cpl_parameter_get_int(p);
3230
3231 p = cpl_parameterlist_find(list, "giraffe.psf.parameters.fraction");
3232 self->clip.fraction = cpl_parameter_get_double(p);
3233
3234 return self;
3235
3236}
3237
3238
3251void
3253{
3254
3255 if (self != NULL) {
3256 if (self->profile != NULL) {
3257 cx_free((cxptr) self->profile);
3258 self->profile = NULL;
3259 }
3260
3261 cx_free(self);
3262 }
3263
3264 return;
3265
3266}
3267
3268
3280void
3281giraffe_psf_config_add(cpl_parameterlist* list)
3282{
3283
3284 cpl_parameter* p = NULL;
3285
3286
3287 if (list == NULL) {
3288 return;
3289 }
3290
3291 p = cpl_parameter_new_enum("giraffe.psf.model",
3292 CPL_TYPE_STRING,
3293 "PSF profile model: `psfexp', `psfexp2'",
3294 "giraffe.psf",
3295 "psfexp2", 3, "psfexp", "psfexp2", "gaussian");
3296 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-model");
3297 cpl_parameterlist_append(list, p);
3298
3299 p = cpl_parameter_new_value("giraffe.psf.normalize",
3300 CPL_TYPE_BOOL,
3301 "Use normalized pixel values.",
3302 "giraffe.psf",
3303 FALSE);
3304 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-norm");
3305 cpl_parameterlist_append(list, p);
3306
3307
3308 p = cpl_parameter_new_value("giraffe.psf.binsize",
3309 CPL_TYPE_INT,
3310 "Size of bin along dispersion axis",
3311 "giraffe.psf",
3312 64);
3313 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-binsize");
3314 cpl_parameterlist_append(list, p);
3315
3316
3317 p = cpl_parameter_new_value("giraffe.psf.maxwidth",
3318 CPL_TYPE_DOUBLE,
3319 "Maximum width of the PSF profile.",
3320 "giraffe.psf",
3321 16.);
3322 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-maxwidth");
3323 cpl_parameterlist_append(list, p);
3324
3325
3326 p = cpl_parameter_new_value("giraffe.psf.width",
3327 CPL_TYPE_DOUBLE,
3328 "Initial width of the PSF profile.",
3329 "giraffe.psf",
3330 0.);
3331 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-width");
3332 cpl_parameterlist_append(list, p);
3333
3334
3335 p = cpl_parameter_new_value("giraffe.psf.exponent",
3336 CPL_TYPE_DOUBLE,
3337 "Exponent of the exponential PSF profile "
3338 "(will not be fitted if > 0).",
3339 "giraffe.psf",
3340 -3.);
3341 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-exponent");
3342 cpl_parameterlist_append(list, p);
3343
3344
3345 p = cpl_parameter_new_value("giraffe.psf.profile.iterations",
3346 CPL_TYPE_INT,
3347 "Maximum number of iterations used for "
3348 "the fit of the fiber PSF profile.",
3349 "giraffe.psf",
3350 120);
3351
3352 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfniter");
3353 cpl_parameterlist_append(list, p);
3354
3355
3356 p = cpl_parameter_new_value("giraffe.psf.profile.tests",
3357 CPL_TYPE_INT,
3358 "Maximum number of tests used for the fit "
3359 "of the fiber PSF profile",
3360 "giraffe.psf",
3361 7);
3362
3363 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfntest");
3364 cpl_parameterlist_append(list, p);
3365
3366
3367 p = cpl_parameter_new_value("giraffe.psf.profile.dchisquare",
3368 CPL_TYPE_DOUBLE,
3369 "Minimum chi-square difference used for the "
3370 "fit of the fiber PSF profile.",
3371 "giraffe.psf",
3372 0.001);
3373
3374 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-pfdchisq");
3375 cpl_parameterlist_append(list, p);
3376
3377
3378 p = cpl_parameter_new_value("giraffe.psf.parameters.fit",
3379 CPL_TYPE_BOOL,
3380 "2D fit of the PSF profile parameters "
3381 "using a Chebyshev polynomial model.",
3382 "giraffe.psf",
3383 FALSE);
3384 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-prmfit");
3385 cpl_parameterlist_append(list, p);
3386
3387
3388 p = cpl_parameter_new_value("giraffe.psf.parameters.yorder",
3389 CPL_TYPE_INT,
3390 "Order of Chebyshev polynomial fit.",
3391 "giraffe.psf",
3392 4);
3393 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-yorder");
3394 cpl_parameterlist_append(list, p);
3395
3396
3397 p = cpl_parameter_new_value("giraffe.psf.parameters.worder",
3398 CPL_TYPE_INT,
3399 "Order of Chebyshev 2D polynomial fit.",
3400 "giraffe.psf",
3401 4);
3402 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-worder");
3403 cpl_parameterlist_append(list, p);
3404
3405
3406 p = cpl_parameter_new_value("giraffe.psf.parameters.sigma",
3407 CPL_TYPE_DOUBLE,
3408 "PSF parameter fitting: sigma threshold "
3409 "factor",
3410 "giraffe.psf",
3411 3.5);
3412 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-sigma");
3413 cpl_parameterlist_append(list, p);
3414
3415
3416 p = cpl_parameter_new_value("giraffe.psf.parameters.iterations",
3417 CPL_TYPE_INT,
3418 "PSF parameter fitting: number of "
3419 "iterations",
3420 "giraffe.psf",
3421 10);
3422 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-niter");
3423 cpl_parameterlist_append(list, p);
3424
3425
3426 p = cpl_parameter_new_range("giraffe.psf.parameters.fraction",
3427 CPL_TYPE_DOUBLE,
3428 "PSF parameter fitting: minimum fraction "
3429 "of points accepted/total.",
3430 "giraffe.psf",
3431 0.8, 0.0, 1.0);
3432 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "psf-mfrac");
3433 cpl_parameterlist_append(list, p);
3434
3435 return;
3436
3437}
const cxchar * giraffe_fiberlist_query_index(const cpl_table *fibers)
Query a fiber list for the name of the fiber reference index column.
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
Definition: giimage.c:218
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
Definition: giimage.c:282
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_copy_matrix(GiImage *self, cpl_matrix *matrix)
Copies matrix elements into an image.
Definition: giimage.c:345
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:3252
GiPsfConfig * giraffe_psf_config_create(cpl_parameterlist *list)
Creates a setup object for the PSF profile fit.
Definition: gipsf.c:3148
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:2599
void giraffe_psf_config_add(cpl_parameterlist *list)
Adds parameters for the PSF profile computation of the fibers.
Definition: gipsf.c:3281
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
cxint giraffe_table_copy_matrix(GiTable *table, const cxchar *name, cpl_matrix *matrix)
Copies matrix elements into a table.
Definition: gitable.c:259
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
Definition: gitable.c:433
cpl_propertylist * giraffe_table_get_properties(const GiTable *self)
Gets the table properties.
Definition: gitable.c:489
cxdouble giraffe_propertylist_get_conad(const cpl_propertylist *properties)
Retrieve the ADU to electrons conversion factor from the given properties.
Definition: giutils.c:1473
cxdouble giraffe_propertylist_get_ron(const cpl_propertylist *properties)
Retrieve the read-out noise from the given properties.
Definition: giutils.c:1557

This file is part of the GIRAFFE Pipeline Reference Manual 2.19.4.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Feb 6 2026 13:47:22 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004