GIRAFFE Pipeline Reference Manual

giextract.c
1/*
2 * This file is part of the GIRAFFE Pipeline
3 * Copyright (C) 2002-2019 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <math.h>
25#include <float.h>
26
27#include <cxmemory.h>
28#include <cxmessages.h>
29#include <cxstring.h>
30#include <cxstrutils.h>
31
32#include <cpl_parameterlist.h>
33#include <cpl_matrix.h>
34#include <cpl_table.h>
35#include <cpl_msg.h>
36
37#include "gimacros.h"
38#include "gierror.h"
39#include "gialias.h"
40#include "giclip.h"
41#include "giarray.h"
42#include "giimage.h"
43#include "gimatrix.h"
44#include "giwindow.h"
45#include "gipsfdata.h"
46#include "gimodel.h"
47#include "gimath.h"
48#include "gilocalization.h"
49#include "gimessages.h"
50#include "gifiberutils.h"
51#include "giutils.h"
52#include "giextract.h"
53
54
63enum GiProfileId {
64 PROFILE_PSFEXP = 1 << 1,
65 PROFILE_PSFEXP2 = 1 << 2,
66 PROFILE_GAUSSIAN = 1 << 3
67};
68
69typedef enum GiProfileId GiProfileId;
70
71
72/*
73 * Optimal spectrum extraction algorithm configuration data
74 */
75
76struct GiExtractOptimalConfig {
77
78 GiClipParams clip;
79
80 cxbool limits;
81
82 cxint bkgorder;
83
84 cxdouble exptime;
85 cxdouble ron;
86 cxdouble dark;
87 cxdouble ewidth;
88};
89
90typedef struct GiExtractOptimalConfig GiExtractOptimalConfig;
91
92
93/*
94 * Original Horne spectrum extraction algorithm configuration data
95 */
96
97struct GiExtractHorneConfig {
98 GiClipParams clip;
99
100 cxdouble exptime;
101 cxdouble ron;
102 cxdouble dark;
103 cxdouble ewidth;
104};
105
106typedef struct GiExtractHorneConfig GiExtractHorneConfig;
107
108
109struct GiExtractionData {
110 cxdouble value;
111 cxdouble error;
112 cxdouble position;
113 cxdouble npixels;
114};
115
116typedef struct GiExtractionData GiExtractionData;
117
118
119struct GiExtractionSlice {
120 cxint fsize;
121 cxint msize;
122
123 cxint nflx;
124 cxint nbkg;
125
126 cpl_matrix* flux;
127 cpl_matrix* variance;
128 cpl_matrix* model;
129};
130
131typedef struct GiExtractionSlice GiExtractionSlice;
132
133
134struct GiExtractionPsfLimits {
135 cxint size;
136
137 cxint* ymin;
138 cxint* ymax;
139};
140
141typedef struct GiExtractionPsfLimits GiExtractionPsfLimits;
142
143
144struct GiExtractionWorkspace {
145 cpl_matrix* atw;
146 cpl_matrix* atwa;
147 cpl_matrix* atws;
148 cpl_matrix* c;
149 cpl_matrix* tmp;
150};
151
152typedef struct GiExtractionWorkspace GiExtractionWorkspace;
153
154
155struct GiVirtualSlit {
156 cxint width;
157
158 cxdouble center;
159 cxdouble extra_width;
160
161 cxdouble* position;
162 cxdouble* signal;
163 cxdouble* variance;
164 cxdouble* fraction;
165
166 cxint* mask;
167 cxint* offset;
168};
169
170typedef struct GiVirtualSlit GiVirtualSlit;
171
172
173/*
174 * Extraction slice implementation
175 */
176
177inline static GiExtractionSlice*
178_giraffe_extractionslice_new(cxint nflx, cxint ndata, cxint nbkg)
179{
180
181 GiExtractionSlice* self = cx_malloc(sizeof *self);
182
183 self->nflx = nflx;
184 self->nbkg = nbkg;
185
186 self->fsize = nflx + nbkg;
187 self->msize = ndata;
188
189 self->flux = cpl_matrix_new(self->fsize, 1);
190 self->variance = cpl_matrix_new(self->fsize, 1);
191 self->model = cpl_matrix_new(self->msize, 1);
192
193 return self;
194
195}
196
197
198inline static void
199_giraffe_extractionslice_delete(GiExtractionSlice* self)
200{
201
202 if (self != NULL) {
203 if (self->model != NULL) {
204 cpl_matrix_delete(self->model);
205 self->model = NULL;
206 }
207
208 if (self->variance != NULL) {
209 cpl_matrix_delete(self->variance);
210 self->variance = NULL;
211 }
212
213 if (self->flux != NULL) {
214 cpl_matrix_delete(self->flux);
215 self->flux = NULL;
216 }
217
218 cx_free(self);
219 }
220
221 return;
222
223}
224
225
226inline static GiExtractionPsfLimits*
227_giraffe_extraction_psflimits_new(cxint size)
228{
229
230 GiExtractionPsfLimits* self = cx_malloc(sizeof *self);
231
232 self->size = size;
233
234 self->ymin = cx_calloc(self->size, sizeof(cxint));
235 self->ymax = cx_calloc(self->size, sizeof(cxint));
236
237 return self;
238
239}
240
241
242inline static void
243_giraffe_extraction_psflimits_delete(GiExtractionPsfLimits* self)
244{
245
246 if (self != NULL) {
247 if (self->ymin != NULL) {
248 cx_free(self->ymin);
249 }
250
251 if (self->ymax != NULL) {
252 cx_free(self->ymax);
253 }
254
255 cx_free(self);
256 }
257
258 return;
259
260}
261
262
263inline static GiExtractionWorkspace*
264_giraffe_optimal_workspace_new(cxint m, cxint n)
265{
266
267 GiExtractionWorkspace* self = cx_malloc(sizeof *self);
268
269
270 self->atw = cpl_matrix_new(m, n);
271 self->atwa = cpl_matrix_new(m, m);
272 self->c = cpl_matrix_new(m, m);
273 self->atws = cpl_matrix_new(m, 1);
274
275 self->tmp = cpl_matrix_new(m, m);
276
277 return self;
278
279}
280
281
282inline static void
283_giraffe_optimal_workspace_delete(GiExtractionWorkspace* self)
284{
285
286 if (self != NULL) {
287 if (self->atws != NULL) {
288 cpl_matrix_delete(self->atws);
289 }
290
291 if (self->atwa != NULL) {
292 cpl_matrix_delete(self->atwa);
293 }
294
295 if (self->c != NULL) {
296 cpl_matrix_delete(self->c);
297 }
298
299 if (self->atw != NULL) {
300 cpl_matrix_delete(self->atw);
301 }
302
303 if (self->tmp != NULL) {
304 cpl_matrix_delete(self->tmp);
305 }
306
307 cx_free(self);
308
309 }
310
311 return;
312
313}
314
315
316/*
317 * Virtual slit implementation
318 */
319
320inline static void
321_giraffe_virtualslit_allocate(GiVirtualSlit* self)
322{
323
324 if ((self != NULL) && (self->width > 0)) {
325
326 self->position = cx_calloc(self->width, sizeof(cxdouble));
327 self->signal = cx_calloc(self->width, sizeof(cxdouble));
328 self->variance = cx_calloc(self->width, sizeof(cxdouble));
329 self->fraction = cx_calloc(self->width, sizeof(cxdouble));
330
331 self->mask = cx_calloc(self->width, sizeof(cxdouble));
332 self->offset = cx_calloc(self->width, sizeof(cxdouble));
333
334 }
335
336 return;
337
338}
339
340
341inline static GiVirtualSlit*
342_giraffe_virtualslit_new(cxdouble extra_width)
343{
344
345 GiVirtualSlit* self = cx_calloc(1, sizeof *self);
346
347 self->width = 0;
348 self->center = 0.;
349 self->extra_width = extra_width;
350
351 self->position = NULL;
352 self->signal = NULL;
353 self->variance = NULL;
354 self->fraction = NULL;
355 self->mask = NULL;
356 self->offset = NULL;
357
358 return self;
359
360}
361
362
363inline static void
364_giraffe_virtualslit_clear(GiVirtualSlit* self)
365{
366
367 if (self != NULL) {
368
369 if (self->position != NULL) {
370 cx_free(self->position);
371 self->position = NULL;
372 }
373
374 if (self->signal != NULL) {
375 cx_free(self->signal);
376 self->signal = NULL;
377 }
378
379 if (self->variance != NULL) {
380 cx_free(self->variance);
381 self->variance = NULL;
382 }
383
384 if (self->fraction != NULL) {
385 cx_free(self->fraction);
386 self->fraction = NULL;
387 }
388
389 if (self->mask != NULL) {
390 cx_free(self->mask);
391 self->mask = NULL;
392 }
393
394 if (self->offset != NULL) {
395 cx_free(self->offset);
396 self->offset = NULL;
397 }
398
399 self->extra_width = 0.;
400 self->center = 0.;
401 self->width = 0;
402
403 }
404
405 return;
406
407}
408
409
410inline static void
411_giraffe_virtualslit_delete(GiVirtualSlit* self)
412{
413
414 if (self != NULL) {
415 _giraffe_virtualslit_clear(self);
416
417 cx_free(self);
418 }
419
420 return;
421
422}
423
424
425inline static cxint
426_giraffe_virtualslit_setup(GiVirtualSlit* self, cxint bin,
427 cxdouble center, cxdouble width,
428 const cpl_image* signal, const cpl_image* variance,
429 const cpl_image* bpixel)
430{
431
432 register cxint ny = cpl_image_get_size_x(signal);
433 register cxint offset = bin * cpl_image_get_size_x(signal);
434
435 register cxdouble lower = center - (width + self->extra_width);
436 register cxdouble upper = center + (width + self->extra_width);
437
438 register cxint first = (cxint) floor(lower);
439 register cxint last = (cxint) ceil(upper);
440
441 const cxdouble* s = cpl_image_get_data_double_const(signal);
442 const cxdouble* v = cpl_image_get_data_double_const(variance);
443
444
445 /*
446 * Upper, lower border and width of the virtual slit
447 */
448
449 lower = CX_MAX(0., lower);
450 upper = CX_MIN(ny-1, upper);
451
452 first = CX_MAX(0, first);
453 last = CX_MIN(ny-1, last);
454
455 self->center = center;
456 self->width = last - first + 1;
457
458
459 /*
460 * Create and fill the buffers
461 */
462
463 _giraffe_virtualslit_allocate(self);
464
465 if (bpixel != NULL) {
466
467 register cxint k = 0;
468 register cxint y = 0;
469
470 const cxint* _bpixel = cpl_image_get_data_int_const(bpixel);
471
472
473 for (y = first; y <= last; y++) {
474
475 register cxint ypos = offset + y;
476
477 cxint ok = (_bpixel[ypos] & GIR_M_PIX_SET) == 0 ? 1 : 0;
478
479
480 self->position[k] = y - center;
481 self->fraction[k] = 1.;
482
483 self->signal[k] = s[ypos];
484 self->variance[k] = v[ypos];
485
486 self->mask[k] = ok;
487 self->offset[k] = ypos;
488 ++k;
489
490 }
491
492 }
493 else {
494
495 register cxint k = 0;
496 register cxint y = 0;
497
498
499 for (y = first; y <= last; y++) {
500
501 register cxint ypos = offset + y;
502
503 cxint ok = 1;
504
505
506 self->position[k] = y - center;
507 self->fraction[k] = 1.;
508
509 self->signal[k] = s[ypos];
510 self->variance[k] = v[ypos];
511
512 self->mask[k] = ok;
513 self->offset[k] = ypos;
514 ++k;
515
516 }
517
518 }
519
520
521 /*
522 * Correct for pixel fractions at the borders of the
523 * virtual slit, since they have been set to the full
524 * pixel in the above loop.
525 */
526
527 self->fraction[0] = ((cxdouble)first + 1.) - lower;
528 self->fraction[self->width - 1] = upper - ((cxdouble)last - 1.);
529
530 return self->width;
531
532}
533
534
535/*
536 * Compute the inverse of a square matrix
537 */
538
539inline static cxint
540_giraffe_matrix_invert(cpl_matrix* m_inv, const cpl_matrix* m, cpl_matrix* lu)
541{
542
543 cxint i = 0;
544 cxint status = 0;
545 cxint n = cpl_matrix_get_ncol(m);
546
547 register cxint sz = n * n * sizeof(cxdouble);
548
549 const cxdouble* _m = cpl_matrix_get_data_const(m);
550
551 cxdouble* _m_inv = cpl_matrix_get_data(m_inv);
552 cxdouble* _m_lu = cpl_matrix_get_data(lu);
553
554 cpl_array* perm = cpl_array_new(n, CPL_TYPE_INT);
555
556 register cxint* perm_data = cpl_array_get_data_int(perm);
557
558
559 memset(_m_inv, 0, sz);
560 memcpy(_m_lu, _m, sz);
561
562 if (cpl_matrix_decomp_lu(lu, perm, &i) != 0) {
563 cpl_array_delete(perm);
564 return 1;
565 }
566
567
568 /*
569 * Create an identity matrix with the rows permuted
570 */
571
572 for (i = 0; i < n; ++i) {
573 _m_inv[i * n + perm_data[i]] = 1.;
574 }
575
576 cpl_array_delete(perm);
577
578
579 status = cpl_matrix_solve_lu(lu, m_inv, NULL);
580
581 if (status != 0) {
582 cpl_matrix_delete(m_inv);
583 return 2;
584 }
585
586 return 0;
587
588}
589
590
591/*
592 * Compute the PSF profile for a set of abscissa values.
593 */
594
595inline static cpl_matrix*
596_giraffe_compute_psf(GiModel* psf, const cpl_matrix* x)
597{
598
599 register cxint i = 0;
600 register cxint n = 0;
601
602 cxint status = 0;
603
604 const cxdouble* _x = NULL;
605
606 cxdouble* _y = NULL;
607
608 cpl_matrix* y = NULL;
609
610 cx_assert(psf != NULL);
611 cx_assert(x != NULL);
612 cx_assert(cpl_matrix_get_ncol(x) == 1);
613
614 n = cpl_matrix_get_nrow(x);
615
616 y = cpl_matrix_new(n, 1);
617
618 _x = cpl_matrix_get_data_const(x);
619 _y = cpl_matrix_get_data(y);
620
621 for (i = 0; i < n; i++) {
622 giraffe_model_set_argument(psf, "x", _x[i]);
623 giraffe_model_evaluate(psf, &_y[i], &status);
624
625 if (status != 0) {
626 cpl_matrix_delete(y);
627 return NULL;
628 }
629 }
630
631 return y;
632
633}
634
635
636/*
637 * Horne extraction of a single wavelength bin for the given virtual
638 * slit.
639 */
640
641inline static cxint
642_giraffe_horne_extract_slit(GiExtractionData* result,
643 const GiVirtualSlit* vslit, GiModel* psf,
644 const GiExtractHorneConfig* config)
645{
646
647 cxint i = 0;
648 cxint ngood = 0;
649
650 cxdouble var = 0.;
651 cxdouble bkg = 0.;
652 cxdouble flx = 0.;
653 cxdouble norm = 0.;
654 cxdouble* tdata = NULL;
655 cxdouble* _mnpsf = NULL;
656
657 cpl_matrix* mnpsf = NULL;
658 cpl_matrix* mvslit = NULL;
659
660
661
662 /*
663 * Compute the PSF model.
664 */
665
666 mvslit = cpl_matrix_wrap(vslit->width, 1, vslit->position);
667 mnpsf = _giraffe_compute_psf(psf, mvslit);
668
669 cpl_matrix_unwrap(mvslit);
670 mvslit = NULL;
671
672 if (mnpsf == NULL) {
673 return -1;
674 }
675
676
677 /*
678 * Enforce positivity and normalization of the profile model.
679 */
680
681 _mnpsf = cpl_matrix_get_data(mnpsf);
682
683 norm = 0.;
684
685 for (i = 0; i < vslit->width; ++i) {
686 _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
687 norm += _mnpsf[i];
688 }
689
690 for (i = 0; i < vslit->width; ++i) {
691 _mnpsf[i] /= norm;
692 }
693
694
695 /*
696 * Estimate background and determine the number of valid pixels
697 */
698
699 tdata = cx_malloc(vslit->width * sizeof(cxdouble));
700
701 i = 0;
702 ngood = 0;
703
704 while (i < vslit->width) {
705 if (vslit->mask[i] > 0) {
706 tdata[ngood] = CX_MAX(vslit->signal[i], 0.);
707 ++ngood;
708 }
709 ++i;
710 }
711
712 if (ngood > 1) {
713 giraffe_array_sort(tdata, ngood);
714 bkg = 0.5 * (tdata[0] + tdata[1]);
715 }
716
717 cx_free(tdata);
718 tdata = NULL;
719
720
721 /*
722 * Try extraction only if there are good pixels available. If no good
723 * pixels are left skip this spectral bin and set the flux and the variance
724 * to zero.
725 */
726
727 if (ngood > 0) {
728
729 cxint iteration = 0;
730 cxint nreject = -1;
731 cxint niter = config->clip.iterations;
732 cxint nmin = (cxint)config->clip.fraction;
733
734 cxdouble sigma = config->clip.level * config->clip.level;
735 cxdouble* variance = NULL;
736
737
738 /*
739 * Compute standard extraction flux and rescale it to account for
740 * bad pixels.
741 */
742
743 norm = 0.;
744 flx = 0.;
745
746 for (i = 0; i < vslit->width; ++i) {
747 if (vslit->mask[i] != 0) {
748 flx += (vslit->signal[i] - bkg) * vslit->fraction[i];
749 norm += vslit->fraction[i] * _mnpsf[i];
750 }
751 }
752
753 flx /= norm;
754
755
756 /*
757 * Allocate buffer for the variance estimates and compute the initial
758 * variances from the expected profile.
759 */
760
761 variance = cx_calloc(vslit->width, sizeof(cxdouble));
762
763 for (i = 0; i < vslit->width; ++i) {
764
765 register cxdouble ve = flx * _mnpsf[i] + bkg;
766
767 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
768
769 }
770
771
772 /*
773 * Reject cosmics and extract spectrum
774 */
775
776 nreject = -1;
777
778 while ((iteration < niter) && (ngood > nmin) && (nreject != 0)) {
779
780 cxint imax = 0;
781
782 cxdouble _flx = 0.;
783 cxdouble mmax = 0.;
784
785
786 norm = 0.;
787 var = 0.;
788 nreject = 0;
789
790
791 /*
792 * Reject cosmics
793 */
794
795 for (i = 0; i < vslit->width; ++i) {
796
797 if (vslit->mask[i] != 0) {
798
799 cxdouble m = vslit->signal[i] - bkg - flx * _mnpsf[i];
800
801 m *= vslit->fraction[i];
802 m *= m / variance[i] ;
803
804 if (m > mmax) {
805 mmax = m;
806 imax = i;
807 }
808
809 }
810
811 }
812
813 if ((sigma > 0.) && (mmax > sigma)) {
814 vslit->mask[imax] = 0;
815 ++nreject;
816 --ngood;
817 }
818
819
820 /*
821 * Compute flux and variance estimates.
822 */
823
824 for (i = 0; i < vslit->width; ++i) {
825
826 if (vslit->mask[i] != 0) {
827
828 register cxdouble data = vslit->signal[i] - bkg;
829 register cxdouble p = _mnpsf[i];
830
831 data *= vslit->fraction[i];
832 p *= vslit->fraction[i];
833
834 norm += p * p / variance[i];
835 _flx += p * data / variance[i];
836 var += p;
837
838 }
839
840 }
841
842 flx = _flx / norm;
843 var /= norm;
844
845
846 /*
847 * Update variance estimates
848 */
849
850 for (i = 0; i < vslit->width; ++i) {
851
852 register cxdouble ve = flx * _mnpsf[i] + bkg;
853
854 variance[i] = vslit->variance[i] + fabs(vslit->fraction[i] * ve);
855
856 }
857
858 ++iteration;
859
860 }
861
862 cx_free(variance);
863 variance = NULL;
864
865 }
866
867 cpl_matrix_delete(mnpsf);
868 mnpsf = NULL;
869
870 result->value = flx;
871 result->error = sqrt(var);
872 result->position = vslit->center;
873 result->npixels = ngood;
874
875 return ngood == 0 ? 1 : 0;
876
877}
878
879
880/*
881 * @brief
882 * Compute the optimal extracted flux and its variance for a single
883 * wavelength bin.
884 *
885 * @param slice The results container to store the flux, variance and
886 * extraction model.
887 * @param AT The transposed design matrix of the linear system.
888 * @param S Column vector of the measured signal.
889 * @param W Matrix of weights of the measured signals.
890 * @param limits Cutoff parameters.
891 * @param ws Workspace for the matrix operations.
892 *
893 * @return
894 * The function returns 0 on success, and a non-zero value if an error
895 * occurred.
896 *
897 * The functions computes the optimal extracted fluxes for a single wavelength
898 * bin by solving the linear system:
899 * @f[
900 * \mathbf{f}\left(x\right) =
901 * \left(\mathbf{A}^\mathrm{T}\mathbf{W}\mathbf{A}\right)^{-1}
902 * \mathbf{A}^\mathrm{T}\mathbf{W}\mathbf{s}
903 * @f]
904 * where the @f$\mathbf{s}@f$ is the column vector of the measured fluxes
905 * written as an @f$\left(n \times 1\right)@f$ matrix, @f$\mathbf{W}@f$ is the
906 * diagonal @f$\left(n \times n\right)@f$ matrix of the weights, and
907 * @f$\mathbf{A}^\mathrm{T}@f$ is the transposed of the
908 * @f$\left(n \times m\right)@f$ design matrix @f$\mathbf{A}@f$.
909 *
910 * Defining the matrix @f$\mathbf{C} = \left(c_\mathrm{ij}\right) \equiv
911 * \left(\mathbf{A}^\mathrm{T}\mathbf{W}\mathbf{A}\right)^{-1}@f$, and using
912 * @f$a_\mathrm{ij}@f$ and @f$w_\mathrm{ij}@f$ to denote the elements of the
913 * transposed design matrix and the weight matrix respectively, the extracted
914 * flux can be written as the following sum:
915 * @f[
916 * f_\mathrm{i} = \sum\limits_{\mathrm{l} = 0}^{\mathrm{m} - 1} c_\mathrm{il}
917 * \sum\limits_{\mathrm{k} = 0}^{\mathrm{n} - 1} a_\mathrm{lk}
918 * w_\mathrm{kk} s_\mathrm{k}
919 * @f]
920 *
921 */
922
923inline static cxint
924_giraffe_optimal_extract_slice(GiExtractionSlice* slice,
925 const cpl_matrix* AT,
926 const cpl_matrix* S,
927 const cpl_matrix* W,
928 GiExtractionPsfLimits* limits,
929 GiExtractionWorkspace* ws)
930{
931
932 register cxint i = 0;
933 register cxint n = cpl_matrix_get_ncol(AT);
934 register cxint m = cpl_matrix_get_nrow(AT);
935
936 cxint status = 0;
937
938 const cxdouble* at = cpl_matrix_get_data_const(AT);
939 const cxdouble* w = cpl_matrix_get_data_const(W);
940 const cxdouble* s = cpl_matrix_get_data_const(S);
941 const cxdouble* c = cpl_matrix_get_data_const(ws->c);
942
943 cxdouble* atw = cpl_matrix_get_data(ws->atw);
944 cxdouble* atwa = cpl_matrix_get_data(ws->atwa);
945 cxdouble* atws = cpl_matrix_get_data(ws->atws);
946 cxdouble* sf = cpl_matrix_get_data(slice->flux);
947 cxdouble* sv = cpl_matrix_get_data(slice->variance);
948 cxdouble* sm = cpl_matrix_get_data(slice->model);
949
950
951 for (i = 0; i < m; ++i) {
952
953 register cxint j = 0;
954 register cxint im = i * m;
955 register cxint in = i * n;
956 register cxint ymin = limits->ymin[i];
957 register cxint ymax = limits->ymax[i];
958
959
960 atws[i] = 0.;
961
962 for (j = 0; j < n; ++j) {
963
964 register cxint k = in + j;
965
966
967 atw[k] = w[j] * at[k];
968 atws[i] += atw[k] * s[j];
969
970 }
971
972 for (j = 0; j < i; ++j) {
973
974 register cxint k = 0;
975 register cxint l = im + j;
976
977 atwa[l] = 0.;
978 for (k = ymin; k < ymax; ++k) {
979 atwa[l] += atw[in + k] * at[j * n + k];
980 }
981
982 atwa[j * m + i] = atwa[l];
983
984 }
985
986 atwa[im + i] = 0.;
987
988 for (j = ymin; j < ymax; ++j) {
989 atwa[im + i] += atw[in + j] * at[in + j];
990 }
991
992 }
993
994
995 status = _giraffe_matrix_invert(ws->c, ws->atwa, ws->tmp);
996
997 if (status != 0) {
998 return 1;
999 }
1000
1001 for (i = 0; i < m; ++i) {
1002
1003 register cxint j = 0;
1004 register cxint im = i * m;
1005
1006
1007 sf[i] = 0.;
1008 sv[i] = c[im + i];
1009
1010 for (j = 0; j < m; ++j) {
1011 sf[i] += c[im + j] * atws[j];
1012 }
1013
1014 }
1015
1016 for (i = 0; i < n; ++i) {
1017
1018 register cxint j = 0;
1019
1020
1021 sm[i] = 0.;
1022
1023 for (j = 0; j < m; ++j) {
1024 sm[i] += at[j * n + i] * sf[j];
1025 }
1026
1027 }
1028
1029 return 0;
1030
1031}
1032
1033
1034/*
1035 * @brief
1036 * Extract spectra by simple summation.
1037 *
1038 * @param mz Pixels values [nx, ny]
1039 * @param mslz Scattered light model pixel values [nx, ny]
1040 * @param fibers List of fibers to extract
1041 * @param my Fiber centroid positions [ns, ny]
1042 * @param mw Fiber widths [ns, ny]
1043 * @param ms Extracted flux [ns, ny]
1044 * @param mse Extracted flux error [ns, ny]
1045 * @param msn Number of extracted pixels [ns, ny]
1046 * @param msy Extracted flux centroid position [ns, ny]
1047 *
1048 * For each X bin and each spectrum the flux is computed as the sum of
1049 * pixels value from @em mz[nx,ny] along the virtual slit computed using
1050 * the localization mask @em my[ns, ny] and @em mw[ns, ny].
1051 * The table @em fibers specifies the spectra to extract.
1052 *
1053 * The images @em ms[ns, ny], @em mse[ns, ny], @em msn[ns, ny] and
1054 * @em msy[ns, ny] must be allocated by the caller.
1055 */
1056
1057inline static cxint
1058_giraffe_extract_summation(const cpl_image* mz, const cpl_image* mvarz,
1059 const cpl_table* fibers, const cpl_image* my,
1060 const cpl_image* mw, cpl_image* mbpx,
1061 cpl_image* ms, cpl_image* mse,
1062 cpl_image* msn, cpl_image* msy)
1063{
1064
1065 register cxint nn;
1066
1067 const cxchar* idx = NULL;
1068
1069 cxint ny = cpl_image_get_size_x(mz);
1070 cxint nfibers = cpl_table_get_nrow(fibers);
1071 cxint nspectra = cpl_image_get_size_x(my);
1072 cxint nbins = cpl_image_get_size_y(my);
1073
1074 const cxdouble* pixels = cpl_image_get_data_double_const(mz);
1075 const cxdouble* variances = cpl_image_get_data_double_const(mvarz);
1076 const cxdouble* locy = cpl_image_get_data_double_const(my);
1077 const cxdouble* locw = cpl_image_get_data_double_const(mw);
1078
1079 cxdouble* flux = cpl_image_get_data_double(ms);
1080 cxdouble* flux_error = cpl_image_get_data_double(mse);
1081 cxdouble* flux_npixels = cpl_image_get_data_double(msn);
1082 cxdouble* flux_ypos = cpl_image_get_data_double(msy);
1083
1084
1085 /*
1086 * The number of fibers to be process must be less or equal to the
1087 * number of spectra available in the localization.
1088 */
1089
1090 cx_assert(nfibers <= nspectra);
1091
1092 idx = giraffe_fiberlist_query_index(fibers);
1093
1094 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1095
1096 if (mbpx != NULL) {
1097
1098 const cxint* bpx = cpl_image_get_data_int(mbpx);
1099
1100 for (nn = 0; nn < nfibers; nn++) {
1101
1102 register cxint x;
1103 register cxint ns = cpl_table_get_int(fibers, idx, nn, NULL) - 1;
1104
1105
1106 for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
1107
1108 cxint y;
1109 cxint yup, ylo;
1110 cxint lx = x * nspectra + ns;
1111 cxint sx = x * nfibers + nn;
1112
1113 cxdouble ylower = locy[lx] - locw[lx];
1114 cxdouble yupper = locy[lx] + locw[lx];
1115 cxdouble zsum = 0.;
1116 cxdouble ysum = 0.;
1117 cxdouble error2 = 0.;
1118
1119
1120 flux[sx] = 0.;
1121 flux_npixels[sx] = 0.;
1122 flux_error[sx] = 0.;
1123 flux_ypos[sx] = 0.;
1124
1125
1126 /*
1127 * Skip zero-width (invalid) spectra
1128 */
1129
1130 if (locw[lx] <= 0.0) {
1131 continue;
1132 }
1133
1134
1135 /*
1136 * Upper and lower border of the virtual slit. The real ones
1137 * and the borders corrected for pixel fractions. If we are
1138 * out of the the image boundaries we skip the extraction
1139 * for this bin and fiber.
1140 */
1141
1142 ylo = (cxint) ceil(ylower);
1143 yup = (cxint) floor(yupper);
1144
1145
1146 if (yup < 0. || ylo - 1 >= ny) {
1147 continue;
1148 }
1149
1150
1151 /*
1152 * Summation along the virtual slit. Check that y is always
1153 * in the range of valid pixels [0, ny[. Take into account
1154 * pixel fractions at the beginning and the end of the
1155 * virtual slit.
1156 */
1157
1158 /*
1159 * Lower ordinate pixel fraction
1160 */
1161
1162 y = ylo - 1;
1163
1164 if (y >= 0) {
1165
1166 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1167
1168 cxdouble extcoeff = (cxdouble)ylo - ylower;
1169 cxdouble extcoeff2 = extcoeff * extcoeff;
1170 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1171
1172 flux[sx] = pixels[x * ny + y] * extcoeff;
1173 flux_npixels[sx] = extcoeff;
1174 error2 = variances[x * ny + y] * extcoeff2;
1175
1176 zsum = px * extcoeff;
1177 ysum = y * px * extcoeff;
1178
1179 }
1180
1181 }
1182
1183
1184 /*
1185 * Sum pixel values along virtual slit.
1186 */
1187
1188 for (y = CX_MAX(ylo, 0); (y < yup) && (y < ny); y++) {
1189
1190 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1191
1192 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1193
1194 flux[sx] += pixels[x * ny + y];
1195 flux_npixels[sx] += 1.0;
1196 error2 += variances[x * ny + y];
1197
1198 zsum += px;
1199 ysum += y * px;
1200
1201 }
1202
1203 }
1204
1205
1206 /*
1207 * Upper ordinate pixel fraction
1208 */
1209
1210 y = yup;
1211
1212 if (y < ny) {
1213
1214 if (!(bpx[x * ny + y] & GIR_M_PIX_SET)) {
1215
1216 cxdouble extcoeff = yupper - (cxdouble)yup;
1217 cxdouble extcoeff2 = extcoeff * extcoeff;
1218 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1219
1220 flux[sx] += pixels[x * ny + y] * extcoeff;
1221 flux_npixels[sx] += extcoeff;
1222 error2 += variances[x * ny + y] * extcoeff2;
1223
1224 zsum += px * extcoeff;
1225 ysum += y * px * extcoeff;
1226
1227 }
1228
1229 }
1230
1231 flux_error[sx] = sqrt(error2);
1232
1233 // FIXME: Check this protection from division by zero. Also
1234 // the minimum condition for the pixel values above.
1235
1236 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
1237 flux_ypos[sx] = 0.5 * (yupper + ylower);
1238 }
1239 else {
1240 flux_ypos[sx] = ysum / zsum;
1241 }
1242
1243 }
1244
1245 }
1246
1247 }
1248 else {
1249
1250 for (nn = 0; nn < nfibers; nn++) {
1251
1252 register cxint x;
1253 register cxint ns = cpl_table_get_int(fibers, idx,
1254 nn, NULL) - 1;
1255
1256
1257 for (x = 0; x < cpl_image_get_size_y(mz) && x < nbins; x++) {
1258
1259 cxint y;
1260 cxint yup, ylo;
1261 cxint lx = x * nspectra + ns;
1262 cxint sx = x * nfibers + nn;
1263
1264 cxdouble yupper, ylower;
1265 cxdouble zsum = 0.;
1266 cxdouble ysum = 0.;
1267 cxdouble error2 = 0.;
1268
1269
1270 flux[sx] = 0.;
1271 flux_npixels[sx] = 0.;
1272 flux_error[sx] = 0.;
1273 flux_ypos[sx] = 0.;
1274
1275
1276 /*
1277 * Skip zero-width (invalid) spectra
1278 */
1279
1280 if (locw[lx] <= 0.0) {
1281 continue;
1282 }
1283
1284
1285 /*
1286 * Upper and lower border of the virtual slit. The real ones
1287 * and the borders corrected for pixel fractions. If we are
1288 * out of the the image boundaries we skip the extraction
1289 * for this bin and fiber.
1290 */
1291
1292 yupper = locy[lx] + locw[lx];
1293 ylower = locy[lx] - locw[lx];
1294
1295 ylo = (cxint) ceil(ylower);
1296 yup = (cxint) floor(yupper);
1297
1298
1299 if (yup < 0. || ylo - 1 >= ny) {
1300 continue;
1301 }
1302
1303
1304 /*
1305 * Summation along the virtual slit. Check that y is always
1306 * in the range of valid pixels [0, ny[. Take into account
1307 * pixel fractions at the beginning and the end of the
1308 * virtual slit.
1309 */
1310
1311 /*
1312 * Lower ordinate pixel fraction
1313 */
1314
1315 y = ylo - 1;
1316
1317 if (y >= 0) {
1318
1319 cxdouble extcoeff = (cxdouble)ylo - ylower;
1320 cxdouble extcoeff2 = extcoeff * extcoeff;
1321 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1322
1323 flux[sx] = pixels[x * ny + y] * extcoeff;
1324 flux_npixels[sx] = extcoeff;
1325 error2 = variances[x * ny + y] * extcoeff2;
1326
1327 zsum = px * extcoeff;
1328 ysum = y * px * extcoeff;
1329
1330 }
1331
1332
1333 /*
1334 * Sum pixel values along virtual slit.
1335 */
1336
1337 for (y = CX_MAX(ylo, 0); (y < yup) && (y < ny); y++) {
1338
1339 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1340
1341 flux[sx] += pixels[x * ny + y];
1342 flux_npixels[sx] += 1.0;
1343 error2 += variances[x * ny + y];
1344
1345 zsum += px;
1346 ysum += y * px;
1347 }
1348
1349
1350 /*
1351 * Upper ordinate pixel fraction
1352 */
1353
1354 y = yup;
1355
1356 if (y < ny) {
1357
1358 cxdouble extcoeff = yupper - (cxdouble)yup;
1359 cxdouble extcoeff2 = extcoeff * extcoeff;
1360 cxdouble px = CX_MAX(pixels[x * ny + y], 0.);
1361
1362 flux[sx] += pixels[x * ny + y] * extcoeff;
1363 flux_npixels[sx] += extcoeff;
1364 error2 += variances[x * ny + y] * extcoeff2;
1365
1366 zsum += px * extcoeff;
1367 ysum += y * px * extcoeff;
1368
1369 }
1370
1371 flux_error[sx] = sqrt(error2);
1372
1373 // FIXME: Check this protection from division by zero. Also
1374 // the minimum condition for the pixel values above.
1375
1376 if (fabs(ysum) < DBL_EPSILON || fabs(zsum) < DBL_EPSILON) {
1377 flux_ypos[sx] = 0.5 * (yupper + ylower);
1378 }
1379 else {
1380 flux_ypos[sx] = ysum / zsum;
1381 }
1382
1383 }
1384
1385 }
1386
1387 }
1388
1389 return 0;
1390
1391}
1392
1393
1394/*
1395 * @brief
1396 * Extract spectra using the optimal extraction method.
1397 *
1398 * @param mz Pixels values [nx, ny]
1399 * @param mvar Initial variance [nx, ny]
1400 * @param fibers List of fibers to extract
1401 * @param my Fiber centroid positions [ns, ny]
1402 * @param mw Fiber widths [ns, ny]
1403 * @param ms Extracted flux [ns, ny]
1404 * @param mse Extracted flux error [ns, ny]
1405 * @param msn Number of extracted pixels [ns, ny]
1406 * @param msy Extracted flux centroid position [ns, ny]
1407 * @param config Optimal extraction method setup
1408 *
1409 * TBD
1410 *
1411 * The images @em ms[ns, ny], @em mse[ns, ny], @em msn[ns, ny] and
1412 * @em msy[ns, ny] must be allocated by the caller.
1413 */
1414
1415inline static cxint
1416_giraffe_extract_horne(const cpl_image* mz, const cpl_image* mzvar,
1417 const cpl_table* fibers, const cpl_image* my,
1418 const cpl_image* mw, const GiPsfData* psfdata,
1419 cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
1420 cpl_image* msn, cpl_image* msy,
1421 const GiExtractHorneConfig* config)
1422{
1423
1424 const cxchar* idx = NULL;
1425
1426 cxint nx = 0;
1427 cxint ny = 0;
1428 cxint fiber = 0;
1429 cxint nfibers = 0;
1430
1431 const cxdouble* locy = NULL;
1432 const cxdouble* locw = NULL;
1433 const cxdouble* width = NULL;
1434 const cxdouble* exponent = NULL;
1435
1436 GiModel* psfmodel = NULL;
1437
1438
1439 cx_assert(mz != NULL);
1440 cx_assert(mzvar != NULL);
1441
1442 cx_assert(fibers != NULL);
1443
1444 cx_assert(my != NULL);
1445 cx_assert(mw != NULL);
1446
1447 cx_assert(psfdata != NULL);
1448
1449 cx_assert(ms != NULL);
1450 cx_assert(mse != NULL);
1451 cx_assert(msn != NULL);
1452 cx_assert(msy != NULL);
1453
1454 cx_assert(config != NULL);
1455
1456 ny = cpl_image_get_size_x(mz);
1457 nx = cpl_image_get_size_y(mz);
1458 nfibers = cpl_table_get_nrow(fibers);
1459
1460 locy = cpl_image_get_data_double_const(my);
1461 locw = cpl_image_get_data_double_const(mw);
1462
1463 cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
1464 (nx == cpl_image_get_size_y(mzvar)));
1465
1466 cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
1467 cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
1468
1469 cx_assert(giraffe_psfdata_fibers(psfdata) ==
1470 (cxsize)cpl_image_get_size_x(my));
1471 cx_assert(giraffe_psfdata_bins(psfdata) ==
1472 (cxsize)cpl_image_get_size_y(my));
1473
1474 cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
1475 (nx == cpl_image_get_size_y(ms)));
1476 cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
1477 (nx == cpl_image_get_size_y(mse)));
1478 cx_assert((nfibers == cpl_image_get_size_x(msn)) &&
1479 (nx == cpl_image_get_size_y(msn)));
1480 cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
1481 (nx == cpl_image_get_size_y(msy)));
1482
1483 cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
1484 (nx == cpl_image_get_size_y(mbpx))));
1485
1486
1487 /*
1488 * Get the index column mapping the current spectum number to the
1489 * corresponding reference localization spectrum number.
1490 */
1491
1492 idx = giraffe_fiberlist_query_index(fibers);
1493
1494 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1495
1496
1497 /*
1498 * Get the PSF profile data arrays for efficency reasons in the
1499 * following loops.
1500 */
1501
1502 if (giraffe_psfdata_contains(psfdata, "Center") == FALSE) {
1503 return -1;
1504 }
1505
1506 if (giraffe_psfdata_contains(psfdata, "Width2") == TRUE) {
1507 exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1508 "Width2"));
1509 }
1510
1511 width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1512 "Width1"));
1513
1514
1515 /*
1516 * Create the PSF profile model from the PSF data object.
1517 */
1518
1519 psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
1520
1521 if (psfmodel == NULL) {
1522 return -2;
1523 }
1524
1525 giraffe_model_set_parameter(psfmodel, "Center", 0.);
1526 giraffe_model_set_parameter(psfmodel, "Amplitude", 1.);
1527 giraffe_model_set_parameter(psfmodel, "Background", 0.);
1528
1529
1530 /*
1531 * Extract each fiber spectrum
1532 */
1533
1534 for (fiber = 0; fiber < nfibers; ++fiber) {
1535
1536 register cxint bin = 0;
1537 register cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
1538
1539 cxint nbins = CX_MIN(nx, cpl_image_get_size_y(my));
1540
1541 cxdouble* _ms = cpl_image_get_data_double(ms);
1542 cxdouble* _mse = cpl_image_get_data_double(mse);
1543 cxdouble* _msy = cpl_image_get_data_double(msy);
1544 cxdouble* _msn = cpl_image_get_data_double(msn);
1545
1546
1547 for (bin = 0; bin < nbins; bin++) {
1548
1549 register cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
1550 register cxint spos = bin * nfibers + fiber;
1551
1552 cxint status = 0;
1553 cxint vwidth = 0;
1554
1555 register cxdouble lcenter = locy[lpos];
1556 register cxdouble lwidth = locw[lpos];
1557
1558 register cxdouble ylower = lcenter - lwidth;
1559 register cxdouble yupper = lcenter + lwidth;
1560
1561 GiVirtualSlit* vslit = NULL;
1562
1563 GiExtractionData result = {0., 0., 0., 0.};
1564
1565
1566 /*
1567 * Skip zero-width, invalid spectra
1568 */
1569
1570 if ((lwidth <= 0.) || (yupper < 0.) || (ylower > ny)) {
1571 continue;
1572 }
1573
1574 /*
1575 * Fill the virtual slit with data
1576 */
1577
1578 vslit = _giraffe_virtualslit_new(config->ewidth);
1579
1580 vwidth = _giraffe_virtualslit_setup(vslit, bin, lcenter, lwidth,
1581 mz, mzvar, mbpx);
1582
1583 if (vwidth == 0) {
1584 _giraffe_virtualslit_delete(vslit);
1585 vslit = NULL;
1586
1587 continue;
1588 }
1589
1590
1591 /*
1592 * Update PSF profile model width and exponent
1593 */
1594
1595 giraffe_model_set_parameter(psfmodel, "Width1", width[lpos]);
1596
1597 if (exponent != NULL) {
1598 giraffe_model_set_parameter(psfmodel, "Width2",
1599 exponent[lpos]);
1600 }
1601
1602
1603 /*
1604 * Compute flux from the virtual slit using Horne's optimal
1605 * extraction algorithm.
1606 */
1607
1608 status = _giraffe_horne_extract_slit(&result, vslit, psfmodel,
1609 config);
1610
1611 _giraffe_virtualslit_delete(vslit);
1612 vslit = NULL;
1613
1614 if (status < 0) {
1615
1616 giraffe_model_delete(psfmodel);
1617 psfmodel = NULL;
1618
1619 return 1;
1620 }
1621
1622 _ms[spos] = result.value;
1623 _mse[spos] = result.error;
1624 _msy[spos] = result.position;
1625 _msn[spos] = result.npixels;
1626
1627 }
1628
1629 }
1630
1631
1632 giraffe_model_delete(psfmodel);
1633 psfmodel = NULL;
1634
1635 return 0;
1636
1637}
1638
1639
1640/*
1641 * Fill extraction matrix with the fiber profiles and the coefficients of
1642 * the Chebyshev polynomial model of the background.
1643 */
1644
1645inline static cxint
1646_giraffe_optimal_build_profiles(cpl_matrix* profiles,
1647 GiExtractionPsfLimits* limits,
1648 const cpl_image* my, const cpl_image* mw,
1649 const cpl_table* fibers, cxint bin,
1650 GiModel* psf, const cxdouble* width,
1651 const cxdouble* exponent, cxdouble wfactor)
1652{
1653
1654 const cxchar* idx = giraffe_fiberlist_query_index(fibers);
1655
1656 cxint fiber = 0;
1657 cxint nfibers = cpl_table_get_nrow(fibers);
1658 cxint ny = cpl_matrix_get_ncol(profiles);
1659
1660 const cxdouble* locy = cpl_image_get_data_double_const(my);
1661 const cxdouble* locw = cpl_image_get_data_double_const(mw);
1662
1663 cxdouble* _profiles = cpl_matrix_get_data(profiles);
1664
1665 cxdouble* ypos = NULL;
1666
1667
1668 cx_assert(cpl_table_has_column(fibers, idx) != 0);
1669 cx_assert((limits == NULL) ||
1670 (cpl_matrix_get_nrow(profiles) == limits->size));
1671
1672 ypos = cx_calloc(ny, sizeof(cxdouble));
1673
1674 for (fiber = 0; fiber < nfibers; ++fiber) {
1675
1676 register cxint i = 0;
1677 register cxint y = 0;
1678 register cxint k = 0;
1679
1680 cxint fidx = cpl_table_get_int(fibers, idx, fiber, NULL) - 1;
1681 cxint lpos = bin * cpl_image_get_size_x(my) + fidx;
1682
1683 register cxdouble lcenter = locy[lpos];
1684 register cxdouble lwidth = locw[lpos];
1685
1686 register cxdouble ylower = lcenter - fabs(wfactor) * lwidth;
1687 register cxdouble yupper = lcenter + fabs(wfactor) * lwidth;
1688
1689 register cxint first = (cxint) floor(ylower);
1690 register cxint last = (cxint) ceil(yupper);
1691
1692 register cxint vwidth = 0;
1693
1694 cxdouble norm = 0.;
1695 cxdouble* _mnpsf = NULL;
1696
1697 cpl_matrix* positions = NULL;
1698 cpl_matrix* mnpsf = NULL;
1699
1700
1701 /*
1702 * Upper, lower border and width of the virtual slit
1703 */
1704
1705 ylower = CX_MAX(0., ylower);
1706 yupper = CX_MIN(ny - 1., yupper);
1707
1708 first = CX_MAX(0, first);
1709 last = CX_MIN(ny - 1, last);
1710
1711 vwidth = last - first + 1;
1712
1713 if (vwidth < 1) {
1714 continue;
1715 }
1716
1717 if (limits != NULL) {
1718 limits->ymin[fiber] = first;
1719 limits->ymax[fiber] = last + 1;
1720 }
1721
1722
1723 /*
1724 * Update PSF profile model width and exponent
1725 */
1726
1727 giraffe_model_set_parameter(psf, "Width1", width[lpos]);
1728
1729 if (exponent != NULL) {
1730 giraffe_model_set_parameter(psf, "Width2", exponent[lpos]);
1731 }
1732
1733
1734 /*
1735 * Compute normalized psf model
1736 */
1737
1738 k = 0;
1739 for (y = first; y <= last; ++y) {
1740 ypos[k] = y - lcenter;
1741 ++k;
1742 }
1743
1744 positions = cpl_matrix_wrap(vwidth, 1, ypos);
1745 mnpsf = _giraffe_compute_psf(psf, positions);
1746
1747 cpl_matrix_unwrap(positions);
1748 positions = NULL;
1749
1750 if (mnpsf == NULL) {
1751 cx_free(ypos);
1752 ypos = NULL;
1753
1754 return 1;
1755 }
1756
1757 _mnpsf = cpl_matrix_get_data(mnpsf);
1758
1759 for (i = 0; i < vwidth; ++i) {
1760 _mnpsf[i] = CX_MAX(_mnpsf[i], 0.);
1761 norm += _mnpsf[i];
1762 }
1763
1764 for (i = 0; i < vwidth; ++i) {
1765 _mnpsf[i] /= norm;
1766 }
1767
1768 k = fiber * ny + first;
1769 for (y = 0; y < vwidth; ++y) {
1770 _profiles[k + y] = _mnpsf[y];
1771 }
1772
1773 cpl_matrix_delete(mnpsf);
1774 mnpsf = NULL;
1775
1776 }
1777
1778 cx_free(ypos);
1779 ypos = NULL;
1780
1781 return 0;
1782
1783}
1784
1785
1786inline static cxint
1787_giraffe_extract_optimal(const cpl_image* mz, const cpl_image* mzvar,
1788 const cpl_table* fibers, const cpl_image* my,
1789 const cpl_image* mw, const GiPsfData* psfdata,
1790 cpl_image* mbpx, cpl_image* ms, cpl_image* mse,
1791 cpl_image* msm, cpl_image* msy,
1792 const GiExtractOptimalConfig* config)
1793{
1794
1795 const cxbool nolimits = (config->limits == TRUE) ? FALSE : TRUE;
1796
1797 const cxint bkg_nc = config->bkgorder + 1;
1798 const cxint niter = config->clip.iterations;
1799
1800 register cxint i = 0;
1801
1802 cxint nx = 0;
1803 cxint ny = 0;
1804 cxint bin = 0;
1805 cxint nbins = 0;
1806 cxint nfibers = 0;
1807
1808 const cxdouble wfactor = config->ewidth;
1809 const cxdouble sigma = config->clip.level * config->clip.level;
1810 const cxdouble fraction = config->clip.fraction;
1811
1812 const cxdouble* width = NULL;
1813 const cxdouble* exponent = NULL;
1814
1815 cxdouble* _ypos = NULL;
1816 cxdouble* _bkg_base = NULL;
1817 cxdouble* _profiles = NULL;
1818 cxdouble* _signal = NULL;
1819 cxdouble* _variance = NULL;
1820 cxdouble* _mask = NULL;
1821 cxdouble* _weights = NULL;
1822
1823 cpl_matrix* ypos = NULL;
1824 cpl_matrix* bkg_base = NULL;
1825 cpl_matrix* profiles = NULL;
1826 cpl_matrix* weights = NULL;
1827 cpl_matrix* signal = NULL;
1828 cpl_matrix* variance = NULL;
1829 cpl_matrix* mask = NULL;
1830
1831 GiModel* psfmodel = NULL;
1832
1833 GiExtractionPsfLimits* limits = NULL;
1834
1835 GiExtractionSlice* slice = NULL;
1836
1837 GiExtractionWorkspace* workspace;
1838
1839
1840 cx_assert(mz != NULL);
1841 cx_assert(mzvar != NULL);
1842
1843 cx_assert(fibers != NULL);
1844
1845 cx_assert(my != NULL);
1846 cx_assert(mw != NULL);
1847
1848 cx_assert(psfdata != NULL);
1849
1850 cx_assert(ms != NULL);
1851 cx_assert(mse != NULL);
1852 cx_assert(msm != NULL);
1853 cx_assert(msy != NULL);
1854
1855 ny = cpl_image_get_size_x(mz);
1856 nx = cpl_image_get_size_y(mz);
1857
1858 nfibers = cpl_table_get_nrow(fibers);
1859 nbins = CX_MIN(nx, cpl_image_get_size_y(my));
1860
1861 cx_assert((ny == cpl_image_get_size_x(mzvar)) &&
1862 (nx == cpl_image_get_size_y(mzvar)));
1863
1864 cx_assert(cpl_image_get_size_x(my) == cpl_image_get_size_x(mw));
1865 cx_assert(cpl_image_get_size_y(my) == cpl_image_get_size_y(mw));
1866
1867 cx_assert(giraffe_psfdata_fibers(psfdata) ==
1868 (cxsize)cpl_image_get_size_x(my));
1869 cx_assert(giraffe_psfdata_bins(psfdata) ==
1870 (cxsize)cpl_image_get_size_y(my));
1871
1872 cx_assert((nfibers == cpl_image_get_size_x(ms)) &&
1873 (nx == cpl_image_get_size_y(ms)));
1874 cx_assert((nfibers == cpl_image_get_size_x(mse)) &&
1875 (nx == cpl_image_get_size_y(mse)));
1876 cx_assert((nfibers == cpl_image_get_size_x(msy)) &&
1877 (nx == cpl_image_get_size_y(msy)));
1878 cx_assert((ny == cpl_image_get_size_x(msm)) &&
1879 (nx == cpl_image_get_size_y(msm)));
1880
1881 cx_assert((mbpx == NULL) || ((ny == cpl_image_get_size_x(mbpx)) &&
1882 (nx == cpl_image_get_size_y(mbpx))));
1883
1884
1885 /*
1886 * Get the PSF profile data arrays for efficiency reasons in the
1887 * following loops.
1888 */
1889
1890 if (giraffe_psfdata_contains(psfdata, "Center") == FALSE) {
1891 return -1;
1892 }
1893
1894 if (giraffe_psfdata_contains(psfdata, "Width2") == TRUE) {
1895 exponent = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1896 "Width2"));
1897 }
1898
1899 width = cpl_image_get_data_const(giraffe_psfdata_get_data(psfdata,
1900 "Width1"));
1901
1902
1903 /*
1904 * Create the PSF profile model from the PSF data object.
1905 */
1906
1907 psfmodel = giraffe_model_new(giraffe_psfdata_get_model(psfdata));
1908
1909 if (psfmodel == NULL) {
1910 return -2;
1911 }
1912
1913 giraffe_model_set_parameter(psfmodel, "Amplitude", 1.);
1914 giraffe_model_set_parameter(psfmodel, "Background", 0.);
1915 giraffe_model_set_parameter(psfmodel, "Center", 0.);
1916
1917
1918 /*
1919 * Set up the vector of pixel positions
1920 */
1921
1922 ypos = cpl_matrix_new(ny, 1);
1923
1924 if (ypos == NULL) {
1925 giraffe_model_delete(psfmodel);
1926 psfmodel = NULL;
1927
1928 return -3;
1929 }
1930
1931 _ypos = cpl_matrix_get_data(ypos);
1932
1933 for (i = 0; i < ny; ++i) {
1934 _ypos[i] = i;
1935 }
1936
1937
1938 /*
1939 * Create profile matrix and the matrices for the signal, bad
1940 * pixel mask, variance and the weights.
1941 */
1942
1943 profiles = cpl_matrix_new(nfibers + bkg_nc, ny);
1944
1945 if (profiles == NULL) {
1946 cpl_matrix_delete(ypos);
1947 ypos = NULL;
1948
1949 giraffe_model_delete(psfmodel);
1950 psfmodel = NULL;
1951
1952 return -3;
1953 }
1954
1955 _profiles = cpl_matrix_get_data(profiles);
1956
1957
1958 signal = cpl_matrix_new(ny, 1);
1959
1960 if (signal == NULL) {
1961 cpl_matrix_delete(profiles);
1962 profiles = NULL;
1963
1964 cpl_matrix_delete(ypos);
1965 ypos = NULL;
1966
1967 giraffe_model_delete(psfmodel);
1968 psfmodel = NULL;
1969
1970 return -3;
1971 }
1972
1973 _signal = cpl_matrix_get_data(signal);
1974
1975
1976 variance = cpl_matrix_new(ny, 1);
1977
1978 if (variance == NULL) {
1979 cpl_matrix_delete(signal);
1980 signal = NULL;
1981
1982 cpl_matrix_delete(profiles);
1983 profiles = NULL;
1984
1985 cpl_matrix_delete(ypos);
1986 ypos = NULL;
1987
1988 giraffe_model_delete(psfmodel);
1989 psfmodel = NULL;
1990
1991 return -3;
1992 }
1993
1994 _variance = cpl_matrix_get_data(variance);
1995
1996
1997 mask = cpl_matrix_new(ny, 1);
1998
1999 if (mask == NULL) {
2000 cpl_matrix_delete(variance);
2001 variance = NULL;
2002
2003 cpl_matrix_delete(signal);
2004 signal = NULL;
2005
2006 cpl_matrix_delete(profiles);
2007 profiles = NULL;
2008
2009 cpl_matrix_delete(ypos);
2010 ypos = NULL;
2011
2012 giraffe_model_delete(psfmodel);
2013 psfmodel = NULL;
2014
2015 return -3;
2016 }
2017
2018 _mask = cpl_matrix_get_data(mask);
2019
2020
2021 weights = cpl_matrix_new(ny, 1);
2022
2023 if (weights == NULL) {
2024 cpl_matrix_delete(mask);
2025 mask = NULL;
2026
2027 cpl_matrix_delete(variance);
2028 variance = NULL;
2029
2030 cpl_matrix_delete(signal);
2031 signal = NULL;
2032
2033 cpl_matrix_delete(profiles);
2034 profiles = NULL;
2035
2036 cpl_matrix_delete(ypos);
2037 ypos = NULL;
2038
2039 giraffe_model_delete(psfmodel);
2040 psfmodel = NULL;
2041
2042 return -3;
2043 }
2044
2045 _weights = cpl_matrix_get_data(weights);
2046
2047
2048 /*
2049 * Fill design matrix with the basis functions of the
2050 * background polynomial model.
2051 */
2052
2053 bkg_base = giraffe_chebyshev_base1d(0., ny, bkg_nc, ypos);
2054
2055 cpl_matrix_delete(ypos);
2056 ypos = NULL;
2057
2058 if (bkg_base == NULL) {
2059 cpl_matrix_delete(weights);
2060 weights = NULL;
2061
2062 cpl_matrix_delete(mask);
2063 mask = NULL;
2064
2065 cpl_matrix_delete(variance);
2066 variance = NULL;
2067
2068 cpl_matrix_delete(signal);
2069 signal = NULL;
2070
2071 cpl_matrix_delete(profiles);
2072 profiles = NULL;
2073
2074 cpl_matrix_delete(ypos);
2075 ypos = NULL;
2076
2077 giraffe_model_delete(psfmodel);
2078 psfmodel = NULL;
2079
2080 return -3;
2081 }
2082
2083 _bkg_base = cpl_matrix_get_data(bkg_base);
2084
2085 for (i = 0; i < bkg_nc; ++i) {
2086
2087 register cxint j = 0;
2088 register cxint offset = nfibers * ny;
2089
2090 for (j = 0; j < ny; ++j) {
2091 _profiles[i * ny + j + offset] = _bkg_base[i * ny + j];
2092 }
2093
2094 }
2095
2096 _bkg_base = NULL;
2097
2098 cpl_matrix_delete(bkg_base);
2099 bkg_base = NULL;
2100
2101
2102 /*
2103 * Extract all fiber spectra simultaneously for each wavelength bin
2104 */
2105
2106 slice = _giraffe_extractionslice_new(nfibers, ny, bkg_nc);
2107
2108 if (slice == NULL) {
2109 cpl_matrix_delete(weights);
2110 weights = NULL;
2111
2112 cpl_matrix_delete(mask);
2113 mask = NULL;
2114
2115 cpl_matrix_delete(variance);
2116 variance = NULL;
2117
2118 cpl_matrix_delete(signal);
2119 signal = NULL;
2120
2121 cpl_matrix_delete(profiles);
2122 profiles = NULL;
2123
2124 cpl_matrix_delete(ypos);
2125 ypos = NULL;
2126
2127 giraffe_model_delete(psfmodel);
2128 psfmodel = NULL;
2129
2130 return -3;
2131 }
2132
2133
2134 limits = _giraffe_extraction_psflimits_new(nfibers + bkg_nc);
2135
2136 if (limits == NULL) {
2137
2138 _giraffe_extractionslice_delete(slice);
2139 slice = NULL;
2140
2141 cpl_matrix_delete(weights);
2142 weights = NULL;
2143
2144 cpl_matrix_delete(mask);
2145 mask = NULL;
2146
2147 cpl_matrix_delete(variance);
2148 variance = NULL;
2149
2150 cpl_matrix_delete(signal);
2151 signal = NULL;
2152
2153 cpl_matrix_delete(profiles);
2154 profiles = NULL;
2155
2156 cpl_matrix_delete(ypos);
2157 ypos = NULL;
2158
2159 giraffe_model_delete(psfmodel);
2160 psfmodel = NULL;
2161
2162 return -3;
2163
2164 }
2165
2166 for (i = 0; i < limits->size; ++i) {
2167 limits->ymin[i] = 0;
2168 limits->ymax[i] = ny;
2169 }
2170
2171
2172 /*
2173 * Allocate workspace for matrix multiplications
2174 */
2175
2176 workspace = _giraffe_optimal_workspace_new(nfibers + bkg_nc, ny);
2177
2178 for (bin = 0; bin < nbins; ++bin) {
2179
2180 cxbool stop = FALSE;
2181
2182 cxint iter = 0;
2183 cxint nmin = 0;
2184 cxint ngood = ny;
2185
2186 const cxdouble* _my = cpl_image_get_data_double_const(my);
2187 const cxdouble* _mz = cpl_image_get_data_double_const(mz);
2188 const cxdouble* _mzvar = cpl_image_get_data_double_const(mzvar);
2189
2190 cxdouble* _ms = cpl_image_get_data_double(ms);
2191 cxdouble* _mse = cpl_image_get_data_double(mse);
2192 cxdouble* _msy = cpl_image_get_data_double(msy);
2193 cxdouble* _msm = cpl_image_get_data_double(msm);
2194
2195 cxint status = 0;
2196
2197 GiExtractionPsfLimits* _limits = (nolimits == FALSE) ? limits : NULL;
2198
2199 cx_assert(_mz != NULL);
2200 cx_assert(_mzvar != NULL);
2201
2202
2203 /*
2204 * Fill the design matrix with the fiber profiles for the
2205 * current wavelength bin
2206 */
2207
2208 status = _giraffe_optimal_build_profiles(profiles, _limits, my, mw,
2209 fibers, bin, psfmodel, width,
2210 exponent, wfactor);
2211
2212 if (status != 0) {
2213 _giraffe_optimal_workspace_delete(workspace);
2214 workspace = NULL;
2215
2216 _giraffe_extraction_psflimits_delete(limits);
2217 limits = NULL;
2218
2219 _giraffe_extractionslice_delete(slice);
2220 slice = NULL;
2221
2222 cpl_matrix_delete(weights);
2223 weights = NULL;
2224
2225 cpl_matrix_delete(mask);
2226 mask = NULL;
2227
2228 cpl_matrix_delete(variance);
2229 variance = NULL;
2230
2231 cpl_matrix_delete(signal);
2232 signal = NULL;
2233
2234 cpl_matrix_delete(profiles);
2235 profiles = NULL;
2236
2237 cpl_matrix_delete(ypos);
2238 ypos = NULL;
2239
2240 giraffe_model_delete(psfmodel);
2241 psfmodel = NULL;
2242
2243 return -4;
2244 }
2245
2246
2247 /*
2248 * Fill the signal, variance, mask and weight matrices
2249 */
2250
2251
2252 if (mbpx != NULL) {
2253
2254 const cxint* _mbpx = cpl_image_get_data_int_const(mbpx);
2255
2256
2257 cx_assert(_mbpx != NULL);
2258
2259 for (i = 0; i < ny; ++i) {
2260
2261 cxbool bad = (_mbpx[bin * ny + i] & GIR_M_PIX_SET) ||
2262 (_mz[bin * ny + i] < 0.);
2263
2264 _signal[i] = _mz[bin * ny + i];
2265 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
2266 _mask[i] = 1.;
2267
2268 if (bad == TRUE) {
2269 _mask[i] = 0.;
2270 --ngood;
2271 }
2272
2273 _weights[i] = _mask[i] / _variance[i];
2274
2275 }
2276
2277 }
2278 else {
2279
2280 for (i = 0; i < ny; ++i) {
2281
2282 cxbool bad = (_mz[bin * ny + i] < 0.);
2283
2284 _signal[i] = _mz[bin * ny + i];
2285 _variance[i] = _signal[i] + _mzvar[bin * ny + i];
2286 _mask[i] = 1.;
2287
2288 if (bad == TRUE) {
2289 _mask[i] = 0.;
2290 --ngood;
2291 }
2292
2293 _weights[i] = _mask[i] / _variance[i];
2294
2295 }
2296
2297 }
2298
2299
2300 /*
2301 * Extract simultaneously the fluxes of all fibers for the current
2302 * wavelength bin
2303 */
2304
2305 nmin = (cxint)(fraction * ngood);
2306
2307 while ((iter < niter) && (stop == FALSE)) {
2308
2309 cxint nreject = 0;
2310
2311 const cxdouble* _model = NULL;
2312
2313
2314 status = _giraffe_optimal_extract_slice(slice, profiles,
2315 signal, weights, limits, workspace);
2316
2317 if (status != 0) {
2318 _giraffe_optimal_workspace_delete(workspace);
2319 workspace = NULL;
2320
2321 _giraffe_extraction_psflimits_delete(limits);
2322 limits = NULL;
2323
2324 _giraffe_extractionslice_delete(slice);
2325 slice = NULL;
2326
2327 cpl_matrix_delete(weights);
2328 weights = NULL;
2329
2330 cpl_matrix_delete(mask);
2331 mask = NULL;
2332
2333 cpl_matrix_delete(variance);
2334 variance = NULL;
2335
2336 cpl_matrix_delete(signal);
2337 signal = NULL;
2338
2339 cpl_matrix_delete(profiles);
2340 profiles = NULL;
2341
2342 cpl_matrix_delete(ypos);
2343 ypos = NULL;
2344
2345 giraffe_model_delete(psfmodel);
2346 psfmodel = NULL;
2347
2348 return -5;
2349 }
2350
2351
2352 /*
2353 * Update weighting factors
2354 */
2355
2356 _model = cpl_matrix_get_data(slice->model);
2357
2358 for (i = 0; i < ny; ++i) {
2359
2360 if (_mask[i] > 0.) {
2361
2362 cxbool bad = FALSE;
2363 cxdouble residual = _signal[i] - _model[i];
2364
2365
2366 _variance[i] = _model[i] + _mzvar[bin * ny + i];
2367
2368 bad = (residual * residual) > (sigma * _variance[i]) ?
2369 TRUE : FALSE;
2370
2371 if (bad == TRUE) {
2372 _mask[i] = 0.;
2373 ++nreject;
2374 --ngood;
2375 }
2376
2377 _weights[i] = _mask[i] / _variance[i];
2378
2379 }
2380
2381 }
2382
2383 if ((nreject == 0) || (ngood <= nmin)) {
2384 stop = TRUE;
2385 }
2386
2387 ++iter;
2388
2389 }
2390
2391
2392 /*
2393 * Copy the extracted fluxes, their variance and the modeled signal
2394 * to the result images.
2395 */
2396
2397 memcpy(&_ms[bin * nfibers], cpl_matrix_get_data(slice->flux),
2398 slice->nflx * sizeof(cxdouble));
2399 memcpy(&_mse[bin * nfibers], cpl_matrix_get_data(slice->variance),
2400 slice->nflx * sizeof(cxdouble));
2401 memcpy(&_msm[bin * ny], cpl_matrix_get_data(slice->model),
2402 slice->msize * sizeof(cxdouble));
2403
2404 memcpy(&_msy[bin * nfibers], &_my[bin * nfibers],
2405 nfibers * sizeof(cxdouble));
2406
2407
2408 /*
2409 * Reset the profile part of the design matrix
2410 */
2411
2412 cpl_matrix_fill_window(profiles, 0., 0, 0, nfibers, ny);
2413
2414 }
2415
2416
2417 /*
2418 * Compute errors of the extracted spectra from the variance
2419 */
2420
2421 cpl_image_power(mse, 0.5);
2422
2423 _giraffe_optimal_workspace_delete(workspace);
2424 workspace = NULL;
2425
2426 _giraffe_extraction_psflimits_delete(limits);
2427 limits = NULL;
2428
2429 _giraffe_extractionslice_delete(slice);
2430 slice = NULL;
2431
2432 cpl_matrix_delete(weights);
2433 weights = NULL;
2434
2435 cpl_matrix_delete(mask);
2436 mask = NULL;
2437
2438 cpl_matrix_delete(variance);
2439 variance = NULL;
2440
2441 cpl_matrix_delete(signal);
2442 signal = NULL;
2443
2444 cpl_matrix_delete(profiles);
2445 profiles = NULL;
2446
2447 giraffe_model_delete(psfmodel);
2448 psfmodel = NULL;
2449
2450 return 0;
2451
2452}
2453
2454
2479cxint
2480giraffe_extract_spectra(GiExtraction* result, GiImage* image,
2481 GiTable* fibers, GiLocalization* sloc,
2482 GiImage* bpixel, GiImage* slight,
2483 GiExtractConfig* config)
2484{
2485
2486 const cxchar *fctid = "giraffe_extract_spectra";
2487
2488
2489 cxint ns = 0;
2490 cxint nx = 0;
2491 cxint ny = 0;
2492 cxint status = 0;
2493 cxint nframes = 1;
2494
2495 cxdouble bias_ron = 0.;
2496 cxdouble bias_sigma = 0.;
2497 cxdouble dark_value = 0.;
2498 cxdouble exptime = 0.;
2499 cxdouble conad = 1.;
2500
2501 cpl_propertylist *properties;
2502
2503 cpl_image* _image = NULL;
2504 cpl_image* _locy = NULL;
2505 cpl_image* _locw = NULL;
2506 cpl_image* _spectra = NULL;
2507 cpl_image* _error = NULL;
2508 cpl_image* _npixels = NULL;
2509 cpl_image* _centroid = NULL;
2510 cpl_image* _model = NULL;
2511
2512 cpl_table* _fibers = NULL;
2513
2514
2515 /*
2516 * Preprocessing
2517 */
2518
2519 if (!result || !image || !fibers || !sloc || !config) {
2520 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2521 return 1;
2522 }
2523
2524
2525 if ((sloc->locy == NULL) || (sloc->locw == NULL)) {
2526 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2527 return 1;
2528 }
2529
2530
2531 if (result->spectra != NULL || result->error != NULL ||
2532 result->npixels != NULL || result->centroid != NULL ||
2533 result->model != NULL) {
2534 gi_warning("%s: Results structure at %p is not empty! Contents "
2535 "might be lost.", fctid, (void*)result);
2536 }
2537
2538
2539 _fibers = giraffe_table_get(fibers);
2540
2541 if (_fibers == NULL) {
2542 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2543 return 1;
2544 }
2545
2546
2547 if ((config->emethod == GIEXTRACT_OPTIMAL) && (sloc->psf == NULL)) {
2548 cpl_msg_error(fctid, "Missing data: PSF profile data is required "
2549 "for optimal spectrum extraction!");
2550 cpl_error_set(fctid, CPL_ERROR_NULL_INPUT);
2551
2552 return 1;
2553 }
2554
2555
2556 properties = giraffe_image_get_properties(image);
2557
2558 if (properties == NULL) {
2559 cpl_error_set(fctid, CPL_ERROR_DATA_NOT_FOUND);
2560 return 1;
2561 }
2562
2563
2564 giraffe_error_push();
2565
2566 conad = giraffe_propertylist_get_conad(properties);
2567
2568 if (cpl_error_get_code() != CPL_ERROR_NONE) {
2569 return 1;
2570 }
2571
2572 giraffe_error_pop();
2573
2574
2575 if (!cpl_propertylist_has(properties, GIALIAS_BIASERROR)) {
2576 cpl_msg_warning(fctid, "Missing bias error property (%s)! Setting "
2577 "bias error to 0.", GIALIAS_BIASERROR);
2578 bias_sigma = 0.;
2579 }
2580 else {
2581 bias_sigma = cpl_propertylist_get_double(properties, GIALIAS_BIASERROR);
2582 }
2583
2584
2585 if (config->ron > 0.) {
2586
2587 cpl_msg_info(fctid, "Setting bias RMS property (%s) to %.4g ADU",
2588 GIALIAS_BIASSIGMA, config->ron);
2589
2590 cpl_propertylist_update_double(properties, GIALIAS_BIASSIGMA,
2591 config->ron);
2592 }
2593
2594 bias_ron = giraffe_propertylist_get_ron(properties);
2595
2596
2597 if (!cpl_propertylist_has(properties, GIALIAS_DARKVALUE)) {
2598
2599 dark_value = 0.;
2600
2601 cpl_msg_warning(fctid, "Missing dark value property (%s), will be "
2602 "set to 0.!", GIALIAS_DARKVALUE);
2603 cpl_propertylist_append_double(properties, GIALIAS_DARKVALUE,
2604 dark_value);
2605
2606 }
2607 else {
2608 dark_value = cpl_propertylist_get_double(properties,
2609 GIALIAS_DARKVALUE);
2610 }
2611
2612
2613 if (!cpl_propertylist_has(properties, GIALIAS_EXPTIME)) {
2614 cpl_msg_error(fctid, "Missing exposure time property (%s)!",
2615 GIALIAS_EXPTIME);
2616 return 1;
2617 }
2618 else {
2619 exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
2620 }
2621
2622
2623 if (cpl_propertylist_has(properties, GIALIAS_DATANCOM)) {
2624 nframes = cpl_propertylist_get_int(properties, GIALIAS_DATANCOM);
2625 }
2626
2627
2628 /*
2629 * Processing
2630 */
2631
2632 /*
2633 * Convert the bias and dark errors from ADU to electrons.
2634 */
2635
2636 bias_sigma *= conad;
2637 dark_value *= conad;
2638
2639 /*
2640 * For extracting the spectra, the bias and dark corrected raw image is
2641 * converted from ADU to electrons, and, in case it is an averaged frame,
2642 * it is scaled by the number of frames which were used. This turns the
2643 * raw frame into an image of the total number of the recorded
2644 * photoelectrons.
2645 *
2646 * To compensate for that, the extracted spectra, their errors, and,
2647 * possibly the spectrum model are rescaled after the extraction step
2648 * is completed.
2649 */
2650
2651 _image = cpl_image_multiply_scalar_create(giraffe_image_get(image),
2652 nframes * conad);
2653
2654 _locy = giraffe_image_get(sloc->locy);
2655 _locw = giraffe_image_get(sloc->locw);
2656
2657 ny = cpl_image_get_size_x(_image);
2658 nx = cpl_image_get_size_y(_locw);
2659 ns = cpl_table_get_nrow(_fibers);
2660
2661
2662 switch (config->emethod) {
2663 case GIEXTRACT_SUM:
2664 {
2665
2666 cxint xsize = cpl_image_get_size_x(_image);
2667 cxint ysize = cpl_image_get_size_y(_image);
2668
2669 cxdouble ron_variance = bias_ron * bias_ron;
2670 cxdouble bias_variance = bias_sigma * bias_sigma;
2671 cxdouble dark_variance = dark_value * exptime;
2672
2673 cpl_image* bpixmap = NULL;
2674 cpl_image* variance = NULL;
2675
2676
2677 result->spectra = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2678 result->error = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2679 result->npixels = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2680 result->centroid = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2681 result->model = NULL;
2682
2683 _spectra = giraffe_image_get(result->spectra);
2684 _error = giraffe_image_get(result->error);
2685 _npixels = giraffe_image_get(result->npixels);
2686 _centroid = giraffe_image_get(result->centroid);
2687
2688 if (bpixel != NULL) {
2689
2690 bpixmap = giraffe_image_get(bpixel);
2691
2692 if (cpl_image_get_size_x(bpixmap) != xsize ||
2693 cpl_image_get_size_y(bpixmap) != ysize) {
2694
2695 cxbool crop = FALSE;
2696
2697 cpl_propertylist *p =
2699
2700 GiWindow w = {1, 1, 0, 0};
2701
2702
2703 w.x1 = cpl_image_get_size_x(bpixmap);
2704 w.y1 = cpl_image_get_size_y(bpixmap);
2705
2706 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
2707 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
2708 crop = TRUE;
2709 }
2710
2711 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
2712 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
2713 crop = TRUE;
2714 }
2715
2716 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
2717 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
2718 crop = TRUE;
2719 }
2720
2721 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
2722 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
2723 crop = TRUE;
2724 }
2725
2726 if ((w.x1 - w.x0 + 1) != xsize ||
2727 (w.y1 - w.y0 + 1) != ysize) {
2728 cpl_msg_error(fctid, "Invalid bad pixel map! Image "
2729 "sizes do not match!");
2730
2731 giraffe_image_delete(result->spectra);
2732 result->spectra = NULL;
2733
2734 giraffe_image_delete(result->error);
2735 result->error = NULL;
2736
2737 giraffe_image_delete(result->npixels);
2738 result->npixels = NULL;
2739
2740 giraffe_image_delete(result->centroid);
2741 result->centroid = NULL;
2742
2743 giraffe_image_delete(result->model);
2744 result->model = NULL;
2745
2746 cpl_image_delete(_image);
2747 _image = NULL;
2748
2749 return 1;
2750 }
2751
2752 if (crop == TRUE) {
2753 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
2754 w.x1, w.y1);
2755 }
2756
2757 }
2758
2759 }
2760
2761 if (slight != NULL) {
2762 cpl_msg_warning(fctid, "Scattered light model will be "
2763 "ignored for extraction method `SUM'");
2764 }
2765
2766 variance = cpl_image_abs_create(_image);
2767
2768 /*
2769 * Add readout noise for the raw frame, and the errors due
2770 * to bias and dark subtraction, rescaled to the number of
2771 * frames used to create the input frame.
2772 */
2773
2774 cpl_image_add_scalar(variance, nframes * (ron_variance + nframes *
2775 (bias_variance + dark_variance)));
2776
2777 status = _giraffe_extract_summation(_image, variance, _fibers,
2778 _locy, _locw, bpixmap,
2779 _spectra, _error, _npixels,
2780 _centroid);
2781
2782 cpl_image_delete(variance);
2783 if (bpixmap != giraffe_image_get(bpixel)) {
2784 cpl_image_delete(bpixmap);
2785 }
2786 bpixmap = NULL;
2787
2788 break;
2789
2790 }
2791
2792 case GIEXTRACT_OPTIMAL:
2793 {
2794
2795 cxint xsize = cpl_image_get_size_x(_image);
2796 cxint ysize = cpl_image_get_size_y(_image);
2797
2798 cxdouble v0 = 0.;
2799 cxdouble ron_variance = bias_ron * bias_ron;
2800 cxdouble bias_variance = bias_sigma * bias_sigma;
2801 cxdouble dark_variance = dark_value * exptime;
2802
2803 cpl_image* variance = NULL;
2804 cpl_image* bpixmap = NULL;
2805
2806 GiExtractOptimalConfig setup;
2807
2808
2809 result->spectra = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2810 result->error = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2811 result->npixels = NULL;
2812 result->model = giraffe_image_create(CPL_TYPE_DOUBLE, ny, nx);
2813 result->centroid = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2814
2815 _spectra = giraffe_image_get(result->spectra);
2816 _error = giraffe_image_get(result->error);
2817 _model = giraffe_image_get(result->model);
2818 _centroid = giraffe_image_get(result->centroid);
2819
2820 setup.clip.iterations = config->psf.iterations;
2821 setup.clip.level = config->psf.sigma;
2822 setup.clip.fraction = config->optimal.fraction;
2823 setup.limits = config->optimal.wfactor < 0. ? FALSE : TRUE;
2824 setup.ewidth = CX_MAX(1., fabs(config->optimal.wfactor));
2825 setup.bkgorder = config->optimal.bkgorder;
2826 setup.exptime = exptime;
2827 setup.ron = bias_sigma;
2828 setup.dark = dark_value;
2829
2830
2831 if (bpixel != NULL) {
2832
2833 bpixmap = giraffe_image_get(bpixel);
2834
2835 if (cpl_image_get_size_x(bpixmap) != xsize ||
2836 cpl_image_get_size_y(bpixmap) != ysize) {
2837
2838 cxbool crop = FALSE;
2839
2840 cpl_propertylist *p =
2842
2843 GiWindow w = {1, 1, 0, 0};
2844
2845
2846 w.x1 = cpl_image_get_size_x(bpixmap);
2847 w.y1 = cpl_image_get_size_y(bpixmap);
2848
2849 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
2850 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
2851 crop = TRUE;
2852 }
2853
2854 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
2855 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
2856 crop = TRUE;
2857 }
2858
2859 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
2860 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
2861 crop = TRUE;
2862 }
2863
2864 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
2865 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
2866 crop = TRUE;
2867 }
2868
2869 if ((w.x1 - w.x0 + 1) != xsize ||
2870 (w.y1 - w.y0 + 1) != ysize) {
2871
2872 cpl_msg_error(fctid, "Invalid bad pixel map! "
2873 "Image sizes do not match!");
2874
2875 giraffe_image_delete(result->spectra);
2876 result->spectra = NULL;
2877
2878 giraffe_image_delete(result->error);
2879 result->error = NULL;
2880
2881 giraffe_image_delete(result->npixels);
2882 result->npixels = NULL;
2883
2884 giraffe_image_delete(result->centroid);
2885 result->centroid = NULL;
2886
2887 giraffe_image_delete(result->model);
2888 result->model = NULL;
2889
2890 cpl_image_delete(_image);
2891 _image = NULL;
2892
2893 return 1;
2894
2895 }
2896
2897 if (crop == TRUE) {
2898 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
2899 w.x1, w.y1);
2900 }
2901
2902 }
2903
2904 }
2905
2906 variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
2907
2908 /*
2909 * Add readout noise for the raw frame, and the errors due
2910 * to bias and dark subtraction, rescaled to the number of
2911 * frames used to create the input frame.
2912 */
2913
2914 v0 = nframes * (ron_variance + nframes *
2915 (bias_variance + dark_variance));
2916
2917
2918 /*
2919 * If a scattered light map has been used, add its contribution
2920 * to the variance, rescaled to the number of raw frames used, and
2921 * converted to photoelectrons.
2922 */
2923
2924 if (slight != NULL) {
2925
2926 register cxsize i = 0;
2927 register cxsize npixels = xsize * ysize;
2928
2929 const cxdouble* _slight =
2930 cpl_image_get_data_double(giraffe_image_get(slight));
2931
2932 cxdouble* _variance = cpl_image_get_data_double(variance);
2933
2934 for (i = 0; i < npixels; i++) {
2935 _variance[i] = v0 + fabs(_slight[i]) * conad * nframes;
2936 }
2937
2938 }
2939 else {
2940
2941 register cxsize i = 0;
2942 register cxsize npixels = xsize * ysize;
2943
2944 cxdouble* _variance = cpl_image_get_data_double(variance);
2945
2946 for (i = 0; i < npixels; i++) {
2947 _variance[i] = v0;
2948 }
2949
2950 }
2951
2952
2953 status = _giraffe_extract_optimal(_image, variance, _fibers,
2954 _locy, _locw, sloc->psf,
2955 bpixmap, _spectra, _error,
2956 _model, _centroid, &setup);
2957
2958 cpl_image_delete(variance);
2959 variance = NULL;
2960
2961 if (bpixmap != giraffe_image_get(bpixel)) {
2962 cpl_image_delete(bpixmap);
2963 }
2964 bpixmap = NULL;
2965
2966 break;
2967
2968 }
2969
2970 case GIEXTRACT_HORNE:
2971 {
2972
2973 cxint xsize = cpl_image_get_size_x(_image);
2974 cxint ysize = cpl_image_get_size_y(_image);
2975
2976 cxdouble v0 = 0.;
2977 cxdouble ron_variance = bias_ron * bias_ron;
2978 cxdouble bias_variance = bias_sigma * bias_sigma;
2979 cxdouble dark_variance = dark_value * exptime;
2980
2981 cpl_image* variance = NULL;
2982 cpl_image* bpixmap = NULL;
2983
2984 GiExtractHorneConfig setup;
2985
2986
2987 result->spectra = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2988 result->error = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2989 result->npixels = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2990 result->centroid = giraffe_image_create(CPL_TYPE_DOUBLE, ns, nx);
2991 result->model = NULL;
2992
2993 _spectra = giraffe_image_get(result->spectra);
2994 _error = giraffe_image_get(result->error);
2995 _npixels = giraffe_image_get(result->npixels);
2996 _centroid = giraffe_image_get(result->centroid);
2997
2998 setup.clip.iterations = config->psf.iterations;
2999 setup.clip.level = config->psf.sigma;
3000 setup.clip.fraction = config->horne.mingood;
3001 setup.ewidth = config->horne.ewidth;
3002 setup.exptime = exptime;
3003 setup.ron = bias_sigma;
3004 setup.dark = dark_value;
3005
3006 if (bpixel != NULL) {
3007
3008 bpixmap = giraffe_image_get(bpixel);
3009
3010 if (cpl_image_get_size_x(bpixmap) != xsize ||
3011 cpl_image_get_size_y(bpixmap) != ysize) {
3012
3013 cxbool crop = FALSE;
3014
3015 cpl_propertylist *p =
3017
3018 GiWindow w = {1, 1, 0, 0};
3019
3020
3021 w.x1 = cpl_image_get_size_x(bpixmap);
3022 w.y1 = cpl_image_get_size_y(bpixmap);
3023
3024 if (cpl_propertylist_has(p, GIALIAS_PRSCX)) {
3025 w.x0 += cpl_propertylist_get_int(p, GIALIAS_PRSCX);
3026 crop = TRUE;
3027 }
3028
3029 if (cpl_propertylist_has(p, GIALIAS_OVSCX)) {
3030 w.x1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCX);
3031 crop = TRUE;
3032 }
3033
3034 if (cpl_propertylist_has(p, GIALIAS_PRSCY)) {
3035 w.y0 += cpl_propertylist_get_int(p, GIALIAS_PRSCY);
3036 crop = TRUE;
3037 }
3038
3039 if (cpl_propertylist_has(p, GIALIAS_OVSCY)) {
3040 w.y1 -= cpl_propertylist_get_int(p, GIALIAS_OVSCY);
3041 crop = TRUE;
3042 }
3043
3044 if ((w.x1 - w.x0 + 1) != xsize ||
3045 (w.y1 - w.y0 + 1) != ysize) {
3046
3047 cpl_msg_error(fctid, "Invalid bad pixel map! "
3048 "Image sizes do not match!");
3049
3050 giraffe_image_delete(result->spectra);
3051 result->spectra = NULL;
3052
3053 giraffe_image_delete(result->error);
3054 result->error = NULL;
3055
3056 giraffe_image_delete(result->npixels);
3057 result->npixels = NULL;
3058
3059 giraffe_image_delete(result->centroid);
3060 result->centroid = NULL;
3061
3062 giraffe_image_delete(result->model);
3063 result->model = NULL;
3064
3065 cpl_image_delete(_image);
3066 _image = NULL;
3067
3068 return 1;
3069
3070 }
3071
3072 if (crop == TRUE) {
3073 bpixmap = cpl_image_extract(bpixmap, w.x0, w.y0,
3074 w.x1, w.y1);
3075 }
3076
3077 }
3078
3079 }
3080
3081 variance = cpl_image_new(xsize, ysize, CPL_TYPE_DOUBLE);
3082
3083 /*
3084 * Add readout noise for the raw frame, and the errors due
3085 * to bias and dark subtraction, rescaled to the number of
3086 * frames used to create the input frame.
3087 */
3088
3089 v0 = nframes * (ron_variance + nframes *
3090 (bias_variance + dark_variance));
3091
3092
3093 /*
3094 * If a scattered light map has been used, add its contribution
3095 * to the variance, rescaled to the number of raw frames used, and
3096 * converted to photoelectrons.
3097 */
3098
3099
3100 if (slight != NULL) {
3101
3102 register cxsize i = 0;
3103 register cxsize npixels = xsize * ysize;
3104
3105 const cxdouble* _slight =
3106 cpl_image_get_data_double(giraffe_image_get(slight));
3107
3108 cxdouble* _variance = cpl_image_get_data_double(variance);
3109
3110 for (i = 0; i < npixels; i++) {
3111 _variance[i] = v0 + fabs(_slight[i]) * nframes * conad;
3112 }
3113
3114 }
3115 else {
3116
3117 register cxsize i = 0;
3118 register cxsize npixels = xsize * ysize;
3119
3120 cxdouble* _variance = cpl_image_get_data_double(variance);
3121
3122 for (i = 0; i < npixels; i++) {
3123 _variance[i] = v0;
3124 }
3125
3126 }
3127
3128
3129 status = _giraffe_extract_horne(_image, variance, _fibers,
3130 _locy, _locw, sloc->psf,
3131 bpixmap, _spectra, _error,
3132 _npixels, _centroid, &setup);
3133
3134 cpl_image_delete(variance);
3135 variance = NULL;
3136
3137 if (bpixmap != giraffe_image_get(bpixel)) {
3138 cpl_image_delete(bpixmap);
3139 }
3140 bpixmap = NULL;
3141
3142 break;
3143
3144 }
3145
3146 default:
3147 gi_message("%s: Method %d selected for spectrum extraction.",
3148 fctid, config->emethod);
3149 cpl_msg_error(fctid, "Invalid extraction method!");
3150
3151 status = 1;
3152 break;
3153 }
3154
3155 cpl_image_delete(_image);
3156 _image = NULL;
3157
3158 if (status) {
3159
3160 giraffe_image_delete(result->spectra);
3161 result->spectra = NULL;
3162
3163 giraffe_image_delete(result->error);
3164 result->error = NULL;
3165
3166 giraffe_image_delete(result->npixels);
3167 result->npixels = NULL;
3168
3169 giraffe_image_delete(result->centroid);
3170 result->centroid = NULL;
3171
3172 giraffe_image_delete(result->model);
3173 result->model = NULL;
3174
3175 cpl_msg_error(fctid, "Spectrum extraction (method %d) failed!",
3176 config->emethod);
3177
3178 cpl_image_delete(_image);
3179 _image = NULL;
3180
3181 return 1;
3182
3183 }
3184
3185
3186 /*
3187 * Postprocessing
3188 */
3189
3190
3191 /*
3192 * Rescale the spectrum extraction products to the original, averaged
3193 * input raw frame.
3194 */
3195
3196 if (result->spectra) {
3197 cpl_image_divide_scalar(giraffe_image_get(result->spectra),
3198 nframes * conad);
3199 }
3200
3201 if (result->model) {
3202 cpl_image_divide_scalar(giraffe_image_get(result->model),
3203 nframes * conad);
3204 }
3205
3206 if (result->error) {
3207 cpl_image_divide_scalar(giraffe_image_get(result->error),
3208 nframes * conad);
3209 }
3210
3211
3212 /*
3213 * Extracted spectra frame
3214 */
3215
3216 properties = giraffe_image_get_properties(image);
3217 giraffe_image_set_properties(result->spectra, properties);
3218
3219 properties = giraffe_image_get_properties(result->spectra);
3220
3221 /*
3222 * Copy some properties from the localization frame.
3223 */
3224
3225 // FIXME: Is this really needed? (RP)
3226
3227 giraffe_propertylist_update(properties,
3228 giraffe_image_get_properties(sloc->locy),
3229 "^ESO PRO LOC.*");
3230
3231 cpl_propertylist_set_int(properties, GIALIAS_NAXIS1,
3232 cpl_image_get_size_x(_spectra));
3233 cpl_propertylist_set_int(properties, GIALIAS_NAXIS2,
3234 cpl_image_get_size_y(_spectra));
3235
3236 cpl_propertylist_set_int(properties, GIALIAS_BITPIX, -32);
3237 cpl_propertylist_set_double(properties, GIALIAS_BZERO, 0.);
3238 cpl_propertylist_set_double(properties, GIALIAS_BSCALE, 1.);
3239
3240 cpl_propertylist_update_int(properties, GIALIAS_NFIBERS,
3241 cpl_image_get_size_x(_spectra));
3242
3243 cpl_propertylist_append_int(properties, GIALIAS_EXT_NX,
3244 cpl_image_get_size_y(_spectra));
3245 cpl_propertylist_append_int(properties, GIALIAS_EXT_NS,
3246 cpl_image_get_size_x(_spectra));
3247
3248 switch (config->emethod) {
3249 case GIEXTRACT_SUM:
3250 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3251 "SUM");
3252 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3253 "Spectrum extraction method");
3254 break;
3255
3256 case GIEXTRACT_HORNE:
3257 {
3258
3259 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3260 "HORNE");
3261 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3262 "Spectrum extraction method");
3263
3264 cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
3265 config->psf.model);
3266 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
3267 "PSF model used");
3268 cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
3269 config->psf.sigma);
3270 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
3271 "PSF fit sigma clipping threshold");
3272 cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
3273 config->psf.iterations);
3274 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
3275 "PSF fit maximum number of "
3276 "iterations");
3277
3278 cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_EWIDTH,
3279 config->horne.ewidth);
3280 cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_EWIDTH,
3281 "Number of extra pixels used");
3282 cpl_propertylist_append_int(properties, GIALIAS_EXTHRN_MINGOOD,
3283 config->horne.mingood);
3284 cpl_propertylist_set_comment(properties, GIALIAS_EXTHRN_MINGOOD,
3285 "Minimum number of pixels to keep");
3286
3287
3288 break;
3289 }
3290
3291 case GIEXTRACT_OPTIMAL:
3292 cpl_propertylist_append_string(properties, GIALIAS_EXT_METHOD,
3293 "OPTIMAL");
3294 cpl_propertylist_set_comment(properties, GIALIAS_EXT_METHOD,
3295 "Spectrum extraction method");
3296
3297 cpl_propertylist_append_string(properties, GIALIAS_EXTPSF_MODEL,
3298 config->psf.model);
3299 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_MODEL,
3300 "PSF model used");
3301 cpl_propertylist_append_double(properties, GIALIAS_EXTPSF_SIGMA,
3302 config->psf.sigma);
3303 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_SIGMA,
3304 "PSF fit sigma clipping threshold");
3305 cpl_propertylist_append_int(properties, GIALIAS_EXTPSF_NITER,
3306 config->psf.iterations);
3307 cpl_propertylist_set_comment(properties, GIALIAS_EXTPSF_NITER,
3308 "PSF fit maximum number of "
3309 "iterations");
3310
3311 cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_FRACTION,
3312 config->optimal.fraction);
3313 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_FRACTION,
3314 "Minimum fraction of pixels used.");
3315 cpl_propertylist_append_double(properties, GIALIAS_EXTOPT_WFACTOR,
3316 config->optimal.wfactor);
3317 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_WFACTOR,
3318 "Multiple of the fiber PSF half "
3319 "width used for spectrum "
3320 "extraction.");
3321 cpl_propertylist_append_int(properties, GIALIAS_EXTOPT_BGORDER,
3322 config->optimal.bkgorder);
3323 cpl_propertylist_set_comment(properties, GIALIAS_EXTOPT_BGORDER,
3324 "Order of the background polynomial "
3325 "model along the spatial direction.");
3326
3327 break;
3328
3329 default:
3330 break;
3331 }
3332
3333 cpl_propertylist_update_string(properties, GIALIAS_GIRFTYPE, "EXTSP");
3334 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3335 "Extracted spectra");
3336
3337
3338 /*
3339 * Extracted spectra errors frame
3340 */
3341
3342 giraffe_image_set_properties(result->error, properties);
3343 properties = giraffe_image_get_properties(result->error);
3344
3345 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "EXTERRS");
3346 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3347 "Extracted spectra errors");
3348
3349
3350 /*
3351 * Extracted spectra centroids frame
3352 */
3353
3354 giraffe_image_set_properties(result->centroid, properties);
3355 properties = giraffe_image_get_properties(result->centroid);
3356
3357 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "EXTYCEN");
3358 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3359 "Extracted spectra centroids");
3360
3361
3362 /*
3363 * Extracted spectra npixels frame
3364 */
3365
3366 if (result->npixels != NULL) {
3367 giraffe_image_set_properties(result->npixels, properties);
3368 properties = giraffe_image_get_properties(result->npixels);
3369
3370 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "EXTNPIX");
3371 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3372 "Extracted spectra npixels");
3373 }
3374
3375
3376 /*
3377 * Model spectra frame
3378 */
3379
3380 if (result->model != NULL) {
3381 giraffe_image_set_properties(result->model, properties);
3382 properties = giraffe_image_get_properties(result->model);
3383
3384 cpl_propertylist_set_string(properties, GIALIAS_GIRFTYPE, "EXTMODEL");
3385 cpl_propertylist_set_comment(properties, GIALIAS_GIRFTYPE,
3386 "Model spectra used for extraction");
3387 }
3388
3389 return 0;
3390
3391}
3392
3393
3404GiExtractConfig*
3405giraffe_extract_config_create(cpl_parameterlist* list)
3406{
3407
3408 const cxchar* s;
3409 cpl_parameter* p;
3410
3411 GiExtractConfig* config = NULL;
3412
3413
3414 if (!list) {
3415 return NULL;
3416 }
3417
3418 config = cx_calloc(1, sizeof *config);
3419
3420 p = cpl_parameterlist_find(list, "giraffe.extraction.method");
3421 s = cpl_parameter_get_string(p);
3422 if (!strcmp(s, "OPTIMAL")) {
3423 config->emethod = GIEXTRACT_OPTIMAL;
3424 }
3425 else if (!strcmp(s, "HORNE")) {
3426 config->emethod = GIEXTRACT_HORNE;
3427 }
3428 else {
3429 config->emethod = GIEXTRACT_SUM;
3430 }
3431
3432 p = cpl_parameterlist_find(list, "giraffe.extraction.ron");
3433 config->ron = cpl_parameter_get_double(p);
3434
3435 p = cpl_parameterlist_find(list, "giraffe.extraction.psf.model");
3436 config->psf.model = cx_strdup(cpl_parameter_get_string(p));
3437
3438 p = cpl_parameterlist_find(list, "giraffe.extraction.psf.sigma");
3439 config->psf.sigma = cpl_parameter_get_double(p);
3440
3441 p = cpl_parameterlist_find(list, "giraffe.extraction.psf.iterations");
3442 config->psf.iterations = cpl_parameter_get_int(p);
3443
3444
3445 p = cpl_parameterlist_find(list, "giraffe.extraction.horne.extrawidth");
3446 config->horne.ewidth = cpl_parameter_get_int(p);
3447
3448 p = cpl_parameterlist_find(list, "giraffe.extraction.horne.mingood");
3449 config->horne.mingood = cpl_parameter_get_double(p);
3450
3451
3452 p = cpl_parameterlist_find(list, "giraffe.extraction.optimal.fraction");
3453 config->optimal.fraction = cpl_parameter_get_double(p);
3454
3455 p = cpl_parameterlist_find(list, "giraffe.extraction.optimal.wfactor");
3456 config->optimal.wfactor = cpl_parameter_get_double(p);
3457
3458 p = cpl_parameterlist_find(list, "giraffe.extraction.optimal.bkgorder");
3459 config->optimal.bkgorder = cpl_parameter_get_int(p);
3460
3461 return config;
3462
3463}
3464
3465
3478void
3479giraffe_extract_config_destroy(GiExtractConfig* config)
3480{
3481
3482 if (config) {
3483
3484 if (config->psf.model) {
3485 cx_free(config->psf.model);
3486 }
3487
3488 cx_free(config);
3489
3490 }
3491
3492 return;
3493
3494}
3495
3496
3508void
3509giraffe_extract_config_add(cpl_parameterlist* list)
3510{
3511
3512 cpl_parameter* p = NULL;
3513
3514
3515 if (list == NULL) {
3516 return;
3517 }
3518
3519 p = cpl_parameter_new_enum("giraffe.extraction.method",
3520 CPL_TYPE_STRING,
3521 "Extraction method: 'SUM', 'HORNE' or "
3522 "'OPTIMAL'",
3523 "giraffe.extraction",
3524 "SUM", 3, "SUM", "OPTIMAL", "HORNE");
3525 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-method");
3526 cpl_parameterlist_append(list, p);
3527
3528
3529 p = cpl_parameter_new_value("giraffe.extraction.ron",
3530 CPL_TYPE_DOUBLE,
3531 "New bias sigma (RON) value for "
3532 "bias and dark "
3533 "corrected image",
3534 "giraffe.extraction",
3535 -1.);
3536 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-ron");
3537 cpl_parameterlist_append(list, p);
3538
3539
3540 p = cpl_parameter_new_enum("giraffe.extraction.psf.model",
3541 CPL_TYPE_STRING,
3542 "PSF profile model: `psfexp', `psfexp2'",
3543 "giraffe.extraction.psf",
3544 "psfexp2", 2, "psfexp", "psfexp2");
3545 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-psfmodel");
3546 cpl_parameterlist_append(list, p);
3547
3548
3549 p = cpl_parameter_new_value("giraffe.extraction.psf.sigma",
3550 CPL_TYPE_DOUBLE,
3551 "Sigma clippging threshold used for "
3552 "rejecting data points during PSF fitting "
3553 "(Horne's sigma). It is used to reject bad "
3554 "pixels and cosmics.",
3555 "giraffe.extraction.psf",
3556 7.);
3557 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-psfsigma");
3558 cpl_parameterlist_append(list, p);
3559
3560
3561 p = cpl_parameter_new_value("giraffe.extraction.psf.iterations",
3562 CPL_TYPE_INT,
3563 "Maximum number of iterations used for "
3564 "fitting the PSF profile.",
3565 "giraffe.extraction.psf",
3566 2);
3567 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-psfniter");
3568 cpl_parameterlist_append(list, p);
3569
3570
3571 p = cpl_parameter_new_value("giraffe.extraction.horne.extrawidth",
3572 CPL_TYPE_INT,
3573 "Horne extraction method: Number of "
3574 "extra pixels added to the fiber "
3575 "half-width.",
3576 "giraffe.extraction.horne",
3577 2);
3578 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-hewidth");
3579 cpl_parameterlist_append(list, p);
3580
3581
3582 p = cpl_parameter_new_value("giraffe.extraction.horne.mingood",
3583 CPL_TYPE_INT,
3584 "Horne extraction method: Minimum number of "
3585 "points used for the profile fit. It sets "
3586 "the lower limit of data points for the "
3587 "pixel rejection.",
3588 "giraffe.extraction.horne",
3589 3);
3590 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-hmingood");
3591 cpl_parameterlist_append(list, p);
3592
3593
3594 p = cpl_parameter_new_range("giraffe.extraction.optimal.fraction",
3595 CPL_TYPE_DOUBLE,
3596 "Optimal extraction method: Minimum fraction "
3597 "of the data points used for fitting the "
3598 "fiber profiles. It sets the lower limit "
3599 "for the pixel rejection.",
3600 "giraffe.extraction.optimal",
3601 0.9, 0.0, 1.0);
3602 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-omfrac");
3603 cpl_parameterlist_append(list, p);
3604
3605
3606 p = cpl_parameter_new_value("giraffe.extraction.optimal.wfactor",
3607 CPL_TYPE_DOUBLE,
3608 "Optimal extraction method: Factor by which "
3609 "the fiber PSF half width is multiplied. "
3610 "Adjacent spectra within this area are "
3611 "assumed to affect the spectrum being "
3612 "extracted.",
3613 "giraffe.extraction.optimal",
3614 3.);
3615 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-owfactor");
3616 cpl_parameterlist_append(list, p);
3617
3618
3619 p = cpl_parameter_new_value("giraffe.extraction.optimal.bkgorder",
3620 CPL_TYPE_INT,
3621 "Optimal extraction method: Order of the "
3622 "polynomial background model, which is "
3623 "fitted for each wavelength bin along the "
3624 "spatial direction.",
3625 "giraffe.extraction.optimal",
3626 2);
3627 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "extr-obkgorder");
3628 cpl_parameterlist_append(list, p);
3629
3630
3631 return;
3632
3633}
cxint giraffe_array_sort(cxdouble *array, cxsize size)
Sorts an array in ascending order.
Definition: giarray.c:169
void giraffe_extract_config_add(cpl_parameterlist *list)
Adds parameters for the spectrum extraction.
Definition: giextract.c:3509
cxint giraffe_extract_spectra(GiExtraction *result, GiImage *image, GiTable *fibers, GiLocalization *sloc, GiImage *bpixel, GiImage *slight, GiExtractConfig *config)
Extracts the spectra from a preprocessed frame.
Definition: giextract.c:2480
GiExtractConfig * giraffe_extract_config_create(cpl_parameterlist *list)
Creates a setup structure for the spectrum extraction.
Definition: giextract.c:3405
void giraffe_extract_config_destroy(GiExtractConfig *config)
Destroys a spectrum extraction setup structure.
Definition: giextract.c:3479
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
void giraffe_image_delete(GiImage *self)
Destroys an image.
Definition: giimage.c:181
GiImage * giraffe_image_create(cpl_type type, cxint nx, cxint ny)
Creates an image container of a given type.
Definition: giimage.c:95
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
Definition: giimage.c:312
void gi_warning(const cxchar *format,...)
Log a warning.
Definition: gimessages.c:119
void gi_message(const cxchar *format,...)
Log a normal message.
Definition: gimessages.c:146
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
Definition: gitable.c:433
cxdouble giraffe_propertylist_get_conad(const cpl_propertylist *properties)
Retrieve the ADU to electrons conversion factor from the given properties.
Definition: giutils.c:1473
cxint giraffe_propertylist_update(cpl_propertylist *self, cpl_propertylist *properties, const cxchar *regexp)
Update a property list.
Definition: giutils.c:1017
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