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

This file is part of the GIRAFFE Pipeline Reference Manual 2.17.1.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Mon Jan 13 2025 15:02:27 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004