GRAVI Pipeline Reference Manual  1.2.3
gravi_p2vmred.c
1 /* $Id: gravi_tf.c,v 1.10 2014/11/12 15:10:40 nazouaoui Exp $
2  *
3  * This file is part of the GRAVI Pipeline
4  * Copyright (C) 2002,2003 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20 
33 /*
34  * History :
35  * 11/01/2019 Fix Warnings , unused parameter : npol_ft, npol_sc, p2vmreducedFlag ,init_type_data, end_type_data
36  */
37 
38 /*-----------------------------------------------------------------------------
39  Includes
40  -----------------------------------------------------------------------------*/
41 
42 #ifdef HAVE_CONFIG_H
43 #include <config.h>
44 #endif
45 
46 #include <cpl.h>
47 #include <string.h>
48 #include <stdio.h>
49 #include <math.h>
50 #include <time.h>
51 #include <complex.h>
52 
53 #include "gravi_data.h"
54 #include "gravi_dfs.h"
55 #include "gravi_pfits.h"
56 #include "gravi_cpl.h"
57 
58 #include "gravi_utils.h"
59 
60 #include "gravi_p2vmred.h"
61 #include "gravi_vis.h"
62 
63 /*-----------------------------------------------------------------------------
64  Private prototypes
65  -----------------------------------------------------------------------------*/
66 
67 cpl_table * gravi_create_oitarget_table (const cpl_propertylist *header,
68  const char * mode);
69 
70 cpl_table * gravi_create_oiarray_table (const cpl_table * array_geometry,
71  int is_cal);
72 
73 /*-----------------------------------------------------------------------------
74  Function code
75  -----------------------------------------------------------------------------*/
76 
77 /*----------------------------------------------------------------------------*/
88 /*----------------------------------------------------------------------------*/
89 
90 cpl_table * gravi_create_oitarget_table (const cpl_propertylist *header,
91  const char * mode)
92 {
93  /* Message and timer */
94  gravi_msg_function_start(1);
95  cpl_ensure (header, CPL_ERROR_NULL_INPUT, NULL);
96  cpl_ensure (mode, CPL_ERROR_NULL_INPUT, NULL);
97 
98  /* Check if this observation has a reference object in FT and a science object in SC */
99  int n_target = 1;
100  if (!(strcmp (mode, "gravi_dual"))) n_target = 2;
101  else if (!(strcmp (mode, "gravi_single"))) n_target = 1;
102  else {cpl_error_set_message(cpl_func, CPL_ERROR_NULL_INPUT, "Invalid mode (bug)");return NULL;}
103 
104  /* Create the table */
105  cpl_table * oi_target = cpl_table_new (n_target);
106 
107  /* TARGET_ID */
108  cpl_table_new_column (oi_target, "TARGET_ID", CPL_TYPE_INT);
109  cpl_table_set_column_savetype (oi_target, "TARGET_ID", CPL_TYPE_SHORT);
110  /* TARGET */
111  cpl_table_new_column (oi_target, "TARGET", CPL_TYPE_STRING);
112  /* EQUINOX */
113  cpl_table_new_column (oi_target, "EQUINOX", CPL_TYPE_FLOAT);
114  cpl_table_set_column_unit (oi_target, "EQUINOX", "yr");
115  /* RAEP0 and DECEP0 */
116  cpl_table_new_column (oi_target, "RAEP0", CPL_TYPE_DOUBLE);
117  cpl_table_set_column_unit (oi_target, "RAEP0", "deg");
118  cpl_table_new_column (oi_target, "DECEP0", CPL_TYPE_DOUBLE);
119  cpl_table_set_column_unit (oi_target, "DECEP0", "deg");
120  /* RA_ERR and DEC_ERR */
121  cpl_table_new_column (oi_target, "RA_ERR", CPL_TYPE_DOUBLE);
122  cpl_table_set_column_unit (oi_target, "RA_ERR", "deg");
123  cpl_table_new_column (oi_target, "DEC_ERR", CPL_TYPE_DOUBLE);
124  cpl_table_set_column_unit (oi_target, "DEC_ERR", "deg");
125  /* SYSVEL, VELTYP, VELDEF */
126  cpl_table_new_column (oi_target, "SYSVEL", CPL_TYPE_DOUBLE);
127  cpl_table_set_column_unit (oi_target, "SYSVEL", "m/s");
128  cpl_table_new_column (oi_target, "VELTYP", CPL_TYPE_STRING);
129  cpl_table_new_column (oi_target, "VELDEF", CPL_TYPE_STRING);
130  /* PMRA, PMDEC and associated errors */
131  cpl_table_new_column (oi_target, "PMRA", CPL_TYPE_DOUBLE);
132  cpl_table_set_column_unit (oi_target, "PMRA", "deg/yr");
133  cpl_table_new_column (oi_target, "PMDEC", CPL_TYPE_DOUBLE);
134  cpl_table_set_column_unit (oi_target, "PMDEC", "deg/yr");
135  cpl_table_new_column (oi_target, "PMRA_ERR", CPL_TYPE_DOUBLE);
136  cpl_table_set_column_unit (oi_target, "PMRA_ERR", "deg/yr");
137  cpl_table_new_column (oi_target, "PMDEC_ERR", CPL_TYPE_DOUBLE);
138  cpl_table_set_column_unit (oi_target, "PMDEC_ERR", "deg/yr");
139  /* PARALLAX and PARA_ERR */
140  cpl_table_new_column (oi_target, "PARALLAX", CPL_TYPE_FLOAT);
141  cpl_table_set_column_unit (oi_target, "PARALLAX", "deg");
142  cpl_table_new_column (oi_target, "PARA_ERR", CPL_TYPE_FLOAT);
143  cpl_table_set_column_unit (oi_target, "PARA_ERR", "deg");
144  /* SPECTYP */
145  cpl_table_new_column (oi_target, "SPECTYP", CPL_TYPE_STRING);
146  CPLCHECK_NUL("Cannot create columns in OI_TARGET");
147 
148  /* loop on target to fill the columns*/
149  for (int ti=0; ti<n_target; ti++) {
150 
151  /* TARGET_ID=1 is for FT and TARGET_ID=2 is for SC (if any) */
152  cpl_table_set_int (oi_target, "TARGET_ID", ti, ti+1);
153 
154  /* TARGET name, forced to match 16 chars */
155  const char *name = (ti?gravi_pfits_get_sobj(header):gravi_pfits_get_robj(header));
156  gravi_table_set_string_fixlen (oi_target, "TARGET", ti, name, 16);
157 
158  CPLCHECK_NUL("Cannot set Target");
159 
160  /* At the moment, we can't fill the following columns. We need to put something for
161  * standard compliance. Some third party software fail if those columns are missing.
162  * Should be checked whether better values are available. */
163 
164  /* EQUINOX, RAEP0 and DECEP0 at mean equinox, for FT or SC */
165  float equinox = (float)gravi_pfits_get_double_silentdefault (header, "EQUINOX", 0.);
166  double raep = (ti?gravi_pfits_get_sobj_raep (header):gravi_pfits_get_robj_raep (header));
167  double decp = (ti?gravi_pfits_get_sobj_decep (header):gravi_pfits_get_robj_decep (header));
168  if (ti==0) gravi_dump_the_boss (raep, decp);
169 
170  double ra_err = 0.0;
171  double dec_err = 0.0;
172  raep *= CPL_MATH_DEG_RAD; // [deg]
173  decp *= CPL_MATH_DEG_RAD; // [deg]
174  CPLCHECK_NUL("Cannot get EQUINOX, RA and DEC");
175 
176  /* Some verbose */
177  cpl_msg_info (cpl_func,"Found target '%s' with ra=%fd and dec=%fd at equinox %.2f", name, raep, decp, equinox);
178 
179  /* SYSVEL, VELTYP, VELDEF */
180  double sysvel = gravi_pfits_get_double_silentdefault (header, "ESO FT ROBJ RADVEL", 0.);
181  const char * veltyp = "UNKNOWN ";
182  const char * veldef = "OPTICAL ";
183  CPLCHECK_NUL("Cannot get SYSVEL, VELTYPE...");
184 
185  /* PMRA, PMDEC and associated errors in deg/years
186  * These quantities are in as/years in the HEADER */
187  double pmra = gravi_pfits_get_pmra (header) / 3600.0; // [deg/year]
188  double pmdec = gravi_pfits_get_pmdec (header) / 3600.0; // [deg/year]
189  double pmra_err = 0.0;
190  double pmdec_err = 0.0;
191  CPLCHECK_NUL("Cannot get PMRA...");
192 
193  /* PARALLAX and PARA_ERR */
194  float parallax = gravi_pfits_get_plx (header); // [as]
195  float para_err = 0.0;
196  CPLCHECK_NUL("Cannot get PARRALAX...");
197 
198  /* SPECTYP */
199  const char * spectyp = "UNKNOWN ";
200 
201  /* Fill common columns */
202  cpl_table_set_float (oi_target, "EQUINOX", ti, equinox);
203  cpl_table_set_double (oi_target, "RAEP0", ti, raep);
204  cpl_table_set_double (oi_target, "DECEP0", ti, decp);
205  cpl_table_set_double (oi_target, "RA_ERR", ti, ra_err);
206  cpl_table_set_double (oi_target, "DEC_ERR", ti, dec_err);
207  cpl_table_set_double (oi_target, "SYSVEL", ti, sysvel);
208  cpl_table_set_string (oi_target, "VELTYP", ti, veltyp);
209  cpl_table_set_string (oi_target, "VELDEF", ti, veldef);
210  cpl_table_set_double (oi_target, "PMRA", ti, pmra);
211  cpl_table_set_double (oi_target, "PMDEC", ti, pmdec);
212  cpl_table_set_double (oi_target, "PMRA_ERR", ti, pmra_err);
213  cpl_table_set_double (oi_target, "PMDEC_ERR",ti, pmdec_err);
214  cpl_table_set_float (oi_target, "PARALLAX", ti, parallax);
215  cpl_table_set_float (oi_target, "PARA_ERR", ti, para_err);
216  cpl_table_set_string (oi_target, "SPECTYP", ti, spectyp);
217  }
218  /* End loop on target_id */
219 
220  gravi_msg_function_exit(1);
221  return oi_target;
222 }
223 
224 /*----------------------------------------------------------------------------*/
236 /*----------------------------------------------------------------------------*/
237 
238 cpl_table * gravi_create_oiarray_table (const cpl_table * array_geometry,
239  int is_cal)
240 {
241  gravi_msg_function_start(1);
242  cpl_ensure (array_geometry, CPL_ERROR_NULL_INPUT, NULL);
243 
244  cpl_table * oi_array = cpl_table_duplicate (array_geometry);
245 
246  /* Rename column STA to STAXYZ */
247  if (cpl_table_has_column (oi_array, "STA") ) {
248  cpl_table_name_column (oi_array, "STA", "STAXYZ");
249  }
250  CPLCHECK_NUL ("Cannot rename STA into STAXYZ");
251 
252  /* Make sure there are 4 rows (one per beam) */
253  cpl_size n_row = cpl_table_get_nrow (oi_array);
254  if (n_row < 4) {
255 
256  if (is_cal) cpl_msg_info (cpl_func, "ARRAY_GEOMETRY has %lld rows instead of 4", n_row);
257  else cpl_msg_warning (cpl_func, "ARRAY_GEOMETRY has %lld rows instead of 4", n_row);
258  cpl_table_set_size (oi_array, 4);
259 
260  cpl_array * zero_array = gravi_array_init_double (3,0.0);
261 
262  /* Fill these new rows with default values. */
263  for ( int row=n_row; row<4; row++ ) {
264  char name16[17];
265  cpl_table_set_int (oi_array, "STA_INDEX", row, 100+row);
266  sprintf (name16, "S%i", row);
267  cpl_table_set_string (oi_array, "STA_NAME", row, name16);
268  sprintf (name16, "T%i", row);
269  cpl_table_set_string (oi_array, "TEL_NAME", row, name16);
270  cpl_table_set_array (oi_array, "STAXYZ", row, zero_array);
271  cpl_table_set_float (oi_array, "DIAMETER", row, 0.0);
272  cpl_table_set_int (oi_array, "MNTSTA", row, 0);
273  if (is_cal) cpl_msg_info (cpl_func,"Add STA_INDEX %i with TEL_NAME=%s in OI_ARRAY",100+row,name16);
274  else cpl_msg_warning (cpl_func,"Add STA_INDEX %i with TEL_NAME=%s in OI_ARRAY",100+row,name16);
275  CPLCHECK_NUL ("Cannot insert rows");
276  }
277 
278  FREE (cpl_array_delete, zero_array);
279 
280  }/* End case there are missing rows */
281 
282  /* Update TEL_NAME and STA_NAME to use 16 char for OIFITS compliance */
283  for ( int row=0; row<cpl_table_get_nrow (oi_array); row++ ) {
284  const char * tel_name = cpl_table_get_string (oi_array, "TEL_NAME", row);
285  gravi_table_set_string_fixlen (oi_array, "TEL_NAME", row, tel_name, 16);
286  const char * sta_name = cpl_table_get_string (oi_array, "STA_NAME", row);
287  gravi_table_set_string_fixlen (oi_array, "STA_NAME", row, sta_name, 16);
288  }
289  CPLCHECK_NUL ("Cannot force 16 chars in OI_ARRAY");
290 
291  gravi_msg_function_exit(1);
292  return oi_array;
293 }
294 
295 /*----------------------------------------------------------------------------*/
328 /*----------------------------------------------------------------------------*/
329 
330 gravi_data * gravi_compute_p2vmred (gravi_data * preproc_data, gravi_data * p2vm_map,
331  const char * mode, const cpl_parameterlist * parlist,
332  enum gravi_detector_type det_type)
333 {
334  /* Message and timer */
335  gravi_msg_function_start(1);
336  cpl_ensure (preproc_data, CPL_ERROR_NULL_INPUT, NULL);
337  cpl_ensure (p2vm_map, CPL_ERROR_NULL_INPUT, NULL);
338  cpl_ensure (mode, CPL_ERROR_NULL_INPUT, NULL);
339  cpl_ensure (parlist, CPL_ERROR_NULL_INPUT, NULL);
340 
341  char qc_name[90];
342  int ntel = 4, nbase = 6;
343  int nv; /* npol_ft, npol_sc; */
344 
345  /* Timers */
346  clock_t timer_ft = 0, timer_sc = 0, timer_other = 0, timer_start = 0;
347  timer_start = clock();
348 
349  /* Global properties */
350  cpl_propertylist * preproc_header = gravi_data_get_header (preproc_data);
351 
352  /* Flag if the p2vmreduced-file is requested */
353  /* int p2vmreducedFlag = 0; */
354  if ( (cpl_parameterlist_find_const (parlist, "gravity.dfs.p2vmred-file") != NULL &&
355  gravi_param_get_bool (parlist,"gravity.dfs.p2vmred-file")) ||
356  (cpl_parameterlist_find_const (parlist, "gravity.dfs.astro-file") != NULL &&
357  gravi_param_get_bool (parlist,"gravity.dfs.astro-file")) ) {
358  cpl_msg_info (cpl_func, "p2vmreduced-file is requested, will add computation time.");
359  /* p2vmreducedFlag = 1; */
360  } else {
361  cpl_msg_info (cpl_func, "p2vmreduced-file is not requested.");
362  }
363 
364  CPLCHECK_NUL("Cannot check the parameter");
365 
366  /* Get this flag if internal calib or on-sky */
367  int is_cal = gravi_pfits_is_calib (preproc_header);
368 
369  /* Build the output gravi_data. */
370  gravi_data * p2vmred_data = gravi_data_new (0);
371  cpl_propertylist * p2vmred_header = gravi_data_get_header (p2vmred_data);
372 
373  /* Dump all header from RAW product */
374  cpl_propertylist_append (p2vmred_header, preproc_header);
375 
376  /* Duplicate necessary tables in product */
377  gravi_data_copy_ext (p2vmred_data, preproc_data, GRAVI_OI_WAVELENGTH_EXT);
378 
379  /* Create OI_TARGET from header and add it to product */
380  cpl_table * oi_target = gravi_create_oitarget_table (preproc_header, mode);
381  gravi_data_add_table (p2vmred_data, NULL, GRAVI_OI_TARGET_EXT, oi_target);
382  CPLCHECK_NUL("Cannot create OI_TARGET");
383 
384  /* Duplicate ARRAY_GEOMETRY as OI_ARRAY */
385  cpl_table * array_geo = gravi_data_get_table (preproc_data, GRAVI_ARRAY_GEOMETRY_EXT);
386  CPLCHECK_NUL("Cannot get ARRAY_GEOMETRY");
387  cpl_table * oi_array = gravi_create_oiarray_table (array_geo, is_cal);
388  CPLCHECK_NUL("Cannot convert ARRAY_GEOMETRY into OI_ARRAY");
389 
390  /* Build the header of OI_ARRAY from the header of OI_ARRAY */
391  cpl_propertylist * oiarray_plist = gravi_data_get_plist (preproc_data, GRAVI_ARRAY_GEOMETRY_EXT);
392  oiarray_plist = cpl_propertylist_duplicate (oiarray_plist);
393 
394  /* Set OI_ARRAY in output data */
395  gravi_data_add_table (p2vmred_data, oiarray_plist,
396  GRAVI_OI_ARRAY_EXT, oi_array);
397  CPLCHECK_NUL ("Cannot add OI_ARRAY");
398 
399  /* Update the keyword ARRAYX/Y/Z to ensure double */
400  cpl_msg_info (cpl_func, "Update the ARRAYX/Y/Z keywords as double precision");
401  gravi_pfits_ensure_double (oiarray_plist, "ARRAYX");
402  gravi_pfits_ensure_double (oiarray_plist, "ARRAYY");
403  gravi_pfits_ensure_double (oiarray_plist, "ARRAYZ");
404 
405  /* Read OPTICAL_TRAIN */
406  cpl_table * optical_train_table = gravi_data_get_table (preproc_data, GRAVI_OPTICAL_TRAIN_EXT);
407 
408  /* Loop on lab input corresponding to GRAVITY to make sure all beams exists
409  * If they don't, they are filled with fake value to macth the OI_ARRAY */
410  int labInput[4] = {GRAVI_LABINPUT_1,GRAVI_LABINPUT_2,GRAVI_LABINPUT_3,GRAVI_LABINPUT_4};
411  for (int lab = 0 ; lab < 4 ; lab++) {
412 
413  /* Search if this LABINIT already exist */
414  cpl_table_unselect_all (optical_train_table);
415  if ( cpl_table_or_selected_int (optical_train_table, "VALUE2", CPL_EQUAL_TO, labInput[lab]) == 1 ) {
416  continue;
417  }
418 
419  /* If not, add a rows and fill it with default values */
420  char name16[17];
421  int nrow = cpl_table_get_nrow (optical_train_table);
422  cpl_table_set_size (optical_train_table, nrow + 1);
423  cpl_table_set_int (optical_train_table, "VALUE2", nrow, labInput[lab]);
424  sprintf (name16, "T%i", nrow);
425  cpl_table_set_string (optical_train_table, "TEL_NAME", nrow, name16);
426  if (is_cal) cpl_msg_info (cpl_func,"Add LABINPUT %i with TEL_NAME=%s in OPTICAL_TRAIN",labInput[lab],name16);
427  else cpl_msg_warning (cpl_func,"Add LABINPUT %i with TEL_NAME=%s in OPTICAL_TRAIN",labInput[lab],name16);
428  CPLCHECK_NUL("Cannot insert rows");
429  } /* End loop in labInput */
430 
431  /*
432  * For each type of data SC / FT
433  */
434  /* int init_type_data = 1;
435  int end_type_data = 1;
436 
437  if(det_type == GRAVI_DET_SC || det_type == GRAVI_DET_ALL)
438  init_type_data = 0;
439  if(det_type == GRAVI_DET_FT || det_type == GRAVI_DET_ALL)
440  end_type_data = 1;
441  */
442  for (int type_data = 0; type_data < 2; type_data ++){
443 
444  /* Check if this data is present */
445  if (!gravi_data_has_extension (preproc_data, GRAVI_SPECTRUM_DATA_EXT(type_data))) {
446  cpl_msg_info (cpl_func,"No data for %s, skip it", GRAVI_TYPE(type_data));
447  continue;
448  }
449 
450  /* Get the table and property list utilities */
451  cpl_table * p2vm_table = gravi_data_get_p2vm_data (p2vm_map, type_data);
452  cpl_table * spectrum_table = gravi_data_get_spectrum_data (preproc_data, type_data);
453  cpl_table * detector_table = gravi_data_get_imaging_detector (preproc_data, type_data);
454  cpl_table * detector_p2vm = gravi_data_get_imaging_detector (p2vm_map, type_data);
455  CPLCHECK_NUL ("Cannot get data");
456 
457  /* Verify the data and P2VM are conformable */
458  cpl_ensure (cpl_table_get_nrow (detector_table) ==
459  cpl_table_get_nrow (detector_p2vm),
460  CPL_ERROR_ILLEGAL_INPUT, NULL);
461 
462  /* Read nrow and nregion */
463  cpl_size nrow = cpl_table_get_nrow (spectrum_table);
464  int n_detregion = cpl_table_get_nrow (detector_table);
465 
466  /* Guess the number of polarisation by looking at the number of region
467  * Save the number of polarisation for further use */
468  int npol = (n_detregion>24 ? 2 : 1);
469  /*if (type_data == GRAVI_FT) npol_ft = npol;
470  if (type_data == GRAVI_SC) npol_sc = npol; */
471 
472  /* FIXME: Here we assume the two polarisation (if any)
473  * have the same number of channels ? */
474  cpl_size nwave = gravi_spectrum_get_nwave (spectrum_table);
475 
476  /*
477  * Loop on polarisations
478  */
479  for (int p = 0; p < npol; p ++) {
480 
481  cpl_msg_info(cpl_func, "Visibility extraction: polarisation %d over %d for %s",
482  p+1, npol, type_data==GRAVI_FT?"FT":"SC");
483 
484  timer_other += (clock() - timer_start);
485  timer_start = clock();
486 
487  /*
488  *
489  * Create output tables and set them in output data
490  *
491  */
492  cpl_msg_info(cpl_func, "Create and set output tables...");
493 
494  /* Create table */
495  cpl_table * oi_vis = gravi_table_oi_create (nwave, nrow, GRAVI_OI_VIS_EXT);
496  cpl_table * oi_flux = gravi_table_oi_create (nwave, nrow, GRAVI_OI_FLUX_EXT);
497 
498  /* Delete useless columns in this product */
499  cpl_table_erase_column (oi_vis, "VISAMP");
500  cpl_table_erase_column (oi_vis, "VISPHI");
501  cpl_table_erase_column (oi_vis, "VISAMPERR");
502  cpl_table_erase_column (oi_vis, "VISPHIERR");
503  cpl_table_erase_column (oi_vis, "RVIS");
504  cpl_table_erase_column (oi_vis, "RVISERR");
505  cpl_table_erase_column (oi_vis, "IVIS");
506  cpl_table_erase_column (oi_vis, "IVISERR");
507 
508  /* Set table in p2vmred */
509  cpl_propertylist * oi_plist = cpl_propertylist_new ();
510  cpl_propertylist_copy_property (oi_plist, oiarray_plist, "ARRNAME");
511  cpl_propertylist_copy_property (oi_plist, preproc_header, "DATE-OBS");
512  cpl_propertylist_update_int (oi_plist, "OI_REVN", 1);
513  cpl_propertylist_update_int (oi_plist, "NWAVE", nwave);
514  cpl_propertylist_update_string (oi_plist, "INSNAME", GRAVI_INSNAME(type_data,p,npol));
515  cpl_propertylist_update_int (oi_plist, "EXTVER", GRAVI_EXTVER(type_data,p,npol));
516 
517  gravi_data_add_table (p2vmred_data, oi_plist,
518  GRAVI_OI_VIS_EXT, oi_vis);
519 
520  oi_plist = cpl_propertylist_duplicate (oi_plist);
521 
522  gravi_data_add_table (p2vmred_data, oi_plist,
523  GRAVI_OI_FLUX_EXT, oi_flux);
524 
525  cpl_msg_info(cpl_func, "Total time to create tables: %.4f s", (double)(clock()-timer_start)/(double)CLOCKS_PER_SEC );
526 
527  /*
528  *
529  * Compute the P2VM and the V2PM for all wavelengths
530  *
531  */
532  timer_other += (clock() - timer_start);
533  timer_start = clock();
534 
535  cpl_msg_info (cpl_func, "Compute the invers V2P2M -> P2VM matrix...");
536 
537  /* Construction of output tables */
538  cpl_matrix ** p2vm = cpl_calloc (nwave,sizeof (cpl_matrix*));
539 
540  /* Get the list of regions having this pol */
541  int all_region[48];
542  int n_region = 0;
543  for (int detreg = 0; detreg < n_detregion; detreg++) {
544  if (gravi_region_get_pol (detector_p2vm, detreg) != p) continue;
545  all_region[n_region] = detreg;
546  n_region++;
547  }
548 
549  /* Get direct pointer to arrays */
550  const cpl_array ** trans, ** coh, ** phase;
551  trans = cpl_table_get_data_array_const (p2vm_table, TRANSMISSION);
552  coh = cpl_table_get_data_array_const (p2vm_table, COHERENCE);
553  phase = cpl_table_get_data_array_const (p2vm_table, PHASE);
554  CPLCHECK_NUL ("Cannot load the p2vm data");
555 
556  /* Allocate memory for direct access to P2VM */
557  double ** pP2VM = cpl_malloc (sizeof(double*) * nwave);
558 
559  /* Loop on wave */
560  for (cpl_size wave = 0; wave < nwave; wave ++) {
561 
562  /* Construction of the v2pm matrix */
563  cpl_matrix * v2pm = cpl_matrix_new (n_region, 16);
564 
565  /* Loop on region */
566  for (cpl_size region = 0; region < n_region; region++) {
567  int detregion = all_region[region];
568 
569  /* Replace the four first columns of the v2pm by
570  * the transmission */
571  for (int i = 0; i < 4; i++){
572  cpl_matrix_set (v2pm, region, i,
573  cpl_array_get (trans[detregion], wave + i * nwave, &nv));
574  }
575 
576  /* Replace the twelve other columns of the v2pm by
577  * the real part and the imaginary part of the coherence
578  * and the phase */
579  for (int i = 0; i < 6; i++) {
580  cpl_matrix_set (v2pm, region, i + 4,
581  cpl_array_get (coh[detregion], wave + i * nwave, &nv) *
582  cos(cpl_array_get (phase[detregion], wave + i * nwave, &nv)));
583  cpl_matrix_set (v2pm, region, i + 10,
584  cpl_array_get (coh[detregion], wave + i * nwave, &nv) *
585  sin(cpl_array_get (phase[detregion], wave + i * nwave, &nv)));
586  }
587  }
588  CPLCHECK_NUL("Cannot fill the V2PM");
589 
590  /* Ensure the V2PM is flux conservative */
591  cpl_matrix_multiply_scalar (v2pm, 1./n_region);
592 
593  /* Compute the matrix inversion of the v2pm using the
594  * singular value decomposition method */
595  p2vm[wave] = gravi_matrix_invertSV_create (v2pm);
596  cpl_matrix_delete (v2pm);
597 
598  /* Keep a pointer to the data */
599  pP2VM[wave] = cpl_matrix_get_data (p2vm[wave]);
600 
601  CPLCHECK_NUL ("Cannot invers V2PM");
602  }
603  /* End loop on wavelength */
604 
605  cpl_msg_info (cpl_func, "Total time to invers matrix: %.4f s", (double)(clock()-timer_start)/(double)CLOCKS_PER_SEC );
606 
607  /*
608  * Fill the STA_INDEX and TIME.
609  */
610  cpl_msg_info (cpl_func, "Fill the STA_INDEX and TIME.");
611  timer_other += (clock() - timer_start);
612  timer_start = clock();
613 
614  /* Wrap the TIME column into an array. TIME is in [us] from
615  * the PCR.ACQ.START (start of RMN recording) */
616  cpl_array * times = cpl_array_wrap_int (cpl_table_get_data_int (spectrum_table, "TIME"),
617  cpl_table_get_nrow (spectrum_table));
618  cpl_ensure (times, CPL_ERROR_ILLEGAL_INPUT, NULL);
619 
620  /* Compute the MJDs for each row, by adding PCR.ACQ.TIME
621  * (in MJD) to the TIME column */
622  cpl_array * mjds = cpl_array_cast (times, CPL_TYPE_DOUBLE);
623  cpl_array_divide_scalar (mjds, 86400.E6);
624  double mjd0 = gravi_convert_to_mjd (gravi_pfits_get_start_prcacq (preproc_header));
625  cpl_array_add_scalar (mjds, mjd0);
626 
627  /* We keep the TIME column of the P2VMRED in [us] from
628  * the PCR.ACQ.START, in order to correlate with RMN tables.
629  * Thus this TIME column is *not* at the OIFITS standart */
630  cpl_table_set_column_unit (oi_vis, "TIME", "usec");
631  cpl_table_set_column_unit (oi_flux, "TIME", "usec");
632 
633  /* Fill STA_INDEX and times for OI_VIS */
634  cpl_array * sta_index = cpl_array_new (2, CPL_TYPE_INT);
635 
636  for (int base=0; base < nbase; ++base) {
637 
638  /* Build sta_index */
639  int sta0 = gravi_sta_index(GRAVI_BASE_TEL[base][0]+1, optical_train_table, oi_array);
640  int sta1 = gravi_sta_index(GRAVI_BASE_TEL[base][1]+1, optical_train_table, oi_array);
641  cpl_array_set_int (sta_index, 0, sta0);
642  cpl_array_set_int (sta_index, 1, sta1);
643  CPLCHECK_NUL ("Cannot find the sta_index");
644 
645  /* loop on rows */
646  for (int row=0; row < nrow; ++row) {
647  int idx = row * nbase + base;
648  cpl_table_set_array (oi_vis, "STA_INDEX", idx, sta_index);
649  cpl_table_set (oi_vis, "TIME", idx, cpl_array_get (times, row, &nv));
650  cpl_table_set (oi_vis, "MJD", idx, cpl_array_get (mjds, row, &nv));
651  }
652  } /* End loop on base */
653 
654  cpl_array_delete (sta_index);
655  CPLCHECK_NUL ("Cannot fill sta_index or time in OI_VIS");
656 
657  /* Fill STA_INDEX and times for OI_FLUX */
658  for (int tel = 0; tel < ntel; tel++){
659  int sta0 = gravi_sta_index(tel+1, optical_train_table, oi_array);
660  for (int row=0; row < nrow; ++row) {
661  int idx = row * ntel + tel;
662  cpl_table_set_int (oi_flux, "STA_INDEX", idx, sta0);
663  cpl_table_set (oi_flux, "TIME", idx, cpl_array_get (times, row, &nv));
664  cpl_table_set (oi_flux, "MJD", idx, cpl_array_get (mjds, row, &nv));
665  }
666  } /* End loop on tel */
667 
668  cpl_array_delete (mjds);
669  cpl_array_unwrap (times);
670  CPLCHECK_NUL ("Cannot fill sta_index or time in OI_FLUX");
671 
672  /* Fill the exposure time */
673  double exptime = gravi_pfits_get_dit (gravi_data_get_header (preproc_data), type_data);
674  cpl_table_fill_column_window (oi_vis, "INT_TIME", 0, nrow * nbase, exptime);
675  cpl_table_fill_column_window (oi_flux, "INT_TIME", 0, nrow * ntel, exptime);
676  CPLCHECK_NUL ("Cannot fill exptime");
677 
678  /* target_id is 1 unless type_data==SC and mode==dual_field
679  * Same definition applies when building the OI_TARGET */
680  int target_id = (!strcmp(mode, "gravi_dual") && (type_data==GRAVI_SC) )?2:1;
681  cpl_table_fill_column_window_int (oi_vis, "TARGET_ID", 0, nrow * nbase, target_id);
682  cpl_table_fill_column_window_int (oi_flux, "TARGET_ID", 0, nrow * ntel, target_id);
683  CPLCHECK_NUL ("Cannot fill target_id");
684 
685  cpl_msg_info (cpl_func, "Total time to fill STA_INDEX and TIME: %.4f s",
686  (double)(clock()-timer_start)/(double)CLOCKS_PER_SEC );
687 
688  /*
689  *
690  * Extract visibility with p2vm
691  *
692  */
693 
694  cpl_msg_info (cpl_func,"Apply p2vm to the data...");
695  timer_other += (clock() - timer_start);
696  timer_start = clock();
697 
698  /* Get the pointers to the input data */
699  double ** pReg = cpl_malloc (n_region * sizeof(double*));
700  double ** pErr = cpl_malloc (n_region * sizeof(double*));
701  cpl_array *** pRegArr = cpl_malloc (n_region * sizeof(cpl_array**));
702  cpl_array *** pErrArr = cpl_malloc (n_region * sizeof(cpl_array**));
703  for (int reg = 0; reg < n_region; reg++) {
704  pRegArr[reg] = cpl_table_get_data_array (spectrum_table, GRAVI_DATA[all_region[reg]]);
705  pErrArr[reg] = cpl_table_get_data_array (spectrum_table, GRAVI_DATAERR[all_region[reg]]);
706  }
707  CPLCHECK_NUL ("Cannot get data");
708 
709  /* Get the pointers to the output data */
710  cpl_array** tFlux = cpl_table_get_data_array (oi_flux, "FLUX");
711  cpl_array** tFluxErr = cpl_table_get_data_array (oi_flux, "FLUXERR");
712  cpl_array** tVis = cpl_table_get_data_array (oi_vis, "VISDATA");
713  cpl_array** tVisErr = cpl_table_get_data_array (oi_vis, "VISERR");
714  CPLCHECK_NUL ("Cannot get data");
715 
716  /* Temporary matrix output memory */
717  double* pOut = cpl_malloc (16 * nwave * sizeof(double));
718  double* pOutVar = cpl_malloc (16 * nwave * sizeof(double));
719 
720  /* Quantities to test flux conservation */
721  double full_flux_reg = 0.0, full_flux_tel = 0.0;
722 
723  cpl_msg_debug (cpl_func, "Matrix multiplication");
724 
725  /* Loop on the frames */
726  for (cpl_size row = 0; row < nrow; row ++) {
727 
728  /* Get pointers to the input data of this row */
729  for (int reg = 0; reg < n_region; reg++) {
730  pReg[reg] = cpl_array_get_data_double (pRegArr[reg][row]);
731  pErr[reg] = cpl_array_get_data_double (pErrArr[reg][row]);
732  }
733 
734  /* Loop on wavelength */
735  for (cpl_size wave = 0 ; wave < nwave ; wave++ ) {
736 
737  /* Integration the input flux of the row */
738  for (int reg = 0; reg < n_region; reg++) full_flux_reg += pReg[reg][wave];
739 
740  /* Matrix multiplication, loop on outputs and regions
741  * We neglect the input correlation and don't compute
742  * the output correlations. FIXME: we need to compute
743  * the output covariance of {R,I} */
744  for (int out=0; out<16; out++) {
745  pOut[out*nwave+wave] = 0.0;
746  pOutVar[out*nwave+wave] = 0.0;
747  for (cpl_size reg = 0; reg < n_region; reg++) {
748  pOut[out*nwave+wave] += pReg[reg][wave] * pP2VM[wave][out*n_region+reg];
749  pOutVar[out*nwave+wave] += gravi_pow2 (pErr[reg][wave] * pP2VM[wave][out*n_region+reg]);
750  }
751  } /* End outputs and regions */
752 
753  /* Integration the output flux of the row */
754  for (int tel = 0; tel < 4; tel++) full_flux_tel += pOut[tel*nwave+wave];
755 
756  } /* End loop on wavelengths */
757 
758  /* Set FLUX (wrap is the fastest to create an array) */
759  for (int tel = 0; tel < ntel; tel++){
760  double * data = cpl_malloc (nwave * sizeof(double));
761  for (cpl_size wave = 0 ; wave < nwave ; wave++ )
762  data[wave] = pOut[tel*nwave+wave];
763  tFlux[row*ntel+tel] = cpl_array_wrap_double (data, nwave);
764  }
765 
766  /* Set FLUXERR */
767  for (int tel = 0; tel < ntel; tel++){
768  double * data = cpl_malloc (nwave * sizeof(double));
769  for (cpl_size wave = 0 ; wave < nwave ; wave++ )
770  data[wave] = sqrt (pOutVar[tel*nwave+wave]);
771  tFluxErr[row*ntel+tel] = cpl_array_wrap_double (data, nwave);
772  }
773 
774  /* Set VISDATA */
775  for (int base = 0; base < nbase; base++){
776  double complex * data = cpl_malloc (nwave * sizeof(double complex));
777  for (cpl_size wave = 0 ; wave < nwave ; wave++ )
778  data[wave] = (double complex)( pOut[(base+4)*nwave+wave] + 1.*I * pOut[(base+10)*nwave+wave] );
779  tVis[row*nbase+base] = cpl_array_wrap_double_complex (data, nwave);
780  }
781 
782  /* Set VISDATAERR */
783  for (int base = 0; base < nbase; base++){
784  double complex * data = cpl_malloc (nwave * sizeof(double complex));
785  for (cpl_size wave = 0 ; wave < nwave ; wave++ )
786  data[wave] = (double complex)( sqrt(pOutVar[(base+4)*nwave+wave]) + 1.*I * sqrt(pOutVar[(base+10)*nwave+wave]) );
787  tVisErr[row*nbase+base] = cpl_array_wrap_double_complex (data, nwave);
788  }
789 
790  /* Free the PREPROC data to save memory
791  * (ex: max pointer 34065038 versus 48947400) */
792  for (cpl_size reg = 0; reg < n_region; reg++) {
793  FREE (cpl_array_delete, pRegArr[reg][row]);
794  FREE (cpl_array_delete, pErrArr[reg][row]);
795  }
796 
797 
798  }/* End loop on frames */
799 
800  /* Deallocation of variables */
801  cpl_msg_debug (cpl_func, "Free pointers to data");
802  FREE (cpl_free, pReg);
803  FREE (cpl_free, pErr);
804  FREE (cpl_free, pRegArr);
805  FREE (cpl_free, pErrArr);
806  FREE (cpl_free, pOut);
807  FREE (cpl_free, pOutVar);
808  FREE (cpl_free, pP2VM);
809  FREELOOP (cpl_matrix_delete, p2vm, nwave);
810 
811  /* Check how "flux conservative" is the P2VM */
812  cpl_msg_info (cpl_func, "Total flux in TELs: %.2f [e], in REGIONs:%.2f [e] (ratio=%.5f)",
813  full_flux_tel,full_flux_reg,full_flux_tel/full_flux_reg);
814 
815  sprintf (qc_name, "ESO QC TRANS P2VM %s",GRAVI_TYPE(type_data));
816  cpl_propertylist_update_double (p2vmred_header, qc_name, full_flux_tel/full_flux_reg);
817  cpl_propertylist_set_comment (p2vmred_header, qc_name, "[e/e] at P2VM extraction");
818 
819  /* Count time */
820  cpl_msg_info (cpl_func, "Total time to apply matrix: %.4f s", (double)(clock()-timer_start)/(double)CLOCKS_PER_SEC );
821 
822  if( type_data==GRAVI_FT )
823  timer_ft += (clock() - timer_start);
824  else
825  timer_sc += (clock() - timer_start);
826  timer_start = clock();
827  }
828  /* End loop on polarisation */
829  }
830  /* Loop on data type SC / FT */
831 
832  /*
833  * Add QC for lambda met wavelength
834  */
835  if (gravi_data_has_extension(preproc_data, GRAVI_METROLOGY_EXT)){
836  double lambda_met = gravi_pfits_get_met_wavelength_mean(preproc_header, gravi_data_get_table(preproc_data, GRAVI_METROLOGY_EXT));
837  sprintf (qc_name, "ESO QC MET LAMBDA MEAN");
838  cpl_propertylist_update_double (p2vmred_header, qc_name, lambda_met);
839  cpl_propertylist_set_comment (p2vmred_header, qc_name, "[m] Effective mean metrology wavelength");
840  }
841 
842 
843  /*
844  * Print timing
845  */
846  timer_other += (clock() - timer_start);
847  cpl_msg_info (cpl_func, "Total time for FT: %10.4f s", (double)(timer_ft)/(double)CLOCKS_PER_SEC);
848  cpl_msg_info (cpl_func, "Total time for SC: %10.4f s", (double)(timer_sc)/(double)CLOCKS_PER_SEC);
849  cpl_msg_info (cpl_func, "Total time for OTHER: %7.4f s", (double)(timer_other)/(double)CLOCKS_PER_SEC);
850 
851  /* Message and timer */
852  gravi_msg_function_exit(1);
853  return p2vmred_data;
854 }
855 
856 /*----------------------------------------------------------------------------*/
868 /*----------------------------------------------------------------------------*/
869 
870 cpl_error_code gravi_compute_opdc_state (gravi_data * p2vmred_data)
871 {
872  /* Message and timer */
873  gravi_msg_function_start(1);
874  cpl_ensure_code (p2vmred_data, CPL_ERROR_NULL_INPUT);
875 
876  int nbase = 6, ntel = 4;
877  char qc_name[90];
878 
879  /* Get necessary data */
880  cpl_propertylist * header = gravi_data_get_header (p2vmred_data);
881  int npol_ft = gravi_pfits_get_pola_num (header, GRAVI_FT);
882  cpl_table * oi_vis = gravi_data_get_oi_vis (p2vmred_data, GRAVI_FT, 0, npol_ft);
883  cpl_table * oi_flux = gravi_data_get_oi_flux (p2vmred_data, GRAVI_FT, 0, npol_ft);
884  cpl_table * opdc = gravi_data_get_table (p2vmred_data, GRAVI_OPDC_EXT);
885  cpl_size nrow_ft = cpl_table_get_nrow (oi_vis) / nbase;
886  cpl_size nrow_opdc = cpl_table_get_nrow (opdc);
887  CPLCHECK_MSG ("Cannot get data");
888 
889  /* Create the target phase for each baseline in the OI_VIS table */
890  gravi_table_new_column (oi_vis, "TARGET_PHASE", "rad", CPL_TYPE_DOUBLE);
891 
892  /* Create the OPDC state for each telescope in the OI_FLUX table */
893  cpl_table_new_column (oi_flux, "STATE", CPL_TYPE_INT);
894  cpl_table_fill_column_window (oi_flux, "STATE", 0, nrow_ft * ntel, -1);
895 
896  /* Create the OPDC state for each baseline in the OI_VIS table */
897  cpl_table_new_column (oi_vis, "STATE", CPL_TYPE_INT);
898  cpl_table_fill_column_window (oi_vis, "STATE", 0, nrow_ft * nbase, -1);
899 
900  /* Create the Global OPDC state in the OI_VIS table */
901  cpl_table_new_column (oi_vis, "OPDC_STATE", CPL_TYPE_INT);
902  cpl_table_fill_column_window (oi_vis, "OPDC_STATE", 0, nrow_ft * nbase, -1);
903 
904  if (nrow_opdc < nrow_ft)
905  cpl_msg_warning (cpl_func,"Missing FT or OPDC data: nrow_ft - nrow_opdc = %lli", nrow_ft-nrow_opdc);
906 
907 
908  /* Set the global OPDC state */
909  int * time_opdc = cpl_table_get_data_int (opdc, "TIME");
910  double * time_ft = cpl_table_get_data_double (oi_vis, "TIME");
911  int * global_state_opdc = cpl_table_get_data_int (opdc, "STATE");
912  int * global_state = cpl_table_get_data_int (oi_vis, "OPDC_STATE");
913  CPLCHECK_MSG ("Cannot get data");
914 
915  /* Loop on FT rows */
916  for (cpl_size row_opdc=0, row_ft=0 ; row_ft<nrow_ft ; row_ft++) {
917 
918  /* Check bounds or find the OPDC sample just following the current FT
919  * FIXME: We should use the closesd OPDC sample in the past, not future */
920  if ( (time_ft[row_ft*nbase] < time_opdc[0]) || (time_ft[row_ft*nbase] > time_opdc[nrow_opdc-1]) ) continue;
921  while ( time_ft[row_ft*nbase] > time_opdc[row_opdc] ) row_opdc ++;
922 
923  /* Set the global OPDC state */
924  for (int base = 0; base < nbase; base++)
925  global_state[row_ft*nbase+base] = global_state_opdc[row_opdc];
926  }
927 
928  /* BASELINE_STATE was not in the original data of the instrument */
929  if ( cpl_table_has_column (opdc,"BASELINE_STATE") ) {
930 
931  int * steps_opdc = cpl_table_get_data_int (opdc, "STEPS");
932  int * state_opdc = cpl_table_get_data_int (opdc, "BASELINE_STATE");
933  int * base_state = cpl_table_get_data_int (oi_vis, "STATE");
934  int * tel_state = cpl_table_get_data_int (oi_flux, "STATE");
935  double * base_steps = cpl_table_get_data_double (oi_vis, "TARGET_PHASE");
936  CPLCHECK_MSG ("Cannot get data");
937 
938  /* Loop on FT rows */
939  for (cpl_size row_opdc=0, row_ft=0 ; row_ft<nrow_ft ; row_ft++) {
940 
941  /* Check bounds or find the OPDC sample just following the current FT
942  * FIXME: We should use the closesd OPDC sample in the past, not future */
943  if ( (time_ft[row_ft*nbase] < time_opdc[0]) || (time_ft[row_ft*nbase] > time_opdc[nrow_opdc-1]) ) continue;
944  while ( time_ft[row_ft*nbase] > time_opdc[row_opdc] ) row_opdc ++;
945 
946  /* Get the flag */
947  int state_flag = state_opdc[row_opdc];
948 
949  /* Disentangle the state of each telescope:
950  * The 4 beams are the first 4 bits */
951  for (int tel = 0; tel < ntel; tel++)
952  tel_state[row_ft*ntel+tel] = gravi_bit_get (state_flag, tel);
953 
954  /* Disentangle the state of each baseline:
955  * The 6 baselines are the bits from 5 to 10 */
956  for (int base = 0; base < nbase; base++)
957  base_state[row_ft*nbase+base] = gravi_bit_get (state_flag, ntel+base);
958 
959  /* Bit 11 (>>10) is the Kalman state */
960  // kalman = gravi_bit_get (state_opdc[row_opdc], ntel+nbase);
961 
962  /* Remaing bits are the off-load system */
963  // offload = state_opdc[row_opdc] >> 11;
964 
965  /* Use the closing triangles to recover the tracking on some baselines */
966  for (cpl_size base = 0; base < nbase; base++) {
967  base_state[row_ft*nbase+base] = CPL_MAX( base_state[row_ft*nbase+base],
968  base_state[row_ft*nbase+GRAVI_TRI_BASE[base][0][0]] *
969  base_state[row_ft*nbase+GRAVI_TRI_BASE[base][0][1]] );
970  base_state[row_ft*nbase+base] = CPL_MAX( base_state[row_ft*nbase+base],
971  base_state[row_ft*nbase+GRAVI_TRI_BASE[base][1][0]] *
972  base_state[row_ft*nbase+GRAVI_TRI_BASE[base][1][1]] );
973  }
974 
975  /* Disentangle the piezo steps, in units of [pi/8 rad]
976  * Each beam is coded over 4 bits (16 values over the circle)
977  * *BUT* the last telescope is coded in wrong memory place !
978  * beam0 = 0-3, beam1 = 4-7, beam2 = 8-11, beam3 = 16-19 */
979  int tmp_arr[4];
980  for (int tel = 0; tel < ntel; tel++) {
981  int pos = (tel<3) ? 4*tel : 4*(tel+1);
982  tmp_arr[tel] = 15 & ((steps_opdc[row_opdc]) >> pos);
983  }
984 
985  /* Compute the FT target phase of each baseline, in [rad] */
986  for (int base=0; base<nbase; base++) {
987  base_steps[row_ft*nbase+base] = (tmp_arr[GRAVI_BASE_TEL[base][1]] - tmp_arr[GRAVI_BASE_TEL[base][0]]) * CPL_MATH_PI / 8.0;
988  }
989 
990  } /* End loop on FT rows */
991 
992  } else {
993  cpl_msg_warning (cpl_func,"No column BASELINE_STATE in OPDC... old data ?");
994  cpl_msg_warning (cpl_func,"The STATE flags are set to 1 (valid) although the information is unavailable");
995  cpl_table_fill_column_window (oi_flux, "STATE", 0, nrow_ft * ntel, 1);
996  cpl_table_fill_column_window (oi_vis, "STATE", 0, nrow_ft * nbase, 1);
997  }
998 
999  /* Duplicate in the second polarisation if any */
1000  if ( npol_ft>1 ) {
1001  cpl_table * tmp;
1002  cpl_msg_debug (cpl_func, "Duplicate the FT tracking state for 2sd polarisation");
1003 
1004  tmp = gravi_data_get_oi_vis (p2vmred_data, GRAVI_FT, 1, npol_ft);
1005  cpl_table_duplicate_column (tmp, "TARGET_PHASE", oi_vis, "TARGET_PHASE");
1006  cpl_table_duplicate_column (tmp, "STATE", oi_vis, "STATE");
1007  cpl_table_duplicate_column (tmp, "OPDC_STATE", oi_vis, "OPDC_STATE");
1008 
1009  tmp = gravi_data_get_oi_flux (p2vmred_data, GRAVI_FT, 1, npol_ft);
1010  cpl_table_duplicate_column (tmp, "STATE", oi_flux, "STATE");
1011  }
1012 
1013 
1014  /*
1015  * QC: Compute the phase RMS per base when in tracking state
1016  */
1017 
1018  cpl_array **visdata = cpl_table_get_data_array (oi_vis,"VISDATA");
1019  int * state = cpl_table_get_data_int (oi_vis,"STATE");
1020  double * target_phase = cpl_table_get_data_double (oi_vis,"TARGET_PHASE");
1021  CPLCHECK_MSG ("Cannot get data");
1022 
1023  double complex * tmp_cpx = cpl_malloc (sizeof(double complex)*nrow_ft);
1024 
1025  /* Loop on base */
1026  for (int base = 0; base < nbase; base ++) {
1027 
1028  /* Compute real-time phase and its mean (as phasors) */
1029  double complex mean_cpx = 0.0 + I * 0.0;
1030  for (cpl_size row_ft=0 ; row_ft<nrow_ft ; row_ft++) {
1031  if (state[row_ft*nbase+base] < 1) continue;
1032  tmp_cpx[row_ft] = cpl_array_get_mean_complex (visdata[row_ft*nbase+base]) * cexp (-I*target_phase[row_ft*nbase+base]);
1033  mean_cpx += tmp_cpx[row_ft];
1034  }
1035 
1036  /* Compute <phi**2> and the number of valid point */
1037  double sum = 0.0000001, sum2 = 0.0;
1038  for (cpl_size row_ft=0 ; row_ft<nrow_ft ; row_ft++) {
1039  if (state[row_ft*nbase+base] < 1) continue;
1040  sum2 += pow (carg (tmp_cpx[row_ft] * conj(mean_cpx)), 2);
1041  sum += 1.0;
1042  }
1043 
1044  /* Write the QC parameter for this base */
1045  sprintf (qc_name, "ESO QC PHASE_FT%d%d RMS",GRAVI_BASE_TEL[base][0]+1, GRAVI_BASE_TEL[base][1]+1);
1046  cpl_propertylist_update_double (header, qc_name, sqrt (sum2 / sum));
1047  cpl_propertylist_set_comment (header, qc_name, "[rad] residuals when tracking");
1048 
1049  /* Write the QC parameter for this base */
1050  sprintf (qc_name, "ESO QC TRACKING_RATIO_FT%d%d",GRAVI_BASE_TEL[base][0]+1, GRAVI_BASE_TEL[base][1]+1);
1051  cpl_propertylist_update_double (header, qc_name, sum*100.0/nrow_ft);
1052  cpl_propertylist_set_comment (header, qc_name, "[%] ratio of time with tracking");
1053  }
1054  /* End loop on base */
1055 
1056  FREE (cpl_free,tmp_cpx);
1057 
1058  /*
1059  * QC: Compute fraction of time traking
1060  */
1061 
1062  int* tab_state = cpl_table_get_data_int (gravi_data_get_table (p2vmred_data, GRAVI_OPDC_EXT), "STATE");
1063  CPLCHECK_MSG ("Cannot get data");
1064 
1065  long tracking = 0;
1066  for (cpl_size row_opdc=0; row_opdc < nrow_opdc; row_opdc++) {
1067  if (tab_state[row_opdc] == 3 || tab_state[row_opdc] == 2) tracking ++;
1068  }
1069 
1070  sprintf (qc_name, "ESO QC TRACKING_RATIO");
1071  cpl_propertylist_update_int (header, qc_name, tracking*100/nrow_ft);
1072  cpl_propertylist_set_comment (header, qc_name, "[%] ratio of time with full FT tracking");
1073 
1074  CPLCHECK_MSG ("Cannot put QC parameters");
1075 
1076  /* Message and timer */
1077  gravi_msg_function_exit(1);
1078  return CPL_ERROR_NONE;
1079 }
1080 
1081 
1082 /*----------------------------------------------------------------------------*/
1092 /*----------------------------------------------------------------------------*/
1093 
1094 cpl_error_code gravi_compute_tau0 (gravi_data * data)
1095 {
1096  /* verbose */
1097  gravi_msg_function_start(1);
1098  cpl_ensure_code (data, CPL_ERROR_NULL_INPUT);
1099 
1100  int nbase = 6;
1101  double gain = 16.8; // [rad/V]
1102  char qc_name[90];
1103 
1104  /* Get the OPDC table */
1105  cpl_propertylist * header = gravi_data_get_header (data);
1106  cpl_table * opdc = gravi_data_get_table (data, GRAVI_OPDC_EXT);
1107  cpl_size nrow = cpl_table_get_nrow (opdc);
1108 
1109  CPLCHECK_MSG("Cannot load the OPDC table");
1110 
1111  /* Compute the delays to explore */
1112  cpl_size max_delay = 200;
1113 
1114  /* Time in [usec] and offset in [V] */
1115  int *time = cpl_table_get_data_int (opdc, "TIME");
1116  CPLCHECK_MSG("Cannot load the TIME column");
1117 
1118  float ** piezo = gravi_table_get_data_array_float (opdc, "PIEZO_DL_OFFSET");
1119  CPLCHECK_MSG("Cannot load the PIEZO_OFFSET columns");
1120 
1121  /* Loop on base */
1122  for (int base=0; base<nbase; base++) {
1123  int t1 = GRAVI_BASE_TEL[base][0];
1124  int t2 = GRAVI_BASE_TEL[base][1];
1125 
1126  /* Init the QC parameters */
1127  sprintf (qc_name, "ESO QC TAU0 OPDC%d%d", t1+1,t2+1);
1128  cpl_propertylist_update_double (header, qc_name, GRAVI_NAN_DOUBLE);
1129  cpl_propertylist_set_comment (header, qc_name, "[s] tau0 for variance of 1 rad2");
1130 
1131 
1132  /* Loop on delays */
1133  for (cpl_size delay=0; delay < max_delay; delay++) {
1134 
1135  /* srtf(d) = < (opd(t) - opd(t+d))^2> in [rad^2] */
1136  double strf = 0.0;
1137  for (cpl_size row=0; row < nrow - max_delay - 1 ; row++) {
1138  float diff = (piezo[row][t1]-piezo[row][t2]) - (piezo[row+delay][t1]-piezo[row+delay][t2]);
1139  strf += (double)(diff * diff);
1140  }
1141  strf = strf * gain * gain / (nrow - max_delay - 1);
1142 
1143  /* If the variance is larger than 1rad2,
1144  * we found the tau0 */
1145  if ( strf > 1.0 ) {
1146  double tau0 = (time[delay+1] - time[1]) * 1e-6;
1147  cpl_msg_info (cpl_func,"Compute %s = %.2f [ms]", qc_name, 1e3 * tau0);
1148  cpl_propertylist_update_double (header, qc_name, tau0);
1149  break;
1150  }
1151 
1152  } /* End loop on delay */
1153 
1154  } /* End loop on base */
1155 
1156  /* Free the list of arrays */
1157  FREE (cpl_free, piezo);
1158 
1159  gravi_msg_function_exit(1);
1160  return CPL_ERROR_NONE;
1161 }
1162 
1163 /*----------------------------------------------------------------------------*/
1173 /*----------------------------------------------------------------------------*/
1174 
1175 cpl_error_code gravi_compute_qc_injection (gravi_data * data)
1176 {
1177  gravi_msg_function_start(1);
1178  cpl_ensure_code (data, CPL_ERROR_NULL_INPUT);
1179 
1180  char qc_name[100];
1181  double p05 = 0.0, p95 = 0.0;
1182 
1183  cpl_propertylist * header = gravi_data_get_header (data);
1184  int npol = gravi_pfits_get_pola_num (header, GRAVI_FT);
1185  int ntel = 4, hw = 2;
1186 
1187  /* Create the kernel to smooth the flux series */
1188  cpl_msg_info (cpl_func, "Smooth flux sery over %i samples", hw);
1189 
1190  /* How many rows do we have per telescope */
1191  cpl_table * table = gravi_data_get_oi_flux (data, GRAVI_FT, 0, npol);
1192  cpl_size nrow = cpl_table_get_nrow (table) / ntel;
1193 
1194 
1195  /* For each telescope */
1196  for (int tel = 0; tel<ntel; tel++) {
1197 
1198  /* Build a vector of total flux */
1199  cpl_vector * raw_flux = cpl_vector_new (nrow);
1200  cpl_vector_fill (raw_flux, 0.0);
1201 
1202  for (int pol = 0; pol<npol; pol++) {
1203 
1204  /* Get table */
1205  cpl_table * table = gravi_data_get_oi_flux (data, GRAVI_FT, pol, npol);
1206  const cpl_array ** flux_array = cpl_table_get_data_array_const (table, "FLUX");
1207 
1208  for (int row = 0; row<nrow; row++)
1209  cpl_vector_set (raw_flux, row, cpl_vector_get (raw_flux, row) +
1210  cpl_array_get_mean (flux_array[tel+row*ntel]));
1211  }
1212 
1213  /* Convolve to filter noise, delete raw flux */
1214  cpl_vector * flux;
1215  flux = cpl_vector_filter_lowpass_create (raw_flux, CPL_LOWPASS_GAUSSIAN, hw);
1216  cpl_vector_delete (raw_flux);
1217 
1218  /* Define positive */
1219  for (int row = 0; row<nrow; row++)
1220  cpl_vector_set (flux, row, CPL_MAX (0.0, cpl_vector_get (flux, row)));
1221 
1222  /* Sort the flux array and normalise */
1223  cpl_vector_sort (flux, CPL_SORT_ASCENDING);
1224  cpl_vector_divide_scalar (flux, cpl_vector_get_max (flux) / 100);
1225 
1226  /* Histogram as a string, values are 0-100 */
1227  char qc_value[100];
1228  sprintf (qc_value,"%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f",
1229  cpl_vector_get (flux, (cpl_size)(0.1*(nrow-1))),
1230  cpl_vector_get (flux, (cpl_size)(0.2*(nrow-1))),
1231  cpl_vector_get (flux, (cpl_size)(0.3*(nrow-1))),
1232  cpl_vector_get (flux, (cpl_size)(0.4*(nrow-1))),
1233  cpl_vector_get (flux, (cpl_size)(0.5*(nrow-1))),
1234  cpl_vector_get (flux, (cpl_size)(0.6*(nrow-1))),
1235  cpl_vector_get (flux, (cpl_size)(0.7*(nrow-1))),
1236  cpl_vector_get (flux, (cpl_size)(0.8*(nrow-1))),
1237  cpl_vector_get (flux, (cpl_size)(0.9*(nrow-1))));
1238 
1239  /* Create the QC entry in the FITS header */
1240  cpl_msg_info (cpl_func, "Flux Histo: %s", qc_value);
1241  sprintf (qc_name, "ESO QC FLUX_FT%d HISTO", tel+1);
1242  cpl_propertylist_update_string (header, qc_name, qc_value);
1243  cpl_propertylist_set_comment (header, qc_name, "decile in percent");
1244 
1245  /* Compute the 5 percentile and 95 percentile */
1246  p05 = cpl_vector_get (flux, (cpl_size)(0.05*(nrow-1)));
1247  p95 = cpl_vector_get (flux, (cpl_size)(0.95*(nrow-1)));
1248 
1249  /* Create the QC entry in the FITS header */
1250  sprintf (qc_name, "ESO QC FLUX_FT%d P05P95", tel+1);
1251  cpl_propertylist_update_double (header, qc_name, p05/p95);
1252  cpl_propertylist_set_comment (header, qc_name, "injected flux 5 percentile over 95 percentile");
1253 
1254  /* Deleve vector */
1255  cpl_vector_delete (flux);
1256  }
1257 
1258  gravi_msg_function_exit(1);
1259  return CPL_ERROR_NONE;
1260 }
1261 
1262 
1263 /*----------------------------------------------------------------------------*/
1273 /*----------------------------------------------------------------------------*/
1274 
1275 cpl_error_code gravi_compute_qc_ft_opd_estimator (gravi_data * p2vmred_data)
1276 {
1277  gravi_msg_function_start(1);
1278  cpl_ensure_code (p2vmred_data, CPL_ERROR_NULL_INPUT);
1279 
1280  int nbase = 6;
1281  char qc_name[100];
1282  int nv;
1283 
1284  /* Get necessary data */
1285  cpl_propertylist * header = gravi_data_get_header (p2vmred_data);
1286  int npol = gravi_pfits_get_pola_num (header, GRAVI_FT);
1287  cpl_table * opdc = gravi_data_get_table (p2vmred_data, GRAVI_OPDC_EXT);
1288  int * time_opdc = cpl_table_get_data_int (opdc, "TIME");
1289  if (cpl_table_has_column(opdc, "OPD") == 1){
1290  cpl_array **opd_data_opdc = cpl_table_get_data_array (opdc,"OPD");
1291  cpl_size nrow = cpl_table_get_nrow (opdc);
1292  CPLCHECK_MSG ("Cannot get data");
1293 
1294  /* initialize arrays */
1295  cpl_array * opd_res_array = cpl_array_new(nrow, CPL_TYPE_DOUBLE_COMPLEX);
1296  cpl_array * opd_array = cpl_array_new(nrow, CPL_TYPE_DOUBLE);
1297  cpl_array * snr_array = cpl_array_new(nrow, CPL_TYPE_DOUBLE);
1298 
1299  /* do it for both polarisations (if needed) */
1300  for (int pol = 0; pol<npol; pol++) {
1301  cpl_table * oi_vis = gravi_data_get_oi_vis (p2vmred_data, GRAVI_FT, pol, npol);
1302  double * time_oivis = cpl_table_get_data_double (oi_vis, "TIME");
1303  cpl_array **visdata = cpl_table_get_data_array (oi_vis,"VISDATA");
1304  cpl_array **visdata_err = cpl_table_get_data_array (oi_vis,"VISERR");
1305 
1306  cpl_size nrow_oivis = cpl_table_get_nrow (oi_vis);
1307  cpl_size nwave= cpl_array_get_size (visdata[0]);
1308 
1309 
1310  /* For each baseline */
1311  for (int base = 0; base<nbase; base++) {
1312  cpl_size row_oivis = base;
1313  /* For each row */
1314  for (cpl_size row = 0; row<nrow; row++)
1315  {
1316 
1317  /* FIXME: get rid of the 0.0011/2 (half step) */
1318  while (( fabs (time_oivis[row_oivis+nbase]-time_opdc[row]+0.0011/2*1e6 )< fabs (time_oivis[row_oivis]-time_opdc[row]+0.0011/2*1e6 ) ) && (row_oivis<nrow_oivis-2*nbase))
1319  {
1320  row_oivis+=nbase;
1321  }
1322 
1323  double complex visdata_mean=cpl_array_get_mean_complex (visdata[row_oivis]);
1324  cpl_array_set_double_complex(opd_res_array,row, visdata_mean*cexp ( I * cpl_array_get_float (opd_data_opdc[row], base, &nv) ));
1325  cpl_array_set_double(opd_array, row, carg(visdata_mean));
1326 
1327 
1328  /* get SNR */
1329  double signal =0.0;
1330  double noise =0.0;
1331  for (cpl_size wave = 0 ; wave < nwave ; wave ++)
1332  {
1333  signal+=cabs(cpl_array_get_double_complex (visdata[row_oivis],wave, &nv));
1334  double err=cabs(cpl_array_get_double_complex (visdata_err[row_oivis],wave, &nv));
1335  noise+=err*err;
1336  }
1337  noise=sqrt(noise+1);
1338  if (signal > noise)
1339  cpl_array_set_double(snr_array,row, signal /noise -1 );
1340  else
1341  cpl_array_set_double(snr_array,row, 0 );
1342 
1343  }
1344  CPLCHECK_MSG ("Cannot compute new array for cross-referencing OPD and FT OPD");
1345 
1346  double complex opd_mean = cpl_array_get_mean_complex (opd_res_array);
1347  cpl_array_multiply_scalar_complex(opd_res_array,conj(opd_mean));
1348 
1349  double c_o=0,s_o=0;
1350  double a_num=0,b_num=0,c_num=0,d_num=0,a_den=1,b_den=1,c_den=1,d_den=1;
1351  double opd_std_num=0,opd_std_den=1;
1352  double opd, opd_ref, snr_ref;
1353 
1354  for (cpl_size row = 0; row<nrow; row++)
1355  {
1356  opd=cpl_array_get_double(opd_array,row,&nv);
1357  opd_ref=carg(cpl_array_get_double_complex(opd_res_array,row,&nv));
1358  snr_ref=cpl_array_get_double(snr_array,row,&nv);
1359  c_o=cos(opd);
1360  a_num+=snr_ref*opd_ref*c_o;
1361  a_den+=snr_ref*c_o*c_o;
1362  s_o=sin(opd);
1363  b_num+=snr_ref*opd_ref*s_o;
1364  b_den+=snr_ref*s_o*s_o;
1365  c_o=c_o*c_o;
1366  c_num+=snr_ref*opd_ref*c_o;
1367  c_den+=snr_ref*c_o*c_o;
1368  s_o=s_o*s_o;
1369  d_num+=snr_ref*opd_ref*s_o;
1370  d_den+=snr_ref*s_o*s_o;
1371 
1372  /* calculate the standard deviation: numerator and denominator*/
1373  opd_std_num+=snr_ref*opd_ref*snr_ref*opd_ref;
1374  opd_std_den+=snr_ref;
1375 
1376  }
1377 
1378  CPLCHECK_MSG ("Cannot calculate variance of linearity");
1379 
1380  double a=a_num/a_den;
1381  double b=b_num/b_den;
1382  double c=c_num/c_den;
1383  double d=d_num/d_den;
1384  /* calculate standard deviation */
1385  double opd_std=sqrt(0.1+opd_std_num)/opd_std_den;
1386 
1387  /* calculate the average standard error of cos, sin, sin2 and cos2 */
1388  double res_car=a*a+b*b+c*c+d*d-16*opd_std*opd_std;
1389  if (res_car > 0) res_car=sqrt(res_car);
1390  else res_car=0;
1391 
1392  /* Create the QC entry in the FITS header*/
1393  if (npol == 2) sprintf (qc_name, "ESO QC LIN_FT P%d_B%d", pol,base+1);
1394  if (npol == 1) sprintf (qc_name, "ESO QC LIN_FT P%d_B%d", 3,base+1);
1395  cpl_propertylist_update_double (header, qc_name, res_car);
1396  cpl_propertylist_set_comment (header, qc_name, "FT nonlinearity biases [rad]");
1397 
1398  CPLCHECK_MSG ("Cannot store QCs");
1399 
1400  }
1401  }
1402 
1403  cpl_array_delete (opd_array);
1404  cpl_array_delete (opd_res_array);
1405  cpl_array_delete (snr_array);
1406  CPLCHECK_MSG ("Cannot close arrays");
1407  }
1408  else{
1409  cpl_msg_info(cpl_func, "Cannot found the OPD column (probably old data). Skip computation of QC LIN_FT");
1410  }
1411 
1412  gravi_msg_function_exit(1);
1413  return CPL_ERROR_NONE;
1414 }
1415 
1416 
cpl_error_code gravi_compute_opdc_state(gravi_data *p2vmred_data)
Compute the real-time tracking state from OPDC.
gravi_data * gravi_data_new(int nb_ext)
Create an empty gravi_data.
Definition: gravi_data.c:102
short gravi_sta_index(int gravi_input, cpl_table *optical_train_table, cpl_table *array_geometry_table)
Retrieve STA_INDEX corresponding to a given input.
Definition: gravi_utils.c:828
int gravi_region_get_pol(cpl_table *imaging_detector, int region)
Return the polarisation id of a region.
Definition: gravi_utils.c:445
cpl_error_code gravi_data_copy_ext(gravi_data *output, gravi_data *input, const char *name)
Copy extensions from one data to another.
Definition: gravi_data.c:1325
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:1443
cpl_table * gravi_create_oiarray_table(const cpl_table *array_geometry, int is_cal)
Create the OI_ARRAY table from the ARRAY_GEOMETRY.
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:1716
cpl_matrix * gravi_matrix_invertSV_create(cpl_matrix *a_in)
Invers a matrix with singular value decomposition.
Definition: gravi_cpl.c:2486
gravi_data * gravi_compute_p2vmred(gravi_data *preproc_data, gravi_data *p2vm_map, const char *mode, const cpl_parameterlist *parlist, enum gravi_detector_type det_type)
Converts preprocessed data into coherent fluxes using the P2VM.
cpl_error_code gravi_table_set_string_fixlen(cpl_table *table, const char *name, int row, const char *value, int len)
Set string in table, ensuring fixed length (right space padding). see cpl_table_set_string.
Definition: gravi_cpl.c:605
cpl_error_code gravi_compute_tau0(gravi_data *data)
Compute the QC TAU0 parameter.
cpl_error_code gravi_compute_qc_injection(gravi_data *data)
Compute the QC for the injection stability.
cpl_error_code gravi_compute_qc_ft_opd_estimator(gravi_data *p2vmred_data)
Compute the QC for the FT linearity.
cpl_table * gravi_create_oitarget_table(const cpl_propertylist *header, const char *mode)
Create the OI_TARGET table from the main header.
Definition: gravi_p2vmred.c:90
cpl_propertylist * gravi_data_get_plist(gravi_data *self, const char *extname)
Get the propertylist from EXTNAME.
Definition: gravi_data.c:1684
cpl_error_code gravi_data_add_table(gravi_data *self, cpl_propertylist *plist, const char *extname, cpl_table *table)
Add a BINTABLE extension in gravi_data.
Definition: gravi_data.c:1909
cpl_table * gravi_table_oi_create(int nwave, int nrow, const char *oi_name)
Create the oi table (oi_vis, oi_vis2, oi_t3)
Definition: gravi_utils.c:153