GIRAFFE Pipeline Reference Manual

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

This file is part of the GIRAFFE Pipeline Reference Manual 2.16.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Dec 15 2022 21:18:51 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2004