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

This file is part of the GIRAFFE Pipeline Reference Manual 2.16.11.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Tue Apr 23 2024 11:02:51 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004