GIRAFFE Pipeline Reference Manual

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.14.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Wed Mar 11 2015 13:19:41 by doxygen 1.8.9.1 written by Dimitri van Heesch, © 1997-2004