GIRAFFE Pipeline Reference Manual

giscience.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 <assert.h>
33 
34 #include <cxslist.h>
35 #include <cxmessages.h>
36 
37 #include <cpl_recipe.h>
38 #include <cpl_plugininfo.h>
39 #include <cpl_parameterlist.h>
40 #include <cpl_frameset.h>
41 #include <cpl_msg.h>
42 #include <cpl_errorstate.h>
43 
44 #include <irplib_sdp_spectrum.h>
45 
46 #include "gialias.h"
47 #include "giframe.h"
48 #include "gifibers.h"
49 #include "gifiberutils.h"
50 #include "gislitgeometry.h"
51 #include "gipsfdata.h"
52 #include "gibias.h"
53 #include "gidark.h"
54 #include "giextract.h"
55 #include "giflat.h"
56 #include "gitransmission.h"
57 #include "girebinning.h"
58 #include "gisgcalibration.h"
59 #include "giastrometry.h"
60 #include "gifov.h"
61 #include "gimessages.h"
62 #include "gierror.h"
63 #include "giutils.h"
64 
65 
66 static cxint giscience(cpl_parameterlist*, cpl_frameset*);
67 
68 static cxint _giraffe_make_sdp_spectra(const cxchar* flux_filename,
69  const cxchar* err_filename,
70  cxint nassoc_keys,
71  cpl_frameset* allframes,
72  const cpl_parameterlist* parlist,
73  const cxchar* recipe_id);
74 
75 
76 /*
77  * Create the recipe instance, i.e. setup the parameter list for this
78  * recipe and make it available to the application using the interface.
79  */
80 
81 static cxint
82 giscience_create(cpl_plugin* plugin)
83 {
84 
85  cpl_recipe* recipe = (cpl_recipe*)plugin;
86 
87  cpl_parameter* p = NULL;
88 
89 
90  giraffe_error_init();
91 
92 
93  /*
94  * We have to provide the option we accept to the application. We
95  * need to setup our parameter list and hook it into the recipe
96  * interface.
97  */
98 
99  recipe->parameters = cpl_parameterlist_new();
100  cx_assert(recipe->parameters != NULL);
101 
102 
103  /*
104  * Fill the parameter list.
105  */
106 
107  /* Bias removal */
108 
109  giraffe_bias_config_add(recipe->parameters);
110 
111  /* Dark subtraction */
112 
113  /* TBD */
114 
115  /* Spectrum extraction */
116 
117  giraffe_extract_config_add(recipe->parameters);
118 
119  /* Flat fielding and relative fiber transmission correction */
120 
121  giraffe_flat_config_add(recipe->parameters);
122 
123  /* Spectrum rebinning */
124 
125  giraffe_rebin_config_add(recipe->parameters);
126 
127  /* Simultaneous wavelength calibration correction */
128 
129  p = cpl_parameter_new_value("giraffe.siwc.apply",
130  CPL_TYPE_BOOL,
131  "Enable simultaneous wavelength calibration "
132  "correction.",
133  "giraffe.siwc",
134  TRUE);
135 
136  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "siwc-apply");
137  cpl_parameterlist_append(recipe->parameters, p);
138 
139  giraffe_sgcalibration_config_add(recipe->parameters);
140 
141  /* Image reconstruction (IFU and Argus only) */
142 
143  giraffe_fov_config_add(recipe->parameters);
144 
145  /* Science Data Product format generation parameters: */
146 
147  p = cpl_parameter_new_value("giraffe.sdp.format.generate",
148  CPL_TYPE_BOOL,
149  "TRUE if additional files should be generated"
150  " in Science Data Product (SDP) format.",
151  "giraffe.sdp",
152  FALSE);
153 
154  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "generate-SDP-format");
155  cpl_parameterlist_append(recipe->parameters, p);
156 
157  p = cpl_parameter_new_value("giraffe.sdp.nassoc.keys",
158  CPL_TYPE_INT,
159  "Sets the number of dummy (empty) ASSONi,"
160  " ASSOCi and ASSOMi keywords to create.",
161  "giraffe.sdp",
162  (int)0);
163 
164  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
165  "dummy-association-keys");
166  cpl_parameterlist_append(recipe->parameters, p);
167 
168  return 0;
169 
170 }
171 
172 
173 /*
174  * Execute the plugin instance given by the interface.
175  */
176 
177 static cxint
178 giscience_exec(cpl_plugin* plugin)
179 {
180  cxint result;
181  cpl_errorstate prev_state;
182 
183  cpl_recipe* recipe = (cpl_recipe*)plugin;
184 
185 
186  cx_assert(recipe->parameters != NULL);
187  cx_assert(recipe->frames != NULL);
188 
189  prev_state = cpl_errorstate_get();
190  result = giscience(recipe->parameters, recipe->frames);
191  if (result != 0) {
192  cpl_errorstate_dump(prev_state, CPL_FALSE, cpl_errorstate_dump_one);
193  }
194  return result;
195 }
196 
197 
198 static cxint
199 giscience_destroy(cpl_plugin* plugin)
200 {
201 
202  cpl_recipe* recipe = (cpl_recipe*)plugin;
203 
204 
205  /*
206  * We just destroy what was created during the plugin initialization
207  * phase, i.e. the parameter list. The frame set is managed by the
208  * application which called us, so we must not touch it,
209  */
210 
211  cpl_parameterlist_delete(recipe->parameters);
212 
213  giraffe_error_clear();
214 
215  return 0;
216 
217 }
218 
219 
220 /*
221  * The actual recipe starts here.
222  */
223 
224 static cxint
225 giscience(cpl_parameterlist* config, cpl_frameset* set)
226 {
227 
228  const cxchar* const _id = "giscience";
229 
230 
231  const cxchar* filename = NULL;
232 
233  cxbool siwc = FALSE;
234  cxbool calsim = FALSE;
235 
236  cxbool gensdp = FALSE;
237  cxint nassoc_keys = 0;
238  cxchar* flux_filename = NULL;
239  cxchar* err_filename = NULL;
240 
241  cxint status = 0;
242 
243  cxlong i;
244  cxlong nscience = 0;
245 
246  cxdouble exptime = 0.;
247 
248  cx_slist* slist = NULL;
249 
250  cpl_propertylist* properties = NULL;
251 
252  cpl_matrix* biasareas = NULL;
253 
254  cpl_frame* science_frame = NULL;
255  cpl_frame* mbias_frame = NULL;
256  cpl_frame* mdark_frame = NULL;
257  cpl_frame* bpixel_frame = NULL;
258  cpl_frame* slight_frame = NULL;
259  cpl_frame* locy_frame = NULL;
260  cpl_frame* locw_frame = NULL;
261  cpl_frame* psfdata_frame = NULL;
262  cpl_frame* grating_frame = NULL;
263  cpl_frame* linemask_frame = NULL;
264  cpl_frame* slit_frame = NULL;
265  cpl_frame* wcal_frame = NULL;
266  cpl_frame* rscience_frame = NULL;
267  cpl_frame* sext_frame = NULL;
268  cpl_frame* rbin_frame = NULL;
269 
270  cpl_parameter* p = NULL;
271 
272  GiImage* mbias = NULL;
273  GiImage* mdark = NULL;
274  GiImage* bpixel = NULL;
275  GiImage* slight = NULL;
276  GiImage* sscience = NULL;
277  GiImage* rscience = NULL;
278 
279  GiTable* fibers = NULL;
280  GiTable* slitgeometry = NULL;
281  GiTable* grating = NULL;
282  GiTable* wcalcoeff = NULL;
283 
284  GiLocalization* localization = NULL;
285  GiExtraction* extraction = NULL;
286  GiRebinning* rebinning = NULL;
287 
288  GiBiasConfig* bias_config = NULL;
289  GiExtractConfig* extract_config = NULL;
290  GiFlatConfig* flat_config = NULL;
291  GiRebinConfig* rebin_config = NULL;
292 
293  GiInstrumentMode mode;
294 
295  GiRecipeInfo info = {(cxchar*)_id, 1, NULL};
296 
297  GiGroupInfo groups[] = {
298  {GIFRAME_SCIENCE, CPL_FRAME_GROUP_RAW},
299  {GIFRAME_BADPIXEL_MAP, CPL_FRAME_GROUP_CALIB},
300  {GIFRAME_BIAS_MASTER, CPL_FRAME_GROUP_CALIB},
301  {GIFRAME_DARK_MASTER, CPL_FRAME_GROUP_CALIB},
302  {GIFRAME_FIBER_FLAT_EXTSPECTRA, CPL_FRAME_GROUP_CALIB},
303  {GIFRAME_FIBER_FLAT_EXTERRORS, CPL_FRAME_GROUP_CALIB},
304  {GIFRAME_SCATTERED_LIGHT_MODEL, CPL_FRAME_GROUP_CALIB},
305  {GIFRAME_LOCALIZATION_CENTROID, CPL_FRAME_GROUP_CALIB},
306  {GIFRAME_LOCALIZATION_WIDTH, CPL_FRAME_GROUP_CALIB},
307  {GIFRAME_PSF_CENTROID, CPL_FRAME_GROUP_CALIB},
308  {GIFRAME_PSF_WIDTH, CPL_FRAME_GROUP_CALIB},
309  {GIFRAME_PSF_DATA, CPL_FRAME_GROUP_CALIB},
310  {GIFRAME_WAVELENGTH_SOLUTION, CPL_FRAME_GROUP_CALIB},
311  {GIFRAME_LINE_MASK, CPL_FRAME_GROUP_CALIB},
312  {GIFRAME_SLITSETUP, CPL_FRAME_GROUP_CALIB},
313  {GIFRAME_SLITMASTER, CPL_FRAME_GROUP_CALIB},
314  {GIFRAME_GRATING, CPL_FRAME_GROUP_CALIB},
315  {NULL, CPL_FRAME_GROUP_NONE}
316  };
317 
318 
319 
320  if (!config) {
321  cpl_msg_error(_id, "Invalid parameter list! Aborting ...");
322  return 1;
323  }
324 
325  if (!set) {
326  cpl_msg_error(_id, "Invalid frame set! Aborting ...");
327  return 1;
328  }
329 
330  status = giraffe_frameset_set_groups(set, groups);
331 
332  if (status != 0) {
333  cpl_msg_error(_id, "Setting frame group information failed!");
334  return 1;
335  }
336 
337 
338  /*
339  * Verify the frame set contents
340  */
341 
342  nscience = cpl_frameset_count_tags(set, GIFRAME_SCIENCE);
343 
344  if (nscience < 1) {
345  cpl_msg_error(_id, "Too few (%ld) raw frames (%s) present in "
346  "frame set! Aborting ...", nscience, GIFRAME_SCIENCE);
347  return 1;
348  }
349 
350  locy_frame = cpl_frameset_find(set, GIFRAME_PSF_CENTROID);
351 
352  if (locy_frame == NULL) {
353 
354  locy_frame = cpl_frameset_find(set, GIFRAME_LOCALIZATION_CENTROID);
355 
356  if (locy_frame == NULL) {
357  cpl_msg_info(_id, "No master localization (centroid position) "
358  "present in frame set. Aborting ...");
359  return 1;
360  }
361 
362  }
363 
364  locw_frame = cpl_frameset_find(set, GIFRAME_PSF_WIDTH);
365 
366  if (locw_frame == NULL) {
367 
368  locw_frame = cpl_frameset_find(set, GIFRAME_LOCALIZATION_WIDTH);
369 
370  if (locw_frame == NULL) {
371  cpl_msg_info(_id, "No master localization (spectrum width) "
372  "present in frame set. Aborting ...");
373  return 1;
374  }
375 
376  }
377 
378  grating_frame = cpl_frameset_find(set, GIFRAME_GRATING);
379 
380  if (!grating_frame) {
381  cpl_msg_error(_id, "No grating data present in frame set. "
382  "Aborting ...");
383  return 1;
384  }
385 
386  slit_frame = giraffe_get_slitgeometry(set);
387 
388  if (!slit_frame) {
389  cpl_msg_error(_id, "No slit geometry present in frame set. "
390  "Aborting ...");
391  return 1;
392  }
393 
394  wcal_frame = cpl_frameset_find(set, GIFRAME_WAVELENGTH_SOLUTION);
395 
396  if (!wcal_frame) {
397  cpl_msg_error(_id, "No dispersion solution present in frame set. "
398  "Aborting ...");
399  return 1;
400  }
401 
402  linemask_frame = cpl_frameset_find(set, GIFRAME_LINE_MASK);
403 
404  if (!linemask_frame) {
405  cpl_msg_warning(_id, "No reference line mask present in frame set.");
406  }
407 
408  bpixel_frame = cpl_frameset_find(set, GIFRAME_BADPIXEL_MAP);
409 
410  if (!bpixel_frame) {
411  cpl_msg_info(_id, "No bad pixel map present in frame set.");
412  }
413 
414  mbias_frame = cpl_frameset_find(set, GIFRAME_BIAS_MASTER);
415 
416  if (!mbias_frame) {
417  cpl_msg_info(_id, "No master bias present in frame set.");
418  }
419 
420  mdark_frame = cpl_frameset_find(set, GIFRAME_DARK_MASTER);
421 
422  if (!mdark_frame) {
423  cpl_msg_info(_id, "No master dark present in frame set.");
424  }
425 
426  slight_frame = cpl_frameset_find(set, GIFRAME_SCATTERED_LIGHT_MODEL);
427 
428  if (!slight_frame) {
429  cpl_msg_info(_id, "No scattered light model present in frame set.");
430  }
431 
432  psfdata_frame = cpl_frameset_find(set, GIFRAME_PSF_DATA);
433 
434  if (!psfdata_frame) {
435  cpl_msg_info(_id, "No PSF profile parameters present in frame set.");
436  }
437 
438 
439  /*
440  * Load raw images
441  */
442 
443  slist = cx_slist_new();
444 
445  science_frame = cpl_frameset_find(set, GIFRAME_SCIENCE);
446 
447  for (i = 0; i < nscience; i++) {
448 
449  filename = cpl_frame_get_filename(science_frame);
450 
451  GiImage* raw = giraffe_image_new(CPL_TYPE_DOUBLE);
452 
453 
454  status = giraffe_image_load(raw, filename, 0);
455 
456  if (status) {
457  cpl_msg_error(_id, "Cannot load raw science frame from '%s'. "
458  "Aborting ...", filename);
459 
460  cx_slist_destroy(slist, (cx_free_func) giraffe_image_delete);
461 
462  return 1;
463  }
464 
465  cx_slist_push_back(slist, raw);
466 
467  science_frame = cpl_frameset_find(set, NULL);
468 
469  }
470 
471  nscience = (cxint)cx_slist_size(slist);
472  sscience = cx_slist_pop_front(slist);
473 
474  properties = giraffe_image_get_properties(sscience);
475  cx_assert(properties != NULL);
476 
477  if (nscience > 1) {
478 
479  /*
480  * Create a stacked science image from the list of raw images.
481  * Each raw image is disposed when it is no longer needed.
482  */
483 
484  cpl_msg_info(_id, "Averaging science frames ...");
485 
486  exptime = cpl_propertylist_get_double(properties, GIALIAS_EXPTIME);
487 
488  for (i = 1; i < nscience; i++) {
489 
490  cpl_propertylist* _properties;
491 
492  GiImage* science = cx_slist_pop_front(slist);
493 
494 
495  cpl_image_add(giraffe_image_get(sscience),
496  giraffe_image_get(science));
497 
498  _properties = giraffe_image_get_properties(science);
499  cx_assert(_properties != NULL);
500 
501  exptime += cpl_propertylist_get_double(_properties, GIALIAS_EXPTIME);
502 
503  giraffe_image_delete(science);
504 
505  }
506 
507  cpl_image_divide_scalar(giraffe_image_get(sscience), nscience);
508  }
509 
510  cx_assert(cx_slist_empty(slist));
511  cx_slist_delete(slist);
512  slist = NULL;
513 
514 
515  if (nscience > 1) {
516 
517  /*
518  * Update stacked science image properties
519  */
520 
521  cpl_msg_info(_id, "Updating stacked science image properties ...");
522 
523  cpl_propertylist_set_double(properties, GIALIAS_EXPTIME,
524  exptime / nscience);
525 
526  cpl_propertylist_append_double(properties, GIALIAS_EXPTTOT, exptime);
527  cpl_propertylist_set_comment(properties, GIALIAS_EXPTTOT,
528  "Total exposure time of all frames "
529  "combined");
530 
531  cpl_propertylist_append_int(properties, GIALIAS_DATANCOM, nscience);
532  cpl_propertylist_set_comment(properties, GIALIAS_DATANCOM,
533  "Number of frames combined");
534 
535  cpl_propertylist_erase(properties, GIALIAS_TPLEXPNO);
536 
537  }
538 
539 
540  /*
541  * Prepare for bias subtraction
542  */
543 
544  bias_config = giraffe_bias_config_create(config);
545 
546  /*
547  * Setup user defined areas to use for the bias computation
548  */
549 
550  if (bias_config->method == GIBIAS_METHOD_MASTER ||
551  bias_config->method == GIBIAS_METHOD_ZMASTER) {
552 
553  if (!mbias_frame) {
554  cpl_msg_error(_id, "Missing master bias frame! Selected bias "
555  "removal method requires a master bias frame!");
556 
557  giraffe_bias_config_destroy(bias_config);
558  giraffe_image_delete(sscience);
559 
560  return 1;
561  }
562  else {
563  filename = cpl_frame_get_filename(mbias_frame);
564 
565 
566  mbias = giraffe_image_new(CPL_TYPE_DOUBLE);
567  status = giraffe_image_load(mbias, filename, 0);
568 
569  if (status) {
570  cpl_msg_error(_id, "Cannot load master bias from '%s'. "
571  "Aborting ...", filename);
572 
573  giraffe_bias_config_destroy(bias_config);
574  giraffe_image_delete(sscience);
575 
576  return 1;
577  }
578  }
579  }
580 
581 
582  /*
583  * Load bad pixel map if it is present in the frame set.
584  */
585 
586  if (bpixel_frame) {
587 
588  filename = cpl_frame_get_filename(bpixel_frame);
589 
590 
591  bpixel = giraffe_image_new(CPL_TYPE_INT);
592  status = giraffe_image_load(bpixel, filename, 0);
593 
594  if (status) {
595  cpl_msg_error(_id, "Cannot load bad pixel map from '%s'. "
596  "Aborting ...", filename);
597 
598  giraffe_image_delete(bpixel);
599  bpixel = NULL;
600 
601  if (mbias != NULL) {
602  giraffe_image_delete(mbias);
603  mbias = NULL;
604  }
605 
606  giraffe_bias_config_destroy(bias_config);
607  bias_config = NULL;
608 
609  giraffe_image_delete(sscience);
610  sscience = NULL;
611 
612  return 1;
613  }
614 
615  }
616 
617 
618  /*
619  * Compute and remove the bias from the stacked flat field frame.
620  */
621 
622  rscience = giraffe_image_new(CPL_TYPE_DOUBLE);
623 
624  status = giraffe_bias_remove(rscience, sscience, mbias, bpixel, biasareas,
625  bias_config);
626 
627  giraffe_image_delete(sscience);
628 
629  if (mbias) {
630  giraffe_image_delete(mbias);
631  mbias = NULL;
632  }
633 
634  giraffe_bias_config_destroy(bias_config);
635 
636  if (status) {
637  cpl_msg_error(_id, "Bias removal failed. Aborting ...");
638 
639  giraffe_image_delete(rscience);
640  rscience = NULL;
641 
642  if (bpixel != NULL) {
643  giraffe_image_delete(bpixel);
644  bpixel = NULL;
645  }
646 
647  return 1;
648  }
649 
650 
651  /*
652  * Load master dark if it is present in the frame set and correct
653  * the master flat field for the dark current.
654  */
655 
656  if (mdark_frame) {
657 
658  GiDarkConfig dark_config = {GIDARK_METHOD_ZMASTER, 0.};
659 
660 
661  cpl_msg_info(_id, "Correcting for dark current ...");
662 
663  filename = cpl_frame_get_filename(mdark_frame);
664 
665  mdark = giraffe_image_new(CPL_TYPE_DOUBLE);
666  status = giraffe_image_load(mdark, filename, 0);
667 
668  if (status != 0) {
669  cpl_msg_error(_id, "Cannot load master dark from '%s'. "
670  "Aborting ...", filename);
671 
672  giraffe_image_delete(rscience);
673  rscience = NULL;
674 
675  if (bpixel != NULL) {
676  giraffe_image_delete(bpixel);
677  bpixel = NULL;
678  }
679 
680  return 1;
681  }
682 
683  status = giraffe_subtract_dark(rscience, mdark, bpixel, NULL,
684  &dark_config);
685 
686  if (status != 0) {
687  cpl_msg_error(_id, "Dark subtraction failed! Aborting ...");
688 
689  giraffe_image_delete(mdark);
690  mdark = NULL;
691 
692  giraffe_image_delete(rscience);
693  rscience = NULL;
694 
695  if (bpixel != NULL) {
696  giraffe_image_delete(bpixel);
697  bpixel = NULL;
698  }
699 
700  return 1;
701  }
702 
703  giraffe_image_delete(mdark);
704  mdark = NULL;
705 
706  }
707 
708 
709  /*
710  * Update the reduced science properties, save the reduced science frame
711  * and register it as product.
712  */
713 
714  cpl_msg_info(_id, "Writing pre-processed science image ...");
715 
716  giraffe_image_add_info(rscience, &info, set);
717 
718  rscience_frame = giraffe_frame_create_image(rscience,
719  GIFRAME_SCIENCE_REDUCED,
720  CPL_FRAME_LEVEL_INTERMEDIATE,
721  TRUE, TRUE);
722 
723  if (rscience_frame == NULL) {
724  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
725 
726  giraffe_image_delete(rscience);
727 
728  return 1;
729  }
730 
731  cpl_frameset_insert(set, rscience_frame);
732 
733 
734  /*
735  * Determine fiber setup
736  */
737 
738  science_frame = cpl_frameset_find(set, GIFRAME_SCIENCE);
739 
740  cpl_msg_info(_id, "Building fiber setup for frame '%s'.",
741  cpl_frame_get_filename(science_frame));
742 
743  fibers = giraffe_fibers_setup(science_frame, locy_frame);
744 
745  if (!fibers) {
746  cpl_msg_error(_id, "Cannot create fiber setup for frame '%s'! "
747  "Aborting ...", cpl_frame_get_filename(science_frame));
748 
749  if (bpixel) {
750  giraffe_image_delete(bpixel);
751  bpixel = NULL;
752  }
753 
754  giraffe_image_delete(rscience);
755  rscience = NULL;
756 
757  return 1;
758  }
759 
760  cpl_msg_info(_id, "Fiber reference setup taken from localization "
761  "frame '%s'.", cpl_frame_get_filename(locy_frame));
762 
763 
764  /*
765  * Load fiber localization
766  */
767 
768  localization = giraffe_localization_new();
769 
770  filename = cpl_frame_get_filename(locy_frame);
771  status = 0;
772 
773  localization->locy = giraffe_image_new(CPL_TYPE_DOUBLE);
774  status = giraffe_image_load(localization->locy, filename, 0);
775 
776  if (status) {
777  cpl_msg_error(_id, "Cannot load localization (centroid "
778  "position) frame from '%s'. Aborting ...",
779  filename);
780 
781  giraffe_localization_destroy(localization);
782 
783  if (bpixel) {
784  giraffe_image_delete(bpixel);
785  bpixel = NULL;
786  }
787 
788  giraffe_table_delete(fibers);
789  giraffe_image_delete(rscience);
790 
791  return 1;
792  }
793 
794 
795  filename = cpl_frame_get_filename(locw_frame);
796  status = 0;
797 
798  localization->locw = giraffe_image_new(CPL_TYPE_DOUBLE);
799  status = giraffe_image_load(localization->locw, filename, 0);
800 
801  if (status) {
802  cpl_msg_error(_id, "Cannot load localization (spectrum width) "
803  "frame from '%s'. Aborting ...", filename);
804 
805  giraffe_localization_destroy(localization);
806 
807  if (bpixel) {
808  giraffe_image_delete(bpixel);
809  bpixel = NULL;
810  }
811 
812  giraffe_table_delete(fibers);
813  giraffe_image_delete(rscience);
814 
815  return 1;
816  }
817 
818 
819  /*
820  * Spectrum extraction
821  */
822 
823  if (slight_frame) {
824 
825  filename = cpl_frame_get_filename(slight_frame);
826 
827 
828  slight = giraffe_image_new(CPL_TYPE_DOUBLE);
829  status = giraffe_image_load(slight, filename, 0);
830 
831  if (status) {
832  cpl_msg_error(_id, "Cannot load scattered light model from '%s'. "
833  "Aborting ...", filename);
834 
835  giraffe_image_delete(slight);
836 
837  giraffe_localization_destroy(localization);
838 
839  if (bpixel) {
840  giraffe_image_delete(bpixel);
841  bpixel = NULL;
842  }
843 
844  giraffe_table_delete(fibers);
845  giraffe_image_delete(rscience);
846 
847  return 1;
848 
849  }
850 
851  }
852 
853 
854  extract_config = giraffe_extract_config_create(config);
855 
856  if ((extract_config->emethod == GIEXTRACT_OPTIMAL) ||
857  (extract_config->emethod == GIEXTRACT_HORNE)) {
858 
859  if (psfdata_frame == NULL) {
860 
861  const cxchar* emethod = "Optimal";
862 
863  if (extract_config->emethod == GIEXTRACT_HORNE) {
864  emethod = "Horne";
865  }
866 
867  cpl_msg_error(_id, "%s spectrum extraction requires PSF "
868  "profile data. Aborting ...", emethod);
869 
870  giraffe_extract_config_destroy(extract_config);
871  extract_config = NULL;
872 
873  if (slight != NULL) {
874  giraffe_image_delete(slight);
875  slight = NULL;
876  }
877 
878  giraffe_localization_destroy(localization);
879  localization = NULL;
880 
881  if (bpixel) {
882  giraffe_image_delete(bpixel);
883  bpixel = NULL;
884  }
885 
886  giraffe_table_delete(fibers);
887  fibers = NULL;
888 
889  giraffe_image_delete(rscience);
890  rscience = NULL;
891 
892  return 1;
893 
894  }
895  else {
896 
897  filename = cpl_frame_get_filename(psfdata_frame);
898  status = 0;
899 
900  localization->psf = giraffe_psfdata_new();
901  status = giraffe_psfdata_load(localization->psf, filename);
902 
903  if (status) {
904  cpl_msg_error(_id, "Cannot load PSF profile data frame from "
905  "'%s'. Aborting ...", filename);
906 
907  giraffe_extract_config_destroy(extract_config);
908  extract_config = NULL;
909 
910  if (slight != NULL) {
911  giraffe_image_delete(slight);
912  slight = NULL;
913  }
914 
915  giraffe_localization_destroy(localization);
916  localization = NULL;
917 
918  if (bpixel) {
919  giraffe_image_delete(bpixel);
920  bpixel = NULL;
921  }
922 
923  giraffe_table_delete(fibers);
924  fibers = NULL;
925 
926  giraffe_image_delete(rscience);
927  rscience = NULL;
928 
929  return 1;
930 
931  }
932 
933  }
934 
935  }
936 
937 
938  extraction = giraffe_extraction_new();
939 
940  status = giraffe_extract_spectra(extraction, rscience, fibers,
941  localization, bpixel, slight,
942  extract_config);
943 
944  if (status) {
945  cpl_msg_error(_id, "Spectrum extraction failed! Aborting ...");
946 
947  giraffe_extraction_destroy(extraction);
948  giraffe_extract_config_destroy(extract_config);
949 
950  giraffe_image_delete(slight);
951 
952  giraffe_localization_destroy(localization);
953 
954  if (bpixel) {
955  giraffe_image_delete(bpixel);
956  bpixel = NULL;
957  }
958 
959  giraffe_table_delete(fibers);
960  giraffe_image_delete(rscience);
961 
962  return 1;
963  }
964 
965  giraffe_image_delete(slight);
966  slight = NULL;
967 
968  if (bpixel) {
969  giraffe_image_delete(bpixel);
970  bpixel = NULL;
971  }
972 
973  giraffe_image_delete(rscience);
974  rscience = NULL;
975 
976  giraffe_extract_config_destroy(extract_config);
977 
978 
979  /*
980  * Apply flat field and apply the relative fiber transmission correction.
981  */
982 
983  flat_config = giraffe_flat_config_create(config);
984 
985  if (flat_config->load == TRUE) {
986 
987  cpl_frame* flat_frame = NULL;
988 
989  GiImage* flat = NULL;
990 
991 
992  flat_frame = cpl_frameset_find(set, GIFRAME_FIBER_FLAT_EXTSPECTRA);
993 
994  if (flat_frame == NULL) {
995  cpl_msg_error(_id, "Missing flat field spectra frame!");
996 
997  giraffe_flat_config_destroy(flat_config);
998 
999  giraffe_extraction_destroy(extraction);
1000  giraffe_localization_destroy(localization);
1001 
1002  giraffe_table_delete(wcalcoeff);
1003 
1004  giraffe_table_delete(grating);
1005  giraffe_table_delete(fibers);
1006 
1007  return 1;
1008  }
1009 
1010  filename = cpl_frame_get_filename(flat_frame);
1011 
1012  flat = giraffe_image_new(CPL_TYPE_DOUBLE);
1013  status = giraffe_image_load(flat, filename, 0);
1014 
1015  if (status) {
1016  cpl_msg_error(_id, "Cannot load flat field spectra from '%s'. "
1017  "Aborting ...", filename);
1018 
1019  giraffe_image_delete(flat);
1020 
1021  giraffe_flat_config_destroy(flat_config);
1022 
1023  giraffe_extraction_destroy(extraction);
1024  giraffe_localization_destroy(localization);
1025 
1026  giraffe_table_delete(wcalcoeff);
1027 
1028  giraffe_table_delete(grating);
1029  giraffe_table_delete(fibers);
1030 
1031  return 1;
1032  }
1033 
1034  if (flat_config->apply == TRUE) {
1035 
1036  GiImage* errors = NULL;
1037 
1038 
1039  flat_frame = cpl_frameset_find(set, GIFRAME_FIBER_FLAT_EXTERRORS);
1040 
1041  if (flat_frame == NULL) {
1042  cpl_msg_warning(_id, "Missing flat field spectra errors "
1043  "frame!");
1044  }
1045  else {
1046 
1047  filename = cpl_frame_get_filename(flat_frame);
1048 
1049  errors = giraffe_image_new(CPL_TYPE_DOUBLE);
1050  status = giraffe_image_load(errors, filename, 0);
1051 
1052  if (status) {
1053  cpl_msg_error(_id, "Cannot load flat field spectra "
1054  "errors from '%s'. Aborting ...",
1055  filename);
1056 
1057  giraffe_image_delete(errors);
1058  giraffe_image_delete(flat);
1059 
1060  giraffe_flat_config_destroy(flat_config);
1061 
1062  giraffe_extraction_destroy(extraction);
1063  giraffe_localization_destroy(localization);
1064 
1065  giraffe_table_delete(wcalcoeff);
1066 
1067  giraffe_table_delete(grating);
1068  giraffe_table_delete(fibers);
1069 
1070  return 1;
1071  }
1072 
1073  }
1074 
1075  cpl_msg_info(_id, "Applying flat field correction ...");
1076 
1077  status = giraffe_flat_apply(extraction, fibers, flat, errors,
1078  flat_config);
1079 
1080  if (status) {
1081  cpl_msg_error(_id, "Flat field correction failed! "
1082  "Aborting ...");
1083 
1084  giraffe_image_delete(errors);
1085  giraffe_image_delete(flat);
1086 
1087  giraffe_flat_config_destroy(flat_config);
1088 
1089  giraffe_extraction_destroy(extraction);
1090  giraffe_localization_destroy(localization);
1091 
1092  giraffe_table_delete(wcalcoeff);
1093 
1094  giraffe_table_delete(grating);
1095  giraffe_table_delete(fibers);
1096 
1097  return 1;
1098  }
1099 
1100  giraffe_image_delete(errors);
1101  errors = NULL;
1102 
1103  }
1104 
1105  if (flat_config->transmission == TRUE) {
1106 
1107  const cxchar* _filename = cpl_frame_get_filename(flat_frame);
1108 
1109  GiTable* _fibers = NULL;
1110 
1111 
1112  cpl_msg_info(_id, "Loading fiber setup for frame '%s'.",
1113  _filename);
1114 
1115  _fibers = giraffe_fiberlist_load(_filename, 1, "FIBER_SETUP");
1116 
1117  if (!_fibers) {
1118  cpl_msg_error(_id, "Cannot create fiber setup for "
1119  "frame '%s'! Aborting ...", _filename);
1120 
1121  giraffe_image_delete(flat);
1122 
1123  giraffe_flat_config_destroy(flat_config);
1124 
1125  giraffe_extraction_destroy(extraction);
1126  giraffe_localization_destroy(localization);
1127 
1128  giraffe_table_delete(wcalcoeff);
1129 
1130  giraffe_table_delete(grating);
1131  giraffe_table_delete(fibers);
1132 
1133  return 1;
1134  }
1135 
1136  cpl_msg_info(_id, "Applying relative fiber transmission "
1137  "correction");
1138 
1139  status = giraffe_transmission_setup(fibers, _fibers);
1140  giraffe_table_delete(_fibers);
1141 
1142  if (status == 0) {
1143  status = giraffe_transmission_apply(extraction, fibers);
1144  }
1145 
1146  if (status) {
1147 
1148  cpl_msg_error(_id, "Relative transmission correction failed! "
1149  "Aborting ...");
1150 
1151  giraffe_image_delete(flat);
1152 
1153  giraffe_flat_config_destroy(flat_config);
1154 
1155  giraffe_extraction_destroy(extraction);
1156  giraffe_localization_destroy(localization);
1157 
1158  giraffe_table_delete(wcalcoeff);
1159 
1160  giraffe_table_delete(grating);
1161  giraffe_table_delete(fibers);
1162 
1163  return 1;
1164 
1165  }
1166 
1167  }
1168 
1169  giraffe_image_delete(flat);
1170 
1171  }
1172 
1173  giraffe_flat_config_destroy(flat_config);
1174 
1175 
1176  /*
1177  * Save the spectrum extraction results and register them as
1178  * products.
1179  */
1180 
1181  cpl_msg_info(_id, "Writing extracted spectra ...");
1182 
1183  /* Extracted spectra */
1184 
1185  giraffe_image_add_info(extraction->spectra, &info, set);
1186 
1187  sext_frame = giraffe_frame_create_image(extraction->spectra,
1188  GIFRAME_SCIENCE_EXTSPECTRA,
1189  CPL_FRAME_LEVEL_FINAL,
1190  TRUE, TRUE);
1191 
1192  if (sext_frame == NULL) {
1193  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
1194 
1195  giraffe_extraction_destroy(extraction);
1196  giraffe_localization_destroy(localization);
1197 
1198  giraffe_table_delete(wcalcoeff);
1199 
1200  giraffe_table_delete(grating);
1201  giraffe_table_delete(fibers);
1202 
1203  return 1;
1204  }
1205 
1206  status = giraffe_fiberlist_attach(sext_frame, fibers);
1207 
1208  if (status) {
1209  cpl_msg_error(_id, "Cannot attach fiber setup to local file '%s'! "
1210  "Aborting ...", cpl_frame_get_filename(sext_frame));
1211 
1212  cpl_frame_delete(sext_frame);
1213 
1214  giraffe_extraction_destroy(extraction);
1215  giraffe_localization_destroy(localization);
1216 
1217  giraffe_table_delete(wcalcoeff);
1218 
1219  giraffe_table_delete(grating);
1220  giraffe_table_delete(fibers);
1221 
1222  return 1;
1223  }
1224 
1225  cpl_frameset_insert(set, sext_frame);
1226 
1227  /* Extracted spectra errors */
1228 
1229  giraffe_image_add_info(extraction->error, &info, set);
1230 
1231  sext_frame = giraffe_frame_create_image(extraction->error,
1232  GIFRAME_SCIENCE_EXTERRORS,
1233  CPL_FRAME_LEVEL_FINAL,
1234  TRUE, TRUE);
1235 
1236  if (sext_frame == NULL) {
1237  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
1238 
1239  giraffe_extraction_destroy(extraction);
1240  giraffe_localization_destroy(localization);
1241 
1242  giraffe_table_delete(wcalcoeff);
1243 
1244  giraffe_table_delete(grating);
1245  giraffe_table_delete(fibers);
1246 
1247  return 1;
1248  }
1249 
1250  status = giraffe_fiberlist_attach(sext_frame, fibers);
1251 
1252  if (status) {
1253  cpl_msg_error(_id, "Cannot attach fiber setup to local file '%s'! "
1254  "Aborting ...", cpl_frame_get_filename(sext_frame));
1255 
1256  cpl_frame_delete(sext_frame);
1257 
1258  giraffe_extraction_destroy(extraction);
1259  giraffe_localization_destroy(localization);
1260 
1261  giraffe_table_delete(wcalcoeff);
1262 
1263  giraffe_table_delete(grating);
1264  giraffe_table_delete(fibers);
1265 
1266  return 1;
1267  }
1268 
1269  cpl_frameset_insert(set, sext_frame);
1270 
1271  /* Extracted spectra pixels */
1272 
1273  if (extraction->npixels != NULL) {
1274 
1275  giraffe_image_add_info(extraction->npixels, &info, set);
1276 
1277  sext_frame = giraffe_frame_create_image(extraction->npixels,
1278  GIFRAME_SCIENCE_EXTPIXELS,
1279  CPL_FRAME_LEVEL_FINAL,
1280  TRUE, TRUE);
1281 
1282  if (sext_frame == NULL) {
1283  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
1284 
1285  giraffe_extraction_destroy(extraction);
1286  giraffe_localization_destroy(localization);
1287 
1288  giraffe_table_delete(wcalcoeff);
1289 
1290  giraffe_table_delete(grating);
1291  giraffe_table_delete(fibers);
1292 
1293  return 1;
1294  }
1295 
1296  status = giraffe_fiberlist_attach(sext_frame, fibers);
1297 
1298  if (status) {
1299  cpl_msg_error(_id, "Cannot attach fiber setup to local file '%s'! "
1300  "Aborting ...", cpl_frame_get_filename(sext_frame));
1301 
1302  cpl_frame_delete(sext_frame);
1303 
1304  giraffe_extraction_destroy(extraction);
1305  giraffe_localization_destroy(localization);
1306 
1307  giraffe_table_delete(wcalcoeff);
1308 
1309  giraffe_table_delete(grating);
1310  giraffe_table_delete(fibers);
1311 
1312  return 1;
1313  }
1314 
1315  cpl_frameset_insert(set, sext_frame);
1316 
1317  }
1318 
1319  /* Extracted spectra centroids */
1320 
1321  giraffe_image_add_info(extraction->centroid, &info, set);
1322 
1323  sext_frame = giraffe_frame_create_image(extraction->centroid,
1324  GIFRAME_SCIENCE_EXTTRACE,
1325  CPL_FRAME_LEVEL_FINAL,
1326  TRUE, TRUE);
1327 
1328  if (sext_frame == NULL) {
1329  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
1330 
1331  giraffe_extraction_destroy(extraction);
1332  giraffe_localization_destroy(localization);
1333 
1334  giraffe_table_delete(wcalcoeff);
1335 
1336  giraffe_table_delete(grating);
1337  giraffe_table_delete(fibers);
1338 
1339  return 1;
1340  }
1341 
1342  status = giraffe_fiberlist_attach(sext_frame, fibers);
1343 
1344  if (status) {
1345  cpl_msg_error(_id, "Cannot attach fiber setup to local file '%s'! "
1346  "Aborting ...", cpl_frame_get_filename(sext_frame));
1347 
1348  cpl_frame_delete(sext_frame);
1349 
1350  giraffe_extraction_destroy(extraction);
1351  giraffe_localization_destroy(localization);
1352 
1353  giraffe_table_delete(wcalcoeff);
1354 
1355  giraffe_table_delete(grating);
1356  giraffe_table_delete(fibers);
1357 
1358  return 1;
1359  }
1360 
1361  cpl_frameset_insert(set, sext_frame);
1362 
1363  /* Extraction model spectra */
1364 
1365  if (extraction->model != NULL) {
1366 
1367  giraffe_image_add_info(extraction->model, &info, set);
1368 
1369  sext_frame = giraffe_frame_create_image(extraction->model,
1370  GIFRAME_SCIENCE_EXTMODEL,
1371  CPL_FRAME_LEVEL_FINAL,
1372  TRUE, TRUE);
1373 
1374  if (sext_frame == NULL) {
1375  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
1376 
1377  giraffe_extraction_destroy(extraction);
1378  giraffe_localization_destroy(localization);
1379 
1380  giraffe_table_delete(wcalcoeff);
1381 
1382  giraffe_table_delete(grating);
1383  giraffe_table_delete(fibers);
1384 
1385  return 1;
1386  }
1387 
1388  status = giraffe_fiberlist_attach(sext_frame, fibers);
1389 
1390  if (status != 0) {
1391  cpl_msg_error(_id, "Cannot attach fiber setup to local file '%s'! "
1392  "Aborting ...", cpl_frame_get_filename(sext_frame));
1393 
1394  cpl_frame_delete(sext_frame);
1395 
1396  giraffe_extraction_destroy(extraction);
1397  giraffe_localization_destroy(localization);
1398 
1399  giraffe_table_delete(wcalcoeff);
1400 
1401  giraffe_table_delete(grating);
1402  giraffe_table_delete(fibers);
1403 
1404  return 1;
1405  }
1406 
1407  cpl_frameset_insert(set, sext_frame);
1408 
1409  }
1410 
1411 
1412  /*
1413  * Load dispersion solution
1414  */
1415 
1416 
1417  filename = (cxchar *)cpl_frame_get_filename(wcal_frame);
1418 
1419  wcalcoeff = giraffe_table_new();
1420  status = giraffe_table_load(wcalcoeff, filename, 1, NULL);
1421 
1422  if (status) {
1423  cpl_msg_error(_id, "Cannot load dispersion solution from "
1424  "'%s'. Aborting ...", filename);
1425 
1426  giraffe_extraction_destroy(extraction);
1427  giraffe_localization_destroy(localization);
1428 
1429  giraffe_table_delete(wcalcoeff);
1430 
1431  giraffe_table_delete(grating);
1432  giraffe_table_delete(fibers);
1433 
1434  return 1;
1435  }
1436 
1437 
1438  /*
1439  * Load grating data
1440  */
1441 
1442  filename = (cxchar *)cpl_frame_get_filename(grating_frame);
1443 
1444  status = 0;
1445 
1446  grating = giraffe_table_new();
1447  status = giraffe_table_load(grating, filename, 1, NULL);
1448 
1449  if (status) {
1450  cpl_msg_error(_id, "Cannot load grating data from '%s'. "
1451  "Aborting ...", filename);
1452 
1453  giraffe_extraction_destroy(extraction);
1454  giraffe_localization_destroy(localization);
1455 
1456  giraffe_table_delete(wcalcoeff);
1457 
1458  giraffe_table_delete(grating);
1459  giraffe_table_delete(fibers);
1460 
1461  return 1;
1462  }
1463 
1464 
1465  /*
1466  * Load slit geometry data
1467  */
1468 
1469 
1470  filename = (cxchar *)cpl_frame_get_filename(slit_frame);
1471 
1472  slitgeometry = giraffe_slitgeometry_load(fibers, filename, 1, NULL);
1473 
1474  if (slitgeometry == NULL) {
1475  cpl_msg_error(_id, "Cannot load slit geometry data from '%s'. "
1476  "Aborting ...", filename);
1477 
1478  giraffe_table_delete(wcalcoeff);
1479 
1480  giraffe_extraction_destroy(extraction);
1481  giraffe_localization_destroy(localization);
1482 
1483  giraffe_table_delete(wcalcoeff);
1484 
1485  giraffe_table_delete(grating);
1486  giraffe_table_delete(fibers);
1487 
1488  return 1;
1489  }
1490  else {
1491 
1492  /*
1493  * Check whether the contains the positions for all fibers
1494  * provided by the fiber setup. If this is not the case
1495  * this is an error.
1496  */
1497 
1498  if (giraffe_fiberlist_compare(slitgeometry, fibers) != 1) {
1499  cpl_msg_error(_id, "Slit geometry data from '%s' is not "
1500  "applicable for current fiber setup! "
1501  "Aborting ...", filename);
1502 
1503  giraffe_table_delete(slitgeometry);
1504  giraffe_table_delete(wcalcoeff);
1505 
1506  giraffe_extraction_destroy(extraction);
1507  giraffe_localization_destroy(localization);
1508 
1509  giraffe_table_delete(wcalcoeff);
1510 
1511  giraffe_table_delete(grating);
1512  giraffe_table_delete(fibers);
1513 
1514  return 1;
1515  }
1516 
1517  }
1518 
1519 
1520 
1521  /*
1522  * Spectrum rebinning
1523  */
1524 
1525  cpl_msg_info(_id, "Spectrum rebinning");
1526 
1527  rebin_config = giraffe_rebin_config_create(config);
1528 
1529  rebinning = giraffe_rebinning_new();
1530 
1531  status = giraffe_rebin_spectra(rebinning, extraction, fibers,
1532  localization, grating, slitgeometry,
1533  wcalcoeff, rebin_config);
1534 
1535  if (status) {
1536  cpl_msg_error(_id, "Rebinning of science spectra failed! Aborting...");
1537 
1538  giraffe_rebinning_destroy(rebinning);
1539 
1540  giraffe_extraction_destroy(extraction);
1541  giraffe_localization_destroy(localization);
1542 
1543  giraffe_table_delete(wcalcoeff);
1544 
1545  giraffe_table_delete(slitgeometry);
1546  giraffe_table_delete(grating);
1547  giraffe_table_delete(fibers);
1548 
1549  giraffe_rebin_config_destroy(rebin_config);
1550 
1551  return 1;
1552 
1553  }
1554 
1555 
1556  /*
1557  * Optionally compute and apply spectral shifts from the simultaneous
1558  * calibration fibers. This is only done if the simultaneous calibration
1559  * fibers were used.
1560  */
1561 
1562  p = cpl_parameterlist_find(config, "giraffe.siwc.apply");
1563  cx_assert(p != NULL);
1564 
1565  siwc = cpl_parameter_get_bool(p);
1566  p = NULL;
1567 
1568  properties = giraffe_image_get_properties(rebinning->spectra);
1569  cx_assert(properties != NULL);
1570 
1571 
1572  if (cpl_propertylist_has(properties, GIALIAS_STSCTAL) == TRUE) {
1573  calsim = cpl_propertylist_get_bool(properties, GIALIAS_STSCTAL);
1574  }
1575 
1576 
1577  if ((siwc == TRUE) && (calsim == TRUE) && (linemask_frame != NULL)) {
1578 
1579  GiTable* linemask = giraffe_table_new();
1580 
1581  GiSGCalConfig* siwc_config = NULL;
1582 
1583 
1584  siwc_config = giraffe_sgcalibration_config_create(config);
1585 
1586  if (siwc_config == NULL) {
1587 
1588  giraffe_table_delete(linemask);
1589  linemask = NULL;
1590 
1591  giraffe_rebinning_destroy(rebinning);
1592 
1593  giraffe_extraction_destroy(extraction);
1594  giraffe_localization_destroy(localization);
1595 
1596  giraffe_table_delete(wcalcoeff);
1597 
1598  giraffe_table_delete(slitgeometry);
1599  giraffe_table_delete(grating);
1600  giraffe_table_delete(fibers);
1601 
1602  giraffe_rebin_config_destroy(rebin_config);
1603 
1604  return 1;
1605 
1606  }
1607 
1608  filename = cpl_frame_get_filename(linemask_frame);
1609 
1610  status = giraffe_table_load(linemask, filename, 1, NULL);
1611 
1612  if (status) {
1613  cpl_msg_error(_id, "Cannot load line reference mask from '%s'. "
1614  "Aborting ...", filename);
1615 
1617  siwc_config = NULL;
1618 
1619  giraffe_table_delete(linemask);
1620  linemask = NULL;
1621 
1622  giraffe_rebinning_destroy(rebinning);
1623 
1624  giraffe_extraction_destroy(extraction);
1625  giraffe_localization_destroy(localization);
1626 
1627  giraffe_table_delete(wcalcoeff);
1628 
1629  giraffe_table_delete(slitgeometry);
1630  giraffe_table_delete(grating);
1631  giraffe_table_delete(fibers);
1632 
1633  giraffe_rebin_config_destroy(rebin_config);
1634 
1635  return 1;
1636 
1637  }
1638 
1639 
1640  status = giraffe_compute_offsets(fibers, rebinning, grating,
1641  linemask, siwc_config);
1642 
1643  if (status != 0) {
1644  cpl_msg_error(_id, "Applying simultaneous wavelength "
1645  "calibration correction failed! Aborting...");
1646 
1648  siwc_config = NULL;
1649 
1650  giraffe_table_delete(linemask);
1651  linemask = NULL;
1652 
1653  giraffe_rebinning_destroy(rebinning);
1654 
1655  giraffe_extraction_destroy(extraction);
1656  giraffe_localization_destroy(localization);
1657 
1658  giraffe_table_delete(wcalcoeff);
1659 
1660  giraffe_table_delete(slitgeometry);
1661  giraffe_table_delete(grating);
1662  giraffe_table_delete(fibers);
1663 
1664  giraffe_rebin_config_destroy(rebin_config);
1665 
1666  return 1;
1667 
1668  }
1669 
1671  siwc_config = NULL;
1672 
1673  giraffe_table_delete(linemask);
1674  linemask = NULL;
1675 
1676  giraffe_rebinning_destroy(rebinning);
1677  rebinning = giraffe_rebinning_new();
1678 
1679  status = giraffe_rebin_spectra(rebinning, extraction, fibers,
1680  localization, grating, slitgeometry,
1681  wcalcoeff, rebin_config);
1682 
1683  if (status) {
1684  cpl_msg_error(_id, "Rebinning of science spectra failed! "
1685  "Aborting...");
1686 
1687  giraffe_rebinning_destroy(rebinning);
1688 
1689  giraffe_extraction_destroy(extraction);
1690  giraffe_localization_destroy(localization);
1691 
1692  giraffe_table_delete(wcalcoeff);
1693 
1694  giraffe_table_delete(slitgeometry);
1695  giraffe_table_delete(grating);
1696  giraffe_table_delete(fibers);
1697 
1698  giraffe_rebin_config_destroy(rebin_config);
1699 
1700  return 1;
1701 
1702  }
1703 
1704  }
1705 
1706  giraffe_extraction_destroy(extraction);
1707  extraction = NULL;
1708 
1709  giraffe_localization_destroy(localization);
1710  localization = NULL;
1711 
1712  giraffe_rebin_config_destroy(rebin_config);
1713  rebin_config = NULL;
1714 
1715 
1716  /*
1717  * Compute barycentric correction for each object spectrum (fiber)
1718  */
1719 
1720  status = giraffe_add_rvcorrection(fibers, rebinning->spectra);
1721 
1722  switch (status) {
1723  case 0:
1724  {
1725  break;
1726  }
1727 
1728  case 1:
1729  {
1730  cpl_msg_warning(_id, "Missing observation time properties! "
1731  "Barycentric correction computation "
1732  "skipped!");
1733  status = 0;
1734  break;
1735  }
1736  case 2:
1737  {
1738  cpl_msg_warning(_id, "Missing telescope location properties! "
1739  "Barycentric correction computation "
1740  "skipped!");
1741  status = 0;
1742  break;
1743  }
1744  case 3:
1745  {
1746  cpl_msg_warning(_id, "Object positions are not available "
1747  "Barycentric correction computation "
1748  "skipped!");
1749  status = 0;
1750  break;
1751  }
1752  default:
1753  {
1754  cpl_msg_error(_id, "Barycentric correction computation "
1755  "failed! Aborting...");
1756 
1757  giraffe_rebinning_destroy(rebinning);
1758 
1759  giraffe_table_delete(wcalcoeff);
1760 
1761  giraffe_table_delete(slitgeometry);
1762  giraffe_table_delete(grating);
1763  giraffe_table_delete(fibers);
1764 
1765  return 1;
1766  break;
1767  }
1768 
1769  }
1770 
1771 
1772  /*
1773  * Save and register the results of the spectrum rebinning.
1774  */
1775 
1776  /* Rebinned spectra */
1777 
1778  giraffe_image_add_info(rebinning->spectra, &info, set);
1779 
1780  rbin_frame = giraffe_frame_create_image(rebinning->spectra,
1781  GIFRAME_SCIENCE_RBNSPECTRA,
1782  CPL_FRAME_LEVEL_FINAL,
1783  TRUE, TRUE);
1784 
1785  if (rbin_frame == NULL) {
1786  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
1787 
1788  giraffe_rebinning_destroy(rebinning);
1789 
1790  giraffe_table_delete(wcalcoeff);
1791 
1792  giraffe_table_delete(slitgeometry);
1793  giraffe_table_delete(grating);
1794  giraffe_table_delete(fibers);
1795 
1796  return 1;
1797  }
1798 
1799  status = giraffe_fiberlist_attach(rbin_frame, fibers);
1800 
1801  if (status) {
1802  cpl_msg_error(_id, "Cannot attach fiber setup to local "
1803  "file '%s'! Aborting ...",
1804  cpl_frame_get_filename(rbin_frame));
1805 
1806  giraffe_rebinning_destroy(rebinning);
1807  giraffe_table_delete(wcalcoeff);
1808 
1809  giraffe_table_delete(slitgeometry);
1810  giraffe_table_delete(grating);
1811  giraffe_table_delete(fibers);
1812 
1813  cpl_frame_delete(rbin_frame);
1814 
1815  return 1;
1816  }
1817 
1818  cpl_frameset_insert(set, rbin_frame);
1819  flux_filename = cpl_strdup(cpl_frame_get_filename(rbin_frame));
1820 
1821  /* Rebinned spectra errors */
1822 
1823  giraffe_image_add_info(rebinning->errors, &info, set);
1824 
1825  rbin_frame = giraffe_frame_create_image(rebinning->errors,
1826  GIFRAME_SCIENCE_RBNERRORS,
1827  CPL_FRAME_LEVEL_FINAL,
1828  TRUE, TRUE);
1829 
1830  if (rbin_frame == NULL) {
1831  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
1832 
1833  giraffe_rebinning_destroy(rebinning);
1834 
1835  giraffe_table_delete(wcalcoeff);
1836 
1837  giraffe_table_delete(slitgeometry);
1838  giraffe_table_delete(grating);
1839  giraffe_table_delete(fibers);
1840  cpl_free(flux_filename);
1841 
1842  return 1;
1843  }
1844 
1845  status = giraffe_fiberlist_attach(rbin_frame, fibers);
1846 
1847  if (status) {
1848  cpl_msg_error(_id, "Cannot attach fiber setup to local "
1849  "file '%s'! Aborting ...",
1850  cpl_frame_get_filename(rbin_frame));
1851 
1852  giraffe_rebinning_destroy(rebinning);
1853 
1854  giraffe_table_delete(wcalcoeff);
1855 
1856  giraffe_table_delete(slitgeometry);
1857  giraffe_table_delete(grating);
1858  giraffe_table_delete(fibers);
1859 
1860  cpl_frame_delete(rbin_frame);
1861  cpl_free(flux_filename);
1862 
1863  return 1;
1864  }
1865 
1866  cpl_frameset_insert(set, rbin_frame);
1867  err_filename = cpl_strdup(cpl_frame_get_filename(rbin_frame));
1868 
1869  properties = giraffe_image_get_properties(rebinning->spectra);
1870  mode = giraffe_get_mode(properties);
1871 
1872  /*
1873  * Optionally generate spectra in Science Data Product (SDP) format.
1874  */
1875  p = cpl_parameterlist_find(config, "giraffe.sdp.format.generate");
1876  cx_assert(p != NULL);
1877  gensdp = cpl_parameter_get_bool(p);
1878  p = cpl_parameterlist_find(config, "giraffe.sdp.nassoc.keys");
1879  cx_assert(p != NULL);
1880  nassoc_keys = cpl_parameter_get_int(p);
1881  p = NULL;
1882  if (gensdp) {
1883  if (mode == GIMODE_MEDUSA) {
1884  status = _giraffe_make_sdp_spectra(flux_filename, err_filename,
1885  nassoc_keys, set, config, _id);
1886  if (status) {
1887  cpl_msg_error(_id, "Failed to generate spectra in Science Data"
1888  " Product format.");
1889 
1890  giraffe_rebinning_destroy(rebinning);
1891 
1892  giraffe_table_delete(wcalcoeff);
1893 
1894  giraffe_table_delete(slitgeometry);
1895  giraffe_table_delete(grating);
1896  giraffe_table_delete(fibers);
1897 
1898  cpl_free(flux_filename);
1899  cpl_free(err_filename);
1900 
1901  return 1;
1902  }
1903  } else {
1904  cpl_msg_warning(_id, "Requested to generate SDP 1D spectra, but"
1905  " this is currently only supported for the MEDUSA"
1906  " mode. Skipping SDP generation.");
1907  }
1908  }
1909  cpl_free(flux_filename);
1910  cpl_free(err_filename);
1911 
1912 
1913  /*
1914  * Optional image and data cube construction (only for IFU and Argus)
1915  */
1916 
1917  if (mode == GIMODE_IFU || mode == GIMODE_ARGUS) {
1918 
1919  cpl_frame* rimg_frame = NULL;
1920 
1921  GiFieldOfView* fov = NULL;
1922 
1923  GiFieldOfViewConfig* fov_config = NULL;
1924 
1925  GiFieldOfViewCubeFormat cube_format = GIFOV_FORMAT_ESO3D;
1926 
1927 
1928  fov_config = giraffe_fov_config_create(config);
1929 
1930  cube_format = fov_config->format;
1931 
1932 
1933  cpl_msg_info(_id, "Reconstructing image and data cube from rebinned "
1934  "spectra ...");
1935 
1936  fov = giraffe_fov_new();
1937 
1938  status = giraffe_fov_build(fov, rebinning, fibers, wcalcoeff, grating,
1939  slitgeometry, fov_config);
1940 
1941  if (status) {
1942 
1943  if (status == -2) {
1944  cpl_msg_warning(_id, "No reconstructed image was built. "
1945  "Fiber list has no fiber position "
1946  "information.");
1947  }
1948  else {
1949  cpl_msg_error(_id, "Image reconstruction failed! Aborting...");
1950 
1951  giraffe_fov_delete(fov);
1952  giraffe_rebinning_destroy(rebinning);
1953 
1954  giraffe_table_delete(wcalcoeff);
1955 
1956  giraffe_table_delete(slitgeometry);
1957  giraffe_table_delete(grating);
1958  giraffe_table_delete(fibers);
1959 
1960  giraffe_fov_config_destroy(fov_config);
1961 
1962  return 1;
1963  }
1964 
1965  }
1966 
1967  giraffe_fov_config_destroy(fov_config);
1968 
1969 
1970  /*
1971  * Save and register the results of the image reconstruction.
1972  */
1973 
1974  /* Reconstructed image */
1975 
1976  giraffe_image_add_info(fov->fov.spectra, &info, set);
1977 
1978  rimg_frame = giraffe_frame_create_image(fov->fov.spectra,
1979  GIFRAME_SCIENCE_RCSPECTRA,
1980  CPL_FRAME_LEVEL_FINAL,
1981  TRUE, TRUE);
1982 
1983  if (rimg_frame == NULL) {
1984  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
1985 
1986  giraffe_fov_delete(fov);
1987  giraffe_rebinning_destroy(rebinning);
1988 
1989  giraffe_table_delete(wcalcoeff);
1990 
1991  giraffe_table_delete(slitgeometry);
1992  giraffe_table_delete(grating);
1993  giraffe_table_delete(fibers);
1994 
1995  return 1;
1996  }
1997 
1998  cpl_frameset_insert(set, rimg_frame);
1999 
2000 
2001  /* Reconstructed image errors */
2002 
2003  giraffe_image_add_info(fov->fov.errors, &info, set);
2004 
2005  rimg_frame = giraffe_frame_create_image(fov->fov.errors,
2006  GIFRAME_SCIENCE_RCERRORS,
2007  CPL_FRAME_LEVEL_FINAL,
2008  TRUE, TRUE);
2009 
2010  if (rimg_frame == NULL) {
2011  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
2012 
2013  giraffe_fov_delete(fov);
2014  giraffe_rebinning_destroy(rebinning);
2015 
2016  giraffe_table_delete(wcalcoeff);
2017 
2018  giraffe_table_delete(slitgeometry);
2019  giraffe_table_delete(grating);
2020  giraffe_table_delete(fibers);
2021 
2022  return 1;
2023  }
2024 
2025  cpl_frameset_insert(set, rimg_frame);
2026 
2027 
2028  /* Save data cubes according to format selection */
2029 
2030  if (cube_format == GIFOV_FORMAT_SINGLE) {
2031 
2032  /* Spectrum cube */
2033 
2034  if (fov->cubes.spectra != NULL) {
2035 
2036  cxint component = 0;
2037 
2038  GiFrameCreator creator = (GiFrameCreator) giraffe_fov_save_cubes;
2039 
2040 
2041  properties = giraffe_image_get_properties(rebinning->spectra);
2042  properties = cpl_propertylist_duplicate(properties);
2043 
2044  giraffe_add_frameset_info(properties, set, info.sequence);
2045 
2046  rimg_frame = giraffe_frame_create(GIFRAME_SCIENCE_CUBE_SPECTRA,
2047  CPL_FRAME_LEVEL_FINAL,
2048  properties,
2049  fov,
2050  &component,
2051  creator);
2052 
2053  cpl_propertylist_delete(properties);
2054  properties = NULL;
2055 
2056  if (rimg_frame == NULL) {
2057  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
2058 
2059  giraffe_fov_delete(fov);
2060  fov = NULL;
2061 
2062  giraffe_rebinning_destroy(rebinning);
2063  rebinning = NULL;
2064 
2065  giraffe_table_delete(wcalcoeff);
2066  wcalcoeff = NULL;
2067 
2068  giraffe_table_delete(slitgeometry);
2069  slitgeometry = NULL;
2070 
2071  giraffe_table_delete(grating);
2072  grating = NULL;
2073 
2074  giraffe_table_delete(fibers);
2075  fibers = NULL;
2076 
2077  return 1;
2078  }
2079 
2080  status = giraffe_fiberlist_attach(rimg_frame, fibers);
2081 
2082  if (status != 0) {
2083  cpl_msg_error(_id, "Cannot attach fiber setup to local "
2084  "file '%s'! Aborting ...",
2085  cpl_frame_get_filename(rimg_frame));
2086 
2087  cpl_frame_delete(rimg_frame);
2088 
2089  giraffe_fov_delete(fov);
2090  fov = NULL;
2091 
2092  giraffe_rebinning_destroy(rebinning);
2093  rebinning = NULL;
2094 
2095  giraffe_table_delete(wcalcoeff);
2096  wcalcoeff = NULL;
2097 
2098  giraffe_table_delete(slitgeometry);
2099  slitgeometry = NULL;
2100 
2101  giraffe_table_delete(grating);
2102  grating = NULL;
2103 
2104  giraffe_table_delete(fibers);
2105  fibers = NULL;
2106 
2107  return 1;
2108  }
2109 
2110  cpl_frameset_insert(set, rimg_frame);
2111 
2112  }
2113 
2114  /* Error cube */
2115 
2116  if (fov->cubes.errors != NULL) {
2117 
2118  cxint component = 1;
2119 
2120  GiFrameCreator creator = (GiFrameCreator) giraffe_fov_save_cubes;
2121 
2122 
2123  properties = giraffe_image_get_properties(rebinning->errors);
2124  properties = cpl_propertylist_duplicate(properties);
2125 
2126  giraffe_add_frameset_info(properties, set, info.sequence);
2127 
2128  rimg_frame = giraffe_frame_create(GIFRAME_SCIENCE_CUBE_ERRORS,
2129  CPL_FRAME_LEVEL_FINAL,
2130  properties,
2131  fov,
2132  &component,
2133  creator);
2134 
2135  cpl_propertylist_delete(properties);
2136  properties = NULL;
2137 
2138  if (rimg_frame == NULL) {
2139  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
2140 
2141  giraffe_fov_delete(fov);
2142  fov = NULL;
2143 
2144  giraffe_rebinning_destroy(rebinning);
2145  rebinning = NULL;
2146 
2147  giraffe_table_delete(wcalcoeff);
2148  wcalcoeff = NULL;
2149 
2150  giraffe_table_delete(slitgeometry);
2151  slitgeometry = NULL;
2152 
2153  giraffe_table_delete(grating);
2154  grating = NULL;
2155 
2156  giraffe_table_delete(fibers);
2157  fibers = NULL;
2158 
2159  return 1;
2160  }
2161 
2162  status = giraffe_fiberlist_attach(rimg_frame, fibers);
2163 
2164  if (status != 0) {
2165  cpl_msg_error(_id, "Cannot attach fiber setup to local "
2166  "file '%s'! Aborting ...",
2167  cpl_frame_get_filename(rimg_frame));
2168 
2169  cpl_frame_delete(rimg_frame);
2170 
2171  giraffe_fov_delete(fov);
2172  fov = NULL;
2173 
2174  giraffe_rebinning_destroy(rebinning);
2175  rebinning = NULL;
2176 
2177  giraffe_table_delete(wcalcoeff);
2178  wcalcoeff = NULL;
2179 
2180  giraffe_table_delete(slitgeometry);
2181  slitgeometry = NULL;
2182 
2183  giraffe_table_delete(grating);
2184  grating = NULL;
2185 
2186  giraffe_table_delete(fibers);
2187  fibers = NULL;
2188 
2189  return 1;
2190  }
2191 
2192  cpl_frameset_insert(set, rimg_frame);
2193  }
2194 
2195  }
2196  else {
2197 
2198  /* Data Cube (ESO 3D format) */
2199 
2200  GiFrameCreator creator = (GiFrameCreator) giraffe_fov_save_cubes_eso3d;
2201 
2202  properties = giraffe_image_get_properties(rebinning->spectra);
2203  properties = cpl_propertylist_duplicate(properties);
2204 
2205  giraffe_add_frameset_info(properties, set, info.sequence);
2206 
2207  rimg_frame = giraffe_frame_create(GIFRAME_SCIENCE_CUBE,
2208  CPL_FRAME_LEVEL_FINAL,
2209  properties,
2210  fov,
2211  NULL,
2212  creator);
2213 
2214  cpl_propertylist_delete(properties);
2215  properties = NULL;
2216 
2217  if (rimg_frame == NULL) {
2218  cpl_msg_error(_id, "Cannot create local file! Aborting ...");
2219 
2220  giraffe_fov_delete(fov);
2221  fov = NULL;
2222 
2223  giraffe_rebinning_destroy(rebinning);
2224  rebinning = NULL;
2225 
2226  giraffe_table_delete(wcalcoeff);
2227  wcalcoeff = NULL;
2228 
2229  giraffe_table_delete(slitgeometry);
2230  slitgeometry = NULL;
2231 
2232  giraffe_table_delete(grating);
2233  grating = NULL;
2234 
2235  giraffe_table_delete(fibers);
2236  fibers = NULL;
2237 
2238  return 1;
2239  }
2240 
2241  status = giraffe_fiberlist_attach(rimg_frame, fibers);
2242 
2243  if (status != 0) {
2244  cpl_msg_error(_id, "Cannot attach fiber setup to local "
2245  "file '%s'! Aborting ...",
2246  cpl_frame_get_filename(rimg_frame));
2247 
2248  cpl_frame_delete(rimg_frame);
2249 
2250  giraffe_fov_delete(fov);
2251  fov = NULL;
2252 
2253  giraffe_rebinning_destroy(rebinning);
2254  rebinning = NULL;
2255 
2256  giraffe_table_delete(wcalcoeff);
2257  wcalcoeff = NULL;
2258 
2259  giraffe_table_delete(slitgeometry);
2260  slitgeometry = NULL;
2261 
2262  giraffe_table_delete(grating);
2263  grating = NULL;
2264 
2265  giraffe_table_delete(fibers);
2266  fibers = NULL;
2267 
2268  return 1;
2269  }
2270 
2271  cpl_frameset_insert(set, rimg_frame);
2272 
2273  }
2274 
2275  giraffe_fov_delete(fov);
2276  fov = NULL;
2277 
2278  }
2279 
2280 
2281  /*
2282  * Cleanup
2283  */
2284 
2285  giraffe_table_delete(wcalcoeff);
2286 
2287  giraffe_table_delete(slitgeometry);
2288  giraffe_table_delete(grating);
2289  giraffe_table_delete(fibers);
2290 
2291  giraffe_rebinning_destroy(rebinning);
2292 
2293  return 0;
2294 
2295 }
2296 
2297 
2298 /*
2299  * Build table of contents, i.e. the list of available plugins, for
2300  * this module. This function is exported.
2301  */
2302 
2303 int
2304 cpl_plugin_get_info(cpl_pluginlist* list)
2305 {
2306 
2307  cpl_recipe* recipe = cx_calloc(1, sizeof *recipe);
2308  cpl_plugin* plugin = &recipe->interface;
2309 
2310 
2311  cpl_plugin_init(plugin,
2312  CPL_PLUGIN_API,
2313  GIRAFFE_BINARY_VERSION,
2314  CPL_PLUGIN_TYPE_RECIPE,
2315  "giscience",
2316  "Process a science observation.",
2317  "For detailed information please refer to the "
2318  "GIRAFFE pipeline user manual.\nIt is available at "
2319  "http://www.eso.org/pipelines.",
2320  "Giraffe Pipeline",
2321  PACKAGE_BUGREPORT,
2323  giscience_create,
2324  giscience_exec,
2325  giscience_destroy);
2326 
2327  cpl_pluginlist_append(list, plugin);
2328 
2329  return 0;
2330 
2331 }
2332 
2333 
2334 /* Structure of a lookup table entry for the LUT used to get values for
2335  * SPEC_RES. */
2336 typedef struct _giraffe_lut_entry {
2337  const char* expmode;
2338  double specres;
2339  double lamrms; /* given in (px), to convert to (nm) one needs:
2340  lamrms * spec_length / 4100 */
2341  double spec_length;
2342  int lamnlin;
2343 } giraffe_lut_entry;
2344 
2345 
2346 #ifndef NDEBUG
2347 
2352 static cpl_boolean _giraffe_lut_is_sorted(const giraffe_lut_entry* lut,
2353  size_t nentries)
2354 {
2355  size_t i;
2356  if (nentries < 2) return CPL_TRUE;
2357  for (i = 0; i < nentries - 1; ++i) {
2358  if (strcmp(lut[i].expmode, lut[i+1].expmode) >= 0) {
2359  return CPL_FALSE;
2360  }
2361  }
2362  return CPL_TRUE;
2363 }
2364 
2365 #endif /* NDEBUG */
2366 
2367 
2375 static const giraffe_lut_entry* _giraffe_find_lut_entry(const char* expmode)
2376 {
2377  static giraffe_lut_entry lut[] = {
2378  /* INS_EXP_MODE lambda LAMRMS spec_length LAMNLIN
2379  * R = ------------- (px) (nm) line count
2380  * delta(lambda)
2381  */
2382  {"H379.", 25000, 0.2250, 20.0, 60},
2383  {"H379.0", 25000, 0.2250, 20.0, 60},
2384  {"H395.8", 22000, 0.1913, 21.1, 53},
2385  {"H412.4", 30000, 0.1326, 22.3, 48},
2386  {"H429.7", 23000, 0.1471, 23.5, 67},
2387  {"H447.1", 20000, 0.0906, 24.9, 63},
2388  {"H447.1A", 20000, 0.0906, 24.9, 63},
2389  {"H447.1B", 31000, 0.1297, 23.5, 58},
2390  {"H465.6", 23000, 0.1573, 22.4, 57},
2391  {"H484.5", 19000, 0.1175, 23.5, 57},
2392  {"H484.5A", 19000, 0.1175, 23.5, 57},
2393  {"H484.5B", 33000, 0.0907, 24.6, 61},
2394  {"H504.8", 22000, 0.1726, 25.2, 56},
2395  {"H525.8", 17000, 0.1397, 25.8, 49},
2396  {"H525.8A", 17000, 0.1397, 25.8, 49},
2397  {"H525.8B", 29000, 0.2014, 26.4, 44},
2398  {"H548.8", 20000, 0.2389, 26.9, 51},
2399  {"H572.8", 27000, 0.1844, 27.5, 49},
2400  {"H599.3", 18000, 0.1683, 28.3, 57},
2401  {"H627.3", 24000, 0.1268, 29.0, 50},
2402  {"H651.5", 17000, 0.1185, 30.0, 42},
2403  {"H651.5A", 17000, 0.1185, 30.0, 42},
2404  {"H651.5B", 35000, 0.1253, 31.5, 37},
2405  {"H665.", 17000, 0.2076, 33.0, 45},
2406  {"H665.0", 17000, 0.2076, 33.0, 45},
2407  {"H679.7", 19000, 0.1621, 34.5, 51},
2408  {"H710.5", 25000, 0.1495, 36.0, 48},
2409  {"H737.", 16000, 0.1456, 37.5, 47},
2410  {"H737.0", 16000, 0.1456, 37.5, 47},
2411  {"H737.0A", 16000, 0.1456, 37.5, 47},
2412  {"H737.0B", 35000, 0.1147, 39.5, 40},
2413  {"H769.1", 19000, 0.2811, 41.8, 35},
2414  {"H805.3", 14000, 0.2662, 42.4, 39},
2415  {"H805.3A", 14000, 0.2662, 42.4, 39},
2416  {"H805.3B", 25000, 0.2342, 42.9, 28},
2417  {"H836.6", 16000, 0.2032, 43.3, 21},
2418  {"H836.6A", 16000, 0.2032, 43.3, 21},
2419  {"H836.6B", 34000, 0.1073, 43.7, 14},
2420  {"H875.7", 18000, 0.2026, 44.3, 29},
2421  {"H920.5", 12000, 0.1568, 44.9, 32},
2422  {"H920.5A", 12000, 0.1568, 44.9, 32},
2423  {"H920.5B", 24000, 0.2531, 45.9, 28},
2424  {"L385.7", 7500, 0.3358, 58.0, 43},
2425  {"L427.2", 6300, 0.2152, 61.1, 62},
2426  {"L479.7", 7500, 0.1554, 71.0, 61},
2427  {"L543.1", 5800, 0.2065, 82.1, 58},
2428  {"L614.2", 6600, 0.1803, 79.0, 51},
2429  {"L682.2", 8100, 0.1843, 74.0, 50},
2430  {"L773.4", 5400, 0.1617, 94.0, 44},
2431  {"L881.7", 6600, 0.1614, 119.0, 29}
2432  };
2433  static const size_t nentries = sizeof(lut) / sizeof(giraffe_lut_entry);
2434  int low = 0; /* Bottom of search region. */
2435  int high = (int)nentries - 1; /* Top of search region. */
2436 
2437  assert(_giraffe_lut_is_sorted(lut, nentries));
2438  assert(expmode != NULL);
2439 
2440  /* Perform a binary search for the entry. */
2441  do {
2442  int mid = (low + high) >> 1; /* Find mid point of search range. */
2443  int result = strcmp(expmode, lut[mid].expmode);
2444  if (result == 0) {
2445  return &lut[mid];
2446  } else if (result < 0) {
2447  high = mid - 1;
2448  } else {
2449  low = mid + 1;
2450  }
2451  } while (high >= low);
2452  return NULL;
2453 }
2454 
2461 static double _giraffe_lookup_specres(const char* expmode)
2462 {
2463  const giraffe_lut_entry* entry = _giraffe_find_lut_entry(expmode);
2464  if (entry == NULL) return NAN;
2465  return entry->specres;
2466 }
2467 
2474 static double _giraffe_lookup_lamrms(const char* expmode)
2475 {
2476  const giraffe_lut_entry* entry = _giraffe_find_lut_entry(expmode);
2477  if (entry == NULL) return NAN;
2478  if (isnan(entry->lamrms) || isnan(entry->spec_length)) return NAN;
2479  return entry->lamrms * entry->spec_length / 4100.;
2480 }
2481 
2488 static double _giraffe_lookup_lamnlin(const char* expmode)
2489 {
2490  const giraffe_lut_entry* entry = _giraffe_find_lut_entry(expmode);
2491  if (entry == NULL) return -1;
2492  return entry->lamnlin;
2493 }
2494 
2495 
2506 static cpl_type _giraffe_calc_wave_type(double crval2, double crpix2,
2507  double cdelt2, cpl_size naxis2)
2508 {
2509  static const double errfrac = 0.02;
2510  static const double single_precision_digits = 7;
2511  double lo = (1.0 - crpix2) * cdelt2;
2512  double hi = ((double)naxis2 - crpix2) * cdelt2;
2513  double maxwave = crval2 + (hi > lo ? hi : lo);
2514  double binfrac = (maxwave != 0.0) ? fabs(cdelt2 / maxwave) : 0.0;
2515  if (binfrac * errfrac < pow(10, -single_precision_digits)) {
2516  return CPL_TYPE_DOUBLE;
2517  } else {
2518  return CPL_TYPE_FLOAT;
2519  }
2520 }
2521 
2522 
2532 static cpl_boolean _giraffe_ancillary_data_available(const char* filename,
2533  const GiTable* fibertable)
2534 {
2535  cpl_table* tbl = giraffe_table_get(fibertable);
2536  cpl_size i;
2537  const char** spectypes = NULL;
2538 
2539  assert(filename != NULL);
2540 
2541  cpl_error_ensure(tbl != NULL, cpl_error_get_code(), return CPL_FALSE,
2542  "The fiber table is not available for '%s'.", filename);
2543 
2544  spectypes = cpl_table_get_data_string_const(tbl, GIALIAS_COLUMN_TYPE);
2545  cpl_error_ensure(spectypes != NULL, cpl_error_get_code(), return CPL_FALSE,
2546  "Could not fetch the '%s' column from the fiber setup"
2547  " table in '%s'.", GIALIAS_COLUMN_TYPE, filename);
2548  for (i = 0; i < cpl_table_get_nrow(tbl); ++i) {
2549  if (strcmp(spectypes[i], "M") != 0) {
2550  return CPL_TRUE;
2551  }
2552  }
2553  return CPL_FALSE;
2554 }
2555 
2556 
2576 static cpl_error_code _giraffe_make_ancillary_file(cpl_frameset* allframes,
2577  const char* outputfile,
2578  const char* infilename,
2579  const GiImage* fluximage,
2580  const GiTable* fibertable)
2581 {
2582  cpl_error_code error = CPL_ERROR_NONE;
2583  cxint retcode;
2584  cpl_frame* frame = NULL;
2585  cpl_image* srcimg = giraffe_image_get(fluximage);
2586  GiImage* image = NULL;
2587  GiTable* table = giraffe_table_duplicate(fibertable);
2588  cpl_image* img = NULL;
2589  cpl_table* tbl = giraffe_table_get(table);
2590  cpl_image* subimg = NULL;
2591  cpl_propertylist* comments = NULL;
2592  cpl_size ny, i;
2593  int* indices = NULL;
2594  const char** spectypes = NULL;
2595 
2596  assert(allframes != NULL);
2597  assert(outputfile != NULL);
2598  assert(infilename != NULL);
2599 
2600  cpl_error_ensure(srcimg != NULL && table != NULL && tbl != NULL,
2601  cpl_error_get_code(), goto cleanup,
2602  "The image or table are not available for '%s'.",
2603  infilename);
2604 
2605  /* Setup a new frame for the output file. */
2606  frame = cpl_frame_new();
2607  error |= cpl_frame_set_filename(frame, outputfile);
2608  error |= cpl_frame_set_tag(frame, GIALIAS_ASSO_PROCATG_VALUE);
2609  error |= cpl_frame_set_type(frame, CPL_FRAME_TYPE_IMAGE);
2610  error |= cpl_frame_set_group(frame, CPL_FRAME_GROUP_PRODUCT);
2611  error |= cpl_frame_set_level(frame, CPL_FRAME_LEVEL_FINAL);
2612  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
2613  "Failed to setup a new output frame for '%s'.",
2614  outputfile);
2615 
2616  /* First step to filter out the science entries in the image and fiber setup
2617  * table is to go through the table entries, mark the entries to remove, and
2618  * delete the ones that are marked. */
2619  spectypes = cpl_table_get_data_string_const(tbl, GIALIAS_COLUMN_TYPE);
2620  cpl_error_ensure(spectypes != NULL, cpl_error_get_code(), goto cleanup,
2621  "Could not fetch the '%s' column from the fiber setup"
2622  " table in '%s'.", GIALIAS_COLUMN_TYPE, infilename);
2623  for (i = 0; i < cpl_table_get_nrow(tbl); ++i) {
2624  /* Deselect all non-science spectra. Initially all rows are selected. */
2625  if (strcmp(spectypes[i], "M") != 0) {
2626  error |= cpl_table_unselect_row(tbl, i);
2627  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
2628  "Failed to deselect row %"CPL_SIZE_FORMAT
2629  " in fiber setup table from '%s'.", i, infilename);
2630  }
2631  }
2632  error |= cpl_table_erase_selected(tbl);
2633  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
2634  "Failed to erase selected entries from fiber setup table"
2635  " in '%s'.", infilename);
2636 
2637  /* Create a new output image which is wide enough to store the data with the
2638  * stripped out spectra removed. */
2639  ny = cpl_image_get_size_y(srcimg);
2640  image = giraffe_image_create(cpl_image_get_type(srcimg),
2641  cpl_table_get_nrow(tbl), ny);
2642  img = giraffe_image_get(image);
2643  retcode = giraffe_image_set_properties(
2644  image, giraffe_image_get_properties(fluximage));
2645  cpl_error_ensure(image != NULL && img != NULL && retcode == 0,
2646  cpl_error_get_code(), goto cleanup,
2647  "Failed to create image for output file '%s'.",
2648  outputfile);
2649 
2650  error |= cpl_propertylist_update_string(giraffe_image_get_properties(image),
2651  GIALIAS_PROCATG,
2652  GIALIAS_ASSO_PROCATG_VALUE);
2653  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
2654  "Could not update keyword '%s' for output file '%s'.",
2655  GIALIAS_PROCATG, outputfile);
2656 
2657  /* Strip out extra comments which will be added by CPL anyway. */
2658  if (cpl_propertylist_has(giraffe_image_get_properties(image), "COMMENT")) {
2659  long i;
2660  const char* comments_to_remove[] = {
2661  " FITS (Flexible Image Transport System) format is defined in"
2662  " 'Astronomy",
2663  " and Astrophysics', volume 376, page 359; bibcode: 2001A&A..."
2664  "376..359H",
2665  NULL
2666  };
2667  cpl_propertylist* props = giraffe_image_get_properties(image);
2668  comments = cpl_propertylist_new();
2669  error |= cpl_propertylist_copy_property_regexp(comments, props,
2670  "^COMMENT$", 0);
2671  cpl_propertylist_erase_regexp(props, "^COMMENT$", 0);
2672  for (i = 0; i < cpl_propertylist_get_size(comments); ++i) {
2673  const char** cmnt_str;
2674  cpl_property* p = cpl_propertylist_get(comments, i);
2675  for (cmnt_str = comments_to_remove; *cmnt_str != NULL; ++cmnt_str) {
2676  if (strcmp(cpl_property_get_string(p), *cmnt_str) == 0) {
2677  goto dont_add_comment;
2678  }
2679  }
2680  /* Add back comments that should not be removed. */
2681  error |= cpl_propertylist_append_property(props, p);
2682  dont_add_comment:
2683  /* Land up here if the comment was found in the comments_to_remove
2684  * list of strings. */
2685  ;
2686  }
2687  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
2688  "Failed to cleanup comments in primary HDU for '%s'.",
2689  outputfile);
2690  cpl_propertylist_delete(comments);
2691  comments = NULL;
2692  }
2693 
2694  /* We now have to relabel the index numbers in the fiber setup table and
2695  * copy corresponding image columns to the new image. */
2696  indices = cpl_table_get_data_int(tbl, GIALIAS_COLUMN_INDEX);
2697  cpl_error_ensure(indices != NULL, cpl_error_get_code(), goto cleanup,
2698  "Could not fetch the '%s' column from the fiber setup"
2699  " table in '%s'.", GIALIAS_COLUMN_INDEX, infilename);
2700  for (i = 0; i < cpl_table_get_nrow(tbl); ++i) {
2701  cpl_size oldindex = indices[i];
2702  cpl_size newindex = i+1;
2703  indices[i] = newindex;
2704  subimg = cpl_image_extract(srcimg, oldindex, 1, oldindex, ny);
2705  cpl_error_ensure(subimg != NULL, cpl_error_get_code(), goto cleanup,
2706  "Could not extract sub image from '%s' at column %"
2707  CPL_SIZE_FORMAT".", infilename, oldindex);
2708  error |= cpl_image_copy(img, subimg, newindex, 1);
2709  cpl_image_delete(subimg);
2710  subimg = NULL;
2711  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
2712  "Could write sub image from '%s' at column %"
2713  CPL_SIZE_FORMAT" to new image in '%s' at column %"
2714  CPL_SIZE_FORMAT".", infilename, oldindex, outputfile,
2715  newindex);
2716  }
2717 
2718  /* Now write the actual FITS file. */
2719  retcode = giraffe_image_save(image, outputfile);
2720  cpl_error_ensure(retcode == 0, cpl_error_get_code(), goto cleanup,
2721  "Failed to write image to file '%s'.", outputfile);
2722  retcode = giraffe_fiberlist_attach(frame, table);
2723  cpl_error_ensure(retcode == 0, cpl_error_get_code(), goto cleanup,
2724  "Failed to attach the fiber setup table to file '%s'.",
2725  outputfile);
2726 
2727  /* Add the new frame to the output frame set for the ancillary file.
2728  * Note: this should be the last step so that we do not delete this frame
2729  * anymore once in the frame set. */
2730  error |= cpl_frameset_insert(allframes, frame);
2731  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
2732  "Could not add a new frame to the frame list for '%s'.",
2733  outputfile);
2734 
2735  giraffe_image_delete(image);
2736  giraffe_table_delete(table);
2737  return CPL_ERROR_NONE;
2738 
2739 cleanup:
2740  /* Error handling. Note: NULL pointer checks done by delete functions. */
2741  cpl_image_delete(subimg);
2742  giraffe_image_delete(image);
2743  giraffe_table_delete(table);
2744  cpl_frame_delete(frame);
2745  cpl_propertylist_delete(comments);
2746  return cpl_error_get_code();
2747 }
2748 
2749 
2756 static char* _giraffe_calc_format_string(cpl_size maxfiles)
2757 {
2758  double ndigits = 1.0;
2759  if (maxfiles > 1) {
2760  /* Figure out how many digits must be printed to support a index number
2761  * as large as 'maxfiles'. */
2762  ndigits = ceil(log10((double)maxfiles + 1.0));
2763  }
2764  return cpl_sprintf("science_spectrum_%%0%.0f"CPL_SIZE_FORMAT".fits",
2765  ndigits);
2766 }
2767 
2768 
2785 static cxint _giraffe_make_sdp_spectra(const cxchar* flux_filename,
2786  const cxchar* err_filename,
2787  cxint nassoc_keys,
2788  cpl_frameset* allframes,
2789  const cpl_parameterlist* parlist,
2790  const cxchar* recipe_id)
2791 {
2792  cxint result_code = 1;
2793  cxint errorcode;
2794  cpl_error_code error = CPL_ERROR_NONE;
2795  cpl_errorstate prestate;
2796  const char* ancillary_filename = "science_ancillary.fits";
2797  GiImage* fluximage = giraffe_image_new(CPL_TYPE_DOUBLE);
2798  GiImage* errimage = giraffe_image_new(CPL_TYPE_DOUBLE);
2799  GiTable* fibertable = NULL;
2800  const cxchar* fibertable_name = NULL;
2801  irplib_sdp_spectrum* spectrum = NULL;
2802  cpl_propertylist* extrakeys = cpl_propertylist_new();
2803  cpl_propertylist* tablekeys = cpl_propertylist_new();
2804  cpl_propertylist* props;
2805  char* pipe_id = cpl_sprintf("%s/%s", PACKAGE, VERSION);
2806  const char* dict_id = PRODUCT_DID;
2807  const cpl_frame* inherit = NULL;
2808  cpl_frameset* usedframes = NULL;
2809  cpl_frameset* rawframes = NULL;
2810  cpl_frameset_iterator* iterator = NULL;
2811  cpl_size nx, ny, i, filecount;
2812  double exptime = NAN;
2813  double mjdobs = NAN;
2814  double mjdend = NAN;
2815  double wavelmin = NAN;
2816  double wavelmax = NAN;
2817  double specbin = NAN;
2818  double crpix2 = NAN;
2819  double crval2 = NAN;
2820  double cdelt2 = NAN;
2821  const char* cunit2 = NULL;
2822  double specres;
2823  const char* expmode = NULL;
2824  char strbuf[64];
2825  const int* indices = NULL;
2826  const int* fps = NULL;
2827  const char** objects = NULL;
2828  const char** spectypes = NULL;
2829  const double* ras = NULL;
2830  const double* decs = NULL;
2831  const double* gcorr = NULL;
2832  const double* hcorr = NULL;
2833  const double* bcorr = NULL;
2834  char* formatstr = NULL;
2835  char* filename = NULL;
2836  cpl_type wavecoltype;
2837  cpl_array* refwavearray = NULL;
2838  cpl_array* array = NULL;
2839  float* data_float = NULL;
2840  double* data_double = NULL;
2841  cpl_vector* fluximgcol = NULL;
2842  cpl_vector* errimgcol = NULL;
2843  cpl_boolean got_ancillary_data = CPL_FALSE;
2844  cpl_size assoc_key_offset = 1;
2845  int lamnlin = -1;
2846  double lamrms = NAN;
2847  double specerr = NAN;
2848  double specsye = NAN;
2849 
2850  cpl_error_ensure(flux_filename != NULL && err_filename != NULL
2851  && allframes != NULL && parlist != NULL
2852  && recipe_id != NULL, CPL_ERROR_NULL_INPUT, goto cleanup,
2853  "NULL input parameters.");
2854 
2855  error |= cpl_propertylist_append_string(extrakeys, GIALIAS_PROCATG,
2856  GIALIAS_PROCATG_RBNSPEC_IDP);
2857  error |= cpl_propertylist_set_comment(extrakeys, GIALIAS_PROCATG,
2858  GIALIAS_PROCATG_COMMENT);
2859  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
2860  "Could not set keyword '%s'.", GIALIAS_PROCATG);
2861 
2862  /* Load the input flux and error data, including FITS header keywords. */
2863  errorcode = giraffe_image_load(fluximage, flux_filename, 0);
2864  cpl_error_ensure(errorcode == 0, cpl_error_get_code(), goto cleanup,
2865  "Could not load image data in primary HDU from '%s'.",
2866  flux_filename);
2867  errorcode = giraffe_image_load(errimage, err_filename, 0);
2868  cpl_error_ensure(errorcode == 0, cpl_error_get_code(), goto cleanup,
2869  "Could not load image data in primary HDU from '%s'.",
2870  err_filename);
2871 
2872  giraffe_error_push();
2873  fibertable = giraffe_fiberlist_load(flux_filename, 1, GIALIAS_FIBER_SETUP);
2874  if (fibertable == NULL) {
2875  fibertable = giraffe_fiberlist_load(err_filename, 1,
2876  GIALIAS_FIBER_SETUP);
2877  fibertable_name = err_filename;
2878  } else {
2879  fibertable_name = flux_filename;
2880  }
2881  cpl_error_ensure(fibertable != NULL, CPL_ERROR_DATA_NOT_FOUND, goto cleanup,
2882  "Could not load the %s table from either '%s' or '%s'.",
2883  GIALIAS_FIBER_SETUP, flux_filename, err_filename);
2884  giraffe_error_pop();
2885 
2886  /* Check that the image sizes are the same. */
2887  nx = cpl_image_get_size_x(giraffe_image_get(fluximage));
2888  ny = cpl_image_get_size_y(giraffe_image_get(fluximage));
2889  cpl_error_ensure(cpl_image_get_size_x(giraffe_image_get(errimage)) == nx
2890  && cpl_image_get_size_y(giraffe_image_get(errimage)) == ny,
2891  CPL_ERROR_INCOMPATIBLE_INPUT, goto cleanup,
2892  "The images in files '%s' and '%s' are not the same size.",
2893  flux_filename, err_filename);
2894 
2895  /* Construct the used frame list from the existing list of all frames.
2896  * The frames to be included as the used frames are RAW and CALIB. */
2897  usedframes = cpl_frameset_new();
2898  rawframes = cpl_frameset_new();
2899  iterator = cpl_frameset_iterator_new(allframes);
2900  do {
2901  const cpl_frame* frame = cpl_frameset_iterator_get_const(iterator);
2902  if (frame != NULL) {
2903  switch (cpl_frame_get_group(frame)) {
2904  case CPL_FRAME_GROUP_RAW:
2905  /* Mark the first RAW frame from which to inherit keywords. */
2906  if (inherit == NULL) inherit = frame;
2907  error |= cpl_frameset_insert(rawframes,
2908  cpl_frame_duplicate(frame));
2909  case CPL_FRAME_GROUP_CALIB:
2910  error |= cpl_frameset_insert(usedframes,
2911  cpl_frame_duplicate(frame));
2912  break;
2913  default: /* Ignore all other groups */
2914  break;
2915  }
2916  }
2917  prestate = cpl_errorstate_get();
2918  error |= cpl_frameset_iterator_advance(iterator, 1);
2919  if (error == CPL_ERROR_ACCESS_OUT_OF_RANGE) {
2920  cpl_errorstate_set(prestate);
2921  break;
2922  } else if (error != CPL_ERROR_NONE) {
2923  goto cleanup;
2924  }
2925  } while (1);
2926 
2927  cpl_error_ensure(inherit != NULL, CPL_ERROR_DATA_NOT_FOUND, goto cleanup,
2928  "No raw input frames found.");
2929 
2930  /* Fetch the EXPTIME and MJD-OBS keywords from the product file. */
2931  props = giraffe_image_get_properties(fluximage);
2932  prestate = cpl_errorstate_get();
2933  exptime = cpl_propertylist_get_double(props, GIALIAS_EXPTIME);
2934  cpl_error_ensure(cpl_errorstate_is_equal(prestate), cpl_error_get_code(),
2935  goto cleanup, "Could not find keyword '%s' in '%s'.",
2936  GIALIAS_EXPTIME, flux_filename);
2937  mjdobs = cpl_propertylist_get_double(props, GIALIAS_MJDOBS);
2938  cpl_error_ensure(cpl_errorstate_is_equal(prestate), cpl_error_get_code(),
2939  goto cleanup, "Could not find keyword '%s' in '%s'.",
2940  GIALIAS_MJDOBS, flux_filename);
2941 
2942  mjdend = mjdobs + exptime / 86400.;
2943 
2944  /* Calculate the min/max wavelength values. */
2945  crpix2 = cpl_propertylist_get_double(props, GIALIAS_CRPIX2);
2946  cpl_error_ensure(cpl_errorstate_is_equal(prestate), cpl_error_get_code(),
2947  goto cleanup, "Could not find keyword '%s' in '%s'.",
2948  GIALIAS_CRPIX2, flux_filename);
2949  crval2 = cpl_propertylist_get_double(props, GIALIAS_CRVAL2);
2950  cpl_error_ensure(cpl_errorstate_is_equal(prestate), cpl_error_get_code(),
2951  goto cleanup, "Could not find keyword '%s' in '%s'.",
2952  GIALIAS_CRVAL2, flux_filename);
2953  cdelt2 = cpl_propertylist_get_double(props, GIALIAS_CDELT2);
2954  cpl_error_ensure(cpl_errorstate_is_equal(prestate), cpl_error_get_code(),
2955  goto cleanup, "Could not find keyword '%s' in '%s'.",
2956  GIALIAS_CDELT2, flux_filename);
2957  cunit2 = cpl_propertylist_get_string(props, GIALIAS_CUNIT2);
2958  cpl_error_ensure(cunit2 != NULL, cpl_error_get_code(),
2959  goto cleanup, "Could not find keyword '%s' in '%s'.",
2960  GIALIAS_CUNIT2, flux_filename);
2961 
2962  if (strcmp(cunit2, "nm") == 0) {
2963  wavelmin = (1.0 - crpix2) * cdelt2 + crval2;
2964  wavelmax = ((double)ny - crpix2) * cdelt2 + crval2;
2965  if (wavelmax < wavelmin) {
2966  double tmp = wavelmin;
2967  wavelmin = wavelmax;
2968  wavelmax = tmp;
2969  }
2970  specbin = fabs(cdelt2);
2971  } else {
2972  cpl_msg_warning(cpl_func, "Do not know how to handle keyword %s = '%s'."
2973  " Will not set WAVELMIN, WAVELMAX or SPEC_BIN.",
2974  GIALIAS_CUNIT2, cunit2);
2975  }
2976 
2977  if (cpl_propertylist_has(props, GIALIAS_SETUPNAME)) {
2978  expmode = cpl_propertylist_get_string(props, GIALIAS_SETUPNAME);
2979  cpl_error_ensure(expmode != NULL, cpl_error_get_code(), goto cleanup,
2980  "Could not fetch the keyword '%s' from '%s'.",
2981  GIALIAS_SETUPNAME, flux_filename);
2982  } else if (cpl_propertylist_has(props, GIALIAS_GRATNAME)) {
2983  const char* name = cpl_propertylist_get_string(props, GIALIAS_GRATNAME);
2984  cpl_error_ensure(name != NULL, cpl_error_get_code(), goto cleanup,
2985  "Could not fetch the keyword '%s' from '%s'.",
2986  GIALIAS_GRATNAME, flux_filename);
2987  double wlen = cpl_propertylist_get_double(props, GIALIAS_GRATWLEN);
2988  cpl_error_ensure(cpl_errorstate_is_equal(prestate),
2989  cpl_error_get_code(), goto cleanup,
2990  "Could not find keyword '%s' in '%s'.",
2991  GIALIAS_GRATWLEN, flux_filename);
2992  strbuf[0] = name[0];
2993  sprintf(strbuf+1, "%32f", wlen);
2994  expmode = strbuf;
2995  } else {
2996  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
2997  "Neither '%s' nor '%s' and '%s' keywords were found in the"
2998  " file '%s'.", GIALIAS_SETUPNAME, GIALIAS_GRATNAME,
2999  GIALIAS_GRATWLEN, flux_filename);
3000  goto cleanup;
3001  }
3002 
3003  specres = _giraffe_lookup_specres(expmode);
3004  if (isnan(specres)) {
3005  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
3006  "The exposure mode '%s' is invalid or an unknown value."
3007  " Could not lookup the spectral resolution for 'SPEC_RES'.",
3008  expmode);
3009  goto cleanup;
3010  }
3011 
3012  /* Add the FILTER keyword as OFILTER to the extra keywords list if it
3013  * exists. The FILTER keyword itself will be deleted. */
3014  if (cpl_propertylist_has(props, "FILTER")) {
3015  prestate = cpl_errorstate_get();
3016  cpl_propertylist_copy_property(extrakeys, props, "FILTER");
3017  cpl_property* prop = cpl_propertylist_get_property(extrakeys, "FILTER");
3018  cpl_property_set_name(prop, "OFILTER");
3019  cpl_error_ensure(cpl_errorstate_is_equal(prestate),
3020  cpl_error_get_code(), goto cleanup,
3021  "Could not rename the 'FILTER' keyword.");
3022  }
3023 
3024  /* Write the ancillary data file if any ancillary data is available. */
3025  prestate = cpl_errorstate_get();
3026  got_ancillary_data = _giraffe_ancillary_data_available(flux_filename,
3027  fibertable);
3028  if (! cpl_errorstate_is_equal(prestate)) goto cleanup;
3029  if (got_ancillary_data) {
3030  error = _giraffe_make_ancillary_file(allframes, ancillary_filename,
3031  flux_filename, fluximage,
3032  fibertable);
3033  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
3034  "Failed to write the ancillary file '%s'.",
3035  ancillary_filename);
3036  }
3037 
3038  /* Create a new spectrum object and setup header keywords. */
3039  spectrum = irplib_sdp_spectrum_new();
3040  error = CPL_ERROR_NONE;
3041  error |= irplib_sdp_spectrum_set_origin(spectrum, GIALIAS_ORIGIN_VALUE);
3042  error |= irplib_sdp_spectrum_set_prodlvl(spectrum, GIALIAS_PRODLVL_VALUE);
3043  error |= irplib_sdp_spectrum_copy_dispelem(spectrum,
3044  props, GIALIAS_GRATNAME);
3045  error |= irplib_sdp_spectrum_set_specsys(spectrum, GIALIAS_SPECSYS_VALUE);
3046  error |= irplib_sdp_spectrum_set_extobj(spectrum, GIALIAS_EXT_OBJ_VALUE);
3047  /* The OBJECT, RA and DEC keywords we fill now with dummy values to maintain
3048  * the order of keywords. The actual values are set later. */
3049  error |= irplib_sdp_spectrum_set_object(spectrum, "");
3050  error |= irplib_sdp_spectrum_set_ra(spectrum, 0.0);
3051  error |= irplib_sdp_spectrum_set_dec(spectrum, 0.0);
3052  error |= irplib_sdp_spectrum_copy_exptime(spectrum, props, GIALIAS_EXPTIME);
3053  error |= irplib_sdp_spectrum_copy_texptime(spectrum,
3054  props, GIALIAS_EXPTIME);
3055  error |= irplib_sdp_spectrum_copy_mjdobs(spectrum, props, GIALIAS_MJDOBS);
3056  error |= irplib_sdp_spectrum_set_mjdend(spectrum, mjdend);
3057  if (cpl_propertylist_has(props, GIALIAS_TIMESYS)) {
3058  error |= irplib_sdp_spectrum_copy_timesys(spectrum,
3059  props, GIALIAS_TIMESYS);
3060  }
3061  error |= irplib_sdp_spectrum_copy_progid(spectrum, props, GIALIAS_PROGID);
3062  error |= irplib_sdp_spectrum_copy_obid(spectrum, 1, props, GIALIAS_OBSID);
3063  error |= irplib_sdp_spectrum_set_mepoch(spectrum, GIALIAS_M_EPOCH_VALUE);
3064  error |= irplib_sdp_spectrum_append_prov(spectrum, 1, rawframes);
3065  error |= irplib_sdp_spectrum_copy_procsoft(spectrum,
3066  props, GIALIAS_PROPIPEID);
3067  error |= irplib_sdp_spectrum_copy_obstech(spectrum, props, GIALIAS_PROTECH);
3068  error |= irplib_sdp_spectrum_set_prodcatg(spectrum, GIALIAS_PRODCATG_VALUE);
3069  error |= irplib_sdp_spectrum_set_fluxcal(spectrum, GIALIAS_FLUXCAL_VALUE);
3070  error |= irplib_sdp_spectrum_set_contnorm(spectrum, GIALIAS_CONTNORM_VALUE);
3071  /* Set dummy values for WAVELMIN, WAVELMAX and SPEC_BIN to keep the order
3072  * of the keywords. Will fill these in later with actual Heliocentric
3073  * corrected values. */
3074  error |= irplib_sdp_spectrum_set_wavelmin(spectrum, 0.0);
3075  error |= irplib_sdp_spectrum_set_wavelmax(spectrum, 0.0);
3076  error |= irplib_sdp_spectrum_set_specbin(spectrum, 0.0);
3077  error |= irplib_sdp_spectrum_set_totflux(spectrum, GIALIAS_TOTFLUX_VALUE);
3078  error |= irplib_sdp_spectrum_set_fluxerr(spectrum, GIALIAS_FLUXERR_VALUE);
3079  error |= irplib_sdp_spectrum_set_ncombine(spectrum,
3080  cpl_frameset_get_size(rawframes));
3081  error |= irplib_sdp_spectrum_set_referenc(spectrum, GIALIAS_REFERENC);
3082 
3083  /* Set dummy value for SNR to maintain keyword ordering. Filled later. */
3084  error |= irplib_sdp_spectrum_set_snr(spectrum, 0.0);
3085 
3086  /* Copy LAMNLIN if available from flux image else try look it up. */
3087  if (cpl_propertylist_has(props, GIALIAS_LAMNLIN)) {
3088  error |= irplib_sdp_spectrum_copy_lamnlin(spectrum, props,
3089  GIALIAS_LAMNLIN);
3090  lamnlin = irplib_sdp_spectrum_get_lamnlin(spectrum);
3091  } else {
3092  lamnlin = _giraffe_lookup_lamnlin(expmode);
3093  if (lamnlin != -1) {
3094  error |= irplib_sdp_spectrum_set_lamnlin(spectrum, lamnlin);
3095  }
3096  }
3097 
3098  /* Copy LAMRMS if available from flux image else try look it up.
3099  * Note: we are going to have to correct this value later and fill it in
3100  * again for each spectrum. */
3101  if (cpl_propertylist_has(props, GIALIAS_LAMRMS)) {
3102  error |= irplib_sdp_spectrum_copy_lamrms(spectrum, props,
3103  GIALIAS_LAMRMS);
3104  lamrms = irplib_sdp_spectrum_get_lamrms(spectrum);
3105  } else {
3106  lamrms = _giraffe_lookup_lamrms(expmode);
3107  if (! isnan(lamrms)) {
3108  error |= irplib_sdp_spectrum_set_lamrms(spectrum, lamrms);
3109  }
3110  }
3111 
3112  /* Copy SPEC_ERR if available from the flux image, else estimate it as:
3113  * if CRDER1 is available then
3114  * SPEC_ERR = CRDER1 / sqrt(LAMNLIN)
3115  * else
3116  * SPEC_ERR = LAMRMS / sqrt(LAMNLIN)
3117  * end
3118  *
3119  * Note: we are going to have to correct this value later and fill it in
3120  * again for each spectrum.
3121  */
3122  if (cpl_propertylist_has(props, GIALIAS_SPEC_ERR)) {
3123  error |= irplib_sdp_spectrum_copy_specerr(spectrum, props,
3124  GIALIAS_SPEC_ERR);
3125  specerr = irplib_sdp_spectrum_get_specerr(spectrum);
3126  } else if (lamnlin > 0) {
3127  if (cpl_propertylist_has(props, GIALIAS_CRDER1)) {
3128  prestate = cpl_errorstate_get();
3129  double crder1 = cpl_propertylist_get_double(props, GIALIAS_CRDER1);
3130  if (cpl_errorstate_is_equal(prestate) && crder1 > 0) {
3131  specerr = crder1 / sqrt(lamnlin);
3132  } else {
3133  error = cpl_error_get_code();
3134  }
3135  } else if (! isnan(lamrms)) {
3136  specerr = lamrms / sqrt(lamnlin);
3137  }
3138  if (! isnan(specerr)) {
3139  error |= irplib_sdp_spectrum_set_specerr(spectrum, specerr);
3140  }
3141  }
3142 
3143  /* Copy SPEC_SYE if available from the flux image, else estimate it as:
3144  * 0.002 nm
3145  */
3146  if (cpl_propertylist_has(props, GIALIAS_SPEC_SYE)) {
3147  error |= irplib_sdp_spectrum_copy_specsye(spectrum, props,
3148  GIALIAS_SPEC_SYE);
3149  specsye = irplib_sdp_spectrum_get_specsye(spectrum);
3150  } else {
3151  /* Don't set the local specsye variable so that it does not get
3152  * corrected later. We want it to always be 0.002 nm. Just set the
3153  * keyword in the spectrum object directly. */
3154  error |= irplib_sdp_spectrum_set_specsye(spectrum, 0.002);
3155  }
3156 
3157  error |= irplib_sdp_spectrum_set_specres(spectrum, specres);
3158  error |= irplib_sdp_spectrum_copy_gain(spectrum, props, GIALIAS_CONAD);
3159  error |= irplib_sdp_spectrum_copy_detron(spectrum, props, GIALIAS_RON);
3160  if (got_ancillary_data) {
3161  error |= irplib_sdp_spectrum_set_asson(spectrum, 1, ancillary_filename);
3162  error |= irplib_sdp_spectrum_set_assoc(spectrum, 1,
3163  GIALIAS_ASSOC_VALUE);
3164  error |= irplib_sdp_spectrum_set_assom(spectrum, 1, "");
3165  assoc_key_offset = 2;
3166  }
3167  for (i = assoc_key_offset; i < nassoc_keys + assoc_key_offset; ++i) {
3168  /* Add extra dummy association keywords if requested. */
3169  error |= irplib_sdp_spectrum_set_asson(spectrum, i, "");
3170  error |= irplib_sdp_spectrum_set_assoc(spectrum, i, "");
3171  error |= irplib_sdp_spectrum_set_assom(spectrum, i, "");
3172  }
3173 
3174  error |= irplib_sdp_spectrum_set_voclass(spectrum, GIALIAS_VOCLASS_VALUE);
3175  error |= irplib_sdp_spectrum_set_vopub(spectrum, GIALIAS_VOPUB_VALUE);
3176  error |= irplib_sdp_spectrum_set_title(spectrum, ""); /* Set dummy value */
3177  error |= cpl_propertylist_append_double(tablekeys, GIALIAS_APERTURE,
3178  GIALIAS_APERTURE_VALUE);
3179  error |= cpl_propertylist_set_comment(tablekeys, GIALIAS_APERTURE,
3180  GIALIAS_APERTURE_COMMENT);
3181  /*
3182  * Normally: telapse = (mjdend - mjdobs) * 86400.
3183  * However, doing this calculation directly leads to rounding errors that
3184  * can cause the invalid condition TELAPSE < EXPTIME. Since we have:
3185  * mjdend = mjdobs + exptime / 86400.
3186  * we can simplify the above to be: telapse = exptime
3187  * which will always satisfy the condition TELAPSE >= EXPTIME.
3188  */
3189  error |= irplib_sdp_spectrum_set_telapse(spectrum, exptime);
3190  error |= irplib_sdp_spectrum_set_tmid(spectrum, (mjdobs + mjdend) * 0.5);
3191  /* Set dummy values for the SPEC_VAL, SPEC_BW, TDMIN and TDMAX values to
3192  * keep the order of the keywords on the header. Will fill these in later
3193  * with Heliocentric corrected values. */
3194  error |= irplib_sdp_spectrum_set_specval(spectrum, 0.0);
3195  error |= irplib_sdp_spectrum_set_specbw(spectrum, 0.0);
3196  error |= irplib_sdp_spectrum_set_nelem(spectrum, ny);
3197  error |= irplib_sdp_spectrum_set_tdmin(spectrum, 0.0);
3198  error |= irplib_sdp_spectrum_set_tdmax(spectrum, 0.0);
3199 
3200  /* Add the keyword FPS */
3201  error |= cpl_propertylist_append_int(extrakeys, GIALIAS_FPS, -1);
3202  error |= cpl_propertylist_set_comment(extrakeys, GIALIAS_FPS,
3203  GIALIAS_FPS_COMMENT);
3204 
3205  /* Add dummy [G,H,B]CORR keywords to be updated from the fiber table. */
3206  error |= cpl_propertylist_append_double(extrakeys, GIALIAS_GEOCORR, NAN);
3207  error |= cpl_propertylist_set_comment(extrakeys, GIALIAS_GEOCORR,
3208  GIALIAS_GEOCORR_COMMENT);
3209  error |= cpl_propertylist_append_double(extrakeys, GIALIAS_HELICORR, NAN);
3210  error |= cpl_propertylist_set_comment(extrakeys, GIALIAS_HELICORR,
3211  GIALIAS_HELICORR_COMMENT);
3212  error |= cpl_propertylist_append_double(extrakeys, GIALIAS_BARYCORR, NAN);
3213  error |= cpl_propertylist_set_comment(extrakeys, GIALIAS_BARYCORR,
3214  GIALIAS_BARYCORR_COMMENT);
3215 
3216  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
3217  "Could not setup the common SDP spectrum keywords.");
3218 
3219  /* Figure out the data type required for the WAVE column to preserve the
3220  * precision of the data values. */
3221  wavecoltype = _giraffe_calc_wave_type(crval2, crpix2, cdelt2, ny);
3222 
3223  /* Calculate the reference wavelength values (before corrections). */
3224  refwavearray = cpl_array_new(ny, CPL_TYPE_DOUBLE);
3225  data_double = cpl_array_get_data_double(refwavearray);
3226  assert(data_double != NULL);
3227  for (i = 1; i <= ny; ++i) {
3228  data_double[i-1] = (i-crpix2) * cdelt2 + crval2;
3229  }
3230  data_double = NULL;
3231 
3232  /* Try setup the SDP table columns. */
3233  error |= irplib_sdp_spectrum_add_column(
3234  spectrum, GIALIAS_COLUMN_WAVE, wavecoltype,
3235  GIALIAS_COLUMN_WAVE_UNIT, NULL, GIALIAS_COLUMN_WAVE_TUTYP,
3236  GIALIAS_COLUMN_WAVE_TUCD, NULL);
3237  error |= irplib_sdp_spectrum_set_column_tcomm(
3238  spectrum, GIALIAS_COLUMN_WAVE, GIALIAS_COLUMN_WAVE_TCOMM);
3239  error |= irplib_sdp_spectrum_add_column(
3240  spectrum, GIALIAS_COLUMN_FLUX_REDUCED, CPL_TYPE_DOUBLE,
3241  GIALIAS_COLUMN_FLUX_REDUCED_UNIT, NULL,
3242  GIALIAS_COLUMN_FLUX_REDUCED_TUTYP,
3243  GIALIAS_COLUMN_FLUX_REDUCED_TUCD, NULL);
3244  error |= irplib_sdp_spectrum_set_column_tcomm(
3245  spectrum, GIALIAS_COLUMN_FLUX_REDUCED, "");
3246  error |= irplib_sdp_spectrum_add_column(
3247  spectrum, GIALIAS_COLUMN_ERR_REDUCED, CPL_TYPE_DOUBLE,
3248  GIALIAS_COLUMN_ERR_REDUCED_UNIT, NULL,
3249  GIALIAS_COLUMN_ERR_REDUCED_TUTYP,
3250  GIALIAS_COLUMN_ERR_REDUCED_TUCD, NULL);
3251  error |= irplib_sdp_spectrum_set_column_tcomm(
3252  spectrum, GIALIAS_COLUMN_ERR_REDUCED, "");
3253  error |= irplib_sdp_spectrum_add_column(
3254  spectrum, GIALIAS_COLUMN_SNR, CPL_TYPE_DOUBLE,
3255  GIALIAS_COLUMN_SNR_UNIT, NULL, GIALIAS_COLUMN_SNR_TUTYP,
3256  GIALIAS_COLUMN_SNR_TUCD, NULL);
3257  error |= irplib_sdp_spectrum_set_column_tcomm(
3258  spectrum, GIALIAS_COLUMN_SNR, GIALIAS_COLUMN_SNR_TCOMM);
3259  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
3260  "Could not setup the SDP spectrum columns.");
3261 
3262  indices = cpl_table_get_data_int_const(giraffe_table_get(fibertable),
3263  GIALIAS_COLUMN_INDEX);
3264  fps = cpl_table_get_data_int_const(giraffe_table_get(fibertable),
3265  GIALIAS_FPS);
3266  objects = cpl_table_get_data_string_const(giraffe_table_get(fibertable),
3267  GIALIAS_COLUMN_OBJECT);
3268  spectypes = cpl_table_get_data_string_const(giraffe_table_get(fibertable),
3269  GIALIAS_COLUMN_TYPE);
3270  ras = cpl_table_get_data_double_const(giraffe_table_get(fibertable),
3271  GIALIAS_COLUMN_RA);
3272  decs = cpl_table_get_data_double_const(giraffe_table_get(fibertable),
3273  GIALIAS_COLUMN_DEC);
3274  gcorr = cpl_table_get_data_double_const(giraffe_table_get(fibertable),
3275  GIALIAS_COLUMN_GCORR);
3276  hcorr = cpl_table_get_data_double_const(giraffe_table_get(fibertable),
3277  GIALIAS_COLUMN_HCORR);
3278  bcorr = cpl_table_get_data_double_const(giraffe_table_get(fibertable),
3279  GIALIAS_COLUMN_BCORR);
3280  cpl_error_ensure(indices != NULL && fps != NULL && objects != NULL
3281  && spectypes != NULL && ras != NULL && decs != NULL
3282  && gcorr != NULL && hcorr != NULL && bcorr != NULL,
3283  cpl_error_get_code(), goto cleanup,
3284  "Could not fetch data from the fiber setup table in '%s'.",
3285  fibertable_name);
3286 
3287  formatstr = _giraffe_calc_format_string(
3288  cpl_table_get_nrow(giraffe_table_get(fibertable)));
3289 
3290  /* Write the individual spectrum files: */
3291  filecount = 0;
3292  for (i = 0; i < cpl_table_get_nrow(giraffe_table_get(fibertable)); ++i) {
3293  const double* wave_data;
3294  double* flux_data;
3295  double* err_data;
3296  cpl_size j;
3297  double snr = 0.0;
3298  /* Keywords to remove: */
3299  const char* remregexp = "^(CDELT[0-9]+|CD[0-9]+_[0-9]+|CRPIX[0-9]+"
3300  "|CRDER[0-9]+|CSYER[0-9]+|BUNIT|BSCALE|BZERO"
3301  "|BLANK|FILTER)$";
3302  cpl_size specindex = indices[i];
3303  double vela, velb, beta;
3304  double correction_factor = 1.0;
3305 
3306  /* Skip non-science spectra. */
3307  if (strcmp(spectypes[i], "M") != 0) continue;
3308 
3309  filename = cpl_sprintf(formatstr, ++filecount);
3310 
3311  /* Calculate the Heliocentric correction factor to apply.
3312  * The gcorr and hcorr values are in km/s, so must be converted to m/s.
3313  */
3314  vela = gcorr[i] * 1e3;
3315  velb = hcorr[i] * 1e3;
3316  beta = (vela + velb) / CPL_PHYS_C;
3317  cpl_error_ensure(-1 <= beta && beta <= 1,
3318  CPL_ERROR_ILLEGAL_OUTPUT, goto cleanup,
3319  "The velocities GCORR = %g and HCORR = %g for spectrum"
3320  "%"CPL_SIZE_FORMAT" in file '%s' give invalid"
3321  " Heliocentric correction factor values.",
3322  gcorr[i], hcorr[i], specindex, flux_filename);
3323  correction_factor = sqrt((1.0 + beta) / (1.0 - beta));
3324 
3325  /* Calculate and set corrected wavelength array, remembering to cast
3326  * to the appropriate type. */
3327  wave_data = cpl_array_get_data_double_const(refwavearray);
3328  if (wavecoltype == CPL_TYPE_FLOAT) {
3329  data_float = cpl_malloc(ny * sizeof(float));
3330  for (j = 0; j < ny; ++j) {
3331  data_float[j] = wave_data[j] * correction_factor;
3332  }
3333  array = cpl_array_wrap_float(data_float, ny);
3334  } else {
3335  data_double = cpl_malloc(ny * sizeof(double));
3336  for (j = 0; j < ny; ++j) {
3337  data_double[j] = wave_data[j] * correction_factor;
3338  }
3339  array = cpl_array_wrap_double(data_double, ny);
3340  }
3341  error |= irplib_sdp_spectrum_set_column_data(
3342  spectrum, GIALIAS_COLUMN_WAVE, array);
3343  cpl_array_unwrap(array);
3344  array = NULL;
3345 
3346  fluximgcol = cpl_vector_new_from_image_column(
3347  giraffe_image_get(fluximage), specindex);
3348  flux_data = cpl_vector_get_data(fluximgcol);
3349  cpl_error_ensure(flux_data != NULL, cpl_error_get_code(), goto cleanup,
3350  "Unable to extract data in column %"CPL_SIZE_FORMAT
3351  " from image in file '%s'.", specindex, flux_filename);
3352  array = cpl_array_wrap_double(flux_data, ny);
3353  error |= irplib_sdp_spectrum_set_column_data(
3354  spectrum, GIALIAS_COLUMN_FLUX_REDUCED, array);
3355  cpl_array_unwrap(array);
3356  array = NULL;
3357 
3358  errimgcol = cpl_vector_new_from_image_column(
3359  giraffe_image_get(errimage), specindex);
3360  err_data = cpl_vector_get_data(errimgcol);
3361  cpl_error_ensure(err_data != NULL, cpl_error_get_code(), goto cleanup,
3362  "Unable to extract data in column %"CPL_SIZE_FORMAT
3363  " from image in file '%s'.", specindex, err_filename);
3364  array = cpl_array_wrap_double(err_data, ny);
3365  error |= irplib_sdp_spectrum_set_column_data(
3366  spectrum, GIALIAS_COLUMN_ERR_REDUCED, array);
3367  cpl_array_unwrap(array);
3368  array = NULL;
3369 
3370  data_double = cpl_malloc(ny * sizeof(double));
3371  for (j = 0; j < ny; ++j) {
3372  data_double[j] = (err_data[j] != 0.0) ? flux_data[j] / err_data[j]
3373  : 0.0;
3374  }
3375  array = cpl_array_wrap_double(data_double, ny);
3376  snr = cpl_array_get_median(array);
3377  error |= irplib_sdp_spectrum_set_column_data(spectrum,
3378  GIALIAS_COLUMN_SNR, array);
3379  cpl_array_unwrap(array);
3380  array = NULL;
3381  cpl_free(data_double);
3382  data_double = NULL;
3383 
3384  cpl_vector_delete(fluximgcol);
3385  fluximgcol = NULL;
3386  cpl_vector_delete(errimgcol);
3387  errimgcol = NULL;
3388 
3389  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
3390  "Could not setup the SDP spectrum columns for '%s'.",
3391  filename);
3392 
3393  error |= irplib_sdp_spectrum_set_object(spectrum, objects[i]);
3394  error |= irplib_sdp_spectrum_set_title(spectrum, objects[i]);
3395  error |= irplib_sdp_spectrum_set_ra(spectrum, ras[i]);
3396  error |= irplib_sdp_spectrum_set_dec(spectrum, decs[i]);
3397  error |= irplib_sdp_spectrum_set_snr(spectrum, snr);
3398  error |= irplib_sdp_spectrum_set_column_tcomm(
3399  spectrum, GIALIAS_COLUMN_FLUX_REDUCED, flux_filename);
3400  error |= irplib_sdp_spectrum_set_column_tcomm(
3401  spectrum, GIALIAS_COLUMN_ERR_REDUCED, err_filename);
3402 
3403  error |= cpl_propertylist_update_int(extrakeys, GIALIAS_FPS, fps[i]);
3404  error |= cpl_propertylist_update_double(extrakeys, GIALIAS_GEOCORR,
3405  gcorr[i]);
3406  error |= cpl_propertylist_update_double(extrakeys, GIALIAS_HELICORR,
3407  hcorr[i]);
3408  error |= cpl_propertylist_update_double(extrakeys, GIALIAS_BARYCORR,
3409  bcorr[i]);
3410 
3411  /* Set corrected values that depend on wavelengths. */
3412  error |= irplib_sdp_spectrum_set_wavelmin(spectrum,
3413  wavelmin * correction_factor);
3414  error |= irplib_sdp_spectrum_set_wavelmax(spectrum,
3415  wavelmax * correction_factor);
3416  error |= irplib_sdp_spectrum_set_specval(spectrum,
3417  (wavelmax + wavelmin) * 0.5 * correction_factor);
3418  error |= irplib_sdp_spectrum_set_specbw(spectrum,
3419  (wavelmax - wavelmin) * correction_factor);
3420  error |= irplib_sdp_spectrum_set_tdmin(spectrum,
3421  wavelmin * correction_factor);
3422  error |= irplib_sdp_spectrum_set_tdmax(spectrum,
3423  wavelmax * correction_factor);
3424  error |= irplib_sdp_spectrum_set_specbin(spectrum,
3425  specbin * correction_factor);
3426  if (! isnan(lamrms)) {
3427  error |= irplib_sdp_spectrum_set_lamrms(spectrum,
3428  lamrms * correction_factor);
3429  }
3430  if (! isnan(specerr)) {
3431  error |= irplib_sdp_spectrum_set_specerr(spectrum,
3432  specerr * correction_factor);
3433  }
3434  if (! isnan(specsye)) {
3435  error |= irplib_sdp_spectrum_set_specsye(spectrum,
3436  specsye * correction_factor);
3437  }
3438 
3439  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
3440  "Could not setup the SDP spectrum keywords for '%s'.",
3441  filename);
3442 
3443  error |= irplib_dfs_save_spectrum(allframes, NULL, parlist, usedframes,
3444  inherit, spectrum, recipe_id,
3445  extrakeys, tablekeys, remregexp,
3446  pipe_id, dict_id, filename);
3447  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
3448  "Failed to save SDP spectrum %"CPL_SIZE_FORMAT
3449  " to file '%s'.", specindex, filename);
3450 
3451  error |= irplib_fits_update_checksums(filename);
3452  cpl_error_ensure(! error, cpl_error_get_code(), goto cleanup,
3453  "Failed to save update checksums for file '%s'.",
3454  filename);
3455  cpl_free(filename);
3456  filename = NULL;
3457  }
3458 
3459  if (filecount == 0) {
3460  cpl_msg_warning(cpl_func, "No science spectra found in '%s'."
3461  " No SDP spectra created.", flux_filename);
3462  }
3463 
3464  result_code = 0; /* Indicate success. */
3465 
3466 cleanup:
3467  /* Cleanup objects and memory. Note: the delete functions already check for
3468  * NULL pointers. */
3469  cpl_vector_delete(fluximgcol);
3470  cpl_vector_delete(errimgcol);
3471  cpl_array_unwrap(array);
3472  cpl_array_delete(refwavearray);
3473  cpl_free(data_float);
3474  cpl_free(data_double);
3475  cpl_free(filename);
3476  cpl_free(formatstr);
3477  cpl_frameset_delete(usedframes);
3478  cpl_frameset_delete(rawframes);
3479  cpl_frameset_iterator_delete(iterator);
3480  cpl_free(pipe_id);
3481  cpl_propertylist_delete(tablekeys);
3482  cpl_propertylist_delete(extrakeys);
3483  giraffe_image_delete(fluximage);
3484  giraffe_image_delete(errimage);
3485  giraffe_table_delete(fibertable);
3486  irplib_sdp_spectrum_delete(spectrum);
3487  return result_code;
3488 }
cpl_frame * giraffe_get_slitgeometry(const cpl_frameset *set)
Get the slit geometry frame from a frame set.
Definition: giframe.c:783
cxint giraffe_image_add_info(GiImage *image, const GiRecipeInfo *info, const cpl_frameset *set)
Add additional frame information to an image.
Definition: giimage.c:781
GiFieldOfView * giraffe_fov_new(void)
Create an empty container for the results of the field of view reconstruction.
Definition: gifov.c:1401
GiInstrumentMode giraffe_get_mode(cpl_propertylist *properties)
Determines the instrument mode from a property list.
Definition: giutils.c:306
void giraffe_fov_delete(GiFieldOfView *self)
Deallocate a field of view object and its contents.
Definition: gifov.c:1502
cxint giraffe_add_frameset_info(cpl_propertylist *plist, const cpl_frameset *set, cxint sequence)
Add frameset specific information to a property list.
Definition: giutils.c:587
GiExtractConfig * giraffe_extract_config_create(cpl_parameterlist *list)
Creates a setup structure for the spectrum extraction.
Definition: giextract.c:3408
void giraffe_fov_config_add(cpl_parameterlist *list)
Adds parameters for the image and data cube construction.
Definition: gifov.c:2097
void giraffe_rebin_config_add(cpl_parameterlist *list)
Adds parameters for the rebinning.
Definition: girebinning.c:4957
GiFieldOfViewConfig * giraffe_fov_config_create(cpl_parameterlist *list)
Creates a setup structure for the field of view reconstruction.
Definition: gifov.c:2020
cpl_frame * giraffe_frame_create(const cxchar *tag, cpl_frame_level level, const cpl_propertylist *properties, cxcptr object, cxcptr data, GiFrameCreator creator)
Create a product frame using a provided frame creator.
Definition: giframe.c:245
cxint giraffe_fov_save_cubes_eso3d(const GiFieldOfView *self, cpl_propertylist *properties, const cxchar *filename, cxptr data)
Write the cube components of a field-of-view object to a file.
Definition: gifov.c:1681
cpl_table * giraffe_table_get(const GiTable *self)
Get the table data from a Giraffe table.
Definition: gitable.c:441
cxint giraffe_rebin_spectra(GiRebinning *rebinning, const GiExtraction *extraction, const GiTable *fibers, const GiLocalization *localization, const GiTable *grating, const GiTable *slitgeo, const GiTable *solution, const GiRebinConfig *config)
Rebin an Extracted Spectra Frame and associated Errors Frame.
Definition: girebinning.c:4059
void giraffe_flat_config_add(cpl_parameterlist *list)
Adds parameters for the flat field correction.
Definition: giflat.c:384
cxint giraffe_bias_remove(GiImage *result, const GiImage *raw, const GiImage *master_bias, const GiImage *bad_pixels, const cpl_matrix *biaslimits, const GiBiasConfig *config)
Removes the bias from an image.
Definition: gibias.c:3114
void giraffe_bias_config_destroy(GiBiasConfig *config)
Destroys a bias removal setup structure.
Definition: gibias.c:3577
void giraffe_table_delete(GiTable *self)
Destroys a Giraffe table.
Definition: gitable.c:162
GiRebinConfig * giraffe_rebin_config_create(cpl_parameterlist *list)
Creates a setup structure for the rebinning.
Definition: girebinning.c:4833
GiTable * giraffe_table_new(void)
Creates a new, empty Giraffe table.
Definition: gitable.c:93
void giraffe_extract_config_add(cpl_parameterlist *list)
Adds parameters for the spectrum extraction.
Definition: giextract.c:3512
void giraffe_sgcalibration_config_destroy(GiSGCalConfig *config)
Destroys a sgcalibration field setup structure.
GiSGCalConfig * giraffe_sgcalibration_config_create(cpl_parameterlist *list)
Creates a setup structure for the slit geometry calibration.
cxint giraffe_image_load(GiImage *self, const cxchar *filename, cxint position)
Gets image data and properties from a file.
Definition: giimage.c:544
void giraffe_rebin_config_destroy(GiRebinConfig *config)
Destroys a spectrum extraction setup structure.
Definition: girebinning.c:4933
cxint giraffe_image_save(GiImage *self, const cxchar *filename)
Write a Giraffe image to a file.
Definition: giimage.c:578
cxint giraffe_table_load(GiTable *self, const cxchar *filename, cxint position, const cxchar *id)
Reads a data set from a file into a Giraffe table.
Definition: gitable.c:570
cxint giraffe_flat_apply(GiExtraction *extraction, const GiTable *fibers, const GiImage *flat, const GiImage *errors, GiFlatConfig *config)
Apply the flat field correction to the given extracted spectra.
Definition: giflat.c:246
cxint giraffe_fiberlist_attach(cpl_frame *frame, GiTable *fibers)
Attach a fiber table to a frame.
Definition: gifiberutils.c:853
GiTable * giraffe_table_duplicate(const GiTable *src)
Duplicate a Giraffe table.
Definition: gitable.c:184
GiBiasConfig * giraffe_bias_config_create(cpl_parameterlist *list)
Creates a setup structure for a bias removal task.
Definition: gibias.c:3446
void giraffe_image_delete(GiImage *self)
Destroys an image.
Definition: giimage.c:189
cxint giraffe_fov_build(GiFieldOfView *result, GiRebinning *rebinning, GiTable *fibers, GiTable *wsolution, GiTable *grating, GiTable *slitgeometry, GiFieldOfViewConfig *config)
Create and image and a data cube from extracted and rebinned spectra.
Definition: gifov.c:436
cxint giraffe_add_rvcorrection(GiTable *fibers, const GiImage *spectra)
Add the barycentric and heliocentric corrections to the given fiber setup.
Definition: giastrometry.c:76
GiImage * giraffe_image_new(cpl_type type)
Creates an empty image container.
Definition: giimage.c:73
void giraffe_flat_config_destroy(GiFlatConfig *config)
Destroys a flat field setup structure.
Definition: giflat.c:361
GiTable * giraffe_fibers_setup(const cpl_frame *frame, const cpl_frame *reference)
Setup a fiber list.
Definition: gifibers.c:226
void giraffe_rebinning_destroy(GiRebinning *rebinning)
Destroys a rebinning results container and its contents.
Definition: girebinning.c:4795
void giraffe_bias_config_add(cpl_parameterlist *list)
Adds parameters for the bias removal.
Definition: gibias.c:3605
GiImage * giraffe_image_create(cpl_type type, cxint nx, cxint ny)
Creates an image container of a given type.
Definition: giimage.c:103
GiTable * giraffe_slitgeometry_load(const GiTable *fibers, const cxchar *filename, cxint pos, const cxchar *tag)
Load the slit geometry information for a given fiber setup.
GiFlatConfig * giraffe_flat_config_create(cpl_parameterlist *list)
Creates a setup structure for the flat field correction.
Definition: giflat.c:310
cxint giraffe_fov_save_cubes(const GiFieldOfView *self, cpl_propertylist *properties, const cxchar *filename, cxptr data)
Write the cube components of a field-of-view object to a file.
Definition: gifov.c:1538
const cxchar * giraffe_get_license(void)
Get the pipeline copyright and license.
Definition: giutils.c:284
cpl_image * giraffe_image_get(const GiImage *self)
Gets the image data.
Definition: giimage.c:226
GiRebinning * giraffe_rebinning_new(void)
Create an empty rebinning results container.
Definition: girebinning.c:4701
void giraffe_sgcalibration_config_add(cpl_parameterlist *list)
Adds parameters for the sgcalibration correction computation.
Slit geometry calibration configuration data structure.
void giraffe_extract_config_destroy(GiExtractConfig *config)
Destroys a spectrum extraction setup structure.
Definition: giextract.c:3482
cxint giraffe_image_set_properties(GiImage *self, cpl_propertylist *properties)
Attaches a property list to an image.
Definition: giimage.c:320
cpl_frame * giraffe_frame_create_image(GiImage *image, const cxchar *tag, cpl_frame_level level, cxbool save, cxbool update)
Create an image product frame.
Definition: giframe.c:401
cxint giraffe_fiberlist_compare(const GiTable *fibers, const GiTable *reference)
Compare two fiber lists.
Definition: gifiberutils.c:921
cxint giraffe_subtract_dark(GiImage *image, const GiImage *dark, const GiImage *bpixel, GiDarkResults *data, const GiDarkConfig *config)
Subtract the dark current from a bias corrected image.
Definition: gidark.c:486
GiTable * giraffe_fiberlist_load(const cxchar *filename, cxint dataset, const cxchar *tag)
Load a fiber table from a file.
Definition: gifiberutils.c:723
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
Definition: giimage.c:290
cxint giraffe_compute_offsets(GiTable *fibers, const GiRebinning *rebinning, const GiTable *grating, const GiTable *mask, const GiSGCalConfig *config)
Compute wavelength offsets for a set of rebinned input spectrum.
void giraffe_fov_config_destroy(GiFieldOfViewConfig *config)
Destroys a field of view setup structure.
Definition: gifov.c:2075
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:42 by doxygen 1.8.9.1 written by Dimitri van Heesch, © 1997-2004