GRAVI Pipeline Reference Manual  1.2.3
gravi_eop.c
1 /*
2  * This file is part of the GRAVI Pipeline
3  * Copyright (C) 2002,2003 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 
34 /*-----------------------------------------------------------------------------
35  Includes
36  -----------------------------------------------------------------------------*/
37 
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41 
42 #include <stdlib.h>
43 #include <string.h>
44 #include <stdio.h>
45 #include <time.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <netdb.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 
53 #include <erfa.h>
54 
55 #include "gravi_eop.h"
56 #include "gravi_cpl.h"
57 #include "gravi_pfits.h"
58 
59 #include "gravi_utils.h"
60 
61 /*-----------------------------------------------------------------------------
62  Private prototypes and define
63  -----------------------------------------------------------------------------*/
64 
65 #define LINE_SIZE 188
66 #define BUFFER_LENGTH 32768
67 
68 char * gravity_eop_get_ftp_file (int socketfd, int * data_length);
69 int gravity_get_socket_connection (const char * host, const char * port);
70 int gravity_eop_send_ftpcmd (int sockfd, const char *cmd);
71 int gravity_eop_send_pasv (int sockfd, const char *cmd);
72 int gravity_eop_ftp_reply (int sockfd, char ** message);
73 int gravity_eop_verify_ftp_code (char * msg, int length);
74 
75 cpl_error_code gravi_eop_interpolate (cpl_size n, double *mjd,
76  double *pmx, double *pmy,
77  double *dut,
78  cpl_table * eop_table,
79  cpl_propertylist * header);
80 
81 void eraAtboq (double rc, double dc, eraASTROM *astrom, double enuob[3]);
82 void eraAtcoq (double rc, double dc, double pmr, double pmd, double px,
83  double rv, eraASTROM *astrom, double enuob[3]);
84 void dtp2s (double xi, double eta, double raz,
85  double decz, double *ra, double *dec);
86 void rotate_vector (double in[3], double angle, double axis[3], double out[3]);
87 void difference (double x[3], double y[3], double z[3]);
88 void multiply (double xyz[3], double factor);
89 void normalize (double xyz[3]);
90 void cross (double x[3], double y[3], double z[3]);
91 
92 /*-----------------------------------------------------------------------------
93  Functions code
94  -----------------------------------------------------------------------------*/
95 
96 /*
97  * Private function to interpolate EOP parameter from EOP_PARAM
98  * and manipulate coordinates and 3D vectors
99  */
100 
101 cpl_error_code gravi_eop_interpolate (cpl_size n, double *mjd,
102  double *pmx, double *pmy,
103  double *dut,
104  cpl_table * eop_table,
105  cpl_propertylist * header)
106 {
107  gravi_msg_function_start(1);
108  cpl_ensure_code (eop_table, CPL_ERROR_NULL_INPUT);
109  cpl_ensure_code (header, CPL_ERROR_NULL_INPUT);
110  cpl_ensure_code (n>0, CPL_ERROR_ILLEGAL_INPUT);
111  cpl_ensure_code (mjd, CPL_ERROR_NULL_INPUT);
112  cpl_ensure_code (pmx, CPL_ERROR_NULL_INPUT);
113  cpl_ensure_code (pmy, CPL_ERROR_NULL_INPUT);
114  cpl_ensure_code (dut, CPL_ERROR_NULL_INPUT);
115 
116  /* Wrap inputs in vectors */
117  cpl_vector *vmjd = cpl_vector_wrap (n, mjd);
118  cpl_vector *vpmx = cpl_vector_wrap (n, pmx);
119  cpl_vector *vpmy = cpl_vector_wrap (n, pmy);
120  cpl_vector *vdut = cpl_vector_wrap (n, dut);
121 
122  /* Check validity of input mjd with table */
123  if (cpl_vector_get_min (vmjd) < cpl_table_get_column_min (eop_table, "MJD") ||
124  cpl_vector_get_max (vmjd) > cpl_table_get_column_max (eop_table, "MJD"))
125  {
126  cpl_msg_warning (cpl_func, "Some MJD are outside the EOP_PARAM range (MJD=%.2f, %.2f..%.2f). Use EOP and DUT=0.0s",
127  cpl_vector_get_mean (vmjd),
128  cpl_table_get_column_min (eop_table, "MJD"),
129  cpl_table_get_column_max (eop_table, "MJD"));
130  cpl_vector_unwrap (vmjd);
131  cpl_vector_unwrap (vpmx);
132  cpl_vector_unwrap (vpmy);
133  cpl_vector_unwrap (vdut);
134  return CPL_ERROR_NONE;
135  }
136 
137  /* Check if within the prediction range */
138  if(cpl_vector_get_max (vmjd) > cpl_propertylist_get_double (header, "ESO QC EOP MJD LAST FINAL"))
139  {
140  cpl_msg_warning (cpl_func, "Some MJD are inside the EOP_PARAM prediction range...");
141  }
142 
143  /* Wrap output vectors */
144  cpl_size nrow = cpl_table_get_nrow (eop_table);
145  cpl_vector * eop_mjd = cpl_vector_wrap (nrow, cpl_table_get_data_double (eop_table, "MJD"));
146  cpl_vector * eop_pmx = cpl_vector_wrap (nrow, cpl_table_get_data_double (eop_table, "PMX"));
147  cpl_vector * eop_pmy = cpl_vector_wrap (nrow, cpl_table_get_data_double (eop_table, "PMY"));
148  cpl_vector * eop_dut = cpl_vector_wrap (nrow, cpl_table_get_data_double (eop_table, "DUT"));
149 
150  /* Perform interpolations */
151  cpl_bivector *mjd_pmx = cpl_bivector_wrap_vectors (vmjd, vpmx);
152  cpl_bivector *eop_mjd_pmx = cpl_bivector_wrap_vectors (eop_mjd, eop_pmx);
153  cpl_bivector_interpolate_linear (mjd_pmx, eop_mjd_pmx);
154  cpl_bivector_unwrap_vectors (mjd_pmx);
155  cpl_bivector_unwrap_vectors (eop_mjd_pmx);
156 
157  cpl_bivector *mjd_pmy = cpl_bivector_wrap_vectors (vmjd, vpmy);
158  cpl_bivector *eop_mjd_pmy = cpl_bivector_wrap_vectors (eop_mjd, eop_pmy);
159  cpl_bivector_interpolate_linear (mjd_pmy, eop_mjd_pmy);
160  cpl_bivector_unwrap_vectors (mjd_pmy);
161  cpl_bivector_unwrap_vectors (eop_mjd_pmy);
162 
163  cpl_bivector *mjd_dut = cpl_bivector_wrap_vectors (vmjd, vdut);
164  cpl_bivector *eop_mjd_dut = cpl_bivector_wrap_vectors (eop_mjd, eop_dut);
165  cpl_bivector_interpolate_linear (mjd_dut, eop_mjd_dut);
166  cpl_bivector_unwrap_vectors (mjd_dut);
167  cpl_bivector_unwrap_vectors (eop_mjd_dut);
168 
169  /* Print diagnostics */
170  cpl_msg_info(cpl_func, "EOP averages: MJD=%.2f DUT1=%.3f PMX=%.3farcsec PMY=%.3farcsec",
171  cpl_vector_get_mean (vmjd), cpl_vector_get_mean (vdut),
172  cpl_vector_get_mean (vpmx), cpl_vector_get_mean (vpmy));
173 
174  /* Unwrap vectors */
175  cpl_vector_unwrap (vmjd);
176  cpl_vector_unwrap (vpmx);
177  cpl_vector_unwrap (vpmy);
178  cpl_vector_unwrap (vdut);
179  cpl_vector_unwrap (eop_mjd);
180  cpl_vector_unwrap (eop_pmx);
181  cpl_vector_unwrap (eop_pmy);
182  cpl_vector_unwrap (eop_dut);
183 
184  CPLCHECK_MSG ("Cannot interpolate EOP");
185 
186  gravi_msg_function_exit(1);
187  return CPL_ERROR_NONE;
188 }
189 
190 /*
191  * Quick transform from BCRS to Observed
192  * rc,dc double ICRS right ascension at J2000.0 (radians)
193  */
194 void eraAtboq (double rc, double dc, eraASTROM *astrom, double enuob[3])
195 {
196  double ri, di;
197  double aob, zob, hob, dob, rob;
198 
199  /* Transform from BCRS to Intermediate */
200  eraAtciq (rc, dc, 0.0, 0.0, 0.0, 0.0, astrom, &ri, &di);
201 
202  /* Transform from Intermediate to Observed. */
203  eraAtioq (ri, di, astrom, &aob, &zob, &hob, &dob, &rob);
204 
205  /* Convert from equatorial to cartesian */
206  eraS2c(CPL_MATH_PI/2.0-aob, CPL_MATH_PI/2.0-zob, enuob);
207 }
208 
209 void rotate_vector (double in[3], double angle, double axis[3], double out[3])
210 {
211  double rv[3];
212  double rm[3][3];
213  eraSxp(angle, axis, rv);
214  eraRv2m(rv, rm);
215  eraRxp(rm, in, out);
216 }
217 
218 /*----------------------------------------------------------------------------*/
240 /*----------------------------------------------------------------------------*/
241 
242 cpl_error_code gravi_eop_pointing_uv (cpl_table * input_table,
243  cpl_propertylist * header,
244  cpl_table * eop_table,
245  cpl_propertylist * eop_header,
246  int save_pointing,
247  cpl_table * array_table)
248 {
249  gravi_msg_function_start(1);
250  cpl_ensure_code (input_table, CPL_ERROR_NULL_INPUT);
251  cpl_ensure_code (header, CPL_ERROR_NULL_INPUT);
252 
253  /* Check and announce the optional parameters */
254  if (save_pointing > 0) {
255  cpl_msg_info (cpl_func, "Will save [E_U,E_V,E_W,E_AZ,E_ZD]");
256  }
257  int compute_uv;
258  if (array_table != NULL) {
259  cpl_msg_info (cpl_func, "Will compute [UCOORD,VCOORD]");
260  compute_uv = 1;
261  } else {
262  compute_uv = 0;
263  }
264 
265  double t_skip = 1./24/3600, mjd0 = -1.0, mjd1 = -1.0;
266  cpl_msg_info (cpl_func, "Compute pointing with full ERFA every %.2f s",t_skip*24*3600.0);
267 
268  /* If requested, loop on bases to compute the physical 3D baseline as T2-T1
269  * Note: The telescope locations STAXYZ are given in the [West,South,Up]
270  * frame (ESO convention), whereas the UVW, calculated later on, are in
271  * the [East,North,Up] frame. Hence the sign changes below on X and Y. */
272  double baseline[6][3];
273  double * uCoord;
274  double * vCoord;
275  if (compute_uv) {
276  for ( int base = 0; base < 6; base ++) {
277  int tel1=0; while ( cpl_table_get (array_table, "STA_INDEX", tel1, NULL) != gravi_table_get_value (input_table, "STA_INDEX", base, 0) ) tel1++;
278  int tel2=0; while ( cpl_table_get (array_table, "STA_INDEX", tel2, NULL) != gravi_table_get_value (input_table, "STA_INDEX", base, 1) ) tel2++;
279  baseline[base][0] = -(gravi_table_get_value (array_table, "STAXYZ", tel2, 0) - gravi_table_get_value (array_table, "STAXYZ", tel1, 0));
280  baseline[base][1] = -(gravi_table_get_value (array_table, "STAXYZ", tel2, 1) - gravi_table_get_value (array_table, "STAXYZ", tel1, 1));
281  baseline[base][2] = +(gravi_table_get_value (array_table, "STAXYZ", tel2, 2) - gravi_table_get_value (array_table, "STAXYZ", tel1, 2));
282  }
283  uCoord = cpl_table_get_data_double (input_table, "UCOORD");
284  vCoord = cpl_table_get_data_double (input_table, "VCOORD");
285  }
286 
287  /* Read UTC as MJD */
288  double *mjd = cpl_table_get_data_double (input_table, "MJD");
289  double mean_mjd = cpl_table_get_column_mean (input_table, "MJD");
290 
291  /* If requested, create [E_U,E_V,E_V,E_AZ,E_ZD] columns */
292  cpl_array ** p_u;
293  cpl_array ** p_v;
294  cpl_array ** p_w;
295  cpl_array ** p_az;
296  cpl_array ** p_zd;
297  if (save_pointing) {
298  cpl_msg_info (cpl_func, "Saving E_U, E_V, E_W, E_AZ, E_ZD");
299  cpl_table_new_column_array (input_table, "E_U", CPL_TYPE_DOUBLE, 3);
300  cpl_table_new_column_array (input_table, "E_V", CPL_TYPE_DOUBLE, 3);
301  cpl_table_new_column_array (input_table, "E_W", CPL_TYPE_DOUBLE, 3);
302  cpl_table_new_column_array (input_table, "E_AZ", CPL_TYPE_DOUBLE, 3);
303  cpl_table_new_column_array (input_table, "E_ZD", CPL_TYPE_DOUBLE, 3);
304  p_u = cpl_table_get_data_array (input_table,"E_U");
305  p_v = cpl_table_get_data_array (input_table,"E_V");
306  p_w = cpl_table_get_data_array (input_table,"E_W");
307  p_az = cpl_table_get_data_array (input_table,"E_AZ");
308  p_zd = cpl_table_get_data_array (input_table,"E_ZD");
309  CPLCHECK_MSG ("Cannot create [E_U, E_V, E_W, E_AZ, E_ZD]");
310  }
311 
312  /* Get the location of the observer from the header */
313  double elev = gravi_pfits_get_geoelev (header); // Height in [m]
314  double lon = gravi_pfits_get_geolon (header) * CPL_MATH_RAD_DEG; // Lat in [rad]
315  double lat = gravi_pfits_get_geolat (header) * CPL_MATH_RAD_DEG; // Lon in [rad], East positive
316  CPLCHECK_MSG ("Cannot get the observer location");
317 
318  /* If EOP are supplied, interpolate to the mean MJD */
319  double dut1 = 0, pmx = 0, pmy = 0;
320  if (eop_table != NULL) {
321  gravi_eop_interpolate (1, &mean_mjd, &pmx, &pmy, &dut1,
322  eop_table, eop_header);
323  CPLCHECK_MSG ("Cannot interpolate");
324  } else {
325  cpl_msg_warning (cpl_func, "No EOP_PARAM. Use EOP and DUT=0.0s");
326  }
327 
328  /* We use the mid-point between FT and SC to compute the UV coordinates */
329  double rc = gravi_pfits_get_mid_raep (header); // [rad]
330  double dc = gravi_pfits_get_mid_decep (header); // [rad]
331  double pmr = gravi_pfits_get_pmra (header) * CPL_MATH_RAD_DEG / 3600.0 / cos(dc); // dRA/dt, not cos(Dec)xdRA/dt [rad/year]
332  double pmd = gravi_pfits_get_pmdec (header) * CPL_MATH_RAD_DEG / 3600.0; // dDec/dt [rad/year]
333  double parallax = gravi_pfits_get_plx (header); // [as]
334  double sysvel = 0.0;
335  CPLCHECK_MSG ("Cannot get the header data");
336 
337  /* Prepare the following loop computations */
338  eraASTROM astrom;
339  double eo;
340  double eps = 10.0 / 3600.0 * CPL_MATH_RAD_DEG; // 10 arcsec has been chosen for optimal accuracy
341  double rb, db;
342  double eUb[3], eVb[3], eWb[3], eZb[3];
343  double eWo_up[3], eWo_um[3], eWo_vp[3], eWo_vm[3];
344  double eWb_up[3], eWb_um[3], eWb_vp[3], eWb_vm[3];
345  double rb_up, db_up, rb_um, db_um, rb_vp, db_vp, rb_vm, db_vm;
346  double eUo[3], eVo[3], eWo[3], eAZo[3], eZDo[3];
347  double ez[3] = {0.0, 0.0, 1.0}; // Zenith direction in ENU frame
348  double norm;
349  double pressure = 0.0; // Pressure at zero to disable atmospheric refraction
350  double temperature = 0.0;
351  double humidity = 0.0;
352  double wavelength = 0.0;
353 
354  /* Loop on rows. The baselines are not assumed to share the
355  * same MJD since this function is called after the averaging */
356  cpl_size n_row = cpl_table_get_nrow (input_table);
357  for (cpl_size row = 0 ; row < n_row ; row++) {
358 
359  /* If the time is strickly the same as previous row, we skip this computation */
360  if (mjd[row] != mjd1 ) {
361 
362  /* Full transformation from ICRS to Observed
363  * update every t_skip, otherwise rotate earth only */
364  if ( fabs (mjd[row]-mjd0) > t_skip ) {
365  eraApco13 (2400000.5, mjd[row], dut1, lon, lat, elev,
366  pmx/3600.0*CPL_MATH_RAD_DEG, pmy/3600.0*CPL_MATH_RAD_DEG,
367  pressure, temperature, humidity, wavelength,
368  &astrom, &eo);
369  mjd0 = mjd[row];
370  }
371  else
372  eraAper13 (2400000.5, mjd[row] + dut1/(24.0*3600.0), &astrom);
373 
374  /* Apply proper motion (ICRS->BCRS) */
375  eraPmpx(rc, dc, pmr, pmd, parallax, sysvel, astrom.pmt, astrom.eb, eWb);
376  eraC2s(eWb, &rb, &db);
377 
378  /* Create (eU,eV,eW) in the BCRS */
379  eraS2c(0.0, CPL_MATH_PI/2.0, eZb); // eZc is the unit vector to the ICRS pole
380  eraPxp(eZb, eWb, eUb); // eUc is the cross product of eZc and eWc
381  eraPn(eUb, &norm, eUb); // eUc is normalized to a unit vector
382  eraPxp(eWb, eUb, eVb); // eWc is the cross product of eWc and eUc
383 
384  /* Create a 10 arcsec cardinal asterism around eWb in the BCRS */
385  rotate_vector(eWb, -eps, eVb, eWb_up);
386  rotate_vector(eWb, +eps, eVb, eWb_um);
387  rotate_vector(eWb, +eps, eUb, eWb_vp);
388  rotate_vector(eWb, -eps, eUb, eWb_vm);
389  eraC2s(eWb_up, &rb_up, &db_up);
390  eraC2s(eWb_um, &rb_um, &db_um);
391  eraC2s(eWb_vp, &rb_vp, &db_vp);
392  eraC2s(eWb_vm, &rb_vm, &db_vm);
393 
394  /* Transform the pointing direction and the cardinal asterism from BCRS to observed */
395  eraAtboq (rb , db , &astrom, eWo );
396  eraAtboq (rb_up, db_up, &astrom, eWo_up);
397  eraAtboq (rb_um, db_um, &astrom, eWo_um);
398  eraAtboq (rb_vp, db_vp, &astrom, eWo_vp);
399  eraAtboq (rb_vm, db_vm, &astrom, eWo_vm);
400 
401  /* Compute the observed (eUo,eVo,eWo) reference frame */
402  eraPxp(eWo_up, eWo_um, eUo);
403  eraPxp(eWo, eUo, eUo);
404  eraSxp(1./(2.0*eps), eUo, eUo);
405  eraPxp(eWo_vp, eWo_vm, eVo);
406  eraPxp(eWo, eVo, eVo);
407  eraSxp(1./(2.0*eps), eVo, eVo);
408 
409  /* Using eWo and zenith directions, compute eAz */
410  eraPxp(eWo, ez, eAZo);
411  eraPn(eAZo, &norm, eAZo);
412 
413  /* Using eWo and azimuth directions, compute eZd */
414  eraPxp(eWo, eAZo, eZDo);
415  eraPn(eZDo, &norm, eZDo);
416  }
417 
418  /* If requested, store [E_U,E_V,E_V,E_AZ,E_ZD] columns */
419  if (save_pointing) {
420  if (mjd[row] != mjd1 ) {
421  double * eU = cpl_malloc (sizeof(double) * 3);
422  double * eV = cpl_malloc (sizeof(double) * 3);
423  double * eW = cpl_malloc (sizeof(double) * 3);
424  double * eAZ = cpl_malloc (sizeof(double) * 3);
425  double * eZD = cpl_malloc (sizeof(double) * 3);
426  for ( cpl_size c = 0; c < 3; c++) {
427  eU[c] = eUo[c];
428  eV[c] = eVo[c];
429  eW[c] = eWo[c];
430  eAZ[c] = eAZo[c];
431  eZD[c] = eZDo[c];
432  }
433  /* Wrap into vectors. It makes the data valid, and is the fastest
434  * This and the duplication take most of the time of this function */
435  p_u[row] = cpl_array_wrap_double (eU, 3);
436  p_v[row] = cpl_array_wrap_double (eV, 3);
437  p_w[row] = cpl_array_wrap_double (eW, 3);
438  p_az[row] = cpl_array_wrap_double (eAZ, 3);
439  p_zd[row] = cpl_array_wrap_double (eZD, 3);
440  } else {
441  p_u[row] = cpl_array_duplicate (p_u [row-1]);
442  p_v[row] = cpl_array_duplicate (p_v [row-1]);
443  p_w[row] = cpl_array_duplicate (p_w [row-1]);
444  p_az[row] = cpl_array_duplicate (p_az[row-1]);
445  p_zd[row] = cpl_array_duplicate (p_zd[row-1]);
446  }
447  }
448 
449  /* If requested, compute the projected baseline [UCOORD,VCOORD]
450  * Note: The baseline length is corrected by the air refractive index
451  * at the HeNe wavelength and Paranal pressure. We want to use
452  * the vacuum baseline. */
453  double n_air = 1.0002028;
454  if (compute_uv) {
455  int base = row % 6;
456  uCoord[row] = (eUo[0] * baseline[base][0] + eUo[1] * baseline[base][1] + eUo[2] * baseline[base][2])/n_air;
457  vCoord[row] = (eVo[0] * baseline[base][0] + eVo[1] * baseline[base][1] + eVo[2] * baseline[base][2])/n_air;
458  }
459 
460  mjd1 = mjd[row];
461  CPLCHECK_MSG ("Cannot run the ERFA transform");
462  } /* End loop on rows */
463 
464  gravi_msg_function_exit(1);
465  return CPL_ERROR_NONE;
466 }
467 
468 /*----------------------------------------------------------------------------*/
478 /*----------------------------------------------------------------------------*/
479 
480 cpl_error_code gravi_compute_pointing_uv (gravi_data * p2vmred_data, gravi_data * eop_data)
481 {
482  gravi_msg_function_start(1);
483  cpl_ensure_code (p2vmred_data, CPL_ERROR_NULL_INPUT);
484 
485  /* Get the header */
486  cpl_propertylist * hdr_data = gravi_data_get_header (p2vmred_data);
487 
488  /* Get the OI_ARRAY table */
489  cpl_table * oi_array = gravi_data_get_table (p2vmred_data, GRAVI_OI_ARRAY_EXT);
490 
491  /* For each type of data SC / FT */
492  int type_data, ntype_data = 2;
493  for (type_data = 0; type_data < ntype_data ; type_data ++) {
494 
495  int npol = gravi_pfits_get_pola_num (hdr_data, type_data);
496  cpl_table * oi_vis = gravi_data_get_oi_vis (p2vmred_data, type_data, 0, npol);
497 
498  /* Verbose */
499  cpl_msg_info(cpl_func, "Compute pointing for %s ",type_data==GRAVI_FT?"FT":"SC");
500 
501  /* Compute for this polarisation */
502  gravi_eop_pointing_uv (oi_vis, hdr_data,
503  (eop_data ? gravi_data_get_table_x (eop_data, 0) : NULL),
504  (eop_data ? gravi_data_get_header (eop_data) : NULL),
505  type_data==GRAVI_FT?0:1,
506  oi_array);
507  CPLCHECK_MSG ("Cannot compute pointing");
508 
509  /* If second polarisation, for SC only, duplicate [E_U,E_V,E_V,E_AZ,E_ZD] */
510  if ((npol > 1) && (type_data==GRAVI_SC)) {
511  cpl_msg_debug (cpl_func,"Duplicate in the 2nd polarisation");
512 
513  cpl_table * oi_vis_1 = gravi_data_get_oi_vis (p2vmred_data, type_data, 1, npol);
514  cpl_table_duplicate_column (oi_vis_1, "E_U", oi_vis, "E_U");
515  cpl_table_duplicate_column (oi_vis_1, "E_V", oi_vis, "E_V");
516  cpl_table_duplicate_column (oi_vis_1, "E_W", oi_vis, "E_W");
517  cpl_table_duplicate_column (oi_vis_1, "E_AZ", oi_vis, "E_AZ");
518  cpl_table_duplicate_column (oi_vis_1, "E_ZD", oi_vis, "E_ZD");
519 
520  CPLCHECK_MSG ("Cannot duplicate");
521  }
522 
523  /* If second polarisation, duplicate [UCOORD,VCOORD] */
524  if (npol > 1) {
525  cpl_msg_info (cpl_func,"Duplicate in the 2nd polarisation");
526 
527  cpl_table * oi_vis_1 = gravi_data_get_oi_vis (p2vmred_data, type_data, 1, npol);
528  cpl_table_copy_data_double (oi_vis_1, "UCOORD", cpl_table_get_data_double (oi_vis, "UCOORD"));
529  cpl_table_copy_data_double (oi_vis_1, "VCOORD", cpl_table_get_data_double (oi_vis, "VCOORD"));
530  }
531  CPLCHECK_MSG ("Cannot duplicate");
532 
533  } /* End loop on FT/SC */
534 
535  gravi_msg_function_exit(1);
536  return CPL_ERROR_NONE;
537 }
538 
539 /*----------------------------------------------------------------------------*/
557 /*----------------------------------------------------------------------------*/
558 
559 char * gravity_eop_download_finals2000A (const char * eop_host,
560  const char * eop_urlpath,
561  int * data_length)
562 {
563  gravi_msg_function_start(1);
564 
565  const char ftp_port[] = "21";
566  int cmd_socket, data_socket;
567 
568  /* Check and dump the input */
569  cpl_ensure (eop_host, CPL_ERROR_NULL_INPUT, NULL);
570  cpl_ensure (eop_urlpath, CPL_ERROR_NULL_INPUT, NULL);
571  cpl_ensure (data_length, CPL_ERROR_NULL_INPUT, NULL);
572  cpl_msg_debug (cpl_func, "Using URL ftp://%s%s", eop_host, eop_urlpath);
573 
574  /* Getting the communication socket. */
575  cmd_socket = gravity_get_socket_connection(eop_host, ftp_port);
576  if (cmd_socket == 0)
577  {
578  char * msg = cpl_malloc(strlen(eop_host) + 31);
579  snprintf(msg, strlen(eop_host) + 30,
580  "Couldn't connect to the host %s", eop_host);
581  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, "%s", msg);
582  cpl_free(msg);
583  return NULL;
584  }
585 
586  if(!gravity_eop_ftp_reply(cmd_socket, NULL))
587  {
588  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
589  "FTP server didn't reply");
590  close(cmd_socket);
591  return NULL;
592  }
593  cpl_msg_debug(cpl_func, "SEND");
594  if(!gravity_eop_send_ftpcmd(cmd_socket, "USER anonymous\n"))
595  {
596  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
597  "Failed to send anonymous user");
598  close(cmd_socket);
599  return NULL;
600  }
601  if(!gravity_eop_send_ftpcmd(cmd_socket, "PASS ftp@eso.org\n"))
602  {
603  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
604  "Failed to send pasword");
605  close(cmd_socket);
606  return NULL;
607  }
608  int data_port = gravity_eop_send_pasv(cmd_socket, "PASV\n");
609  if(!data_port)
610  {
611  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
612  "Failed to get data port");
613  close(cmd_socket);
614  return NULL;
615  }
616 
617  /* Getting the data socket in passive mode */
618  char data_port_s[256];
619  snprintf(data_port_s, 255, "%d", data_port);
620  data_socket = gravity_get_socket_connection(eop_host, data_port_s);
621  if (data_socket == 0)
622  {
623  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
624  "Couldn't open ftp data connection");
625  return NULL;
626  }
627 
628  /* Retrieving the file */
629  if(!gravity_eop_send_ftpcmd(cmd_socket, "TYPE I\n"))
630  return NULL;
631  char * retr_command = cpl_malloc(strlen(eop_urlpath) + 7);
632  snprintf(retr_command, strlen(eop_urlpath) + 7, "RETR %s\n", eop_urlpath);
633  if(!gravity_eop_send_ftpcmd(cmd_socket, retr_command))
634  {
635  close(cmd_socket);
636  close(data_socket);
637  cpl_free(retr_command);
638  return NULL;
639  }
640  cpl_free(retr_command);
641 
642  char * data_eop = gravity_eop_get_ftp_file(data_socket, data_length);
643 
644  /* Close connection and free resources */
645  close(cmd_socket);
646  close(data_socket);
647 
648  gravi_msg_function_exit(1);
649  return data_eop;
650 }
651 
652 int gravity_get_socket_connection (const char * host, const char * port)
653 {
654  int sockfd;
655  gravi_msg_function_start(0);
656 
657  /* IP Name resolution */
658 
659  /* Set the hints. First we initialize to 0 the structure.
660  Only retrieve IPv4 or IPv6 if configured in the system */
661  struct addrinfo hints = { 0 };
662  hints.ai_family = AF_UNSPEC;
663  hints.ai_flags = AI_ADDRCONFIG;
664  hints.ai_socktype = SOCK_STREAM;
665  /* Getting the list of IP addresses */
666  cpl_msg_debug(cpl_func, "Getting IP");
667  struct addrinfo * addr_list ;
668  if (getaddrinfo(host, port, &hints, &addr_list) != 0) {
669  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
670  "Couldn't get address for host");
671  return 0;
672  }
673 
674  /* Connecting to the server for the FTP commands.
675  The first address to which we can connect will be used.
676  addr_list is a linked list */
677  cpl_msg_debug(cpl_func, "Connecting to server");
678  struct addrinfo *this_addr;
679  for(this_addr = addr_list; this_addr != NULL; this_addr = this_addr->ai_next)
680  {
681  /* Opening the socket */
682  if ((sockfd = socket(this_addr->ai_family, this_addr->ai_socktype,
683  this_addr->ai_protocol)) == -1) {
684  continue;
685  }
686 
687  if (connect(sockfd, this_addr->ai_addr, this_addr->ai_addrlen) == -1) {
688  close(sockfd);
689  if(errno == ECONNREFUSED)
690  errno = 0; //Reset errno if no remote partner at this address
691  continue;
692  }
693  cpl_msg_debug(cpl_func, "Connection established");
694  break;
695  }
696 
697  if (this_addr == NULL)
698  {
699  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
700  "Couldn't connect to the host");
701  return 0;
702  }
703 
704  freeaddrinfo(addr_list);
705 
706  gravi_msg_function_exit(0);
707  return sockfd;
708 }
709 
710 int gravity_eop_send_ftpcmd (int sockfd, const char *cmd)
711 {
712  gravi_msg_function_start(0);
713  cpl_msg_debug(cpl_func, "Sending FTP command <<%s>>", cmd);
714 
715  if(write(sockfd, cmd, strlen(cmd))==0)
716  {
717  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
718  "Problem during FTP transaction");
719  return 0;
720  }
721 
722  char * msg;
723  if(!gravity_eop_ftp_reply(sockfd, &msg))
724  {
725  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
726  "Problem during FTP transaction");
727  return 0;
728  }
729  if (msg != NULL)
730  free(msg);
731 
732  gravi_msg_function_exit(0);
733  return 1;
734 }
735 
736 
737 int gravity_eop_send_pasv (int sockfd, const char *cmd)
738 {
739  gravi_msg_function_start(0);
740 
741  if(write(sockfd, cmd, strlen(cmd))==0)
742  {
743  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
744  "Problem during FTP transaction");
745  return 0;
746  }
747 
748  char * msg = NULL;
749  char * new_con;
750  unsigned int v[6];
751 
752  if(!gravity_eop_ftp_reply(sockfd, &msg))
753  {
754  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
755  "Problem during FTP transaction");
756  return 0;
757  }
758  if (msg == NULL)
759  return 0;
760 
761  new_con = strchr(msg,'(');
762  if (new_con == NULL)
763  return 0;
764  sscanf(new_con+1,"%u,%u,%u,%u,%u,%u",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]);
765 
766  //Get the new port connection of passive mode
767  //This is coded in the reply of PASV
768  //See http://www.freefire.org/articles/ftpexample.php
769  int data_port = v[0] * 256 + v[1];
770 
771  free(msg);
772 
773  gravi_msg_function_exit(0);
774  return data_port;
775 }
776 
777 int gravity_eop_ftp_reply (int sockfd, char ** message)
778 {
779  gravi_msg_function_start(0);
780  int length, n;
781  char buffer[BUFFER_LENGTH];
782  char * msg = NULL;
783 
784  length = 0;
785  if(message != NULL)
786  *message = NULL;
787 
788  while( ( n = recv(sockfd, buffer, BUFFER_LENGTH - 1, 0) ) > 0)
789  {
790  void * tmp = realloc(msg, length + n + 1);
791  if(tmp == NULL)
792  {
793  free(msg);
794  return 0;
795  }
796  else
797  {
798  msg =tmp;
799  }
800 
801  strncpy(msg + length, buffer, n);
802  length += n;
803 
804  //Check FTP end of message.
805  //http://www.tcpipguide.com/free/t_FTPRepliesReplyCodeFormatandImportantReplyCodes-5.htm
806  if(msg[length-1]=='\n') //Reply contains full lines
807  {
808  //Search for the beggining of the last line
809  char * eolchar= msg + length - 1;
810  while(--eolchar != msg)
811  if (*(eolchar - 1) == '\n')
812  break;
813 
814  //The FTP code has 3 numbers and a space afterwards if it is the last line
815  if(*(eolchar+3) == ' ')
816  break;
817  }
818  }
819 
820  if(length == 0)
821  {
822  free(msg);
823  return 0;
824  }
825  msg[length] = '\0';
826 
827  cpl_msg_debug(cpl_func,"FTP reply: <<%s>>", msg);
828 
829  /* verify */
830  int verify = gravity_eop_verify_ftp_code(msg, length + 1);
831 
832  if(message != NULL && verify)
833  *message = msg;
834  else
835  free(msg);
836 
837  gravi_msg_function_exit(0);
838  return verify;
839 }
840 
841 char * gravity_eop_get_ftp_file(int sockfd, int * data_length)
842 {
843  gravi_msg_function_start(1);
844  int length, n;
845  char buffer[BUFFER_LENGTH];
846  char * msg = NULL;
847 
848  length = 0;
849 
850  /* Get the data */
851  cpl_msg_info(cpl_func, "Get the data");
852  while( ( n = recv(sockfd, buffer, BUFFER_LENGTH - 1, 0) ) > 0)
853  {
854  void * tmp = realloc(msg, length + n + 1);
855  if(tmp == NULL)
856  {
857  free(msg);
858  return 0;
859  }
860  else
861  {
862  msg =tmp;
863  }
864 
865  strncpy(msg + length, buffer, n);
866  length += n;
867 
868  cpl_msg_debug(cpl_func, "Received %d bytes so far",length);
869  }
870  if(length == 0)
871  return 0;
872  msg[length] = '\0';
873  *data_length = length+1;
874 
875  gravi_msg_function_exit(1);
876  return msg;
877 }
878 
879 int gravity_eop_verify_ftp_code (char * msg, int length)
880 {
881  gravi_msg_function_start(0);
882  char * line = msg;
883 
884  //FTP protocol specifies that lines starting with 2xx codes are ok
885  //Starting with 3xx are ok but the server expects some extra input
886  //Starting with 4xx, 5xx or 6xx it denotes an error.
887  //http://www.tcpipguide.com/free/t_FTPRepliesReplyCodeFormatandImportantReplyCodes-2.htm
888  while(line[0] == '1' || line[0] == '2' || line[0] == '3')
889  {
890  line = strchr(line, '\n');
891  if(line == NULL || line - msg + 2 == length)
892  return 1;
893  line = line + 1;
894  }
895 
896  gravi_msg_function_exit(0);
897  return 0;
898 }
899 
900 /*----------------------------------------------------------------------------*/
916 /*----------------------------------------------------------------------------*/
917 
918 cpl_table * gravity_eop_data_totable (const char * eop_data, int data_length)
919 {
920  gravi_msg_function_start(1);
921  cpl_ensure (eop_data, CPL_ERROR_NULL_INPUT, NULL);
922 
923  if(!((data_length - 1) % LINE_SIZE == 0))
924  {
925  cpl_error_set_message(cpl_func, CPL_ERROR_NULL_INPUT,
926  "Raw data doesn't have a fixed record width");
927  return 0;
928  }
929 
930  /* Create tables */
931  cpl_size n_entries = (data_length - 1 ) / LINE_SIZE;
932  cpl_table * eop_table = cpl_table_new (n_entries);
933  cpl_msg_info (cpl_func, " EOP data has a total of %"CPL_SIZE_FORMAT" entries", n_entries);
934 
935  /* Create columns */
936  cpl_table_new_column (eop_table, "MJD", CPL_TYPE_DOUBLE);
937  cpl_table_new_column (eop_table, "PMX", CPL_TYPE_DOUBLE);
938  cpl_table_new_column (eop_table, "PMY", CPL_TYPE_DOUBLE);
939  cpl_table_new_column (eop_table, "DUT", CPL_TYPE_DOUBLE);
940  cpl_table_new_column (eop_table, "FLAG", CPL_TYPE_STRING);
941 
942  /* Set units */
943  cpl_table_set_column_unit (eop_table, "MJD", "d");
944  cpl_table_set_column_unit (eop_table, "PMX", "arcsec");
945  cpl_table_set_column_unit (eop_table, "PMY", "arcsec");
946  cpl_table_set_column_unit (eop_table, "DUT", "s");
947 
948  /* Fill the columns from the string buffer */
949  for(cpl_size i=0; i<n_entries; i++)
950  {
951  char flag[2];
952  strncpy(flag, eop_data+i*LINE_SIZE+16, 1);
953  flag[1] = '\0';
954  cpl_table_set_string(eop_table, "FLAG", i, flag);
955 
956  cpl_table_set_double(eop_table, "MJD", i, atof(eop_data+i*LINE_SIZE+7));
957  if(!strncmp(flag, "I", 1) || !strncmp(flag, "P", 1))
958  {
959  cpl_table_set_double(eop_table, "PMX", i, atof(eop_data+i*LINE_SIZE+18));
960  cpl_table_set_double(eop_table, "PMY", i, atof(eop_data+i*LINE_SIZE+37));
961  cpl_table_set_double(eop_table, "DUT", i, atof(eop_data+i*LINE_SIZE+58));
962  }
963  }
964 
965  /* Remove the NULL columns */
966  cpl_table_unselect_all (eop_table);
967  cpl_table_or_selected_invalid (eop_table, "PMX");
968  cpl_table_or_selected_invalid (eop_table, "PMY");
969  cpl_table_or_selected_invalid (eop_table, "DUT");
970  cpl_msg_info (cpl_func,"Found %lld invalid", cpl_table_count_selected (eop_table));
971  cpl_table_erase_selected (eop_table);
972 
973  gravi_msg_function_exit(1);
974  return eop_table;
975 }
976 
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
char * gravity_eop_download_finals2000A(const char *eop_host, const char *eop_urlpath, int *data_length)
Retrieve the Earth Orientation Parameters computed by IERS.
Definition: gravi_eop.c:559
cpl_error_code gravi_eop_pointing_uv(cpl_table *input_table, cpl_propertylist *header, cpl_table *eop_table, cpl_propertylist *eop_header, int save_pointing, cpl_table *array_table)
Compute the pointing directions and projected baselines.
Definition: gravi_eop.c:242
cpl_error_code gravi_compute_pointing_uv(gravi_data *p2vmred_data, gravi_data *eop_data)
Compute the pointing directions and projected baselines in OI_VIS.
Definition: gravi_eop.c:480
cpl_table * gravi_data_get_table_x(gravi_data *self, int i)
Get the table of an extension by position.
Definition: gravi_data.c:1536
cpl_table * gravity_eop_data_totable(const char *eop_data, int data_length)
Export a raw string buffer containing EOP data to a CPL table.
Definition: gravi_eop.c:918