GRAVI Pipeline Reference Manual 1.9.2
Loading...
Searching...
No Matches
gravi_idp.c
Go to the documentation of this file.
1/*
2 * This file is part of the GRAVI Pipeline
3 * Copyright (C) 2022 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20/*-----------------------------------------------------------------------------
21 Includes
22 -----------------------------------------------------------------------------*/
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <math.h>
29#include <string.h>
30#include "gravi_utils.h"
31#include "gravi_idp.h"
32#include "gravi_pfits.h"
33#include "gravi_dfs.h"
34#include "gravi_cpl.h"
35#include "gravi_vis.h"
36
37/*-----------------------------------------------------------------------------*/
46/*-----------------------------------------------------------------------------*/
47cpl_propertylist * gravi_idp_compute (gravi_data * vis_data,
48 cpl_propertylist * header,
49 cpl_frameset * frameset)
50{
51 cpl_propertylist * idp_plist = cpl_propertylist_new ();
52 int nbase = 6;
53
55 char qc_name[100];
56
57 /* Scratch tables to merge polarization tables */
58 cpl_table * oi_vis2_SC_allpol = NULL;
59 cpl_table * oi_T3_SC_allpol = NULL;
60 cpl_table * oi_wave_SC_allpol = NULL;
61
62 /* There are products like the p2vmred that do not have all columns */
64 {
65 double vis2err = 0;
66 double visphierr = 0;
67 double t3phierr = 0;
68 oi_vis2_SC_allpol = cpl_table_duplicate(gravi_data_get_oi_vis2 (vis_data, GRAVI_SC, 0, npol_sc));
69 oi_T3_SC_allpol = cpl_table_duplicate(gravi_data_get_oi_t3 (vis_data, GRAVI_SC, 0, npol_sc));
70 oi_wave_SC_allpol = cpl_table_duplicate(gravi_data_get_oi_wave (vis_data, GRAVI_SC, 0, npol_sc));
71 for (int pol = 1; pol < npol_sc; pol++) {
72 cpl_table_insert(oi_vis2_SC_allpol, gravi_data_get_oi_vis2 (vis_data, GRAVI_SC, pol, npol_sc), cpl_table_get_nrow(oi_vis2_SC_allpol));
73 cpl_table_insert(oi_T3_SC_allpol, gravi_data_get_oi_t3 (vis_data, GRAVI_SC, pol, npol_sc), cpl_table_get_nrow(oi_T3_SC_allpol));
74 cpl_table_insert(oi_wave_SC_allpol, gravi_data_get_oi_wave (vis_data, GRAVI_SC, pol, npol_sc), cpl_table_get_nrow(oi_wave_SC_allpol));
75 }
76
77 for (int pol = 0; pol < npol_sc; pol++) {
78 double this_pol_vis2err = gravi_table_get_column_flagged_mean(gravi_data_get_oi_vis2 (vis_data, GRAVI_SC, pol, npol_sc), "VIS2ERR");
79 vis2err += this_pol_vis2err * this_pol_vis2err;
80 double this_pol_visphierr = gravi_table_get_column_flagged_mean(gravi_data_get_oi_vis (vis_data, GRAVI_SC, pol, npol_sc), "VISPHIERR");
81 visphierr += this_pol_visphierr * this_pol_visphierr;
82 double this_pol_t3phierr = gravi_table_get_column_flagged_mean(gravi_data_get_oi_t3 (vis_data, GRAVI_SC, pol, npol_sc), "T3PHIERR");
83 t3phierr += this_pol_t3phierr * this_pol_t3phierr;
84 }
85
86 /* Compute QC values that aggregate on polarizations */
87 sprintf (qc_name, "VIS2ERR");
88 cpl_propertylist_update_double (idp_plist, qc_name, sqrt(vis2err));
89 cpl_propertylist_set_comment (idp_plist, qc_name, "Representative squared visibility error [%]");
90
91 sprintf (qc_name, "VISPHERR");
92 cpl_propertylist_update_double (idp_plist, qc_name, sqrt(visphierr));
93 cpl_propertylist_set_comment (idp_plist, qc_name, "Representative visibility phase error [%]");
94
95 sprintf (qc_name, "T3PHIERR");
96 cpl_propertylist_update_double (idp_plist, qc_name, sqrt(t3phierr));
97 cpl_propertylist_set_comment (idp_plist, qc_name, "Representative closure phase error [deg]");
98
99 double min_uvcoord, max_uvcoord;
100 gravi_data_get_minmax_uvcoord(oi_vis2_SC_allpol, &min_uvcoord, &max_uvcoord);
101
102 double min_eff_wave = cpl_table_get_column_min(oi_wave_SC_allpol, "EFF_WAVE");
103 double max_eff_wave = cpl_table_get_column_max(oi_wave_SC_allpol, "EFF_WAVE");
104
105 double base_max = max_uvcoord / min_eff_wave;
106 double base_min = min_uvcoord / max_eff_wave;
107 if(isnan(base_max) || isinf(base_max))
108 base_max = 0;
109 if(isnan(base_min) || isinf(base_min))
110 base_min = 0;
111
112 sprintf (qc_name, "BASE_MAX");
113 cpl_propertylist_update_double (idp_plist, qc_name, base_max);
114 cpl_propertylist_set_comment (idp_plist, qc_name, "Maximum baseline / Minimum effective wavelenth");
115
116 sprintf (qc_name, "BASE_MIN");
117 cpl_propertylist_update_double (idp_plist, qc_name, base_min);
118 cpl_propertylist_set_comment (idp_plist, qc_name, "Minimum baseline / Maximum effective wavelenth");
119
120 /* The rows in oi_wave_SC_allpol contain each wavelenght npol_sc times,
121 since it has been aggregated. Therefore dividing by npol_sc times */
122 sprintf (qc_name, "NUM_CHAN");
123 cpl_propertylist_update_int (idp_plist, qc_name, cpl_table_get_nrow(oi_wave_SC_allpol) / npol_sc );
124 cpl_propertylist_set_comment (idp_plist, qc_name, "Number of wavelength channels");
125
126 sprintf (qc_name, "WAVELMAX");
127 double avg_band = cpl_table_get_column_mean(oi_wave_SC_allpol, "EFF_BAND");
128 cpl_propertylist_update_double (idp_plist, qc_name, (max_eff_wave + avg_band / 2.) * 1e9);
129 cpl_propertylist_set_comment (idp_plist, qc_name, "[nm] Maximum wavelength");
130
131 sprintf (qc_name, "WAVELMIN");
132 cpl_propertylist_update_double (idp_plist, qc_name, (min_eff_wave - avg_band / 2.) * 1e9);
133 cpl_propertylist_set_comment (idp_plist, qc_name, "[nm] Minimum wavelength");
134
135 cpl_table_duplicate_column(oi_wave_SC_allpol, "SPEC_RES", oi_wave_SC_allpol, "EFF_WAVE");
136 cpl_table_divide_columns(oi_wave_SC_allpol,"SPEC_RES", "EFF_BAND");
137
138 sprintf (qc_name, "SPEC_RES");
139 cpl_propertylist_update_double (idp_plist, qc_name, cpl_table_get_column_mean(oi_wave_SC_allpol, "SPEC_RES"));
140 cpl_propertylist_set_comment (idp_plist, qc_name, "Spectral resolution");
141
142 /* This is the mean INT_TIME, which includes duplicated entries due to the several polarizations.
143 Since it is a mean, the final value should be the same */
144 sprintf (qc_name, "EXPTIME");
145 double mean_int_time = 0.0;
146 cpl_size valid_elem = 0;
147 cpl_size nrow = cpl_table_get_nrow (oi_vis2_SC_allpol);
148 double * inttime = cpl_table_get_data_double (oi_vis2_SC_allpol, "INT_TIME");
149 for (cpl_size r=0; r<nrow;r++)
150 {
151 if (inttime[r])
152 {
153 mean_int_time += inttime[r];
154 valid_elem++;
155 }
156 }
157
158 if(valid_elem)
159 mean_int_time = mean_int_time / valid_elem;
160 cpl_propertylist_update_double (idp_plist, qc_name, mean_int_time);
161 cpl_propertylist_set_comment (idp_plist, qc_name, "Exposure time");
162 /* This is the sum of all INT_TIME divided by the number of baselines. The mean is multiplied
163 by the number of rows and then divided by the number of polarizations since they have been
164 aggregated. Finally divided by the number of baselines as specified in PIPE-9900 */
165 if(!cpl_propertylist_has(header, "TEXPTIME"))
166 {
167 double texptime = 0;
168 for (int pol = 0; pol < npol_sc; pol++) {
169 cpl_table * this_pol_table = gravi_data_get_oi_vis2 (vis_data, GRAVI_SC, pol, npol_sc);
170 double this_pol_texptime = 0;
171 cpl_size nsets = cpl_table_get_nrow(this_pol_table) / GRAVI_NBASE ;
172 for (cpl_size set = 0 ; set < nsets ; set++)
173 {
174 cpl_table * int_time_this_set = cpl_table_extract(this_pol_table, GRAVI_NBASE * set, GRAVI_NBASE);
175 double max_int_time = gravi_table_get_column_flagged_max(int_time_this_set, "INT_TIME");
176 this_pol_texptime += max_int_time;
177 cpl_table_delete(int_time_this_set);
178 }
179 texptime = fmax(texptime, this_pol_texptime);
180 }
181
182 sprintf (qc_name, "TEXPTIME");
183 cpl_propertylist_update_double (idp_plist, qc_name, texptime);
184 cpl_propertylist_set_comment (idp_plist, qc_name, "Total exposure time");
185 }
186 else
187 {
188 cpl_propertylist_update_double (idp_plist, "TEXPTIME",
189 cpl_propertylist_get_double(header, "TEXPTIME") );
190 cpl_propertylist_set_comment (idp_plist, "TEXPTIME", "Total exposure time");
191 }
192 }
193
194 /* PRODCATG */
195 cpl_propertylist_update_string (idp_plist, "PRODCATG", "SCIENCE.VISIBILITY.UNCALIBRATED");
196 cpl_propertylist_set_comment (idp_plist, "PRODCATG", "Data product category");
197
198 /* MJD-OBS */
199 double mjd_obs_first = DBL_MAX;
200 if(frameset != NULL)
201 {
202 const cpl_frame *frame;
203 cpl_frameset * science_frames = gravi_frameset_extract_fringe_data(frameset);
204 cpl_frameset_iterator *it = cpl_frameset_iterator_new(science_frames);
205 while ((frame = cpl_frameset_iterator_get(it)) != NULL) {
206 cpl_propertylist * this_frame_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
207 double mjd_obs = gravi_pfits_get_mjd(this_frame_header);
208 if (mjd_obs < mjd_obs_first)
209 mjd_obs_first = mjd_obs;
210 cpl_frameset_iterator_advance(it, 1);
211 cpl_propertylist_delete(this_frame_header);
212 }
213 cpl_frameset_delete(science_frames);
214 cpl_frameset_iterator_delete(it);
215 }
216 if (mjd_obs_first == DBL_MAX)
217 mjd_obs_first = gravi_pfits_get_mjd(header);
218 cpl_propertylist_update_double (idp_plist, "MJD-OBS", mjd_obs_first);
219 cpl_propertylist_set_comment (idp_plist, "MJD-OBS", "Start of observation");
220
221 /* MJD-END */
222 double mjd_obs_last = 0;
223 double exptime_last = 0;
224 if(frameset != NULL)
225 {
226 const cpl_frame *frame;
227 cpl_frameset * science_frames = gravi_frameset_extract_fringe_data(frameset);
228 cpl_frameset_iterator *it = cpl_frameset_iterator_new(science_frames);
229 while ((frame = cpl_frameset_iterator_get(it)) != NULL) {
230 cpl_propertylist * this_frame_header = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
231 double mjd_obs = gravi_pfits_get_mjd(this_frame_header);
232 if (mjd_obs > mjd_obs_last)
233 {
234 mjd_obs_last = mjd_obs;
235 exptime_last = cpl_propertylist_get_double(this_frame_header, "EXPTIME");
236 }
237 cpl_frameset_iterator_advance(it, 1);
238 cpl_propertylist_delete(this_frame_header);
239 }
240 cpl_frameset_delete(science_frames);
241 cpl_frameset_iterator_delete(it);
242 }
243 if (mjd_obs_last == 0)
244 mjd_obs_last = gravi_pfits_get_mjd(header);
245 if (exptime_last == 0)
246 {
247 if ( cpl_propertylist_has(header, "EXPTIME") )
248 exptime_last = cpl_propertylist_get_double(header, "EXPTIME");
249 else
250 exptime_last = cpl_propertylist_get_double(idp_plist, "EXPTIME");
251 }
252
253 cpl_propertylist_update_double (idp_plist, "MJD-END",
254 mjd_obs_last + exptime_last / 86400.);
255 cpl_propertylist_set_comment (idp_plist, "MJD-END", "End of observation");
256
257 /* Delete MJD-OBS from the main header, since it will take precedence over what is computed here*/
258 cpl_propertylist_erase (header, "MJD-OBS");
259
260 /* OBID */
261 cpl_propertylist_update_int (idp_plist, "OBID1",
262 cpl_propertylist_get_int(header, "ESO OBS ID"));
263 cpl_propertylist_set_comment (idp_plist, "OBID1", "Obseration Block ID");
264
265 /* NCOMBINE */
266 if(frameset != NULL)
267 {
268 cpl_frameset * science_frames = gravi_frameset_extract_fringe_data(frameset);
269 cpl_size nscience = cpl_frameset_get_size(science_frames);
270 cpl_frameset_delete(science_frames);
271 if(!cpl_propertylist_has(header, "NCOMBINE"))
272 {
273 if (nscience != 0)
274 {
275 cpl_propertylist_update_int (idp_plist, "NCOMBINE", nscience);
276 cpl_propertylist_set_comment (idp_plist, "NCOMBINE", "Number of raw science combined");
277 }
278 }
279 else
280 {
281 cpl_propertylist_update_int (idp_plist, "NCOMBINE",
282 cpl_propertylist_get_int(header, "NCOMBINE") );
283 cpl_propertylist_set_comment (idp_plist, "NCOMBINE", "Number of raw science combined");
284 }
285 }
286 /* OBSTECH */
287 // Only create OBSTECH if it does not exist yet.
288 // This is needed for For gravity_viscal which starts from
289 // products and does not have a ESO DPR TECH anymore
290 if(cpl_propertylist_has(header, "ESO DPR TECH"))
291 {
292 cpl_propertylist_update_string (idp_plist, "OBSTECH",
293 cpl_propertylist_get_string(header, "ESO DPR TECH") );
294 cpl_propertylist_set_comment (idp_plist, "OBSTECH", "Observation technique");
295 }
296 else if(cpl_propertylist_has(header, "OBSTECH"))
297 {
298 cpl_propertylist_update_string (idp_plist, "OBSTECH",
299 cpl_propertylist_get_string(header, "OBSTECH") );
300 cpl_propertylist_set_comment (idp_plist, "OBSTECH", "Observation technique");
301 }
302
303 /* SPECSYS */
304 cpl_propertylist_update_string (idp_plist, "SPECSYS", "TOPOCENT");
305 cpl_propertylist_set_comment (idp_plist, "SPECSYS", "Frame of reference for spectral coordinates");
306
307 /* TIMESYS */
308 cpl_propertylist_update_string (idp_plist, "TIMESYS", "UTC");
309 cpl_propertylist_set_comment (idp_plist, "TIMESYS", "Time system");
310
311 /* SPEC_ERR */
312 /* According to https://jira.eso.org/browse/PIPE-9900 this
313 is hard-coded depending on resolution */
314 double spec_err = 0;
315 const char * resolution = gravi_pfits_get_resolution (header);
316 if ( !strcmp (resolution, "HIGH") )
317 spec_err = 0.28;
318 if ( !strcmp (resolution, "MED") )
319 spec_err = 2.2;
320 if ( !strcmp (resolution, "LOW") )
321 spec_err = 50;
322 cpl_propertylist_update_double (idp_plist, "SPEC_ERR", spec_err);
323 cpl_propertylist_set_comment (idp_plist, "SPEC_ERR", "Statistical error in spectral coordinate");
324
325 /* SPEC_SYE */
326 /* Hard-coded to the values in Sanchez-Bermudez et al. 2017 */
327 cpl_propertylist_update_double (idp_plist, "SPEC_SYE", 0.1);
328 cpl_propertylist_set_comment (idp_plist, "SPEC_SYE", "Systematic error in spectral coordinate");
329
330 /* PROV keywords */
331 if(frameset != NULL)
332 {
333 const cpl_frame *frame;
334 size_t i_prov = 1;
335 char prov_keyword[8];
336 cpl_frameset_iterator *it = cpl_frameset_iterator_new(frameset);
337 while ((frame = cpl_frameset_iterator_get(it)) != NULL) {
338 if (strcmp(cpl_frame_get_tag(frame), GRAVI_SINGLE_SCIENCE_RAW) == 0 ||
339 strcmp(cpl_frame_get_tag(frame), GRAVI_DUAL_SCIENCE_RAW) == 0 ||
340 strcmp(cpl_frame_get_tag(frame), GRAVI_SINGLE_CALIB_RAW) == 0 ||
341 strcmp(cpl_frame_get_tag(frame), GRAVI_DUAL_CALIB_RAW) == 0)
342 {
343 snprintf(prov_keyword, 7, "PROV%zu",i_prov);
344 const char * filename = cpl_frame_get_filename(frame);
345 const char * filename_no_path = strrchr(filename, '/');
346 if (filename_no_path == NULL)
347 filename_no_path = filename;
348 else
349 filename_no_path += 1;
350 cpl_propertylist_update_string(idp_plist, prov_keyword, filename_no_path);
351 i_prov++;
352 }
353 cpl_frameset_iterator_advance(it, 1);
354 }
355 cpl_frameset_iterator_delete(it);
356 }
357
359 {
360 cpl_table * oi_array = gravi_data_get_table (vis_data, GRAVI_OI_ARRAY_EXT);
361 if(!cpl_table_has_column(oi_array, "FOV"))
362 {
363 cpl_table_new_column(oi_array, "FOV", CPL_TYPE_DOUBLE);
364 cpl_table_new_column(oi_array, "FOVTYPE", CPL_TYPE_STRING);
365 double fov;
366 const char * telname = gravi_conf_get_telname (0, header);
367 if (telname == NULL) {
368 cpl_msg_warning(cpl_func, "Cannot get TELNAME, FOV is not determined");
369 fov = 0.0;
370 } else {
371 if (telname[0] == 'U')
372 fov = 0.03; // Hard-coded UT FOV
373 else
374 fov = 0.14; // Hard-coded AT FOV
375 }
376
377 cpl_table_fill_column_window_double(oi_array, "FOV", 0, cpl_table_get_nrow(oi_array), fov);
378 cpl_table_fill_column_window_string(oi_array, "FOVTYPE", 0, cpl_table_get_nrow(oi_array),"RADIUS");
379 }
380 }
381
382 /* Delete scratch tables */
383 cpl_table_delete(oi_vis2_SC_allpol);
384 cpl_table_delete(oi_T3_SC_allpol);
385 cpl_table_delete(oi_wave_SC_allpol);
386
387 return idp_plist;
388}
389
typedefCPL_BEGIN_DECLS struct _gravi_data_ gravi_data
Definition: gravi_data.h:39
#define gravi_data_get_oi_t3(data, type, pol, npol)
Definition: gravi_data.h:48
#define gravi_data_get_oi_vis2(data, type, pol, npol)
Definition: gravi_data.h:47
#define gravi_data_get_oi_wave(data, type, pol, npol)
Definition: gravi_data.h:45
#define gravi_data_get_oi_vis(data, type, pol, npol)
Definition: gravi_data.h:46
#define GRAVI_SINGLE_SCIENCE_RAW
Definition: gravi_dfs.h:52
#define GRAVI_DUAL_CALIB_RAW
Definition: gravi_dfs.h:53
#define GRAVI_SINGLE_CALIB_RAW
Definition: gravi_dfs.h:51
#define GRAVI_DUAL_SCIENCE_RAW
Definition: gravi_dfs.h:54
cpl_propertylist * gravi_idp_compute(gravi_data *vis_data, cpl_propertylist *header, cpl_frameset *frameset)
Create IDP keywords to satisfy standard.
Definition: gravi_idp.c:47
cpl_propertylist * header
Definition: gravi_old.c:2004
cpl_propertylist_update_double(header, "ESO QC MINWAVE SC", cpl_propertylist_get_double(plist, "ESO QC MINWAVE SC"))
#define GRAVI_OI_VIS2_EXT
Definition: gravi_pfits.h:95
#define GRAVI_OI_ARRAY_EXT
Definition: gravi_pfits.h:83
#define GRAVI_SC
Definition: gravi_pfits.h:165
#define GRAVI_NBASE
Definition: gravi_utils.h:105
double gravi_table_get_column_flagged_mean(cpl_table *table, const char *name)
Function to compute the mean of a column table with arrays.
Definition: gravi_cpl.c:217
double gravi_table_get_column_flagged_max(cpl_table *table, const char *name)
Function to compute the maximum of a column table with arrays.
Definition: gravi_cpl.c:290
int gravi_data_has_extension(gravi_data *raw_calib, const char *ext_name)
Check if data has extension with given EXTNAME.
Definition: gravi_data.c:1808
cpl_table * gravi_data_get_table(gravi_data *self, const char *extname)
Return a pointer on a table extension by its EXTNAME.
Definition: gravi_data.c:2096
cpl_frameset * gravi_frameset_extract_fringe_data(cpl_frameset *frameset)
Definition: gravi_dfs.c:1323
int gravi_pfits_get_pola_num(const cpl_propertylist *plist, int type_data)
Definition: gravi_pfits.c:263
double gravi_pfits_get_mjd(const cpl_propertylist *plist)
Definition: gravi_pfits.c:526
const char * gravi_pfits_get_resolution(const cpl_propertylist *plist)
Definition: gravi_pfits.c:155
const char * gravi_conf_get_telname(int gravi_beam, cpl_propertylist *header)
Definition: gravi_utils.c:1191
cpl_error_code gravi_data_get_minmax_uvcoord(const cpl_table *oi_vis2, double *min_uvcoord, double *max_uvcoord)
Compute the minimum and maximum values of sqrt(ucoord**2 + vcoord**2)
Definition: gravi_vis.c:2509