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

This file is part of the GIRAFFE Pipeline Reference Manual 2.19.4.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Feb 6 2026 11:30:08 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004