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
58static cxint giscience(cpl_parameterlist*, cpl_frameset*);
59
60static 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
73static cxint
74giscience_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
169static cxint
170giscience_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
190static cxint
191giscience_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
216static cxint
217giscience(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) {
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) {
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
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
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
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);
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);
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
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
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
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
2296int
2297cpl_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. */
2329typedef 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
2345static 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
2368static 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
2454static 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
2467static 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
2481static 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
2499static 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
2525static 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
2574static 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);
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
2735cleanup:
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
2752static 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
2781static 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
3473cleanup:
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
GiTable * giraffe_fiberlist_load(const cxchar *filename, cxint dataset, const cxchar *tag)
Load a fiber table from a file.
Definition: gifiberutils.c:715
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
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
GiFieldOfViewConfig * giraffe_fov_config_create(cpl_parameterlist *list)
Creates a setup structure for the field of view reconstruction.
Definition: gifov.c:2012
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
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(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
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_image * giraffe_image_get(const GiImage *self)
Gets the image data.
Definition: giimage.c:218
cpl_propertylist * giraffe_image_get_properties(const GiImage *self)
Get the properties of an image.
Definition: giimage.c:282
void giraffe_image_delete(GiImage *self)
Destroys an image.
Definition: giimage.c:181
GiImage * giraffe_image_create(cpl_type type, cxint nx, cxint ny)
Creates an image container of a given type.
Definition: giimage.c:95
cxint giraffe_image_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
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
GiRebinConfig * giraffe_rebin_config_create(cpl_parameterlist *list)
Creates a setup structure for the rebinning.
Definition: girebinning.c:4825
GiRebinning * giraffe_rebinning_new(void)
Create an empty rebinning results container.
Definition: girebinning.c:4693
void giraffe_rebinning_destroy(GiRebinning *rebinning)
Destroys a rebinning results container and its contents.
Definition: girebinning.c:4787
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.
GiTable * giraffe_table_new(void)
Creates a new, empty Giraffe table.
Definition: gitable.c:85
GiTable * giraffe_table_duplicate(const GiTable *src)
Duplicate a Giraffe table.
Definition: gitable.c:176
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
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
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.12.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Feb 21 2025 12:08:13 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004