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

This file is part of the GIRAFFE Pipeline Reference Manual 2.12.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Mon Mar 24 2014 11:43:52 by doxygen 1.8.2 written by Dimitri van Heesch, © 1997-2004