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 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
1413inline 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
1814inline 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
1970inline 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
2126inline 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
2393inline 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
2600cxint
2601giraffe_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
3149GiPsfConfig*
3150giraffe_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
3253void
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
3282void
3283giraffe_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_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: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
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:1471
cxdouble giraffe_propertylist_get_ron(const cpl_propertylist *properties)
Retrieve the read-out noise from the given properties.
Definition: giutils.c:1555

This file is part of the GIRAFFE Pipeline Reference Manual 2.18.2.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Mar 27 2025 12:23:44 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004