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 
62 enum GiProfileId {
63  PROFILE_PSFEXP = 1 << 1,
64  PROFILE_PSFEXP2 = 1 << 2,
65  PROFILE_GAUSSIAN = 1 << 3
66 };
67 
68 typedef enum GiProfileId GiProfileId;
69 
70 
71 /*
72  * Optimal spectrum extraction algorithm configuration data
73  */
74 
75 struct 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 
89 typedef struct GiExtractOptimalConfig GiExtractOptimalConfig;
90 
91 
92 /*
93  * Original Horne spectrum extraction algorithm configuration data
94  */
95 
96 struct GiExtractHorneConfig {
97  GiClipParams clip;
98 
99  cxdouble exptime;
100  cxdouble ron;
101  cxdouble dark;
102  cxdouble ewidth;
103 };
104 
105 typedef struct GiExtractHorneConfig GiExtractHorneConfig;
106 
107 
108 struct GiExtractionData {
109  cxdouble value;
110  cxdouble error;
111  cxdouble position;
112  cxdouble npixels;
113 };
114 
115 typedef struct GiExtractionData GiExtractionData;
116 
117 
118 struct 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 
130 typedef struct GiExtractionSlice GiExtractionSlice;
131 
132 
133 struct GiExtractionPsfLimits {
134  cxint size;
135 
136  cxint* ymin;
137  cxint* ymax;
138 };
139 
140 typedef struct GiExtractionPsfLimits GiExtractionPsfLimits;
141 
142 
143 struct GiExtractionWorkspace {
144  cpl_matrix* atw;
145  cpl_matrix* atwa;
146  cpl_matrix* atws;
147  cpl_matrix* c;
148  cpl_matrix* tmp;
149 };
150 
151 typedef struct GiExtractionWorkspace GiExtractionWorkspace;
152 
153 
154 struct 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 
169 typedef struct GiVirtualSlit GiVirtualSlit;
170 
171 
172 /*
173  * Extraction slice implementation
174  */
175 
176 inline 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 
197 inline 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 
225 inline 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 
241 inline 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 
262 inline 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 
281 inline 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 
319 inline 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 
340 inline 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 
362 inline 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 
409 inline 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 
424 inline 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 
538 inline 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 
594 inline 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 
640 inline 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 
922 inline 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 
1056 inline 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 
1414 inline 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 
1644 inline 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 
1781 inline 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 
2474 cxint
2475 giraffe_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 
3399 GiExtractConfig*
3400 giraffe_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 
3473 void
3474 giraffe_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 
3503 void
3504 giraffe_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.
void giraffe_image_delete(GiImage *self)
Destroys an image.
Definition: giimage.c:181
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
Definition: giimage.c:282
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
Definition: giimage.c:218
GiImage * giraffe_image_create(cpl_type type, cxint nx, cxint ny)
Creates an image container of a given type.
Definition: giimage.c:95
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
Definition: giimage.c:312
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.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Dec 15 2022 21:18:51 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2004